Skip to content

refactor: Remove static company sizes and KILL parallel arrays - #1405

Merged
OH296 merged 19 commits into
Adeptus-Dominus:mainfrom
OH296:static_array_lengths
Aug 2, 2026
Merged

refactor: Remove static company sizes and KILL parallel arrays#1405
OH296 merged 19 commits into
Adeptus-Dominus:mainfrom
OH296:static_array_lengths

Conversation

@OH296

@OH296 OH296 commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Summary by cubic

Removed the static 501-slot arrays and all parallel fields; units now own age, specials, and god_status. Company rosters and spawns are fully dynamic, loops/serialization use real lengths, and marine ascension is turn-based.

  • Refactors

    • Replaced fixed 2D mirrors with per‑company dynamic arrays; dropped age/spe/god mirrors and stored those on the unit.
    • find_company_open_slot grows mirrors/TTRPG and returns a valid index; scr_company_order resizes mirrors to roster length.
    • scr_max_marine returns a unit instead of a delimited string; dialogue and CM psyker checks read unit.specials. Ship occupants rebuilt to use UnitIndex and tally vehicle roles.
  • Bug Fixes

    • Reclusiam/jail, role counts, and penitents now read/write unit.god_status and iterate current roster lengths.
    • Gene‑seed recovery and kill‑and‑recover thresholds use unit.age and marine_ascension (turn‑based).
    • Removed hardcoded bounds in crusade, equipment checks, force counting, ship occupants, CM detection, and religion collection.

Written for commit 7413137. Summary will update on new commits.

Review in cubic

@github-actions github-actions Bot added Size: Medium Type: Refactor Rewriting/restructuring code, while keeping general behavior labels Jul 28, 2026

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread objects/obj_ini/Create_0.gml
Comment thread scripts/scr_company_view/scr_company_view.gml Outdated
Comment thread objects/obj_controller/Alarm_5.gml Outdated
Comment thread scripts/scr_company_view/scr_company_view.gml Outdated
Comment thread scripts/scr_add_man/scr_add_man.gml Outdated
Comment thread objects/obj_ini/Create_0.gml
Comment thread scripts/scr_chapter_managent_events/scr_chapter_managent_events.gml Outdated
Comment thread scripts/scr_company_order/scr_company_order.gml
Comment thread scripts/scr_marine_struct/scr_marine_struct.gml

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0 issues found across 1 file (changes from recent commits).

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Requires human review: Auto-approval blocked by 20 unresolved issues from previous reviews.

Re-trigger cubic

Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 4 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread objects/obj_controller/Alarm_5.gml Outdated
Comment thread scripts/scr_company_view/scr_company_view.gml Outdated
Comment thread scripts/scr_company_view/scr_company_view.gml

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 5 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread scripts/scr_unit_detail_text/scr_unit_detail_text.gml Outdated
Comment thread scripts/scr_company_view/scr_company_view.gml Outdated
Comment thread scripts/scr_marine_struct/scr_marine_struct.gml
Comment thread scripts/scr_kill_unit/scr_kill_unit.gml
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0 issues found across 1 file (changes from recent commits).

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Requires human review: Auto-approval blocked by 17 unresolved issues from previous reviews.

Re-trigger cubic

@OH296

OH296 commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator Author

/oc
Please, review this pull request:

Check for code quality issues of the newly added/changed code (docs/CODE_STYLE.md)
Look for potential bugs and regressions
Suggest improvements
Response style: you're the main maintainer of this codebase.

@opencode-agent

Copy link
Copy Markdown
Contributor

Here's my review as the project maintainer.


Overview

This PR removes the fixed-size 501-slot arrays and parallel age/spe/god fields in favor of dynamic per-company rosters and per-unit properties. The direction is correct, but there are several critical bugs still unresolved and some code quality concerns.


P0 — Will crash at runtime

1. obj_ini/Create_0.gml:121race is not a 2D array, will crash on any company > 0 access

race = array_create(11, 501);

This creates [501, 501, 501, ...] — each element is the integer 501, not a sub-array. race[c][i] for c=1..10 indexes an integer and throws.

