Skip to content
Merged
Show file tree
Hide file tree
Changes from 64 commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
5c393d1
Add fuel system, integrate with player and tests
ikostan Apr 7, 2026
78b8c0d
Update player.gd
ikostan Apr 7, 2026
3c09275
Update globals.gd
ikostan Apr 7, 2026
bbe9181
Update game_settings_resource.gd
ikostan Apr 7, 2026
d8e69b4
Update player.gd
ikostan Apr 7, 2026
af4983f
Update main_scene.gd
ikostan Apr 7, 2026
5d90fe6
Use GameSettings for fuel and reset on spawn
ikostan Apr 7, 2026
5384388
Update test_player.gd
ikostan Apr 7, 2026
55416e8
Update test_difficulty.gd
ikostan Apr 7, 2026
cc1030e
Update test_difficulty_integration.gd
ikostan Apr 7, 2026
b54c44d
Reset speed on fuel out & update test helper
ikostan Apr 7, 2026
aa8b37b
Add GUT tests for fuel edge cases
ikostan Apr 7, 2026
686191c
Update test/gut/test_fuel_ui.gd
ikostan Apr 8, 2026
5dc44cf
test_fuel_initialization() can't fail right now.
ikostan Apr 8, 2026
aa03e99
Update test/gut/test_fuel_edge_cases.gd
ikostan Apr 8, 2026
7196efb
Update test/gut/test_fuel_resource.gd
ikostan Apr 8, 2026
4bf75bb
Use Globals.settings.max_fuel for the fuel fixtures and bar assertions.
ikostan Apr 8, 2026
8c755c1
Isolate the singleton fuel state in this test.
ikostan Apr 8, 2026
20d00e3
Restore the shared fuel state after this integration test.
ikostan Apr 8, 2026
c9a0e69
Update scripts/player.gd
ikostan Apr 8, 2026
c871aa9
Update scripts/game_settings_resource.gd
ikostan Apr 8, 2026
fba243a
Update game_settings_resource.gd
ikostan Apr 8, 2026
312b477
Avoid auto-saving current_fuel on every drain tick.
ikostan Apr 8, 2026
3073aee
Update globals.gd
ikostan Apr 8, 2026
d0d607a
Update globals.gd
ikostan Apr 8, 2026
2b3c364
suggestion (bug_risk): Handle max_fuel changes so the fuel bar’s max …
ikostan Apr 8, 2026
f0b3cc8
Introduce a Godot Resource that centralizes fuel configuration
ikostan Apr 9, 2026
b9807a3
Update player.gd
ikostan Apr 9, 2026
985e74c
Update player.gd
ikostan Apr 9, 2026
59c8027
Update player.gd
ikostan Apr 9, 2026
41d9de6
Add automated unit tests for core fuel logic
ikostan Apr 9, 2026
a080857
Add tests for persistence and UI integration of the fuel system
ikostan Apr 9, 2026
f17205c
Update test_fuel_persistence_integration.gd
ikostan Apr 9, 2026
0cf39f2
issue (bug_risk): The setters for max_fuel and current_fuel are recur…
ikostan Apr 9, 2026
d5d3da8
question (bug_risk): Resetting current_fuel to max on player spawn ma…
ikostan Apr 9, 2026
f98206b
Update test/gut/test_fuel_additional_edge_cases..gd
ikostan Apr 9, 2026
3c39448
Merge branch 'convert-hard-coded-fuel-elements-to-godot-resource' of …
ikostan Apr 9, 2026
4c800b7
Update test/gut/test_fuel_persistence_integration.gd
ikostan Apr 9, 2026
e2ba596
Need to explicitly sever the ties before the node leaves the tree
ikostan Apr 9, 2026
b99e75f
Merge branch 'convert-hard-coded-fuel-elements-to-godot-resource' of …
ikostan Apr 9, 2026
3687769
Update player.gd
ikostan Apr 9, 2026
1f90f7b
Update main_scene.tscn
ikostan Apr 9, 2026
e6ce226
Update player.gd
ikostan Apr 9, 2026
e3bf849
issue (bug_risk): The current_fuel setter recursively assigns to itse…
ikostan Apr 9, 2026
e34245c
Update game_settings_resource.gd
ikostan Apr 9, 2026
85a4a46
issue (bug_risk): Threshold setters assign to their own exported prop…
ikostan Apr 9, 2026
f8bb977
Update game_settings_resource.gd
ikostan Apr 9, 2026
5582ee4
Update game_settings_resource.gd
ikostan Apr 9, 2026
9c7318c
issue (bug_risk): Fuel warning uses absolute current_fuel vs a thresh…
ikostan Apr 9, 2026
69661e4
The new test file test_fuel_additional_edge_cases..gd has a double do…
ikostan Apr 9, 2026
049558b
Update test_fuel_persistence_integration.gd
ikostan Apr 9, 2026
60d3224
issue: The max_fuel setter can indirectly force current_fuel to 0 and…
ikostan Apr 9, 2026
8e17078
Update player.gd
ikostan Apr 9, 2026
60239e3
Update player.gd
ikostan Apr 9, 2026
6940141
issue: Guard against invalid or equal fuel threshold values that woul…
ikostan Apr 9, 2026
bbda2c3
Update test_fuel_additional_edge_cases.gd
ikostan Apr 9, 2026
3c08ad8
Update player.gd
ikostan Apr 9, 2026
b639983
Update player.gd
ikostan Apr 9, 2026
e65f0ff
suggestion (bug_risk): The cached _settings reference is not used con…
ikostan Apr 9, 2026
7f9da9c
Update game_settings_resource.gd
ikostan Apr 9, 2026
11712ee
Update game_settings_resource.gd
ikostan Apr 9, 2026
ed2064b
Update game_settings_resource.gd
ikostan Apr 9, 2026
b34983c
Update player.gd
ikostan Apr 9, 2026
25df0e2
Update player.gd
ikostan Apr 9, 2026
ce2ace0
Update test/gut/test_fuel_additional_edge_cases.gd
ikostan Apr 9, 2026
981857b
Update scripts/player.gd
ikostan Apr 9, 2026
ad8a596
Update player.gd
ikostan Apr 9, 2026
0f9350a
Update globals.gd
ikostan Apr 10, 2026
249c0c8
Update globals.gd
ikostan Apr 10, 2026
9de3148
Update globals.gd
ikostan Apr 10, 2026
87cb1f5
Update test_fuel_persistence_integration.gd
ikostan Apr 10, 2026
6fa8828
Update test_fuel_edge_cases.gd
ikostan Apr 10, 2026
231d117
issue (bug_risk): Direct access to Globals.settings.current_fuel assu…
ikostan Apr 10, 2026
18e1d7c
Update main_scene.gd
ikostan Apr 10, 2026
a94c9b2
issue (bug_risk): Fallback GameSettingsResource can desync player fue…
ikostan Apr 10, 2026
6f9ea4b
Update player.gd
ikostan Apr 10, 2026
9e6daa6
issue (bug_risk): Fuel timer never restarts after refuel, so fuel con…
ikostan Apr 10, 2026
c314ebc
Update game_settings_resource.gd
ikostan Apr 10, 2026
f0bfd62
Update player.gd
ikostan Apr 10, 2026
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 scenes/main_scene.tscn
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
[ext_resource type="PackedScene" uid="uid://cb4n4cqkuddqg" path="res://scenes/pause_menu.tscn" id="1_w2twt"]
[ext_resource type="Texture2D" uid="uid://ce63ga8f5mua7" path="res://files/trees/tree_01.png" id="2_fm3ay"]
[ext_resource type="FontFile" uid="uid://borwvgqdgawbj" path="res://files/fonts/EMPIREST.TTF" id="2_pu3yx"]
[ext_resource type="PackedScene" uid="uid://37rarq1yywmc" path="res://scenes/player.tscn" id="2_pw63i"]
[ext_resource type="PackedScene" uid="uid://37rarq1yywmc" path="res://scenes/Player.tscn" id="2_pw63i"]
[ext_resource type="Script" uid="uid://csdce7ynrpivs" path="res://scripts/resource_preloader.gd" id="3_c1pb6"]
[ext_resource type="Texture2D" uid="uid://bsmbcgovcph7p" path="res://files/trees/tree_02.png" id="3_sgkfd"]
[ext_resource type="Texture2D" uid="uid://syw5eae1ebna" path="res://files/trees/tree_11.png" id="4_qj6t7"]
Expand Down
111 changes: 111 additions & 0 deletions scripts/game_settings_resource.gd
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,96 @@ extends Resource
## without the UI having to explicitly call persistence or logging methods.
signal setting_changed(setting_name: String, new_value: Variant)

