Files
Sprout-Farm/MainGame.gd
2025-07-12 14:33:41 +08:00

3480 lines
117 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.
extends Node
# 变量定义
@onready var grid_container : GridContainer = $GridContainer # 农场地块的 GridContainer
@onready var crop_item : Button = $CopyNodes/CropItem
@onready var pet_patrol_path_line: Line2D = $PetPatrolPathLine #宠物农场巡逻线
#显示信息栏
@onready var show_money : Label = $UI/GUI/GameInfoHBox1/money # 显示当前剩余的钱
@onready var show_experience : Label = $UI/GUI/GameInfoHBox1/experience # 显示当前玩家的经验
@onready var show_level : Label = $UI/GUI/GameInfoHBox1/level # 显示当前玩家的等级
@onready var show_tip : Label = $UI/GUI/GameInfoHBox3/tip # 显示小提示
@onready var show_like: Label = $UI/GUI/GameInfoHBox1/like # 显示别人给自己点赞的总赞数
@onready var show_onlineplayer: Label = $UI/GUI/GameInfoHBox2/onlineplayer # 显示服务器在线人数
@onready var show_player_name : Label = $UI/GUI/GameInfoHBox2/player_name # 显示玩家昵称
@onready var show_farm_name : Label = $UI/GUI/GameInfoHBox2/farm_name # 显示农场名称
@onready var show_status_label : Label = $UI/GUI/GameInfoHBox2/StatusLabel # 显示与服务器连接状态
@onready var show_fps: Label = $UI/GUI/GameInfoHBox2/FPS # 显示游戏FPS
@onready var show_hunger_value :Label = $UI/GUI/GameInfoHBox1/hunger_value # 显示玩家体力值
@onready var global_server_broadcast: Label = $UI/GUI/GameInfoHBox3/GlobalServerBroadcast # 显示全服大喇叭的最新消息,走马灯式滚动显示
@onready var watch_broadcast_button: Button = $UI/GUI/GameInfoHBox3/WatchBroadcast # 查看大喇叭按钮
@onready var network_status_label :Label = get_node("/root/main/UI/BigPanel/TCPNetworkManagerPanel/StatusLabel")
#访问模式按钮
@onready var return_my_farm_button: Button = $UI/GUI/VisitVBox/ReturnMyFarmButton #返回我的农场
@onready var like_button: Button = $UI/GUI/VisitVBox/LikeButton #给别人点赞
#和农场操作相关的按钮
@onready var one_click_harvestbutton: Button = $UI/GUI/FarmVBox/OneClickHarvestButton #一键收获
@onready var one_click_plant_button: Button = $UI/GUI/FarmVBox/OneClickPlantButton #一键种植面板
@onready var player_bag_button: Button = $UI/GUI/FarmVBox/SeedWarehouseButton #打开玩家背包
@onready var add_new_ground_button: Button = $UI/GUI/FarmVBox/AddNewGroundButton #购买新地块
@onready var open_store_button: Button = $UI/GUI/FarmVBox/SeedStoreButton #打开种子商店
#其他一些按钮(暂未分类)
@onready var setting_button: Button = $UI/GUI/OtherVBox/SettingButton #打开设置面板
@onready var lucky_draw_button: Button = $UI/GUI/OtherVBox/LuckyDrawButton #幸运抽奖
@onready var daily_check_in_button: Button = $UI/GUI/OtherVBox/DailyCheckInButton #每日签到
@onready var player_ranking_button: Button = $UI/GUI/OtherVBox/PlayerRankingButton #打开玩家排行榜
@onready var scare_crow_button: Button = $UI/GUI/OtherVBox/ScareCrowButton #打开稻草人面板按钮
@onready var return_main_menu_button: Button = $UI/GUI/OtherVBox/ReturnMainMenuButton #返回主菜单按钮
@onready var new_player_gift_button: Button = $UI/GUI/OtherVBox/NewPlayerGiftButton #领取新手大礼包按钮
@onready var account_setting_button: Button = $UI/GUI/OtherVBox/AccountSettingButton #账户设置按钮
@onready var crop_grid_container : GridContainer = $UI/BigPanel/CropStorePanel/ScrollContainer/Crop_Grid #种子商店格子
@onready var player_bag_grid_container : GridContainer = $UI/BigPanel/PlayerBagPanel/ScrollContainer/Bag_Grid #玩家背包格子
#作物品质按钮
@onready var item_button :Button = $CopyNodes/item_button
#各种面板
#大面板
@onready var lucky_draw_panel: LuckyDrawPanel = $UI/BigPanel/LuckyDrawPanel #幸运抽奖面板
@onready var daily_check_in_panel: DailyCheckInPanel = $UI/BigPanel/DailyCheckInPanel #每日签到面板
@onready var tcp_network_manager_panel: Panel = $UI/BigPanel/TCPNetworkManagerPanel #网络管理器面板
@onready var item_store_panel: Panel = $UI/BigPanel/ItemStorePanel #道具商店面板
@onready var item_bag_panel: Panel = $UI/BigPanel/ItemBagPanel #道具背包面板
@onready var player_bag_panel: Panel = $UI/BigPanel/PlayerBagPanel #种子背包面板
@onready var crop_warehouse_panel: Panel = $UI/BigPanel/CropWarehousePanel #作物仓库面板
@onready var crop_store_panel: Panel = $UI/BigPanel/CropStorePanel #种子商店面板
@onready var player_ranking_panel: Panel = $UI/BigPanel/PlayerRankingPanel #玩家排行榜面板
@onready var login_panel: PanelContainer = $UI/BigPanel/LoginPanel #登录面板
@onready var pet_bag_panel: Panel = $UI/BigPanel/PetBagPanel #宠物背包面板
@onready var pet_store_panel: Panel = $UI/BigPanel/PetStorePanel #宠物商店面板
@onready var pet_fight_panel: Panel = $UI/BigPanel/PetFightPanel #宠物战斗面板
@onready var pet_inform_panel: Panel = $UI/SmallPanel/PetInformPanel #宠物信息面板
#小面板
@onready var land_panel: Panel = $UI/SmallPanel/LandPanel #地块面板
@onready var load_progress_panel: Panel = $UI/SmallPanel/LoadProgressPanel #加载进度面板
@onready var account_setting_panel: Panel = $UI/SmallPanel/AccountSettingPanel #账户设置面板
@onready var one_click_plant_panel: Panel = $UI/SmallPanel/OneClickPlantPanel #一键种植地块面板
@onready var online_gift_panel: Panel = $UI/SmallPanel/OnlineGiftPanel #在线礼包面板
@onready var debug_panel: Panel = $UI/SmallPanel/DebugPanel #调试面板
@onready var global_server_broadcast_panel: Panel = $UI/SmallPanel/GlobalServerBroadcastPanel #全服大喇叭面板
@onready var scare_crow_panel: Panel = $UI/SmallPanel/ScareCrowPanel #农场稻草人设置面板
@onready var wisdom_tree_panel: Panel = $UI/SmallPanel/WisdomTreePanel #智慧树设置面板
#稻草人系统
@onready var scare_crow: Button = $Decoration/ScareCrow #打开农场稻草人设置面板
@onready var scare_crow_image: Sprite2D = $Decoration/ScareCrow/ScareCrowImage #稻草人显示的图片
@onready var scare_crow_name: RichTextLabel = $Decoration/ScareCrow/ScareCrowName #稻草人显示的昵称
@onready var scare_crowtalks: RichTextLabel = $Decoration/ScareCrow/BackgroundPanel/ScareCrowtalks #稻草人显示的话
#智慧树系统
@onready var wisdom_tree_image: Sprite2D = $Decoration/WisdomTree/WisdomTreeImage #智慧树图片从大小从0.5变到1.6
@onready var tree_status: Label = $Decoration/WisdomTree/TreeStatus #智慧树状态 只显示 等级和高度
@onready var anonymous_talk: RichTextLabel = $Decoration/WisdomTree/BackgroundPanel/AnonymousTalk #给智慧树听音乐100%会刷新 施肥浇水
#各种弹窗
@onready var accept_dialog: AcceptDialog = $UI/DiaLog/AcceptDialog
@onready var batch_buy_popup: PanelContainer = $UI/DiaLog/BatchBuyPopup
@onready var load_progress_bar: ProgressBar = $UI/SmallPanel/LoadProgressPanel/LoadProgressBar #显示加载进度进度条
#用于一键隐藏或者显示
@onready var game_info_h_box_1: HBoxContainer = $UI/GUI/GameInfoHBox1
@onready var game_info_h_box_2: HBoxContainer = $UI/GUI/GameInfoHBox2
@onready var game_info_h_box_3: HBoxContainer = $UI/GUI/GameInfoHBox3
@onready var farm_v_box: VBoxContainer = $UI/GUI/FarmVBox
@onready var visit_v_box: VBoxContainer = $UI/GUI/VisitVBox
@onready var other_v_box: VBoxContainer = $UI/GUI/OtherVBox
#玩家基本信息
var money: int = 500 # 默认每个人初始为100元
var experience: float = 0.0 # 初始每个玩家的经验为0
#var grow_speed: float = 1 # 作物生长速度
var level: int = 1 # 初始玩家等级为1
var dig_money : int = 1000 #开垦费用
var stamina: int = 20 # 玩家体力值默认20点
var user_name : String = ""
var user_password : String = ""
var login_data : Dictionary = {}
#var data : Dictionary = {}
var start_game : bool = false
# 种子背包数据
var player_bag : Array = []
# 作物仓库数据
var crop_warehouse : Array = []
# 道具背包数据
var item_bag : Array = []
# 宠物背包数据
var pet_bag : Array = []
# 巡逻宠物数据
var patrol_pets : Array = []
# 出战宠物数据
var battle_pets : Array = []
#农作物种类JSON
var can_planted_crop : Dictionary = {}
#道具配置数据
var item_config_data : Dictionary = {}
# 新手大礼包领取状态
var new_player_gift_claimed : bool = false
# 访问模式相关变量
var is_visiting_mode : bool = false # 是否处于访问模式
var original_player_data : Dictionary = {} # 保存原始玩家数据
var visited_player_data : Dictionary = {} # 被访问玩家的数据
# 作物图片缓存
var crop_textures_cache : Dictionary = {} # 缓存已加载的作物图片
var crop_frame_counts : Dictionary = {} # 记录每种作物的帧数
var crop_mature_textures_cache : Dictionary = {} # 缓存已加载的作物成熟图片
# FPS显示相关变量
var fps_timer: float = 0.0 # FPS更新计时器
var fps_update_interval: float = 0.5 # FPS更新间隔
var frame_count: int = 0 # 帧数计数器
var current_fps: float = 0.0 # 当前FPS值
var client_version :String = GlobalVariables.client_version #记录客户端版本
#五秒计时器
var five_timer = 0.0
var five_interval = 5.0
#一秒计时器
var one_timer: float = 0.0
var one_interval: float = 1.0
# 稻草人话语切换相关
var scare_crow_talk_index: int = 0
var scare_crow_talk_timer: float = 0.0
var scare_crow_talk_interval: float = 5.0 # 每5秒切换一次
var scare_crow_talks_list: Array = []
#=======================临时变量=======================
# 道具选择状态
var selected_item_name : String = ""
var is_item_selected : bool = false
# 当前被选择的地块索引
var selected_lot_index : int = -1
var farm_lots : Array = [] # 用于保存每个地块的状态
var dig_index : int = 0
var climate_death_timer : int = 0
#=======================临时变量=======================
#=======================脚本基础方法=======================
func _ready():
# 显示加载进度面板隐藏其他所有UI
load_progress_panel.show()
load_progress_bar.value = 0
# 初始化调试面板(默认隐藏)
debug_panel.hide()
debug_panel_script = debug_panel
# 在加载进度面板上添加调试按钮
_add_debug_button_to_loading_panel()
#未登录时隐藏所有UI
game_info_h_box_1.hide()
game_info_h_box_2.hide()
game_info_h_box_3.hide()
farm_v_box.hide()
visit_v_box.hide()
other_v_box.hide()
# 隐藏面板
crop_store_panel.hide()
player_bag_panel.hide()
crop_warehouse_panel.hide()
item_bag_panel.hide()
item_store_panel.hide()
lucky_draw_panel.hide()
daily_check_in_panel.hide()
player_ranking_panel.hide()
one_click_plant_panel.hide()
account_setting_panel.hide()
global_server_broadcast_panel.hide()
accept_dialog.hide()
_update_ui()
_create_farm_buttons() # 创建地块按钮
_update_farm_lots_state() # 初始更新地块状态
# 先尝试加载本地数据进行快速初始化
_load_local_crop_data()
_load_local_item_config()
#==================================初始化比较重要的几个面板==================================
# 初始化种子仓库UI
player_bag_panel.init_player_bag()
# 初始化作物仓库UI
crop_warehouse_panel.init_crop_warehouse()
# 初始化道具背包UI
item_bag_panel.init_item_bag()
# 初始化种子商店
crop_store_panel.init_store()
# 初始化道具商店UI
item_store_panel.init_item_store()
#==================================初始化比较重要的几个面板==================================
# 连接AcceptDialog的确认信号
accept_dialog.confirmed.connect(_on_accept_dialog_confirmed)
# 启动在线人数更新定时器
_start_online_players_timer()
# 预加载所有作物图片(带进度显示)
await _preload_all_crop_textures()
# 启动后稍等片刻尝试从服务器获取最新数据
await get_tree().create_timer(0.5).timeout
_try_load_from_server()
func _process(delta: float) -> void:
# FPS计算和显示
fps_timer += delta
frame_count += 1
#更新一次FPS显示
if fps_timer >= fps_update_interval:
# 计算FPS帧数 / 时间间隔
current_fps = frame_count / fps_timer
# 更新FPS显示保留1位小数
show_fps.text = "FPS: " + str("%d" % current_fps)
# 根据FPS值设置颜色
if current_fps >= 50:
show_fps.modulate = Color.GREEN # 绿色:流畅
elif current_fps >= 30:
show_fps.modulate = Color.YELLOW # 黄色:一般
elif current_fps >= 20:
show_fps.modulate = Color.ORANGE # 橙色:较卡
else:
show_fps.modulate = Color.RED # 红色:卡顿
# 重置计数器
fps_timer = 0.0
frame_count = 0
# 检查ESC键取消一键种植地块选择模式
if Input.is_action_just_pressed("ui_cancel"):
if one_click_plant_panel and one_click_plant_panel.has_method("cancel_lot_selection"):
one_click_plant_panel.cancel_lot_selection()
pass
#每时每刻都更新
func _physics_process(delta):
#1秒计时器
one_timer += delta
if one_timer >= one_interval:
one_timer = 0.0 # 重置计时器
#同步网络管理器的状态
show_status_label.text = "服务器状态:"+network_status_label.text
show_status_label.modulate = network_status_label.modulate
if start_game == true:
_update_farm_lots_state() # 更新地块状态不重新创建UI
#访客模式处理
if is_visiting_mode:
farm_v_box.hide()
visit_v_box.show()
other_v_box.hide()
pass
else:
game_info_h_box_1.show()
game_info_h_box_2.show()
game_info_h_box_3.show()
farm_v_box.show()
visit_v_box.hide()
other_v_box.show()
pass
pass
#5秒计时器
five_timer += delta
if five_timer >= five_interval:
five_timer = 0.0 # 重置计时器
show_tip.text = _random_small_game_tips()
# 稻草人话语切换计时器
if scare_crow_talks_list.size() > 0 and scare_crow.visible:
scare_crow_talk_timer += delta
if scare_crow_talk_timer >= scare_crow_talk_interval:
scare_crow_talk_timer = 0.0
_update_scare_crow_talk()
func _input(event):
if event is InputEventKey and event.pressed:
var key_code = event.keycode
if key_code == KEY_F10:
# 显示调试面板
if debug_panel:
debug_panel.visible = !debug_panel.visible
elif key_code == KEY_F11:
# 切换全屏模式
if DisplayServer.window_get_mode() == DisplayServer.WINDOW_MODE_FULLSCREEN:
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED)
else:
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN)
elif key_code == KEY_F12:
# 截图
print("截图功能暂未实现")
#=======================脚本基础方法=======================
#==========================玩家排行榜+访问模式处理============================
# 处理玩家排行榜响应
func _handle_player_rankings_response(data):
player_ranking_panel.handle_player_rankings_response(data)
# 处理玩家游玩时间响应
func _handle_play_time_response(data):
player_ranking_panel.handle_play_time_response(data)
# 处理访问玩家响应
func _handle_visit_player_response(data):
var success = data.get("success", false)
var message = data.get("message", "")
if success:
var target_player_data = data.get("player_data", {})
# 保存当前玩家数据
if not is_visiting_mode:
original_player_data = {
"user_name": user_name,
"player_name": show_player_name.text.replace("玩家昵称:", ""),
"farm_name": show_farm_name.text.replace("农场名称:", ""),
"level": level,
"money": money,
"experience": experience,
"stamina": stamina,
"farm_lots": farm_lots.duplicate(true),
"player_bag": player_bag.duplicate(true)
}
# 切换到访问模式
is_visiting_mode = true
visited_player_data = target_player_data
# 更新显示数据
money = target_player_data.get("money", 0)
experience = target_player_data.get("experience", 0)
level = target_player_data.get("level", 1)
stamina = target_player_data.get("体力值", 20)
farm_lots = target_player_data.get("farm_lots", [])
player_bag = target_player_data.get("player_bag", [])
crop_warehouse = target_player_data.get("作物仓库", [])
item_bag = target_player_data.get("道具背包", [])
pet_bag = target_player_data.get("宠物背包", [])
patrol_pets = target_player_data.get("巡逻宠物", [])
# 更新UI显示
show_player_name.text = "玩家昵称:" + target_player_data.get("player_name", "未知")
show_farm_name.text = "农场名称:" + target_player_data.get("farm_name", "未知农场")
# 显示被访问玩家的点赞数
var target_likes = target_player_data.get("total_likes", 0)
show_like.text = "点赞数:" + str(int(target_likes))
_update_ui()
# 重新创建地块按钮以显示被访问玩家的农场
_create_farm_buttons()
_update_farm_lots_state()
# 更新背包UI
if player_bag_panel and player_bag_panel.has_method("update_player_bag_ui"):
player_bag_panel.update_player_bag_ui()
# 更新作物仓库UI
if crop_warehouse_panel and crop_warehouse_panel.has_method("update_crop_warehouse_ui"):
crop_warehouse_panel.update_crop_warehouse_ui()
# 更新道具背包UI
if item_bag_panel and item_bag_panel.has_method("update_item_bag_ui"):
item_bag_panel.update_item_bag_ui()
# 更新宠物背包UI
if pet_bag_panel and pet_bag_panel.has_method("update_pet_bag_ui"):
pet_bag_panel.update_pet_bag_ui()
# 初始化巡逻宠物(访问模式)
if has_method("init_patrol_pets"):
init_patrol_pets()
# 更新稻草人显示(访问模式)
update_scare_crow_display()
# 更新智慧树配置显示(访问模式)
if target_player_data.has("智慧树配置") and target_player_data["智慧树配置"] != null:
# 确保智慧树配置格式正确
var target_wisdom_config = target_player_data["智慧树配置"]
if target_wisdom_config is Dictionary:
target_wisdom_config = _ensure_wisdom_tree_config_format(target_wisdom_config)
# 更新智慧树显示
_update_wisdom_tree_display(target_wisdom_config)
else:
print("智慧树配置不是Dictionary类型", typeof(target_wisdom_config))
else:
print("目标玩家没有智慧树配置或配置为空")
# 隐藏排行榜面板
if player_ranking_panel:
player_ranking_panel.hide()
Toast.show("正在访问 " + target_player_data.get("player_name", "未知") + " 的农场", Color.CYAN)
else:
Toast.show("访问失败:" + message, Color.RED)
print("访问玩家失败:", message)
# 处理返回自己农场响应
func _handle_return_my_farm_response(data):
var success = data.get("success", false)
var message = data.get("message", "")
if success:
var player_data = data.get("player_data", {})
# 恢复玩家数据
money = player_data.get("money", 500)
experience = player_data.get("experience", 0)
level = player_data.get("level", 1)
stamina = player_data.get("体力值", 20)
farm_lots = player_data.get("farm_lots", [])
player_bag = player_data.get("player_bag", [])
crop_warehouse = player_data.get("作物仓库", [])
item_bag = player_data.get("道具背包", [])
pet_bag = player_data.get("宠物背包", [])
patrol_pets = player_data.get("巡逻宠物", [])
# 恢复UI显示
show_player_name.text = "玩家昵称:" + player_data.get("player_name", "未知")
show_farm_name.text = "农场名称:" + player_data.get("farm_name", "我的农场")
# 显示自己的点赞数
var my_likes = player_data.get("total_likes", 0)
show_like.text = "总赞数:" + str(int(my_likes))
# 退出访问模式
is_visiting_mode = false
visited_player_data.clear()
original_player_data.clear()
# 更新UI
_update_ui()
# 重新创建地块按钮以显示自己的农场
_create_farm_buttons()
_update_farm_lots_state()
# 更新背包UI
if player_bag_panel and player_bag_panel.has_method("update_player_bag_ui"):
player_bag_panel.update_player_bag_ui()
# 更新作物仓库UI
if crop_warehouse_panel and crop_warehouse_panel.has_method("update_crop_warehouse_ui"):
crop_warehouse_panel.update_crop_warehouse_ui()
# 更新道具背包UI
if item_bag_panel and item_bag_panel.has_method("update_item_bag_ui"):
item_bag_panel.update_item_bag_ui()
# 更新宠物背包UI
if pet_bag_panel and pet_bag_panel.has_method("update_pet_bag_ui"):
pet_bag_panel.update_pet_bag_ui()
# 初始化巡逻宠物(返回自己农场)
if has_method("init_patrol_pets"):
init_patrol_pets()
# 更新稻草人显示(返回自己农场)
update_scare_crow_display()
# 恢复智慧树显示(返回自己农场)
if player_data.has("智慧树配置") and player_data["智慧树配置"] != null:
var my_wisdom_config = player_data["智慧树配置"]
if my_wisdom_config is Dictionary:
my_wisdom_config = _ensure_wisdom_tree_config_format(my_wisdom_config)
# 更新本地智慧树配置
login_data["智慧树配置"] = my_wisdom_config
# 恢复智慧树显示
update_wisdom_tree_display()
Toast.show("已返回自己的农场", Color.GREEN)
else:
Toast.show("返回农场失败:" + message, Color.RED)
print("返回农场失败:", message)
#访客模式下返回我的农场
func _on_return_my_farm_button_pressed() -> void:
# 如果当前处于访问模式,返回自己的农场
if is_visiting_mode:
return_to_my_farm()
else:
# 如果不在访问模式,这个按钮可能用于其他功能或者不做任何操作
print("当前已在自己的农场")
# 返回自己的农场
func return_to_my_farm():
if not is_visiting_mode:
return
# 发送返回自己农场的请求到服务器
if tcp_network_manager_panel and tcp_network_manager_panel.has_method("sendReturnMyFarm"):
var success = tcp_network_manager_panel.sendReturnMyFarm()
if success:
print("已发送返回自己农场的请求")
else:
Toast.show("网络未连接,无法返回农场", Color.RED)
print("发送返回农场请求失败,网络未连接")
else:
Toast.show("网络管理器不可用", Color.RED)
print("网络管理器不可用")
#==========================玩家排行榜+访问模式处理============================
#===============================================这个函数也比较重要===============================================
# 处理地块点击事件
func _on_item_selected(index):
# 检查是否处于一键种植的地块选择模式
if one_click_plant_panel.on_lot_selected(index):
return
# 检查是否有道具被选择,如果有则使用道具
if is_item_selected and selected_item_name != "":
_use_item_on_lot(index, selected_item_name)
return
# 正常模式下,先设置地块索引,再打开土地面板
land_panel.selected_lot_index = index
selected_lot_index = index
land_panel.show_panel()
land_panel._update_button_texts()
#===============================================这个函数也比较重要===============================================
#========================================杂项未分类函数=======================================
#随机游戏提示
func _random_small_game_tips() -> String:
const game_tips = [
"按住wsad可以移动游戏画面",
"使用鼠标滚轮来缩放游戏画面",
"移动端双指缩放游戏画面",
"不要一上来就花光你的初始资金",
"钱币是目前游戏唯一货币",
"每隔一小时体力值+1",
"不要忘记领取你的新手礼包!",
"记得使用一键截图来分享你的农场",
"新注册用户可享受三天10倍速作物生长",
"偷别人菜时不要忘了给别人浇水哦",
"你能分得清小麦和稻谷吗",
"凌晨刷新体力值",
"面板左上角有刷新按钮,可以刷新面板",
"小心偷菜被巡逻宠物发现"
]
var random_index = randi() % game_tips.size()
var selected_tip = game_tips[random_index]
return selected_tip
# 处理服务器作物更新消息
func _handle_crop_update(update_data):
# 检查是否是访问模式的更新
var is_visiting_update = update_data.get("is_visiting", false)
if is_visiting_update and is_visiting_mode:
# 访问模式下的更新,更新被访问玩家的农场数据
farm_lots = update_data["farm_lots"]
elif not is_visiting_update and not is_visiting_mode:
# 正常模式下的更新,更新自己的农场数据
farm_lots = update_data["farm_lots"]
else:
# 状态不匹配,忽略更新
print("忽略不匹配的作物更新,当前访问模式:", is_visiting_mode, ",更新类型:", is_visiting_update)
return
# 更新UI显示
_update_farm_lots_state()
# 修复背包数据确保所有物品都有quality字段
func _fix_player_bag_data():
if not player_bag:
return
print("检查并修复背包数据...")
var fixed_count = 0
for i in range(player_bag.size()):
var item = player_bag[i]
# 如果物品缺少quality字段尝试从作物数据中获取或设置默认值
if not item.has("quality"):
var item_name = item.get("name", "")
var quality = "普通" # 默认质量
# 尝试从作物数据中获取质量
if can_planted_crop.has(item_name):
quality = can_planted_crop[item_name].get("品质", "普通")
item["quality"] = quality
fixed_count += 1
print("修复背包物品 [", item_name, "] 的质量字段为:", quality)
if fixed_count > 0:
print("背包数据修复完成,共修复 ", fixed_count, " 个物品")
else:
print("背包数据检查完成,无需修复")
# 处理登录成功
func handle_login_success(player_data: Dictionary):
# 修复背包数据兼容性问题
_fix_player_bag_data()
# 更新新手大礼包状态
new_player_gift_claimed = player_data.get("new_player_gift_claimed", false)
# 根据新手大礼包状态控制按钮显示
var new_player_gift_button = find_child("NewPlayerGiftButton")
if new_player_gift_button:
if new_player_gift_claimed:
new_player_gift_button.hide()
else:
new_player_gift_button.show()
# 立即请求在线人数
if tcp_network_manager_panel and tcp_network_manager_panel.is_connected_to_server():
tcp_network_manager_panel.sendGetOnlinePlayers()
print("登录成功后请求在线人数更新")
# 其他登录成功后的初始化逻辑可以在这里添加
start_game = true
# 登录成功后初始化大喇叭显示
_init_broadcast_display()
# 初始化稻草人显示
init_scare_crow_config()
# 初始化智慧树显示
update_wisdom_tree_display()
# 立即请求服务器历史消息以刷新显示
call_deferred("_request_server_history_for_refresh")
#创建作物按钮
func _create_crop_button(crop_name: String, crop_quality: String) -> Button:
# 根据品质选择相应的进度条
var button = item_button.duplicate()
match crop_quality:
"普通":
button.get_node("Title").modulate = Color.HONEYDEW#白色
"优良":
button.get_node("Title").modulate =Color.DODGER_BLUE#深蓝色
"稀有":
button.get_node("Title").modulate =Color.HOT_PINK#品红色
"史诗":
button.get_node("Title").modulate =Color.YELLOW#黄色
"传奇":
button.get_node("Title").modulate =Color.ORANGE_RED#红色
# 确保按钮可见并可点击
button.visible = true
button.disabled = false
button.focus_mode = Control.FOCUS_ALL
# 设置按钮文本
button.text = str(crop_quality + "-" + crop_name)
# 如果按钮有标题标签,设置标题
if button.has_node("Title"):
button.get_node("Title").text = crop_quality
return button
# 初始化农场地块按钮 - 只在游戏开始时调用一次
func _create_farm_buttons():
# 清空当前显示的地块
for child in grid_container.get_children():
child.queue_free()
# 创建所有地块按钮
for i in range(len(farm_lots)):
var button = crop_item.duplicate()
button.name = "FarmLot_" + str(i)
button.connect("pressed", Callable(self, "_on_item_selected").bind(i))
grid_container.add_child(button)
# 更新农场地块状态
func _update_farm_lots_state():
var digged_count = 0
var land_colors = {0: Color.WHITE, 1: Color(1.0, 1.0, 0.0), 2: Color(1.0, 0.41, 0.0), 3: Color(0.55, 0.29, 0.97), 4: Color(0.33, 0.4, 0.59)}
var quality_colors = {"普通": Color.HONEYDEW, "优良": Color.DODGER_BLUE, "稀有": Color.HOT_PINK, "史诗": Color.YELLOW, "传奇": Color.ORANGE_RED}
for i in range(len(farm_lots)):
if i >= grid_container.get_child_count():
break
var lot = farm_lots[i]
var button = grid_container.get_child(i)
var label = button.get_node("crop_name")
var ground_image = button.get_node("ground_sprite")
var status_label = button.get_node("status_label")
var progressbar = button.get_node("ProgressBar")
_update_lot_crop_sprite(button, lot)
ground_image.self_modulate = land_colors.get(int(lot.get("土地等级", 0)), Color.WHITE)
if not lot["is_diged"]:
label.show()
label.modulate = Color.WEB_GRAY
label.text = "[未开垦]"
progressbar.hide()
status_label.text = ""
button.tooltip_text = ""
elif not lot["is_planted"]:
digged_count += 1
label.show()
label.modulate = Color.GREEN
label.text = "[空地]"
progressbar.hide()
status_label.text = ""
button.tooltip_text = ""
elif lot["is_dead"]:
digged_count += 1
label.show()
label.modulate = Color.NAVY_BLUE
label.text = "[" + lot["crop_type"] + "已死亡]"
progressbar.hide()
status_label.text = ""
button.tooltip_text = ""
else:
digged_count += 1
var crop_name = lot["crop_type"]
var is_weed = can_planted_crop.has(crop_name) and can_planted_crop[crop_name].get("是否杂草", false)
if is_weed:
label.hide()
progressbar.hide()
status_label.text = ""
button.tooltip_text = ""
else:
label.show()
progressbar.show()
if can_planted_crop.has(crop_name):
var crop_quality = can_planted_crop[crop_name]["品质"]
label.text = "[" + crop_quality + "-" + crop_name + "]"
label.modulate = quality_colors.get(crop_quality, Color.WHITE)
else:
label.text = "[" + crop_name + "]"
label.modulate = Color.WHITE
progressbar.max_value = int(lot["max_grow_time"])
progressbar.value = int(lot["grow_time"])
var status_indicators = []
var current_time = Time.get_unix_time_from_system()
var last_water_time = lot.get("浇水时间", 0)
if current_time - last_water_time < 3600:
status_indicators.append("已浇水")
if lot.get("已施肥", false):
status_indicators.append("已施肥")
status_label.text = " ".join(status_indicators)
if can_planted_crop.has(crop_name):
var crop = can_planted_crop[crop_name]
var grow_time = int(crop["生长时间"])
var days = grow_time / 86400
var hours = (grow_time % 86400) / 3600
var minutes = (grow_time % 3600) / 60
var seconds = grow_time % 60
var time_str = ""
if days > 0: time_str += str(days) + ""
if hours > 0: time_str += str(hours) + "小时"
if minutes > 0: time_str += str(minutes) + "分钟"
if seconds > 0: time_str += str(seconds) + ""
if time_str == "": time_str = "0秒"
button.tooltip_text = "作物: " + crop_name + "\n品质: " + crop.get("品质", "未知") + "\n价格: " + str(crop["花费"]) + "\n成熟时间: " + time_str + "\n收获收益: " + str(crop["收益"]) + "\n需求等级: " + str(crop["等级"]) + "\n经验: " + str(crop["经验"]) + ""
else:
button.tooltip_text = "作物: " + crop_name + "\n作物数据未找到"
dig_money = digged_count * 1000
# 仅在加载游戏或特定情况下完全刷新地块 - 用于与服务器同步时
func _refresh_farm_lots():
_create_farm_buttons()
_update_farm_lots_state()
# 更新玩家信息显示
func _update_ui():
show_money.text = "钱币数:" + str(money) + ""
show_experience.text = "经验值:" + str(experience) + ""
show_level.text = "等级:" + str(level) + ""
show_hunger_value.text = "体力值:" + str(stamina)
# 显示点赞数
var my_likes = login_data.get("total_likes", 0)
show_like.text = "点赞数:" + str(int(my_likes))
#打开玩家排行榜面板
func _on_player_ranking_button_pressed() -> void:
player_ranking_panel.show()
player_ranking_panel.request_player_rankings()
pass
#打开设置面板 暂时没想到可以设置什么
func _on_setting_button_pressed() -> void:
pass
#查看全服大喇叭按钮点击事件
func _on_watch_broadcast_button_pressed() -> void:
# 显示全服大喇叭面板
global_server_broadcast_panel.show()
global_server_broadcast_panel.move_to_front()
# 处理AcceptDialog的确认信号
func _on_accept_dialog_confirmed() -> void:
var action_type = accept_dialog.get_meta("action_type", "")
if action_type == "one_click_harvest":
# 执行一键收获逻辑
_execute_one_click_harvest()
elif action_type == "buy_new_ground":
# 执行购买新地块逻辑
_execute_buy_new_ground()
elif action_type == "return_main_menu":
# 执行返回主菜单逻辑
_execute_return_main_menu()
else:
# 处理其他类型的确认逻辑
pass
#打开一键种植面板
func _on_one_click_plant_button_pressed() -> void:
# 如果处于访问模式,不允许操作
if is_visiting_mode:
Toast.show("访问模式下无法使用一键种植", Color.ORANGE)
return
# 显示一键种植面板
one_click_plant_panel.show()
one_click_plant_panel.move_to_front()
# 处理连接断开事件
func _on_connection_lost():
# 重置游戏状态
start_game = false
# 隐藏所有游戏UI
game_info_h_box_1.hide()
game_info_h_box_2.hide()
game_info_h_box_3.hide()
farm_v_box.hide()
visit_v_box.hide()
other_v_box.hide()
# 隐藏所有面板
crop_store_panel.hide()
player_bag_panel.hide()
lucky_draw_panel.hide()
daily_check_in_panel.hide()
player_ranking_panel.hide()
one_click_plant_panel.hide()
global_server_broadcast_panel.hide()
land_panel.hide()
accept_dialog.hide()
# 重置访问模式
if is_visiting_mode:
_handle_return_my_farm_response({"success": true})
# 显示登录面板
login_panel.show()
#========================================杂项未分类函数=======================================
#==========================打开基础面板================================
#打开种子商店面板
func _on_open_store_button_pressed() -> void:
# 如果处于访问模式,不允许打开商店
if is_visiting_mode:
Toast.show("访问模式下无法使用商店", Color.ORANGE)
return
# 确保商店面板已初始化
crop_store_panel.init_store()
# 显示商店面板
crop_store_panel.show()
# 确保在最前面显示
crop_store_panel.move_to_front()
pass
#打开种子仓库面板
func _on_seed_warehouse_button_pressed() -> void:
player_bag_panel.show()
#打开玩家道具背包面板
func _on_item_bag_button_pressed() -> void:
item_bag_panel.show()
#打开道具商店面板
func _on_item_store_button_pressed() -> void:
item_store_panel.show()
#打开作物仓库面板
func _on_crop_warehouse_button_pressed() -> void:
crop_warehouse_panel.show()
#打开宠物背包面板
func _on_pet_bag_button_pressed() -> void:
pet_bag_panel.show()
pass
#打开宠物商店面板
func _on_pet_store_button_pressed() -> void:
pet_store_panel.show()
pass
#==========================打开基础面板================================
#===============================================初始化数据处理===============================================
# 从服务器获取作物数据
func _load_crop_data():
var network_manager = get_node("/root/main/UI/TCPNerworkManager")
if network_manager and network_manager.is_connected_to_server():
# 从服务器请求作物数据
network_manager.sendGetCropData()
else:
# 如果无法连接服务器,尝试加载本地数据
print("无法连接服务器,尝试加载本地作物数据...")
_load_local_crop_data()
# 尝试从服务器加载最新数据
#玩家登录后在后台把服务器的配置文件通过网络覆写到本地config里面 然后也使用服务器的配置
func _try_load_from_server():
if tcp_network_manager_panel and tcp_network_manager_panel.is_connected_to_server():
# 从服务器请求最新作物数据
print("尝试从服务器获取最新作物数据...")
tcp_network_manager_panel.sendGetCropData()
# 从服务器请求最新道具配置数据
print("尝试从服务器获取最新道具配置数据...")
tcp_network_manager_panel.sendGetItemConfig()
else:
print("服务器未连接,使用当前本地数据")
# 处理服务器作物数据响应
func _handle_crop_data_response(response_data):
var success = response_data.get("success", false)
if success:
var crop_data = response_data.get("crop_data", {})
if crop_data:
# 保存到本地文件
_save_crop_data_to_local(crop_data)
# 设置全局变量
can_planted_crop = crop_data
print("作物数据已从服务器更新")
# 重新初始化商店和背包UI因为现在有了作物数据
_refresh_ui_after_crop_data_loaded()
else:
print("服务器返回的作物数据为空")
_load_local_crop_data()
else:
var message = response_data.get("message", "未知错误")
print("从服务器获取作物数据失败:", message)
_load_local_crop_data()
# 从本地文件加载作物数据(备用方案)
func _load_local_crop_data():
# 优先尝试加载用户目录下的缓存文件
var file = FileAccess.open("user://crop_data.json", FileAccess.READ)
if file:
var json_text = file.get_as_text()
file.close()
var json = JSON.new()
var parse_result = json.parse(json_text)
if parse_result == OK:
can_planted_crop = json.get_data()
print("已加载本地缓存的作物数据")
_refresh_ui_after_crop_data_loaded()
return
else:
print("本地缓存作物数据JSON解析错误", json.get_error_message())
_refresh_ui_after_crop_data_loaded()
# 作物数据加载后刷新UI
func _refresh_ui_after_crop_data_loaded():
# 重新初始化商店和背包UI因为现在有了作物数据
crop_store_panel.init_store()
print("种子商店已根据作物数据重新初始化")
player_bag_panel.update_player_bag_ui()
print("种子背包已根据作物数据重新初始化")
crop_warehouse_panel.update_crop_warehouse_ui()
print("作物仓库已根据作物数据重新初始化")
item_bag_panel.update_item_bag_ui()
print("道具背包已根据作物数据重新初始化")
item_store_panel.init_item_store()
print("道具商店已根据作物数据重新初始化")
# 保存作物数据到本地文件
func _save_crop_data_to_local(crop_data):
var file = FileAccess.open("user://crop_data.json", FileAccess.WRITE)
if not file:
print("无法创建本地作物数据缓存文件!")
return
var json_string = JSON.stringify(crop_data, "\t")
file.store_string(json_string)
file.close()
print("作物数据已保存到本地缓存")
# 保存道具配置数据到本地文件
func _save_item_config_to_local(config_data):
var file = FileAccess.open("user://item_config.json", FileAccess.WRITE)
if not file:
print("无法创建本地道具配置缓存文件!")
return
var json_string = JSON.stringify(config_data, "\t")
file.store_string(json_string)
file.close()
print("道具配置数据已保存到本地缓存")
# 从本地文件加载道具配置数据(备用方案)
func _load_local_item_config():
# 优先尝试加载用户目录下的缓存文件
var file = FileAccess.open("user://item_config.json", FileAccess.READ)
if file:
var json_text = file.get_as_text()
file.close()
var json = JSON.new()
var parse_result = json.parse(json_text)
if parse_result == OK:
item_config_data = json.get_data()
print("已加载本地缓存的道具配置数据")
_refresh_ui_after_item_config_loaded()
return
else:
print("本地缓存道具配置数据JSON解析错误", json.get_error_message())
_refresh_ui_after_item_config_loaded()
# 道具配置数据加载后刷新UI
func _refresh_ui_after_item_config_loaded():
# 重新初始化道具相关UI
item_store_panel.init_item_store()
print("道具商店已根据道具配置数据重新初始化")
item_bag_panel.update_item_bag_ui()
print("道具背包已根据道具配置数据重新初始化")
# 处理服务器道具配置响应
func _handle_item_config_response(response_data):
var success = response_data.get("success", false)
if success:
var config_data = response_data.get("item_config", {})
if config_data:
# 保存到本地文件
_save_item_config_to_local(config_data)
# 设置全局变量
item_config_data = config_data
print("道具配置数据已从服务器更新,道具种类:", item_config_data.size())
# 重新初始化道具相关UI
_refresh_ui_after_item_config_loaded()
else:
print("服务器返回的道具配置数据为空")
_load_local_item_config()
else:
var message = response_data.get("message", "未知错误")
print("从服务器获取道具配置数据失败:", message)
_load_local_item_config()
#===============================================初始化数据处理===============================================
#===============================================作物图片缓存系统===============================================
## 优化的作物图片缓存和管理系统
class CropTextureManager:
# 缓存字典
var texture_cache: Dictionary = {} # 序列帧缓存 {crop_name: [Texture2D]}
var mature_texture_cache: Dictionary = {} # 成熟图片缓存 {crop_name: Texture2D}
var frame_counts: Dictionary = {} # 帧数记录 {crop_name: int}
var failed_resources: Array = [] # 记录加载失败的资源路径
# 加载状态
var is_loading: bool = false
var load_progress: float = 0.0
var total_crops: int = 0
var loaded_crops: int = 0
var failed_crops: int = 0
# 线程管理
var worker_threads: Array = []
var max_threads: int = 4 # 最大线程数
var loading_queue: Array = []
var loading_mutex: Mutex
var results_mutex: Mutex
var completed_results: Array = []
# 调试面板引用
var debug_panel_ref = null
# 内存管理
var max_cache_size: int = 300 # 最大缓存图片数量
var cache_access_order: Array = [] # LRU缓存访问顺序
func _init():
print("[CropTextureManager] 初始化优化的作物图片管理器")
loading_mutex = Mutex.new()
results_mutex = Mutex.new()
# 根据设备性能动态调整线程数
_adjust_thread_count()
# 尝试获取调试面板引用
_connect_debug_panel()
## 根据设备性能调整线程数
func _adjust_thread_count():
var platform = OS.get_name()
var processor_count = OS.get_processor_count()
match platform:
"Android", "iOS":
# 移动设备使用较少线程,避免过热和电量消耗
max_threads = min(3, max(1, processor_count / 2))
"Windows", "Linux", "macOS":
# 桌面设备可以使用更多线程
max_threads = min(6, max(2, processor_count - 1))
_:
max_threads = 3
print("[CropTextureManager] 设备: %s, CPU核心: %d, 使用线程数: %d" % [platform, processor_count, max_threads])
## 连接调试面板
func _connect_debug_panel():
# 延迟获取调试面板引用,因为初始化时可能还未创建
call_deferred("_try_get_debug_panel")
## 尝试获取调试面板引用
func _try_get_debug_panel():
var main_node = Engine.get_main_loop().current_scene
if main_node:
debug_panel_ref = main_node.get_node_or_null("UI/SmallPanel/DebugPanel")
if debug_panel_ref:
print("[CropTextureManager] 已连接到调试面板")
## 向调试面板发送消息
func _send_debug_message(message: String, color: Color = Color.WHITE):
if debug_panel_ref and debug_panel_ref.has_method("add_debug_message"):
debug_panel_ref.add_debug_message(message, color)
## 设置当前加载项目
func _set_current_loading_item(item_name: String):
if debug_panel_ref and debug_panel_ref.has_method("set_current_loading_item"):
debug_panel_ref.set_current_loading_item(item_name)
## 异步预加载所有作物图片 - 主要入口函数
func preload_all_textures_async(crop_data: Dictionary, progress_callback: Callable) -> void:
if is_loading:
print("[CropTextureManager] 正在加载中,跳过重复请求")
return
is_loading = true
load_progress = 0.0
total_crops = crop_data.size()
loaded_crops = 0
failed_crops = 0
failed_resources.clear()
completed_results.clear()
print("[CropTextureManager] 开始预加载 %d 种作物图片" % total_crops)
_send_debug_message("开始预加载 %d 种作物图片" % total_crops, Color.CYAN)
# 阶段1加载默认图片 (0-10%)
progress_callback.call(0, "正在加载默认图片...")
_send_debug_message("阶段1: 加载默认图片", Color.YELLOW)
await _load_default_textures_async()
progress_callback.call(10, "默认图片加载完成")
_send_debug_message("默认图片加载完成", Color.GREEN)
# 阶段2多线程批量加载作物图片 (10-90%)
_send_debug_message("阶段2: 多线程加载作物图片", Color.YELLOW)
await _load_crops_multithreaded_async(crop_data, progress_callback)
# 阶段3完成 (90-100%)
progress_callback.call(100, "所有作物图片加载完成!")
_print_cache_stats()
_send_debug_message("所有作物图片加载完成!", Color.GREEN)
# 清理线程
await _cleanup_threads()
is_loading = false
var success_message = "预加载完成,成功: %d, 失败: %d" % [loaded_crops, failed_crops]
print("[CropTextureManager] " + success_message)
_send_debug_message(success_message, Color.CYAN)
## 多线程批量异步加载作物图片
func _load_crops_multithreaded_async(crop_data: Dictionary, progress_callback: Callable) -> void:
var crop_names = crop_data.keys()
# 准备加载队列
loading_mutex.lock()
loading_queue.clear()
for crop_name in crop_names:
loading_queue.append({
"crop_name": crop_name,
"type": "sequence"
})
loading_queue.append({
"crop_name": crop_name,
"type": "mature"
})
loading_mutex.unlock()
# 启动工作线程
_send_debug_message("启动 %d 个工作线程" % max_threads, Color.CYAN)
for i in range(max_threads):
var thread = Thread.new()
worker_threads.append(thread)
thread.start(_worker_thread_function)
# 监控进度
var total_tasks = loading_queue.size()
var last_completed = 0
while true:
# 处理完成的结果
results_mutex.lock()
var current_results = completed_results.duplicate()
completed_results.clear()
results_mutex.unlock()
# 应用加载结果
for result in current_results:
_apply_loading_result(result)
loaded_crops += 1
# 更新进度
var completed_tasks = total_tasks - loading_queue.size()
if completed_tasks != last_completed:
var progress = 10 + int((float(completed_tasks) / float(total_tasks)) * 80)
var message = "多线程加载中... (%d/%d)" % [completed_tasks, total_tasks]
progress_callback.call(progress, message)
last_completed = completed_tasks
# 检查是否完成
loading_mutex.lock()
var queue_empty = loading_queue.is_empty()
loading_mutex.unlock()
if queue_empty and completed_results.is_empty():
break
# 短暂等待
await Engine.get_main_loop().process_frame
## 工作线程函数
func _worker_thread_function():
while true:
# 获取任务
loading_mutex.lock()
if loading_queue.is_empty():
loading_mutex.unlock()
break
var task = loading_queue.pop_front()
loading_mutex.unlock()
# 执行加载任务
var result = _load_texture_task(task)
# 存储结果
results_mutex.lock()
completed_results.append(result)
results_mutex.unlock()
## 执行单个纹理加载任务
func _load_texture_task(task: Dictionary) -> Dictionary:
var crop_name = task["crop_name"]
var task_type = task["type"]
var result = {
"crop_name": crop_name,
"type": task_type,
"success": false,
"textures": [],
"texture": null,
"error": ""
}
if task_type == "sequence":
result["textures"] = _load_crop_textures_threadsafe(crop_name)
result["success"] = result["textures"].size() > 0
elif task_type == "mature":
result["texture"] = _load_mature_texture_threadsafe(crop_name)
result["success"] = result["texture"] != null
# 检查加载是否成功
if not result["success"]:
result["error"] = "加载失败: " + crop_name
failed_resources.append(crop_name)
return result
## 线程安全的作物序列帧加载
func _load_crop_textures_threadsafe(crop_name: String) -> Array:
var textures = []
var crop_path = "res://assets/作物/" + crop_name + "/"
# 检查作物文件夹是否存在
if not DirAccess.dir_exists_absolute(crop_path):
return []
# 使用ResourceLoader.load_threaded_request进行异步加载
var frame_index = 0
var max_frames = 20 # 限制最大帧数,避免无限循环
while frame_index < max_frames:
var texture_path = crop_path + str(frame_index) + ".webp"
if not ResourceLoader.exists(texture_path):
break
# 使用线程安全的资源加载
var texture = _load_resource_safe(texture_path)
if texture:
textures.append(texture)
frame_index += 1
else:
break
return textures
## 线程安全的成熟图片加载
func _load_mature_texture_threadsafe(crop_name: String) -> Texture2D:
var crop_path = "res://assets/作物/" + crop_name + "/"
var mature_path = crop_path + "成熟.webp"
if ResourceLoader.exists(mature_path):
return _load_resource_safe(mature_path)
return null
## 安全的资源加载函数,带错误处理
func _load_resource_safe(path: String) -> Resource:
if not ResourceLoader.exists(path):
return null
# 使用ResourceLoader.load它在Godot 4中是线程安全的
var resource = ResourceLoader.load(path, "Texture2D")
# 验证资源
if resource and resource is Texture2D:
return resource
else:
if resource == null:
print("[错误] 加载资源失败: ", path)
failed_resources.append(path)
else:
print("[警告] 资源类型不匹配: ", path)
return null
## 应用加载结果到缓存
func _apply_loading_result(result: Dictionary):
var crop_name = result["crop_name"]
var task_type = result["type"]
var success = result["success"]
if not success:
var error_msg = "加载失败: %s (%s)" % [crop_name, task_type]
_send_debug_message(error_msg, Color.RED)
return
if task_type == "sequence":
var textures = result["textures"]
if textures.size() > 0:
texture_cache[crop_name] = textures
frame_counts[crop_name] = textures.size()
_update_cache_access(crop_name)
_send_debug_message("%s: %d" % [crop_name, textures.size()], Color.GREEN)
elif task_type == "mature":
var texture = result["texture"]
if texture:
mature_texture_cache[crop_name] = texture
_update_cache_access(crop_name + "_mature")
_send_debug_message("%s: 成熟图片" % crop_name, Color.GREEN)
# 检查缓存大小,必要时清理
_check_and_cleanup_cache()
## 立即加载默认图片(同步,但优化)
func _load_default_textures_async() -> void:
const DEFAULT_CROP = "默认"
const DEFAULT_PATH = "res://assets/作物/默认/"
if texture_cache.has(DEFAULT_CROP):
return
var textures = []
var frame_index = 0
# 限制默认图片帧数
while frame_index < 10:
var texture_path = DEFAULT_PATH + str(frame_index) + ".webp"
if ResourceLoader.exists(texture_path):
var texture = _load_resource_safe(texture_path)
if texture:
textures.append(texture)
frame_index += 1
else:
break
else:
break
# 如果没有序列帧,尝试加载单个图片
if textures.size() == 0:
var single_path = DEFAULT_PATH + "0.webp"
if ResourceLoader.exists(single_path):
var texture = _load_resource_safe(single_path)
if texture:
textures.append(texture)
# 缓存结果
if textures.size() > 0:
texture_cache[DEFAULT_CROP] = textures
frame_counts[DEFAULT_CROP] = textures.size()
# 加载默认成熟图片
var mature_path = DEFAULT_PATH + "成熟.webp"
if ResourceLoader.exists(mature_path):
var mature_texture = _load_resource_safe(mature_path)
if mature_texture:
mature_texture_cache[DEFAULT_CROP] = mature_texture
print("[CropTextureManager] 默认图片加载完成:%d" % textures.size())
# 让出一帧
await Engine.get_main_loop().process_frame
## 更新缓存访问顺序LRU
func _update_cache_access(key: String):
if key in cache_access_order:
cache_access_order.erase(key)
cache_access_order.append(key)
## 检查并清理缓存
func _check_and_cleanup_cache():
var total_cached = texture_cache.size() + mature_texture_cache.size()
if total_cached > max_cache_size:
var to_remove = total_cached - max_cache_size + 10 # 多清理一些
_send_debug_message("⚠ 缓存超限,开始清理 %d 个项目" % to_remove, Color.ORANGE)
for i in range(min(to_remove, cache_access_order.size())):
var key = cache_access_order[i]
# 不清理默认图片
if key.begins_with("默认"):
continue
if key.ends_with("_mature"):
var crop_name = key.replace("_mature", "")
mature_texture_cache.erase(crop_name)
else:
texture_cache.erase(key)
frame_counts.erase(key)
# 更新访问顺序
cache_access_order = cache_access_order.slice(to_remove)
var current_size = texture_cache.size() + mature_texture_cache.size()
var cleanup_msg = "缓存清理完成,当前缓存: %d" % current_size
print("[CropTextureManager] " + cleanup_msg)
_send_debug_message(cleanup_msg, Color.YELLOW)
## 根据生长进度获取作物图片(带缓存优化)
func get_texture_by_progress(crop_name: String, progress: float) -> Texture2D:
# 更新访问记录
_update_cache_access(crop_name)
# 100%成熟时优先使用成熟图片
if progress >= 1.0:
var mature_texture = mature_texture_cache.get(crop_name, null)
if mature_texture:
_update_cache_access(crop_name + "_mature")
return mature_texture
# 使用序列帧图片
var textures = texture_cache.get(crop_name, [])
if textures.size() == 0:
# 如果没有缓存,尝试使用默认图片
textures = texture_cache.get("默认", [])
if textures.size() == 0:
return null
if textures.size() == 1:
return textures[0]
# 根据进度计算帧索引
var frame_index = int(progress * (textures.size() - 1))
frame_index = clamp(frame_index, 0, textures.size() - 1)
return textures[frame_index]
## 清理线程
func _cleanup_threads() -> void:
for thread in worker_threads:
if thread.is_started():
thread.wait_to_finish()
worker_threads.clear()
print("[CropTextureManager] 工作线程已清理")
## 清理缓存
func clear_cache() -> void:
await _cleanup_threads()
texture_cache.clear()
mature_texture_cache.clear()
frame_counts.clear()
cache_access_order.clear()
failed_resources.clear()
print("[CropTextureManager] 缓存已清理")
## 打印缓存统计信息
func _print_cache_stats() -> void:
print("[CropTextureManager] 缓存统计:")
print(" - 序列帧缓存: %d 种作物" % texture_cache.size())
print(" - 成熟图片缓存: %d 种作物" % mature_texture_cache.size())
print(" - 加载失败: %d 个资源" % failed_resources.size())
var total_frames = 0
for count in frame_counts.values():
total_frames += count
print(" - 总图片帧数: %d" % total_frames)
if failed_resources.size() > 0:
print(" - 失败的资源:")
for failed in failed_resources:
print(" * ", failed)
## 获取详细缓存信息
func get_cache_info() -> String:
var info = "作物图片缓存详情:\n"
for crop_name in texture_cache.keys():
var frame_count = frame_counts.get(crop_name, 0)
var has_mature = mature_texture_cache.has(crop_name)
info += " - %s: %d" % [crop_name, frame_count]
if has_mature:
info += " (含成熟图片)"
info += "\n"
if failed_resources.size() > 0:
info += "\n加载失败的资源:\n"
for failed in failed_resources:
info += " - " + failed + "\n"
return info
## 预热常用作物(可选优化)
func preheat_common_crops(common_crops: Array) -> void:
print("[CropTextureManager] 预热常用作物: ", common_crops.size(), "")
for crop_name in common_crops:
# 确保常用作物在缓存中
if not texture_cache.has(crop_name):
var textures = _load_crop_textures_threadsafe(crop_name)
if textures.size() > 0:
texture_cache[crop_name] = textures
frame_counts[crop_name] = textures.size()
if not mature_texture_cache.has(crop_name):
var mature = _load_mature_texture_threadsafe(crop_name)
if mature:
mature_texture_cache[crop_name] = mature
# 全局作物图片管理器实例
var crop_texture_manager: CropTextureManager
# 资源加载调试器(可选,用于调试)
var resource_debugger = null
# 调试面板脚本引用
var debug_panel_script = null
#===============================================作物图片缓存系统===============================================
#===============================================作物图片更新===============================================
## 更新单个地块的作物图片显示(直接切换)
func _update_lot_crop_sprite(button: Button, lot_data: Dictionary) -> void:
var crop_sprite = button.get_node("crop_sprite")
# 未开垦或空地,隐藏图片
if not lot_data["is_diged"] or not lot_data["is_planted"] or lot_data["crop_type"] == "":
crop_sprite.visible = false
return
# 显示作物图片
crop_sprite.visible = true
var crop_name = lot_data["crop_type"]
var grow_time = float(lot_data["grow_time"])
var max_grow_time = float(lot_data["max_grow_time"])
# 计算生长进度
var progress = 0.0
if max_grow_time > 0:
progress = clamp(grow_time / max_grow_time, 0.0, 1.0)
# 获取对应图片
var texture: Texture2D = null
if crop_texture_manager:
texture = crop_texture_manager.get_texture_by_progress(crop_name, progress)
if texture:
# 直接切换图片,无渐变效果
crop_sprite.texture = texture
crop_sprite.modulate = Color.WHITE
else:
crop_sprite.visible = false
## 批量刷新所有地块的作物图片
func _refresh_all_crop_sprites() -> void:
for i in range(min(farm_lots.size(), grid_container.get_child_count())):
var button = grid_container.get_child(i)
var lot = farm_lots[i]
_update_lot_crop_sprite(button, lot)
#===============================================作物图片更新===============================================
#===============================================加载进度管理===============================================
## 更新加载进度显示
func _update_load_progress(progress: int, message: String = "") -> void:
load_progress_bar.value = progress
# 更新消息显示
var message_label = load_progress_panel.get_node_or_null("MessageLabel")
if message_label and message != "":
message_label.text = message
# 向调试面板发送进度信息
if debug_panel_script and debug_panel_script.has_method("add_debug_message"):
if message != "":
#debug_panel_script.add_debug_message("进度 %d%%: %s" % [progress, message], Color.CYAN)
pass
# 检测卡顿
_check_loading_stuck(progress)
if message != "":
#print("[加载进度] %d%% - %s" % [progress, message])
pass
# 上一次进度更新的时间和进度值
var last_progress_time: float = 0.0
var last_progress_value: int = 0
## 检测加载卡顿
func _check_loading_stuck(progress: int):
var current_time = Time.get_ticks_msec() / 1000.0
# 如果进度没有变化
if progress == last_progress_value:
var stuck_time = current_time - last_progress_time
if stuck_time > 5.0: # 5秒没有进度变化
if debug_panel_script and debug_panel_script.has_method("add_debug_message"):
debug_panel_script.add_debug_message("⚠ 加载卡顿检测: 在 %d%% 停留了 %.1f" % [progress, stuck_time], Color.ORANGE)
else:
# 进度有变化,更新记录
last_progress_value = progress
last_progress_time = current_time
## 主预加载函数 - 游戏启动时调用
func _preload_all_crop_textures() -> void:
# 初始化管理器
if not crop_texture_manager:
crop_texture_manager = CropTextureManager.new()
# 等待作物数据加载
_update_load_progress(0, "等待作物数据...")
await _wait_for_crop_data()
# 开始异步预加载
await crop_texture_manager.preload_all_textures_async(
can_planted_crop,
Callable(self, "_update_load_progress")
)
# 完成后隐藏加载面板
await get_tree().create_timer(0.5).timeout
load_progress_panel.hide()
print("[主游戏] 图片预加载完成,隐藏加载面板")
## 等待作物数据加载完成
func _wait_for_crop_data() -> void:
const MAX_WAIT_TIME = 5.0
var wait_time = 0.0
while can_planted_crop.size() == 0 and wait_time < MAX_WAIT_TIME:
await get_tree().create_timer(0.1).timeout
wait_time += 0.1
if can_planted_crop.size() == 0:
_update_load_progress(90, "作物数据加载失败,跳过图片预加载")
print("[警告] 作物数据未加载,跳过图片预加载")
else:
print("[主游戏] 作物数据加载完成,共 %d 种作物" % can_planted_crop.size())
#===============================================加载进度管理============================================
#===============================================调试和维护工具===============================================
## 调试:打印缓存信息
func _debug_print_crop_cache() -> void:
if crop_texture_manager:
print(crop_texture_manager.get_cache_info())
else:
print("[调试] 作物图片管理器未初始化")
## 调试:强制刷新所有图片
func _debug_refresh_all_crop_sprites() -> void:
print("[调试] 强制刷新所有地块图片...")
_refresh_all_crop_sprites()
print("[调试] 图片刷新完成")
## 调试:清理图片缓存
func _debug_clear_crop_cache() -> void:
if crop_texture_manager:
crop_texture_manager.clear_cache()
print("[调试] 图片缓存已清理")
## 调试:启用资源加载调试器
func _debug_enable_resource_debugger() -> void:
if not resource_debugger:
resource_debugger = preload("res://GlobalScript/ResourceLoadingDebugger.gd").new()
add_child(resource_debugger)
print("[调试] 资源加载调试器已启用")
else:
print("[调试] 资源加载调试器已经在运行")
## 调试:生成资源加载报告
func _debug_generate_loading_report() -> void:
if resource_debugger:
var report = resource_debugger.generate_loading_report()
print(report)
resource_debugger.export_debug_data_to_file()
else:
print("[调试] 资源加载调试器未启用,请先调用 _debug_enable_resource_debugger()")
## 调试:检测设备能力
func _debug_detect_device_capabilities() -> void:
if resource_debugger:
var capabilities = resource_debugger.detect_device_capabilities()
print("[调试] 设备能力检测结果:")
for key in capabilities:
print(" %s: %s" % [key, str(capabilities[key])])
else:
print("[调试] 资源加载调试器未启用")
## 调试:强制触发低内存模式
func _debug_trigger_low_memory_mode() -> void:
if crop_texture_manager:
# 临时降低缓存大小来模拟低内存环境
crop_texture_manager.max_cache_size = 50
crop_texture_manager._check_and_cleanup_cache()
print("[调试] 已触发低内存模式缓存大小限制为50")
## 调试:恢复正常内存模式
func _debug_restore_normal_memory_mode() -> void:
if crop_texture_manager:
crop_texture_manager.max_cache_size = 200
print("[调试] 已恢复正常内存模式缓存大小限制为200")
## 在加载进度面板上添加调试按钮
func _add_debug_button_to_loading_panel():
# 创建调试按钮
var debug_button = Button.new()
debug_button.text = "调试信息"
debug_button.size_flags_horizontal = Control.SIZE_SHRINK_CENTER
debug_button.position = Vector2(10, 500) # 左下角位置
debug_button.size = Vector2(120, 40)
# 设置按钮样式
debug_button.modulate = Color(0.8, 0.8, 1.0, 0.9) # 半透明蓝色
# 连接点击信号
debug_button.pressed.connect(_on_debug_button_pressed)
# 添加到加载进度面板
load_progress_panel.add_child(debug_button)
print("[MainGame] 调试按钮已添加到加载进度面板")
## 调试按钮点击处理
func _on_debug_button_pressed():
if debug_panel.visible:
debug_panel.hide()
else:
debug_panel.show()
debug_panel.move_to_front()
print("[MainGame] 调试面板切换显示状态")
#===============================================调试和维护工具===============================================
#===============================================向后兼容性===============================================
# 为了保持向后兼容,保留一些原来的函数名
func _load_crop_textures(crop_name: String) -> Array:
if crop_texture_manager:
return crop_texture_manager._load_crop_textures_threadsafe(crop_name)
return []
func _get_crop_texture_by_progress(crop_name: String, progress: float) -> Texture2D:
if crop_texture_manager:
return crop_texture_manager.get_texture_by_progress(crop_name, progress)
return null
func _clear_crop_textures_cache() -> void:
if crop_texture_manager:
crop_texture_manager.clear_cache()
func _get_crop_cache_info() -> String:
if crop_texture_manager:
return crop_texture_manager.get_cache_info()
return "图片管理器未初始化"
#===============================================向后兼容性===============================================
#===============================================添加新地块处理===============================================
#添加新的地块默认花费2000
func _on_add_new_ground_button_pressed() -> void:
# 如果处于访问模式,不允许操作
if is_visiting_mode:
Toast.show("访问模式下无法购买新地块", Color.ORANGE)
return
# 检查玩家金钱是否足够
var new_ground_cost = 2000
if money < new_ground_cost:
Toast.show("金钱不足!购买新地块需要 " + str(new_ground_cost) + "", Color.RED)
return
# 检查地块数量限制(可以根据需要设置最大地块数)
var max_lots = 80 # 假设最大100个地块
if farm_lots.size() >= max_lots:
Toast.show("已达到最大地块数量限制(" + str(max_lots) + "个)", Color.YELLOW)
return
# 显示确认弹窗
accept_dialog.set_dialog_title("购买新地块确认")
accept_dialog.set_dialog_content("购买一个新的未开垦地块\n花费 " + str(new_ground_cost) + " 元?\n\n当前地块数量:" + str(farm_lots.size()) + "")
accept_dialog.popup_centered()
# 标记当前操作为购买新地块
accept_dialog.set_meta("action_type", "buy_new_ground")
# 执行购买新地块逻辑
func _execute_buy_new_ground():
var new_ground_cost = 2000
# 发送购买新地块请求到服务器
if tcp_network_manager_panel and tcp_network_manager_panel.has_method("sendBuyNewGround"):
var success = tcp_network_manager_panel.sendBuyNewGround()
if success:
print("已发送购买新地块请求")
else:
Toast.show("网络未连接,无法购买新地块", Color.RED)
else:
Toast.show("网络管理器不可用", Color.RED)
#===============================================添加新地块处理===============================================
#===============================================每日签到处理===============================================
#每日签到 奖励可以有钱币,经验,随机种子 连续签到奖励更多 连续签到只要不中断,奖励会随着签到的次数逐渐变得丰厚
func _on_daily_check_in_button_pressed() -> void:
daily_check_in_panel.show()
# 刷新签到数据
daily_check_in_panel.refresh_check_in_data()
# 处理每日签到响应
func _handle_daily_check_in_response(response: Dictionary) -> void:
# 更新玩家数据
var updated_data = response.get("updated_data", {})
money = updated_data["money"]
experience = updated_data["experience"]
level = updated_data["level"]
player_bag = updated_data["player_bag"]
_fix_player_bag_data()
# 更新UI
_update_ui()
# 更新玩家背包UI
player_bag_panel.update_player_bag_ui()
# 向签到面板传递响应
daily_check_in_panel.handle_check_in_response(response)
# 显示签到结果通知
var success = response.get("success", false)
if success:
var rewards = response.get("rewards", {})
var consecutive_days = response.get("consecutive_days", 1)
var message = "签到成功!连续签到 %d" % consecutive_days
Toast.show(message, Color.GREEN)
else:
var error_message = response.get("message", "签到失败")
Toast.show(error_message, Color.RED)
# 处理获取签到数据响应
func _handle_check_in_data_response(response: Dictionary) -> void:
# 向签到面板传递响应
daily_check_in_panel.handle_check_in_data_response(response)
#===============================================每日签到处理===============================================
#===============================================一键收获处理===============================================
#一键收获 默认花费400元 可以一键收获已成熟作物
func _on_one_click_harvestbutton_pressed() -> void:
# 如果处于访问模式,不允许操作
if is_visiting_mode:
Toast.show("访问模式下无法使用一键收获", Color.ORANGE)
return
# 统计有多少成熟的作物
var mature_crops_count = 0
for lot in farm_lots:
if lot["is_diged"] and lot["is_planted"] and not lot.get("is_dead", false):
if lot["grow_time"] >= lot["max_grow_time"]:
mature_crops_count += 1
# 如果没有成熟的作物
if mature_crops_count == 0:
Toast.show("没有可以收获的成熟作物", Color.YELLOW)
return
# 检查玩家金钱是否足够
var one_click_cost = 400
if money < one_click_cost:
Toast.show("金钱不足!一键收获需要 " + str(one_click_cost) + "", Color.RED)
return
# 显示确认弹窗
accept_dialog.set_dialog_title("一键收获确认")
accept_dialog.set_dialog_content("发现 " + str(mature_crops_count) + " 个成熟作物\n花费 " + str(one_click_cost) + " 元进行一键收获?")
accept_dialog.popup_centered()
# 标记当前操作为一键收获
accept_dialog.set_meta("action_type", "one_click_harvest")
# 执行一键收获逻辑
func _execute_one_click_harvest():
var one_click_cost = 400
var harvested_count = 0
var success_count = 0
# 先扣除费用
money -= one_click_cost
_update_ui()
# 遍历所有地块,收获成熟作物
for i in range(len(farm_lots)):
var lot = farm_lots[i]
if lot["is_diged"] and lot["is_planted"] and not lot.get("is_dead", false):
if lot["grow_time"] >= lot["max_grow_time"]:
harvested_count += 1
# 发送收获请求到服务器
if tcp_network_manager_panel and tcp_network_manager_panel.sendHarvestCrop(i):
success_count += 1
# 添加小延迟避免服务器压力过大
await get_tree().create_timer(0.3).timeout
# 显示结果
if success_count > 0:
Toast.show("一键收获完成!成功收获 " + str(success_count) + " 个作物,花费 " + str(one_click_cost) + "", Color.GREEN)
print("一键收获完成,收获了 ", success_count, " 个作物")
else:
Toast.show("一键收获失败,请检查网络连接", Color.RED)
# 如果失败,退还费用
money += one_click_cost
_update_ui()
#===============================================一键收获处理===============================================
#===============================================返回主菜单处理===============================================
#断开连接并返回主菜单界面
func _on_return_main_menu_button_pressed() -> void:
# 显示确认弹窗
accept_dialog.set_dialog_title("返回主菜单")
accept_dialog.set_dialog_content("确定要断开连接并返回主菜单吗?\n")
accept_dialog.popup_centered()
# 标记当前操作为返回主菜单
accept_dialog.set_meta("action_type", "return_main_menu")
# 执行返回主菜单逻辑
func _execute_return_main_menu():
# 断开与服务器的连接
if tcp_network_manager_panel and tcp_network_manager_panel.is_connected_to_server():
tcp_network_manager_panel.client.disconnect_from_server()
print("已断开与服务器的连接")
# 直接切换到主菜单场景
get_tree().change_scene_to_file('res://GUI/MainMenuPanel.tscn')
#===============================================返回主菜单处理===============================================
#===============================================幸运抽奖处理===============================================
#幸运抽奖 默认800元抽一次 五连抽打九折 十连抽打八折 奖励可以有钱币,经验,随机种子
func _on_lucky_draw_button_pressed() -> void:
lucky_draw_panel.show()
# 刷新抽奖显示数据
lucky_draw_panel.refresh_reward_display()
# 处理幸运抽奖响应
func _handle_lucky_draw_response(response: Dictionary) -> void:
# 更新玩家数据
var updated_data = response.get("updated_data", {})
money = updated_data["money"]
experience = updated_data["experience"]
level = updated_data["level"]
player_bag = updated_data["player_bag"]
_fix_player_bag_data()
# 更新UI
_update_ui()
# 更新玩家背包UI
player_bag_panel.update_player_bag_ui()
# 向抽奖面板传递响应
lucky_draw_panel.handle_lucky_draw_response(response)
# 显示抽奖结果通知
var success = response.get("success", false)
if success:
var draw_type = response.get("draw_type", "single")
var cost = response.get("cost", 0)
var rewards = response.get("rewards", [])
var type_names = {
"single": "单抽",
"five": "五连抽",
"ten": "十连抽"
}
var message = "%s成功!消费 %d 金币,获得 %d 个奖励" % [
type_names.get(draw_type, draw_type), cost, rewards.size()
]
Toast.show(message, Color.GREEN)
# 检查是否有传奇奖励
var has_legendary = false
for reward in rewards:
if reward.get("rarity") == "传奇":
has_legendary = true
break
if has_legendary:
Toast.show("🎉 恭喜获得传奇奖励!", Color.GOLD)
else:
var error_message = response.get("message", "抽奖失败")
Toast.show(error_message, Color.RED)
# 幸运抽奖完成信号处理
func _on_lucky_draw_completed(rewards: Array, draw_type: String) -> void:
# 可以在这里添加额外的处理逻辑,比如成就检查、特殊效果等
print("幸运抽奖完成:", draw_type, ",获得奖励:", rewards.size(), "")
# 幸运抽奖失败信号处理
func _on_lucky_draw_failed(error_message: String) -> void:
print("幸运抽奖失败:", error_message)
# 获取作物数据(供抽奖面板使用)
func get_crop_data() -> Dictionary:
return can_planted_crop
#===============================================幸运抽奖处理===============================================
#===============================================点赞处理===============================================
#访客模式下可以给别人点赞然后总赞数显示在show_like节点上
func _on_like_button_pressed() -> void:
# 检查是否处于访问模式
if not is_visiting_mode:
Toast.show("只能在访问其他玩家农场时点赞", Color.ORANGE)
return
# 获取被访问玩家的用户名
var target_username = visited_player_data.get("user_name", "")
if target_username == "":
Toast.show("无法获取目标玩家信息", Color.RED)
return
# 发送点赞请求
var success = tcp_network_manager_panel.sendLikePlayer(target_username)
if success:
print("已发送点赞请求给玩家:", target_username)
else:
Toast.show("网络未连接,无法点赞", Color.RED)
print("发送点赞请求失败,网络未连接")
# 处理点赞响应
func _handle_like_player_response(data):
var success = data.get("success", false)
var message = data.get("message", "")
if success:
var target_likes = data.get("target_likes", 0)
Toast.show(message, Color.PINK)
# 更新被访问玩家的点赞数显示
visited_player_data["total_likes"] = target_likes
show_like.text = "点赞数:" + str(int(target_likes))
print("点赞成功,目标玩家总赞数:", target_likes)
else:
Toast.show(message, Color.RED)
print("点赞失败:", message)
#===============================================点赞处理===============================================
#===============================================获取在线人数处理===============================================
# 启动在线人数更新定时器
func _start_online_players_timer():
# 初始显示连接中状态
_update_online_players_display(0, false, true)
# 立即请求一次在线人数
_request_online_players()
# 创建定时器每10秒请求一次在线人数
var timer = Timer.new()
timer.wait_time = 10.0 # 10秒
timer.timeout.connect(_request_online_players)
timer.autostart = true
add_child(timer)
# 请求在线人数
func _request_online_players():
if tcp_network_manager_panel and tcp_network_manager_panel.is_connected_to_server():
var success = tcp_network_manager_panel.sendGetOnlinePlayers()
if success:
print("已发送在线人数请求")
else:
print("发送在线人数请求失败")
_update_online_players_display(0, false, false)
else:
print("未连接服务器,无法获取在线人数")
_update_online_players_display(0, false, false)
# 处理在线人数响应
func _handle_online_players_response(data):
var success = data.get("success", false)
if success:
var online_players = data.get("online_players", 0)
_update_online_players_display(online_players, true, false)
print("当前在线人数:", online_players)
else:
var message = data.get("message", "获取在线人数失败")
print("在线人数请求失败:", message)
_update_online_players_display(0, false, false)
# 更新在线设备显示
func _update_online_players_display(count: int, connected: bool, connecting: bool = false):
if connecting:
show_onlineplayer.text = "连接中..."
show_onlineplayer.modulate = Color.YELLOW
elif connected:
show_onlineplayer.text = "在线设备:" + str(count)
show_onlineplayer.modulate = Color.GREEN
else:
show_onlineplayer.text = "离线"
show_onlineplayer.modulate = Color.RED
#===============================================获取在线人数处理===============================================
#====================================领取新手玩家礼包处理=========================================
#新手玩家大礼包按钮点击,只能领取一次,领取后这个按钮对该账号永久隐藏
func _on_new_player_gift_button_pressed() -> void:
# 显示确认对话框
var confirm_dialog = preload("res://Script/Dialog/AcceptDialog.gd").new()
add_child(confirm_dialog)
confirm_dialog.set_dialog_title("领取新手大礼包")
confirm_dialog.set_dialog_content("新手大礼包包含:\n• 6000金币\n• 1000经验\n• 龙果种子 x1\n• 杂交树1种子 x1\n• 杂交树2种子 x1\n\n每个账号只能领取一次,确定要领取吗?")
confirm_dialog.set_ok_text("领取")
confirm_dialog.set_cancel_text("取消")
# 连接信号
confirm_dialog.confirmed.connect(_on_confirm_claim_new_player_gift)
confirm_dialog.canceled.connect(_on_cancel_claim_new_player_gift.bind(confirm_dialog))
# 显示对话框
confirm_dialog.popup_centered()
#确认领取新手大礼包
func _on_confirm_claim_new_player_gift():
var success = tcp_network_manager_panel.sendClaimNewPlayerGift()
if success:
pass
else:
Toast.show("发送请求失败", Color.RED)
#取消领取新手大礼包
func _on_cancel_claim_new_player_gift(dialog):
if dialog:
dialog.queue_free()
#处理新手大礼包响应
func _handle_new_player_gift_response(data):
var success = data.get("success", false)
var message = data.get("message", "")
var updated_data = data.get("updated_data", {})
if success:
# 更新玩家数据
money = updated_data["money"]
experience = updated_data["experience"]
level = updated_data["level"]
player_bag = updated_data["player_bag"]
_fix_player_bag_data()
new_player_gift_claimed = updated_data["new_player_gift_claimed"]
pet_bag = updated_data["宠物背包"]
# 隐藏新手大礼包按钮
new_player_gift_button.hide()
# 更新UI
_update_ui()
# 更新宠物背包UI
pet_bag_panel.update_pet_bag_ui()
# 显示成功消息
Toast.show(message, Color.GOLD, 3.0, 1.0)
print("新手大礼包领取成功!")
else:
# 如果已经领取过,也隐藏按钮
if message.find("已经领取过") >= 0:
new_player_gift_claimed = true
new_player_gift_button.hide()
# 显示错误消息
Toast.show(message, Color.RED, 2.0, 1.0)
#====================================领取新手玩家礼包处理=========================================
#====================================全服大喇叭处理=========================================
# 处理全服大喇叭消息
func _handle_global_broadcast_message(data: Dictionary):
# 将消息传递给大喇叭面板处理
global_server_broadcast_panel.receive_broadcast_message(data)
# 处理全服大喇叭发送响应
func _handle_global_broadcast_response(data: Dictionary):
var success = data.get("success", false)
var message = data.get("message", "")
if success:
Toast.show("大喇叭消息发送成功", Color.GREEN, 2.0, 1.0)
else:
Toast.show("大喇叭消息发送失败:" + message, Color.RED, 3.0, 1.0)
# 处理全服大喇叭历史消息响应
func _handle_broadcast_history_response(data: Dictionary):
print("收到历史消息响应: ", data.get("messages", []).size(), " 条消息")
if global_server_broadcast_panel and global_server_broadcast_panel.has_method("receive_history_messages"):
global_server_broadcast_panel.receive_history_messages(data)
# 更新主界面大喇叭显示为最新消息
if global_server_broadcast:
var latest_message = global_server_broadcast_panel.get_latest_message()
if latest_message != "暂无消息":
global_server_broadcast.text = latest_message
print("主界面大喇叭已更新为: ", latest_message)
else:
global_server_broadcast.text = ""
print("没有消息,清空主界面大喇叭显示")
# 初始化大喇叭显示
func _init_broadcast_display():
if global_server_broadcast and global_server_broadcast_panel:
# 先设置为空
global_server_broadcast.text = ""
# 直接从本地文件加载历史消息
_load_broadcast_from_local()
# 无论是否有本地消息,都请求服务器获取最新消息
_request_latest_broadcast_message()
# 从本地文件加载大喇叭消息
func _load_broadcast_from_local():
var file_path = "user://chat_history.json"
if FileAccess.file_exists(file_path):
var file = FileAccess.open(file_path, FileAccess.READ)
if file:
var json_string = file.get_as_text()
file.close()
var json = JSON.new()
var parse_result = json.parse(json_string)
if parse_result == OK:
var data = json.data
if data is Array and data.size() > 0:
# 按时间戳排序
data.sort_custom(func(a, b): return a.get("timestamp", 0) < b.get("timestamp", 0))
# 获取最新消息
var latest = data[-1]
var display_name = latest.get("display_name", "匿名")
var content = latest.get("content", "")
global_server_broadcast.text = display_name + ": " + content
# 请求服务器获取最新的一条大喇叭消息
func _request_latest_broadcast_message():
# 请求最近1天的消息只获取最新的一条
var success = tcp_network_manager_panel.send_message({
"type": "request_broadcast_history",
"days": 1,
"limit": 1, # 只要最新的一条
"timestamp": Time.get_unix_time_from_system()
})
if not success:
print("请求最新大喇叭消息失败")
# 请求服务器历史消息用于刷新显示
func _request_server_history_for_refresh():
if tcp_network_manager_panel and tcp_network_manager_panel.is_connected_to_server():
# 请求最近3天的消息
var success = tcp_network_manager_panel.send_message({
"type": "request_broadcast_history",
"days": 3,
"timestamp": Time.get_unix_time_from_system()
})
if success:
pass
else:
print("请求服务器历史消息失败")
#====================================全服大喇叭处理=========================================
#====================================一键截图处理=========================================
#一键截图按钮,隐藏所有UI截图然后保存在相应位置
func _on_one_click_screen_shot_pressed() -> void:
# 保存当前UI状态
var ui_state = _save_ui_visibility_state()
# 隐藏所有UI
_hide_all_ui_for_screenshot()
await get_tree().create_timer(10.0).timeout
# 恢复UI显示
_restore_ui_visibility_state(ui_state)
# 保存当前UI可见性状态
func _save_ui_visibility_state() -> Dictionary:
var state = {}
# 保存主要UI容器状态
state["game_info_h_box_1"] = game_info_h_box_1.visible
state["game_info_h_box_2"] = game_info_h_box_2.visible
state["farm_v_box"] = farm_v_box.visible
state["visit_v_box"] = visit_v_box.visible
state["other_v_box"] = other_v_box.visible
state["game_info_h_box_3"] = game_info_h_box_3.visible
# 保存面板状态
state["crop_store_panel"] = crop_store_panel.visible
state["player_bag_panel"] = player_bag_panel.visible
state["lucky_draw_panel"] = lucky_draw_panel.visible
state["daily_check_in_panel"] = daily_check_in_panel.visible
state["player_ranking_panel"] = player_ranking_panel.visible
state["one_click_plant_panel"] = one_click_plant_panel.visible
state["land_panel"] = land_panel.visible
state["accept_dialog"] = accept_dialog.visible
state["login_panel"] = login_panel.visible if login_panel else false
return state
# 隐藏所有UI用于截图
func _hide_all_ui_for_screenshot():
# 隐藏主要UI容器
game_info_h_box_1.hide()
game_info_h_box_2.hide()
farm_v_box.hide()
visit_v_box.hide()
other_v_box.hide()
game_info_h_box_3.hide()
# 隐藏所有面板
crop_store_panel.hide()
player_bag_panel.hide()
lucky_draw_panel.hide()
daily_check_in_panel.hide()
player_ranking_panel.hide()
one_click_plant_panel.hide()
land_panel.hide()
accept_dialog.hide()
login_panel.hide()
# 恢复UI可见性状态
func _restore_ui_visibility_state(state: Dictionary):
# 恢复主要UI容器状态
if state.get("game_info_h_box_1", false):
game_info_h_box_1.show()
if state.get("game_info_h_box_2", false):
game_info_h_box_2.show()
if state.get("farm_v_box", false):
farm_v_box.show()
if state.get("visit_v_box", false):
visit_v_box.show()
if state.get("other_v_box", false):
other_v_box.show()
if state.get("game_info_h_box_3",false):
game_info_h_box_3.show()
# 恢复面板状态
if state.get("crop_store_panel", false):
crop_store_panel.show()
if state.get("player_bag_panel", false):
player_bag_panel.show()
if state.get("lucky_draw_panel", false):
lucky_draw_panel.show()
if state.get("daily_check_in_panel", false):
daily_check_in_panel.show()
if state.get("player_ranking_panel", false):
player_ranking_panel.show()
if state.get("one_click_plant_panel", false):
one_click_plant_panel.show()
if state.get("land_panel", false):
land_panel.show()
if state.get("accept_dialog", false):
accept_dialog.show()
if state.get("login_panel", false) and login_panel:
login_panel.show()
#====================================一键截图处理=========================================
#====================================在线礼包处理=========================================
#在线礼包,在线时间越久,越丰富,默认 1分钟 10分钟 30分钟 1小时 3小时 5小时 每天刷新
func _on_online_gift_button_pressed() -> void:
# 每次打开面板时都请求最新的在线数据
online_gift_panel.show_panel_and_request_data()
# 处理在线礼包数据响应
func _handle_online_gift_data_response(data: Dictionary):
online_gift_panel.handle_online_gift_data_response(data)
# 处理领取在线礼包响应
func _handle_claim_online_gift_response(data: Dictionary):
var success = data.get("success", false)
var updated_data = data.get("updated_data", {})
if success:
# 更新玩家数据
money = updated_data["money"]
experience = updated_data["experience"]
level = updated_data["level"]
player_bag = updated_data["player_bag"]
_fix_player_bag_data()
pet_bag = updated_data["宠物背包"]
# 更新UI
_update_ui()
player_bag_panel.update_player_bag_ui()
# 更新宠物背包UI
pet_bag_panel.update_pet_bag_ui()
# 将响应传递给在线礼包面板处理UI更新
online_gift_panel.handle_claim_online_gift_response(data)
#====================================在线礼包处理=========================================
#====================================账户设置处理=========================================
# 处理账户设置响应
func _handle_account_setting_response(data: Dictionary):
# 如果是刷新玩家信息响应,需要同步更新主游戏的数据
if data.get("type") == "refresh_player_info_response" and data.get("success", false):
if data.has("account_info"):
var account_info = data["account_info"]
# 只更新账户相关信息,不影响农场和背包数据
user_password = account_info["user_password"]
show_farm_name.text = "农场名称:" + account_info.get("farm_name", "")
show_player_name.text = "玩家昵称:" + account_info.get("player_name", "")
# 更新基本游戏状态显示
experience = account_info.get("experience", 0)
level = account_info.get("level", 1)
money = account_info.get("money", 0)
# 同步更新login_data和data中的账户信息
if login_data.size() > 0:
login_data["user_password"] = account_info.get("user_password", "")
login_data["player_name"] = account_info.get("player_name", "")
login_data["farm_name"] = account_info.get("farm_name", "")
login_data["个人简介"] = account_info.get("个人简介", "")
if data.size() > 0:
data["user_password"] = account_info.get("user_password", "")
data["player_name"] = account_info.get("player_name", "")
data["farm_name"] = account_info.get("farm_name", "")
data["个人简介"] = account_info.get("个人简介", "")
# 更新UI显示
_update_ui()
# 将响应传递给账户设置面板
account_setting_panel.handle_account_response(data)
# 处理宠物使用道具响应
func _handle_use_pet_item_response(data: Dictionary):
var success = data.get("success", false)
var message = data.get("message", "")
var updated_data = data.get("updated_data", {})
if success:
# 更新宠物背包数据
pet_bag = updated_data["宠物背包"]
# 更新宠物背包UI
pet_bag_panel.update_pet_bag_ui()
# 更新道具背包数据
item_bag = updated_data["道具背包"]
# 更新道具背包UI
item_bag_panel.update_item_bag_ui()
# 刷新宠物信息面板(如果当前有显示的宠物)
var pet_inform_panel = get_node_or_null("UI/SmallPanel/PetInformPanel")
if pet_inform_panel and pet_inform_panel.has_method("show_pet_info"):
# 如果宠物信息面板当前有显示的宠物,刷新其信息
if not pet_inform_panel.current_pet_data.is_empty():
var current_pet_id = pet_inform_panel.current_pet_data.get("基本信息", {}).get("宠物ID", "")
if current_pet_id != "":
# 查找更新后的宠物数据
for pet in pet_bag:
if pet.get("基本信息", {}).get("宠物ID", "") == current_pet_id:
pet_inform_panel.show_pet_info(pet_inform_panel.current_pet_name, pet)
break
Toast.show(message, Color.GREEN, 3.0, 1.0)
else:
Toast.show(message, Color.RED, 3.0, 1.0)
# 处理农场道具使用响应
func _handle_use_farm_item_response(data: Dictionary):
var success = data.get("success", false)
var message = data.get("message", "")
var updated_data = data.get("updated_data", {})
if success:
# 更新金币
money = updated_data["money"]
# 更新经验
experience = updated_data["experience"]
# 更新等级
level = updated_data["level"]
# 更新道具背包数据
item_bag = updated_data["道具背包"]
# 更新道具背包UI
item_bag_panel.update_item_bag_ui()
# 更新UI显示
_update_ui()
Toast.show(message, Color.GREEN, 3.0, 1.0)
else:
Toast.show(message, Color.RED, 3.0, 1.0)
#打开账户设置面板
func _on_account_setting_button_pressed() -> void:
account_setting_panel.show()
GlobalVariables.isZoomDisabled = true
account_setting_panel._refresh_player_info()
pass
#====================================账户设置处理=========================================
#====================================稻草人系统处理=========================================
# 处理购买稻草人响应
func _handle_buy_scare_crow_response(data: Dictionary):
var success = data.get("success", false)
var message = data.get("message", "")
var updated_data = data.get("updated_data", {})
if success:
# 更新玩家数据
money = updated_data["money"]
login_data["稻草人配置"] = updated_data["稻草人配置"]
# 将稻草人配置传递给稻草人面板
scare_crow_panel.handle_buy_scare_crow_response(success, message, updated_data)
# 更新UI
_update_ui()
# 更新稻草人显示
update_scare_crow_display()
else:
scare_crow_panel.handle_buy_scare_crow_response(success, message, updated_data)
# 处理修改稻草人配置响应
func _handle_modify_scare_crow_config_response(data: Dictionary):
var success = data.get("success", false)
var message = data.get("message", "")
var updated_data = data.get("updated_data", {})
if success:
# 更新玩家数据
money = updated_data["money"]
login_data["稻草人配置"] = updated_data["稻草人配置"]
# 将稻草人配置传递给稻草人面板
scare_crow_panel.handle_modify_scare_crow_config_response(success, message, updated_data)
# 更新UI
_update_ui()
# 更新稻草人显示
update_scare_crow_display()
else:
scare_crow_panel.handle_modify_scare_crow_config_response(success, message, updated_data)
# 处理获取稻草人配置响应
func _handle_get_scare_crow_config_response(data: Dictionary):
var success = data.get("success", false)
var scare_crow_config = data.get("scare_crow_config", {})
if success:
# 更新登录数据中的稻草人配置
login_data["稻草人配置"] = scare_crow_config
# 将稻草人配置传递给稻草人面板
scare_crow_panel.set_player_scare_crow_config(scare_crow_config)
# 更新稻草人显示
update_scare_crow_display()
# 更新稻草人显示
func update_scare_crow_display():
scare_crow.show()
# 如果处于访问模式,显示被访问玩家的稻草人
var config_to_display = {}
if is_visiting_mode and visited_player_data.has("稻草人配置"):
config_to_display = visited_player_data["稻草人配置"]
elif login_data.has("稻草人配置"):
config_to_display = login_data["稻草人配置"]
else:
# 如果没有稻草人配置,隐藏稻草人图片和话语
scare_crow_image.hide()
scare_crowtalks.hide()
scare_crow_name.hide()
return
# 检查是否有已拥有的稻草人类型
var owned_types = config_to_display.get("已拥有稻草人类型", [])
if owned_types.size() == 0:
# 如果没有购买过任何稻草人,隐藏稻草人图片和话语
scare_crow_image.hide()
scare_crowtalks.hide()
scare_crow_name.hide()
return
# 显示稻草人元素
scare_crow_image.show()
scare_crowtalks.show()
scare_crow_name.show()
# 更新稻草人图片
var display_type = config_to_display.get("稻草人展示类型", "")
if display_type != "":
var image_path = ""
match display_type:
"稻草人1":
image_path = "res://assets/稻草人图片/稻草人1.webp"
"稻草人2":
image_path = "res://assets/稻草人图片/稻草人2.webp"
"稻草人3":
image_path = "res://assets/稻草人图片/稻草人3.webp"
if image_path != "" and ResourceLoader.exists(image_path):
var texture = load(image_path)
scare_crow_image.texture = texture
# 更新稻草人昵称和颜色
var scare_crow_nickname = config_to_display.get("稻草人昵称", "稻草人")
var nickname_color = config_to_display.get("稻草人昵称颜色", "#ffffff")
scare_crow_name.text = "[color=" + nickname_color + "]" + scare_crow_nickname + "[/color]"
# 准备稻草人说的话列表
var talks = config_to_display.get("稻草人说的话", {})
scare_crow_talks_list.clear()
for i in range(1, 5):
var talk_key = "" + ["", "", "", ""][i-1] + "句话"
if talks.has(talk_key):
var talk_data = talks[talk_key]
var content = talk_data.get("内容", "")
var color = talk_data.get("颜色", "#000000")
if content != "":
scare_crow_talks_list.append({
"content": content,
"color": color
})
# 如果没有话语内容,添加默认话语
if scare_crow_talks_list.size() == 0:
scare_crow_talks_list.append({
"content": "我是一个可爱的稻草人!",
"color": "#000000"
})
# 重置话语索引和计时器
scare_crow_talk_index = 0
scare_crow_talk_timer = 0.0
# 显示第一句话
_update_scare_crow_talk()
# 更新稻草人当前说的话
func _update_scare_crow_talk():
if scare_crow_talks_list.size() == 0:
return
# 循环切换话语索引
if scare_crow_talk_index >= scare_crow_talks_list.size():
scare_crow_talk_index = 0
# 获取当前话语
var current_talk = scare_crow_talks_list[scare_crow_talk_index]
var content = current_talk.get("content", "")
var color = current_talk.get("color", "#000000")
# 更新显示
scare_crowtalks.text = "[color=" + color + "]" + content + "[/color]"
# 切换到下一句话
scare_crow_talk_index += 1
# 初始化稻草人配置(登录时调用)
func init_scare_crow_config():
if login_data.has("稻草人配置"):
# 有稻草人配置,检查是否有已拥有的稻草人类型
var scare_crow_config = login_data["稻草人配置"]
var owned_types = scare_crow_config.get("已拥有稻草人类型", [])
if owned_types.size() > 0:
# 有已拥有的稻草人,更新显示
update_scare_crow_display()
# 传递配置给稻草人面板
if scare_crow_panel and scare_crow_panel.has_method("set_player_scare_crow_config"):
scare_crow_panel.set_player_scare_crow_config(scare_crow_config)
else:
# 没有已拥有的稻草人,隐藏稻草人
scare_crow.hide()
else:
# 没有稻草人配置,隐藏稻草人
scare_crow.hide()
#打开农场稻草人设置面板
func _on_scare_crow_pressed() -> void:
if is_visiting_mode:
Toast.show("访问模式不能打开稻草人配置面板",Color.RED)
return
scare_crow_panel.show()
scare_crow_panel.move_to_front()
pass
#====================================稻草人系统处理=========================================
#===============================================道具使用处理===============================================
# 在地块上使用道具
func _use_item_on_lot(lot_index: int, item_name: String):
# 基础检查
if lot_index < 0 or lot_index >= farm_lots.size():
Toast.show("无效的地块索引", Color.RED, 2.0, 1.0)
return
if is_visiting_mode:
Toast.show("访问模式下无法使用道具", Color.ORANGE, 2.0, 1.0)
return
var lot = farm_lots[lot_index]
# 根据道具类型执行不同的逻辑
var action_type = ""
var action_name = ""
match item_name:
"农家肥", "金坷垃", "生长素":
action_type = "fertilize"
action_name = "施肥"
if not _validate_lot_for_growth_items(lot, action_name) or lot.get("已施肥", false):
if lot.get("已施肥", false):
Toast.show("此作物已经施过肥了", Color.ORANGE, 2.0, 1.0)
return
"水壶", "水桶":
action_type = "water"
action_name = "浇水"
if not _validate_lot_for_growth_items(lot, action_name):
return
"铲子":
action_type = "remove"
action_name = "铲除"
if not _validate_lot_for_planted_crop(lot, action_name):
return
"除草剂":
action_type = "weed_killer"
action_name = "除草"
if not _validate_lot_for_planted_crop(lot, action_name):
return
var crop_type = lot.get("crop_type", "")
var is_weed = can_planted_crop.has(crop_type) and can_planted_crop[crop_type].get("是否杂草", false)
if not is_weed:
Toast.show("除草剂只能用于清除杂草,此作物不是杂草", Color.ORANGE, 2.0, 1.0)
return
"精准采集锄", "时运锄":
action_type = "harvest"
action_name = "收获"
if not _validate_lot_for_harvest(lot, action_name):
return
_:
Toast.show("该道具暂未实现使用功能: " + item_name, Color.YELLOW, 2.0, 1.0)
return
# 检查道具并发送请求
if not _has_item_in_bag(item_name):
Toast.show("您没有 " + item_name, Color.RED, 2.0, 1.0)
return
_send_use_item_request(lot_index, item_name, action_type, action_name)
# 验证地块是否适合使用生长类道具(施肥、浇水)
func _validate_lot_for_growth_items(lot: Dictionary, action_name: String) -> bool:
if not lot.get("is_diged", false):
Toast.show("此地块尚未开垦,无法" + action_name, Color.ORANGE, 2.0, 1.0)
return false
if not lot.get("is_planted", false) or lot.get("crop_type", "") == "":
Toast.show("此地块没有种植作物,无法" + action_name, Color.ORANGE, 2.0, 1.0)
return false
if lot.get("is_dead", false):
Toast.show("作物已死亡,无法" + action_name, Color.ORANGE, 2.0, 1.0)
return false
var grow_time = float(lot.get("grow_time", 0))
var max_grow_time = float(lot.get("max_grow_time", 1))
if grow_time >= max_grow_time:
Toast.show("作物已成熟,无需" + action_name, Color.ORANGE, 2.0, 1.0)
return false
return true
# 验证地块是否适合铲除类操作
func _validate_lot_for_planted_crop(lot: Dictionary, action_name: String) -> bool:
if not lot.get("is_diged", false):
Toast.show("此地块尚未开垦,无法" + action_name, Color.ORANGE, 2.0, 1.0)
return false
if not lot.get("is_planted", false) or lot.get("crop_type", "") == "":
Toast.show("此地块没有种植作物,无法" + action_name, Color.ORANGE, 2.0, 1.0)
return false
return true
# 验证地块是否适合收获
func _validate_lot_for_harvest(lot: Dictionary, action_name: String) -> bool:
if not _validate_lot_for_planted_crop(lot, action_name):
return false
if lot.get("is_dead", false):
Toast.show("作物已死亡,无法" + action_name, Color.ORANGE, 2.0, 1.0)
return false
var grow_time = float(lot.get("grow_time", 0))
var max_grow_time = float(lot.get("max_grow_time", 1))
if grow_time < max_grow_time:
Toast.show("作物还未成熟,无法" + action_name, Color.ORANGE, 2.0, 1.0)
return false
return true
# 检查玩家是否拥有指定道具
func _has_item_in_bag(item_name: String) -> bool:
for item in item_bag:
if item.get("name", "") == item_name and item.get("count", 0) > 0:
return true
return false
# 发送使用道具请求
func _send_use_item_request(lot_index: int, item_name: String, action_type: String, action_name: String):
var target_username = visited_player_data.get("user_name", "") if is_visiting_mode else ""
if tcp_network_manager_panel and tcp_network_manager_panel.has_method("sendUseItem"):
if tcp_network_manager_panel.sendUseItem(lot_index, item_name, action_type, target_username):
_clear_item_selection()
var action_text = ("帮助" if is_visiting_mode else "") + action_name
Toast.show("正在使用 " + item_name + " " + action_text + "...", Color.CYAN, 2.0, 1.0)
else:
Toast.show("发送使用道具请求失败", Color.RED, 2.0, 1.0)
else:
Toast.show("网络未连接,无法使用道具", Color.RED, 2.0, 1.0)
# 清除道具选择状态
func _clear_item_selection():
selected_item_name = ""
is_item_selected = false
if item_bag_panel and item_bag_panel.has_method("_deselect_item"):
item_bag_panel._deselect_item()
#===============================================道具使用处理===============================================
#===============================================巡逻宠物管理===============================================
var current_patrol_pet: CharacterBody2D = null
# 初始化巡逻宠物(登录时调用)
func init_patrol_pets():
if patrol_pets == null:
patrol_pets = []
if pet_patrol_path_line:
print("巡逻线节点找到,路径点数: " + str(pet_patrol_path_line.points.size()))
else:
print("错误:找不到巡逻线节点 PetPatrolPathLine")
return
update_patrol_pets()
# 更新巡逻宠物显示
func update_patrol_pets():
clear_patrol_pets()
if patrol_pets == null or patrol_pets.size() == 0:
return
# 目前只支持一个巡逻宠物
var first_patrol_pet = patrol_pets[0]
var pet_id = first_patrol_pet.get("基本信息", {}).get("宠物ID", "")
if pet_id != "":
_create_patrol_pet_instance(first_patrol_pet)
# 清除巡逻宠物实例
func clear_patrol_pets():
if current_patrol_pet and is_instance_valid(current_patrol_pet):
current_patrol_pet.queue_free()
current_patrol_pet = null
if pet_patrol_path_line:
for child in pet_patrol_path_line.get_children():
if child is CharacterBody2D:
child.queue_free()
# 根据宠物ID设置巡逻宠物
func set_patrol_pet_by_id(pet_id: String):
if pet_id == "":
print("警告宠物ID为空")
return
var pet_data = _find_pet_by_id(pet_id)
if pet_data.is_empty():
print("错误找不到宠物ID: " + pet_id)
return
clear_patrol_pets()
await get_tree().process_frame
_create_patrol_pet_instance(pet_data)
# 查找宠物数据
func _find_pet_by_id(pet_id: String) -> Dictionary:
if pet_bag == null:
return {}
for pet_data in pet_bag:
var current_id = pet_data.get("基本信息", {}).get("宠物ID", "")
if current_id == pet_id:
return pet_data
return {}
# 创建巡逻宠物实例(统一的创建逻辑)
func _create_patrol_pet_instance(pet_data: Dictionary):
if not _validate_patrol_prerequisites():
return
var scene_path = pet_data.get("场景路径", "")
if scene_path == "" or not ResourceLoader.exists(scene_path):
print("错误:无效的场景路径: " + scene_path)
return
var pet_scene = load(scene_path)
if not pet_scene:
print("错误:无法加载宠物场景: " + scene_path)
return
var pet_instance = pet_scene.instantiate()
if not pet_instance:
print("错误:无法创建宠物实例")
return
_setup_patrol_pet(pet_instance, pet_data)
pet_patrol_path_line.add_child(pet_instance)
current_patrol_pet = pet_instance
pet_instance.position = pet_patrol_path_line.points[0]
var pet_name = pet_data.get("基本信息", {}).get("宠物名称", "未知")
print("创建巡逻宠物成功: " + pet_name)
# 验证巡逻前提条件
func _validate_patrol_prerequisites() -> bool:
if not pet_patrol_path_line:
print("错误:找不到巡逻线节点")
return false
if pet_patrol_path_line.points.size() < 2:
print("警告巡逻路径点数少于2个")
return false
return true
# 设置巡逻宠物属性
func _setup_patrol_pet(pet_instance: CharacterBody2D, pet_data: Dictionary):
var basic_info = pet_data.get("基本信息", {})
var level_exp = pet_data.get("等级经验", {})
var health_defense = pet_data.get("生命与防御", {})
# 基本信息
var original_name = basic_info.get("宠物名称", basic_info.get("宠物类型", "未知宠物"))
pet_instance.pet_name = "[巡逻] " + original_name
pet_instance.pet_id = basic_info.get("宠物ID", "")
pet_instance.pet_type = basic_info.get("宠物类型", "")
pet_instance.pet_birthday = basic_info.get("生日", "")
pet_instance.pet_personality = basic_info.get("性格", "活泼")
pet_instance.pet_team = "patrol"
# 等级经验
pet_instance.pet_level = level_exp.get("宠物等级", 1)
pet_instance.pet_experience = level_exp.get("当前经验", 0.0)
pet_instance.max_experience = level_exp.get("最大经验", 100.0)
pet_instance.pet_intimacy = level_exp.get("亲密度", 0.0)
# 生命防御
pet_instance.max_health = health_defense.get("最大生命值", 100.0)
pet_instance.current_health = health_defense.get("当前生命值", pet_instance.max_health)
pet_instance.max_shield = health_defense.get("最大护盾值", 0.0)
pet_instance.current_shield = health_defense.get("当前护盾值", 0.0)
pet_instance.max_armor = health_defense.get("最大护甲值", 0.0)
pet_instance.current_armor = health_defense.get("当前护甲值", 0.0)
# 巡逻设置
pet_instance.is_patrolling = true
pet_instance.patrol_path = pet_patrol_path_line.points.duplicate()
pet_instance.patrol_speed = 80.0
pet_instance.current_patrol_index = 0
pet_instance.patrol_wait_time = 0.0
pet_instance.current_state = pet_instance.PetState.PATROLLING
# 禁用战斗行为
if pet_instance.has_method("set_combat_enabled"):
pet_instance.set_combat_enabled(false)
# 显示状态栏和名称
if pet_instance.has_node("PetInformVBox"):
pet_instance.get_node("PetInformVBox").visible = true
if pet_instance.pet_name_rich_text:
pet_instance.pet_name_rich_text.text = pet_instance.pet_name
pet_instance.pet_name_rich_text.modulate = Color.YELLOW
pet_instance.pet_name_rich_text.visible = true
# 检查出战宠物和巡逻宠物是否冲突
func check_battle_patrol_conflict(battle_pet_id: String, patrol_pet_id: String) -> bool:
if battle_pet_id == "" or patrol_pet_id == "":
return false
return battle_pet_id == patrol_pet_id
# 根据宠物ID获取完整的宠物数据
func get_pet_data_by_id(pet_id: String) -> Dictionary:
for pet_data in pet_bag:
var current_id = pet_data.get("基本信息", {}).get("宠物ID", "")
if current_id == pet_id:
return pet_data
return {}
#===============================================巡逻宠物管理===============================================
#====================================偷菜被发现-宠物对战处理=========================================
# 处理偷菜被发现响应
func _handle_steal_caught_response(data: Dictionary):
var success = data.get("success", false)
var message = data.get("message", "")
var has_battle_pet = data.get("has_battle_pet", false)
if not success:
if has_battle_pet:
# 有出战宠物,显示对战选择弹窗
var patrol_pet_data = data.get("patrol_pet_data", {})
var battle_pet_data = data.get("battle_pet_data", {})
var escape_cost = data.get("escape_cost", 1000)
var battle_cost = data.get("battle_cost", 1300)
var target_username = data.get("target_username", "")
var current_username = data.get("current_username", "")
_show_steal_caught_dialog(
message,
patrol_pet_data,
battle_pet_data,
escape_cost,
battle_cost,
target_username,
current_username
)
else:
# 没有出战宠物,直接显示逃跑结果
var updated_data = data.get("updated_data", {})
if updated_data.has("money"):
money = updated_data["money"]
_update_ui()
Toast.show(message, Color.RED, 3.0)
else:
# 成功情况的处理(如果有的话)
Toast.show(message, Color.GREEN)
# 显示偷菜被发现对话框
func _show_steal_caught_dialog(message: String, patrol_pet_data: Dictionary, battle_pet_data: Dictionary, escape_cost: int, battle_cost: int, target_username: String, current_username: String):
# 使用AcceptDialog创建对战选择弹窗
if not accept_dialog:
print("错误找不到AcceptDialog")
return
# 获取巡逻宠物和出战宠物信息
var patrol_pet_name = patrol_pet_data.get("基本信息", {}).get("宠物名称", "未知宠物")
var patrol_pet_level = patrol_pet_data.get("等级经验", {}).get("宠物等级", 1)
var patrol_pet_type = patrol_pet_data.get("基本信息", {}).get("宠物类型", "未知类型")
var battle_pet_name = battle_pet_data.get("基本信息", {}).get("宠物名称", "未知宠物")
var battle_pet_level = battle_pet_data.get("等级经验", {}).get("宠物等级", 1)
var battle_pet_type = battle_pet_data.get("基本信息", {}).get("宠物类型", "未知类型")
# 构建对话框内容
var dialog_content = message + "\n\n"
dialog_content += "🛡️ " + target_username + "的巡逻宠物:\n"
dialog_content += " " + patrol_pet_name + " (类型:" + patrol_pet_type + ", 等级:" + str(patrol_pet_level) + ")\n\n"
dialog_content += "⚔️ 你的出战宠物:\n"
dialog_content += " " + battle_pet_name + " (类型:" + battle_pet_type + ", 等级:" + str(battle_pet_level) + ")\n\n"
dialog_content += "请选择你的行动:\n"
dialog_content += "💰 逃跑:支付 " + str(escape_cost) + " 金币\n"
dialog_content += "⚔️ 对战:如果失败支付 " + str(battle_cost) + " 金币"
# 设置对话框
accept_dialog.set_dialog_title("偷菜被发现!")
accept_dialog.set_dialog_content(dialog_content)
accept_dialog.set_ok_text("宠物对战")
accept_dialog.set_cancel_text("逃跑")
# 清除之前的信号连接
if accept_dialog.confirmed.is_connected(_on_steal_battle_confirmed):
accept_dialog.confirmed.disconnect(_on_steal_battle_confirmed)
if accept_dialog.canceled.is_connected(_on_steal_escape_confirmed):
accept_dialog.canceled.disconnect(_on_steal_escape_confirmed)
# 连接新的信号处理
accept_dialog.confirmed.connect(_on_steal_battle_confirmed.bind(patrol_pet_data, battle_pet_data, target_username))
accept_dialog.canceled.connect(_on_steal_escape_confirmed.bind(escape_cost))
# 居中显示对话框
var screen_size = get_viewport().get_visible_rect().size
var dialog_pos = Vector2(
(screen_size.x - 500) / 2, # 假设对话框宽度为500
(screen_size.y - 400) / 2 # 假设对话框高度为400
)
accept_dialog.set_dialog_position(dialog_pos)
# 显示对话框
accept_dialog.popup_centered()
print("显示偷菜被发现对话框")
# 玩家选择宠物对战
func _on_steal_battle_confirmed(patrol_pet_data: Dictionary, battle_pet_data: Dictionary, target_username: String):
print("玩家选择宠物对战")
# 验证宠物数据完整性
var battle_pet_id = battle_pet_data.get("基本信息", {}).get("宠物ID", "")
var patrol_pet_id = patrol_pet_data.get("基本信息", {}).get("宠物ID", "")
if battle_pet_id == "" or patrol_pet_id == "":
Toast.show("宠物数据不完整,无法开始对战", Color.RED, 3.0)
return
# 检查是否为同一个宠物
if check_battle_patrol_conflict(battle_pet_id, patrol_pet_id):
Toast.show("出战宠物和巡逻宠物不能为同一个!", Color.RED, 3.0)
return
# 停止宠物对战面板的自动对战逻辑
if pet_fight_panel and pet_fight_panel.has_method("stop_auto_battle"):
pet_fight_panel.stop_auto_battle()
# 加载双方宠物数据到对战面板
if pet_fight_panel and pet_fight_panel.has_method("setup_steal_battle"):
pet_fight_panel.setup_steal_battle(battle_pet_data, patrol_pet_data, user_name, target_username)
# 显示宠物对战面板
pet_fight_panel.show()
GlobalVariables.isZoomDisabled = true
Toast.show("准备进入宠物对战!", Color.YELLOW, 2.0)
# 玩家选择逃跑
func _on_steal_escape_confirmed(escape_cost: int):
print("玩家选择逃跑,支付", escape_cost, "金币")
# 扣除金币
money -= escape_cost
_update_ui()
Toast.show("支付了 " + str(escape_cost) + " 金币逃跑成功", Color.ORANGE, 3.0)
#====================================偷菜被发现-宠物对战处理=========================================
#=======================================智慧树系统=========================================
#智慧树按钮点击
func _on_wisdom_tree_pressed() -> void:
if is_visiting_mode:
Toast.show("访问模式不能打开智慧树配置面板",Color.RED)
return
wisdom_tree_panel.show()
# 更新智慧树显示
func update_wisdom_tree_display():
var config = login_data.get("智慧树配置", {})
if config.is_empty():
return
_update_wisdom_tree_display(_ensure_wisdom_tree_config_format(config))
# 更新智慧树显示(统一处理)
func _update_wisdom_tree_display(config: Dictionary):
var level = config.get("等级", 1)
var height = config.get("高度", 20)
var current_health = config.get("当前生命值", 100)
var max_health = config.get("最大生命值", 100)
var message = config.get("智慧树显示的话", "")
if tree_status:
tree_status.text = "等级lv" + str(level) + " 高度:" + str(height) + "cm"
if wisdom_tree_image:
var scale_factor = 0.5 + min((height - 20.0) / 80.0, 1.1)
wisdom_tree_image.scale = Vector2(scale_factor, scale_factor)
if current_health <= 0:
wisdom_tree_image.self_modulate = Color(0.5, 0.5, 0.5)
elif current_health <= max_health * 0.3:
wisdom_tree_image.self_modulate = Color(1.0, 0.8, 0.8)
else:
wisdom_tree_image.self_modulate = Color.WHITE
if anonymous_talk:
if is_visiting_mode:
anonymous_talk.hide()
elif message != "":
anonymous_talk.show()
var time_str = Time.get_datetime_string_from_system().replace(" ", " ")
anonymous_talk.text = "[color=cyan][" + time_str + "][/color] " + message
else:
anonymous_talk.show()
anonymous_talk.text = "给未来的某个陌生人说一句话吧"
# 显示随机智慧树消息
func show_random_wisdom_tree_message():
if tcp_network_manager_panel:
tcp_network_manager_panel.send_wisdom_tree_operation("get_random_message")
# 处理智慧树响应消息
func handle_wisdom_tree_response(data: Dictionary):
var message_type = data.get("operation_type", "")
var message_content = data.get("random_message", "")
if message_type == "play_music" and message_content != "" and anonymous_talk:
var time_str = Time.get_datetime_string_from_system().replace(" ", " ")
anonymous_talk.text = "[color=cyan][" + time_str + "][/color] " + message_content
if login_data.has("智慧树配置"):
login_data["智慧树配置"]["智慧树显示的话"] = message_content
# 确保智慧树配置格式正确
func _ensure_wisdom_tree_config_format(config: Dictionary) -> Dictionary:
var new_config = config.duplicate()
# 确保必需字段
for key in ["当前生命值", "最大生命值", "当前经验值"]:
if not new_config.has(key):
new_config[key] = 100 if "生命" in key else 0
if not new_config.has("最大经验值"):
var level = new_config.get("等级", 1)
new_config["最大经验值"] = int(50 * pow(level, 1.5) * 1.2)
return new_config
# 处理智慧树配置响应
func _handle_wisdom_tree_config_response(data):
if data.get("success", false):
var config = _ensure_wisdom_tree_config_format(data.get("config", {}))
login_data["智慧树配置"] = config
update_wisdom_tree_display()
var wisdom_tree_panel = get_node_or_null("BigPanel/SmallPanel/WisdomTreePanel")
if wisdom_tree_panel and wisdom_tree_panel.visible:
wisdom_tree_panel.wisdom_tree_config = config
wisdom_tree_panel.update_ui()
# ======================================= 智慧树系统 =========================================