2023-01-26 18:25:40 +00:00
|
|
|
extends Node
|
|
|
|
|
|
|
|
# _process(delta) is called by this class
|
|
|
|
# player input is handled here
|
|
|
|
# unit declares its intention in process_unit()
|
|
|
|
# stage environment interacts with the unit in interact()
|
|
|
|
# unit executes its resulting state in react()
|
|
|
|
# stage environment interacts with the unit once more in interact_post()
|
|
|
|
class_name GameScene
|
|
|
|
|
|
|
|
export var tile_set_name: String
|
2023-01-30 08:02:47 +00:00
|
|
|
export var camera_h_offset : float = 3
|
|
|
|
export var camera_v_offset : float = 1
|
2023-01-30 08:40:31 +00:00
|
|
|
export var finish_x_pos : int
|
|
|
|
export var target_time : float
|
|
|
|
export var defeat_cutscene : String
|
|
|
|
export var victory_cutscene : String
|
2023-01-30 13:54:29 +00:00
|
|
|
export var level : int
|
2023-01-26 18:25:40 +00:00
|
|
|
const Constants = preload("res://Scripts/Constants.gd")
|
|
|
|
const UNIT_DIRECTORY = {
|
2023-01-29 22:46:06 +00:00
|
|
|
Constants.UnitType.CIRNO: preload("res://Units/DownhillAutoscrollerNPCCirno.tscn"),
|
|
|
|
Constants.UnitType.SANAE: preload("res://Units/DownhillAutoscrollerNPCSanae.tscn"),
|
2023-01-26 18:25:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
# positions to unit string
|
2023-01-30 13:54:29 +00:00
|
|
|
var spawning : Dictionary = {}
|
2023-01-26 18:25:40 +00:00
|
|
|
var spawning_map = {} # keeps track of what's alive
|
|
|
|
|
|
|
|
var paused : bool = false
|
2023-01-30 08:40:31 +00:00
|
|
|
var race_over : bool = false
|
2023-01-26 18:25:40 +00:00
|
|
|
|
|
|
|
var units = []
|
|
|
|
var player : Player
|
|
|
|
var player_cam : Camera2D
|
2023-01-30 08:02:47 +00:00
|
|
|
var player_cam_easing_cum_time : float = 0
|
2023-01-26 18:25:40 +00:00
|
|
|
|
|
|
|
# [pressed?, just pressed?, just released?]
|
|
|
|
var input_table = {
|
|
|
|
Constants.PlayerInput.UP: [false, false, false],
|
|
|
|
Constants.PlayerInput.DOWN: [false, false, false],
|
|
|
|
Constants.PlayerInput.LEFT: [false, false, false],
|
|
|
|
Constants.PlayerInput.RIGHT: [false, false, false],
|
|
|
|
Constants.PlayerInput.GBA_A: [false, false, false],
|
|
|
|
Constants.PlayerInput.GBA_B: [false, false, false],
|
|
|
|
Constants.PlayerInput.GBA_SELECT: [false, false, false],
|
|
|
|
}
|
|
|
|
const I_T_PRESSED : int = 0
|
|
|
|
const I_T_JUST_PRESSED : int = 1
|
|
|
|
const I_T_JUST_RELEASED : int = 2
|
|
|
|
|
|
|
|
var stage_env
|
|
|
|
|
|
|
|
var time_elapsed : float = 0
|
2023-01-27 22:35:01 +00:00
|
|
|
|
|
|
|
var stage_finished : bool = false
|
|
|
|
|
|
|
|
# for UI
|
|
|
|
var time_elapsed_in_race : float = 0
|
2023-01-27 22:30:20 +00:00
|
|
|
var player_speed_mph : float = 0
|
2023-01-26 18:25:40 +00:00
|
|
|
|
|
|
|
var rng = RandomNumberGenerator.new()
|
|
|
|
|
|
|
|
# Called when the node enters the scene tree for the first time.
|
|
|
|
func _ready():
|
2023-01-30 13:54:29 +00:00
|
|
|
if level == 1:
|
|
|
|
MusicController.play_kyouko_snow()
|
|
|
|
else:
|
|
|
|
MusicController.play_letty_snow()
|
2023-01-30 02:39:59 +00:00
|
|
|
|
2023-01-30 10:12:53 +00:00
|
|
|
units.append($Player)
|
2023-01-26 18:25:40 +00:00
|
|
|
player = units[0]
|
|
|
|
player.init_unit_w_scene(self)
|
2023-01-30 10:12:53 +00:00
|
|
|
player_cam = $Player/Camera2D
|
2023-01-26 18:25:40 +00:00
|
|
|
player_cam.make_current()
|
2023-01-30 08:02:47 +00:00
|
|
|
player_cam.offset_v = camera_v_offset
|
2023-01-26 18:25:40 +00:00
|
|
|
|
2023-01-30 10:12:53 +00:00
|
|
|
units.append($Rival)
|
|
|
|
$Rival.init_unit_w_scene(self)
|
2023-01-29 07:24:33 +00:00
|
|
|
|
2023-01-26 18:25:40 +00:00
|
|
|
stage_env = load("res://Scripts/StageEnvironment.gd").new(self)
|
2023-01-30 10:12:53 +00:00
|
|
|
$Player/Camera2D.make_current()
|
2023-01-26 18:25:40 +00:00
|
|
|
for spawning_key in spawning:
|
|
|
|
spawning_map[spawning_key] = null
|
2023-01-30 01:07:02 +00:00
|
|
|
|
|
|
|
find_node("PitTransitionPlayer").play("InitialFade")
|
2023-01-30 10:12:53 +00:00
|
|
|
|
|
|
|
target_time = $Rival.replay.length()
|
2023-01-26 18:25:40 +00:00
|
|
|
|
|
|
|
# Called every frame. 'delta' is the elapsed time since the previous frame.
|
2023-01-30 08:02:47 +00:00
|
|
|
func _process(delta):
|
2023-01-26 18:25:40 +00:00
|
|
|
# visual effects
|
|
|
|
if (player.facing == Constants.Direction.RIGHT):
|
2023-01-27 21:55:12 +00:00
|
|
|
player_cam.offset_h = camera_h_offset
|
2023-01-26 18:25:40 +00:00
|
|
|
else:
|
2023-01-27 21:55:12 +00:00
|
|
|
player_cam.offset_h = -camera_h_offset
|
2023-01-26 18:25:40 +00:00
|
|
|
|
|
|
|
read_paused()
|
|
|
|
if not paused:
|
|
|
|
# game logic
|
|
|
|
process_spawning()
|
|
|
|
for unit in units:
|
|
|
|
unit.reset_actions()
|
|
|
|
unit.handle_input(delta)
|
|
|
|
unit.process_unit(delta, time_elapsed)
|
|
|
|
stage_env.interact(unit, delta)
|
|
|
|
unit.react(delta)
|
|
|
|
time_elapsed += delta
|
2023-01-27 22:35:01 +00:00
|
|
|
if !stage_finished:
|
|
|
|
time_elapsed_in_race += delta
|
2023-01-27 22:30:20 +00:00
|
|
|
# 1 grid unit = 2ft, 1 grid unit / s = 1.36 mph
|
2023-01-30 08:40:31 +00:00
|
|
|
player_speed_mph = player.h_speed * 1.36
|
|
|
|
|
|
|
|
if not race_over and player.pos.x >= finish_x_pos:
|
|
|
|
race_over = true
|
2023-01-30 10:12:53 +00:00
|
|
|
|
|
|
|
if is_instance_valid(player.recorder):
|
|
|
|
player.recorder.save()
|
|
|
|
|
2023-01-30 08:40:31 +00:00
|
|
|
if time_elapsed_in_race > target_time:
|
|
|
|
# lost race
|
|
|
|
get_tree().change_scene("res://Scenes/" + defeat_cutscene + ".tscn")
|
|
|
|
else:
|
|
|
|
# won race
|
|
|
|
get_tree().change_scene("res://Scenes/" + victory_cutscene + ".tscn")
|
2023-01-26 18:25:40 +00:00
|
|
|
|
|
|
|
func read_paused():
|
|
|
|
if Input.is_action_just_pressed(Constants.INPUT_MAP[Constants.PlayerInput.GBA_START]):
|
|
|
|
paused = !paused
|
|
|
|
|
|
|
|
func process_spawning():
|
|
|
|
for one_spawn in spawning.keys():
|
|
|
|
if spawning_map[one_spawn] != null:
|
|
|
|
continue
|
|
|
|
if abs(one_spawn[0] - player.pos.x) >= Constants.SPAWN_DISTANCE + 1 or abs(one_spawn[1] - player.pos.y) >= Constants.SPAWN_DISTANCE + 1:
|
|
|
|
continue
|
|
|
|
if abs(one_spawn[0] - player.pos.x) <= Constants.SPAWN_DISTANCE:
|
|
|
|
continue
|
|
|
|
# NPCUnit
|
|
|
|
var npc_scene = UNIT_DIRECTORY[Constants.UnitType.get(spawning[one_spawn])]
|
|
|
|
var npc_instance = npc_scene.instance()
|
|
|
|
add_child(npc_instance)
|
|
|
|
units.append(npc_instance)
|
|
|
|
npc_instance.spawn_point = one_spawn
|
|
|
|
spawning_map[one_spawn] = npc_instance
|
|
|
|
npc_instance.pos.x = one_spawn[0]
|
|
|
|
npc_instance.pos.y = one_spawn[1]
|
|
|
|
npc_instance.position.x = npc_instance.pos.x * Constants.GRID_SIZE
|
|
|
|
npc_instance.position.y = -1 * npc_instance.pos.y * Constants.GRID_SIZE
|
|
|
|
npc_instance.init_unit_w_scene(self)
|
|
|
|
|
|
|
|
func handle_player_input():
|
|
|
|
# early exit
|
|
|
|
|
|
|
|
if player.get_current_action() == Constants.UnitCurrentAction.RECOILING:
|
|
|
|
player.set_action(Constants.ActionType.RECOIL)
|
|
|
|
return
|
|
|
|
|
|
|
|
for input_num in input_table.keys():
|
|
|
|
if Input.is_action_pressed(Constants.INPUT_MAP[input_num]):
|
|
|
|
input_table[input_num][I_T_PRESSED] = true
|
|
|
|
input_table[input_num][I_T_JUST_RELEASED] = false
|
|
|
|
if Input.is_action_just_pressed(Constants.INPUT_MAP[input_num]):
|
|
|
|
input_table[input_num][I_T_JUST_PRESSED] = true
|
|
|
|
else:
|
|
|
|
input_table[input_num][I_T_JUST_PRESSED] = false
|
|
|
|
else:
|
|
|
|
input_table[input_num][I_T_PRESSED] = false
|
|
|
|
input_table[input_num][I_T_JUST_PRESSED] = false
|
|
|
|
if Input.is_action_just_released(Constants.INPUT_MAP[input_num]):
|
|
|
|
input_table[input_num][I_T_JUST_RELEASED] = true
|
|
|
|
else:
|
|
|
|
input_table[input_num][I_T_JUST_RELEASED] = false
|
|
|
|
|
|
|
|
# process input_table
|
|
|
|
|
|
|
|
if input_table[Constants.PlayerInput.LEFT][I_T_PRESSED] or input_table[Constants.PlayerInput.RIGHT][I_T_PRESSED]:
|
|
|
|
if input_table[Constants.PlayerInput.LEFT][I_T_PRESSED] and input_table[Constants.PlayerInput.RIGHT][I_T_PRESSED]:
|
|
|
|
input_table[Constants.PlayerInput.LEFT][I_T_PRESSED] = false
|
|
|
|
input_table[Constants.PlayerInput.LEFT][I_T_JUST_PRESSED] = false
|
|
|
|
var input_dir
|
|
|
|
if input_table[Constants.PlayerInput.LEFT][I_T_PRESSED]:
|
|
|
|
input_dir = Constants.Direction.LEFT
|
|
|
|
else:
|
|
|
|
input_dir = Constants.Direction.RIGHT
|
|
|
|
# if action-idle or action-jumping
|
|
|
|
if (player.get_current_action() == Constants.UnitCurrentAction.IDLE
|
|
|
|
or player.get_current_action() == Constants.UnitCurrentAction.JUMPING):
|
|
|
|
# set move
|
|
|
|
player.set_action(Constants.ActionType.MOVE)
|
|
|
|
# set facing
|
|
|
|
player.facing = input_dir
|
|
|
|
|
|
|
|
if input_table[Constants.PlayerInput.GBA_A][I_T_PRESSED]:
|
|
|
|
if (player.get_current_action() == Constants.UnitCurrentAction.JUMPING
|
2023-01-30 16:26:49 +00:00
|
|
|
or ((player.get_current_action() == Constants.UnitCurrentAction.IDLE)
|
2023-01-26 18:25:40 +00:00
|
|
|
and input_table[Constants.PlayerInput.GBA_A][I_T_JUST_PRESSED])):
|
2023-01-30 13:54:29 +00:00
|
|
|
if player.unit_conditions[Constants.UnitCondition.IS_ON_GROUND] or player.get_current_action() == Constants.UnitCurrentAction.JUMPING:
|
|
|
|
player.set_action(Constants.ActionType.JUMP)
|
|
|
|
else:
|
|
|
|
player.buffer_jump()
|
2023-01-28 01:56:15 +00:00
|
|
|
|
|
|
|
player.custom_inputs()
|