From 5addad0b8c132500aaaeac895fdac3cb5bfb767b Mon Sep 17 00:00:00 2001 From: Andre Date: Sat, 14 Mar 2026 15:35:13 +0100 Subject: [PATCH] =?UTF-8?q?Initial=20commit:=20DungeonCrawler=20Grundger?= =?UTF-8?q?=C3=BCst?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Third-Person Spieler mit WASD-Bewegung und Kamerasteuerung (RMB + Mausrad-Zoom) - HP-System mit Healthbar und Aktionsleiste (Slots 1-9) - Autoattack-System: Linksklick markiert Ziel, Rechtsklick markiert + greift an - Waffensystem-Basis: Schaden basiert auf ausgerüsteter Waffe (unbewaffnet = 1) - Gegner-KI: läuft auf Spieler zu, greift bei Reichweite an, zeigt HP-Label bei Markierung - Ressourcen-Klassen: Attack und Weapon Co-Authored-By: Claude Sonnet 4.6 --- .editorconfig | 4 + .gitattributes | 2 + .gitignore | 3 + basic_attack.tres | 9 +++ camera_pivot.gd | 32 ++++++++ camera_pivot.gd.uid | 1 + enemy.gd | 79 +++++++++++++++++++ enemy.gd.uid | 1 + enemy.tscn | 34 ++++++++ hud.gd | 33 ++++++++ hud.gd.uid | 1 + hud.tscn | 161 ++++++++++++++++++++++++++++++++++++++ icon.svg | 1 + icon.svg.import | 43 ++++++++++ player.gd | 168 ++++++++++++++++++++++++++++++++++++++++ player.gd.uid | 1 + player.tscn | 29 +++++++ project.godot | 107 +++++++++++++++++++++++++ resources/attack.gd | 28 +++++++ resources/attack.gd.uid | 1 + resources/weapon.gd | 14 ++++ resources/weapon.gd.uid | 1 + world.gd | 11 +++ world.gd.uid | 1 + world.tscn | 38 +++++++++ 25 files changed, 803 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 basic_attack.tres create mode 100644 camera_pivot.gd create mode 100644 camera_pivot.gd.uid create mode 100644 enemy.gd create mode 100644 enemy.gd.uid create mode 100644 enemy.tscn create mode 100644 hud.gd create mode 100644 hud.gd.uid create mode 100644 hud.tscn create mode 100644 icon.svg create mode 100644 icon.svg.import create mode 100644 player.gd create mode 100644 player.gd.uid create mode 100644 player.tscn create mode 100644 project.godot create mode 100644 resources/attack.gd create mode 100644 resources/attack.gd.uid create mode 100644 resources/weapon.gd create mode 100644 resources/weapon.gd.uid create mode 100644 world.gd create mode 100644 world.gd.uid create mode 100644 world.tscn diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..f28239b --- /dev/null +++ b/.editorconfig @@ -0,0 +1,4 @@ +root = true + +[*] +charset = utf-8 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..8ad74f7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Normalize EOL for all files that Git considers text files. +* text=auto eol=lf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0af181c --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +# Godot 4+ specific ignores +.godot/ +/android/ diff --git a/basic_attack.tres b/basic_attack.tres new file mode 100644 index 0000000..e1a34be --- /dev/null +++ b/basic_attack.tres @@ -0,0 +1,9 @@ +[gd_resource type="Resource" script_class="Attack" format=3 uid="uid://d02bao1dwygan"] + +[ext_resource type="Script" uid="uid://cpa1p4k6xtiga" path="res://resources/attack.gd" id="1_4028k"] + +[resource] +script = ExtResource("1_4028k") +name = "Autoattack" +cooldown = 1.5 +metadata/_custom_type_script = "uid://cpa1p4k6xtiga" diff --git a/camera_pivot.gd b/camera_pivot.gd new file mode 100644 index 0000000..344e374 --- /dev/null +++ b/camera_pivot.gd @@ -0,0 +1,32 @@ +# CameraPivot.gd +# Steuert die Third-Person-Kamera: Maussteuerung (RMB), Zoom per Mausrad +extends Node3D + +@export var sensitivity = 0.3 # Mausempfindlichkeit +@export var min_pitch = -40.0 # Maximale Neigung nach unten +@export var max_pitch = 20.0 # Maximale Neigung nach oben +@export var min_zoom = 5.0 # Minimale Kameraentfernung +@export var max_zoom = 20.0 # Maximale Kameraentfernung +@export var zoom_speed = 1.0 # Zoom-Geschwindigkeit pro Mausrad-Schritt + +var pitch = 0.0 + +@onready var camera = $Camera3D + +func _ready(): + Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) + +func _input(event): + # RMB gehalten: Kamera drehen + if event is InputEventMouseMotion and Input.is_mouse_button_pressed(MOUSE_BUTTON_RIGHT): + get_parent().rotate_y(deg_to_rad(-event.relative.x * sensitivity)) + pitch -= event.relative.y * sensitivity + pitch = clamp(pitch, min_pitch, max_pitch) + rotation_degrees.x = pitch + + # Mausrad: Zoom + if event is InputEventMouseButton: + if event.button_index == MOUSE_BUTTON_WHEEL_DOWN: + camera.position.z = clamp(camera.position.z + zoom_speed, min_zoom, max_zoom) + if event.button_index == MOUSE_BUTTON_WHEEL_UP: + camera.position.z = clamp(camera.position.z - zoom_speed, min_zoom, max_zoom) diff --git a/camera_pivot.gd.uid b/camera_pivot.gd.uid new file mode 100644 index 0000000..0ae3d0f --- /dev/null +++ b/camera_pivot.gd.uid @@ -0,0 +1 @@ +uid://bwtwon54po4w3 diff --git a/enemy.gd b/enemy.gd new file mode 100644 index 0000000..7a60e6b --- /dev/null +++ b/enemy.gd @@ -0,0 +1,79 @@ +# Enemy.gd +# Steuert den Gegner: KI-Bewegung zum Spieler, Angriff, HP, Zielanzeige +extends CharacterBody3D + +const SPEED = 3.0 +const GRAVITY = 9.8 +const ATTACK_DAMAGE = 5 +const ATTACK_RANGE = 1.5 +const ATTACK_COOLDOWN = 2.0 + +var max_hp = 50 +var current_hp = 50 +var target = null # Ziel des Gegners (normalerweise der Spieler) +var can_attack = true + +@onready var health_label = $HealthLabel + +func _ready(): + health_label.visible = false + _update_label() + +# HP-Label Text aktualisieren +func _update_label(): + health_label.text = str(current_hp) + " / " + str(max_hp) + +# HP-Label anzeigen (wenn Gegner markiert wird) +func show_health(): + health_label.visible = true + +# HP-Label verstecken (wenn Markierung aufgehoben wird) +func hide_health(): + health_label.visible = false + +# Schaden nehmen und Label aktualisieren +func take_damage(amount): + current_hp -= amount + _update_label() + if current_hp <= 0: + die() + +# Gegner aus der Szene entfernen +func die(): + print("Gegner besiegt!") + queue_free() + +func _physics_process(delta): + if not is_on_floor(): + velocity.y -= GRAVITY * delta + + if target == null: + move_and_slide() + return + + var distance = global_position.distance_to(target.global_position) + + if distance <= ATTACK_RANGE: + # In Reichweite: angreifen + velocity.x = 0 + velocity.z = 0 + if can_attack: + _attack() + else: + # Direkt auf Ziel zubewegen + var direction = (target.global_position - global_position) + direction.y = 0 + direction = direction.normalized() + velocity.x = direction.x * SPEED + velocity.z = direction.z * SPEED + look_at(Vector3(target.global_position.x, global_position.y, target.global_position.z)) + + move_and_slide() + +# Angriff mit Cooldown +func _attack(): + can_attack = false + target.take_damage(ATTACK_DAMAGE) + print("Gegner greift an!") + await get_tree().create_timer(ATTACK_COOLDOWN).timeout + can_attack = true diff --git a/enemy.gd.uid b/enemy.gd.uid new file mode 100644 index 0000000..963bf6f --- /dev/null +++ b/enemy.gd.uid @@ -0,0 +1 @@ +uid://bg5qs3pcfp7p7 diff --git a/enemy.tscn b/enemy.tscn new file mode 100644 index 0000000..823a15e --- /dev/null +++ b/enemy.tscn @@ -0,0 +1,34 @@ +[gd_scene format=3 uid="uid://cvojaeanxugfj"] + +[ext_resource type="PackedScene" uid="uid://tosl2au4emxt" path="res://assets/kenney_blocky-characters_20/Models/GLB format/character-d.glb" id="1_7k104"] + +[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_4gyqm"] +radius = 0.6 +height = 3.0 + +[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_7k104"] +radius = 0.6 +height = 3.0 + +[node name="Enemy" type="CharacterBody3D" unique_id=332011146] + +[node name="character-d2" parent="." unique_id=846574684 instance=ExtResource("1_7k104")] +transform = Transform3D(-1, 0, -8.742278e-08, 0, 1, 0, 8.742278e-08, 0, -1, 0, 0, 0) + +[node name="CollisionShape3D" type="CollisionShape3D" parent="." unique_id=1323028920] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.35, 0) +shape = SubResource("CapsuleShape3D_4gyqm") + +[node name="NavigationAgent3D" type="NavigationAgent3D" parent="." unique_id=457987844] + +[node name="Area3D" type="Area3D" parent="." unique_id=1689838821] + +[node name="CollisionShape3D" type="CollisionShape3D" parent="Area3D" unique_id=116643275] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.35, 0) +shape = SubResource("CapsuleShape3D_7k104") + +[node name="HealthLabel" type="Label3D" parent="." unique_id=1251847350] +transform = Transform3D(-1, 0, -8.742278e-08, 0, 1, 0, 8.742278e-08, 0, -1, 0, 3.5, 0) +visible = false +text = "50 / 50" +outline_size = 8 diff --git a/hud.gd b/hud.gd new file mode 100644 index 0000000..32798cb --- /dev/null +++ b/hud.gd @@ -0,0 +1,33 @@ +# HUD.gd +# Verwaltet die Spieler-UI: HP-Leiste, Aktionsleiste (Slots 1-9) +extends CanvasLayer + +@onready var health_bar = $Control/HealthBar +@onready var health_label = $Control/HealthBar/HealthLabel +@onready var action_slots = [ + $Control/ActionBar/A1, + $Control/ActionBar/A2, + $Control/ActionBar/A3, + $Control/ActionBar/A4, + $Control/ActionBar/A5, + $Control/ActionBar/A6, + $Control/ActionBar/A7, + $Control/ActionBar/A8, + $Control/ActionBar/A9 +] + +var active_slot = 0 + +# HP-Leiste und Text aktualisieren +func update_health(current_hp, max_hp): + health_bar.max_value = max_hp + health_bar.value = current_hp + health_label.text = str(current_hp) + " / " + str(max_hp) + +# Aktions-Slot kurz golden hervorheben (0.1s) +func set_active_slot(index): + action_slots[active_slot].self_modulate = Color(1, 1, 1) + active_slot = index + action_slots[active_slot].self_modulate = Color(1, 0.8, 0) + await get_tree().create_timer(0.1).timeout + action_slots[active_slot].self_modulate = Color(1, 1, 1) diff --git a/hud.gd.uid b/hud.gd.uid new file mode 100644 index 0000000..944cf9a --- /dev/null +++ b/hud.gd.uid @@ -0,0 +1 @@ +uid://c1vae5t5fabmf diff --git a/hud.tscn b/hud.tscn new file mode 100644 index 0000000..36d80ed --- /dev/null +++ b/hud.tscn @@ -0,0 +1,161 @@ +[gd_scene format=3 uid="uid://bej3excyoxrdh"] + +[ext_resource type="Script" uid="uid://c1vae5t5fabmf" path="res://hud.gd" id="1_37p78"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_37p78"] +bg_color = Color(0.82191056, 2.623126e-06, 7.70092e-07, 1) + +[node name="HUD" type="CanvasLayer" unique_id=1901284390] +script = ExtResource("1_37p78") + +[node name="Control" type="Control" parent="." unique_id=1898217661] +layout_mode = 3 +anchors_preset = 0 +offset_right = 40.0 +offset_bottom = 40.0 + +[node name="HealthBar" type="ProgressBar" parent="Control" unique_id=1012082084] +layout_mode = 0 +offset_left = 20.0 +offset_top = 20.0 +offset_right = 220.0 +offset_bottom = 47.0 +theme_override_styles/fill = SubResource("StyleBoxFlat_37p78") +value = 100.0 +show_percentage = false + +[node name="HealthLabel" type="Label" parent="Control/HealthBar" unique_id=347950412] +layout_mode = 0 +offset_right = 200.0 +offset_bottom = 27.0 +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="ActionBar" type="HBoxContainer" parent="Control" unique_id=1015761408] +layout_mode = 1 +anchors_preset = -1 +anchor_left = 14.875001 +anchor_top = 15.125001 +anchor_right = 14.875001 +anchor_bottom = 15.125001 +offset_left = -225.00006 +offset_top = -25.000061 +offset_right = 224.99994 +offset_bottom = 24.999939 + +[node name="A1" type="Panel" parent="Control/ActionBar" unique_id=712323959] +custom_minimum_size = Vector2(50, 50) +layout_mode = 2 + +[node name="Label" type="Label" parent="Control/ActionBar/A1" unique_id=2101503869] +custom_minimum_size = Vector2(46, 50) +layout_mode = 0 +offset_left = 2.0 +offset_right = 48.0 +offset_bottom = 50.0 +theme_override_font_sizes/font_size = 12 +text = "1" + +[node name="A2" type="Panel" parent="Control/ActionBar" unique_id=1606850166] +custom_minimum_size = Vector2(50, 50) +layout_mode = 2 + +[node name="Label" type="Label" parent="Control/ActionBar/A2" unique_id=908151683] +custom_minimum_size = Vector2(46, 50) +layout_mode = 0 +offset_left = 2.0 +offset_right = 48.0 +offset_bottom = 50.0 +theme_override_font_sizes/font_size = 12 +text = "2" + +[node name="A3" type="Panel" parent="Control/ActionBar" unique_id=1199740879] +custom_minimum_size = Vector2(50, 50) +layout_mode = 2 + +[node name="Label" type="Label" parent="Control/ActionBar/A3" unique_id=2139034524] +custom_minimum_size = Vector2(46, 50) +layout_mode = 0 +offset_left = 2.0 +offset_right = 48.0 +offset_bottom = 50.0 +theme_override_font_sizes/font_size = 12 +text = "3" + +[node name="A4" type="Panel" parent="Control/ActionBar" unique_id=268936083] +custom_minimum_size = Vector2(50, 50) +layout_mode = 2 + +[node name="Label" type="Label" parent="Control/ActionBar/A4" unique_id=1504208330] +custom_minimum_size = Vector2(46, 50) +layout_mode = 0 +offset_left = 2.0 +offset_right = 48.0 +offset_bottom = 50.0 +theme_override_font_sizes/font_size = 12 +text = "4" + +[node name="A5" type="Panel" parent="Control/ActionBar" unique_id=1768061775] +custom_minimum_size = Vector2(50, 50) +layout_mode = 2 + +[node name="Label" type="Label" parent="Control/ActionBar/A5" unique_id=1852586315] +custom_minimum_size = Vector2(46, 50) +layout_mode = 0 +offset_left = 2.0 +offset_right = 48.0 +offset_bottom = 50.0 +theme_override_font_sizes/font_size = 12 +text = "5" + +[node name="A6" type="Panel" parent="Control/ActionBar" unique_id=514136908] +custom_minimum_size = Vector2(50, 50) +layout_mode = 2 + +[node name="Label" type="Label" parent="Control/ActionBar/A6" unique_id=129205342] +custom_minimum_size = Vector2(46, 50) +layout_mode = 0 +offset_left = 2.0 +offset_right = 48.0 +offset_bottom = 50.0 +theme_override_font_sizes/font_size = 12 +text = "6" + +[node name="A7" type="Panel" parent="Control/ActionBar" unique_id=930419077] +custom_minimum_size = Vector2(50, 50) +layout_mode = 2 + +[node name="Label" type="Label" parent="Control/ActionBar/A7" unique_id=1594316102] +custom_minimum_size = Vector2(46, 50) +layout_mode = 0 +offset_left = 2.0 +offset_right = 48.0 +offset_bottom = 50.0 +theme_override_font_sizes/font_size = 12 +text = "7" + +[node name="A8" type="Panel" parent="Control/ActionBar" unique_id=1826123904] +custom_minimum_size = Vector2(50, 50) +layout_mode = 2 + +[node name="Label" type="Label" parent="Control/ActionBar/A8" unique_id=682267067] +custom_minimum_size = Vector2(46, 50) +layout_mode = 0 +offset_left = 2.0 +offset_right = 48.0 +offset_bottom = 50.0 +theme_override_font_sizes/font_size = 12 +text = "8" + +[node name="A9" type="Panel" parent="Control/ActionBar" unique_id=1708070042] +custom_minimum_size = Vector2(50, 50) +layout_mode = 2 + +[node name="Label" type="Label" parent="Control/ActionBar/A9" unique_id=989106090] +custom_minimum_size = Vector2(46, 50) +layout_mode = 0 +offset_left = 2.0 +offset_right = 48.0 +offset_bottom = 50.0 +theme_override_font_sizes/font_size = 12 +text = "9" diff --git a/icon.svg b/icon.svg new file mode 100644 index 0000000..c6bbb7d --- /dev/null +++ b/icon.svg @@ -0,0 +1 @@ + diff --git a/icon.svg.import b/icon.svg.import new file mode 100644 index 0000000..176d369 --- /dev/null +++ b/icon.svg.import @@ -0,0 +1,43 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cghqrs3ts1t2y" +path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icon.svg" +dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.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 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/player.gd b/player.gd new file mode 100644 index 0000000..4ae3bed --- /dev/null +++ b/player.gd @@ -0,0 +1,168 @@ +# Player.gd +# Steuert den Spielercharakter: Bewegung, Kamera, HP, Angriff, Zielauswahl +extends CharacterBody3D + +const SPEED = 5.0 +const JUMP_VELOCITY = 4.5 +const GRAVITY = 9.8 + +var max_hp = 100 +var current_hp = 100 +var can_attack = true +var target = null # Aktuell markierter Gegner +var equipped_weapon = null # Ausgerüstete Waffe (null = unbewaffnet, Schaden = 1) + +@onready var camera_pivot = $CameraPivot +@onready var camera = $CameraPivot/Camera3D +@onready var hud = $HUD + +func _ready(): + hud.update_health(current_hp, max_hp) + hud.set_active_slot(0) + +# Schaden am Spieler abziehen und HP-Leiste aktualisieren +func take_damage(amount): + current_hp = clamp(current_hp - amount, 0, max_hp) + hud.update_health(current_hp, max_hp) + if current_hp <= 0: + die() + +# HP heilen und HP-Leiste aktualisieren +func heal(amount): + current_hp = clamp(current_hp + amount, 0, max_hp) + hud.update_health(current_hp, max_hp) + +func die(): + print("Spieler gestorben!") + +# Schaden basierend auf ausgerüsteter Waffe (unbewaffnet = 1) +func get_attack_damage() -> int: + if equipped_weapon == null: + return 1 + return randi_range(equipped_weapon.min_damage, equipped_weapon.max_damage) + +# Reichweite basierend auf ausgerüsteter Waffe (unbewaffnet = 1.5) +func get_attack_range() -> float: + if equipped_weapon == null: + return 1.5 + return equipped_weapon.range + +# Angriffsgeschwindigkeit basierend auf ausgerüsteter Waffe (unbewaffnet = 1.5s) +func get_attack_cooldown() -> float: + if equipped_weapon == null: + return 1.5 + return equipped_weapon.attack_speed + +# Ziel markieren — start_attack=true startet sofort die Autoattack +func set_target(new_target, start_attack: bool = false): + if target != null and is_instance_valid(target): + target.hide_health() + target = new_target + target.show_health() + print("Ziel markiert: ", target.name) + if start_attack and can_attack: + autoattack() + +# Autoattack: greift wiederholt an solange Ziel gültig ist +func autoattack(): + if target == null or not is_instance_valid(target): + target = null + return + if not can_attack: + return + + var distance = global_position.distance_to(target.global_position) + if distance <= get_attack_range(): + can_attack = false + var dmg = get_attack_damage() + target.take_damage(dmg) + print("Autoattack: ", dmg, " Schaden") + await get_tree().create_timer(get_attack_cooldown()).timeout + can_attack = true + autoattack() + else: + await get_tree().create_timer(0.5).timeout + autoattack() + +# Raycast von der Kamera auf Mausposition — trifft Gegner mit take_damage() +func _try_select_target(start_attack: bool = false): + var space_state = get_world_3d().direct_space_state + var viewport = get_viewport() + var mouse_pos = viewport.get_mouse_position() + var ray_origin = camera.project_ray_origin(mouse_pos) + var ray_end = ray_origin + camera.project_ray_normal(mouse_pos) * 100.0 + var query = PhysicsRayQueryParameters3D.create(ray_origin, ray_end) + query.exclude = [self] + var result = space_state.intersect_ray(query) + if result and result.collider.has_method("take_damage"): + set_target(result.collider, start_attack) + +func _physics_process(delta): + # Schwerkraft + if not is_on_floor(): + velocity.y -= GRAVITY * delta + + # Springen + if Input.is_action_just_pressed("ui_accept") and is_on_floor(): + velocity.y = JUMP_VELOCITY + + # Linksklick: nur markieren + if Input.is_action_just_pressed("select_target"): + _try_select_target(false) + + # Rechtsklick: markieren + angreifen + if Input.is_action_just_pressed("ui_right_mouse"): + _try_select_target(true) + + # Aktionsleiste 1-9 + if Input.is_action_just_pressed("action_1"): + hud.set_active_slot(0) + if target != null: + autoattack() + if Input.is_action_just_pressed("action_2"): + hud.set_active_slot(1) + if Input.is_action_just_pressed("action_3"): + hud.set_active_slot(2) + if Input.is_action_just_pressed("action_4"): + hud.set_active_slot(3) + if Input.is_action_just_pressed("action_5"): + hud.set_active_slot(4) + if Input.is_action_just_pressed("action_6"): + hud.set_active_slot(5) + if Input.is_action_just_pressed("action_7"): + hud.set_active_slot(6) + if Input.is_action_just_pressed("action_8"): + hud.set_active_slot(7) + if Input.is_action_just_pressed("action_9"): + hud.set_active_slot(8) + + # TEST: T drücken = 10 Schaden + if Input.is_action_just_pressed("test_damage"): + take_damage(10) + + # Eingabe + var input_dir = Vector2.ZERO + if Input.is_action_pressed("move_forward"): + input_dir.y -= 1 + if Input.is_action_pressed("move_back"): + input_dir.y += 1 + if Input.is_action_pressed("move_left"): + input_dir.x -= 1 + if Input.is_action_pressed("move_right"): + input_dir.x += 1 + + # Bewegung relativ zur Kamera + var world_yaw = rotation.y + camera_pivot.rotation.y + var forward = Vector3(-sin(world_yaw), 0, -cos(world_yaw)).normalized() + var right = Vector3(cos(world_yaw), 0, -sin(world_yaw)).normalized() + var direction = (forward * -input_dir.y + right * input_dir.x) + + velocity.x = direction.x * SPEED + velocity.z = direction.z * SPEED + + # RMB gehalten: Spieler schaut in Kamerarichtung + if Input.is_mouse_button_pressed(MOUSE_BUTTON_RIGHT): + rotation.y = world_yaw + camera_pivot.rotation.y = 0 + + move_and_slide() diff --git a/player.gd.uid b/player.gd.uid new file mode 100644 index 0000000..2b4ce41 --- /dev/null +++ b/player.gd.uid @@ -0,0 +1 @@ +uid://b6n25e5fh82ra diff --git a/player.tscn b/player.tscn new file mode 100644 index 0000000..14906d5 --- /dev/null +++ b/player.tscn @@ -0,0 +1,29 @@ +[gd_scene format=3 uid="uid://dniyuebl8yhtv"] + +[ext_resource type="Script" uid="uid://b6n25e5fh82ra" path="res://player.gd" id="1_4flbx"] +[ext_resource type="PackedScene" uid="uid://01rrtitc6yh1" path="res://assets/kenney_blocky-characters_20/Models/GLB format/character-b.glb" id="2_hqtel"] +[ext_resource type="Script" uid="uid://bwtwon54po4w3" path="res://camera_pivot.gd" id="2_onrkg"] +[ext_resource type="PackedScene" uid="uid://bej3excyoxrdh" path="res://hud.tscn" id="4_hqtel"] + +[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_4flbx"] +radius = 0.6 +height = 3.0 + +[node name="CharacterBody3D" type="CharacterBody3D" unique_id=937297102] +transform = Transform3D(0.5, 0, 0, 0, 0.5, 0, 0, 0, 0.5, 0, 0, 0) +script = ExtResource("1_4flbx") + +[node name="character-b2" parent="." unique_id=926968795 instance=ExtResource("2_hqtel")] +transform = Transform3D(-1, 0, -8.742278e-08, 0, 1, 0, 8.742278e-08, 0, -1, 0, 0, 0) + +[node name="CollisionShape3D" type="CollisionShape3D" parent="." unique_id=1359412306] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.35, 0) +shape = SubResource("CapsuleShape3D_4flbx") + +[node name="CameraPivot" type="Node3D" parent="." unique_id=638440275] +script = ExtResource("2_onrkg") + +[node name="Camera3D" type="Camera3D" parent="CameraPivot" unique_id=1625345908] +transform = Transform3D(2, 0, 0, 0, 1.8126155, 0.84523654, 0, -0.84523654, 1.8126155, 0, 5, 5) + +[node name="HUD" parent="." unique_id=1901284390 instance=ExtResource("4_hqtel")] diff --git a/project.godot b/project.godot new file mode 100644 index 0000000..b0f0c19 --- /dev/null +++ b/project.godot @@ -0,0 +1,107 @@ +; 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="DungeonCrawler" +run/main_scene="uid://bp0g1glxo816h" +config/features=PackedStringArray("4.6", "Forward Plus") +config/icon="res://icon.svg" + +[input] + +move_forward={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null) +] +} +move_back={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null) +] +} +move_left={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null) +] +} +move_right={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null) +] +} +test_damage={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":84,"key_label":0,"unicode":116,"location":0,"echo":false,"script":null) +] +} +action_1={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":49,"key_label":0,"unicode":49,"location":0,"echo":false,"script":null) +] +} +action_2={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":50,"key_label":0,"unicode":50,"location":0,"echo":false,"script":null) +] +} +action_3={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":51,"key_label":0,"unicode":51,"location":0,"echo":false,"script":null) +] +} +action_4={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":52,"key_label":0,"unicode":52,"location":0,"echo":false,"script":null) +] +} +action_5={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":53,"key_label":0,"unicode":53,"location":0,"echo":false,"script":null) +] +} +action_6={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":54,"key_label":0,"unicode":54,"location":0,"echo":false,"script":null) +] +} +action_7={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":55,"key_label":0,"unicode":55,"location":0,"echo":false,"script":null) +] +} +action_8={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":56,"key_label":0,"unicode":56,"location":0,"echo":false,"script":null) +] +} +action_9={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":57,"key_label":0,"unicode":57,"location":0,"echo":false,"script":null) +] +} +select_target={ +"deadzone": 0.2, +"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":1,"position":Vector2(250, 17),"global_position":Vector2(259, 65),"factor":1.0,"button_index":1,"canceled":false,"pressed":true,"double_click":false,"script":null) +] +} +ui_right_mouse={ +"deadzone": 0.2, +"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":2,"position":Vector2(411, 19),"global_position":Vector2(420, 67),"factor":1.0,"button_index":2,"canceled":false,"pressed":true,"double_click":false,"script":null) +] +} + +[physics] + +3d/physics_engine="Jolt Physics" + +[rendering] + +rendering_device/driver.windows="d3d12" diff --git a/resources/attack.gd b/resources/attack.gd new file mode 100644 index 0000000..14ad0fb --- /dev/null +++ b/resources/attack.gd @@ -0,0 +1,28 @@ +# Attack.gd +# Ressource für Angriffe — Schaden, Reichweite und Cooldown kommen von der ausgerüsteten Waffe +extends Resource +class_name Attack + +enum DamageType { PHYSICAL, FIRE, ICE, LIGHTNING, POISON } + +@export var name: String = "Unbenannt" +@export var damage_type: DamageType = DamageType.PHYSICAL +@export var icon: Texture2D = null + +# Schaden berechnen — ohne Waffe = 1 +func get_damage(weapon: Weapon) -> int: + if weapon == null or weapon.weapon_type == Weapon.WeaponType.UNARMED: + return 1 + return randi_range(weapon.min_damage, weapon.max_damage) + +# Reichweite — ohne Waffe = 1.5 +func get_range(weapon: Weapon) -> float: + if weapon == null or weapon.weapon_type == Weapon.WeaponType.UNARMED: + return 1.5 + return weapon.range + +# Cooldown — ohne Waffe = 1.5s +func get_cooldown(weapon: Weapon) -> float: + if weapon == null or weapon.weapon_type == Weapon.WeaponType.UNARMED: + return 1.5 + return weapon.attack_speed diff --git a/resources/attack.gd.uid b/resources/attack.gd.uid new file mode 100644 index 0000000..3d0513c --- /dev/null +++ b/resources/attack.gd.uid @@ -0,0 +1 @@ +uid://cpa1p4k6xtiga diff --git a/resources/weapon.gd b/resources/weapon.gd new file mode 100644 index 0000000..e44c404 --- /dev/null +++ b/resources/weapon.gd @@ -0,0 +1,14 @@ +# Weapon.gd +# Ressource für Waffen — definiert Schadensbereich, Angriffsgeschwindigkeit und Reichweite +extends Resource +class_name Weapon + +enum WeaponType { UNARMED, SWORD, AXE, MACE, DAGGER, STAFF, BOW } + +@export var name: String = "Unbewaffnet" +@export var weapon_type: WeaponType = WeaponType.UNARMED +@export var min_damage: int = 1 +@export var max_damage: int = 1 +@export var attack_speed: float = 1.5 # Cooldown in Sekunden +@export var range: float = 2.0 +@export var icon: Texture2D = null diff --git a/resources/weapon.gd.uid b/resources/weapon.gd.uid new file mode 100644 index 0000000..9b9713f --- /dev/null +++ b/resources/weapon.gd.uid @@ -0,0 +1 @@ +uid://c0jenjw4stba2 diff --git a/world.gd b/world.gd new file mode 100644 index 0000000..3ee514d --- /dev/null +++ b/world.gd @@ -0,0 +1,11 @@ +# World.gd +# Initialisiert die Spielwelt: weist dem Gegner den Spieler als Ziel zu +extends Node3D + +func _ready(): + var player = get_node("Player") + var enemy = get_node("Enemy") + if enemy and player: + enemy.target = player + else: + print("Fehler: Player oder Enemy nicht gefunden!") diff --git a/world.gd.uid b/world.gd.uid new file mode 100644 index 0000000..aa7fd4f --- /dev/null +++ b/world.gd.uid @@ -0,0 +1 @@ +uid://cx56h588mfsk0 diff --git a/world.tscn b/world.tscn new file mode 100644 index 0000000..6f1de4f --- /dev/null +++ b/world.tscn @@ -0,0 +1,38 @@ +[gd_scene format=3 uid="uid://bp0g1glxo816h"] + +[ext_resource type="PackedScene" uid="uid://dniyuebl8yhtv" path="res://player.tscn" id="1_f3sb7"] +[ext_resource type="Script" uid="uid://cx56h588mfsk0" path="res://world.gd" id="1_tlwt5"] +[ext_resource type="PackedScene" uid="uid://cvojaeanxugfj" path="res://enemy.tscn" id="2_fj7yv"] +[ext_resource type="Script" uid="uid://bg5qs3pcfp7p7" path="res://enemy.gd" id="4_aqk2v"] + +[sub_resource type="BoxShape3D" id="BoxShape3D_fj7yv"] +size = Vector3(200, 0.5, 200) + +[sub_resource type="BoxMesh" id="BoxMesh_tlwt5"] +size = Vector3(200, 0.5, 200) + +[sub_resource type="NavigationMesh" id="NavigationMesh_fj7yv"] + +[node name="World" type="Node3D" unique_id=2007838514] +script = ExtResource("1_tlwt5") + +[node name="StaticBody3D" type="StaticBody3D" parent="." unique_id=2101916269] + +[node name="CollisionShape3D" type="CollisionShape3D" parent="StaticBody3D" unique_id=1873339390] +shape = SubResource("BoxShape3D_fj7yv") + +[node name="MeshInstance3D" type="MeshInstance3D" parent="StaticBody3D" unique_id=1214783061] +mesh = SubResource("BoxMesh_tlwt5") + +[node name="Player" parent="." unique_id=937297102 instance=ExtResource("1_f3sb7")] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.3, 0) + +[node name="DirectionalLight3D" type="DirectionalLight3D" parent="." unique_id=1394887598] +transform = Transform3D(-45, 0, 0, 0, -45, 0, 0, 0, -45, 0, 0, 0) + +[node name="Enemy" parent="." unique_id=332011146 instance=ExtResource("2_fj7yv")] +transform = Transform3D(-1, 0, -8.742278e-08, 0, 1, 0, 8.742278e-08, 0, -1, 0, 0.3, -7.038) +script = ExtResource("4_aqk2v") + +[node name="NavigationRegion3D" type="NavigationRegion3D" parent="." unique_id=827244005] +navigation_mesh = SubResource("NavigationMesh_fj7yv")