2025-04-21 13:31:12 +00:00
|
|
|
|
extends CanvasLayer
|
|
|
|
|
|
2025-05-14 20:43:55 +00:00
|
|
|
|
signal exit(success)
|
|
|
|
|
|
2025-06-05 07:17:51 +00:00
|
|
|
|
@onready var animation_player = $AnimationPlayer as AnimationPlayer
|
2025-04-21 13:31:12 +00:00
|
|
|
|
@onready var pivot = $Pivot as Node2D
|
2025-05-13 11:45:33 +00:00
|
|
|
|
@onready var hand_pivot = $Pivot/HandPivot as Node2D
|
2025-06-05 07:17:51 +00:00
|
|
|
|
@onready var label = %RichTextLabel as RichTextLabel
|
2025-06-05 09:37:30 +00:00
|
|
|
|
@onready var audio_player = %AudioStreamPlayer as AudioStreamPlayer
|
2025-06-06 16:19:37 +00:00
|
|
|
|
|
|
|
|
|
# ogg文件:丢球(drop 地面明显碰撞声)+球碰球(hit 响亮)+细碎滚动声(loop)三个部分
|
2025-06-05 09:37:30 +00:00
|
|
|
|
@onready var sfx_hit = $SfxHit as Sfx
|
|
|
|
|
@onready var sfx_shoot = $SfxShoot as Sfx
|
2025-06-06 16:19:37 +00:00
|
|
|
|
@onready var sfx_dispatch = $SfxDispatch as Sfx
|
|
|
|
|
|
2025-04-21 13:31:12 +00:00
|
|
|
|
|
2025-05-14 20:43:55 +00:00
|
|
|
|
var dialogue_c02 = preload("res://asset/dialogue/c02.dialogue")
|
|
|
|
|
|
2025-06-05 07:17:51 +00:00
|
|
|
|
var balls_scene_dict = {
|
|
|
|
|
0: preload("uid://d0tuv2dtlosfe"),
|
|
|
|
|
1: preload("uid://cv12saxinfoi7"),
|
|
|
|
|
2: preload("uid://dr0rwr0xjgnjw"),
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-21 13:31:12 +00:00
|
|
|
|
|
|
|
|
|
func _ready() -> void:
|
|
|
|
|
layer = GlobalConfig.CANVAS_LAYER_LITTLE_GAME
|
2025-06-05 07:17:51 +00:00
|
|
|
|
hand_pivot.modulate.a = 0
|
|
|
|
|
# 0:默认 1:寻找弹珠(老虎钳可以换弹珠) 2:给出弹珠 3:游戏结束
|
|
|
|
|
if ArchiveManager.get_global_value("c02_ball_game_stage", 0) == 2:
|
|
|
|
|
# checkout_round(2)
|
2025-06-05 07:18:23 +00:00
|
|
|
|
checkout_round(0)
|
2025-06-05 07:17:51 +00:00
|
|
|
|
else:
|
|
|
|
|
intro()
|
|
|
|
|
label.hide()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 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")
|
|
|
|
|
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()
|
2025-04-21 13:31:12 +00:00
|
|
|
|
wave_hand()
|
2025-06-05 07:17:51 +00:00
|
|
|
|
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))
|
2025-06-06 16:19:37 +00:00
|
|
|
|
sfx_dispatch.play()
|
2025-04-21 13:31:12 +00:00
|
|
|
|
|
|
|
|
|
|
2025-06-05 07:17:51 +00:00
|
|
|
|
func reload_hand_ball() -> void:
|
|
|
|
|
shooting = false
|
2025-04-21 13:31:12 +00:00
|
|
|
|
# 重新加载
|
|
|
|
|
var ball_scene = preload("res://scene/little_game/弹珠游戏/ball.tscn")
|
|
|
|
|
var ball = ball_scene.instantiate()
|
|
|
|
|
ball.is_shooter = true
|
2025-06-05 07:17:51 +00:00
|
|
|
|
var old_ball = pivot.get_node_or_null("Ball")
|
|
|
|
|
if old_ball:
|
|
|
|
|
pivot.remove_child(old_ball)
|
|
|
|
|
old_ball.queue_free()
|
2025-04-21 13:31:12 +00:00
|
|
|
|
pivot.add_child(ball)
|
|
|
|
|
pivot.move_child(ball, 0)
|
2025-06-05 07:17:51 +00:00
|
|
|
|
ball.name = "Ball"
|
|
|
|
|
# ball.rand_id()
|
|
|
|
|
ball.hit_ball.connect(_on_hit_ball)
|
2025-06-05 09:37:30 +00:00
|
|
|
|
ball.hit_ball_callback.connect(_on_hit_ball_callback)
|
2025-06-05 07:17:51 +00:00
|
|
|
|
ball.hit_boundary.connect(_on_hit_boundary)
|
2025-04-21 13:31:12 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var wave_tween: Tween
|
|
|
|
|
|
2025-05-14 20:43:55 +00:00
|
|
|
|
|
2025-04-21 13:31:12 +00:00
|
|
|
|
func wave_hand() -> void:
|
2025-06-05 07:17:51 +00:00
|
|
|
|
hand_pivot.visible = true
|
|
|
|
|
if hand_pivot.modulate.a < 1:
|
|
|
|
|
create_tween().tween_property(hand_pivot, "modulate:a", 1.0, 0.5)
|
2025-06-05 09:37:30 +00:00
|
|
|
|
# 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)
|
2025-04-21 13:31:12 +00:00
|
|
|
|
# 转动 pivot
|
2025-05-13 11:45:33 +00:00
|
|
|
|
if wave_tween:
|
|
|
|
|
if not wave_tween.is_running():
|
|
|
|
|
wave_tween.play()
|
2025-04-21 13:31:12 +00:00
|
|
|
|
return
|
|
|
|
|
wave_tween = create_tween()
|
2025-05-13 11:45:33 +00:00
|
|
|
|
var timeout = 2.0
|
2025-05-14 20:43:55 +00:00
|
|
|
|
(
|
|
|
|
|
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)
|
|
|
|
|
)
|
2025-04-21 13:31:12 +00:00
|
|
|
|
wave_tween.set_loops(9999999)
|
|
|
|
|
|
|
|
|
|
|
2025-06-05 07:17:51 +00:00
|
|
|
|
# interact(E) 与 spacebar 都可以 shoot
|
2025-04-21 13:31:12 +00:00
|
|
|
|
func shoot() -> void:
|
2025-06-05 07:17:51 +00:00
|
|
|
|
if not round_ready or shooting or round_id < 0:
|
2025-04-21 13:31:12 +00:00
|
|
|
|
return
|
|
|
|
|
shooting = true
|
2025-06-05 09:37:30 +00:00
|
|
|
|
sfx_shoot.play()
|
2025-04-21 13:31:12 +00:00
|
|
|
|
# 触发射击事件
|
|
|
|
|
if wave_tween and wave_tween.is_running():
|
2025-06-05 09:37:30 +00:00
|
|
|
|
wave_tween.pause()
|
2025-04-21 13:31:12 +00:00
|
|
|
|
var ball = pivot.get_child(0) as RigidBody2D
|
2025-05-13 11:45:33 +00:00
|
|
|
|
var direction = Vector2(0, -1).rotated(hand_pivot.rotation)
|
2025-04-21 13:31:12 +00:00
|
|
|
|
ball.linear_velocity = direction * 200
|
2025-05-13 11:45:33 +00:00
|
|
|
|
ball.angular_velocity = 20.0
|
2025-06-05 07:17:51 +00:00
|
|
|
|
create_tween().tween_property(hand_pivot, "modulate:a", 0.0, 0.5)
|
2025-05-14 20:43:55 +00:00
|
|
|
|
|
|
|
|
|
|
2025-04-21 13:31:12 +00:00
|
|
|
|
func _on_hit_ball():
|
2025-06-05 09:37:30 +00:00
|
|
|
|
sfx_hit.play()
|
|
|
|
|
sfx_shoot.stop()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _on_hit_ball_callback():
|
2025-06-05 07:17:51 +00:00
|
|
|
|
reload_hand_ball()
|
2025-05-14 20:43:55 +00:00
|
|
|
|
hit_count += 1
|
2025-06-05 07:17:51 +00:00
|
|
|
|
if round_id == 0:
|
|
|
|
|
round_ready = false
|
|
|
|
|
_success_dialogue()
|
|
|
|
|
await dialogue_ended
|
|
|
|
|
checkout_round(1)
|
|
|
|
|
elif round_id == 1 and hit_count >= 3:
|
|
|
|
|
round_ready = false
|
|
|
|
|
_success_dialogue()
|
|
|
|
|
await dialogue_ended
|
|
|
|
|
checkout_round(2)
|
|
|
|
|
elif round_id == 2 and hit_count >= 5:
|
|
|
|
|
round_ready = false
|
2025-06-05 07:18:23 +00:00
|
|
|
|
game_win()
|
2025-06-05 07:17:51 +00:00
|
|
|
|
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")
|
2025-05-14 20:43:55 +00:00
|
|
|
|
|
2025-04-21 13:31:12 +00:00
|
|
|
|
|
|
|
|
|
func _on_hit_boundary():
|
2025-06-05 09:37:30 +00:00
|
|
|
|
sfx_shoot.stop()
|
2025-06-05 07:17:51 +00:00
|
|
|
|
if round_id > 0:
|
|
|
|
|
# 后面的回合必须每次都击中球
|
2025-06-05 09:37:30 +00:00
|
|
|
|
get_tree().call_group("Balls" + str(round_id), "ease_out")
|
2025-06-05 07:17:51 +00:00
|
|
|
|
DialogueManager.show_dialogue_balloon_scene(self, dialogue_c02, "c02_弹珠游戏fail")
|
|
|
|
|
await dialogue_ended
|
|
|
|
|
reload_round()
|
|
|
|
|
else:
|
|
|
|
|
reload_hand_ball()
|
|
|
|
|
wave_hand()
|
2025-04-21 13:31:12 +00:00
|
|
|
|
shooting = false
|
|
|
|
|
|
|
|
|
|
|
2025-06-05 07:18:23 +00:00
|
|
|
|
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
|
2025-06-05 07:17:51 +00:00
|
|
|
|
DialogueManager.show_dialogue_balloon_scene(self, dialogue_c02, "c02_弹珠游戏4")
|
|
|
|
|
await dialogue_ended
|
2025-06-05 07:18:23 +00:00
|
|
|
|
await get_tree().create_timer(3.0).timeout
|
2025-06-05 07:17:51 +00:00
|
|
|
|
exit.emit(true)
|
|
|
|
|
|
|
|
|
|
|
2025-04-21 13:31:12 +00:00
|
|
|
|
func _unhandled_input(event: InputEvent) -> void:
|
|
|
|
|
if event.is_action_pressed("interact"):
|
2025-06-05 07:17:51 +00:00
|
|
|
|
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()
|
|
|
|
|
elif event.is_action_pressed("space"):
|
|
|
|
|
if round_id >= 0:
|
|
|
|
|
shoot()
|
2025-05-29 06:14:02 +00:00
|
|
|
|
get_viewport().set_input_as_handled()
|
2025-05-14 20:43:55 +00:00
|
|
|
|
|
|
|
|
|
|
2025-06-05 07:17:51 +00:00
|
|
|
|
signal 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
|
2025-06-05 14:01:47 +00:00
|
|
|
|
label.text = ("[wave amp=10.0 freq=5.0][shake rate=4.0 level=3] " + tr(translation_key))
|
2025-06-05 07:17:51 +00:00
|
|
|
|
label.show()
|
|
|
|
|
# 因为版权问题,有些 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
|
|
|
|
|
label.hide()
|
|
|
|
|
dialogue_ended.emit()
|