添加小游戏面板
This commit is contained in:
@@ -1,7 +1,6 @@
|
|||||||
[gd_scene load_steps=3 format=3 uid="uid://ibl5wbbw3pwc"]
|
[gd_scene load_steps=2 format=3 uid="uid://ibl5wbbw3pwc"]
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://c6ylh1o2kgqth" path="res://CopyItems/item_crop.gd" id="1_e25nh"]
|
[ext_resource type="Script" uid="uid://c6ylh1o2kgqth" path="res://CopyItems/item_crop.gd" id="1_e25nh"]
|
||||||
[ext_resource type="Texture2D" uid="uid://bl48i2h22htyd" path="res://assets/作物/人参/收获物.webp" id="2_abhds"]
|
|
||||||
|
|
||||||
[node name="item_button" type="Button"]
|
[node name="item_button" type="Button"]
|
||||||
custom_minimum_size = Vector2(400, 400)
|
custom_minimum_size = Vector2(400, 400)
|
||||||
@@ -14,7 +13,6 @@ script = ExtResource("1_e25nh")
|
|||||||
[node name="CropImage" type="Sprite2D" parent="."]
|
[node name="CropImage" type="Sprite2D" parent="."]
|
||||||
position = Vector2(200, 200)
|
position = Vector2(200, 200)
|
||||||
scale = Vector2(1.5625, 1.5625)
|
scale = Vector2(1.5625, 1.5625)
|
||||||
texture = ExtResource("2_abhds")
|
|
||||||
|
|
||||||
[node name="Title" type="Label" parent="."]
|
[node name="Title" type="Label" parent="."]
|
||||||
layout_mode = 0
|
layout_mode = 0
|
||||||
|
|||||||
@@ -87,6 +87,15 @@ offset_bottom = 718.0
|
|||||||
theme_override_font_sizes/font_size = 30
|
theme_override_font_sizes/font_size = 30
|
||||||
text = "版本号:v1.0.1"
|
text = "版本号:v1.0.1"
|
||||||
|
|
||||||
|
[node name="GameVersionLabel2" type="Label" parent="GUI"]
|
||||||
|
layout_mode = 0
|
||||||
|
offset_left = 575.0
|
||||||
|
offset_top = 676.0
|
||||||
|
offset_right = 875.0
|
||||||
|
offset_bottom = 718.0
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "学习项目,偏休闲娱乐"
|
||||||
|
|
||||||
[node name="Developer" type="RichTextLabel" parent="GUI"]
|
[node name="Developer" type="RichTextLabel" parent="GUI"]
|
||||||
layout_mode = 0
|
layout_mode = 0
|
||||||
offset_left = 1194.0
|
offset_left = 1194.0
|
||||||
|
|||||||
@@ -505,7 +505,7 @@ texture = ExtResource("5_5b81d")
|
|||||||
expand_mode = 2
|
expand_mode = 2
|
||||||
|
|
||||||
[node name="tip" type="RichTextLabel" parent="UI/GUI/GameInfoHBox3"]
|
[node name="tip" type="RichTextLabel" parent="UI/GUI/GameInfoHBox3"]
|
||||||
custom_minimum_size = Vector2(300, 0)
|
custom_minimum_size = Vector2(500, 0)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||||
|
|||||||
@@ -48,11 +48,11 @@ size_flags_horizontal = 3
|
|||||||
size_flags_vertical = 3
|
size_flags_vertical = 3
|
||||||
columns = 5
|
columns = 5
|
||||||
|
|
||||||
[node name="VBox" type="VBoxContainer" parent="ScrollContainer/Grid"]
|
[node name="2048Game" type="VBoxContainer" parent="ScrollContainer/Grid"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
alignment = 1
|
alignment = 1
|
||||||
|
|
||||||
[node name="GameTitle" type="Label" parent="ScrollContainer/Grid/VBox"]
|
[node name="GameTitle" type="Label" parent="ScrollContainer/Grid/2048Game"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
theme_override_colors/font_shadow_color = Color(0, 0, 0, 1)
|
theme_override_colors/font_shadow_color = Color(0, 0, 0, 1)
|
||||||
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||||
@@ -61,24 +61,24 @@ theme_override_constants/shadow_offset_y = 3
|
|||||||
theme_override_constants/outline_size = 15
|
theme_override_constants/outline_size = 15
|
||||||
theme_override_constants/shadow_outline_size = 15
|
theme_override_constants/shadow_outline_size = 15
|
||||||
theme_override_font_sizes/font_size = 35
|
theme_override_font_sizes/font_size = 35
|
||||||
text = "贪吃蛇"
|
text = "2048"
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
vertical_alignment = 1
|
vertical_alignment = 1
|
||||||
|
|
||||||
[node name="TextureRect" type="TextureRect" parent="ScrollContainer/Grid/VBox"]
|
[node name="TextureRect" type="TextureRect" parent="ScrollContainer/Grid/2048Game"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
texture = ExtResource("2_wlugc")
|
texture = ExtResource("2_wlugc")
|
||||||
|
|
||||||
[node name="Button" type="Button" parent="ScrollContainer/Grid/VBox"]
|
[node name="2048GameButton" type="Button" parent="ScrollContainer/Grid/2048Game"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
theme_override_font_sizes/font_size = 30
|
theme_override_font_sizes/font_size = 30
|
||||||
text = "点击游玩"
|
text = "点击游玩"
|
||||||
|
|
||||||
[node name="VBox2" type="VBoxContainer" parent="ScrollContainer/Grid"]
|
[node name="PushBox" type="VBoxContainer" parent="ScrollContainer/Grid"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
alignment = 1
|
alignment = 1
|
||||||
|
|
||||||
[node name="GameTitle" type="Label" parent="ScrollContainer/Grid/VBox2"]
|
[node name="GameTitle" type="Label" parent="ScrollContainer/Grid/PushBox"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
theme_override_colors/font_shadow_color = Color(0, 0, 0, 1)
|
theme_override_colors/font_shadow_color = Color(0, 0, 0, 1)
|
||||||
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||||
@@ -87,24 +87,24 @@ theme_override_constants/shadow_offset_y = 3
|
|||||||
theme_override_constants/outline_size = 15
|
theme_override_constants/outline_size = 15
|
||||||
theme_override_constants/shadow_outline_size = 15
|
theme_override_constants/shadow_outline_size = 15
|
||||||
theme_override_font_sizes/font_size = 35
|
theme_override_font_sizes/font_size = 35
|
||||||
text = "贪吃蛇"
|
text = "推箱子"
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
vertical_alignment = 1
|
vertical_alignment = 1
|
||||||
|
|
||||||
[node name="TextureRect" type="TextureRect" parent="ScrollContainer/Grid/VBox2"]
|
[node name="TextureRect" type="TextureRect" parent="ScrollContainer/Grid/PushBox"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
texture = ExtResource("2_wlugc")
|
texture = ExtResource("2_wlugc")
|
||||||
|
|
||||||
[node name="Button" type="Button" parent="ScrollContainer/Grid/VBox2"]
|
[node name="PushBoxButton" type="Button" parent="ScrollContainer/Grid/PushBox"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
theme_override_font_sizes/font_size = 30
|
theme_override_font_sizes/font_size = 30
|
||||||
text = "点击游玩"
|
text = "点击游玩"
|
||||||
|
|
||||||
[node name="VBox3" type="VBoxContainer" parent="ScrollContainer/Grid"]
|
[node name="SnakeGame" type="VBoxContainer" parent="ScrollContainer/Grid"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
alignment = 1
|
alignment = 1
|
||||||
|
|
||||||
[node name="GameTitle" type="Label" parent="ScrollContainer/Grid/VBox3"]
|
[node name="GameTitle" type="Label" parent="ScrollContainer/Grid/SnakeGame"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
theme_override_colors/font_shadow_color = Color(0, 0, 0, 1)
|
theme_override_colors/font_shadow_color = Color(0, 0, 0, 1)
|
||||||
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||||
@@ -117,20 +117,20 @@ text = "贪吃蛇"
|
|||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
vertical_alignment = 1
|
vertical_alignment = 1
|
||||||
|
|
||||||
[node name="TextureRect" type="TextureRect" parent="ScrollContainer/Grid/VBox3"]
|
[node name="TextureRect" type="TextureRect" parent="ScrollContainer/Grid/SnakeGame"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
texture = ExtResource("2_wlugc")
|
texture = ExtResource("2_wlugc")
|
||||||
|
|
||||||
[node name="Button" type="Button" parent="ScrollContainer/Grid/VBox3"]
|
[node name="SnakeGameButton" type="Button" parent="ScrollContainer/Grid/SnakeGame"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
theme_override_font_sizes/font_size = 30
|
theme_override_font_sizes/font_size = 30
|
||||||
text = "点击游玩"
|
text = "点击游玩"
|
||||||
|
|
||||||
[node name="VBox4" type="VBoxContainer" parent="ScrollContainer/Grid"]
|
[node name="Tetris" type="VBoxContainer" parent="ScrollContainer/Grid"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
alignment = 1
|
alignment = 1
|
||||||
|
|
||||||
[node name="GameTitle" type="Label" parent="ScrollContainer/Grid/VBox4"]
|
[node name="GameTitle" type="Label" parent="ScrollContainer/Grid/Tetris"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
theme_override_colors/font_shadow_color = Color(0, 0, 0, 1)
|
theme_override_colors/font_shadow_color = Color(0, 0, 0, 1)
|
||||||
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||||
@@ -139,15 +139,15 @@ theme_override_constants/shadow_offset_y = 3
|
|||||||
theme_override_constants/outline_size = 15
|
theme_override_constants/outline_size = 15
|
||||||
theme_override_constants/shadow_outline_size = 15
|
theme_override_constants/shadow_outline_size = 15
|
||||||
theme_override_font_sizes/font_size = 35
|
theme_override_font_sizes/font_size = 35
|
||||||
text = "贪吃蛇"
|
text = "俄罗斯方块"
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
vertical_alignment = 1
|
vertical_alignment = 1
|
||||||
|
|
||||||
[node name="TextureRect" type="TextureRect" parent="ScrollContainer/Grid/VBox4"]
|
[node name="TextureRect" type="TextureRect" parent="ScrollContainer/Grid/Tetris"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
texture = ExtResource("2_wlugc")
|
texture = ExtResource("2_wlugc")
|
||||||
|
|
||||||
[node name="Button" type="Button" parent="ScrollContainer/Grid/VBox4"]
|
[node name="TetrisButton" type="Button" parent="ScrollContainer/Grid/Tetris"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
theme_override_font_sizes/font_size = 30
|
theme_override_font_sizes/font_size = 30
|
||||||
text = "点击游玩"
|
text = "点击游玩"
|
||||||
@@ -165,7 +165,7 @@ theme_override_constants/shadow_offset_y = 3
|
|||||||
theme_override_constants/outline_size = 15
|
theme_override_constants/outline_size = 15
|
||||||
theme_override_constants/shadow_outline_size = 15
|
theme_override_constants/shadow_outline_size = 15
|
||||||
theme_override_font_sizes/font_size = 35
|
theme_override_font_sizes/font_size = 35
|
||||||
text = "贪吃蛇"
|
text = "敬请期待"
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
vertical_alignment = 1
|
vertical_alignment = 1
|
||||||
|
|
||||||
@@ -177,3 +177,138 @@ texture = ExtResource("2_wlugc")
|
|||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
theme_override_font_sizes/font_size = 30
|
theme_override_font_sizes/font_size = 30
|
||||||
text = "点击游玩"
|
text = "点击游玩"
|
||||||
|
|
||||||
|
[node name="VBox6" type="VBoxContainer" parent="ScrollContainer/Grid"]
|
||||||
|
layout_mode = 2
|
||||||
|
alignment = 1
|
||||||
|
|
||||||
|
[node name="GameTitle" type="Label" parent="ScrollContainer/Grid/VBox6"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_colors/font_shadow_color = Color(0, 0, 0, 1)
|
||||||
|
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||||
|
theme_override_constants/shadow_offset_x = 3
|
||||||
|
theme_override_constants/shadow_offset_y = 3
|
||||||
|
theme_override_constants/outline_size = 15
|
||||||
|
theme_override_constants/shadow_outline_size = 15
|
||||||
|
theme_override_font_sizes/font_size = 35
|
||||||
|
text = "敬请期待"
|
||||||
|
horizontal_alignment = 1
|
||||||
|
vertical_alignment = 1
|
||||||
|
|
||||||
|
[node name="TextureRect" type="TextureRect" parent="ScrollContainer/Grid/VBox6"]
|
||||||
|
layout_mode = 2
|
||||||
|
texture = ExtResource("2_wlugc")
|
||||||
|
|
||||||
|
[node name="Button" type="Button" parent="ScrollContainer/Grid/VBox6"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "点击游玩"
|
||||||
|
|
||||||
|
[node name="VBox7" type="VBoxContainer" parent="ScrollContainer/Grid"]
|
||||||
|
layout_mode = 2
|
||||||
|
alignment = 1
|
||||||
|
|
||||||
|
[node name="GameTitle" type="Label" parent="ScrollContainer/Grid/VBox7"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_colors/font_shadow_color = Color(0, 0, 0, 1)
|
||||||
|
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||||
|
theme_override_constants/shadow_offset_x = 3
|
||||||
|
theme_override_constants/shadow_offset_y = 3
|
||||||
|
theme_override_constants/outline_size = 15
|
||||||
|
theme_override_constants/shadow_outline_size = 15
|
||||||
|
theme_override_font_sizes/font_size = 35
|
||||||
|
text = "敬请期待"
|
||||||
|
horizontal_alignment = 1
|
||||||
|
vertical_alignment = 1
|
||||||
|
|
||||||
|
[node name="TextureRect" type="TextureRect" parent="ScrollContainer/Grid/VBox7"]
|
||||||
|
layout_mode = 2
|
||||||
|
texture = ExtResource("2_wlugc")
|
||||||
|
|
||||||
|
[node name="Button" type="Button" parent="ScrollContainer/Grid/VBox7"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "点击游玩"
|
||||||
|
|
||||||
|
[node name="VBox8" type="VBoxContainer" parent="ScrollContainer/Grid"]
|
||||||
|
layout_mode = 2
|
||||||
|
alignment = 1
|
||||||
|
|
||||||
|
[node name="GameTitle" type="Label" parent="ScrollContainer/Grid/VBox8"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_colors/font_shadow_color = Color(0, 0, 0, 1)
|
||||||
|
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||||
|
theme_override_constants/shadow_offset_x = 3
|
||||||
|
theme_override_constants/shadow_offset_y = 3
|
||||||
|
theme_override_constants/outline_size = 15
|
||||||
|
theme_override_constants/shadow_outline_size = 15
|
||||||
|
theme_override_font_sizes/font_size = 35
|
||||||
|
text = "敬请期待"
|
||||||
|
horizontal_alignment = 1
|
||||||
|
vertical_alignment = 1
|
||||||
|
|
||||||
|
[node name="TextureRect" type="TextureRect" parent="ScrollContainer/Grid/VBox8"]
|
||||||
|
layout_mode = 2
|
||||||
|
texture = ExtResource("2_wlugc")
|
||||||
|
|
||||||
|
[node name="Button" type="Button" parent="ScrollContainer/Grid/VBox8"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "点击游玩"
|
||||||
|
|
||||||
|
[node name="VBox9" type="VBoxContainer" parent="ScrollContainer/Grid"]
|
||||||
|
layout_mode = 2
|
||||||
|
alignment = 1
|
||||||
|
|
||||||
|
[node name="GameTitle" type="Label" parent="ScrollContainer/Grid/VBox9"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_colors/font_shadow_color = Color(0, 0, 0, 1)
|
||||||
|
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||||
|
theme_override_constants/shadow_offset_x = 3
|
||||||
|
theme_override_constants/shadow_offset_y = 3
|
||||||
|
theme_override_constants/outline_size = 15
|
||||||
|
theme_override_constants/shadow_outline_size = 15
|
||||||
|
theme_override_font_sizes/font_size = 35
|
||||||
|
text = "敬请期待"
|
||||||
|
horizontal_alignment = 1
|
||||||
|
vertical_alignment = 1
|
||||||
|
|
||||||
|
[node name="TextureRect" type="TextureRect" parent="ScrollContainer/Grid/VBox9"]
|
||||||
|
layout_mode = 2
|
||||||
|
texture = ExtResource("2_wlugc")
|
||||||
|
|
||||||
|
[node name="Button" type="Button" parent="ScrollContainer/Grid/VBox9"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "点击游玩"
|
||||||
|
|
||||||
|
[node name="VBox10" type="VBoxContainer" parent="ScrollContainer/Grid"]
|
||||||
|
layout_mode = 2
|
||||||
|
alignment = 1
|
||||||
|
|
||||||
|
[node name="GameTitle" type="Label" parent="ScrollContainer/Grid/VBox10"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_colors/font_shadow_color = Color(0, 0, 0, 1)
|
||||||
|
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||||
|
theme_override_constants/shadow_offset_x = 3
|
||||||
|
theme_override_constants/shadow_offset_y = 3
|
||||||
|
theme_override_constants/outline_size = 15
|
||||||
|
theme_override_constants/shadow_outline_size = 15
|
||||||
|
theme_override_font_sizes/font_size = 35
|
||||||
|
text = "敬请期待"
|
||||||
|
horizontal_alignment = 1
|
||||||
|
vertical_alignment = 1
|
||||||
|
|
||||||
|
[node name="TextureRect" type="TextureRect" parent="ScrollContainer/Grid/VBox10"]
|
||||||
|
layout_mode = 2
|
||||||
|
texture = ExtResource("2_wlugc")
|
||||||
|
|
||||||
|
[node name="Button" type="Button" parent="ScrollContainer/Grid/VBox10"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
|
text = "点击游玩"
|
||||||
|
|
||||||
|
[connection signal="pressed" from="ScrollContainer/Grid/2048Game/2048GameButton" to="." method="_on_game_button_pressed"]
|
||||||
|
[connection signal="pressed" from="ScrollContainer/Grid/PushBox/PushBoxButton" to="." method="_on_push_box_button_pressed"]
|
||||||
|
[connection signal="pressed" from="ScrollContainer/Grid/SnakeGame/SnakeGameButton" to="." method="_on_snake_game_button_pressed"]
|
||||||
|
[connection signal="pressed" from="ScrollContainer/Grid/Tetris/TetrisButton" to="." method="_on_tetris_button_pressed"]
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ extends Panel
|
|||||||
const GRID_SIZE = 4
|
const GRID_SIZE = 4
|
||||||
const CELL_SIZE = 90
|
const CELL_SIZE = 90
|
||||||
const CELL_MARGIN = 10
|
const CELL_MARGIN = 10
|
||||||
|
const SWIPE_THRESHOLD = 50
|
||||||
|
const DATA_FILE_PATH = "user://playergamedata.json"
|
||||||
|
|
||||||
# 数字颜色配置
|
# 数字颜色配置
|
||||||
const NUMBER_COLORS = {
|
const NUMBER_COLORS = {
|
||||||
@@ -18,7 +20,11 @@ const NUMBER_COLORS = {
|
|||||||
256: Color(0.93, 0.80, 0.38),
|
256: Color(0.93, 0.80, 0.38),
|
||||||
512: Color(0.93, 0.78, 0.31),
|
512: Color(0.93, 0.78, 0.31),
|
||||||
1024: Color(0.93, 0.77, 0.25),
|
1024: Color(0.93, 0.77, 0.25),
|
||||||
2048: Color(0.93, 0.76, 0.18)
|
2048: Color(0.93, 0.76, 0.18),
|
||||||
|
4096: Color(0.93, 0.70, 0.15),
|
||||||
|
8192: Color(0.93, 0.65, 0.12),
|
||||||
|
16384: Color(0.93, 0.60, 0.10),
|
||||||
|
32768: Color(0.93, 0.55, 0.08)
|
||||||
}
|
}
|
||||||
|
|
||||||
const TEXT_COLORS = {
|
const TEXT_COLORS = {
|
||||||
@@ -32,7 +38,11 @@ const TEXT_COLORS = {
|
|||||||
256: Color.WHITE,
|
256: Color.WHITE,
|
||||||
512: Color.WHITE,
|
512: Color.WHITE,
|
||||||
1024: Color.WHITE,
|
1024: Color.WHITE,
|
||||||
2048: Color.WHITE
|
2048: Color.WHITE,
|
||||||
|
4096: Color.WHITE,
|
||||||
|
8192: Color.WHITE,
|
||||||
|
16384: Color.WHITE,
|
||||||
|
32768: Color.WHITE
|
||||||
}
|
}
|
||||||
|
|
||||||
# 游戏变量
|
# 游戏变量
|
||||||
@@ -42,6 +52,14 @@ var best_score = 0
|
|||||||
var game_over = false
|
var game_over = false
|
||||||
var won = false
|
var won = false
|
||||||
var can_continue = true
|
var can_continue = true
|
||||||
|
var highest_tile = 0
|
||||||
|
var games_played = 0
|
||||||
|
var total_moves = 0
|
||||||
|
var player_data = {}
|
||||||
|
|
||||||
|
# 触摸控制变量
|
||||||
|
var touch_start_pos = Vector2.ZERO
|
||||||
|
var is_touching = false
|
||||||
|
|
||||||
# 节点引用
|
# 节点引用
|
||||||
@onready var game_board = $GameBoard
|
@onready var game_board = $GameBoard
|
||||||
@@ -49,11 +67,15 @@ var can_continue = true
|
|||||||
@onready var best_label = $BestLabel
|
@onready var best_label = $BestLabel
|
||||||
@onready var game_over_label = $GameOverLabel
|
@onready var game_over_label = $GameOverLabel
|
||||||
@onready var win_label = $WinLabel
|
@onready var win_label = $WinLabel
|
||||||
|
@onready var stats_label = $StatsLabel
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
# 设置游戏板样式
|
# 设置游戏板样式
|
||||||
game_board.modulate = Color(0.7, 0.6, 0.5)
|
game_board.modulate = Color(0.7, 0.6, 0.5)
|
||||||
|
|
||||||
|
# 加载玩家数据
|
||||||
|
load_player_data()
|
||||||
|
|
||||||
# 初始化游戏
|
# 初始化游戏
|
||||||
init_game()
|
init_game()
|
||||||
|
|
||||||
@@ -63,6 +85,7 @@ func init_game():
|
|||||||
won = false
|
won = false
|
||||||
can_continue = true
|
can_continue = true
|
||||||
score = 0
|
score = 0
|
||||||
|
games_played += 1
|
||||||
|
|
||||||
# 初始化网格
|
# 初始化网格
|
||||||
grid.clear()
|
grid.clear()
|
||||||
@@ -83,6 +106,7 @@ func init_game():
|
|||||||
queue_redraw()
|
queue_redraw()
|
||||||
|
|
||||||
func _input(event):
|
func _input(event):
|
||||||
|
# 键盘输入
|
||||||
if event is InputEventKey and event.pressed:
|
if event is InputEventKey and event.pressed:
|
||||||
if game_over:
|
if game_over:
|
||||||
if event.keycode == KEY_R:
|
if event.keycode == KEY_R:
|
||||||
@@ -111,10 +135,28 @@ func _input(event):
|
|||||||
return
|
return
|
||||||
|
|
||||||
if moved:
|
if moved:
|
||||||
add_random_number()
|
handle_successful_move()
|
||||||
update_ui()
|
|
||||||
check_game_state()
|
# 触摸输入
|
||||||
queue_redraw()
|
elif event is InputEventScreenTouch:
|
||||||
|
if event.pressed:
|
||||||
|
touch_start_pos = event.position
|
||||||
|
is_touching = true
|
||||||
|
else:
|
||||||
|
if is_touching:
|
||||||
|
handle_swipe(event.position)
|
||||||
|
is_touching = false
|
||||||
|
|
||||||
|
# 鼠标输入(用于桌面测试)
|
||||||
|
elif event is InputEventMouseButton:
|
||||||
|
if event.button_index == MOUSE_BUTTON_LEFT:
|
||||||
|
if event.pressed:
|
||||||
|
touch_start_pos = event.position
|
||||||
|
is_touching = true
|
||||||
|
else:
|
||||||
|
if is_touching:
|
||||||
|
handle_swipe(event.position)
|
||||||
|
is_touching = false
|
||||||
|
|
||||||
func move_left() -> bool:
|
func move_left() -> bool:
|
||||||
var moved = false
|
var moved = false
|
||||||
@@ -216,14 +258,53 @@ func add_random_number():
|
|||||||
var value = 2 if randf() < 0.9 else 4
|
var value = 2 if randf() < 0.9 else 4
|
||||||
grid[random_cell.y][random_cell.x] = value
|
grid[random_cell.y][random_cell.x] = value
|
||||||
|
|
||||||
|
func handle_successful_move():
|
||||||
|
total_moves += 1
|
||||||
|
add_random_number()
|
||||||
|
update_ui()
|
||||||
|
check_game_state()
|
||||||
|
save_player_data()
|
||||||
|
queue_redraw()
|
||||||
|
|
||||||
|
func handle_swipe(end_pos: Vector2):
|
||||||
|
if game_over or (won and not can_continue):
|
||||||
|
return
|
||||||
|
|
||||||
|
var delta = end_pos - touch_start_pos
|
||||||
|
var moved = false
|
||||||
|
|
||||||
|
if abs(delta.x) > SWIPE_THRESHOLD or abs(delta.y) > SWIPE_THRESHOLD:
|
||||||
|
if abs(delta.x) > abs(delta.y):
|
||||||
|
# 水平滑动
|
||||||
|
if delta.x > 0:
|
||||||
|
moved = move_right()
|
||||||
|
else:
|
||||||
|
moved = move_left()
|
||||||
|
else:
|
||||||
|
# 垂直滑动
|
||||||
|
if delta.y > 0:
|
||||||
|
moved = move_down()
|
||||||
|
else:
|
||||||
|
moved = move_up()
|
||||||
|
|
||||||
|
if moved:
|
||||||
|
handle_successful_move()
|
||||||
|
|
||||||
func check_game_state():
|
func check_game_state():
|
||||||
# 检查是否达到2048
|
# 更新最高数字
|
||||||
|
for y in range(GRID_SIZE):
|
||||||
|
for x in range(GRID_SIZE):
|
||||||
|
if grid[y][x] > highest_tile:
|
||||||
|
highest_tile = grid[y][x]
|
||||||
|
|
||||||
|
# 检查是否达到2048或更高目标
|
||||||
if not won:
|
if not won:
|
||||||
for y in range(GRID_SIZE):
|
for y in range(GRID_SIZE):
|
||||||
for x in range(GRID_SIZE):
|
for x in range(GRID_SIZE):
|
||||||
if grid[y][x] == 2048:
|
if grid[y][x] == 2048:
|
||||||
won = true
|
won = true
|
||||||
can_continue = false
|
can_continue = false
|
||||||
|
win_label.text = "恭喜!达到2048!\n按C继续挑战更高目标"
|
||||||
win_label.visible = true
|
win_label.visible = true
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -257,38 +338,102 @@ func can_move() -> bool:
|
|||||||
func update_ui():
|
func update_ui():
|
||||||
score_label.text = "分数: " + str(score)
|
score_label.text = "分数: " + str(score)
|
||||||
best_label.text = "最高分: " + str(best_score)
|
best_label.text = "最高分: " + str(best_score)
|
||||||
|
if stats_label:
|
||||||
|
stats_label.text = "游戏次数: " + str(games_played) + " | 总步数: " + str(total_moves)
|
||||||
|
|
||||||
func hide_labels():
|
func hide_labels():
|
||||||
game_over_label.visible = false
|
game_over_label.visible = false
|
||||||
win_label.visible = false
|
win_label.visible = false
|
||||||
|
|
||||||
|
func load_player_data():
|
||||||
|
if FileAccess.file_exists(DATA_FILE_PATH):
|
||||||
|
var file = FileAccess.open(DATA_FILE_PATH, FileAccess.READ)
|
||||||
|
if file:
|
||||||
|
var json_string = file.get_as_text()
|
||||||
|
file.close()
|
||||||
|
|
||||||
|
var json = JSON.new()
|
||||||
|
var parse_result = json.parse(json_string)
|
||||||
|
if parse_result == OK:
|
||||||
|
player_data = json.data
|
||||||
|
if player_data.has("2048"):
|
||||||
|
var game_data = player_data["2048"]
|
||||||
|
best_score = game_data.get("best_score", 0)
|
||||||
|
games_played = game_data.get("games_played", 0)
|
||||||
|
highest_tile = game_data.get("highest_tile", 0)
|
||||||
|
total_moves = game_data.get("total_moves", 0)
|
||||||
|
|
||||||
|
func save_player_data():
|
||||||
|
if not player_data.has("2048"):
|
||||||
|
player_data["2048"] = {}
|
||||||
|
|
||||||
|
player_data["2048"]["best_score"] = best_score
|
||||||
|
player_data["2048"]["current_score"] = score
|
||||||
|
player_data["2048"]["games_played"] = games_played
|
||||||
|
player_data["2048"]["highest_tile"] = highest_tile
|
||||||
|
player_data["2048"]["total_moves"] = total_moves
|
||||||
|
|
||||||
|
# 更新全局数据
|
||||||
|
if not player_data.has("global"):
|
||||||
|
player_data["global"] = {}
|
||||||
|
player_data["global"]["last_played"] = Time.get_datetime_string_from_system()
|
||||||
|
|
||||||
|
var file = FileAccess.open(DATA_FILE_PATH, FileAccess.WRITE)
|
||||||
|
if file:
|
||||||
|
var json_string = JSON.stringify(player_data)
|
||||||
|
file.store_string(json_string)
|
||||||
|
file.close()
|
||||||
|
|
||||||
func _draw():
|
func _draw():
|
||||||
if not game_board:
|
if not game_board:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# 绘制背景渐变
|
||||||
|
var gradient = Gradient.new()
|
||||||
|
gradient.add_point(0.0, Color(0.2, 0.3, 0.5, 0.8))
|
||||||
|
gradient.add_point(1.0, Color(0.1, 0.2, 0.4, 0.9))
|
||||||
|
draw_rect(Rect2(Vector2.ZERO, size), gradient.sample(0.5), true)
|
||||||
|
|
||||||
# 获取游戏板位置
|
# 获取游戏板位置
|
||||||
var board_pos = game_board.position
|
var board_pos = game_board.position
|
||||||
|
|
||||||
# 绘制网格背景
|
# 绘制游戏板阴影
|
||||||
|
var shadow_offset = Vector2(4, 4)
|
||||||
|
var board_rect = Rect2(board_pos + shadow_offset, game_board.size)
|
||||||
|
draw_rect(board_rect, Color(0, 0, 0, 0.3), true, 8)
|
||||||
|
|
||||||
|
# 绘制游戏板背景
|
||||||
|
board_rect = Rect2(board_pos, game_board.size)
|
||||||
|
draw_rect(board_rect, Color(0.7, 0.6, 0.5, 0.9), true, 8)
|
||||||
|
|
||||||
|
# 绘制网格
|
||||||
for y in range(GRID_SIZE):
|
for y in range(GRID_SIZE):
|
||||||
for x in range(GRID_SIZE):
|
for x in range(GRID_SIZE):
|
||||||
var cell_x = board_pos.x + x * (CELL_SIZE + CELL_MARGIN) + CELL_MARGIN
|
var cell_x = board_pos.x + x * (CELL_SIZE + CELL_MARGIN) + CELL_MARGIN
|
||||||
var cell_y = board_pos.y + y * (CELL_SIZE + CELL_MARGIN) + CELL_MARGIN
|
var cell_y = board_pos.y + y * (CELL_SIZE + CELL_MARGIN) + CELL_MARGIN
|
||||||
var rect = Rect2(cell_x, cell_y, CELL_SIZE, CELL_SIZE)
|
var rect = Rect2(cell_x, cell_y, CELL_SIZE, CELL_SIZE)
|
||||||
|
|
||||||
|
# 绘制单元格阴影
|
||||||
|
draw_rect(rect.grow(2), Color(0, 0, 0, 0.2), true)
|
||||||
|
|
||||||
# 绘制单元格背景
|
# 绘制单元格背景
|
||||||
draw_rect(rect, Color(0.8, 0.7, 0.6), true)
|
draw_rect(rect, Color(0.8, 0.7, 0.6, 0.8), true)
|
||||||
|
|
||||||
# 绘制数字
|
# 绘制数字
|
||||||
var value = grid[y][x]
|
var value = grid[y][x]
|
||||||
if value > 0:
|
if value > 0:
|
||||||
# 绘制数字背景
|
# 绘制数字背景(带渐变效果)
|
||||||
var bg_color = NUMBER_COLORS.get(value, Color.GOLD)
|
var bg_color = NUMBER_COLORS.get(value, Color.GOLD)
|
||||||
draw_rect(rect, bg_color, true)
|
draw_rect(rect, bg_color, true)
|
||||||
|
|
||||||
|
# 绘制高光效果
|
||||||
|
var highlight_rect = Rect2(rect.position, Vector2(rect.size.x, rect.size.y * 0.3))
|
||||||
|
var highlight_color = Color(1, 1, 1, 0.3)
|
||||||
|
draw_rect(highlight_rect, highlight_color, true)
|
||||||
|
|
||||||
# 绘制数字文本
|
# 绘制数字文本
|
||||||
var text = str(value)
|
var text = str(value)
|
||||||
var font_size = 24 if value < 100 else (20 if value < 1000 else 16)
|
var font_size = 24 if value < 100 else (20 if value < 1000 else (16 if value < 10000 else 14))
|
||||||
var text_color = TEXT_COLORS.get(value, Color.WHITE)
|
var text_color = TEXT_COLORS.get(value, Color.WHITE)
|
||||||
|
|
||||||
# 获取默认字体
|
# 获取默认字体
|
||||||
@@ -303,5 +448,30 @@ func _draw():
|
|||||||
cell_y + (CELL_SIZE - text_size.y) / 2 + text_size.y
|
cell_y + (CELL_SIZE - text_size.y) / 2 + text_size.y
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# 绘制文本阴影
|
||||||
|
draw_string(font, text_pos + Vector2(1, 1), text, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, Color(0, 0, 0, 0.5))
|
||||||
# 绘制文本
|
# 绘制文本
|
||||||
draw_string(font, text_pos, text, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, text_color)
|
draw_string(font, text_pos, text, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, text_color)
|
||||||
|
|
||||||
|
|
||||||
|
#退出2048游戏界面
|
||||||
|
func _on_quit_button_pressed() -> void:
|
||||||
|
self.hide()
|
||||||
|
get_parent().remove_child(self)
|
||||||
|
queue_free()
|
||||||
|
pass
|
||||||
|
|
||||||
|
#手机端继续游戏按钮
|
||||||
|
func _on_continue_button_pressed() -> void:
|
||||||
|
if won and not can_continue:
|
||||||
|
can_continue = true
|
||||||
|
win_label.visible = false
|
||||||
|
return
|
||||||
|
pass
|
||||||
|
|
||||||
|
#手机端重置游戏按钮
|
||||||
|
func _on_reast_button_pressed() -> void:
|
||||||
|
if game_over:
|
||||||
|
init_game()
|
||||||
|
return
|
||||||
|
pass
|
||||||
|
|||||||
@@ -14,10 +14,10 @@ anchor_left = 0.5
|
|||||||
anchor_top = 0.5
|
anchor_top = 0.5
|
||||||
anchor_right = 0.5
|
anchor_right = 0.5
|
||||||
anchor_bottom = 0.5
|
anchor_bottom = 0.5
|
||||||
offset_left = -200.0
|
offset_left = -217.0
|
||||||
offset_top = -200.0
|
offset_top = -250.0
|
||||||
offset_right = 200.0
|
offset_right = 183.0
|
||||||
offset_bottom = 200.0
|
offset_bottom = 150.0
|
||||||
grow_horizontal = 2
|
grow_horizontal = 2
|
||||||
grow_vertical = 2
|
grow_vertical = 2
|
||||||
|
|
||||||
@@ -90,13 +90,64 @@ anchor_left = 1.0
|
|||||||
anchor_top = 1.0
|
anchor_top = 1.0
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
anchor_bottom = 1.0
|
anchor_bottom = 1.0
|
||||||
offset_left = -300.0
|
offset_left = -350.0
|
||||||
offset_top = -150.0
|
offset_top = -180.0
|
||||||
offset_right = -50.0
|
offset_right = -50.0
|
||||||
offset_bottom = -50.0
|
offset_bottom = -50.0
|
||||||
grow_horizontal = 0
|
grow_horizontal = 0
|
||||||
grow_vertical = 0
|
grow_vertical = 0
|
||||||
text = "操作说明:
|
text = "操作说明:
|
||||||
WASD或方向键 - 移动
|
WASD或方向键 - 移动
|
||||||
|
滑动屏幕 - 移动(手机)
|
||||||
R - 重新开始
|
R - 重新开始
|
||||||
C - 继续游戏(达到2048后)"
|
C - 继续游戏(达到2048后)
|
||||||
|
|
||||||
|
目标: 合并数字达到2048或更高!"
|
||||||
|
|
||||||
|
[node name="StatsLabel" type="Label" parent="."]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 2
|
||||||
|
anchor_top = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_left = 50.0
|
||||||
|
offset_top = -40.0
|
||||||
|
offset_right = 300.0
|
||||||
|
offset_bottom = -10.0
|
||||||
|
grow_vertical = 0
|
||||||
|
text = "游戏次数: 0 | 总步数: 0"
|
||||||
|
|
||||||
|
[node name="ReastButton" type="Button" parent="."]
|
||||||
|
layout_mode = 0
|
||||||
|
offset_left = 19.0
|
||||||
|
offset_top = 19.0
|
||||||
|
offset_right = 97.0
|
||||||
|
offset_bottom = 76.0
|
||||||
|
theme_override_font_sizes/font_size = 35
|
||||||
|
text = "重置"
|
||||||
|
|
||||||
|
[node name="ContinueButton" type="Button" parent="."]
|
||||||
|
layout_mode = 0
|
||||||
|
offset_left = 97.0
|
||||||
|
offset_top = 19.0
|
||||||
|
offset_right = 175.0
|
||||||
|
offset_bottom = 76.0
|
||||||
|
theme_override_font_sizes/font_size = 35
|
||||||
|
text = "继续"
|
||||||
|
|
||||||
|
[node name="QuitButton" type="Button" parent="."]
|
||||||
|
self_modulate = Color(1, 0.247059, 0, 1)
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 1
|
||||||
|
anchor_left = 1.0
|
||||||
|
anchor_right = 1.0
|
||||||
|
offset_left = -129.0
|
||||||
|
offset_right = -46.0
|
||||||
|
offset_bottom = 57.0
|
||||||
|
grow_horizontal = 0
|
||||||
|
scale = Vector2(1.54345, 1.50915)
|
||||||
|
theme_override_font_sizes/font_size = 25
|
||||||
|
text = "关闭"
|
||||||
|
|
||||||
|
[connection signal="pressed" from="ReastButton" to="." method="_on_reast_button_pressed"]
|
||||||
|
[connection signal="pressed" from="ContinueButton" to="." method="_on_continue_button_pressed"]
|
||||||
|
[connection signal="pressed" from="QuitButton" to="." method="_on_quit_button_pressed"]
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ extends Panel
|
|||||||
|
|
||||||
# 游戏常量
|
# 游戏常量
|
||||||
const CELL_SIZE = 40
|
const CELL_SIZE = 40
|
||||||
|
const DATA_FILE_PATH = "user://playergamedata.json"
|
||||||
|
|
||||||
# 地图元素
|
# 地图元素
|
||||||
enum CellType {
|
enum CellType {
|
||||||
@@ -72,6 +73,328 @@ const LEVELS = [
|
|||||||
"#..$......@#",
|
"#..$......@#",
|
||||||
"#..........#",
|
"#..........#",
|
||||||
"############"
|
"############"
|
||||||
|
],
|
||||||
|
# 关卡5 - 角落挑战
|
||||||
|
[
|
||||||
|
"#########",
|
||||||
|
"#*......#",
|
||||||
|
"#.##....#",
|
||||||
|
"#.#$....#",
|
||||||
|
"#.#.....#",
|
||||||
|
"#.#.....#",
|
||||||
|
"#.......#",
|
||||||
|
"#......@#",
|
||||||
|
"#########"
|
||||||
|
],
|
||||||
|
# 关卡6 - 多箱子排列
|
||||||
|
[
|
||||||
|
"##########",
|
||||||
|
"#........#",
|
||||||
|
"#.*.*.*..#",
|
||||||
|
"#........#",
|
||||||
|
"#.$.$.$..#",
|
||||||
|
"#........#",
|
||||||
|
"#....@...#",
|
||||||
|
"#........#",
|
||||||
|
"##########"
|
||||||
|
],
|
||||||
|
# 关卡7 - 迷宫式
|
||||||
|
[
|
||||||
|
"############",
|
||||||
|
"#..........#",
|
||||||
|
"#.##.##.##.#",
|
||||||
|
"#.*#.#*.#*.#",
|
||||||
|
"#..#.#..#..#",
|
||||||
|
"#.$#.#$.#$.#",
|
||||||
|
"#..#.#..#..#",
|
||||||
|
"#..#.#..#..#",
|
||||||
|
"#..........#",
|
||||||
|
"#....@.....#",
|
||||||
|
"############"
|
||||||
|
],
|
||||||
|
# 关卡8 - 紧密配合
|
||||||
|
[
|
||||||
|
"#########",
|
||||||
|
"#.......#",
|
||||||
|
"#.##*##.#",
|
||||||
|
"#.#$.$#.#",
|
||||||
|
"#.#*.*#.#",
|
||||||
|
"#.#$.$#.#",
|
||||||
|
"#.##*##.#",
|
||||||
|
"#...@...#",
|
||||||
|
"#########"
|
||||||
|
],
|
||||||
|
# 关卡9 - 长廊挑战
|
||||||
|
[
|
||||||
|
"##############",
|
||||||
|
"#............#",
|
||||||
|
"#.##########.#",
|
||||||
|
"#.*........*.#",
|
||||||
|
"#.$.......$.#",
|
||||||
|
"#............#",
|
||||||
|
"#............#",
|
||||||
|
"#.$.......$.#",
|
||||||
|
"#.*........*.#",
|
||||||
|
"#.##########.#",
|
||||||
|
"#......@.....#",
|
||||||
|
"##############"
|
||||||
|
],
|
||||||
|
# 关卡10 - 螺旋结构
|
||||||
|
[
|
||||||
|
"###########",
|
||||||
|
"#.........#",
|
||||||
|
"#.#######.#",
|
||||||
|
"#.#*...#.#",
|
||||||
|
"#.#.#$.#.#",
|
||||||
|
"#.#.#*.#.#",
|
||||||
|
"#.#.#$.#.#",
|
||||||
|
"#.#...#.#",
|
||||||
|
"#.#####.#",
|
||||||
|
"#...@...#",
|
||||||
|
"###########"
|
||||||
|
],
|
||||||
|
# 关卡11 - 对称美学
|
||||||
|
[
|
||||||
|
"############",
|
||||||
|
"#..........#",
|
||||||
|
"#.*#....#*.#",
|
||||||
|
"#.$#....#$.#",
|
||||||
|
"#..#....#..#",
|
||||||
|
"#..........#",
|
||||||
|
"#..........#",
|
||||||
|
"#..#....#..#",
|
||||||
|
"#.$#....#$.#",
|
||||||
|
"#.*#....#*.#",
|
||||||
|
"#.....@....#",
|
||||||
|
"############"
|
||||||
|
],
|
||||||
|
# 关卡12 - 十字路口
|
||||||
|
[
|
||||||
|
"###########",
|
||||||
|
"#.........#",
|
||||||
|
"#....#....#",
|
||||||
|
"#.*..#..*#",
|
||||||
|
"#.$.###.$.#",
|
||||||
|
"#...#@#...#",
|
||||||
|
"#.$.###.$.#",
|
||||||
|
"#.*..#..*#",
|
||||||
|
"#....#....#",
|
||||||
|
"#.........#",
|
||||||
|
"###########"
|
||||||
|
],
|
||||||
|
# 关卡13 - 复杂迷宫
|
||||||
|
[
|
||||||
|
"##############",
|
||||||
|
"#............#",
|
||||||
|
"#.##.####.##.#",
|
||||||
|
"#.*#......#*.#",
|
||||||
|
"#.$#.####.#$.#",
|
||||||
|
"#..#.#..#.#..#",
|
||||||
|
"#....#..#....#",
|
||||||
|
"#..#.#..#.#..#",
|
||||||
|
"#.$#.####.#$.#",
|
||||||
|
"#.*#......#*.#",
|
||||||
|
"#.##.####.##.#",
|
||||||
|
"#......@.....#",
|
||||||
|
"##############"
|
||||||
|
],
|
||||||
|
# 关卡14 - 精密操作
|
||||||
|
[
|
||||||
|
"##########",
|
||||||
|
"#........#",
|
||||||
|
"#.######.#",
|
||||||
|
"#.#*..*.#",
|
||||||
|
"#.#$..$.#",
|
||||||
|
"#.#....#.#",
|
||||||
|
"#.#$..$.#",
|
||||||
|
"#.#*..*.#",
|
||||||
|
"#.######.#",
|
||||||
|
"#...@....#",
|
||||||
|
"##########"
|
||||||
|
],
|
||||||
|
# 关卡15 - 终极挑战
|
||||||
|
[
|
||||||
|
"###############",
|
||||||
|
"#.............#",
|
||||||
|
"#.###.###.###.#",
|
||||||
|
"#.*#*.*#*.*#*.#",
|
||||||
|
"#.$#$.$#$.$#$.#",
|
||||||
|
"#.###.###.###.#",
|
||||||
|
"#.............#",
|
||||||
|
"#.###.###.###.#",
|
||||||
|
"#.$#$.$#$.$#$.#",
|
||||||
|
"#.*#*.*#*.*#*.#",
|
||||||
|
"#.###.###.###.#",
|
||||||
|
"#.......@.....#",
|
||||||
|
"###############"
|
||||||
|
],
|
||||||
|
# 关卡16 - 狭窄通道
|
||||||
|
[
|
||||||
|
"#############",
|
||||||
|
"#...........#",
|
||||||
|
"#.#.#.#.#.#.#",
|
||||||
|
"#*#*#*#*#*#*#",
|
||||||
|
"#$#$#$#$#$#$#",
|
||||||
|
"#.#.#.#.#.#.#",
|
||||||
|
"#...........#",
|
||||||
|
"#.#.#.#.#.#.#",
|
||||||
|
"#$#$#$#$#$#$#",
|
||||||
|
"#*#*#*#*#*#*#",
|
||||||
|
"#.#.#.#.#.#.#",
|
||||||
|
"#.....@.....#",
|
||||||
|
"#############"
|
||||||
|
],
|
||||||
|
# 关卡17 - 环形结构
|
||||||
|
[
|
||||||
|
"##############",
|
||||||
|
"#............#",
|
||||||
|
"#.##########.#",
|
||||||
|
"#.#........#.#",
|
||||||
|
"#.#.######.#.#",
|
||||||
|
"#.#.#*..*.#.#.#",
|
||||||
|
"#.#.#$..$.#.#.#",
|
||||||
|
"#.#.#....#.#.#",
|
||||||
|
"#.#.######.#.#",
|
||||||
|
"#.#........#.#",
|
||||||
|
"#.##########.#",
|
||||||
|
"#......@.....#",
|
||||||
|
"##############"
|
||||||
|
],
|
||||||
|
# 关卡18 - 多层迷宫
|
||||||
|
[
|
||||||
|
"################",
|
||||||
|
"#..............#",
|
||||||
|
"#.############.#",
|
||||||
|
"#.#*........*.#.#",
|
||||||
|
"#.#$........$.#.#",
|
||||||
|
"#.#..########..#.#",
|
||||||
|
"#.#..#*....*.#..#.#",
|
||||||
|
"#.#..#$....$.#..#.#",
|
||||||
|
"#.#..########..#.#",
|
||||||
|
"#.#$........$.#.#",
|
||||||
|
"#.#*........*.#.#",
|
||||||
|
"#.############.#",
|
||||||
|
"#........@.....#",
|
||||||
|
"################"
|
||||||
|
],
|
||||||
|
# 关卡19 - 钻石形状
|
||||||
|
[
|
||||||
|
"#########",
|
||||||
|
"#.......#",
|
||||||
|
"#...*...#",
|
||||||
|
"#..*$*..#",
|
||||||
|
"#.*$@$*.#",
|
||||||
|
"#..*$*..#",
|
||||||
|
"#...*...#",
|
||||||
|
"#.......#",
|
||||||
|
"#########"
|
||||||
|
],
|
||||||
|
# 关卡20 - 复杂交叉
|
||||||
|
[
|
||||||
|
"###############",
|
||||||
|
"#.............#",
|
||||||
|
"#.#.#.#.#.#.#.#",
|
||||||
|
"#*#*#*#*#*#*#*#",
|
||||||
|
"#$#$#$#$#$#$#$#",
|
||||||
|
"#.#.#.#.#.#.#.#",
|
||||||
|
"#.............#",
|
||||||
|
"#.#.#.#.#.#.#.#",
|
||||||
|
"#$#$#$#$#$#$#$#",
|
||||||
|
"#*#*#*#*#*#*#*#",
|
||||||
|
"#.#.#.#.#.#.#.#",
|
||||||
|
"#.............#",
|
||||||
|
"#.#.#.#@#.#.#.#",
|
||||||
|
"#.............#",
|
||||||
|
"###############"
|
||||||
|
],
|
||||||
|
# 关卡21 - 螺旋深渊
|
||||||
|
[
|
||||||
|
"#############",
|
||||||
|
"#...........#",
|
||||||
|
"#.#########.#",
|
||||||
|
"#.#.......#.#",
|
||||||
|
"#.#.#####.#.#",
|
||||||
|
"#.#.#*..#.#.#",
|
||||||
|
"#.#.#$#.#.#.#",
|
||||||
|
"#.#.#*#.#.#.#",
|
||||||
|
"#.#.#$#.#.#.#",
|
||||||
|
"#.#.###.#.#.#",
|
||||||
|
"#.#.....#.#.#",
|
||||||
|
"#.#######.#.#",
|
||||||
|
"#.........#.#",
|
||||||
|
"#.....@...#.#",
|
||||||
|
"#############"
|
||||||
|
],
|
||||||
|
# 关卡22 - 双重挑战
|
||||||
|
[
|
||||||
|
"##############",
|
||||||
|
"#............#",
|
||||||
|
"#.####..####.#",
|
||||||
|
"#.#*.#..#.*#.#",
|
||||||
|
"#.#$.#..#.$#.#",
|
||||||
|
"#.#..####..#.#",
|
||||||
|
"#.#........#.#",
|
||||||
|
"#.#........#.#",
|
||||||
|
"#.#..####..#.#",
|
||||||
|
"#.#$.#..#.$#.#",
|
||||||
|
"#.#*.#..#.*#.#",
|
||||||
|
"#.####..####.#",
|
||||||
|
"#......@.....#",
|
||||||
|
"##############"
|
||||||
|
],
|
||||||
|
# 关卡23 - 星形布局
|
||||||
|
[
|
||||||
|
"###########",
|
||||||
|
"#.........#",
|
||||||
|
"#....#....#",
|
||||||
|
"#.#.*#*.#.#",
|
||||||
|
"#.#$###$#.#",
|
||||||
|
"#.*#.@.#*.#",
|
||||||
|
"#.#$###$#.#",
|
||||||
|
"#.#.*#*.#.#",
|
||||||
|
"#....#....#",
|
||||||
|
"#.........#",
|
||||||
|
"###########"
|
||||||
|
],
|
||||||
|
# 关卡24 - 终极迷宫
|
||||||
|
[
|
||||||
|
"################",
|
||||||
|
"#..............#",
|
||||||
|
"#.############.#",
|
||||||
|
"#.#*.........*.#",
|
||||||
|
"#.#$#########$#.#",
|
||||||
|
"#.#.#*......*.#.#",
|
||||||
|
"#.#.#$######$#.#.#",
|
||||||
|
"#.#.#.#*..*.#.#.#.#",
|
||||||
|
"#.#.#.#$..$.#.#.#.#",
|
||||||
|
"#.#.#.######.#.#.#",
|
||||||
|
"#.#.#........#.#.#",
|
||||||
|
"#.#.##########.#.#",
|
||||||
|
"#.#............#.#",
|
||||||
|
"#.##############.#",
|
||||||
|
"#........@.......#",
|
||||||
|
"################"
|
||||||
|
],
|
||||||
|
# 关卡25 - 大师级挑战
|
||||||
|
[
|
||||||
|
"#################",
|
||||||
|
"#...............#",
|
||||||
|
"#.#############.#",
|
||||||
|
"#.#*.*.*.*.*.*#.#",
|
||||||
|
"#.#$.$.$.$.$.$#.#",
|
||||||
|
"#.#.###########.#",
|
||||||
|
"#.#.#*.*.*.*.*#.#",
|
||||||
|
"#.#.#$.$.$.$.$#.#",
|
||||||
|
"#.#.#.#######.#.#",
|
||||||
|
"#.#.#.#*.*.*#.#.#",
|
||||||
|
"#.#.#.#$.$.$#.#.#",
|
||||||
|
"#.#.#.#####.#.#.#",
|
||||||
|
"#.#.#.......#.#.#",
|
||||||
|
"#.#.#########.#.#",
|
||||||
|
"#.#...........#.#",
|
||||||
|
"#.#############.#",
|
||||||
|
"#.........@.....#",
|
||||||
|
"#################"
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -83,19 +406,31 @@ var moves = 0
|
|||||||
var level_completed = false
|
var level_completed = false
|
||||||
var map_width = 0
|
var map_width = 0
|
||||||
var map_height = 0
|
var map_height = 0
|
||||||
|
var total_moves = 0
|
||||||
|
var levels_completed = 0
|
||||||
|
var best_moves_per_level = {}
|
||||||
|
var player_data = {}
|
||||||
|
|
||||||
# 节点引用
|
# 节点引用
|
||||||
@onready var game_area = $GameArea
|
@onready var game_area = $GameArea
|
||||||
@onready var level_label = $LevelLabel
|
@onready var level_label = $LevelLabel
|
||||||
@onready var moves_label = $MovesLabel
|
@onready var moves_label = $MovesLabel
|
||||||
@onready var win_label = $WinLabel
|
@onready var win_label = $WinLabel
|
||||||
|
@onready var stats_label = $StatsLabel
|
||||||
|
@onready var virtual_controls = $VirtualControls
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
# 设置游戏区域样式
|
# 设置游戏区域样式
|
||||||
game_area.modulate = Color(0.9, 0.9, 0.9)
|
game_area.modulate = Color(0.9, 0.9, 0.9)
|
||||||
|
|
||||||
|
# 加载玩家数据
|
||||||
|
load_player_data()
|
||||||
|
|
||||||
# 初始化游戏
|
# 初始化游戏
|
||||||
init_level()
|
init_level()
|
||||||
|
|
||||||
|
# 设置虚拟按键
|
||||||
|
setup_virtual_controls()
|
||||||
|
|
||||||
func init_level():
|
func init_level():
|
||||||
# 重置游戏状态
|
# 重置游戏状态
|
||||||
@@ -242,12 +577,14 @@ func move_player(direction: Vector2):
|
|||||||
|
|
||||||
# 增加步数
|
# 增加步数
|
||||||
moves += 1
|
moves += 1
|
||||||
|
total_moves += 1
|
||||||
|
|
||||||
# 检查是否过关
|
# 检查是否过关
|
||||||
check_win_condition()
|
check_win_condition()
|
||||||
|
|
||||||
# 更新UI和重绘
|
# 更新UI和重绘
|
||||||
update_ui()
|
update_ui()
|
||||||
|
save_player_data()
|
||||||
queue_redraw()
|
queue_redraw()
|
||||||
|
|
||||||
func check_win_condition():
|
func check_win_condition():
|
||||||
@@ -259,12 +596,22 @@ func check_win_condition():
|
|||||||
|
|
||||||
# 所有箱子都在目标点上,过关!
|
# 所有箱子都在目标点上,过关!
|
||||||
level_completed = true
|
level_completed = true
|
||||||
|
levels_completed += 1
|
||||||
|
|
||||||
|
# 记录最佳步数
|
||||||
|
var level_key = str(current_level + 1)
|
||||||
|
if not best_moves_per_level.has(level_key) or moves < best_moves_per_level[level_key]:
|
||||||
|
best_moves_per_level[level_key] = moves
|
||||||
|
|
||||||
|
win_label.text = "恭喜过关!\n步数: " + str(moves) + "\n最佳: " + str(best_moves_per_level.get(level_key, moves)) + "\n按N进入下一关\n按R重新开始"
|
||||||
win_label.visible = true
|
win_label.visible = true
|
||||||
|
|
||||||
func next_level():
|
func next_level():
|
||||||
if current_level < LEVELS.size() - 1:
|
if current_level < LEVELS.size() - 1:
|
||||||
current_level += 1
|
current_level += 1
|
||||||
init_level()
|
init_level()
|
||||||
|
else:
|
||||||
|
win_label.text = "恭喜!你已完成所有关卡!\n总步数: " + str(total_moves) + "\n按R重新开始第一关"
|
||||||
|
|
||||||
func prev_level():
|
func prev_level():
|
||||||
if current_level > 0:
|
if current_level > 0:
|
||||||
@@ -272,16 +619,98 @@ func prev_level():
|
|||||||
init_level()
|
init_level()
|
||||||
|
|
||||||
func update_ui():
|
func update_ui():
|
||||||
level_label.text = "关卡: " + str(current_level + 1)
|
level_label.text = "关卡: " + str(current_level + 1) + "/" + str(LEVELS.size())
|
||||||
moves_label.text = "步数: " + str(moves)
|
moves_label.text = "步数: " + str(moves)
|
||||||
|
if stats_label:
|
||||||
|
stats_label.text = "已完成: " + str(levels_completed) + " | 总步数: " + str(total_moves)
|
||||||
|
|
||||||
|
func setup_virtual_controls():
|
||||||
|
if not virtual_controls:
|
||||||
|
return
|
||||||
|
|
||||||
|
# 连接虚拟按键信号
|
||||||
|
var up_btn = virtual_controls.get_node("UpButton")
|
||||||
|
var down_btn = virtual_controls.get_node("DownButton")
|
||||||
|
var left_btn = virtual_controls.get_node("LeftButton")
|
||||||
|
var right_btn = virtual_controls.get_node("RightButton")
|
||||||
|
var reset_btn = virtual_controls.get_node("ResetButton")
|
||||||
|
|
||||||
|
if up_btn:
|
||||||
|
up_btn.pressed.connect(_on_virtual_button_pressed.bind(Vector2(0, -1)))
|
||||||
|
if down_btn:
|
||||||
|
down_btn.pressed.connect(_on_virtual_button_pressed.bind(Vector2(0, 1)))
|
||||||
|
if left_btn:
|
||||||
|
left_btn.pressed.connect(_on_virtual_button_pressed.bind(Vector2(-1, 0)))
|
||||||
|
if right_btn:
|
||||||
|
right_btn.pressed.connect(_on_virtual_button_pressed.bind(Vector2(1, 0)))
|
||||||
|
if reset_btn:
|
||||||
|
reset_btn.pressed.connect(init_level)
|
||||||
|
|
||||||
|
func _on_virtual_button_pressed(direction: Vector2):
|
||||||
|
if not level_completed:
|
||||||
|
move_player(direction)
|
||||||
|
|
||||||
|
func load_player_data():
|
||||||
|
if FileAccess.file_exists(DATA_FILE_PATH):
|
||||||
|
var file = FileAccess.open(DATA_FILE_PATH, FileAccess.READ)
|
||||||
|
if file:
|
||||||
|
var json_string = file.get_as_text()
|
||||||
|
file.close()
|
||||||
|
|
||||||
|
var json = JSON.new()
|
||||||
|
var parse_result = json.parse(json_string)
|
||||||
|
if parse_result == OK:
|
||||||
|
player_data = json.data
|
||||||
|
if player_data.has("pushbox"):
|
||||||
|
var game_data = player_data["pushbox"]
|
||||||
|
current_level = game_data.get("current_level", 0)
|
||||||
|
total_moves = game_data.get("total_moves", 0)
|
||||||
|
levels_completed = game_data.get("levels_completed", 0)
|
||||||
|
best_moves_per_level = game_data.get("best_moves_per_level", {})
|
||||||
|
|
||||||
|
func save_player_data():
|
||||||
|
if not player_data.has("pushbox"):
|
||||||
|
player_data["pushbox"] = {}
|
||||||
|
|
||||||
|
player_data["pushbox"]["current_level"] = current_level
|
||||||
|
player_data["pushbox"]["max_level_reached"] = max(current_level, player_data.get("pushbox", {}).get("max_level_reached", 0))
|
||||||
|
player_data["pushbox"]["total_moves"] = total_moves
|
||||||
|
player_data["pushbox"]["levels_completed"] = levels_completed
|
||||||
|
player_data["pushbox"]["best_moves_per_level"] = best_moves_per_level
|
||||||
|
|
||||||
|
# 更新全局数据
|
||||||
|
if not player_data.has("global"):
|
||||||
|
player_data["global"] = {}
|
||||||
|
player_data["global"]["last_played"] = Time.get_datetime_string_from_system()
|
||||||
|
|
||||||
|
var file = FileAccess.open(DATA_FILE_PATH, FileAccess.WRITE)
|
||||||
|
if file:
|
||||||
|
var json_string = JSON.stringify(player_data)
|
||||||
|
file.store_string(json_string)
|
||||||
|
file.close()
|
||||||
|
|
||||||
func _draw():
|
func _draw():
|
||||||
if not game_area:
|
if not game_area:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# 绘制背景渐变
|
||||||
|
var gradient = Gradient.new()
|
||||||
|
gradient.add_point(0.0, Color(0.15, 0.25, 0.35, 0.9))
|
||||||
|
gradient.add_point(1.0, Color(0.1, 0.15, 0.25, 0.95))
|
||||||
|
draw_rect(Rect2(Vector2.ZERO, size), gradient.sample(0.5), true)
|
||||||
|
|
||||||
# 获取游戏区域位置
|
# 获取游戏区域位置
|
||||||
var area_pos = game_area.position
|
var area_pos = game_area.position
|
||||||
|
|
||||||
|
# 绘制游戏区域阴影
|
||||||
|
var shadow_offset = Vector2(6, 6)
|
||||||
|
var area_rect = Rect2(area_pos + shadow_offset, game_area.size)
|
||||||
|
draw_rect(area_rect, Color(0, 0, 0, 0.4), true)
|
||||||
|
|
||||||
|
# 绘制游戏区域背景
|
||||||
|
area_rect = Rect2(area_pos, game_area.size)
|
||||||
|
draw_rect(area_rect, Color(0.8, 0.75, 0.7, 0.95), true)
|
||||||
|
|
||||||
# 计算起始绘制位置(居中)
|
# 计算起始绘制位置(居中)
|
||||||
var start_x = area_pos.x + (game_area.size.x - map_width * CELL_SIZE) / 2
|
var start_x = area_pos.x + (game_area.size.x - map_width * CELL_SIZE) / 2
|
||||||
var start_y = area_pos.y + (game_area.size.y - map_height * CELL_SIZE) / 2
|
var start_y = area_pos.y + (game_area.size.y - map_height * CELL_SIZE) / 2
|
||||||
@@ -294,24 +723,89 @@ func _draw():
|
|||||||
var rect = Rect2(cell_x, cell_y, CELL_SIZE, CELL_SIZE)
|
var rect = Rect2(cell_x, cell_y, CELL_SIZE, CELL_SIZE)
|
||||||
|
|
||||||
var cell_type = level_data[y][x]
|
var cell_type = level_data[y][x]
|
||||||
var color = CELL_COLORS[cell_type]
|
|
||||||
|
|
||||||
# 绘制单元格
|
# 绘制单元格阴影
|
||||||
draw_rect(rect, color, true)
|
draw_rect(rect.grow(1), Color(0, 0, 0, 0.2), true)
|
||||||
|
|
||||||
# 绘制边框
|
# 根据类型绘制不同效果
|
||||||
draw_rect(rect, Color.BLACK, false, 1)
|
match cell_type:
|
||||||
|
CellType.EMPTY:
|
||||||
|
draw_rect(rect, Color(0.9, 0.85, 0.8, 0.7), true)
|
||||||
|
CellType.WALL:
|
||||||
|
# 绘制立体墙壁效果
|
||||||
|
draw_rect(rect, Color(0.3, 0.3, 0.3), true)
|
||||||
|
# 高光
|
||||||
|
var highlight_rect = Rect2(rect.position, Vector2(rect.size.x, rect.size.y * 0.3))
|
||||||
|
draw_rect(highlight_rect, Color(0.5, 0.5, 0.5, 0.8), true)
|
||||||
|
CellType.TARGET:
|
||||||
|
# 绘制目标点(带光晕效果)
|
||||||
|
draw_rect(rect, Color(0.6, 0.8, 1.0, 0.8), true)
|
||||||
|
# 内圈
|
||||||
|
var inner_rect = rect.grow(-8)
|
||||||
|
draw_rect(inner_rect, Color(0.4, 0.6, 0.9, 0.9), true)
|
||||||
|
CellType.BOX:
|
||||||
|
# 绘制立体箱子
|
||||||
|
draw_rect(rect, Color(0.7, 0.5, 0.3), true)
|
||||||
|
# 高光
|
||||||
|
var box_highlight = Rect2(rect.position + Vector2(2, 2), Vector2(rect.size.x - 4, rect.size.y * 0.3))
|
||||||
|
draw_rect(box_highlight, Color(0.9, 0.7, 0.5, 0.8), true)
|
||||||
|
# 边框
|
||||||
|
draw_rect(rect, Color(0.5, 0.3, 0.1), false, 2)
|
||||||
|
CellType.PLAYER:
|
||||||
|
# 检查玩家下面是否有目标点
|
||||||
|
var level_strings = LEVELS[current_level]
|
||||||
|
if y < level_strings.size() and x < level_strings[y].length():
|
||||||
|
var original_char = level_strings[y][x]
|
||||||
|
if original_char == '*': # 玩家在目标点上
|
||||||
|
# 先绘制目标点
|
||||||
|
draw_rect(rect, Color(0.6, 0.8, 1.0, 0.8), true)
|
||||||
|
var inner_rect = rect.grow(-8)
|
||||||
|
draw_rect(inner_rect, Color(0.4, 0.6, 0.9, 0.9), true)
|
||||||
|
else:
|
||||||
|
draw_rect(rect, Color(0.9, 0.85, 0.8, 0.7), true)
|
||||||
|
else:
|
||||||
|
draw_rect(rect, Color(0.9, 0.85, 0.8, 0.7), true)
|
||||||
|
|
||||||
|
# 绘制玩家(圆形)
|
||||||
|
var center = rect.get_center()
|
||||||
|
var radius = min(rect.size.x, rect.size.y) * 0.3
|
||||||
|
# 阴影
|
||||||
|
draw_circle(center + Vector2(1, 1), radius, Color(0, 0, 0, 0.3))
|
||||||
|
# 玩家主体
|
||||||
|
draw_circle(center, radius, Color(0.2, 0.8, 0.2))
|
||||||
|
# 高光
|
||||||
|
draw_circle(center - Vector2(2, 2), radius * 0.5, Color(0.6, 1.0, 0.6, 0.7))
|
||||||
|
CellType.BOX_ON_TARGET:
|
||||||
|
# 绘制目标点背景
|
||||||
|
draw_rect(rect, Color(0.6, 0.8, 1.0, 0.8), true)
|
||||||
|
var inner_rect = rect.grow(-8)
|
||||||
|
draw_rect(inner_rect, Color(0.4, 0.6, 0.9, 0.9), true)
|
||||||
|
|
||||||
|
# 绘制完成的箱子(绿色)
|
||||||
|
var box_rect = rect.grow(-4)
|
||||||
|
draw_rect(box_rect, Color(0.2, 0.7, 0.2), true)
|
||||||
|
# 高光
|
||||||
|
var box_highlight = Rect2(box_rect.position + Vector2(2, 2), Vector2(box_rect.size.x - 4, box_rect.size.y * 0.3))
|
||||||
|
draw_rect(box_highlight, Color(0.4, 0.9, 0.4, 0.8), true)
|
||||||
|
# 边框
|
||||||
|
draw_rect(box_rect, Color(0.1, 0.5, 0.1), false, 2)
|
||||||
|
|
||||||
# 特殊处理:如果是玩家在目标点上,需要先绘制目标点
|
# 绘制网格线(淡色)
|
||||||
if cell_type == CellType.PLAYER:
|
draw_rect(rect, Color(0.6, 0.6, 0.6, 0.3), false, 1)
|
||||||
# 检查玩家下面是否有目标点(通过检查原始关卡数据)
|
|
||||||
var level_strings = LEVELS[current_level]
|
#手机端下一关
|
||||||
if y < level_strings.size() and x < level_strings[y].length():
|
func _on_next_button_pressed() -> void:
|
||||||
var original_char = level_strings[y][x]
|
next_level()
|
||||||
if original_char == '*': # 玩家在目标点上
|
pass
|
||||||
draw_rect(rect, CELL_COLORS[CellType.TARGET], true)
|
|
||||||
draw_rect(rect, Color.BLACK, false, 1)
|
#手机端上一关
|
||||||
# 再绘制玩家(半透明)
|
func _on_last_button_pressed() -> void:
|
||||||
var player_color = CELL_COLORS[CellType.PLAYER]
|
prev_level()
|
||||||
player_color.a = 0.8
|
pass
|
||||||
draw_rect(rect, player_color, true)
|
|
||||||
|
#关闭推箱子游戏界面
|
||||||
|
func _on_quit_button_pressed() -> void:
|
||||||
|
self.hide()
|
||||||
|
get_parent().remove_child(self)
|
||||||
|
queue_free()
|
||||||
|
pass
|
||||||
|
|||||||
@@ -73,13 +73,170 @@ anchor_top = 1.0
|
|||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
anchor_bottom = 1.0
|
anchor_bottom = 1.0
|
||||||
offset_left = -300.0
|
offset_left = -300.0
|
||||||
offset_top = -150.0
|
offset_top = -180.0
|
||||||
offset_right = -50.0
|
offset_right = -50.0
|
||||||
offset_bottom = -50.0
|
offset_bottom = -50.0
|
||||||
grow_horizontal = 0
|
grow_horizontal = 0
|
||||||
grow_vertical = 0
|
grow_vertical = 0
|
||||||
text = "操作说明:
|
text = "操作说明:
|
||||||
WASD或方向键 - 移动
|
WASD或方向键 - 移动
|
||||||
|
虚拟按钮 - 移动(手机)
|
||||||
R - 重新开始当前关卡
|
R - 重新开始当前关卡
|
||||||
N - 下一关(过关后)
|
N - 下一关(过关后)
|
||||||
P - 上一关"
|
P - 上一关"
|
||||||
|
|
||||||
|
[node name="ObjectiveLabel" type="Label" parent="."]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 1
|
||||||
|
anchor_left = 1.0
|
||||||
|
anchor_right = 1.0
|
||||||
|
offset_left = -1402.0
|
||||||
|
offset_right = -1051.0
|
||||||
|
offset_bottom = 35.0
|
||||||
|
grow_horizontal = 0
|
||||||
|
theme_override_font_sizes/font_size = 25
|
||||||
|
text = "目标: 将所有箱子推到目标位置!"
|
||||||
|
horizontal_alignment = 1
|
||||||
|
|
||||||
|
[node name="StatsLabel" type="Label" parent="."]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 2
|
||||||
|
anchor_top = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_left = 50.0
|
||||||
|
offset_top = -40.0
|
||||||
|
offset_right = 400.0
|
||||||
|
offset_bottom = -10.0
|
||||||
|
grow_vertical = 0
|
||||||
|
text = "完成关卡: 0 | 总步数: 0"
|
||||||
|
|
||||||
|
[node name="VirtualControls" type="Control" parent="."]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 3
|
||||||
|
anchor_left = 1.0
|
||||||
|
anchor_top = 1.0
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_left = -1289.0
|
||||||
|
offset_top = -472.0
|
||||||
|
offset_right = -1089.0
|
||||||
|
offset_bottom = -272.0
|
||||||
|
grow_horizontal = 0
|
||||||
|
grow_vertical = 0
|
||||||
|
|
||||||
|
[node name="UpButton" type="Button" parent="VirtualControls"]
|
||||||
|
custom_minimum_size = Vector2(90, 90)
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 1
|
||||||
|
anchor_left = 1.0
|
||||||
|
anchor_right = 1.0
|
||||||
|
offset_left = -144.0
|
||||||
|
offset_top = -72.0
|
||||||
|
offset_right = -54.0
|
||||||
|
offset_bottom = 18.0
|
||||||
|
grow_horizontal = 0
|
||||||
|
theme_override_font_sizes/font_size = 35
|
||||||
|
text = "W"
|
||||||
|
|
||||||
|
[node name="DownButton" type="Button" parent="VirtualControls"]
|
||||||
|
custom_minimum_size = Vector2(90, 90)
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 1
|
||||||
|
anchor_left = 1.0
|
||||||
|
anchor_right = 1.0
|
||||||
|
offset_left = -144.0
|
||||||
|
offset_top = 108.0
|
||||||
|
offset_right = -54.0
|
||||||
|
offset_bottom = 198.0
|
||||||
|
grow_horizontal = 0
|
||||||
|
theme_override_font_sizes/font_size = 35
|
||||||
|
text = "S"
|
||||||
|
|
||||||
|
[node name="LeftButton" type="Button" parent="VirtualControls"]
|
||||||
|
custom_minimum_size = Vector2(90, 90)
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 1
|
||||||
|
anchor_left = 1.0
|
||||||
|
anchor_right = 1.0
|
||||||
|
offset_left = -236.0
|
||||||
|
offset_top = 18.0
|
||||||
|
offset_right = -146.0
|
||||||
|
offset_bottom = 108.0
|
||||||
|
grow_horizontal = 0
|
||||||
|
theme_override_font_sizes/font_size = 35
|
||||||
|
text = "A"
|
||||||
|
|
||||||
|
[node name="RightButton" type="Button" parent="VirtualControls"]
|
||||||
|
custom_minimum_size = Vector2(90, 90)
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 1
|
||||||
|
anchor_left = 1.0
|
||||||
|
anchor_right = 1.0
|
||||||
|
offset_left = -54.0
|
||||||
|
offset_top = 18.0
|
||||||
|
offset_right = 36.0
|
||||||
|
offset_bottom = 108.0
|
||||||
|
grow_horizontal = 0
|
||||||
|
theme_override_font_sizes/font_size = 35
|
||||||
|
text = "D"
|
||||||
|
|
||||||
|
[node name="ResetButton" type="Button" parent="VirtualControls"]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 1
|
||||||
|
anchor_left = 1.0
|
||||||
|
anchor_right = 1.0
|
||||||
|
offset_left = 819.0
|
||||||
|
offset_top = 62.0
|
||||||
|
offset_right = 902.0
|
||||||
|
offset_bottom = 119.0
|
||||||
|
grow_horizontal = 0
|
||||||
|
scale = Vector2(1.54345, 1.50915)
|
||||||
|
theme_override_font_sizes/font_size = 25
|
||||||
|
text = "重置"
|
||||||
|
|
||||||
|
[node name="NextButton" type="Button" parent="VirtualControls"]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 1
|
||||||
|
anchor_left = 1.0
|
||||||
|
anchor_right = 1.0
|
||||||
|
offset_left = 819.0
|
||||||
|
offset_top = -110.0
|
||||||
|
offset_right = 902.0
|
||||||
|
offset_bottom = -53.0
|
||||||
|
grow_horizontal = 0
|
||||||
|
scale = Vector2(1.54345, 1.50915)
|
||||||
|
theme_override_font_sizes/font_size = 25
|
||||||
|
text = "下一关"
|
||||||
|
|
||||||
|
[node name="LastButton" type="Button" parent="VirtualControls"]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 1
|
||||||
|
anchor_left = 1.0
|
||||||
|
anchor_right = 1.0
|
||||||
|
offset_left = 819.0
|
||||||
|
offset_top = -24.0
|
||||||
|
offset_right = 902.0
|
||||||
|
offset_bottom = 33.0
|
||||||
|
grow_horizontal = 0
|
||||||
|
scale = Vector2(1.54345, 1.50915)
|
||||||
|
theme_override_font_sizes/font_size = 25
|
||||||
|
text = "上一关"
|
||||||
|
|
||||||
|
[node name="QuitButton" type="Button" parent="VirtualControls"]
|
||||||
|
self_modulate = Color(1, 0.247059, 0, 1)
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 1
|
||||||
|
anchor_left = 1.0
|
||||||
|
anchor_right = 1.0
|
||||||
|
offset_left = 947.0
|
||||||
|
offset_top = -248.0
|
||||||
|
offset_right = 1030.0
|
||||||
|
offset_bottom = -191.0
|
||||||
|
grow_horizontal = 0
|
||||||
|
scale = Vector2(1.54345, 1.50915)
|
||||||
|
theme_override_font_sizes/font_size = 25
|
||||||
|
text = "关闭"
|
||||||
|
|
||||||
|
[connection signal="pressed" from="VirtualControls/NextButton" to="." method="_on_next_button_pressed"]
|
||||||
|
[connection signal="pressed" from="VirtualControls/LastButton" to="." method="_on_last_button_pressed"]
|
||||||
|
[connection signal="pressed" from="VirtualControls/QuitButton" to="." method="_on_quit_button_pressed"]
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ extends Panel
|
|||||||
const GRID_SIZE = 20
|
const GRID_SIZE = 20
|
||||||
const GRID_WIDTH = 30
|
const GRID_WIDTH = 30
|
||||||
const GRID_HEIGHT = 30
|
const GRID_HEIGHT = 30
|
||||||
|
const DATA_FILE_PATH = "user://playergamedata.json"
|
||||||
|
|
||||||
# 方向枚举
|
# 方向枚举
|
||||||
enum Direction {
|
enum Direction {
|
||||||
@@ -13,36 +14,67 @@ enum Direction {
|
|||||||
RIGHT
|
RIGHT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# 食物类型枚举
|
||||||
|
enum FoodType {
|
||||||
|
NORMAL, # 普通食物 +10分
|
||||||
|
GOLDEN, # 金色食物 +50分
|
||||||
|
SPEED, # 加速食物 +20分,临时加速
|
||||||
|
SLOW, # 减速食物 +30分,临时减速
|
||||||
|
BONUS # 奖励食物 +100分
|
||||||
|
}
|
||||||
|
|
||||||
# 游戏变量
|
# 游戏变量
|
||||||
var snake_body = []
|
var snake_body = []
|
||||||
var snake_direction = Direction.RIGHT
|
var snake_direction = Direction.RIGHT
|
||||||
var next_direction = Direction.RIGHT
|
var next_direction = Direction.RIGHT
|
||||||
var food_position = Vector2()
|
var food_position = Vector2()
|
||||||
|
var food_type = FoodType.NORMAL
|
||||||
var score = 0
|
var score = 0
|
||||||
|
var best_score = 0
|
||||||
|
var level = 1
|
||||||
|
var speed_multiplier = 1.0
|
||||||
|
var speed_effect_timer = 0.0
|
||||||
var game_over = false
|
var game_over = false
|
||||||
|
var game_started = false # 添加游戏开始状态
|
||||||
|
var obstacles = []
|
||||||
|
var games_played = 0
|
||||||
|
var total_food_eaten = 0
|
||||||
|
var player_data = {}
|
||||||
|
|
||||||
# 节点引用
|
# 节点引用
|
||||||
@onready var game_area = $GameArea
|
@onready var game_area = $GameArea
|
||||||
@onready var score_label = $ScoreLabel
|
@onready var score_label = $ScoreLabel
|
||||||
@onready var game_over_label = $GameOverLabel
|
@onready var game_over_label = $GameOverLabel
|
||||||
@onready var game_timer = $GameTimer
|
@onready var game_timer = $GameTimer
|
||||||
|
@onready var virtual_controls = $VirtualControls
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
# 连接定时器信号
|
# 连接定时器信号
|
||||||
game_timer.timeout.connect(_on_game_timer_timeout)
|
game_timer.timeout.connect(_on_game_timer_timeout)
|
||||||
|
|
||||||
# 设置游戏区域样式
|
# 设置游戏区域样式
|
||||||
game_area.modulate = Color(0.2, 0.2, 0.2, 1.0)
|
game_area.modulate = Color(0.1, 0.1, 0.15, 0.9)
|
||||||
|
|
||||||
# 初始化游戏
|
# 连接虚拟按键信号
|
||||||
init_game()
|
setup_virtual_controls()
|
||||||
|
|
||||||
|
# 加载玩家数据
|
||||||
|
load_player_data()
|
||||||
|
|
||||||
|
# 显示游戏开始界面
|
||||||
|
show_start_screen()
|
||||||
|
|
||||||
func init_game():
|
func init_game():
|
||||||
# 重置游戏状态
|
# 重置游戏状态
|
||||||
game_over = false
|
game_over = false
|
||||||
|
game_started = true
|
||||||
score = 0
|
score = 0
|
||||||
|
level = 1
|
||||||
|
speed_multiplier = 1.0
|
||||||
|
speed_effect_timer = 0.0
|
||||||
snake_direction = Direction.RIGHT
|
snake_direction = Direction.RIGHT
|
||||||
next_direction = Direction.RIGHT
|
next_direction = Direction.RIGHT
|
||||||
|
games_played += 1
|
||||||
|
|
||||||
# 初始化蛇身
|
# 初始化蛇身
|
||||||
snake_body.clear()
|
snake_body.clear()
|
||||||
@@ -50,42 +82,74 @@ func init_game():
|
|||||||
snake_body.append(Vector2(4, 5))
|
snake_body.append(Vector2(4, 5))
|
||||||
snake_body.append(Vector2(3, 5))
|
snake_body.append(Vector2(3, 5))
|
||||||
|
|
||||||
# 生成食物
|
# 清空障碍物
|
||||||
|
obstacles.clear()
|
||||||
|
|
||||||
|
# 生成食物和障碍物
|
||||||
generate_food()
|
generate_food()
|
||||||
|
generate_obstacles()
|
||||||
|
|
||||||
# 更新UI
|
# 更新UI
|
||||||
update_score()
|
update_score()
|
||||||
game_over_label.visible = false
|
game_over_label.visible = false
|
||||||
|
|
||||||
# 启动定时器
|
# 设置定时器速度
|
||||||
|
game_timer.wait_time = 0.2 / speed_multiplier
|
||||||
game_timer.start()
|
game_timer.start()
|
||||||
|
|
||||||
func _input(event):
|
func _input(event):
|
||||||
if event is InputEventKey and event.pressed:
|
if event is InputEventKey and event.pressed:
|
||||||
|
# 游戏未开始时,按Q键开始游戏
|
||||||
|
if not game_started:
|
||||||
|
if event.keycode == KEY_Q:
|
||||||
|
init_game()
|
||||||
|
return
|
||||||
|
|
||||||
if game_over:
|
if game_over:
|
||||||
if event.keycode == KEY_SPACE:
|
if event.keycode == KEY_SPACE:
|
||||||
init_game()
|
init_game()
|
||||||
|
elif event.keycode == KEY_Q:
|
||||||
|
init_game()
|
||||||
return
|
return
|
||||||
|
|
||||||
# 控制蛇的方向
|
# 控制蛇的方向
|
||||||
match event.keycode:
|
match event.keycode:
|
||||||
KEY_UP, KEY_W:
|
KEY_UP, KEY_W:
|
||||||
if snake_direction != Direction.DOWN:
|
change_direction(Direction.UP)
|
||||||
next_direction = Direction.UP
|
|
||||||
KEY_DOWN, KEY_S:
|
KEY_DOWN, KEY_S:
|
||||||
if snake_direction != Direction.UP:
|
change_direction(Direction.DOWN)
|
||||||
next_direction = Direction.DOWN
|
|
||||||
KEY_LEFT, KEY_A:
|
KEY_LEFT, KEY_A:
|
||||||
if snake_direction != Direction.RIGHT:
|
change_direction(Direction.LEFT)
|
||||||
next_direction = Direction.LEFT
|
|
||||||
KEY_RIGHT, KEY_D:
|
KEY_RIGHT, KEY_D:
|
||||||
if snake_direction != Direction.LEFT:
|
change_direction(Direction.RIGHT)
|
||||||
next_direction = Direction.RIGHT
|
|
||||||
|
func change_direction(new_direction: Direction):
|
||||||
|
# 防止蛇反向移动
|
||||||
|
match new_direction:
|
||||||
|
Direction.UP:
|
||||||
|
if snake_direction != Direction.DOWN:
|
||||||
|
next_direction = Direction.UP
|
||||||
|
Direction.DOWN:
|
||||||
|
if snake_direction != Direction.UP:
|
||||||
|
next_direction = Direction.DOWN
|
||||||
|
Direction.LEFT:
|
||||||
|
if snake_direction != Direction.RIGHT:
|
||||||
|
next_direction = Direction.LEFT
|
||||||
|
Direction.RIGHT:
|
||||||
|
if snake_direction != Direction.LEFT:
|
||||||
|
next_direction = Direction.RIGHT
|
||||||
|
|
||||||
func _on_game_timer_timeout():
|
func _on_game_timer_timeout():
|
||||||
if game_over:
|
if not game_started or game_over:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# 处理速度效果
|
||||||
|
if speed_effect_timer > 0:
|
||||||
|
speed_effect_timer -= game_timer.wait_time
|
||||||
|
if speed_effect_timer <= 0:
|
||||||
|
speed_multiplier = 1.0
|
||||||
|
game_timer.wait_time = 0.2 / speed_multiplier
|
||||||
|
|
||||||
# 更新方向
|
# 更新方向
|
||||||
snake_direction = next_direction
|
snake_direction = next_direction
|
||||||
|
|
||||||
@@ -118,12 +182,14 @@ func move_snake():
|
|||||||
|
|
||||||
# 检查是否吃到食物
|
# 检查是否吃到食物
|
||||||
if new_head == food_position:
|
if new_head == food_position:
|
||||||
# 增加分数
|
# 根据食物类型增加分数和效果
|
||||||
score += 10
|
eat_food()
|
||||||
update_score()
|
|
||||||
|
|
||||||
# 生成新食物
|
# 生成新食物
|
||||||
generate_food()
|
generate_food()
|
||||||
|
|
||||||
|
# 检查等级提升
|
||||||
|
check_level_up()
|
||||||
else:
|
else:
|
||||||
# 移除尾部
|
# 移除尾部
|
||||||
snake_body.pop_back()
|
snake_body.pop_back()
|
||||||
@@ -143,6 +209,13 @@ func check_collisions():
|
|||||||
game_over = true
|
game_over = true
|
||||||
show_game_over()
|
show_game_over()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# 检查障碍物碰撞
|
||||||
|
for obstacle in obstacles:
|
||||||
|
if head == obstacle:
|
||||||
|
game_over = true
|
||||||
|
show_game_over()
|
||||||
|
return
|
||||||
|
|
||||||
func generate_food():
|
func generate_food():
|
||||||
var attempts = 0
|
var attempts = 0
|
||||||
@@ -152,56 +225,337 @@ func generate_food():
|
|||||||
randi() % GRID_HEIGHT
|
randi() % GRID_HEIGHT
|
||||||
)
|
)
|
||||||
|
|
||||||
# 确保食物不在蛇身上
|
# 确保食物不在蛇身上和障碍物上
|
||||||
var food_on_snake = false
|
var food_blocked = false
|
||||||
for segment in snake_body:
|
for segment in snake_body:
|
||||||
if segment == food_position:
|
if segment == food_position:
|
||||||
food_on_snake = true
|
food_blocked = true
|
||||||
break
|
break
|
||||||
|
|
||||||
if not food_on_snake:
|
if not food_blocked:
|
||||||
|
for obstacle in obstacles:
|
||||||
|
if obstacle == food_position:
|
||||||
|
food_blocked = true
|
||||||
|
break
|
||||||
|
|
||||||
|
if not food_blocked:
|
||||||
break
|
break
|
||||||
|
|
||||||
attempts += 1
|
attempts += 1
|
||||||
|
|
||||||
|
# 随机生成食物类型
|
||||||
|
var rand = randf()
|
||||||
|
if rand < 0.6: # 60% 普通食物
|
||||||
|
food_type = FoodType.NORMAL
|
||||||
|
elif rand < 0.75: # 15% 金色食物
|
||||||
|
food_type = FoodType.GOLDEN
|
||||||
|
elif rand < 0.85: # 10% 加速食物
|
||||||
|
food_type = FoodType.SPEED
|
||||||
|
elif rand < 0.95: # 10% 减速食物
|
||||||
|
food_type = FoodType.SLOW
|
||||||
|
else: # 5% 奖励食物
|
||||||
|
food_type = FoodType.BONUS
|
||||||
|
|
||||||
func update_score():
|
func update_score():
|
||||||
score_label.text = "分数: " + str(score)
|
score_label.text = "🐍 分数: " + str(score) + "\n🏆 最高分: " + str(best_score) + "\n⭐ 等级: " + str(level) + "\n🎮 游戏次数: " + str(games_played)
|
||||||
|
|
||||||
func show_game_over():
|
func show_game_over():
|
||||||
game_timer.stop()
|
game_timer.stop()
|
||||||
|
game_started = false
|
||||||
|
if score > best_score:
|
||||||
|
best_score = score
|
||||||
|
update_score()
|
||||||
|
save_player_data()
|
||||||
|
game_over_label.text = "🎮 游戏结束\n🏆 分数: " + str(score) + "\n⭐ 等级: " + str(level) + "\n\n🔄 按Q键或空格重新开始"
|
||||||
game_over_label.visible = true
|
game_over_label.visible = true
|
||||||
|
|
||||||
|
func eat_food():
|
||||||
|
total_food_eaten += 1
|
||||||
|
match food_type:
|
||||||
|
FoodType.NORMAL:
|
||||||
|
score += 10
|
||||||
|
FoodType.GOLDEN:
|
||||||
|
score += 50
|
||||||
|
FoodType.SPEED:
|
||||||
|
score += 20
|
||||||
|
speed_multiplier = 1.5
|
||||||
|
speed_effect_timer = 5.0
|
||||||
|
game_timer.wait_time = 0.2 / speed_multiplier
|
||||||
|
FoodType.SLOW:
|
||||||
|
score += 30
|
||||||
|
speed_multiplier = 0.7
|
||||||
|
speed_effect_timer = 5.0
|
||||||
|
game_timer.wait_time = 0.2 / speed_multiplier
|
||||||
|
FoodType.BONUS:
|
||||||
|
score += 100
|
||||||
|
update_score()
|
||||||
|
|
||||||
|
func check_level_up():
|
||||||
|
var new_level = (total_food_eaten / 10) + 1
|
||||||
|
if new_level > level:
|
||||||
|
level = new_level
|
||||||
|
generate_obstacles() # 每升级增加障碍物
|
||||||
|
|
||||||
|
func generate_obstacles():
|
||||||
|
# 根据等级生成障碍物
|
||||||
|
var obstacle_count = min(level - 1, 10) # 最多10个障碍物
|
||||||
|
obstacles.clear()
|
||||||
|
|
||||||
|
for i in range(obstacle_count):
|
||||||
|
var attempts = 0
|
||||||
|
while attempts < 50:
|
||||||
|
var obstacle_pos = Vector2(
|
||||||
|
randi() % GRID_WIDTH,
|
||||||
|
randi() % GRID_HEIGHT
|
||||||
|
)
|
||||||
|
|
||||||
|
# 确保障碍物不在蛇身、食物或其他障碍物上
|
||||||
|
var blocked = false
|
||||||
|
for segment in snake_body:
|
||||||
|
if segment == obstacle_pos:
|
||||||
|
blocked = true
|
||||||
|
break
|
||||||
|
|
||||||
|
if not blocked and obstacle_pos == food_position:
|
||||||
|
blocked = true
|
||||||
|
|
||||||
|
if not blocked:
|
||||||
|
for existing_obstacle in obstacles:
|
||||||
|
if existing_obstacle == obstacle_pos:
|
||||||
|
blocked = true
|
||||||
|
break
|
||||||
|
|
||||||
|
if not blocked:
|
||||||
|
obstacles.append(obstacle_pos)
|
||||||
|
break
|
||||||
|
|
||||||
|
attempts += 1
|
||||||
|
|
||||||
|
func setup_virtual_controls():
|
||||||
|
if not virtual_controls:
|
||||||
|
return
|
||||||
|
|
||||||
|
var up_btn = virtual_controls.get_node("UpButton")
|
||||||
|
var down_btn = virtual_controls.get_node("DownButton")
|
||||||
|
var left_btn = virtual_controls.get_node("LeftButton")
|
||||||
|
var right_btn = virtual_controls.get_node("RightButton")
|
||||||
|
var restart_btn = virtual_controls.get_node("RestartButton")
|
||||||
|
|
||||||
|
if up_btn:
|
||||||
|
up_btn.pressed.connect(_on_virtual_button_pressed.bind(Direction.UP))
|
||||||
|
if down_btn:
|
||||||
|
down_btn.pressed.connect(_on_virtual_button_pressed.bind(Direction.DOWN))
|
||||||
|
if left_btn:
|
||||||
|
left_btn.pressed.connect(_on_virtual_button_pressed.bind(Direction.LEFT))
|
||||||
|
if right_btn:
|
||||||
|
right_btn.pressed.connect(_on_virtual_button_pressed.bind(Direction.RIGHT))
|
||||||
|
if restart_btn:
|
||||||
|
restart_btn.pressed.connect(_on_restart_button_pressed)
|
||||||
|
|
||||||
|
func _on_virtual_button_pressed(direction: Direction):
|
||||||
|
if game_started and not game_over:
|
||||||
|
change_direction(direction)
|
||||||
|
|
||||||
|
func _on_restart_button_pressed():
|
||||||
|
if not game_started or game_over:
|
||||||
|
init_game()
|
||||||
|
|
||||||
|
func load_player_data():
|
||||||
|
if FileAccess.file_exists(DATA_FILE_PATH):
|
||||||
|
var file = FileAccess.open(DATA_FILE_PATH, FileAccess.READ)
|
||||||
|
if file:
|
||||||
|
var json_string = file.get_as_text()
|
||||||
|
file.close()
|
||||||
|
|
||||||
|
var json = JSON.new()
|
||||||
|
var parse_result = json.parse(json_string)
|
||||||
|
if parse_result == OK:
|
||||||
|
player_data = json.data
|
||||||
|
if player_data.has("snake"):
|
||||||
|
var game_data = player_data["snake"]
|
||||||
|
best_score = game_data.get("best_score", 0)
|
||||||
|
games_played = game_data.get("games_played", 0)
|
||||||
|
total_food_eaten = game_data.get("total_food_eaten", 0)
|
||||||
|
|
||||||
|
func show_start_screen():
|
||||||
|
# 重置状态
|
||||||
|
game_started = false
|
||||||
|
game_over = false
|
||||||
|
|
||||||
|
# 初始化蛇身用于显示
|
||||||
|
snake_body.clear()
|
||||||
|
snake_body.append(Vector2(5, 5))
|
||||||
|
snake_body.append(Vector2(4, 5))
|
||||||
|
snake_body.append(Vector2(3, 5))
|
||||||
|
|
||||||
|
# 清空障碍物
|
||||||
|
obstacles.clear()
|
||||||
|
|
||||||
|
# 生成初始食物
|
||||||
|
food_position = Vector2(10, 10)
|
||||||
|
food_type = FoodType.NORMAL
|
||||||
|
|
||||||
|
# 显示开始提示
|
||||||
|
game_over_label.text = "🐍 贪吃蛇游戏 🐍\n\n🏆 最高分数: " + str(best_score) + "\n🎯 游戏次数: " + str(games_played) + "\n\n🎮 按Q键开始游戏\n\n🎯 操作说明:\n方向键/WASD - 控制方向\n\n🍎 食物类型:\n🔴 普通食物 +10分\n🟡 金色食物 +50分\n🔵 加速食物 +20分\n🟣 减速食物 +30分\n🌈 奖励食物 +100分"
|
||||||
|
game_over_label.visible = true
|
||||||
|
|
||||||
|
# 停止定时器
|
||||||
|
game_timer.stop()
|
||||||
|
|
||||||
|
update_score()
|
||||||
|
queue_redraw()
|
||||||
|
|
||||||
|
func save_player_data():
|
||||||
|
if not player_data.has("snake"):
|
||||||
|
player_data["snake"] = {}
|
||||||
|
|
||||||
|
player_data["snake"]["best_score"] = best_score
|
||||||
|
player_data["snake"]["current_score"] = score
|
||||||
|
player_data["snake"]["games_played"] = games_played
|
||||||
|
player_data["snake"]["total_food_eaten"] = total_food_eaten
|
||||||
|
player_data["snake"]["max_level_reached"] = level
|
||||||
|
|
||||||
|
# 更新全局数据
|
||||||
|
if not player_data.has("global"):
|
||||||
|
player_data["global"] = {}
|
||||||
|
player_data["global"]["last_played"] = Time.get_datetime_string_from_system()
|
||||||
|
|
||||||
|
var file = FileAccess.open(DATA_FILE_PATH, FileAccess.WRITE)
|
||||||
|
if file:
|
||||||
|
var json_string = JSON.stringify(player_data)
|
||||||
|
file.store_string(json_string)
|
||||||
|
file.close()
|
||||||
|
|
||||||
func _draw():
|
func _draw():
|
||||||
if not game_area:
|
if not game_area:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# 绘制背景渐变
|
||||||
|
var gradient = Gradient.new()
|
||||||
|
gradient.add_point(0.0, Color(0.05, 0.1, 0.2, 0.95))
|
||||||
|
gradient.add_point(0.5, Color(0.1, 0.15, 0.25, 0.9))
|
||||||
|
gradient.add_point(1.0, Color(0.15, 0.2, 0.3, 0.95))
|
||||||
|
draw_rect(Rect2(Vector2.ZERO, size), gradient.sample(0.5), true)
|
||||||
|
|
||||||
# 获取游戏区域的位置和大小
|
# 获取游戏区域的位置和大小
|
||||||
var area_pos = game_area.position
|
var area_pos = game_area.position
|
||||||
var area_size = game_area.size
|
var area_size = game_area.size
|
||||||
|
|
||||||
|
# 绘制游戏区域阴影
|
||||||
|
var shadow_offset = Vector2(4, 4)
|
||||||
|
var area_rect = Rect2(area_pos + shadow_offset, area_size)
|
||||||
|
draw_rect(area_rect, Color(0, 0, 0, 0.3), true)
|
||||||
|
|
||||||
|
# 绘制游戏区域背景
|
||||||
|
area_rect = Rect2(area_pos, area_size)
|
||||||
|
draw_rect(area_rect, Color(0.08, 0.12, 0.18, 0.9), true)
|
||||||
|
|
||||||
# 计算网格大小
|
# 计算网格大小
|
||||||
var cell_width = area_size.x / GRID_WIDTH
|
var cell_width = area_size.x / GRID_WIDTH
|
||||||
var cell_height = area_size.y / GRID_HEIGHT
|
var cell_height = area_size.y / GRID_HEIGHT
|
||||||
|
|
||||||
|
# 绘制网格线(淡色)
|
||||||
|
for x in range(GRID_WIDTH + 1):
|
||||||
|
var start_pos = Vector2(area_pos.x + x * cell_width, area_pos.y)
|
||||||
|
var end_pos = Vector2(area_pos.x + x * cell_width, area_pos.y + area_size.y)
|
||||||
|
draw_line(start_pos, end_pos, Color(0.3, 0.3, 0.4, 0.3), 1)
|
||||||
|
|
||||||
|
for y in range(GRID_HEIGHT + 1):
|
||||||
|
var start_pos = Vector2(area_pos.x, area_pos.y + y * cell_height)
|
||||||
|
var end_pos = Vector2(area_pos.x + area_size.x, area_pos.y + y * cell_height)
|
||||||
|
draw_line(start_pos, end_pos, Color(0.3, 0.3, 0.4, 0.3), 1)
|
||||||
|
|
||||||
|
# 绘制障碍物
|
||||||
|
for obstacle in obstacles:
|
||||||
|
var rect = Rect2(
|
||||||
|
area_pos.x + obstacle.x * cell_width,
|
||||||
|
area_pos.y + obstacle.y * cell_height,
|
||||||
|
cell_width - 2,
|
||||||
|
cell_height - 2
|
||||||
|
)
|
||||||
|
# 绘制立体障碍物效果
|
||||||
|
draw_rect(rect, Color(0.4, 0.2, 0.1), true)
|
||||||
|
# 高光
|
||||||
|
var highlight_rect = Rect2(rect.position + Vector2(1, 1), Vector2(rect.size.x - 2, rect.size.y * 0.3))
|
||||||
|
draw_rect(highlight_rect, Color(0.6, 0.4, 0.2, 0.8), true)
|
||||||
|
|
||||||
# 绘制蛇身
|
# 绘制蛇身
|
||||||
for i in range(snake_body.size()):
|
for i in range(snake_body.size()):
|
||||||
var segment = snake_body[i]
|
var segment = snake_body[i]
|
||||||
var rect = Rect2(
|
var rect = Rect2(
|
||||||
area_pos.x + segment.x * cell_width,
|
area_pos.x + segment.x * cell_width,
|
||||||
area_pos.y + segment.y * cell_height,
|
area_pos.y + segment.y * cell_height,
|
||||||
cell_width - 1,
|
cell_width - 2,
|
||||||
cell_height - 1
|
cell_height - 2
|
||||||
)
|
)
|
||||||
|
|
||||||
# 头部用不同颜色
|
if i == 0: # 头部
|
||||||
var color = Color.GREEN if i == 0 else Color.LIME_GREEN
|
# 绘制蛇头(圆形,带渐变)
|
||||||
draw_rect(rect, color)
|
var center = rect.get_center()
|
||||||
|
var radius = min(rect.size.x, rect.size.y) * 0.4
|
||||||
|
# 阴影
|
||||||
|
draw_circle(center + Vector2(1, 1), radius, Color(0, 0, 0, 0.3))
|
||||||
|
# 主体
|
||||||
|
draw_circle(center, radius, Color(0.2, 0.8, 0.2))
|
||||||
|
# 高光
|
||||||
|
draw_circle(center - Vector2(2, 2), radius * 0.6, Color(0.4, 1.0, 0.4, 0.7))
|
||||||
|
# 眼睛
|
||||||
|
var eye_size = radius * 0.2
|
||||||
|
draw_circle(center + Vector2(-eye_size, -eye_size), eye_size * 0.5, Color.BLACK)
|
||||||
|
draw_circle(center + Vector2(eye_size, -eye_size), eye_size * 0.5, Color.BLACK)
|
||||||
|
else: # 身体
|
||||||
|
# 绘制蛇身(渐变色)
|
||||||
|
var body_color = Color.LIME_GREEN.lerp(Color.DARK_GREEN, float(i) / snake_body.size())
|
||||||
|
draw_rect(rect, body_color, true)
|
||||||
|
# 高光
|
||||||
|
var highlight_rect = Rect2(rect.position + Vector2(1, 1), Vector2(rect.size.x - 2, rect.size.y * 0.3))
|
||||||
|
draw_rect(highlight_rect, Color(1, 1, 1, 0.3), true)
|
||||||
|
|
||||||
# 绘制食物
|
# 绘制食物
|
||||||
var food_rect = Rect2(
|
var food_rect = Rect2(
|
||||||
area_pos.x + food_position.x * cell_width,
|
area_pos.x + food_position.x * cell_width,
|
||||||
area_pos.y + food_position.y * cell_height,
|
area_pos.y + food_position.y * cell_height,
|
||||||
cell_width - 1,
|
cell_width - 2,
|
||||||
cell_height - 1
|
cell_height - 2
|
||||||
)
|
)
|
||||||
draw_rect(food_rect, Color.RED)
|
|
||||||
|
# 根据食物类型绘制不同效果
|
||||||
|
var food_center = food_rect.get_center()
|
||||||
|
var food_radius = min(food_rect.size.x, food_rect.size.y) * 0.4
|
||||||
|
|
||||||
|
match food_type:
|
||||||
|
FoodType.NORMAL:
|
||||||
|
# 普通红色食物
|
||||||
|
draw_circle(food_center + Vector2(1, 1), food_radius, Color(0, 0, 0, 0.3)) # 阴影
|
||||||
|
draw_circle(food_center, food_radius, Color.RED)
|
||||||
|
draw_circle(food_center - Vector2(1, 1), food_radius * 0.6, Color(1, 0.5, 0.5, 0.8)) # 高光
|
||||||
|
FoodType.GOLDEN:
|
||||||
|
# 金色食物(闪烁效果)
|
||||||
|
var pulse = sin(Time.get_ticks_msec() * 0.008) * 0.2 + 0.8
|
||||||
|
draw_circle(food_center + Vector2(1, 1), food_radius, Color(0, 0, 0, 0.3)) # 阴影
|
||||||
|
draw_circle(food_center, food_radius, Color.GOLD * pulse)
|
||||||
|
draw_circle(food_center - Vector2(1, 1), food_radius * 0.6, Color.YELLOW) # 高光
|
||||||
|
FoodType.SPEED:
|
||||||
|
# 蓝色加速食物
|
||||||
|
draw_circle(food_center + Vector2(1, 1), food_radius, Color(0, 0, 0, 0.3)) # 阴影
|
||||||
|
draw_circle(food_center, food_radius, Color.CYAN)
|
||||||
|
draw_circle(food_center - Vector2(1, 1), food_radius * 0.6, Color.LIGHT_BLUE) # 高光
|
||||||
|
FoodType.SLOW:
|
||||||
|
# 紫色减速食物
|
||||||
|
draw_circle(food_center + Vector2(1, 1), food_radius, Color(0, 0, 0, 0.3)) # 阴影
|
||||||
|
draw_circle(food_center, food_radius, Color.PURPLE)
|
||||||
|
draw_circle(food_center - Vector2(1, 1), food_radius * 0.6, Color.MAGENTA) # 高光
|
||||||
|
FoodType.BONUS:
|
||||||
|
# 彩虹奖励食物(旋转彩虹效果)
|
||||||
|
var rainbow_time = Time.get_ticks_msec() * 0.003
|
||||||
|
var rainbow_color = Color.from_hsv(fmod(rainbow_time, 1.0), 1.0, 1.0)
|
||||||
|
draw_circle(food_center + Vector2(1, 1), food_radius, Color(0, 0, 0, 0.3)) # 阴影
|
||||||
|
draw_circle(food_center, food_radius, rainbow_color)
|
||||||
|
draw_circle(food_center - Vector2(1, 1), food_radius * 0.6, Color.WHITE) # 高光
|
||||||
|
|
||||||
|
|
||||||
|
func _on_quit_button_pressed() -> void:
|
||||||
|
self.hide()
|
||||||
|
get_parent().remove_child(self)
|
||||||
|
queue_free()
|
||||||
|
pass
|
||||||
|
|||||||
@@ -26,11 +26,11 @@ layout_mode = 1
|
|||||||
anchors_preset = 2
|
anchors_preset = 2
|
||||||
anchor_top = 1.0
|
anchor_top = 1.0
|
||||||
anchor_bottom = 1.0
|
anchor_bottom = 1.0
|
||||||
offset_left = 20.0
|
offset_top = -726.0
|
||||||
offset_top = -50.0
|
offset_right = 180.0
|
||||||
offset_right = 200.0
|
offset_bottom = -684.0
|
||||||
offset_bottom = -20.0
|
|
||||||
grow_vertical = 0
|
grow_vertical = 0
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
text = "分数: 0"
|
text = "分数: 0"
|
||||||
|
|
||||||
[node name="GameOverLabel" type="Label" parent="."]
|
[node name="GameOverLabel" type="Label" parent="."]
|
||||||
@@ -55,3 +55,120 @@ vertical_alignment = 1
|
|||||||
[node name="GameTimer" type="Timer" parent="."]
|
[node name="GameTimer" type="Timer" parent="."]
|
||||||
wait_time = 0.2
|
wait_time = 0.2
|
||||||
autostart = true
|
autostart = true
|
||||||
|
|
||||||
|
[node name="VirtualControls" type="Control" parent="."]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_top = 160.0
|
||||||
|
offset_bottom = 160.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
|
||||||
|
[node name="UpButton" type="Button" parent="VirtualControls"]
|
||||||
|
custom_minimum_size = Vector2(80, 80)
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 5
|
||||||
|
anchor_left = 0.5
|
||||||
|
anchor_right = 0.5
|
||||||
|
offset_left = -529.5
|
||||||
|
offset_top = 114.0
|
||||||
|
offset_right = -449.5
|
||||||
|
offset_bottom = 194.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
theme_override_font_sizes/font_size = 35
|
||||||
|
text = "W"
|
||||||
|
|
||||||
|
[node name="TestButton" type="Button" parent="VirtualControls"]
|
||||||
|
custom_minimum_size = Vector2(80, 80)
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 5
|
||||||
|
anchor_left = 0.5
|
||||||
|
anchor_right = 0.5
|
||||||
|
offset_left = -529.5
|
||||||
|
offset_top = 194.0
|
||||||
|
offset_right = -449.5
|
||||||
|
offset_bottom = 274.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
theme_override_font_sizes/font_size = 35
|
||||||
|
disabled = true
|
||||||
|
|
||||||
|
[node name="DownButton" type="Button" parent="VirtualControls"]
|
||||||
|
custom_minimum_size = Vector2(80, 80)
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 5
|
||||||
|
anchor_left = 0.5
|
||||||
|
anchor_right = 0.5
|
||||||
|
offset_left = -529.5
|
||||||
|
offset_top = 274.0
|
||||||
|
offset_right = -449.5
|
||||||
|
offset_bottom = 354.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
theme_override_font_sizes/font_size = 35
|
||||||
|
text = "S"
|
||||||
|
|
||||||
|
[node name="LeftButton" type="Button" parent="VirtualControls"]
|
||||||
|
custom_minimum_size = Vector2(80, 80)
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 4
|
||||||
|
anchor_top = 0.5
|
||||||
|
anchor_bottom = 0.5
|
||||||
|
offset_left = 94.0
|
||||||
|
offset_top = -169.0
|
||||||
|
offset_right = 174.0
|
||||||
|
offset_bottom = -89.0
|
||||||
|
grow_vertical = 2
|
||||||
|
theme_override_font_sizes/font_size = 35
|
||||||
|
text = "A"
|
||||||
|
|
||||||
|
[node name="RightButton" type="Button" parent="VirtualControls"]
|
||||||
|
custom_minimum_size = Vector2(80, 80)
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 6
|
||||||
|
anchor_left = 1.0
|
||||||
|
anchor_top = 0.5
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 0.5
|
||||||
|
offset_left = -1151.0
|
||||||
|
offset_top = -169.0
|
||||||
|
offset_right = -1071.0
|
||||||
|
offset_bottom = -89.0
|
||||||
|
grow_horizontal = 0
|
||||||
|
grow_vertical = 2
|
||||||
|
theme_override_font_sizes/font_size = 35
|
||||||
|
text = "D"
|
||||||
|
|
||||||
|
[node name="RestartButton" type="Button" parent="VirtualControls"]
|
||||||
|
custom_minimum_size = Vector2(80, 80)
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 3
|
||||||
|
anchor_left = 1.0
|
||||||
|
anchor_top = 1.0
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_left = -1291.0
|
||||||
|
offset_top = -276.0
|
||||||
|
offset_right = -1086.0
|
||||||
|
offset_bottom = -196.0
|
||||||
|
grow_horizontal = 0
|
||||||
|
grow_vertical = 0
|
||||||
|
theme_override_font_sizes/font_size = 35
|
||||||
|
text = "🔄 重新开始"
|
||||||
|
|
||||||
|
[node name="QuitButton" type="Button" parent="."]
|
||||||
|
self_modulate = Color(1, 0.247059, 0, 1)
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 1
|
||||||
|
anchor_left = 1.0
|
||||||
|
anchor_right = 1.0
|
||||||
|
offset_left = -137.0
|
||||||
|
offset_top = -1.52588e-05
|
||||||
|
offset_right = -54.0
|
||||||
|
offset_bottom = 57.0
|
||||||
|
grow_horizontal = 0
|
||||||
|
scale = Vector2(1.54345, 1.50915)
|
||||||
|
theme_override_font_sizes/font_size = 25
|
||||||
|
text = "关闭"
|
||||||
|
|
||||||
|
[connection signal="pressed" from="QuitButton" to="." method="_on_quit_button_pressed"]
|
||||||
|
|||||||
@@ -4,6 +4,15 @@ extends Panel
|
|||||||
const BOARD_WIDTH = 10
|
const BOARD_WIDTH = 10
|
||||||
const BOARD_HEIGHT = 20
|
const BOARD_HEIGHT = 20
|
||||||
const CELL_SIZE = 30
|
const CELL_SIZE = 30
|
||||||
|
const DATA_FILE_PATH = "user://playergamedata.json"
|
||||||
|
|
||||||
|
# 特殊方块类型
|
||||||
|
enum SpecialType {
|
||||||
|
NORMAL,
|
||||||
|
BOMB, # 炸弹方块,消除周围方块
|
||||||
|
LINE, # 直线方块,消除整行
|
||||||
|
RAINBOW # 彩虹方块,消除同色方块
|
||||||
|
}
|
||||||
|
|
||||||
# 方块类型
|
# 方块类型
|
||||||
enum PieceType {
|
enum PieceType {
|
||||||
@@ -68,7 +77,14 @@ var score = 0
|
|||||||
var level = 1
|
var level = 1
|
||||||
var lines_cleared = 0
|
var lines_cleared = 0
|
||||||
var game_over = false
|
var game_over = false
|
||||||
|
var game_started = false # 添加游戏开始状态
|
||||||
var drop_time = 1.0
|
var drop_time = 1.0
|
||||||
|
var best_score = 0
|
||||||
|
var games_played = 0
|
||||||
|
var total_lines_cleared = 0
|
||||||
|
var combo_count = 0
|
||||||
|
var special_pieces = [] # 存储特殊方块位置和类型
|
||||||
|
var player_data = {}
|
||||||
|
|
||||||
# 节点引用
|
# 节点引用
|
||||||
@onready var game_area = $GameArea
|
@onready var game_area = $GameArea
|
||||||
@@ -78,28 +94,39 @@ var drop_time = 1.0
|
|||||||
@onready var lines_label = $LinesLabel
|
@onready var lines_label = $LinesLabel
|
||||||
@onready var game_over_label = $GameOverLabel
|
@onready var game_over_label = $GameOverLabel
|
||||||
@onready var drop_timer = $DropTimer
|
@onready var drop_timer = $DropTimer
|
||||||
|
@onready var virtual_controls = $VirtualControls
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
# 连接定时器信号
|
# 连接定时器信号
|
||||||
drop_timer.timeout.connect(_on_drop_timer_timeout)
|
drop_timer.timeout.connect(_on_drop_timer_timeout)
|
||||||
|
|
||||||
# 设置游戏区域样式
|
# 设置游戏区域样式
|
||||||
game_area.modulate = Color(0.1, 0.1, 0.1, 1.0)
|
game_area.modulate = Color(0.08, 0.12, 0.18, 0.9)
|
||||||
next_piece_area.modulate = Color(0.2, 0.2, 0.2, 1.0)
|
next_piece_area.modulate = Color(0.1, 0.15, 0.25, 0.9)
|
||||||
|
|
||||||
# 初始化游戏
|
# 设置虚拟控制
|
||||||
init_game()
|
setup_virtual_controls()
|
||||||
|
|
||||||
|
# 加载玩家数据
|
||||||
|
load_player_data()
|
||||||
|
|
||||||
|
# 显示游戏开始界面
|
||||||
|
show_start_screen()
|
||||||
|
|
||||||
func init_game():
|
func init_game():
|
||||||
# 重置游戏状态
|
# 重置游戏状态
|
||||||
game_over = false
|
game_over = false
|
||||||
|
game_started = true
|
||||||
score = 0
|
score = 0
|
||||||
level = 1
|
level = 1
|
||||||
lines_cleared = 0
|
lines_cleared = 0
|
||||||
drop_time = 1.0
|
drop_time = 1.0
|
||||||
|
combo_count = 0
|
||||||
|
games_played += 1
|
||||||
|
|
||||||
# 初始化游戏板
|
# 初始化游戏板
|
||||||
board.clear()
|
board.clear()
|
||||||
|
special_pieces.clear()
|
||||||
for y in range(BOARD_HEIGHT):
|
for y in range(BOARD_HEIGHT):
|
||||||
var row = []
|
var row = []
|
||||||
for x in range(BOARD_WIDTH):
|
for x in range(BOARD_WIDTH):
|
||||||
@@ -120,9 +147,17 @@ func init_game():
|
|||||||
|
|
||||||
func _input(event):
|
func _input(event):
|
||||||
if event is InputEventKey and event.pressed:
|
if event is InputEventKey and event.pressed:
|
||||||
|
# 游戏未开始时,按Q键开始游戏
|
||||||
|
if not game_started:
|
||||||
|
if event.keycode == KEY_Q:
|
||||||
|
init_game()
|
||||||
|
return
|
||||||
|
|
||||||
if game_over:
|
if game_over:
|
||||||
if event.keycode == KEY_SPACE:
|
if event.keycode == KEY_SPACE:
|
||||||
init_game()
|
init_game()
|
||||||
|
elif event.keycode == KEY_Q:
|
||||||
|
init_game()
|
||||||
return
|
return
|
||||||
|
|
||||||
if not current_piece:
|
if not current_piece:
|
||||||
@@ -131,15 +166,34 @@ func _input(event):
|
|||||||
# 控制方块
|
# 控制方块
|
||||||
match event.keycode:
|
match event.keycode:
|
||||||
KEY_A:
|
KEY_A:
|
||||||
move_piece(-1, 0)
|
handle_move_left()
|
||||||
KEY_D:
|
KEY_D:
|
||||||
move_piece(1, 0)
|
handle_move_right()
|
||||||
KEY_S:
|
KEY_S:
|
||||||
move_piece(0, 1)
|
handle_move_down()
|
||||||
KEY_W:
|
KEY_W:
|
||||||
rotate_piece()
|
handle_rotate()
|
||||||
KEY_SPACE:
|
KEY_SPACE:
|
||||||
drop_piece()
|
handle_drop()
|
||||||
|
|
||||||
|
# 控制函数
|
||||||
|
func handle_move_left():
|
||||||
|
move_piece(-1, 0)
|
||||||
|
|
||||||
|
func handle_move_right():
|
||||||
|
move_piece(1, 0)
|
||||||
|
|
||||||
|
func handle_move_down():
|
||||||
|
move_piece(0, 1)
|
||||||
|
|
||||||
|
func handle_rotate():
|
||||||
|
rotate_piece()
|
||||||
|
|
||||||
|
func handle_drop():
|
||||||
|
drop_piece()
|
||||||
|
|
||||||
|
func handle_restart():
|
||||||
|
init_game()
|
||||||
|
|
||||||
func spawn_new_piece():
|
func spawn_new_piece():
|
||||||
current_piece = {
|
current_piece = {
|
||||||
@@ -222,6 +276,9 @@ func clear_lines():
|
|||||||
if is_full:
|
if is_full:
|
||||||
lines_to_clear.append(y)
|
lines_to_clear.append(y)
|
||||||
|
|
||||||
|
# 处理特殊方块效果
|
||||||
|
process_special_pieces(lines_to_clear)
|
||||||
|
|
||||||
# 清除行并下移
|
# 清除行并下移
|
||||||
for line_y in lines_to_clear:
|
for line_y in lines_to_clear:
|
||||||
board.erase(board[line_y])
|
board.erase(board[line_y])
|
||||||
@@ -232,15 +289,37 @@ func clear_lines():
|
|||||||
|
|
||||||
# 更新分数和等级
|
# 更新分数和等级
|
||||||
if lines_to_clear.size() > 0:
|
if lines_to_clear.size() > 0:
|
||||||
lines_cleared += lines_to_clear.size()
|
# 连击系统
|
||||||
score += lines_to_clear.size() * 100 * level
|
combo_count += 1
|
||||||
|
var combo_bonus = combo_count * 50
|
||||||
|
|
||||||
# 每10行提升一个等级
|
lines_cleared += lines_to_clear.size()
|
||||||
|
total_lines_cleared += lines_to_clear.size()
|
||||||
|
|
||||||
|
# 计算分数(包含连击奖励)
|
||||||
|
var base_score = lines_to_clear.size() * 100 * level
|
||||||
|
var line_bonus = 0
|
||||||
|
match lines_to_clear.size():
|
||||||
|
1: line_bonus = 0
|
||||||
|
2: line_bonus = 300
|
||||||
|
3: line_bonus = 500
|
||||||
|
4: line_bonus = 800 # 俄罗斯方块
|
||||||
|
|
||||||
|
score += base_score + line_bonus + combo_bonus
|
||||||
|
|
||||||
|
# 每10行提升一个等级,速度递增
|
||||||
level = lines_cleared / 10 + 1
|
level = lines_cleared / 10 + 1
|
||||||
drop_time = max(0.1, 1.0 - (level - 1) * 0.1)
|
drop_time = max(0.05, 1.0 - (level - 1) * 0.08)
|
||||||
drop_timer.wait_time = drop_time
|
drop_timer.wait_time = drop_time
|
||||||
|
|
||||||
update_ui()
|
# 随机生成特殊方块
|
||||||
|
if randf() < 0.1 + level * 0.02: # 等级越高,特殊方块概率越大
|
||||||
|
generate_special_piece()
|
||||||
|
else:
|
||||||
|
# 重置连击
|
||||||
|
combo_count = 0
|
||||||
|
|
||||||
|
update_ui()
|
||||||
|
|
||||||
func get_piece_shape(type: PieceType, rotation: int) -> Array:
|
func get_piece_shape(type: PieceType, rotation: int) -> Array:
|
||||||
return PIECE_SHAPES[type][rotation]
|
return PIECE_SHAPES[type][rotation]
|
||||||
@@ -249,16 +328,27 @@ func get_piece_rotations(type: PieceType) -> int:
|
|||||||
return PIECE_SHAPES[type].size()
|
return PIECE_SHAPES[type].size()
|
||||||
|
|
||||||
func update_ui():
|
func update_ui():
|
||||||
score_label.text = "分数: " + str(score)
|
score_label.text = "🏆 分数: " + str(score) + "\n💎 最高: " + str(best_score)
|
||||||
level_label.text = "等级: " + str(level)
|
level_label.text = "⚡ 等级: " + str(level) + "\n🎮 游戏: " + str(games_played)
|
||||||
lines_label.text = "消除行数: " + str(lines_cleared)
|
lines_label.text = "📊 消除: " + str(lines_cleared) + "\n🔥 连击: " + str(combo_count)
|
||||||
|
|
||||||
func show_game_over():
|
func show_game_over():
|
||||||
drop_timer.stop()
|
drop_timer.stop()
|
||||||
|
game_started = false
|
||||||
|
|
||||||
|
# 检查并更新最高分
|
||||||
|
if score > best_score:
|
||||||
|
best_score = score
|
||||||
|
|
||||||
|
# 保存玩家数据
|
||||||
|
save_player_data()
|
||||||
|
|
||||||
|
# 显示游戏结束信息
|
||||||
|
game_over_label.text = "🎮 游戏结束 🎮\n\n🏆 本次分数: " + str(score) + "\n💎 最高分数: " + str(best_score) + "\n📊 消除行数: " + str(lines_cleared) + "\n⚡ 达到等级: " + str(level) + "\n\n🔄 按Q键或空格重新开始"
|
||||||
game_over_label.visible = true
|
game_over_label.visible = true
|
||||||
|
|
||||||
func _on_drop_timer_timeout():
|
func _on_drop_timer_timeout():
|
||||||
if game_over or not current_piece:
|
if not game_started or game_over or not current_piece:
|
||||||
return
|
return
|
||||||
|
|
||||||
if can_place_piece(current_piece_pos + Vector2(0, 1), current_piece_rotation):
|
if can_place_piece(current_piece_pos + Vector2(0, 1), current_piece_rotation):
|
||||||
@@ -267,12 +357,177 @@ func _on_drop_timer_timeout():
|
|||||||
else:
|
else:
|
||||||
place_piece()
|
place_piece()
|
||||||
|
|
||||||
|
# 虚拟控制设置
|
||||||
|
func setup_virtual_controls():
|
||||||
|
if virtual_controls:
|
||||||
|
virtual_controls.get_node("LeftButton").pressed.connect(_on_virtual_button_pressed.bind("left"))
|
||||||
|
virtual_controls.get_node("RightButton").pressed.connect(_on_virtual_button_pressed.bind("right"))
|
||||||
|
virtual_controls.get_node("DownButton").pressed.connect(_on_virtual_button_pressed.bind("down"))
|
||||||
|
virtual_controls.get_node("RotateButton").pressed.connect(_on_virtual_button_pressed.bind("rotate"))
|
||||||
|
virtual_controls.get_node("DropButton").pressed.connect(_on_virtual_button_pressed.bind("drop"))
|
||||||
|
virtual_controls.get_node("RestartButton").pressed.connect(_on_virtual_button_pressed.bind("restart"))
|
||||||
|
|
||||||
|
func _on_virtual_button_pressed(action: String):
|
||||||
|
if not game_started:
|
||||||
|
if action == "restart":
|
||||||
|
init_game()
|
||||||
|
return
|
||||||
|
|
||||||
|
if game_over and action == "restart":
|
||||||
|
handle_restart()
|
||||||
|
return
|
||||||
|
|
||||||
|
if game_over or not current_piece:
|
||||||
|
return
|
||||||
|
|
||||||
|
match action:
|
||||||
|
"left":
|
||||||
|
handle_move_left()
|
||||||
|
"right":
|
||||||
|
handle_move_right()
|
||||||
|
"down":
|
||||||
|
handle_move_down()
|
||||||
|
"rotate":
|
||||||
|
handle_rotate()
|
||||||
|
"drop":
|
||||||
|
handle_drop()
|
||||||
|
|
||||||
|
# 特殊方块处理
|
||||||
|
func process_special_pieces(lines_to_clear: Array):
|
||||||
|
# 处理特殊方块效果
|
||||||
|
for special in special_pieces:
|
||||||
|
var pos = special.position
|
||||||
|
var type = special.type
|
||||||
|
|
||||||
|
match type:
|
||||||
|
SpecialType.BOMB:
|
||||||
|
# 炸弹方块:清除周围3x3区域
|
||||||
|
for dy in range(-1, 2):
|
||||||
|
for dx in range(-1, 2):
|
||||||
|
var x = pos.x + dx
|
||||||
|
var y = pos.y + dy
|
||||||
|
if x >= 0 and x < BOARD_WIDTH and y >= 0 and y < BOARD_HEIGHT:
|
||||||
|
board[y][x] = 0
|
||||||
|
SpecialType.LINE:
|
||||||
|
# 直线方块:清除整行
|
||||||
|
for x in range(BOARD_WIDTH):
|
||||||
|
board[pos.y][x] = 0
|
||||||
|
SpecialType.RAINBOW:
|
||||||
|
# 彩虹方块:清除同色方块
|
||||||
|
var target_color = board[pos.y][pos.x]
|
||||||
|
if target_color > 0:
|
||||||
|
for y in range(BOARD_HEIGHT):
|
||||||
|
for x in range(BOARD_WIDTH):
|
||||||
|
if board[y][x] == target_color:
|
||||||
|
board[y][x] = 0
|
||||||
|
|
||||||
|
# 清空特殊方块列表
|
||||||
|
special_pieces.clear()
|
||||||
|
|
||||||
|
func generate_special_piece():
|
||||||
|
# 在随机位置生成特殊方块
|
||||||
|
var x = randi() % BOARD_WIDTH
|
||||||
|
var y = randi() % (BOARD_HEIGHT - 5) + 5 # 在下半部分生成
|
||||||
|
|
||||||
|
if board[y][x] != 0: # 只在有方块的位置生成特殊效果
|
||||||
|
var special_type = randi() % 3 + 1 # 随机选择特殊类型
|
||||||
|
special_pieces.append({
|
||||||
|
"position": Vector2(x, y),
|
||||||
|
"type": special_type
|
||||||
|
})
|
||||||
|
|
||||||
|
# 数据存储功能
|
||||||
|
func load_player_data():
|
||||||
|
if FileAccess.file_exists(DATA_FILE_PATH):
|
||||||
|
var file = FileAccess.open(DATA_FILE_PATH, FileAccess.READ)
|
||||||
|
if file:
|
||||||
|
var json_string = file.get_as_text()
|
||||||
|
file.close()
|
||||||
|
|
||||||
|
var json = JSON.new()
|
||||||
|
var parse_result = json.parse(json_string)
|
||||||
|
if parse_result == OK:
|
||||||
|
player_data = json.data
|
||||||
|
if player_data.has("tetris"):
|
||||||
|
var tetris_data = player_data["tetris"]
|
||||||
|
best_score = tetris_data.get("best_score", 0)
|
||||||
|
games_played = tetris_data.get("games_played", 0)
|
||||||
|
total_lines_cleared = tetris_data.get("total_lines_cleared", 0)
|
||||||
|
|
||||||
|
func show_start_screen():
|
||||||
|
# 重置状态
|
||||||
|
game_started = false
|
||||||
|
game_over = false
|
||||||
|
current_piece = null
|
||||||
|
|
||||||
|
# 显示开始提示
|
||||||
|
game_over_label.text = "🎮 俄罗斯方块 🎮\n\n🏆 最高分数: " + str(best_score) + "\n🎯 游戏次数: " + str(games_played) + "\n\n🎮 按Q键开始游戏\n\n🎯 操作说明:\nA/D - 左右移动\nW - 旋转\nS - 快速下落\n空格 - 直接落下"
|
||||||
|
game_over_label.visible = true
|
||||||
|
|
||||||
|
# 停止定时器
|
||||||
|
drop_timer.stop()
|
||||||
|
|
||||||
|
# 清空游戏板
|
||||||
|
board.clear()
|
||||||
|
special_pieces.clear()
|
||||||
|
for y in range(BOARD_HEIGHT):
|
||||||
|
var row = []
|
||||||
|
for x in range(BOARD_WIDTH):
|
||||||
|
row.append(0)
|
||||||
|
board.append(row)
|
||||||
|
|
||||||
|
update_ui()
|
||||||
|
queue_redraw()
|
||||||
|
|
||||||
|
func save_player_data():
|
||||||
|
if not player_data.has("tetris"):
|
||||||
|
player_data["tetris"] = {}
|
||||||
|
|
||||||
|
player_data["tetris"]["best_score"] = best_score
|
||||||
|
player_data["tetris"]["games_played"] = games_played
|
||||||
|
player_data["tetris"]["total_lines_cleared"] = total_lines_cleared
|
||||||
|
player_data["tetris"]["last_played"] = Time.get_datetime_string_from_system()
|
||||||
|
|
||||||
|
var file = FileAccess.open(DATA_FILE_PATH, FileAccess.WRITE)
|
||||||
|
if file:
|
||||||
|
var json_string = JSON.stringify(player_data)
|
||||||
|
file.store_string(json_string)
|
||||||
|
file.close()
|
||||||
|
|
||||||
func _draw():
|
func _draw():
|
||||||
if not game_area:
|
if not game_area:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# 绘制背景渐变
|
||||||
|
var gradient = Gradient.new()
|
||||||
|
gradient.add_point(0.0, Color(0.05, 0.1, 0.2, 0.95))
|
||||||
|
gradient.add_point(0.5, Color(0.1, 0.15, 0.25, 0.9))
|
||||||
|
gradient.add_point(1.0, Color(0.15, 0.2, 0.3, 0.95))
|
||||||
|
draw_rect(Rect2(Vector2.ZERO, size), gradient.sample(0.5), true)
|
||||||
|
|
||||||
# 获取游戏区域的位置
|
# 获取游戏区域的位置
|
||||||
var area_pos = game_area.position
|
var area_pos = game_area.position
|
||||||
|
var area_size = Vector2(BOARD_WIDTH * CELL_SIZE, BOARD_HEIGHT * CELL_SIZE)
|
||||||
|
|
||||||
|
# 绘制游戏区域阴影
|
||||||
|
var shadow_offset = Vector2(4, 4)
|
||||||
|
var area_rect = Rect2(area_pos + shadow_offset, area_size)
|
||||||
|
draw_rect(area_rect, Color(0, 0, 0, 0.4), true)
|
||||||
|
|
||||||
|
# 绘制游戏区域背景
|
||||||
|
area_rect = Rect2(area_pos, area_size)
|
||||||
|
draw_rect(area_rect, Color(0.08, 0.12, 0.18, 0.9), true)
|
||||||
|
|
||||||
|
# 绘制网格线(淡色)
|
||||||
|
for x in range(BOARD_WIDTH + 1):
|
||||||
|
var start_pos = Vector2(area_pos.x + x * CELL_SIZE, area_pos.y)
|
||||||
|
var end_pos = Vector2(area_pos.x + x * CELL_SIZE, area_pos.y + BOARD_HEIGHT * CELL_SIZE)
|
||||||
|
draw_line(start_pos, end_pos, Color(0.3, 0.3, 0.4, 0.3), 1)
|
||||||
|
|
||||||
|
for y in range(BOARD_HEIGHT + 1):
|
||||||
|
var start_pos = Vector2(area_pos.x, area_pos.y + y * CELL_SIZE)
|
||||||
|
var end_pos = Vector2(area_pos.x + BOARD_WIDTH * CELL_SIZE, area_pos.y + y * CELL_SIZE)
|
||||||
|
draw_line(start_pos, end_pos, Color(0.3, 0.3, 0.4, 0.3), 1)
|
||||||
|
|
||||||
# 绘制游戏板
|
# 绘制游戏板
|
||||||
for y in range(BOARD_HEIGHT):
|
for y in range(BOARD_HEIGHT):
|
||||||
@@ -282,15 +537,75 @@ func _draw():
|
|||||||
var rect = Rect2(
|
var rect = Rect2(
|
||||||
area_pos.x + x * CELL_SIZE,
|
area_pos.x + x * CELL_SIZE,
|
||||||
area_pos.y + y * CELL_SIZE,
|
area_pos.y + y * CELL_SIZE,
|
||||||
CELL_SIZE - 1,
|
CELL_SIZE - 2,
|
||||||
CELL_SIZE - 1
|
CELL_SIZE - 2
|
||||||
)
|
)
|
||||||
var color = PIECE_COLORS[cell_value - 1]
|
var color = PIECE_COLORS[cell_value - 1]
|
||||||
draw_rect(rect, color)
|
|
||||||
|
# 检查是否为特殊方块
|
||||||
|
var is_special = false
|
||||||
|
var special_type = SpecialType.NORMAL
|
||||||
|
for special in special_pieces:
|
||||||
|
if special.position == Vector2(x, y):
|
||||||
|
is_special = true
|
||||||
|
special_type = special.type
|
||||||
|
break
|
||||||
|
|
||||||
|
if is_special:
|
||||||
|
# 绘制特殊方块效果
|
||||||
|
match special_type:
|
||||||
|
SpecialType.BOMB:
|
||||||
|
# 炸弹方块(红色闪烁)
|
||||||
|
var pulse = sin(Time.get_ticks_msec() * 0.01) * 0.3 + 0.7
|
||||||
|
draw_rect(rect, Color.RED * pulse, true)
|
||||||
|
draw_rect(Rect2(rect.position + Vector2(2, 2), rect.size - Vector2(4, 4)), Color.ORANGE, true)
|
||||||
|
SpecialType.LINE:
|
||||||
|
# 直线方块(蓝色闪烁)
|
||||||
|
var pulse = sin(Time.get_ticks_msec() * 0.008) * 0.3 + 0.7
|
||||||
|
draw_rect(rect, Color.CYAN * pulse, true)
|
||||||
|
draw_rect(Rect2(rect.position + Vector2(2, 2), rect.size - Vector2(4, 4)), Color.LIGHT_BLUE, true)
|
||||||
|
SpecialType.RAINBOW:
|
||||||
|
# 彩虹方块(彩虹色)
|
||||||
|
var rainbow_time = Time.get_ticks_msec() * 0.003
|
||||||
|
var rainbow_color = Color.from_hsv(fmod(rainbow_time, 1.0), 1.0, 1.0)
|
||||||
|
draw_rect(rect, rainbow_color, true)
|
||||||
|
draw_rect(Rect2(rect.position + Vector2(2, 2), rect.size - Vector2(4, 4)), Color.WHITE, true)
|
||||||
|
else:
|
||||||
|
# 普通方块(立体效果)
|
||||||
|
draw_rect(rect, color, true)
|
||||||
|
# 高光
|
||||||
|
var highlight_rect = Rect2(rect.position + Vector2(1, 1), Vector2(rect.size.x - 2, rect.size.y * 0.3))
|
||||||
|
draw_rect(highlight_rect, Color(1, 1, 1, 0.3), true)
|
||||||
|
# 阴影
|
||||||
|
var shadow_rect = Rect2(rect.position + Vector2(1, rect.size.y * 0.7), Vector2(rect.size.x - 2, rect.size.y * 0.3))
|
||||||
|
draw_rect(shadow_rect, Color(0, 0, 0, 0.2), true)
|
||||||
|
|
||||||
# 绘制当前方块
|
# 绘制当前方块(带透明度预览)
|
||||||
if current_piece:
|
if current_piece:
|
||||||
var shape = get_piece_shape(current_piece.type, current_piece_rotation)
|
var shape = get_piece_shape(current_piece.type, current_piece_rotation)
|
||||||
|
|
||||||
|
# 绘制投影(显示方块会落在哪里)
|
||||||
|
var shadow_pos = current_piece_pos
|
||||||
|
while can_place_piece(shadow_pos + Vector2(0, 1), current_piece_rotation):
|
||||||
|
shadow_pos.y += 1
|
||||||
|
|
||||||
|
for y in range(shape.size()):
|
||||||
|
for x in range(shape[y].size()):
|
||||||
|
if shape[y][x] == 1:
|
||||||
|
var board_x = shadow_pos.x + x
|
||||||
|
var board_y = shadow_pos.y + y
|
||||||
|
if board_y >= 0 and board_x >= 0 and board_x < BOARD_WIDTH and board_y < BOARD_HEIGHT:
|
||||||
|
var rect = Rect2(
|
||||||
|
area_pos.x + board_x * CELL_SIZE,
|
||||||
|
area_pos.y + board_y * CELL_SIZE,
|
||||||
|
CELL_SIZE - 2,
|
||||||
|
CELL_SIZE - 2
|
||||||
|
)
|
||||||
|
var shadow_color = PIECE_COLORS[current_piece.type]
|
||||||
|
shadow_color.a = 0.3
|
||||||
|
draw_rect(rect, shadow_color, false, 2)
|
||||||
|
|
||||||
|
# 绘制当前方块
|
||||||
for y in range(shape.size()):
|
for y in range(shape.size()):
|
||||||
for x in range(shape[y].size()):
|
for x in range(shape[y].size()):
|
||||||
if shape[y][x] == 1:
|
if shape[y][x] == 1:
|
||||||
@@ -300,25 +615,23 @@ func _draw():
|
|||||||
var rect = Rect2(
|
var rect = Rect2(
|
||||||
area_pos.x + board_x * CELL_SIZE,
|
area_pos.x + board_x * CELL_SIZE,
|
||||||
area_pos.y + board_y * CELL_SIZE,
|
area_pos.y + board_y * CELL_SIZE,
|
||||||
CELL_SIZE - 1,
|
CELL_SIZE - 2,
|
||||||
CELL_SIZE - 1
|
CELL_SIZE - 2
|
||||||
)
|
)
|
||||||
var color = PIECE_COLORS[current_piece.type]
|
var color = PIECE_COLORS[current_piece.type]
|
||||||
draw_rect(rect, color)
|
# 立体效果
|
||||||
|
draw_rect(rect, color, true)
|
||||||
|
# 高光
|
||||||
|
var highlight_rect = Rect2(rect.position + Vector2(1, 1), Vector2(rect.size.x - 2, rect.size.y * 0.3))
|
||||||
|
draw_rect(highlight_rect, Color(1, 1, 1, 0.4), true)
|
||||||
|
|
||||||
# 绘制网格线
|
# 绘制下一个方块预览区域背景
|
||||||
for x in range(BOARD_WIDTH + 1):
|
var next_area_pos = next_piece_area.position
|
||||||
var start_pos = Vector2(area_pos.x + x * CELL_SIZE, area_pos.y)
|
var next_area_size = next_piece_area.size
|
||||||
var end_pos = Vector2(area_pos.x + x * CELL_SIZE, area_pos.y + BOARD_HEIGHT * CELL_SIZE)
|
draw_rect(Rect2(next_area_pos + Vector2(2, 2), next_area_size), Color(0, 0, 0, 0.3), true)
|
||||||
draw_line(start_pos, end_pos, Color.GRAY, 1)
|
draw_rect(Rect2(next_area_pos, next_area_size), Color(0.1, 0.15, 0.25, 0.9), true)
|
||||||
|
|
||||||
for y in range(BOARD_HEIGHT + 1):
|
|
||||||
var start_pos = Vector2(area_pos.x, area_pos.y + y * CELL_SIZE)
|
|
||||||
var end_pos = Vector2(area_pos.x + BOARD_WIDTH * CELL_SIZE, area_pos.y + y * CELL_SIZE)
|
|
||||||
draw_line(start_pos, end_pos, Color.GRAY, 1)
|
|
||||||
|
|
||||||
# 绘制下一个方块预览
|
# 绘制下一个方块预览
|
||||||
var next_area_pos = next_piece_area.position
|
|
||||||
var next_shape = get_piece_shape(next_piece_type, 0)
|
var next_shape = get_piece_shape(next_piece_type, 0)
|
||||||
for y in range(next_shape.size()):
|
for y in range(next_shape.size()):
|
||||||
for x in range(next_shape[y].size()):
|
for x in range(next_shape[y].size()):
|
||||||
@@ -326,8 +639,19 @@ func _draw():
|
|||||||
var rect = Rect2(
|
var rect = Rect2(
|
||||||
next_area_pos.x + 20 + x * 20,
|
next_area_pos.x + 20 + x * 20,
|
||||||
next_area_pos.y + 50 + y * 20,
|
next_area_pos.y + 50 + y * 20,
|
||||||
19,
|
18,
|
||||||
19
|
18
|
||||||
)
|
)
|
||||||
var color = PIECE_COLORS[next_piece_type]
|
var color = PIECE_COLORS[next_piece_type]
|
||||||
draw_rect(rect, color)
|
# 立体效果
|
||||||
|
draw_rect(rect, color, true)
|
||||||
|
# 高光
|
||||||
|
var highlight_rect = Rect2(rect.position + Vector2(1, 1), Vector2(rect.size.x - 2, rect.size.y * 0.3))
|
||||||
|
draw_rect(highlight_rect, Color(1, 1, 1, 0.3), true)
|
||||||
|
|
||||||
|
|
||||||
|
func _on_quit_button_pressed() -> void:
|
||||||
|
self.hide()
|
||||||
|
get_parent().remove_child(self)
|
||||||
|
queue_free()
|
||||||
|
pass
|
||||||
|
|||||||
@@ -12,10 +12,10 @@ layout_mode = 1
|
|||||||
anchors_preset = 4
|
anchors_preset = 4
|
||||||
anchor_top = 0.5
|
anchor_top = 0.5
|
||||||
anchor_bottom = 0.5
|
anchor_bottom = 0.5
|
||||||
offset_left = 50.0
|
offset_left = 542.0
|
||||||
offset_top = -300.0
|
offset_top = -300.5
|
||||||
offset_right = 350.0
|
offset_right = 842.0
|
||||||
offset_bottom = 300.0
|
offset_bottom = 299.5
|
||||||
grow_vertical = 2
|
grow_vertical = 2
|
||||||
|
|
||||||
[node name="NextPieceArea" type="Panel" parent="."]
|
[node name="NextPieceArea" type="Panel" parent="."]
|
||||||
@@ -23,9 +23,9 @@ layout_mode = 1
|
|||||||
anchors_preset = 1
|
anchors_preset = 1
|
||||||
anchor_left = 1.0
|
anchor_left = 1.0
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
offset_left = -200.0
|
offset_left = -469.0
|
||||||
offset_top = 50.0
|
offset_top = 50.0
|
||||||
offset_right = -50.0
|
offset_right = -319.0
|
||||||
offset_bottom = 200.0
|
offset_bottom = 200.0
|
||||||
grow_horizontal = 0
|
grow_horizontal = 0
|
||||||
|
|
||||||
@@ -35,10 +35,10 @@ anchors_preset = 5
|
|||||||
anchor_left = 0.5
|
anchor_left = 0.5
|
||||||
anchor_right = 0.5
|
anchor_right = 0.5
|
||||||
offset_left = -50.0
|
offset_left = -50.0
|
||||||
offset_top = 10.0
|
|
||||||
offset_right = 50.0
|
offset_right = 50.0
|
||||||
offset_bottom = 40.0
|
offset_bottom = 42.0
|
||||||
grow_horizontal = 2
|
grow_horizontal = 2
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
text = "下一个"
|
text = "下一个"
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
|
|
||||||
@@ -47,11 +47,12 @@ layout_mode = 1
|
|||||||
anchors_preset = 1
|
anchors_preset = 1
|
||||||
anchor_left = 1.0
|
anchor_left = 1.0
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
offset_left = -200.0
|
offset_left = -255.0
|
||||||
offset_top = 220.0
|
offset_top = 92.0
|
||||||
offset_right = -50.0
|
offset_right = -105.0
|
||||||
offset_bottom = 250.0
|
offset_bottom = 134.0
|
||||||
grow_horizontal = 0
|
grow_horizontal = 0
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
text = "分数: 0"
|
text = "分数: 0"
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
|
|
||||||
@@ -60,11 +61,12 @@ layout_mode = 1
|
|||||||
anchors_preset = 1
|
anchors_preset = 1
|
||||||
anchor_left = 1.0
|
anchor_left = 1.0
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
offset_left = -200.0
|
offset_left = -252.0
|
||||||
offset_top = 260.0
|
offset_top = 201.0
|
||||||
offset_right = -50.0
|
offset_right = -102.0
|
||||||
offset_bottom = 290.0
|
offset_bottom = 243.0
|
||||||
grow_horizontal = 0
|
grow_horizontal = 0
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
text = "等级: 1"
|
text = "等级: 1"
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
|
|
||||||
@@ -73,28 +75,29 @@ layout_mode = 1
|
|||||||
anchors_preset = 1
|
anchors_preset = 1
|
||||||
anchor_left = 1.0
|
anchor_left = 1.0
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
offset_left = -200.0
|
offset_left = -255.0
|
||||||
offset_top = 300.0
|
offset_top = 312.0
|
||||||
offset_right = -50.0
|
offset_right = -102.0
|
||||||
offset_bottom = 330.0
|
offset_bottom = 354.0
|
||||||
grow_horizontal = 0
|
grow_horizontal = 0
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
text = "消除行数: 0"
|
text = "消除行数: 0"
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
|
|
||||||
[node name="GameOverLabel" type="Label" parent="."]
|
[node name="GameOverLabel" type="Label" parent="."]
|
||||||
visible = false
|
|
||||||
layout_mode = 1
|
layout_mode = 1
|
||||||
anchors_preset = 8
|
anchors_preset = 8
|
||||||
anchor_left = 0.5
|
anchor_left = 0.5
|
||||||
anchor_top = 0.5
|
anchor_top = 0.5
|
||||||
anchor_right = 0.5
|
anchor_right = 0.5
|
||||||
anchor_bottom = 0.5
|
anchor_bottom = 0.5
|
||||||
offset_left = -100.0
|
offset_left = -116.5
|
||||||
offset_top = -50.0
|
offset_top = -48.5
|
||||||
offset_right = 100.0
|
offset_right = 93.5
|
||||||
offset_bottom = 50.0
|
offset_bottom = 51.5
|
||||||
grow_horizontal = 2
|
grow_horizontal = 2
|
||||||
grow_vertical = 2
|
grow_vertical = 2
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
text = "游戏结束
|
text = "游戏结束
|
||||||
按空格重新开始"
|
按空格重新开始"
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
@@ -108,13 +111,144 @@ layout_mode = 1
|
|||||||
anchors_preset = 1
|
anchors_preset = 1
|
||||||
anchor_left = 1.0
|
anchor_left = 1.0
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
offset_left = -200.0
|
offset_left = -214.0
|
||||||
offset_top = 400.0
|
offset_top = 494.0
|
||||||
offset_right = -50.0
|
offset_right = -9.0
|
||||||
offset_bottom = 500.0
|
offset_bottom = 716.0
|
||||||
grow_horizontal = 0
|
grow_horizontal = 0
|
||||||
|
theme_override_font_sizes/font_size = 30
|
||||||
text = "操作说明:
|
text = "操作说明:
|
||||||
A/D - 左右移动
|
A/D - 左右移动
|
||||||
S - 快速下降
|
S - 快速下降
|
||||||
W - 旋转
|
W - 旋转
|
||||||
空格 - 瞬间下降"
|
空格 - 瞬间下降"
|
||||||
|
|
||||||
|
[node name="VirtualControls" type="Control" parent="."]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
|
||||||
|
[node name="LeftButton" type="Button" parent="VirtualControls"]
|
||||||
|
custom_minimum_size = Vector2(80, 80)
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 2
|
||||||
|
anchor_top = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_left = 96.0
|
||||||
|
offset_top = -351.0
|
||||||
|
offset_right = 176.0
|
||||||
|
offset_bottom = -271.0
|
||||||
|
grow_vertical = 0
|
||||||
|
theme_override_font_sizes/font_size = 40
|
||||||
|
text = "A"
|
||||||
|
|
||||||
|
[node name="RightButton" type="Button" parent="VirtualControls"]
|
||||||
|
custom_minimum_size = Vector2(80, 80)
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 2
|
||||||
|
anchor_top = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_left = 260.0
|
||||||
|
offset_top = -350.0
|
||||||
|
offset_right = 340.0
|
||||||
|
offset_bottom = -270.0
|
||||||
|
grow_vertical = 0
|
||||||
|
theme_override_font_sizes/font_size = 40
|
||||||
|
text = "D"
|
||||||
|
|
||||||
|
[node name="DownButton" type="Button" parent="VirtualControls"]
|
||||||
|
custom_minimum_size = Vector2(80, 80)
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 2
|
||||||
|
anchor_top = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_left = 180.0
|
||||||
|
offset_top = -270.0
|
||||||
|
offset_right = 260.0
|
||||||
|
offset_bottom = -190.0
|
||||||
|
grow_vertical = 0
|
||||||
|
theme_override_font_sizes/font_size = 40
|
||||||
|
text = "S"
|
||||||
|
|
||||||
|
[node name="TestButton" type="Button" parent="VirtualControls"]
|
||||||
|
custom_minimum_size = Vector2(80, 80)
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 2
|
||||||
|
anchor_top = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_left = 178.0
|
||||||
|
offset_top = -334.0
|
||||||
|
offset_right = 238.0
|
||||||
|
offset_bottom = -271.0
|
||||||
|
grow_vertical = 0
|
||||||
|
theme_override_font_sizes/font_size = 40
|
||||||
|
disabled = true
|
||||||
|
|
||||||
|
[node name="RotateButton" type="Button" parent="VirtualControls"]
|
||||||
|
custom_minimum_size = Vector2(80, 80)
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 3
|
||||||
|
anchor_left = 1.0
|
||||||
|
anchor_top = 1.0
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_left = -1225.0
|
||||||
|
offset_top = -431.0
|
||||||
|
offset_right = -1145.0
|
||||||
|
offset_bottom = -351.0
|
||||||
|
grow_horizontal = 0
|
||||||
|
grow_vertical = 0
|
||||||
|
theme_override_font_sizes/font_size = 40
|
||||||
|
text = "W"
|
||||||
|
|
||||||
|
[node name="DropButton" type="Button" parent="VirtualControls"]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 3
|
||||||
|
anchor_left = 1.0
|
||||||
|
anchor_top = 1.0
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_left = -1284.0
|
||||||
|
offset_top = -583.0
|
||||||
|
offset_right = -1061.0
|
||||||
|
offset_bottom = -520.0
|
||||||
|
grow_horizontal = 0
|
||||||
|
grow_vertical = 0
|
||||||
|
theme_override_font_sizes/font_size = 40
|
||||||
|
text = "🚀瞬间下降"
|
||||||
|
|
||||||
|
[node name="RestartButton" type="Button" parent="VirtualControls"]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 8
|
||||||
|
anchor_left = 0.5
|
||||||
|
anchor_top = 0.5
|
||||||
|
anchor_right = 0.5
|
||||||
|
anchor_bottom = 0.5
|
||||||
|
offset_left = -582.5
|
||||||
|
offset_top = 248.5
|
||||||
|
offset_right = -377.5
|
||||||
|
offset_bottom = 305.5
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
theme_override_font_sizes/font_size = 35
|
||||||
|
text = "🔄 重新开始"
|
||||||
|
|
||||||
|
[node name="QuitButton" type="Button" parent="."]
|
||||||
|
self_modulate = Color(1, 0.247059, 0, 1)
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 1
|
||||||
|
anchor_left = 1.0
|
||||||
|
anchor_right = 1.0
|
||||||
|
offset_left = -137.0
|
||||||
|
offset_top = -1.52588e-05
|
||||||
|
offset_right = -54.0
|
||||||
|
offset_bottom = 57.0
|
||||||
|
grow_horizontal = 0
|
||||||
|
scale = Vector2(1.54345, 1.50915)
|
||||||
|
theme_override_font_sizes/font_size = 25
|
||||||
|
text = "关闭"
|
||||||
|
|
||||||
|
[connection signal="pressed" from="QuitButton" to="." method="_on_quit_button_pressed"]
|
||||||
|
|||||||
@@ -389,10 +389,23 @@ func _apply_filter_and_sort():
|
|||||||
|
|
||||||
# 自定义排序函数
|
# 自定义排序函数
|
||||||
func _sort_crop_items(a, b):
|
func _sort_crop_items(a, b):
|
||||||
|
# 安全地获取排序值,并进行类型转换
|
||||||
|
var value_a = a["data"].get(current_sort_key, 0)
|
||||||
|
var value_b = b["data"].get(current_sort_key, 0)
|
||||||
|
|
||||||
|
# 如果是数值类型的字段,确保转换为数值进行比较
|
||||||
|
if current_sort_key in ["花费", "生长时间", "收益", "等级", "经验", "耐候性"]:
|
||||||
|
# 转换为数值,如果转换失败则使用0
|
||||||
|
if typeof(value_a) == TYPE_STRING:
|
||||||
|
value_a = int(value_a) if value_a.is_valid_int() else 0
|
||||||
|
if typeof(value_b) == TYPE_STRING:
|
||||||
|
value_b = int(value_b) if value_b.is_valid_int() else 0
|
||||||
|
|
||||||
|
# 执行排序比较
|
||||||
if current_sort_ascending:
|
if current_sort_ascending:
|
||||||
return a["data"][current_sort_key] < b["data"][current_sort_key]
|
return value_a < value_b
|
||||||
else:
|
else:
|
||||||
return a["data"][current_sort_key] > b["data"][current_sort_key]
|
return value_a > value_b
|
||||||
|
|
||||||
# 更新金钱显示
|
# 更新金钱显示
|
||||||
func _update_money_display():
|
func _update_money_display():
|
||||||
|
|||||||
@@ -210,11 +210,23 @@ func _sort_crop_items(a, b):
|
|||||||
print("警告: 排序键 ", current_sort_key, " 在某些成熟物数据中不存在")
|
print("警告: 排序键 ", current_sort_key, " 在某些成熟物数据中不存在")
|
||||||
return false
|
return false
|
||||||
|
|
||||||
# 执行排序
|
# 安全地获取排序值,并进行类型转换
|
||||||
|
var value_a = a["data"].get(current_sort_key, 0)
|
||||||
|
var value_b = b["data"].get(current_sort_key, 0)
|
||||||
|
|
||||||
|
# 如果是数值类型的字段,确保转换为数值进行比较
|
||||||
|
if current_sort_key in ["花费", "生长时间", "收益", "等级", "经验", "耐候性"]:
|
||||||
|
# 转换为数值,如果转换失败则使用0
|
||||||
|
if typeof(value_a) == TYPE_STRING:
|
||||||
|
value_a = int(value_a) if value_a.is_valid_int() else 0
|
||||||
|
if typeof(value_b) == TYPE_STRING:
|
||||||
|
value_b = int(value_b) if value_b.is_valid_int() else 0
|
||||||
|
|
||||||
|
# 执行排序比较
|
||||||
if current_sort_ascending:
|
if current_sort_ascending:
|
||||||
return a["data"][current_sort_key] < b["data"][current_sort_key]
|
return value_a < value_b
|
||||||
else:
|
else:
|
||||||
return a["data"][current_sort_key] > b["data"][current_sort_key]
|
return value_a > value_b
|
||||||
|
|
||||||
# 按品质过滤成熟物
|
# 按品质过滤成熟物
|
||||||
func _filter_by_quality(quality: String):
|
func _filter_by_quality(quality: String):
|
||||||
|
|||||||
@@ -1 +1,27 @@
|
|||||||
extends Panel
|
extends Panel
|
||||||
|
|
||||||
|
|
||||||
|
var _2048_GAME = preload('res://Scene/SmallGame/2048Game.tscn').instantiate()
|
||||||
|
var PUSH_BOX = preload('res://Scene/SmallGame/PushBox.tscn').instantiate()
|
||||||
|
var SNAKE_GAME = preload('res://Scene/SmallGame/SnakeGame.tscn').instantiate()
|
||||||
|
var TETRIS = preload('res://Scene/SmallGame/Tetris.tscn').instantiate()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_game_button_pressed() -> void:
|
||||||
|
self.add_child(_2048_GAME)
|
||||||
|
pass # Replace with function body.
|
||||||
|
|
||||||
|
|
||||||
|
func _on_push_box_button_pressed() -> void:
|
||||||
|
self.add_child(PUSH_BOX)
|
||||||
|
pass # Replace with function body.
|
||||||
|
|
||||||
|
|
||||||
|
func _on_snake_game_button_pressed() -> void:
|
||||||
|
self.add_child(SNAKE_GAME)
|
||||||
|
pass # Replace with function body.
|
||||||
|
|
||||||
|
|
||||||
|
func _on_tetris_button_pressed() -> void:
|
||||||
|
self.add_child(TETRIS)
|
||||||
|
pass # Replace with function body.
|
||||||
|
|||||||
@@ -165,11 +165,23 @@ func _sort_seed_items(a, b):
|
|||||||
print("警告: 排序键 ", current_sort_key, " 在某些种子数据中不存在")
|
print("警告: 排序键 ", current_sort_key, " 在某些种子数据中不存在")
|
||||||
return false
|
return false
|
||||||
|
|
||||||
# 执行排序
|
# 安全地获取排序值,并进行类型转换
|
||||||
|
var value_a = a["data"].get(current_sort_key, 0)
|
||||||
|
var value_b = b["data"].get(current_sort_key, 0)
|
||||||
|
|
||||||
|
# 如果是数值类型的字段,确保转换为数值进行比较
|
||||||
|
if current_sort_key in ["花费", "生长时间", "收益", "等级", "经验", "耐候性"]:
|
||||||
|
# 转换为数值,如果转换失败则使用0
|
||||||
|
if typeof(value_a) == TYPE_STRING:
|
||||||
|
value_a = int(value_a) if value_a.is_valid_int() else 0
|
||||||
|
if typeof(value_b) == TYPE_STRING:
|
||||||
|
value_b = int(value_b) if value_b.is_valid_int() else 0
|
||||||
|
|
||||||
|
# 执行排序比较
|
||||||
if current_sort_ascending:
|
if current_sort_ascending:
|
||||||
return a["data"][current_sort_key] < b["data"][current_sort_key]
|
return value_a < value_b
|
||||||
else:
|
else:
|
||||||
return a["data"][current_sort_key] > b["data"][current_sort_key]
|
return value_a > value_b
|
||||||
|
|
||||||
# 按品质过滤种子
|
# 按品质过滤种子
|
||||||
func _filter_by_quality(quality: String):
|
func _filter_by_quality(quality: String):
|
||||||
|
|||||||
@@ -2425,11 +2425,7 @@ class TCPGameServer(TCPServer):
|
|||||||
return self.send_data(client_id, response)
|
return self.send_data(client_id, response)
|
||||||
|
|
||||||
crop_name = message.get("crop_name", "")
|
crop_name = message.get("crop_name", "")
|
||||||
quantity = message.get("quantity", 1) # 获取购买数量,默认为1
|
quantity = max(1, int(message.get("quantity", 1))) # 确保购买数量为正整数
|
||||||
|
|
||||||
# 确保购买数量为正整数
|
|
||||||
if not isinstance(quantity, int) or quantity <= 0:
|
|
||||||
quantity = 1
|
|
||||||
|
|
||||||
# 加载作物配置
|
# 加载作物配置
|
||||||
crop_data = self._load_crop_data()
|
crop_data = self._load_crop_data()
|
||||||
@@ -2447,7 +2443,9 @@ class TCPGameServer(TCPServer):
|
|||||||
def _process_seed_purchase(self, client_id, player_data, username, crop_name, crop, quantity=1):
|
def _process_seed_purchase(self, client_id, player_data, username, crop_name, crop, quantity=1):
|
||||||
"""处理种子购买逻辑"""
|
"""处理种子购买逻辑"""
|
||||||
# 检查玩家等级
|
# 检查玩家等级
|
||||||
if player_data["等级"] < crop.get("等级", 1):
|
player_level = int(player_data.get("等级", 1))
|
||||||
|
required_level = int(crop.get("等级", 1))
|
||||||
|
if player_level < required_level:
|
||||||
return self._send_action_error(client_id, "buy_seed", "等级不足,无法购买此种子")
|
return self._send_action_error(client_id, "buy_seed", "等级不足,无法购买此种子")
|
||||||
|
|
||||||
# 计算总花费
|
# 计算总花费
|
||||||
@@ -2456,7 +2454,7 @@ class TCPGameServer(TCPServer):
|
|||||||
|
|
||||||
# 检查玩家金钱
|
# 检查玩家金钱
|
||||||
if player_data["钱币"] < total_cost:
|
if player_data["钱币"] < total_cost:
|
||||||
return self._send_action_error(client_id, "buy_seed", f"金钱不足,无法购买此种子。需要{total_cost}元,当前只有{player_data['money']}元")
|
return self._send_action_error(client_id, "buy_seed", f"金钱不足,无法购买此种子。需要{total_cost}元,当前只有{player_data['钱币']}元")
|
||||||
|
|
||||||
# 扣除金钱
|
# 扣除金钱
|
||||||
player_data["钱币"] -= total_cost
|
player_data["钱币"] -= total_cost
|
||||||
|
|||||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
1071
Server/test/玩家农场/2143323382.json
Normal file
1071
Server/test/玩家农场/2143323382.json
Normal file
File diff suppressed because it is too large
Load Diff
1248
Server/test/玩家农场/3205788256.json
Normal file
1248
Server/test/玩家农场/3205788256.json
Normal file
File diff suppressed because it is too large
Load Diff
1
Test/playergamedata.json
Normal file
1
Test/playergamedata.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"2048":{"best_score":2056,"current_score":2056,"games_played":4.0,"highest_tile":128.0,"total_moves":515.0},"global":{"last_played":"2025-08-26T19:02:30"},"pushbox":{"best_moves_per_level":{"1":4.0,"2.0":13.0},"current_level":1.0,"levels_completed":3.0,"max_level_reached":1.0,"total_moves":56.0}}
|
||||||
34
assets/作物/咖啡豆/未成熟.webp.import
Normal file
34
assets/作物/咖啡豆/未成熟.webp.import
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://j8bi7kpe2qbo"
|
||||||
|
path="res://.godot/imported/未成熟.webp-82494694de3fd628511fc7826d35227c.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/作物/咖啡豆/未成熟.webp"
|
||||||
|
dest_files=["res://.godot/imported/未成熟.webp-82494694de3fd628511fc7826d35227c.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.01
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=true
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=0
|
||||||
32
playergamedata.json
Normal file
32
playergamedata.json
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"2048": {
|
||||||
|
"best_score": 0,
|
||||||
|
"current_score": 0,
|
||||||
|
"games_played": 0,
|
||||||
|
"highest_tile": 0,
|
||||||
|
"total_moves": 0,
|
||||||
|
"achievements": [],
|
||||||
|
"settings": {
|
||||||
|
"sound_enabled": true,
|
||||||
|
"animations_enabled": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pushbox": {
|
||||||
|
"current_level": 1,
|
||||||
|
"max_level_reached": 1,
|
||||||
|
"total_moves": 0,
|
||||||
|
"levels_completed": 0,
|
||||||
|
"best_moves_per_level": {},
|
||||||
|
"achievements": [],
|
||||||
|
"settings": {
|
||||||
|
"sound_enabled": true,
|
||||||
|
"show_hints": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"global": {
|
||||||
|
"total_play_time": 0,
|
||||||
|
"last_played": "",
|
||||||
|
"player_name": "Player",
|
||||||
|
"version": "1.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user