xiandie/scene/little_game/弹珠游戏/弹珠游戏.gd

312 lines
8.2 KiB
GDScript3
Raw Normal View History

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
@onready var hand_pivot = $Pivot/HandPivot as Node2D
2025-06-05 07:17:51 +00:00
@onready var label = %RichTextLabel as RichTextLabel
@onready var audio_player = %AudioStreamPlayer as AudioStreamPlayer
2025-06-06 16:19:37 +00:00
# ogg文件丢球drop 地面明显碰撞声)+球碰球hit 响亮)+细碎滚动声loop三个部分
@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)
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)
# 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
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()
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
# interactE 与 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
sfx_shoot.play()
2025-04-21 13:31:12 +00:00
# 触发射击事件
if wave_tween and wave_tween.is_running():
wave_tween.pause()
2025-04-21 13:31:12 +00:00
var ball = pivot.get_child(0) as RigidBody2D
var direction = Vector2(0, -1).rotated(hand_pivot.rotation)
2025-04-21 13:31:12 +00:00
ball.linear_velocity = direction * 200
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():
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():
sfx_shoot.stop()
2025-06-05 07:17:51 +00:00
if round_id > 0:
# 后面的回合必须每次都击中球
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
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()