Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ai-pr-review.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:
- name: Review PR with reusable AI reviewer
if: github.event_name == 'pull_request'
id: review
uses: misospace/pr-reviewer-action@41d9ffadf39c7f317ef3cb6885d9f48447dcad23 # v2.1.1
uses: misospace/pr-reviewer-action@7e25f7d0d8651e936f3158477b0a364f27ed7383 # v2.0.0

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Minor (style): pr-reviewer-action version downgraded from v2.1.1 to v2.0.0; verify this is intentional given recent upgrade history.

Automated finding from AI PR review.

with:
github_token: ${{ steps.app-token.outputs.token }}
ai_primary_retries: "3"
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ jobs:
PY

- name: Upload release asset
uses: softprops/action-gh-release@718ea10b132b3b2eba29c1007bb80653f286566b # v3
uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3
with:
tag_name: ${{ env.RELEASE_TAG }}
files: build/windowstead-linux.zip
Expand Down Expand Up @@ -199,7 +199,7 @@ jobs:
PY

- name: Upload release asset
uses: softprops/action-gh-release@718ea10b132b3b2eba29c1007bb80653f286566b # v3
uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3
with:
tag_name: ${{ env.RELEASE_TAG }}
files: build/windowstead-windows.zip
Expand Down Expand Up @@ -262,7 +262,7 @@ jobs:
PY

- name: Upload release asset
uses: softprops/action-gh-release@718ea10b132b3b2eba29c1007bb80653f286566b # v3
uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3
with:
tag_name: ${{ env.RELEASE_TAG }}
files: build/windowstead-macos.zip
Expand Down
64 changes: 0 additions & 64 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -115,18 +115,6 @@ jobs:
exit 1
fi

