hy-farm/godot/FarmGodot/scripts/state/player_inventory_facade.gd
2026-05-29 19:54:56 +08:00

221 lines
8.0 KiB
GDScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

class_name PlayerInventoryFacade
extends RefCounted
# 玩家背包/货币门面:
# - UI 面板只调用这里的“刷新、检查、扣减、发奖”接口,不直接改 InventoryState。
# - 后端返回的仓库数据由 InventoryMapper 归一化,金币/钻石/石材/钢材等资源保留在 PlayerState。
# - 该类仍通过 callbacks 兼容旧 game_scene 字段,后续拆面板时优先依赖它而不是直接读主场景。
const CostRulesScript := preload("res://scripts/domain/rules/cost_rules.gd")
# PlayerState 是玩家快照的唯一权威来源InventoryState 只管理种子/肥料/仓库列表。
var _player_state
var _player_data: Dictionary = {}
var _inventory_state
var _inventory_mapper
var _inventory_api
var _game_config
# callbacks 用于通知 UI 局部刷新,避免 Facade 反向依赖具体面板节点。
var _callbacks: Dictionary = {}
var _item_id_gem := 105001
var _item_id_gold := 105002
var _item_id_stone := 201000
var _item_id_steel := 201001
func setup(options: Dictionary) -> void:
# 所有依赖在 bootstrap 阶段注入,保证状态、接口、配置来源一致。
_player_state = options.get("player_state")
_player_data = options.get("player_data", {})
_inventory_state = options.get("inventory_state")
_inventory_mapper = options.get("inventory_mapper")
_inventory_api = options.get("inventory_api")
_game_config = options.get("game_config")
_callbacks = options.get("callbacks", {})
_item_id_gem = int(options.get("item_id_gem", _item_id_gem))
_item_id_gold = int(options.get("item_id_gold", _item_id_gold))
_item_id_stone = int(options.get("item_id_stone", _item_id_stone))
_item_id_steel = int(options.get("item_id_steel", _item_id_steel))
func player_data() -> Dictionary:
if _player_state != null and _player_state.has_method("player_data"):
return _player_state.player_data()
return _player_data
func set_player_data(player_data: Dictionary) -> void:
if _player_state != null and _player_state.has_method("set_data"):
_player_state.set_data(player_data)
_player_data = player_data
func seed_items() -> Array[Dictionary]:
return _inventory_state.seed_items if _inventory_state != null else []
func fertilizer_items() -> Array[Dictionary]:
return _inventory_state.fertilizer_items if _inventory_state != null else []
func warehouse_items() -> Array[Dictionary]:
return _inventory_state.warehouse_items if _inventory_state != null else []
func refresh_store_house(token: String) -> void:
if token.is_empty() or _inventory_api == null or _inventory_mapper == null or _inventory_state == null:
return
# 仓库刷新是网络操作;只在打开背包/仓库或关键操作后调用,不放进 process 轮询。
var result: Dictionary = await _inventory_api.fetch_store_house(token)
if not result.get("ok", false):
push_warning("store-house failed: %s" % result.get("game_status", result.get("status", 0)))
return
var body = result.get("body", {})
if not body is Dictionary:
return
var store_data = body.get("data", [])
# 后端一份列表拆成种子、肥料、仓库三份 UI 数据,渲染层无需理解老接口字段。
_inventory_state.set_items(
_inventory_mapper.extract_seed_items(store_data),
_inventory_mapper.extract_fertilizer_items(store_data),
_inventory_mapper.extract_warehouse_items(store_data)
)
_emit_inventory_changed()
# 只刷新当前可见的面板,避免背包更新导致所有功能面板重建。
if bool(_call("should_render_bag", [], false)):
_call("render_bag")
if bool(_call("should_render_warehouse", [], false)):
_call("render_warehouse")
func parse_cost_string(cost_text: String) -> Array[Dictionary]:
return CostRulesScript.parse_cost_string(cost_text)
func aggregate_costs(costs: Array[Dictionary]) -> Dictionary:
return CostRulesScript.aggregate_costs(costs)
func has_costs(cost_text: String) -> bool:
# 消耗判断同时覆盖 player_data 货币和仓库道具;各面板不要再各写一份判断。
return CostRulesScript.has_costs(cost_text, player_data(), warehouse_items())
func owned_amount(item_id: int) -> int:
return CostRulesScript.get_owned_amount(item_id, player_data(), warehouse_items())
func apply_cost_deduction(cost_text: String) -> void:
var aggregated := aggregate_costs(parse_cost_string(cost_text))
var data_ref := player_data()
# 本地扣减按物品 id 分流:资源写 player_data普通道具写 InventoryState。
for item_id in aggregated.keys():
var count := int(aggregated[item_id])
match int(item_id):
_item_id_gem:
data_ref["gem"] = maxi(int(data_ref.get("gem", 0)) - count, 0)
_item_id_gold:
data_ref["gold"] = maxi(int(data_ref.get("gold", 0)) - count, 0)
_item_id_stone:
data_ref["stone"] = maxi(int(data_ref.get("stone", 0)) - count, 0)
_item_id_steel:
data_ref["steel"] = maxi(int(data_ref.get("steel", 0)) - count, 0)
_:
decrease_warehouse_item(int(item_id), count)
# 扣减后立即刷新顶部资源和相关背包视图,保证操作反馈接近原版 H5。
_call("render_player")
_emit_inventory_changed()
func add_reward_items(cost_text: String) -> void:
# 奖励文本复用 CostRules 格式,活动奖励都能走同一个落账入口。
var data_ref := player_data()
for cost in parse_cost_string(cost_text):
var item_id := int(cost.get("item_id", 0))
var count := int(cost.get("count", 0))
match item_id:
_item_id_gem:
data_ref["gem"] = int(data_ref.get("gem", 0)) + count
_item_id_gold:
data_ref["gold"] = int(data_ref.get("gold", 0)) + count
_item_id_stone:
data_ref["stone"] = int(data_ref.get("stone", 0)) + count
_item_id_steel:
data_ref["steel"] = int(data_ref.get("steel", 0)) + count
_:
add_warehouse_item(item_id, count)
_call("render_player")
_emit_inventory_changed()
func add_warehouse_item(item_id: int, count: int) -> void:
var items := warehouse_items()
for index in range(items.size()):
if int(items[index].get("item_id", 0)) != item_id:
continue
# 同类道具合并数量,同时写 num/number 两个字段兼容不同旧面板读取习惯。
var next_num := int(items[index].get("num", items[index].get("number", 0))) + count
items[index]["num"] = next_num
items[index]["number"] = next_num
return
# 新道具需要补齐 icon/name/config避免仓库面板再回头查多次配置。
var config: Dictionary = _game_config.normalize_item_config(_game_config.get_item_config(item_id))
items.append({
"item_id": item_id,
"num": count,
"number": count,
"icon_id": int(config.get("icon", _game_config.get_item_icon_id(item_id))),
"name": str(config.get("name", _game_config.get_item_name(item_id))),
"config": config
})
func decrease_warehouse_item(item_id: int, count: int) -> void:
if _inventory_state == null:
return
_inventory_state.decrease_warehouse(item_id, count)
_emit_inventory_changed()
func decrease_seed_item(item_id: int) -> void:
if _inventory_state == null:
return
_inventory_state.decrease_seed(item_id)
_emit_inventory_changed()
func decrease_fertilizer_item(item_id: int) -> void:
if _inventory_state == null:
return
_inventory_state.decrease_fertilizer(item_id)
_emit_inventory_changed()
func decrease_player_currency(gold_cost: int, gem_cost: int) -> void:
# 旧接口多以 gold_cost/gem_cost 分列返回,因此保留专用货币扣减入口。
var data_ref := player_data()
data_ref["gold"] = int(data_ref.get("gold", 0)) - gold_cost
data_ref["gem"] = int(data_ref.get("gem", 0)) - gem_cost
_call("render_player")
func add_player_gold(amount: int) -> void:
var data_ref := player_data()
data_ref["gold"] = int(data_ref.get("gold", 0)) + amount
_call("render_player")
func set_dog_hunger_end_time(end_time: int) -> void:
player_data()["dog_hunger_end_time"] = end_time
func _emit_inventory_changed() -> void:
# 单点广播给主场景同步旧字段;后续面板可直接订阅 Facade减少主场景中转。
_call("inventory_changed")
func _call(name: String, args: Array = [], fallback: Variant = null) -> Variant:
var callable: Callable = _callbacks.get(name, Callable())
if not callable.is_valid():
return fallback
return callable.callv(args)