game: add levelups

This commit is contained in:
2025-08-20 01:59:45 +02:00
parent eec0e6a50f
commit c3f49c885a
13 changed files with 265 additions and 3 deletions

View File

@@ -15,3 +15,97 @@ const GROUP_ENEMY = "enemy"
const GROUP_DAMAGEABLE = "damagable" const GROUP_DAMAGEABLE = "damagable"
const GROUP_PLAYER = "player" const GROUP_PLAYER = "player"
const GROUP_XP_ORB = "xp_orb" 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)

View File

@@ -93,6 +93,7 @@ func _on_attack_area_body_entered(body: Node2D) -> void:
return return
if body.is_in_group(GlobalConst.GROUP_ENEMY) and body.is_in_group(GlobalConst.GROUP_DAMAGEABLE): 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) 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 damage_dealt = base_damage
var is_crit = randf() >= 1 - crit_chance var is_crit = randf() >= 1 - crit_chance
if is_crit: if is_crit:

View File

@@ -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()

View File

@@ -0,0 +1 @@
uid://c5wglrsnl38v2

View File

@@ -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"]

View File

@@ -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

View File

@@ -0,0 +1 @@
uid://dybfo3bablxhk

View File

@@ -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

View File

@@ -4,8 +4,10 @@ extends Control
@onready var pause_ui: PauseUI = $CanvasLayer/PauseUI @onready var pause_ui: PauseUI = $CanvasLayer/PauseUI
@onready var player_ui: PlayerUI = $CanvasLayer/PlayerUI @onready var player_ui: PlayerUI = $CanvasLayer/PlayerUI
@onready var debug_ui: DebugUI = $CanvasLayer/DebugUI @onready var debug_ui: DebugUI = $CanvasLayer/DebugUI
@onready var level_up_ui: LevelUpUI = $CanvasLayer/LevelUpUI
func _ready() -> void: func _ready() -> void:
pause_ui.visible = false pause_ui.visible = false
player_ui.visible = true player_ui.visible = true
level_up_ui.visible = false

View File

@@ -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://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://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://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="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"] [node name="MainUI" type="Control"]
layout_mode = 3 layout_mode = 3
@@ -161,6 +162,8 @@ mouse_filter = 2
[node name="VBoxContainer" type="VBoxContainer" parent="CanvasLayer/DebugUI/StatsContainer"] [node name="VBoxContainer" type="VBoxContainer" parent="CanvasLayer/DebugUI/StatsContainer"]
layout_mode = 2 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/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/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"] [connection signal="pressed" from="CanvasLayer/PauseUI/CenterContainer/MarginContainer/PanelContainer/MarginContainer/VBoxContainer/OptionsButton" to="CanvasLayer/PauseUI" method="_on_options_button_pressed"]

View File

@@ -87,10 +87,35 @@ func get_taunted():
func toggle_god_mode(value: bool): func toggle_god_mode(value: bool):
god_mode = value 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: func _on_pickup_area_area_entered(area: Area2D) -> void:
var body: XPOrb = area.get_parent() var body: XPOrb = area.get_parent()
if body.is_in_group(GlobalConst.GROUP_XP_ORB): 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) GlobalConst.sig_debug_stats_set.emit("player_xp", "%s" % player_stats.current_xp)
body.queue_free() body.queue_free()

View File

@@ -19,7 +19,7 @@ func get_final(stat: String, modifiers: Array[PlayerStatsModifier]) -> Variant:
if mod.stat_name == stat: if mod.stat_name == stat:
if mod.type == PlayerStatsModifier.ModifierType.ADDITIVE: if mod.type == PlayerStatsModifier.ModifierType.ADDITIVE:
add += mod.value add += mod.value
if mod.type == PlayerStatsModifier.ModifierType.ADDITIVE: if mod.type == PlayerStatsModifier.ModifierType.MULTIPLICATIVE:
mul *= mod.value mul *= mod.value
if mod.type == PlayerStatsModifier.ModifierType.ABSOLUTE: if mod.type == PlayerStatsModifier.ModifierType.ABSOLUTE:
return mod.value return mod.value

View File

@@ -6,3 +6,8 @@ enum ModifierType { ADDITIVE, MULTIPLICATIVE, ABSOLUTE }
var stat_name: String var stat_name: String
var value: Variant var value: Variant
var type: ModifierType = ModifierType.ADDITIVE var type: ModifierType = ModifierType.ADDITIVE
var internal_name: String
var title: String
var rarity: GlobalConst.ModRarity
var tex: Texture2D
var description: String