From 4bc5673726b2e6ac0df2a579f123f796d94c15e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A0=91=E8=90=8C=E8=8A=BD?= <3205788256@qq.com> Date: Fri, 15 Aug 2025 13:20:01 +0800 Subject: [PATCH] =?UTF-8?q?=E8=BF=9B=E4=B8=80=E6=AD=A5=E5=AE=8C=E5=96=84?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1=E5=99=A8=E5=8A=9F=E8=83=BD=EF=BC=8C=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E8=BF=9C=E7=A8=8B=E5=91=BD=E4=BB=A4=E7=B3=BB=E7=BB=9F?= =?UTF-8?q?=EF=BC=8C=E8=B8=A2=E4=BA=BA=E7=B3=BB=E7=BB=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- GlobalScript/GlobalVariables.gd | 4 +- MainGame.gd | 499 ++- MainGame.tscn | 147 +- MainGame.tscn89825202664.tmp | 3764 +++++++++++++++++ Network/TCPNetworkManager.gd | 94 +- Scene/BigPanel/ItemBagPanel.tscn | 40 + Scene/BigPanel/ItemStorePanel.tscn | 45 +- Scene/BigPanel/PlayGamePanel.tscn | 179 + Scene/BigPanel/SpecialFarmPanel.tscn | 223 + Scene/SmallGame/2048Game.gd | 307 ++ Scene/SmallGame/2048Game.gd.uid | 1 + Scene/SmallGame/2048Game.tscn | 102 + Scene/SmallGame/PushBox.gd | 317 ++ Scene/SmallGame/PushBox.gd.uid | 1 + Scene/SmallGame/PushBox.tscn | 85 + Scene/SmallGame/SnakeGame.gd | 207 + Scene/SmallGame/SnakeGame.gd.uid | 1 + Scene/SmallGame/SnakeGame.tscn | 57 + Scene/SmallGame/Tetris.gd | 333 ++ Scene/SmallGame/Tetris.gd.uid | 1 + Scene/SmallGame/Tetris.tscn | 120 + Script/BigPanel/CropStorePanel.gd | 327 +- Script/BigPanel/CropWarehousePanel.gd | 2 + Script/BigPanel/ItemBagPanel.gd | 58 +- Script/BigPanel/ItemStorePanel.gd | 60 +- Script/BigPanel/PetBagPanel.gd | 2 + Script/BigPanel/PlayGamePanel.gd | 1 + Script/BigPanel/PlayGamePanel.gd.uid | 1 + Script/BigPanel/PlayerBagPanel.gd | 2 + Script/BigPanel/SpecialFarmPanel.gd | 72 + Script/BigPanel/SpecialFarmPanel.gd.uid | 1 + Script/Dialog/AddProduct2StorePopup.gd | 20 +- Script/Dialog/BatchBuyPopup.gd | 24 +- Script/Dialog/BatchSellPopup.gd | 15 +- Server/ConsoleCommandsAPI.py | 177 + Server/QQEmailSendAPI.py | 9 + Server/SMYMongoDBAPI.py | 268 +- Server/SpecialFarm.py | 507 +++ Server/TCPGameServer.py | 589 ++- Server/WSRemoteCmdApi.py | 254 ++ .../ConsoleCommandsAPI.cpython-313.pyc | Bin 43957 -> 50941 bytes .../QQEmailSendAPI.cpython-313.pyc | Bin 23933 -> 23935 bytes .../__pycache__/SMYMongoDBAPI.cpython-313.pyc | Bin 58795 -> 57710 bytes .../__pycache__/SpecialFarm.cpython-313.pyc | Bin 0 -> 18999 bytes .../__pycache__/TCPGameServer.cpython-313.pyc | Bin 334093 -> 357625 bytes .../WSRemoteCmdApi.cpython-313.pyc | Bin 0 -> 13114 bytes Server/crop_data_debug.json | 1071 +++++ Server/logs/server_monitor_20250815.log | 85 + Server/requirements.txt | 2 + Server/special_farm.log | 0 .../{ => test}/ConsoleCommandsAPI_README.md | 0 Server/{ => test}/console_demo.py | 0 .../gameconfig/mengyafarm.gameconfig.json | 2578 +++++++++++ Server/test/gameconfig/游戏小提示配置.jsonc | 22 + Server/test/import_game_tips_config.py | 84 + Server/test/item_config_debug.json | 132 + Server/test/monitor_special_farm.py | 155 + Server/test/special_farm.log | 0 Server/test/test_client_data.py | 147 + Server/test/test_complete_game_tips_system.py | 208 + Server/test/test_database_documents.py | 138 + Server/test/test_flower_farm.py | 0 Server/test/test_game_tips_config.py | 117 + Server/test/test_game_tips_integration.py | 242 ++ Server/test/test_special_farm.py | 151 + Server/test/test_visit_mode_update.py | 143 + .../test/文档/DisplayServer_总结_Markdown.md | 559 +++ Server/test/文档/display总结.txt | 2120 ++++++++++ Server/test/游戏小提示配置系统说明.md | 144 + Server/test/游戏配置/作物系统.json | 1071 +++++ Server/test/游戏配置/访问系统.json | 8 + Server/test/特殊农场/小麦谷222.json | 968 +++++ Server/test/特殊农场/幸运农场888.json | 968 +++++ Server/test/特殊农场/杂交农场666.json | 971 +++++ Server/test/特殊农场/瓜果农场333.json | 968 +++++ Server/test/特殊农场/稻香111.json | 968 +++++ Server/test/特殊农场/花卉农场520.json | 968 +++++ Server/test/特殊农场管理系统说明.md | 218 + Server/test_websocket.py | 52 + Server/特殊农场系统优化说明.md | 199 + assets/游戏UI/大喇叭.webp | Bin 0 -> 11974 bytes assets/游戏UI/大喇叭.webp.import | 34 + assets/菜单UI/黄皮大耗子.webp | Bin 0 -> 29374 bytes assets/菜单UI/黄皮大耗子.webp.import | 34 + assets/装饰物图片/占卜台.webp | Bin 0 -> 15424 bytes assets/装饰物图片/占卜台.webp.import | 34 + assets/装饰物图片/宠物天梯赛.webp | Bin 0 -> 23064 bytes assets/装饰物图片/宠物天梯赛.webp.import | 34 + assets/装饰物图片/宠物小窝.webp | Bin 0 -> 18538 bytes assets/装饰物图片/宠物小窝.webp.import | 34 + assets/装饰物图片/玩玩小游戏.webp | Bin 0 -> 18102 bytes assets/装饰物图片/玩玩小游戏.webp.import | 34 + assets/装饰物图片/神秘农场.webp | Bin 0 -> 24096 bytes assets/装饰物图片/神秘农场.webp.import | 34 + project.godot | 3 +- test_bullet_system.gd | 76 - test_bullet_system.gd.uid | 1 - test_weapon_system.gd | 49 - test_weapon_system.gd.uid | 1 - 99 files changed, 24189 insertions(+), 552 deletions(-) create mode 100644 MainGame.tscn89825202664.tmp create mode 100644 Scene/BigPanel/PlayGamePanel.tscn create mode 100644 Scene/BigPanel/SpecialFarmPanel.tscn create mode 100644 Scene/SmallGame/2048Game.gd create mode 100644 Scene/SmallGame/2048Game.gd.uid create mode 100644 Scene/SmallGame/2048Game.tscn create mode 100644 Scene/SmallGame/PushBox.gd create mode 100644 Scene/SmallGame/PushBox.gd.uid create mode 100644 Scene/SmallGame/PushBox.tscn create mode 100644 Scene/SmallGame/SnakeGame.gd create mode 100644 Scene/SmallGame/SnakeGame.gd.uid create mode 100644 Scene/SmallGame/SnakeGame.tscn create mode 100644 Scene/SmallGame/Tetris.gd create mode 100644 Scene/SmallGame/Tetris.gd.uid create mode 100644 Scene/SmallGame/Tetris.tscn create mode 100644 Script/BigPanel/PlayGamePanel.gd create mode 100644 Script/BigPanel/PlayGamePanel.gd.uid create mode 100644 Script/BigPanel/SpecialFarmPanel.gd create mode 100644 Script/BigPanel/SpecialFarmPanel.gd.uid create mode 100644 Server/SpecialFarm.py create mode 100644 Server/WSRemoteCmdApi.py create mode 100644 Server/__pycache__/SpecialFarm.cpython-313.pyc create mode 100644 Server/__pycache__/WSRemoteCmdApi.cpython-313.pyc create mode 100644 Server/crop_data_debug.json create mode 100644 Server/logs/server_monitor_20250815.log create mode 100644 Server/special_farm.log rename Server/{ => test}/ConsoleCommandsAPI_README.md (100%) rename Server/{ => test}/console_demo.py (100%) create mode 100644 Server/test/gameconfig/mengyafarm.gameconfig.json create mode 100644 Server/test/gameconfig/游戏小提示配置.jsonc create mode 100644 Server/test/import_game_tips_config.py create mode 100644 Server/test/item_config_debug.json create mode 100644 Server/test/monitor_special_farm.py create mode 100644 Server/test/special_farm.log create mode 100644 Server/test/test_client_data.py create mode 100644 Server/test/test_complete_game_tips_system.py create mode 100644 Server/test/test_database_documents.py create mode 100644 Server/test/test_flower_farm.py create mode 100644 Server/test/test_game_tips_config.py create mode 100644 Server/test/test_game_tips_integration.py create mode 100644 Server/test/test_special_farm.py create mode 100644 Server/test/test_visit_mode_update.py create mode 100644 Server/test/文档/DisplayServer_总结_Markdown.md create mode 100644 Server/test/文档/display总结.txt create mode 100644 Server/test/游戏小提示配置系统说明.md create mode 100644 Server/test/游戏配置/作物系统.json create mode 100644 Server/test/游戏配置/访问系统.json create mode 100644 Server/test/特殊农场/小麦谷222.json create mode 100644 Server/test/特殊农场/幸运农场888.json create mode 100644 Server/test/特殊农场/杂交农场666.json create mode 100644 Server/test/特殊农场/瓜果农场333.json create mode 100644 Server/test/特殊农场/稻香111.json create mode 100644 Server/test/特殊农场/花卉农场520.json create mode 100644 Server/test/特殊农场管理系统说明.md create mode 100644 Server/test_websocket.py create mode 100644 Server/特殊农场系统优化说明.md create mode 100644 assets/游戏UI/大喇叭.webp create mode 100644 assets/游戏UI/大喇叭.webp.import create mode 100644 assets/菜单UI/黄皮大耗子.webp create mode 100644 assets/菜单UI/黄皮大耗子.webp.import create mode 100644 assets/装饰物图片/占卜台.webp create mode 100644 assets/装饰物图片/占卜台.webp.import create mode 100644 assets/装饰物图片/宠物天梯赛.webp create mode 100644 assets/装饰物图片/宠物天梯赛.webp.import create mode 100644 assets/装饰物图片/宠物小窝.webp create mode 100644 assets/装饰物图片/宠物小窝.webp.import create mode 100644 assets/装饰物图片/玩玩小游戏.webp create mode 100644 assets/装饰物图片/玩玩小游戏.webp.import create mode 100644 assets/装饰物图片/神秘农场.webp create mode 100644 assets/装饰物图片/神秘农场.webp.import delete mode 100644 test_bullet_system.gd delete mode 100644 test_bullet_system.gd.uid delete mode 100644 test_weapon_system.gd delete mode 100644 test_weapon_system.gd.uid diff --git a/GlobalScript/GlobalVariables.gd b/GlobalScript/GlobalVariables.gd index 8c55725..cc479f8 100644 --- a/GlobalScript/GlobalVariables.gd +++ b/GlobalScript/GlobalVariables.gd @@ -5,12 +5,12 @@ const client_version :String = "2.2.0" #记录客户端版本 var isZoomDisabled :bool = false const server_configs = [ - #{"host": "127.0.0.1", "port": 6060, "name": "本地"}, + {"host": "127.0.0.1", "port": 7070, "name": "本地"}, #{"host": "192.168.31.233", "port": 6060, "name": "家里面局域网"}, #{"host": "192.168.31.205", "port": 6060, "name": "家里面电脑"}, #{"host": "192.168.1.110", "port": 4040, "name": "萌芽局域网"}, #{"host": "47.108.90.0", "port": 4040, "name": "成都内网穿透"}#成都内网穿透 - {"host": "47.108.90.0", "port": 6060, "name": "成都公网"}#成都服务器 + #{"host": "47.108.90.0", "port": 6060, "name": "成都公网"}#成都服务器 ] const DisableWeatherDisplay :bool = false #是否禁止显示天气 diff --git a/MainGame.gd b/MainGame.gd index 86b2bba..3919fca 100644 --- a/MainGame.gd +++ b/MainGame.gd @@ -9,7 +9,7 @@ extends Node @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_tip : RichTextLabel = $UI/GUI/GameInfoHBox3/tip # 显示小提示 @onready var show_like: Label = $UI/GUI/GameInfoHBox1/like # 显示别人给自己点赞的总赞数 @onready var show_onlineplayer: Label = $UI/GUI/GameInfoHBox3/onlineplayer # 显示服务器在线人数 @onready var show_player_name : Label = $UI/GUI/GameInfoHBox2/player_name # 显示玩家昵称 @@ -17,7 +17,7 @@ extends Node @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 global_server_broadcast: RichTextLabel = $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") @@ -69,6 +69,8 @@ extends Node @onready var pet_inform_panel: Panel = $UI/SmallPanel/PetInformPanel #宠物信息面板 @onready var player_store_panel: Panel = $UI/BigPanel/PlayerStorePanel #玩家小卖部面板 @onready var game_setting_panel: Panel = $UI/BigPanel/GameSettingPanel #游戏设置面板 +@onready var play_game_panel: Panel = $UI/BigPanel/PlayGamePanel #玩玩小游戏面板 +@onready var special_farm_panel: Panel = $UI/BigPanel/SpecialFarmPanel #神秘农场面板 @@ -118,7 +120,7 @@ extends Node @onready var pet_battle_panel: PetBattlePanel = $UI/BigPanel/PetBattlePanel #新的宠物对战场景 - +#宠物巡逻系统 @onready var pet_patrol_points: Node = $PetPatrolPoints @onready var pos_1: Marker2D = $PetPatrolPoints/Pos1 #生成点1 @onready var pos_2: Marker2D = $PetPatrolPoints/Pos2#生成点2 @@ -396,6 +398,21 @@ func _input(event): #==========================玩家排行榜+访问模式处理============================ +#打开玩家排行榜 +func _on_player_rank_pressed() -> void: + if is_visiting_mode: + Toast.show("您不能访问他人玩家排行榜",Color.RED) + return + player_ranking_panel.show() + pass + +#打开玩家排行榜面板 +func _on_player_ranking_button_pressed() -> void: + player_ranking_panel.show() + player_ranking_panel.request_player_rankings() + pass + + # 处理玩家排行榜响应 func _handle_player_rankings_response(data): player_ranking_panel.handle_player_rankings_response(data) @@ -645,9 +662,15 @@ func _on_item_selected(index): #========================================杂项未分类函数======================================= -#随机游戏提示 +# 游戏小提示相关变量 +var game_tips_config = {} # 服务端游戏小提示配置 +var current_tip_index = 0 # 当前小提示索引(用于顺序模式) +#OS +#DisplayServer +#获取游戏提示 func _random_small_game_tips() -> String: - const game_tips = [ + # 默认游戏小提示(作为备用) + const default_game_tips = [ "按住wsad可以移动游戏画面", "使用鼠标滚轮来缩放游戏画面", "移动端双指缩放游戏画面", @@ -663,8 +686,32 @@ func _random_small_game_tips() -> String: "面板左上角有刷新按钮,可以刷新面板", "小心偷菜被巡逻宠物发现" ] - var random_index = randi() % game_tips.size() - var selected_tip = game_tips[random_index] + + # 获取游戏小提示数组 + var game_tips = default_game_tips + var switch_mode = "随机" # 默认切换模式 + + # 如果有服务端配置,使用服务端配置 + if game_tips_config.has("游戏小提示") and game_tips_config["游戏小提示"].size() > 0: + game_tips = game_tips_config["游戏小提示"] + switch_mode = game_tips_config.get("切换模式", "随机") + + if game_tips.size() == 0: + return "欢迎来到萌芽农场!" + + # 根据切换模式选择小提示 + var selected_tip = "" + match switch_mode: + "顺序": + selected_tip = game_tips[current_tip_index] + current_tip_index = (current_tip_index + 1) % game_tips.size() + "倒序": + selected_tip = game_tips[current_tip_index] + current_tip_index = (current_tip_index - 1 + game_tips.size()) % game_tips.size() + "随机", _: + var random_index = randi() % game_tips.size() + selected_tip = game_tips[random_index] + return selected_tip @@ -713,6 +760,13 @@ func handle_login_success(player_data: Dictionary): else: print("登录成功后请求宠物配置数据失败") + # 登录成功后请求游戏小提示配置数据 + if tcp_network_manager_panel and tcp_network_manager_panel.has_method("sendGetGameTipsConfig"): + if tcp_network_manager_panel.sendGetGameTipsConfig(): + print("登录成功后请求游戏小提示配置数据") + else: + print("登录成功后请求游戏小提示配置数据失败") + # 其他登录成功后的初始化逻辑可以在这里添加 start_game = true @@ -903,12 +957,6 @@ func _update_ui(): var my_likes = like_system.get("总点赞数", 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: @@ -1002,32 +1050,95 @@ func _on_open_store_button_pressed() -> void: crop_store_panel.move_to_front() pass +#打开种子商店面板 +func _on_seed_store_pressed() -> void: + if is_visiting_mode: + Toast.show("您不能访问他人种子商店",Color.RED) + return + crop_store_panel.show() + 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_seed_warehouse_pressed() -> void: + if is_visiting_mode: + Toast.show("您不能访问他人种子仓库",Color.RED) + return + crop_store_panel.show() + pass + #打开作物仓库面板 func _on_crop_warehouse_button_pressed() -> void: crop_warehouse_panel.show() +#打开作物仓库面板 +func _on_crop_warehouse_pressed() -> void: + if is_visiting_mode: + Toast.show("您不能访问他人作物仓库",Color.RED) + return + crop_warehouse_panel.show() + pass + + +#打开道具背包面板 +func _on_item_bag_button_pressed() -> void: + item_bag_panel.show() + +#打开道具背包面板 +func _on_item_bag_pressed() -> void: + if is_visiting_mode: + Toast.show("您不能访问他人道具背包",Color.RED) + return + item_bag_panel.show() + pass + + +#打开道具商店面板 +func _on_item_store_button_pressed() -> void: + item_store_panel.show() + +#打开道具商店面板 +func _on_item_store_pressed() -> void: + if is_visiting_mode: + Toast.show("您不能访问他人道具商店",Color.RED) + return + item_store_panel.show() + pass + + #打开宠物背包面板 func _on_pet_bag_button_pressed() -> void: pet_bag_panel.show() pass +#打开宠物背包面板 +func _on_pet_bag_pressed() -> void: + if is_visiting_mode: + Toast.show("您不能访问他人宠物背包",Color.RED) + return + pet_bag_panel.show() + pass + + #打开宠物商店面板 func _on_pet_store_button_pressed() -> void: pet_store_panel.show() pass +#打开宠物商店面板 +func _on_pet_store_pressed() -> void: + if is_visiting_mode: + Toast.show("您不能访问他人宠物商店",Color.RED) + return + pet_store_panel.show() + pass + + #==========================打开基础面板================================ @@ -1055,6 +1166,10 @@ func _try_load_from_server(): # 从服务器请求最新道具配置数据 print("尝试从服务器获取最新道具配置数据...") tcp_network_manager_panel.sendGetItemConfig() + + # 从服务器请求游戏小提示配置数据 + print("尝试从服务器获取游戏小提示配置数据...") + tcp_network_manager_panel.sendGetGameTipsConfig() else: print("服务器未连接,使用当前本地数据") @@ -1213,6 +1328,38 @@ func _handle_pet_config_response(response_data): print("从服务器获取宠物配置数据失败:", message) pet_config = {} +# 处理服务器游戏小提示配置响应 +func _handle_game_tips_config_response(response_data): + var success = response_data.get("success", false) + + if success: + var config_data = response_data.get("game_tips_config", {}) + if config_data: + # 更新游戏小提示配置 + game_tips_config = config_data + print("游戏小提示配置已从服务器更新") + print("切换模式:", config_data.get("切换模式", "顺序")) + print("切换速度:", config_data.get("切换速度", 5)) + print("小提示数量:", config_data.get("游戏小提示", []).size()) + + # 更新小提示切换间隔时间 + var switch_speed = config_data.get("切换速度", 5) + five_interval = float(switch_speed) + print("小提示切换间隔已更新为:", five_interval, "秒") + + # 如果游戏小提示面板存在,更新其配置 + if has_node("GameTipsPanel"): + var tips_panel = get_node("GameTipsPanel") + if tips_panel.has_method("update_config"): + tips_panel.update_config(config_data) + else: + print("服务器返回的游戏小提示配置数据为空") + game_tips_config = {} + else: + var message = response_data.get("message", "未知错误") + print("从服务器获取游戏小提示配置数据失败:", message) + game_tips_config = {} + #===============================================初始化数据处理=============================================== @@ -1640,8 +1787,6 @@ class CropTextureManager: if texture: texture_cache[crop_name][stage] = texture - - # 全局作物图片管理器实例 var crop_texture_manager: CropTextureManager @@ -1848,6 +1993,16 @@ func _execute_buy_new_ground(): #===============================================每日签到处理=============================================== +#打开每日签到 +func _on_daily_checkin_gift_pressed() -> void: + if is_visiting_mode: + Toast.show("您不能访问他人每日签到礼包",Color.RED) + return + daily_check_in_panel.show() + # 刷新签到数据 + daily_check_in_panel.refresh_check_in_data() + pass + #每日签到 奖励可以有钱币,经验,随机种子 连续签到奖励更多 连续签到只要不中断,奖励会随着签到的次数逐渐变得丰厚 func _on_daily_check_in_button_pressed() -> void: daily_check_in_panel.show() @@ -2328,7 +2483,6 @@ func update_broadcast_display_from_panel(): else: global_server_broadcast.text = "全服大喇叭" - # 初始化大喇叭显示 func _init_broadcast_display(): if global_server_broadcast and global_server_broadcast_panel: @@ -2428,7 +2582,6 @@ func _on_one_click_screen_shot_pressed() -> void: # 恢复UI显示 _restore_ui_visibility_state(ui_state) - # 保存当前UI可见性状态 func _save_ui_visibility_state() -> Dictionary: @@ -2518,6 +2671,15 @@ func _restore_ui_visibility_state(state: Dictionary): #====================================在线礼包处理========================================= +#打开在线礼包 +func _on_online_time_gift_pressed() -> void: + if is_visiting_mode: + Toast.show("您不能访问他人在线时长礼包",Color.RED) + return + # 每次打开面板时都请求最新的在线数据 + online_gift_panel.show_panel_and_request_data() + pass + #在线礼包,在线时间越久,越丰富,默认 1分钟 10分钟 30分钟 1小时 3小时 5小时 每天刷新 func _on_online_gift_button_pressed() -> void: # 每次打开面板时都请求最新的在线数据 @@ -2850,7 +3012,6 @@ func init_scare_crow_config(): # 没有稻草人配置,隐藏稻草人 scare_crow.hide() - #打开农场稻草人设置面板 func _on_scare_crow_pressed() -> void: if is_visiting_mode: @@ -3181,10 +3342,6 @@ func init_patrol_pets(): func check_battle_patrol_conflict(battle_pet_id: String, patrol_pet_id: String) -> bool: return battle_pet_id == patrol_pet_id -#===============================================巡逻宠物管理=============================================== - - - # 通用对话框显示函数 func _show_battle_dialog(title: String, content: String, ok_text: String, cancel_text: String, ok_callback: Callable, cancel_callback: Callable): # 使用专用的EscapeDialog创建对战选择弹窗 @@ -3288,6 +3445,9 @@ func _customize_escape_button(button: Button, normal_color: Color, hover_color: button.add_theme_color_override("font_color_hover", Color.WHITE) button.add_theme_color_override("font_color_pressed", Color.WHITE) button.add_theme_font_size_override("font_size", 18) +#===============================================巡逻宠物管理=============================================== + + #====================================偷菜被发现-宠物对战处理========================================= # 处理偷菜被发现响应 @@ -3413,6 +3573,7 @@ func _on_steal_escape_confirmed(escape_cost: int): #====================================偷菜被发现-宠物对战处理========================================= + #========================访问模式直接向巡逻宠物发起战斗======================== func _on_battle_button_pressed() -> void: # 检查是否为访问模式 @@ -3535,6 +3696,40 @@ func _update_battle_button_visibility() -> void: +# ======================================= 宠物对战系统 ========================================= +# 处理宠物对战结束 +func _on_pet_battle_ended(winner_team: String, battle_data: Dictionary): + """处理宠物对战结束后的逻辑""" + print("[宠物对战] 对战结束,获胜方: ", winner_team) + print("[宠物对战] 对战数据: ", battle_data) + + # 准备发送到服务器的对战结果数据 + var battle_result = { + "winner_team": winner_team, + "attacker_name": user_name, # 攻击方(玩家自己) + "defender_name": visited_player_data.get("玩家昵称", "未知玩家"), # 防守方(被访问玩家) + "battle_type": "steal_battle", # 对战类型:偷菜对战 + "attacker_pets": battle_data.get("attacker_pets", []), # 攻击方宠物数据 + "defender_pets": battle_data.get("defender_pets", []), # 防守方宠物数据 + "battle_duration": battle_data.get("battle_duration", 0), # 对战持续时间 + "timestamp": Time.get_unix_time_from_system() # 对战时间戳 + } + + # 发送对战结果到服务器 + if tcp_network_manager_panel and tcp_network_manager_panel.has_method("send_pet_battle_result"): + tcp_network_manager_panel.send_pet_battle_result(battle_result) + print("[宠物对战] 对战结果已发送到服务器") + else: + print("[宠物对战] 无法发送对战结果到服务器") + + # 显示对战结果提示 + if winner_team == "attacker": + Toast.show("恭喜!您在偷菜对战中获胜!", Color.GREEN) + else: + Toast.show("很遗憾,您在偷菜对战中失败了。", Color.RED) +# ======================================= 宠物对战系统 ========================================= + + #=======================================智慧树系统========================================= #智慧树按钮点击 @@ -3661,6 +3856,19 @@ func _handle_save_game_settings_response(data): # ======================================= 今日占卜系统 ========================================= +#今日占卜 +func _on_today_divination_pressed() -> void: + if is_visiting_mode: + return + today_divination_panel.show() + pass + +#打开今日占卜面板 +func _on_today_divination_button_pressed() -> void: + today_divination_panel.show() + pass + + # 处理占卜响应 func _handle_divination_response(divination_data): """处理服务器返回的占卜数据,更新本地玩家数据""" @@ -3676,136 +3884,155 @@ func get_player_divination_data(): # ======================================= 今日占卜系统 ========================================= + +#========================================玩家小卖部=========================== #打开小卖部面板 func _on_my_store_button_pressed() -> void: - if is_visiting_mode: - Toast.show("您不能访问他人小卖部",Color.RED) - return player_store_panel.show() pass #打开小卖部面板 func _on_player_store_pressed() -> void: - if is_visiting_mode: - Toast.show("您不能访问他人小卖部",Color.RED) - return player_store_panel.show() pass +#========================================玩家小卖部=========================== -#打开种子商店 -func _on_seed_store_pressed() -> void: - if is_visiting_mode: - Toast.show("您不能访问他人种子商店",Color.RED) - return - crop_store_panel.show() +#宠物星辰塔 +func _on_pet_ladder_match_pressed() -> void: pass -#打开道具商店 -func _on_item_store_pressed() -> void: - if is_visiting_mode: - Toast.show("您不能访问他人道具商店",Color.RED) - return - item_store_panel.show() +#宠物小窝 +func _on_pet_nest_pressed() -> void: pass -#打开宠物商店 -func _on_pet_store_pressed() -> void: +#玩玩小游戏 +func _on_play_small_game_pressed() -> void: if is_visiting_mode: - Toast.show("您不能访问他人宠物商店",Color.RED) return - pet_store_panel.show() + play_game_panel.show() + pass + +#神秘农场 +func _on_mystery_farmland_pressed() -> void: + if is_visiting_mode: + return + special_farm_panel.show() pass -#打开作物仓库 -func _on_crop_warehouse_pressed() -> void: - if is_visiting_mode: - Toast.show("您不能访问他人作物仓库",Color.RED) +#访问模式下送金币(已经设置了只能访问模式才会显示这个按钮) +func _on_send_money_button_pressed() -> void: + if not is_visiting_mode: + Toast.show("只能在访问模式下送金币", Color.RED) return - crop_warehouse_panel.show() - pass - -#打开种子仓库 -func _on_seed_warehouse_pressed() -> void: - if is_visiting_mode: - Toast.show("您不能访问他人种子仓库",Color.RED) - return - crop_store_panel.show() - pass - -#打开玩家排行榜 -func _on_player_rank_pressed() -> void: - if is_visiting_mode: - Toast.show("您不能访问他人玩家排行榜",Color.RED) - return - player_ranking_panel.show() - pass - -#打开每日签到 -func _on_daily_checkin_gift_pressed() -> void: - if is_visiting_mode: - Toast.show("您不能访问他人每日签到礼包",Color.RED) - return - daily_check_in_panel.show() - pass - -#打开在线礼包 -func _on_online_time_gift_pressed() -> void: - if is_visiting_mode: - Toast.show("您不能访问他人在线时长礼包",Color.RED) - return - online_gift_panel.show() - pass - -#打开宠物背包 -func _on_pet_bag_pressed() -> void: - if is_visiting_mode: - Toast.show("您不能访问他人宠物背包",Color.RED) - return - pet_bag_panel.show() - pass - -#打开道具背包 -func _on_item_bag_pressed() -> void: - if is_visiting_mode: - Toast.show("您不能访问他人道具背包",Color.RED) - return - item_bag_panel.show() - pass - -# ======================================= 宠物对战系统 ========================================= -# 处理宠物对战结束 -func _on_pet_battle_ended(winner_team: String, battle_data: Dictionary): - """处理宠物对战结束后的逻辑""" - print("[宠物对战] 对战结束,获胜方: ", winner_team) - print("[宠物对战] 对战数据: ", battle_data) - # 准备发送到服务器的对战结果数据 - var battle_result = { - "winner_team": winner_team, - "attacker_name": user_name, # 攻击方(玩家自己) - "defender_name": visited_player_data.get("玩家昵称", "未知玩家"), # 防守方(被访问玩家) - "battle_type": "steal_battle", # 对战类型:偷菜对战 - "attacker_pets": battle_data.get("attacker_pets", []), # 攻击方宠物数据 - "defender_pets": battle_data.get("defender_pets", []), # 防守方宠物数据 - "battle_duration": battle_data.get("battle_duration", 0), # 对战持续时间 - "timestamp": Time.get_unix_time_from_system() # 对战时间戳 - } + # 获取自己的原始金币数(从保存的原始数据中获取) + var my_money = original_player_data.get("钱币", 0) - # 发送对战结果到服务器 - if tcp_network_manager_panel and tcp_network_manager_panel.has_method("send_pet_battle_result"): - tcp_network_manager_panel.send_pet_battle_result(battle_result) - print("[宠物对战] 对战结果已发送到服务器") + # 检查自己的金币是否足够 + if my_money < 500: + Toast.show("您的金币不足500,无法送金币", Color.RED) + return + + # 获取被访问玩家的昵称 + var target_nickname = visited_player_data.get("玩家昵称", "未知玩家") + + # 显示确认弹窗 + _show_send_money_confirmation(target_nickname, my_money) + +func _show_send_money_confirmation(target_nickname: String, my_money: int): + """显示送金币确认弹窗""" + var confirmation_dialog = AcceptDialog.new() + confirmation_dialog.title = "送金币确认" + confirmation_dialog.dialog_text = "确定要送给 " + target_nickname + " 500金币吗?\n\n您当前拥有:" + str(my_money) + "金币" + confirmation_dialog.ok_button_text = "确定送出" + + # 添加取消按钮 + confirmation_dialog.add_cancel_button("取消") + + # 连接确认信号 + confirmation_dialog.confirmed.connect(_on_send_money_confirmed) + + # 添加到场景并显示 + get_tree().current_scene.add_child(confirmation_dialog) + confirmation_dialog.popup_centered() + +func _on_send_money_confirmed(): + """确认送金币后的处理""" + if not is_visiting_mode: + return + + # 再次检查自己的金币是否足够(防止在弹窗期间金币发生变化) + var my_money = original_player_data.get("钱币", 0) + if my_money < 500: + Toast.show("金币不足,无法送出", Color.RED) + return + + # 获取被访问玩家的用户名 + var target_username = visited_player_data.get("玩家账号", "") + if target_username == "": + Toast.show("无法获取目标玩家信息", Color.RED) + return + + # 发送送金币请求到服务器 + if tcp_network_manager_panel and tcp_network_manager_panel.has_method("sendGiveMoney"): + var success = tcp_network_manager_panel.sendGiveMoney(target_username, 500) + if success: + Toast.show("正在送出金币...", Color.CYAN) + else: + Toast.show("网络未连接,无法送金币", Color.RED) else: - print("[宠物对战] 无法发送对战结果到服务器") + Toast.show("网络管理器不可用", Color.RED) + +func _handle_give_money_response(data): + """处理送金币响应""" + var success = data.get("success", false) + var message = data.get("message", "") + var updated_data = data.get("updated_data", {}) + var target_updated_data = data.get("target_updated_data", {}) - # 显示对战结果提示 - if winner_team == "attacker": - Toast.show("恭喜!您在偷菜对战中获胜!", Color.GREEN) + if success: + # 更新自己原始数据中的金币 + original_player_data["钱币"] = updated_data.get("钱币", original_player_data.get("钱币", 0)) + + # 如果在访问模式下,更新被访问者的金币显示 + if is_visiting_mode and target_updated_data.has("钱币"): + # 更新被访问者的数据 + visited_player_data["钱币"] = target_updated_data.get("钱币", 0) + # 更新当前显示的金币数(显示被访问者的金币) + money = target_updated_data.get("钱币", 0) + _update_ui() + else: + # 非访问模式,正常更新自己的金币显示 + money = updated_data.get("钱币", money) + _update_ui() + + Toast.show(message, Color.GREEN) else: - Toast.show("很遗憾,您在偷菜对战中失败了。", Color.RED) -# ======================================= 宠物对战系统 ========================================= + Toast.show("送金币失败:" + message, Color.RED) +func _handle_money_received_notification(data): + """处理收到金币通知""" + var sender_nickname = data.get("sender_nickname", "未知玩家") + var amount = data.get("amount", 0) + var new_money = data.get("new_money", 0) + + # 更新自己的金币 + money = new_money + _update_ui() + + # 显示收到金币的通知 + Toast.show("收到来自 " + sender_nickname + " 的 " + str(amount) + " 金币!", Color.GOLD) + + # 可以添加音效或其他特效 + print("收到金币通知:来自 ", sender_nickname, ",金额:", amount) -func _on_today_divination_button_pressed() -> void: - today_divination_panel.show() - pass +# 返回登录界面 +func return_to_login(): + """被踢出时返回登录界面""" + # 清理当前游戏状态 + is_visiting_mode = false + visited_player_data = {} + original_player_data = {} + + # 切换到登录场景 + get_tree().change_scene_to_file("res://LoginPanel.tscn") diff --git a/MainGame.tscn b/MainGame.tscn index 6ec2658..0e8f39a 100644 --- a/MainGame.tscn +++ b/MainGame.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=107 format=3 uid="uid://dgh61dttaas5a"] +[gd_scene load_steps=113 format=3 uid="uid://dgh61dttaas5a"] [ext_resource type="Script" uid="uid://2pt11sfcaxf7" path="res://MainGame.gd" id="1_v3yaj"] [ext_resource type="Texture2D" uid="uid://du2pyiojliasy" path="res://assets/游戏UI/经验球.webp" id="2_6jgly"] @@ -18,6 +18,7 @@ [ext_resource type="Texture2D" uid="uid://bso5fyjavdien" path="res://assets/游戏UI/玩家数图标.webp" id="10_vygm6"] [ext_resource type="PackedScene" uid="uid://cbhitturvihqj" path="res://Scene/BigPanel/LoginPanel.tscn" id="11_6jgly"] [ext_resource type="PackedScene" uid="uid://dckc8nrn7p425" path="res://Scene/SmallPanel/LandPanel.tscn" id="12_y1hsh"] +[ext_resource type="Texture2D" uid="uid://dsds2gsvqxfak" path="res://assets/游戏UI/大喇叭.webp" id="13_5liyv"] [ext_resource type="PackedScene" uid="uid://dpiy0aim20n2h" path="res://Scene/SmallPanel/OnlineGiftPanel.tscn" id="14_5b81d"] [ext_resource type="PackedScene" uid="uid://4rwitowdt4h" path="res://Scene/SmallPanel/OneClickPlantPanel.tscn" id="15_8kysg"] [ext_resource type="PackedScene" uid="uid://btp1h6hic2sin" path="res://Scene/Dialog/AcceptDialog.tscn" id="16_0igvr"] @@ -40,8 +41,10 @@ [ext_resource type="Script" uid="uid://d4fvv2sjngajr" path="res://Script/Dialog/BatchBuyPopup.gd" id="29_5b81d"] [ext_resource type="PackedScene" uid="uid://diwbnwhnq026" path="res://Scene/NewPet/PetBattlePanel.tscn" id="29_mw3xw"] [ext_resource type="PackedScene" uid="uid://dos15dmc1b6bt" path="res://GUI/GameSettingPanel.tscn" id="30_game_setting"] +[ext_resource type="PackedScene" uid="uid://jjlgdnoo8e52" path="res://Scene/BigPanel/PlayGamePanel.tscn" id="30_rawle"] [ext_resource type="Texture2D" uid="uid://2sdfbvf1isif" path="res://icon.svg" id="31_uc6q1"] [ext_resource type="Script" uid="uid://doo34ll0yb078" path="res://Script/SmallPanel/PetInformPanel.gd" id="31_vygm6"] +[ext_resource type="PackedScene" uid="uid://byxhjyyaahs6q" path="res://Scene/BigPanel/SpecialFarmPanel.tscn" id="31_ww70k"] [ext_resource type="Script" uid="uid://bakeq8tm6r4j4" path="res://Script/SmallPanel/GlobalServerBroadcastPanel.gd" id="34_k1sw4"] [ext_resource type="Script" uid="uid://dobao5y5s2dij" path="res://Script/SmallPanel/ScareCrowPanel.gd" id="35_6ylhg"] [ext_resource type="Texture2D" uid="uid://d06uaoowmcvxj" path="res://assets/稻草人图片/稻草人2.webp" id="36_jyxgm"] @@ -57,8 +60,6 @@ [ext_resource type="Script" uid="uid://cha0uw4ra1trr" path="res://Script/Dialog/AddProduct2StorePopup.gd" id="46_8d602"] [ext_resource type="Texture2D" uid="uid://dilipbs0lncpd" path="res://assets/草地图片/草地10.webp" id="48_2i8fe"] [ext_resource type="Texture2D" uid="uid://du34yctd8bd8m" path="res://assets/灌木丛图片/灌木丛1.webp" id="49_xjiif"] -[ext_resource type="Texture2D" uid="uid://dswjorjhf1i6f" path="res://assets/灌木丛图片/灌木丛2.webp" id="50_sqnmr"] -[ext_resource type="Texture2D" uid="uid://go3n3qnpancf" path="res://assets/灌木丛图片/灌木丛3.webp" id="51_2i8fe"] [ext_resource type="Texture2D" uid="uid://dk4yl4ghmxaa2" path="res://assets/天气系统图片/雪花.webp" id="53_4ka7t"] [ext_resource type="Texture2D" uid="uid://chcgrmluhfxuk" path="res://assets/天气系统图片/樱花1.webp" id="53_tdq2s"] [ext_resource type="Texture2D" uid="uid://c1qqalp7owgy2" path="res://assets/天气系统图片/栀子花1.webp" id="53_xyeuq"] @@ -80,7 +81,12 @@ [ext_resource type="Texture2D" uid="uid://ic2nvi3xlwl4" path="res://assets/装饰物图片/在线时长礼包.webp" id="64_crc4a"] [ext_resource type="Texture2D" uid="uid://1xgmal8sw6il" path="res://assets/装饰物图片/宠物背包.webp" id="67_onvxb"] [ext_resource type="Texture2D" uid="uid://ywdg7xgq7hm8" path="res://assets/装饰物图片/道具背包.webp" id="68_bpbm8"] +[ext_resource type="Texture2D" uid="uid://b8k7gknqsokt2" path="res://assets/装饰物图片/占卜台.webp" id="68_yceks"] [ext_resource type="Texture2D" uid="uid://bnv6wb0k443fv" path="res://assets/天气系统图片/柳叶2.webp" id="69_uyv6e"] +[ext_resource type="Texture2D" uid="uid://c1lviq3nc00u5" path="res://assets/装饰物图片/宠物天梯赛.webp" id="69_yceks"] +[ext_resource type="Texture2D" uid="uid://bwljgu2ltfc7j" path="res://assets/装饰物图片/宠物小窝.webp" id="70_yceks"] +[ext_resource type="Texture2D" uid="uid://dcfb173iikas" path="res://assets/装饰物图片/玩玩小游戏.webp" id="71_w7q6d"] +[ext_resource type="Texture2D" uid="uid://cpsfkh7khetfb" path="res://assets/装饰物图片/神秘农场.webp" id="72_7i23t"] [ext_resource type="Script" uid="uid://di8wjflimodb0" path="res://GameManager/DayNightSystem.gd" id="73_6fhdl"] [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_adtqp"] @@ -315,7 +321,7 @@ anchors_preset = 0 [node name="GameInfoHBox1" type="HBoxContainer" parent="UI/GUI"] layout_mode = 0 -offset_right = 1339.0 +offset_right = 1401.0 offset_bottom = 35.0 [node name="experience_image" type="TextureRect" parent="UI/GUI/GameInfoHBox1"] @@ -413,7 +419,7 @@ text = "点赞数:0" [node name="GameInfoHBox2" type="HBoxContainer" parent="UI/GUI"] layout_mode = 0 offset_top = 35.0 -offset_right = 1336.0 +offset_right = 1396.0 offset_bottom = 70.0 [node name="player_name_image" type="TextureRect" parent="UI/GUI/GameInfoHBox2"] @@ -490,7 +496,7 @@ text = "FPS:0" [node name="GameInfoHBox3" type="HBoxContainer" parent="UI/GUI"] layout_mode = 0 offset_top = 70.0 -offset_right = 1335.0 +offset_right = 1398.0 offset_bottom = 113.0 [node name="tip_image" type="TextureRect" parent="UI/GUI/GameInfoHBox3"] @@ -498,39 +504,50 @@ layout_mode = 2 texture = ExtResource("5_5b81d") expand_mode = 2 -[node name="tip" type="Label" parent="UI/GUI/GameInfoHBox3"] +[node name="tip" type="RichTextLabel" parent="UI/GUI/GameInfoHBox3"] +custom_minimum_size = Vector2(300, 0) layout_mode = 2 -theme_override_colors/font_color = Color(1, 0.564706, 1, 1) -theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +size_flags_horizontal = 3 theme_override_colors/font_outline_color = Color(0, 0, 0, 1) -theme_override_constants/shadow_offset_x = 3 -theme_override_constants/shadow_offset_y = 3 +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) theme_override_constants/outline_size = 10 +theme_override_constants/shadow_offset_y = 3 +theme_override_constants/shadow_offset_x = 3 theme_override_constants/shadow_outline_size = 10 -theme_override_fonts/font = ExtResource("3_t4s8j") -theme_override_font_sizes/font_size = 25 +theme_override_font_sizes/bold_italics_font_size = 25 +theme_override_font_sizes/italics_font_size = 25 +theme_override_font_sizes/mono_font_size = 25 +theme_override_font_sizes/normal_font_size = 25 +theme_override_font_sizes/bold_font_size = 25 +bbcode_enabled = true text = "游戏小提示" +vertical_alignment = 1 +threaded = true [node name="GlobalServerBroadcast_image" type="TextureRect" parent="UI/GUI/GameInfoHBox3"] layout_mode = 2 -texture = ExtResource("5_5b81d") +texture = ExtResource("13_5liyv") expand_mode = 2 -[node name="GlobalServerBroadcast" type="Label" parent="UI/GUI/GameInfoHBox3"] +[node name="GlobalServerBroadcast" type="RichTextLabel" parent="UI/GUI/GameInfoHBox3"] custom_minimum_size = Vector2(300, 0) layout_mode = 2 -theme_override_colors/font_color = Color(1, 0.564706, 0.137255, 1) -theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +size_flags_horizontal = 3 theme_override_colors/font_outline_color = Color(0, 0, 0, 1) -theme_override_constants/shadow_offset_x = 3 -theme_override_constants/shadow_offset_y = 3 +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) theme_override_constants/outline_size = 10 +theme_override_constants/shadow_offset_y = 3 +theme_override_constants/shadow_offset_x = 3 theme_override_constants/shadow_outline_size = 10 -theme_override_fonts/font = ExtResource("3_t4s8j") -theme_override_font_sizes/font_size = 25 +theme_override_font_sizes/bold_italics_font_size = 25 +theme_override_font_sizes/italics_font_size = 25 +theme_override_font_sizes/mono_font_size = 25 +theme_override_font_sizes/normal_font_size = 25 +theme_override_font_sizes/bold_font_size = 25 +bbcode_enabled = true text = "全服大喇叭" vertical_alignment = 1 -clip_text = true +threaded = true [node name="WatchBroadcast" type="Button" parent="UI/GUI/GameInfoHBox3"] layout_mode = 2 @@ -547,6 +564,7 @@ expand_mode = 2 [node name="onlineplayer" type="Label" parent="UI/GUI/GameInfoHBox3"] layout_mode = 2 +size_flags_horizontal = 3 theme_override_colors/font_color = Color(0.423529, 1, 0.533333, 1) theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) theme_override_colors/font_outline_color = Color(0, 0, 0, 1) @@ -644,6 +662,12 @@ layout_mode = 2 theme_override_font_sizes/font_size = 40 text = "使用道具" +[node name="SendMoneyButton" type="Button" parent="UI/GUI/VisitVBox"] +modulate = Color(1, 1, 0, 1) +layout_mode = 2 +theme_override_font_sizes/font_size = 40 +text = "送金币" + [node name="ReturnMyFarmButton" type="Button" parent="UI/GUI/VisitVBox"] modulate = Color(1, 1, 0.721569, 1) layout_mode = 2 @@ -694,7 +718,7 @@ text = "新手礼包" modulate = Color(0.407843, 0.796078, 0.996078, 1) layout_mode = 2 theme_override_font_sizes/font_size = 40 -text = "一键截图" +text = "截图模式" [node name="LuckyDrawButton" type="Button" parent="UI/GUI/OtherVBox"] modulate = Color(0.729412, 0.764706, 1, 1) @@ -777,11 +801,15 @@ offset_right = 1473.0 offset_bottom = 748.0 [node name="ItemStorePanel" parent="UI/BigPanel" instance=ExtResource("21_uhubb")] +visible = false offset_left = 80.0 offset_top = 77.0 offset_right = 1620.0 offset_bottom = 797.0 +[node name="ItemBagPanel" parent="UI/BigPanel" instance=ExtResource("20_n03md")] +visible = false + [node name="CropWarehousePanel" parent="UI/BigPanel" instance=ExtResource("18_5b81d")] [node name="PlayerBagPanel" parent="UI/BigPanel" instance=ExtResource("19_8kysg")] @@ -790,9 +818,6 @@ visible = false [node name="CropStorePanel" parent="UI/BigPanel" instance=ExtResource("17_ql8k3")] visible = false -[node name="ItemBagPanel" parent="UI/BigPanel" instance=ExtResource("20_n03md")] -visible = false - [node name="PlayerStorePanel" type="Panel" parent="UI/BigPanel"] visible = false offset_left = 69.0 @@ -957,6 +982,7 @@ visible = false visible = false [node name="LoginPanel" parent="UI/BigPanel" instance=ExtResource("11_6jgly")] +visible = false [node name="GameSettingPanel" parent="UI/BigPanel" instance=ExtResource("30_game_setting")] visible = false @@ -964,6 +990,12 @@ visible = false [node name="PetBattlePanel" parent="UI/BigPanel" instance=ExtResource("29_mw3xw")] visible = false +[node name="PlayGamePanel" parent="UI/BigPanel" instance=ExtResource("30_rawle")] +visible = false + +[node name="SpecialFarmPanel" parent="UI/BigPanel" instance=ExtResource("31_ww70k")] +visible = false + [node name="SmallPanel" type="CanvasLayer" parent="UI"] [node name="LoadProgressPanel" parent="UI/SmallPanel" instance=ExtResource("27_vygm6")] @@ -2111,7 +2143,7 @@ position_smoothing_enabled = true script = ExtResource("10_o8l48") max_zoom = 1.8 bounds_enabled = true -bounds_min = Vector2(-500, -400) +bounds_min = Vector2(-500, -600) bounds_max = Vector2(500, 500) [node name="GameManager" type="Node" parent="."] @@ -3293,7 +3325,7 @@ text = "在线时长礼包" horizontal_alignment = 1 vertical_alignment = 1 -[node name="Decoration2" type="Button" parent="Decoration4"] +[node name="TodayDivination" type="Button" parent="Decoration4"] self_modulate = Color(1, 1, 1, 0) custom_minimum_size = Vector2(100, 100) offset_left = 244.0 @@ -3302,17 +3334,17 @@ offset_right = 508.0 offset_bottom = 156.0 scale = Vector2(0.4, 0.4) -[node name="GrassGroundImage" type="Sprite2D" parent="Decoration4/Decoration2"] +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration4/TodayDivination"] position = Vector2(132, 134) scale = Vector2(1.4, 1.4) texture = ExtResource("48_2i8fe") -[node name="Image" type="Sprite2D" parent="Decoration4/Decoration2"] +[node name="Image" type="Sprite2D" parent="Decoration4/TodayDivination"] position = Vector2(132, 48) scale = Vector2(1.2, 1.2) -texture = ExtResource("50_sqnmr") +texture = ExtResource("68_yceks") -[node name="Name" type="RichTextLabel" parent="Decoration4/Decoration2"] +[node name="Name" type="RichTextLabel" parent="Decoration4/TodayDivination"] z_index = 5 layout_mode = 0 offset_left = -65.0 @@ -3325,10 +3357,11 @@ theme_override_font_sizes/mono_font_size = 40 theme_override_font_sizes/normal_font_size = 40 theme_override_font_sizes/bold_font_size = 40 bbcode_enabled = true +text = "今日占卜" horizontal_alignment = 1 vertical_alignment = 1 -[node name="Decoration3" type="Button" parent="Decoration4"] +[node name="PetLadderMatch" type="Button" parent="Decoration4"] self_modulate = Color(1, 1, 1, 0) custom_minimum_size = Vector2(100, 100) offset_left = 366.0 @@ -3337,17 +3370,17 @@ offset_right = 630.0 offset_bottom = 156.0 scale = Vector2(0.4, 0.4) -[node name="GrassGroundImage" type="Sprite2D" parent="Decoration4/Decoration3"] +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration4/PetLadderMatch"] position = Vector2(132, 134) scale = Vector2(1.4, 1.4) texture = ExtResource("48_2i8fe") -[node name="Image" type="Sprite2D" parent="Decoration4/Decoration3"] +[node name="Image" type="Sprite2D" parent="Decoration4/PetLadderMatch"] position = Vector2(132, 48) scale = Vector2(1.2, 1.2) -texture = ExtResource("51_2i8fe") +texture = ExtResource("69_yceks") -[node name="Name" type="RichTextLabel" parent="Decoration4/Decoration3"] +[node name="Name" type="RichTextLabel" parent="Decoration4/PetLadderMatch"] z_index = 5 layout_mode = 0 offset_left = -65.0 @@ -3360,10 +3393,11 @@ theme_override_font_sizes/mono_font_size = 40 theme_override_font_sizes/normal_font_size = 40 theme_override_font_sizes/bold_font_size = 40 bbcode_enabled = true +text = "宠物星辰塔" horizontal_alignment = 1 vertical_alignment = 1 -[node name="Decoration4" type="Button" parent="Decoration4"] +[node name="PetNest" type="Button" parent="Decoration4"] self_modulate = Color(1, 1, 1, 0) custom_minimum_size = Vector2(100, 100) offset_left = 488.0 @@ -3372,17 +3406,17 @@ offset_right = 752.0 offset_bottom = 156.0 scale = Vector2(0.4, 0.4) -[node name="GrassGroundImage" type="Sprite2D" parent="Decoration4/Decoration4"] +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration4/PetNest"] position = Vector2(132, 134) scale = Vector2(1.4, 1.4) texture = ExtResource("48_2i8fe") -[node name="Image" type="Sprite2D" parent="Decoration4/Decoration4"] +[node name="Image" type="Sprite2D" parent="Decoration4/PetNest"] position = Vector2(132, 48) scale = Vector2(1.2, 1.2) -texture = ExtResource("50_sqnmr") +texture = ExtResource("70_yceks") -[node name="Name" type="RichTextLabel" parent="Decoration4/Decoration4"] +[node name="Name" type="RichTextLabel" parent="Decoration4/PetNest"] z_index = 5 layout_mode = 0 offset_left = -65.0 @@ -3395,10 +3429,11 @@ theme_override_font_sizes/mono_font_size = 40 theme_override_font_sizes/normal_font_size = 40 theme_override_font_sizes/bold_font_size = 40 bbcode_enabled = true +text = "宠物小窝" horizontal_alignment = 1 vertical_alignment = 1 -[node name="Decoration5" type="Button" parent="Decoration4"] +[node name="PlaySmallGame" type="Button" parent="Decoration4"] self_modulate = Color(1, 1, 1, 0) custom_minimum_size = Vector2(100, 100) offset_left = 610.0 @@ -3407,17 +3442,17 @@ offset_right = 874.0 offset_bottom = 156.0 scale = Vector2(0.4, 0.4) -[node name="GrassGroundImage" type="Sprite2D" parent="Decoration4/Decoration5"] +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration4/PlaySmallGame"] position = Vector2(132, 134) scale = Vector2(1.4, 1.4) texture = ExtResource("48_2i8fe") -[node name="Image" type="Sprite2D" parent="Decoration4/Decoration5"] +[node name="Image" type="Sprite2D" parent="Decoration4/PlaySmallGame"] position = Vector2(132, 48) scale = Vector2(1.2, 1.2) -texture = ExtResource("49_xjiif") +texture = ExtResource("71_w7q6d") -[node name="Name" type="RichTextLabel" parent="Decoration4/Decoration5"] +[node name="Name" type="RichTextLabel" parent="Decoration4/PlaySmallGame"] z_index = 5 layout_mode = 0 offset_left = -65.0 @@ -3430,10 +3465,11 @@ theme_override_font_sizes/mono_font_size = 40 theme_override_font_sizes/normal_font_size = 40 theme_override_font_sizes/bold_font_size = 40 bbcode_enabled = true +text = "玩玩小游戏" horizontal_alignment = 1 vertical_alignment = 1 -[node name="Decoration6" type="Button" parent="Decoration4"] +[node name="MysteryFarmland" type="Button" parent="Decoration4"] self_modulate = Color(1, 1, 1, 0) custom_minimum_size = Vector2(100, 100) offset_left = 732.0 @@ -3442,17 +3478,17 @@ offset_right = 996.0 offset_bottom = 156.0 scale = Vector2(0.4, 0.4) -[node name="GrassGroundImage" type="Sprite2D" parent="Decoration4/Decoration6"] +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration4/MysteryFarmland"] position = Vector2(132, 134) scale = Vector2(1.4, 1.4) texture = ExtResource("48_2i8fe") -[node name="Image" type="Sprite2D" parent="Decoration4/Decoration6"] +[node name="Image" type="Sprite2D" parent="Decoration4/MysteryFarmland"] position = Vector2(132, 48) scale = Vector2(1.2, 1.2) -texture = ExtResource("49_xjiif") +texture = ExtResource("72_7i23t") -[node name="Name" type="RichTextLabel" parent="Decoration4/Decoration6"] +[node name="Name" type="RichTextLabel" parent="Decoration4/MysteryFarmland"] z_index = 5 layout_mode = 0 offset_left = -65.0 @@ -3465,6 +3501,7 @@ theme_override_font_sizes/mono_font_size = 40 theme_override_font_sizes/normal_font_size = 40 theme_override_font_sizes/bold_font_size = 40 bbcode_enabled = true +text = "神秘农场" horizontal_alignment = 1 vertical_alignment = 1 @@ -3689,6 +3726,7 @@ environment = SubResource("Environment_uyv6e") [connection signal="pressed" from="UI/GUI/FarmVBox/AddNewGroundButton" to="." method="_on_add_new_ground_button_pressed"] [connection signal="pressed" from="UI/GUI/VisitVBox/LikeButton" to="." method="_on_like_button_pressed"] [connection signal="pressed" from="UI/GUI/VisitVBox/BattleButton" to="." method="_on_battle_button_pressed"] +[connection signal="pressed" from="UI/GUI/VisitVBox/SendMoneyButton" to="." method="_on_send_money_button_pressed"] [connection signal="pressed" from="UI/GUI/VisitVBox/ReturnMyFarmButton" to="." method="_on_return_my_farm_button_pressed"] [connection signal="pressed" from="UI/GUI/OtherVBox/TodayDivinationButton" to="." method="_on_today_divination_button_pressed"] [connection signal="pressed" from="UI/GUI/OtherVBox/MyStoreButton" to="." method="_on_my_store_button_pressed"] @@ -3718,5 +3756,10 @@ environment = SubResource("Environment_uyv6e") [connection signal="pressed" from="Decoration/WisdomTree" to="." method="_on_wisdom_tree_pressed"] [connection signal="pressed" from="Decoration4/DailyCheckinGift" to="." method="_on_daily_checkin_gift_pressed"] [connection signal="pressed" from="Decoration4/OnlineTimeGift" to="." method="_on_online_time_gift_pressed"] +[connection signal="pressed" from="Decoration4/TodayDivination" to="." method="_on_today_divination_pressed"] +[connection signal="pressed" from="Decoration4/PetLadderMatch" to="." method="_on_pet_ladder_match_pressed"] +[connection signal="pressed" from="Decoration4/PetNest" to="." method="_on_pet_nest_pressed"] +[connection signal="pressed" from="Decoration4/PlaySmallGame" to="." method="_on_play_small_game_pressed"] +[connection signal="pressed" from="Decoration4/MysteryFarmland" to="." method="_on_mystery_farmland_pressed"] [connection signal="pressed" from="Decoration4/PetBag" to="." method="_on_pet_bag_pressed"] [connection signal="pressed" from="Decoration4/ItemBag" to="." method="_on_item_bag_pressed"] diff --git a/MainGame.tscn89825202664.tmp b/MainGame.tscn89825202664.tmp new file mode 100644 index 0000000..bf2d16c --- /dev/null +++ b/MainGame.tscn89825202664.tmp @@ -0,0 +1,3764 @@ +[gd_scene load_steps=113 format=3 uid="uid://dgh61dttaas5a"] + +[ext_resource type="Script" uid="uid://2pt11sfcaxf7" path="res://MainGame.gd" id="1_v3yaj"] +[ext_resource type="Texture2D" uid="uid://du2pyiojliasy" path="res://assets/游戏UI/经验球.webp" id="2_6jgly"] +[ext_resource type="PackedScene" uid="uid://bkivlkirrx6u8" path="res://CopyItems/crop_item.tscn" id="3_isiom"] +[ext_resource type="FontFile" uid="uid://dl31rnmd4lia" path="res://assets/字体/MapleMono-NF-CN-Bold.ttf" id="3_t4s8j"] +[ext_resource type="Texture2D" uid="uid://ftv231igtdoq" path="res://assets/游戏UI/等级.webp" id="3_y1hsh"] +[ext_resource type="Texture2D" uid="uid://bqib5y8uwg6hx" path="res://assets/游戏UI/钱币.webp" id="4_ql8k3"] +[ext_resource type="Texture2D" uid="uid://waqbwo2r33j3" path="res://assets/游戏UI/小提示.webp" id="5_5b81d"] +[ext_resource type="Texture2D" uid="uid://bnhyqsw8yjekh" path="res://assets/游戏UI/体力值图标.webp" id="5_n03md"] +[ext_resource type="Texture2D" uid="uid://cj0qac0wmm0q8" path="res://assets/游戏UI/点赞图标.webp" id="6_8kysg"] +[ext_resource type="PackedScene" uid="uid://cpxiaqh0y6a5d" path="res://Network/TCPNetworkManager.tscn" id="7_401ut"] +[ext_resource type="Texture2D" uid="uid://d3pev0nbt8sjd" path="res://assets/游戏UI/玩家昵称.webp" id="7_n03md"] +[ext_resource type="Texture2D" uid="uid://cxm72d5t4dn0q" path="res://assets/游戏UI/农场名称.webp" id="8_uhubb"] +[ext_resource type="Texture2D" uid="uid://b665dc0ye72lg" path="res://assets/游戏UI/服务器连接状态.webp" id="9_uc6q1"] +[ext_resource type="Script" uid="uid://c7bxje0wvvgo4" path="res://game_camera.gd" id="10_o8l48"] +[ext_resource type="Texture2D" uid="uid://dsuaw8kcdtrst" path="res://assets/游戏UI/FPS图标.webp" id="10_uhubb"] +[ext_resource type="Texture2D" uid="uid://bso5fyjavdien" path="res://assets/游戏UI/玩家数图标.webp" id="10_vygm6"] +[ext_resource type="PackedScene" uid="uid://cbhitturvihqj" path="res://Scene/BigPanel/LoginPanel.tscn" id="11_6jgly"] +[ext_resource type="PackedScene" uid="uid://dckc8nrn7p425" path="res://Scene/SmallPanel/LandPanel.tscn" id="12_y1hsh"] +[ext_resource type="Texture2D" uid="uid://dsds2gsvqxfak" path="res://assets/游戏UI/大喇叭.webp" id="13_5liyv"] +[ext_resource type="PackedScene" uid="uid://dpiy0aim20n2h" path="res://Scene/SmallPanel/OnlineGiftPanel.tscn" id="14_5b81d"] +[ext_resource type="PackedScene" uid="uid://4rwitowdt4h" path="res://Scene/SmallPanel/OneClickPlantPanel.tscn" id="15_8kysg"] +[ext_resource type="PackedScene" uid="uid://btp1h6hic2sin" path="res://Scene/Dialog/AcceptDialog.tscn" id="16_0igvr"] +[ext_resource type="PackedScene" uid="uid://dbfqu87627yg6" path="res://Scene/BigPanel/PlayerRankingPanel.tscn" id="16_n03md"] +[ext_resource type="Script" uid="uid://dckw8dskfbnkp" path="res://background.gd" id="17_0igvr"] +[ext_resource type="PackedScene" uid="uid://bndf1e4sgdjr6" path="res://Scene/BigPanel/LuckyDrawPanel.tscn" id="17_f21le"] +[ext_resource type="PackedScene" uid="uid://hesp70n3ondo" path="res://Scene/BigPanel/CropStorePanel.tscn" id="17_ql8k3"] +[ext_resource type="PackedScene" uid="uid://drw18a6mcr2of" path="res://Scene/BigPanel/CropWarehousePanel.tscn" id="18_5b81d"] +[ext_resource type="PackedScene" uid="uid://smypui0vyso5" path="res://Scene/BigPanel/DailyCheckInPanel.tscn" id="18_m6fch"] +[ext_resource type="PackedScene" uid="uid://bseuwniienrqy" path="res://Scene/BigPanel/PlayerBagPanel.tscn" id="19_8kysg"] +[ext_resource type="PackedScene" uid="uid://cehw5sx5pgmmc" path="res://Scene/BigPanel/ItemBagPanel.tscn" id="20_n03md"] +[ext_resource type="PackedScene" uid="uid://j4ft87o7jk14" path="res://Scene/BigPanel/ItemStorePanel.tscn" id="21_uhubb"] +[ext_resource type="Script" uid="uid://bdhwvqsmakna2" path="res://Script/BigPanel/PetBagPanel.gd" id="23_uc6q1"] +[ext_resource type="Script" uid="uid://bdavskipn547h" path="res://Script/BigPanel/PlayerStorePanel.gd" id="24_dygid"] +[ext_resource type="PackedScene" uid="uid://cnjidcwuv4nn4" path="res://Scene/BigPanel/PetStorePanel.tscn" id="24_uc6q1"] +[ext_resource type="PackedScene" uid="uid://d3i0l6ysrde6o" path="res://Scene/SmallPanel/AccountSettingPanel.tscn" id="26_uc6q1"] +[ext_resource type="PackedScene" uid="uid://d1lu2yg4xl382" path="res://Scene/SmallPanel/LoadProgressPanel.tscn" id="27_vygm6"] +[ext_resource type="Script" uid="uid://bvhupqlw2h1j8" path="res://Script/SmallPanel/DebugPanel.gd" id="28_8kysg"] +[ext_resource type="Script" uid="uid://ca2chgx5w3g1y" path="res://Components/GameBGMPlayer.gd" id="28_m6fch"] +[ext_resource type="Script" uid="uid://d4fvv2sjngajr" path="res://Script/Dialog/BatchBuyPopup.gd" id="29_5b81d"] +[ext_resource type="PackedScene" uid="uid://diwbnwhnq026" path="res://Scene/NewPet/PetBattlePanel.tscn" id="29_mw3xw"] +[ext_resource type="PackedScene" uid="uid://dos15dmc1b6bt" path="res://GUI/GameSettingPanel.tscn" id="30_game_setting"] +[ext_resource type="PackedScene" uid="uid://jjlgdnoo8e52" path="res://Scene/BigPanel/PlayGamePanel.tscn" id="30_rawle"] +[ext_resource type="Texture2D" uid="uid://2sdfbvf1isif" path="res://icon.svg" id="31_uc6q1"] +[ext_resource type="Script" uid="uid://doo34ll0yb078" path="res://Script/SmallPanel/PetInformPanel.gd" id="31_vygm6"] +[ext_resource type="PackedScene" uid="uid://byxhjyyaahs6q" path="res://Scene/BigPanel/SpecialFarmPanel.tscn" id="31_ww70k"] +[ext_resource type="Script" uid="uid://bakeq8tm6r4j4" path="res://Script/SmallPanel/GlobalServerBroadcastPanel.gd" id="34_k1sw4"] +[ext_resource type="Script" uid="uid://dobao5y5s2dij" path="res://Script/SmallPanel/ScareCrowPanel.gd" id="35_6ylhg"] +[ext_resource type="Texture2D" uid="uid://d06uaoowmcvxj" path="res://assets/稻草人图片/稻草人2.webp" id="36_jyxgm"] +[ext_resource type="Texture2D" uid="uid://cbe0f8ex17csy" path="res://assets/稻草人图片/稻草人3.webp" id="37_6ylhg"] +[ext_resource type="PackedScene" uid="uid://ibl5wbbw3pwc" path="res://CopyItems/item_button.tscn" id="39_cdkxt"] +[ext_resource type="Script" uid="uid://dwf28j01ckg3y" path="res://Script/SmallPanel/WisdomTreePanel.gd" id="39_np7ck"] +[ext_resource type="Script" uid="uid://b185o1hjnlrv5" path="res://Script/SmallPanel/CropInformPanel.gd" id="41_iluto"] +[ext_resource type="Texture2D" uid="uid://3ff2lnbc0op7" path="res://assets/稻草人图片/稻草人1.webp" id="43_6rkns"] +[ext_resource type="Script" uid="uid://dsmmxivba06ab" path="res://Script/Dialog/BatchSellPopup.gd" id="44_av1bx"] +[ext_resource type="Script" uid="uid://bkel88rscubov" path="res://Script/SmallPanel/TodayDivinationPanel.gd" id="44_mw3xw"] +[ext_resource type="FontFile" uid="uid://b81msdfp2ym2g" path="res://assets/字体/MapleMono-NF-CN-BoldItalic.ttf" id="45_vexnj"] +[ext_resource type="Texture2D" uid="uid://cbdm5e6s8bf6l" path="res://assets/智慧树图片/智慧树4.webp" id="45_xvovi"] +[ext_resource type="Script" uid="uid://cha0uw4ra1trr" path="res://Script/Dialog/AddProduct2StorePopup.gd" id="46_8d602"] +[ext_resource type="Texture2D" uid="uid://dilipbs0lncpd" path="res://assets/草地图片/草地10.webp" id="48_2i8fe"] +[ext_resource type="Texture2D" uid="uid://du34yctd8bd8m" path="res://assets/灌木丛图片/灌木丛1.webp" id="49_xjiif"] +[ext_resource type="Texture2D" uid="uid://dk4yl4ghmxaa2" path="res://assets/天气系统图片/雪花.webp" id="53_4ka7t"] +[ext_resource type="Texture2D" uid="uid://chcgrmluhfxuk" path="res://assets/天气系统图片/樱花1.webp" id="53_tdq2s"] +[ext_resource type="Texture2D" uid="uid://c1qqalp7owgy2" path="res://assets/天气系统图片/栀子花1.webp" id="53_xyeuq"] +[ext_resource type="Texture2D" uid="uid://b7g5ut03rm53n" path="res://assets/装饰物图片/小卖部.webp" id="54_06tja"] +[ext_resource type="Texture2D" uid="uid://dfkgmj7e0555q" path="res://assets/天气系统图片/樱花2.webp" id="54_dygjy"] +[ext_resource type="Texture2D" uid="uid://bfducebx4c4il" path="res://assets/天气系统图片/柳叶3.webp" id="54_jiccn"] +[ext_resource type="Texture2D" uid="uid://cd7x78uyi2csh" path="res://assets/天气系统图片/栀子花2.webp" id="54_t4s8j"] +[ext_resource type="Texture2D" uid="uid://dl58ie2lneq77" path="res://assets/天气系统图片/柳叶1.webp" id="55_e8wx8"] +[ext_resource type="Texture2D" uid="uid://q0difb6wjkgm" path="res://assets/天气系统图片/樱花3.webp" id="55_edvcq"] +[ext_resource type="Texture2D" uid="uid://j3h0ocagxf8" path="res://assets/装饰物图片/种子商店.webp" id="55_g4i0f"] +[ext_resource type="Texture2D" uid="uid://cqqyc3ddwtvpn" path="res://assets/天气系统图片/栀子花3.webp" id="55_iluto"] +[ext_resource type="Texture2D" uid="uid://d4e54gh5iccul" path="res://assets/装饰物图片/道具商店.webp" id="56_rlmnt"] +[ext_resource type="Texture2D" uid="uid://deow5dqdm412v" path="res://assets/装饰物图片/宠物商店.webp" id="57_rlmnt"] +[ext_resource type="Texture2D" uid="uid://dfeqilibb6ecs" path="res://assets/装饰物图片/作物仓库.webp" id="58_mjfri"] +[ext_resource type="Texture2D" uid="uid://b738esrtjlho7" path="res://assets/装饰物图片/种子仓库.webp" id="59_612dn"] +[ext_resource type="Texture2D" uid="uid://cbj1s6ctp0utb" path="res://assets/装饰物图片/玩家排行榜.webp" id="60_2fqxv"] +[ext_resource type="Script" uid="uid://o4mcuqoivqri" path="res://GameManager/WeatherSystem.gd" id="62_uyv6e"] +[ext_resource type="Texture2D" uid="uid://deh0dnprkw155" path="res://assets/装饰物图片/每日签到礼包.webp" id="63_ekowe"] +[ext_resource type="Texture2D" uid="uid://ic2nvi3xlwl4" path="res://assets/装饰物图片/在线时长礼包.webp" id="64_crc4a"] +[ext_resource type="Texture2D" uid="uid://1xgmal8sw6il" path="res://assets/装饰物图片/宠物背包.webp" id="67_onvxb"] +[ext_resource type="Texture2D" uid="uid://ywdg7xgq7hm8" path="res://assets/装饰物图片/道具背包.webp" id="68_bpbm8"] +[ext_resource type="Texture2D" uid="uid://b8k7gknqsokt2" path="res://assets/装饰物图片/占卜台.webp" id="68_yceks"] +[ext_resource type="Texture2D" uid="uid://bnv6wb0k443fv" path="res://assets/天气系统图片/柳叶2.webp" id="69_uyv6e"] +[ext_resource type="Texture2D" uid="uid://c1lviq3nc00u5" path="res://assets/装饰物图片/宠物天梯赛.webp" id="69_yceks"] +[ext_resource type="Texture2D" uid="uid://bwljgu2ltfc7j" path="res://assets/装饰物图片/宠物小窝.webp" id="70_yceks"] +[ext_resource type="Texture2D" uid="uid://dcfb173iikas" path="res://assets/装饰物图片/玩玩小游戏.webp" id="71_w7q6d"] +[ext_resource type="Texture2D" uid="uid://cpsfkh7khetfb" path="res://assets/装饰物图片/神秘农场.webp" id="72_7i23t"] +[ext_resource type="Script" uid="uid://di8wjflimodb0" path="res://GameManager/DayNightSystem.gd" id="73_6fhdl"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_adtqp"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_n03md"] +border_width_left = 5 +border_width_top = 5 +border_width_right = 5 +border_width_bottom = 5 +corner_radius_top_left = 20 +corner_radius_top_right = 20 +corner_radius_bottom_right = 20 +corner_radius_bottom_left = 20 +corner_detail = 20 +shadow_size = 20 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_8d602"] +border_width_left = 5 +border_width_top = 5 +border_width_right = 5 +border_width_bottom = 5 +corner_radius_top_left = 20 +corner_radius_top_right = 20 +corner_radius_bottom_right = 20 +corner_radius_bottom_left = 20 +corner_detail = 20 +shadow_size = 20 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_5b81d"] +border_width_left = 10 +border_width_top = 10 +border_width_right = 10 +border_width_bottom = 10 +corner_detail = 20 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_2i8fe"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_k1sw4"] +bg_color = Color(0.6, 0.6, 0.6, 0.984314) +border_width_left = 10 +border_width_top = 10 +border_width_right = 10 +border_width_bottom = 10 +corner_detail = 20 +shadow_size = 20 +shadow_offset = Vector2(5, 5) + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_jyxgm"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_np7ck"] +border_width_left = 15 +border_width_top = 15 +border_width_right = 15 +border_width_bottom = 15 +corner_radius_top_left = 20 +corner_radius_top_right = 20 +corner_radius_bottom_right = 20 +corner_radius_bottom_left = 20 +corner_detail = 20 +shadow_size = 30 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_xyeuq"] +border_width_left = 15 +border_width_top = 15 +border_width_right = 15 +border_width_bottom = 15 +corner_detail = 20 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_bpbm8"] +border_width_left = 15 +border_width_top = 15 +border_width_right = 15 +border_width_bottom = 15 +corner_detail = 20 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_t4s8j"] +border_width_left = 15 +border_width_top = 15 +border_width_right = 15 +border_width_bottom = 15 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_6ylhg"] +border_width_left = 10 +border_width_top = 10 +border_width_right = 10 +border_width_bottom = 10 +corner_radius_top_left = 20 +corner_radius_top_right = 20 +corner_radius_bottom_right = 20 +corner_radius_bottom_left = 20 +corner_detail = 20 +shadow_size = 20 +shadow_offset = Vector2(3, 3) + +[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_tdq2s"] +particle_flag_disable_z = true +emission_shape = 3 +emission_box_extents = Vector3(1000, 1, 1) +gravity = Vector3(-30, 80, 0) +scale_min = 0.2 +scale_max = 0.3 +turbulence_enabled = true +turbulence_noise_speed = Vector3(10, 0, 0) +turbulence_influence_min = 0.02 +turbulence_influence_max = 0.07 + +[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_dygjy"] +particle_flag_disable_z = true +emission_shape = 3 +emission_box_extents = Vector3(1000, 1, 1) +gravity = Vector3(-30, 80, 0) +scale_min = 0.2 +scale_max = 0.3 +turbulence_enabled = true +turbulence_noise_speed = Vector3(10, 0, 0) +turbulence_influence_min = 0.02 +turbulence_influence_max = 0.07 + +[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_6fhdl"] +particle_flag_disable_z = true +emission_shape = 3 +emission_box_extents = Vector3(1000, 1, 1) +gravity = Vector3(-30, 80, 0) +scale_min = 0.4 +scale_max = 0.5 +turbulence_enabled = true +turbulence_noise_speed = Vector3(10, 0, 0) +turbulence_influence_min = 0.02 +turbulence_influence_max = 0.07 + +[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_e8wx8"] +particle_flag_disable_z = true +emission_shape = 3 +emission_box_extents = Vector3(1000, 1, 1) +gravity = Vector3(-30, 80, 0) +scale_min = 0.4 +scale_max = 0.5 +turbulence_enabled = true +turbulence_noise_speed = Vector3(10, 0, 0) +turbulence_influence_min = 0.02 +turbulence_influence_max = 0.07 + +[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_4ka7t"] +particle_flag_disable_z = true +emission_shape = 3 +emission_box_extents = Vector3(1000, 1, 1) +gravity = Vector3(-45, 80, 0) +scale_min = 0.4 +scale_max = 0.5 +turbulence_enabled = true +turbulence_noise_speed = Vector3(10, 0, 0) +turbulence_influence_min = 0.02 +turbulence_influence_max = 0.07 + +[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_jiccn"] +lifetime_randomness = 0.6 +particle_flag_disable_z = true +emission_shape = 3 +emission_box_extents = Vector3(1000, 1, 1) +direction = Vector3(0, 1, 0) +spread = 0.0 +initial_velocity_min = 100.0 +initial_velocity_max = 400.0 +gravity = Vector3(0, 98, 0) +scale_min = 4.0 +scale_max = 6.0 +color = Color(0, 0.380392, 1, 1) +turbulence_influence_min = 0.02 +turbulence_influence_max = 0.08 + +[sub_resource type="Curve" id="Curve_5dq3w"] +_data = [Vector2(0, 0.987952), 0.0, 0.0, 0, 0, Vector2(1e-05, 1), 0.0, 0.0, 0, 0, Vector2(0.0153846, 1), 0.0, 0.0, 0, 0, Vector2(0.838462, 0.253012), -2.16867, -2.16867, 0, 0, Vector2(0.976923, 0), 0.0, 0.0, 0, 0, Vector2(0.99999, 0), 0.0, 0.0, 0, 0, Vector2(1, 0), 0.0, 0.0, 0, 0] +point_count = 7 + +[sub_resource type="CurveTexture" id="CurveTexture_jiccn"] +curve = SubResource("Curve_5dq3w") + +[sub_resource type="Gradient" id="Gradient_e8wx8"] +offsets = PackedFloat32Array(0.372727, 0.718182) +colors = PackedColorArray(0.886, 0.886, 0.886, 0.313, 1, 1, 1, 1) + +[sub_resource type="GradientTexture1D" id="GradientTexture1D_tdq2s"] +gradient = SubResource("Gradient_e8wx8") + +[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_nf3jg"] +particle_flag_disable_z = true +emission_shape = 3 +emission_box_extents = Vector3(1000, 1, 1) +gravity = Vector3(45, 98, 0) +scale_min = 0.4 +scale_max = 0.5 +color_initial_ramp = SubResource("GradientTexture1D_tdq2s") +alpha_curve = SubResource("CurveTexture_jiccn") +turbulence_enabled = true +turbulence_noise_speed = Vector3(10, 0, 0) +turbulence_influence_min = 0.02 +turbulence_influence_max = 0.07 + +[sub_resource type="Environment" id="Environment_uyv6e"] +background_mode = 3 +glow_enabled = true +glow_intensity = 1.0 +glow_strength = 0.75 +glow_bloom = 1.0 +glow_blend_mode = 1 +fog_mode = 1 +fog_sun_scatter = 1.0 +fog_density = 1.0 + +[node name="main" type="Node"] +script = ExtResource("1_v3yaj") + +[node name="PetPatrolPoints" type="Node" parent="."] + +[node name="Pos1" type="Marker2D" parent="PetPatrolPoints"] +position = Vector2(-276, -151) + +[node name="Pos2" type="Marker2D" parent="PetPatrolPoints"] +position = Vector2(-276, 1052) + +[node name="Pos3" type="Marker2D" parent="PetPatrolPoints"] +position = Vector2(1391, 1052) + +[node name="Pos4" type="Marker2D" parent="PetPatrolPoints"] +position = Vector2(1391, -184) + +[node name="UI" type="CanvasLayer" parent="."] + +[node name="GUI" type="Control" parent="UI"] +layout_mode = 3 +anchors_preset = 0 + +[node name="GameInfoHBox1" type="HBoxContainer" parent="UI/GUI"] +layout_mode = 0 +offset_right = 1401.0 +offset_bottom = 35.0 + +[node name="experience_image" type="TextureRect" parent="UI/GUI/GameInfoHBox1"] +self_modulate = Color(0.498039, 1, 0, 1) +layout_mode = 2 +texture = ExtResource("2_6jgly") +expand_mode = 2 + +[node name="experience" type="Label" parent="UI/GUI/GameInfoHBox1"] +modulate = Color(0, 1, 0, 1) +layout_mode = 2 +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/shadow_offset_x = 3 +theme_override_constants/shadow_offset_y = 3 +theme_override_constants/outline_size = 10 +theme_override_constants/shadow_outline_size = 10 +theme_override_fonts/font = ExtResource("3_t4s8j") +theme_override_font_sizes/font_size = 25 +text = "经验:999" +vertical_alignment = 1 + +[node name="level_image" type="TextureRect" parent="UI/GUI/GameInfoHBox1"] +layout_mode = 2 +texture = ExtResource("3_y1hsh") +expand_mode = 2 + +[node name="level" type="Label" parent="UI/GUI/GameInfoHBox1"] +modulate = Color(0, 1, 1, 1) +layout_mode = 2 +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +theme_override_colors/font_outline_color = Color(0, 0, 0.372549, 1) +theme_override_constants/shadow_offset_x = 3 +theme_override_constants/shadow_offset_y = 3 +theme_override_constants/outline_size = 10 +theme_override_constants/shadow_outline_size = 10 +theme_override_fonts/font = ExtResource("3_t4s8j") +theme_override_font_sizes/font_size = 25 +text = "等级:100" + +[node name="money_image" type="TextureRect" parent="UI/GUI/GameInfoHBox1"] +layout_mode = 2 +texture = ExtResource("4_ql8k3") +expand_mode = 2 + +[node name="money" type="Label" parent="UI/GUI/GameInfoHBox1"] +modulate = Color(1, 1, 0, 1) +layout_mode = 2 +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +theme_override_colors/font_outline_color = Color(0, 0, 0.372549, 1) +theme_override_constants/shadow_offset_x = 3 +theme_override_constants/shadow_offset_y = 3 +theme_override_constants/outline_size = 10 +theme_override_constants/shadow_outline_size = 10 +theme_override_fonts/font = ExtResource("3_t4s8j") +theme_override_font_sizes/font_size = 25 +text = "钱币:999" + +[node name="hungervalue_image" type="TextureRect" parent="UI/GUI/GameInfoHBox1"] +layout_mode = 2 +texture = ExtResource("5_n03md") +expand_mode = 2 + +[node name="hunger_value" type="Label" parent="UI/GUI/GameInfoHBox1"] +modulate = Color(0.88617, 0.748355, 0.764238, 1) +layout_mode = 2 +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +theme_override_colors/font_outline_color = Color(0, 0, 0.372549, 1) +theme_override_constants/shadow_offset_x = 3 +theme_override_constants/shadow_offset_y = 3 +theme_override_constants/outline_size = 10 +theme_override_constants/shadow_outline_size = 10 +theme_override_fonts/font = ExtResource("3_t4s8j") +theme_override_font_sizes/font_size = 25 +text = "体力值:20" + +[node name="like_image" type="TextureRect" parent="UI/GUI/GameInfoHBox1"] +layout_mode = 2 +texture = ExtResource("6_8kysg") +expand_mode = 2 + +[node name="like" type="Label" parent="UI/GUI/GameInfoHBox1"] +layout_mode = 2 +theme_override_colors/font_color = Color(0.992157, 0.482353, 0.482353, 1) +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +theme_override_colors/font_outline_color = Color(0, 0, 0.372549, 1) +theme_override_constants/shadow_offset_x = 3 +theme_override_constants/shadow_offset_y = 3 +theme_override_constants/outline_size = 10 +theme_override_constants/shadow_outline_size = 10 +theme_override_fonts/font = ExtResource("3_t4s8j") +theme_override_font_sizes/font_size = 25 +text = "点赞数:0" + +[node name="GameInfoHBox2" type="HBoxContainer" parent="UI/GUI"] +layout_mode = 0 +offset_top = 35.0 +offset_right = 1396.0 +offset_bottom = 70.0 + +[node name="player_name_image" type="TextureRect" parent="UI/GUI/GameInfoHBox2"] +layout_mode = 2 +texture = ExtResource("7_n03md") +expand_mode = 2 + +[node name="player_name" type="Label" parent="UI/GUI/GameInfoHBox2"] +modulate = Color(1, 0.670588, 0.490196, 1) +layout_mode = 2 +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/shadow_offset_x = 3 +theme_override_constants/shadow_offset_y = 3 +theme_override_constants/outline_size = 10 +theme_override_constants/shadow_outline_size = 10 +theme_override_fonts/font = ExtResource("3_t4s8j") +theme_override_font_sizes/font_size = 25 +text = "玩家昵称:树萌芽" + +[node name="farm_name_image" type="TextureRect" parent="UI/GUI/GameInfoHBox2"] +layout_mode = 2 +texture = ExtResource("8_uhubb") +expand_mode = 3 + +[node name="farm_name" type="Label" parent="UI/GUI/GameInfoHBox2"] +modulate = Color(1, 0.858824, 0.623529, 1) +layout_mode = 2 +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/shadow_offset_x = 3 +theme_override_constants/shadow_offset_y = 3 +theme_override_constants/outline_size = 10 +theme_override_constants/shadow_outline_size = 10 +theme_override_fonts/font = ExtResource("3_t4s8j") +theme_override_font_sizes/font_size = 25 +text = "农场名称:树萌芽的农场" + +[node name="status_label_image" type="TextureRect" parent="UI/GUI/GameInfoHBox2"] +layout_mode = 2 +texture = ExtResource("9_uc6q1") +expand_mode = 2 + +[node name="StatusLabel" type="Label" parent="UI/GUI/GameInfoHBox2"] +layout_mode = 2 +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/shadow_offset_x = 3 +theme_override_constants/shadow_offset_y = 3 +theme_override_constants/outline_size = 10 +theme_override_constants/shadow_outline_size = 10 +theme_override_fonts/font = ExtResource("3_t4s8j") +theme_override_font_sizes/font_size = 25 +text = "服务器状态:正在检测中" + +[node name="FPS_image" type="TextureRect" parent="UI/GUI/GameInfoHBox2"] +layout_mode = 2 +texture = ExtResource("10_uhubb") +expand_mode = 2 + +[node name="FPS" type="Label" parent="UI/GUI/GameInfoHBox2"] +layout_mode = 2 +theme_override_colors/font_color = Color(0.68755, 0.948041, 0, 1) +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/shadow_offset_x = 3 +theme_override_constants/shadow_offset_y = 3 +theme_override_constants/outline_size = 10 +theme_override_constants/shadow_outline_size = 10 +theme_override_fonts/font = ExtResource("3_t4s8j") +theme_override_font_sizes/font_size = 25 +text = "FPS:0" + +[node name="GameInfoHBox3" type="HBoxContainer" parent="UI/GUI"] +layout_mode = 0 +offset_top = 70.0 +offset_right = 1398.0 +offset_bottom = 113.0 + +[node name="tip_image" type="TextureRect" parent="UI/GUI/GameInfoHBox3"] +layout_mode = 2 +texture = ExtResource("5_5b81d") +expand_mode = 2 + +[node name="tip" type="RichTextLabel" parent="UI/GUI/GameInfoHBox3"] +custom_minimum_size = Vector2(300, 0) +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +theme_override_constants/outline_size = 10 +theme_override_constants/shadow_offset_y = 3 +theme_override_constants/shadow_offset_x = 3 +theme_override_constants/shadow_outline_size = 10 +theme_override_font_sizes/bold_italics_font_size = 25 +theme_override_font_sizes/italics_font_size = 25 +theme_override_font_sizes/mono_font_size = 25 +theme_override_font_sizes/normal_font_size = 25 +theme_override_font_sizes/bold_font_size = 25 +bbcode_enabled = true +text = "游戏小提示" +vertical_alignment = 1 +threaded = true + +[node name="GlobalServerBroadcast_image" type="TextureRect" parent="UI/GUI/GameInfoHBox3"] +layout_mode = 2 +texture = ExtResource("13_5liyv") +expand_mode = 2 + +[node name="GlobalServerBroadcast" type="RichTextLabel" parent="UI/GUI/GameInfoHBox3"] +custom_minimum_size = Vector2(300, 0) +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +theme_override_constants/outline_size = 10 +theme_override_constants/shadow_offset_y = 3 +theme_override_constants/shadow_offset_x = 3 +theme_override_constants/shadow_outline_size = 10 +theme_override_font_sizes/bold_italics_font_size = 25 +theme_override_font_sizes/italics_font_size = 25 +theme_override_font_sizes/mono_font_size = 25 +theme_override_font_sizes/normal_font_size = 25 +theme_override_font_sizes/bold_font_size = 25 +bbcode_enabled = true +text = "全服大喇叭" +vertical_alignment = 1 +threaded = true + +[node name="WatchBroadcast" type="Button" parent="UI/GUI/GameInfoHBox3"] +layout_mode = 2 +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_colors/font_color = Color(0.513233, 0.513233, 0.513233, 1) +theme_override_constants/outline_size = 10 +theme_override_font_sizes/font_size = 25 +text = "查看" + +[node name="onlineplayer_image" type="TextureRect" parent="UI/GUI/GameInfoHBox3"] +layout_mode = 2 +texture = ExtResource("10_vygm6") +expand_mode = 2 + +[node name="onlineplayer" type="Label" parent="UI/GUI/GameInfoHBox3"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_colors/font_color = Color(0.423529, 1, 0.533333, 1) +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/shadow_offset_x = 3 +theme_override_constants/shadow_offset_y = 3 +theme_override_constants/outline_size = 10 +theme_override_constants/shadow_outline_size = 10 +theme_override_fonts/font = ExtResource("3_t4s8j") +theme_override_font_sizes/font_size = 25 +text = "检测中..." + +[node name="FarmVBox" type="VBoxContainer" parent="UI/GUI"] +layout_mode = 0 +offset_left = 4.0 +offset_top = 263.0 +offset_right = 252.0 +offset_bottom = 795.0 +scale = Vector2(0.8, 0.8) +alignment = 2 + +[node name="SeedStoreButton" type="Button" parent="UI/GUI/FarmVBox"] +visible = false +modulate = Color(1, 0.564706, 0.647059, 1) +layout_mode = 2 +theme_override_font_sizes/font_size = 40 +text = "种子商店" + +[node name="SeedWarehouseButton" type="Button" parent="UI/GUI/FarmVBox"] +modulate = Color(1, 0.772549, 1, 1) +layout_mode = 2 +theme_override_font_sizes/font_size = 40 +text = "种子仓库" + +[node name="CropWarehouseButton" type="Button" parent="UI/GUI/FarmVBox"] +modulate = Color(1, 0.772549, 0.219608, 1) +layout_mode = 2 +theme_override_font_sizes/font_size = 40 +text = "作物仓库" + +[node name="ItemStoreButton" type="Button" parent="UI/GUI/FarmVBox"] +visible = false +modulate = Color(0.231373, 0.772549, 1, 1) +layout_mode = 2 +theme_override_font_sizes/font_size = 40 +text = "道具商店" + +[node name="ItemBagButton" type="Button" parent="UI/GUI/FarmVBox"] +modulate = Color(0.639216, 0.984314, 1, 1) +layout_mode = 2 +theme_override_font_sizes/font_size = 40 +text = "道具背包" + +[node name="OneClickHarvestButton" type="Button" parent="UI/GUI/FarmVBox"] +modulate = Color(0.841258, 0.700703, 0.325362, 1) +layout_mode = 2 +theme_override_font_sizes/font_size = 40 +text = "一键收获" + +[node name="OneClickPlantButton" type="Button" parent="UI/GUI/FarmVBox"] +modulate = Color(0.513945, 0.818793, 3.85046e-07, 1) +layout_mode = 2 +theme_override_font_sizes/font_size = 40 +text = "一键种植" + +[node name="AddNewGroundButton" type="Button" parent="UI/GUI/FarmVBox"] +modulate = Color(1, 0.803922, 0.729412, 1) +layout_mode = 2 +theme_override_font_sizes/font_size = 40 +text = "添加新土地" + +[node name="VisitVBox" type="VBoxContainer" parent="UI/GUI"] +layout_mode = 0 +offset_left = 4.0 +offset_top = 115.0 +offset_right = 252.0 +offset_bottom = 245.0 +scale = Vector2(0.8, 0.8) +alignment = 2 + +[node name="LikeButton" type="Button" parent="UI/GUI/VisitVBox"] +modulate = Color(0.992157, 0.482353, 0.482353, 1) +layout_mode = 2 +theme_override_font_sizes/font_size = 40 +text = "点赞" + +[node name="BattleButton" type="Button" parent="UI/GUI/VisitVBox"] +modulate = Color(0, 0.784782, 0.487639, 1) +layout_mode = 2 +theme_override_font_sizes/font_size = 40 +text = "发起对战" + +[node name="UseItemButton" type="Button" parent="UI/GUI/VisitVBox"] +modulate = Color(0.37232, 0.679193, 1, 1) +layout_mode = 2 +theme_override_font_sizes/font_size = 40 +text = "使用道具" + +[node name="SendMoneyButton" type="Button" parent="UI/GUI/VisitVBox"] +modulate = Color(1, 1, 0, 1) +layout_mode = 2 +theme_override_font_sizes/font_size = 40 +text = "送金币" + +[node name="ReturnMyFarmButton" type="Button" parent="UI/GUI/VisitVBox"] +modulate = Color(1, 1, 0.721569, 1) +layout_mode = 2 +theme_override_font_sizes/font_size = 40 +text = "返回我的农场" + +[node name="OtherVBox" type="VBoxContainer" parent="UI/GUI"] +layout_mode = 0 +offset_left = 1198.0 +offset_top = 77.0 +offset_right = 1446.0 +offset_bottom = 408.0 +scale = Vector2(0.8, 0.8) +alignment = 2 + +[node name="TodayDivinationButton" type="Button" parent="UI/GUI/OtherVBox"] +modulate = Color(0.917346, 0.737213, 1, 1) +layout_mode = 2 +theme_override_font_sizes/font_size = 40 +text = "今日占卜" + +[node name="MyStoreButton" type="Button" parent="UI/GUI/OtherVBox"] +visible = false +modulate = Color(0.917346, 0.737213, 1, 1) +layout_mode = 2 +theme_override_font_sizes/font_size = 40 +text = "我的小卖部" + +[node name="AccountSettingButton" type="Button" parent="UI/GUI/OtherVBox"] +modulate = Color(0.843137, 0.772549, 1, 1) +layout_mode = 2 +theme_override_font_sizes/font_size = 40 +text = "账户设置" + +[node name="OnlineGiftButton" type="Button" parent="UI/GUI/OtherVBox"] +modulate = Color(1, 0.615686, 0.447059, 1) +layout_mode = 2 +theme_override_font_sizes/font_size = 40 +text = "在线礼包" + +[node name="NewPlayerGiftButton" type="Button" parent="UI/GUI/OtherVBox"] +modulate = Color(1, 1, 0.447059, 1) +layout_mode = 2 +theme_override_font_sizes/font_size = 40 +text = "新手礼包" + +[node name="OneClickScreenShot" type="Button" parent="UI/GUI/OtherVBox"] +modulate = Color(0.407843, 0.796078, 0.996078, 1) +layout_mode = 2 +theme_override_font_sizes/font_size = 40 +text = "截图模式" + +[node name="LuckyDrawButton" type="Button" parent="UI/GUI/OtherVBox"] +modulate = Color(0.729412, 0.764706, 1, 1) +layout_mode = 2 +theme_override_font_sizes/font_size = 40 +text = "幸运抽奖" + +[node name="PlayerRankingButton" type="Button" parent="UI/GUI/OtherVBox"] +modulate = Color(0.717647, 1, 0.576471, 1) +layout_mode = 2 +theme_override_font_sizes/font_size = 40 +text = "玩家排行榜" + +[node name="DailyCheckInButton" type="Button" parent="UI/GUI/OtherVBox"] +modulate = Color(0.807843, 1, 1, 1) +layout_mode = 2 +theme_override_font_sizes/font_size = 40 +text = "每日签到" + +[node name="ReturnMainMenuButton" type="Button" parent="UI/GUI/OtherVBox"] +modulate = Color(0.639216, 0.482353, 0.870588, 1) +layout_mode = 2 +theme_override_font_sizes/font_size = 40 +text = "返回主菜单" + +[node name="SmallGameButton" type="Button" parent="UI/GUI/OtherVBox"] +visible = false +modulate = Color(0.513726, 0.615686, 1, 1) +layout_mode = 2 +theme_override_font_sizes/font_size = 40 +text = "游玩小游戏" + +[node name="SettingButton" type="Button" parent="UI/GUI/OtherVBox"] +modulate = Color(0.345098, 0.764706, 1, 1) +layout_mode = 2 +theme_override_font_sizes/font_size = 40 +text = "设置" + +[node name="WisdomTreeButton" type="Button" parent="UI/GUI/OtherVBox"] +visible = false +modulate = Color(0.345098, 0.764706, 0.611765, 1) +layout_mode = 2 +theme_override_font_sizes/font_size = 40 +text = "智慧树" + +[node name="PetBagButton" type="Button" parent="UI/GUI/OtherVBox"] +modulate = Color(0.992157, 0.482353, 0.870588, 1) +layout_mode = 2 +theme_override_font_sizes/font_size = 40 +text = "宠物背包" + +[node name="PetStoreButton" type="Button" parent="UI/GUI/OtherVBox"] +visible = false +modulate = Color(0.992157, 0.482353, 0.870588, 1) +layout_mode = 2 +theme_override_font_sizes/font_size = 40 +text = "宠物商店" + +[node name="ScareCrowButton" type="Button" parent="UI/GUI/OtherVBox"] +visible = false +modulate = Color(0.937381, 0.612088, 0.36654, 1) +layout_mode = 2 +theme_override_font_sizes/font_size = 40 +text = "稻草人" + +[node name="BigPanel" type="CanvasLayer" parent="UI"] + +[node name="TCPNetworkManagerPanel" parent="UI/BigPanel" instance=ExtResource("7_401ut")] +visible = false +offset_top = 97.0 +offset_bottom = 97.0 +scale = Vector2(0.7, 0.7) +theme_override_styles/panel = SubResource("StyleBoxFlat_adtqp") + +[node name="PlayerRankingPanel" parent="UI/BigPanel" instance=ExtResource("16_n03md")] +visible = false +offset_left = 73.0 +offset_top = 28.0 +offset_right = 1473.0 +offset_bottom = 748.0 + +[node name="ItemStorePanel" parent="UI/BigPanel" instance=ExtResource("21_uhubb")] +visible = false +offset_left = 80.0 +offset_top = 77.0 +offset_right = 1620.0 +offset_bottom = 797.0 + +[node name="ItemBagPanel" parent="UI/BigPanel" instance=ExtResource("20_n03md")] +visible = false + +[node name="CropWarehousePanel" parent="UI/BigPanel" instance=ExtResource("18_5b81d")] + +[node name="PlayerBagPanel" parent="UI/BigPanel" instance=ExtResource("19_8kysg")] +visible = false + +[node name="CropStorePanel" parent="UI/BigPanel" instance=ExtResource("17_ql8k3")] +visible = false + +[node name="PlayerStorePanel" type="Panel" parent="UI/BigPanel"] +visible = false +offset_left = 69.0 +offset_top = 56.0 +offset_right = 1635.0 +offset_bottom = 836.0 +scale = Vector2(0.8, 0.8) +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_styles/panel = SubResource("StyleBoxFlat_n03md") +script = ExtResource("24_dygid") + +[node name="TMBackGround" type="ColorRect" parent="UI/BigPanel/PlayerStorePanel"] +layout_mode = 0 +offset_left = -90.0 +offset_top = -71.0 +offset_right = 1672.0 +offset_bottom = 831.0 +color = Color(1, 1, 1, 0) + +[node name="ScrollContainer" type="ScrollContainer" parent="UI/BigPanel/PlayerStorePanel"] +layout_mode = 2 +offset_left = 28.0 +offset_top = 95.0 +offset_right = 3805.0 +offset_bottom = 1723.0 +scale = Vector2(0.4, 0.4) +size_flags_vertical = 3 +horizontal_scroll_mode = 0 + +[node name="Store_Grid" type="GridContainer" parent="UI/BigPanel/PlayerStorePanel/ScrollContainer"] +layout_mode = 2 +size_flags_horizontal = 6 +size_flags_vertical = 3 +columns = 8 + +[node name="Title" type="Label" parent="UI/BigPanel/PlayerStorePanel"] +layout_mode = 2 +offset_right = 1566.0 +offset_bottom = 69.0 +size_flags_horizontal = 3 +theme_override_colors/font_color = Color(1, 1, 0.807843, 1) +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/shadow_offset_x = 4 +theme_override_constants/shadow_offset_y = 4 +theme_override_constants/outline_size = 15 +theme_override_constants/shadow_outline_size = 15 +theme_override_font_sizes/font_size = 50 +text = "玩家小卖部" +horizontal_alignment = 1 + +[node name="QuitButton" type="Button" parent="UI/BigPanel/PlayerStorePanel"] +custom_minimum_size = Vector2(60, 60) +layout_mode = 2 +offset_left = 1478.75 +offset_top = 20.0 +offset_right = 1538.75 +offset_bottom = 83.0 +theme_override_font_sizes/font_size = 40 +text = "X" + +[node name="RefreshButton" type="Button" parent="UI/BigPanel/PlayerStorePanel"] +custom_minimum_size = Vector2(60, 60) +layout_mode = 2 +offset_left = 27.5 +offset_top = 16.25 +offset_right = 115.5 +offset_bottom = 79.25 +theme_override_font_sizes/font_size = 40 +text = "刷新" + +[node name="BuyProductBoothButton" type="Button" parent="UI/BigPanel/PlayerStorePanel"] +custom_minimum_size = Vector2(60, 60) +layout_mode = 2 +offset_left = 207.5 +offset_top = 17.5 +offset_right = 535.5 +offset_bottom = 80.5 +theme_override_font_sizes/font_size = 40 +text = "购买商品摊位" + +[node name="PetStorePanel" parent="UI/BigPanel" instance=ExtResource("24_uc6q1")] +visible = false + +[node name="PetBagPanel" type="Panel" parent="UI/BigPanel"] +visible = false +offset_left = 69.0 +offset_top = 56.0 +offset_right = 1635.0 +offset_bottom = 836.0 +scale = Vector2(0.8, 0.8) +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_styles/panel = SubResource("StyleBoxFlat_8d602") +script = ExtResource("23_uc6q1") + +[node name="TMBackGround" type="ColorRect" parent="UI/BigPanel/PetBagPanel"] +layout_mode = 0 +offset_left = -90.0 +offset_top = -71.0 +offset_right = 1672.0 +offset_bottom = 831.0 +color = Color(1, 1, 1, 0) + +[node name="ScrollContainer" type="ScrollContainer" parent="UI/BigPanel/PetBagPanel"] +layout_mode = 2 +offset_left = 28.0 +offset_top = 95.0 +offset_right = 3805.0 +offset_bottom = 1723.0 +scale = Vector2(0.4, 0.4) +size_flags_vertical = 3 +horizontal_scroll_mode = 0 + +[node name="Bag_Grid" type="GridContainer" parent="UI/BigPanel/PetBagPanel/ScrollContainer"] +layout_mode = 2 +size_flags_horizontal = 6 +size_flags_vertical = 3 +columns = 8 + +[node name="Title" type="Label" parent="UI/BigPanel/PetBagPanel"] +layout_mode = 2 +offset_right = 1566.0 +offset_bottom = 69.0 +size_flags_horizontal = 3 +theme_override_colors/font_color = Color(1, 1, 0.807843, 1) +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/shadow_offset_x = 4 +theme_override_constants/shadow_offset_y = 4 +theme_override_constants/outline_size = 15 +theme_override_constants/shadow_outline_size = 15 +theme_override_font_sizes/font_size = 50 +text = "宠物背包" +horizontal_alignment = 1 + +[node name="QuitButton" type="Button" parent="UI/BigPanel/PetBagPanel"] +custom_minimum_size = Vector2(60, 60) +layout_mode = 2 +offset_left = 1478.75 +offset_top = 20.0 +offset_right = 1538.75 +offset_bottom = 83.0 +theme_override_font_sizes/font_size = 40 +text = "X" + +[node name="RefreshButton" type="Button" parent="UI/BigPanel/PetBagPanel"] +custom_minimum_size = Vector2(60, 60) +layout_mode = 2 +offset_left = 27.5 +offset_top = 16.25 +offset_right = 115.5 +offset_bottom = 79.25 +theme_override_font_sizes/font_size = 40 +text = "刷新" + +[node name="DailyCheckInPanel" parent="UI/BigPanel" instance=ExtResource("18_m6fch")] +visible = false + +[node name="LuckyDrawPanel" parent="UI/BigPanel" instance=ExtResource("17_f21le")] +visible = false + +[node name="LoginPanel" parent="UI/BigPanel" instance=ExtResource("11_6jgly")] +visible = false + +[node name="GameSettingPanel" parent="UI/BigPanel" instance=ExtResource("30_game_setting")] +visible = false + +[node name="PetBattlePanel" parent="UI/BigPanel" instance=ExtResource("29_mw3xw")] +visible = false + +[node name="PlayGamePanel" parent="UI/BigPanel" instance=ExtResource("30_rawle")] +visible = false + +[node name="SpecialFarmPanel" parent="UI/BigPanel" instance=ExtResource("31_ww70k")] + +[node name="SmallPanel" type="CanvasLayer" parent="UI"] + +[node name="LoadProgressPanel" parent="UI/SmallPanel" instance=ExtResource("27_vygm6")] + +[node name="AccountSettingPanel" parent="UI/SmallPanel" instance=ExtResource("26_uc6q1")] +offset_left = 219.0 +offset_top = 45.0 +offset_right = 1130.0 +offset_bottom = 692.0 + +[node name="DebugPanel" type="Panel" parent="UI/SmallPanel"] +visible = false +offset_left = 1000.0 +offset_top = -3.0 +offset_right = 1402.0 +offset_bottom = 716.0 +theme_override_styles/panel = SubResource("StyleBoxFlat_5b81d") +script = ExtResource("28_8kysg") + +[node name="Title" type="Label" parent="UI/SmallPanel/DebugPanel"] +layout_mode = 0 +offset_right = 402.0 +offset_bottom = 60.0 +theme_override_font_sizes/font_size = 40 +text = "调试面板" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="DebugRichText" type="RichTextLabel" parent="UI/SmallPanel/DebugPanel"] +layout_mode = 0 +offset_left = 8.0 +offset_top = 63.0 +offset_right = 391.0 +offset_bottom = 709.0 +theme_override_font_sizes/normal_font_size = 20 +bbcode_enabled = true + +[node name="QuitButton" type="Button" parent="UI/SmallPanel/DebugPanel"] +custom_minimum_size = Vector2(70, 70) +layout_mode = 0 +offset_left = 322.0 +offset_top = 11.0 +offset_right = 392.0 +offset_bottom = 81.0 +theme_override_font_sizes/font_size = 40 +text = "X" + +[node name="PetInformPanel" type="Panel" parent="UI/SmallPanel"] +visible = false +offset_left = 342.0 +offset_right = 1022.0 +offset_bottom = 723.0 +theme_override_styles/panel = SubResource("StyleBoxFlat_2i8fe") +script = ExtResource("31_vygm6") + +[node name="Title" type="Label" parent="UI/SmallPanel/PetInformPanel"] +layout_mode = 0 +offset_right = 680.0 +offset_bottom = 49.0 +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/shadow_offset_x = 4 +theme_override_constants/shadow_offset_y = 4 +theme_override_constants/outline_size = 20 +theme_override_constants/shadow_outline_size = 20 +theme_override_font_sizes/font_size = 35 +text = "宠物信息" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="PetImage" type="TextureRect" parent="UI/SmallPanel/PetInformPanel"] +layout_mode = 0 +offset_left = 289.0 +offset_top = 56.0 +offset_right = 422.0 +offset_bottom = 184.0 +scale = Vector2(0.8, 0.8) +texture = ExtResource("31_uc6q1") + +[node name="InformScroll" type="ScrollContainer" parent="UI/SmallPanel/PetInformPanel"] +layout_mode = 0 +offset_top = 159.0 +offset_right = 683.0 +offset_bottom = 647.0 + +[node name="VBox" type="VBoxContainer" parent="UI/SmallPanel/PetInformPanel/InformScroll"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +alignment = 1 + +[node name="PetNameHBox" type="HBoxContainer" parent="UI/SmallPanel/PetInformPanel/InformScroll/VBox"] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="PetName" type="Label" parent="UI/SmallPanel/PetInformPanel/InformScroll/VBox/PetNameHBox"] +layout_mode = 2 +theme_override_font_sizes/font_size = 25 +text = "宠物名字" +horizontal_alignment = 1 + +[node name="PetNameEdit" type="LineEdit" parent="UI/SmallPanel/PetInformPanel/InformScroll/VBox/PetNameHBox"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_font_sizes/font_size = 25 +placeholder_text = "请输入宠物名字" + +[node name="PetInform" type="RichTextLabel" parent="UI/SmallPanel/PetInformPanel/InformScroll/VBox"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_constants/outline_size = 15 +theme_override_font_sizes/bold_italics_font_size = 25 +theme_override_font_sizes/italics_font_size = 25 +theme_override_font_sizes/mono_font_size = 25 +theme_override_font_sizes/normal_font_size = 25 +theme_override_font_sizes/bold_font_size = 30 +bbcode_enabled = true +threaded = true + +[node name="ButtonHBox" type="HBoxContainer" parent="UI/SmallPanel/PetInformPanel"] +layout_mode = 0 +offset_top = 646.0 +offset_right = 680.0 +offset_bottom = 719.0 + +[node name="EditInformButton" type="Button" parent="UI/SmallPanel/PetInformPanel/ButtonHBox"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_font_sizes/font_size = 25 +text = "修改信息" + +[node name="FeedButton" type="Button" parent="UI/SmallPanel/PetInformPanel/ButtonHBox"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_font_sizes/font_size = 25 +text = "喂养" + +[node name="UseItemButton" type="Button" parent="UI/SmallPanel/PetInformPanel/ButtonHBox"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_font_sizes/font_size = 25 +text = "使用道具" + +[node name="BattleButton" type="Button" parent="UI/SmallPanel/PetInformPanel/ButtonHBox"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_font_sizes/font_size = 25 +text = "出战" + +[node name="PatroButton" type="Button" parent="UI/SmallPanel/PetInformPanel/ButtonHBox"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_font_sizes/font_size = 25 +toggle_mode = true +text = "巡逻" + +[node name="QuitButton" type="Button" parent="UI/SmallPanel/PetInformPanel"] +custom_minimum_size = Vector2(50, 50) +layout_mode = 0 +offset_left = 626.0 +offset_right = 676.0 +offset_bottom = 50.0 +theme_override_font_sizes/font_size = 30 +text = "X" + +[node name="RefreshButton" type="Button" parent="UI/SmallPanel/PetInformPanel"] +custom_minimum_size = Vector2(50, 50) +layout_mode = 0 +offset_right = 8.0 +offset_bottom = 8.0 +theme_override_font_sizes/font_size = 30 +text = "刷新" + +[node name="GlobalServerBroadcastPanel" type="Panel" parent="UI/SmallPanel"] +visible = false +offset_left = 322.0 +offset_right = 985.0 +offset_bottom = 721.0 +theme_override_styles/panel = SubResource("StyleBoxFlat_k1sw4") +script = ExtResource("34_k1sw4") + +[node name="Title" type="Label" parent="UI/SmallPanel/GlobalServerBroadcastPanel"] +layout_mode = 0 +offset_left = 13.0 +offset_top = 12.0 +offset_right = 653.0 +offset_bottom = 63.0 +theme_override_colors/font_color = Color(0.821789, 0.821789, 0.821789, 1) +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/shadow_offset_x = 4 +theme_override_constants/shadow_offset_y = 4 +theme_override_constants/outline_size = 15 +theme_override_constants/shadow_outline_size = 15 +theme_override_font_sizes/font_size = 35 +text = "全服大喇叭" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="MessageContents" type="RichTextLabel" parent="UI/SmallPanel/GlobalServerBroadcastPanel"] +layout_mode = 0 +offset_left = 10.0 +offset_top = 72.0 +offset_right = 653.0 +offset_bottom = 642.0 +theme_override_font_sizes/bold_italics_font_size = 15 +theme_override_font_sizes/italics_font_size = 15 +theme_override_font_sizes/mono_font_size = 15 +theme_override_font_sizes/normal_font_size = 15 +theme_override_font_sizes/bold_font_size = 15 +bbcode_enabled = true + +[node name="HBox" type="HBoxContainer" parent="UI/SmallPanel/GlobalServerBroadcastPanel"] +layout_mode = 0 +offset_left = 8.0 +offset_top = 649.0 +offset_right = 653.0 +offset_bottom = 709.0 + +[node name="InputMessage" type="LineEdit" parent="UI/SmallPanel/GlobalServerBroadcastPanel/HBox"] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="SendMessageButton" type="Button" parent="UI/SmallPanel/GlobalServerBroadcastPanel/HBox"] +layout_mode = 2 +theme_override_font_sizes/font_size = 30 +text = " 发送 " + +[node name="QuitButton" type="Button" parent="UI/SmallPanel/GlobalServerBroadcastPanel"] +custom_minimum_size = Vector2(50, 50) +layout_mode = 0 +offset_left = 601.0 +offset_top = 12.0 +offset_right = 651.0 +offset_bottom = 62.0 +theme_override_font_sizes/font_size = 30 +text = "X" + +[node name="WatchMoreButton" type="Button" parent="UI/SmallPanel/GlobalServerBroadcastPanel"] +custom_minimum_size = Vector2(50, 50) +layout_mode = 0 +offset_left = 13.0 +offset_top = 12.0 +offset_right = 141.0 +offset_bottom = 62.0 +theme_override_font_sizes/font_size = 30 +text = "查看更多" + +[node name="ScareCrowPanel" type="Panel" parent="UI/SmallPanel"] +visible = false +offset_left = 122.0 +offset_top = 68.0 +offset_right = 1524.0 +offset_bottom = 791.0 +scale = Vector2(0.8, 0.8) +theme_override_styles/panel = SubResource("StyleBoxFlat_jyxgm") +script = ExtResource("35_6ylhg") + +[node name="Background" type="ColorRect" parent="UI/SmallPanel/ScareCrowPanel"] +layout_mode = 0 +offset_left = -150.0 +offset_top = -80.0 +offset_right = 1597.0 +offset_bottom = 810.0 +color = Color(1, 1, 1, 0) + +[node name="Title" type="Label" parent="UI/SmallPanel/ScareCrowPanel"] +layout_mode = 0 +offset_top = -3.0 +offset_right = 1403.0 +offset_bottom = 63.0 +theme_override_colors/font_color = Color(0.89059, 0.89059, 0.89059, 1) +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/shadow_offset_x = 4 +theme_override_constants/shadow_offset_y = 4 +theme_override_constants/outline_size = 15 +theme_override_constants/shadow_outline_size = 15 +theme_override_font_sizes/font_size = 40 +text = "农场稻草人" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="BuyScareCrowHbox" type="HBoxContainer" parent="UI/SmallPanel/ScareCrowPanel"] +layout_mode = 0 +offset_top = 69.0 +offset_right = 2337.0 +offset_bottom = 333.0 +scale = Vector2(0.6, 0.6) +alignment = 1 + +[node name="ScareCrow1" type="Button" parent="UI/SmallPanel/ScareCrowPanel/BuyScareCrowHbox"] +custom_minimum_size = Vector2(100, 100) +layout_mode = 2 +theme_override_font_sizes/font_size = 40 +text = "花费:999元" +icon = ExtResource("43_6rkns") +icon_alignment = 1 + +[node name="ScareCrow2" type="Button" parent="UI/SmallPanel/ScareCrowPanel/BuyScareCrowHbox"] +custom_minimum_size = Vector2(100, 100) +layout_mode = 2 +theme_override_font_sizes/font_size = 40 +text = "花费888元" +icon = ExtResource("36_jyxgm") +icon_alignment = 1 + +[node name="ScareCrow3" type="Button" parent="UI/SmallPanel/ScareCrowPanel/BuyScareCrowHbox"] +custom_minimum_size = Vector2(100, 100) +layout_mode = 2 +theme_override_font_sizes/font_size = 40 +text = "花费1000元" +icon = ExtResource("37_6ylhg") +icon_alignment = 1 + +[node name="HBox" type="HBoxContainer" parent="UI/SmallPanel/ScareCrowPanel"] +layout_mode = 0 +offset_top = 227.0 +offset_right = 1403.0 +offset_bottom = 284.0 + +[node name="ScareCrowName" type="Label" parent="UI/SmallPanel/ScareCrowPanel/HBox"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_font_sizes/font_size = 35 +text = "稻草人名字" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="ScareCrowInput" type="LineEdit" parent="UI/SmallPanel/ScareCrowPanel/HBox"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_font_sizes/font_size = 35 +text = "请输入稻草人名字" + +[node name="ScareCrowNameColor" type="Label" parent="UI/SmallPanel/ScareCrowPanel/HBox"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_font_sizes/font_size = 35 +text = "稻草人名字颜色" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="ScareCrowNameColorInput" type="ColorPickerButton" parent="UI/SmallPanel/ScareCrowPanel/HBox"] +layout_mode = 2 +size_flags_horizontal = 3 +color = Color(0.733005, 0.733005, 0.733005, 1) + +[node name="ScareCrowTalksGrid" type="GridContainer" parent="UI/SmallPanel/ScareCrowPanel"] +layout_mode = 0 +offset_top = 283.0 +offset_right = 1402.0 +offset_bottom = 387.0 +columns = 2 + +[node name="Talk1" type="LineEdit" parent="UI/SmallPanel/ScareCrowPanel/ScareCrowTalksGrid"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_font_sizes/font_size = 30 +placeholder_text = "请输入稻草人说的第一句话" + +[node name="Talk2" type="LineEdit" parent="UI/SmallPanel/ScareCrowPanel/ScareCrowTalksGrid"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_font_sizes/font_size = 30 +placeholder_text = "请输入稻草人说的第二句话" + +[node name="Talk3" type="LineEdit" parent="UI/SmallPanel/ScareCrowPanel/ScareCrowTalksGrid"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_font_sizes/font_size = 30 +placeholder_text = "请输入稻草人说的第三句话" + +[node name="Talk4" type="LineEdit" parent="UI/SmallPanel/ScareCrowPanel/ScareCrowTalksGrid"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_font_sizes/font_size = 30 +placeholder_text = "请输入稻草人说的第四句话" + +[node name="ScareCrowTalksColorGrid" type="GridContainer" parent="UI/SmallPanel/ScareCrowPanel"] +layout_mode = 0 +offset_left = -2.0 +offset_top = 399.0 +offset_right = 1400.0 +offset_bottom = 503.0 +columns = 4 + +[node name="TalkColor1" type="Label" parent="UI/SmallPanel/ScareCrowPanel/ScareCrowTalksColorGrid"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_font_sizes/font_size = 35 +text = "第一句话颜色 " +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="ColorPickerButton1" type="ColorPickerButton" parent="UI/SmallPanel/ScareCrowPanel/ScareCrowTalksColorGrid"] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="TalkColor2" type="Label" parent="UI/SmallPanel/ScareCrowPanel/ScareCrowTalksColorGrid"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_font_sizes/font_size = 35 +text = "第二句话颜色 " +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="ColorPickerButton2" type="ColorPickerButton" parent="UI/SmallPanel/ScareCrowPanel/ScareCrowTalksColorGrid"] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="TalkColor3" type="Label" parent="UI/SmallPanel/ScareCrowPanel/ScareCrowTalksColorGrid"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_font_sizes/font_size = 35 +text = "第三句话颜色 " +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="ColorPickerButton3" type="ColorPickerButton" parent="UI/SmallPanel/ScareCrowPanel/ScareCrowTalksColorGrid"] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="TalkColor4" type="Label" parent="UI/SmallPanel/ScareCrowPanel/ScareCrowTalksColorGrid"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_font_sizes/font_size = 35 +text = "第四句话颜色 " +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="ColorPickerButton4" type="ColorPickerButton" parent="UI/SmallPanel/ScareCrowPanel/ScareCrowTalksColorGrid"] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="QuitButton" type="Button" parent="UI/SmallPanel/ScareCrowPanel"] +custom_minimum_size = Vector2(60, 60) +layout_mode = 0 +offset_left = 1334.0 +offset_top = 6.0 +offset_right = 1394.0 +offset_bottom = 66.0 +theme_override_font_sizes/font_size = 30 +text = "X" + +[node name="Label" type="Label" parent="UI/SmallPanel/ScareCrowPanel"] +layout_mode = 0 +offset_top = 516.0 +offset_right = 1096.0 +offset_bottom = 571.0 +theme_override_font_sizes/font_size = 40 +text = "这个可爱的稻草人可以在别人访问你农场时向他/她展示几句话" + +[node name="HBox2" type="HBoxContainer" parent="UI/SmallPanel/ScareCrowPanel"] +layout_mode = 0 +offset_left = -1.0 +offset_top = 656.0 +offset_right = 1402.0 +offset_bottom = 719.0 +alignment = 1 + +[node name="SureButton" type="Button" parent="UI/SmallPanel/ScareCrowPanel/HBox2"] +layout_mode = 2 +theme_override_font_sizes/font_size = 40 +text = "确认修改" + +[node name="WisdomTreePanel" type="Panel" parent="UI/SmallPanel"] +visible = false +offset_left = 129.0 +offset_top = 63.0 +offset_right = 1529.0 +offset_bottom = 783.0 +scale = Vector2(0.8, 0.8) +theme_override_styles/panel = SubResource("StyleBoxFlat_np7ck") +script = ExtResource("39_np7ck") + +[node name="Background" type="ColorRect" parent="UI/SmallPanel/WisdomTreePanel"] +layout_mode = 0 +offset_left = -156.0 +offset_top = -76.0 +offset_right = 1589.0 +offset_bottom = 821.0 +color = Color(1, 1, 1, 0) + +[node name="Title" type="Label" parent="UI/SmallPanel/WisdomTreePanel"] +layout_mode = 0 +offset_left = 17.0 +offset_top = 19.0 +offset_right = 1382.0 +offset_bottom = 74.0 +theme_override_colors/font_color = Color(0.954954, 0.709181, 7.70092e-07, 1) +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/shadow_offset_x = 4 +theme_override_constants/shadow_offset_y = 4 +theme_override_constants/outline_size = 20 +theme_override_constants/shadow_outline_size = 20 +theme_override_font_sizes/font_size = 50 +text = "智慧树" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Description" type="Label" parent="UI/SmallPanel/WisdomTreePanel"] +layout_mode = 0 +offset_left = 58.0 +offset_top = 62.0 +offset_right = 1322.0 +offset_bottom = 280.0 +theme_override_colors/font_color = Color(0.654902, 1, 1, 1) +theme_override_font_sizes/font_size = 30 +text = "只需花费一点点钱币,就可以匿名发送一句话,由于智慧树强大的链接性, +其他玩家在浇水施肥时就有概率收到这句话。向他人发送一句祝福吧(,,・ω・,,) , +只要你不断的给智慧树浇水施肥,它就会长得越来越高" +horizontal_alignment = 1 +vertical_alignment = 1 +autowrap_mode = 2 + +[node name="HBox" type="HBoxContainer" parent="UI/SmallPanel/WisdomTreePanel"] +layout_mode = 0 +offset_left = 17.0 +offset_top = 431.0 +offset_right = 1382.0 +offset_bottom = 531.0 +alignment = 1 + +[node name="WaterButton" type="Button" parent="UI/SmallPanel/WisdomTreePanel/HBox"] +custom_minimum_size = Vector2(100, 100) +layout_mode = 2 +theme_override_font_sizes/font_size = 30 +text = "浇水" + +[node name="FertilizeButton" type="Button" parent="UI/SmallPanel/WisdomTreePanel/HBox"] +custom_minimum_size = Vector2(100, 100) +layout_mode = 2 +theme_override_font_sizes/font_size = 30 +text = "施肥" + +[node name="PlayMusicButton" type="Button" parent="UI/SmallPanel/WisdomTreePanel/HBox"] +custom_minimum_size = Vector2(100, 100) +layout_mode = 2 +theme_override_font_sizes/font_size = 30 +text = "放音乐" + +[node name="KillBugButton" type="Button" parent="UI/SmallPanel/WisdomTreePanel/HBox"] +custom_minimum_size = Vector2(100, 100) +layout_mode = 2 +theme_override_font_sizes/font_size = 30 +text = "杀虫" + +[node name="KillGrassButton" type="Button" parent="UI/SmallPanel/WisdomTreePanel/HBox"] +custom_minimum_size = Vector2(100, 100) +layout_mode = 2 +theme_override_font_sizes/font_size = 30 +text = "除草" + +[node name="ReviveButton" type="Button" parent="UI/SmallPanel/WisdomTreePanel/HBox"] +custom_minimum_size = Vector2(100, 100) +layout_mode = 2 +theme_override_font_sizes/font_size = 30 +text = "复活" + +[node name="VBox" type="VBoxContainer" parent="UI/SmallPanel/WisdomTreePanel"] +layout_mode = 0 +offset_left = 17.0 +offset_top = 531.0 +offset_right = 1383.0 +offset_bottom = 706.0 + +[node name="Explanation" type="Label" parent="UI/SmallPanel/WisdomTreePanel/VBox"] +layout_mode = 2 +theme_override_font_sizes/font_size = 35 +text = "请在下方输入您想发送给别人的一句话" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="HBox" type="HBoxContainer" parent="UI/SmallPanel/WisdomTreePanel/VBox"] +layout_mode = 2 + +[node name="TalkInput" type="LineEdit" parent="UI/SmallPanel/WisdomTreePanel/VBox/HBox"] +custom_minimum_size = Vector2(800, 0) +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_font_sizes/font_size = 35 +placeholder_text = "在这里输入(*´∀ ˋ*)" +alignment = 1 + +[node name="SendButton" type="Button" parent="UI/SmallPanel/WisdomTreePanel/VBox/HBox"] +custom_minimum_size = Vector2(160, 0) +layout_mode = 2 +size_flags_horizontal = 4 +theme_override_font_sizes/font_size = 35 +text = "发送" + +[node name="Grid" type="GridContainer" parent="UI/SmallPanel/WisdomTreePanel"] +layout_mode = 0 +offset_left = 17.0 +offset_top = 303.0 +offset_right = 1383.0 +offset_bottom = 431.0 +columns = 2 + +[node name="Level" type="Label" parent="UI/SmallPanel/WisdomTreePanel/Grid"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_font_sizes/font_size = 30 +text = "等级:999" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Health" type="Label" parent="UI/SmallPanel/WisdomTreePanel/Grid"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_font_sizes/font_size = 30 +text = "生命值:999" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Experience" type="Label" parent="UI/SmallPanel/WisdomTreePanel/Grid"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_font_sizes/font_size = 30 +text = "经验值:999" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Height" type="Label" parent="UI/SmallPanel/WisdomTreePanel/Grid"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_font_sizes/font_size = 30 +text = "高度:999" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Label" type="Label" parent="UI/SmallPanel/WisdomTreePanel"] +layout_mode = 0 +offset_left = 17.0 +offset_top = 235.0 +offset_right = 1383.0 +offset_bottom = 290.0 +theme_override_font_sizes/font_size = 40 +text = "智慧树状态" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="QuitButton" type="Button" parent="UI/SmallPanel/WisdomTreePanel"] +custom_minimum_size = Vector2(60, 60) +layout_mode = 0 +offset_left = 1317.0 +offset_top = 22.0 +offset_right = 1377.0 +offset_bottom = 85.0 +theme_override_font_sizes/font_size = 40 +text = "X" + +[node name="LandPanel" parent="UI/SmallPanel" instance=ExtResource("12_y1hsh")] +visible = false + +[node name="OnlineGiftPanel" parent="UI/SmallPanel" instance=ExtResource("14_5b81d")] +visible = false + +[node name="OneClickPlantPanel" parent="UI/SmallPanel" instance=ExtResource("15_8kysg")] +visible = false +offset_left = 371.0 +offset_top = 166.0 +offset_right = 979.0 +offset_bottom = 482.0 + +[node name="CropInformPanel" type="Panel" parent="UI/SmallPanel"] +visible = false +offset_left = 379.0 +offset_right = 1007.0 +offset_bottom = 723.0 +theme_override_styles/panel = SubResource("StyleBoxFlat_xyeuq") +script = ExtResource("41_iluto") + +[node name="Title" type="Label" parent="UI/SmallPanel/CropInformPanel"] +layout_mode = 0 +offset_top = 17.0 +offset_right = 628.0 +offset_bottom = 74.0 +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/shadow_offset_x = 4 +theme_override_constants/shadow_offset_y = 4 +theme_override_constants/outline_size = 20 +theme_override_constants/shadow_outline_size = 20 +theme_override_font_sizes/font_size = 35 +text = "作物信息" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="QuitButton" type="Button" parent="UI/SmallPanel/CropInformPanel"] +custom_minimum_size = Vector2(50, 50) +layout_mode = 0 +offset_left = 561.0 +offset_top = 17.0 +offset_right = 611.0 +offset_bottom = 67.0 +theme_override_font_sizes/font_size = 30 +text = "X" + +[node name="VBox" type="VBoxContainer" parent="UI/SmallPanel/CropInformPanel"] +layout_mode = 0 +offset_left = 17.0 +offset_top = 74.0 +offset_right = 615.0 +offset_bottom = 708.0 + +[node name="CropImage" type="TextureRect" parent="UI/SmallPanel/CropInformPanel/VBox"] +layout_mode = 2 +texture = ExtResource("31_uc6q1") +stretch_mode = 3 + +[node name="CropName" type="Label" parent="UI/SmallPanel/CropInformPanel/VBox"] +layout_mode = 2 +theme_override_font_sizes/font_size = 30 +text = "名称:" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="CropDescription" type="Label" parent="UI/SmallPanel/CropInformPanel/VBox"] +layout_mode = 2 +theme_override_font_sizes/font_size = 25 +text = "描述:" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="CropPrice" type="Label" parent="UI/SmallPanel/CropInformPanel/VBox"] +layout_mode = 2 +theme_override_font_sizes/font_size = 25 +text = "收购价:" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="CropQuality" type="Label" parent="UI/SmallPanel/CropInformPanel/VBox"] +layout_mode = 2 +theme_override_font_sizes/font_size = 25 +text = "品质:" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="HBox" type="HBoxContainer" parent="UI/SmallPanel/CropInformPanel/VBox"] +layout_mode = 2 +size_flags_vertical = 10 +alignment = 1 + +[node name="SaleProduct" type="Button" parent="UI/SmallPanel/CropInformPanel/VBox/HBox"] +custom_minimum_size = Vector2(120, 70) +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 10 +theme_override_font_sizes/font_size = 30 +text = "直接出售" + +[node name="AddToStore" type="Button" parent="UI/SmallPanel/CropInformPanel/VBox/HBox"] +custom_minimum_size = Vector2(120, 70) +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 10 +theme_override_font_sizes/font_size = 30 +text = "放入小卖部" + +[node name="TodayDivinationPanel" type="PanelContainer" parent="UI/SmallPanel"] +visible = false +offset_left = 345.0 +offset_right = 1050.0 +offset_bottom = 713.0 +theme_override_styles/panel = SubResource("StyleBoxFlat_bpbm8") +script = ExtResource("44_mw3xw") + +[node name="VBox" type="VBoxContainer" parent="UI/SmallPanel/TodayDivinationPanel"] +layout_mode = 2 + +[node name="Title" type="Label" parent="UI/SmallPanel/TodayDivinationPanel/VBox"] +layout_mode = 2 +size_flags_vertical = 0 +theme_override_colors/font_color = Color(0.807843, 1, 0, 1) +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/shadow_offset_x = 4 +theme_override_constants/shadow_offset_y = 4 +theme_override_constants/outline_size = 20 +theme_override_constants/shadow_outline_size = 20 +theme_override_font_sizes/font_size = 40 +text = "今日占卜" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Scroll" type="ScrollContainer" parent="UI/SmallPanel/TodayDivinationPanel/VBox"] +layout_mode = 2 +size_flags_vertical = 3 + +[node name="Contents" type="RichTextLabel" parent="UI/SmallPanel/TodayDivinationPanel/VBox/Scroll"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_fonts/normal_font = ExtResource("45_vexnj") +theme_override_fonts/mono_font = ExtResource("45_vexnj") +theme_override_fonts/italics_font = ExtResource("45_vexnj") +theme_override_fonts/bold_italics_font = ExtResource("45_vexnj") +theme_override_fonts/bold_font = ExtResource("45_vexnj") +theme_override_font_sizes/bold_italics_font_size = 25 +theme_override_font_sizes/italics_font_size = 25 +theme_override_font_sizes/mono_font_size = 25 +theme_override_font_sizes/normal_font_size = 25 +theme_override_font_sizes/bold_font_size = 25 +bbcode_enabled = true +text = "这是占卜内容" +horizontal_alignment = 1 +threaded = true + +[node name="StartButton" type="Button" parent="UI/SmallPanel/TodayDivinationPanel/VBox"] +custom_minimum_size = Vector2(300, 0) +layout_mode = 2 +size_flags_horizontal = 4 +theme_override_font_sizes/font_size = 35 +text = "开始占卜" + +[node name="QuitButton" type="Button" parent="UI/SmallPanel/TodayDivinationPanel/VBox"] +custom_minimum_size = Vector2(300, 0) +layout_mode = 2 +size_flags_horizontal = 4 +theme_override_font_sizes/font_size = 35 +text = "结束占卜" + +[node name="DiaLog" type="CanvasLayer" parent="UI"] + +[node name="AcceptDialog" parent="UI/DiaLog" instance=ExtResource("16_0igvr")] +visible = false + +[node name="BatchBuyPopup" type="PanelContainer" parent="UI/DiaLog"] +visible = false +offset_left = 426.0 +offset_top = 90.0 +offset_right = 928.0 +offset_bottom = 556.0 +theme_override_styles/panel = SubResource("StyleBoxFlat_t4s8j") +script = ExtResource("29_5b81d") + +[node name="VBox" type="VBoxContainer" parent="UI/DiaLog/BatchBuyPopup"] +layout_mode = 2 + +[node name="Title" type="Label" parent="UI/DiaLog/BatchBuyPopup/VBox"] +layout_mode = 2 +theme_override_font_sizes/font_size = 40 +text = "是否购买" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Contents" type="Label" parent="UI/DiaLog/BatchBuyPopup/VBox"] +layout_mode = 2 +size_flags_vertical = 3 +theme_override_font_sizes/font_size = 20 +text = "这是说明内容" +horizontal_alignment = 1 + +[node name="BuyNumEdit" type="LineEdit" parent="UI/DiaLog/BatchBuyPopup/VBox"] +layout_mode = 2 +theme_override_font_sizes/font_size = 30 + +[node name="HBox" type="HBoxContainer" parent="UI/DiaLog/BatchBuyPopup/VBox"] +layout_mode = 2 + +[node name="SureButton" type="Button" parent="UI/DiaLog/BatchBuyPopup/VBox/HBox"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_font_sizes/font_size = 30 +text = "确定" + +[node name="CancelButton" type="Button" parent="UI/DiaLog/BatchBuyPopup/VBox/HBox"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_font_sizes/font_size = 30 +text = "取消" + +[node name="BatchSellPopup" type="PanelContainer" parent="UI/DiaLog"] +visible = false +offset_left = 391.0 +offset_top = 95.0 +offset_right = 893.0 +offset_bottom = 561.0 +theme_override_styles/panel = SubResource("StyleBoxFlat_t4s8j") +script = ExtResource("44_av1bx") + +[node name="VBox" type="VBoxContainer" parent="UI/DiaLog/BatchSellPopup"] +layout_mode = 2 + +[node name="Title" type="Label" parent="UI/DiaLog/BatchSellPopup/VBox"] +layout_mode = 2 +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/shadow_offset_x = 3 +theme_override_constants/shadow_offset_y = 3 +theme_override_constants/outline_size = 20 +theme_override_constants/shadow_outline_size = 20 +theme_override_font_sizes/font_size = 40 +text = "请选择出售数量" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Contents" type="Label" parent="UI/DiaLog/BatchSellPopup/VBox"] +layout_mode = 2 +size_flags_vertical = 3 +theme_override_font_sizes/font_size = 20 +text = "这是说明内容" +horizontal_alignment = 1 + +[node name="SellNumEdit" type="LineEdit" parent="UI/DiaLog/BatchSellPopup/VBox"] +layout_mode = 2 +theme_override_font_sizes/font_size = 30 + +[node name="HBox" type="HBoxContainer" parent="UI/DiaLog/BatchSellPopup/VBox"] +layout_mode = 2 + +[node name="SureButton" type="Button" parent="UI/DiaLog/BatchSellPopup/VBox/HBox"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_font_sizes/font_size = 30 +text = "确定" + +[node name="CancelButton" type="Button" parent="UI/DiaLog/BatchSellPopup/VBox/HBox"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_font_sizes/font_size = 30 +text = "取消" + +[node name="AddProductToStorePopup" type="PanelContainer" parent="UI/DiaLog"] +visible = false +offset_left = 426.0 +offset_top = 59.0 +offset_right = 1026.0 +offset_bottom = 559.0 +theme_override_styles/panel = SubResource("StyleBoxFlat_t4s8j") +script = ExtResource("46_8d602") + +[node name="VBox" type="VBoxContainer" parent="UI/DiaLog/AddProductToStorePopup"] +layout_mode = 2 + +[node name="Title" type="Label" parent="UI/DiaLog/AddProductToStorePopup/VBox"] +layout_mode = 2 +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/shadow_offset_x = 3 +theme_override_constants/shadow_offset_y = 3 +theme_override_constants/outline_size = 20 +theme_override_constants/shadow_outline_size = 20 +theme_override_font_sizes/font_size = 40 +text = "弹窗标题" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Contents" type="Label" parent="UI/DiaLog/AddProductToStorePopup/VBox"] +layout_mode = 2 +theme_override_colors/font_color = Color(0.762404, 0.762404, 0.762404, 1) +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/shadow_offset_x = 3 +theme_override_constants/shadow_offset_y = 3 +theme_override_constants/outline_size = 20 +theme_override_constants/shadow_outline_size = 20 +theme_override_font_sizes/font_size = 24 +text = "弹窗内容" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="SellNum" type="Label" parent="UI/DiaLog/AddProductToStorePopup/VBox"] +layout_mode = 2 +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/shadow_offset_x = 3 +theme_override_constants/shadow_offset_y = 3 +theme_override_constants/outline_size = 20 +theme_override_constants/shadow_outline_size = 20 +theme_override_font_sizes/font_size = 24 +text = "请选择售卖的数量" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="SellNumInput" type="LineEdit" parent="UI/DiaLog/AddProductToStorePopup/VBox"] +layout_mode = 2 +theme_override_font_sizes/font_size = 30 + +[node name="SellPrice" type="Label" parent="UI/DiaLog/AddProductToStorePopup/VBox"] +layout_mode = 2 +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/shadow_offset_x = 3 +theme_override_constants/shadow_offset_y = 3 +theme_override_constants/outline_size = 20 +theme_override_constants/shadow_outline_size = 20 +theme_override_font_sizes/font_size = 24 +text = "请选择售卖的价格" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="SellPriceInput" type="LineEdit" parent="UI/DiaLog/AddProductToStorePopup/VBox"] +layout_mode = 2 +theme_override_font_sizes/font_size = 30 + +[node name="HBox" type="HBoxContainer" parent="UI/DiaLog/AddProductToStorePopup/VBox"] +layout_mode = 2 + +[node name="SureButton" type="Button" parent="UI/DiaLog/AddProductToStorePopup/VBox/HBox"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_font_sizes/font_size = 30 +text = "确定" + +[node name="CancelButton" type="Button" parent="UI/DiaLog/AddProductToStorePopup/VBox/HBox"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_font_sizes/font_size = 30 +text = "取消" + +[node name="EscapeDialog" type="ConfirmationDialog" parent="UI/DiaLog"] +title = "弹窗标题" +initial_position = 3 +size = Vector2i(600, 500) +current_screen = 0 +content_scale_factor = 1.4 +ok_button_text = "确定" +cancel_button_text = "取消" + +[node name="BackgroundUI" type="CanvasLayer" parent="."] +layer = -1 + +[node name="BackgroundSwitcher" type="Sprite2D" parent="BackgroundUI"] +show_behind_parent = true +z_index = -100 +z_as_relative = false +position = Vector2(703, 360) +scale = Vector2(0.92, 0.92) +script = ExtResource("17_0igvr") + +[node name="Background2" type="Sprite2D" parent="BackgroundUI/BackgroundSwitcher"] + +[node name="Timer" type="Timer" parent="BackgroundUI/BackgroundSwitcher"] + +[node name="GridContainer" type="GridContainer" parent="."] +z_as_relative = false +custom_minimum_size = Vector2(100, 100) +offset_left = -2.0 +offset_top = 2.0 +offset_right = 1398.0 +offset_bottom = 722.0 +columns = 10 + +[node name="Button" type="Button" parent="GridContainer"] +custom_minimum_size = Vector2(100, 100) +layout_mode = 2 + +[node name="Button2" type="Button" parent="GridContainer"] +custom_minimum_size = Vector2(100, 100) +layout_mode = 2 + +[node name="Button3" type="Button" parent="GridContainer"] +custom_minimum_size = Vector2(100, 100) +layout_mode = 2 + +[node name="Button4" type="Button" parent="GridContainer"] +custom_minimum_size = Vector2(100, 100) +layout_mode = 2 + +[node name="Button5" type="Button" parent="GridContainer"] +custom_minimum_size = Vector2(100, 100) +layout_mode = 2 + +[node name="Button6" type="Button" parent="GridContainer"] +custom_minimum_size = Vector2(100, 100) +layout_mode = 2 + +[node name="Button7" type="Button" parent="GridContainer"] +custom_minimum_size = Vector2(100, 100) +layout_mode = 2 + +[node name="Button8" type="Button" parent="GridContainer"] +custom_minimum_size = Vector2(100, 100) +layout_mode = 2 + +[node name="Button9" type="Button" parent="GridContainer"] +custom_minimum_size = Vector2(100, 100) +layout_mode = 2 + +[node name="Button10" type="Button" parent="GridContainer"] +custom_minimum_size = Vector2(100, 100) +layout_mode = 2 + +[node name="Button11" type="Button" parent="GridContainer"] +custom_minimum_size = Vector2(100, 100) +layout_mode = 2 + +[node name="Button12" type="Button" parent="GridContainer"] +custom_minimum_size = Vector2(100, 100) +layout_mode = 2 + +[node name="Button13" type="Button" parent="GridContainer"] +custom_minimum_size = Vector2(100, 100) +layout_mode = 2 + +[node name="Button14" type="Button" parent="GridContainer"] +custom_minimum_size = Vector2(100, 100) +layout_mode = 2 + +[node name="Button15" type="Button" parent="GridContainer"] +custom_minimum_size = Vector2(100, 100) +layout_mode = 2 + +[node name="Button16" type="Button" parent="GridContainer"] +custom_minimum_size = Vector2(100, 100) +layout_mode = 2 + +[node name="Button17" type="Button" parent="GridContainer"] +custom_minimum_size = Vector2(100, 100) +layout_mode = 2 + +[node name="Button18" type="Button" parent="GridContainer"] +custom_minimum_size = Vector2(100, 100) +layout_mode = 2 + +[node name="Button19" type="Button" parent="GridContainer"] +custom_minimum_size = Vector2(100, 100) +layout_mode = 2 + +[node name="Button20" type="Button" parent="GridContainer"] +custom_minimum_size = Vector2(100, 100) +layout_mode = 2 + +[node name="CopyNodes" type="Node2D" parent="."] +position = Vector2(-1000, 0) + +[node name="CropItem" parent="CopyNodes" instance=ExtResource("3_isiom")] +z_index = 2 +z_as_relative = false +offset_left = -1433.0 +offset_top = -161.0 +offset_right = -1333.0 +offset_bottom = -61.0 + +[node name="item_button" parent="CopyNodes" instance=ExtResource("39_cdkxt")] + +[node name="GameCamera" type="Camera2D" parent="."] +anchor_mode = 0 +position_smoothing_enabled = true +script = ExtResource("10_o8l48") +max_zoom = 1.8 +bounds_enabled = true +bounds_min = Vector2(-500, -600) +bounds_max = Vector2(500, 500) + +[node name="GameManager" type="Node" parent="."] + +[node name="GameBGMPlayer" type="Node" parent="."] +script = ExtResource("28_m6fch") +play_mode = 1 +music_files_list = Array[String](["res://assets/音乐/Anibli&RelaxingPianoMusic-StrollThroughtheSky.ogg", "res://assets/音乐/BanAM-Futatabi.ogg", "res://assets/音乐/MCMZebra-AlwaysandManyTimes.ogg", "res://assets/音乐/MicMusicbox-Ashitakasekki.ogg", "res://assets/音乐/Nemuネム-PromiseoftheWorld.ogg", "res://assets/音乐/α-WaveRelaxationHealingMusicLab-いつも何度でも[「千と千尋の神隠し」より][ピアノ].ogg", "res://assets/音乐/久石让-ふたたび.ogg", "res://assets/音乐/广桥真纪子-いのちの名前(生命之名).ogg", "res://assets/音乐/日本群星-PromiseoftheWorld.ogg"]) + +[node name="Decoration" type="Node2D" parent="."] + +[node name="ScareCrow" type="Button" parent="Decoration"] +self_modulate = Color(1, 1, 1, 0) +custom_minimum_size = Vector2(100, 100) +offset_top = -108.0 +offset_right = 264.0 +offset_bottom = 156.0 +scale = Vector2(0.4, 0.4) + +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration/ScareCrow"] +position = Vector2(132, 134) +scale = Vector2(1.4, 1.4) +texture = ExtResource("48_2i8fe") + +[node name="ScareCrowImage" type="Sprite2D" parent="Decoration/ScareCrow"] +position = Vector2(132, 48) +scale = Vector2(1.2, 1.2) +texture = ExtResource("43_6rkns") + +[node name="ScareCrowName" type="RichTextLabel" parent="Decoration/ScareCrow"] +layout_mode = 0 +offset_left = -65.0 +offset_top = -145.0 +offset_right = 339.0 +offset_bottom = -90.0 +theme_override_font_sizes/bold_italics_font_size = 40 +theme_override_font_sizes/italics_font_size = 40 +theme_override_font_sizes/mono_font_size = 40 +theme_override_font_sizes/normal_font_size = 40 +theme_override_font_sizes/bold_font_size = 40 +bbcode_enabled = true +text = "稻草人" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="BackgroundPanel" type="Panel" parent="Decoration/ScareCrow"] +layout_mode = 0 +offset_left = -65.0 +offset_top = -265.0 +offset_right = 339.0 +offset_bottom = -145.0 +theme_override_styles/panel = SubResource("StyleBoxFlat_6ylhg") + +[node name="ScareCrowtalks" type="RichTextLabel" parent="Decoration/ScareCrow/BackgroundPanel"] +layout_mode = 0 +offset_left = 13.0 +offset_right = 390.0 +offset_bottom = 113.0 +theme_override_constants/outline_size = 20 +theme_override_font_sizes/bold_italics_font_size = 35 +theme_override_font_sizes/italics_font_size = 35 +theme_override_font_sizes/mono_font_size = 35 +theme_override_font_sizes/normal_font_size = 35 +theme_override_font_sizes/bold_font_size = 35 +bbcode_enabled = true +text = "臭小子不要偷我的菜,被我逮到你就完蛋了!" +autowrap_mode = 2 +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="PlayerStore" type="Button" parent="Decoration"] +self_modulate = Color(1, 1, 1, 0) +custom_minimum_size = Vector2(100, 100) +offset_left = 122.0 +offset_top = -108.0 +offset_right = 386.0 +offset_bottom = 156.0 +scale = Vector2(0.4, 0.4) + +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration/PlayerStore"] +position = Vector2(132, 134) +scale = Vector2(1.4, 1.4) +texture = ExtResource("48_2i8fe") + +[node name="Image" type="Sprite2D" parent="Decoration/PlayerStore"] +position = Vector2(140, 57.5) +scale = Vector2(1.5, 1.5) +texture = ExtResource("54_06tja") + +[node name="Name" type="RichTextLabel" parent="Decoration/PlayerStore"] +layout_mode = 0 +offset_left = -65.0 +offset_top = -145.0 +offset_right = 339.0 +offset_bottom = -90.0 +theme_override_font_sizes/bold_italics_font_size = 40 +theme_override_font_sizes/italics_font_size = 40 +theme_override_font_sizes/mono_font_size = 40 +theme_override_font_sizes/normal_font_size = 40 +theme_override_font_sizes/bold_font_size = 40 +bbcode_enabled = true +text = "小卖部" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="SeedStore" type="Button" parent="Decoration"] +self_modulate = Color(1, 1, 1, 0) +custom_minimum_size = Vector2(100, 100) +offset_left = 244.0 +offset_top = -108.0 +offset_right = 508.0 +offset_bottom = 156.0 +scale = Vector2(0.4, 0.4) + +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration/SeedStore"] +position = Vector2(132, 134) +scale = Vector2(1.4, 1.4) +texture = ExtResource("48_2i8fe") + +[node name="Image" type="Sprite2D" parent="Decoration/SeedStore"] +position = Vector2(132, 48) +scale = Vector2(1.5, 1.5) +texture = ExtResource("55_g4i0f") + +[node name="Name" type="RichTextLabel" parent="Decoration/SeedStore"] +layout_mode = 0 +offset_left = -65.0 +offset_top = -145.0 +offset_right = 339.0 +offset_bottom = -90.0 +theme_override_font_sizes/bold_italics_font_size = 40 +theme_override_font_sizes/italics_font_size = 40 +theme_override_font_sizes/mono_font_size = 40 +theme_override_font_sizes/normal_font_size = 40 +theme_override_font_sizes/bold_font_size = 40 +bbcode_enabled = true +text = "种子商店" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="ItemStore" type="Button" parent="Decoration"] +self_modulate = Color(1, 1, 1, 0) +custom_minimum_size = Vector2(100, 100) +offset_left = 366.0 +offset_top = -108.0 +offset_right = 630.0 +offset_bottom = 156.0 +scale = Vector2(0.4, 0.4) + +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration/ItemStore"] +position = Vector2(132, 134) +scale = Vector2(1.4, 1.4) +texture = ExtResource("48_2i8fe") + +[node name="Image" type="Sprite2D" parent="Decoration/ItemStore"] +position = Vector2(132, 48) +scale = Vector2(1.5, 1.5) +texture = ExtResource("56_rlmnt") + +[node name="Name" type="RichTextLabel" parent="Decoration/ItemStore"] +layout_mode = 0 +offset_left = -65.0 +offset_top = -145.0 +offset_right = 339.0 +offset_bottom = -90.0 +theme_override_font_sizes/bold_italics_font_size = 40 +theme_override_font_sizes/italics_font_size = 40 +theme_override_font_sizes/mono_font_size = 40 +theme_override_font_sizes/normal_font_size = 40 +theme_override_font_sizes/bold_font_size = 40 +bbcode_enabled = true +text = "道具商店" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="PetStore" type="Button" parent="Decoration"] +self_modulate = Color(1, 1, 1, 0) +custom_minimum_size = Vector2(100, 100) +offset_left = 488.0 +offset_top = -108.0 +offset_right = 752.0 +offset_bottom = 156.0 +scale = Vector2(0.4, 0.4) + +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration/PetStore"] +position = Vector2(132, 134) +scale = Vector2(1.4, 1.4) +texture = ExtResource("48_2i8fe") + +[node name="Image" type="Sprite2D" parent="Decoration/PetStore"] +position = Vector2(132, 48) +scale = Vector2(1.5, 1.5) +texture = ExtResource("57_rlmnt") + +[node name="Name" type="RichTextLabel" parent="Decoration/PetStore"] +layout_mode = 0 +offset_left = -65.0 +offset_top = -145.0 +offset_right = 339.0 +offset_bottom = -90.0 +theme_override_font_sizes/bold_italics_font_size = 40 +theme_override_font_sizes/italics_font_size = 40 +theme_override_font_sizes/mono_font_size = 40 +theme_override_font_sizes/normal_font_size = 40 +theme_override_font_sizes/bold_font_size = 40 +bbcode_enabled = true +text = "宠物商店" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="CropWarehouse" type="Button" parent="Decoration"] +self_modulate = Color(1, 1, 1, 0) +custom_minimum_size = Vector2(100, 100) +offset_left = 610.0 +offset_top = -108.0 +offset_right = 874.0 +offset_bottom = 156.0 +scale = Vector2(0.4, 0.4) + +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration/CropWarehouse"] +position = Vector2(132, 134) +scale = Vector2(1.4, 1.4) +texture = ExtResource("48_2i8fe") + +[node name="Image" type="Sprite2D" parent="Decoration/CropWarehouse"] +position = Vector2(132, 48) +scale = Vector2(1.2, 1.2) +texture = ExtResource("58_mjfri") + +[node name="Name" type="RichTextLabel" parent="Decoration/CropWarehouse"] +layout_mode = 0 +offset_left = -65.0 +offset_top = -145.0 +offset_right = 339.0 +offset_bottom = -90.0 +theme_override_font_sizes/bold_italics_font_size = 40 +theme_override_font_sizes/italics_font_size = 40 +theme_override_font_sizes/mono_font_size = 40 +theme_override_font_sizes/normal_font_size = 40 +theme_override_font_sizes/bold_font_size = 40 +bbcode_enabled = true +text = "作物仓库" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="SeedWarehouse" type="Button" parent="Decoration"] +self_modulate = Color(1, 1, 1, 0) +custom_minimum_size = Vector2(100, 100) +offset_left = 732.0 +offset_top = -108.0 +offset_right = 996.0 +offset_bottom = 156.0 +scale = Vector2(0.4, 0.4) + +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration/SeedWarehouse"] +position = Vector2(132, 134) +scale = Vector2(1.4, 1.4) +texture = ExtResource("48_2i8fe") + +[node name="Image" type="Sprite2D" parent="Decoration/SeedWarehouse"] +position = Vector2(132, 48) +scale = Vector2(1.2, 1.2) +texture = ExtResource("59_612dn") + +[node name="Name" type="RichTextLabel" parent="Decoration/SeedWarehouse"] +layout_mode = 0 +offset_left = -65.0 +offset_top = -145.0 +offset_right = 339.0 +offset_bottom = -90.0 +theme_override_font_sizes/bold_italics_font_size = 40 +theme_override_font_sizes/italics_font_size = 40 +theme_override_font_sizes/mono_font_size = 40 +theme_override_font_sizes/normal_font_size = 40 +theme_override_font_sizes/bold_font_size = 40 +bbcode_enabled = true +text = "种子仓库" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="PlayerRank" type="Button" parent="Decoration"] +self_modulate = Color(1, 1, 1, 0) +custom_minimum_size = Vector2(100, 100) +offset_left = 854.0 +offset_top = -108.0 +offset_right = 1118.0 +offset_bottom = 156.0 +scale = Vector2(0.4, 0.4) + +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration/PlayerRank"] +position = Vector2(132, 134) +scale = Vector2(1.4, 1.4) +texture = ExtResource("48_2i8fe") + +[node name="Image" type="Sprite2D" parent="Decoration/PlayerRank"] +position = Vector2(132, 48) +scale = Vector2(1.2, 1.2) +texture = ExtResource("60_2fqxv") + +[node name="Name" type="RichTextLabel" parent="Decoration/PlayerRank"] +layout_mode = 0 +offset_left = -65.0 +offset_top = -145.0 +offset_right = 339.0 +offset_bottom = -90.0 +theme_override_font_sizes/bold_italics_font_size = 40 +theme_override_font_sizes/italics_font_size = 40 +theme_override_font_sizes/mono_font_size = 40 +theme_override_font_sizes/normal_font_size = 40 +theme_override_font_sizes/bold_font_size = 40 +bbcode_enabled = true +text = "[rainbow]玩家排行榜[/rainbow]" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="WisdomTree" type="Button" parent="Decoration"] +self_modulate = Color(1, 1, 1, 0) +custom_minimum_size = Vector2(100, 100) +offset_left = 976.0 +offset_top = -108.0 +offset_right = 1240.0 +offset_bottom = 156.0 +scale = Vector2(0.4, 0.4) + +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration/WisdomTree"] +position = Vector2(132, 134) +scale = Vector2(1.4, 1.4) +texture = ExtResource("48_2i8fe") + +[node name="WisdomTreeImage" type="Sprite2D" parent="Decoration/WisdomTree"] +position = Vector2(135, -107.5) +scale = Vector2(2.5, 2.5) +texture = ExtResource("45_xvovi") + +[node name="TreeName" type="Label" parent="Decoration/WisdomTree"] +self_modulate = Color(2, 2, 2, 1) +layout_mode = 0 +offset_left = 37.0 +offset_top = -450.0 +offset_right = 302.0 +offset_bottom = -401.0 +theme_override_font_sizes/font_size = 35 +text = "智慧树" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="TreeStatus" type="Label" parent="Decoration/WisdomTree"] +self_modulate = Color(2, 2, 2, 1) +layout_mode = 0 +offset_left = -85.0 +offset_top = -495.0 +offset_right = 407.0 +offset_bottom = -446.0 +theme_override_font_sizes/font_size = 35 +text = "等级lv:99 高度:99m" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="BackgroundPanel" type="Panel" parent="Decoration/WisdomTree"] +layout_mode = 0 +offset_left = -175.0 +offset_top = -655.0 +offset_right = 528.0 +offset_bottom = -500.0 +theme_override_styles/panel = SubResource("StyleBoxFlat_6ylhg") + +[node name="AnonymousTalk" type="RichTextLabel" parent="Decoration/WisdomTree/BackgroundPanel"] +layout_mode = 0 +offset_left = 13.0 +offset_right = 695.0 +offset_bottom = 155.0 +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/outline_size = 15 +theme_override_font_sizes/bold_italics_font_size = 35 +theme_override_font_sizes/italics_font_size = 35 +theme_override_font_sizes/mono_font_size = 35 +theme_override_font_sizes/normal_font_size = 35 +theme_override_font_sizes/bold_font_size = 35 +bbcode_enabled = true +text = "给未来的某个陌生人说一句话吧" +autowrap_mode = 2 +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Decoration2" type="Node2D" parent="."] + +[node name="Decoration1" type="Button" parent="Decoration2"] +self_modulate = Color(1, 1, 1, 0) +custom_minimum_size = Vector2(100, 100) +offset_left = -122.0 +offset_top = -108.0 +offset_right = 142.0 +offset_bottom = 156.0 +scale = Vector2(0.4, 0.4) + +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration2/Decoration1"] +position = Vector2(132, 134) +scale = Vector2(1.4, 1.4) +texture = ExtResource("48_2i8fe") + +[node name="Image" type="Sprite2D" parent="Decoration2/Decoration1"] +position = Vector2(132, 48) +scale = Vector2(1.2, 1.2) +texture = ExtResource("49_xjiif") + +[node name="Name" type="RichTextLabel" parent="Decoration2/Decoration1"] +visible = false +layout_mode = 0 +offset_left = -65.0 +offset_top = -145.0 +offset_right = 339.0 +offset_bottom = -90.0 +theme_override_font_sizes/bold_italics_font_size = 40 +theme_override_font_sizes/italics_font_size = 40 +theme_override_font_sizes/mono_font_size = 40 +theme_override_font_sizes/normal_font_size = 40 +theme_override_font_sizes/bold_font_size = 40 +bbcode_enabled = true +text = "稻草人" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Decoration2" type="Button" parent="Decoration2"] +self_modulate = Color(1, 1, 1, 0) +custom_minimum_size = Vector2(100, 100) +offset_left = -122.0 +offset_right = 142.0 +offset_bottom = 264.0 +scale = Vector2(0.4, 0.4) + +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration2/Decoration2"] +position = Vector2(132, 134) +scale = Vector2(1.4, 1.4) +texture = ExtResource("48_2i8fe") + +[node name="Image" type="Sprite2D" parent="Decoration2/Decoration2"] +position = Vector2(132, 48) +scale = Vector2(1.2, 1.2) +texture = ExtResource("49_xjiif") + +[node name="Name" type="RichTextLabel" parent="Decoration2/Decoration2"] +visible = false +layout_mode = 0 +offset_left = -65.0 +offset_top = -145.0 +offset_right = 339.0 +offset_bottom = -90.0 +theme_override_font_sizes/bold_italics_font_size = 40 +theme_override_font_sizes/italics_font_size = 40 +theme_override_font_sizes/mono_font_size = 40 +theme_override_font_sizes/normal_font_size = 40 +theme_override_font_sizes/bold_font_size = 40 +bbcode_enabled = true +text = "稻草人" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Decoration3" type="Button" parent="Decoration2"] +self_modulate = Color(1, 1, 1, 0) +custom_minimum_size = Vector2(100, 100) +offset_left = -122.0 +offset_top = 108.0 +offset_right = 142.0 +offset_bottom = 372.0 +scale = Vector2(0.4, 0.4) + +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration2/Decoration3"] +position = Vector2(132, 134) +scale = Vector2(1.4, 1.4) +texture = ExtResource("48_2i8fe") + +[node name="Image" type="Sprite2D" parent="Decoration2/Decoration3"] +position = Vector2(132, 48) +scale = Vector2(1.2, 1.2) +texture = ExtResource("49_xjiif") + +[node name="Name" type="RichTextLabel" parent="Decoration2/Decoration3"] +visible = false +layout_mode = 0 +offset_left = -65.0 +offset_top = -145.0 +offset_right = 339.0 +offset_bottom = -90.0 +theme_override_font_sizes/bold_italics_font_size = 40 +theme_override_font_sizes/italics_font_size = 40 +theme_override_font_sizes/mono_font_size = 40 +theme_override_font_sizes/normal_font_size = 40 +theme_override_font_sizes/bold_font_size = 40 +bbcode_enabled = true +text = "稻草人" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Decoration4" type="Button" parent="Decoration2"] +self_modulate = Color(1, 1, 1, 0) +custom_minimum_size = Vector2(100, 100) +offset_left = -122.0 +offset_top = 216.0 +offset_right = 142.0 +offset_bottom = 480.0 +scale = Vector2(0.4, 0.4) + +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration2/Decoration4"] +position = Vector2(132, 134) +scale = Vector2(1.4, 1.4) +texture = ExtResource("48_2i8fe") + +[node name="Image" type="Sprite2D" parent="Decoration2/Decoration4"] +position = Vector2(132, 48) +scale = Vector2(1.2, 1.2) +texture = ExtResource("49_xjiif") + +[node name="Name" type="RichTextLabel" parent="Decoration2/Decoration4"] +visible = false +layout_mode = 0 +offset_left = -65.0 +offset_top = -145.0 +offset_right = 339.0 +offset_bottom = -90.0 +theme_override_font_sizes/bold_italics_font_size = 40 +theme_override_font_sizes/italics_font_size = 40 +theme_override_font_sizes/mono_font_size = 40 +theme_override_font_sizes/normal_font_size = 40 +theme_override_font_sizes/bold_font_size = 40 +bbcode_enabled = true +text = "稻草人" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Decoration5" type="Button" parent="Decoration2"] +self_modulate = Color(1, 1, 1, 0) +custom_minimum_size = Vector2(100, 100) +offset_left = -122.0 +offset_top = 324.0 +offset_right = 142.0 +offset_bottom = 588.0 +scale = Vector2(0.4, 0.4) + +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration2/Decoration5"] +position = Vector2(132, 134) +scale = Vector2(1.4, 1.4) +texture = ExtResource("48_2i8fe") + +[node name="Image" type="Sprite2D" parent="Decoration2/Decoration5"] +position = Vector2(132, 48) +scale = Vector2(1.2, 1.2) +texture = ExtResource("49_xjiif") + +[node name="Name" type="RichTextLabel" parent="Decoration2/Decoration5"] +visible = false +layout_mode = 0 +offset_left = -65.0 +offset_top = -145.0 +offset_right = 339.0 +offset_bottom = -90.0 +theme_override_font_sizes/bold_italics_font_size = 40 +theme_override_font_sizes/italics_font_size = 40 +theme_override_font_sizes/mono_font_size = 40 +theme_override_font_sizes/normal_font_size = 40 +theme_override_font_sizes/bold_font_size = 40 +bbcode_enabled = true +text = "稻草人" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Decoration6" type="Button" parent="Decoration2"] +self_modulate = Color(1, 1, 1, 0) +custom_minimum_size = Vector2(100, 100) +offset_left = -122.0 +offset_top = 432.0 +offset_right = 142.0 +offset_bottom = 696.0 +scale = Vector2(0.4, 0.4) + +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration2/Decoration6"] +position = Vector2(132, 134) +scale = Vector2(1.4, 1.4) +texture = ExtResource("48_2i8fe") + +[node name="Image" type="Sprite2D" parent="Decoration2/Decoration6"] +position = Vector2(132, 48) +scale = Vector2(1.2, 1.2) +texture = ExtResource("49_xjiif") + +[node name="Name" type="RichTextLabel" parent="Decoration2/Decoration6"] +visible = false +layout_mode = 0 +offset_left = -65.0 +offset_top = -145.0 +offset_right = 339.0 +offset_bottom = -90.0 +theme_override_font_sizes/bold_italics_font_size = 40 +theme_override_font_sizes/italics_font_size = 40 +theme_override_font_sizes/mono_font_size = 40 +theme_override_font_sizes/normal_font_size = 40 +theme_override_font_sizes/bold_font_size = 40 +bbcode_enabled = true +text = "稻草人" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Decoration7" type="Button" parent="Decoration2"] +self_modulate = Color(1, 1, 1, 0) +custom_minimum_size = Vector2(100, 100) +offset_left = -122.0 +offset_top = 540.0 +offset_right = 142.0 +offset_bottom = 804.0 +scale = Vector2(0.4, 0.4) + +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration2/Decoration7"] +position = Vector2(132, 134) +scale = Vector2(1.4, 1.4) +texture = ExtResource("48_2i8fe") + +[node name="Image" type="Sprite2D" parent="Decoration2/Decoration7"] +position = Vector2(132, 48) +scale = Vector2(1.2, 1.2) +texture = ExtResource("49_xjiif") + +[node name="Name" type="RichTextLabel" parent="Decoration2/Decoration7"] +visible = false +layout_mode = 0 +offset_left = -65.0 +offset_top = -145.0 +offset_right = 339.0 +offset_bottom = -90.0 +theme_override_font_sizes/bold_italics_font_size = 40 +theme_override_font_sizes/italics_font_size = 40 +theme_override_font_sizes/mono_font_size = 40 +theme_override_font_sizes/normal_font_size = 40 +theme_override_font_sizes/bold_font_size = 40 +bbcode_enabled = true +text = "稻草人" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Decoration8" type="Button" parent="Decoration2"] +self_modulate = Color(1, 1, 1, 0) +custom_minimum_size = Vector2(100, 100) +offset_left = -122.0 +offset_top = 648.0 +offset_right = 142.0 +offset_bottom = 912.0 +scale = Vector2(0.4, 0.4) + +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration2/Decoration8"] +position = Vector2(132, 134) +scale = Vector2(1.4, 1.4) +texture = ExtResource("48_2i8fe") + +[node name="Image" type="Sprite2D" parent="Decoration2/Decoration8"] +position = Vector2(132, 48) +scale = Vector2(1.2, 1.2) +texture = ExtResource("49_xjiif") + +[node name="Name" type="RichTextLabel" parent="Decoration2/Decoration8"] +visible = false +layout_mode = 0 +offset_left = -65.0 +offset_top = -145.0 +offset_right = 339.0 +offset_bottom = -90.0 +theme_override_font_sizes/bold_italics_font_size = 40 +theme_override_font_sizes/italics_font_size = 40 +theme_override_font_sizes/mono_font_size = 40 +theme_override_font_sizes/normal_font_size = 40 +theme_override_font_sizes/bold_font_size = 40 +bbcode_enabled = true +text = "稻草人" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Decoration9" type="Button" parent="Decoration2"] +self_modulate = Color(1, 1, 1, 0) +custom_minimum_size = Vector2(100, 100) +offset_left = -122.0 +offset_top = 756.0 +offset_right = 142.0 +offset_bottom = 1020.0 +scale = Vector2(0.4, 0.4) + +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration2/Decoration9"] +position = Vector2(132, 134) +scale = Vector2(1.4, 1.4) +texture = ExtResource("48_2i8fe") + +[node name="Image" type="Sprite2D" parent="Decoration2/Decoration9"] +position = Vector2(132, 48) +scale = Vector2(1.2, 1.2) +texture = ExtResource("49_xjiif") + +[node name="Name" type="RichTextLabel" parent="Decoration2/Decoration9"] +visible = false +layout_mode = 0 +offset_left = -65.0 +offset_top = -145.0 +offset_right = 339.0 +offset_bottom = -90.0 +theme_override_font_sizes/bold_italics_font_size = 40 +theme_override_font_sizes/italics_font_size = 40 +theme_override_font_sizes/mono_font_size = 40 +theme_override_font_sizes/normal_font_size = 40 +theme_override_font_sizes/bold_font_size = 40 +bbcode_enabled = true +text = "稻草人" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Decoration10" type="Button" parent="Decoration2"] +self_modulate = Color(1, 1, 1, 0) +custom_minimum_size = Vector2(100, 100) +offset_left = -122.0 +offset_top = 864.0 +offset_right = 142.0 +offset_bottom = 1128.0 +scale = Vector2(0.4, 0.4) + +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration2/Decoration10"] +position = Vector2(132, 134) +scale = Vector2(1.4, 1.4) +texture = ExtResource("48_2i8fe") + +[node name="Image" type="Sprite2D" parent="Decoration2/Decoration10"] +position = Vector2(132, 48) +scale = Vector2(1.2, 1.2) +texture = ExtResource("49_xjiif") + +[node name="Name" type="RichTextLabel" parent="Decoration2/Decoration10"] +visible = false +layout_mode = 0 +offset_left = -65.0 +offset_top = -145.0 +offset_right = 339.0 +offset_bottom = -90.0 +theme_override_font_sizes/bold_italics_font_size = 40 +theme_override_font_sizes/italics_font_size = 40 +theme_override_font_sizes/mono_font_size = 40 +theme_override_font_sizes/normal_font_size = 40 +theme_override_font_sizes/bold_font_size = 40 +bbcode_enabled = true +text = "稻草人" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Decoration3" type="Node2D" parent="."] +position = Vector2(1220, 0) + +[node name="Decoration1" type="Button" parent="Decoration3"] +self_modulate = Color(1, 1, 1, 0) +custom_minimum_size = Vector2(100, 100) +offset_left = -122.0 +offset_top = -108.0 +offset_right = 142.0 +offset_bottom = 156.0 +scale = Vector2(0.4, 0.4) + +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration3/Decoration1"] +position = Vector2(132, 134) +scale = Vector2(1.4, 1.4) +texture = ExtResource("48_2i8fe") + +[node name="Image" type="Sprite2D" parent="Decoration3/Decoration1"] +position = Vector2(132, 48) +scale = Vector2(1.2, 1.2) +texture = ExtResource("49_xjiif") + +[node name="Name" type="RichTextLabel" parent="Decoration3/Decoration1"] +visible = false +layout_mode = 0 +offset_left = -65.0 +offset_top = -145.0 +offset_right = 339.0 +offset_bottom = -90.0 +theme_override_font_sizes/bold_italics_font_size = 40 +theme_override_font_sizes/italics_font_size = 40 +theme_override_font_sizes/mono_font_size = 40 +theme_override_font_sizes/normal_font_size = 40 +theme_override_font_sizes/bold_font_size = 40 +bbcode_enabled = true +text = "稻草人" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Decoration2" type="Button" parent="Decoration3"] +self_modulate = Color(1, 1, 1, 0) +custom_minimum_size = Vector2(100, 100) +offset_left = -122.0 +offset_right = 142.0 +offset_bottom = 264.0 +scale = Vector2(0.4, 0.4) + +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration3/Decoration2"] +position = Vector2(132, 134) +scale = Vector2(1.4, 1.4) +texture = ExtResource("48_2i8fe") + +[node name="Image" type="Sprite2D" parent="Decoration3/Decoration2"] +position = Vector2(132, 48) +scale = Vector2(1.2, 1.2) +texture = ExtResource("49_xjiif") + +[node name="Name" type="RichTextLabel" parent="Decoration3/Decoration2"] +visible = false +layout_mode = 0 +offset_left = -65.0 +offset_top = -145.0 +offset_right = 339.0 +offset_bottom = -90.0 +theme_override_font_sizes/bold_italics_font_size = 40 +theme_override_font_sizes/italics_font_size = 40 +theme_override_font_sizes/mono_font_size = 40 +theme_override_font_sizes/normal_font_size = 40 +theme_override_font_sizes/bold_font_size = 40 +bbcode_enabled = true +text = "稻草人" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Decoration3" type="Button" parent="Decoration3"] +self_modulate = Color(1, 1, 1, 0) +custom_minimum_size = Vector2(100, 100) +offset_left = -122.0 +offset_top = 108.0 +offset_right = 142.0 +offset_bottom = 372.0 +scale = Vector2(0.4, 0.4) + +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration3/Decoration3"] +position = Vector2(132, 134) +scale = Vector2(1.4, 1.4) +texture = ExtResource("48_2i8fe") + +[node name="Image" type="Sprite2D" parent="Decoration3/Decoration3"] +position = Vector2(132, 48) +scale = Vector2(1.2, 1.2) +texture = ExtResource("49_xjiif") + +[node name="Name" type="RichTextLabel" parent="Decoration3/Decoration3"] +visible = false +layout_mode = 0 +offset_left = -65.0 +offset_top = -145.0 +offset_right = 339.0 +offset_bottom = -90.0 +theme_override_font_sizes/bold_italics_font_size = 40 +theme_override_font_sizes/italics_font_size = 40 +theme_override_font_sizes/mono_font_size = 40 +theme_override_font_sizes/normal_font_size = 40 +theme_override_font_sizes/bold_font_size = 40 +bbcode_enabled = true +text = "稻草人" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Decoration4" type="Button" parent="Decoration3"] +self_modulate = Color(1, 1, 1, 0) +custom_minimum_size = Vector2(100, 100) +offset_left = -122.0 +offset_top = 216.0 +offset_right = 142.0 +offset_bottom = 480.0 +scale = Vector2(0.4, 0.4) + +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration3/Decoration4"] +position = Vector2(132, 134) +scale = Vector2(1.4, 1.4) +texture = ExtResource("48_2i8fe") + +[node name="Image" type="Sprite2D" parent="Decoration3/Decoration4"] +position = Vector2(132, 48) +scale = Vector2(1.2, 1.2) +texture = ExtResource("49_xjiif") + +[node name="Name" type="RichTextLabel" parent="Decoration3/Decoration4"] +visible = false +layout_mode = 0 +offset_left = -65.0 +offset_top = -145.0 +offset_right = 339.0 +offset_bottom = -90.0 +theme_override_font_sizes/bold_italics_font_size = 40 +theme_override_font_sizes/italics_font_size = 40 +theme_override_font_sizes/mono_font_size = 40 +theme_override_font_sizes/normal_font_size = 40 +theme_override_font_sizes/bold_font_size = 40 +bbcode_enabled = true +text = "稻草人" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Decoration5" type="Button" parent="Decoration3"] +self_modulate = Color(1, 1, 1, 0) +custom_minimum_size = Vector2(100, 100) +offset_left = -122.0 +offset_top = 324.0 +offset_right = 142.0 +offset_bottom = 588.0 +scale = Vector2(0.4, 0.4) + +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration3/Decoration5"] +position = Vector2(132, 134) +scale = Vector2(1.4, 1.4) +texture = ExtResource("48_2i8fe") + +[node name="Image" type="Sprite2D" parent="Decoration3/Decoration5"] +position = Vector2(132, 48) +scale = Vector2(1.2, 1.2) +texture = ExtResource("49_xjiif") + +[node name="Name" type="RichTextLabel" parent="Decoration3/Decoration5"] +visible = false +layout_mode = 0 +offset_left = -65.0 +offset_top = -145.0 +offset_right = 339.0 +offset_bottom = -90.0 +theme_override_font_sizes/bold_italics_font_size = 40 +theme_override_font_sizes/italics_font_size = 40 +theme_override_font_sizes/mono_font_size = 40 +theme_override_font_sizes/normal_font_size = 40 +theme_override_font_sizes/bold_font_size = 40 +bbcode_enabled = true +text = "稻草人" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Decoration6" type="Button" parent="Decoration3"] +self_modulate = Color(1, 1, 1, 0) +custom_minimum_size = Vector2(100, 100) +offset_left = -122.0 +offset_top = 432.0 +offset_right = 142.0 +offset_bottom = 696.0 +scale = Vector2(0.4, 0.4) + +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration3/Decoration6"] +position = Vector2(132, 134) +scale = Vector2(1.4, 1.4) +texture = ExtResource("48_2i8fe") + +[node name="Image" type="Sprite2D" parent="Decoration3/Decoration6"] +position = Vector2(132, 48) +scale = Vector2(1.2, 1.2) +texture = ExtResource("49_xjiif") + +[node name="Name" type="RichTextLabel" parent="Decoration3/Decoration6"] +visible = false +layout_mode = 0 +offset_left = -65.0 +offset_top = -145.0 +offset_right = 339.0 +offset_bottom = -90.0 +theme_override_font_sizes/bold_italics_font_size = 40 +theme_override_font_sizes/italics_font_size = 40 +theme_override_font_sizes/mono_font_size = 40 +theme_override_font_sizes/normal_font_size = 40 +theme_override_font_sizes/bold_font_size = 40 +bbcode_enabled = true +text = "稻草人" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Decoration7" type="Button" parent="Decoration3"] +self_modulate = Color(1, 1, 1, 0) +custom_minimum_size = Vector2(100, 100) +offset_left = -122.0 +offset_top = 540.0 +offset_right = 142.0 +offset_bottom = 804.0 +scale = Vector2(0.4, 0.4) + +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration3/Decoration7"] +position = Vector2(132, 134) +scale = Vector2(1.4, 1.4) +texture = ExtResource("48_2i8fe") + +[node name="Image" type="Sprite2D" parent="Decoration3/Decoration7"] +position = Vector2(132, 48) +scale = Vector2(1.2, 1.2) +texture = ExtResource("49_xjiif") + +[node name="Name" type="RichTextLabel" parent="Decoration3/Decoration7"] +visible = false +layout_mode = 0 +offset_left = -65.0 +offset_top = -145.0 +offset_right = 339.0 +offset_bottom = -90.0 +theme_override_font_sizes/bold_italics_font_size = 40 +theme_override_font_sizes/italics_font_size = 40 +theme_override_font_sizes/mono_font_size = 40 +theme_override_font_sizes/normal_font_size = 40 +theme_override_font_sizes/bold_font_size = 40 +bbcode_enabled = true +text = "稻草人" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Decoration8" type="Button" parent="Decoration3"] +self_modulate = Color(1, 1, 1, 0) +custom_minimum_size = Vector2(100, 100) +offset_left = -122.0 +offset_top = 648.0 +offset_right = 142.0 +offset_bottom = 912.0 +scale = Vector2(0.4, 0.4) + +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration3/Decoration8"] +position = Vector2(132, 134) +scale = Vector2(1.4, 1.4) +texture = ExtResource("48_2i8fe") + +[node name="Image" type="Sprite2D" parent="Decoration3/Decoration8"] +position = Vector2(132, 48) +scale = Vector2(1.2, 1.2) +texture = ExtResource("49_xjiif") + +[node name="Name" type="RichTextLabel" parent="Decoration3/Decoration8"] +visible = false +layout_mode = 0 +offset_left = -65.0 +offset_top = -145.0 +offset_right = 339.0 +offset_bottom = -90.0 +theme_override_font_sizes/bold_italics_font_size = 40 +theme_override_font_sizes/italics_font_size = 40 +theme_override_font_sizes/mono_font_size = 40 +theme_override_font_sizes/normal_font_size = 40 +theme_override_font_sizes/bold_font_size = 40 +bbcode_enabled = true +text = "稻草人" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Decoration9" type="Button" parent="Decoration3"] +self_modulate = Color(1, 1, 1, 0) +custom_minimum_size = Vector2(100, 100) +offset_left = -122.0 +offset_top = 756.0 +offset_right = 142.0 +offset_bottom = 1020.0 +scale = Vector2(0.4, 0.4) + +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration3/Decoration9"] +position = Vector2(132, 134) +scale = Vector2(1.4, 1.4) +texture = ExtResource("48_2i8fe") + +[node name="Image" type="Sprite2D" parent="Decoration3/Decoration9"] +position = Vector2(132, 48) +scale = Vector2(1.2, 1.2) +texture = ExtResource("49_xjiif") + +[node name="Name" type="RichTextLabel" parent="Decoration3/Decoration9"] +visible = false +layout_mode = 0 +offset_left = -65.0 +offset_top = -145.0 +offset_right = 339.0 +offset_bottom = -90.0 +theme_override_font_sizes/bold_italics_font_size = 40 +theme_override_font_sizes/italics_font_size = 40 +theme_override_font_sizes/mono_font_size = 40 +theme_override_font_sizes/normal_font_size = 40 +theme_override_font_sizes/bold_font_size = 40 +bbcode_enabled = true +text = "稻草人" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Decoration10" type="Button" parent="Decoration3"] +self_modulate = Color(1, 1, 1, 0) +custom_minimum_size = Vector2(100, 100) +offset_left = -122.0 +offset_top = 864.0 +offset_right = 142.0 +offset_bottom = 1128.0 +scale = Vector2(0.4, 0.4) + +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration3/Decoration10"] +position = Vector2(132, 134) +scale = Vector2(1.4, 1.4) +texture = ExtResource("48_2i8fe") + +[node name="Image" type="Sprite2D" parent="Decoration3/Decoration10"] +position = Vector2(132, 48) +scale = Vector2(1.2, 1.2) +texture = ExtResource("49_xjiif") + +[node name="Name" type="RichTextLabel" parent="Decoration3/Decoration10"] +visible = false +layout_mode = 0 +offset_left = -65.0 +offset_top = -145.0 +offset_right = 339.0 +offset_bottom = -90.0 +theme_override_font_sizes/bold_italics_font_size = 40 +theme_override_font_sizes/italics_font_size = 40 +theme_override_font_sizes/mono_font_size = 40 +theme_override_font_sizes/normal_font_size = 40 +theme_override_font_sizes/bold_font_size = 40 +bbcode_enabled = true +text = "稻草人" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Decoration4" type="Node2D" parent="."] +position = Vector2(0, 976) + +[node name="DailyCheckinGift" type="Button" parent="Decoration4"] +self_modulate = Color(1, 1, 1, 0) +custom_minimum_size = Vector2(100, 100) +offset_top = -108.0 +offset_right = 264.0 +offset_bottom = 156.0 +scale = Vector2(0.4, 0.4) + +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration4/DailyCheckinGift"] +position = Vector2(132, 134) +scale = Vector2(1.4, 1.4) +texture = ExtResource("48_2i8fe") + +[node name="Image" type="Sprite2D" parent="Decoration4/DailyCheckinGift"] +position = Vector2(132, 48) +scale = Vector2(1.2, 1.2) +texture = ExtResource("63_ekowe") + +[node name="Name" type="RichTextLabel" parent="Decoration4/DailyCheckinGift"] +z_index = 5 +layout_mode = 0 +offset_left = -65.0 +offset_top = -145.0 +offset_right = 339.0 +offset_bottom = -90.0 +theme_override_font_sizes/bold_italics_font_size = 40 +theme_override_font_sizes/italics_font_size = 40 +theme_override_font_sizes/mono_font_size = 40 +theme_override_font_sizes/normal_font_size = 40 +theme_override_font_sizes/bold_font_size = 40 +bbcode_enabled = true +text = "每日签到礼包" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="OnlineTimeGift" type="Button" parent="Decoration4"] +self_modulate = Color(1, 1, 1, 0) +custom_minimum_size = Vector2(100, 100) +offset_left = 122.0 +offset_top = -108.0 +offset_right = 386.0 +offset_bottom = 156.0 +scale = Vector2(0.4, 0.4) + +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration4/OnlineTimeGift"] +position = Vector2(132, 134) +scale = Vector2(1.4, 1.4) +texture = ExtResource("48_2i8fe") + +[node name="Image" type="Sprite2D" parent="Decoration4/OnlineTimeGift"] +position = Vector2(132, 48) +scale = Vector2(1.2, 1.2) +texture = ExtResource("64_crc4a") + +[node name="Name" type="RichTextLabel" parent="Decoration4/OnlineTimeGift"] +z_index = 5 +layout_mode = 0 +offset_left = -65.0 +offset_top = -145.0 +offset_right = 339.0 +offset_bottom = -90.0 +theme_override_font_sizes/bold_italics_font_size = 40 +theme_override_font_sizes/italics_font_size = 40 +theme_override_font_sizes/mono_font_size = 40 +theme_override_font_sizes/normal_font_size = 40 +theme_override_font_sizes/bold_font_size = 40 +bbcode_enabled = true +text = "在线时长礼包" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="TodayDivination" type="Button" parent="Decoration4"] +self_modulate = Color(1, 1, 1, 0) +custom_minimum_size = Vector2(100, 100) +offset_left = 244.0 +offset_top = -108.0 +offset_right = 508.0 +offset_bottom = 156.0 +scale = Vector2(0.4, 0.4) + +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration4/TodayDivination"] +position = Vector2(132, 134) +scale = Vector2(1.4, 1.4) +texture = ExtResource("48_2i8fe") + +[node name="Image" type="Sprite2D" parent="Decoration4/TodayDivination"] +position = Vector2(132, 48) +scale = Vector2(1.2, 1.2) +texture = ExtResource("68_yceks") + +[node name="Name" type="RichTextLabel" parent="Decoration4/TodayDivination"] +z_index = 5 +layout_mode = 0 +offset_left = -65.0 +offset_top = -145.0 +offset_right = 339.0 +offset_bottom = -90.0 +theme_override_font_sizes/bold_italics_font_size = 40 +theme_override_font_sizes/italics_font_size = 40 +theme_override_font_sizes/mono_font_size = 40 +theme_override_font_sizes/normal_font_size = 40 +theme_override_font_sizes/bold_font_size = 40 +bbcode_enabled = true +text = "今日占卜" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="PetLadderMatch" type="Button" parent="Decoration4"] +self_modulate = Color(1, 1, 1, 0) +custom_minimum_size = Vector2(100, 100) +offset_left = 366.0 +offset_top = -108.0 +offset_right = 630.0 +offset_bottom = 156.0 +scale = Vector2(0.4, 0.4) + +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration4/PetLadderMatch"] +position = Vector2(132, 134) +scale = Vector2(1.4, 1.4) +texture = ExtResource("48_2i8fe") + +[node name="Image" type="Sprite2D" parent="Decoration4/PetLadderMatch"] +position = Vector2(132, 48) +scale = Vector2(1.2, 1.2) +texture = ExtResource("69_yceks") + +[node name="Name" type="RichTextLabel" parent="Decoration4/PetLadderMatch"] +z_index = 5 +layout_mode = 0 +offset_left = -65.0 +offset_top = -145.0 +offset_right = 339.0 +offset_bottom = -90.0 +theme_override_font_sizes/bold_italics_font_size = 40 +theme_override_font_sizes/italics_font_size = 40 +theme_override_font_sizes/mono_font_size = 40 +theme_override_font_sizes/normal_font_size = 40 +theme_override_font_sizes/bold_font_size = 40 +bbcode_enabled = true +text = "宠物星辰塔" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="PetNest" type="Button" parent="Decoration4"] +self_modulate = Color(1, 1, 1, 0) +custom_minimum_size = Vector2(100, 100) +offset_left = 488.0 +offset_top = -108.0 +offset_right = 752.0 +offset_bottom = 156.0 +scale = Vector2(0.4, 0.4) + +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration4/PetNest"] +position = Vector2(132, 134) +scale = Vector2(1.4, 1.4) +texture = ExtResource("48_2i8fe") + +[node name="Image" type="Sprite2D" parent="Decoration4/PetNest"] +position = Vector2(132, 48) +scale = Vector2(1.2, 1.2) +texture = ExtResource("70_yceks") + +[node name="Name" type="RichTextLabel" parent="Decoration4/PetNest"] +z_index = 5 +layout_mode = 0 +offset_left = -65.0 +offset_top = -145.0 +offset_right = 339.0 +offset_bottom = -90.0 +theme_override_font_sizes/bold_italics_font_size = 40 +theme_override_font_sizes/italics_font_size = 40 +theme_override_font_sizes/mono_font_size = 40 +theme_override_font_sizes/normal_font_size = 40 +theme_override_font_sizes/bold_font_size = 40 +bbcode_enabled = true +text = "宠物小窝" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="PlaySmallGame" type="Button" parent="Decoration4"] +self_modulate = Color(1, 1, 1, 0) +custom_minimum_size = Vector2(100, 100) +offset_left = 610.0 +offset_top = -108.0 +offset_right = 874.0 +offset_bottom = 156.0 +scale = Vector2(0.4, 0.4) + +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration4/PlaySmallGame"] +position = Vector2(132, 134) +scale = Vector2(1.4, 1.4) +texture = ExtResource("48_2i8fe") + +[node name="Image" type="Sprite2D" parent="Decoration4/PlaySmallGame"] +position = Vector2(132, 48) +scale = Vector2(1.2, 1.2) +texture = ExtResource("71_w7q6d") + +[node name="Name" type="RichTextLabel" parent="Decoration4/PlaySmallGame"] +z_index = 5 +layout_mode = 0 +offset_left = -65.0 +offset_top = -145.0 +offset_right = 339.0 +offset_bottom = -90.0 +theme_override_font_sizes/bold_italics_font_size = 40 +theme_override_font_sizes/italics_font_size = 40 +theme_override_font_sizes/mono_font_size = 40 +theme_override_font_sizes/normal_font_size = 40 +theme_override_font_sizes/bold_font_size = 40 +bbcode_enabled = true +text = "玩玩小游戏" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="MysteryFarmland" type="Button" parent="Decoration4"] +self_modulate = Color(1, 1, 1, 0) +custom_minimum_size = Vector2(100, 100) +offset_left = 732.0 +offset_top = -108.0 +offset_right = 996.0 +offset_bottom = 156.0 +scale = Vector2(0.4, 0.4) + +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration4/MysteryFarmland"] +position = Vector2(132, 134) +scale = Vector2(1.4, 1.4) +texture = ExtResource("48_2i8fe") + +[node name="Image" type="Sprite2D" parent="Decoration4/MysteryFarmland"] +position = Vector2(132, 48) +scale = Vector2(1.2, 1.2) +texture = ExtResource("72_7i23t") + +[node name="Name" type="RichTextLabel" parent="Decoration4/MysteryFarmland"] +z_index = 5 +layout_mode = 0 +offset_left = -65.0 +offset_top = -145.0 +offset_right = 339.0 +offset_bottom = -90.0 +theme_override_font_sizes/bold_italics_font_size = 40 +theme_override_font_sizes/italics_font_size = 40 +theme_override_font_sizes/mono_font_size = 40 +theme_override_font_sizes/normal_font_size = 40 +theme_override_font_sizes/bold_font_size = 40 +bbcode_enabled = true +text = "神秘农场" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="PetBag" type="Button" parent="Decoration4"] +self_modulate = Color(1, 1, 1, 0) +custom_minimum_size = Vector2(100, 100) +offset_left = 854.0 +offset_top = -108.0 +offset_right = 1118.0 +offset_bottom = 156.0 +scale = Vector2(0.4, 0.4) + +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration4/PetBag"] +position = Vector2(132, 134) +scale = Vector2(1.4, 1.4) +texture = ExtResource("48_2i8fe") + +[node name="Image" type="Sprite2D" parent="Decoration4/PetBag"] +position = Vector2(132, 48) +scale = Vector2(1.2, 1.2) +texture = ExtResource("67_onvxb") + +[node name="Name" type="RichTextLabel" parent="Decoration4/PetBag"] +z_index = 5 +layout_mode = 0 +offset_left = -65.0 +offset_top = -145.0 +offset_right = 339.0 +offset_bottom = -90.0 +theme_override_font_sizes/bold_italics_font_size = 40 +theme_override_font_sizes/italics_font_size = 40 +theme_override_font_sizes/mono_font_size = 40 +theme_override_font_sizes/normal_font_size = 40 +theme_override_font_sizes/bold_font_size = 40 +bbcode_enabled = true +text = "宠物背包" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="ItemBag" type="Button" parent="Decoration4"] +self_modulate = Color(1, 1, 1, 0) +custom_minimum_size = Vector2(100, 100) +offset_left = 976.0 +offset_top = -108.0 +offset_right = 1240.0 +offset_bottom = 156.0 +scale = Vector2(0.4, 0.4) + +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration4/ItemBag"] +position = Vector2(132, 134) +scale = Vector2(1.4, 1.4) +texture = ExtResource("48_2i8fe") + +[node name="Image" type="Sprite2D" parent="Decoration4/ItemBag"] +position = Vector2(132, 48) +scale = Vector2(1.2, 1.2) +texture = ExtResource("68_bpbm8") + +[node name="Name" type="Label" parent="Decoration4/ItemBag"] +self_modulate = Color(2, 2, 2, 1) +z_index = 5 +layout_mode = 0 +offset_top = -145.0 +offset_right = 265.0 +offset_bottom = -96.0 +theme_override_font_sizes/font_size = 35 +text = "道具背包" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="WeatherSystem" type="Node2D" parent="."] +script = ExtResource("62_uyv6e") + +[node name="CherryBlossomRain" type="Node2D" parent="WeatherSystem"] +visible = false +z_index = 10 +position = Vector2(934, -469) + +[node name="CherryBlossomRain1" type="GPUParticles2D" parent="WeatherSystem/CherryBlossomRain"] +self_modulate = Color(0.7, 0.7, 0.7, 1) +z_index = 10 +position = Vector2(-143, 11) +amount = 50 +texture = ExtResource("53_xyeuq") +lifetime = 20.0 +preprocess = 10.0 +visibility_rect = Rect2(-900, 0, 2300, 2000) +process_material = SubResource("ParticleProcessMaterial_tdq2s") + +[node name="CherryBlossomRain2" type="GPUParticles2D" parent="WeatherSystem/CherryBlossomRain"] +self_modulate = Color(0.7, 0.7, 0.7, 1) +z_index = 10 +position = Vector2(-143, 11) +amount = 50 +texture = ExtResource("54_t4s8j") +lifetime = 20.0 +preprocess = 10.0 +visibility_rect = Rect2(-900, 0, 2300, 2000) +process_material = SubResource("ParticleProcessMaterial_tdq2s") + +[node name="CherryBlossomRain3" type="GPUParticles2D" parent="WeatherSystem/CherryBlossomRain"] +self_modulate = Color(0.7, 0.7, 0.7, 1) +z_index = 10 +position = Vector2(-143, 11) +amount = 50 +texture = ExtResource("55_iluto") +lifetime = 20.0 +preprocess = 10.0 +visibility_rect = Rect2(-900, 0, 2300, 2000) +process_material = SubResource("ParticleProcessMaterial_tdq2s") + +[node name="GardeniaRain" type="Node2D" parent="WeatherSystem"] +visible = false +z_index = 10 +position = Vector2(759, -370) + +[node name="WillowLeafRain1" type="GPUParticles2D" parent="WeatherSystem/GardeniaRain"] +self_modulate = Color(0.9, 0.9, 0.9, 1) +z_index = 10 +amount = 50 +texture = ExtResource("53_tdq2s") +lifetime = 20.0 +preprocess = 10.0 +visibility_rect = Rect2(-900, 0, 2300, 2000) +process_material = SubResource("ParticleProcessMaterial_dygjy") + +[node name="WillowLeafRain2" type="GPUParticles2D" parent="WeatherSystem/GardeniaRain"] +self_modulate = Color(0.9, 0.9, 0.9, 1) +z_index = 10 +amount = 50 +texture = ExtResource("54_dygjy") +lifetime = 20.0 +preprocess = 10.0 +visibility_rect = Rect2(-900, 0, 2300, 2000) +process_material = SubResource("ParticleProcessMaterial_dygjy") + +[node name="WillowLeafRain3" type="GPUParticles2D" parent="WeatherSystem/GardeniaRain"] +self_modulate = Color(0.9, 0.9, 0.9, 1) +z_index = 10 +amount = 50 +texture = ExtResource("55_edvcq") +lifetime = 20.0 +preprocess = 10.0 +visibility_rect = Rect2(-900, 0, 2300, 2000) +process_material = SubResource("ParticleProcessMaterial_dygjy") + +[node name="WillowLeafRain" type="Node2D" parent="WeatherSystem"] +visible = false +z_index = 10 +position = Vector2(882, -469) + +[node name="WillowLeafRain1" type="GPUParticles2D" parent="WeatherSystem/WillowLeafRain"] +self_modulate = Color(0.7, 0.7, 0.7, 1) +z_index = 10 +amount = 50 +texture = ExtResource("69_uyv6e") +lifetime = 20.0 +preprocess = 10.0 +visibility_rect = Rect2(-900, 0, 2300, 2000) +process_material = SubResource("ParticleProcessMaterial_6fhdl") + +[node name="WillowLeafRain2" type="GPUParticles2D" parent="WeatherSystem/WillowLeafRain"] +self_modulate = Color(0.7, 0.7, 0.7, 1) +z_index = 10 +amount = 50 +texture = ExtResource("55_e8wx8") +lifetime = 20.0 +preprocess = 10.0 +visibility_rect = Rect2(-900, 0, 2300, 2000) +process_material = SubResource("ParticleProcessMaterial_e8wx8") + +[node name="WillowLeafRain3" type="GPUParticles2D" parent="WeatherSystem/WillowLeafRain"] +self_modulate = Color(0.7, 0.7, 0.7, 1) +z_index = 10 +amount = 50 +texture = ExtResource("54_jiccn") +lifetime = 20.0 +preprocess = 10.0 +visibility_rect = Rect2(-900, 0, 2300, 2000) +process_material = SubResource("ParticleProcessMaterial_4ka7t") + +[node name="Rain" type="GPUParticles2D" parent="WeatherSystem"] +visible = false +z_index = 10 +position = Vector2(246, -482) +amount = 450 +lifetime = 10.0 +preprocess = 20.0 +speed_scale = 1.5 +visibility_rect = Rect2(-900, 0, 2000, 2000) +trail_lifetime = 0.01 +process_material = SubResource("ParticleProcessMaterial_jiccn") + +[node name="Snow" type="GPUParticles2D" parent="WeatherSystem"] +visible = false +z_index = 10 +position = Vector2(16, -520) +amount = 300 +texture = ExtResource("53_4ka7t") +lifetime = 18.0 +preprocess = 30.0 +visibility_rect = Rect2(-900, 0, 2300, 2000) +process_material = SubResource("ParticleProcessMaterial_nf3jg") + +[node name="DayNightSystem" type="Node2D" parent="."] +script = ExtResource("73_6fhdl") + +[node name="CanvasModulate" type="CanvasModulate" parent="DayNightSystem"] +visible = false + +[node name="WorldEnvironment" type="WorldEnvironment" parent="."] +environment = SubResource("Environment_uyv6e") + +[connection signal="pressed" from="UI/GUI/GameInfoHBox3/WatchBroadcast" to="." method="_on_watch_broadcast_button_pressed"] +[connection signal="pressed" from="UI/GUI/FarmVBox/SeedStoreButton" to="." method="_on_open_store_button_pressed"] +[connection signal="pressed" from="UI/GUI/FarmVBox/SeedWarehouseButton" to="." method="_on_seed_warehouse_button_pressed"] +[connection signal="pressed" from="UI/GUI/FarmVBox/CropWarehouseButton" to="." method="_on_crop_warehouse_button_pressed"] +[connection signal="pressed" from="UI/GUI/FarmVBox/ItemStoreButton" to="." method="_on_item_store_button_pressed"] +[connection signal="pressed" from="UI/GUI/FarmVBox/ItemBagButton" to="." method="_on_item_bag_button_pressed"] +[connection signal="pressed" from="UI/GUI/FarmVBox/OneClickHarvestButton" to="." method="_on_one_click_harvestbutton_pressed"] +[connection signal="pressed" from="UI/GUI/FarmVBox/OneClickPlantButton" to="." method="_on_one_click_plant_button_pressed"] +[connection signal="pressed" from="UI/GUI/FarmVBox/AddNewGroundButton" to="." method="_on_add_new_ground_button_pressed"] +[connection signal="pressed" from="UI/GUI/VisitVBox/LikeButton" to="." method="_on_like_button_pressed"] +[connection signal="pressed" from="UI/GUI/VisitVBox/BattleButton" to="." method="_on_battle_button_pressed"] +[connection signal="pressed" from="UI/GUI/VisitVBox/SendMoneyButton" to="." method="_on_send_money_button_pressed"] +[connection signal="pressed" from="UI/GUI/VisitVBox/ReturnMyFarmButton" to="." method="_on_return_my_farm_button_pressed"] +[connection signal="pressed" from="UI/GUI/OtherVBox/TodayDivinationButton" to="." method="_on_today_divination_button_pressed"] +[connection signal="pressed" from="UI/GUI/OtherVBox/MyStoreButton" to="." method="_on_my_store_button_pressed"] +[connection signal="pressed" from="UI/GUI/OtherVBox/AccountSettingButton" to="." method="_on_account_setting_button_pressed"] +[connection signal="pressed" from="UI/GUI/OtherVBox/OnlineGiftButton" to="." method="_on_online_gift_button_pressed"] +[connection signal="pressed" from="UI/GUI/OtherVBox/NewPlayerGiftButton" to="." method="_on_new_player_gift_button_pressed"] +[connection signal="pressed" from="UI/GUI/OtherVBox/OneClickScreenShot" to="." method="_on_one_click_screen_shot_pressed"] +[connection signal="pressed" from="UI/GUI/OtherVBox/LuckyDrawButton" to="." method="_on_lucky_draw_button_pressed"] +[connection signal="pressed" from="UI/GUI/OtherVBox/PlayerRankingButton" to="." method="_on_player_ranking_button_pressed"] +[connection signal="pressed" from="UI/GUI/OtherVBox/DailyCheckInButton" to="." method="_on_daily_check_in_button_pressed"] +[connection signal="pressed" from="UI/GUI/OtherVBox/ReturnMainMenuButton" to="." method="_on_return_main_menu_button_pressed"] +[connection signal="pressed" from="UI/GUI/OtherVBox/SmallGameButton" to="." method="_on_online_gift_button_pressed"] +[connection signal="pressed" from="UI/GUI/OtherVBox/SettingButton" to="." method="_on_setting_button_pressed"] +[connection signal="pressed" from="UI/GUI/OtherVBox/WisdomTreeButton" to="." method="_on_setting_button_pressed"] +[connection signal="pressed" from="UI/GUI/OtherVBox/PetBagButton" to="." method="_on_pet_bag_button_pressed"] +[connection signal="pressed" from="UI/GUI/OtherVBox/PetStoreButton" to="." method="_on_pet_store_button_pressed"] +[connection signal="pressed" from="UI/GUI/OtherVBox/ScareCrowButton" to="." method="_on_my_pet_button_pressed"] +[connection signal="pressed" from="UI/SmallPanel/DebugPanel/QuitButton" to="UI/SmallPanel/DebugPanel" method="_on_quit_button_pressed"] +[connection signal="pressed" from="Decoration/ScareCrow" to="." method="_on_scare_crow_pressed"] +[connection signal="pressed" from="Decoration/PlayerStore" to="." method="_on_player_store_pressed"] +[connection signal="pressed" from="Decoration/SeedStore" to="." method="_on_seed_store_pressed"] +[connection signal="pressed" from="Decoration/ItemStore" to="." method="_on_item_store_pressed"] +[connection signal="pressed" from="Decoration/PetStore" to="." method="_on_pet_store_pressed"] +[connection signal="pressed" from="Decoration/CropWarehouse" to="." method="_on_crop_warehouse_pressed"] +[connection signal="pressed" from="Decoration/SeedWarehouse" to="." method="_on_seed_warehouse_pressed"] +[connection signal="pressed" from="Decoration/PlayerRank" to="." method="_on_player_rank_pressed"] +[connection signal="pressed" from="Decoration/WisdomTree" to="." method="_on_wisdom_tree_pressed"] +[connection signal="pressed" from="Decoration4/DailyCheckinGift" to="." method="_on_daily_checkin_gift_pressed"] +[connection signal="pressed" from="Decoration4/OnlineTimeGift" to="." method="_on_online_time_gift_pressed"] +[connection signal="pressed" from="Decoration4/TodayDivination" to="." method="_on_today_divination_pressed"] +[connection signal="pressed" from="Decoration4/PetLadderMatch" to="." method="_on_pet_ladder_match_pressed"] +[connection signal="pressed" from="Decoration4/PetNest" to="." method="_on_pet_nest_pressed"] +[connection signal="pressed" from="Decoration4/PlaySmallGame" to="." method="_on_play_small_game_pressed"] +[connection signal="pressed" from="Decoration4/MysteryFarmland" to="." method="_on_mystery_farmland_pressed"] +[connection signal="pressed" from="Decoration4/PetBag" to="." method="_on_pet_bag_pressed"] +[connection signal="pressed" from="Decoration4/ItemBag" to="." method="_on_item_bag_pressed"] diff --git a/Network/TCPNetworkManager.gd b/Network/TCPNetworkManager.gd index 7ecdc70..0255918 100644 --- a/Network/TCPNetworkManager.gd +++ b/Network/TCPNetworkManager.gd @@ -219,6 +219,16 @@ func _on_data_received(data): var player_data = data.get("player_data", {}) var remaining_likes = data.get("remaining_likes", 10) + # 处理被禁止登录的情况 + if status == "banned": + var ban_end_time = data.get("ban_end_time", "") + var ban_message = message + if ban_end_time != "": + ban_message += "\n禁止登录至: " + ban_end_time + Toast.show(ban_message, Color.RED) + login_panel._on_login_response_received(false, ban_message, {}) + return + # 在登录成功时显示剩余点赞次数并更新主游戏数据 if status == "success": var likes_message = "今日剩余点赞次数:" + str(remaining_likes) @@ -578,6 +588,8 @@ func _on_data_received(data): main_game._handle_pet_config_response(data) # 同时传递给宠物商店面板 pet_store_panel._on_pet_config_received(data) + elif message_type == "game_tips_config_response": + main_game._handle_game_tips_config_response(data) elif message_type == "visit_player_response": main_game._handle_visit_player_response(data) elif message_type == "return_my_farm_response": @@ -666,14 +678,52 @@ func _on_data_received(data): var divination_panel = get_node_or_null("/root/main/UI/SmallPanel/TodayDivinationPanel") if divination_panel and divination_panel.has_method("handle_divination_response"): divination_panel.handle_divination_response(success, message, divination_data) + + # 送金币响应 + elif message_type == "give_money_response": + main_game._handle_give_money_response(data) + + # 收到金币通知 + elif message_type == "money_received_notification": + main_game._handle_money_received_notification(data) + + # 背包数据同步响应 + elif message_type == "sync_bag_data_response": + var success = data.get("success", false) + var message = data.get("message", "") + var bag_data = data.get("bag_data", {}) + + if success: + # 更新主游戏中的背包数据 + main_game.item_bag = bag_data.get("道具背包", []) + main_game.pet_bag = bag_data.get("宠物背包", []) + main_game.player_bag = bag_data.get("种子仓库", []) + main_game.crop_warehouse = bag_data.get("作物仓库", []) + + # 更新所有背包面板的UI + main_game.item_bag_panel.update_item_bag_ui() + main_game.pet_bag_panel.update_pet_bag_ui() + main_game.player_bag_panel.update_player_bag_ui() + main_game.crop_warehouse_panel.update_crop_warehouse_ui() + + print("背包数据同步成功") + else: + print("背包数据同步失败: ", message) + + # 踢出通知 + elif message_type == "kick_notification": + var reason = data.get("reason", "您已被管理员踢出服务器") + var duration = data.get("duration", 0) + + # 显示踢出原因 + Toast.show(reason, Color.RED) + + # 断开连接并返回登录界面 + client.disconnect_from_server() + main_game.return_to_login() # ============================= 客户端与服务端通信核心 ===================================== - - - - - #=====================================网络操作处理========================================= func _on_connection_button_pressed(): if client.is_client_connected(): @@ -743,6 +793,17 @@ func sendRegisterInfo(username, password, farmname, player_name="", verification "client_version": main_game.client_version }) +#发送背包数据同步请求 +func send_sync_bag_data_request(): + if not client.is_client_connected(): + print("网络未连接,无法同步背包数据") + return + + client.send_data({ + "type": "sync_bag_data" + }) + print("已发送背包数据同步请求") + #发送收获作物信息 func sendHarvestCrop(lot_index, target_username = ""): if not client.is_client_connected(): @@ -1004,6 +1065,16 @@ func sendGetPetConfig(): }) return true +#发送获取游戏小提示配置数据请求 +func sendGetGameTipsConfig(): + if not client.is_client_connected(): + return false + + client.send_data({ + "type": "request_game_tips_config" + }) + return true + #发送访问玩家请求 func sendVisitPlayer(target_username): if not client.is_client_connected(): @@ -1262,6 +1333,19 @@ func sendDivinationRequest(): }) return true +#发送送金币请求 +func sendGiveMoney(target_username: String, amount: int): + if not client.is_client_connected(): + return false + + client.send_data({ + "type": "give_money", + "target_username": target_username, + "amount": amount, + "timestamp": Time.get_unix_time_from_system() + }) + return true + #检查是否连接到服务器 func is_connected_to_server(): return client.is_client_connected() diff --git a/Scene/BigPanel/ItemBagPanel.tscn b/Scene/BigPanel/ItemBagPanel.tscn index 1e275c8..755be6a 100644 --- a/Scene/BigPanel/ItemBagPanel.tscn +++ b/Scene/BigPanel/ItemBagPanel.tscn @@ -85,5 +85,45 @@ offset_bottom = 79.25 theme_override_font_sizes/font_size = 40 text = "刷新" +[node name="All_Filter" type="Button" parent="."] +custom_minimum_size = Vector2(60, 60) +offset_left = 217.5 +offset_top = 12.5 +offset_right = 305.5 +offset_bottom = 75.5 +theme_override_font_sizes/font_size = 40 +text = "全部" + +[node name="Pet_Filter" type="Button" parent="."] +custom_minimum_size = Vector2(60, 60) +offset_left = 310.0 +offset_top = 13.75 +offset_right = 398.0 +offset_bottom = 76.75 +theme_override_font_sizes/font_size = 40 +text = "宠物" + +[node name="Crop_Filter" type="Button" parent="."] +custom_minimum_size = Vector2(60, 60) +offset_left = 397.5 +offset_top = 13.75 +offset_right = 485.5 +offset_bottom = 76.75 +theme_override_font_sizes/font_size = 40 +text = "作物" + +[node name="Farm_Filter" type="Button" parent="."] +custom_minimum_size = Vector2(60, 60) +offset_left = 485.0 +offset_top = 13.75 +offset_right = 573.0 +offset_bottom = 76.75 +theme_override_font_sizes/font_size = 40 +text = "农场" + [connection signal="pressed" from="QuitButton" to="." method="_on_quit_button_pressed"] [connection signal="pressed" from="RefreshButton" to="." method="_on_refresh_button_pressed"] +[connection signal="pressed" from="All_Filter" to="." method="_on_all_filter_pressed"] +[connection signal="pressed" from="Pet_Filter" to="." method="_on_pet_filter_pressed"] +[connection signal="pressed" from="Crop_Filter" to="." method="_on_crop_filter_pressed"] +[connection signal="pressed" from="Farm_Filter" to="." method="_on_farm_filter_pressed"] diff --git a/Scene/BigPanel/ItemStorePanel.tscn b/Scene/BigPanel/ItemStorePanel.tscn index 47781bf..8c7853d 100644 --- a/Scene/BigPanel/ItemStorePanel.tscn +++ b/Scene/BigPanel/ItemStorePanel.tscn @@ -15,7 +15,6 @@ corner_detail = 20 shadow_size = 20 [node name="ItemStorePanel" type="Panel"] -visible = false offset_left = 58.0 offset_top = 79.0 offset_right = 1598.0 @@ -87,4 +86,48 @@ offset_bottom = 75.5 theme_override_font_sizes/font_size = 40 text = "刷新" +[node name="All_Filter" type="Button" parent="."] +custom_minimum_size = Vector2(60, 60) +layout_mode = 2 +offset_left = 217.5 +offset_top = 12.5 +offset_right = 305.5 +offset_bottom = 75.5 +theme_override_font_sizes/font_size = 40 +text = "全部" + +[node name="Pet_Filter" type="Button" parent="."] +custom_minimum_size = Vector2(60, 60) +layout_mode = 2 +offset_left = 310.0 +offset_top = 13.75 +offset_right = 398.0 +offset_bottom = 76.75 +theme_override_font_sizes/font_size = 40 +text = "宠物" + +[node name="Crop_Filter" type="Button" parent="."] +custom_minimum_size = Vector2(60, 60) +layout_mode = 2 +offset_left = 397.5 +offset_top = 13.75 +offset_right = 485.5 +offset_bottom = 76.75 +theme_override_font_sizes/font_size = 40 +text = "作物" + +[node name="Farm_Filter" type="Button" parent="."] +custom_minimum_size = Vector2(60, 60) +layout_mode = 2 +offset_left = 485.0 +offset_top = 13.75 +offset_right = 573.0 +offset_bottom = 76.75 +theme_override_font_sizes/font_size = 40 +text = "农场" + [connection signal="pressed" from="RefreshButton" to="." method="_on_refresh_button_pressed"] +[connection signal="pressed" from="All_Filter" to="." method="_on_all_filter_pressed"] +[connection signal="pressed" from="Pet_Filter" to="." method="_on_pet_filter_pressed"] +[connection signal="pressed" from="Crop_Filter" to="." method="_on_crop_filter_pressed"] +[connection signal="pressed" from="Farm_Filter" to="." method="_on_farm_filter_pressed"] diff --git a/Scene/BigPanel/PlayGamePanel.tscn b/Scene/BigPanel/PlayGamePanel.tscn new file mode 100644 index 0000000..c52ad9f --- /dev/null +++ b/Scene/BigPanel/PlayGamePanel.tscn @@ -0,0 +1,179 @@ +[gd_scene load_steps=4 format=3 uid="uid://jjlgdnoo8e52"] + +[ext_resource type="Script" uid="uid://dvaah0n1ia1a3" path="res://Script/BigPanel/PlayGamePanel.gd" id="1_i6cj7"] +[ext_resource type="Texture2D" uid="uid://ywdg7xgq7hm8" path="res://assets/装饰物图片/道具背包.webp" id="2_wlugc"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_7i23t"] +border_width_left = 15 +border_width_top = 15 +border_width_right = 15 +border_width_bottom = 15 +corner_detail = 20 +shadow_size = 20 + +[node name="PlayGamePanel" type="Panel"] +offset_right = 1399.0 +offset_bottom = 720.0 +theme_override_styles/panel = SubResource("StyleBoxFlat_7i23t") +script = ExtResource("1_i6cj7") + +[node name="Title" type="Label" parent="."] +layout_mode = 0 +offset_top = 17.0 +offset_right = 1401.0 +offset_bottom = 86.0 +theme_override_colors/font_color = Color(0.560784, 1, 0, 1) +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/shadow_offset_x = 3 +theme_override_constants/shadow_offset_y = 3 +theme_override_constants/outline_size = 15 +theme_override_constants/shadow_outline_size = 20 +theme_override_font_sizes/font_size = 50 +text = "玩玩小游戏" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="ScrollContainer" type="ScrollContainer" parent="."] +layout_mode = 0 +offset_left = 15.0 +offset_top = 88.0 +offset_right = 1384.0 +offset_bottom = 707.0 +horizontal_scroll_mode = 0 + +[node name="Grid" type="GridContainer" parent="ScrollContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +columns = 5 + +[node name="VBox" type="VBoxContainer" parent="ScrollContainer/Grid"] +layout_mode = 2 +alignment = 1 + +[node name="GameTitle" type="Label" parent="ScrollContainer/Grid/VBox"] +layout_mode = 2 +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/shadow_offset_x = 3 +theme_override_constants/shadow_offset_y = 3 +theme_override_constants/outline_size = 15 +theme_override_constants/shadow_outline_size = 15 +theme_override_font_sizes/font_size = 35 +text = "贪吃蛇" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="TextureRect" type="TextureRect" parent="ScrollContainer/Grid/VBox"] +layout_mode = 2 +texture = ExtResource("2_wlugc") + +[node name="Button" type="Button" parent="ScrollContainer/Grid/VBox"] +layout_mode = 2 +theme_override_font_sizes/font_size = 30 +text = "点击游玩" + +[node name="VBox2" type="VBoxContainer" parent="ScrollContainer/Grid"] +layout_mode = 2 +alignment = 1 + +[node name="GameTitle" type="Label" parent="ScrollContainer/Grid/VBox2"] +layout_mode = 2 +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/shadow_offset_x = 3 +theme_override_constants/shadow_offset_y = 3 +theme_override_constants/outline_size = 15 +theme_override_constants/shadow_outline_size = 15 +theme_override_font_sizes/font_size = 35 +text = "贪吃蛇" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="TextureRect" type="TextureRect" parent="ScrollContainer/Grid/VBox2"] +layout_mode = 2 +texture = ExtResource("2_wlugc") + +[node name="Button" type="Button" parent="ScrollContainer/Grid/VBox2"] +layout_mode = 2 +theme_override_font_sizes/font_size = 30 +text = "点击游玩" + +[node name="VBox3" type="VBoxContainer" parent="ScrollContainer/Grid"] +layout_mode = 2 +alignment = 1 + +[node name="GameTitle" type="Label" parent="ScrollContainer/Grid/VBox3"] +layout_mode = 2 +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/shadow_offset_x = 3 +theme_override_constants/shadow_offset_y = 3 +theme_override_constants/outline_size = 15 +theme_override_constants/shadow_outline_size = 15 +theme_override_font_sizes/font_size = 35 +text = "贪吃蛇" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="TextureRect" type="TextureRect" parent="ScrollContainer/Grid/VBox3"] +layout_mode = 2 +texture = ExtResource("2_wlugc") + +[node name="Button" type="Button" parent="ScrollContainer/Grid/VBox3"] +layout_mode = 2 +theme_override_font_sizes/font_size = 30 +text = "点击游玩" + +[node name="VBox4" type="VBoxContainer" parent="ScrollContainer/Grid"] +layout_mode = 2 +alignment = 1 + +[node name="GameTitle" type="Label" parent="ScrollContainer/Grid/VBox4"] +layout_mode = 2 +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/shadow_offset_x = 3 +theme_override_constants/shadow_offset_y = 3 +theme_override_constants/outline_size = 15 +theme_override_constants/shadow_outline_size = 15 +theme_override_font_sizes/font_size = 35 +text = "贪吃蛇" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="TextureRect" type="TextureRect" parent="ScrollContainer/Grid/VBox4"] +layout_mode = 2 +texture = ExtResource("2_wlugc") + +[node name="Button" type="Button" parent="ScrollContainer/Grid/VBox4"] +layout_mode = 2 +theme_override_font_sizes/font_size = 30 +text = "点击游玩" + +[node name="VBox5" type="VBoxContainer" parent="ScrollContainer/Grid"] +layout_mode = 2 +alignment = 1 + +[node name="GameTitle" type="Label" parent="ScrollContainer/Grid/VBox5"] +layout_mode = 2 +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/shadow_offset_x = 3 +theme_override_constants/shadow_offset_y = 3 +theme_override_constants/outline_size = 15 +theme_override_constants/shadow_outline_size = 15 +theme_override_font_sizes/font_size = 35 +text = "贪吃蛇" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="TextureRect" type="TextureRect" parent="ScrollContainer/Grid/VBox5"] +layout_mode = 2 +texture = ExtResource("2_wlugc") + +[node name="Button" type="Button" parent="ScrollContainer/Grid/VBox5"] +layout_mode = 2 +theme_override_font_sizes/font_size = 30 +text = "点击游玩" diff --git a/Scene/BigPanel/SpecialFarmPanel.tscn b/Scene/BigPanel/SpecialFarmPanel.tscn new file mode 100644 index 0000000..42e972d --- /dev/null +++ b/Scene/BigPanel/SpecialFarmPanel.tscn @@ -0,0 +1,223 @@ +[gd_scene load_steps=4 format=3 uid="uid://byxhjyyaahs6q"] + +[ext_resource type="Texture2D" uid="uid://ywdg7xgq7hm8" path="res://assets/装饰物图片/道具背包.webp" id="1_mv0g4"] +[ext_resource type="Script" uid="uid://btm2je8pg7rgk" path="res://Script/BigPanel/SpecialFarmPanel.gd" id="1_n2mxy"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_7i23t"] +border_width_left = 15 +border_width_top = 15 +border_width_right = 15 +border_width_bottom = 15 +corner_detail = 20 +shadow_size = 20 + +[node name="SpecialFarmPanel" type="Panel"] +offset_right = 1399.0 +offset_bottom = 720.0 +theme_override_styles/panel = SubResource("StyleBoxFlat_7i23t") +script = ExtResource("1_n2mxy") + +[node name="QuitButton" type="Button" parent="."] +custom_minimum_size = Vector2(80, 80) +layout_mode = 0 +offset_left = 1303.0 +offset_top = 17.0 +offset_right = 1383.0 +offset_bottom = 97.0 +theme_override_font_sizes/font_size = 50 +text = "X" + +[node name="Title" type="Label" parent="."] +layout_mode = 0 +offset_top = 17.0 +offset_right = 1401.0 +offset_bottom = 86.0 +theme_override_colors/font_color = Color(0.560784, 1, 0, 1) +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/shadow_offset_x = 3 +theme_override_constants/shadow_offset_y = 3 +theme_override_constants/outline_size = 15 +theme_override_constants/shadow_outline_size = 20 +theme_override_font_sizes/font_size = 50 +text = "神秘农场" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="ScrollContainer" type="ScrollContainer" parent="."] +layout_mode = 0 +offset_left = 15.0 +offset_top = 88.0 +offset_right = 1384.0 +offset_bottom = 707.0 +horizontal_scroll_mode = 0 + +[node name="Grid" type="GridContainer" parent="ScrollContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +columns = 5 + +[node name="FlowerFarm" type="VBoxContainer" parent="ScrollContainer/Grid"] +layout_mode = 2 +alignment = 1 + +[node name="GameTitle" type="Label" parent="ScrollContainer/Grid/FlowerFarm"] +layout_mode = 2 +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/shadow_offset_x = 3 +theme_override_constants/shadow_offset_y = 3 +theme_override_constants/outline_size = 15 +theme_override_constants/shadow_outline_size = 15 +theme_override_font_sizes/font_size = 35 +text = "花卉农场" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="TextureRect" type="TextureRect" parent="ScrollContainer/Grid/FlowerFarm"] +layout_mode = 2 +texture = ExtResource("1_mv0g4") + +[node name="FlowerFarmButton" type="Button" parent="ScrollContainer/Grid/FlowerFarm"] +layout_mode = 2 +theme_override_font_sizes/font_size = 30 +text = "点击访问" + +[node name="FruitFarm" type="VBoxContainer" parent="ScrollContainer/Grid"] +layout_mode = 2 +alignment = 1 + +[node name="GameTitle" type="Label" parent="ScrollContainer/Grid/FruitFarm"] +layout_mode = 2 +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/shadow_offset_x = 3 +theme_override_constants/shadow_offset_y = 3 +theme_override_constants/outline_size = 15 +theme_override_constants/shadow_outline_size = 15 +theme_override_font_sizes/font_size = 35 +text = "瓜果飘香" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="TextureRect" type="TextureRect" parent="ScrollContainer/Grid/FruitFarm"] +layout_mode = 2 +texture = ExtResource("1_mv0g4") + +[node name="FruitFarmButton" type="Button" parent="ScrollContainer/Grid/FruitFarm"] +layout_mode = 2 +theme_override_font_sizes/font_size = 30 +text = "点击访问" + +[node name="HybridFarm" type="VBoxContainer" parent="ScrollContainer/Grid"] +layout_mode = 2 +alignment = 1 + +[node name="GameTitle" type="Label" parent="ScrollContainer/Grid/HybridFarm"] +layout_mode = 2 +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/shadow_offset_x = 3 +theme_override_constants/shadow_offset_y = 3 +theme_override_constants/outline_size = 15 +theme_override_constants/shadow_outline_size = 15 +theme_override_font_sizes/font_size = 35 +text = "杂交基地" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="TextureRect" type="TextureRect" parent="ScrollContainer/Grid/HybridFarm"] +layout_mode = 2 +texture = ExtResource("1_mv0g4") + +[node name="HybridFarmButton" type="Button" parent="ScrollContainer/Grid/HybridFarm"] +layout_mode = 2 +theme_override_font_sizes/font_size = 30 +text = "点击访问" + +[node name="LuckyFarm" type="VBoxContainer" parent="ScrollContainer/Grid"] +layout_mode = 2 +alignment = 1 + +[node name="GameTitle" type="Label" parent="ScrollContainer/Grid/LuckyFarm"] +layout_mode = 2 +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/shadow_offset_x = 3 +theme_override_constants/shadow_offset_y = 3 +theme_override_constants/outline_size = 15 +theme_override_constants/shadow_outline_size = 15 +theme_override_font_sizes/font_size = 35 +text = "幸运农场" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="TextureRect" type="TextureRect" parent="ScrollContainer/Grid/LuckyFarm"] +layout_mode = 2 +texture = ExtResource("1_mv0g4") + +[node name="LuckyFarmButton" type="Button" parent="ScrollContainer/Grid/LuckyFarm"] +layout_mode = 2 +theme_override_font_sizes/font_size = 30 +text = "点击访问" + +[node name="RiceFarm" type="VBoxContainer" parent="ScrollContainer/Grid"] +layout_mode = 2 +alignment = 1 + +[node name="GameTitle" type="Label" parent="ScrollContainer/Grid/RiceFarm"] +layout_mode = 2 +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/shadow_offset_x = 3 +theme_override_constants/shadow_offset_y = 3 +theme_override_constants/outline_size = 15 +theme_override_constants/shadow_outline_size = 15 +theme_override_font_sizes/font_size = 35 +text = "稻香" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="TextureRect" type="TextureRect" parent="ScrollContainer/Grid/RiceFarm"] +layout_mode = 2 +texture = ExtResource("1_mv0g4") + +[node name="RiceFarmButton" type="Button" parent="ScrollContainer/Grid/RiceFarm"] +layout_mode = 2 +theme_override_font_sizes/font_size = 30 +text = "点击访问" + +[node name="WheatFarm" type="VBoxContainer" parent="ScrollContainer/Grid"] +layout_mode = 2 +alignment = 1 + +[node name="GameTitle" type="Label" parent="ScrollContainer/Grid/WheatFarm"] +layout_mode = 2 +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/shadow_offset_x = 3 +theme_override_constants/shadow_offset_y = 3 +theme_override_constants/outline_size = 15 +theme_override_constants/shadow_outline_size = 15 +theme_override_font_sizes/font_size = 35 +text = "小麦谷" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="TextureRect" type="TextureRect" parent="ScrollContainer/Grid/WheatFarm"] +layout_mode = 2 +texture = ExtResource("1_mv0g4") + +[node name="WheatFarmButton" type="Button" parent="ScrollContainer/Grid/WheatFarm"] +layout_mode = 2 +theme_override_font_sizes/font_size = 30 +text = "点击访问" + +[connection signal="pressed" from="QuitButton" to="." method="_on_quit_button_pressed"] +[connection signal="pressed" from="ScrollContainer/Grid/FlowerFarm/FlowerFarmButton" to="." method="_on_flower_farm_button_pressed"] +[connection signal="pressed" from="ScrollContainer/Grid/FruitFarm/FruitFarmButton" to="." method="_on_fruit_farm_button_pressed"] +[connection signal="pressed" from="ScrollContainer/Grid/HybridFarm/HybridFarmButton" to="." method="_on_hybrid_farm_button_pressed"] +[connection signal="pressed" from="ScrollContainer/Grid/LuckyFarm/LuckyFarmButton" to="." method="_on_lucky_farm_button_pressed"] +[connection signal="pressed" from="ScrollContainer/Grid/RiceFarm/RiceFarmButton" to="." method="_on_rice_farm_button_pressed"] +[connection signal="pressed" from="ScrollContainer/Grid/WheatFarm/WheatFarmButton" to="." method="_on_wheat_farm_button_pressed"] diff --git a/Scene/SmallGame/2048Game.gd b/Scene/SmallGame/2048Game.gd new file mode 100644 index 0000000..26f0a5b --- /dev/null +++ b/Scene/SmallGame/2048Game.gd @@ -0,0 +1,307 @@ +extends Panel + +# 游戏常量 +const GRID_SIZE = 4 +const CELL_SIZE = 90 +const CELL_MARGIN = 10 + +# 数字颜色配置 +const NUMBER_COLORS = { + 0: Color.TRANSPARENT, + 2: Color(0.93, 0.89, 0.85), + 4: Color(0.93, 0.88, 0.78), + 8: Color(0.95, 0.69, 0.47), + 16: Color(0.96, 0.58, 0.39), + 32: Color(0.96, 0.49, 0.37), + 64: Color(0.96, 0.37, 0.23), + 128: Color(0.93, 0.81, 0.45), + 256: Color(0.93, 0.80, 0.38), + 512: Color(0.93, 0.78, 0.31), + 1024: Color(0.93, 0.77, 0.25), + 2048: Color(0.93, 0.76, 0.18) +} + +const TEXT_COLORS = { + 2: Color.BLACK, + 4: Color.BLACK, + 8: Color.WHITE, + 16: Color.WHITE, + 32: Color.WHITE, + 64: Color.WHITE, + 128: Color.WHITE, + 256: Color.WHITE, + 512: Color.WHITE, + 1024: Color.WHITE, + 2048: Color.WHITE +} + +# 游戏变量 +var grid = [] +var score = 0 +var best_score = 0 +var game_over = false +var won = false +var can_continue = true + +# 节点引用 +@onready var game_board = $GameBoard +@onready var score_label = $ScoreLabel +@onready var best_label = $BestLabel +@onready var game_over_label = $GameOverLabel +@onready var win_label = $WinLabel + +func _ready(): + # 设置游戏板样式 + game_board.modulate = Color(0.7, 0.6, 0.5) + + # 初始化游戏 + init_game() + +func init_game(): + # 重置游戏状态 + game_over = false + won = false + can_continue = true + score = 0 + + # 初始化网格 + grid.clear() + for y in range(GRID_SIZE): + var row = [] + for x in range(GRID_SIZE): + row.append(0) + grid.append(row) + + # 添加两个初始数字 + add_random_number() + add_random_number() + + # 更新UI + update_ui() + hide_labels() + + queue_redraw() + +func _input(event): + if event is InputEventKey and event.pressed: + if game_over: + if event.keycode == KEY_R: + init_game() + return + + if won and not can_continue: + if event.keycode == KEY_C: + can_continue = true + win_label.visible = false + return + + # 移动控制 + var moved = false + match event.keycode: + KEY_UP, KEY_W: + moved = move_up() + KEY_DOWN, KEY_S: + moved = move_down() + KEY_LEFT, KEY_A: + moved = move_left() + KEY_RIGHT, KEY_D: + moved = move_right() + KEY_R: + init_game() + return + + if moved: + add_random_number() + update_ui() + check_game_state() + queue_redraw() + +func move_left() -> bool: + var moved = false + for y in range(GRID_SIZE): + var row = grid[y].duplicate() + var new_row = merge_line(row) + if new_row != grid[y]: + moved = true + grid[y] = new_row + return moved + +func move_right() -> bool: + var moved = false + for y in range(GRID_SIZE): + var row = grid[y].duplicate() + row.reverse() + var new_row = merge_line(row) + new_row.reverse() + if new_row != grid[y]: + moved = true + grid[y] = new_row + return moved + +func move_up() -> bool: + var moved = false + for x in range(GRID_SIZE): + var column = [] + for y in range(GRID_SIZE): + column.append(grid[y][x]) + + var new_column = merge_line(column) + var changed = false + for y in range(GRID_SIZE): + if grid[y][x] != new_column[y]: + changed = true + grid[y][x] = new_column[y] + + if changed: + moved = true + return moved + +func move_down() -> bool: + var moved = false + for x in range(GRID_SIZE): + var column = [] + for y in range(GRID_SIZE): + column.append(grid[y][x]) + + column.reverse() + var new_column = merge_line(column) + new_column.reverse() + + var changed = false + for y in range(GRID_SIZE): + if grid[y][x] != new_column[y]: + changed = true + grid[y][x] = new_column[y] + + if changed: + moved = true + return moved + +func merge_line(line: Array) -> Array: + # 移除零 + var filtered = [] + for num in line: + if num != 0: + filtered.append(num) + + # 合并相同数字 + var merged = [] + var i = 0 + while i < filtered.size(): + if i < filtered.size() - 1 and filtered[i] == filtered[i + 1]: + # 合并 + var new_value = filtered[i] * 2 + merged.append(new_value) + score += new_value + i += 2 + else: + merged.append(filtered[i]) + i += 1 + + # 填充零到指定长度 + while merged.size() < GRID_SIZE: + merged.append(0) + + return merged + +func add_random_number(): + var empty_cells = [] + for y in range(GRID_SIZE): + for x in range(GRID_SIZE): + if grid[y][x] == 0: + empty_cells.append(Vector2(x, y)) + + if empty_cells.size() > 0: + var random_cell = empty_cells[randi() % empty_cells.size()] + var value = 2 if randf() < 0.9 else 4 + grid[random_cell.y][random_cell.x] = value + +func check_game_state(): + # 检查是否达到2048 + if not won: + for y in range(GRID_SIZE): + for x in range(GRID_SIZE): + if grid[y][x] == 2048: + won = true + can_continue = false + win_label.visible = true + return + + # 检查是否游戏结束 + if not can_move(): + game_over = true + if score > best_score: + best_score = score + game_over_label.visible = true + +func can_move() -> bool: + # 检查是否有空格 + for y in range(GRID_SIZE): + for x in range(GRID_SIZE): + if grid[y][x] == 0: + return true + + # 检查是否可以合并 + for y in range(GRID_SIZE): + for x in range(GRID_SIZE): + var current = grid[y][x] + # 检查右边 + if x < GRID_SIZE - 1 and grid[y][x + 1] == current: + return true + # 检查下面 + if y < GRID_SIZE - 1 and grid[y + 1][x] == current: + return true + + return false + +func update_ui(): + score_label.text = "分数: " + str(score) + best_label.text = "最高分: " + str(best_score) + +func hide_labels(): + game_over_label.visible = false + win_label.visible = false + +func _draw(): + if not game_board: + return + + # 获取游戏板位置 + var board_pos = game_board.position + + # 绘制网格背景 + for y in range(GRID_SIZE): + for x in range(GRID_SIZE): + var cell_x = board_pos.x + x * (CELL_SIZE + CELL_MARGIN) + CELL_MARGIN + var cell_y = board_pos.y + y * (CELL_SIZE + CELL_MARGIN) + CELL_MARGIN + var rect = Rect2(cell_x, cell_y, CELL_SIZE, CELL_SIZE) + + # 绘制单元格背景 + draw_rect(rect, Color(0.8, 0.7, 0.6), true) + + # 绘制数字 + var value = grid[y][x] + if value > 0: + # 绘制数字背景 + var bg_color = NUMBER_COLORS.get(value, Color.GOLD) + draw_rect(rect, bg_color, true) + + # 绘制数字文本 + var text = str(value) + var font_size = 24 if value < 100 else (20 if value < 1000 else 16) + var text_color = TEXT_COLORS.get(value, Color.WHITE) + + # 获取默认字体 + var font = ThemeDB.fallback_font + + # 计算文本尺寸 + var text_size = font.get_string_size(text, HORIZONTAL_ALIGNMENT_CENTER, -1, font_size) + + # 计算文本位置(居中) + var text_pos = Vector2( + cell_x + (CELL_SIZE - text_size.x) / 2, + cell_y + (CELL_SIZE - text_size.y) / 2 + text_size.y + ) + + # 绘制文本 + draw_string(font, text_pos, text, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, text_color) diff --git a/Scene/SmallGame/2048Game.gd.uid b/Scene/SmallGame/2048Game.gd.uid new file mode 100644 index 0000000..3dc1289 --- /dev/null +++ b/Scene/SmallGame/2048Game.gd.uid @@ -0,0 +1 @@ +uid://5dkxyuiuuhxk diff --git a/Scene/SmallGame/2048Game.tscn b/Scene/SmallGame/2048Game.tscn new file mode 100644 index 0000000..2188250 --- /dev/null +++ b/Scene/SmallGame/2048Game.tscn @@ -0,0 +1,102 @@ +[gd_scene load_steps=2 format=3 uid="uid://dn0f1hjolxori"] + +[ext_resource type="Script" uid="uid://5dkxyuiuuhxk" path="res://Scene/SmallGame/2048Game.gd" id="1_4d5vb"] + +[node name="2048Game" type="Panel"] +offset_right = 1400.0 +offset_bottom = 720.0 +script = ExtResource("1_4d5vb") + +[node name="GameBoard" type="Panel" parent="."] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -200.0 +offset_top = -200.0 +offset_right = 200.0 +offset_bottom = 200.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="ScoreLabel" type="Label" parent="."] +layout_mode = 1 +anchors_preset = 2 +anchor_top = 1.0 +anchor_bottom = 1.0 +offset_left = 50.0 +offset_top = -100.0 +offset_right = 250.0 +offset_bottom = -70.0 +grow_vertical = 0 +text = "分数: 0" + +[node name="BestLabel" type="Label" parent="."] +layout_mode = 1 +anchors_preset = 2 +anchor_top = 1.0 +anchor_bottom = 1.0 +offset_left = 50.0 +offset_top = -70.0 +offset_right = 250.0 +offset_bottom = -40.0 +grow_vertical = 0 +text = "最高分: 0" + +[node name="GameOverLabel" type="Label" parent="."] +visible = false +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -150.0 +offset_top = -50.0 +offset_right = 150.0 +offset_bottom = 50.0 +grow_horizontal = 2 +grow_vertical = 2 +text = "游戏结束 +按R重新开始" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="WinLabel" type="Label" parent="."] +visible = false +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -150.0 +offset_top = -100.0 +offset_right = 150.0 +offset_bottom = -50.0 +grow_horizontal = 2 +grow_vertical = 2 +text = "恭喜!达到2048! +按C继续游戏" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="ControlsLabel" type="Label" parent="."] +layout_mode = 1 +anchors_preset = 3 +anchor_left = 1.0 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = -300.0 +offset_top = -150.0 +offset_right = -50.0 +offset_bottom = -50.0 +grow_horizontal = 0 +grow_vertical = 0 +text = "操作说明: +WASD或方向键 - 移动 +R - 重新开始 +C - 继续游戏(达到2048后)" diff --git a/Scene/SmallGame/PushBox.gd b/Scene/SmallGame/PushBox.gd new file mode 100644 index 0000000..8f72743 --- /dev/null +++ b/Scene/SmallGame/PushBox.gd @@ -0,0 +1,317 @@ +extends Panel + +# 游戏常量 +const CELL_SIZE = 40 + +# 地图元素 +enum CellType { + EMPTY, # 空地 + WALL, # 墙壁 + TARGET, # 目标点 + BOX, # 箱子 + PLAYER, # 玩家 + BOX_ON_TARGET # 箱子在目标点上 +} + +# 颜色配置 +const CELL_COLORS = { + CellType.EMPTY: Color.WHITE, + CellType.WALL: Color.DARK_GRAY, + CellType.TARGET: Color.LIGHT_BLUE, + CellType.BOX: Color.SADDLE_BROWN, + CellType.PLAYER: Color.GREEN, + CellType.BOX_ON_TARGET: Color.DARK_GREEN +} + +# 关卡数据 +const LEVELS = [ + # 关卡1 - 简单入门 + [ + "########", + "#......#", + "#..##..#", + "#..#*..#", + "#..#$..#", + "#..@...#", + "#......#", + "########" + ], + # 关卡2 - 两个箱子 + [ + "########", + "#......#", + "#.*$*..#", + "#......#", + "#..$...#", + "#..@...#", + "#......#", + "########" + ], + # 关卡3 - 稍微复杂 + [ + "##########", + "#........#", + "#..####..#", + "#..*##*..#", + "#..$$....#", + "#........#", + "#...@....#", + "#........#", + "##########" + ], + # 关卡4 - 更复杂的布局 + [ + "############", + "#..........#", + "#..######..#", + "#..*....#..#", + "#..$....#..#", + "#.......#..#", + "#..#....#..#", + "#..*....#..#", + "#..$......@#", + "#..........#", + "############" + ] +] + +# 游戏变量 +var current_level = 0 +var level_data = [] +var player_pos = Vector2() +var moves = 0 +var level_completed = false +var map_width = 0 +var map_height = 0 + +# 节点引用 +@onready var game_area = $GameArea +@onready var level_label = $LevelLabel +@onready var moves_label = $MovesLabel +@onready var win_label = $WinLabel + +func _ready(): + # 设置游戏区域样式 + game_area.modulate = Color(0.9, 0.9, 0.9) + + # 初始化游戏 + init_level() + +func init_level(): + # 重置游戏状态 + level_completed = false + moves = 0 + + # 加载当前关卡 + load_level(current_level) + + # 更新UI + update_ui() + win_label.visible = false + + queue_redraw() + +func load_level(level_index: int): + if level_index >= LEVELS.size(): + level_index = LEVELS.size() - 1 + + var level_strings = LEVELS[level_index] + map_height = level_strings.size() + map_width = level_strings[0].length() + + # 初始化关卡数据 + level_data.clear() + for y in range(map_height): + var row = [] + for x in range(map_width): + row.append(CellType.EMPTY) + level_data.append(row) + + # 解析关卡字符串 + for y in range(map_height): + var line = level_strings[y] + for x in range(line.length()): + var char = line[x] + match char: + '#': # 墙壁 + level_data[y][x] = CellType.WALL + '*': # 目标点 + level_data[y][x] = CellType.TARGET + '$': # 箱子 + level_data[y][x] = CellType.BOX + '@': # 玩家 + level_data[y][x] = CellType.PLAYER + player_pos = Vector2(x, y) + '+': # 箱子在目标点上 + level_data[y][x] = CellType.BOX_ON_TARGET + '.': # 空地 + level_data[y][x] = CellType.EMPTY + +func _input(event): + if event is InputEventKey and event.pressed: + if level_completed: + match event.keycode: + KEY_N: + next_level() + KEY_R: + init_level() + return + + # 移动控制 + var direction = Vector2.ZERO + match event.keycode: + KEY_UP, KEY_W: + direction = Vector2(0, -1) + KEY_DOWN, KEY_S: + direction = Vector2(0, 1) + KEY_LEFT, KEY_A: + direction = Vector2(-1, 0) + KEY_RIGHT, KEY_D: + direction = Vector2(1, 0) + KEY_R: + init_level() + return + KEY_P: + prev_level() + return + KEY_N: + next_level() + return + + if direction != Vector2.ZERO: + move_player(direction) + +func move_player(direction: Vector2): + var new_pos = player_pos + direction + + # 检查边界 + if new_pos.x < 0 or new_pos.x >= map_width or new_pos.y < 0 or new_pos.y >= map_height: + return + + var target_cell = level_data[new_pos.y][new_pos.x] + + # 检查是否撞墙 + if target_cell == CellType.WALL: + return + + # 检查是否推箱子 + if target_cell == CellType.BOX or target_cell == CellType.BOX_ON_TARGET: + var box_new_pos = new_pos + direction + + # 检查箱子新位置是否有效 + if box_new_pos.x < 0 or box_new_pos.x >= map_width or box_new_pos.y < 0 or box_new_pos.y >= map_height: + return + + var box_target_cell = level_data[box_new_pos.y][box_new_pos.x] + + # 箱子不能推到墙上或其他箱子上 + if box_target_cell == CellType.WALL or box_target_cell == CellType.BOX or box_target_cell == CellType.BOX_ON_TARGET: + return + + # 移动箱子 + var was_on_target = (target_cell == CellType.BOX_ON_TARGET) + var moving_to_target = (box_target_cell == CellType.TARGET) + + # 更新箱子原位置 + if was_on_target: + level_data[new_pos.y][new_pos.x] = CellType.TARGET + else: + level_data[new_pos.y][new_pos.x] = CellType.EMPTY + + # 更新箱子新位置 + if moving_to_target: + level_data[box_new_pos.y][box_new_pos.x] = CellType.BOX_ON_TARGET + else: + level_data[box_new_pos.y][box_new_pos.x] = CellType.BOX + + # 移动玩家 + # 恢复玩家原位置(检查是否在目标点上) + var level_strings = LEVELS[current_level] + if player_pos.y < level_strings.size() and player_pos.x < level_strings[player_pos.y].length(): + var original_char = level_strings[player_pos.y][player_pos.x] + if original_char == '*': # 玩家原来在目标点上 + level_data[player_pos.y][player_pos.x] = CellType.TARGET + else: + level_data[player_pos.y][player_pos.x] = CellType.EMPTY + else: + level_data[player_pos.y][player_pos.x] = CellType.EMPTY + + # 更新玩家位置 + player_pos = new_pos + level_data[player_pos.y][player_pos.x] = CellType.PLAYER + + # 增加步数 + moves += 1 + + # 检查是否过关 + check_win_condition() + + # 更新UI和重绘 + update_ui() + queue_redraw() + +func check_win_condition(): + # 检查是否所有箱子都在目标点上 + for y in range(map_height): + for x in range(map_width): + if level_data[y][x] == CellType.BOX: + return # 还有箱子不在目标点上 + + # 所有箱子都在目标点上,过关! + level_completed = true + win_label.visible = true + +func next_level(): + if current_level < LEVELS.size() - 1: + current_level += 1 + init_level() + +func prev_level(): + if current_level > 0: + current_level -= 1 + init_level() + +func update_ui(): + level_label.text = "关卡: " + str(current_level + 1) + moves_label.text = "步数: " + str(moves) + +func _draw(): + if not game_area: + return + + # 获取游戏区域位置 + var area_pos = game_area.position + + # 计算起始绘制位置(居中) + var start_x = area_pos.x + (game_area.size.x - map_width * CELL_SIZE) / 2 + var start_y = area_pos.y + (game_area.size.y - map_height * CELL_SIZE) / 2 + + # 绘制地图 + for y in range(map_height): + for x in range(map_width): + var cell_x = start_x + x * CELL_SIZE + var cell_y = start_y + y * CELL_SIZE + var rect = Rect2(cell_x, cell_y, CELL_SIZE, CELL_SIZE) + + var cell_type = level_data[y][x] + var color = CELL_COLORS[cell_type] + + # 绘制单元格 + draw_rect(rect, color, true) + + # 绘制边框 + draw_rect(rect, Color.BLACK, false, 1) + + # 特殊处理:如果是玩家在目标点上,需要先绘制目标点 + if cell_type == CellType.PLAYER: + # 检查玩家下面是否有目标点(通过检查原始关卡数据) + var level_strings = LEVELS[current_level] + if y < level_strings.size() and x < level_strings[y].length(): + var original_char = level_strings[y][x] + if original_char == '*': # 玩家在目标点上 + draw_rect(rect, CELL_COLORS[CellType.TARGET], true) + draw_rect(rect, Color.BLACK, false, 1) + # 再绘制玩家(半透明) + var player_color = CELL_COLORS[CellType.PLAYER] + player_color.a = 0.8 + draw_rect(rect, player_color, true) diff --git a/Scene/SmallGame/PushBox.gd.uid b/Scene/SmallGame/PushBox.gd.uid new file mode 100644 index 0000000..8205e9a --- /dev/null +++ b/Scene/SmallGame/PushBox.gd.uid @@ -0,0 +1 @@ +uid://b1t5fxyg0jjx5 diff --git a/Scene/SmallGame/PushBox.tscn b/Scene/SmallGame/PushBox.tscn new file mode 100644 index 0000000..9dc6cf3 --- /dev/null +++ b/Scene/SmallGame/PushBox.tscn @@ -0,0 +1,85 @@ +[gd_scene load_steps=2 format=3 uid="uid://cq14axh7bovu6"] + +[ext_resource type="Script" uid="uid://b1t5fxyg0jjx5" path="res://Scene/SmallGame/PushBox.gd" id="1_qdfdf"] + +[node name="PushBox" type="Panel"] +offset_right = 1402.0 +offset_bottom = 720.0 +script = ExtResource("1_qdfdf") + +[node name="GameArea" type="Panel" parent="."] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -300.0 +offset_top = -250.0 +offset_right = 300.0 +offset_bottom = 250.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="LevelLabel" type="Label" parent="."] +layout_mode = 1 +anchors_preset = 2 +anchor_top = 1.0 +anchor_bottom = 1.0 +offset_left = 50.0 +offset_top = -100.0 +offset_right = 200.0 +offset_bottom = -70.0 +grow_vertical = 0 +text = "关卡: 1" + +[node name="MovesLabel" type="Label" parent="."] +layout_mode = 1 +anchors_preset = 2 +anchor_top = 1.0 +anchor_bottom = 1.0 +offset_left = 50.0 +offset_top = -70.0 +offset_right = 200.0 +offset_bottom = -40.0 +grow_vertical = 0 +text = "步数: 0" + +[node name="WinLabel" type="Label" parent="."] +visible = false +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -150.0 +offset_top = -50.0 +offset_right = 150.0 +offset_bottom = 50.0 +grow_horizontal = 2 +grow_vertical = 2 +text = "恭喜过关! +按N进入下一关 +按R重新开始" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="ControlsLabel" type="Label" parent="."] +layout_mode = 1 +anchors_preset = 3 +anchor_left = 1.0 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = -300.0 +offset_top = -150.0 +offset_right = -50.0 +offset_bottom = -50.0 +grow_horizontal = 0 +grow_vertical = 0 +text = "操作说明: +WASD或方向键 - 移动 +R - 重新开始当前关卡 +N - 下一关(过关后) +P - 上一关" diff --git a/Scene/SmallGame/SnakeGame.gd b/Scene/SmallGame/SnakeGame.gd new file mode 100644 index 0000000..d8fe41c --- /dev/null +++ b/Scene/SmallGame/SnakeGame.gd @@ -0,0 +1,207 @@ +extends Panel + +# 游戏常量 +const GRID_SIZE = 20 +const GRID_WIDTH = 30 +const GRID_HEIGHT = 30 + +# 方向枚举 +enum Direction { + UP, + DOWN, + LEFT, + RIGHT +} + +# 游戏变量 +var snake_body = [] +var snake_direction = Direction.RIGHT +var next_direction = Direction.RIGHT +var food_position = Vector2() +var score = 0 +var game_over = false + +# 节点引用 +@onready var game_area = $GameArea +@onready var score_label = $ScoreLabel +@onready var game_over_label = $GameOverLabel +@onready var game_timer = $GameTimer + +func _ready(): + # 连接定时器信号 + game_timer.timeout.connect(_on_game_timer_timeout) + + # 设置游戏区域样式 + game_area.modulate = Color(0.2, 0.2, 0.2, 1.0) + + # 初始化游戏 + init_game() + +func init_game(): + # 重置游戏状态 + game_over = false + score = 0 + snake_direction = Direction.RIGHT + next_direction = Direction.RIGHT + + # 初始化蛇身 + snake_body.clear() + snake_body.append(Vector2(5, 5)) + snake_body.append(Vector2(4, 5)) + snake_body.append(Vector2(3, 5)) + + # 生成食物 + generate_food() + + # 更新UI + update_score() + game_over_label.visible = false + + # 启动定时器 + game_timer.start() + +func _input(event): + if event is InputEventKey and event.pressed: + if game_over: + if event.keycode == KEY_SPACE: + init_game() + return + + # 控制蛇的方向 + match event.keycode: + KEY_UP, KEY_W: + if snake_direction != Direction.DOWN: + next_direction = Direction.UP + KEY_DOWN, KEY_S: + if snake_direction != Direction.UP: + next_direction = Direction.DOWN + KEY_LEFT, KEY_A: + if snake_direction != Direction.RIGHT: + next_direction = Direction.LEFT + KEY_RIGHT, KEY_D: + if snake_direction != Direction.LEFT: + next_direction = Direction.RIGHT + +func _on_game_timer_timeout(): + if game_over: + return + + # 更新方向 + snake_direction = next_direction + + # 移动蛇 + move_snake() + + # 检查碰撞 + check_collisions() + + # 重绘游戏 + queue_redraw() + +func move_snake(): + var head = snake_body[0] + var new_head = head + + # 根据方向计算新的头部位置 + match snake_direction: + Direction.UP: + new_head = Vector2(head.x, head.y - 1) + Direction.DOWN: + new_head = Vector2(head.x, head.y + 1) + Direction.LEFT: + new_head = Vector2(head.x - 1, head.y) + Direction.RIGHT: + new_head = Vector2(head.x + 1, head.y) + + # 添加新头部 + snake_body.insert(0, new_head) + + # 检查是否吃到食物 + if new_head == food_position: + # 增加分数 + score += 10 + update_score() + + # 生成新食物 + generate_food() + else: + # 移除尾部 + snake_body.pop_back() + +func check_collisions(): + var head = snake_body[0] + + # 检查边界碰撞 + if head.x < 0 or head.x >= GRID_WIDTH or head.y < 0 or head.y >= GRID_HEIGHT: + game_over = true + show_game_over() + return + + # 检查自身碰撞 + for i in range(1, snake_body.size()): + if head == snake_body[i]: + game_over = true + show_game_over() + return + +func generate_food(): + var attempts = 0 + while attempts < 100: # 防止无限循环 + food_position = Vector2( + randi() % GRID_WIDTH, + randi() % GRID_HEIGHT + ) + + # 确保食物不在蛇身上 + var food_on_snake = false + for segment in snake_body: + if segment == food_position: + food_on_snake = true + break + + if not food_on_snake: + break + + attempts += 1 + +func update_score(): + score_label.text = "分数: " + str(score) + +func show_game_over(): + game_timer.stop() + game_over_label.visible = true + +func _draw(): + if not game_area: + return + + # 获取游戏区域的位置和大小 + var area_pos = game_area.position + var area_size = game_area.size + + # 计算网格大小 + var cell_width = area_size.x / GRID_WIDTH + var cell_height = area_size.y / GRID_HEIGHT + + # 绘制蛇身 + for i in range(snake_body.size()): + var segment = snake_body[i] + var rect = Rect2( + area_pos.x + segment.x * cell_width, + area_pos.y + segment.y * cell_height, + cell_width - 1, + cell_height - 1 + ) + + # 头部用不同颜色 + var color = Color.GREEN if i == 0 else Color.LIME_GREEN + draw_rect(rect, color) + + # 绘制食物 + var food_rect = Rect2( + area_pos.x + food_position.x * cell_width, + area_pos.y + food_position.y * cell_height, + cell_width - 1, + cell_height - 1 + ) + draw_rect(food_rect, Color.RED) diff --git a/Scene/SmallGame/SnakeGame.gd.uid b/Scene/SmallGame/SnakeGame.gd.uid new file mode 100644 index 0000000..d572078 --- /dev/null +++ b/Scene/SmallGame/SnakeGame.gd.uid @@ -0,0 +1 @@ +uid://rut5cdp1l3nh diff --git a/Scene/SmallGame/SnakeGame.tscn b/Scene/SmallGame/SnakeGame.tscn new file mode 100644 index 0000000..65d841b --- /dev/null +++ b/Scene/SmallGame/SnakeGame.tscn @@ -0,0 +1,57 @@ +[gd_scene load_steps=2 format=3 uid="uid://grpkpy4duwac"] + +[ext_resource type="Script" uid="uid://rut5cdp1l3nh" path="res://Scene/SmallGame/SnakeGame.gd" id="1_o070e"] + +[node name="SnakeGame" type="Panel"] +offset_right = 1403.0 +offset_bottom = 726.0 +script = ExtResource("1_o070e") + +[node name="GameArea" type="Panel" parent="."] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -300.0 +offset_top = -300.0 +offset_right = 300.0 +offset_bottom = 300.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="ScoreLabel" type="Label" parent="."] +layout_mode = 1 +anchors_preset = 2 +anchor_top = 1.0 +anchor_bottom = 1.0 +offset_left = 20.0 +offset_top = -50.0 +offset_right = 200.0 +offset_bottom = -20.0 +grow_vertical = 0 +text = "分数: 0" + +[node name="GameOverLabel" type="Label" parent="."] +visible = false +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -100.0 +offset_top = -50.0 +offset_right = 100.0 +offset_bottom = 50.0 +grow_horizontal = 2 +grow_vertical = 2 +text = "游戏结束 +按空格重新开始" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="GameTimer" type="Timer" parent="."] +wait_time = 0.2 +autostart = true diff --git a/Scene/SmallGame/Tetris.gd b/Scene/SmallGame/Tetris.gd new file mode 100644 index 0000000..99093ac --- /dev/null +++ b/Scene/SmallGame/Tetris.gd @@ -0,0 +1,333 @@ +extends Panel + +# 游戏常量 +const BOARD_WIDTH = 10 +const BOARD_HEIGHT = 20 +const CELL_SIZE = 30 + +# 方块类型 +enum PieceType { + I, O, T, S, Z, J, L +} + +# 方块形状定义 +const PIECE_SHAPES = { + PieceType.I: [ + [[1, 1, 1, 1]], + [[1], [1], [1], [1]] + ], + PieceType.O: [ + [[1, 1], [1, 1]] + ], + PieceType.T: [ + [[0, 1, 0], [1, 1, 1]], + [[1, 0], [1, 1], [1, 0]], + [[1, 1, 1], [0, 1, 0]], + [[0, 1], [1, 1], [0, 1]] + ], + PieceType.S: [ + [[0, 1, 1], [1, 1, 0]], + [[1, 0], [1, 1], [0, 1]] + ], + PieceType.Z: [ + [[1, 1, 0], [0, 1, 1]], + [[0, 1], [1, 1], [1, 0]] + ], + PieceType.J: [ + [[1, 0, 0], [1, 1, 1]], + [[1, 1], [1, 0], [1, 0]], + [[1, 1, 1], [0, 0, 1]], + [[0, 1], [0, 1], [1, 1]] + ], + PieceType.L: [ + [[0, 0, 1], [1, 1, 1]], + [[1, 0], [1, 0], [1, 1]], + [[1, 1, 1], [1, 0, 0]], + [[1, 1], [0, 1], [0, 1]] + ] +} + +# 方块颜色 +const PIECE_COLORS = { + PieceType.I: Color.CYAN, + PieceType.O: Color.YELLOW, + PieceType.T: Color.MAGENTA, + PieceType.S: Color.GREEN, + PieceType.Z: Color.RED, + PieceType.J: Color.BLUE, + PieceType.L: Color.ORANGE +} + +# 游戏变量 +var board = [] +var current_piece = null +var current_piece_pos = Vector2() +var current_piece_rotation = 0 +var next_piece_type = PieceType.I +var score = 0 +var level = 1 +var lines_cleared = 0 +var game_over = false +var drop_time = 1.0 + +# 节点引用 +@onready var game_area = $GameArea +@onready var next_piece_area = $NextPieceArea +@onready var score_label = $ScoreLabel +@onready var level_label = $LevelLabel +@onready var lines_label = $LinesLabel +@onready var game_over_label = $GameOverLabel +@onready var drop_timer = $DropTimer + +func _ready(): + # 连接定时器信号 + drop_timer.timeout.connect(_on_drop_timer_timeout) + + # 设置游戏区域样式 + game_area.modulate = Color(0.1, 0.1, 0.1, 1.0) + next_piece_area.modulate = Color(0.2, 0.2, 0.2, 1.0) + + # 初始化游戏 + init_game() + +func init_game(): + # 重置游戏状态 + game_over = false + score = 0 + level = 1 + lines_cleared = 0 + drop_time = 1.0 + + # 初始化游戏板 + board.clear() + for y in range(BOARD_HEIGHT): + var row = [] + for x in range(BOARD_WIDTH): + row.append(0) + board.append(row) + + # 生成第一个方块 + next_piece_type = randi() % PieceType.size() + spawn_new_piece() + + # 更新UI + update_ui() + game_over_label.visible = false + + # 启动定时器 + drop_timer.wait_time = drop_time + drop_timer.start() + +func _input(event): + if event is InputEventKey and event.pressed: + if game_over: + if event.keycode == KEY_SPACE: + init_game() + return + + if not current_piece: + return + + # 控制方块 + match event.keycode: + KEY_A: + move_piece(-1, 0) + KEY_D: + move_piece(1, 0) + KEY_S: + move_piece(0, 1) + KEY_W: + rotate_piece() + KEY_SPACE: + drop_piece() + +func spawn_new_piece(): + current_piece = { + "type": next_piece_type, + "rotation": 0 + } + current_piece_pos = Vector2(BOARD_WIDTH / 2 - 1, 0) + current_piece_rotation = 0 + + # 生成下一个方块 + next_piece_type = randi() % PieceType.size() + + # 检查游戏是否结束 + if not can_place_piece(current_piece_pos, current_piece_rotation): + game_over = true + show_game_over() + +func move_piece(dx: int, dy: int): + var new_pos = current_piece_pos + Vector2(dx, dy) + if can_place_piece(new_pos, current_piece_rotation): + current_piece_pos = new_pos + queue_redraw() + +func rotate_piece(): + var new_rotation = (current_piece_rotation + 1) % get_piece_rotations(current_piece.type) + if can_place_piece(current_piece_pos, new_rotation): + current_piece_rotation = new_rotation + queue_redraw() + +func drop_piece(): + while can_place_piece(current_piece_pos + Vector2(0, 1), current_piece_rotation): + current_piece_pos.y += 1 + place_piece() + +func can_place_piece(pos: Vector2, rotation: int) -> bool: + var shape = get_piece_shape(current_piece.type, rotation) + for y in range(shape.size()): + for x in range(shape[y].size()): + if shape[y][x] == 1: + var board_x = pos.x + x + var board_y = pos.y + y + + # 检查边界 + if board_x < 0 or board_x >= BOARD_WIDTH or board_y >= BOARD_HEIGHT: + return false + + # 检查碰撞 + if board_y >= 0 and board[board_y][board_x] != 0: + return false + return true + +func place_piece(): + var shape = get_piece_shape(current_piece.type, current_piece_rotation) + for y in range(shape.size()): + for x in range(shape[y].size()): + if shape[y][x] == 1: + var board_x = current_piece_pos.x + x + var board_y = current_piece_pos.y + y + if board_y >= 0: + board[board_y][board_x] = current_piece.type + 1 + + # 检查并清除完整的行 + clear_lines() + + # 生成新方块 + spawn_new_piece() + + queue_redraw() + +func clear_lines(): + var lines_to_clear = [] + + # 找到完整的行 + for y in range(BOARD_HEIGHT): + var is_full = true + for x in range(BOARD_WIDTH): + if board[y][x] == 0: + is_full = false + break + if is_full: + lines_to_clear.append(y) + + # 清除行并下移 + for line_y in lines_to_clear: + board.erase(board[line_y]) + var new_row = [] + for x in range(BOARD_WIDTH): + new_row.append(0) + board.insert(0, new_row) + + # 更新分数和等级 + if lines_to_clear.size() > 0: + lines_cleared += lines_to_clear.size() + score += lines_to_clear.size() * 100 * level + + # 每10行提升一个等级 + level = lines_cleared / 10 + 1 + drop_time = max(0.1, 1.0 - (level - 1) * 0.1) + drop_timer.wait_time = drop_time + + update_ui() + +func get_piece_shape(type: PieceType, rotation: int) -> Array: + return PIECE_SHAPES[type][rotation] + +func get_piece_rotations(type: PieceType) -> int: + return PIECE_SHAPES[type].size() + +func update_ui(): + score_label.text = "分数: " + str(score) + level_label.text = "等级: " + str(level) + lines_label.text = "消除行数: " + str(lines_cleared) + +func show_game_over(): + drop_timer.stop() + game_over_label.visible = true + +func _on_drop_timer_timeout(): + if game_over or not current_piece: + return + + if can_place_piece(current_piece_pos + Vector2(0, 1), current_piece_rotation): + current_piece_pos.y += 1 + queue_redraw() + else: + place_piece() + +func _draw(): + if not game_area: + return + + # 获取游戏区域的位置 + var area_pos = game_area.position + + # 绘制游戏板 + for y in range(BOARD_HEIGHT): + for x in range(BOARD_WIDTH): + var cell_value = board[y][x] + if cell_value > 0: + var rect = Rect2( + area_pos.x + x * CELL_SIZE, + area_pos.y + y * CELL_SIZE, + CELL_SIZE - 1, + CELL_SIZE - 1 + ) + var color = PIECE_COLORS[cell_value - 1] + draw_rect(rect, color) + + # 绘制当前方块 + if current_piece: + var shape = get_piece_shape(current_piece.type, current_piece_rotation) + for y in range(shape.size()): + for x in range(shape[y].size()): + if shape[y][x] == 1: + var board_x = current_piece_pos.x + x + var board_y = current_piece_pos.y + y + if board_y >= 0: + var rect = Rect2( + area_pos.x + board_x * CELL_SIZE, + area_pos.y + board_y * CELL_SIZE, + CELL_SIZE - 1, + CELL_SIZE - 1 + ) + var color = PIECE_COLORS[current_piece.type] + draw_rect(rect, color) + + # 绘制网格线 + for x in range(BOARD_WIDTH + 1): + var start_pos = Vector2(area_pos.x + x * CELL_SIZE, area_pos.y) + var end_pos = Vector2(area_pos.x + x * CELL_SIZE, area_pos.y + BOARD_HEIGHT * CELL_SIZE) + draw_line(start_pos, end_pos, Color.GRAY, 1) + + for y in range(BOARD_HEIGHT + 1): + var start_pos = Vector2(area_pos.x, area_pos.y + y * CELL_SIZE) + var end_pos = Vector2(area_pos.x + BOARD_WIDTH * CELL_SIZE, area_pos.y + y * CELL_SIZE) + draw_line(start_pos, end_pos, Color.GRAY, 1) + + # 绘制下一个方块预览 + var next_area_pos = next_piece_area.position + var next_shape = get_piece_shape(next_piece_type, 0) + for y in range(next_shape.size()): + for x in range(next_shape[y].size()): + if next_shape[y][x] == 1: + var rect = Rect2( + next_area_pos.x + 20 + x * 20, + next_area_pos.y + 50 + y * 20, + 19, + 19 + ) + var color = PIECE_COLORS[next_piece_type] + draw_rect(rect, color) diff --git a/Scene/SmallGame/Tetris.gd.uid b/Scene/SmallGame/Tetris.gd.uid new file mode 100644 index 0000000..4ef1c36 --- /dev/null +++ b/Scene/SmallGame/Tetris.gd.uid @@ -0,0 +1 @@ +uid://yhljjw1pbovq diff --git a/Scene/SmallGame/Tetris.tscn b/Scene/SmallGame/Tetris.tscn new file mode 100644 index 0000000..c0d449b --- /dev/null +++ b/Scene/SmallGame/Tetris.tscn @@ -0,0 +1,120 @@ +[gd_scene load_steps=2 format=3 uid="uid://c3nuuwu5te3bu"] + +[ext_resource type="Script" uid="uid://yhljjw1pbovq" path="res://Scene/SmallGame/Tetris.gd" id="1_s1q1g"] + +[node name="Tetris" type="Panel"] +offset_right = 1403.0 +offset_bottom = 721.0 +script = ExtResource("1_s1q1g") + +[node name="GameArea" type="Panel" parent="."] +layout_mode = 1 +anchors_preset = 4 +anchor_top = 0.5 +anchor_bottom = 0.5 +offset_left = 50.0 +offset_top = -300.0 +offset_right = 350.0 +offset_bottom = 300.0 +grow_vertical = 2 + +[node name="NextPieceArea" type="Panel" parent="."] +layout_mode = 1 +anchors_preset = 1 +anchor_left = 1.0 +anchor_right = 1.0 +offset_left = -200.0 +offset_top = 50.0 +offset_right = -50.0 +offset_bottom = 200.0 +grow_horizontal = 0 + +[node name="NextPieceLabel" type="Label" parent="NextPieceArea"] +layout_mode = 1 +anchors_preset = 5 +anchor_left = 0.5 +anchor_right = 0.5 +offset_left = -50.0 +offset_top = 10.0 +offset_right = 50.0 +offset_bottom = 40.0 +grow_horizontal = 2 +text = "下一个" +horizontal_alignment = 1 + +[node name="ScoreLabel" type="Label" parent="."] +layout_mode = 1 +anchors_preset = 1 +anchor_left = 1.0 +anchor_right = 1.0 +offset_left = -200.0 +offset_top = 220.0 +offset_right = -50.0 +offset_bottom = 250.0 +grow_horizontal = 0 +text = "分数: 0" +horizontal_alignment = 1 + +[node name="LevelLabel" type="Label" parent="."] +layout_mode = 1 +anchors_preset = 1 +anchor_left = 1.0 +anchor_right = 1.0 +offset_left = -200.0 +offset_top = 260.0 +offset_right = -50.0 +offset_bottom = 290.0 +grow_horizontal = 0 +text = "等级: 1" +horizontal_alignment = 1 + +[node name="LinesLabel" type="Label" parent="."] +layout_mode = 1 +anchors_preset = 1 +anchor_left = 1.0 +anchor_right = 1.0 +offset_left = -200.0 +offset_top = 300.0 +offset_right = -50.0 +offset_bottom = 330.0 +grow_horizontal = 0 +text = "消除行数: 0" +horizontal_alignment = 1 + +[node name="GameOverLabel" type="Label" parent="."] +visible = false +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -100.0 +offset_top = -50.0 +offset_right = 100.0 +offset_bottom = 50.0 +grow_horizontal = 2 +grow_vertical = 2 +text = "游戏结束 +按空格重新开始" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="DropTimer" type="Timer" parent="."] +autostart = true + +[node name="ControlsLabel" type="Label" parent="."] +layout_mode = 1 +anchors_preset = 1 +anchor_left = 1.0 +anchor_right = 1.0 +offset_left = -200.0 +offset_top = 400.0 +offset_right = -50.0 +offset_bottom = 500.0 +grow_horizontal = 0 +text = "操作说明: +A/D - 左右移动 +S - 快速下降 +W - 旋转 +空格 - 瞬间下降" diff --git a/Script/BigPanel/CropStorePanel.gd b/Script/BigPanel/CropStorePanel.gd index 3e1797b..5e40399 100644 --- a/Script/BigPanel/CropStorePanel.gd +++ b/Script/BigPanel/CropStorePanel.gd @@ -43,6 +43,11 @@ var current_filter_quality = "" var current_sort_key = "" var current_sort_ascending = true +# 库存系统 +var crop_stock_data : Dictionary = {} # 存储每个作物的库存数量 +var stock_file_path : String = "" # 库存数据文件路径(根据用户名动态设置) +var last_refresh_date : String = "" # 上次刷新库存的日期 + # 准备函数 func _ready(): # 连接按钮信号 @@ -51,6 +56,9 @@ func _ready(): # 连接可见性改变信号 visibility_changed.connect(_on_visibility_changed) + # 初始化库存系统 + _init_stock_system() + # 隐藏面板(初始默认隐藏) self.hide() @@ -60,6 +68,8 @@ func _ready(): func _connect_buttons(): # 关闭按钮 quit_button.pressed.connect(self._on_quit_button_pressed) + # 刷新按钮 + refresh_button.pressed.connect(self._on_refresh_button_pressed) # 过滤按钮 sort_all_button.pressed.connect(func(): _filter_by_quality("")) @@ -78,11 +88,22 @@ func _connect_buttons(): # 初始化商店 func init_store(): + # 重新初始化库存系统(确保用户名正确) + _init_stock_system() + # 清空已有的作物按钮 for child in crop_grid_container.get_children(): child.queue_free() - # 遍历可种植的作物数据并添加到商店 + # 检查并刷新库存(如果需要) + _check_daily_refresh() + + # 获取玩家当前等级,确定可解锁的格子数量 + var player_level = main_game.level + var max_unlocked_slots = player_level # 玩家等级 = 可解锁的格子数量 + + # 收集符合条件的作物 + var available_crops = [] for crop_name in main_game.can_planted_crop: var crop = main_game.can_planted_crop[crop_name] @@ -92,13 +113,33 @@ func init_store(): # 只显示当前等级可以种植的作物 if crop["等级"] <= main_game.level: - var store_btn = _create_store_button(crop_name, crop["品质"]) - crop_grid_container.add_child(store_btn) + available_crops.append({"name": crop_name, "data": crop}) - print("商店初始化完成,共添加按钮: " + str(crop_grid_container.get_child_count()) + "个") + # 根据等级限制显示的格子数量 + var slots_to_show = min(available_crops.size(), max_unlocked_slots) + + # 添加可显示的作物按钮 + for i in range(slots_to_show): + var crop_info = available_crops[i] + var store_btn = _create_store_button(crop_info["name"], crop_info["data"]["品质"]) + crop_grid_container.add_child(store_btn) + + # 添加锁定的格子(如果有剩余的可用作物但等级不够解锁) + var remaining_crops = available_crops.size() - slots_to_show + if remaining_crops > 0: + # 创建锁定格子提示 + var locked_slots = min(remaining_crops, 5) # 最多显示5个锁定格子作为提示 + for i in range(locked_slots): + var locked_btn = _create_locked_slot_button(player_level + 1) + crop_grid_container.add_child(locked_btn) + + print("商店初始化完成,玩家等级: ", player_level, ", 解锁格子: ", slots_to_show, ", 可用作物: ", available_crops.size()) # 更新金钱显示 _update_money_display() + + # 显示等级限制提示 + _show_level_restriction_info(player_level, available_crops.size(), slots_to_show) # 创建商店按钮 func _create_store_button(crop_name: String, crop_quality: String) -> Button: @@ -107,14 +148,26 @@ func _create_store_button(crop_name: String, crop_quality: String) -> Button: var crop = main_game.can_planted_crop[crop_name] - # 确保按钮可见并可点击 + # 获取当前库存 + var current_stock = _get_crop_stock(crop_name) + var is_sold_out = current_stock <= 0 + + # 设置按钮状态 button.visible = true - button.disabled = false + button.disabled = is_sold_out button.focus_mode = Control.FOCUS_ALL - # 设置按钮文本,显示价格 + # 设置按钮文本,显示价格和库存 var display_name = crop.get("作物名称", crop_name) - button.text = str(crop_quality + "-" + display_name + "\n价格: ¥" + str(crop["花费"])) + var stock_text = "库存: " + str(current_stock) if not is_sold_out else "已售罄" + var price_text = "价格: ¥" + str(crop["花费"]) + + if is_sold_out: + button.text = str(crop_quality + "-" + display_name + "\n" + price_text + "\n" + stock_text) + button.modulate = Color(0.6, 0.6, 0.6, 0.8) # 灰色半透明效果 + else: + button.text = str(crop_quality + "-" + display_name + "\n" + price_text + "\n" + stock_text) + button.modulate = Color.WHITE # 正常颜色 # 将成熟时间从秒转换为天时分秒格式 var total_seconds = int(crop["生长时间"]) @@ -145,6 +198,9 @@ func _create_store_button(crop_name: String, crop_quality: String) -> Button: if seconds > 0: time_str += str(seconds) + "秒" + # 添加库存信息到tooltip + var stock_tooltip = "\n库存: " + str(current_stock) + " 个" if not is_sold_out else "\n状态: 已售罄" + button.tooltip_text = str( "作物: " + display_name + "\n" + "品质: " + crop_quality + "\n" + @@ -153,7 +209,7 @@ func _create_store_button(crop_name: String, crop_quality: String) -> Button: "收获收益: " + str(crop["收益"]) + "元\n" + "需求等级: " + str(crop["等级"]) + "\n" + "耐候性: " + str(crop["耐候性"]) + "\n" + - "经验: " + str(crop["经验"]) + "点\n" + + "经验: " + str(crop["经验"]) + "点" + stock_tooltip + "\n" + "描述: " + str(crop["描述"]) ) @@ -183,6 +239,11 @@ func _create_store_button(crop_name: String, crop_quality: String) -> Button: func _on_store_buy_pressed(crop_name: String): var crop = main_game.can_planted_crop[crop_name] + # 检查库存 + if not _is_crop_in_stock(crop_name): + Toast.show("该种子已售罄,请等待明日刷新", Color.RED) + return + # 检查等级要求 if main_game.level < crop["等级"]: Toast.show("等级不足,无法购买此种子", Color.RED) @@ -196,13 +257,15 @@ func _on_store_buy_pressed(crop_name: String): # 显示批量购买弹窗 if batch_buy_popup: var crop_desc = crop.get("描述", "暂无描述") + var max_stock = _get_crop_stock(crop_name) batch_buy_popup.show_buy_popup( crop_name, crop["花费"], crop_desc, "seed", _on_confirm_buy_seed, - _on_cancel_buy_seed + _on_cancel_buy_seed, + max_stock # 传递最大库存限制 ) else: print("批量购买弹窗未找到") @@ -211,6 +274,12 @@ func _on_store_buy_pressed(crop_name: String): func _on_confirm_buy_seed(crop_name: String, unit_cost: int, quantity: int, buy_type: String): var total_cost = unit_cost * quantity + # 再次检查库存是否足够 + var current_stock = _get_crop_stock(crop_name) + if current_stock < quantity: + Toast.show("库存不足!当前库存: " + str(current_stock) + ",需要: " + str(quantity), Color.RED, 3.0, 1.0) + return + # 再次检查金钱是否足够 if main_game.money < total_cost: Toast.show("金钱不足!需要 " + str(total_cost) + " 元,当前只有 " + str(main_game.money) + " 元", Color.RED, 3.0, 1.0) @@ -229,6 +298,15 @@ func _send_batch_buy_seed_request(crop_name: String, quantity: int): if tcp_network_manager_panel and tcp_network_manager_panel.sendBuySeed(crop_name, quantity): # 服务器会处理批量购买逻辑,客户端等待响应 print("已发送批量购买种子请求:", crop_name, " 数量:", quantity) + + # 购买成功后扣减库存 + if _reduce_crop_stock(crop_name, quantity): + print("库存扣减成功:", crop_name, " 扣减数量:", quantity) + # 刷新商店显示 + _apply_filter_and_sort() + Toast.show("购买成功!剩余库存: " + str(_get_crop_stock(crop_name)), Color.GREEN, 2.0, 1.0) + else: + Toast.show("库存扣减失败", Color.RED, 2.0, 1.0) else: Toast.show("购买请求发送失败", Color.RED, 2.0, 1.0) @@ -255,6 +333,10 @@ func _apply_filter_and_sort(): for child in crop_grid_container.get_children(): child.queue_free() + # 获取玩家当前等级,确定可解锁的格子数量 + var player_level = main_game.level + var max_unlocked_slots = player_level + # 收集符合条件的作物 var filtered_crops = [] for crop_name in main_game.can_planted_crop: @@ -281,13 +363,29 @@ func _apply_filter_and_sort(): if current_sort_key != "": filtered_crops.sort_custom(Callable(self, "_sort_crop_items")) - # 添加所有过滤和排序后的作物 - for crop in filtered_crops: + # 根据等级限制显示的格子数量 + var slots_to_show = min(filtered_crops.size(), max_unlocked_slots) + + # 添加可显示的作物按钮 + for i in range(slots_to_show): + var crop = filtered_crops[i] var store_btn = _create_store_button(crop["name"], crop["data"]["品质"]) crop_grid_container.add_child(store_btn) + + # 添加锁定的格子(如果有剩余的可用作物但等级不够解锁) + var remaining_crops = filtered_crops.size() - slots_to_show + if remaining_crops > 0: + # 创建锁定格子提示 + var locked_slots = min(remaining_crops, 5) # 最多显示5个锁定格子作为提示 + for i in range(locked_slots): + var locked_btn = _create_locked_slot_button(player_level + 1) + crop_grid_container.add_child(locked_btn) # 更新金钱显示 _update_money_display() + + # 显示等级限制提示 + _show_level_restriction_info(player_level, filtered_crops.size(), slots_to_show) # 自定义排序函数 func _sort_crop_items(a, b): @@ -512,6 +610,211 @@ func _update_button_crop_image(button: Button, crop_name: String): func _on_crop_selected(crop_name: String): _on_store_buy_pressed(crop_name) +#=========================库存系统========================= + +# 初始化库存系统 +func _init_stock_system(): + # 根据用户名设置库存文件路径,实现账号隔离 + var user_name = main_game.user_name if main_game.user_name != "" else "default_user" + stock_file_path = "user://crop_stock_" + user_name + ".json" + print("库存系统初始化,用户:", user_name, ",文件路径:", stock_file_path) + + _load_stock_data() + _check_daily_refresh() + +# 加载库存数据 +func _load_stock_data(): + print("尝试加载库存数据,文件路径:", stock_file_path) + + if FileAccess.file_exists(stock_file_path): + var file = FileAccess.open(stock_file_path, FileAccess.READ) + if file: + var json_string = file.get_as_text() + file.close() + print("读取到的JSON数据:", json_string) + + var json = JSON.new() + var parse_result = json.parse(json_string) + if parse_result == OK: + var data = json.data + crop_stock_data = data.get("stock", {}) + last_refresh_date = data.get("last_refresh_date", "") + print("库存数据加载成功,库存条目数:", crop_stock_data.size()) + print("加载的库存数据:", crop_stock_data) + print("上次刷新日期:", last_refresh_date) + + # 如果库存数据为空,重新生成 + if crop_stock_data.is_empty(): + print("库存数据为空,重新生成") + _generate_initial_stock() + else: + print("库存数据解析失败,错误:", parse_result, ",重新生成") + _generate_initial_stock() + else: + print("无法打开库存文件,重新生成") + _generate_initial_stock() + else: + print("库存文件不存在,生成初始库存") + _generate_initial_stock() + +# 保存库存数据 +func _save_stock_data(): + var data = { + "stock": crop_stock_data, + "last_refresh_date": last_refresh_date + } + + print("准备保存库存数据到:", stock_file_path) + print("保存的数据:", data) + + var file = FileAccess.open(stock_file_path, FileAccess.WRITE) + if file: + var json_string = JSON.stringify(data) + file.store_string(json_string) + file.close() + print("库存数据保存成功,JSON字符串:", json_string) + else: + print("无法保存库存数据,文件打开失败") + +# 生成初始库存 +func _generate_initial_stock(): + crop_stock_data.clear() + + # 确保main_game和can_planted_crop存在 + if not main_game or not main_game.can_planted_crop: + print("错误:无法访问主游戏数据,无法生成库存") + return + + var generated_count = 0 + for crop_name in main_game.can_planted_crop: + var crop = main_game.can_planted_crop[crop_name] + + # 检查是否可以购买 + if not crop.get("能否购买", true): + continue + + # 根据品质设置库存范围 + var stock_amount = _get_stock_amount_by_quality(crop["品质"]) + crop_stock_data[crop_name] = stock_amount + generated_count += 1 + print("生成库存:", crop_name, " - ", crop["品质"], " - ", stock_amount, "个") + + # 设置当前日期为刷新日期 + last_refresh_date = _get_current_date() + _save_stock_data() + print("初始库存生成完成,共生成", generated_count, "种作物的库存") + print("当前库存数据:", crop_stock_data) + +# 根据品质获取库存数量 +func _get_stock_amount_by_quality(quality: String) -> int: + var min_stock: int + var max_stock: int + + match quality: + "传奇": + min_stock = 10 + max_stock = 30 + "史诗": + min_stock = 20 + max_stock = 50 + "稀有": + min_stock = 40 + max_stock = 80 + "优良": + min_stock = 80 + max_stock = 150 + "普通": + min_stock = 150 + max_stock = 300 + _: + min_stock = 100 + max_stock = 200 + + return randi_range(min_stock, max_stock) + +# 获取当前日期字符串 +func _get_current_date() -> String: + var datetime = Time.get_datetime_dict_from_system() + return str(datetime.year) + "-" + str(datetime.month).pad_zeros(2) + "-" + str(datetime.day).pad_zeros(2) + +# 检查是否需要每日刷新 +func _check_daily_refresh(): + var current_date = _get_current_date() + if last_refresh_date != current_date: + print("检测到新的一天,刷新库存") + _refresh_daily_stock() + +# 每日刷新库存 +func _refresh_daily_stock(): + _generate_initial_stock() + Toast.show("种子商店库存已刷新!", Color.GREEN, 3.0, 1.0) + +# 获取作物当前库存 +func _get_crop_stock(crop_name: String) -> int: + return crop_stock_data.get(crop_name, 0) + +# 减少作物库存 +func _reduce_crop_stock(crop_name: String, amount: int) -> bool: + var current_stock = _get_crop_stock(crop_name) + if current_stock >= amount: + crop_stock_data[crop_name] = current_stock - amount + _save_stock_data() + return true + return false + +# 检查作物是否有库存 +func _is_crop_in_stock(crop_name: String) -> bool: + return _get_crop_stock(crop_name) > 0 + + +# 创建锁定格子按钮 +func _create_locked_slot_button(required_level: int) -> Button: + var button = main_game.item_button.duplicate() + + # 设置按钮为禁用状态 + button.disabled = true + button.modulate = Color(0.5, 0.5, 0.5, 0.8) # 灰色半透明效果 + + # 设置按钮文本 + button.text = "🔒 锁定\n需要等级: " + str(required_level) + button.tooltip_text = "此格子已锁定,需要达到等级 " + str(required_level) + " 才能解锁" + + # 隐藏作物图片 + var crop_image = button.get_node_or_null("CropImage") + if crop_image: + crop_image.visible = false + + # 设置标题颜色为灰色 + if button.has_node("Title"): + button.get_node("Title").modulate = Color.GRAY + + return button + +# 显示等级限制信息 +func _show_level_restriction_info(player_level: int, total_crops: int, unlocked_slots: int): + # 查找或创建信息标签 + var info_label = get_node_or_null("LevelInfoLabel") + if info_label == null: + info_label = Label.new() + info_label.name = "LevelInfoLabel" + info_label.position = Vector2(10, 55) + info_label.size = Vector2(300, 30) + + # 设置标签样式 + info_label.add_theme_color_override("font_color", Color(0.8, 0.8, 1.0, 1.0)) # 淡蓝色 + info_label.add_theme_font_size_override("font_size", 18) + + add_child(info_label) + + # 更新信息显示 + var locked_crops = total_crops - unlocked_slots + if locked_crops > 0: + info_label.text = "等级 " + str(player_level) + " | 已解锁: " + str(unlocked_slots) + "/" + str(total_crops) + " 个格子" + info_label.modulate = Color.YELLOW + else: + info_label.text = "等级 " + str(player_level) + " | 所有格子已解锁 (" + str(unlocked_slots) + "/" + str(total_crops) + ")" + info_label.modulate = Color.GREEN + #=========================面板通用处理========================= #手动刷新种子商店面板 diff --git a/Script/BigPanel/CropWarehousePanel.gd b/Script/BigPanel/CropWarehousePanel.gd index c3afedc..d80f4fa 100644 --- a/Script/BigPanel/CropWarehousePanel.gd +++ b/Script/BigPanel/CropWarehousePanel.gd @@ -571,6 +571,8 @@ func _on_quit_button_pressed(): func _on_visibility_changed(): if visible: GlobalVariables.isZoomDisabled = true + # 面板显示时请求同步最新的背包数据 + tcp_network_manager_panel.send_sync_bag_data_request() update_crop_warehouse_ui() pass else: diff --git a/Script/BigPanel/ItemBagPanel.gd b/Script/BigPanel/ItemBagPanel.gd index 7047d2e..90b0c39 100644 --- a/Script/BigPanel/ItemBagPanel.gd +++ b/Script/BigPanel/ItemBagPanel.gd @@ -73,6 +73,8 @@ func init_item_bag(): for child in bag_grid.get_children(): child.queue_free() + # 重置筛选状态为显示全部 + current_filter_type = "全部" # 显示背包中的道具 update_item_bag_ui() @@ -85,6 +87,9 @@ func update_item_bag_ui(): # 获取过滤后的道具列表 var filtered_items = _get_filtered_items() + # 输出筛选统计信息 + print("道具背包筛选结果: ", current_filter_type, " - 共 ", filtered_items.size(), " 个道具") + # 为背包中的每个道具创建按钮 for item_data in filtered_items: var item_name = item_data["name"] @@ -177,8 +182,9 @@ func _get_filtered_items() -> Array: if _is_pet_item(item_name): filtered_items.append(item_data) else: - # 正常模式显示所有道具 - filtered_items.append(item_data) + # 正常模式下,应用筛选条件 + if _is_item_match_filter(item_name): + filtered_items.append(item_data) return filtered_items @@ -501,6 +507,8 @@ func _on_refresh_button_pressed() -> void: func _on_visibility_changed(): if visible: GlobalVariables.isZoomDisabled = true + # 面板显示时请求同步最新的背包数据 + tcp_network_manager_panel.send_sync_bag_data_request() update_item_bag_ui() pass else: @@ -515,3 +523,49 @@ func get_selected_item_name() -> String: # 检查是否有道具被选择 func is_item_currently_selected() -> bool: return is_item_selected + + +#========================筛选功能============================ +# 当前筛选类型 +var current_filter_type: String = "全部" + +#显示全部,既全部显示(默认) +func _on_all_filter_pressed() -> void: + current_filter_type = "全部" + update_item_bag_ui() + Toast.show("显示全部道具", Color.GREEN, 2.0, 1.0) + +#筛选宠物类型道具 +func _on_pet_filter_pressed() -> void: + current_filter_type = "宠物道具" + update_item_bag_ui() + Toast.show("筛选宠物道具", Color.GREEN, 2.0, 1.0) + +#筛选作物类型道具 +func _on_crop_filter_pressed() -> void: + current_filter_type = "作物道具" + update_item_bag_ui() + Toast.show("筛选作物道具", Color.GREEN, 2.0, 1.0) + +#筛选农场类型道具 +func _on_farm_filter_pressed() -> void: + current_filter_type = "农场道具" + update_item_bag_ui() + Toast.show("筛选农场道具", Color.GREEN, 2.0, 1.0) + +# 检查道具是否符合当前筛选条件 +func _is_item_match_filter(item_name: String) -> bool: + # 如果是显示全部,直接返回true + if current_filter_type == "全部": + return true + + # 检查道具配置中的类型 + var item_config = _load_item_config() + if item_config and item_config.has(item_name): + var item_info = item_config[item_name] + var item_type = item_info.get("类型", "") + return item_type == current_filter_type + + # 如果没有配置信息,默认不显示 + return false +#========================筛选功能============================ diff --git a/Script/BigPanel/ItemStorePanel.gd b/Script/BigPanel/ItemStorePanel.gd index d6a1003..33c450e 100644 --- a/Script/BigPanel/ItemStorePanel.gd +++ b/Script/BigPanel/ItemStorePanel.gd @@ -38,6 +38,8 @@ func _ready(): func init_item_store(): # 从主游戏脚本获取道具配置数据 _load_item_config_from_main() + # 重置筛选状态为显示全部 + current_filter_type = "全部" update_item_store_ui() # 更新道具商店UI @@ -46,10 +48,17 @@ func update_item_store_ui(): for child in store_grid.get_children(): child.queue_free() - print("更新道具商店UI,道具种类:", item_config.size()) + print("更新道具商店UI,道具种类:", item_config.size(), ",当前筛选:", current_filter_type) + + # 统计符合筛选条件的道具数量 + var filtered_count = 0 # 为每个道具配置创建按钮 for item_name in item_config.keys(): + # 检查是否符合筛选条件 + if not _is_item_match_filter(item_name): + continue + var item_info = item_config[item_name] var item_cost = item_info.get("花费", 0) var item_desc = item_info.get("描述", "暂无描述") @@ -64,6 +73,9 @@ func update_item_store_ui(): button.pressed.connect(func(): _on_store_item_selected(item_name, item_cost, item_desc)) store_grid.add_child(button) + filtered_count += 1 + + print("筛选后显示道具数量:", filtered_count) # 创建道具按钮 func _create_item_button(item_name: String, item_cost: int, item_desc: String) -> Button: @@ -273,3 +285,49 @@ func _on_visibility_changed(): GlobalVariables.isZoomDisabled = false pass #=========================面板通用处理========================= + + + +#========================筛选功能============================ +# 当前筛选类型 +var current_filter_type: String = "全部" + +#显示全部,既全部显示(默认) +func _on_all_filter_pressed() -> void: + current_filter_type = "全部" + update_item_store_ui() + Toast.show("显示全部道具", Color.GREEN, 2.0, 1.0) + +#筛选宠物类型道具 +func _on_pet_filter_pressed() -> void: + current_filter_type = "宠物道具" + update_item_store_ui() + Toast.show("筛选宠物道具", Color.GREEN, 2.0, 1.0) + +#筛选作物类型道具 +func _on_crop_filter_pressed() -> void: + current_filter_type = "作物道具" + update_item_store_ui() + Toast.show("筛选作物道具", Color.GREEN, 2.0, 1.0) + +#筛选农场类型道具 +func _on_farm_filter_pressed() -> void: + current_filter_type = "农场道具" + update_item_store_ui() + Toast.show("筛选农场道具", Color.GREEN, 2.0, 1.0) + +# 检查道具是否符合当前筛选条件 +func _is_item_match_filter(item_name: String) -> bool: + # 如果是显示全部,直接返回true + if current_filter_type == "全部": + return true + + # 检查道具配置中的类型 + if item_config.has(item_name): + var item_info = item_config[item_name] + var item_type = item_info.get("类型", "") + return item_type == current_filter_type + + # 如果没有配置信息,默认不显示 + return false +#========================筛选功能============================ diff --git a/Script/BigPanel/PetBagPanel.gd b/Script/BigPanel/PetBagPanel.gd index 1b624af..f82b88b 100644 --- a/Script/BigPanel/PetBagPanel.gd +++ b/Script/BigPanel/PetBagPanel.gd @@ -283,6 +283,8 @@ func _on_refresh_button_pressed() -> void: func _on_visibility_changed(): if visible: GlobalVariables.isZoomDisabled = true + # 面板显示时请求同步最新的背包数据 + tcp_network_manager_panel.send_sync_bag_data_request() # 面板显示时自动刷新数据 update_pet_bag_ui() pass diff --git a/Script/BigPanel/PlayGamePanel.gd b/Script/BigPanel/PlayGamePanel.gd new file mode 100644 index 0000000..c1c91d4 --- /dev/null +++ b/Script/BigPanel/PlayGamePanel.gd @@ -0,0 +1 @@ +extends Panel diff --git a/Script/BigPanel/PlayGamePanel.gd.uid b/Script/BigPanel/PlayGamePanel.gd.uid new file mode 100644 index 0000000..b17cac4 --- /dev/null +++ b/Script/BigPanel/PlayGamePanel.gd.uid @@ -0,0 +1 @@ +uid://dvaah0n1ia1a3 diff --git a/Script/BigPanel/PlayerBagPanel.gd b/Script/BigPanel/PlayerBagPanel.gd index 1964c7c..3f1b893 100644 --- a/Script/BigPanel/PlayerBagPanel.gd +++ b/Script/BigPanel/PlayerBagPanel.gd @@ -487,6 +487,8 @@ func _on_quit_button_pressed(): func _on_visibility_changed(): if visible: GlobalVariables.isZoomDisabled = true + # 面板显示时请求同步最新的背包数据 + tcp_network_manager_panel.send_sync_bag_data_request() # 面板显示时自动刷新数据 update_player_bag_ui() pass diff --git a/Script/BigPanel/SpecialFarmPanel.gd b/Script/BigPanel/SpecialFarmPanel.gd new file mode 100644 index 0000000..5f982c0 --- /dev/null +++ b/Script/BigPanel/SpecialFarmPanel.gd @@ -0,0 +1,72 @@ +extends Panel + +# 预添加常用的面板 +@onready var main_game = get_node("/root/main") +@onready var tcp_network_manager_panel: Panel = $'../TCPNetworkManagerPanel' + +func _ready() -> void: + self.hide() + visibility_changed.connect(_on_visibility_changed) + pass + +func _on_quit_button_pressed() -> void: + self.hide() + pass + +#访问花卉农场QQ:520 +func _on_flower_farm_button_pressed() -> void: + _visit_special_farm("520", "花卉农场") + +#访问杂交农场QQ:666 +func _on_hybrid_farm_button_pressed() -> void: + _visit_special_farm("666", "杂交农场") + +#访问幸运农场QQ:888 +func _on_lucky_farm_button_pressed() -> void: + _visit_special_farm("888", "幸运农场") + +#访问稻谷农场QQ:111 +func _on_rice_farm_button_pressed() -> void: + _visit_special_farm("111", "稻谷农场") + +#访问小麦农场QQ:222 +func _on_wheat_farm_button_pressed() -> void: + _visit_special_farm("222", "小麦农场") + +#访问水果农场QQ:333 +func _on_fruit_farm_button_pressed() -> void: + _visit_special_farm("333", "水果农场") + +# 访问特殊农场的通用函数 +func _visit_special_farm(farm_qq: String, farm_name: String) -> void: + # 访问农场后取消禁用相机功能,否则无法恢复 + GlobalVariables.isZoomDisabled = false + + # 检查网络连接 + if not tcp_network_manager_panel or not tcp_network_manager_panel.is_connected_to_server(): + Toast.show("未连接服务器,无法访问" + farm_name, Color.RED) + return + + # 检查是否尝试访问自己 + if main_game and main_game.user_name == farm_qq: + Toast.show("不能访问自己的农场", Color.ORANGE) + return + + # 发送访问特殊农场请求 + if tcp_network_manager_panel and tcp_network_manager_panel.has_method("sendVisitPlayer"): + var success = tcp_network_manager_panel.sendVisitPlayer(farm_qq) + if success: + Toast.show("正在访问" + farm_name + "...", Color.YELLOW) + # 隐藏面板 + self.hide() + else: + Toast.show("发送访问请求失败", Color.RED) + else: + Toast.show("网络管理器不可用", Color.RED) + +# 面板显示与隐藏切换处理 +func _on_visibility_changed(): + if visible: + GlobalVariables.isZoomDisabled = true + else: + GlobalVariables.isZoomDisabled = false diff --git a/Script/BigPanel/SpecialFarmPanel.gd.uid b/Script/BigPanel/SpecialFarmPanel.gd.uid new file mode 100644 index 0000000..25f5265 --- /dev/null +++ b/Script/BigPanel/SpecialFarmPanel.gd.uid @@ -0,0 +1 @@ +uid://btm2je8pg7rgk diff --git a/Script/Dialog/AddProduct2StorePopup.gd b/Script/Dialog/AddProduct2StorePopup.gd index 86d09d8..1b98c15 100644 --- a/Script/Dialog/AddProduct2StorePopup.gd +++ b/Script/Dialog/AddProduct2StorePopup.gd @@ -82,6 +82,12 @@ func _on_sell_num_changed(new_text: String): if char.is_valid_int(): filtered_text += char + # 检查是否超过最大值并自动修正 + if not filtered_text.is_empty(): + var quantity = filtered_text.to_int() + if quantity > current_max_count: + filtered_text = str(current_max_count) + if filtered_text != new_text: sell_num_input.text = filtered_text sell_num_input.caret_column = filtered_text.length() @@ -97,6 +103,12 @@ func _on_sell_price_changed(new_text: String): if char.is_valid_int(): filtered_text += char + # 检查是否超过最大值并自动修正 + if not filtered_text.is_empty(): + var price = filtered_text.to_int() + if price > 999999999: + filtered_text = "999999999" + if filtered_text != new_text: sell_price_input.text = filtered_text sell_price_input.caret_column = filtered_text.length() @@ -144,7 +156,9 @@ func get_sell_quantity() -> int: return 1 var quantity = text.to_int() - return max(1, quantity) # 至少出售1个 + quantity = max(1, quantity) # 至少出售1个 + quantity = min(quantity, current_max_count) # 不超过最大值 + return quantity # 获取出售价格 func get_sell_price() -> int: @@ -153,7 +167,9 @@ func get_sell_price() -> int: return current_suggested_price var price = text.to_int() - return max(1, price) # 至少1元 + price = max(1, price) # 至少1元 + price = min(price, 999999999) # 不超过最大值 + return price # 确认添加按钮处理 func _on_sure_button_pressed(): diff --git a/Script/Dialog/BatchBuyPopup.gd b/Script/Dialog/BatchBuyPopup.gd index 4b528a9..738ff09 100644 --- a/Script/Dialog/BatchBuyPopup.gd +++ b/Script/Dialog/BatchBuyPopup.gd @@ -11,6 +11,7 @@ var current_item_name: String = "" var current_item_cost: int = 0 var current_item_desc: String = "" var current_buy_type: String = "" # "seed" 或 "item" +var max_stock: int = 999999999 # 最大库存限制,默认无限制 # 回调函数,用于处理确认购买 var confirm_callback: Callable @@ -41,13 +42,14 @@ func _on_visibility_changed(): pass # 显示批量购买弹窗 -func show_buy_popup(item_name: String, item_cost: int, item_desc: String, buy_type: String, on_confirm: Callable, on_cancel: Callable = Callable()): +func show_buy_popup(item_name: String, item_cost: int, item_desc: String, buy_type: String, on_confirm: Callable, on_cancel: Callable = Callable(), stock_limit: int = 999999999): current_item_name = item_name current_item_cost = item_cost current_item_desc = item_desc current_buy_type = buy_type confirm_callback = on_confirm cancel_callback = on_cancel + max_stock = stock_limit # 设置弹窗内容 if buy_type == "seed": @@ -55,10 +57,15 @@ func show_buy_popup(item_name: String, item_cost: int, item_desc: String, buy_ty else: title.text = "批量购买道具" + # 添加库存信息到内容中 + var stock_info = "" + if stock_limit < 999999999: + stock_info = "\n当前库存: " + str(stock_limit) + " 个" + contents.text = str( "商品名称: " + item_name + "\n" + "单价: " + str(item_cost) + " 元\n" + - "描述: " + item_desc + "\n\n" + + "描述: " + item_desc + stock_info + "\n\n" + "请输入购买数量:" ) @@ -77,6 +84,14 @@ func _on_buy_num_changed(new_text: String): if char.is_valid_int(): filtered_text += char + # 检查是否超过库存限制并自动修正 + if not filtered_text.is_empty(): + var quantity = filtered_text.to_int() + if quantity > max_stock: + filtered_text = str(max_stock) + elif quantity > 999999999: + filtered_text = "999999999" + if filtered_text != new_text: buy_num_edit.text = filtered_text buy_num_edit.caret_column = filtered_text.length() @@ -118,6 +133,11 @@ func _on_sure_button_pressed(): _show_error("购买数量必须大于0") return + # 检查是否超过库存限制 + if quantity > max_stock: + _show_error("购买数量超过库存限制!最大可购买: " + str(max_stock)) + return + # 调用确认回调函数 if confirm_callback.is_valid(): confirm_callback.call(current_item_name, current_item_cost, quantity, current_buy_type) diff --git a/Script/Dialog/BatchSellPopup.gd b/Script/Dialog/BatchSellPopup.gd index cf1060a..54e53d4 100644 --- a/Script/Dialog/BatchSellPopup.gd +++ b/Script/Dialog/BatchSellPopup.gd @@ -78,6 +78,12 @@ func _on_sell_num_changed(new_text: String): if char.is_valid_int(): filtered_text += char + # 检查是否超过最大值并自动修正 + if not filtered_text.is_empty(): + var quantity = filtered_text.to_int() + if quantity > current_max_count: + filtered_text = str(current_max_count) + if filtered_text != new_text: sell_num_edit.text = filtered_text sell_num_edit.caret_column = filtered_text.length() @@ -94,7 +100,7 @@ func _update_total_income(): var quantity_status = "" if quantity > current_max_count: quantity_status = " (超出库存!)" - + #quantity = current_max_count var income_info = "\n出售数量: " + str(quantity) + " 个" + quantity_status + "\n总收入: " + str(total_income) + " 元" # 更新内容显示 @@ -115,7 +121,9 @@ func get_sell_quantity() -> int: return 1 var quantity = text.to_int() - return max(1, quantity) # 至少出售1个 + quantity = max(1, quantity) # 至少出售1个 + quantity = min(quantity, current_max_count) # 不超过最大值 + return quantity # 确认出售按钮处理 func _on_sure_button_pressed(): @@ -126,6 +134,7 @@ func _on_sure_button_pressed(): return if quantity > current_max_count: + #quantity = current_max_count _show_error("出售数量不能超过库存数量(" + str(current_max_count) + ")") return @@ -151,4 +160,4 @@ func _show_error(message: String): if has_node("/root/Toast"): get_node("/root/Toast").show(message, Color.RED, 2.0, 1.0) else: - print("批量出售弹窗错误: " + message) + print("批量出售弹窗错误: " + message) diff --git a/Server/ConsoleCommandsAPI.py b/Server/ConsoleCommandsAPI.py index 982f615..2fe9127 100644 --- a/Server/ConsoleCommandsAPI.py +++ b/Server/ConsoleCommandsAPI.py @@ -32,6 +32,10 @@ class ConsoleCommandsAPI: "lsplayer": self.cmd_list_players, # 列出所有玩家 "playerinfo": self.cmd_player_info, # 查看玩家信息 "resetland": self.cmd_reset_land, # 重置玩家土地 + "repasswd": self.cmd_reset_password, # 重置玩家密码 + "rename": self.cmd_rename_player, # 重命名玩家昵称 + "refarmname": self.cmd_rename_farm, # 重命名农场名称 + "ban": self.cmd_ban_player, # 踢出玩家 "weather": self.cmd_weather, # 设置天气 "help": self.cmd_help, # 显示帮助信息 "stop": self.cmd_stop, # 停止服务器 @@ -392,6 +396,175 @@ class ConsoleCommandsAPI: else: print(" 当前无在线客户端") + def cmd_reset_password(self, args: List[str]): + """重置玩家密码命令: /repasswd QQ号""" + if len(args) != 1: + print("❌ 用法: /repasswd ") + return + + qq_number = args[0] + + # 加载玩家数据 + player_data = self.server.load_player_data(qq_number) + if not player_data: + print(f"❌ 玩家 {qq_number} 不存在") + return + + # 重置密码为123456 + old_password = player_data.get("玩家密码", "未设置") + player_data["玩家密码"] = "123456" + + # 保存数据 + success = self.server.save_player_data(qq_number, player_data) + if success: + print(f"✅ 已重置玩家 {qq_number} 的密码") + print(f" 新密码: 123456") + else: + print(f"❌ 重置玩家 {qq_number} 密码失败") + + def cmd_rename_player(self, args: List[str]): + """重命名玩家昵称命令: /rename QQ号 新昵称""" + if len(args) != 2: + print("❌ 用法: /rename <新昵称>") + return + + qq_number, new_nickname = args + + # 加载玩家数据 + player_data = self.server.load_player_data(qq_number) + if not player_data: + print(f"❌ 玩家 {qq_number} 不存在") + return + + # 修改玩家昵称 + old_nickname = player_data.get("玩家昵称", "未设置") + player_data["玩家昵称"] = new_nickname + + # 保存数据 + success = self.server.save_player_data(qq_number, player_data) + if success: + print(f"✅ 已重命名玩家 {qq_number} 的昵称") + print(f" 原昵称: {old_nickname} → 新昵称: {new_nickname}") + else: + print(f"❌ 重命名玩家 {qq_number} 昵称失败") + + def cmd_rename_farm(self, args: List[str]): + """重命名农场名称命令: /refarmname QQ号 新农场名称""" + if len(args) != 2: + print("❌ 用法: /refarmname <新农场名称>") + return + + qq_number, new_farm_name = args + + # 加载玩家数据 + player_data = self.server.load_player_data(qq_number) + if not player_data: + print(f"❌ 玩家 {qq_number} 不存在") + return + + # 修改农场名称 + old_farm_name = player_data.get("农场名称", "未设置") + player_data["农场名称"] = new_farm_name + + # 保存数据 + success = self.server.save_player_data(qq_number, player_data) + if success: + print(f"✅ 已重命名玩家 {qq_number} 的农场名称") + print(f" 原农场名: {old_farm_name} → 新农场名: {new_farm_name}") + else: + print(f"❌ 重命名玩家 {qq_number} 农场名称失败") + + def cmd_ban_player(self, args: List[str]): + """踢出玩家命令: /ban QQ号 [时长] [原因]""" + if len(args) < 1 or len(args) > 3: + print("❌ 用法: /ban [时长(秒)] [原因]") + print(" 时长默认为0秒(立即可重新登录),原因默认为'您已被管理员踢出服务器'") + return + + qq_number = args[0] + ban_duration = 0 # 默认0秒 + ban_reason = "您已被管理员踢出服务器" # 默认原因 + + # 解析时长参数 + if len(args) >= 2: + try: + ban_duration = int(args[1]) + if ban_duration < 0: + print("❌ 踢出时长不能为负数") + return + except ValueError: + print("❌ 踢出时长必须是整数(秒)") + return + + # 解析原因参数 + if len(args) >= 3: + ban_reason = args[2] + + # 加载玩家数据 + player_data = self.server.load_player_data(qq_number) + if not player_data: + print(f"❌ 玩家 {qq_number} 不存在") + return + + # 计算禁止登录时间 + current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + ban_end_time = "" + if ban_duration > 0: + from datetime import timedelta + end_datetime = datetime.now() + timedelta(seconds=ban_duration) + ban_end_time = end_datetime.strftime("%Y-%m-%d %H:%M:%S") + + # 设置禁用系统 + if "禁用系统" not in player_data: + player_data["禁用系统"] = {} + + player_data["禁用系统"] = { + "是否被禁止登录": ban_duration > 0, + "禁止登录原因": ban_reason, + "禁止登录开始": current_time, + "禁止登录截止": ban_end_time + } + + # 保存数据 + success = self.server.save_player_data(qq_number, player_data) + if not success: + print(f"❌ 保存玩家 {qq_number} 数据失败") + return + + # 如果玩家在线,强制下线 + kicked_online = False + if hasattr(self.server, 'user_data'): + for client_id, user_info in self.server.user_data.items(): + if user_info.get("username") == qq_number and user_info.get("logged_in", False): + # 发送踢出消息 + kick_message = { + "type": "kick_notification", + "reason": ban_reason, + "duration": ban_duration + } + self.server.send_data(client_id, kick_message) + + # 断开连接 + if hasattr(self.server, 'disconnect_client'): + self.server.disconnect_client(client_id) + + kicked_online = True + break + + # 输出结果 + if ban_duration > 0: + print(f"✅ 已踢出玩家 {qq_number},禁止登录 {ban_duration} 秒") + print(f" 踢出原因: {ban_reason}") + print(f" 禁止登录至: {ban_end_time}") + else: + print(f"✅ 已踢出玩家 {qq_number},可立即重新登录") + print(f" 踢出原因: {ban_reason}") + + if kicked_online: + print(f" 玩家已在线,已强制下线") + else: + print(f" 玩家当前不在线") + def cmd_help(self, args: List[str]): """显示帮助信息""" print("🌱 萌芽农场服务器控制台命令帮助") @@ -404,6 +577,10 @@ class ConsoleCommandsAPI: print(" /lsplayer - 列出所有已注册玩家") print(" /playerinfo - 查看玩家详细信息") print(" /resetland - 重置玩家土地状态") + print(" /repasswd - 重置玩家密码为123456") + print(" /rename <新昵称> - 重命名玩家昵称") + print(" /refarmname <新农场名> - 重命名农场名称") + print(" /ban [时长] [原因] - 踢出玩家(时长秒,原因可选)") print("") print("游戏控制命令:") print(" /weather <类型> - 控制全服天气") diff --git a/Server/QQEmailSendAPI.py b/Server/QQEmailSendAPI.py index 426288b..0da7ae9 100644 --- a/Server/QQEmailSendAPI.py +++ b/Server/QQEmailSendAPI.py @@ -31,6 +31,7 @@ class QQMailAPI: self.smtp_server = 'smtp.qq.com' self.smtp_port = 465 # SSL端口 + # 发送纯文本邮件 def send_text_email(self, receiver_email, subject, content, cc_emails=None): """ 发送纯文本邮件 @@ -65,6 +66,7 @@ class QQMailAPI: print(f"邮件发送失败:{str(e)}") return False + # 发送HTML格式邮件,可带附件 def send_html_email(self, receiver_email, subject, html_content, cc_emails=None, attachments=None): """ 发送HTML格式邮件,可带附件 @@ -115,6 +117,8 @@ class QQMailAPI: return False class EmailVerification: + + #生成指定长度的随机验证码 @staticmethod def generate_verification_code(length=6): """ @@ -130,6 +134,7 @@ class EmailVerification: chars = string.ascii_uppercase + string.digits return ''.join(random.choice(chars) for _ in range(length)) + #发送验证码邮件到QQ邮箱 @staticmethod def send_verification_email(qq_number, verification_code, email_type="register"): """ @@ -192,6 +197,7 @@ class EmailVerification: except Exception as e: return False, f"发送验证码失败: {str(e)}" + #保存验证码到MongoDB(优先)或缓存文件(备用) @staticmethod def save_verification_code(qq_number, verification_code, expiry_time=300, code_type="register"): """ @@ -266,6 +272,7 @@ class EmailVerification: print(f"保存验证码失败: {str(e)}") return False + #验证用户输入的验证码(优先使用MongoDB) @staticmethod def verify_code(qq_number, input_code, code_type="register"): """ @@ -366,6 +373,7 @@ class EmailVerification: print(f"[验证码系统-JSON] QQ {qq_number} 验证失败: 验证码不匹配") return False, "验证码错误" + #清理过期的验证码和已使用的验证码(优先使用MongoDB) @staticmethod def clean_expired_codes(): """ @@ -435,6 +443,7 @@ class EmailVerification: except Exception as e: print(f"清理验证码失败: {str(e)}") + #获取验证码状态(优先使用MongoDB) @staticmethod def get_verification_status(qq_number): """ diff --git a/Server/SMYMongoDBAPI.py b/Server/SMYMongoDBAPI.py index 69dce69..3d6cbb6 100644 --- a/Server/SMYMongoDBAPI.py +++ b/Server/SMYMongoDBAPI.py @@ -14,6 +14,30 @@ from datetime import datetime, timedelta from bson import ObjectId class SMYMongoDBAPI: + + # ===================== 配置系统常量 ===================== + #游戏配置JSON文档id + CONFIG_IDS = { + "daily_checkin": "687cce278e77ba00a7414ba2",#每日签到配置 + "lucky_draw": "687cd52e8e77ba00a7414ba3",#幸运抽奖配置 + "new_player": "687cdbd78e77ba00a7414ba4",#新玩家配置 + "wisdom_tree": "687cdfbe8e77ba00a7414ba5",#智慧树配置 + "online_gift": "687ce7678e77ba00a7414ba6",#在线礼包配置 + "scare_crow": "687cea258e77ba00a7414ba8",# 稻草人系统配置 + "item": "687cf17c8e77ba00a7414baa",# 道具系统配置 + "pet": "687cf59b8e77ba00a7414bab",# 宠物系统配置 + "stamina": "687cefba8e77ba00a7414ba9",# 体力值系统配置 + "crop_data": "687cfb3d8e77ba00a7414bac",# 作物系统配置 + "initial_player_data": "687e2f3f8e77ba00a7414bb0",# 初始玩家数据配置 + "verification_codes": "687e35078e77ba00a7414bb1",# 验证码配置 + "game_tips": "687e40008e77ba00a7414bb2",# 游戏小提示配置 + } + # ===================== 配置系统常量 ===================== + + + + + #初始化MongoDB API def __init__(self, environment: str = "test"): """ 初始化MongoDB API @@ -51,6 +75,7 @@ class SMYMongoDBAPI: # 连接数据库 self.connect() + # 连接到MongoDB数据库 def connect(self) -> bool: """ 连接到MongoDB数据库 @@ -93,6 +118,7 @@ class SMYMongoDBAPI: self.connected = False return False + # 断开数据库连接 def disconnect(self): """断开数据库连接""" if self.client: @@ -100,10 +126,12 @@ class SMYMongoDBAPI: self.connected = False self.logger.info("已断开MongoDB连接") + # 检查数据库连接状态 def is_connected(self) -> bool: """检查是否已连接到数据库""" return self.connected and self.client is not None + # 获取集合对象 def get_collection(self, collection_name: str): """ 获取集合对象 @@ -120,6 +148,7 @@ class SMYMongoDBAPI: # ========================= 游戏配置管理 ========================= + # 获取游戏配置(通过ObjectId) def _get_config_by_id(self, object_id: str, config_name: str) -> Optional[Dict[str, Any]]: """ 通用方法:根据ObjectId获取配置 @@ -139,11 +168,13 @@ class SMYMongoDBAPI: result = collection.find_one({"_id": oid}) if result: - # 移除MongoDB的_id字段和updated_at字段 + # 移除MongoDB的_id字段、updated_at字段和config_type字段 if "_id" in result: del result["_id"] if "updated_at" in result: del result["updated_at"] + if "config_type" in result: + del result["config_type"] self.logger.info(f"成功获取{config_name}") return result @@ -155,6 +186,7 @@ class SMYMongoDBAPI: self.logger.error(f"获取{config_name}失败: {e}") return None + # 更新游戏配置(通过ObjectId) def _update_config_by_id(self, object_id: str, config_data: Dict[str, Any], config_name: str) -> bool: """ 通用方法:根据ObjectId更新配置 @@ -179,10 +211,14 @@ class SMYMongoDBAPI: **config_data } - result = collection.replace_one({"_id": oid}, update_data) + # 使用upsert=True来创建或更新文档 + result = collection.replace_one({"_id": oid}, update_data, upsert=True) - if result.acknowledged and result.matched_count > 0: - self.logger.info(f"成功更新{config_name}") + if result.acknowledged: + if result.matched_count > 0: + self.logger.info(f"成功更新{config_name}") + elif result.upserted_id: + self.logger.info(f"成功创建{config_name}") return True else: self.logger.error(f"更新{config_name}失败") @@ -192,6 +228,7 @@ class SMYMongoDBAPI: self.logger.error(f"更新{config_name}异常: {e}") return False + # 获取游戏配置(通过config_type) def get_game_config(self, config_type: str) -> Optional[Dict[str, Any]]: """ 获取游戏配置(通过config_type) @@ -225,6 +262,8 @@ class SMYMongoDBAPI: except Exception as e: self.logger.error(f"获取游戏配置失败 [{config_type}]: {e}") return None + + # 更新游戏配置(通过config_type) def set_game_config(self, config_type: str, config_data: Dict[str, Any]) -> bool: """ 设置游戏配置 @@ -260,23 +299,10 @@ class SMYMongoDBAPI: except Exception as e: self.logger.error(f"设置游戏配置异常 [{config_type}]: {e}") return False + # ========================= 游戏配置管理 ========================= + - # ===================== 配置系统常量 ===================== - CONFIG_IDS = { - "daily_checkin": "687cce278e77ba00a7414ba2", - "lucky_draw": "687cd52e8e77ba00a7414ba3", - "new_player": "687cdbd78e77ba00a7414ba4", - "wisdom_tree": "687cdfbe8e77ba00a7414ba5", - "online_gift": "687ce7678e77ba00a7414ba6", - "scare_crow": "687cea258e77ba00a7414ba8", - "item": "687cf17c8e77ba00a7414baa", - "pet": "687cf59b8e77ba00a7414bab", - "stamina": "687cefba8e77ba00a7414ba9", - "crop_data": "687cfb3d8e77ba00a7414bac", - "initial_player_data": "687e2f3f8e77ba00a7414bb0", - "verification_codes": "687e35078e77ba00a7414bb1" - } #=====================每日签到系统====================== def get_daily_checkin_config(self) -> Optional[Dict[str, Any]]: @@ -399,6 +425,18 @@ class SMYMongoDBAPI: #=====================初始玩家数据模板系统====================== + #=====================游戏小提示系统====================== + def get_game_tips_config(self) -> Optional[Dict[str, Any]]: + """获取游戏小提示配置""" + return self._get_config_by_id(self.CONFIG_IDS["game_tips"], "游戏小提示配置") + + def update_game_tips_config(self, config_data: Dict[str, Any]) -> bool: + """更新游戏小提示配置""" + return self._update_config_by_id(self.CONFIG_IDS["game_tips"], config_data, "游戏小提示配置") + #=====================游戏小提示系统====================== + + + #批量更新离线玩家的作物生长(优化版本,支持完整的加速效果计算) def batch_update_offline_players_crops(self, growth_multiplier: float = 1.0, exclude_online_players: List[str] = None) -> int: """ 批量更新离线玩家的作物生长(优化版本,支持完整的加速效果计算) @@ -531,6 +569,7 @@ class SMYMongoDBAPI: self.logger.error(f"批量更新离线玩家作物失败: {e}") return 0 + #检查玩家是否在新玩家奖励期内(注册后3天内享受10倍生长速度) def _is_new_player_bonus_active(self, register_time_str: str) -> bool: """ 检查玩家是否在新玩家奖励期内(注册后3天内享受10倍生长速度) @@ -562,13 +601,10 @@ class SMYMongoDBAPI: except ValueError as e: self.logger.warning(f"解析注册时间格式错误: {register_time_str}, 错误: {str(e)}") return False - - # 注意:get_offline_players_with_crops 方法已被移除 - # 现在使用优化的 batch_update_offline_players_crops 方法直接在 MongoDB 中处理查询和更新 - #=====================玩家数据管理====================== + # ========================= 验证码系统 ========================= - + # 保存验证码到MongoDB def save_verification_code(self, qq_number: str, verification_code: str, expiry_time: int = 300, code_type: str = "register") -> bool: """ @@ -617,8 +653,8 @@ class SMYMongoDBAPI: self.logger.error(f"保存验证码异常 [QQ {qq_number}]: {e}") return False - def verify_verification_code(self, qq_number: str, input_code: str, - code_type: str = "register") -> tuple[bool, str]: + #验证用户输入的验证码 + def verify_verification_code(self, qq_number: str, input_code: str, code_type: str = "register") -> tuple[bool, str]: """ 验证用户输入的验证码 @@ -671,6 +707,7 @@ class SMYMongoDBAPI: self.logger.error(f"验证验证码异常 [QQ {qq_number}]: {e}") return False, "验证码验证失败" + #清理过期的验证码和已使用的验证码 def clean_expired_verification_codes(self) -> int: """ 清理过期的验证码和已使用的验证码 @@ -708,10 +745,12 @@ class SMYMongoDBAPI: self.logger.error(f"清理验证码异常: {e}") return 0 - #=====================验证码系统====================== + # ========================= 验证码系统 ========================= - # ========================= 通用数据库操作 ========================= - + + + #========================玩家数据管理========================== + #获取玩家数据 def get_player_data(self, account_id: str) -> Optional[Dict[str, Any]]: """获取玩家数据 @@ -746,6 +785,7 @@ class SMYMongoDBAPI: self.logger.error(f"获取玩家数据失败 [{account_id}]: {e}") return None + #保存玩家数据 def save_player_data(self, account_id: str, player_data: Dict[str, Any]) -> bool: """保存玩家数据 @@ -780,6 +820,7 @@ class SMYMongoDBAPI: self.logger.error(f"保存玩家数据异常 [{account_id}]: {e}") return False + #删除玩家数据 def delete_player_data(self, account_id: str) -> bool: """删除玩家数据 @@ -806,6 +847,7 @@ class SMYMongoDBAPI: self.logger.error(f"删除玩家数据异常 [{account_id}]: {e}") return False + #获取所有玩家的基本信息 def get_all_players_basic_info(self, projection: Dict[str, int] = None) -> List[Dict[str, Any]]: """获取所有玩家的基本信息(优化版本,用于排行榜等) @@ -848,6 +890,7 @@ class SMYMongoDBAPI: self.logger.error(f"获取玩家基本信息失败: {e}") return [] + #根据条件获取玩家数据 def get_players_by_condition(self, condition: Dict[str, Any], projection: Dict[str, int] = None, limit: int = 0) -> List[Dict[str, Any]]: @@ -886,6 +929,7 @@ class SMYMongoDBAPI: self.logger.error(f"根据条件获取玩家数据失败: {e}") return [] + #获取长时间离线的玩家 def get_offline_players(self, offline_days: int = 3) -> List[Dict[str, Any]]: """获取长时间离线的玩家(用于杂草生长等) @@ -931,6 +975,7 @@ class SMYMongoDBAPI: self.logger.error(f"获取离线玩家失败: {e}") return [] + #检查玩家是否离线超过指定天数 def _is_player_offline_by_time(self, last_login_str: str, offline_days: int) -> bool: """检查玩家是否离线超过指定天数""" try: @@ -949,6 +994,7 @@ class SMYMongoDBAPI: except Exception: return False + #递归转换数据中的datetime对象为字符串 def _convert_datetime_to_string(self, data: Any) -> Any: """ 递归转换数据中的datetime对象为字符串 @@ -970,6 +1016,7 @@ class SMYMongoDBAPI: else: return data + #统计玩家总数 def count_total_players(self) -> int: """统计玩家总数 @@ -987,6 +1034,7 @@ class SMYMongoDBAPI: self.logger.error(f"统计玩家总数失败: {e}") return 0 + #更新玩家的特定字段 def update_player_field(self, account_id: str, field_updates: Dict[str, Any]) -> bool: """更新玩家的特定字段 @@ -1019,153 +1067,11 @@ class SMYMongoDBAPI: except Exception as e: self.logger.error(f"更新玩家字段异常 [{account_id}]: {e}") return False - #=====================玩家数据管理====================== + #========================玩家数据管理========================== - # ========================= 验证码系统 ========================= - - def save_verification_code(self, qq_number: str, verification_code: str, - expiry_time: int = 300, code_type: str = "register") -> bool: - """ - 保存验证码到MongoDB - - Args: - qq_number: QQ号 - verification_code: 验证码 - expiry_time: 过期时间(秒),默认5分钟 - code_type: 验证码类型,"register" 或 "reset_password" - - Returns: - bool: 保存成功返回True,否则返回False - """ - try: - import time - from datetime import datetime, timedelta - - collection = self.get_collection("verification_codes") - - # 计算过期时间 - expire_at = datetime.now() + timedelta(seconds=expiry_time) - - # 验证码文档 - verification_doc = { - "qq_number": qq_number, - "code": verification_code, - "code_type": code_type, - "created_at": datetime.now(), - "expire_at": expire_at, - "used": False - } - - # 使用upsert更新或插入(覆盖同一QQ号的旧验证码) - query = {"qq_number": qq_number, "code_type": code_type} - result = collection.replace_one(query, verification_doc, upsert=True) - - if result.acknowledged: - self.logger.info(f"成功保存验证码: QQ {qq_number}, 类型 {code_type}") - return True - else: - self.logger.error(f"保存验证码失败: QQ {qq_number}") - return False - - except Exception as e: - self.logger.error(f"保存验证码异常 [QQ {qq_number}]: {e}") - return False - - def verify_verification_code(self, qq_number: str, input_code: str, - code_type: str = "register") -> tuple: - """ - 验证用户输入的验证码 - - Args: - qq_number: QQ号 - input_code: 用户输入的验证码 - code_type: 验证码类型,"register" 或 "reset_password" - - Returns: - tuple: (验证成功, 消息) - """ - try: - from datetime import datetime - - collection = self.get_collection("verification_codes") - - # 查找验证码 - query = {"qq_number": qq_number, "code_type": code_type} - code_doc = collection.find_one(query) - - if not code_doc: - return False, "验证码不存在,请重新获取" - - # 检查是否已使用 - if code_doc.get("used", False): - return False, "验证码已使用,请重新获取" - - # 检查是否过期 - if datetime.now() > code_doc.get("expire_at", datetime.now()): - return False, "验证码已过期,请重新获取" - - # 验证码码 - if input_code.upper() != code_doc.get("code", "").upper(): - return False, "验证码错误,请重新输入" - - # 标记为已使用 - update_result = collection.update_one( - query, - {"$set": {"used": True, "used_at": datetime.now()}} - ) - - if update_result.acknowledged: - self.logger.info(f"验证码验证成功: QQ {qq_number}, 类型 {code_type}") - return True, "验证码验证成功" - else: - self.logger.error(f"标记验证码已使用失败: QQ {qq_number}") - return False, "验证码验证失败" - - except Exception as e: - self.logger.error(f"验证验证码异常 [QQ {qq_number}]: {e}") - return False, "验证码验证失败" - - def clean_expired_verification_codes(self) -> int: - """ - 清理过期的验证码和已使用的验证码 - - Returns: - int: 清理的验证码数量 - """ - try: - from datetime import datetime, timedelta - - collection = self.get_collection("verification_codes") - - current_time = datetime.now() - one_hour_ago = current_time - timedelta(hours=1) - - # 删除条件:过期的验证码 或 已使用超过1小时的验证码 - delete_query = { - "$or": [ - {"expire_at": {"$lt": current_time}}, # 过期的 - {"used": True, "used_at": {"$lt": one_hour_ago}} # 已使用超过1小时的 - ] - } - - result = collection.delete_many(delete_query) - - if result.acknowledged: - deleted_count = result.deleted_count - self.logger.info(f"清理验证码完成: 删除了 {deleted_count} 个验证码") - return deleted_count - else: - self.logger.error("清理验证码失败") - return 0 - - except Exception as e: - self.logger.error(f"清理验证码异常: {e}") - return 0 - - #=====================验证码系统====================== # ========================= 通用数据库操作 ========================= - + #插入文档 def insert_document(self, collection_name: str, document: Dict[str, Any]) -> Optional[str]: """ 插入文档 @@ -1191,6 +1097,7 @@ class SMYMongoDBAPI: self.logger.error(f"插入文档失败 [{collection_name}]: {e}") return None + #查找文档 def find_documents(self, collection_name: str, query: Dict[str, Any] = None, limit: int = 0) -> List[Dict[str, Any]]: """ @@ -1220,6 +1127,9 @@ class SMYMongoDBAPI: for doc in documents: if "_id" in doc: doc["_id"] = str(doc["_id"]) + # 如果是gameconfig集合,移除config_type字段以防止客户端报错 + if collection_name == "gameconfig" and "config_type" in doc: + del doc["config_type"] # 转换datetime对象为字符串 documents = [self._convert_datetime_to_string(doc) for doc in documents] @@ -1230,6 +1140,7 @@ class SMYMongoDBAPI: self.logger.error(f"查找文档失败 [{collection_name}]: {e}") return [] + #更新文档 def update_document(self, collection_name: str, query: Dict[str, Any], update: Dict[str, Any]) -> bool: """ @@ -1253,6 +1164,7 @@ class SMYMongoDBAPI: self.logger.error(f"更新文档失败 [{collection_name}]: {e}") return False + #删除文档 def delete_document(self, collection_name: str, query: Dict[str, Any]) -> bool: """ 删除文档 @@ -1273,9 +1185,12 @@ class SMYMongoDBAPI: except Exception as e: self.logger.error(f"删除文档失败 [{collection_name}]: {e}") return False - + # ========================= 通用数据库操作 ========================= + + + # ========================= 聊天消息管理 ========================= - + #保存聊天消息到MongoDB(按天存储) def save_chat_message(self, username: str, player_name: str, content: str) -> bool: """ 保存聊天消息到MongoDB(按天存储) @@ -1341,6 +1256,7 @@ class SMYMongoDBAPI: self.logger.error(f"保存聊天消息异常: {e}") return False + #获取聊天历史消息(从按天存储的chat集合) def get_chat_history(self, days: int = 3, limit: int = 500) -> List[Dict[str, Any]]: """ 获取聊天历史消息(从按天存储的chat集合) @@ -1399,6 +1315,7 @@ class SMYMongoDBAPI: self.logger.error(f"获取聊天历史失败: {e}") return [] + #获取最新的聊天消息(从按天存储的chat集合) def get_latest_chat_message(self) -> Optional[Dict[str, Any]]: """ 获取最新的聊天消息(从按天存储的chat集合) @@ -1439,6 +1356,7 @@ class SMYMongoDBAPI: self.logger.error(f"获取最新聊天消息失败: {e}") return None + #删除旧的聊天消息(从按天存储的chat集合) def clean_old_chat_messages(self, keep_days: int = 30) -> int: """ 清理旧的聊天消息(从按天存储的chat集合) diff --git a/Server/SpecialFarm.py b/Server/SpecialFarm.py new file mode 100644 index 0000000..f6df522 --- /dev/null +++ b/Server/SpecialFarm.py @@ -0,0 +1,507 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +特殊农场管理系统 +作者: AI Assistant +功能: 管理特殊农场的自动种植和维护 +""" + +import time +import random +import logging +from datetime import datetime +from SMYMongoDBAPI import SMYMongoDBAPI +from bson import ObjectId + +#杂交农场666-种植杂交树1,杂交树2-每天0点种植 +#花卉农场520-随机种植各种花卉-星期一,星期三,星期五,星期日零点种植 +#瓜果农场333-随机种植各种瓜果-星期二,星期四,星期六零点种植 +#小麦谷222-全屏种植小麦-每天0点种植 +#稻香111-全屏种植稻谷-每天0点种植 +#幸运农场888-随机种植1-80个幸运草和幸运花-星期一零点种植 + + + +class SpecialFarmManager: + #初始化特殊农场管理器 + def __init__(self, environment=None): + """ + 初始化特殊农场管理器 + + Args: + environment: 环境类型,"test" 或 "production",如果为None则自动检测 + """ + # 如果没有指定环境,使用与TCPGameServer相同的环境检测逻辑 + if environment is None: + import os + if os.path.exists('/.dockerenv') or os.environ.get('PRODUCTION', '').lower() == 'true': + environment = "production" + else: + environment = "test" + + self.environment = environment + self.mongo_api = SMYMongoDBAPI(environment) + + # 设置日志 + logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(levelname)s - %(message)s', + handlers=[ + logging.FileHandler('special_farm.log', encoding='utf-8'), + logging.StreamHandler() + ] + ) + self.logger = logging.getLogger(__name__) + + # 特殊农场配置 + self.special_farms = { + "杂交农场": { + "object_id": "689b4b9286cf953f2f4e56ee", + "crops": ["杂交树1", "杂交树2"], + "description": "专门种植杂交树的特殊农场" + }, + "花卉农场": { + "object_id": "689bec6286cf953f2f4e56f1", + "crops": ["郁金香", "牵牛花", "百合花", "栀子花", "玫瑰花", "向日葵", "藏红花", "幸运花"], + "description": "盛产各种美丽花卉的特殊农场", + "plant_type": "random_flowers" # 标记为随机花卉种植类型 + }, + "瓜果农场": { + "object_id": "689bf73886cf953f2f4e56fa", + "crops": ["西瓜", "南瓜", "哈密瓜", "葫芦", "黄瓜", "龙果", "菠萝", "芒果"], + "description": "盛产各种瓜果的农场", + "plant_type": "random_fruits" # 标记为随机瓜果种植类型 + }, + "小麦谷": { + "object_id": "689bf9a886cf953f2f4e56fd", + "crops": ["小麦"], + "description": "盛产小麦的农场", + "plant_type": "single_wheat" # 标记为单一小麦种植类型 + }, + "稻香": { + "object_id": "689bf9ac86cf953f2f4e56fe", + "crops": ["稻谷"], + "description": "盛产稻谷的农场", + "plant_type": "single_rice" # 标记为单一稻谷种植类型 + }, + "幸运农场": { + "object_id": "689c027886cf953f2f4e56ff", + "crops": ["幸运草", "幸运花"], + "description": "盛产幸运草和幸运花的农场", + "plant_type": "random_lucky" # 标记为随机幸运植物种植类型 + } + } + + self.logger.info(f"特殊农场管理器初始化完成 - 环境: {environment}") + + #获取作物系统数据 + def get_crop_data(self): + """ + 获取作物配置数据 + + Returns: + dict: 作物配置数据 + """ + try: + crop_config = self.mongo_api.get_crop_data_config() + if crop_config: + # 移除MongoDB相关字段 + if "_id" in crop_config: + del crop_config["_id"] + if "config_type" in crop_config: + del crop_config["config_type"] + if "updated_at" in crop_config: + del crop_config["updated_at"] + return crop_config + else: + self.logger.error("无法获取作物配置数据") + return {} + except Exception as e: + self.logger.error(f"获取作物配置数据时出错: {str(e)}") + return {} + + #通过文档ID获取农场数据 + def get_player_data_by_object_id(self, object_id): + """ + 通过ObjectId获取玩家数据 + + Args: + object_id: MongoDB文档ID + + Returns: + dict: 玩家数据,如果未找到返回None + """ + try: + collection = self.mongo_api.get_collection("playerdata") + oid = ObjectId(object_id) + player_data = collection.find_one({"_id": oid}) + + if player_data: + self.logger.info(f"成功获取玩家数据: {player_data.get('玩家昵称', 'Unknown')}") + return player_data + else: + self.logger.warning(f"未找到ObjectId为 {object_id} 的玩家数据") + return None + + except Exception as e: + self.logger.error(f"获取玩家数据时出错: {str(e)}") + return None + + #通过文档ID保存农场数据 + def save_player_data_by_object_id(self, object_id, player_data): + """ + 通过ObjectId保存玩家数据 + + Args: + object_id: MongoDB文档ID + player_data: 玩家数据 + + Returns: + bool: 是否保存成功 + """ + try: + collection = self.mongo_api.get_collection("playerdata") + oid = ObjectId(object_id) + + # 更新最后登录时间 + player_data["最后登录时间"] = datetime.now().strftime("%Y年%m月%d日%H时%M分%S秒") + + result = collection.replace_one({"_id": oid}, player_data) + + if result.acknowledged and result.matched_count > 0: + self.logger.info(f"成功保存玩家数据: {player_data.get('玩家昵称', 'Unknown')}") + return True + else: + self.logger.error(f"保存玩家数据失败: ObjectId {object_id}") + return False + + except Exception as e: + self.logger.error(f"保存玩家数据时出错: {str(e)}") + return False + + #在指定农场种植作物 + def plant_crops_in_farm(self, farm_name): + """ + 为指定特殊农场种植作物 + + Args: + farm_name: 农场名称 + + Returns: + bool: 是否种植成功 + """ + if farm_name not in self.special_farms: + self.logger.error(f"未知的特殊农场: {farm_name}") + return False + + farm_config = self.special_farms[farm_name] + object_id = farm_config["object_id"] + available_crops = farm_config["crops"] + + # 获取玩家数据 + player_data = self.get_player_data_by_object_id(object_id) + if not player_data: + return False + + # 获取作物配置 + crop_data = self.get_crop_data() + if not crop_data: + self.logger.error("无法获取作物配置,跳过种植") + return False + + # 检查作物是否存在 + for crop_name in available_crops: + if crop_name not in crop_data: + self.logger.error(f"作物 {crop_name} 不存在于作物配置中") + return False + + # 获取农场土地 + farm_lands = player_data.get("农场土地", []) + if not farm_lands: + self.logger.error(f"农场 {farm_name} 没有土地数据") + return False + + planted_count = 0 + plant_type = farm_config.get("plant_type", "normal") + + # 遍历所有土地,先开垦再种植作物 + for i, land in enumerate(farm_lands): + # 根据农场类型选择作物 + if plant_type == "random_flowers": + # 花卉农场:随机种植各种花卉,种满所有土地 + crop_name = random.choice(available_crops) + should_plant = True # 100%种植率,种满所有土地 + elif plant_type == "random_fruits": + # 瓜果农场:随机种植各种瓜果,种满所有土地 + crop_name = random.choice(available_crops) + should_plant = True # 100%种植率,种满所有土地 + elif plant_type == "single_wheat": + # 小麦谷:全屏种植小麦,种满所有土地 + crop_name = "小麦" + should_plant = True # 100%种植率,种满所有土地 + elif plant_type == "single_rice": + # 稻香:全屏种植稻谷,种满所有土地 + crop_name = "稻谷" + should_plant = True # 100%种植率,种满所有土地 + elif plant_type == "random_lucky": + # 幸运农场:随机种植1-80个幸运草和幸运花 + crop_name = random.choice(available_crops) + # 随机决定是否种植,确保总数在1-80之间 + target_plants = random.randint(1, min(80, len(farm_lands))) + should_plant = planted_count < target_plants + else: + # 普通农场:按原逻辑随机选择 + crop_name = random.choice(available_crops) + should_plant = True + + if should_plant: + crop_info = crop_data[crop_name] + + # 更新土地数据(先开垦,再种植) + land.update({ + "is_diged": True, # 确保土地已开垦 + "is_planted": True, + "crop_type": crop_name, + "grow_time": 0, # 立即成熟 + "max_grow_time": crop_info.get("生长时间", 21600), + "is_dead": False, + "已浇水": True, + "已施肥": True, + "土地等级": 0 + }) + + # 清除施肥时间戳 + if "施肥时间" in land: + del land["施肥时间"] + + planted_count += 1 + else: + # 留空的土地:只开垦不种植 + land.update({ + "is_diged": True, + "is_planted": False, + "crop_type": "", + "grow_time": 0, + "max_grow_time": 3, + "is_dead": False, + "已浇水": False, + "已施肥": False, + "土地等级": 0 + }) + + # 清除施肥时间戳 + if "施肥时间" in land: + del land["施肥时间"] + + # 保存玩家数据 + if self.save_player_data_by_object_id(object_id, player_data): + self.logger.info(f"成功为 {farm_name} 种植了 {planted_count} 块土地的作物") + return True + else: + self.logger.error(f"保存 {farm_name} 数据失败") + return False + + #每日维护任务 + def daily_maintenance(self): + """ + 每日维护任务 + """ + from datetime import datetime + + self.logger.info("开始执行特殊农场维护任务...") + + success_count = 0 + total_farms = 0 + current_time = datetime.now() + current_weekday = current_time.weekday() # 0=Monday, 1=Tuesday, ..., 6=Sunday + current_time_str = current_time.strftime("%Y-%m-%d %H:%M:%S") + + for farm_name in self.special_farms.keys(): + # 检查农场是否需要在今天维护 + should_maintain = True + + if farm_name == "瓜果农场": + # 瓜果农场只在星期二(1)、四(3)、六(5)维护 + if current_weekday not in [1, 3, 5]: + should_maintain = False + self.logger.info(f"瓜果农场今日({['周一','周二','周三','周四','周五','周六','周日'][current_weekday]})不需要维护") + elif farm_name == "幸运农场": + # 幸运农场只在星期一(0)维护 + if current_weekday != 0: + should_maintain = False + self.logger.info(f"幸运农场今日({['周一','周二','周三','周四','周五','周六','周日'][current_weekday]})不需要维护") + + if should_maintain: + total_farms += 1 + try: + if self.plant_crops_in_farm(farm_name): + success_count += 1 + self.logger.info(f"农场 {farm_name} 维护完成") + + # 更新维护时间记录 + try: + farm_config = self.special_farms[farm_name] + object_id = farm_config["object_id"] + player_data = self.get_player_data_by_object_id(object_id) + if player_data: + player_data["特殊农场最后维护时间"] = current_time_str + self.save_player_data_by_object_id(object_id, player_data) + except Exception as record_error: + self.logger.error(f"更新 {farm_name} 维护时间记录失败: {str(record_error)}") + else: + self.logger.error(f"农场 {farm_name} 维护失败") + except Exception as e: + self.logger.error(f"维护农场 {farm_name} 时出错: {str(e)}") + + self.logger.info(f"维护任务完成: {success_count}/{total_farms} 个农场维护成功") + + #启动定时任务调度器(后台线程模式) + def start_scheduler(self): + """ + 启动定时任务调度器(后台线程模式) + """ + import threading + + self.logger.info("特殊农场定时任务调度器已启动") + self.logger.info("维护任务将在每天凌晨0点执行") + + # 检查是否需要立即执行维护任务 + self._check_and_run_initial_maintenance() + + # 在后台线程中运行调度循环 + def scheduler_loop(): + last_maintenance_date = None + + while True: + try: + now = datetime.now() + current_date = now.strftime("%Y-%m-%d") + + # 检查是否到了零点且今天还没有执行过维护 + if (now.hour == 0 and now.minute == 0 and + last_maintenance_date != current_date): + self.logger.info("零点维护时间到,开始执行维护任务...") + self.daily_maintenance() + last_maintenance_date = current_date + + time.sleep(60) # 每分钟检查一次 + except Exception as e: + self.logger.error(f"调度器运行时出错: {str(e)}") + time.sleep(60) + + # 启动后台线程 + scheduler_thread = threading.Thread(target=scheduler_loop, daemon=True) + scheduler_thread.start() + self.logger.info("特殊农场调度器已在后台线程启动") + + #检查是否需要执行初始维护任务 + def _check_and_run_initial_maintenance(self): + """ + 检查是否需要执行初始维护任务 + 避免服务器重启时重复执行 + """ + try: + from datetime import datetime, timedelta + + # 检查今天是否已经执行过维护任务 + current_time = datetime.now() + today = current_time.strftime("%Y-%m-%d") + current_weekday = current_time.weekday() # 0=Monday, 1=Tuesday, ..., 6=Sunday + + # 获取特殊农场数据,检查最后维护时间 + for farm_name, farm_config in self.special_farms.items(): + object_id = farm_config["object_id"] + player_data = self.get_player_data_by_object_id(object_id) + + # 检查农场是否需要在今天维护 + should_maintain = True + if farm_name == "瓜果农场": + # 瓜果农场只在星期二(1)、四(3)、六(5)维护 + if current_weekday not in [1, 3, 5]: + should_maintain = False + self.logger.info(f"瓜果农场今日({['周一','周二','周三','周四','周五','周六','周日'][current_weekday]})不需要维护") + elif farm_name == "幸运农场": + # 幸运农场只在星期一(0)维护 + if current_weekday != 0: + should_maintain = False + self.logger.info(f"幸运农场今日({['周一','周二','周三','周四','周五','周六','周日'][current_weekday]})不需要维护") + + if should_maintain and player_data: + last_maintenance = player_data.get("特殊农场最后维护时间", "") + + # 如果今天还没有维护过,则执行维护 + if not last_maintenance or not last_maintenance.startswith(today): + self.logger.info(f"检测到 {farm_name} 今日尚未维护,执行维护任务...") + if self.plant_crops_in_farm(farm_name): + # 更新维护时间记录 + player_data["特殊农场最后维护时间"] = current_time.strftime("%Y-%m-%d %H:%M:%S") + self.save_player_data_by_object_id(object_id, player_data) + else: + self.logger.info(f"{farm_name} 今日已维护过,跳过初始维护") + + except Exception as e: + self.logger.error(f"检查初始维护任务时出错: {str(e)}") + # 如果检查失败,执行一次维护作为备用 + self.logger.info("执行备用维护任务...") + self.daily_maintenance() + + #停止定时任务调度器 + def stop_scheduler(self): + """ + 停止定时任务调度器 + """ + try: + # 设置停止标志(如果需要的话) + self.logger.info("特殊农场定时任务调度器已停止") + except Exception as e: + self.logger.error(f"停止定时任务调度器时出错: {str(e)}") + + #手动执行维护任务 + def manual_maintenance(self, farm_name=None): + """ + 手动执行维护任务 + + Args: + farm_name: 指定农场名称,如果为None则维护所有农场 + """ + if farm_name: + if farm_name in self.special_farms: + self.logger.info(f"手动维护农场: {farm_name}") + return self.plant_crops_in_farm(farm_name) + else: + self.logger.error(f"未知的农场名称: {farm_name}") + return False + else: + self.logger.info("手动维护所有特殊农场") + self.daily_maintenance() + return True + +def main(): + """ + 主函数 + """ + import sys + + # 检查命令行参数 + environment = "production" + if len(sys.argv) > 1: + if sys.argv[1] in ["test", "production"]: + environment = sys.argv[1] + else: + print("使用方法: python SpecialFarm.py [test|production]") + sys.exit(1) + + # 创建特殊农场管理器 + manager = SpecialFarmManager(environment) + + # 检查是否为手动模式 + if len(sys.argv) > 2 and sys.argv[2] == "manual": + # 手动执行维护 + farm_name = sys.argv[3] if len(sys.argv) > 3 else None + manager.manual_maintenance(farm_name) + else: + # 启动定时任务 + manager.start_scheduler() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/Server/TCPGameServer.py b/Server/TCPGameServer.py index 89abb90..fc1f2e9 100644 --- a/Server/TCPGameServer.py +++ b/Server/TCPGameServer.py @@ -12,6 +12,8 @@ import random from SMYMongoDBAPI import SMYMongoDBAPI #导入MongoDB数据库模块 from QQEmailSendAPI import EmailVerification#导入QQ邮箱发送模块 from ConsoleCommandsAPI import ConsoleCommandsAPI #导入控制台命令API模块 +from SpecialFarm import SpecialFarmManager #导入特殊农场管理系统 +from WSRemoteCmdApi import WSRemoteCmdApi #导入WebSocket远程命令API """ 萌芽农场TCP游戏服务器 @@ -24,7 +26,7 @@ from ConsoleCommandsAPI import ConsoleCommandsAPI #导入控制台命令API模 server_host: str = "0.0.0.0" server_port: int = 7070 buffer_size: int = 4096 -server_version: str = "2.0.1" +server_version: str = "2.2.0" class TCPGameServer(TCPServer): @@ -65,6 +67,15 @@ class TCPGameServer(TCPServer): self.log('INFO', f"萌芽农场TCP游戏服务器初始化完成 - 版本: {server_version}", 'SERVER') + # 清理配置缓存,确保使用最新的配置数据 + self._clear_config_cache() + + # 初始化特殊农场管理系统 + self._init_special_farm_manager() + + # 初始化WebSocket远程命令API + self._init_websocket_remote_api() + # 启动定时器 self.start_crop_growth_timer() self.start_weed_growth_timer() @@ -111,6 +122,47 @@ class TCPGameServer(TCPServer): self.weed_growth_probability = 0.3 # 每个空地长杂草的概率(30%) self.last_weed_check_time = time.time() # 上次检查杂草的时间 + #初始化特殊农场管理系统 + def _init_special_farm_manager(self): + """初始化特殊农场管理系统""" + try: + # 使用自动环境检测,确保与游戏服务器环境一致 + self.special_farm_manager = SpecialFarmManager() + + # 启动特殊农场定时任务 + self.special_farm_manager.start_scheduler() + + self.log('INFO', f"特殊农场管理系统初始化完成 - 环境: {self.special_farm_manager.environment}", 'SERVER') + + except Exception as e: + self.log('ERROR', f"特殊农场管理系统初始化失败: {str(e)}", 'SERVER') + self.special_farm_manager = None + + #初始化WebSocket远程命令API + def _init_websocket_remote_api(self): + """初始化WebSocket远程命令API服务器""" + try: + # 创建WebSocket远程命令API实例 + ws_host = "0.0.0.0" + ws_port = 7071 + auth_key = "mengya2024" # 可以从配置文件读取 + + self.ws_remote_api = WSRemoteCmdApi( + game_server=self, + host=ws_host, + port=ws_port, + auth_key=auth_key + ) + + # 启动WebSocket服务器 + self.ws_remote_api.start_server() + + self.log('INFO', f"WebSocket远程命令API初始化完成 - ws://{ws_host}:{ws_port}", 'SERVER') + + except Exception as e: + self.log('ERROR', f"WebSocket远程命令API初始化失败: {str(e)}", 'SERVER') + self.ws_remote_api = None + #设置游戏服务器日志配置 def _setup_game_server_logging(self): """设置游戏服务器日志配置,禁用父类重复输出""" @@ -230,6 +282,28 @@ class TCPGameServer(TCPServer): self.offline_crop_timer = None self.log('INFO', "离线作物更新定时器已停止", 'SERVER') + # 停止特殊农场管理系统 + if hasattr(self, 'special_farm_manager') and self.special_farm_manager: + try: + # 停止特殊农场定时任务 + self.special_farm_manager.stop_scheduler() + + # 清理特殊农场管理器引用 + self.special_farm_manager = None + + self.log('INFO', "特殊农场管理系统已停止", 'SERVER') + except Exception as e: + self.log('ERROR', f"停止特殊农场管理系统时出错: {str(e)}", 'SERVER') + + # 停止WebSocket远程命令API服务器 + if hasattr(self, 'ws_remote_api') and self.ws_remote_api: + try: + self.ws_remote_api.stop_server() + self.ws_remote_api = None + self.log('INFO', "WebSocket远程命令API服务器已停止", 'SERVER') + except Exception as e: + self.log('ERROR', f"停止WebSocket远程命令API服务器时出错: {str(e)}", 'SERVER') + # 显示服务器统计信息 stats = self.get_server_stats() self.log('INFO', f"服务器统计 - 在线玩家: {stats['online_players']}, 总连接: {stats['total_connections']}", 'SERVER') @@ -252,14 +326,6 @@ class TCPGameServer(TCPServer): self._update_player_logout_time(client_id, username) self.log('INFO', f"用户 {username} 登出", 'SERVER') - # 广播用户离开消息 - self.broadcast({ - "type": "user_left", - "user_id": client_id, - "timestamp": time.time(), - "remaining_users": len(self.clients) - 1 - }, exclude=[client_id]) - # 清理用户数据 if client_id in self.user_data: # 清理偷菜免被发现计数器 @@ -267,8 +333,20 @@ class TCPGameServer(TCPServer): del self.user_data[client_id] self.log('INFO', f"用户 {username} 已离开游戏", 'SERVER') - - super()._remove_client(client_id) + + # 先调用父类方法移除客户端,避免递归调用 + super()._remove_client(client_id) + + # 在客户端已移除后再广播用户离开消息,避免向已断开的客户端发送消息 + self.broadcast({ + "type": "user_left", + "user_id": client_id, + "timestamp": time.time(), + "remaining_users": len(self.clients) + }) + else: + # 如果客户端不在列表中,直接调用父类方法 + super()._remove_client(client_id) #==========================客户端连接管理========================== @@ -364,6 +442,12 @@ class TCPGameServer(TCPServer): return player_data, username, None #加载作物配置数据(优化版本) + def _clear_config_cache(self): + """清理配置缓存,强制重新加载""" + self.crop_data_cache = None + self.crop_data_cache_time = 0 + self.log('INFO', "配置缓存已清理", 'SERVER') + def _load_crop_data(self): """加载作物配置数据(从MongoDB,带缓存优化)""" current_time = time.time() @@ -468,20 +552,34 @@ class TCPGameServer(TCPServer): self.log('WARNING', 'MongoDB未配置或不可用,无法更新离线玩家作物', 'SERVER') return - # 获取当前在线玩家列表 - online_players = [] + # 获取需要排除的玩家列表(在线玩家 + 被访问的玩家) + exclude_players = [] + + # 添加在线玩家 for client_id, user_info in self.user_data.items(): if user_info.get("logged_in", False) and user_info.get("username"): - online_players.append(user_info["username"]) + exclude_players.append(user_info["username"]) - # 直接调用优化后的批量更新方法,传入在线玩家列表进行排除 + # 添加被访问的玩家(避免访问模式下的重复更新) + visited_players = set() + for client_id, user_info in self.user_data.items(): + if (user_info.get("logged_in", False) and + user_info.get("visiting_mode", False) and + user_info.get("visiting_target")): + visited_players.add(user_info["visiting_target"]) + + # 将被访问的玩家也加入排除列表 + exclude_players.extend(list(visited_players)) + + # 直接调用优化后的批量更新方法,传入排除玩家列表 + # 离线更新间隔为60秒,所以每次更新应该增长60秒 updated_count = self.mongo_api.batch_update_offline_players_crops( - growth_multiplier=1.0, - exclude_online_players=online_players + growth_multiplier=60.0, + exclude_online_players=exclude_players ) if updated_count > 0: - self.log('INFO', f"成功更新了 {updated_count} 个离线玩家的作物生长", 'SERVER') + self.log('INFO', f"成功更新了 {updated_count} 个离线玩家的作物生长(排除了 {len(exclude_players)} 个在线/被访问玩家)", 'SERVER') else: self.log('DEBUG', "没有离线玩家的作物需要更新", 'SERVER') @@ -496,26 +594,37 @@ class TCPGameServer(TCPServer): #作物生长更新系统 def update_crops_growth(self): """更新所有玩家的作物生长""" - # 更新在线玩家的作物 + # 收集所有需要更新的玩家(在线玩家 + 被访问的玩家) + players_to_update = set() + + # 添加在线玩家 for client_id, user_info in self.user_data.items(): - if not user_info.get("logged_in", False): - continue - - username = user_info.get("username") - if not username: - continue - + if user_info.get("logged_in", False) and user_info.get("username"): + players_to_update.add(user_info.get("username")) + + # 添加被访问的玩家(即使他们不在线) + for client_id, user_info in self.user_data.items(): + if user_info.get("logged_in", False) and user_info.get("visiting_mode", False): + visiting_target = user_info.get("visiting_target", "") + if visiting_target: + players_to_update.add(visiting_target) + + # 更新所有需要更新的玩家的作物 + for username in players_to_update: try: player_data = self.load_player_data(username) if not player_data: continue if self.update_player_crops(player_data, username): - self.save_player_data(username, player_data) - self._push_crop_update_to_player(username, player_data) + # 确保数据保存成功后才推送更新 + if self.save_player_data(username, player_data): + self._push_crop_update_to_player(username, player_data) + else: + self.log('ERROR', f"保存玩家 {username} 数据失败,跳过推送更新", 'SERVER') except Exception as e: - self.log('ERROR', f"更新在线玩家 {username} 作物时出错: {str(e)}", 'SERVER') + self.log('ERROR', f"更新玩家 {username} 作物时出错: {str(e)}", 'SERVER') #更新单个玩家的作物 def update_player_crops(self, player_data, account_id): @@ -593,6 +702,9 @@ class TCPGameServer(TCPServer): self._send_visiting_update(client_id, visiting_target) else: self._send_normal_update(client_id, player_data) + + # 检查是否有其他玩家正在访问这个玩家的农场 + self._push_update_to_visitors(account_id, player_data) #根据用户名查找客户端ID def _find_client_by_username(self, username): @@ -629,6 +741,31 @@ class TCPGameServer(TCPServer): "is_visiting": False } self.send_data(client_id, update_message) + + #向正在访问某个玩家农场的其他玩家推送更新 + def _push_update_to_visitors(self, target_username, target_player_data): + """向正在访问某个玩家农场的其他玩家推送更新""" + for visitor_client_id, visitor_info in self.user_data.items(): + if not visitor_info.get("logged_in", False): + continue + + visiting_mode = visitor_info.get("visiting_mode", False) + visiting_target = visitor_info.get("visiting_target", "") + + # 如果这个玩家正在访问目标玩家的农场,发送更新 + if visiting_mode and visiting_target == target_username: + target_client_id = self._find_client_by_username(target_username) + + update_message = { + "type": "crop_update", + "农场土地": target_player_data.get("农场土地", []), + "timestamp": time.time(), + "is_visiting": True, + "visited_player": target_username, + "target_online": target_client_id is not None + } + self.send_data(visitor_client_id, update_message) + self.log('DEBUG', f"向访问者 {visitor_info.get('username', 'unknown')} 推送 {target_username} 的农场更新", 'SERVER') #================================作物系统管理========================================= @@ -709,6 +846,8 @@ class TCPGameServer(TCPServer): return self._handle_item_config_request(client_id) elif message_type == "request_pet_config":#请求宠物配置数据 return self._handle_pet_config_request(client_id) + elif message_type == "request_game_tips_config":#请求游戏小提示配置数据 + return self._handle_game_tips_config_request(client_id) elif message_type == "visit_player":#拜访其他玩家农场 return self._handle_visit_player_request(client_id, message) elif message_type == "return_my_farm":#返回我的农场 @@ -769,7 +908,15 @@ class TCPGameServer(TCPServer): return self._handle_pet_battle_result(client_id, message) elif message_type == "today_divination":#今日占卜 return self._handle_today_divination(client_id, message) + elif message_type == "give_money":#送金币 + return self._handle_give_money_request(client_id, message) + elif message_type == "sync_bag_data":#同步背包数据 + return self._handle_sync_bag_data(client_id, message) #--------------------------------------------------------------------------- + + # 管理员操作相关 + elif message_type == "kick_player":#踢出玩家 + return self._handle_kick_player(client_id, message) elif message_type == "message":#处理聊天消息(暂未实现) return self._handle_chat_message(client_id, message) @@ -833,6 +980,60 @@ class TCPGameServer(TCPServer): player_data = self.load_player_data(username) if player_data and player_data.get("玩家密码") == password: + # 检查禁用系统 + ban_system = player_data.get("禁用系统", {}) + is_banned = ban_system.get("是否被禁止登录", False) + + if is_banned: + # 检查禁止登录是否已过期 + ban_end_time = ban_system.get("禁止登录截止", "") + if ban_end_time: + try: + end_datetime = datetime.datetime.strptime(ban_end_time, "%Y-%m-%d %H:%M:%S") + current_datetime = datetime.datetime.now() + + if current_datetime >= end_datetime: + # 禁止登录已过期,解除禁止 + player_data["禁用系统"] = { + "是否被禁止登录": False, + "禁止登录原因": "", + "禁止登录开始": "", + "禁止登录截止": "" + } + self.save_player_data(username, player_data) + self.log('INFO', f"用户 {username} 禁止登录已过期,自动解除", 'SERVER') + else: + # 仍在禁止期内 + ban_reason = ban_system.get("禁止登录原因", "您已被管理员禁止登录") + self.log('WARNING', f"用户 {username} 登录失败: 账号被禁止登录", 'SERVER') + response = { + "type": "login_response", + "status": "banned", + "message": ban_reason, + "ban_end_time": ban_end_time + } + return self.send_data(client_id, response) + except Exception as e: + self.log('ERROR', f"解析禁止登录时间出错: {e}", 'SERVER') + # 如果解析出错,仍然禁止登录 + ban_reason = ban_system.get("禁止登录原因", "您已被管理员禁止登录") + response = { + "type": "login_response", + "status": "banned", + "message": ban_reason + } + return self.send_data(client_id, response) + else: + # 永久禁止或没有截止时间 + ban_reason = ban_system.get("禁止登录原因", "您已被管理员禁止登录") + self.log('WARNING', f"用户 {username} 登录失败: 账号被永久禁止登录", 'SERVER') + response = { + "type": "login_response", + "status": "banned", + "message": ban_reason + } + return self.send_data(client_id, response) + # 登录成功 self.log('INFO', f"用户 {username} 登录成功", 'SERVER') @@ -2441,6 +2642,25 @@ class TCPGameServer(TCPServer): self.log('ERROR', f"从MongoDB加载宠物配置失败: {str(e)}", 'SERVER') return {} + def _load_game_tips_config(self): + """从MongoDB加载游戏小提示配置数据""" + try: + if not hasattr(self, 'mongo_api') or not self.mongo_api: + self.log('ERROR', 'MongoDB未配置或不可用,无法加载游戏小提示配置数据', 'SERVER') + return {} + + config = self.mongo_api.get_game_tips_config() + if config: + self.log('INFO', "成功从MongoDB加载游戏小提示配置", 'SERVER') + return config + else: + self.log('ERROR', "MongoDB中未找到游戏小提示配置", 'SERVER') + return {} + + except Exception as e: + self.log('ERROR', f"从MongoDB加载游戏小提示配置失败: {str(e)}", 'SERVER') + return {} + # 将巡逻宠物ID转换为完整宠物数据 def _convert_patrol_pets_to_full_data(self, player_data): """将存储的巡逻宠物ID转换为完整的宠物数据""" @@ -5228,8 +5448,27 @@ class TCPGameServer(TCPServer): "success": False, "message": "无法读取宠物配置数据" }) + #==========================道具配置数据处理========================== + #处理客户端请求游戏小提示配置数据 + def _handle_game_tips_config_request(self, client_id): + """处理客户端请求游戏小提示配置数据""" + game_tips_config = self._load_game_tips_config() + + if game_tips_config: + self.log('INFO', f"向客户端 {client_id} 发送游戏小提示配置数据", 'SERVER') + return self.send_data(client_id, { + "type": "game_tips_config_response", + "success": True, + "game_tips_config": game_tips_config + }) + else: + return self.send_data(client_id, { + "type": "game_tips_config_response", + "success": False, + "message": "无法读取游戏小提示配置数据" + }) #==========================升级土地处理========================== @@ -6325,10 +6564,19 @@ class TCPGameServer(TCPServer): "点赞数": target_player_data.get("点赞系统", {}).get("总点赞数", 0), # 添加点赞数 "最后登录时间": target_player_data.get("最后登录时间", "未知"), "总游玩时间": target_player_data.get("总游玩时间", "0时0分0秒"), - "total_likes": target_player_data.get("total_likes", 0) + "total_likes": target_player_data.get("total_likes", 0), + "访问系统": target_player_data.get("访问系统", { + "总访问人数": 0, + "今日访问人数": 0, + "访问记录": {} + }) # 添加访问系统数据 } current_username = self.user_data[client_id]["username"] + + # 更新被访问玩家的访问系统数据 + self._update_visit_system(target_username, current_username) + self.log('INFO', f"玩家 {current_username} 访问了玩家 {target_username} 的农场", 'SERVER') # 记录玩家的访问状态 @@ -6344,6 +6592,229 @@ class TCPGameServer(TCPServer): }) #==========================访问其他玩家农场处理========================== + #==========================访问系统处理========================== + def _update_visit_system(self, target_username, visitor_username): + """更新被访问玩家的访问系统数据""" + try: + # 加载被访问玩家的数据 + target_player_data = self.load_player_data(target_username) + if not target_player_data: + self.log('ERROR', f"无法加载被访问玩家 {target_username} 的数据", 'SERVER') + return + + # 获取访问者的昵称 + visitor_player_data = self.load_player_data(visitor_username) + visitor_nickname = visitor_player_data.get("玩家昵称", visitor_username) if visitor_player_data else visitor_username + + # 初始化访问系统(如果不存在) + if "访问系统" not in target_player_data: + target_player_data["访问系统"] = { + "总访问人数": 0, + "今日访问人数": 0, + "访问记录": {} + } + + visit_system = target_player_data["访问系统"] + + # 获取今日日期 + from datetime import datetime + today = datetime.now().strftime("%Y-%m-%d") + + # 更新总访问人数 + visit_system["总访问人数"] = visit_system.get("总访问人数", 0) + 1 + + # 检查是否需要重置今日访问人数(新的一天) + last_visit_date = visit_system.get("最后访问日期", "") + if last_visit_date != today: + visit_system["今日访问人数"] = 0 + visit_system["最后访问日期"] = today + + # 更新今日访问人数 + visit_system["今日访问人数"] = visit_system.get("今日访问人数", 0) + 1 + + # 更新访问记录 + if "访问记录" not in visit_system: + visit_system["访问记录"] = {} + + if today not in visit_system["访问记录"]: + visit_system["访问记录"][today] = [] + + # 添加访问者昵称到今日访问记录(避免重复) + if visitor_nickname not in visit_system["访问记录"][today]: + visit_system["访问记录"][today].append(visitor_nickname) + + # 保存更新后的数据 + if self.save_player_data(target_username, target_player_data): + self.log('INFO', f"成功更新玩家 {target_username} 的访问系统数据,访问者: {visitor_nickname}", 'SERVER') + else: + self.log('ERROR', f"保存玩家 {target_username} 的访问系统数据失败", 'SERVER') + + except Exception as e: + self.log('ERROR', f"更新访问系统数据时出错: {e}", 'SERVER') + + def _reset_daily_visit_count(self): + """重置所有玩家的今日访问人数(凌晨调用)""" + try: + # 获取所有玩家的基本信息 + if hasattr(self, 'mongo_api') and self.mongo_api: + players_info = self.mongo_api.get_all_players_basic_info() + + from datetime import datetime + today = datetime.now().strftime("%Y-%m-%d") + + reset_count = 0 + for player_info in players_info: + username = player_info.get("玩家账号") + if username: + player_data = self.load_player_data(username) + if player_data and "访问系统" in player_data: + visit_system = player_data["访问系统"] + last_visit_date = visit_system.get("最后访问日期", "") + + # 如果不是今天,重置今日访问人数 + if last_visit_date != today: + visit_system["今日访问人数"] = 0 + visit_system["最后访问日期"] = today + + if self.save_player_data(username, player_data): + reset_count += 1 + + self.log('INFO', f"成功重置了 {reset_count} 个玩家的今日访问人数", 'SERVER') + + except Exception as e: + self.log('ERROR', f"重置今日访问人数时出错: {e}", 'SERVER') + + def _handle_give_money_request(self, client_id, message): + """处理送金币请求""" + try: + # 获取发送者信息 + sender_info = self.user_data.get(client_id) + if not sender_info or not sender_info.get("logged_in", False): + self.send_data(client_id, { + "type": "give_money_response", + "success": False, + "message": "请先登录" + }) + return + + sender_username = sender_info.get("username") + target_username = message.get("target_username", "") + amount = message.get("amount", 0) + + # 验证参数 + if not target_username: + self.send_data(client_id, { + "type": "give_money_response", + "success": False, + "message": "目标玩家用户名不能为空" + }) + return + + if amount != 500: + self.send_data(client_id, { + "type": "give_money_response", + "success": False, + "message": "每次只能送500金币" + }) + return + + if sender_username == target_username: + self.send_data(client_id, { + "type": "give_money_response", + "success": False, + "message": "不能给自己送金币" + }) + return + + # 加载发送者数据 + sender_data = self.load_player_data(sender_username) + if not sender_data: + self.send_data(client_id, { + "type": "give_money_response", + "success": False, + "message": "无法加载发送者数据" + }) + return + + # 检查发送者金币是否足够 + sender_money = sender_data.get("钱币", 0) + if sender_money < amount: + self.send_data(client_id, { + "type": "give_money_response", + "success": False, + "message": f"您的金币不足,当前拥有{sender_money}金币" + }) + return + + # 加载接收者数据 + target_data = self.load_player_data(target_username) + if not target_data: + self.send_data(client_id, { + "type": "give_money_response", + "success": False, + "message": "目标玩家不存在" + }) + return + + # 执行金币转移 + sender_data["钱币"] = sender_money - amount + target_data["钱币"] = target_data.get("钱币", 0) + amount + + # 记录送金币日志 + from datetime import datetime + current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + log_message = f"[{current_time}] {sender_username} 送给 {target_username} {amount}金币" + self.log('INFO', log_message, 'GIVE_MONEY') + + # 保存数据 + if self.save_player_data(sender_username, sender_data) and self.save_player_data(target_username, target_data): + # 获取目标玩家昵称 + target_nickname = target_data.get("玩家昵称", target_username) + + # 发送成功响应 + self.send_data(client_id, { + "type": "give_money_response", + "success": True, + "message": f"成功送给 {target_nickname} {amount}金币!", + "updated_data": { + "钱币": sender_data["钱币"] + }, + "target_updated_data": { + "钱币": target_data["钱币"] + } + }) + + # 如果目标玩家在线,通知他们收到金币 + target_client_id = None + for cid, user_info in self.user_data.items(): + if user_info.get("username") == target_username and user_info.get("logged_in", False): + target_client_id = cid + break + + if target_client_id: + sender_nickname = sender_data.get("玩家昵称", sender_username) + self.send_data(target_client_id, { + "type": "money_received_notification", + "sender_nickname": sender_nickname, + "amount": amount, + "new_money": target_data["钱币"] + }) + else: + self.send_data(client_id, { + "type": "give_money_response", + "success": False, + "message": "数据保存失败,请重试" + }) + + except Exception as e: + self.log('ERROR', f"处理送金币请求失败: {str(e)}", 'GIVE_MONEY') + self.send_data(client_id, { + "type": "give_money_response", + "success": False, + "message": "服务器内部错误" + }) + #==========================访问系统处理========================== + @@ -8232,6 +8703,63 @@ class TCPGameServer(TCPServer): "success": False, "message": message }) + + #处理背包数据同步消息 + def _handle_sync_bag_data(self, client_id, message): + """处理背包数据同步请求""" + username = self.user_data.get(client_id, {}).get("username") + + if not username: + return self.send_data(client_id, { + "type": "sync_bag_data_response", + "success": False, + "message": "用户未登录" + }) + + # 从数据库加载最新的玩家数据 + player_data = self.load_player_data(username) + if not player_data: + return self.send_data(client_id, { + "type": "sync_bag_data_response", + "success": False, + "message": "玩家数据加载失败" + }) + + # 提取所有背包数据 + bag_data = { + "道具背包": player_data.get("道具背包", []), + "宠物背包": player_data.get("宠物背包", []), + "种子仓库": player_data.get("种子仓库", []), + "作物仓库": player_data.get("作物仓库", []) + } + + self.log('INFO', f"用户 {username} 请求同步背包数据", 'SERVER') + + return self.send_data(client_id, { + "type": "sync_bag_data_response", + "success": True, + "message": "背包数据同步成功", + "bag_data": bag_data + }) + + def _handle_kick_player(self, client_id, message): + """处理踢出玩家消息(服务器内部使用)""" + # 这个函数主要用于接收来自控制台命令的踢出消息 + # 实际的踢出逻辑在 ConsoleCommandsAPI 中处理 + reason = message.get("reason", "您已被管理员踢出服务器") + duration = message.get("duration", 0) + + # 发送踢出通知给客户端 + response = { + "type": "kick_notification", + "reason": reason, + "duration": duration, + "message": reason + } + + self.log('INFO', f"向客户端 {client_id} 发送踢出通知: {reason}", 'SERVER') + return self.send_data(client_id, response) + # ================================账户设置处理方法================================ @@ -9914,6 +10442,7 @@ if __name__ == "__main__": print("👋 感谢使用萌芽农场服务器!") print("=" * 60) sys.exit(0) + except Exception as e: print(f"\n❌ 服务器启动失败: {str(e)}") print("🔧 请检查配置并重试") diff --git a/Server/WSRemoteCmdApi.py b/Server/WSRemoteCmdApi.py new file mode 100644 index 0000000..b1308b3 --- /dev/null +++ b/Server/WSRemoteCmdApi.py @@ -0,0 +1,254 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +WebSocket协议的服务器远程命令API +作者: AI Assistant +功能: 提供基于WebSocket的远程控制台命令执行功能 +""" + +import asyncio +import websockets +import json +import threading +import time +from typing import Dict, Any, Optional +from ConsoleCommandsAPI import ConsoleCommandsAPI + +class WSRemoteCmdApi: + """WebSocket远程命令API服务器""" + + def __init__(self, game_server, host="0.0.0.0", port=7071, auth_key="mengya2024"): + """ + 初始化WebSocket远程命令API服务器 + + Args: + game_server: 游戏服务器实例 + host: WebSocket服务器监听地址 + port: WebSocket服务器监听端口 + auth_key: 认证密钥 + """ + self.game_server = game_server + self.host = host + self.port = port + self.auth_key = auth_key + self.server = None + self.clients = {} # 存储已连接的客户端 + self.console_api = ConsoleCommandsAPI(game_server) + self.running = False + + async def register_client(self, websocket, path=None): + """注册新的客户端连接""" + client_id = f"{websocket.remote_address[0]}:{websocket.remote_address[1]}_{int(time.time())}" + self.clients[client_id] = { + "websocket": websocket, + "authenticated": False, + "connect_time": time.time() + } + + try: + # 发送欢迎消息 + await self.send_message(websocket, { + "type": "welcome", + "message": "欢迎连接到萌芽农场远程控制台", + "server_version": getattr(self.game_server, 'server_version', '2.2.0'), + "require_auth": True + }) + + # 处理客户端消息 + async for message in websocket: + await self.handle_message(client_id, message) + + except websockets.exceptions.ConnectionClosed: + pass + except Exception as e: + print(f"❌ 客户端 {client_id} 连接处理出错: {str(e)}") + finally: + # 清理客户端连接 + if client_id in self.clients: + del self.clients[client_id] + print(f"🔌 客户端 {client_id} 已断开连接") + + async def handle_message(self, client_id: str, message: str): + """处理客户端消息""" + try: + data = json.loads(message) + message_type = data.get("type", "") + + if message_type == "auth": + await self.handle_auth(client_id, data) + elif message_type == "command": + await self.handle_command(client_id, data) + elif message_type == "ping": + await self.handle_ping(client_id, data) + else: + await self.send_error(client_id, f"未知消息类型: {message_type}") + + except json.JSONDecodeError: + await self.send_error(client_id, "无效的JSON格式") + except Exception as e: + await self.send_error(client_id, f"处理消息时出错: {str(e)}") + + async def handle_auth(self, client_id: str, data: Dict[str, Any]): + """处理客户端认证""" + if client_id not in self.clients: + return + + provided_key = data.get("auth_key", "") + + if provided_key == self.auth_key: + self.clients[client_id]["authenticated"] = True + await self.send_message(self.clients[client_id]["websocket"], { + "type": "auth_result", + "success": True, + "message": "认证成功,欢迎使用远程控制台" + }) + print(f"✅ 客户端 {client_id} 认证成功") + else: + await self.send_message(self.clients[client_id]["websocket"], { + "type": "auth_result", + "success": False, + "message": "认证失败,密钥错误" + }) + print(f"❌ 客户端 {client_id} 认证失败") + + async def handle_command(self, client_id: str, data: Dict[str, Any]): + """处理控制台命令""" + if client_id not in self.clients: + return + + # 检查是否已认证 + if not self.clients[client_id]["authenticated"]: + await self.send_error(client_id, "请先进行认证") + return + + command = data.get("command", "").strip() + if not command: + await self.send_error(client_id, "命令不能为空") + return + + # 执行命令并捕获输出 + try: + # 重定向标准输出来捕获命令执行结果 + import io + import sys + + old_stdout = sys.stdout + sys.stdout = captured_output = io.StringIO() + + # 执行命令 + success = self.console_api.process_command(command) + + # 恢复标准输出 + sys.stdout = old_stdout + output = captured_output.getvalue() + + # 发送执行结果 + await self.send_message(self.clients[client_id]["websocket"], { + "type": "command_result", + "command": command, + "success": success, + "output": output if output else ("命令执行成功" if success else "命令执行失败") + }) + + print(f"📝 客户端 {client_id} 执行命令: {command} - {'成功' if success else '失败'}") + + except Exception as e: + await self.send_error(client_id, f"执行命令时出错: {str(e)}") + + async def handle_ping(self, client_id: str, data: Dict[str, Any]): + """处理ping请求""" + if client_id not in self.clients: + return + + await self.send_message(self.clients[client_id]["websocket"], { + "type": "pong", + "timestamp": time.time() + }) + + async def send_message(self, websocket, data: Dict[str, Any]): + """发送消息到客户端""" + try: + message = json.dumps(data, ensure_ascii=False) + await websocket.send(message) + except Exception as e: + print(f"❌ 发送消息失败: {str(e)}") + + async def send_error(self, client_id: str, error_message: str): + """发送错误消息到客户端""" + if client_id in self.clients: + await self.send_message(self.clients[client_id]["websocket"], { + "type": "error", + "message": error_message + }) + + def start_server(self): + """启动WebSocket服务器""" + if self.running: + return + + async def run_server_async(): + try: + self.server = await websockets.serve( + self.register_client, + self.host, + self.port + ) + + self.running = True + print(f"🌐 WebSocket远程控制台服务器已启动: ws://{self.host}:{self.port}") + print(f"🔑 认证密钥: {self.auth_key}") + + # 保持服务器运行 + await self.server.wait_closed() + + except Exception as e: + print(f"❌ WebSocket服务器启动失败: {str(e)}") + self.running = False + + def run_server(): + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + + try: + loop.run_until_complete(run_server_async()) + except Exception as e: + print(f"❌ WebSocket服务器线程异常: {str(e)}") + self.running = False + + # 在新线程中运行WebSocket服务器 + server_thread = threading.Thread(target=run_server, daemon=True) + server_thread.start() + + def stop_server(self): + """停止WebSocket服务器""" + if not self.running: + return + + self.running = False + + # 关闭所有客户端连接 + for client_id, client_info in list(self.clients.items()): + try: + asyncio.create_task(client_info["websocket"].close()) + except: + pass + + self.clients.clear() + + if self.server: + try: + self.server.close() + except: + pass + + print("🌐 WebSocket远程控制台服务器已停止") + + def get_status(self) -> Dict[str, Any]: + """获取服务器状态""" + return { + "running": self.running, + "host": self.host, + "port": self.port, + "connected_clients": len(self.clients), + "authenticated_clients": len([c for c in self.clients.values() if c["authenticated"]]) + } \ No newline at end of file diff --git a/Server/__pycache__/ConsoleCommandsAPI.cpython-313.pyc b/Server/__pycache__/ConsoleCommandsAPI.cpython-313.pyc index 769a2d49569e5e838a674ff86e2c91116861858d..37d13da63650b62c15d137c6f33dfa09a734be39 100644 GIT binary patch delta 9505 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 LwSQocQlb9?fYl29 delta 4055 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 diff --git a/Server/__pycache__/SpecialFarm.cpython-313.pyc b/Server/__pycache__/SpecialFarm.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f28feff9c93bcfb5803a92a43b863a97eddf085d GIT binary patch literal 18999 zcmbt+dwf&XmjB7CdA3R4NuTMvX=y1`5X#F^9xa8UJ;aMsB!)CCm6GB~g5nePDlLK( zUlc(p_o~!;N3E|*1r>aZm*3|%ef^a1L`1Xr5Mg1o}@If9;TzeD}uTnI{P_%@Wwo4cZ`IRzK z@+)Ix@GEPVuah%!GnSFJE7mC(1xYK~Q`RXNrI|8Pr4*f#N72d!QjRjCqE&emqu!#S z)lHiz%C4fQE$UKgL9}5r)hq3lS~T8mkfE6#`YiJP{_yk1!^h7|zkhOi`1$FN&rY8` zp_%&P_{`qtn#|2@=4Q9M&+W0hJeu(S6En|#(PWO66xV(Iw=?_x5Z*sFed=K3t%Kp0 z4osi@B(nc4%`7(C@vekbb=y5oPv1_bMZ%`i8}8dM;OZS%wX*r{HpozI-1eZ;;c4r3 zM0-hoq9dZN!7yO1woouf6wVLKO&TrBqaKtna#{|fRnQ8EQ*b62C9Q-wIt#QK(i&O= zv6j|Ctb>|5QZtoKWzt|K^lS#b%jxK|_ph-tJ2%)}_FgA5Yr<|j_#R+)a7|_K@7e>9 z`zmFXIDSJ>H1wvOa!IYyPH|Z*51~`R{fc8+wm`x?S4XqE>nRHAr-(~&=}z^Hd9<8X z@N3{<=2rFDy?qH=k$_k1e0Z|6$s)WtHhxZMK2RO}b}Rm4Au zMrGjhVD9&Pse^cD`IIrMb}s*X;h<>?=PHCiKW*2 zB)r#z2j2*vdLew^wHps=_~@8M)6Dd`n>1z!PS@@}X27-6>GHqzuz1A z^aV`^gM0c>?cDnA0mlv}15L1+yE`|ovbJn$+t|TUtlYzRovbEC6LO%bM=cfAb~iKv zr`>I?GgnmiJ9j(#U3TGlr_=3*b7pZrYH)Lh&(;H{ufBhv_fdt{(^Izyxq{Q>80hYE z^;%@Cf>2;ndIp%Cb`PuCZg+L}I~g}?T)1fQwmY^hp1)|Jqi6Ag#-8~-cQ_X;bUM9w zU?XolGj-;z$mrpD@sIPpm`#)dPe(+4{O!g@YN`U+lAA}^}y*^n9zfsc@~uy5Af`>PtWcGaUCnf)JfPe;ED4-fKB zk3_lEhW=@@@@Ta8S%#lOWXMP9oe#92SpH0l1BlW7_Y5Lf) zsWYd-!@mVyJpJX1Qxji6x$wcEgn_YX49wxcPFqj^z{4<;7CDm*wHZ9kOfE#QuKmFE zZJs^5oZKLL?rdBXKg~UM7$owPnYYhPzkHk|!v~LGl636l!SDyq^Dkx&|8ZvjJEY9) z+28WHvtJ&CEyxi$G&(c<2AACb66WRu>A3N{d>pc2B%H`%Q~5D4-ab!sGxRLBC-$=Y zN9<#G{Lt)~cf2^tC{24lyWp$kvQ@kbeOJRaoR$6T0Z=`@N1Gdn_ql9n|Lb zF*iLVgIjzq<3}t36AyD3EkRa^OaqPvX9>mudzF;rnv9m5i|@oDpTGs+LIRHpzkeVy zI1J~VV?Rx1OC~EFaI^AVcF%S;#rX)p2sf*Yaw=BV>-506#?8lSMeK_~smZE$B0#X& zclEJK;0nF4GOTu+-QDMC8F2OV^|JD|jx`%u?V7%RXDi1H09SYzr+p{?sD=jCLsg)! zve}UP*lcVH5`&ynG2ekc_PKfn;H=Bt&i)<-Br?UIe0HP62kv@(1N^RT+Ol?_d%(lR zvnM~BKKA~W$Oo^?eEBRlbXXa{P2rcH-ZC?MU}pap+{|sEoy=}0vqgk5>UZtIF7>(k zJkX_l(VHm#sOMke0dUiR81_<^4U{g|uPyqEE<2=8KlJd>!-t+6dU9lEP(R17pEE8! zt@%)MI_<->iN`N=1{&@THr(rPxHr(S*|%j|pkZ66sOGTtQc?M^_ELW3q{?{FR5|)s zz|=S>Kah4kg;E=Ws{HR&`4=-xN9G)!6UtmZWylR0%Dy+0`N|ib&-EMHE@DQ-_lAmq zq3W_imXmf>sZ?vO8>rl(U{2NdIaU9x3gpZKdfLkxs%TLtuX0T1Gc5=eufC#`7p7g- zLFQE{mA6tFDsBxG)n8F7AU6$iSyO+gcxkApE|wL_D;>!T6)y}G&4J83u3Rj$7BX}B z%!<*+e5OWdhbvr|c4b+4k!Dansmi;&UP7r;4{3+AL6y<3GWzn@U08CV&S%>EgX+F( zFt?N=ipDTaDd8W%cukP|Dpf>*6elm@qfRW75P00>l2|1?va=Tg`hw>rP}UB0tbp;= zfEnX*q>v+v%aN&}WI49YYYU>bIjthgv2prg|h6rMDTnsF`{3bokI~a3rRO-kp8!;Pe;o zM_wI|9DHA+>2!L$j4KLVy89e}S&|fEWhisC4szf*Ii&IKLPet6X7_jz=|zr=Mm~P^ zW(|4EpvcW$MvnX`yzk8HD@U8m9hMXZ`5T)BEW?H}-UhAMZCuODo%l-OWS9YlRj+=; z;p7Cr%8tL+wNDL0zF= zSLidheA{wC^39s_i+n}vCv_V_nb`;1{&86W1%GzqgS z@;o@piktK~Ge(O~fgXl%hXkvF=MwR~gmbKNDVi0%pflsI=p1XjLr_&SyZ7~(bNhH5 zojXs{FTNXo|4;mRj_Dx+R&Fv!Rs6_n`ywZQ-?oaVDalU0n3SNCM2`OIk>}hbmY_NQ{$}C>e}HNc-Td7 zo6rP5GSGv}UYu<+NOCc$4li)>C?gT)i&%(7%W;r2a*_x7`@x<8+5v-*idFUWxw>sI z1j0&~#gNC$!57Mh?Tibc4YL}vR$;UTB49_zRR|zwH4cFqD;q$D2)G<(fv_m170S7> z5kM&rIEGVzoe-6>ZQElL7KWXB6aI6v3XfwGXCMN0l$EA7hD?PgijEcqO$+^|h2xJ0 zOsfXhUChWESsciy7;FjUl?U_c{CRa_-f_oRe;{wkV0%QL7vdR@*>5oW%9c&+I5+Uw zzy+6YYj0rrcHgo-U&(`$h8+o9s>~1gqjJea+r$#zntOfaznL^_4&~AGXCsFS@BHs+?5&BSM!S@{yIa`k}CVUpsGbB`$naJ zv_mY^1vX^{75MgmfQU`WlCmjz7cLhG$yiU!#VSkAl&taw35ai0 z;UNIiP;{noX>lyD!WBSC(J8Q+SQ%Q;^a{Wi5Rbri1fx*weW$=+VtI55uwv9969TQI zReX=)nNY9#Ce%?QN(i*(27`$f*d+1-5~6Gp%rQy(n~b8a3lBI6)|8vF$y8TLb4st4 zCch#!ndUtVRTJ3c)VVjp?~RDLh1gspoW|sAQgjs2nj>Phc*x_K=(d4@{w8x|i6E-C#wiXa&Yqu^~M8d<8vy>Lo9(YUKFd@bHV% zN6&`8cop8x9{GfDSYiSavDq6()nsN+Ibu2>+C%`?jb*}beK_;U+f8Pk(VEu~hI(T` z5ku{;=m|$NwC*F_$7#Wawf=^+zSix5hQ7gepK*>~ zS9dA9IGA1Q&#oP_j<<}hpQsIFuNho(NuM3mm%{&}yMt9r{8dXP?18Ekx6GmQ<3VKk z+6(IQkNHaJNyDa4ZUN7mO~1yQjf6LwqP%%!Lkn=_#v+Kn$oCwMr%XR;7S+ z9E3y&hP?V(D}caPsm+FDD%ui&m-b@9v!WajY6u~zVICB<1fGOSlTas?uu2epqS2q0 zkrV(=T24}OsF4JMS_Mdle3<9C-O$HgYy*uj2|-XNQ>-aMc`$fG>02F-0Qd09$12aH zS}o9P<>uWuH{C#G16W9JgILB=F&RY#`^t!laiz;0FXd_9v|D`~9-)(CU3>vOAyP9s+(AQ!p>37Je1 zmQq$h(RL|my(@L&2-G(GuWRpt+UcY=Y#urzO49|onL=(>G}i&S*(7)M?e$YMCgk%8 z0WM;4Vk!ci+a*Zt*nIJ`$^W|2How^fa|h!$il`^S4mZx2HI?55_PZ!*b1ykHdaJH8 znN-%(daIJwTJ=fS*+`rC9>Fynl!XhWym~di%4Ukrx9SAbi;m6*vx=I|se>KiQm>V4 z0BLg5cBOOyP%8ww_}@+Fb^{jzIxrwBp$=&<9Fp@ub)w-eA#7g2_g9#MvYVa+0guFp z&_(1V6kFAB4$Y0?^R2&)b6i5Gl*XSYOPTi}Xh{PqP~^bA@cXaFnR>~kE@z+-gBZNB zA$HX!GiT}y4<7_AD~X~f#wgqsaTKF?ale5gHhto4)VW0V)t<}%D) z)Tqsok4{F84~5BFPTwVAuMLC&JTfU(7u7HaA!@km>h^rq7%r84>a>iuv$@U>5c@ z#od`I4HIX zQ&x=HFe3V}+UfG{bTVM!1Jp&Xr`Qz7_5r*?QQ~u-%frfc_PJPDzthF0aK=DpE7o5O zk;TAS;Zu1D-i%v3H7NSYKFJLvKHmpOpn@x?b zM-)zy<(Kraa(v6`+}j7d{oUM}v8iC6h^pW&xbol@5N{aV$Rr7P6h$14%p~NwF2T(J zxesM4lc2{W|jB?zo0&#txv#UcTT9sAM+KpOzKvJGL4sV@`5?l z{+#MDlRu|%P#elB9@YA@<_xMsc@?9*{=E7@{pA!YH$Rwjr#}bk269#oYQgjfbs<07 z6wIphXH^EWst477(>5TgN*C*^34>f78cf$x-E)8chE4S94lEj>KphObun5NwqEG zivrn82Q>hVjU}Ub!Q44RYX_Sz=}o@Ex`4iJs-X15hSxWY%?%VZ4XZ=NWrua4^!$;h z0_pWPdG%x4ff9n2| z_m3|>zaUW27Ah_el~myW%O7t)pE~iBufy&u+%~Cmgfeq4iS5hWN{-k0itd`!HN!$O%V8(SUC9V; zH>mxA!xTD8QR^s|#C$v0RlwhX3M4LY+5uy+B$>-i%=+R2L-8*na|OIlhCgE%5iJnN zP!mFD65}tA_X#!yECi@e0hn1Cb>pc6oS>kUM60O;tf+!_x2_4O!L3DekHo4#v<28f zE0+Bm=R+$xf-HdrLo1S|he3C`{Z>+csTC3W2R60A11*6Q7r|mOa?K_<&cr6ms^qbp zfR`SGdeUe;)SQ4;Vo0jn0gi-m8^q%#ttMF=tCC<+HDJYblF~psNlfcj4c|ML)~XeH z2zX}m4mtzz3e-b^lMpulkw<}MYQs-37eAAd$wrbC6O)nTh(`jr6JSmhcP68Q!$=6U z$tr~z$+OBJ&X2>A*sF=tVB+g;CUt)L-aDYI7>|;vDG(q+`9X6nL{>>NJrL4uRo} zb84JAdp5lPq=rL*1S{cchdHS)m7}QtLQdYv^G_2 zXsVz$F>u(ZcV&zNewZB)&Ekr!`4M-W*jr=j?0)D(^}9;qs1ZIqHZ`$VNS-+$BqxUW zWcb)IJ~?&f6+Rh$?ma#U^nOH4VgZaidwlQAJ5O`{WpGHO17atTXa~5C+Mxso@wh6S z4+O#NKccx^JPA?jBJm03kR}0&kz=1kUK=+PHy<-cg?l^m{y5knd9z_hTr z67N+r!Lnwk&R^=1h2x5FE5>!O$;?XTdU1n;UhoGF=|`I;XxEZP?2ICFA-bQ!O^wsJ zquaiRxeuNQie=?HoO|5NQ}|3^EPPsWWD^-62S3xh0Tl1C5lT*%-35rdJPG)`q0~IkYTS0H~Cqyys)=~n!yv`Md1RQ3bXpF-(* zBQAtxXDwsRhpF4M49n5X==Qf3m z6=UgsW6ek^z_SzDquQXc+Hb5LyK{W;c7@g);GeKqTY#tm0mBvfAG<#mb9GIPgM$DW$7pVv-2 z;ak7WXL9_Y>i+4vo-$jmQ4;K3f%!!H(e_|Loxh-NTuHQxmG@6>wfnZY0$T@s_wVx6 zKNKur0tL)JF3YgSKd!3)l|yZu7k<$y{8<%aspEa)UFUWGO8f5V{Qvh}X!TWEgIV_m zvhKy2Q2J+g3vAtQ<}|HcDE+poS+b^4`Y&^e;o-YR&DuuAcgt$l)+_#8C5N0p*DE0B z&y6xnFVwUyR9v8HTIVS)%u_%*4qhUoMHYUo0QT`#0bsf$n{FLkh`{A-)Dj6osD)OC zgup{WGXRM@B zrWd^5ZT$lSyV#~AVj8~JVLUdWVExkm0f)Wcy}Vw?B6U3|ygvi-KkcPL<<&l2$)${( z%hI&;vWt18C+569=UDx4OQ@{!)Y6kn$2EbnCBq<=C{w`+)lt=G^Rd*ivJYzBt(lPg z5xmao0>))=;>>5+I%%{)R?t}QH`Y%Y8$u0Bea5;eVEgM{S~tSH(s9K^W#;~LJ&%Y4 zX?j_lMDSIw{I>D@L*FbsU+t@GpUhf+k*|29<8TLF9;TQ5>^4I0pI@1_GKcy)N7GUz z``Wy$rBwD!sRGiQEK)XwGsUqf-F7Fq6no*o#W5ja-I4Je9}_b$jt^NaxoAY#ZU){=-wT40D^t&t@YjJ)6@PII@&X8AhA76Yu@ z3+Q%NFIY*qJ6^QBB(dPc?#o?)GFu0JZpB+W;;bZ~ep$82JcMF5tU?P9K-pWLFM~A& zEE?%z`v}w)1XFC*V}3q-OLj=yWS?d5wga#VrBwkDpefilVl+1o@uM~4&0q<+A1uF} z{*&Hkia!PeI-pacb*DE61atx48VKkuQOiuSy@VG~34zY)f*x@p&L$)Hl$md5rnC7u zA+^eQ%CTSm91%-T4qAG8b7=A_66Lwxd!U8}KRG+`Hs{2|*&#S!$9-HPhJl*dr_Y6- zI~X~B5R5Q@I%oGC1V)W!14z7ekgLW)PK%UCZs6Y_lDnP#z=TJjF=C?Nj4sTx@Vth3 z4s)N!Xdgznz|0^<=>E!}k&wF%Bj3dVs4#oMeE{qS#2ynKe;qF{39taP5w!rt;|%5* zq=6fauXOq&t_ECN3ip@d!4gj@ZdSzE9C8t%6Jk4w&edD{re7Ho&lMn?4oe0HU;Y^y zCpd$%V7!2jiy&eZeIDmdH)q1Q6LS}1GzVX4i2QLs+y~N$*g2AzI2c4dfRTu3LoUvc zmJVTkg=YXwCc~JE28#sy1oILWG9*GG&QOsG??4d70uhPK+gN2m5;6Ge;`l|Bj9Osk za7(7CRag9n?hcT81?4A}zP|L>^1=0?EORi+;?J^-)daE@48rFmHNm3!{-XJTqB{mR zLebi}oGu=$S?RA?8LV05H&nu{P(jt0)?aW3q>4&|MJxP8E6%F}Me9KQPoc`HhV`M$ zg3&ba#J`kVIo21*y)#rZcSIi#m1>uNzv3ydOEgRGMvKK=l6%lfv7!^>PU$xMsd1pm z!qg2Wi|nG&`apIA(klt%*8U&qO=p%4uQ>JC=wqR(`KO*b`IPU@-}vt93RG?JWtIOI z%+DNOV@t5H!{69(!5eI}`WvlreIIK=#oc$;4xgoe(zr8}UmVPD^yfE@w*~W;`}3Dy zTE98C-tJ!yHUV#7{qB)=Uv;a$pbbn1!Gh)fg5?u?f(7?pRZ;~j;Ikp#L_y3UH1R2V7f-h)fK{JQ_}Aggj*)Oit-gUP3o3Fg`lp~uPY7e%Kf_X ze_U1rm7m>cdVQv)U`-JPm=WXJmGj}}yCO}iT=rdcbH>^gvOlj-z(btf0Ko`>G!u~c z-#Q2bOP2vl7oZQY^m)M2B_LdYYGOzVfTUf5^H}UlhaZwca5_;_l5+f{6{xx4#8NyWPIG(U;~W7t zvn5G5L41*~@YHtz2{`pCx%wVS!ro~6LAQ?k`b7dS6SmcwZ`~K>eY5-gc3;uPpzdzJ z?ryY3LAjsZs3|{_UqrpBX*S8ms+zN8Uu7vE8Rx5rOG~y%Df9yWXaI@;4EFoM1d!Zw z5#P=wm;nc#5R#iE(4$9;8DIn922BXD4MILkNk(1ROtGr?xmrB+UK!9&RvQ)7gdjFC zK&>SAr7@JytAKA?aBd(aEt7^-BHMHzx5;N{ zO7QLFKD{ViJ85VI-`<3yvuskgTwtq{x^lSQC(0ivy5I;G9X)Ji4ro{;=+X_hiGV9? zww>^22*3l!#_DXghhW6f&vW%QTTdV3_Vo9;oUQ?zjaiD-iGz?8Qv`sJxx{H?-o*$Z z0rN3Ne}ah3w!sC7-P7lQZv;I|-!`z(xNWv~Dbh6#M5wSZMyMCK?}F%g>biWDMDb6O zs~yr@Mb6bbj0)q`g@p>!)w?7QOW?;NYg9NTSNkR9igi#((x9lg+9OdZ?vea=PP&3~ zTp%PJKurjE_R0DGj+}o^?{yJ|+=clhz_~y^kq~%vP0Pvm7b^%yNK%f|N@>LssBbs? zaw+&jAO&3lXP|{I9SDL?cJ@UELeWQ|A~WbMk3vaCvGJz;Be(9fifGKR=C@2&P3Y=x zN>`0cX(M=zkk11VR3?O&sS?fH;8P5=5`&0G;h)YyE(V^GXT$rx0PnV_d7HtTHF7fw zY6sFF8N_Hl^~E^==8@MvgHKhP%)9n@why?>qC*XzgPXhX4`)0c`%4=4&mxBfBO~Dl z=pipi7(_zODC=-A9f9ITmmT&tos}B(h2%gMZhCpm1Vj zLR`%mndErq$NUQv`wwIxry)TsM+$WQ$}Ha7eo?6zY&^7RXwkDz3_lc58bjJNd>U>Z zTH?!V2xuEZd4(sck5-S)J6b!`KBY_dWwcG|)`fKXL#;!t!}lC$2S+D;$!LBct#0pH zNUOAQ^hW3tn~!cDwIA&oYYi0MF;!MM>OS?z$wz*_YX@rPB618J-dn&EUaV{UfE@-GTsm;lxmsG{VS+Z#&e5fh1eunO&mO7Ds3lHVTY7quCYYPcuTRobZoxq zife0)0m|8GVxnZliYxBfYs_#j_NqGKt|-Uqv}5zoxHVN)D)mGr#Mda7Pom4Fa%M$M z{PHQ{;UtqSelih~_=K9IrsPmDSxwUNsdVdB5@!u{K5-v zd}g>2!CB+X4%Jc5$z&Cip-}vC*GRSDRE=}^ncSM(G0*VqnN{JEBi}X7n%q*T6+aGH zMT1PH!wE~namWf+=a5xJq(FAEtV&)cPnK;Y(1A5{b&_BV-{PrjbT`d&3tKAOUU!2>5SJ-!+@ta0^~wzS`20fgeq|>4t@yHX z?JQmoWEIUv0*rCV0J);#WY?<&6H5aTLfXt<L#`ME58AfipaDo!R^)mh2wp_GSr1Kh}O0NB9S0o=s@H($@n)~K`K zxy_jF{i(W>w8PzA{yBgz@;v~5!9Nf1CH@6~$M{}=$N3ilzRdpu;0gXEfUoe!0KUo} z2lyKQGQijQ697;0uK+y7zY6d){~EwI_}6RRPE4ecs~xB0*EZ`Ayf zP*)})t6PDW3+%5BtNNEF`8v8mDPsjU;5TWZ@{>fNoL+SbM`9>J*$Dtw-%je-W+5K#OHI`Olp zL^4-Y8>&*R*s_%kM0gzXYo#`8GvS`^EAa-y4cbd8S$0l`;*2_7{JSAleA8eTjmA8~ z8Fk5kTHmkE3#jwnSLcf}jPWs6;8PEx7$qR)WM1MT;^jQEcs9#SPrWJEir=l$DD!19 z7on%WB4&EuDY;p6T&JKdrHb@&Ud5y&NKZb(%T;l_+RIftnpDse_E}>Hr(9f^=fFIv zhjZz#l?tcWW3*^Bkk6#OjP86(5jS4T>s%ZNMSZcHj||Ia2S^q^YPcj+!*j#+vFD~6 zc;ndeDY7+8Vq%-X77JuDul$GWD30c1TykjJG`4Nzie7oS9JwNXkK&4E@lj z<&C<6{QBm)%^tzy-4@g?U0yZEHG9Rvs>+}&sAv`1JV9-X&|KeE*V@?Zg&dEsb%kIO zFGOc6L3GOIiPo6AN+W|@voEMl zGd5$6T>h3^IY<41xr{_(H+=*^s9OmlsWpjii;1*|ZI-DdU))R-g z#8%@RYSwUeab~7MEYG)6%?p60U#1vi8dNa<5?(=JnEygrY2f0O3SPM;Qp~rfw(}~$ zpyA43j#XY#P8w9w_oz9JXiI@A20I=Zd~)~TJrAAV_43fsy9a-IaPZ)X^G6S#@BHcc z-dE1Qa!4>iskZR|zOTe^6@y3b8EWt7bc}PHzwf!h$L^To5D)`|iCBa4N7eB7^A}$H z`4mT`GeU@f2ZH8nv7Vqo;yONHQT1(29zj4PVb~y~&%(##zEGT^a<_QSS|TA_?`w%uiTGZ;R@`pSB@@M; z*)s`LXwO`N11AmS3RZRNCc)#bZ}c_x?e)e!$Uz&5gv@qCgvyJ?D4A@9Gg7F)swxpI1Q0aCMAmJtb$jb;8-(WVwc9@1LqaVUUylGKgAj|rjbH-+r<_f8P+!+30H11=qz{{VTnjlg;mOYwUr5LykM{m4 zVLqoBMogM~QehY8CdWjuehTx%b;((A-`jyK@lbMN!f@kP4?vbUkUWtb6^)L2NIlYd z?@NyRNCqP%3-ssWQ}dK4Lp!5o-Hkc3+_+cfWrOAB*=oU;FkY=%c z1g}QD3UY*CO!z6LIxwAKWLB>pSoX}gu?oZgqJq3DlU2)nw?kt{&T>sP8PFU1_2UBi zacA@eUutD(c~2HRR?w5$Q{O*fbzs8kzSK3SYa|ai$8{+B)yV;M@)>m|(=;&Mug(al zGu~Hcef?!5jm#j05kfswUPq|=ZqD4ROD`hhY>*+~_yi;Bni>HgePINZ*EQA)jKMIx zM6?teGfK((;-QQ?RNp9Mvgw3=Qb+8)GczCM#9(G5(Tg*)Z8S5EfbN&65?3afX-kwM zUR;_LK_bMS>=e4$z(r!w*r@O_ExfRE&jVdYw%jP*QWCE!#Z{+E(owpy4MdF(D-FE7 zR8AMKk|)uf2BIMx-TEo9t_B4v#>Md_CAZfOKLUq;#GAnm+6R$uaHA*;H+fdaSyzSu7vyTlO zym#>K!-%AeevktVJ^Qo4gWXr$&d<*m%;E#iY*;+M=FHMGLBTeZ!V+~Wb4+Bb7@Om4 z7q;P@bN~{vUy~vRkG&w(FD%E>DOmb@MC3@(VTK|-dhG`yUpX|2n8aB*neB)WZIJ-- z978)_xp4B)q5D7z3O7NH(;yL##CYr6-a1c{fQxjA1L8{xTM^((vAzbQe665{k4VQ6 zKGKe5kO6^K4rRj1lw&q#AvbUJ3AvCCYg9WWjp|PEPdT=TkMKqU*ZZ#=EouK!CDWTa zOZ>6p`gQpMU4BnVfAJ#tW3-hP(5Ln5a{~IDGy1%N%p4S*X>cbbCKG3$sy?Hy9Im3E zUso8=751#?FRg^XOPnTh137Cn_Zyu7qqE;QDPWv*#yACJ@3^8)#TldXtTEb5)qB=W={MQ`%_o?tJn7% z-2tQfUzg-qPLjCqSV|T~$bJ=}T{uPltLWr~rSe~uDj{9RiP3RpRW&CO=JoPe)Jx_Q zw^nS)%cEDtfdCJRk%cMdSON&31Mu}u z*;$?Wkbb}Zj5w?CWE7jLqJXieC;N`HE+^Lk5wui7|IyYNW)Oy{ZemsIXYQf`cY4-X&K4e9x@+1FT zUOaEyB8Y9cNx$sp0twHAvd@b5RO2J-LkNM1P5u z{$n#SvBh+SR@$}%8vS#*X|*JahaT)1y7#Cge35rlGbwOlt`$=gghunlGN3kZkx0*9eaD2kw+w z5GPI?SD+HgAyb%vfT4{6)?KwB^nfM1dqm+t#vr~nF~KMxX9#lx9P4ip5fGIADuE8<%t_e}8^|+@wIoPkuQAHVT<)4MtTRG0s{+B3%W@Mu_Lwud-~$W6}9#uvD~w7f)i zJPp9<1RKHFKGnGURV$C?Q=ZrU`$4-pI>)T8i0!SJD7<5iR^8WcSH9wp}hAK*pKoqS~Q*?psDbg-i* zgbV+N$Z>U5s8-IF&0Eoid_PpFRBAA^+WHD5&*%poLO0}8I>%s)SUAmEIt%gzR7t~x z`V?k7jo<(RhAyLM6Z3k+r>CV|zX_3s!nK4nu|EQ763$xVsCTco-&z{5miAoVXRYj* z|B=Cp=rn>claJ>dPw=PCdsltNxNN|d@U^(LY+_~zKgP?Br32F}?Y%TzPqG>7LmF&DE#qoRa|lo1%b3OdnW@J8m^_F8mx;n7;`*7^ zsf?37gV)$N3?6b<0XZ3CUJ`RXlX;@u zoI+oGiL>6{t7;$7xf+O5)s_X%OUnm|SDb%U1SA43G5G35OnGE0u6 zJ|Py2egKpgsTQ{U05vrU3JBn22rUMO*OhAMC+4Q19i#jj)gOVz)FSUq!O4MAvg%&NA+o|KQ z+!#?POGlj*ZaSHED!w)=NBpf$V%Rj8>z+1x^|EknFvZZ_?V=0McWHK#hYl58FsUI)}Ynk9?(%!?}=A+V64YZH_L z5sijb#-p!f$d~37N9@Ouza$fL=9jmtWS}@1lHsqb^~qbGyfv(LUFT0)>#uD%qu(@O zvUDuGq|j@u1NMYNQTwC%?NbBxseSgcj)fl?>;qYaXN>6sd7yGR!>kkHRyjQ#MTaqXg z;m8=aHToJ`!AemJ@(bh{Lvmr4=vZ8p$fTlEJ)%jm83@W7y{*DC;ysHK7xmy2Tt%&n z6`aJ{qft6CTSBeR1D0{HN+1D`p&7l(;yk-1W!=8#ob!BNq7sdm0%8OYjNr&R>FA4+u|Kn zw?;5Z{H2UWXA_ecztl>8BaUA>6w-fs$1giaNRepcEA+7bliFG#Ob}c6Nt*DWOcS5y z6Vv*!741rQQ=4b5AT)<8X^g?ELyd};`Ao7&w691vG2BzbBNE?Y%2(Bj#pGl0;T2!f zrn@-}_mupas+X!kQGiQ7w_a|QjP7Uabh<^rZDBB4(m-t@i{uuu@G~8qzD%yBJ+Bj` z5=bRhQ6KQsE<>O^I(;cva;vYoNix_b83U z!j{64`DnDQ2y{(p9ew_RLl+-BDZo6*+I}?qysEx&Av+agsg;7XdSHZk3Mv*(189K% z@Tr{XzY+xJh=116Sqkn88N?IH4tHY-H7=OECS_F|A->b7+kPj+1TpY;*-oyDtAkZ$6F7E zaB!{2-Sze8b!o*MMH4t*f~gyj$%NOHVH;d%#6p(4o3W8u2pB(zVEiCx2#<1jc-c}- z)8=dU2;jNf*c@WXp8C+z!WWE`421QP+fmXH*|ImR{$Lwz0D?JVEqgy?So{WM-U|Rw zpUeQOUix%F8^qWfhOeM!-PbR*cc ze`S9{MIfPKAT@I!Yr-X^EU}VYlF1XJIw~${WyZvPTl)--5s98w+*8pr!#~S=rda?F zMJt)jeF3}}92IcYOy=U(Ty6t?rPd+za9hxa5$)whNq$fZ-Z!qqB^otmko>ubFSnP@ zOO_p<0O0g^Gr%{rmMa4eyDzN&A)WC`*ji-k`P7nNQ&rLX;gy65V$`!NAJ5y zp|L;%!$JY7-v%0}GKog7Q`l*83a6ou&QL@uK@e6XNviLjGP(A_|Hw6D7QKzU@tF=7xsXUuq|<0{sQWV2m#%u!5W!a)OO3+;&BTLEoU?2rUeL*x}!*m2N)1TA-S zHeCd`rBSx%!4jLi-;%2sT?sf3BfSyj5f#sZW16K#$$==b>AI};Yw=~Km@$qfJO|j& zmI}Zj*&(DAC#+E}9P7Ps@~)x#x-aewvp_SJ#g%A?1u|!aqYX`Ij*x|Rga=lFP6Q}v z3z^CO289^o6E0)w8S$^z#j1KB83bqWhWIs1#~a1VB;7_*@6c7z+8knq(sJz|SP6=l zQERqq=ss9r9ju1pQcykjG2d0|CMQg!uaz+-uy7_i9-ix0$BK_u+ou`qdv4iv%f2<; z8U2RBK0{&8s$*-PUwdi_*gX46D+i1z{l<)dF{7*T$t{Ps^epPjobiEiCUBUHsbJph zsAK0tT z!b(BCU)^Qvt~j23JkCGcWZCkC1lr7#&D z1MD`s6f7_g*7Rb^npoX%QLma7og=y8Q){e|pizZu@M@tL#Nb2`4&hYu#z-odyv1 zW_!P>IAAL7S=?`$88FQR5!8^eTmaLwP$Vifl+^#b*l6j zztI80H`>vkHZhPk@woO>d0*PRfDuSc{A=y%w9Nb`n+`YiOg~lBmpQky>Z~m-U@P%Y zTGVG-bk?2~uus`<5I3){unfdIyQ}-+O9vd;-AR7OBNcPb6suYqnin>wcW8il(2Wb2i$wmDAIIpX97!=k*%SArrGU z6q!%K;Lnqo7cP*&h!?brAJ>C%g4w60iMuxxR0Qp{Ep5K=dQWf>Z)RvF;5s@~~@)mAMsLN2WZ=5{+5$_dg7wUG+9{Q2e@nDXe}k*UDpi zH+ZzfECU9-qVkAJGSK0baG$ty8;+Qh@+mJF;;!=o* z-PclvFyAJA^Qu-3ECS2WPB+mxyegM68+wBu7BsvVJmqeTfMZw7AtsK*i3M&A7s1m{ zeoE}(`pZxN>D?aa^ZH zk5WXG|N;#bD=N$*;?nQ%8pmb>gawsR5y|qyu*j{^X@|uRIj8l#kXPBt_&D zR5OD60!~Uhn}`*aLONVX3o~HR*qGGJg`GcfFfkvDX!CCNHgET~so}mu(oGy~M!bN< zt<;6kat(DA2vHh~vO+wzFg(AKT}k5!Et@3sRBfBjBQSrK6AA~TLu3cmRE!b;7hF&u z&ce0S2;B)eSUJNGWZz)}Ovomi1cY#Zow7X~dO1SRr;sGSh9|%%TTuMmtw(O{FPI%D znEgS)+<-pUuTLB>C4=WCU`p#+++*oCO$?YO{=sNJn~>d~kQYeE>#jZS=}VZ?Q2}9a z{Ym+Oq$}dMoq{ehcT{6m|Ei`Ix zls_44kmcdEiVakVy#DdC0^?_$n)2=qedAa8i&pzrZ}8U%{taOFl(kBWmNXk%w9JRd zO<)$$OS!*0Wrg!dh?kOo^wuAE|IBw7$&T9Z)C>SJLe7$?O#c)$Vg@r<}m~UYLnB=oZpa*ds&13JwRVfg=fmiG zG@Att;aj0x&{HNdMT2ooe3zg;bgQ=Yh8BnVa5&2&xFbedUru+3E=IGgdTx{WcSF1(6u9ESVgIgbkQ4{3C^(;0RyUfUpeJZML6m5b54E{0{P z`EI{b#bj`i_}QK$vP;x-I>2+E)0qL%aA~Jig{)BcSZwP2jfIta9UD4{09mqdQe3h( zb2|D4nHGtG4zE2Z+2CL<*+BdT3*%HWeIsZPJX_oln+YcUrpC=4AK0yX_AZPWThwV4 zrir@y<1P5M!ZY|V8B~_^wRL{z{2r}TzZJhX!Je=O1 zAj%K8EW@e{n|#ymjM9hEuRx&|+_Ho-x zMhM?wy=({uAm!ww{v(pYT#m&+A0P^HDG7~~oLD{}>6ZyVkKz_7#Jr!+f{@+jpT}}v zkMgOA|9K)p{NR+8PF2Y@u&zsD9)zAKMFKs#2kb)iF(Ucz1bXRJu!e0fliR^@O#L$y z7OJ^fUO@M(lq)G0B~Mg%H7*rh_93x|mTqez7=g-2HuMKSsQo!)5bvjVY2cqqbZ<6` z>jQcfZ^&1d5-C*Be@d>WPYRrs#xLhg;&)G|uvf%Lo6F=5GcSiVE^c>-acKaWK=V_I zx9A-ZV#Awx3vcCZ`SS2unt5ooQrj%19b98*pE@d!h4{wA{jo$TE`2!-!W9$u+b4ye z@^N6E(x7=NJ@h?doC15-==v57*B4=Dy^4jJY;uU1XZ6Nmz3o4yUT1t;F7$J-^uc^6V-VqmBdO{%2Q{0rAUc7g|SqGSC1TbYMu5nEXsawnRCzu(=fo zK0)v)f*|xGsFGA&kOC);I#!{nPZE6EQB)?8KlWM!bqiVqjLpx+;+p^j)fjPH3vqKk zL>QpP&_^OlH78a*m)h=9087V>Md)hC*FbFI6#-9ZJ1Y#>KrQf36Z&jEztML|5fRx; z29i<_dG~w!lPUs96@5unow|=~sRQxJ{qZG%_>!Jgpu3ind1!ZsaCEq^%;&;NFSy*7 z`S~?xj5XYsda<%+TMpU^z>1L_FlBec_l$sP26M-UTlVzXHu{YlVLO7Td4B1t$;=eT zVJ}94WmcSVRr^=1?sKj2uc+~-vWNiVrqR_*uRr72+i$=~OwUGqm$3%ik`TF~3CqPCkM3J0;5blY1Y0K}pu3XuOC#NkUy1 zez0r!aW>TmfJw-XXas_31p+yMpt{iqif@CbUf?hfgSo!K^%6$8{Nk>mo)-lz=0_0m z>)wSk-pBL@04kkaP${^*pmu%^rNc{eM3x|F3@SG^dYbC5Sejcowv9x5=*3uam)P@S z2Z{t(po`0Y@iK@6{Y$ez>Q%k8e%gMhML2}ja%`&|!|ZSqOeA$-iNgS{XyOkctHXa@ zGLu81>DaEYB!21GeQKZVl?u`>|Z%#5!!+x-_WZlgdYv0U*-rn`*To}vWy_wfu0A)eJVx5+T6rXqo zd(OIFh;^(oZa_%NwT7Cprs44xilMkq4aLXuq?_+jFdKY`U^Enrp>H6J9ZkGB?u}290@IW9>K@i zn<1qJ^@Ki4=9d&XW4*8_r6G!2B5|66Iwi(I_8WxfHAzK~MckVJ1zz#UEC3B!fn0aIu;!S0o>u&sFab zKUrlFBR|%Q?|gJ7(uyPj3Sk^5gz;kc$7vY};HH6qYd$ewIpU7xlg79@lEh0NyVfMT zqTxx0D-vLeO9wEuIKpeH((uXbD$NxEdFjPUugMjKasxkZwCR}m3|ADN8EQKV(%DkF zSTPRFFMUj(B8bAul+5seq0_@vN|l8z~5 zL;Ic^?0jPAk=rlayK9~>UHsc8+w#xA%}U98gVOQ*f&KV-d4}*CsNx70RG`f@B2%s+$s@M?;$rG^( zCQx-mw0^qHjGibaZyo2Kym#=~PR9;mgV^9RY*Cilzt`ZfsrCc>?Z9B*+ z?n~I#xmqkfzr&;sOk3ukx*Ya^`V+77uX3N!Z|Hsf{5!~$-5#*qFHnnzFZP47aKmSb zWCx=YOKEtXy*if;_MblhI9tS`02I}5Py9reGy(jX8n%I3%dis*l5xNaAu?UX>qji= zRfW8Yk6f(w>RtNfMm#y!j5$5 zM@k#w!~NkC@;M;tvj>RPTJ7W$!|%}sIN>#WEiNy>RW1$~2phuM(ND<3 zPq00Dsn~xUT5l}A5qq21DIhuA6QN5SpBjEgK%vp^IQs3uqzRlkjC5)E6xJhg*U<~c1gs6eb1`eago%MtR_W+YOel^VRXqxW@2wn!K?V~B zK&GO%J2{E)6Cp-@k}D2Ibuycs@GRjd(_{q~N2bv7tt44Km7hj03tTc)-p-A)@-y;P z{LC?Op}cLen02`_LgM?=#rrPT)UYKDQ&o|xw>e<=A$sN{SSkj8axa7`4({1|{=S}} zXAZ-1eekwp7xo;KLTqr~z^)_bUfVC-`ux=cp~%YxP=mC}S}p$T@{;y7$XII-tVOU6 z0ZO0qP!2w4XGoFFwV02>M_3Q3HWj3#<|K8f4sWBON$?JrU$t?&__6S}C8^nSy%9d@$Wq5BI_U~o@!Pr_fSd86UCxtl%ZRor4wK4O;=)+go z5)*nPP@P0;=5T5E7I54MsH+OC2#{}GQ}o4;5`8PMkP88N7A382C0JVD;8k#d>f{=BiMo91_U=ExJm5&GBfUGOx}XvRs`(`m>7+xd!Hpq zAu+lfocn&xM3>&GP>aQ1nImSvy!#9)vGS`I+s`JZ^e0XYBu@2DThf=fuA_3mkl01~ z3@H#z*l)}W81ww&%3v#J*ji!jn&{7+>d%?xpH|~v+vKm=;@{E^qPHB}d+@a!PAvhk zTn=AwHIrL`U#TvH*KxHhP;ou?g-RBkG+>IQW%uWli8H$D`b^HVXnvUt^%9EFQ~baTZv!Q98`^&ZYKRV zh}y4rn$`#Axc##?_&03yCpP#Sx1P}py^nky!6o!3mIM+@de)s<-j}$bKXFMQaf!cT zoqzp?zQj85$Ty|1^!WIj!}2&ZM6jjB#S6eNC8U%nSv>HcGFWsD{AZVBW0%$rV$*jA z)2~?dKZFxQ5$r>-6v2cC^9WC-Ke&_Jvi2IqgNBg%5Z!rf2{QbAP#3yY*9;~ywopI} zgZ1k$1xVP83zeXm&Ws>ty`-NZoj}eq2*pSmv(&o^Pw6v+B$Ivgw}hlh!4|tY`uxun zan!&OD|!1^CP&tz8wF!6xVL1qNKM9(1sM93@X#erOCNP`I$b<)VKC?}CSE1I9Ivp_ zdvAgm{t6|r)8~dj-+(xVnsfNijh|3HSU z5>D8_tS#V>r1z>wQajVY;fR!>{m)%E_T1p>cZTCSKE)wW*LyrIb;`6C&$uBM%(9-e;YTy{f5i1Z94u^3~U_&@8$okaei1s#y8+N3qM0JGRVT?c>Qw(Pat>_ zz?cE-!rUVWoGdz#d`F6uMkXOgbx!lW-IZ=hGG~IhKQxDYR4?mQP?K zV5|_y&lz&m5JH#fh>hgYMjff*G9u^+9UM}Sf9zWwd4Q~A;${QZi;`mA3Q3TmNCS+( zo56^!ZSE#GZA-vXP%ERESCQj08)L{R$1vn3EDupg5cI}^LtdbNizHU&r4x?9J>L~E zA2czFOyo);=*lSK2hxBp#U7h!AX7NgNWYkj{W-Kp_e7IL)6Lj2uDeF}Rd@;erj$H} ztb?gojL9C>OPwTQo~E`K*mU>`-n^O4iy;}R6Y!*PjBbqyImv=n`g{zr(!((%lUo^a z>{l^l3JLE6CwgK&mS`r?q?Otu(YvV8s z%|u%wIR$;hLaeI0Vd8y1rK_XJ1p2v!tdP2*q^v7ztwc+fvFXDhk(3AQitsjou%(gp z;dRVCjR0ptIECQaeQ1TFGwQ;9_`Qw9lb_MQ*vL<%mLVYggxq{=x1Btwf*t26$s}FJ z<^(E)Gm_}iWHK>gHC)USCeZtm$qZEtHnp4fC6fsea!e~YYIKl0+f(t03Iq(D{)QuGrimEW+DI@V0f-X*;zLY!vczOALG#7F&T}*!wE^T_c*PDFr6- z8Jdwol2y#Qw23ZCAxNftSEtRR(KQp7M<6EcW-mBKvi!$ew?Me-tXyg*;WcMT@dgOlY+snYwf z-m+oAE{|azo9I(nq|!bc@~^=v0@RpIayYyC*tBf2ND(hX-jV=4o;S+a!C2L9dN_|* z1Vd^4fokZ_D}_De)B6S&hdFnxKFOHQ^iu<)GpTiy_6-NJ#}$ z`P^Gt;4~{Xr%SMus%}$rW1WY|SZ8#I67iv@5S+o57~A>>^dzYDY-wq|K@X=xz#$%K zc<(yeF^=Tv`Z3KYcOE@C4$iB|!>8W^WW62mP?JwAT!H$SBcD8mmI@3zC9B2f>k2s7 z!;qM$NN(qN5?-HzHHvF1gy%2ZcWiJ^C=hS(xtGsBd~~SmF3FvUKF6!p8ceYPHk*QN zNs-UVYq(Q(qABE|zp+~c@P5_Zws{?3^zzE9iUq=wnW`XJ2BVCpyI1`N6V zhJt{hphtInd7oiszhO?mFvmX^>@utS3~T)AH6RaIi0u?O8=xpj?S?UIAk_})evdQL z^~>Zc?NrE;vMJdtPl$3c;{eYwBh3bJYf+Nb1r`IzX28qw)E~)afVW0ULqMgw2(#oa zPV>-ad7?JFvagSX?6~C`I4B3UbSv9%Z}8Ad9T#@ebH{hWLhR~c1gyl-*bQKXlSrd^ z8L~!33vb_zNNuL^mpN_w=%ojqROmYjC18|v*8Zd^fut#Yx+w!D`&nILe@1yAqx@8E zAfvKRSIP1W{kp7xE~{G?(3SX0=Ld8P-Yx3WErE@_{rc>HKD#>}PC)ZdSkR|m`0lcR zz7oE{Ns0T9as0a%2Mm)I`zOQK693X=eTHQN;J-8%2F!&$@d0z0fBI6tc^NzaI}L2b z0UO3<+h($T2PFY}NzWR;y&OEPXAPi~K%e0>5jf zzi&#{=B~9pxj0>A%lyk4{nt198(aLbTm8P9-dEquCg-8GpsF{)%p{1Nc?yz&cn#ZU z-BaWR?F~v$vsLUQCh4SsVKyd5I(@($fcPAXsW3i= z(D}mA_V7H747P9;BN{$~D8$~!+DX1=_^(-ZFAhJ>WO(@7L*s8q8O^s0mH$6jOei|S z+EvyQ-xGP<<)7{H&synU+3Zhg@e4P;Z@lS}L1wHXm*i{)p%Zm-8U3GHx#`%41>`m^ zX9ma#$;>hzhE8Dm&hY9++6S-@ZzOQ^uJI&^;|P6nJjrf<463@)$9FU4ErZg*=vp}3 zu?~V^Q7!XgXy%x8^ut(C^5tQQsj3~AV)h=^hwGpkpBC~Q!yn1r(GDFBI#|pitD9~3 z{h9h$OEJj@9pKoZ5^|i2W-r5ZbX3__2s7xWi6o`{Q@9YK@RgbRjG{;q?h&IjCKi#w z6eQ*n9nq4|pf}=ml9(s>iiRG)7xz|6&ZdJ?GVPdL{C-M+Wlx%u?u2PB%Br;|PKa^-4xx!0xx zn&_DAmc7VKy0e^&(=)p;3>%uT=*N}d5&o#0Ow1U@B!)LDMp=K^x=XTj(J8aYG%2v{ zAiWs^AeS@SEPIyu1SRF4O*+%0rNv5Wc*LBc_39N%8C0X?hON|*&ZSFcBV(^U;@W5B za3cq-C?5;=F*K`m?R=6zmeCvMlf;}KF@y0qc?oiQdOj?^8UEc)|2m(fa&;PNTtNO9 zT596~vE-xsr^mDNa%j#%QXyXYy)Ir=ojGMYXreH!?4T+YXhiB_aJ}bq^zvpflSMBg zvGE$FFKMNG2&{y_P+p(Uf!ZHQ=Pn|1$$q+f5sA~lV4_Lz0gisPh>Yiqa_U@6vfA03 zCqhS^aU8gZK_+=?j%^)=KdtO{P8H|2A@d5kZ}LZ7ZANyaop zk_QmA8QsjHA61eGmMF|MB1pkoAyUh)B5}qT%rzlEK`tDm)m0=bfj!L1GNOwZ#c9p7 za|JQiT$A`l$2H+BN_>{hEY2ZdFU*o2XEw~suCXFdmg82ttk?%|_7Z{GK;)a^>9oDm zx>VhYKGQf_x0H;tw*+do`qv14q0MjH_Jy37cfer>BytDsUP^uyHap?*gJc`MXBqii z%_|MK2GG#0am1{}D+m?Qx*}KK^z(j2q(N7G zO!_$%7la-=b#y8Zc3~XzAQ=~w7mlxQgV%%*j5|TK{~3-q4uJI98ybbyO|UsfGAqMg zGdS^FyT!eub`vbq;J{_{s|8~i>W9uTmTrIqV@6MA^Z5ivwi_#6fvL9;G<*s$sJH=6 z@P`A0n!Rw$x*Xb(L?VjGunc7Lz{DS$mMdz_!=7s3Hv(V>5E{Z+t?F>Eg<@clFDuLv zzs!|m#5&!(0*ukWz_~g_pISkxw`}b)JsEpAw!65?(7m~3|}jeh(1frRvd*p$l}Wuo>=nNp*J&0yBleoIEclF`-F zGrP|+sY5$3Zld4f45)L?8sh!x_^`3MDdZGL2s;G~Hdu6{Ew&;77zHe^L&km;EnZ0` zR3Ta+h8`Az@i@zL!c6PPQXps&;i@@C@aQwUYdy6A{T%`q1{b8q{ z$>H>}aCm>dxbl%vr=Fp02Dx_hRR^AldyZHks1nZgQNcYpijW?TfbF@zo@0sEc)4<5 zSh{ZSx_-;}fMtB2rKDrtM+Qr{EW~NgJ+jvrHx$}gljUzjIcAl}-^eGJ zE>Qv$yBya4S1>6TG0EJ{Hmwcz>>1kAdH(SeV^CrSP8UNjwCw(vy%C`kPo|80H%W6g-M!<#0}M|w7QmL6*EcjD3l)MhkOVxnIH06EA4X_ z6>p}0s3i`S9146P((h_X*}6$x%llv(N5%W5$)B2&;fQQ@XM=|wE6;fKdwSP1wMHf}sklk37m&HTP2F5d$`IMjpEQ17oJ4qY4!wNL@I zf`)M4TVQZoK$ix{Kj>5a3OzkrPX@>mdfTNCrs-V~BbY{?zeHl`y*@dd?6i+$#18*f zkB`Hkf$H5n`?WpZK(}rr#iH(vo+cazuczY+d6f89oyiVCa4}4!H?in3vrEm4&KB|T zYiSV0#er^bV|kn_!li~&|CGEIb^1cW+w&tv`B`FVRRby2%?1(*In3$(4P*}S)BXlh z5P?HFPZ&>an@Bpm$Tav;g%taZm}VUDAnn{la-{vnW24TcDtR0t3=#p4*<;X&E3Hvz zh>|>1J$N@2KvG}rYqHXbdnfLj+RgQua=Ul*nI_U#t|zgUwA{`` zdkq5#j!w-VjF!tLP`LhKrJr4oW()C?_hPwI%44@JQ&8CeCPdc_hD%(raan{A(1#I+_Ef6+-YK=}Co;HZ9eb`IV=q)!x(cB_p zw~dZ4)dGie0I!RxK2QJYC0R<0m9^50W-?KONn0@oXA|7EFE-lFFesKq9 zlp0-7#mZ0JJl2luHAtrNhH!cSVg*5b$aT zL?LS+X4k~lRy1iV(I%TAXkR}S5Xh~DKI-`tw#YW~sp!9P#fZ;8YL0iA!vVn#>E+kBczQz{iHc}LBAq3N2rrOt|E`cPr2oDh?1SI7kvnvuKqRD7 zO1F``7`=7R^j*{U<#q{ux}5G!;5eaYzX2`a+igT0ozs2&bKWD~dUY0Gp#yi z-A@e-p%N0J8wHiKLI-MAF2Af6>wX zUNG==T6da1sVPj6@nipM1}fmQsIdBV+4pq8C1dX6wTmoWdd=88b@}atPLGz z@x9c@T4mID2q;WIk`&frPey2Q9bQ%;U@8!62DP8iu9Xlu{zM|j-M0fdevIS@w(T+G zxbsR%OrbB`K@xxjy>QM+hk8h7rp7x-+%d(S5UcfniQrDDs%#UeO{K&wC*NH=wmEc*1{An5Zz2eD3fMOR8gL{iYkTC!4IN6 zE&%c4ehuV~VfbnzTp4*Al8Sr%L(AlL{LM)5bY2Sm=ie0iNDyYjMaR))i{W=*RIrO6 z7HqaepJe6Z_;@C>6I`YcO(sDfPx4%n!li=lrFtZ7)b+{38{WK|YNM z+w@``pM;8{DFa9a3qru1TgdY>5 z{#{_r|K%3+;Ly&ws zDi(1BT~ord0;ML^r|iLa3`Q!$0K?i``;s|cJ4 z*+y)5qzg@Q_#rG|3ufX;wn8(476e-n2nc)#n4yuW2N?Y^mcoBZpRbU|Q+f}?GtL3s z&Ucg+tp$1e&OPMql!3UEL%I8N{h1SwTl(T=bQlKg$%l0NbzN0G%lqt;IwI-4_Yzn8 z@p=BGwF3o{{EKP^3a0uOK%hHkT2DG6{-KuXO+$tx@O#hiOPhK&!};X8!|VDpCI&Jl z9@oGDq8ao0GZqCh7Wo&~_}8uP%W(URsUMj#n2S9xV9x7a>z@Y3f@NpSa5S==F4((Z z-^#A--M4f%`^(n)*R}Z9ZoMSSf(-^sFlauLMbKAq*I~@OrJ4gu`@nF2ZvMdn8d`Ck)>iF6^W^aXlIPD3FjKM z%u1#K&Xblz(xI18h<~0PaC{Y<0$1k&4?sI)pkH8AX2@>>2UfW2ZeSkwAbW1aeQ+My zg3qd%!APtSZ@>@sHVe`rVQ@y68-8T5-i^WR4N}pPnL-KlODYil=|p^{U05NM!5ybb z(lQy6GBh89;Xi=j!xAkR)!bCiIwScqf|23tQ0MS+Oy`18-XhqqKJsG0NZh|Z>V5EX z%p*P%>Z#VyK9ud?LV_PgpcJxHwhyu!GQTM+pw1r1DC(RZP^Ur}y`^*O?y|FqS%=o` zU+2%81_$IN&VlVL37Ll~_g8k$I6k*8VHO;i=g4_7>Ts05VAiQQeU622)ZQidagLID`m<3@S0fjSS8f@q1*M;4K*sqY}kb4uDQQXI);UP2I(ZD7b{zc=(?$Izn( z!_L`v50b*L4d*`xAy|klP?MnvFx7nTvXaF)7~Cx_aGc~wUvZt;riWGrAG-bG&kjkJ zYdEp@F?=@+kQs?Hk=C3nxaL*38#IT5klL9G28RHa&XV%W!p@mu9D#413gw-ncceXx z)qk%Rj9nFW<4{DgW5~Auf3&>^d{k8yKc4sAq)vM8nRF6T2%(oi0)!sKBnYBF=n+Wp z1?)0*z_kRU-k>M}Td+V-bWlVQU02sO0exLs@ z;+^;2?dRTm?z!il(x5L}*-Yj!4{$+5pSyZ zpbDJf#oR}gMN({HBg?tKelS1;Joy6QhY5y#0|>fI6Q}P2u^E0V=JcZNN-Z?>A=Cf6 z?aE#0KIhv0_)ov-z|Hd&T_w(hO0i;x@@imCX;-)-XPg+gQz?>LTL5_z3_Kjbw|>Hs z2Z|RwakJRAQ@K4Jk_P^K66?T%gJ2)c-lm|oE4L-I*|&u%!Sp27#A!H1(xWvPo{j)3 z`fD=JZ7-(Z(77Asj##TUh0XmvYyOtf-e;JKF2HkGt+ac9c#>Jh?rJu^nCAy_aR}-| zYC~wlb8}>xLX6Ua+@%c$rHtxlqR6?2jqP`*vHd<@8%#SNs*=*uFy=V-XsuaT{;MQs z%`J8}+XBn;MxQH?8nPzb=WwxioRXMY8{|%h-T1%UJ!qNZY96~PMny{+9bO;dNkWXU za7u&(%%h!qN^Z$8KP$nS$5luL_-Fb zkiV=c*~g7sA{2&`Nax>^GNnGnfNdB`OsY>Yx*o^%rl8(ft~b%BcRtsf$n~aDE%1_x zFb)S7cbV?rrZkF)@QG$Up^&TtTW)!7#2Ml`5!BE5@Q~fcMQ6!S$*IX;Eu z)Mxi8EVn)vg-PzBzrk$`tj**m=k$U}4#OmmN+!W&z$DK*BA&+OQ_LEln5F?f_2<;-*4o4Mhy7EtpSN z-27KxC|Y(Yx#q&kLR{n*HQ`r0wo92*Ksy5yg+5Z53+UVRg#T`!fP_o*>%@?!l{{-X zeIU|L?mqX!{pf+7J0EsMFNQ((-lP?)OY`<|>M5jw$A;}auRIB{6>vcovY~b^1~r#mB_K65i%TyJ!RENx!80@iS$Y_mezLD@CVUGb@h zU!!_O^m9szRtL4B_-B-Cc1~VhjSEWBj zDITY4qG3LWgz0=^C!hx{14*5`#2};Zl@dNA5LrmWY3%;7n5ZpFP4edXMkP4yc-nyN z3%4%pTmVg*wDBTpw{np+GO>M(Ga|n`CBHMsky6$g)SW(nOyNPtd;gWr^vYIqcXAGS z1hp58-QVI&p3oX-5X_y|@2_-PDj-Xdoa;y)gzrT|I)dN`7^HVvI-K;xr#a&XbY8x{ z)>#ZQ{%IXiTWVUTcE>`cB(HUHcWh?IY)7mZwnvG#8tFbM9uHxnLBs7S1EFQ!T|Ua5 zQtF5t1me1Vv?DSf<;KD}PTSb-K_fe+IAV*BlbTA2BetaTx+AfZ5FMG)zS@aHLwaH7 zXh-_+R`c=9flsCHNbd^Wzr>kY)f(QNHqenal9g&(L)ynctroe`8wSV|!uUfaQsuUM z>>;zGe0pn$Y{2o-Q5|NqsJ|MzWexU06A$4Icq&)Xxk^@2KGt1=7}up(tbESsya52Q zRJMk7rxtWhbEFOfqy`Q#TGKsvOlw3(xFfREr!GrT=X{4{BywaIKUJ}#qO0UkrZamY zOm}x@6l~wHb%VWR{Gl9Y#^lz}FY^mK$L=q(Po3u&HP4xUsT~diWAZyQarM?+X05bO zoa-o?+d8d%1Ax|DKDl*P$8>1<_|%nF&Y*^lkeRR@Zja3Dj>+k`5*O#)gWp6bzym4Da5@y zj%WZ~ozqg)8g?u-qa&;<#9KT2{*o;3^_`8xfO6;>?M{$v} zs==ALm@frEp#$9RZ-sbX8Im_4YDT`_n{iX==l%SUnd#d5Wmz+mv=3qw zJb#enk8lZik`MxJ6#*|O`B0HJD<+ZIfU_By{TH_uo06E}YHbRb4`d+!=1%BE>;?M7 z)b@%A@`+i}D<*jEm9=az2fr-&I``&U6YX*MmDFHBZ6GsaKtj&lQfrnRn=Qml5hwlt zQuymYEi^O4r-H|NRbZaGx7KW=Ef0fpJD_0tYYO$r@@B6rVRIb@4<@`ILQLJK6w9rT z_d-IJywNaHl)ESGi4v7Ew%kUqY%k;yJi$z)loFwh!EJ7s5xs}8=L&IiH&(9IZ$ln0 z`$Hnzk@|B~=0=WkBXk)h^Gd#kzLydNQ(?Tv>iaaY!=c4X*~=$bMm9#Yp3wrV%tP{Q zOb-%v28WI0f&%=qayx?^nPv8%biBk{NQ|vxMpw2oZWx3q6SK(sXUFwjRnElWo5H%2 zvbU#iP45itYH%iv*cAR{Tw;5&BWa8?ZtSLzV~Hv4a4v?F6E=k%OG<5D06vB@Y0{=} z>+$U3)-jGC3oHs4yppU^$wkR4Zyo0d%0|_p$?cU+a~k+6tv5JA!B!b5c^#fs*>gth z&yqFyR_iGf3F9(E&6b*uF3n z;CeQEfr9&iK#1>>KQNmArlug0`8`M13Pq-1GK4H6|31`&NC*UT(&7b5sAiTg5y4*b zY&a|hiW46z7GfddX^o1faG4Ibe~Ug=2Z)>2YQY&1^+BEsgK&_>!JfB3yoHE2j^Mrm z!UdV)qC+Yi`GB#3t2>0aW=9yqHtk~hhA$4+0`YEM1&&M!AYXB6Zvfp2F?0Xg1STni zV5>Q#-drCFvsgG6%?}rgc4%cm5yk;d+&oc@k56M1Hfv5SQI=0hG%^l^8BGQ~dmVyk??AigTx- zLrQHj;fvZFiA%tWK_rpr;*E8 zWBLK~O2*`Xbj;5}8b$vsq){Y8+DLODqybErWi$YtV8YCe7RP66*)jRx=i$ghlSy`B z5N>@xGNoAMtr9MiaMg6`KpS{vtZEAh(=T6n{K80m9hQj4fDKz&36M8h(((vh*SNx# zE?W+3n($o7_7UCYZb&NJ6($ie;z0YhxUAl^{2sJ)Jx&;n%}sLOxCirUJ!YGqZv&-C z>@WA<-Lw0ylMn1;-rjl(C1Ocw-=vme3vSDBg?946mq%Gx`>n`wlFw!G3pp0^E3%wv zf4*n;Ci#gjHH$2#_8#bY^f$;8$*(8xy7|({aQ z54mZV&j-oyG(Ld!Mk(k(`3Y^rzrnly2YNb6!F%-DjVEr=sTU5MIvH zn8B#@>D^uMXCYM+Bl=~i&ZGiakRXemP9SiFE!UgZkmYeG^sQOT2F<024;?*Wh??c( z=+cp|Ddo+h)z>t0e_Yk~sresJz&DW}($hy2*eP&O@G%86Q$2UQI=ouLBJl0}&9fMI zwS;?K(aLo#YVVuLeWKwzQ%WKo3yq6Yc&&iXRFSbIqazVI5>X|a0=g5kw?}M^=#1DO?Mxg4YNYRbe5PpL z(%i8Imz(h;kiTzy-}jhwF>cGaj!~UBFqCZyvUbO2h*?`eUhj*wB|V??LW(nX?55!F zaJq|s^zxUA*|$rfgZ#MIhfTFlU+Ii#w1+l=?g$PueAlOp-e0|c#G$eF*$Z%`YG1h8 zzUDgn>g#d0c>|2N<7cvZG5u6an`E^ryG>oHuBMOaOW-~gziZUB^pkugg)Qp!bQ>Cc zgDP4NE3sCnh9q0Mlk(_aYJPV{5&a{*n5<**=||)99r5``%n2@B9O<`vf{eoj#Yd~}z4Jw45Oy}4mK_lUYGBje-_3agoh(f1%VCNF2 zxvaZzXjhY?aMGcrhv3R_#-{1*;f_!%orOb_J5rqHqT|^)-Ih%Hmz)NroPpsoNj`bO zOKq=lL=N2K-yIZY56^H0Svo2mLAm%nZjK@g<d3j&e%a;D^aYzHwGP-kOKcgT z##lp>oaUU)wEY8|1yvC9JepeONGK;%)|FUw?qoMIeJb9f;Z*Ou$79Wiq;)op5b@`FV34mE_LhFqk3*g7k88dXh z=8PHLJ*4tbiPbS=Hd%PR_!9e&x$Y7`XqR?waAa2C_y_-e8H1dWWnHr!k;CwNJO<8! z3cF*oIx3v8gS#3WvBQtV4nFStj_-g;Y!(~edD$Z~qx`w-e%fWj$Y~Ik`2>+km zDJ3xS-4TVK1)r64cn&)D>mjL|eJVFn7`R{icX0 zlugEqAWvL@eX-?h)>AGQg970Z@xhms7o_JD+mct5Yt=Xtks?f4L*CkWjuGcxRpxjXavk{P^6|^05q{ZZ41Ud2Z*3-QJ7@XoZL-X&))dhc4yjexd3{Z3&63uKXkV(4 zckrr9Eg9reR=3cAkjAU{<~5~KT@WUQy{-(4^rKPp>U>TSUmb!(0gWQ!kdow9Rx0kin!BexZG5ZYj0IuVcCGZepx0-8&DwHWq>l9$iM!-COdcyi#qm9+X@ z;rcRMIzlwTa5&Z=Zhb>3lyd3EDRDZh_KT0+Py*FU!))DeC{`tUgd=;1J!@#!rH2CF zF+0ZB*~i%$-&C&9tgJITgKGUNGM{+|jX3iy4Gl-YsI6f&fs;(et3j+BgfZ{a`=0yq z<*sT;j8Jy)qOYW)h`8+6$k!=~U>-CrJqS51=S6mo+8xP842Xzrf%4J%rgid$G`L|k zIKDIkq%DH0P=qVIW!Y+6XyYnW#&|&0!ZQIHO7c$%0RP?KJG}lo$U;9J_S7dZZtX@U zU%vyBbk?cAh(g@P^3iry!kLC9^rIS2u<~M*I1fhQJ7Jc;Y*?+IKvyq77C6A^n+aR6 zr-v{ptI6gqJ`S@v-cy3eyoKw1<)C8z7xK%onJRXEs06Tm*fDnL~@hy$Z&D1dbuici8&j3BYsTFk!&t;27#<7X>`2v*?Mi zs@r&3zw?=fEYyGi#uh60)Zl)!sQtj77cmzpN%D5x$nsU#OLhnq(~!YaiEWlc`M0)Y z8ui1A_1VlPcXbMC)1Rbv;lXz&@EJxP!Qg%=-)AWkcl8GG+$YM=kw3c))KNwC6mT=R z1#HE34t=}CS7GE%G*&8%h>QN9gao97qx=41_Ips!SpBjhofFu0{6Tq#M*50wWsWI; z;Xh5heN4#>~Mt-UU1RADFMB1k?xwo9!yiP3sRH-Ot9wDz=TC%6EkKH}_ z#C5lglZ%wVu4@3Ydj@>)snU$?Wb9`UR9q!mKZ8#AHWXGTZ5#7UFv*#~2!O3-c;>_| zvjh~+2|h6phtUTGd__~MzsVBqv$^&`e$fO>SIE_5bWYBdm15lIN>g$)wVgTu9caB7 zj{jO(q%q%Kj_u6ooV0F|_~3D1z?tE;;N!|*4PYJfh4Q+|96`M|VEdncqQC55=ghX7 zPCt3GMK=IVN=i|b&}$JUnqFflK>Pf3337UhrEhT*#8c24{(1s^qg|1k?hAYL8C`hItg#z%aV6=~dbPAxkaT+bppl1sOnG~!-!}KhA@)^P! z`bY~{&p}KtaZvC4+^vbx=alRv(`kJFA1~-P&M70+P!p?iNwJ#W zurzwx=Ig@MZ^Lbs|4^b~wAWVmt+G>-D{#8NEE(v#0&{#9Uv?0C7>7RzaohJw_R!wL zU&p4v`x|<_lz?T7wK(>@k`&1!&4Ua(gwP@H#Fjiguh#W9MWel_&Gi)1wA>kB$Gl-W7b{rhSup8fT!H|t|5 zwHJ$J(<`rz3Os>)@gn;^Xo)B0I7-fwp7$?o+kO`JpCmPgnAp4oT|PhX8Qe}RiFom^ zsy~4STi287pi07D!6XjmV~9t-Gr$?QFuEYZ&uw4nbAL5Qxm=h6)N*SizN4W#X)Jml zXJDSQ;TmaJ<2!K2@qo7;MBw^?*1S7IokTqS@*ogPc?aTHk4Y20`5?4xH%v%Bpt32TU&D*f(d zxdHeyqfUJ{Mv8q}GXxT-ibMn#kZ|}u1$c#Qld>tM2(ucd{w~6nj$fi&ibB=DR&#@- zz$Kbi!%haI>0ZP0El)azI#)*oKRn+WvrNvg=|(MU!_+wS%LuVCOl>xmAdMJ*&=jxU z!hY7Xl?)i;OBC?7o}(^h+Z=7<^CJCWngeD#&K4(#J>jNOGS1Ovxx^&?8;%3$-T&4C z#doWem;tb~6o7k5bd&xpev}K6K0)%Gom~QYO>C~iL}r;pO{6*?bh+PHEyHiQ0@kcy znPYsE8ahw1lcnv1u_)sIz;=Qh^Imosc+iP_m8Ln81l2*NIp#*GY?`B>vIa*uHrFs7 z$ymv3CJbE(s{)&*o;8KQaE99kN4X=e+!;5tuhD}4VS{6CYw-8S zA`&mK!EtEzp$Yc%Denw85?bqP!k{GwO?@M9XjL@48pa|#934_sp&d?5AgdeY3O$eX zM<^=CXz{jZV!_DEok2^@yYKSsBSP901hH1>o_a9crzWW5<8~6%dLtcuw&+MuFCOAW zoJ0WhD?rP2aPf77Qcx)EDiV*}MR+7TFfGOy7%J($(M3_97TQ3Nf<;K!Tkh$&=}eX5e{}wxY!iE2MGoKDcHMoNICX)q%>Dh@N<39S5d6&3GGp<>2(bSO%yaE0Q_a|(0W%I z`JRSooG$JvR`2#P#rTW#p--nneAbTJoZTWK^o!MNB5n7UsA(X2+CKjw$@cajwMEH= zr4&OjITBA%xI!hF0a7cBDc72UXDx!OrC70Gu$odDiv-eKMVu#7e4k7SoM|*NC32?R zL{Fw9Pp0HPnNq|XqtpydgK%z0xeAlkwdvxTay2tI1GC%VCCT zE<_pfMCPqprd6rUhu=T=m_|Z(Bym#R98~tvhxYVv#qP=5n;ixSo924v#dU+B&6^m=m{UeC{7<{1!E~9^TDz3 ztO0#Ft&IPhGt@H$ROu!P`rrdT8}Ol-cfnoBEuY+%WYcsMo(S9KP9? z*yERq-1K{tI~HijnFa+DMuda*k}Eepo4>yJdNXA^@DH zOJg_>?s@DbiTRipakD5GtKM6{h=kc0{lHznol0XK#2wkr!(KnwuagbGce&TQE*71fF6?hqj%S5>dfpJfo*bU>7%{BD_F| z$_zvm1VnYViV;F)2tFTVxJZ;|pZ23lWiiE9H8RCJ^JFa#2vNrdxjw z_2{=z^8S_p^Jje40j1kWL4UZ1(T?VWc03qI-;6v3<9+Axuh?QQ#3RaNHBK^Mt~MAj zKqRSZW|XLztbVK%ip(i$s_9C5ye=xI0Fe}mE9k>od;FEXK5z;kk|Ocb6!jZS{pRbz zQV*S`ZqtVRZ- zEfmog(=dD=q!{Mj+=r)v2*wqQ<(2KP1u}CleX$0LY(r@+gi9^G|KtJu8Wa2Md2pN> z6}#{%7`P@Q=gXszrCnl{n5Gr`hTwDlGS+=z3~0!`ZuZSWtHzb(Oa* z&t&|C`x~!ia(ReXkD{Yyu<*}Ss4=+ggu)I~KIoY8RFqAbqrMH&nW1{(5_P&3 z$4V5%s^%E_6OPifa=S@eTItYkM;m0FvbTc`f5VK15G|$+ZG&b zFH&MKY5k)}gLGBnYWVD7uy#k;VlP(A!Zp8zh+|3$CRDODuq562C15J1r{O*0@CdCmqh<`nXNA_MTn( zc53gX0B*J3L67}RE`0|@{GU%QMu`lMom9YM6l`RouJ?XZr$2?L^|&~K^^*O!pV|7j zbg6C8J1GmZw)?>9aYR6G+RKIb6|mxtA29n)Chrb(ok^VApz&l9e=b%N)v9RQ^efa$ ziOv1wYW2&2?*Q_sKFv+<5I8#kVL_>MF8 z-=+r!Ph+_1SKJlPjoyV)0V%JjOVMxniV385laHIj*m^WMRwt<;xqdk#``kQS^-zY@px<3LX}hU8P#2G|D>6I|Gl4|J(*H?e)>N z&a2e7O_(O2IxK|c9^xc$$C&eCmnoQGR z5f#uyuX`zYkLCx>BYj~N#q6VxFH+EZAO4jy=b~HH?26l|p#RJ1Q)&`>Z&l;18>x(6 zoI$#cT1oT{NFClfcqj2rmA9x@|L5~1iRO!2Q1@5hh6|_8&(9ovKed_n`+lbjTHKO^ zz#@Koo0=rC^7US+e*{)uQEu zP?*5rrT!Qob)GmFr@*f0+S2p-?YQFax%U=aNa&lyk-OB)0BPS){QE97ZO~4F9y7OT z-NA-L3uNrsegyMKm+5-5uCs|H;_{N;$SsiGSa7$R?881|O42JS$c>?P$Q_$kqlK=I z`eUCxDKhurp8QXDtLA`K>T`g&C`5}BpH9@$MEX5yx50W<*4d)&g-fwuoPXD&?6p^j zH=cmYPqVl=REbei#dWP}T1;qdFnPeJ4Vyc!HoQcGv5*MybgP;Z$-4tfO3Irk@mZ^y zW!_4?XKNjgiO4N#&P5EGH)!+@BXFyb5jWSp?{aUvJzPR*IYTyenC`Bm$qUM_KkehQ z4z!vfz5@ipnnd$`Cdw}wroYlV-YadhxsPDws9(quHU&;Fr)l}=n zXbepFbHx#OCfizm7B@rnP02Fa-VT=0nHaIJT^)!yKrVW$c}NTs{C*JaR9Gcu0N9MG z;mHH&jl9Q6y~bJo-e-7{@c8PegN-4G5Zt~>M16yS21i9!_Utsh0RX&DJ9&eln75Lk&F5i z!`3g1V)(gFF;PCnM2|*48-}4zF}1O^5TdP3sEsc1Z_;WLYhm7pl#*(r#gz}K36og^ z3{taRpk<(kZ0n&<1r}x(XO;t~1iIQRuwUTBuN<~thiJn2kXo$ef``22uqn2PKj(Yz zS2Ms$ZVIdnTroiY`Zzf#5EC9&$68&huo3#FAm@puN3S#}E>I|(0 z%lOSl)q;s1U@aAB)#u5Hq2!^TWV1xlw4hX^se$8#D#ZI3J}?}=FbaD{4BD<%Buzky zelBzze0<8V@=k!86BuarYf%7OGGkw@1dl!*k=R}XW&7hXrCqSsbyKU{yOa+gxBSse~4cD?zt3;|8YEdhoax5fH$(~6vJEzI?3U3+w&pb z=WO>8rNy7Jh}OH5AaSM>w841L#4WqXfMkL#dYAgG7Rn;f@^E)EOroe`l<8vD zb7~dLp5OPJS}^7-s-TCuuAfARYk5BU%4JTy@>Whe9(G&ze*3i{Tf-7O`JIs9c{0|P zxm*2Cfmlysms%Z)#=zexWT`xk+}a_vZe9 zB7J`!?^2W1!?EH&UFv2@?x<4QA#soTH$#84mk10G{CV_j{XlAd&%ImD+;p?V@4o4v z+#o;Vz>K@auMK;pt{6hPQVfPS5}LJjXzN|nZXTgL3{JIl>pM?$oP6Pd(|ZoUZSsXx z@H9u~$!U$mtNnML3hdqa2O;B8qQ*ebVnwg!z=KoEx+65?8_*m`-NYpwjVpA-6*}XJ zHieut$GN3#$XV&U&gu5yi;pZ>ZeMYwbBS(W++t5#XTNI0kx(o2;zY%kijIQr#NzIh z4482dj_q2qhF8(NSB+On9)u+>Q8G$9s0919_WcL5rS|{`ab>p} zd*9}u26A8EQ#IPigK&lhp+yctO2*NYVUCnx&Xf`Mpv05rlnX{-o;?jpZ%dDaF7ttW zIYB;W2tz)(I}xvz?v#n=0!=01-}sqgBhLE4FBwc9oLi_Rkycl*I%y^$Sr&`o6AIRr zuLOT8-m7&5{rpAs3-R|C)Rd%tH7!uw-E_>#5Bx9n@2ue|a_K(xGjJs)yr^EOvQspv zKz!h~o^9K4_q+nlpuh20CfNk#1~deENo&DMJ!22VN~F8V6D`k8bT;>lg^mCUxQgYGx<`KTW<~NrImy-`0gh#_$gw*8|v^BW--uMR5NQ*PoYQyz}LN@ zPIH^V{QVp1Jwp2)c0t7D{J7SDOw6>9ipSlzg4S&M#@hN*w=5XY!? zmXJF^eg1+17k%>_z}=jM{zwhaa7N4S{f%-`ZF+$eB$Q0Yd+|l8QHk;IK>+*lINQwk z)W6W}#@Y}i$u|2#bYxKyb&Wy7Oz331)=B83Pz?8+2DlnC?nLj#(xCP7wny=Y#vcp@!e`nt0`i3S(t(jO@YAOp<^xI0^;xFpZ z012s22~ve3?68&~b{qkRAu(RObVU7gua-Uk2@s=FCbk?^rvR0pIYlC%Ta78E4GxqQ zH-P7{cQ~mPZla4v?S!cARt@j3pV7Wku8xTpPj;*C_p0lzKhovit)li%>cw%)jPdix?^f>+8N~N=Op(z^}1O0TKN?G zr22SU(x)&sg(?zW9tSexcUHa3^W`u{sgv+6aH~*%#_AYbAa2$S}L2Nqm{CE;KeZxKSyOHFv&>5|5x@ zBn5#gdKkF0QD@^IK?@fyZv^9Dq07{~c3G3lbVW-uLUP>ZimFCTP4l{H(k`7u{7qNH zwAxuU6Q{tw!qR2@#TZB!{A0odhPbiZC!ikB7x<=G=`v9zeMQwr`;#(6+x7~7F{eij z54ajoYO#w!swv)fQ;#}RQ?3;so>nt2@{*p{p=7|}2I>eOQ@Dpr&lqI97dH@vs8fc% z^|ng;^Ov5%@IDi7)6b|?nw6LQ2wFP~^(pkkpr1-l%c-ns^h7faE%*pO7!Cv@DH!E> z;yKB&^sTNMijOBKGcP;uA{b2>xbbgM>`CI+xpjW!qM*+!DIle5eGCO-{l&b$t0OXK z?&{=f$Q8Z1p#_E`F&=Qh)j@8iC0C{+! zwz>iu*04fu2Q`-)_ZMWnz^CY4BJiA=t^Olk6rDq@C8Fz`dfzyos;E9L+*b~H3H97t zDBgUJWShjz|4@t6|Hg|w|4@ga%>S6->FlR}s85+cqt<*%L5$e@jXElWa1t3i^X?}m zZzsU=5BXNjF%+fI5?68C*Du8pyb@La1hMj4mTmrgxhcu!{8lZ|B6*~Fim+OeT||Aa zE>dO+xL6n{Hh&M5iAT2Jl=}G=B`%6Cl+HZUPKHF82KZ9E{Jokrn_5(@uR)*c^wkL0 z({;p``|l*HE_Xf-w-?gljXc^n6Cn7yYb`zL6g-Tf(66K?n&f(uDEgP08Od{v=as|) z;_`o~d2vh$@!Vo6j1#RC?f+7z%1Ls9UCr?%QGURgGa|v3_Jex4dutT;|3^JM%-dhS z!+r?$8`SUj;|f$4Jl0>$Dn`cqaQU;QySVd5wIIF|i7%KG>%`d&@cr=VkB|tS1K&wM z;-GH0nVwR@yK z|7P)ls-75S+6hn_lp;OxS`49r2yjrT2MS#0GA(XyUM>9% zduNTWxW&&AmHI8>F-;pQsj(O2X;yT6Yzj(ffPq;)__ZusN1{P3D}l&bdiXBKR$|hQ z7|T(f@@=UB+8t`e>(oR>LhI=1eF{FH;6r}Gbd}Patlq7^Ngv;(z#uq}2WeSFG}+*U zVReJV6F!Gcf1kts2eb&^6eYo0>eN0U@*>>c6Rg)Ai1%{KJJntnZNXZNa*z0Pur^A9 zWQ%0v;D9MALbOnIUxIB$h*rsjs#)7&s^T7dJMw$#4NpGI14DpsGCln)P~bZuQkxWT zm=eW`XCt-YaeXENmp)v4AE_;{@)`L8e@XrI_#-ONPJx30@3ipcgY^pqO8zzlA5-9@ z;0Ohu`1vKkS`$4UrJ$RFKT_Zl&vmNFa(Ui&-@~!O60Ic!{E;TiY{~Mfi`FVguA(hg zn;iz^)03%{g?s6uXh2Iu=4uOyK(~9;J){K==n-&mQn2&B6arz$5PAaY~Y$sBcU;>YDCNIg?(!DQ~6M=PBS?KBFh9*(Kh|(F#&E zyk8ekSi;nfQld|!R!pLx+Fyj_YK!xB(hF@y`eS$^u?jDI++Au(6c6NTd8Q|jf}qS0 zx@Ca=lqYW|f5=u)m} z0aX0RSW0TXRul6$ZZw#Py~(dj=_-1jxFt8{Yh#BoH1<--&r=JGOjPPV`uHM(Cz1D? zAbjGy1D-b{@7a89)QG1k#V_aWq9}k+0+=~ZjBgZqOe>wNly>n0bc+gb# z_n|+7nz_d#>FIy8;n`k&FMK|X%i!!~JP&@k5xbFHe}$@H%y6PTYoVcs;lR2-D({tKXTkFHvkO)bf-w;>|+sQXi*po5X;D+HV3RrV=j? z)bb;@QKKHAU^@jJ;_N_eRw^T`6d!&EZ(x-)#N)*LBFz$c13mu@lh36!G+nDFh~E@x zIo4xnkF-T&TCuu$ao>YM5SdEnlL6RQs~gwpHB{#;3NE6ciVDr5pbmk{)Y7c4E3o)5 zviod^eH%X)g}6cu(8$xm+w{pyW?YQV(s>jwUIMN}mozI~-~)l&UEK@|OgQi8I!`u9 z8;U!MwE+S3lzxKPSFBYBFfOu?>F=zt@e&!eVIhF7BOSSSv$O;?KGC+IMEfL2N*cXK z-Rixm5T#FF8Q(KSMM&Rm%p`$eN%5{goN{omZ;}b!SY0CZeomRcr_M+>*lU*6Ld(i} zi{dYA!?if|<3!ts!?jh)Y(|Wz0M`_sQ=OVTNBkMjZ0!zBE2OH!B{+!mXPBIzkLuEO zEhaoz@F%-|Ol6@w@yJLmP5mO#_UcHjUMXSvfbR_#o(UJQlMkS*-t6T2*|1*Q%Il-lZxh8^qqS>OY1u6y)ad>T!vnp4^R_=& zQ_4berQQAwIh0IX^j*|E47K%xd4yBR6`lAIF|Z0X0wOPs@BO(g}nLmGS~X- ze7vdg4%=sE^KOJRCFB(#?{R(#d1RwKn!WbOt3(!W`Gi_Uo+qWeWaoj?cW?gk6~k@4 zzDj&lr6q!Cbh=8*wKA`ZHdeV^^4xoqDk3VBfDQ>JTf!2K$K6^CMoqS#U#&=?I_n($hc`1Xji{;A@lNm`OWKAa5_UDaA_KrlUrh)g`I=2VeZttAbhPvL%H ziT0#{N0Uk&Nu^!p{j;4(#md6sC_bK_)r3^>tUEeceo9Fd z$E&r3BubU;7nagK@n~uljAPp;)H_q>oXtoTk(0G#w0fZYsh+H@hJUb=?gP%l3+`6`ci+lp$T0I#R~dS29C+G$_*% zl-W^wBxvGs;h3y#!10f6d>|(y#+}vD+e#Z^)uoATTN)E#qrlYmR~y9Z>7yMApcD3XnocQS||CBg=!R*eJ$Hchp8v<2VKc z8?0j^hQ8EGgYi0)fCQniKWs`( zuZ@PCZpbE)b03Hznc$DfK+_gK0KVZ_HKACkjpHQo9z>h`n?fo>R=_b7{rZSDC9bFK?c~GzPCnRmdgn6|X-FeT(DE`~*^;fX zQxOflK9%zDy(qL+Paiz!$!T@lPkIt5n82=)rw7I@j8y*)NI}@{1SMd0;8*y-M-awl zC%4_y(|V7r*f8qL2d%Fu)maJ{L#+p}*5{xMQbzMO6PA8uJIRA6Bg)e^-3~pJJxIx| z?w#D7S_&tv&qgW(6h5d|76=|u&&F5I+`n~{1-42UyYaTn1R;0k3*@%?806(MYZX4a z@fzVUS0tmoW}Q^OnK_J`6MT+wf0oo4(`??fO^r)dF}WDp3u#id@UGgtwBcG;1QTsx zGM?{0Ao%Dt81xV30_XIDdKVDJ48qWm6xHzHS~$1=W1KZIRs?McYEQ7|j6alNA3xhZ zd%iu3M52yFuIP@82TsY(Kbke%ku}^tVu~{hE($X~yR6xsG5Bc4Fh|C)t_}9dmmJBM zd)CkID%iKRho&M2tefTzcSa09o%9#Gh@XnQ2An>r!~pAGd(E$M7>q>SA( z<2<|_hQaP>s+e)#jJBGiu>&2k13RZVV~1}dpDLxJkCs$8N-FH*F0x-->nwr2*SU_8 zxtnI#lgP;-Qe+G~nqJ~aFR_PdPihArM7g{IPxbPn&ilz=8Txu9Z60PB8xcsXyhnI`GP}10= z5jl>CoQ{P@Bc?hcrgjtPt$=;JJ$qP})jn*peex>%>Kp8I zarY>7%670{JQ{kjBlO~b zpVcVE4=ptFZz-Kr?Dzb{EQG&}k4vDR;*jbD?YG0@tE08Iqy4KB#JGc|guvC9ERcF_ z`zX$$oNqfDm#KWwwl;4_WRn8>>l)*A6?k3s;&t(Agkp};;8)I6uf+IGxfKWt4(r;s zyc3t=KNRzy{+a^n4Gih$w-i;(Ok-ck!ahz59z=c^+&d6Q0uMpZHE=XC@;mNn;1??Z z2m*fZ>Vv3g@%P`G!cyn9(Kf+p;P463xk1vS$lMVynRDe@xR8A02{l-BzNUml<1z!* z3PQ-MK$B_a?S6h{QFf1(Tnq=T^(NrzFd*V^A3Pnk0{;OS6Zy`6gp9?8a;WQCT1_Ua z#Kb;1fQh3R6GzEkA50v*ek6(@0nmfHPj0^HHR) ze*NlZc~J$h!TB&@NkXkw$-ZIS`!D+F#iUH>5+-HS@6t4vtozGX?yyMNRM>m^g(gV$ z32z59dyLGzXi+QCs6L3Z2AFUjwGe~lr~i&xL?}%Ef&#vTW^BsHm9b|QMN}a$P^kAz z%hsSc^M6r{%M8gs7N{bNr5{tln~*D75~x4Y`%e@YDA8JkNLL~q8jxveLnB;tNyNz} zPhEM2xnP_bap6<)bLqAc<}AVNCCr(Mb@XLKeCxXQ`9~uP91#VbOZPPGY3k0%CU;0j zGp0B)rZ_XEwTAXY7F)X$E!#sM3Vk^IXkw`&v9v41nK-)D|9E;vtNBmarLEJuGYYp` zw^}owhZg-7#7JMu>FpW@7$)NcMfC9i#SXT|>cpA4MRJrD@jF|fLQ zMt8|5H)^c4*I(i+ncF@KIL1=?NyhN*N!1;p_Toy1#ah)pX&OIO9S0iEhF#hb`xB35 z&vItZ>W1UVGDpskozu^y`Q;2cYk_HK-U@(@%Dbj_jkS+oXwO_^Uwp+Uk*m&@`jw8g zM-+Vj?O?yiq;LFwCAt~gG7JN7OXawU!G4GR<0;HAQJ9~M@J;_T3iB)o-wFINgBlOKI9It2=vH4@Vvj-e9RS4Yo>BrnV z$N?b`DAU4XZKRTr?^9QO08`+>h9w4bZQOLzG&sDmkT;sL9wolbUKu0?yrsn!!rP1+ zpPNC8l5qoAg+Z9aCwv%L;6?^Fg|dsnz?Aw>GLhY;gr>*No$T(M8`U>_*0$(KQrx@? zzP|$=CbcpQzCPTMO<`j8JT+xBIsS!h?OIuv1>A@z>KPD#-ynrN<#?W9Q`?={g6*U! zyzN|Zy!hN7x46fbn9U=A9szBb*oF-9K%9hVn!xEOji|9hy*3cOFgovtc19Q6` zM!7A1vsAMmAQbND41j11B%1B@zZxa>PEryw!(sCVM-zARYO_l`NocCB2;ko3LzkiN z==IrwfL)uS8MpeedCdPRWz_1U5ZNH-Rb z--bI=NV}4mn*BGQ-eWtt_ZGG_R_%&e(TIyTC>1Qb*29vKN(_5!J-2TRIgKH0Gm`;Tb)#*}0wabxMTWzW61x9z$2X(&mc4MPVF zlFi^qxJ$w3QwO%5-uQHnZLg<|>|Mq8z#_$UaW*hcHN?v1YERzMi35c!l1h8&1(M)B zXl?7+eD}%Uz{kjDz5+DrB(ENxhW6}!iQKh3*WRb&bm#m$2s<@HMF%R|M(TK|REM!q90HT*2&Ij7Cuk zAU|O3I(hf*FJFIzdiVI=)BB#F^VJ?3HvQ9k9*|ZAp`PR?RXsdlms3M#fRh=5*2!&m z_uRRoXKQEA9k-vkwG-#5(|h*74kj?SaXZXng-imRpl^~kumYz@hB(s&Ur|gI1$7AA zj^-bv4+Imqu9UqZD#6o5&w-1l9QV_mj-8f-8krbs;KnuRB=EzcHYX~n=#^YF7{P9{ z@2&_Vkq3H()J_(O;fuP60K85>w%$JGUvkS`2U&fW3rqaIOhg3<91u~lK?#mHo}5m; zE{`S`Ig*Py=Q)#yZ<_vDOnS$vrABliZCz!x7mkMK&Ghm35|=9GZm_sG@i{{ufF5x1q zUikdFnulFjb1ulGk??R5~C=5%lziI$Fmohxy;KdY&^@$f8@l&Ia&H(cI32&H9JDQH>jdJ9T`a|9rxK*9dp46RS5jSqRvEx!_!l15M zpCn9pC-|eVcf;&+8th9}9-Y$YoMLV49#~>e9Pr1)v~z)e`D4Ee@rwn2vyA6<)1hW( z-kjE1-LbHtx}^s6XSZc+_rOuzSp&Lr2X!Sna!0`z%dq(Ke*SR@U=79QoQ>|)J6Z9eh%lRB79R#p)kD!;ag#46po|k-y3u;gg(=lJ8L*F_CsM4$n`Z*Bs``;M|-WKY!=2>T%|R9q9R#wS>0&-T~)%susx)9e;!;!4p%pOuWU3glSqBe;zSS3+IPRjfb|&JI$&*feW|{ zmyw628KrFd9Qo)3J!+ihNwx^jZBI^!C$46QSo#DUg=AzxUBD>i@-%PKXQyk+Qsw~{ zR?FK1-rwIqXWZ8dMKiRlNIEt93MGgIGqllGzMT0L_cFMWF>Yk|9_A#S@8}xi*9k53 z$^8ixMz=WnLQg^ZIV$Y}BhS23-A_%=qJU4Vo-2-CW{i1zzLpa0px_w_HVJ*EHd4x( zY$XZFvVh8DVEk94midn}HC_B|jTUMea3rYQ)?B0IlT7T150h+Nv$Qj6>H=zGF7=gc z8%nB6f1OIs_ZKf-tYw6e?L%XC*B=rmF4jus?xc7ru1`;&QW-Ppi5t%yqHXopnWHYG zdTA@v4~iAF+927NN5vzx+VH@usW0;tcHt#TKQ~RYji}QC0-_e8N>?z6QyUhy#jH!U z3W&wubE!5okoz;hU;O@3%@WE0bNA@GOSK7Z=P~2vYvm9&YniX*E%%C`unY!W%S%Bh!L-ivOSfHy$0x#C2KDc0--7Y+cIX@f8Mnv!|=6K;7@ z5B1Tn*&fmNae|eWAmOkL#W~{Uv;;0mOI$u+HJD zR0j2ISE!UT;OTl!bT8H{c?YQB!R#7_887VVg+&K!RQx}Hry$_O2K}YrSxJDr2PY@9 z;_ek%Ou#~rl;0=YRxQz{U`Dh0yzPUfT9#Vt?E<%+T-eB)R?_d0;#A&F)Ik^m`aYQ8 z-dG-BY6w)32__=~ysiaY$)fBsEm>T(LMsf&Pr>Z05Y0=p!M5%dWVTA`BM7xod##9P z67$8l9VkzlaWn)@e5GY~iN7^!L*)%5LC=*&Qmi3ve?ym~i0M~o*PwLB-X_^Ty+Z3z zR?xhLF)=EYXE#s!d|FH-ScwjKNnl=^H0|8hdUgn&>M0v4zY>l>qO_M)(xdKZWP1feiktF%J%CaRCN4_BgSzD7$-bDx+z5i%>cYnW)eO55$0W}SSsRy=(tG0M`HPMlwcI>Cm?laH1$79n(+Wo602<;_8r-6Ih#4+U->8kS)*=DS+JNujqsE}m zpy@V~f>G4fpfwGvmO-PeJe*}wIdur!hOfA{Ap}zHJk{e0VxXZs z-7=I42%`fQYfF$$gLF9=y0j5;QV^--OR!){0s92{wJv43KHQ^bY zY^&xUP`!-ifeF`REi7>aIqAkVkhWso6Qrh!k|*RWu>NvffEmc;}LLwnWm&lKZ`~ zv1MJeeyyY>c%D^JraBR_QOhX#*}21&T|qUjMgX4^Eh`|9b;(A}Ftz#wZ5%@&*ns(x zPcp2rwvRSybwSbdDGwnzT)}c7F=A+|RuoUy1?%xYq?0yvStM`&vH)>)t2Q*Wm)Pe1 zR&8n|j~;1oT&IRw7{d6CM-++YC*UxH;C8B&hr9*i_psZ5TJ@H{z+Snq|;? z)MA#8yBwv1e(@qMo{kKA1~Ci94Hy61s%5V@PPvGn1`+oWJ=-a8P{6Z_Iw~RbXZptZ z8IN)Ivgn)dLft^UqjFp!Eb_LdVcn9IdV(r$en?Bp;8nxTqQ0#{(Er8ozCe}Wx#g3d!RWRnJlAycigFCSs@)GmZAxq|)|IOmyqgwhJ z>T4ZD1b0%?y=#p7$*9_krKxvrVZvWV#GBEkegp|^yf20Ows3cdm?w3yJS`$0FzvTM_48X%&VOHwlcxBB#C1<< zSE#{?IQgWuAb=LSK3PnAO8d96OjLJjgOtm})t%anO0+oNscp0FpeYtEFXgxkgri)E zlJP~?V(dr}a-asi$`hrEO6{{2LHEn;p^qr*E`z2Z(w8}5dL1pQ?BEA zCq1oASDmT0<@oi0)3#eH3*?ao3ix`rwpbY^F5aUJGS8#pWP`=sd$i0#Ps1>E7;Dy2 zH4nLK^%2t)-FvhpswvGjwo7~6q+BX~+^4NB^k8(!Kn#a_9@%&BO&gn7Hq?D~ZF^B0 zk8{Y87jd%c=NzIBB$!@s?4T2eD-vJ?HMM>nvHf^k;xkB;&n%`hh$~5?9M+PeyeQ4M z9Tm5|gu0&=zkNxYH^_Sekv4bxB{G<_;AON@)Y!Cv<`>aAok!!GxXq>+3ZV~4Wxe0q zU??jAoZOZuc8h2Gk4^i@eE^Mn1s18y^ReRf=c$mrV*RVyik$t_T|+zJb^3k~K_6-S z2M=nKRXt5iJ*Z9h**wFfn@&tG=CZyhb|2J|t)z+9YxCq|*-KRP7#h!)>FESLeL(>u z_jhQtZHV}FXV}-M2!_N;D&ie_ z8GiM3?Iop(piv{uzLVmW6eJGtY2sD%m0G9&nX03`#my+-@mVSMy`dEr67*al5+;OF zc^B-@$I(k7=&)9#Zb-8YJFGQn*3YR_+QFqJ2R9;uDhblhQWRIs5aaq8V&^D=0Z6Tb z{3fp&0t|FY*?TI+1luGJ>4((z>nUKM(FkFaCAlLKh8EdxYpL=uL}TL`CO-TTt_q%f z2{b`_n(daiwMR`>$X*!DTc`qU;SGO560 zxK^q_TXxM__LQm<{$l)i>e**mx`AJM9^a+!q=e?h%}wjcoJV6RN_mVv1SIaIB@Q~kuK0VJ?JEbCg;Mg8Q`@gt|3Izc;XF!Dv_R+-x%G5uui}$yfE3vf zt4bitQ}GEFEPTe#`$TgQtjV&;%s*&pnrxnz;lM59hVqf0o5yOVUS2>Sc7zAM)y$bN zI4RP7n6ZXN#S$6cPv{2V!`pACm!s%b1!b#dUn5Dr_(JY zz*B^pEdbuF%trFz5F*p5g_jT{)dji51JmAl3^_uX)y z`1qeV7>8uoPX1GSGk~pOq>Fd{3tp;mz&P<=bbq)g`bk^U>tNBAUJXmWKWP({^ACiW z##Ts(QzOmP|4;-gK6s!0j*ati_R0GzI}dq}(gtJA00ZCm!0o2tV;`VF3#Xllhioz> z5cG|$@i0GSE4@BJ!NUj|-vVvl$OfNZc;H{A8T0PL$Mg8+ckeTO1W?#UPnIhw`fzMz*B{F9o54qhT**ht>9^oetOpHF3o$uV{Seo3n*bB z1!TQ|siQML(9?tTjR&NeUTFjxzf$qkP7yqm%MsDI-;CFX#0?>)QP~3!#bP-pAx$Hm zkg}U#{PD3$vZ=%$LrkeNyU$l29~_K{GMP2|EH$imPKxnd*Q*@lcFuPT}{+0kHpo zAKp6g{4aX`Cw=b?spMEw%8dUXA@v{H{tMMT8MfzQ(IPW%ITDwMwdJsk@sD^@kb3bp zYR(V9B6W=46L0TyUtlNORHf)U1^M}Kqjky|n8*)Xp@1ylM4BPDp!Q)mNt-n6vh?0I$yVFkUwhm8{bv?-QPbSZkKcdi zoHNgFX1lQXJq=$Q3(r{qF~94b5s3)Irp6;D^oYzl%(X<> z-t860$E8t+IBRNrGK~$TJzezc;#swn4vd|NLLWtWlrD14cH2+f*pEs7JxRePF(iPb(NK9OEF{)1mCDM3CI;&FJ`3LE2 zK26sKPGTd&eumFtbL1azg&_8UT$xO~dEF#7Z15w#Yae2{>JrwrOk#1~nTUP@|HCA< zP&vsnGMLFCggRm|AZRY+wwq97_mnFOj~I6z66#p}3yLAGUnZq%r@%vKhZZHoWimcetQ~w ztR`=Jh~Ip=W|__|`kMWCR3;lT3g<#_DILy*c#k7ij6c#O4loDK@}VOu$^zY3(&moR zt?;2kDF%LdC5wpgF$bs9VQRC_0^dTc{=^^M|CV;uYha{Fw=9sTiF4mLsZXnK=knMBv>!G|G#%E`-QASbcf=uK2 zwk$S=p>F7SGJDF1^P60_e+1V`2&tIeMcfXSIMl?t%?JF%MF|K|F z%hk_~<@qz%uplscvZvuKwIrzl)Xrc}dIe}Fxnl-30U@T(BtK#w)!LtSZ1UIT_nhb! zBEdL(6|E$PTvqS-%dvr@<>K{XKo&r>k%#OBeFya}nuaK@=t_u1TSOnM!Atm=nJhS3 zGz=rXUbM*6^Cwr~C#6jEPcvD#cSdZl2??)))et(ck$p6*s72bI7aW>ei&`tfT$Vq=tT=1qxbtCth zPAqQ7uhjP-!IcR3jbViMS==&kE&T|G%JNE-&Kv82$wlupbn7eR9#xQRn@PEk{F$Y&Nn)QhJDO;P*@>P7V1 zmA!owON5?!Fln-fo>C|iqKDS#H%IGVjBSjW!$S253n`Smyy}82AiJGVZ6ArmX=vQn)aKW_gDBvWVp%L11wnjng@K+UgDRO7Jv( zYyo@QTgnFb)6cPq-hsr0G&`?;j%9dHL^Qkk&!1yIQnqsbJTr|c$1AyfrWi{w-!?g} zUF>XbpmSecJ=P0ME}OLQ9LoRrJR3DUiQ+x4m(n1iA_pe_uy)*Jp1F{vXNV!Rmy^3r zZ+*)K#d@T+!NA{L$i_?lVk?`Ey`_JO*Z))>+*thrdj`X& z5T}0qBD=$e6;Qb}F*z}=B4dwBX%CCm-g4En5lK{`X_j;6BDOHfEme69pEdzp6kL+a z#uL?!}LLX}2a~HFD zu{#j*8;FJ+Rq?jPENN=rF|bE*;@=CtP+Dy-;;-hz^T?FcFfaZ@9*w)ni1@rqhs40E z^4I{wpFqeo%G@n|NaM{s7VAY48fs|`O41{r(fTem*BkocB2{+xMV_iE6 z*niM`Wp8Gr0itl%kyBp&JAGu$?yo+*X3!`d^p7$$cBfV&F=V`R?Og8)VJ#I)ks@*Y|u~hYQ#8vNbH?u_f&> z`R&VA|Hgk!hyNOJgn2C;VUDLPY{?v-HAeTn^bo@D_Y*kAZ~8E=?>}am9_RIgICc6k z{uS5H=C{|fq4AYO0y%tq)9A6&gV*OTBwoKcj!)DW?*GB20^h8$+#xmJ8qGeg6tfBX z@;E-JSOmu9L5-GT)}~KbNiEwNKvrQjaW2Qj*HITM(W_TtD!qc|t!EXf4|K7UsKJMf z>519&XNWY4XTC|pc#GK2c60iIh@iRvH(+#s(@z4H?&?2zA`DiKQ$Og*cD+#WGDS;(}1HPjzMYw(=8sM{P%A#U+;Ty)OhOo>epFfW5iZgNfc?Mw&PF| z9!ZQ-h*A(kG>uujemhHumMvy2iYUmj3NBYG`15v_Nv$BYf+bjbi$gLIF)J17Kw=B@ zJ+vtI+{9(DT8yxLtJoTdo1JpV9DXtOkZBSjA{hgI=mT21{^WAu0_E2_(sgp2e^kMS zdXF4JKF7%tr&coP8#iX|_{I{Sez3%Icd~>ilvo;{6}!NN9tE!6DGK}{c`JOV!0xh& zT)PWw@DL(AP~d)|z>7NHVQt~||l?A=%lGcctt=>SQ$QOq+?yV*fk2EB%a;1gz z4L{$-n!cfG&-4~l@&kT*4;wBEDmBDVLq#41)$b*7dnli?mrWRhf}Vd!y$0ON8US(C z>Qz00DACnPW;UER?qvzj3eNa{B153iHCDmKJ=rmkeSTWtN_QahonP+_axX1i5S7)woaX zrzJ)x|H*z9H`-m-Ub1n!OXV^c{a|b6F$dT{IUT&jryXD^`lca`YYs42T$c=dKWF_E zsn{vlh9G|(zjBZ|(>Y{R3`ZZ3D_b7qJiLW0JJ_$Bpd+zlJIH>$!d`Ys^jDhp{TKn< z^hO7ye&`;A`ZhaFke6gQu68DySp&sk9f$tJpF70T^q&vmm510d483|9J+lt85HDjP z#VjvsQYZbo4cO7LTy~W@%i+}GZ8(|bgbY6dvFfde*ZVm&G{#9F;g@a-;b0EUXXVW(e*q0#f>yv zOgY93230n-a7NB^GtzD#+hYQVBwN^eGnaVV*6R5~bn z^Swp!jZNp+i!Q~BX*WsnV&r*t+e=RNg${5C|M~(;=}`|)zQ|IQVE)~U4{QbUtruCY zRA7D@nHLiWkL@;;MO3Y*7$LS7<;%i{bO3G?@KD8vI{Q8f>cWTC1!l1e zMRdRgURW^TAEfn-23|6ZKrXH2@pC(GYGeca$fg$>$b1qio^NC?q@%rSzeo7KdxBPF zL$=(?*3-R)Ma3l~Kgli5D=4sf7tq1?qIEp2iRI366Hf>`SxE_%1$uH;I&6@aTPW1= z=2IG06`#gFxNZ_vCxjlG_$N(lu;t%Z-X!F(}Y3u6AcJ`!q7?|6O{9rps*6tW=x(+j1;|zEZEuh)x*M*ww+2SUrJjRwl{a0$oGQ72j;FPgHGa}R!Qk0`1B?`wepl-urWq)jA9eLEe_|oHAm?<5@`_ZNdnA8M z8{buY?#Jxaur6?Bu{d$S)eZO~|KwwuxDBI3p_rhPl_YG*_N`~+)qqm@0K&X@+9y=+ zu9jW;35!p_4d=orNV2K3wb6e3hDly6TmHr*RBHK;KVeB0krQ{_cpgY{&u~QD zY(lk&3KDhmfcQ4-|57c1$Z!xK7|`EG$F<+F#Hha2qxs^atP=k2?`Tq%jHEAtsKShE zP3Fc>Lo4cFnM+1V$B3Wrt%Ku#V5?aFHSq3NT)282ZAL7~-$-X126wR`s~$mmX|iVj zHd{@Z{oKvYo7HX`En&zCI;Iv5*8o5$Kz2X$JeQKE*ew=nB4W$fRUMbQSgZxr;UYT4 zl3FM{1&(kWdsLn!w$B#l+x&nsZl}>@*+{G#6bqO#EHb^WGZce{s-p#fYKgBVQgh zi|wWxuBq3=Zlk-t_WUP1WSF!T-pSZZVIdit>9xcnh%fRcc%5iHqEAzDE~Vfqp7$3P zuaEWNm49I`r9Y~MA$_-r8h)T|MYTqt)B}8Y%so~-NifY243;5a2>+kj{^>pHQGx;Y z7eSFja7itc9!aVyqIP$-f5Qkdipo=2qAuN4+gq!NQIGC%eBFI!3Qs_Z1l8OG^Y!9y z-)CbCarhSK!#nS@#AMOepl4_eTwFp{@^d!O_R;dyAiv<3p7mSv#XZ-4!DUzxPu4FP z+PM3#Y#Zz0exv@*(!2~B)gx(8@@4;Iap4bC#vj4yqV)XOKUtEuR1D-FlD{ZHYd7h| zi0$e_#(%NL<=nZJFZ>tFUL?GUh(*76XbCeJ#1lNrO|6Hvejg>@6c;o``S5@Ki>1ji z`wHETGF%Bc@PuOIrGI7K2D*l3-SUryzMHEiqxnKgs}$0GOM(dFodXfb$82;x`@5dSh3)h`!1WyH z`X1zZ4(4k!V36`JUU3mHIt&ABq2ynL7p8~9YEIl7V)Vux>@o}dd)K@oEYj?xt@^mF zx>lNUk`oMJ<59lWO3)s};Zd+S&k(jt&5p|_Lrv?TF*VPEQI@hPyCWhg7SleB_QGw35N_sFJDnyAi|5UMJ-%$&d0C zWTbrg(lswc?L0+@LM_8-INPr?D`x#H9T~|bW8P-f>`fbwQke)YKQrkPiFdR;Nt={- z7i12mtx32-C`2e+qr8jsbUy=iRPz9OA8C#+$Z&I_IZ3!in3K&T;Tq*r?a~n8&7g7cufP zPP`c}aDu>z0@DRfa`{wWq>%@IF=Tc(BZ>?HhtMSo@^2im4g8VKUxlwB!RG8Zoq6&C z?;cI2B7<2WuBk8bMW`oeuqg6dxWoJySemfT>$7m?+gevla)!r6H4%|Socd&|O?`n(TV;7?(v;@yIN3ykJza1raw zB)5n$Ku5P#7|mJ2M}T>Hig&y&afX=cb?rL_nT6n-E@p!n&0I0}x1By`+fymeI_#vy z07*(P)wF0c4Ad>@q(I?nO)e6eF2WHu~Thp>3Dtub{}c9ooeVl zyp8@TaA(tY@@zX@?Wo!<6KlNb4suNfIkgM9KYgC2;}oR(L<^kEt7?#FtlA}FF$8HyTyLBQiSO`+v-l{iU`tG_KvN*l7^?wmU;@(ResJ< zT1qA(yXvY$5JzRbJIHkr#CG{IZDraUu66HkLl8^dRZ64l)Sj+mS7p}9_qxMemN~n6 z(|-M|EH{OrHM^r>kFESNwYNmCk}RUP%T#pR9+3F#7RRAx@<#zWTdKMaZ?o0c(xc*rbtf!A;KgQP?ow{w{;&O zD)kVszP9`5AzRxUoo)3FOUpYRxuMM1oAz|xg!xgKl*~V+W50NYS{6+=WM(kidpI-5 zMt!ZVz5+`kqSC%rL_UW65|5yNd*ccGlTp)ym%3_?k$2a0PbSlOW3R2e6>O~R5&|L- zq7fPu1^1{Z-d-cl6QOorBFC3R7rT`XH#jSE|YSp&5@}xs2JyK`CaD}=7)t|lfg#FA7 z&t$sG+HB?5yZ2Q&c8JF0CKNK;Mh(WJu8&%3?hOT?qzGJ`RU6|jcKKyxUS#N9ZY&uM4Rq2hrs+R7(a9l%|_UJ}bJ__A+ z@s^|hBznP~o#eN(>6UHBTeg;Mw(_RWOD$z&#@BZKu;WBs*P)vw&}Q;XnIwQjMmsOJ zH<9NusWV7tC>&?Yz=Uhcke#x_^ag!<+f`fr;XWN%=D>FGY-jUvYP;0pMQ&@TH&Rv| zTPrQ}jKV5Km z8f5D_cgeP^ikgp$TKk%92e#TSmv`RWO;?v(vYmY65t(*eYVB-3D_YYVl#=6StNrLL zTis>48Ws5{zuHxC8ODDfL2!)M^DFqt0D93mdy^gZrIJe~ydG2bx*4rt`w|!?NS=+TWT<+R^ksgVQbT(b2`lg!b zX(bj~pScp=T}_3htEOn`LY}G1Q8B1z+DlQ%L_$Z+R^p3vCu~nxchK^y_N#R?S+KP< zlHcw_Z+G6fCTT;Dl6T1d+c(c`JHN2pIBeT?#kPYi1EcZ4`{{GtCy&z3vg2AaJyH#W z04*f{vf$g2G*FD3EZrwYEP;)_2yQ{lu%V>s1-9rRL zo*g$Y5L?T0O}i=g(zqxFguj4|4$;jw(Hdoej%M4Wg1tJ}ti!x^y>7p1(#@gG1DG;j zrcCf!s*tP;Gu64`=16+5(QHwyrLk8|drwk6h#N_*lX=4jN>rfdN}txOJhn{;np8bEio6|bX%PU7A+oOD{g&gugll$@fHRn~yK5>2BWMsWs;z6A8{ zydvhjLYeR2-nTvJSY37mg{?D)Oyzd1Ok2oM_wW zv^-D+PlNbBq}EWX``jn&OCMc$OkHrp3myJb?}iPk>)%k&;XlpO6?J@ZLwbk*V}0M{ zHY9fVPyVu>p}${+_m|;_B&E0pjS;a6e2H$%}4i0+7*+U$KNYU-VcsTKZr zLn8TVtrrHg(-MpyXUjf|=Y_%w1*MZia4(G@fA`bQmq;?Qc6TvW`x8yNAcz{I<; zL-z#yHYn^XUtRb>(rX+)v5aIF1vpd2@RQTPsM466_JUp__&@nvY zPSoUzz`L#&#*X1r?~s{m$UwqL#F1Dxj0{8H4Ntfmom{uDBYJ#$)P#n$?debbXnaTd zv+Wbj?E~iDHKh^ZO_Rx&TT50591=q{6B=+7fR^2~l>rz)lPjGz1w(+a6JpCBCu$+a2^F*1+)Tw04M>h11v+fI^g;_pc1eH z@HJo`zWx&~VG&;s_En0MjBss%D+aE?fDwQMKs+GZ0_-(ati1u>TLI;OZGa8%vK_A7 zaGe2M!(#>DQ#}3&P>Dw!BiI3#5iUPK6&}aHH5O2Y$0y*L#js_3AJ01h3-R)Kz;l45 zfR_M^0BZni0UH1t0U7{9gZ2vGRlqvHYk;$e^gX!V2mAog0XT;bU%+MA59a{@2UG+8 zg)h6{f;?Q?4cEtjPXNCI{2q{juY2J72(S}%PyzT9kDmdAR?=0t_5$7n;4WP4CkXqo z9-VL>o^ffV=EO^eVeC2{4*}i+90R1_!&HFSBF~0vB;Yu{o(4RJ$1`vh!LSuOZt1;BES)(SWZKY9ZJ%y?+Ecnrg1G9Lc{cn?hvx)Y?XQjoV! z3&-bF08Y2cEtwMtcM^ajTiOfwumn&Du+-t@dBEF%D!kkY*avt=d=M_c1;9l>J>U|c z0dN8Spn0Y><5B1u-hpc+a(EA}dVIy6vep2&47j2Y=h5DUlSalOgF3=8f&vuN3*9t1 zVg?`r09a@77OJ4Hx9PrOdew(6r;8ueG^>5VqQB{q)KSjwC{gNi#W~5KraD9WsUw{x zrY1Q9{8W=ODuBS`NACvs)7OE1>TqY2x9WGl|5!ER{(1_SM1xO%HOgrj8>9|!4h>Qx z?+p!8M>tcNI@#$TrG`5v^;f^EcP0g^If`?5q#EfQKTKVyIA2yw>SBdPxXEga^K~Ua zo$8EJ)lg?pfI8k8=cA5xhKH;3^!KOC(bK0DY_K}UIYY@&@Z&XDeHB56t1lrUgKBgd z!c!655?<+}PK=nLr&n{?6gAM9rT3>;V+8QKK5Ft{P0=HqPna5ZFU4EktT=<1ny)zh z{nSm0GoE|ICQL!4yjt6>=bm6XpyJrs50*3=bu`qyWZdz>j{3LO=7pD0@cd zQ(hUWMR9IYMo_|wm6sLuS;d(}c7@eJPC6<|#C%G54IZ-@FZERyszDTEGXKO^-4geN zqGmW>Ms$JB*IpYCI0{D7Ab#LrEgkP>tSIvrZy%K|;Y05M``I^U4=u;sN zF{)oay-v_(z%)fIRGc&PBh;k|Rrhdph2osAc$2@NUI?FsD8HkB20u&nafDAO5vrea zwA%}(eYSNbXAu?9=$xh>ts4KfTzN%N>1+#74BJ`&4mmYC{nbddsogN->yu%s_yM(I?^GTaxO$jNr$Wyi&&6IyOtZ_&%HAekn1K$#&4(p#s{maVe zZj@p%#=rWQAHUpR&ES6xQ5X0ZQV5q#`U?JhsCs2YnnuHg%i6q9P83RQKLzg-Ir|J> zKLo7hW5d)qi`8gelb=_RTbP}ztw%qaZf zQ}klwU0Vpq11tlG+${xvSWNz`{Un8YN}G#!0hS(0xM+|A?<*;^)mtb67E3$Oe?~$F z0DlEwIVe@?#BAgpJc=pFR&rTc{zfu7EGh>TAl6N?-jng!XEL2rEGf*NqHXrk8Fb)B z#q0T#;cBwqC%U9BnL%M+2JubdYT_`q;$^+!x97=wI^l4>w3!W8rhNIFqGSFQuio*B z{D=j7$tE`bEL?3-lu!Bg0qT?|#!qK6GIZ}{BtDtq^?t&*$J6vbj0p!$1b)yz2AHbQ e*ALS4kB?J77&DdJKN_n7(?>j+==I}7xc?W=mQe8k delta 72588 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 EKZ&z1w*UYD literal 0 HcmV?d00001 diff --git a/Server/crop_data_debug.json b/Server/crop_data_debug.json new file mode 100644 index 0000000..966973b --- /dev/null +++ b/Server/crop_data_debug.json @@ -0,0 +1,1071 @@ +{ + "type": "crop_data_response", + "success": true, + "crop_data": { + "测试作物": { + "作物名称": "0号作物", + "成熟物名称": "0号作物", + "花费": 1, + "生长时间": 1, + "收益": 10000, + "品质": "普通", + "描述": "仅供测试使用的特殊作物", + "耐候性": 10, + "等级": 1, + "经验": 999, + "能否购买": false + }, + "野草1": { + "作物名称": "野草1", + "成熟物名称": null, + "花费": 1, + "生长时间": 1, + "收益": 0, + "品质": "普通", + "描述": "野草", + "耐候性": 99, + "等级": 1, + "经验": 0, + "能否购买": false, + "是否杂草": true + }, + "野草2": { + "作物名称": "野草2", + "成熟物名称": null, + "花费": 0, + "生长时间": 1, + "收益": 0, + "品质": "普通", + "描述": "野草", + "耐候性": 99, + "等级": 1, + "经验": 0, + "能否购买": false, + "是否杂草": true + }, + "杂草1": { + "作物名称": "杂草1", + "成熟物名称": null, + "花费": 0, + "生长时间": 1, + "收益": 0, + "品质": "普通", + "描述": "杂草", + "耐候性": 99, + "等级": 1, + "经验": 0, + "能否购买": false, + "是否杂草": true + }, + "杂草2": { + "作物名称": "杂草2", + "成熟物名称": null, + "花费": 0, + "生长时间": 1, + "收益": 0, + "品质": "普通", + "描述": "野草", + "耐候性": 99, + "等级": 1, + "经验": 0, + "能否购买": false, + "是否杂草": true + }, + "小麦": { + "作物名称": "小麦", + "成熟物名称": "小麦", + "花费": 50, + "生长时间": 300, + "收益": 75, + "品质": "普通", + "描述": "最基础的粮食作物,生长快速,适合新手练习", + "耐候性": 8, + "等级": 1, + "经验": 8, + "能否购买": true + }, + "胡萝卜": { + "作物名称": "胡萝卜", + "成熟物名称": "胡萝卜", + "花费": 40, + "生长时间": 240, + "收益": 65, + "品质": "普通", + "描述": "营养丰富的根茎类蔬菜,容易种植", + "耐候性": 9, + "等级": 1, + "经验": 7, + "能否购买": true, + "喂养效果": { + "移动速度": 5, + "闪避率": 2 + } + }, + "土豆": { + "作物名称": "土豆", + "成熟物名称": "马铃薯", + "花费": 60, + "生长时间": 480, + "收益": 95, + "品质": "普通", + "描述": "耐寒的块茎作物,产量稳定", + "耐候性": 12, + "等级": 1, + "经验": 10, + "能否购买": true + }, + "稻谷": { + "作物名称": "稻谷", + "成熟物名称": "稻谷", + "花费": 70, + "生长时间": 600, + "收益": 110, + "品质": "普通", + "描述": "重要的主粮作物,需要充足水分", + "耐候性": 7, + "等级": 1, + "经验": 12, + "能否购买": true + }, + "大蒜": { + "作物名称": "大蒜", + "成熟物名称": "大蒜", + "花费": 50, + "生长时间": 550, + "收益": 100, + "品质": "普通", + "描述": "常用调味作物,具有抗菌功效,种植门槛低", + "耐候性": 10, + "等级": 1, + "经验": 8, + "能否购买": true + }, + "生菜": { + "作物名称": "生菜", + "成熟物名称": "生菜", + "花费": 45, + "生长时间": 650, + "收益": 120, + "品质": "普通", + "描述": "速生叶菜,适合沙拉生食,需保持土壤湿润", + "耐候性": 17, + "等级": 1, + "经验": 9, + "能否购买": true + }, + "石榴": { + "作物名称": "石榴", + "成熟物名称": "石榴", + "花费": 45, + "生长时间": 650, + "收益": 120, + "品质": "普通", + "描述": "速生叶菜,适合沙拉生食,需保持土壤湿润", + "耐候性": 17, + "等级": 1, + "经验": 9, + "能否购买": true + }, + "辣椒": { + "作物名称": "辣椒", + "成熟物名称": "辣椒", + "花费": 45, + "生长时间": 650, + "收益": 120, + "品质": "普通", + "描述": "速生叶菜,适合沙拉生食,需保持土壤湿润", + "耐候性": 17, + "等级": 1, + "经验": 9, + "能否购买": true + }, + "番茄": { + "作物名称": "番茄", + "成熟物名称": "番茄", + "花费": 90, + "生长时间": 720, + "收益": 140, + "品质": "普通", + "描述": "多汁的果实,市场需求大", + "耐候性": 8, + "等级": 2, + "经验": 16, + "能否购买": true + }, + "洋葱": { + "作物名称": "洋葱", + "成熟物名称": "洋葱", + "花费": 75, + "生长时间": 840, + "收益": 125, + "品质": "普通", + "描述": "调味蔬菜,储存时间长", + "耐候性": 11, + "等级": 2, + "经验": 14, + "能否购买": true + }, + "大豆": { + "作物名称": "大豆", + "成熟物名称": "大豆", + "花费": 85, + "生长时间": 1080, + "收益": 145, + "品质": "普通", + "描述": "富含蛋白质的豆类作物", + "耐候性": 9, + "等级": 2, + "经验": 18, + "能否购买": true + }, + "豌豆": { + "作物名称": "豌豆", + "成熟物名称": "豌豆", + "花费": 65, + "生长时间": 720, + "收益": 110, + "品质": "普通", + "描述": "嫩绿的豆荚蔬菜", + "耐候性": 10, + "等级": 2, + "经验": 15, + "能否购买": true + }, + "黄瓜": { + "作物名称": "黄瓜", + "成熟物名称": "黄瓜", + "花费": 85, + "生长时间": 1200, + "收益": 150, + "品质": "普通", + "描述": "清脆爽口的瓜类", + "耐候性": 8, + "等级": 2, + "经验": 18, + "能否购买": true + }, + "大白菜": { + "作物名称": "大白菜", + "成熟物名称": "大白菜", + "花费": 70, + "生长时间": 900, + "收益": 120, + "品质": "普通", + "描述": "北方冬季的主要蔬菜", + "耐候性": 15, + "等级": 2, + "经验": 16, + "能否购买": true + }, + "葡萄": { + "作物名称": "葡萄", + "成熟物名称": "葡萄", + "花费": 200, + "生长时间": 2700, + "收益": 340, + "品质": "普通", + "描述": "用于酿酒的珍贵果实", + "耐候性": 9, + "等级": 4, + "经验": 35, + "能否购买": true + }, + "南瓜": { + "作物名称": "南瓜", + "成熟物名称": "南瓜", + "花费": 180, + "生长时间": 3600, + "收益": 320, + "品质": "普通", + "描述": "大型瓜果,节庆装饰的首选", + "耐候性": 12, + "等级": 4, + "经验": 38, + "能否购买": true + }, + "茄子": { + "作物名称": "茄子", + "成熟物名称": "茄子", + "花费": 190, + "生长时间": 2400, + "收益": 310, + "品质": "普通", + "描述": "紫色的营养蔬菜", + "耐候性": 10, + "等级": 4, + "经验": 32, + "能否购买": true + }, + "玉米": { + "作物名称": "玉米", + "成熟物名称": "玉米", + "花费": 80, + "生长时间": 900, + "收益": 130, + "品质": "优良", + "描述": "高产的谷物作物,营养价值高", + "耐候性": 10, + "等级": 2, + "经验": 15, + "能否购买": true + }, + "葫芦": { + "作物名称": "葫芦", + "成熟物名称": "葫芦", + "花费": 85, + "生长时间": 1080, + "收益": 145, + "品质": "优良", + "描述": "富含蛋白质的豆类作物", + "耐候性": 9, + "等级": 2, + "经验": 18, + "能否购买": true + }, + "柠檬": { + "作物名称": "柠檬树", + "成熟物名称": "柠檬", + "花费": 85, + "生长时间": 1080, + "收益": 145, + "品质": "优良", + "描述": "富含蛋白质的豆类作物", + "耐候性": 9, + "等级": 2, + "经验": 18, + "能否购买": true + }, + "枇杷": { + "作物名称": "枇杷树", + "成熟物名称": "枇杷", + "花费": 85, + "生长时间": 1080, + "收益": 145, + "品质": "优良", + "描述": "富含蛋白质的豆类作物", + "耐候性": 9, + "等级": 2, + "经验": 18, + "能否购买": true + }, + "山楂": { + "作物名称": "山楂树", + "成熟物名称": "山楂", + "花费": 85, + "生长时间": 1080, + "收益": 145, + "品质": "优良", + "描述": "富含蛋白质的豆类作物", + "耐候性": 9, + "等级": 2, + "经验": 18, + "能否购买": true + }, + "仙人掌": { + "作物名称": "仙人掌", + "成熟物名称": "仙人掌", + "花费": 85, + "生长时间": 1080, + "收益": 145, + "品质": "优良", + "描述": "富含蛋白质的豆类作物", + "耐候性": 9, + "等级": 2, + "经验": 18, + "能否购买": true + }, + "椰子": { + "作物名称": "椰子树", + "成熟物名称": "椰子", + "花费": 85, + "生长时间": 1080, + "收益": 145, + "品质": "优良", + "描述": "富含蛋白质的豆类作物", + "耐候性": 9, + "等级": 2, + "经验": 18, + "能否购买": true + }, + "西瓜": { + "作物名称": "西瓜", + "成熟物名称": "西瓜", + "花费": 250, + "生长时间": 4800, + "收益": 450, + "品质": "优良", + "描述": "夏日消暑的大型水果", + "耐候性": 6, + "等级": 5, + "经验": 50, + "能否购买": true + }, + "甘蔗": { + "作物名称": "甘蔗", + "成熟物名称": "甘蔗", + "花费": 280, + "生长时间": 5400, + "收益": 500, + "品质": "优良", + "描述": "制糖的重要原料", + "耐候性": 5, + "等级": 5, + "经验": 55, + "能否购买": true + }, + "甜菜": { + "作物名称": "甜菜", + "成熟物名称": "甜菜", + "花费": 240, + "生长时间": 4200, + "收益": 420, + "品质": "优良", + "描述": "制糖的另一种选择", + "耐候性": 11, + "等级": 5, + "经验": 45, + "能否购买": true + }, + "郁金香": { + "作物名称": "郁金香", + "成熟物名称": "郁金香", + "花费": 280, + "生长时间": 5100, + "收益": 510, + "品质": "优良", + "描述": "美丽的观赏植物", + "耐候性": 10, + "等级": 5, + "经验": 50, + "能否购买": true + }, + "花椰菜": { + "作物名称": "花椰菜", + "成熟物名称": "花椰菜", + "花费": 110, + "生长时间": 1320, + "收益": 185, + "品质": "优良", + "描述": "营养丰富的十字花科蔬菜", + "耐候性": 8, + "等级": 3, + "经验": 20, + "能否购买": true + }, + "柿子": { + "作物名称": "柿子树", + "成熟物名称": "柿子", + "花费": 140, + "生长时间": 1800, + "收益": 230, + "品质": "优良", + "描述": "秋季成熟的甜美果实", + "耐候性": 10, + "等级": 3, + "经验": 25, + "能否购买": true + }, + "梨子": { + "作物名称": "梨树", + "成熟物名称": "梨子", + "花费": 210, + "生长时间": 2820, + "收益": 350, + "品质": "优良", + "描述": "春季开花的落叶果树", + "耐候性": 10, + "等级": 3, + "经验": 25, + "能否购买": true + }, + "桃子": { + "作物名称": "桃树", + "成熟物名称": "桃子", + "花费": 220, + "生长时间": 2940, + "收益": 360, + "品质": "优良", + "描述": "粉色花海中的果实树种", + "耐候性": 9, + "等级": 3, + "经验": 25, + "能否购买": true, + "作物ID": "作物35", + "喂养效果": { + "经验": 10, + "生命值": 3, + "攻击力": 2, + "移动速度": 1, + "亲密度": 5 + } + }, + "荔枝": { + "作物名称": "荔枝树", + "成熟物名称": "荔枝", + "花费": 230, + "生长时间": 3060, + "收益": 380, + "品质": "优良", + "描述": "亚热带珍稀水果,果肉如凝脂", + "耐候性": 8, + "等级": 3, + "经验": 26, + "能否购买": true + }, + "树莓": { + "作物名称": "树莓", + "成熟物名称": "树莓", + "花费": 140, + "生长时间": 2700, + "收益": 240, + "品质": "优良", + "描述": "小巧精致的浆果", + "耐候性": 9, + "等级": 3, + "经验": 30, + "能否购买": true + }, + "鱼腥草": { + "作物名称": "鱼腥草", + "成熟物名称": "鱼腥草", + "花费": 300, + "生长时间": 7200, + "收益": 600, + "品质": "优良", + "描述": "具有特殊药用价值的野菜", + "耐候性": 18, + "等级": 6, + "经验": 90, + "能否购买": true + }, + "芦笋": { + "作物名称": "芦笋", + "成熟物名称": "芦笋", + "花费": 220, + "生长时间": 3000, + "收益": 370, + "品质": "优良", + "描述": "高档蔬菜,营养价值极高", + "耐候性": 8, + "等级": 4, + "经验": 40, + "能否购买": true + }, + "苹果": { + "作物名称": "苹果树", + "成熟物名称": "苹果", + "花费": 450, + "生长时间": 9600, + "收益": 820, + "品质": "优良", + "描述": "结果丰富的果树", + "耐候性": 14, + "等级": 7, + "经验": 110, + "能否购买": true, + "喂养效果": { + "生命值": 5, + "护甲值": 2 + } + }, + "橘子": { + "作物名称": "橘子树", + "成熟物名称": "橘子", + "花费": 480, + "生长时间": 10200, + "收益": 850, + "品质": "优良", + "描述": "维生素C丰富的果树", + "耐候性": 12, + "等级": 7, + "经验": 115, + "能否购买": true + }, + "香蕉": { + "作物名称": "香蕉树", + "成熟物名称": "香蕉", + "花费": 420, + "生长时间": 8400, + "收益": 780, + "品质": "优良", + "描述": "热带水果之王", + "耐候性": 5, + "等级": 7, + "经验": 100, + "能否购买": true + }, + "牵牛花": { + "作物名称": "牵牛花", + "成熟物名称": "牵牛花", + "花费": 85, + "生长时间": 1080, + "收益": 145, + "品质": "稀有", + "描述": "富含蛋白质的豆类作物", + "耐候性": 9, + "等级": 2, + "经验": 18, + "能否购买": true + }, + "百合花": { + "作物名称": "百合花", + "成熟物名称": "百合花", + "花费": 260, + "生长时间": 4800, + "收益": 480, + "品质": "稀有", + "描述": "美丽的观赏植物", + "耐候性": 10, + "等级": 5, + "经验": 50, + "能否购买": true + }, + "山葵": { + "作物名称": "山葵", + "成熟物名称": "山葵", + "花费": 500, + "生长时间": 10800, + "收益": 1000, + "品质": "稀有", + "描述": "日式料理的珍贵调料", + "耐候性": 22, + "等级": 7, + "经验": 150, + "能否购买": true + }, + "草莓": { + "作物名称": "草莓", + "成熟物名称": "草莓", + "花费": 120, + "生长时间": 1440, + "收益": 200, + "品质": "稀有", + "描述": "甜美的浆果,深受喜爱", + "耐候性": 6, + "等级": 3, + "经验": 22, + "能否购买": true, + "喂养效果": { + "经验": 8, + "暴击率": 3, + "亲密度": 10 + } + }, + "蓝莓": { + "作物名称": "蓝莓", + "成熟物名称": "蓝莓", + "花费": 160, + "生长时间": 2100, + "收益": 260, + "品质": "稀有", + "描述": "抗氧化的超级食物", + "耐候性": 7, + "等级": 3, + "经验": 28, + "能否购买": true + }, + "栀子花": { + "作物名称": "栀子花", + "成熟物名称": "栀子花", + "花费": 180, + "生长时间": 2400, + "收益": 300, + "品质": "稀有", + "描述": "美丽的观赏植物", + "耐候性": 10, + "等级": 3, + "经验": 25, + "能否购买": true + }, + "玫瑰花": { + "作物名称": "玫瑰花", + "成熟物名称": "玫瑰花", + "花费": 190, + "生长时间": 2520, + "收益": 310, + "品质": "稀有", + "描述": "美丽的观赏植物", + "耐候性": 10, + "等级": 3, + "经验": 25, + "能否购买": true + }, + "菠萝": { + "作物名称": "菠萝", + "成熟物名称": "菠萝", + "花费": 200, + "生长时间": 2700, + "收益": 340, + "品质": "稀有", + "描述": "热带水果之王", + "耐候性": 10, + "等级": 3, + "经验": 25, + "能否购买": true + }, + "芒果": { + "作物名称": "芒果树", + "成熟物名称": "芒果", + "花费": 240, + "生长时间": 3180, + "收益": 400, + "品质": "稀有", + "描述": "热带阳光孕育的香甜果实", + "耐候性": 10, + "等级": 3, + "经验": 27, + "能否购买": true + }, + "咖啡豆": { + "作物名称": "咖啡豆", + "成熟物名称": "咖啡豆", + "花费": 200, + "生长时间": 2500, + "收益": 340, + "品质": "稀有", + "描述": "热带经济作物,果实经烘焙后可制成饮品,需温暖气候", + "耐候性": 8, + "等级": 4, + "经验": 35, + "能否购买": true + }, + "可可豆": { + "作物名称": "可可树", + "成熟物名称": "可可果", + "花费": 200, + "生长时间": 2500, + "收益": 340, + "品质": "稀有", + "描述": "制作巧克力的原料,需高温高湿环境,经济价值较高", + "耐候性": 10, + "等级": 4, + "经验": 36, + "能否购买": true + }, + "向日葵": { + "作物名称": "向日葵", + "成熟物名称": "向日葵", + "花费": 160, + "生长时间": 3600, + "收益": 280, + "品质": "稀有", + "描述": "向阳而生的美丽花朵", + "耐候性": 13, + "等级": 4, + "经验": 35, + "能否购买": true + }, + "哈密瓜": { + "作物名称": "哈密瓜", + "成熟物名称": "哈密瓜", + "花费": 380, + "生长时间": 7800, + "收益": 700, + "品质": "稀有", + "描述": "甘甜的高级水果", + "耐候性": 8, + "等级": 6, + "经验": 85, + "能否购买": true + }, + "藏红花": { + "作物名称": "藏红花", + "成熟物名称": "藏红花", + "花费": 400, + "生长时间": 7000, + "收益": 710, + "品质": "稀有", + "描述": "世界上最昂贵的香料之一,需精心照料,花朵可提取柱头", + "耐候性": 9, + "等级": 6, + "经验": 80, + "能否购买": true + }, + "龙果": { + "作物名称": "火龙果", + "成熟物名称": "火龙果", + "花费": 800, + "生长时间": 14400, + "收益": 1500, + "品质": "稀有", + "描述": "传说中的神秘果实,蕴含强大能量", + "耐候性": 25, + "等级": 8, + "经验": 200, + "能否购买": true + }, + "杨桃": { + "作物名称": "杨桃", + "成熟物名称": "杨桃", + "花费": 85, + "生长时间": 1080, + "收益": 145, + "品质": "史诗", + "描述": "富含蛋白质的豆类作物", + "耐候性": 9, + "等级": 2, + "经验": 18, + "能否购买": true + }, + "香草": { + "作物名称": "香草", + "成熟物名称": "香草", + "花费": 300, + "生长时间": 6000, + "收益": 550, + "品质": "史诗", + "描述": "珍贵的调料植物", + "耐候性": 7, + "等级": 5, + "经验": 60, + "能否购买": true + }, + "康乃馨": { + "作物名称": "康乃馨", + "成熟物名称": "康乃馨", + "花费": 270, + "生长时间": 5100, + "收益": 510, + "品质": "史诗", + "描述": "美丽的观赏植物", + "耐候性": 10, + "等级": 5, + "经验": 50, + "能否购买": true + }, + "藏羚羊草": { + "作物名称": "藏羚羊草", + "成熟物名称": "藏羚羊草", + "花费": 80, + "生长时间": 200, + "收益": 200, + "品质": "史诗", + "描述": "高原珍稀草种,用于生态修复,对土壤要求苛刻但市场需求大", + "耐候性": 15, + "等级": 5, + "经验": 20, + "能否购买": true + }, + "迷迭香": { + "作物名称": "迷迭香", + "成熟物名称": "迷迭香", + "花费": 100, + "生长时间": 1680, + "收益": 210, + "品质": "史诗", + "描述": "芳香型草本植物,可用于烹饪和精油提取,耐旱易存活", + "耐候性": 12, + "等级": 3, + "经验": 22, + "能否购买": true + }, + "蕨菜": { + "作物名称": "蕨菜", + "成熟物名称": "蕨菜", + "花费": 180, + "生长时间": 2400, + "收益": 300, + "品质": "史诗", + "描述": "野生山菜,口感独特", + "耐候性": 16, + "等级": 4, + "经验": 35, + "能否购买": true + }, + "人参": { + "作物名称": "人参", + "成熟物名称": "人参", + "花费": 400, + "生长时间": 7200, + "收益": 720, + "品质": "史诗", + "描述": "珍贵的药用植物,需要耐心培养", + "耐候性": 15, + "等级": 6, + "经验": 80, + "能否购买": true + }, + "富贵竹": { + "作物名称": "富贵竹", + "成熟物名称": "富贵竹", + "花费": 350, + "生长时间": 6600, + "收益": 650, + "品质": "史诗", + "描述": "寓意吉祥的观赏植物", + "耐候性": 12, + "等级": 6, + "经验": 75, + "能否购买": true + }, + "芦荟": { + "作物名称": "芦荟", + "成熟物名称": "芦荟", + "花费": 320, + "生长时间": 6000, + "收益": 600, + "品质": "史诗", + "描述": "具有药用价值的多肉植物", + "耐候性": 18, + "等级": 6, + "经验": 70, + "能否购买": true + }, + "金橘": { + "作物名称": "金橘子树", + "成熟物名称": "金橘", + "花费": 500, + "生长时间": 10800, + "收益": 900, + "品质": "史诗", + "描述": "金黄色的珍贵柑橘", + "耐候性": 10, + "等级": 7, + "经验": 120, + "能否购买": true + }, + "松露": { + "作物名称": "松露", + "成熟物名称": "松露", + "花费": 1000, + "生长时间": 18000, + "收益": 2000, + "品质": "史诗", + "描述": "地下的黑金,顶级料理的灵魂", + "耐候性": 20, + "等级": 8, + "经验": 250, + "能否购买": true + }, + "冬虫夏草": { + "作物名称": "冬虫夏草", + "成熟物名称": "冬虫夏草", + "花费": 600, + "生长时间": 12000, + "收益": 1200, + "品质": "史诗", + "描述": "稀世珍宝,药王之称", + "耐候性": 30, + "等级": 7, + "经验": 180, + "能否购买": true + }, + "糖果树": { + "作物名称": "糖果树", + "成熟物名称": null, + "花费": 250, + "生长时间": 3300, + "收益": 420, + "品质": "传奇", + "描述": "传说中结出彩色糖果的魔法树", + "耐候性": 7, + "等级": 3, + "经验": 25, + "能否购买": true + }, + "面包树": { + "作物名称": "面包树", + "成熟物名称": null, + "花费": 260, + "生长时间": 3420, + "收益": 440, + "品质": "传奇", + "描述": "热带地区的淀粉质主食树种", + "耐候性": 9, + "等级": 3, + "经验": 25, + "能否购买": true + }, + "幸运草": { + "作物名称": "幸运草", + "成熟物名称": null, + "花费": 150, + "生长时间": 1560, + "收益": 220, + "品质": "传奇", + "描述": "四片叶子的幸运象征植物", + "耐候性": 6, + "等级": 3, + "经验": 20, + "能否购买": true + }, + "幸运花": { + "作物名称": "幸运花", + "成熟物名称": null, + "花费": 170, + "生长时间": 1680, + "收益": 240, + "品质": "传奇", + "描述": "绽放时带来好运的神秘花卉", + "耐候性": 7, + "等级": 3, + "经验": 22, + "能否购买": true + }, + "摇钱树": { + "作物名称": "摇钱树", + "成熟物名称": null, + "花费": 300, + "生长时间": 3600, + "收益": 500, + "品质": "传奇", + "描述": "传说中能结出金币的神树", + "耐候性": 10, + "等级": 3, + "经验": 30, + "能否购买": true + }, + "月光草": { + "作物名称": "月光草", + "成熟物名称": "月光草", + "花费": 700, + "生长时间": 14500, + "收益": 1500, + "品质": "传奇", + "描述": "夜间开花的神秘植物,花瓣在月光下散发荧光,传说具有许愿功效", + "耐候性": 20, + "等级": 8, + "经验": 200, + "能否购买": true + }, + "凤凰木": { + "作物名称": "凤凰树", + "成熟物名称": "凤凰木", + "花费": 650, + "生长时间": 12600, + "收益": 1400, + "品质": "传奇", + "描述": "传说中凤凰栖息的神树,开花时如火焰般绚烂,木材可驱邪避灾", + "耐候性": 25, + "等级": 8, + "经验": 210, + "能否购买": true + }, + "杂交树1": { + "作物名称": "0号杂交树", + "成熟物名称": null, + "花费": 1200, + "生长时间": 21600, + "收益": 2500, + "品质": "传奇", + "描述": "初中生物书最后的幻想", + "耐候性": 35, + "等级": 9, + "经验": 300, + "能否购买": true + }, + "杂交树2": { + "作物名称": "1号杂交树", + "成熟物名称": null, + "花费": 1500, + "生长时间": 25200, + "收益": 3000, + "品质": "传奇", + "描述": "初中生物书最后的想象", + "耐候性": 40, + "等级": 10, + "经验": 400, + "能否购买": true + } + } +} \ No newline at end of file diff --git a/Server/logs/server_monitor_20250815.log b/Server/logs/server_monitor_20250815.log new file mode 100644 index 0000000..7966e43 --- /dev/null +++ b/Server/logs/server_monitor_20250815.log @@ -0,0 +1,85 @@ +2025-08-15 13:15:48,010 [INFO] 服务器监控器启动 +2025-08-15 13:15:48,010 [INFO] 正在启动服务器... +2025-08-15 13:15:48,406 [INFO] [SERVER] ============================================================ +2025-08-15 13:15:48,420 [INFO] [SERVER] Traceback (most recent call last): +2025-08-15 13:15:48,420 [ERROR] 检测到严重错误: Traceback (most recent call last): +2025-08-15 13:15:48,422 [INFO] [SERVER] File "E:\Godot\Godot项目\比较完成的作品\萌芽农场\Server\TCPGameServer.py", line 10395, in +2025-08-15 13:15:48,423 [INFO] [SERVER] print(f"\U0001f331 萌芽农场游戏服务器 v{server_version} \U0001f331") +2025-08-15 13:15:48,423 [INFO] [SERVER] ~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +2025-08-15 13:15:48,423 [INFO] [SERVER] File "E:\ProgramLanguage\Python\python313\Lib\site-packages\colorama\ansitowin32.py", line 47, in write +2025-08-15 13:15:48,423 [INFO] [SERVER] self.__convertor.write(text) +2025-08-15 13:15:48,423 [INFO] [SERVER] ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ +2025-08-15 13:15:48,423 [INFO] [SERVER] File "E:\ProgramLanguage\Python\python313\Lib\site-packages\colorama\ansitowin32.py", line 177, in write +2025-08-15 13:15:48,423 [INFO] [SERVER] self.write_and_convert(text) +2025-08-15 13:15:48,423 [INFO] [SERVER] ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ +2025-08-15 13:15:48,424 [INFO] [SERVER] File "E:\ProgramLanguage\Python\python313\Lib\site-packages\colorama\ansitowin32.py", line 205, in write_and_convert +2025-08-15 13:15:48,424 [INFO] [SERVER] self.write_plain_text(text, cursor, len(text)) +2025-08-15 13:15:48,424 [INFO] [SERVER] ~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ +2025-08-15 13:15:48,424 [INFO] [SERVER] File "E:\ProgramLanguage\Python\python313\Lib\site-packages\colorama\ansitowin32.py", line 210, in write_plain_text +2025-08-15 13:15:48,424 [INFO] [SERVER] self.wrapped.write(text[start:end]) +2025-08-15 13:15:48,424 [INFO] [SERVER] ~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^ +2025-08-15 13:15:48,424 [INFO] [SERVER] UnicodeEncodeError: 'gbk' codec can't encode character '\U0001f331' in position 0: illegal multibyte sequence +2025-08-15 13:15:48,424 [INFO] [SERVER] During handling of the above exception, another exception occurred: +2025-08-15 13:15:48,424 [INFO] [SERVER] Traceback (most recent call last): +2025-08-15 13:15:48,424 [ERROR] 检测到严重错误: Traceback (most recent call last): +2025-08-15 13:15:48,424 [INFO] [SERVER] File "E:\Godot\Godot项目\比较完成的作品\萌芽农场\Server\TCPGameServer.py", line 10447, in +2025-08-15 13:15:48,424 [INFO] [SERVER] print(f"\n\u274c 服务器启动失败: {str(e)}") +2025-08-15 13:15:48,424 [INFO] [SERVER] ~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +2025-08-15 13:15:48,425 [INFO] [SERVER] File "E:\ProgramLanguage\Python\python313\Lib\site-packages\colorama\ansitowin32.py", line 47, in write +2025-08-15 13:15:48,425 [INFO] [SERVER] self.__convertor.write(text) +2025-08-15 13:15:48,425 [INFO] [SERVER] ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ +2025-08-15 13:15:48,425 [INFO] [SERVER] File "E:\ProgramLanguage\Python\python313\Lib\site-packages\colorama\ansitowin32.py", line 177, in write +2025-08-15 13:15:48,425 [INFO] [SERVER] self.write_and_convert(text) +2025-08-15 13:15:48,425 [INFO] [SERVER] ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ +2025-08-15 13:15:48,425 [INFO] [SERVER] File "E:\ProgramLanguage\Python\python313\Lib\site-packages\colorama\ansitowin32.py", line 205, in write_and_convert +2025-08-15 13:15:48,425 [INFO] [SERVER] self.write_plain_text(text, cursor, len(text)) +2025-08-15 13:15:48,425 [INFO] [SERVER] ~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ +2025-08-15 13:15:48,425 [INFO] [SERVER] File "E:\ProgramLanguage\Python\python313\Lib\site-packages\colorama\ansitowin32.py", line 210, in write_plain_text +2025-08-15 13:15:48,425 [INFO] [SERVER] self.wrapped.write(text[start:end]) +2025-08-15 13:15:48,425 [INFO] [SERVER] ~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^ +2025-08-15 13:15:48,425 [INFO] [SERVER] UnicodeEncodeError: 'gbk' codec can't encode character '\u274c' in position 2: illegal multibyte sequence +2025-08-15 13:15:53,015 [ERROR] 服务器启动失败 +2025-08-15 13:15:53,015 [ERROR] 初始启动失败,退出监控 +2025-08-15 13:17:34,183 [INFO] 服务器监控器启动 +2025-08-15 13:17:34,183 [INFO] 正在启动服务器... +2025-08-15 13:17:34,623 [INFO] [SERVER] ============================================================ +2025-08-15 13:17:34,623 [INFO] [SERVER] [ERROR] 服务器启动失败: 'gbk' codec can't encode character '\U0001f331' in position 0: illegal multibyte sequence +2025-08-15 13:17:34,628 [INFO] [SERVER] Traceback (most recent call last): +2025-08-15 13:17:34,628 [ERROR] 检测到严重错误: Traceback (most recent call last): +2025-08-15 13:17:34,628 [INFO] [SERVER] File "E:\Godot\Godot项目\比较完成的作品\萌芽农场\Server\TCPGameServer.py", line 10395, in +2025-08-15 13:17:34,628 [INFO] [SERVER] print(f"\U0001f331 萌芽农场游戏服务器 v{server_version} \U0001f331") +2025-08-15 13:17:34,629 [INFO] [SERVER] ~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +2025-08-15 13:17:34,629 [INFO] [SERVER] File "E:\ProgramLanguage\Python\python313\Lib\site-packages\colorama\ansitowin32.py", line 47, in write +2025-08-15 13:17:34,629 [INFO] [SERVER] self.__convertor.write(text) +2025-08-15 13:17:34,629 [INFO] [SERVER] ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ +2025-08-15 13:17:34,629 [INFO] [SERVER] File "E:\ProgramLanguage\Python\python313\Lib\site-packages\colorama\ansitowin32.py", line 177, in write +2025-08-15 13:17:34,629 [INFO] [SERVER] self.write_and_convert(text) +2025-08-15 13:17:34,629 [INFO] [SERVER] ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ +2025-08-15 13:17:34,630 [INFO] [SERVER] File "E:\ProgramLanguage\Python\python313\Lib\site-packages\colorama\ansitowin32.py", line 205, in write_and_convert +2025-08-15 13:17:34,630 [INFO] [SERVER] self.write_plain_text(text, cursor, len(text)) +2025-08-15 13:17:34,630 [INFO] [SERVER] ~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ +2025-08-15 13:17:34,630 [INFO] [SERVER] File "E:\ProgramLanguage\Python\python313\Lib\site-packages\colorama\ansitowin32.py", line 210, in write_plain_text +2025-08-15 13:17:34,630 [INFO] [SERVER] self.wrapped.write(text[start:end]) +2025-08-15 13:17:34,630 [INFO] [SERVER] ~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^ +2025-08-15 13:17:34,630 [INFO] [SERVER] UnicodeEncodeError: 'gbk' codec can't encode character '\U0001f331' in position 0: illegal multibyte sequence +2025-08-15 13:17:34,630 [INFO] [SERVER] During handling of the above exception, another exception occurred: +2025-08-15 13:17:34,630 [INFO] [SERVER] Traceback (most recent call last): +2025-08-15 13:17:34,631 [ERROR] 检测到严重错误: Traceback (most recent call last): +2025-08-15 13:17:34,631 [INFO] [SERVER] File "E:\Godot\Godot项目\比较完成的作品\萌芽农场\Server\TCPGameServer.py", line 10448, in +2025-08-15 13:17:34,631 [INFO] [SERVER] print("\U0001f527 请检查配置并重试") +2025-08-15 13:17:34,631 [INFO] [SERVER] ~~~~~^^^^^^^^^^^^^^^^^^^^^^^ +2025-08-15 13:17:34,631 [INFO] [SERVER] File "E:\ProgramLanguage\Python\python313\Lib\site-packages\colorama\ansitowin32.py", line 47, in write +2025-08-15 13:17:34,631 [INFO] [SERVER] self.__convertor.write(text) +2025-08-15 13:17:34,632 [INFO] [SERVER] ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ +2025-08-15 13:17:34,632 [INFO] [SERVER] File "E:\ProgramLanguage\Python\python313\Lib\site-packages\colorama\ansitowin32.py", line 177, in write +2025-08-15 13:17:34,632 [INFO] [SERVER] self.write_and_convert(text) +2025-08-15 13:17:34,632 [INFO] [SERVER] ~~~~~~~~~~~~~~~~~~~~~~^^^^^^ +2025-08-15 13:17:34,632 [INFO] [SERVER] File "E:\ProgramLanguage\Python\python313\Lib\site-packages\colorama\ansitowin32.py", line 205, in write_and_convert +2025-08-15 13:17:34,632 [INFO] [SERVER] self.write_plain_text(text, cursor, len(text)) +2025-08-15 13:17:34,632 [INFO] [SERVER] ~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ +2025-08-15 13:17:34,632 [INFO] [SERVER] File "E:\ProgramLanguage\Python\python313\Lib\site-packages\colorama\ansitowin32.py", line 210, in write_plain_text +2025-08-15 13:17:34,633 [INFO] [SERVER] self.wrapped.write(text[start:end]) +2025-08-15 13:17:34,633 [INFO] [SERVER] ~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^ +2025-08-15 13:17:34,633 [INFO] [SERVER] UnicodeEncodeError: 'gbk' codec can't encode character '\U0001f527' in position 0: illegal multibyte sequence +2025-08-15 13:17:39,187 [ERROR] 服务器启动失败 +2025-08-15 13:17:39,187 [ERROR] 初始启动失败,退出监控 diff --git a/Server/requirements.txt b/Server/requirements.txt index fe2618d..2097e64 100644 --- a/Server/requirements.txt +++ b/Server/requirements.txt @@ -1,5 +1,7 @@ # Game Server Dependencies colorama>=0.4.6 # For colored terminal output pymongo>=4.6.0 # MongoDB driver for Python +schedule>=1.2.0 # Task scheduling for special farm management +websockets>=11.0.3 # WebSocket server for remote console API # Email Requirements # Standard library dependencies are not listed (socket, threading, json, etc.) \ No newline at end of file diff --git a/Server/special_farm.log b/Server/special_farm.log new file mode 100644 index 0000000..e69de29 diff --git a/Server/ConsoleCommandsAPI_README.md b/Server/test/ConsoleCommandsAPI_README.md similarity index 100% rename from Server/ConsoleCommandsAPI_README.md rename to Server/test/ConsoleCommandsAPI_README.md diff --git a/Server/console_demo.py b/Server/test/console_demo.py similarity index 100% rename from Server/console_demo.py rename to Server/test/console_demo.py diff --git a/Server/test/gameconfig/mengyafarm.gameconfig.json b/Server/test/gameconfig/mengyafarm.gameconfig.json new file mode 100644 index 0000000..3fb44a8 --- /dev/null +++ b/Server/test/gameconfig/mengyafarm.gameconfig.json @@ -0,0 +1,2578 @@ +[{ + "_id": { + "$oid": "687cce278e77ba00a7414ba2" + }, + "updated_at": { + "$date": "2025-07-20T19:29:44.518Z" + }, + "config_type":"每日签到系统配置", + "基础奖励": { + "金币": { + "最小值": 200, + "最大值": 500, + "图标": "💰", + "颜色": "#FFD700" + }, + "经验": { + "最小值": 50, + "最大值": 120, + "图标": "⭐", + "颜色": "#00BFFF" + } + }, + "种子奖励": { + "普通": { + "概率": 0.6, + "数量范围": [ + 2, + 5 + ], + "种子池": [ + "小麦", + "胡萝卜", + "土豆", + "稻谷" + ] + }, + "优良": { + "概率": 0.25, + "数量范围": [ + 2, + 4 + ], + "种子池": [ + "玉米", + "番茄", + "洋葱", + "大豆", + "豌豆", + "黄瓜", + "大白菜" + ] + }, + "稀有": { + "概率": 0.12, + "数量范围": [ + 1, + 3 + ], + "种子池": [ + "草莓", + "花椰菜", + "柿子", + "蓝莓", + "树莓" + ] + }, + "史诗": { + "概率": 0.025, + "数量范围": [ + 1, + 2 + ], + "种子池": [ + "葡萄", + "南瓜", + "芦笋", + "茄子", + "向日葵", + "蕨菜" + ] + }, + "传奇": { + "概率": 0.005, + "数量范围": [ + 1, + 1 + ], + "种子池": [ + "西瓜", + "甘蔗", + "香草", + "甜菜", + "人参", + "富贵竹", + "芦荟", + "哈密瓜" + ] + } + }, + "连续签到奖励": { + "第3天": { + "额外金币": 100, + "额外经验": 50, + "描述": "连续签到奖励" + }, + "第7天": { + "额外金币": 200, + "额外经验": 100, + "描述": "一周连击奖励" + }, + "第14天": { + "额外金币": 500, + "额外经验": 200, + "描述": "半月连击奖励" + }, + "第21天": { + "额外金币": 800, + "额外经验": 300, + "描述": "三周连击奖励" + }, + "第30天": { + "额外金币": 1500, + "额外经验": 500, + "描述": "满月连击奖励" + } + } +}, +{ + "_id": { + "$oid": "687cd52e8e77ba00a7414ba3" + }, + "config_type":"幸运抽奖系统配置", + "抽奖费用": { + "单抽": 800, + "五连抽": 3600, + "十连抽": 6400 + }, + "概率配置": { + "普通": { + "概率": 0.45, + "金币范围": [ + 100, + 300 + ], + "经验范围": [ + 50, + 150 + ], + "种子数量": [ + 2, + 4 + ] + }, + "优良": { + "概率": 0.25, + "金币范围": [ + 300, + 600 + ], + "经验范围": [ + 150, + 300 + ], + "种子数量": [ + 1, + 3 + ] + }, + "稀有": { + "概率": 0.12, + "金币范围": [ + 600, + 1000 + ], + "经验范围": [ + 300, + 500 + ], + "种子数量": [ + 1, + 2 + ] + }, + "史诗": { + "概率": 0.025, + "金币范围": [ + 1000, + 1500 + ], + "经验范围": [ + 500, + 800 + ], + "种子数量": [ + 1, + 1 + ] + }, + "传奇": { + "概率": 0.005, + "金币范围": [ + 1500, + 2500 + ], + "经验范围": [ + 800, + 1200 + ], + "种子数量": [ + 1, + 1 + ] + }, + "空奖": { + "概率": 0.15, + "提示语": [ + "谢谢惠顾", + "下次再来", + "再试一次", + "继续努力" + ] + } + }, + "礼包配置": { + "成长套餐": { + "稀有度": "优良", + "内容": { + "金币": [ + 200, + 400 + ], + "经验": [ + 100, + 200 + ], + "种子数量": [ + 2, + 3 + ] + } + }, + "稀有礼包": { + "稀有度": "稀有", + "内容": { + "金币": [ + 400, + 700 + ], + "经验": [ + 200, + 350 + ], + "种子数量": [ + 2, + 3 + ] + } + }, + "史诗礼包": { + "稀有度": "史诗", + "内容": { + "金币": [ + 700, + 1200 + ], + "经验": [ + 350, + 600 + ], + "种子数量": [ + 1, + 2 + ] + } + }, + "传奇大礼包": { + "稀有度": "传奇", + "内容": { + "金币": [ + 1000, + 2000 + ], + "经验": [ + 600, + 1000 + ], + "史诗种子数量": [ + 1, + 1 + ], + "稀有种子数量": [ + 2, + 3 + ] + } + } + }, + "保底机制": { + "十连抽保底": true, + "保底最低稀有度": "稀有" + }, + "奖励类型权重": { + "普通": { + "金币": 0.4, + "经验": 0.3, + "种子": 0.3 + }, + "优良": { + "金币": 0.3, + "经验": 0.2, + "种子": 0.3, + "礼包": 0.2 + }, + "稀有": { + "金币": 0.2, + "经验": 0.2, + "种子": 0.4, + "礼包": 0.2 + }, + "史诗": { + "金币": 0.2, + "经验": 0.2, + "种子": 0.4, + "礼包": 0.2 + }, + "传奇": { + "金币": 0.1, + "经验": 0.1, + "种子": 0.5, + "礼包": 0.3 + } + } +}, +{ + "_id": { + "$oid": "687cdbd78e77ba00a7414ba4" + }, + "config_type":"新手礼包系统配置", + "新手礼包配置": { + "礼包说明": "每个账号仅可领取一次的新手大礼包", + "奖励内容": { + "金币": 6000, + "经验": 1000, + "种子": [ + { + "名称": "龙果", + "品质": "传奇", + "数量": 1 + }, + { + "名称": "杂交树1", + "品质": "传奇", + "数量": 1 + }, + { + "名称": "杂交树2", + "品质": "传奇", + "数量": 1 + } + ] + }, + "提示消息": { + "成功": "新手大礼包领取成功!获得6000金币、1000经验和3个传奇种子", + "已领取": "新手大礼包已经领取过了" + } + } +}, +{ + "_id": { + "$oid": "687cdfbe8e77ba00a7414ba5" + }, + "updated_at": { + "$date": "2025-07-20T20:38:52.577Z" + }, + "config_type":"智慧树系统配置", + "messages": [ + { + "timestamp": "2024-01-01 12:00:00", + "sender": "system", + "content": "欢迎来到智慧树!这里是一个匿名的心愿许愿池,分享你的心声吧~", + "id": "default_message_001" + }, + { + "timestamp": "2024-01-01 12:30:00", + "sender": "system", + "content": "记得定期给智慧树浇水施肥哦,它会越长越高~", + "id": "default_message_002" + }, + { + "timestamp": "2024-01-01 13:00:00", + "sender": "system", + "content": "智慧树需要你的呵护,除草杀虫让它更健康!", + "id": "default_message_003" + }, + { + "timestamp": "2025-07-09 10:52:31", + "sender": "2143323382", + "content": "陌生人你好呀", + "id": "e62d7c3e-4965-4110-b7f2-a5f5a3806d86" + }, + { + "timestamp": "2025-07-09 15:46:18", + "sender": "2143323382", + "content": "你好大傻逼!", + "id": "13c3e296-b23b-4ad4-a68e-93d28769614a" + }, + { + "timestamp": "2025-07-09 16:53:16", + "sender": "2143323382", + "content": "我喜欢柚小青", + "id": "5bf10c6b-fc73-4a5f-a246-99a474148175" + }, + { + "timestamp": "2025-07-09 16:54:31", + "sender": "2143323382", + "content": "柚小青最可爱", + "id": "f1af2d77-c860-4f1a-8d2b-f5dd0dc5a672" + }, + { + "timestamp": "2025-07-20 20:36:26", + "sender": "2143323382", + "content": "测试MongoDB数据库的智慧树配置", + "id": "ea1c181d-d7c7-4597-9e3d-d3eaa1886ec3" + }, + { + "timestamp": "2025-07-20 20:38:08", + "sender": "2143323382", + "content": "测试数据库保存成功", + "id": "0d4d364f-f920-4b7f-8a8a-9de9f5f315b8" + }, + { + "timestamp": "2025-07-20 20:38:52", + "sender": "2143323382", + "content": "你好,树萌芽", + "id": "c566ebb7-4950-46b2-a5c0-cfed7f063733" + } + ], + "total_messages": 10, + "last_update": "2025-07-20 20:38:52" +}, +{ + "_id": { + "$oid": "687ce7678e77ba00a7414ba6" + }, + "updated_at": { + "$date": "2025-07-20T20:59:35.585Z" + }, + "config_type":"在线礼包系统配置", + "在线礼包配置": { + "1分钟": { + "时长秒数": 60, + "奖励": { + "金币": 100, + "经验": 50, + "种子": [ + { + "名称": "小麦", + "数量": 5 + }, + { + "名称": "胡萝卜", + "数量": 3 + } + ] + } + }, + "3分钟": { + "时长秒数": 180, + "奖励": { + "金币": 250, + "经验": 150, + "种子": [ + { + "名称": "胡萝卜", + "数量": 5 + }, + { + "名称": "玉米", + "数量": 3 + } + ] + } + }, + "5分钟": { + "时长秒数": 300, + "奖励": { + "金币": 500, + "经验": 250, + "种子": [ + { + "名称": "玉米", + "数量": 3 + }, + { + "名称": "番茄", + "数量": 2 + } + ] + } + }, + "10分钟": { + "时长秒数": 600, + "奖励": { + "金币": 800, + "经验": 400, + "种子": [ + { + "名称": "番茄", + "数量": 3 + }, + { + "名称": "辣椒", + "数量": 2 + } + ] + } + }, + "30分钟": { + "时长秒数": 1800, + "奖励": { + "金币": 1500, + "经验": 750, + "种子": [ + { + "名称": "草莓", + "数量": 2 + }, + { + "名称": "花椰菜", + "数量": 2 + } + ] + } + }, + "1小时": { + "时长秒数": 3600, + "奖励": { + "金币": 3000, + "经验": 1500, + "种子": [ + { + "名称": "葡萄", + "数量": 2 + }, + { + "名称": "南瓜", + "数量": 1 + }, + { + "名称": "咖啡豆", + "数量": 1 + } + ] + } + }, + "3小时": { + "时长秒数": 10800, + "奖励": { + "金币": 8000, + "经验": 4000, + "种子": [ + { + "名称": "人参", + "数量": 1 + }, + { + "名称": "藏红花", + "数量": 1 + } + ] + } + }, + "5小时": { + "时长秒数": 18000, + "奖励": { + "金币": 15000, + "经验": 7500, + "种子": [ + { + "名称": "龙果", + "数量": 1 + }, + { + "名称": "松露", + "数量": 1 + }, + { + "名称": "月光草", + "数量": 1 + } + ] + } + } + }, + "每日重置": true, + "在线时长记录保留天数": 1 +}, +{ + "_id": { + "$oid": "687cea258e77ba00a7414ba8" + }, + "updated_at": { + "$date": "2025-07-20T21:15:49.136Z" + }, + "config_type":"稻草人系统配置", + "稻草人类型": { + "稻草人1": { + "图片": "res://assets/道具图片/稻草人1.webp", + "价格": 1000 + }, + "稻草人2": { + "图片": "res://assets/道具图片/稻草人2.webp", + "价格": 1000 + }, + "稻草人3": { + "图片": "res://assets/道具图片/稻草人3.webp", + "价格": 1000 + } + }, + "修改稻草人配置花费": 300 +}, +{ + "_id": { + "$oid": "687cefba8e77ba00a7414ba9" + }, + "updated_at": { + "$date": "2025-07-20T21:35:10.396Z" + }, + "config_type":"体力系统配置", + "体力系统配置": { + "最大体力值": 25, + "每小时恢复体力": 1, + "恢复间隔秒数": 3600, + "新玩家初始体力": 20, + "体力消耗": { + "浇水": 1, + "施肥": 1, + "挖地": 2, + "收获": 1, + "种植": 1, + "除草": 1, + "偷菜": 2 + }, + "提示消息": { + "体力不足": "体力值不足!当前体力:{current_stamina},需要:{required_stamina}", + "体力恢复": "体力已恢复到:{current_stamina}", + "体力重置": "新的一天,体力已重置为:{current_stamina}", + "体力消耗": "消耗 {amount} 点体力,剩余体力:{remaining_stamina}" + } + } +}, +{ + "_id": { + "$oid": "687cf17c8e77ba00a7414baa" + }, + "updated_at": { + "$date": "2025-07-20T21:48:31.814Z" + }, + "config_type":"道具系统配置", + "精准采集-镰刀": { + "花费": 100, + "描述": "可以在收获作物时必定掉落该作物的种子", + "类型": "作物道具", + "道具图片": "res://assets/道具图片/紫水晶镰刀.webp" + }, + "时运-镰刀": { + "花费": 100, + "描述": "可以在收获作物时掉落更多的作物的收获物", + "类型": "作物道具", + "道具图片": "res://assets/道具图片/红宝石镰刀.webp" + }, + "农家肥": { + "花费": 100, + "描述": "(施肥道具)可以在30分钟内2倍速作物生长", + "类型": "作物道具", + "道具图片": "res://assets/道具图片/农家肥.webp" + }, + "金坷垃": { + "花费": 100, + "描述": "(施肥道具)可以在5分钟内5倍速作物的生长", + "类型": "作物道具", + "道具图片": "res://assets/道具图片/金坷垃2.webp" + }, + "水壶": { + "花费": 100, + "描述": "(浇水道具)直接让作物生长进度+1%", + "类型": "作物道具", + "道具图片": "res://assets/道具图片/铁质洒水壶.webp" + }, + "水桶": { + "花费": 100, + "描述": "(浇水道具)让作物生长进度+2%", + "类型": "作物道具", + "道具图片": "res://assets/道具图片/木质水桶2.webp" + }, + "杀虫剂": { + "花费": 100, + "描述": "杀虫,暂时没什么用", + "类型": "作物道具", + "道具图片": "res://assets/道具图片/杀虫剂.webp" + }, + "除草剂": { + "花费": 100, + "描述": "除草", + "类型": "作物道具", + "道具图片": "res://assets/道具图片/除草剂.webp" + }, + "生长素": { + "花费": 100, + "描述": "时运可以10分钟内3倍速作物生长,而且作物生长速度+3%", + "类型": "作物道具", + "道具图片": "res://assets/道具图片/生长素.webp" + }, + "铲子": { + "花费": 100, + "描述": "铲除作物", + "类型": "作物道具", + "道具图片": "res://assets/道具图片/附魔铁铲.webp" + }, + "不死图腾": { + "花费": 100, + "描述": "让宠物死亡复活一次", + "类型": "宠物道具", + "道具图片": "res://assets/道具图片/不死图腾.webp" + }, + "荆棘护甲": { + "花费": 100, + "描述": "宠物收到伤害时反弹伤害给敌人", + "类型": "宠物道具", + "道具图片": "res://assets/道具图片/荆棘护甲.webp" + }, + "狂暴药水": { + "花费": 100, + "描述": "宠物血量低于某个值,进入狂暴模式", + "类型": "宠物道具", + "道具图片": "res://assets/道具图片/狂暴药水.webp" + }, + "援军令牌": { + "花费": 100, + "描述": "血量低于某个值,召唤一堆宠物仆从帮助你战斗", + "类型": "宠物道具", + "道具图片": "res://assets/道具图片/援军令牌.webp" + }, + "金刚图腾": { + "花费": 100, + "描述": "更改宠物元素为金元素", + "类型": "宠物道具", + "道具图片": "res://assets/道具图片/金元素图腾.webp" + }, + "灵木图腾": { + "花费": 100, + "描述": "更改宠物元素为木元素", + "类型": "宠物道具", + "道具图片": "res://assets/道具图片/木元素图腾.webp" + }, + "潮汐图腾": { + "花费": 100, + "描述": "更改宠物元素为水元素", + "类型": "宠物道具", + "道具图片": "res://assets/道具图片/水元素图腾.webp" + }, + "烈焰图腾": { + "花费": 100, + "描述": "更改宠物元素为火元素", + "类型": "宠物道具", + "道具图片": "res://assets/道具图片/火元素图腾.webp" + }, + "敦岩图腾": { + "花费": 100, + "描述": "更改宠物元素为土元素", + "类型": "宠物道具", + "道具图片": "res://assets/道具图片/土元素图腾.webp" + }, + "小额经验卡": { + "花费": 100, + "描述": "让玩家立即获得500经验值", + "类型": "农场道具", + "道具图片": "res://assets/道具图片/小额经验卡.webp" + }, + "小额金币卡": { + "花费": 100, + "描述": "让玩家立即获得500金币", + "类型": "农场道具", + "道具图片": "res://assets/道具图片/小额金币卡.webp" + } +}, +{ + "_id": { + "$oid": "687cf59b8e77ba00a7414bab" + }, + "updated_at": { + "$date": "2025-07-20T22:13:38.521Z" + }, + "config_type":"宠物系统配置", + "烈焰鸟": { + "pet_name": "树萌芽の烈焰鸟", + "can_purchase": true, + "cost": 1000, + "pet_image": "res://Scene/NewPet/PetType/flying_bird.tscn", + "pet_id": "0001", + "pet_type": "烈焰鸟", + "pet_level": 1, + "pet_experience": 500, + "pet_temperament": "勇猛", + "pet_birthday": "2023-03-15", + "pet_hobby": "喜欢战斗和烈火", + "pet_introduction": "我爱吃虫子", + "max_health": 300, + "enable_health_regen": true, + "health_regen": 2, + "enable_shield_regen": true, + "max_shield": 150, + "shield_regen": 1.5, + "max_armor": 120, + "base_attack_damage": 40, + "crit_rate": 0.15, + "crit_damage": 2, + "armor_penetration": 10, + "enable_multi_projectile_skill": true, + "multi_projectile_delay": 2, + "enable_berserker_skill": true, + "berserker_bonus": 1.8, + "berserker_duration": 6, + "enable_self_destruct_skill": false, + "enable_summon_pet_skill": false, + "enable_death_respawn_skill": true, + "respawn_health_percentage": 0.4, + "move_speed": 180, + "dodge_rate": 0.08, + "element_type": "FIRE", + "element_damage_bonus": 75, + "left_weapon": "钻石剑", + "right_weapon": "钻石剑" + }, + "大蓝虫": { + "pet_name": "树萌芽の大蓝虫", + "can_purchase": true, + "cost": 2000, + "pet_image": "res://Scene/NewPet/PetType/big_beetle.tscn", + "pet_id": "0002", + "pet_type": "大蓝虫", + "pet_level": 1, + "pet_experience": 0, + "pet_temperament": "冷静", + "pet_birthday": "2023-06-20", + "pet_hobby": "喜欢和小蓝虫一起玩", + "pet_introduction": "我是大蓝虫,不是大懒虫!", + "max_health": 500, + "enable_health_regen": true, + "health_regen": 1.2, + "enable_shield_regen": true, + "max_shield": 200, + "shield_regen": 2.5, + "max_armor": 80, + "base_attack_damage": 35, + "crit_rate": 0.12, + "crit_damage": 1.8, + "armor_penetration": 15, + "enable_multi_projectile_skill": true, + "multi_projectile_delay": 1.5, + "enable_berserker_skill": false, + "enable_self_destruct_skill": false, + "enable_summon_pet_skill": true, + "summon_count": 2, + "summon_scale": 0.15, + "enable_death_respawn_skill": false, + "move_speed": 120, + "dodge_rate": 0.12, + "element_type": "WATER", + "element_damage_bonus": 100, + "left_weapon": "钻石剑", + "right_weapon": "钻石剑" + }, + "小蓝虫": { + "pet_name": "树萌芽の小蓝虫", + "can_purchase": true, + "cost": 800, + "pet_image": "res://Scene/NewPet/PetType/small_beetle.tscn", + "pet_id": "0002", + "pet_type": "小蓝虫", + "pet_level": 1, + "pet_experience": 0, + "pet_temperament": "冷静", + "pet_birthday": "2023-06-20", + "pet_hobby": "喜欢和大蓝虫玩", + "pet_introduction": "我是小蓝虫,不是小懒虫!", + "max_health": 300, + "enable_health_regen": true, + "health_regen": 1.2, + "enable_shield_regen": true, + "max_shield": 200, + "shield_regen": 2.5, + "max_armor": 80, + "base_attack_damage": 35, + "crit_rate": 0.12, + "crit_damage": 1.8, + "armor_penetration": 15, + "enable_multi_projectile_skill": true, + "multi_projectile_delay": 1.5, + "enable_berserker_skill": false, + "enable_self_destruct_skill": false, + "enable_summon_pet_skill": true, + "summon_count": 2, + "summon_scale": 0.15, + "enable_death_respawn_skill": false, + "move_speed": 120, + "dodge_rate": 0.12, + "element_type": "WATER", + "element_damage_bonus": 100, + "left_weapon": "钻石剑", + "right_weapon": "钻石剑" + }, + "小蓝": { + "pet_name": "树萌芽の小蓝", + "can_purchase": true, + "cost": 1500, + "pet_image": "res://Scene/NewPet/PetType/small_blue.tscn", + "pet_id": "0002", + "pet_type": "小蓝", + "pet_level": 1, + "pet_experience": 0, + "pet_temperament": "冷静", + "pet_birthday": "2023-06-20", + "pet_hobby": "喜欢和小黄一起玩", + "pet_introduction": "我是小黄!", + "max_health": 120, + "enable_health_regen": true, + "health_regen": 1.2, + "enable_shield_regen": true, + "max_shield": 200, + "shield_regen": 2.5, + "max_armor": 80, + "base_attack_damage": 35, + "crit_rate": 0.12, + "crit_damage": 1.8, + "armor_penetration": 15, + "enable_multi_projectile_skill": true, + "multi_projectile_delay": 1.5, + "enable_berserker_skill": false, + "enable_self_destruct_skill": false, + "enable_summon_pet_skill": true, + "summon_count": 2, + "summon_scale": 0.15, + "enable_death_respawn_skill": false, + "move_speed": 120, + "dodge_rate": 0.12, + "element_type": "WATER", + "element_damage_bonus": 100, + "left_weapon": "钻石剑", + "right_weapon": "钻石剑" + } +}, +{ + "_id": { + "$oid": "687cfb3d8e77ba00a7414bac" + }, + "config_type":"作物系统配置", + "测试作物": { + "作物名称": "0号作物", + "成熟物名称": "0号作物", + "花费": 1, + "生长时间": 1, + "收益": 10000, + "品质": "普通", + "描述": "仅供测试使用的特殊作物", + "耐候性": 10, + "等级": 1, + "经验": 999, + "能否购买": false + }, + "野草1": { + "作物名称": "野草1", + "成熟物名称": null, + "花费": 1, + "生长时间": 1, + "收益": 0, + "品质": "普通", + "描述": "野草", + "耐候性": 99, + "等级": 1, + "经验": 0, + "能否购买": false, + "是否杂草": true + }, + "野草2": { + "作物名称": "野草2", + "成熟物名称": null, + "花费": 0, + "生长时间": 1, + "收益": 0, + "品质": "普通", + "描述": "野草", + "耐候性": 99, + "等级": 1, + "经验": 0, + "能否购买": false, + "是否杂草": true + }, + "杂草1": { + "作物名称": "杂草1", + "成熟物名称": null, + "花费": 0, + "生长时间": 1, + "收益": 0, + "品质": "普通", + "描述": "杂草", + "耐候性": 99, + "等级": 1, + "经验": 0, + "能否购买": false, + "是否杂草": true + }, + "杂草2": { + "作物名称": "杂草2", + "成熟物名称": null, + "花费": 0, + "生长时间": 1, + "收益": 0, + "品质": "普通", + "描述": "野草", + "耐候性": 99, + "等级": 1, + "经验": 0, + "能否购买": false, + "是否杂草": true + }, + "小麦": { + "作物名称": "小麦", + "成熟物名称": "小麦", + "花费": 50, + "生长时间": 300, + "收益": 75, + "品质": "普通", + "描述": "最基础的粮食作物,生长快速,适合新手练习", + "耐候性": 8, + "等级": 1, + "经验": 8, + "能否购买": true + }, + "胡萝卜": { + "作物名称": "胡萝卜", + "成熟物名称": "胡萝卜", + "花费": 40, + "生长时间": 240, + "收益": 65, + "品质": "普通", + "描述": "营养丰富的根茎类蔬菜,容易种植", + "耐候性": 9, + "等级": 1, + "经验": 7, + "能否购买": true, + "喂养效果": { + "移动速度": 5, + "闪避率": 2 + } + }, + "土豆": { + "作物名称": "土豆", + "成熟物名称": "马铃薯", + "花费": 60, + "生长时间": 480, + "收益": 95, + "品质": "普通", + "描述": "耐寒的块茎作物,产量稳定", + "耐候性": 12, + "等级": 1, + "经验": 10, + "能否购买": true + }, + "稻谷": { + "作物名称": "稻谷", + "成熟物名称": "稻谷", + "花费": 70, + "生长时间": 600, + "收益": 110, + "品质": "普通", + "描述": "重要的主粮作物,需要充足水分", + "耐候性": 7, + "等级": 1, + "经验": 12, + "能否购买": true + }, + "大蒜": { + "作物名称": "大蒜", + "成熟物名称": "大蒜", + "花费": 50, + "生长时间": 550, + "收益": 100, + "品质": "普通", + "描述": "常用调味作物,具有抗菌功效,种植门槛低", + "耐候性": 10, + "等级": 1, + "经验": 8, + "能否购买": true + }, + "生菜": { + "作物名称": "生菜", + "成熟物名称": "生菜", + "花费": 45, + "生长时间": 650, + "收益": 120, + "品质": "普通", + "描述": "速生叶菜,适合沙拉生食,需保持土壤湿润", + "耐候性": 17, + "等级": 1, + "经验": 9, + "能否购买": true + }, + "石榴": { + "作物名称": "石榴", + "成熟物名称": "石榴", + "花费": 45, + "生长时间": 650, + "收益": 120, + "品质": "普通", + "描述": "速生叶菜,适合沙拉生食,需保持土壤湿润", + "耐候性": 17, + "等级": 1, + "经验": 9, + "能否购买": true + }, + "辣椒": { + "作物名称": "辣椒", + "成熟物名称": "辣椒", + "花费": 45, + "生长时间": 650, + "收益": 120, + "品质": "普通", + "描述": "速生叶菜,适合沙拉生食,需保持土壤湿润", + "耐候性": 17, + "等级": 1, + "经验": 9, + "能否购买": true + }, + "番茄": { + "作物名称": "番茄", + "成熟物名称": "番茄", + "花费": 90, + "生长时间": 720, + "收益": 140, + "品质": "普通", + "描述": "多汁的果实,市场需求大", + "耐候性": 8, + "等级": 2, + "经验": 16, + "能否购买": true + }, + "洋葱": { + "作物名称": "洋葱", + "成熟物名称": "洋葱", + "花费": 75, + "生长时间": 840, + "收益": 125, + "品质": "普通", + "描述": "调味蔬菜,储存时间长", + "耐候性": 11, + "等级": 2, + "经验": 14, + "能否购买": true + }, + "大豆": { + "作物名称": "大豆", + "成熟物名称": "大豆", + "花费": 85, + "生长时间": 1080, + "收益": 145, + "品质": "普通", + "描述": "富含蛋白质的豆类作物", + "耐候性": 9, + "等级": 2, + "经验": 18, + "能否购买": true + }, + "豌豆": { + "作物名称": "豌豆", + "成熟物名称": "豌豆", + "花费": 65, + "生长时间": 720, + "收益": 110, + "品质": "普通", + "描述": "嫩绿的豆荚蔬菜", + "耐候性": 10, + "等级": 2, + "经验": 15, + "能否购买": true + }, + "黄瓜": { + "作物名称": "黄瓜", + "成熟物名称": "黄瓜", + "花费": 85, + "生长时间": 1200, + "收益": 150, + "品质": "普通", + "描述": "清脆爽口的瓜类", + "耐候性": 8, + "等级": 2, + "经验": 18, + "能否购买": true + }, + "大白菜": { + "作物名称": "大白菜", + "成熟物名称": "大白菜", + "花费": 70, + "生长时间": 900, + "收益": 120, + "品质": "普通", + "描述": "北方冬季的主要蔬菜", + "耐候性": 15, + "等级": 2, + "经验": 16, + "能否购买": true + }, + "葡萄": { + "作物名称": "葡萄", + "成熟物名称": "葡萄", + "花费": 200, + "生长时间": 2700, + "收益": 340, + "品质": "普通", + "描述": "用于酿酒的珍贵果实", + "耐候性": 9, + "等级": 4, + "经验": 35, + "能否购买": true + }, + "南瓜": { + "作物名称": "南瓜", + "成熟物名称": "南瓜", + "花费": 180, + "生长时间": 3600, + "收益": 320, + "品质": "普通", + "描述": "大型瓜果,节庆装饰的首选", + "耐候性": 12, + "等级": 4, + "经验": 38, + "能否购买": true + }, + "茄子": { + "作物名称": "茄子", + "成熟物名称": "茄子", + "花费": 190, + "生长时间": 2400, + "收益": 310, + "品质": "普通", + "描述": "紫色的营养蔬菜", + "耐候性": 10, + "等级": 4, + "经验": 32, + "能否购买": true + }, + "玉米": { + "作物名称": "玉米", + "成熟物名称": "玉米", + "花费": 80, + "生长时间": 900, + "收益": 130, + "品质": "优良", + "描述": "高产的谷物作物,营养价值高", + "耐候性": 10, + "等级": 2, + "经验": 15, + "能否购买": true + }, + "葫芦": { + "作物名称": "葫芦", + "成熟物名称": "葫芦", + "花费": 85, + "生长时间": 1080, + "收益": 145, + "品质": "优良", + "描述": "富含蛋白质的豆类作物", + "耐候性": 9, + "等级": 2, + "经验": 18, + "能否购买": true + }, + "柠檬": { + "作物名称": "柠檬树", + "成熟物名称": "柠檬", + "花费": 85, + "生长时间": 1080, + "收益": 145, + "品质": "优良", + "描述": "富含蛋白质的豆类作物", + "耐候性": 9, + "等级": 2, + "经验": 18, + "能否购买": true + }, + "枇杷": { + "作物名称": "枇杷树", + "成熟物名称": "枇杷", + "花费": 85, + "生长时间": 1080, + "收益": 145, + "品质": "优良", + "描述": "富含蛋白质的豆类作物", + "耐候性": 9, + "等级": 2, + "经验": 18, + "能否购买": true + }, + "山楂": { + "作物名称": "山楂树", + "成熟物名称": "山楂", + "花费": 85, + "生长时间": 1080, + "收益": 145, + "品质": "优良", + "描述": "富含蛋白质的豆类作物", + "耐候性": 9, + "等级": 2, + "经验": 18, + "能否购买": true + }, + "仙人掌": { + "作物名称": "仙人掌", + "成熟物名称": "仙人掌", + "花费": 85, + "生长时间": 1080, + "收益": 145, + "品质": "优良", + "描述": "富含蛋白质的豆类作物", + "耐候性": 9, + "等级": 2, + "经验": 18, + "能否购买": true + }, + "椰子": { + "作物名称": "椰子树", + "成熟物名称": "椰子", + "花费": 85, + "生长时间": 1080, + "收益": 145, + "品质": "优良", + "描述": "富含蛋白质的豆类作物", + "耐候性": 9, + "等级": 2, + "经验": 18, + "能否购买": true + }, + "西瓜": { + "作物名称": "西瓜", + "成熟物名称": "西瓜", + "花费": 250, + "生长时间": 4800, + "收益": 450, + "品质": "优良", + "描述": "夏日消暑的大型水果", + "耐候性": 6, + "等级": 5, + "经验": 50, + "能否购买": true + }, + "甘蔗": { + "作物名称": "甘蔗", + "成熟物名称": "甘蔗", + "花费": 280, + "生长时间": 5400, + "收益": 500, + "品质": "优良", + "描述": "制糖的重要原料", + "耐候性": 5, + "等级": 5, + "经验": 55, + "能否购买": true + }, + "甜菜": { + "作物名称": "甜菜", + "成熟物名称": "甜菜", + "花费": 240, + "生长时间": 4200, + "收益": 420, + "品质": "优良", + "描述": "制糖的另一种选择", + "耐候性": 11, + "等级": 5, + "经验": 45, + "能否购买": true + }, + "郁金香": { + "作物名称": "郁金香", + "成熟物名称": "郁金香", + "花费": 280, + "生长时间": 5100, + "收益": 510, + "品质": "优良", + "描述": "美丽的观赏植物", + "耐候性": 10, + "等级": 5, + "经验": 50, + "能否购买": true + }, + "花椰菜": { + "作物名称": "花椰菜", + "成熟物名称": "花椰菜", + "花费": 110, + "生长时间": 1320, + "收益": 185, + "品质": "优良", + "描述": "营养丰富的十字花科蔬菜", + "耐候性": 8, + "等级": 3, + "经验": 20, + "能否购买": true + }, + "柿子": { + "作物名称": "柿子树", + "成熟物名称": "柿子", + "花费": 140, + "生长时间": 1800, + "收益": 230, + "品质": "优良", + "描述": "秋季成熟的甜美果实", + "耐候性": 10, + "等级": 3, + "经验": 25, + "能否购买": true + }, + "梨子": { + "作物名称": "梨树", + "成熟物名称": "梨子", + "花费": 210, + "生长时间": 2820, + "收益": 350, + "品质": "优良", + "描述": "春季开花的落叶果树", + "耐候性": 10, + "等级": 3, + "经验": 25, + "能否购买": true + }, + "桃子": { + "作物名称": "桃树", + "成熟物名称": "桃子", + "花费": 220, + "生长时间": 2940, + "收益": 360, + "品质": "优良", + "描述": "粉色花海中的果实树种", + "耐候性": 9, + "等级": 3, + "经验": 25, + "能否购买": true, + "作物ID": "作物35", + "喂养效果": { + "经验": 10, + "生命值": 3, + "攻击力": 2, + "移动速度": 1, + "亲密度": 5 + } + }, + "荔枝": { + "作物名称": "荔枝树", + "成熟物名称": "荔枝", + "花费": 230, + "生长时间": 3060, + "收益": 380, + "品质": "优良", + "描述": "亚热带珍稀水果,果肉如凝脂", + "耐候性": 8, + "等级": 3, + "经验": 26, + "能否购买": true + }, + "树莓": { + "作物名称": "树莓", + "成熟物名称": "树莓", + "花费": 140, + "生长时间": 2700, + "收益": 240, + "品质": "优良", + "描述": "小巧精致的浆果", + "耐候性": 9, + "等级": 3, + "经验": 30, + "能否购买": true + }, + "鱼腥草": { + "作物名称": "鱼腥草", + "成熟物名称": "鱼腥草", + "花费": 300, + "生长时间": 7200, + "收益": 600, + "品质": "优良", + "描述": "具有特殊药用价值的野菜", + "耐候性": 18, + "等级": 6, + "经验": 90, + "能否购买": true + }, + "芦笋": { + "作物名称": "芦笋", + "成熟物名称": "芦笋", + "花费": 220, + "生长时间": 3000, + "收益": 370, + "品质": "优良", + "描述": "高档蔬菜,营养价值极高", + "耐候性": 8, + "等级": 4, + "经验": 40, + "能否购买": true + }, + "苹果": { + "作物名称": "苹果树", + "成熟物名称": "苹果", + "花费": 450, + "生长时间": 9600, + "收益": 820, + "品质": "优良", + "描述": "结果丰富的果树", + "耐候性": 14, + "等级": 7, + "经验": 110, + "能否购买": true, + "喂养效果": { + "生命值": 5, + "护甲值": 2 + } + }, + "橘子": { + "作物名称": "橘子树", + "成熟物名称": "橘子", + "花费": 480, + "生长时间": 10200, + "收益": 850, + "品质": "优良", + "描述": "维生素C丰富的果树", + "耐候性": 12, + "等级": 7, + "经验": 115, + "能否购买": true + }, + "香蕉": { + "作物名称": "香蕉树", + "成熟物名称": "香蕉", + "花费": 420, + "生长时间": 8400, + "收益": 780, + "品质": "优良", + "描述": "热带水果之王", + "耐候性": 5, + "等级": 7, + "经验": 100, + "能否购买": true + }, + "牵牛花": { + "作物名称": "牵牛花", + "成熟物名称": "牵牛花", + "花费": 85, + "生长时间": 1080, + "收益": 145, + "品质": "稀有", + "描述": "富含蛋白质的豆类作物", + "耐候性": 9, + "等级": 2, + "经验": 18, + "能否购买": true + }, + "百合花": { + "作物名称": "百合花", + "成熟物名称": "百合花", + "花费": 260, + "生长时间": 4800, + "收益": 480, + "品质": "稀有", + "描述": "美丽的观赏植物", + "耐候性": 10, + "等级": 5, + "经验": 50, + "能否购买": true + }, + "山葵": { + "作物名称": "山葵", + "成熟物名称": "山葵", + "花费": 500, + "生长时间": 10800, + "收益": 1000, + "品质": "稀有", + "描述": "日式料理的珍贵调料", + "耐候性": 22, + "等级": 7, + "经验": 150, + "能否购买": true + }, + "草莓": { + "作物名称": "草莓", + "成熟物名称": "草莓", + "花费": 120, + "生长时间": 1440, + "收益": 200, + "品质": "稀有", + "描述": "甜美的浆果,深受喜爱", + "耐候性": 6, + "等级": 3, + "经验": 22, + "能否购买": true, + "喂养效果": { + "经验": 8, + "暴击率": 3, + "亲密度": 10 + } + }, + "蓝莓": { + "作物名称": "蓝莓", + "成熟物名称": "蓝莓", + "花费": 160, + "生长时间": 2100, + "收益": 260, + "品质": "稀有", + "描述": "抗氧化的超级食物", + "耐候性": 7, + "等级": 3, + "经验": 28, + "能否购买": true + }, + "栀子花": { + "作物名称": "栀子花", + "成熟物名称": "栀子花", + "花费": 180, + "生长时间": 2400, + "收益": 300, + "品质": "稀有", + "描述": "美丽的观赏植物", + "耐候性": 10, + "等级": 3, + "经验": 25, + "能否购买": true + }, + "玫瑰花": { + "作物名称": "玫瑰花", + "成熟物名称": "玫瑰花", + "花费": 190, + "生长时间": 2520, + "收益": 310, + "品质": "稀有", + "描述": "美丽的观赏植物", + "耐候性": 10, + "等级": 3, + "经验": 25, + "能否购买": true + }, + "菠萝": { + "作物名称": "菠萝", + "成熟物名称": "菠萝", + "花费": 200, + "生长时间": 2700, + "收益": 340, + "品质": "稀有", + "描述": "热带水果之王", + "耐候性": 10, + "等级": 3, + "经验": 25, + "能否购买": true + }, + "芒果": { + "作物名称": "芒果树", + "成熟物名称": "芒果", + "花费": 240, + "生长时间": 3180, + "收益": 400, + "品质": "稀有", + "描述": "热带阳光孕育的香甜果实", + "耐候性": 10, + "等级": 3, + "经验": 27, + "能否购买": true + }, + "咖啡豆": { + "作物名称": "咖啡豆", + "成熟物名称": "咖啡豆", + "花费": 200, + "生长时间": 2500, + "收益": 340, + "品质": "稀有", + "描述": "热带经济作物,果实经烘焙后可制成饮品,需温暖气候", + "耐候性": 8, + "等级": 4, + "经验": 35, + "能否购买": true + }, + "可可豆": { + "作物名称": "可可树", + "成熟物名称": "可可果", + "花费": 200, + "生长时间": 2500, + "收益": 340, + "品质": "稀有", + "描述": "制作巧克力的原料,需高温高湿环境,经济价值较高", + "耐候性": 10, + "等级": 4, + "经验": 36, + "能否购买": true + }, + "向日葵": { + "作物名称": "向日葵", + "成熟物名称": "向日葵", + "花费": 160, + "生长时间": 3600, + "收益": 280, + "品质": "稀有", + "描述": "向阳而生的美丽花朵", + "耐候性": 13, + "等级": 4, + "经验": 35, + "能否购买": true + }, + "哈密瓜": { + "作物名称": "哈密瓜", + "成熟物名称": "哈密瓜", + "花费": 380, + "生长时间": 7800, + "收益": 700, + "品质": "稀有", + "描述": "甘甜的高级水果", + "耐候性": 8, + "等级": 6, + "经验": 85, + "能否购买": true + }, + "藏红花": { + "作物名称": "藏红花", + "成熟物名称": "藏红花", + "花费": 400, + "生长时间": 7000, + "收益": 710, + "品质": "稀有", + "描述": "世界上最昂贵的香料之一,需精心照料,花朵可提取柱头", + "耐候性": 9, + "等级": 6, + "经验": 80, + "能否购买": true + }, + "龙果": { + "作物名称": "火龙果", + "成熟物名称": "火龙果", + "花费": 800, + "生长时间": 14400, + "收益": 1500, + "品质": "稀有", + "描述": "传说中的神秘果实,蕴含强大能量", + "耐候性": 25, + "等级": 8, + "经验": 200, + "能否购买": true + }, + "杨桃": { + "作物名称": "杨桃", + "成熟物名称": "杨桃", + "花费": 85, + "生长时间": 1080, + "收益": 145, + "品质": "史诗", + "描述": "富含蛋白质的豆类作物", + "耐候性": 9, + "等级": 2, + "经验": 18, + "能否购买": true + }, + "香草": { + "作物名称": "香草", + "成熟物名称": "香草", + "花费": 300, + "生长时间": 6000, + "收益": 550, + "品质": "史诗", + "描述": "珍贵的调料植物", + "耐候性": 7, + "等级": 5, + "经验": 60, + "能否购买": true + }, + "康乃馨": { + "作物名称": "康乃馨", + "成熟物名称": "康乃馨", + "花费": 270, + "生长时间": 5100, + "收益": 510, + "品质": "史诗", + "描述": "美丽的观赏植物", + "耐候性": 10, + "等级": 5, + "经验": 50, + "能否购买": true + }, + "藏羚羊草": { + "作物名称": "藏羚羊草", + "成熟物名称": "藏羚羊草", + "花费": 80, + "生长时间": 200, + "收益": 200, + "品质": "史诗", + "描述": "高原珍稀草种,用于生态修复,对土壤要求苛刻但市场需求大", + "耐候性": 15, + "等级": 5, + "经验": 20, + "能否购买": true + }, + "迷迭香": { + "作物名称": "迷迭香", + "成熟物名称": "迷迭香", + "花费": 100, + "生长时间": 1680, + "收益": 210, + "品质": "史诗", + "描述": "芳香型草本植物,可用于烹饪和精油提取,耐旱易存活", + "耐候性": 12, + "等级": 3, + "经验": 22, + "能否购买": true + }, + "蕨菜": { + "作物名称": "蕨菜", + "成熟物名称": "蕨菜", + "花费": 180, + "生长时间": 2400, + "收益": 300, + "品质": "史诗", + "描述": "野生山菜,口感独特", + "耐候性": 16, + "等级": 4, + "经验": 35, + "能否购买": true + }, + "人参": { + "作物名称": "人参", + "成熟物名称": "人参", + "花费": 400, + "生长时间": 7200, + "收益": 720, + "品质": "史诗", + "描述": "珍贵的药用植物,需要耐心培养", + "耐候性": 15, + "等级": 6, + "经验": 80, + "能否购买": true + }, + "富贵竹": { + "作物名称": "富贵竹", + "成熟物名称": "富贵竹", + "花费": 350, + "生长时间": 6600, + "收益": 650, + "品质": "史诗", + "描述": "寓意吉祥的观赏植物", + "耐候性": 12, + "等级": 6, + "经验": 75, + "能否购买": true + }, + "芦荟": { + "作物名称": "芦荟", + "成熟物名称": "芦荟", + "花费": 320, + "生长时间": 6000, + "收益": 600, + "品质": "史诗", + "描述": "具有药用价值的多肉植物", + "耐候性": 18, + "等级": 6, + "经验": 70, + "能否购买": true + }, + "金橘": { + "作物名称": "金橘子树", + "成熟物名称": "金橘", + "花费": 500, + "生长时间": 10800, + "收益": 900, + "品质": "史诗", + "描述": "金黄色的珍贵柑橘", + "耐候性": 10, + "等级": 7, + "经验": 120, + "能否购买": true + }, + "松露": { + "作物名称": "松露", + "成熟物名称": "松露", + "花费": 1000, + "生长时间": 18000, + "收益": 2000, + "品质": "史诗", + "描述": "地下的黑金,顶级料理的灵魂", + "耐候性": 20, + "等级": 8, + "经验": 250, + "能否购买": true + }, + "冬虫夏草": { + "作物名称": "冬虫夏草", + "成熟物名称": "冬虫夏草", + "花费": 600, + "生长时间": 12000, + "收益": 1200, + "品质": "史诗", + "描述": "稀世珍宝,药王之称", + "耐候性": 30, + "等级": 7, + "经验": 180, + "能否购买": true + }, + "糖果树": { + "作物名称": "糖果树", + "成熟物名称": null, + "花费": 250, + "生长时间": 3300, + "收益": 420, + "品质": "传奇", + "描述": "传说中结出彩色糖果的魔法树", + "耐候性": 7, + "等级": 3, + "经验": 25, + "能否购买": false + }, + "面包树": { + "作物名称": "面包树", + "成熟物名称": null, + "花费": 260, + "生长时间": 3420, + "收益": 440, + "品质": "传奇", + "描述": "热带地区的淀粉质主食树种", + "耐候性": 9, + "等级": 3, + "经验": 25, + "能否购买": false + }, + "幸运草": { + "作物名称": "幸运草", + "成熟物名称": null, + "花费": 150, + "生长时间": 1560, + "收益": 220, + "品质": "传奇", + "描述": "四片叶子的幸运象征植物", + "耐候性": 6, + "等级": 3, + "经验": 20, + "能否购买": false + }, + "幸运花": { + "作物名称": "幸运花", + "成熟物名称": null, + "花费": 170, + "生长时间": 1680, + "收益": 240, + "品质": "传奇", + "描述": "绽放时带来好运的神秘花卉", + "耐候性": 7, + "等级": 3, + "经验": 22, + "能否购买": false + }, + "摇钱树": { + "作物名称": "摇钱树", + "成熟物名称": null, + "花费": 300, + "生长时间": 3600, + "收益": 500, + "品质": "传奇", + "描述": "传说中能结出金币的神树", + "耐候性": 10, + "等级": 3, + "经验": 30, + "能否购买": false + }, + "月光草": { + "作物名称": "月光草", + "成熟物名称": "月光草", + "花费": 700, + "生长时间": 14500, + "收益": 1500, + "品质": "传奇", + "描述": "夜间开花的神秘植物,花瓣在月光下散发荧光,传说具有许愿功效", + "耐候性": 20, + "等级": 8, + "经验": 200, + "能否购买": false + }, + "凤凰木": { + "作物名称": "凤凰树", + "成熟物名称": "凤凰木", + "花费": 650, + "生长时间": 12600, + "收益": 1400, + "品质": "传奇", + "描述": "传说中凤凰栖息的神树,开花时如火焰般绚烂,木材可驱邪避灾", + "耐候性": 25, + "等级": 8, + "经验": 210, + "能否购买": false + }, + "杂交树1": { + "作物名称": "0号杂交树", + "成熟物名称": null, + "花费": 1200, + "生长时间": 21600, + "收益": 2500, + "品质": "传奇", + "描述": "初中生物书最后的幻想", + "耐候性": 35, + "等级": 9, + "经验": 300, + "能否购买": false + }, + "杂交树2": { + "作物名称": "1号杂交树", + "成熟物名称": null, + "花费": 1500, + "生长时间": 25200, + "收益": 3000, + "品质": "传奇", + "描述": "初中生物书最后的想象", + "耐候性": 40, + "等级": 10, + "经验": 400, + "能否购买": false + } +}, +{ + "_id": { + "$oid": "687e2f3f8e77ba00a7414bb0" + }, + "updated_at": { + "$date": "2025-07-21T20:23:07.118Z" + }, + "config_type":"注册玩家模版配置", + "经验值": 0, + "等级": 1, + "钱币": 6000, + "农场名称": "农场名称", + "玩家昵称": "玩家名称", + "玩家账号": "用户名", + "玩家密码": "密码", + "最后登录时间": "2025年07月20日17时19分16秒", + "总游玩时间": "0时0分0秒", + "注册时间": "2025年05月21日15时00分00秒", + "个人简介": "个人简介", + "农场土地": [ + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + } + ], + "种子仓库": [], + "作物仓库": [], + "道具背包": [], + "宠物背包": [], + "巡逻宠物": [], + "出战宠物": [], + "稻草人配置": { + "已拥有稻草人类型": [ + "稻草人1" + ], + "稻草人展示类型": "", + "稻草人昵称": "稻草人", + "稻草人说的话": { + "第一句话": { + "内容": "第一句话", + "颜色": "52dceeff" + }, + "第二句话": { + "内容": "第二句话", + "颜色": "80d5ffff" + }, + "第三句话": { + "内容": "第三句话", + "颜色": "ac52ffff" + }, + "第四句话": { + "内容": "第四句话", + "颜色": "f881ffff" + } + }, + "稻草人昵称颜色": "b38282ff" + }, + "智慧树配置": { + "距离上一次杀虫时间": 1753004237, + "距离上一次除草时间": 1753004237, + "智慧树显示的话": "", + "等级": 1, + "当前经验值": 0, + "最大经验值": 100, + "最大生命值": 100, + "当前生命值": 100, + "高度": 20 + }, + "签到历史": {}, + "在线礼包": { + "当前日期": "2025-07-20", + "今日在线时长": 0, + "已领取礼包": [], + "登录时间": 1753003043.7163484 + }, + "点赞系统": { + "今日剩余点赞次数": 10, + "点赞上次刷新时间": "2025-07-20", + "总点赞数": 0 + }, + "新手礼包": { + "已领取": false, + "领取时间": "2025-07-12 23:02:25" + }, + "体力系统": { + "当前体力值": 20, + "最大体力值": 20, + "上次刷新时间": "2025-07-20", + "上次恢复时间": 1753003043.7066433 + }, + "小卖部配置": { + "商品列表": [], + "格子数": 10 + }, + "游戏设置": { + "背景音乐音量": 1, + "天气显示": true + } +}, +{ + "_id": { + "$oid": "687e35078e77ba00a7414bb1" + }, + "updated_at": { + "$date": "2025-07-22T08:48:31.788Z" + }, + "config_type":"验证码系统配置" + +}, +{ + "_id": { + "$oid": "687e40128e77ba00a7414bb2" + }, + "updated_at": { + "$date": "2025-07-27T22:03:03.398Z" + }, + "config_type":"天气系统配置", + "服务器当前天气": "snow" +}] \ No newline at end of file diff --git a/Server/test/gameconfig/游戏小提示配置.jsonc b/Server/test/gameconfig/游戏小提示配置.jsonc new file mode 100644 index 0000000..d913ac2 --- /dev/null +++ b/Server/test/gameconfig/游戏小提示配置.jsonc @@ -0,0 +1,22 @@ +{ + "切换模式":"顺序",//可选,顺序,随机,倒序 + "切换速度":5, + "游戏小提示": + [ + "按住wsad可以移动游戏画面", + "使用鼠标滚轮来缩放游戏画面", + "移动端双指缩放游戏画面", + "不要一上来就花光你的初始资金", + "钱币是目前游戏唯一货币", + "每隔一小时体力值+1", + "不要忘记领取你的新手礼包!", + "记得使用一键截图来分享你的农场", + "新注册用户可享受三天10倍速作物生长", + "偷别人菜时不要忘了给别人浇水哦", + "你能分得清小麦和稻谷吗", + "凌晨刷新体力值", + "面板左上角有刷新按钮,可以刷新面板", + "小心偷菜被巡逻宠物发现", + "访问特殊农场来获得一些特殊的作物" + ] +} \ No newline at end of file diff --git a/Server/test/import_game_tips_config.py b/Server/test/import_game_tips_config.py new file mode 100644 index 0000000..3714826 --- /dev/null +++ b/Server/test/import_game_tips_config.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +游戏小提示配置数据导入脚本 +将游戏小提示配置数据导入到MongoDB数据库中 +""" + +import sys +import os + +# 添加当前目录到Python路径 +sys.path.append(os.path.dirname(os.path.abspath(__file__))) + +from SMYMongoDBAPI import SMYMongoDBAPI + +def import_game_tips_config(): + """导入游戏小提示配置数据到MongoDB""" + + # 游戏小提示配置数据 + game_tips_config = { + "切换模式": "顺序", # 可选:顺序,随机,倒序 + "切换速度": 5, + "游戏小提示": [ + "按住wsad可以移动游戏画面", + "使用鼠标滚轮来缩放游戏画面", + "移动端双指缩放游戏画面", + "不要一上来就花光你的初始资金", + "钱币是目前游戏唯一货币", + "每隔一小时体力值+1", + "不要忘记领取你的新手礼包!", + "记得使用一键截图来分享你的农场", + "新注册用户可享受三天10倍速作物生长", + "偷别人菜时不要忘了给别人浇水哦", + "你能分得清小麦和稻谷吗", + "凌晨刷新体力值", + "面板左上角有刷新按钮,可以刷新面板", + "小心偷菜被巡逻宠物发现", + "访问特殊农场来获得一些特殊的作物" + ] + } + + try: + # 创建MongoDB API实例 + mongo_api = SMYMongoDBAPI() + + # 连接到数据库 + if not mongo_api.connect(): + print("错误:无法连接到MongoDB数据库") + return False + + print("成功连接到MongoDB数据库") + + # 更新游戏小提示配置 + result = mongo_api.update_game_tips_config(game_tips_config) + + if result: + print("成功导入游戏小提示配置数据到MongoDB") + print(f"配置内容:") + print(f" 切换模式: {game_tips_config['切换模式']}") + print(f" 切换速度: {game_tips_config['切换速度']}") + print(f" 游戏小提示数量: {len(game_tips_config['游戏小提示'])}") + return True + else: + print("错误:导入游戏小提示配置数据失败") + return False + + except Exception as e: + print(f"导入过程中发生错误: {str(e)}") + return False + finally: + # 断开数据库连接 + if 'mongo_api' in locals(): + mongo_api.disconnect() + print("已断开MongoDB数据库连接") + +if __name__ == "__main__": + print("开始导入游戏小提示配置数据...") + success = import_game_tips_config() + + if success: + print("\n导入完成!") + else: + print("\n导入失败!") + sys.exit(1) \ No newline at end of file diff --git a/Server/test/item_config_debug.json b/Server/test/item_config_debug.json new file mode 100644 index 0000000..6209a5f --- /dev/null +++ b/Server/test/item_config_debug.json @@ -0,0 +1,132 @@ +{ + "type": "item_config_response", + "success": true, + "item_config": { + "精准采集-镰刀": { + "花费": 100, + "描述": "可以在收获作物时必定掉落该作物的种子", + "类型": "作物道具", + "道具图片": "res://assets/道具图片/紫水晶镰刀.webp" + }, + "时运-镰刀": { + "花费": 100, + "描述": "可以在收获作物时掉落更多的作物的收获物", + "类型": "作物道具", + "道具图片": "res://assets/道具图片/红宝石镰刀.webp" + }, + "农家肥": { + "花费": 100, + "描述": "(施肥道具)可以在30分钟内2倍速作物生长", + "类型": "作物道具", + "道具图片": "res://assets/道具图片/农家肥.webp" + }, + "金坷垃": { + "花费": 100, + "描述": "(施肥道具)可以在5分钟内5倍速作物的生长", + "类型": "作物道具", + "道具图片": "res://assets/道具图片/金坷垃2.webp" + }, + "水壶": { + "花费": 100, + "描述": "(浇水道具)直接让作物生长进度+1%", + "类型": "作物道具", + "道具图片": "res://assets/道具图片/铁质洒水壶.webp" + }, + "水桶": { + "花费": 100, + "描述": "(浇水道具)让作物生长进度+2%", + "类型": "作物道具", + "道具图片": "res://assets/道具图片/木质水桶2.webp" + }, + "杀虫剂": { + "花费": 100, + "描述": "杀虫,暂时没什么用", + "类型": "作物道具", + "道具图片": "res://assets/道具图片/杀虫剂.webp" + }, + "除草剂": { + "花费": 100, + "描述": "除草", + "类型": "作物道具", + "道具图片": "res://assets/道具图片/除草剂.webp" + }, + "生长素": { + "花费": 100, + "描述": "时运可以10分钟内3倍速作物生长,而且作物生长速度+3%", + "类型": "作物道具", + "道具图片": "res://assets/道具图片/生长素.webp" + }, + "铲子": { + "花费": 100, + "描述": "铲除作物", + "类型": "作物道具", + "道具图片": "res://assets/道具图片/附魔铁铲.webp" + }, + "不死图腾": { + "花费": 100, + "描述": "让宠物死亡复活一次", + "类型": "宠物道具", + "道具图片": "res://assets/道具图片/不死图腾.webp" + }, + "荆棘护甲": { + "花费": 100, + "描述": "宠物收到伤害时反弹伤害给敌人", + "类型": "宠物道具", + "道具图片": "res://assets/道具图片/荆棘护甲.webp" + }, + "狂暴药水": { + "花费": 100, + "描述": "宠物血量低于某个值,进入狂暴模式", + "类型": "宠物道具", + "道具图片": "res://assets/道具图片/狂暴药水.webp" + }, + "援军令牌": { + "花费": 100, + "描述": "血量低于某个值,召唤一堆宠物仆从帮助你战斗", + "类型": "宠物道具", + "道具图片": "res://assets/道具图片/援军令牌.webp" + }, + "金刚图腾": { + "花费": 100, + "描述": "更改宠物元素为金元素", + "类型": "宠物道具", + "道具图片": "res://assets/道具图片/金元素图腾.webp" + }, + "灵木图腾": { + "花费": 100, + "描述": "更改宠物元素为木元素", + "类型": "宠物道具", + "道具图片": "res://assets/道具图片/木元素图腾.webp" + }, + "潮汐图腾": { + "花费": 100, + "描述": "更改宠物元素为水元素", + "类型": "宠物道具", + "道具图片": "res://assets/道具图片/水元素图腾.webp" + }, + "烈焰图腾": { + "花费": 100, + "描述": "更改宠物元素为火元素", + "类型": "宠物道具", + "道具图片": "res://assets/道具图片/火元素图腾.webp" + }, + "敦岩图腾": { + "花费": 100, + "描述": "更改宠物元素为土元素", + "类型": "宠物道具", + "道具图片": "res://assets/道具图片/土元素图腾.webp" + }, + "小额经验卡": { + "花费": 100, + "描述": "让玩家立即获得500经验值", + "类型": "农场道具", + "道具图片": "res://assets/道具图片/小额经验卡.webp" + }, + "小额金币卡": { + "花费": 100, + "描述": "让玩家立即获得500金币", + "类型": "农场道具", + "道具图片": "res://assets/道具图片/小额金币卡.webp" + } + } +} \ No newline at end of file diff --git a/Server/test/monitor_special_farm.py b/Server/test/monitor_special_farm.py new file mode 100644 index 0000000..b75cded --- /dev/null +++ b/Server/test/monitor_special_farm.py @@ -0,0 +1,155 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +特殊农场系统性能监控脚本 +用于监控特殊农场系统的资源使用情况 +""" + +import psutil +import time +import threading +from datetime import datetime + +class SpecialFarmMonitor: + def __init__(self): + self.monitoring = False + self.monitor_thread = None + self.stats = { + 'cpu_usage': [], + 'memory_usage': [], + 'thread_count': [], + 'start_time': None + } + + def start_monitoring(self, duration=60): + """开始监控指定时间(秒)""" + if self.monitoring: + print("监控已在运行中") + return + + self.monitoring = True + self.stats['start_time'] = datetime.now() + + def monitor_loop(): + print(f"开始监控特殊农场系统性能 - {self.stats['start_time']}") + print(f"监控时长: {duration} 秒") + print("=" * 50) + + start_time = time.time() + while self.monitoring and (time.time() - start_time) < duration: + try: + # 获取当前进程信息 + process = psutil.Process() + + # CPU使用率 + cpu_percent = process.cpu_percent() + self.stats['cpu_usage'].append(cpu_percent) + + # 内存使用情况 + memory_info = process.memory_info() + memory_mb = memory_info.rss / 1024 / 1024 + self.stats['memory_usage'].append(memory_mb) + + # 线程数量 + thread_count = process.num_threads() + self.stats['thread_count'].append(thread_count) + + # 实时显示 + elapsed = int(time.time() - start_time) + print(f"\r[{elapsed:3d}s] CPU: {cpu_percent:5.1f}% | 内存: {memory_mb:6.1f}MB | 线程: {thread_count:2d}", end="", flush=True) + + time.sleep(1) + + except Exception as e: + print(f"\n监控出错: {str(e)}") + break + + self.monitoring = False + print("\n" + "=" * 50) + self._print_summary() + + self.monitor_thread = threading.Thread(target=monitor_loop, daemon=True) + self.monitor_thread.start() + + def stop_monitoring(self): + """停止监控""" + self.monitoring = False + if self.monitor_thread: + self.monitor_thread.join(timeout=2) + + def _print_summary(self): + """打印监控摘要""" + if not self.stats['cpu_usage']: + print("没有收集到监控数据") + return + + print("监控摘要:") + print(f"监控时间: {self.stats['start_time']} - {datetime.now()}") + print(f"数据点数: {len(self.stats['cpu_usage'])}") + + # CPU统计 + cpu_avg = sum(self.stats['cpu_usage']) / len(self.stats['cpu_usage']) + cpu_max = max(self.stats['cpu_usage']) + print(f"CPU使用率 - 平均: {cpu_avg:.1f}%, 最高: {cpu_max:.1f}%") + + # 内存统计 + mem_avg = sum(self.stats['memory_usage']) / len(self.stats['memory_usage']) + mem_max = max(self.stats['memory_usage']) + print(f"内存使用量 - 平均: {mem_avg:.1f}MB, 最高: {mem_max:.1f}MB") + + # 线程统计 + thread_avg = sum(self.stats['thread_count']) / len(self.stats['thread_count']) + thread_max = max(self.stats['thread_count']) + print(f"线程数量 - 平均: {thread_avg:.1f}, 最高: {thread_max}") + + # 性能评估 + print("\n性能评估:") + if cpu_avg < 1.0: + print("✓ CPU使用率很低,性能良好") + elif cpu_avg < 5.0: + print("✓ CPU使用率正常") + else: + print("⚠ CPU使用率较高,可能需要优化") + + if mem_avg < 50: + print("✓ 内存使用量很低") + elif mem_avg < 100: + print("✓ 内存使用量正常") + else: + print("⚠ 内存使用量较高") + + if thread_max <= 10: + print("✓ 线程数量合理") + else: + print("⚠ 线程数量较多,注意资源管理") + +def main(): + """主函数""" + print("特殊农场系统性能监控工具") + print("使用说明:") + print("1. 启动游戏服务器") + print("2. 运行此监控脚本") + print("3. 观察特殊农场系统的资源使用情况") + print() + + monitor = SpecialFarmMonitor() + + try: + # 监控60秒 + monitor.start_monitoring(60) + + # 等待监控完成 + while monitor.monitoring: + time.sleep(1) + + print("\n监控完成") + + except KeyboardInterrupt: + print("\n用户中断监控") + monitor.stop_monitoring() + except Exception as e: + print(f"\n监控过程中出错: {str(e)}") + monitor.stop_monitoring() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/Server/test/special_farm.log b/Server/test/special_farm.log new file mode 100644 index 0000000..e69de29 diff --git a/Server/test/test_client_data.py b/Server/test/test_client_data.py new file mode 100644 index 0000000..1916954 --- /dev/null +++ b/Server/test/test_client_data.py @@ -0,0 +1,147 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +测试发送给客户端的数据结构 +模拟服务器发送给客户端的数据格式 +""" + +import json +from SMYMongoDBAPI import SMYMongoDBAPI + +def test_client_data_format(): + """测试发送给客户端的数据格式""" + print("=== 测试客户端数据格式 ===") + + try: + # 初始化MongoDB API + mongo_api = SMYMongoDBAPI("test") + + if not mongo_api.is_connected(): + print("❌ MongoDB连接失败") + return + + print("✅ MongoDB连接成功") + + # 模拟_load_crop_data方法 + print("\n=== 测试作物数据 ===") + crop_data = mongo_api.get_crop_data_config() + + if crop_data: + # 模拟服务器发送的crop_data_message + crop_data_message = { + "type": "crop_data_response", + "success": True, + "crop_data": crop_data + } + + print(f"作物数据类型: {type(crop_data)}") + print(f"作物数据键数量: {len(crop_data)}") + + # 检查是否有config_type字段 + if 'config_type' in crop_data: + print(f"⚠️ 作物数据包含config_type: {crop_data['config_type']}") + else: + print("✅ 作物数据不包含config_type字段") + + # 检查前几个作物的数据结构 + crop_count = 0 + for crop_name, crop_info in crop_data.items(): + if crop_name not in ['_id', 'config_type'] and crop_count < 3: + print(f"\n作物 {crop_name}:") + print(f" 数据类型: {type(crop_info)}") + + if isinstance(crop_info, dict): + # 检查关键字段 + key_fields = ['能否购买', '品质', '等级', '作物名称'] + for key in key_fields: + if key in crop_info: + value = crop_info[key] + print(f" {key}: {value} (类型: {type(value)})") + + # 特别检查能否购买字段 + if key == '能否购买': + if isinstance(value, str): + print(f" ⚠️ '能否购买'字段是字符串,这会导致Godot报错!") + elif isinstance(value, bool): + print(f" ✅ '能否购买'字段是布尔值,正确") + elif isinstance(crop_info, str): + print(f" ⚠️ 整个作物数据是字符串: '{crop_info[:50]}...'") + print(f" 这会导致Godot调用.get()方法时报错!") + + crop_count += 1 + + # 保存作物数据到文件以便检查 + with open('crop_data_debug.json', 'w', encoding='utf-8') as f: + json.dump(crop_data_message, f, ensure_ascii=False, indent=2, default=str) + print(f"\n✅ 作物数据已保存到 crop_data_debug.json") + + # 测试道具数据 + print("\n=== 测试道具数据 ===") + item_config = mongo_api.get_item_config() + + if item_config: + # 模拟服务器发送的item_config_message + item_config_message = { + "type": "item_config_response", + "success": True, + "item_config": item_config + } + + print(f"道具数据类型: {type(item_config)}") + + # 检查是否有config_type字段 + if 'config_type' in item_config: + print(f"⚠️ 道具数据包含config_type: {item_config['config_type']}") + else: + print("✅ 道具数据不包含config_type字段") + + # 保存道具数据到文件 + with open('item_config_debug.json', 'w', encoding='utf-8') as f: + json.dump(item_config_message, f, ensure_ascii=False, indent=2, default=str) + print(f"✅ 道具数据已保存到 item_config_debug.json") + + # 检查JSON序列化后的数据 + print("\n=== 测试JSON序列化 ===") + if crop_data: + try: + # 模拟服务器发送数据时的JSON序列化过程 + json_str = json.dumps(crop_data_message, ensure_ascii=False, default=str) + + # 模拟客户端接收数据时的JSON反序列化过程 + received_data = json.loads(json_str) + + print("✅ JSON序列化/反序列化成功") + + # 检查反序列化后的数据结构 + received_crop_data = received_data.get('crop_data', {}) + + # 检查第一个作物的数据 + for crop_name, crop_info in received_crop_data.items(): + if crop_name not in ['_id', 'config_type']: + print(f"\n反序列化后的作物 {crop_name}:") + print(f" 数据类型: {type(crop_info)}") + + if isinstance(crop_info, dict): + if '能否购买' in crop_info: + value = crop_info['能否购买'] + print(f" 能否购买: {value} (类型: {type(value)})") + elif isinstance(crop_info, str): + print(f" ⚠️ 反序列化后变成字符串: '{crop_info[:50]}...'") + + break + + except Exception as e: + print(f"❌ JSON序列化/反序列化失败: {e}") + + except Exception as e: + print(f"❌ 测试过程中出错: {e}") + import traceback + traceback.print_exc() + + finally: + if 'mongo_api' in locals(): + mongo_api.disconnect() + print("\n✅ 数据库连接已关闭") + +if __name__ == "__main__": + test_client_data_format() \ No newline at end of file diff --git a/Server/test/test_complete_game_tips_system.py b/Server/test/test_complete_game_tips_system.py new file mode 100644 index 0000000..11b5e90 --- /dev/null +++ b/Server/test/test_complete_game_tips_system.py @@ -0,0 +1,208 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +完整的游戏小提示配置系统测试 +测试从数据库导入到服务端处理的完整流程 +""" + +import sys +import os +import socket +import json +import time +import threading + +# 添加当前目录到Python路径 +sys.path.append(os.path.dirname(os.path.abspath(__file__))) + +from SMYMongoDBAPI import SMYMongoDBAPI +from TCPGameServer import TCPGameServer + +def test_database_operations(): + """测试数据库操作""" + print("=== 测试数据库操作 ===\n") + + try: + mongo_api = SMYMongoDBAPI() + if not mongo_api.connect(): + print("❌ 无法连接到MongoDB数据库") + return False + + print("✓ 成功连接到MongoDB数据库") + + # 测试获取配置 + config = mongo_api.get_game_tips_config() + if config: + print("✓ 成功获取游戏小提示配置") + print(f" 切换模式: {config.get('切换模式', 'N/A')}") + print(f" 切换速度: {config.get('切换速度', 'N/A')}") + print(f" 小提示数量: {len(config.get('游戏小提示', []))}") + return True + else: + print("❌ 无法获取游戏小提示配置") + return False + + except Exception as e: + print(f"❌ 数据库测试失败: {str(e)}") + return False + finally: + if 'mongo_api' in locals(): + mongo_api.disconnect() + +def test_server_loading(): + """测试服务器加载配置""" + print("\n=== 测试服务器加载配置 ===\n") + + try: + server = TCPGameServer() + server.mongo_api = SMYMongoDBAPI() + + if not server.mongo_api.connect(): + print("❌ 服务器无法连接到MongoDB") + return False + + print("✓ 服务器成功连接到MongoDB") + + # 测试服务器加载配置 + config = server._load_game_tips_config() + if config: + print("✓ 服务器成功加载游戏小提示配置") + print(f" 切换模式: {config.get('切换模式', 'N/A')}") + print(f" 切换速度: {config.get('切换速度', 'N/A')}") + print(f" 小提示数量: {len(config.get('游戏小提示', []))}") + return True + else: + print("❌ 服务器无法加载游戏小提示配置") + return False + + except Exception as e: + print(f"❌ 服务器测试失败: {str(e)}") + return False + finally: + if 'server' in locals() and hasattr(server, 'mongo_api') and server.mongo_api: + server.mongo_api.disconnect() + +def test_client_server_communication(): + """测试客户端-服务端通信""" + print("\n=== 测试客户端-服务端通信 ===\n") + + # 启动服务器(在后台线程中) + server = None + server_thread = None + + try: + print("启动测试服务器...") + server = TCPGameServer() + + # 在后台线程中启动服务器 + def run_server(): + try: + server.start_server() + except Exception as e: + print(f"服务器启动失败: {e}") + + server_thread = threading.Thread(target=run_server, daemon=True) + server_thread.start() + + # 等待服务器启动 + time.sleep(2) + + # 创建客户端连接 + print("创建客户端连接...") + client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + client_socket.settimeout(5) + + try: + client_socket.connect(('localhost', 12345)) + print("✓ 客户端成功连接到服务器") + + # 发送游戏小提示配置请求 + request = { + "type": "request_game_tips_config" + } + + message = json.dumps(request, ensure_ascii=False) + client_socket.send(message.encode('utf-8')) + print("✓ 已发送游戏小提示配置请求") + + # 接收响应 + response_data = client_socket.recv(4096) + if response_data: + response = json.loads(response_data.decode('utf-8')) + print("✓ 收到服务器响应") + + if response.get("type") == "game_tips_config_response": + success = response.get("success", False) + if success: + config = response.get("game_tips_config", {}) + print("✓ 成功获取游戏小提示配置") + print(f" 切换模式: {config.get('切换模式', 'N/A')}") + print(f" 切换速度: {config.get('切换速度', 'N/A')}") + print(f" 小提示数量: {len(config.get('游戏小提示', []))}") + return True + else: + message = response.get("message", "未知错误") + print(f"❌ 服务器返回失败: {message}") + return False + else: + print(f"❌ 收到意外的响应类型: {response.get('type')}") + return False + else: + print("❌ 未收到服务器响应") + return False + + except socket.timeout: + print("❌ 客户端连接超时") + return False + except ConnectionRefusedError: + print("❌ 无法连接到服务器") + return False + finally: + client_socket.close() + + except Exception as e: + print(f"❌ 通信测试失败: {str(e)}") + return False + finally: + # 停止服务器 + if server: + try: + server.stop_server() + except: + pass + +def main(): + """主测试函数""" + print("开始完整的游戏小提示配置系统测试...\n") + + # 执行各项测试 + db_success = test_database_operations() + server_success = test_server_loading() + comm_success = test_client_server_communication() + + # 输出测试结果 + print("\n" + "="*50) + print("测试结果汇总") + print("="*50) + print(f"数据库操作测试: {'✓ 通过' if db_success else '❌ 失败'}") + print(f"服务器加载测试: {'✓ 通过' if server_success else '❌ 失败'}") + print(f"客户端通信测试: {'✓ 通过' if comm_success else '❌ 失败'}") + + if db_success and server_success and comm_success: + print("\n🎉 所有测试通过!游戏小提示配置系统完全正常工作。") + print("\n系统功能说明:") + print("1. ✓ 配置数据已成功导入MongoDB数据库") + print("2. ✓ 服务端能正确加载和处理配置数据") + print("3. ✓ 客户端能成功请求并接收配置数据") + print("4. ✓ 支持顺序、倒序、随机三种切换模式") + print("5. ✓ 支持自定义切换速度") + print("\n现在客户端可以从服务端获取游戏小提示配置,") + print("并根据配置的切换模式和速度显示小提示。") + return True + else: + print("\n❌ 部分测试失败,请检查系统配置。") + return False + +if __name__ == "__main__": + success = main() + sys.exit(0 if success else 1) \ No newline at end of file diff --git a/Server/test/test_database_documents.py b/Server/test/test_database_documents.py new file mode 100644 index 0000000..dbe5b89 --- /dev/null +++ b/Server/test/test_database_documents.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +数据库文档结构测试脚本 +用于检查MongoDB中gameconfig集合的文档结构 +""" + +import json +from SMYMongoDBAPI import SMYMongoDBAPI + +def test_database_documents(): + """测试数据库文档结构""" + print("=== 数据库文档结构测试 ===") + + try: + # 初始化MongoDB API + mongo_api = SMYMongoDBAPI("test") + + if not mongo_api.is_connected(): + print("❌ MongoDB连接失败") + return + + print("✅ MongoDB连接成功") + + # 获取gameconfig集合 + collection = mongo_api.get_collection("gameconfig") + + print("\n=== 检查gameconfig集合中的所有文档 ===") + + # 查找所有文档 + documents = list(collection.find({})) + + print(f"找到 {len(documents)} 个文档") + + for i, doc in enumerate(documents): + print(f"\n--- 文档 {i+1} ---") + print(f"_id: {doc.get('_id')}") + print(f"config_type: {doc.get('config_type', '未找到')}") + + # 检查是否有config_type字段 + if 'config_type' in doc: + print(f"⚠️ 发现config_type字段: {doc['config_type']}") + else: + print("✅ 没有config_type字段") + + # 显示文档的所有键 + print(f"文档键: {list(doc.keys())}") + + # 如果是作物配置,检查具体内容 + if doc.get('config_type') == '作物数据配置': + print("\n=== 作物数据配置详细检查 ===") + + # 检查作物数据结构 + for key, value in doc.items(): + if key not in ['_id', 'config_type']: + print(f"作物 {key}: {type(value)}") + + if isinstance(value, dict): + # 检查作物的具体字段 + crop_keys = list(value.keys()) + print(f" 作物字段: {crop_keys}") + + # 检查是否有字符串类型的字段被误认为是字典 + for crop_key, crop_value in value.items(): + if isinstance(crop_value, str) and crop_key in ['能否购买', '品质', '等级']: + print(f" ⚠️ 字段 {crop_key} 是字符串类型: '{crop_value}'") + elif isinstance(value, str): + print(f" ⚠️ 整个作物数据是字符串: '{value[:100]}...'") + + print("\n=== 测试API方法返回的数据 ===") + + # 测试get_crop_data_config方法 + print("\n--- 测试get_crop_data_config ---") + crop_data = mongo_api.get_crop_data_config() + if crop_data: + print(f"返回数据类型: {type(crop_data)}") + print(f"返回数据键: {list(crop_data.keys()) if isinstance(crop_data, dict) else 'N/A'}") + + # 检查是否还有config_type字段 + if 'config_type' in crop_data: + print(f"⚠️ API返回的数据仍包含config_type: {crop_data['config_type']}") + else: + print("✅ API返回的数据不包含config_type字段") + + # 检查第一个作物的数据结构 + for crop_name, crop_info in crop_data.items(): + if crop_name not in ['_id', 'config_type']: + print(f"\n作物 {crop_name}:") + print(f" 类型: {type(crop_info)}") + + if isinstance(crop_info, dict): + print(f" 字段: {list(crop_info.keys())}") + + # 检查关键字段 + for key in ['能否购买', '品质', '等级']: + if key in crop_info: + value = crop_info[key] + print(f" {key}: {value} (类型: {type(value)})") + elif isinstance(crop_info, str): + print(f" ⚠️ 作物数据是字符串: '{crop_info[:50]}...'") + + break # 只检查第一个作物 + else: + print("❌ get_crop_data_config返回空数据") + + # 测试get_item_config方法 + print("\n--- 测试get_item_config ---") + item_data = mongo_api.get_item_config() + if item_data: + print(f"道具配置数据类型: {type(item_data)}") + if 'config_type' in item_data: + print(f"⚠️ 道具配置仍包含config_type: {item_data['config_type']}") + else: + print("✅ 道具配置不包含config_type字段") + + # 测试find_documents方法 + print("\n--- 测试find_documents方法 ---") + all_configs = mongo_api.find_documents("gameconfig", {}) + if all_configs: + print(f"find_documents返回 {len(all_configs)} 个文档") + for doc in all_configs: + if 'config_type' in doc: + print(f"⚠️ find_documents返回的文档仍包含config_type: {doc['config_type']}") + else: + print(f"✅ 文档ID {doc.get('_id')} 不包含config_type字段") + + except Exception as e: + print(f"❌ 测试过程中出错: {e}") + import traceback + traceback.print_exc() + + finally: + if 'mongo_api' in locals(): + mongo_api.disconnect() + print("\n✅ 数据库连接已关闭") + +if __name__ == "__main__": + test_database_documents() \ No newline at end of file diff --git a/Server/test/test_flower_farm.py b/Server/test/test_flower_farm.py new file mode 100644 index 0000000..e69de29 diff --git a/Server/test/test_game_tips_config.py b/Server/test/test_game_tips_config.py new file mode 100644 index 0000000..3207978 --- /dev/null +++ b/Server/test/test_game_tips_config.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +测试游戏小提示配置功能 +验证服务端能否正确加载和返回游戏小提示配置数据 +""" + +import sys +import os + +# 添加当前目录到Python路径 +sys.path.append(os.path.dirname(os.path.abspath(__file__))) + +from SMYMongoDBAPI import SMYMongoDBAPI +from TCPGameServer import TCPGameServer + +def test_mongo_api(): + """测试MongoDB API的游戏小提示配置功能""" + print("=== 测试MongoDB API ===\n") + + try: + # 创建MongoDB API实例 + mongo_api = SMYMongoDBAPI() + + # 连接到数据库 + if not mongo_api.connect(): + print("错误:无法连接到MongoDB数据库") + return False + + print("成功连接到MongoDB数据库") + + # 获取游戏小提示配置 + config = mongo_api.get_game_tips_config() + + if config: + print("成功获取游戏小提示配置:") + print(f" 切换模式: {config.get('切换模式', 'N/A')}") + print(f" 切换速度: {config.get('切换速度', 'N/A')}") + tips = config.get('游戏小提示', []) + print(f" 游戏小提示数量: {len(tips)}") + print(" 前3条小提示:") + for i, tip in enumerate(tips[:3]): + print(f" {i+1}. {tip}") + return True + else: + print("错误:无法获取游戏小提示配置") + return False + + except Exception as e: + print(f"测试过程中发生错误: {str(e)}") + return False + finally: + # 断开数据库连接 + if 'mongo_api' in locals(): + mongo_api.disconnect() + print("已断开MongoDB数据库连接") + +def test_game_server(): + """测试游戏服务器的游戏小提示配置加载功能""" + print("\n=== 测试游戏服务器 ===\n") + + try: + # 创建游戏服务器实例(不启动网络服务) + server = TCPGameServer() + + # 初始化MongoDB连接 + server.mongo_api = SMYMongoDBAPI() + if not server.mongo_api.connect(): + print("错误:服务器无法连接到MongoDB数据库") + return False + + print("服务器成功连接到MongoDB数据库") + + # 测试加载游戏小提示配置 + config = server._load_game_tips_config() + + if config: + print("服务器成功加载游戏小提示配置:") + print(f" 切换模式: {config.get('切换模式', 'N/A')}") + print(f" 切换速度: {config.get('切换速度', 'N/A')}") + tips = config.get('游戏小提示', []) + print(f" 游戏小提示数量: {len(tips)}") + print(" 前3条小提示:") + for i, tip in enumerate(tips[:3]): + print(f" {i+1}. {tip}") + return True + else: + print("错误:服务器无法加载游戏小提示配置") + return False + + except Exception as e: + print(f"测试过程中发生错误: {str(e)}") + return False + finally: + # 断开数据库连接 + if 'server' in locals() and hasattr(server, 'mongo_api') and server.mongo_api: + server.mongo_api.disconnect() + print("服务器已断开MongoDB数据库连接") + +if __name__ == "__main__": + print("开始测试游戏小提示配置功能...\n") + + # 测试MongoDB API + mongo_success = test_mongo_api() + + # 测试游戏服务器 + server_success = test_game_server() + + print("\n=== 测试结果 ===\n") + print(f"MongoDB API测试: {'✓ 通过' if mongo_success else '✗ 失败'}") + print(f"游戏服务器测试: {'✓ 通过' if server_success else '✗ 失败'}") + + if mongo_success and server_success: + print("\n🎉 所有测试通过!游戏小提示配置功能正常工作。") + else: + print("\n❌ 部分测试失败,请检查配置。") + sys.exit(1) \ No newline at end of file diff --git a/Server/test/test_game_tips_integration.py b/Server/test/test_game_tips_integration.py new file mode 100644 index 0000000..893d2d9 --- /dev/null +++ b/Server/test/test_game_tips_integration.py @@ -0,0 +1,242 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +游戏小提示配置系统集成测试 +测试完整的配置流程:数据库 -> 服务端 -> 客户端请求 -> 配置应用 +""" + +import sys +import os +import time +import threading +import socket +import json + +# 添加当前目录到Python路径 +sys.path.append(os.path.dirname(os.path.abspath(__file__))) + +from SMYMongoDBAPI import SMYMongoDBAPI +from TCPGameServer import TCPGameServer + +def test_database_config(): + """测试数据库配置""" + print("=== 测试数据库配置 ===") + + try: + # 连接数据库 + mongo_api = SMYMongoDBAPI(environment="test") + + if not mongo_api.is_connected(): + print("❌ 数据库连接失败") + return False + + # 获取游戏小提示配置 + config = mongo_api.get_game_tips_config() + + if config: + print("✓ 成功获取游戏小提示配置:") + print(f" 切换模式: {config.get('切换模式', '未设置')}") + print(f" 切换速度: {config.get('切换速度', '未设置')}") + print(f" 游戏小提示数量: {len(config.get('游戏小提示', []))}") + + tips = config.get('游戏小提示', []) + if tips: + print(" 前3条小提示:") + for i, tip in enumerate(tips[:3], 1): + print(f" {i}. {tip}") + + mongo_api.disconnect() + return True + else: + print("❌ 未找到游戏小提示配置") + mongo_api.disconnect() + return False + + except Exception as e: + print(f"❌ 数据库测试失败: {e}") + return False + +def test_server_config_loading(): + """测试服务端配置加载""" + print("\n=== 测试服务端配置加载 ===") + + try: + # 初始化游戏服务器 + server = TCPGameServer(server_host="localhost", server_port=0) + + if not server.mongo_api or not server.mongo_api.is_connected(): + print("❌ 服务器MongoDB连接失败") + return False + + print("✓ 服务器成功连接到MongoDB数据库") + + # 测试配置加载 + config = server._load_game_tips_config() + + if config: + print("✓ 服务器成功加载游戏小提示配置:") + print(f" 切换模式: {config.get('切换模式', '未设置')}") + print(f" 切换速度: {config.get('切换速度', '未设置')}") + print(f" 游戏小提示数量: {len(config.get('游戏小提示', []))}") + + tips = config.get('游戏小提示', []) + if tips: + print(" 前3条小提示:") + for i, tip in enumerate(tips[:3], 1): + print(f" {i}. {tip}") + + server.mongo_api.disconnect() + print("✓ 服务器已断开MongoDB数据库连接") + return True + else: + print("❌ 服务器加载游戏小提示配置失败") + server.mongo_api.disconnect() + return False + + except Exception as e: + print(f"❌ 服务端测试失败: {e}") + return False + +def test_client_server_communication(): + """测试客户端-服务端通信""" + print("\n=== 测试客户端-服务端通信 ===") + + server = None + client_socket = None + + try: + # 启动服务器(使用固定端口进行测试) + test_port = 17070 + server = TCPGameServer(server_host="localhost", server_port=test_port) + + if not server.mongo_api or not server.mongo_api.is_connected(): + print("❌ 服务器MongoDB连接失败") + return False + + # 在新线程中启动服务器 + server_thread = threading.Thread(target=server.start, daemon=True) + server_thread.start() + + # 等待服务器启动 + time.sleep(1) + + # 获取服务器端口 + server_port = test_port + print(f"✓ 服务器已启动,端口: {server_port}") + + # 创建客户端连接 + client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + client_socket.settimeout(5) + client_socket.connect(("localhost", server_port)) + print("✓ 客户端已连接到服务器") + + # 发送游戏小提示配置请求 + request = { + "type": "request_game_tips_config" + } + + request_data = json.dumps(request).encode('utf-8') + client_socket.send(len(request_data).to_bytes(4, byteorder='big')) + client_socket.send(request_data) + print("✓ 已发送游戏小提示配置请求") + + # 接收响应 + response_length_bytes = client_socket.recv(4) + if len(response_length_bytes) != 4: + print("❌ 接收响应长度失败") + return False + + response_length = int.from_bytes(response_length_bytes, byteorder='big') + response_data = b'' + + while len(response_data) < response_length: + chunk = client_socket.recv(response_length - len(response_data)) + if not chunk: + break + response_data += chunk + + if len(response_data) != response_length: + print("❌ 接收响应数据不完整") + return False + + # 解析响应 + response = json.loads(response_data.decode('utf-8')) + print("✓ 已接收服务器响应") + + # 验证响应 + if response.get("type") == "game_tips_config_response": + if response.get("success"): + config = response.get("game_tips_config", {}) + print("✓ 成功接收游戏小提示配置:") + print(f" 切换模式: {config.get('切换模式', '未设置')}") + print(f" 切换速度: {config.get('切换速度', '未设置')}") + print(f" 游戏小提示数量: {len(config.get('游戏小提示', []))}") + + tips = config.get('游戏小提示', []) + if tips: + print(" 前3条小提示:") + for i, tip in enumerate(tips[:3], 1): + print(f" {i}. {tip}") + + return True + else: + message = response.get("message", "未知错误") + print(f"❌ 服务器返回失败: {message}") + return False + else: + print(f"❌ 收到意外的响应类型: {response.get('type')}") + return False + + except Exception as e: + print(f"❌ 客户端-服务端通信测试失败: {e}") + return False + + finally: + # 清理资源 + if client_socket: + try: + client_socket.close() + except: + pass + + if server and server.mongo_api: + try: + server.mongo_api.disconnect() + except: + pass + +def main(): + """主测试函数""" + print("🚀 开始游戏小提示配置系统集成测试\n") + + # 测试结果 + results = { + "数据库配置": test_database_config(), + "服务端配置加载": test_server_config_loading(), + "客户端-服务端通信": test_client_server_communication() + } + + print("\n=== 测试结果 ===\n") + + all_passed = True + for test_name, result in results.items(): + status = "✓ 通过" if result else "❌ 失败" + print(f"{test_name}测试: {status}") + if not result: + all_passed = False + + if all_passed: + print("\n🎉 所有测试通过!游戏小提示配置系统完全正常工作。") + print("\n📋 系统功能确认:") + print(" ✓ 数据库配置存储和读取正常") + print(" ✓ 服务端配置加载正常") + print(" ✓ 客户端-服务端通信正常") + print(" ✓ 配置数据传输完整") + print("\n🎮 客户端现在应该能够正确使用数据库中的游戏小提示配置!") + else: + print("\n❌ 部分测试失败,请检查相关组件。") + + return all_passed + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/Server/test/test_special_farm.py b/Server/test/test_special_farm.py new file mode 100644 index 0000000..82e4ac3 --- /dev/null +++ b/Server/test/test_special_farm.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +特殊农场管理系统测试脚本 +作者: AI Assistant +功能: 测试特殊农场的种植功能 +""" + +import sys +import os +sys.path.append(os.path.dirname(os.path.abspath(__file__))) + +from SpecialFarm import SpecialFarmManager + +def test_special_farm(): + """ + 测试特殊农场功能 + """ + print("=" * 50) + print("特殊农场管理系统测试") + print("=" * 50) + + try: + # 创建管理器(使用测试环境) + print("1. 初始化特殊农场管理器...") + manager = SpecialFarmManager("test") + print("✓ 管理器初始化成功") + + # 测试数据库连接 + print("\n2. 测试数据库连接...") + if manager.mongo_api.is_connected(): + print("✓ 数据库连接成功") + else: + print("✗ 数据库连接失败") + return False + + # 测试获取作物配置 + print("\n3. 测试获取作物配置...") + crop_data = manager.get_crop_data() + if crop_data: + print(f"✓ 成功获取作物配置,共 {len(crop_data)} 种作物") + + # 检查杂交树是否存在 + if "杂交树1" in crop_data and "杂交树2" in crop_data: + print("✓ 杂交树1和杂交树2配置存在") + print(f" - 杂交树1: {crop_data['杂交树1']['作物名称']}") + print(f" - 杂交树2: {crop_data['杂交树2']['作物名称']}") + else: + print("✗ 杂交树配置不存在") + return False + else: + print("✗ 获取作物配置失败") + return False + + # 测试获取杂交农场数据 + print("\n4. 测试获取杂交农场数据...") + farm_config = manager.special_farms["杂交农场"] + object_id = farm_config["object_id"] + + player_data = manager.get_player_data_by_object_id(object_id) + if player_data: + print(f"✓ 成功获取杂交农场数据") + print(f" - 农场名称: {player_data.get('农场名称', 'Unknown')}") + print(f" - 玩家昵称: {player_data.get('玩家昵称', 'Unknown')}") + print(f" - 土地数量: {len(player_data.get('农场土地', []))}") + + # 统计土地状态 + farm_lands = player_data.get("农场土地", []) + digged_count = sum(1 for land in farm_lands if land.get("is_diged", False)) + planted_count = sum(1 for land in farm_lands if land.get("is_planted", False)) + + print(f" - 已开垦土地: {digged_count}") + print(f" - 已种植土地: {planted_count}") + else: + print("✗ 获取杂交农场数据失败") + return False + + # 测试种植功能 + print("\n5. 测试杂交农场种植功能...") + if manager.plant_crops_in_farm("杂交农场"): + print("✓ 杂交农场种植成功") + + # 重新获取数据验证种植结果 + updated_data = manager.get_player_data_by_object_id(object_id) + if updated_data: + farm_lands = updated_data.get("农场土地", []) + planted_count = sum(1 for land in farm_lands if land.get("is_planted", False)) + + # 统计种植的作物类型 + crop_types = {} + for land in farm_lands: + if land.get("is_planted", False): + crop_type = land.get("crop_type", "") + crop_types[crop_type] = crop_types.get(crop_type, 0) + 1 + + print(f" - 种植后已种植土地: {planted_count}") + print(f" - 作物分布:") + for crop_type, count in crop_types.items(): + print(f" * {crop_type}: {count} 块") + else: + print("✗ 杂交农场种植失败") + return False + + print("\n" + "=" * 50) + print("✓ 所有测试通过!特殊农场管理系统工作正常") + print("=" * 50) + return True + + except Exception as e: + print(f"\n✗ 测试过程中出错: {str(e)}") + import traceback + traceback.print_exc() + return False + +def test_manual_maintenance(): + """ + 测试手动维护功能 + """ + print("\n" + "=" * 50) + print("测试手动维护功能") + print("=" * 50) + + try: + manager = SpecialFarmManager("test") + + print("执行手动维护...") + if manager.manual_maintenance("杂交农场"): + print("✓ 手动维护成功") + else: + print("✗ 手动维护失败") + + except Exception as e: + print(f"✗ 手动维护测试出错: {str(e)}") + +if __name__ == "__main__": + # 运行基础测试 + success = test_special_farm() + + if success: + # 运行手动维护测试 + test_manual_maintenance() + + print("\n" + "=" * 50) + print("使用说明:") + print("1. 自动模式: python SpecialFarm.py [test|production]") + print("2. 手动模式: python SpecialFarm.py [test|production] manual [农场名称]") + print("3. 日志文件: special_farm.log") + print("=" * 50) + else: + print("\n测试失败,请检查配置和数据库连接") + sys.exit(1) \ No newline at end of file diff --git a/Server/test/test_visit_mode_update.py b/Server/test/test_visit_mode_update.py new file mode 100644 index 0000000..de8b0db --- /dev/null +++ b/Server/test/test_visit_mode_update.py @@ -0,0 +1,143 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +简化的访问模式实时更新功能测试 +""" + +import sys +import os + +print("开始测试访问模式下的实时更新功能...") + +try: + # 测试导入 + print("正在导入模块...") + + # 检查文件是否存在 + if os.path.exists('TCPGameServer.py'): + print("✓ TCPGameServer.py 文件存在") + else: + print("❌ TCPGameServer.py 文件不存在") + sys.exit(1) + + if os.path.exists('SMYMongoDBAPI.py'): + print("✓ SMYMongoDBAPI.py 文件存在") + else: + print("❌ SMYMongoDBAPI.py 文件不存在") + sys.exit(1) + + # 尝试导入 + from TCPGameServer import TCPGameServer + print("✓ 成功导入 TCPGameServer") + + # 检查关键方法是否存在 + server = TCPGameServer() + + if hasattr(server, '_push_update_to_visitors'): + print("✓ _push_update_to_visitors 方法存在") + else: + print("❌ _push_update_to_visitors 方法不存在") + + if hasattr(server, 'update_crops_growth'): + print("✓ update_crops_growth 方法存在") + else: + print("❌ update_crops_growth 方法不存在") + + if hasattr(server, '_push_crop_update_to_player'): + print("✓ _push_crop_update_to_player 方法存在") + else: + print("❌ _push_crop_update_to_player 方法不存在") + + print("\n=== 功能验证 ===") + + # 模拟用户数据 + server.user_data = { + "client_a": { + "logged_in": True, + "username": "user_a", + "visiting_mode": False, + "visiting_target": "" + }, + "client_b": { + "logged_in": True, + "username": "user_b", + "visiting_mode": True, + "visiting_target": "user_a" + } + } + + # 测试 update_crops_growth 方法是否能正确收集需要更新的玩家 + print("测试作物生长更新逻辑...") + + # 重写 load_player_data 方法以避免数据库依赖 + def mock_load_player_data(username): + return { + "农场土地": [ + { + "is_planted": True, + "crop_type": "番茄", + "grow_time": 300, + "max_grow_time": 600 + } + ] + } + + def mock_save_player_data(username, data): + pass + + def mock_update_player_crops(data, username): + return True + + def mock_push_crop_update_to_player(username, data): + print(f" 推送作物更新给: {username}") + + server.load_player_data = mock_load_player_data + server.save_player_data = mock_save_player_data + server.update_player_crops = mock_update_player_crops + server._push_crop_update_to_player = mock_push_crop_update_to_player + + # 调用作物生长更新 + print("调用 update_crops_growth...") + server.update_crops_growth() + + print("\n=== 测试访问者推送功能 ===") + + # 重写 send_data 方法 + def mock_send_data(client_id, data): + print(f" 向 {client_id} 发送消息: {data.get('type', 'unknown')}") + if data.get('type') == 'crop_update': + print(f" - 是否访问模式: {data.get('is_visiting', False)}") + print(f" - 被访问玩家: {data.get('visited_player', 'N/A')}") + + def mock_find_client_by_username(username): + if username == "user_a": + return "client_a" + return None + + server.send_data = mock_send_data + server._find_client_by_username = mock_find_client_by_username + + # 测试向访问者推送更新 + target_player_data = { + "农场土地": [ + { + "is_planted": True, + "crop_type": "番茄", + "grow_time": 400, + "max_grow_time": 600 + } + ] + } + + print("调用 _push_update_to_visitors...") + server._push_update_to_visitors("user_a", target_player_data) + + print("\n🎉 所有功能验证通过!访问模式下的实时更新功能已正确实现。") + +except Exception as e: + print(f"❌ 测试过程中出现错误: {e}") + import traceback + traceback.print_exc() + sys.exit(1) + +print("\n测试完成!") \ No newline at end of file diff --git a/Server/test/文档/DisplayServer_总结_Markdown.md b/Server/test/文档/DisplayServer_总结_Markdown.md new file mode 100644 index 0000000..0cd7d2b --- /dev/null +++ b/Server/test/文档/DisplayServer_总结_Markdown.md @@ -0,0 +1,559 @@ +# DisplayServer API 参考文档 + +## 类简介 + +**继承**:Object + +DisplayServer 是用于低阶窗口管理的服务器接口。所有与窗口管理相关的内容都由 DisplayServer(显示服务器)处理。 + +> **无头模式**:如果使用 `--headless` 命令行参数启动引擎,就会禁用所有渲染和窗口管理功能,此时 DisplayServer 的大多数函数都会返回虚设值。 + +--- + +## 方法列表 + +### 🔔 系统交互 + +#### `void beep()` +发出系统提示音。 + +#### `void enable_for_stealing_focus(process_id: int)` +允许指定进程获取焦点。 + +#### `void force_process_and_drop_events()` +强制处理并丢弃所有事件。 + +--- + +### 📋 剪贴板操作 + +#### `String clipboard_get()` +获取剪贴板文本内容。 + +#### `Image clipboard_get_image()` +获取剪贴板图像内容。 + +#### `String clipboard_get_primary()` +获取主剪贴板文本内容(仅限 Linux)。 + +#### `bool clipboard_has()` +检查剪贴板是否有内容。 + +#### `bool clipboard_has_image()` +检查剪贴板是否有图像。 + +#### `void clipboard_set(clipboard: String)` +设置剪贴板文本内容。 + +#### `void clipboard_set_primary(clipboard_primary: String)` +设置主剪贴板文本内容(仅限 Linux)。 + +--- + +### 🖱️ 鼠标和光标 + +#### `CursorShape cursor_get_shape()` +获取当前光标形状。 + +#### `void cursor_set_custom_image(cursor: Resource, shape: CursorShape = 0, hotspot: Vector2 = Vector2(0, 0))` +设置自定义光标图像。 + +#### `void cursor_set_shape(shape: CursorShape)` +设置光标形状。 + +#### `BitField[MouseButtonMask] mouse_get_button_state()` +获取鼠标按键状态。 + +#### `MouseMode mouse_get_mode()` +获取鼠标模式。 + +#### `Vector2i mouse_get_position()` +获取鼠标位置。 + +#### `void mouse_set_mode(mouse_mode: MouseMode)` +设置鼠标模式。 + +#### `void warp_mouse(position: Vector2i)` +将鼠标光标移动到指定位置。 + +--- + +### 💬 对话框 + +#### `Error dialog_input_text(title: String, description: String, existing_text: String, callback: Callable)` +显示文本输入对话框。 + +#### `Error dialog_show(title: String, description: String, buttons: PackedStringArray, callback: Callable)` +显示系统对话框。 + +#### `Error file_dialog_show(title: String, current_directory: String, filename: String, show_hidden: bool, mode: FileDialogMode, filters: PackedStringArray, callback: Callable)` +显示文件选择对话框。 + +#### `Error file_dialog_with_options_show(title: String, current_directory: String, root: String, filename: String, show_hidden: bool, mode: FileDialogMode, filters: PackedStringArray, options: Array[Dictionary], callback: Callable)` +显示带扩展选项的文件选择对话框。 + +--- + +### 🎨 主题和颜色 + +#### `Color get_accent_color()` +获取系统强调色。 + +#### `Color get_base_color()` +获取系统基础色。 + +#### `bool is_dark_mode()` +检查系统是否为深色模式。 + +#### `bool is_dark_mode_supported()` +检查系统是否支持深色模式。 + +#### `void set_system_theme_change_callback(callable: Callable)` +设置系统主题变化时的回调。 + +--- + +### 📱 显示和屏幕 + +#### `Array[Rect2] get_display_cutouts()` +获取显示器刘海信息。 + +#### `Rect2i get_display_safe_area()` +获取显示器安全区域。 + +#### `int get_keyboard_focus_screen()` +获取键盘焦点所在屏幕。 + +#### `String get_name()` +获取显示服务器名称。 + +#### `int get_primary_screen()` +获取主屏幕索引。 + +#### `int get_screen_count()` +获取屏幕数量。 + +#### `int get_screen_from_rect(rect: Rect2)` +根据矩形位置获取屏幕索引。 + +#### `bool get_swap_cancel_ok()` +获取是否交换确定取消按钮。 + +#### `int get_window_at_screen_position(position: Vector2i)` +获取指定屏幕位置的窗口ID。 + +#### `PackedInt32Array get_window_list()` +获取所有窗口ID列表。 + +--- + +### 🖥️ 屏幕操作 + +#### `int screen_get_dpi(screen: int = -1)` +获取屏幕DPI。 + +#### `Image screen_get_image(screen: int = -1)` +获取屏幕截图。 + +#### `Image screen_get_image_rect(rect: Rect2i)` +获取屏幕指定区域截图。 + +#### `float screen_get_max_scale()` +获取所有屏幕的最大缩放系数。 + +#### `ScreenOrientation screen_get_orientation(screen: int = -1)` +获取屏幕朝向。 + +#### `Color screen_get_pixel(position: Vector2i)` +获取指定位置的像素颜色。 + +#### `Vector2i screen_get_position(screen: int = -1)` +获取屏幕位置。 + +#### `float screen_get_refresh_rate(screen: int = -1)` +获取屏幕刷新率。 + +#### `float screen_get_scale(screen: int = -1)` +获取屏幕缩放系数。 + +#### `Vector2i screen_get_size(screen: int = -1)` +获取屏幕大小。 + +#### `Rect2i screen_get_usable_rect(screen: int = -1)` +获取屏幕可用区域。 + +#### `bool screen_is_kept_on()` +检查屏幕是否保持开启。 + +#### `void screen_set_keep_on(enable: bool)` +设置屏幕保持开启。 + +#### `void screen_set_orientation(orientation: ScreenOrientation, screen: int = -1)` +设置屏幕朝向。 + +--- + +### 🖼️ 图标设置 + +#### `void set_icon(image: Image)` +设置窗口图标。 + +#### `void set_native_icon(filename: String)` +使用原生格式设置窗口图标。 + +--- + +### 💾 输出管理 + +#### `bool has_additional_outputs()` +检查是否有额外输出设备。 + +#### `void register_additional_output(object: Object)` +注册额外输出设备。 + +#### `void unregister_additional_output(object: Object)` +取消注册额外输出设备。 + +--- + +### ⚡ 功能检测 + +#### `bool has_feature(feature: Feature)` +检查是否支持指定功能。 + +#### `bool has_hardware_keyboard()` +检查是否有硬件键盘。 + +#### `bool is_touchscreen_available()` +检查是否支持触屏。 + +#### `bool is_window_transparency_available()` +检查是否支持窗口透明。 + +--- + +### ⌨️ 键盘 + +#### `int keyboard_get_current_layout()` +获取当前键盘布局。 + +#### `Key keyboard_get_keycode_from_physical(keycode: Key)` +从物理按键获取键码。 + +#### `Key keyboard_get_label_from_physical(keycode: Key)` +从物理按键获取标签。 + +#### `int keyboard_get_layout_count()` +获取键盘布局数量。 + +#### `String keyboard_get_layout_language(index: int)` +获取键盘布局语言。 + +#### `String keyboard_get_layout_name(index: int)` +获取键盘布局名称。 + +#### `void keyboard_set_current_layout(index: int)` +设置当前键盘布局。 + +--- + +### 📝 输入法 + +#### `Vector2i ime_get_selection()` +获取输入法选中范围。 + +#### `String ime_get_text()` +获取输入法文本。 + +--- + +### 🎯 状态指示器 + +#### `int create_status_indicator(icon: Texture2D, tooltip: String, callback: Callable)` +创建状态指示器。 + +#### `void delete_status_indicator(id: int)` +删除状态指示器。 + +#### `Rect2 status_indicator_get_rect(id: int)` +获取状态指示器位置。 + +#### `void status_indicator_set_callback(id: int, callback: Callable)` +设置状态指示器回调。 + +#### `void status_indicator_set_icon(id: int, icon: Texture2D)` +设置状态指示器图标。 + +#### `void status_indicator_set_menu(id: int, menu_rid: RID)` +设置状态指示器菜单。 + +#### `void status_indicator_set_tooltip(id: int, tooltip: String)` +设置状态指示器提示文本。 + +--- + +### 📱 数位板 + +#### `String tablet_get_current_driver()` +获取当前数位板驱动。 + +#### `int tablet_get_driver_count()` +获取数位板驱动数量。 + +#### `String tablet_get_driver_name(idx: int)` +获取数位板驱动名称。 + +#### `void tablet_set_current_driver(name: String)` +设置数位板驱动。 + +--- + +### 🗣️ 文本转语音 + +#### `Array[Dictionary] tts_get_voices()` +获取语音列表。 + +#### `PackedStringArray tts_get_voices_for_language(language: String)` +获取指定语言的语音列表。 + +#### `bool tts_is_paused()` +检查是否暂停。 + +#### `bool tts_is_speaking()` +检查是否正在朗读。 + +#### `void tts_pause()` +暂停朗读。 + +#### `void tts_resume()` +恢复朗读。 + +#### `void tts_set_utterance_callback(event: TTSUtteranceEvent, callable: Callable)` +设置朗读事件回调。 + +#### `void tts_speak(text: String, voice: String, volume: int = 50, pitch: float = 1.0, rate: float = 1.0, utterance_id: int = 0, interrupt: bool = false)` +开始朗读文本。 + +#### `void tts_stop()` +停止朗读。 + +--- + +### ⌨️ 虚拟键盘 + +#### `int virtual_keyboard_get_height()` +获取虚拟键盘高度。 + +#### `void virtual_keyboard_hide()` +隐藏虚拟键盘。 + +#### `void virtual_keyboard_show(existing_text: String, position: Rect2 = Rect2(0, 0, 0, 0), type: VirtualKeyboardType = 0, max_length: int = -1, cursor_start: int = -1, cursor_end: int = -1)` +显示虚拟键盘。 + +--- + +### 🪟 窗口管理 + +#### `bool window_can_draw(window_id: int = 0)` +检查窗口是否可绘制。 + +#### `int window_get_active_popup()` +获取活动弹出窗口ID。 + +#### `int window_get_attached_instance_id(window_id: int = 0)` +获取窗口附加的实例ID。 + +#### `int window_get_current_screen(window_id: int = 0)` +获取窗口所在屏幕。 + +#### `bool window_get_flag(flag: WindowFlags, window_id: int = 0)` +获取窗口标志。 + +#### `Vector2i window_get_max_size(window_id: int = 0)` +获取窗口最大尺寸。 + +#### `Vector2i window_get_min_size(window_id: int = 0)` +获取窗口最小尺寸。 + +#### `WindowMode window_get_mode(window_id: int = 0)` +获取窗口模式。 + +#### `int window_get_native_handle(handle_type: HandleType, window_id: int = 0)` +获取窗口原生句柄。 + +#### `Rect2i window_get_popup_safe_rect(window: int)` +获取弹出窗口安全区域。 + +#### `Vector2i window_get_position(window_id: int = 0)` +获取窗口位置。 + +#### `Vector2i window_get_position_with_decorations(window_id: int = 0)` +获取窗口位置(含边框)。 + +#### `Vector3i window_get_safe_title_margins(window_id: int = 0)` +获取标题栏安全边距。 + +#### `Vector2i window_get_size(window_id: int = 0)` +获取窗口大小。 + +#### `Vector2i window_get_size_with_decorations(window_id: int = 0)` +获取窗口大小(含边框)。 + +#### `Vector2i window_get_title_size(title: String, window_id: int = 0)` +获取标题栏大小。 + +#### `VSyncMode window_get_vsync_mode(window_id: int = 0)` +获取垂直同步模式。 + +#### `bool window_is_focused(window_id: int = 0)` +检查窗口是否有焦点。 + +#### `bool window_is_maximize_allowed(window_id: int = 0)` +检查窗口是否可最大化。 + +#### `bool window_maximize_on_title_dbl_click()` +检查双击标题栏是否最大化。 + +#### `bool window_minimize_on_title_dbl_click()` +检查双击标题栏是否最小化。 + +#### `void window_move_to_foreground(window_id: int = 0)` +将窗口移到前台。 + +#### `void window_request_attention(window_id: int = 0)` +请求窗口注意。 + +#### `void window_set_current_screen(screen: int, window_id: int = 0)` +设置窗口所在屏幕。 + +#### `void window_set_drop_files_callback(callback: Callable, window_id: int = 0)` +设置文件拖放回调。 + +#### `void window_set_exclusive(window_id: int, exclusive: bool)` +设置窗口独占模式。 + +#### `void window_set_flag(flag: WindowFlags, enabled: bool, window_id: int = 0)` +设置窗口标志。 + +#### `void window_set_ime_active(active: bool, window_id: int = 0)` +设置输入法是否激活。 + +#### `void window_set_ime_position(position: Vector2i, window_id: int = 0)` +设置输入法位置。 + +#### `void window_set_input_event_callback(callback: Callable, window_id: int = 0)` +设置输入事件回调。 + +#### `void window_set_input_text_callback(callback: Callable, window_id: int = 0)` +设置文本输入回调。 + +#### `void window_set_max_size(max_size: Vector2i, window_id: int = 0)` +设置窗口最大尺寸。 + +#### `void window_set_min_size(min_size: Vector2i, window_id: int = 0)` +设置窗口最小尺寸。 + +#### `void window_set_mode(mode: WindowMode, window_id: int = 0)` +设置窗口模式。 + +#### `void window_set_mouse_passthrough(region: PackedVector2Array, window_id: int = 0)` +设置鼠标穿透区域。 + +#### `void window_set_popup_safe_rect(window: int, rect: Rect2i)` +设置弹出窗口安全区域。 + +#### `void window_set_position(position: Vector2i, window_id: int = 0)` +设置窗口位置。 + +#### `void window_set_rect_changed_callback(callback: Callable, window_id: int = 0)` +设置窗口位置大小变化回调。 + +#### `void window_set_size(size: Vector2i, window_id: int = 0)` +设置窗口大小。 + +#### `void window_set_title(title: String, window_id: int = 0)` +设置窗口标题。 + +#### `void window_set_transient(window_id: int, parent_window_id: int)` +设置窗口为瞬态。 + +#### `void window_set_vsync_mode(vsync_mode: VSyncMode, window_id: int = 0)` +设置垂直同步模式。 + +#### `void window_set_window_buttons_offset(offset: Vector2i, window_id: int = 0)` +设置窗口按钮偏移。 + +#### `void window_set_window_event_callback(callback: Callable, window_id: int = 0)` +设置窗口事件回调。 + +#### `void window_start_drag(window_id: int = 0)` +开始拖拽窗口。 + +#### `void window_start_resize(edge: WindowResizeEdge, window_id: int = 0)` +开始调整窗口大小。 + +--- + +### 📞 帮助系统 + +#### `void help_set_search_callbacks(search_callback: Callable, action_callback: Callable)` +设置帮助系统搜索回调。 + +#### `void show_emoji_and_symbol_picker()` +显示表情符号选择器。 + +--- + +### ⚙️ 事件处理 + +#### `void process_events()` +处理事件。 + +--- + +## 常量 + +- `SCREEN_WITH_MOUSE_FOCUS = -4`:鼠标焦点所在屏幕 +- `SCREEN_WITH_KEYBOARD_FOCUS = -3`:键盘焦点所在屏幕 +- `SCREEN_PRIMARY = -2`:主屏幕 +- `SCREEN_OF_MAIN_WINDOW = -1`:主窗口所在屏幕 +- `MAIN_WINDOW_ID = 0`:主窗口ID +- `INVALID_WINDOW_ID = -1`:无效窗口ID + +--- + +## 枚举 + +### Feature +系统功能支持检测枚举,包含多种功能如子窗口、触屏、鼠标、剪贴板、虚拟键盘等支持检测。 + +### MouseMode +鼠标模式枚举:可见、隐藏、捕获、限制等模式。 + +### ScreenOrientation +屏幕朝向枚举:横屏、竖屏及其反向,以及传感器自动模式。 + +### VirtualKeyboardType +虚拟键盘类型:默认、多行、数字、小数、电话、邮箱、密码、URL等。 + +### CursorShape +光标形状枚举:箭头、工字形、指向手形等多种光标样式。 + +### WindowFlags +窗口标志枚举:控制窗口的各种行为和外观属性。 + +### WindowMode +窗口模式枚举:窗口、最小化、最大化、全屏等模式。 + +### HandleType +句柄类型枚举:用于获取不同类型的原生窗口句柄。 + +### VSyncMode +垂直同步模式枚举:控制画面撕裂和帧率同步。 + +### TTSUtteranceEvent +语音朗读事件枚举:开始、结束、取消、边界等事件。 + +--- + +> **注意**:此文档已排除所有已弃用的方法。某些功能可能仅在特定平台上可用,请参考原始文档中的平台支持说明。 \ No newline at end of file diff --git a/Server/test/文档/display总结.txt b/Server/test/文档/display总结.txt new file mode 100644 index 0000000..c5a5fb3 --- /dev/null +++ b/Server/test/文档/display总结.txt @@ -0,0 +1,2120 @@ +类: DisplayServer +继承: Object + +用于低阶窗口管理的服务器接口。 + +描述 + +所有与窗口管理相关的内容都由 DisplayServer(显示服务器)处理。因为一个操作系统可能支持多个显示服务器,所以与 OS 是分开的。 + +无头模式:如果使用 --headless 命令行参数 启动引擎,就会禁用所有渲染和窗口管理功能。此时 DisplayServer 的大多数函数都会返回虚设值。 + +方法 + +voidbeep() constStringclipboard_get() constImageclipboard_get_image() constStringclipboard_get_primary() constboolclipboard_has() constboolclipboard_has_image() constvoidclipboard_set(clipboard: String)voidclipboard_set_primary(clipboard_primary: String)intcreate_status_indicator(icon: Texture2D, tooltip: String, callback: Callable)CursorShapecursor_get_shape() constvoidcursor_set_custom_image(cursor: Resource, shape: CursorShape = 0, hotspot: Vector2 = Vector2(0, 0))voidcursor_set_shape(shape: CursorShape)voiddelete_status_indicator(id: int)Errordialog_input_text(title: String, description: String, existing_text: String, callback: Callable)Errordialog_show(title: String, description: String, buttons: PackedStringArray, callback: Callable)voidenable_for_stealing_focus(process_id: int)Errorfile_dialog_show(title: String, current_directory: String, filename: String, show_hidden: bool, mode: FileDialogMode, filters: PackedStringArray, callback: Callable)Errorfile_dialog_with_options_show(title: String, current_directory: String, root: String, filename: String, show_hidden: bool, mode: FileDialogMode, filters: PackedStringArray, options: Array[Dictionary], callback: Callable)voidforce_process_and_drop_events()Colorget_accent_color() constColorget_base_color() constArray[Rect2]get_display_cutouts() constRect2iget_display_safe_area() constintget_keyboard_focus_screen() constStringget_name() constintget_primary_screen() constintget_screen_count() constintget_screen_from_rect(rect: Rect2) constboolget_swap_cancel_ok()intget_window_at_screen_position(position: Vector2i) constPackedInt32Arrayget_window_list() constintglobal_menu_add_check_item(menu_root: String, label: String, callback: Callable = Callable(), key_callback: Callable = Callable(), tag: Variant = null, accelerator: Key = 0, index: int = -1) 已弃用intglobal_menu_add_icon_check_item(menu_root: String, icon: Texture2D, label: String, callback: Callable = Callable(), key_callback: Callable = Callable(), tag: Variant = null, accelerator: Key = 0, index: int = -1) 已弃用intglobal_menu_add_icon_item(menu_root: String, icon: Texture2D, label: String, callback: Callable = Callable(), key_callback: Callable = Callable(), tag: Variant = null, accelerator: Key = 0, index: int = -1) 已弃用intglobal_menu_add_icon_radio_check_item(menu_root: String, icon: Texture2D, label: String, callback: Callable = Callable(), key_callback: Callable = Callable(), tag: Variant = null, accelerator: Key = 0, index: int = -1) 已弃用intglobal_menu_add_item(menu_root: String, label: String, callback: Callable = Callable(), key_callback: Callable = Callable(), tag: Variant = null, accelerator: Key = 0, index: int = -1) 已弃用intglobal_menu_add_multistate_item(menu_root: String, label: String, max_states: int, default_state: int, callback: Callable = Callable(), key_callback: Callable = Callable(), tag: Variant = null, accelerator: Key = 0, index: int = -1) 已弃用intglobal_menu_add_radio_check_item(menu_root: String, label: String, callback: Callable = Callable(), key_callback: Callable = Callable(), tag: Variant = null, accelerator: Key = 0, index: int = -1) 已弃用intglobal_menu_add_separator(menu_root: String, index: int = -1) 已弃用intglobal_menu_add_submenu_item(menu_root: String, label: String, submenu: String, index: int = -1) 已弃用voidglobal_menu_clear(menu_root: String) 已弃用Keyglobal_menu_get_item_accelerator(menu_root: String, idx: int) const 已弃用Callableglobal_menu_get_item_callback(menu_root: String, idx: int) const 已弃用intglobal_menu_get_item_count(menu_root: String) const 已弃用Texture2Dglobal_menu_get_item_icon(menu_root: String, idx: int) const 已弃用intglobal_menu_get_item_indentation_level(menu_root: String, idx: int) const 已弃用intglobal_menu_get_item_index_from_tag(menu_root: String, tag: Variant) const 已弃用intglobal_menu_get_item_index_from_text(menu_root: String, text: String) const 已弃用Callableglobal_menu_get_item_key_callback(menu_root: String, idx: int) const 已弃用intglobal_menu_get_item_max_states(menu_root: String, idx: int) const 已弃用intglobal_menu_get_item_state(menu_root: String, idx: int) const 已弃用Stringglobal_menu_get_item_submenu(menu_root: String, idx: int) const 已弃用Variantglobal_menu_get_item_tag(menu_root: String, idx: int) const 已弃用Stringglobal_menu_get_item_text(menu_root: String, idx: int) const 已弃用Stringglobal_menu_get_item_tooltip(menu_root: String, idx: int) const 已弃用Dictionaryglobal_menu_get_system_menu_roots() const 已弃用boolglobal_menu_is_item_checkable(menu_root: String, idx: int) const 已弃用boolglobal_menu_is_item_checked(menu_root: String, idx: int) const 已弃用boolglobal_menu_is_item_disabled(menu_root: String, idx: int) const 已弃用boolglobal_menu_is_item_hidden(menu_root: String, idx: int) const 已弃用boolglobal_menu_is_item_radio_checkable(menu_root: String, idx: int) const 已弃用voidglobal_menu_remove_item(menu_root: String, idx: int) 已弃用voidglobal_menu_set_item_accelerator(menu_root: String, idx: int, keycode: Key) 已弃用voidglobal_menu_set_item_callback(menu_root: String, idx: int, callback: Callable) 已弃用voidglobal_menu_set_item_checkable(menu_root: String, idx: int, checkable: bool) 已弃用voidglobal_menu_set_item_checked(menu_root: String, idx: int, checked: bool) 已弃用voidglobal_menu_set_item_disabled(menu_root: String, idx: int, disabled: bool) 已弃用voidglobal_menu_set_item_hidden(menu_root: String, idx: int, hidden: bool) 已弃用voidglobal_menu_set_item_hover_callbacks(menu_root: String, idx: int, callback: Callable) 已弃用voidglobal_menu_set_item_icon(menu_root: String, idx: int, icon: Texture2D) 已弃用voidglobal_menu_set_item_indentation_level(menu_root: String, idx: int, level: int) 已弃用voidglobal_menu_set_item_key_callback(menu_root: String, idx: int, key_callback: Callable) 已弃用voidglobal_menu_set_item_max_states(menu_root: String, idx: int, max_states: int) 已弃用voidglobal_menu_set_item_radio_checkable(menu_root: String, idx: int, checkable: bool) 已弃用voidglobal_menu_set_item_state(menu_root: String, idx: int, state: int) 已弃用voidglobal_menu_set_item_submenu(menu_root: String, idx: int, submenu: String) 已弃用voidglobal_menu_set_item_tag(menu_root: String, idx: int, tag: Variant) 已弃用voidglobal_menu_set_item_text(menu_root: String, idx: int, text: String) 已弃用voidglobal_menu_set_item_tooltip(menu_root: String, idx: int, tooltip: String) 已弃用voidglobal_menu_set_popup_callbacks(menu_root: String, open_callback: Callable, close_callback: Callable) 已弃用boolhas_additional_outputs() constboolhas_feature(feature: Feature) constboolhas_hardware_keyboard() constvoidhelp_set_search_callbacks(search_callback: Callable, action_callback: Callable)Vector2iime_get_selection() constStringime_get_text() constboolis_dark_mode() constboolis_dark_mode_supported() constboolis_touchscreen_available() constboolis_window_transparency_available() constintkeyboard_get_current_layout() constKeykeyboard_get_keycode_from_physical(keycode: Key) constKeykeyboard_get_label_from_physical(keycode: Key) constintkeyboard_get_layout_count() constStringkeyboard_get_layout_language(index: int) constStringkeyboard_get_layout_name(index: int) constvoidkeyboard_set_current_layout(index: int)BitField[MouseButtonMask]mouse_get_button_state() constMouseModemouse_get_mode() constVector2imouse_get_position() constvoidmouse_set_mode(mouse_mode: MouseMode)voidprocess_events()voidregister_additional_output(object: Object)intscreen_get_dpi(screen: int = -1) constImagescreen_get_image(screen: int = -1) constImagescreen_get_image_rect(rect: Rect2i) constfloatscreen_get_max_scale() constScreenOrientationscreen_get_orientation(screen: int = -1) constColorscreen_get_pixel(position: Vector2i) constVector2iscreen_get_position(screen: int = -1) constfloatscreen_get_refresh_rate(screen: int = -1) constfloatscreen_get_scale(screen: int = -1) constVector2iscreen_get_size(screen: int = -1) constRect2iscreen_get_usable_rect(screen: int = -1) constboolscreen_is_kept_on() constvoidscreen_set_keep_on(enable: bool)voidscreen_set_orientation(orientation: ScreenOrientation, screen: int = -1)voidset_icon(image: Image)voidset_native_icon(filename: String)voidset_system_theme_change_callback(callable: Callable)voidshow_emoji_and_symbol_picker() constRect2status_indicator_get_rect(id: int) constvoidstatus_indicator_set_callback(id: int, callback: Callable)voidstatus_indicator_set_icon(id: int, icon: Texture2D)voidstatus_indicator_set_menu(id: int, menu_rid: RID)voidstatus_indicator_set_tooltip(id: int, tooltip: String)Stringtablet_get_current_driver() constinttablet_get_driver_count() constStringtablet_get_driver_name(idx: int) constvoidtablet_set_current_driver(name: String)Array[Dictionary]tts_get_voices() constPackedStringArraytts_get_voices_for_language(language: String) constbooltts_is_paused() constbooltts_is_speaking() constvoidtts_pause()voidtts_resume()voidtts_set_utterance_callback(event: TTSUtteranceEvent, callable: Callable)voidtts_speak(text: String, voice: String, volume: int = 50, pitch: float = 1.0, rate: float = 1.0, utterance_id: int = 0, interrupt: bool = false)voidtts_stop()voidunregister_additional_output(object: Object)intvirtual_keyboard_get_height() constvoidvirtual_keyboard_hide()voidvirtual_keyboard_show(existing_text: String, position: Rect2 = Rect2(0, 0, 0, 0), type: VirtualKeyboardType = 0, max_length: int = -1, cursor_start: int = -1, cursor_end: int = -1)voidwarp_mouse(position: Vector2i)boolwindow_can_draw(window_id: int = 0) constintwindow_get_active_popup() constintwindow_get_attached_instance_id(window_id: int = 0) constintwindow_get_current_screen(window_id: int = 0) constboolwindow_get_flag(flag: WindowFlags, window_id: int = 0) constVector2iwindow_get_max_size(window_id: int = 0) constVector2iwindow_get_min_size(window_id: int = 0) constWindowModewindow_get_mode(window_id: int = 0) constintwindow_get_native_handle(handle_type: HandleType, window_id: int = 0) constRect2iwindow_get_popup_safe_rect(window: int) constVector2iwindow_get_position(window_id: int = 0) constVector2iwindow_get_position_with_decorations(window_id: int = 0) constVector3iwindow_get_safe_title_margins(window_id: int = 0) constVector2iwindow_get_size(window_id: int = 0) constVector2iwindow_get_size_with_decorations(window_id: int = 0) constVector2iwindow_get_title_size(title: String, window_id: int = 0) constVSyncModewindow_get_vsync_mode(window_id: int = 0) constboolwindow_is_focused(window_id: int = 0) constboolwindow_is_maximize_allowed(window_id: int = 0) constboolwindow_maximize_on_title_dbl_click() constboolwindow_minimize_on_title_dbl_click() constvoidwindow_move_to_foreground(window_id: int = 0)voidwindow_request_attention(window_id: int = 0)voidwindow_set_current_screen(screen: int, window_id: int = 0)voidwindow_set_drop_files_callback(callback: Callable, window_id: int = 0)voidwindow_set_exclusive(window_id: int, exclusive: bool)voidwindow_set_flag(flag: WindowFlags, enabled: bool, window_id: int = 0)voidwindow_set_ime_active(active: bool, window_id: int = 0)voidwindow_set_ime_position(position: Vector2i, window_id: int = 0)voidwindow_set_input_event_callback(callback: Callable, window_id: int = 0)voidwindow_set_input_text_callback(callback: Callable, window_id: int = 0)voidwindow_set_max_size(max_size: Vector2i, window_id: int = 0)voidwindow_set_min_size(min_size: Vector2i, window_id: int = 0)voidwindow_set_mode(mode: WindowMode, window_id: int = 0)voidwindow_set_mouse_passthrough(region: PackedVector2Array, window_id: int = 0)voidwindow_set_popup_safe_rect(window: int, rect: Rect2i)voidwindow_set_position(position: Vector2i, window_id: int = 0)voidwindow_set_rect_changed_callback(callback: Callable, window_id: int = 0)voidwindow_set_size(size: Vector2i, window_id: int = 0)voidwindow_set_title(title: String, window_id: int = 0)voidwindow_set_transient(window_id: int, parent_window_id: int)voidwindow_set_vsync_mode(vsync_mode: VSyncMode, window_id: int = 0)voidwindow_set_window_buttons_offset(offset: Vector2i, window_id: int = 0)voidwindow_set_window_event_callback(callback: Callable, window_id: int = 0)voidwindow_start_drag(window_id: int = 0)voidwindow_start_resize(edge: WindowResizeEdge, window_id: int = 0) + +枚举 + +enum Feature: + +● FEATURE_GLOBAL_MENU = 0 + 已弃用: 改用 NativeMenu 或 PopupMenu。 +显示服务器支持全局菜单。能够让应用程序在操作系统的顶部栏显示其菜单项。macOS +● FEATURE_SUBWINDOWS = 1 +显示服务器支持多窗口,可以移动到主窗口之外。Windows、macOS、Linux(X11) +● FEATURE_TOUCHSCREEN = 2 +显示服务器支持触屏输入。Windows、Linux(X11)、Android、iOS、Web +● FEATURE_MOUSE = 3 +显示服务器支持鼠标输入。Windows、macOS、Linux(X11/Wayland)、Android、Web +● FEATURE_MOUSE_WARP = 4 +显示服务器支持扭曲鼠标坐标以将鼠标光标限制在一个区域内,但在到达其中一个边缘时循环。Windows, macOS, Linux (X11/Wayland) +● FEATURE_CLIPBOARD = 5 +显示服务器支持剪贴板数据的设置和获取。另见 FEATURE_CLIPBOARD_PRIMARY。Windows、macOS、Linux(X11/Wayland)、Android、iOS、Web +● FEATURE_VIRTUAL_KEYBOARD = 6 +显示服务器支持在请求输入文本但没有物理键盘时弹出虚拟键盘。Android、iOS、Web +● FEATURE_CURSOR_SHAPE = 7 +显示服务器支持将鼠标光标形状设置为与默认不同。Windows、macOS、Linux(X11/Wayland)、Android、Web +● FEATURE_CUSTOM_CURSOR_SHAPE = 8 +显示服务器支持将鼠标光标形状设置为自定义图像。Windows、macOS、Linux(X11/Wayland)、Web +● FEATURE_NATIVE_DIALOG = 9 +显示服务器支持使用操作系统的原生外观生成文本对话框。请参阅 dialog_show()。Windows、macOS +● FEATURE_IME = 10 +显示服务器支持 输入法 ,它通常用于输入中文、日文和韩文文本。这由操作系统处理,而不是由 Godot 处理。Windows, macOS, Linux (X11) +● FEATURE_WINDOW_TRANSPARENCY = 11 +显示服务器支持窗口可以使用逐像素透明,以使它们后面的窗口部分或完全可见。Windows、macOS、Linux(X11/Wayland) +● FEATURE_HIDPI = 12 +显示服务器支持查询操作系统的显示缩放系数。这允许可靠地执行自动 hiDPI 显示器检测,而不是根据屏幕分辨率和报告的显示器 DPI 进行猜测(由于显示器 EDID 损坏,这可能不可靠)。Windows、Linux(Wayland)、macOS +● FEATURE_ICON = 13 +显示服务器支持改变窗口图标(通常显示在左上角)。Windows、macOS、Linux(X11) +● FEATURE_NATIVE_ICON = 14 +显示服务器支持改变窗口图标(通常显示在左上角)。Windows、macOS +● FEATURE_ORIENTATION = 15 +显示服务器支持改变屏幕朝向。Android、iOS +● FEATURE_SWAP_BUFFERS = 16 +显示服务器支持将垂直同步状态改为非默认状态(不支持此功能的平台强制启用垂直同步)。Windows、macOS、Linux(X11/Wayland) +● FEATURE_CLIPBOARD_PRIMARY = 18 +显示服务器支持使用主剪贴板。主剪贴板和 FEATURE_CLIPBOARD 是不同的剪贴板。Linux(X11/Wayland) +● FEATURE_TEXT_TO_SPEECH = 19 +显示服务器支持文字转语音。见 tts_* 方法。Windows、macOS、Linux(X11/Wayland)、Android、iOS、Web +● FEATURE_EXTEND_TO_TITLE = 20 +显示服务器支持将窗口内容扩展到标题。见 WINDOW_FLAG_EXTEND_TO_TITLE。macOS +● FEATURE_SCREEN_CAPTURE = 21 +显示服务器支持读取屏幕像素。见 screen_get_pixel()。 +● FEATURE_STATUS_INDICATOR = 22 +显示服务器支持应用程序状态指示器。 +● FEATURE_NATIVE_HELP = 23 +显示服务器支持本机帮助系统搜索回调。请参阅 help_set_search_callbacks()。 +● FEATURE_NATIVE_DIALOG_INPUT = 24 +显示服务器支持使用操作系统的原生外观生成文本输入对话框。请参阅 dialog_input_text()。Windows、macOS +● FEATURE_NATIVE_DIALOG_FILE = 25 +显示服务器支持使用操作系统的原生外观和操作方式来生成选择文件或目录的对话框。见 file_dialog_show()。Windows、macOS、Linux(X11/Wayland)、Android +● FEATURE_NATIVE_DIALOG_FILE_EXTRA = 26 +显示服务器支持 FEATURE_NATIVE_DIALOG_FILE 的所有功能,并增加了“选项”功能以及对 res:// 和 user:// 路径的原生对话框文件访问。见 file_dialog_show() 和 file_dialog_with_options_show()。Windows、macOS、Linux(X11/Wayland) +● FEATURE_WINDOW_DRAG = 27 +显示服务器支持在需要时发起窗口拖拽和大小调整操作。见 window_start_drag() 和 window_start_resize()。 +● FEATURE_SCREEN_EXCLUDE_FROM_CAPTURE = 28 +显示服务器支持窗口标志 WINDOW_FLAG_EXCLUDE_FROM_CAPTURE。 +● FEATURE_WINDOW_EMBEDDING = 29 +显示服务器支持嵌入其他进程的窗口。Windows、Linux(X11) +● FEATURE_NATIVE_DIALOG_FILE_MIME = 30 +原生文件选择对话框支持使用 MIME 类型作为过滤器。 +● FEATURE_EMOJI_AND_SYMBOL_PICKER = 31 +显示服务器支持系统 Emoji 和符号拾取器。Windows、macOS + +enum MouseMode: + +● MOUSE_MODE_VISIBLE = 0 +如果鼠标光标处于隐藏状态,则使其可见。 +● MOUSE_MODE_HIDDEN = 1 +如果鼠标光标是可见的,则使其隐藏。 + +● MOUSE_MODE_CAPTURED = 2 +捕获鼠标。鼠标将被隐藏,其位置被锁定在窗口管理器窗口的中心。 + +注意:如果你想在这种模式下处理鼠标的移动,则需要使用 InputEventMouseMotion.relative。 + +● MOUSE_MODE_CONFINED = 3 +将鼠标光标限制在游戏窗口内,并使其可见。 +● MOUSE_MODE_CONFINED_HIDDEN = 4 +将鼠标光标限制在游戏窗口内,并使其隐藏。 +● MOUSE_MODE_MAX = 5 +MouseMode 的最大值。 + +enum ScreenOrientation: + +● SCREEN_LANDSCAPE = 0 +默认横屏朝向。 +● SCREEN_PORTRAIT = 1 +默认竖屏朝向。 +● SCREEN_REVERSE_LANDSCAPE = 2 +倒横屏朝向(上下颠倒)。 +● SCREEN_REVERSE_PORTRAIT = 3 +倒竖屏朝向(上下颠倒)。 +● SCREEN_SENSOR_LANDSCAPE = 4 +自动横屏朝向(传感器决定默认或倒向)。 +● SCREEN_SENSOR_PORTRAIT = 5 +自动竖屏朝向(传感器决定默认或倒向)。 +● SCREEN_SENSOR = 6 +自动横屏或竖屏朝向(传感器决定默认或倒向)。 + +enum VirtualKeyboardType: + +● KEYBOARD_TYPE_DEFAULT = 0 +默认文本虚拟键盘。 +● KEYBOARD_TYPE_MULTILINE = 1 +多行虚拟键盘。 +● KEYBOARD_TYPE_NUMBER = 2 +虚拟数字键盘,可用于 PIN 输入。 +● KEYBOARD_TYPE_NUMBER_DECIMAL = 3 +虚拟数字键盘,可用于输入小数。 +● KEYBOARD_TYPE_PHONE = 4 +虚拟手机号码键盘。 +● KEYBOARD_TYPE_EMAIL_ADDRESS = 5 +带有附加键的虚拟键盘,可帮助输入电子邮件地址。 + +● KEYBOARD_TYPE_PASSWORD = 6 +用于输入密码的虚拟键盘。在大多数平台上,这应该会禁用自动完成和自动首字母大写功能。 + +注意:Web 平台不支持。与 KEYBOARD_TYPE_DEFAULT 的行为相同。 + +● KEYBOARD_TYPE_URL = 7 +带有附加键的虚拟键盘,可帮助输入 URL。 + +enum CursorShape: + +● CURSOR_ARROW = 0 +箭头光标形状。这是默认形状,没有指向 LineEdit 和 TextEdit 等会覆盖鼠标指针的节点时显示。 +● CURSOR_IBEAM = 1 +工字光标形状。默认在悬停于 LineEdit 和 TextEdit 等接受文本输入的控件时显示。 +● CURSOR_POINTING_HAND = 2 +指点的手形光标形状。默认在悬停于 LinkButton 或 RichTextLabel 中的 URL 标签时使用。 +● CURSOR_CROSS = 3 +十字光标。应当在用户需要精确瞄准某个元素时显示,例如矩形选择工具和颜色拾取器。 +● CURSOR_WAIT = 4 +等待光标。大多数光标主题会在箭头旁边显示旋转图标。旨在用于非阻塞操作(此时用户可以做其他事情)。另见 CURSOR_BUSY。 +● CURSOR_BUSY = 5 +等待光标。大多数光标主题会把箭头替换为旋转图标。旨在用于阻塞操作(此时用户无法做其他事情)。另见 CURSOR_WAIT。 +● CURSOR_DRAG = 6 +拖动的手形光标。在拖放操作过程中显示。另见 CURSOR_CAN_DROP。 +● CURSOR_CAN_DROP = 7 +“能放下”光标。在拖放操作过程中,如果将鼠标悬停在可以接受拖放事件的 Control 上,就会显示这个光标。大多数光标主题会显示一只正在拖拽的手,旁边有一个箭头符号。另见 CURSOR_DRAG。 +● CURSOR_FORBIDDEN = 8 +禁止光标。在拖放操作过程中,如果将鼠标悬停在不可接受拖放事件的 Control 上,就会显示这个光标。 +● CURSOR_VSIZE = 9 +垂直尺寸调整光标。只在用于悬停的 Control 可以用鼠标调整垂直大小时显示。另见 CURSOR_VSPLIT。 +● CURSOR_HSIZE = 10 +水平尺寸调整光标。只在用于悬停的 Control 可以用鼠标调整水平大小时显示。另见 CURSOR_HSPLIT。 +● CURSOR_BDIAGSIZE = 11 +辅助对角线尺寸调整光标(右上/左下)。只在但悬停的 Control 可以使用鼠标同时在两个轴上调整大小时显示。 +● CURSOR_FDIAGSIZE = 12 +主对角线尺寸调整光标(左上/右下)。只在当悬停的 Control 可以使用鼠标同时在两个轴上调整大小时显示。 +● CURSOR_MOVE = 13 +移动光标。应在能够使用鼠标移动被悬停 Control 时显示。 +● CURSOR_VSPLIT = 14 +垂直分割光标。当光标悬停于 VSplitContainer 等能够使用鼠标调整拆分的垂直大小的 Control 时显示。部分光标主题中,该光标的外观和 CURSOR_VSIZE 一致。 +● CURSOR_HSPLIT = 15 +水平分割光标。当光标悬停于 HSplitContainer 等能够使用鼠标调整拆分的水平大小的 Control 时显示。部分光标主题中,该光标的外观和 CURSOR_HSIZE 一致。 +● CURSOR_HELP = 16 +帮助光标。在大多数光标主题中显示为问号图标,不显示为鼠标光标。应在用户请求对下一次点击的元素提供帮助信息时使用。 +● CURSOR_MAX = 17 +代表 CursorShape 枚举的大小。 + +enum FileDialogMode: + +● FILE_DIALOG_MODE_OPEN_FILE = 0 +该原生对话框只允许选择一个文件。 +● FILE_DIALOG_MODE_OPEN_FILES = 1 +该原生对话框允许选择多个文件。 +● FILE_DIALOG_MODE_OPEN_DIR = 2 +该原生对话框只允许选择一个目录,不允许选择任何文件。 +● FILE_DIALOG_MODE_OPEN_ANY = 3 +该原生对话框允许选择一个文件或目录。 +● FILE_DIALOG_MODE_SAVE_FILE = 4 +当文件存在时,原生对话框会发出警告。 + +enum WindowMode: + +● WINDOW_MODE_WINDOWED = 0 +窗口模式,即 Window 不占据整个屏幕(除非设置为屏幕的大小)。 +● WINDOW_MODE_MINIMIZED = 1 +最小化窗口模式,即 Window 在窗口管理器的窗口列表中既不可见也不可用。通常发生在按下最小化按钮时。 +● WINDOW_MODE_MAXIMIZED = 2 +最大化窗口模式,即 Window 会占据整个屏幕区域,任务栏除外,并且会显示边框。通常发生在按下最大化按钮时。 + +● WINDOW_MODE_FULLSCREEN = 3 +具有完整多窗口支持的全屏模式。 + +全屏窗口覆盖屏幕的整个显示区域,且没有任何装饰。显示的视频模式没有更改。 + +在 Android 上:将启用沉浸模式。 + +在 Windows 上:多窗口全屏模式具有 1px 宽的颜色为 渲染 > 环境 > 默认 > 默认清屏颜色 的边框。 + +在 macOS 上:使用新桌面来显示正在运行的项目。 + +注意:无论平台如何,启用全屏都会更改窗口大小以匹配显示器的大小。因此,请确保你的项目在启用全屏模式时支持多种分辨率 。 + +● WINDOW_MODE_EXCLUSIVE_FULLSCREEN = 4 +单窗口全屏模式。这种模式开销较小,但一次只能在给定屏幕上打开一个窗口(打开子窗口或切换应用程序会触发全屏过渡)。 + +全屏窗口会覆盖屏幕的整个显示区域,没有边框或装饰。显示视频模式没有改变。 + +在 Android 上:将启用沉浸模式。 + +在 Windows 上:取决于视频驱动程序,全屏过渡可能会导致屏幕暂时变黑。 + +在 macOS 上:一个新的桌面用于显示正在运行的项目。当鼠标指针悬停在屏幕边缘时,独占全屏模式会阻止 Dock 和 Menu 出现。 + +在 Linux(X11)上:独占全屏模式会绕过合成器。 + +在 Linux(Wayland)上:等价于 WINDOW_MODE_FULLSCREEN。 + +注意:无论平台如何,启用全屏都会更改窗口大小以匹配显示器的大小。因此,确保你的项目在启用全屏模式时支持多个分辨率 。 + +enum WindowFlags: + +● WINDOW_FLAG_RESIZE_DISABLED = 0 +该窗口不能通过拖动其调整大小的手柄来调整大小。但仍然可以使用 window_set_size() 调整窗口大小。全屏窗口会忽略该标志。 +● WINDOW_FLAG_BORDERLESS = 1 +该窗口没有原生标题栏和其他装饰。全屏窗口会忽略该标志。 +● WINDOW_FLAG_ALWAYS_ON_TOP = 2 +该窗口悬浮在所有其他窗口之上。全屏窗口会忽略该标志。 + +● WINDOW_FLAG_TRANSPARENT = 3 +该窗口背景可以是透明的。 + +注意:如果 is_window_transparency_available() 返回 false,则该标志无效。 + +注意:Linux (X11/Wayland)、macOS 和 Windows 上实现了透明支持,但可用性可能因 GPU 驱动程序、显示管理器和合成器功能而异。 + +● WINDOW_FLAG_NO_FOCUS = 4 +该窗口无法获得焦点。无聚焦窗口会忽略除鼠标点击外的所有输入。 +● WINDOW_FLAG_POPUP = 5 +窗口是菜单或 OptionButton 下拉菜单的一部分。当窗口可见时,不能更改该标志。活动的弹出窗口会以独占的形式接收所有输入,但不会从其父窗口窃取焦点。当在其外部点击或切换应用程序时,弹出窗口将会自动关闭。 弹出窗口必须已经设置了临时父级(参见 window_set_transient())。 + +● WINDOW_FLAG_EXTEND_TO_TITLE = 6 +窗口内容扩展到窗口的全部大小。与无边框窗口不同,框架仍保持不变,可以调整窗口大小,标题栏是透明的,但具有最小化/最大化/关闭按钮。 + +使用 window_set_window_buttons_offset() 调整最小化/最大化/关闭按钮的偏移量。 + +使用 window_get_safe_title_margins() 确定标题栏下方未被装饰覆盖的区域。 + +注意:该标志仅在 macOS 上实现。 + +● WINDOW_FLAG_MOUSE_PASSTHROUGH = 7 +所有鼠标事件都被传递到同一应用程序的底层窗口。 + +● WINDOW_FLAG_SHARP_CORNERS = 8 +覆盖了窗口样式,强制使用尖角。 + +注意:该标志仅在 Windows(11)上实现。 + +● WINDOW_FLAG_EXCLUDE_FROM_CAPTURE = 9 +在 screen_get_image()、screen_get_image_rect() 和 screen_get_pixel() 的截图中排除该窗口。 + +注意:该标志在 macOS 和 Windows 上实现。 + +注意:设置该标志不会阻止其他应用进行截图,不应用作安全措施。 + +● WINDOW_FLAG_MAX = 10 +WindowFlags 的最大值。 + +enum WindowEvent: + +● WINDOW_EVENT_MOUSE_ENTER = 0 +当鼠标指针进入该窗口时发送。 +● WINDOW_EVENT_MOUSE_EXIT = 1 +当鼠标指针退出该窗口时发送。 +● WINDOW_EVENT_FOCUS_IN = 2 +当窗口获得焦点时发送。 +● WINDOW_EVENT_FOCUS_OUT = 3 +当窗口失去焦点时发送。 +● WINDOW_EVENT_CLOSE_REQUEST = 4 +当用户试图关闭该窗口时发送(例如按下关闭按钮)。 + +● WINDOW_EVENT_GO_BACK_REQUEST = 5 +当按下设备的“后退”按钮时发送。 + +注意:该事件仅在 Android 上实现。 + +● WINDOW_EVENT_DPI_CHANGE = 6 +当窗口被移动到具有不同 DPI 的显示器上,或者显示器的 DPI 更改时发送。 + +注意:该标志仅在 macOS 上实现。 + +● WINDOW_EVENT_TITLEBAR_CHANGE = 7 +当窗口标题栏的装饰改变时发送(例如 WINDOW_FLAG_EXTEND_TO_TITLE 被设置或窗口进入/退出全屏模式)。 + +注意:该标志仅在 macOS 上实现。 + +enum WindowResizeEdge: + +● WINDOW_EDGE_TOP_LEFT = 0 +窗口左上边缘。 +● WINDOW_EDGE_TOP = 1 +窗口上边缘。 +● WINDOW_EDGE_TOP_RIGHT = 2 +窗口右上边缘。 +● WINDOW_EDGE_LEFT = 3 +窗口左边缘。 +● WINDOW_EDGE_RIGHT = 4 +窗口右边缘。 +● WINDOW_EDGE_BOTTOM_LEFT = 5 +窗口左下边缘。 +● WINDOW_EDGE_BOTTOM = 6 +窗口下边缘。 +● WINDOW_EDGE_BOTTOM_RIGHT = 7 +窗口右下边缘。 +● WINDOW_EDGE_MAX = 8 +代表 WindowResizeEdge 枚举的大小。 + +enum VSyncMode: + +● VSYNC_DISABLED = 0 +没有垂直同步,这意味着引擎将尽可能快地显示帧(可能会有可见的撕裂)。帧速率是未限制的(不考虑 Engine.max_fps)。 +● VSYNC_ENABLED = 1 +默认的垂直同步模式,图像只在垂直消隐间隔显示(没有可见的撕裂)。帧速率受显示器刷新率的限制(不考虑 Engine.max_fps)。 +● VSYNC_ADAPTIVE = 2 +当帧速率降至屏幕刷新率以下以减少卡顿(可能有可见的撕裂)时,行为类似于 VSYNC_DISABLED。否则,启用垂直同步以避免撕裂。帧速率受显示器刷新率的限制(不考虑 Engine.max_fps)。使用兼容渲染方法时表现得像 VSYNC_ENABLED。 + +● VSYNC_MAILBOX = 3 +在垂直消隐间隔显示队列中的最新图像,同时对其他图像渲染(没有可见的撕裂)。帧速率是未限制的(不考虑 Engine.max_fps)。 + +虽然不能保证,但可以尽可能快地渲染图像,这可能会减少输入滞后(也称为“快速”V-Sync 模式)。VSYNC_MAILBOX 在渲染的帧数至少是显示器刷新率的两倍时效果最佳。使用兼容渲染方法时表现得像 VSYNC_ENABLED。 + +enum HandleType: + +● DISPLAY_HANDLE = 0 +显示器句柄: + +- Linux(X11):显示器的 X11::Display*。 + +- Linux(Wayland):显示器的 wl_display。 + +- Android:显示器的 EGLDisplay。 + +● WINDOW_HANDLE = 1 +窗口句柄: + +- Windows:窗口的 HWND。 + +- Linux(X11):窗口的 X11::Window*。 + +- Linux(Wayland):窗口的 wl_surface。 + +- macOS:窗口的 NSWindow*。 + +- iOS:视图控制器的 UIViewController*。 + +- Android:Activity 的 jObject。 + +● WINDOW_VIEW = 2 +窗口视图: + +- Windows:窗口的 HDC(仅适用于 Compatibility 渲染器)。 + +- macOS:窗口主视图的 NSView*。 + +- iOS:窗口主视图的 UIView*。 + +● OPENGL_CONTEXT = 3 +OpenGL 上下文(仅适用于 Compatibility 渲染器): + +- Windows:窗口的 HGLRC(原生 GL)或窗口的 EGLContext(ANGLE)。 + +- Linux(X11):窗口的 GLXContext*。 + +- Linux(Wayland):窗口的 EGLContext。 + +- macOS:窗口的 NSOpenGLContext*(原生 GL)或窗口的 EGLContext(ANGLE)。 + +- Android:窗口的 EGLContext。 + +● EGL_DISPLAY = 4 +- Windows:窗口的 EGLDisplay(ANGLE)。 + +- macOS:窗口的 EGLDisplay(ANGLE)。 + +- Linux(Wayland):窗口的 EGLDisplay。 + +● EGL_CONFIG = 5 +- Windows:窗口的 EGLConfig(ANGLE)。 + +- macOS:窗口的 EGLConfig(ANGLE)。 + +- Linux(Wayland):窗口的 EGLConfig。 + +enum TTSUtteranceEvent: + +● TTS_UTTERANCE_STARTED = 0 +发言开始。 +● TTS_UTTERANCE_ENDED = 1 +发言顺利结束。 +● TTS_UTTERANCE_CANCELED = 2 +发言取消,或者 TTS 服务无法处理。 +● TTS_UTTERANCE_BOUNDARY = 3 +发言到达单词或句子的边界。 + +常量 + +● SCREEN_WITH_MOUSE_FOCUS = -4 +表示包含鼠标指针的屏幕。 + +注意:在 Linux(Wayland)上,该常量始终代表索引 0 处的屏幕。 + +● SCREEN_WITH_KEYBOARD_FOCUS = -3 +表示包含具有键盘焦点的窗口的屏幕。 + +注意:在 Linux(Wayland)上,该常量始终代表索引 0 处的屏幕。 + +● SCREEN_PRIMARY = -2 +代表主屏幕。 + +注意:在 Linux(Wayland)上,该常量始终代表索引 0 处的屏幕。 + +● SCREEN_OF_MAIN_WINDOW = -1 +代表主窗口所在的屏幕。这通常是允许指定多个屏幕之一的函数中的默认值。 + +注意:在 Linux(Wayland)上,该常量始终代表索引 0 处的屏幕。 + +● MAIN_WINDOW_ID = 0 +主窗口的 ID,可以传给需要 window_id 的方法,该窗口由引擎生成。 +● INVALID_WINDOW_ID = -1 +指向一个不存在窗口的 ID。如果没有窗口与请求的结果相匹配,某些 DisplayServer 方法将返回这个 ID。 +● INVALID_INDICATOR_ID = -1 +引用不存在的应用程序状态指示器的 ID。 + + +方法说明 + + +● void beep() const + +播放操作系统的“哔”声,需要操作系统支持。因为发出声音的是操作系统,所以即便应用程序被静音也能听到“哔”声。这一功能可能被用户在操作系统层面禁用。 + +注意:该方法在 macOS、Linux(X11/Wayland)和 Windows。 + + +● String clipboard_get() const + +如果可能,将用户的剪贴板作为字符串返回。 + + +● Image clipboard_get_image() const + +如果可能,将用户的剪贴板作为图像返回。 + +注意:该方法使用复制的像素数据(例如来自图像编辑软件或 Web 浏览器的数据),而不是从文件资源管理器复制的图像文件。 + + +● String clipboard_get_primary() const + +如果可能的话,将用户的主 剪贴板作为字符串返回。这是当用户在任何应用程序中选择文本时设置的剪贴板,而不是在按下 Ctrl + C 时设置的。然后可以通过在支持主剪贴板机制的任何应用程序中,通过点击鼠标中键来粘贴该剪贴板数据。 + +注意:这个方法只在 Linux(X11/Wayland)上实现。 + + +● bool clipboard_has() const + +如果用户的剪贴板中有文本内容,则返回 true。 + + +● bool clipboard_has_image() const + +如果用户的剪贴板中有图像内容,则返回 true。 + + +● void clipboard_set(clipboard: String) + +将用户的剪贴板内容设置为给定的字符串。 + + +● void clipboard_set_primary(clipboard_primary: String) + +将用户的主剪贴板 内容设置为给定的字符串。这是用户在应用程序中选中文本时设置的剪贴板,不是按 Ctrl + C 时设置的。设置后可以在任何支持主剪贴板机制的应用程序中通过点击鼠标中键粘贴剪贴板数据。 + +注意:这个方法只在 Linux(X11/Wayland)上实现。 + + +● int create_status_indicator(icon: Texture2D, tooltip: String, callback: Callable) + +新建应用程序状态指示器,可以指定图标、工具提示以及激活回调。 + +callback 应该接受两个参数:按下的鼠标按键(MouseButton 常量)以及点击位置(屏幕坐标 Vector2i)。 + + +● CursorShape cursor_get_shape() const + +返回默认鼠标光标形状,由 cursor_set_shape() 设置。 + + +● void cursor_set_custom_image(cursor: Resource, shape: CursorShape = 0, hotspot: Vector2 = Vector2(0, 0)) + +为给定的形状 shape 设置自定义鼠标指针图像。这意味着用户的操作系统和鼠标光标主题不再影响鼠标光标的外观。 + +cursor 可以是 Texture2D 或 Image,并且它不应大于 256×256 才能正确显示。还可以选择设置 hotspot 以偏移图像相对于点击点的位置。默认情况下,hotspot 被设置为图像的左上角。另见 cursor_set_shape()。 + + +● void cursor_set_shape(shape: CursorShape) + +设置默认的鼠标光标形状。光标的外观将根据用户的操作系统和鼠标光标主题而变化。另见 cursor_get_shape() 和 cursor_set_custom_image()。 + + +● void delete_status_indicator(id: int) + +移除应用程序状态指示器。 + + +● Error dialog_input_text(title: String, description: String, existing_text: String, callback: Callable) + +显示文本输入对话框,该对话框使用操作系统原生外观。callback 应接受包含文本字段内容的单个 String 参数。 + +注意:如果显示服务器具有 FEATURE_NATIVE_DIALOG_INPUT 功能,则实现该方法。支持的平台包括 macOS、Windows 和 Android。 + + +● Error dialog_show(title: String, description: String, buttons: PackedStringArray, callback: Callable) + +显示文本对话框,该对话框使用操作系统原生外观。callback 应接受与按下按钮的索引相对应的单个 int 参数。 + +注意:如果显示服务器具有 FEATURE_NATIVE_DIALOG 功能,则实现该方法。支持的平台包括 macOS、Windows 和 Android。 + + +● void enable_for_stealing_focus(process_id: int) + +让进程 PID process_id 窃取该窗口的焦点。换句话说,会禁用操作系统对指定 PID 的焦点窃取保护。 + +注意:该方法仅在 Windows 上实现。 + + +● Error file_dialog_show(title: String, current_directory: String, filename: String, show_hidden: bool, mode: FileDialogMode, filters: PackedStringArray, callback: Callable) + +显示操作系统原生对话框,用于选择文件系统中的文件或目录。 + +filters 数组中的每个过滤器字符串都应该使用类似 *.png,*.jpg,*.jpeg;图像文件;image/png,image/jpeg 的格式。过滤器的描述文本不是必填项,可以省略。建议同时设置文件扩展名和 MIME 类型。另见 FileDialog.filters。 + +回调的参数如下:status: bool, selected_paths: PackedStringArray, selected_filter_index: int。在 Android 平台,回调参数 selected_filter_index 始终为 0。 + +注意:如果显示服务器具有 FEATURE_NATIVE_DIALOG 功能,则该方法已被实现。支持的平台包括 Linux(X11/Wayland)、Windows、macOS 和 Android。 + +注意:current_directory 可能会被忽略。 + +注意:内嵌文件对话框和 Windows 文件对话框仅支持文件扩展名,而 Android、Linux 和 macOS 的文件对话框还支持 MIME 类型。 + +注意:在 Android 和 Linux 上,show_hidden 会被忽略。 + +注意:在 Android 和 macOS 上,原生文件对话框没有标题。 + +注意:在 macOS 上,沙盒应用程序将保存安全范围的书签,以保留对多个会话中打开的文件夹的访问权限。请使用 OS.get_granted_permissions() 获取已保存书签的列表。 + + +● Error file_dialog_with_options_show(title: String, current_directory: String, root: String, filename: String, show_hidden: bool, mode: FileDialogMode, filters: PackedStringArray, options: Array[Dictionary], callback: Callable) + +显示操作系统原生对话框,用于使用其他用户可选选项,选择文件系统中的文件或目录。 + +filters 数组中的每个过滤字符串都应该使用类似 png,*.jpg,*.jpeg;图像文件;image/png,image/jpeg 的格式。过滤器的描述文本不是必填项,可以省略。建议同时设置文件扩展名和 MIME 类型。另见 FileDialog.filters。 + +options 是具有以下字段的 Dictionary 的数组: + +- "name" - 选项的名称 String。 + +- "values" - 值的 PackedStringArray。如果为空,则使用布尔选项(复选框)。 + +- "default" - 默认选择的选项索引(int)或默认布尔值(bool)。 + +回调具有以下参数:status: bool, selected_paths: PackedStringArray, selected_filter_index: int, selected_option: Dictionary。 + +注意:如果显示服务器具有 FEATURE_NATIVE_DIALOG 功能,则该方法已被实现。支持的平台包括 Linux(X11/Wayland)、Windows、macOS。 + +注意:current_directory 可能会被忽略。 + +注意:内嵌文件对话框和 Windows 文件对话框仅支持文件扩展名,而 Android、Linux 和 macOS 的文件对话框还支持 MIME 类型。 + +注意:在 Linux(X11) 上,show_hidden 会被忽略。 + +注意:在 macOS 上,原生文件对话框没有标题。 + +注意:在 macOS 上,沙盒应用程序将保存安全范围的书签,以保留对多个会话中打开的文件夹的访问权限。请使用 OS.get_granted_permissions() 获取已保存书签的列表。 + + +● void force_process_and_drop_events() + +强制窗口管理器进行处理,会忽略所有 InputEvent。另见 process_events()。 + +注意:这个方法在 Windows 和 macOS 上实现。 + + +● Color get_accent_color() const + +返回操作系统主题色。如果主题色未知,则返回 Color(0, 0, 0, 0)。 + +注意:该方法在 macOS、Windows 和 Android 上实现。 + + +● Color get_base_color() const + +返回操作系统主题基色(默认控件背景)。如果基色未知,则返回 Color(0, 0, 0, 0)。 + +注意:该方法在 macOS、Windows 和 Android 上实现。 + + +● Array[Rect2] get_display_cutouts() const + +返回 Rect2 的 Array,其中每个都是显示切口或凹口的边界矩形。这些是相机和传感器使用的无边框屏幕上的非功能区域。如果设备没有切口,则返回一个空数组。另见 get_display_safe_area()。 + +注意:目前仅在 Android 上实现。其他平台将返回一个空数组,即使它们确实有显示切口或凹口。 + + +● Rect2i get_display_safe_area() const + +返回显示器上未被遮挡的区域,交互式空间应当在此区域中渲染。另见 get_display_cutouts()。 + +注意:当前仅在 Android 和 iOS 上实现。其他平台上会使用 screen_get_usable_rect(SCREEN_OF_MAIN_WINDOW) 作为回退返回。另见 screen_get_usable_rect()。 + + +● int get_keyboard_focus_screen() const + +返回包含具有键盘焦点的窗口的屏幕索引,如果没有被聚焦的窗口,则返回主屏幕。 + + +● String get_name() const + +返回当前使用的 DisplayServer 的名称。大多数操作系统只有一种 DisplayServer,但 Linux 可以使用多种 DisplayServer(目前有 X11 和 Wayland 两种)。 + +内置显示服务器的名称有 Windows、macOS、X11(Linux)、Wayland(Linux)、Android、iOS、web(HTML5)、headless(使用 --headless 命令行参数 启动)。 + + +● int get_primary_screen() const + +返回主屏幕的索引。 + + +● int get_screen_count() const + +返回可用的显示器数量。 + + +● int get_screen_from_rect(rect: Rect2) const + +返回与给定矩形重合最多的屏幕的索引。如果矩形没有与任何屏幕重合或面积为零,则返回 -1。 + + +● bool get_swap_cancel_ok() + +如果对话框中的确定和取消按钮进行了交换,则返回 true。在 Windows 上默认启用,从而遵循界面规范,可以使用 GUI > 通用 > 交换取消确定 开关。 + +注意:由 DisplayServer.dialog_show() 等生成的原生对话框不受影响。 + + +● int get_window_at_screen_position(position: Vector2i) const + +返回位于指定屏幕位置 position 的窗口 ID(单位为像素)。使用多个监视器时,屏幕位置是相对于虚拟桌面区域的位置。如果多监视器中使用了不同的屏幕分辨率或朝向,原点有可能位于所有显示器之外,类似于: + +* (0, 0) +-------+ + | | ++-------------+ | | +| | | | +| | | | ++-------------+ +-------+ + + +● PackedInt32Array get_window_list() const + +返回属于该进程的 Godot 窗口 ID 列表。 + +注意:这个列表中不含原生对话框。 + + +● int global_menu_add_check_item(menu_root: String, label: String, callback: Callable = Callable(), key_callback: Callable = Callable(), tag: Variant = null, accelerator: Key = 0, index: int = -1) + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +向 ID 为 menu_root 的全局菜单添加新的可勾选菜单项,显示的文本为 label。 + +返回插入菜单项的索引,不保证与 index 的值相同。 + +还可以定义键盘快捷键 accelerator,按下后即便该菜单按钮尚未打开,也会进行触发。accelerator 通常是将 KeyModifierMask 和 Key 用按位或操作进行的组合,例如 KEY_MASK_CTRL | KEY_A(Ctrl + A)。 + +注意:callback 和 key_callback Callable 均只接受一个 Variant 参数,传入 Callable 的参数是传给 tag 的参数。 + +注意:该方法仅在 macOS 上实现。 + +支持的系统菜单 ID: + +"_main" - 主菜单(macOS)。 +"_dock" - 程序坞弹出菜单(macOS)。 +"_apple" - Apple 菜单(macOS,在“服务”之前添加的自定义项目)。 +"_window" - 窗口菜单(macOS,“将所有内容置于前面”之后添加的自定义项目)。 +"_help" - 帮助菜单 (macOS)。 + + +● int global_menu_add_icon_check_item(menu_root: String, icon: Texture2D, label: String, callback: Callable = Callable(), key_callback: Callable = Callable(), tag: Variant = null, accelerator: Key = 0, index: int = -1) + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +向 ID 为 menu_root 的全局菜单添加新的可勾选菜单项,显示的文本为 label,图标为 icon。 + +返回插入菜单项的索引,不保证与 index 的值相同。 + +还可以定义键盘快捷键 accelerator,按下后即便该菜单按钮尚未打开,也会进行触发。accelerator 通常是将 KeyModifierMask 和 Key 用按位或操作进行的组合,例如 KEY_MASK_CTRL | KEY_A(Ctrl + A)。 + +注意:callback 和 key_callback Callable 均只接受一个 Variant 参数,传入 Callable 的参数是传给 tag 的参数。 + +注意:该方法仅在 macOS 上实现。 + +支持的系统菜单 ID: + +"_main" - 主菜单(macOS)。 +"_dock" - 程序坞弹出菜单(macOS)。 +"_apple" - Apple 菜单(macOS,在“服务”之前添加的自定义项目)。 +"_window" - 窗口菜单(macOS,“将所有内容置于前面”之后添加的自定义项目)。 +"_help" - 帮助菜单 (macOS)。 + + +● int global_menu_add_icon_item(menu_root: String, icon: Texture2D, label: String, callback: Callable = Callable(), key_callback: Callable = Callable(), tag: Variant = null, accelerator: Key = 0, index: int = -1) + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +向 ID 为 menu_root 的全局菜单添加新的菜单项,显示的文本为 label,图标为 icon。 + +返回插入菜单项的索引,不保证与 index 的值相同。 + +还可以定义键盘快捷键 accelerator,按下后即便该菜单按钮尚未打开,也会进行触发。accelerator 通常是将 KeyModifierMask 和 Key 用按位或操作进行的组合,例如 KEY_MASK_CTRL | KEY_A(Ctrl + A)。 + +注意:callback 和 key_callback Callable 均只接受一个 Variant 参数,传入 Callable 的参数是传给 tag 的参数。 + +注意:该方法仅在 macOS 上实现。 + +支持的系统菜单 ID: + +"_main" - 主菜单(macOS)。 +"_dock" - 程序坞弹出菜单(macOS)。 +"_apple" - Apple 菜单(macOS,在“服务”之前添加的自定义项目)。 +"_window" - 窗口菜单(macOS,“将所有内容置于前面”之后添加的自定义项目)。 +"_help" - 帮助菜单 (macOS)。 + + +● int global_menu_add_icon_radio_check_item(menu_root: String, icon: Texture2D, label: String, callback: Callable = Callable(), key_callback: Callable = Callable(), tag: Variant = null, accelerator: Key = 0, index: int = -1) + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +向 ID 为 menu_root 的全局菜单添加新的单选菜单项,显示的文本为 label,图标为 icon。 + +返回插入菜单项的索引,不保证与 index 的值相同。 + +还可以定义键盘快捷键 accelerator,按下后即便该菜单按钮尚未打开,也会进行触发。accelerator 通常是将 KeyModifierMask 和 Key 用按位或操作进行的组合,例如 KEY_MASK_CTRL | KEY_A(Ctrl + A)。 + +注意:单选菜单项只负责显示选中标记,并没有任何内置检查行为,必须手动进行选中、取消选中的操作。关于如何进行控制的更多信息见 global_menu_set_item_checked()。 + +注意:callback 和 key_callback Callable 均只接受一个 Variant 参数,传入 Callable 的参数是传给 tag 的参数。 + +注意:该方法仅在 macOS 上实现。 + +支持的系统菜单 ID: + +"_main" - 主菜单(macOS)。 +"_dock" - 程序坞弹出菜单(macOS)。 +"_apple" - Apple 菜单(macOS,在“服务”之前添加的自定义项目)。 +"_window" - 窗口菜单(macOS,“将所有内容置于前面”之后添加的自定义项目)。 +"_help" - 帮助菜单 (macOS)。 + + +● int global_menu_add_item(menu_root: String, label: String, callback: Callable = Callable(), key_callback: Callable = Callable(), tag: Variant = null, accelerator: Key = 0, index: int = -1) + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +向 ID 为 menu_root 的全局菜单添加新的菜单项,显示的文本为 label。 + +返回插入菜单项的索引,不保证与 index 的值相同。 + +还可以定义键盘快捷键 accelerator,按下后即便该菜单按钮尚未打开,也会进行触发。accelerator 通常是将 KeyModifierMask 和 Key 用按位或操作进行的组合,例如 KEY_MASK_CTRL | KEY_A(Ctrl + A)。 + +注意:callback 和 key_callback Callable 均只接受一个 Variant 参数,传入 Callable 的参数是传给 tag 的参数。 + +注意:该方法仅在 macOS 上实现。 + +支持的系统菜单 ID: + +"_main" - 主菜单(macOS)。 +"_dock" - 程序坞弹出菜单(macOS)。 +"_apple" - Apple 菜单(macOS,在“服务”之前添加的自定义项目)。 +"_window" - 窗口菜单(macOS,“将所有内容置于前面”之后添加的自定义项目)。 +"_help" - 帮助菜单 (macOS)。 + + +● int global_menu_add_multistate_item(menu_root: String, label: String, max_states: int, default_state: int, callback: Callable = Callable(), key_callback: Callable = Callable(), tag: Variant = null, accelerator: Key = 0, index: int = -1) + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +向 ID 为 menu_root 的全局菜单添加新的菜单项,显示的文本为 label。 + +与常规的二态菜单项不同,多状态菜单项的状态可以多于两个,由 max_states 定义。每点击或激活该菜单项一次,状态就会加一。默认值由 default_state 定义。 + +返回插入菜单项的索引,不保证与 index 的值相同。 + +还可以定义键盘快捷键 accelerator,按下后即便该菜单按钮尚未打开,也会进行触发。accelerator 通常是将 KeyModifierMask 和 Key 用按位或操作进行的组合,例如 KEY_MASK_CTRL | KEY_A(Ctrl + A)。 + +注意:默认情况下不会展示当前菜单项的状态,应该手动更改。 + +注意:callback 和 key_callback Callable 均只接受一个 Variant 参数,传入 Callable 的参数是传给 tag 的参数。 + +注意:该方法仅在 macOS 上实现。 + +支持的系统菜单 ID: + +"_main" - 主菜单(macOS)。 +"_dock" - 程序坞弹出菜单(macOS)。 +"_apple" - Apple 菜单(macOS,在“服务”之前添加的自定义项目)。 +"_window" - 窗口菜单(macOS,“将所有内容置于前面”之后添加的自定义项目)。 +"_help" - 帮助菜单 (macOS)。 + + +● int global_menu_add_radio_check_item(menu_root: String, label: String, callback: Callable = Callable(), key_callback: Callable = Callable(), tag: Variant = null, accelerator: Key = 0, index: int = -1) + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +向 ID 为 menu_root 的全局菜单添加新的单选菜单项,显示的文本为 label。 + +返回插入菜单项的索引,不保证与 index 的值相同。 + +还可以定义键盘快捷键 accelerator,按下后即便该菜单按钮尚未打开,也会进行触发。accelerator 通常是将 KeyModifierMask 和 Key 用按位或操作进行的组合,例如 KEY_MASK_CTRL | KEY_A(Ctrl + A)。 + +注意:单选菜单项只负责显示选中标记,并没有任何内置检查行为,必须手动进行选中、取消选中的操作。关于如何进行控制的更多信息见 global_menu_set_item_checked()。 + +注意:callback 和 key_callback Callable 均只接受一个 Variant 参数,传入 Callable 的参数是传给 tag 的参数。 + +注意:该方法仅在 macOS 上实现。 + +支持的系统菜单 ID: + +"_main" - 主菜单(macOS)。 +"_dock" - 程序坞弹出菜单(macOS)。 +"_apple" - Apple 菜单(macOS,在“服务”之前添加的自定义项目)。 +"_window" - 窗口菜单(macOS,“将所有内容置于前面”之后添加的自定义项目)。 +"_help" - 帮助菜单 (macOS)。 + + +● int global_menu_add_separator(menu_root: String, index: int = -1) + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +向 ID 为 menu_root 的全局菜单添加分隔符。分隔符也拥有索引。 + +返回插入菜单项的索引,不保证与 index 的值相同。 + +注意:该方法仅在 macOS 上实现。 + +支持的系统菜单 ID: + +"_main" - 主菜单(macOS)。 +"_dock" - 程序坞弹出菜单(macOS)。 +"_apple" - Apple 菜单(macOS,在“服务”之前添加的自定义项目)。 +"_window" - 窗口菜单(macOS,“将所有内容置于前面”之后添加的自定义项目)。 +"_help" - 帮助菜单 (macOS)。 + + +● int global_menu_add_submenu_item(menu_root: String, label: String, submenu: String, index: int = -1) + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +向 ID 为 menu_root 的全局菜单添加作为子菜单的菜单项。submenu 参数为全局菜单根菜单项的 ID,会在点击该菜单项时显示 + +返回插入菜单项的索引,不保证与 index 的值相同。 + +注意:该方法仅在 macOS 上实现。 + +支持的系统菜单 ID: + +"_main" - 主菜单(macOS)。 +"_dock" - 程序坞弹出菜单(macOS)。 +"_apple" - Apple 菜单(macOS,在“服务”之前添加的自定义项目)。 +"_window" - 窗口菜单(macOS,“将所有内容置于前面”之后添加的自定义项目)。 +"_help" - 帮助菜单 (macOS)。 + + +● void global_menu_clear(menu_root: String) + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +移除 ID 为 menu_root 的全局菜单中的所有菜单项。 + +注意:该方法仅在 macOS 上实现。 + +支持的系统菜单 ID: + +"_main" - 主菜单(macOS)。 +"_dock" - 程序坞弹出菜单(macOS)。 +"_apple" - Apple 菜单(macOS,在“服务”之前添加的自定义项目)。 +"_window" - 窗口菜单(macOS,“将所有内容置于前面”之后添加的自定义项目)。 +"_help" - 帮助菜单 (macOS)。 + + +● Key global_menu_get_item_accelerator(menu_root: String, idx: int) const + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +返回索引为 idx 的菜单项的快捷键。快捷键是能够激活该菜单项的特殊按键组合,无论该控件是否有焦点。 + +注意:该方法仅在 macOS 上实现。 + + +● Callable global_menu_get_item_callback(menu_root: String, idx: int) const + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +返回索引为 idx 的菜单项的回调。 + +注意:该方法仅在 macOS 上实现。 + + +● int global_menu_get_item_count(menu_root: String) const + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +返回 ID 为 menu_root 的全局菜单中菜单项的数量。 + +注意:该方法仅在 macOS 上实现。 + + +● Texture2D global_menu_get_item_icon(menu_root: String, idx: int) const + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +返回索引为 idx 的菜单项的图标。 + +注意:该方法仅在 macOS 上实现。 + + +● int global_menu_get_item_indentation_level(menu_root: String, idx: int) const + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +返回索引为 idx 的菜单项的水平偏移量。 + +注意:该方法仅在 macOS 上实现。 + + +● int global_menu_get_item_index_from_tag(menu_root: String, tag: Variant) const + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +返回标签为指定的 tag 的菜单项的索引。引擎会自动为每个菜单项分配索引,无法手动设置。 + +注意:该方法仅在 macOS 上实现。 + + +● int global_menu_get_item_index_from_text(menu_root: String, text: String) const + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +返回文本为指定的 text 的菜单项的索引。引擎会自动为每个菜单项分配索引,无法手动设置。 + +注意:该方法仅在 macOS 上实现。 + + +● Callable global_menu_get_item_key_callback(menu_root: String, idx: int) const + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +返回索引为 idx 的菜单项的快捷键回调。 + +注意:该方法仅在 macOS 上实现。 + + +● int global_menu_get_item_max_states(menu_root: String, idx: int) const + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +返回多状态项的状态数。详见 global_menu_add_multistate_item()。 + +注意:该方法仅在 macOS 上实现。 + + +● int global_menu_get_item_state(menu_root: String, idx: int) const + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +返回多状态项的状态。详见 global_menu_add_multistate_item()。 + +注意:该方法仅在 macOS 上实现。 + + +● String global_menu_get_item_submenu(menu_root: String, idx: int) const + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +返回索引为 idx 的菜单项的子菜单 ID。关于如何添加子菜单的更多信息见 global_menu_add_submenu_item()。 + +注意:该方法仅在 macOS 上实现。 + + +● Variant global_menu_get_item_tag(menu_root: String, idx: int) const + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +返回指定菜单项的元数据,可能是任何类型。元数据可以使用 global_menu_set_item_tag() 设置,可以方法地为菜单项关联上下文数据。 + +注意:该方法仅在 macOS 上实现。 + + +● String global_menu_get_item_text(menu_root: String, idx: int) const + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +返回索引为 idx 的菜单项的文本。 + +注意:该方法仅在 macOS 上实现。 + + +● String global_menu_get_item_tooltip(menu_root: String, idx: int) const + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +返回索引为 idx 的菜单项所关联的工具提示。 + +注意:该方法仅在 macOS 上实现。 + + +● Dictionary global_menu_get_system_menu_roots() const + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +返回受支持的系统菜单 ID 和名称的字典。 + +注意:该方法仅在 macOS 上实现。 + + +● bool global_menu_is_item_checkable(menu_root: String, idx: int) const + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +如果索引为 idx 的菜单项能够以某种方式选中,即有复选框或单选按钮,则返回 true。 + +注意:该方法仅在 macOS 上实现。 + + +● bool global_menu_is_item_checked(menu_root: String, idx: int) const + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +如果索引为 idx 的菜单项处于选中状态,则返回 true。 + +注意:该方法仅在 macOS 上实现。 + + +● bool global_menu_is_item_disabled(menu_root: String, idx: int) const + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +如果索引为 idx 的菜单项处于禁用状态,则返回 true。禁用状态下无法被选中,也无法激活动作。 + +关于如何禁用菜单项的更多信息见 global_menu_set_item_disabled()。 + +注意:该方法仅在 macOS 上实现。 + + +● bool global_menu_is_item_hidden(menu_root: String, idx: int) const + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +如果索引为 idx 的菜单项被隐藏,则返回 true。 + +关于如何隐藏菜单项的更多信息见 global_menu_set_item_hidden()。 + +注意:该方法仅在 macOS 上实现。 + + +● bool global_menu_is_item_radio_checkable(menu_root: String, idx: int) const + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +如果索引为 idx 的菜单项为单选按钮风格,则返回 true。 + +注意:仅为装饰作用;必须自行为单选组添加选中、取消选中的逻辑。 + +注意:该方法仅在 macOS 上实现。 + + +● void global_menu_remove_item(menu_root: String, idx: int) + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +从全局菜单 menu_root 移除索引为 idx 的菜单项。 + +注意:位置在被移除菜单项之后的菜单项的索引号都会减一。 + +注意:该方法仅在 macOS 上实现。 + + +● void global_menu_set_item_accelerator(menu_root: String, idx: int, keycode: Key) + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +设置索引为 idx 的菜单项的快捷键。keycode 可以是单一 Key,也可以是 KeyModifierMask 和 Key 用按位或操作进行的组合,例如 KEY_MASK_CTRL | KEY_A(Ctrl + A)。 + +注意:该方法仅在 macOS 上实现。 + + +● void global_menu_set_item_callback(menu_root: String, idx: int, callback: Callable) + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +设置索引为 idx 的菜单项的回调。回调会在按下菜单项时发出。 + +注意:callback Callable 只接受一个 Variant 参数,传入 Callable 的参数是创建菜单项时传给 tag 参数的值。 + +注意:该方法仅在 macOS 上实现。 + + +● void global_menu_set_item_checkable(menu_root: String, idx: int, checkable: bool) + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +设置索引为 idx 的菜单项是否为复选框。如果为 false,则会将该菜单项的类型设置为纯文本。 + +注意:该方法仅在 macOS 上实现。 + + +● void global_menu_set_item_checked(menu_root: String, idx: int, checked: bool) + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +设置索引为 idx 的菜单项的选中状态。 + +注意:该方法仅在 macOS 上实现。 + + +● void global_menu_set_item_disabled(menu_root: String, idx: int, disabled: bool) + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +启用/禁用索引为 idx 的菜单项。禁用状态下无法被选中,也无法激活动作。 + +注意:该方法仅在 macOS 上实现。 + + +● void global_menu_set_item_hidden(menu_root: String, idx: int, hidden: bool) + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +隐藏/显示索引为 idx 的菜单项。当它被隐藏时,项目不会出现在菜单中,并且无法调用其操作。 + +注意:该方法仅在 macOS 上实现。 + + +● void global_menu_set_item_hover_callbacks(menu_root: String, idx: int, callback: Callable) + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +设置索引为 idx 的菜单项的回调。回调会在菜单项被悬停时发出。 + +注意:callback Callable 需要接受一个 Variant 参数,传入 Callable 的参数是创建菜单项时传给 tag 参数的值。 + +注意:该方法仅在 macOS 上实现。 + + +● void global_menu_set_item_icon(menu_root: String, idx: int, icon: Texture2D) + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +替换指定索引 idx 的 Texture2D 图标。 + +注意:该方法仅在 macOS 上实现。 + +注意:该方法不支持 macOS 的“_dock”菜单项。 + + +● void global_menu_set_item_indentation_level(menu_root: String, idx: int, level: int) + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +设置索引为 idx 的菜单项的水平偏移量。 + +注意:该方法仅在 macOS 上实现。 + + +● void global_menu_set_item_key_callback(menu_root: String, idx: int, key_callback: Callable) + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +设置索引为 idx 的菜单项的回调。回调会在激活快捷键时发出。 + +注意:key_callback Callable 只接受一个 Variant 参数,传入 Callable 的参数是创建菜单项时传给 tag 参数的值。 + +注意:该方法仅在 macOS 上实现。 + + +● void global_menu_set_item_max_states(menu_root: String, idx: int, max_states: int) + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +设置多状态项的状态数。详见 global_menu_add_multistate_item()。 + +注意:该方法仅在 macOS 上实现。 + + +● void global_menu_set_item_radio_checkable(menu_root: String, idx: int, checkable: bool) + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +将索引为 idx 的菜单项设置为单选按钮风格。如果为 false,则会将该菜单项的类型设置为纯文本。 + +注意:仅为装饰作用;必须自行为单选组添加选中、取消选中的逻辑。 + +注意:该方法仅在 macOS 上实现。 + + +● void global_menu_set_item_state(menu_root: String, idx: int, state: int) + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +设置多状态项的状态。详见 global_menu_add_multistate_item()。 + +注意:该方法仅在 macOS 上实现。 + + +● void global_menu_set_item_submenu(menu_root: String, idx: int, submenu: String) + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +设置索引为 idx 的菜单项的子菜单。子菜单是某个全局菜单根菜单项的 ID,点击该菜单项时会显示子菜单。 + +注意:该方法仅在 macOS 上实现。 + + +● void global_menu_set_item_tag(menu_root: String, idx: int, tag: Variant) + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +设置指定菜单项的元数据,可以是任何类型。后续可以使用 global_menu_get_item_tag() 获取,可以方法地为菜单项关联上下文数据。 + +注意:该方法仅在 macOS 上实现。 + + +● void global_menu_set_item_text(menu_root: String, idx: int, text: String) + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +设置索引为 idx 的菜单项的文本。 + +注意:该方法仅在 macOS 上实现。 + + +● void global_menu_set_item_tooltip(menu_root: String, idx: int, tooltip: String) + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +设置索引为 idx 的菜单项的工具提示 String。 + +注意:该方法仅在 macOS 上实现。 + + +● void global_menu_set_popup_callbacks(menu_root: String, open_callback: Callable, close_callback: Callable) + + 已弃用: 改用 NativeMenu 或 PopupMenu。 + +注册当菜单分别即将显示或关闭时发出的可调用对象。回调方法应该没有参数。 + + +● bool has_additional_outputs() const + +如果已通过 register_additional_output() 注册了任何额外输出,则返回 true。 + + +● bool has_feature(feature: Feature) const + +如果当前的 DisplayServer 支持指定的特性 feature,则返回 true,否则返回 false。 + + +● bool has_hardware_keyboard() const + +如果连接了物理键盘,则返回 true。 + +注意:该方法在 Android 和 iOS 上实现,在其他平台上始终返回 true。 + + +● void help_set_search_callbacks(search_callback: Callable, action_callback: Callable) + +设置原生帮助系统搜索回调。 + +search_callback 的参数是 String search_string, int result_limit,返回的是包含一对对“key、显示名称”的 Dictionary。用户在帮助菜单中输入搜索内容的时候就会调用这个回调。 + +action_callback 的参数是 String key。用户在帮助菜单中选择某个搜索结果时就会调用这个回调。 + +注意:该方法仅在 macOS 上实现。 + + +● Vector2i ime_get_selection() const + +返回输入法编辑器 编组字符串中选中的文本,Vector2i 的 x 分量为光标的位置,y 则为所选项的长度。 + +注意:该方法仅在 macOS 上实现。 + + +● String ime_get_text() const + +返回输入法编辑器 窗口中的编组字符串。 + +注意:该方法仅在 macOS 上实现。 + + +● bool is_dark_mode() const + +如果操作系统正在使用暗黑模式,则返回 true。 + +注意:该方法在 Android、iOS、macOS、Windows 和 Linux(X11/Wayland)上实现。 + + +● bool is_dark_mode_supported() const + +如果操作系统支持暗黑模式,则返回 true。 + +注意:该方法在 Android、iOS、macOS、Windows 和 Linux(X11/Wayland)上实现。 + + +● bool is_touchscreen_available() const + +如果触摸事件可用(Android 或 iOS)、在 Web 平台上检测到该功能或如果 输入设备 > 指点 > 用鼠标模拟触摸 为 true 时,则返回 true。 + + +● bool is_window_transparency_available() const + +如果窗口背景可以设为透明,则返回 true。如果 显示 > 窗口 > 像素级透明 > 允许 被设置为 false,或者如果渲染器或 OS 合成器不支持透明,则该方法将返回 false。 + + +● int keyboard_get_current_layout() const + +返回激活的键盘布局的索引。 + +注意:本方法在 Linux(X11/Wayland)、macOS 和 Windows 上实现。 + + +● Key keyboard_get_keycode_from_physical(keycode: Key) const + +将物理(美式 QWERTY)键码 keycode 转换为激活键盘布局中的键码。 + +注意:本方法在 Linux(X11/Wayland)、macOS 和 Windows 上实现。 + + +● Key keyboard_get_label_from_physical(keycode: Key) const + +将物理(美式 QWERTY)键码 keycode 转换为活动键盘布局中的按键上印刷的本地化标签。 + +注意:该方法在 Linux(X11/Wayland)、macOS 和 Windows 上实现。 + + +● int keyboard_get_layout_count() const + +返回键盘布局的数量。 + +注意:本方法在 Linux(X11/Wayland)、macOS 和 Windows 上实现。 + + +● String keyboard_get_layout_language(index: int) const + +返回位于 index 位置的键盘布局的 ISO-639/BCP-47 语言代码。 + +注意:本方法在 Linux(X11/Wayland)、macOS 和 Windows 上实现。 + + +● String keyboard_get_layout_name(index: int) const + +返回位于 index 位置的键盘布局的本地化名称。 + +注意:本方法在 Linux(X11/Wayland)、macOS 和 Windows 上实现。 + + +● void keyboard_set_current_layout(index: int) + +设置激活的键盘布局。 + +注意:本方法在 Linux(X11/Wayland)、macOS 和 Windows 上实现。 + + +● BitField[MouseButtonMask] mouse_get_button_state() const + +以位掩码的形式返回当前鼠标按键的状态(各个按钮是否处于按下状态)。如果同时按下了多个按键,则会同时设置多个比特位。等价于 Input.get_mouse_button_mask()。 + + +● MouseMode mouse_get_mode() const + +返回当前的鼠标模式。另见 mouse_set_mode()。 + + +● Vector2i mouse_get_position() const + +返回鼠标光标的当前位置,使用屏幕坐标。 + + +● void mouse_set_mode(mouse_mode: MouseMode) + +设置当前的鼠标模式。另见 mouse_get_mode()。 + + +● void process_events() + +执行窗口管理器处理,包括输入的清空。另见 force_process_and_drop_events()、Input.flush_buffered_events()、Input.use_accumulated_input。 + + +● void register_additional_output(object: Object) + +注册一个 Object,表示除了普通窗口之外还将渲染的额外输出。Object 仅用作标识符,稍后可将其传递给 unregister_additional_output()。 + +这可用于防止 Godot 在没有可见普通窗口时跳过渲染。 + + +● int screen_get_dpi(screen: int = -1) const + +返回指定屏幕的每英寸点数密度。如果 screen 为 SCREEN_OF_MAIN_WINDOW(默认值),则将使用带有主窗口的屏幕。 + +注意:在 macOS 上,如果使用小数显示缩放模式,则返回值不准确。 + +注意:在 Android 设备上,实际屏幕密度分为六种通用密度: + +ldpi - 120 dpi + mdpi - 160 dpi + hdpi - 240 dpi + xhdpi - 320 dpi + xxhdpi - 480 dpi +xxxhdpi - 640 dpi +注意:该方法在 Android、Linux(X11/Wayland)、macOS 和 Windows 上实现。在不受支持的平台上返回 72。 + + +● Image screen_get_image(screen: int = -1) const + +返回 screen 的屏幕截图。 + +注意:该方法在 Linux(X11)、macOS 和 Windows 上实现。 + +注意:在 macOS 上,该方法需要“屏幕录制”权限,如果未授予权限将返回桌面壁纸颜色。 + + +● Image screen_get_image_rect(rect: Rect2i) const + +返回屏幕上矩形框 rect 的截图。 + +注意:该方法在 macOS 和 Windows 上实现。 + +注意:在 macOS 上,该方法需要“屏幕录制”权限,如果未授予权限将返回桌面壁纸颜色。 + + +● float screen_get_max_scale() const + +返回所有屏幕的最大缩放系数。 + +注意:在 macOS 上,如果系统中至少有一个 hiDPI(Retina)屏幕,则返回值为 2.0,在所有其他情况下返回值为 1.0 。 + +注意:该方法仅在 macOS 上实现。 + + +● ScreenOrientation screen_get_orientation(screen: int = -1) const + +返回 screen 的当前朝向。另见 screen_set_orientation()。 + +注意:该方法在 Android 和 iOS 上实现。 + + +● Color screen_get_pixel(position: Vector2i) const + +返回 position 处的显示像素的颜色。 + +注意:该方法在 Linux(X11)、macOS 和 Windows 上实现。 + +注意:在 macOS 上,该方法需要“屏幕录制”权限,如果未授予权限将返回桌面壁纸颜色。 + + +● Vector2i screen_get_position(screen: int = -1) const + +返回屏幕左上角的位置,单位为像素。使用多个监视器时,屏幕位置是相对于虚拟桌面区域的位置。如果多监视器中使用了不同的屏幕分辨率或朝向,原点有可能位于所有显示器之外,类似于: + +* (0, 0) +-------+ + | | ++-------------+ | | +| | | | +| | | | ++-------------+ +-------+ +另见 screen_get_size()。 + +注意:在 Linux(Wayland)上,该方法始终返回 (0, 0)。 + + +● float screen_get_refresh_rate(screen: int = -1) const + +返回指定屏幕的当前刷新率。如果 screen 为 SCREEN_OF_MAIN_WINDOW(默认值),将使用带有主窗口的屏幕。 + +注意:如果 DisplayServer 未能找到指定屏幕的刷新率,则返回 -1.0。在 Web 上,screen_get_refresh_rate() 将始终返回 -1.0,因为无法在该平台上检索到刷新率。 + +要在该方法失败时回退至默认刷新率,请尝试: + +var refresh_rate = DisplayServer.screen_get_refresh_rate() +if refresh_rate < 0: + refresh_rate = 60.0 + + +● float screen_get_scale(screen: int = -1) const + +返回屏幕的缩放系数,屏幕使用索引号指定。 + +注意:在 macOS 上,hiDPI(视网膜)屏幕返回 2.0,其它所有情况均返回 1.0。 + +注意:在 Linux(Wayland)上,只有 screen 为 SCREEN_OF_MAIN_WINDOW 时返回值才是精确的。由于 API 的限制,如果屏幕缩放存在小数点,传入直接的索引号返回的是向上取整后的结果(即 1.25 会向上取整成 2.0)。 + +注意:该方法在 Android、iOS、Web、macOS 和 Linux(Wayland)上实现。 + + +● Vector2i screen_get_size(screen: int = -1) const + +返回屏幕大小。单位:像素。另见 screen_get_position() 和 screen_get_usable_rect()。 + + +● Rect2i screen_get_usable_rect(screen: int = -1) const + +返回屏幕上没有被状态栏遮挡的部分,单位为像素。另见 screen_get_size()。 + + +● bool screen_is_kept_on() const + +如果操作系统的节电措施永远不会关闭屏幕,则返回 true。另见 screen_set_keep_on()。 + + +● void screen_set_keep_on(enable: bool) + +设置屏幕是否总是不会被操作系统的节能措施关闭。另见 screen_is_kept_on()。 + + +● void screen_set_orientation(orientation: ScreenOrientation, screen: int = -1) + +设置 screen 的 orientation。另见 screen_get_orientation()。 + +注意:在 iOS 上,如果 显示 > 窗口 > 手持设备 > 朝向 未设置为 SCREEN_SENSOR,则该方法无效。 + + +● void set_icon(image: Image) + +使用 Image 设置窗口图标(通常显示在左上角)。要使用操作系统的原生格式设置图标,请改用 set_native_icon()。 + +注意:需要支持 FEATURE_ICON。 + + +● void set_native_icon(filename: String) + +使用操作系统的原生格式设置窗口图标(通常显示在左上角)。位于 filename 的文件在 Windows 上必须为 .ico 格式,在 macOS 上必须为 .icns 格式。使用特制的 .ico 或 .icns 图标,就能够让 set_native_icon() 指定以不同尺寸显示图标时显示不同的图标。大小由操作系统和用户首选项决定(包括显示器缩放系数)。要使用其他格式的图标,请改用 set_icon()。 + +注意:需要支持 FEATURE_NATIVE_ICON。 + + +● void set_system_theme_change_callback(callable: Callable) + +设置更改系统主题设置时应调用的 callable。回调方法应该有零个参数。 + +注意:该方法在 Android、iOS、macOS、Windows 和 Linux(X11/Wayland)上实现。 + + +● void show_emoji_and_symbol_picker() const + +打开系统 Emoji 和符号拾取器。 + +注意:该方法在 macOS 和 Windows 上实现。 + + +● Rect2 status_indicator_get_rect(id: int) const + +返回给定状态指示器 id 的矩形,使用屏幕坐标系。如果状态指示器不可见,则返回空的 Rect2。 + +注意:该方法在 macOS 和 Windows 上实现。 + + +● void status_indicator_set_callback(id: int, callback: Callable) + +设置应用程序状态指示器激活回调。callback 应采用两个参数:int 鼠标按钮索引(MouseButton 值之一)和 Vector2i 屏幕坐标中的点击位置。 + +注意:该方法在 macOS 和 Windows 上实现。 + + +● void status_indicator_set_icon(id: int, icon: Texture2D) + +设置应用程序状态指示器图标。 + +注意:该方法在 macOS 和 Windows 上实现。 + + +● void status_indicator_set_menu(id: int, menu_rid: RID) + +设置应用程序状态指示器原生弹出菜单。 + +注意:在 macOS 上,该菜单可通过任何鼠标按键激活。其激活回调未触发。 + +注意:在 Windows 上,该菜单可通过鼠标右键激活,选择状态图标并按下 Shift + F10 或应用程序键。菜单的其他鼠标按键的激活回调仍会触发。 + +注意:仅当 NativeMenu 支持 NativeMenu.FEATURE_POPUP_MENU 功能时,才支持原生弹出窗口。 + + +● void status_indicator_set_tooltip(id: int, tooltip: String) + +设置应用程序状态指示器工具提示。 + +注意:该方法在 macOS 和 Windows 上实现。 + + +● String tablet_get_current_driver() const + +返回当前活动的数位板驱动程序的名称。 + +注意:该方法仅在 Windows 上实现。 + + +● int tablet_get_driver_count() const + +返回可用的数位板驱动程序的总数。 + +注意:该方法仅在 Windows 上实现。 + + +● String tablet_get_driver_name(idx: int) const + +返回给定索引的数位板驱动程序名称。 + +注意:该方法仅在 Windows 上实现。 + + +● void tablet_set_current_driver(name: String) + +设置活动数位板驱动程序名称。 + +支持的驱动程序: + +- winink:Windows Ink API,默认(需要 Windows 8.1+)。 + +- wintab:Wacom Wintab API(需要兼容的设备驱动程序)。 + +- dummy:虚设驱动程序,数位板输入被禁用。 + +注意:该方法仅在 Windows 上实现。 + + +● Array[Dictionary] tts_get_voices() const + +返回语音信息字典的 Array。 + +每个 Dictionary 包含两个 String 条目: + +- name 是语音名称。 + +- id 是语音标识符。 + +- language 是语言代码,格式为 lang_Variant 。lang 部分是小写的基于 ISO-639 标准的 2 或 3 字母代码。而 Variant 部分是一个依赖于引擎的字符串,描述国家、地区或/和方言。 + +请注意,Godot 依赖于系统库来实现文本到语音的功能。这些库在 Windows 和 MacOS 上是默认安装的,但并非安装在所有 Linux 发行版上。如果它们不存在,此方法将返回一个空列表。这适用于 Linux 上的 Godot 用户,以及在 Linux 上运行使用文本到语音的 Godot 游戏的最终用户。 + +注意:这个方法在 Android、iOS、Web、Linux(X11/Wayland)、macOS 和 Windows 上实现。 + +注意:音频 > 常规 > 文本转语音 应当为 true 才能够使用文本到语音功能。 + + +● PackedStringArray tts_get_voices_for_language(language: String) const + +返回 language 的语音标识符的 PackedStringArray。 + +注意:该方法在 Android、iOS、Web、Linux(X11/Wayland)、macOS 和 Windows 上实现。 + +注意:音频 > 常规 > 文本转语音 应为 true 才能使用文本转语音。 + + +● bool tts_is_paused() const + +如果合成器处于暂停状态,则返回 true。 + +注意:该方法在 Android、iOS、Web、Linux(X11/Wayland)、macOS 和 Windows 上实现。 + +注意:音频 > 常规 > 文本转语音 应为 true 才能使用文本转语音。 + + +● bool tts_is_speaking() const + +如果合成器正在生成语音,或者有发言正在队列中等待,则返回 true。 + +注意:该方法在 Android、iOS、Web、Linux(X11/Wayland)、macOS 和 Windows 上实现。 + +注意:音频 > 常规 > 文本转语音 应为 true 才能使用文本转语音。 + + +● void tts_pause() + +让合成器进入暂停状态。 + +注意:该方法在 Android、iOS、Web、Linux(X11/Wayland)、macOS 以及 Windows 上实现。 + +注意:要使用文本转语音,音频 > 常规 > 文本转语音 应该为 true。 + + +● void tts_resume() + +让处于暂停状态的合成器继续执行。 + +注意:该方法在 Android、iOS、Web、Linux(X11/Wayland)、macOS 以及 Windows 上实现。 + +注意:要使用文本转语音,音频 > 常规 > 文本转语音 应该为 true。 + + +● void tts_set_utterance_callback(event: TTSUtteranceEvent, callable: Callable) + +添加回调,会在发言开始、结束、取消、到达文本边界时调用。 + +- TTS_UTTERANCE_STARTED、TTS_UTTERANCE_ENDED、TTS_UTTERANCE_CANCELED 可调用体的方法应接受一个 int 参数,即发言 ID。 + +- TTS_UTTERANCE_BOUNDARY 可调用体的方法应接受两个 int 参数:字符索引和发言 ID。 + +注意:边界回调的颗粒度由引擎决定。 + +注意:该方法在 Android、iOS、Web、Linux(X11/Wayland)、macOS 以及 Windows 上实现。 + +注意:要使用文本转语音,音频 > 常规 > 文本转语音 应该为 true。 + + +● void tts_speak(text: String, voice: String, volume: int = 50, pitch: float = 1.0, rate: float = 1.0, utterance_id: int = 0, interrupt: bool = false) + +向队列中添加发言。如果 interrupt 为 true,则会先清空队列。 + +- voice 语音标识符是 tts_get_voices() 所返回的 "id" 值,也可以是 tts_get_voices_for_language() 返回的值。 + +- volume 音量从 0(最低)到 100(最高)。 + +- pitch 音高从 0.0(最低)到 2.0(最高), 1.0 为当前语音的默认音高。 + +- rate 语速从 0.1(最低)到 10.0(最高), 1.0 为普通语速。其他值为相对百分比。 + +- utterance_id 话语 ID 会作为参数传递给回调函数。 + +注意:在 Windows 和 Linux(X11/Wayland)上,发言的 text 可以使用 SSML 标记。对 SSML 支持取决于引擎和语音。如果引擎不支持 SSML,你应该在调用 tts_speak() 之前剥离所有 XML 标记。 + +注意:音高、语速、音量的颗粒度由引擎和语音决定。设置的值可能被截断。 + +注意:该方法在 Android、iOS、Web、Linux(X11/Wayland)、macOS 以及 Windows 上实现。 + +注意:要使用文本转语音,音频 > 常规 > 文本转语音 应该为 true。 + + +● void tts_stop() + +停止执行中的合成器,移除队列中的所有发言。 + +注意:该方法在 Android、iOS、Web、Linux(X11/Wayland)、macOS 以及 Windows 上实现。 + +注意:要使用文本转语音,音频 > 常规 > 文本转语音 应该为 true。 + + +● void unregister_additional_output(object: Object) + +取消注册通过 register_additional_output() 注册的代表额外输出的 Object。 + + +● int virtual_keyboard_get_height() const + +返回键盘在屏幕上的高度,单位为像素。如果没有键盘或当前键盘被隐藏,则返回0。 + + +● void virtual_keyboard_hide() + +如果虚拟键盘为显示状态则隐藏虚拟键盘,否则不做任何操作。 + + +● void virtual_keyboard_show(existing_text: String, position: Rect2 = Rect2(0, 0, 0, 0), type: VirtualKeyboardType = 0, max_length: int = -1, cursor_start: int = -1, cursor_end: int = -1) + +如果该平台有虚拟键盘,则显示虚拟键盘。 + +existing_text 参数对于实现你自己的 LineEdit 或 TextEdit 很有用,因为它告诉虚拟键盘已经输入了哪些文本(虚拟键盘使用它进行自动更正和预测)。 + +position 参数为编辑文本的屏幕空间 Rect2。 + +type 参数允许配置要显示的虚拟键盘类型。 + +max_length 在当与 -1 不同时,限制可输入的字符数。 + +如果未设置 cursor_end,则可选参数 cursor_start 可以定义当前文本光标位置。 + +可选参数 cursor_start 和 cursor_end,可以定义当前文本选区。 + +注意:该方法在 Android、iOS 和 Web 上实现。 + + +● void warp_mouse(position: Vector2i) + +将鼠标光标位置设置为相对于当前聚焦的游戏窗口管理器窗口左上角的原点的给定 position。 + +注意:warp_mouse() 仅在 Windows、macOS 和 Linux(X11/Wayland)上受支持。它在 Android、iOS 和 Web 上无效。 + + +● bool window_can_draw(window_id: int = 0) const + +如果可以在 window_id 指定的窗口中绘制任何内容,则返回 true,否则返回 false。使用 --disable-render-loop 命令行参数或无头构建将返回 false。 + + +● int window_get_active_popup() const + +返回活动弹出窗口的 ID,如果没有则返回 INVALID_WINDOW_ID。 + + +● int window_get_attached_instance_id(window_id: int = 0) const + +返回 window_id 所附加的 Window 的 Object.get_instance_id()。 + + +● int window_get_current_screen(window_id: int = 0) const + +该函数返回窗口 window_id 所在的屏幕。如果屏幕跨越多个显示器,则返回窗口中心所在的屏幕。另见 window_set_current_screen() 。 + + +● bool window_get_flag(flag: WindowFlags, window_id: int = 0) const + +返回给定窗口当前的 flag 值。 + + +● Vector2i window_get_max_size(window_id: int = 0) const + +返回该窗口的最大尺寸,单位为像素。另见 window_set_max_size()。 + + +● Vector2i window_get_min_size(window_id: int = 0) const + +返回该窗口的最小尺寸,单位为像素。另见 window_set_min_size()。 + + +● WindowMode window_get_mode(window_id: int = 0) const + +返回给定窗口的模式。 + + +● int window_get_native_handle(handle_type: HandleType, window_id: int = 0) const + +该函数返回用于插件的内部结构指针。 + +注意:该方法在 Android、Linux(X11/Wayland)、macOS 和 Windows 上实现。 + + +● Rect2i window_get_popup_safe_rect(window: int) const + +该函数返回控件或菜单项在屏幕坐标系统中的边界框,这个控件或菜单项被用来打开弹出窗口。 + + +● Vector2i window_get_position(window_id: int = 0) const + +返回屏幕上给定窗口的客户端区域位置。 + + +● Vector2i window_get_position_with_decorations(window_id: int = 0) const + +该函数返回给定窗口在屏幕上的位置,包括操作系统绘制的边框。另见 window_get_position()。 + + +● Vector3i window_get_safe_title_margins(window_id: int = 0) const + +当设置了 WINDOW_FLAG_EXTEND_TO_TITLE 标志时,该函数返回标题左边距 (x)、右边距 (y) 和高度 (z),这些边距可以安全地使用(不包含任何按钮或其他元素)。 + + +● Vector2i window_get_size(window_id: int = 0) const + +返回窗口的大小(单位为像素),不包含操作系统绘制的边框,该窗口由 window_id 指定。这个区域也叫做“客户区域”。另见 window_get_size_with_decorations()、window_set_size()、window_get_position()。 + + +● Vector2i window_get_size_with_decorations(window_id: int = 0) const + +返回窗口的大小(单位为像素),包含操作系统绘制的边框,该窗口由 window_id 指定。另见 window_get_size()。 + + +● Vector2i window_get_title_size(title: String, window_id: int = 0) const + +返回由 window_id 指定的窗口的估计窗口标题栏大小(包括文本和窗口按钮)(单位:像素)。该方法不会更改窗口标题。 + +注意:该方法在 macOS 和 Windows 上实现。 + + +● VSyncMode window_get_vsync_mode(window_id: int = 0) const + +返回给定窗口的垂直同步模式。 + + +● bool window_is_focused(window_id: int = 0) const + +如果 window_id 指定的窗口已获得焦点,则返回 true。 + + +● bool window_is_maximize_allowed(window_id: int = 0) const + +如果给定的窗口能够最大化(最大化按钮已启用),则返回 true。 + + +● bool window_maximize_on_title_dbl_click() const + +如果双击窗口标题应将其最大化,则返回 true。 + +注意:这个方法仅在 macOS 上实现。 + + +● bool window_minimize_on_title_dbl_click() const + +如果双击窗口标题应将其最小化,则返回 true。 + +注意:这个方法仅在 macOS 上实现。 + + +● void window_move_to_foreground(window_id: int = 0) + +将由 window_id 指定的窗口移动至前台,使其位于其他窗口之上。 + + +● void window_request_attention(window_id: int = 0) + +让由 window_id 指定的窗口请求注意,该窗口获得焦点之前会闪烁窗口标题和任务栏项目。如果该窗口目前持有焦点,则通常是没有可见效果的。实际的行为因操作系统而异。 + + +● void window_set_current_screen(screen: int, window_id: int = 0) + +将由 window_id 指定的窗口移动至指定的屏幕 screen。另见 window_get_current_screen()。 + + +● void window_set_drop_files_callback(callback: Callable, window_id: int = 0) + +设置当文件从操作系统的文件管理器拖放到 window_id 指定的窗口时应调用的 callback。callback 应采用一个 PackedStringArray 参数,即拖放的文件列表。 + +警告:仅限高级用户!将这样的回调添加到 Window 节点将覆盖其默认实现,这可能会引入错误。 + +注意:这个方法在 Windows、macOS、Linux(X11/Wayland)、Web 上实现。 + + +● void window_set_exclusive(window_id: int, exclusive: bool) + +如果设置为 true,该窗口将始终位于其父窗口之上,父窗口将在该窗口打开时忽略输入。 + +注意:在 macOS 上,独占窗口被限制在与父窗口相同的空间(虚拟桌面或屏幕)中。 + +注意:该方法在 macOS 和 Windows 上实现。 + + +● void window_set_flag(flag: WindowFlags, enabled: bool, window_id: int = 0) + +启用或禁用给定窗口的给定标志 flag。可能的值和相应的行为见 WindowFlags。 + + +● void window_set_ime_active(active: bool, window_id: int = 0) + +设置是否应该为窗口启用输入法编辑器 ,该窗口由 window_id 指定。另见 window_set_ime_position()。 + + +● void window_set_ime_position(position: Vector2i, window_id: int = 0) + +设置指定 window_id 的输入法编辑器 弹出框的位置。仅在指定 window_id 的 window_set_ime_active() 为 true 时有效。 + + +● void window_set_input_event_callback(callback: Callable, window_id: int = 0) + +设置回调 callback,向由 window_id 指定的窗口发送任何 InputEvent 时会进行回调。 + +警告:仅限高级用户!将这样的回调添加到 Window 节点将覆盖其默认实现,这可能会引入错误。 + + +● void window_set_input_text_callback(callback: Callable, window_id: int = 0) + +设置回调 callback,使用虚拟键盘向由 window_id 指定的窗口输入文本时会进行回调。 + +警告:仅限高级用户!将这样的回调添加到 Window 节点将覆盖其默认实现,这可能会引入错误。 + + +● void window_set_max_size(max_size: Vector2i, window_id: int = 0) + +设置由 window_id 指定的窗口的最大大小(单位为像素)。通常,用户将无法拖动窗口使其大于该指定大小。另见 window_get_max_size()。 + +注意:建议改用 Window.max_size 更改此值。 + +注意:使用第三方工具,用户可以禁用窗口几何限制,从而绕过此限制。 + + +● void window_set_min_size(min_size: Vector2i, window_id: int = 0) + +将给定窗口的最小大小设置为 min_size(单位为像素)。通常,用户将无法拖动窗口使其小于该指定大小。另见 window_get_min_size()。 + +注意:建议改用 Window.min_size 来更改此值。 + +注意:默认情况下,主窗口的最小大小为 Vector2i(64, 64)。这可以防止将窗口调整为接近零的大小时可能出现的问题。 + +注意:使用第三方工具,用户可以禁用窗口几何限制,从而绕过此限制。 + + +● void window_set_mode(mode: WindowMode, window_id: int = 0) + +将给定窗口的窗口模式设置为 mode。可能的值以及各个模式的行为见 WindowMode。 + +注意:在 Android 上,设为 WINDOW_MODE_FULLSCREEN 或 WINDOW_MODE_EXCLUSIVE_FULLSCREEN 会启用沉浸模式。 + +注意:将窗口设置为全屏会强制将无边框标志设为 true,所以不再需要时请务必将其设回 false。 + + +● void window_set_mouse_passthrough(region: PackedVector2Array, window_id: int = 0) + +设置一个接受鼠标事件的窗口的多边形区域。该区域外的鼠标事件将被传递出去。 + +传递一个空数组将禁用穿透支持(所有鼠标事件将被窗口拦截,这是默认行为)。 + +# 设置区域,使用 Path2D 节点。 +DisplayServer.window_set_mouse_passthrough($Path2D.curve.get_baked_points()) + +# 设置区域,使用 Polygon2D 节点。 +DisplayServer.window_set_mouse_passthrough($Polygon2D.polygon) + +# 重置区域为默认值。 +DisplayServer.window_set_mouse_passthrough([]) +注意:在 Windows 上,不会绘制位于区域之外的窗口部分,而在 Linux(X11)和 macOS 上则会绘制。 + +注意:该方法在 Linux(X11)、macOS 和 Windows 上实现。 + + +● void window_set_popup_safe_rect(window: int, rect: Rect2i) + +设置用于打开弹出窗口的控件或菜单项的范围框,使用屏幕坐标系。在该区域中点击不会自动关闭该弹出框。 + + +● void window_set_position(position: Vector2i, window_id: int = 0) + +将给定窗口的位置设置为 position。使用多个监视器时,屏幕位置是相对于虚拟桌面区域的位置。如果多监视器中使用了不同的屏幕分辨率或朝向,原点有可能位于所有显示器之外,类似于: + +* (0, 0) +-------+ + | | ++-------------+ | | +| | | | +| | | | ++-------------+ +-------+ +另见 window_get_position() 和 window_set_size()。 + +注意:建议改用 Window.position 更改此值。 + +注意:在 Linux(Wayland)上:该方法是没有操作。 + + +● void window_set_rect_changed_callback(callback: Callable, window_id: int = 0) + +设置回调 callback,由 window_id 指定的窗口发生移动或调整大小时会进行回调。 + +警告:仅限高级用户!将这样的回调添加到 Window 节点将覆盖其默认实现,这可能会引入错误。 + + +● void window_set_size(size: Vector2i, window_id: int = 0) + +将给定窗口的大小设置为 size(单位为像素)。另见 window_get_size() 和 window_get_position()。 + +注意:建议改用 Window.size 更改此值。 + + +● void window_set_title(title: String, window_id: int = 0) + +将给定窗口的标题设置为 title。 + +注意:建议改用 Window.title 更改此值。 + +注意:避免每一帧都更改窗口标题,因为这会导致某些窗口管理器出现性能问题。尝试每秒最多更改几次窗口标题。 + + +● void window_set_transient(window_id: int, parent_window_id: int) + +设置窗口瞬态父级。瞬态窗口将与其瞬态父级一起销毁,并在关闭时将焦点返回到父级。瞬态窗口显示在非排他性全屏父窗口的顶部。瞬态窗口无法进入全屏模式。 + +注意:建议改用 Window.transient 更改此值。 + +注意:行为可能因平台而异。 + + +● void window_set_vsync_mode(vsync_mode: VSyncMode, window_id: int = 0) + +设置给定窗口的垂直同步模式。另见 显示 > 窗口 > 垂直同步 > 垂直同步模式。 + +参阅 DisplayServer.VSyncMode 了解可能的值,以及它们如何影响应用程序的行为。 + +根据平台和使用的渲染器,如果不支持所需的模式,引擎将回退到 VSYNC_ENABLED。 + +注意:除 VSYNC_ENABLED 以外的垂直同步模式,仅支持 Forward+ 和 Mobile 渲染方式,不支持 Compatibility。 + + +● void window_set_window_buttons_offset(offset: Vector2i, window_id: int = 0) + +设置了 WINDOW_FLAG_EXTEND_TO_TITLE 标志时,会设置第一个标题栏按钮中心的偏移量。 + +注意:这个标志仅在 macOS 上实现。 + + +● void window_set_window_event_callback(callback: Callable, window_id: int = 0) + +设置回调 callback,由 window_id 指定的窗口发生事件时会进行回调。 + +警告:仅限高级用户!将这样的回调添加到 Window 节点将覆盖其默认实现,这可能会引入错误。 + + +● void window_start_drag(window_id: int = 0) + +在窗口编号为 window_id 的窗口上启动交互式拖动操作,使用当前鼠标位置。处理鼠标按钮按下事件时调用该方法可以模拟在窗口标题栏上按下的事件。使用该方法可以使窗口参与空间切换、平铺等系统功能。 + +注意:该方法在 Linux(X11/Wayland)、macOS 和 Windows 上实现。 + + +● void window_start_resize(edge: WindowResizeEdge, window_id: int = 0) + +在窗口编号为 window_id 的窗口上启动交互式调整大小操作,使用当前鼠标位置。处理鼠标按钮按下事件时调用该方法可以模拟在窗口边缘上按下的事件。 + +注意:该方法在 Linux(X11/Wayland)、macOS 和 Windows 上实现。 + diff --git a/Server/test/游戏小提示配置系统说明.md b/Server/test/游戏小提示配置系统说明.md new file mode 100644 index 0000000..95d4aae --- /dev/null +++ b/Server/test/游戏小提示配置系统说明.md @@ -0,0 +1,144 @@ +# 游戏小提示配置系统实现说明 + +## 概述 + +本系统成功将游戏小提示配置从客户端硬编码迁移到服务端数据库管理,实现了动态配置和灵活的显示模式。 + +## 系统架构 + +### 1. 数据库层 (MongoDB) +- **配置ID**: `687e40008e77ba00a7414bb2` +- **集合**: `gameconfig` +- **配置结构**: + ```json + { + "切换模式": "顺序", // 可选:顺序、随机、倒序 + "切换速度": 5, // 切换间隔(秒) + "游戏小提示": [ // 小提示内容数组 + "按住wsad可以移动游戏画面", + "使用鼠标滚轮来缩放游戏画面", + // ... 更多小提示 + ] + } + ``` + +### 2. 服务端层 (Python) + +#### SMYMongoDBAPI.py 新增功能 +- `get_game_tips_config()`: 获取游戏小提示配置 +- `update_game_tips_config()`: 更新游戏小提示配置 +- 配置ID: `CONFIG_IDS["game_tips"]` + +#### TCPGameServer.py 新增功能 +- `_load_game_tips_config()`: 从数据库加载配置 +- `_handle_game_tips_config_request()`: 处理客户端配置请求 +- 消息路由: `request_game_tips_config` → `_handle_game_tips_config_request` + +### 3. 客户端层 (GDScript) + +#### TCPNetworkManager.gd 新增功能 +- `sendGetGameTipsConfig()`: 发送配置请求 +- 消息处理: `game_tips_config_response` → `main_game._handle_game_tips_config_response` + +#### MainGame.gd 新增功能 +- `game_tips_config`: 存储服务端配置 +- `current_tip_index`: 顺序/倒序模式的索引 +- `_handle_game_tips_config_response()`: 处理服务端响应 +- `_random_small_game_tips()`: 重构为支持多种切换模式 + +## 功能特性 + +### 1. 切换模式 +- **顺序模式**: 按配置顺序依次显示小提示 +- **倒序模式**: 按配置倒序依次显示小提示 +- **随机模式**: 随机选择小提示显示 + +### 2. 动态配置 +- 服务端可随时更新小提示内容 +- 客户端启动时自动获取最新配置 +- 支持热更新(无需重启游戏) + +### 3. 容错机制 +- 服务端配置不可用时使用本地默认配置 +- 配置为空时显示默认欢迎信息 +- 网络异常时优雅降级 + +## 部署文件 + +### 1. 配置导入脚本 +- `import_game_tips_config.py`: 将配置数据导入MongoDB +- 包含15条游戏小提示 +- 默认配置:顺序模式,5秒切换间隔 + +### 2. 测试脚本 +- `test_game_tips_config.py`: 基础功能测试 +- `test_complete_game_tips_system.py`: 完整系统测试 + +## 使用方法 + +### 1. 初始化配置 +```bash +cd Server +python import_game_tips_config.py +``` + +### 2. 测试系统 +```bash +python test_game_tips_config.py +``` + +### 3. 客户端集成 +游戏启动时会自动请求服务端配置: +```gdscript +# 在MainGame.gd中已集成 +tcp_network_manager_panel.sendGetGameTipsConfig() +``` + +## 配置管理 + +### 1. 查看当前配置 +```python +from SMYMongoDBAPI import SMYMongoDBAPI +api = SMYMongoDBAPI() +api.connect() +config = api.get_game_tips_config() +print(config) +``` + +### 2. 更新配置 +```python +new_config = { + "切换模式": "随机", + "切换速度": 3, + "游戏小提示": ["新的小提示1", "新的小提示2"] +} +api.update_game_tips_config(new_config) +``` + +## 测试结果 + +✅ **数据库操作测试**: 通过 +✅ **服务器加载测试**: 通过 +✅ **配置导入功能**: 通过 +✅ **客户端集成**: 完成 + +## 技术优势 + +1. **集中管理**: 所有小提示内容统一在服务端管理 +2. **动态更新**: 无需客户端更新即可修改小提示 +3. **灵活配置**: 支持多种显示模式和自定义切换速度 +4. **高可用性**: 完善的容错机制确保系统稳定性 +5. **易于维护**: 清晰的代码结构和完整的测试覆盖 + +## 后续扩展 + +1. **多语言支持**: 可扩展为支持多语言的小提示 +2. **个性化配置**: 可为不同用户群体配置不同的小提示 +3. **统计分析**: 可添加小提示显示统计和用户反馈 +4. **管理界面**: 可开发Web管理界面方便运营人员管理 + +--- + +**实现完成时间**: 2024年12月 +**版本**: 1.0 +**状态**: 已完成并测试通过 \ No newline at end of file diff --git a/Server/test/游戏配置/作物系统.json b/Server/test/游戏配置/作物系统.json new file mode 100644 index 0000000..34bbdcc --- /dev/null +++ b/Server/test/游戏配置/作物系统.json @@ -0,0 +1,1071 @@ +{ + "_id": { + "$oid": "687cfb3d8e77ba00a7414bac" + }, + "config_type": "作物系统配置", + "测试作物": { + "作物名称": "0号作物", + "成熟物名称": "0号作物", + "花费": 1, + "生长时间": 1, + "收益": 10000, + "品质": "普通", + "描述": "仅供测试使用的特殊作物", + "耐候性": 10, + "等级": 1, + "经验": 999, + "能否购买": false + }, + "野草1": { + "作物名称": "野草1", + "成熟物名称": null, + "花费": 1, + "生长时间": 1, + "收益": 0, + "品质": "普通", + "描述": "野草", + "耐候性": 99, + "等级": 1, + "经验": 0, + "能否购买": false, + "是否杂草": true + }, + "野草2": { + "作物名称": "野草2", + "成熟物名称": null, + "花费": 0, + "生长时间": 1, + "收益": 0, + "品质": "普通", + "描述": "野草", + "耐候性": 99, + "等级": 1, + "经验": 0, + "能否购买": false, + "是否杂草": true + }, + "杂草1": { + "作物名称": "杂草1", + "成熟物名称": null, + "花费": 0, + "生长时间": 1, + "收益": 0, + "品质": "普通", + "描述": "杂草", + "耐候性": 99, + "等级": 1, + "经验": 0, + "能否购买": false, + "是否杂草": true + }, + "杂草2": { + "作物名称": "杂草2", + "成熟物名称": null, + "花费": 0, + "生长时间": 1, + "收益": 0, + "品质": "普通", + "描述": "野草", + "耐候性": 99, + "等级": 1, + "经验": 0, + "能否购买": false, + "是否杂草": true + }, + "小麦": { + "作物名称": "小麦", + "成熟物名称": "小麦", + "花费": 50, + "生长时间": 300, + "收益": 75, + "品质": "普通", + "描述": "最基础的粮食作物,生长快速,适合新手练习", + "耐候性": 8, + "等级": 1, + "经验": 8, + "能否购买": true + }, + "胡萝卜": { + "作物名称": "胡萝卜", + "成熟物名称": "胡萝卜", + "花费": 40, + "生长时间": 240, + "收益": 65, + "品质": "普通", + "描述": "营养丰富的根茎类蔬菜,容易种植", + "耐候性": 9, + "等级": 1, + "经验": 7, + "能否购买": true, + "喂养效果": { + "移动速度": 5, + "闪避率": 2 + } + }, + "土豆": { + "作物名称": "土豆", + "成熟物名称": "马铃薯", + "花费": 60, + "生长时间": 480, + "收益": 95, + "品质": "普通", + "描述": "耐寒的块茎作物,产量稳定", + "耐候性": 12, + "等级": 1, + "经验": 10, + "能否购买": true + }, + "稻谷": { + "作物名称": "稻谷", + "成熟物名称": "稻谷", + "花费": 70, + "生长时间": 600, + "收益": 110, + "品质": "普通", + "描述": "重要的主粮作物,需要充足水分", + "耐候性": 7, + "等级": 1, + "经验": 12, + "能否购买": true + }, + "大蒜": { + "作物名称": "大蒜", + "成熟物名称": "大蒜", + "花费": 50, + "生长时间": 550, + "收益": 100, + "品质": "普通", + "描述": "常用调味作物,具有抗菌功效,种植门槛低", + "耐候性": 10, + "等级": 1, + "经验": 8, + "能否购买": true + }, + "生菜": { + "作物名称": "生菜", + "成熟物名称": "生菜", + "花费": 45, + "生长时间": 650, + "收益": 120, + "品质": "普通", + "描述": "速生叶菜,适合沙拉生食,需保持土壤湿润", + "耐候性": 17, + "等级": 1, + "经验": 9, + "能否购买": true + }, + "石榴": { + "作物名称": "石榴", + "成熟物名称": "石榴", + "花费": 45, + "生长时间": 650, + "收益": 120, + "品质": "普通", + "描述": "速生叶菜,适合沙拉生食,需保持土壤湿润", + "耐候性": 17, + "等级": 1, + "经验": 9, + "能否购买": true + }, + "辣椒": { + "作物名称": "辣椒", + "成熟物名称": "辣椒", + "花费": 45, + "生长时间": 650, + "收益": 120, + "品质": "普通", + "描述": "速生叶菜,适合沙拉生食,需保持土壤湿润", + "耐候性": 17, + "等级": 1, + "经验": 9, + "能否购买": true + }, + "番茄": { + "作物名称": "番茄", + "成熟物名称": "番茄", + "花费": 90, + "生长时间": 720, + "收益": 140, + "品质": "普通", + "描述": "多汁的果实,市场需求大", + "耐候性": 8, + "等级": 2, + "经验": 16, + "能否购买": true + }, + "洋葱": { + "作物名称": "洋葱", + "成熟物名称": "洋葱", + "花费": 75, + "生长时间": 840, + "收益": 125, + "品质": "普通", + "描述": "调味蔬菜,储存时间长", + "耐候性": 11, + "等级": 2, + "经验": 14, + "能否购买": true + }, + "大豆": { + "作物名称": "大豆", + "成熟物名称": "大豆", + "花费": 85, + "生长时间": 1080, + "收益": 145, + "品质": "普通", + "描述": "富含蛋白质的豆类作物", + "耐候性": 9, + "等级": 2, + "经验": 18, + "能否购买": true + }, + "豌豆": { + "作物名称": "豌豆", + "成熟物名称": "豌豆", + "花费": 65, + "生长时间": 720, + "收益": 110, + "品质": "普通", + "描述": "嫩绿的豆荚蔬菜", + "耐候性": 10, + "等级": 2, + "经验": 15, + "能否购买": true + }, + "黄瓜": { + "作物名称": "黄瓜", + "成熟物名称": "黄瓜", + "花费": 85, + "生长时间": 1200, + "收益": 150, + "品质": "普通", + "描述": "清脆爽口的瓜类", + "耐候性": 8, + "等级": 2, + "经验": 18, + "能否购买": true + }, + "大白菜": { + "作物名称": "大白菜", + "成熟物名称": "大白菜", + "花费": 70, + "生长时间": 900, + "收益": 120, + "品质": "普通", + "描述": "北方冬季的主要蔬菜", + "耐候性": 15, + "等级": 2, + "经验": 16, + "能否购买": true + }, + "葡萄": { + "作物名称": "葡萄", + "成熟物名称": "葡萄", + "花费": 200, + "生长时间": 2700, + "收益": 340, + "品质": "普通", + "描述": "用于酿酒的珍贵果实", + "耐候性": 9, + "等级": 4, + "经验": 35, + "能否购买": true + }, + "南瓜": { + "作物名称": "南瓜", + "成熟物名称": "南瓜", + "花费": 180, + "生长时间": 3600, + "收益": 320, + "品质": "普通", + "描述": "大型瓜果,节庆装饰的首选", + "耐候性": 12, + "等级": 4, + "经验": 38, + "能否购买": true + }, + "茄子": { + "作物名称": "茄子", + "成熟物名称": "茄子", + "花费": 190, + "生长时间": 2400, + "收益": 310, + "品质": "普通", + "描述": "紫色的营养蔬菜", + "耐候性": 10, + "等级": 4, + "经验": 32, + "能否购买": true + }, + "玉米": { + "作物名称": "玉米", + "成熟物名称": "玉米", + "花费": 80, + "生长时间": 900, + "收益": 130, + "品质": "优良", + "描述": "高产的谷物作物,营养价值高", + "耐候性": 10, + "等级": 2, + "经验": 15, + "能否购买": true + }, + "葫芦": { + "作物名称": "葫芦", + "成熟物名称": "葫芦", + "花费": 85, + "生长时间": 1080, + "收益": 145, + "品质": "优良", + "描述": "富含蛋白质的豆类作物", + "耐候性": 9, + "等级": 2, + "经验": 18, + "能否购买": true + }, + "柠檬": { + "作物名称": "柠檬树", + "成熟物名称": "柠檬", + "花费": 85, + "生长时间": 1080, + "收益": 145, + "品质": "优良", + "描述": "富含蛋白质的豆类作物", + "耐候性": 9, + "等级": 2, + "经验": 18, + "能否购买": true + }, + "枇杷": { + "作物名称": "枇杷树", + "成熟物名称": "枇杷", + "花费": 85, + "生长时间": 1080, + "收益": 145, + "品质": "优良", + "描述": "富含蛋白质的豆类作物", + "耐候性": 9, + "等级": 2, + "经验": 18, + "能否购买": true + }, + "山楂": { + "作物名称": "山楂树", + "成熟物名称": "山楂", + "花费": 85, + "生长时间": 1080, + "收益": 145, + "品质": "优良", + "描述": "富含蛋白质的豆类作物", + "耐候性": 9, + "等级": 2, + "经验": 18, + "能否购买": true + }, + "仙人掌": { + "作物名称": "仙人掌", + "成熟物名称": "仙人掌", + "花费": 85, + "生长时间": 1080, + "收益": 145, + "品质": "优良", + "描述": "富含蛋白质的豆类作物", + "耐候性": 9, + "等级": 2, + "经验": 18, + "能否购买": true + }, + "椰子": { + "作物名称": "椰子树", + "成熟物名称": "椰子", + "花费": 85, + "生长时间": 1080, + "收益": 145, + "品质": "优良", + "描述": "富含蛋白质的豆类作物", + "耐候性": 9, + "等级": 2, + "经验": 18, + "能否购买": true + }, + "西瓜": { + "作物名称": "西瓜", + "成熟物名称": "西瓜", + "花费": 250, + "生长时间": 4800, + "收益": 450, + "品质": "优良", + "描述": "夏日消暑的大型水果", + "耐候性": 6, + "等级": 5, + "经验": 50, + "能否购买": true + }, + "甘蔗": { + "作物名称": "甘蔗", + "成熟物名称": "甘蔗", + "花费": 280, + "生长时间": 5400, + "收益": 500, + "品质": "优良", + "描述": "制糖的重要原料", + "耐候性": 5, + "等级": 5, + "经验": 55, + "能否购买": true + }, + "甜菜": { + "作物名称": "甜菜", + "成熟物名称": "甜菜", + "花费": 240, + "生长时间": 4200, + "收益": 420, + "品质": "优良", + "描述": "制糖的另一种选择", + "耐候性": 11, + "等级": 5, + "经验": 45, + "能否购买": true + }, + "郁金香": { + "作物名称": "郁金香", + "成熟物名称": "郁金香", + "花费": 280, + "生长时间": 5100, + "收益": 510, + "品质": "优良", + "描述": "美丽的观赏植物", + "耐候性": 10, + "等级": 5, + "经验": 50, + "能否购买": true + }, + "花椰菜": { + "作物名称": "花椰菜", + "成熟物名称": "花椰菜", + "花费": 110, + "生长时间": 1320, + "收益": 185, + "品质": "优良", + "描述": "营养丰富的十字花科蔬菜", + "耐候性": 8, + "等级": 3, + "经验": 20, + "能否购买": true + }, + "柿子": { + "作物名称": "柿子树", + "成熟物名称": "柿子", + "花费": 140, + "生长时间": 1800, + "收益": 230, + "品质": "优良", + "描述": "秋季成熟的甜美果实", + "耐候性": 10, + "等级": 3, + "经验": 25, + "能否购买": true + }, + "梨子": { + "作物名称": "梨树", + "成熟物名称": "梨子", + "花费": 210, + "生长时间": 2820, + "收益": 350, + "品质": "优良", + "描述": "春季开花的落叶果树", + "耐候性": 10, + "等级": 3, + "经验": 25, + "能否购买": true + }, + "桃子": { + "作物名称": "桃树", + "成熟物名称": "桃子", + "花费": 220, + "生长时间": 2940, + "收益": 360, + "品质": "优良", + "描述": "粉色花海中的果实树种", + "耐候性": 9, + "等级": 3, + "经验": 25, + "能否购买": true, + "作物ID": "作物35", + "喂养效果": { + "经验": 10, + "生命值": 3, + "攻击力": 2, + "移动速度": 1, + "亲密度": 5 + } + }, + "荔枝": { + "作物名称": "荔枝树", + "成熟物名称": "荔枝", + "花费": 230, + "生长时间": 3060, + "收益": 380, + "品质": "优良", + "描述": "亚热带珍稀水果,果肉如凝脂", + "耐候性": 8, + "等级": 3, + "经验": 26, + "能否购买": true + }, + "树莓": { + "作物名称": "树莓", + "成熟物名称": "树莓", + "花费": 140, + "生长时间": 2700, + "收益": 240, + "品质": "优良", + "描述": "小巧精致的浆果", + "耐候性": 9, + "等级": 3, + "经验": 30, + "能否购买": true + }, + "鱼腥草": { + "作物名称": "鱼腥草", + "成熟物名称": "鱼腥草", + "花费": 300, + "生长时间": 7200, + "收益": 600, + "品质": "优良", + "描述": "具有特殊药用价值的野菜", + "耐候性": 18, + "等级": 6, + "经验": 90, + "能否购买": true + }, + "芦笋": { + "作物名称": "芦笋", + "成熟物名称": "芦笋", + "花费": 220, + "生长时间": 3000, + "收益": 370, + "品质": "优良", + "描述": "高档蔬菜,营养价值极高", + "耐候性": 8, + "等级": 4, + "经验": 40, + "能否购买": true + }, + "苹果": { + "作物名称": "苹果树", + "成熟物名称": "苹果", + "花费": 450, + "生长时间": 9600, + "收益": 820, + "品质": "优良", + "描述": "结果丰富的果树", + "耐候性": 14, + "等级": 7, + "经验": 110, + "能否购买": true, + "喂养效果": { + "生命值": 5, + "护甲值": 2 + } + }, + "橘子": { + "作物名称": "橘子树", + "成熟物名称": "橘子", + "花费": 480, + "生长时间": 10200, + "收益": 850, + "品质": "优良", + "描述": "维生素C丰富的果树", + "耐候性": 12, + "等级": 7, + "经验": 115, + "能否购买": true + }, + "香蕉": { + "作物名称": "香蕉树", + "成熟物名称": "香蕉", + "花费": 420, + "生长时间": 8400, + "收益": 780, + "品质": "优良", + "描述": "热带水果之王", + "耐候性": 5, + "等级": 7, + "经验": 100, + "能否购买": true + }, + "牵牛花": { + "作物名称": "牵牛花", + "成熟物名称": "牵牛花", + "花费": 85, + "生长时间": 1080, + "收益": 145, + "品质": "稀有", + "描述": "富含蛋白质的豆类作物", + "耐候性": 9, + "等级": 2, + "经验": 18, + "能否购买": true + }, + "百合花": { + "作物名称": "百合花", + "成熟物名称": "百合花", + "花费": 260, + "生长时间": 4800, + "收益": 480, + "品质": "稀有", + "描述": "美丽的观赏植物", + "耐候性": 10, + "等级": 5, + "经验": 50, + "能否购买": true + }, + "山葵": { + "作物名称": "山葵", + "成熟物名称": "山葵", + "花费": 500, + "生长时间": 10800, + "收益": 1000, + "品质": "稀有", + "描述": "日式料理的珍贵调料", + "耐候性": 22, + "等级": 7, + "经验": 150, + "能否购买": true + }, + "草莓": { + "作物名称": "草莓", + "成熟物名称": "草莓", + "花费": 120, + "生长时间": 1440, + "收益": 200, + "品质": "稀有", + "描述": "甜美的浆果,深受喜爱", + "耐候性": 6, + "等级": 3, + "经验": 22, + "能否购买": true, + "喂养效果": { + "经验": 8, + "暴击率": 3, + "亲密度": 10 + } + }, + "蓝莓": { + "作物名称": "蓝莓", + "成熟物名称": "蓝莓", + "花费": 160, + "生长时间": 2100, + "收益": 260, + "品质": "稀有", + "描述": "抗氧化的超级食物", + "耐候性": 7, + "等级": 3, + "经验": 28, + "能否购买": true + }, + "栀子花": { + "作物名称": "栀子花", + "成熟物名称": "栀子花", + "花费": 180, + "生长时间": 2400, + "收益": 300, + "品质": "稀有", + "描述": "美丽的观赏植物", + "耐候性": 10, + "等级": 3, + "经验": 25, + "能否购买": true + }, + "玫瑰花": { + "作物名称": "玫瑰花", + "成熟物名称": "玫瑰花", + "花费": 190, + "生长时间": 2520, + "收益": 310, + "品质": "稀有", + "描述": "美丽的观赏植物", + "耐候性": 10, + "等级": 3, + "经验": 25, + "能否购买": true + }, + "菠萝": { + "作物名称": "菠萝", + "成熟物名称": "菠萝", + "花费": 200, + "生长时间": 2700, + "收益": 340, + "品质": "稀有", + "描述": "热带水果之王", + "耐候性": 10, + "等级": 3, + "经验": 25, + "能否购买": true + }, + "芒果": { + "作物名称": "芒果树", + "成熟物名称": "芒果", + "花费": 240, + "生长时间": 3180, + "收益": 400, + "品质": "稀有", + "描述": "热带阳光孕育的香甜果实", + "耐候性": 10, + "等级": 3, + "经验": 27, + "能否购买": true + }, + "咖啡豆": { + "作物名称": "咖啡豆", + "成熟物名称": "咖啡豆", + "花费": 200, + "生长时间": 2500, + "收益": 340, + "品质": "稀有", + "描述": "热带经济作物,果实经烘焙后可制成饮品,需温暖气候", + "耐候性": 8, + "等级": 4, + "经验": 35, + "能否购买": true + }, + "可可豆": { + "作物名称": "可可树", + "成熟物名称": "可可果", + "花费": 200, + "生长时间": 2500, + "收益": 340, + "品质": "稀有", + "描述": "制作巧克力的原料,需高温高湿环境,经济价值较高", + "耐候性": 10, + "等级": 4, + "经验": 36, + "能否购买": true + }, + "向日葵": { + "作物名称": "向日葵", + "成熟物名称": "向日葵", + "花费": 160, + "生长时间": 3600, + "收益": 280, + "品质": "稀有", + "描述": "向阳而生的美丽花朵", + "耐候性": 13, + "等级": 4, + "经验": 35, + "能否购买": true + }, + "哈密瓜": { + "作物名称": "哈密瓜", + "成熟物名称": "哈密瓜", + "花费": 380, + "生长时间": 7800, + "收益": 700, + "品质": "稀有", + "描述": "甘甜的高级水果", + "耐候性": 8, + "等级": 6, + "经验": 85, + "能否购买": true + }, + "藏红花": { + "作物名称": "藏红花", + "成熟物名称": "藏红花", + "花费": 400, + "生长时间": 7000, + "收益": 710, + "品质": "稀有", + "描述": "世界上最昂贵的香料之一,需精心照料,花朵可提取柱头", + "耐候性": 9, + "等级": 6, + "经验": 80, + "能否购买": true + }, + "龙果": { + "作物名称": "火龙果", + "成熟物名称": "火龙果", + "花费": 800, + "生长时间": 14400, + "收益": 1500, + "品质": "稀有", + "描述": "传说中的神秘果实,蕴含强大能量", + "耐候性": 25, + "等级": 8, + "经验": 200, + "能否购买": true + }, + "杨桃": { + "作物名称": "杨桃", + "成熟物名称": "杨桃", + "花费": 85, + "生长时间": 1080, + "收益": 145, + "品质": "史诗", + "描述": "富含蛋白质的豆类作物", + "耐候性": 9, + "等级": 2, + "经验": 18, + "能否购买": true + }, + "香草": { + "作物名称": "香草", + "成熟物名称": "香草", + "花费": 300, + "生长时间": 6000, + "收益": 550, + "品质": "史诗", + "描述": "珍贵的调料植物", + "耐候性": 7, + "等级": 5, + "经验": 60, + "能否购买": true + }, + "康乃馨": { + "作物名称": "康乃馨", + "成熟物名称": "康乃馨", + "花费": 270, + "生长时间": 5100, + "收益": 510, + "品质": "史诗", + "描述": "美丽的观赏植物", + "耐候性": 10, + "等级": 5, + "经验": 50, + "能否购买": true + }, + "藏羚羊草": { + "作物名称": "藏羚羊草", + "成熟物名称": "藏羚羊草", + "花费": 80, + "生长时间": 200, + "收益": 200, + "品质": "史诗", + "描述": "高原珍稀草种,用于生态修复,对土壤要求苛刻但市场需求大", + "耐候性": 15, + "等级": 5, + "经验": 20, + "能否购买": true + }, + "迷迭香": { + "作物名称": "迷迭香", + "成熟物名称": "迷迭香", + "花费": 100, + "生长时间": 1680, + "收益": 210, + "品质": "史诗", + "描述": "芳香型草本植物,可用于烹饪和精油提取,耐旱易存活", + "耐候性": 12, + "等级": 3, + "经验": 22, + "能否购买": true + }, + "蕨菜": { + "作物名称": "蕨菜", + "成熟物名称": "蕨菜", + "花费": 180, + "生长时间": 2400, + "收益": 300, + "品质": "史诗", + "描述": "野生山菜,口感独特", + "耐候性": 16, + "等级": 4, + "经验": 35, + "能否购买": true + }, + "人参": { + "作物名称": "人参", + "成熟物名称": "人参", + "花费": 400, + "生长时间": 7200, + "收益": 720, + "品质": "史诗", + "描述": "珍贵的药用植物,需要耐心培养", + "耐候性": 15, + "等级": 6, + "经验": 80, + "能否购买": true + }, + "富贵竹": { + "作物名称": "富贵竹", + "成熟物名称": "富贵竹", + "花费": 350, + "生长时间": 6600, + "收益": 650, + "品质": "史诗", + "描述": "寓意吉祥的观赏植物", + "耐候性": 12, + "等级": 6, + "经验": 75, + "能否购买": true + }, + "芦荟": { + "作物名称": "芦荟", + "成熟物名称": "芦荟", + "花费": 320, + "生长时间": 6000, + "收益": 600, + "品质": "史诗", + "描述": "具有药用价值的多肉植物", + "耐候性": 18, + "等级": 6, + "经验": 70, + "能否购买": true + }, + "金橘": { + "作物名称": "金橘子树", + "成熟物名称": "金橘", + "花费": 500, + "生长时间": 10800, + "收益": 900, + "品质": "史诗", + "描述": "金黄色的珍贵柑橘", + "耐候性": 10, + "等级": 7, + "经验": 120, + "能否购买": true + }, + "松露": { + "作物名称": "松露", + "成熟物名称": "松露", + "花费": 1000, + "生长时间": 18000, + "收益": 2000, + "品质": "史诗", + "描述": "地下的黑金,顶级料理的灵魂", + "耐候性": 20, + "等级": 8, + "经验": 250, + "能否购买": true + }, + "冬虫夏草": { + "作物名称": "冬虫夏草", + "成熟物名称": "冬虫夏草", + "花费": 600, + "生长时间": 12000, + "收益": 1200, + "品质": "史诗", + "描述": "稀世珍宝,药王之称", + "耐候性": 30, + "等级": 7, + "经验": 180, + "能否购买": true + }, + "糖果树": { + "作物名称": "糖果树", + "成熟物名称": null, + "花费": 250, + "生长时间": 3300, + "收益": 420, + "品质": "传奇", + "描述": "传说中结出彩色糖果的魔法树", + "耐候性": 7, + "等级": 3, + "经验": 25, + "能否购买": false + }, + "面包树": { + "作物名称": "面包树", + "成熟物名称": null, + "花费": 260, + "生长时间": 3420, + "收益": 440, + "品质": "传奇", + "描述": "热带地区的淀粉质主食树种", + "耐候性": 9, + "等级": 3, + "经验": 25, + "能否购买": false + }, + "幸运草": { + "作物名称": "幸运草", + "成熟物名称": null, + "花费": 150, + "生长时间": 1560, + "收益": 220, + "品质": "传奇", + "描述": "四片叶子的幸运象征植物", + "耐候性": 6, + "等级": 3, + "经验": 20, + "能否购买": false + }, + "幸运花": { + "作物名称": "幸运花", + "成熟物名称": null, + "花费": 170, + "生长时间": 1680, + "收益": 240, + "品质": "传奇", + "描述": "绽放时带来好运的神秘花卉", + "耐候性": 7, + "等级": 3, + "经验": 22, + "能否购买": false + }, + "摇钱树": { + "作物名称": "摇钱树", + "成熟物名称": null, + "花费": 300, + "生长时间": 3600, + "收益": 500, + "品质": "传奇", + "描述": "传说中能结出金币的神树", + "耐候性": 10, + "等级": 3, + "经验": 30, + "能否购买": false + }, + "月光草": { + "作物名称": "月光草", + "成熟物名称": "月光草", + "花费": 700, + "生长时间": 14500, + "收益": 1500, + "品质": "传奇", + "描述": "夜间开花的神秘植物,花瓣在月光下散发荧光,传说具有许愿功效", + "耐候性": 20, + "等级": 8, + "经验": 200, + "能否购买": false + }, + "凤凰木": { + "作物名称": "凤凰树", + "成熟物名称": "凤凰木", + "花费": 650, + "生长时间": 12600, + "收益": 1400, + "品质": "传奇", + "描述": "传说中凤凰栖息的神树,开花时如火焰般绚烂,木材可驱邪避灾", + "耐候性": 25, + "等级": 8, + "经验": 210, + "能否购买": false + }, + "杂交树1": { + "作物名称": "0号杂交树", + "成熟物名称": null, + "花费": 1200, + "生长时间": 21600, + "收益": 2500, + "品质": "传奇", + "描述": "初中生物书最后的幻想", + "耐候性": 35, + "等级": 9, + "经验": 300, + "能否购买": false + }, + "杂交树2": { + "作物名称": "1号杂交树", + "成熟物名称": null, + "花费": 1500, + "生长时间": 25200, + "收益": 3000, + "品质": "传奇", + "描述": "初中生物书最后的想象", + "耐候性": 40, + "等级": 10, + "经验": 400, + "能否购买": false + } +} \ No newline at end of file diff --git a/Server/test/游戏配置/访问系统.json b/Server/test/游戏配置/访问系统.json new file mode 100644 index 0000000..fc42da3 --- /dev/null +++ b/Server/test/游戏配置/访问系统.json @@ -0,0 +1,8 @@ +"访问系统":{ + "总访问人数":0, + "今日访问人数":0, + "访问记录":{ + "2025-07-21":["树萌芽","柚大青"], + "2025-07-1":["树萌芽","柚大青","大萌芽"], + } +} \ No newline at end of file diff --git a/Server/test/特殊农场/小麦谷222.json b/Server/test/特殊农场/小麦谷222.json new file mode 100644 index 0000000..5daab29 --- /dev/null +++ b/Server/test/特殊农场/小麦谷222.json @@ -0,0 +1,968 @@ +{ + "经验值": 0, + "等级": 1, + "钱币": 0, + "农场名称": "小麦谷", + "玩家昵称": "小麦谷", + "玩家账号": "222", + "玩家密码": "tyh@19900420", + "最后登录时间": "2025年07月20日17时19分16秒", + "总游玩时间": "0时0分0秒", + "注册时间": "2025年05月21日15时00分00秒", + "个人简介": "盛产小麦的农场", + "农场土地": [ + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + } + ], + "种子仓库": [], + "作物仓库": [], + "道具背包": [], + "宠物背包": [], + "巡逻宠物": [], + "出战宠物": [], + "稻草人配置": { + "已拥有稻草人类型": [ + "稻草人1" + ], + "稻草人展示类型": "稻草人1", + "稻草人昵称": "稻草人", + "稻草人说的话": { + "第一句话": { + "内容": "这里是花谷", + "颜色": "52dceeff" + }, + "第二句话": { + "内容": "一个盛产各种奇异花卉的地方", + "颜色": "80d5ffff" + }, + "第三句话": { + "内容": "星期一,三,五,七零点种植", + "颜色": "ac52ffff" + }, + "第四句话": { + "内容": "欢迎参观!", + "颜色": "f881ffff" + } + }, + "稻草人昵称颜色": "b38282ff" + }, + "智慧树配置": { + "距离上一次杀虫时间": 1753004237, + "距离上一次除草时间": 1753004237, + "智慧树显示的话": "", + "等级": 1, + "当前经验值": 0, + "最大经验值": 100, + "最大生命值": 100, + "当前生命值": 100, + "高度": 20 + }, + "签到历史": {}, + "在线礼包": { + "当前日期": "2025-07-20", + "今日在线时长": 0, + "已领取礼包": [], + "登录时间": 1753003043.7163484 + }, + "点赞系统": { + "今日剩余点赞次数": 10, + "点赞上次刷新时间": "2025-07-20", + "总点赞数": 0 + }, + "新手礼包": { + "已领取": false, + "领取时间": "2025-07-12 23:02:25" + }, + "体力系统": { + "当前体力值": 20, + "最大体力值": 20, + "上次刷新时间": "2025-07-20", + "上次恢复时间": 1753003043.7066433 + }, + "小卖部配置": { + "商品列表": [], + "格子数": 10 + }, + "游戏设置": { + "背景音乐音量": 1, + "天气显示": true + } +} \ No newline at end of file diff --git a/Server/test/特殊农场/幸运农场888.json b/Server/test/特殊农场/幸运农场888.json new file mode 100644 index 0000000..9d7d33e --- /dev/null +++ b/Server/test/特殊农场/幸运农场888.json @@ -0,0 +1,968 @@ +{ + "经验值": 0, + "等级": 1, + "钱币": 0, + "农场名称": "幸运农场", + "玩家昵称": "幸运农场", + "玩家账号": "111", + "玩家密码": "tyh@19900420", + "最后登录时间": "2025年07月20日17时19分16秒", + "总游玩时间": "0时0分0秒", + "注册时间": "2025年05月21日15时00分00秒", + "个人简介": "盛产幸运草和幸运花的农场", + "农场土地": [ + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + } + ], + "种子仓库": [], + "作物仓库": [], + "道具背包": [], + "宠物背包": [], + "巡逻宠物": [], + "出战宠物": [], + "稻草人配置": { + "已拥有稻草人类型": [ + "稻草人1" + ], + "稻草人展示类型": "稻草人1", + "稻草人昵称": "稻草人", + "稻草人说的话": { + "第一句话": { + "内容": "这里是花谷", + "颜色": "52dceeff" + }, + "第二句话": { + "内容": "一个盛产各种奇异花卉的地方", + "颜色": "80d5ffff" + }, + "第三句话": { + "内容": "星期一,三,五,七零点种植", + "颜色": "ac52ffff" + }, + "第四句话": { + "内容": "欢迎参观!", + "颜色": "f881ffff" + } + }, + "稻草人昵称颜色": "b38282ff" + }, + "智慧树配置": { + "距离上一次杀虫时间": 1753004237, + "距离上一次除草时间": 1753004237, + "智慧树显示的话": "", + "等级": 1, + "当前经验值": 0, + "最大经验值": 100, + "最大生命值": 100, + "当前生命值": 100, + "高度": 20 + }, + "签到历史": {}, + "在线礼包": { + "当前日期": "2025-07-20", + "今日在线时长": 0, + "已领取礼包": [], + "登录时间": 1753003043.7163484 + }, + "点赞系统": { + "今日剩余点赞次数": 10, + "点赞上次刷新时间": "2025-07-20", + "总点赞数": 0 + }, + "新手礼包": { + "已领取": false, + "领取时间": "2025-07-12 23:02:25" + }, + "体力系统": { + "当前体力值": 20, + "最大体力值": 20, + "上次刷新时间": "2025-07-20", + "上次恢复时间": 1753003043.7066433 + }, + "小卖部配置": { + "商品列表": [], + "格子数": 10 + }, + "游戏设置": { + "背景音乐音量": 1, + "天气显示": true + } +} \ No newline at end of file diff --git a/Server/test/特殊农场/杂交农场666.json b/Server/test/特殊农场/杂交农场666.json new file mode 100644 index 0000000..3a25880 --- /dev/null +++ b/Server/test/特殊农场/杂交农场666.json @@ -0,0 +1,971 @@ +{ + "_id": { + "$oid": "689b4b9286cf953f2f4e56ee" + }, + "经验值": 0, + "等级": 1, + "钱币": 0, + "农场名称": "杂交农场", + "玩家昵称": "杂交农场", + "玩家账号": "666", + "玩家密码": "tyh@19900420", + "最后登录时间": "2025年07月20日17时19分16秒", + "总游玩时间": "0时0分0秒", + "注册时间": "2025年05月21日15时00分00秒", + "个人简介": "盛产杂交果实的农场", + "农场土地": [ + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + } + ], + "种子仓库": [], + "作物仓库": [], + "道具背包": [], + "宠物背包": [], + "巡逻宠物": [], + "出战宠物": [], + "稻草人配置": { + "已拥有稻草人类型": [ + "稻草人1" + ], + "稻草人展示类型": "", + "稻草人昵称": "稻草人", + "稻草人说的话": { + "第一句话": { + "内容": "第一句话", + "颜色": "52dceeff" + }, + "第二句话": { + "内容": "第二句话", + "颜色": "80d5ffff" + }, + "第三句话": { + "内容": "第三句话", + "颜色": "ac52ffff" + }, + "第四句话": { + "内容": "第四句话", + "颜色": "f881ffff" + } + }, + "稻草人昵称颜色": "b38282ff" + }, + "智慧树配置": { + "距离上一次杀虫时间": 1753004237, + "距离上一次除草时间": 1753004237, + "智慧树显示的话": "", + "等级": 1, + "当前经验值": 0, + "最大经验值": 100, + "最大生命值": 100, + "当前生命值": 100, + "高度": 20 + }, + "签到历史": {}, + "在线礼包": { + "当前日期": "2025-07-20", + "今日在线时长": 0, + "已领取礼包": [], + "登录时间": 1753003043.7163484 + }, + "点赞系统": { + "今日剩余点赞次数": 10, + "点赞上次刷新时间": "2025-07-20", + "总点赞数": 0 + }, + "新手礼包": { + "已领取": false, + "领取时间": "2025-07-12 23:02:25" + }, + "体力系统": { + "当前体力值": 20, + "最大体力值": 20, + "上次刷新时间": "2025-07-20", + "上次恢复时间": 1753003043.7066433 + }, + "小卖部配置": { + "商品列表": [], + "格子数": 10 + }, + "游戏设置": { + "背景音乐音量": 1, + "天气显示": true + } +} \ No newline at end of file diff --git a/Server/test/特殊农场/瓜果农场333.json b/Server/test/特殊农场/瓜果农场333.json new file mode 100644 index 0000000..09b22d8 --- /dev/null +++ b/Server/test/特殊农场/瓜果农场333.json @@ -0,0 +1,968 @@ +{ + "经验值": 0, + "等级": 1, + "钱币": 0, + "农场名称": "瓜果农场", + "玩家昵称": "瓜果农场", + "玩家账号": "333", + "玩家密码": "tyh@19900420", + "最后登录时间": "2025年07月20日17时19分16秒", + "总游玩时间": "0时0分0秒", + "注册时间": "2025年05月21日15时00分00秒", + "个人简介": "盛产各种瓜果的农场", + "农场土地": [ + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + } + ], + "种子仓库": [], + "作物仓库": [], + "道具背包": [], + "宠物背包": [], + "巡逻宠物": [], + "出战宠物": [], + "稻草人配置": { + "已拥有稻草人类型": [ + "稻草人1" + ], + "稻草人展示类型": "稻草人1", + "稻草人昵称": "稻草人", + "稻草人说的话": { + "第一句话": { + "内容": "这里是花谷", + "颜色": "52dceeff" + }, + "第二句话": { + "内容": "一个盛产各种奇异花卉的地方", + "颜色": "80d5ffff" + }, + "第三句话": { + "内容": "星期一,三,五,七零点种植", + "颜色": "ac52ffff" + }, + "第四句话": { + "内容": "欢迎参观!", + "颜色": "f881ffff" + } + }, + "稻草人昵称颜色": "b38282ff" + }, + "智慧树配置": { + "距离上一次杀虫时间": 1753004237, + "距离上一次除草时间": 1753004237, + "智慧树显示的话": "", + "等级": 1, + "当前经验值": 0, + "最大经验值": 100, + "最大生命值": 100, + "当前生命值": 100, + "高度": 20 + }, + "签到历史": {}, + "在线礼包": { + "当前日期": "2025-07-20", + "今日在线时长": 0, + "已领取礼包": [], + "登录时间": 1753003043.7163484 + }, + "点赞系统": { + "今日剩余点赞次数": 10, + "点赞上次刷新时间": "2025-07-20", + "总点赞数": 0 + }, + "新手礼包": { + "已领取": false, + "领取时间": "2025-07-12 23:02:25" + }, + "体力系统": { + "当前体力值": 20, + "最大体力值": 20, + "上次刷新时间": "2025-07-20", + "上次恢复时间": 1753003043.7066433 + }, + "小卖部配置": { + "商品列表": [], + "格子数": 10 + }, + "游戏设置": { + "背景音乐音量": 1, + "天气显示": true + } +} \ No newline at end of file diff --git a/Server/test/特殊农场/稻香111.json b/Server/test/特殊农场/稻香111.json new file mode 100644 index 0000000..7bf39f6 --- /dev/null +++ b/Server/test/特殊农场/稻香111.json @@ -0,0 +1,968 @@ +{ + "经验值": 0, + "等级": 1, + "钱币": 0, + "农场名称": "稻香", + "玩家昵称": "稻香", + "玩家账号": "111", + "玩家密码": "tyh@19900420", + "最后登录时间": "2025年07月20日17时19分16秒", + "总游玩时间": "0时0分0秒", + "注册时间": "2025年05月21日15时00分00秒", + "个人简介": "盛产稻谷的农场", + "农场土地": [ + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + } + ], + "种子仓库": [], + "作物仓库": [], + "道具背包": [], + "宠物背包": [], + "巡逻宠物": [], + "出战宠物": [], + "稻草人配置": { + "已拥有稻草人类型": [ + "稻草人1" + ], + "稻草人展示类型": "稻草人1", + "稻草人昵称": "稻草人", + "稻草人说的话": { + "第一句话": { + "内容": "这里是花谷", + "颜色": "52dceeff" + }, + "第二句话": { + "内容": "一个盛产各种奇异花卉的地方", + "颜色": "80d5ffff" + }, + "第三句话": { + "内容": "星期一,三,五,七零点种植", + "颜色": "ac52ffff" + }, + "第四句话": { + "内容": "欢迎参观!", + "颜色": "f881ffff" + } + }, + "稻草人昵称颜色": "b38282ff" + }, + "智慧树配置": { + "距离上一次杀虫时间": 1753004237, + "距离上一次除草时间": 1753004237, + "智慧树显示的话": "", + "等级": 1, + "当前经验值": 0, + "最大经验值": 100, + "最大生命值": 100, + "当前生命值": 100, + "高度": 20 + }, + "签到历史": {}, + "在线礼包": { + "当前日期": "2025-07-20", + "今日在线时长": 0, + "已领取礼包": [], + "登录时间": 1753003043.7163484 + }, + "点赞系统": { + "今日剩余点赞次数": 10, + "点赞上次刷新时间": "2025-07-20", + "总点赞数": 0 + }, + "新手礼包": { + "已领取": false, + "领取时间": "2025-07-12 23:02:25" + }, + "体力系统": { + "当前体力值": 20, + "最大体力值": 20, + "上次刷新时间": "2025-07-20", + "上次恢复时间": 1753003043.7066433 + }, + "小卖部配置": { + "商品列表": [], + "格子数": 10 + }, + "游戏设置": { + "背景音乐音量": 1, + "天气显示": true + } +} \ No newline at end of file diff --git a/Server/test/特殊农场/花卉农场520.json b/Server/test/特殊农场/花卉农场520.json new file mode 100644 index 0000000..7040d6d --- /dev/null +++ b/Server/test/特殊农场/花卉农场520.json @@ -0,0 +1,968 @@ +{ + "经验值": 0, + "等级": 1, + "钱币": 0, + "农场名称": "花谷", + "玩家昵称": "花谷", + "玩家账号": "520", + "玩家密码": "tyh@19900420", + "最后登录时间": "2025年07月20日17时19分16秒", + "总游玩时间": "0时0分0秒", + "注册时间": "2025年05月21日15时00分00秒", + "个人简介": "盛产各种花卉的农场", + "农场土地": [ + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": true, + "is_planted": false, + "max_grow_time": 3, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "", + "grow_time": 0, + "is_dead": false, + "is_diged": false, + "is_planted": false, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + } + ], + "种子仓库": [], + "作物仓库": [], + "道具背包": [], + "宠物背包": [], + "巡逻宠物": [], + "出战宠物": [], + "稻草人配置": { + "已拥有稻草人类型": [ + "稻草人1" + ], + "稻草人展示类型": "稻草人1", + "稻草人昵称": "稻草人", + "稻草人说的话": { + "第一句话": { + "内容": "这里是花谷", + "颜色": "52dceeff" + }, + "第二句话": { + "内容": "一个盛产各种奇异花卉的地方", + "颜色": "80d5ffff" + }, + "第三句话": { + "内容": "星期一,三,五,七零点种植", + "颜色": "ac52ffff" + }, + "第四句话": { + "内容": "欢迎参观!", + "颜色": "f881ffff" + } + }, + "稻草人昵称颜色": "b38282ff" + }, + "智慧树配置": { + "距离上一次杀虫时间": 1753004237, + "距离上一次除草时间": 1753004237, + "智慧树显示的话": "", + "等级": 1, + "当前经验值": 0, + "最大经验值": 100, + "最大生命值": 100, + "当前生命值": 100, + "高度": 20 + }, + "签到历史": {}, + "在线礼包": { + "当前日期": "2025-07-20", + "今日在线时长": 0, + "已领取礼包": [], + "登录时间": 1753003043.7163484 + }, + "点赞系统": { + "今日剩余点赞次数": 10, + "点赞上次刷新时间": "2025-07-20", + "总点赞数": 0 + }, + "新手礼包": { + "已领取": false, + "领取时间": "2025-07-12 23:02:25" + }, + "体力系统": { + "当前体力值": 20, + "最大体力值": 20, + "上次刷新时间": "2025-07-20", + "上次恢复时间": 1753003043.7066433 + }, + "小卖部配置": { + "商品列表": [], + "格子数": 10 + }, + "游戏设置": { + "背景音乐音量": 1, + "天气显示": true + } +} \ No newline at end of file diff --git a/Server/test/特殊农场管理系统说明.md b/Server/test/特殊农场管理系统说明.md new file mode 100644 index 0000000..2f30e41 --- /dev/null +++ b/Server/test/特殊农场管理系统说明.md @@ -0,0 +1,218 @@ +# 特殊农场管理系统 + +## 概述 + +特殊农场管理系统是一个自动化的农场维护工具,用于管理游戏中的特殊农场,为玩家提供特殊的不可购买作物。系统支持定时任务和手动维护两种模式。 + +## 功能特性 + +### 1. 自动定时维护 +- **执行时间**: 每天凌晨0点自动执行 +- **维护内容**: 为特殊农场种植指定的作物 +- **日志记录**: 详细记录维护过程和结果 + +### 2. 手动维护模式 +- 支持即时手动维护指定农场 +- 可用于测试和紧急维护 + +### 3. 多环境支持 +- **测试环境**: 用于开发和测试 +- **生产环境**: 用于正式运行 + +## 当前支持的特殊农场 + +### 杂交农场 +- **农场ID**: `689b4b9286cf953f2f4e56ee` +- **种植作物**: 0号杂交树、1号杂交树 +- **种植方式**: 随机全屏种植 +- **土地要求**: 仅在已开垦的土地上种植 +- **作物状态**: 自动设置为已浇水、已施肥、立即成熟 + +## 使用方法 + +### 方法一:使用启动脚本(推荐) +```bash +# Windows +start_special_farm.bat + +# 然后选择运行模式: +# 1. 测试环境 +# 2. 生产环境 +# 3. 手动维护模式 +``` + +### 方法二:直接命令行 + +#### 自动模式(定时任务) +```bash +# 测试环境 +python SpecialFarm.py test + +# 生产环境 +python SpecialFarm.py production +``` + +#### 手动维护模式 +```bash +# 手动维护杂交农场(测试环境) +python SpecialFarm.py test manual 杂交农场 + +# 手动维护杂交农场(生产环境) +python SpecialFarm.py production manual 杂交农场 +``` + +### 方法三:运行测试 +```bash +# 运行完整测试 +python test_special_farm.py +``` + +## 系统架构 + +### 核心组件 + +1. **SpecialFarmManager**: 主管理类 + - 负责农场配置管理 + - 处理定时任务调度 + - 执行种植逻辑 + +2. **MongoDB集成**: + - 使用 `SMYMongoDBAPI` 进行数据库操作 + - 支持玩家数据和作物配置的读写 + +3. **定时任务**: + - 使用 `schedule` 库实现定时功能 + - 支持每日凌晨0点自动执行 + +### 数据结构 + +#### 农场配置 +```python +special_farms = { + "杂交农场": { + "object_id": "689b4b9286cf953f2f4e56ee", + "crop_types": ["杂交树1", "杂交树2"] + } +} +``` + +#### 作物种植逻辑 +- 随机选择配置的作物类型 +- 仅在已开垦(`is_diged=True`)的土地上种植 +- 自动设置作物状态: + - `is_planted=True` + - `is_watered=True` + - `is_fertilized=True` + - `growth_time=0`(立即成熟) + +## 日志系统 + +### 日志文件 +- **文件名**: `special_farm.log` +- **位置**: 与脚本同目录 +- **格式**: `时间 - 级别 - 模块 - 消息` + +### 日志级别 +- **INFO**: 正常操作信息 +- **WARNING**: 警告信息 +- **ERROR**: 错误信息 + +### 示例日志 +``` +2024-01-15 00:00:01,123 - INFO - __main__ - 开始执行每日特殊农场维护任务... +2024-01-15 00:00:02,456 - INFO - __main__ - 成功为 杂交农场 种植了 40 块土地的作物 +2024-01-15 00:00:02,789 - INFO - __main__ - 每日维护任务完成: 1/1 个农场维护成功 +``` + +## 依赖要求 + +### Python包 +``` +schedule>=1.2.0 +pymongo>=4.0.0 +``` + +### 系统要求 +- Python 3.7+ +- MongoDB数据库连接 +- 有效的游戏数据库配置 + +## 扩展指南 + +### 添加新的特殊农场 + +1. **在数据库中创建农场数据** + - 在 `playerdata` 集合中添加新的农场文档 + - 记录文档的 `ObjectId` + +2. **更新农场配置** + ```python + # 在 SpecialFarm.py 中的 special_farms 字典添加 + "新农场名称": { + "object_id": "新农场的ObjectId", + "crop_types": ["作物类型1", "作物类型2"] + } + ``` + +3. **测试新农场** + ```bash + python SpecialFarm.py test manual 新农场名称 + ``` + +### 自定义种植逻辑 + +可以在 `plant_crops_in_farm` 方法中修改种植逻辑: +- 改变作物选择算法 +- 调整作物生长参数 +- 添加特殊种植条件 + +## 故障排除 + +### 常见问题 + +1. **数据库连接失败** + - 检查MongoDB服务是否运行 + - 验证数据库连接配置 + +2. **农场数据不存在** + - 确认农场ObjectId是否正确 + - 检查数据库中是否存在对应文档 + +3. **作物配置错误** + - 验证作物类型名称是否正确 + - 检查作物配置数据是否完整 + +### 调试模式 + +运行测试脚本获取详细信息: +```bash +python test_special_farm.py +``` + +## 安全注意事项 + +1. **生产环境使用** + - 确保在生产环境中使用正确的数据库配置 + - 定期备份重要数据 + +2. **权限管理** + - 确保脚本有足够的数据库读写权限 + - 限制对生产数据库的访问 + +3. **监控和告警** + - 定期检查日志文件 + - 设置维护任务失败的告警机制 + +## 版本历史 + +- **v1.0.0**: 初始版本,支持杂交农场的自动维护 + - 实现定时任务功能 + - 支持手动维护模式 + - 完整的日志系统 + - 测试和生产环境分离 + +--- + +**作者**: AI Assistant +**创建时间**: 2024年 +**最后更新**: 2024年 \ No newline at end of file diff --git a/Server/test_websocket.py b/Server/test_websocket.py new file mode 100644 index 0000000..d111326 --- /dev/null +++ b/Server/test_websocket.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +测试WebSocket远程命令API +""" + +import asyncio +import websockets +import json + +async def test_client(): + """测试WebSocket客户端""" + uri = "ws://localhost:7071" + + try: + async with websockets.connect(uri) as websocket: + print(f"✅ 已连接到 {uri}") + + # 接收欢迎消息 + welcome_msg = await websocket.recv() + print(f"收到欢迎消息: {welcome_msg}") + + # 发送认证请求 + auth_data = { + "type": "auth", + "auth_key": "mengya2024" + } + await websocket.send(json.dumps(auth_data)) + print("已发送认证请求") + + # 接收认证结果 + auth_result = await websocket.recv() + print(f"认证结果: {auth_result}") + + # 发送测试命令 + command_data = { + "type": "command", + "command": "help" + } + await websocket.send(json.dumps(command_data)) + print("已发送help命令") + + # 接收命令结果 + command_result = await websocket.recv() + print(f"命令结果: {command_result}") + + except Exception as e: + print(f"❌ 连接失败: {e}") + +if __name__ == "__main__": + print("开始测试WebSocket连接...") + asyncio.run(test_client()) \ No newline at end of file diff --git a/Server/特殊农场系统优化说明.md b/Server/特殊农场系统优化说明.md new file mode 100644 index 0000000..8e9d5c0 --- /dev/null +++ b/Server/特殊农场系统优化说明.md @@ -0,0 +1,199 @@ +# 特殊农场系统优化说明 + +## 概述 + +特殊农场系统已经过全面优化,解决了服务器重启时间重置和性能问题。现在系统更加稳定、高效,不会影响游戏服务器的正常运行。 + +## 主要优化内容 + +### 1. 解决服务器重启时间重置问题 + +**问题**: 之前每次服务器重启都会立即执行维护任务,导致重复种植。 + +**解决方案**: +- 添加维护时间记录功能 +- 服务器启动时检查当日是否已执行维护 +- 只有当天未维护过才会执行维护任务 +- 确保每天只执行一次维护,无论服务器重启多少次 + +### 2. 性能优化 + +**CPU使用优化**: +- 调度器检查间隔从1分钟优化到5分钟 +- 减少不必要的CPU占用 +- 后台线程使用守护模式,不阻塞主进程 + +**内存管理优化**: +- 改进资源清理机制 +- 服务器停止时正确释放特殊农场系统资源 +- 避免内存泄漏 + +### 3. 稳定性改进 + +**错误处理**: +- 增强异常处理机制 +- 添加详细的日志记录 +- 系统出错时自动恢复 + +**资源管理**: +- 正确的启动和停止流程 +- 线程安全的操作 +- 避免资源竞争 + +## 系统工作原理 + +### 定时任务调度 + +``` +启动服务器 → 初始化特殊农场管理器 → 检查当日维护状态 + ↓ +如果未维护 → 执行维护任务 → 记录维护时间 + ↓ +如果已维护 → 跳过维护任务 + ↓ +启动后台调度器 → 每天凌晨0点自动维护 +``` + +### 维护任务内容 + +1. **土地开垦**: 确保所有土地都处于已开垦状态 +2. **作物种植**: 随机种植杂交树1或杂交树2 +3. **状态设置**: 设置作物为立即成熟状态 +4. **记录更新**: 更新维护时间记录 + +## 使用方法 + +### 正常启动 + +特殊农场系统已集成到游戏服务器中,无需单独启动: + +```bash +cd Server +python TCPGameServer.py +``` + +### 手动维护 + +如果需要手动执行维护任务: + +```bash +# 维护所有特殊农场 +python SpecialFarm.py test manual + +# 维护指定农场 +python SpecialFarm.py test manual 杂交农场 +``` + +### 性能监控 + +使用性能监控工具检查系统资源使用: + +```bash +python monitor_special_farm.py +``` + +### 测试优化效果 + +运行测试脚本验证优化效果: + +```bash +python test_special_farm_optimization.py +``` + +## 配置说明 + +### 特殊农场配置 + +在 `SpecialFarm.py` 中的 `special_farms` 字典: + +```python +self.special_farms = { + "杂交农场": { + "object_id": "689b4b9286cf953f2f4e56ee", + "crops": ["杂交树1", "杂交树2"], + "description": "专门种植杂交树的特殊农场" + } +} +``` + +### 调度器配置 + +- **维护时间**: 每天凌晨0点 +- **检查间隔**: 5分钟 +- **运行模式**: 后台守护线程 + +## 性能指标 + +### 优化前 vs 优化后 + +| 指标 | 优化前 | 优化后 | 改进 | +|------|--------|--------|------| +| CPU检查间隔 | 1分钟 | 5分钟 | 减少80%检查频率 | +| 重启重复执行 | 是 | 否 | 避免重复维护 | +| 资源清理 | 不完整 | 完整 | 防止内存泄漏 | +| 错误恢复 | 基础 | 增强 | 提高稳定性 | + +### 预期性能表现 + +- **CPU使用率**: < 1%(正常运行时) +- **内存使用**: < 50MB(额外占用) +- **线程数**: +1(后台调度线程) +- **启动时间**: < 1秒 + +## 故障排除 + +### 常见问题 + +1. **特殊农场未自动维护** + - 检查MongoDB连接是否正常 + - 查看日志文件 `special_farm.log` + - 确认特殊农场配置正确 + +2. **服务器启动缓慢** + - 检查MongoDB连接速度 + - 查看是否有网络问题 + - 考虑使用本地MongoDB + +3. **内存使用过高** + - 运行性能监控工具 + - 检查是否有内存泄漏 + - 重启服务器释放资源 + +### 日志文件 + +- **特殊农场日志**: `special_farm.log` +- **服务器日志**: 控制台输出 +- **MongoDB日志**: MongoDB服务器日志 + +## 维护建议 + +### 定期检查 + +1. **每周检查**: + - 查看 `special_farm.log` 日志 + - 确认维护任务正常执行 + - 检查特殊农场作物状态 + +2. **每月检查**: + - 运行性能监控工具 + - 清理过期日志文件 + - 检查MongoDB数据库状态 + +### 备份建议 + +- 定期备份MongoDB数据库 +- 保存特殊农场配置文件 +- 记录重要的配置变更 + +## 技术支持 + +如果遇到问题,请提供以下信息: + +1. 错误日志内容 +2. 服务器运行环境 +3. MongoDB版本和配置 +4. 问题复现步骤 + +--- + +**注意**: 此优化版本向后兼容,不会影响现有的游戏数据和功能。 \ No newline at end of file diff --git a/assets/游戏UI/大喇叭.webp b/assets/游戏UI/大喇叭.webp new file mode 100644 index 0000000000000000000000000000000000000000..0560619f35027477bc75d3a57154915f27fdc559 GIT binary patch literal 11974 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 literal 0 HcmV?d00001 diff --git a/assets/游戏UI/大喇叭.webp.import b/assets/游戏UI/大喇叭.webp.import new file mode 100644 index 0000000..ec7e0c3 --- /dev/null +++ b/assets/游戏UI/大喇叭.webp.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dsds2gsvqxfak" +path="res://.godot/imported/大喇叭.webp-02c8c72b3970655e1b81cbf9fcfdcd24.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/游戏UI/大喇叭.webp" +dest_files=["res://.godot/imported/大喇叭.webp-02c8c72b3970655e1b81cbf9fcfdcd24.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.01 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/assets/菜单UI/黄皮大耗子.webp b/assets/菜单UI/黄皮大耗子.webp new file mode 100644 index 0000000000000000000000000000000000000000..c6e086d10578527bc32f31f52e1ca3746fab11ce GIT binary patch literal 29374 zcmV)dK&QV_Nk&GtasU8VMM6+kP&il$0000G000300093006|PpNL4fd01e37gt0~0RDgf{~x|00Lb@l&UudzExlJ0Ke)KfhYv@__h{;`{MiZz ze|JCPJ-TG)vy6JMpZH$QF%t=7!h3TeAeiQRGyp=tOBC|75`b$a5b{Z)CYr$|GqY1j z)T_{Je@E)o}5ZWf4AuN$L$*BgEKulJQzPYgn;Y+nJawzpPfoB z|MXA#@a1oHX>DBo(VxwE^B;_jPYS5PRlDa{-B_AYB!`NF(bJKVd6y9d4&L~LL}qq0AC`H#!+X2HB@y1SA^C=OA0oc z;eOrQ&d;jFF7dHhx_7tY?rtd%$?~FP)3)(+5TPgtBe({lWmU=e;7&`skYmFwy!~t4 z`~TAmS^UAn&K?`W;op1UKk{>>Pq8Sk{IJW9fA1*qRG2Lw)>8cLPdtT#|M724@XVIm zG<4(pBlkDI&>R2B_k;0q(Z9_T_@7ZY}0ysv%bS(t_!l$AgeC@Tv zs`9dFv=puK=ZaK!!Hj%7FpIDZ&Qr3y-6h8>*g&;0p4yZf*S<2RmOgKYP+yLNc5wf6ALX+b;uy_)u4 z{*-UO@lG=Ev8jm+pWP0R?uwlZvQy#GR?@Bw-R+ZIw;G`=;Gg}d#{E&7M)j^cC6Maw z!&`6O{CLlrWHd`@b?QVVGO7129^LOt!5kZ|>cl0d$t*^r$Wo$Cg)l-y1Q6%Fc9XUa zIaE#yvvB;}?kihm(Ju-eAB-bcrM~JHF&?!PWjKBV&RyCxalNYIVHXTA_+-nuqSq_Y zbOA1N0wJ2V3FDi!bQ z+dMi*;A%v&_gAH!w27d^FbV-9mQ^PXiOJRo8A{J+W;7$5 z!@{CYGN1C~eePjhBFNBoA+d8RSha`>6hbrOx&Y+KT^BLjtGuQi3nAE2qE)YCJ84sF zLkBq5Pkt%a-!5f5nnbRTz!M-DyAavae2ILpTg2m&1Qh{cAS||R5T~Wbszr03dCA1; zy0ta3(LZ~a_~0*9e(STA-Fah@mh(Hm@YML7MgHU} zEvDsj@Z%ubjOi}S1d285QzAJ z^O9n+#@d86CSmC)_nFJHtdu;pU7NUwC>0=?tan5qM6kHai`%;DL%_GCt_$244@@~>jZ%D@Q&%#p?BwhK63m=$6+yK z!31FH?mnFhKrE@lAG$<&XJS-a8^8C{Wjwf3cl$S`7oX0ttk4fn5{UUW`K?!zKD+I) zQq9hNq>Mu!>Ts_kKoTPBmiGVQqn%&A=(6pPJ*~~_Z*=4WfR6w`+_`hsV|mf-pDgtL zNf#AsqyE}+eLfsD&8xpZ#?@`V6eJ%UC1RRWtnb88US+xh0W|-}C#!rEJRVFE0Whgz z-Q#4xE{B6W%kxr`I}8b2BxFJWCYE$55pfx}tc@udbX|}YxkHz_=D0qR(#sMeA`a0A zkO>CBENfH*%ZjKFkZh>V8PRo#1PD{%x>>R`iACmo=6xQjm|{wtLyBNJx5#BgbMFIE zB8^6s^$-Z0lC)%r5G*M!bFPV)vbJkYn3&~#Cs$?w*R`X`{f?jz(-iA+-2kM?cmKfrvtO!Q^}_Q-`h)Lw#wiHZ9>;T^+d15C zjer1rd@@BqI0bQO1ulR7`C|O2HGnw&ay~9Y;SxO}TAL2#T#-QnfMj>WAcJ8q zFF#c5`1=o&`ZMLp_-0FhXf<0(va8nvh-nc#p8VP`Wzy7uD>%7(uM73Ts4RAh=A&a ze0X`cJ9_gZ0sx7X64n+f8#A4bw;YyE&5LgxzHi4b)QY;*{BTC<6r4*(V^&n9s%TzE^L^KP>MSs^z*CoTs&1lTc$-0R; zg_#+qUBogJCPu}QqR2Rnx#|*E<(w0(HBKj*V)w*@Dk}<@nC?uJB7)iCoO7O|e(@+* zmk=5(&VRf~6^@lw|}2v7o>uVzuJ>=eDf5--OZiV>wDi-n*EK z306vWZ{D8_E^Rq1D#iBs9A}>Lqg%H+!{fB{==YlPsU5#|+W)!B8vn_i89W&ZWidi% zDp*$gW5eP?tSY9lZgfa)QNBrfr$E$xEN(+{`^4OHni;n3(1RQ zxf3iXA`n1G5I|^}m=}H#f#zq59w%*LNJc!CI7!Jn#kvbT3RrSTW{E{RkE!b#1K_g0 zdMqfKI~6p6T!1i@RXOpnm|~0$Ff6I+oOdDsjerm&qDA8z=Tv=N8V#KJcv6C56Rov2 zALQqJnvA;;ohl#ztULbRO;Jczet4)06y9F^pjh;f)c_D$*CiG%6D-vmZV!4+ zGV8iv!rZ%D{F}FGn|OkJA!!pv`JrN1IY1jd%XX@IPbEreXDNY-K$tSOts&7 zc^D0_{!T87X!h~X4kq^-TZl+b@EE(}?~b>I7ji7^dcX7mO|U4L6QLN{rh}U=wsX`! z_2;Va&T%Bxl69%rL>s^QQt`<@e>K}bx?Z;rT|f1`0Azb>KvJl=S1c;opsHdUxd;V9 ztX$2~JAYhv7Y1$-0R$rbXM|lOMKdozjFBPiOCV)R{KZ~|CfHg~q-?kuq3Ns~^t{w1 zZG1m$4;%n6xsDJ*u((h{PY4oo%}6QJ>Hx-Z+iC7(o~k-00;o%Ym=+PxrU{C8I2AG1 zmMIyz7DCCp&7hfuvC{YU&JNMOINsf}8R8O!SRtcV5c3(ECglV1D(5AgN%b z1O$s(xcLWi=DBURBCu1>E_b$wX#W@A?*(Fq4;mxp(eGcaleUY-DTZuCP{YFP-n@Ghc1v9mX>e>R%$Mojv*J^P)otV= zh=;$8rng&G{f{{n*3e0p)(6&RE5V}QR9jzOHiW;R-bCNnq~RCZ0m!i z9o;;RAOK=MLtQt%EVF7b$PxB~2?2swTZ#{mO#uiS|H^oL_7`@X%n}qWxSFMzR2~D< zqiufeh{c}G#koG}s#FEy&`W5M4$g|#${;)Rhj1!4gpYef}&=N3I`ZY@_okn`a5BzU4!CZjgzHc;Cjwpb(H;L=w~_@%M=g{M#SPuV(C=(9+%5M^MdPsupdaQ z(9+w#8PAjlrB7%JH z;+CDXiC87$!$m)W8nM=dflRrqjX(jCt7A%*LR)0&oM)FSv9KvtsOp4M zU*=e$M5zSo27-L!)KyvbC5wgAQ z+)+&yBV;Mbx$Q&%lHe4S61q0bsCuP_Q4B^BD<$>Zm9I|vzU*(i?s$~2*48y`jCW65 znO#^LP07fy8Qw^yuWx2WN}C|5G2Ale>*^ty2BX*K^!a`ild%F6!R*3xoFlKRF>Ehjrrz zO%bp4TwMtl6AG8n;SXPM4gzzfM`Fep!H2iL-z9*4`=eRj9k+>CB>|*#;EsLnv#pP} zGHN2%hajx}*7y)-OBA`2b%D=^RqDnKVC2e}QnD}^z%i@bCq07Vsq5M+`N zTGAjHE5>MCj%G&E7|fUAyst!3G7@V^OJ+ussEv51SnCuGUi&?ZA=t_Q!oq{#^X*~f zLWs72a48Wd3IGBC0tyMuo{D*xgovQEktCL-wQZMDdxDlbS)Mr;EgDIzB~x2ZLNgJT zgY8TNYt4A)b*rv@aVI~1^CWR~0Kx9T)U#g<#CK=Y6NWQkihH2HTM z!%Dm3uRZkL{ebOrWmcYjwjJFc8L^&3JoxghrbF`m3#DSs86W=kQSUGQ+h^0suilT3 zCylN@^dJ2<&ZS$wUE3U~8e0cI@~ulne)Wpkt)oD$fVAs>VoiQ=+gG{E^N)Yh-MBvC zS`em;*I&u~pi(V!Cu^7LrYoN=dp?9ZSu!rCssb}hAtc=^dzgfl07OK91g?j3&S!b< zLK}@(mNYVk(7}19A|iRuO-_1=c)TTNRo>e!Ayx0$<29@$C6^~Qi>sU^k$a|Y%tk; zr|tUB{p*+Q^{=^S&d zd-?UPt7ms|eH=`qckdZ*kE|x-*&V-0NbM_Mi`ftT&CA8sA3jLn5-$7XxqN)%odyKc z$sgVG=lTv1Eq?Tad2@f9HjkYA&UjK^+VYD${C>0D`$X;@17Y|7aTn#Of!k&pRGDrR zySfhf6_!<}Z8Flpd4fF4jjbhuOl{jp<^&L_wpBtGZIcLsWNI-o$(+n%>_RfzE@WjS zElui9B=b&0Rj}DKGeee~uXGD3MvIoxEWk*Jlo?XAWQhQvPMrs%k=!6lDJ7(oz`hqO zOrx1)PGl9Nn1v8h-<$}sLJ?I3z+|>A0Lb=^v)Pj2 zYrpaJZul4f;xp)95FY%42LZEF_}W)f`LFzYgY?pu8{-W4vtK$}-<~95vrhi;%?Ro5 zoF8~uim=)j$w8JPn!8)&ORuzcap+ef;`$$YPW(=L_v;VBdL7@0)kk)Eh3^-U_YM+g zu^rtDyO*z)sU3I5L;~W^T<+>N+NRZy>PCH0=1#CYlSxtvR7Dm7vDX~70QM8k2-MDJ4)?_-re)w(AlgPSqzPH_aGG!HOA<&PBFOKpv?x_#bvvpLLg5u_P6mzQR8*Z4nwiaEtn047wN?0z;{-qn5F0P* z37T;+5fu?gN=?&78mgjlSX82oq7+V6qS;e!oZ7YvMnbUumf8@qtZ=>>D!aBxoCQl{ z-FYu1y#fN^Y2XUqW7l?xi)DG9Lj-j_Nt|}-WfAzojAph9K{l8mwQXxI%RDwQ-1t7a zPhKuu|7QnT`}>cA%>ihWueMF)KTrt($%4t|$?(T_^4ouM6c=;VGtZTpbok(R#tA?= z;2FL%fIo?VxqmD-4(iY|KZDH614j8yobm<^%wq3*}nXhHmw4Lz}oM; zb?na+vJt6m0*ALo@i2H z8Gr82Z@2I6hh$Bu8wBfa{bqgUBcI59{@l;v==X2Mg{CDt|D{3m#z8nGpdjaI?`N;s z{$Ur!-+1fr+>Xao`{)Oj&U~ntCnBT?miOl_Wp30)q{D584Q6cWm<=`4tm^r$ZUVT3 z`ll}zJZY2B08z+BiD}V-^QtY6Hlz@`laza%kwP>(xlqU)2t^b^(iRfuyiX~{ggiku zMOC2?B1jZfQISX^xsdqGizQ1T#^AiqHwyw&VxALcMkYgoU^Ypkql3%=#7m_JZL&om z5Fo^0It&gQs;;VX`lqYO(W8z4#P?`SF>wK*ihyKlAH195Q`bt}EWi}P83N5Zf&d7? zq67fJe3AU=y>tGRo9#jZAyRmFJyd%;enSMs%nc zNf*MS(1vKUn)Ja>fAj8Q(6S)`Hlv1aE6X=cvp`M>AU)KUNNTTLjMkK@TtJFp!Pqn% zI1dUJY3+d}pE+4OQB~(Mk%OI#AsMFuKr|d*bQ5B(h^iRVlpz_M=!S{7L<+=-Iwq#j zg@|mca|^+`T(PLAh$0ybv(<^Z$QWa*D%e2DtEw10HF)swPHQw0v&sIk+}s|zIih)% zI~%nGpf1uTeKpxEb;$tHaxg?{PwFs!>7XiicKt#GAPa$n3e_-MTz^}wxZPMB4g5y9 z{39>uAdUJrj{=0my!5d5@+)UbnU@bge71LgKTJs;5|b05Y!dvLM@h0+^nda>wFeK{ zbn?6RPRetYE(8cE3xOg!JYK->{l&rlor93NhvytNQOXaV89X{P>w1|N+DQ|;t{ulLb23W+A!wpph%l2f zat{23v%|qjn^I@lW!;ZF@vZk=lx2rZ}fg3FA1A3o*7KpvsCl;?(CUnUNH1B-?vUlXlcsWuZ#QELezz zVnKly;WPlvceAqn#@|lisW0{1N>cOiLCmWntE$4u$|T<_OBs!h+RRjF7E|mw%+_MC z*E0~UyVWjn_p5ETbtanw5dlcTY2-#&^R=%=)q!34{EipQCy%+c5CmBOF;nwTe7@TM z-@bmk9A4WJSp|>($<1VLpqIXI#$)B0f8=NK?pMC+ZGTUJFu|R@fz0vMkAB7r!0_%- zT!f7GzCCukr7qx@00*4VSWsvxAV5*WrgGZa+ugB$D$DXB6QQ`cQ>Cs8iL)R+ zk}?BgZLg3aaBR_50q3}b&@^-)xH3Q>k~h}fet6uq2gm8k&`oJp6uxOwB0wQAlSq@> z_ngLyITjWnF>Tb1nWi25qq~}4^8CPuDh0$n_ahA-y>bw2j`~2%rOU$BPfKp@W<#z* zwmw%5MlT+r!YJYKYHt#|H-9_2YVZ7Z>19Usp%22|niw&k022;>?O~H&?7M|TvS0v+ zE>^NhNR#6O-+nI+&wi#*DDvHpUiSLUS34s(1?2()kLnZ8NqR7f#426yGnc#WwHyAa zyA$K%fq-E=X~gB36U@}=g)`;;ac7uKCKw@z$8{=mCyNNQXh;As>T<$$vSb!ROx%v% zsXAX2g;zakEyXm0f(3;x&kG;BWUJAlCfS{R$eS|a^_PwaO@`{z==27C5=^}uL6XEnqL{)sT z^S=A{#*uien)mzt$?-UF5rK$m6opuU@Ea8eQOQB7+ zKGU@D(jV68+@Ig|A|l0SeoT-5+3jcmMAKpb1Rz!~QvKZdgNKbhwjX;TZ+`U$!4{!F zD8ziG+rKlg4qN3;7f;#`t>LWrLE ziNfCcUYFL*LbN0r3iX;fCxCW|m=PG1 zh-hT&La?593P7ETQUH?u7~3RVFA$_1e``mpvs;-$gh?@z5UjZX#A6pg6*Phiuo<`X zy~@iB_?`Z^jwx|5&9XfA#wiJy!`JuIlw20j9$nM;?0_Hjp;@{g;@3?)lsS zV#z}Yf=XGlbrtc$5hKBQsrmAaHvicx*(}kmUwCxuVALcoBCh}J)xdil3C6eoc#MnN z*_1!?%V+6=iM;*KeJ{H1k#nrRah-!J7rn?7 zTy^ayx3B-s!?2iM__<8)j}rkz9#5q3DiF9!S2RnR@Z`o<4`P+cMldCkIXnQ7EP^nA zkW)(YVVa1BX!BwS$$4?As#CrkoZr3ic3KF&?EBb8PQkZH)H`)l*LY1JECjZyf(;cD z0VsqPnkS1XCP0O!lixZkZ0|MmKx67`#JnuM0#N6CJgJsYLz#jSfVfm4>_I~z)efLi$X&yk1f8(TeKYlG201$zQ z85R{}zJ@c*xVW3;vZZ$MDHq@QU%zv3oJe`V!1X2uz*V34d>()BYjq5n@^pgh zT{uI$d1PDwHh$+F^v}J&&?x{#|B&DUkWy<3Ipxp&xjn_m^xo~Ze)rez>!4r2m6>i3 zC{P0c&8zLQZ)ue6)g!R8s1n5|yanOLk(MwaJ2 zFW7vcMXytUhz#mZT;iz(Kn6RQu1y;+zvp zDa}!zWom8|I7^)G592+FrF+ZGg+2vkd9vp`@8t0IS6gZtFgh6BuNlR_hO73(sU>aS)fyEdT0BD&wjrk%1 zl9W?SmEOZsMa&yJQyNKPwzZ)%$?{^#IaM@5n-{v6_G$<=)tZ-6xcbF@d3b#k2{3fI zcf^xcqvryovl#$!{jF_@H=22=dGmU)^Gv=$^`Cq#d-{L* zA!+~d?MQ;*rH_`BCk|Zx{AV*CP1<{-WJY2>fzg*wM%kH4r?N|bZfJM+J8~AsFW#%q zZFy`?Ti0oK=TaWheoCNqq^hbsX{6{4iYyq~E*e41B#mH-?l@(c3LxSy>}jaGL;xVg zLQ^Amx;eCDjF~U;G>j>P7&)>$%bh%FR8u-g@r_N+gnrUk%jZ|f!m>WiS?l(;*?n46wAX-(GIgCH}!(%CO zx4V@I0A#yA^Hh6lKM=Fy{eN=Q?|*3MHVtWX?@n*;&8g!uLF5^ZKkQA}O#oaz&W(jkLl zzFydQwM`m;i}BB#tOB~20P1e;Gd(?y>q$B&6ab- z53bxiG-9rZse%LmrpctGZkK}7kxUQ!Lv=I?k(zIQJBp6%+%G-to+PgN)6e0Z!?=JJ zE`){9kBtyBF{AWsObc6L(}(mZIcNm8;o zP@jyL$#nSkBn%5ZH4tm2eBcHeVu;DiNT%qRQA9)=YC=fB2R#&#Z^1cOkU~IEXpECqrN`{Lb72>??{iAGqj77hy^M5udk_vzB#<8z*jX@r-+TV{Dj7YYOM9Pqf?JE60hL zCJ3bX+M}MO9kIQg%E=Q2qD5;92PD4au?Bb4O8O@rGRP|{>XBMDeM>{mYTZL{u95;zZ*>hU;o zWYy8d%U%`oF`1dQD4cU}Fjykz!ICiJcOjR8`27LLm{HTzrUe7B%Z-xu}W&pe0Lf*CnnQ)5Bjm@K^q; z=Y8+N@%XhzlLQI`B0Im(j$S^l6Bn78Cg1v2V^=N}&z{Znl(X=l&Ev}tRo(d<0Mze% z_+fmvnJk^^znZED-Yvx z*45)?^4)vAANxolr`{WHKkHv+lkZgyfJpXCJ?Y$qUkUmdru2Vc9lcy%)J= zFiWI}D~h7bss{}_iV@JPEXxab?nEq2OJ;2wTt3W1PPuM$WiZ}95bsq))%o1M=5B~-&fDd(y>OimI2s8iJ9Y`JMO1D&YgbZ6ks{p7s}0H2p- z`PA$6_Hh&Ei8uj3OCiJzZJT0U+D&d~cLp)A3&4V>42hl4_A>K0`7R(-bHS zD;0Gv^TmE3PIp=i5D|zvQHQEcT}n(zR8{7g!MYQRnyGq~%>c>NxYlL4cO(ITW@N4= ztI7LkcCWs15>GqOgaW|jWzRQtSC6|U88xfQT<4@1I%C0HL;9gT{! zm2MhRGpQ{vda9}dK-jOc{=*aFQX)=0HIgJwdjUA*dR}AA*o`efokPzu0bt1xyVxMh zR5s2mA)u?Z%nK&~+}1Px=;fkxvp@L5)M(fC9F1=9>BzsP)< zj=%S*UZT-bFjFUjQtea=|G}j{fPR;_3@qnE+Bw00IaEBsWL1ERzs|5de+Gkb4EhIhP41kqd#4Cj=;Ym_a5XAWl^zA*RR_U2+15 zbE=LiOfecarhn}MCT|>tDFBS4p@RSr(VmWzEdn?ro{++(kSPEJpvADCciF8r)W@}x zv&B@_-|0bpDaW_Q9X3>C@WnIP&sr z4Ds;qKgyoj^&&v_zAyGgU}^Ht-buD0&|Xm@#t9(;5DZ4l7V*7F;39yDJQ*lN*)Af` zZ0~%*ahGEI;9=VCmm(r=Z>w|`+YrV_k(&Z@bymo>^IpZwBKI|rQxKnb$rfVFC{F-J zOLSFKnI-C+xZ`akGlNTodgoMx*dJJmDHi|kA1Z@belpu`R(iO!hid* zve{43_q0uK_M|Z^Z1O~~$*;Zz_jF(9X#T!mEW=yJamw{S|BQGjyAHg6(#3~= zZy&vL=~qq z0hn0|p^a7%P6ZpybnulG>x5trvq zJB$-J#qQ)$lgBHSii(_GLTHkO7*6gTiGvn{OSu39NjrKwg{`62-09{SlW__lV+slt zmwBfust`3Mk^qw>>+TO+mS>qey-0Nli=~vJ37p#XV3dJn5Jw1`G7mi0II4YSsmlaKYcf) zJjd={E~k=(y6G$#P@T<10Jrrc7Y^Qjvm>X3Y}-0z*lcX<&f zc6gGwOnZ2J;xed{+<_1pBAnaWH`ZK$amTQj7pW$+M4r zzEpW^O2fjMJ@pg4lb4U$$xFXh$rGE;ycVVEgU5sDcJ5db0xCz(^35``$7s_HUA z&xHBTiMW)pj!g>vu~WellO#+40tD3e&zv9MJ84N!K+I)#@@|(gNIyT92?3B;oB%PO z;rjOn)I6E?6t2vCuV1=lAW4*S#P#=z@xj4iH=cydF6MaB5_2`b@}(lZc@&Ao$?m&< zR7Z&VOMQpO&v^9Zw=D&y76_>GSAKp^WGR&d&VoPx=Lg5%{O?{n2uZW22JVTopyt6Bp5CCEEAWd5F$a5#_keX%^NQebPsypvK+E>V8f((Je8KOJq2Ici%zv+v