Skip to content

Decouple Fuel System using GameSettingsResource and Signals#534

Merged
ikostan merged 79 commits intomainfrom
convert-hard-coded-fuel-elements-to-godot-resource
Apr 10, 2026
Merged

Decouple Fuel System using GameSettingsResource and Signals#534
ikostan merged 79 commits intomainfrom
convert-hard-coded-fuel-elements-to-godot-resource

Conversation

@ikostan
Copy link
Copy Markdown
Owner

@ikostan ikostan commented Apr 7, 2026

📦 PR Summary

Introduce a centralized fuel subsystem via GameSettingsResource: adds max_fuel, current_fuel, base_consumption_rate, a refuel() helper, and signals (setting_changed, fuel_depleted). Persist/load fuel settings in globals.gd and validate types when loading. Update player.gd to read/write fuel from Globals.settings, connect to setting_changed and fuel_depleted, and move consumption calculations to use the new resource values; UI updates are driven by the resource signals. Add GUT tests covering the resource behavior, UI reactivity, and integration/persistence edge cases (including signal emission and invalid config handling). Clamping and one-time depletion signaling are enforced.

Overview
This PR migrates the fuel system from a hard-coded local state into a robust, globally accessible Godot Resource (GameSettingsResource). In doing so, it establishes a new standard for data management, UI reactivity, and defensive programming across the project.

Key Features & Architectural Changes:

  • The Observer Pattern: Decoupled the UI and Player logic from the _process loop. UI elements and player states now update instantly and automatically by listening to the setting_changed and fuel_depleted signals emitted by the global resource.
  • Defensive "Fail-Safe" Architecture: Implemented strict is_instance_valid() null guards across hot paths (_physics_process in player.gd, _process in main_scene.gd) to prevent engine crashes during scene transitions or isolated tests.
  • Zombie Node & Split-Brain Prevention: The Player node now safely generates and injects a fallback GameSettingsResource into the Globals singleton if the primary settings fail to load, ensuring all systems stay synchronized.
  • Refined Persistence Logic: Separated volatile session data from permanent upgrades. max_fuel and difficulty are saved to the player's disk, while current_fuel correctly resets on load to prevent inescapable 0-fuel death loops.
  • Monotonic Threshold Validation: The color-changing UI thresholds (High, Medium, Low, Empty) now automatically clamp against each other in the resource setters, mathematically preventing broken UI states.
  • Engine Reignite Logic: Fixed a state-machine bug where refueling a dead airplane wouldn't restart the engine. The player script now actively detects refuels from 0 and reignites the rotors and consumption timers.
  • Comprehensive GUT Testing: Built out a robust suite of unit and integration tests (test_fuel_edge_cases.gd, test_fuel_persistence_integration.gd, etc.) to definitively prove signal constraints, persistence fallbacks, and UI reactivity.

name: Default Pull Request Template
about: Suggesting changes to SkyLockAssault
title: ''
labels: ''
assignees: ''

Description

What does this PR do? (e.g., "Fixes player jump physics in level 2" or "Adds
new enemy AI script")

Related Issue

Closes #ISSUE_NUMBER (if applicable)

Changes

  • List key changes here (e.g., "Updated Jump.gd to use Godot 4.4's new Tween
    system")
  • Any breaking changes? (e.g., "Deprecated old signal; migrate to new one")

Testing

  • Ran the game in Godot v4.5 editor—describe what you tested (e.g., "Jump
    works on Win10 with 60 FPS")
  • Any new unit tests added? (Link to test scene if yes)
  • Screenshots/GIFs if UI-related: (Attach below)

Checklist

  • Code follows Godot style guide (e.g., snake_case for variables)
  • No console errors in editor/output
  • Ready for review!

Additional Notes

Anything else? (e.g., "Tested on Win10 64-bit; needs Linux validation")


🤖 Bot & AI Contributions (Updated)

This pull request features extensive automated support from AI agents and development bots, with DeepSource specifically ensuring code health through rigorous static analysis.

Summary of Activity

  • Architectural Refactoring & Summarization: AI tools assisted in centralizing the fuel logic within GameSettingsResource. Automated summaries provided clear visibility into how the fuel system integration impacts player behavior, UI reactivity, and state persistence.
  • Bug Detection & Quality Assurance (DeepSource & AI Agents):
    • Static Analysis (DeepSource): Performed continuous code scanning to detect bug risks, maintainability issues, and anti-patterns within the 64 new commits.
    • Recursive Setter Identification: AI analysis flagged a critical bug in GameSettingsResource.current_fuel where a recursive setter would have caused a stack overflow at runtime.
    • State Divergence Resolution: AI feedback guided the removal of redundant local variables in player.gd, forcing the system to read directly from Globals.settings to prevent UI/logic desynchronization.
    • Null Safety: AI reviews prompted the implementation of null checks for Globals.settings in hot paths (like _physics_process) to prevent crashes during initialization.
  • Test Suite Optimization: Automated reviews identified syntax errors in the new test infrastructure, such as duplicate headers in test_fuel_persistence_integration.gd and member variable declaration issues in edge case tests.
  • Workflow Automation: Bots managed the categorization of the 64 commits and synchronized the PR status with the project's feature tracking (e.g., linking and addressing Issue [FEATURE] Add Signals for Fuel System in Player.gd #278).

AI & Bot Contributors

The following automated entities provided significant technical contributions and quality assurance for this PR:

  • @deepsource-io – Conducted static analysis to ensure code health, identifying risks and helping maintain high standards across the refactored GDScript files.
  • @sourcery-ai – Generated high-level architectural summaries and conducted deep-dive reviews to identify logic errors and refactoring opportunities.
  • @coderabbitai – Provided incremental code reviews, categorized new features vs. bug fixes, and flagged high-frequency debug noise for optimization.
  • @github-project-automation – Orchestrated the linking of features and issues within the project management board.

📝 Contributors List

To include these automated contributors in your GitHub attribution, you can use the following handles:

- [DeepSource](https://github.com/apps/deepsource) (@deepsource-io)
- [Sourcery AI](https://github.com/apps/sourcery-ai) (@sourcery-ai)
- [CodeRabbit AI](https://github.com/apps/coderabbitai) (@coderabbitai)
- [GitHub Project Automation](https://github.com/apps/github-project-automation) (@github-project-automation)

Summary by Sourcery

Centralize the fuel system in a shared GameSettingsResource and integrate it across player behavior, UI, persistence, and tests.

New Features:

  • Introduce a configurable fuel subsystem in GameSettingsResource, including max/current fuel, consumption rate, thresholds, a refuel helper, and fuel_depleted signaling.
  • Drive the player’s fuel behavior and UI from the shared fuel resource, including engine flameout handling when fuel is depleted.

Bug Fixes:

  • Prevent log spam and excessive disk writes by excluding current_fuel changes from automatic logging and autosave.
  • Ensure fuel bar limits and color calculations remain correct when max_fuel changes or is misconfigured in saved settings.
  • Avoid crashes and leaks by validating Globals/settings presence, guarding against invalid instances, and disconnecting player signal handlers on exit.

Enhancements:

  • Persist and load fuel-related settings with type validation and safe fallbacks for missing or invalid config values.
  • Refactor fuel thresholds, consumption, and depletion calculations from the player script into the centralized settings resource.
  • Update background scroll and other systems to reference global fuel state instead of local player fuel fields.

Tests:

  • Add extensive GUT and GdUnit tests for the fuel resource, persistence edge cases, UI reactivity to signals, difficulty-based fuel scaling, and player lifecycle signal cleanup.
  • Augment existing difficulty and player tests to use the new fuel resource API and dynamic max_fuel rather than hardcoded values.

Summary by Sourcery

Centralize the fuel system in a shared GameSettingsResource and integrate it with the player, globals persistence, and tests.

New Features:

  • Introduce a fuel subsystem in GameSettingsResource, including capacity, current fuel, consumption rate, color thresholds, refuel helper, and fuel_depleted signal.
  • Drive player fuel behavior and UI entirely from the shared settings resource, including engine shutdown on depletion and reactive HUD updates via signals.

Bug Fixes:

  • Prevent log spam and unnecessary disk writes by excluding high-frequency current_fuel updates from automatic logging and saving.
  • Ensure new resources and player instances always start with a full fuel tank to avoid bad save-state loops and uninitialized fuel values.
  • Clamp fuel capacity and level to valid ranges to avoid negative fuel, zero-capacity tanks, and repeated depletion events.

Enhancements:

  • Refactor fuel-related logic out of player.gd into the settings resource, making fuel thresholds, capacity, and consumption configurable from a single location.
  • Add defensive handling for missing Globals.settings and clean signal disconnection in the player lifecycle to avoid crashes and dangling listeners.
  • Align main_scene background behavior and movement controls with the centralized fuel state instead of local player dictionaries.

Tests:

  • Extend existing GdUnit tests to use the centralized fuel settings and dynamic max_fuel instead of hardcoded values.
  • Add GUT test suites for fuel resource behavior, signal emission, UI reactivity, persistence (including invalid/missing config handling), and player lifecycle cleanup.

Summary by CodeRabbit

  • New Features
    • Centralized Fuel System: global max/current fuel, base consumption rate, refuel helper, and a fuel-depleted signal that fires once on transition to empty; player spawns with full tank and UI/engine react to global fuel.
  • Bug Fixes
    • Fuel values clamp to valid ranges; max reductions adjust current fuel; settings persist on quit; ignored invalid persisted types with a warning; suppressed high-frequency debug/log noise for runtime fuel ticks.
  • Tests
    • Extensive unit and integration tests for resource behavior, UI reactivity, edge cases, signal emission, scaling, and persistence.

Introduce a centralized fuel subsystem via GameSettingsResource: adds max_fuel, current_fuel, base_consumption_rate, a refuel() helper, and signals (setting_changed, fuel_depleted). Persist/load fuel settings in globals.gd and validate types when loading. Update player.gd to read/write fuel from Globals.settings, connect to setting_changed and fuel_depleted, and move consumption calculations to use the new resource values; UI updates are driven by the resource signals. Add GUT tests covering the resource behavior, UI reactivity, and integration/persistence edge cases (including signal emission and invalid config handling). Clamping and one-time depletion signaling are enforced.
@ikostan ikostan self-assigned this Apr 7, 2026
@ikostan ikostan added enhancement New feature or request mechanics labels Apr 7, 2026
@ikostan ikostan linked an issue Apr 7, 2026 that may be closed by this pull request
@ikostan ikostan moved this to In Progress in Sky Lock Assault Project Apr 7, 2026
@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai Bot commented Apr 7, 2026

Reviewer's Guide

Centralizes the fuel system in GameSettingsResource, rewires player/main_scene and globals persistence to use it as the single source of truth, and adds GUT/GdUnit tests for fuel behavior, UI reactivity, lifecycle cleanup, and persistence edge cases.

Sequence diagram for fuel consumption, depletion, and player/MainScene reactions

sequenceDiagram
    participant FuelTimer as FuelTimer
    participant Player as Player
    participant Settings as GameSettingsResource
    participant Globals as Globals
    participant UI as FuelUI
    participant MainScene as MainScene

    FuelTimer->>Player: _on_fuel_timer_timeout()
    activate Player
    Player->>Settings: current_fuel -= consumption
    deactivate Player

    alt current_fuel clamped and changed
        Settings-->>Player: setting_changed(current_fuel, value)
        activate Player
        Player->>UI: update_fuel_bar()
        Player->>UI: check_fuel_warning()
        deactivate Player
    end

    alt fuel reached 0 from positive value
        Settings-->>Player: fuel_depleted
        activate Player
        Player->>Player: _on_player_out_of_fuel()
        Player->>UI: stop rotors, stop fuel_timer
        deactivate Player
    end

    MainScene->>Globals: read settings
    Globals-->>MainScene: settings reference
    MainScene->>Settings: read current_fuel and difficulty
    alt current_fuel <= 0
        MainScene->>MainScene: stop background scroll
    else current_fuel > 0
        MainScene->>MainScene: scroll background based on speed and difficulty
    end
Loading

Class diagram for centralized fuel system in GameSettingsResource and player integration

classDiagram
    class GameSettingsResource {
        <<Resource>>
        +signal setting_changed(setting_name: String, new_value: Variant)
        +signal fuel_depleted
        +max_fuel: float
        +current_fuel: float
        +base_consumption_rate: float
        +high_fuel_threshold: float
        +medium_fuel_threshold: float
        +low_fuel_threshold: float
        +no_fuel_threshold: float
        +current_log_level: int
        +difficulty: float
        +enable_debug_logging: bool
        +key_mapping_scene: PackedScene
        +options_scene: PackedScene
        -_max_fuel: float
        -_current_fuel: float
        -_high_fuel_threshold: float
        -_medium_fuel_threshold: float
        -_low_fuel_threshold: float
        -_no_fuel_threshold: float
        -_current_log_level: int
        -_difficulty: float
        -_enable_debug_logging: bool
        +_init(): void
        +refuel(amount: float): void
    }

    class Globals {
        <<Autoload>>
        +settings: GameSettingsResource
        +_is_loading_settings: bool
        +_on_setting_changed(setting_name: String, new_value: Variant): void
        +_load_settings(path: String): void
        +_save_settings(path: String): void
        +log_message(message: String, level: int): void
        +_notification(what: int): void
    }

    class Player {
        <<Node2D>>
        +lateral_speed: float
        +acceleration: float
        +deceleration: float
        +screen_size: Vector2
        +player_x_min: float
        +player_x_max: float
        +player_y_min: float
        +player_y_max: float
        +fuel: Dictionary
        +speed: Dictionary
        -_settings: GameSettingsResource
        +_ready(): void
        +_exit_tree(): void
        +_input(event: InputEvent): void
        +_physics_process(delta: float): void
        +_on_setting_changed(setting_name: String, new_value: Variant): void
        +_on_player_out_of_fuel(): void
        +_on_fuel_timer_timeout(): void
        +update_fuel_bar(): void
        +check_fuel_warning(): void
        +update_speed_bar(): void
        +check_speed_warning(): void
        +rotor_start(rotor: Node2D, rotor_sfx: AudioStreamPlayer2D): void
        +rotor_stop(rotor: Node2D, rotor_sfx: AudioStreamPlayer2D): void
    }

    class MainScene {
        <<Node>>
        +player: Player
        +background: ParallaxBackground
        +setup_decor_layer(viewport: Vector2): void
        +_process(delta: float): void
    }

    GameSettingsResource <.. Globals : uses
    Globals o-- GameSettingsResource : settings
    Player ..> Globals : reads_writes_settings
    Player ..> GameSettingsResource : caches__settings
    Player ..> GameSettingsResource : connects_setting_changed
    Player ..> GameSettingsResource : connects_fuel_depleted
    MainScene ..> Globals : reads_settings
    MainScene ..> GameSettingsResource : reads_current_fuel_and_difficulty
Loading

Flow diagram for settings change, logging, and persistence with fuel filtering

flowchart TD
    subgraph SettingsChangeFlow
        A["GameSettingsResource\nproperty change"] --> B["Emit setting_changed"]
        B --> C["Globals._on_setting_changed"]
        C --> D{Setting_name_is_current_fuel?}
        D -- Yes --> E["Skip_logging_and_saving\navoid_log_spam_and_disk_IO"]
        D -- No --> F["log_message at DEBUG"]
        F --> G["_save_settings to disk"]
    end
Loading

File-Level Changes

Change Details Files
Centralize fuel configuration and state in GameSettingsResource, including thresholds, consumption, refuel helper, and signals.
  • Add exported properties for max_fuel, current_fuel, base_consumption_rate, and color thresholds with backing fields and validation/clamping logic.
  • Emit setting_changed on relevant property updates and a fuel_depleted signal when current_fuel transitions from >0 to 0, ensuring single emission.
  • Initialize new resources with a full tank and provide a refuel(amount) helper that clamps to max_fuel.
scripts/game_settings_resource.gd
Refactor player to consume and display fuel from the shared GameSettingsResource and respond to its signals.
  • Cache Globals.settings as _settings in _ready with fallback creation and propagate it back into Globals to avoid split state; guard usages with is_instance_valid checks.
  • Remove local fuel constants and fields, read max_fuel/current_fuel/thresholds from _settings, and reset current_fuel to max_fuel on spawn; treat fuel dict as UI-only.
  • Connect to setting_changed and fuel_depleted to drive fuel bar updates, warning blinking, engine flameout (rotor stop, speed zero, timer stop), and engine reignition on refuel.
  • Move fuel consumption into _on_fuel_timer_timeout using base_consumption_rate and difficulty from _settings and stop doing per-tick UI/log updates directly.
  • Ensure _physics_process uses _settings.current_fuel for movement gating and add _exit_tree cleanup that disconnects the resource signals safely.
scripts/player.gd
Wire main_scene and globals to the centralized fuel settings and improve persistence/load behavior.
  • Make main_scene.gd use a local GameSettingsResource reference for difficulty and current_fuel, early-returning when settings are missing, and stop background scrolling when fuel is depleted based on global current_fuel.
  • Update globals.gd setting_changed handler to skip logging and autosave for current_fuel to avoid log spam and disk I/O churn.
  • Persist and load max_fuel in _save_settings/_load_settings with type validation and warnings on invalid types, and force a settings save on quit in _notification.
scripts/main_scene.gd
scripts/globals.gd
Adapt existing GdUnit tests to the centralized fuel system and dynamic max_fuel.
  • Snapshot and restore max_fuel/current_fuel around difficulty-related tests to prevent state leakage.
  • Update depletion expectations in test_difficulty.gd, test_difficulty_integration.gd, test_player.gd, and test_helpers.gd to use Globals.settings.base_consumption_rate and Globals.settings.current_fuel/max_fuel instead of player-local fuel fields and hardcoded 100.
  • Adjust fuel color and blinking tests to derive percentages from max_fuel and operate through the shared resource rather than directly mutating player.fuel["fuel"].
test/gdunit4/test_difficulty.gd
test/gdunit4/test_difficulty_integration.gd
test/gdunit4/test_player.gd
test/gdunit4/test_helpers.gd
Add GUT test suites covering fuel resource behavior, UI reactivity, lifecycle cleanup, scaling, and persistence edge cases.
  • Introduce tests for GameSettingsResource defaults, deterministic consumption, clamping to zero, and refuel behavior, including negative inputs.
  • Add tests validating that fuel_depleted emits exactly once when transitioning to zero, and that setting_changed can be used to drive ProgressBar updates without orphaned listeners.
  • Cover persistence of max_fuel (valid, invalid type, and missing key cases) using isolated config files and ensuring in-memory defaults are preserved on bad data.
  • Add player lifecycle tests ensuring player connects and disconnects fuel-related signals correctly in _ready/_exit_tree and handles missing Globals.settings without crashing.
  • Add additional integration tests for fuel consumption scaling with speed and difficulty, and UI reactivity helper tests wired to the new signals; include corresponding .uid files for new GUT scripts.
test/gut/test_fuel_resource.gd
test/gut/test_fuel_persistence_integration.gd
test/gut/test_fuel_integration.gd
test/gut/test_fuel_ui.gd
test/gut/test_player_lifecycle.gd
test/gut/test_fuel_edge_cases.gd
test/gut/test_fuel_additional_edge_cases.gd
test/gut/test_fuel_additional_edge_cases.gd.uid
test/gut/test_fuel_edge_cases.gd.uid
test/gut/test_fuel_integration.gd.uid
test/gut/test_fuel_persistence_integration.gd.uid
test/gut/test_fuel_resource.gd.uid
test/gut/test_fuel_ui.gd.uid
test/gut/test_player_lifecycle.gd.uid

Assessment against linked issues

Issue Objective Addressed Explanation
#278 Introduce a signal-based fuel system API (fuel change/depletion events) to decouple fuel state from consumers (Player/UI/GameManager), instead of direct polling or tight coupling in Player.gd.
#278 Refactor Player.gd fuel management to integrate with the new signal-based fuel system, emitting/consuming appropriate events for fuel consumption, depletion, and refuel-driven behavior changes.
#278 Add or update tests and example integrations (e.g., UI) to verify fuel-related signal emissions and reactions, including persistence and edge cases.
#281 Centralize fuel-related configuration (max fuel, consumption rate, and threshold values) into a Godot Resource instead of hard-coded constants in player.gd.
#281 Update player.gd to use the shared fuel resource (GameSettingsResource/Globals.settings) for all fuel calculations and UI, removing direct hard-coded fuel values from the player script.
#281 Ensure fuel-related changes emit signals so other systems (e.g., UI, player behavior) can react to fuel updates and depletion without manual polling.
#533 Add unit tests for the Fuel System core logic (GameSettingsResource), covering initialization to max capacity, deterministic fuel consumption via explicit delta steps, clamping to zero (no negative fuel), and refuel behavior including clamping to max and ignoring negative refuel inputs.
#533 Add integration tests for the Fuel System with Globals and persistence, covering fuel_depleted signal emission exactly once on transition >0→0 and not on further drains, and persistence behavior for max_fuel across valid configs, missing keys, and invalid types using isolated test config files.
#533 Add UI-level tests that verify UI elements (e.g., ProgressBar) update reactively in response to fuel-related signals (setting_changed/current_fuel and max_fuel), without directly asserting core logic.

Possibly linked issues

  • #[QA] Fuel System Test Plan (GUT): The PR implements the fuel system and adds GUT tests for initialization, consumption, clamping, signals, persistence, and UI per plan.
  • #unknown: PR fulfills the issue by moving fuel constants into GameSettingsResource, wiring player usage, adding thresholds and signals.
  • #: PR fulfills the issue’s goal by adding fuel signals (setting_changed, fuel_depleted) and wiring Player/UI to them.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@ikostan ikostan linked an issue Apr 7, 2026 that may be closed by this pull request
Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 2 issues, and left some high level feedback:

  • When max_fuel changes, consider re-clamping current_fuel (e.g., in the max_fuel setter) so that lowering the max capacity cannot leave current_fuel temporarily above the new maximum.
  • The player’s _on_setting_changed handler only reacts to current_fuel; you might also handle max_fuel there (or elsewhere) to keep fuel_bar.max_value in sync with upgrades or settings changes instead of only initializing it in _ready.
  • The fuel dictionary in player.gd still contains fuel/max entries that are no longer updated or used consistently now that fuel comes from Globals.settings; consider either removing these keys or keeping them in sync to avoid confusion and accidental use of stale values.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- When `max_fuel` changes, consider re-clamping `current_fuel` (e.g., in the `max_fuel` setter) so that lowering the max capacity cannot leave `current_fuel` temporarily above the new maximum.
- The player’s `_on_setting_changed` handler only reacts to `current_fuel`; you might also handle `max_fuel` there (or elsewhere) to keep `fuel_bar.max_value` in sync with upgrades or settings changes instead of only initializing it in `_ready`.
- The `fuel` dictionary in `player.gd` still contains `fuel`/`max` entries that are no longer updated or used consistently now that fuel comes from `Globals.settings`; consider either removing these keys or keeping them in sync to avoid confusion and accidental use of stale values.

## Individual Comments

### Comment 1
<location path="scripts/player.gd" line_range="221-224" />
<code_context>


+# NEW: Observer pattern handler to react when GameSettingsResource properties (like fuel) are updated externally.
+func _on_setting_changed(setting_name: String, _value: Variant) -> void:
+	if setting_name == "current_fuel":
+		update_fuel_bar()
+		check_fuel_warning()
+
+
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Handle `max_fuel` changes so the fuel bar’s max stays in sync with the settings resource.

`_on_setting_changed` only handles `"current_fuel"`, so changes to `max_fuel` (from options, save/load, cheats, etc.) leave `fuel_bar.max_value` stale. Either handle `"max_fuel"` here to update `fuel_bar.max_value`, or have `update_fuel_bar()` always read `Globals.settings.max_fuel` and apply it so the bar stays in sync.

Suggested implementation:

```
func _on_setting_changed(setting_name: String, _value: Variant) -> void:
	if setting_name == "current_fuel":
		update_fuel_bar()
		check_fuel_warning()
	elif setting_name == "max_fuel":
		# Keep the UI fuel bar max in sync with the GameSettingsResource.
		fuel_bar.max_value = Globals.settings.max_fuel
		# Re-run the standard UI update so the current fuel is represented correctly
		# relative to the new maximum.
		update_fuel_bar()

```

If there are other settings that can affect how the fuel bar is displayed (e.g. a percentage/scale setting), they can also be handled in `_on_setting_changed` using additional `elif` branches, reusing `update_fuel_bar()` to keep the behavior centralized.
</issue_to_address>

### Comment 2
<location path="scripts/player.gd" line_range="317" />
<code_context>
+	var m_fuel: float = Globals.settings.max_fuel
+	
+	fuel["bar"].value = cur_fuel
+	var fuel_percent: float = (cur_fuel / m_fuel) * 100.0

 	if fuel_percent > HIGH_FUEL_THRESHOLD:
</code_context>
<issue_to_address>
**issue (bug_risk):** Guard against division by zero when computing `fuel_percent` if `max_fuel` is 0.

If `Globals.settings.max_fuel` is ever `0.0` (e.g., via config or test setup), this will raise a division-by-zero error. Consider returning early when `m_fuel <= 0.0` or explicitly setting `fuel_percent` to `0.0` in that case.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread scripts/player.gd Outdated
Comment thread scripts/player.gd Outdated
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 7, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Moves fuel system from local player state to global GameSettingsResource with persistence and signaling. Implements fuel_depleted signal, threshold exports, clamping logic, and refuel helper. Updates Player to consume global fuel, adjusts Globals persistence to load/save fuel, and adds extensive test coverage for signal emission, edge cases, and persistence behavior.

Changes

Cohort / File(s) Summary
Core Fuel System
scripts/game_settings_resource.gd
Added fuel_depleted signal, exported fuel properties (max_fuel, current_fuel, base_consumption_rate), threshold exports with setter/getter logic that emits setting_changed, and refuel() helper. Implements clamping to [0, max_fuel] with special handling for transition to zero.
Global Persistence & Events
scripts/globals.gd
Extended _load_settings() to restore max_fuel and current_fuel from config with type validation. Updated _save_settings() to persist fuel values. Added suppression of DEBUG logging and disk writes for current_fuel changes to prevent high-frequency tick spam.
Gameplay Integration
scripts/player.gd, scripts/main_scene.gd
Removed local fuel state and constants. Player now caches global settings reference, computes fuel consumption from base_consumption_rate with speed/difficulty scaling, and connects to fuel_depleted signal for engine-failure logic. Main scene checks global current_fuel for depletion instead of local dictionary.
Test Infrastructure
test/gdunit4/test_helpers.gd, test/gdunit4/test_difficulty.gd, test/gdunit4/test_difficulty_integration.gd, test/gdunit4/test_player.gd
Updated depletion calculation to derive from Globals.settings.base_consumption_rate. Adjusted test fixtures to snapshot/restore global fuel state around tests. Updated assertions to verify global current_fuel instead of local player.fuel dictionary.
New Fuel-Focused Unit Tests
test/gut/test_fuel_resource.gd, test/gut/test_fuel_ui.gd, test/gut/test_fuel_edge_cases.gd, test/gut/test_fuel_additional_edge_cases.gd
New GUT test suites covering GameSettingsResource initialization, clamping, consumption scaling, refuel logic, signal emission exactly-once behavior, and UI reactivity to fuel changes via setting_changed signal.
Integration & Lifecycle Tests
test/gut/test_fuel_integration.gd, test/gut/test_fuel_persistence_integration.gd, test/gut/test_player_lifecycle.gd
New integration tests validating fuel_depleted signal emission constraints, disk persistence (valid/invalid/missing key fallback), UI updates via signals, and Player._exit_tree() signal disconnection safety when Globals.settings is missing.
Scene & UID Metadata
scenes/main_scene.tscn, test/gut/*.gd.uid
Updated main scene Player reference path from lowercase to Pascal case. Added UID metadata files for all new test scripts.

Sequence Diagram

sequenceDiagram
    participant Player
    participant GameSettingsResource as GlobalSettings
    participant Globals
    participant UINode as UI/ProgressBar

    Player->>GameSettingsResource: _on_fuel_timer_timeout()<br/>current_fuel -= depletion
    GameSettingsResource->>GameSettingsResource: Clamp to [0, max_fuel]
    alt current_fuel crosses 0.0
        GameSettingsResource->>Player: emit fuel_depleted
        Player->>Player: _on_player_out_of_fuel()<br/>stop engines
    else current_fuel changes (not zero)
        GameSettingsResource->>Player: emit setting_changed("current_fuel")
        Player->>Player: Update UI colors/bars
    end
    
    GameSettingsResource->>UINode: emit setting_changed("current_fuel")
    UINode->>UINode: Update progress bar value

    rect rgba(100, 150, 200, 0.5)
    Note over Globals,Globals: On Quit or Manual Save
    Globals->>Globals: _save_settings()
    Globals->>Globals: Write current_fuel to config
    Globals->>Globals: Write max_fuel to config
    end

    rect rgba(100, 150, 200, 0.5)
    Note over Globals,GameSettingsResource: On Game Load
    Globals->>Globals: _load_settings()
    Globals->>GameSettingsResource: settings.current_fuel = loaded_value
    Globals->>GameSettingsResource: settings.max_fuel = loaded_value
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

Possibly related PRs

Suggested labels

testing, gui

Poem

🐰 A fluffy refactor, oh what a sight,
Global fuel flows where locals once were tight,
Signals emitted when reserves deplete,
Persistence saves the day, so bittersweet!
Tests multiply like carrots in spring. 🥕✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed The title 'Decouple Fuel System using GameSettingsResource and Signals' directly and precisely describes the primary objective of the changeset: migrating fuel system logic from local player state to a centralized GameSettingsResource with signal-based communication.
Description check ✅ Passed The PR description comprehensively covers objectives, architecture changes, key features, testing strategy, and includes structured sections matching the template's intent.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch convert-hard-coded-fuel-elements-to-godot-resource

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@deepsource-io
Copy link
Copy Markdown

deepsource-io Bot commented Apr 7, 2026

DeepSource Code Review

We reviewed changes in f8ee6de...f0bfd62 on this pull request. Below is the summary for the review, and you can see the individual issues we found as inline review comments.

See full review on DeepSource ↗

PR Report Card

Overall Grade   Security  

Reliability  

Complexity  

Hygiene  

Code Review Summary

Analyzer Status Updated (UTC) Details
Python Apr 10, 2026 12:52a.m. Review ↗
JavaScript Apr 10, 2026 12:52a.m. Review ↗

Important

AI Review is run only on demand for your team. We're only showing results of static analysis review right now. To trigger AI Review, comment @deepsourcebot review on this thread.

ikostan added 6 commits April 6, 2026 20:19
Read the fuel bar max value from the centralized GameSettingsResource (Globals.settings.max_fuel) instead of a hardcoded MAX_FUEL, ensuring UI capacity stays in sync with game settings. Also reset Globals.settings.current_fuel to max on player spawn so a new game always starts with a full tank. Minor comment formatting adjusted for clarity.
Ensure the player stops moving when fuel is depleted by resetting speed["speed"] to 0.0 inside _on_player_out_of_fuel (migrated from the previous fuel-timer location). Also update the GDUnit helper: rename the player param to player_root, compute a normalized_speed variable, and use Globals.settings.base_consumption_rate instead of a removed local base_fuel_drain so tests reflect the new global consumption resource.
@ikostan
Copy link
Copy Markdown
Owner Author

ikostan commented Apr 7, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 7, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@ikostan ikostan linked an issue Apr 7, 2026 that may be closed by this pull request
@ikostan
Copy link
Copy Markdown
Owner Author

ikostan commented Apr 7, 2026

This one is also addressed: #278

Introduce GUT unit tests covering fuel system edge cases: verify the 'fuel_depleted' signal emits exactly once when current_fuel transitions to zero, and ensure current_fuel is correctly saved to and loaded from a config file. Tests isolate filesystem using a user:// test path, stub Globals.log_message, reset Globals.settings between tests, and clean up the temporary config. Also add a UID file for the new test.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 9

🧹 Nitpick comments (3)
scripts/main_scene.gd (1)

192-196: Handle fuel exhaustion once instead of resetting the parallax every frame.

_on_player_out_of_fuel() already zeroes the player's speed, so _process() will naturally stop scrolling. This branch snaps scroll_offset back to (0, 0) and keeps reapplying it for the rest of the empty-fuel state. If the reset is intentional, drive it off Globals.settings.fuel_depleted; otherwise remove it so the background freezes in place.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/main_scene.gd` around lines 192 - 196, The branch in _process() that
forces background.scroll_offset = Vector2(0, 0) whenever
Globals.settings.current_fuel <= 0 repeatedly snaps the parallax every frame;
either remove this line so the background naturally stops when
_on_player_out_of_fuel() zeroes player speed, or change the condition to check a
one-time flag (Globals.settings.fuel_depleted) so the reset runs only once.
Locate the check using Globals.settings.current_fuel and modify it accordingly
(or delete the scroll_offset assignment) and ensure _on_player_out_of_fuel()
sets Globals.settings.fuel_depleted if you choose the one-time reset approach.
test/gut/test_fuel_integration.gd (1)

44-55: Also exercise the invalid current_fuel branch.

Globals._load_settings() now has a separate type guard for current_fuel, but this test only proves the fallback behavior for max_fuel. Adding the corrupt current_fuel case would lock down the second branch too.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/gut/test_fuel_integration.gd` around lines 44 - 55, Extend the
test_persistence_invalid_types_fallback test to also corrupt the current_fuel
entry and assert fallback behavior: after writing "corrupt_string_value" for
"current_fuel" into the same ConfigFile and saving at TEST_CONFIG_PATH, call
Globals._load_settings(TEST_CONFIG_PATH) and assert that
Globals.settings.current_fuel equals its default (capture it before corrupting)
to exercise the separate type guard for current_fuel in Globals._load_settings.
scripts/player.gd (1)

228-231: Also refresh the fuel bar when max_fuel changes.

fuel_bar.max_value is initialized from the resource in _ready(), but this observer ignores "max_fuel". If capacity is reloaded or tuned while the player is alive, the bar fill and value drift out of sync.

Possible adjustment
 func _on_setting_changed(setting_name: String, _value: Variant) -> void:
-	if setting_name == "current_fuel":
+	if setting_name == "max_fuel":
+		fuel_bar.max_value = Globals.settings.max_fuel
+		update_fuel_bar()
+		check_fuel_warning()
+	elif setting_name == "current_fuel":
 		update_fuel_bar()
 		check_fuel_warning()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/player.gd` around lines 228 - 231, The observer _on_setting_changed
currently only reacts to "current_fuel" changes; also handle "max_fuel" so the
UI stays in sync by updating fuel_bar.max_value and refreshing the display. In
_on_setting_changed (and anywhere settings reload), detect when setting_name ==
"max_fuel", set fuel_bar.max_value from the updated resource or Settings value,
then call update_fuel_bar() (and optionally check_fuel_warning()) to recompute
the filled fraction; reference the existing functions _on_setting_changed,
update_fuel_bar, check_fuel_warning, and the fuel_bar control to locate where to
apply the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/game_settings_resource.gd`:
- Around line 27-30: When lowering max_fuel in the setter for max_fuel, clamp
current_fuel to the new capacity and emit the appropriate events: capture the
previous current_fuel, set max_fuel = max(0.0, value), and if previous_current >
max_fuel then set current_fuel = max_fuel and emit setting_changed for
"current_fuel"; additionally if max_fuel == 0 and previous_current > 0 then emit
fuel_depleted. This ensures the capacity invariant and that fuel_depleted fires
when capacity is reduced to zero.

In `@scripts/globals.gd`:
- Around line 249-251: The change persists current_fuel on every settings write,
causing excessive I/O because Globals._on_setting_changed() is triggered by
runtime fuel ticks; remove or stop writing settings.current_fuel in the generic
save path (the two calls config.set_value("Settings","current_fuel",
settings.current_fuel)) and instead persist fuel only from an explicit
checkpoint/quit handler or via a debounced save routine; update Globals (e.g.,
_on_setting_changed or save_settings) to only write static settings like
max_fuel, and add a new checkpoint_save_current_fuel() or a debounced save tied
to player.gd fuel events to handle runtime current_fuel persistence.

In `@scripts/player.gd`:
- Around line 324-329: Protect the fuel percentage calculation from
division-by-zero by checking m_fuel before dividing: in the block that reads
Globals.settings.current_fuel into cur_fuel and Globals.settings.max_fuel into
m_fuel, compute fuel_percent only if m_fuel > 0 (e.g., set fuel_percent =
(cur_fuel / m_fuel) * 100.0); otherwise set fuel_percent to 0. Also clamp
fuel_percent into the 0..100 range before it is used by the color-threshold
logic so negative or overflow values cannot propagate (refer to variables
cur_fuel, m_fuel, fuel["bar"].value and fuel_percent).

In `@test/gdunit4/test_difficulty_integration.gd`:
- Around line 35-37: The test alters the global fuel state
(Globals.settings.current_fuel) but only restores difficulty, risking leakage
into other tests; update the teardown in test_difficulty_integration.gd to
snapshot and restore the fuel fields (e.g., save Globals.settings.current_fuel
and any related fuel fields before the test and restore them after), or instead
reset current_fuel deterministically using Globals.settings.max_fuel (e.g., set
current_fuel = min(previous_or_default, Globals.settings.max_fuel)) so the test
does not depend on max_fuel >= 100.0.

In `@test/gdunit4/test_difficulty.gd`:
- Around line 33-35: The test mutates the singleton
Globals.settings.current_fuel without restoring it and uses a hard-coded 100.0
baseline, which makes tests order-dependent; fix by snapshotting and restoring
the relevant fuel fields (e.g., save old_current = Globals.settings.current_fuel
and old_max = Globals.settings.max_fuel at test start and restore them in
teardown) or derive the reset value from Globals.settings.max_fuel (e.g., set
Globals.settings.current_fuel = Globals.settings.max_fuel) instead of using
100.0; apply the same change for the other occurrences that set
Globals.settings.current_fuel.

In `@test/gdunit4/test_player.gd`:
- Around line 86-99: The tests set Globals.settings.current_fuel to hardcoded
values (e.g., 95.0, 10.0) which assume a 100-unit tank; change these to scale
from the centralized capacity by using Globals.settings.max_fuel (e.g.,
Globals.settings.current_fuel = Globals.settings.max_fuel * 0.95, *0.7, *0.3, or
= Globals.settings.max_fuel - expected_depletion) before calling
player_root.update_fuel_bar(), and keep the same fuel_bar/StyleBoxFlat color
assertions (style_1/style_2) so the tests remain correct regardless of max_fuel;
apply the same replacement pattern to the other occurrences mentioned (around
the blocks using player_root.update_fuel_bar(), fuel_bar, style_1/style_2).

In `@test/gut/test_fuel_edge_cases.gd`:
- Around line 14-29: The tests overwrite the autoload Globals.settings in
before_each, breaking observer wiring from Globals._ready() for later suites;
save the original Globals.settings at the start of before_each (e.g.,
orig_settings = Globals.settings) and then restore it in after_each by assigning
Globals.settings = orig_settings and cleaning any stubs; ensure the saved symbol
is used to rewire the autoload rather than leaving a new GameSettingsResource
permanently in place.

In `@test/gut/test_fuel_resource.gd`:
- Around line 14-19: The test fixture is masking the resource's defaults by
setting both max_fuel and current_fuel in before_each(); to let
test_fuel_initialization() actually verify defaults, stop overriding
current_fuel in before_each() (only set max_fuel or necessary properties) or
initialize current_fuel to nil/unset so GameSettingsResource can provide its
default; update any other fixtures/tests that repeat this (the similar
assignments at lines referenced 25-27) to avoid pre-setting current_fuel so the
tests validate the resource's real default behavior (refer to before_each(),
GameSettingsResource, and test_fuel_initialization()).

In `@test/gut/test_fuel_ui.gd`:
- Around line 9-21: In test_ui_updates_on_fuel_change_signal capture the
previous fuel value (var prev_fuel = Globals.settings.current_fuel) before
assigning 45.5 and restore it at the end of the test
(Globals.settings.current_fuel = prev_fuel); place the restore in a
finally-style block (or just after the assert) so the global is always reset
even if the test fails; reference the test function name
test_ui_updates_on_fuel_change_signal and the Globals.settings.current_fuel
symbol to locate where to add the save-and-restore logic.

---

Nitpick comments:
In `@scripts/main_scene.gd`:
- Around line 192-196: The branch in _process() that forces
background.scroll_offset = Vector2(0, 0) whenever Globals.settings.current_fuel
<= 0 repeatedly snaps the parallax every frame; either remove this line so the
background naturally stops when _on_player_out_of_fuel() zeroes player speed, or
change the condition to check a one-time flag (Globals.settings.fuel_depleted)
so the reset runs only once. Locate the check using
Globals.settings.current_fuel and modify it accordingly (or delete the
scroll_offset assignment) and ensure _on_player_out_of_fuel() sets
Globals.settings.fuel_depleted if you choose the one-time reset approach.

In `@scripts/player.gd`:
- Around line 228-231: The observer _on_setting_changed currently only reacts to
"current_fuel" changes; also handle "max_fuel" so the UI stays in sync by
updating fuel_bar.max_value and refreshing the display. In _on_setting_changed
(and anywhere settings reload), detect when setting_name == "max_fuel", set
fuel_bar.max_value from the updated resource or Settings value, then call
update_fuel_bar() (and optionally check_fuel_warning()) to recompute the filled
fraction; reference the existing functions _on_setting_changed, update_fuel_bar,
check_fuel_warning, and the fuel_bar control to locate where to apply the
change.

In `@test/gut/test_fuel_integration.gd`:
- Around line 44-55: Extend the test_persistence_invalid_types_fallback test to
also corrupt the current_fuel entry and assert fallback behavior: after writing
"corrupt_string_value" for "current_fuel" into the same ConfigFile and saving at
TEST_CONFIG_PATH, call Globals._load_settings(TEST_CONFIG_PATH) and assert that
Globals.settings.current_fuel equals its default (capture it before corrupting)
to exercise the separate type guard for current_fuel in Globals._load_settings.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 3e7690cd-b820-4de8-8449-35bd20dcef31

📥 Commits

Reviewing files that changed from the base of the PR and between f8ee6de and aa8b37b.

📒 Files selected for processing (16)
  • scripts/game_settings_resource.gd
  • scripts/globals.gd
  • scripts/main_scene.gd
  • scripts/player.gd
  • test/gdunit4/test_difficulty.gd
  • test/gdunit4/test_difficulty_integration.gd
  • test/gdunit4/test_helpers.gd
  • test/gdunit4/test_player.gd
  • test/gut/test_fuel_edge_cases.gd
  • test/gut/test_fuel_edge_cases.gd.uid
  • test/gut/test_fuel_integration.gd
  • test/gut/test_fuel_integration.gd.uid
  • test/gut/test_fuel_resource.gd
  • test/gut/test_fuel_resource.gd.uid
  • test/gut/test_fuel_ui.gd
  • test/gut/test_fuel_ui.gd.uid
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Browser Functional Tests / test
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2026-03-30T04:02:23.747Z
Learnt from: ikostan
Repo: ikostan/SkyLockAssault PR: 500
File: test/gut/test_audio_web_bridge.gd:131-145
Timestamp: 2026-03-30T04:02:23.747Z
Learning: In GUT (Godot Unit Test) for Godot 4, when using `assert_called` / `assert_called_count` with parameter matching, include *every* argument the mocked method accepts, including parameters with default values. GUT does not auto-fill default arguments during call matching. For example, if `JavaScriptBridgeWrapper.eval(script: String, global_exec: bool = false)` is invoked as `eval(js_string)`, the actual call recorded by GUT includes the default (`eval(js_string, false)`), so your assertion must match both arguments (e.g., `.bind(js_string, false)`, not `.bind(js_string)`). Apply this rule to GUT assertions in `test/gut` tests.

Applied to files:

  • test/gut/test_fuel_ui.gd
  • test/gut/test_fuel_resource.gd
  • test/gut/test_fuel_edge_cases.gd
  • test/gut/test_fuel_integration.gd
🔇 Additional comments (6)
scripts/globals.gd (1)

205-223: Nice: the fuel load path now rejects malformed persisted values.

The numeric guards here make the bad-config fallback behavior much safer.

test/gdunit4/test_helpers.gd (1)

13-17: LGTM: the helper now mirrors the centralized drain formula.

Using Globals.settings.base_consumption_rate keeps the shared expectation aligned with the production refactor.

test/gdunit4/test_player.gd (1)

34-37: Nice: this expectation now follows the same fuel-rate source as production.

The helper check stays aligned with the new global resource model.

test/gut/test_fuel_resource.gd (1)

33-71: Nice resource-level coverage for clamp and refuel behavior.

These cases exercise the core fuel invariants without pulling a full scene into the test.

test/gut/test_fuel_edge_cases.gd (1)

32-74: Good edge-case coverage.

The once-only depletion assertion and explicit save/load round-trip hit the fragile parts of the new fuel behavior.

scripts/player.gd (1)

142-165: Nice move to a signal-driven fuel source.

Initializing from Globals.settings and subscribing to fuel_depleted/setting_changed removes the remaining player-local fuel coupling cleanly.

Comment thread scripts/game_settings_resource.gd Outdated
Comment thread scripts/globals.gd Outdated
Comment thread scripts/player.gd Outdated
Comment thread test/gdunit4/test_difficulty_integration.gd Outdated
Comment thread test/gdunit4/test_difficulty.gd Outdated
Comment thread test/gdunit4/test_player.gd Outdated
Comment thread test/gut/test_fuel_edge_cases.gd
Comment thread test/gut/test_fuel_resource.gd Outdated
Comment thread test/gut/test_fuel_ui.gd
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Copy Markdown

@SourceryAI SourceryAI left a comment

Choose a reason for hiding this comment

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

Hey - I've found 3 issues, and left some high level feedback:

  • The fallback path in player.gd that creates a new GameSettingsResource when Globals.settings is missing means the player can end up using a private fuel resource while the rest of the game (e.g. main_scene.gd background scroll logic) still reads Globals.settings, which can desynchronize behavior; consider either enforcing that Globals.settings always exists in runtime scenes or wiring the fallback instance back into Globals.settings so there is a single source of truth.
  • The fuel threshold exports (high_fuel_threshold, medium_fuel_threshold, low_fuel_threshold, no_fuel_threshold) are fully user-configurable but not validated against each other, so misordered or out-of-range values could break both color bands and warning logic; it may be worth clamping and enforcing a monotonic ordering inside the setters (or in a validation helper) to keep the UI behavior predictable.
  • In _on_fuel_timer_timeout() you still log "Fuel left" on every tick while you’ve suppressed log spam for current_fuel in _on_setting_changed; consider gating this log behind enable_debug_logging or removing it to avoid reintroducing high-frequency log noise under normal play.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The fallback path in `player.gd` that creates a new `GameSettingsResource` when `Globals.settings` is missing means the player can end up using a private fuel resource while the rest of the game (e.g. `main_scene.gd` background scroll logic) still reads `Globals.settings`, which can desynchronize behavior; consider either enforcing that `Globals.settings` always exists in runtime scenes or wiring the fallback instance back into `Globals.settings` so there is a single source of truth.
- The fuel threshold exports (`high_fuel_threshold`, `medium_fuel_threshold`, `low_fuel_threshold`, `no_fuel_threshold`) are fully user-configurable but not validated against each other, so misordered or out-of-range values could break both color bands and warning logic; it may be worth clamping and enforcing a monotonic ordering inside the setters (or in a validation helper) to keep the UI behavior predictable.
- In `_on_fuel_timer_timeout()` you still log `"Fuel left"` on every tick while you’ve suppressed log spam for `current_fuel` in `_on_setting_changed`; consider gating this log behind `enable_debug_logging` or removing it to avoid reintroducing high-frequency log noise under normal play.

## Individual Comments

### Comment 1
<location path="scripts/player.gd" line_range="77-83" />
<code_context>

 func _ready() -> void:
+	# Safely cache the settings resource
+	_settings = Globals.settings if is_instance_valid(Globals) else null
+
+	if not is_instance_valid(_settings):
+		# NEW: Log the error, but generate a fallback resource so the player
+		# fully initializes and doesn't become a game-crashing "zombie" node.
+		push_error("Player couldn't find Globals.settings! Using fallback defaults.")
+		_settings = GameSettingsResource.new()
+
 	# Auto-start rotors (overrides editor if needed)
</code_context>
<issue_to_address>
**issue (bug_risk):** Fallback GameSettingsResource can desync player fuel from the global settings used elsewhere.

If `Globals`/`Globals.settings` are invalid and you create a local `GameSettingsResource.new()`, the player ends up using a different settings instance than the rest of the game. For example, `main_scene.gd` reads `Globals.settings.current_fuel`, while the player uses `_settings.current_fuel` from the fallback. This can cause inconsistent fuel behavior. Either fail fast when `Globals.settings` is missing, or initialize `Globals.settings` to the new resource and ensure all code uses that single instance.
</issue_to_address>

### Comment 2
<location path="scripts/player.gd" line_range="264" />
<code_context>
+
+	rotor_stop(rotor_right, rotor_right_sfx)
+	rotor_stop(rotor_left, rotor_left_sfx)
+	fuel_timer.stop()
+
+
</code_context>
<issue_to_address>
**issue (bug_risk):** Fuel timer never restarts after refuel, so fuel consumption stops permanently once depleted.

`_on_player_out_of_fuel()` stops `fuel_timer`, but it’s never restarted when fuel is refilled via `GameSettingsResource.refuel()` or other `current_fuel` updates. Because consumption now depends on resource signals, refueling won’t resume fuel drain unless something explicitly restarts `fuel_timer`, letting the plane fly indefinitely. Consider either restarting the timer when `setting_changed("current_fuel", ...)` detects a transition from 0 to >0, or having the refuel logic explicitly restart `fuel_timer` on the player.
</issue_to_address>

### Comment 3
<location path="scripts/player.gd" line_range="238-246" />
<code_context>
+	if setting_name == "current_fuel":
+		update_fuel_bar()
+		check_fuel_warning()
+	elif setting_name == "max_fuel":
+		# Keep the UI fuel bar max in sync with the GameSettingsResource.
+		fuel_bar.max_value = _settings.max_fuel
+		# Re-run the standard UI update so the current fuel is represented correctly
+		# relative to the new maximum.
+		update_fuel_bar()
+
+
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Fuel warning blinking isn’t re-evaluated when max_fuel changes, which can leave the warning out of sync.

In `_on_setting_changed`, when `max_fuel` changes you resync `fuel_bar.max_value` and call `update_fuel_bar()`, but don’t re-run `check_fuel_warning()`. If `max_fuel` changes, the fuel percentage can cross the low-fuel threshold without updating the blinking state. Please call `check_fuel_warning()` here so the warning stays consistent with the new capacity.

```suggestion
# NEW: Observer pattern handler to react when GameSettingsResource
# properties (like fuel) are updated externally.
func _on_setting_changed(setting_name: String, _value: Variant) -> void:
	if not is_instance_valid(_settings):
		return

	if setting_name == "current_fuel":
		update_fuel_bar()
		check_fuel_warning()
	elif setting_name == "max_fuel":
		# Keep the UI fuel bar max in sync with the GameSettingsResource.
		fuel_bar.max_value = _settings.max_fuel
		# Re-run the standard UI update so the current fuel is represented correctly
		# relative to the new maximum.
		update_fuel_bar()
		# Re-evaluate the fuel warning in case the fuel percentage crossed the threshold.
		check_fuel_warning()
```
</issue_to_address>

Hi @ikostan! 👋

Thanks for trying out Sourcery by commenting with @sourcery-ai review! 🚀

Install the sourcery-ai bot to get automatic code reviews on every pull request ✨

Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread scripts/player.gd
Comment thread scripts/player.gd
Comment thread scripts/player.gd
Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 2 issues, and left some high level feedback:

  • Caching Globals.settings into _settings in player.gd means the player will keep using the old resource if Globals.settings is ever swapped at runtime (e.g., in tests or a future settings reload flow); consider either not caching or reacting to Globals.settings changes so the reference stays in sync.
  • The fuel color/threshold logic is now split between GameSettingsResource (threshold data) and player.gd (percent and color computation); if you expect thresholds to be reused elsewhere, consider moving the percentage/lerp calculation into a helper on GameSettingsResource to avoid duplicating this logic in future consumers.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Caching `Globals.settings` into `_settings` in `player.gd` means the player will keep using the old resource if `Globals.settings` is ever swapped at runtime (e.g., in tests or a future settings reload flow); consider either not caching or reacting to `Globals.settings` changes so the reference stays in sync.
- The fuel color/threshold logic is now split between `GameSettingsResource` (threshold data) and `player.gd` (percent and color computation); if you expect thresholds to be reused elsewhere, consider moving the percentage/lerp calculation into a helper on `GameSettingsResource` to avoid duplicating this logic in future consumers.

## Individual Comments

### Comment 1
<location path="scripts/game_settings_resource.gd" line_range="72-81" />
<code_context>
+@export var high_fuel_threshold: float = 90.0:
</code_context>
<issue_to_address>
**issue (bug_risk):** New fuel threshold settings are not persisted alongside `max_fuel`/`current_fuel`.

These new exported thresholds aren’t included in `_save_settings()` / `_load_settings()`, so changes made in the inspector or at runtime won’t persist across restarts. If they’re meant to be user/difficulty-tunable, please persist them via `Globals._save_settings` / `_load_settings` like `difficulty`, `max_fuel`, and `current_fuel`.
</issue_to_address>

### Comment 2
<location path="scripts/main_scene.gd" line_range="193-195" />
<code_context>
-	if player.fuel["fuel"] <= 0:
+
+	# OLD: if player.fuel["fuel"] <= 0:
+	# NEW: Check the global settings resource for current fuel,
+	# since the local dictionary key was removed.
+	if Globals.settings.current_fuel <= 0:
 		background.scroll_offset = Vector2(0, 0)

</code_context>
<issue_to_address>
**issue (bug_risk):** Direct access to `Globals.settings.current_fuel` assumes the settings resource is always valid.

Here `_process` directly dereferences `Globals.settings.current_fuel`, unlike `player.gd` which caches `_settings` and guards it with `is_instance_valid`. If the settings resource fails to load, is replaced, or set to `null` (e.g., in tests or tools), this will error. Please add a validity check (e.g. `if Globals.settings and is_instance_valid(Globals.settings)`) before accessing `current_fuel`, and use a safe default when it’s unavailable to match `Player`’s behavior.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread scripts/game_settings_resource.gd
Comment thread scripts/main_scene.gd Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
test/gut/test_fuel_persistence_integration.gd (1)

65-65: Assert config save success in test setup.

At Line 65 and Line 87, config.save(...) return values are ignored. If save fails, later assertions fail for the wrong reason.

Suggested patch
-	config.save(TEST_CONFIG_PATH)
+	assert_eq(config.save(TEST_CONFIG_PATH), OK, "Failed to write test config file.")

Also applies to: 87-87

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/gut/test_fuel_persistence_integration.gd` at line 65, The test currently
ignores the boolean return from config.save(TEST_CONFIG_PATH) (and the other
save call) which can hide setup failures; update the test to capture and assert
the save succeeded by checking the return value of config.save(TEST_CONFIG_PATH)
(and the second config.save) and failing the test if it returns false (e.g.,
assert config.save(TEST_CONFIG_PATH) or raise an error), so the test setup fails
immediately with a clear message when saving the config fails.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/player.gd`:
- Around line 146-149: The unconditional reset of _settings.current_fuel to
_settings.max_fuel in the player scene init overwrites persisted fuel; remove
that unconditional assignment and instead only initialize current_fuel when
starting a fresh run (e.g., in your new-game path) or when no persisted value
exists—check for a saved value (null/undefined or a saved-flag) in the player
scene initialization function (e.g., _ready/_init) before assigning from
_settings.max_fuel so that _settings.current_fuel loaded from persistence is
preserved.
- Around line 244-253: When handling setting_name == "max_fuel" in the player.gd
diff, also refresh the warning/blink state and handle any threshold changes:
after setting fuel_bar.max_value = _settings.max_fuel and calling
update_fuel_bar(), also call check_fuel_warning(); additionally, if there are
other fuel-related keys (e.g., a fuel warning threshold property in _settings),
ensure the same sequence (update_fuel_bar() then check_fuel_warning()) runs when
that setting_name is updated so the UI warning/blink state stays in sync with
max/current/threshold changes.

In `@test/gut/test_fuel_additional_edge_cases.gd`:
- Around line 31-33: After instantiating the scene and grabbing player_root,
stop the Player's live fuel timer so background ticks won't race with manual
_on_fuel_timer_timeout() calls; locate the Timer started in Player (e.g., a
child node "FuelTimer" or a field like fuel_timer) and call stop() (or
set_autostart = false) on it immediately after player_root =
main_scene.get_node("Player") so tests become deterministic.

---

Nitpick comments:
In `@test/gut/test_fuel_persistence_integration.gd`:
- Line 65: The test currently ignores the boolean return from
config.save(TEST_CONFIG_PATH) (and the other save call) which can hide setup
failures; update the test to capture and assert the save succeeded by checking
the return value of config.save(TEST_CONFIG_PATH) (and the second config.save)
and failing the test if it returns false (e.g., assert
config.save(TEST_CONFIG_PATH) or raise an error), so the test setup fails
immediately with a clear message when saving the config fails.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 7ba78d2e-ec1c-41f2-a41a-553b218e97e0

📥 Commits

Reviewing files that changed from the base of the PR and between 20d00e3 and 25df0e2.

📒 Files selected for processing (10)
  • scenes/main_scene.tscn
  • scripts/game_settings_resource.gd
  • scripts/globals.gd
  • scripts/player.gd
  • test/gut/test_fuel_additional_edge_cases.gd
  • test/gut/test_fuel_additional_edge_cases.gd.uid
  • test/gut/test_fuel_persistence_integration.gd
  • test/gut/test_fuel_persistence_integration.gd.uid
  • test/gut/test_player_lifecycle.gd
  • test/gut/test_player_lifecycle.gd.uid
✅ Files skipped from review due to trivial changes (4)
  • test/gut/test_player_lifecycle.gd.uid
  • test/gut/test_fuel_persistence_integration.gd.uid
  • test/gut/test_fuel_additional_edge_cases.gd.uid
  • scripts/globals.gd
🚧 Files skipped from review as they are similar to previous changes (1)
  • scripts/game_settings_resource.gd
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Browser Functional Tests / test
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2026-03-30T04:02:23.747Z
Learnt from: ikostan
Repo: ikostan/SkyLockAssault PR: 500
File: test/gut/test_audio_web_bridge.gd:131-145
Timestamp: 2026-03-30T04:02:23.747Z
Learning: In GUT (Godot Unit Test) for Godot 4, when using `assert_called` / `assert_called_count` with parameter matching, include *every* argument the mocked method accepts, including parameters with default values. GUT does not auto-fill default arguments during call matching. For example, if `JavaScriptBridgeWrapper.eval(script: String, global_exec: bool = false)` is invoked as `eval(js_string)`, the actual call recorded by GUT includes the default (`eval(js_string, false)`), so your assertion must match both arguments (e.g., `.bind(js_string, false)`, not `.bind(js_string)`). Apply this rule to GUT assertions in `test/gut` tests.

Applied to files:

  • test/gut/test_fuel_additional_edge_cases.gd
  • test/gut/test_player_lifecycle.gd
  • test/gut/test_fuel_persistence_integration.gd
🔇 Additional comments (5)
scripts/player.gd (1)

347-347: Good defensive math guards in fuel UI computation.

The checks around m_fuel <= 0.0 and non-positive threshold spans prevent invalid interpolation factors and UI breakage under misconfiguration.

Also applies to: 360-373

scenes/main_scene.tscn (1)

7-7: Scene reference update looks correct.

Using res://scenes/Player.tscn is a clean fix for case-sensitive path consistency.

test/gut/test_fuel_additional_edge_cases.gd (1)

15-22: Good global-state isolation in test setup/teardown.

Snapshotting and restoring Globals.settings here prevents cross-test leakage.

test/gut/test_player_lifecycle.gd (1)

31-74: Lifecycle coverage is solid here.

These tests validate both signal disconnection behavior and null-settings safety around _exit_tree().

test/gut/test_fuel_persistence_integration.gd (1)

132-135: Good explicit signal cleanup at test end.

Disconnecting the test listener prevents cross-test signal leakage.

Comment thread scripts/player.gd
Comment thread scripts/player.gd
Comment thread test/gut/test_fuel_additional_edge_cases.gd
ikostan and others added 10 commits April 9, 2026 16:54
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Gemini said
This is a classic case of an AI reviewer lacking the context of your specific game design!

The reviewer is technically correct about the code logic, but you are completely right about the game design.

The Contradiction
The reviewer is pointing out a conflict between two scripts:

In globals.gd, you are carefully saving and loading current_fuel to your settings.cfg file on the player's hard drive.

In player.gd (Line 149 / 206), you specifically added this comment:
# Restored the unconditional fuel reset. Since the game doesn't use mid-run resumes, the player MUST spawn with a full tank to prevent infinite death loops from previous 0-fuel saves.

The reviewer is saying: "Why are you saving the fuel to the hard drive if you just instantly refill the tank the moment the scene loads?"

Who is right?
You are. If your game is an arcade-style shooter or level-based (no mid-run saves), spawning with a 0-fuel save file would create an infinite instant-death loop. The unconditional reset to max_fuel in player.gd is a vital safety net.

The Real Fix
To satisfy the reviewer and clean up the architecture, you shouldn't change player.gd. Instead, you should stop saving current_fuel to the hard drive entirely. Since current_fuel is volatile session data (it only matters while the plane is flying), it doesn't belong in a permanent settings.cfg file next to things like Volume or Controls.

To fix this perfectly, just open globals.gd and delete the current_fuel lines from your load/save functions

Once you do this, max_fuel (the capacity upgrade) is still saved forever, but current_fuel is just a temporary number that always safely resets to maximum when the player spawns. This completely resolves the reviewer's warning while protecting your game's logic!
…mes the settings resource is always valid.

issue (bug_risk): Direct access to Globals.settings.current_fuel assumes the settings resource is always valid.

Here _process directly dereferences Globals.settings.current_fuel, unlike player.gd which caches _settings and guards it with is_instance_valid. If the settings resource fails to load, is replaced, or set to null (e.g., in tests or tools), this will error. Please add a validity check (e.g. if Globals.settings and is_instance_valid(Globals.settings)) before accessing current_fuel, and use a safe default when it’s unavailable to match Player’s behavior.
@ikostan
Copy link
Copy Markdown
Owner Author

ikostan commented Apr 10, 2026

  • Caching Globals.settings into _settings in player.gd means the player will keep using the old resource if Globals.settings is ever swapped at runtime (e.g., in tests or a future settings reload flow); consider either not caching or reacting to Globals.settings changes so the reference stays in sync.
  • The fuel color/threshold logic is now split between GameSettingsResource (threshold data) and player.gd (percent and color computation); if you expect thresholds to be reused elsewhere, consider moving the percentage/lerp calculation into a helper on GameSettingsResource to avoid duplicating this logic in future consumers.

Thanks for the suggestions! We are going to intentionally keep the current implementation for the following architectural reasons:

  1. Regarding the _settings cache: > Hot-swapping the entire Globals.settings resource instance mid-game is an anti-pattern we strictly avoid. We mutate the properties within the resource (which our Observer pattern catches), but the resource pointer itself remains stable during a scene's lifecycle. Therefore, the local cache in _physics_process gives us a safe performance boost without the risk of desyncing.

  2. Regarding moving Color/Lerp logic to the Resource: > We are strictly adhering to the MVC pattern here. GameSettingsResource is pure data (the Model) and should have no knowledge of UI implementations, Color constants, or visual lerp calculations. The Player/UI (the View) is the correct place to interpret that data into visual elements like progress bar colors.

Marking this as resolved!

ikostan added 3 commits April 9, 2026 17:37
…l from the global settings used elsewhere.

issue (bug_risk): Fallback GameSettingsResource can desync player fuel from the global settings used elsewhere.

If Globals/Globals.settings are invalid and you create a local GameSettingsResource.new(), the player ends up using a different settings instance than the rest of the game. For example, main_scene.gd reads Globals.settings.current_fuel, while the player uses _settings.current_fuel from the fallback. This can cause inconsistent fuel behavior. Either fail fast when Globals.settings is missing, or initialize Globals.settings to the new resource and ensure all code uses that single instance.
…sumption stops permanently once depleted.

issue (bug_risk): Fuel timer never restarts after refuel, so fuel consumption stops permanently once depleted.

_on_player_out_of_fuel() stops fuel_timer, but it’s never restarted when fuel is refilled via GameSettingsResource.refuel() or other current_fuel updates. Because consumption now depends on resource signals, refueling won’t resume fuel drain unless something explicitly restarts fuel_timer, letting the plane fly indefinitely. Consider either restarting the timer when setting_changed("current_fuel", ...) detects a transition from 0 to >0, or having the refuel logic explicitly restart fuel_timer on the player.
@ikostan
Copy link
Copy Markdown
Owner Author

ikostan commented Apr 10, 2026

Working on:

  • The fuel threshold exports (high_fuel_threshold, medium_fuel_threshold, low_fuel_threshold, no_fuel_threshold) are fully user-configurable but not validated against each other, so misordered or out-of-range values could break both color bands and warning logic; it may be worth clamping and enforcing a monotonic ordering inside the setters (or in a validation helper) to keep the UI behavior predictable.

The fuel threshold exports (high_fuel_threshold, medium_fuel_threshold, low_fuel_threshold, no_fuel_threshold) are fully user-configurable but not validated against each other, so misordered or out-of-range values could break both color bands and warning logic; it may be worth clamping and enforcing a monotonic ordering inside the setters (or in a validation helper) to keep the UI behavior predictable.
@ikostan
Copy link
Copy Markdown
Owner Author

ikostan commented Apr 10, 2026

Working on:

  • In _on_fuel_timer_timeout() you still log "Fuel left" on every tick while you’ve suppressed log spam for current_fuel in _on_setting_changed; consider gating this log behind enable_debug_logging or removing it to avoid reintroducing high-frequency log noise under normal play.

In _on_fuel_timer_timeout() you still log "Fuel left" on every tick while you’ve suppressed log spam for current_fuel in _on_setting_changed; consider gating this log behind enable_debug_logging or removing it to avoid reintroducing high-frequency log noise under normal play.
@ikostan
Copy link
Copy Markdown
Owner Author

ikostan commented Apr 10, 2026

  • The fallback path in player.gd that creates a new GameSettingsResource when Globals.settings is missing means the player can end up using a private fuel resource while the rest of the game (e.g. main_scene.gd background scroll logic) still reads Globals.settings, which can desynchronize behavior; consider either enforcing that Globals.settings always exists in runtime scenes or wiring the fallback instance back into Globals.settings so there is a single source of truth.
  • The fuel threshold exports (high_fuel_threshold, medium_fuel_threshold, low_fuel_threshold, no_fuel_threshold) are fully user-configurable but not validated against each other, so misordered or out-of-range values could break both color bands and warning logic; it may be worth clamping and enforcing a monotonic ordering inside the setters (or in a validation helper) to keep the UI behavior predictable.
  • In _on_fuel_timer_timeout() you still log "Fuel left" on every tick while you’ve suppressed log spam for current_fuel in _on_setting_changed; consider gating this log behind enable_debug_logging or removing it to avoid reintroducing high-frequency log noise under normal play.

All addressed.

@ikostan
Copy link
Copy Markdown
Owner Author

ikostan commented Apr 10, 2026

@sourcery-ai guide

@ikostan ikostan changed the title Add fuel system, integrate with player and tests Decouple Fuel System using GameSettingsResource and Signals Apr 10, 2026
@ikostan ikostan merged commit c854942 into main Apr 10, 2026
14 checks passed
@github-project-automation github-project-automation Bot moved this from In Progress to Done in Sky Lock Assault Project Apr 10, 2026
@ikostan ikostan deleted the convert-hard-coded-fuel-elements-to-godot-resource branch April 10, 2026 00:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request mechanics

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

[QA] Fuel System Test Plan (GUT) [FEATURE] Convert Hard-Coded Fuel Elements to Godot Resource [FEATURE] Add Signals for Fuel System in Player.gd

2 participants