extends CanvasLayer signal exit(success) @onready var animation_player = $AnimationPlayer as AnimationPlayer @onready var pivot = $Pivot as Node2D @onready var hand_pivot = $Pivot/HandPivot as Node2D @onready var label = %RichTextLabel as RichTextLabel @onready var audio_player = %AudioStreamPlayer as AudioStreamPlayer # ogg文件:丢球(drop 地面明显碰撞声)+球碰球(hit 响亮)+细碎滚动声(loop)三个部分 @onready var sfx_hit = $SfxHit as Sfx @onready var sfx_shoot = $SfxShoot as Sfx @onready var sfx_dispatch = $SfxDispatch as Sfx var dialogue_c02 = preload("res://asset/dialogue/c02.dialogue") var balls_scene_dict = { 0: preload("uid://d0tuv2dtlosfe"), 1: preload("uid://cv12saxinfoi7"), 2: preload("uid://dr0rwr0xjgnjw"), } func _ready() -> void: layer = GlobalConfig.CANVAS_LAYER_LITTLE_GAME hand_pivot.modulate.a = 0 # 0:默认 1:寻找弹珠(老虎钳可以换弹珠) 2:给出弹珠 3:游戏结束 if ArchiveManager.get_global_value("c02_ball_game_stage", 0) == 2: # checkout_round(2) checkout_round(0) else: intro() label.modulate.a = 0 func intro(): round_id = -1 # 关闭所有碰撞 for i in range(3): var balls_name = "Balls" + str(i) get_node(balls_name).visible = false get_tree().call_group(balls_name, "hide_away") pivot.get_child(0).modulate.a = 0 DialogueManager.show_dialogue_balloon_scene(self, dialogue_c02, "c02_弹珠游戏0") await ball_dialogue_ended func intro_finished(): SceneManager.disable_prop_item("prop_弹珠") # 0:默认 1:寻找弹珠(老虎钳可以换弹珠) 2:给出弹珠 3:游戏结束 # 放入弹珠,开始游戏 ArchiveManager.set_global_entry("c02_ball_game_stage", 2) var ball = pivot.get_child(0) as RigidBody2D ball.angular_velocity = 20 animation_player.play("give_ball") sfx_dispatch.play() await animation_player.animation_finished # 开始弹珠游戏 checkout_round(0) var round_ready = false var round_id := 0 #-1:intro; 0, 1, 2 var hit_count = 0 var shooting = false func checkout_round(r: int): round_id = r # 关闭所有碰撞 for i in range(3): var balls_name = "Balls" + str(i) get_node(balls_name).visible = false get_tree().call_group(balls_name, "hide_away") reload_round() # 重置当前回合 introduce:是否播放回合 dialogue func reload_round(): # 重置参数 round_ready = false hit_count = 0 hand_pivot.modulate.a = 0 # 重新加载 balls var balls_name = "Balls" + str(round_id) var old_balls = get_node(balls_name) remove_child(old_balls) old_balls.queue_free() var new_balls = balls_scene_dict[round_id].instantiate() new_balls.name = balls_name add_child(new_balls) new_balls.z_index = 2 get_tree().call_group(balls_name, "hide_away") # 分散 balls animation_player.play("dispatch_balls") await animation_player.animation_finished # 加载手中的球 reload_hand_ball() wave_hand() round_ready = true func do_dispatch_balls(): var balls_name = "Balls" + str(round_id) get_tree().call_group(balls_name, "dispatch", Vector2(296.0, 102.0)) sfx_dispatch.play() func reload_hand_ball() -> void: shooting = false # 重新加载 var ball_scene = preload("res://scene/little_game/弹珠游戏/ball.tscn") var ball = ball_scene.instantiate() ball.is_shooter = true var old_ball = pivot.get_node_or_null("Ball") if old_ball: pivot.remove_child(old_ball) old_ball.queue_free() pivot.add_child(ball) pivot.move_child(ball, 0) ball.name = "Ball" # ball.rand_id() ball.hit_ball.connect(_on_hit_ball) ball.hit_ball_callback.connect(_on_hit_ball_callback) ball.hit_boundary.connect(_on_hit_boundary) var wave_tween: Tween func wave_hand() -> void: hand_pivot.visible = true if hand_pivot.modulate.a < 1: create_tween().tween_property(hand_pivot, "modulate:a", 1.0, 0.5) # var hand_ball = pivot.get_child(0) # if hand_ball.modulate.a < 1: # create_tween().tween_property(hand_ball, "modulate:a", 1.0, 0.5) # 转动 pivot if wave_tween: if not wave_tween.is_running(): wave_tween.play() return wave_tween = create_tween() var timeout = 2.0 ( wave_tween . tween_property(hand_pivot, "rotation", 1.0, timeout) . set_ease(Tween.EASE_OUT) . set_trans(Tween.TRANS_SINE) ) ( wave_tween . tween_property(hand_pivot, "rotation", 0.0, timeout) . set_ease(Tween.EASE_IN) . set_trans(Tween.TRANS_SINE) ) ( wave_tween . tween_property(hand_pivot, "rotation", -1.0, timeout) . set_ease(Tween.EASE_OUT) . set_trans(Tween.TRANS_SINE) ) ( wave_tween . tween_property(hand_pivot, "rotation", 0.0, timeout) . set_ease(Tween.EASE_IN) . set_trans(Tween.TRANS_SINE) ) wave_tween.set_loops(9999999) # interact(E) 与 spacebar 都可以 shoot func shoot() -> void: if not round_ready or shooting or round_id < 0: return shooting = true sfx_shoot.play() # 触发射击事件 if wave_tween and wave_tween.is_running(): wave_tween.pause() var ball = pivot.get_child(0) as RigidBody2D var direction = Vector2(0, -1).rotated(hand_pivot.rotation) ball.linear_velocity = direction * 200 ball.angular_velocity = 20.0 create_tween().tween_property(hand_pivot, "modulate:a", 0.0, 0.5) func _on_hit_ball(): sfx_hit.play() sfx_shoot.stop() func _on_hit_ball_callback(): reload_hand_ball() hit_count += 1 if round_id == 0: round_ready = false _success_dialogue() await ball_dialogue_ended checkout_round(1) elif round_id == 1 and hit_count >= 3: round_ready = false _success_dialogue() await ball_dialogue_ended checkout_round(2) elif round_id == 2 and hit_count >= 5: round_ready = false game_win() else: wave_hand() shooting = false func _success_dialogue(): match round_id: 0: DialogueManager.show_dialogue_balloon_scene(self, dialogue_c02, "c02_弹珠游戏1") 1: DialogueManager.show_dialogue_balloon_scene(self, dialogue_c02, "c02_弹珠游戏2") 2: DialogueManager.show_dialogue_balloon_scene(self, dialogue_c02, "c02_弹珠游戏3") func _on_hit_boundary(): sfx_shoot.stop() if round_id > 0: # 后面的回合必须每次都击中球 get_tree().call_group("Balls" + str(round_id), "ease_out") DialogueManager.show_dialogue_balloon_scene(self, dialogue_c02, "c02_弹珠游戏fail") await ball_dialogue_ended reload_round() else: reload_hand_ball() wave_hand() shooting = false func game_win() -> void: print("game_win 弹珠游戏胜利") # 0:默认 1:寻找弹珠(老虎钳可以换弹珠) 2:给出弹珠 3:游戏结束 ArchiveManager.set_global_entry("c02_ball_game_stage", 3) # 弹珠雨 $BallsFalling.emitting = true pivot.visible = false DialogueManager.show_dialogue_balloon_scene(self, dialogue_c02, "c02_弹珠游戏4") await ball_dialogue_ended await get_tree().create_timer(3.0).timeout exit.emit(true) func _unhandled_input(event: InputEvent) -> void: if event.is_action_pressed("interact"): if round_id == -1: # intro 阶段,给出弹珠 var hud = SceneManager.get_prop_hud() as PropHud if SceneManager.get_current_prop(false) != "prop_弹珠": hud.on_toggle_invalid_prop() else: intro_finished() else: shoot() get_viewport().set_input_as_handled() # 为保持 UI 一致性,弹珠游戏不使用 space 键 # elif event.is_action_pressed("space"): # if round_id >= 0: # shoot() # get_viewport().set_input_as_handled() signal ball_dialogue_ended ## The current line var dialogue_line: DialogueLine: set(value): if value: dialogue_line = value apply_dialogue_line() get: return dialogue_line func start( dialogue_resource: DialogueResource, title: String, extra_game_states: Array = [] ) -> void: var temporary_game_states = [self] + extra_game_states self.dialogue_line = await dialogue_resource.get_next_dialogue_line( title, temporary_game_states ) ## Apply any changes to the balloon given a new [DialogueLine]. func apply_dialogue_line() -> void: var translation_key = dialogue_line.translation_key label.text = ("[wave amp=10.0 freq=5.0][shake rate=4.0 level=3] " + tr(translation_key)) var tween = create_tween() tween.tween_property(label, "modulate:a", 1.0, 0.5) tween.tween_interval(2.0) tween.tween_property(label, "modulate:a", 0.0, 0.5) # # 因为版权问题,有些 mp3 文件打不开,所以使用 ogg 格式 # var audio_path = "res://asset/audio/peiyin/ogg/%s.ogg" % translation_key # if FileAccess.file_exists(audio_path): # var stream = load(audio_path) # audio_player.stream = stream # audio_player.play() # await audio_player.finished # await get_tree().create_timer(1.0).timeout # else: # push_warning("No audio file found for " + translation_key) # await get_tree().create_timer(3.0).timeout ball_dialogue_ended.emit()