hy-farm/godot/FarmGodot/scripts/farm/friend_visit_controller.gd
2026-05-30 20:23:54 +08:00

293 lines
8.9 KiB
GDScript
Raw 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 FriendVisitController
extends RefCounted
# 好友访问控制器。
#
# 参考原 H5
# - 好友列表点击房屋/手指后触发 interaction/get-other-user-info。
# - 访问他人农场后的土地操作统一走 interaction/interaction。
# - interaction type 来自 common/utils/EnumUtil.php
# 0 偷菜1 放虫2 放草3 浇水4 除虫5 除草。
#
# 控制器只处理接口请求和业务回写,不持有场景节点。
const StaminaRulesScript := preload("res://scripts/domain/rules/stamina_rules.gd")
const ACTION_STEAL := "steal"
const ACTION_PUT_BUG := "put_bug"
const ACTION_PUT_GRASS := "put_grass"
const ACTION_WATER := "water"
const ACTION_DIE_BUG := "die_bug"
const ACTION_DIE_GRASS := "die_grass"
const INTERACTION_TYPE_STEAL := 0
const INTERACTION_TYPE_BUG := 1
const INTERACTION_TYPE_GRASS := 2
const INTERACTION_TYPE_WATERING := 3
const INTERACTION_TYPE_DISINSECTION := 4
const INTERACTION_TYPE_WEEDING := 5
var _interaction_api
var _game_config
var _crop_phase_rules
var _callbacks: Dictionary = {}
func setup(options: Dictionary) -> void:
_interaction_api = options.get("interaction_api")
_game_config = options.get("game_config")
_crop_phase_rules = options.get("crop_phase_rules")
_callbacks = options.get("callbacks", {})
func visit_friend_home(row: Dictionary) -> void:
var target_id := target_user_id(row)
if target_id <= 0:
_show_toast("好友数据异常")
return
if target_id == _player_id():
_show_toast("这是你的农场,不用去啦!")
return
if _is_busy() or _interaction_api == null:
return
var token := _token()
if token.is_empty():
_show_toast("登录已失效")
return
_set_busy(true)
var result: Dictionary = await _interaction_api.fetch_other_user_info(token, target_id)
_set_busy(false)
if not result.get("ok", false):
_show_toast(_response_status_text(result, "访问好友失败"))
return
var data = _response_data(result, {})
if not data is Dictionary:
_show_toast("好友数据异常")
return
_call("enter_friend_farm", [data])
func interact_land(action: String) -> void:
if not _is_visiting_friend() or _interaction_api == null:
return
var position := _selected_position()
if position < 0 or _is_busy():
return
var interaction_type := _interaction_type(action)
if interaction_type < 0:
return
var token := _token()
var visited_friend_id := _visited_friend_id()
if token.is_empty() or visited_friend_id <= 0:
_show_toast("登录已失效")
return
if interaction_type == INTERACTION_TYPE_STEAL and not StaminaRulesScript.has_steal_cost(_own_player_data()):
_show_toast("Not enough stamina")
return
var old_land := _land_data(position)
_set_busy(true)
var result: Dictionary = await _interaction_api.interact(token, visited_friend_id, position, interaction_type)
_set_busy(false)
if not result.get("ok", false):
var fail_data = _response_data(result, {})
if fail_data is Dictionary:
_sync_stamina(fail_data)
_show_toast(_response_status_text(result, "好友互动失败"))
return
var data = _response_data(result, {})
var response_data: Dictionary = data if data is Dictionary else {}
_sync_stamina(response_data)
var pet_battle_data := _pet_battle_data(response_data)
if not pet_battle_data.is_empty():
_sync_pet_battle(pet_battle_data)
if _pet_battle_triggered(pet_battle_data):
await _call_async("play_pet_battle_vs")
if interaction_type == INTERACTION_TYPE_STEAL and not _pet_battle_allows_steal(pet_battle_data):
_show_pet_battle_result(pet_battle_data)
return
var land_data: Dictionary = old_land.duplicate(true)
var returned_land = response_data.get("land", {})
if returned_land is Dictionary:
land_data = returned_land.duplicate(true)
if not land_data.has("position"):
land_data["position"] = position
if not land_data.has("crop_phase") and _crop_phase_rules != null:
land_data["crop_phase"] = _crop_phase_rules.get_phase(land_data)
land_data["interaction_type_list"] = _remove_interaction_type(old_land.get("interaction_type_list", []), interaction_type)
_call("apply_land_data", [land_data])
_apply_interaction_reward(interaction_type, response_data, old_land)
_call("hide_selection_ui")
_call("render_lands")
func target_user_id(row: Dictionary) -> int:
for key in ["uid", "user_id", "id"]:
var value := int(row.get(key, 0))
if value > 0:
return value
return 0
func _interaction_type(action: String) -> int:
match action:
ACTION_STEAL:
return INTERACTION_TYPE_STEAL
ACTION_PUT_BUG:
return INTERACTION_TYPE_BUG
ACTION_PUT_GRASS:
return INTERACTION_TYPE_GRASS
ACTION_WATER:
return INTERACTION_TYPE_WATERING
ACTION_DIE_BUG:
return INTERACTION_TYPE_DISINSECTION
ACTION_DIE_GRASS:
return INTERACTION_TYPE_WEEDING
_:
return -1
func _remove_interaction_type(raw_types: Variant, interaction_type: int) -> Array[int]:
var next_types: Array[int] = []
if not raw_types is Array:
return next_types
for raw_type in raw_types:
var type_id := int(raw_type)
if type_id != interaction_type and not next_types.has(type_id):
next_types.append(type_id)
return next_types
func _apply_interaction_reward(interaction_type: int, response_data: Dictionary, old_land: Dictionary) -> void:
match interaction_type:
INTERACTION_TYPE_STEAL:
_apply_steal_reward(response_data, old_land)
INTERACTION_TYPE_BUG:
_show_toast("放虫成功")
INTERACTION_TYPE_GRASS:
_show_toast("放草成功")
INTERACTION_TYPE_WATERING:
_show_toast("浇水成功")
INTERACTION_TYPE_DISINSECTION:
_show_toast("除虫成功")
INTERACTION_TYPE_WEEDING:
_show_toast("除草成功")
func _apply_steal_reward(response_data: Dictionary, old_land: Dictionary) -> void:
var steal_count := int(response_data.get("steal_count", 0))
var crop_id := int(old_land.get("crop_id", 0))
if crop_id <= 0:
var returned_land = response_data.get("land", {})
if returned_land is Dictionary:
crop_id = int(returned_land.get("crop_id", 0))
var crop_name := "作物"
var item_id := 0
if _game_config != null:
crop_name = _game_config.get_crop_name(crop_id)
if _game_config.has_method("get_crop_output_item_id"):
item_id = int(_game_config.get_crop_output_item_id(crop_id))
if item_id > 0 and steal_count > 0:
_call("add_reward_items", ["%s:%s" % [item_id, steal_count]])
_show_toast("偷取%sx%s" % [crop_name, steal_count])
func _sync_stamina(response_data: Dictionary) -> void:
var stamina_data = response_data.get("stamina", {})
if stamina_data is Dictionary:
_call("apply_player_stamina", [stamina_data])
func _sync_pet_battle(pet_battle_data: Dictionary) -> void:
_call("apply_pet_battle_data", [pet_battle_data])
func _pet_battle_data(response_data: Dictionary) -> Dictionary:
var data = response_data.get("pet_battle", {})
return data if data is Dictionary else {}
func _pet_battle_triggered(pet_battle_data: Dictionary) -> bool:
return int(pet_battle_data.get("triggered", 0)) == 1
func _pet_battle_allows_steal(pet_battle_data: Dictionary) -> bool:
return int(pet_battle_data.get("allow_steal", 1)) == 1
func _show_pet_battle_result(pet_battle_data: Dictionary) -> void:
var damage := int(pet_battle_data.get("damage", 0))
var result := str(pet_battle_data.get("result", ""))
if result == "damage":
_show_toast("Pet duel dealt %s damage" % damage)
elif result == "weak_opened":
_show_toast("Pet defeated. Farm weakened.")
else:
_show_toast("Pet duel failed")
func _selected_position() -> int:
return int(_call("selected_position", [], -1))
func _token() -> String:
return str(_call("token", [], ""))
func _player_id() -> int:
return int(_call("player_id", [], 0))
func _own_player_data() -> Dictionary:
var data = _call("own_player_data", [], {})
return data if data is Dictionary else {}
func _visited_friend_id() -> int:
return int(_call("visited_friend_id", [], 0))
func _land_data(position: int) -> Dictionary:
var result = _call("land_data", [position], {})
return result if result is Dictionary else {}
func _is_visiting_friend() -> bool:
return bool(_call("is_visiting_friend", [], false))
func _is_busy() -> bool:
return bool(_call("is_operation_in_progress", [], false))
func _set_busy(value: bool) -> void:
_call("set_operation_in_progress", [value])
func _response_data(result: Dictionary, fallback: Variant = {}) -> Variant:
return _call("response_data", [result, fallback], fallback)
func _response_status_text(result: Dictionary, fallback := "操作失败") -> String:
return str(_call("response_status_text", [result, fallback], fallback))
func _show_toast(message: String) -> void:
_call("show_toast", [message])
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)
func _call_async(name: String, args: Array = [], fallback: Variant = null) -> Variant:
var callable: Callable = _callbacks.get(name, Callable())
if not callable.is_valid():
return fallback
return await callable.callv(args)