diff --git a/bg/dynamic_bg/dynamic_bg.gd b/bg/dynamic_bg/dynamic_bg.gd index 3764568..589e0ff 100644 --- a/bg/dynamic_bg/dynamic_bg.gd +++ b/bg/dynamic_bg/dynamic_bg.gd @@ -43,11 +43,6 @@ func _ready(): x_offset += $Timer.wait_time * VELOCITY.x x_offset -= int(x_offset / W_STEP) * W_STEP y_offset += $Timer.wait_time * VELOCITY.y - print(y_offset) - -# Called every frame. 'delta' is the elapsed time since the previous frame. -func _process(delta): - pass func _on_timer_timeout(): diff --git a/bg/dynamic_bg/dynamic_bg_pattern/dynamic_bg_pattern.gd b/bg/dynamic_bg/dynamic_bg_pattern/dynamic_bg_pattern.gd index 6e37fa4..41df790 100644 --- a/bg/dynamic_bg/dynamic_bg_pattern/dynamic_bg_pattern.gd +++ b/bg/dynamic_bg/dynamic_bg_pattern/dynamic_bg_pattern.gd @@ -18,5 +18,10 @@ func _process(delta): queue_free() func _on_victory_change(v: bool): - $Word1.set_victory(v) - $Word2.set_victory(v) + if v: + $Word1.set_color(ImageLib.PALETTE["golden"]) + $Word2.set_color(ImageLib.PALETTE["golden"]) + else: + $Word1.set_color(ImageLib.PALETTE["default"]) + $Word2.set_color(ImageLib.PALETTE["default"]) + diff --git a/levels/base_level/base_level.gd b/levels/base_level/base_level.gd index 65e9966..4efc086 100644 --- a/levels/base_level/base_level.gd +++ b/levels/base_level/base_level.gd @@ -1,11 +1,17 @@ extends Node -const Block := preload("res://objects/block/block.tscn") -const CardBase := preload("res://objects/card_base/card_base.tscn") -const LevelMenu := preload("res://levels/chapter_menu/level_menu/level_menu.tscn") -const TableCloth := preload("res://objects/table_cloth/table_cloth.tscn") -const BaseLevel := preload("res://levels/base_level/base_level.tscn") +class_name BaseLevel + +# FIXME +# 由于 Godot preload 容易出错,此处改为 load +var BlockScn = load("res://objects/block/block.tscn") +var CardBaseScn = load("res://objects/card_base/card_base.tscn") +var LevelMenuScn = load("res://levels/chapter_menu/level_menu/level_menu.tscn") +var TableClothScn = load("res://objects/table_cloth/table_cloth.tscn") +var BaseLevelScn = load("res://levels/base_level/base_level.tscn") + + const HEIGHT := 1080 / 4 const WIDTH := 1920 / 4 const SEP := 28 @@ -21,14 +27,15 @@ const I_NUMBER = ["I","II","III","VI","V"] const DATA := [ [ - ["=P", "P {} {}", "= P"], - ["P", "P [] {}", "= P P"], - ["=D", "D {} {}", "= D P"], - ["Reverse", "{} {} []", "d d = P P D D"] + ["=P", "P [] []", "= P"], + ["Make a Smile", "P {} {}", "= P P"], + ["=D", "[] {} {}", "P = D D"], + ["Reversed", "{} [] []", "D D = d d"], + ["Watch Out the Direction", "[] {} {}", "d d = R R b b"], ], [ - ["False", "[] [] 0 {} {}", "= + P P"], - ["True", "[] {} {} [] 1", "= + P 1"], + ["0+0=0, 1+0=1", "[] [] {} {} d", "= + 0 d"], + ["1+1=1", "[] [] {} {} 1", "= + d 1"], ["Swap", "Q + [] = {} + []", "P P P P Q Q Q Q"], ["Always True", "1 [] {} = {} [] []", "1++PPdd"], ["Make Me Laugh", "1 {} {} = []", "XDD"], @@ -83,7 +90,7 @@ func init(_chap_id: int, _lvl_id: int) -> void: question = question.replace("{}", "_") - var table_cloth = TableCloth.instantiate() + var table_cloth = TableClothScn.instantiate() var sep := SEP if len(question) >= 16: @@ -103,7 +110,7 @@ func init(_chap_id: int, _lvl_id: int) -> void: for i in range(len(question)): var ch = question[i] - var new_block := Block.instantiate() + var new_block = BlockScn.instantiate() new_block.quest_pos = i if ch != "." and ch != "_": @@ -123,12 +130,12 @@ func init(_chap_id: int, _lvl_id: int) -> void: $Blocks.add_child(new_block) expr = question - print(expr) + # print(expr) pos = WIDTH / 2 - CARDS_SEP * len(choices) / 2 + 16 for ch in choices: - var new_card_base := CardBase.instantiate() + var new_card_base = CardBaseScn.instantiate() new_card_base.set_word(ch) @@ -151,7 +158,7 @@ func _process(_delta): func stage_clear() -> void: $SFXs/LevelClear.play() for card_base: CardBase in $CardBases.get_children(): - card_base.call("set_victory") + card_base.set_victory() $HUDs/TableCloth/GoldenCloth.set_visible(true) $HUDs/NextLevelButton.start_fade() @@ -161,17 +168,17 @@ func _on_card_put() -> void: var block_array = [] for block : Block in $Blocks.get_children(): if not block.occupied: - print(block.quest_pos, " is not occupied") + # print(block.quest_pos, " is not occupied") return expr[block.quest_pos] = block.occupied_word block_array.append(block) - prints("# expr: ", expr) + # prints("# expr: ", expr) if expr.count("_") == 0: - var info = $Calculator.check(expr, req_pos) - prints("expr:", expr) - prints("info:", info) + var info = ExprValidator.check(expr, req_pos) + # prints("expr:", expr) + # prints("info:", info) if info[0] != "OK": $SFXs/WrongAnswer.play() @@ -179,25 +186,25 @@ func _on_card_put() -> void: if info[0] == "INVALID": for block: Block in $Blocks.get_children(): if block.quest_pos in info[1]: - block.call("shake") + block.shake(false) elif info[0] == "SMILE_UNSATISFIED": for block: Block in $Blocks.get_children(): - if block.quest_pos == info[1]: - block.call("shake") + if block.quest_pos in info[1]: + block.shake(true) elif info[0] == "NOT_ALWAYS_TRUE": for block: Block in $Blocks.get_children(): - block.call("shake") + block.shake(false) else: # victory get_tree().current_scene.set_victory(true) stage_clear() - print(block_array) + # print(block_array) for i in range(len(block_array)): if i != 0: - if $Calculator.is_smile(expr[i-1]+expr[i]): - print(expr[i-1]+expr[i]) - block_array[i-1].set_victory(true) - block_array[i].set_victory(true) + if ExprValidator.is_smile(expr[i-1]+expr[i]): + # print(expr[i-1]+expr[i]) + block_array[i-1].set_color(ImageLib.PALETTE["golden"]) + block_array[i].set_color(ImageLib.PALETTE["golden"]) func _input(event: InputEvent): if event is InputEventKey: @@ -212,24 +219,24 @@ func _exit_tree(): func _on_back_button_pressed(): - var level_menu = LevelMenu.instantiate() + var level_menu = LevelMenuScn.instantiate() level_menu.init(chap_id, -1) get_tree().root.add_child(level_menu) queue_free() func _on_next_level_button_pressed(): if lvl_id == len(DATA[chap_id]) - 1: - var level_menu := LevelMenu.instantiate() + var level_menu = LevelMenuScn.instantiate() level_menu.init(chap_id + 1, -1) get_tree().root.add_child(level_menu) else: - var base_level := BaseLevel.instantiate() + var base_level = BaseLevelScn.instantiate() base_level.init(chap_id, lvl_id + 1) get_tree().root.add_child(base_level) queue_free() func _on_replay_button_pressed(): - var new_level = BaseLevel.instantiate() + var new_level = BaseLevelScn.instantiate() new_level.init(chap_id, lvl_id) get_tree().root.add_child(new_level) queue_free() diff --git a/levels/base_level/base_level.tscn b/levels/base_level/base_level.tscn index 36848e6..7e8de92 100644 --- a/levels/base_level/base_level.tscn +++ b/levels/base_level/base_level.tscn @@ -1,7 +1,6 @@ -[gd_scene load_steps=16 format=3 uid="uid://cother2aiigge"] +[gd_scene load_steps=15 format=3 uid="uid://cother2aiigge"] [ext_resource type="Script" path="res://levels/base_level/base_level.gd" id="1_diojb"] -[ext_resource type="PackedScene" uid="uid://xom5kpqs1rue" path="res://objects/calculator/calculator.tscn" id="1_tpuy7"] [ext_resource type="PackedScene" uid="uid://bepyyenjtc0p3" path="res://levels/chapter_menu/level_menu/level_button/level_button.tscn" id="2_0egdl"] [ext_resource type="PackedScene" uid="uid://d1mp3ld6wsrwo" path="res://levels/base_level/next_level_button/next_level_button.tscn" id="2_0363i"] [ext_resource type="Texture2D" uid="uid://bxda1ilvqwc6f" path="res://objects/styled_button/return1.png" id="3_4xtpo"] @@ -82,8 +81,6 @@ sprite_frames = SubResource("SpriteFrames_5hm0m") animation = &"replay" centered = false -[node name="Calculator" parent="." instance=ExtResource("1_tpuy7")] - [node name="Cards" type="Node" parent="."] [node name="Blocks" type="Node" parent="."] diff --git a/levels/base_level/next_level_button/next_level_button.gd b/levels/base_level/next_level_button/next_level_button.gd index 52b0ec9..8b058e6 100644 --- a/levels/base_level/next_level_button/next_level_button.gd +++ b/levels/base_level/next_level_button/next_level_button.gd @@ -16,7 +16,6 @@ func _ready(): position = Vector2(WIDTH / 2 - 20, HEIGHT + 80) $Word.set_word(">") -# Called every frame. 'delta' is the elapsed time since the previous frame. -func _process(delta): +func _process(_delta): if fade_flag: position.y = HEIGHT + 50 - 120 * (1 - pow($FadeTimer.time_left / $FadeTimer.wait_time, 1.5)) diff --git a/levels/chapter_menu/chapter_menu.gd b/levels/chapter_menu/chapter_menu.gd index 7b0e7cc..a9bb8af 100644 --- a/levels/chapter_menu/chapter_menu.gd +++ b/levels/chapter_menu/chapter_menu.gd @@ -1,10 +1,10 @@ extends Node2D +class_name ChapterMenu - -const BaseLevel := preload("res://levels/base_level/base_level.tscn") -const LevelButton := preload("res://levels/chapter_menu/level_menu/level_button/level_button.tscn") -const LevelMenu := preload("res://levels/chapter_menu/level_menu/level_menu.tscn") +const BaseLevelScn := preload("res://levels/base_level/base_level.tscn") +const LevelButtonScn := preload("res://levels/chapter_menu/level_menu/level_button/level_button.tscn") +const LevelMenuScn := preload("res://levels/chapter_menu/level_menu/level_menu.tscn") # Called when the node enters the scene tree for the first time. @@ -13,32 +13,28 @@ const button_width : int = 50 const button_heigth : int = 50 func init() -> void: - var chap_num : int = len(BaseLevel.instantiate().DATA) - for chapter_id in range(0, chap_num): - # print(chapter_id) - var level_num : int = len(BaseLevel.instantiate().DATA[chapter_id]) - var button = LevelButton.instantiate(); - var x : int = button_width * (chapter_id % 9) + 60 - var y : int = button_heigth * (chapter_id / 9) + 100 - button.init(chapter_id, level_num, Vector2(x, y), 0) + var chap_num : int = len(BaseLevelScn.instantiate().DATA) + for lvl_id in range(0, chap_num): + # print(lvl_id) + var level_num : int = len(BaseLevelScn.instantiate().DATA[lvl_id]) + var button = LevelButtonScn.instantiate(); + var x : int = button_width * (lvl_id % 9) + 60 + var y : int = button_heigth * (lvl_id / 9) + 100 + button.init(lvl_id, level_num, Vector2(x, y), 0) button.enter_chapter.connect(_is_choose_chapter) add_child(button) func _ready(): - print("ready") + # print("ready") init() - #print(BaseLevel.instantiate().DATA[chapter_id]) + #print(BaseLevelScn.instantiate().DATA[chapter_id]) # print(chapter_num) -# Called every frame. 'delta' is the elapsed time since the previous frame. -func _process(delta): - pass - -func _is_choose_chapter(chapter_id : int, level_num : int): - var level_menu = LevelMenu.instantiate() - level_menu.init(chapter_id, level_num) +func _is_choose_chapter(chap_id : int, level_num : int): + var level_menu = LevelMenuScn.instantiate() + level_menu.init(chap_id, level_num) # print("here ready to change scene to ", chapter_id) get_tree().root.add_child(level_menu) queue_free() diff --git a/levels/chapter_menu/level_menu/level_button/level_button.gd b/levels/chapter_menu/level_menu/level_button/level_button.gd index f6b5023..0a1b504 100644 --- a/levels/chapter_menu/level_menu/level_button/level_button.gd +++ b/levels/chapter_menu/level_menu/level_button/level_button.gd @@ -7,15 +7,7 @@ signal enter_chapter(chapter_id:int, level_num : int) var lvl_id = 0 # 如果 type = 0 那么 lvl_id 表示的是该章节的关卡数 lvl_num var chap_id = 0 var button_type = 0 # type = 0 表示为选择章节的按钮, type = 1 表示为选择关卡的按钮 -# Called when the node enters the scene tree for the first time. -func _ready(): - pass # Replace with function body. - - -# Called every frame. 'delta' is the elapsed time since the previous frame. -func _process(delta): - pass func set_word(value: String) -> void: $Word.set_word(value) @@ -36,8 +28,8 @@ func init(chapter_id: int, level_id : int, pos : Vector2, type : int) -> void : button_type = type func _on_pressed(): - print("choose: ",chap_id,"-",lvl_id) - print("! ") + # print("choose: ",chap_id,"-",lvl_id) + # print("! ") $SFXButtonDown.play() if button_type == 1: enter_level.emit(chap_id, lvl_id) diff --git a/levels/chapter_menu/level_menu/level_button/level_button.tscn b/levels/chapter_menu/level_menu/level_button/level_button.tscn index 57ad4cf..ccd244e 100644 --- a/levels/chapter_menu/level_menu/level_button/level_button.tscn +++ b/levels/chapter_menu/level_menu/level_button/level_button.tscn @@ -12,3 +12,4 @@ script = ExtResource("1_wwqfn") [node name="Word" parent="." index="0" instance=ExtResource("3_js2i2")] position = Vector2(19, 19) scale = Vector2(1.5, 1.5) +text_id = 0 diff --git a/levels/chapter_menu/level_menu/level_menu.gd b/levels/chapter_menu/level_menu/level_menu.gd index aefa87c..c6c9bbc 100644 --- a/levels/chapter_menu/level_menu/level_menu.gd +++ b/levels/chapter_menu/level_menu/level_menu.gd @@ -1,14 +1,14 @@ extends Node2D - +class_name LevelMenu const CHAP_NAMES = ["=P", "Add and Multiply", "()", "Equal?", "<>"] const I_NUMBER = ["I","II","III","VI","V"] -const LevelButton := preload("res://levels/chapter_menu/level_menu/level_button/level_button.tscn") -const BaseLevel := preload("res://levels/base_level/base_level.tscn") -const Credits := preload("res://objects/credits/credits.tscn") +const LevelButtonScn := preload("res://levels/chapter_menu/level_menu/level_button/level_button.tscn") +const BaseLevelScn := preload("res://levels/base_level/base_level.tscn") +const CreditsScn := preload("res://objects/credits/credits.tscn") # Called when the node enters the scene tree for the first time. @@ -19,21 +19,21 @@ const button_heigth : int = 50 func init(chap_id : int, lvl_num : int) -> void: #print("init?") - if chap_id == len(BaseLevel.instantiate().DATA): - add_child(Credits.instantiate()) + if chap_id == len(BaseLevelScn.instantiate().DATA): + add_child(CreditsScn.instantiate()) $Title.set_text("") return if lvl_num == -1: - lvl_num = len(BaseLevel.instantiate().DATA[chap_id]) + lvl_num = len(BaseLevelScn.instantiate().DATA[chap_id]) $Title.set_text("Ch." + I_NUMBER[chap_id] + " " + CHAP_NAMES[chap_id]) chapter_id = chap_id for level_id in range(0, lvl_num): #print(level_id) - var button = LevelButton.instantiate(); + var button = LevelButtonScn.instantiate(); var x : int = button_width * (level_id % 7) + 60 var y : int = button_heigth * (level_id / 7) + 100 button.init(chapter_id, level_id, Vector2(x, y), 1) @@ -48,12 +48,9 @@ func _ready(): #print("why not?") pass -# Called every frame. 'delta' is the elapsed time since the previous frame. -func _process(delta): - pass func _on_button_enter_level(chap_id: int, lvl_id: int) -> void: - var base_level := BaseLevel.instantiate() + var base_level := BaseLevelScn.instantiate() # print(chap_id, lvl_id) @@ -67,8 +64,8 @@ func _input(event: InputEvent): _on_back_button_pressed() func _on_back_button_pressed(): - var ChapterMenu = load("res://levels/chapter_menu/chapter_menu.tscn") - var chapter_menu = ChapterMenu.instantiate() + var ChapterMenuScn = load("res://levels/chapter_menu/chapter_menu.tscn") + var chapter_menu = ChapterMenuScn.instantiate() chapter_menu.init() get_tree().root.add_child(chapter_menu) diff --git a/objects/block/block.gd b/objects/block/block.gd index 997abc0..97ac109 100644 --- a/objects/block/block.gd +++ b/objects/block/block.gd @@ -3,32 +3,31 @@ extends Area2D class_name Block -const SHAKE_AMOUNT := 4 +const SHAKE_AMOUNT := 3.0 ## 振动时的振幅(单位为像素)。 -var occupied := false -var occupied_word: String -var occupied_card: Card +var occupied := false ## 当 Block 上有 Card 或 Block 字符固定时,occupied 为 true。 +var occupied_word := "_" ## Block 上的字符,若没有字符则为 _ 。 +var occupied_card: Card = null + +var quest_pos := -1 ## 在表达式中对应字符位置的下标。 +var is_shaking := false ## 是否正在振动。 -var quest_pos := -1 -var is_shaking := false func _on_area_entered(area: Card): - #prints("Entered", self, name) if not occupied: area.on_card_entered(self) func _on_area_exited(area: Card): - #prints("Exited", self, name) if not occupied: area.on_card_exited() -func set_word(e: String) -> void: - $Word.set_word(e) - if e != "_" and e != ".": +func set_word(value: String) -> void: + $Word.set_word(value) + if value != "_" and value != ".": occupied = true - occupied_word = e + occupied_word = value else: occupied = false occupied_word = "_" @@ -63,15 +62,33 @@ func _process(_delta): $Word.position.x = offset -func shake(): +## 开始震动。 +## is_frame_red 表示是否将框改为红色。 +func shake(is_frame_red: bool) -> void: + # 开启震动 $ShakeTimer.start() is_shaking = true - -func _on_shake_timer_timeout(): + + # 使附着的卡牌也震动 + if occupied_card != null: + occupied_card.shake(not is_frame_red, SHAKE_AMOUNT, $ShakeTimer.wait_time) # 若框不红,则里面的字要红 + elif not is_frame_red: + $Word.set_color(ImageLib.PALETTE["red"]) + + if is_frame_red: + $GoalFrameSprite.animation = "red" + +func _on_shake_timer_timeout() -> void: is_shaking = false + $GoalFrameSprite.animation = "default" + $Word.set_color(ImageLib.PALETTE["default"]) -func set_victory(v: bool): - if occupied and occupied_card: +func set_victory(v: bool) -> void: + if v and occupied_card != null: occupied_card.set_victory(v) + +func set_color(value: Color) -> void: + if occupied_card != null: + occupied_card.set_color(value) else: - $Word.set_victory(v) + $Word.set_color(value) diff --git a/objects/block/block.tscn b/objects/block/block.tscn index 75f677a..89f6f66 100644 --- a/objects/block/block.tscn +++ b/objects/block/block.tscn @@ -1,13 +1,33 @@ -[gd_scene load_steps=6 format=3 uid="uid://ckjmjwywh78fe"] +[gd_scene load_steps=8 format=3 uid="uid://ckjmjwywh78fe"] [ext_resource type="Script" path="res://objects/block/block.gd" id="1_11h57"] [ext_resource type="PackedScene" uid="uid://cvx7wowcbfo0r" path="res://objects/word/word.tscn" id="2_pjs40"] [ext_resource type="Texture2D" uid="uid://dvj7bjepxeks0" path="res://objects/block/pit.png" id="3_ye3hk"] -[ext_resource type="Texture2D" uid="uid://cb71r50jw2a3i" path="res://objects/block/goal_frame2.png" id="4_v6rgm"] +[ext_resource type="Texture2D" uid="uid://cb71r50jw2a3i" path="res://objects/block/goal_frame.png" id="4_yvtjk"] +[ext_resource type="Texture2D" uid="uid://cvv532ui6p5gg" path="res://objects/block/goal_frame_red.png" id="5_et5vw"] [sub_resource type="RectangleShape2D" id="RectangleShape2D_3if4e"] size = Vector2(16, 16) +[sub_resource type="SpriteFrames" id="SpriteFrames_3dme2"] +animations = [{ +"frames": [{ +"duration": 1.0, +"texture": ExtResource("4_yvtjk") +}], +"loop": true, +"name": &"default", +"speed": 5.0 +}, { +"frames": [{ +"duration": 1.0, +"texture": ExtResource("5_et5vw") +}], +"loop": true, +"name": &"red", +"speed": 5.0 +}] + [node name="Block" type="Area2D"] script = ExtResource("1_11h57") @@ -22,11 +42,11 @@ debug_color = Color(0.968627, 0, 0.470588, 0.419608) z_index = -1 texture = ExtResource("3_ye3hk") -[node name="GoalFrameSprite" type="Sprite2D" parent="."] -texture = ExtResource("4_v6rgm") - [node name="ShakeTimer" type="Timer" parent="."] -wait_time = 0.1 +wait_time = 0.2 + +[node name="GoalFrameSprite" type="AnimatedSprite2D" parent="."] +sprite_frames = SubResource("SpriteFrames_3dme2") [connection signal="area_entered" from="." to="." method="_on_area_entered"] [connection signal="area_exited" from="." to="." method="_on_area_exited"] diff --git a/objects/block/goal_frame2.png b/objects/block/goal_frame.png similarity index 100% rename from objects/block/goal_frame2.png rename to objects/block/goal_frame.png diff --git a/objects/block/goal_frame2.png.import b/objects/block/goal_frame.png.import similarity index 71% rename from objects/block/goal_frame2.png.import rename to objects/block/goal_frame.png.import index bf7c4a2..88950b4 100644 --- a/objects/block/goal_frame2.png.import +++ b/objects/block/goal_frame.png.import @@ -3,15 +3,15 @@ importer="texture" type="CompressedTexture2D" uid="uid://cb71r50jw2a3i" -path="res://.godot/imported/goal_frame2.png-4f7b6acdd1bc6edd73dd7776acb6603f.ctex" +path="res://.godot/imported/goal_frame.png-a7b1f754b088dcafe5e3aa2d70e0feb1.ctex" metadata={ "vram_texture": false } [deps] -source_file="res://objects/block/goal_frame2.png" -dest_files=["res://.godot/imported/goal_frame2.png-4f7b6acdd1bc6edd73dd7776acb6603f.ctex"] +source_file="res://objects/block/goal_frame.png" +dest_files=["res://.godot/imported/goal_frame.png-a7b1f754b088dcafe5e3aa2d70e0feb1.ctex"] [params] diff --git a/objects/block/goal_frame_red.png b/objects/block/goal_frame_red.png new file mode 100644 index 0000000..f4e169c Binary files /dev/null and b/objects/block/goal_frame_red.png differ diff --git a/objects/card_base/card_base_highlighted.png.import b/objects/block/goal_frame_red.png.import similarity index 65% rename from objects/card_base/card_base_highlighted.png.import rename to objects/block/goal_frame_red.png.import index 5c5565a..7fca3a1 100644 --- a/objects/card_base/card_base_highlighted.png.import +++ b/objects/block/goal_frame_red.png.import @@ -2,16 +2,16 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cs7vfmsrhpomj" -path="res://.godot/imported/card_base_highlighted.png-50d4af98046c8a87d73d61ca6664f89e.ctex" +uid="uid://cvv532ui6p5gg" +path="res://.godot/imported/goal_frame_red.png-33a617e8ebea76a5c0e24baba0e7afcc.ctex" metadata={ "vram_texture": false } [deps] -source_file="res://objects/card_base/card_base_highlighted.png" -dest_files=["res://.godot/imported/card_base_highlighted.png-50d4af98046c8a87d73d61ca6664f89e.ctex"] +source_file="res://objects/block/goal_frame_red.png" +dest_files=["res://.godot/imported/goal_frame_red.png-33a617e8ebea76a5c0e24baba0e7afcc.ctex"] [params] diff --git a/objects/calculator/calculator.tscn b/objects/calculator/calculator.tscn deleted file mode 100644 index 3b2d191..0000000 --- a/objects/calculator/calculator.tscn +++ /dev/null @@ -1,6 +0,0 @@ -[gd_scene load_steps=2 format=3 uid="uid://xom5kpqs1rue"] - -[ext_resource type="Script" path="res://objects/calculator/calculator.gd" id="1_b65po"] - -[node name="Calculator" type="Node"] -script = ExtResource("1_b65po") diff --git a/objects/card/card.gd b/objects/card/card.gd index b3676ec..bba1a27 100644 --- a/objects/card/card.gd +++ b/objects/card/card.gd @@ -16,11 +16,16 @@ var last_occupied_area: Block var is_card_base_entered = 0 var entered_card_base_global_position: Vector2 -var is_victory = false +var is_victory := false + +var shake_amount : float +var is_shaking := false + func _ready(): origin_global_position = global_position last_global_position = global_position + $HighlightSprite.visible = false func _on_mouse_release(): #prints(entered_area.name, is_card_entered, entered_area.occupied) @@ -69,7 +74,7 @@ func _on_mouse_pressed(): is_card_base_entered = 0 -func _input_event(viewport: Object, event: InputEvent, shape_idx: int) -> void: +func _input_event(_viewport: Object, event: InputEvent, _shape_idx: int) -> void: #prints(self, event) if not is_victory and event is InputEventMouseButton: if event.is_pressed() and event.button_index == MOUSE_BUTTON_LEFT: @@ -80,14 +85,21 @@ func _input_event(viewport: Object, event: InputEvent, shape_idx: int) -> void: if event.is_pressed() and event.button_index == MOUSE_BUTTON_RIGHT: reset_position() -func _process(delta: float) -> void: - #prints(name, global_position) +func _process(_delta: float) -> void: if not have_deal_on_mouse_release and not Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT): _on_mouse_release() if is_dragging: have_deal_on_mouse_release = false global_position = get_global_mouse_position().round() Input.set_default_cursor_shape(Input.CURSOR_DRAG) + + var offset := 0 + if is_shaking: + var progress = $ShakeTimer.time_left / $ShakeTimer.wait_time * 2 * PI + offset = int(sin(progress) * shake_amount) + $CardBackSprite.position.x = offset + $HighlightSprite.position.x = offset + $Word.position.x = offset func on_card_entered(area: Block) -> void: is_card_entered += 1 @@ -106,22 +118,53 @@ func on_card_base_exited() -> void: is_card_base_entered -= 1 #print(is_card_entered) -func set_word(e: String) -> void: - $Word.set_word(e) +func set_word(value: String) -> void: + $Word.set_word(value) + var card_type := "card-%s" % ExprValidator.get_char_type_as_str(get_word()).to_lower() + if ImageLib.PALETTE.has(card_type): + ImageLib.update_animation( + $CardBackSprite, 1, 3, 1, "res://objects/card/card%d.png", + ImageLib.PALETTE["lightblue"], ImageLib.PALETTE[card_type] + ) + func get_word() -> String: return $Word.get_word() +func shake(is_letter_red: bool, amount: float, duration: float) -> void: + $ShakeTimer.wait_time = duration + shake_amount = amount + + # 开启震动 + $ShakeTimer.start() + is_shaking = true + + + if is_letter_red: + $Word.set_color(ImageLib.PALETTE["red"]) + else: + $HighlightSprite.visible = false + + func _on_mouse_entered(): - $CardBackSprite.animation = "highlighted" + $HighlightSprite.visible = true Input.set_default_cursor_shape(Input.CURSOR_POINTING_HAND) func _on_mouse_exited(): - $CardBackSprite.animation = "default" + $HighlightSprite.visible = false Input.set_default_cursor_shape(Input.CURSOR_ARROW) +func set_color(value: Color) -> void: + $Word.set_color(value) + func set_victory(v: bool): - $Word.set_victory(v) + if v: + $CollisionShape2D.set_deferred("disabled", true) + + +func _on_shake_timer_timeout(): + is_shaking = false + $Word.set_color(ImageLib.PALETTE["default"]) diff --git a/objects/card/card.tscn b/objects/card/card.tscn index 0c0571d..02533d7 100644 --- a/objects/card/card.tscn +++ b/objects/card/card.tscn @@ -5,11 +5,9 @@ [ext_resource type="Texture2D" uid="uid://cfbqh1k0etr6g" path="res://objects/card/card1.png" id="3_o4f0v"] [ext_resource type="Texture2D" uid="uid://c8qjbksyn1bqt" path="res://objects/card/card2.png" id="4_c30j1"] [ext_resource type="Texture2D" uid="uid://bevrfq1hhyqwe" path="res://objects/card/card3.png" id="5_w81n3"] -[ext_resource type="Texture2D" uid="uid://cmcc5xrpkrd0f" path="res://objects/card/card_highlighted.png" id="6_po2i6"] +[ext_resource type="Texture2D" uid="uid://pmapbo480in1" path="res://objects/card/card_highlight_border.png" id="7_kix4t"] [ext_resource type="AudioStream" uid="uid://cj4v8ehypq3sk" path="res://objects/card/put_down.wav" id="7_u4ylf"] -[sub_resource type="RectangleShape2D" id="RectangleShape2D_gu6l6"] - [sub_resource type="SpriteFrames" id="SpriteFrames_fo0r0"] animations = [{ "frames": [{ @@ -25,35 +23,35 @@ animations = [{ "loop": true, "name": &"default", "speed": 5.0 -}, { -"frames": [{ -"duration": 1.0, -"texture": ExtResource("6_po2i6") -}], -"loop": true, -"name": &"highlighted", -"speed": 5.0 }] +[sub_resource type="RectangleShape2D" id="RectangleShape2D_gu6l6"] + [node name="Card" type="Area2D"] z_index = 50 script = ExtResource("1_0bviv") [node name="Word" parent="." instance=ExtResource("1_ns36n")] z_index = 1 -frame_progress = 0.111549 + +[node name="CardBackSprite" type="AnimatedSprite2D" parent="."] +sprite_frames = SubResource("SpriteFrames_fo0r0") + +[node name="HighlightSprite" type="Sprite2D" parent="."] +texture = ExtResource("7_kix4t") [node name="CollisionShape2D" type="CollisionShape2D" parent="."] z_index = 15 shape = SubResource("RectangleShape2D_gu6l6") -[node name="CardBackSprite" type="AnimatedSprite2D" parent="."] -sprite_frames = SubResource("SpriteFrames_fo0r0") -autoplay = "default" - [node name="SFXPutDown" type="AudioStreamPlayer" parent="."] stream = ExtResource("7_u4ylf") volume_db = 13.117 +[node name="ShakeTimer" type="Timer" parent="."] +wait_time = 100.0 +one_shot = true + [connection signal="mouse_entered" from="." to="." method="_on_mouse_entered"] [connection signal="mouse_exited" from="." to="." method="_on_mouse_exited"] +[connection signal="timeout" from="ShakeTimer" to="." method="_on_shake_timer_timeout"] diff --git a/objects/card/card_highlight_border.png b/objects/card/card_highlight_border.png new file mode 100644 index 0000000..2f7f195 Binary files /dev/null and b/objects/card/card_highlight_border.png differ diff --git a/objects/card/card_highlight_border.png.import b/objects/card/card_highlight_border.png.import new file mode 100644 index 0000000..ba37e56 --- /dev/null +++ b/objects/card/card_highlight_border.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://pmapbo480in1" +path="res://.godot/imported/card_highlight_border.png-c0b130850c2d1dcdc5af25ea7e1dafdc.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://objects/card/card_highlight_border.png" +dest_files=["res://.godot/imported/card_highlight_border.png-c0b130850c2d1dcdc5af25ea7e1dafdc.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/objects/card/card_highlighted.png b/objects/card/card_highlighted.png deleted file mode 100644 index 8a2398b..0000000 Binary files a/objects/card/card_highlighted.png and /dev/null differ diff --git a/objects/card_base/card_base.gd b/objects/card_base/card_base.gd index 036de2e..24b8580 100644 --- a/objects/card_base/card_base.gd +++ b/objects/card_base/card_base.gd @@ -2,7 +2,7 @@ extends Area2D class_name CardBase -const Card := preload("res://objects/card/card.tscn") +const CardScn := preload("res://objects/card/card.tscn") const FADE_MOVE_AMOUNT := 70 signal card_put @@ -11,6 +11,9 @@ signal card_put var fade_flag := false var mouse_on := false +enum { DEFAULT, HIGHLIGHT, DISABLED} +var available_stat := DEFAULT + var card_count = 0 var new_card_node: Card @@ -25,11 +28,11 @@ func set_card_count(value): card_count = value if card_count > 0: if mouse_on: - $AnimatedSprite2D.animation = "highlighted" + available_stat = HIGHLIGHT else: - $AnimatedSprite2D.animation = "default" + available_stat = DEFAULT else: - $AnimatedSprite2D.animation = "disabled" + available_stat = DISABLED update_card_count_label() @@ -39,7 +42,7 @@ func draw_card(): set_card_count(card_count - 1) $SFXPickUp.play() Input.set_default_cursor_shape(Input.CURSOR_DRAG) - new_card_node = Card.instantiate() + new_card_node = CardScn.instantiate() new_card_node.set_word($Word.get_word()) new_card_node.set_position(position) $Cards.add_child(new_card_node) @@ -66,13 +69,21 @@ func _on_area_exited(area: Card): if area.get_word() == $Word.get_word(): area.on_card_base_exited() -func _input_event(viewport: Object, event: InputEvent, shape_idx: int) -> void: +func _input_event(_viewport: Object, event: InputEvent, _shape_idx: int) -> void: if event is InputEventMouseButton: if event.is_pressed() and event.button_index == MOUSE_BUTTON_LEFT: draw_card() func set_word(e: String) -> void: $Word.set_word(e) + + var card_type := "card-%s" % ExprValidator.get_char_type_as_str(e).to_lower() + if ImageLib.PALETTE.has(card_type): + ImageLib.update_animation($CardBaseSprite, 1, 1, 1, "res://objects/card_base/card_base%d.png", + ImageLib.PALETTE["lightblue"], ImageLib.PALETTE[card_type]) + +func get_word() -> String: + return $Word.get_word() func _on_card_put(): emit_signal("card_put") @@ -85,28 +96,33 @@ func set_victory() -> void: $Label.set_visible(false) for card: Card in $Cards.get_children(): - card.is_victory = true + card.set_victory(true) func reset_all_card_position(): for card: Card in $Cards.get_children(): card.reset_position() -func _process(delta) -> void: +func _process(_delta) -> void: if fade_flag: var offset = $FadeTimer.time_left / $FadeTimer.wait_time offset = (1 - pow(offset, 1.5)) * FADE_MOVE_AMOUNT - $AnimatedSprite2D.position.y = offset + $CardBaseSprite.position.y = offset + $HighlightSprite.position.y = offset + $DisabledSprite.position.y = offset $Word.position.y = offset + + $HighlightSprite.visible = (available_stat == HIGHLIGHT) + $DisabledSprite.visible = (available_stat == DISABLED) func _on_mouse_entered(): mouse_on = true - if $AnimatedSprite2D.animation == "default": - $AnimatedSprite2D.animation = "highlighted" + if available_stat == DEFAULT: + available_stat = HIGHLIGHT Input.set_default_cursor_shape(Input.CURSOR_POINTING_HAND) func _on_mouse_exited(): mouse_on = false - if $AnimatedSprite2D.animation == "highlighted": - $AnimatedSprite2D.animation = "default" + if available_stat == HIGHLIGHT: + available_stat = DEFAULT Input.set_default_cursor_shape(Input.CURSOR_ARROW) diff --git a/objects/card_base/card_base.tscn b/objects/card_base/card_base.tscn index 1bdbd1b..e02c82e 100644 --- a/objects/card_base/card_base.tscn +++ b/objects/card_base/card_base.tscn @@ -5,7 +5,7 @@ [ext_resource type="FontFile" uid="uid://1000owdyvwfg" path="res://fonts/unifont-15.1.04.otf" id="3_0qykx"] [ext_resource type="Texture2D" uid="uid://b5d2rfms7vyfi" path="res://objects/card_base/card_base1.png" id="3_h7i1h"] [ext_resource type="Texture2D" uid="uid://bkjbrw1ppac5q" path="res://objects/card_base/card_base_disabled.png" id="4_0unxr"] -[ext_resource type="Texture2D" uid="uid://cs7vfmsrhpomj" path="res://objects/card_base/card_base_highlighted.png" id="5_tkdj2"] +[ext_resource type="Texture2D" uid="uid://bkuwo3g8pyuyi" path="res://objects/card_base/card_base_highlight_border.png" id="5_r41oe"] [ext_resource type="AudioStream" uid="uid://0sefo32v1jne" path="res://objects/card_base/pick_up.wav" id="7_0c7kt"] [sub_resource type="RectangleShape2D" id="RectangleShape2D_2cmwb"] @@ -28,14 +28,6 @@ animations = [{ "loop": true, "name": &"disabled", "speed": 5.0 -}, { -"frames": [{ -"duration": 1.0, -"texture": ExtResource("5_tkdj2") -}], -"loop": true, -"name": &"highlighted", -"speed": 5.0 }] [node name="CardBase" type="Area2D"] @@ -45,10 +37,20 @@ script = ExtResource("1_k5qn1") shape = SubResource("RectangleShape2D_2cmwb") debug_color = Color(0.65098, 0.431373, 0.756863, 0.419608) +[node name="CardBaseSprite" type="AnimatedSprite2D" parent="."] +sprite_frames = SubResource("SpriteFrames_bhb3x") + [node name="Word" parent="." instance=ExtResource("2_jvsp3")] -z_index = 1 frame_progress = 0.953211 +[node name="HighlightSprite" type="Sprite2D" parent="."] +visible = false +texture = ExtResource("5_r41oe") + +[node name="DisabledSprite" type="Sprite2D" parent="."] +visible = false +texture = ExtResource("4_0unxr") + [node name="Label" type="Label" parent="."] offset_left = -13.0 offset_top = 17.0 @@ -59,9 +61,6 @@ text = "0" horizontal_alignment = 1 vertical_alignment = 1 -[node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="."] -sprite_frames = SubResource("SpriteFrames_bhb3x") - [node name="Cards" type="Node" parent="."] [node name="FadeTimer" type="Timer" parent="."] diff --git a/objects/card_base/card_base_highlight_border.png b/objects/card_base/card_base_highlight_border.png new file mode 100644 index 0000000..19ad568 Binary files /dev/null and b/objects/card_base/card_base_highlight_border.png differ diff --git a/objects/card_base/card_base_highlight_border.png.import b/objects/card_base/card_base_highlight_border.png.import new file mode 100644 index 0000000..5de750e --- /dev/null +++ b/objects/card_base/card_base_highlight_border.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bkuwo3g8pyuyi" +path="res://.godot/imported/card_base_highlight_border.png-02641e6f6e23a694917134adadde948a.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://objects/card_base/card_base_highlight_border.png" +dest_files=["res://.godot/imported/card_base_highlight_border.png-02641e6f6e23a694917134adadde948a.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/objects/card_base/card_base_highlighted.png b/objects/card_base/card_base_highlighted.png deleted file mode 100644 index 6213968..0000000 Binary files a/objects/card_base/card_base_highlighted.png and /dev/null differ diff --git a/objects/main_menu/main_menu.gd b/objects/main_menu/main_menu.gd index d88730c..3102de5 100644 --- a/objects/main_menu/main_menu.gd +++ b/objects/main_menu/main_menu.gd @@ -1,7 +1,9 @@ extends Node2D -const ChapterMenu = preload("res://levels/chapter_menu/chapter_menu.tscn") -const BaseLevel := preload("res://levels/base_level/base_level.tscn") +class_name MainMenu + +const ChapterMenuScn := preload("res://levels/chapter_menu/chapter_menu.tscn") +const BaseLevelScn := preload("res://levels/base_level/base_level.tscn") signal enter_level() @@ -10,7 +12,7 @@ func _ready(): func _on_start_button_pressed(): - var base_level := BaseLevel.instantiate() + var base_level := BaseLevelScn.instantiate() # print(chap_id, lvl_id) @@ -18,7 +20,7 @@ func _on_start_button_pressed(): get_tree().root.add_child(base_level) enter_level.emit() queue_free() - #get_tree().root.add_child(ChapterMenu.instantiate()) + #get_tree().root.add_child(ChapterMenuScn.instantiate()) #queue_free() diff --git a/objects/main_menu/start_button.gd b/objects/main_menu/start_button.gd index f430e49..02e8a60 100644 --- a/objects/main_menu/start_button.gd +++ b/objects/main_menu/start_button.gd @@ -1,11 +1,5 @@ extends Button -# Called when the node enters the scene tree for the first time. func _ready(): $AnimatedSprite2D.play() - - -# Called every frame. 'delta' is the elapsed time since the previous frame. -func _process(delta): - pass diff --git a/objects/word/word.gd b/objects/word/word.gd index 77fba69..74ba764 100644 --- a/objects/word/word.gd +++ b/objects/word/word.gd @@ -2,7 +2,7 @@ extends AnimatedSprite2D @export var text_id := 1 -@export var is_victory = false +@export var color := Color.BLACK const STEP := 30 @@ -18,60 +18,47 @@ const LETTER_NAME = ["_", "=", "P", "b", "D", func get_letter_id(letter: String) -> int: for i in range(len(LETTER_NAME)): if LETTER_NAME[i] == letter: - return i + 1 + return i assert(false, "Letter " + letter + " not found") return -1 ## 内建,不要用。 func set_text_id(value: int) -> void: + assert(value >= 0, "text id < 0") text_id = value - - assert(value > 0, "text id <= 0") - - var animation_id = str(text_id) + str(is_victory) - if not sprite_frames.has_animation(animation_id): - sprite_frames.add_animation(animation_id) - for i in range(3): - sprite_frames.add_frame(animation_id, load_image(i * STEP + text_id, is_victory)) - animation = animation_id + update_animation() + +func update_animation() -> void: + ImageLib.update_animation(self, text_id + 1, 3, STEP, "res://objects/word/sprites/sprite%d.png", Color.BLACK, color) ## 更改字母为 text。 func set_word(text: String) -> void: if text == "_" or text == ".": - set_text_id(1) + set_text_id(0) else: set_text_id(get_letter_id(text)) ## 获得当前字母。 func get_word() -> String: - return LETTER_NAME[text_id - 1] + return LETTER_NAME[text_id] func _ready(): - set_text_id(text_id) + update_animation() +func set_color(value: Color) -> void: + color = value + update_animation() -func _process(delta): - if animation == "default": - set_text_id(text_id) - -func set_victory(v: bool): - if v != is_victory: - is_victory = v - set_text_id(text_id) - -func load_image(h: int, is_victory: bool): - var image := load("res://objects/word/sprites/sprite" + str(h) + ".png") - if is_victory: - var new_texture = image.get_image() - for x in range(new_texture.get_width()): - for y in range(new_texture.get_height()): - var color = new_texture.get_pixel(x, y) - if color == Color(0, 0, 0, 1): # 如果像素是黑色 - new_texture.set_pixel(x, y, Color.hex(0xf5df4dff)) # 将其改为金黄色 - return ImageTexture.create_from_image(new_texture) - - return image +## 设置是否为关卡通过状态。 +## 已弃用,请使用 set_color 或 set_color_from_name 代替。 +## @deprecated +func set_victory(v: bool) -> void: + push_warning("void set_victory(v: bool) is deprecated.") + if v: + set_color(ImageLib.PALETTE["golden"]) + else: + set_color(ImageLib.PALETTE["default"]) diff --git a/objects/word/word.tscn b/objects/word/word.tscn index 2a8189a..3678ecc 100644 --- a/objects/word/word.tscn +++ b/objects/word/word.tscn @@ -1,33 +1,19 @@ -[gd_scene load_steps=6 format=3 uid="uid://cvx7wowcbfo0r"] +[gd_scene load_steps=4 format=3 uid="uid://cvx7wowcbfo0r"] [ext_resource type="Texture2D" uid="uid://cyyu6tsje4x72" path="res://objects/word/sprites/sprite3.png" id="1_ariam"] [ext_resource type="Script" path="res://objects/word/word.gd" id="1_lkxlh"] -[ext_resource type="Texture2D" uid="uid://bqtqgiv5etyny" path="res://objects/word/sprites/sprite28.png" id="2_l4ops"] -[ext_resource type="Texture2D" uid="uid://dua7hylyf4ws6" path="res://objects/word/sprites/sprite53.png" id="3_2kshj"] [sub_resource type="SpriteFrames" id="SpriteFrames_3n85j"] animations = [{ "frames": [{ "duration": 1.0, "texture": ExtResource("1_ariam") -}, { -"duration": 1.0, -"texture": ExtResource("2_l4ops") -}, { -"duration": 1.0, -"texture": ExtResource("3_2kshj") }], "loop": true, "name": &"default", -"speed": 3.0 -}, { -"frames": [], -"loop": true, -"name": &"new_animation", "speed": 5.0 }] [node name="Word" type="AnimatedSprite2D"] sprite_frames = SubResource("SpriteFrames_3n85j") -autoplay = "default" script = ExtResource("1_lkxlh") diff --git a/project.godot b/project.godot index 6c26cb8..0fb29d5 100644 --- a/project.godot +++ b/project.godot @@ -16,6 +16,10 @@ config/features=PackedStringArray("4.2", "Forward Plus") boot_splash/bg_color=Color(0.196078, 0.184314, 0.301961, 1) config/icon="res://=P3.png" +[debug] + +gdscript/warnings/integer_division=0 + [display] window/size/viewport_width=1920 diff --git a/objects/calculator/calculator.gd b/scripts/expr_validator.gd similarity index 69% rename from objects/calculator/calculator.gd rename to scripts/expr_validator.gd index 9aa281a..bdf841a 100644 --- a/objects/calculator/calculator.gd +++ b/scripts/expr_validator.gd @@ -1,19 +1,17 @@ ## 用于判定表达式 `expr` 是否满足通关要求的脚本。 ## 一般情况下只需要使用 `Array check(expr: String, req_pos: Array)` 即可,其他不需要使用。 +class_name ExprValidator -extends Node - - -func is_alpha(ch: String) -> bool: ## 判断字符 ch 是否为字母。 +static func is_alpha(ch: String) -> bool: ## 判断字符 ch 是否为字母。 return (ch >= 'A' and ch <= 'Z') or (ch >= 'a' and ch <= 'z') -func is_eye(ch: String) -> bool: ## 判断字符 ch 是否为眼睛。 +static func is_eye(ch: String) -> bool: ## 判断字符 ch 是否为眼睛。 return ch == "*" or ch == "=" -func is_left_mouth(ch: String) -> bool: ## 判断字符 ch 是否为左边的嘴巴。 +static func is_left_mouth(ch: String) -> bool: ## 判断字符 ch 是否为左边的嘴巴。 return ch == "q" or ch == "d" or ch == "<" -func is_right_mouth(ch: String) -> bool: ## 判断字符 ch 是否为右边的嘴巴。 +static func is_right_mouth(ch: String) -> bool: ## 判断字符 ch 是否为右边的嘴巴。 return ch == "P" or ch == "b" or ch == "D" or ch == ">" -func is_smile(string: String) -> bool: ## 判断长度为 2 的字符串 string 是否为笑脸。 +static func is_smile(string: String) -> bool: ## 判断长度为 2 的字符串 string 是否为笑脸。 return (is_left_mouth(string[0]) and is_eye(string[1])) or (is_eye(string[0]) and is_right_mouth(string[1])) """ @@ -23,44 +21,78 @@ bracl: ( bracr: ) val: Q/0/1 -左/右 * < ( ) Q -* 0 0 1 0 1 -< 0 1 1 0 1 -( 0 0 1 0 1 -) 1 1 1 1 1 -Q 1 1 1 1 1 +左/右 * < ( ) Q 0 +* 0 0 1 0 1 1 +< 0 1 1 0 1 1 +( 0 0 1 0 1 1 +) 1 1 1 1 1 1 +Q 1 1 1 1 1 0 +0 1 1 1 1 0 0 """ """ -左/右 * < ( ) Q +左/右 * < ( ) Q/0 * / / 0 / 0 < / 0 0 / 0 ( / / 0 / 0 ) 0 0 1 0 1 -Q 0 0 1 0 1 +Q/0 0 0 1 0 1 """ -const IS_PAIR_VALID := [[0, 0, 1, 0, 1], [0, 1, 1, 0, 1], [0, 0, 1, 0, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1]] +const IS_PAIR_VALID := [ + [0, 0, 1, 0, 1, 1], + [0, 1, 1, 0, 1, 1], + [0, 0, 1, 0, 1, 1], + [1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 0], + [1, 1, 1, 1, 0, 0] +] -enum {OP, COMP, BRAC_L, BRAC_R, VAL} +enum {OP, COMP, BRACL, BRACR, VAR, CONST} -func get_char_type(ch: String) -> int: +static func get_char_type(ch: String) -> int: if ch == "*" or ch == "+": return OP elif ch == "<" or ch == "=" or ch == ">": return COMP elif ch == "(": - return BRAC_L + return BRACL elif ch == ")": - return BRAC_R + return BRACR + elif ch == "0" or ch == "1": + return CONST + elif is_alpha(ch): + return VAR + else: + push_error("get_char_type(%s) is undefined" % ch) + return -1 + +static func get_char_type_enum_name(value: int) -> String: + if value == OP: + return "OP" + elif value == COMP: + return "COMP" + elif value == BRACL: + return "BRACL" + elif value == BRACR: + return "BRACR" + elif value == VAR: + return "VAR" + elif value == CONST: + return "CONST" else: - return VAL + push_error("get_char_type_enum_name(%d) is undefined" % value) + return "ERR" -func has_implict_prod(string: String) -> bool: - return get_char_type(string[0]) in [BRAC_R, VAL] and get_char_type(string[1]) in [BRAC_L, VAL] +static func get_char_type_as_str(ch: String) -> String: + return get_char_type_enum_name(get_char_type(ch)) + -func get_priority(ch: String) -> int: +static func has_implict_prod(string: String) -> bool: + return get_char_type(string[0]) in [BRACR, VAR, CONST] and get_char_type(string[1]) in [BRACL, VAR, CONST] + +static func get_priority(ch: String) -> int: if ch == "*": return 4 elif ch == "+": @@ -78,7 +110,7 @@ func get_priority(ch: String) -> int: ## 将中缀表达式 expr 转为后缀表达式并返回(不判断 expr 的合法性)。 -func infix_to_suffix(expr: String) -> String: +static func infix_to_suffix(expr: String) -> String: var opt_stack: Array = [] var res := "" var pre_ch := "@" @@ -93,8 +125,8 @@ func infix_to_suffix(expr: String) -> String: - print("ch=", ch, " stk=", opt_stack, " res=", res) - if get_char_type(ch) == VAL: + #print("ch=", ch, " stk=", opt_stack, " res=", res) + if get_char_type(ch) in [VAR, CONST]: res += ch elif ch == "(": opt_stack.push_back(ch) @@ -119,14 +151,14 @@ func infix_to_suffix(expr: String) -> String: if get_char_type(res[len(res) - 1]) == COMP: res += "@" + #prints(expr, res) - prints(expr, res) return res ## 计算后缀表达式 expr 的值,其中变量的值给定,放在字典 var_values 中(不判断 expr 的合法性)。 -func calculate_value(expr: String, var_values: Dictionary) -> bool: +static func calculate_value(expr: String, var_values: Dictionary) -> bool: var stack: Array = [] var val := false @@ -138,7 +170,7 @@ func calculate_value(expr: String, var_values: Dictionary) -> bool: val = false - elif get_char_type(ch) == VAL: + elif get_char_type(ch) in [VAR, CONST]: if is_alpha(ch): stack.push_back(var_values[ch]) else: @@ -166,10 +198,10 @@ func calculate_value(expr: String, var_values: Dictionary) -> bool: ## 判断表达式 expr 是否合法(不检查括号匹配)。 ## ## 合法返回 [],否则返回不合法的下标。 -func check_valid(expr: String) -> Array: - if get_char_type(expr[0]) != BRAC_L and get_char_type(expr[0]) != VAL: +static func check_valid(expr: String) -> Array: + if get_char_type(expr[0]) != BRACL and get_char_type(expr[0]) not in [VAR, CONST]: return [0] - if get_char_type(expr[len(expr) - 1]) != BRAC_R and get_char_type(expr[len(expr) - 1]) != VAL: + if get_char_type(expr[len(expr) - 1]) != BRACR and get_char_type(expr[len(expr) - 1]) not in [VAR, CONST]: return [len(expr) - 1] for i in range(len(expr) - 1): if not IS_PAIR_VALID[get_char_type(expr[i])][get_char_type(expr[i + 1])]: @@ -192,24 +224,25 @@ func check_valid(expr: String) -> Array: ## 判断表达式 expr 是否满足笑脸要求。 ## -## 合法返回 -1,否则返回不为笑脸的最小字符下标。 -func check_smile(expr: String, req_pos: Array) -> int: +## 合法返回 [],否则返回不为笑脸的字符下标列表。 +static func check_smile(expr: String, req_pos: Array) -> Array: + var res := [] for i in req_pos: if (i == 0 or not is_smile(expr.substr(i - 1, 2))) and (i == len(expr) - 1 or not is_smile(expr.substr(i, 2))): - return i - return -1 + res.append(i) + return res ## 判断表达式 expr 是否恒为真(不检查是否合法)。 ## ## 合法返回空字典,否则一种使得表达式为假的变量取值。 -func check_always_true(expr: String) -> Dictionary: +static func check_always_true(expr: String) -> Dictionary: var var_values: Dictionary = {} # var_values[var] 获取 var 的值。类型:Dictionary[String, bool] var var_names := [] # 变量名称列表 # 中缀转后缀 expr = infix_to_suffix(expr) - print(expr) + #print(expr) for ch in expr: if is_alpha(ch) and not var_names.has(ch): @@ -232,7 +265,7 @@ func check_always_true(expr: String) -> Dictionary: ## 若表达式不恒为正,返回 ["NOT_ALWAYS_TRUE", var_values],其中 var_values: Dictionary[String, bool] 是一种使得表达式为假的变量取值。 ## 否则符合要求,返回 ["OK", 200]。 -func check(expr: String, req_pos: Array) -> Array: +static func check(expr: String, req_pos: Array) -> Array: var tmp @@ -243,7 +276,7 @@ func check(expr: String, req_pos: Array) -> Array: # 判断笑脸要求 tmp = check_smile(expr, req_pos) - if tmp != -1: + if tmp != []: return ["SMILE_UNSATISFIED", tmp] # 判断是否恒为正 @@ -254,7 +287,7 @@ func check(expr: String, req_pos: Array) -> Array: return ["OK", 200] -func _ready(): +static func test(): #check("q*Pq*bd*b=D", []) check("0=P<>P", []) #assert(check("(1=P)+(0=P)", []) == ["OK", 200], "9") @@ -267,8 +300,3 @@ func _ready(): #assert(check("1+P=(0+)", [3]) == ["INVALID", [6, 7]], "4") # #assert(check("P+Q=P", [3]) == ["NOT_ALWAYS_TRUE", {"P": false, "Q": true}], "6") - - - -func _process(_delta): - pass diff --git a/scripts/image_lib.gd b/scripts/image_lib.gd new file mode 100644 index 0000000..dc9224e --- /dev/null +++ b/scripts/image_lib.gd @@ -0,0 +1,50 @@ +class_name ImageLib + +const PALETTE = { + "red": Color("#dd4132"), + "golden": Color("#f5df4d"), + + "orange": Color("#ffb463"), + "card-op": Color("#ffb463"), + + "skyblue": Color("#c2fdff"), + "card-bracl": Color("#c2fdff"), + "card-bracr": Color("#c2fdff"), + + "lightblue": Color("#8eacf3"), + "card-var": Color("#8eacf3"), + + "lightgreen": Color("#8ff297"), + "card-comp": Color("#8ff297"), + + "sliver": Color("#f0f0f0"), + "card-const": Color("#f0f0f0"), + + "default": Color.BLACK, +} + +static func change_color(image: Texture2D, old_color: Color, new_color: Color) -> Texture2D: + var new_texture = image.get_image() + for x in range(new_texture.get_width()): + for y in range(new_texture.get_height()): + var color = new_texture.get_pixel(x, y) + if color == old_color: + new_texture.set_pixel(x, y, new_color) + return ImageTexture.create_from_image(new_texture) + +static func update_animation( + sprite: AnimatedSprite2D, + start: int, + n: int, + step: int, + path_pattern: String, + old_color: Color, + new_color: Color +) -> void: + var animation_id = "%d_%d_%d_%s" % [start, n, step, new_color.to_html()] + if not sprite.sprite_frames.has_animation(animation_id): + sprite.sprite_frames.add_animation(animation_id) + for i in range(n): + var image = load(path_pattern % (i * step + start)) + sprite.sprite_frames.add_frame(animation_id, ImageLib.change_color(image, old_color, new_color)) + sprite.play(animation_id)