feat(progression): XP, niveaux et popups flottants (+N XP)

Nouvel autoload PlayerProgress qui gagne de l'XP sur:
- cassage de blocs (gain variable selon le type: perle, épave, coral > sand)
- collecte de perles (+25)
- crafting (+10)
- paliers de profondeur atteints (+20 à 10/25/50/75/100m)

HUD: barre XP + label niveau en bas centre, bannière "NIVEAU X" au level-up
(son bulle + fade). Popup 3D "+N XP" spawn au point d'action.

Boucle court-terme dopamine: chaque action = feedback visuel immédiat.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-20 18:08:26 +00:00
parent 982a4ec23d
commit 984754183f
7 changed files with 260 additions and 2 deletions

View File

@@ -225,6 +225,7 @@ func _on_block_break(hit_position: Vector3, _normal: Vector3) -> void:
if broken_id > 0:
inventory.add_item(broken_id, 1)
AudioManager.play_bubble_sfx(hit_position)
_award_break_xp(broken_id, hit_position)
else:
_world_sync.server_break_block(hit_position)
AudioManager.play_bubble_sfx(hit_position)
@@ -240,6 +241,24 @@ func _on_block_break(hit_position: Vector3, _normal: Vector3) -> void:
burst.emit_burst(hit_position, broken_color)
func _award_break_xp(block_id: int, hit_position: Vector3) -> void:
var pp: Node = get_node_or_null("/root/PlayerProgress")
if pp == null:
return
var gain: int = pp.XP_BREAK_BY_BLOCK.get(block_id, pp.XP_BREAK_DEFAULT)
pp.award(gain, "bloc", hit_position)
_spawn_xp_popup(gain, hit_position)
func _spawn_xp_popup(amount: int, world_pos: Vector3) -> void:
if amount <= 0:
return
var popup_script: Script = load("res://scripts/progression/XpPopup.gd")
if popup_script == null:
return
popup_script.spawn(self, "+%d XP" % amount, world_pos + Vector3(0, 0.8, 0))
func _on_block_place(hit_position: Vector3, normal: Vector3) -> void:
var selected: Variant = inventory.get_selected_item()
if selected == null: