Initial commit

This commit is contained in:
2024-02-08 22:16:58 +01:00
commit 89e97a200c
524 changed files with 15626 additions and 0 deletions

54
singletons/Dialogs.gd Normal file
View File

@@ -0,0 +1,54 @@
extends Node
"""
This is the Dialogs system. Any object can send text to it by doing Dialogs.show_dialog(text, speaker)
Before using it 'dialog_box' should be set to some node that implements the following
signal dialog_started
signal dialog_ended
func show_dialog(text, speaker)
This script will connect to those signals and use them to set 'active' to true or false and forward them to other nodes,
so they can react to the dialog system being active(showing dialog) or inactive
Calls to show_dialog will be forwarded to the dialog_box which is free to implement them in any way (showing the text on screen,
using text to speech, etc)
"""
signal dialog_started
signal dialog_ended
var active = false
var dialog_box = null: set = _set_dialog_box
func show_dialog(text:String, speaker:String):
if is_instance_valid(dialog_box):
dialog_box.show_dialog(text, speaker)
func _set_dialog_box(node):
if not node is Node:
push_error("provided node doesn't extend Node")
return
dialog_box = node
if dialog_box.get_script().has_script_signal("dialog_started"):
dialog_box.dialog_started.connect(_on_dialog_started)
else:
push_error("provided node doesn't implement dialog_started signal")
if dialog_box.get_script().has_script_signal("dialog_ended"):
dialog_box.dialog_ended.connect(_on_dialog_ended)
else:
push_error("provided node doesn't implement dialog_started signal")
pass
func _on_dialog_started():
active = true
emit_signal("dialog_started")
func _on_dialog_ended():
active = false
emit_signal("dialog_ended")

63
singletons/Globals.gd Normal file
View File

@@ -0,0 +1,63 @@
extends Node
# warning-ignore:unused_class_variable
var spawnpoint = ""
var current_level = ""
func _ready():
RenderingServer.set_default_clear_color(Color.WHITE)
"""
Really simple save file implementation. Just saving some variables to a dictionary
"""
func save_game():
var savefile = FileAccess.open("user://savegame.save", FileAccess.WRITE)
var save_dict = {}
save_dict.spawnpoint = spawnpoint
save_dict.current_level = current_level
save_dict.inventory = Inventory.list()
save_dict.quests = Quest.get_quest_list()
savefile.store_line(JSON.stringify(save_dict))
savefile.close()
pass
"""
If check_only is true it will only check for a valid save file and return true or false without
restoring any data
"""
func load_game(check_only=false):
if not FileAccess.file_exists("user://savegame.save"):
return false
var savefile = FileAccess.open("user://savegame.save", FileAccess.READ)
var test_json_conv = JSON.new()
test_json_conv.parse(savefile.get_line())
var save_dict = test_json_conv.get_data()
if typeof(save_dict) != TYPE_DICTIONARY:
return false
if not check_only:
_restore_data(save_dict)
savefile.close()
return true
"""
Restores data from the JSON dictionary inside the save files
"""
func _restore_data(save_dict):
# JSON numbers are always parsed as floats. In this case we need to turn them into ints
for key in save_dict.quests:
save_dict.quests[key] = int(save_dict.quests[key])
Quest.quest_list = save_dict.quests
# JSON numbers are always parsed as floats. In this case we need to turn them into ints
for key in save_dict.inventory:
save_dict.inventory[key] = int(save_dict.inventory[key])
Inventory.inventory = save_dict.inventory
spawnpoint = save_dict.spawnpoint
current_level = save_dict.current_level
pass

45
singletons/Inventory.gd Normal file
View File

@@ -0,0 +1,45 @@
extends Node
"""
Minimal inventory system implementation.
It's just a dictionary where items are identified by a string key and hold an int amount
"""
# action can be 'added' some amount of some items is added and 'removed' when some amount
# of some item is removed
signal item_changed(action, type, amount)
var inventory = {}
func get_item(type:String) -> int:
if inventory.has(type):
return inventory[type]
else:
return 0
func add_item(type:String, amount:int) -> bool:
if inventory.has(type):
inventory[type] += amount
emit_signal("item_changed", "added", type, amount)
return true
else:
inventory[type] = amount
emit_signal("item_changed", "added", type, amount)
return true
func remove_item(type:String, amount:int) -> bool:
if inventory.has(type) and inventory[type] >= amount:
inventory[type] -= amount
if inventory[type] == 0:
inventory.erase(type)
emit_signal("item_changed", "removed", type, amount)
return true
else:
return false
func list() -> Dictionary:
return inventory.duplicate()

34
singletons/Music.gd Normal file
View File

@@ -0,0 +1,34 @@
extends Node
"""
Music singleton that handles crossfading when a new song starts
and applies a low pass filter when the game is paused. Nothing too wise
"""
var current_track = ""
var music_bus
func _ready():
music_bus = AudioServer.get_bus_index($A.bus)
func play(stream):
if current_track == "a":
$B.stream = load(stream)
$anims.play("AtoB")
current_track = "b"
else:
$A.stream = load(stream)
$anims.play("BtoA")
current_track = "a"
# Simple 'muffled music' effect on pause using a low pass filter
func _notification(what):
if what == NOTIFICATION_PAUSED:
AudioServer.set_bus_effect_enabled(music_bus,0,true)
AudioServer.set_bus_volume_db(music_bus,-10)
elif what == NOTIFICATION_UNPAUSED:
AudioServer.set_bus_effect_enabled(music_bus,0,false)
AudioServer.set_bus_volume_db(music_bus,0)

