diff --git a/.github/workflows/main-godot4.yml b/.github/workflows/main-godot4.yml index 7f70e77d..72ee5bf2 100644 --- a/.github/workflows/main-godot4.yml +++ b/.github/workflows/main-godot4.yml @@ -37,14 +37,6 @@ jobs: max-parallel: 10 matrix: include: - - os: ubuntu-latest - godot-version: '4.3' - godot-status: 'stable' - gdunit-version: 'v5.1' - - os: ubuntu-latest - godot-version: '4.4' - godot-status: 'stable' - gdunit-version: 'v5.1' - os: ubuntu-latest godot-version: '4.5' godot-status: 'stable' diff --git a/Assets/godotfest_swag_shirt.png b/Assets/godotfest_swag_shirt.png new file mode 100644 index 00000000..ecec2b0d Binary files /dev/null and b/Assets/godotfest_swag_shirt.png differ diff --git a/Assets/godotfest_swag_shirt.png.import b/Assets/godotfest_swag_shirt.png.import new file mode 100644 index 00000000..e2e82ff2 --- /dev/null +++ b/Assets/godotfest_swag_shirt.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bpbaxw0vyursa" +path="res://.godot/imported/godotfest_swag_shirt.png-8414ca085264c6db3e10a2f7cd68b762.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Assets/godotfest_swag_shirt.png" +dest_files=["res://.godot/imported/godotfest_swag_shirt.png-8414ca085264c6db3e10a2f7cd68b762.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +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/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +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/README.md b/README.md index 16f8e98b..8e8fadea 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,6 @@ [![Discord](https://img.shields.io/discord/1020665079668166677?color=rgb%2888%2C%20101%2C%20242%29&label=Discord&logo=discord)](https://discord.gg/7K7q2YuNXe) -![Godot Version - 4.3](https://img.shields.io/badge/Godot_Version-4.3-2ea44f?logo=godotengine) -![Godot Version - 4.4](https://img.shields.io/badge/Godot_Version-4.4-2ea44f?logo=godotengine) ![Godot Version - 4.5](https://img.shields.io/badge/Godot_Version-4.5-2ea44f?logo=godotengine) diff --git a/Scenes/Economy.gd b/Scenes/Economy.gd new file mode 100644 index 00000000..6e4a1981 --- /dev/null +++ b/Scenes/Economy.gd @@ -0,0 +1,13 @@ +extends Control + + +func _on_catalog_button_pressed() -> void: + SceneManager.goto_scene("res://Scenes/Economy/Catalog.tscn") + + +func _on_inventory_button_pressed() -> void: + SceneManager.goto_scene("res://Scenes/Economy/Inventory.tscn") + + +func _on_back_button_pressed() -> void: + SceneManager.goto_scene("res://Scenes/LoggedIn.tscn") diff --git a/Scenes/Economy.gd.uid b/Scenes/Economy.gd.uid new file mode 100644 index 00000000..3aafd1d2 --- /dev/null +++ b/Scenes/Economy.gd.uid @@ -0,0 +1 @@ +uid://vn8hsu4l0ml8 diff --git a/Scenes/Economy.tscn b/Scenes/Economy.tscn new file mode 100644 index 00000000..08f11d26 --- /dev/null +++ b/Scenes/Economy.tscn @@ -0,0 +1,40 @@ +[gd_scene load_steps=2 format=3 uid="uid://csavdi738j7na"] + +[ext_resource type="Script" uid="uid://vn8hsu4l0ml8" path="res://Scenes/Economy.gd" id="1_ngxwe"] + +[node name="Economy" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_ngxwe") + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="Label" type="Label" parent="VBoxContainer"] +layout_mode = 2 +text = "Economy V2" + +[node name="CatalogButton" type="Button" parent="VBoxContainer"] +layout_mode = 2 +text = "Catalog" + +[node name="InventoryButton" type="Button" parent="VBoxContainer"] +layout_mode = 2 +text = "Inventory" + +[node name="BackButton" type="Button" parent="VBoxContainer"] +layout_mode = 2 +text = "Back to Main Menu" + +[connection signal="pressed" from="VBoxContainer/CatalogButton" to="." method="_on_catalog_button_pressed"] +[connection signal="pressed" from="VBoxContainer/InventoryButton" to="." method="_on_inventory_button_pressed"] +[connection signal="pressed" from="VBoxContainer/BackButton" to="." method="_on_back_button_pressed"] diff --git a/Scenes/Economy/Catalog.gd b/Scenes/Economy/Catalog.gd new file mode 100644 index 00000000..3ec021d0 --- /dev/null +++ b/Scenes/Economy/Catalog.gd @@ -0,0 +1,30 @@ +extends Control + +@onready var card_scene: PackedScene = preload("res://Scenes/Widgets/ItemCard.tscn") + +func _ready() -> void: + %LoadingIndicator.show() + PlayFabManager.catalog.search_complete.connect(_on_search_complete) + if PlayFabManager.catalog._has_full_catalog: + _on_search_complete() + else: + PlayFabManager.catalog.fetch_catalog() + + +func _on_search_complete() -> void: + var catalog := PlayFabManager.catalog.get_catalog() + for key in catalog: + var catalog_item: CatalogItem = catalog[key] + var card: ItemCard = card_scene.instantiate() + card.reset(catalog_item) + if catalog_item.Type == CatalogItem.TYPE_CURRENCY: + %LoadingIndicator.hide() + %CurrencyCardGridContainer.add_child(card) + else: + %LoadingIndicator.hide() + %ItemCardGridContainer.add_child(card) + + + +func _on_back_button_pressed() -> void: + SceneManager.goto_scene("res://Scenes/Economy.tscn") diff --git a/Scenes/Economy/Catalog.gd.uid b/Scenes/Economy/Catalog.gd.uid new file mode 100644 index 00000000..d40f4e80 --- /dev/null +++ b/Scenes/Economy/Catalog.gd.uid @@ -0,0 +1 @@ +uid://cvn4peu38pqpe diff --git a/Scenes/Economy/Catalog.tscn b/Scenes/Economy/Catalog.tscn new file mode 100644 index 00000000..c827df86 --- /dev/null +++ b/Scenes/Economy/Catalog.tscn @@ -0,0 +1,99 @@ +[gd_scene load_steps=3 format=3 uid="uid://cq24viuorfsjc"] + +[ext_resource type="Script" uid="uid://cvn4peu38pqpe" path="res://Scenes/Economy/Catalog.gd" id="1_lnqhp"] +[ext_resource type="PackedScene" uid="uid://wwvhjjuojn77" path="res://Scenes/Widgets/LoadingIndicator.tscn" id="2_8nk0b"] + +[node name="Catalog" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 6 +size_flags_vertical = 6 +script = ExtResource("1_lnqhp") + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_bottom = -46.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="Header" type="Label" parent="VBoxContainer"] +layout_mode = 2 +text = "Catalog" + +[node name="HSeparator" type="HSeparator" parent="VBoxContainer"] +custom_minimum_size = Vector2(0, 20) +layout_mode = 2 + +[node name="TabContainer" type="TabContainer" parent="VBoxContainer"] +layout_mode = 2 +size_flags_vertical = 3 +current_tab = 0 + +[node name="Items" type="TabBar" parent="VBoxContainer/TabContainer"] +layout_mode = 2 +metadata/_tab_index = 0 + +[node name="ItemCardGridContainer" type="GridContainer" parent="VBoxContainer/TabContainer/Items"] +unique_name_in_owner = true +custom_minimum_size = Vector2(100, 100) +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_vertical = 3 +columns = 4 + +[node name="Currencies" type="TabBar" parent="VBoxContainer/TabContainer"] +visible = false +layout_mode = 2 +metadata/_tab_index = 1 + +[node name="CurrencyCardGridContainer" type="GridContainer" parent="VBoxContainer/TabContainer/Currencies"] +unique_name_in_owner = true +custom_minimum_size = Vector2(100, 100) +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_vertical = 3 +columns = 4 + +[node name="BackButton" type="Button" parent="."] +layout_mode = 1 +anchors_preset = 12 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_top = -29.0 +grow_horizontal = 2 +grow_vertical = 0 +text = "Back to Economy" + +[node name="ProgressCenter" type="CenterContainer" parent="."] +custom_minimum_size = Vector2(100, 100) +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 + +[node name="LoadingIndicator" parent="ProgressCenter" instance=ExtResource("2_8nk0b")] +unique_name_in_owner = true +visible = false +custom_minimum_size = Vector2(100, 100) +layout_mode = 2 + +[connection signal="pressed" from="BackButton" to="." method="_on_back_button_pressed"] diff --git a/Scenes/Economy/Inventory.gd b/Scenes/Economy/Inventory.gd new file mode 100644 index 00000000..053f65fd --- /dev/null +++ b/Scenes/Economy/Inventory.gd @@ -0,0 +1,51 @@ +extends Control + +@onready var card_scene: PackedScene = preload("res://Scenes/Widgets/ItemCard.tscn") + +var inventory_items: Array = [] + +func _ready() -> void: + _update_inventory() + +func _update_inventory() -> void: + %LoadingIndicator.show() + + var catalog: Dictionary[String, CatalogItem] = PlayFabManager.catalog.get_catalog() + var inventory: Dictionary[String, InventoryItem] = PlayFabManager.inventory.get_inventory() + for id in inventory: + var inventory_item: InventoryItem = inventory[id] + var catalog_item: CatalogItem = resolve_catalog_item(inventory_item.Id, catalog) + var card: ItemCard = card_scene.instantiate() + card.reset_inventory(catalog_item, inventory_item) + + if inventory_item.Type == CatalogItem.TYPE_CURRENCY: + %CurrencyCardGridContainer.add_child(card) + else: + %ItemCardGridContainer.add_child(card) + + + %LoadingIndicator.hide() + +func _clear_inventory() -> void: + for child in %CurrencyCardGridContainer.get_children(): + %CurrencyCardGridContainer.remove_child(child) + child.queue_free() + + for child in %ItemCardGridContainer.get_children(): + %ItemCardGridContainer.remove_child(child) + child.queue_free() + +func resolve_catalog_item(item_id: String, catalog: Dictionary[String, CatalogItem]) -> CatalogItem: + var item = catalog.get(item_id, CatalogItem.new()) + return item + +func _on_back_button_pressed() -> void: + SceneManager.goto_scene("res://Scenes/Economy.tscn") + + +func _on_referesh_button_pressed() -> void: + %LoadingIndicator.show() + _clear_inventory() + PlayFabManager.inventory.turboload_inventory(func(): + _update_inventory() + ) diff --git a/Scenes/Economy/Inventory.gd.uid b/Scenes/Economy/Inventory.gd.uid new file mode 100644 index 00000000..0caf6176 --- /dev/null +++ b/Scenes/Economy/Inventory.gd.uid @@ -0,0 +1 @@ +uid://bdlfoyj3jgt24 diff --git a/Scenes/Economy/Inventory.tscn b/Scenes/Economy/Inventory.tscn new file mode 100644 index 00000000..2b06df82 --- /dev/null +++ b/Scenes/Economy/Inventory.tscn @@ -0,0 +1,105 @@ +[gd_scene load_steps=3 format=3 uid="uid://dq043cwwny6ak"] + +[ext_resource type="Script" uid="uid://bdlfoyj3jgt24" path="res://Scenes/Economy/Inventory.gd" id="1_ersrd"] +[ext_resource type="PackedScene" uid="uid://wwvhjjuojn77" path="res://Scenes/Widgets/LoadingIndicator.tscn" id="2_s30pp"] + +[node name="Inventory" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 6 +size_flags_vertical = 6 +script = ExtResource("1_ersrd") + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_bottom = -46.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="Header" type="Label" parent="VBoxContainer"] +layout_mode = 2 +text = "Inventory" + +[node name="HSeparator" type="HSeparator" parent="VBoxContainer"] +custom_minimum_size = Vector2(0, 20) +layout_mode = 2 + +[node name="TabContainer" type="TabContainer" parent="VBoxContainer"] +layout_mode = 2 +size_flags_vertical = 3 +current_tab = 0 + +[node name="Items" type="TabBar" parent="VBoxContainer/TabContainer"] +layout_mode = 2 +metadata/_tab_index = 0 + +[node name="ItemCardGridContainer" type="GridContainer" parent="VBoxContainer/TabContainer/Items"] +unique_name_in_owner = true +custom_minimum_size = Vector2(100, 100) +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_vertical = 3 +columns = 4 + +[node name="Currencies" type="TabBar" parent="VBoxContainer/TabContainer"] +visible = false +layout_mode = 2 +metadata/_tab_index = 1 + +[node name="CurrencyCardGridContainer" type="GridContainer" parent="VBoxContainer/TabContainer/Currencies"] +unique_name_in_owner = true +custom_minimum_size = Vector2(100, 100) +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_vertical = 3 +columns = 4 + +[node name="RefereshButton" type="Button" parent="VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +text = "Refresh Inventory" + +[node name="BackButton" type="Button" parent="."] +layout_mode = 1 +anchors_preset = 12 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_top = -29.0 +grow_horizontal = 2 +grow_vertical = 0 +text = "Back to Economy" + +[node name="ProgressCenter" type="CenterContainer" parent="."] +custom_minimum_size = Vector2(100, 100) +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 + +[node name="LoadingIndicator" parent="ProgressCenter" instance=ExtResource("2_s30pp")] +unique_name_in_owner = true +visible = false +custom_minimum_size = Vector2(100, 100) +layout_mode = 2 + +[connection signal="pressed" from="VBoxContainer/RefereshButton" to="." method="_on_referesh_button_pressed"] +[connection signal="pressed" from="BackButton" to="." method="_on_back_button_pressed"] diff --git a/Scenes/LoggedIn.gd b/Scenes/LoggedIn.gd index 3a56a560..2787d00d 100644 --- a/Scenes/LoggedIn.gd +++ b/Scenes/LoggedIn.gd @@ -37,6 +37,10 @@ func _on_EventsPlayStream_pressed(): SceneManager.goto_scene("res://Scenes/Events.tscn") +func _on_EconomyButton_pressed(): + SceneManager.goto_scene("res://Scenes/Economy.tscn") + + func _on_RequestBuilder_pressed(): SceneManager.goto_scene("res://Scenes/RequestBuilder.tscn") diff --git a/Scenes/LoggedIn.tscn b/Scenes/LoggedIn.tscn index 9ae5f589..a931257e 100644 --- a/Scenes/LoggedIn.tscn +++ b/Scenes/LoggedIn.tscn @@ -105,6 +105,10 @@ text = "Get Title Data" layout_mode = 2 text = "Events & PlayStream API" +[node name="EconomyButton" type="Button" parent="VBoxContainer"] +layout_mode = 2 +text = "Economy V2" + [node name="RequestBuilder" type="Button" parent="VBoxContainer"] layout_mode = 2 text = "Request Builder" @@ -124,6 +128,7 @@ text = "Logout" [connection signal="pressed" from="VBoxContainer/GetTitleDataButton" to="." method="_on_GetTitleDataButton_pressed"] [connection signal="pressed" from="VBoxContainer/EventsPlayStream" to="." method="_on_EventsPlayStream_pressed"] +[connection signal="pressed" from="VBoxContainer/EconomyButton" to="." method="_on_EconomyButton_pressed"] [connection signal="pressed" from="VBoxContainer/RequestBuilder" to="." method="_on_RequestBuilder_pressed"] [connection signal="pressed" from="VBoxContainer/MainMenuButton" to="." method="_on_MainMenuButton_pressed"] [connection signal="pressed" from="VBoxContainer/LogoutButton" to="." method="_on_LogoutButton_pressed"] diff --git a/Scenes/Widgets/ItemCard.gd b/Scenes/Widgets/ItemCard.gd new file mode 100644 index 00000000..436e6fef --- /dev/null +++ b/Scenes/Widgets/ItemCard.gd @@ -0,0 +1,46 @@ +extends Control +class_name ItemCard + + +func reset(catalog_item: CatalogItem) -> void: + %Title.text = catalog_item.Title[PlayFab.LANG_NEUTRAL] + %Description.text = catalog_item.Description[PlayFab.LANG_NEUTRAL] + %Type.text = catalog_item.Type + + for price in catalog_item.PriceOptions.Prices: + var unit_amount: float = price.UnitAmount + var text: String = "%s pieces: " % [unit_amount] + + for currency_amount in price.Amounts: + var currency := PlayFabManager.catalog.resolve_item(currency_amount.ItemId) + text += "| %s (%s)" % [currency_amount.Amount, currency.Title.get(PlayFab.LANG_NEUTRAL)] + + var buy_button = Button.new() + buy_button.text = text + buy_button.pressed.connect(_on_purchase_button_pressed.bindv([unit_amount, catalog_item])) + %VirtualPrices.add_child(buy_button) + + +func reset_inventory(catalog_item: CatalogItem, inventory_item: InventoryItem) -> void: + %Title.text = catalog_item.Title[PlayFab.LANG_NEUTRAL] + %Description.text = catalog_item.Description[PlayFab.LANG_NEUTRAL] + %Type.text = catalog_item.Type + %Amount.text = "%dx" % inventory_item.Amount + + +func _on_purchase_button_pressed(amount: int, catalog_item: CatalogItem) -> void: + var request_data := PurchaseInventoryItemsRequest.from_catalog_item(amount, catalog_item) + + PlayFabManager.inventory.purchase_inventory_items(request_data, func(_response: PurchaseInventoryItemsResponse): + PlayFabManager.inventory.turboload_inventory(func(): + ToastParty.show({ + "text": "🪙 Purchased %sx %s." % [amount, catalog_item.Title.get(PlayFab.LANG_NEUTRAL)], # Text (emojis can be used) + "bgcolor": Color(0, 0, 0, 0.7), # Background Color + "color": Color(1, 1, 1, 1), # Text Color + "gravity": "top", # top or bottom + "direction": "right", # left or center or right + "text_size": 18, # [optional] Text (font) size // experimental (warning!) + "use_font": false # [optional] Use custom ToastParty font // experimental (warning!) + }) + ) + ) diff --git a/Scenes/Widgets/ItemCard.gd.uid b/Scenes/Widgets/ItemCard.gd.uid new file mode 100644 index 00000000..ddbe9f13 --- /dev/null +++ b/Scenes/Widgets/ItemCard.gd.uid @@ -0,0 +1 @@ +uid://md2a6mqg54jn diff --git a/Scenes/Widgets/ItemCard.tscn b/Scenes/Widgets/ItemCard.tscn new file mode 100644 index 00000000..7819d2b8 --- /dev/null +++ b/Scenes/Widgets/ItemCard.tscn @@ -0,0 +1,47 @@ +[gd_scene load_steps=2 format=3 uid="uid://cd8qcg4hmyogs"] + +[ext_resource type="Script" uid="uid://md2a6mqg54jn" path="res://Scenes/Widgets/ItemCard.gd" id="1_0mt03"] + +[node name="ItemCard" type="Control"] +custom_minimum_size = Vector2(100, 100) +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +script = ExtResource("1_0mt03") + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="Title" type="Label" parent="VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +text = "Title" + +[node name="Description" type="RichTextLabel" parent="VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +text = "Description" +fit_content = true + +[node name="Type" type="Label" parent="VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +text = "Type" + +[node name="VirtualPrices" type="VBoxContainer" parent="VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 + +[node name="Amount" type="Label" parent="VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 diff --git a/addons/gdUnit4/src/dotnet/GdUnit4CSharpApi.cs b/addons/gdUnit4/src/dotnet/GdUnit4CSharpApi.cs index 14a1355b..6beff479 100644 --- a/addons/gdUnit4/src/dotnet/GdUnit4CSharpApi.cs +++ b/addons/gdUnit4/src/dotnet/GdUnit4CSharpApi.cs @@ -188,29 +188,29 @@ internal static TestSuiteNode BuildTestSuiteNodeFrom(Array tests) public partial class GdUnit4CSharpApi : RefCounted { - [Signal] - public delegate void ExecutionCompletedEventHandler(); + [Signal] + public delegate void ExecutionCompletedEventHandler(); - public static bool IsApiLoaded() - { - GD.PushWarning("No `gdunit4.api` dependency found, check your project dependencies."); - return false; - } + public static bool IsApiLoaded() + { + GD.PushWarning("No `gdunit4.api` dependency found, check your project dependencies."); + return false; + } - public static string Version() - => "Unknown"; + public static string Version() + => "Unknown"; - public static Array DiscoverTests(CSharpScript sourceScript) => new(); + public static Array DiscoverTests(CSharpScript sourceScript) => new(); - public void ExecuteAsync(Array tests, Callable listener) - { - } + public void ExecuteAsync(Array tests, Callable listener) + { + } - public static bool IsTestSuite(CSharpScript script) - => false; + public static bool IsTestSuite(CSharpScript script) + => false; - public static Dictionary CreateTestSuite(string sourcePath, int lineNumber, string testSuitePath) - => new(); + public static Dictionary CreateTestSuite(string sourcePath, int lineNumber, string testSuitePath) + => new(); } #endif diff --git a/addons/gdUnit4/src/mocking/GdUnitMockFunctionDoubler.gd.uid b/addons/gdUnit4/src/mocking/GdUnitMockFunctionDoubler.gd.uid new file mode 100644 index 00000000..7b32adad --- /dev/null +++ b/addons/gdUnit4/src/mocking/GdUnitMockFunctionDoubler.gd.uid @@ -0,0 +1 @@ +uid://riwtcdugf7la diff --git a/addons/gdUnit4/src/spy/GdUnitSpyFunctionDoubler.gd.uid b/addons/gdUnit4/src/spy/GdUnitSpyFunctionDoubler.gd.uid new file mode 100644 index 00000000..d581f7d4 --- /dev/null +++ b/addons/gdUnit4/src/spy/GdUnitSpyFunctionDoubler.gd.uid @@ -0,0 +1 @@ +uid://cftsh0exnhxgm diff --git a/addons/godot-playfab/JsonSerializable.gd b/addons/godot-playfab/JsonSerializable.gd index 049c549e..7e6b299d 100644 --- a/addons/godot-playfab/JsonSerializable.gd +++ b/addons/godot-playfab/JsonSerializable.gd @@ -15,15 +15,15 @@ func _get_type_for_property(property_name: String): # @returns Dictionary - A Dictionary representation of this object instance func to_dict() -> Dictionary: - var dict = {} - var props = get_property_list() + var dict := {} + var props: Array[Dictionary] = get_property_list() # Skipping the first 3 items because they are metadata we do not need for prop in props: var name = prop["name"] # The name of the property on the object. Will be used to access its's value var type = prop["type"] # The godot built-in type (Array, Object etc) var usage = prop["usage"] # is a combination of PropertyUsageFlags. - + # If it's not PROPERTY_USAGE_SCRIPT_VARIABLE, it's not an actual property and we can ignore it if (usage & PROPERTY_USAGE_SCRIPT_VARIABLE) != PROPERTY_USAGE_SCRIPT_VARIABLE: continue @@ -44,9 +44,40 @@ func to_dict() -> Dictionary: #push_error("If '%s' is not a builtin class, please implement a to_dict() method! If it IS a builtin class, a special handler needs to be implemented in JsonSerializable." % type_name) print_debug("If '%s' is not a builtin class, please implement a to_dict() method! If it IS a builtin class, a special handler needs to be implemented in JsonSerializable." % type_name) dict[name] = type_name + elif type == TYPE_ARRAY: + var arr = get(name) + if arr.size() == 0: + continue + + var is_typed = arr.is_typed() + if is_typed: + var script = arr.get_typed_script() + if script == null: + # Builtin type, just set it and continue with the next element + dict[name] = arr + continue + + var script_name: StringName = (script as Script).get_global_name() + var new_arr := [] + for i in arr.size(): + var element = arr[i] + if element == null: + new_arr.append(null) + elif element.has_method("to_dict"): + new_arr.append(element.to_dict()) + else: + push_error("If '%s' is not a builtin class, please implement a to_dict() method! If it IS a builtin class, a special handler needs to be implemented in JsonSerializable." % script_name) + new_arr.append(script_name) + dict[name] = new_arr + else: + # Untyped array - just set it + dict[name] = arr else: # Get the value of the property - dict[name] = get(name) + var value = get(name) + if type == TYPE_STRING && value == "": + continue + dict[name] = value return dict @@ -56,7 +87,7 @@ func to_dict() -> Dictionary: # @returns void func from_dict(data: Dictionary, instance: JsonSerializable): - var props = instance.get_property_list() + var props: Array[Dictionary] = instance.get_property_list() for key in data.keys(): var type @@ -66,7 +97,29 @@ func from_dict(data: Dictionary, instance: JsonSerializable): break # If basic data type - just set it - if type != TYPE_OBJECT: + if type == TYPE_ARRAY: + var field = instance.get(key) + var is_typed = field.is_typed() + + if is_typed: + var script = field.get_typed_script() + if script == null: + # Builtin type, just set it and continue with the next element + for i in data[key].size(): + var element = data[key][i] + field.append(element) + continue + + var script_name: StringName = (script as Script).get_global_name() + + for i in data[key].size(): + var element = data[key][i] + var nested_instance = script.new() + nested_instance.from_dict(element, nested_instance) + field.append(nested_instance) +# elif type == TYPE_DICTIONARY: +# # TODO: implementation for Typed Dictionaries + elif type != TYPE_OBJECT: instance.set(key, data[key]) elif data[key] == null: instance.set(key, null) diff --git a/addons/godot-playfab/Models/CatalogAlternateId.gd b/addons/godot-playfab/Models/CatalogAlternateId.gd new file mode 100644 index 00000000..902ca43d --- /dev/null +++ b/addons/godot-playfab/Models/CatalogAlternateId.gd @@ -0,0 +1,20 @@ +extends JsonSerializable +class_name CatalogAlternateId + +## Type of the alternate ID. +var Type: String + +## Value of the alternate ID. +var Value: String + + +func _get_type_for_property(property_name: String) -> String: + match property_name: +# "": +# return "" + _: + pass + + push_error("Could not find mapping for property: " + property_name) + return super._get_type_for_property(property_name) + diff --git a/addons/godot-playfab/Models/CatalogAlternateId.gd.uid b/addons/godot-playfab/Models/CatalogAlternateId.gd.uid new file mode 100644 index 00000000..b833aa68 --- /dev/null +++ b/addons/godot-playfab/Models/CatalogAlternateId.gd.uid @@ -0,0 +1 @@ +uid://cyb2w6nccaqcj diff --git a/addons/godot-playfab/Models/CatalogItem.gd b/addons/godot-playfab/Models/CatalogItem.gd new file mode 100644 index 00000000..d0803345 --- /dev/null +++ b/addons/godot-playfab/Models/CatalogItem.gd @@ -0,0 +1,122 @@ +extends JsonSerializable +class_name CatalogItem + + +const TYPE_CATALOG_ITEM: String = "catalogItem" +const TYPE_CURRENCY: String = "currency" +const TYPE_BUNDLE: String = "bundle" +const TYPE_STORE: String = "store" +const TYPE_UGC: String = "ugc" +const TYPE_SUBSCRIPTION: String = "subscription" + +## The alternate IDs associated with this item. An alternate ID can be set to 'FriendlyId' or any of the supported marketplace names. +var AlternateIds: Array[CatalogAlternateId] + +## The client-defined type of the item. +var ContentType: String + +## The set of content/files associated with this item. Up to 100 files can be added to an item. +var Contents: Array[Content] + +## The date and time when this item was created. +var CreationDate: String + +## The ID of the creator of this catalog item. +var CreatorEntity: EntityKey + +## The set of platform specific deep links for this item. +var DeepLinks: Array[DeepLink] + +## The Stack Id that will be used as default for this item in Inventory when an explicit one is not provided. This DefaultStackId can be a static stack id or '{guid}', which will generate a unique stack id for the item. If null, Inventory's default stack id will be used. +var DefaultStackId: String + +## A dictionary of localized descriptions. Key is language code and localized string is the value. The NEUTRAL locale is required. Descriptions have a 10000 character limit per country code. +var Description: Dictionary + +## Game specific properties for display purposes. This is an arbitrary JSON blob. The Display Properties field has a 10000 byte limit per item. +var DisplayProperties: Dictionary + +## The user provided version of the item for display purposes. Maximum character length of 50. +var DisplayVersion: String + +## The current ETag value that can be used for optimistic concurrency in the If-None-Match header. +var ETag: String + +## The date of when the item will cease to be available. If not provided then the product will be available indefinitely. +var EndDate: String + +## The unique ID of the item. +var Id: String + +## The images associated with this item. Images can be thumbnails or screenshots. Up to 100 images can be added to an item. Only .png, .jpg, .gif, and .bmp file types can be uploaded +var Images: Array[PlayFabImage] + +## Indicates if the item is hidden. +var IsHidden: bool + +## The item references associated with this item. For example, the items in a Bundle/Store/Subscription. Every item can have up to 50 item references. +var ItemReferences: Array[CatalogItemReference] + +## A dictionary of localized keywords. Key is language code and localized list of keywords is the value. Keywords have a 50 character limit per keyword and up to 32 keywords can be added per country code. +var Keywords: Dictionary + +## The date and time this item was last updated. +var LastModifiedDate: String + +## The moderation state for this item. +var Moderation: ModerationState + +## The platforms supported by this item. +var Platforms: Array[String] + +## The prices the item can be purchased for. +var PriceOptions: CatalogPriceOptions + +## Rating summary for this item. +var Rating: Rating + +## The real price the item was purchased for per marketplace. +var RealMoneyPriceDetails: RealMoneyPriceDetails + +## The date of when the item will be available. If not provided then the product will appear immediately. +var StartDate: String + +## Optional details for stores items. +var StoreDetails: StoreDetails + +## The list of tags that are associated with this item. Up to 32 tags can be added to an item. +var Tags: Array[String] + +## A dictionary of localized titles. Key is language code and localized string is the value. The NEUTRAL locale is required. Titles have a 512 character limit per country code. +var Title: Dictionary + +## The high-level type of the item. The following item types are supported: bundle, catalogItem, currency, store, ugc, subscription. +var Type: String + + +func _get_type_for_property(property_name: String) -> String: + match property_name: + "CreatorEntity": + return "EntityKey" + "Moderation": + return "ModerationState" + "PriceOptions": + return "CatalogPriceOptions" + "Rating": + return "Rating" + "RealMoneyPriceDetails": + return "RealMoneyPriceDetails" + "StoreDetails": + return "StoreDetails" + _: + pass + + push_error("Could not find mapping for property: " + property_name) + return super._get_type_for_property(property_name) + +const ITEM_TYPE_BUNDLE: String = "bundle" +const ITEM_TYPE_CATALOG_ITEM: String = "catalogItem" +const ITEM_TYPE_CURRENCY: String = "currency" +const ITEM_TYPE_STORE: String = "store" +const ITEM_TYPE_UGC: String = "ugc" +const ITEM_TYPE_SUBSCRIPTION: String = "subscription" diff --git a/addons/godot-playfab/Models/CatalogItem.gd.uid b/addons/godot-playfab/Models/CatalogItem.gd.uid new file mode 100644 index 00000000..1e718f41 --- /dev/null +++ b/addons/godot-playfab/Models/CatalogItem.gd.uid @@ -0,0 +1 @@ +uid://ddo316sr85mhp diff --git a/addons/godot-playfab/Models/CatalogItemReference.gd b/addons/godot-playfab/Models/CatalogItemReference.gd new file mode 100644 index 00000000..0f1490fe --- /dev/null +++ b/addons/godot-playfab/Models/CatalogItemReference.gd @@ -0,0 +1,23 @@ +extends JsonSerializable +class_name CatalogItemReference + +## The amount of the catalog item. +var Amount: float + +## The unique ID of the catalog item. +var Id: String + +## The prices the catalog item can be purchased for. +var PriceOptions: CatalogPriceOptions + + +func _get_type_for_property(property_name: String) -> String: + match property_name: +# "": +# return "" + _: + pass + + push_error("Could not find mapping for property: " + property_name) + return super._get_type_for_property(property_name) + diff --git a/addons/godot-playfab/Models/CatalogItemReference.gd.uid b/addons/godot-playfab/Models/CatalogItemReference.gd.uid new file mode 100644 index 00000000..290960b9 --- /dev/null +++ b/addons/godot-playfab/Models/CatalogItemReference.gd.uid @@ -0,0 +1 @@ +uid://bgq16spf627x6 diff --git a/addons/godot-playfab/Models/CatalogPrice.gd b/addons/godot-playfab/Models/CatalogPrice.gd new file mode 100644 index 00000000..b02376f4 --- /dev/null +++ b/addons/godot-playfab/Models/CatalogPrice.gd @@ -0,0 +1,22 @@ +extends JsonSerializable +class_name CatalogPrice + +## The amounts of the catalog item price. Each price can have up to 15 item amounts. +var Amounts: Array[CatalogPriceAmount] + +## The per-unit amount this price can be used to purchase. +var UnitAmount: float + +## The per-unit duration this price can be used to purchase. The maximum duration is 100 years. +var UnitDurationInSeconds: float + + +func _get_type_for_property(property_name: String) -> String: + match property_name: +# "": +# return "" + _: + pass + + push_error("Could not find mapping for property: " + property_name) + return super._get_type_for_property(property_name) diff --git a/addons/godot-playfab/Models/CatalogPrice.gd.uid b/addons/godot-playfab/Models/CatalogPrice.gd.uid new file mode 100644 index 00000000..e4ea461c --- /dev/null +++ b/addons/godot-playfab/Models/CatalogPrice.gd.uid @@ -0,0 +1 @@ +uid://box2uyibsjdo8 diff --git a/addons/godot-playfab/Models/CatalogPriceAmount.gd b/addons/godot-playfab/Models/CatalogPriceAmount.gd new file mode 100644 index 00000000..35b9460c --- /dev/null +++ b/addons/godot-playfab/Models/CatalogPriceAmount.gd @@ -0,0 +1,19 @@ +extends JsonSerializable +class_name CatalogPriceAmount + +## The amount of the price. +var Amount: float + +## The Item Id of the price. +var ItemId: String + + +func _get_type_for_property(property_name: String) -> String: + match property_name: +# "": +# return "" + _: + pass + + push_error("Could not find mapping for property: " + property_name) + return super._get_type_for_property(property_name) diff --git a/addons/godot-playfab/Models/CatalogPriceAmount.gd.uid b/addons/godot-playfab/Models/CatalogPriceAmount.gd.uid new file mode 100644 index 00000000..06a6d488 --- /dev/null +++ b/addons/godot-playfab/Models/CatalogPriceAmount.gd.uid @@ -0,0 +1 @@ +uid://viyyn5up2wtj diff --git a/addons/godot-playfab/Models/CatalogPriceAmountOverride.gd b/addons/godot-playfab/Models/CatalogPriceAmountOverride.gd new file mode 100644 index 00000000..37a34ef0 --- /dev/null +++ b/addons/godot-playfab/Models/CatalogPriceAmountOverride.gd @@ -0,0 +1,23 @@ +extends JsonSerializable +class_name CatalogPriceAmountOverride + +## The exact value that should be utilized in the override. +var FixedValue: float + +## The id of the item this override should utilize. +var ItemId: String + +## The multiplier that will be applied to the base Catalog value to determine what value should be utilized in the override. +var Multiplier: int + + +func _get_type_for_property(property_name: String) -> String: + match property_name: +# "": +# return "" + _: + pass + + push_error("Could not find mapping for property: " + property_name) + return super._get_type_for_property(property_name) + diff --git a/addons/godot-playfab/Models/CatalogPriceAmountOverride.gd.uid b/addons/godot-playfab/Models/CatalogPriceAmountOverride.gd.uid new file mode 100644 index 00000000..4aab7a6a --- /dev/null +++ b/addons/godot-playfab/Models/CatalogPriceAmountOverride.gd.uid @@ -0,0 +1 @@ +uid://bca371bwoypke diff --git a/addons/godot-playfab/Models/CatalogPriceOptions.gd b/addons/godot-playfab/Models/CatalogPriceOptions.gd new file mode 100644 index 00000000..7954de54 --- /dev/null +++ b/addons/godot-playfab/Models/CatalogPriceOptions.gd @@ -0,0 +1,16 @@ +extends JsonSerializable +class_name CatalogPriceOptions + +## Prices of the catalog item. An item can have up to 15 prices +var Prices: Array[CatalogPrice] + + +func _get_type_for_property(property_name: String) -> String: + match property_name: +# "": +# return "" + _: + pass + + push_error("Could not find mapping for property: " + property_name) + return super._get_type_for_property(property_name) diff --git a/addons/godot-playfab/Models/CatalogPriceOptions.gd.uid b/addons/godot-playfab/Models/CatalogPriceOptions.gd.uid new file mode 100644 index 00000000..ea522848 --- /dev/null +++ b/addons/godot-playfab/Models/CatalogPriceOptions.gd.uid @@ -0,0 +1 @@ +uid://dt1ybgfimvlmq diff --git a/addons/godot-playfab/Models/CatalogPriceOptionsOverride.gd b/addons/godot-playfab/Models/CatalogPriceOptionsOverride.gd new file mode 100644 index 00000000..4c09a50f --- /dev/null +++ b/addons/godot-playfab/Models/CatalogPriceOptionsOverride.gd @@ -0,0 +1,17 @@ +extends JsonSerializable +class_name CatalogPriceOptionsOverride + +## The prices utilized in the override. +var Prices: Array[CatalogPriceOverride] + + +func _get_type_for_property(property_name: String) -> String: + match property_name: +# "": +# return "" + _: + pass + + push_error("Could not find mapping for property: " + property_name) + return super._get_type_for_property(property_name) + diff --git a/addons/godot-playfab/Models/CatalogPriceOptionsOverride.gd.uid b/addons/godot-playfab/Models/CatalogPriceOptionsOverride.gd.uid new file mode 100644 index 00000000..22224b29 --- /dev/null +++ b/addons/godot-playfab/Models/CatalogPriceOptionsOverride.gd.uid @@ -0,0 +1 @@ +uid://r7lvb3agn4fc diff --git a/addons/godot-playfab/Models/CatalogPriceOverride.gd b/addons/godot-playfab/Models/CatalogPriceOverride.gd new file mode 100644 index 00000000..f0210c68 --- /dev/null +++ b/addons/godot-playfab/Models/CatalogPriceOverride.gd @@ -0,0 +1,17 @@ +extends JsonSerializable +class_name CatalogPriceOverride + +## The currency amounts utilized in the override for a singular price. +var Amounts: Array[CatalogPriceAmountOverride] + + +func _get_type_for_property(property_name: String) -> String: + match property_name: +# "": +# return "" + _: + pass + + push_error("Could not find mapping for property: " + property_name) + return super._get_type_for_property(property_name) + diff --git a/addons/godot-playfab/Models/CatalogPriceOverride.gd.uid b/addons/godot-playfab/Models/CatalogPriceOverride.gd.uid new file mode 100644 index 00000000..b0ebef78 --- /dev/null +++ b/addons/godot-playfab/Models/CatalogPriceOverride.gd.uid @@ -0,0 +1 @@ +uid://cs42120j245rh diff --git a/addons/godot-playfab/Models/Content.gd b/addons/godot-playfab/Models/Content.gd new file mode 100644 index 00000000..1533dcc0 --- /dev/null +++ b/addons/godot-playfab/Models/Content.gd @@ -0,0 +1,32 @@ +extends JsonSerializable +class_name Content + +## The content unique ID. +var Id: String + +## The maximum client version that this content is compatible with. Client Versions can be up to 3 segments separated by periods(.) and each segment can have a maximum value of 65535. +var MaxClientVersion: String + +## The minimum client version that this content is compatible with. Client Versions can be up to 3 segments separated by periods(.) and each segment can have a maximum value of 65535. +var MinClientVersion: String + +## The list of tags that are associated with this content. Tags must be defined in the Catalog Config before being used in content. +var Tags: Array[String] + +## The client-defined type of the content. Content Types must be defined in the Catalog Config before being used. +var Type: String + +## The Azure CDN URL for retrieval of the catalog item binary content. +var Url: String + + +func _get_type_for_property(property_name: String) -> String: + match property_name: +# "": +# return "" + _: + pass + + push_error("Could not find mapping for property: " + property_name) + return super._get_type_for_property(property_name) + diff --git a/addons/godot-playfab/Models/Content.gd.uid b/addons/godot-playfab/Models/Content.gd.uid new file mode 100644 index 00000000..c2c3e093 --- /dev/null +++ b/addons/godot-playfab/Models/Content.gd.uid @@ -0,0 +1 @@ +uid://ctpkxd2bp8h7o diff --git a/addons/godot-playfab/Models/DeepLink.gd b/addons/godot-playfab/Models/DeepLink.gd new file mode 100644 index 00000000..46f5f89a --- /dev/null +++ b/addons/godot-playfab/Models/DeepLink.gd @@ -0,0 +1,20 @@ +extends JsonSerializable +class_name DeepLink + +## Target platform for this deep link. +var Platform: String + +## The deep link for this platform. +var Url: String + + +func _get_type_for_property(property_name: String) -> String: + match property_name: +# "": +# return "" + _: + pass + + push_error("Could not find mapping for property: " + property_name) + return super._get_type_for_property(property_name) + diff --git a/addons/godot-playfab/Models/DeepLink.gd.uid b/addons/godot-playfab/Models/DeepLink.gd.uid new file mode 100644 index 00000000..7856da65 --- /dev/null +++ b/addons/godot-playfab/Models/DeepLink.gd.uid @@ -0,0 +1 @@ +uid://8takr4sryjsm diff --git a/addons/godot-playfab/Models/FilterOptions.gd b/addons/godot-playfab/Models/FilterOptions.gd new file mode 100644 index 00000000..83b94577 --- /dev/null +++ b/addons/godot-playfab/Models/FilterOptions.gd @@ -0,0 +1,20 @@ +extends JsonSerializable +class_name FilterOptions + +## The OData filter utilized. Mutually exclusive with 'IncludeAllItems'. More info about Filter Complexity limits can be found here: https://learn.microsoft.com/en-us/gaming/playfab/features/economy-v2/catalog/search#limits +var Filter: String + +## The flag that overrides the filter and allows for returning all catalog items. Mutually exclusive with 'Filter'. +var IncludeAllItems: bool + + +func _get_type_for_property(property_name: String) -> String: + match property_name: +# "": +# return "" + _: + pass + + push_error("Could not find mapping for property: " + property_name) + return super._get_type_for_property(property_name) + diff --git a/addons/godot-playfab/Models/FilterOptions.gd.uid b/addons/godot-playfab/Models/FilterOptions.gd.uid new file mode 100644 index 00000000..0dd86c49 --- /dev/null +++ b/addons/godot-playfab/Models/FilterOptions.gd.uid @@ -0,0 +1 @@ +uid://ckk8jrntu6enk diff --git a/addons/godot-playfab/Models/GetInventoryItemsRequest.gd b/addons/godot-playfab/Models/GetInventoryItemsRequest.gd new file mode 100644 index 00000000..1b92290a --- /dev/null +++ b/addons/godot-playfab/Models/GetInventoryItemsRequest.gd @@ -0,0 +1,40 @@ +extends JsonSerializable +class_name GetInventoryItemsRequest + +const MAX_ITEM_COUNT := 50 + +## Number of items to retrieve. This value is optional. Maximum page size is 50. The default value is 10 +## REQUIRED: true +var Count: int = MAX_ITEM_COUNT + + +## The id of the entity's collection to perform this action on. (Default="default") +var CollectionId: String + + +## An opaque token used to retrieve the next page of items in the inventory, if any are available. Should be null on initial request. +var ContinuationToken: String + + +## The optional custom tags associated with the request (e.g. build number, external trace identifiers, etc.). +var CustomTags: Dictionary[String, Variant] + + +## The entity to perform this action on. +var Entity: EntityKey + + +## OData Filter to refine the items returned. InventoryItem properties 'type', 'id', and 'stackId' can be used in the filter. For example: "type eq 'currency'" +var Filter: String + + + +func _get_type_for_property(property_name: String) -> String: + match property_name: +# "": +# return "" + _: + pass + + push_error("Could not find mapping for property: " + property_name) + return super._get_type_for_property(property_name) diff --git a/addons/godot-playfab/Models/GetInventoryItemsRequest.gd.uid b/addons/godot-playfab/Models/GetInventoryItemsRequest.gd.uid new file mode 100644 index 00000000..7bcf6f6a --- /dev/null +++ b/addons/godot-playfab/Models/GetInventoryItemsRequest.gd.uid @@ -0,0 +1 @@ +uid://cr15lf8hphbi2 diff --git a/addons/godot-playfab/Models/GetInventoryItemsResponse.gd b/addons/godot-playfab/Models/GetInventoryItemsResponse.gd new file mode 100644 index 00000000..068f5bc8 --- /dev/null +++ b/addons/godot-playfab/Models/GetInventoryItemsResponse.gd @@ -0,0 +1,26 @@ +extends JsonSerializable +class_name GetInventoryItemsResponse + +## An opaque token used to retrieve the next page of items, if any are available. +var ContinuationToken: String + + +## ETags are used for concurrency checking when updating resources. More information about using ETags can be found here: https://learn.microsoft.com/en-us/gaming/playfab/features/economy-v2/catalog/etags +var ETag: String + + +## The requested inventory items. +var Items: Array[InventoryItem] + + + +func _get_type_for_property(property_name: String) -> String: + match property_name: +# "": +# return "" + _: + pass + + push_error("Could not find mapping for property: " + property_name) + return super._get_type_for_property(property_name) + diff --git a/addons/godot-playfab/Models/GetInventoryItemsResponse.gd.uid b/addons/godot-playfab/Models/GetInventoryItemsResponse.gd.uid new file mode 100644 index 00000000..04ec2c55 --- /dev/null +++ b/addons/godot-playfab/Models/GetInventoryItemsResponse.gd.uid @@ -0,0 +1 @@ +uid://ckswgkf75ei1b diff --git a/addons/godot-playfab/Models/GetItemsRequest.gd b/addons/godot-playfab/Models/GetItemsRequest.gd new file mode 100644 index 00000000..4684802d --- /dev/null +++ b/addons/godot-playfab/Models/GetItemsRequest.gd @@ -0,0 +1,32 @@ +extends JsonSerializable +## Request to get items from the catalog. +## @tutorial(PlayFab REST API Docs):https://learn.microsoft.com/en-us/rest/api/playfab/economy/catalog/get-items?view=playfab-rest +class_name GetItemsRequest + +## List of item alternate IDs. +var AlternateIds: Array[CatalogAlternateId] + +## The optional custom tags associated with the request (e.g. build number, external trace identifiers, etc.). +var CustomTags: Dictionary + +## The entity to perform this action on. +var Entity: EntityKey + +## List of Item Ids. +var Ids: Array[String] + + +func _init(): + pass + + +func _get_type_for_property(property_name: String) -> String: + match property_name: + "Entity": + return "EntityKey" + _: + pass + + push_error("Could not find mapping for property: " + property_name) + return super._get_type_for_property(property_name) + diff --git a/addons/godot-playfab/Models/GetItemsRequest.gd.uid b/addons/godot-playfab/Models/GetItemsRequest.gd.uid new file mode 100644 index 00000000..28513869 --- /dev/null +++ b/addons/godot-playfab/Models/GetItemsRequest.gd.uid @@ -0,0 +1 @@ +uid://chkgfeb6e8c8o diff --git a/addons/godot-playfab/Models/GetItemsResponse.gd b/addons/godot-playfab/Models/GetItemsResponse.gd new file mode 100644 index 00000000..f085acfb --- /dev/null +++ b/addons/godot-playfab/Models/GetItemsResponse.gd @@ -0,0 +1,18 @@ +extends JsonSerializable +## Response of [Catalog] +## @tutorial https://learn.microsoft.com/en-us/rest/api/playfab/economy/catalog/get-items?view=playfab-rest#getitemsresponse +class_name GetItemsResponse + +## Metadata of set of items. +var Items: Array[CatalogItem] + + +func _get_type_for_property(property_name: String) -> String: + match property_name: +# "": +# return "" + _: + pass + + push_error("Could not find mapping for property: " + property_name) + return super._get_type_for_property(property_name) diff --git a/addons/godot-playfab/Models/GetItemsResponse.gd.uid b/addons/godot-playfab/Models/GetItemsResponse.gd.uid new file mode 100644 index 00000000..2baa3b95 --- /dev/null +++ b/addons/godot-playfab/Models/GetItemsResponse.gd.uid @@ -0,0 +1 @@ +uid://cds1khgm21iep diff --git a/addons/godot-playfab/Models/InitialValues.gd b/addons/godot-playfab/Models/InitialValues.gd new file mode 100644 index 00000000..69b65bd3 --- /dev/null +++ b/addons/godot-playfab/Models/InitialValues.gd @@ -0,0 +1,18 @@ +extends JsonSerializable +class_name InitialValues + +## Game specific properties for display purposes. The Display Properties field has a 1000 byte limit. +var DisplayProperties: Dictionary[String, Variant] + + + +func _get_type_for_property(property_name: String) -> String: + match property_name: +# "": +# return "" + _: + pass + + push_error("Could not find mapping for property: " + property_name) + return super._get_type_for_property(property_name) + diff --git a/addons/godot-playfab/Models/InitialValues.gd.uid b/addons/godot-playfab/Models/InitialValues.gd.uid new file mode 100644 index 00000000..e8d02121 --- /dev/null +++ b/addons/godot-playfab/Models/InitialValues.gd.uid @@ -0,0 +1 @@ +uid://d2tjtptwjrbma diff --git a/addons/godot-playfab/Models/InventoryAlternateId.gd b/addons/godot-playfab/Models/InventoryAlternateId.gd new file mode 100644 index 00000000..645cc3cb --- /dev/null +++ b/addons/godot-playfab/Models/InventoryAlternateId.gd @@ -0,0 +1,22 @@ +extends JsonSerializable +class_name InventoryAlternateId + +## Type of the alternate ID. +var Type: String + + +## Value of the alternate ID. +var Value: String + + + +func _get_type_for_property(property_name: String) -> String: + match property_name: +# "": +# return "" + _: + pass + + push_error("Could not find mapping for property: " + property_name) + return super._get_type_for_property(property_name) + diff --git a/addons/godot-playfab/Models/InventoryAlternateId.gd.uid b/addons/godot-playfab/Models/InventoryAlternateId.gd.uid new file mode 100644 index 00000000..f7317451 --- /dev/null +++ b/addons/godot-playfab/Models/InventoryAlternateId.gd.uid @@ -0,0 +1 @@ +uid://bglbhxuakewmv diff --git a/addons/godot-playfab/Models/InventoryItem.gd b/addons/godot-playfab/Models/InventoryItem.gd new file mode 100644 index 00000000..71403e60 --- /dev/null +++ b/addons/godot-playfab/Models/InventoryItem.gd @@ -0,0 +1,38 @@ +extends JsonSerializable +class_name InventoryItem + +## The amount of the item. +var Amount: float + + +## Game specific properties for display purposes. This is an arbitrary JSON blob. The Display Properties field has a 1000 byte limit. +var DisplayProperties: Dictionary[String, Variant] + + +## Only used for subscriptions. The date of when the item will expire in UTC. +var ExpirationDate: String + + +## The id of the item. This should correspond to the item id in the catalog. +var Id: String + + +## The stack id of the item. +var StackId: String + + +## The type of the item. This should correspond to the item type in the catalog. +var Type: String + + + +func _get_type_for_property(property_name: String) -> String: + match property_name: +# "": +# return "" + _: + pass + + push_error("Could not find mapping for property: " + property_name) + return super._get_type_for_property(property_name) + diff --git a/addons/godot-playfab/Models/InventoryItem.gd.uid b/addons/godot-playfab/Models/InventoryItem.gd.uid new file mode 100644 index 00000000..b2bb0730 --- /dev/null +++ b/addons/godot-playfab/Models/InventoryItem.gd.uid @@ -0,0 +1 @@ +uid://b3ldgqvqu630p diff --git a/addons/godot-playfab/Models/InventoryItemReference.gd b/addons/godot-playfab/Models/InventoryItemReference.gd new file mode 100644 index 00000000..c3f31219 --- /dev/null +++ b/addons/godot-playfab/Models/InventoryItemReference.gd @@ -0,0 +1,25 @@ +extends JsonSerializable +class_name InventoryItemReference + +## The inventory item alternate id the request applies to. +var AlternateId: InventoryAlternateId + + +## The inventory item id the request applies to. +var Id: String + + +## The inventory stack id the request should redeem to. (Default="default") +var StackId: String + + + +func _get_type_for_property(property_name: String) -> String: + match property_name: +# "": +# return "" + _: + pass + + push_error("Could not find mapping for property: " + property_name) + return super._get_type_for_property(property_name) diff --git a/addons/godot-playfab/Models/InventoryItemReference.gd.uid b/addons/godot-playfab/Models/InventoryItemReference.gd.uid new file mode 100644 index 00000000..fba2d154 --- /dev/null +++ b/addons/godot-playfab/Models/InventoryItemReference.gd.uid @@ -0,0 +1 @@ +uid://bhplyquxguh6v diff --git a/addons/godot-playfab/Models/ModerationState.gd b/addons/godot-playfab/Models/ModerationState.gd new file mode 100644 index 00000000..5628d0aa --- /dev/null +++ b/addons/godot-playfab/Models/ModerationState.gd @@ -0,0 +1,29 @@ +extends JsonSerializable +class_name ModerationState + +## The date and time this moderation state was last updated. +var LastModifiedDate: String + +## The current stated reason for the associated item being moderated. +var Reason: String + +## The current moderation status for the associated item. +var Status: ModerationStatus + + +func _get_type_for_property(property_name: String) -> String: + match property_name: +# "": +# return "" + _: + pass + + push_error("Could not find mapping for property: " + property_name) + return super._get_type_for_property(property_name) + +enum ModerationStatus { + Unknown, + Moderation, + Approved, + Rejected +} diff --git a/addons/godot-playfab/Models/ModerationState.gd.uid b/addons/godot-playfab/Models/ModerationState.gd.uid new file mode 100644 index 00000000..4081c8ff --- /dev/null +++ b/addons/godot-playfab/Models/ModerationState.gd.uid @@ -0,0 +1 @@ +uid://b8ieu8ubpoxe diff --git a/addons/godot-playfab/Models/Permissions.gd b/addons/godot-playfab/Models/Permissions.gd new file mode 100644 index 00000000..c101a4b3 --- /dev/null +++ b/addons/godot-playfab/Models/Permissions.gd @@ -0,0 +1,17 @@ +extends JsonSerializable +class_name Permissions + +## The list of ids of Segments that the a player can be in to purchase from the store. When a value is provided, the player must be in at least one of the segments listed for the purchase to be allowed. +var SegmentIds: Array[String] + + +func _get_type_for_property(property_name: String) -> String: + match property_name: +# "": +# return "" + _: + pass + + push_error("Could not find mapping for property: " + property_name) + return super._get_type_for_property(property_name) + diff --git a/addons/godot-playfab/Models/Permissions.gd.uid b/addons/godot-playfab/Models/Permissions.gd.uid new file mode 100644 index 00000000..e5725ad3 --- /dev/null +++ b/addons/godot-playfab/Models/Permissions.gd.uid @@ -0,0 +1 @@ +uid://djkikiemt13dt diff --git a/addons/godot-playfab/Models/PlayFabImage.gd b/addons/godot-playfab/Models/PlayFabImage.gd new file mode 100644 index 00000000..5816980a --- /dev/null +++ b/addons/godot-playfab/Models/PlayFabImage.gd @@ -0,0 +1,25 @@ +extends JsonSerializable +class_name PlayFabImage + +## The image unique ID. +var Id: String + +## The client-defined tag associated with this image. Tags must be defined in the Catalog Config before being used in images +var Tag: String + +## Images can be defined as either a "thumbnail" or "screenshot". There can only be one "thumbnail" image per item. +var Type: String + +## The URL for retrieval of the image. +var Url: String + + +func _get_type_for_property(property_name: String) -> String: + match property_name: +# "": +# return "" + _: + pass + + push_error("Could not find mapping for property: " + property_name) + return super._get_type_for_property(property_name) diff --git a/addons/godot-playfab/Models/PlayFabImage.gd.uid b/addons/godot-playfab/Models/PlayFabImage.gd.uid new file mode 100644 index 00000000..5675f787 --- /dev/null +++ b/addons/godot-playfab/Models/PlayFabImage.gd.uid @@ -0,0 +1 @@ +uid://bh2jvq70ut0oj diff --git a/addons/godot-playfab/Models/PurchaseInventoryItemsRequest.gd b/addons/godot-playfab/Models/PurchaseInventoryItemsRequest.gd new file mode 100644 index 00000000..3252e06f --- /dev/null +++ b/addons/godot-playfab/Models/PurchaseInventoryItemsRequest.gd @@ -0,0 +1,97 @@ +extends JsonSerializable +class_name PurchaseInventoryItemsRequest + +## The amount to purchase. +var Amount: int + + +## The id of the entity's collection to perform this action on. (Default="default"). The number of inventory collections is unlimited. +var CollectionId: String + + +## The optional custom tags associated with the request (e.g. build number, external trace identifiers, etc.). +var CustomTags: Dictionary[String, Variant] + + +## Indicates whether stacks reduced to an amount of 0 during the request should be deleted from the inventory. (Default=false) +var DeleteEmptyStacks: bool + + +## The duration to purchase. +var DurationInSeconds: float + + +## ETags are used for concurrency checking when updating resources. More information about using ETags can be found here: https://learn.microsoft.com/en-us/gaming/playfab/features/economy-v2/catalog/etags +var ETag: String + + +## The entity to perform this action on. +var Entity: EntityKey + + +## The Idempotency ID for this request. Idempotency IDs can be used to prevent operation replay in the medium term but will be garbage collected eventually. +var IdempotencyId: String + + +## The inventory item the request applies to. +var Item: InventoryItemReference + + +## The values to apply to a stack newly created by this request. +var NewStackValues: InitialValues + + +## The per-item price the item is expected to be purchased at. This must match a value configured in the Catalog or specified Store. +var PriceAmounts: Array[PurchasePriceAmount] + + +## The id of the Store to purchase the item from. +var StoreId: String + + + +func _get_type_for_property(property_name: String) -> String: + match property_name: +# "": +# return "" + _: + pass + + push_error("Could not find mapping for property: " + property_name) + return super._get_type_for_property(property_name) + +static func from_catalog_item(purchase_amount: float, catalog_item: CatalogItem) -> PurchaseInventoryItemsRequest: + + var request_data := PurchaseInventoryItemsRequest.new() + request_data.Amount = purchase_amount + + var item_reference = InventoryItemReference.new() + item_reference.Id = catalog_item.Id +# item_reference.AlternateId = catalog_item.AlternateIds[0] +# item_reference.StackId = catalog_item.DefaultStackId + request_data.Item = item_reference + + var price_amounts : Array[PurchasePriceAmount] + + # Find the price option that matches the purchase amount + var catalog_item_price_key = catalog_item.PriceOptions.Prices.find_custom(func(item: CatalogPrice) -> bool: + if item.UnitAmount == purchase_amount: + return true + + return false + ) + + if catalog_item_price_key == -1: + push_error("Could not find price option for item: " + catalog_item.Id + " with amount: " + str(purchase_amount)) + return request_data + + + for catalog_price_amount in catalog_item.PriceOptions.Prices[catalog_item_price_key].Amounts: + var price_amount = PurchasePriceAmount.new() + price_amount.ItemId = catalog_price_amount.ItemId + price_amount.Amount = catalog_price_amount.Amount + price_amounts.append(price_amount) + + request_data.PriceAmounts = price_amounts + + return request_data diff --git a/addons/godot-playfab/Models/PurchaseInventoryItemsRequest.gd.uid b/addons/godot-playfab/Models/PurchaseInventoryItemsRequest.gd.uid new file mode 100644 index 00000000..e42e34a6 --- /dev/null +++ b/addons/godot-playfab/Models/PurchaseInventoryItemsRequest.gd.uid @@ -0,0 +1 @@ +uid://biicttm14u1jv diff --git a/addons/godot-playfab/Models/PurchaseInventoryItemsResponse.gd b/addons/godot-playfab/Models/PurchaseInventoryItemsResponse.gd new file mode 100644 index 00000000..51d02251 --- /dev/null +++ b/addons/godot-playfab/Models/PurchaseInventoryItemsResponse.gd @@ -0,0 +1,25 @@ +extends JsonSerializable +class_name PurchaseInventoryItemsResponse + +## ETags are used for concurrency checking when updating resources. More information about using ETags can be found here: https://learn.microsoft.com/en-us/gaming/playfab/features/economy-v2/catalog/etags +var ETag: String + + +## The idempotency id used in the request. +var IdempotencyId: String + + +## The ids of transactions that occurred as a result of the request. +var TransactionIds: Array[String] + + + +func _get_type_for_property(property_name: String) -> String: + match property_name: +# "": +# return "" + _: + pass + + push_error("Could not find mapping for property: " + property_name) + return super._get_type_for_property(property_name) diff --git a/addons/godot-playfab/Models/PurchaseInventoryItemsResponse.gd.uid b/addons/godot-playfab/Models/PurchaseInventoryItemsResponse.gd.uid new file mode 100644 index 00000000..2dd8c29b --- /dev/null +++ b/addons/godot-playfab/Models/PurchaseInventoryItemsResponse.gd.uid @@ -0,0 +1 @@ +uid://btrm2ufv2o4wl diff --git a/addons/godot-playfab/Models/PurchasePriceAmount.gd b/addons/godot-playfab/Models/PurchasePriceAmount.gd new file mode 100644 index 00000000..b61d2cb6 --- /dev/null +++ b/addons/godot-playfab/Models/PurchasePriceAmount.gd @@ -0,0 +1,25 @@ +extends JsonSerializable +class_name PurchasePriceAmount + +## The amount of the inventory item to use in the purchase . +var Amount: int + + +## The inventory item id to use in the purchase . +var ItemId: String + + +## The inventory stack id the to use in the purchase. Set to "default" by default +var StackId: String + + + +func _get_type_for_property(property_name: String) -> String: + match property_name: +# "": +# return "" + _: + pass + + push_error("Could not find mapping for property: " + property_name) + return super._get_type_for_property(property_name) diff --git a/addons/godot-playfab/Models/PurchasePriceAmount.gd.uid b/addons/godot-playfab/Models/PurchasePriceAmount.gd.uid new file mode 100644 index 00000000..6706a3cb --- /dev/null +++ b/addons/godot-playfab/Models/PurchasePriceAmount.gd.uid @@ -0,0 +1 @@ +uid://dt70qytxnkmvp diff --git a/addons/godot-playfab/Models/Rating.gd b/addons/godot-playfab/Models/Rating.gd new file mode 100644 index 00000000..8090a43a --- /dev/null +++ b/addons/godot-playfab/Models/Rating.gd @@ -0,0 +1,35 @@ +extends JsonSerializable +class_name Rating + +## The average rating for this item. +var Average: float + +## The total count of 1 star ratings for this item. +var Count1Star: int + +## The total count of 2 star ratings for this item. +var Count2Star: int + +## The total count of 3 star ratings for this item. +var Count3Star: int + +## The total count of 4 star ratings for this item. +var Count4Star: int + +## The total count of 5 star ratings for this item. +var Count5Star: int + +## The total count of ratings for this item. +var TotalCount: int + + +func _get_type_for_property(property_name: String) -> String: + match property_name: +# "": +# return "" + _: + pass + + push_error("Could not find mapping for property: " + property_name) + return super._get_type_for_property(property_name) + diff --git a/addons/godot-playfab/Models/Rating.gd.uid b/addons/godot-playfab/Models/Rating.gd.uid new file mode 100644 index 00000000..98c24f5f --- /dev/null +++ b/addons/godot-playfab/Models/Rating.gd.uid @@ -0,0 +1 @@ +uid://mh1qbb3eqmkc diff --git a/addons/godot-playfab/Models/RealMoneyPriceDetails.gd b/addons/godot-playfab/Models/RealMoneyPriceDetails.gd new file mode 100644 index 00000000..0334c125 --- /dev/null +++ b/addons/godot-playfab/Models/RealMoneyPriceDetails.gd @@ -0,0 +1,32 @@ +extends JsonSerializable +class_name RealMoneyPriceDetails + +## The 'AppleAppStore' price amount per CurrencyCode. 'USD' supported only. +var AppleAppStorePrices: Object + +## The 'GooglePlay' price amount per CurrencyCode. 'USD' supported only. +var GooglePlayPrices: Object + +## The 'MicrosoftStore' price amount per CurrencyCode. 'USD' supported only. +var MicrosoftStorePrices: Object + +## The 'NintendoEShop' price amount per CurrencyCode. 'USD' supported only. +var NintendoEShopPrices: Object + +## The 'PlayStationStore' price amount per CurrencyCode. 'USD' supported only. +var PlayStationStorePrices: Object + +## The 'Steam' price amount per CurrencyCode. 'USD' supported only. +var SteamPrices: Object + + +func _get_type_for_property(property_name: String) -> String: + match property_name: +# "": +# return "" + _: + pass + + push_error("Could not find mapping for property: " + property_name) + return super._get_type_for_property(property_name) + diff --git a/addons/godot-playfab/Models/RealMoneyPriceDetails.gd.uid b/addons/godot-playfab/Models/RealMoneyPriceDetails.gd.uid new file mode 100644 index 00000000..5b362c53 --- /dev/null +++ b/addons/godot-playfab/Models/RealMoneyPriceDetails.gd.uid @@ -0,0 +1 @@ +uid://c6i574furyeqs diff --git a/addons/godot-playfab/Models/SearchItemsRequest.gd b/addons/godot-playfab/Models/SearchItemsRequest.gd new file mode 100644 index 00000000..d56bfc56 --- /dev/null +++ b/addons/godot-playfab/Models/SearchItemsRequest.gd @@ -0,0 +1,47 @@ +extends JsonSerializable +class_name SearchItemsRequest + +## Number of items to retrieve. This value is optional. Maximum page size is 50. Default value is 10. +var Count: int + +## An opaque token used to retrieve the next page of items, if any are available. +var ContinuationToken: String + +## The optional custom tags associated with the request (e.g. build number, external trace identifiers, etc.). +var CustomTags: Dictionary + +## The entity to perform this action on. +var Entity: EntityKey + +## An OData filter used to refine the search query (For example: "type eq 'ugc'"). More info about Filter Complexity limits can be found here: https://learn.microsoft.com/en-us/gaming/playfab/features/economy-v2/catalog/search#limits +var Filter: String + +## The locale to be returned in the result. +var Language: String + +## An OData orderBy used to order the results of the search query. For example: "rating/average asc" +var OrderBy: String + +## The text to search for. +var Search: String + +## An OData select query option used to augment the search results. If not defined, the default search result metadata will be returned. +var Select: String + +## The store to restrict the search request to. +var Store: StoreReference + + +func _get_type_for_property(property_name: String) -> String: + match property_name: +# "": +# return "" + "Entity": + return "EntityKey" + "Store": + return "StoreReference" + _: + pass + + push_error("Could not find mapping for property: " + property_name) + return super._get_type_for_property(property_name) diff --git a/addons/godot-playfab/Models/SearchItemsRequest.gd.uid b/addons/godot-playfab/Models/SearchItemsRequest.gd.uid new file mode 100644 index 00000000..60d15ebd --- /dev/null +++ b/addons/godot-playfab/Models/SearchItemsRequest.gd.uid @@ -0,0 +1 @@ +uid://c40xsh48tsvg3 diff --git a/addons/godot-playfab/Models/SearchItemsResponse.gd b/addons/godot-playfab/Models/SearchItemsResponse.gd new file mode 100644 index 00000000..2a70967f --- /dev/null +++ b/addons/godot-playfab/Models/SearchItemsResponse.gd @@ -0,0 +1,19 @@ +extends JsonSerializable +class_name SearchItemsResponse + +## An opaque token used to retrieve the next page of items, if any are available. +var ContinuationToken: String + +## The paginated set of results for the search query. +var Items: Array[CatalogItem] + + +func _get_type_for_property(property_name: String) -> String: + match property_name: +# "": +# return "" + _: + pass + + push_error("Could not find mapping for property: " + property_name) + return super._get_type_for_property(property_name) diff --git a/addons/godot-playfab/Models/SearchItemsResponse.gd.uid b/addons/godot-playfab/Models/SearchItemsResponse.gd.uid new file mode 100644 index 00000000..8fe48b67 --- /dev/null +++ b/addons/godot-playfab/Models/SearchItemsResponse.gd.uid @@ -0,0 +1 @@ +uid://dbxfgoxaq2ssl diff --git a/addons/godot-playfab/Models/StoreDetails.gd b/addons/godot-playfab/Models/StoreDetails.gd new file mode 100644 index 00000000..e6a7ee60 --- /dev/null +++ b/addons/godot-playfab/Models/StoreDetails.gd @@ -0,0 +1,23 @@ +extends JsonSerializable +class_name StoreDetails + +## The options for the filter in filter-based stores. These options are mutually exclusive with item references. +var FilterOptions: FilterOptions + +## The permissions that control which players can purchase from the store. +var Permissions: Permissions + +## The global prices utilized in the store. These options are mutually exclusive with price options in item references. +var PriceOptionsOverride: CatalogPriceOptionsOverride + + +func _get_type_for_property(property_name: String) -> String: + match property_name: +# "": +# return "" + _: + pass + + push_error("Could not find mapping for property: " + property_name) + return super._get_type_for_property(property_name) + diff --git a/addons/godot-playfab/Models/StoreDetails.gd.uid b/addons/godot-playfab/Models/StoreDetails.gd.uid new file mode 100644 index 00000000..949a3d60 --- /dev/null +++ b/addons/godot-playfab/Models/StoreDetails.gd.uid @@ -0,0 +1 @@ +uid://d375y8vs4whit diff --git a/addons/godot-playfab/Models/StoreReference.gd b/addons/godot-playfab/Models/StoreReference.gd new file mode 100644 index 00000000..d0da77cb --- /dev/null +++ b/addons/godot-playfab/Models/StoreReference.gd @@ -0,0 +1,22 @@ +extends JsonSerializable +class_name StoreReference + +## An alternate ID of the store. +var AlternateId: CatalogAlternateId + +## The unique ID of the store. +var Id: String + + +func _get_type_for_property(property_name: String) -> String: + match property_name: +# "": +# return "" + "AlternateId": + return "CatalogAlternateId" + _: + pass + + push_error("Could not find mapping for property: " + property_name) + return super._get_type_for_property(property_name) + diff --git a/addons/godot-playfab/Models/StoreReference.gd.uid b/addons/godot-playfab/Models/StoreReference.gd.uid new file mode 100644 index 00000000..24692bd4 --- /dev/null +++ b/addons/godot-playfab/Models/StoreReference.gd.uid @@ -0,0 +1 @@ +uid://cmrufw3fntl7u diff --git a/addons/godot-playfab/ObjectPool.gd b/addons/godot-playfab/ObjectPool.gd new file mode 100644 index 00000000..9090875c --- /dev/null +++ b/addons/godot-playfab/ObjectPool.gd @@ -0,0 +1,35 @@ +extends Node +class_name ObjectPool + + +var _prototype: Object +var _idle_pool: Array = [] +var _busy_pool: Array = [] + + +func _init(prototype: Object) -> void: + _prototype = prototype + + +func get_instance() -> Object: + if _idle_pool.size() > 0: + var instance = _idle_pool.pop_back() + _busy_pool.append(instance) + return instance + else: + var instance: Object = _create_instance() + _busy_pool.append(instance) + return instance + + +func _create_instance() -> Object: + var instance: Object = _prototype.duplicate() + add_child(instance) + return instance + +func release_instance(instance: Object) -> void: + if instance in _busy_pool: + _busy_pool.erase(instance) + _idle_pool.append(instance) + else: + push_error("Instance not found in busy pool.") diff --git a/addons/godot-playfab/ObjectPool.gd.uid b/addons/godot-playfab/ObjectPool.gd.uid new file mode 100644 index 00000000..f1a77f34 --- /dev/null +++ b/addons/godot-playfab/ObjectPool.gd.uid @@ -0,0 +1 @@ +uid://bx7jt16irkvka diff --git a/addons/godot-playfab/PlayFab.gd b/addons/godot-playfab/PlayFab.gd index 53719743..36f43709 100644 --- a/addons/godot-playfab/PlayFab.gd +++ b/addons/godot-playfab/PlayFab.gd @@ -12,6 +12,8 @@ signal logged_in(login_result) enum AUTH_TYPE {SESSION_TICKET, ENTITY_TOKEN} +const LANG_NEUTRAL := "NEUTRAL" + func _init(): @@ -23,7 +25,8 @@ func _init(): func _ready(): super._ready() - connect("logged_in",Callable(self,"_on_logged_in")) + connect("logged_in",_on_logged_in) + connect("api_error",_on_api_error) func _on_logged_in(login_result: LoginResult): @@ -81,14 +84,14 @@ func login_with_custom_id(custom_id: String, create_user: bool, info_request_par func login_with_steam(steam_auth_ticket: String, is_auth_ticket_for_api: bool, create_account: bool, info_request_parameters: GetPlayerCombinedInfoRequestParams) -> void: PlayFabManager.client_config.login_type = PlayFabClientConfig.LoginType.LOGIN_STEAM PlayFabManager.client_config.login_id = steam_auth_ticket - + var request_params = LoginWithSteamRequest.new() request_params.TitleId = _title_id request_params.CreateAccount = create_account request_params.InfoRequestParameters = info_request_parameters request_params.SteamTicket = steam_auth_ticket request_params.TicketIsServiceSpecific = is_auth_ticket_for_api - + var result = _post(request_params, "/Client/LoginWithSteam", _on_login) # Anonymous login with a GUID as username @@ -192,3 +195,16 @@ func _add_auth_headers(additional_headers: Dictionary, auth_type) -> bool: push_error("auth_type \"" + auth_type + "\" is invalid") return true + + +func _on_api_error(api_error_wrapper: ApiErrorWrapper): + var text: String = "%s\n\n" % api_error_wrapper.errorMessage + var error_details = api_error_wrapper.errorDetails + + if error_details: + for key in error_details.keys(): + text += key + for element in error_details[key]: + text += "%s\n" % element + + push_error(text) diff --git a/addons/godot-playfab/PlayFabCatalog.gd b/addons/godot-playfab/PlayFabCatalog.gd new file mode 100644 index 00000000..3a3ed6ea --- /dev/null +++ b/addons/godot-playfab/PlayFabCatalog.gd @@ -0,0 +1,123 @@ +@icon("res://addons/godot-playfab/icon.png") + +extends PlayFab +class_name PlayFabCatalog + +signal search_complete +signal search_currency_complete + +const PAGE_SIZE := 50 +const FULL_CATALOG_FETCH_TIMEOUT := 3600 # seconds + +var _catalog: Dictionary[String, CatalogItem] = {} # item_id -> ShopItem +var _last_catalog_fetch_time: int = 0 +var _fetching_catalog := false +var _has_full_catalog := false + +# Search for all items using PlayFabManager.catalog.search_items() with pagination +var _search_results : Dictionary[String, CatalogItem] = {} +var continuation_token := "" + +func _ready(): + PlayFabManager.playfab_initialized.connect(func(): + get_catalog() + ) + + +## Returns the cached catalog, fetching it if it's older than 5 minutes. +func get_catalog() -> Dictionary[String, CatalogItem]: + if _last_catalog_fetch_time == 0 or Time.get_unix_time_from_system() - _last_catalog_fetch_time > FULL_CATALOG_FETCH_TIMEOUT: + fetch_catalog() + return _catalog + +## Fetches the entire catalog with pagination. +## Emits [search_complete] when done. +func fetch_catalog() -> void: + if _fetching_catalog: + return + _fetching_catalog = true + _last_catalog_fetch_time = Time.get_unix_time_from_system() + _search_results.clear() + _fetch_catalog_page() + +## Internal function to fetch a page of search results. +func _fetch_catalog_page(token: String = "") -> void: + var request_data: SearchItemsRequest = SearchItemsRequest.new() + request_data.Search = "" +# request_data.Filter = "type ne 'currency'" +# request_data.Filter "tags/any(t:t eq 'desert') and contentType eq 'gameitem'" + request_data.OrderBy = "CreationDate asc" + request_data.ContinuationToken = token + request_data.Count = PAGE_SIZE +# request_data.Language = _locale + + if token != "": + request_data.ContinuationToken = token + + search_items(request_data, _on_search_page_ok) + +## Searches for currencies. +## Not paginated! +## Emits [search_currency_complete] when done. +func search_currency(callback: Callable = func(): pass) -> void: + var request_data: SearchItemsRequest = SearchItemsRequest.new() + request_data.Search = "" + request_data.Filter = "type eq 'currency'" + request_data.OrderBy = "CreationDate asc" + request_data.Count = PAGE_SIZE + + search_items(request_data, _on_search_currency_complete) + +## Internal callback for search_currency +func _on_search_currency_complete(result: Dictionary) -> void: + var res = SearchItemsResponse.new() + res.from_dict(result.data, res) + var results_items : Dictionary[String, CatalogItem] = {} + for item: CatalogItem in res.Items: + results_items[item.Id] = item + + search_currency_complete.emit(results_items) + + +func _on_search_page_ok(result: Dictionary) -> void: + var res = SearchItemsResponse.new() + res.from_dict(result.data, res) + for item: CatalogItem in res.Items: + _search_results[item.Id] = item + + var next_token: String = res.ContinuationToken + if next_token != null and next_token != "": + _fetch_catalog_page(next_token) + else: + _fetching_catalog = false + _has_full_catalog = true + _catalog.clear() + _catalog = _search_results + search_complete.emit() + + +## Resolves an item from the cached catalog by its item ID. +func resolve_item(item_id: String) -> CatalogItem: + if item_id in _catalog: + return _catalog[item_id] + return null + + +## Retrieves items from the public catalog. Up to 50 items can be returned at once. +## GetItems does not work off a cache of the Catalog and should be used when trying to get recent item updates. +## However, please note that item references data is cached and may take a few moments for changes to propagate. +## Callback receives a [GetItemsResponse] +## @tutorial(Request Documentation): https://docs.microsoft.com/gaming/playfab/features/economy/catalog/get-items +func get_items(request_data: GetItemsRequest = GetItemsRequest.new(), callback: Callable = func(): pass): + _post_with_entity_auth(request_data, "/Catalog/GetItems", callback) + +## Executes a search against the public catalog using the provided search parameters +## and returns a set of paginated results. +## SearchItems uses a cache of the catalog with item updates taking up to a few minutes to propagate. +## You should use the GetItem API for when trying to immediately get recent item updates. +## Callback receives a [SearchItemsResponse] of items which can be paginated with a continuation token. +## More information about the Search API can be found here: +## @tutorial(Search API): https://learn.microsoft.com/en-us/gaming/playfab/features/economy-v2/catalog/search +## @tutorial(Request Documentation): https://docs.microsoft.com/gaming/playfab/features/economy/catalog/get-items +func search_items(request_data: SearchItemsRequest = SearchItemsRequest.new(), callback: Callable = func(): pass): + _post_with_entity_auth(request_data, "/Catalog/SearchItems", callback) diff --git a/addons/godot-playfab/PlayFabCatalog.gd.uid b/addons/godot-playfab/PlayFabCatalog.gd.uid new file mode 100644 index 00000000..60017b32 --- /dev/null +++ b/addons/godot-playfab/PlayFabCatalog.gd.uid @@ -0,0 +1 @@ +uid://3g8wadxdycsq diff --git a/addons/godot-playfab/PlayFabHttp.gd b/addons/godot-playfab/PlayFabHttp.gd index 54ff3587..fa542546 100644 --- a/addons/godot-playfab/PlayFabHttp.gd +++ b/addons/godot-playfab/PlayFabHttp.gd @@ -29,6 +29,23 @@ func _ready(): _http = HTTPRequest.new() add_child(_http) + api_error.connect(func(api_error_wrapper: ApiErrorWrapper): + var text = "[b]%s[/b]\n\n" % api_error_wrapper.errorMessage + var error_details = api_error_wrapper.errorDetails + + if error_details: + for key in error_details.keys(): + text += "[color=red][b]%s[/b][/color]: " % key + for element in error_details[key]: + text += "%s\n" % element + + print_rich(text) + ) + + server_error.connect(func(path: String): + push_error("A server error occured while querying %s" % path) + ) + func _dict_to_header_array(dict: Dictionary): if dict.size() < 1: @@ -47,6 +64,26 @@ func _get_api_url() -> String: func _http_request(request_method: int, body: Dictionary, path: String, callback: Callable, additional_headers: Dictionary = {}): + var http_response: PlayFabHttpResult = await _individual_http_request(request_method, body, path, callback, additional_headers) + if http_response.response_code >= 200 and http_response.response_code < 400: + if callback != null: + if callback.is_valid(): + callback.call(http_response.json_parse_result) + else: + push_error("Response calback " + callback.get_method() + " is no longer valid! Make sure, a script is only removed after all requests returned!") + return + elif http_response.response_code >= 400: + var apiErrorWrapper = ApiErrorWrapper.new() + for key in http_response.json_parse_result.keys(): + apiErrorWrapper.set(key, http_response.json_parse_result[key]) + api_error.emit(apiErrorWrapper) + return + if http_response.response_code >= 500: + server_error.emit(path) + return + + +func _individual_http_request(request_method: int, body: Dictionary, path: String, callback: Callable, additional_headers: Dictionary = {}) -> PlayFabHttpResult: # Create a new HTTPRequest instance for each request var http_request = HTTPRequest.new() add_child(http_request) @@ -75,32 +112,5 @@ func _http_request(request_method: int, body: Dictionary, path: String, callback # After the request completes, remove the node http_request.queue_free() - var response_result = args[0] as int - var response_code = args[1] as int - var response_headers = args[2] as PackedStringArray - var response_body = args[3] as PackedByteArray - - var response_body_string = response_body.get_string_from_utf8() - var test_json_conv = JSON.new() - var parse_error = test_json_conv.parse(response_body_string) - var json_parse_result = test_json_conv.data - - if parse_error != OK: - emit_signal("json_parse_error", json_parse_result) - return - if response_code >= 200 and response_code < 400: - if callback != null: - if callback.is_valid(): - callback.call(json_parse_result) - else: - push_error("Response calback " + callback.get_method() + " is no longer valid! Make sure, a script is only removed after all requests returned!") - return - elif response_code >= 400: - var apiErrorWrapper = ApiErrorWrapper.new() - for key in json_parse_result.keys(): - apiErrorWrapper.set(key, json_parse_result[key]) - emit_signal("api_error", apiErrorWrapper) - return - if response_code >= 500: - emit_signal("server_error", path) - return + var http_result := PlayFabHttpResult.new(args) + return http_result diff --git a/addons/godot-playfab/PlayFabHttpResult.gd b/addons/godot-playfab/PlayFabHttpResult.gd new file mode 100644 index 00000000..7e5dc366 --- /dev/null +++ b/addons/godot-playfab/PlayFabHttpResult.gd @@ -0,0 +1,55 @@ +extends RefCounted +class_name PlayFabHttpResult + +const CONTINUATION_TOKEN_KEY_NAME := "ContinuationToken" + +var _json_parse_result: Dictionary + + +var response_result: int + +var response_code: int + +var response_headers: PackedStringArray + +var response_body: PackedByteArray + +var json_parse_result: Dictionary: + get: + return _json_parse_result + +var _continuation_token: String = "" +var continuation_token: String: + get: + return _continuation_token + +func _init(args) -> void: + response_result = args[0] as int + response_code = args[1] as int + response_headers = args[2] as PackedStringArray + response_body = args[3] as PackedByteArray + + _json_parse_result = _parse_response_body() + +func _parse_response_body() -> Dictionary: + var response_body_string: String = response_body.get_string_from_utf8() + var test_json_conv = JSON.new() + var parse_error = test_json_conv.parse(response_body_string) + var json = test_json_conv.data + + if parse_error != OK: + push_error("Failed to parse JSON response body.") + + return json + +## Checks, whether the result has a continuation token. +func has_continuation_token() -> bool: + var token: String = get_continuation_token() + if token == null or token == "": + return false + + return true + +func get_continuation_token() -> String: + _continuation_token = self._json_parse_result["data"].get(CONTINUATION_TOKEN_KEY_NAME, "") + return _continuation_token diff --git a/addons/godot-playfab/PlayFabHttpResult.gd.uid b/addons/godot-playfab/PlayFabHttpResult.gd.uid new file mode 100644 index 00000000..86c5bac5 --- /dev/null +++ b/addons/godot-playfab/PlayFabHttpResult.gd.uid @@ -0,0 +1 @@ +uid://c6jjnj8il6355 diff --git a/addons/godot-playfab/PlayFabInventory.gd b/addons/godot-playfab/PlayFabInventory.gd new file mode 100644 index 00000000..4e700915 --- /dev/null +++ b/addons/godot-playfab/PlayFabInventory.gd @@ -0,0 +1,78 @@ +@icon("res://addons/godot-playfab/icon.png") + +extends PlayFab +class_name PlayFabInventory + +const PAGE_SIZE := 50 +const TURBOLOAD_PAGE_SIZE := 10000 +const FULL_INVENTORY_CACHE_DURATION := 3600 # seconds + + +var _inventory_items: Dictionary[String, InventoryItem] = {} # item_id -> InventoryItem +var _last_inventory_fetch_time: int = 0 +var _fetching_inventory := false +var _has_full_inventory := false + + +func _ready(): + PlayFabManager.playfab_initialized.connect(func(): + turboload_inventory() + ) + + +## Get current inventory items stored locally. +## Returns a dictionary of InventoryItem objects, keyed by their item IDs. +func get_inventory() -> Dictionary[String, InventoryItem]: + if _last_inventory_fetch_time == 0 or Time.get_unix_time_from_system() - _last_inventory_fetch_time > FULL_INVENTORY_CACHE_DURATION: + turboload_inventory() + return _inventory_items + +## Get current inventory items and store them locally. +## Callback receives a [GetInventoryItemsResponse] +## @tutorial: https://learn.microsoft.com/en-us/gaming/playfab/economy-monetization/economy-v2/inventory/turboloading +func turboload_inventory(callback: Callable = func(): pass) -> void: + if _fetching_inventory: + callback.call(GetInventoryItemsResponse.new()) + return + + _fetching_inventory = true + + var request_data: GetInventoryItemsRequest = GetInventoryItemsRequest.new() + request_data.Count = TURBOLOAD_PAGE_SIZE + get_inventory_items(request_data, func(result: GetInventoryItemsResponse) -> void: + _inventory_items.clear() + for item in result.Items: + _inventory_items[item.Id] = item + + _fetching_inventory = false + _has_full_inventory = true + _last_inventory_fetch_time = Time.get_unix_time_from_system() + + if callback.is_valid(): + callback.call() + ) + + + +## Get current inventory items. +## Callback receives a [GetInventoryItemsResponse] +## @tutorial(Request Documentation): https://learn.microsoft.com/en-us/rest/api/playfab/economy/inventory/get-inventory-items?view=playfab-rest +func get_inventory_items(request_data: GetInventoryItemsRequest = GetInventoryItemsRequest.new(), callback: Callable = func(): pass): + _post_with_entity_auth(request_data, "/Inventory/GetInventoryItems", func(result: Dictionary) -> void: + var res = GetInventoryItemsResponse.new() + res.from_dict(result.data, res) + callback.call(res) + ) + + +## Purchase an item or bundle. +## Up to 10,000 stacks of items can be added to a single inventory collection. +## Stack size is uncapped. +## @turorial(Quickstart): https://learn.microsoft.com/en-us/gaming/playfab/economy-monetization/economy-v2/inventory/quickstart#purchase-the-item +## @tutorial(Request Documentation): https://learn.microsoft.com/en-us/rest/api/playfab/economy/inventory/purchase-inventory-items?view=playfab-rest +func purchase_inventory_items(request_data: PurchaseInventoryItemsRequest, callback: Callable = func(): pass) -> void: + _post_with_entity_auth(request_data, "/Inventory/PurchaseInventoryItems", func(result: Dictionary) -> void: + var res = PurchaseInventoryItemsResponse.new() + res.from_dict(result.data, res) + callback.call(res) + ) diff --git a/addons/godot-playfab/PlayFabInventory.gd.uid b/addons/godot-playfab/PlayFabInventory.gd.uid new file mode 100644 index 00000000..52218fcf --- /dev/null +++ b/addons/godot-playfab/PlayFabInventory.gd.uid @@ -0,0 +1 @@ +uid://cv1wacridumt2 diff --git a/addons/godot-playfab/PlayFabManager.gd b/addons/godot-playfab/PlayFabManager.gd index 149c3a90..3b595e30 100644 --- a/addons/godot-playfab/PlayFabManager.gd +++ b/addons/godot-playfab/PlayFabManager.gd @@ -2,8 +2,10 @@ extends Node # This is script must be auto-loaded as `PlayFabManager`. # Use it as a global state/config manager for PlayFab data, like login persistence. +signal playfab_initialized + # Handles saving/loading of the `PlayFabClientConfig` -var _client_config_loader = PlayFabClientConfigLoader.new() +var _client_config_loader := PlayFabClientConfigLoader.new() # **READONLY** # The Tile ID to use for this project. Will be pulled from ProjectSettings. @@ -22,6 +24,14 @@ var client : PlayFabClient = PlayFabClient.new() # see https://docs.microsoft.com/en-us/rest/api/playfab/events/?view=playfab-rest var event: PlayFabEvent = PlayFabEvent.new() +## Represents the PlayFab `Catalog` (Economy V2) API +## @tutorial: https://learn.microsoft.com/en-us/rest/api/playfab/economy/catalog?view=playfab-rest +var catalog: PlayFabCatalog = PlayFabCatalog.new() + +## Represents the PlayFab `Inventory` (Economy V2) API +## @tutorial: https://learn.microsoft.com/en-us/rest/api/playfab/economy/inventory?view=playfab-rest +var inventory: PlayFabInventory = PlayFabInventory.new() + # Retrieves the `title_id` from `ProjectSettings` func _init(): @@ -39,7 +49,10 @@ func _ready(): set_process_mode(manager_process_mode) add_child(client) add_child(event) + add_child(catalog) + add_child(inventory) client_config = _client_config_loader.load(title_id) + playfab_initialized.emit() # Saves the client config to a file diff --git a/addons/godot-playfab/Scenes/PlayFabMainScreen.gd b/addons/godot-playfab/Scenes/PlayFabMainScreen.gd index 223a4ed1..7e227cbe 100644 --- a/addons/godot-playfab/Scenes/PlayFabMainScreen.gd +++ b/addons/godot-playfab/Scenes/PlayFabMainScreen.gd @@ -1,87 +1,95 @@ @tool extends Control +class DocProperty: + var name: String + var type: String + var comment: String + var editor_resource_filesystem_cached func _ready(): # Needed, so can ater refresh the "FileSystem" panel of the Editor editor_resource_filesystem_cached = EditorPlugin.new().get_editor_interface().get_resource_filesystem() -func _on_SaveModel_pressed(): - +func _on_SaveModel_pressed() -> void: + if !guard_class_name_set(): return - - var file_dialog = $FileDialog + + var file_dialog: FileDialog = $FileDialog file_dialog.current_file = $VBoxContainer/ClassNameContainer/LineEdit.text + ".gd" file_dialog.show() file_dialog.connect("file_selected",Callable(self,"_on_file_selected").bind(),CONNECT_ONE_SHOT) -func _on_save_direct_pressed(): - +func _on_save_direct_pressed() -> void: + if !guard_class_name_set(): return - - var file_name = $VBoxContainer/ClassNameContainer/LineEdit.text + ".gd" - var file_path = "res://addons/godot-playfab/Models/" + file_name + + var file_name: String = $VBoxContainer/ClassNameContainer/LineEdit.text + ".gd" + var file_path: String = "res://addons/godot-playfab/Models/" + file_name _on_file_selected(file_path) func _on_file_selected(file_path: String): - - var model = to_model($VBoxContainer/ClassNameContainer/LineEdit.text, $VBoxContainer/Input.text) - var file = FileAccess.open(file_path, FileAccess.WRITE) + + var model: String = to_model($VBoxContainer/ClassNameContainer/LineEdit.text, $VBoxContainer/Input.text) + var file: FileAccess = FileAccess.open(file_path, FileAccess.WRITE) file.store_string(model) # Refresh the "FileSystem" panel editor_resource_filesystem_cached.scan() - + print("Saved model to file path: \"%s\"" % file_path) - + func guard_class_name_set() -> bool: if $VBoxContainer/ClassNameContainer/LineEdit.text.is_empty(): $ErrorPopupDialog/Label.text = "Please first enter a Class Name!" $ErrorPopupDialog.popup_centered(Vector2(0,0)) return false - + return true -static func to_model(object_name: String, input: String) -> String: - var lines = input.split("\n", true) +func to_model(object_name: String, input: String) -> String: + var lines: PackedStringArray = input.split("\n", true) + lines = remove_empty_lines(lines) lines.push_back("") # Hack: add an empty line at the bottom so below logic works & is simpler :-) Otherwise, the last prop would not be written - - var props = [] - var current_prop = "" - var prop_line = 0 + + var props: Array[DocProperty] = [] + var prop_line: int = 0 + var current_prop: DocProperty = DocProperty.new() for line in lines: - - var str_line = (line as String).strip_edges() - - if not str_line.is_empty(): - match prop_line: - 0: # Variable name - current_prop = "var " + str_line - 1: # Type - str_line = fix_type(str_line) - if not str_line.is_empty() and not str_line.begins_with("#"): - current_prop += ": %s" % str_line - else: - current_prop = str_line - 2: # Comment - current_prop = "# %s\n%s" % [str_line, current_prop] - - prop_line += 1 - else: + var str_line: String = (line as String).strip_edges() + + match prop_line: + 0: # Variable name + current_prop.name = str_line + 1: # Type + str_line = fix_type(str_line) + if not str_line.is_empty() and not str_line.begins_with("#"): + current_prop.type = str_line + else: + push_warning("No type specified for property %s, defaulting to Variant" % current_prop.name) + current_prop.type = "Variant" + 2: # Comment + current_prop.comment = str_line + + prop_line += 1 + if prop_line > 2: props.append(current_prop) + current_prop = DocProperty.new() prop_line = 0 - - var model = "extends JsonSerializable\nclass_name " + object_name + "\n\n" - for prop in props: - model += prop + "\n\n" - + + var model: String = "extends JsonSerializable\nclass_name " + object_name + "\n\n" + for prop: DocProperty in props: + model += "## %s \n" % [prop.comment] + model += "var %s: %s \n" % [prop.name, prop.type] + model += "\n\n" + # TODO: Find a way to generate the mapping for props automatically! model += """ func _get_type_for_property(property_name: String) -> String: @@ -90,21 +98,35 @@ func _get_type_for_property(property_name: String) -> String: # return "" _: pass - + push_error("Could not find mapping for property: " + property_name) return super._get_type_for_property(property_name) - + """ return model -static func fix_type(type: String) -> String: - +func fix_type(type: String) -> String: + match type: "string": return "String" "boolean": return "bool" + "number": + return "float" + "object": + return "Dictionary[String, Variant]" _: - if type.ends_with("]"): - return "Array" + if type.ends_with("[]"): + # Example: CatalogItem[] --> Array[CatalogItem] + var inner_type: String = type.substr(0, type.length() - 2) + return "Array[%s]" % inner_type return type + + +func remove_empty_lines(lines: PackedStringArray) -> PackedStringArray: + var result: PackedStringArray = [] + for line in lines: + if not line.strip_edges().is_empty(): + result.append(line) + return result diff --git a/addons/godot-playfab/theme/godot-playfab_theme.tres b/addons/godot-playfab/theme/godot-playfab_theme.tres new file mode 100644 index 00000000..0539f6f6 --- /dev/null +++ b/addons/godot-playfab/theme/godot-playfab_theme.tres @@ -0,0 +1,94 @@ +[gd_resource type="Theme" load_steps=6 format=3 uid="uid://bcyfaw335q2be"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_k71x6"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0.109804, 0.109804, 0.109804, 0.6) +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(0.94902, 0.313726, 0.133333, 1) +corner_radius_top_left = 3 +corner_radius_top_right = 3 +corner_radius_bottom_right = 3 +corner_radius_bottom_left = 3 +expand_margin_left = 2.0 +expand_margin_top = 2.0 +expand_margin_right = 2.0 +expand_margin_bottom = 2.0 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_q26xb"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0.94902, 0.313726, 0.133333, 1) +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(1, 1, 1, 1) +corner_radius_top_left = 3 +corner_radius_top_right = 3 +corner_radius_bottom_right = 3 +corner_radius_bottom_left = 3 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_hjf3w"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0.94902, 0.313726, 0.133333, 0.7) +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(1, 1, 1, 1) +corner_radius_top_left = 3 +corner_radius_top_right = 3 +corner_radius_bottom_right = 3 +corner_radius_bottom_left = 3 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_yksrt"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0.109804, 0.109804, 0.109804, 1) +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(0.45098, 0.45098, 0.45098, 1) +corner_radius_top_left = 3 +corner_radius_top_right = 3 +corner_radius_bottom_right = 3 +corner_radius_bottom_left = 3 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_wveey"] +bg_color = Color(0.109804, 0.109804, 0.109804, 1) +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(0.45098, 0.45098, 0.45098, 1) +corner_radius_top_left = 5 +corner_radius_top_right = 5 +corner_radius_bottom_right = 5 +corner_radius_bottom_left = 5 + +[resource] +Button/colors/font_color = Color(0.878431, 0.878431, 0.878431, 1) +Button/colors/font_disabled_color = Color(0.34902, 0.34902, 0.34902, 1) +Button/colors/font_focus_color = Color(1, 1, 1, 1) +Button/colors/font_hover_color = Color(1, 1, 1, 1) +Button/colors/font_pressed_color = Color(1, 1, 1, 1) +Button/styles/disabled = SubResource("StyleBoxFlat_k71x6") +Button/styles/focus = SubResource("StyleBoxFlat_q26xb") +Button/styles/hover = SubResource("StyleBoxFlat_hjf3w") +Button/styles/normal = SubResource("StyleBoxFlat_yksrt") +Button/styles/pressed = SubResource("StyleBoxFlat_q26xb") +Panel/styles/panel = SubResource("StyleBoxFlat_wveey") diff --git a/addons/toastparty/LICENSE b/addons/toastparty/LICENSE new file mode 100644 index 00000000..f66db9ed --- /dev/null +++ b/addons/toastparty/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Francisco Pereira Alvarado (gammafp) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/addons/toastparty/README.md b/addons/toastparty/README.md new file mode 100644 index 00000000..bd571726 --- /dev/null +++ b/addons/toastparty/README.md @@ -0,0 +1,43 @@ +# Toast Party + +Have fun with this marvelous plugin for generating small toast notifications. + +Toast Party is a versatile plugin for Godot that allows you to easily create toast-style notifications in your games and applications. Add an extra layer of interactivity and visual feedback to your Godot projects with ease. Bring your messages to life with Toast Party! + +Plugin created by Francisco Pereira Alvarado ([gammafp](https://twitter.com/gammafp)). + +Please follow me on my social networks to follow my jobs: [LINKTREE](https://linktr.ee/gammafp) + +![Main Screen](/no-copy-imgs/example.gif) + +## Installation: + +1. Clone this repository into addons folder. +2. Enabled ToastParty, go to: Project > Project Settings > Plugins + +![Drag Racing](/no-copy-imgs/toast-party-install.png) + +## Use: + +ToastParty is an autoload singleton and is used as follows: + +```python +> ToastParty.show({ + "text": "🥑Some Text🥑", # Text (emojis can be used) + "bgcolor": Color(0, 0, 0, 0.7), # Background Color + "color": Color(1, 1, 1, 1), # Text Color + "gravity": "top", # top or bottom + "direction": "right", # left or center or right + "text_size": 18, # [optional] Text (font) size // experimental (warning!) + "use_font": true # [optional] Use custom ToastParty font // experimental (warning!) +}) +``` + +## Thanks to +@davcri + +## License + +Copyright (c) 2024 Francisco Pereira Alvarado (gammafp) + +Unless otherwise specified, files in this repository are licensed under the MIT license. See [LICENSE](LICENSE) for more information. diff --git a/addons/toastparty/fonts/Light.ttf b/addons/toastparty/fonts/Light.ttf new file mode 100644 index 00000000..b3f384d1 Binary files /dev/null and b/addons/toastparty/fonts/Light.ttf differ diff --git a/addons/toastparty/fonts/Light.ttf.import b/addons/toastparty/fonts/Light.ttf.import new file mode 100644 index 00000000..9737c773 --- /dev/null +++ b/addons/toastparty/fonts/Light.ttf.import @@ -0,0 +1,36 @@ +[remap] + +importer="font_data_dynamic" +type="FontFile" +uid="uid://ccprmt0qqd56p" +path="res://.godot/imported/Light.ttf-0f836f71a282c0ba99b5d80af15b4036.fontdata" + +[deps] + +source_file="res://addons/toastparty/fonts/Light.ttf" +dest_files=["res://.godot/imported/Light.ttf-0f836f71a282c0ba99b5d80af15b4036.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 +modulate_color_glyphs=false +hinting=1 +subpixel_positioning=4 +keep_rounding_remainders=true +oversampling=0.0 +Fallbacks=null +fallbacks=[] +Compress=null +compress=true +preload=[] +language_support={} +script_support={} +opentype_features={} diff --git a/addons/toastparty/plugin.cfg b/addons/toastparty/plugin.cfg new file mode 100644 index 00000000..5ced36c2 --- /dev/null +++ b/addons/toastparty/plugin.cfg @@ -0,0 +1,7 @@ +[plugin] + +name="ToastParty" +description="" +author="Francisco Pereira - Gammafp" +version="1.1.0" +script="toastparty.gd" diff --git a/addons/toastparty/toast-autoload.gd b/addons/toastparty/toast-autoload.gd new file mode 100644 index 00000000..8ecde6c2 --- /dev/null +++ b/addons/toastparty/toast-autoload.gd @@ -0,0 +1,130 @@ +extends Node + +const label_resource = preload("toast_label/toast_label.tscn") + +var label_top_left = [] +var label_top_right = [] +var label_bottom_left = [] +var label_bottom_right = [] + +var label_top_center = [] +var label_bottom_center = [] + +# parent node +var canvas_layer: CanvasLayer + +# Called when the node enters the scene tree for the first time. +func _ready(): + canvas_layer = CanvasLayer.new() + canvas_layer.set_name("ToastPartyLayer") + canvas_layer.layer = 128 + add_child(canvas_layer) + + # TODO: We need Debounce function + # Connect signal resize to _on_resize + # get_tree().get_root().connect("size_changed", _on_resize, 1) + +func _add_new_label(config): + # Create a new label + var label = label_resource.instantiate() + canvas_layer.add_child(label) + label.connect("remove_label", remove_label_from_array) + + if config.direction == "left": + if config.gravity == "top": + label_top_left.insert(0, label) + else: + label_bottom_left.insert(0, label) + elif config.direction == "center": + if config.gravity == "top": + label_top_center.insert(0, label) + else: + label_bottom_center.insert(0, label) + else: + if config.gravity == "top": + label_top_right.insert(0, label) + else: + label_bottom_right.insert(0, label) + + # Configuration of the label + label.init(config) + + # Move all labels to new positions when a new label is added + move_positions(config.direction, config.gravity) + +func move_positions(direction, gravity): + if direction == "left" and gravity == "bottom": + for index in label_bottom_left.size(): + var _label = label_bottom_left[index] + _label.move_to(index) + + elif direction == "left" and gravity == "top": + for index in label_top_left.size(): + var _label = label_top_left[index] + _label.move_to(index) + + elif direction == "right" and gravity == "bottom": + for index in label_bottom_right.size(): + var _label = label_bottom_right[index] + _label.move_to(index) + + elif direction == "right" and gravity == "top": + for index in label_top_right.size(): + var _label = label_top_right[index] + _label.move_to(index) + + elif direction == "center" and gravity == "bottom": + for index in label_bottom_center.size(): + var _label = label_bottom_center[index] + _label.move_to(index) + + elif direction == "center" and gravity == "top": + for index in label_top_center.size(): + var _label = label_top_center[index] + _label.move_to(index) + + +func remove_label_from_array(label): + if label.direction == "left": + if label.gravity == "top": + label_top_left.erase(label) + else: + label_bottom_left.erase(label) + elif label.direction == "center": + if label.gravity == "top": + label_top_center.erase(label) + else: + label_bottom_center.erase(label) + else: + if label.gravity == "top": + label_top_right.erase(label) + else: + label_bottom_right.erase(label) + +## Event resize +func _on_resize(): + var toast_labels = label_top_left + label_top_right + label_bottom_left + label_bottom_right + label_top_center + label_bottom_center + for _label in toast_labels: + _label.update_x_position() + +func clean_config(config): + if not config.has("text"): + config.text = "🥑 toast party! 🥑" + + if not config.has("direction"): + config.direction = "right" + + if not config.has("gravity"): + config.gravity = "top" + + if not config.has("bgcolor"): + config.bgcolor = Color(0, 0, 0, 0.7) + + if not config.has("color"): + config.color = Color(1, 1, 1, 1) + + return config + +func show(config = {}): + var _config_cleaned = clean_config(config) + _add_new_label(_config_cleaned) diff --git a/addons/toastparty/toast-autoload.gd.uid b/addons/toastparty/toast-autoload.gd.uid new file mode 100644 index 00000000..229a8104 --- /dev/null +++ b/addons/toastparty/toast-autoload.gd.uid @@ -0,0 +1 @@ +uid://bpmj02udcqdo3 diff --git a/addons/toastparty/toast_label/ToastLabel.gd b/addons/toastparty/toast_label/ToastLabel.gd new file mode 100644 index 00000000..b1678f8d --- /dev/null +++ b/addons/toastparty/toast_label/ToastLabel.gd @@ -0,0 +1,198 @@ +extends Label + +signal remove_label(ToastLabel) + +var resolution = Vector2.ZERO + +# Text margin with box parent +const margins = {"left": 12, "top": 7, "right": 12, "bottom": 7} +# margin between buttons +const margin_between = 23 + +# offset position box with screen position +const offset_position = Vector2(10, 10) + +var button_size +var _tween_in: Tween + +# local variables +var gravity = "bottom" # top, bottom +var direction = "center" # left, right, center +var timer_to_destroy = 5 # seconds by default + + +func _ready(): + _set_resolution() + button_size = self.size + + # start position + _tween_destroy_label_timer() + +func _clean_config(config: Dictionary) -> Dictionary: + var _config = config + if not _config.has("text"): + _config["text"] = "Toast Label" + if not _config.has("text_size"): + _config["text_size"] = 18 + if not _config.has("bgcolor"): + _config["bgcolor"] = Color(0, 0, 0, .7) + if not _config.has("direction"): + _config["direction"] = "center" + if not _config.has("gravity"): + _config["gravity"] = "bottom" + if not _config.has("color"): + _config["color"] = Color(1, 1, 1, 1) + if not _config.has("use_font"): + _config["use_font"] = true + return _config + + +func init(config: Dictionary) -> void: + # TODO: add config validation + var config_cleaned = _clean_config(config) + + update_text(config_cleaned.text) + _set_bg_color(config_cleaned.bgcolor) + _set_color(config_cleaned.color) + _set_font(config_cleaned.use_font) + _set_text_size(config_cleaned.text_size) + + direction = config_cleaned.direction + gravity = config_cleaned.gravity + + position.y = get_y_pos(-100, gravity) + + _set_margins() + _set_shadow_direction() + + +func update_text(_text: String) -> void: + self.text = _text + button_size = self.size + update_x_position() + + +func move_to(index: int) -> void: + update_x_position() + + var offset_y = (margin_between + button_size.y) * index + var _y = get_y_pos(offset_y, gravity) + + # bottom + if index == 0: + _tween_in = get_tree().create_tween() + _tween_in.set_pause_mode(Tween.TWEEN_PAUSE_PROCESS) # pause mode + _tween_in.stop() + var delayed = 0.03 + ( + _tween_in + . tween_property(self, "position", Vector2(position.x, _y), .3) + . set_trans(Tween.TRANS_QUINT) + . set_ease(Tween.EASE_IN) + . set_delay(delayed) + ) + _tween_in.play() + else: + _tween_in = get_tree().create_tween() + _tween_in.set_pause_mode(Tween.TWEEN_PAUSE_PROCESS) # pause mode + _tween_in.stop() + ( + _tween_in + . tween_property(self, "position", Vector2(position.x, _y), .3) + . set_trans(Tween.TRANS_ELASTIC) + . set_ease(Tween.EASE_IN_OUT) + ) + _tween_in.play() + + +func _tween_destroy_label_complete() -> void: + # Send event complete + emit_signal("remove_label", self) + queue_free() + + +func _tween_destroy_label_timer(): + # tween alpha to 0 + var tween_alpha = get_tree().create_tween() + tween_alpha.set_pause_mode(Tween.TWEEN_PAUSE_PROCESS) # pause mode + tween_alpha.tween_property(self, "modulate:a", 0, 0.8).set_delay(timer_to_destroy) + tween_alpha.tween_callback(_tween_destroy_label_complete) + + +func get_y_pos(offset = 0, _gravity = "top") -> float: + # left position.x = margins.left + offset_position.x + var _y_pos = 0 + if _gravity == "top": + _y_pos = margins.top + offset_position.y + offset + else: + _y_pos = resolution.y - margins.top - button_size.y - offset_position.y - offset + return _y_pos + + +func update_x_position() -> void: + _set_resolution() + + if direction == "left": + position.x = margins.left + offset_position.x + elif direction == "center": + position.x = (resolution.x / 2) - (size.x / 2) + else: + position.x = resolution.x - margins.left - size.x - offset_position.x + +func _set_color(color: Color) -> void: + # set color + var theme_override = self.get("label_settings") + theme_override.set("font_color", color) + + +func _set_font(use_font: bool) -> void: + # set font + if use_font == false: + var theme_override = self.get("label_settings") + theme_override.set("font", null) + + +func _set_margins() -> void: + # set margins + var theme_override = self.get("theme_override_styles/normal") + theme_override.set("expand_margin_left", margins.left) + theme_override.set("expand_margin_top", margins.top) + theme_override.set("expand_margin_right", margins.right) + theme_override.set("expand_margin_bottom", margins.bottom) + + +func _set_shadow_direction() -> void: + # set shadow direction + var shadow_offset_abs = 2 + var theme_override = self.get("theme_override_styles/normal") + if gravity == "top" and direction == "left": + theme_override.set("shadow_offset", Vector2(-shadow_offset_abs, shadow_offset_abs)) + elif gravity == "top" and direction == "right": + theme_override.set("shadow_offset", Vector2(shadow_offset_abs, shadow_offset_abs)) + + elif gravity == "bottom" and direction == "left": + theme_override.set("shadow_offset", Vector2(-shadow_offset_abs, shadow_offset_abs)) + elif gravity == "bottom" and direction == "right": + theme_override.set("shadow_offset", Vector2(shadow_offset_abs, shadow_offset_abs)) + + elif gravity == "top" and direction == "center": + theme_override.set("shadow_offset", Vector2(0, shadow_offset_abs)) + elif gravity == "bottom" and direction == "center": + theme_override.set("shadow_offset", Vector2(0, shadow_offset_abs)) + + +func _set_text_size(text_size: int) -> void: + # set text size + var theme_override = self.get("label_settings") + theme_override.set("font_size", text_size) + + +func _set_bg_color(color: Color) -> void: + # set bg color + var theme_override = self.get("theme_override_styles/normal") + theme_override.set("bg_color", color) + + +func _set_resolution(): + resolution.x = get_viewport().get_visible_rect().size.x + resolution.y = get_viewport().get_visible_rect().size.y diff --git a/addons/toastparty/toast_label/ToastLabel.gd.uid b/addons/toastparty/toast_label/ToastLabel.gd.uid new file mode 100644 index 00000000..b02625ee --- /dev/null +++ b/addons/toastparty/toast_label/ToastLabel.gd.uid @@ -0,0 +1 @@ +uid://dqv8kbhts0beq diff --git a/addons/toastparty/toast_label/toast_label.tscn b/addons/toastparty/toast_label/toast_label.tscn new file mode 100644 index 00000000..36b1be1d --- /dev/null +++ b/addons/toastparty/toast_label/toast_label.tscn @@ -0,0 +1,33 @@ +[gd_scene load_steps=5 format=3 uid="uid://dugii0837nslt"] + +[ext_resource type="StyleBox" uid="uid://blt5gbhfsd0p5" path="res://addons/toastparty/toast_label/toast_label_style.tres" id="1_0l35p"] +[ext_resource type="FontFile" uid="uid://ccprmt0qqd56p" path="res://addons/toastparty/fonts/Light.ttf" id="2_xpkfi"] +[ext_resource type="Script" uid="uid://dqv8kbhts0beq" path="res://addons/toastparty/toast_label/ToastLabel.gd" id="3_vwlmp"] + +[sub_resource type="LabelSettings" id="LabelSettings_o6str"] +resource_local_to_scene = true +line_spacing = 0.0 +font = ExtResource("2_xpkfi") +font_size = 18 +font_color = Color(0.94902, 0.94902, 0.94902, 1) +shadow_size = 0 + +[node name="Label" type="Label"] +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -55.0 +offset_top = -13.0 +offset_right = 56.0 +offset_bottom = 13.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_font_sizes/font_size = 18 +theme_override_styles/normal = ExtResource("1_0l35p") +text = "Toast Party" +label_settings = SubResource("LabelSettings_o6str") +horizontal_alignment = 1 +vertical_alignment = 1 +script = ExtResource("3_vwlmp") diff --git a/addons/toastparty/toast_label/toast_label_style.tres b/addons/toastparty/toast_label/toast_label_style.tres new file mode 100644 index 00000000..708fc043 --- /dev/null +++ b/addons/toastparty/toast_label/toast_label_style.tres @@ -0,0 +1,19 @@ +[gd_resource type="StyleBoxFlat" format=3 uid="uid://blt5gbhfsd0p5"] + +[resource] +resource_local_to_scene = true +bg_color = Color(0, 0, 0, 0.835294) +border_color = Color(0, 0, 0, 1) +corner_radius_top_left = 2 +corner_radius_top_right = 2 +corner_radius_bottom_right = 2 +corner_radius_bottom_left = 2 +corner_detail = 10 +expand_margin_left = 15.0 +expand_margin_top = 10.0 +expand_margin_right = 15.0 +expand_margin_bottom = 10.0 +shadow_color = Color(0, 0, 0, 0.607843) +shadow_size = 2 +shadow_offset = Vector2(-2, 2) +anti_aliasing = false diff --git a/addons/toastparty/toastparty.gd b/addons/toastparty/toastparty.gd new file mode 100644 index 00000000..85806143 --- /dev/null +++ b/addons/toastparty/toastparty.gd @@ -0,0 +1,9 @@ +@tool +extends EditorPlugin + +const AUTOLOAD_NAME = "ToastParty" +func _enter_tree(): + add_autoload_singleton(AUTOLOAD_NAME, "res://addons/toastparty/toast-autoload.gd") + +func _exit_tree(): + remove_autoload_singleton(AUTOLOAD_NAME) diff --git a/addons/toastparty/toastparty.gd.uid b/addons/toastparty/toastparty.gd.uid new file mode 100644 index 00000000..9a6fc419 --- /dev/null +++ b/addons/toastparty/toastparty.gd.uid @@ -0,0 +1 @@ +uid://cy2q5qgbyjvjq diff --git a/config/Steam/ItemDem.json b/config/Steam/ItemDem.json new file mode 100644 index 00000000..9699e8d1 --- /dev/null +++ b/config/Steam/ItemDem.json @@ -0,0 +1,50 @@ +{ + "appid": 2908850, + "items": [ + { + "itemdefid": 1, + "type": "item", + "name": "Godots 25", + "description": "25 Godots pack.", + "price": "1;VLV25", + "name_color": "7D6D00", + "background_color": "3C352E", + "icon_url": "https://content1.prod.catalog.playfab.com/pf-title-1a64f85c3341b8ed-b5f9b/9a487eef-e468-4588-906e-d58c78dc8d05/shortcut_icon.png", + "icon_url_large": "https://content1.prod.catalog.playfab.com/pf-title-1a64f85c3341b8ed-b5f9b/9a487eef-e468-4588-906e-d58c78dc8d05/shortcut_icon.png", + "store_tags": "Godots", + "tradable": false, + "marketable": false, + "auto_stack": true + }, + { + "itemdefid": 2, + "type": "item", + "name": "Godots 50", + "description": "50 Godots value pack.", + "price": "1;VLV50", + "name_color": "7D6D00", + "background_color": "3C352E", + "icon_url": "https://content1.prod.catalog.playfab.com/pf-title-1a64f85c3341b8ed-b5f9b/9a487eef-e468-4588-906e-d58c78dc8d05/shortcut_icon.png", + "icon_url_large": "https://content1.prod.catalog.playfab.com/pf-title-1a64f85c3341b8ed-b5f9b/9a487eef-e468-4588-906e-d58c78dc8d05/shortcut_icon.png", + "store_tags": "Godots", + "tradable": false, + "marketable": false, + "auto_stack": true + }, + { + "itemdefid": 3, + "type": "item", + "name": "Godots 75", + "description": "75 Godots super saver pack.", + "price": "1;VLV75", + "name_color": "7D6D00", + "background_color": "3C352E", + "icon_url": "https://content1.prod.catalog.playfab.com/pf-title-1a64f85c3341b8ed-b5f9b/9a487eef-e468-4588-906e-d58c78dc8d05/shortcut_icon.png", + "icon_url_large": "https://content1.prod.catalog.playfab.com/pf-title-1a64f85c3341b8ed-b5f9b/9a487eef-e468-4588-906e-d58c78dc8d05/shortcut_icon.png", + "store_tags": "Godots", + "tradable": false, + "marketable": false, + "auto_stack": true + } + ] +} diff --git a/project.godot b/project.godot index 332cb88a..fa9451eb 100644 --- a/project.godot +++ b/project.godot @@ -20,10 +20,11 @@ config/icon="res://addons/godot-playfab/icon.png" SceneManager="*res://Scripts/SceneManager.gd" PlayFabManager="*res://addons/godot-playfab/PlayFabManager.gd" +ToastParty="*res://addons/toastparty/toast-autoload.gd" [editor_plugins] -enabled=PackedStringArray("res://addons/godot-playfab/plugin.cfg", "res://addons/gdUnit4/plugin.cfg") +enabled=PackedStringArray("res://addons/gdUnit4/plugin.cfg", "res://addons/godot-playfab/plugin.cfg", "res://addons/toastparty/plugin.cfg") [filesystem] @@ -40,7 +41,7 @@ reload=false [gui] -theme/custom="res://Assets/playfab_theme.tres" +theme/custom="uid://bcyfaw335q2be" [physics] diff --git a/raw_assets/Shirt.pdn b/raw_assets/Shirt.pdn new file mode 100644 index 00000000..1356ad9a Binary files /dev/null and b/raw_assets/Shirt.pdn differ diff --git a/raw_assets/godotfest_logo_blue.png b/raw_assets/godotfest_logo_blue.png new file mode 100644 index 00000000..db8455cc Binary files /dev/null and b/raw_assets/godotfest_logo_blue.png differ diff --git a/raw_assets/godotfest_logo_blue.png.import b/raw_assets/godotfest_logo_blue.png.import new file mode 100644 index 00000000..c541ab05 --- /dev/null +++ b/raw_assets/godotfest_logo_blue.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c0odrgixxat3n" +path="res://.godot/imported/godotfest_logo_blue.png-3068ead6b8a91cc36402fd4194949810.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://raw_assets/godotfest_logo_blue.png" +dest_files=["res://.godot/imported/godotfest_logo_blue.png-3068ead6b8a91cc36402fd4194949810.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +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/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +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/soft_retro/Righteous/OFL.txt b/soft_retro/Righteous/OFL.txt new file mode 100644 index 00000000..4f26bf95 --- /dev/null +++ b/soft_retro/Righteous/OFL.txt @@ -0,0 +1,94 @@ +Copyright (c) 2011 by Brian J. Bonislawsky DBA Astigmatic (AOETI) +(astigma@astigmatic.com), with Reserved Font Names "Righteous" + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/soft_retro/Righteous/Righteous-Regular.ttf b/soft_retro/Righteous/Righteous-Regular.ttf new file mode 100644 index 00000000..fc9c0a84 Binary files /dev/null and b/soft_retro/Righteous/Righteous-Regular.ttf differ diff --git a/soft_retro/Righteous/Righteous-Regular.ttf.import b/soft_retro/Righteous/Righteous-Regular.ttf.import new file mode 100644 index 00000000..c3ed3266 --- /dev/null +++ b/soft_retro/Righteous/Righteous-Regular.ttf.import @@ -0,0 +1,36 @@ +[remap] + +importer="font_data_dynamic" +type="FontFile" +uid="uid://i1eetfry6s4h" +path="res://.godot/imported/Righteous-Regular.ttf-d2f7bd177f19b40bc2c26b0c0a91d998.fontdata" + +[deps] + +source_file="res://soft_retro/Righteous/Righteous-Regular.ttf" +dest_files=["res://.godot/imported/Righteous-Regular.ttf-d2f7bd177f19b40bc2c26b0c0a91d998.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 +modulate_color_glyphs=false +hinting=1 +subpixel_positioning=4 +keep_rounding_remainders=true +oversampling=0.0 +Fallbacks=null +fallbacks=[] +Compress=null +compress=true +preload=[] +language_support={} +script_support={} +opentype_features={} diff --git a/soft_retro/art/checked.svg b/soft_retro/art/checked.svg new file mode 100644 index 00000000..ec251121 --- /dev/null +++ b/soft_retro/art/checked.svg @@ -0,0 +1,48 @@ + + + + + + + diff --git a/soft_retro/art/checked.svg.import b/soft_retro/art/checked.svg.import new file mode 100644 index 00000000..d462924f --- /dev/null +++ b/soft_retro/art/checked.svg.import @@ -0,0 +1,43 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://udawlj7nryla" +path="res://.godot/imported/checked.svg-919178c345fa3bb12eedbdefa9339aae.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://soft_retro/art/checked.svg" +dest_files=["res://.godot/imported/checked.svg-919178c345fa3bb12eedbdefa9339aae.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +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/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +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/soft_retro/art/slider_grabber.svg b/soft_retro/art/slider_grabber.svg new file mode 100644 index 00000000..d35192b7 --- /dev/null +++ b/soft_retro/art/slider_grabber.svg @@ -0,0 +1,45 @@ + + + + + + diff --git a/soft_retro/art/slider_grabber.svg.import b/soft_retro/art/slider_grabber.svg.import new file mode 100644 index 00000000..dc23d6e7 --- /dev/null +++ b/soft_retro/art/slider_grabber.svg.import @@ -0,0 +1,43 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bbl50qp6b1eq8" +path="res://.godot/imported/slider_grabber.svg-7efe12275a78af246fbf5c7b8b608956.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://soft_retro/art/slider_grabber.svg" +dest_files=["res://.godot/imported/slider_grabber.svg-7efe12275a78af246fbf5c7b8b608956.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +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/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +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/soft_retro/art/toggle_off.svg b/soft_retro/art/toggle_off.svg new file mode 100644 index 00000000..03df99cf --- /dev/null +++ b/soft_retro/art/toggle_off.svg @@ -0,0 +1,53 @@ + + + + + + + + + diff --git a/soft_retro/art/toggle_off.svg.import b/soft_retro/art/toggle_off.svg.import new file mode 100644 index 00000000..c7d48be5 --- /dev/null +++ b/soft_retro/art/toggle_off.svg.import @@ -0,0 +1,43 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://gameaex7fm1g" +path="res://.godot/imported/toggle_off.svg-ac8c30b12a7d3fcece7eb5f25e48107d.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://soft_retro/art/toggle_off.svg" +dest_files=["res://.godot/imported/toggle_off.svg-ac8c30b12a7d3fcece7eb5f25e48107d.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +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/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +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/soft_retro/art/toggle_on.svg b/soft_retro/art/toggle_on.svg new file mode 100644 index 00000000..664454b8 --- /dev/null +++ b/soft_retro/art/toggle_on.svg @@ -0,0 +1,50 @@ + + + + + + + diff --git a/soft_retro/art/toggle_on.svg.import b/soft_retro/art/toggle_on.svg.import new file mode 100644 index 00000000..260b98ab --- /dev/null +++ b/soft_retro/art/toggle_on.svg.import @@ -0,0 +1,43 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dl5qlemud0d4j" +path="res://.godot/imported/toggle_on.svg-6577369a3687d758cfc6e33c9af512b8.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://soft_retro/art/toggle_on.svg" +dest_files=["res://.godot/imported/toggle_on.svg-6577369a3687d758cfc6e33c9af512b8.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +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/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +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/soft_retro/art/unchecked.svg b/soft_retro/art/unchecked.svg new file mode 100644 index 00000000..71d79b8f --- /dev/null +++ b/soft_retro/art/unchecked.svg @@ -0,0 +1,53 @@ + + + + + + + diff --git a/soft_retro/art/unchecked.svg.import b/soft_retro/art/unchecked.svg.import new file mode 100644 index 00000000..a97ef433 --- /dev/null +++ b/soft_retro/art/unchecked.svg.import @@ -0,0 +1,43 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cbwip815aqfhq" +path="res://.godot/imported/unchecked.svg-a69bdf3a510d8bf5cdf63fcefbc7512b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://soft_retro/art/unchecked.svg" +dest_files=["res://.godot/imported/unchecked.svg-a69bdf3a510d8bf5cdf63fcefbc7512b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +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/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +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/soft_retro/soft_retro.tres b/soft_retro/soft_retro.tres new file mode 100644 index 00000000..22d1cf5e --- /dev/null +++ b/soft_retro/soft_retro.tres @@ -0,0 +1,341 @@ +[gd_resource type="Theme" load_steps=52 format=3 uid="uid://b3klx57slj0l3"] + +[ext_resource type="FontFile" uid="uid://i1eetfry6s4h" path="res://soft_retro/Righteous/Righteous-Regular.ttf" id="1_lopid"] +[ext_resource type="Texture2D" uid="uid://udawlj7nryla" path="res://soft_retro/art/checked.svg" id="2_k0c1y"] +[ext_resource type="Texture2D" uid="uid://dl5qlemud0d4j" path="res://soft_retro/art/toggle_on.svg" id="2_myavk"] +[ext_resource type="Texture2D" uid="uid://gameaex7fm1g" path="res://soft_retro/art/toggle_off.svg" id="3_208pi"] +[ext_resource type="Texture2D" uid="uid://cbwip815aqfhq" path="res://soft_retro/art/unchecked.svg" id="3_cwkfx"] +[ext_resource type="Texture2D" uid="uid://bbl50qp6b1eq8" path="res://soft_retro/art/slider_grabber.svg" id="6_cwkfx"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_k0c1y"] +bg_color = Color(1, 0.690196, 0.639216, 1) +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(1, 0.933333, 0.8, 1) +corner_radius_top_left = 10 +corner_radius_top_right = 10 + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_lopid"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_myavk"] +bg_color = Color(1, 0.933333, 0.8, 1) +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(1, 0.933333, 0.8, 1) +corner_radius_top_left = 10 +corner_radius_top_right = 10 +expand_margin_left = 5.0 +expand_margin_top = 2.0 +expand_margin_right = 5.0 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_lopid"] +bg_color = Color(0.27451, 0.258824, 0.368627, 1) +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(1, 0.933333, 0.8, 1) +corner_radius_top_left = 10 +corner_radius_top_right = 10 +expand_margin_left = 5.0 +expand_margin_top = 2.0 +expand_margin_right = 5.0 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_208pi"] +bg_color = Color(1, 0.411765, 0.45098, 1) +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(1, 0.933333, 0.8, 1) +corner_radius_top_left = 10 +corner_radius_top_right = 10 +expand_margin_left = 5.0 +expand_margin_right = 5.0 + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_fmcpx"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_nas17"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_adq26"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_wjpxi"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_k0c1y"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_cwkfx"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_1b0k0"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_14r85"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_hbuoo"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_nas17"] +bg_color = Color(1, 0.933333, 0.8, 1) +corner_radius_top_left = 5 +corner_radius_top_right = 5 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_adq26"] +bg_color = Color(1, 0.690196, 0.639216, 1) +corner_radius_top_left = 5 +corner_radius_top_right = 5 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_wjpxi"] +bg_color = Color(1, 0.411765, 0.45098, 1) +corner_radius_top_left = 5 +corner_radius_top_right = 5 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_s6qsw"] +bg_color = Color(0.0823529, 0.470588, 0.54902, 1) +corner_radius_top_left = 5 +corner_radius_top_right = 5 + +[sub_resource type="StyleBoxLine" id="StyleBoxLine_k0c1y"] +color = Color(1, 0.933333, 0.8, 1) +thickness = 2 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_cwkfx"] +bg_color = Color(1, 0.411765, 0.45098, 1) +corner_radius_top_left = 5 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_1b0k0"] +bg_color = Color(1, 0.690196, 0.639216, 1) +corner_radius_top_left = 5 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_14r85"] +content_margin_top = 5.0 +content_margin_bottom = 5.0 +bg_color = Color(0.0823529, 0.470588, 0.54902, 1) +corner_radius_top_left = 5 +corner_radius_top_right = 5 + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_b3cdw"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_rga2m"] +content_margin_left = 10.0 +content_margin_right = 10.0 +bg_color = Color(0.0823529, 0.470588, 0.54902, 0.784314) +corner_radius_top_left = 10 +corner_radius_top_right = 10 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_41273"] +bg_color = Color(0.0823529, 0.470588, 0.54902, 0.392157) +corner_radius_top_left = 10 +corner_radius_top_right = 10 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_uaw8g"] +bg_color = Color(0.27451, 0.258824, 0.368627, 1) +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(1, 0.933333, 0.8, 1) +corner_radius_top_left = 10 +corner_radius_top_right = 10 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_hbuoo"] +bg_color = Color(0.0823529, 0.470588, 0.54902, 1) +corner_radius_top_left = 5 +corner_radius_top_right = 5 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_fmcpx"] +bg_color = Color(1, 0.411765, 0.45098, 1) +corner_radius_top_left = 5 +corner_radius_top_right = 5 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_rnlxr"] +bg_color = Color(0.27451, 0.258824, 0.368627, 1) +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(1, 0.933333, 0.8, 1) +corner_radius_top_left = 10 +corner_radius_top_right = 10 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_mdn0p"] +content_margin_left = 5.0 +content_margin_right = 5.0 +bg_color = Color(0.0823529, 0.470588, 0.54902, 0.588235) +corner_radius_top_left = 5 +corner_radius_top_right = 5 + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_g2lif"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_pltt4"] +content_margin_left = 5.0 +content_margin_right = 5.0 +bg_color = Color(0.0823529, 0.470588, 0.54902, 1) +corner_radius_top_left = 5 +corner_radius_top_right = 5 +expand_margin_top = 3.0 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_yhwjn"] +content_margin_left = 5.0 +content_margin_right = 5.0 +bg_color = Color(1, 0.411765, 0.45098, 1) +corner_radius_top_left = 5 +corner_radius_top_right = 5 +expand_margin_top = 3.0 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_l8cky"] +content_margin_left = 5.0 +content_margin_right = 5.0 +bg_color = Color(0.0823529, 0.470588, 0.54902, 1) +corner_radius_top_left = 5 +corner_radius_top_right = 5 + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_l34ym"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_7o6pk"] +content_margin_left = 10.0 +content_margin_right = 10.0 +bg_color = Color(0.0823529, 0.470588, 0.54902, 0.784314) +corner_radius_top_left = 10 +corner_radius_top_right = 10 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_y214y"] +bg_color = Color(1, 0.933333, 0.8, 1) +corner_radius_top_left = 5 +corner_radius_top_right = 5 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_h653x"] +bg_color = Color(1, 0.690196, 0.639216, 1) +corner_radius_top_left = 5 +corner_radius_top_right = 5 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_erami"] +bg_color = Color(1, 0.411765, 0.45098, 1) +corner_radius_top_left = 5 +corner_radius_top_right = 5 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_c7d7l"] +bg_color = Color(0.0823529, 0.470588, 0.54902, 1) +corner_radius_top_left = 5 +corner_radius_top_right = 5 + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_s6qsw"] + +[sub_resource type="StyleBoxLine" id="StyleBoxLine_cwkfx"] +color = Color(1, 0.933333, 0.8, 1) +thickness = 2 +vertical = true + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_g2lif"] +bg_color = Color(1, 0.411765, 0.45098, 1) +corner_radius_top_left = 5 +corner_radius_top_right = 5 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_b3cdw"] +bg_color = Color(1, 0.690196, 0.639216, 1) +corner_radius_top_left = 5 +corner_radius_top_right = 5 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_l34ym"] +content_margin_left = 5.0 +content_margin_right = 5.0 +bg_color = Color(0.0823529, 0.470588, 0.54902, 1) +corner_radius_top_left = 5 +corner_radius_top_right = 5 + +[resource] +default_font_size = 0 +Button/colors/font_color = Color(1, 0.933333, 0.8, 1) +Button/colors/font_disabled_color = Color(0.27451, 0.258824, 0.368627, 0.392157) +Button/colors/font_focus_color = Color(1, 0.933333, 0.8, 1) +Button/colors/font_hover_color = Color(0.27451, 0.258824, 0.368627, 1) +Button/colors/font_hover_pressed_color = Color(1, 0.933333, 0.8, 1) +Button/colors/font_pressed_color = Color(1, 0.933333, 0.8, 1) +Button/fonts/font = ExtResource("1_lopid") +Button/styles/disabled = SubResource("StyleBoxFlat_k0c1y") +Button/styles/focus = SubResource("StyleBoxEmpty_lopid") +Button/styles/hover = SubResource("StyleBoxFlat_myavk") +Button/styles/normal = SubResource("StyleBoxFlat_lopid") +Button/styles/pressed = SubResource("StyleBoxFlat_208pi") +CheckBox/colors/font_color = Color(1, 0.933333, 0.8, 1) +CheckBox/colors/font_focus_color = Color(1, 0.933333, 0.8, 1) +CheckBox/colors/font_hover_color = Color(1, 0.933333, 0.8, 1) +CheckBox/colors/font_hover_pressed_color = Color(1, 0.933333, 0.8, 1) +CheckBox/colors/font_pressed_color = Color(1, 0.933333, 0.8, 1) +CheckBox/icons/checked = ExtResource("2_k0c1y") +CheckBox/icons/unchecked = ExtResource("3_cwkfx") +CheckBox/styles/hover = SubResource("StyleBoxEmpty_fmcpx") +CheckBox/styles/hover_pressed = SubResource("StyleBoxEmpty_nas17") +CheckBox/styles/normal = SubResource("StyleBoxEmpty_adq26") +CheckBox/styles/pressed = SubResource("StyleBoxEmpty_wjpxi") +CheckButton/colors/font_color = Color(1, 0.933333, 0.8, 1) +CheckButton/colors/font_focus_color = Color(1, 0.933333, 0.8, 1) +CheckButton/colors/font_hover_color = Color(1, 0.933333, 0.8, 1) +CheckButton/colors/font_hover_pressed_color = Color(1, 0.933333, 0.8, 1) +CheckButton/colors/font_pressed_color = Color(1, 0.933333, 0.8, 1) +CheckButton/fonts/font = ExtResource("1_lopid") +CheckButton/icons/checked = ExtResource("2_myavk") +CheckButton/icons/unchecked = ExtResource("3_208pi") +CheckButton/styles/focus = SubResource("StyleBoxEmpty_k0c1y") +CheckButton/styles/hover = SubResource("StyleBoxEmpty_cwkfx") +CheckButton/styles/hover_pressed = SubResource("StyleBoxEmpty_1b0k0") +CheckButton/styles/normal = SubResource("StyleBoxEmpty_14r85") +CheckButton/styles/pressed = SubResource("StyleBoxEmpty_hbuoo") +HScrollBar/styles/grabber = SubResource("StyleBoxFlat_nas17") +HScrollBar/styles/grabber_highlight = SubResource("StyleBoxFlat_adq26") +HScrollBar/styles/grabber_pressed = SubResource("StyleBoxFlat_wjpxi") +HScrollBar/styles/scroll = SubResource("StyleBoxFlat_s6qsw") +HSeparator/styles/separator = SubResource("StyleBoxLine_k0c1y") +HSlider/icons/grabber = ExtResource("6_cwkfx") +HSlider/styles/grabber_area = SubResource("StyleBoxFlat_cwkfx") +HSlider/styles/grabber_area_highlight = SubResource("StyleBoxFlat_1b0k0") +HSlider/styles/slider = SubResource("StyleBoxFlat_14r85") +Label/colors/font_color = Color(1, 0.933333, 0.8, 1) +Label/fonts/font = ExtResource("1_lopid") +LineEdit/colors/caret_color = Color(1, 0.933333, 0.8, 1) +LineEdit/colors/clear_button_color_pressed = Color(1, 0.933333, 0.8, 1) +LineEdit/colors/font_color = Color(1, 0.933333, 0.8, 1) +LineEdit/colors/font_placeholder_color = Color(1, 0.933333, 0.8, 0.392157) +LineEdit/colors/font_selected_color = Color(1, 0.933333, 0.8, 1) +LineEdit/colors/selection_color = Color(1, 0.690196, 0.639216, 1) +LineEdit/fonts/font = ExtResource("1_lopid") +LineEdit/styles/focus = SubResource("StyleBoxEmpty_b3cdw") +LineEdit/styles/normal = SubResource("StyleBoxFlat_rga2m") +LineEdit/styles/read_only = SubResource("StyleBoxFlat_41273") +Panel/styles/panel = SubResource("StyleBoxFlat_uaw8g") +ProgressBar/colors/font_color = Color(1, 0.933333, 0.8, 1) +ProgressBar/fonts/font = ExtResource("1_lopid") +ProgressBar/styles/background = SubResource("StyleBoxFlat_hbuoo") +ProgressBar/styles/fill = SubResource("StyleBoxFlat_fmcpx") +TabContainer/colors/font_disabled_color = Color(1, 0.933333, 0.8, 0.392157) +TabContainer/colors/font_hovered_color = Color(1, 0.933333, 0.8, 1) +TabContainer/colors/font_selected_color = Color(1, 0.933333, 0.8, 1) +TabContainer/colors/font_unselected_color = Color(1, 0.933333, 0.8, 1) +TabContainer/fonts/font = ExtResource("1_lopid") +TabContainer/styles/panel = SubResource("StyleBoxFlat_rnlxr") +TabContainer/styles/tab_disabled = SubResource("StyleBoxFlat_mdn0p") +TabContainer/styles/tab_focus = SubResource("StyleBoxEmpty_g2lif") +TabContainer/styles/tab_hovered = SubResource("StyleBoxFlat_pltt4") +TabContainer/styles/tab_selected = SubResource("StyleBoxFlat_yhwjn") +TabContainer/styles/tab_unselected = SubResource("StyleBoxFlat_l8cky") +TextEdit/colors/background_color = Color(0, 0, 0, 0) +TextEdit/colors/caret_color = Color(1, 0.933333, 0.8, 1) +TextEdit/colors/font_color = Color(1, 0.933333, 0.8, 1) +TextEdit/colors/font_placeholder_color = Color(1, 0.933333, 0.8, 0.392157) +TextEdit/colors/font_readonly_color = Color(1, 0.933333, 0.8, 0.392157) +TextEdit/colors/font_selected_color = Color(1, 0.933333, 0.8, 1) +TextEdit/colors/search_result_color = Color(1, 0.690196, 0.639216, 1) +TextEdit/colors/selection_color = Color(1, 0.690196, 0.639216, 1) +TextEdit/fonts/font = ExtResource("1_lopid") +TextEdit/styles/focus = SubResource("StyleBoxEmpty_l34ym") +TextEdit/styles/normal = SubResource("StyleBoxFlat_7o6pk") +VScrollBar/styles/grabber = SubResource("StyleBoxFlat_y214y") +VScrollBar/styles/grabber_highlight = SubResource("StyleBoxFlat_h653x") +VScrollBar/styles/grabber_pressed = SubResource("StyleBoxFlat_erami") +VScrollBar/styles/scroll = SubResource("StyleBoxFlat_c7d7l") +VScrollBar/styles/scroll_focus = SubResource("StyleBoxEmpty_s6qsw") +VSeparator/styles/separator = SubResource("StyleBoxLine_cwkfx") +VSlider/icons/grabber = ExtResource("6_cwkfx") +VSlider/styles/grabber_area = SubResource("StyleBoxFlat_g2lif") +VSlider/styles/grabber_area_highlight = SubResource("StyleBoxFlat_b3cdw") +VSlider/styles/slider = SubResource("StyleBoxFlat_l34ym")