## SIGNAL: fuel_depleted
##
## Emitted when current_fuel reaches exactly 0.0.
## External systems (like the Player or UI) can connect to this to trigger
## game-over states or low-fuel warnings without polling every frame.
signal fuel_depleted

@export_group("Fuel System")

## Maximum fuel capacity.
@export var max_fuel: float = 100.0:
set(value):
# NEW: Enforce a logical minimum capacity of 1.0. This prevents a misconfigured
# save file or options slider from shrinking the tank to 0.0 and
# accidentally triggering a runtime "Game Over" flameout.
var new_max: float = max(1.0, value)

if _max_fuel == new_max:
return
_max_fuel = new_max

# Use the backing field for comparisons, but trigger the public
# setter for current_fuel if needed
if _current_fuel > _max_fuel:
Comment thread
sourcery-ai[bot] marked this conversation as resolved.
self.current_fuel = _max_fuel

setting_changed.emit("max_fuel", _max_fuel)
get:
return _max_fuel

## Current fuel level. Clamped between 0.0 and max_fuel.
@export var current_fuel: float = 100.0:
set(value):
Comment thread
sourcery-ai[bot] marked this conversation as resolved.
# NEW: Use private backing field to safely clamp and check values without recursion
var old_value: float = _current_fuel
var new_fuel: float = clamp(value, 0.0, _max_fuel)

