140 lines
6.4 KiB
GDScript
140 lines
6.4 KiB
GDScript
class_name SkillScreen
|
|
extends Control
|
|
## Skilltree: oben Level, Erfahrungsbalken bis zum nächsten Level und freie Skillpunkte; darunter
|
|
## vier Äste (Skills.BRANCHES) als senkrechte Pfade: Knoten mit Name, Wirkung, Rangpunkten, „Jetzt“
|
|
## und „Nächste“ in Zahlen (Skills.effect), durch Linien verbunden. Der nächste Knoten öffnet, sobald
|
|
## der davor eine Stufe hat; „Lernen“ steht nur, wenn lernbar, fertige Knoten zeigen „Gelernt“.
|
|
|
|
const COL_W := 285
|
|
const COL_STEP := 300
|
|
const NODE_TOP := 218
|
|
const NODE_H := 150
|
|
const NODE_STEP := 172
|
|
|
|
var app
|
|
var level_label: Label
|
|
var xp_bar: ProgressBar
|
|
var xp_label: Label
|
|
var points_label: Label
|
|
var msg_label: Label
|
|
var nodes := {} # Skill-ID -> SkillNode
|
|
var learn_buttons := {} # Skill-ID -> Knopf
|
|
var now_labels := {} # Skill-ID -> Wirkung jetzt
|
|
var next_labels := {} # Skill-ID -> Wirkung der nächsten Stufe
|
|
var state_labels := {} # Skill-ID -> „Gelernt“, „Erst … lernen“ oder „Kein Skillpunkt frei“
|
|
var links := {} # Skill-ID -> Linie zum Knoten davor
|
|
|
|
|
|
## Ein Knoten des Baums: Rahmen und Rangpunkte zeichnet er selbst, Texte und Knopf sind Kinder.
|
|
class SkillNode extends Control:
|
|
var rank := 0
|
|
var max_rank := 1
|
|
var open := true # der Skill davor hat eine Stufe (oder es gibt keinen)
|
|
|
|
func _init(rect: Rect2, mx: int) -> void:
|
|
max_rank = mx
|
|
position = rect.position
|
|
size = rect.size
|
|
|
|
func _draw() -> void:
|
|
var done := rank >= max_rank
|
|
var bg: Color = Style.PANEL_LIGHT if open else Style.darker(Style.PANEL, 0.9)
|
|
var border: Color = Style.GREEN if done else (Style.GOLD if rank > 0 else (Style.MUTED if open else Style.darker(Style.MUTED, 0.5)))
|
|
draw_style_box(Style.box(bg, 8, border, 2), Rect2(Vector2.ZERO, size))
|
|
for i in max_rank: # Rangpunkte oben rechts
|
|
var c := Vector2(size.x - 16 - (max_rank - 1 - i) * 18, 21)
|
|
draw_circle(c, 6, Style.GOLD if i < rank else Style.darker(Style.MUTED, 0.6))
|
|
|
|
|
|
func _ready() -> void:
|
|
Style.header(self, "Skilltree", func(): app.show_screen(MenuScreen.new()))
|
|
level_label = Style.chip(self, "", Rect2(40, 86, 160, 38))
|
|
xp_bar = ProgressBar.new()
|
|
xp_bar.position = Vector2(216, 86)
|
|
xp_bar.size = Vector2(520, 38)
|
|
xp_bar.show_percentage = false
|
|
xp_bar.add_theme_stylebox_override("background", Style.box(Style.PANEL_LIGHT, 19))
|
|
xp_bar.add_theme_stylebox_override("fill", Style.box(Style.darker(Style.BLUE, 0.7), 19))
|
|
add_child(xp_bar)
|
|
xp_label = Style.label(self, "", 16, Style.TEXT, Rect2(216, 86, 520, 38), "center", true)
|
|
points_label = Style.chip(self, "", Rect2(752, 86, 260, 38), Style.GREEN)
|
|
Style.label(self, "Erfahrung sind deine verdienten Münzen. Jedes Level bringt einen Skillpunkt. Jeder Ast öffnet sich von oben nach unten.", 14, Style.MUTED, Rect2(40, 134, 1200, 22), "left")
|
|
for b in Skills.BRANCHES.size():
|
|
var branch: String = Skills.BRANCHES[b]
|
|
var x := 40 + b * COL_STEP
|
|
Style.panel(self, Rect2(x, 166, COL_W, 600))
|
|
Style.label(self, branch, 20, Style.GOLD, Rect2(x, 178, COL_W, 30), "center", true)
|
|
var path: Array = Skills.in_branch(branch)
|
|
for i in path.size():
|
|
var id: String = path[i]
|
|
var sk: Dictionary = Skills.SKILLS[id]
|
|
var y := NODE_TOP + i * NODE_STEP
|
|
if i > 0:
|
|
var line := ColorRect.new()
|
|
line.position = Vector2(x + COL_W / 2.0 - 2, y - NODE_STEP + NODE_H)
|
|
line.size = Vector2(4, NODE_STEP - NODE_H)
|
|
add_child(line)
|
|
links[id] = line
|
|
var node := SkillNode.new(Rect2(x + 14, y, COL_W - 28, NODE_H), int(sk.max))
|
|
add_child(node)
|
|
nodes[id] = node
|
|
var w := COL_W - 28 - 28
|
|
Style.label(node, sk.name, 17, Style.TEXT, Rect2(14, 8, w - 60, 26), "left", true)
|
|
var text := Style.label(node, sk.text, 12, Style.MUTED, Rect2(14, 34, w, 34), "left")
|
|
text.autowrap_mode = TextServer.AUTOWRAP_WORD
|
|
now_labels[id] = Style.label(node, "", 12, Style.TEXT, Rect2(14, 70, w, 20), "left")
|
|
next_labels[id] = Style.label(node, "", 12, Style.GREEN, Rect2(14, 88, w, 20), "left")
|
|
learn_buttons[id] = Style.button(node, "Lernen", Rect2(14, 110, w, 32), learn.bind(id), Style.PRIMARY)
|
|
state_labels[id] = Style.label(node, "", 14, Style.MUTED, Rect2(14, 112, w, 28), "center", true)
|
|
msg_label = Style.label(self, "", 15, Style.RED, Rect2(0, 770, 1280, 24), "center")
|
|
refresh()
|
|
|
|
|
|
func _input(event: InputEvent) -> void:
|
|
if event.is_action_pressed("ui_cancel"):
|
|
app.show_screen(MenuScreen.new())
|
|
|
|
|
|
func learn(id: String) -> void:
|
|
var err: String = app.profile.learn(id)
|
|
msg_label.text = tr(err) if err != "" else ""
|
|
if err == "":
|
|
app.sfx.play("combo")
|
|
app.save()
|
|
refresh()
|
|
|
|
|
|
func refresh() -> void:
|
|
var p: Profile = app.profile
|
|
var lvl := p.player_level()
|
|
var base := Skills.xp_for_level(lvl)
|
|
level_label.text = tr("Level %d") % lvl
|
|
xp_bar.max_value = Skills.xp_for_level(lvl + 1) - base
|
|
xp_bar.value = p.xp - base
|
|
xp_label.text = tr("%d / %d EP bis Level %d") % [p.xp - base, Skills.xp_for_level(lvl + 1) - base, lvl + 1]
|
|
points_label.text = tr("Skillpunkte: %d") % p.skill_points()
|
|
for id in Skills.SKILLS:
|
|
var sk: Dictionary = Skills.SKILLS[id]
|
|
var node: SkillNode = nodes[id]
|
|
var prev := Skills.previous(id)
|
|
node.rank = p.skill(id)
|
|
node.open = prev == "" or p.skill(prev) > 0
|
|
node.modulate = Color(1, 1, 1, 1 if node.open else 0.55)
|
|
node.queue_redraw()
|
|
var done := node.rank >= int(sk.max)
|
|
now_labels[id].text = tr("Jetzt: %s") % Skills.effect(id, node.rank)
|
|
next_labels[id].text = "" if done else tr("Nächste: %s") % Skills.effect(id, node.rank + 1)
|
|
var learnable := node.open and not done and p.skill_points() > 0
|
|
learn_buttons[id].visible = learnable
|
|
var state := ""
|
|
if done:
|
|
state = tr("Gelernt")
|
|
elif not node.open:
|
|
state = tr("Erst %s lernen") % tr(Skills.SKILLS[prev].name)
|
|
elif not learnable:
|
|
state = tr("Kein Skillpunkt frei")
|
|
state_labels[id].text = state
|
|
state_labels[id].add_theme_color_override("font_color", Style.GREEN if done else Style.MUTED)
|
|
if links.has(id):
|
|
links[id].color = Style.GOLD if node.open else Style.darker(Style.MUTED, 0.5)
|