From 9ab6deddff83599ed36861f3f67eac593ad59b6e Mon Sep 17 00:00:00 2001 From: Andre Date: Sun, 15 Mar 2026 20:10:05 +0100 Subject: [PATCH] =?UTF-8?q?Inventar,=20Equipment,=20Klassensystem=20und=20?= =?UTF-8?q?Waffenschaden=20hinzugef=C3=BCgt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - CharacterClass mit Klassen (Krieger, Schurke, Magier) und unbewaffnetem Schaden - Equipment-System mit 7 Slots, Seltenheiten und Stats - Inventar-System mit 20 Slots und Gold - LootTable/LootEntry für Gegner-Drops - Character Panel (C) mit Stats und Equipment-Anzeige - Inventory Panel (I) mit Item-Grid und Tooltips - Klassenauswahl-Menü bei Spielstart - Waffenschaden in Equipment-Spalte, unbewaffnet klassenabhängig Co-Authored-By: Claude Opus 4.6 --- character_class.gd | 32 ++++++ character_class.gd.uid | 1 + character_panel.gd | 104 +++++++++++++++++++ character_panel.gd.uid | 1 + character_panel.tscn | 155 +++++++++++++++++++++++++++++ class_selection_menu.gd | 81 +++++++++++++++ class_selection_menu.gd.uid | 1 + class_selection_menu.tscn | 80 +++++++++++++++ classes/mage.tres | 19 ++++ classes/rogue.tres | 19 ++++ classes/warrior.tres | 19 ++++ equipment.gd | 63 ++++++++++++ equipment.gd.uid | 1 + equipment/iron_helm.tres | 18 ++++ equipment/iron_sword.tres | 18 ++++ equipment/leather_chest.tres | 18 ++++ equipment/steel_sword.tres | 18 ++++ equipment/wooden_shield.tres | 18 ++++ heavy_strike.tres | 9 ++ icons/autoattack_icon.svg | 25 +++++ icons/autoattack_icon.svg.import | 43 ++++++++ icons/heavy_strike_icon.svg | 26 +++++ icons/heavy_strike_icon.svg.import | 43 ++++++++ inventory.gd | 77 ++++++++++++++ inventory.gd.uid | 1 + inventory_panel.gd | 128 ++++++++++++++++++++++++ inventory_panel.gd.uid | 1 + inventory_panel.tscn | 63 ++++++++++++ loot_entry.gd | 7 ++ loot_entry.gd.uid | 1 + loot_table.gd | 24 +++++ loot_table.gd.uid | 1 + 32 files changed, 1115 insertions(+) create mode 100644 character_class.gd create mode 100644 character_class.gd.uid create mode 100644 character_panel.gd create mode 100644 character_panel.gd.uid create mode 100644 character_panel.tscn create mode 100644 class_selection_menu.gd create mode 100644 class_selection_menu.gd.uid create mode 100644 class_selection_menu.tscn create mode 100644 classes/mage.tres create mode 100644 classes/rogue.tres create mode 100644 classes/warrior.tres create mode 100644 equipment.gd create mode 100644 equipment.gd.uid create mode 100644 equipment/iron_helm.tres create mode 100644 equipment/iron_sword.tres create mode 100644 equipment/leather_chest.tres create mode 100644 equipment/steel_sword.tres create mode 100644 equipment/wooden_shield.tres create mode 100644 heavy_strike.tres create mode 100644 icons/autoattack_icon.svg create mode 100644 icons/autoattack_icon.svg.import create mode 100644 icons/heavy_strike_icon.svg create mode 100644 icons/heavy_strike_icon.svg.import create mode 100644 inventory.gd create mode 100644 inventory.gd.uid create mode 100644 inventory_panel.gd create mode 100644 inventory_panel.gd.uid create mode 100644 inventory_panel.tscn create mode 100644 loot_entry.gd create mode 100644 loot_entry.gd.uid create mode 100644 loot_table.gd create mode 100644 loot_table.gd.uid diff --git a/character_class.gd b/character_class.gd new file mode 100644 index 0000000..5304900 --- /dev/null +++ b/character_class.gd @@ -0,0 +1,32 @@ +# CharacterClass.gd +# Definiert Charakterklassen mit Grundstats und Main-Stat +extends Resource +class_name CharacterClass + +enum MainStat { STRENGTH, AGILITY, INTELLIGENCE } + +@export var class_name_de: String = "Krieger" +@export var main_stat: MainStat = MainStat.STRENGTH + +# Grund-Stats auf Level 1 +@export var base_strength: int = 10 +@export var base_agility: int = 10 +@export var base_intelligence: int = 10 +@export var base_stamina: int = 10 # Beeinflusst HP + +# Stat-Zuwachs pro Level +@export var strength_per_level: float = 2.0 +@export var agility_per_level: float = 2.0 +@export var intelligence_per_level: float = 2.0 +@export var stamina_per_level: float = 2.0 + +# Unbewaffneter Schaden (klassenabhängig) +@export var unarmed_min_damage: int = 1 +@export var unarmed_max_damage: int = 2 +@export var unarmed_attack_speed: float = 2.0 # Langsamer als mit Waffe + +# HP pro Stamina-Punkt +const HP_PER_STAMINA = 10 + +# Schaden-Skalierung mit Main-Stat +const DAMAGE_PER_MAIN_STAT = 0.5 diff --git a/character_class.gd.uid b/character_class.gd.uid new file mode 100644 index 0000000..075b0d4 --- /dev/null +++ b/character_class.gd.uid @@ -0,0 +1 @@ +uid://ci45xxb5vn857 diff --git a/character_panel.gd b/character_panel.gd new file mode 100644 index 0000000..a9b55f4 --- /dev/null +++ b/character_panel.gd @@ -0,0 +1,104 @@ +# CharacterPanel.gd +# Zeigt Charakterinfos: Klasse, Level, Stats und Equipment +extends CanvasLayer + +var panel_visible = false + +@onready var panel = $Panel +@onready var class_label = $Panel/HBoxContainer/StatsColumn/ClassLabel +@onready var level_label = $Panel/HBoxContainer/StatsColumn/LevelLabel +@onready var stats_container = $Panel/HBoxContainer/StatsColumn/StatsContainer +@onready var str_label = $Panel/HBoxContainer/StatsColumn/StatsContainer/StrLabel +@onready var agi_label = $Panel/HBoxContainer/StatsColumn/StatsContainer/AgiLabel +@onready var int_label = $Panel/HBoxContainer/StatsColumn/StatsContainer/IntLabel +@onready var sta_label = $Panel/HBoxContainer/StatsColumn/StatsContainer/StaLabel +@onready var armor_label = $Panel/HBoxContainer/StatsColumn/StatsContainer/ArmorLabel +@onready var hp_label = $Panel/HBoxContainer/StatsColumn/HPLabel +@onready var damage_label = $Panel/HBoxContainer/EquipmentColumn/DamageLabel +@onready var dps_label = $Panel/HBoxContainer/EquipmentColumn/DPSLabel + +# Equipment Slots +@onready var head_slot = $Panel/HBoxContainer/EquipmentColumn/EquipmentContainer/HeadSlot +@onready var chest_slot = $Panel/HBoxContainer/EquipmentColumn/EquipmentContainer/ChestSlot +@onready var hands_slot = $Panel/HBoxContainer/EquipmentColumn/EquipmentContainer/HandsSlot +@onready var legs_slot = $Panel/HBoxContainer/EquipmentColumn/EquipmentContainer/LegsSlot +@onready var feet_slot = $Panel/HBoxContainer/EquipmentColumn/EquipmentContainer/FeetSlot +@onready var weapon_slot = $Panel/HBoxContainer/EquipmentColumn/WeaponSlot +@onready var offhand_slot = $Panel/HBoxContainer/EquipmentColumn/OffhandSlot + +func _ready(): + panel.visible = false + +func toggle(): + panel_visible = !panel_visible + panel.visible = panel_visible + +func update_stats(player): + if player.character_class: + var main_stat_name = "" + match player.character_class.main_stat: + CharacterClass.MainStat.STRENGTH: + main_stat_name = "STR" + CharacterClass.MainStat.AGILITY: + main_stat_name = "AGI" + CharacterClass.MainStat.INTELLIGENCE: + main_stat_name = "INT" + class_label.text = player.character_class.class_name_de + " (Haupt: " + main_stat_name + ")" + else: + class_label.text = "Keine Klasse" + + level_label.text = "Level " + str(player.level) + " (" + str(player.current_xp) + "/" + str(player.xp_to_next_level) + " XP)" + + str_label.text = "Stärke: " + str(player.strength) + agi_label.text = "Beweglichkeit: " + str(player.agility) + int_label.text = "Intelligenz: " + str(player.intelligence) + sta_label.text = "Ausdauer: " + str(player.stamina) + armor_label.text = "Rüstung: " + str(player.armor) + + hp_label.text = "HP: " + str(player.current_hp) + " / " + str(player.max_hp) + + # Waffen-Stats (jetzt in Equipment-Spalte) + var weapon = player.get_equipped_weapon() + if weapon: + damage_label.text = "Schaden: " + str(weapon.min_damage) + "-" + str(weapon.max_damage) + " (%.1fs)" % weapon.attack_speed + else: + # Unbewaffnet: klassenabhängiger Schaden + if player.character_class: + var min_dmg = player.character_class.unarmed_min_damage + var max_dmg = player.character_class.unarmed_max_damage + var atk_spd = player.character_class.unarmed_attack_speed + damage_label.text = "Unbewaffnet: " + str(min_dmg) + "-" + str(max_dmg) + " (%.1fs)" % atk_spd + else: + damage_label.text = "Unbewaffnet: 1-2 (2.0s)" + dps_label.text = "DPS: %.1f" % player.get_dps() + + # Main-Stat hervorheben + str_label.modulate = Color(1, 1, 1) + agi_label.modulate = Color(1, 1, 1) + int_label.modulate = Color(1, 1, 1) + + if player.character_class: + match player.character_class.main_stat: + CharacterClass.MainStat.STRENGTH: + str_label.modulate = Color(1, 0.8, 0.2) + CharacterClass.MainStat.AGILITY: + agi_label.modulate = Color(1, 0.8, 0.2) + CharacterClass.MainStat.INTELLIGENCE: + int_label.modulate = Color(1, 0.8, 0.2) + + # Equipment aktualisieren + _update_equipment_slot(head_slot, "Kopf", player.equipment[Equipment.Slot.HEAD]) + _update_equipment_slot(chest_slot, "Brust", player.equipment[Equipment.Slot.CHEST]) + _update_equipment_slot(hands_slot, "Hände", player.equipment[Equipment.Slot.HANDS]) + _update_equipment_slot(legs_slot, "Beine", player.equipment[Equipment.Slot.LEGS]) + _update_equipment_slot(feet_slot, "Füße", player.equipment[Equipment.Slot.FEET]) + _update_equipment_slot(weapon_slot, "Waffe", player.equipment[Equipment.Slot.WEAPON]) + _update_equipment_slot(offhand_slot, "Nebenhand", player.equipment[Equipment.Slot.OFFHAND]) + +func _update_equipment_slot(label: Label, slot_name: String, item: Equipment): + if item == null: + label.text = slot_name + ": -" + label.modulate = Color(0.6, 0.6, 0.6) + else: + label.text = slot_name + ": " + item.item_name + label.modulate = Equipment.get_rarity_color(item.rarity) diff --git a/character_panel.gd.uid b/character_panel.gd.uid new file mode 100644 index 0000000..dfdb2bb --- /dev/null +++ b/character_panel.gd.uid @@ -0,0 +1 @@ +uid://7pdlor66gi51 diff --git a/character_panel.tscn b/character_panel.tscn new file mode 100644 index 0000000..41f1646 --- /dev/null +++ b/character_panel.tscn @@ -0,0 +1,155 @@ +[gd_scene format=3 uid="uid://character_panel"] + +[ext_resource type="Script" path="res://character_panel.gd" id="1_panel"] + +[node name="CharacterPanel" type="CanvasLayer"] +script = ExtResource("1_panel") + +[node name="Panel" type="Panel" parent="."] +anchors_preset = 4 +anchor_top = 0.5 +anchor_bottom = 0.5 +offset_left = 20.0 +offset_top = -200.0 +offset_right = 520.0 +offset_bottom = 200.0 +grow_vertical = 2 + +[node name="HBoxContainer" type="HBoxContainer" parent="Panel"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = 15.0 +offset_top = 15.0 +offset_right = -15.0 +offset_bottom = -15.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_constants/separation = 20 + +[node name="StatsColumn" type="VBoxContainer" parent="Panel/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_constants/separation = 6 + +[node name="Title" type="Label" parent="Panel/HBoxContainer/StatsColumn"] +layout_mode = 2 +theme_override_font_sizes/font_size = 20 +text = "Charakter" +horizontal_alignment = 1 + +[node name="HSeparator" type="HSeparator" parent="Panel/HBoxContainer/StatsColumn"] +layout_mode = 2 + +[node name="ClassLabel" type="Label" parent="Panel/HBoxContainer/StatsColumn"] +layout_mode = 2 +theme_override_font_sizes/font_size = 16 +text = "Krieger (Haupt: STR)" + +[node name="LevelLabel" type="Label" parent="Panel/HBoxContainer/StatsColumn"] +layout_mode = 2 +theme_override_font_sizes/font_size = 14 +text = "Level 1 (0/100 XP)" + +[node name="HSeparator2" type="HSeparator" parent="Panel/HBoxContainer/StatsColumn"] +layout_mode = 2 + +[node name="StatsContainer" type="VBoxContainer" parent="Panel/HBoxContainer/StatsColumn"] +layout_mode = 2 +theme_override_constants/separation = 4 + +[node name="StrLabel" type="Label" parent="Panel/HBoxContainer/StatsColumn/StatsContainer"] +layout_mode = 2 +text = "Stärke: 15" + +[node name="AgiLabel" type="Label" parent="Panel/HBoxContainer/StatsColumn/StatsContainer"] +layout_mode = 2 +text = "Beweglichkeit: 8" + +[node name="IntLabel" type="Label" parent="Panel/HBoxContainer/StatsColumn/StatsContainer"] +layout_mode = 2 +text = "Intelligenz: 5" + +[node name="StaLabel" type="Label" parent="Panel/HBoxContainer/StatsColumn/StatsContainer"] +layout_mode = 2 +text = "Ausdauer: 12" + +[node name="ArmorLabel" type="Label" parent="Panel/HBoxContainer/StatsColumn/StatsContainer"] +layout_mode = 2 +text = "Rüstung: 0" + +[node name="HSeparator3" type="HSeparator" parent="Panel/HBoxContainer/StatsColumn"] +layout_mode = 2 + +[node name="HPLabel" type="Label" parent="Panel/HBoxContainer/StatsColumn"] +layout_mode = 2 +theme_override_font_sizes/font_size = 14 +text = "HP: 120 / 120" + +[node name="VSeparator" type="VSeparator" parent="Panel/HBoxContainer"] +layout_mode = 2 + +[node name="EquipmentColumn" type="VBoxContainer" parent="Panel/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_constants/separation = 6 + +[node name="EquipTitle" type="Label" parent="Panel/HBoxContainer/EquipmentColumn"] +layout_mode = 2 +theme_override_font_sizes/font_size = 20 +text = "Ausrüstung" +horizontal_alignment = 1 + +[node name="HSeparator" type="HSeparator" parent="Panel/HBoxContainer/EquipmentColumn"] +layout_mode = 2 + +[node name="EquipmentContainer" type="VBoxContainer" parent="Panel/HBoxContainer/EquipmentColumn"] +layout_mode = 2 +theme_override_constants/separation = 4 + +[node name="HeadSlot" type="Label" parent="Panel/HBoxContainer/EquipmentColumn/EquipmentContainer"] +layout_mode = 2 +text = "Kopf: -" + +[node name="ChestSlot" type="Label" parent="Panel/HBoxContainer/EquipmentColumn/EquipmentContainer"] +layout_mode = 2 +text = "Brust: -" + +[node name="HandsSlot" type="Label" parent="Panel/HBoxContainer/EquipmentColumn/EquipmentContainer"] +layout_mode = 2 +text = "Hände: -" + +[node name="LegsSlot" type="Label" parent="Panel/HBoxContainer/EquipmentColumn/EquipmentContainer"] +layout_mode = 2 +text = "Beine: -" + +[node name="FeetSlot" type="Label" parent="Panel/HBoxContainer/EquipmentColumn/EquipmentContainer"] +layout_mode = 2 +text = "Füße: -" + +[node name="HSeparator2" type="HSeparator" parent="Panel/HBoxContainer/EquipmentColumn"] +layout_mode = 2 + +[node name="WeaponSlot" type="Label" parent="Panel/HBoxContainer/EquipmentColumn"] +layout_mode = 2 +theme_override_font_sizes/font_size = 14 +text = "Waffe: -" + +[node name="OffhandSlot" type="Label" parent="Panel/HBoxContainer/EquipmentColumn"] +layout_mode = 2 +theme_override_font_sizes/font_size = 14 +text = "Nebenhand: -" + +[node name="HSeparator3" type="HSeparator" parent="Panel/HBoxContainer/EquipmentColumn"] +layout_mode = 2 + +[node name="DamageLabel" type="Label" parent="Panel/HBoxContainer/EquipmentColumn"] +layout_mode = 2 +theme_override_font_sizes/font_size = 14 +text = "Schaden: 3-6 (1.5s)" + +[node name="DPSLabel" type="Label" parent="Panel/HBoxContainer/EquipmentColumn"] +layout_mode = 2 +theme_override_font_sizes/font_size = 14 +text = "DPS: 5.0" diff --git a/class_selection_menu.gd b/class_selection_menu.gd new file mode 100644 index 0000000..b9e6272 --- /dev/null +++ b/class_selection_menu.gd @@ -0,0 +1,81 @@ +# ClassSelectionMenu.gd +# Menü zur Auswahl der Charakterklasse beim Spielstart +extends CanvasLayer + +signal class_selected(character_class: CharacterClass) + +const WARRIOR_CLASS = preload("res://classes/warrior.tres") +const ROGUE_CLASS = preload("res://classes/rogue.tres") +const MAGE_CLASS = preload("res://classes/mage.tres") + +var selected_class: CharacterClass = null + +@onready var panel = $Panel +@onready var warrior_btn = $Panel/VBoxContainer/ClassButtons/WarriorBtn +@onready var rogue_btn = $Panel/VBoxContainer/ClassButtons/RogueBtn +@onready var mage_btn = $Panel/VBoxContainer/ClassButtons/MageBtn +@onready var start_btn = $Panel/VBoxContainer/StartBtn +@onready var class_info = $Panel/VBoxContainer/ClassInfo +@onready var stats_label = $Panel/VBoxContainer/StatsLabel + +func _ready(): + # Spiel pausieren während Menü offen + get_tree().paused = true + process_mode = Node.PROCESS_MODE_ALWAYS + + # Buttons verbinden + warrior_btn.pressed.connect(_on_warrior_selected) + rogue_btn.pressed.connect(_on_rogue_selected) + mage_btn.pressed.connect(_on_mage_selected) + start_btn.pressed.connect(_on_start_pressed) + + # Start-Button deaktiviert bis Klasse gewählt + start_btn.disabled = true + class_info.text = "Wähle eine Klasse!" + stats_label.text = "" + +func _on_warrior_selected(): + selected_class = WARRIOR_CLASS + _update_selection("Krieger", WARRIOR_CLASS) + _highlight_button(warrior_btn) + +func _on_rogue_selected(): + selected_class = ROGUE_CLASS + _update_selection("Schurke", ROGUE_CLASS) + _highlight_button(rogue_btn) + +func _on_mage_selected(): + selected_class = MAGE_CLASS + _update_selection("Magier", MAGE_CLASS) + _highlight_button(mage_btn) + +func _update_selection(cls_name: String, char_class: CharacterClass): + var main_stat_name = "" + match char_class.main_stat: + CharacterClass.MainStat.STRENGTH: + main_stat_name = "Stärke" + CharacterClass.MainStat.AGILITY: + main_stat_name = "Beweglichkeit" + CharacterClass.MainStat.INTELLIGENCE: + main_stat_name = "Intelligenz" + + class_info.text = cls_name + " - Haupt-Stat: " + main_stat_name + stats_label.text = "STR: " + str(char_class.base_strength) + " AGI: " + str(char_class.base_agility) + " INT: " + str(char_class.base_intelligence) + " STA: " + str(char_class.base_stamina) + start_btn.disabled = false + +func _highlight_button(active_btn: Button): + # Alle Buttons zurücksetzen + warrior_btn.modulate = Color(1, 1, 1) + rogue_btn.modulate = Color(1, 1, 1) + mage_btn.modulate = Color(1, 1, 1) + # Aktiven Button hervorheben + active_btn.modulate = Color(1, 0.8, 0.2) + +func _on_start_pressed(): + if selected_class == null: + return + + # Signal senden und Menü schließen + class_selected.emit(selected_class) + get_tree().paused = false + queue_free() diff --git a/class_selection_menu.gd.uid b/class_selection_menu.gd.uid new file mode 100644 index 0000000..493bc1f --- /dev/null +++ b/class_selection_menu.gd.uid @@ -0,0 +1 @@ +uid://db8m2uw42hqfc diff --git a/class_selection_menu.tscn b/class_selection_menu.tscn new file mode 100644 index 0000000..337d396 --- /dev/null +++ b/class_selection_menu.tscn @@ -0,0 +1,80 @@ +[gd_scene format=3 uid="uid://class_selection_menu"] + +[ext_resource type="Script" path="res://class_selection_menu.gd" id="1_menu"] + +[node name="ClassSelectionMenu" type="CanvasLayer"] +script = ExtResource("1_menu") + +[node name="Panel" type="Panel" parent="."] +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -200.0 +offset_top = -180.0 +offset_right = 200.0 +offset_bottom = 180.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="VBoxContainer" type="VBoxContainer" parent="Panel"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = 20.0 +offset_top = 20.0 +offset_right = -20.0 +offset_bottom = -20.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_constants/separation = 15 + +[node name="Title" type="Label" parent="Panel/VBoxContainer"] +layout_mode = 2 +theme_override_font_sizes/font_size = 28 +text = "Wähle deine Klasse" +horizontal_alignment = 1 + +[node name="ClassButtons" type="HBoxContainer" parent="Panel/VBoxContainer"] +layout_mode = 2 +theme_override_constants/separation = 20 +alignment = 1 + +[node name="WarriorBtn" type="Button" parent="Panel/VBoxContainer/ClassButtons"] +custom_minimum_size = Vector2(100, 80) +layout_mode = 2 +theme_override_font_sizes/font_size = 16 +text = "Krieger" + +[node name="RogueBtn" type="Button" parent="Panel/VBoxContainer/ClassButtons"] +custom_minimum_size = Vector2(100, 80) +layout_mode = 2 +theme_override_font_sizes/font_size = 16 +text = "Schurke" + +[node name="MageBtn" type="Button" parent="Panel/VBoxContainer/ClassButtons"] +custom_minimum_size = Vector2(100, 80) +layout_mode = 2 +theme_override_font_sizes/font_size = 16 +text = "Magier" + +[node name="ClassInfo" type="Label" parent="Panel/VBoxContainer"] +layout_mode = 2 +theme_override_font_sizes/font_size = 18 +text = "Wähle eine Klasse!" +horizontal_alignment = 1 + +[node name="StatsLabel" type="Label" parent="Panel/VBoxContainer"] +layout_mode = 2 +theme_override_font_sizes/font_size = 14 +text = "" +horizontal_alignment = 1 + +[node name="StartBtn" type="Button" parent="Panel/VBoxContainer"] +custom_minimum_size = Vector2(0, 50) +layout_mode = 2 +theme_override_font_sizes/font_size = 20 +disabled = true +text = "Spiel starten" diff --git a/classes/mage.tres b/classes/mage.tres new file mode 100644 index 0000000..943b96d --- /dev/null +++ b/classes/mage.tres @@ -0,0 +1,19 @@ +[gd_resource type="Resource" script_class="CharacterClass" format=3] + +[ext_resource type="Script" path="res://character_class.gd" id="1"] + +[resource] +script = ExtResource("1") +class_name_de = "Magier" +main_stat = 2 +base_strength = 5 +base_agility = 8 +base_intelligence = 15 +base_stamina = 8 +strength_per_level = 1.0 +agility_per_level = 1.5 +intelligence_per_level = 3.0 +stamina_per_level = 1.5 +unarmed_min_damage = 1 +unarmed_max_damage = 2 +unarmed_attack_speed = 2.0 diff --git a/classes/rogue.tres b/classes/rogue.tres new file mode 100644 index 0000000..ea63f98 --- /dev/null +++ b/classes/rogue.tres @@ -0,0 +1,19 @@ +[gd_resource type="Resource" script_class="CharacterClass" format=3] + +[ext_resource type="Script" path="res://character_class.gd" id="1"] + +[resource] +script = ExtResource("1") +class_name_de = "Schurke" +main_stat = 1 +base_strength = 8 +base_agility = 15 +base_intelligence = 7 +base_stamina = 10 +strength_per_level = 1.5 +agility_per_level = 3.0 +intelligence_per_level = 1.5 +stamina_per_level = 2.0 +unarmed_min_damage = 1 +unarmed_max_damage = 3 +unarmed_attack_speed = 1.5 diff --git a/classes/warrior.tres b/classes/warrior.tres new file mode 100644 index 0000000..690eb51 --- /dev/null +++ b/classes/warrior.tres @@ -0,0 +1,19 @@ +[gd_resource type="Resource" script_class="CharacterClass" format=3] + +[ext_resource type="Script" path="res://character_class.gd" id="1"] + +[resource] +script = ExtResource("1") +class_name_de = "Krieger" +main_stat = 0 +base_strength = 15 +base_agility = 8 +base_intelligence = 5 +base_stamina = 12 +strength_per_level = 3.0 +agility_per_level = 1.5 +intelligence_per_level = 1.0 +stamina_per_level = 2.5 +unarmed_min_damage = 2 +unarmed_max_damage = 4 +unarmed_attack_speed = 1.8 diff --git a/equipment.gd b/equipment.gd new file mode 100644 index 0000000..bc40061 --- /dev/null +++ b/equipment.gd @@ -0,0 +1,63 @@ +# Equipment.gd +# Resource für Ausrüstungsgegenstände +extends Resource +class_name Equipment + +enum Slot { + HEAD, # Helm + CHEST, # Brustpanzer + HANDS, # Handschuhe + LEGS, # Beinschienen + FEET, # Stiefel + WEAPON, # Waffe + OFFHAND # Nebenhand (Schild, etc.) +} + +enum Rarity { + COMMON, # Weiß + UNCOMMON, # Grün + RARE, # Blau + EPIC # Lila +} + +@export var item_name: String = "Unbekannt" +@export var slot: Slot = Slot.WEAPON +@export var rarity: Rarity = Rarity.COMMON + +# Stats die das Item gibt +@export var armor: int = 0 +@export var strength: int = 0 +@export var agility: int = 0 +@export var intelligence: int = 0 +@export var stamina: int = 0 +@export var haste: float = 0.0 # Angriffsgeschwindigkeit (0.1 = 10% schneller) + +# Nur für Waffen +@export var min_damage: int = 0 +@export var max_damage: int = 0 +@export var attack_speed: float = 1.5 # Sekunden zwischen Angriffen +@export var weapon_range: float = 3.0 + +# Icon für UI +@export var icon: Texture2D + +# Slot-Namen für Anzeige +static func get_slot_name(s: Slot) -> String: + match s: + Slot.HEAD: return "Kopf" + Slot.CHEST: return "Brust" + Slot.HANDS: return "Hände" + Slot.LEGS: return "Beine" + Slot.FEET: return "Füße" + Slot.WEAPON: return "Waffe" + Slot.OFFHAND: return "Nebenhand" + return "Unbekannt" + +# Seltenheitsfarbe +static func get_rarity_color(r: Rarity) -> Color: + match r: + Rarity.COMMON: return Color(1, 1, 1) # Weiß + Rarity.UNCOMMON: return Color(0.2, 0.8, 0.2) # Grün + Rarity.RARE: return Color(0.3, 0.5, 1.0) # Blau + Rarity.EPIC: return Color(0.7, 0.3, 0.9) # Lila + return Color(1, 1, 1) diff --git a/equipment.gd.uid b/equipment.gd.uid new file mode 100644 index 0000000..361aeaf --- /dev/null +++ b/equipment.gd.uid @@ -0,0 +1 @@ +uid://re0xiie1udfq diff --git a/equipment/iron_helm.tres b/equipment/iron_helm.tres new file mode 100644 index 0000000..f1c45d2 --- /dev/null +++ b/equipment/iron_helm.tres @@ -0,0 +1,18 @@ +[gd_resource type="Resource" script_class="Equipment" load_steps=2 format=3] + +[ext_resource type="Script" path="res://equipment.gd" id="1_equipment"] + +[resource] +script = ExtResource("1_equipment") +item_name = "Eisenhelm" +slot = 0 +rarity = 0 +armor = 5 +strength = 1 +agility = 0 +intelligence = 0 +stamina = 1 +min_damage = 0 +max_damage = 0 +attack_speed = 1.5 +weapon_range = 3.0 diff --git a/equipment/iron_sword.tres b/equipment/iron_sword.tres new file mode 100644 index 0000000..ee231ee --- /dev/null +++ b/equipment/iron_sword.tres @@ -0,0 +1,18 @@ +[gd_resource type="Resource" script_class="Equipment" load_steps=2 format=3] + +[ext_resource type="Script" path="res://equipment.gd" id="1_equipment"] + +[resource] +script = ExtResource("1_equipment") +item_name = "Eisenschwert" +slot = 5 +rarity = 0 +armor = 0 +strength = 2 +agility = 0 +intelligence = 0 +stamina = 0 +min_damage = 3 +max_damage = 6 +attack_speed = 1.5 +weapon_range = 3.0 diff --git a/equipment/leather_chest.tres b/equipment/leather_chest.tres new file mode 100644 index 0000000..ef2a30f --- /dev/null +++ b/equipment/leather_chest.tres @@ -0,0 +1,18 @@ +[gd_resource type="Resource" script_class="Equipment" load_steps=2 format=3] + +[ext_resource type="Script" path="res://equipment.gd" id="1_equipment"] + +[resource] +script = ExtResource("1_equipment") +item_name = "Lederrüstung" +slot = 1 +rarity = 0 +armor = 8 +strength = 0 +agility = 1 +intelligence = 0 +stamina = 2 +min_damage = 0 +max_damage = 0 +attack_speed = 1.5 +weapon_range = 3.0 diff --git a/equipment/steel_sword.tres b/equipment/steel_sword.tres new file mode 100644 index 0000000..b1b01e2 --- /dev/null +++ b/equipment/steel_sword.tres @@ -0,0 +1,18 @@ +[gd_resource type="Resource" script_class="Equipment" load_steps=2 format=3] + +[ext_resource type="Script" path="res://equipment.gd" id="1_equipment"] + +[resource] +script = ExtResource("1_equipment") +item_name = "Stahlschwert" +slot = 5 +rarity = 1 +armor = 0 +strength = 4 +agility = 0 +intelligence = 0 +stamina = 0 +min_damage = 5 +max_damage = 9 +attack_speed = 1.4 +weapon_range = 3.0 diff --git a/equipment/wooden_shield.tres b/equipment/wooden_shield.tres new file mode 100644 index 0000000..10e0186 --- /dev/null +++ b/equipment/wooden_shield.tres @@ -0,0 +1,18 @@ +[gd_resource type="Resource" script_class="Equipment" load_steps=2 format=3] + +[ext_resource type="Script" path="res://equipment.gd" id="1_equipment"] + +[resource] +script = ExtResource("1_equipment") +item_name = "Holzschild" +slot = 6 +rarity = 0 +armor = 6 +strength = 0 +agility = 0 +intelligence = 0 +stamina = 2 +min_damage = 0 +max_damage = 0 +attack_speed = 1.5 +weapon_range = 3.0 diff --git a/heavy_strike.tres b/heavy_strike.tres new file mode 100644 index 0000000..317d522 --- /dev/null +++ b/heavy_strike.tres @@ -0,0 +1,9 @@ +[gd_resource type="Resource" script_class="Attack" load_steps=2 format=3] + +[ext_resource type="Script" path="res://resources/attack.gd" id="1"] + +[resource] +script = ExtResource("1") +name = "Heavy Strike" +damage_type = 0 +icon = null diff --git a/icons/autoattack_icon.svg b/icons/autoattack_icon.svg new file mode 100644 index 0000000..dc7b97d --- /dev/null +++ b/icons/autoattack_icon.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons/autoattack_icon.svg.import b/icons/autoattack_icon.svg.import new file mode 100644 index 0000000..0549514 --- /dev/null +++ b/icons/autoattack_icon.svg.import @@ -0,0 +1,43 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bkujmu8r6370p" +path="res://.godot/imported/autoattack_icon.svg-8c48165bd13a879d5f6cdbc811e19882.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icons/autoattack_icon.svg" +dest_files=["res://.godot/imported/autoattack_icon.svg-8c48165bd13a879d5f6cdbc811e19882.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/icons/heavy_strike_icon.svg b/icons/heavy_strike_icon.svg new file mode 100644 index 0000000..ce005ff --- /dev/null +++ b/icons/heavy_strike_icon.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons/heavy_strike_icon.svg.import b/icons/heavy_strike_icon.svg.import new file mode 100644 index 0000000..790abfa --- /dev/null +++ b/icons/heavy_strike_icon.svg.import @@ -0,0 +1,43 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dqkaowq4cvtbl" +path="res://.godot/imported/heavy_strike_icon.svg-ae86ca74d5a61e67598cea5812ee34ac.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icons/heavy_strike_icon.svg" +dest_files=["res://.godot/imported/heavy_strike_icon.svg-ae86ca74d5a61e67598cea5812ee34ac.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/inventory.gd b/inventory.gd new file mode 100644 index 0000000..17a292e --- /dev/null +++ b/inventory.gd @@ -0,0 +1,77 @@ +# Inventory.gd +# Verwaltet das Spieler-Inventar mit Items und Gold +extends Resource +class_name Inventory + +signal inventory_changed +signal gold_changed(new_amount: int) + +const MAX_SLOTS = 20 # Maximale Inventarplätze + +var items: Array[Equipment] = [] +var gold: int = 0 + +func _init(): + items = [] + gold = 0 + +# Item hinzufügen - gibt true zurück wenn erfolgreich +func add_item(item: Equipment) -> bool: + if items.size() >= MAX_SLOTS: + print("Inventar voll!") + return false + items.append(item) + inventory_changed.emit() + print("Item erhalten: ", item.item_name) + return true + +# Item an Index entfernen +func remove_item_at(index: int) -> Equipment: + if index < 0 or index >= items.size(): + return null + var item = items[index] + items.remove_at(index) + inventory_changed.emit() + return item + +# Item direkt entfernen +func remove_item(item: Equipment) -> bool: + var index = items.find(item) + if index == -1: + return false + items.remove_at(index) + inventory_changed.emit() + return true + +# Gold hinzufügen +func add_gold(amount: int): + gold += amount + gold_changed.emit(gold) + print("+", amount, " Gold (Gesamt: ", gold, ")") + +# Gold ausgeben - gibt true zurück wenn genug vorhanden +func spend_gold(amount: int) -> bool: + if gold < amount: + print("Nicht genug Gold!") + return false + gold -= amount + gold_changed.emit(gold) + return true + +# Prüfen ob Inventar voll ist +func is_full() -> bool: + return items.size() >= MAX_SLOTS + +# Anzahl freier Slots +func free_slots() -> int: + return MAX_SLOTS - items.size() + +# Item an Index holen +func get_item(index: int) -> Equipment: + if index < 0 or index >= items.size(): + return null + return items[index] + +# Anzahl Items +func item_count() -> int: + return items.size() diff --git a/inventory.gd.uid b/inventory.gd.uid new file mode 100644 index 0000000..9e173d9 --- /dev/null +++ b/inventory.gd.uid @@ -0,0 +1 @@ +uid://b7x1jr36w0bfg diff --git a/inventory_panel.gd b/inventory_panel.gd new file mode 100644 index 0000000..8285374 --- /dev/null +++ b/inventory_panel.gd @@ -0,0 +1,128 @@ +# InventoryPanel.gd +# UI für das Spieler-Inventar +extends CanvasLayer + +signal item_selected(item: Equipment, index: int) + +var panel_visible = false +var player = null + +@onready var panel = $Panel +@onready var gold_label = $Panel/VBoxContainer/Header/GoldLabel +@onready var item_grid = $Panel/VBoxContainer/ScrollContainer/ItemGrid + +const SLOT_SIZE = 50 + +func _ready(): + panel.visible = false + +func setup(p): + player = p + if player and player.inventory: + player.inventory.inventory_changed.connect(_on_inventory_changed) + player.inventory.gold_changed.connect(_on_gold_changed) + _refresh_inventory() + +func toggle(): + panel_visible = !panel_visible + panel.visible = panel_visible + if panel_visible: + _refresh_inventory() + +func _on_inventory_changed(): + _refresh_inventory() + +func _on_gold_changed(new_amount: int): + gold_label.text = str(new_amount) + " Gold" + +func _refresh_inventory(): + if player == null or player.inventory == null: + return + + # Gold aktualisieren + gold_label.text = str(player.inventory.gold) + " Gold" + + # Alte Slots entfernen + for child in item_grid.get_children(): + child.queue_free() + + # Slots erstellen (immer MAX_SLOTS anzeigen) + for i in range(Inventory.MAX_SLOTS): + var slot = _create_slot(i) + item_grid.add_child(slot) + +func _create_slot(index: int) -> Panel: + var slot = Panel.new() + slot.custom_minimum_size = Vector2(SLOT_SIZE, SLOT_SIZE) + + # Slot-Hintergrund stylen + var style = StyleBoxFlat.new() + style.bg_color = Color(0.15, 0.15, 0.15) + style.border_color = Color(0.3, 0.3, 0.3) + style.set_border_width_all(1) + slot.add_theme_stylebox_override("panel", style) + + # Item vorhanden? + if player.inventory and index < player.inventory.item_count(): + var item = player.inventory.get_item(index) + if item: + # Item-Name Label + var label = Label.new() + label.text = item.item_name.substr(0, 3) # Erste 3 Buchstaben + label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER + label.add_theme_font_size_override("font_size", 10) + label.modulate = Equipment.get_rarity_color(item.rarity) + label.anchors_preset = Control.PRESET_FULL_RECT + slot.add_child(label) + + # Rahmen in Seltenheitsfarbe + style.border_color = Equipment.get_rarity_color(item.rarity) + style.set_border_width_all(2) + + # Klick-Handler + slot.gui_input.connect(_on_slot_clicked.bind(index, item)) + + # Tooltip + slot.tooltip_text = _get_item_tooltip(item) + + return slot + +func _on_slot_clicked(event: InputEvent, index: int, item: Equipment): + if event is InputEventMouseButton and event.pressed: + if event.button_index == MOUSE_BUTTON_LEFT: + # Linksklick: Item auswählen/anlegen + item_selected.emit(item, index) + elif event.button_index == MOUSE_BUTTON_RIGHT: + # Rechtsklick: Item direkt anlegen + if player: + var old_item = player.equip_item(item) + player.inventory.remove_item(item) + if old_item: + player.inventory.add_item(old_item) + _refresh_inventory() + +func _get_item_tooltip(item: Equipment) -> String: + var tooltip = item.item_name + "\n" + tooltip += Equipment.get_slot_name(item.slot) + "\n" + tooltip += "---\n" + + if item.slot == Equipment.Slot.WEAPON: + tooltip += "Schaden: " + str(item.min_damage) + "-" + str(item.max_damage) + "\n" + tooltip += "Tempo: " + str(item.attack_speed) + "s\n" + + if item.armor > 0: + tooltip += "Rüstung: +" + str(item.armor) + "\n" + if item.strength > 0: + tooltip += "Stärke: +" + str(item.strength) + "\n" + if item.agility > 0: + tooltip += "Beweglichkeit: +" + str(item.agility) + "\n" + if item.intelligence > 0: + tooltip += "Intelligenz: +" + str(item.intelligence) + "\n" + if item.stamina > 0: + tooltip += "Ausdauer: +" + str(item.stamina) + "\n" + if item.haste > 0: + tooltip += "Tempo: +" + str(int(item.haste * 100)) + "%\n" + + tooltip += "\n[Rechtsklick zum Anlegen]" + return tooltip diff --git a/inventory_panel.gd.uid b/inventory_panel.gd.uid new file mode 100644 index 0000000..2be1619 --- /dev/null +++ b/inventory_panel.gd.uid @@ -0,0 +1 @@ +uid://bjmcu0tyxqxmd diff --git a/inventory_panel.tscn b/inventory_panel.tscn new file mode 100644 index 0000000..b31478d --- /dev/null +++ b/inventory_panel.tscn @@ -0,0 +1,63 @@ +[gd_scene format=3 uid="uid://inventory_panel"] + +[ext_resource type="Script" path="res://inventory_panel.gd" id="1_panel"] + +[node name="InventoryPanel" type="CanvasLayer"] +script = ExtResource("1_panel") + +[node name="Panel" type="Panel" parent="."] +anchors_preset = 6 +anchor_left = 1.0 +anchor_top = 0.5 +anchor_right = 1.0 +anchor_bottom = 0.5 +offset_left = -320.0 +offset_top = -250.0 +offset_right = -20.0 +offset_bottom = 250.0 +grow_horizontal = 0 +grow_vertical = 2 + +[node name="VBoxContainer" type="VBoxContainer" parent="Panel"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = 10.0 +offset_top = 10.0 +offset_right = -10.0 +offset_bottom = -10.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_constants/separation = 8 + +[node name="Header" type="HBoxContainer" parent="Panel/VBoxContainer"] +layout_mode = 2 + +[node name="Title" type="Label" parent="Panel/VBoxContainer/Header"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_font_sizes/font_size = 20 +text = "Inventar" + +[node name="GoldLabel" type="Label" parent="Panel/VBoxContainer/Header"] +layout_mode = 2 +theme_override_font_sizes/font_size = 16 +theme_override_colors/font_color = Color(1, 0.85, 0, 1) +text = "0 Gold" +horizontal_alignment = 2 + +[node name="HSeparator" type="HSeparator" parent="Panel/VBoxContainer"] +layout_mode = 2 + +[node name="ScrollContainer" type="ScrollContainer" parent="Panel/VBoxContainer"] +layout_mode = 2 +size_flags_vertical = 3 + +[node name="ItemGrid" type="GridContainer" parent="Panel/VBoxContainer/ScrollContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_constants/h_separation = 5 +theme_override_constants/v_separation = 5 +columns = 5 diff --git a/loot_entry.gd b/loot_entry.gd new file mode 100644 index 0000000..c84dbaa --- /dev/null +++ b/loot_entry.gd @@ -0,0 +1,7 @@ +# LootEntry.gd +# Ein einzelner Eintrag in einer LootTable +extends Resource +class_name LootEntry + +@export var item: Equipment +@export_range(0.0, 1.0) var drop_chance: float = 0.1 # 10% Standard diff --git a/loot_entry.gd.uid b/loot_entry.gd.uid new file mode 100644 index 0000000..1b7af3a --- /dev/null +++ b/loot_entry.gd.uid @@ -0,0 +1 @@ +uid://cx2w8nkuoylv5 diff --git a/loot_table.gd b/loot_table.gd new file mode 100644 index 0000000..ae688e8 --- /dev/null +++ b/loot_table.gd @@ -0,0 +1,24 @@ +# LootTable.gd +# Definiert mögliche Drops eines Gegners +extends Resource +class_name LootTable + +# Gold-Drop +@export var min_gold: int = 1 +@export var max_gold: int = 5 + +# Item-Drops mit Wahrscheinlichkeiten +@export var possible_drops: Array[LootEntry] = [] + +# Generiert Loot basierend auf Tabelle +func generate_loot() -> Dictionary: + var result = { + "gold": randi_range(min_gold, max_gold), + "items": [] + } + + for entry in possible_drops: + if randf() <= entry.drop_chance: + result["items"].append(entry.item) + + return result diff --git a/loot_table.gd.uid b/loot_table.gd.uid new file mode 100644 index 0000000..c5e4b89 --- /dev/null +++ b/loot_table.gd.uid @@ -0,0 +1 @@ +uid://dej1tpamsi71r