130
singletons/Music.tscn Normal file
View File

@@ -0,0 +1,130 @@
[gd_scene load_steps=6 format=3 uid="uid://d2sehpndnpyg5"]
[ext_resource type="Script" path="res://singletons/Music.gd" id="1_wold4"]
[sub_resource type="Animation" id="2"]
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("A:volume_db")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0, 1),
"transitions": PackedFloat32Array(1, 1),
"update": 0,
"values": [0.0, -30.0]
}
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath("B:volume_db")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
"times": PackedFloat32Array(0, 1),
"transitions": PackedFloat32Array(1, 1),
"update": 0,
"values": [-30.0, 0.0]
}
tracks/2/type = "value"
tracks/2/imported = false
tracks/2/enabled = true
tracks/2/path = NodePath("A:playing")
tracks/2/interp = 1
tracks/2/loop_wrap = true
tracks/2/keys = {
"times": PackedFloat32Array(1),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [false]
}
tracks/3/type = "value"
tracks/3/imported = false
tracks/3/enabled = true
tracks/3/path = NodePath("B:playing")
tracks/3/interp = 1
tracks/3/loop_wrap = true
tracks/3/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [true]
}
[sub_resource type="Animation" id="3"]
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("A:volume_db")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0, 1),
"transitions": PackedFloat32Array(1, 1),
"update": 0,
"values": [-30.0, 0.0]
}
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath("B:volume_db")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
"times": PackedFloat32Array(0, 1),
"transitions": PackedFloat32Array(1, 1),
"update": 0,
"values": [0.0, -30.0]
}
tracks/2/type = "value"
tracks/2/imported = false
tracks/2/enabled = true
tracks/2/path = NodePath("B:playing")
tracks/2/interp = 1
tracks/2/loop_wrap = true
tracks/2/keys = {
"times": PackedFloat32Array(1),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [false]
}
tracks/3/type = "value"
tracks/3/imported = false
tracks/3/enabled = true
tracks/3/path = NodePath("A:playing")
tracks/3/interp = 1
tracks/3/loop_wrap = true
tracks/3/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [true]
}
[sub_resource type="Animation" id="4"]
resource_name = "pause"
length = 0.5
[sub_resource type="AnimationLibrary" id="AnimationLibrary_y1x2n"]
_data = {
"AtoB": SubResource("2"),
"BtoA": SubResource("3"),
"pause": SubResource("4")
}
[node name="Music" type="Node"]
process_mode = 3
script = ExtResource("1_wold4")
[node name="A" type="AudioStreamPlayer" parent="."]
bus = &"Music"
[node name="B" type="AudioStreamPlayer" parent="."]
volume_db = -30.0
bus = &"Music"
[node name="anims" type="AnimationPlayer" parent="."]
libraries = {
"": SubResource("AnimationLibrary_y1x2n")
}

73
singletons/Quest.gd Normal file
View File

@@ -0,0 +1,73 @@
extends Node
"""
Minimal quest system implementation.
A dictionary where each string key represents a quest and an int value represanting a status
"""
enum STATUS { NONEXISTENT, STARTED, COMPLETE, FAILED }
# Emitted whenever a quests changes. It'll pass the quest name and new status
signal quest_changed(quest_name, status)
var quest_list = {}
# Get the status of a quest. If it's not found it returns STATUS.NONEXISTENT
func get_status(quest_name:String) -> int:
if quest_list.has(quest_name):
return quest_list[quest_name]
else:
return STATUS.NONEXISTENT
func get_status_as_text(quest_name:String) -> int:
var status = get_status(quest_name)
return STATUS.keys()[status]
# Change the state of some quest. status should be Quests.STATUS.<some status>
func change_status(quest_name:String, status:int) -> bool:
if quest_list.has(quest_name):
quest_list[quest_name] = status
emit_signal("quest_changed", quest_name, status)
return true
else:
return false
# Start a new quest
func accept_quest(quest_name:String) -> bool:
if quest_list.has(quest_name):
return false
else:
quest_list[quest_name] = STATUS.STARTED
emit_signal("quest_changed", quest_name, STATUS.STARTED)
return true
# List all the quest in a certain status
func list(status:int) -> Array:
if status == -1:
return quest_list.keys()
var result = []
for quest in quest_list.keys():
if quest_list[quest] == status:
result.append(quest)
return result
func get_quest_list() -> Dictionary:
return quest_list.duplicate()
# Remove a quest from the list of quests
func remove_quest(quest_name:String) -> bool:
if quest_list.has(quest_name):
quest_list.erase(quest_name)
emit_signal("quest_changed", quest_name, STATUS.NONEXISTENT)
return true
else:
return false