Compare commits

..

No commits in common. "bc91204aa27e1bce2b9673a0a3da35757ca96311" and "1b8d556929b353f2a4b08f77188dd706b0c7b2ff" have entirely different histories.

7 changed files with 132 additions and 138 deletions

80
.gitignore vendored
View File

@ -1,73 +1,13 @@
# Godot 4+ specific ignores
.godot/
.nomedia
*md.backup
src/.godot/
*.blend1
result
src/addons/godot-jolt
*png~
*~lock*
*_recovered*
# Godot-specific ignores
.import/
export.cfg
export_credentials.cfg
*.tmp
builds/
# Imported translations (automatically generated from CSV files)
*.translation
# Mono-specific ignores
.mono/
data_*/
mono_crash.*.json
# IDE Clutter
# VS Code
# https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
/builds
.idea
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets
!*.code-workspace
# Built Visual Studio Code Extensions
*.vsix
AGENTS.md
CHANGELOG.md
ISSUES.md
**/.idea
**/.DS_Store
**/.gradle
**/*.iml
**/obj
**/bin
# Jetbrains
# https://github.com/JetBrains/godot-support/blob/master/.gitignore
rider/.intellijPlatform
rider/build
rider/protocol/build
rider/classes
rider/nuget
gdscript/.intellijPlatform
gdscript/build
gdscript/protocol/build
gdscript/classes
community/.intellijPlatform
community/build
community/protocol/build
community/classes
community/nuget
*generated*
*Generated*
NuGet.Config
*.hprof
knowledge.md

110
CHANGELOG.md Normal file
View File