- name: Run food-bias sort tests (issue #245)
shell: bash
run: |
set -euo pipefail
./.tools/Godot_v${{ steps.godot-config.outputs.version }}-${{ steps.godot-config.outputs.status }}_linux.x86_64 --headless --path . --script res://tests/test_food_bias_sort.gd > food-bias-tests.log 2>&1
FB_EXIT=$?
cat food-bias-tests.log
if [ $FB_EXIT -ne 0 ]; then
echo "::error::Food-bias sort tests failed (exit code $FB_EXIT)"
exit 1
fi

macos-validation:
name: macOS validation
runs-on: macos-latest
Expand Down Expand Up @@ -230,55 +218,3 @@ jobs:
echo "::error::Layout regression tests failed (exit code $LAYOUT_EXIT)"
exit 1
fi

export-validation:
name: Export validation (Linux)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7

- uses: ./.github/actions/load-godot-toolchain
id: godot-config

- name: Download Godot
shell: bash
run: |
set -euo pipefail
mkdir -p .tools
curl -fsSL "${{ steps.godot-config.outputs.linux_url }}" -o /tmp/godot_linux.zip
printf '%s %s\n' "${{ steps.godot-config.outputs.linux_sha256 }}" /tmp/godot_linux.zip | shasum -a 256 -c -
unzip -oq /tmp/godot_linux.zip -d .tools/
chmod +x .tools/Godot_v${{ steps.godot-config.outputs.version }}-${{ steps.godot-config.outputs.status }}_linux.x86_64

- name: Install export templates
shell: bash
run: |
set -euo pipefail
mkdir -p ~/.local/share/godot/export_templates/${{ steps.godot-config.outputs.version }}.${{ steps.godot-config.outputs.status }}
curl -fsSL "${{ steps.godot-config.outputs.templates_url }}" -o /tmp/godot_export_templates.tpz
printf '%s %s\n' "${{ steps.godot-config.outputs.templates_sha256 }}" /tmp/godot_export_templates.tpz | shasum -a 256 -c -
unzip -oq /tmp/godot_export_templates.tpz -d /tmp/godot_export_templates
cp -f /tmp/godot_export_templates/templates/* ~/.local/share/godot/export_templates/${{ steps.godot-config.outputs.version }}.${{ steps.godot-config.outputs.status }}/

- name: Export Linux build
shell: bash
run: |
set -euo pipefail
mkdir -p build/export-validation/linux
.tools/Godot_v${{ steps.godot-config.outputs.version }}-${{ steps.godot-config.outputs.status }}_linux.x86_64 --headless --path . --export-release "Linux/X11" ./build/export-validation/linux/windowstead.x86_64

- name: Verify export output
shell: bash
run: |
set -euo pipefail
BINARY="build/export-validation/linux/windowstead.x86_64"
PCK="build/export-validation/linux/windowstead.pck"
if [ ! -s "$BINARY" ]; then
echo "::error::Export binary not found or empty: $BINARY"
exit 1
fi
if [ ! -s "$PCK" ]; then
echo "::error::Export PCK not found or empty: $PCK"
exit 1
fi
echo "Export validation passed — binary and PCK present"
3 changes: 2 additions & 1 deletion scripts/colony_stance.gd
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ static func get_effective_priority_order(colony_stance: String, player_order: Ar

# ── Check if a gather task matches food-biased stance ──
static func is_food_gather_task(task: Dictionary) -> bool:
if String(task.get("kind", "")) != "gather":
var kind := String(task.get("kind", ""))
if kind != "gather" and kind != "gather_food":
return false
return String(task.get("resource", "")) == "food"
10 changes: 1 addition & 9 deletions scripts/constants.gd
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
## Extracted from scripts/main.gd to reduce its blast radius.
## All values are immutable const dictionaries/arrays — safe to preload anywhere.

const WORKER_NAMES := ["Jun", "Mara", "Kai", "Lia", "Ren", "Sia", "Nia", "Tao", "Yun", "Zoe"]
const WORKER_NAMES := ["Jun", "Mara"]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Major: Worker names reduced from 10 to 2 without documentation in PR body or issue PR 230; this is a gameplay-affecting change that should be in a separate PR.

Automated finding from AI PR review.


const BASE_TICK_SECONDS := 0.9
const EVENT_INTERVAL_TICKS := 66
Expand Down Expand Up @@ -40,14 +40,6 @@ const TILE_BACKDROPS := {
const WORKER_BADGE_COLORS := {
"Jun": Color("#f58f6c"),
"Mara": Color("#75c7ff"),
"Kai": Color("#a3e635"),
"Lia": Color("#f472b6"),
"Ren": Color("#facc15"),
"Sia": Color("#c084fc"),
"Nia": Color("#fb923c"),
"Tao": Color("#34d399"),
"Yun": Color("#60a5fa"),
"Zoe": Color("#f87171"),
}

const BUILD_COSTS := {
Expand Down
74 changes: 64 additions & 10 deletions scripts/main.gd
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ const GoalProgression := preload("res://scripts/goal_progression.gd")
const GoalReward := preload("res://scripts/goal_reward.gd")
const RESOURCE_TRENDS := Constants.RESOURCE_TRENDS
const ColonyStance := preload("res://scripts/colony_stance.gd")
const WorkerRenderer := preload("res://scripts/worker_renderer.gd")


@onready var world_grid: GridContainer = %WorldGrid
Expand Down Expand Up @@ -67,6 +66,7 @@ var food_upkeep_tracker := 0
var rng := RandomNumberGenerator.new()
var prev_resources: Dictionary = {}
var tick_timer: Timer
var worker_texture_cache: Dictionary = {}
var pending_build_kind := ""
var priority_order: Array[String] = ["build", "haul", "gather"]
var colony_stance := ColonyStance.STANCE_BALANCED
Expand Down Expand Up @@ -1449,12 +1449,10 @@ func choose_task(worker: Dictionary) -> Dictionary:
var tasks: Array[Dictionary] = tasks_for_kind(String(kind))
if tasks.is_empty():
continue
# Bias toward food gathering when food is low (issue #147) or the food
# stance has injected a gather_food task. Both paths share the same
# food-first comparator because gather_gather_tasks() emits
# kind="gather" tasks with a resource field, so is_food_gather_task()
# works uniformly across them (issue #245).
if (String(kind) == "gather" and should_bias_to_food_gathering()) or String(kind) == "gather_food":
# Bias toward food gathering when food is low (issue #147) or food stance active
var should_sort_by_food := String(kind) == "gather_food" or \
(String(kind) == "gather" and should_bias_to_food_gathering())
if should_sort_by_food:
tasks.sort_custom(func(a: Dictionary, b: Dictionary) -> bool:
var a_is_food := ColonyStance.is_food_gather_task(a)
var b_is_food := ColonyStance.is_food_gather_task(b)
Expand Down Expand Up @@ -1906,7 +1904,7 @@ func render_worker_overlay() -> void:
sprite.custom_minimum_size = Vector2(int(tile_size.x * 0.96), int(tile_size.y * 1.08))
sprite.size = sprite.custom_minimum_size
sprite.visible = true
sprite.texture = WorkerRenderer.worker_texture(name, worker_anim_frame(worker), carried_resource(worker))
sprite.texture = worker_texture(name, worker_anim_frame(worker), carried_resource(worker))
var from_pos := data_to_vec(worker.get("prev_pos", worker.get("pos", vec_to_data(stockpile_pos))))
var to_pos := data_to_vec(worker.get("pos", vec_to_data(stockpile_pos)))
var from_center := tile_center(from_pos)
Expand Down Expand Up @@ -2173,7 +2171,7 @@ func render_worker_sprites(container: HBoxContainer, workers_here: Array) -> voi
sprite.custom_minimum_size = Vector2(int(tile_size.x * 0.62), int(tile_size.y * 0.7))
sprite.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
sprite.stretch_mode = TextureRect.STRETCH_SCALE
sprite.texture = WorkerRenderer.worker_texture(String(worker.name), worker_anim_frame(worker), carried_resource(worker))
sprite.texture = worker_texture(String(worker.name), worker_anim_frame(worker), carried_resource(worker))
container.add_child(sprite)

func carried_resource(worker: Dictionary) -> String:
Expand All @@ -2191,6 +2189,62 @@ func worker_anim_frame(worker: Dictionary) -> int:
return 0 if tick % 10 < 5 else 1
return tick % 2

func worker_texture(name: String, frame: int, carrying: String = "") -> Texture2D:
var cache_key := "%s:%d:%s" % [name, frame, carrying]
if worker_texture_cache.has(cache_key):
return worker_texture_cache[cache_key]
var accent: Color = WORKER_BADGE_COLORS.get(name, Color.WHITE)
var shadow := accent.darkened(0.45)
var skin := Color("#f2d0b1")
var image := Image.create(12, 14, false, Image.FORMAT_RGBA8)
image.fill(Color(0, 0, 0, 0))

# head
for y in range(0, 4):
for x in range(4, 8):
image.set_pixel(x, y, skin)

# body
for y in range(4, 10):
for x in range(3, 9):
image.set_pixel(x, y, accent)

# arms
for y in range(5, 9):
image.set_pixel(2, y, shadow)
image.set_pixel(9, y, shadow)

if not carrying.is_empty():
var cargo_color := Color("#9aa3aa")
if carrying == "wood":
cargo_color = Color("#8b5a2b")
elif carrying == "food":
cargo_color = Color("#6fbf73")
for y in range(5, 9):
for x in range(0, 3):
image.set_pixel(x, y, cargo_color)
image.set_pixel(1, 4, cargo_color.lightened(0.25))

# legs alternate per frame for a simple walk bob
if frame % 2 == 0:
image.set_pixel(4, 10, shadow)
image.set_pixel(4, 11, shadow)
image.set_pixel(7, 10, shadow)
image.set_pixel(7, 11, shadow)
else:
image.set_pixel(5, 10, shadow)
image.set_pixel(4, 11, shadow)
image.set_pixel(6, 10, shadow)
image.set_pixel(7, 11, shadow)

# feet
image.set_pixel(3, 13, shadow)
image.set_pixel(7, 13, shadow)

var texture := ImageTexture.create_from_image(image)
worker_texture_cache[cache_key] = texture
return texture

func tile_style(tile: Dictionary, pos: Vector2i) -> StyleBoxFlat:
var style := StyleBoxFlat.new()
style.corner_radius_top_left = 8
Expand Down Expand Up @@ -2369,7 +2423,7 @@ func render_event_drawer() -> void:

func push_event(text: String) -> void:
state.events.push_front({"tick": tick, "text": text})
while state.events.size() > 20:
while state.events.size() > 8:
state.events.pop_back()
_mark_dirty()

Expand Down
73 changes: 0 additions & 73 deletions scripts/worker_renderer.gd

This file was deleted.

2 changes: 2 additions & 0 deletions tests/test_colony_stance.gd
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,12 @@ func _initialize() -> void:
var food_task := {"kind": "gather", "resource": "food"}
var wood_task := {"kind": "gather", "resource": "wood"}
var haul_task := {"kind": "haul", "resource": "wood"}
var gather_food_task := {"kind": "gather_food", "resource": "food"}

_assert(stance_script.is_food_gather_task(food_task), "food gather: food task detected")
_assert(not stance_script.is_food_gather_task(wood_task), "food gather: wood task not detected")
_assert(not stance_script.is_food_gather_task(haul_task), "food gather: haul task not detected")
_assert(stance_script.is_food_gather_task(gather_food_task), "food gather: gather_food kind detected")

# ── Test 8: All stances defined ──
print("")
Expand Down
Loading
Loading