From c3f49c885a5385ffa0489b5cb8b0afe2179e86f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torjus=20H=C3=A5kestad?= Date: Wed, 20 Aug 2025 01:59:45 +0200 Subject: [PATCH] game: add levelups --- global_const.gd | 94 +++++++++++++++++++++++ scenes/attacks/attack_sword.gd | 1 + scenes/managers/ui/level_up_choice.gd | 34 ++++++++ scenes/managers/ui/level_up_choice.gd.uid | 1 + scenes/managers/ui/level_up_choice.tscn | 46 +++++++++++ scenes/managers/ui/level_up_ui.gd | 15 ++++ scenes/managers/ui/level_up_ui.gd.uid | 1 + scenes/managers/ui/level_up_ui.tscn | 35 +++++++++ scenes/managers/ui/main_ui.gd | 2 + scenes/managers/ui/main_ui.tscn | 5 +- scenes/player.gd | 27 ++++++- scenes/player_stats.gd | 2 +- scenes/player_stats_modifier.gd | 5 ++ 13 files changed, 265 insertions(+), 3 deletions(-) create mode 100644 scenes/managers/ui/level_up_choice.gd create mode 100644 scenes/managers/ui/level_up_choice.gd.uid create mode 100644 scenes/managers/ui/level_up_choice.tscn create mode 100644 scenes/managers/ui/level_up_ui.gd create mode 100644 scenes/managers/ui/level_up_ui.gd.uid create mode 100644 scenes/managers/ui/level_up_ui.tscn diff --git a/global_const.gd b/global_const.gd index a721c87..f8d06f5 100644 --- a/global_const.gd +++ b/global_const.gd @@ -15,3 +15,97 @@ const GROUP_ENEMY = "enemy" const GROUP_DAMAGEABLE = "damagable" const GROUP_PLAYER = "player" const GROUP_XP_ORB = "xp_orb" + +enum ModRarity { LEGENDARY, EPIC, RARE, NORMAL } + +var placeholder_tex: Texture2D + +var MOD_CHOICES = [ + { + "internal_name": "flat_health_small", + "name": "+10 Health", + "rarity": ModRarity.NORMAL, + "tex": placeholder_tex, + "description": "Adds 10 flat health.", + "weight": 100, + "mod_name": "max_health", + "mod_value": 10.0, + "mod_type": PlayerStatsModifier.ModifierType.ADDITIVE + }, + { + "internal_name": "flat_health_med", + "name": "+25 Health", + "rarity": ModRarity.RARE, + "tex": placeholder_tex, + "description": "Adds 25 flat health.", + "weight": 50, + "mod_name": "max_health", + "mod_value": 25.0, + "mod_type": PlayerStatsModifier.ModifierType.ADDITIVE + }, + { + "internal_name": "flat_health_large", + "name": "+50 Health", + "rarity": ModRarity.EPIC, + "tex": placeholder_tex, + "description": "Adds 50 flat health.", + "weight": 10, + "mod_name": "max_health", + "mod_value": 50.0, + "mod_type": PlayerStatsModifier.ModifierType.ADDITIVE + }, + { + "internal_name": "flat_crit_small", + "name": "2.5% More Critical Chance", + "rarity": ModRarity.RARE, + "tex": placeholder_tex, + "description": "Gives 2.5% more base critical hit chance", + "weight": 50, + "mod_name": "crit_chance", + "mod_value": 0.025, + "mod_type": PlayerStatsModifier.ModifierType.ADDITIVE + }, + { + "internal_name": "flat_crit_large", + "name": "5% More Critical Chance", + "rarity": ModRarity.EPIC, + "tex": placeholder_tex, + "description": "Gives 5% more base critical hit chance", + "weight": 10, + "mod_name": "crit_chance", + "mod_value": 0.05, + "mod_type": PlayerStatsModifier.ModifierType.ADDITIVE + }, +] + +func _ready() -> void: + placeholder_tex = PlaceholderTexture2D.new() + placeholder_tex.size = Vector2(64.0, 64.0) + +func _draw_random_choice(fortune: float = 1.0) -> Dictionary: + var total_weight: int = 0 + for choice in MOD_CHOICES: + total_weight += calculate_weight(choice["weight"], fortune) + var roll = randi() % total_weight + var cumulative = 0 + for u in MOD_CHOICES: + cumulative += u["weight"] + if roll < cumulative: + return u + return MOD_CHOICES.back() + +func draw_random_mod(fortune: float = 1.0) -> PlayerStatsModifier: + var choice = _draw_random_choice(fortune) + var mod: PlayerStatsModifier = PlayerStatsModifier.new() + mod.stat_name = choice["mod_name"] + mod.value = choice["mod_value"] + mod.type = choice["mod_type"] + mod.description = choice["description"] + mod.internal_name = choice["internal_name"] + mod.tex = choice["tex"] + mod.title = choice["name"] + + return mod + +func calculate_weight(weight: float, fortune: float): + return weight**(1 / 1 + fortune) diff --git a/scenes/attacks/attack_sword.gd b/scenes/attacks/attack_sword.gd index 31bf7e9..84b5cbe 100644 --- a/scenes/attacks/attack_sword.gd +++ b/scenes/attacks/attack_sword.gd @@ -93,6 +93,7 @@ func _on_attack_area_body_entered(body: Node2D) -> void: return if body.is_in_group(GlobalConst.GROUP_ENEMY) and body.is_in_group(GlobalConst.GROUP_DAMAGEABLE): var crit_chance = player.player_stats.get_final("crit_chance", player.modifiers) + print_debug("crit_chance: %s" % crit_chance) var damage_dealt = base_damage var is_crit = randf() >= 1 - crit_chance if is_crit: diff --git a/scenes/managers/ui/level_up_choice.gd b/scenes/managers/ui/level_up_choice.gd new file mode 100644 index 0000000..91aa373 --- /dev/null +++ b/scenes/managers/ui/level_up_choice.gd @@ -0,0 +1,34 @@ +class_name LevelUpChoice +extends PanelContainer + +@export var player: Player +@export var mod: PlayerStatsModifier + +signal lvlup_picked(mod: PlayerStatsModifier) + +@onready var upgrade_name: Label = $MarginContainer/VBoxContainer/UpgradeName +@onready var upgrade_description: Label = $MarginContainer/VBoxContainer/UpgradeDescription +@onready var upgrade_tex: TextureRect = $MarginContainer/VBoxContainer/CenterContainer/UpgradeTex + +func _ready() -> void: + match mod.rarity: + GlobalConst.ModRarity.NORMAL: + upgrade_name.add_theme_color_override("font_color", Color.WHITE) + GlobalConst.ModRarity.RARE: + upgrade_name.add_theme_color_override("font_color", Color.DODGER_BLUE) + GlobalConst.ModRarity.EPIC: + upgrade_name.add_theme_color_override("font_color", Color.DARK_ORCHID) + GlobalConst.ModRarity.LEGENDARY: + upgrade_name.add_theme_color_override("font_color", Color.DARK_ORANGE) + + upgrade_name.text = mod.title + upgrade_description.text = mod.description + upgrade_tex.texture = mod.tex + + +func _on_pick_button_pressed() -> void: + player.modifiers.append(mod) + print_debug("player mods: %s" % len(player.modifiers)) + Engine.time_scale = 1.0 + lvlup_picked.emit(mod) + queue_free() diff --git a/scenes/managers/ui/level_up_choice.gd.uid b/scenes/managers/ui/level_up_choice.gd.uid new file mode 100644 index 0000000..8761d8c --- /dev/null +++ b/scenes/managers/ui/level_up_choice.gd.uid @@ -0,0 +1 @@ +uid://c5wglrsnl38v2 diff --git a/scenes/managers/ui/level_up_choice.tscn b/scenes/managers/ui/level_up_choice.tscn new file mode 100644 index 0000000..13a32ee --- /dev/null +++ b/scenes/managers/ui/level_up_choice.tscn @@ -0,0 +1,46 @@ +[gd_scene load_steps=3 format=3 uid="uid://cbjae7oyakpfw"] + +[ext_resource type="Script" uid="uid://c5wglrsnl38v2" path="res://scenes/managers/ui/level_up_choice.gd" id="1_afnb8"] + +[sub_resource type="PlaceholderTexture2D" id="PlaceholderTexture2D_a1rnr"] +size = Vector2(64, 64) + +[node name="LevelUpChoice" type="PanelContainer"] +offset_right = 220.0 +offset_bottom = 270.0 +script = ExtResource("1_afnb8") + +[node name="MarginContainer" type="MarginContainer" parent="."] +layout_mode = 2 +theme_override_constants/margin_left = 10 +theme_override_constants/margin_top = 20 +theme_override_constants/margin_right = 10 +theme_override_constants/margin_bottom = 20 + +[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"] +layout_mode = 2 + +[node name="UpgradeName" type="Label" parent="MarginContainer/VBoxContainer"] +layout_mode = 2 +text = "+10% Area of attack" + +[node name="CenterContainer" type="CenterContainer" parent="MarginContainer/VBoxContainer"] +layout_mode = 2 + +[node name="UpgradeTex" type="TextureRect" parent="MarginContainer/VBoxContainer/CenterContainer"] +layout_mode = 2 +texture = SubResource("PlaceholderTexture2D_a1rnr") +stretch_mode = 4 + +[node name="UpgradeDescription" type="Label" parent="MarginContainer/VBoxContainer"] +custom_minimum_size = Vector2(200, 100) +layout_mode = 2 +theme_override_font_sizes/font_size = 8 +text = "Bla bla, this gives you 10% bigger attacks lol" +autowrap_mode = 2 + +[node name="PickButton" type="Button" parent="MarginContainer/VBoxContainer"] +layout_mode = 2 +text = "Pick" + +[connection signal="pressed" from="MarginContainer/VBoxContainer/PickButton" to="." method="_on_pick_button_pressed"] diff --git a/scenes/managers/ui/level_up_ui.gd b/scenes/managers/ui/level_up_ui.gd new file mode 100644 index 0000000..f24dd83 --- /dev/null +++ b/scenes/managers/ui/level_up_ui.gd @@ -0,0 +1,15 @@ +class_name LevelUpUI +extends Control + +@onready var choice_container: HBoxContainer = $VBoxContainer/PanelContainer/CenterContainer/ChoiceContainer + +func clear(): + for child in choice_container.get_children(): + child.queue_free() + +func add_choice(choice: LevelUpChoice) -> void: + choice.lvlup_picked.connect(_on_levelup_picked) + choice_container.add_child(choice) + +func _on_levelup_picked(mod: PlayerStatsModifier) -> void: + visible = false diff --git a/scenes/managers/ui/level_up_ui.gd.uid b/scenes/managers/ui/level_up_ui.gd.uid new file mode 100644 index 0000000..5d90755 --- /dev/null +++ b/scenes/managers/ui/level_up_ui.gd.uid @@ -0,0 +1 @@ +uid://dybfo3bablxhk diff --git a/scenes/managers/ui/level_up_ui.tscn b/scenes/managers/ui/level_up_ui.tscn new file mode 100644 index 0000000..ae9f0bf --- /dev/null +++ b/scenes/managers/ui/level_up_ui.tscn @@ -0,0 +1,35 @@ +[gd_scene load_steps=2 format=3 uid="uid://isg7vt4l7eem"] + +[ext_resource type="Script" uid="uid://dybfo3bablxhk" path="res://scenes/managers/ui/level_up_ui.gd" id="1_5751k"] + +[node name="LevelUpUI" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_5751k") + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -334.0 +offset_top = -135.0 +offset_right = 334.0 +offset_bottom = 135.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="PanelContainer" type="PanelContainer" parent="VBoxContainer"] +layout_mode = 2 + +[node name="CenterContainer" type="CenterContainer" parent="VBoxContainer/PanelContainer"] +layout_mode = 2 + +[node name="ChoiceContainer" type="HBoxContainer" parent="VBoxContainer/PanelContainer/CenterContainer"] +layout_mode = 2 diff --git a/scenes/managers/ui/main_ui.gd b/scenes/managers/ui/main_ui.gd index d15518c..1b88e42 100644 --- a/scenes/managers/ui/main_ui.gd +++ b/scenes/managers/ui/main_ui.gd @@ -4,8 +4,10 @@ extends Control @onready var pause_ui: PauseUI = $CanvasLayer/PauseUI @onready var player_ui: PlayerUI = $CanvasLayer/PlayerUI @onready var debug_ui: DebugUI = $CanvasLayer/DebugUI +@onready var level_up_ui: LevelUpUI = $CanvasLayer/LevelUpUI func _ready() -> void: pause_ui.visible = false player_ui.visible = true + level_up_ui.visible = false diff --git a/scenes/managers/ui/main_ui.tscn b/scenes/managers/ui/main_ui.tscn index b1ee6c1..02adf84 100644 --- a/scenes/managers/ui/main_ui.tscn +++ b/scenes/managers/ui/main_ui.tscn @@ -1,9 +1,10 @@ -[gd_scene load_steps=5 format=3 uid="uid://b18uib08hvdpq"] +[gd_scene load_steps=6 format=3 uid="uid://b18uib08hvdpq"] [ext_resource type="Script" uid="uid://dcxc70fvu7kl2" path="res://scenes/managers/ui/main_ui.gd" id="1_3a826"] [ext_resource type="Script" uid="uid://sjnxf0hj3egp" path="res://scenes/managers/ui/pause_ui.gd" id="1_lke1m"] [ext_resource type="Script" uid="uid://dbq74tvxtpfjc" path="res://scenes/managers/ui/player_ui.gd" id="3_gaipe"] [ext_resource type="Script" uid="uid://d2o6tqnqg2o25" path="res://scenes/managers/ui/debug_ui.gd" id="4_217l8"] +[ext_resource type="PackedScene" uid="uid://isg7vt4l7eem" path="res://scenes/managers/ui/level_up_ui.tscn" id="5_cfhdr"] [node name="MainUI" type="Control"] layout_mode = 3 @@ -161,6 +162,8 @@ mouse_filter = 2 [node name="VBoxContainer" type="VBoxContainer" parent="CanvasLayer/DebugUI/StatsContainer"] layout_mode = 2 +[node name="LevelUpUI" parent="CanvasLayer" instance=ExtResource("5_cfhdr")] + [connection signal="pressed" from="CanvasLayer/PauseUI/CenterContainer/MarginContainer/PanelContainer/MarginContainer/VBoxContainer/ResumeButton" to="CanvasLayer/PauseUI" method="_on_resume_button_pressed"] [connection signal="pressed" from="CanvasLayer/PauseUI/CenterContainer/MarginContainer/PanelContainer/MarginContainer/VBoxContainer/NewGameBtuton" to="CanvasLayer/PauseUI" method="_on_new_game_btuton_pressed"] [connection signal="pressed" from="CanvasLayer/PauseUI/CenterContainer/MarginContainer/PanelContainer/MarginContainer/VBoxContainer/OptionsButton" to="CanvasLayer/PauseUI" method="_on_options_button_pressed"] diff --git a/scenes/player.gd b/scenes/player.gd index 5ef360e..92efda6 100644 --- a/scenes/player.gd +++ b/scenes/player.gd @@ -87,10 +87,35 @@ func get_taunted(): func toggle_god_mode(value: bool): god_mode = value +func add_xp(amount: float) -> void: + player_stats.current_xp += amount + _check_level_up() + +func _check_level_up() -> void: + var required_for_level = 25.0 + if player_stats.current_xp >= required_for_level: + player_stats.current_level += 1 + player_stats.current_xp -= required_for_level + _trigger_level_up() + +func _trigger_level_up() -> void: + var choice_count = 3 + var choices: Array[LevelUpChoice] = [] + print_debug("level up") + main_ui.level_up_ui.clear() + for i in range(choice_count): + # TODO: implement fortune + var mod = GlobalConst.draw_random_mod(1.0) + var l_choice = preload("res://scenes/managers/ui/level_up_choice.tscn").instantiate() + l_choice.mod = mod + l_choice.player = self + main_ui.level_up_ui.add_choice(l_choice) + Engine.time_scale = 0.0 + main_ui.level_up_ui.visible = true func _on_pickup_area_area_entered(area: Area2D) -> void: var body: XPOrb = area.get_parent() if body.is_in_group(GlobalConst.GROUP_XP_ORB): - player_stats.current_xp += body.value + add_xp(body.value) GlobalConst.sig_debug_stats_set.emit("player_xp", "%s" % player_stats.current_xp) body.queue_free() diff --git a/scenes/player_stats.gd b/scenes/player_stats.gd index 670f755..600418e 100644 --- a/scenes/player_stats.gd +++ b/scenes/player_stats.gd @@ -19,7 +19,7 @@ func get_final(stat: String, modifiers: Array[PlayerStatsModifier]) -> Variant: if mod.stat_name == stat: if mod.type == PlayerStatsModifier.ModifierType.ADDITIVE: add += mod.value - if mod.type == PlayerStatsModifier.ModifierType.ADDITIVE: + if mod.type == PlayerStatsModifier.ModifierType.MULTIPLICATIVE: mul *= mod.value if mod.type == PlayerStatsModifier.ModifierType.ABSOLUTE: return mod.value diff --git a/scenes/player_stats_modifier.gd b/scenes/player_stats_modifier.gd index 4f419df..12aab6b 100644 --- a/scenes/player_stats_modifier.gd +++ b/scenes/player_stats_modifier.gd @@ -6,3 +6,8 @@ enum ModifierType { ADDITIVE, MULTIPLICATIVE, ABSOLUTE } var stat_name: String var value: Variant var type: ModifierType = ModifierType.ADDITIVE +var internal_name: String +var title: String +var rarity: GlobalConst.ModRarity +var tex: Texture2D +var description: String