@ -0,0 +1,110 @@
# Changelog
All notable changes to Frame of Mind since commit `c75348845a5136379ce9ab09d2ff64b7bec90f12`.
## [Unreleased]
### Features
- New interactable system with action prompts that can play scenes
- Added new interactables to youth room with repositioned layout
- Interactable prompts now update dynamically based on collected state (shows \"read again\" after collection)
- Type-safe interactive handling for player
- Room loading system implementation
- Basic room start logic
- Room play functionality
### Fixes
#### Interactables & Positioning
- Small improvements to positioning
- Sorting function was not according to spec
#### Save System
- Save game parsing failure no longer crashes the game
- Save game directory creation on first load
- Fixed race condition with settings initialization
- New save game only creates once (reduced from multiple duplicates)
- Save game picker layout improvements
#### UI & Menus
- Fixed markdown not loading nonexistent resources
- Markdown now uses application theme
- Fixed masking on loading indicator
- Fixed loading spinner animation states
- Fixed screenshot size (was 261, corrected to 216 height)
- Nav UI in accessibility settings is now accessible again
- Card picker input propagation fixed
- Card picker now closes properly
- Cards now derived from prefab scene
- Text updates now forced to deferred
- Skip buttons work via mouse
- Cardboard can now be clicked and opened
#### Player & Controls
- Player crouching on leaving bed fixed
- Line renderer errors due to zero magnitude axis fixed
- Climb volume now automatically crouches the player
- Player raising and crouching stabilized
- Removed annoying half-second wait
- Player controller cleanup
#### Rooms & Scenes
- Theme files reconstructed
- Regenerated missing UIDs in theme files
- Resaved various scenes to fix UIDs
- Removed forced preload of all 3 base rooms
- Fixed various path and hierarchy issues in youth room
- Viewport children no longer break
- Found and fixed many broken/orphaned viewports in youth room
- Main scene is no longer a Control (fixed input issues)
- Removed broken LightmapGI node that was causing scene to be dirtied on every load
#### Story & Playables
- Story playables no longer destroy their own serialized text
- Reinstated accidentally deleted paragraph length data
- Fixed Draven/Draeven naming inconsistencies
- Manual scroll enable on Draven and Voice fixed
- Story playables now control their own canvas layer visibility
- Board now correctly identifies itself
#### Code Quality
- Fixed hardcoded localization load (en) removed from scene player
- Fixed settings panel refactoring path
- Fixed shadowing issues in card-board
- Fixed syntax errors
- Removed all warnings
- Fixed untyped variables throughout codebase
- Fixed unknown fields issues
### Refactored
- New interactable system replaces old interactive_sprite.gd system
- Interactable now has separate `_update_caption()` and `_update_prompt()` methods
- Renamed `collectable.gd` to `interactive_sprite.gd`
- Moved various embedded scripts to their own `.gd` files
- Focus system refactored
- Removed deprecated sequencing calls
- Removed deprecated signals and `is_board`
- Stage system refactored, dead code cleared
- Collectable logic refactored
- Renamed `SaveGameHandle` to `SaveGameList`
- Renamed `scnees` folder to `scenes`
- Menu animations moved to tweens/subscenes
- Better inheritance and render order for mementos
- New direct interactable system
### Changed
- Replaced all bare `print()` statements with `print_debug()`
- Game window set to 1600x900 for better testing/debugging
- Explicit search index layout
- Main scene changed from Control to different node type
### Tests
- Added player tests for crouching and raiser functionality
- Raiser now uses AnimatableBody3D
### Chores
- Generated missing .import files
- Added IDE configuration files
- Fixed button/Button filename case issues
- Cleaned up project structure
- Updated .gitignore files

View File

@ -51,8 +51,7 @@ var current_room_path: String:
@export var is_demo: bool = OS.has_feature("Demo")
@export var is_empty: bool = true:
get():
return not FileAccess.file_exists(filepath) or (current_room == State.rooms.NULL)
return not FileAccess.file_exists("%s.json:" % filepath)
@export var save_manually: bool = false:
set(val):
if val: save_to_file(thumbnail)

View File

@ -253,6 +253,3 @@ func pick_cards(id: Scenes.id, repeat: bool):
await cards_picked
hide()
await get_tree().process_frame
State.room.save_room()

View File

@ -7,11 +7,9 @@ var interaction_ui : Control = null
@onready var frame: Sprite3D = $Frame
@onready var canvas_layer: CanvasLayer = $CanvasLayer
@onready var note: Node3D = $View/Sprite3D
@onready var caption : Label3D = %Caption
@onready var prompt : Label3D = %Prompt
@export var billboard : bool = true
var active : bool = true
@ -26,7 +24,6 @@ var collected : bool = false:
var tween: Tween = null
func _ready() -> void:
assert(note and frame and canvas_layer, "Interactable must have views and frame attached")
view.scale = Vector3.ZERO
frame.modulate.a = 0
if interaction:
@ -47,29 +44,21 @@ func _player_active(value: bool) -> void:
func expand() -> void:
shown = true
if tween and tween.is_valid():
tween.kill()
else:
view.scale = Vector3.ZERO
note.rotation.z = -PI*0.5 # Godot angle wrapping is ... something
frame.modulate = Color.TRANSPARENT
frame.scale = Vector3(1.5, 1.5, 1.5)
view.scale = Vector3.ZERO
frame.modulate = Color.TRANSPARENT
view.rotation.z = -PI*0.5 # Godot angle wrapping is ... something
if tween: tween.kill()
tween = create_tween().set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_BACK)
tween.parallel().tween_property(view, "scale", Vector3.ONE, 1.0).set_delay(0.5)
tween.parallel().tween_property(note, "rotation:z", 0, 0.8).set_delay(0.5)
tween.parallel().tween_property(frame, "modulate:a", 1.0, 2.0).set_trans(Tween.TRANS_QUAD)
tween.parallel().tween_property(frame, "scale", Vector3.ONE, 1.0).set_trans(Tween.TRANS_QUART)
tween.parallel().tween_property(view, "rotation:z", 0, 0.8).set_delay(0.5)
tween.parallel().tween_property(frame, "modulate:a", 1.0, 2.0)
func collapse() -> void:
shown = false
if tween and tween.is_valid(): tween.kill()
if tween: tween.kill()
tween = create_tween().set_ease(Tween.EASE_IN).set_trans(Tween.TRANS_BACK)
tween.parallel().tween_property(view, "scale", Vector3.ZERO, 0.3)
tween.parallel().tween_property(frame, "modulate:a", 0, 0.5).set_trans(Tween.TRANS_QUAD)
tween.parallel().tween_property(frame, "scale", Vector3.ONE * 2.0, 1.0).set_trans(Tween.TRANS_QUAD)
tween.parallel().tween_property(frame, "modulate:a", 0, 0.6)
func _process(_delta: float) -> void:
_process_billboard()

