2025-12-02 00:34:27 +00:00
|
|
|
extends Node2D
|
|
|
|
|
|
2025-12-02 00:54:27 +00:00
|
|
|
var chunk_tick_timer = 0
|
|
|
|
|
var chunks = {}
|
|
|
|
|
var chunk_process_distance = 6
|
2025-12-02 01:53:58 +00:00
|
|
|
var time_since_last_atmospheric_track = 999
|
|
|
|
|
var current_atmospheric_track = null
|
2025-12-02 00:54:27 +00:00
|
|
|
|
|
|
|
|
var asteroid_scene = preload("res://scenes/asteroid.tscn")
|
2025-12-04 22:09:20 +00:00
|
|
|
var enemy_scene = preload("res://scenes/enemy.tscn")
|
2025-12-06 16:43:37 +00:00
|
|
|
var star_scene = preload("res://scenes/star.tscn")
|
|
|
|
|
|
|
|
|
|
@onready var spawn_points = {
|
2025-12-07 21:13:25 +00:00
|
|
|
"space_station_1": $Orbits/SpaceStation1/space_station_1/ExitPoint,
|
|
|
|
|
"space_station_2": $Orbits/SpaceStation2/space_station_2/ExitPoint
|
2025-12-06 16:43:37 +00:00
|
|
|
}
|
2025-12-02 00:54:27 +00:00
|
|
|
|
2025-12-06 22:03:58 +00:00
|
|
|
@onready var navigation_points = [
|
|
|
|
|
{
|
2025-12-07 21:13:25 +00:00
|
|
|
"name": "Space Station 01",
|
2025-12-06 22:03:58 +00:00
|
|
|
"id": "ss1",
|
2025-12-07 21:13:25 +00:00
|
|
|
"point": $Orbits/SpaceStation1/space_station_1
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"name": "Space Station 02",
|
|
|
|
|
"id": "ss2",
|
|
|
|
|
"point": $Orbits/SpaceStation2/space_station_2
|
2025-12-06 22:03:58 +00:00
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
|
2025-12-04 22:09:20 +00:00
|
|
|
func ship_health(health: float = 1000) -> void:
|
2025-12-03 21:54:06 +00:00
|
|
|
$Player.health = health
|
2025-12-04 22:09:20 +00:00
|
|
|
|
|
|
|
|
func summon_enemy() -> void:
|
|
|
|
|
var spawn_position = $Player.position + ($Player.transform.y * 1024)
|
|
|
|
|
|
|
|
|
|
var enemy = enemy_scene.instantiate()
|
|
|
|
|
enemy.position = spawn_position
|
|
|
|
|
|
|
|
|
|
add_child(enemy)
|
2025-12-03 21:54:06 +00:00
|
|
|
|
|
|
|
|
## Takes a world coordinate and converts it to a chunk coordinate.
|
2025-12-02 00:54:27 +00:00
|
|
|
func world_to_chunk(position: Vector2) -> Vector2:
|
|
|
|
|
return Vector2(floori(position.x / 1024), floori(position.y / 1024))
|
|
|
|
|
|
2025-12-03 21:54:06 +00:00
|
|
|
## Takes a chunk coordinate and converts it to a world coordinate.
|
2025-12-02 00:54:27 +00:00
|
|
|
func chunk_to_world(position: Vector2) -> Vector2:
|
|
|
|
|
return Vector2(floori(position.x * 1024), floori(position.y * 1024))
|
2025-12-02 21:00:10 +00:00
|
|
|
|
2025-12-07 03:06:34 +00:00
|
|
|
## Saves the game.
|
|
|
|
|
func save_game() -> void:
|
|
|
|
|
$UI/Control/SaveIndicator.visible = true
|
|
|
|
|
|
|
|
|
|
global.stats.position = {
|
|
|
|
|
"x": $Player.global_position.x,
|
|
|
|
|
"y": $Player.global_position.y
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-07 18:36:35 +00:00
|
|
|
global.stats.location = "space"
|
|
|
|
|
|
2025-12-07 03:06:34 +00:00
|
|
|
global.save_game()
|
|
|
|
|
|
|
|
|
|
await get_tree().create_timer(2).timeout
|
|
|
|
|
|
|
|
|
|
$UI/Control/SaveIndicator.visible = false
|
|
|
|
|
|
2025-12-02 21:00:10 +00:00
|
|
|
func enter_physical(map):
|
|
|
|
|
get_tree().paused = true
|
|
|
|
|
|
|
|
|
|
var i = 0
|
|
|
|
|
while i < 5:
|
|
|
|
|
$UI/FadeToBlack.modulate.a = (i/5.0)
|
|
|
|
|
|
|
|
|
|
await get_tree().create_timer(0.2).timeout
|
|
|
|
|
|
|
|
|
|
i += 1
|
|
|
|
|
|
|
|
|
|
$UI/FadeToBlack.modulate.a = 1
|
|
|
|
|
|
|
|
|
|
await get_tree().create_timer(0.4).timeout
|
|
|
|
|
|
|
|
|
|
get_tree().paused = false
|
|
|
|
|
|
2025-12-06 16:43:37 +00:00
|
|
|
global.stats.location = map
|
2025-12-02 21:00:10 +00:00
|
|
|
|
2025-12-08 02:12:30 +00:00
|
|
|
global.stats.position = null
|
|
|
|
|
|
2025-12-03 02:56:54 +00:00
|
|
|
get_tree().change_scene_to_file("res://scenes/ground.tscn")
|
2025-12-02 21:00:10 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
func _ready() -> void:
|
2025-12-04 22:09:20 +00:00
|
|
|
LimboConsole.register_command(ship_health, "ship_health", "Sets the ship's health.")
|
|
|
|
|
LimboConsole.register_command(summon_enemy, "summon_enemy", "Summons an enemy.")
|
|
|
|
|
|
2025-12-05 21:50:38 +00:00
|
|
|
var events = InputMap.action_get_events("forward")
|
2025-12-06 16:43:37 +00:00
|
|
|
|
2025-12-06 22:23:14 +00:00
|
|
|
for n in navigation_points:
|
|
|
|
|
var navigation_item = preload("res://scenes/navigation_item.tscn").instantiate()
|
|
|
|
|
|
|
|
|
|
navigation_item.goal = n
|
|
|
|
|
navigation_item.game = self
|
|
|
|
|
|
|
|
|
|
$UI/Control/Navpanel/NavigationRoutes/Scroll/Box.add_child(navigation_item)
|
|
|
|
|
|
2025-12-06 16:43:37 +00:00
|
|
|
var i = 0
|
2025-12-06 20:52:57 +00:00
|
|
|
while i < 1024 / 3:
|
2025-12-06 16:43:37 +00:00
|
|
|
var star = star_scene.instantiate()
|
|
|
|
|
star.position = Vector2(-99999999999999999, -9999999999999999999)
|
|
|
|
|
|
|
|
|
|
$Stars.add_child(star)
|
|
|
|
|
|
|
|
|
|
i += 1
|
2025-12-06 20:52:57 +00:00
|
|
|
|
|
|
|
|
i = 0
|
|
|
|
|
while i < 1024 / 3:
|
|
|
|
|
var star = star_scene.instantiate()
|
|
|
|
|
star.position = Vector2(-99999999999999999, -9999999999999999999)
|
|
|
|
|
star.parallax = 0.2
|
|
|
|
|
|
|
|
|
|
$Stars.add_child(star)
|
|
|
|
|
|
|
|
|
|
i += 1
|
|
|
|
|
|
|
|
|
|
i = 0
|
|
|
|
|
while i < 1024 / 3:
|
|
|
|
|
var star = star_scene.instantiate()
|
|
|
|
|
star.position = Vector2(-99999999999999999, -9999999999999999999)
|
|
|
|
|
star.parallax = 0.5
|
|
|
|
|
|
|
|
|
|
$Stars.add_child(star)
|
|
|
|
|
|
|
|
|
|
i += 1
|
2025-12-07 03:06:34 +00:00
|
|
|
|
2025-12-07 18:36:35 +00:00
|
|
|
if (global.stats.location != "space") and (spawn_points.has(global.stats.location)):
|
|
|
|
|
$Player.global_position = spawn_points[global.stats.location].global_position
|
|
|
|
|
$Player.rotation = spawn_points[global.stats.location].rotation
|
|
|
|
|
elif global.stats.position:
|
2025-12-07 03:06:34 +00:00
|
|
|
$Player.global_position.x = global.stats.position.x
|
|
|
|
|
$Player.global_position.y = global.stats.position.y
|
|
|
|
|
|
|
|
|
|
global.stats.position = null
|
2025-12-07 18:36:35 +00:00
|
|
|
|
|
|
|
|
global.stats.location = "space"
|
|
|
|
|
|
|
|
|
|
if global.stats.story_progress < 4: global.stats.story_progress = 4
|
|
|
|
|
|
|
|
|
|
if global.stats.loaded:
|
2025-12-07 03:06:34 +00:00
|
|
|
save_game()
|
2025-12-07 18:36:35 +00:00
|
|
|
else:
|
|
|
|
|
global.stats.loaded = true
|
2025-12-02 00:54:27 +00:00
|
|
|
|
2025-12-02 00:34:27 +00:00
|
|
|
func _process(delta: float) -> void:
|
2025-12-02 21:00:10 +00:00
|
|
|
if get_tree().paused:
|
|
|
|
|
return
|
2025-12-06 22:03:58 +00:00
|
|
|
|
|
|
|
|
if global.stats.navigation_goal:
|
2025-12-07 18:36:35 +00:00
|
|
|
if !global.stats.navigation_goal.point || (typeof(global.stats.navigation_goal.point) == TYPE_STRING):
|
|
|
|
|
if global.stats.navigation_goal.id:
|
|
|
|
|
for n in navigation_points:
|
|
|
|
|
if n.id == global.stats.navigation_goal.id:
|
|
|
|
|
global.stats.navigation_goal.point = n.point
|
|
|
|
|
break
|
2025-12-06 22:03:58 +00:00
|
|
|
else:
|
|
|
|
|
if !$Navring.has_node("NavigationMarker"):
|
|
|
|
|
var navigation_marker = preload("res://scenes/navigation_marker.tscn").instantiate()
|
|
|
|
|
|
|
|
|
|
navigation_marker.destination = global.stats.navigation_goal.point
|
|
|
|
|
|
|
|
|
|
$Navring.add_child(navigation_marker)
|
|
|
|
|
|
2025-12-06 22:53:47 +00:00
|
|
|
if $UI/Control/Navpanel/NavigationHyperboost.visible:
|
|
|
|
|
var distance_to_hyperboost = (global.stats.navigation_goal.point.global_position - $Player.global_position).length() - 3000
|
|
|
|
|
var fuel_cost = floori((distance_to_hyperboost / 4096) * 7)
|
|
|
|
|
|
|
|
|
|
$UI/Control/Navpanel/NavigationHyperboost/FuelRequired.text = "FUEL REQUIRED:\n"+ str(fuel_cost) + "%"
|
|
|
|
|
|
2025-12-06 22:03:58 +00:00
|
|
|
if $UI/Control/Navpanel/Navigation.visible:
|
|
|
|
|
|
|
|
|
|
$UI/Control/Navpanel/Navigation/CurrentRoute.visible = true
|
|
|
|
|
$UI/Control/Navpanel/Navigation/Destination.visible = true
|
|
|
|
|
|
2025-12-06 22:53:47 +00:00
|
|
|
$UI/Control/Navpanel/Navigation/Hyperboost.visible = true
|
|
|
|
|
|
2025-12-07 18:36:35 +00:00
|
|
|
if global.stats.navigation_goal.point and (typeof(global.stats.navigation_goal.point) != TYPE_STRING):
|
|
|
|
|
var goal_distance = (global.stats.navigation_goal.point.position - $Player.position).length()
|
2025-12-06 22:03:58 +00:00
|
|
|
|
2025-12-07 18:36:35 +00:00
|
|
|
$UI/Control/Navpanel/Navigation/Destination.text = global.stats.navigation_goal.name + " (" + str(floori(goal_distance)) + "u)"
|
2025-12-06 22:03:58 +00:00
|
|
|
else:
|
|
|
|
|
if $Navring.has_node("NavigationMarker"):
|
|
|
|
|
$Navring.get_node("NavigationMarker").queue_free()
|
|
|
|
|
|
|
|
|
|
if $UI/Control/Navpanel/Navigation.visible:
|
|
|
|
|
$UI/Control/Navpanel/Navigation/CurrentRoute.visible = false
|
|
|
|
|
$UI/Control/Navpanel/Navigation/Destination.visible = false
|
2025-12-06 22:53:47 +00:00
|
|
|
|
|
|
|
|
$UI/Control/Navpanel/Navigation/Hyperboost.visible = false
|
|
|
|
|
|
2025-12-06 23:15:20 +00:00
|
|
|
if $UI/Control/Navpanel/Start.visible:
|
|
|
|
|
$UI/Control/Navpanel/Start/Fuel.value = global.stats.fuel
|
|
|
|
|
$UI/Control/Navpanel/Start/Fuel/Label.text = str(roundi(global.stats.fuel)) + "%"
|
|
|
|
|
|
2025-12-06 22:53:47 +00:00
|
|
|
if $UI/Control/Navpanel/NavigationRoutes.visible:
|
|
|
|
|
for n in $UI/Control/Navpanel/NavigationRoutes/Scroll/Box.get_children():
|
|
|
|
|
n.update()
|
2025-12-02 21:00:10 +00:00
|
|
|
|
2025-12-02 19:51:40 +00:00
|
|
|
$Navring.position = $Player.position
|
|
|
|
|
|
2025-12-02 01:53:58 +00:00
|
|
|
if current_atmospheric_track:
|
|
|
|
|
if not current_atmospheric_track.playing:
|
|
|
|
|
time_since_last_atmospheric_track = 0
|
|
|
|
|
else:
|
|
|
|
|
time_since_last_atmospheric_track += delta
|
|
|
|
|
|
|
|
|
|
if time_since_last_atmospheric_track > 60:
|
|
|
|
|
var tracks = [$BalladOfTheCats, $ConcreteHalls]
|
|
|
|
|
|
2025-12-02 03:48:05 +00:00
|
|
|
current_atmospheric_track = tracks[randi_range(0, tracks.size() - 1)]
|
2025-12-02 01:53:58 +00:00
|
|
|
current_atmospheric_track.play()
|
|
|
|
|
|
2025-12-02 00:54:27 +00:00
|
|
|
chunk_tick_timer -= delta
|
|
|
|
|
|
|
|
|
|
if chunk_tick_timer < 0:
|
|
|
|
|
chunk_tick_timer = 1
|
|
|
|
|
|
|
|
|
|
var player_chunk = world_to_chunk($Player.position)
|
|
|
|
|
|
|
|
|
|
var valid_chunks = []
|
|
|
|
|
|
|
|
|
|
var x_mod = -chunk_process_distance
|
|
|
|
|
|
|
|
|
|
while x_mod <= chunk_process_distance:
|
|
|
|
|
var y_mod = -chunk_process_distance
|
|
|
|
|
|
|
|
|
|
while y_mod <= chunk_process_distance:
|
|
|
|
|
var chunk_position = Vector2(player_chunk.x - x_mod, player_chunk.y - y_mod)
|
|
|
|
|
|
|
|
|
|
valid_chunks.push_front(chunk_position)
|
|
|
|
|
|
|
|
|
|
if !chunks.has(chunk_position):
|
|
|
|
|
chunks[chunk_position] = {
|
|
|
|
|
"loaded": true,
|
|
|
|
|
"time_to_unload": 0,
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-02 01:21:10 +00:00
|
|
|
var world_position = chunk_to_world(chunk_position)
|
|
|
|
|
var orbit_zone = null
|
|
|
|
|
for n in global.orbit_zones:
|
|
|
|
|
|
|
|
|
|
if world_position.length() < n.distance:
|
|
|
|
|
continue
|
|
|
|
|
orbit_zone = n
|
|
|
|
|
|
|
|
|
|
for n in orbit_zone.spawns:
|
|
|
|
|
var i = 0
|
2025-12-02 00:54:27 +00:00
|
|
|
|
2025-12-02 01:21:10 +00:00
|
|
|
while i < n.amount_min:
|
|
|
|
|
var asteroid = asteroid_scene.instantiate()
|
2025-12-02 00:54:27 +00:00
|
|
|
|
2025-12-02 01:21:10 +00:00
|
|
|
asteroid.rotation = randf_range(-5.0, 5.0)
|
|
|
|
|
asteroid.position = chunk_to_world(chunk_position) + Vector2(randi_range(0, 1024), randi_range(0, 1024))
|
|
|
|
|
|
|
|
|
|
$Unloadables.add_child(asteroid)
|
|
|
|
|
|
|
|
|
|
i += 1
|
2025-12-02 00:54:27 +00:00
|
|
|
|
|
|
|
|
y_mod += 1
|
|
|
|
|
|
|
|
|
|
x_mod += 1
|
|
|
|
|
|
|
|
|
|
for n in chunks.keys():
|
|
|
|
|
if !valid_chunks.has(n):
|
|
|
|
|
chunks.erase(n)
|
|
|
|
|
|
|
|
|
|
for n in $Unloadables.get_children():
|
|
|
|
|
var n_pos = world_to_chunk(n.position)
|
|
|
|
|
if abs(n_pos.x - player_chunk.x) > chunk_process_distance:
|
|
|
|
|
n.queue_free()
|
|
|
|
|
elif abs(n_pos.y - player_chunk.y) > chunk_process_distance:
|
|
|
|
|
n.queue_free()
|
2025-12-06 22:03:58 +00:00
|
|
|
|
|
|
|
|
func _set_navpanel_menu(menu: String) -> void:
|
|
|
|
|
for n in $UI/Control/Navpanel.get_children():
|
|
|
|
|
if n.get_name() == menu:
|
|
|
|
|
n.visible = true
|
|
|
|
|
|
2025-12-06 22:23:14 +00:00
|
|
|
if menu == "NavigationRoutes":
|
|
|
|
|
n.get_node("Scroll/Box").get_children()[0].get_node("Button").grab_focus()
|
|
|
|
|
else:
|
|
|
|
|
for o in n.get_children():
|
|
|
|
|
if o.is_class("Button"):
|
|
|
|
|
o.grab_focus()
|
|
|
|
|
break
|
2025-12-06 22:03:58 +00:00
|
|
|
else:
|
|
|
|
|
n.visible = false
|
2025-12-05 21:50:38 +00:00
|
|
|
|
|
|
|
|
func _input(event: InputEvent) -> void:
|
|
|
|
|
if event.is_action_pressed("pause"):
|
2025-12-07 19:29:07 +00:00
|
|
|
$UiSelect.play()
|
|
|
|
|
|
2025-12-05 21:50:38 +00:00
|
|
|
$UI/Control/PauseMenu.visible = true
|
|
|
|
|
|
|
|
|
|
$UI/Control/PauseMenu/Panel/Flow/Resume.grab_focus()
|
|
|
|
|
|
|
|
|
|
get_tree().paused = true
|
2025-12-06 22:03:58 +00:00
|
|
|
elif event.is_action_pressed("navpanel"):
|
|
|
|
|
if $UI/Control/Navpanel.visible:
|
2025-12-06 22:53:47 +00:00
|
|
|
$UiBack.play()
|
|
|
|
|
|
2025-12-06 22:03:58 +00:00
|
|
|
$UI/Control/Navpanel.visible = false
|
|
|
|
|
else:
|
2025-12-06 22:53:47 +00:00
|
|
|
$UiSelect.play()
|
|
|
|
|
|
2025-12-06 22:03:58 +00:00
|
|
|
$UI/Control/Navpanel.visible = true
|
|
|
|
|
|
|
|
|
|
_set_navpanel_menu("Start")
|
2025-12-06 22:23:14 +00:00
|
|
|
elif event.is_action_pressed("ui_back"):
|
|
|
|
|
if $UI/Control/Navpanel.visible:
|
2025-12-06 22:53:47 +00:00
|
|
|
$UiBack.play()
|
2025-12-06 22:23:14 +00:00
|
|
|
|
2025-12-06 22:53:47 +00:00
|
|
|
if ($UI/Control/Navpanel/NavigationRoutes.visible) or ($UI/Control/Navpanel/NavigationHyperboost.visible):
|
2025-12-06 22:23:14 +00:00
|
|
|
_set_navpanel_menu("Navigation")
|
|
|
|
|
elif $UI/Control/Navpanel/Start.visible:
|
|
|
|
|
$UI/Control/Navpanel.visible = false
|
|
|
|
|
else:
|
|
|
|
|
_set_navpanel_menu("Start")
|
2025-12-06 20:28:33 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
func _on_tree_exiting() -> void:
|
|
|
|
|
LimboConsole.unregister_command(ship_health)
|
|
|
|
|
LimboConsole.unregister_command(summon_enemy)
|
2025-12-06 22:03:58 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
func _on_navigation_pressed() -> void:
|
|
|
|
|
$UiSelect.play()
|
|
|
|
|
_set_navpanel_menu("Navigation")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _on_navpanel_back_pressed() -> void:
|
2025-12-06 22:53:47 +00:00
|
|
|
$UiBack.play()
|
2025-12-06 22:03:58 +00:00
|
|
|
_set_navpanel_menu("Start")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _on_new_route_pressed() -> void:
|
|
|
|
|
$UiSelect.play()
|
2025-12-06 22:23:14 +00:00
|
|
|
_set_navpanel_menu("NavigationRoutes")
|
2025-12-06 22:03:58 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
func _on_cancel_route_pressed() -> void:
|
|
|
|
|
$UiSelect.play()
|
|
|
|
|
global.stats.navigation_goal = null
|
2025-12-06 22:23:14 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
func _on_quit_pressed() -> void:
|
2025-12-06 22:53:47 +00:00
|
|
|
$UiBack.play()
|
2025-12-06 22:23:14 +00:00
|
|
|
$UI/Control/Navpanel.visible = false
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _navigation_item_pressed(goal) -> void:
|
|
|
|
|
global.stats.navigation_goal = goal
|
|
|
|
|
$UiSelect.play()
|
|
|
|
|
_set_navpanel_menu("Navigation")
|
2025-12-06 22:53:47 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
func _on_navigation_back_pressed() -> void:
|
|
|
|
|
$UiBack.play()
|
|
|
|
|
_set_navpanel_menu("Navigation")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _on_hyperboost_pressed() -> void:
|
|
|
|
|
$UiSelect.play()
|
|
|
|
|
_set_navpanel_menu("NavigationHyperboost")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _on_enable_hyperboost_pressed() -> void:
|
|
|
|
|
$UiSelect.play()
|
|
|
|
|
_set_navpanel_menu("Navigation")
|
|
|
|
|
$Player.hyperboosting = true
|