scr_initialize_custom at line 1482 only replaces race[0] with a real array via _init_marine_row(0, ...). Companies 1–10 are left as bare integers.

Fix: Same pattern as the other arrays — use array_create(11, []) and let scr_initialize_custom populate them, or revert to array_create_2d. But note issue #2 first.

2. obj_ini/Create_0.gml:123-137 — Shared array references for companies 1–10

name = array_create(11, []);
role = array_create(11, []);
// ... wep1, wep2, armour, gear, mobi, TTRPG

array_create(11, []) gives all 11 rows the same empty array reference. Companies 1–10 share a single array for name, role, TTRPG, etc. Writing to name[1][i] overwrites name[2][i] through name[10][i].

scr_initialize_custom only replaces company 0's array. Companies 1–10 remain aliased.

This is the exact bug cubic flagged three reviews ago and suggested array_create_ext for.

3. objects/obj_controller/Alarm_5.gml:293-296 — Null guard after property access

var _unit = fetch_unit([c,e]);
if (_unit.god_status == 10) {       // CRASH if _unit is undefined
    if (!is_struct(_unit)) {         // guard is too late
        continue;
    }

fetch_unit returns undefined for empty slots. The is_struct check needs to come before the property access.

4. scripts/scr_add_man/scr_add_man.gml:19-77 — Writes to undefined unit on normal recruitment path

Flow for other_gear == false:

  1. Line 17: _company_slot = find_company_open_slot(...)
  2. Line 19: scr_wipe_unit(...) — clears the slot to undefined
  3. Line 20: _unit = fetch_unit(...)undefined
  4. Line 77: _unit.age = ...CRASH on undefined
  5. Line 116: _unit = new TTRPG_stats(...) — reassignment happens too late

The other_gear == true path is fine (switch blocks reassign _unit before line 77). The normal path is not.

5. scripts/scr_company_order/scr_company_order.gml:20 — Array index syntax error

function company_length(company){
    return obj_ini.TTRPG(company)
}

This calls TTRPG as a function, not indexing it as an array. Should be obj_ini.TTRPG[company].

This also means scr_ship_occupants at line 6 (for i <= company_length(co)) will crash on any ship occupant listing.


P1 — Will silently produce wrong results

6. scripts/scr_enemy_ai_d/scr_enemy_ai_d.gml:170 — Wrong property name

if (string_count("0", _unit.special) > 0) {

_unit.special (singular) doesn't exist. The field is specials (plural), used correctly everywhere else. The Warp Shadow psyker detection always fails as a result.

7. scripts/scr_random_event/scr_random_event.gml:712 — Same typo

if (_unit.role() == obj_ini.role[100][eROLE.CHAPTERMASTER] && string_count("0", _unit.special) > 0) {

Same _unit.special_unit.specials fix needed.

8. scripts/scr_kill_unit/scr_kill_unit.gml:56-61 — Gene-seed recovery uses wrong time comparison

if (unit.marine_ascension > 30 && !obj_ini.zygote && !obj_ini.doomed) {
    obj_controller.gene_seed += 1;
}
if (unit.marine_ascension > 100 && !obj_ini.doomed) {
    obj_controller.gene_seed += 1;
}

marine_ascension is now obj_controller.turn at creation. A marine created on turn 5 is forever ineligible (5 > 30 is false). Previously this checked the calendar age (in years). The intent is an elapsed-time check, so these should use obj_controller.turn - unit.marine_ascension.

9. scripts/scr_marine_struct/scr_marine_struct.gml:259 — Turn number displayed as calendar year

marine_ascension = obj_controller.turn;

Previously this stored millenium * 1000 + year (a calendar year like 41735). Now it stores a turn count like 101. The detail panel at scr_unit_detail_text.gml:52 renders "{round(age)} years old. Ascended to an Astartes in the {ascension_date} year." — which will display "101 year". Either the display needs converting, or the field needs both values.


P2 — Design & code quality

10. scripts/scr_initialize_custom/scr_initialize_custom.gml:1482-1489 — Only initializes company 0

_init_marine_row(0, ...) and TTRPG[0] = array_create(501) only set up company 0. Companies 1–10 are left with bare integers (race) or shared arrays (everything else). The deserialize at Create_0.gml:303-306 also hardcodes for _mar <= 500 with TTRPG[_coy][_mar] — which will crash on integer race and corrupt the shared arrays.

This needs to be consistent — either initialize all companies at startup or ensure the dynamic-resize path is universally reliable.

11. scripts/scr_company_order/scr_company_order.gml:122-131 — Compaction resizes arrays but other consumers don't know

The company ordering function truncates arrays to _new_length (the number of actual marines). Other consumers like scr_max_marine, scr_check_equip, scr_crusade previously iterated to 501 and may now index out of bounds. Need to verify they all use array_length() dynamically.

12. scripts/scr_chapter_managent_events/scr_chapter_managent_events.gml:276 — No struct guard

for (var i = 0; i < array_length(obj_ini.TTRPG[0]); i++) {
    scr_move_unit_info(pick.company, 0, pick.marine_number, i);
    break;

Missing is_struct(obj_ini.TTRPG[0][i]) before the move. Also this loop always breaks after the first iteration — is that intentional?


Summary

Must fix before merge:

  1. race = array_create(11, 501) → crash on any company > 0
  2. Shared [] references for name/role/wep1/wep2/armour/gear/mobi/TTRPG → data corruption across companies 1–10
  3. _unit.god_status accessed before is_struct guard in Alarm_5.gml
  4. _unit.age assignment on undefined in scr_add_man normal path
  5. TTRPG(company) instead of TTRPG[company] in company_length → crashes scr_ship_occupants

Should fix:
6-7. _unit.special_unit.specials in two files
8-9. Gene-seed elapsed-time check, marine_ascension display units

This is a substantial refactor with a lot of moving parts. The shared-array initialization and the race 1D-vs-2D issue are the most concerning because they corrupt data silently or crash immediately on any interaction with companies 1–10.

github run

@OH296

OH296 commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator Author

/review

@opencode-agent

opencode-agent Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Review — static_array_lengths (maintainer pass on current head 44e9a25)

Good direction, but this PR is not mergeable. The previous review's P0s are almost all still live at head, plus the refactor introduced new ones. Several fixes landed (dialogue [o,i][0,i], round(age) template syntax, specials()→field, TTRPG()→return index, extra brace) — credit where due — but the core issues remain.


P0 — Will crash at runtime

1. company_length() is broken — scr_company_order.gml:19-21

function company_length(company){
    return obj_ini.TTRPG(company)
}

Calls the array as a function (() not []) and forgets array_length. Every consumer crashes: scr_ship_occupants (fleet advisor UI), scr_crusade (turn end), scr_max_marine. Fix: return array_length(obj_ini.TTRPG[company]);. Note scr_ship_occupants:6 also uses <= (off-by-one, reads one past end).

2. scr_max_marine — regressed into nonsense, scr_max_marine.gml:11-39
Loop variable is co, but the body uses c — never assigned. fetch_unit([c, i]) and man_c = c always read/write a stale instance var, and obj_ini.name[man_c][man_i] at line 39 will OOB. The old c = -1; repeat(11){c+=1} version was correct. It returns the wrong marine for every max_type.

3. Alarm_5.gml:293-296 — guard still after the dereference

var _unit = fetch_unit([c,e]);
if (_unit.god_status == 10) {
    if (!is_struct(_unit)) { continue; }

fetch_unit returns undefined for wiped slots (killed marines persist as undefined until the next compaction). Crash every turn. is_struct must come first. Same unguarded obj_ini.TTRPG[c][e].god_status pattern in Mouse_50.gml:34,48 and scr_controller_helpers.gml:240.

4. scr_add_man.gml:77_unit.age on undefined
scr_wipe_unitfetch_unitundefined_unit.age = ... on the other_gear == false path. This is the normal recruitment flow (Alarm_5:89). Guard with is_struct(_unit) or move the assignment into the other_gear branch.

5. scr_initialize_custom — the old 11×501 init loop was never deleted, lines 2001-2014
It still runs after the new _init_marine_row block: writes race[c][i] = 1 for c=1..10 where race[c] is the bare integer 501, and fills the shared [] arrays for name/role/wep/TTRPG. It's the exact code the refactor was supposed to remove, now writing into aliased arrays (or crashing, depending on GML's real-as-array conversion). Delete it; the new _init_marine_row + TTRPG[0] block is the only init that should exist.

6. deserialize — shared-array corruption on load, Create_0.gml:303-307
TTRPG = array_create(11, []) aliases all 11 rows. On load, scr_initialize_custom doesn't run (global.load != -1), so the 501-loop writes all companies into one array. After load, companies 1-10 and company 0 point at the same roster: units duplicated 11× in UI/role counts/serialize, and Alarm_5's god loop (item 3) processes every marine 11 times. scr_company_order eventually unshares, but only after fetch_unit has already handed out wrong data. Fix: TTRPG[_coy] = array_create(array_length(name[_coy])) per company, or reuse the row initializer.

7. obj_controller/Create_0.gml:594 — obj_ini.TTRPG[0][0].specials unguarded
On the load path TTRPG[0][0] is undefined until deserialize runs → crash at Create. Also silently changed from the old slot-1 Chapter Master check to slot 0.


P1 — Silent wrong behavior

8. _unit.special (singular) typo ×2 — scr_enemy_ai_d.gml:170, scr_random_event.gml:712. Field is specials. Warp Shadow psyker detection always fails. Also no is_struct guard on either loop.

9. Gene-seed thresholds, scr_kill_unit.gml:56-61marine_ascension is now obj_controller.turn at creation, so unit.marine_ascension > 30/100 means "recruited before turn 30/100" — almost no late-game marine ever yields gene-seed. Regression vs unit.age() > 30. Should be obj_controller.turn - unit.marine_ascension.

10. Ascension year is now a turn countscr_marine_struct.gml:259 stores a turn; detail panel prints "Ascended in the 101 year." Either keep the calendar value (millenium*1000)+year or relabel the display.

11. scr_dialogue.gml:181-186_unit.role() on undefined slots (company 0 can hold wiped slots between kills and compaction). Needs is_struct before role().

12. new_forge_master_chosen, scr_chapter_managent_events.gml:275-279 — the empty-slot search became scr_move_unit_info(pick.company, 0, pick.marine_number, i); break; — always moves to slot 0 on the first iteration, overwriting whoever is there. The loop body must still test for an empty slot.

13. Save compatibility — new specials/age/god_status/marine_ascension struct fields default to ""/0 for old saves; old spe/age/god obj_ini data deserializes into dead fields. Existing saves lose psychic markers, ages, and jail status. Needs a migration or defaulting on load.

14. Hazard: resized arrays + stale indices. scr_company_order now truncates to actual roster size (was always 501). Any code holding a [company, slot] across a compaction can OOB on unguarded reads like scr_kill_unit.gml:4 (obj_ini.role[company][unit_slot]). Worth an audit pass — scr_kill_unit itself is a candidate.


P2 — Quality

  • array_create(11, []) aliasing — unshared only when scr_company_order happens to run first (works by timing, not by design). Use array_create_ext(11, ...) for distinct rows. race = array_create(11, 501) is the same trap — companies 1-10 hold the integer 501, and array_push(obj_ini.race[target_company], 0) in find_company_open_slot will throw on an unsorted company.
  • find_company_open_slot — line 53 good = array_length(...) is dead code (overwritten at line 63); the pushed 0 race value is a magic number (CODE_STYLE) — use eFACTION.PLAYER.
  • scr_check_equip.gml:12i <= array_length(...) off-by-one (absorbed by fetch_unit's try/catch, but wrong).
  • scr_company_order.gml:122-131array_create(_new_length, 0) fills string arrays with 0 before overwrite; harmless today, but array_create(_new_length, "") for the string fields and undefined for TTRPG would be honest. The 9 near-identical lines are also loopable.
  • serialize — iterating name[_coy][_mar] != "" is fine, but it reads TTRPG via jsonify_marine_struct on a possibly-not-yet-sorted load state (see P0-6).

Bottom line: the structural direction is right and the per-unit field migration is mostly consistent, but company_length, scr_max_marine, the scr_add_man/Alarm_5 guards, the leftover old init loop, and the deserialize aliasing are all blockers. Items 1-7 must be fixed before merge; 8-12 before release; 13-14 deserve an explicit decision. I'd also want a "new game → recruit → save → load → recruit" smoke test plus one full turn with a killed marine, since that's the sequence every P0 above hits.

github run

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 6 files (changes from recent commits).

Confidence score: 2/5

  • In objects/obj_ini/Create_0.gml, using race = array_create(11, []); will make all entries point to the same underlying array, so updating one race slot can unintentionally mutate every slot and corrupt game state/setup data; initialize each index with a distinct array instance (e.g., looped per-slot assignment) to remove the shared-reference risk.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="objects/obj_ini/Create_0.gml">

<violation number="1" location="objects/obj_ini/Create_0.gml:121">
P0: Custom agent: **Code Quality Review**

The change to `race = array_create(11, []);` introduces a shared-reference bug. In GameMaker, `array_create(n, value)` stores the same reference in every slot when `value` is an array, so all 11 company indices alias the same array. During initialization in `scr_initialize_custom`, the loop writing `race[c][i]` for companies 1–10 mutates that single shared array, meaning later changes to one company will corrupt the others. Use an explicit loop to create a distinct inner array for each company instead.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread objects/obj_ini/Create_0.gml

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

8 issues found across 10 files (changes from recent commits).

Confidence score: 2/5

  • In scripts/scr_company_order/scr_company_order.gml, company_length() now returns an array while callers in scr_max_marine.gml, scr_crusade.gml, and scr_ship_occupants.gml still treat it as an integer loop bound, which can break iteration logic and occupant calculations at runtime — restore an integer count return (or update all call sites to use array length explicitly).
  • In scripts/scr_max_marine/scr_max_marine.gml, the refactor switched lookup to co but max-tracking still writes man_c = c, so the selected company can be wrong or undefined and propagate bad assignment decisions — align downstream assignments to the new loop variable and add a quick assertion/test around max-company selection.
  • In scripts/scr_chapter_managent_events/scr_chapter_managent_events.gml, find_company_open_slot(0) has side effects (mutating parallel arrays) and can also allow a Forge Master replacement into reserved slot 0, risking silent state corruption of leadership slots — keep the slot-1 guard for this path or split lookup from allocation to avoid mutation during checks.
  • In scripts/scr_add_man/scr_add_man.gml, moving _unit.age assignment to the end overwrites roll_age() results for marines, causing incorrect character age data that can affect progression/balance logic — only apply _unit.age when intended (or gate by unit type) before/around biological age rolls.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="objects/obj_controller/Alarm_5.gml">

<violation number="1" location="objects/obj_controller/Alarm_5.gml:297">
P2: Custom agent: **Code Quality Review**

This line introduces a raw magic number `10` as a domain identifier for the penitent/reclusiam `god_status` state. The value `10` is already compared against `god_status` in at least six other places across the codebase, so it qualifies as a reused semantic constant. Per the code-quality rule, raw numbers used as identifiers more than once should be declared as a `#macro` or `enum` (a scalar constant, not an array). Consider defining something like `#macro GOD_STATUS_PENITENT 10` and replacing all occurrences so the intent is self-documenting.</violation>
</file>

<file name="scripts/scr_chapter_managent_events/scr_chapter_managent_events.gml">

<violation number="1" location="scripts/scr_chapter_managent_events/scr_chapter_managent_events.gml:275">
P2: Custom agent: **Code Quality Review**

The newly added code repeats the raw literal `0` as a company identifier in two coupled call sites (`find_company_open_slot(0)` and `scr_move_unit_info(..., 0, ...)`). Because both refer to the same destination company, this obscures intent and makes future refactors error-prone. Consider introducing a named constant (e.g., a `#macro` or `enum` value) for the headquarters/unassigned company index.</violation>

<violation number="2" location="scripts/scr_chapter_managent_events/scr_chapter_managent_events.gml:275">
P2: Custom agent: **Code Quality Review**

The call to `find_company_open_slot(0)` looks like a simple lookup, but the function mutates `obj_ini` parallel arrays by pushing new empty entries when no open slot exists. Because `scr_move_unit_info` immediately writes into `obj_ini.*[0][_company_slot]`, this control path implicitly depends on the array-growth side effect. A function named `find_*` should ideally be a pure query; consider renaming it to something like `get_or_create_company_open_slot`, or split the logic so callers explicitly reserve a slot before moving a unit.</violation>

<violation number="3" location="scripts/scr_chapter_managent_events/scr_chapter_managent_events.gml:275">
P2: A Forge Master replacement can be placed into the reserved Chapter Master slot when slot 0 is empty, overwriting that slot’s state. Preserve the existing slot-1 lower bound for this flow, or let `find_company_open_slot` accept a minimum slot.</violation>
</file>

<file name="scripts/scr_add_man/scr_add_man.gml">

<violation number="1" location="scripts/scr_add_man/scr_add_man.gml:123">
P2: Custom agent: **Code Quality Review**

The current-age calculation `(obj_controller.millenium * 1000) + obj_controller.year` is repeated in at least six other files (e.g., `scr_initialize_custom`, `scr_after_combat`, `obj_p_assra`). Using a raw `1000` as a conversion factor across that many call sites creates maintenance debt if the calendar scale ever changes. You already have `obj_controller` in scope here, so extracting a `get_current_year()` method on `obj_controller` (or a global helper) would let you unify the logic and remove the scattered raw constant in one step.</violation>

<violation number="2" location="scripts/scr_add_man/scr_add_man.gml:123">
P1: Custom agent: **Code Quality Review**

Moving `_unit.age` to the end of the function causes it to silently overwrite the biological age rolled by `roll_age()` for marine units. In the original placement, this assignment happened before the marine branch reassigned `_unit`, so marines retained their `roll_age()` value; now the current calendar year (~40000) replaces that rolled age. Because `age` is used in equipment quality calculations like `age + exp`, this corrupts spawned marine stats. Preserve the rolled marine age by making the date assignment conditional on non-marine roles, or restore the original placement where the marine reassignment naturally discarded it.</violation>
</file>

<file name="scripts/scr_max_marine/scr_max_marine.gml">

<violation number="1" location="scripts/scr_max_marine/scr_max_marine.gml:13">
P1: Custom agent: **Code Quality Review**

This refactor updated `fetch_unit` to use the loop counter `co`, but the downstream assignments that record the max company (`man_c = c`) still reference the undeclared variable `c` in all three branches. Because `c` is never declared in this function, GameMaker falls back to instance-scope resolution, which will likely yield 0 or a stale value rather than the actual company index. To keep the fetch and the tracking consistent, change `man_c = c` to `man_c = co` in the chaos, age, and exp branches.</violation>
</file>

<file name="scripts/scr_company_order/scr_company_order.gml">

<violation number="1" location="scripts/scr_company_order/scr_company_order.gml:20">
P1: company_length now returns the TTRPG array instead of the unit count, but every caller uses it as an integer: scr_max_marine.gml:12 and scr_crusade.gml:66 loop `i < company_length(co)`, and scr_ship_occupants.gml:6 sets `_co_length = company_length(co)` then loops `i < _co_length`. Since obj_ini.TTRPG[company] is a 2D array of units (obj_ini/Create_0.gml), these become number-vs-array comparisons, which raise GML runtime errors and break unit iteration. Return the array length instead.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread scripts/scr_add_man/scr_add_man.gml Outdated
continue;
for (co = 0; co <= obj_ini.companies; co++) {
for (var i = 0; i < company_length(co); i++) {
unit = fetch_unit([co, i]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Custom agent: Code Quality Review

This refactor updated fetch_unit to use the loop counter co, but the downstream assignments that record the max company (man_c = c) still reference the undeclared variable c in all three branches. Because c is never declared in this function, GameMaker falls back to instance-scope resolution, which will likely yield 0 or a stale value rather than the actual company index. To keep the fetch and the tracking consistent, change man_c = c to man_c = co in the chaos, age, and exp branches.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At scripts/scr_max_marine/scr_max_marine.gml, line 13:

<comment>This refactor updated `fetch_unit` to use the loop counter `co`, but the downstream assignments that record the max company (`man_c = c`) still reference the undeclared variable `c` in all three branches. Because `c` is never declared in this function, GameMaker falls back to instance-scope resolution, which will likely yield 0 or a stale value rather than the actual company index. To keep the fetch and the tracking consistent, change `man_c = c` to `man_c = co` in the chaos, age, and exp branches.</comment>

<file context>
@@ -10,7 +10,7 @@ function scr_max_marine(max_type) {
     for (co = 0; co <= obj_ini.companies; co++) {
         for (var i = 0; i < company_length(co); i++) {
-            unit = fetch_unit([c, i]);
+            unit = fetch_unit([co, i]);
             if (!is_struct(unit)) {
                 continue;
</file context>

Comment thread scripts/scr_company_order/scr_company_order.gml Outdated
Comment thread objects/obj_controller/Alarm_5.gml
Comment thread scripts/scr_chapter_managent_events/scr_chapter_managent_events.gml
Comment thread scripts/scr_chapter_managent_events/scr_chapter_managent_events.gml
Comment thread scripts/scr_add_man/scr_add_man.gml Outdated
Comment thread scripts/scr_chapter_managent_events/scr_chapter_managent_events.gml
OH296 and others added 2 commits August 1, 2026 19:02
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 10 files (changes from recent commits).

Confidence score: 3/5

  • In scripts/scr_max_marine/scr_max_marine.gml, the fetch uses loop key co but assignment and name lookup still use c (man_c = c and obj_ini.name[man_c][man_i]), which can attach the wrong candidate/category and surface incorrect marine selection or naming at runtime — align all three branches and the return lookup to the same loop variable.
  • In scripts/scr_add_man/scr_add_man.gml, age assignment logic is split under conditions tied to array_contains(non_marine_roles, man_role), so marines and non-marines may follow inconsistent age paths and produce unintended demographics — consolidate the branch gating so each role type takes exactly one intended age calculation path.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="scripts/scr_max_marine/scr_max_marine.gml">

<violation number="1" location="scripts/scr_max_marine/scr_max_marine.gml:13">
P2: The fetch is now correctly keyed by the loop variable `co`, but the result is still recorded against `man_c = c;` (three branches) and the return name lookup `obj_ini.name[man_c][man_i]`. `c` is not the loop variable and is never assigned in this function, so if the max marine is in company co>0, the returned/all dialogue column reports company 0's marine name while the value it found is from another company. Complete the rename to `co` in all branches so the returned marine identity matches what was actually selected.</violation>
</file>

<file name="scripts/scr_add_man/scr_add_man.gml">

<violation number="1" location="scripts/scr_add_man/scr_add_man.gml:123">
P3: The marine branch sets age via roll_age() inside the `!array_contains(non_marine_roles, man_role)` block, while non-marines get the date-based age in this second, separate block gated by the same array_contains check. Keeping one initial-age mechanism in two disconnected places with a duplicated guard makes it easy for the two branches to drift. Consider setting age inside the existing non-marine (`else`) path or behind a small helper so the age logic lives in one place.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread scripts/scr_max_marine/scr_max_marine.gml
Comment thread scripts/scr_add_man/scr_add_man.gml

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 issues found across 11 files (changes from recent commits).

Confidence score: 3/5

  • In objects/obj_ini/Create_0.gml, array_create(11, []) risks aliasing inner arrays and only allocates rows 0..10, while other scripts index race[100..102]; this can cause cross-slot data contamination or missing-row/runtime errors when defaults/custom setup paths run — initialize each row as a distinct array and explicitly size or guard high-index rows used by defaults_slot.
  • In scripts/scr_enemy_ai_d/scr_enemy_ai_d.gml, string_count("0", _unit.specials) > 0 uses a magic literal that obscures intent and can silently misclassify abilities if the specials format changes — replace it with a named constant or helper predicate that documents the flag meaning.
  • In scripts/scr_add_man/scr_add_man.gml, the duplicated array_contains(non_marine_roles, man_role) checks with opposite polarity create avoidable branch-maintenance risk and can drift into inconsistent behavior — collapse to one membership evaluation and branch from that single result.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="objects/obj_ini/Create_0.gml">

<violation number="1" location="objects/obj_ini/Create_0.gml:121">
P2: race = array_create(11, []) leaves only 11 outer rows (0-10), but race is indexed at row 100/101/102 via defaults_slot across scr_ui_settings/scr_role_setup/scr_initialize_custom. Sizing now depends on rows being created on demand by out-of-bounds writes in scr_initialize_custom (race[100]/race[102] = array_create(...)) plus GameMaker's copy-on-write on a single shared empty array; reading race[100][...] before those rows are built is an out-of-bounds read and will error. This replaces the target branch's self-contained array_create_2d(11, 501, 0) with implicit, undocumented sizing.</violation>

<violation number="2" location="objects/obj_ini/Create_0.gml:121">
P1: Custom agent: **Code Quality Review**

In GameMaker, `array_create(11, [])` stores the *same* empty array reference in all 11 slots. While GML’s copy-on-write can protect some direct index assignments, any mutation through a function call (e.g. `array_push`) or an indirect reference will leak data across company rows. This is a fragile aliasing bug that is easy to miss and exactly the kind of subtle language behavior the Code Quality Review criteria ask us to flag.

**Suggested fix:** Initialize each slot with its own array instance, for example:
```gml
race = array_create(11);
for (var i = 0; i < 11; i++) {
    race[i] = [];
}

(Note that the same issue already exists for name, role, etc. in this file, but this diff newly introduces it for race.)

P2: Custom agent: **Code Quality Review**

The array_contains(non_marine_roles, man_role) check appears twice in this function with opposite polarity. Since the two paths are mutually exclusive, they should be combined into a single if/else block. Duplicating the same membership test makes the control flow harder to follow and increases maintenance burden if the role list or branching logic ever changes.

P2: Custom agent: **Code Quality Review**

The check string_count("0", _unit.specials) > 0 relies on an unexplained numeric string literal as a magic constant, which makes the intent opaque to readers and forces the same fragile pattern to be repeated across multiple files. Because this PR is already a code-quality refactor, consider introducing an enum or a helper method such as has_special(special_id) so ability checks are self-documenting and can be maintained in one place.


</details>

<sub>**Tip**: Review your code locally with the [cubic CLI](https://docs.cubic.dev/ide/cli-review?utm_source=github&utm_content=general_review_body) to iterate faster.<br /><br />[Re-trigger cubic](https://www.cubic.dev/action/re-review/pr/Adeptus-Dominus/ChapterMaster/1405/ai_pr_review_1785607719961_420d2c12-4789-4a4b-953d-9cf5d7ff9cbb?returnTo=https%3A%2F%2Fgithub.com%2FAdeptus-Dominus%2FChapterMaster%2Fpull%2F1405)</sub>

<!-- cubic:review-post:ai_pr_review_1785607719961_420d2c12-4789-4a4b-953d-9cf5d7ff9cbb:7413137bf750c8a47a2ea89b36ebe05a6a528119:ba2b52ee-82d9-4502-8a89-a7148285bd7b,0f37d097-5fc9-432c-80df-c842ecf4309b -->

Comment thread objects/obj_ini/Create_0.gml
Comment thread objects/obj_ini/Create_0.gml
Comment thread scripts/scr_add_man/scr_add_man.gml
Comment thread scripts/scr_enemy_ai_d/scr_enemy_ai_d.gml
@OH296
OH296 merged commit c1630ff into Adeptus-Dominus:main Aug 2, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Size: Big Type: Refactor Rewriting/restructuring code, while keeping general behavior

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant