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 export var camera_h_offset : float = 3 export var camera_v_offset : float = 1 export var finish_x_pos : int export var target_time : float export var defeat_cutscene : String export var victory_cutscene : String const Constants = preload("res://Scripts/Constants.gd") const UNIT_DIRECTORY = { Constants.UnitType.CIRNO: preload("res://Units/DownhillAutoscrollerNPCCirno.tscn"), Constants.UnitType.SANAE: preload("res://Units/DownhillAutoscrollerNPCSanae.tscn"), } # positions to unit string export var spawning : Dictionary var spawning_map = {} # keeps track of what's alive var paused : bool = false var race_over : bool = false var units = [] var player : Player var player_cam : Camera2D var player_cam_easing_cum_time : float = 0 # [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 var stage_finished : bool = false # for UI var time_elapsed_in_race : float = 0 var player_speed_mph : float = 0 var rng = RandomNumberGenerator.new() # Called when the node enters the scene tree for the first time. func _ready(): MusicController.play_kyouko_snow() units.append($Player) player = units[0] player.init_unit_w_scene(self) player_cam = $Player/Camera2D player_cam.make_current() player_cam.offset_v = camera_v_offset units.append($Rival) $Rival.init_unit_w_scene(self) stage_env = load("res://Scripts/StageEnvironment.gd").new(self) $Player/Camera2D.make_current() for spawning_key in spawning: spawning_map[spawning_key] = null find_node("PitTransitionPlayer").play("InitialFade") target_time = $Rival.replay.length() # Called every frame. 'delta' is the elapsed time since the previous frame. func _process(delta): # visual effects if (player.facing == Constants.Direction.RIGHT): player_cam.offset_h = camera_h_offset else: player_cam.offset_h = -camera_h_offset 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 if !stage_finished: time_elapsed_in_race += delta # 1 grid unit = 2ft, 1 grid unit / s = 1.36 mph player_speed_mph = player.h_speed * 1.36 if not race_over and player.pos.x >= finish_x_pos: race_over = true if is_instance_valid(player.recorder): player.recorder.save() 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") 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 or (player.get_current_action() == Constants.UnitCurrentAction.IDLE and player.unit_conditions[Constants.UnitCondition.IS_ON_GROUND] and input_table[Constants.PlayerInput.GBA_A][I_T_JUST_PRESSED])): player.set_action(Constants.ActionType.JUMP) player.custom_inputs()