From 5362a6280ba63ebb4fd305a54e2f4c9f6d2184d9 Mon Sep 17 00:00:00 2001 From: torsten Date: Sat, 21 Feb 2026 10:07:53 +0100 Subject: [PATCH] Neustart mit LFS --- .gitattributes | 7 + .gitignore | 17 ++ README.md | 3 + prompts/main.txt | 82 ++++++ tools/start_server.sh | 3 + weltenauge/.continue/refactor-godot.prompt | 0 weltenauge/.cursorrules | 1 + weltenauge/.editorconfig | 4 + weltenauge/.vscode/settings.json | 3 + .../01_core/base_classes/character_data.gd | 35 +++ .../base_classes/character_data.gd.uid | 1 + weltenauge/01_core/base_classes/save_load.gd | 33 +++ .../01_core/base_classes/save_load.gd.uid | 1 + weltenauge/01_core/components/camera_logic.gd | 12 + .../01_core/components/camera_logic.gd.uid | 1 + weltenauge/01_core/systems/chat_manager.gd | 26 ++ .../01_core/systems/chat_manager.gd.uid | 1 + weltenauge/01_core/systems/game_events.gd | 13 + weltenauge/01_core/systems/game_events.gd.uid | 1 + weltenauge/01_core/systems/network_manager.gd | 249 ++++++++++++++++++ .../01_core/systems/network_manager.gd.uid | 1 + .../02_library/meshes/humanoid/dude.glb | 3 + .../meshes/humanoid/dude.glb.import | 54 ++++ .../02_library/meshes/humanoid/dude.tres | 99 +++++++ .../02_library/meshes/humanoid/dude.tscn | 6 + .../icons/program/logo_projekt_weltenauge.png | 3 + .../logo_projekt_weltenauge.png.import | 40 +++ weltenauge/04_entities/player/player.tscn | 38 +++ .../04_entities/player/player_controller.gd | 137 ++++++++++ .../player/player_controller.gd.uid | 1 + .../04_entities/ui/windows/account/login.gd | 16 ++ .../ui/windows/account/login.gd.uid | 1 + .../04_entities/ui/windows/account/login.tscn | 82 ++++++ .../character_create/character_create.gd | 22 ++ .../character_create/character_create.gd.uid | 1 + .../character_create/character_create.tscn | 42 +++ .../character_select/character_select.gd | 31 +++ .../character_select/character_select.gd.uid | 1 + .../character_select/character_select.tscn | 33 +++ .../areas/outdoor/grass/keuwin_grasland.tscn | 61 +++++ .../areas/planes/fugue/halle_der_ahnen.tscn | 65 +++++ weltenauge/05_worlds/prefabs/portal/portal.gd | 4 + .../05_worlds/prefabs/portal/portal.gd.uid | 1 + .../05_worlds/prefabs/portal/portal.tscn | 19 ++ weltenauge/PROJEKT_UEBERSICHT.md | 185 +++++++++++++ weltenauge/game_root.gd | 4 + weltenauge/game_root.gd.uid | 1 + weltenauge/game_root.tscn | 17 ++ weltenauge/main.tscn | 8 + weltenauge/project.godot | 46 ++++ weltenauge_struktur.txt | 42 +++ workbench/zauberturm_html/index.html | 126 +++++++++ .../logo_projekt_weltenauge.png | 3 + workbench/zauberturm_html/logo_zauberturm.png | 3 + 54 files changed, 1689 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 README.md create mode 100644 prompts/main.txt create mode 100755 tools/start_server.sh create mode 100644 weltenauge/.continue/refactor-godot.prompt create mode 100644 weltenauge/.cursorrules create mode 100644 weltenauge/.editorconfig create mode 100644 weltenauge/.vscode/settings.json create mode 100644 weltenauge/01_core/base_classes/character_data.gd create mode 100644 weltenauge/01_core/base_classes/character_data.gd.uid create mode 100644 weltenauge/01_core/base_classes/save_load.gd create mode 100644 weltenauge/01_core/base_classes/save_load.gd.uid create mode 100644 weltenauge/01_core/components/camera_logic.gd create mode 100644 weltenauge/01_core/components/camera_logic.gd.uid create mode 100644 weltenauge/01_core/systems/chat_manager.gd create mode 100644 weltenauge/01_core/systems/chat_manager.gd.uid create mode 100644 weltenauge/01_core/systems/game_events.gd create mode 100644 weltenauge/01_core/systems/game_events.gd.uid create mode 100644 weltenauge/01_core/systems/network_manager.gd create mode 100644 weltenauge/01_core/systems/network_manager.gd.uid create mode 100644 weltenauge/02_library/meshes/humanoid/dude.glb create mode 100644 weltenauge/02_library/meshes/humanoid/dude.glb.import create mode 100644 weltenauge/02_library/meshes/humanoid/dude.tres create mode 100644 weltenauge/02_library/meshes/humanoid/dude.tscn create mode 100644 weltenauge/02_library/ui_assets/icons/program/logo_projekt_weltenauge.png create mode 100644 weltenauge/02_library/ui_assets/icons/program/logo_projekt_weltenauge.png.import create mode 100644 weltenauge/04_entities/player/player.tscn create mode 100644 weltenauge/04_entities/player/player_controller.gd create mode 100644 weltenauge/04_entities/player/player_controller.gd.uid create mode 100644 weltenauge/04_entities/ui/windows/account/login.gd create mode 100644 weltenauge/04_entities/ui/windows/account/login.gd.uid create mode 100644 weltenauge/04_entities/ui/windows/account/login.tscn create mode 100644 weltenauge/04_entities/ui/windows/character_create/character_create.gd create mode 100644 weltenauge/04_entities/ui/windows/character_create/character_create.gd.uid create mode 100644 weltenauge/04_entities/ui/windows/character_create/character_create.tscn create mode 100644 weltenauge/04_entities/ui/windows/character_select/character_select.gd create mode 100644 weltenauge/04_entities/ui/windows/character_select/character_select.gd.uid create mode 100644 weltenauge/04_entities/ui/windows/character_select/character_select.tscn create mode 100644 weltenauge/05_worlds/areas/outdoor/grass/keuwin_grasland.tscn create mode 100644 weltenauge/05_worlds/areas/planes/fugue/halle_der_ahnen.tscn create mode 100644 weltenauge/05_worlds/prefabs/portal/portal.gd create mode 100644 weltenauge/05_worlds/prefabs/portal/portal.gd.uid create mode 100644 weltenauge/05_worlds/prefabs/portal/portal.tscn create mode 100644 weltenauge/PROJEKT_UEBERSICHT.md create mode 100644 weltenauge/game_root.gd create mode 100644 weltenauge/game_root.gd.uid create mode 100644 weltenauge/game_root.tscn create mode 100644 weltenauge/main.tscn create mode 100644 weltenauge/project.godot create mode 100644 weltenauge_struktur.txt create mode 100644 workbench/zauberturm_html/index.html create mode 100644 workbench/zauberturm_html/logo_projekt_weltenauge.png create mode 100644 workbench/zauberturm_html/logo_zauberturm.png diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..48b1a8f --- /dev/null +++ b/.gitattributes @@ -0,0 +1,7 @@ +*.blend filter=lfs diff=lfs merge=lfs -text +*.glb filter=lfs diff=lfs merge=lfs -text +*.fbx filter=lfs diff=lfs merge=lfs -text +*.png filter=lfs diff=lfs merge=lfs -text +*.jpg filter=lfs diff=lfs merge=lfs -text +*.tga filter=lfs diff=lfs merge=lfs -text +*.zip filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bf83296 --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +# ---> Godot +# Godot 4+ specific ignores +.godot/ + +# Godot-specific ignores +.import/ +export.cfg +export_presets.cfg + +# Imported translations (automatically generated from CSV files) +*.translation + +# Mono-specific ignores +.mono/ +data_*/ +mono_crash.*.json + diff --git a/README.md b/README.md new file mode 100644 index 0000000..ab8ee4f --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# weltenauge + +NWN-ähnliche Multiplayer-Welt. \ No newline at end of file diff --git a/prompts/main.txt b/prompts/main.txt new file mode 100644 index 0000000..781aa69 --- /dev/null +++ b/prompts/main.txt @@ -0,0 +1,82 @@ +Für mein NWN/Godot Projekt brauche ich eine Referenzimplementierung für die Client/Server Logik. +Client und Server sind in der gleichen Codebase/Projekt. Normal aufgerufen, kann der Anwender +Join und Host auswählen. Stellt das Programm fest, dass es headless läuft, wird automatisch als Host gestartet. +Die Szenen(Areas) in denen die Spieler laden, werden nach bedarf geladen. Wenn Spieler A in Area 1 ist, hat sein +Client nur Area 1 geladen. Wenn Spieler B in Area 2 ist, hat sein Client nur Area 2 geladen. Nur +Host hat beide Areas geladen, und synchronisiert seine Area 1 mit Area 1 des Clients von Spieler A und +seine Area 2 mit der Area 2 des Clients von Spieler B. Ein Übergang zu einer anderen Area kann vom Host veranlasst +werden . Er lädt die neue Area(Szene) und erzeugt den Avatar des Spielers. Der Client des betroffenen Spielers +muss die neue Area(Szene) auf seiner Seite ebenfalls laden, damit die Szenenobjekte synchronisiert werden können. + +Sorge dafür, dass die Areas auf dem Server sich nicht überlagern. Jeder MultiPlayerSynchronizer sollte nur +position der Spieler synchronisieren, aber nicht global_position. + +Jede Area soll auf dem Server einen eigenen SubViewport haben. (Godot Multi-World Physics) +Sorge mit Visibility Filtering dafür, dass die MultiplayerSynchronizer nur die Objekte in ihrer jeweiligen Area synchronisieren. + +Aus Performancegründen soll die Click-to-Move Funktionalität über AStar implementiert werden. + +Das Spielermodell liegt unter res://systems/player/player.tres +Area 1 liegt unter res://maps/outdoor/grass/keuwin_grasland.tscn +Area 2 liegt unter res://maps/planes/fugue/halle_der_ahnen.tscn + +Alle Scripte müssen in einem thematisch passenden Subfolder unterhalb res://systems/ abgelegt werden. + +Dies ist die Struktur des Projektes: + +/assets/ # REINE ROHDATEN (Dumm, keine Spiellogik) + /characters/ + /humanoid/ + /base_meshes/ # .glb Dateien und .tres (Mesh-Resources) + /materials/ # .tres (Material-Resources) + /environment/ + /dungeon/ # Tileset-Modelle + /nature/ # Bäume, Steine + /items/ + /armor/ + /weapons/ + /consumables/ + +/systems/ # DAS GEHIRN (Logik, muss auf dem Server laufen) + /core/ # Game-Loop, Signale, State-Machine + /network/ # Multiplayer, Peer-Handling, Headless-Logik + /player/ # Player-Controller (ohne Kamera/UI) + /ai/ # NPC-Verhalten, Monster-Logik + /combat/ # Regelwerk, Würfelwürfe, Stats + /dialogue/ # Gesprächs-Logik, Quests + /inventory/ # Item-Management, Container + /interaction/ # Türen, Hebel, Portale + /navigation/ # AStar-Management, Pathfinder + /simulation/ # Tag-Nacht-Zyklus, Fraktionen + /data/ # Speicher-Logik, DB-Anbindung + /fx/ # Wetter-Logik, Sound-Trigger + +/ui/ # DAS GESICHT (Nur für den Client) + /hud/ # Permanente Anzeigen (HP, Chat) + /menus/ # Startmenü, Optionen + /windows/ # Inventar-Fenster, Charakterbogen + /components/ # Buttons, Slots, wiederverwendbare Teile + /themes/ # Schriften, Styles, Visual Design + +/maps/ # DIE WELT (Szenen, in denen man spielt) + /templates/ # Basis-Setups für neue Karten + /outdoor/ # Wald, Wüste, Gebirge + /indoor/ # Häuser, Burgen + /dungeons/ # Krypten, Höhlen + /planes/ # Andere Existenzebenen + +/audio/ # KLANGWELT + /music/ # Hintergrundmusik (.ogg) + /sfx/ # Geräusche (Schwert, Schritte) + /ambience/ # Atmosphäre-Loops (Wind, Feuer) + +/data/ # DIE DEFINITIONEN (Inhaltliche Datenkarten) + /items/ # .tres Dateien für jedes Schwert/Schild + /tables/ # SQLite .db oder JSON-Tabellen + +/licencing/ # Rechtliches (Asset-Lizenzen) + + +Erstelle bitte eine Referenzimplementierung, die folgende Funktionen abdeckt: Host Game, Join Game, Scenenwechsel, Server-Authority +Erzeuge so wenige Dateien wie möglich. + diff --git a/tools/start_server.sh b/tools/start_server.sh new file mode 100755 index 0000000..75fc063 --- /dev/null +++ b/tools/start_server.sh @@ -0,0 +1,3 @@ +#/opt/godot/Godot_v4.6-stable_linux.x86_64 --path /home/torsten/repos/weltenauge/weltenauge --headless -- --server +#/opt/godot/Godot_v4.6-stable_linux.x86_64 --path /home/torsten/Dokumente/Gamedev/Projekt_Weltenauge/Projekt_Weltenauge --headless -- --server +/opt/godot/Godot_v4.6-stable_linux.x86_64 --path /home/tschneider/Dokumente/GameDev/Projekt_Weltenauge/Projekt_Weltenauge --headless -- --server diff --git a/weltenauge/.continue/refactor-godot.prompt b/weltenauge/.continue/refactor-godot.prompt new file mode 100644 index 0000000..e69de29 diff --git a/weltenauge/.cursorrules b/weltenauge/.cursorrules new file mode 100644 index 0000000..29b7410 --- /dev/null +++ b/weltenauge/.cursorrules @@ -0,0 +1 @@ +Always use Godot 4.x GDScript syntax. Prefer static typing (e.g., var x: int = 5) and use the @onready annotation for nodes. \ No newline at end of file diff --git a/weltenauge/.editorconfig b/weltenauge/.editorconfig new file mode 100644 index 0000000..f28239b --- /dev/null +++ b/weltenauge/.editorconfig @@ -0,0 +1,4 @@ +root = true + +[*] +charset = utf-8 diff --git a/weltenauge/.vscode/settings.json b/weltenauge/.vscode/settings.json new file mode 100644 index 0000000..a9bd9e2 --- /dev/null +++ b/weltenauge/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "godotTools.editorPath.godot4": "/opt/godot/godot_v4.6-stable_linux.x86_64" +} \ No newline at end of file diff --git a/weltenauge/01_core/base_classes/character_data.gd b/weltenauge/01_core/base_classes/character_data.gd new file mode 100644 index 0000000..6ed0079 --- /dev/null +++ b/weltenauge/01_core/base_classes/character_data.gd @@ -0,0 +1,35 @@ +class_name CharacterData + +var id: String = "" # UUID des Charakters +var name: String = "Neuer Held" +var level: int = 1 +var area: String = "keuwin_grasland" +var position: Vector3 = Vector3.ZERO + +# Erstellt ein Dictionary – Vector3 bleibt ein Vector3! +func to_dict() -> Dictionary: + return { + "id": id, + "name": name, + "level": level, + "area": area, + "position": position + } + +func from_dict(data: Dictionary) -> void: + id = data.get("id", id) + name = data.get("name", name) + level = data.get("level", level) + area = data.get("area", area) + position = data.get("position", position) + +## Auf dem Client +#var data = mein_charakter.to_dict() +#update_server_pos.rpc(data) + +## Die RPC Funktion +#@rpc("any_peer") +#func update_server_pos(data: Dictionary): +# var server_char = CharacterData.new() +# server_char.from_dict(data) +# print(server_char.position) # Funktioniert sofort als Vector3 diff --git a/weltenauge/01_core/base_classes/character_data.gd.uid b/weltenauge/01_core/base_classes/character_data.gd.uid new file mode 100644 index 0000000..e409918 --- /dev/null +++ b/weltenauge/01_core/base_classes/character_data.gd.uid @@ -0,0 +1 @@ +uid://dsylf8nx1e2wc diff --git a/weltenauge/01_core/base_classes/save_load.gd b/weltenauge/01_core/base_classes/save_load.gd new file mode 100644 index 0000000..c7332bb --- /dev/null +++ b/weltenauge/01_core/base_classes/save_load.gd @@ -0,0 +1,33 @@ +class_name SaveLoad + +const SAVE_PATH = "user://player_data.json" + +static func save_character(data: CharacterData): + var save_dict = data.to_dict() + + var file = FileAccess.open(SAVE_PATH, FileAccess.WRITE) + if file: + file.store_string(JSON.stringify(save_dict, "\t")) + file.close() + print("Saved player data to ", SAVE_PATH) + else: + printerr("Error saving to ", SAVE_PATH) + +static func load_character() -> CharacterData: + var file = FileAccess.open(SAVE_PATH, FileAccess.READ) + if file: + var json_string = file.get_as_text() + file.close() + + var data = JSON.parse_string(json_string) + if data == null: + printerr("JSON Parse Error") + return null + + var char_data = CharacterData.new() + char_data.from_dict(data) + print("Loaded player data from ", SAVE_PATH) + return char_data + else: + print("No savefile found, returning default CharacterData.") + return null diff --git a/weltenauge/01_core/base_classes/save_load.gd.uid b/weltenauge/01_core/base_classes/save_load.gd.uid new file mode 100644 index 0000000..a4c4271 --- /dev/null +++ b/weltenauge/01_core/base_classes/save_load.gd.uid @@ -0,0 +1 @@ +uid://dhettikhnfdt4 diff --git a/weltenauge/01_core/components/camera_logic.gd b/weltenauge/01_core/components/camera_logic.gd new file mode 100644 index 0000000..fbdb185 --- /dev/null +++ b/weltenauge/01_core/components/camera_logic.gd @@ -0,0 +1,12 @@ +extends Camera3D + +var target: Node3D = null +#var offset = Vector3(0, 12, 8) # Klassische NWN Perspektive +var offset = Vector3(0, 3, 4) # Klassische NWN Perspektive + +func _process(delta): + if is_instance_valid(target) and target.is_inside_tree(): + # Sanftes Folgen (Lerp) für smoothe Bewegungen + var target_pos = target.global_position + offset + global_position = global_position.lerp(target_pos, delta * 5.0) + look_at(target.global_position) diff --git a/weltenauge/01_core/components/camera_logic.gd.uid b/weltenauge/01_core/components/camera_logic.gd.uid new file mode 100644 index 0000000..d78bd4b --- /dev/null +++ b/weltenauge/01_core/components/camera_logic.gd.uid @@ -0,0 +1 @@ +uid://ctxhm624vs1ya diff --git a/weltenauge/01_core/systems/chat_manager.gd b/weltenauge/01_core/systems/chat_manager.gd new file mode 100644 index 0000000..6e87f7a --- /dev/null +++ b/weltenauge/01_core/systems/chat_manager.gd @@ -0,0 +1,26 @@ +extends Node + +# Signal für die UI, um neue Nachrichten anzuzeigen +signal network_message_received(sender_name: String, message: String, channel: String) + +@rpc("any_peer", "call_remote", "reliable") +func send_message(message: String): + var peer_id = multiplayer.get_remote_sender_id() + + # Serverseitige Validierung (Spam-Schutz, Filter) + if message.length() > 256: + message = message.left(256) + + var sender_name = "Player " + str(peer_id) # Hier später Name aus DB laden + + # Ermittle die Area des Spielers (Sichtbarkeits-Logik) + var area = NetworkManager.player_area_map.get(peer_id, "") + + # Broadcast: Nur an Spieler in der gleichen Area senden (NWN-Style) + for id in NetworkManager.player_area_map: + if NetworkManager.player_area_map[id] == area: + cl_receive_message.rpc_id(id, sender_name, message, "local") + +@rpc("authority", "call_remote", "reliable") +func cl_receive_message(sender_name: String, message: String, channel: String): + network_message_received.emit(sender_name, message, channel) diff --git a/weltenauge/01_core/systems/chat_manager.gd.uid b/weltenauge/01_core/systems/chat_manager.gd.uid new file mode 100644 index 0000000..39bf8c5 --- /dev/null +++ b/weltenauge/01_core/systems/chat_manager.gd.uid @@ -0,0 +1 @@ +uid://c351ysuym2mdm diff --git a/weltenauge/01_core/systems/game_events.gd b/weltenauge/01_core/systems/game_events.gd new file mode 100644 index 0000000..6527b22 --- /dev/null +++ b/weltenauge/01_core/systems/game_events.gd @@ -0,0 +1,13 @@ +extends Node + +signal ui_join_requested(ip: String, username: String, password: String) +signal ui_host_requested() +signal ui_create_character_screen() +signal ui_character_created(character: CharacterData) +signal ui_character_select() +signal ui_area_join_requested(area_key: String) + +signal player_area_join_requested(area_key: String) + +signal network_show_characters(characters:Array) +signal network_new_character_received(character: CharacterData) diff --git a/weltenauge/01_core/systems/game_events.gd.uid b/weltenauge/01_core/systems/game_events.gd.uid new file mode 100644 index 0000000..ca9021e --- /dev/null +++ b/weltenauge/01_core/systems/game_events.gd.uid @@ -0,0 +1 @@ +uid://4xof67e363t4 diff --git a/weltenauge/01_core/systems/network_manager.gd b/weltenauge/01_core/systems/network_manager.gd new file mode 100644 index 0000000..8f1f74a --- /dev/null +++ b/weltenauge/01_core/systems/network_manager.gd @@ -0,0 +1,249 @@ +extends Node + +const PORT = 7777 +const MAX_CLIENTS = 32 + +# Kamera (wird nur auf dem Client gebraucht) +var local_camera: Camera3D = null + +# Mapping der Areas +var area_paths = { + "keuwin_grasland": "res://05_worlds/area/outdoor/grass/keuwin_grasland.tscn", + "ahnenhalle": "res://05_worlds/area/planes/fugue/halle_der_ahnen.tscn" +} + +# Server-seitiges Tracking: peer_id -> SubViewportContainer +var player_worlds = {} + +func _ready(): + GameEvents.ui_host_requested.connect(host_game) + GameEvents.ui_join_requested.connect(join_game) + GameEvents.ui_character_created.connect(request_character_store) + + if DisplayServer.get_name() == "headless" or OS.get_cmdline_args().has("--server"): + host_game() + +func host_game(): + var peer = ENetMultiplayerPeer.new() + peer.create_server(PORT, MAX_CLIENTS) + multiplayer.multiplayer_peer = peer + multiplayer.peer_connected.connect(_on_player_connected) + multiplayer.peer_disconnected.connect(_on_player_disconnected) + print("Server gestartet. id=" + str(multiplayer.get_unique_id())) + +func join_game(ip: String, username: String, password: String): + print("NetworkManager.join_game(", ip, "/", username, "/", password) + var peer = ENetMultiplayerPeer.new() + peer.create_client(ip, PORT) + multiplayer.multiplayer_peer = peer + + +func _on_player_connected(id): + print("Player connected with id:", id) + cl_show_characters.rpc_id(id, []) + + +@rpc("authority", "call_remote", "reliable") +func cl_show_characters(characters): + print("Got client_show_characters") + GameEvents.network_show_characters.emit(characters) + + +func _on_player_disconnected(id): + if not multiplayer.is_server(): return + + print("Player disconnected with id: ", id) + + # Der MultiplayerSpawner entfernt den Node automatisch aus dem Szenenbaum. + # Unsere einzige Aufgabe ist es, unseren internen Zustand aufzuräumen. + if player_worlds.has(id): + print("Removing player ", id, " from internal tracking.") + player_worlds.erase(id) + +func change_player_area(id: int, area_key: String): + if not multiplayer.is_server(): return + print("Server is loading ", area_key) + var path = area_paths[area_key] + + var container_name = area_key + "_container" + var viewport_name = area_key + "_viewport" + + # Server: Erstelle SubViewportContainer + SubViewport für physikalische Trennung, falls nicht existent + var container_node = get_node_or_null(container_name) + if not container_node: + #container_node = SubViewportContainer.new() + container_node = Node3D.new() + container_node.name = container_name + add_child(container_node) + + var viewport_node = container_node.get_node_or_null(viewport_name) + var map_instance = null + if not viewport_node: + viewport_node = SubViewport.new() + viewport_node.name = viewport_name + viewport_node.own_world_3d = true # Multi-World Physics! + container_node.add_child(viewport_node) + else: + map_instance = viewport_node.get_node_or_null(area_key) + + map_instance = load(path).instantiate() + map_instance.name = area_key + viewport_node.add_child(map_instance) + + # --- ALTEN ZUSTAND AUFRÄUMEN --- + if player_worlds.has(id): + player_worlds.erase(id) + + # --- NEUEN ZUSTAND REGISTRIEREN --- + player_worlds[id] = container_node + + # Client anweisen, Szene zu laden + cl_load_area.rpc_id(id, area_key) + + # Avatar auf Server erstellen erfolgt erst wenn Client Vollzug meldet (client_ready_to_spawn) + +@rpc("any_peer", "call_local", "reliable") +func client_ready_to_spawn(area_key:String): + var id = multiplayer.get_remote_sender_id() + if not multiplayer.is_server(): return + + if player_worlds.has(id): + var container_node = player_worlds[id] + var viewport_node = container_node.get_node(area_key+"_viewport") + var spawn_target_node = viewport_node.get_node(area_key).get_node("SpawnPoints") + + print("Container Node:", container_node) + print("Viewport Node:", viewport_node) + print("spawn target Node:", spawn_target_node) + + print("Client ", id, " bereits für Spawning.") + _spawn_player_in_world(id, spawn_target_node) + + +@rpc("authority", "call_remote", "reliable") +func cl_load_area(area_key): + print("Client loads area") + # Client löscht alte Map, lädt neue lokal in das Root + for child in get_children(): + if child.is_in_group("maps"): child.queue_free() + + var container_name = area_key + "_container" + var viewport_name = area_key + "_viewport" + + var container_node = SubViewportContainer.new() + container_node.name = container_name + container_node.stretch = true + container_node.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) + add_child(container_node) + container_node.add_to_group("maps") + + var viewport_node = SubViewport.new() + viewport_node.own_world_3d = true + viewport_node.name = viewport_name + viewport_node.size = Vector2i(1280,720) + container_node.add_child(viewport_node) + + var map = load(area_paths[area_key]).instantiate() + map.name = area_key + viewport_node.add_child(map) + + # Kamera-Setup nur beim ersten Laden oder falls sie gelöscht wurde + + if not multiplayer.is_server(): #and not is_instance_valid(local_camera): + print("Setting up camera") + setup_client_camera(viewport_node) + + # Jetzt können wir dem Server ok fürs Spawnen geben + client_ready_to_spawn.rpc_id(1, area_key) # Server ist immer id=1 + +func setup_client_camera(node): + local_camera = Camera3D.new() + local_camera.name = "Camera3D" + local_camera.set_script(load("res://01_core/components/camera_logic.gd")) + local_camera.current = true + node.add_child(local_camera) + # Ein AudioListener gehört auf dem Client auch an die Kamera + local_camera.add_child(AudioListener3D.new()) + + +func _spawn_player_in_world(id, spawn_target_node): + var player_scene = load("res://systems/player/player.tscn") + var player = player_scene.instantiate() + player.name = str(id) + player.get_node("PlayerNameLabel").text = str(id) + + #player.set_multiplayer_authority(id) + player.set_multiplayer_authority(1) + spawn_target_node.add_child(player, true) + + # SCHRITT 3: Jetzt ist der Spieler garantiert bereit und konfiguriert. + # Alle folgenden Operationen sind sicher. + var sync = player.get_node("MultiplayerSynchronizer") + + # Visibility Filtering: Nur Spieler im gleichen Viewport sehen sich + for other in spawn_target_node.get_children(): + if other.is_in_group("players"): + var other_id = other.name.to_int() + # Der neue Spieler sieht den alten + other.get_node("MultiplayerSynchronizer").set_visibility_for(id, true) + # Der alte Spieler sieht den neuen + sync.set_visibility_for(other_id, true) + + # Stelle sicher, dass der Spieler IMMER für seinen eigenen Client sichtbar ist. + # Dies ist die wichtigste Regel, damit der Client seine eigenen Updates erhält. + sync.set_visibility_for(id, true) + + # Wir schicken dem Client eine Info, welchen Node er verfolgen soll + # (Da der Name des Player-Nodes die ID ist, weiß der Client welcher seiner ist) + cl_assign_camera_target.rpc_id(id, str(id)) + +@rpc("authority", "call_remote", "reliable") +func cl_assign_camera_target(node_name: String): + # Der Client sucht seinen Player-Node im Baum + var p = get_tree().root.find_child(node_name, true, false) + if p and local_camera: + local_camera.target = p + + # var camera = get_viewport().get_camera_3d() + +@rpc("any_peer", "call_remote", "reliable") +func srv_handle_area_change_request(area_key: String): + var id = multiplayer.get_remote_sender_id() + if not player_worlds.has(id): return + + print("Server: Player ", id, " requests area change to ", area_key) + + # 1. Alten Spieler finden und entfernen + var old_container = player_worlds[id] + print("container name=", old_container) + var old_viewport = old_container.get_child(0) + print("Viewport name=", old_viewport) + var player_node = old_viewport.get_child(0).get_node("SpawnPoints").get_node(str(id)) + + if player_node: + player_node.queue_free() + + else: + print("Old viewport:", old_viewport) + print("Player for erasing not found:", id) + + # 2. Bestehende Funktion für den Wechsel aufrufen + change_player_area(id, area_key) + +func request_character_store(new_character: CharacterData): + var character_dict = new_character.to_dict() + srv_store_new_character.rpc_id(1,character_dict) + +@rpc("any_peer", "call_remote", "reliable") +func srv_store_new_character(character_dict: Dictionary): + var new_character = CharacterData.new() + new_character.from_dict(character_dict) + # TODO: In Zukunft erhalten neue Charaktere einen area_key und position gesetzt + var client_id = multiplayer.get_remote_sender_id() + cl_receive_new_character.rpc_id(client_id, new_character.to_dict()) + +@rpc("authority", "call_remote", "reliable") +func cl_receive_new_character(char_dict: Dictionary): + var new_character = CharacterData.new() + new_character.from_dict(char_dict) + GameEvents.network_new_character_received.emit(new_character) diff --git a/weltenauge/01_core/systems/network_manager.gd.uid b/weltenauge/01_core/systems/network_manager.gd.uid new file mode 100644 index 0000000..e9b10b5 --- /dev/null +++ b/weltenauge/01_core/systems/network_manager.gd.uid @@ -0,0 +1 @@ +uid://tniwwfk5703p diff --git a/weltenauge/02_library/meshes/humanoid/dude.glb b/weltenauge/02_library/meshes/humanoid/dude.glb new file mode 100644 index 0000000..f5c3064 --- /dev/null +++ b/weltenauge/02_library/meshes/humanoid/dude.glb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:03128ab9dd8f57dbb438779ceaca80a4b848134a2c4d4d276dfd58d35ddb08e1 +size 13600 diff --git a/weltenauge/02_library/meshes/humanoid/dude.glb.import b/weltenauge/02_library/meshes/humanoid/dude.glb.import new file mode 100644 index 0000000..b989a1f --- /dev/null +++ b/weltenauge/02_library/meshes/humanoid/dude.glb.import @@ -0,0 +1,54 @@ +[remap] + +importer="scene" +importer_version=1 +type="PackedScene" +uid="uid://do451iws7munv" +path="res://.godot/imported/dude.glb-ff042c8f74305db1da7e47c7a83b2cea.scn" + +[deps] + +source_file="res://02_library/meshes/humanoid/dude.glb" +dest_files=["res://.godot/imported/dude.glb-ff042c8f74305db1da7e47c7a83b2cea.scn"] + +[params] + +nodes/root_type="" +nodes/root_name="" +nodes/root_script=null +nodes/apply_root_scale=true +nodes/root_scale=1.0 +nodes/import_as_skeleton_bones=false +nodes/use_name_suffixes=true +nodes/use_node_type_suffixes=true +meshes/ensure_tangents=true +meshes/generate_lods=true +meshes/create_shadow_meshes=true +meshes/light_baking=1 +meshes/lightmap_texel_size=0.2 +meshes/force_disable_compression=false +skins/use_named_skins=true +animation/import=true +animation/fps=30 +animation/trimming=false +animation/remove_immutable_tracks=true +animation/import_rest_as_RESET=false +import_script/path="" +materials/extract=0 +materials/extract_format=0 +materials/extract_path="" +_subresources={ +"meshes": { +"Dude_Cube_002": { +"generate/lightmap_uv": 0, +"generate/lods": 0, +"generate/shadow_meshes": 0, +"lods/normal_merge_angle": 20.0, +"save_to_file/enabled": true, +"save_to_file/fallback_path": "res://assets/characters/humanoid/Dude.tres", +"save_to_file/path": "uid://c6h0s0by6gg3n" +} +} +} +gltf/naming_version=2 +gltf/embedded_image_handling=1 diff --git a/weltenauge/02_library/meshes/humanoid/dude.tres b/weltenauge/02_library/meshes/humanoid/dude.tres new file mode 100644 index 0000000..43ffd0d --- /dev/null +++ b/weltenauge/02_library/meshes/humanoid/dude.tres @@ -0,0 +1,99 @@ +[gd_resource type="ArrayMesh" format=4 uid="uid://bi22s15yeh6mw"] + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_h8qyk"] +resource_name = "pink.002" +cull_mode = 2 +albedo_color = Color(0.8201731, 0.17311497, 0.9063355, 1) +roughness = 0.5 + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_c6dnh"] +resource_name = "schwarz" +cull_mode = 2 +albedo_color = Color(0, 0, 0, 1) +roughness = 0.5 + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_i8imj"] +resource_name = "weiss" +cull_mode = 2 +albedo_color = Color(0.9063318, 0.9063318, 0.9063318, 1) +roughness = 0.5 + +[sub_resource type="ArrayMesh" id="ArrayMesh_wt8sa"] +_surfaces = [{ +"aabb": AABB(-0.28403106, 0.008408189, -0.083770275, 0.6, 0.6000001, 0.60000014), +"format": 34896613377, +"index_count": 582, +"index_data": PackedByteArray("PQAGAD4APQAEAAYAPwAGAAQAQAAEAD0APwAEAEAAHAAGAD8AHAA+AAYAQQA/AEAAQgA9AD4AQAA9AEMAQwA9AEIARABAAEMAQQBAAEQAGAA/AEEAGAAcAD8ANABBAEQAHAAYABoAHAAaAB0AGAAZABoAHgAcAB0AHgA+ABwAHgAdABsAFgAbABkAGAAWABkAFgAeABsAEQAYAEEAEQBBADQARQAWABgARQAYABEARQARABIAEQANABIARQASABMAEQBGAA0ARgARADQARgALAA0ARwBFABMARwAWAEUARwATABUARwAfABYAHwAeABYAFABHABUAFAAfAEcAFAAVABAARgAIAAsACAAKAAsARgA0AEgACABGAEgASAA0AEkANABEAEkASQBEAEoARABDAEoASABJAEsASgBDAEwAQwBCAEwASQBKAE0ASwBJAE0ASgBMADUATQBKADUATgBIAEsACABIAE4ASwBNAE8ATQA1AFAATwBNAFAAUQBLAE8ATgBLAFEABwBOAFEAUgAIAE4AUgBOAAcABwBRAAAAAABSAAcAUQBPAFMAAABRAFMAUwBPAFQATwBQAFQAAABTAFUAAABVAFIAUwBUAFYAVQBTAFYAVABQAFcAVQBYAFIAWAAIAFIAVQBWAFkAVQBZAFgAVgBUAFoAVABXAFoAWQBWAFsAVgBaAFsAWABcAAgACABcAAoAXAAMAAoAXQBcAFgAWQBdAFgAXABeAAwAXQBeAFwAXgAOAAwAWQBfAF0AWQBbAF8AYABeAF0AXwBgAF0AXgAJAA4AYAAJAF4ACQAPAA4AXwBhAGAACQBiAA8AYgAQAA8AAgAJAGAAYQACAGAAYgAUABAAYQABAAIAAgAwAAkAMABiAAkAAQAwAAIAMgAUAGIAMAAyAGIAAQAxADAAMQAyADAAYQAxAAEAXwBjAGEAYQBjADEAXwBbAGMAMQAzADIAYwAzADEAWwBkAGMAYwBkADMAWwBaAGQAMwA8ADIAMgA8ABQAZABlADMAMwBlADwAWgBmAGQAZABmAGUAWgBXAGYAPAAXABQAZQA7ADwAPAA7ABcAZgBnAGUAZQBnADsAVwBoAGYAZgBoAGcAFAAXAB8AVwBpAGgAUABpAFcAUAA1AGkANQBqAGkANQBMAGoAaQBrAGgAaQBqAGsAaABrADgAaAA4AGcATAA2AGoATABCADYAagBsAGsAagA2AGwAawADADgAawBsAAMAOAADAAUAZwA4ADoAOAAFADoAZwA6ADsAAwA5AAUAAwBsADkAOgAFADkAOwA6ACEAOgA5ACEAbAA3ADkAbAA2ADcAIQA5ADcAOwAhABcANgBtADcANgBCAG0AQgA+AG0AHgBtAD4AIAA3AG0AIABtAB4AIQA3ACAAHwAgAB4AKgAhACsAKwAhACwAIQAqACkAIAAsACEAIQApABcAIAAtACwAIAAuAC0ALgAgAC8ALwAgACYAHwAmACAAHwAkACYAJAAfACMAIwAfACIAFwAiAB8AFwAlACIAFwAnACUAJwAXACgAKAAXACkA"), +"lods": [0.00012957268, PackedByteArray("AAABAAIAAQAwAAIAAQAxADAAAAAxAAEAAgAwAAkAMQAyADAAMAAyAAkAMQAzADIAAAAzADEAAAACAAcAAgAIAAcAAgAJAAgABwAEAAAACAA0AAcABwA0AAQAAAAEADUAAAA1ADMANAAGAAQANQAEADYANgAEAAYANgADADUAEQAGADQACAARADQANgAGADcAAwA2ADcANQADADgANQA4ADMAOAADAAUAAwA3ADkAAwA5AAUAOAAFADoAOgAFADkAMwA4ADoAIQA5ADcAOgA5ACEAMwA6ADsAOwA6ACEAMwA7ADwAMwA8ADIAPAA7ABcAOwAhABcAMgA8ABQAPAAXABQAMgAUAAkAJwAXACgAFwAnACUAKAAXACkAIQApABcAFwAlACIAIQAqACkAKgAhACsAKwAhACwAFwAiAB8AFAAXAB8AIwAfACIAJAAfACMAIAAsACEAHwAkACYAHwAmACAALwAgACYAIQA3ACAALgAgAC8AIAAtACwAIAAuAC0AIAA3AAYAHwAgAB4AIAAGAB4AHwAeABYAFAAfABYAHgAGABwAHgAcAB0AHgAdABsAFgAeABsAHAAaAB0AFgAbABkAGAAcAAYAHAAYABoAGAAZABoAGAAWABkAEQAYAAYAEQAWABgAFAAWABEAFAARABMAEQASABMAFAATABUAEQANABIAFAAVABAAEQAIAA0ACAALAA0ACAAKAAsACAAMAAoACQAUABAACAAJAAwACQAQAA8ACQAOAAwACQAPAA4A"), 0.00025139967, PackedByteArray("AAABAAIAAAADAAEAAAAEAAMAAQADAAUAAQAFAAIAAwAEAAYAAwAGAAUABwAEAAAAAAACAAcABwAGAAQAAgAIAAcACAAGAAcAAgAJAAgAAgAFAAkACAAKAAsACAAMAAoACAALAA0ACAAJAAwACQAOAAwACQAPAA4ACQAQAA8AEQAIAA0AEQANABIACAARAAYAEQASABMACQAUABAAFAARABMAFAAVABAAFAATABUABQAUAAkAFAAWABEABQAXABQAEQAWABgAEQAYAAYAGAAWABkAGAAZABoAFgAbABkAHAAYABoAGAAcAAYAHAAaAB0AHgAdABsAHgAcAB0AFgAeABsAHgAGABwAFAAfABYAHwAeABYAFAAXAB8AIAAGAB4AIAAFAAYAHwAgAB4ABQAhABcAIQAFACAAFwAiAB8AIwAfACIAJAAfACMAFwAlACIAHwAkACYAHwAmACAAFwAnACUAJwAXACgAKAAXACkAIQApABcAIQAqACkAKgAhACsAKwAhACwAIAAsACEAIAAtACwAIAAuAC0ALgAgAC8ALwAgACYA")], +"name": "pink.002", +"primitive": 3, +"uv_scale": Vector4(0, 0, 0, 0), +"vertex_count": 110, +"vertex_data": PackedByteArray("AAAAAAAAAAD//wAAAAAAAP//AAD//wAA/////wAAAAAAAP//AAAAAP///////wAAAAD/////AAAAAAAA//8AADIzqir//wAAzMyqKv//AABlZqoqefAAADIzqip58AAAmZmqKnnwAAAyM1RVefAAAMzMqip58AAAzMxUVXnwAADMzP9/efAAADIz/3///wAAMjP/f3nwAABlZv9/efAAAMzM/3///wAAmZn/f3nwAABlZqqq//8AAMzMqqr//wAAMjOqqv//AABlZqqq+PMAADIzqqr48wAAZWZU1fjzAAAyM1TV//8AADIzVNX48wAAZWZU1f//AACZmaqq//8AAJmZVNX//wAAzMxU1f//AACZmaqqKOwAAJmZ/78o7AAAmZmpyijsAAAhoqqqKOwAAJmZVNUo7AAAqaqqqijsAAAys6qqKOwAAMzMqqoo7AAAzMz/vyjsAADMzKnKKOwAAMzMVNUo7AAAMrNU1SjsAACpqlTVKOwAACGiVNUo7AAA//+qKv//AAD//6oqAAAAAP//VFX//wAA//9UVQAAAAAAAP9///8AAGVmqqoAAAAAmZn//wAAAACZmf////8AAP//VNUAAAAAzMz/////AAD//1TV//8AAP//qqr//wAA////f///AAAyM///AAAAADIz/////wAAAABU1f//AAAAAFTVAAAAAAAAqqr//wAAZWb//wAAAAAyM1TVAAAAAAAAqqoAAAAAZWb/f///AAAyM1RV//8AAJmZ/3///wAAAABVVf//AAAAAP9/AAAAADIzqqoAAAAAAABUVQAAAABlZlTVAAAAADIz/38AAAAAAACqKv//AAAyM1RVAAAAAGVm/38AAAAAAACqKgAAAAAyMwAA//8AADIzqioAAAAAZWZUVQAAAAAyMwAAAAAAAGVmqioAAAAAmZn/fwAAAABlZgAA//8AAGVmAAAAAAAAmZlUVQAAAACZmaoqAAAAAGVmqir//wAAmZkAAP//AACZmaoq//8AAJmZAAAAAAAAzMwAAP//AADMzAAAAAAAAMzMVFX//wAAzMyqKgAAAADMzFRVAAAAAP///38AAAAAzMz/fwAAAAD//6qqAAAAAMzMqqoAAAAAmZmqqgAAAACZmVTVAAAAAMzMVNUAAAAAzMz//wAAAABlZv////8AAA==") +}, { +"aabb": AABB(-0.16403107, 0.10840821, 0.46973133, 0.36, 0.39999998, 0.03833282), +"format": 34896613377, +"index_count": 174, +"index_data": PackedByteArray("CgALAAwACgANAAsACgAOAA0ADwAMAAsADQAOABAAEAAMAA8ADQARAAsACwARABIACwASAA8ADQATABEADwASABQAEAAVAA0AEAAPABUAFQATAA0ADwAUABUAFQAUABMAFgAXABgAFgAZABcAGgAWABgAGgAYABsAHAAZABYAHAAdABkAHgAbAB8AHgAaABsAIAAaAB4AGgAhABYAFgAhACIAFgAiABwAGgAjACEAIAAkABoAJAAjABoAJQAkACAAJAAmACMAHAAiACYAHAAmACQAJQAnACQAJwAcACQAKAAnACUAKAApACcAJwAqABwAKQAqACcAKgAdABwAKQArACoAKgAsAB0AKwAsACoAKwAtACwAAAABAAIAAAAuAAEAAwABAC4ABQAuAAAAAwAuAAQABQAvAC4ALwAEAC4ACQAvAAUALwAGAAQACQAHAC8ABwAGAC8ABwAIAAYA"), +"lods": [0.1056379, PackedByteArray("AAABAAIAAwABAAAAAwAAAAQABQAEAAAABQAGAAQABwAGAAUABwAIAAYACQAHAAUA")], +"name": "schwarz", +"primitive": 3, +"uv_scale": Vector4(0, 0, 0, 0), +"vertex_count": 48, +"vertex_data": PackedByteArray("VFUAAJdDAAAAAP8/l0MAAAAAAACXQwAAAAD/f5dDAABUVf9/l0MAAKqqAACXQwAAqqr/f5dDAAD///8/l0MAAP///3+XQwAA//8AAJdDAAAAAP+/THoAAK8MKORMegAAAAD//0x6AABtIrbkTHoAAFRV/79MegAA0gzr8kx6AABUVf//THoAAK8MKOT//wAA0gzr8v//AABtIrbk//8AAOUgD/P//wAA5SAP80x6AADiuP/vAAAAAKqq//8AAAAAqqr/7wAAAADiuP//AAAAAOK4/98AAAAAqqr/3wAAAAAxzP/vAAAAABvH//8AAAAA4rj/vwAAAACqqv+/AAAAABvH/78AAAAA4rj/72FgAAAxzP/vYWAAAOK4/99hYAAAMcz/3wAAAABU1f+/AAAAADHM/99hYAAAVNX/3wAAAAD///+/AAAAAP///98AAAAAVNX/7wAAAAD////vAAAAAFTV//8AAAAA/////wAAAABUVf8/l0MAAKqq/z+XQwAA") +}, { +"aabb": AABB(-0.14619195, 0.45840824, 0.48416355, 0.26931208, 0.029790878, 0.023900598), +"format": 34896613377, +"index_count": 12, +"index_data": PackedByteArray("AAABAAIAAAADAAEABAAFAAYABAAHAAUA"), +"name": "weiss", +"primitive": 3, +"uv_scale": Vector4(0, 0, 0, 0), +"vertex_count": 8, +"vertex_data": PackedByteArray("//8AAAAAAAAv5tPWAAAAAC/mAAAAAAAA///T1gAAAAAuABr+//8AABAdVj///wAABBv/////AAAAAOU3//8AAA==") +}] +blend_shape_mode = 0 + +[resource] +resource_name = "Dude_Cube_002" +_surfaces = [{ +"aabb": AABB(-0.28403106, 0.008408189, -0.083770275, 0.6, 0.6000001, 0.60000014), +"attribute_data": PackedByteArray("x6zHrOH9x6zh/eH9x6zh/cesclfg/XJX4P2MqMesjKhVEvZkHQJyV44i9mRVEntyN1NyV1US/3+OIntyVRIImx0CjKj/QgibN1OMqMYy9mTGMnty/0L/f/9C9mT/QntyjKjh/XJXx6yMqMesclfh/WEhjiJMJo4iTCbGMmEhN1NMJv9CTCY3UzxlN1NRYDdTUWCzRTxlLjhRYC44h5M3U1aX/0JWlzdTh5P/QnEvN1OHKjdThyr/QnEvjiKHKsYyhyqOIn9+s0XIhDdTf343U8iEdUzIhBRJyISzRR0Cx6w3U8esN1Ph/R0C4f1yV3JXjKiMqHJXjKiMqHJXYZ//QpGbN1ORm/9CYZ83U5yjN1Nrp7NFa6c3U5yjs0V1r7NFpqs3U6ars0V1rzdTRHo3U/tz/0JEev9C+3MbS/tzz037c4NQ+3M3U0yPN1MDibNFTI+zRQOJFEkDiXVMA4k3U3dp/0LAbzdTd2k3U8Bvg1DAb89NwG8bS8Bv/0IrVzdTFlwuOBZcs0UrVy44Flw3U1zwcldc8Iyoz8dyV9jijKhU1Yyoz8eMqEu6cldLuoyoLjg4zbNF4f03U3DdJh3h/aEP4f0dAvZkVRJyV44iclcdAntyHQL/fx0Cg40dAgibN1P/f1SYx6wbiMesG4jh/f9/cleqZ+H9qmfHrON34f3jd8esVJjh/TdT9mT/QnJXN1N7csYyclfGMv9//0KDjTdTg42OIv9/N1MIm/9CjKjGMoyoxjIIm44ijKiOIgibVRKMqFUSg40Im3JXCJuMqIONcleDjYyo/3+MqHtycld7coyo9mRyV/ZkjKip7cesqe3h/XDdx6xw3eH9OM3HrDjN4f3/vMes/7zh/djicldU1XJXs0Wp7TdTqe0uOKntLjjh/bNFcN0uOHDdqiqp7aoq4f2qKnDds0U4zTdTOM2qKjjNs0X/vDdT/7yzRcesLjjHrC44/7yqKsesqir/vCYdx6wmHf+8oQ/HrCYdOM2hD/+8HQL/vKEPOM0dAjjNJh1w3aEPcN0dAnDdJh2p7aEPqe0dAqntYSHGMmEh/0I8ZbNFcS//QnEvxjIrV7NF"), +"format": 34896613399, +"index_count": 582, +"index_data": PackedByteArray("fQAZAH4AfQAbABkAfwB9AH4AfwB+AIAAewB/AIAAewCAAHoAgQB7AHoAgQB6AHkAGACBAHkAGAB5ABoAFgAMAIIAFgCDAAwAFwAWAIIAEwCDABYAFwCCAIQAEwCFAIMAFQAXAIQAFQCEAHgAhgAXABUAhwAVAHgAhgAUABcAhwB4AIgAiQAUAIYAEQCHAIgAEQCIAIoAiwARAIoAiwCKABIAjAARAIsAjACNABEAjgCNAIwAjgCPAI0AkACPAI4AkAAPAI8AEAAPAJAAEAB3AA8AdwCRAA8AdwB2AJEAdgANAJEAdgB1AA0AdQALAA0AdQB0AAsADQALAA4ADQAOAIkAiQAOABQADgATABQADgAKABMACgCFABMACgBzAIUAdAAIAAsACABzAAoAdABxAAgACAByAHMAcQByAAgAcQAJAHIAkgA8AJMAkgA+ADwAlACSAJMAlACTAJUAfACUAJUAfACVAJYAlwB8AJYAlwCWAJgAmQCXAJgAmQCYAJoAOwCZAJoAOwCaAD0AmwACAJwAmwABAAIAnQCbAJwAnQCcAJ4AnwCdAJ4AnwCeAKAAoQCfAKAAoQCgAKIAAAChAKIAAACiAAMAZAAGAGUAZAAFAAYAowBkAGUAowBlAGcApACjAGcApABnAGgAZgCkAGgAZgBoAGkAagBmAGkAagBpAGsABABqAGsABABrAAcApQA5AG0ApQCmADkApwClAG0ApwBtAKgAqQCmAKUAqQBuAKYAqgClAKcAqgCpAKUAqwCnAKgAqwCoAKwArQCqAKcArQCnAKsArgBuAKkArgCvAG4AbACpAKoAbACuAKkAsACqAK0AsABsAKoAsQCvAK4AsQCyAK8AswCyALEAswA4ALIAtACzALEAtQCxAK4AtACxALUAtQCuAGwAtgC0ALUAtwC1AGwAtgC1ALcAtwBsALAAuAC2ALcAuAC3ALkAuQC3ALAAugC4ALkAuQCwALsAugC5ALwAvAC5ALsANwC6ALwAuwCwAK0ANwC8AL0AvQC8AL4AvAC7AL4AvQC+AL8AuwCtAMAAvgC7AMAAwACtAKsAvwC+AMEAvgDAAMEAvwDBAMIAwACrAMMAwQDAAMMAwwCrAKwAwwCsAG8AwQDDAMQAwgDBAMQAxADDAG8AwgDEAMUAxABvAHAAxQDEAHAAxQBwADoAxgAdAB4AxgAcAB0AxwDGAB4AxwAeACAAHwDHACAAHwAgACEAyAAjACQAyAAiACMAJQDIACQAJQAkACYAJwAoACkAJwAqACgAyQAsAC0AyQArACwAygDJAC0AygAtAC8ALgDKAC8ALgAvADAAMQAyADMAMQA0ADIANAAxADUANQAxADYAPwBAAEEAPwBCAEAAQwBEAEUAQwBGAEQARwBIAEkARwBKAEgASwBMAE0ASwBOAEwASwBPAE4ATwBLAFAAUABLAFEAUgBTAFQAUgBVAFMAVQBSAFYAVgBSAFcAWABZAFoAWABbAFkAWABcAFsAXABYAF0AXQBYAF4AywBgAGEAywBiAGAAXwDLAGEAXwBhAGMA"), +"lods": [0.00012957268, PackedByteArray("AAABAAIAAAACAAMAZAAGAGUAZAAFAAYAZgBkAGUAZgBlAGcAZgBnAGgAZgBoAGkAagBmAGkAagBpAGsABABqAGsABABrAAcAbAA5AG0AbgA5AGwAbAA4AG4AbABtAG8ANwA4AGwANwBsAG8ANwBvAHAANwBwADoAcQByAAgAcQAJAHIACAByAHMAdABxAAgACABzAAoAdAAIAAsAdQB0AAsACgBzAAwAdQALAA0AdgB1AA0ADQALAA4AdgANAA8AdwB2AA8AEAB3AA8AEAAPABEAEAARABIAEQB4ABIAEQAVAHgAFQAMAHgADQAUABUADQAOABQAFQAUABcAFQAXAAwADgATABQADgAKABMACgAMABMAFwAWAAwAEwAMABYAGAB5ABoAGAB6AHkAGAB7AHoAewAZAHoAewAbABkAHAAdAB4AHwAcAB4AHwAeACAAHwAgACEAIgAjACQAJQAiACQAJQAkACYAJwAoACkAJwAqACgAKwAsAC0ALgArAC0ALgAtAC8ALgAvADAAMQAyADMAMQA0ADIANAAxADUANQAxADYAOwA8AD0AOwB8ADwAfAA+ADwAPwBAAEEAPwBCAEAAQwBEAEUAQwBGAEQARwBIAEkARwBKAEgASwBMAE0ASwBOAEwASwBPAE4ATwBLAFAAUABLAFEAUgBTAFQAUgBVAFMAVQBSAFYAVgBSAFcAWABZAFoAWABbAFkAWABcAFsAXABYAF0AXQBYAF4AXwBgAGEAXwBiAGAAXwBhAGMA"), 0.00025139967, PackedByteArray("AAABAAIAAAACAAMABAAFAAYABAAGAAcACAAJAAoACQAIAAsACgAJAAwACQALAA0ADQALAA4ACQANAA8AEAAJAA8AEAAPABEAEAARABIAEQAMABIADgAKABMACgAMABMADQAOABQADgATABQADQAUABUAEQAVAAwAEwAMABYAFQAUABcAFwAWAAwAFQAXAAwAGAAZABoAGAAbABkAHAAdAB4AHwAcAB4AHwAeACAAHwAgACEAIgAjACQAJQAiACQAJQAkACYAJwAoACkAJwAqACgAKwAsAC0ALgArAC0ALgAtAC8ALgAvADAAMQAyADMAMQA0ADIANAAxADUANQAxADYANwA4ADkANwA5ADoAOwA8AD0AOwA+ADwAPwBAAEEAPwBCAEAAQwBEAEUAQwBGAEQARwBIAEkARwBKAEgASwBMAE0ASwBOAEwASwBPAE4ATwBLAFAAUABLAFEAUgBTAFQAUgBVAFMAVQBSAFYAVgBSAFcAWABZAFoAWABbAFkAWABcAFsAXABYAF0AXQBYAF4AXwBgAGEAXwBiAGAAXwBhAGMA")], +"material": SubResource("StandardMaterial3D_h8qyk"), +"name": "pink.002", +"primitive": 3, +"uv_scale": Vector4(0, 0, 0, 0), +"vertex_count": 204, +"vertex_data": PackedByteArray("AAAAAAAA/7///wAAAAD/v///AAD///+/AAAAAP///7///wAAAABU1f////8AAFTV////////VNX//wAA//9U1czMVNX///////////////+ZmVTV/////8zMqqr/////AAD////////MzP9//////5mZqqr/////zMyqKv///////wAA/////zIzqir/////AAAAAP////9lZlTV/////2Vmqqr/////MjP/f/////8yM1TV/////zIzqqr//////////wAA/78AAP//////v/////////+/AAD//wAA/7/MzKoq//9U1czMqip58FTVmZmqKnnwVNUyM6oq//9U1WVmqip58FTVMjOqKnnwVNUyM6oq////vzIzqip58P+/MjNUVXnw/78yM/9/////vzIz/3958P+/MjOqqv//VNVlZqqq+PNU1TIzqqr481TVZWaqqv//VNUyM/9///9U1TIz/3958FTVZWb/f3nwVNXMzP9///9U1ZmZ/3958FTVzMz/f3nwVNXMzFTV////v8zMqqoo7P+/zMyqqv///7/MzP+/KOz/v8zMqcoo7P+/zMxU1Sjs/78AAAAAAAD/vwAA//8AAP+//////wAA/7///wAAAAD/vwAAAAD//1TVAAD//wAAVNUAAAAAAABU1QAA/////1TVZWZU1f//VNUyM1TV+PNU1WVmVNX481TVMjNU1f//VNVlZqqq////v2VmVNX48/+/ZWaqqvjz/79lZlTV////vzIzVNX///+/MjOqqvjz/78yM1TV+PP/vzIzqqr///+/mZlU1f//VNXMzFTVKOxU1czMVNX//1TVMrNU1SjsVNWpqlTVKOxU1SGiVNUo7FTVmZlU1SjsVNWZmaqq////v5mZVNUo7P+/mZlU1f///7+ZmanKKOz/v5mZ/78o7P+/mZmqqijs/7/MzKqq//9U1ZmZqqoo7FTVmZmqqv//VNUhoqqqKOxU1amqqqoo7FTVMrOqqijsVNXMzKqqKOxU1czMqir///+/zMz/f3nw/7/MzFRVefD/v8zM/3////+/zMyqKnnw/7///1TVAABU1f//VNX//1TV//9UVQAAVNX//6qq//9U1f///3///1TV//9UVf//VNX//6oqAABU1f//qir//1TVZWaqqgAA/7///1TVAAD/v5mZ//8AAP+///9UVQAA/7///6oqAAD/v///VNX/////zMz///////+Zmf//////////qqr/////////f////////1RV////////qir/////AAD/f//////MzP//////v5mZ//////+/mZn//wAA/78AAP9///9U1TIz//8AAP+/MjP//////79lZv//AAD/v2Vm//////+/zMz//wAA/78AAFTV/////zIz////////AACqqv////9lZv///////2Vm/3//////MjNUVf////8AAFVV/////5mZ/3//////AACqKv////8yMwAA/////2VmAAD/////ZWaqKv////+ZmQAA/////5mZqir/////zMwAAP/////MzFRV/////wAAVNX//1TVAABU1QAAVNUAAKqq//9U1QAAqqoAAFTVAAD/fwAAVNUAAFVV//9U1QAAVFUAAFTVAACqKv//VNUAAKoqAABU1czMAAAAAP+/zMwAAP///7+ZmQAAAAD/v5mZAAD///+/ZWYAAAAA/79lZgAA////vzIzAAAAAP+/MjMAAP///7///6qqAABU1f///38AAFTVzMxU1QAA/7/MzP//AAD/v8zMqqoAAP+///+qqgAA/7+ZmVTVAAD/v5mZqqoAAP+/zMz/fwAA/7////9/AAD/v5mZ/38AAP+/ZWZU1QAA/79lZv//AAD/v2Vm/38AAP+/MjNU1QAA/78yM///AAD/vwAAVNUAAP+/AACqqgAA/78yM6qqAAD/vwAA/38AAP+/MjP/fwAA/78AAFRVAAD/vzIzVFUAAP+/AACqKgAA/79lZlRVAAD/vzIzqioAAP+/MjMAAAAA/79lZqoqAAD/v2VmAAAAAP+/mZlUVQAA/7+ZmaoqAAD/v5mZAAAAAP+/zMxUVQAA/7/MzKoqAAD/v8zMAAAAAP+/mZmqKv//VNVlZqoq//9U1TIzVFX///+/ZWb/f///VNWZmf9///9U1czMVFX///+/////f////3////9/////f1TVVNVU1VTVVNVU1VTVVNX/f////3////9/////f////3////9/////f////3////9/////f/7//3////9/////f////3////9/////f///AAD/fwAA/38AAP9/AAD/f1RVVFVUVVRVVFVUVVRVVFVUVVRVVFVUVf9/////f////3////9/////f///VFVUVVRVVFVUVVRVVFVUVaqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqv9/AAD/fwAA/38AAP9/AAD/fwAA/38AAP////////////////////+qKqoqqiqqKqoqqiqqKqoqqqqqqqqqqqqqqqqqqqqqqv9/AAD/fwAA/38AAP9/AAD/f////3////9/////f///qqqpqqqqqqqqqqqqqqqpqqqqqaqpqqqqqqqqqv9//v//f/7//3////9//v//f/7//3///1VVVFVUVVVVVFVUVVRVVVVVVVRVVVVUVVRVVFX/fwAA/38AAP9/AAD/fwAA/38AAFTVVNVU1VTVVNVU1VTVVNVU1VTVVNVU1VTVVNVU1VTV////////////////////////////f////3////9/////f////3////9/////f////3/+/wAA/38AAP9/AAD/f6oqqioAAP9/AAD/fwAA/38AAP9/AAD/f/9/////f////3////9/////f////3/+//9//v//f////3////9/////f////3////9/////f////3////9///+qKqoqqiqqKqoqqiqqKqoqqiqqKqoqqiqqKqoqqiqqKqoqqir///9/////f////3////9/////f////3////9/////f1TVVNVU1VTV////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////VFVUVVRVVFX/f///qqqqqqqqqqr/fwAA") +}, { +"aabb": AABB(-0.16403107, 0.10840821, 0.46973133, 0.36, 0.39999998, 0.03833282), +"attribute_data": PackedByteArray("HQL/QqEPN1MdAjdTJh03UyYd/0IdAsYyJh3GMqEPjiImHY4iHQKOIqwzN1NQO85QMUE3U24+x1BuO6xMMUH/Qqwz/0J1PvZMj0+DUPBSN1OPTzdT8FKDUC5Mg1CPT9dMLkw3U/BSz01sRYNQbEU3U2xFz00uTNdMbEUbSy5MG0tsRf9CLkz/Qo9PG0uPT/9C8FIbS/BS/0KhD/9CoQ/GMljgN1Nk3otPWOCLT2TeN1Pw8NZP4/I3U/DwN1Pj8tZPk+SLT4bmN1OT5DdThuaLT7XsN1PB6tZPtezWT8HqN1NKzDdTlclmT0rMZk+VyTdTpcIVT1rFN1OlwjdTWsUVT3XXL1Ap2jdTddc3UynaL1A60zdThdAZUDrTGVCF0DdT"), +"format": 34896613399, +"index_count": 174, +"index_data": PackedByteArray("CgALAAwADQAMAAsACgAOAAsADwAMAA0ACgAQAA4ADgAQAA8ADwARAA4ADwANABEAEgATABQAEgAVABMAFgASABQAFwAVABIAFgAUABgAFwAZABUAGgAWABgAGgAYABsAHAAWABoAHAAdABYAHgAdABwAHgAfAB0AHwAXAB0AIAAfAB4AIAAhAB8AHwAiABcAIQAiAB8AIgAZABcAIQAjACIAIgAkABkAIwAkACIAIwAlACQAAAABAAIAAAAmAAEAAwABACYABQAmAAAAAwAmAAQABQAnACYAJwAEACYACQAnAAUAJwAGAAQACQAHACcABwAGACcABwAIAAYAKAApACoAKAArACkALAAtAC4ALAAvAC0AMAAxADIAMAAzADEANAA1ADYANAA3ADUAOAA5ADoAOAA7ADkAPAA9AD4APAA/AD0AQABBAEIAQABDAEEARABFAEYARABHAEUA"), +"lods": [0.1056379, PackedByteArray("AAABAAIAAwABAAAAAwAAAAQABQAEAAAABQAGAAQABwAGAAUABwAIAAYACQAHAAUA")], +"material": SubResource("StandardMaterial3D_c6dnh"), +"name": "schwarz", +"primitive": 3, +"uv_scale": Vector4(0, 0, 0, 0), +"vertex_count": 72, +"vertex_data": PackedByteArray("VFUAAJdD//8AAP8/l0P//wAAAACXQ///AAD/f5dD//9UVf9/l0P//6qqAACXQ///qqr/f5dD//////8/l0P//////3+XQ/////8AAJdD//8AAP+/THr//68MKORMev//AAD//0x6///SDOvyTHr//20ituRMev//VFX//0x6//9UVf+/THr//+UgD/NMev//4rj/7wAA//+qqv//AAD//6qq/+8AAP//4rj//wAA///iuP/fAAD//zHM/+8AAP//qqr/3wAA//8bx///AAD//+K4/78AAP//qqr/vwAA//8bx/+/AAD//zHM/98AAP//VNX/vwAA//9U1f/fAAD//////78AAP//////3wAA//9U1f/vAAD//////+8AAP//VNX//wAA////////AAD//1RV/z+XQ///qqr/P5dD///iuP/vAABU1THM/+9hYFTVMcz/7wAAVNXiuP/vYWBU1THM/+8AAP+/Mcz/32Fg/78xzP/fAAD/vzHM/+9hYP+/Mcz/3wAAVNXiuP/fYWBU1eK4/98AAFTVMcz/32FgVNXiuP/fAAD/v+K4/+9hYP+/4rj/7wAA/7/iuP/fYWD/v9IM6/JMeiXV5SAP8///JdXlIA/zTHol1dIM6/L//yXVbSK25Ex6qtSvDCjk//+q1K8MKORMeqrUbSK25P//qtTlIA/zTHoXwG0ituT//xfAbSK25Ex6F8DlIA/z//8XwK8MKORMev+/0gzr8v///7/SDOvyTHr/v68MKOT///+//7//v/+//7//v/+//7//v/+//7//v/+//7//v/+//7//v/+//7//v/+//7//v/+//7//v/+//7//v/+//7//v/+//7//v/+//7//v/+//7//v/+//7//v/+//7//v/+//7//v/+//7//v/+//7//v/+//7//v/+//7//v/+//7//v/+//7//v/+//7//v/+//7//v/+//7//v/+//7//v1RVVFVUVVRVVFVUVVRVVFX/f////3////9/////f///qqqqqqqqqqqqqqqqqqqqqv9/AAD/fwAA/38AAP9/AABxVRxVcVUcVXFVHFVxVRxVQqp5q0KqeatCqnmrQqp5q850ZvrOdGb6znRm+s50Zvp4fw0BeH8NAXh/DQF4fw0B") +}, { +"aabb": AABB(-0.14619195, 0.45840824, 0.48416355, 0.26931208, 0.029790878, 0.023900598), +"attribute_data": PackedByteArray("CbuLT2q+N1MJuzdTar6LT862GlOwsxRPurZJT7CzN1M="), +"format": 34896613399, +"index_count": 12, +"index_data": PackedByteArray("AAABAAIAAAADAAEABAAFAAYABAAHAAUA"), +"material": SubResource("StandardMaterial3D_i8imj"), +"name": "weiss", +"primitive": 3, +"uv_scale": Vector4(0, 0, 0, 0), +"vertex_count": 8, +"vertex_data": PackedByteArray("//8AAAAA//8v5tPWAAD//y/mAAAAAP/////T1gAA//8uABr+/////xAdVj//////BBv///////8AAOU3//////+//7//v/+//7//v/+//78Xv+fAF7/nwBe/58AXv+fA") +}] +blend_shape_mode = 0 +shadow_mesh = SubResource("ArrayMesh_wt8sa") diff --git a/weltenauge/02_library/meshes/humanoid/dude.tscn b/weltenauge/02_library/meshes/humanoid/dude.tscn new file mode 100644 index 0000000..46b5d55 --- /dev/null +++ b/weltenauge/02_library/meshes/humanoid/dude.tscn @@ -0,0 +1,6 @@ +[gd_scene format=3 uid="uid://dgjgtr82ye25d"] + +[ext_resource type="ArrayMesh" uid="uid://bi22s15yeh6mw" path="res://02_library/meshes/humanoid/dude.tres" id="1_jchph"] + +[node name="Dude" type="MeshInstance3D" unique_id=1185543431] +mesh = ExtResource("1_jchph") diff --git a/weltenauge/02_library/ui_assets/icons/program/logo_projekt_weltenauge.png b/weltenauge/02_library/ui_assets/icons/program/logo_projekt_weltenauge.png new file mode 100644 index 0000000..6d19032 --- /dev/null +++ b/weltenauge/02_library/ui_assets/icons/program/logo_projekt_weltenauge.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8cca249374b1fc72c54e701d63e515945db5c4ff85490be8db50ec57aa7f561f +size 1607842 diff --git a/weltenauge/02_library/ui_assets/icons/program/logo_projekt_weltenauge.png.import b/weltenauge/02_library/ui_assets/icons/program/logo_projekt_weltenauge.png.import new file mode 100644 index 0000000..0fc5221 --- /dev/null +++ b/weltenauge/02_library/ui_assets/icons/program/logo_projekt_weltenauge.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://d1cyxxj8ypfu" +path="res://.godot/imported/logo_projekt_weltenauge.png-050b0b56f8f1da7cb817f7c3bf842b44.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://02_library/ui_assets/icons/program/logo_projekt_weltenauge.png" +dest_files=["res://.godot/imported/logo_projekt_weltenauge.png-050b0b56f8f1da7cb817f7c3bf842b44.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/weltenauge/04_entities/player/player.tscn b/weltenauge/04_entities/player/player.tscn new file mode 100644 index 0000000..05e2bed --- /dev/null +++ b/weltenauge/04_entities/player/player.tscn @@ -0,0 +1,38 @@ +[gd_scene format=3 uid="uid://bjurf8vfd1fg8"] + +[ext_resource type="Script" uid="uid://bhym64rc7xtin" path="res://04_entities/player/player_controller.gd" id="1_pnayr"] +[ext_resource type="PackedScene" uid="uid://dgjgtr82ye25d" path="res://02_library/meshes/humanoid/dude.tscn" id="2_11qf6"] + +[sub_resource type="CylinderShape3D" id="CylinderShape3D_ktl23"] + +[sub_resource type="SystemFont" id="SystemFont_ktl23"] + +[sub_resource type="SceneReplicationConfig" id="SceneReplicationConfig_fqfg4"] + +[node name="CharacterBody3D" type="CharacterBody3D" unique_id=682006733 groups=["players"]] +script = ExtResource("1_pnayr") +interpolation_speed = 5.0 + +[node name="CollisionShape3D" type="CollisionShape3D" parent="." unique_id=159496934] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.0185384, -0.18054307) +shape = SubResource("CylinderShape3D_ktl23") + +[node name="Visuals" type="Node3D" parent="." unique_id=1840405751] +transform = Transform3D(-0.99988204, 0, 0.015358234, 0, 1, 0, -0.015358234, 0, -0.99988204, 0, 0, 0) + +[node name="Dude" parent="Visuals" unique_id=1185543431 instance=ExtResource("2_11qf6")] + +[node name="AnimationPlayer" type="AnimationPlayer" parent="." unique_id=1343500738] +root_node = NodePath("../..") + +[node name="PlayerNameLabel" type="Label3D" parent="." unique_id=461683617] +transform = Transform3D(-1, 0, -8.742278e-08, 0, 1, 0, 8.742278e-08, 0, -1, 0, 0.73133814, 0) +text = "-label-" +font = SubResource("SystemFont_ktl23") +vertical_alignment = 2 +uppercase = true + +[node name="MultiplayerSynchronizer" type="MultiplayerSynchronizer" parent="." unique_id=656518492] +replication_interval = 0.05 +replication_config = SubResource("SceneReplicationConfig_fqfg4") +public_visibility = false diff --git a/weltenauge/04_entities/player/player_controller.gd b/weltenauge/04_entities/player/player_controller.gd new file mode 100644 index 0000000..fb6708b --- /dev/null +++ b/weltenauge/04_entities/player/player_controller.gd @@ -0,0 +1,137 @@ +extends CharacterBody3D + +@onready var synchronizer = $MultiplayerSynchronizer +@export var target_position: Vector3 + +@export var sync_position: Vector3 +@export var sync_rotation: Vector3 + +@export var interpolation_speed = 15.0 # Geschwindigkeit für die Glättung + +var path: PackedVector3Array = [] +var speed = 5.0 + +func _enter_tree(): + # Da der Name des Nodes die Peer-ID ist, setzen wir die Authority einfach selbst + if name.is_valid_int(): + #set_multiplayer_authority(name.to_int()) + set_multiplayer_authority(1) + + +func _ready(): + # --- START: Dynamische Replikations-Konfiguration --- + # Eine neue Konfigurations-Ressource im Code erstellen. + var config = SceneReplicationConfig.new() + # Die zu synchronisierenden Eigenschaften hinzufügen. Der Pfad '..:sync_position' bedeutet: + # '..': Gehe zum Eltern-Node (dem CharacterBody3D). + # ':': Greife auf eine Eigenschaft dieses Nodes zu. + # 'sync_position': Der Name der Eigenschaft. + config.add_property(NodePath(":sync_position")) + config.add_property(NodePath(":sync_rotation")) + config.add_property(NodePath("PlayerNameLabel:text")) + # Die generierte Konfiguration dem Synchronizer zuweisen. Dies überschreibt alle Editor-Einstellungen. + synchronizer.replication_config = config + # --- ENDE: Dynamische Replikations-Konfiguration --- + + target_position = global_position + # Wir schalten die automatische Physik-Synchronisation aus + set_physics_process(multiplayer.is_server()) + + +func _physics_process(delta): + # Dieser Teil läuft NUR auf dem Server + if path.size() > 0: + var next_point = path[0] + var direction = (next_point - global_position).normalized() + + # Richte den Avatar in die Bewegungsrichtung aus. + look_at(next_point, Vector3.UP) + + # Wir nutzen move_and_slide, damit Kollisionen (Wände) funktionieren + velocity = direction * speed + move_and_slide() + + # Nach der Bewegung speichern wir das Ergebnis für die Clients + sync_position = global_position + sync_rotation = rotation + + if global_position.distance_to(next_point) < 0.2: + path.remove_at(0) + else: + velocity = Vector3.ZERO + sync_position = global_position # Sicherstellen, dass Standbild syncron bleibt + + +@rpc("any_peer", "call_remote", "reliable") +func srv_request_move(target: Vector3): + if not multiplayer.is_server(): return + + #print("Server: Request Move received from client") + + # Sicherheits-Check: Nur der Besitzer des Avatars darf ihn bewegen + #if multiplayer.get_remote_sender_id() != get_multiplayer_authority(): + # return + + # Hier später: path = astar.get_point_path(global_position, target) + #path = PackedVector3Array([target]) + print("Server Move to Target: ", target) + path = [target] + target_position = target + +# ERSETZT die alte _input(event)-Funktion +func _input(event): + # Ist dieser Avatar nicht der des lokalen Spielers? Wenn ja, sofort abbrechen. + if name.to_int() != multiplayer.get_unique_id(): return + if DisplayServer.get_name() == "headless": return # Server ignoriert Klicks + if multiplayer.is_server(): return # Kein Input vom Server + + # Wir reagieren nur auf den Klick der linken Maustaste + if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT: + var click_result = _get_click_result() + + if click_result: # Prüfen, ob der Strahl überhaupt etwas getroffen hat + var collider = click_result.collider + + # PRIORITÄT 1: Haben wir ein Portal angeklickt? + # Wir prüfen, ob das getroffene Objekt gültig ist und zur Gruppe "portals" gehört. + if is_instance_valid(collider) and collider.is_in_group("portals"): + print("Portal angeklickt. Fordere Area-Wechsel an zu: ", collider.target_area_key) + # Sende RPC direkt an den NetworkManager mit dem Ziel des Portals + NetworkManager.srv_handle_area_change_request.rpc_id(1, collider.target_area_key) + return # Wichtig: Aktion ist beendet, nichts weiter tun. + + # PRIORITÄT 2 (Fallback): Wir haben etwas anderes getroffen (vermutlich den Boden), also bewegen. + var target_position = click_result.position + srv_request_move.rpc_id(1, target_position) + + + +# ERSETZT die alte _get_click_floor_pos()-Funktion +func _get_click_result(): + var camera = get_viewport().get_camera_3d() + if not camera: return null + + var mouse_pos = get_viewport().get_mouse_position() + var space_state = get_world_3d().direct_space_state + var ray_origin = camera.project_ray_origin(mouse_pos) + var ray_dir = camera.project_ray_normal(mouse_pos) + + var query = PhysicsRayQueryParameters3D.create(ray_origin, ray_origin + ray_dir * 1000) + # WICHTIG: Schließe den eigenen Körper von der Abfrage aus, um nicht auf sich selbst zu klicken. + query.exclude = [self] + + # NEU: Sage dem Raycast, dass er auch mit Area3D-Nodes kollidieren soll. + query.collide_with_areas = true + + var result = space_state.intersect_ray(query) + return result # Gibt das komplette Ergebnis-Dictionary zurück + + +func _process(_delta): + # Dieser Teil läuft auf dem Client (besonders dem, der die Authority hat) + if not multiplayer.is_server(): + # Bewege die lokale Position sanft in Richtung der vom Server vorgegebenen Position. + global_position = global_position.lerp(sync_position, _delta * interpolation_speed) + + # Rotiere sanft in Richtung der vom Server vorgegebenen Rotation. + rotation = rotation.lerp(sync_rotation, _delta * interpolation_speed) diff --git a/weltenauge/04_entities/player/player_controller.gd.uid b/weltenauge/04_entities/player/player_controller.gd.uid new file mode 100644 index 0000000..9e9a0df --- /dev/null +++ b/weltenauge/04_entities/player/player_controller.gd.uid @@ -0,0 +1 @@ +uid://bhym64rc7xtin diff --git a/weltenauge/04_entities/ui/windows/account/login.gd b/weltenauge/04_entities/ui/windows/account/login.gd new file mode 100644 index 0000000..3231b54 --- /dev/null +++ b/weltenauge/04_entities/ui/windows/account/login.gd @@ -0,0 +1,16 @@ +extends Control + +@onready var ip_input: TextEdit = $IPInput + +func _on_host_button_pressed() -> void: + print("host pressed") + GameEvents.ui_host_requested.emit() + DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_MINIMIZED) + hide() + +func _on_join_button_pressed(): + var username = $UsernameLineEdit.text + var password = $PasswordLineEdit.text # TODO: Handle Password + + GameEvents.ui_join_requested.emit(ip_input.text, username, password) + hide() diff --git a/weltenauge/04_entities/ui/windows/account/login.gd.uid b/weltenauge/04_entities/ui/windows/account/login.gd.uid new file mode 100644 index 0000000..a30aaac --- /dev/null +++ b/weltenauge/04_entities/ui/windows/account/login.gd.uid @@ -0,0 +1 @@ +uid://c6yngwnpqsnpi diff --git a/weltenauge/04_entities/ui/windows/account/login.tscn b/weltenauge/04_entities/ui/windows/account/login.tscn new file mode 100644 index 0000000..566182b --- /dev/null +++ b/weltenauge/04_entities/ui/windows/account/login.tscn @@ -0,0 +1,82 @@ +[gd_scene format=3 uid="uid://lb6btb6426mi"] + +[ext_resource type="Script" uid="uid://c6yngwnpqsnpi" path="res://04_entities/ui/windows/account/login.gd" id="1_x74xt"] + +[node name="Login" type="Control" unique_id=1694671237] +layout_mode = 3 +anchors_preset = 0 +offset_left = 23.0 +offset_top = 102.0 +offset_right = 23.0 +offset_bottom = 102.0 +script = ExtResource("1_x74xt") + +[node name="IPInput" type="TextEdit" parent="." unique_id=1340335140] +custom_minimum_size = Vector2(150, 40) +layout_mode = 0 +offset_left = 375.0 +offset_top = -70.0 +offset_right = 525.0 +offset_bottom = -30.0 +text = "127.0.0.1" +editable = false + +[node name="Join_Button" type="Button" parent="." unique_id=811892478] +layout_mode = 0 +offset_left = 30.0 +offset_top = 12.0 +offset_right = 231.0 +offset_bottom = 43.0 +text = "Join" + +[node name="Host_Button" type="Button" parent="." unique_id=1465654892] +layout_mode = 0 +offset_left = 313.0 +offset_top = 11.0 +offset_right = 530.0 +offset_bottom = 42.0 +text = "Host" + +[node name="lb_User" type="Label" parent="." unique_id=1356001317] +layout_mode = 0 +offset_left = 21.0 +offset_top = -72.0 +offset_right = 62.0 +offset_bottom = -49.0 +text = "User:" + +[node name="lb_User" type="Label" parent="lb_User" unique_id=463394394] +layout_mode = 0 +offset_left = 286.0 +offset_top = 11.0 +offset_right = 340.0 +offset_bottom = 34.0 +text = "Server:" + +[node name="lb_Password" type="Label" parent="." unique_id=1027088176] +layout_mode = 0 +offset_left = 18.0 +offset_top = -39.0 +offset_right = 94.0 +offset_bottom = -16.0 +text = "Passwort:" + +[node name="UsernameLineEdit" type="LineEdit" parent="." unique_id=1896094376] +layout_mode = 0 +offset_left = 122.0 +offset_top = -72.0 +offset_right = 231.0 +offset_bottom = -41.0 +text = "test_user" + +[node name="PasswordLineEdit" type="LineEdit" parent="." unique_id=214865529] +layout_mode = 0 +offset_left = 121.0 +offset_top = -38.0 +offset_right = 230.0 +offset_bottom = -7.0 +text = "test_pw" + +[connection signal="pressed" from="Join_Button" to="." method="_on_join_button_pressed"] +[connection signal="button_up" from="Host_Button" to="." method="_on_host_pressed"] +[connection signal="pressed" from="Host_Button" to="." method="_on_host_button_pressed"] diff --git a/weltenauge/04_entities/ui/windows/character_create/character_create.gd b/weltenauge/04_entities/ui/windows/character_create/character_create.gd new file mode 100644 index 0000000..153b254 --- /dev/null +++ b/weltenauge/04_entities/ui/windows/character_create/character_create.gd @@ -0,0 +1,22 @@ +extends Control + +@onready var name_line_edit: LineEdit = $NameLineEdit + +func _ready(): + GameEvents.ui_create_character_screen.connect(show_creation_screen) + +func show_creation_screen(): + name_line_edit.text = "" + show() + +func _on_create_button_pressed() -> void: + var new_character = CharacterData.new() + new_character.name = name_line_edit.text + print("Create Button pressed") + GameEvents.ui_character_created.emit(new_character) + hide() + + +func _on_cancel_button_pressed() -> void: + get_parent().get_node("CharacterSelect").show() + hide() diff --git a/weltenauge/04_entities/ui/windows/character_create/character_create.gd.uid b/weltenauge/04_entities/ui/windows/character_create/character_create.gd.uid new file mode 100644 index 0000000..fff73b3 --- /dev/null +++ b/weltenauge/04_entities/ui/windows/character_create/character_create.gd.uid @@ -0,0 +1 @@ +uid://dx24rxnsd0eoj diff --git a/weltenauge/04_entities/ui/windows/character_create/character_create.tscn b/weltenauge/04_entities/ui/windows/character_create/character_create.tscn new file mode 100644 index 0000000..af0cf32 --- /dev/null +++ b/weltenauge/04_entities/ui/windows/character_create/character_create.tscn @@ -0,0 +1,42 @@ +[gd_scene format=3 uid="uid://cdwkmx452hc4k"] + +[ext_resource type="Script" uid="uid://dx24rxnsd0eoj" path="res://04_entities/ui/windows/character_create/character_create.gd" id="1_mdh5q"] + +[node name="CharacterCreate" type="Control" unique_id=2056198803] +layout_mode = 3 +anchors_preset = 0 +script = ExtResource("1_mdh5q") + +[node name="NameLabel" type="Label" parent="." unique_id=1413425912] +layout_mode = 0 +offset_left = 24.0 +offset_top = 32.0 +offset_right = 82.0 +offset_bottom = 61.0 +text = "Name:" + +[node name="CreateButton" type="Button" parent="." unique_id=50672893] +layout_mode = 0 +offset_left = 29.0 +offset_top = 81.0 +offset_right = 140.0 +offset_bottom = 117.0 +text = "Erstellen" + +[node name="CancelButton" type="Button" parent="." unique_id=861690836] +layout_mode = 0 +offset_left = 175.0 +offset_top = 80.0 +offset_right = 286.0 +offset_bottom = 116.0 +text = "Abbrechen" + +[node name="NameLineEdit" type="LineEdit" parent="." unique_id=2114380893] +layout_mode = 0 +offset_left = 89.0 +offset_top = 29.0 +offset_right = 288.0 +offset_bottom = 60.0 + +[connection signal="pressed" from="CreateButton" to="." method="_on_create_button_pressed"] +[connection signal="pressed" from="CancelButton" to="." method="_on_cancel_button_pressed"] diff --git a/weltenauge/04_entities/ui/windows/character_select/character_select.gd b/weltenauge/04_entities/ui/windows/character_select/character_select.gd new file mode 100644 index 0000000..e4d94e9 --- /dev/null +++ b/weltenauge/04_entities/ui/windows/character_select/character_select.gd @@ -0,0 +1,31 @@ +extends Control + +@onready var character_item_list: ItemList = $CharacterItemList + +var characters: Array[CharacterData] = [] + +func _ready(): + GameEvents.network_show_characters.connect(show_list) + GameEvents.network_new_character_received.connect(receive_new_character) + +func receive_new_character(character: CharacterData): + characters.append(character) + character_item_list.add_item(character.name) + show() + +func show_list(characters: Array): + print("show_list triggered") + character_item_list.clear() + for character: CharacterData in characters: + character_item_list.add_item(character.name) + show() + +func _on_create_character_button_pressed() -> void: + GameEvents.ui_create_character_screen.emit() + hide() + +# Doubleclick on Character should start area transition +func _on_character_item_list_item_activated(index: int) -> void: + print("Doubleclick for index=", index) + var item = character_item_list.get_selected_items()[0] + print("=>",character_item_list.get_item_text(item)) diff --git a/weltenauge/04_entities/ui/windows/character_select/character_select.gd.uid b/weltenauge/04_entities/ui/windows/character_select/character_select.gd.uid new file mode 100644 index 0000000..aaec836 --- /dev/null +++ b/weltenauge/04_entities/ui/windows/character_select/character_select.gd.uid @@ -0,0 +1 @@ +uid://b5htxbkq23mo6 diff --git a/weltenauge/04_entities/ui/windows/character_select/character_select.tscn b/weltenauge/04_entities/ui/windows/character_select/character_select.tscn new file mode 100644 index 0000000..7c77b3b --- /dev/null +++ b/weltenauge/04_entities/ui/windows/character_select/character_select.tscn @@ -0,0 +1,33 @@ +[gd_scene format=3 uid="uid://ca2bhph2k3xpy"] + +[ext_resource type="Script" uid="uid://b5htxbkq23mo6" path="res://04_entities/ui/windows/character_select/character_select.gd" id="1_o7qwi"] + +[node name="CharacterSelect" type="Control" unique_id=2056198803] +layout_mode = 3 +anchors_preset = 0 +script = ExtResource("1_o7qwi") + +[node name="CharacterItemList" type="ItemList" parent="." unique_id=1442954519] +layout_mode = 0 +offset_top = 32.0 +offset_right = 368.0 +offset_bottom = 571.0 + +[node name="Label" type="Label" parent="." unique_id=1413425912] +layout_mode = 0 +offset_left = 111.0 +offset_top = 2.0 +offset_right = 217.0 +offset_bottom = 31.0 +text = "Charaktere:" + +[node name="CreateCharacterButton" type="Button" parent="." unique_id=50672893] +layout_mode = 0 +offset_left = 113.0 +offset_top = 585.0 +offset_right = 224.0 +offset_bottom = 621.0 +text = "Neu" + +[connection signal="item_activated" from="CharacterItemList" to="." method="_on_character_item_list_item_activated"] +[connection signal="pressed" from="CreateCharacterButton" to="." method="_on_create_character_button_pressed"] diff --git a/weltenauge/05_worlds/areas/outdoor/grass/keuwin_grasland.tscn b/weltenauge/05_worlds/areas/outdoor/grass/keuwin_grasland.tscn new file mode 100644 index 0000000..1f9dbe2 --- /dev/null +++ b/weltenauge/05_worlds/areas/outdoor/grass/keuwin_grasland.tscn @@ -0,0 +1,61 @@ +[gd_scene format=3 uid="uid://h5qt2pcidh8x"] + +[ext_resource type="PackedScene" uid="uid://coq1j1cdrvqyn" path="res://05_worlds/prefabs/portal/portal.tscn" id="1_1yg6u"] + +[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_d3jw7"] + +[sub_resource type="Sky" id="Sky_1yg6u"] +sky_material = SubResource("ProceduralSkyMaterial_d3jw7") + +[sub_resource type="Environment" id="Environment_8r0t0"] +background_mode = 2 +sky = SubResource("Sky_1yg6u") +tonemap_mode = 2 + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_30klc"] +albedo_color = Color(9.3854965e-07, 0.4579985, 0.32070312, 1) + +[sub_resource type="PlaneMesh" id="PlaneMesh_0iae2"] +material = SubResource("StandardMaterial3D_30klc") + +[sub_resource type="ConcavePolygonShape3D" id="ConcavePolygonShape3D_1yg6u"] +data = PackedVector3Array(1, 0, 1, -1, 0, 1, 1, 0, -1, -1, 0, 1, -1, 0, -1, 1, 0, -1) + +[node name="keuwin_grasland" type="Node3D" unique_id=1953380732] + +[node name="DirectionalLight3D" type="DirectionalLight3D" parent="." unique_id=1262732162] +transform = Transform3D(-0.8660254, -0.3535534, 0.3535534, 0, 0.70710677, 0.70710677, -0.50000006, 0.6123724, -0.6123724, 0, 0, 0) +light_color = Color(0.90022373, 0.9250896, 0.6778064, 1) +light_energy = 1.5 +shadow_enabled = true + +[node name="WorldEnvironment" type="WorldEnvironment" parent="." unique_id=1286599151] +environment = SubResource("Environment_8r0t0") + +[node name="Architecture" type="Node3D" parent="." unique_id=97923552] + +[node name="MeshInstance3D" type="MeshInstance3D" parent="Architecture" unique_id=1708107393] +transform = Transform3D(10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0) +mesh = SubResource("PlaneMesh_0iae2") + +[node name="StaticBody3D" type="StaticBody3D" parent="Architecture/MeshInstance3D" unique_id=209028061] + +[node name="CollisionShape3D" type="CollisionShape3D" parent="Architecture/MeshInstance3D/StaticBody3D" unique_id=1119853048] +shape = SubResource("ConcavePolygonShape3D_1yg6u") + +[node name="Navigation" type="Node3D" parent="." unique_id=1472582667] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.01873529, 0) + +[node name="SpawnPoints" type="Node3D" parent="." unique_id=438053421] + +[node name="Interaction" type="Node3D" parent="." unique_id=1592705952] + +[node name="Triggers" type="Node3D" parent="." unique_id=1507691477] + +[node name="Portal" parent="." unique_id=988801188 instance=ExtResource("1_1yg6u")] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -6, 0, -6) +target_area_key = "ahnenhalle" + +[node name="MultiplayerSpawner" type="MultiplayerSpawner" parent="." unique_id=58641401] +_spawnable_scenes = PackedStringArray("uid://bjurf8vfd1fg8") +spawn_path = NodePath("../SpawnPoints") diff --git a/weltenauge/05_worlds/areas/planes/fugue/halle_der_ahnen.tscn b/weltenauge/05_worlds/areas/planes/fugue/halle_der_ahnen.tscn new file mode 100644 index 0000000..efbb63d --- /dev/null +++ b/weltenauge/05_worlds/areas/planes/fugue/halle_der_ahnen.tscn @@ -0,0 +1,65 @@ +[gd_scene format=3 uid="uid://bef0tj7n6g8uo"] + +[ext_resource type="PackedScene" uid="uid://coq1j1cdrvqyn" path="res://05_worlds/prefabs/portal/portal.tscn" id="1_4r2hu"] + +[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_d3jw7"] +sky_top_color = Color(0.055328622, 0.3637527, 0.67898166, 1) +ground_bottom_color = Color(0.28193337, 0.24096331, 0.19489476, 1) +ground_horizon_color = Color(0.5439266, 0.5434616, 0.51488423, 1) + +[sub_resource type="Sky" id="Sky_1yg6u"] +sky_material = SubResource("ProceduralSkyMaterial_d3jw7") + +[sub_resource type="Environment" id="Environment_8r0t0"] +background_mode = 2 +sky = SubResource("Sky_1yg6u") +tonemap_mode = 2 +fog_enabled = true +fog_light_color = Color(0.061652586, 0.3592321, 0.673373, 1) + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_30klc"] +albedo_color = Color(0.1800012, 0.004964735, 0.2305904, 1) + +[sub_resource type="PlaneMesh" id="PlaneMesh_0iae2"] +material = SubResource("StandardMaterial3D_30klc") + +[sub_resource type="ConcavePolygonShape3D" id="ConcavePolygonShape3D_0hy1c"] +data = PackedVector3Array(1, 0, 1, -1, 0, 1, 1, 0, -1, -1, 0, 1, -1, 0, -1, 1, 0, -1) + +[node name="halle_der_ahnen" type="Node3D" unique_id=1953380732] + +[node name="DirectionalLight3D" type="DirectionalLight3D" parent="." unique_id=1262732162] +transform = Transform3D(-0.8660254, -0.12940952, 0.48296294, 0, 0.96592575, 0.258819, -0.50000006, 0.22414385, -0.8365162, 0, 0, 0) +light_color = Color(0.94507605, 0.7726238, 0.95401555, 1) +light_energy = 0.5 +shadow_enabled = true + +[node name="WorldEnvironment" type="WorldEnvironment" parent="." unique_id=1286599151] +environment = SubResource("Environment_8r0t0") + +[node name="Architecture" type="Node3D" parent="." unique_id=97923552] + +[node name="MeshInstance3D" type="MeshInstance3D" parent="Architecture" unique_id=1708107393] +transform = Transform3D(10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0) +mesh = SubResource("PlaneMesh_0iae2") + +[node name="StaticBody3D" type="StaticBody3D" parent="Architecture/MeshInstance3D" unique_id=1395982254] + +[node name="CollisionShape3D" type="CollisionShape3D" parent="Architecture/MeshInstance3D/StaticBody3D" unique_id=521000493] +shape = SubResource("ConcavePolygonShape3D_0hy1c") + +[node name="Navigation" type="Node3D" parent="." unique_id=1472582667] + +[node name="SpawnPoints" type="Node3D" parent="." unique_id=438053421] + +[node name="Interaction" type="Node3D" parent="." unique_id=1592705952] + +[node name="Triggers" type="Node3D" parent="." unique_id=1507691477] + +[node name="MultiplayerSpawner" type="MultiplayerSpawner" parent="." unique_id=1610787579] +_spawnable_scenes = PackedStringArray("uid://bjurf8vfd1fg8") +spawn_path = NodePath("../SpawnPoints") + +[node name="Portal" parent="." unique_id=988801188 instance=ExtResource("1_4r2hu")] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 7.1332607, 0.014829755, 8.73144) +target_area_key = "keuwin_grasland" diff --git a/weltenauge/05_worlds/prefabs/portal/portal.gd b/weltenauge/05_worlds/prefabs/portal/portal.gd new file mode 100644 index 0000000..32ec95a --- /dev/null +++ b/weltenauge/05_worlds/prefabs/portal/portal.gd @@ -0,0 +1,4 @@ +extends Area3D + +# Dieser Wert muss einem Schlüssel in NetworkManager.area_paths entsprechen (z.B. "ahnenhalle"). +@export var target_area_key: String = "" diff --git a/weltenauge/05_worlds/prefabs/portal/portal.gd.uid b/weltenauge/05_worlds/prefabs/portal/portal.gd.uid new file mode 100644 index 0000000..09b5634 --- /dev/null +++ b/weltenauge/05_worlds/prefabs/portal/portal.gd.uid @@ -0,0 +1 @@ +uid://c23uu6akws8nj diff --git a/weltenauge/05_worlds/prefabs/portal/portal.tscn b/weltenauge/05_worlds/prefabs/portal/portal.tscn new file mode 100644 index 0000000..c77673e --- /dev/null +++ b/weltenauge/05_worlds/prefabs/portal/portal.tscn @@ -0,0 +1,19 @@ +[gd_scene format=3 uid="uid://coq1j1cdrvqyn"] + +[ext_resource type="Script" uid="uid://c23uu6akws8nj" path="res://05_worlds/prefabs/portal/portal.gd" id="1_8dnr8"] + +[sub_resource type="BoxShape3D" id="BoxShape3D_h1tlh"] +size = Vector3(3.2, 1, 1) + +[sub_resource type="BoxMesh" id="BoxMesh_gtfyq"] + +[node name="Portal" type="Area3D" unique_id=988801188 groups=["portals"]] +script = ExtResource("1_8dnr8") + +[node name="CollisionShape3D" type="CollisionShape3D" parent="." unique_id=1301383310] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.50266325, 0) +shape = SubResource("BoxShape3D_h1tlh") + +[node name="MeshInstance3D" type="MeshInstance3D" parent="." unique_id=1902207257] +transform = Transform3D(3.2577846, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.48517025, 0) +mesh = SubResource("BoxMesh_gtfyq") diff --git a/weltenauge/PROJEKT_UEBERSICHT.md b/weltenauge/PROJEKT_UEBERSICHT.md new file mode 100644 index 0000000..d89e04a --- /dev/null +++ b/weltenauge/PROJEKT_UEBERSICHT.md @@ -0,0 +1,185 @@ +## 1. Projektziel & Vision + +"weltenauge" ist ein von Neverwinter Nights (NWN) inspiriertes Multiplayer-Rollenspiel. Das Kernstück ist eine robuste Client/Server-Architektur, die in einer einzigen Godot-Codebase umgesetzt wird. + +## 2. Kernkonzepte der Architektur + +### Client/Server-Modell + +- **Eine Codebase:** Client und Server sind Teil desselben Godot-Projekts. +- **Start-Logik:** + - Im normalen Modus startet das Spiel mit einem Menü, das "Host" und "Beitreten" anbietet. + - Im Headless-Modus (`--headless`) startet das Programm automatisch als dedizierter Server (Host). +- **Verantwortlichkeiten:** Der Server (Host) ist die Autorität für den Spielzustand. Clients empfangen und visualisieren den Zustand. + +### Dynamisches Area-Management + +- **Serverseitig:** Der Server lädt **alle** von Spielern bewohnten Areas (Szenen) gleichzeitig. +- **Clientseitig:** Ein Client lädt **nur** die eine Area, in der sich sein Charakter gerade befindet. Dies spart Ressourcen auf den Client-Rechnern. +- **Area-Wechsel:** Ein Wechsel wird immer vom Server initiiert. Der Server lädt die neue Szene für den Spieler, platziert dessen Avatar und sendet ein Signal an den Client. Der Client lädt daraufhin ebenfalls die neue Szene. + +### Isolierte Welten auf dem Server + +## 3. Geplante Ordnerstruktur + +Alle Skripte und die dazugehörige Logik sollen thematisch in Unterordnern unter `res://systems/` abgelegt werden: + +/project_root +│ +├── 01_core/                     # Die Logik (Script-only, Architektur) +│   ├── systems/                 # Globale Manager (Autoloads): network.gd, game_state.gd, signal_bus.gd +│   ├── components/              # Logik-Nodes: health_component.gd, interactor.gd, inventory_controller.gd +│   └── base_classes/            # Definitionen & Klassen: item_data.gd, spell_base.gd, entity_base.gd +│ +├── 02_library/                  # Das Rohmaterial (Assets & Bausteine) +│   ├── meshes/                  # 3D-Importe (.glb, .obj) nach Kategorien: nature/, architecture/ +│ ├── mesh_libraries/ # Deine fertigen GridMap-Kataloge (.tres) +│   ├── materials/               # Globale Materialien (.tres): wood.tres, stone.tres, metal.tres +│   ├── textures/                # Roh-Bilder für Materialien & Shader +│   ├── ui_assets/               # Visuelles Interface: icons/, fonts/, themes/ +│   ├── shaders/                 # Shader-Dateien (.gdshader) +│   ├── vfx/                     # Partikelsysteme und visuelle Effekte +│   └── audio/                   # Sound-Dateien: sfx/, music/, ambient/ +│ +├── 03_database/                 # Die Spieldaten (Content-Definitionen) +│   ├── items/                   # Hunderte .tres Dateien (iron_sword.tres, health_potion.tres) +│   ├── spells/                  # Zaubersprüche als Resources (.tres) +│   ├── loot_tables/             # Definitionen für Drops und Wahrscheinlichkeiten +│   ├── dialogues/               # Dialog-Skripte oder JSON-Dateien +│   ├── localization/            # Übersetzungsdateien (.csv, .po) +│ └── spawn_tables/ # Definitionen: Welche Monster-Gruppe spawnt in welcher Area? +│ +├── 04_entities/                 # Die Spielobjekte (Zusammengesetzte Szenen) +│   ├── player/                  # Spieler-Szene (player.tscn + player.gd) +│   ├── npcs/                    # Monster, Händler, KI-Gegner +│   ├── props/                   # Interaktive Welt-Objekte: chest.tscn, door.tscn +│   ├── pickups/                 # Das world_item.tscn (Visualisierung von Database-Items) +│   ├── ui/                      # UI-Szenen: hud/, windows/, menus/, shared/ +│ └── markers/ # Unsichtbare Helfer: spawn_point.tscn, area_trigger.tscn +│ +├── 05_worlds/                   # Die Level (Das "NWN Modul") +│   ├── areas/                   # Die eigentlichen Karten/Level (.tscn) +│   ├── prefabs/                 # Wiederverwendbare Szenen-Gruppen (Häuser, Lagerfeuer-Sets) +│   └── environments/            # Licht- & Welt-Einstellungen (.tres) +│ +└── 06_tests/                    # Die Sandkästen (Isolierte Prototypen) +    ├── test_ui.tscn             # Nur UI-Fenster testen +    ├── test_combat.tscn         # Kampf-Mechanik ohne Level testen +    └── test_inventory.tscn      # Item-Logik prüfen + +## 4. Wichtige Dateien & Szenen + +- **Spieler-Modell:** `res://systems/player/player.tres` +- **Area 1:** `res://maps/outdoor/grass/keuwin_grasland.tscn` +- **Area 2:** `res://maps/planes/fugue/halle_der_ahnen.tscn` +- **Startpunkt:** `main.tscn` (vermutlich für das Hauptmenü) +- **Haupt-Logik:** `game_root.gd` / `game_root.tscn` (vermutlich die Wurzel des aktiven Spiels) + +## 5. Implementierungsdetails & Konventionen + +- **Click-to-Move:** Die Spielerbewegung soll aus Performancegründen über eine AStar-Implementierung (`systems/navigation/`) gesteuert werden. +- **Version:** Basis ist **Godot 4.6**. Nutze aktuelle APIs (besonders für Navigation und Multiplayer). +- **GDScript-Regel (No Async Keyword):** Das Schlüsselwort `async` darf **nicht** verwendet werden. Funktionen mit `await` sind automatisch asynchron. +- - *Korrekt:* `func move(): await get_tree().process_frame` +- **Statische Typisierung:** Nutze konsequent Typ-Hints (z.B. `var health: int = 100`), um Fehlerquellen zu minimieren. +- **Node-Referenzen:** Nutze `@onready var` und relative Pfade. Vermeide `/root/`-Pfade wegen der SubViewport-Struktur. + +## 6. Globale Skripte (Autoloads) + +Die folgenden Skripte sind als Singletons (Autoloads in Godot) konfiguriert und global verfügbar: + +- **`GameEvents`** (`res://systems/core/game_events.gd`): Zentraler Event-Bus für die Kommunikation zwischen entkoppelten Systemen. +- `/data/`: Speicher-Logik, Daten-Wrapper (z.B. für JSON, SQLite). +- **`ChatManager`** (`res://systems/network/chat_manager.gd`): Kümmert sich um die Logik des In-Game-Chats. +## 7. Analyse der existierenden Skripte (Stand: Commit 30b6ade) +- `/data/`: Speicher-Logik, Daten-Wrapper (z.B. für JSON, SQLite). +Dieser Abschnitt beschreibt die Verantwortlichkeiten der zentralen Skripte im Projekt. +- **Rolle:** Absoluter Startpunkt des Spiels. +- **Verantwortlichkeit:** Hält die Instanz des Hauptmenüs (`main.tscn`). Seine einzige Aufgabe ist es, zu existieren und die initiale Szene zu laden. Die eigentliche Logik wird sofort an die globalen Manager und das UI delegiert. + +### `/systems/core/game_events.gd` (Autoload: `GameEvents`) +- **Rolle:** Globaler Event-Bus. + +### `/systems/network/network_manager.gd` (Autoload: `NetworkManager`) +- **Rolle:** Das Herzstück der gesamten Anwendung. +- **Verantwortlichkeit:** + - **Startlogik:** Prüft in `_ready()` selbst, ob das Spiel headless gestartet wurde und ruft dann `host_game()` auf. + - **Verbindungs-Management:** Startet den Server (`host_game`) oder verbindet als Client (`join_game`). + - **Area-Management:** Lädt und verwaltet die Spielwelten (Areas). Erstellt für jede Area auf dem Server einen eigenen `SubViewport` mit `own_world_3d = true`, um die Physikwelten zu trennen. + - **Spieler-Spawning:** Koordiniert den komplexen Prozess des Spieler-Spawns. Weist den Client an, eine Szene zu laden (`client_load_area`), wartet auf dessen Bereitschaftsmeldung (`client_ready_to_spawn`) und instanziiert dann den Spieler-Avatar serverseitig in der korrekten Sub-Welt. + +### `/systems/player/player_controller.gd` +- **Rolle:** Steuerungslogik für den Spieler-Avatar. +- **Verantwortlichkeit:** Dieses Skript läuft auf dem Server und allen Clients, verhält sich aber je nach Autorität unterschiedlich: + - **Client (mit Autorität):** Fängt Mauseklicks ab (`_input`), berechnet die Zielposition auf dem Boden und sendet einen `request_move` RPC an den Server. + - **Server:** Empfängt `request_move`, berechnet den Pfad (aktuell nur direktes Ziel) und bewegt den `CharacterBody3D` mittels `move_and_slide()` im `_physics_process`. Die resultierende Position und Rotation werden in `sync_position` und `sync_rotation` gespeichert. + - **Client (ohne Autorität):** Liest im `_process` kontinuierlich `sync_position` und `sync_rotation` aus und setzt die `global_position` und `rotation` des Avatars hart auf diese Werte, um ihn mit dem Server synchron zu halten. + +### `/systems/player/camera_logic.gd` +- **Rolle:** Kamera-Steuerung. +- **Verantwortlichkeit:** Ein einfaches Skript, das an einer `Camera3D` hängt. Es verfolgt sanft (`lerp`) ein `target` (den Spieler-Avatar) und behält dabei einen festen `offset` bei, um eine klassische Top-Down-Perspektive zu erzeugen. Die Zuweisung des Ziels erfolgt durch den `NetworkManager` über den `assign_camera_target` RPC. + +### `/main.tscn` +- **Rolle:** Hauptmenü-UI. +- **Verantwortlichkeit:** Enthält die UI-Elemente wie "Host" und "Join" Buttons. Es wird angenommen, dass die `pressed()` Signale dieser Buttons direkt im Godot-Editor mit den entsprechenden Signalen im `GameEvents`-Singleton (`host_requested`, `join_requested`) verbunden sind. + +## 8. Wichtige Erkenntnisse & Best Practices + +* **MultiplayerSynchronizer:** Konfiguration **nur per Code** in `_ready()`. +* **Property-Syntax:** Pfade in der `SceneReplicationConfig` müssen mit Doppelpunkt starten, wenn sie sich auf den Root-Pfad beziehen (z.B. `config.add_property(":sync_position")`). +* **MultiplayerSpawner:** Nutze einen generischen Spawner in der Basis-Level-Szene für automatische Synchronisation bei Late-Joinern. + +## 7. Anweisungen für die KI (Refactoring & Generierung) + +* **Thinking-Phase:** Analysiere vor der Code-Ausgabe, ob Änderungen die SubViewport-Isolation oder die Server-Autorität beeinflussen könnten. +* **Minimaler Eingriff:** Verändere nur angeforderte Logik. Behalte Kommentare und Signal-Strukturen bei. + - **Spieler-Spawning:** Koordiniert den komplexen Prozess des Spieler-Spawns. Weist den Client an, eine Szene zu laden (`client_load_area`), wartet auf dessen Bereitschaftsmeldung (`client_ready_to_spawn`) und instanziiert dann den Spieler-Avatar serverseitig in der korrekten Sub-Welt. + + +Es gibt kein async Keyword, aber await funktioniert trotzdem. => await get_tree().process_frame + + - **Spieler-Spawning:** Koordiniert den komplexen Prozess des Spieler-Spawns. Weist den Client an, eine Szene zu laden (`client_load_area`), wartet auf dessen Bereitschaftsmeldung (`client_ready_to_spawn`) und instanziiert dann den Spieler-Avatar serverseitig in der korrekten Sub-Welt. + +Die Konfiguration des `MultiplayerSynchronizer` für wiederverwendbare Szenen (wie den Spieler) ist kritisch und muss korrekt gehandhabt werden, um Timing- und Pfad-Probleme zu vermeiden. Die folgenden vier Regeln sind entscheidend: +1. **Konfiguration nur per Code:** Die Konfiguration muss zur Laufzeit im Code erfolgen, nicht im Editor. Der Editor speichert absolute Pfade, die in einem dynamischen Szenen-Setup (mehrere Welten/Viewports) unbrauchbar sind. + +2. **Ort der Konfiguration: `_ready()`:** Die Logik zur Erstellung der `SceneReplicationConfig` gehört in die `_ready()`-funktion des Skripts, dessen Eigenschaften synchronisiert werden sollen (z.B. `player_controller.gd`). + +3. **Korrekte Eigenschafts-Syntax:** Der Pfad zu einer Eigenschaft, die sich direkt auf dem `root_path`-Node des Synchronizers befindet (standardmäßig sein Parent), **muss mit einem Doppelpunkt beginnen**. + - **Korrekt:** `config.add_property(":sync_position")` + - **Falsch:** `config.add_property("sync_position")` oder `config.add_property("..:sync_position")` + + - **Falsch:** `config.add_property("sync_position")` oder `config.add_property("..:sync_position")` + +### Dynamische Konfiguration des MultiplayerSpawner + +- Handhabt nicht nur originäre Objekterzeugung in den Levels, sondern auch Objektaktualisierungen für "Late Joiner" +- Generischer MultiplayerSpawner in Basis Level, von dem alle Level erben, so dass die Spawnliste nur 1x bearbeitet werden muss + +## Namenskonventionen +Signale: +Verwenden Sie einheitliche Präfixe, um die Quelle des Signals anzugeben (z.B. player_, ui_, network_). +Verwenden Sie Verben in der Vergangenheitsform, um anzuzeigen, dass ein Ereignis stattgefunden hat (z.B. player_moved, ui_buttonPressed, network_connected). + +RPCs: +An den Server gerichtet RPCs beginnen mit srv_ und an den Client gerictete RPCs beginnen mit cl_ + +Variablen: +- snake_case für lokale Variablen und Funktionsnamen (z.B. player_health, calculate_damage). +- PascalCase für Klassennamen und Ressourcen (z.B. CharacterData, MyCustomShader). +- Konstanten in GROSSBUCHSTABEN (z.B. MAX_HEALTH, DEFAULT_SPEED). + +## 9. Reihenfolge rpc Methoden +- Programmstart: Autoload Singletons: NetworkManager, GameEvents und ChatManager werden instanziert. + -- Wenn der NetworkManager erkennt, dass er headless ist, startet er automatisch den Server +- Die Haupt Szene startet: game_root.tscn - in ihr sind alle UI Scenen eingebettet. Bis auf eine ( Login ) sind alle hidden. + -- Login beinhaltet eine UI, in der man Host oder Join anwählen kann. Zu join gehört auch noch Username und Passwort +- Es wird Join geklickt: + * GameEvents.ui_join_requested.emit(ip_input.text, username, password) + * Der NetworkManager des Clients hat das Signal abonniert und startet join_game, das den MultiplayerPeer instanziert. + * Der NetworkManager des Servers empfängt das Signal _on_player_connected aktiviert auf dem Client + => cl_show_characters.rpc_id(id, []) , was auf dem Client die Charakterliste anzeigt( die aktuell leer ist). + In der Charakterliste kann über einen Button ein neuer Charakter angelegt werden. Der Button + löst aus: GameEvents.create_character_screen.emit() , der von CharacterCreate abonniert ist, und dort die + Methode show_creation_screen auslöst. + ** Wird das Formular ausgefüllt und Create angeklickt, löst dies die an den Server gerichtete rpc store_player_charakter aus. diff --git a/weltenauge/game_root.gd b/weltenauge/game_root.gd new file mode 100644 index 0000000..b2df6e2 --- /dev/null +++ b/weltenauge/game_root.gd @@ -0,0 +1,4 @@ +extends Node + +func _ready(): + print("--- Start: Projekt Weltenauge ---") diff --git a/weltenauge/game_root.gd.uid b/weltenauge/game_root.gd.uid new file mode 100644 index 0000000..fb86bcc --- /dev/null +++ b/weltenauge/game_root.gd.uid @@ -0,0 +1 @@ +uid://162cjpb4lac4 diff --git a/weltenauge/game_root.tscn b/weltenauge/game_root.tscn new file mode 100644 index 0000000..bac1d61 --- /dev/null +++ b/weltenauge/game_root.tscn @@ -0,0 +1,17 @@ +[gd_scene format=3 uid="uid://cpxxy0664jyjm"] + +[ext_resource type="Script" uid="uid://162cjpb4lac4" path="res://game_root.gd" id="1_xj5yv"] +[ext_resource type="PackedScene" uid="uid://ca2bhph2k3xpy" path="res://04_entities/ui/windows/character_select/character_select.tscn" id="3_bxsma"] +[ext_resource type="PackedScene" uid="uid://lb6btb6426mi" path="res://04_entities/ui/windows/account/login.tscn" id="3_lkrbl"] +[ext_resource type="PackedScene" uid="uid://cdwkmx452hc4k" path="res://04_entities/ui/windows/character_create/character_create.tscn" id="4_lkrbl"] + +[node name="Game_root" type="Node2D" unique_id=1336425327] +script = ExtResource("1_xj5yv") + +[node name="Login" parent="." unique_id=1694671237 instance=ExtResource("3_lkrbl")] + +[node name="CharacterSelect" parent="." unique_id=2079351175 instance=ExtResource("3_bxsma")] +visible = false + +[node name="CharacterCreate" parent="." unique_id=2056198803 instance=ExtResource("4_lkrbl")] +visible = false diff --git a/weltenauge/main.tscn b/weltenauge/main.tscn new file mode 100644 index 0000000..9fc2d97 --- /dev/null +++ b/weltenauge/main.tscn @@ -0,0 +1,8 @@ +[gd_scene format=3 uid="uid://dvmffuxafo75u"] + +[ext_resource type="ArrayMesh" uid="uid://bi22s15yeh6mw" path="res://02_library/meshes/humanoid/dude.tres" id="1_ig7tw"] + +[node name="Node3D" type="Node3D" unique_id=1431241737] + +[node name="MeshInstance3D" type="MeshInstance3D" parent="." unique_id=231315459] +mesh = ExtResource("1_ig7tw") diff --git a/weltenauge/project.godot b/weltenauge/project.godot new file mode 100644 index 0000000..e132cc6 --- /dev/null +++ b/weltenauge/project.godot @@ -0,0 +1,46 @@ +; Engine configuration file. +; It's best edited using the editor UI and not directly, +; since the parameters that go here are not all obvious. +; +; Format: +; [section] ; section goes between [] +; param=value ; assign values to parameters + +config_version=5 + +[application] + +config/name="Project Weltenauge" +run/main_scene="uid://cpxxy0664jyjm" +config/features=PackedStringArray("4.6", "Forward Plus") +run/low_processor_mode=true +boot_splash/image="uid://d1cyxxj8ypfu" +config/icon="uid://d1cyxxj8ypfu" +boot_splash/minimum_display_time=1000 + +[autoload] + +GameEvents="*uid://4xof67e363t4" +ChatManager="*uid://c351ysuym2mdm" +NetworkManager="*uid://tniwwfk5703p" + +[display] + +window/size/viewport_width=1280 +window/size/viewport_height=720 +window/size/window_width_override=1280 +window/size/window_height_override=720 +window/stretch/mode="canvas_items" + +[global_group] + +players="Verwaltung der Spieler Objekte" +portals="Area Übergänge" + +[physics] + +3d/physics_engine="Jolt Physics" + +[rendering] + +rendering_device/driver.windows="d3d12" diff --git a/weltenauge_struktur.txt b/weltenauge_struktur.txt new file mode 100644 index 0000000..7dd1a29 --- /dev/null +++ b/weltenauge_struktur.txt @@ -0,0 +1,42 @@ +/project_root +│ +├── 01_core/                     # Die Logik (Script-only, Architektur) +│   ├── systems/                 # Globale Manager (Autoloads): network.gd, game_state.gd, signal_bus.gd +│   ├── components/              # Logik-Nodes: health_component.gd, interactor.gd, inventory_controller.gd +│   └── base_classes/            # Definitionen & Klassen: item_data.gd, spell_base.gd, entity_base.gd +│ +├── 02_library/                  # Das Rohmaterial (Assets & Bausteine) +│   ├── meshes/                  # 3D-Importe (.glb, .obj) nach Kategorien: nature/, architecture/ +│ ├── mesh_libraries/ # Deine fertigen GridMap-Kataloge (.tres) +│   ├── materials/               # Globale Materialien (.tres): wood.tres, stone.tres, metal.tres +│   ├── textures/                # Roh-Bilder für Materialien & Shader +│   ├── ui_assets/               # Visuelles Interface: icons/, fonts/, themes/ +│   ├── shaders/                 # Shader-Dateien (.gdshader) +│   ├── vfx/                     # Partikelsysteme und visuelle Effekte +│   └── audio/                   # Sound-Dateien: sfx/, music/, ambient/ +│ +├── 03_database/                 # Die Spieldaten (Content-Definitionen) +│   ├── items/                   # Hunderte .tres Dateien (iron_sword.tres, health_potion.tres) +│   ├── spells/                  # Zaubersprüche als Resources (.tres) +│   ├── loot_tables/             # Definitionen für Drops und Wahrscheinlichkeiten +│   ├── dialogues/               # Dialog-Skripte oder JSON-Dateien +│   ├── localization/            # Übersetzungsdateien (.csv, .po) +│ └── spawn_tables/ # Definitionen: Welche Monster-Gruppe spawnt in welcher Area? +│ +├── 04_entities/                 # Die Spielobjekte (Zusammengesetzte Szenen) +│   ├── player/                  # Spieler-Szene (player.tscn + player.gd) +│   ├── npcs/                    # Monster, Händler, KI-Gegner +│   ├── props/                   # Interaktive Welt-Objekte: chest.tscn, door.tscn +│   ├── pickups/                 # Das world_item.tscn (Visualisierung von Database-Items) +│   ├── ui/                      # UI-Szenen: hud/, windows/, menus/, shared/ +│ └── markers/ # Unsichtbare Helfer: spawn_point.tscn, area_trigger.tscn +│ +├── 05_worlds/                   # Die Level (Das "NWN Modul") +│   ├── areas/                   # Die eigentlichen Karten/Level (.tscn) +│   ├── prefabs/                 # Wiederverwendbare Szenen-Gruppen (Häuser, Lagerfeuer-Sets) +│   └── environments/            # Licht- & Welt-Einstellungen (.tres) +│ +└── 06_tests/                    # Die Sandkästen (Isolierte Prototypen) +    ├── test_ui.tscn             # Nur UI-Fenster testen +    ├── test_combat.tscn         # Kampf-Mechanik ohne Level testen +    └── test_inventory.tscn      # Item-Logik prüfen diff --git a/workbench/zauberturm_html/index.html b/workbench/zauberturm_html/index.html new file mode 100644 index 0000000..87baa82 --- /dev/null +++ b/workbench/zauberturm_html/index.html @@ -0,0 +1,126 @@ + + + + + + Zauberturm Projekt-Log + + + + +
+

Zauberturm.de

+

Projekt-Entwicklung: Screenshot Log

+
+ + + + + + + \ No newline at end of file diff --git a/workbench/zauberturm_html/logo_projekt_weltenauge.png b/workbench/zauberturm_html/logo_projekt_weltenauge.png new file mode 100644 index 0000000..6d19032 --- /dev/null +++ b/workbench/zauberturm_html/logo_projekt_weltenauge.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8cca249374b1fc72c54e701d63e515945db5c4ff85490be8db50ec57aa7f561f +size 1607842 diff --git a/workbench/zauberturm_html/logo_zauberturm.png b/workbench/zauberturm_html/logo_zauberturm.png new file mode 100644 index 0000000..74ecef4 --- /dev/null +++ b/workbench/zauberturm_html/logo_zauberturm.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b0362c477dd6f156b85378a7b253439ccfc2997531ae12f52ecb194b3786cfb1 +size 2217752