diff --git a/Fonts/Qubi.ttf b/Fonts/Qubi.ttf new file mode 100644 index 0000000..ce2e10a Binary files /dev/null and b/Fonts/Qubi.ttf differ diff --git a/Fonts/Qubi.ttf.import b/Fonts/Qubi.ttf.import new file mode 100644 index 0000000..e704151 --- /dev/null +++ b/Fonts/Qubi.ttf.import @@ -0,0 +1,40 @@ +[remap] + +importer="font_data_dynamic" +type="FontFile" +uid="uid://byubry1acvlug" +path="res://.godot/imported/Qubi.ttf-ef2072390941155281c267608c0b0094.fontdata" + +[deps] + +source_file="res://Fonts/Qubi.ttf" +dest_files=["res://.godot/imported/Qubi.ttf-ef2072390941155281c267608c0b0094.fontdata"] + +[params] + +Rendering=null +antialiasing=1 +generate_mipmaps=false +disable_embedded_bitmaps=true +multichannel_signed_distance_field=false +msdf_pixel_range=8 +msdf_size=48 +allow_system_fallback=true +force_autohinter=false +hinting=0 +subpixel_positioning=1 +oversampling=0.0 +Fallbacks=null +fallbacks=[] +Compress=null +compress=true +preload=[{ +"chars": [], +"glyphs": [], +"name": "New Configuration", +"size": Vector2i(16, 0), +"variation_embolden": 0.0 +}] +language_support={} +script_support={} +opentype_features={} diff --git a/Fonts/Qubio.ttf b/Fonts/Qubio.ttf new file mode 100644 index 0000000..fb02072 Binary files /dev/null and b/Fonts/Qubio.ttf differ diff --git a/Fonts/Qubio.ttf.import b/Fonts/Qubio.ttf.import new file mode 100644 index 0000000..09a20b5 --- /dev/null +++ b/Fonts/Qubio.ttf.import @@ -0,0 +1,34 @@ +[remap] + +importer="font_data_dynamic" +type="FontFile" +uid="uid://chmfli1xm8oe1" +path="res://.godot/imported/Qubio.ttf-32f8479b52e99fe78535ddaada4aa1d7.fontdata" + +[deps] + +source_file="res://Fonts/Qubio.ttf" +dest_files=["res://.godot/imported/Qubio.ttf-32f8479b52e99fe78535ddaada4aa1d7.fontdata"] + +[params] + +Rendering=null +antialiasing=1 +generate_mipmaps=false +disable_embedded_bitmaps=true +multichannel_signed_distance_field=false +msdf_pixel_range=8 +msdf_size=48 +allow_system_fallback=true +force_autohinter=false +hinting=0 +subpixel_positioning=1 +oversampling=0.0 +Fallbacks=null +fallbacks=[] +Compress=null +compress=true +preload=[] +language_support={} +script_support={} +opentype_features={} diff --git a/Fonts/Theme.tres b/Fonts/Theme.tres new file mode 100644 index 0000000..267a889 --- /dev/null +++ b/Fonts/Theme.tres @@ -0,0 +1,7 @@ +[gd_resource type="Theme" load_steps=2 format=3 uid="uid://ck7603ob4gflc"] + +[ext_resource type="FontFile" uid="uid://byubry1acvlug" path="res://Fonts/Qubi.ttf" id="1_y7uny"] + +[resource] +default_font = ExtResource("1_y7uny") +default_font_size = 8 diff --git a/Main.tscn b/Main.tscn new file mode 100644 index 0000000..bf09a1d --- /dev/null +++ b/Main.tscn @@ -0,0 +1,3 @@ +[gd_scene format=3 uid="uid://bs6ojoud4mvb8"] + +[node name="Main" type="Node2D"] diff --git a/addons/laia_highlighter/plugin.cfg b/addons/laia_highlighter/plugin.cfg new file mode 100644 index 0000000..fc0bffb --- /dev/null +++ b/addons/laia_highlighter/plugin.cfg @@ -0,0 +1,7 @@ +[plugin] + +name="Laia Highlighter" +description="A highlighter to highting laia scripting files" +author="Ategon" +version="v1.0.0" +script="plugin.gd" diff --git a/addons/laia_highlighter/plugin.gd b/addons/laia_highlighter/plugin.gd new file mode 100644 index 0000000..03061d2 --- /dev/null +++ b/addons/laia_highlighter/plugin.gd @@ -0,0 +1,81 @@ +@tool +extends EditorPlugin + + +var dialogue_highlighter:DialogueHighlighter + + +func _enter_tree() -> void: + dialogue_highlighter = DialogueHighlighter.new() + var script_editor = EditorInterface.get_script_editor() + print(script_editor) + print(dialogue_highlighter) + script_editor.register_syntax_highlighter(dialogue_highlighter) + print(script_editor.get_current_script()) + print(script_editor.get_open_scripts()) + script_editor.goto_line(20) + + +func _exit_tree() -> void: + if is_instance_valid(dialogue_highlighter): + var script_editor = EditorInterface.get_script_editor() + script_editor.unregister_syntax_highlighter(dialogue_highlighter) + dialogue_highlighter = null + + +class DialogueHighlighter extends EditorSyntaxHighlighter: + func _get_name() -> String: + return "Laia" + + func _get_supported_languages() -> PackedStringArray: + return ["TextFile"] + + func _get_line_syntax_highlighting(line: int) -> Dictionary: + var color_map = {} + var text_editor = get_text_edit() + var str = text_editor.get_line(line) + + # Comment + if str.strip_edges().begins_with("#"): + color_map[0] = { "color": Color.WEB_GRAY } + return color_map + + # Key + var regex3 = RegEx.new() + regex3.compile("([a-zA-Z0-9_]+:).*") + var result3 = regex3.search(str) + + if result3: + color_map[result3.get_start(1)] = { "color": Color.SEA_GREEN } + color_map[result3.get_end(1)] = { "color": Color.WHITE } + + # Array + var regex = RegEx.new() + regex.compile("([a-zA-Z0-9_\\-])+\\[\\]") + var result = regex.search(str) + + if result: + color_map[result.get_start()] = { "color": Color.CYAN } + color_map[result.get_end(1)] = { "color": Color.LIGHT_CYAN } + + # Enum + var regex4 = RegEx.new() + regex4.compile("[a-zA-Z0-9_]+(\\.[a-zA-Z0-9_]+)") + var result4 = regex4.search(str) + + if result4: + color_map[result4.get_start()] = { "color": Color.ORANGE } + color_map[result4.get_start(1)] = { "color": Color.ORANGE_RED } + color_map[result4.get_end()] = { "color": Color.WHITE } + + # Color + var regex2 = RegEx.new() + regex2.compile("#[a-zA-Z0-9]+") + var result2 = regex2.search(str) + + if result2: + color_map[result2.get_start()] = { "color": Color(result2.get_string()) } + + + + return color_map diff --git a/components/Achievements/Achievements.tscn b/components/Achievements/Achievements.tscn new file mode 100644 index 0000000..c36b88f --- /dev/null +++ b/components/Achievements/Achievements.tscn @@ -0,0 +1,164 @@ +[gd_scene load_steps=11 format=3 uid="uid://c8nsgn4idu8ay"] + +[ext_resource type="Script" path="res://components/Achievements/achievements.gd" id="1_fj2tg"] +[ext_resource type="Texture2D" uid="uid://dat440dg86mjl" path="res://components/Achievements/back.png" id="2_1q85v"] +[ext_resource type="Theme" uid="uid://ck7603ob4gflc" path="res://Fonts/Theme.tres" id="2_p5tbu"] +[ext_resource type="PackedScene" uid="uid://dykc1mgg5uopw" path="res://components/Cursor/MouseHandler.tscn" id="2_vij8n"] +[ext_resource type="Texture2D" uid="uid://bypm21lqwg7g0" path="res://components/Achievements/Village 6.png" id="4_1mson"] +[ext_resource type="Texture2D" uid="uid://ur6oiwg6ksns" path="res://components/Achievements/award.svg" id="4_7grt2"] +[ext_resource type="Texture2D" uid="uid://dcj0xniur7dk1" path="res://components/Achievements/thumbnail-mask.png" id="6_dwjjq"] +[ext_resource type="AudioStream" uid="uid://dxac8e02mctgp" path="res://components/Achievements/achievement.wav" id="8_74gug"] +[ext_resource type="AudioStream" uid="uid://4m4dwlrv78l" path="res://components/Achievements/achievement-back.ogg" id="9_c3xir"] + +[sub_resource type="RectangleShape2D" id="RectangleShape2D_yajw4"] +size = Vector2(180, 32) + +[node name="Achievements" type="Node"] +script = ExtResource("1_fj2tg") + +[node name="CanvasLayer" type="CanvasLayer" parent="."] + +[node name="Popup" type="Control" parent="CanvasLayer"] +layout_mode = 3 +anchors_preset = 3 +anchor_left = 1.0 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = -140.0 +offset_top = -42.0 +offset_right = 40.0 +offset_bottom = -12.0 +grow_horizontal = 0 +grow_vertical = 0 + +[node name="MouseHandler" parent="CanvasLayer/Popup" instance=ExtResource("2_vij8n")] +position = Vector2(62, 15) + +[node name="CollisionShape2D" type="CollisionShape2D" parent="CanvasLayer/Popup/MouseHandler"] +position = Vector2(29, 0) +shape = SubResource("RectangleShape2D_yajw4") + +[node name="Shadow" type="TextureRect" parent="CanvasLayer/Popup"] +modulate = Color(0, 0, 0, 1) +show_behind_parent = true +layout_mode = 1 +anchors_preset = 3 +anchor_left = 1.0 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = -178.0 +offset_top = -29.0 +offset_right = 2.0 +offset_bottom = 2.0 +grow_horizontal = 0 +grow_vertical = 0 +size_flags_horizontal = 8 +size_flags_vertical = 8 +theme = ExtResource("2_p5tbu") +texture = ExtResource("2_1q85v") +expand_mode = 1 + +[node name="Popup" type="TextureRect" parent="CanvasLayer/Popup"] +clip_children = 2 +layout_mode = 1 +anchors_preset = 6 +anchor_left = 1.0 +anchor_top = 0.5 +anchor_right = 1.0 +anchor_bottom = 0.5 +offset_left = -180.0 +offset_top = -15.0 +offset_bottom = 15.0 +grow_horizontal = 0 +grow_vertical = 2 +size_flags_horizontal = 8 +size_flags_vertical = 8 +texture = ExtResource("2_1q85v") +expand_mode = 1 + +[node name="Title" type="RichTextLabel" parent="CanvasLayer/Popup/Popup"] +modulate = Color(0.752941, 0.478431, 0.129412, 1) +layout_mode = 1 +anchors_preset = 4 +anchor_top = 0.5 +anchor_bottom = 0.5 +offset_left = 46.0 +offset_top = -11.0 +offset_right = 217.0 +offset_bottom = 7.0 +grow_vertical = 2 +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +theme_override_font_sizes/normal_font_size = 7 +text = "Achievement Text" + +[node name="Body" type="RichTextLabel" parent="CanvasLayer/Popup/Popup"] +layout_mode = 1 +anchors_preset = 4 +anchor_top = 0.5 +anchor_bottom = 0.5 +offset_left = 37.0 +offset_top = 2.0 +offset_right = 223.0 +offset_bottom = 18.0 +grow_vertical = 2 +theme_override_colors/default_color = Color(0.607843, 0.607843, 0.607843, 1) +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +theme_override_font_sizes/normal_font_size = 7 +text = "Achievement Body" + +[node name="AwardShadow" type="TextureRect" parent="CanvasLayer/Popup/Popup"] +modulate = Color(0, 0, 0, 1) +layout_mode = 0 +offset_left = 36.0 +offset_top = 6.0 +offset_right = 45.0 +offset_bottom = 15.0 +texture = ExtResource("4_7grt2") +expand_mode = 2 + +[node name="Award" type="TextureRect" parent="CanvasLayer/Popup/Popup"] +modulate = Color(0.752941, 0.478431, 0.129412, 1) +layout_mode = 0 +offset_left = 34.5 +offset_top = 4.5 +offset_right = 43.5 +offset_bottom = 13.5 +texture = ExtResource("4_7grt2") +expand_mode = 2 + +[node name="ThumbnailMask" type="TextureRect" parent="CanvasLayer/Popup"] +clip_children = 1 +layout_mode = 1 +anchors_preset = 4 +anchor_top = 0.5 +anchor_bottom = 0.5 +offset_left = 3.0 +offset_top = -12.0 +offset_right = 32.0 +offset_bottom = 12.0 +grow_vertical = 2 +texture = ExtResource("6_dwjjq") +expand_mode = 1 + +[node name="Thumbnail" type="TextureRect" parent="CanvasLayer/Popup/ThumbnailMask"] +layout_mode = 0 +offset_right = 29.0 +offset_bottom = 24.0 +texture = ExtResource("4_1mson") +expand_mode = 1 + +[node name="AudioStreamPlayer" type="AudioStreamPlayer" parent="."] +stream = ExtResource("8_74gug") +volume_db = -9.043 + +[node name="AudioStreamPlayer2" type="AudioStreamPlayer" parent="."] +stream = ExtResource("9_c3xir") +volume_db = -17.441 + +[connection signal="clicked" from="CanvasLayer/Popup/MouseHandler" to="." method="_on_mouse_handler_clicked"] +[connection signal="hovered" from="CanvasLayer/Popup/MouseHandler" to="." method="_on_mouse_handler_hovered"] +[connection signal="unhovered" from="CanvasLayer/Popup/MouseHandler" to="." method="_on_mouse_handler_unhovered"] + +[editable path="CanvasLayer/Popup/MouseHandler"] diff --git a/components/Achievements/Village 6.png b/components/Achievements/Village 6.png new file mode 100644 index 0000000..4ad409e Binary files /dev/null and b/components/Achievements/Village 6.png differ diff --git a/components/Achievements/Village 6.png.import b/components/Achievements/Village 6.png.import new file mode 100644 index 0000000..b6bef0a --- /dev/null +++ b/components/Achievements/Village 6.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bypm21lqwg7g0" +path="res://.godot/imported/Village 6.png-38dba7a44eb73e6643b934b25a9dbbf9.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://components/Achievements/Village 6.png" +dest_files=["res://.godot/imported/Village 6.png-38dba7a44eb73e6643b934b25a9dbbf9.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/components/Achievements/achievement-back.ogg b/components/Achievements/achievement-back.ogg new file mode 100644 index 0000000..89ff71d Binary files /dev/null and b/components/Achievements/achievement-back.ogg differ diff --git a/components/Achievements/achievement-back.ogg.import b/components/Achievements/achievement-back.ogg.import new file mode 100644 index 0000000..9a6ae42 --- /dev/null +++ b/components/Achievements/achievement-back.ogg.import @@ -0,0 +1,19 @@ +[remap] + +importer="oggvorbisstr" +type="AudioStreamOggVorbis" +uid="uid://4m4dwlrv78l" +path="res://.godot/imported/achievement-back.ogg-e15f114c4307f4746154a747d47f126e.oggvorbisstr" + +[deps] + +source_file="res://components/Achievements/achievement-back.ogg" +dest_files=["res://.godot/imported/achievement-back.ogg-e15f114c4307f4746154a747d47f126e.oggvorbisstr"] + +[params] + +loop=false +loop_offset=0 +bpm=0 +beat_count=0 +bar_beats=4 diff --git a/components/Achievements/achievement.wav b/components/Achievements/achievement.wav new file mode 100644 index 0000000..e426e74 Binary files /dev/null and b/components/Achievements/achievement.wav differ diff --git a/components/Achievements/achievement.wav.import b/components/Achievements/achievement.wav.import new file mode 100644 index 0000000..7dbdd07 --- /dev/null +++ b/components/Achievements/achievement.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://dxac8e02mctgp" +path="res://.godot/imported/achievement.wav-eb6c9e09c6c75764f5cf65335ad5f16a.sample" + +[deps] + +source_file="res://components/Achievements/achievement.wav" +dest_files=["res://.godot/imported/achievement.wav-eb6c9e09c6c75764f5cf65335ad5f16a.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=0 diff --git a/components/Achievements/achievements.gd b/components/Achievements/achievements.gd new file mode 100644 index 0000000..056d2f5 --- /dev/null +++ b/components/Achievements/achievements.gd @@ -0,0 +1,261 @@ +extends Base +## Achievement handler to check and unlock achievements +## +## An achievement handler that will check the available achievements defined in +## both the game files and the mods. When called with triggers will check to see +## if that trigger value unlocks achievements. + +signal achievement_unlocked(achievement: String) + +const CACHE_TIME = 60000 +const POPUP_POP_TIME = 1000 +const POPUP_LINGER_TIME = 5000 +const POPUP_HIDE_TIME = 1000 +const POPUP_BREAK = 1000 +const POPUP_HOVER_TIME = 250 + +@onready var popup = $"CanvasLayer/Popup" +@onready var popup_title = $"CanvasLayer/Popup/Popup/Title" +@onready var popup_body = $"CanvasLayer/Popup/Popup/Body" +@onready var audio = $"AudioStreamPlayer" +@onready var audio2 = $"AudioStreamPlayer2" + +enum AchievementState { + ENTERING, + SHOWN, + HOVERED, + EXITING, + HIDDEN +} + +var queue = [] +var cached_percents = {} +var last_percent_pull: int +var showing_popup = false +var starting_popup_pos +var starting_popup_size +var popup_tween +var popup_timer = 0 +var popup_time = 6 +#var entering = false +#var exiting = false +#var hovered = false + +var achievement_state = AchievementState.HIDDEN +var hovered = false + + + +func _ready(): + #_log_category = "ACH" + #_log_icon = "res://components/Logger/scroll-text.svg" + #_log_color = "#a166a1" + super() + _info("Achievements is active") + starting_popup_pos = popup.position + starting_popup_size = popup.size + popup.position.x = starting_popup_pos.x + starting_popup_size.x + popup.modulate = Color.TRANSPARENT + popup.scale = Vector2(0.5, 0.5) + if _triggerer: + _triggerer.listen("any", _on_trigger) + + +func _on_trigger(data: Dictionary) -> void: + _check_trigger(data.trigger, "Trigger") + + +func _process(delta): + popup_timer += delta + + if popup_timer > popup_time and achievement_state == AchievementState.SHOWN: + _exit_popup() + + + if not queue.is_empty() and not showing_popup: + _show_popup(queue.pop_front()) + + if popup_tween and not popup_tween.is_running(): + if hovered and achievement_state == AchievementState.SHOWN: + achievement_state = AchievementState.HOVERED + + popup_tween = create_tween() + popup_tween.set_parallel() + popup_tween.set_trans(Tween.TRANS_QUAD) + popup_tween.tween_property(popup, "scale", Vector2(1.05, 1.05), float(POPUP_HOVER_TIME) / 1000) + popup_tween.tween_property(popup, "position:x", starting_popup_pos.x - 20, float(POPUP_HOVER_TIME) / 1000) + if not hovered and achievement_state == AchievementState.HOVERED: + achievement_state = AchievementState.SHOWN + + popup_tween = create_tween() + popup_tween.set_parallel() + popup_tween.set_trans(Tween.TRANS_QUAD) + popup_tween.tween_property(popup, "scale", Vector2(1, 1), float(POPUP_HOVER_TIME) / 1000) + popup_tween.tween_property(popup, "position:x", starting_popup_pos.x, float(POPUP_HOVER_TIME) / 1000) + + +func _exit_popup(): + achievement_state = AchievementState.EXITING + audio2.play() + + if popup_tween: popup_tween.kill() + popup_tween = create_tween() + + popup_tween.set_ease(Tween.EASE_IN) + popup_tween.set_trans(Tween.TRANS_BACK) + popup_tween.set_parallel() + popup_tween.tween_property(popup, "modulate", Color.TRANSPARENT, POPUP_POP_TIME / 1000) + popup_tween.tween_property(popup, "scale", Vector2(0.5, 0.5), POPUP_POP_TIME / 1000) + popup_tween.tween_property(popup, "position:x", starting_popup_pos.x + starting_popup_size.x, POPUP_HIDE_TIME / 1000) + popup_tween.tween_interval(POPUP_BREAK / 1000) + popup_tween.chain() + popup_tween.tween_callback(func(): showing_popup = false) + + _info("Hiding achievement popup") + + +func _on_data_persisted(key: String, value, category: PersisterEnums.Scope): + _check_trigger(key, "Data", value) + + +## Get all of the achievement info +func get_achievements() -> Dictionary: + if not _data: + if (_logger): _logger.warn("Could not find data autoload when getting achievements") + return {} + + if not _data.data.has("achievements"): + if (_logger): _logger.warn("Could not find achievement data") + return {} + + var local_ach_data = _data.data.achievements.duplicate() + + var percents = _get_achievement_percents() + + for achievement in _data.data.achievements: + if percents.keys().has(achievement): + local_ach_data.percent = percents[achievement] + + return _data.data.achievements + + +## Get achievement info given the achievement slug +func get_achievement(achievement: String) -> Dictionary: + if not _data: + if _logger: _logger.warn("Could not find data autoload when getting achievement %s" % [achievement]) + return {} + + if not _data.data.has("achievements"): + if _logger: _logger.warn("Could not find achievement data") + return {} + + if not _data.data.achievements.has(achievement): + if _logger: _logger.warn("Could not find achievement %s" % [achievement]) + return {} + + var achievement_data = _data.data.achievements[achievement].duplicate() + + # TODO: Add in achievement percent + + return achievement_data + + +func _show_popup(achievement: String) -> void: + var achievement_data = get_achievement(achievement) + achievement_state = AchievementState.ENTERING + popup_timer = 0 + + if achievement_data.is_empty(): + if _logger: _logger.error("Could not get achievement %s info when trying to show" % [achievement]) + return + + popup_title.text = achievement_data.name + popup_body.text = achievement_data.description + + showing_popup = true + popup.position.x = starting_popup_pos.x + starting_popup_size.x + popup.modulate = Color.TRANSPARENT + popup.scale = Vector2(0.5, 0.5) + #popup.position.y = starting_popup_pos.y + starting_popup_size.y + + if popup_tween: + popup_tween.kill() + + audio.play() + + popup_tween = create_tween() + popup_tween.set_parallel() + popup_tween.set_ease(Tween.EASE_OUT) + popup_tween.set_trans(Tween.TRANS_BACK) + popup_tween.tween_property(popup, "modulate", Color.WHITE, POPUP_POP_TIME / 1000) + popup_tween.tween_property(popup, "scale", Vector2(1, 1), POPUP_POP_TIME / 1000) + popup_tween.tween_property(popup, "position:x", starting_popup_pos.x, POPUP_POP_TIME / 1000) + popup_tween.chain() + popup_tween.tween_callback(func(): + achievement_state = AchievementState.SHOWN + ) + + _info("Showing achievement %s popup" % [achievement]) + + +func _get_achievement_percents() -> Dictionary: + if not last_percent_pull: + return _get_percents_from_db() + + if last_percent_pull + CACHE_TIME <= Time.get_ticks_msec(): + return cached_percents + + return _get_percents_from_db() + + +func _get_percents_from_db() -> Dictionary: + last_percent_pull = Time.get_ticks_msec() + return {} + + +func _send_to_db(achievement: String) -> void: + pass + + +func _check_trigger(key: String, type: String, value = null): + if not _data: + if (_logger): _logger.warn("Could not find data autoload when checking trigger") + return + + if not _data.data.has("achievements"): + return + + for achievement in _data.data.achievements: + # If the persister does not have the achievement already + if not _persister.get_value(achievement): + var ach_data = _data.data.achievements[achievement] + + var valid = false + + match type: + "Data": + if ach_data.has("key") and ach_data.key == key: + valid = ach_data.value <= value + "Trigger": + if ach_data.has("trigger") and ach_data.trigger == key: + valid = true + + if valid: + achievement_unlocked.emit(achievement) + _persister.persist_data(achievement, true, PersisterEnums.Scope.PERMANENT) + _info("Added achievement %s to queue" % [achievement]) + queue.push_back(achievement) + _send_to_db(achievement) + + +func _on_mouse_handler_hovered(): + hovered = true + + +func _on_mouse_handler_clicked(): + if achievement_state != AchievementState.EXITING: + _exit_popup() + + +func _on_mouse_handler_unhovered(): + hovered = false diff --git a/components/Achievements/award.svg b/components/Achievements/award.svg new file mode 100644 index 0000000..5a4fe24 --- /dev/null +++ b/components/Achievements/award.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/components/Achievements/award.svg.import b/components/Achievements/award.svg.import new file mode 100644 index 0000000..a625d70 --- /dev/null +++ b/components/Achievements/award.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ur6oiwg6ksns" +path="res://.godot/imported/award.svg-6bd80af369d65193b7f216d026b9ff51.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://components/Achievements/award.svg" +dest_files=["res://.godot/imported/award.svg-6bd80af369d65193b7f216d026b9ff51.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/components/Achievements/back.png b/components/Achievements/back.png new file mode 100644 index 0000000..a42a0ca Binary files /dev/null and b/components/Achievements/back.png differ diff --git a/components/Achievements/back.png.import b/components/Achievements/back.png.import new file mode 100644 index 0000000..01d89eb --- /dev/null +++ b/components/Achievements/back.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dat440dg86mjl" +path="res://.godot/imported/back.png-7865ce000d117e80cc752a23c0588205.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://components/Achievements/back.png" +dest_files=["res://.godot/imported/back.png-7865ce000d117e80cc752a23c0588205.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/components/Achievements/information.txt b/components/Achievements/information.txt new file mode 100644 index 0000000..9287528 --- /dev/null +++ b/components/Achievements/information.txt @@ -0,0 +1,8 @@ +name: Achievements +short: Achievement handler to check and unlock achievements +description: An achievement handler that will check the available achievements + defined in both the game files and the mods. When called with triggers will + check to see if that trigger value unlocks achievements. +accent: #a166a1 +log_category: ACH +icon: res://components/Logger/scroll-text.svg diff --git a/components/Achievements/particle.aseprite b/components/Achievements/particle.aseprite new file mode 100644 index 0000000..4d9848b Binary files /dev/null and b/components/Achievements/particle.aseprite differ diff --git a/components/Achievements/particle.png b/components/Achievements/particle.png new file mode 100644 index 0000000..e571fbb Binary files /dev/null and b/components/Achievements/particle.png differ diff --git a/components/Achievements/particle.png.import b/components/Achievements/particle.png.import new file mode 100644 index 0000000..3d16af9 --- /dev/null +++ b/components/Achievements/particle.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://brmlpo1rv8m47" +path="res://.godot/imported/particle.png-659e7c73fd80f7940a2d61abac8f1984.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://components/Achievements/particle.png" +dest_files=["res://.godot/imported/particle.png-659e7c73fd80f7940a2d61abac8f1984.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/components/Achievements/popup-background.png b/components/Achievements/popup-background.png new file mode 100644 index 0000000..49d8c01 Binary files /dev/null and b/components/Achievements/popup-background.png differ diff --git a/components/Achievements/popup-background.png.import b/components/Achievements/popup-background.png.import new file mode 100644 index 0000000..be4e21f --- /dev/null +++ b/components/Achievements/popup-background.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dpmrungfff7jd" +path="res://.godot/imported/popup-background.png-1f543826c612308bac959317015a3a98.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://components/Achievements/popup-background.png" +dest_files=["res://.godot/imported/popup-background.png-1f543826c612308bac959317015a3a98.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/components/Achievements/thumbnail-mask.png b/components/Achievements/thumbnail-mask.png new file mode 100644 index 0000000..bd290e6 Binary files /dev/null and b/components/Achievements/thumbnail-mask.png differ diff --git a/components/Achievements/thumbnail-mask.png.import b/components/Achievements/thumbnail-mask.png.import new file mode 100644 index 0000000..3a0cd27 --- /dev/null +++ b/components/Achievements/thumbnail-mask.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dcj0xniur7dk1" +path="res://.godot/imported/thumbnail-mask.png-9e7311d6f1141413915356d4bb61948c.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://components/Achievements/thumbnail-mask.png" +dest_files=["res://.godot/imported/thumbnail-mask.png-9e7311d6f1141413915356d4bb61948c.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/components/Audio/Audio.tscn b/components/Audio/Audio.tscn new file mode 100644 index 0000000..8bb1e41 --- /dev/null +++ b/components/Audio/Audio.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://cjcs6jhw45bj4"] + +[ext_resource type="Script" path="res://components/Audio/audio.gd" id="1_c2epj"] + +[node name="Audio" type="Node"] +script = ExtResource("1_c2epj") diff --git a/components/Audio/audio.gd b/components/Audio/audio.gd new file mode 100644 index 0000000..ed4ba46 --- /dev/null +++ b/components/Audio/audio.gd @@ -0,0 +1,99 @@ +@icon("res://components/Audio/music.svg") +extends Base + +var music_data +var sfx_data + +var current_playing_music + +var tweens = {} + +func _spawned(): + if _triggerer: + _triggerer.listen("any", _on_trigger) + + music_data = _data.data.music + sfx_data = _data.data.sfx + + for music in music_data: + var music_value = music_data[music] + + if not _data.data.audio.has(music): + _error("Could not find file for music %s" % [music]) + continue + + music_value.track = _data.data.audio[music] + + var music_object = AudioStreamPlayer.new() + music_object.name = "Music - %s" % [music_value.name] + music_object.stream = music_value.track + add_child(music_object) + music_value.object = music_object + + _info("Loaded music %s" % [music]) + + for sfx in sfx_data: + var sfx_value = sfx_data[sfx] + + if not _data.data.audio.has(sfx): + _error("Could not find file for sfx %s" % [sfx]) + continue + + sfx_value.track = _data.data.audio[sfx] + + var sfx_object = AudioStreamPlayer.new() + sfx_object.name = "SFX - %s" % [sfx_value.name] + sfx_object.stream = sfx_value.track + add_child(sfx_object) + sfx_value.object = sfx_object + + _info("Loaded sfx %s" % [sfx]) + + +func _on_trigger(data: Dictionary) -> void: + var trigger = data.trigger + + if not trigger.begins_with("audio_"): + return + + var audio_name = trigger.split("_", false, 1)[1] + + for music in music_data: + var music_value = music_data[music] + + if not music == audio_name: + continue + + if not music_value.has("track"): + continue + + if current_playing_music: + if tweens.has(current_playing_music): + tweens[current_playing_music].kill() + + tweens[current_playing_music] = create_tween() + tweens[current_playing_music].tween_property(music_data[current_playing_music].object, "volume_db", -30, 1) + tweens[current_playing_music].tween_callback(func(): + music_data[current_playing_music].object.stop() + ) + + if tweens.has(music): + tweens[music].kill() + + if data.has("keep_position") and data.keep_position: + music_value.object.play(music_data[current_playing_music].object.get_playback_position()) + else: + music_value.object.play() + + tweens[music] = create_tween() + tweens[music].tween_property(music_data[music].object, "volume_db", 0, 1) + + current_playing_music = music + + return + + for sfx in sfx_data: + if not sfx == audio_name: + continue + + pass diff --git a/components/Audio/information.txt b/components/Audio/information.txt new file mode 100644 index 0000000..4eb13c5 --- /dev/null +++ b/components/Audio/information.txt @@ -0,0 +1,6 @@ +name: Audio +short: ??? +description: ??? +accent: #32ad61 +log_category: AUD +icon: res://components/Audio/music.svg diff --git a/components/Audio/music.svg b/components/Audio/music.svg new file mode 100644 index 0000000..b16547a --- /dev/null +++ b/components/Audio/music.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/components/Audio/music.svg.import b/components/Audio/music.svg.import new file mode 100644 index 0000000..8f7fc54 --- /dev/null +++ b/components/Audio/music.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bnrlsuxullf0v" +path="res://.godot/imported/music.svg-641a4ffd0c5780599b93a42e5f43eb33.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://components/Audio/music.svg" +dest_files=["res://.godot/imported/music.svg-641a4ffd0c5780599b93a42e5f43eb33.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/components/Base/base.gd b/components/Base/base.gd new file mode 100644 index 0000000..39a5fea --- /dev/null +++ b/components/Base/base.gd @@ -0,0 +1,90 @@ +class_name Base +extends Node +## A base addon +## +## A base addon for shared functionality between all other addons + +var _logger +var _data +var _persister +var _triggerer +var _information = { + "log_category": "???", + "accent": "#000000", + "icon": "res://components/Cursor/mouse-pointer-2.svg" +} + +var CORE_NODES = { + "Logger": { + "property": "_logger" + }, + "Data": { + "property": "_data" + }, + "Triggerer": { + "property": "_triggerer", + }, + "Persister": { + "property": "_persister", + "connections": {"data_persisted": "_on_data_persisted"} + } +} + + +func _ready(): + for node in CORE_NODES: + # Prevent circular references + if name == node: + break + + # Connect up core nodes + var node_value = CORE_NODES[node] + + if get_tree().root.has_node(node): + set(node_value.property, get_tree().root.get_node(node)) + var property = get(node_value.property) + + if node_value.has("connections"): + var connections = node_value.connections + + for connection in connections: + var connection_callback = connections[connection] + + property.get(connection).connect(get(connection_callback)) + + if _data and _data.components.has(name): + _information = _data.components[name].information + + if _information.has("triggers"): + for trigger in _information.triggers: + _triggerer.listen(trigger, Callable(self, _information.triggers[trigger])) + + if has_method("_prespawned"): + call("_prespawned") + + _info("%s is active" % [name]) + + if has_method("_spawned"): + call("_spawned") + + _info("%s is ready" % [name]) + + +func _on_data_persisted(key: String, value, category: PersisterEnums.Scope): + pass + + +func _debug(message: String): + if _logger: _logger.debug(message, { "category": _information.log_category, "image": _information.icon, "color": _information.accent }) + + +func _info(message: String): + if _logger: _logger.info(message, { "category": _information.log_category, "image": _information.icon, "color": _information.accent }) + + +func _warn(message: String): + if _logger: _logger.warn(message, { "category": _information.log_category, "image": _information.icon, "color": _information.accent }) + + +func _error(message: String): + if _logger: _logger.error(message, { "category": _information.log_category, "image": _information.icon, "color": _information.accent }) diff --git a/components/Cursor/Cursor.tscn b/components/Cursor/Cursor.tscn new file mode 100644 index 0000000..938b920 --- /dev/null +++ b/components/Cursor/Cursor.tscn @@ -0,0 +1,21 @@ +[gd_scene load_steps=4 format=3 uid="uid://qecwga1b4yqn"] + +[ext_resource type="Script" path="res://components/Cursor/cursor.gd" id="1_nmkwm"] +[ext_resource type="Texture2D" uid="uid://yg134s8wlxmi" path="res://icon.svg" id="2_yaf0k"] + +[sub_resource type="RectangleShape2D" id="RectangleShape2D_4cq27"] + +[node name="Cursor" type="CanvasLayer"] +script = ExtResource("1_nmkwm") + +[node name="MouseControl" type="Area2D" parent="."] + +[node name="CollisionShape2D" type="CollisionShape2D" parent="MouseControl"] +shape = SubResource("RectangleShape2D_4cq27") + +[node name="Sprite2D" type="Sprite2D" parent="MouseControl"] +scale = Vector2(0.15625, 0.15625) +texture = ExtResource("2_yaf0k") + +[connection signal="area_entered" from="MouseControl" to="." method="_on_mouse_control_area_entered"] +[connection signal="area_exited" from="MouseControl" to="." method="_on_mouse_control_area_exited"] diff --git a/components/Cursor/MouseHandler.tscn b/components/Cursor/MouseHandler.tscn new file mode 100644 index 0000000..e610a47 --- /dev/null +++ b/components/Cursor/MouseHandler.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://dykc1mgg5uopw"] + +[ext_resource type="Script" path="res://components/Cursor/mouse_handler.gd" id="1_n30mb"] + +[node name="MouseHandler" type="Area2D"] +script = ExtResource("1_n30mb") diff --git a/components/Cursor/cursor.gd b/components/Cursor/cursor.gd new file mode 100644 index 0000000..11c8dce --- /dev/null +++ b/components/Cursor/cursor.gd @@ -0,0 +1,30 @@ +@icon("res://components/Cursor/mouse-pointer-2.svg") +extends Base + +@onready var mouse_control = $"MouseControl" + + +func _process(delta): + Input.set_mouse_mode(Input.MOUSE_MODE_HIDDEN) + mouse_control.position = mouse_control.get_global_mouse_position() + + if Input.is_action_just_pressed("left_click"): + var overlapping_areas = mouse_control.get_overlapping_areas() + overlapping_areas.sort_custom(func(a, b): + return a.z_index > b.z_index + ) + + for area in overlapping_areas: + if area.has_method("_on_clicked"): + if not area._on_clicked(): + break + + +func _on_mouse_control_area_entered(area): + if area.has_method("_on_hovered"): + area._on_hovered() + + +func _on_mouse_control_area_exited(area): + if area.has_method("_on_unhovered"): + area._on_unhovered() diff --git a/components/Cursor/information.txt b/components/Cursor/information.txt new file mode 100644 index 0000000..c4152e4 --- /dev/null +++ b/components/Cursor/information.txt @@ -0,0 +1,6 @@ +name: Cursor +short: ??? +description: ??? +accent: #35b84b +log_category: CUR +icon: res://components/Cursor/mouse-pointer-2.svg diff --git a/components/Cursor/mouse-pointer-2.svg b/components/Cursor/mouse-pointer-2.svg new file mode 100644 index 0000000..2bad1dd --- /dev/null +++ b/components/Cursor/mouse-pointer-2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/components/Cursor/mouse-pointer-2.svg.import b/components/Cursor/mouse-pointer-2.svg.import new file mode 100644 index 0000000..c38994b --- /dev/null +++ b/components/Cursor/mouse-pointer-2.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cdu6b7pclsrrg" +path="res://.godot/imported/mouse-pointer-2.svg-528a80312e1a4aed351ba060d183df97.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://components/Cursor/mouse-pointer-2.svg" +dest_files=["res://.godot/imported/mouse-pointer-2.svg-528a80312e1a4aed351ba060d183df97.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/components/Cursor/mouse-pointer-click.svg b/components/Cursor/mouse-pointer-click.svg new file mode 100644 index 0000000..31aa43d --- /dev/null +++ b/components/Cursor/mouse-pointer-click.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/components/Cursor/mouse-pointer-click.svg.import b/components/Cursor/mouse-pointer-click.svg.import new file mode 100644 index 0000000..973b20a --- /dev/null +++ b/components/Cursor/mouse-pointer-click.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ddkp18the486t" +path="res://.godot/imported/mouse-pointer-click.svg-f0b06ba161b377fd039b4f40e477ff4d.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://components/Cursor/mouse-pointer-click.svg" +dest_files=["res://.godot/imported/mouse-pointer-click.svg-f0b06ba161b377fd039b4f40e477ff4d.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/components/Cursor/mouse_handler.gd b/components/Cursor/mouse_handler.gd new file mode 100644 index 0000000..5895c0b --- /dev/null +++ b/components/Cursor/mouse_handler.gd @@ -0,0 +1,39 @@ +@icon("res://components/Cursor/mouse-pointer-click.svg") +extends Area2D + +signal clicked +signal hovered +signal unhovered + +@export var passthrough = false + +var _logger + +func _ready(): + if get_tree().root.has_node("Logger"): + _logger = get_tree().root.get_node("Logger") + +func _on_clicked(): + clicked.emit() + #_logger.info("Clicked mouse handler for object →%s←" % [get_parent().name], { + #"color": "#919191", + #"image": "res://components/Cursor/mouse-pointer-click.svg", + #"category": "MOU" + #}) + return passthrough + +func _on_hovered(): + hovered.emit() + #_logger.info("Hovered over mouse handler for object →%s←" % [get_parent().name], { + #"color": "#919191", + #"image": "res://components/Cursor/mouse-pointer-click.svg", + #"category": "MOU" + #}) + +func _on_unhovered(): + unhovered.emit() + #_logger.info("Unhovered mouse handler for object →%s←" % [get_parent().name], { + #"color": "#919191", + #"image": "res://components/Cursor/mouse-pointer-click.svg", + #"category": "MOU" + #}) diff --git a/components/Data/Data.tscn b/components/Data/Data.tscn new file mode 100644 index 0000000..91ec856 --- /dev/null +++ b/components/Data/Data.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://b4o7cekdsf4pu"] + +[ext_resource type="Script" path="res://components/Data/data.gd" id="1_xutp1"] + +[node name="Data" type="Node"] +script = ExtResource("1_xutp1") diff --git a/components/Data/book-marked.svg b/components/Data/book-marked.svg new file mode 100644 index 0000000..0b394ea --- /dev/null +++ b/components/Data/book-marked.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/components/Data/book-marked.svg.import b/components/Data/book-marked.svg.import new file mode 100644 index 0000000..4b41724 --- /dev/null +++ b/components/Data/book-marked.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://05aeafxrb5ph" +path="res://.godot/imported/book-marked.svg-4d90597eaf7440ba51cd915ad6f20255.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://components/Data/book-marked.svg" +dest_files=["res://.godot/imported/book-marked.svg-4d90597eaf7440ba51cd915ad6f20255.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/components/Data/data-toolbar.gd b/components/Data/data-toolbar.gd new file mode 100644 index 0000000..56e1614 --- /dev/null +++ b/components/Data/data-toolbar.gd @@ -0,0 +1,18 @@ +const LOG_CATEGORY = "DATAT" +const NAME = "Data" + + +func send_log(parent): + parent.tools[NAME].node.info("Test Debug!", LOG_CATEGORY) + + +func send_debug(parent): + parent.tools[NAME].node.debug("Test Log!", LOG_CATEGORY) + + +func send_warning(parent): + parent.tools[NAME].node.warn("Test Warning!", LOG_CATEGORY) + + +func send_error(parent): + parent.tools[NAME].node.error("Test Error!", LOG_CATEGORY) diff --git a/components/Data/data.gd b/components/Data/data.gd new file mode 100644 index 0000000..0a2de61 --- /dev/null +++ b/components/Data/data.gd @@ -0,0 +1,241 @@ +@icon("res://components/Data/book-marked.svg") +class_name DataType +extends Base +## Data handler to read in data from files +## +## A data handler that will read in data from txt files and other sources for +## use in various parts of the game. Reads both from the parts folder in the +## main game directory and the mods folder in the user folder to allow users to +## easily mod the game. + +var _FILE_FUNCTIONS = { + "*.txt": _read_txt, + "*.png": _read_resource, + "*.png.import": _read_png_import, + "*.ogg": _read_resource, + "*.mp3": _read_resource, + "*.wav": _read_resource, + "*.gd": _read_script_resource, +} + +var _FILE_LOCATION_OVERRIDES = { + "*.gd": "scripts", + "*.png.import": "images", + "*.png": "images", + "*.ogg": "audio", + "*.wav": "audio" +} + +var data = {} +var components = {} +var location_overrides = {} + + +func _prespawned(): + _information = { + "accent": "#a27155", + "log_category": "DATA", + "icon": "res://components/Data/book-marked.svg" + } + + +func _spawned(): + reload_data() + + +## Reload the data object by pulling from available sources +func reload_data() -> void: + data = {} # Remove all old mod data + + # Open part folder from project (Ignore if none) and if exists set mod data to it + var res_dir = DirAccess.open("res://") + if res_dir.dir_exists("parts"): + data = _read_directory("res://parts") + data = _merge_objects(location_overrides, data) + + if res_dir.dir_exists("components"): + location_overrides = {} + components = _read_directory("res://components") + components = _merge_objects(location_overrides, components) + + +func _merge_objects(object1, object2): + var newObject = {} + for key in object1: + newObject[key] = object1[key] + for key in object2: + if(newObject.has(key)): + if(typeof(newObject[key]) == TYPE_STRING): + newObject[key] = object1[key] + elif(typeof(newObject[key]) == TYPE_OBJECT): + if newObject[key] is Texture: + newObject[key] = object1[key] + else: + newObject[key] = _merge_objects(object1[key], object2[key]) + elif(typeof(newObject[key]) == TYPE_DICTIONARY): + newObject[key] = _merge_objects(object1[key], object2[key]) + elif(typeof(newObject[key]) == TYPE_ARRAY): + newObject[key] = object1[key] + object2[key] + else: + newObject[key] = object2[key] + return newObject + + +func _read_directory(path) -> Dictionary: + var dir = DirAccess.open(path) + var local_data = {} + + if not dir: + _warn("Could not read directory [%s]" % [path]) + return {} + + dir.list_dir_begin() + var file_name = dir.get_next() + while file_name != "": + if dir.current_is_dir(): + var micro_data = _read_directory("%s/%s" % [path, file_name]) + if micro_data: + local_data[file_name] = micro_data + else: + var file_handling = _get_file_handling(file_name) + + if (file_handling): + var location_override = _get_location_override(file_name) + + if location_override: + var micro_data = file_handling.call("%s/%s" % [path, file_name]) + if not location_overrides.has(location_override): + location_overrides[location_override] = {} + location_overrides[location_override][file_name.split(".")[0]] = micro_data + else: + var micro_data = file_handling.call("%s/%s" % [path, file_name]) + if micro_data: + local_data[file_name.split(".")[0]] = micro_data + + file_name = dir.get_next() + + _info("Read directory ♢%s♢" % [path]) + return local_data + + +func _get_file_handling(file): + var split_file = file.split(".", true, 1) + + for file_function in _FILE_FUNCTIONS: + if(file_function == file): + return _FILE_FUNCTIONS[file_function] + + var split_file_function = file_function.split(".", true, 1) + + if(split_file_function[0] == "*" and split_file_function[1] == split_file[1]): + return _FILE_FUNCTIONS[file_function] + + return null + + +func _read_script_resource(path): + return load(path) + + +func _read_resource(path): + var song = load(path) + return song + + +func _read_png_import(path): + var image = load(path.rsplit(".", true, 1)[0]) + return image + + +func _get_location_override(file): + var split_file = file.split(".", true, 1) + + for location_override in _FILE_LOCATION_OVERRIDES: + if location_override == file: + return _FILE_LOCATION_OVERRIDES[location_override] + + var split_file_function = location_override.split(".", true, 1) + + if(split_file_function[0] == "*" and split_file_function[1] == split_file[1]): + return _FILE_LOCATION_OVERRIDES[location_override] + + +func _read_txt(path) -> Dictionary: + # Open file for reading + var file = FileAccess.open(path, FileAccess.READ) + var content = file.get_as_text() + var local_data = {} + + # Separate into lines + var split_content = content.split("\n", false) + + # Indentation + var indentation = 0 + var indentation_levels = [] + + # Iterate over everything + for content_piece in split_content: + # Fix Indentation to actual content level + var actual_indentation = _count_indentation(content_piece) + + while(actual_indentation < indentation): + indentation -= 1 + indentation_levels.pop_back() + + # Navigate to current indentation data + var micro_data = local_data + var previous_data = null + var i = 0 + while i < indentation: + previous_data = micro_data + micro_data = micro_data[indentation_levels[i]] + i += 1 + + # Reading + if(content_piece.ends_with("[]")): + # Array handling + var trimmed_content = content_piece.strip_edges().trim_suffix("[]") + + indentation += 1 + indentation_levels.append(trimmed_content) + micro_data[trimmed_content] = {} + else: + var split_args = content_piece.split(":", true, 1) + + if split_args.size() == 2: + # Dict Handling + var key = split_args[0].strip_edges() + var value = split_args[1].strip_edges() + + if value.is_valid_int(): + value = value.to_int() + + micro_data[key] = value + elif split_args.size() == 1: + # Value Handling + var value = split_args[0].strip_edges() + + if typeof(micro_data) == TYPE_DICTIONARY: + # TODO: FIX BELOW + if indentation_levels.size() == 0: + continue + + if previous_data[indentation_levels[indentation_levels.size()-1]].keys().size() == 0: + previous_data[indentation_levels[indentation_levels.size()-1]] = [split_args[0].strip_edges()] + else: + pass + else: + previous_data[indentation_levels[indentation_levels.size()-1]] += [split_args[0].strip_edges()] + return local_data + + +func _count_indentation(string: String): + var tabs = 0 + + for character in string: + if(character == "\t"): + tabs += 1 + else: + return tabs + + return tabs diff --git a/components/Data/information.txt b/components/Data/information.txt new file mode 100644 index 0000000..3a7bbb4 --- /dev/null +++ b/components/Data/information.txt @@ -0,0 +1,10 @@ +name: Data +short: Data handler to read in data from files +description: | + A data handler that will read in data from txt files and other + sources for use in various parts of the game. Reads both from the + parts folder in the main game directory and the mods folder in the + user folder to allow users to easily mod the game. +accent: #a27155 +log_category: DATA +icon: res://components/Data/book-marked.svg diff --git a/components/Data/toolbar.txt b/components/Data/toolbar.txt new file mode 100644 index 0000000..82c0550 --- /dev/null +++ b/components/Data/toolbar.txt @@ -0,0 +1,20 @@ +debug[] + type: button + name: Send Debug + accent: #11a1a1 + function: send_debug +log[] + type: button + name: Send Log + accent: #11a11a + function: send_log +warning[] + type: button + name: Send Warning + accent: #a1a111 + function: send_warning +error[] + type: button + name: Send Error + accent: #f13333 + function: send_error diff --git a/components/Dialogue/Dialogue.tscn b/components/Dialogue/Dialogue.tscn new file mode 100644 index 0000000..ba8fedc --- /dev/null +++ b/components/Dialogue/Dialogue.tscn @@ -0,0 +1,100 @@ +[gd_scene load_steps=6 format=3 uid="uid://mhltvrm84abx"] + +[ext_resource type="Script" path="res://components/Dialogue/dialogue.gd" id="1_0g08k"] +[ext_resource type="Script" path="res://components/Dialogue/area.gd" id="3_4w6ab"] +[ext_resource type="Texture2D" uid="uid://camtxohytwrqd" path="res://components/Dialogue/textbox.png" id="3_o1ce5"] +[ext_resource type="Texture2D" uid="uid://yg134s8wlxmi" path="res://icon.svg" id="5_s3ups"] + +[sub_resource type="RectangleShape2D" id="RectangleShape2D_wsoph"] +size = Vector2(480, 80) + +[node name="Dialogue" type="CanvasLayer"] +script = ExtResource("1_0g08k") + +[node name="TextBox" type="Control" parent="."] +layout_mode = 3 +anchors_preset = 0 +offset_left = 80.0 +offset_top = 280.0 +offset_right = 560.0 +offset_bottom = 360.0 +pivot_offset = Vector2(240, 80) + +[node name="MinimizeHandler" type="Area2D" parent="TextBox"] +visible = false +z_index = 10 +script = ExtResource("3_4w6ab") + +[node name="CollisionShape2D" type="CollisionShape2D" parent="TextBox/MinimizeHandler"] +position = Vector2(240, 40) +shape = SubResource("RectangleShape2D_wsoph") + +[node name="Graphics" type="TextureRect" parent="TextBox"] +layout_mode = 1 +anchors_preset = 7 +anchor_left = 0.5 +anchor_top = 1.0 +anchor_right = 0.5 +anchor_bottom = 1.0 +offset_left = -240.0 +offset_top = -80.0 +offset_right = 240.0 +grow_horizontal = 2 +grow_vertical = 0 +texture = ExtResource("3_o1ce5") + +[node name="TextBoxAvatar" type="TextureRect" parent="TextBox/Graphics"] +layout_mode = 1 +anchors_preset = 7 +anchor_left = 0.5 +anchor_top = 1.0 +anchor_right = 0.5 +anchor_bottom = 1.0 +offset_left = -206.0 +offset_top = -104.0 +offset_right = -158.0 +offset_bottom = -40.0 +grow_horizontal = 2 +grow_vertical = 0 +texture = ExtResource("5_s3ups") + +[node name="TextBoxText" type="RichTextLabel" parent="TextBox/Graphics"] +modulate = Color(0.705882, 0.690196, 0.552941, 1) +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -125.0 +offset_top = -24.0 +offset_right = 226.0 +offset_bottom = 30.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +theme_override_constants/shadow_offset_y = 1 +theme_override_constants/shadow_offset_x = 0 +theme_override_font_sizes/normal_font_size = 16 +text = "This is test text" + +[node name="TextBoxName" type="RichTextLabel" parent="TextBox/Graphics"] +modulate = Color(0.705882, 0.690196, 0.552941, 1) +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -237.0 +offset_top = 14.0 +offset_right = -125.0 +offset_bottom = 38.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +theme_override_constants/shadow_offset_y = 1 +theme_override_constants/shadow_offset_x = 0 +theme_override_font_sizes/normal_font_size = 16 +bbcode_enabled = true +text = "[center]NAME" diff --git a/components/Dialogue/area.gd b/components/Dialogue/area.gd new file mode 100644 index 0000000..4858f31 --- /dev/null +++ b/components/Dialogue/area.gd @@ -0,0 +1,9 @@ +extends Area2D + +signal clicked + +@export var passthrough = false + +func _on_clicked(): + clicked.emit() + return passthrough diff --git a/components/Dialogue/dialogue.gd b/components/Dialogue/dialogue.gd new file mode 100644 index 0000000..b08075a --- /dev/null +++ b/components/Dialogue/dialogue.gd @@ -0,0 +1,144 @@ +extends Base + +var used_lines = [] +var mouse_over = false + +@onready var text_box = $"TextBox" +@onready var text_box_text = $"TextBox/Graphics/TextBoxText" +@onready var text_box_name = $"TextBox/Graphics/TextBoxName" +@onready var text_box_image = $"TextBox/Graphics/TextBoxAvatar" +@onready var minimize_handler = $"TextBox/MinimizeHandler" + +var pop_tween +var last_lines = {} + +func _spawned(): + text_box.modulate = Color.TRANSPARENT + text_box.scale = Vector2(0, 0) + text_box.position.y = 360 + + minimize_handler.clicked.connect(_on_clicked) + + if _triggerer: + _triggerer.listen("any", _on_trigger) + + +func _on_trigger(data: Dictionary) -> void: + if has_trigger(data.trigger): + trigger_dialogue(data.trigger) + + +func _on_clicked(): + if pop_tween: + pop_tween.kill() + + pop_tween = create_tween() + pop_tween.set_ease(Tween.EASE_IN) + pop_tween.set_trans(Tween.TRANS_BACK) + pop_tween.set_parallel() + pop_tween.tween_property(text_box, "modulate", Color.TRANSPARENT, 0.5) + pop_tween.tween_property(text_box, "scale", Vector2(0, 0), 0.5) + pop_tween.tween_property(text_box, "position:y", 360, 0.5) + pop_tween.chain() + +func get_priority_from_string(string: String): + match string: + "low": return 1 + "medium": return 2 + "high": return 3 + +func trigger_dialogue(trigger: String): + _info("Dialogue trigger %s triggered" % [trigger]) + var dialogue = get_dialogue(trigger) + + if not dialogue: + return + + var lines = dialogue.values() + + + + + if pop_tween: + pop_tween.kill() + + #var person = Data.data.people[lines[0].character] + + text_box_text.text = lines[0].text + #text_box_image.texture = Data.data.images[lines[0].character] + #text_box_name.text = "[center]%s" % [person.short] + + pop_tween = create_tween() + pop_tween.set_ease(Tween.EASE_OUT) + pop_tween.set_trans(Tween.TRANS_BACK) + pop_tween.set_parallel() + pop_tween.tween_property(text_box, "modulate", Color.WHITE, 0.5) + pop_tween.tween_property(text_box, "scale", Vector2(1, 1), 0.5) + pop_tween.tween_property(text_box, "position:y", 280, 0.5) + pop_tween.chain() + + #get_parent().move_child(self, get_parent().get_child_count() - 1) + + +func has_trigger(trigger: String): + var dialogue_files = Data.data.dialogue.values() + + for file in dialogue_files: + for key in file: + if file[key].has("trigger"): + if file[key].trigger == trigger: + return true + + return false + + +func get_dialogue(trigger: String): + var dialogue_files = Data.data.dialogue.values() + + var combined_object = {} + + for file in dialogue_files: + for key in file: + combined_object[key] = file[key] + combined_object[key].key = key + + var lines = combined_object.values() + var trigger_lines = lines.filter(func(x): return x.trigger == trigger) + + trigger_lines.sort_custom(func(a, b): + var apriority = a.rules.priority if a.rules.has("priority") else "medium" + var bpriority = b.rules.priority if b.rules.has("priority") else "medium" + + if get_priority_from_string(apriority) == get_priority_from_string(bpriority): + return randf() < 0.5 + + return get_priority_from_string(apriority) > get_priority_from_string(bpriority) + ) + + for line in trigger_lines: + var invalid = false + + if last_lines.has(line.trigger) and last_lines[line.trigger] == line.key: + invalid = true + + for rule in line.rules: + if rule == "priority": + continue + elif rule == "unique": + if used_lines.has(line.key): + invalid = true + break + else: + var value = Persister.get_value(rule) + if value != line.rules[rule]: + invalid = true + break + + if invalid: + continue + + last_lines[line.trigger] = line.key + used_lines.push_back(line.key) + return line.lines + + return null diff --git a/components/Dialogue/information.txt b/components/Dialogue/information.txt new file mode 100644 index 0000000..bfab415 --- /dev/null +++ b/components/Dialogue/information.txt @@ -0,0 +1,6 @@ +name: Dialogue +short: ??? +description: ??? +accent: #2ec778 +log_category: DIA +icon: res://components/Dialogue/message-square-more.svg diff --git a/components/Dialogue/message-square-more.svg b/components/Dialogue/message-square-more.svg new file mode 100644 index 0000000..c9d13fd --- /dev/null +++ b/components/Dialogue/message-square-more.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/components/Dialogue/message-square-more.svg.import b/components/Dialogue/message-square-more.svg.import new file mode 100644 index 0000000..f76a029 --- /dev/null +++ b/components/Dialogue/message-square-more.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bwghow6a1p84w" +path="res://.godot/imported/message-square-more.svg-c15333f4fba2c19064690b510c5f7565.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://components/Dialogue/message-square-more.svg" +dest_files=["res://.godot/imported/message-square-more.svg-c15333f4fba2c19064690b510c5f7565.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/components/Dialogue/textbox.aseprite b/components/Dialogue/textbox.aseprite new file mode 100644 index 0000000..82f4bd6 Binary files /dev/null and b/components/Dialogue/textbox.aseprite differ diff --git a/components/Dialogue/textbox.png b/components/Dialogue/textbox.png new file mode 100644 index 0000000..852ab79 Binary files /dev/null and b/components/Dialogue/textbox.png differ diff --git a/components/Dialogue/textbox.png.import b/components/Dialogue/textbox.png.import new file mode 100644 index 0000000..d67d920 --- /dev/null +++ b/components/Dialogue/textbox.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://camtxohytwrqd" +path="res://.godot/imported/textbox.png-a3656dca7d0232cc5645bc8f60474933.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://components/Dialogue/textbox.png" +dest_files=["res://.godot/imported/textbox.png-a3656dca7d0232cc5645bc8f60474933.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/components/Leaderboard/Leaderboard.tscn b/components/Leaderboard/Leaderboard.tscn new file mode 100644 index 0000000..a343f72 --- /dev/null +++ b/components/Leaderboard/Leaderboard.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://dvjtjq2j522co"] + +[ext_resource type="Script" path="res://components/Leaderboard/leaderboard.gd" id="1_gvlwy"] + +[node name="Leaderboard" type="Node"] +script = ExtResource("1_gvlwy") diff --git a/components/Leaderboard/information.txt b/components/Leaderboard/information.txt new file mode 100644 index 0000000..4283aae --- /dev/null +++ b/components/Leaderboard/information.txt @@ -0,0 +1,6 @@ +name: Leaderboard +short: ??? +description: ??? +accent: #2e4fc7 +log_category: LEA +icon: res://components/Leaderboard/trophy.svg diff --git a/components/Leaderboard/leaderboard.gd b/components/Leaderboard/leaderboard.gd new file mode 100644 index 0000000..c934586 --- /dev/null +++ b/components/Leaderboard/leaderboard.gd @@ -0,0 +1,2 @@ +@icon("res://components/Leaderboard/trophy.svg") +extends Base diff --git a/components/Leaderboard/trophy.svg b/components/Leaderboard/trophy.svg new file mode 100644 index 0000000..12a06e2 --- /dev/null +++ b/components/Leaderboard/trophy.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/components/Leaderboard/trophy.svg.import b/components/Leaderboard/trophy.svg.import new file mode 100644 index 0000000..c53616d --- /dev/null +++ b/components/Leaderboard/trophy.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://for54f1or3ir" +path="res://.godot/imported/trophy.svg-50b272fe3a1d033c20559dd811d9f235.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://components/Leaderboard/trophy.svg" +dest_files=["res://.godot/imported/trophy.svg-50b272fe3a1d033c20559dd811d9f235.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/components/Logger/Logger.tscn b/components/Logger/Logger.tscn new file mode 100644 index 0000000..6b955d8 --- /dev/null +++ b/components/Logger/Logger.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://7p8ecy62n2h8"] + +[ext_resource type="Script" path="res://components/Logger/logger.gd" id="1_gse6q"] + +[node name="Logger" type="Node"] +script = ExtResource("1_gse6q") diff --git a/components/Logger/commands.gd b/components/Logger/commands.gd new file mode 100644 index 0000000..6428a4d --- /dev/null +++ b/components/Logger/commands.gd @@ -0,0 +1,2 @@ +func send_log(): + pass diff --git a/components/Logger/information.txt b/components/Logger/information.txt new file mode 100644 index 0000000..2f84ff4 --- /dev/null +++ b/components/Logger/information.txt @@ -0,0 +1,9 @@ +name: Logger +short: A logger to log data to relevant locations +description: A logger that logs various forms of data at different log levels to + both built in locations in Godot as well as other components that listen to + the provided log created signal. +accent: #71a255 +log_category: LOG +icon: res://components/Logger/scroll-text.svg +version: v1.0.0 diff --git a/components/Logger/logger-toolbar.gd b/components/Logger/logger-toolbar.gd new file mode 100644 index 0000000..780725b --- /dev/null +++ b/components/Logger/logger-toolbar.gd @@ -0,0 +1,34 @@ +const LOG_CATEGORY = "TOOLT" +const NAME = "Logger" + + +func send_log(parent): + parent.info("Test Log!", { + "image": "res://components/Toolbar/hammer.svg", + "color": "#818181", + "category": "TST" + }) + + +func send_debug(parent): + parent.debug("Test Debug!", { + "image": "res://components/Toolbar/hammer.svg", + "color": "#818181", + "category": "TST" + }) + + +func send_warning(parent): + parent.warn("Test Warn!", { + "image": "res://components/Toolbar/hammer.svg", + "color": "#818181", + "category": "TST" + }) + + +func send_error(parent): + parent.error("Test Error!", { + "image": "res://components/Toolbar/hammer.svg", + "color": "#818181", + "category": "TST" + }) diff --git a/components/Logger/logger.gd b/components/Logger/logger.gd new file mode 100644 index 0000000..f1831bc --- /dev/null +++ b/components/Logger/logger.gd @@ -0,0 +1,133 @@ +@icon("res://components/Logger/scroll-text.svg") +extends Node +## A logger to log data to relevant locations +## +## A logger that logs various forms of data at different log levels to both +## built in locations in Godot as well as other components that listen to the +## provided log created signal. + +signal log_created(message: String, level: LogLevel) + +enum LogLevel { + DEBUG, + INFO, + WARN, + ERROR +} + +## The log level that should be outputted as a minimum. +@export var log_level: LogLevel + + +## Log a message at log level debug (meant for troubleshooting). +func debug(message: String, arguments: Dictionary = {}) -> void: + _log(message, LogLevel.DEBUG, arguments) + + +## Log a message at log level info (log to indicate something happened). +func info(message: String, arguments: Dictionary = {}) -> void: + _log(message, LogLevel.INFO, arguments) + + +## Log a warning at log level warning (something unexpected happened but it can +## continue). +func warn(message: String, arguments: Dictionary = {}) -> void: + _log(message, LogLevel.WARN, arguments) + + +## Log an error at log level error (an issue that prevents something from +## functioning). +func error(message: String, arguments: Dictionary = {}) -> void: + _log(message, LogLevel.ERROR, arguments) + + +func _log(message: String, level: LogLevel, arguments: Dictionary = {}) -> void: + var category = arguments.category if arguments.has("category") and arguments.category else "???" + var color = arguments.color if arguments.has("color") else "olive" + var image = arguments.image if arguments.has("image") and arguments.image else "res://components/Logger/scroll-text.svg" + + var adjusted_message = _clean_message(message) + + var constructed_message = "[color=%s][%s][/color] [img= width=12 height=12 valign=center]%s[/img] %s" % [color, category, image, adjusted_message] + print_rich(constructed_message) + log_created.emit(constructed_message, level) + + +func _clean_message(message: String) -> String: + var cleans = [ + { + "type": "button", + "regex": "μ(.*)μ", + "color": Color.html("#a4bf37") + }, + { + "type": "key", + "regex": "<(.*)>", + "color": Color.html("#42ad24") + }, + { + "type": "tool", + "regex": "λ(.*)λ", + "color": Color.html("#bf9d37") + }, + { + "type": "object", + "regex": "→(.*)←", + "color": Color.html("#854322") + }, + { + "type": "path", + "regex": "♢(.*)♢", + "color": Color.html("#22852e") + }, + { + "type": "function", + "regex": "∨(.*)∨", + "color": Color.html("#ad2452") + }, + { + "type": "trigger", + "regex": "∧(.*)∧", + "color": Color.html("#ad2d24") + }, + { + "type": "category", + "regex": "\\{(.*)\\}", + "color": Color.html("#ad9b24") + }, + { + "type": "value", + "regex": "\\|(.*)\\|", + "color": Color.TEAL + } + ] + + var adjusted_message = message + + for clean in cleans: + adjusted_message = _replace_regex_color(adjusted_message, clean.regex, clean.color) + + return adjusted_message + + +func _replace_regex_color(message: String, regex_string: String, color: Color) -> String: + return _replace_regex(message, regex_string, "[color=" + color.to_html() + "]%s[/color]") + + +func _replace_regex(message: String, regex_string: String, new_content: String) -> String: + var adjusted_message = message + var stripped_message = message + + var regex = RegEx.new() + regex.compile(regex_string) + var result = regex.search(adjusted_message) + + while result: + var before_content = adjusted_message.substr(0, result.get_start()) + var after_content = adjusted_message.substr(result.get_end(), adjusted_message.length()) + var template_string = "%s" + new_content + "%s" + adjusted_message = template_string % [before_content, result.get_string(1), after_content] + + result = regex.search(adjusted_message) + + return adjusted_message diff --git a/components/Logger/scroll-text.svg b/components/Logger/scroll-text.svg new file mode 100644 index 0000000..e5bc2c0 --- /dev/null +++ b/components/Logger/scroll-text.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/components/Logger/scroll-text.svg.import b/components/Logger/scroll-text.svg.import new file mode 100644 index 0000000..8113916 --- /dev/null +++ b/components/Logger/scroll-text.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bt1irjjewldcq" +path="res://.godot/imported/scroll-text.svg-3edd1135d80784e9e14ee4871d671a9a.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://components/Logger/scroll-text.svg" +dest_files=["res://.godot/imported/scroll-text.svg-3edd1135d80784e9e14ee4871d671a9a.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/components/Logger/settings.txt b/components/Logger/settings.txt new file mode 100644 index 0000000..e5864fc --- /dev/null +++ b/components/Logger/settings.txt @@ -0,0 +1,11 @@ +log_level[] + name: Log Level + description: The level of info you want as a minimum to be logged to + relevant locations. + category: misc + default: LogLevel.WARN + values[] + LogLevel.DEBUG + LogLevel.INFO + LogLevel.WARN + LogLevel.ERROR diff --git a/components/Logger/toolbar.txt b/components/Logger/toolbar.txt new file mode 100644 index 0000000..8295f47 --- /dev/null +++ b/components/Logger/toolbar.txt @@ -0,0 +1,24 @@ +debug[] + type: button + name: Send Debug + accent: #11a1a1 + function: send_debug +test[] + type: label + name: Testing Label + accent: #11a1cc +log[] + type: button + name: Send Log + accent: #11a11a + function: send_log +warning[] + type: button + name: Send Warning + accent: #a1a111 + function: send_warning +error[] + type: button + name: Send Error + accent: #f13333 + function: send_error diff --git a/components/Menu/Menu.tscn b/components/Menu/Menu.tscn new file mode 100644 index 0000000..02acd1c --- /dev/null +++ b/components/Menu/Menu.tscn @@ -0,0 +1,691 @@ +[gd_scene load_steps=14 format=3 uid="uid://dcu7jrwg24hg1"] + +[ext_resource type="Script" path="res://components/Menu/menu.gd" id="1_f186y"] +[ext_resource type="Theme" uid="uid://ck7603ob4gflc" path="res://Fonts/Theme.tres" id="1_fowhs"] +[ext_resource type="Shader" path="res://components/Menu/god_rays.gdshader" id="1_n8upd"] +[ext_resource type="Script" path="res://components/Menu/populate_from_metadata.gd" id="3_727p5"] +[ext_resource type="PackedScene" uid="uid://cmyjaeahyipq4" path="res://components/Menu/MenuButton.tscn" id="5_goj86"] +[ext_resource type="Script" path="res://components/Menu/credits.gd" id="6_b724h"] + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_ygqow"] +shader = ExtResource("1_n8upd") +shader_parameter/angle = 0.525 +shader_parameter/position = 0.27 +shader_parameter/spread = 0.595 +shader_parameter/cutoff = 0.0710001 +shader_parameter/falloff = 0.845 +shader_parameter/edge_fade = 0.34 +shader_parameter/speed = 1.0 +shader_parameter/ray1_density = 8.0 +shader_parameter/ray2_density = 30.0 +shader_parameter/ray2_intensity = 0.169 +shader_parameter/color = Vector4(1, 0.9, 0.65, 0.1) +shader_parameter/hdr = false +shader_parameter/seed = 5.0 + +[sub_resource type="Animation" id="Animation_5jiy0"] +resource_name = "show_main" +length = 5.0 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("CanvasLayer/MainMenu/GameInfo/GameTitle:position") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 0.0333333, 0.0666667, 0.1, 0.133333, 0.166667, 0.2, 0.233333, 0.266667, 0.3, 0.333333, 0.366667, 0.4, 0.433333, 0.466667, 0.5), +"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), +"update": 0, +"values": [Vector2(135, 168), Vector2(185.297, 168), Vector2(226.423, 168), Vector2(259.221, 168), Vector2(284.538, 168), Vector2(303.219, 168), Vector2(316.109, 168), Vector2(324.053, 168), Vector2(327.897, 168), Vector2(328.486, 168), Vector2(326.665, 168), Vector2(323.28, 168), Vector2(319.175, 168), Vector2(315.197, 168), Vector2(312.19, 168), Vector2(311, 168)] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath("CanvasLayer/MainMenu/GameInfo/GameUnderline:position") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0, 0.0333333, 0.0666667, 0.1, 0.133333, 0.166667, 0.2, 0.233333, 0.266667, 0.3, 0.333333, 0.366667, 0.4, 0.433333, 0.466667, 0.5), +"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), +"update": 0, +"values": [Vector2(650, 194), Vector2(582.556, 194), Vector2(527.41, 194), Vector2(483.431, 194), Vector2(449.483, 194), Vector2(424.434, 194), Vector2(407.15, 194), Vector2(396.497, 194), Vector2(391.343, 194), Vector2(390.553, 194), Vector2(392.995, 194), Vector2(397.534, 194), Vector2(403.038, 194), Vector2(408.372, 194), Vector2(412.404, 194), Vector2(414, 194)] +} +tracks/2/type = "value" +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/path = NodePath("CanvasLayer/MainMenu/GameInfo/Author:position") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/keys = { +"times": PackedFloat32Array(0, 0.0333333, 0.0666667, 0.1, 0.133333, 0.166667, 0.2, 0.233333, 0.266667, 0.3, 0.333333, 0.366667, 0.4, 0.433333, 0.466667, 0.5), +"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), +"update": 0, +"values": [Vector2(648, 199), Vector2(561.123, 199), Vector2(490.088, 199), Vector2(433.436, 199), Vector2(389.707, 199), Vector2(357.44, 199), Vector2(335.176, 199), Vector2(321.454, 199), Vector2(314.814, 199), Vector2(313.797, 199), Vector2(316.942, 199), Vector2(322.79, 199), Vector2(329.879, 199), Vector2(336.751, 199), Vector2(341.944, 199), Vector2(344, 199)] +} +tracks/3/type = "value" +tracks/3/imported = false +tracks/3/enabled = true +tracks/3/path = NodePath("CanvasLayer/MainMenu/Rays:position") +tracks/3/interp = 1 +tracks/3/loop_wrap = true +tracks/3/keys = { +"times": PackedFloat32Array(0, 0.0333333, 0.0666667, 0.1, 0.133333, 0.166667, 0.2, 0.233333, 0.266667, 0.3, 0.333333, 0.366667, 0.4, 0.433333, 0.466667, 0.5), +"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), +"update": 0, +"values": [Vector2(0, 0), Vector2(32.0074, 0), Vector2(58.1781, 0), Vector2(79.0498, 0), Vector2(95.1607, 0), Vector2(107.048, 0), Vector2(115.251, 0), Vector2(120.307, 0), Vector2(122.753, 0), Vector2(123.127, 0), Vector2(121.969, 0), Vector2(119.814, 0), Vector2(117.202, 0), Vector2(114.671, 0), Vector2(112.757, 0), Vector2(112, 0)] +} +tracks/4/type = "value" +tracks/4/imported = false +tracks/4/enabled = true +tracks/4/path = NodePath("CanvasLayer/MainMenu/MenuButtons/PlayButton:position") +tracks/4/interp = 1 +tracks/4/loop_wrap = true +tracks/4/keys = { +"times": PackedFloat32Array(0.9, 0.933333, 0.966667, 1, 1.03333, 1.06667, 1.1, 1.13333, 1.16667, 1.2), +"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1), +"update": 0, +"values": [Vector2(-110, 128), Vector2(-39.3658, 128), Vector2(9.80145, 128), Vector2(41.0148, 128), Vector2(57.7873, 128), Vector2(63.6323, 128), Vector2(62.0629, 128), Vector2(56.5923, 128), Vector2(50.7336, 128), Vector2(48, 128)] +} +tracks/5/type = "value" +tracks/5/imported = false +tracks/5/enabled = true +tracks/5/path = NodePath("CanvasLayer/MainMenu/MenuButtons/OptionsButton:position") +tracks/5/interp = 1 +tracks/5/loop_wrap = true +tracks/5/keys = { +"times": PackedFloat32Array(1.15, 1.18333, 1.21667, 1.25, 1.28333, 1.31667, 1.35, 1.38333, 1.41667, 1.45), +"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1), +"update": 0, +"values": [Vector2(-110, 160), Vector2(-32.2129, 160), Vector2(21.9332, 160), Vector2(56.3074, 160), Vector2(74.7784, 160), Vector2(81.2154, 160), Vector2(79.487, 160), Vector2(73.4624, 160), Vector2(67.0104, 160), Vector2(64, 160)] +} +tracks/6/type = "value" +tracks/6/imported = false +tracks/6/enabled = true +tracks/6/path = NodePath("CanvasLayer/MainMenu/MenuButtons/CreditsButton:position") +tracks/6/interp = 1 +tracks/6/loop_wrap = true +tracks/6/keys = { +"times": PackedFloat32Array(1.4, 1.43333, 1.46667, 1.5, 1.53333, 1.56667, 1.6, 1.63333, 1.66667, 1.7), +"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1), +"update": 0, +"values": [Vector2(-110, 192), Vector2(-25.0601, 192), Vector2(34.065, 192), Vector2(71.6, 192), Vector2(91.7696, 192), Vector2(98.7984, 192), Vector2(96.9111, 192), Vector2(90.3325, 192), Vector2(83.2872, 192), Vector2(80, 192)] +} +tracks/7/type = "value" +tracks/7/imported = false +tracks/7/enabled = true +tracks/7/path = NodePath("CanvasLayer/MainMenu/MenuButtons/QuitButton:position") +tracks/7/interp = 1 +tracks/7/loop_wrap = true +tracks/7/keys = { +"times": PackedFloat32Array(1.65, 1.68333, 1.71667, 1.75, 1.78333, 1.81667, 1.85, 1.88333, 1.91667, 1.95), +"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1), +"update": 0, +"values": [Vector2(-110, 224), Vector2(-17.9072, 224), Vector2(46.1968, 224), Vector2(86.8927, 224), Vector2(108.761, 224), Vector2(116.381, 224), Vector2(114.335, 224), Vector2(107.203, 224), Vector2(99.5641, 224), Vector2(96, 224)] +} + +[sub_resource type="Animation" id="Animation_u1e5j"] +length = 0.001 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("CanvasLayer/MainMenu/GameInfo/GameTitle:position") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Vector2(135, 168)] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath("CanvasLayer/MainMenu/GameInfo/GameUnderline:position") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Vector2(650, 194)] +} +tracks/2/type = "value" +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/path = NodePath("CanvasLayer/MainMenu/GameInfo/Author:position") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Vector2(648, 199)] +} +tracks/3/type = "value" +tracks/3/imported = false +tracks/3/enabled = true +tracks/3/path = NodePath("CanvasLayer/MainMenu/Rays:position") +tracks/3/interp = 1 +tracks/3/loop_wrap = true +tracks/3/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Vector2(0, 0)] +} +tracks/4/type = "value" +tracks/4/imported = false +tracks/4/enabled = true +tracks/4/path = NodePath("CanvasLayer/MainMenu/MenuButtons/PlayButton:position") +tracks/4/interp = 1 +tracks/4/loop_wrap = true +tracks/4/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Vector2(-110, 128)] +} +tracks/5/type = "value" +tracks/5/imported = false +tracks/5/enabled = true +tracks/5/path = NodePath("CanvasLayer/MainMenu/MenuButtons/OptionsButton:position") +tracks/5/interp = 1 +tracks/5/loop_wrap = true +tracks/5/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Vector2(-110, 160)] +} +tracks/6/type = "value" +tracks/6/imported = false +tracks/6/enabled = true +tracks/6/path = NodePath("CanvasLayer/MainMenu/MenuButtons/CreditsButton:position") +tracks/6/interp = 1 +tracks/6/loop_wrap = true +tracks/6/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Vector2(-110, 192)] +} +tracks/7/type = "value" +tracks/7/imported = false +tracks/7/enabled = true +tracks/7/path = NodePath("CanvasLayer/MainMenu/MenuButtons/QuitButton:position") +tracks/7/interp = 1 +tracks/7/loop_wrap = true +tracks/7/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Vector2(-110, 224)] +} + +[sub_resource type="Animation" id="Animation_ob40t"] +resource_name = "hide_main" +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("CanvasLayer/MainMenu/MenuButtons/PlayButton:position") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 0.0333333, 0.0666667, 0.1, 0.133333, 0.166667, 0.2, 0.233333, 0.266667, 0.3, 0.333333, 0.366667, 0.4, 0.433333, 0.466667, 0.5), +"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), +"update": 0, +"values": [Vector2(48, 128), Vector2(27.6356, 128), Vector2(8.67555, 128), Vector2(-8.88, 128), Vector2(-25.0311, 128), Vector2(-39.7778, 128), Vector2(-53.12, 128), Vector2(-65.0578, 128), Vector2(-75.5911, 128), Vector2(-84.72, 128), Vector2(-92.4444, 128), Vector2(-98.7645, 128), Vector2(-103.68, 128), Vector2(-107.191, 128), Vector2(-109.298, 128), Vector2(-110, 128)] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath("CanvasLayer/MainMenu/MenuButtons/OptionsButton:position") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0, 0.0333333, 0.0666667, 0.1, 0.133333, 0.166667, 0.2, 0.233333, 0.266667, 0.3, 0.333333, 0.366667, 0.4, 0.433333, 0.466667, 0.5), +"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), +"update": 0, +"values": [Vector2(64, 160), Vector2(41.5733, 160), Vector2(20.6933, 160), Vector2(1.36, 160), Vector2(-16.4267, 160), Vector2(-32.6667, 160), Vector2(-47.36, 160), Vector2(-60.5067, 160), Vector2(-72.1067, 160), Vector2(-82.16, 160), Vector2(-90.6667, 160), Vector2(-97.6267, 160), Vector2(-103.04, 160), Vector2(-106.907, 160), Vector2(-109.227, 160), Vector2(-110, 160)] +} +tracks/2/type = "value" +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/path = NodePath("CanvasLayer/MainMenu/MenuButtons/CreditsButton:position") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/keys = { +"times": PackedFloat32Array(0, 0.0333333, 0.0666667, 0.1, 0.133333, 0.166667, 0.2, 0.233333, 0.266667, 0.3, 0.333333, 0.366667, 0.4, 0.433333, 0.466667, 0.5), +"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), +"update": 0, +"values": [Vector2(80, 192), Vector2(55.5111, 192), Vector2(32.7111, 192), Vector2(11.6, 192), Vector2(-7.82223, 192), Vector2(-25.5556, 192), Vector2(-41.6, 192), Vector2(-55.9556, 192), Vector2(-68.6222, 192), Vector2(-79.6, 192), Vector2(-88.8889, 192), Vector2(-96.4889, 192), Vector2(-102.4, 192), Vector2(-106.622, 192), Vector2(-109.156, 192), Vector2(-110, 192)] +} +tracks/3/type = "value" +tracks/3/imported = false +tracks/3/enabled = true +tracks/3/path = NodePath("CanvasLayer/MainMenu/MenuButtons/QuitButton:position") +tracks/3/interp = 1 +tracks/3/loop_wrap = true +tracks/3/keys = { +"times": PackedFloat32Array(0, 0.0333333, 0.0666667, 0.1, 0.133333, 0.166667, 0.2, 0.233333, 0.266667, 0.3, 0.333333, 0.366667, 0.4, 0.433333, 0.466667, 0.5), +"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), +"update": 0, +"values": [Vector2(96, 224), Vector2(69.4489, 224), Vector2(44.7289, 224), Vector2(21.84, 224), Vector2(0.782219, 224), Vector2(-18.4445, 224), Vector2(-35.84, 224), Vector2(-51.4044, 224), Vector2(-65.1378, 224), Vector2(-77.04, 224), Vector2(-87.1111, 224), Vector2(-95.3511, 224), Vector2(-101.76, 224), Vector2(-106.338, 224), Vector2(-109.084, 224), Vector2(-110, 224)] +} +tracks/4/type = "value" +tracks/4/imported = false +tracks/4/enabled = true +tracks/4/path = NodePath("CanvasLayer/MainMenu/GameInfo/GameTitle:position") +tracks/4/interp = 1 +tracks/4/loop_wrap = true +tracks/4/keys = { +"times": PackedFloat32Array(0, 0.0333333, 0.0666667, 0.1, 0.133333, 0.166667, 0.2, 0.233333, 0.266667, 0.3, 0.333333, 0.366667, 0.4, 0.433333, 0.466667, 0.5), +"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), +"update": 0, +"values": [Vector2(311, 168), Vector2(288.316, 168), Vector2(267.196, 168), Vector2(247.64, 168), Vector2(229.649, 168), Vector2(213.222, 168), Vector2(198.36, 168), Vector2(185.062, 168), Vector2(173.329, 168), Vector2(163.16, 168), Vector2(154.556, 168), Vector2(147.516, 168), Vector2(142.04, 168), Vector2(138.129, 168), Vector2(135.782, 168), Vector2(135, 168)] +} +tracks/5/type = "value" +tracks/5/imported = false +tracks/5/enabled = true +tracks/5/path = NodePath("CanvasLayer/MainMenu/GameInfo/GameUnderline:position") +tracks/5/interp = 1 +tracks/5/loop_wrap = true +tracks/5/keys = { +"times": PackedFloat32Array(0, 0.0333333, 0.0666667, 0.1, 0.133333, 0.166667, 0.2, 0.233333, 0.266667, 0.3, 0.333333), +"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), +"update": 0, +"values": [Vector2(414, 194), Vector2(458.84, 194), Vector2(498.96, 194), Vector2(534.36, 194), Vector2(565.04, 194), Vector2(591, 194), Vector2(612.24, 194), Vector2(628.76, 194), Vector2(640.56, 194), Vector2(647.64, 194), Vector2(650, 194)] +} +tracks/6/type = "value" +tracks/6/imported = false +tracks/6/enabled = true +tracks/6/path = NodePath("CanvasLayer/MainMenu/GameInfo/Author:position") +tracks/6/interp = 1 +tracks/6/loop_wrap = true +tracks/6/keys = { +"times": PackedFloat32Array(0, 0.0333333, 0.0666667, 0.1, 0.133333, 0.166667, 0.2, 0.233333, 0.266667, 0.3, 0.333333, 0.366667, 0.4, 0.433333, 0.466667, 0.5), +"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), +"update": 0, +"values": [Vector2(344, 199), Vector2(383.182, 199), Vector2(419.662, 199), Vector2(453.44, 199), Vector2(484.516, 199), Vector2(512.889, 199), Vector2(538.56, 199), Vector2(561.529, 199), Vector2(581.796, 199), Vector2(599.36, 199), Vector2(614.222, 199), Vector2(626.382, 199), Vector2(635.84, 199), Vector2(642.596, 199), Vector2(646.649, 199), Vector2(648, 199)] +} +tracks/7/type = "value" +tracks/7/imported = false +tracks/7/enabled = true +tracks/7/path = NodePath("CanvasLayer/MainMenu/Rays:position") +tracks/7/interp = 1 +tracks/7/loop_wrap = true +tracks/7/keys = { +"times": PackedFloat32Array(0, 0.0333333, 0.0666667, 0.1, 0.133333, 0.166667, 0.2, 0.233333, 0.266667, 0.3, 0.333333, 0.366667, 0.4, 0.433333, 0.466667, 0.5), +"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), +"update": 0, +"values": [Vector2(112, 0), Vector2(97.5644, 0), Vector2(84.1244, 0), Vector2(71.68, 0), Vector2(60.2311, 0), Vector2(49.7778, 0), Vector2(40.32, 0), Vector2(31.8578, 0), Vector2(24.3911, 0), Vector2(17.92, 0), Vector2(12.4445, 0), Vector2(7.96444, 0), Vector2(4.48, 0), Vector2(1.99111, 0), Vector2(0.49778, 0), Vector2(0, 0)] +} + +[sub_resource type="Animation" id="Animation_2xxnd"] +resource_name = "show_credits" +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("CanvasLayer/MainMenu/MenuButtons/PlayButton:position") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 0.0333333, 0.0666667, 0.1, 0.133333, 0.166667, 0.2, 0.233333, 0.266667, 0.3, 0.333333, 0.366667, 0.4, 0.433333, 0.466667, 0.5), +"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), +"update": 0, +"values": [Vector2(48, 128), Vector2(27.6356, 128), Vector2(8.67555, 128), Vector2(-8.88, 128), Vector2(-25.0311, 128), Vector2(-39.7778, 128), Vector2(-53.12, 128), Vector2(-65.0578, 128), Vector2(-75.5911, 128), Vector2(-84.72, 128), Vector2(-92.4444, 128), Vector2(-98.7645, 128), Vector2(-103.68, 128), Vector2(-107.191, 128), Vector2(-109.298, 128), Vector2(-110, 128)] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath("CanvasLayer/MainMenu/MenuButtons/OptionsButton:position") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0, 0.0333333, 0.0666667, 0.1, 0.133333, 0.166667, 0.2, 0.233333, 0.266667, 0.3, 0.333333, 0.366667, 0.4, 0.433333, 0.466667, 0.5), +"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), +"update": 0, +"values": [Vector2(64, 160), Vector2(41.5733, 160), Vector2(20.6933, 160), Vector2(1.36, 160), Vector2(-16.4267, 160), Vector2(-32.6667, 160), Vector2(-47.36, 160), Vector2(-60.5067, 160), Vector2(-72.1067, 160), Vector2(-82.16, 160), Vector2(-90.6667, 160), Vector2(-97.6267, 160), Vector2(-103.04, 160), Vector2(-106.907, 160), Vector2(-109.227, 160), Vector2(-110, 160)] +} +tracks/2/type = "value" +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/path = NodePath("CanvasLayer/MainMenu/MenuButtons/CreditsButton:position") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/keys = { +"times": PackedFloat32Array(0, 0.0333333, 0.0666667, 0.1, 0.133333, 0.166667, 0.2, 0.233333, 0.266667, 0.3, 0.333333, 0.366667, 0.4, 0.433333, 0.466667, 0.5), +"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), +"update": 0, +"values": [Vector2(80, 192), Vector2(55.5111, 192), Vector2(32.7111, 192), Vector2(11.6, 192), Vector2(-7.82223, 192), Vector2(-25.5556, 192), Vector2(-41.6, 192), Vector2(-55.9556, 192), Vector2(-68.6222, 192), Vector2(-79.6, 192), Vector2(-88.8889, 192), Vector2(-96.4889, 192), Vector2(-102.4, 192), Vector2(-106.622, 192), Vector2(-109.156, 192), Vector2(-110, 192)] +} +tracks/3/type = "value" +tracks/3/imported = false +tracks/3/enabled = true +tracks/3/path = NodePath("CanvasLayer/MainMenu/MenuButtons/QuitButton:position") +tracks/3/interp = 1 +tracks/3/loop_wrap = true +tracks/3/keys = { +"times": PackedFloat32Array(0, 0.0333333, 0.0666667, 0.1, 0.133333, 0.166667, 0.2, 0.233333, 0.266667, 0.3, 0.333333, 0.366667, 0.4, 0.433333, 0.466667, 0.5), +"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), +"update": 0, +"values": [Vector2(96, 224), Vector2(69.4489, 224), Vector2(44.7289, 224), Vector2(21.84, 224), Vector2(0.782219, 224), Vector2(-18.4445, 224), Vector2(-35.84, 224), Vector2(-51.4044, 224), Vector2(-65.1378, 224), Vector2(-77.04, 224), Vector2(-87.1111, 224), Vector2(-95.3511, 224), Vector2(-101.76, 224), Vector2(-106.338, 224), Vector2(-109.084, 224), Vector2(-110, 224)] +} +tracks/4/type = "value" +tracks/4/imported = false +tracks/4/enabled = true +tracks/4/path = NodePath("CanvasLayer/MainMenu/GameInfo/GameTitle:position") +tracks/4/interp = 1 +tracks/4/loop_wrap = true +tracks/4/keys = { +"times": PackedFloat32Array(0, 0.0333333, 0.0666667, 0.1, 0.133333, 0.166667, 0.2, 0.233333, 0.266667, 0.3, 0.333333, 0.366667, 0.4, 0.433333, 0.466667, 0.5), +"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), +"update": 0, +"values": [Vector2(311, 168), Vector2(354.049, 168), Vector2(394.129, 168), Vector2(431.24, 168), Vector2(465.382, 168), Vector2(496.556, 168), Vector2(524.76, 168), Vector2(549.996, 168), Vector2(572.262, 168), Vector2(591.56, 168), Vector2(607.889, 168), Vector2(621.249, 168), Vector2(631.64, 168), Vector2(639.062, 168), Vector2(643.516, 168), Vector2(645, 168)] +} +tracks/5/type = "value" +tracks/5/imported = false +tracks/5/enabled = true +tracks/5/path = NodePath("CanvasLayer/MainMenu/GameInfo/GameUnderline:position") +tracks/5/interp = 1 +tracks/5/loop_wrap = true +tracks/5/keys = { +"times": PackedFloat32Array(0, 0.0333333, 0.0666667, 0.1, 0.133333, 0.166667, 0.2, 0.233333, 0.266667, 0.3, 0.333333), +"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), +"update": 0, +"values": [Vector2(414, 194), Vector2(458.84, 194), Vector2(498.96, 194), Vector2(534.36, 194), Vector2(565.04, 194), Vector2(591, 194), Vector2(612.24, 194), Vector2(628.76, 194), Vector2(640.56, 194), Vector2(647.64, 194), Vector2(650, 194)] +} +tracks/6/type = "value" +tracks/6/imported = false +tracks/6/enabled = true +tracks/6/path = NodePath("CanvasLayer/MainMenu/GameInfo/Author:position") +tracks/6/interp = 1 +tracks/6/loop_wrap = true +tracks/6/keys = { +"times": PackedFloat32Array(0, 0.0333333, 0.0666667, 0.1, 0.133333, 0.166667, 0.2, 0.233333, 0.266667, 0.3, 0.333333, 0.366667, 0.4, 0.433333, 0.466667, 0.5), +"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), +"update": 0, +"values": [Vector2(344, 199), Vector2(383.182, 199), Vector2(419.662, 199), Vector2(453.44, 199), Vector2(484.516, 199), Vector2(512.889, 199), Vector2(538.56, 199), Vector2(561.529, 199), Vector2(581.796, 199), Vector2(599.36, 199), Vector2(614.222, 199), Vector2(626.382, 199), Vector2(635.84, 199), Vector2(642.596, 199), Vector2(646.649, 199), Vector2(648, 199)] +} +tracks/7/type = "value" +tracks/7/imported = false +tracks/7/enabled = true +tracks/7/path = NodePath("CanvasLayer/MainMenu/Rays:position") +tracks/7/interp = 1 +tracks/7/loop_wrap = true +tracks/7/keys = { +"times": PackedFloat32Array(0, 0.0333333, 0.0666667, 0.1, 0.133333, 0.166667, 0.2, 0.233333, 0.266667, 0.3, 0.333333, 0.366667, 0.4, 0.433333, 0.466667, 0.5), +"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), +"update": 0, +"values": [Vector2(112, 0), Vector2(97.5644, 0), Vector2(84.1244, 0), Vector2(71.68, 0), Vector2(60.2311, 0), Vector2(49.7778, 0), Vector2(40.32, 0), Vector2(31.8578, 0), Vector2(24.3911, 0), Vector2(17.92, 0), Vector2(12.4445, 0), Vector2(7.96444, 0), Vector2(4.48, 0), Vector2(1.99111, 0), Vector2(0.49778, 0), Vector2(0, 0)] +} + +[sub_resource type="Animation" id="Animation_awvye"] +resource_name = "hide_credits" +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("CanvasLayer/MainMenu/MenuButtons/PlayButton:position") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 0.0333333, 0.0666667, 0.1, 0.133333, 0.166667, 0.2, 0.233333, 0.266667, 0.3, 0.333333, 0.366667, 0.4, 0.433333, 0.466667, 0.5), +"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), +"update": 0, +"values": [Vector2(-110, 128), Vector2(-89.6356, 128), Vector2(-70.6756, 128), Vector2(-53.12, 128), Vector2(-36.9689, 128), Vector2(-22.2222, 128), Vector2(-8.87999, 128), Vector2(3.05778, 128), Vector2(13.5911, 128), Vector2(22.72, 128), Vector2(30.4444, 128), Vector2(36.7645, 128), Vector2(41.68, 128), Vector2(45.1911, 128), Vector2(47.2978, 128), Vector2(48, 128)] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath("CanvasLayer/MainMenu/MenuButtons/OptionsButton:position") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0, 0.0333333, 0.0666667, 0.1, 0.133333, 0.166667, 0.2, 0.233333, 0.266667, 0.3, 0.333333, 0.366667, 0.4, 0.433333, 0.466667, 0.5), +"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), +"update": 0, +"values": [Vector2(-110, 160), Vector2(-87.5733, 160), Vector2(-66.6933, 160), Vector2(-47.36, 160), Vector2(-29.5733, 160), Vector2(-13.3333, 160), Vector2(1.36001, 160), Vector2(14.5067, 160), Vector2(26.1067, 160), Vector2(36.16, 160), Vector2(44.6667, 160), Vector2(51.6267, 160), Vector2(57.04, 160), Vector2(60.9067, 160), Vector2(63.2267, 160), Vector2(64, 160)] +} +tracks/2/type = "value" +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/path = NodePath("CanvasLayer/MainMenu/MenuButtons/CreditsButton:position") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/keys = { +"times": PackedFloat32Array(0, 0.0333333, 0.0666667, 0.1, 0.133333, 0.166667, 0.2, 0.233333, 0.266667, 0.3, 0.333333, 0.366667, 0.4, 0.433333, 0.466667, 0.5), +"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), +"update": 0, +"values": [Vector2(-110, 192), Vector2(-85.5111, 192), Vector2(-62.7111, 192), Vector2(-41.6, 192), Vector2(-22.1778, 192), Vector2(-4.44444, 192), Vector2(11.6, 192), Vector2(25.9556, 192), Vector2(38.6222, 192), Vector2(49.6, 192), Vector2(58.8889, 192), Vector2(66.4889, 192), Vector2(72.4, 192), Vector2(76.6222, 192), Vector2(79.1555, 192), Vector2(80, 192)] +} +tracks/3/type = "value" +tracks/3/imported = false +tracks/3/enabled = true +tracks/3/path = NodePath("CanvasLayer/MainMenu/MenuButtons/QuitButton:position") +tracks/3/interp = 1 +tracks/3/loop_wrap = true +tracks/3/keys = { +"times": PackedFloat32Array(0, 0.0333333, 0.0666667, 0.1, 0.133333, 0.166667, 0.2, 0.233333, 0.266667, 0.3, 0.333333, 0.366667, 0.4, 0.433333, 0.466667, 0.5), +"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), +"update": 0, +"values": [Vector2(-110, 224), Vector2(-83.4489, 224), Vector2(-58.7289, 224), Vector2(-35.84, 224), Vector2(-14.7822, 224), Vector2(4.44445, 224), Vector2(21.84, 224), Vector2(37.4044, 224), Vector2(51.1378, 224), Vector2(63.04, 224), Vector2(73.1111, 224), Vector2(81.3511, 224), Vector2(87.76, 224), Vector2(92.3378, 224), Vector2(95.0844, 224), Vector2(96, 224)] +} +tracks/4/type = "value" +tracks/4/imported = false +tracks/4/enabled = true +tracks/4/path = NodePath("CanvasLayer/MainMenu/GameInfo/GameTitle:position") +tracks/4/interp = 1 +tracks/4/loop_wrap = true +tracks/4/keys = { +"times": PackedFloat32Array(0, 0.0333333, 0.0666667, 0.1, 0.133333, 0.166667, 0.2, 0.233333, 0.266667, 0.3, 0.333333, 0.366667, 0.4, 0.433333, 0.466667, 0.5), +"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), +"update": 0, +"values": [Vector2(645, 168), Vector2(601.951, 168), Vector2(561.871, 168), Vector2(524.76, 168), Vector2(490.618, 168), Vector2(459.444, 168), Vector2(431.24, 168), Vector2(406.004, 168), Vector2(383.738, 168), Vector2(364.44, 168), Vector2(348.111, 168), Vector2(334.751, 168), Vector2(324.36, 168), Vector2(316.938, 168), Vector2(312.484, 168), Vector2(311, 168)] +} +tracks/5/type = "value" +tracks/5/imported = false +tracks/5/enabled = true +tracks/5/path = NodePath("CanvasLayer/MainMenu/GameInfo/GameUnderline:position") +tracks/5/interp = 1 +tracks/5/loop_wrap = true +tracks/5/keys = { +"times": PackedFloat32Array(0.0666667, 0.1, 0.133333, 0.166667, 0.2, 0.233333, 0.266667, 0.3, 0.333333, 0.366667, 0.4, 0.433333, 0.466667, 0.5), +"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), +"update": 0, +"values": [Vector2(650, 194), Vector2(615.089, 194), Vector2(582.97, 194), Vector2(553.645, 194), Vector2(527.112, 194), Vector2(503.373, 194), Vector2(482.426, 194), Vector2(464.272, 194), Vector2(448.911, 194), Vector2(436.343, 194), Vector2(426.568, 194), Vector2(419.586, 194), Vector2(415.396, 194), Vector2(414, 194)] +} +tracks/6/type = "value" +tracks/6/imported = false +tracks/6/enabled = true +tracks/6/path = NodePath("CanvasLayer/MainMenu/GameInfo/Author:position") +tracks/6/interp = 1 +tracks/6/loop_wrap = true +tracks/6/keys = { +"times": PackedFloat32Array(0, 0.0333333, 0.0666667, 0.1, 0.133333, 0.166667, 0.2, 0.233333, 0.266667, 0.3, 0.333333, 0.366667, 0.4, 0.433333, 0.466667, 0.5), +"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), +"update": 0, +"values": [Vector2(648, 199), Vector2(608.818, 199), Vector2(572.338, 199), Vector2(538.56, 199), Vector2(507.484, 199), Vector2(479.111, 199), Vector2(453.44, 199), Vector2(430.471, 199), Vector2(410.204, 199), Vector2(392.64, 199), Vector2(377.778, 199), Vector2(365.618, 199), Vector2(356.16, 199), Vector2(349.404, 199), Vector2(345.351, 199), Vector2(344, 199)] +} +tracks/7/type = "value" +tracks/7/imported = false +tracks/7/enabled = true +tracks/7/path = NodePath("CanvasLayer/MainMenu/Rays:position") +tracks/7/interp = 1 +tracks/7/loop_wrap = true +tracks/7/keys = { +"times": PackedFloat32Array(0, 0.0333333, 0.0666667, 0.1, 0.133333, 0.166667, 0.2, 0.233333, 0.266667, 0.3, 0.333333, 0.366667, 0.4, 0.433333, 0.466667, 0.5), +"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), +"update": 0, +"values": [Vector2(0, 0), Vector2(14.4356, 0), Vector2(27.8756, 0), Vector2(40.32, 0), Vector2(51.7689, 0), Vector2(62.2222, 0), Vector2(71.68, 0), Vector2(80.1422, 0), Vector2(87.6089, 0), Vector2(94.08, 0), Vector2(99.5555, 0), Vector2(104.036, 0), Vector2(107.52, 0), Vector2(110.009, 0), Vector2(111.502, 0), Vector2(112, 0)] +} + +[sub_resource type="AnimationLibrary" id="AnimationLibrary_twm3r"] +_data = { +"RESET": SubResource("Animation_u1e5j"), +"hide_credits": SubResource("Animation_awvye"), +"hide_main": SubResource("Animation_ob40t"), +"show_credits": SubResource("Animation_2xxnd"), +"show_main": SubResource("Animation_5jiy0") +} + +[node name="Menu" type="Node"] +script = ExtResource("1_f186y") + +[node name="CanvasLayer" type="CanvasLayer" parent="."] + +[node name="MainMenu" type="Control" parent="CanvasLayer"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme = ExtResource("1_fowhs") + +[node name="Rays" type="ColorRect" parent="CanvasLayer/MainMenu"] +z_index = 100 +material = SubResource("ShaderMaterial_ygqow") +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="AnimationPlayer" type="AnimationPlayer" parent="CanvasLayer/MainMenu"] +root_node = NodePath("../../..") +libraries = { +"": SubResource("AnimationLibrary_twm3r") +} + +[node name="GameInfo" type="Control" parent="CanvasLayer/MainMenu"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="GameTitle" type="RichTextLabel" parent="CanvasLayer/MainMenu/GameInfo"] +z_index = 200 +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -185.0 +offset_top = -12.0 +offset_right = 183.0 +offset_bottom = 28.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_font_sizes/normal_font_size = 24 +bbcode_enabled = true +text = "[center]Game Title" +script = ExtResource("3_727p5") +key = "name" + +[node name="GameUnderline" type="ColorRect" parent="CanvasLayer/MainMenu/GameInfo"] +z_index = 200 +layout_mode = 1 +anchors_preset = 6 +anchor_left = 1.0 +anchor_top = 0.5 +anchor_right = 1.0 +anchor_bottom = 0.5 +offset_left = 10.0 +offset_top = 14.0 +offset_right = 330.525 +offset_bottom = 16.0 +grow_horizontal = 0 +grow_vertical = 2 + +[node name="Author" type="RichTextLabel" parent="CanvasLayer/MainMenu/GameInfo"] +z_index = 200 +layout_mode = 1 +anchors_preset = 6 +anchor_left = 1.0 +anchor_top = 0.5 +anchor_right = 1.0 +anchor_bottom = 0.5 +offset_left = 8.0 +offset_top = 19.0 +offset_right = 296.0 +offset_bottom = 59.0 +grow_horizontal = 0 +grow_vertical = 2 +bbcode_enabled = true +text = "[right]by Team Auboreal" +script = ExtResource("3_727p5") +key = "author" +alignment = "right" + +[node name="MenuButtons" type="Control" parent="CanvasLayer/MainMenu"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="PlayButton" parent="CanvasLayer/MainMenu/MenuButtons" instance=ExtResource("5_goj86")] +layout_mode = 0 +anchors_preset = 0 +anchor_top = 0.0 +anchor_bottom = 0.0 +offset_left = -110.0 +offset_top = 128.0 +offset_right = -19.13 +offset_bottom = 152.515 +grow_vertical = 1 + +[node name="OptionsButton" parent="CanvasLayer/MainMenu/MenuButtons" instance=ExtResource("5_goj86")] +layout_mode = 0 +anchors_preset = 0 +anchor_top = 0.0 +anchor_bottom = 0.0 +offset_left = -110.0 +offset_top = 160.0 +offset_right = -19.13 +offset_bottom = 184.515 +grow_vertical = 1 +text = "Options" + +[node name="CreditsButton" parent="CanvasLayer/MainMenu/MenuButtons" instance=ExtResource("5_goj86")] +layout_mode = 0 +anchors_preset = 0 +anchor_top = 0.0 +anchor_bottom = 0.0 +offset_left = -110.0 +offset_top = 192.0 +offset_right = -19.13 +offset_bottom = 216.515 +grow_vertical = 1 +text = "Credits" + +[node name="QuitButton" parent="CanvasLayer/MainMenu/MenuButtons" instance=ExtResource("5_goj86")] +layout_mode = 0 +anchors_preset = 0 +anchor_top = 0.0 +anchor_bottom = 0.0 +offset_left = -110.0 +offset_top = 224.0 +offset_right = -19.13 +offset_bottom = 248.515 +grow_vertical = 1 +text = "Quit" + +[node name="Credits" type="Control" parent="CanvasLayer/MainMenu"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("6_b724h") + +[connection signal="clicked" from="CanvasLayer/MainMenu/MenuButtons/PlayButton" to="." method="_on_play_button_clicked"] +[connection signal="clicked" from="CanvasLayer/MainMenu/MenuButtons/OptionsButton" to="." method="_on_options_button_clicked"] +[connection signal="clicked" from="CanvasLayer/MainMenu/MenuButtons/CreditsButton" to="." method="_on_credits_button_clicked"] +[connection signal="clicked" from="CanvasLayer/MainMenu/MenuButtons/QuitButton" to="." method="_on_quit_button_clicked"] diff --git a/components/Menu/MenuButton.tscn b/components/Menu/MenuButton.tscn new file mode 100644 index 0000000..a866a05 --- /dev/null +++ b/components/Menu/MenuButton.tscn @@ -0,0 +1,45 @@ +[gd_scene load_steps=5 format=3 uid="uid://cmyjaeahyipq4"] + +[ext_resource type="Script" path="res://components/Menu/menu_button.gd" id="1_kxfx8"] +[ext_resource type="PackedScene" uid="uid://dykc1mgg5uopw" path="res://components/Cursor/MouseHandler.tscn" id="2_g6apf"] +[ext_resource type="Script" path="res://components/Menu/background_highlight.gd" id="3_cxaw3"] + +[sub_resource type="RectangleShape2D" id="RectangleShape2D_oahx4"] +size = Vector2(147, 22) + +[node name="MenuButton" type="RichTextLabel"] +clip_contents = false +anchors_preset = 4 +anchor_top = 0.5 +anchor_bottom = 0.5 +offset_left = 48.0 +offset_top = -52.0 +offset_right = 138.87 +offset_bottom = -27.485 +grow_vertical = 2 +theme_override_font_sizes/normal_font_size = 16 +bbcode_enabled = true +text = "Play" +script = ExtResource("1_kxfx8") + +[node name="MouseHandler" parent="." instance=ExtResource("2_g6apf")] + +[node name="CollisionShape2D" type="CollisionShape2D" parent="MouseHandler"] +position = Vector2(36.5, 7) +shape = SubResource("RectangleShape2D_oahx4") + +[node name="BackgroundHighlight" type="ColorRect" parent="."] +show_behind_parent = true +layout_mode = 0 +offset_left = -420.0 +offset_top = -8.0 +offset_right = -142.0 +offset_bottom = 22.0 +color = Color(0, 0, 0, 1) +script = ExtResource("3_cxaw3") + +[connection signal="clicked" from="MouseHandler" to="." method="_on_play_mouse_handler_clicked"] +[connection signal="hovered" from="MouseHandler" to="." method="_on_mouse_handler_hovered"] +[connection signal="hovered" from="MouseHandler" to="BackgroundHighlight" method="_on_mouse_handler_hovered"] +[connection signal="unhovered" from="MouseHandler" to="." method="_on_mouse_handler_unhovered"] +[connection signal="unhovered" from="MouseHandler" to="BackgroundHighlight" method="_on_mouse_handler_unhovered"] diff --git a/components/Menu/background_highlight.gd b/components/Menu/background_highlight.gd new file mode 100644 index 0000000..388900b --- /dev/null +++ b/components/Menu/background_highlight.gd @@ -0,0 +1,35 @@ +extends ColorRect + +var tween + +func _on_mouse_handler_hovered() -> void: + if tween: + tween.kill() + + tween = create_tween() + tween.set_ease(Tween.EASE_OUT) + tween.set_trans(Tween.TRANS_BACK) + tween.tween_property(self, "position:x", -136, 0.5) + tween.set_ease(Tween.EASE_IN_OUT) + tween.set_trans(Tween.TRANS_QUAD) + tween.tween_property(self, "position:x", -128, 0.25) + tween.set_ease(Tween.EASE_IN_OUT) + tween.tween_property(self, "position:x", -132, 0.25) + tween.tween_callback(func(): + tween = create_tween() + tween.set_loops() + tween.set_ease(Tween.EASE_IN_OUT) + tween.set_trans(Tween.TRANS_QUAD) + tween.tween_property(self, "position:x", -136, 1) + tween.tween_property(self, "position:x", -132, 1) + ) + + +func _on_mouse_handler_unhovered() -> void: + if tween: + tween.kill() + + tween = create_tween() + tween.set_ease(Tween.EASE_OUT) + tween.set_trans(Tween.TRANS_BACK) + tween.tween_property(self, "position:x", -420, 0.5) diff --git a/components/Menu/credits.gd b/components/Menu/credits.gd new file mode 100644 index 0000000..77bb034 --- /dev/null +++ b/components/Menu/credits.gd @@ -0,0 +1,69 @@ +extends Control + +var _data + +const TEXT_PADDING = 30 + +var items = [] +var tween + +func _ready() -> void: + if get_tree().root.has_node("Data"): + _data = get_tree().root.get_node("Data") + + if _data.data.has("metadata") and _data.data.metadata.has("credits"): + var credits = _data.data.metadata.credits + var sections = credits.keys() + var y = 50 + + for section in sections: + var section_text = RichTextLabel.new() + section_text.position = Vector2(-120, y) + section_text.size = Vector2(40, TEXT_PADDING) + section_text.bbcode_enabled = true + section_text.text = "[center]%s" % [section] + add_child(section_text) + items.push_back(section_text) + y += TEXT_PADDING + var people = credits[section].keys() + + for person in people: + var person_text = RichTextLabel.new() + person_text.position = Vector2(-120, y) + person_text.size = Vector2(40, TEXT_PADDING) + person_text.bbcode_enabled = true + person_text.text = "[center]%s" % [person] + add_child(person_text) + items.push_back(person_text) + y += TEXT_PADDING + + var links = credits[section][person].keys() + + for link in links: + var url = credits[section][person][link] + + y += TEXT_PADDING + + +func show_credits(): + if tween: + tween.kill() + + tween = create_tween() + tween.set_ease(Tween.EASE_OUT) + tween.set_trans(Tween.TRANS_BACK) + + for item in items: + tween.tween_property(item, "position:x", 320, 0.25) + +func hide_credits(): + if tween: + tween.kill() + + tween = create_tween() + tween.set_parallel() + tween.set_ease(Tween.EASE_OUT) + tween.set_trans(Tween.TRANS_QUAD) + + for item in items: + tween.tween_property(item, "position:x", -120, 0.25) diff --git a/components/Menu/god_rays.gdshader b/components/Menu/god_rays.gdshader new file mode 100644 index 0000000..b94552c --- /dev/null +++ b/components/Menu/god_rays.gdshader @@ -0,0 +1,109 @@ +/* +Shader from Godot Shaders - the free shader library. +godotshaders.com/shader/god-rays + +Feel free to use, improve and change this shader according to your needs +and consider sharing the modified result on godotshaders.com. +*/ + +shader_type canvas_item; + +uniform float angle = -0.3; +uniform float position = -0.2; +uniform float spread : hint_range(0.0, 1.0) = 0.5; +uniform float cutoff : hint_range(-1.0, 1.0) = 0.1; +uniform float falloff : hint_range(0.0, 1.0) = 0.2; +uniform float edge_fade : hint_range(0.0, 1.0) = 0.15; + +uniform float speed = 1.0; +uniform float ray1_density = 8.0; +uniform float ray2_density = 30.0; +uniform float ray2_intensity : hint_range(0.0, 1.0) = 0.3; + +uniform vec4 color = vec4(1.0, 0.9, 0.65, 0.8); + +uniform bool hdr = false; +uniform float seed = 5.0; + +uniform sampler2D SCREEN_TEXTURE : hint_screen_texture, filter_linear_mipmap; + +// Random and noise functions from Book of Shader's chapter on Noise. +float random(vec2 _uv) { + return fract(sin(dot(_uv.xy, + vec2(12.9898, 78.233))) * + 43758.5453123); +} + +float noise (in vec2 uv) { + vec2 i = floor(uv); + vec2 f = fract(uv); + + // Four corners in 2D of a tile + float a = random(i); + float b = random(i + vec2(1.0, 0.0)); + float c = random(i + vec2(0.0, 1.0)); + float d = random(i + vec2(1.0, 1.0)); + + + // Smooth Interpolation + + // Cubic Hermine Curve. Same as SmoothStep() + vec2 u = f * f * (3.0-2.0 * f); + + // Mix 4 coorners percentages + return mix(a, b, u.x) + + (c - a)* u.y * (1.0 - u.x) + + (d - b) * u.x * u.y; +} + +mat2 rotate(float _angle){ + return mat2(vec2(cos(_angle), -sin(_angle)), + vec2(sin(_angle), cos(_angle))); +} + +vec4 screen(vec4 base, vec4 blend){ + return 1.0 - (1.0 - base) * (1.0 - blend); +} + +void fragment() +{ + + // Rotate, skew and move the UVs + vec2 transformed_uv = ( rotate(angle) * (UV - position) ) / ( (UV.y + spread) - (UV.y * spread) ); + + // Animate the ray according the the new transformed UVs + vec2 ray1 = vec2(transformed_uv.x * ray1_density + sin(TIME * 0.1 * speed) * (ray1_density * 0.2) + seed, 1.0); + vec2 ray2 = vec2(transformed_uv.x * ray2_density + sin(TIME * 0.2 * speed) * (ray1_density * 0.2) + seed, 1.0); + + // Cut off the ray's edges + float cut = step(cutoff, transformed_uv.x) * step(cutoff, 1.0 - transformed_uv.x); + ray1 *= cut; + ray2 *= cut; + + // Apply the noise pattern (i.e. create the rays) + float rays; + + if (hdr){ + // This is not really HDR, but check this to not clamp the two merged rays making + // their values go over 1.0. Can make for some nice effect + rays = noise(ray1) + (noise(ray2) * ray2_intensity); + } + else{ + rays = clamp(noise(ray1) + (noise(ray2) * ray2_intensity), 0., 1.); + } + + // Fade out edges + rays *= smoothstep(0.0, falloff, (1.0 - UV.y)); // Bottom + rays *= smoothstep(0.0 + cutoff, edge_fade + cutoff, transformed_uv.x); // Left + rays *= smoothstep(0.0 + cutoff, edge_fade + cutoff, 1.0 - transformed_uv.x); // Right + + // Color to the rays + vec3 shine = vec3(rays) * color.rgb; + + // Try different blending modes for a nicer effect. "Screen" is included in the code, + // but take a look at https://godotshaders.com/snippet/blending-modes/ for more. + // With "Screen" blend mode: + shine = screen(texture(SCREEN_TEXTURE, SCREEN_UV), vec4(color)).rgb; + + COLOR = vec4(shine, rays * color.a); +} \ No newline at end of file diff --git a/components/Menu/menu.gd b/components/Menu/menu.gd new file mode 100644 index 0000000..dbc7bac --- /dev/null +++ b/components/Menu/menu.gd @@ -0,0 +1,41 @@ +extends Base + +@onready var animation_player: AnimationPlayer = $CanvasLayer/MainMenu/AnimationPlayer +@onready var credits: Control = $CanvasLayer/MainMenu/Credits + +var menu_state = "start" + +func _process(delta: float) -> void: + if menu_state == "start" and Input.is_action_just_pressed("left_click"): + menu_state = "main" + animation_player.play("show_main") + if Input.is_action_just_pressed("escape"): + match menu_state: + "main": + menu_state = "start" + animation_player.play("hide_main") + "credits": + menu_state = "main" + animation_player.play("hide_credits") + credits.hide_credits() + + +func _on_play_button_clicked() -> void: + if _data: + get_tree().change_scene_to_packed(load(_data.data.metadata.start_scene)) + else: + _error("No start scene defined") + + +func _on_options_button_clicked() -> void: + pass # Replace with function body. + + +func _on_credits_button_clicked() -> void: + menu_state = "credits" + animation_player.play("show_credits") + credits.show_credits() + + +func _on_quit_button_clicked() -> void: + get_tree().quit() diff --git a/components/Menu/menu_button.gd b/components/Menu/menu_button.gd new file mode 100644 index 0000000..67a0437 --- /dev/null +++ b/components/Menu/menu_button.gd @@ -0,0 +1,24 @@ +extends RichTextLabel + +signal clicked + +var color_tween + +func _on_play_mouse_handler_clicked() -> void: + clicked.emit() + + +func _on_mouse_handler_hovered() -> void: + if color_tween: + color_tween.kill() + + color_tween = create_tween() + color_tween.tween_property(self, "self_modulate", Color.GOLD, 0.25) + + +func _on_mouse_handler_unhovered() -> void: + if color_tween: + color_tween.kill() + + color_tween = create_tween() + color_tween.tween_property(self, "self_modulate", Color.WHITE, 0.25) diff --git a/components/Menu/populate_from_metadata.gd b/components/Menu/populate_from_metadata.gd new file mode 100644 index 0000000..b9ce4e7 --- /dev/null +++ b/components/Menu/populate_from_metadata.gd @@ -0,0 +1,13 @@ +extends RichTextLabel + +@export var key := "" +@export var alignment := "center" + +var _data + +func _ready() -> void: + if get_tree().root.has_node("Data"): + _data = get_tree().root.get_node("Data") + + if _data.data.has("metadata"): + text = "[%s]%s" % [alignment, _data.data.metadata[key]] diff --git a/components/Menu/square-menu.svg b/components/Menu/square-menu.svg new file mode 100644 index 0000000..b52cd87 --- /dev/null +++ b/components/Menu/square-menu.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/components/Menu/square-menu.svg.import b/components/Menu/square-menu.svg.import new file mode 100644 index 0000000..6a7b3ee --- /dev/null +++ b/components/Menu/square-menu.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cp8tbups5d6p7" +path="res://.godot/imported/square-menu.svg-7299523024a845ac9eafa86412b4d5c3.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://components/Menu/square-menu.svg" +dest_files=["res://.godot/imported/square-menu.svg-7299523024a845ac9eafa86412b4d5c3.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/components/Persister/Persister.tscn b/components/Persister/Persister.tscn new file mode 100644 index 0000000..8bf81ad --- /dev/null +++ b/components/Persister/Persister.tscn @@ -0,0 +1,12 @@ +[gd_scene load_steps=2 format=3 uid="uid://pht0rn54n3t8"] + +[ext_resource type="Script" path="res://components/Persister/persister.gd" id="1_taxaa"] + +[node name="Persister" type="Node"] +script = ExtResource("1_taxaa") + +[node name="Timer" type="Timer" parent="."] +wait_time = 20.0 +autostart = true + +[connection signal="timeout" from="Timer" to="." method="_on_timer_timeout"] diff --git a/components/Persister/data_scope.gd b/components/Persister/data_scope.gd new file mode 100644 index 0000000..e71d732 --- /dev/null +++ b/components/Persister/data_scope.gd @@ -0,0 +1,11 @@ +class_name PersisterEnums + +enum Scope { + PERMANENT, # Stays forever + SAVE, # Persists through a save + GAME, # Persists through runs + RUN, # Persists through an entire run of the game + ROUND, # Persists through a round (if applicable, e.g. between ifa round is 2 mins between shops) + ROOM, # Persists within a game room (if applicable) + UNKNOWN +} diff --git a/components/Persister/hard-drive.svg b/components/Persister/hard-drive.svg new file mode 100644 index 0000000..02da606 --- /dev/null +++ b/components/Persister/hard-drive.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/components/Persister/hard-drive.svg.import b/components/Persister/hard-drive.svg.import new file mode 100644 index 0000000..9549ea2 --- /dev/null +++ b/components/Persister/hard-drive.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bru35jf108ajj" +path="res://.godot/imported/hard-drive.svg-f9677d2ad3a8226891a78779d376d996.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://components/Persister/hard-drive.svg" +dest_files=["res://.godot/imported/hard-drive.svg-f9677d2ad3a8226891a78779d376d996.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/components/Persister/information.txt b/components/Persister/information.txt new file mode 100644 index 0000000..c004363 --- /dev/null +++ b/components/Persister/information.txt @@ -0,0 +1,10 @@ +name: Persister +short: Data persister to persist data through locations +description: A data persister that is used to persist data through the game, + run, round, or room. A function is called to set data with a key in one + of the categories and then other functions can be called to clear + the data for a key or all data in a category. +accent: #a1a166 +log_category: PER +icon: res://components/Persister/hard-drive.svg +version: v1.0.0 diff --git a/components/Persister/persister-toolbar.gd b/components/Persister/persister-toolbar.gd new file mode 100644 index 0000000..752ce0c --- /dev/null +++ b/components/Persister/persister-toolbar.gd @@ -0,0 +1,5 @@ +func save_one(parent): + parent.change_save(1) + +func save_two(parent): + parent.change_save(2) diff --git a/components/Persister/persister.gd b/components/Persister/persister.gd new file mode 100644 index 0000000..706d642 --- /dev/null +++ b/components/Persister/persister.gd @@ -0,0 +1,334 @@ +@icon("res://components/Persister/hard-drive.svg") +extends Base +## Data persister to persist data through locations +## +## A data persister that is used to persist data through the game, run, round, +## or room. A function is called to set data with a key in one of the categories +## and then other functions can be called to clear the data for a key or all +## data in a category. + +signal data_persisted(key: String, value, category: PersisterEnums.Scope) + +var _persisted = {} + + +func _spawned(): + if _triggerer: + _info("Connecting to data scope triggers") + _triggerer.listen("game", _on_game_triggered) + _triggerer.listen("run", _on_run_triggered) + _triggerer.listen("round", _on_round_triggered) + _triggerer.listen("room", _on_room_triggered) + _load() + + +## Store data in a category +func persist_data(key: String, value, category := PersisterEnums.Scope.RUN) -> void: + # Only allow ints, bools or strings to be persisted and as Strings + if value is int: + value = str(value) + + if value is bool: + value = str(value) + + if not value is String: + _warn("Attempted to persist data for key <%s> with an invalid data type |%s|" % [key, type_string(typeof(value))]) + return + + # Create category if does not exist + if not _persisted.has(category): + _info("Created new persister category {%s}" % [_get_category_name(category)]) + _persisted[category] = {} + + # If key is already set to value exit early + if _persisted[category].has(key) and _persisted[category][key] == value: + return + + _info("Set key <%s> in category {%s} to value |%s|" % [key, _get_category_name(category), value]) + _persisted[category][key] = value + + _emit_change(key, value, category) + + +## Get the value associated with a key in the highest priority or specified category +func get_value(key: String, category: PersisterEnums.Scope = PersisterEnums.Scope.UNKNOWN): + if category == PersisterEnums.Scope.UNKNOWN: + category = _get_key_category(key) + + if category == PersisterEnums.Scope.UNKNOWN: + return null + + if _persisted[category][key].is_valid_int(): + return int(_persisted[category][key]) + + if _persisted[category][key] == "true" || _persisted[category][key] == "false": + return _persisted[category][key] == "true" + + return _persisted[category][key] + + +## Delete data associated with a key from a certain category +func clear_data(key: String, category := PersisterEnums.Scope.RUN) -> void: + if not _persisted.has(category): + _info("Attempted to clear key <%s> in category {%s} but the category did not exist" % [key, _get_category_name(category)]) + return + + if not _persisted[category].has(key): + _info("Attempted to clear key <%s> in category {%s} but it did not exist" % [key, _get_category_name(category)]) + return + + _info("Cleared key <%s> in category {%s}" % [key, _get_category_name(category)]) + _persisted[category].erase(key) + + _emit_removal(key, category) + + +## Delete a category including all data within it +func clear_category(category := PersisterEnums.Scope.RUN) -> void: + if not _persisted.has(category): + _error("Attempted to clear category {%s} but it did not exist" % [_get_category_name(category)]) + return + + var keys = _persisted[category].keys() + + _info("Cleared category {%s}" % [_get_category_name(category)]) + _persisted.erase(category) + + for key in keys: + _emit_removal(key, category) + + +## Add a number to a number value with the given key from a certain category +func change_value(key: String, value: int, category := PersisterEnums.Scope.RUN) -> void: + if not _persisted.has(category): + _persisted[category] = {} + + if not _persisted[category].has(key): + persist_data(key, value, category) + return + + if is_nan(int(_persisted[category][key])): + _error("Attempted to add number |%d| to key <%s> in category {%s} that is not a number (value: |%s|)" % [value, key, category, _persisted[category][key]]) + return + + var old_value = int(_persisted[category][key]) + _info("Added number |%d| to key <%s> in category {%s} (old: |%d|) (new: |%d|)" % [value, key, _get_category_name(category), old_value, old_value + value]) + persist_data(key, value + old_value, category) + + +func change_save(index: int): + _save() + persist_data("save", index) + _load() + + +func save(): + _save() + + +func _emit_change(key: String, value: String, category: PersisterEnums.Scope) -> void: + var key_category = _get_key_category(key) + + # Only emit change if the category set has the highest priority + if key_category == category: + if is_nan(int(value)): + data_persisted.emit(key, value, category) + else: + data_persisted.emit(key, int(value), category) + + +func _emit_removal(key: String, category: PersisterEnums.Scope) -> void: + var key_category = _get_key_category(key) + + if key_category == PersisterEnums.Scope.UNKNOWN: + return + + var category_priority = _get_category_priority(category) + var key_category_priority = _get_category_priority(key_category) + + if category_priority > key_category_priority: + var value = _persister[key_category][key] + + if is_nan(int(value)): + data_persisted.emit(key, value, key_category) + else: + data_persisted.emit(key, int(value), key_category) + + +func _get_category_name(category: PersisterEnums.Scope) -> String: + match category: + PersisterEnums.Scope.PERMANENT: + return "permanent" + PersisterEnums.Scope.SAVE: + return "save" + PersisterEnums.Scope.GAME: + return "game" + PersisterEnums.Scope.RUN: + return "run" + PersisterEnums.Scope.ROUND: + return "round" + PersisterEnums.Scope.ROOM: + return "room" + _: + return "unknown" + + +func _get_category_priority(category: PersisterEnums.Scope) -> int: + match category: + PersisterEnums.Scope.PERMANENT: + return 1 + PersisterEnums.Scope.SAVE: + return 2 + PersisterEnums.Scope.GAME: + return 3 + PersisterEnums.Scope.RUN: + return 4 + PersisterEnums.Scope.ROUND: + return 5 + PersisterEnums.Scope.ROOM: + return 6 + _: + return 0 + + +func _get_key_category(key: String) -> PersisterEnums.Scope: + var category_order = [] + + for category in PersisterEnums.Scope: + var category_value = PersisterEnums.Scope[category] + + category_order.push_back( + { + "priority": _get_category_priority(category_value), + "category": category_value + } + ) + + category_order.sort_custom(func(a, b): + return a.priority > b.priority + ) + + for value in category_order: + var category = value.category + + if _persisted.has(category): + if _persisted[category].has(key): + return category + + return PersisterEnums.Scope.UNKNOWN + + +func _get_all_category_data(category: PersisterEnums.Scope) -> Dictionary: + if not _persisted.has(category): + return {} + + return _persisted[category] + + +func _on_game_triggered(data: Dictionary) -> void: + clear_category(PersisterEnums.Scope.GAME) + + +func _on_run_triggered(data: Dictionary) -> void: + clear_category(PersisterEnums.Scope.RUN) + + +func _on_round_triggered(data: Dictionary) -> void: + clear_category(PersisterEnums.Scope.ROUND) + + +func _on_room_triggered(data: Dictionary) -> void: + clear_category(PersisterEnums.Scope.ROOM) + + +func _save(): + _info("Saving") + _save_permanent_data() + _save_save_data() + + +func _load(): + _info("Loading") + _load_permanent_data() + + if not get_value("save"): + _info("Creating starting save") + persist_data("save", 1, PersisterEnums.Scope.PERMANENT) + + clear_category(PersisterEnums.Scope.SAVE) + _load_save_data() + + +func _save_permanent_data(): + var save_file = FileAccess.open("user://data.json", FileAccess.WRITE) + + var data = _get_all_category_data(PersisterEnums.Scope.PERMANENT) + var data_string = JSON.stringify(data) + + _info("Saving permanent save data") + + save_file.store_line(data_string) + + +func _save_save_data(): + if not FileAccess.file_exists("user://saves"): + DirAccess.make_dir_absolute("user://saves") + + var save = get_value("save") + var save_file = FileAccess.open("user://saves/save-%d.json" % [save], FileAccess.WRITE) + + var data = _get_all_category_data(PersisterEnums.Scope.SAVE) + var data_string = JSON.stringify(data) + + _info("Saving save save data") + + save_file.store_line(data_string) + + +func _load_permanent_data(): + var save_file = FileAccess.open("user://data.json", FileAccess.READ) + + if not save_file: + return + + _info("Loading permanent save data") + var json_string = "" + + while save_file.get_position() < save_file.get_length(): + json_string += save_file.get_line() + + var json = JSON.new() + + var result = json.parse(json_string) + var data = json.get_data() + + for key in data: + Persister.persist_data(key, data[key], PersisterEnums.Scope.PERMANENT) + + +func _load_save_data(): + var save = get_value("save") + var save_file = FileAccess.open("user://saves/save-%s.json" % [save], FileAccess.READ) + + if not save_file: + return + + _info("Loading save save data") + var json_string = "" + + while save_file.get_position() < save_file.get_length(): + json_string += save_file.get_line() + + var json = JSON.new() + + var result = json.parse(json_string) + var data = json.get_data() + + Persister.clear_category(PersisterEnums.Scope.SAVE) + + for key in data: + Persister.persist_data(key, data[key], PersisterEnums.Scope.SAVE) + + +func _on_timer_timeout(): + _save() diff --git a/components/Persister/toolbar.txt b/components/Persister/toolbar.txt new file mode 100644 index 0000000..a5a1bd8 --- /dev/null +++ b/components/Persister/toolbar.txt @@ -0,0 +1,10 @@ +save[] + type: button + name: Swap Save 1 + accent: #11a1a1 + function: save_one +save2[] + type: button + name: Swap Save 2 + accent: #11a1a1 + function: save_two diff --git a/components/Settings/Settings.tscn b/components/Settings/Settings.tscn new file mode 100644 index 0000000..a9306aa --- /dev/null +++ b/components/Settings/Settings.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://d3uc4ryq5tqby"] + +[ext_resource type="Script" path="res://components/Settings/settings.gd" id="1_wmbcp"] + +[node name="Settings" type="Node"] +script = ExtResource("1_wmbcp") diff --git a/components/Settings/information.txt b/components/Settings/information.txt new file mode 100644 index 0000000..e69de29 diff --git a/components/Settings/settings.gd b/components/Settings/settings.gd new file mode 100644 index 0000000..dc34858 --- /dev/null +++ b/components/Settings/settings.gd @@ -0,0 +1,16 @@ +@icon("res://components/Audio/music.svg") +extends Base + + +func _ready(): + #_log_category = "AUD" + #_log_icon = "res://components/Audio/music.svg" + #_log_color = "#32ad61" + super() + _info("Audio is active") + if _triggerer: + _triggerer.listen("any", _on_trigger) + + +func _on_trigger(data: Dictionary) -> void: + pass diff --git a/components/Settings/settings.svg b/components/Settings/settings.svg new file mode 100644 index 0000000..2d72c63 --- /dev/null +++ b/components/Settings/settings.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/components/Settings/settings.svg.import b/components/Settings/settings.svg.import new file mode 100644 index 0000000..aef76a8 --- /dev/null +++ b/components/Settings/settings.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c1toq8e1yg4he" +path="res://.godot/imported/settings.svg-0c63f5959ea52eb948e9c6dfb59e2f91.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://components/Settings/settings.svg" +dest_files=["res://.godot/imported/settings.svg-0c63f5959ea52eb948e9c6dfb59e2f91.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/components/Triggerer/TriggerReceiver.tscn b/components/Triggerer/TriggerReceiver.tscn new file mode 100644 index 0000000..f762c18 --- /dev/null +++ b/components/Triggerer/TriggerReceiver.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://cgivlj3yp8nsy"] + +[ext_resource type="Script" path="res://components/Triggerer/trigger_receiver.gd" id="1_rb2wc"] + +[node name="TriggerReceiver" type="Node"] +script = ExtResource("1_rb2wc") diff --git a/components/Triggerer/Triggerer.tscn b/components/Triggerer/Triggerer.tscn new file mode 100644 index 0000000..bf52ae0 --- /dev/null +++ b/components/Triggerer/Triggerer.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://rjec7f6cseh"] + +[ext_resource type="Script" path="res://components/Triggerer/triggerer.gd" id="1_e3bhf"] + +[node name="Triggerer" type="Node"] +script = ExtResource("1_e3bhf") diff --git a/components/Triggerer/antenna.svg b/components/Triggerer/antenna.svg new file mode 100644 index 0000000..d448672 --- /dev/null +++ b/components/Triggerer/antenna.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/components/Triggerer/antenna.svg.import b/components/Triggerer/antenna.svg.import new file mode 100644 index 0000000..a4034d5 --- /dev/null +++ b/components/Triggerer/antenna.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://hnqq5c8erlns" +path="res://.godot/imported/antenna.svg-9e84cc3637193460d89c85029a059bef.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://components/Triggerer/antenna.svg" +dest_files=["res://.godot/imported/antenna.svg-9e84cc3637193460d89c85029a059bef.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/components/Triggerer/editor.gd b/components/Triggerer/editor.gd new file mode 100644 index 0000000..ca3abe1 --- /dev/null +++ b/components/Triggerer/editor.gd @@ -0,0 +1,8 @@ +@tool +extends EditorPlugin + +func _enter_tree(): + add_custom_type("TriggerReceiver", "Node", preload("res://components/Triggerer/trigger_receiver.gd"), preload("res://components/Triggerer/antenna.svg")) + +func _exit_tree(): + remove_custom_type("TriggerReceiver") diff --git a/components/Triggerer/information.txt b/components/Triggerer/information.txt new file mode 100644 index 0000000..a14833c --- /dev/null +++ b/components/Triggerer/information.txt @@ -0,0 +1,11 @@ +name: Triggerer +short: A trigger object to trigger things and read in triggers +description: An object used for other objects to connect to for triggers. + Objects can call the read function with a callback function to + read in future triggers of a key to that callback. Objects can + then call the trigger function to trigger everything listening + to a key. +accent: #dada00 +log_category: TRI +icon: res://components/Triggerer/radio.svg +version: v1.0.0 diff --git a/components/Triggerer/radio.svg b/components/Triggerer/radio.svg new file mode 100644 index 0000000..2173a3f --- /dev/null +++ b/components/Triggerer/radio.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/components/Triggerer/radio.svg.import b/components/Triggerer/radio.svg.import new file mode 100644 index 0000000..f672b6c --- /dev/null +++ b/components/Triggerer/radio.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cxpp5fn5c75wj" +path="res://.godot/imported/radio.svg-17a71d0d8df639a42271899eb9c43d1e.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://components/Triggerer/radio.svg" +dest_files=["res://.godot/imported/radio.svg-17a71d0d8df639a42271899eb9c43d1e.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/components/Triggerer/toolbar.txt b/components/Triggerer/toolbar.txt new file mode 100644 index 0000000..10d80ab --- /dev/null +++ b/components/Triggerer/toolbar.txt @@ -0,0 +1,20 @@ +trigger-event[] + type: modal + name: Trigger Event + accent: #11a1a1 + modal[] + name[] + type: input + name: Event Name + +trigger-event-data[] + type: modal + name: Trigger Event w/ Data + accent: #2181c1 + modal[] + name[] + type: input + name: Event Name + data[] + type: dictionary + name: Event Data diff --git a/components/Triggerer/trigger_receiver.gd b/components/Triggerer/trigger_receiver.gd new file mode 100644 index 0000000..8a12829 --- /dev/null +++ b/components/Triggerer/trigger_receiver.gd @@ -0,0 +1,22 @@ +@icon("res://components/Triggerer/antenna.svg") +class_name TriggerReceiver +extends Node + +signal received(data: Dictionary) + +@export var keys: Array[String] + +var _triggerer + + +func _ready(): + if get_tree().root.has_node("Triggerer"): + _triggerer = get_tree().root.get_node("Triggerer") + + if _triggerer: + for key in keys: + _triggerer.listen(key, _on_received) + + +func _on_received(data: Dictionary): + received.emit(data) diff --git a/components/Triggerer/triggerer.gd b/components/Triggerer/triggerer.gd new file mode 100644 index 0000000..ada5aa0 --- /dev/null +++ b/components/Triggerer/triggerer.gd @@ -0,0 +1,36 @@ +@icon("res://components/Triggerer/radio.svg") +extends Base +## A trigger object to trigger things and read in triggers +## +## An object used for other objects to connect to for triggers. +## Objects can call the read function with a callback function to +## read in future triggers of a key to that callback. Objects can +## then call the trigger function to trigger everything listening +## to a key. + +var _connections = {} + + +## Trigger an event to be read in by other objects +func trigger(key: String, data: Dictionary = {}) -> void: + _info("Triggered key ∧%s∧" % [key]) + + data.trigger = key + + if _connections.has(key): + for callback in _connections[key]: + callback.call(data) + + if _connections.has("any"): + for callback in _connections["any"]: + callback.call(data) + + +## Read in future triggers of a key to a callback +func listen(key: String, callback: Callable) -> void: + if not _connections.has(key): + _info("Created new connection key ∧%s∧" % [key]) + _connections[key] = [] + + _info("Callback ∨%s∨ on →%s← added to key ∧%s∧" % [callback.get_method(), callback.get_object().name, key]) + _connections[key].push_back(callback) diff --git a/components/Unlocks/Unlocks.tscn b/components/Unlocks/Unlocks.tscn new file mode 100644 index 0000000..7b26b7a --- /dev/null +++ b/components/Unlocks/Unlocks.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://cwcttvh7s42ca"] + +[ext_resource type="Script" path="res://components/Unlocks/unlocks.gd" id="1_ege4w"] + +[node name="Unlocks" type="Node"] +script = ExtResource("1_ege4w") diff --git a/components/Unlocks/information.txt b/components/Unlocks/information.txt new file mode 100644 index 0000000..e69de29 diff --git a/components/Unlocks/unlocks.gd b/components/Unlocks/unlocks.gd new file mode 100644 index 0000000..a31c3d6 --- /dev/null +++ b/components/Unlocks/unlocks.gd @@ -0,0 +1,127 @@ +class_name UnlocksType +extends Base + +# Signals + +# Constants +const LOG_CATEGORY = "LOCK" + +var unlocks = {} + +func unlock_item(key: String, category: String): + if not unlocks.has(category): + if _logger: _logger.error("Attempted to unlock item %s in invalid category %s" % [key, category], LOG_CATEGORY) + return + + if not unlocks[category].has(key): + if _logger: _logger.error("Attempted to unlock invalid item %s in category %s" % [key, category], LOG_CATEGORY) + return + + if unlocks[category][key]: + if _logger: _logger.warn("Unlocked already unlocked item %s in category %s" % [key, category], LOG_CATEGORY) + + unlocks[category][key].status = true + if _logger: _logger.info("Unlocked item %s in category %s" % [key, category], LOG_CATEGORY) + + +func lock_item(key: String, category: String): + if not unlocks.has(category): + if _logger: _logger.error("Attempted to lock item %s in invalid category %s" % [key, category], LOG_CATEGORY) + return + + if not unlocks[category].has(key): + if _logger: _logger.error("Attempted to lock invalid item %s in category %s" % [key, category], LOG_CATEGORY) + return + + if not unlocks[category][key]: + if _logger: _logger.warn("Locked already locked item %s in category %s" % [key, category], LOG_CATEGORY) + + unlocks[category][key].status = false + if _logger: _logger.info("Locked item %s in category %s" % [key, category], LOG_CATEGORY) + + +func reset_unlocks(): + if (_data): + unlocks = {} + + for category in _data.data: + var category_data = _data.data[category] + + if category_data.has("!_config"): + if category_data["!_config"].has("unlock"): + if category_data["!_config"]["unlock"] == "true": + unlocks[category] = {} + + for key in category_data: + if key == "!_config": + continue + + var element = category_data[key] + + if element.has("locked"): + if element["locked"] == "true": + if not element.has("unlock_trigger"): + if _logger: _logger.warn("Locked element %s in category %s has no unlock trigger" % [key, category], LOG_CATEGORY) + unlocks[category][key] = { + "status": false + } + continue + + if not element.has("unlock_amount"): + if _logger: _logger.warn("Locked element %s in category %s has no unlock amount" % [key, category], LOG_CATEGORY) + unlocks[category][key] = { + "status": false + } + continue + + if is_nan(int(element["unlock_amount"])): + if _logger: _logger.warn("Locked element %s in category %s unlock amount %s is not a number" % [key, category, element["unlock_amount"]], LOG_CATEGORY) + unlocks[category][key] = { + "status": false + } + continue + + unlocks[category][key] = { + "status": false, + "trigger": element["unlock_trigger"], + "amount": int(element["unlock_amount"]) + } + else: + unlocks[category][key] = { + "status": true + } + else: + unlocks[category][key] = { + "status": true + } + if _logger: _logger.info("Reset Unlocks", LOG_CATEGORY) + +func _ready(): + super() + + reset_unlocks() + + +#func _on_number_persisted(key: String, value, category: PersisterType.DataCategory): + #_check_trigger(key, value) + + +func _check_trigger(key: String, value: int): + for category in unlocks: + var category_data = unlocks[category] + + for element in category_data: + var element_data = category_data[element] + + if not element_data.has("trigger"): + continue + + if element_data["trigger"] != key: + continue + + if element_data["status"]: + continue + + if _logger: _logger.debug("Checking trigger %s for item %s in category %s: %d (required) vs %d (incoming)" % [key, element, category, element_data["amount"], value], LOG_CATEGORY) + if value > element_data["amount"]: + unlock_item(element, category) diff --git a/parts/metadata.txt b/parts/metadata.txt new file mode 100644 index 0000000..2a0abe1 --- /dev/null +++ b/parts/metadata.txt @@ -0,0 +1,3 @@ +name: Name Pending +author: Team Auboreal +start_scene: res://Main.tscn diff --git a/project.godot b/project.godot index 749bbb6..75254fd 100644 --- a/project.godot +++ b/project.godot @@ -11,9 +11,47 @@ config_version=5 [application] config/name="ld-56" +run/main_scene="res://components/Menu/Menu.tscn" config/features=PackedStringArray("4.3", "GL Compatibility") config/icon="res://icon.svg" +[autoload] + +Logger="*res://components/Logger/Logger.tscn" +Data="*res://components/Data/Data.tscn" +Triggerer="*res://components/Triggerer/Triggerer.tscn" +Persister="*res://components/Persister/Persister.tscn" +Cursor="*res://components/Cursor/Cursor.tscn" +Achievements="*res://components/Achievements/Achievements.tscn" +Dialogue="*res://components/Dialogue/Dialogue.tscn" + +[display] + +window/size/viewport_width=640 +window/size/viewport_height=360 + +[editor_plugins] + +enabled=PackedStringArray("res://addons/laia_highlighter/plugin.cfg") + +[input] + +left_click={ +"deadzone": 0.5, +"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":1,"position":Vector2(201, 7),"global_position":Vector2(210, 53),"factor":1.0,"button_index":1,"canceled":false,"pressed":true,"double_click":false,"script":null) +] +} +right_click={ +"deadzone": 0.5, +"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":2,"position":Vector2(204, 2),"global_position":Vector2(213, 48),"factor":1.0,"button_index":2,"canceled":false,"pressed":true,"double_click":false,"script":null) +] +} +escape={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194305,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} + [rendering] renderer/rendering_method="gl_compatibility"