大更新,太多了,具体进游戏查看详细更新内容
反正很多
106
Components/HTTPTextureRect.gd
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
extends TextureRect
|
||||||
|
class_name HTTPTextureRect
|
||||||
|
|
||||||
|
signal loading_started
|
||||||
|
signal loading_finished(success: bool)
|
||||||
|
|
||||||
|
# HTTP请求节点
|
||||||
|
var http_request: HTTPRequest
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
# 创建HTTP请求节点
|
||||||
|
http_request = HTTPRequest.new()
|
||||||
|
add_child(http_request)
|
||||||
|
|
||||||
|
# 连接信号
|
||||||
|
http_request.request_completed.connect(_on_request_completed)
|
||||||
|
|
||||||
|
# 从URL加载图像
|
||||||
|
func load_from_url(url: String, custom_headers: Array = []) -> void:
|
||||||
|
if url.is_empty():
|
||||||
|
push_error("HTTPTextureRect: URL不能为空")
|
||||||
|
loading_finished.emit(false)
|
||||||
|
return
|
||||||
|
|
||||||
|
loading_started.emit()
|
||||||
|
|
||||||
|
# 发起HTTP请求
|
||||||
|
var error = http_request.request(url, custom_headers)
|
||||||
|
if error != OK:
|
||||||
|
push_error("HTTPTextureRect: 发起HTTP请求失败,错误码: " + str(error))
|
||||||
|
loading_finished.emit(false)
|
||||||
|
|
||||||
|
# HTTP请求完成的回调函数
|
||||||
|
func _on_request_completed(result, response_code, headers, body):
|
||||||
|
if result != HTTPRequest.RESULT_SUCCESS:
|
||||||
|
push_error("HTTPTextureRect: HTTP请求失败,错误码: " + str(result))
|
||||||
|
loading_finished.emit(false)
|
||||||
|
return
|
||||||
|
|
||||||
|
if response_code != 200:
|
||||||
|
push_error("HTTPTextureRect: HTTP请求返回非200状态码: " + str(response_code))
|
||||||
|
loading_finished.emit(false)
|
||||||
|
return
|
||||||
|
|
||||||
|
# 检查内容类型
|
||||||
|
var content_type = ""
|
||||||
|
for header in headers:
|
||||||
|
if header.to_lower().begins_with("content-type:"):
|
||||||
|
content_type = header.substr(13).strip_edges().to_lower()
|
||||||
|
print("HTTPTextureRect: 内容类型: ", content_type)
|
||||||
|
break
|
||||||
|
|
||||||
|
# 创建图像
|
||||||
|
var image = Image.new()
|
||||||
|
var error = ERR_INVALID_DATA
|
||||||
|
|
||||||
|
# 根据内容类型选择加载方法
|
||||||
|
if content_type.begins_with("image/jpeg") or content_type.begins_with("image/jpg"):
|
||||||
|
error = image.load_jpg_from_buffer(body)
|
||||||
|
elif content_type.begins_with("image/png"):
|
||||||
|
error = image.load_png_from_buffer(body)
|
||||||
|
elif content_type.begins_with("image/webp"):
|
||||||
|
error = image.load_webp_from_buffer(body)
|
||||||
|
elif content_type.begins_with("image/bmp"):
|
||||||
|
error = image.load_bmp_from_buffer(body)
|
||||||
|
else:
|
||||||
|
# 未知内容类型,尝试常见格式
|
||||||
|
error = image.load_jpg_from_buffer(body)
|
||||||
|
if error != OK:
|
||||||
|
error = image.load_png_from_buffer(body)
|
||||||
|
if error != OK:
|
||||||
|
error = image.load_webp_from_buffer(body)
|
||||||
|
if error != OK:
|
||||||
|
error = image.load_bmp_from_buffer(body)
|
||||||
|
|
||||||
|
# 检查加载结果
|
||||||
|
if error != OK:
|
||||||
|
push_error("HTTPTextureRect: 无法加载图像,错误码: " + str(error))
|
||||||
|
loading_finished.emit(false)
|
||||||
|
return
|
||||||
|
|
||||||
|
# 创建纹理并应用
|
||||||
|
var texture = ImageTexture.create_from_image(image)
|
||||||
|
self.texture = texture
|
||||||
|
print("HTTPTextureRect: 图像加载成功,尺寸: ", image.get_width(), "x", image.get_height())
|
||||||
|
loading_finished.emit(true)
|
||||||
|
|
||||||
|
# 加载QQ头像的便捷方法
|
||||||
|
func load_qq_avatar(qq_number: String) -> void:
|
||||||
|
if not qq_number.is_valid_int():
|
||||||
|
push_error("HTTPTextureRect: QQ号必须为纯数字")
|
||||||
|
loading_finished.emit(false)
|
||||||
|
return
|
||||||
|
|
||||||
|
# 使用QQ头像API
|
||||||
|
#var url = "https://q.qlogo.cn/headimg_dl?dst_uin=" + qq_number + "&spec=640&img_type=png"
|
||||||
|
var url = "http://q1.qlogo.cn/g?b=qq&nk="+qq_number+"&s=100"
|
||||||
|
|
||||||
|
# 添加浏览器模拟头
|
||||||
|
var headers = [
|
||||||
|
"User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
|
||||||
|
"Accept: image/png,image/jpeg,image/webp,image/*,*/*;q=0.8"
|
||||||
|
]
|
||||||
|
|
||||||
|
# 加载图像
|
||||||
|
load_from_url(url, headers)
|
||||||
1
Components/HTTPTextureRect.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://0d2j5m6j2ema
|
||||||
1
Components/ToastShow.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://caly13tf4ni1d
|
||||||
27
CopyItems/black_blue_crop.tscn
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
[gd_scene load_steps=3 format=3 uid="uid://cm1e72lhd7j7v"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://c6ylh1o2kgqth" path="res://CopyItems/item_crop.gd" id="1_sgirt"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://kdhowrc6av4g" path="res://assets/作物/默认/0.png" id="2_sgirt"]
|
||||||
|
|
||||||
|
[node name="BlackBlueCrop" type="Button"]
|
||||||
|
custom_minimum_size = Vector2(400, 400)
|
||||||
|
offset_right = 400.0
|
||||||
|
offset_bottom = 400.0
|
||||||
|
scale = Vector2(0.3, 0.3)
|
||||||
|
theme_override_font_sizes/font_size = 1
|
||||||
|
icon_alignment = 1
|
||||||
|
script = ExtResource("1_sgirt")
|
||||||
|
|
||||||
|
[node name="CropImage" type="Sprite2D" parent="."]
|
||||||
|
position = Vector2(199.569, 201.043)
|
||||||
|
scale = Vector2(0.260977, 0.259058)
|
||||||
|
texture = ExtResource("2_sgirt")
|
||||||
|
|
||||||
|
[node name="Title" type="Label" parent="."]
|
||||||
|
modulate = Color(0, 0.152941, 0.984314, 1)
|
||||||
|
layout_mode = 0
|
||||||
|
offset_right = 400.0
|
||||||
|
offset_bottom = 55.0
|
||||||
|
theme_override_font_sizes/font_size = 50
|
||||||
|
text = "普通"
|
||||||
|
horizontal_alignment = 1
|
||||||
42
CopyItems/crop_item.tscn
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
[gd_scene load_steps=4 format=3 uid="uid://bkivlkirrx6u8"]
|
||||||
|
|
||||||
|
[ext_resource type="Texture2D" uid="uid://c4l0qn0p4yav8" path="res://assets/tu3.png" id="1_bns1c"]
|
||||||
|
[ext_resource type="Script" uid="uid://xh5tr5co5kfu" path="res://GUI/SMY_ProgressBar.gd" id="2_1n4xp"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://kdhowrc6av4g" path="res://assets/作物/默认/0.png" id="2_bns1c"]
|
||||||
|
|
||||||
|
[node name="CropItem" type="Button"]
|
||||||
|
self_modulate = Color(1, 1, 1, 0.435294)
|
||||||
|
custom_minimum_size = Vector2(100, 100)
|
||||||
|
offset_right = 40.0
|
||||||
|
offset_bottom = 40.0
|
||||||
|
|
||||||
|
[node name="ground_sprite" type="Sprite2D" parent="."]
|
||||||
|
position = Vector2(50, 63)
|
||||||
|
scale = Vector2(0.130329, 0.130329)
|
||||||
|
texture = ExtResource("1_bns1c")
|
||||||
|
|
||||||
|
[node name="crop_sprite" type="Sprite2D" parent="."]
|
||||||
|
visible = false
|
||||||
|
position = Vector2(50, 36)
|
||||||
|
scale = Vector2(0.0660772, 0.0660772)
|
||||||
|
texture = ExtResource("2_bns1c")
|
||||||
|
|
||||||
|
[node name="ProgressBar" type="ProgressBar" parent="."]
|
||||||
|
layout_mode = 2
|
||||||
|
offset_top = 86.0
|
||||||
|
offset_right = 495.0
|
||||||
|
offset_bottom = 159.0
|
||||||
|
scale = Vector2(0.2, 0.2)
|
||||||
|
theme_override_font_sizes/font_size = 50
|
||||||
|
script = ExtResource("2_1n4xp")
|
||||||
|
|
||||||
|
[node name="Label" type="Label" parent="."]
|
||||||
|
layout_mode = 2
|
||||||
|
offset_right = 250.0
|
||||||
|
offset_bottom = 42.0
|
||||||
|
scale = Vector2(0.4, 0.4)
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "[普通-胡萝卜]"
|
||||||
|
horizontal_alignment = 1
|
||||||
|
vertical_alignment = 1
|
||||||
25
CopyItems/green_crop.tscn
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
[gd_scene load_steps=2 format=3 uid="uid://2m54c0f1ejir"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://c6ylh1o2kgqth" path="res://CopyItems/item_crop.gd" id="1_ihcyw"]
|
||||||
|
|
||||||
|
[node name="GreenCrop" type="Button"]
|
||||||
|
custom_minimum_size = Vector2(400, 400)
|
||||||
|
offset_right = 400.0
|
||||||
|
offset_bottom = 400.0
|
||||||
|
scale = Vector2(0.3, 0.3)
|
||||||
|
theme_override_font_sizes/font_size = 1
|
||||||
|
icon_alignment = 1
|
||||||
|
script = ExtResource("1_ihcyw")
|
||||||
|
|
||||||
|
[node name="CropImage" type="Sprite2D" parent="."]
|
||||||
|
position = Vector2(199.569, 201.043)
|
||||||
|
scale = Vector2(0.260977, 0.259058)
|
||||||
|
|
||||||
|
[node name="Title" type="Label" parent="."]
|
||||||
|
modulate = Color(0.243137, 0.729412, 0, 1)
|
||||||
|
layout_mode = 0
|
||||||
|
offset_right = 400.0
|
||||||
|
offset_bottom = 55.0
|
||||||
|
theme_override_font_sizes/font_size = 50
|
||||||
|
text = "普通"
|
||||||
|
horizontal_alignment = 1
|
||||||
9
CopyItems/item_crop.gd
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
extends Button
|
||||||
|
|
||||||
|
@onready var title :Label = $Title
|
||||||
|
@onready var crop_image: Sprite2D = $CropImage
|
||||||
|
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
title.text = self.text
|
||||||
|
pass
|
||||||
1
CopyItems/item_crop.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://c6ylh1o2kgqth
|
||||||
25
CopyItems/orange_crop.tscn
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
[gd_scene load_steps=2 format=3 uid="uid://forqk66f354p"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://c6ylh1o2kgqth" path="res://CopyItems/item_crop.gd" id="1_00rx4"]
|
||||||
|
|
||||||
|
[node name="OrangeCrop" type="Button"]
|
||||||
|
custom_minimum_size = Vector2(400, 400)
|
||||||
|
offset_right = 400.0
|
||||||
|
offset_bottom = 400.0
|
||||||
|
scale = Vector2(0.3, 0.3)
|
||||||
|
theme_override_font_sizes/font_size = 1
|
||||||
|
icon_alignment = 1
|
||||||
|
script = ExtResource("1_00rx4")
|
||||||
|
|
||||||
|
[node name="CropImage" type="Sprite2D" parent="."]
|
||||||
|
position = Vector2(200, 200)
|
||||||
|
scale = Vector2(0.308178, 0.308356)
|
||||||
|
|
||||||
|
[node name="Title" type="Label" parent="."]
|
||||||
|
modulate = Color(0.822776, 0.578065, 0, 1)
|
||||||
|
layout_mode = 0
|
||||||
|
offset_right = 400.0
|
||||||
|
offset_bottom = 55.0
|
||||||
|
theme_override_font_sizes/font_size = 50
|
||||||
|
text = "普通"
|
||||||
|
horizontal_alignment = 1
|
||||||
25
CopyItems/pink_crop.tscn
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
[gd_scene load_steps=2 format=3 uid="uid://cmdoymcviv0ai"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://c6ylh1o2kgqth" path="res://CopyItems/item_crop.gd" id="1_3xhou"]
|
||||||
|
|
||||||
|
[node name="PinkCrop" type="Button"]
|
||||||
|
custom_minimum_size = Vector2(400, 400)
|
||||||
|
offset_right = 400.0
|
||||||
|
offset_bottom = 400.0
|
||||||
|
scale = Vector2(0.3, 0.3)
|
||||||
|
theme_override_font_sizes/font_size = 1
|
||||||
|
icon_alignment = 1
|
||||||
|
script = ExtResource("1_3xhou")
|
||||||
|
|
||||||
|
[node name="CropImage" type="Sprite2D" parent="."]
|
||||||
|
position = Vector2(199.569, 201.043)
|
||||||
|
scale = Vector2(0.260977, 0.259058)
|
||||||
|
|
||||||
|
[node name="Title" type="Label" parent="."]
|
||||||
|
modulate = Color(0.980392, 0, 0.552941, 1)
|
||||||
|
layout_mode = 0
|
||||||
|
offset_right = 400.0
|
||||||
|
offset_bottom = 55.0
|
||||||
|
theme_override_font_sizes/font_size = 50
|
||||||
|
text = "普通"
|
||||||
|
horizontal_alignment = 1
|
||||||
25
CopyItems/red_crop.tscn
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
[gd_scene load_steps=2 format=3 uid="uid://d3ve4qeggsdqy"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://c6ylh1o2kgqth" path="res://CopyItems/item_crop.gd" id="1_1yrv4"]
|
||||||
|
|
||||||
|
[node name="RedCrop" type="Button"]
|
||||||
|
custom_minimum_size = Vector2(400, 400)
|
||||||
|
offset_right = 400.0
|
||||||
|
offset_bottom = 400.0
|
||||||
|
scale = Vector2(0.3, 0.3)
|
||||||
|
theme_override_font_sizes/font_size = 1
|
||||||
|
icon_alignment = 1
|
||||||
|
script = ExtResource("1_1yrv4")
|
||||||
|
|
||||||
|
[node name="CropImage" type="Sprite2D" parent="."]
|
||||||
|
position = Vector2(199.569, 201.043)
|
||||||
|
scale = Vector2(0.260977, 0.259058)
|
||||||
|
|
||||||
|
[node name="Title" type="Label" parent="."]
|
||||||
|
modulate = Color(1, 0.0509804, 0.0352941, 1)
|
||||||
|
layout_mode = 0
|
||||||
|
offset_right = 400.0
|
||||||
|
offset_bottom = 55.0
|
||||||
|
theme_override_font_sizes/font_size = 50
|
||||||
|
text = "普通"
|
||||||
|
horizontal_alignment = 1
|
||||||
25
CopyItems/white_blue_crop.tscn
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
[gd_scene load_steps=2 format=3 uid="uid://dagh3u5med30i"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://c6ylh1o2kgqth" path="res://CopyItems/item_crop.gd" id="1_nfqts"]
|
||||||
|
|
||||||
|
[node name="WhiteBlueCrop" type="Button"]
|
||||||
|
custom_minimum_size = Vector2(400, 400)
|
||||||
|
offset_right = 400.0
|
||||||
|
offset_bottom = 400.0
|
||||||
|
scale = Vector2(0.3, 0.3)
|
||||||
|
theme_override_font_sizes/font_size = 1
|
||||||
|
icon_alignment = 1
|
||||||
|
script = ExtResource("1_nfqts")
|
||||||
|
|
||||||
|
[node name="CropImage" type="Sprite2D" parent="."]
|
||||||
|
position = Vector2(199.569, 201.043)
|
||||||
|
scale = Vector2(0.260977, 0.259058)
|
||||||
|
|
||||||
|
[node name="Title" type="Label" parent="."]
|
||||||
|
modulate = Color(0.111013, 0.795686, 0.959948, 1)
|
||||||
|
layout_mode = 0
|
||||||
|
offset_right = 400.0
|
||||||
|
offset_bottom = 55.0
|
||||||
|
theme_override_font_sizes/font_size = 50
|
||||||
|
text = "普通"
|
||||||
|
horizontal_alignment = 1
|
||||||
31
Data/crop_data.json
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"测试作物": {"花费": 1, "生长时间": 3, "收益": 9999, "品质": "普通", "描述": "测试作物", "耐候性": 10, "等级": 1, "经验": 999},
|
||||||
|
|
||||||
|
"小麦": {"花费": 120, "生长时间": 120, "收益": 100, "品质": "普通", "描述": "基础作物,品质较低,适合新手种植", "耐候性": 10, "等级": 1, "经验": 10},
|
||||||
|
"稻谷": {"花费": 100, "生长时间": 240, "收益": 120, "品质": "普通", "描述": "适合大规模种植的基础作物", "耐候性": 10, "等级": 1, "经验": 10},
|
||||||
|
"玉米": {"花费": 70, "生长时间": 600, "收益": 90, "品质": "普通", "描述": "营养丰富的优良作物,适合稍有经验的玩家", "耐候性": 15, "等级": 2, "经验": 15},
|
||||||
|
"土豆": {"花费": 75, "生长时间": 360, "收益": 90, "品质": "普通", "描述": "容易种植的耐寒作物", "耐候性": 12, "等级": 1, "经验": 10},
|
||||||
|
"胡萝卜": {"花费": 60, "生长时间": 480, "收益": 80, "品质": "普通", "描述": "适合新手的健康作物", "耐候性": 12, "等级": 1, "经验": 10},
|
||||||
|
|
||||||
|
"草莓": {"花费": 120, "生长时间": 960, "收益": 150, "品质": "优良", "描述": "营养丰富的果实,收益不错", "耐候性": 14, "等级": 2, "经验": 20},
|
||||||
|
"番茄": {"花费": 100, "生长时间": 720, "收益": 130, "品质": "优良", "描述": "常见作物,适合小规模种植", "耐候性": 12, "等级": 2, "经验": 15},
|
||||||
|
"大豆": {"花费": 90, "生长时间": 840, "收益": 110, "品质": "优良", "描述": "富含蛋白质的基础作物", "耐候性": 11, "等级": 2, "经验": 12},
|
||||||
|
|
||||||
|
"蓝莓": {"花费": 150, "生长时间": 1200, "收益": 200, "品质": "稀有", "描述": "较为稀有的作物,市场价值较高", "耐候性": 18, "等级": 3, "经验": 25},
|
||||||
|
"洋葱": {"花费": 85, "生长时间": 600, "收益": 105, "品质": "稀有", "描述": "烹饪常用的作物,适合中级种植", "耐候性": 10, "等级": 2, "经验": 10},
|
||||||
|
"南瓜": {"花费": 180, "生长时间": 1440, "收益": 250, "品质": "稀有", "描述": "秋季收获的高收益作物", "耐候性": 20, "等级": 4, "经验": 30},
|
||||||
|
"葡萄": {"花费": 200, "生长时间": 1200, "收益": 300, "品质": "稀有", "描述": "需要特殊管理的高收益作物", "耐候性": 15, "等级": 4, "经验": 35},
|
||||||
|
"柿子": {"花费": 160, "生长时间": 1080, "收益": 240, "品质": "稀有", "描述": "富含营养的秋季作物", "耐候性": 18, "等级": 3, "经验": 28},
|
||||||
|
"花椰菜": {"花费": 130, "生长时间": 960, "收益": 170, "品质": "稀有", "描述": "耐寒的高品质作物,适合经验丰富的玩家", "耐候性": 17, "等级": 3, "经验": 22},
|
||||||
|
"芦笋": {"花费": 200, "生长时间": 1560, "收益": 280, "品质": "稀有", "描述": "市场需求量高的稀有作物", "耐候性": 15, "等级": 4, "经验": 30},
|
||||||
|
|
||||||
|
"香草": {"花费": 250, "生长时间": 1800, "收益": 400, "品质": "史诗", "描述": "非常稀有且收益极高的作物", "耐候性": 22, "等级": 5, "经验": 40},
|
||||||
|
"西瓜": {"花费": 240, "生长时间": 2400, "收益": 420, "品质": "史诗", "描述": "夏季丰产的高价值作物", "耐候性": 21, "等级": 5, "经验": 45},
|
||||||
|
"甜菜": {"花费": 220, "生长时间": 2160, "收益": 350, "品质": "史诗", "描述": "营养丰富的根茎作物,收益较高", "耐候性": 20, "等级": 5, "经验": 38},
|
||||||
|
"甘蔗": {"花费": 260, "生长时间": 3000, "收益": 450, "品质": "史诗", "描述": "需要充足水源的高价值作物", "耐候性": 18, "等级": 5, "经验": 50},
|
||||||
|
|
||||||
|
"龙果": {"花费": 400, "生长时间": 4800, "收益": 600, "品质": "传奇", "描述": "极为稀有的热带作物,产量和价值都极高", "耐候性": 25, "等级": 6, "经验": 60},
|
||||||
|
"松露": {"花费": 500, "生长时间": 7200, "收益": 700, "品质": "传奇", "描述": "极其珍贵的地下作物,市场价格极高", "耐候性": 23, "等级": 7, "经验": 80},
|
||||||
|
"人参": {"花费": 450, "生长时间": 6600, "收益": 650, "品质": "传奇", "描述": "需要耐心等待的珍贵药材", "耐候性": 22, "等级": 6, "经验": 75},
|
||||||
|
"金橘": {"花费": 420, "生长时间": 4800, "收益": 620, "品质": "传奇", "描述": "少见的耐寒果树,市场需求量极大", "耐候性": 26, "等级": 7, "经验": 70}
|
||||||
|
}
|
||||||
48
Data/initial_player_data_template.json
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
"experience": 0,
|
||||||
|
"farm_lots": [
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": true, "is_planted": false, "max_grow_time": 3},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": true, "is_planted": false, "max_grow_time": 3},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": true, "is_planted": false, "max_grow_time": 3},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": true, "is_planted": false, "max_grow_time": 3},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": true, "is_planted": false, "max_grow_time": 3},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": true, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": true, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": true, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": true, "is_planted": false, "max_grow_time": 5}
|
||||||
|
],
|
||||||
|
"level": 1,
|
||||||
|
"money": 1000,
|
||||||
|
"user_name": "shumengya",
|
||||||
|
"user_password": "0123456789"
|
||||||
|
}
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
extends Panel
|
|
||||||
|
|
||||||
#用户登录账号,用QQ号代替
|
|
||||||
@onready var username_input = $username_input
|
|
||||||
#用户登录密码
|
|
||||||
@onready var password_input = $password_input
|
|
||||||
#登录按钮
|
|
||||||
@onready var login_button = $login_button
|
|
||||||
|
|
||||||
|
|
||||||
func _ready():
|
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
||||||
func _process(delta):
|
|
||||||
pass
|
|
||||||
|
|
||||||
func _on_login_button_pressed():
|
|
||||||
pass
|
|
||||||
1
GUI/SMY_ProgressBar.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://xh5tr5co5kfu
|
||||||
494
GUI/crop_store_panel.gd
Normal file
@@ -0,0 +1,494 @@
|
|||||||
|
extends Panel
|
||||||
|
|
||||||
|
#种子商店面板
|
||||||
|
#种子商店格子
|
||||||
|
@onready var crop_grid_container : GridContainer = $ScrollContainer/Crop_Grid
|
||||||
|
@onready var quit_button : Button = $QuitButton
|
||||||
|
|
||||||
|
#各种排序过滤按钮
|
||||||
|
@onready var sort_all_button : Button = $SortContainer/Sort_All#全部
|
||||||
|
@onready var sort_common_button : Button = $SortContainer/Sort_Common#普通
|
||||||
|
@onready var sort_superior_button : Button = $SortContainer/Sort_Superior#优良
|
||||||
|
@onready var sort_rare_button : Button = $SortContainer/Sort_Rare#稀有
|
||||||
|
@onready var sort_epic_button : Button = $SortContainer/Sort_Epic#史诗
|
||||||
|
@onready var sort_legendary_button : Button = $SortContainer/Sort_Legendary#传奇
|
||||||
|
@onready var sort_price_button : Button = $SortContainer/Sort_Price#价格
|
||||||
|
@onready var sort_growtime_button : Button = $SortContainer/Sort_GrowTime#生长时间
|
||||||
|
@onready var sort_profit_button : Button = $SortContainer/Sort_Profit#收益
|
||||||
|
@onready var sort_level_button : Button = $SortContainer/Sort_Level#等级
|
||||||
|
|
||||||
|
#预添加常用的面板
|
||||||
|
@onready var main_game = get_node("/root/main")
|
||||||
|
@onready var land_panel = get_node("/root/main/UI/LandPanel")
|
||||||
|
@onready var crop_store_panel = get_node("/root/main/UI/PlayerBagPanel")
|
||||||
|
@onready var player_ranking_panel = get_node("/root/main/UI/PlayerRankingPanel")
|
||||||
|
@onready var player_bag_panel = get_node("/root/main/UI/PlayerBagPanel")
|
||||||
|
@onready var network_manager = get_node("/root/main/UI/TCPNetworkManager")
|
||||||
|
|
||||||
|
# 作物图片缓存(复用主游戏的缓存系统)
|
||||||
|
var crop_textures_cache : Dictionary = {}
|
||||||
|
var crop_frame_counts : Dictionary = {}
|
||||||
|
|
||||||
|
# 当前过滤和排序设置
|
||||||
|
var current_filter_quality = ""
|
||||||
|
var current_sort_key = ""
|
||||||
|
var current_sort_ascending = true
|
||||||
|
|
||||||
|
# 准备函数
|
||||||
|
func _ready():
|
||||||
|
# 连接按钮信号
|
||||||
|
_connect_buttons()
|
||||||
|
# 隐藏面板(初始默认隐藏)
|
||||||
|
self.hide()
|
||||||
|
|
||||||
|
# 连接所有按钮信号
|
||||||
|
func _connect_buttons():
|
||||||
|
# 关闭按钮
|
||||||
|
quit_button.pressed.connect(self._on_quit_button_pressed)
|
||||||
|
|
||||||
|
# 过滤按钮
|
||||||
|
sort_all_button.pressed.connect(func(): _filter_by_quality(""))
|
||||||
|
sort_common_button.pressed.connect(func(): _filter_by_quality("普通"))
|
||||||
|
sort_superior_button.pressed.connect(func(): _filter_by_quality("优良"))
|
||||||
|
sort_rare_button.pressed.connect(func(): _filter_by_quality("稀有"))
|
||||||
|
sort_epic_button.pressed.connect(func(): _filter_by_quality("史诗"))
|
||||||
|
sort_legendary_button.pressed.connect(func(): _filter_by_quality("传奇"))
|
||||||
|
|
||||||
|
# 排序按钮
|
||||||
|
sort_price_button.pressed.connect(func(): _sort_by("花费"))
|
||||||
|
sort_growtime_button.pressed.connect(func(): _sort_by("生长时间"))
|
||||||
|
sort_profit_button.pressed.connect(func(): _sort_by("收益"))
|
||||||
|
sort_level_button.pressed.connect(func(): _sort_by("等级"))
|
||||||
|
|
||||||
|
# 初始化商店
|
||||||
|
func init_store():
|
||||||
|
print("初始化商店...")
|
||||||
|
|
||||||
|
# 清空已有的作物按钮
|
||||||
|
for child in crop_grid_container.get_children():
|
||||||
|
child.queue_free()
|
||||||
|
|
||||||
|
# 遍历可种植的作物数据并添加到商店
|
||||||
|
print("初始化商店,显示所有作物...")
|
||||||
|
for crop_name in main_game.can_planted_crop:
|
||||||
|
var crop = main_game.can_planted_crop[crop_name]
|
||||||
|
|
||||||
|
# 只显示当前等级可以种植的作物
|
||||||
|
if crop["等级"] <= main_game.level:
|
||||||
|
var store_btn = _create_store_button(crop_name, crop["品质"])
|
||||||
|
crop_grid_container.add_child(store_btn)
|
||||||
|
#print("添加商店按钮: " + crop_name)
|
||||||
|
|
||||||
|
print("商店初始化完成,共添加按钮: " + str(crop_grid_container.get_child_count()) + "个")
|
||||||
|
|
||||||
|
# 更新金钱显示
|
||||||
|
_update_money_display()
|
||||||
|
|
||||||
|
# 创建商店按钮
|
||||||
|
func _create_store_button(crop_name: String, crop_quality: String) -> Button:
|
||||||
|
# 根据品质选择相应的按钮
|
||||||
|
var button = null
|
||||||
|
match crop_quality:
|
||||||
|
"普通":
|
||||||
|
button = main_game.green_bar.duplicate()
|
||||||
|
"优良":
|
||||||
|
button = main_game.orange_bar.duplicate()
|
||||||
|
"稀有":
|
||||||
|
button = main_game.white_blue_bar.duplicate()
|
||||||
|
"史诗":
|
||||||
|
button = main_game.pink_bar.duplicate()
|
||||||
|
"传奇":
|
||||||
|
button = main_game.black_blue_bar.duplicate()
|
||||||
|
_: # 默认情况
|
||||||
|
button = main_game.green_bar.duplicate()
|
||||||
|
|
||||||
|
var crop = main_game.can_planted_crop[crop_name]
|
||||||
|
|
||||||
|
# 确保按钮可见并可点击
|
||||||
|
button.visible = true
|
||||||
|
button.disabled = false
|
||||||
|
button.focus_mode = Control.FOCUS_ALL
|
||||||
|
|
||||||
|
# 设置按钮文本,显示价格
|
||||||
|
button.text = str(crop_quality + "-" + crop_name + "\n价格: ¥" + str(crop["花费"]))
|
||||||
|
|
||||||
|
# 将成熟时间从秒转换为天时分秒格式
|
||||||
|
var total_seconds = int(crop["生长时间"])
|
||||||
|
|
||||||
|
# 定义时间单位换算
|
||||||
|
var SECONDS_PER_MINUTE = 60
|
||||||
|
var SECONDS_PER_HOUR = 3600
|
||||||
|
var SECONDS_PER_DAY = 86400
|
||||||
|
|
||||||
|
# 计算各时间单位
|
||||||
|
var days = total_seconds / SECONDS_PER_DAY
|
||||||
|
total_seconds %= SECONDS_PER_DAY
|
||||||
|
|
||||||
|
var hours = total_seconds / SECONDS_PER_HOUR
|
||||||
|
total_seconds %= SECONDS_PER_HOUR
|
||||||
|
|
||||||
|
var minutes = total_seconds / SECONDS_PER_MINUTE
|
||||||
|
var seconds = total_seconds % SECONDS_PER_MINUTE
|
||||||
|
|
||||||
|
# 构建时间字符串(只显示有值的单位)
|
||||||
|
var time_str = ""
|
||||||
|
if days > 0:
|
||||||
|
time_str += str(days) + "天"
|
||||||
|
if hours > 0:
|
||||||
|
time_str += str(hours) + "小时"
|
||||||
|
if minutes > 0:
|
||||||
|
time_str += str(minutes) + "分钟"
|
||||||
|
if seconds > 0:
|
||||||
|
time_str += str(seconds) + "秒"
|
||||||
|
|
||||||
|
button.tooltip_text = str(
|
||||||
|
"作物: " + crop_name + "\n" +
|
||||||
|
"品质: " + crop_quality + "\n" +
|
||||||
|
"价格: " + str(crop["花费"]) + "元\n" +
|
||||||
|
"成熟时间: " + time_str + "\n" +
|
||||||
|
"收获收益: " + str(crop["收益"]) + "元\n" +
|
||||||
|
"需求等级: " + str(crop["等级"]) + "\n" +
|
||||||
|
"耐候性: " + str(crop["耐候性"]) + "\n" +
|
||||||
|
"经验: " + str(crop["经验"]) + "点\n" +
|
||||||
|
"描述: " + str(crop["描述"])
|
||||||
|
)
|
||||||
|
|
||||||
|
# 添加按钮事件
|
||||||
|
button.pressed.connect(func(): _on_store_buy_pressed(crop_name))
|
||||||
|
|
||||||
|
# 更新按钮的作物图片
|
||||||
|
_update_button_crop_image(button, crop_name)
|
||||||
|
|
||||||
|
# 如果按钮有标题标签,设置标题
|
||||||
|
if button.has_node("Title"):
|
||||||
|
match crop_quality:
|
||||||
|
"普通":
|
||||||
|
button.get_node("Title").modulate = Color.HONEYDEW#白色
|
||||||
|
"优良":
|
||||||
|
button.get_node("Title").modulate =Color.DODGER_BLUE#深蓝色
|
||||||
|
"稀有":
|
||||||
|
button.get_node("Title").modulate =Color.HOT_PINK#品红色
|
||||||
|
"史诗":
|
||||||
|
button.get_node("Title").modulate =Color.YELLOW#黄色
|
||||||
|
"传奇":
|
||||||
|
button.get_node("Title").modulate =Color.ORANGE_RED#红色
|
||||||
|
|
||||||
|
return button
|
||||||
|
|
||||||
|
# 购买种子事件处理
|
||||||
|
func _on_store_buy_pressed(crop_name: String):
|
||||||
|
print("购买种子: " + crop_name)
|
||||||
|
var crop = main_game.can_planted_crop[crop_name]
|
||||||
|
|
||||||
|
# 检查等级要求
|
||||||
|
if main_game.level < crop["等级"]:
|
||||||
|
Toast.show("等级不足,无法购买此种子", Color.RED)
|
||||||
|
return
|
||||||
|
|
||||||
|
# 检查金钱是否足够
|
||||||
|
if main_game.money < crop["花费"]:
|
||||||
|
Toast.show("金钱不足,无法购买种子", Color.RED)
|
||||||
|
return
|
||||||
|
|
||||||
|
# 发送购买请求到服务器
|
||||||
|
if network_manager and network_manager.sendBuySeed(crop_name):
|
||||||
|
# 购买请求已发送,等待服务器响应
|
||||||
|
Toast.show("正在购买种子...", Color.YELLOW, 2.0, 1.0)
|
||||||
|
|
||||||
|
# 将种子添加到背包
|
||||||
|
var found = false
|
||||||
|
for seed_item in main_game.player_bag:
|
||||||
|
if seed_item["name"] == crop_name:
|
||||||
|
seed_item["count"] += 1
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
|
||||||
|
if not found:
|
||||||
|
main_game.player_bag.append({
|
||||||
|
"name": crop_name,
|
||||||
|
"quality": crop["品质"],
|
||||||
|
"count": 1
|
||||||
|
})
|
||||||
|
|
||||||
|
# 显示购买成功消息
|
||||||
|
Toast.show("购买了" + crop["品质"] + "-" + crop_name + "种子", Color.GREEN)
|
||||||
|
|
||||||
|
# 更新背包UI
|
||||||
|
crop_store_panel.update_player_bag_ui()
|
||||||
|
|
||||||
|
# 更新金钱显示
|
||||||
|
_update_money_display()
|
||||||
|
|
||||||
|
# 关闭面板
|
||||||
|
func _on_quit_button_pressed():
|
||||||
|
print("关闭商店面板")
|
||||||
|
self.hide()
|
||||||
|
|
||||||
|
# 按品质过滤作物
|
||||||
|
func _filter_by_quality(quality: String):
|
||||||
|
current_filter_quality = quality
|
||||||
|
print("过滤作物,品质: " + (quality if quality != "" else "全部"))
|
||||||
|
_apply_filter_and_sort()
|
||||||
|
|
||||||
|
# 按指定键排序
|
||||||
|
func _sort_by(sort_key: String):
|
||||||
|
# 切换排序方向或设置新排序键
|
||||||
|
if current_sort_key == sort_key:
|
||||||
|
current_sort_ascending = !current_sort_ascending
|
||||||
|
else:
|
||||||
|
current_sort_key = sort_key
|
||||||
|
current_sort_ascending = true
|
||||||
|
|
||||||
|
print("排序作物,键: " + sort_key + ",升序: " + str(current_sort_ascending))
|
||||||
|
_apply_filter_and_sort()
|
||||||
|
|
||||||
|
# 应用过滤和排序
|
||||||
|
func _apply_filter_and_sort():
|
||||||
|
# 清空现有按钮
|
||||||
|
for child in crop_grid_container.get_children():
|
||||||
|
child.queue_free()
|
||||||
|
|
||||||
|
# 收集符合条件的作物
|
||||||
|
var filtered_crops = []
|
||||||
|
for crop_name in main_game.can_planted_crop:
|
||||||
|
var crop = main_game.can_planted_crop[crop_name]
|
||||||
|
|
||||||
|
# 检查等级和品质过滤
|
||||||
|
if crop["等级"] > main_game.level:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if current_filter_quality != "" and crop["品质"] != current_filter_quality:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 添加到过滤后的列表
|
||||||
|
filtered_crops.append({
|
||||||
|
"name": crop_name,
|
||||||
|
"data": crop
|
||||||
|
})
|
||||||
|
|
||||||
|
# 如果有排序条件,进行排序
|
||||||
|
if current_sort_key != "":
|
||||||
|
filtered_crops.sort_custom(Callable(self, "_sort_crop_items"))
|
||||||
|
|
||||||
|
# 添加所有过滤和排序后的作物
|
||||||
|
for crop in filtered_crops:
|
||||||
|
var store_btn = _create_store_button(crop["name"], crop["data"]["品质"])
|
||||||
|
crop_grid_container.add_child(store_btn)
|
||||||
|
|
||||||
|
# 更新金钱显示
|
||||||
|
_update_money_display()
|
||||||
|
|
||||||
|
# 自定义排序函数
|
||||||
|
func _sort_crop_items(a, b):
|
||||||
|
if current_sort_ascending:
|
||||||
|
return a["data"][current_sort_key] < b["data"][current_sort_key]
|
||||||
|
else:
|
||||||
|
return a["data"][current_sort_key] > b["data"][current_sort_key]
|
||||||
|
|
||||||
|
# 更新金钱显示
|
||||||
|
func _update_money_display():
|
||||||
|
var money_label = get_node_or_null("MoneyLabel")
|
||||||
|
if money_label == null:
|
||||||
|
# 创建金钱显示标签
|
||||||
|
money_label = Label.new()
|
||||||
|
money_label.name = "MoneyLabel"
|
||||||
|
money_label.position = Vector2(10, 10)
|
||||||
|
money_label.size = Vector2(300, 45)
|
||||||
|
|
||||||
|
# 设置标签样式
|
||||||
|
money_label.add_theme_color_override("font_color", Color(1, 0.647, 0, 1)) # 橙色
|
||||||
|
money_label.add_theme_font_size_override("font_size", 24)
|
||||||
|
|
||||||
|
add_child(money_label)
|
||||||
|
|
||||||
|
# 更新金钱显示
|
||||||
|
money_label.text = "当前金钱:" + str(main_game.money) + " 元"
|
||||||
|
print("更新商店金钱显示:" + str(main_game.money))
|
||||||
|
|
||||||
|
# 刷新商店内容,可以在金钱变化或等级提升后调用
|
||||||
|
func refresh_store():
|
||||||
|
# 清空并重新创建商店按钮
|
||||||
|
init_store()
|
||||||
|
|
||||||
|
# 尝试创建过滤按钮(如果商店面板中没有这些按钮)
|
||||||
|
_create_filter_buttons_if_needed()
|
||||||
|
|
||||||
|
# 如果需要,动态创建过滤按钮
|
||||||
|
func _create_filter_buttons_if_needed():
|
||||||
|
# 检查是否已存在过滤器容器
|
||||||
|
var filter_container = get_node_or_null("FilterContainer")
|
||||||
|
if filter_container == null:
|
||||||
|
# 创建过滤器容器
|
||||||
|
filter_container = HBoxContainer.new()
|
||||||
|
filter_container.name = "FilterContainer"
|
||||||
|
|
||||||
|
# 设置容器位置和大小
|
||||||
|
filter_container.position = Vector2(320, 10)
|
||||||
|
filter_container.size = Vector2(770, 45)
|
||||||
|
|
||||||
|
add_child(filter_container)
|
||||||
|
|
||||||
|
# 添加过滤按钮
|
||||||
|
_add_filter_button(filter_container, "全部", func(): _filter_by_quality(""))
|
||||||
|
_add_filter_button(filter_container, "普通", func(): _filter_by_quality("普通"))
|
||||||
|
_add_filter_button(filter_container, "优良", func(): _filter_by_quality("优良"))
|
||||||
|
_add_filter_button(filter_container, "稀有", func(): _filter_by_quality("稀有"))
|
||||||
|
_add_filter_button(filter_container, "史诗", func(): _filter_by_quality("史诗"))
|
||||||
|
_add_filter_button(filter_container, "传奇", func(): _filter_by_quality("传奇"))
|
||||||
|
|
||||||
|
# 检查是否已存在排序容器
|
||||||
|
var sort_container = get_node_or_null("SortContainer")
|
||||||
|
if sort_container == null:
|
||||||
|
# 创建排序容器
|
||||||
|
sort_container = HBoxContainer.new()
|
||||||
|
sort_container.name = "SortContainer"
|
||||||
|
|
||||||
|
# 设置容器位置和大小
|
||||||
|
sort_container.position = Vector2(320, 55)
|
||||||
|
sort_container.size = Vector2(770, 30)
|
||||||
|
|
||||||
|
add_child(sort_container)
|
||||||
|
|
||||||
|
# 添加排序按钮
|
||||||
|
_add_filter_button(sort_container, "按价格", func(): _sort_by("花费"))
|
||||||
|
_add_filter_button(sort_container, "按生长时间", func(): _sort_by("生长时间"))
|
||||||
|
_add_filter_button(sort_container, "按收益", func(): _sort_by("收益"))
|
||||||
|
_add_filter_button(sort_container, "按等级", func(): _sort_by("等级"))
|
||||||
|
|
||||||
|
# 添加过滤按钮
|
||||||
|
func _add_filter_button(container, text, callback):
|
||||||
|
var button = Button.new()
|
||||||
|
button.text = text
|
||||||
|
button.custom_minimum_size = Vector2(100, 0)
|
||||||
|
button.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||||
|
container.add_child(button)
|
||||||
|
button.pressed.connect(callback)
|
||||||
|
|
||||||
|
# 获取作物的最后一帧图片(用于商店显示)
|
||||||
|
func _get_crop_final_texture(crop_name: String) -> Texture2D:
|
||||||
|
"""
|
||||||
|
获取作物的最后一帧图片,用于商店和背包显示
|
||||||
|
如果作物图片不存在,使用默认图片的最后一帧
|
||||||
|
"""
|
||||||
|
# 先尝试从主游戏的缓存中获取
|
||||||
|
if main_game and main_game.crop_textures_cache.has(crop_name):
|
||||||
|
var textures = main_game.crop_textures_cache[crop_name]
|
||||||
|
if textures.size() > 0:
|
||||||
|
return textures[textures.size() - 1] # 返回最后一帧
|
||||||
|
|
||||||
|
# 如果主游戏缓存中没有,自己加载
|
||||||
|
var textures = _load_crop_textures(crop_name)
|
||||||
|
if textures.size() > 0:
|
||||||
|
return textures[textures.size() - 1] # 返回最后一帧
|
||||||
|
|
||||||
|
return null
|
||||||
|
|
||||||
|
# 加载作物图片序列帧(复用主游戏的逻辑)
|
||||||
|
func _load_crop_textures(crop_name: String) -> Array:
|
||||||
|
"""
|
||||||
|
加载指定作物的所有序列帧图片
|
||||||
|
"""
|
||||||
|
if crop_textures_cache.has(crop_name):
|
||||||
|
return crop_textures_cache[crop_name]
|
||||||
|
|
||||||
|
var textures = []
|
||||||
|
var crop_path = "res://assets/作物/" + crop_name + "/"
|
||||||
|
var default_path = "res://assets/作物/默认/"
|
||||||
|
|
||||||
|
# 检查作物文件夹是否存在
|
||||||
|
if DirAccess.dir_exists_absolute(crop_path):
|
||||||
|
# 尝试加载作物的序列帧(从0开始)
|
||||||
|
var frame_index = 0
|
||||||
|
while true:
|
||||||
|
var texture_path = crop_path + str(frame_index) + ".png"
|
||||||
|
if ResourceLoader.exists(texture_path):
|
||||||
|
var texture = load(texture_path)
|
||||||
|
if texture:
|
||||||
|
textures.append(texture)
|
||||||
|
frame_index += 1
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
if textures.size() > 0:
|
||||||
|
print("商店加载作物 ", crop_name, " 的 ", textures.size(), " 帧图片")
|
||||||
|
else:
|
||||||
|
print("商店:作物 ", crop_name, " 文件夹存在但没有找到有效图片,使用默认图片")
|
||||||
|
textures = _load_default_textures()
|
||||||
|
else:
|
||||||
|
print("商店:作物 ", crop_name, " 的文件夹不存在,使用默认图片")
|
||||||
|
textures = _load_default_textures()
|
||||||
|
|
||||||
|
# 缓存结果
|
||||||
|
crop_textures_cache[crop_name] = textures
|
||||||
|
crop_frame_counts[crop_name] = textures.size()
|
||||||
|
|
||||||
|
return textures
|
||||||
|
|
||||||
|
# 加载默认图片
|
||||||
|
func _load_default_textures() -> Array:
|
||||||
|
"""
|
||||||
|
加载默认作物图片
|
||||||
|
"""
|
||||||
|
if crop_textures_cache.has("默认"):
|
||||||
|
return crop_textures_cache["默认"]
|
||||||
|
|
||||||
|
var textures = []
|
||||||
|
var default_path = "res://assets/作物/默认/"
|
||||||
|
|
||||||
|
# 尝试加载默认图片序列帧
|
||||||
|
var frame_index = 0
|
||||||
|
while true:
|
||||||
|
var texture_path = default_path + str(frame_index) + ".png"
|
||||||
|
if ResourceLoader.exists(texture_path):
|
||||||
|
var texture = load(texture_path)
|
||||||
|
if texture:
|
||||||
|
textures.append(texture)
|
||||||
|
frame_index += 1
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
# 如果没有找到序列帧,尝试加载单个默认图片
|
||||||
|
if textures.size() == 0:
|
||||||
|
var single_texture_path = default_path + "0.png"
|
||||||
|
if ResourceLoader.exists(single_texture_path):
|
||||||
|
var texture = load(single_texture_path)
|
||||||
|
if texture:
|
||||||
|
textures.append(texture)
|
||||||
|
|
||||||
|
# 缓存默认图片
|
||||||
|
crop_textures_cache["默认"] = textures
|
||||||
|
crop_frame_counts["默认"] = textures.size()
|
||||||
|
|
||||||
|
print("商店加载了 ", textures.size(), " 个默认作物图片")
|
||||||
|
return textures
|
||||||
|
|
||||||
|
# 更新按钮的作物图片
|
||||||
|
func _update_button_crop_image(button: Button, crop_name: String):
|
||||||
|
"""
|
||||||
|
更新按钮中的作物图片
|
||||||
|
"""
|
||||||
|
# 检查按钮是否有CropImage节点
|
||||||
|
var crop_image = button.get_node_or_null("CropImage")
|
||||||
|
if not crop_image:
|
||||||
|
print("商店按钮没有找到CropImage节点:", button.name)
|
||||||
|
return
|
||||||
|
|
||||||
|
# 获取作物的最后一帧图片
|
||||||
|
var texture = _get_crop_final_texture(crop_name)
|
||||||
|
|
||||||
|
if texture:
|
||||||
|
# CropImage是Sprite2D,直接设置texture属性
|
||||||
|
crop_image.texture = texture
|
||||||
|
crop_image.visible = true
|
||||||
|
print("商店更新作物图片:", crop_name)
|
||||||
|
else:
|
||||||
|
crop_image.visible = false
|
||||||
|
print("商店无法获取作物图片:", crop_name)
|
||||||
|
|
||||||
|
# 兼容MainGame.gd中的调用,转发到_on_store_buy_pressed
|
||||||
|
func _on_crop_selected(crop_name: String):
|
||||||
|
_on_store_buy_pressed(crop_name)
|
||||||
1
GUI/crop_store_panel.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://mtfp0ct42nrx
|
||||||
126
GUI/land_panel.gd
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
extends Panel
|
||||||
|
|
||||||
|
#获取玩家要操作的地块序号
|
||||||
|
var selected_lot_index = 0
|
||||||
|
|
||||||
|
#预添加常用的面板
|
||||||
|
@onready var main_game = get_node("/root/main")
|
||||||
|
@onready var land_panel = get_node("/root/main/UI/LandPanel")
|
||||||
|
@onready var crop_store_panel = get_node("/root/main/UI/PlayerBagPanel")
|
||||||
|
@onready var player_ranking_panel = get_node("/root/main/UI/PlayerRankingPanel")
|
||||||
|
@onready var player_bag_panel = get_node("/root/main/UI/PlayerBagPanel")
|
||||||
|
@onready var network_manager = get_node("/root/main/UI/TCPNetworkManager")
|
||||||
|
|
||||||
|
@onready var quit_button :Button = $Quit_Button
|
||||||
|
@onready var dig_button: Button = $Grid/Dig_Button
|
||||||
|
@onready var water_button: Button = $Grid/Water_Button
|
||||||
|
@onready var fertilize_button: Button = $Grid/Fertilize_Button
|
||||||
|
@onready var upgrade_button: Button = $Grid/Upgrade_Button
|
||||||
|
@onready var plant_button: Button = $Grid/Plant_Button
|
||||||
|
@onready var remove_button: Button = $Grid/Remove_Button
|
||||||
|
@onready var harvest_button: Button = $Grid/Harvest_Button
|
||||||
|
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
self.hide()
|
||||||
|
quit_button.pressed.connect(self._on_quit_button_pressed)
|
||||||
|
dig_button.pressed.connect(self._on_dig_button_pressed)
|
||||||
|
water_button.pressed.connect(self._on_water_button_pressed)
|
||||||
|
fertilize_button.pressed.connect(self._on_fertilize_button_pressed)
|
||||||
|
upgrade_button.pressed.connect(self._on_upgrade_button_pressed)
|
||||||
|
plant_button.pressed.connect(self._on_plant_button_pressed)
|
||||||
|
remove_button.pressed.connect(self._on_remove_button_pressed)
|
||||||
|
harvest_button.pressed.connect(self._on_harvest_button_pressed)
|
||||||
|
dig_button.text = "开垦"+"\n花费:"+str(main_game.dig_money)
|
||||||
|
|
||||||
|
#开垦
|
||||||
|
func _on_dig_button_pressed():
|
||||||
|
# 检查是否处于访问模式
|
||||||
|
if main_game.is_visiting_mode:
|
||||||
|
Toast.show("访问模式下无法开垦土地", Color.ORANGE, 2.0, 1.0)
|
||||||
|
self.hide()
|
||||||
|
return
|
||||||
|
|
||||||
|
dig_button.text = "开垦"+"\n花费:"+str(main_game.dig_money)
|
||||||
|
|
||||||
|
if network_manager and network_manager.is_connected_to_server():
|
||||||
|
# 使用服务器API来开垦土地
|
||||||
|
if network_manager.sendDigGround(selected_lot_index):
|
||||||
|
self.hide()
|
||||||
|
#浇水
|
||||||
|
func _on_water_button_pressed():
|
||||||
|
# 检查是否处于访问模式
|
||||||
|
if main_game.is_visiting_mode:
|
||||||
|
Toast.show("访问模式下无法浇水", Color.ORANGE, 2.0, 1.0)
|
||||||
|
self.hide()
|
||||||
|
return
|
||||||
|
|
||||||
|
self.hide()
|
||||||
|
pass
|
||||||
|
#施肥
|
||||||
|
func _on_fertilize_button_pressed():
|
||||||
|
# 检查是否处于访问模式
|
||||||
|
if main_game.is_visiting_mode:
|
||||||
|
Toast.show("访问模式下无法施肥", Color.ORANGE, 2.0, 1.0)
|
||||||
|
self.hide()
|
||||||
|
return
|
||||||
|
|
||||||
|
self.hide()
|
||||||
|
pass
|
||||||
|
#升级
|
||||||
|
func _on_upgrade_button_pressed():
|
||||||
|
# 检查是否处于访问模式
|
||||||
|
if main_game.is_visiting_mode:
|
||||||
|
Toast.show("访问模式下无法升级", Color.ORANGE, 2.0, 1.0)
|
||||||
|
self.hide()
|
||||||
|
return
|
||||||
|
|
||||||
|
self.hide()
|
||||||
|
pass
|
||||||
|
#种植
|
||||||
|
func _on_plant_button_pressed():
|
||||||
|
# 检查是否处于访问模式
|
||||||
|
if main_game.is_visiting_mode:
|
||||||
|
Toast.show("访问模式下无法种植", Color.ORANGE, 2.0, 1.0)
|
||||||
|
self.hide()
|
||||||
|
return
|
||||||
|
|
||||||
|
player_bag_panel.show()
|
||||||
|
self.hide()
|
||||||
|
pass
|
||||||
|
#铲除
|
||||||
|
func _on_remove_button_pressed():
|
||||||
|
# 检查是否处于访问模式
|
||||||
|
if main_game.is_visiting_mode:
|
||||||
|
Toast.show("访问模式下无法铲除作物", Color.ORANGE, 2.0, 1.0)
|
||||||
|
self.hide()
|
||||||
|
return
|
||||||
|
|
||||||
|
main_game.root_out_crop(selected_lot_index)
|
||||||
|
self.hide()
|
||||||
|
pass
|
||||||
|
#收获
|
||||||
|
func _on_harvest_button_pressed():
|
||||||
|
# 检查是否处于访问模式
|
||||||
|
if main_game.is_visiting_mode:
|
||||||
|
Toast.show("访问模式下无法收获作物", Color.ORANGE, 2.0, 1.0)
|
||||||
|
self.hide()
|
||||||
|
return
|
||||||
|
|
||||||
|
main_game._harvest_crop(selected_lot_index)
|
||||||
|
self.hide()
|
||||||
|
pass
|
||||||
|
#退出
|
||||||
|
func _on_quit_button_pressed():
|
||||||
|
self.hide()
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
1
GUI/land_panel.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://bljtkxil64h14
|
||||||
87
GUI/login.gd
@@ -1,87 +0,0 @@
|
|||||||
extends Node
|
|
||||||
|
|
||||||
var http_request: HTTPRequest
|
|
||||||
var farm_lots = []
|
|
||||||
|
|
||||||
@onready var harvest = $harvest
|
|
||||||
@onready var label1 = $Label
|
|
||||||
@onready var username_input = $Panel/username_input
|
|
||||||
@onready var password_input = $Panel/password_input
|
|
||||||
@onready var login_button = $Panel/login_button
|
|
||||||
@onready var panel = $Panel
|
|
||||||
@onready var item_list = $item_list #ItemList
|
|
||||||
|
|
||||||
func _ready():
|
|
||||||
# 创建 HTTPRequest 节点
|
|
||||||
http_request = HTTPRequest.new()
|
|
||||||
add_child(http_request)
|
|
||||||
# 连接信号
|
|
||||||
http_request.connect("request_completed", Callable(self, "_on_request_completed"))
|
|
||||||
# 连接登录按钮点击事件
|
|
||||||
login_button.connect("pressed", Callable(self, "_on_login_button_pressed"))
|
|
||||||
|
|
||||||
# 登录按钮按下事件
|
|
||||||
func _on_login_button_pressed():
|
|
||||||
# 隐藏面板(只有在输入后才进行隐藏)
|
|
||||||
panel.hide()
|
|
||||||
|
|
||||||
# 获取用户名和密码输入
|
|
||||||
var username = username_input.text # 直接获取输入
|
|
||||||
var password = password_input.text # 直接获取输入
|
|
||||||
|
|
||||||
# 打印调试信息
|
|
||||||
print("Username entered: ", username)
|
|
||||||
print("Password entered: ", password)
|
|
||||||
|
|
||||||
# 检查用户名和密码是否为空
|
|
||||||
if username == "" or password == "":
|
|
||||||
print("用户名和密码不能为空")
|
|
||||||
panel.show() # 如果输入为空,显示面板
|
|
||||||
return
|
|
||||||
|
|
||||||
# 构建登录请求的 URL 和参数
|
|
||||||
var url = "https://api.shumengya.top/smyfarm/login.php"
|
|
||||||
var body = {
|
|
||||||
"username": username,
|
|
||||||
"password": password
|
|
||||||
}
|
|
||||||
|
|
||||||
# 发送 POST 请求进行登录
|
|
||||||
var err = http_request.request(url, [], HTTPClient.METHOD_POST, JSON.stringify(body))
|
|
||||||
if err != OK:
|
|
||||||
print("Error making HTTP POST request: ", err)
|
|
||||||
|
|
||||||
# 请求完成后的回调函数
|
|
||||||
func _on_request_completed(result, response_code, headers, body):
|
|
||||||
if response_code == 200:
|
|
||||||
var json = JSON.new()
|
|
||||||
var parse_result = json.parse(body.get_string_from_utf8())
|
|
||||||
|
|
||||||
if parse_result != OK:
|
|
||||||
print("Error parsing JSON: ", json.get_error_message())
|
|
||||||
return
|
|
||||||
|
|
||||||
var json_data = json.data
|
|
||||||
|
|
||||||
if json_data.has("error"):
|
|
||||||
print("Error: " + str(json_data["error"]))
|
|
||||||
panel.show() # 如果登录失败,显示面板,允许重新输入
|
|
||||||
elif json_data.has("message"):
|
|
||||||
print(json_data["message"])
|
|
||||||
|
|
||||||
# 处理登录成功
|
|
||||||
if json_data.has("data"):
|
|
||||||
var player_data = json_data["data"]
|
|
||||||
print("欢迎, " + player_data["username"])
|
|
||||||
|
|
||||||
# 加载玩家数据,进入游戏逻辑
|
|
||||||
_load_player_data(player_data)
|
|
||||||
else:
|
|
||||||
print("HTTP Request failed with response code: " + str(response_code))
|
|
||||||
panel.show() # 请求失败,重新显示面板
|
|
||||||
|
|
||||||
# 加载玩家数据
|
|
||||||
func _load_player_data(player_data):
|
|
||||||
# 根据返回的 player_data 初始化玩家的农场状态等
|
|
||||||
pass
|
|
||||||
|
|
||||||
433
GUI/login_panel.gd
Normal file
@@ -0,0 +1,433 @@
|
|||||||
|
#玩家登录注册面板
|
||||||
|
extends PanelContainer
|
||||||
|
|
||||||
|
#玩家登录账号,用QQ号代替
|
||||||
|
@onready var username_input : LineEdit = $VBox/UserName/username_input
|
||||||
|
#用户登录密码
|
||||||
|
@onready var password_input : LineEdit = $VBox/Password1/password_input
|
||||||
|
#登录按钮
|
||||||
|
@onready var login_button : Button = $VBox/LoginRegister/login_button
|
||||||
|
|
||||||
|
#下面是注册相关的
|
||||||
|
#注册按钮
|
||||||
|
@onready var register_button : Button = $VBox/LoginRegister/register_button
|
||||||
|
#注册账号时二次确认密码
|
||||||
|
@onready var password_input_2 : LineEdit = $VBox/Password2/password_input2
|
||||||
|
#农场名称
|
||||||
|
@onready var farmname_input : LineEdit = $VBox/FarmName/farmname_input
|
||||||
|
#玩家昵称
|
||||||
|
@onready var playername_input :LineEdit = $VBox/PlayerName/playername_input
|
||||||
|
#邮箱验证码
|
||||||
|
@onready var verificationcode_input :LineEdit = $VBox/VerificationCode/verificationcode_input
|
||||||
|
#发送验证码按钮
|
||||||
|
@onready var send_button :Button = $VBox/VerificationCode/SendButton
|
||||||
|
#状态提示标签
|
||||||
|
@onready var status_label : Label = $VBox/status_label
|
||||||
|
|
||||||
|
# 记住密码选项(如果UI中有CheckBox的话)
|
||||||
|
var remember_password : bool = true # 默认记住密码
|
||||||
|
|
||||||
|
# 引用主场景和全局函数
|
||||||
|
@onready var main_game = get_node("/root/main")
|
||||||
|
@onready var land_panel = get_node("/root/main/UI/LandPanel")
|
||||||
|
@onready var crop_store_panel = get_node("/root/main/UI/PlayerBagPanel")
|
||||||
|
@onready var player_ranking_panel = get_node("/root/main/UI/PlayerRankingPanel")
|
||||||
|
@onready var player_bag_panel = get_node("/root/main/UI/PlayerBagPanel")
|
||||||
|
@onready var tcp_network_manager = get_node("/root/main/UI/TCPNetworkManager")
|
||||||
|
|
||||||
|
# 准备函数
|
||||||
|
func _ready():
|
||||||
|
|
||||||
|
# 连接按钮信号
|
||||||
|
login_button.pressed.connect(self._on_login_button_pressed)
|
||||||
|
register_button.pressed.connect(self._on_register_button_pressed)
|
||||||
|
send_button.pressed.connect(self._on_send_button_pressed)
|
||||||
|
|
||||||
|
# 加载保存的登录信息
|
||||||
|
_load_login_info()
|
||||||
|
|
||||||
|
# 处理登录按钮点击
|
||||||
|
func _on_login_button_pressed():
|
||||||
|
var user_name = username_input.text.strip_edges() # 修剪前后的空格
|
||||||
|
var user_password = password_input.text.strip_edges()
|
||||||
|
var farmname = farmname_input.text.strip_edges()
|
||||||
|
|
||||||
|
if user_name == "" or user_password == "":
|
||||||
|
status_label.text = "用户名或密码不能为空!"
|
||||||
|
status_label.modulate = Color.RED
|
||||||
|
return
|
||||||
|
|
||||||
|
# 检查网络连接状态
|
||||||
|
if !tcp_network_manager.client.is_client_connected():
|
||||||
|
status_label.text = "未连接到服务器,正在尝试连接..."
|
||||||
|
status_label.modulate = Color.YELLOW
|
||||||
|
# 尝试自动连接到服务器
|
||||||
|
tcp_network_manager._on_connection_button_pressed()
|
||||||
|
await get_tree().create_timer(1.0).timeout
|
||||||
|
|
||||||
|
# 再次检查连接状态
|
||||||
|
if !tcp_network_manager.client.is_client_connected():
|
||||||
|
status_label.text = "连接服务器失败,请检查网络设置!"
|
||||||
|
status_label.modulate = Color.RED
|
||||||
|
return
|
||||||
|
|
||||||
|
# 禁用按钮,防止重复点击
|
||||||
|
login_button.disabled = true
|
||||||
|
|
||||||
|
status_label.text = "正在登录,请稍候..."
|
||||||
|
status_label.modulate = Color.YELLOW
|
||||||
|
|
||||||
|
# 如果启用了记住密码,保存登录信息
|
||||||
|
if remember_password:
|
||||||
|
_save_login_info(user_name, user_password)
|
||||||
|
|
||||||
|
tcp_network_manager.sendLoginInfo(user_name, user_password)
|
||||||
|
|
||||||
|
# 更新主游戏数据
|
||||||
|
main_game.user_name = user_name
|
||||||
|
main_game.user_password = user_password
|
||||||
|
main_game.farmname = farmname
|
||||||
|
|
||||||
|
# 5秒后重新启用按钮(如果没有收到响应)
|
||||||
|
await get_tree().create_timer(5.0).timeout
|
||||||
|
if login_button.disabled:
|
||||||
|
login_button.disabled = false
|
||||||
|
status_label.text = "登录超时,请重试!"
|
||||||
|
status_label.modulate = Color.RED
|
||||||
|
|
||||||
|
# 处理验证码发送按钮点击
|
||||||
|
func _on_send_button_pressed():
|
||||||
|
var user_name = username_input.text.strip_edges()
|
||||||
|
|
||||||
|
if user_name == "":
|
||||||
|
status_label.text = "请输入QQ号以接收验证码!"
|
||||||
|
status_label.modulate = Color.RED
|
||||||
|
return
|
||||||
|
|
||||||
|
if !is_valid_qq_number(user_name):
|
||||||
|
status_label.text = "请输入正确的QQ号码(5-12位数字)!"
|
||||||
|
status_label.modulate = Color.RED
|
||||||
|
return
|
||||||
|
|
||||||
|
# 检查网络连接状态
|
||||||
|
if !tcp_network_manager.client.is_client_connected():
|
||||||
|
status_label.text = "未连接到服务器,正在尝试连接..."
|
||||||
|
status_label.modulate = Color.YELLOW
|
||||||
|
# 尝试自动连接到服务器
|
||||||
|
tcp_network_manager._on_connection_button_pressed()
|
||||||
|
await get_tree().create_timer(1.0).timeout
|
||||||
|
|
||||||
|
# 再次检查连接状态
|
||||||
|
if !tcp_network_manager.client.is_client_connected():
|
||||||
|
status_label.text = "连接服务器失败,请检查网络设置!"
|
||||||
|
status_label.modulate = Color.RED
|
||||||
|
return
|
||||||
|
|
||||||
|
# 禁用按钮,防止重复点击
|
||||||
|
send_button.disabled = true
|
||||||
|
|
||||||
|
status_label.text = "正在发送验证码,请稍候..."
|
||||||
|
status_label.modulate = Color.YELLOW
|
||||||
|
|
||||||
|
# 发送验证码请求
|
||||||
|
tcp_network_manager.sendVerificationCodeRequest(user_name)
|
||||||
|
|
||||||
|
# 60秒后重新启用按钮(或收到响应后提前启用)
|
||||||
|
var timer = 60
|
||||||
|
while timer > 0 and send_button.disabled:
|
||||||
|
send_button.text = "重新发送(%d)" % timer
|
||||||
|
await get_tree().create_timer(1.0).timeout
|
||||||
|
timer -= 1
|
||||||
|
|
||||||
|
if send_button.disabled:
|
||||||
|
send_button.disabled = false
|
||||||
|
send_button.text = "发送验证码"
|
||||||
|
|
||||||
|
if status_label.text == "正在发送验证码,请稍候...":
|
||||||
|
status_label.text = "验证码发送超时,请重试!"
|
||||||
|
status_label.modulate = Color.RED
|
||||||
|
|
||||||
|
# 处理注册按钮点击
|
||||||
|
func _on_register_button_pressed():
|
||||||
|
var user_name = username_input.text.strip_edges()
|
||||||
|
var user_password = password_input.text.strip_edges()
|
||||||
|
var user_password_2 = password_input_2.text.strip_edges()
|
||||||
|
var farmname = farmname_input.text.strip_edges()
|
||||||
|
var player_name = playername_input.text.strip_edges()
|
||||||
|
var verification_code = verificationcode_input.text.strip_edges()
|
||||||
|
|
||||||
|
if user_name == "" or user_password == "":
|
||||||
|
status_label.text = "用户名或密码不能为空!"
|
||||||
|
status_label.modulate = Color.RED
|
||||||
|
return
|
||||||
|
if farmname == "":
|
||||||
|
status_label.text = "农场名称不能为空!"
|
||||||
|
status_label.modulate = Color.RED
|
||||||
|
return
|
||||||
|
if user_password != user_password_2:
|
||||||
|
status_label.text = "两次输入的密码不一致!"
|
||||||
|
status_label.modulate = Color.RED
|
||||||
|
return
|
||||||
|
|
||||||
|
if !is_valid_qq_number(user_name):
|
||||||
|
status_label.text = "请输入正确的QQ号码(5-12位数字)!"
|
||||||
|
status_label.modulate = Color.RED
|
||||||
|
return
|
||||||
|
|
||||||
|
if verification_code == "":
|
||||||
|
status_label.text = "请输入验证码!"
|
||||||
|
status_label.modulate = Color.RED
|
||||||
|
return
|
||||||
|
|
||||||
|
# 检查网络连接状态
|
||||||
|
if !tcp_network_manager.client.is_client_connected():
|
||||||
|
status_label.text = "未连接到服务器,正在尝试连接..."
|
||||||
|
status_label.modulate = Color.YELLOW
|
||||||
|
# 尝试自动连接到服务器
|
||||||
|
tcp_network_manager._on_connection_button_pressed()
|
||||||
|
await get_tree().create_timer(1.0).timeout
|
||||||
|
|
||||||
|
# 再次检查连接状态
|
||||||
|
if !tcp_network_manager.client.is_client_connected():
|
||||||
|
status_label.text = "连接服务器失败,请检查网络设置!"
|
||||||
|
status_label.modulate = Color.RED
|
||||||
|
return
|
||||||
|
|
||||||
|
# 禁用按钮,防止重复点击
|
||||||
|
register_button.disabled = true
|
||||||
|
|
||||||
|
status_label.text = "正在注册,请稍候..."
|
||||||
|
status_label.modulate = Color.YELLOW
|
||||||
|
|
||||||
|
# 发送注册请求
|
||||||
|
tcp_network_manager.sendRegisterInfo(user_name, user_password, farmname, player_name, verification_code)
|
||||||
|
|
||||||
|
# 更新主游戏数据
|
||||||
|
main_game.user_name = user_name
|
||||||
|
main_game.user_password = user_password
|
||||||
|
main_game.farmname = farmname
|
||||||
|
|
||||||
|
# 5秒后重新启用按钮(如果没有收到响应)
|
||||||
|
await get_tree().create_timer(5.0).timeout
|
||||||
|
if register_button.disabled:
|
||||||
|
register_button.disabled = false
|
||||||
|
status_label.text = "注册超时,请重试!"
|
||||||
|
status_label.modulate = Color.RED
|
||||||
|
|
||||||
|
# 处理验证码发送响应
|
||||||
|
func _on_verification_code_response(success: bool, message: String):
|
||||||
|
if success:
|
||||||
|
status_label.text = message
|
||||||
|
status_label.modulate = Color.GREEN
|
||||||
|
else:
|
||||||
|
status_label.text = message
|
||||||
|
status_label.modulate = Color.RED
|
||||||
|
send_button.disabled = false
|
||||||
|
send_button.text = "发送验证码"
|
||||||
|
|
||||||
|
# 处理验证码验证响应
|
||||||
|
func _on_verify_code_response(success: bool, message: String):
|
||||||
|
if success:
|
||||||
|
status_label.text = message
|
||||||
|
status_label.modulate = Color.GREEN
|
||||||
|
else:
|
||||||
|
status_label.text = message
|
||||||
|
status_label.modulate = Color.RED
|
||||||
|
|
||||||
|
# 验证QQ号是否有效
|
||||||
|
func is_valid_qq_number(qq_number: String) -> bool:
|
||||||
|
# QQ号的标准格式是5到12位的数字
|
||||||
|
var qq_regex = RegEx.new()
|
||||||
|
var pattern = r"^\d{5,12}$"
|
||||||
|
|
||||||
|
var error = qq_regex.compile(pattern)
|
||||||
|
if error != OK:
|
||||||
|
status_label.text = "QQ号验证失败部错误"
|
||||||
|
status_label.modulate = Color.RED
|
||||||
|
return false
|
||||||
|
|
||||||
|
return qq_regex.search(qq_number) != null
|
||||||
|
|
||||||
|
# 处理登录响应
|
||||||
|
func _on_login_response_received(success: bool, message: String, user_data: Dictionary):
|
||||||
|
# 启用按钮
|
||||||
|
login_button.disabled = false
|
||||||
|
|
||||||
|
if success:
|
||||||
|
status_label.text = "登录成功!正在加载游戏..."
|
||||||
|
status_label.modulate = Color.GREEN
|
||||||
|
|
||||||
|
# 更新主游戏数据
|
||||||
|
main_game.experience = user_data.get("experience", 0)
|
||||||
|
main_game.farm_lots = user_data.get("farm_lots", [])
|
||||||
|
main_game.level = user_data.get("level", 1)
|
||||||
|
main_game.money = user_data.get("money", 0)
|
||||||
|
main_game.farmname = user_data.get("farm_name", "")
|
||||||
|
farmname_input.text = user_data.get("farm_name", "")
|
||||||
|
|
||||||
|
# 加载玩家背包数据
|
||||||
|
if user_data.has("player_bag"):
|
||||||
|
main_game.player_bag = user_data.get("player_bag", [])
|
||||||
|
else:
|
||||||
|
main_game.player_bag = []
|
||||||
|
|
||||||
|
main_game.start_game = true
|
||||||
|
self.hide()
|
||||||
|
|
||||||
|
# 确保在更新数据后调用主游戏的 UI 更新函数
|
||||||
|
main_game._update_ui()
|
||||||
|
main_game._refresh_farm_lots()
|
||||||
|
player_bag_panel.update_player_bag_ui()
|
||||||
|
else:
|
||||||
|
status_label.text = "登录失败:" + message
|
||||||
|
status_label.modulate = Color.RED
|
||||||
|
|
||||||
|
# 如果登录失败且是密码错误,可以选择清除保存的信息
|
||||||
|
if "密码" in message or "password" in message.to_lower():
|
||||||
|
print("登录失败,可能是密码错误。如需清除保存的登录信息,请调用_clear_login_info()")
|
||||||
|
|
||||||
|
# 处理注册响应
|
||||||
|
func _on_register_response_received(success: bool, message: String):
|
||||||
|
# 启用按钮
|
||||||
|
register_button.disabled = false
|
||||||
|
|
||||||
|
if success:
|
||||||
|
status_label.text = "注册成功!请登录游戏"
|
||||||
|
status_label.modulate = Color.GREEN
|
||||||
|
|
||||||
|
# 注册成功后,如果启用了记住密码,保存登录信息
|
||||||
|
if remember_password:
|
||||||
|
var user_name = username_input.text.strip_edges()
|
||||||
|
var user_password = password_input.text.strip_edges()
|
||||||
|
_save_login_info(user_name, user_password)
|
||||||
|
|
||||||
|
# 清除注册相关的输入框
|
||||||
|
password_input_2.text = ""
|
||||||
|
verificationcode_input.text = ""
|
||||||
|
else:
|
||||||
|
status_label.text = "注册失败:" + message
|
||||||
|
status_label.modulate = Color.RED
|
||||||
|
|
||||||
|
# 保存登录信息到JSON文件
|
||||||
|
func _save_login_info(user_name: String, password: String):
|
||||||
|
var login_data = {
|
||||||
|
"user_name": user_name,
|
||||||
|
"password": password
|
||||||
|
}
|
||||||
|
|
||||||
|
var file = FileAccess.open("user://login.json", FileAccess.WRITE)
|
||||||
|
if file:
|
||||||
|
var json_string = JSON.stringify(login_data, "\t")
|
||||||
|
file.store_string(json_string)
|
||||||
|
file.close()
|
||||||
|
print("登录信息已保存")
|
||||||
|
else:
|
||||||
|
print("无法保存登录信息")
|
||||||
|
|
||||||
|
# 从JSON文件加载登录信息
|
||||||
|
func _load_login_info():
|
||||||
|
var file = FileAccess.open("user://login.json", FileAccess.READ)
|
||||||
|
if file:
|
||||||
|
var json_text = file.get_as_text()
|
||||||
|
file.close()
|
||||||
|
|
||||||
|
var json = JSON.new()
|
||||||
|
var parse_result = json.parse(json_text)
|
||||||
|
if parse_result == OK:
|
||||||
|
var login_data = json.get_data()
|
||||||
|
if login_data.has("user_name") and login_data.has("password"):
|
||||||
|
var saved_username = login_data.get("user_name", "")
|
||||||
|
var saved_password = login_data.get("password", "")
|
||||||
|
|
||||||
|
if saved_username != "" and saved_password != "":
|
||||||
|
username_input.text = saved_username
|
||||||
|
password_input.text = saved_password
|
||||||
|
status_label.text = "已加载保存的登录信息"
|
||||||
|
status_label.modulate = Color.CYAN
|
||||||
|
print("登录信息已加载:用户名 =", saved_username)
|
||||||
|
else:
|
||||||
|
status_label.text = "欢迎使用萌芽农场"
|
||||||
|
status_label.modulate = Color.WHITE
|
||||||
|
print("没有有效的保存登录信息")
|
||||||
|
else:
|
||||||
|
print("登录信息格式错误")
|
||||||
|
else:
|
||||||
|
print("登录信息JSON解析错误:", json.get_error_message())
|
||||||
|
else:
|
||||||
|
# 创建默认的登录信息文件
|
||||||
|
_save_login_info("", "")
|
||||||
|
status_label.text = "欢迎使用萌芽农场"
|
||||||
|
status_label.modulate = Color.WHITE
|
||||||
|
print("没有找到保存的登录信息,已创建默认文件")
|
||||||
|
|
||||||
|
# 清除保存的登录信息
|
||||||
|
func _clear_login_info():
|
||||||
|
var file = FileAccess.open("user://login.json", FileAccess.WRITE)
|
||||||
|
if file:
|
||||||
|
var empty_data = {
|
||||||
|
"user_name": "",
|
||||||
|
"password": ""
|
||||||
|
}
|
||||||
|
var json_string = JSON.stringify(empty_data, "\t")
|
||||||
|
file.store_string(json_string)
|
||||||
|
file.close()
|
||||||
|
print("登录信息已清除")
|
||||||
|
else:
|
||||||
|
print("无法清除登录信息")
|
||||||
|
|
||||||
|
# 切换记住密码选项
|
||||||
|
func toggle_remember_password():
|
||||||
|
remember_password = !remember_password
|
||||||
|
print("记住密码选项:", "开启" if remember_password else "关闭")
|
||||||
|
|
||||||
|
# 如果关闭了记住密码,清除已保存的信息
|
||||||
|
if not remember_password:
|
||||||
|
_clear_login_info()
|
||||||
|
|
||||||
|
# 检查是否有保存的登录信息
|
||||||
|
func has_saved_login_info() -> bool:
|
||||||
|
var file = FileAccess.open("user://login.json", FileAccess.READ)
|
||||||
|
if file:
|
||||||
|
var json_text = file.get_as_text()
|
||||||
|
file.close()
|
||||||
|
|
||||||
|
var json = JSON.new()
|
||||||
|
var parse_result = json.parse(json_text)
|
||||||
|
if parse_result == OK:
|
||||||
|
var login_data = json.get_data()
|
||||||
|
var user_name = login_data.get("user_name", "")
|
||||||
|
var password = login_data.get("password", "")
|
||||||
|
return user_name != "" and password != ""
|
||||||
|
|
||||||
|
return false
|
||||||
|
|
||||||
|
# 快捷登录(使用保存的登录信息)
|
||||||
|
func quick_login():
|
||||||
|
if has_saved_login_info():
|
||||||
|
var user_name = username_input.text.strip_edges()
|
||||||
|
var user_password = password_input.text.strip_edges()
|
||||||
|
|
||||||
|
if user_name != "" and user_password != "":
|
||||||
|
print("执行快捷登录...")
|
||||||
|
_on_login_button_pressed()
|
||||||
|
else:
|
||||||
|
status_label.text = "保存的登录信息不完整"
|
||||||
|
status_label.modulate = Color.ORANGE
|
||||||
|
else:
|
||||||
|
status_label.text = "没有保存的登录信息"
|
||||||
|
status_label.modulate = Color.ORANGE
|
||||||
|
|
||||||
|
# 获取保存的用户名(用于调试或显示)
|
||||||
|
func get_saved_username() -> String:
|
||||||
|
var file = FileAccess.open("user://login.json", FileAccess.READ)
|
||||||
|
if file:
|
||||||
|
var json_text = file.get_as_text()
|
||||||
|
file.close()
|
||||||
|
|
||||||
|
var json = JSON.new()
|
||||||
|
var parse_result = json.parse(json_text)
|
||||||
|
if parse_result == OK:
|
||||||
|
var login_data = json.get_data()
|
||||||
|
return login_data.get("user_name", "")
|
||||||
|
|
||||||
|
return ""
|
||||||
1
GUI/login_panel.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://cka0r4g8tbf0
|
||||||
479
GUI/player_bag_panel.gd
Normal file
@@ -0,0 +1,479 @@
|
|||||||
|
extends Panel
|
||||||
|
|
||||||
|
# 背包格子容器
|
||||||
|
@onready var player_bag_grid_container : GridContainer = $ScrollContainer/Bag_Grid
|
||||||
|
@onready var quit_button : Button = $QuitButton
|
||||||
|
|
||||||
|
#各种排序过滤按钮
|
||||||
|
@onready var sort_all_button : Button = $SortContainer/Sort_All#全部
|
||||||
|
@onready var sort_common_button : Button = $SortContainer/Sort_Common#普通
|
||||||
|
@onready var sort_superior_button : Button = $SortContainer/Sort_Superior#优良
|
||||||
|
@onready var sort_rare_button : Button = $SortContainer/Sort_Rare#稀有
|
||||||
|
@onready var sort_epic_button : Button = $SortContainer/Sort_Epic#史诗
|
||||||
|
@onready var sort_legendary_button : Button = $SortContainer/Sort_Legendary#传奇
|
||||||
|
@onready var sort_price_button : Button = $SortContainer/Sort_Price#价格
|
||||||
|
@onready var sort_growtime_button : Button = $SortContainer/Sort_GrowTime#生长时间
|
||||||
|
@onready var sort_profit_button : Button = $SortContainer/Sort_Profit#收益
|
||||||
|
@onready var sort_level_button : Button = $SortContainer/Sort_Level#等级
|
||||||
|
|
||||||
|
#预添加常用的面板
|
||||||
|
@onready var main_game = get_node("/root/main")
|
||||||
|
@onready var land_panel = get_node("/root/main/UI/LandPanel")
|
||||||
|
@onready var crop_store_panel = get_node("/root/main/UI/PlayerBagPanel")
|
||||||
|
@onready var player_ranking_panel = get_node("/root/main/UI/PlayerRankingPanel")
|
||||||
|
@onready var player_bag_panel = get_node("/root/main/UI/PlayerBagPanel")
|
||||||
|
@onready var network_manager = get_node("/root/main/UI/TCPNetworkManager")
|
||||||
|
|
||||||
|
# 作物图片缓存(复用主游戏的缓存系统)
|
||||||
|
var crop_textures_cache : Dictionary = {}
|
||||||
|
var crop_frame_counts : Dictionary = {}
|
||||||
|
|
||||||
|
# 当前选择的地块索引,从MainGame获取
|
||||||
|
var selected_lot_index : int = -1
|
||||||
|
|
||||||
|
# 当前过滤和排序设置
|
||||||
|
var current_filter_quality = ""
|
||||||
|
var current_sort_key = ""
|
||||||
|
var current_sort_ascending = true
|
||||||
|
|
||||||
|
# 准备函数
|
||||||
|
func _ready():
|
||||||
|
# 连接按钮信号
|
||||||
|
_connect_buttons()
|
||||||
|
|
||||||
|
# 隐藏面板(初始默认隐藏)
|
||||||
|
self.hide()
|
||||||
|
|
||||||
|
# 连接所有按钮信号
|
||||||
|
func _connect_buttons():
|
||||||
|
# 关闭按钮
|
||||||
|
quit_button.pressed.connect(self._on_quit_button_pressed)
|
||||||
|
|
||||||
|
# 过滤按钮
|
||||||
|
sort_all_button.pressed.connect(func(): _filter_by_quality(""))
|
||||||
|
sort_common_button.pressed.connect(func(): _filter_by_quality("普通"))
|
||||||
|
sort_superior_button.pressed.connect(func(): _filter_by_quality("优良"))
|
||||||
|
sort_rare_button.pressed.connect(func(): _filter_by_quality("稀有"))
|
||||||
|
sort_epic_button.pressed.connect(func(): _filter_by_quality("史诗"))
|
||||||
|
sort_legendary_button.pressed.connect(func(): _filter_by_quality("传奇"))
|
||||||
|
|
||||||
|
# 排序按钮
|
||||||
|
sort_price_button.pressed.connect(func(): _sort_by("花费"))
|
||||||
|
sort_growtime_button.pressed.connect(func(): _sort_by("生长时间"))
|
||||||
|
sort_profit_button.pressed.connect(func(): _sort_by("收益"))
|
||||||
|
sort_level_button.pressed.connect(func(): _sort_by("等级"))
|
||||||
|
|
||||||
|
# 初始化玩家背包
|
||||||
|
func init_player_bag():
|
||||||
|
# 清空玩家背包格子
|
||||||
|
for child in player_bag_grid_container.get_children():
|
||||||
|
child.queue_free()
|
||||||
|
|
||||||
|
# 显示背包中的种子
|
||||||
|
update_player_bag_ui()
|
||||||
|
|
||||||
|
# 更新玩家背包UI
|
||||||
|
func update_player_bag_ui():
|
||||||
|
# 清空玩家背包格子
|
||||||
|
for child in player_bag_grid_container.get_children():
|
||||||
|
child.queue_free()
|
||||||
|
#print("更新背包UI,背包中物品数量:", main_game.player_bag.size())
|
||||||
|
|
||||||
|
# 应用过滤和排序
|
||||||
|
var filtered_seeds = _get_filtered_and_sorted_seeds()
|
||||||
|
|
||||||
|
# 为背包中的每个过滤后的种子创建按钮
|
||||||
|
for seed_item in filtered_seeds:
|
||||||
|
var crop_name = seed_item["name"]
|
||||||
|
var crop_quality = seed_item["quality"]
|
||||||
|
var crop_count = seed_item["count"]
|
||||||
|
#print("背包物品:", crop_name, " 数量:", crop_count)
|
||||||
|
# 创建种子按钮
|
||||||
|
var button = _create_crop_button(crop_name, crop_quality)
|
||||||
|
# 更新按钮文本显示数量
|
||||||
|
button.text = str(crop_quality + "-" + crop_name + "\n数量:" + str(crop_count))
|
||||||
|
|
||||||
|
# 根据是否处于访问模式连接不同的事件
|
||||||
|
if main_game.is_visiting_mode:
|
||||||
|
# 访问模式下,点击种子只显示信息,不能种植
|
||||||
|
button.pressed.connect(func(): _on_visit_seed_selected(crop_name, crop_count))
|
||||||
|
else:
|
||||||
|
# 正常模式下,连接种植事件
|
||||||
|
button.pressed.connect(func(): _on_bag_seed_selected(crop_name))
|
||||||
|
|
||||||
|
player_bag_grid_container.add_child(button)
|
||||||
|
|
||||||
|
# 获取过滤和排序后的种子列表
|
||||||
|
func _get_filtered_and_sorted_seeds():
|
||||||
|
var filtered_seeds = []
|
||||||
|
|
||||||
|
# 收集符合条件的种子
|
||||||
|
for seed_item in main_game.player_bag:
|
||||||
|
# 品质过滤
|
||||||
|
if current_filter_quality != "" and seed_item["quality"] != current_filter_quality:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 获取种子对应的作物数据
|
||||||
|
var crop_data = null
|
||||||
|
if main_game.can_planted_crop.has(seed_item["name"]):
|
||||||
|
crop_data = main_game.can_planted_crop[seed_item["name"]]
|
||||||
|
|
||||||
|
# 添加到过滤后的列表
|
||||||
|
filtered_seeds.append({
|
||||||
|
"name": seed_item["name"],
|
||||||
|
"quality": seed_item["quality"],
|
||||||
|
"count": seed_item["count"],
|
||||||
|
"data": crop_data
|
||||||
|
})
|
||||||
|
|
||||||
|
# 如果有排序条件且数据可用,进行排序
|
||||||
|
if current_sort_key != "":
|
||||||
|
filtered_seeds.sort_custom(Callable(self, "_sort_seed_items"))
|
||||||
|
|
||||||
|
return filtered_seeds
|
||||||
|
|
||||||
|
# 自定义排序函数
|
||||||
|
func _sort_seed_items(a, b):
|
||||||
|
# 检查是否有有效数据用于排序
|
||||||
|
if a["data"] == null or b["data"] == null:
|
||||||
|
# 如果某一项没有数据,将其排在后面
|
||||||
|
if a["data"] == null and b["data"] != null:
|
||||||
|
return false
|
||||||
|
if a["data"] != null and b["data"] == null:
|
||||||
|
return true
|
||||||
|
# 如果都没有数据,按名称排序
|
||||||
|
return a["name"] < b["name"]
|
||||||
|
|
||||||
|
# 确保排序键存在于数据中
|
||||||
|
if !a["data"].has(current_sort_key) or !b["data"].has(current_sort_key):
|
||||||
|
print("警告: 排序键 ", current_sort_key, " 在某些种子数据中不存在")
|
||||||
|
return false
|
||||||
|
|
||||||
|
# 执行排序
|
||||||
|
if current_sort_ascending:
|
||||||
|
return a["data"][current_sort_key] < b["data"][current_sort_key]
|
||||||
|
else:
|
||||||
|
return a["data"][current_sort_key] > b["data"][current_sort_key]
|
||||||
|
|
||||||
|
# 按品质过滤种子
|
||||||
|
func _filter_by_quality(quality: String):
|
||||||
|
current_filter_quality = quality
|
||||||
|
print("过滤种子,品质: " + (quality if quality != "" else "全部"))
|
||||||
|
update_player_bag_ui()
|
||||||
|
|
||||||
|
# 按指定键排序
|
||||||
|
func _sort_by(sort_key: String):
|
||||||
|
# 切换排序方向或设置新排序键
|
||||||
|
if current_sort_key == sort_key:
|
||||||
|
current_sort_ascending = !current_sort_ascending
|
||||||
|
else:
|
||||||
|
current_sort_key = sort_key
|
||||||
|
current_sort_ascending = true
|
||||||
|
|
||||||
|
print("排序种子,键: " + sort_key + ",升序: " + str(current_sort_ascending))
|
||||||
|
update_player_bag_ui()
|
||||||
|
|
||||||
|
# 创建作物按钮
|
||||||
|
func _create_crop_button(crop_name: String, crop_quality: String) -> Button:
|
||||||
|
# 根据品质选择相应的进度条
|
||||||
|
var button = null
|
||||||
|
#普通 Color.HONEYDEW#白色
|
||||||
|
#优良 Color.DODGER_BLUE#深蓝色
|
||||||
|
#稀有 Color.HOT_PINK#品红色
|
||||||
|
#史诗 Color.YELLOW#黄色
|
||||||
|
#传奇 Color.ORANGE_RED#红色
|
||||||
|
#空地 Color.GREEN#绿色
|
||||||
|
#未开垦 Color.WEB_GRAY#深褐色
|
||||||
|
match crop_quality:
|
||||||
|
"普通":
|
||||||
|
button = main_game.green_bar.duplicate()
|
||||||
|
"优良":
|
||||||
|
button = main_game.orange_bar.duplicate()
|
||||||
|
"稀有":
|
||||||
|
button = main_game.white_blue_bar.duplicate()
|
||||||
|
"史诗":
|
||||||
|
button = main_game.pink_bar.duplicate()
|
||||||
|
"传奇":
|
||||||
|
button = main_game.black_blue_bar.duplicate()
|
||||||
|
_: # 默认情况
|
||||||
|
button = main_game.green_bar.duplicate()
|
||||||
|
|
||||||
|
# 确保按钮可见并可点击
|
||||||
|
button.visible = true
|
||||||
|
button.disabled = false
|
||||||
|
button.focus_mode = Control.FOCUS_ALL
|
||||||
|
|
||||||
|
# 设置按钮文本
|
||||||
|
button.text = str(crop_quality + "-" + crop_name)
|
||||||
|
|
||||||
|
# 添加工具提示 (tooltip)
|
||||||
|
if main_game.can_planted_crop.has(crop_name):
|
||||||
|
var crop = main_game.can_planted_crop[crop_name]
|
||||||
|
|
||||||
|
# 将成熟时间从秒转换为天时分秒格式
|
||||||
|
var total_seconds = int(crop["生长时间"])
|
||||||
|
|
||||||
|
# 定义时间单位换算
|
||||||
|
var SECONDS_PER_MINUTE = 60
|
||||||
|
var SECONDS_PER_HOUR = 3600
|
||||||
|
var SECONDS_PER_DAY = 86400
|
||||||
|
|
||||||
|
# 计算各时间单位
|
||||||
|
var days = total_seconds / SECONDS_PER_DAY
|
||||||
|
total_seconds %= SECONDS_PER_DAY
|
||||||
|
|
||||||
|
var hours = total_seconds / SECONDS_PER_HOUR
|
||||||
|
total_seconds %= SECONDS_PER_HOUR
|
||||||
|
|
||||||
|
var minutes = total_seconds / SECONDS_PER_MINUTE
|
||||||
|
var seconds = total_seconds % SECONDS_PER_MINUTE
|
||||||
|
|
||||||
|
# 构建时间字符串(只显示有值的单位)
|
||||||
|
var time_str = ""
|
||||||
|
if days > 0:
|
||||||
|
time_str += str(days) + "天"
|
||||||
|
if hours > 0:
|
||||||
|
time_str += str(hours) + "小时"
|
||||||
|
if minutes > 0:
|
||||||
|
time_str += str(minutes) + "分钟"
|
||||||
|
if seconds > 0:
|
||||||
|
time_str += str(seconds) + "秒"
|
||||||
|
|
||||||
|
button.tooltip_text = str(
|
||||||
|
"作物: " + crop_name + "\n" +
|
||||||
|
"品质: " + crop_quality + "\n" +
|
||||||
|
"价格: " + str(crop["花费"]) + "元\n" +
|
||||||
|
"成熟时间: " + time_str + "\n" +
|
||||||
|
"收获收益: " + str(crop["收益"]) + "元\n" +
|
||||||
|
"需求等级: " + str(crop["等级"]) + "\n" +
|
||||||
|
"耐候性: " + str(crop["耐候性"]) + "\n" +
|
||||||
|
"经验: " + str(crop["经验"]) + "点\n" +
|
||||||
|
"描述: " + str(crop["描述"])
|
||||||
|
)
|
||||||
|
|
||||||
|
# 如果按钮有标题标签,设置标题
|
||||||
|
if button.has_node("Title"):
|
||||||
|
button.get_node("Title").text = crop_quality
|
||||||
|
match crop_quality:
|
||||||
|
"普通":
|
||||||
|
button.get_node("Title").modulate = Color.HONEYDEW#白色
|
||||||
|
"优良":
|
||||||
|
button.get_node("Title").modulate =Color.DODGER_BLUE#深蓝色
|
||||||
|
"稀有":
|
||||||
|
button.get_node("Title").modulate =Color.HOT_PINK#品红色
|
||||||
|
"史诗":
|
||||||
|
button.get_node("Title").modulate =Color.YELLOW#黄色
|
||||||
|
"传奇":
|
||||||
|
button.get_node("Title").modulate =Color.ORANGE_RED#红色
|
||||||
|
|
||||||
|
# 更新按钮的作物图片
|
||||||
|
_update_button_crop_image(button, crop_name)
|
||||||
|
|
||||||
|
return button
|
||||||
|
|
||||||
|
# 从背包中选择种子并种植
|
||||||
|
func _on_bag_seed_selected(crop_name):
|
||||||
|
# 检查是否处于访问模式
|
||||||
|
if main_game.is_visiting_mode:
|
||||||
|
Toast.show("访问模式下无法种植", Color.ORANGE, 2.0, 1.0)
|
||||||
|
return
|
||||||
|
|
||||||
|
# 从主场景获取当前选择的地块索引
|
||||||
|
selected_lot_index = main_game.selected_lot_index
|
||||||
|
|
||||||
|
if selected_lot_index != -1:
|
||||||
|
# 检查背包中是否有这个种子
|
||||||
|
var seed_index = -1
|
||||||
|
for i in range(len(main_game.player_bag)):
|
||||||
|
if main_game.player_bag[i]["name"] == crop_name:
|
||||||
|
seed_index = i
|
||||||
|
break
|
||||||
|
#print("选择种子:", crop_name, ",背包索引:", seed_index)
|
||||||
|
|
||||||
|
if seed_index != -1 and main_game.player_bag[seed_index]["count"] > 0:
|
||||||
|
# 种植种子并从背包中减少数量
|
||||||
|
_plant_crop_from_bag(selected_lot_index, crop_name, seed_index)
|
||||||
|
main_game.selected_lot_index = -1
|
||||||
|
self.hide()
|
||||||
|
|
||||||
|
# 访问模式下的种子点击处理
|
||||||
|
func _on_visit_seed_selected(crop_name, crop_count):
|
||||||
|
# 显示种子信息
|
||||||
|
var info_text = ""
|
||||||
|
|
||||||
|
if main_game.can_planted_crop.has(crop_name):
|
||||||
|
var crop = main_game.can_planted_crop[crop_name]
|
||||||
|
var quality = crop.get("品质", "未知")
|
||||||
|
var price = crop.get("花费", 0)
|
||||||
|
var grow_time = crop.get("生长时间", 0)
|
||||||
|
var profit = crop.get("收益", 0)
|
||||||
|
var level_req = crop.get("等级", 1)
|
||||||
|
|
||||||
|
# 将成熟时间转换为可读格式
|
||||||
|
var time_str = ""
|
||||||
|
var total_seconds = int(grow_time)
|
||||||
|
var hours = total_seconds / 3600
|
||||||
|
var minutes = (total_seconds % 3600) / 60
|
||||||
|
var seconds = total_seconds % 60
|
||||||
|
|
||||||
|
if hours > 0:
|
||||||
|
time_str += str(hours) + "小时"
|
||||||
|
if minutes > 0:
|
||||||
|
time_str += str(minutes) + "分钟"
|
||||||
|
if seconds > 0:
|
||||||
|
time_str += str(seconds) + "秒"
|
||||||
|
|
||||||
|
info_text = quality + "-" + crop_name + " (数量: " + str(crop_count) + ")\n"
|
||||||
|
info_text += "价格: " + str(price) + "元, 收益: " + str(profit) + "元\n"
|
||||||
|
info_text += "成熟时间: " + time_str + ", 需求等级: " + str(level_req)
|
||||||
|
else:
|
||||||
|
info_text = crop_name + " (数量: " + str(crop_count) + ")"
|
||||||
|
|
||||||
|
Toast.show(info_text, Color.CYAN, 3.0, 1.0)
|
||||||
|
print("查看种子信息: ", info_text)
|
||||||
|
|
||||||
|
# 从背包种植作物
|
||||||
|
func _plant_crop_from_bag(index, crop_name, seed_index):
|
||||||
|
var crop = main_game.can_planted_crop[crop_name]
|
||||||
|
|
||||||
|
# 检查是否有效的种子索引,防止越界访问
|
||||||
|
if seed_index < 0 or seed_index >= main_game.player_bag.size():
|
||||||
|
#print("错误:无效的种子索引 ", seed_index)
|
||||||
|
return
|
||||||
|
|
||||||
|
# 发送种植请求到服务器
|
||||||
|
|
||||||
|
if network_manager and network_manager.sendPlantCrop(index, crop_name):
|
||||||
|
# 种植请求已发送,等待服务器响应
|
||||||
|
Toast.show("正在发送种植请求", Color.YELLOW, 2.0, 1.0)
|
||||||
|
# 关闭背包面板
|
||||||
|
hide()
|
||||||
|
|
||||||
|
# 关闭面板
|
||||||
|
func _on_quit_button_pressed():
|
||||||
|
self.hide()
|
||||||
|
|
||||||
|
# 获取作物的最后一帧图片(用于背包显示)
|
||||||
|
func _get_crop_final_texture(crop_name: String) -> Texture2D:
|
||||||
|
"""
|
||||||
|
获取作物的最后一帧图片,用于背包显示
|
||||||
|
如果作物图片不存在,使用默认图片的最后一帧
|
||||||
|
"""
|
||||||
|
# 先尝试从主游戏的缓存中获取
|
||||||
|
if main_game and main_game.crop_textures_cache.has(crop_name):
|
||||||
|
var textures = main_game.crop_textures_cache[crop_name]
|
||||||
|
if textures.size() > 0:
|
||||||
|
return textures[textures.size() - 1] # 返回最后一帧
|
||||||
|
|
||||||
|
# 如果主游戏缓存中没有,自己加载
|
||||||
|
var textures = _load_crop_textures(crop_name)
|
||||||
|
if textures.size() > 0:
|
||||||
|
return textures[textures.size() - 1] # 返回最后一帧
|
||||||
|
|
||||||
|
return null
|
||||||
|
|
||||||
|
# 加载作物图片序列帧(复用主游戏的逻辑)
|
||||||
|
func _load_crop_textures(crop_name: String) -> Array:
|
||||||
|
"""
|
||||||
|
加载指定作物的所有序列帧图片
|
||||||
|
"""
|
||||||
|
if crop_textures_cache.has(crop_name):
|
||||||
|
return crop_textures_cache[crop_name]
|
||||||
|
|
||||||
|
var textures = []
|
||||||
|
var crop_path = "res://assets/作物/" + crop_name + "/"
|
||||||
|
var default_path = "res://assets/作物/默认/"
|
||||||
|
|
||||||
|
# 检查作物文件夹是否存在
|
||||||
|
if DirAccess.dir_exists_absolute(crop_path):
|
||||||
|
# 尝试加载作物的序列帧(从0开始)
|
||||||
|
var frame_index = 0
|
||||||
|
while true:
|
||||||
|
var texture_path = crop_path + str(frame_index) + ".png"
|
||||||
|
if ResourceLoader.exists(texture_path):
|
||||||
|
var texture = load(texture_path)
|
||||||
|
if texture:
|
||||||
|
textures.append(texture)
|
||||||
|
frame_index += 1
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
if textures.size() > 0:
|
||||||
|
print("背包加载作物 ", crop_name, " 的 ", textures.size(), " 帧图片")
|
||||||
|
else:
|
||||||
|
print("背包:作物 ", crop_name, " 文件夹存在但没有找到有效图片,使用默认图片")
|
||||||
|
textures = _load_default_textures()
|
||||||
|
else:
|
||||||
|
print("背包:作物 ", crop_name, " 的文件夹不存在,使用默认图片")
|
||||||
|
textures = _load_default_textures()
|
||||||
|
|
||||||
|
# 缓存结果
|
||||||
|
crop_textures_cache[crop_name] = textures
|
||||||
|
crop_frame_counts[crop_name] = textures.size()
|
||||||
|
|
||||||
|
return textures
|
||||||
|
|
||||||
|
# 加载默认图片
|
||||||
|
func _load_default_textures() -> Array:
|
||||||
|
"""
|
||||||
|
加载默认作物图片
|
||||||
|
"""
|
||||||
|
if crop_textures_cache.has("默认"):
|
||||||
|
return crop_textures_cache["默认"]
|
||||||
|
|
||||||
|
var textures = []
|
||||||
|
var default_path = "res://assets/作物/默认/"
|
||||||
|
|
||||||
|
# 尝试加载默认图片序列帧
|
||||||
|
var frame_index = 0
|
||||||
|
while true:
|
||||||
|
var texture_path = default_path + str(frame_index) + ".png"
|
||||||
|
if ResourceLoader.exists(texture_path):
|
||||||
|
var texture = load(texture_path)
|
||||||
|
if texture:
|
||||||
|
textures.append(texture)
|
||||||
|
frame_index += 1
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
# 如果没有找到序列帧,尝试加载单个默认图片
|
||||||
|
if textures.size() == 0:
|
||||||
|
var single_texture_path = default_path + "0.png"
|
||||||
|
if ResourceLoader.exists(single_texture_path):
|
||||||
|
var texture = load(single_texture_path)
|
||||||
|
if texture:
|
||||||
|
textures.append(texture)
|
||||||
|
|
||||||
|
# 缓存默认图片
|
||||||
|
crop_textures_cache["默认"] = textures
|
||||||
|
crop_frame_counts["默认"] = textures.size()
|
||||||
|
|
||||||
|
print("背包加载了 ", textures.size(), " 个默认作物图片")
|
||||||
|
return textures
|
||||||
|
|
||||||
|
# 更新按钮的作物图片
|
||||||
|
func _update_button_crop_image(button: Button, crop_name: String):
|
||||||
|
"""
|
||||||
|
更新按钮中的作物图片
|
||||||
|
"""
|
||||||
|
# 检查按钮是否有CropImage节点
|
||||||
|
var crop_image = button.get_node_or_null("CropImage")
|
||||||
|
if not crop_image:
|
||||||
|
print("背包按钮没有找到CropImage节点:", button.name)
|
||||||
|
return
|
||||||
|
|
||||||
|
# 获取作物的最后一帧图片
|
||||||
|
var texture = _get_crop_final_texture(crop_name)
|
||||||
|
|
||||||
|
if texture:
|
||||||
|
# CropImage是Sprite2D,直接设置texture属性
|
||||||
|
crop_image.texture = texture
|
||||||
|
crop_image.visible = true
|
||||||
|
print("背包更新作物图片:", crop_name)
|
||||||
|
else:
|
||||||
|
crop_image.visible = false
|
||||||
|
print("背包无法获取作物图片:", crop_name)
|
||||||
1
GUI/player_bag_panel.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://cgr332wsx63a8
|
||||||
71
GUI/player_ranking_item.tscn
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
[gd_scene load_steps=3 format=3 uid="uid://crd28qnymob7"]
|
||||||
|
|
||||||
|
[ext_resource type="Texture2D" uid="uid://c3vng0nal1wr8" path="res://assets/Test/g.png" id="1_sgoxp"]
|
||||||
|
[ext_resource type="Script" uid="uid://0d2j5m6j2ema" path="res://Components/HTTPTextureRect.gd" id="2_ky0k8"]
|
||||||
|
|
||||||
|
[node name="player_ranking_item" type="VBoxContainer"]
|
||||||
|
offset_right = 1152.0
|
||||||
|
offset_bottom = 82.0
|
||||||
|
|
||||||
|
[node name="HBox" type="HBoxContainer" parent="."]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="SerialNumber" type="Label" parent="HBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "1."
|
||||||
|
|
||||||
|
[node name="PlayerAvatar" type="TextureRect" parent="HBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
texture = ExtResource("1_sgoxp")
|
||||||
|
expand_mode = 3
|
||||||
|
script = ExtResource("2_ky0k8")
|
||||||
|
metadata/_custom_type_script = "uid://0d2j5m6j2ema"
|
||||||
|
|
||||||
|
[node name="PlayerName" type="Label" parent="HBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "树萌芽"
|
||||||
|
|
||||||
|
[node name="PlayerMoney" type="Label" parent="HBox"]
|
||||||
|
modulate = Color(1, 1, 0, 1)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "钱币:999"
|
||||||
|
|
||||||
|
[node name="SeedNum" type="Label" parent="HBox"]
|
||||||
|
modulate = Color(0, 1, 0, 1)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "种子数:999"
|
||||||
|
|
||||||
|
[node name="PlayerLevel" type="Label" parent="HBox"]
|
||||||
|
modulate = Color(0, 1, 1, 1)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "等级:999"
|
||||||
|
|
||||||
|
[node name="VisitButton" type="Button" parent="HBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "访问"
|
||||||
|
|
||||||
|
[node name="HBox2" type="HBoxContainer" parent="."]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="LastLoginTime" type="Label" parent="HBox2"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
theme_override_font_sizes/font_size = 20
|
||||||
|
text = "最后在线:2025年12时09分35秒"
|
||||||
|
|
||||||
|
[node name="OnlineTime" type="Label" parent="HBox2"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
theme_override_font_sizes/font_size = 20
|
||||||
|
text = "累计在线时长:99时60分60秒"
|
||||||
110
GUI/player_ranking_panel.gd
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
extends Panel
|
||||||
|
|
||||||
|
@onready var player_ranking_list : VBoxContainer = $Scroll/PlayerList
|
||||||
|
@onready var refresh_button : Button = $RefreshButton
|
||||||
|
@onready var quit_button : Button = $QuitButton
|
||||||
|
|
||||||
|
#预添加常用的面板
|
||||||
|
@onready var main_game = get_node("/root/main")
|
||||||
|
@onready var land_panel = get_node("/root/main/UI/LandPanel")
|
||||||
|
@onready var crop_store_panel = get_node("/root/main/UI/PlayerBagPanel")
|
||||||
|
@onready var player_ranking_panel = get_node("/root/main/UI/PlayerRankingPanel")
|
||||||
|
@onready var player_bag_panel = get_node("/root/main/UI/PlayerBagPanel")
|
||||||
|
@onready var network_manager = get_node("/root/main/UI/TCPNetworkManager")
|
||||||
|
|
||||||
|
#下面这是每个玩家要展示的信息,直接获取服务器玩家数据json文件来实现
|
||||||
|
#模板用于复制创建新的玩家条目
|
||||||
|
@onready var player_info_template : VBoxContainer = $Scroll/PlayerList/player_ranking_item
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
# 隐藏模板
|
||||||
|
player_info_template.visible = false
|
||||||
|
|
||||||
|
# 连接按钮信号
|
||||||
|
refresh_button.pressed.connect(_on_refresh_button_pressed)
|
||||||
|
quit_button.pressed.connect(_on_quit_button_pressed)
|
||||||
|
|
||||||
|
# 初始加载排行榜
|
||||||
|
request_player_rankings()
|
||||||
|
|
||||||
|
# 请求玩家排行榜数据
|
||||||
|
func request_player_rankings():
|
||||||
|
if network_manager:
|
||||||
|
network_manager.sendGetPlayerRankings()
|
||||||
|
|
||||||
|
# 处理玩家排行榜响应
|
||||||
|
func handle_player_rankings_response(data):
|
||||||
|
# 检查响应是否成功
|
||||||
|
if not data.get("success", false):
|
||||||
|
print("获取玩家排行榜失败:", data.get("message", "未知错误"))
|
||||||
|
return
|
||||||
|
|
||||||
|
# 清除现有的玩家条目(除了模板)
|
||||||
|
for child in player_ranking_list.get_children():
|
||||||
|
if child != player_info_template:
|
||||||
|
child.queue_free()
|
||||||
|
|
||||||
|
# 添加玩家条目
|
||||||
|
var players = data.get("players", [])
|
||||||
|
for player_data in players:
|
||||||
|
add_player_entry(player_data)
|
||||||
|
|
||||||
|
# 添加单个玩家条目
|
||||||
|
func add_player_entry(player_data):
|
||||||
|
# 复制模板
|
||||||
|
var player_entry = player_info_template.duplicate()
|
||||||
|
player_entry.visible = true
|
||||||
|
player_ranking_list.add_child(player_entry)
|
||||||
|
|
||||||
|
# 设置玩家信息
|
||||||
|
var player_name = player_entry.get_node("HBox/PlayerName")
|
||||||
|
var player_level = player_entry.get_node("HBox/PlayerLevel")
|
||||||
|
var player_money = player_entry.get_node("HBox/PlayerMoney")
|
||||||
|
var player_seed_num = player_entry.get_node("HBox/SeedNum")
|
||||||
|
var player_online_time = player_entry.get_node("HBox2/OnlineTime")
|
||||||
|
var player_last_login_time = player_entry.get_node("HBox2/LastLoginTime")
|
||||||
|
var player_avatar = player_entry.get_node("HBox/PlayerAvatar")
|
||||||
|
var visit_button = player_entry.get_node("HBox/VisitButton")
|
||||||
|
|
||||||
|
# 填充数据
|
||||||
|
var username = player_data.get("user_name", "未知")
|
||||||
|
var display_name = player_data.get("player_name", username)
|
||||||
|
player_name.text = display_name
|
||||||
|
player_level.text = "等级: " + str(player_data.get("level", 0))
|
||||||
|
player_money.text = "金币: " + str(player_data.get("money", 0))
|
||||||
|
player_seed_num.text = "种子: " + str(player_data.get("seed_count", 0))
|
||||||
|
player_online_time.text = "游玩时间: " + player_data.get("total_login_time", "0时0分0秒")
|
||||||
|
player_last_login_time.text = "最后登录: " + player_data.get("last_login_time", "未知")
|
||||||
|
|
||||||
|
# 尝试加载玩家头像(使用用户名/QQ号加载头像,而不是显示名)
|
||||||
|
if username.is_valid_int():
|
||||||
|
player_avatar.load_from_url("http://q1.qlogo.cn/g?b=qq&nk=" + username + "&s=100")
|
||||||
|
|
||||||
|
# 设置访问按钮
|
||||||
|
visit_button.pressed.connect(func(): _on_visit_player_pressed(username))
|
||||||
|
|
||||||
|
# 访问玩家按钮点击
|
||||||
|
func _on_visit_player_pressed(username):
|
||||||
|
print("访问玩家:", username)
|
||||||
|
|
||||||
|
# 发送访问玩家请求
|
||||||
|
if network_manager and network_manager.has_method("sendVisitPlayer"):
|
||||||
|
var success = network_manager.sendVisitPlayer(username)
|
||||||
|
if success:
|
||||||
|
print("已发送访问玩家请求:", username)
|
||||||
|
else:
|
||||||
|
print("发送访问玩家请求失败,网络未连接")
|
||||||
|
else:
|
||||||
|
print("网络管理器不可用")
|
||||||
|
|
||||||
|
# 刷新按钮点击
|
||||||
|
func _on_refresh_button_pressed():
|
||||||
|
request_player_rankings()
|
||||||
|
|
||||||
|
# 退出按钮点击
|
||||||
|
func _on_quit_button_pressed():
|
||||||
|
self.hide()
|
||||||
|
|
||||||
|
# 添加到main.gd中调用
|
||||||
|
func _handle_player_rankings_response(data):
|
||||||
|
handle_player_rankings_response(data)
|
||||||
1
GUI/player_ranking_panel.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://fk4q3x6uqydd
|
||||||
76
GlobalScript/GlobalFunctions.gd
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
extends Node
|
||||||
|
|
||||||
|
# 全局通用功能脚本
|
||||||
|
# 使用方法:首先在项目设置的自动加载中添加此脚本,然后在任何地方使用 GlobalFunctions.函数名() 调用
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
print("全局函数库已加载")
|
||||||
|
|
||||||
|
# 写入 TXT 文件
|
||||||
|
func write_txt_file(file_path: String, text: String, append: bool = false) -> void:
|
||||||
|
var file
|
||||||
|
if append == true:
|
||||||
|
file = FileAccess.open(file_path, FileAccess.READ_WRITE) # 追加模式
|
||||||
|
if file:
|
||||||
|
file.seek_end() # 移动光标到文件末尾
|
||||||
|
else:
|
||||||
|
file = FileAccess.open(file_path, FileAccess.WRITE) # 覆盖模式
|
||||||
|
if file:
|
||||||
|
file.store_string(text)
|
||||||
|
file.close()
|
||||||
|
if has_node("/root/ToastScript"):
|
||||||
|
get_node("/root/ToastScript").show("游戏已保存!", Color.GREEN, 5.0, 1.0)
|
||||||
|
else:
|
||||||
|
print("写入文件时打开失败: ", file_path)
|
||||||
|
if has_node("/root/ToastScript"):
|
||||||
|
get_node("/root/ToastScript").show("写入文件时打开失败!", Color.RED, 5.0, 1.0)
|
||||||
|
|
||||||
|
|
||||||
|
# 读取 TXT 文件
|
||||||
|
func read_txt_file(file_path: String) -> String:
|
||||||
|
var file = FileAccess.open(file_path, FileAccess.READ)
|
||||||
|
if file:
|
||||||
|
var text = file.get_as_text()
|
||||||
|
file.close()
|
||||||
|
return text
|
||||||
|
else:
|
||||||
|
print("打开文件失败: ", file_path)
|
||||||
|
return "false"
|
||||||
|
|
||||||
|
|
||||||
|
#生成随机数-用于作物随机死亡
|
||||||
|
func random_probability(probability: float) -> bool:
|
||||||
|
# 确保传入的概率值在 0 到 1 之间
|
||||||
|
if probability*0.001 < 0.0 or probability*0.001 > 1.0:
|
||||||
|
print("概率值必须在 0 和 1 之间")
|
||||||
|
return false
|
||||||
|
|
||||||
|
# 生成一个 0 到 1 之间的随机数
|
||||||
|
var random_value = randf()
|
||||||
|
|
||||||
|
# 如果随机数小于等于概率值,则返回 true
|
||||||
|
return random_value <= (probability*0.001)
|
||||||
|
|
||||||
|
|
||||||
|
# 格式化时间为可读字符串
|
||||||
|
func format_time(seconds: int) -> String:
|
||||||
|
var minutes = seconds / 60
|
||||||
|
seconds = seconds % 60
|
||||||
|
var hours = minutes / 60
|
||||||
|
minutes = minutes % 60
|
||||||
|
|
||||||
|
if hours > 0:
|
||||||
|
return "%02d:%02d:%02d" % [hours, minutes, seconds]
|
||||||
|
else:
|
||||||
|
return "%02d:%02d" % [minutes, seconds]
|
||||||
|
|
||||||
|
|
||||||
|
#双击切换UI事件-比如按一下打开再按一下关闭
|
||||||
|
func double_click_close(node):
|
||||||
|
if node.visible == false:
|
||||||
|
node.show()
|
||||||
|
pass
|
||||||
|
else :
|
||||||
|
node.hide()
|
||||||
|
pass
|
||||||
|
pass
|
||||||
1
GlobalScript/GlobalFunctions.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://bv4lf4v2c73pb
|
||||||
1
GlobalScript/GlobalVariables.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://co5rk48low4kq
|
||||||
1
GlobalScript/Toast.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://336lik63ehtt
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
[gd_scene load_steps=2 format=3 uid="uid://cvip7owyfmqav"]
|
[gd_scene load_steps=2 format=3 uid="uid://cvip7owyfmqav"]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://components/toast.gd" id="1_rdgmi"]
|
[ext_resource type="Script" uid="uid://336lik63ehtt" path="res://Components/Toast.gd" id="1_rdgmi"]
|
||||||
|
|
||||||
[node name="Toast" type="Node"]
|
[node name="Toast" type="Node"]
|
||||||
script = ExtResource("1_rdgmi")
|
script = ExtResource("1_rdgmi")
|
||||||
1256
MainGame.gd
1
MainGame.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://2pt11sfcaxf7
|
||||||
851
MainGame.tscn
@@ -1,343 +1,744 @@
|
|||||||
[gd_scene load_steps=11 format=3 uid="uid://dgh61dttaas5a"]
|
[gd_scene load_steps=25 format=3 uid="uid://dgh61dttaas5a"]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://MainGame.gd" id="1_v3yaj"]
|
[ext_resource type="Script" uid="uid://2pt11sfcaxf7" path="res://MainGame.gd" id="1_v3yaj"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://b4wi8yusmbbu8" path="res://assets/GUI/玩家昵称.png" id="2_ma1re"]
|
||||||
|
[ext_resource type="Script" uid="uid://cka0r4g8tbf0" path="res://GUI/login_panel.gd" id="2_mi4js"]
|
||||||
[ext_resource type="Texture2D" uid="uid://cbjtfrej7iq3x" path="res://assets/background2.jpg" id="2_psm5w"]
|
[ext_resource type="Texture2D" uid="uid://cbjtfrej7iq3x" path="res://assets/background2.jpg" id="2_psm5w"]
|
||||||
[ext_resource type="PackedScene" uid="uid://bkivlkirrx6u8" path="res://crop_item.tscn" id="3_isiom"]
|
[ext_resource type="Texture2D" uid="uid://clvhlo0mc3e7v" path="res://assets/GUI/农场名称.png" id="3_28psf"]
|
||||||
[ext_resource type="PackedScene" uid="uid://ffw2vjwnwvew" path="res://components/ToastShow.tscn" id="4_7kdbl"]
|
[ext_resource type="Script" uid="uid://bljtkxil64h14" path="res://GUI/land_panel.gd" id="3_401ut"]
|
||||||
[ext_resource type="Texture2D" uid="uid://b7yavo67sf4v7" path="res://assets/GUI/green_bar.tres" id="5_vtsi2"]
|
[ext_resource type="PackedScene" uid="uid://bkivlkirrx6u8" path="res://CopyItems/crop_item.tscn" id="3_isiom"]
|
||||||
[ext_resource type="Texture2D" uid="uid://d0h1s3wrx45a7" path="res://assets/GUI/white_blue_bar.tres" id="6_0sxhs"]
|
[ext_resource type="PackedScene" uid="uid://2m54c0f1ejir" path="res://CopyItems/green_crop.tscn" id="3_o8l48"]
|
||||||
[ext_resource type="Texture2D" uid="uid://bc0rsd5x4pxhn" path="res://assets/GUI/orange_bar.tres" id="7_2f3e4"]
|
[ext_resource type="Script" uid="uid://mtfp0ct42nrx" path="res://GUI/crop_store_panel.gd" id="3_qtrx8"]
|
||||||
[ext_resource type="Texture2D" uid="uid://beckne13egl8u" path="res://assets/GUI/pink_bar.tres" id="8_qyoht"]
|
[ext_resource type="Texture2D" uid="uid://cwloibftcmp76" path="res://assets/GUI/经验球.png" id="4_a6adi"]
|
||||||
[ext_resource type="Texture2D" uid="uid://bh73krj8mnojv" path="res://assets/GUI/black_blue_bar.tres" id="9_tunh0"]
|
[ext_resource type="Script" uid="uid://cgr332wsx63a8" path="res://GUI/player_bag_panel.gd" id="4_led80"]
|
||||||
[ext_resource type="Texture2D" uid="uid://b73vvxnp31xs4" path="res://assets/GUI/red_bar.tres" id="10_duo33"]
|
[ext_resource type="Script" uid="uid://fk4q3x6uqydd" path="res://GUI/player_ranking_panel.gd" id="4_yphxy"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://cm1e72lhd7j7v" path="res://CopyItems/black_blue_crop.tscn" id="5_o8l48"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://c87kujyuxnx2s" path="res://assets/GUI/等级.png" id="5_va67g"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://crd28qnymob7" path="res://GUI/player_ranking_item.tscn" id="5_yphxy"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://forqk66f354p" path="res://CopyItems/orange_crop.tscn" id="6_0v7qb"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://c6i00d35fnl12" path="res://assets/GUI/钱币.png" id="6_t0yo1"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://ckqy5yq2ltax6" path="res://assets/GUI/小提示.png" id="7_6brkw"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://cpxiaqh0y6a5d" path="res://Network/TCPNetworkManager.tscn" id="7_401ut"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://cmdoymcviv0ai" path="res://CopyItems/pink_crop.tscn" id="7_qtrx8"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://by5qcip8tel1f" path="res://assets/GUI/服务器连接状态.png" id="8_cgwad"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://d3ve4qeggsdqy" path="res://CopyItems/red_crop.tscn" id="8_led80"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://dagh3u5med30i" path="res://CopyItems/white_blue_crop.tscn" id="9_mi4js"]
|
||||||
|
[ext_resource type="Script" uid="uid://c7bxje0wvvgo4" path="res://game_camera.gd" id="10_o8l48"]
|
||||||
|
|
||||||
[node name="main" type="Node"]
|
[node name="main" type="Node"]
|
||||||
script = ExtResource("1_v3yaj")
|
script = ExtResource("1_v3yaj")
|
||||||
|
|
||||||
[node name="background" type="Sprite2D" parent="."]
|
[node name="UI" type="CanvasLayer" parent="."]
|
||||||
position = Vector2(590, 344)
|
|
||||||
scale = Vector2(0.658482, 0.666992)
|
|
||||||
texture = ExtResource("2_psm5w")
|
|
||||||
|
|
||||||
[node name="ItemList" type="ItemList" parent="."]
|
[node name="GUI" type="Control" parent="UI"]
|
||||||
visible = false
|
layout_mode = 3
|
||||||
offset_left = 80.0
|
anchors_preset = 0
|
||||||
offset_top = 156.0
|
|
||||||
offset_right = 1050.0
|
|
||||||
offset_bottom = 606.0
|
|
||||||
scale = Vector2(1.02983, 1.02983)
|
|
||||||
size_flags_horizontal = 3
|
|
||||||
size_flags_vertical = 3
|
|
||||||
theme_override_font_sizes/font_size = 20
|
|
||||||
allow_reselect = true
|
|
||||||
allow_rmb_select = true
|
|
||||||
auto_height = true
|
|
||||||
max_columns = 100
|
|
||||||
same_column_width = true
|
|
||||||
fixed_column_width = 100
|
|
||||||
icon_mode = 0
|
|
||||||
|
|
||||||
[node name="GridContainer" type="GridContainer" parent="."]
|
[node name="HBox2" type="HBoxContainer" parent="UI/GUI"]
|
||||||
custom_minimum_size = Vector2(100, 100)
|
layout_mode = 0
|
||||||
offset_top = 143.0
|
offset_top = 55.0
|
||||||
offset_right = 100.0
|
offset_right = 1400.0
|
||||||
offset_bottom = 243.0
|
offset_bottom = 111.0
|
||||||
columns = 10
|
|
||||||
|
|
||||||
[node name="CropItem" parent="." instance=ExtResource("3_isiom")]
|
[node name="player_name_image" type="TextureRect" parent="UI/GUI/HBox2"]
|
||||||
offset_left = -538.0
|
|
||||||
offset_top = 37.0
|
|
||||||
offset_right = -438.0
|
|
||||||
offset_bottom = 137.0
|
|
||||||
|
|
||||||
[node name="CropList" type="ItemList" parent="."]
|
|
||||||
visible = false
|
|
||||||
custom_minimum_size = Vector2(100, 100)
|
|
||||||
offset_left = 1.0
|
|
||||||
offset_top = 41.0
|
|
||||||
offset_right = 1152.0
|
|
||||||
offset_bottom = 141.0
|
|
||||||
size_flags_horizontal = 3
|
|
||||||
size_flags_vertical = 3
|
|
||||||
theme_override_font_sizes/font_size = 20
|
|
||||||
allow_reselect = true
|
|
||||||
allow_rmb_select = true
|
|
||||||
max_columns = 8
|
|
||||||
same_column_width = true
|
|
||||||
fixed_column_width = 222
|
|
||||||
icon_mode = 0
|
|
||||||
|
|
||||||
[node name="ToastShow" parent="." instance=ExtResource("4_7kdbl")]
|
|
||||||
visible = false
|
|
||||||
offset_top = 580.0
|
|
||||||
offset_bottom = 603.0
|
|
||||||
|
|
||||||
[node name="ToastShow2" parent="." instance=ExtResource("4_7kdbl")]
|
|
||||||
visible = false
|
|
||||||
offset_left = 1.0
|
|
||||||
offset_top = 41.0
|
|
||||||
offset_right = 65.0
|
|
||||||
offset_bottom = 64.0
|
|
||||||
|
|
||||||
[node name="ScrollContainer" type="ScrollContainer" parent="."]
|
|
||||||
offset_left = 1.0
|
|
||||||
offset_top = 42.0
|
|
||||||
offset_right = 2065.0
|
|
||||||
offset_bottom = 244.0
|
|
||||||
scale = Vector2(0.5, 0.5)
|
|
||||||
horizontal_scroll_mode = 0
|
|
||||||
|
|
||||||
[node name="Crop_GridContainer" type="GridContainer" parent="ScrollContainer"]
|
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
columns = 5
|
texture = ExtResource("2_ma1re")
|
||||||
|
expand_mode = 2
|
||||||
|
|
||||||
[node name="Copy_Nodes" type="Node2D" parent="."]
|
[node name="player_name" type="Label" parent="UI/GUI/HBox2"]
|
||||||
position = Vector2(-1000, 0)
|
modulate = Color(1, 0.670588, 0.490196, 1)
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "树萌芽"
|
||||||
|
|
||||||
[node name="Green" type="Button" parent="Copy_Nodes"]
|
[node name="farm_name_image" type="TextureRect" parent="UI/GUI/HBox2"]
|
||||||
offset_left = 1.0
|
layout_mode = 2
|
||||||
offset_top = 42.0
|
texture = ExtResource("3_28psf")
|
||||||
offset_right = 409.0
|
expand_mode = 3
|
||||||
offset_bottom = 138.0
|
|
||||||
theme_override_font_sizes/font_size = 40
|
|
||||||
text = "普通"
|
|
||||||
icon = ExtResource("5_vtsi2")
|
|
||||||
icon_alignment = 1
|
|
||||||
|
|
||||||
[node name="White_Blue" type="Button" parent="Copy_Nodes"]
|
[node name="farm_name" type="Label" parent="UI/GUI/HBox2"]
|
||||||
offset_left = -5.0
|
modulate = Color(1, 0.858824, 0.623529, 1)
|
||||||
offset_top = 148.0
|
layout_mode = 2
|
||||||
offset_right = 403.0
|
theme_override_font_sizes/font_size = 30
|
||||||
offset_bottom = 244.0
|
text = "树萌芽的农场"
|
||||||
theme_override_font_sizes/font_size = 40
|
|
||||||
text = "稀有"
|
|
||||||
icon = ExtResource("6_0sxhs")
|
|
||||||
icon_alignment = 1
|
|
||||||
|
|
||||||
[node name="Orange" type="Button" parent="Copy_Nodes"]
|
[node name="status_label_image" type="TextureRect" parent="UI/GUI/HBox2"]
|
||||||
offset_left = -6.0
|
layout_mode = 2
|
||||||
offset_top = 252.0
|
texture = ExtResource("8_cgwad")
|
||||||
offset_right = 402.0
|
expand_mode = 2
|
||||||
offset_bottom = 348.0
|
|
||||||
theme_override_font_sizes/font_size = 40
|
|
||||||
text = "优良"
|
|
||||||
icon = ExtResource("7_2f3e4")
|
|
||||||
icon_alignment = 1
|
|
||||||
|
|
||||||
[node name="Pink" type="Button" parent="Copy_Nodes"]
|
[node name="StatusLabel" type="Label" parent="UI/GUI/HBox2"]
|
||||||
offset_left = -16.0
|
layout_mode = 2
|
||||||
offset_top = 362.0
|
theme_override_font_sizes/font_size = 30
|
||||||
offset_right = 392.0
|
text = "服务器状态:正在检测中"
|
||||||
offset_bottom = 458.0
|
|
||||||
theme_override_font_sizes/font_size = 40
|
|
||||||
text = "史诗"
|
|
||||||
icon = ExtResource("8_qyoht")
|
|
||||||
icon_alignment = 1
|
|
||||||
|
|
||||||
[node name="Black_Blue" type="Button" parent="Copy_Nodes"]
|
[node name="HBox" type="HBoxContainer" parent="UI/GUI"]
|
||||||
offset_left = -5.0
|
layout_mode = 0
|
||||||
offset_top = 481.0
|
offset_right = 1400.0
|
||||||
offset_right = 403.0
|
offset_bottom = 56.0
|
||||||
offset_bottom = 577.0
|
|
||||||
theme_override_font_sizes/font_size = 40
|
|
||||||
text = "传奇"
|
|
||||||
icon = ExtResource("9_tunh0")
|
|
||||||
icon_alignment = 1
|
|
||||||
|
|
||||||
[node name="Red" type="Button" parent="Copy_Nodes"]
|
[node name="experience_image" type="TextureRect" parent="UI/GUI/HBox"]
|
||||||
offset_left = 7.0
|
layout_mode = 2
|
||||||
offset_top = 596.0
|
texture = ExtResource("4_a6adi")
|
||||||
offset_right = 415.0
|
expand_mode = 2
|
||||||
offset_bottom = 692.0
|
|
||||||
theme_override_font_sizes/font_size = 40
|
|
||||||
text = "神话"
|
|
||||||
icon = ExtResource("10_duo33")
|
|
||||||
icon_alignment = 1
|
|
||||||
|
|
||||||
[node name="HTTPRequest" type="HTTPRequest" parent="."]
|
[node name="experience" type="Label" parent="UI/GUI/HBox"]
|
||||||
|
modulate = Color(0, 1, 0, 1)
|
||||||
[node name="GUI" type="Node2D" parent="."]
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
[node name="level" type="Label" parent="GUI"]
|
|
||||||
offset_left = 632.0
|
|
||||||
offset_right = 773.0
|
|
||||||
offset_bottom = 42.0
|
|
||||||
theme_override_font_sizes/font_size = 20
|
|
||||||
text = "等级:100"
|
|
||||||
|
|
||||||
[node name="experience" type="Label" parent="GUI"]
|
|
||||||
offset_left = 334.0
|
|
||||||
offset_right = 475.0
|
|
||||||
offset_bottom = 42.0
|
|
||||||
theme_override_font_sizes/font_size = 20
|
|
||||||
text = "经验:999"
|
text = "经验:999"
|
||||||
|
|
||||||
[node name="LoginPanel" type="Panel" parent="GUI"]
|
[node name="level_image" type="TextureRect" parent="UI/GUI/HBox"]
|
||||||
offset_left = 379.0
|
|
||||||
offset_top = 156.0
|
|
||||||
offset_right = 765.0
|
|
||||||
offset_bottom = 444.0
|
|
||||||
|
|
||||||
[node name="VBox" type="VBoxContainer" parent="GUI/LoginPanel"]
|
|
||||||
layout_mode = 0
|
|
||||||
offset_top = 2.0
|
|
||||||
offset_right = 386.0
|
|
||||||
offset_bottom = 286.0
|
|
||||||
|
|
||||||
[node name="Title" type="Label" parent="GUI/LoginPanel/VBox"]
|
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
texture = ExtResource("5_va67g")
|
||||||
|
expand_mode = 2
|
||||||
|
|
||||||
|
[node name="level" type="Label" parent="UI/GUI/HBox"]
|
||||||
|
modulate = Color(0, 1, 1, 1)
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "等级:100"
|
||||||
|
|
||||||
|
[node name="money_image" type="TextureRect" parent="UI/GUI/HBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
texture = ExtResource("6_t0yo1")
|
||||||
|
expand_mode = 2
|
||||||
|
|
||||||
|
[node name="money" type="Label" parent="UI/GUI/HBox"]
|
||||||
|
modulate = Color(1, 1, 0, 1)
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "钱币:999"
|
||||||
|
|
||||||
|
[node name="tip_image" type="TextureRect" parent="UI/GUI/HBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
texture = ExtResource("7_6brkw")
|
||||||
|
expand_mode = 2
|
||||||
|
|
||||||
|
[node name="tip" type="Label" parent="UI/GUI/HBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_colors/font_color = Color(1, 0, 1, 1)
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "游戏小提示"
|
||||||
|
|
||||||
|
[node name="VBox" type="VBoxContainer" parent="UI/GUI"]
|
||||||
|
layout_mode = 0
|
||||||
|
offset_left = 5.0
|
||||||
|
offset_top = 522.0
|
||||||
|
offset_right = 253.0
|
||||||
|
offset_bottom = 719.0
|
||||||
|
alignment = 2
|
||||||
|
|
||||||
|
[node name="ReturnMyFarmButton" type="Button" parent="UI/GUI/VBox"]
|
||||||
|
visible = false
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 40
|
||||||
|
text = "返回我的农场"
|
||||||
|
|
||||||
|
[node name="OpenStoreButton" type="Button" parent="UI/GUI/VBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 40
|
||||||
|
text = "种子商店"
|
||||||
|
|
||||||
|
[node name="PlayerRankingButton" type="Button" parent="UI/GUI/VBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 40
|
||||||
|
text = "玩家排行榜"
|
||||||
|
|
||||||
|
[node name="LoginPanel" type="PanelContainer" parent="UI"]
|
||||||
|
offset_left = 486.0
|
||||||
|
offset_top = 143.0
|
||||||
|
offset_right = 989.0
|
||||||
|
offset_bottom = 616.0
|
||||||
|
script = ExtResource("2_mi4js")
|
||||||
|
|
||||||
|
[node name="VBox" type="VBoxContainer" parent="UI/LoginPanel"]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="Title" type="Label" parent="UI/LoginPanel/VBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 25
|
||||||
text = "登录/注册面板"
|
text = "登录/注册面板"
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
vertical_alignment = 1
|
vertical_alignment = 1
|
||||||
|
|
||||||
[node name="HBox" type="HBoxContainer" parent="GUI/LoginPanel/VBox"]
|
[node name="UserName" type="HBoxContainer" parent="UI/LoginPanel/VBox"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="Label" type="Label" parent="GUI/LoginPanel/VBox/HBox"]
|
[node name="Label" type="Label" parent="UI/LoginPanel/VBox/UserName"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 20
|
||||||
text = "账号"
|
text = "账号"
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
vertical_alignment = 1
|
vertical_alignment = 1
|
||||||
|
|
||||||
[node name="username_input" type="LineEdit" parent="GUI/LoginPanel/VBox/HBox"]
|
[node name="username_input" type="LineEdit" parent="UI/LoginPanel/VBox/UserName"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
|
theme_override_font_sizes/font_size = 20
|
||||||
placeholder_text = "请输入QQ号..."
|
placeholder_text = "请输入QQ号..."
|
||||||
metadata/_edit_use_anchors_ = true
|
metadata/_edit_use_anchors_ = true
|
||||||
|
|
||||||
[node name="HBox2" type="HBoxContainer" parent="GUI/LoginPanel/VBox"]
|
[node name="Password1" type="HBoxContainer" parent="UI/LoginPanel/VBox"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="Label2" type="Label" parent="GUI/LoginPanel/VBox/HBox2"]
|
[node name="Label2" type="Label" parent="UI/LoginPanel/VBox/Password1"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 20
|
||||||
text = "密码"
|
text = "密码"
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
vertical_alignment = 1
|
vertical_alignment = 1
|
||||||
|
|
||||||
[node name="password_input" type="LineEdit" parent="GUI/LoginPanel/VBox/HBox2"]
|
[node name="password_input" type="LineEdit" parent="UI/LoginPanel/VBox/Password1"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
|
theme_override_font_sizes/font_size = 20
|
||||||
placeholder_text = "请输入密码..."
|
placeholder_text = "请输入密码..."
|
||||||
|
|
||||||
[node name="Title3" type="Label" parent="GUI/LoginPanel/VBox"]
|
[node name="Title3" type="Label" parent="UI/LoginPanel/VBox"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 20
|
||||||
text = "以下为注册填写内容"
|
text = "以下为注册填写内容"
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
vertical_alignment = 1
|
vertical_alignment = 1
|
||||||
|
|
||||||
[node name="HBox5" type="HBoxContainer" parent="GUI/LoginPanel/VBox"]
|
[node name="Password2" type="HBoxContainer" parent="UI/LoginPanel/VBox"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="Label2" type="Label" parent="GUI/LoginPanel/VBox/HBox5"]
|
[node name="Label2" type="Label" parent="UI/LoginPanel/VBox/Password2"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "密码[选填]"
|
theme_override_font_sizes/font_size = 20
|
||||||
|
text = "确认密码"
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
vertical_alignment = 1
|
vertical_alignment = 1
|
||||||
|
|
||||||
[node name="password_input2" type="LineEdit" parent="GUI/LoginPanel/VBox/HBox5"]
|
[node name="password_input2" type="LineEdit" parent="UI/LoginPanel/VBox/Password2"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
|
theme_override_font_sizes/font_size = 20
|
||||||
placeholder_text = "请再次输入密码(登录不需要)..."
|
placeholder_text = "请再次输入密码(登录不需要)..."
|
||||||
|
|
||||||
[node name="HBox3" type="HBoxContainer" parent="GUI/LoginPanel/VBox"]
|
[node name="VerificationCode" type="HBoxContainer" parent="UI/LoginPanel/VBox"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="Label" type="Label" parent="GUI/LoginPanel/VBox/HBox3"]
|
[node name="Label" type="Label" parent="UI/LoginPanel/VBox/VerificationCode"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "名称[选填]"
|
theme_override_font_sizes/font_size = 20
|
||||||
|
text = "验证码"
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
vertical_alignment = 1
|
vertical_alignment = 1
|
||||||
|
|
||||||
[node name="farmname_input" type="LineEdit" parent="GUI/LoginPanel/VBox/HBox3"]
|
[node name="verificationcode_input" type="LineEdit" parent="UI/LoginPanel/VBox/VerificationCode"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
|
theme_override_font_sizes/font_size = 20
|
||||||
|
placeholder_text = "请输入您的QQ邮箱收到的验证码..."
|
||||||
|
metadata/_edit_use_anchors_ = true
|
||||||
|
|
||||||
|
[node name="SendButton" type="Button" parent="UI/LoginPanel/VBox/VerificationCode"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 20
|
||||||
|
text = "发送验证码"
|
||||||
|
|
||||||
|
[node name="PlayerName" type="HBoxContainer" parent="UI/LoginPanel/VBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="Label2" type="Label" parent="UI/LoginPanel/VBox/PlayerName"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 20
|
||||||
|
text = "玩家昵称"
|
||||||
|
horizontal_alignment = 1
|
||||||
|
vertical_alignment = 1
|
||||||
|
|
||||||
|
[node name="playername_input" type="LineEdit" parent="UI/LoginPanel/VBox/PlayerName"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
theme_override_font_sizes/font_size = 20
|
||||||
|
placeholder_text = "请输入您的玩家昵称..."
|
||||||
|
|
||||||
|
[node name="FarmName" type="HBoxContainer" parent="UI/LoginPanel/VBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="Label" type="Label" parent="UI/LoginPanel/VBox/FarmName"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 20
|
||||||
|
text = "农场名称"
|
||||||
|
horizontal_alignment = 1
|
||||||
|
vertical_alignment = 1
|
||||||
|
|
||||||
|
[node name="farmname_input" type="LineEdit" parent="UI/LoginPanel/VBox/FarmName"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
theme_override_font_sizes/font_size = 20
|
||||||
placeholder_text = "请输入您的农场名称(登录不需要)..."
|
placeholder_text = "请输入您的农场名称(登录不需要)..."
|
||||||
metadata/_edit_use_anchors_ = true
|
metadata/_edit_use_anchors_ = true
|
||||||
|
|
||||||
[node name="HBox4" type="HBoxContainer" parent="GUI/LoginPanel/VBox"]
|
[node name="LoginRegister" type="HBoxContainer" parent="UI/LoginPanel/VBox"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="login_button" type="Button" parent="GUI/LoginPanel/VBox/HBox4"]
|
[node name="login_button" type="Button" parent="UI/LoginPanel/VBox/LoginRegister"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
|
theme_override_font_sizes/font_size = 20
|
||||||
text = "登录"
|
text = "登录"
|
||||||
|
|
||||||
[node name="register_button" type="Button" parent="GUI/LoginPanel/VBox/HBox4"]
|
[node name="register_button" type="Button" parent="UI/LoginPanel/VBox/LoginRegister"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
|
theme_override_font_sizes/font_size = 20
|
||||||
text = "注册"
|
text = "注册"
|
||||||
|
|
||||||
[node name="Title2" type="Label" parent="GUI/LoginPanel/VBox"]
|
[node name="Note" type="Label" parent="UI/LoginPanel/VBox"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "注意:账号请输入您的QQ号,方便匹配QQ好友,
|
theme_override_font_sizes/font_size = 20
|
||||||
账号,密码请不要和您的QQ密码相同,防止信息泄露"
|
text = "注意:账号请直接使用您的QQ号,系统会直接向您的QQ
|
||||||
|
邮箱发送一串验证码进行注册验证,密码请设置的复杂一
|
||||||
|
点,以免被暴力破解"
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
vertical_alignment = 1
|
vertical_alignment = 1
|
||||||
|
|
||||||
[node name="tip" type="Label" parent="GUI"]
|
[node name="status_label" type="Label" parent="UI/LoginPanel/VBox"]
|
||||||
offset_left = 878.0
|
layout_mode = 2
|
||||||
offset_right = 1150.0
|
|
||||||
offset_bottom = 42.0
|
|
||||||
theme_override_colors/font_color = Color(1, 0, 1, 1)
|
|
||||||
theme_override_font_sizes/font_size = 20
|
theme_override_font_sizes/font_size = 20
|
||||||
text = "游戏自动保存剩余【10】秒"
|
text = "连接状态"
|
||||||
|
horizontal_alignment = 1
|
||||||
|
|
||||||
[node name="money" type="Label" parent="GUI"]
|
[node name="LandPanel" type="Panel" parent="UI"]
|
||||||
offset_left = 1.0
|
|
||||||
offset_right = 142.0
|
|
||||||
offset_bottom = 42.0
|
|
||||||
theme_override_font_sizes/font_size = 20
|
|
||||||
text = "钱币:999"
|
|
||||||
|
|
||||||
[node name="Land_Panel" type="PanelContainer" parent="GUI"]
|
|
||||||
visible = false
|
visible = false
|
||||||
offset_right = 556.0
|
offset_left = 475.0
|
||||||
offset_bottom = 58.0
|
offset_top = 145.0
|
||||||
|
offset_right = 991.0
|
||||||
|
offset_bottom = 616.0
|
||||||
|
script = ExtResource("3_401ut")
|
||||||
|
|
||||||
[node name="VBox" type="VBoxContainer" parent="GUI/Land_Panel"]
|
[node name="Quit_Button" type="Button" parent="UI/LandPanel"]
|
||||||
layout_mode = 2
|
layout_mode = 0
|
||||||
|
offset_left = 465.0
|
||||||
|
offset_right = 515.0
|
||||||
|
offset_bottom = 50.0
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "X"
|
||||||
|
|
||||||
[node name="Title" type="Label" parent="GUI/Land_Panel/VBox"]
|
[node name="HBox" type="HBoxContainer" parent="UI/LandPanel"]
|
||||||
|
layout_mode = 0
|
||||||
|
offset_right = 40.0
|
||||||
|
offset_bottom = 40.0
|
||||||
|
|
||||||
|
[node name="Title" type="Label" parent="UI/LandPanel"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
offset_right = 516.0
|
||||||
|
offset_bottom = 42.0
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
text = "土地面板"
|
text = "土地面板"
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
vertical_alignment = 1
|
vertical_alignment = 1
|
||||||
|
|
||||||
[node name="HBox" type="HBoxContainer" parent="GUI/Land_Panel/VBox"]
|
[node name="Grid" type="GridContainer" parent="UI/LandPanel"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
offset_top = 46.0
|
||||||
|
offset_right = 500.0
|
||||||
|
offset_bottom = 210.0
|
||||||
|
columns = 5
|
||||||
|
|
||||||
[node name="Dig_button" type="Button" parent="GUI/Land_Panel/VBox/HBox"]
|
[node name="Dig_Button" type="Button" parent="UI/LandPanel/Grid"]
|
||||||
|
modulate = Color(1, 0.419608, 0.352941, 1)
|
||||||
|
custom_minimum_size = Vector2(100, 100)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
text = "开垦"
|
text = "开垦"
|
||||||
|
|
||||||
[node name="Description" type="Label" parent="GUI/Land_Panel/VBox/HBox"]
|
[node name="Plant_Button" type="Button" parent="UI/LandPanel/Grid"]
|
||||||
|
modulate = Color(1, 0.682353, 0, 1)
|
||||||
|
custom_minimum_size = Vector2(100, 100)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "土地需要开垦才能种植,开垦所需费用随玩家已开垦土地数量增多而增多"
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "种植"
|
||||||
|
|
||||||
[node name="HBox2" type="HBoxContainer" parent="GUI/Land_Panel/VBox"]
|
[node name="Remove_Button" type="Button" parent="UI/LandPanel/Grid"]
|
||||||
visible = false
|
visible = false
|
||||||
|
modulate = Color(1, 1, 0, 1)
|
||||||
|
custom_minimum_size = Vector2(100, 100)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "铲除"
|
||||||
|
|
||||||
[node name="Button" type="Button" parent="GUI/Land_Panel/VBox/HBox2"]
|
[node name="Harvest_Button" type="Button" parent="UI/LandPanel/Grid"]
|
||||||
|
modulate = Color(0.223529, 1, 0.290196, 1)
|
||||||
|
custom_minimum_size = Vector2(100, 100)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "收获"
|
||||||
|
|
||||||
|
[node name="Water_Button" type="Button" parent="UI/LandPanel/Grid"]
|
||||||
|
visible = false
|
||||||
|
modulate = Color(0, 1, 1, 1)
|
||||||
|
custom_minimum_size = Vector2(100, 100)
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "浇水"
|
||||||
|
|
||||||
|
[node name="Fertilize_Button" type="Button" parent="UI/LandPanel/Grid"]
|
||||||
|
visible = false
|
||||||
|
modulate = Color(0.592157, 0.337255, 1, 1)
|
||||||
|
custom_minimum_size = Vector2(80, 80)
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "施肥"
|
||||||
|
|
||||||
|
[node name="Upgrade_Button" type="Button" parent="UI/LandPanel/Grid"]
|
||||||
|
visible = false
|
||||||
|
modulate = Color(0.0784314, 0.470588, 1, 1)
|
||||||
|
custom_minimum_size = Vector2(100, 100)
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
text = "升级"
|
text = "升级"
|
||||||
|
|
||||||
[node name="Description" type="Label" parent="GUI/Land_Panel/VBox/HBox2"]
|
[node name="PlayerRankingPanel" type="Panel" parent="UI"]
|
||||||
layout_mode = 2
|
|
||||||
text = "升级描述"
|
|
||||||
|
|
||||||
[node name="HBox3" type="HBoxContainer" parent="GUI/Land_Panel/VBox"]
|
|
||||||
visible = false
|
visible = false
|
||||||
|
offset_right = 1400.0
|
||||||
|
offset_bottom = 720.0
|
||||||
|
script = ExtResource("4_yphxy")
|
||||||
|
|
||||||
|
[node name="Scroll" type="ScrollContainer" parent="UI/PlayerRankingPanel"]
|
||||||
|
layout_mode = 2
|
||||||
|
offset_top = 68.0
|
||||||
|
offset_right = 1400.0
|
||||||
|
offset_bottom = 720.0
|
||||||
|
size_flags_vertical = 3
|
||||||
|
|
||||||
|
[node name="PlayerList" type="VBoxContainer" parent="UI/PlayerRankingPanel/Scroll"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
size_flags_vertical = 3
|
||||||
|
|
||||||
|
[node name="player_ranking_item" parent="UI/PlayerRankingPanel/Scroll/PlayerList" instance=ExtResource("5_yphxy")]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="Button" type="Button" parent="GUI/Land_Panel/VBox/HBox3"]
|
[node name="Title" type="RichTextLabel" parent="UI/PlayerRankingPanel"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "恢复"
|
offset_left = 2.0
|
||||||
|
offset_right = 1395.0
|
||||||
|
offset_bottom = 56.0
|
||||||
|
size_flags_vertical = 3
|
||||||
|
theme_override_font_sizes/normal_font_size = 40
|
||||||
|
bbcode_enabled = true
|
||||||
|
text = "玩家排行榜"
|
||||||
|
horizontal_alignment = 1
|
||||||
|
|
||||||
[node name="Description" type="Label" parent="GUI/Land_Panel/VBox/HBox3"]
|
[node name="QuitButton" type="Button" parent="UI/PlayerRankingPanel"]
|
||||||
|
custom_minimum_size = Vector2(55, 55)
|
||||||
|
layout_mode = 0
|
||||||
|
offset_left = 1338.0
|
||||||
|
offset_top = 3.0
|
||||||
|
offset_right = 1395.0
|
||||||
|
offset_bottom = 60.0
|
||||||
|
theme_override_font_sizes/font_size = 35
|
||||||
|
text = "X"
|
||||||
|
|
||||||
|
[node name="RefreshButton" type="Button" parent="UI/PlayerRankingPanel"]
|
||||||
|
custom_minimum_size = Vector2(55, 55)
|
||||||
|
layout_mode = 0
|
||||||
|
offset_left = 1.0
|
||||||
|
offset_top = 3.0
|
||||||
|
offset_right = 79.0
|
||||||
|
offset_bottom = 60.0
|
||||||
|
theme_override_font_sizes/font_size = 35
|
||||||
|
text = "刷新"
|
||||||
|
|
||||||
|
[node name="CropStorePanel" type="Panel" parent="UI"]
|
||||||
|
visible = false
|
||||||
|
offset_left = 1.0
|
||||||
|
offset_right = 1400.0
|
||||||
|
offset_bottom = 720.0
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
size_flags_vertical = 3
|
||||||
|
script = ExtResource("3_qtrx8")
|
||||||
|
|
||||||
|
[node name="SortContainer" type="HBoxContainer" parent="UI/CropStorePanel"]
|
||||||
|
layout_mode = 0
|
||||||
|
offset_top = 58.0
|
||||||
|
offset_right = 1399.0
|
||||||
|
offset_bottom = 121.0
|
||||||
|
|
||||||
|
[node name="SortLabel" type="Label" parent="UI/CropStorePanel/SortContainer"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "恢复描述"
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "排序:"
|
||||||
|
|
||||||
[connection signal="pressed" from="GUI/LoginPanel/VBox/HBox4/login_button" to="." method="_on_login_button_pressed"]
|
[node name="Sort_All" type="Button" parent="UI/CropStorePanel/SortContainer"]
|
||||||
[connection signal="pressed" from="GUI/LoginPanel/VBox/HBox4/register_button" to="." method="_on_register_button_pressed"]
|
layout_mode = 2
|
||||||
[connection signal="pressed" from="GUI/Land_Panel/VBox/HBox/Dig_button" to="." method="_on_dig_button_pressed"]
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "全部"
|
||||||
|
|
||||||
|
[node name="Sort_Common" type="Button" parent="UI/CropStorePanel/SortContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "普通"
|
||||||
|
|
||||||
|
[node name="Sort_Superior" type="Button" parent="UI/CropStorePanel/SortContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "优良"
|
||||||
|
|
||||||
|
[node name="Sort_Rare" type="Button" parent="UI/CropStorePanel/SortContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "稀有"
|
||||||
|
|
||||||
|
[node name="Sort_Epic" type="Button" parent="UI/CropStorePanel/SortContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "史诗"
|
||||||
|
|
||||||
|
[node name="Sort_Legendary" type="Button" parent="UI/CropStorePanel/SortContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "传奇"
|
||||||
|
|
||||||
|
[node name="Sort_Price" type="Button" parent="UI/CropStorePanel/SortContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "按价格"
|
||||||
|
|
||||||
|
[node name="Sort_GrowTime" type="Button" parent="UI/CropStorePanel/SortContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "按生长时间"
|
||||||
|
|
||||||
|
[node name="Sort_Profit" type="Button" parent="UI/CropStorePanel/SortContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "按收益"
|
||||||
|
|
||||||
|
[node name="Sort_Level" type="Button" parent="UI/CropStorePanel/SortContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "按等级"
|
||||||
|
|
||||||
|
[node name="ScrollContainer" type="ScrollContainer" parent="UI/CropStorePanel"]
|
||||||
|
layout_mode = 2
|
||||||
|
offset_top = 134.0
|
||||||
|
offset_right = 4657.0
|
||||||
|
offset_bottom = 2087.0
|
||||||
|
scale = Vector2(0.3, 0.3)
|
||||||
|
size_flags_vertical = 3
|
||||||
|
horizontal_scroll_mode = 0
|
||||||
|
|
||||||
|
[node name="Crop_Grid" type="GridContainer" parent="UI/CropStorePanel/ScrollContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
size_flags_vertical = 3
|
||||||
|
columns = 11
|
||||||
|
|
||||||
|
[node name="Title" type="Label" parent="UI/CropStorePanel"]
|
||||||
|
layout_mode = 2
|
||||||
|
offset_right = 1397.0
|
||||||
|
offset_bottom = 55.0
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
theme_override_font_sizes/font_size = 40
|
||||||
|
text = "种子商店"
|
||||||
|
horizontal_alignment = 1
|
||||||
|
|
||||||
|
[node name="QuitButton" type="Button" parent="UI/CropStorePanel"]
|
||||||
|
custom_minimum_size = Vector2(60, 60)
|
||||||
|
layout_mode = 2
|
||||||
|
offset_left = 1337.0
|
||||||
|
offset_top = 3.0
|
||||||
|
offset_right = 1397.0
|
||||||
|
offset_bottom = 66.0
|
||||||
|
theme_override_font_sizes/font_size = 40
|
||||||
|
text = "X"
|
||||||
|
|
||||||
|
[node name="PlayerBagPanel" type="Panel" parent="UI"]
|
||||||
|
visible = false
|
||||||
|
offset_left = 1.0
|
||||||
|
offset_right = 1398.0
|
||||||
|
offset_bottom = 720.0
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
size_flags_vertical = 3
|
||||||
|
script = ExtResource("4_led80")
|
||||||
|
|
||||||
|
[node name="SortContainer" type="HBoxContainer" parent="UI/PlayerBagPanel"]
|
||||||
|
layout_mode = 0
|
||||||
|
offset_top = 58.0
|
||||||
|
offset_right = 1397.0
|
||||||
|
offset_bottom = 121.0
|
||||||
|
|
||||||
|
[node name="SortLabel" type="Label" parent="UI/PlayerBagPanel/SortContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "排序:"
|
||||||
|
|
||||||
|
[node name="Sort_All" type="Button" parent="UI/PlayerBagPanel/SortContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "全部"
|
||||||
|
|
||||||
|
[node name="Sort_Common" type="Button" parent="UI/PlayerBagPanel/SortContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "普通"
|
||||||
|
|
||||||
|
[node name="Sort_Superior" type="Button" parent="UI/PlayerBagPanel/SortContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "优良"
|
||||||
|
|
||||||
|
[node name="Sort_Rare" type="Button" parent="UI/PlayerBagPanel/SortContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "稀有"
|
||||||
|
|
||||||
|
[node name="Sort_Epic" type="Button" parent="UI/PlayerBagPanel/SortContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "史诗"
|
||||||
|
|
||||||
|
[node name="Sort_Legendary" type="Button" parent="UI/PlayerBagPanel/SortContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "传奇"
|
||||||
|
|
||||||
|
[node name="Sort_Price" type="Button" parent="UI/PlayerBagPanel/SortContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "按价格"
|
||||||
|
|
||||||
|
[node name="Sort_GrowTime" type="Button" parent="UI/PlayerBagPanel/SortContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "按生长时间"
|
||||||
|
|
||||||
|
[node name="Sort_Profit" type="Button" parent="UI/PlayerBagPanel/SortContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "按收益"
|
||||||
|
|
||||||
|
[node name="Sort_Level" type="Button" parent="UI/PlayerBagPanel/SortContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "按等级"
|
||||||
|
|
||||||
|
[node name="ScrollContainer" type="ScrollContainer" parent="UI/PlayerBagPanel"]
|
||||||
|
layout_mode = 2
|
||||||
|
offset_top = 128.0
|
||||||
|
offset_right = 4657.0
|
||||||
|
offset_bottom = 2101.0
|
||||||
|
scale = Vector2(0.3, 0.3)
|
||||||
|
size_flags_vertical = 3
|
||||||
|
horizontal_scroll_mode = 0
|
||||||
|
|
||||||
|
[node name="Bag_Grid" type="GridContainer" parent="UI/PlayerBagPanel/ScrollContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
size_flags_vertical = 3
|
||||||
|
columns = 11
|
||||||
|
|
||||||
|
[node name="Title" type="Label" parent="UI/PlayerBagPanel"]
|
||||||
|
layout_mode = 2
|
||||||
|
offset_right = 1276.0
|
||||||
|
offset_bottom = 55.0
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
theme_override_font_sizes/font_size = 40
|
||||||
|
text = "玩家背包"
|
||||||
|
horizontal_alignment = 1
|
||||||
|
|
||||||
|
[node name="QuitButton" type="Button" parent="UI/PlayerBagPanel"]
|
||||||
|
custom_minimum_size = Vector2(60, 60)
|
||||||
|
layout_mode = 2
|
||||||
|
offset_left = 1337.0
|
||||||
|
offset_right = 1397.0
|
||||||
|
offset_bottom = 63.0
|
||||||
|
theme_override_font_sizes/font_size = 40
|
||||||
|
text = "X"
|
||||||
|
|
||||||
|
[node name="TCPNetworkManager" parent="UI" instance=ExtResource("7_401ut")]
|
||||||
|
visible = false
|
||||||
|
offset_left = 2.00012
|
||||||
|
offset_top = 143.0
|
||||||
|
offset_right = 2.00012
|
||||||
|
offset_bottom = 143.0
|
||||||
|
scale = Vector2(0.7, 0.7)
|
||||||
|
|
||||||
|
[node name="BackgroundUI" type="CanvasLayer" parent="."]
|
||||||
|
layer = -1
|
||||||
|
|
||||||
|
[node name="background" type="Sprite2D" parent="BackgroundUI"]
|
||||||
|
modulate = Color(1, 1, 1, 0.372549)
|
||||||
|
show_behind_parent = true
|
||||||
|
z_index = -100
|
||||||
|
z_as_relative = false
|
||||||
|
position = Vector2(702.875, 360)
|
||||||
|
scale = Vector2(0.779157, 0.703125)
|
||||||
|
texture = ExtResource("2_psm5w")
|
||||||
|
|
||||||
|
[node name="GridContainer" type="GridContainer" parent="."]
|
||||||
|
z_as_relative = false
|
||||||
|
custom_minimum_size = Vector2(100, 100)
|
||||||
|
offset_top = 3.0
|
||||||
|
offset_right = 1400.0
|
||||||
|
offset_bottom = 720.0
|
||||||
|
columns = 10
|
||||||
|
|
||||||
|
[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="GreenCrop" parent="CopyNodes" instance=ExtResource("3_o8l48")]
|
||||||
|
offset_left = 16.0
|
||||||
|
offset_top = 143.0
|
||||||
|
offset_right = 416.0
|
||||||
|
offset_bottom = 543.0
|
||||||
|
|
||||||
|
[node name="BlackBlueCrop" parent="CopyNodes" instance=ExtResource("5_o8l48")]
|
||||||
|
offset_left = -24.0
|
||||||
|
offset_top = -27.0
|
||||||
|
offset_right = 376.0
|
||||||
|
offset_bottom = 373.0
|
||||||
|
|
||||||
|
[node name="OrangeCrop" parent="CopyNodes" instance=ExtResource("6_0v7qb")]
|
||||||
|
offset_left = 57.0
|
||||||
|
offset_top = -184.0
|
||||||
|
offset_right = 457.0
|
||||||
|
offset_bottom = 216.0
|
||||||
|
|
||||||
|
[node name="PinkCrop" parent="CopyNodes" instance=ExtResource("7_qtrx8")]
|
||||||
|
offset_left = -149.0
|
||||||
|
offset_top = -185.0
|
||||||
|
offset_right = 251.0
|
||||||
|
offset_bottom = 215.0
|
||||||
|
|
||||||
|
[node name="RedCrop" parent="CopyNodes" instance=ExtResource("8_led80")]
|
||||||
|
offset_left = -311.0
|
||||||
|
offset_top = -18.0
|
||||||
|
offset_right = 89.0
|
||||||
|
offset_bottom = 382.0
|
||||||
|
|
||||||
|
[node name="WhiteBlueCrop" parent="CopyNodes" instance=ExtResource("9_mi4js")]
|
||||||
|
offset_left = -212.0
|
||||||
|
offset_top = 134.0
|
||||||
|
offset_right = 188.0
|
||||||
|
offset_bottom = 534.0
|
||||||
|
|
||||||
|
[node name="GameCamera" type="Camera2D" parent="."]
|
||||||
|
anchor_mode = 0
|
||||||
|
position_smoothing_enabled = true
|
||||||
|
script = ExtResource("10_o8l48")
|
||||||
|
bounds_enabled = true
|
||||||
|
bounds_min = Vector2(-500, -500)
|
||||||
|
bounds_max = Vector2(500, 500)
|
||||||
|
|
||||||
|
[node name="GameManager" type="Node" parent="."]
|
||||||
|
|
||||||
|
[connection signal="pressed" from="UI/GUI/VBox/ReturnMyFarmButton" to="." method="_on_return_my_farm_button_pressed"]
|
||||||
|
[connection signal="pressed" from="UI/GUI/VBox/OpenStoreButton" to="." method="_on_open_store_button_pressed"]
|
||||||
|
[connection signal="pressed" from="UI/GUI/VBox/PlayerRankingButton" to="." method="_on_player_ranking_button_pressed"]
|
||||||
|
[connection signal="pressed" from="UI/CropStorePanel/QuitButton" to="." method="_on_quit_button_pressed"]
|
||||||
|
[connection signal="pressed" from="UI/PlayerBagPanel/QuitButton" to="." method="_on_quit_button_pressed"]
|
||||||
|
|||||||
157
Network/TCPClient.gd
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
extends Node
|
||||||
|
#一个基本的TCP客户端API
|
||||||
|
class_name TCPClient
|
||||||
|
|
||||||
|
signal connected_to_server#连接到服务器信号
|
||||||
|
signal connection_failed#连接失败信号
|
||||||
|
signal connection_closed#连接关闭信号
|
||||||
|
signal data_received(data)#收到数据信号
|
||||||
|
|
||||||
|
var tcp: StreamPeerTCP = StreamPeerTCP.new()
|
||||||
|
var host: String = "127.0.0.1"
|
||||||
|
var port: int = 4040
|
||||||
|
var is_connected: bool = false
|
||||||
|
var auto_reconnect: bool = true
|
||||||
|
var reconnect_delay: float = 2.0
|
||||||
|
|
||||||
|
# 缓冲区管理
|
||||||
|
var buffer = ""
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
pass
|
||||||
|
|
||||||
|
func _process(_delta):
|
||||||
|
# 更新连接状态
|
||||||
|
tcp.poll()
|
||||||
|
_update_connection_status()
|
||||||
|
_check_for_data()
|
||||||
|
|
||||||
|
|
||||||
|
func connect_to_server(custom_host = null, custom_port = null):
|
||||||
|
if custom_host != null:
|
||||||
|
host = custom_host
|
||||||
|
if custom_port != null:
|
||||||
|
port = custom_port
|
||||||
|
|
||||||
|
if tcp.get_status() != StreamPeerTCP.STATUS_CONNECTED:
|
||||||
|
tcp.disconnect_from_host()
|
||||||
|
print("连接到服务器: %s:%s" % [host, port])
|
||||||
|
var error = tcp.connect_to_host(host, port)
|
||||||
|
if error != OK:
|
||||||
|
print("连接错误: %s" % error)
|
||||||
|
emit_signal("connection_failed")
|
||||||
|
|
||||||
|
|
||||||
|
func disconnect_from_server():
|
||||||
|
tcp.disconnect_from_host()
|
||||||
|
is_connected = false
|
||||||
|
emit_signal("connection_closed")
|
||||||
|
|
||||||
|
|
||||||
|
func _update_connection_status():
|
||||||
|
var status = tcp.get_status()
|
||||||
|
|
||||||
|
match status:
|
||||||
|
StreamPeerTCP.STATUS_NONE:
|
||||||
|
if is_connected:
|
||||||
|
is_connected = false
|
||||||
|
print("连接已断开")
|
||||||
|
emit_signal("connection_closed")
|
||||||
|
|
||||||
|
if auto_reconnect:
|
||||||
|
var timer = get_tree().create_timer(reconnect_delay)
|
||||||
|
await timer.timeout
|
||||||
|
connect_to_server()
|
||||||
|
|
||||||
|
StreamPeerTCP.STATUS_CONNECTING:
|
||||||
|
pass
|
||||||
|
|
||||||
|
StreamPeerTCP.STATUS_CONNECTED:
|
||||||
|
if not is_connected:
|
||||||
|
is_connected = true
|
||||||
|
tcp.set_no_delay(true) # 禁用Nagle算法提高响应速度
|
||||||
|
print("已连接到服务器")
|
||||||
|
emit_signal("connected_to_server")
|
||||||
|
|
||||||
|
StreamPeerTCP.STATUS_ERROR:
|
||||||
|
is_connected = false
|
||||||
|
print("连接错误")
|
||||||
|
emit_signal("connection_failed")
|
||||||
|
|
||||||
|
if auto_reconnect:
|
||||||
|
var timer = get_tree().create_timer(reconnect_delay)
|
||||||
|
await timer.timeout
|
||||||
|
connect_to_server()
|
||||||
|
|
||||||
|
|
||||||
|
func _check_for_data():
|
||||||
|
if tcp.get_status() == StreamPeerTCP.STATUS_CONNECTED and tcp.get_available_bytes() > 0:
|
||||||
|
var bytes = tcp.get_available_bytes()
|
||||||
|
var data = tcp.get_utf8_string(bytes)
|
||||||
|
|
||||||
|
# 将数据添加到缓冲区进行处理
|
||||||
|
buffer += data
|
||||||
|
_process_buffer()
|
||||||
|
|
||||||
|
|
||||||
|
func _process_buffer():
|
||||||
|
# 处理缓冲区中的JSON消息
|
||||||
|
# 假设每条消息以换行符结尾
|
||||||
|
while "\n" in buffer:
|
||||||
|
var message_end = buffer.find("\n")
|
||||||
|
var message_text = buffer.substr(0, message_end)
|
||||||
|
buffer = buffer.substr(message_end + 1)
|
||||||
|
|
||||||
|
# 处理JSON数据
|
||||||
|
if message_text.strip_edges() != "":
|
||||||
|
var json = JSON.new()
|
||||||
|
var error = json.parse(message_text)
|
||||||
|
|
||||||
|
if error == OK:
|
||||||
|
var data = json.get_data()
|
||||||
|
#print("收到JSON数据: ", data)
|
||||||
|
emit_signal("data_received", data)
|
||||||
|
else:
|
||||||
|
# 非JSON格式数据,直接传递
|
||||||
|
#print("收到原始数据: ", message_text)
|
||||||
|
emit_signal("data_received", message_text)
|
||||||
|
|
||||||
|
func send_data(data):
|
||||||
|
if not is_connected:
|
||||||
|
print("未连接,无法发送数据")
|
||||||
|
return false
|
||||||
|
|
||||||
|
var message: String
|
||||||
|
|
||||||
|
# 如果是字典/数组,转换为JSON
|
||||||
|
if typeof(data) == TYPE_DICTIONARY or typeof(data) == TYPE_ARRAY:
|
||||||
|
message = JSON.stringify(data) + "\n"
|
||||||
|
else:
|
||||||
|
# 否则简单转换为字符串
|
||||||
|
message = str(data) + "\n"
|
||||||
|
|
||||||
|
var result = tcp.put_data(message.to_utf8_buffer())
|
||||||
|
return result == OK
|
||||||
|
|
||||||
|
func is_client_connected() -> bool:
|
||||||
|
return is_connected
|
||||||
|
|
||||||
|
# 示例: 如何使用此客户端
|
||||||
|
#
|
||||||
|
# func _ready():
|
||||||
|
# var client = TCPClient.new()
|
||||||
|
# add_child(client)
|
||||||
|
#
|
||||||
|
# client.connected_to_server.connect(_on_connected)
|
||||||
|
# client.connection_failed.connect(_on_connection_failed)
|
||||||
|
# client.connection_closed.connect(_on_connection_closed)
|
||||||
|
# client.data_received.connect(_on_data_received)
|
||||||
|
#
|
||||||
|
# client.connect_to_server("127.0.0.1", 9000)
|
||||||
|
#
|
||||||
|
# func _on_connected():
|
||||||
|
# print("已连接")
|
||||||
|
# client.send_data({"type": "greeting", "content": "Hello Server!"})
|
||||||
|
#
|
||||||
|
# func _on_data_received(data):
|
||||||
|
# print("收到数据: ", data)
|
||||||
1
Network/TCPClient.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://cylhhkh8ooxcu
|
||||||
377
Network/TCPNetworkManager.gd
Normal file
@@ -0,0 +1,377 @@
|
|||||||
|
extends Panel
|
||||||
|
|
||||||
|
# TCP客户端演示
|
||||||
|
# 这个脚本展示如何在UI中使用TCPClient类
|
||||||
|
|
||||||
|
# UI组件引用
|
||||||
|
@onready var status_label = $StatusLabel
|
||||||
|
@onready var message_input = $MessageInput
|
||||||
|
@onready var send_button = $SendButton
|
||||||
|
@onready var response_label = $Scroll/ResponseLabel
|
||||||
|
@onready var connection_button = $ConnectionButton
|
||||||
|
@onready var login_panel = $"/root/main/UI/LoginPanel"
|
||||||
|
@onready var main_game = get_node("/root/main")
|
||||||
|
|
||||||
|
# TCP客户端
|
||||||
|
var client: TCPClient = TCPClient.new()
|
||||||
|
|
||||||
|
# 服务器配置 - 支持多个服务器地址
|
||||||
|
var server_configs = [
|
||||||
|
#{"host": "127.0.0.1", "port": 4040, "name": "本地服务器"},
|
||||||
|
#{"host": "192.168.1.110", "port": 4040, "name": "局域网服务器"},
|
||||||
|
{"host": "47.108.90.0", "port": 4040, "name": "公网服务器"}#成都内网穿透
|
||||||
|
]
|
||||||
|
|
||||||
|
var current_server_index = 0
|
||||||
|
var auto_retry = true
|
||||||
|
var retry_delay = 3.0
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
# 创建TCP客户端实例
|
||||||
|
self.add_child(client)
|
||||||
|
|
||||||
|
# 连接信号
|
||||||
|
client.connected_to_server.connect(_on_connected)
|
||||||
|
client.connection_failed.connect(_on_connection_failed)
|
||||||
|
client.connection_closed.connect(_on_connection_closed)
|
||||||
|
client.data_received.connect(_on_data_received)
|
||||||
|
|
||||||
|
# 连接按钮事件
|
||||||
|
connection_button.pressed.connect(_on_connection_button_pressed)
|
||||||
|
send_button.pressed.connect(_on_send_button_pressed)
|
||||||
|
|
||||||
|
# 初始设置
|
||||||
|
status_label.text = "未连接"
|
||||||
|
response_label.text = "等待响应..."
|
||||||
|
connection_button.text = "连接"
|
||||||
|
|
||||||
|
func _on_connected():
|
||||||
|
status_label.text = "已连接"
|
||||||
|
status_label.modulate = Color.GREEN
|
||||||
|
connection_button.text = "断开"
|
||||||
|
|
||||||
|
# 发送连接成功消息
|
||||||
|
client.send_data({
|
||||||
|
"type": "greeting",
|
||||||
|
"content": "你好,服务器!"
|
||||||
|
})
|
||||||
|
|
||||||
|
# 连接成功后立即请求作物数据
|
||||||
|
print("连接成功,正在请求最新作物数据...")
|
||||||
|
sendGetCropData()
|
||||||
|
|
||||||
|
func _on_connection_failed():
|
||||||
|
status_label.text = "连接失败"
|
||||||
|
status_label.modulate = Color.RED
|
||||||
|
connection_button.text = "连接"
|
||||||
|
|
||||||
|
# 自动尝试下一个服务器
|
||||||
|
if auto_retry:
|
||||||
|
try_next_server()
|
||||||
|
|
||||||
|
func _on_connection_closed():
|
||||||
|
status_label.text = "连接断开"
|
||||||
|
status_label.modulate = Color.RED
|
||||||
|
connection_button.text = "连接"
|
||||||
|
|
||||||
|
# 自动重连当前服务器
|
||||||
|
if auto_retry:
|
||||||
|
var timer = get_tree().create_timer(retry_delay)
|
||||||
|
await timer.timeout
|
||||||
|
if not client.is_client_connected():
|
||||||
|
_on_connection_button_pressed()
|
||||||
|
|
||||||
|
func _on_data_received(data):
|
||||||
|
# 根据数据类型处理数据
|
||||||
|
response_label.text = "收到: %s" % JSON.stringify(data)
|
||||||
|
match typeof(data):
|
||||||
|
|
||||||
|
TYPE_DICTIONARY:
|
||||||
|
# 处理JSON对象
|
||||||
|
var message_type = data.get("type", "")
|
||||||
|
|
||||||
|
match message_type:
|
||||||
|
"ping":
|
||||||
|
return
|
||||||
|
"response":
|
||||||
|
# 显示服务器响应
|
||||||
|
if data.has("original"):
|
||||||
|
var original = data.get("original", {})
|
||||||
|
return
|
||||||
|
"login_response":
|
||||||
|
# 处理登录响应
|
||||||
|
var status = data.get("status", "")
|
||||||
|
var message = data.get("message", "")
|
||||||
|
var player_data = data.get("player_data", {})
|
||||||
|
if login_panel:
|
||||||
|
# 调用登录面板的响应处理方法
|
||||||
|
login_panel._on_login_response_received(status == "success", message, player_data)
|
||||||
|
"register_response":
|
||||||
|
# 处理注册响应
|
||||||
|
var status = data.get("status", "")
|
||||||
|
var message = data.get("message", "")
|
||||||
|
if login_panel:
|
||||||
|
# 调用登录面板的响应处理方法
|
||||||
|
login_panel._on_register_response_received(status == "success", message)
|
||||||
|
"verification_code_response":
|
||||||
|
# 处理验证码发送响应
|
||||||
|
var success = data.get("success", false)
|
||||||
|
var message = data.get("message", "")
|
||||||
|
if login_panel:
|
||||||
|
# 调用登录面板的验证码响应处理方法
|
||||||
|
login_panel._on_verification_code_response(success, message)
|
||||||
|
"verify_code_response":
|
||||||
|
# 处理验证码验证响应
|
||||||
|
var success = data.get("success", false)
|
||||||
|
var message = data.get("message", "")
|
||||||
|
if login_panel:
|
||||||
|
# 调用登录面板的验证码验证响应处理方法
|
||||||
|
login_panel._on_verify_code_response(success, message)
|
||||||
|
"crop_update":
|
||||||
|
# 处理作物生长更新
|
||||||
|
if main_game:
|
||||||
|
main_game._handle_crop_update(data)
|
||||||
|
"action_response":
|
||||||
|
# 处理玩家动作响应
|
||||||
|
if main_game:
|
||||||
|
main_game._handle_action_response(data)
|
||||||
|
"play_time_response":
|
||||||
|
# 处理玩家游玩时间响应
|
||||||
|
if main_game and main_game.has_method("_handle_play_time_response"):
|
||||||
|
main_game._handle_play_time_response(data)
|
||||||
|
"player_rankings_response":
|
||||||
|
# 处理玩家排行榜响应
|
||||||
|
if main_game and main_game.has_method("_handle_player_rankings_response"):
|
||||||
|
main_game._handle_player_rankings_response(data)
|
||||||
|
"crop_data_response":
|
||||||
|
# 处理作物数据响应
|
||||||
|
if main_game and main_game.has_method("_handle_crop_data_response"):
|
||||||
|
main_game._handle_crop_data_response(data)
|
||||||
|
"visit_player_response":
|
||||||
|
# 处理访问玩家响应
|
||||||
|
if main_game and main_game.has_method("_handle_visit_player_response"):
|
||||||
|
main_game._handle_visit_player_response(data)
|
||||||
|
"return_my_farm_response":
|
||||||
|
# 处理返回自己农场响应
|
||||||
|
if main_game and main_game.has_method("_handle_return_my_farm_response"):
|
||||||
|
main_game._handle_return_my_farm_response(data)
|
||||||
|
_:
|
||||||
|
# 显示其他类型的消息
|
||||||
|
return
|
||||||
|
_:
|
||||||
|
# 处理非JSON数据
|
||||||
|
return
|
||||||
|
|
||||||
|
func _on_connection_button_pressed():
|
||||||
|
if client.is_client_connected():
|
||||||
|
# 断开连接
|
||||||
|
client.disconnect_from_server()
|
||||||
|
else:
|
||||||
|
# 连接服务器
|
||||||
|
status_label.text = "正在连接..."
|
||||||
|
client.connect_to_server(server_configs[current_server_index]["host"], server_configs[current_server_index]["port"])
|
||||||
|
|
||||||
|
func _on_send_button_pressed():
|
||||||
|
if not client.is_client_connected():
|
||||||
|
status_label.text = "未连接,无法发送"
|
||||||
|
return
|
||||||
|
|
||||||
|
# 获取输入文本
|
||||||
|
var text = message_input.text.strip_edges()
|
||||||
|
if text.is_empty():
|
||||||
|
return
|
||||||
|
|
||||||
|
# 发送消息
|
||||||
|
client.send_data({
|
||||||
|
"type": "message",
|
||||||
|
"content": text,
|
||||||
|
"timestamp": Time.get_unix_time_from_system()
|
||||||
|
})
|
||||||
|
|
||||||
|
# 清空输入
|
||||||
|
message_input.text = ""
|
||||||
|
|
||||||
|
#发送登录信息
|
||||||
|
func sendLoginInfo(username, password):
|
||||||
|
client.send_data({
|
||||||
|
"type": "login",
|
||||||
|
"username": username,
|
||||||
|
"password": password
|
||||||
|
})
|
||||||
|
|
||||||
|
#发送注册信息
|
||||||
|
func sendRegisterInfo(username, password, farmname, player_name="", verification_code=""):
|
||||||
|
client.send_data({
|
||||||
|
"type": "register",
|
||||||
|
"username": username,
|
||||||
|
"password": password,
|
||||||
|
"farm_name": farmname,
|
||||||
|
"player_name": player_name,
|
||||||
|
"verification_code": verification_code
|
||||||
|
})
|
||||||
|
|
||||||
|
#发送收获作物信息
|
||||||
|
func sendHarvestCrop(lot_index):
|
||||||
|
if not client.is_client_connected():
|
||||||
|
return false
|
||||||
|
|
||||||
|
client.send_data({
|
||||||
|
"type": "harvest_crop",
|
||||||
|
"lot_index": lot_index,
|
||||||
|
"timestamp": Time.get_unix_time_from_system()
|
||||||
|
})
|
||||||
|
return true
|
||||||
|
|
||||||
|
#发送种植作物信息
|
||||||
|
func sendPlantCrop(lot_index, crop_name):
|
||||||
|
if not client.is_client_connected():
|
||||||
|
return false
|
||||||
|
|
||||||
|
client.send_data({
|
||||||
|
"type": "plant_crop",
|
||||||
|
"lot_index": lot_index,
|
||||||
|
"crop_name": crop_name,
|
||||||
|
"timestamp": Time.get_unix_time_from_system()
|
||||||
|
})
|
||||||
|
return true
|
||||||
|
|
||||||
|
#发送开垦土地信息
|
||||||
|
func sendDigGround(lot_index):
|
||||||
|
if not client.is_client_connected():
|
||||||
|
return false
|
||||||
|
|
||||||
|
client.send_data({
|
||||||
|
"type": "dig_ground",
|
||||||
|
"lot_index": lot_index,
|
||||||
|
"timestamp": Time.get_unix_time_from_system()
|
||||||
|
})
|
||||||
|
return true
|
||||||
|
|
||||||
|
#发送购买种子信息
|
||||||
|
func sendBuySeed(crop_name):
|
||||||
|
if not client.is_client_connected():
|
||||||
|
return false
|
||||||
|
|
||||||
|
client.send_data({
|
||||||
|
"type": "buy_seed",
|
||||||
|
"crop_name": crop_name,
|
||||||
|
"timestamp": Time.get_unix_time_from_system()
|
||||||
|
})
|
||||||
|
return true
|
||||||
|
|
||||||
|
#发送获取游玩时间请求
|
||||||
|
func sendGetPlayTime():
|
||||||
|
if not client.is_client_connected():
|
||||||
|
return false
|
||||||
|
|
||||||
|
client.send_data({
|
||||||
|
"type": "get_play_time"
|
||||||
|
})
|
||||||
|
return true
|
||||||
|
|
||||||
|
#发送更新游玩时间请求
|
||||||
|
func sendUpdatePlayTime():
|
||||||
|
if not client.is_client_connected():
|
||||||
|
return false
|
||||||
|
|
||||||
|
client.send_data({
|
||||||
|
"type": "update_play_time"
|
||||||
|
})
|
||||||
|
return true
|
||||||
|
|
||||||
|
#发送获取玩家排行榜请求
|
||||||
|
func sendGetPlayerRankings():
|
||||||
|
if not client.is_client_connected():
|
||||||
|
return false
|
||||||
|
|
||||||
|
client.send_data({
|
||||||
|
"type": "request_player_rankings"
|
||||||
|
})
|
||||||
|
return true
|
||||||
|
|
||||||
|
#发送验证码请求
|
||||||
|
func sendVerificationCodeRequest(qq_number):
|
||||||
|
if not client.is_client_connected():
|
||||||
|
return false
|
||||||
|
|
||||||
|
client.send_data({
|
||||||
|
"type": "request_verification_code",
|
||||||
|
"qq_number": qq_number,
|
||||||
|
"timestamp": Time.get_unix_time_from_system()
|
||||||
|
})
|
||||||
|
return true
|
||||||
|
|
||||||
|
#发送验证码验证
|
||||||
|
func sendVerifyCode(qq_number, code):
|
||||||
|
if not client.is_client_connected():
|
||||||
|
return false
|
||||||
|
|
||||||
|
client.send_data({
|
||||||
|
"type": "verify_code",
|
||||||
|
"qq_number": qq_number,
|
||||||
|
"code": code,
|
||||||
|
"timestamp": Time.get_unix_time_from_system()
|
||||||
|
})
|
||||||
|
return true
|
||||||
|
|
||||||
|
#发送获取作物数据请求
|
||||||
|
func sendGetCropData():
|
||||||
|
if not client.is_client_connected():
|
||||||
|
return false
|
||||||
|
|
||||||
|
client.send_data({
|
||||||
|
"type": "request_crop_data"
|
||||||
|
})
|
||||||
|
return true
|
||||||
|
|
||||||
|
#发送访问玩家请求
|
||||||
|
func sendVisitPlayer(target_username):
|
||||||
|
if not client.is_client_connected():
|
||||||
|
return false
|
||||||
|
|
||||||
|
client.send_data({
|
||||||
|
"type": "visit_player",
|
||||||
|
"target_username": target_username,
|
||||||
|
"timestamp": Time.get_unix_time_from_system()
|
||||||
|
})
|
||||||
|
return true
|
||||||
|
|
||||||
|
#发送返回自己农场请求
|
||||||
|
func sendReturnMyFarm():
|
||||||
|
if not client.is_client_connected():
|
||||||
|
return false
|
||||||
|
|
||||||
|
client.send_data({
|
||||||
|
"type": "return_my_farm",
|
||||||
|
"timestamp": Time.get_unix_time_from_system()
|
||||||
|
})
|
||||||
|
return true
|
||||||
|
|
||||||
|
#检查是否连接到服务器
|
||||||
|
func is_connected_to_server():
|
||||||
|
return client.is_client_connected()
|
||||||
|
|
||||||
|
# 尝试连接下一个服务器
|
||||||
|
func try_next_server():
|
||||||
|
current_server_index = (current_server_index + 1) % server_configs.size()
|
||||||
|
var config = server_configs[current_server_index]
|
||||||
|
|
||||||
|
status_label.text = "尝试连接 " + config["name"]
|
||||||
|
print("尝试连接服务器: ", config["name"], " (", config["host"], ":", config["port"], ")")
|
||||||
|
|
||||||
|
var timer = get_tree().create_timer(retry_delay)
|
||||||
|
await timer.timeout
|
||||||
|
|
||||||
|
if not client.is_client_connected():
|
||||||
|
client.connect_to_server(config["host"], config["port"])
|
||||||
|
|
||||||
|
# 检查网络连接状态
|
||||||
|
func check_network_status():
|
||||||
|
# 检查设备是否有网络连接
|
||||||
|
if OS.get_name() == "Android":
|
||||||
|
# 在Android上检查网络状态
|
||||||
|
status_label.text = "检查网络状态..."
|
||||||
|
|
||||||
|
# 尝试连接到当前配置的服务器
|
||||||
|
if not client.is_client_connected():
|
||||||
|
_on_connection_button_pressed()
|
||||||
1
Network/TCPNetworkManager.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://q1f3tubmdsrk
|
||||||
69
Network/TCPNetworkManager.tscn
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
[gd_scene load_steps=2 format=3 uid="uid://cpxiaqh0y6a5d"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://q1f3tubmdsrk" path="res://Network/TCPNetworkManager.gd" id="1_tfd57"]
|
||||||
|
|
||||||
|
[node name="TCPNetworkManager" type="Panel"]
|
||||||
|
script = ExtResource("1_tfd57")
|
||||||
|
|
||||||
|
[node name="Scroll" type="ScrollContainer" parent="."]
|
||||||
|
layout_mode = 0
|
||||||
|
offset_left = 1.0
|
||||||
|
offset_top = 142.0
|
||||||
|
offset_right = 401.0
|
||||||
|
offset_bottom = 647.0
|
||||||
|
horizontal_scroll_mode = 0
|
||||||
|
|
||||||
|
[node name="ResponseLabel" type="Label" parent="Scroll"]
|
||||||
|
custom_minimum_size = Vector2(400, 500)
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_colors/font_outline_color = Color(1, 1, 1, 1)
|
||||||
|
theme_override_font_sizes/font_size = 20
|
||||||
|
text = "回应"
|
||||||
|
autowrap_mode = 3
|
||||||
|
|
||||||
|
[node name="Title" type="Label" parent="."]
|
||||||
|
layout_mode = 0
|
||||||
|
offset_right = 400.0
|
||||||
|
offset_bottom = 42.0
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "TCP网络调试面板"
|
||||||
|
horizontal_alignment = 1
|
||||||
|
|
||||||
|
[node name="ColorRect" type="ColorRect" parent="."]
|
||||||
|
layout_mode = 0
|
||||||
|
offset_right = 400.0
|
||||||
|
offset_bottom = 647.0
|
||||||
|
color = Color(0.104753, 0.146763, 0.23013, 0.427451)
|
||||||
|
|
||||||
|
[node name="StatusLabel" type="Label" parent="."]
|
||||||
|
layout_mode = 0
|
||||||
|
offset_top = 100.0
|
||||||
|
offset_right = 120.0
|
||||||
|
offset_bottom = 142.0
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "连接状态"
|
||||||
|
|
||||||
|
[node name="MessageInput" type="LineEdit" parent="."]
|
||||||
|
layout_mode = 0
|
||||||
|
offset_left = 136.0
|
||||||
|
offset_top = 50.0
|
||||||
|
offset_right = 400.0
|
||||||
|
offset_bottom = 100.0
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
|
|
||||||
|
[node name="SendButton" type="Button" parent="."]
|
||||||
|
layout_mode = 0
|
||||||
|
offset_left = 68.0
|
||||||
|
offset_top = 50.0
|
||||||
|
offset_right = 136.0
|
||||||
|
offset_bottom = 100.0
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "发送"
|
||||||
|
|
||||||
|
[node name="ConnectionButton" type="Button" parent="."]
|
||||||
|
layout_mode = 0
|
||||||
|
offset_top = 50.0
|
||||||
|
offset_right = 68.0
|
||||||
|
offset_bottom = 100.0
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "连接"
|
||||||
100
Server/JsonEdit/README.md
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
# JSON 批量编辑器
|
||||||
|
|
||||||
|
一个简洁强大的JSON编辑工具,专门用于批量添加键值对到JSON文件中的所有对象。
|
||||||
|
|
||||||
|
## 🚀 功能特点
|
||||||
|
|
||||||
|
- **批量添加属性**: 一键为JSON中所有对象添加新的键值对
|
||||||
|
- **智能类型识别**: 自动识别并转换数据类型
|
||||||
|
- **支持所有JSON数据类型**: 字符串、数字、布尔值、对象、数组、null
|
||||||
|
- **文件上传**: 支持JSON文件拖拽上传
|
||||||
|
- **即时下载**: 编辑完成后立即下载修改后的JSON文件
|
||||||
|
- **无需服务器**: 纯前端实现,直接在浏览器中运行
|
||||||
|
|
||||||
|
## 📋 支持的数据类型
|
||||||
|
|
||||||
|
| 类型 | 输入示例 | 转换结果 |
|
||||||
|
|------|----------|----------|
|
||||||
|
| 字符串 | `hello world` | `"hello world"` |
|
||||||
|
| 整数 | `123` | `123` |
|
||||||
|
| 小数 | `3.14` | `3.14` |
|
||||||
|
| 布尔值 | `true` 或 `false` | `true` 或 `false` |
|
||||||
|
| 空值 | `null` | `null` |
|
||||||
|
| 对象 | `{"key": "value"}` | `{"key": "value"}` |
|
||||||
|
| 数组 | `[1, 2, 3]` | `[1, 2, 3]` |
|
||||||
|
| 空字符串 | *(留空)* | `""` |
|
||||||
|
|
||||||
|
## 🎯 使用方法
|
||||||
|
|
||||||
|
### 1. 加载JSON数据
|
||||||
|
- **方法一**: 点击"上传JSON文件"按钮选择文件
|
||||||
|
- **方法二**: 直接在编辑器中粘贴JSON数据
|
||||||
|
- **方法三**: 点击"加载示例数据"使用预设数据
|
||||||
|
|
||||||
|
### 2. 批量添加属性
|
||||||
|
1. 在"键名"字段输入要添加的属性名,如: `能否购买`
|
||||||
|
2. 在"键值"字段输入属性值,如: `true`
|
||||||
|
3. 点击"批量添加到所有对象"按钮
|
||||||
|
4. 系统会自动为JSON中的每个对象添加该属性
|
||||||
|
|
||||||
|
### 3. 下载结果
|
||||||
|
点击"下载修改后的JSON"按钮保存编辑后的文件
|
||||||
|
|
||||||
|
## 💡 使用示例
|
||||||
|
|
||||||
|
### 原始JSON:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"小麦": {
|
||||||
|
"花费": 120,
|
||||||
|
"收益": 100,
|
||||||
|
"品质": "普通"
|
||||||
|
},
|
||||||
|
"稻谷": {
|
||||||
|
"花费": 100,
|
||||||
|
"收益": 120,
|
||||||
|
"品质": "普通"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 添加属性: 键名=`能否购买`, 键值=`true`
|
||||||
|
|
||||||
|
### 结果JSON:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"小麦": {
|
||||||
|
"花费": 120,
|
||||||
|
"收益": 100,
|
||||||
|
"品质": "普通",
|
||||||
|
"能否购买": true
|
||||||
|
},
|
||||||
|
"稻谷": {
|
||||||
|
"花费": 100,
|
||||||
|
"收益": 120,
|
||||||
|
"品质": "普通",
|
||||||
|
"能否购买": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔧 快速开始
|
||||||
|
|
||||||
|
1. 直接在浏览器中打开 `templates/json_editor.html` 文件
|
||||||
|
2. 无需安装任何依赖或服务器
|
||||||
|
3. 开始使用批量编辑功能
|
||||||
|
|
||||||
|
## ⚠️ 注意事项
|
||||||
|
|
||||||
|
- 工具会递归处理嵌套对象,为所有找到的对象添加指定属性
|
||||||
|
- 数组元素如果是对象,也会被添加属性
|
||||||
|
- 确保JSON格式正确,否则无法处理
|
||||||
|
- 修改前建议备份原始文件
|
||||||
|
|
||||||
|
## 🎨 界面说明
|
||||||
|
|
||||||
|
- **左侧边栏**: 文件操作、批量编辑功能、快速示例
|
||||||
|
- **右侧编辑区**: JSON数据显示和编辑
|
||||||
|
- **智能提示**: 实时显示操作结果和错误信息
|
||||||
|
|
||||||
|
这个工具特别适合游戏开发、配置文件管理等需要批量修改JSON数据的场景。
|
||||||
65
Server/JsonEdit/example_formats.md
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
# JSON格式化示例
|
||||||
|
|
||||||
|
以下展示三种不同的JSON格式化效果:
|
||||||
|
|
||||||
|
## 原始数据
|
||||||
|
```json
|
||||||
|
{"小麦":{"花费":120,"收益":100,"品质":"普通"},"稻谷":{"花费":100,"收益":120,"品质":"普通"}}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 1. 标准格式化(2空格缩进)
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"小麦": {
|
||||||
|
"花费": 120,
|
||||||
|
"收益": 100,
|
||||||
|
"品质": "普通"
|
||||||
|
},
|
||||||
|
"稻谷": {
|
||||||
|
"花费": 100,
|
||||||
|
"收益": 120,
|
||||||
|
"品质": "普通"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. 最小化(压缩)
|
||||||
|
```json
|
||||||
|
{"小麦":{"花费":120,"收益":100,"品质":"普通"},"稻谷":{"花费":100,"收益":120,"品质":"普通"}}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. 一行化(一个对象一行)
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"小麦": {"花费":120,"收益":100,"品质":"普通"},
|
||||||
|
"稻谷": {"花费":100,"收益":120,"品质":"普通"}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用场景
|
||||||
|
|
||||||
|
- **标准格式化**: 适合阅读和编辑,开发时使用
|
||||||
|
- **最小化**: 适合网络传输,节省带宽
|
||||||
|
- **一行化**: 适合比较不同对象,每行一个对象便于查看差异
|
||||||
|
|
||||||
|
## 批量添加属性示例
|
||||||
|
|
||||||
|
添加键名: `能否购买`, 键值: `true`
|
||||||
|
|
||||||
|
### 结果:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"小麦": {
|
||||||
|
"花费": 120,
|
||||||
|
"收益": 100,
|
||||||
|
"品质": "普通",
|
||||||
|
"能否购买": true
|
||||||
|
},
|
||||||
|
"稻谷": {
|
||||||
|
"花费": 100,
|
||||||
|
"收益": 120,
|
||||||
|
"品质": "普通",
|
||||||
|
"能否购买": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
267
Server/JsonEdit/json_editor.py
Normal file
@@ -0,0 +1,267 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from flask import Flask, request, jsonify, render_template, send_file
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
class JSONFormatter:
|
||||||
|
"""JSON格式化工具类"""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def format_standard(data, indent=2):
|
||||||
|
"""标准格式化 - 带缩进的可读格式"""
|
||||||
|
return json.dumps(data, ensure_ascii=False, indent=indent)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def minify(data):
|
||||||
|
"""最小化 - 压缩去除空格"""
|
||||||
|
return json.dumps(data, ensure_ascii=False, separators=(',', ':'))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def one_line_per_object(data):
|
||||||
|
"""一行化 - 每个对象/元素占一行"""
|
||||||
|
if isinstance(data, list):
|
||||||
|
# 如果是数组,每个元素占一行
|
||||||
|
lines = ['[']
|
||||||
|
for i, item in enumerate(data):
|
||||||
|
comma = ',' if i < len(data) - 1 else ''
|
||||||
|
lines.append(f' {json.dumps(item, ensure_ascii=False)}{comma}')
|
||||||
|
lines.append(']')
|
||||||
|
return '\n'.join(lines)
|
||||||
|
|
||||||
|
elif isinstance(data, dict):
|
||||||
|
# 如果是对象,每个键值对占一行
|
||||||
|
lines = ['{']
|
||||||
|
keys = list(data.keys())
|
||||||
|
for i, key in enumerate(keys):
|
||||||
|
comma = ',' if i < len(keys) - 1 else ''
|
||||||
|
value_str = json.dumps(data[key], ensure_ascii=False)
|
||||||
|
lines.append(f' {json.dumps(key, ensure_ascii=False)}: {value_str}{comma}')
|
||||||
|
lines.append('}')
|
||||||
|
return '\n'.join(lines)
|
||||||
|
|
||||||
|
else:
|
||||||
|
# 基本类型直接返回
|
||||||
|
return json.dumps(data, ensure_ascii=False)
|
||||||
|
|
||||||
|
@app.route('/')
|
||||||
|
def index():
|
||||||
|
"""主页"""
|
||||||
|
return render_template('json_editor.html')
|
||||||
|
|
||||||
|
@app.route('/api/format', methods=['POST'])
|
||||||
|
def format_json():
|
||||||
|
"""JSON格式化API"""
|
||||||
|
try:
|
||||||
|
data = request.get_json()
|
||||||
|
content = data.get('content', '')
|
||||||
|
format_type = data.get('format_type', 'standard') # standard, minify, oneline
|
||||||
|
|
||||||
|
if not content.strip():
|
||||||
|
return jsonify({'success': False, 'message': '请提供JSON内容'})
|
||||||
|
|
||||||
|
# 解析JSON
|
||||||
|
try:
|
||||||
|
json_data = json.loads(content)
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
return jsonify({'success': False, 'message': f'JSON格式错误: {str(e)}'})
|
||||||
|
|
||||||
|
# 根据类型格式化
|
||||||
|
formatter = JSONFormatter()
|
||||||
|
|
||||||
|
if format_type == 'standard':
|
||||||
|
formatted = formatter.format_standard(json_data)
|
||||||
|
message = 'JSON标准格式化完成'
|
||||||
|
elif format_type == 'minify':
|
||||||
|
formatted = formatter.minify(json_data)
|
||||||
|
message = 'JSON最小化完成'
|
||||||
|
elif format_type == 'oneline':
|
||||||
|
formatted = formatter.one_line_per_object(json_data)
|
||||||
|
message = 'JSON一行化格式完成'
|
||||||
|
else:
|
||||||
|
return jsonify({'success': False, 'message': '不支持的格式化类型'})
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
'success': True,
|
||||||
|
'message': message,
|
||||||
|
'formatted': formatted,
|
||||||
|
'original_length': len(content),
|
||||||
|
'formatted_length': len(formatted)
|
||||||
|
})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({'success': False, 'message': f'处理错误: {str(e)}'})
|
||||||
|
|
||||||
|
@app.route('/api/batch_add', methods=['POST'])
|
||||||
|
def batch_add_property():
|
||||||
|
"""批量添加属性API"""
|
||||||
|
try:
|
||||||
|
data = request.get_json()
|
||||||
|
content = data.get('content', '')
|
||||||
|
key_name = data.get('key_name', '')
|
||||||
|
key_value = data.get('key_value', '')
|
||||||
|
|
||||||
|
if not content.strip():
|
||||||
|
return jsonify({'success': False, 'message': '请提供JSON内容'})
|
||||||
|
|
||||||
|
if not key_name.strip():
|
||||||
|
return jsonify({'success': False, 'message': '请提供键名'})
|
||||||
|
|
||||||
|
# 解析JSON
|
||||||
|
try:
|
||||||
|
json_data = json.loads(content)
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
return jsonify({'success': False, 'message': f'JSON格式错误: {str(e)}'})
|
||||||
|
|
||||||
|
# 智能解析键值
|
||||||
|
parsed_value = parse_value(key_value)
|
||||||
|
|
||||||
|
# 批量添加属性
|
||||||
|
count = add_property_to_all_objects(json_data, key_name, parsed_value)
|
||||||
|
|
||||||
|
# 格式化输出
|
||||||
|
formatted = JSONFormatter.format_standard(json_data)
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
'success': True,
|
||||||
|
'message': f'成功为 {count} 个对象添加了属性 "{key_name}": {json.dumps(parsed_value, ensure_ascii=False)}',
|
||||||
|
'formatted': formatted,
|
||||||
|
'count': count
|
||||||
|
})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({'success': False, 'message': f'处理错误: {str(e)}'})
|
||||||
|
|
||||||
|
def parse_value(value_str):
|
||||||
|
"""智能解析值的类型"""
|
||||||
|
if value_str == '':
|
||||||
|
return ''
|
||||||
|
|
||||||
|
# null
|
||||||
|
if value_str.lower() == 'null':
|
||||||
|
return None
|
||||||
|
|
||||||
|
# boolean
|
||||||
|
if value_str.lower() == 'true':
|
||||||
|
return True
|
||||||
|
if value_str.lower() == 'false':
|
||||||
|
return False
|
||||||
|
|
||||||
|
# number
|
||||||
|
try:
|
||||||
|
if '.' in value_str:
|
||||||
|
return float(value_str)
|
||||||
|
else:
|
||||||
|
return int(value_str)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# JSON object or array
|
||||||
|
if (value_str.startswith('{') and value_str.endswith('}')) or \
|
||||||
|
(value_str.startswith('[') and value_str.endswith(']')):
|
||||||
|
try:
|
||||||
|
return json.loads(value_str)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# string
|
||||||
|
return value_str
|
||||||
|
|
||||||
|
def add_property_to_all_objects(obj, key, value):
|
||||||
|
"""递归为所有对象添加属性"""
|
||||||
|
count = 0
|
||||||
|
|
||||||
|
def traverse(current):
|
||||||
|
nonlocal count
|
||||||
|
if isinstance(current, dict):
|
||||||
|
current[key] = value
|
||||||
|
count += 1
|
||||||
|
# 继续递归处理嵌套对象
|
||||||
|
for val in current.values():
|
||||||
|
if isinstance(val, (dict, list)) and val != current:
|
||||||
|
traverse(val)
|
||||||
|
elif isinstance(current, list):
|
||||||
|
for item in current:
|
||||||
|
traverse(item)
|
||||||
|
|
||||||
|
traverse(obj)
|
||||||
|
return count
|
||||||
|
|
||||||
|
@app.route('/api/validate', methods=['POST'])
|
||||||
|
def validate_json():
|
||||||
|
"""JSON验证API"""
|
||||||
|
try:
|
||||||
|
data = request.get_json()
|
||||||
|
content = data.get('content', '')
|
||||||
|
|
||||||
|
if not content.strip():
|
||||||
|
return jsonify({'success': False, 'message': '请提供JSON内容'})
|
||||||
|
|
||||||
|
try:
|
||||||
|
json_data = json.loads(content)
|
||||||
|
return jsonify({
|
||||||
|
'success': True,
|
||||||
|
'message': 'JSON格式正确 ✓',
|
||||||
|
'valid': True
|
||||||
|
})
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
return jsonify({
|
||||||
|
'success': False,
|
||||||
|
'message': f'JSON格式错误: {str(e)}',
|
||||||
|
'valid': False,
|
||||||
|
'error': str(e)
|
||||||
|
})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({'success': False, 'message': f'验证错误: {str(e)}'})
|
||||||
|
|
||||||
|
@app.route('/api/download', methods=['POST'])
|
||||||
|
def download_json():
|
||||||
|
"""下载JSON文件"""
|
||||||
|
try:
|
||||||
|
data = request.get_json()
|
||||||
|
content = data.get('content', '')
|
||||||
|
format_type = data.get('format_type', 'standard')
|
||||||
|
|
||||||
|
if not content.strip():
|
||||||
|
return jsonify({'success': False, 'message': '没有可下载的内容'})
|
||||||
|
|
||||||
|
# 验证JSON格式
|
||||||
|
try:
|
||||||
|
json_data = json.loads(content)
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
return jsonify({'success': False, 'message': f'JSON格式错误: {str(e)}'})
|
||||||
|
|
||||||
|
# 格式化
|
||||||
|
formatter = JSONFormatter()
|
||||||
|
if format_type == 'minify':
|
||||||
|
formatted_content = formatter.minify(json_data)
|
||||||
|
elif format_type == 'oneline':
|
||||||
|
formatted_content = formatter.one_line_per_object(json_data)
|
||||||
|
else:
|
||||||
|
formatted_content = formatter.format_standard(json_data)
|
||||||
|
|
||||||
|
# 生成文件名
|
||||||
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||||
|
filename = f"edited_json_{format_type}_{timestamp}.json"
|
||||||
|
|
||||||
|
# 创建临时文件
|
||||||
|
temp_file = f"temp_{filename}"
|
||||||
|
with open(temp_file, 'w', encoding='utf-8') as f:
|
||||||
|
f.write(formatted_content)
|
||||||
|
|
||||||
|
return send_file(temp_file, as_attachment=True, download_name=filename)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({'success': False, 'message': f'下载错误: {str(e)}'})
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# 确保templates目录存在
|
||||||
|
os.makedirs('templates', exist_ok=True)
|
||||||
|
|
||||||
|
# 运行应用
|
||||||
|
app.run(debug=True, host='0.0.0.0', port=5000)
|
||||||
2
Server/JsonEdit/requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
Flask==2.3.3
|
||||||
|
Werkzeug==2.3.7
|
||||||
627
Server/JsonEdit/templates/json_editor.html
Normal file
@@ -0,0 +1,627 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>简易JSON编辑器 - 批量添加键值</title>
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||||
|
background: #f5f5f5;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
background: white;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
background: #4a90e2;
|
||||||
|
color: white;
|
||||||
|
padding: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header h1 {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-content {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 300px 1fr;
|
||||||
|
gap: 0;
|
||||||
|
min-height: 600px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
background: #f8f9fa;
|
||||||
|
border-right: 1px solid #dee2e6;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-area {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section {
|
||||||
|
background: white;
|
||||||
|
border: 1px solid #dee2e6;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 15px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section h3 {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
background: #4a90e2;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 10px 20px;
|
||||||
|
border-radius: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
margin: 5px 5px 5px 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn:hover {
|
||||||
|
background: #357abd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-success {
|
||||||
|
background: #28a745;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-success:hover {
|
||||||
|
background: #218838;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-group {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-group label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-group input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border: 1px solid #dee2e6;
|
||||||
|
border-radius: 5px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-group input:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: #4a90e2;
|
||||||
|
}
|
||||||
|
|
||||||
|
#jsonEditor {
|
||||||
|
width: 100%;
|
||||||
|
height: 500px;
|
||||||
|
border: 1px solid #dee2e6;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 15px;
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.5;
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
#jsonEditor:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: #4a90e2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-upload {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
cursor: pointer;
|
||||||
|
overflow: hidden;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-upload input[type=file] {
|
||||||
|
position: absolute;
|
||||||
|
left: -9999px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert {
|
||||||
|
padding: 10px 15px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
border-radius: 5px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-success {
|
||||||
|
background: #d4edda;
|
||||||
|
color: #155724;
|
||||||
|
border: 1px solid #c3e6cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-error {
|
||||||
|
background: #f8d7da;
|
||||||
|
color: #721c24;
|
||||||
|
border: 1px solid #f5c6cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-info {
|
||||||
|
background: #d1ecf1;
|
||||||
|
color: #0c5460;
|
||||||
|
border: 1px solid #bee5eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.main-content {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="header">
|
||||||
|
<h1>🔧 JSON批量编辑器</h1>
|
||||||
|
<p>批量添加键值对到JSON文件</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="main-content">
|
||||||
|
<!-- 侧边栏 -->
|
||||||
|
<div class="sidebar">
|
||||||
|
<!-- 文件操作 -->
|
||||||
|
<div class="section">
|
||||||
|
<h3>📁 文件操作</h3>
|
||||||
|
<div class="file-upload btn">
|
||||||
|
<input type="file" id="fileInput" accept=".json" />
|
||||||
|
上传JSON文件
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-success" onclick="downloadJSON()">
|
||||||
|
下载修改后的JSON
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 批量添加键值 -->
|
||||||
|
<div class="section">
|
||||||
|
<h3>⚡ 批量添加键值</h3>
|
||||||
|
<div class="input-group">
|
||||||
|
<label for="keyName">键名:</label>
|
||||||
|
<input type="text" id="keyName" placeholder="例: 能否购买" />
|
||||||
|
</div>
|
||||||
|
<div class="input-group">
|
||||||
|
<label for="keyValue">键值:</label>
|
||||||
|
<input type="text" id="keyValue" placeholder="支持多种类型,见下方说明" />
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-success" onclick="batchAddProperty()">
|
||||||
|
批量添加到所有对象
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div style="margin-top: 15px; padding: 10px; background: #f8f9fa; border-radius: 5px; font-size: 12px;">
|
||||||
|
<strong>支持的数据类型:</strong><br>
|
||||||
|
• 字符串: hello world<br>
|
||||||
|
• 数字: 123 或 3.14<br>
|
||||||
|
• 布尔值: true 或 false<br>
|
||||||
|
• 空值: null<br>
|
||||||
|
• 对象: {"key": "value"}<br>
|
||||||
|
• 数组: [1, 2, 3]<br>
|
||||||
|
<small style="color: #666;">系统会自动识别并转换数据类型</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 快速示例 -->
|
||||||
|
<div class="section">
|
||||||
|
<h3>📝 快速示例</h3>
|
||||||
|
<button class="btn" onclick="loadSampleJSON()">
|
||||||
|
加载示例数据
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div style="margin-top: 15px;">
|
||||||
|
<strong style="font-size: 12px;">快速填入示例键值:</strong><br>
|
||||||
|
<button class="btn" style="font-size: 11px; padding: 5px 10px; margin: 2px;" onclick="fillExample('能否购买', 'true')">
|
||||||
|
布尔值示例
|
||||||
|
</button>
|
||||||
|
<button class="btn" style="font-size: 11px; padding: 5px 10px; margin: 2px;" onclick="fillExample('价格', '150')">
|
||||||
|
数字示例
|
||||||
|
</button>
|
||||||
|
<button class="btn" style="font-size: 11px; padding: 5px 10px; margin: 2px;" onclick="fillExample('备注', '新增属性')">
|
||||||
|
字符串示例
|
||||||
|
</button>
|
||||||
|
<button class="btn" style="font-size: 11px; padding: 5px 10px; margin: 2px;" onclick="fillExample('tags', '["新", "热门"]')">
|
||||||
|
数组示例
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- JSON格式化操作 -->
|
||||||
|
<div class="section">
|
||||||
|
<h3>🔧 格式化操作</h3>
|
||||||
|
<button class="btn" onclick="formatJSONStandard()">
|
||||||
|
标准格式化
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-success" onclick="minifyJSON()">
|
||||||
|
最小化(压缩)
|
||||||
|
</button>
|
||||||
|
<button class="btn" style="background: #17a2b8;" onclick="oneLinePerObject()">
|
||||||
|
一行化(一个对象一行)
|
||||||
|
</button>
|
||||||
|
<button class="btn" style="background: #6f42c1; color: white;" onclick="validateJSON()">
|
||||||
|
验证JSON格式
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div style="margin-top: 15px; padding: 10px; background: #f8f9fa; border-radius: 5px; font-size: 12px;">
|
||||||
|
<strong>格式化说明:</strong><br>
|
||||||
|
• <strong>标准格式化</strong>: 2空格缩进,易于阅读<br>
|
||||||
|
• <strong>最小化</strong>: 去除空格,节省空间<br>
|
||||||
|
• <strong>一行化</strong>: 每个对象占一行,便于比较<br>
|
||||||
|
• <strong>验证格式</strong>: 检查JSON语法是否正确<br>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 编辑区域 -->
|
||||||
|
<div class="editor-area">
|
||||||
|
<!-- 消息区域 -->
|
||||||
|
<div id="messageArea"></div>
|
||||||
|
|
||||||
|
<!-- JSON编辑器 -->
|
||||||
|
<textarea id="jsonEditor" placeholder="在此输入或上传JSON数据..."></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// 初始化
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
document.getElementById('fileInput').addEventListener('change', handleFileUpload);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 显示消息
|
||||||
|
function showMessage(message, type = 'success') {
|
||||||
|
const messageArea = document.getElementById('messageArea');
|
||||||
|
let alertClass = 'alert-success';
|
||||||
|
|
||||||
|
if (type === 'error') {
|
||||||
|
alertClass = 'alert-error';
|
||||||
|
} else if (type === 'info') {
|
||||||
|
alertClass = 'alert-info';
|
||||||
|
}
|
||||||
|
|
||||||
|
messageArea.innerHTML = `
|
||||||
|
<div class="alert ${alertClass}">
|
||||||
|
${message}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
messageArea.innerHTML = '';
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载示例JSON
|
||||||
|
function loadSampleJSON() {
|
||||||
|
const sampleJSON = {
|
||||||
|
"测试作物": {
|
||||||
|
"花费": 1,
|
||||||
|
"生长时间": 3,
|
||||||
|
"收益": 9999,
|
||||||
|
"品质": "普通"
|
||||||
|
},
|
||||||
|
"小麦": {
|
||||||
|
"花费": 120,
|
||||||
|
"生长时间": 120,
|
||||||
|
"收益": 100,
|
||||||
|
"品质": "普通"
|
||||||
|
},
|
||||||
|
"稻谷": {
|
||||||
|
"花费": 100,
|
||||||
|
"生长时间": 240,
|
||||||
|
"收益": 120,
|
||||||
|
"品质": "普通"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
document.getElementById('jsonEditor').value = JSON.stringify(sampleJSON, null, 2);
|
||||||
|
showMessage('示例数据已加载');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 快速填入示例键值对
|
||||||
|
function fillExample(keyName, keyValue) {
|
||||||
|
document.getElementById('keyName').value = keyName;
|
||||||
|
document.getElementById('keyValue').value = keyValue;
|
||||||
|
showMessage(`已填入示例: ${keyName} = ${keyValue}`, 'info');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 文件上传处理
|
||||||
|
function handleFileUpload(event) {
|
||||||
|
const file = event.target.files[0];
|
||||||
|
if (!file) return;
|
||||||
|
|
||||||
|
if (!file.name.toLowerCase().endsWith('.json')) {
|
||||||
|
showMessage('请选择JSON文件', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = function(e) {
|
||||||
|
try {
|
||||||
|
const content = e.target.result;
|
||||||
|
JSON.parse(content); // 验证JSON格式
|
||||||
|
document.getElementById('jsonEditor').value = content;
|
||||||
|
showMessage('文件上传成功');
|
||||||
|
} catch (error) {
|
||||||
|
showMessage('JSON格式错误: ' + error.message, 'error');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
reader.readAsText(file);
|
||||||
|
|
||||||
|
// 清空文件输入
|
||||||
|
event.target.value = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量添加属性
|
||||||
|
function batchAddProperty() {
|
||||||
|
const keyName = document.getElementById('keyName').value.trim();
|
||||||
|
const keyValue = document.getElementById('keyValue').value.trim();
|
||||||
|
const jsonContent = document.getElementById('jsonEditor').value.trim();
|
||||||
|
|
||||||
|
if (!keyName) {
|
||||||
|
showMessage('请输入键名', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!jsonContent) {
|
||||||
|
showMessage('请输入或上传JSON数据', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
let jsonData = JSON.parse(jsonContent);
|
||||||
|
|
||||||
|
// 智能类型转换
|
||||||
|
let processedValue = parseValue(keyValue);
|
||||||
|
|
||||||
|
// 批量添加属性
|
||||||
|
const result = addPropertyToAllObjects(jsonData, keyName, processedValue);
|
||||||
|
|
||||||
|
if (result.count > 0) {
|
||||||
|
document.getElementById('jsonEditor').value = JSON.stringify(jsonData, null, 2);
|
||||||
|
showMessage(`成功为 ${result.count} 个对象添加了属性 "${keyName}": ${JSON.stringify(processedValue)}`);
|
||||||
|
} else {
|
||||||
|
showMessage('未找到可添加属性的对象', 'info');
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
showMessage('JSON格式错误: ' + error.message, 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 智能解析值的类型
|
||||||
|
function parseValue(value) {
|
||||||
|
// 如果输入为空,返回空字符串
|
||||||
|
if (value === '') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试解析为null
|
||||||
|
if (value.toLowerCase() === 'null') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试解析为undefined(虽然JSON不支持,但转为null)
|
||||||
|
if (value.toLowerCase() === 'undefined') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试解析为布尔值
|
||||||
|
if (value.toLowerCase() === 'true') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (value.toLowerCase() === 'false') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试解析为数字
|
||||||
|
if (!isNaN(value) && !isNaN(parseFloat(value))) {
|
||||||
|
// 检查是否为整数
|
||||||
|
if (Number.isInteger(parseFloat(value))) {
|
||||||
|
return parseInt(value, 10);
|
||||||
|
} else {
|
||||||
|
return parseFloat(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试解析为JSON对象或数组
|
||||||
|
if ((value.startsWith('{') && value.endsWith('}')) ||
|
||||||
|
(value.startsWith('[') && value.endsWith(']'))) {
|
||||||
|
try {
|
||||||
|
return JSON.parse(value);
|
||||||
|
} catch (e) {
|
||||||
|
// 如果解析失败,当作字符串处理
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 默认当作字符串处理
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 递归为所有对象添加属性
|
||||||
|
function addPropertyToAllObjects(obj, key, value) {
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
function traverse(current) {
|
||||||
|
if (typeof current === 'object' && current !== null) {
|
||||||
|
if (Array.isArray(current)) {
|
||||||
|
// 处理数组
|
||||||
|
current.forEach(item => traverse(item));
|
||||||
|
} else {
|
||||||
|
// 处理对象
|
||||||
|
current[key] = value;
|
||||||
|
count++;
|
||||||
|
|
||||||
|
// 继续递归处理嵌套对象
|
||||||
|
Object.values(current).forEach(val => {
|
||||||
|
if (typeof val === 'object' && val !== null && val !== current) {
|
||||||
|
traverse(val);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
traverse(obj);
|
||||||
|
return { count };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下载JSON
|
||||||
|
function downloadJSON() {
|
||||||
|
const content = document.getElementById('jsonEditor').value.trim();
|
||||||
|
|
||||||
|
if (!content) {
|
||||||
|
showMessage('没有可下载的内容', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 验证JSON格式
|
||||||
|
JSON.parse(content);
|
||||||
|
|
||||||
|
const blob = new Blob([content], { type: 'application/json' });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = url;
|
||||||
|
link.download = `edited_json_${new Date().getTime()}.json`;
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
document.body.removeChild(link);
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
|
||||||
|
showMessage('JSON文件下载成功');
|
||||||
|
} catch (error) {
|
||||||
|
showMessage('JSON格式错误,无法下载: ' + error.message, 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 标准格式化JSON
|
||||||
|
function formatJSONStandard() {
|
||||||
|
const content = document.getElementById('jsonEditor').value.trim();
|
||||||
|
|
||||||
|
if (!content) {
|
||||||
|
showMessage('请输入JSON数据', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const jsonData = JSON.parse(content);
|
||||||
|
const formatted = JSON.stringify(jsonData, null, 2);
|
||||||
|
document.getElementById('jsonEditor').value = formatted;
|
||||||
|
showMessage('JSON标准格式化完成');
|
||||||
|
} catch (error) {
|
||||||
|
showMessage('JSON格式错误: ' + error.message, 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 最小化JSON(压缩)
|
||||||
|
function minifyJSON() {
|
||||||
|
const content = document.getElementById('jsonEditor').value.trim();
|
||||||
|
|
||||||
|
if (!content) {
|
||||||
|
showMessage('请输入JSON数据', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const jsonData = JSON.parse(content);
|
||||||
|
const minified = JSON.stringify(jsonData);
|
||||||
|
document.getElementById('jsonEditor').value = minified;
|
||||||
|
showMessage('JSON最小化完成');
|
||||||
|
} catch (error) {
|
||||||
|
showMessage('JSON格式错误: ' + error.message, 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 一行化格式(一个对象一行)
|
||||||
|
function oneLinePerObject() {
|
||||||
|
const content = document.getElementById('jsonEditor').value.trim();
|
||||||
|
|
||||||
|
if (!content) {
|
||||||
|
showMessage('请输入JSON数据', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const jsonData = JSON.parse(content);
|
||||||
|
let formatted = '';
|
||||||
|
|
||||||
|
if (Array.isArray(jsonData)) {
|
||||||
|
// 如果是数组,每个元素占一行
|
||||||
|
formatted = '[\n';
|
||||||
|
jsonData.forEach((item, index) => {
|
||||||
|
formatted += ' ' + JSON.stringify(item);
|
||||||
|
if (index < jsonData.length - 1) {
|
||||||
|
formatted += ',';
|
||||||
|
}
|
||||||
|
formatted += '\n';
|
||||||
|
});
|
||||||
|
formatted += ']';
|
||||||
|
} else if (typeof jsonData === 'object' && jsonData !== null) {
|
||||||
|
// 如果是对象,每个键值对占一行
|
||||||
|
formatted = '{\n';
|
||||||
|
const keys = Object.keys(jsonData);
|
||||||
|
keys.forEach((key, index) => {
|
||||||
|
formatted += ' ' + JSON.stringify(key) + ': ' + JSON.stringify(jsonData[key]);
|
||||||
|
if (index < keys.length - 1) {
|
||||||
|
formatted += ',';
|
||||||
|
}
|
||||||
|
formatted += '\n';
|
||||||
|
});
|
||||||
|
formatted += '}';
|
||||||
|
} else {
|
||||||
|
// 基本类型直接输出
|
||||||
|
formatted = JSON.stringify(jsonData);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('jsonEditor').value = formatted;
|
||||||
|
showMessage('JSON一行化格式完成');
|
||||||
|
} catch (error) {
|
||||||
|
showMessage('JSON格式错误: ' + error.message, 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证JSON格式
|
||||||
|
function validateJSON() {
|
||||||
|
const content = document.getElementById('jsonEditor').value.trim();
|
||||||
|
|
||||||
|
if (!content) {
|
||||||
|
showMessage('请输入JSON数据', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
JSON.parse(content);
|
||||||
|
showMessage('JSON格式验证通过');
|
||||||
|
} catch (error) {
|
||||||
|
showMessage('JSON格式错误: ' + error.message, 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
337
Server/QQEmailSend.py
Normal file
@@ -0,0 +1,337 @@
|
|||||||
|
import smtplib
|
||||||
|
from email.mime.text import MIMEText
|
||||||
|
from email.mime.multipart import MIMEMultipart
|
||||||
|
from email.mime.application import MIMEApplication
|
||||||
|
from email.header import Header
|
||||||
|
import random
|
||||||
|
import string
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
|
# 邮件发送配置
|
||||||
|
SENDER_EMAIL = '3205788256@qq.com' # 发件人邮箱
|
||||||
|
SENDER_AUTH_CODE = 'szcaxvbftusqddhi' # 授权码
|
||||||
|
SMTP_SERVER = 'smtp.qq.com' # QQ邮箱SMTP服务器
|
||||||
|
SMTP_PORT = 465 # QQ邮箱SSL端口
|
||||||
|
|
||||||
|
# 验证码缓存文件
|
||||||
|
VERIFICATION_CACHE_FILE = os.path.join("config", "verification_codes.json")
|
||||||
|
|
||||||
|
class QQMailAPI:
|
||||||
|
"""QQ邮箱发送邮件API类"""
|
||||||
|
|
||||||
|
def __init__(self, sender_email, authorization_code):
|
||||||
|
"""
|
||||||
|
初始化邮箱配置
|
||||||
|
:param sender_email: 发送方QQ邮箱地址
|
||||||
|
:param authorization_code: QQ邮箱授权码
|
||||||
|
"""
|
||||||
|
self.sender_email = sender_email
|
||||||
|
self.authorization_code = authorization_code
|
||||||
|
self.smtp_server = 'smtp.qq.com'
|
||||||
|
self.smtp_port = 465 # SSL端口
|
||||||
|
|
||||||
|
def send_text_email(self, receiver_email, subject, content, cc_emails=None):
|
||||||
|
"""
|
||||||
|
发送纯文本邮件
|
||||||
|
:param receiver_email: 接收方邮箱地址(单个)
|
||||||
|
:param subject: 邮件主题
|
||||||
|
:param content: 邮件正文内容
|
||||||
|
:param cc_emails: 抄送邮箱列表
|
||||||
|
:return: 发送成功返回True,失败返回False
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# 创建邮件对象
|
||||||
|
message = MIMEText(content, 'plain', 'utf-8')
|
||||||
|
message['From'] = Header(self.sender_email, 'utf-8')
|
||||||
|
message['To'] = Header(receiver_email, 'utf-8')
|
||||||
|
message['Subject'] = Header(subject, 'utf-8')
|
||||||
|
|
||||||
|
# 添加抄送
|
||||||
|
if cc_emails:
|
||||||
|
message['Cc'] = Header(",".join(cc_emails), 'utf-8')
|
||||||
|
all_receivers = [receiver_email] + cc_emails
|
||||||
|
else:
|
||||||
|
all_receivers = [receiver_email]
|
||||||
|
|
||||||
|
# 连接SMTP服务器并发送邮件
|
||||||
|
with smtplib.SMTP_SSL(self.smtp_server, self.smtp_port) as server:
|
||||||
|
server.login(self.sender_email, self.authorization_code)
|
||||||
|
server.sendmail(self.sender_email, all_receivers, message.as_string())
|
||||||
|
|
||||||
|
print(f"邮件发送成功:主题='{subject}', 收件人='{receiver_email}'")
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
print(f"邮件发送失败:{str(e)}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def send_html_email(self, receiver_email, subject, html_content, cc_emails=None, attachments=None):
|
||||||
|
"""
|
||||||
|
发送HTML格式邮件,可带附件
|
||||||
|
:param receiver_email: 接收方邮箱地址(单个)
|
||||||
|
:param subject: 邮件主题
|
||||||
|
:param html_content: HTML格式的邮件正文
|
||||||
|
:param cc_emails: 抄送邮箱列表
|
||||||
|
:param attachments: 附件文件路径列表
|
||||||
|
:return: 发送成功返回True,失败返回False
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# 创建带附件的邮件对象
|
||||||
|
message = MIMEMultipart()
|
||||||
|
message['From'] = Header(self.sender_email, 'utf-8')
|
||||||
|
message['To'] = Header(receiver_email, 'utf-8')
|
||||||
|
message['Subject'] = Header(subject, 'utf-8')
|
||||||
|
|
||||||
|
# 添加抄送
|
||||||
|
if cc_emails:
|
||||||
|
message['Cc'] = Header(",".join(cc_emails), 'utf-8')
|
||||||
|
all_receivers = [receiver_email] + cc_emails
|
||||||
|
else:
|
||||||
|
all_receivers = [receiver_email]
|
||||||
|
|
||||||
|
# 添加HTML正文
|
||||||
|
message.attach(MIMEText(html_content, 'html', 'utf-8'))
|
||||||
|
|
||||||
|
# 添加附件
|
||||||
|
if attachments:
|
||||||
|
for file_path in attachments:
|
||||||
|
try:
|
||||||
|
with open(file_path, 'rb') as file:
|
||||||
|
attachment = MIMEApplication(file.read(), _subtype="octet-stream")
|
||||||
|
attachment.add_header('Content-Disposition', 'attachment', filename=file_path.split("/")[-1])
|
||||||
|
message.attach(attachment)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"添加附件失败 {file_path}: {str(e)}")
|
||||||
|
|
||||||
|
# 连接SMTP服务器并发送邮件
|
||||||
|
with smtplib.SMTP_SSL(self.smtp_server, self.smtp_port) as server:
|
||||||
|
server.login(self.sender_email, self.authorization_code)
|
||||||
|
server.sendmail(self.sender_email, all_receivers, message.as_string())
|
||||||
|
|
||||||
|
print(f"HTML邮件发送成功:主题='{subject}', 收件人='{receiver_email}'")
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
print(f"HTML邮件发送失败:{str(e)}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
class EmailVerification:
|
||||||
|
@staticmethod
|
||||||
|
def generate_verification_code(length=6):
|
||||||
|
"""
|
||||||
|
生成指定长度的随机验证码
|
||||||
|
|
||||||
|
参数:
|
||||||
|
length (int): 验证码长度,默认6位
|
||||||
|
|
||||||
|
返回:
|
||||||
|
str: 生成的验证码
|
||||||
|
"""
|
||||||
|
# 生成包含大写字母和数字的验证码
|
||||||
|
chars = string.ascii_uppercase + string.digits
|
||||||
|
return ''.join(random.choice(chars) for _ in range(length))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def send_verification_email(qq_number, verification_code):
|
||||||
|
"""
|
||||||
|
发送验证码邮件到QQ邮箱
|
||||||
|
|
||||||
|
参数:
|
||||||
|
qq_number (str): 接收者QQ号
|
||||||
|
verification_code (str): 验证码
|
||||||
|
|
||||||
|
返回:
|
||||||
|
bool: 发送成功返回True,否则返回False
|
||||||
|
str: 成功或错误信息
|
||||||
|
"""
|
||||||
|
receiver_email = f"{qq_number}@qq.com"
|
||||||
|
|
||||||
|
# 创建邮件内容
|
||||||
|
message = MIMEText(f'''
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<div style="font-family: Arial, sans-serif; color: #333;">
|
||||||
|
<h2 style="color: #4CAF50;">萌芽农场 - 邮箱验证码</h2>
|
||||||
|
<p>亲爱的玩家,您好!</p>
|
||||||
|
<p>您正在注册萌芽农场游戏账号,您的验证码是:</p>
|
||||||
|
<div style="background-color: #f2f2f2; padding: 10px; font-size: 24px; font-weight: bold; color: #4CAF50; text-align: center; margin: 20px 0;">
|
||||||
|
{verification_code}
|
||||||
|
</div>
|
||||||
|
<p>该验证码有效期为5分钟,请勿泄露给他人。</p>
|
||||||
|
<p>如果这不是您本人的操作,请忽略此邮件。</p>
|
||||||
|
<p style="margin-top: 30px; font-size: 12px; color: #999;">
|
||||||
|
本邮件由系统自动发送,请勿直接回复。
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
''', 'html', 'utf-8')
|
||||||
|
|
||||||
|
# 修正From头格式,符合QQ邮箱的要求
|
||||||
|
message['From'] = SENDER_EMAIL
|
||||||
|
message['To'] = receiver_email
|
||||||
|
message['Subject'] = Header('【萌芽农场】注册验证码', 'utf-8')
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 使用SSL/TLS连接而不是STARTTLS
|
||||||
|
smtp_obj = smtplib.SMTP_SSL(SMTP_SERVER, 465)
|
||||||
|
smtp_obj.login(SENDER_EMAIL, SENDER_AUTH_CODE)
|
||||||
|
smtp_obj.sendmail(SENDER_EMAIL, [receiver_email], message.as_string())
|
||||||
|
smtp_obj.quit()
|
||||||
|
return True, "验证码发送成功"
|
||||||
|
except Exception as e:
|
||||||
|
return False, f"发送验证码失败: {str(e)}"
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def save_verification_code(qq_number, verification_code, expiry_time=300):
|
||||||
|
"""
|
||||||
|
保存验证码到缓存文件
|
||||||
|
|
||||||
|
参数:
|
||||||
|
qq_number (str): QQ号
|
||||||
|
verification_code (str): 验证码
|
||||||
|
expiry_time (int): 过期时间(秒),默认5分钟
|
||||||
|
|
||||||
|
返回:
|
||||||
|
bool: 保存成功返回True,否则返回False
|
||||||
|
"""
|
||||||
|
import time
|
||||||
|
|
||||||
|
# 创建目录(如果不存在)
|
||||||
|
os.makedirs(os.path.dirname(VERIFICATION_CACHE_FILE), exist_ok=True)
|
||||||
|
|
||||||
|
# 读取现有的验证码数据
|
||||||
|
verification_data = {}
|
||||||
|
if os.path.exists(VERIFICATION_CACHE_FILE):
|
||||||
|
try:
|
||||||
|
with open(VERIFICATION_CACHE_FILE, 'r', encoding='utf-8') as file:
|
||||||
|
verification_data = json.load(file)
|
||||||
|
except:
|
||||||
|
verification_data = {}
|
||||||
|
|
||||||
|
# 添加新的验证码
|
||||||
|
expire_at = time.time() + expiry_time
|
||||||
|
verification_data[qq_number] = {
|
||||||
|
"code": verification_code,
|
||||||
|
"expire_at": expire_at
|
||||||
|
}
|
||||||
|
|
||||||
|
# 保存到文件
|
||||||
|
try:
|
||||||
|
with open(VERIFICATION_CACHE_FILE, 'w', encoding='utf-8') as file:
|
||||||
|
json.dump(verification_data, file, indent=2, ensure_ascii=False)
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
print(f"保存验证码失败: {str(e)}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def verify_code(qq_number, input_code):
|
||||||
|
"""
|
||||||
|
验证用户输入的验证码
|
||||||
|
|
||||||
|
参数:
|
||||||
|
qq_number (str): QQ号
|
||||||
|
input_code (str): 用户输入的验证码
|
||||||
|
|
||||||
|
返回:
|
||||||
|
bool: 验证成功返回True,否则返回False
|
||||||
|
str: 成功或错误信息
|
||||||
|
"""
|
||||||
|
import time
|
||||||
|
|
||||||
|
# 检查缓存文件是否存在
|
||||||
|
if not os.path.exists(VERIFICATION_CACHE_FILE):
|
||||||
|
return False, "验证码不存在或已过期"
|
||||||
|
|
||||||
|
# 读取验证码数据
|
||||||
|
try:
|
||||||
|
with open(VERIFICATION_CACHE_FILE, 'r', encoding='utf-8') as file:
|
||||||
|
verification_data = json.load(file)
|
||||||
|
except:
|
||||||
|
return False, "验证码数据损坏"
|
||||||
|
|
||||||
|
# 检查该QQ号是否有验证码
|
||||||
|
if qq_number not in verification_data:
|
||||||
|
return False, "验证码不存在,请重新获取"
|
||||||
|
|
||||||
|
# 获取存储的验证码信息
|
||||||
|
code_info = verification_data[qq_number]
|
||||||
|
stored_code = code_info.get("code", "")
|
||||||
|
expire_at = code_info.get("expire_at", 0)
|
||||||
|
|
||||||
|
# 检查验证码是否过期
|
||||||
|
current_time = time.time()
|
||||||
|
if current_time > expire_at:
|
||||||
|
# 移除过期的验证码
|
||||||
|
del verification_data[qq_number]
|
||||||
|
with open(VERIFICATION_CACHE_FILE, 'w', encoding='utf-8') as file:
|
||||||
|
json.dump(verification_data, file, indent=2, ensure_ascii=False)
|
||||||
|
return False, "验证码已过期,请重新获取"
|
||||||
|
|
||||||
|
# 验证码比较(不区分大小写)
|
||||||
|
if input_code.upper() == stored_code.upper():
|
||||||
|
# 验证成功后移除该验证码
|
||||||
|
del verification_data[qq_number]
|
||||||
|
with open(VERIFICATION_CACHE_FILE, 'w', encoding='utf-8') as file:
|
||||||
|
json.dump(verification_data, file, indent=2, ensure_ascii=False)
|
||||||
|
return True, "验证码正确"
|
||||||
|
else:
|
||||||
|
return False, "验证码错误"
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def clean_expired_codes():
|
||||||
|
"""
|
||||||
|
清理过期的验证码
|
||||||
|
"""
|
||||||
|
import time
|
||||||
|
|
||||||
|
if not os.path.exists(VERIFICATION_CACHE_FILE):
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(VERIFICATION_CACHE_FILE, 'r', encoding='utf-8') as file:
|
||||||
|
verification_data = json.load(file)
|
||||||
|
|
||||||
|
current_time = time.time()
|
||||||
|
removed_keys = []
|
||||||
|
|
||||||
|
# 找出过期的验证码
|
||||||
|
for qq_number, code_info in verification_data.items():
|
||||||
|
expire_at = code_info.get("expire_at", 0)
|
||||||
|
if current_time > expire_at:
|
||||||
|
removed_keys.append(qq_number)
|
||||||
|
|
||||||
|
# 移除过期的验证码
|
||||||
|
for key in removed_keys:
|
||||||
|
del verification_data[key]
|
||||||
|
|
||||||
|
# 保存更新后的数据
|
||||||
|
with open(VERIFICATION_CACHE_FILE, 'w', encoding='utf-8') as file:
|
||||||
|
json.dump(verification_data, file, indent=2, ensure_ascii=False)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"清理过期验证码失败: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
|
# 测试邮件发送
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# 清理过期验证码
|
||||||
|
EmailVerification.clean_expired_codes()
|
||||||
|
|
||||||
|
# 生成验证码
|
||||||
|
test_qq = input("请输入测试QQ号: ")
|
||||||
|
verification_code = EmailVerification.generate_verification_code()
|
||||||
|
print(f"生成的验证码: {verification_code}")
|
||||||
|
|
||||||
|
# 发送测试邮件
|
||||||
|
success, message = EmailVerification.send_verification_email(test_qq, verification_code)
|
||||||
|
print(f"发送结果: {success}, 消息: {message}")
|
||||||
|
|
||||||
|
if success:
|
||||||
|
# 保存验证码
|
||||||
|
EmailVerification.save_verification_code(test_qq, verification_code)
|
||||||
|
|
||||||
|
# 测试验证
|
||||||
|
test_input = input("请输入收到的验证码: ")
|
||||||
|
verify_success, verify_message = EmailVerification.verify_code(test_qq, test_input)
|
||||||
|
print(f"验证结果: {verify_success}, 消息: {verify_message}")
|
||||||
BIN
Server/QQEmailSend.zip
Normal file
1519
Server/TCPGameServer.py
Normal file
355
Server/TCPServer.py
Normal file
@@ -0,0 +1,355 @@
|
|||||||
|
import socket
|
||||||
|
import threading
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
import sys
|
||||||
|
import logging
|
||||||
|
import colorama
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
# 初始化colorama以支持跨平台彩色终端输出
|
||||||
|
colorama.init()
|
||||||
|
|
||||||
|
# 自定义日志格式化器,带有颜色和分类
|
||||||
|
class MinecraftStyleFormatter(logging.Formatter):
|
||||||
|
"""Minecraft风格的日志格式化器,带有颜色和分类"""
|
||||||
|
|
||||||
|
# ANSI颜色代码
|
||||||
|
COLORS = {
|
||||||
|
'RESET': colorama.Fore.RESET,
|
||||||
|
'BLACK': colorama.Fore.BLACK,
|
||||||
|
'RED': colorama.Fore.RED,
|
||||||
|
'GREEN': colorama.Fore.GREEN,
|
||||||
|
'YELLOW': colorama.Fore.YELLOW,
|
||||||
|
'BLUE': colorama.Fore.BLUE,
|
||||||
|
'MAGENTA': colorama.Fore.MAGENTA,
|
||||||
|
'CYAN': colorama.Fore.CYAN,
|
||||||
|
'WHITE': colorama.Fore.WHITE,
|
||||||
|
'BRIGHT_BLACK': colorama.Fore.LIGHTBLACK_EX,
|
||||||
|
'BRIGHT_RED': colorama.Fore.LIGHTRED_EX,
|
||||||
|
'BRIGHT_GREEN': colorama.Fore.LIGHTGREEN_EX,
|
||||||
|
'BRIGHT_YELLOW': colorama.Fore.LIGHTYELLOW_EX,
|
||||||
|
'BRIGHT_BLUE': colorama.Fore.LIGHTBLUE_EX,
|
||||||
|
'BRIGHT_MAGENTA': colorama.Fore.LIGHTMAGENTA_EX,
|
||||||
|
'BRIGHT_CYAN': colorama.Fore.LIGHTCYAN_EX,
|
||||||
|
'BRIGHT_WHITE': colorama.Fore.LIGHTWHITE_EX,
|
||||||
|
}
|
||||||
|
|
||||||
|
# 日志级别颜色(类似于Minecraft)
|
||||||
|
LEVEL_COLORS = {
|
||||||
|
'DEBUG': COLORS['BRIGHT_BLACK'],
|
||||||
|
'INFO': COLORS['WHITE'],
|
||||||
|
'WARNING': COLORS['YELLOW'],
|
||||||
|
'ERROR': COLORS['RED'],
|
||||||
|
'CRITICAL': COLORS['BRIGHT_RED'],
|
||||||
|
}
|
||||||
|
|
||||||
|
# 类别及其颜色
|
||||||
|
CATEGORIES = {
|
||||||
|
'SERVER': COLORS['BRIGHT_CYAN'],
|
||||||
|
'NETWORK': COLORS['BRIGHT_GREEN'],
|
||||||
|
'CLIENT': COLORS['BRIGHT_YELLOW'],
|
||||||
|
'SYSTEM': COLORS['BRIGHT_MAGENTA'],
|
||||||
|
}
|
||||||
|
|
||||||
|
def format(self, record):
|
||||||
|
# 获取日志级别颜色
|
||||||
|
level_color = self.LEVEL_COLORS.get(record.levelname, self.COLORS['WHITE'])
|
||||||
|
|
||||||
|
# 从记录名称中确定类别,默认为SERVER
|
||||||
|
category_name = getattr(record, 'category', 'SERVER')
|
||||||
|
category_color = self.CATEGORIES.get(category_name, self.COLORS['WHITE'])
|
||||||
|
|
||||||
|
# 格式化时间戳,类似于Minecraft:[HH:MM:SS]
|
||||||
|
timestamp = datetime.now().strftime('%H:%M:%S')
|
||||||
|
|
||||||
|
# 格式化消息
|
||||||
|
formatted_message = f"{self.COLORS['BRIGHT_BLACK']}[{timestamp}] {category_color}[{category_name}] {level_color}{record.levelname}: {record.getMessage()}{self.COLORS['RESET']}"
|
||||||
|
|
||||||
|
return formatted_message
|
||||||
|
|
||||||
|
|
||||||
|
class TCPServer:
|
||||||
|
def __init__(self, host='127.0.0.1', port=9000, buffer_size=4096):
|
||||||
|
"""初始化TCP服务器"""
|
||||||
|
self.host = host
|
||||||
|
self.port = port
|
||||||
|
self.buffer_size = buffer_size
|
||||||
|
self.socket = None
|
||||||
|
self.clients = {} # 存储客户端连接 {client_id: (socket, address)}
|
||||||
|
self.running = False
|
||||||
|
self.client_buffers = {} # 每个客户端的消息缓冲区
|
||||||
|
|
||||||
|
# 配置日志
|
||||||
|
self._setup_logging()
|
||||||
|
|
||||||
|
def _setup_logging(self):
|
||||||
|
"""设置Minecraft风格的日志系统"""
|
||||||
|
# 创建日志器
|
||||||
|
self.logger = logging.getLogger('TCPServer')
|
||||||
|
self.logger.setLevel(logging.INFO)
|
||||||
|
|
||||||
|
# 清除任何现有的处理器
|
||||||
|
if self.logger.handlers:
|
||||||
|
self.logger.handlers.clear()
|
||||||
|
|
||||||
|
# 创建控制台处理器
|
||||||
|
console_handler = logging.StreamHandler()
|
||||||
|
console_handler.setLevel(logging.INFO)
|
||||||
|
|
||||||
|
# 设置格式化器
|
||||||
|
formatter = MinecraftStyleFormatter()
|
||||||
|
console_handler.setFormatter(formatter)
|
||||||
|
|
||||||
|
# 添加处理器到日志器
|
||||||
|
self.logger.addHandler(console_handler)
|
||||||
|
|
||||||
|
def log(self, level, message, category='SERVER'):
|
||||||
|
"""使用指定的分类和级别记录日志"""
|
||||||
|
record = logging.LogRecord(
|
||||||
|
name=self.logger.name,
|
||||||
|
level=getattr(logging, level),
|
||||||
|
pathname='',
|
||||||
|
lineno=0,
|
||||||
|
msg=message,
|
||||||
|
args=(),
|
||||||
|
exc_info=None
|
||||||
|
)
|
||||||
|
record.category = category
|
||||||
|
self.logger.handle(record)
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
"""启动服务器"""
|
||||||
|
try:
|
||||||
|
# 创建TCP套接字
|
||||||
|
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||||
|
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) # 禁用Nagle算法
|
||||||
|
|
||||||
|
# 绑定地址和监听
|
||||||
|
self.socket.bind((self.host, self.port))
|
||||||
|
self.socket.listen(5)
|
||||||
|
|
||||||
|
self.running = True
|
||||||
|
self.log('INFO', f"服务器启动,监听 {self.host}:{self.port}", 'SERVER')
|
||||||
|
|
||||||
|
# 接受客户端连接的主循环
|
||||||
|
self._accept_clients()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.log('ERROR', f"服务器启动错误: {e}", 'SYSTEM')
|
||||||
|
self.stop()
|
||||||
|
|
||||||
|
def _accept_clients(self):
|
||||||
|
"""接受客户端连接的循环"""
|
||||||
|
while self.running:
|
||||||
|
try:
|
||||||
|
# 接受新的客户端连接
|
||||||
|
client_socket, address = self.socket.accept()
|
||||||
|
client_id = f"{address[0]}:{address[1]}"
|
||||||
|
|
||||||
|
self.log('INFO', f"新客户端连接: {client_id}", 'NETWORK')
|
||||||
|
|
||||||
|
# 存储客户端信息
|
||||||
|
self.clients[client_id] = (client_socket, address)
|
||||||
|
self.client_buffers[client_id] = ""
|
||||||
|
|
||||||
|
# 创建处理线程
|
||||||
|
client_thread = threading.Thread(
|
||||||
|
target=self._handle_client,
|
||||||
|
args=(client_id,)
|
||||||
|
)
|
||||||
|
client_thread.daemon = True
|
||||||
|
client_thread.start()
|
||||||
|
|
||||||
|
# 通知客户端连接成功
|
||||||
|
self.send_data(client_id, {"type": "connection_status", "status": "connected"})
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
self.log('INFO', "收到中断信号,服务器停止中...", 'SYSTEM')
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
self.log('ERROR', f"接受连接时出错: {e}", 'NETWORK')
|
||||||
|
time.sleep(1) # 避免CPU过度使用
|
||||||
|
|
||||||
|
def _handle_client(self, client_id):
|
||||||
|
"""处理客户端消息的线程"""
|
||||||
|
client_socket, _ = self.clients.get(client_id, (None, None))
|
||||||
|
if not client_socket:
|
||||||
|
return
|
||||||
|
|
||||||
|
# 设置超时,用于定期检查连接状态
|
||||||
|
client_socket.settimeout(30)
|
||||||
|
|
||||||
|
while self.running and client_id in self.clients:
|
||||||
|
try:
|
||||||
|
# 接收数据
|
||||||
|
data = client_socket.recv(self.buffer_size)
|
||||||
|
|
||||||
|
if not data:
|
||||||
|
# 客户端断开连接
|
||||||
|
self.log('INFO', f"客户端 {client_id} 断开连接", 'CLIENT')
|
||||||
|
self._remove_client(client_id)
|
||||||
|
break
|
||||||
|
|
||||||
|
# 处理接收的数据
|
||||||
|
self._process_data(client_id, data)
|
||||||
|
|
||||||
|
except socket.timeout:
|
||||||
|
# 发送保活消息
|
||||||
|
try:
|
||||||
|
self.send_data(client_id, {"type": "ping"})
|
||||||
|
except:
|
||||||
|
self.log('INFO', f"客户端 {client_id} 连接超时", 'CLIENT')
|
||||||
|
self._remove_client(client_id)
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
self.log('ERROR', f"处理客户端 {client_id} 数据时出错: {e}", 'CLIENT')
|
||||||
|
self._remove_client(client_id)
|
||||||
|
break
|
||||||
|
|
||||||
|
def _process_data(self, client_id, data):
|
||||||
|
"""处理从客户端接收的数据"""
|
||||||
|
# 将接收的字节添加到缓冲区
|
||||||
|
try:
|
||||||
|
decoded_data = data.decode('utf-8')
|
||||||
|
self.client_buffers[client_id] += decoded_data
|
||||||
|
|
||||||
|
# 处理可能包含多条JSON消息的缓冲区
|
||||||
|
self._process_buffer(client_id)
|
||||||
|
|
||||||
|
except UnicodeDecodeError as e:
|
||||||
|
self.log('ERROR', f"解码客户端 {client_id} 数据出错: {e}", 'CLIENT')
|
||||||
|
|
||||||
|
def _process_buffer(self, client_id):
|
||||||
|
"""处理客户端消息缓冲区"""
|
||||||
|
buffer = self.client_buffers.get(client_id, "")
|
||||||
|
|
||||||
|
# 按换行符分割消息
|
||||||
|
while '\n' in buffer:
|
||||||
|
message_end = buffer.find('\n')
|
||||||
|
message_text = buffer[:message_end].strip()
|
||||||
|
buffer = buffer[message_end + 1:]
|
||||||
|
|
||||||
|
# 处理非空消息
|
||||||
|
if message_text:
|
||||||
|
try:
|
||||||
|
# 解析JSON消息
|
||||||
|
message = json.loads(message_text)
|
||||||
|
self.log('INFO', f"从客户端 {client_id} 接收JSON: {message}", 'CLIENT')
|
||||||
|
|
||||||
|
# 处理消息 - 实现自定义逻辑
|
||||||
|
self._handle_message(client_id, message)
|
||||||
|
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
# 非JSON格式,作为原始文本处理
|
||||||
|
self.log('INFO', f"从客户端 {client_id} 接收文本: {message_text}", 'CLIENT')
|
||||||
|
self._handle_raw_message(client_id, message_text)
|
||||||
|
|
||||||
|
# 更新缓冲区
|
||||||
|
self.client_buffers[client_id] = buffer
|
||||||
|
|
||||||
|
def _handle_message(self, client_id, message):
|
||||||
|
"""处理JSON消息 - 可被子类覆盖以实现自定义逻辑"""
|
||||||
|
# 默认实现:简单回显
|
||||||
|
response = {
|
||||||
|
"type": "response",
|
||||||
|
"original": message,
|
||||||
|
"timestamp": time.time()
|
||||||
|
}
|
||||||
|
self.send_data(client_id, response)
|
||||||
|
|
||||||
|
def _handle_raw_message(self, client_id, message):
|
||||||
|
"""处理原始文本消息 - 可被子类覆盖以实现自定义逻辑"""
|
||||||
|
# 默认实现:简单回显
|
||||||
|
response = {
|
||||||
|
"type": "text_response",
|
||||||
|
"content": f"收到: {message}",
|
||||||
|
"timestamp": time.time()
|
||||||
|
}
|
||||||
|
self.send_data(client_id, response)
|
||||||
|
|
||||||
|
def send_data(self, client_id, data):
|
||||||
|
"""向指定客户端发送JSON数据"""
|
||||||
|
if client_id not in self.clients:
|
||||||
|
self.log('WARNING', f"客户端 {client_id} 不存在,无法发送数据", 'NETWORK')
|
||||||
|
return False
|
||||||
|
|
||||||
|
client_socket, _ = self.clients[client_id]
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 转换为JSON字符串,添加换行符
|
||||||
|
if isinstance(data, (dict, list)):
|
||||||
|
message = json.dumps(data) + '\n'
|
||||||
|
else:
|
||||||
|
message = str(data) + '\n'
|
||||||
|
|
||||||
|
# 发送数据
|
||||||
|
client_socket.sendall(message.encode('utf-8'))
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
self.log('ERROR', f"向客户端 {client_id} 发送数据时出错: {e}", 'NETWORK')
|
||||||
|
self._remove_client(client_id)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def broadcast(self, data, exclude=None):
|
||||||
|
"""向所有客户端广播消息,可选排除特定客户端"""
|
||||||
|
exclude = exclude or []
|
||||||
|
for client_id in list(self.clients.keys()):
|
||||||
|
if client_id not in exclude:
|
||||||
|
self.send_data(client_id, data)
|
||||||
|
|
||||||
|
def _remove_client(self, client_id):
|
||||||
|
"""断开并移除客户端连接"""
|
||||||
|
if client_id in self.clients:
|
||||||
|
client_socket, _ = self.clients[client_id]
|
||||||
|
try:
|
||||||
|
client_socket.close()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
del self.clients[client_id]
|
||||||
|
if client_id in self.client_buffers:
|
||||||
|
del self.client_buffers[client_id]
|
||||||
|
|
||||||
|
self.log('INFO', f"客户端 {client_id} 已移除", 'CLIENT')
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
"""停止服务器"""
|
||||||
|
self.running = False
|
||||||
|
|
||||||
|
# 关闭所有客户端连接
|
||||||
|
for client_id in list(self.clients.keys()):
|
||||||
|
self._remove_client(client_id)
|
||||||
|
|
||||||
|
# 关闭服务器套接字
|
||||||
|
if self.socket:
|
||||||
|
try:
|
||||||
|
self.socket.close()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.log('INFO', "服务器已停止", 'SERVER')
|
||||||
|
|
||||||
|
|
||||||
|
# 使用示例
|
||||||
|
if __name__ == "__main__":
|
||||||
|
try:
|
||||||
|
# 创建并启动服务器
|
||||||
|
server = TCPServer()
|
||||||
|
|
||||||
|
# 以阻塞方式启动服务器
|
||||||
|
server_thread = threading.Thread(target=server.start)
|
||||||
|
server_thread.daemon = True
|
||||||
|
server_thread.start()
|
||||||
|
|
||||||
|
# 运行直到按Ctrl+C
|
||||||
|
print("服务器运行中,按Ctrl+C停止...")
|
||||||
|
while True:
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("\n程序被用户中断")
|
||||||
|
if 'server' in locals():
|
||||||
|
server.stop()
|
||||||
|
sys.exit(0)
|
||||||
BIN
Server/__pycache__/QQEmailSend.cpython-313.pyc
Normal file
BIN
Server/__pycache__/TCPServer.cpython-313.pyc
Normal file
BIN
Server/__pycache__/web_admin.cpython-313.pyc
Normal file
28
Server/config/crop_data.json
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"测试作物": {"花费": 1, "生长时间": 3, "收益": 9999, "品质": "普通", "描述": "测试作物", "耐候性": 10, "等级": 1, "经验": 999},
|
||||||
|
"小麦": {"花费": 120, "生长时间": 120, "收益": 100, "品质": "普通", "描述": "基础作物,品质较低,适合新手种植", "耐候性": 10, "等级": 1, "经验": 10},
|
||||||
|
"稻谷": {"花费": 100, "生长时间": 240, "收益": 120, "品质": "普通", "描述": "适合大规模种植的基础作物", "耐候性": 10, "等级": 1, "经验": 10},
|
||||||
|
"玉米": {"花费": 70, "生长时间": 600, "收益": 90, "品质": "普通", "描述": "营养丰富的优良作物,适合稍有经验的玩家", "耐候性": 15, "等级": 2, "经验": 15},
|
||||||
|
"土豆": {"花费": 75, "生长时间": 360, "收益": 90, "品质": "普通", "描述": "容易种植的耐寒作物", "耐候性": 12, "等级": 1, "经验": 10},
|
||||||
|
"胡萝卜": {"花费": 60, "生长时间": 480, "收益": 80, "品质": "普通", "描述": "适合新手的健康作物", "耐候性": 12, "等级": 1, "经验": 10},
|
||||||
|
"草莓": {"花费": 120, "生长时间": 960, "收益": 150, "品质": "优良", "描述": "营养丰富的果实,收益不错", "耐候性": 14, "等级": 2, "经验": 20},
|
||||||
|
"番茄": {"花费": 100, "生长时间": 720, "收益": 130, "品质": "优良", "描述": "常见作物,适合小规模种植", "耐候性": 12, "等级": 2, "经验": 15},
|
||||||
|
"大豆": {"花费": 90, "生长时间": 840, "收益": 110, "品质": "优良", "描述": "富含蛋白质的基础作物", "耐候性": 11, "等级": 2, "经验": 12},
|
||||||
|
"蓝莓": {"花费": 150, "生长时间": 1200, "收益": 200, "品质": "稀有", "描述": "较为稀有的作物,市场价值较高", "耐候性": 18, "等级": 3, "经验": 25},
|
||||||
|
"洋葱": {"花费": 85, "生长时间": 600, "收益": 105, "品质": "稀有", "描述": "烹饪常用的作物,适合中级种植", "耐候性": 10, "等级": 2, "经验": 10},
|
||||||
|
"南瓜": {"花费": 180, "生长时间": 1440, "收益": 250, "品质": "稀有", "描述": "秋季收获的高收益作物", "耐候性": 20, "等级": 4, "经验": 30},
|
||||||
|
"葡萄": {"花费": 200, "生长时间": 1200, "收益": 300, "品质": "稀有", "描述": "需要特殊管理的高收益作物", "耐候性": 15, "等级": 4, "经验": 35},
|
||||||
|
"柿子": {"花费": 160, "生长时间": 1080, "收益": 240, "品质": "稀有", "描述": "富含营养的秋季作物", "耐候性": 18, "等级": 3, "经验": 28},
|
||||||
|
"花椰菜": {"花费": 130, "生长时间": 960, "收益": 170, "品质": "稀有", "描述": "耐寒的高品质作物,适合经验丰富的玩家", "耐候性": 17, "等级": 3, "经验": 22},
|
||||||
|
"芦笋": {"花费": 200, "生长时间": 1560, "收益": 280, "品质": "稀有", "描述": "市场需求量高的稀有作物", "耐候性": 15, "等级": 4, "经验": 30},
|
||||||
|
"香草": {"花费": 250, "生长时间": 1800, "收益": 400, "品质": "史诗", "描述": "非常稀有且收益极高的作物", "耐候性": 22, "等级": 5, "经验": 40},
|
||||||
|
"西瓜": {"花费": 240, "生长时间": 2400, "收益": 420, "品质": "史诗", "描述": "夏季丰产的高价值作物", "耐候性": 21, "等级": 5, "经验": 45},
|
||||||
|
"甜菜": {"花费": 220, "生长时间": 2160, "收益": 350, "品质": "史诗", "描述": "营养丰富的根茎作物,收益较高", "耐候性": 20, "等级": 5, "经验": 38},
|
||||||
|
"甘蔗": {"花费": 260, "生长时间": 3000, "收益": 450, "品质": "史诗", "描述": "需要充足水源的高价值作物", "耐候性": 18, "等级": 5, "经验": 50},
|
||||||
|
"龙果": {"花费": 400, "生长时间": 4800, "收益": 600, "品质": "传奇", "描述": "极为稀有的热带作物,产量和价值都极高", "耐候性": 25, "等级": 6, "经验": 60},
|
||||||
|
"松露": {"花费": 500, "生长时间": 7200, "收益": 700, "品质": "传奇", "描述": "极其珍贵的地下作物,市场价格极高", "耐候性": 23, "等级": 7, "经验": 80},
|
||||||
|
"人参": {"花费": 450, "生长时间": 6600, "收益": 650, "品质": "传奇", "描述": "需要耐心等待的珍贵药材", "耐候性": 22, "等级": 6, "经验": 75},
|
||||||
|
"富贵竹": {"花费": 450, "生长时间": 6600, "收益": 650, "品质": "传奇", "描述": "需要耐心等待的珍贵药材", "耐候性": 22, "等级": 6, "经验": 75},
|
||||||
|
"芦荟": {"花费": 450, "生长时间": 6600, "收益": 650, "品质": "传奇", "描述": "需要耐心等待的珍贵药材", "耐候性": 22, "等级": 6, "经验": 75},
|
||||||
|
"金橘": {"花费": 420, "生长时间": 4800, "收益": 620, "品质": "传奇", "描述": "少见的耐寒果树,市场需求量极大", "耐候性": 26, "等级": 7, "经验": 70}
|
||||||
|
}
|
||||||
32
Server/config/crop_data.json.backup
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
|
||||||
|
"测试作物": {"花费": 1, "生长时间": 3, "收益": 9999, "品质": "普通", "描述": "测试作物", "耐候性": 10, "等级": 1, "经验": 999},
|
||||||
|
|
||||||
|
"小麦": {"花费": 120, "生长时间": 120, "收益": 100, "品质": "普通", "描述": "基础作物,品质较低,适合新手种植", "耐候性": 10, "等级": 1, "经验": 10},
|
||||||
|
"稻谷": {"花费": 100, "生长时间": 240, "收益": 120, "品质": "普通", "描述": "适合大规模种植的基础作物", "耐候性": 10, "等级": 1, "经验": 10},
|
||||||
|
"玉米": {"花费": 70, "生长时间": 600, "收益": 90, "品质": "普通", "描述": "营养丰富的优良作物,适合稍有经验的玩家", "耐候性": 15, "等级": 2, "经验": 15},
|
||||||
|
"土豆": {"花费": 75, "生长时间": 360, "收益": 90, "品质": "普通", "描述": "容易种植的耐寒作物", "耐候性": 12, "等级": 1, "经验": 10},
|
||||||
|
"胡萝卜": {"花费": 60, "生长时间": 480, "收益": 80, "品质": "普通", "描述": "适合新手的健康作物", "耐候性": 12, "等级": 1, "经验": 10},
|
||||||
|
|
||||||
|
"草莓": {"花费": 120, "生长时间": 960, "收益": 150, "品质": "优良", "描述": "营养丰富的果实,收益不错", "耐候性": 14, "等级": 2, "经验": 20},
|
||||||
|
"番茄": {"花费": 100, "生长时间": 720, "收益": 130, "品质": "优良", "描述": "常见作物,适合小规模种植", "耐候性": 12, "等级": 2, "经验": 15},
|
||||||
|
"大豆": {"花费": 90, "生长时间": 840, "收益": 110, "品质": "优良", "描述": "富含蛋白质的基础作物", "耐候性": 11, "等级": 2, "经验": 12},
|
||||||
|
|
||||||
|
"蓝莓": {"花费": 150, "生长时间": 1200, "收益": 200, "品质": "稀有", "描述": "较为稀有的作物,市场价值较高", "耐候性": 18, "等级": 3, "经验": 25},
|
||||||
|
"洋葱": {"花费": 85, "生长时间": 600, "收益": 105, "品质": "稀有", "描述": "烹饪常用的作物,适合中级种植", "耐候性": 10, "等级": 2, "经验": 10},
|
||||||
|
"南瓜": {"花费": 180, "生长时间": 1440, "收益": 250, "品质": "稀有", "描述": "秋季收获的高收益作物", "耐候性": 20, "等级": 4, "经验": 30},
|
||||||
|
"葡萄": {"花费": 200, "生长时间": 1200, "收益": 300, "品质": "稀有", "描述": "需要特殊管理的高收益作物", "耐候性": 15, "等级": 4, "经验": 35},
|
||||||
|
"柿子": {"花费": 160, "生长时间": 1080, "收益": 240, "品质": "稀有", "描述": "富含营养的秋季作物", "耐候性": 18, "等级": 3, "经验": 28},
|
||||||
|
"花椰菜": {"花费": 130, "生长时间": 960, "收益": 170, "品质": "稀有", "描述": "耐寒的高品质作物,适合经验丰富的玩家", "耐候性": 17, "等级": 3, "经验": 22},
|
||||||
|
"芦笋": {"花费": 200, "生长时间": 1560, "收益": 280, "品质": "稀有", "描述": "市场需求量高的稀有作物", "耐候性": 15, "等级": 4, "经验": 30},
|
||||||
|
|
||||||
|
"香草": {"花费": 250, "生长时间": 1800, "收益": 400, "品质": "史诗", "描述": "非常稀有且收益极高的作物", "耐候性": 22, "等级": 5, "经验": 40},
|
||||||
|
"西瓜": {"花费": 240, "生长时间": 2400, "收益": 420, "品质": "史诗", "描述": "夏季丰产的高价值作物", "耐候性": 21, "等级": 5, "经验": 45},
|
||||||
|
"甜菜": {"花费": 220, "生长时间": 2160, "收益": 350, "品质": "史诗", "描述": "营养丰富的根茎作物,收益较高", "耐候性": 20, "等级": 5, "经验": 38},
|
||||||
|
"甘蔗": {"花费": 260, "生长时间": 3000, "收益": 450, "品质": "史诗", "描述": "需要充足水源的高价值作物", "耐候性": 18, "等级": 5, "经验": 50},
|
||||||
|
|
||||||
|
"龙果": {"花费": 400, "生长时间": 4800, "收益": 600, "品质": "传奇", "描述": "极为稀有的热带作物,产量和价值都极高", "耐候性": 25, "等级": 6, "经验": 60},
|
||||||
|
"松露": {"花费": 500, "生长时间": 7200, "收益": 700, "品质": "传奇", "描述": "极其珍贵的地下作物,市场价格极高", "耐候性": 23, "等级": 7, "经验": 80},
|
||||||
|
"人参": {"花费": 450, "生长时间": 6600, "收益": 650, "品质": "传奇", "描述": "需要耐心等待的珍贵药材", "耐候性": 22, "等级": 6, "经验": 75},
|
||||||
|
"金橘": {"花费": 420, "生长时间": 4800, "收益": 620, "品质": "传奇", "描述": "少见的耐寒果树,市场需求量极大", "耐候性": 26, "等级": 7, "经验": 70}
|
||||||
|
}
|
||||||
64
Server/config/initial_player_data_template.json
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
{
|
||||||
|
"experience": 0,
|
||||||
|
"level": 1,
|
||||||
|
"money": 1000,
|
||||||
|
"farm_name": "农场",
|
||||||
|
"user_name": "shumengya",
|
||||||
|
"player_name": "玩家昵称",
|
||||||
|
"user_password": "0123456789",
|
||||||
|
"last_login_time": "2025年12时09分35秒",
|
||||||
|
"total_login_time": "0时0分0秒",
|
||||||
|
"farm_lots": [
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": true, "is_planted": false, "max_grow_time": 3},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": true, "is_planted": false, "max_grow_time": 3},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": true, "is_planted": false, "max_grow_time": 3},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": true, "is_planted": false, "max_grow_time": 3},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": true, "is_planted": false, "max_grow_time": 3},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
|
||||||
|
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5}
|
||||||
|
],
|
||||||
|
"player_bag": []
|
||||||
|
}
|
||||||
6
Server/config/verification_codes.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"1232132": {
|
||||||
|
"code": "5UFH2Z",
|
||||||
|
"expire_at": 1748005553.6155243
|
||||||
|
}
|
||||||
|
}
|
||||||
124
Server/deployment_guide.md
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
# 萌芽农场游戏服务器部署指南
|
||||||
|
|
||||||
|
## 系统要求
|
||||||
|
- Python 3.7 或更高版本
|
||||||
|
- 稳定的互联网连接
|
||||||
|
- 建议:2GB+ 内存,足够的磁盘空间存储玩家数据
|
||||||
|
|
||||||
|
## 安装步骤
|
||||||
|
|
||||||
|
### 1. 准备环境
|
||||||
|
```bash
|
||||||
|
# 在服务器上创建项目文件夹
|
||||||
|
mkdir MengYaFarm
|
||||||
|
cd MengYaFarm
|
||||||
|
|
||||||
|
# 克隆或上传服务器代码到此文件夹
|
||||||
|
# (手动上传文件或使用Git)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 安装依赖
|
||||||
|
```bash
|
||||||
|
# 创建虚拟环境(推荐)
|
||||||
|
python -m venv venv
|
||||||
|
# Linux/Mac激活虚拟环境
|
||||||
|
source venv/bin/activate
|
||||||
|
# Windows激活虚拟环境
|
||||||
|
# venv\Scripts\activate
|
||||||
|
|
||||||
|
# 安装依赖
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 配置服务器
|
||||||
|
1. 确保已创建所需文件夹:
|
||||||
|
```bash
|
||||||
|
mkdir -p game_saves config
|
||||||
|
```
|
||||||
|
|
||||||
|
2. 创建初始玩家数据模板 (如果尚未存在):
|
||||||
|
```bash
|
||||||
|
# 在config目录中创建initial_player_data_template.json
|
||||||
|
```
|
||||||
|
|
||||||
|
3. 检查 TCPGameServer.py 中的服务器地址和端口配置:
|
||||||
|
```python
|
||||||
|
server_host: str = "0.0.0.0" # 使用0.0.0.0允许所有网络接口访问
|
||||||
|
server_port: int = 9000 # 确保此端口在防火墙中开放
|
||||||
|
```
|
||||||
|
|
||||||
|
4. 如需使用QQ邮箱验证功能,请在QQEmailSend.py中更新发件邮箱配置:
|
||||||
|
```python
|
||||||
|
SENDER_EMAIL = 'your_qq_number@qq.com' # 发件人邮箱
|
||||||
|
SENDER_AUTH_CODE = 'your_auth_code' # 授权码
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 启动服务器
|
||||||
|
```bash
|
||||||
|
# 直接启动
|
||||||
|
python Server/TCPGameServer.py
|
||||||
|
|
||||||
|
# 或使用nohup在后台运行
|
||||||
|
nohup python Server/TCPGameServer.py > server.log 2>&1 &
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. 监控与维护
|
||||||
|
- 服务器日志会输出到控制台或server.log
|
||||||
|
- 玩家数据存储在game_saves文件夹中
|
||||||
|
- 定期备份game_saves文件夹以防数据丢失
|
||||||
|
|
||||||
|
### 6. 防火墙配置
|
||||||
|
确保服务器防火墙允许TCP 9000端口的入站连接:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Ubuntu/Debian
|
||||||
|
sudo ufw allow 9000/tcp
|
||||||
|
|
||||||
|
# CentOS/RHEL
|
||||||
|
sudo firewall-cmd --permanent --add-port=9000/tcp
|
||||||
|
sudo firewall-cmd --reload
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7. 系统服务配置 (可选)
|
||||||
|
可以创建systemd服务使服务器自动启动:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 创建服务文件
|
||||||
|
sudo nano /etc/systemd/system/mengyafarm.service
|
||||||
|
|
||||||
|
# 添加以下内容
|
||||||
|
[Unit]
|
||||||
|
Description=MengYa Farm Game Server
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=your_username
|
||||||
|
WorkingDirectory=/path/to/MengYaFarm
|
||||||
|
ExecStart=/path/to/MengYaFarm/venv/bin/python /path/to/MengYaFarm/Server/TCPGameServer.py
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=5
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
|
||||||
|
# 启用并启动服务
|
||||||
|
sudo systemctl enable mengyafarm.service
|
||||||
|
sudo systemctl start mengyafarm.service
|
||||||
|
```
|
||||||
|
|
||||||
|
## 常见问题
|
||||||
|
|
||||||
|
### 服务器无法启动
|
||||||
|
- 检查Python版本
|
||||||
|
- 确认所有依赖已正确安装
|
||||||
|
- 检查端口是否被占用
|
||||||
|
|
||||||
|
### 客户端无法连接
|
||||||
|
- 确认服务器IP和端口配置正确
|
||||||
|
- 检查防火墙设置
|
||||||
|
- 验证网络连接
|
||||||
|
|
||||||
|
### 发送验证码失败
|
||||||
|
- 检查QQ邮箱和授权码设置
|
||||||
|
- 确认SMTP服务器可访问
|
||||||
@@ -1,5 +1,13 @@
|
|||||||
{
|
{
|
||||||
"experience": 0,
|
"experience": 0,
|
||||||
|
"level": 1,
|
||||||
|
"money": 1000,
|
||||||
|
"farm_name": "柚大青の小农场",
|
||||||
|
"player_name": "柚大青",
|
||||||
|
"user_name": "2143323382",
|
||||||
|
"user_password": "tyh@19900420",
|
||||||
|
"last_login_time": "2025年05月25日16时43分38秒",
|
||||||
|
"total_login_time": "0时0分29秒",
|
||||||
"farm_lots": [
|
"farm_lots": [
|
||||||
{
|
{
|
||||||
"crop_type": "",
|
"crop_type": "",
|
||||||
@@ -285,7 +293,7 @@
|
|||||||
"crop_type": "",
|
"crop_type": "",
|
||||||
"grow_time": 0,
|
"grow_time": 0,
|
||||||
"is_dead": false,
|
"is_dead": false,
|
||||||
"is_diged": true,
|
"is_diged": false,
|
||||||
"is_planted": false,
|
"is_planted": false,
|
||||||
"max_grow_time": 5
|
"max_grow_time": 5
|
||||||
},
|
},
|
||||||
@@ -293,7 +301,7 @@
|
|||||||
"crop_type": "",
|
"crop_type": "",
|
||||||
"grow_time": 0,
|
"grow_time": 0,
|
||||||
"is_dead": false,
|
"is_dead": false,
|
||||||
"is_diged": true,
|
"is_diged": false,
|
||||||
"is_planted": false,
|
"is_planted": false,
|
||||||
"max_grow_time": 5
|
"max_grow_time": 5
|
||||||
},
|
},
|
||||||
@@ -301,7 +309,7 @@
|
|||||||
"crop_type": "",
|
"crop_type": "",
|
||||||
"grow_time": 0,
|
"grow_time": 0,
|
||||||
"is_dead": false,
|
"is_dead": false,
|
||||||
"is_diged": true,
|
"is_diged": false,
|
||||||
"is_planted": false,
|
"is_planted": false,
|
||||||
"max_grow_time": 5
|
"max_grow_time": 5
|
||||||
},
|
},
|
||||||
@@ -309,7 +317,7 @@
|
|||||||
"crop_type": "",
|
"crop_type": "",
|
||||||
"grow_time": 0,
|
"grow_time": 0,
|
||||||
"is_dead": false,
|
"is_dead": false,
|
||||||
"is_diged": true,
|
"is_diged": false,
|
||||||
"is_planted": false,
|
"is_planted": false,
|
||||||
"max_grow_time": 5
|
"max_grow_time": 5
|
||||||
},
|
},
|
||||||
@@ -317,14 +325,90 @@
|
|||||||
"crop_type": "",
|
"crop_type": "",
|
||||||
"grow_time": 0,
|
"grow_time": 0,
|
||||||
"is_dead": false,
|
"is_dead": false,
|
||||||
"is_diged": true,
|
"is_diged": false,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": false,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": false,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": false,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": false,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": false,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": false,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": false,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": false,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": false,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": false,
|
||||||
"is_planted": false,
|
"is_planted": false,
|
||||||
"max_grow_time": 5
|
"max_grow_time": 5
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"farm_name": "1234",
|
"player_bag": []
|
||||||
"level": 0,
|
|
||||||
"money": 1000,
|
|
||||||
"user_name": "1242423",
|
|
||||||
"user_password": "1234"
|
|
||||||
}
|
}
|
||||||
370
Server/game_saves/3205788256.json.backup
Normal file
@@ -0,0 +1,370 @@
|
|||||||
|
{
|
||||||
|
"experience": 508,
|
||||||
|
"farm_lots": [
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": true,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": true,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "花椰菜",
|
||||||
|
"grow_time": 960,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": true,
|
||||||
|
"is_planted": true,
|
||||||
|
"max_grow_time": 960
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": true,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": true,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": true,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": true,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": true,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": true,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 4800
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": true,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 1200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": false,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": false,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": true,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": true,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 4800
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": true,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 2400
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": true,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": true,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": true,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": true,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 720
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": true,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 2400
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": false,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": false,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": true,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": true,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": true,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 1200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": true,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 1800
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": true,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 600
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": true,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 480
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": true,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 960
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": true,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 960
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": false,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": false,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": false,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": true,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": true,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": true,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 2400
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": true,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": true,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": true,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 960
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"crop_type": "",
|
||||||
|
"grow_time": 0,
|
||||||
|
"is_dead": false,
|
||||||
|
"is_diged": true,
|
||||||
|
"is_planted": false,
|
||||||
|
"max_grow_time": 960
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"player_bag": [
|
||||||
|
{
|
||||||
|
"count": 18,
|
||||||
|
"name": "测试作物",
|
||||||
|
"quality": "普通"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"count": 253,
|
||||||
|
"name": "金橘",
|
||||||
|
"quality": "传奇"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "胡萝卜",
|
||||||
|
"quality": "普通",
|
||||||
|
"count": 17
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "花椰菜",
|
||||||
|
"quality": "稀有",
|
||||||
|
"count": 11
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "芦笋",
|
||||||
|
"quality": "稀有",
|
||||||
|
"count": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "草莓",
|
||||||
|
"quality": "优良",
|
||||||
|
"count": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "香草",
|
||||||
|
"quality": "史诗",
|
||||||
|
"count": 3
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"farm_name": "树萌芽の大农场",
|
||||||
|
"player_name": "树萌芽",
|
||||||
|
"level": 36,
|
||||||
|
"money": 40013,
|
||||||
|
"last_login_time": "2025年05月24日11时51分35秒",
|
||||||
|
"total_login_time": "100时33分38秒",
|
||||||
|
"user_name": "3205788256",
|
||||||
|
"user_password": "tyh@19900420"
|
||||||
|
}
|
||||||
4
Server/requirements.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# Game Server Dependencies
|
||||||
|
colorama>=0.4.6 # For colored terminal output
|
||||||
|
# Email Requirements
|
||||||
|
# Standard library dependencies are not listed (socket, threading, json, etc.)
|
||||||
1
Shader/CropProgress.gdshader.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://cmrhmjs8fohm
|
||||||
4
Temp/login.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"user_name":"",
|
||||||
|
"password":""
|
||||||
|
}
|
||||||
46
Test/HTTPTextureRectDemo.gd
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
extends Control
|
||||||
|
|
||||||
|
@onready var http_texture_rect = $VBoxContainer/ImageContainer/HTTPTextureRect
|
||||||
|
@onready var url_input = $VBoxContainer/HBoxContainer/URLInput
|
||||||
|
@onready var load_url_button = $VBoxContainer/HBoxContainer/LoadURLButton
|
||||||
|
@onready var qq_input = $VBoxContainer/HBoxContainer2/QQInput
|
||||||
|
@onready var load_qq_button = $VBoxContainer/HBoxContainer2/LoadQQButton
|
||||||
|
@onready var status_label = $VBoxContainer/StatusLabel
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
# 设置默认URL和QQ号
|
||||||
|
url_input.text = "https://picsum.photos/200"
|
||||||
|
qq_input.text = "3205788256"
|
||||||
|
|
||||||
|
# 连接按钮信号
|
||||||
|
load_url_button.pressed.connect(_on_load_url_button_pressed)
|
||||||
|
load_qq_button.pressed.connect(_on_load_qq_button_pressed)
|
||||||
|
|
||||||
|
# 连接HTTP纹理矩形的信号
|
||||||
|
http_texture_rect.loading_started.connect(_on_loading_started)
|
||||||
|
http_texture_rect.loading_finished.connect(_on_loading_finished)
|
||||||
|
|
||||||
|
func _on_load_url_button_pressed():
|
||||||
|
var url = url_input.text.strip_edges()
|
||||||
|
if url.is_empty():
|
||||||
|
status_label.text = "状态: URL不能为空"
|
||||||
|
return
|
||||||
|
|
||||||
|
http_texture_rect.load_from_url(url)
|
||||||
|
|
||||||
|
func _on_load_qq_button_pressed():
|
||||||
|
var qq_number = qq_input.text.strip_edges()
|
||||||
|
if qq_number.is_empty() or not qq_number.is_valid_int():
|
||||||
|
status_label.text = "状态: 无效的QQ号"
|
||||||
|
return
|
||||||
|
|
||||||
|
http_texture_rect.load_qq_avatar(qq_number)
|
||||||
|
|
||||||
|
func _on_loading_started():
|
||||||
|
status_label.text = "状态: 正在加载..."
|
||||||
|
|
||||||
|
func _on_loading_finished(success: bool):
|
||||||
|
if success:
|
||||||
|
status_label.text = "状态: 加载成功"
|
||||||
|
else:
|
||||||
|
status_label.text = "状态: 加载失败"
|
||||||
1
Test/HTTPTextureRectDemo.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://cgylg6qxwg1f0
|
||||||
76
Test/HTTPTextureRectDemo.tscn
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
[gd_scene load_steps=3 format=3 uid="uid://dyh0q82ytbk3v"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://cgylg6qxwg1f0" path="res://Test/HTTPTextureRectDemo.gd" id="1_vgcbi"]
|
||||||
|
[ext_resource type="Script" uid="uid://0d2j5m6j2ema" path="res://Components/HTTPTextureRect.gd" id="2_pujh8"]
|
||||||
|
|
||||||
|
[node name="HTTPTextureRectDemo" type="Control"]
|
||||||
|
layout_mode = 3
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
script = ExtResource("1_vgcbi")
|
||||||
|
|
||||||
|
[node name="VBoxContainer" type="VBoxContainer" parent="."]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
alignment = 1
|
||||||
|
|
||||||
|
[node name="Label" type="Label" parent="VBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "HTTP图像加载演示"
|
||||||
|
horizontal_alignment = 1
|
||||||
|
|
||||||
|
[node name="ImageContainer" type="CenterContainer" parent="VBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_vertical = 3
|
||||||
|
|
||||||
|
[node name="HTTPTextureRect" type="TextureRect" parent="VBoxContainer/ImageContainer"]
|
||||||
|
custom_minimum_size = Vector2(200, 200)
|
||||||
|
layout_mode = 2
|
||||||
|
stretch_mode = 5
|
||||||
|
script = ExtResource("2_pujh8")
|
||||||
|
|
||||||
|
[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
alignment = 1
|
||||||
|
|
||||||
|
[node name="URLLabel" type="Label" parent="VBoxContainer/HBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "URL:"
|
||||||
|
|
||||||
|
[node name="URLInput" type="LineEdit" parent="VBoxContainer/HBoxContainer"]
|
||||||
|
custom_minimum_size = Vector2(300, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
placeholder_text = "输入HTTP图像URL"
|
||||||
|
|
||||||
|
[node name="LoadURLButton" type="Button" parent="VBoxContainer/HBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "加载图像"
|
||||||
|
|
||||||
|
[node name="HBoxContainer2" type="HBoxContainer" parent="VBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
alignment = 1
|
||||||
|
|
||||||
|
[node name="QQLabel" type="Label" parent="VBoxContainer/HBoxContainer2"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "QQ号:"
|
||||||
|
|
||||||
|
[node name="QQInput" type="LineEdit" parent="VBoxContainer/HBoxContainer2"]
|
||||||
|
custom_minimum_size = Vector2(200, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
placeholder_text = "输入QQ号"
|
||||||
|
|
||||||
|
[node name="LoadQQButton" type="Button" parent="VBoxContainer/HBoxContainer2"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "加载QQ头像"
|
||||||
|
|
||||||
|
[node name="StatusLabel" type="Label" parent="VBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "状态: 等待加载"
|
||||||
|
horizontal_alignment = 1
|
||||||
31
android/build/AndroidManifest.xml
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="mengyafarm.smy"
|
||||||
|
android:versionCode="1"
|
||||||
|
android:versionName="1.0">
|
||||||
|
|
||||||
|
<!-- 网络权限 -->
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||||
|
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
|
||||||
|
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
|
||||||
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
|
|
||||||
|
<application
|
||||||
|
android:label="萌芽农场"
|
||||||
|
android:icon="@mipmap/icon"
|
||||||
|
android:networkSecurityConfig="@xml/network_security_config"
|
||||||
|
android:usesCleartextTraffic="true">
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name="org.godotengine.godot.GodotApp"
|
||||||
|
android:theme="@style/GodotAppMainTheme"
|
||||||
|
android:exported="true">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
</application>
|
||||||
|
</manifest>
|
||||||
15
android/build/res/xml/network_security_config.xml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<network-security-config>
|
||||||
|
<domain-config cleartextTrafficPermitted="true">
|
||||||
|
<domain includeSubdomains="true">192.168.1.110</domain>
|
||||||
|
<domain includeSubdomains="true">127.0.0.1</domain>
|
||||||
|
<domain includeSubdomains="true">localhost</domain>
|
||||||
|
<!-- 添加你的服务器域名 -->
|
||||||
|
</domain-config>
|
||||||
|
<!-- 允许所有明文流量(测试用,生产环境不推荐) -->
|
||||||
|
<base-config cleartextTrafficPermitted="true">
|
||||||
|
<trust-anchors>
|
||||||
|
<certificates src="system"/>
|
||||||
|
</trust-anchors>
|
||||||
|
</base-config>
|
||||||
|
</network-security-config>
|
||||||
BIN
assets/GUI/农场名称.png
Normal file
|
After Width: | Height: | Size: 915 KiB |
34
assets/GUI/农场名称.png.import
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://clvhlo0mc3e7v"
|
||||||
|
path="res://.godot/imported/农场名称.png-1101845a74d5743a7bffe958ff6ddc5d.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/GUI/农场名称.png"
|
||||||
|
dest_files=["res://.godot/imported/农场名称.png-1101845a74d5743a7bffe958ff6ddc5d.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
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=1
|
||||||
BIN
assets/GUI/小提示.png
Normal file
|
After Width: | Height: | Size: 436 KiB |
34
assets/GUI/小提示.png.import
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://ckqy5yq2ltax6"
|
||||||
|
path="res://.godot/imported/小提示.png-af968d997d0bd20225a871f0c885a204.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/GUI/小提示.png"
|
||||||
|
dest_files=["res://.godot/imported/小提示.png-af968d997d0bd20225a871f0c885a204.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
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=1
|
||||||
BIN
assets/GUI/服务器连接状态.png
Normal file
|
After Width: | Height: | Size: 943 KiB |
34
assets/GUI/服务器连接状态.png.import
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://by5qcip8tel1f"
|
||||||
|
path="res://.godot/imported/服务器连接状态.png-a55f4e8174e2bbf848a6ffd7e554646c.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/GUI/服务器连接状态.png"
|
||||||
|
dest_files=["res://.godot/imported/服务器连接状态.png-a55f4e8174e2bbf848a6ffd7e554646c.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
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=1
|
||||||
BIN
assets/GUI/玩家昵称.png
Normal file
|
After Width: | Height: | Size: 881 KiB |
34
assets/GUI/玩家昵称.png.import
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://b4wi8yusmbbu8"
|
||||||
|
path="res://.godot/imported/玩家昵称.png-1608f19521048d10cd2802563f027b42.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/GUI/玩家昵称.png"
|
||||||
|
dest_files=["res://.godot/imported/玩家昵称.png-1608f19521048d10cd2802563f027b42.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
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=1
|
||||||
BIN
assets/GUI/等级.png
Normal file
|
After Width: | Height: | Size: 1.0 MiB |
34
assets/GUI/等级.png.import
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://c87kujyuxnx2s"
|
||||||
|
path="res://.godot/imported/等级.png-571abcf9eb5b920577c180e41ac1eb10.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/GUI/等级.png"
|
||||||
|
dest_files=["res://.godot/imported/等级.png-571abcf9eb5b920577c180e41ac1eb10.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
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=1
|
||||||
BIN
assets/GUI/经验球.png
Normal file
|
After Width: | Height: | Size: 784 KiB |
34
assets/GUI/经验球.png.import
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://cwloibftcmp76"
|
||||||
|
path="res://.godot/imported/经验球.png-e7ec652ceb5e77fccd6dbb0e0fbd7dcd.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/GUI/经验球.png"
|
||||||
|
dest_files=["res://.godot/imported/经验球.png-e7ec652ceb5e77fccd6dbb0e0fbd7dcd.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
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=1
|
||||||
BIN
assets/GUI/钱币.png
Normal file
|
After Width: | Height: | Size: 786 KiB |
34
assets/GUI/钱币.png.import
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://c6i00d35fnl12"
|
||||||
|
path="res://.godot/imported/钱币.png-f6b277b749721715d601deace45b6e54.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/GUI/钱币.png"
|
||||||
|
dest_files=["res://.godot/imported/钱币.png-f6b277b749721715d601deace45b6e54.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
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=1
|
||||||
BIN
assets/Test/g.jpg
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
34
assets/Test/g.jpg.import
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://y4gfkorl1g4f"
|
||||||
|
path="res://.godot/imported/g.jpg-9129d5433d811daa5a9462e7b8751277.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/Test/g.jpg"
|
||||||
|
dest_files=["res://.godot/imported/g.jpg-9129d5433d811daa5a9462e7b8751277.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
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=1
|
||||||
BIN
assets/Test/g.png
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
34
assets/Test/g.png.import
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://c3vng0nal1wr8"
|
||||||
|
path="res://.godot/imported/g.png-3604f64f9277adc00d21f2056f418ce3.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/Test/g.png"
|
||||||
|
dest_files=["res://.godot/imported/g.png-3604f64f9277adc00d21f2056f418ce3.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
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=1
|
||||||
|
Before Width: | Height: | Size: 138 KiB After Width: | Height: | Size: 138 KiB |
34
assets/tu1.jpg.import
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://duoqp854aoeeg"
|
||||||
|
path="res://.godot/imported/tu1.jpg-c72e43aa0cb817520503d62990aab61d.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/tu1.jpg"
|
||||||
|
dest_files=["res://.godot/imported/tu1.jpg-c72e43aa0cb817520503d62990aab61d.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
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=1
|
||||||
BIN
assets/tu2.png
Normal file
|
After Width: | Height: | Size: 218 KiB |
34
assets/tu2.png.import
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://d046caoogul6g"
|
||||||
|
path="res://.godot/imported/tu2.png-5c1f2dd0cefd9388c2780247e294469e.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/tu2.png"
|
||||||
|
dest_files=["res://.godot/imported/tu2.png-5c1f2dd0cefd9388c2780247e294469e.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
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=1
|
||||||
BIN
assets/tu3.png
Normal file
|
After Width: | Height: | Size: 193 KiB |
34
assets/tu3.png.import
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://c4l0qn0p4yav8"
|
||||||
|
path="res://.godot/imported/tu3.png-53f50a01a12d698dc896d7552428cb9a.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/tu3.png"
|
||||||
|
dest_files=["res://.godot/imported/tu3.png-53f50a01a12d698dc896d7552428cb9a.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
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=1
|
||||||
BIN
assets/作物/人参/0.png
Normal file
|
After Width: | Height: | Size: 794 KiB |
34
assets/作物/人参/0.png.import
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://c461olyam2wi"
|
||||||
|
path="res://.godot/imported/0.png-f9b471f54a505f90386a524550e73d6f.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/作物/人参/0.png"
|
||||||
|
dest_files=["res://.godot/imported/0.png-f9b471f54a505f90386a524550e73d6f.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
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=1
|
||||||
BIN
assets/作物/人参/1.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |