-
Notifications
You must be signed in to change notification settings - Fork 43
feat: Population and PDF growth #1083
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
📝 WalkthroughWalkthroughThe flux flows through the warp in manifold directions, Tech-Priest. This incursion introduces population and PDF targeting mechanisms. A new Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Suggested labels
Suggested reviewers
Pre-merge checks✅ Passed checks (3 passed)
Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 11
📜 Review details
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (8)
ChapterMaster.yypobjects/obj_star/Alarm_1.gmlobjects/obj_star/Create_0.gmlobjects/obj_star_select/Draw_64.gmlscripts/scr_PlanetData/scr_PlanetData.gmlscripts/scr_enemy_ai_c/scr_enemy_ai_c.gmlscripts/scr_imperial_manage_fleet_functions/scr_imperial_manage_fleet_functions.gmlscripts/scr_ork_fleet_functions/scr_ork_fleet_functions.gml
💤 Files with no reviewable changes (2)
- scripts/scr_enemy_ai_c/scr_enemy_ai_c.gml
- scripts/scr_ork_fleet_functions/scr_ork_fleet_functions.gml
🧰 Additional context used
📓 Path-based instructions (2)
**/*.gml
⚙️ CodeRabbit configuration file
**/*.gml: - Constants ofmacrotype MUST have a space between the constant name and its value. Without it, the compiler will throw an error.
- WRONG:
#macro COLOR_RED11119- RIGHT:
#macro COLOR_RED 11119- Color codes in the code SHOULDN'T have any spaces in their ID.
- WRONG:
# 80bf40- RIGHT:
#80bf40- All code should comply with the up-to-date GML documentation.
Files:
scripts/scr_PlanetData/scr_PlanetData.gmlscripts/scr_imperial_manage_fleet_functions/scr_imperial_manage_fleet_functions.gmlobjects/obj_star/Create_0.gmlobjects/obj_star/Alarm_1.gmlobjects/obj_star_select/Draw_64.gml
**/*.*
⚙️ CodeRabbit configuration file
**/*.*: - Do not prioritize the shortest code. Prioritize code that explicitly reveals its intent.
- Understandable variable naming is extremely important.
- If a solution is "clever" but hard to parse mentally, request a refactor to a more verbose but clearer approach.
- Apply the "Rule of Three": suggest abstraction when logic is repeated three times or more times.
- For subjective improvements (naming, architectural choice), do not give a concrete suggestion immediately. Instead, ask a guiding question to prompt the user's reflection.
- Every suggestion for a code change must include a brief justification.
- If a TODO comment is added, ask the user if you should create a GitHub issue for this TODO.
- If a TODO comment is deleted, remind the user if there is an active GitHub issue related to that comment.
Files:
scripts/scr_PlanetData/scr_PlanetData.gmlscripts/scr_imperial_manage_fleet_functions/scr_imperial_manage_fleet_functions.gmlobjects/obj_star/Create_0.gmlobjects/obj_star/Alarm_1.gmlChapterMaster.yypobjects/obj_star_select/Draw_64.gml
🧠 Learnings (3)
📚 Learning: 2025-03-31T23:32:30.003Z
Learnt from: OH296
Repo: Adeptus-Dominus/ChapterMaster PR: 646
File: objects/obj_pnunit/Alarm_5.gml:84-91
Timestamp: 2025-03-31T23:32:30.003Z
Learning: In obj_pnunit/Alarm_5.gml, the function get_armour_data("maintenance") will always return a numeric value (at minimum 0), making null/undefined checks unnecessary.
Applied to files:
objects/obj_star/Alarm_1.gml
📚 Learning: 2025-03-07T01:56:40.971Z
Learnt from: MCPO-Spartan-117
Repo: Adeptus-Dominus/ChapterMaster PR: 562
File: scripts/scr_marine_struct/scr_marine_struct.gml:0-0
Timestamp: 2025-03-07T01:56:40.971Z
Learning: Marines' ages should be incremented at the year transition in obj_turn_end/Alarm_1.gml rather than calculated dynamically based on the current year and recruitment date. This ensures proper aging without retroactive application.
Applied to files:
objects/obj_star/Alarm_1.gml
📚 Learning: 2025-09-04T14:37:58.773Z
Learnt from: CR
Repo: Adeptus-Dominus/ChapterMaster PR: 0
File: docs/CODE_STYLE.md:0-0
Timestamp: 2025-09-04T14:37:58.773Z
Learning: Applies to docs/**/*.gml : File names should follow GameMaker type-prefix conventions
Applied to files:
ChapterMaster.yyp
🔇 Additional comments (2)
objects/obj_star/Create_0.gml (1)
43-43: p_target_pdf array initialisation is aligned with existing planet state.Tech-Priest, the new
p_target_pdfarray is birthed in the same litany as the otherp_…fields, so save/load rites inserialize/deserializewill naturally include it. For fresh sectors this pleases the machine spirit.One point to confirm: for legacy save data that never stored
p_target_pdf, planets will boot with a target PDF of0until some other script synthesises values fromp_pdf. Is this lack of back-fill for old campaigns acceptable to you, or should a post-load ritual derivep_target_pdffrom currentp_pdfto keep older savegames in balance?objects/obj_star_select/Draw_64.gml (1)
380-381: The machine spirit's prescience guards against temporal paradoxes, Tech-Priest.The defensive
variable_instance_existscheck is both necessary and commendable. New star instances initializep_target_pdfinCreate_0.gml, yet legacy save files from before this variable's introduction lack it. The same protective pattern appears throughout the codebase—notably inscr_imperial_manage_fleet_functions.gml—confirming that graceful degradation to 0 is the intentional doctrine. This two-tier strategy (defensive checks paired with safe defaults) ensures compatibility with ancient archives whilst maintaining full functionality for newly-awakened celestial bodies.
| "ForcedPrefabProjectReferences":[ | ||
| {"link":"io.gamemaker.sdfshaders-1.0.0","name":"io.gamemaker.sdfshaders-1.0.0","path":"io.gamemaker.sdfshaders-1.0.0.yyp",}, | ||
| ], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# Search for the referenced SDF shaders prefab
fd 'io.gamemaker.sdfshaders-1.0.0.yyp'Repository: Adeptus-Dominus/ChapterMaster
Length of output: 55
🏁 Script executed:
# Inspect the project file structure to understand dependencies
fd '\.yyp$' | head -20Repository: Adeptus-Dominus/ChapterMaster
Length of output: 90
🏁 Script executed:
# Search for CI/build configuration that may specify IDE versions
fd -e yaml -e yml -e json | xargs grep -l '2024\|IDE\|GameMaker' 2>/dev/null | head -10Repository: Adeptus-Dominus/ChapterMaster
Length of output: 55
🏁 Script executed:
# Examine the ChapterMaster.yyp file itself for context on other dependencies
grep -n 'ForcedPrefabProjectReferences\|IDEVersion' ChapterMaster.yypRepository: Adeptus-Dominus/ChapterMaster
Length of output: 150
🏁 Script executed:
# Check for git submodules
cat .gitmodules 2>/dev/null || echo "No submodules configured"Repository: Adeptus-Dominus/ChapterMaster
Length of output: 97
🏁 Script executed:
# Examine the ChapterMaster.yyp file for context around the prefab reference
sed -n '115,130p' ChapterMaster.yypRepository: Adeptus-Dominus/ChapterMaster
Length of output: 2169
🏁 Script executed:
# Search for any documentation or configuration about external libraries/prefabs
fd -e md -e txt | xargs grep -l 'sdfshaders\|prefab\|library' 2>/dev/null | head -5Repository: Adeptus-Dominus/ChapterMaster
Length of output: 91
🏁 Script executed:
# Check if there are any other ForcedPrefabProjectReferences that might provide a pattern
grep 'ForcedPrefabProjectReferences' -A 20 ChapterMaster.yypRepository: Adeptus-Dominus/ChapterMaster
Length of output: 3438
🏁 Script executed:
# Review the CODE_STYLE.md for guidance on external dependencies
cat docs/CODE_STYLE.md | head -100Repository: Adeptus-Dominus/ChapterMaster
Length of output: 4037
The SDF shaders prefab is absent from the repository—the project-spirit shall not awaken.
The manifest declares an allegiance to io.gamemaker.sdfshaders-1.0.0.yyp within ForcedPrefabProjectReferences (line 121), yet the archival search reveals no trace of this artefact within the forge. Without it, the IDE will reject the project at load-time, and compilation shall be rendered impossible. No submodule, no external resolver, no rite of installation has been inscribed.
The project cannot proceed to merge in this corrupted state. The prefab must either be committed to the repository, installed via established ritual (documented in README or setup guide), or the ForcedPrefabProjectReferences entry removed entirely if its inclusion was erroneous.
Regarding IDEVersion 2024.14.2.213 at line 584: Ensure all build-forges and developer workstations possess this version or newer, else synchronisation shall falter.
🤖 Prompt for AI Agents
In ChapterMaster.yyp around lines 121-123 (and note IDEVersion at line 584), the
ForcedPrefabProjectReferences points to io.gamemaker.sdfshaders-1.0.0.yyp which
is missing from the repository; either add/commit the prefab file at the
declared path (io.gamemaker.sdfshaders-1.0.0.yyp), wire it as a submodule or
include installation steps in the README/setup script to fetch it, or remove the
ForcedPrefabProjectReferences entry if it was added in error; also verify and
document the required IDEVersion 2024.14.2.213 in the setup docs so developers
and CI use a compatible IDE.
| draw_set_halign(fa_left); | ||
|
|
||
| if (!target.space_hulk) { | ||
| if (obj_controller.faction_status[eFACTION.Imperium] != "War" && p_data.current_owner <= 5) || (obj_controller.faction_status[eFACTION.Imperium] == "War") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Clarify boolean precedence with explicit parentheses, Tech-Priest.
The machine logic chains multiple conditions without sacred parentheses. While the cogitator may parse correctly through precedence runes, mortal comprehension falters.
Is your intent ((status != "War") && (owner <= 5)) || (status == "War")?
⚙️ Proposed clarification
-if (obj_controller.faction_status[eFACTION.Imperium] != "War" && p_data.current_owner <= 5) || (obj_controller.faction_status[eFACTION.Imperium] == "War") {
+if ((obj_controller.faction_status[eFACTION.Imperium] != "War" && p_data.current_owner <= 5) || (obj_controller.faction_status[eFACTION.Imperium] == "War")) {As per coding guidelines: prioritise code that explicitly reveals intent over brevity.
🤖 Prompt for AI Agents
In objects/obj_star_select/Draw_64.gml around line 277, the complex conditional
lacks explicit parentheses which makes boolean precedence unclear; update the if
condition to explicitly group as
((obj_controller.faction_status[eFACTION.Imperium] != "War") &&
(p_data.current_owner <= 5)) ||
(obj_controller.faction_status[eFACTION.Imperium] == "War") so the intent — war
status overrides the owner check, otherwise require owner <= 5 — is unambiguous.
|
|
||
| if (p_data.has_feature(P_features.Recruiting_World)) { | ||
| var _recruit_world = p_data.get_features(P_features.Recruiting_World)[0]; | ||
| if (_recruit_world.recruit_type == 0) && (obj_controller.faction_status[p_data.current_owner] != "War" && obj_controller.faction_status[p_data.current_owner] != "Antagonism" || p_data.player_disposition >= 50) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Boolean labyrinth requires illumination, Tech-Priest.
This conditional pathway interweaves four logic gates without parenthetical guidance. The intent remains obscured in ambiguity.
Reforge with explicit parentheses to reveal the true logic path: (type == 0) AND ((not War AND not Antagonism) OR (disposition >= 50))?
⚙️ Proposed clarification
-if (_recruit_world.recruit_type == 0) && (obj_controller.faction_status[p_data.current_owner] != "War" && obj_controller.faction_status[p_data.current_owner] != "Antagonism" || p_data.player_disposition >= 50) {
+if ((_recruit_world.recruit_type == 0) && ((obj_controller.faction_status[p_data.current_owner] != "War" && obj_controller.faction_status[p_data.current_owner] != "Antagonism") || p_data.player_disposition >= 50)) {As per coding guidelines: if a solution is hard to parse mentally, refactor to clarity.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (_recruit_world.recruit_type == 0) && (obj_controller.faction_status[p_data.current_owner] != "War" && obj_controller.faction_status[p_data.current_owner] != "Antagonism" || p_data.player_disposition >= 50) { | |
| if ((_recruit_world.recruit_type == 0) && ((obj_controller.faction_status[p_data.current_owner] != "War" && obj_controller.faction_status[p_data.current_owner] != "Antagonism") || p_data.player_disposition >= 50)) { |
🤖 Prompt for AI Agents
In objects/obj_star_select/Draw_64.gml around line 294, the if-statement mixes
&& and || without explicit grouping, making the intended logic ambiguous;
rewrite the condition with explicit parentheses to enforce:
(_recruit_world.recruit_type == 0) AND (
(obj_controller.faction_status[p_data.current_owner] != "War" AND
obj_controller.faction_status[p_data.current_owner] != "Antagonism") OR
(p_data.player_disposition >= 50) ). Update the line to use these parentheses so
the boolean precedence is clear and the intended short-circuiting is preserved.
| } | ||
| garrison_data_slate.draw(344+main_data_slate.width-4, 160, 0.6, 0.6); | ||
| } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
Extraneous whitespace mars the sacred text, Tech-Priest.
Line 328 contains only a tab character. While the cogitator ignores such trivia, clean scripture pleases the Machine God.
🤖 Prompt for AI Agents
In objects/obj_star_select/Draw_64.gml around line 328, there is an extraneous
tab character on a line by itself; remove that trailing blank line or replace
the tab with an empty line (no whitespace) so the file contains no lone
whitespace-only line at 328, and save with normalized line endings to prevent
reintroduction.
| function fmt_compact(_n){ | ||
| _n = abs(_n); | ||
| if (_n >= 1000000000) return string_format(_n / 1000000000, 0, 2) + "B"; | ||
| if (_n >= 1000000) return string_format(_n / 1000000, 0, 2) + "M"; | ||
| if (_n >= 1000) return string_format(_n / 1000, 0, 1) + "K"; | ||
| return string(floor(_n)); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
Function ritual repeats with each render cycle, Tech-Priest.
The fmt_compact subroutine manifests anew every frame within the drawing rite. The Draw_64 event invokes sixty times per second—this function regenerates endlessly.
Hoist this logic to initialization or script scope to honour efficiency protocols.
⚙️ Proposed optimisation
Define the formatter once outside the draw method, perhaps as a script or at object creation:
// In a separate script file or Create event
function fmt_compact(_n){
_n = abs(_n);
if (_n >= 1000000000) return string_format(_n / 1000000000, 0, 2) + "B";
if (_n >= 1000000) return string_format(_n / 1000000, 0, 2) + "M";
if (_n >= 1000) return string_format(_n / 1000, 0, 1) + "K";
return string(floor(_n));
}🤖 Prompt for AI Agents
In objects/obj_star_select/Draw_64.gml around lines 331-337, the fmt_compact
function is being redefined every frame inside the Draw event which is wasteful;
move the function definition out of the Draw_64 event into an initialization
scope (either a dedicated script file or the object's Create event) so it is
defined once, remove the inline definition from the Draw_64 code, ensure the
Draw_64 code calls the now-global/script-scoped fmt_compact, and verify there
are no name collisions or ordering issues (define the function before any Draw
events that call it).
| if (_cur_pop_abs > 0 && _supported_pop_abs > 0 && _cur_pop_abs < _supported_pop_abs){ | ||
| _pop_growth_abs = ceil(_cur_pop_abs * POP_GROWTH_RATE); | ||
| _pop_growth_abs = min(_pop_growth_abs, _supported_pop_abs - _cur_pop_abs); | ||
| } else if (_cur_pop_abs > 0 && _supported_pop_abs <= 0) { | ||
| _pop_growth_abs = ceil(_cur_pop_abs * POP_GROWTH_RATE); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# First, let's look at the full context of lines 367-372
fd "Draw_64.gml" -t f | head -1 | xargs cat -n | sed -n '355,385p'Repository: Adeptus-Dominus/ChapterMaster
Length of output: 55
🏁 Script executed:
# Search for p_max_population assignments and usage
rg -n 'p_max_population' --type=gml -B2 -A2 | head -100Repository: Adeptus-Dominus/ChapterMaster
Length of output: 100
🏁 Script executed:
# Search for recolonization and colonization mechanics
rg -n -i 'recolon|coloniz' --type=gml -B2 -A2 | head -80Repository: Adeptus-Dominus/ChapterMaster
Length of output: 100
🏁 Script executed:
# Search for POP_GROWTH_RATE constant definition
rg -n 'POP_GROWTH_RATE' --type=gml -B1 -A1Repository: Adeptus-Dominus/ChapterMaster
Length of output: 100
🏁 Script executed:
# Find the Draw_64.gml file
fd "Draw_64.gml" -t fRepository: Adeptus-Dominus/ChapterMaster
Length of output: 419
🏁 Script executed:
# Read the context around lines 367-372
fd "Draw_64.gml" -t f -exec cat -n {} \; | sed -n '360,380p'Repository: Adeptus-Dominus/ChapterMaster
Length of output: 303
🏁 Script executed:
# Search for p_max_population across all files
rg -n 'p_max_population' -B2 -A2 | head -150Repository: Adeptus-Dominus/ChapterMaster
Length of output: 11866
🏁 Script executed:
# Search for recolonization and colonization logic
rg -n -i 'recolon|coloniz' -B2 -A2 | head -100Repository: Adeptus-Dominus/ChapterMaster
Length of output: 8199
🏁 Script executed:
# Search for POP_GROWTH_RATE definition
rg -n 'POP_GROWTH_RATE' -B1 -A1 | head -50Repository: Adeptus-Dominus/ChapterMaster
Length of output: 837
The logic paradox remains unresolved, Tech-Priest.
The second conditional pathway (lines 370-372) permits population expansion even when _supported_pop_abs <= 0—a state that manifests upon planetary destruction or during initialisation when p_max_population is not yet assigned. This violates the stated doctrine: "Growth capped so it does not exceed a planet's maximum supported population."
Observe the asymmetry: line 369 applies a min() constraint to cap growth against capacity, yet line 371 applies no such restraint. Population multiplies in the void of support.
Either this conditional path serves a purpose beyond the archives' current knowledge, or it represents unintended logic that should be excised. Which is it?
🤖 Prompt for AI Agents
In objects/obj_star_select/Draw_64.gml around lines 367 to 372, the else-if
branch allows population growth when _supported_pop_abs <= 0, contradicting the
capacity cap logic; change the logic so growth is only computed when
_supported_pop_abs > 0 (use the existing ceil/min clamp), and otherwise set
_pop_growth_abs to 0 to prevent growth when there is no support.
| } else if (target.p_pdf[p] > 0){ | ||
| _support_pct = 999; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
The sentinel value 999 obscures its meaning, Tech-Priest.
When PDF exists but capacity equals zero, the display renders "999%" to the user. This arcane number lacks semantic clarity.
Consider displaying textual wisdom ("Over Capacity" or "N/A") when this paradoxical state manifests, or define a named constant CAPACITY_INVALID = 999 to document intent.
⚙️ Alternative rendering logic
if (_pdf_cap > 0){
_support_pct = (target.p_pdf[p] / _pdf_cap) * 100;
-} else if (target.p_pdf[p] > 0){
- _support_pct = 999;
-}
+} else if (target.p_pdf[p] > 0){
+ _support_pct = -1; // Use -1 as sentinel
+}
+// Later in display logic:
+if (_support_pct >= 0){
+ draw_text(..., "PDF capacity usage: " + string_format(_support_pct, 0, 1) + "%...");
+} else {
+ draw_text(..., "PDF capacity usage: Over Capacity (" + fmt_compact(target.p_pdf[p]) + ")");
+}Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In objects/obj_star_select/Draw_64.gml around lines 398-400, the code sets
_support_pct = 999 as a magic sentinel when target.p_pdf[p] > 0 but capacity is
zero; replace this unclear literal with a named constant (e.g., CAPACITY_INVALID
= 999) declared near the top of the file and use that constant here, and update
the rendering logic to check for CAPACITY_INVALID and display a human-readable
label ("Over Capacity" or "N/A") instead of "999%". Ensure the constant is
documented with a comment and the rendering branch prefers the textual message
when CAPACITY_INVALID is set.
| if (p_population[i]>=10000000){ | ||
| var military=p_population[i]/470; | ||
| p_guardsmen[i]=floor(military*0.25); | ||
| p_pdf[i]=floor(military*0.75); | ||
| } | ||
| if (p_population[i]>=5000000) and (p_population[i]<10000000){ | ||
| var military=p_population[i]/200; | ||
| p_guardsmen[i]=floor(military*0.25); | ||
| p_pdf[i]=floor(military*0.75); | ||
| } | ||
| if (p_population[i]>=100000) and (p_population[i]<5000000){ | ||
| var military=p_population[i]/50; | ||
| p_guardsmen[i]=floor(military*0.25); | ||
| p_pdf[i]=floor(military*0.75); | ||
| } | ||
| if (p_population[i]<100000) and (p_population[i]>5) and (p_large[i]==0){ | ||
| p_pdf[i]=floor(p_population[i]/25); | ||
| } | ||
| if (p_population[i]<2000) and (p_population[i]>5) and (p_large[i]==0){ | ||
| p_pdf[i]=floor(p_population[i]/10); | ||
| } | ||
| if (p_large[i]==1){ | ||
| p_guardsmen[i]=floor(p_population[i]*1250000); | ||
| p_pdf[i]=p_guardsmen[i]*3; | ||
| } | ||
|
|
||
| //the pdf on planet gen is set as the target_pdf | ||
| p_target_pdf[i] = p_pdf[i]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
p_target_pdf is not reset for Craftworlds, risking unintended PDF regrowth.
Tech-Priest, the logic that forges p_pdf and mirrors it into p_target_pdf at generation is sound for Imperial worlds: the target becomes “PDF at game birth”, as desired.
Yet note the later Craftworld rite:
- You purge
p_guardsmen[i]andp_pdf[i]back to0forcraftworld==1, - But you leave
p_target_pdf[i]at the pre-purge value you just computed.
Any subsequent population/PDF growth that climbs towards p_target_pdf will then quietly conjure Imperial-pattern PDF on an Eldar Craftworld, which seems both mechanically and lore-incongruent.
Would it not better honour the design if Craftworld planets also had their p_target_pdf[i] annulled alongside p_pdf[i], so no Imperial PDF cap exists there at all?
Possible adjustment to keep Craftworlds PDF-free
if (craftworld==1){
- p_guardsmen[i]=0;
- p_pdf[i]=0;
+ p_guardsmen[i]=0;
+ p_pdf[i]=0;
+ p_target_pdf[i]=0;
p_eldar[i]=6;
owner = eFACTION.Eldar;
p_owner[1]=6;
warp_lanes=[];
x2=0;
}Secondary incantation: in the 5–2000 population band both the <100000 and <2000 branches fire, reassigning p_pdf[i] twice. If this is intentional, consider tightening the first condition to p_population[i] >= 2000 && p_population[i] < 100000 to make the intent explicit to future adepts.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In objects/obj_star/Alarm_1.gml around lines 78 to 105, p_target_pdf is being
set from the computed p_pdf for all worlds but later Craftworlds reset
p_guardsmen and p_pdf to 0 while leaving p_target_pdf unchanged (allowing
unintended PDF regrowth); also the population bands for <100000 and <2000
overlap causing p_pdf to be assigned twice. Fix by ensuring when a planet is a
Craftworld (craftworld==1) you also set p_target_pdf[i]=0 alongside
p_guardsmen[i]=0 and p_pdf[i]=0, and tighten the first small-population branch
to only apply for 2000 <= p_population[i] < 100000 (or change the second branch
to p_population[i] < 2000) so the two branches do not both execute.
| var data = cargo_data.colonize; | ||
| if (data.target_planet>0){ | ||
| var targ_planet = data.target_planet; | ||
| if (!star.p_large[targ_planet]){ | ||
| star.p_population[targ_planet] += data.colonists; | ||
| } else { | ||
| star.p_population[targ_planet] += data.colonists/power(10,8); | ||
| } | ||
| var start_influ = star.p_influence[targ_planet][eFACTION.Tyranids]; | ||
| with (star){ | ||
| merge_influences(data.colonist_influence,targ_planet); | ||
| } | ||
| var colony_purpose = data.mission=="new_colony"? "recolonise" : "bolster population" ; | ||
| var alert_string = $"Imperial citizens {colony_purpose} {planet_numeral_name(targ_planet, star)} I."; | ||
| var player_vision = star.p_player[targ_planet]>0 || star.p_owner[targ_planet] == eFACTION.Player; | ||
| if (star.p_influence[targ_planet][eFACTION.Tyranids]>start_influ && (player_vision)){ | ||
| alert_string += " They bring with them traces of a Genestelar Cult"; | ||
| if (data.target_planet > 0){ | ||
| var targ_planet = data.target_planet; | ||
| var _pop_add = 0; | ||
|
|
||
| if (!star.p_large[targ_planet]){ | ||
| _pop_add = data.colonists; | ||
| star.p_population[targ_planet] += _pop_add; | ||
| } else { | ||
| _pop_add = data.colonists / power(10, 8); | ||
| star.p_population[targ_planet] += _pop_add; | ||
| } | ||
|
|
||
| if (!variable_instance_exists(star.id, "p_max_population")){ | ||
| star.p_max_population = array_create(array_length(star.p_population), 0); | ||
| } | ||
|
|
||
| if (!variable_instance_exists(star.id, "p_target_pdf")){ | ||
| star.p_target_pdf = array_create(array_length(star.p_population), 0); | ||
| for (var _j = 0; _j < array_length(star.p_target_pdf); _j++){ | ||
| star.p_target_pdf[_j] = star.p_pdf[_j]; | ||
| } | ||
| scr_alert("green","duhuhuhu",alert_string,star.x,star.y); | ||
| } | ||
|
|
||
| var _pop_mult = 0.25; | ||
| var _pdf_mult = 1.0; | ||
|
|
||
| switch (star.p_type[targ_planet]){ | ||
| case "Lava": _pop_mult = 0.2; _pdf_mult = 0.2; break; | ||
| case "Desert": _pop_mult = 0.5; _pdf_mult = 0.5; break; | ||
| case "Hive": _pop_mult = 1; _pdf_mult = 1.5; break; | ||
| case "Agri": _pop_mult = 1; _pdf_mult = 0.7; break; | ||
| case "Temperate": _pop_mult = 1; _pdf_mult = 1; break; | ||
| case "Shrine": _pop_mult = 1; _pdf_mult = 1; break; | ||
| case "Ice": _pop_mult = 0.5; _pdf_mult = 0.5; break; | ||
| case "Feudal": _pop_mult = 1; _pdf_mult = 0.5; break; | ||
| case "Forge": _pop_mult = 1; _pdf_mult = 3; break; | ||
| case "Death": _pop_mult = 0.10; _pdf_mult = 3; break; | ||
| case "Craftworld": _pop_mult = 0.00; _pdf_mult = 0.0; break; | ||
| } | ||
|
|
||
| var _target_pop_add = _pop_add * _pop_mult; | ||
| star.p_max_population[targ_planet] += _target_pop_add; | ||
|
|
||
| var _pdf_target_add = ceil(_pop_add * 0.001 * _pdf_mult); | ||
| star.p_target_pdf[targ_planet] += _pdf_target_add; | ||
|
|
||
| if (star.p_max_population[targ_planet] < 0) star.p_max_population[targ_planet] = 0; | ||
| if (star.p_target_pdf[targ_planet] < 0) star.p_target_pdf[targ_planet] = 0; | ||
|
|
||
| var start_influ = star.p_influence[targ_planet][eFACTION.Tyranids]; | ||
| with (star){ | ||
| merge_influences(data.colonist_influence, targ_planet); | ||
| } | ||
|
|
||
| var colony_purpose = data.mission=="new_colony"? "recolonise" : "bolster population"; | ||
| var alert_string = $"Imperial citizens {colony_purpose} {planet_numeral_name(targ_planet, star)} I."; | ||
| var player_vision = star.p_player[targ_planet]>0 || star.p_owner[targ_planet] == eFACTION.Player; | ||
| if (star.p_influence[targ_planet][eFACTION.Tyranids] > start_influ && player_vision){ | ||
| alert_string += "They bring with them traces of a Genestealer Cult"; | ||
| } | ||
|
|
||
| show_debug_message( | ||
| "[COLONISE] " + planet_numeral_name(targ_planet, star) + | ||
| " | +pop=" + string(_pop_add) + | ||
| " | +target_pop=" + string(_target_pop_add) + | ||
| " | +target_pdf=" + string(_pdf_target_add) | ||
| ); | ||
|
|
||
| scr_alert("green","duhuhuhu",alert_string,star.x,star.y); | ||
| } else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Coloniser impact on p_target_pdf for large worlds appears vanishingly small.
Tech-Priest, the new colonisation litany cleanly separates targeted colonies (data.target_planet > 0) from the older broadcast mode and wires p_max_population and p_target_pdf into the ritual. Yet the numeric spirits for large-population worlds whisper of imbalance:
- When
star.p_large[targ_planet]is true,_pop_addis in “billions” units (e.g._pop_add = 0.5for +0.5B souls). _pdf_target_addis thenceil(_pop_add * 0.001 * _pdf_mult), so that same +0.5B influx on, say, a Hive world yields only1extra point ofp_target_pdf.- Meanwhile the initial
p_target_pdffor such a world is on the order of hundreds of millions of PDF personnel (per the generation rites inAlarm_1).
Thus, for large worlds, colonisation practically never changes the PDF cap, which seems at odds with the stated objective that colonisation should increase target PDF in a type-weighted fashion.
Should _pdf_target_add instead be derived from the actual headcount of added colonists (taking p_large into account), so that a fixed fraction of those new bodies becomes potential PDF, regardless of representation? For example, computing an _actual_pop_add that multiplies _pop_add by the large-world conversion factor when p_large is set, and then applying your 0.001 * _pdf_mult to that, would align p_target_pdf updates with the scale used in p_pdf.
A separate query to your logic-engines: in this targeted path, you no longer adjust star.dispo[targ_planet], whereas the legacy non-target branch still reinitialises disposition after recolonisation. Is this divergence intended (e.g. “stealth” resettlement that does not alter local feelings), or should the disposition change be unified between both flows?
| // Check for industrial facilities | ||
| var fleet_buildable = ((planet_type!="Dead" && planet_type!="Lava") || _has_warboss || _has_stronghold); | ||
| if (fleet_buildable && planet_forces[eFACTION.Ork]>=4){// Used to not have Ice either | ||
| if (fleet_buildable && planet_forces[eFACTION.Ork]>=4 && pdf < 200000){// Used to not have Ice either | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ork shipbuilding gate matches intent, but the threshold is an opaque constant.
Tech-Priest, constraining the ship-forges with pdf < 200000 correctly prevents new Ork fleets once local PDF reaches 200,000 or higher, as the design-chant decrees.
However, the raw literal 200000 obfuscates the rite’s purpose. Would this cogitation be clearer – to future priests and to your own memory – if expressed as a named threshold (or at least commented) indicating “PDF level beyond which Orks cease building ships on this world”, and is it deliberate that only the current planet’s PDF, not system-wide PDF, governs this gate?
Note - after opening PR I am going to make a few changes to address the mechanicus ai's comments as it raised a few good points, so please wait until I create a new commit to give a full review.
Purpose and Description
Additional changes
Addtional context
Planetary population growth is set at 0.025% of it's current population per turn which is approixmately per month. (Planets with no population have no growth until they are recolonized)
Planetary pdf growth is set at 1% per month as this is a fraction of the exisiting populaton and so would replenish faster.
This means planets can rebuild a pdf much faster than it can rebuild a eviscerated population which is a good detail.
A planets max supported PDF as a percentage is tied to population, for example a planet with 50% of it's populaton can only support 50% of it's target pdf until it's populaton recovers.
There is now a incentive to save planets with large populations from xenos genocide if the planets pdf collapses as if the population is saved they will rebuild their pdf to it's maximum reasonably fast within 100 turns.
Visual changes

Here's the new data which is displayed in each planets population report
Testing done
Started up several new games and played a bit in each, checked that balance is not detrimentally effected, they are no preformance issues, no obvious bugs with what I have added either logic wise or visually. It's seems to function as intended and makes the sector feel more alive and dynamic. Orkageddon still happens (. especially because in my longest game I couldn't kill the warboss. ) albiet slightly slower because orks have to reduce a planets pdf before they can start producing ships.
I have tested the colonization changes, pdf growth, and the change to ork ships not being created with exisitng pdf on planet.