140 lines
4.5 KiB
GDScript
140 lines
4.5 KiB
GDScript
## PlayerStats — 玩家属性统计(Autoload: PlayerStats)
|
||
## 权威来源:development_plan.md S2、numerical_design.md §1.2
|
||
extends Node
|
||
|
||
signal stats_changed
|
||
signal leveled_up(new_level: int)
|
||
|
||
# ── HP ───────────────────────────────────────────────
|
||
var hp: float = 100.0
|
||
var hp_max: float = 100.0
|
||
|
||
# ── 资源 ──────────────────────────────────────────────
|
||
var gold: int = 0
|
||
var xp: int = 0
|
||
|
||
# ── 等级 ──────────────────────────────────────────────
|
||
var level: int = 1
|
||
var xp_to_next: int = 10 # 升级所需 XP
|
||
|
||
# ── 战斗属性 ───────────────────────────────────────────
|
||
var cpu_limit: int = 5 # 控制 MAX_OPS = cpu_limit * 40
|
||
var armor: float = 0.0
|
||
var resistance: float = 0.0 # 0.0~1.0
|
||
|
||
# ── 魔力(MVP)─────────────────────────────────────────
|
||
var mana: float = 100.0
|
||
var mana_max: float = 100.0
|
||
var mana_regen: float = 5.0 # 每秒回复
|
||
var mana_heat: float = 0.0 # 递增蓝耗热值(0=无,1=蓝耗翻倍)
|
||
var mana_leech: float = 0.0 # 击杀回蓝量(换杖/换牌重算)
|
||
|
||
func _ready() -> void:
|
||
EventBus.subscribe(EventID.PLAYER_DAMAGED, _on_player_damaged)
|
||
EventBus.subscribe(EventID.ENEMY_KILLED, _on_enemy_killed)
|
||
|
||
func _on_enemy_killed(_payload: Dictionary) -> void:
|
||
gain_mana(mana_leech)
|
||
|
||
## 升级曲线:roundf(10 × 1.4^(level-1)),P6-N16
|
||
## Level 1→2: 10 XP; Level 5→6: ≈ 54 XP
|
||
static func xp_for_level(lv: int) -> int:
|
||
return roundi(10.0 * pow(1.4, float(lv - 1)))
|
||
|
||
func gain_xp(amount: int) -> void:
|
||
xp += amount
|
||
while xp >= xp_to_next:
|
||
xp -= xp_to_next
|
||
level += 1
|
||
xp_to_next = xp_for_level(level)
|
||
leveled_up.emit(level)
|
||
EventBus.emit(EventID.LEVEL_UP, {"level": level})
|
||
stats_changed.emit()
|
||
|
||
func gain_gold(amount: int) -> void:
|
||
gold += amount
|
||
stats_changed.emit()
|
||
|
||
func spend_gold(amount: int) -> bool:
|
||
if gold < amount:
|
||
return false
|
||
gold -= amount
|
||
stats_changed.emit()
|
||
return true
|
||
|
||
func take_damage(amount: float) -> void:
|
||
hp = max(0.0, hp - amount * SettingsManager.player_dmg_taken_mult()) # 难度减伤(初学者×0.7)
|
||
stats_changed.emit()
|
||
if hp <= 0.0:
|
||
EventBus.emit(EventID.PLAYER_DIED, {})
|
||
|
||
func heal(amount: float) -> void:
|
||
hp = min(hp_max, hp + amount)
|
||
stats_changed.emit()
|
||
|
||
func get_hp_percent() -> float:
|
||
return hp / max(hp_max, 0.001)
|
||
|
||
## 换杖时设定蓝池上限/回速,并把当前蓝夹到 [0, max]
|
||
func set_mana_pool(max_v: float, regen: float) -> void:
|
||
mana_max = max_v
|
||
mana_regen = regen
|
||
mana = clampf(mana, 0.0, mana_max)
|
||
stats_changed.emit()
|
||
|
||
## 花费魔力;够则扣除返回 true,否则不变返回 false
|
||
func spend_mana(cost: float) -> bool:
|
||
if mana < cost:
|
||
return false
|
||
mana -= cost
|
||
stats_changed.emit()
|
||
return true
|
||
|
||
## 每帧回蓝(PlayerManager 驱动)。不 emit stats_changed —— HUD 每帧直接读 mana,
|
||
## 避免回蓝过程每帧发信号触发商店刷新。
|
||
func regen_mana(delta: float) -> void:
|
||
if mana >= mana_max:
|
||
return
|
||
mana = min(mana_max, mana + mana_regen * delta)
|
||
|
||
## 击杀回蓝(魔力虹吸)
|
||
func gain_mana(n: float) -> void:
|
||
if n <= 0.0:
|
||
return
|
||
mana = min(mana_max, mana + n)
|
||
stats_changed.emit()
|
||
|
||
## 主动 HP 代价(infinite_spells / heavy_cost)——直接扣血,不走难度减伤
|
||
func spend_hp_cost(amount: float) -> void:
|
||
if amount <= 0.0:
|
||
return
|
||
hp = max(0.0, hp - amount)
|
||
stats_changed.emit()
|
||
if hp <= 0.0:
|
||
EventBus.emit(EventID.PLAYER_DIED, {})
|
||
|
||
func _on_player_damaged(payload: Dictionary) -> void:
|
||
take_damage(float(payload.get("damage", 0.0)))
|
||
|
||
func reset_for_run() -> void:
|
||
hp = hp_max
|
||
mana = mana_max
|
||
gold = 0
|
||
xp = 0
|
||
level = 1
|
||
xp_to_next = xp_for_level(1)
|
||
stats_changed.emit()
|
||
|
||
func get_save_data() -> Dictionary:
|
||
return {"hp": hp, "hp_max": hp_max, "gold": gold, "xp": xp, "level": level, "cpu_limit": cpu_limit}
|
||
|
||
func load_save_data(data: Dictionary) -> void:
|
||
hp = float(data.get("hp", 100.0))
|
||
hp_max = float(data.get("hp_max", 100.0))
|
||
gold = int(data.get("gold", 0))
|
||
xp = int(data.get("xp", 0))
|
||
level = int(data.get("level", 1))
|
||
cpu_limit = int(data.get("cpu_limit", 5))
|
||
xp_to_next = xp_for_level(level)
|
||
stats_changed.emit()
|