if _current_fuel == new_fuel:
return

_current_fuel = new_fuel

if old_value > 0.0 and _current_fuel == 0.0:
fuel_depleted.emit()

setting_changed.emit("current_fuel", _current_fuel)
get:
# NEW: Return the backing field
return _current_fuel

## Base rate of fuel consumption per second.
@export var base_consumption_rate: float = 1.0

# NEW: Migrated thresholds from player.gd to centralize all fuel configuration
@export var high_fuel_threshold: float = 90.0:
set(value):
if _high_fuel_threshold == value:
return
_high_fuel_threshold = value
setting_changed.emit("high_fuel_threshold", _high_fuel_threshold)
get:
return _high_fuel_threshold

@export var medium_fuel_threshold: float = 50.0:
Comment thread
sourcery-ai[bot] marked this conversation as resolved.
set(value):
if _medium_fuel_threshold == value:
return
_medium_fuel_threshold = value
setting_changed.emit("medium_fuel_threshold", _medium_fuel_threshold)
get:
return _medium_fuel_threshold

Comment thread
sourcery-ai[bot] marked this conversation as resolved.
@export var low_fuel_threshold: float = 30.0:
set(value):
if _low_fuel_threshold == value:
return
_low_fuel_threshold = value
setting_changed.emit("low_fuel_threshold", _low_fuel_threshold)
get:
return _low_fuel_threshold

@export var no_fuel_threshold: float = 15.0:
set(value):
if _no_fuel_threshold == value:
return
_no_fuel_threshold = value
setting_changed.emit("no_fuel_threshold", _no_fuel_threshold)
get:
return _no_fuel_threshold

@export_group("Logging")
# Current log level: 0=DEBUG, 1=INFO, 2=WARNING, 3=ERROR, 4=NONE
@export_range(0, 4, 1) var current_log_level: int = 1:
Expand Down Expand Up @@ -58,9 +148,20 @@ signal setting_changed(setting_name: String, new_value: Variant)
@export var options_scene: PackedScene

# Private member variables moved to bottom to satisfy class-definitions-order
# NEW: GDScript requires backing fields to be declared BEFORE
# the properties that reference them.
# Moved these from the bottom of the script to the top to resolve
# the "Identifier not declared" scope error.
var _max_fuel: float = 100.0
var _current_fuel: float = _max_fuel # ← Automatically syncs to whatever max_fuel is
var _current_log_level: int = 1
var _difficulty: float = 1.0
var _enable_debug_logging: bool = false
# NEW: Add backing fields
var _high_fuel_threshold: float = 90.0
var _medium_fuel_threshold: float = 50.0
var _low_fuel_threshold: float = 30.0
var _no_fuel_threshold: float = 15.0


func _init() -> void:
Expand All @@ -69,3 +170,13 @@ func _init() -> void:
key_mapping_scene = load("res://scenes/key_mapping_menu.tscn")
if not options_scene:
options_scene = load("res://scenes/options_menu.tscn")
# NEW: Safely enforce the invariant that a brand new resource
# always starts with a full tank, without bypassing the validation setters.
_current_fuel = _max_fuel


## Helper method to increase fuel safely.
## Increases fuel level by specified amount, clamped to max_fuel.
func refuel(amount: float) -> void:
if amount > 0:
current_fuel += amount
42 changes: 38 additions & 4 deletions scripts/globals.gd
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,17 @@ func _on_setting_changed(setting_name: String, new_value: Variant) -> void:
var log_msg: String = "Setting '%s' updated to: %s" % [setting_name, str(new_value)]

# Automatically log the change
log_message(log_msg, LogLevel.DEBUG)
# OLD: log_message(log_msg, LogLevel.DEBUG)
# NEW: Prevent log spam by filtering out high-frequency runtime changes like fuel ticks
if setting_name != "current_fuel":
log_message(log_msg, LogLevel.DEBUG)

# Automatically persist to disk
_save_settings()
# OLD: _save_settings()
# NEW: Prevent disk I/O lag by stopping current_fuel from
# triggering a file save on every frame/timer tick
if setting_name != "current_fuel":
_save_settings()


## Centralized "ensure initial focus" helper.
Expand Down Expand Up @@ -151,7 +158,8 @@ func load_key_mapping(menu_to_hide: Node) -> void:
get_tree().root.add_child(km_instance)


## Loads persisted settings from config if valid types; skips invalid/missing to keep current.
## Loads persisted settings from config if valid types;
## skips invalid/missing to keep current.
## :param path: Config file path (default: Settings.CONFIG_PATH).
## :type path: String
## :rtype: void
Expand Down Expand Up @@ -202,6 +210,26 @@ func _load_settings(path: String = Settings.CONFIG_PATH) -> void:
LogLevel.DEBUG
)

# NEW: Load the fuel related settings
if config.has_section_key("Settings", "max_fuel"):
var loaded_max: Variant = config.get_value("Settings", "max_fuel")
if loaded_max is float or loaded_max is int:
settings.max_fuel = float(loaded_max)
else:
log_message(
"Invalid type for max_fuel: " + str(typeof(loaded_max)), LogLevel.WARNING
)

if config.has_section_key("Settings", "current_fuel"):
var loaded_current: Variant = config.get_value("Settings", "current_fuel")
if loaded_current is float or loaded_current is int:
settings.current_fuel = float(loaded_current)
else:
log_message(
"Invalid type for current_fuel: " + str(typeof(loaded_current)),
LogLevel.WARNING
)

# Disable the guard and log a single summary instead
_is_loading_settings = false
log_message("All settings loaded and synchronized.", LogLevel.DEBUG)
Expand All @@ -226,6 +254,10 @@ func _save_settings(path: String = Settings.CONFIG_PATH) -> void:
config.set_value("Settings", "difficulty", settings.difficulty)
# NEW: Persist the debug logging flag
config.set_value("Settings", "enable_debug_logging", settings.enable_debug_logging)
# NEW: Persist the fuel settings
config.set_value("Settings", "max_fuel", settings.max_fuel)
config.set_value("Settings", "current_fuel", settings.current_fuel)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

err = config.save(path)
if err != OK:
log_message("Failed to save settings: " + str(err), LogLevel.ERROR)
Expand Down Expand Up @@ -321,7 +353,9 @@ func _notification(what: int) -> void:

# Example: Save game state if you have a save system.
# Replace with your actual save function, e.g., from a save_manager.gd.
# save_game_state() # Uncomment and implement as needed.
# OLD: # save_game_state() # Uncomment and implement as needed.
# NEW: Explicitly save all settings (including current_fuel) right before the game quits
_save_settings()

# After cleanup, let the quit proceed (optional on desktop; auto on web).
get_tree().quit()
Expand Down
9 changes: 8 additions & 1 deletion scripts/main_scene.gd
Original file line number Diff line number Diff line change
Expand Up @@ -188,9 +188,16 @@ func setup_decor_layer(viewport: Vector2) -> void:
func _process(delta: float) -> void:
var scroll_speed: float = player.speed["speed"] * delta * Globals.settings.difficulty * 0.8
background.scroll_offset.y += scroll_speed
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:
Comment thread
sourcery-ai[bot] marked this conversation as resolved.
Outdated
background.scroll_offset = Vector2(0, 0)

# 1. Critical unbound controls warning (shown ONCE per session)
# Flag stays true until player fixes bindings (e.g., in key_mapping.gd after remap).

# 1. Critical unbound controls warning (shown ONCE per session)
# Flag stays true until player fixes bindings (e.g., in key_mapping.gd after remap).
# Do NOT reset here — that would make it repeat every 4s (bug fixed).
Expand Down
Loading
Loading