ld48/src/GameState.gd

717 lines
20 KiB
GDScript3

extends Node
signal orbit_updated
signal stats_updated
signal crew_updated
# Declare member variables here. Examples:
# var a = 2
# var b = "text"
const debug = true
var current_scene = null
var difficulty = null
var difficulties = {
"Easy": 0,
"Medium": 1,
"Hard": 2,
}
var difficulty_data = {
0: {
"planet": "res://assets/moon.png",
"starting_altitude": 60000,
"starting_crew": 5,
"starting_drag": 75,
"drag_function": funcref(self, "moon_drag"),
"rescue_altitude": 10000,
"maximum_crew": 15,
"minimum_altitude": 1000,
},
1: {
"planet": "res://assets/planet.png",
"starting_altitude": 600000,
"starting_crew": 4,
"starting_drag": 100,
"drag_function": funcref(self, "planet_drag"),
"rescue_altitude": 100000,
"maximum_crew": 10,
"minimum_altitude": 30000,
},
2: {
"planet": "res://assets/blackhole.png",
"starting_altitude": 200000001000000,
"starting_crew": 3,
"starting_drag": 125,
"drag_function": funcref(self, "blackhole_drag"),
"rescue_altitude": 200000000100000,
"maximum_crew": 6,
"minimum_altitude": 200000000000000,
}
}
var current_depth = 0
const OptionsPerLevel = 3
const TreeDepth = 5
const EncounterTypes = [
"ArtificialSatellite",
"NaturalSatellite",
#"Comet",
#"Debris",
##"Gases",
#"Mystery",
#"Alien",
"Ship",
#"Station",
#"LocationSpecific", # Eg., Moon, Planet, Blackhole...
]
const EncounterData = {
# "a": {
# "tooltip": "",
# "situations": {
# 0: {
# "title": "a x",
# "picture": null,
# "text": "asdfs",
# "choices": {
# 0: {
# "title": "x",
# "tooltip": "aa",
# "then": "aa",
# "results": {
# # starting from last, if chance >= index between 0-100
# 0: {
# "title": "a",
# "text": "a",
# "stat_changes": Dictionary(),
# },
# }
# }
# }
# }
# }
# }
"RescueOperation": {
"tooltip": "A ship is stranded here, and they need your help",
"situations": {
0: {
"title": "The Diligent's Rescue",
"picture": null,
"text": "",
"choices": {
0: {
"title": "Prioritise the warp crystal",
"tooltip": "",
"then": "You shove past the haggard looking crew of the " + \
"Diligent, ignoring their arched eyebrows and sideways " + \
"glances as you hurry to the Engineering section. \n\n" + \
"The [rainbow freq=0.5 sat=2 val=200]Warp Crystal[/rainbow] " + \
"is already packed in an open carrying case. It looks like a " + \
"good one, and should last a few jumps.\n\nYou grab the case " + \
"and make haste back to the Wellspring before the Diligent " + \
"sinks further into the gravity well",
"results": {
0: {
"title": "Gaining supplies, losing morale",
"text": "",
"stat_changes": {
"supplies": 500,
},
"crew_changes": {
"morale": -50,
}
}
}
},
1: {
"title": "Prioritise the crew of the Diligent",
"then": "You rush to get all the members of the Diligent's crew " + \
"abord before the ships sink deeper into the gravity well. Your mates " + \
"just finish getting that last stretcher on board before the a beeping klaxon " + \
"starts to signal that the two ships are about to be torn away from each other.",
"tooltip": "",
"results": {
0: {
"title": "Gaining crew",
"text": "",
"stat_changes": {
"extra_crew": 5,
},
}
}
},
2: {
"title": "Try to repair the Diligent",
"tooltip": "",
"then": "Your crew works tirelessly hand-in-hand with" + \
" the Diligent's trying to make their ship fully spaceworthy " + \
"again.",
"results": {
0: {
"title": "Lose altitude",
"text": "",
"orbit_changes": {
"altitude": -1000,
"escape_dv": 100,
}
}
}
}
}
}
}
},
"ArtificialSatellite" : {
"tooltip": "",
"situations": {
0: {
"title": "Abandoned observation satellite",
"picture": null,
"text": "During the coast phase of your descent, your ship is " + \
"passing near a derelict satellite. A visual inspection shows " + \
"scorch marks and loose panel on the side",
"choices": {
0: {
"title": "Download data",
"tooltip": "",
"then": "Your wireless systems initialize a mesh connection " + \
"with the object and the data begins to stream back to your ship",
"results": {
0: {
"title": "Losing DeltaV",
"text": "The data seems to decodes itself and a malicious program " + \
"begins to run on your ship systems. It manages to fire some of the " + \
"thrusters before you can lock it down.",
"stat_changes": {
"deltav": -100,
},
},
50: {
"title": "Gaining DeltaV",
"text": "After decoding the data, it seems to be a detailed chart of " + \
"the local gravity well. This should help you fine tune the course " + \
"towards the [b]Diligent[/b]",
"stat_changes": {
"deltav": 100,
},
},
90: {
"title": "Nothing",
"text": "The data appears to be hopelessly corrupt, and you cannot glean anything from it",
},
},
},
1: {
"title": "Salvage the probe",
"tooltip": "",
"then": "Your crew attempts to haul the probe abord using a magnetic cable",
"results": {
0: {
"title": "Nothing",
"text": "The materials making up the probe are so corroded that they " + \
"are unusable and only fit for melting down",
"stat_changes": Dictionary(),
},
25: {
"title": "Gaining Supplies",
"text": "Despite evidence that somebody had tried to repair - or tamper - " + \
"with the satellite, the material composition of it yields many useful " + \
"components",
"stat_changes": {
"supplies": 100,
},
},
90: {
"title": "An event occuring",
"text": "During the cable connection, the line gets caught. " + \
"A crew member heads on EVA to try and fix it." + \
"As they near the satellite, it explodes! Their space suit is shredded and they die instantly.",
"stat_changes": {
"extra_crew": -1,
}
},
95: {
"title": "An event occuring",
"text": "During the cable connection, the line gets caught." + \
"A crew member heads on EVA to try and fix it." + \
"Nearing the satellite, they see a blinking light just under the loose panel. Fearing a bomb, they cut the cable and come back to the ship",
}
}
},
2: {
"title": "Do nothing",
"tooltip": "",
"then": "You drift silently past the satellite on way down to the Diligent",
"results": {
0: {
"title": "Nothing happening",
"text": "The satellite's collision avoidance system seems to kick in and it adjusts it's orbit away from your ship",
}
}
}
}
},
},
},
"NaturalSatellite" : {
"tooltip": "",
"situations": {
0: {
"title": "Unexpected object",
"picture": null,
"text": "The proximity sensor alarms blare suddenly, there's a rock on a collision course!",
"choices": {
0: {
"title": "Evade",
"then": "You fire the thrusters, quickly moving yourself away from the source of danger",
"tooltip": "",
"results": {
0: {
"title": "Losing DeltaV",
"text": "The sudden unplanned maneauver eats into your fuel reservers",
"stat_changes": {
"deltav": -100,
}
}
}
},
1: {
"title": "Brace for impact",
"then": "You slam the red alert button and your crew pile into their crash seats, gritting their teeth for what is to come",
"tooltip": "",
"results": {
0: {
"title": "Losing Crew and Health",
"text": "The object pierces through your hull at hundreds of meters per second, tearing flesh and steel alike. Bulkheads clamp down and sirens wail. After a few seconds, the situation stabilises and you look around, noting that not everyone made it.",
"stat_changes": {
"extra_crew": -1,
}
},
50: {
"title": "Losing health",
"text": "The object pierces through your hull at hundreds of meters per second, tearing through all manners of ships systems. Chaos reigns as emergency repair systems kick in",
"stat_changes": {
"health": -250,
}
},
}
},
}
}
}
},
"Comet" : {
"tooltip": "",
"situations": {
0: {
"title": "Extra-solar fly-by",
"picture": null,
"text": "A large comet blazes through the system, passing nearby. It leaves sparkling trails of ice and gases in it's wake",
"choices": {
0: {
"title": "Observer the comet",
"then": "You direct the majority of the sensors on your ship to observe the comet, and the crew crams in the observation deck to catch glimpses of the fly-by",
"results": {
},
},
},
},
},
},
"Debris" : {
},
#"Gases" : {},
"Mystery" : {},
"Alien" : {},
"Ship" : {
"tooltip": "",
"situations": {
0: {
"title": "Another ship",
"picture": null,
"text": "There's the quiet remanents of a ship floating through space. " + \
"The hull seems largely intact, but the lights are off.",
"choices": {
0: {
"title": "Send a boarding party",
"then": "You send over a skiff with some of your crew",
"tooltip": "",
"results": {
0: {
"title": "Gain DeltaV",
"text": "Nobody is home, but the fuel reserves are almost full",
"stat_changes": {
"deltav": 100,
}
},
25: {
"title": "Gain crew",
"text": "The away team finds cryo-stasis pods running on battery power",
"stat_changes": {
"extra_crew": 1
}
},
50: {
"title": "Nothing",
"text": "The ship is empty of people and supplies. There is a nice diamond ring though",
},
80: {
"title": "Event",
"text": "An explosion rips through the ship as the boarding skiff docks with it. All hands are lost",
"stat_changes": {
"extra_crew": -2,
}
},
90: {
"title": "Event",
"text": "The hulk is piled full of dessicated corpses dressed in white and gold vestments, bearing aquiline sigils. Disturbing the grave seems to rub your crew the wrong way.",
"crew_changes": {
"morale": -25,
}
}
}
},
2: {
"title": "Do nothing",
"tooltip": "",
"then": "You drift silently past the ship on way down to the Diligent",
"results": {
0: {
"title": "Nothing happening",
"text": "The crew looks on through sensors and portholes at the diminishing spec that may have once been a refuge in the void to some souls, now quiet.",
}
}
}
},
}
}
},
"Station" : {},
"LocationSpecific" : {},
}
var rng = null
var encounters = {
# 0: [
# {
# "type": EncounterTypeString,
# "instance": EncounterNode,
# "selected": Boolean (false),
# "visited": Boolean (false),
# }, ...
# ], ...
}
var current_state = 0
var states = {
0: "Choose Difficulty",
1: "Generate Map",
2: "Intro",
3: "Set Resources and Generate Crew",
4: "Choose flight-path In",
5: "Encounters",
6: "Going Deeper",
7: "Choose flight-path Out",
8: "Encounters 2",
9: "End Game",
}
const default_ship_stats = {
"health": 1000,
"deltav": 1000,
"supplies": 500,
"crew": [],
"next_crew_id": 2,
"target_position": Vector2(0, 0),
"starting_position": Vector2(0, 0),
}
var ship_stats = {}
const default_orbit_stats = {
"altitude": 0,
"drag": 0,
"record_low": 0,
"escape_dv": 0,
}
var orbit_stats = {}
# Called when the node enters the scene tree for the first time.
func _ready():
self.rng = RandomNumberGenerator.new()
self.rng.randomize()
func reset_to_new():
var d = difficulty
self.reset()
self.initialize(d)
self.current_state = 4
goto_scene("res://src/ui/ChooseFlightIn.tscn")
func reset_to_main_menu():
goto_scene("res://src/ui/MainMenu.tscn")
self.reset()
func reset():
# @TODO: do we need to free the encounter or crew instances? I don't think so,
# since they are part of the tree that's freed when the scene is changed...
ship_stats = Dictionary()
orbit_stats = Dictionary()
# orbit stats: probably not necessary
self.current_state = 0
self.encounters = {}
self.current_depth = 0
self.difficulty = null
var bg = get_tree().get_root().get_node("Background")
if bg:
bg.free()
var end = get_tree().get_root().find_node("End")
if end:
end.free()
func initialize(d):
self.difficulty = d
for k in default_ship_stats.keys():
ship_stats[k] = default_ship_stats[k]
ship_stats['crew'] = Array()
for k in default_orbit_stats.keys():
orbit_stats[k] = default_orbit_stats[k] # Generate a map
var i = 0
while i < self.TreeDepth:
var n = 0
self.encounters[i] = []
while n < self.OptionsPerLevel:
var type = self.EncounterTypes[self.rng.randi_range(0, len(self.EncounterTypes)-1)]
var encounter = {
"type": type,
"instance": null,
"visited": false,
"selected": false,
"locked": false,
}
self.encounters[i].append(encounter)
n += 1
i += 1
self.encounters[i] = []
self.encounters[i].append({
"type": "RescueOperation",
"instance": null,
"visited": false,
"selected": true,
"locked": true,
})
for level in encounters.keys():
print("Encounter level " + str(level))
for e in self.encounters[level]:
print("\tOption: " + e.type)
# Set the orbit stats
self.orbit_stats["altitude"] = get_difficulty_data("starting_altitude")
self.orbit_stats["drag"] = get_difficulty_data("starting_drag")
self.orbit_stats["record_low"] = get_difficulty_data("starting_altitude")
self.generate_crew()
get_tree().get_root().call_deferred("add_child", ResourceLoader.load("res://src/ui/Background.tscn").instance())
func get_difficulty_data(key):
return self.difficulty_data[self.difficulty][key]
func generate_crew():
self.ship_stats["crew"].append({
"name": "Wellspring's Captain",
"may_be_sacrified": false,
"picture": null,
"personality": "paragon",
"position": null,
"morale": null,
"tooltip": "This is you",
"upkeep": 10,
"id": 1,
})
var x = 0
while x < self.get_difficulty_data("starting_crew"):
self.add_random_crew_member()
x += 1
func add_random_crew_member():
self.ship_stats["crew"].append({
"name": "Xxx",
"may_be_sacrified": true,
"picture": null,
"position": null,
"upkeep": 10,
"morale": 100,
"tooltip": null,
"personality": null,
"id": self.ship_stats['next_crew_id'],
})
self.ship_stats['next_crew_id'] += 1
func generate_encounter_instance(data, level = null, index = null):
var instance = null
if index == null:
index = 0
var source_data = self.EncounterData[data[index]["type"]]
var situation = self.rng.randi_range(0, len(source_data["situations"])-1)
instance = ResourceLoader.load("res://src/ui/Encounter.tscn").instance()
instance.initialize(source_data["situations"][situation])
if level != null and index != null:
self.encounters[level][index]["instance"] = instance
return instance
func goto_scene(scene, current = null, free = true):
call_deferred("_deferred_goto_scene", scene, current, free)
func _deferred_goto_scene(scene, current = null, free = true):
get_tree().get_root().print_tree_pretty()
if current:
if free:
current.free()
else:
current.set_visible(false)
elif self.current_scene:
if free:
self.current_scene.free()
else:
self.current_scene.set_visible(false)
var next = ResourceLoader.load(scene).instance()
get_tree().get_root().add_child(next)
get_tree().set_current_scene(next)
self.current_scene = next
func start_introduction():
self.current_state = 2
self.goto_scene("res://src/ui/Intro.tscn")
func end_introduction():
self.current_state = 4
self.goto_scene("res://src/ui/ChooseFlightIn.tscn")
func start_encounters(picks):
var x = 0
while x < len(self.encounters):
self.encounters[x][picks[x]]["selected"] = true
self.encounters[x][picks[x]]["locked"] = true
x += 1
if self.current_state < 5:
self.current_state = 5
self.goto_scene("res://src/ui/Encounters.tscn")
if self.current_state > 5:
self.current_state = 8
self.goto_scene("res://src/ui/Encounters.tscn", null, false)
func choose_flightpath_out():
GameState.current_state = 7
#get_tree().get_root().print_tree_pretty()
self.ship_stats['starting_position'] = get_tree().get_root().get_node("Encounters/Map/Ship").get_global_position()
self.encounters.erase(GameState.current_depth)
GameState.current_depth -= 1
self.goto_scene("res://src/ui/ChooseFlightIn.tscn", null, false)
func update_stats(d):
for k in d.keys():
if k == "extra_crew":
var max_crew = get_difficulty_data("maximum_crew")
var crew_to_add = d[k]
var x = 0
var modif = false
if crew_to_add > 0:
if d[k] + len(ship_stats['crew']) > max_crew:
crew_to_add = max_crew - len(ship_stats['crew'])
while x < crew_to_add:
self.add_random_crew_member()
modif = true
x += 1
elif crew_to_add < 0:
print("Needing to lose crew")
while crew_to_add < 0 and len(ship_stats['crew']) > 0:
ship_stats['crew'].pop_back()
print("Lost crew")
modif = true
crew_to_add += 1
self.update_crew({"morale": -5})
if len(ship_stats['crew']) == 0:
self.end(false, "Everybody died")
if modif:
emit_signal("crew_updated")
continue
if d[k] == null:
continue
if d[k] is Vector2:
ship_stats[k] = d[k]
else:
print("Stat change for " + k)
print("\tOld: " + str(ship_stats[k]))
print("\tNew: " + str(ship_stats[k] + d[k]))
ship_stats[k] += d[k]
if ship_stats['deltav'] <= 0:
self.end(false, "Ran out of fuel")
if ship_stats['health'] <= 0:
self.end(false, "Your ship fell apart")
if ship_stats['supplies'] <= 0:
self.end(false, "You ran out of supplies")
if ship_stats['supplies'] > 1000:
ship_stats['supplies'] = 1000;
if ship_stats['deltav'] > 1000:
ship_stats['deltav'] = 1000
if ship_stats['health'] > 1000:
ship_stats['health'] = 1000
emit_signal("stats_updated")
func get_supply_upkeep():
var upkeep = 0
var x = 0
while x < len(ship_stats['crew']):
upkeep += ship_stats['crew'][x]['upkeep']
#if ship_stats['crew'][x]['morale'] != null:
# upkeep += ship_stats['crew'][x]['upkeep'] *
x += 1
return upkeep
func update_orbit(d):
for k in d.keys():
if k == "altitude":
var old = orbit_stats[k]
if (orbit_stats[k] + d[k]) < orbit_stats[k]:
orbit_stats["record_low"] = orbit_stats[k] + d[k]
if d[k] is Vector2:
orbit_stats[k] = d[k]
else:
print("Orbit change for " + k)
print("\tOld: " + str(orbit_stats[k]))
print("\tNew: " + str(orbit_stats[k] + d[k]))
orbit_stats[k] += d[k]
emit_signal("orbit_updated")
func update_crew(d):
var c = len(ship_stats['crew'])-1
while c >= 0:
for k in d.keys():
if ship_stats['crew'][c][k] != null:
ship_stats['crew'][c][k] += d[k]
if ship_stats['crew'][c][k] <= 0:
print("Crew #" + str(c) + " has taken a walk on the wrong side of the hull due to low " + k)
ship_stats['crew'].remove(c)
c -= 1
emit_signal("crew_updated")
func end(win_or_fail, reason):
# Load the dialog
print("ending: " + str(win_or_fail) + " " + reason)
var e = ResourceLoader.load("res://src/ui/End.tscn").instance()
e.initialize(win_or_fail, reason)
self.add_child(e)
e.popup_centered()
self.current_state = 9
# Called every frame. 'delta' is the elapsed time since the previous frame.
#func _process(delta):
# pass