View File

@ -1,7 +1,5 @@
class_name SaveGameDisplay extends Button
signal delete_requested
var _is_built: bool = false
@export var save: SaveGame:
@ -55,7 +53,7 @@ func rebuild():
match TranslationServer.get_locale():
"de":
localised_weekday = ["Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag"]
localised_weekday = ["Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag"]
localised_date_time = "%s, %d.%d.%d um %d:%02d" % [localised_weekday[date_time["weekday"]],
date_time["day"],
date_time["month"],
@ -64,7 +62,7 @@ func rebuild():
date_time["minute"]
]
_:
localised_weekday = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
localised_weekday = ["Monday", "Tuseday", "Wensday", "Thursday", "Friday", "Saturday", "Sunday"]
localised_date_time = "%s, %d/%d/%d - %d:%02d (%s)" % [localised_weekday[date_time["weekday"]],
date_time["day"],
date_time["month"],
@ -77,7 +75,6 @@ func rebuild():
time_label.text = localised_date_time if not save.current_room == State.rooms.NULL else "Start new game"
var info:= VBoxContainer.new()
info.size_flags_horizontal = Control.SIZE_EXPAND_FILL
base_container.add_child(info)
info.add_child(heading_split)
@ -111,16 +108,6 @@ func rebuild():
info.add_child(room)
info.add_child(state)
info.size_flags_vertical = Control.SIZE_SHRINK_CENTER
# Delete button anchored to bottom right
var delete_button := Button.new()
delete_button.text = "Delete"
delete_button.size_flags_vertical = Control.SIZE_SHRINK_END
delete_button.pressed.connect(func():
delete_requested.emit()
get_viewport().set_input_as_handled()
)
base_container.add_child(delete_button)
theme_type_variation = "SaveButton"

View File

@ -10,7 +10,6 @@ var save_buttons: Array[SaveGameDisplay]
_load_games()
@onready var list_container: VBoxContainer = %ListContainer
@onready var back_button: Button = $MarginContainer/VBoxContainer/HBoxContainer/Button
func _validate_property(property: Dictionary) -> void:
if property.name == "saves":
@ -22,7 +21,6 @@ func _ready() -> void:
_load_games()
hide()
set_process_input(false)
back_button.pressed.connect(cancel)
func _ensure_directory() -> void:
var dir := DirAccess.open(State.user_saves_path)
@ -44,10 +42,7 @@ func _load_games():
for path in filepaths:
if path is String and path.ends_with(".json"):
var save := SaveGame.new("%s/%s" % [State.user_saves_path, path.get_basename()])
# HACK: Skip empty saves (we decide later what to do with them)
if not save.is_empty:
saves.append(save)
saves.append(SaveGame.new("%s/%s" % [State.user_saves_path, path.get_basename()]))
_sort_saves()
_rebuild_buttons()
@ -72,34 +67,11 @@ func _rebuild_buttons() -> void:
save_box.add_child(new_button)
save_buttons.append(new_button)
new_button.pressed.connect(_on_game_picked.bind(i))
new_button.delete_requested.connect(_on_delete_requested.bind(i))
func _on_game_picked(id: int) -> void:
_picked.emit(saves[id])
func _on_delete_requested(id: int) -> void:
var save_to_delete := saves[id]
var save_path := save_to_delete.filepath
var thumbnail_path := "%s/thumbnails/%s.png" % [save_path.get_base_dir(), save_to_delete.unique_save_name]
# Delete the save file
if FileAccess.file_exists(save_path):
DirAccess.remove_absolute(save_path)
print_debug("Deleted save file: %s" % save_path)
# Delete the thumbnail
if FileAccess.file_exists(thumbnail_path):
DirAccess.remove_absolute(thumbnail_path)
print_debug("Deleted thumbnail: %s" % thumbnail_path)
# Reload the save list
_load_games()
# Refocus on a valid button if any exist
if save_buttons.size() > 0:
var focus_index := mini(id, save_buttons.size() - 1)
save_buttons[focus_index].grab_focus()
func get_most_recent_save() -> SaveGame:
_sort_saves()