Inventar, Equipment, Klassensystem und Waffenschaden hinzugefügt
- 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 <noreply@anthropic.com>
This commit is contained in:
parent
1616431d1c
commit
9ab6deddff
32 changed files with 1115 additions and 0 deletions
32
character_class.gd
Normal file
32
character_class.gd
Normal file
|
|
@ -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
|
||||||
1
character_class.gd.uid
Normal file
1
character_class.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
uid://ci45xxb5vn857
|
||||||
104
character_panel.gd
Normal file
104
character_panel.gd
Normal file
|
|
@ -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)
|
||||||
1
character_panel.gd.uid
Normal file
1
character_panel.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
uid://7pdlor66gi51
|
||||||
155
character_panel.tscn
Normal file
155
character_panel.tscn
Normal file
|
|
@ -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"
|
||||||
81
class_selection_menu.gd
Normal file
81
class_selection_menu.gd
Normal file
|
|
@ -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()
|
||||||
1
class_selection_menu.gd.uid
Normal file
1
class_selection_menu.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
uid://db8m2uw42hqfc
|
||||||
80
class_selection_menu.tscn
Normal file
80
class_selection_menu.tscn
Normal file
|
|
@ -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"
|
||||||
19
classes/mage.tres
Normal file
19
classes/mage.tres
Normal file
|
|
@ -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
|
||||||
19
classes/rogue.tres
Normal file
19
classes/rogue.tres
Normal file
|
|
@ -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
|
||||||
19
classes/warrior.tres
Normal file
19
classes/warrior.tres
Normal file
|
|
@ -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
|
||||||
63
equipment.gd
Normal file
63
equipment.gd
Normal file
|
|
@ -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)
|
||||||
1
equipment.gd.uid
Normal file
1
equipment.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
uid://re0xiie1udfq
|
||||||
18
equipment/iron_helm.tres
Normal file
18
equipment/iron_helm.tres
Normal file
|
|
@ -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
|
||||||
18
equipment/iron_sword.tres
Normal file
18
equipment/iron_sword.tres
Normal file
|
|
@ -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
|
||||||
18
equipment/leather_chest.tres
Normal file
18
equipment/leather_chest.tres
Normal file
|
|
@ -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
|
||||||
18
equipment/steel_sword.tres
Normal file
18
equipment/steel_sword.tres
Normal file
|
|
@ -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
|
||||||
18
equipment/wooden_shield.tres
Normal file
18
equipment/wooden_shield.tres
Normal file
|
|
@ -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
|
||||||
9
heavy_strike.tres
Normal file
9
heavy_strike.tres
Normal file
|
|
@ -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
|
||||||
25
icons/autoattack_icon.svg
Normal file
25
icons/autoattack_icon.svg
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
<svg width="64" height="64" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<!-- Autoattack Icon - Faust -->
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="fist" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||||
|
<stop offset="0%" style="stop-color:#ffd4a3;stop-opacity:1" />
|
||||||
|
<stop offset="100%" style="stop-color:#d4a574;stop-opacity:1" />
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<!-- Circular arrow (autoattack symbol) -->
|
||||||
|
<path d="M 45 15 A 12 12 0 1 1 33 3" stroke="#4CAF50" stroke-width="3" fill="none" stroke-linecap="round"/>
|
||||||
|
<polygon points="45,15 50,10 50,20" fill="#4CAF50"/>
|
||||||
|
|
||||||
|
<!-- Fist palm -->
|
||||||
|
<rect x="18" y="35" width="16" height="20" rx="3" fill="url(#fist)" stroke="#b8945f" stroke-width="2"/>
|
||||||
|
|
||||||
|
<!-- Thumb -->
|
||||||
|
<ellipse cx="35" cy="45" rx="4" ry="8" fill="url(#fist)" stroke="#b8945f" stroke-width="2"/>
|
||||||
|
|
||||||
|
<!-- Fingers -->
|
||||||
|
<rect x="18" y="28" width="3" height="10" rx="1.5" fill="url(#fist)" stroke="#b8945f" stroke-width="1"/>
|
||||||
|
<rect x="22" y="25" width="3" height="12" rx="1.5" fill="url(#fist)" stroke="#b8945f" stroke-width="1"/>
|
||||||
|
<rect x="26" y="26" width="3" height="11" rx="1.5" fill="url(#fist)" stroke="#b8945f" stroke-width="1"/>
|
||||||
|
<rect x="30" y="28" width="3" height="9" rx="1.5" fill="url(#fist)" stroke="#b8945f" stroke-width="1"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.2 KiB |
43
icons/autoattack_icon.svg.import
Normal file
43
icons/autoattack_icon.svg.import
Normal file
|
|
@ -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
|
||||||
26
icons/heavy_strike_icon.svg
Normal file
26
icons/heavy_strike_icon.svg
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
<svg width="64" height="64" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<!-- Heavy Strike Icon - Schwert mit Schlageffekt -->
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="blade" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||||
|
<stop offset="0%" style="stop-color:#e0e0e0;stop-opacity:1" />
|
||||||
|
<stop offset="100%" style="stop-color:#808080;stop-opacity:1" />
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<!-- Impact lines (red) -->
|
||||||
|
<path d="M 10 15 L 20 10" stroke="#ff4444" stroke-width="3" stroke-linecap="round"/>
|
||||||
|
<path d="M 8 25 L 18 20" stroke="#ff4444" stroke-width="3" stroke-linecap="round"/>
|
||||||
|
<path d="M 12 35 L 22 30" stroke="#ff4444" stroke-width="3" stroke-linecap="round"/>
|
||||||
|
|
||||||
|
<!-- Sword blade -->
|
||||||
|
<polygon points="25,10 35,10 40,50 20,50" fill="url(#blade)" stroke="#404040" stroke-width="2"/>
|
||||||
|
|
||||||
|
<!-- Sword guard -->
|
||||||
|
<rect x="15" y="48" width="30" height="4" fill="#8B4513" stroke="#654321" stroke-width="1"/>
|
||||||
|
|
||||||
|
<!-- Sword handle -->
|
||||||
|
<rect x="25" y="50" width="10" height="10" fill="#654321" stroke="#3d2817" stroke-width="1"/>
|
||||||
|
|
||||||
|
<!-- Pommel -->
|
||||||
|
<circle cx="30" cy="62" r="4" fill="#DAA520" stroke="#B8860B" stroke-width="1"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
43
icons/heavy_strike_icon.svg.import
Normal file
43
icons/heavy_strike_icon.svg.import
Normal file
|
|
@ -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
|
||||||
77
inventory.gd
Normal file
77
inventory.gd
Normal file
|
|
@ -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()
|
||||||
1
inventory.gd.uid
Normal file
1
inventory.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
uid://b7x1jr36w0bfg
|
||||||
128
inventory_panel.gd
Normal file
128
inventory_panel.gd
Normal file
|
|
@ -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
|
||||||
1
inventory_panel.gd.uid
Normal file
1
inventory_panel.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
uid://bjmcu0tyxqxmd
|
||||||
63
inventory_panel.tscn
Normal file
63
inventory_panel.tscn
Normal file
|
|
@ -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
|
||||||
7
loot_entry.gd
Normal file
7
loot_entry.gd
Normal file
|
|
@ -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
|
||||||
1
loot_entry.gd.uid
Normal file
1
loot_entry.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
uid://cx2w8nkuoylv5
|
||||||
24
loot_table.gd
Normal file
24
loot_table.gd
Normal file
|
|
@ -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
|
||||||
1
loot_table.gd.uid
Normal file
1
loot_table.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
uid://dej1tpamsi71r
|
||||||
Loading…
Add table
Reference in a new issue