boot/: opening_screen (attribution -> title/flavor text placeholder, explicitly marked TODO for a later real cinematic, skippable by keypress/click) -> main_menu (Neues Spiel/Fortsetzen/Optionen/Credits/ Beenden, ConfirmationDialog for overwrite-existing-save) -> options_menu (TabContainer with 4 empty placeholder categories: Grafik/Sound/ Steuerung/Spieloptionen) and credits_screen, both just navigable stubs. project.godot's run/main_scene now points at boot/opening_screen.tscn instead of directly at the gameplay scene. autoload/save_manager.gd: new SaveManager singleton. Deliberately minimal — persists only a timestamp to user://savegame.json — but a real, working has_save()/save_game()/load_game()/start_new_game() round trip, enough to back the menu's Start/Continue/overwrite-confirm logic without inventing game state that doesn't exist yet. player/equipment/: Equipment (Node) + EquipmentItem (Resource) stub classes per the user's explicit ask for a "class diagram skeleton" to build on later — exported fields and empty TODO methods, no logic. Wired as a child of world/island.tscn's real Player (not player.tscn, which is only a debug prototype scene, not the actual game's player). player.gd's new `equipment` onready var uses get_node_or_null() since that child only exists on the real player, not every scene reusing this script. Verified headless: all new boot scenes load cleanly, a full boot smoke test via run/main_scene succeeds, and — since button-click navigation can't be simulated headlessly — SaveManager's actual save/load/ overwrite/cleanup cycle was exercised directly and behaves correctly (has_save flips true/false correctly, loaded data matches what was saved). Manual in-editor check of the menu navigation itself is still needed and not yet done.
65 lines
1.7 KiB
GDScript
Executable File
65 lines
1.7 KiB
GDScript
Executable File
extends CharacterBody3D
|
|
|
|
@export var move_speed: float = 5.0
|
|
@export var gravity: float = 9.8
|
|
|
|
var camera_rig: Node3D
|
|
|
|
@onready var sprite: AnimatedSprite3D = $AnimatedSprite3D
|
|
# Nur in world/island.tscn vorhanden, nicht in diesem Debug-Prototyp -> optional
|
|
@onready var equipment: Equipment = get_node_or_null("Equipment")
|
|
|
|
func _ready() -> void:
|
|
camera_rig = get_tree().get_first_node_in_group("camera_rig")
|
|
if camera_rig == null:
|
|
push_error("CameraRig nicht gefunden! Gruppe 'camera_rig' gesetzt?")
|
|
|
|
func _physics_process(delta: float) -> void:
|
|
# Bewegung sperren während Interaction
|
|
if InteractionManager.active:
|
|
velocity = Vector3.ZERO
|
|
return
|
|
# Schwerkraft
|
|
if not is_on_floor():
|
|
velocity.y -= gravity * delta
|
|
|
|
# Eingabe
|
|
var input_dir = Vector2(
|
|
Input.get_axis("move_left", "move_right"),
|
|
Input.get_axis("move_forward", "move_backward")
|
|
)
|
|
|
|
if input_dir != Vector2.ZERO:
|
|
var cam_basis = camera_rig.global_transform.basis
|
|
var forward = -cam_basis.z
|
|
var right = cam_basis.x
|
|
forward.y = 0
|
|
right.y = 0
|
|
forward = forward.normalized()
|
|
right = right.normalized()
|
|
|
|
var move_dir = (forward * -input_dir.y + right * input_dir.x).normalized()
|
|
velocity.x = move_dir.x * move_speed
|
|
velocity.z = move_dir.z * move_speed
|
|
|
|
_update_animation(input_dir)
|
|
else:
|
|
velocity.x = move_toward(velocity.x, 0, move_speed)
|
|
velocity.z = move_toward(velocity.z, 0, move_speed)
|
|
sprite.play("idle")
|
|
|
|
move_and_slide()
|
|
|
|
func _update_animation(input_dir: Vector2) -> void:
|
|
|
|
if input_dir.y > 0.1:
|
|
sprite.play("move_down")
|
|
elif input_dir.y < -0.1:
|
|
sprite.play("move_up")
|
|
elif input_dir.x > 0.1:
|
|
sprite.play("move_right")
|
|
elif input_dir.x < -0.1:
|
|
sprite.play("move_left")
|
|
else:
|
|
sprite.play("idle")
|