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..82526fc 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_from_name("golden") + $Word2.set_color_from_name("golden") + else: + $Word1.set_color_from_name("default") + $Word2.set_color_from_name("default") + diff --git a/levels/base_level/base_level.gd b/levels/base_level/base_level.gd index 65e9966..8c14381 100644 --- a/levels/base_level/base_level.gd +++ b/levels/base_level/base_level.gd @@ -21,14 +21,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"], @@ -123,7 +124,7 @@ 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 @@ -151,7 +152,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 +162,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) + # prints("expr:", expr) + # prints("info:", info) if info[0] != "OK": $SFXs/WrongAnswer.play() @@ -179,25 +180,26 @@ 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) + # print(expr[i-1]+expr[i]) + block_array[i-1].set_color_from_name("golden") + block_array[i].set_color_from_name("golden") func _input(event: InputEvent): if event is InputEventKey: 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/objects/block/block.gd b/objects/block/block.gd index 997abc0..8fdd1f1 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_from_name("red") + + if is_frame_red: + $GoalFrameSprite.animation = "red" + +func _on_shake_timer_timeout() -> void: is_shaking = false + $GoalFrameSprite.animation = "default" + $Word.set_color_from_name("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_from_name(name: String) -> void: + if occupied_card != null: + occupied_card.set_color_from_name(name) else: - $Word.set_victory(v) + $Word.set_color_from_name("golden") 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/card/card.gd b/objects/card/card.gd index b3676ec..f9e8b64 100644 --- a/objects/card/card.gd +++ b/objects/card/card.gd @@ -16,7 +16,11 @@ 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 @@ -81,13 +85,19 @@ func _input_event(viewport: Object, event: InputEvent, shape_idx: int) -> void: reset_position() func _process(delta: float) -> void: - #prints(name, global_position) 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 + $Word.position.x = offset func on_card_entered(area: Block) -> void: is_card_entered += 1 @@ -113,6 +123,20 @@ 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_from_name("red") + else: + $CardBackSprite.animation = "default" + func _on_mouse_entered(): @@ -123,5 +147,18 @@ func _on_mouse_exited(): $CardBackSprite.animation = "default" Input.set_default_cursor_shape(Input.CURSOR_ARROW) +func set_color_from_name(name: String) -> void: + $Word.set_color_from_name(name) + +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_from_name("default") diff --git a/objects/card/card.tscn b/objects/card/card.tscn index 0c0571d..912f83d 100644 --- a/objects/card/card.tscn +++ b/objects/card/card.tscn @@ -55,5 +55,10 @@ autoplay = "default" 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_base/card_base.gd b/objects/card_base/card_base.gd index 036de2e..c6ca2c7 100644 --- a/objects/card_base/card_base.gd +++ b/objects/card_base/card_base.gd @@ -73,6 +73,9 @@ func _input_event(viewport: Object, event: InputEvent, shape_idx: int) -> void: func set_word(e: String) -> void: $Word.set_word(e) + +func get_word() -> String: + return $Word.get_word() func _on_card_put(): emit_signal("card_put") @@ -85,7 +88,7 @@ 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(): diff --git a/objects/word/word.gd b/objects/word/word.gd index 77fba69..c66e7b5 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,22 +18,23 @@ 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) + update_animation() + +func update_animation() -> void: + var animation_id = str(text_id) + color.to_html() 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)) + sprite_frames.add_frame(animation_id, load_image(i * STEP + text_id + 1, color)) animation = animation_id @@ -41,37 +42,56 @@ func set_text_id(value: int) -> void: ## 更改字母为 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 _process(delta): - if animation == "default": - set_text_id(text_id) + update_animation() -func set_victory(v: bool): - if v != is_victory: - is_victory = v - set_text_id(text_id) +func set_color_from_name(name: String) -> void: + if name == "red": + set_color(Color("#DD4132")) + elif name == "golden": + set_color(Color("#F5DF4D")) + elif name == "default": + set_color(Color.BLACK) + else: + assert(false, "color %s not in list" % name) + +func set_color(value: Color) -> void: + color = value + update_animation() + +## 设置是否为关卡通过状态。 +## 已弃用,请使用 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_from_name("golden") + else: + set_color_from_name("default") -func load_image(h: int, is_victory: bool): +func load_image(h: int, new_color: Color): var image := load("res://objects/word/sprites/sprite" + str(h) + ".png") - if is_victory: + if new_color != Color.BLACK: 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)) # 将其改为金黄色 + if color == Color.BLACK: # 如果像素是黑色 + new_texture.set_pixel(x, y, new_color) # 将其改为金黄色 return ImageTexture.create_from_image(new_texture) return image