Implement Vendor NPCs (#37)
* Add WIP vendors * Add TimerExtensions * chore: Update dialogue manager version * Add Inventory.EquippedItem signal * Move files to UI.Inventory namespace * Modify Hotbar and InventorySlot organization Made InventorySlot a bit more modular. * Modify hotbar icons * Implement request to display shop * Add forsenLevel placeholder icon * Add shop UI * Update UI to use theme and click through * Update item metadata Added placeholder icons for items without an icon. * Add Snus Dealer example vendor NPC * Implement factions with bitflags instead * Fix typo in Doc dialogue * Hide and show shop menu * Add shaders * Add newline to console output * Add modal interfacerefactor
							parent
							
								
									516d79d86f
								
							
						
					
					
						commit
						b8a5e641b6
					
				|  | @ -1,6 +1,6 @@ | |||
| [remap] | ||||
| 
 | ||||
| importer="dialogue_manager_compiler_11" | ||||
| importer="dialogue_manager_compiler_12" | ||||
| type="Resource" | ||||
| uid="uid://dilmuoilweoeh" | ||||
| path="res://.godot/imported/books.dialogue-cc272ebae322ae3ca46820dca11a3437.tres" | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| [remap] | ||||
| 
 | ||||
| importer="dialogue_manager_compiler_11" | ||||
| importer="dialogue_manager_compiler_12" | ||||
| type="Resource" | ||||
| uid="uid://c2om4y0fm81yr" | ||||
| path="res://.godot/imported/clone-machine.dialogue-8810934a67eacdad52469e9ef5f970fb.tres" | ||||
|  |  | |||
|  | @ -18,11 +18,10 @@ Doc, The Two Time: Nothing but success. | |||
| Doc, The Two Time: That's what my life's about. Period. | ||||
| Doc, The Two Time: And the arena today, ladies and gentlemen... | ||||
| Doc, The Two Time: is wide open, and the crowds are flooding in. VIP seating. Skybox section. Reserved for the Slick Daddy Club. | ||||
| Doc, The Two Time: The Slick Daddy Club lofoking so damn good today. I'm feeling so damn good. It's obvious. | ||||
| Doc, The Two Time: The V of success. | ||||
| Doc, The Two Time: The Slick Daddy Club looking so damn good today. I'm feeling so damn good. It's obvious. | ||||
| Challenge [b]Doc, The Two Time[/b] to a duel? | ||||
| - Yes | ||||
| 	do emit("SummonBoss", "Doc") | ||||
| - No => END | ||||
| - No, I'm a little chubby cheek wannabe punk kid. => END | ||||
| 
 | ||||
| => END | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| [remap] | ||||
| 
 | ||||
| importer="dialogue_manager_compiler_11" | ||||
| importer="dialogue_manager_compiler_12" | ||||
| type="Resource" | ||||
| uid="uid://dntkvjjr8mrgf" | ||||
| path="res://.godot/imported/doc.dialogue-8f95f6a09d3ac685b71d7e07c49df1c6.tres" | ||||
|  |  | |||
											
												Binary file not shown.
											
										
									
								
											
												Binary file not shown.
											
										
									
								| After Width: | Height: | Size: 189 B | 
|  | @ -0,0 +1,34 @@ | |||
| [remap] | ||||
| 
 | ||||
| importer="texture" | ||||
| type="CompressedTexture2D" | ||||
| uid="uid://dc1gcsbhkchvg" | ||||
| path="res://.godot/imported/hotbar-active.png-7657e1b4001be05323aa0d0697509f58.ctex" | ||||
| metadata={ | ||||
| "vram_texture": false | ||||
| } | ||||
| 
 | ||||
| [deps] | ||||
| 
 | ||||
| source_file="res://Assets/Sprites/UI/hotbar-active.png" | ||||
| dest_files=["res://.godot/imported/hotbar-active.png-7657e1b4001be05323aa0d0697509f58.ctex"] | ||||
| 
 | ||||
| [params] | ||||
| 
 | ||||
| compress/mode=0 | ||||
| compress/high_quality=false | ||||
| compress/lossy_quality=0.7 | ||||
| compress/hdr_compression=1 | ||||
| compress/normal_map=0 | ||||
| compress/channel_pack=0 | ||||
| mipmaps/generate=false | ||||
| mipmaps/limit=-1 | ||||
| roughness/mode=0 | ||||
| roughness/src_normal="" | ||||
| process/fix_alpha_border=true | ||||
| process/premult_alpha=false | ||||
| process/normal_map_invert_y=false | ||||
| process/hdr_as_srgb=false | ||||
| process/hdr_clamp_exposure=false | ||||
| process/size_limit=0 | ||||
| detect_3d/compress_to=1 | ||||
											
												Binary file not shown.
											
										
									
								
											
												Binary file not shown.
											
										
									
								| Before Width: | Height: | Size: 115 B After Width: | Height: | Size: 153 B | 
											
												Binary file not shown.
											
										
									
								
											
												Binary file not shown.
											
										
									
								| After Width: | Height: | Size: 832 B | 
|  | @ -0,0 +1,34 @@ | |||
| [remap] | ||||
| 
 | ||||
| importer="texture" | ||||
| type="CompressedTexture2D" | ||||
| uid="uid://uhmowtsi3wfh" | ||||
| path="res://.godot/imported/menu-rect-no-bg-white.png-5ea2d275879af97991070fb370031211.ctex" | ||||
| metadata={ | ||||
| "vram_texture": false | ||||
| } | ||||
| 
 | ||||
| [deps] | ||||
| 
 | ||||
| source_file="res://Assets/Sprites/UI/menu-rect-no-bg-white.png" | ||||
| dest_files=["res://.godot/imported/menu-rect-no-bg-white.png-5ea2d275879af97991070fb370031211.ctex"] | ||||
| 
 | ||||
| [params] | ||||
| 
 | ||||
| compress/mode=0 | ||||
| compress/high_quality=false | ||||
| compress/lossy_quality=0.7 | ||||
| compress/hdr_compression=1 | ||||
| compress/normal_map=0 | ||||
| compress/channel_pack=0 | ||||
| mipmaps/generate=false | ||||
| mipmaps/limit=-1 | ||||
| roughness/mode=0 | ||||
| roughness/src_normal="" | ||||
| process/fix_alpha_border=true | ||||
| process/premult_alpha=false | ||||
| process/normal_map_invert_y=false | ||||
| process/hdr_as_srgb=false | ||||
| process/hdr_clamp_exposure=false | ||||
| process/size_limit=0 | ||||
| detect_3d/compress_to=1 | ||||
											
												Binary file not shown.
											
										
									
								
											
												Binary file not shown.
											
										
									
								| After Width: | Height: | Size: 579 B | 
|  | @ -0,0 +1,34 @@ | |||
| [remap] | ||||
| 
 | ||||
| importer="texture" | ||||
| type="CompressedTexture2D" | ||||
| uid="uid://vjdyrv8wp7gl" | ||||
| path="res://.godot/imported/forsenLevel.png-ffd03264deccb9275d087fadbb57e56f.ctex" | ||||
| metadata={ | ||||
| "vram_texture": false | ||||
| } | ||||
| 
 | ||||
| [deps] | ||||
| 
 | ||||
| source_file="res://Assets/Sprites/forsenLevel.png" | ||||
| dest_files=["res://.godot/imported/forsenLevel.png-ffd03264deccb9275d087fadbb57e56f.ctex"] | ||||
| 
 | ||||
| [params] | ||||
| 
 | ||||
| compress/mode=0 | ||||
| compress/high_quality=false | ||||
| compress/lossy_quality=0.7 | ||||
| compress/hdr_compression=1 | ||||
| compress/normal_map=0 | ||||
| compress/channel_pack=0 | ||||
| mipmaps/generate=false | ||||
| mipmaps/limit=-1 | ||||
| roughness/mode=0 | ||||
| roughness/src_normal="" | ||||
| process/fix_alpha_border=true | ||||
| process/premult_alpha=false | ||||
| process/normal_map_invert_y=false | ||||
| process/hdr_as_srgb=false | ||||
| process/hdr_clamp_exposure=false | ||||
| process/size_limit=0 | ||||
| detect_3d/compress_to=1 | ||||
|  | @ -5,6 +5,6 @@ namespace SupaLidlGame.BoundingBoxes; | |||
| 
 | ||||
| public abstract partial class BoundingBox : Area2D, IFaction | ||||
| { | ||||
|     [Export] | ||||
|     public ushort Faction { get; set; } | ||||
|     [Export(PropertyHint.Flags)] | ||||
|     public FactionName Faction { get; set; } | ||||
| } | ||||
|  |  | |||
|  | @ -93,8 +93,8 @@ public partial class Character : CharacterBody2D, IFaction | |||
|     [Export] | ||||
|     public BoundingBoxes.Hurtbox Hurtbox { get; set; } | ||||
| 
 | ||||
|     [Export] | ||||
|     public ushort Faction { get; set; } | ||||
|     [Export(PropertyHint.Flags)] | ||||
|     public FactionName Faction { get; set; } | ||||
| 
 | ||||
|     public AnimationPlayer MovementAnimation { get; set; } | ||||
| 
 | ||||
|  |  | |||
|  | @ -573,6 +573,7 @@ Sprite = NodePath("Sprite") | |||
| Inventory = NodePath("Inventory") | ||||
| StateMachine = NodePath("StateMachine") | ||||
| Hurtbox = NodePath("Hurtbox") | ||||
| Faction = 2 | ||||
| 
 | ||||
| [node name="Stats" type="Node" parent="."] | ||||
| script = ExtResource("5_a7fiw") | ||||
|  |  | |||
|  | @ -3,6 +3,7 @@ | |||
| using Godot; | ||||
| using SupaLidlGame.Extensions; | ||||
| using SupaLidlGame.Items; | ||||
| using SupaLidlGame.Utils; | ||||
| using System; | ||||
| 
 | ||||
| namespace SupaLidlGame.Characters; | ||||
|  | @ -127,6 +128,37 @@ public partial class NPC : Character | |||
|         return bestChar; | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Finds the best character whose faction aligns with this character's. | ||||
|     /// </summary> | ||||
|     public virtual Character FindBestNeutral() | ||||
|     { | ||||
|         float bestScore = float.MaxValue; | ||||
|         Character bestChar = null; | ||||
|         // NOTE: this relies on all Characters being under the Entities node | ||||
|         foreach (Node node in GetParent().GetChildren()) | ||||
|         { | ||||
|             if (node is Character character) | ||||
|             { | ||||
|                 bool isFriendly = ((IFaction)this).AlignsWith(character); | ||||
|                 if (isFriendly || character.Health <= 0) | ||||
|                 { | ||||
|                     continue; | ||||
|                 } | ||||
| 
 | ||||
|                 float score = 0; | ||||
|                 score -= Position.DistanceTo(character.Position); | ||||
| 
 | ||||
|                 if (score < bestScore) | ||||
|                 { | ||||
|                     bestScore = score; | ||||
|                     bestChar = character; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return bestChar; | ||||
|     } | ||||
| 
 | ||||
|     public override void _Process(double delta) | ||||
|     { | ||||
|         ThinkerStateMachine.Process(delta); | ||||
|  |  | |||
|  | @ -0,0 +1,74 @@ | |||
| [gd_scene load_steps=11 format=3 uid="uid://rd08pot25h00"] | ||||
| 
 | ||||
| [ext_resource type="Script" path="res://Characters/NPC.cs" id="1_04gcf"] | ||||
| [ext_resource type="Script" path="res://State/Character/CharacterStateMachine.cs" id="2_kynkg"] | ||||
| [ext_resource type="Texture2D" uid="uid://bej8thq7ruyty" path="res://Assets/Sprites/Characters/forsen2.png" id="2_s5nik"] | ||||
| [ext_resource type="Script" path="res://State/Character/NPCIdleState.cs" id="3_pcrll"] | ||||
| [ext_resource type="Script" path="res://State/Thinker/ThinkerStateMachine.cs" id="4_mo4wj"] | ||||
| [ext_resource type="Script" path="res://State/Thinker/VendorIdle.cs" id="5_oau5d"] | ||||
| [ext_resource type="PackedScene" uid="uid://dldnp8eunxj3q" path="res://BoundingBoxes/InteractionTrigger.tscn" id="5_sjs24"] | ||||
| [ext_resource type="Script" path="res://Utils/InteractionTriggerDialogue.cs" id="5_yknpw"] | ||||
| [ext_resource type="Resource" uid="uid://c4n7vhoxybu70" path="res://Dialogue/snus-dealer.dialogue" id="6_isvnq"] | ||||
| [ext_resource type="Script" path="res://Items/Inventory.cs" id="7_vip6b"] | ||||
| 
 | ||||
| [node name="SnusDealer" type="CharacterBody2D" node_paths=PackedStringArray("ThinkerStateMachine", "Sprite", "Inventory", "StateMachine")] | ||||
| script = ExtResource("1_04gcf") | ||||
| ThinkerStateMachine = NodePath("Thinker") | ||||
| Sprite = NodePath("Sprites/Sprite") | ||||
| Inventory = NodePath("Inventory") | ||||
| StateMachine = NodePath("StateMachine") | ||||
| 
 | ||||
| [node name="StateMachine" type="Node" parent="." node_paths=PackedStringArray("InitialState", "Character")] | ||||
| script = ExtResource("2_kynkg") | ||||
| InitialState = NodePath("Idle") | ||||
| Character = NodePath("..") | ||||
| 
 | ||||
| [node name="Idle" type="Node" parent="StateMachine" node_paths=PackedStringArray("Character")] | ||||
| script = ExtResource("3_pcrll") | ||||
| Character = NodePath("../..") | ||||
| 
 | ||||
| [node name="Thinker" type="Node" parent="." node_paths=PackedStringArray("InitialState")] | ||||
| script = ExtResource("4_mo4wj") | ||||
| InitialState = NodePath("Idle") | ||||
| 
 | ||||
| [node name="Idle" type="Node" parent="Thinker" node_paths=PackedStringArray("NPC")] | ||||
| script = ExtResource("5_oau5d") | ||||
| NPC = NodePath("../..") | ||||
| 
 | ||||
| [node name="Animations" type="Node" parent="."] | ||||
| 
 | ||||
| [node name="Movement" type="AnimationPlayer" parent="Animations"] | ||||
| 
 | ||||
| [node name="Hurt" type="AnimationPlayer" parent="Animations"] | ||||
| 
 | ||||
| [node name="Stun" type="AnimationPlayer" parent="Animations"] | ||||
| 
 | ||||
| [node name="Attack" type="AnimationPlayer" parent="Animations"] | ||||
| 
 | ||||
| [node name="Stats" type="Node" parent="."] | ||||
| 
 | ||||
| [node name="Sprites" type="Node2D" parent="."] | ||||
| 
 | ||||
| [node name="Sprite" type="Sprite2D" parent="Sprites"] | ||||
| texture = ExtResource("2_s5nik") | ||||
| centered = false | ||||
| offset = Vector2(-12, -20) | ||||
| hframes = 46 | ||||
| 
 | ||||
| [node name="Inventory" type="Node2D" parent="." node_paths=PackedStringArray("Hotbar")] | ||||
| script = ExtResource("7_vip6b") | ||||
| Hotbar = [] | ||||
| 
 | ||||
| [node name="Interaction" type="Node2D" parent="." node_paths=PackedStringArray("InteractionTrigger")] | ||||
| position = Vector2(0, -4) | ||||
| script = ExtResource("5_yknpw") | ||||
| InteractionTrigger = NodePath("InteractionTrigger") | ||||
| DialogueResource = ExtResource("6_isvnq") | ||||
| DialogueTitle = "start" | ||||
| 
 | ||||
| [node name="InteractionTrigger" parent="Interaction" instance=ExtResource("5_sjs24")] | ||||
| 
 | ||||
| [node name="Label" parent="Interaction/InteractionTrigger/Popup" index="0"] | ||||
| text = "Listen" | ||||
| 
 | ||||
| [editable path="Interaction/InteractionTrigger"] | ||||
|  | @ -0,0 +1,27 @@ | |||
| //using Godot; | ||||
| //using SupaLidlGame.BoundingBoxes; | ||||
| //using SupaLidlGame.Extensions; | ||||
| // | ||||
| //namespace SupaLidlGame.Characters; | ||||
| // | ||||
| //public partial class Vendor : NPC | ||||
| //{ | ||||
| //    [Export] | ||||
| //    public InteractionTrigger InteractionTrigger { get; set; } | ||||
| // | ||||
| //    [Export(PropertyHint.File, "*.dialogue")] | ||||
| //    public Resource DialogueResource { get; set; } | ||||
| // | ||||
| //    [Export] | ||||
| //    public string DialogueTitle { get; set; } | ||||
| // | ||||
| //    public override void _Ready() | ||||
| //    { | ||||
| //        InteractionTrigger.Interaction += OnInteraction; | ||||
| //    } | ||||
| // | ||||
| //    private void OnInteraction() | ||||
| //    { | ||||
| //        this.GetWorld().DialogueBalloon.Start(DialogueResource, DialogueTitle); | ||||
| //    } | ||||
| //} | ||||
|  | @ -34,6 +34,8 @@ public sealed partial class DebugConsole : Control | |||
| 
 | ||||
|     private Node ctx => Context; | ||||
| 
 | ||||
|     public const double DEBUG_REFRESH_INTERVAL = 0.25; | ||||
| 
 | ||||
|     public override void _Ready() | ||||
|     { | ||||
|         _entry = GetNode<Entry>("%Entry"); | ||||
|  | @ -61,6 +63,39 @@ public sealed partial class DebugConsole : Control | |||
|                 } | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         GD.Print("DebugConsole init"); | ||||
|         // TODO: put this in a separate class | ||||
|         // watch godot.log | ||||
|         bool isFileLoggingEnabled = ProjectSettings | ||||
|             .GetSetting("debug/file_logging/enable_file_logging.pc") | ||||
|             .AsBool(); | ||||
| 
 | ||||
|         if (isFileLoggingEnabled) | ||||
|         { | ||||
|             GD.Print("File logging is enabled."); | ||||
|             string logPath = ProjectSettings | ||||
|                 .GetSetting("debug/file_logging/log_path") | ||||
|                 .AsString(); | ||||
|             var fs = FileAccess.Open(logPath, FileAccess.ModeFlags.Read); | ||||
| 
 | ||||
|             var timer = new Timer(); | ||||
|             AddChild(timer); | ||||
|             timer.Timeout += () => | ||||
|             { | ||||
|                 // push  | ||||
|                 while (fs.GetPosition() < fs.GetLength()) | ||||
|                 { | ||||
|                     string line = fs.GetLine(); | ||||
|                     _output.Text += line + "\n"; | ||||
|                 } | ||||
|             }; | ||||
|             timer.Start(DEBUG_REFRESH_INTERVAL); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             GD.PushWarning("File logging is not enabled."); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public IEnumerable<NodePathToken> SplitPath(NodePath path) | ||||
|  | @ -209,13 +244,21 @@ public sealed partial class DebugConsole : Control | |||
| 
 | ||||
|         Godot.Expression exp = new(); | ||||
| 
 | ||||
|         string[] reserved = { "from", "set_context", "context", "set_prop", "to_node_path" }; | ||||
|         string[] reserved = { | ||||
|             "from", | ||||
|             "set_context", | ||||
|             "context", | ||||
|             "set_prop", | ||||
|             "to_node_path", | ||||
|             "load", | ||||
|         }; | ||||
|         Godot.Collections.Array reservedMap = new(); | ||||
|         reservedMap.Add(new Callable(this, MethodName.From)); | ||||
|         reservedMap.Add(new Callable(this, MethodName.SetContext)); | ||||
|         reservedMap.Add(Context); | ||||
|         reservedMap.Add(new Callable(this, MethodName.SetProp)); | ||||
|         reservedMap.Add(new Callable(this, MethodName.ToNodePath)); | ||||
|         reservedMap.Add(new Callable(this, MethodName.Load)); | ||||
| 
 | ||||
|         var err = exp.Parse(str, reserved); | ||||
|         if (err != Error.Ok) | ||||
|  | @ -246,4 +289,9 @@ public sealed partial class DebugConsole : Control | |||
|     { | ||||
|         Context = node; | ||||
|     } | ||||
| 
 | ||||
|     private Resource Load(string path) | ||||
|     { | ||||
|         return ResourceLoader.Load(path); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -0,0 +1,23 @@ | |||
| ~ start | ||||
| 
 | ||||
| Snus Dealer: Hey kid, wanna buy some snus? | ||||
| - Alright. => shop | ||||
| - No, I don't think so. => END | ||||
| - Snus? => dont_snus | ||||
| 
 | ||||
| => END | ||||
| 
 | ||||
| ~ dont_snus | ||||
| 
 | ||||
| Snus Dealer: You know what they say. | ||||
| Snus Dealer: If you don't snus... | ||||
| Snus Dealer: you lose. | ||||
| - Pepepains | ||||
| 
 | ||||
| => start | ||||
| 
 | ||||
| ~ shop | ||||
| 
 | ||||
| do emit("EnterShop", "res://Items/Shops/SnusDealer.tres") | ||||
| 
 | ||||
| => END | ||||
|  | @ -0,0 +1,15 @@ | |||
| [remap] | ||||
| 
 | ||||
| importer="dialogue_manager_compiler_12" | ||||
| type="Resource" | ||||
| uid="uid://c4n7vhoxybu70" | ||||
| path="res://.godot/imported/snus-dealer.dialogue-69dbddee28632f18888364bae03f393d.tres" | ||||
| 
 | ||||
| [deps] | ||||
| 
 | ||||
| source_file="res://Dialogue/snus-dealer.dialogue" | ||||
| dest_files=["res://.godot/imported/snus-dealer.dialogue-69dbddee28632f18888364bae03f393d.tres"] | ||||
| 
 | ||||
| [params] | ||||
| 
 | ||||
| defaults=true | ||||
|  | @ -58,7 +58,7 @@ public partial class DynamicDoor : StaticBody2D | |||
|             foreach (var navmesh in Rebake) | ||||
|             { | ||||
|                 // rebake navmesh so NPCs can correctly travel conditionally | ||||
|                 GD.Print("rebaking"); | ||||
|                 GD.Print("Dynamic door updated; rebaking navmeshes..."); | ||||
|                 navmesh.BakeNavigationPolygon(); | ||||
|             } | ||||
|         } | ||||
|  |  | |||
|  | @ -102,12 +102,12 @@ y_sort_enabled = true | |||
| texture_filter = 1 | ||||
| sprite_frames = SubResource("SpriteFrames_gf7ku") | ||||
| autoplay = "default" | ||||
| frame = 6 | ||||
| frame_progress = 0.743234 | ||||
| frame = 9 | ||||
| frame_progress = 0.966501 | ||||
| offset = Vector2(0, -12) | ||||
| 
 | ||||
| [node name="PointLight2D" type="PointLight2D" parent="."] | ||||
| color = Color(1, 0.9525, 0.85, 1) | ||||
| color = Color(1, 0.811765, 0.537255, 1) | ||||
| energy = 1.2 | ||||
| blend_mode = 2 | ||||
| shadow_filter_smooth = 3.0 | ||||
|  |  | |||
|  | @ -51,6 +51,9 @@ public partial class EventBus : Node | |||
|     [Signal] | ||||
|     public delegate void ExitTransitionEventHandler(); | ||||
| 
 | ||||
|     [Signal] | ||||
|     public delegate void EnterShopEventHandler(string shopResourcePath); | ||||
| 
 | ||||
|     public override void _Ready() | ||||
|     { | ||||
|         ProcessMode = ProcessModeEnum.Always; | ||||
|  |  | |||
|  | @ -0,0 +1,12 @@ | |||
| using Godot; | ||||
| 
 | ||||
| namespace SupaLidlGame.Extensions; | ||||
| 
 | ||||
| public static class TimerExtensions | ||||
| { | ||||
|     public static void Restart(this Timer timer, double timeSec = -1) | ||||
|     { | ||||
|         timer.Stop(); | ||||
|         timer.Start(timeSec); | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,15 @@ | |||
| namespace SupaLidlGame.Items; | ||||
| 
 | ||||
| public interface IItemCollection | ||||
| { | ||||
|     public System.Collections.Generic.IEnumerable<ItemMetadata> GetItems(); | ||||
| 
 | ||||
|     public int Capacity { get; } | ||||
| } | ||||
| 
 | ||||
| public interface IItemCollection<T> : IItemCollection | ||||
| { | ||||
|     public bool Add(T item); | ||||
| 
 | ||||
|     public bool Remove(T item); | ||||
| } | ||||
|  | @ -4,7 +4,7 @@ using Godot.Collections; | |||
| 
 | ||||
| namespace SupaLidlGame.Items; | ||||
| 
 | ||||
| public partial class Inventory : Node2D | ||||
| public partial class Inventory : Node2D, IItemCollection<ItemMetadata> | ||||
| { | ||||
|     public Character Character { get; private set; } | ||||
| 
 | ||||
|  | @ -20,7 +20,15 @@ public partial class Inventory : Node2D | |||
|     [Signal] | ||||
|     public delegate void UsedItemEventHandler(Item item); | ||||
| 
 | ||||
|     public const int MaxCapacity = 3; | ||||
|     [Signal] | ||||
|     public delegate void EquippedItemEventHandler(Item newItem, Item prevItem); | ||||
| 
 | ||||
|     [Signal] | ||||
|     public delegate void ItemAddedEventHandler(ItemMetadata newItemMetadata); | ||||
| 
 | ||||
|     public int Capacity { get; set; } = 30; | ||||
| 
 | ||||
|     public const int HotbarCapacity = 3; | ||||
| 
 | ||||
|     private Item _selectedItem; | ||||
| 
 | ||||
|  | @ -87,7 +95,8 @@ public partial class Inventory : Node2D | |||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         _selectedItem?.Unequip(Character); | ||||
|         Item prevItem = _selectedItem; | ||||
|         prevItem?.Unequip(Character); | ||||
|         _selectedIndex = index; | ||||
| 
 | ||||
|         if (index >= 0) | ||||
|  | @ -100,6 +109,8 @@ public partial class Inventory : Node2D | |||
|             _selectedItem = null; | ||||
|         } | ||||
| 
 | ||||
|         EmitSignal(SignalName.EquippedItem, prevItem, _selectedItem); | ||||
| 
 | ||||
|         GD.Print($"Inventory: {index} is new selected index."); | ||||
| 
 | ||||
|         return true; | ||||
|  | @ -135,8 +146,9 @@ public partial class Inventory : Node2D | |||
|         return null; | ||||
|     } | ||||
| 
 | ||||
|     public Item AddItemToHotbar(ItemMetadata metadata) | ||||
|     public Item AddToHotbar(ItemMetadata metadata) | ||||
|     { | ||||
|         //AddItemMetadata(metadata); | ||||
|         var item = metadata.Instance.Instantiate<Item>(); | ||||
|         AddItem(item); | ||||
|         AddChild(item); | ||||
|  | @ -146,7 +158,7 @@ public partial class Inventory : Node2D | |||
| 
 | ||||
|     public Item AddItem(Item item) | ||||
|     { | ||||
|         if (Hotbar.Count >= MaxCapacity) | ||||
|         if (Hotbar.Count >= HotbarCapacity) | ||||
|         { | ||||
|             return null; | ||||
|         } | ||||
|  | @ -157,9 +169,32 @@ public partial class Inventory : Node2D | |||
|         { | ||||
|             Hotbar.Add(item); | ||||
|         } | ||||
| 
 | ||||
|         return item; | ||||
|     } | ||||
| 
 | ||||
|     public System.Collections.Generic.IEnumerable<ItemMetadata> GetItems() | ||||
|     { | ||||
|         return Items; | ||||
|     } | ||||
| 
 | ||||
|     public bool Add(ItemMetadata item) | ||||
|     { | ||||
|         if (Items.Count >= Capacity) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         Items.Add(item); | ||||
|         EmitSignal(SignalName.ItemAdded, item); | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     public bool Remove(ItemMetadata item) | ||||
|     { | ||||
|         return Items.Remove(item); | ||||
|     } | ||||
| 
 | ||||
|     public Item DropItem(Item item) | ||||
|     { | ||||
|         item.CharacterOwner = null; | ||||
|  |  | |||
|  | @ -0,0 +1,45 @@ | |||
| using Godot; | ||||
| using Godot.Collections; | ||||
| using GodotUtilities.Collections; | ||||
| using System.Linq; | ||||
| using SupaLidlGame.Utils; | ||||
| 
 | ||||
| namespace SupaLidlGame.Items; | ||||
| 
 | ||||
| [GlobalClass] | ||||
| public partial class Shop : Resource, IItemCollection<ShopEntry> | ||||
| { | ||||
|     [Export] | ||||
|     protected Godot.Collections.Array<ShopEntry> Entries { get; private set; } | ||||
| 
 | ||||
|     public System.Collections.Generic.IEnumerable<ItemMetadata> GetItems() | ||||
|     { | ||||
|         var mapState = World.Instance.GlobalState.MapState; | ||||
|         return Entries | ||||
|             .Where( | ||||
|                 (entry) => | ||||
|                 { | ||||
|                     var condition = entry.MapStateCondition; | ||||
|                     if (string.IsNullOrEmpty(condition)) | ||||
|                     { | ||||
|                         return true; | ||||
|                     } | ||||
|                     return mapState.GetBoolean(condition) ?? false; | ||||
|                 } | ||||
|             ) | ||||
|             .Select((entry) => entry.Item); | ||||
|     } | ||||
| 
 | ||||
|     public bool Add(ShopEntry entry) | ||||
|     { | ||||
|         Entries.Add(entry); | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     public bool Remove(ShopEntry entry) | ||||
|     { | ||||
|         return Entries.Remove(entry); | ||||
|     } | ||||
| 
 | ||||
|     public int Capacity => Entries.Count; | ||||
| } | ||||
|  | @ -0,0 +1,23 @@ | |||
| using Godot; | ||||
| 
 | ||||
| namespace SupaLidlGame.Items; | ||||
| 
 | ||||
| [GlobalClass] | ||||
| public partial class ShopEntry : Resource | ||||
| { | ||||
|     public ShopEntry() : base() | ||||
|     { | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     public ShopEntry(ItemMetadata item) : base() | ||||
|     { | ||||
|         Item = item; | ||||
|     } | ||||
| 
 | ||||
|     [Export] | ||||
|     public ItemMetadata Item { get; set; } | ||||
| 
 | ||||
|     [Export] | ||||
|     public string MapStateCondition { get; set; } | ||||
| } | ||||
|  | @ -0,0 +1,54 @@ | |||
| [gd_resource type="Resource" script_class="Shop" load_steps=16 format=3 uid="uid://djqd88vdkoi6d"] | ||||
| 
 | ||||
| [ext_resource type="Script" path="res://Items/Shop.cs" id="1_betbc"] | ||||
| [ext_resource type="Resource" uid="uid://cjsh0dcgbfn77" path="res://Items/Weapons/Bow.tres" id="1_ntroj"] | ||||
| [ext_resource type="Script" path="res://Items/ShopEntry.cs" id="2_xgvwu"] | ||||
| [ext_resource type="Resource" uid="uid://iqe6rgnb3jur" path="res://Items/Weapons/Pugio.tres" id="3_nfeft"] | ||||
| [ext_resource type="Resource" uid="uid://dkm216ug0vj2h" path="res://Items/Weapons/Shotgun.tres" id="4_aw0ju"] | ||||
| 
 | ||||
| [sub_resource type="Resource" id="Resource_jdx0p"] | ||||
| script = ExtResource("2_xgvwu") | ||||
| Item = ExtResource("1_ntroj") | ||||
| MapStateCondition = "" | ||||
| 
 | ||||
| [sub_resource type="Resource" id="Resource_min4b"] | ||||
| script = ExtResource("2_xgvwu") | ||||
| Item = ExtResource("3_nfeft") | ||||
| MapStateCondition = "" | ||||
| 
 | ||||
| [sub_resource type="Resource" id="Resource_t0x08"] | ||||
| script = ExtResource("2_xgvwu") | ||||
| Item = ExtResource("4_aw0ju") | ||||
| MapStateCondition = "" | ||||
| 
 | ||||
| [sub_resource type="Resource" id="Resource_exmab"] | ||||
| script = ExtResource("2_xgvwu") | ||||
| MapStateCondition = "" | ||||
| 
 | ||||
| [sub_resource type="Resource" id="Resource_jlgb3"] | ||||
| script = ExtResource("2_xgvwu") | ||||
| MapStateCondition = "" | ||||
| 
 | ||||
| [sub_resource type="Resource" id="Resource_8rd47"] | ||||
| script = ExtResource("2_xgvwu") | ||||
| MapStateCondition = "" | ||||
| 
 | ||||
| [sub_resource type="Resource" id="Resource_pqgh5"] | ||||
| script = ExtResource("2_xgvwu") | ||||
| MapStateCondition = "" | ||||
| 
 | ||||
| [sub_resource type="Resource" id="Resource_8mift"] | ||||
| script = ExtResource("2_xgvwu") | ||||
| MapStateCondition = "" | ||||
| 
 | ||||
| [sub_resource type="Resource" id="Resource_4e8it"] | ||||
| script = ExtResource("2_xgvwu") | ||||
| MapStateCondition = "" | ||||
| 
 | ||||
| [sub_resource type="Resource" id="Resource_jp7ms"] | ||||
| script = ExtResource("2_xgvwu") | ||||
| MapStateCondition = "" | ||||
| 
 | ||||
| [resource] | ||||
| script = ExtResource("1_betbc") | ||||
| Entries = Array[Object]([SubResource("Resource_jdx0p"), SubResource("Resource_min4b"), SubResource("Resource_t0x08"), SubResource("Resource_exmab"), SubResource("Resource_jlgb3"), SubResource("Resource_8rd47"), SubResource("Resource_pqgh5"), SubResource("Resource_8mift"), SubResource("Resource_4e8it"), SubResource("Resource_jp7ms")]) | ||||
|  | @ -1,5 +1,6 @@ | |||
| [gd_resource type="Resource" script_class="ItemMetadata" load_steps=4 format=3 uid="uid://cjsh0dcgbfn77"] | ||||
| [gd_resource type="Resource" script_class="ItemMetadata" load_steps=5 format=3 uid="uid://cjsh0dcgbfn77"] | ||||
| 
 | ||||
| [ext_resource type="Texture2D" uid="uid://vjdyrv8wp7gl" path="res://Assets/Sprites/forsenLevel.png" id="1_5blro"] | ||||
| [ext_resource type="Script" path="res://Utils/ScenePath.cs" id="1_haiji"] | ||||
| [ext_resource type="Script" path="res://Items/ItemMetadata.cs" id="2_hjbs0"] | ||||
| 
 | ||||
|  | @ -10,6 +11,7 @@ Path = "res://Items/Weapons/Bow.tscn" | |||
| [resource] | ||||
| script = ExtResource("2_hjbs0") | ||||
| Instance = SubResource("Resource_mjj1w") | ||||
| Icon = ExtResource("1_5blro") | ||||
| Name = "Bow" | ||||
| Description = "A bow and arrow." | ||||
| BuyPrice = 0 | ||||
|  |  | |||
|  | @ -24,6 +24,15 @@ public partial class ProjectileSpawner : Ranged | |||
|     [Export] | ||||
|     public float ProjectileAngleDeviation { get; set; } | ||||
| 
 | ||||
|     public string ProjectilePath | ||||
|     { | ||||
|         get => Projectile?.ResourcePath; | ||||
|         set | ||||
|         { | ||||
|             Projectile = GD.Load<PackedScene>(value); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     protected virtual void SpawnProjectile(Scenes.Map map, | ||||
|         Vector2 direction, float velocityModifier = 1) | ||||
|     { | ||||
|  |  | |||
|  | @ -1,5 +1,6 @@ | |||
| [gd_resource type="Resource" script_class="ItemMetadata" load_steps=4 format=3 uid="uid://iqe6rgnb3jur"] | ||||
| [gd_resource type="Resource" script_class="ItemMetadata" load_steps=5 format=3 uid="uid://iqe6rgnb3jur"] | ||||
| 
 | ||||
| [ext_resource type="Texture2D" uid="uid://vjdyrv8wp7gl" path="res://Assets/Sprites/forsenLevel.png" id="1_3ptey"] | ||||
| [ext_resource type="Script" path="res://Utils/ScenePath.cs" id="1_o026a"] | ||||
| [ext_resource type="Script" path="res://Items/ItemMetadata.cs" id="2_j4tmu"] | ||||
| 
 | ||||
|  | @ -10,6 +11,7 @@ Path = "res://Items/Weapons/Pugio.tscn" | |||
| [resource] | ||||
| script = ExtResource("2_j4tmu") | ||||
| Instance = SubResource("Resource_abrg1") | ||||
| Icon = ExtResource("1_3ptey") | ||||
| Name = "Pugio" | ||||
| Description = "A sidearm dagger." | ||||
| BuyPrice = 0 | ||||
|  |  | |||
|  | @ -0,0 +1,18 @@ | |||
| [gd_resource type="Resource" script_class="ItemMetadata" load_steps=5 format=3 uid="uid://dkm216ug0vj2h"] | ||||
| 
 | ||||
| [ext_resource type="Script" path="res://Utils/ScenePath.cs" id="1_owc5r"] | ||||
| [ext_resource type="Texture2D" uid="uid://vjdyrv8wp7gl" path="res://Assets/Sprites/forsenLevel.png" id="1_v76vk"] | ||||
| [ext_resource type="Script" path="res://Items/ItemMetadata.cs" id="1_vtlr1"] | ||||
| 
 | ||||
| [sub_resource type="Resource" id="Resource_6sxq7"] | ||||
| script = ExtResource("1_owc5r") | ||||
| Path = "res://Items/Weapons/Shotgun.tscn" | ||||
| 
 | ||||
| [resource] | ||||
| script = ExtResource("1_vtlr1") | ||||
| Instance = SubResource("Resource_6sxq7") | ||||
| Icon = ExtResource("1_v76vk") | ||||
| Name = "Shotgun" | ||||
| Description = "" | ||||
| BuyPrice = 1887 | ||||
| SellPrice = 0 | ||||
|  | @ -1,9 +1,10 @@ | |||
| [gd_scene load_steps=26 format=3 uid="uid://d1d4vg7we5rjr"] | ||||
| [gd_scene load_steps=27 format=3 uid="uid://d1d4vg7we5rjr"] | ||||
| 
 | ||||
| [ext_resource type="Script" path="res://Items/Weapons/ProjectileSpawner.cs" id="1_4xyyt"] | ||||
| [ext_resource type="Script" path="res://State/Weapon/WeaponStateMachine.cs" id="2_ag6rd"] | ||||
| [ext_resource type="PackedScene" uid="uid://da1do2r2pbayb" path="res://Entities/ShotgunPellet.tscn" id="2_p3wx2"] | ||||
| [ext_resource type="Script" path="res://State/Weapon/RangedIdleState.cs" id="3_dd6bh"] | ||||
| [ext_resource type="Resource" uid="uid://dkm216ug0vj2h" path="res://Items/Weapons/Shotgun.tres" id="3_ju036"] | ||||
| [ext_resource type="Script" path="res://State/Weapon/RangedFireState.cs" id="4_bwqd6"] | ||||
| [ext_resource type="Texture2D" uid="uid://b1omx2kdb2x1n" path="res://Assets/Sprites/Items/shotgun.png" id="5_g8d45"] | ||||
| [ext_resource type="Texture2D" uid="uid://c1a7lvb4uuwfy" path="res://Assets/Sprites/Particles/circle-16.png" id="6_va8ee"] | ||||
|  | @ -216,11 +217,9 @@ curve = SubResource("Curve_mqgo6") | |||
| [sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_uexo4"] | ||||
| particle_flag_disable_z = true | ||||
| flatness = 1.0 | ||||
| gravity = Vector3(0, -4, 0) | ||||
| initial_velocity_min = -8.0 | ||||
| initial_velocity_max = 24.0 | ||||
| orbit_velocity_min = 0.0 | ||||
| orbit_velocity_max = 0.0 | ||||
| gravity = Vector3(0, -4, 0) | ||||
| scale_min = 0.75 | ||||
| scale_curve = SubResource("CurveTexture_3omj5") | ||||
| color_ramp = SubResource("GradientTexture1D_83h4s") | ||||
|  | @ -236,13 +235,11 @@ gradient = SubResource("Gradient_jks7f") | |||
| particle_flag_disable_z = true | ||||
| direction = Vector3(-1, -2, 0) | ||||
| spread = 10.0 | ||||
| gravity = Vector3(0, 64, 0) | ||||
| initial_velocity_min = 16.0 | ||||
| initial_velocity_max = 32.0 | ||||
| angular_velocity_min = 30.0 | ||||
| angular_velocity_max = 60.0 | ||||
| orbit_velocity_min = 0.0 | ||||
| orbit_velocity_max = 0.0 | ||||
| gravity = Vector3(0, 64, 0) | ||||
| scale_min = 2.0 | ||||
| scale_max = 2.0 | ||||
| color_ramp = SubResource("GradientTexture1D_eeskk") | ||||
|  | @ -259,6 +256,7 @@ Damage = 12.0 | |||
| UseTime = 1.5 | ||||
| InitialVelocity = 220.0 | ||||
| PlayerLevelGain = 0.5 | ||||
| Metadata = ExtResource("3_ju036") | ||||
| 
 | ||||
| [node name="State" type="Node" parent="." node_paths=PackedStringArray("InitialState")] | ||||
| script = ExtResource("2_ag6rd") | ||||
|  |  | |||
|  | @ -3,11 +3,10 @@ | |||
| [ext_resource type="Script" path="res://Utils/World.cs" id="1_1k6ew"] | ||||
| [ext_resource type="PackedScene" uid="uid://c271rdjhd1gfo" path="res://UI/Base.tscn" id="2_mm0qt"] | ||||
| 
 | ||||
| [node name="World" type="Node2D" node_paths=PackedStringArray("MusicPlayer", "DialogueBalloon")] | ||||
| [node name="World" type="Node2D" node_paths=PackedStringArray("MusicPlayer")] | ||||
| process_mode = 3 | ||||
| script = ExtResource("1_1k6ew") | ||||
| MusicPlayer = NodePath("MusicPlayer") | ||||
| DialogueBalloon = NodePath("CanvasLayer/SubViewportContainer/UIViewport/DialogBalloon") | ||||
| 
 | ||||
| [node name="CanvasLayer" parent="." instance=ExtResource("2_mm0qt")] | ||||
| 
 | ||||
|  | @ -21,6 +20,7 @@ anchor_right = 1.0 | |||
| anchor_bottom = 1.0 | ||||
| grow_horizontal = 2 | ||||
| grow_vertical = 2 | ||||
| mouse_filter = 2 | ||||
| 
 | ||||
| [node name="MusicPlayer" type="AudioStreamPlayer" parent="."] | ||||
| bus = &"Music" | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| [gd_scene load_steps=22 format=3 uid="uid://cumi1cbg6xfdd"] | ||||
| [gd_scene load_steps=23 format=3 uid="uid://cumi1cbg6xfdd"] | ||||
| 
 | ||||
| [ext_resource type="PackedScene" uid="uid://clwv2owvk6abe" path="res://Scenes/BaseMap.tscn" id="1_oy3cp"] | ||||
| [ext_resource type="Texture2D" uid="uid://c70cn53osy56w" path="res://Assets/Sprites/dev-tileset.png" id="2_2gdry"] | ||||
|  | @ -11,6 +11,7 @@ | |||
| [ext_resource type="Texture2D" uid="uid://chwwo6vmf8iri" path="res://Assets/Sprites/Props/tree-autumn-leaves-2.png" id="8_4laic"] | ||||
| [ext_resource type="Texture2D" uid="uid://dhf2f5a1ty502" path="res://Assets/Sprites/Props/tree-autumn-leaves-1.png" id="8_hyhpu"] | ||||
| [ext_resource type="Texture2D" uid="uid://crvbsxrda5gcj" path="res://Assets/Sprites/Props/tree-autumn-leaves-3.png" id="9_exvkw"] | ||||
| [ext_resource type="PackedScene" uid="uid://rd08pot25h00" path="res://Characters/SnusDealer.tscn" id="12_eyny2"] | ||||
| 
 | ||||
| [sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_s1h2f"] | ||||
| texture = ExtResource("2_2gdry") | ||||
|  | @ -458,8 +459,8 @@ cell_size = 16.0 | |||
| agent_radius = 8.0 | ||||
| 
 | ||||
| [sub_resource type="NavigationPolygon" id="NavigationPolygon_8xwo5"] | ||||
| vertices = PackedVector2Array(280, -232, 280, -120, 276, -120, 216, -232, 216, -40, 276, -72, 296, -72, 296, -88, 392, -88, 392, -136, 472, -136, 472, -40, 440, -40, 440, 8, 488, 8, 488, 24, 440, 24, 440, 72, 424, 72, 424, 24, 424, 8, 376, 24, 376, 8, 424, -40, 296, -40, 296, -56, 276, -56, 276, -40) | ||||
| polygons = Array[PackedInt32Array]([PackedInt32Array(0, 1, 2, 3), PackedInt32Array(4, 3, 2, 5), PackedInt32Array(6, 7, 8), PackedInt32Array(8, 9, 10, 11, 12), PackedInt32Array(13, 14, 15, 16), PackedInt32Array(16, 17, 18, 19), PackedInt32Array(13, 16, 19, 20), PackedInt32Array(19, 21, 22, 20), PackedInt32Array(12, 13, 20, 23), PackedInt32Array(8, 12, 23), PackedInt32Array(8, 23, 24, 25), PackedInt32Array(6, 8, 25), PackedInt32Array(5, 6, 25, 26), PackedInt32Array(4, 5, 26), PackedInt32Array(4, 26, 27)]) | ||||
| vertices = PackedVector2Array(392, -88, 392, -136, 472, -136, 472, -40, 440, -40, 440, 8, 488, 8, 488, 24, 440, 24, 440, 72, 424, 72, 424, 24, 424, 8, 376, 24, 376, 8, 424, -40, 216, -40, 280, -88, 216, -232, 280, -232) | ||||
| polygons = Array[PackedInt32Array]([PackedInt32Array(0, 1, 2, 3, 4), PackedInt32Array(5, 6, 7, 8), PackedInt32Array(8, 9, 10, 11), PackedInt32Array(5, 8, 11, 12), PackedInt32Array(11, 13, 14, 12), PackedInt32Array(4, 5, 12, 15), PackedInt32Array(0, 4, 15), PackedInt32Array(0, 15, 16, 17), PackedInt32Array(17, 16, 18, 19)]) | ||||
| outlines = Array[PackedVector2Array]([PackedVector2Array(208, -240, 208, -32, 416, -32, 416.001, -15.9759, 368.001, -15.9787, 368, 32, 416, 32, 416, 80, 448, 80, 448, 32, 496, 32, 496.001, -15.9713, 448.001, -15.9741, 448, -32, 480, -32, 480.008, -143.972, 384.008, -143.978, 384.006, -95.9778, 288, -96, 288, -240)]) | ||||
| source_geometry_mode = 1 | ||||
| source_geometry_group_name = &"navigation" | ||||
|  | @ -479,6 +480,9 @@ Markers = NodePath("Markers") | |||
| AreaName = "Forest" | ||||
| MapName = "Forest" | ||||
| 
 | ||||
| [node name="CanvasModulate" parent="." index="7"] | ||||
| position = Vector2(0, 1) | ||||
| 
 | ||||
| [node name="StaticBody2D" type="StaticBody2D" parent="Props" index="0"] | ||||
| y_sort_enabled = true | ||||
| position = Vector2(0, -96) | ||||
|  | @ -843,7 +847,14 @@ position = Vector2(1272, 0) | |||
| [node name="Campfire" parent="Entities" index="0" instance=ExtResource("3_ve4i2")] | ||||
| position = Vector2(-24, -8) | ||||
| 
 | ||||
| [node name="Areas" parent="." index="3"] | ||||
| [node name="SnusDealer" parent="Entities" index="1" node_paths=PackedStringArray("ThinkerStateMachine", "Sprite", "Inventory", "StateMachine") instance=ExtResource("12_eyny2")] | ||||
| position = Vector2(79, -6) | ||||
| ThinkerStateMachine = NodePath("Thinker") | ||||
| Sprite = NodePath("Sprites/Sprite") | ||||
| Inventory = NodePath("Inventory") | ||||
| StateMachine = NodePath("StateMachine") | ||||
| 
 | ||||
| [node name="Areas" parent="." index="10"] | ||||
| visible = false | ||||
| 
 | ||||
| [node name="Main" type="NavigationRegion2D" parent="Areas" index="0"] | ||||
|  |  | |||
|  | @ -3,9 +3,5 @@ shader_type canvas_item; | |||
| uniform float amount : hint_range(0.5, 1.0, 0.01) = 1; | ||||
| 
 | ||||
| void fragment() { | ||||
| 	// Place fragment code here. | ||||
|     //COLOR = texture(TEXTURE, UV); | ||||
|     if (UV.x > amount || UV.x < 1.0 - amount) { | ||||
|         COLOR.a = 0.0; | ||||
|     } | ||||
|     COLOR.a *= step(UV.x, amount) * step(1.0 - amount, UV.x); | ||||
| } | ||||
|  |  | |||
|  | @ -0,0 +1,10 @@ | |||
| shader_type canvas_item; | ||||
| 
 | ||||
| uniform float x_amount : hint_range(0.5, 1.0, 0.01) = 1; | ||||
| uniform float y_amount : hint_range(0.5, 1.0, 0.01) = 0.5; | ||||
| 
 | ||||
| void fragment() { | ||||
|     COLOR.a *= step(UV.x, x_amount) * step(1.0 - x_amount, UV.x); | ||||
|     //COLOR.a *= step(1.0 - y_amount, UV.y) * step(UV.y, y_amount); | ||||
|     COLOR.a *= 1.0 - (step(1.0 - y_amount, UV.y) * step(UV.y, y_amount)); | ||||
| } | ||||
|  | @ -14,6 +14,8 @@ public partial class GlobalState : Node | |||
|     [Export] | ||||
|     public Stats Stats { get; set; } | ||||
| 
 | ||||
|     public static GlobalState Instance { get; private set; } | ||||
| 
 | ||||
|     [Export] | ||||
|     public GameSettings Settings { get; set; } | ||||
| 
 | ||||
|  | @ -30,6 +32,13 @@ public partial class GlobalState : Node | |||
| 
 | ||||
|     public override void _Ready() | ||||
|     { | ||||
|         if (Instance != null) | ||||
|         { | ||||
|             throw new MultipleSingletonsException(); | ||||
|         } | ||||
| 
 | ||||
|         Instance = this; | ||||
| 
 | ||||
|         ProcessMode = ProcessModeEnum.Always; | ||||
|         LoadSettings(); | ||||
|     } | ||||
|  |  | |||
|  | @ -45,4 +45,13 @@ public partial class MapState : Resource | |||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public bool? GetBoolean(string key) | ||||
|     { | ||||
|         if (_state[key].VariantType == Variant.Type.Bool) | ||||
|         { | ||||
|             return (bool)_state[key]; | ||||
|         } | ||||
|         return null; | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -0,0 +1,17 @@ | |||
| using Godot; | ||||
| using GodotUtilities; | ||||
| 
 | ||||
| namespace SupaLidlGame.State.Thinker; | ||||
| 
 | ||||
| public partial class VendorIdle : ThinkerState | ||||
| { | ||||
|     public override ThinkerState Think() | ||||
|     { | ||||
|         var bestNeutral = NPC.FindBestNeutral(); | ||||
|         if (bestNeutral is not null) | ||||
|         { | ||||
|             NPC.Target = bestNeutral.Position - NPC.Position; | ||||
|         } | ||||
|         return null; | ||||
|     } | ||||
| } | ||||
								
									
									
										
											72
										
									
									UI/Base.tscn
									
									
									
									
								
								
							
							
										
											72
										
									
									UI/Base.tscn
									
									
									
									
								|  | @ -1,13 +1,13 @@ | |||
| [gd_scene load_steps=14 format=3 uid="uid://c271rdjhd1gfo"] | ||||
| 
 | ||||
| [ext_resource type="PackedScene" uid="uid://73jm5qjy52vq" path="res://Dialogue/balloon.tscn" id="1_atjb1"] | ||||
| [ext_resource type="Script" path="res://UI/UIController.cs" id="2_b4b6l"] | ||||
| [ext_resource type="PackedScene" uid="uid://bxo553hblp6nf" path="res://UI/HealthBar.tscn" id="3_j1j6h"] | ||||
| [ext_resource type="PackedScene" uid="uid://01d24ij5av1y" path="res://UI/BossBar.tscn" id="4_igi28"] | ||||
| [ext_resource type="PackedScene" uid="uid://cr7tkxctmyags" path="res://UI/LevelBar.tscn" id="4_rcekd"] | ||||
| [ext_resource type="PackedScene" uid="uid://c77754nvmckn" path="res://UI/LocationDisplay.tscn" id="5_cr6vo"] | ||||
| [ext_resource type="PackedScene" uid="uid://sfs8dpfitpdu" path="res://UI/Hotbar.tscn" id="5_mmp18"] | ||||
| [ext_resource type="PackedScene" uid="uid://sfs8dpfitpdu" path="res://UI/Inventory/Hotbar.tscn" id="5_mmp18"] | ||||
| [ext_resource type="PackedScene" uid="uid://d3q1yu3n7cqfj" path="res://UI/SceneTransition.tscn" id="6_j0nhv"] | ||||
| [ext_resource type="PackedScene" uid="uid://cyggkyqosjk36" path="res://UI/Inventory/ShopMenu.tscn" id="8_ep3ae"] | ||||
| [ext_resource type="PackedScene" uid="uid://2afbrf8asy2a" path="res://UI/PostProcessing/Vignette.tscn" id="9_p1ubd"] | ||||
| [ext_resource type="PackedScene" uid="uid://b1wsryv4bn0cn" path="res://UI/PostProcessing/StunEffect.tscn" id="10_646ma"] | ||||
| [ext_resource type="Shader" path="res://Shaders/Grayscale.gdshader" id="11_w4gn1"] | ||||
|  | @ -20,6 +20,26 @@ shader_parameter/grayscale_intensity = 0.0 | |||
| [node name="BaseUI" type="CanvasLayer"] | ||||
| process_mode = 3 | ||||
| 
 | ||||
| [node name="PostProcessing" type="CanvasLayer" parent="."] | ||||
| 
 | ||||
| [node name="Vignette" parent="PostProcessing" instance=ExtResource("9_p1ubd")] | ||||
| 
 | ||||
| [node name="StunEffect" parent="PostProcessing" instance=ExtResource("10_646ma")] | ||||
| 
 | ||||
| [node name="Sprite2D" type="TextureRect" parent="PostProcessing"] | ||||
| visible = false | ||||
| material = SubResource("ShaderMaterial_kbd61") | ||||
| anchors_preset = 3 | ||||
| anchor_left = 1.0 | ||||
| anchor_top = 1.0 | ||||
| anchor_right = 1.0 | ||||
| anchor_bottom = 1.0 | ||||
| offset_left = -128.0 | ||||
| offset_top = -128.0 | ||||
| grow_horizontal = 0 | ||||
| grow_vertical = 0 | ||||
| texture = ExtResource("12_tyv35") | ||||
| 
 | ||||
| [node name="SubViewportContainer" type="SubViewportContainer" parent="."] | ||||
| anchors_preset = 15 | ||||
| anchor_right = 1.0 | ||||
|  | @ -36,9 +56,6 @@ handle_input_locally = false | |||
| size = Vector2i(640, 360) | ||||
| render_target_update_mode = 4 | ||||
| 
 | ||||
| [node name="DialogBalloon" parent="SubViewportContainer/UIViewport" instance=ExtResource("1_atjb1")] | ||||
| layer = 4 | ||||
| 
 | ||||
| [node name="MainUILayer" type="CanvasLayer" parent="SubViewportContainer/UIViewport"] | ||||
| layer = 3 | ||||
| 
 | ||||
|  | @ -52,6 +69,7 @@ grow_horizontal = 2 | |||
| grow_vertical = 2 | ||||
| size_flags_horizontal = 3 | ||||
| size_flags_vertical = 3 | ||||
| mouse_filter = 2 | ||||
| script = ExtResource("2_b4b6l") | ||||
| 
 | ||||
| [node name="Top" type="HBoxContainer" parent="SubViewportContainer/UIViewport/MainUILayer/Main"] | ||||
|  | @ -78,7 +96,6 @@ layout_mode = 2 | |||
| layout_mode = 2 | ||||
| 
 | ||||
| [node name="Margin2" type="MarginContainer" parent="SubViewportContainer/UIViewport/MainUILayer/Main/Top/Left/VBoxContainer"] | ||||
| visible = false | ||||
| layout_mode = 2 | ||||
| theme_override_constants/margin_left = 16 | ||||
| theme_override_constants/margin_top = 16 | ||||
|  | @ -92,6 +109,23 @@ theme_override_constants/margin_right = 16 | |||
| layout_mode = 2 | ||||
| _slots = [NodePath("InventorySlot"), NodePath("InventorySlot2"), NodePath("InventorySlot3")] | ||||
| 
 | ||||
| [node name="BoxContainer" type="HBoxContainer" parent="SubViewportContainer/UIViewport/MainUILayer/Main"] | ||||
| layout_mode = 1 | ||||
| anchors_preset = 11 | ||||
| anchor_left = 1.0 | ||||
| anchor_right = 1.0 | ||||
| anchor_bottom = 1.0 | ||||
| offset_left = -236.0 | ||||
| grow_horizontal = 0 | ||||
| grow_vertical = 2 | ||||
| alignment = 2 | ||||
| 
 | ||||
| [node name="ShopMenu" parent="SubViewportContainer/UIViewport/MainUILayer/Main/BoxContainer" node_paths=PackedStringArray("_inventoryGrid") instance=ExtResource("8_ep3ae")] | ||||
| unique_name_in_owner = true | ||||
| visible = false | ||||
| layout_mode = 2 | ||||
| _inventoryGrid = NodePath("PanelContainer/VBoxContainer/ScrollContainer/InventoryGrid") | ||||
| 
 | ||||
| [node name="Bottom" type="HBoxContainer" parent="SubViewportContainer/UIViewport/MainUILayer/Main"] | ||||
| layout_mode = 1 | ||||
| anchors_preset = 12 | ||||
|  | @ -107,27 +141,13 @@ alignment = 1 | |||
| visible = false | ||||
| layout_mode = 2 | ||||
| 
 | ||||
| [node name="VBoxContainer" type="VBoxContainer" parent="SubViewportContainer/UIViewport/MainUILayer/Main"] | ||||
| visible = false | ||||
| layout_mode = 0 | ||||
| offset_right = 40.0 | ||||
| offset_bottom = 40.0 | ||||
| 
 | ||||
| [node name="LocationDisplay" parent="SubViewportContainer/UIViewport/MainUILayer" instance=ExtResource("5_cr6vo")] | ||||
| 
 | ||||
| [node name="SceneTransition" parent="SubViewportContainer/UIViewport/MainUILayer" instance=ExtResource("6_j0nhv")] | ||||
| z_index = 1 | ||||
| 
 | ||||
| [node name="PostProcessing" type="CanvasLayer" parent="."] | ||||
| 
 | ||||
| [node name="Vignette" parent="PostProcessing" instance=ExtResource("9_p1ubd")] | ||||
| 
 | ||||
| [node name="StunEffect" parent="PostProcessing" instance=ExtResource("10_646ma")] | ||||
| 
 | ||||
| [node name="Sprite2D" type="TextureRect" parent="PostProcessing"] | ||||
| visible = false | ||||
| material = SubResource("ShaderMaterial_kbd61") | ||||
| anchors_preset = 3 | ||||
| anchor_left = 1.0 | ||||
| anchor_top = 1.0 | ||||
| anchor_right = 1.0 | ||||
| anchor_bottom = 1.0 | ||||
| offset_left = -128.0 | ||||
| offset_top = -128.0 | ||||
| grow_horizontal = 0 | ||||
| grow_vertical = 0 | ||||
| texture = ExtResource("12_tyv35") | ||||
|  |  | |||
|  | @ -1,25 +0,0 @@ | |||
| [gd_scene load_steps=3 format=3 uid="uid://sfs8dpfitpdu"] | ||||
| 
 | ||||
| [ext_resource type="Script" path="res://UI/Hotbar.cs" id="1_2sak2"] | ||||
| [ext_resource type="PackedScene" uid="uid://ctad0dkoyw8ad" path="res://UI/InventorySlot.tscn" id="1_ct3cn"] | ||||
| 
 | ||||
| [node name="Hotbar" type="GridContainer" node_paths=PackedStringArray("_slots")] | ||||
| anchors_preset = 1 | ||||
| anchor_left = 1.0 | ||||
| anchor_right = 1.0 | ||||
| offset_left = -112.0 | ||||
| offset_bottom = 32.0 | ||||
| grow_horizontal = 0 | ||||
| theme_override_constants/h_separation = 8 | ||||
| columns = 3 | ||||
| script = ExtResource("1_2sak2") | ||||
| _slots = [NodePath("InventorySlot"), NodePath("InventorySlot2"), NodePath("InventorySlot3")] | ||||
| 
 | ||||
| [node name="InventorySlot" parent="." instance=ExtResource("1_ct3cn")] | ||||
| layout_mode = 2 | ||||
| 
 | ||||
| [node name="InventorySlot2" parent="." instance=ExtResource("1_ct3cn")] | ||||
| layout_mode = 2 | ||||
| 
 | ||||
| [node name="InventorySlot3" parent="." instance=ExtResource("1_ct3cn")] | ||||
| layout_mode = 2 | ||||
|  | @ -0,0 +1,6 @@ | |||
| namespace SupaLidlGame.UI; | ||||
| 
 | ||||
| public interface IModal | ||||
| { | ||||
|     public void HideModal(); | ||||
| } | ||||
|  | @ -1,7 +1,6 @@ | |||
| using Godot; | ||||
| using SupaLidlGame.Items; | ||||
| 
 | ||||
| namespace SupaLidlGame.UI; | ||||
| namespace SupaLidlGame.UI.Inventory; | ||||
| 
 | ||||
| public partial class Hotbar : GridContainer | ||||
| { | ||||
|  | @ -13,7 +12,7 @@ public partial class Hotbar : GridContainer | |||
|         Events.EventBus.Instance.PlayerInventoryUpdate += OnInventoryUpdate; | ||||
|     } | ||||
| 
 | ||||
|     public void OnInventoryUpdate(Inventory inventory) | ||||
|     public void OnInventoryUpdate(Items.Inventory inventory) | ||||
|     { | ||||
|         GD.Print($"UPDATE: {inventory.SelectedIndex} is selected index."); | ||||
|         for (int i = 0; i < 3; i++) | ||||
|  | @ -0,0 +1,30 @@ | |||
| [gd_scene load_steps=4 format=3 uid="uid://sfs8dpfitpdu"] | ||||
| 
 | ||||
| [ext_resource type="Script" path="res://UI/Inventory/Hotbar.cs" id="1_2sak2"] | ||||
| [ext_resource type="PackedScene" uid="uid://dmvu2hjyrwc1y" path="res://UI/Inventory/HotbarSlot.tscn" id="2_3axfe"] | ||||
| 
 | ||||
| [sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_6jbma"] | ||||
| 
 | ||||
| [node name="Hotbar" type="GridContainer" node_paths=PackedStringArray("_slots")] | ||||
| anchors_preset = 1 | ||||
| anchor_left = 1.0 | ||||
| anchor_right = 1.0 | ||||
| offset_left = -112.0 | ||||
| offset_bottom = 32.0 | ||||
| grow_horizontal = 0 | ||||
| theme_override_constants/h_separation = 8 | ||||
| columns = 3 | ||||
| script = ExtResource("1_2sak2") | ||||
| _slots = [NodePath("InventorySlot"), NodePath("InventorySlot2"), NodePath("InventorySlot3")] | ||||
| 
 | ||||
| [node name="InventorySlot" parent="." instance=ExtResource("2_3axfe")] | ||||
| layout_mode = 2 | ||||
| theme_override_styles/panel = SubResource("StyleBoxEmpty_6jbma") | ||||
| 
 | ||||
| [node name="InventorySlot2" parent="." instance=ExtResource("2_3axfe")] | ||||
| layout_mode = 2 | ||||
| theme_override_styles/panel = SubResource("StyleBoxEmpty_6jbma") | ||||
| 
 | ||||
| [node name="InventorySlot3" parent="." instance=ExtResource("2_3axfe")] | ||||
| layout_mode = 2 | ||||
| theme_override_styles/panel = SubResource("StyleBoxEmpty_6jbma") | ||||
|  | @ -0,0 +1,16 @@ | |||
| [gd_scene load_steps=4 format=3 uid="uid://dmvu2hjyrwc1y"] | ||||
| 
 | ||||
| [ext_resource type="PackedScene" uid="uid://ctad0dkoyw8ad" path="res://UI/Inventory/InventorySlot.tscn" id="1_fb62b"] | ||||
| [ext_resource type="Texture2D" uid="uid://dp7osg05ip5oo" path="res://Assets/Sprites/sword.png" id="2_aqbyo"] | ||||
| [ext_resource type="Texture2D" uid="uid://dc1gcsbhkchvg" path="res://Assets/Sprites/UI/hotbar-active.png" id="2_bcv71"] | ||||
| 
 | ||||
| [node name="InventorySlot" instance=ExtResource("1_fb62b")] | ||||
| 
 | ||||
| [node name="TextureRect" parent="." index="0"] | ||||
| texture = ExtResource("2_aqbyo") | ||||
| expand_mode = 3 | ||||
| 
 | ||||
| [node name="SelectedFrame" type="NinePatchRect" parent="." index="2"] | ||||
| visible = false | ||||
| layout_mode = 2 | ||||
| texture = ExtResource("2_bcv71") | ||||
|  | @ -0,0 +1,172 @@ | |||
| using Godot; | ||||
| using GodotUtilities; | ||||
| using SupaLidlGame.Items; | ||||
| 
 | ||||
| namespace SupaLidlGame.UI.Inventory; | ||||
| 
 | ||||
| public partial class InventoryGrid : GridContainer | ||||
| { | ||||
|     private SupaLidlGame.Items.IItemCollection _source; | ||||
| 
 | ||||
|     [Export] | ||||
|     private PackedScene _slotScene; | ||||
| 
 | ||||
|     public ButtonGroup ButtonGroup { get; private set; } | ||||
| 
 | ||||
|     public SupaLidlGame.Items.IItemCollection Source | ||||
|     { | ||||
|         get => _source; | ||||
|         set | ||||
|         { | ||||
|             GD.Print("Set InventoryGrid source"); | ||||
|             _source = value; | ||||
|             Redraw(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     [Signal] | ||||
|     public delegate void SlotFocusedEventHandler(InventorySlot slot); | ||||
| 
 | ||||
|     [Signal] | ||||
|     public delegate void SlotUnfocusedEventHandler(InventorySlot slot); | ||||
| 
 | ||||
|     [Signal] | ||||
|     public delegate void SlotSelectedEventHandler(InventorySlot slot); | ||||
| 
 | ||||
|     public InventoryGrid() | ||||
|     { | ||||
|         ButtonGroup = new(); | ||||
|     } | ||||
| 
 | ||||
|     public void Redraw() | ||||
|     { | ||||
|         GD.Print("Redrawing inventory grid..."); | ||||
| 
 | ||||
|         if (_source is null) | ||||
|         { | ||||
|             this.QueueFreeChildren(); | ||||
|         } | ||||
| 
 | ||||
|         var children = GetChildren(); | ||||
| 
 | ||||
|         for (int i = children.Count; i < _source.Capacity; i++) | ||||
|         { | ||||
|             AddInventorySlot(); | ||||
|         } | ||||
| 
 | ||||
|         for (int i = children.Count - 1; i >= _source.Capacity; i--) | ||||
|         { | ||||
|             children[i].QueueFree(); | ||||
|         } | ||||
| 
 | ||||
|         children = GetChildren(); | ||||
| 
 | ||||
|         // iterate through items and update the grid | ||||
|         using (var items = _source.GetItems().GetEnumerator()) | ||||
|         { | ||||
|             GD.Print("Updating items..."); | ||||
|             int i; | ||||
|             for (i = 0; items.MoveNext(); i++) | ||||
|             { | ||||
|                 InventorySlot slot = children[i] as InventorySlot; | ||||
| 
 | ||||
|                 ItemMetadata item = items.Current; | ||||
|                 slot.Item = item; | ||||
|             } | ||||
| 
 | ||||
|             // make remaining slots display empty | ||||
|             for (; i < _source.Capacity; i++) | ||||
|             { | ||||
|                 InventorySlot slot = children[i] as InventorySlot; | ||||
| 
 | ||||
|                 slot.Item = null; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         for (int i = 0; i < children.Count; i++) | ||||
|         { | ||||
|             var child = children[i] as Control; | ||||
|             if (i > 0) | ||||
|             { | ||||
|                 child.FocusPrevious = child.GetPathTo(children[i - 1]); | ||||
|             } | ||||
|             if (i < children.Count - 1) | ||||
|             { | ||||
|                 child.FocusNext = child.GetPathTo(children[i + 1]); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if (children.Count > 0) | ||||
|         { | ||||
|             var button = children[0] as Button; | ||||
|             button.ButtonPressed = true; | ||||
|             button.GrabFocus(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private InventorySlot AddInventorySlot() | ||||
|     { | ||||
|         var slot = _slotScene.Instantiate<InventorySlot>(); | ||||
|         AddChild(slot); | ||||
|         slot.ButtonGroup = ButtonGroup; | ||||
| 
 | ||||
|         void focusedHandler() | ||||
|         { | ||||
|             EmitSignal(SignalName.SlotFocused, slot); | ||||
|         } | ||||
| 
 | ||||
|         void unfocusedHandler() | ||||
|         { | ||||
|             EmitSignal(SignalName.SlotUnfocused, slot); | ||||
|         } | ||||
| 
 | ||||
|         void toggledHandler() | ||||
|         { | ||||
|             EmitSignal(SignalName.SlotSelected, slot); | ||||
|         } | ||||
| 
 | ||||
|         slot.Connect( | ||||
|             InventorySlot.SignalName.FocusEntered, | ||||
|             Callable.From(focusedHandler) | ||||
|         ); | ||||
| 
 | ||||
|         slot.Connect( | ||||
|             InventorySlot.SignalName.FocusExited, | ||||
|             Callable.From(unfocusedHandler) | ||||
|         ); | ||||
| 
 | ||||
|         slot.Connect( | ||||
|             InventorySlot.SignalName.MouseEntered, | ||||
|             Callable.From(focusedHandler) | ||||
|         ); | ||||
| 
 | ||||
|         slot.Connect( | ||||
|             InventorySlot.SignalName.MouseExited, | ||||
|             Callable.From(unfocusedHandler) | ||||
|         ); | ||||
| 
 | ||||
|         slot.Connect( | ||||
|             InventorySlot.SignalName.Pressed, | ||||
|             Callable.From(toggledHandler) | ||||
|         ); | ||||
| 
 | ||||
|         return slot; | ||||
|     } | ||||
| 
 | ||||
|     private void RemoveInventorySlot(InventorySlot slot) | ||||
|     { | ||||
|         RemoveChild(slot); | ||||
|     } | ||||
| 
 | ||||
|     public bool GrabSlotFocus() | ||||
|     { | ||||
|         var children = GetChildren(); | ||||
|         if (children.Count > 0) | ||||
|         { | ||||
|             var button = children[0] as Button; | ||||
|             button.GrabFocus(); | ||||
|             return true; | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,14 @@ | |||
| [gd_scene load_steps=3 format=3 uid="uid://chmokkxsy5vas"] | ||||
| 
 | ||||
| [ext_resource type="Script" path="res://UI/Inventory/InventoryGrid.cs" id="1_7128g"] | ||||
| [ext_resource type="PackedScene" uid="uid://ctad0dkoyw8ad" path="res://UI/Inventory/InventorySlot.tscn" id="2_b6vp8"] | ||||
| 
 | ||||
| [node name="InventoryGrid" type="GridContainer"] | ||||
| anchors_preset = 15 | ||||
| anchor_right = 1.0 | ||||
| anchor_bottom = 1.0 | ||||
| grow_horizontal = 2 | ||||
| grow_vertical = 2 | ||||
| columns = 5 | ||||
| script = ExtResource("1_7128g") | ||||
| _slotScene = ExtResource("2_b6vp8") | ||||
|  | @ -0,0 +1,95 @@ | |||
| using Godot; | ||||
| using GodotUtilities; | ||||
| using GodotUtilities.SourceGenerators; | ||||
| 
 | ||||
| namespace SupaLidlGame.UI.Inventory; | ||||
| 
 | ||||
| public partial class InventorySlot : Button | ||||
| { | ||||
|     private bool _isSelected = false; | ||||
| 
 | ||||
|     public bool IsSelected | ||||
|     { | ||||
|         get => _isSelected; | ||||
|         set | ||||
|         { | ||||
|             _isSelected = value; | ||||
|             if (_selectedFrame is not null) | ||||
|             { | ||||
|                 //_selectedFrame.Visible = _isSelected; | ||||
|                 //_frame.Visible = !_isSelected; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     [Export] | ||||
|     public bool UseFocusAsSelected { get; set; } = true; | ||||
| 
 | ||||
|     private TextureRect _textureRect; | ||||
| 
 | ||||
|     private NinePatchRect _frame; | ||||
| 
 | ||||
|     private NinePatchRect _selectedFrame; | ||||
| 
 | ||||
|     private static Texture2D _placeholderTexture; | ||||
| 
 | ||||
|     private Items.ItemMetadata _item; | ||||
| 
 | ||||
|     public Items.ItemMetadata Item | ||||
|     { | ||||
|         get => _item; | ||||
|         set | ||||
|         { | ||||
|             _item = value; | ||||
| 
 | ||||
|             if (_item is null) | ||||
|             { | ||||
|                 //_textureRect.Texture = null; | ||||
|                 Icon = null; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 //_textureRect.Texture = _item.Icon; | ||||
|                 Icon = _item.Icon; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     static InventorySlot() | ||||
|     { | ||||
|         _placeholderTexture = ResourceLoader.Load<Texture2D>( | ||||
|             "res://Assets/Sprites/UI/hotbar-inactive.png"); | ||||
|     } | ||||
| 
 | ||||
|     public override void _Ready() | ||||
|     { | ||||
|         _textureRect = GetNode<TextureRect>("TextureRect"); | ||||
|         _frame = GetNode<NinePatchRect>("Frame"); | ||||
|         _selectedFrame = GetNode<NinePatchRect>("SelectedFrame"); | ||||
| 
 | ||||
|         if (Item is null) | ||||
|         { | ||||
|             // do this to reset the icon | ||||
|             Item = null; | ||||
|         } | ||||
| 
 | ||||
|         if (UseFocusAsSelected) | ||||
|         { | ||||
|             void focusEntered() | ||||
|             { | ||||
|                 IsSelected = true; | ||||
|             } | ||||
| 
 | ||||
|             void focusExited() | ||||
|             { | ||||
|                 IsSelected = false; | ||||
|             } | ||||
| 
 | ||||
|             Connect(SignalName.FocusEntered, Callable.From(focusEntered)); | ||||
|             Connect(SignalName.FocusExited, Callable.From(focusExited)); | ||||
| 
 | ||||
|             Connect(SignalName.MouseEntered, Callable.From(focusEntered)); | ||||
|             Connect(SignalName.MouseExited, Callable.From(focusExited)); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,41 @@ | |||
| [gd_scene load_steps=8 format=3 uid="uid://ctad0dkoyw8ad"] | ||||
| 
 | ||||
| [ext_resource type="Script" path="res://UI/Inventory/InventorySlot.cs" id="1_fju5i"] | ||||
| [ext_resource type="Theme" uid="uid://cksjbu3vrup5" path="res://UI/Themes/supalidl.tres" id="1_wnu7u"] | ||||
| [ext_resource type="StyleBox" uid="uid://nvb4etac7ee2" path="res://UI/Themes/InventorySlotButtonFocus.tres" id="2_3wx0v"] | ||||
| [ext_resource type="Texture2D" uid="uid://dc1gcsbhkchvg" path="res://Assets/Sprites/UI/hotbar-active.png" id="2_m56j3"] | ||||
| [ext_resource type="StyleBox" uid="uid://pqtn0115bqtp" path="res://UI/Themes/InventorySlotButtonPressed.tres" id="3_46bp6"] | ||||
| [ext_resource type="StyleBox" uid="uid://cfqp0ycwvwx7c" path="res://UI/Themes/InventorySlotButtonNormal.tres" id="4_cc2jo"] | ||||
| 
 | ||||
| [sub_resource type="PlaceholderTexture2D" id="PlaceholderTexture2D_ajutj"] | ||||
| size = Vector2(24, 24) | ||||
| 
 | ||||
| [node name="InventorySlot" type="Button"] | ||||
| custom_minimum_size = Vector2(32, 32) | ||||
| theme = ExtResource("1_wnu7u") | ||||
| theme_type_variation = &"InventorySlotButton" | ||||
| theme_override_styles/focus = ExtResource("2_3wx0v") | ||||
| theme_override_styles/pressed = ExtResource("3_46bp6") | ||||
| theme_override_styles/normal = ExtResource("4_cc2jo") | ||||
| toggle_mode = true | ||||
| action_mode = 0 | ||||
| icon_alignment = 1 | ||||
| script = ExtResource("1_fju5i") | ||||
| 
 | ||||
| [node name="TextureRect" type="TextureRect" parent="."] | ||||
| visible = false | ||||
| layout_mode = 2 | ||||
| offset_right = 32.0 | ||||
| offset_bottom = 32.0 | ||||
| mouse_filter = 2 | ||||
| texture = SubResource("PlaceholderTexture2D_ajutj") | ||||
| expand_mode = 2 | ||||
| stretch_mode = 3 | ||||
| 
 | ||||
| [node name="Frame" type="NinePatchRect" parent="."] | ||||
| visible = false | ||||
| self_modulate = Color(1, 1, 1, 0.5) | ||||
| layout_mode = 2 | ||||
| offset_right = 32.0 | ||||
| offset_bottom = 32.0 | ||||
| texture = ExtResource("2_m56j3") | ||||
|  | @ -0,0 +1,44 @@ | |||
| using Godot; | ||||
| using SupaLidlGame.Items; | ||||
| 
 | ||||
| namespace SupaLidlGame.UI.Inventory; | ||||
| 
 | ||||
| public partial class ItemTooltip : Control | ||||
| { | ||||
|     private ItemMetadata _item; | ||||
| 
 | ||||
|     public ItemMetadata Item | ||||
|     { | ||||
|         get => _item; | ||||
|         set | ||||
|         { | ||||
|             _item = value; | ||||
|             if (IsNodeReady()) | ||||
|             { | ||||
|                 Render(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public override void _Ready() | ||||
|     { | ||||
|         Render(); | ||||
|     } | ||||
| 
 | ||||
|     public void Render() | ||||
|     { | ||||
|         if (_item is null) | ||||
|         { | ||||
|             GetNode<Label>("%ItemLabel").Text = "Nothing here"; | ||||
|             GetNode<TextureRect>("%ItemTexture").Texture = null; | ||||
|             GetNode<RichTextLabel>("%ItemDescription").Text = "We are ready for My Summer Car"; | ||||
|             GetNode<Label>("%Ingredients/Label").Text = "0 Shillings"; | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         GetNode<Label>("%ItemLabel").Text = _item.Name; | ||||
|         GetNode<TextureRect>("%ItemTexture").Texture = _item.Icon; | ||||
|         GetNode<RichTextLabel>("%ItemDescription").Text = _item.Description; | ||||
|         GetNode<Label>("%Ingredients/Label").Text = _item.BuyPrice + " Shillings"; | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,127 @@ | |||
| [gd_scene load_steps=12 format=3 uid="uid://bsheehtfcdwhh"] | ||||
| 
 | ||||
| [ext_resource type="Theme" uid="uid://cksjbu3vrup5" path="res://UI/Themes/supalidl.tres" id="1_elbte"] | ||||
| [ext_resource type="Script" path="res://UI/Inventory/ItemTooltip.cs" id="2_entlu"] | ||||
| [ext_resource type="Texture2D" uid="uid://b16461tjso0j7" path="res://Assets/Sprites/UI/hotbar-inactive.png" id="2_mcttf"] | ||||
| [ext_resource type="Texture2D" uid="uid://cd8ak8gu0104t" path="res://Assets/Sprites/UI/border.png" id="2_sxqyx"] | ||||
| [ext_resource type="Texture2D" uid="uid://dc1gcsbhkchvg" path="res://Assets/Sprites/UI/hotbar-active.png" id="2_vjw5e"] | ||||
| 
 | ||||
| [sub_resource type="Animation" id="Animation_mmh32"] | ||||
| resource_name = "wipe" | ||||
| 
 | ||||
| [sub_resource type="AnimationLibrary" id="AnimationLibrary_4bp3t"] | ||||
| _data = { | ||||
| "wipe": SubResource("Animation_mmh32") | ||||
| } | ||||
| 
 | ||||
| [sub_resource type="StyleBoxTexture" id="StyleBoxTexture_xhduc"] | ||||
| content_margin_left = 0.0 | ||||
| content_margin_top = 0.0 | ||||
| content_margin_right = 0.0 | ||||
| content_margin_bottom = 0.0 | ||||
| texture = ExtResource("2_mcttf") | ||||
| texture_margin_left = 16.0 | ||||
| texture_margin_top = 16.0 | ||||
| texture_margin_right = 16.0 | ||||
| texture_margin_bottom = 16.0 | ||||
| 
 | ||||
| [sub_resource type="StyleBoxTexture" id="StyleBoxTexture_rhjxi"] | ||||
| content_margin_left = 4.0 | ||||
| content_margin_top = 4.0 | ||||
| content_margin_right = 4.0 | ||||
| content_margin_bottom = 4.0 | ||||
| texture = ExtResource("2_vjw5e") | ||||
| texture_margin_left = 8.0 | ||||
| texture_margin_top = 8.0 | ||||
| texture_margin_right = 8.0 | ||||
| texture_margin_bottom = 8.0 | ||||
| 
 | ||||
| [sub_resource type="PlaceholderTexture2D" id="PlaceholderTexture2D_qnr6k"] | ||||
| size = Vector2(24, 24) | ||||
| 
 | ||||
| [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_idehr"] | ||||
| bg_color = Color(0.976471, 0.956863, 0.956863, 1) | ||||
| 
 | ||||
| [node name="ItemTooltip" type="BoxContainer"] | ||||
| offset_right = 92.0 | ||||
| offset_bottom = 161.0 | ||||
| theme = ExtResource("1_elbte") | ||||
| script = ExtResource("2_entlu") | ||||
| 
 | ||||
| [node name="AnimationPlayer" type="AnimationPlayer" parent="."] | ||||
| libraries = { | ||||
| "": SubResource("AnimationLibrary_4bp3t") | ||||
| } | ||||
| 
 | ||||
| [node name="VBoxContainer" type="VBoxContainer" parent="."] | ||||
| layout_mode = 2 | ||||
| size_flags_horizontal = 3 | ||||
| theme_override_constants/separation = 0 | ||||
| 
 | ||||
| [node name="PanelContainer" type="PanelContainer" parent="VBoxContainer"] | ||||
| layout_mode = 2 | ||||
| theme_override_styles/panel = SubResource("StyleBoxTexture_xhduc") | ||||
| 
 | ||||
| [node name="Margin" type="MarginContainer" parent="VBoxContainer/PanelContainer"] | ||||
| layout_mode = 2 | ||||
| 
 | ||||
| [node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/PanelContainer/Margin"] | ||||
| layout_mode = 2 | ||||
| alignment = 1 | ||||
| 
 | ||||
| [node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/PanelContainer/Margin/VBoxContainer"] | ||||
| layout_mode = 2 | ||||
| theme_override_constants/separation = 8 | ||||
| 
 | ||||
| [node name="PanelContainer" type="PanelContainer" parent="VBoxContainer/PanelContainer/Margin/VBoxContainer/HBoxContainer"] | ||||
| layout_mode = 2 | ||||
| size_flags_vertical = 0 | ||||
| theme_override_styles/panel = SubResource("StyleBoxTexture_rhjxi") | ||||
| 
 | ||||
| [node name="ItemTexture" type="TextureRect" parent="VBoxContainer/PanelContainer/Margin/VBoxContainer/HBoxContainer/PanelContainer"] | ||||
| unique_name_in_owner = true | ||||
| layout_mode = 2 | ||||
| texture = SubResource("PlaceholderTexture2D_qnr6k") | ||||
| stretch_mode = 2 | ||||
| 
 | ||||
| [node name="ItemLabel" type="Label" parent="VBoxContainer/PanelContainer/Margin/VBoxContainer/HBoxContainer"] | ||||
| unique_name_in_owner = true | ||||
| layout_mode = 2 | ||||
| size_flags_horizontal = 3 | ||||
| text = "Really long item name" | ||||
| autowrap_mode = 2 | ||||
| 
 | ||||
| [node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/PanelContainer/Margin/VBoxContainer/HBoxContainer"] | ||||
| visible = false | ||||
| layout_mode = 2 | ||||
| size_flags_horizontal = 3 | ||||
| 
 | ||||
| [node name="NinePatchRect" type="NinePatchRect" parent="VBoxContainer/PanelContainer/Margin/VBoxContainer/HBoxContainer/VBoxContainer"] | ||||
| layout_mode = 2 | ||||
| texture = ExtResource("2_sxqyx") | ||||
| region_rect = Rect2(0, 6, 48, 5) | ||||
| patch_margin_left = 16 | ||||
| patch_margin_top = 5 | ||||
| patch_margin_right = 16 | ||||
| 
 | ||||
| [node name="ItemDescription" type="RichTextLabel" parent="VBoxContainer/PanelContainer/Margin/VBoxContainer"] | ||||
| unique_name_in_owner = true | ||||
| layout_mode = 2 | ||||
| theme_override_colors/default_color = Color(0.937255, 0.854902, 0.854902, 1) | ||||
| text = "Speed, violence, momentum" | ||||
| fit_content = true | ||||
| 
 | ||||
| [node name="PanelContainer2" type="PanelContainer" parent="VBoxContainer"] | ||||
| layout_mode = 2 | ||||
| theme_override_styles/panel = SubResource("StyleBoxFlat_idehr") | ||||
| 
 | ||||
| [node name="Ingredients" type="MarginContainer" parent="VBoxContainer/PanelContainer2"] | ||||
| unique_name_in_owner = true | ||||
| layout_mode = 2 | ||||
| theme_override_constants/margin_top = 2 | ||||
| theme_override_constants/margin_bottom = 2 | ||||
| 
 | ||||
| [node name="Label" type="Label" parent="VBoxContainer/PanelContainer2/Ingredients"] | ||||
| layout_mode = 2 | ||||
| theme_override_colors/font_color = Color(0, 0, 0, 1) | ||||
| text = "250 Shillings" | ||||
|  | @ -0,0 +1,100 @@ | |||
| using Godot; | ||||
| using System.Collections.Generic; | ||||
| 
 | ||||
| namespace SupaLidlGame.UI.Inventory; | ||||
| 
 | ||||
| public partial class ShopMenu : Control, IModal | ||||
| { | ||||
|     private Items.IItemCollection<Items.ShopEntry> _source; | ||||
| 
 | ||||
|     public Items.IItemCollection<Items.ShopEntry> Source | ||||
|     { | ||||
|         get => _source; | ||||
|         set | ||||
|         { | ||||
|             _source = value; | ||||
|             _inventoryGrid.Source = value; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     [Export] | ||||
|     private InventoryGrid _inventoryGrid; | ||||
| 
 | ||||
|     private InventorySlot _selected; | ||||
| 
 | ||||
|     public void ShowModal() | ||||
|     { | ||||
|         Show(); | ||||
|         var animPlayer = GetNode<AnimationPlayer>("%AnimationPlayer"); | ||||
|         animPlayer.Play("open"); | ||||
|     } | ||||
| 
 | ||||
|     public void HideModal() | ||||
|     { | ||||
|         Hide(); | ||||
|         _source = null; | ||||
|     } | ||||
| 
 | ||||
|     public async void Close() | ||||
|     { | ||||
|         var animPlayer = GetNode<AnimationPlayer>("%AnimationPlayer"); | ||||
|         animPlayer.Play("close"); | ||||
|         await ToSignal(animPlayer, AnimationPlayer.SignalName.AnimationFinished); | ||||
|         HideModal(); | ||||
|     } | ||||
| 
 | ||||
|     public override void _Ready() | ||||
|     { | ||||
|         _inventoryGrid.SlotFocused += (InventorySlot slot) => | ||||
|         { | ||||
|             if (slot.Item is not null) | ||||
|             { | ||||
|                 SetTooltipItem(slot); | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         _inventoryGrid.SlotUnfocused += (InventorySlot slot) => | ||||
|         { | ||||
|             SetTooltipItem(_selected); | ||||
|         }; | ||||
| 
 | ||||
|         _inventoryGrid.SlotSelected += (InventorySlot slot) => | ||||
|         { | ||||
|             _selected = slot; | ||||
|             SetTooltipItem(slot); | ||||
|             GetNode<Button>("%BuyButton").GrabFocus(); | ||||
|         }; | ||||
| 
 | ||||
|         GetNode<Button>("%BuyButton").GuiInput += (InputEvent @event) => | ||||
|         { | ||||
|             if (@event.IsActionPressed("ui_cancel")) | ||||
|             { | ||||
|                 GetViewport().SetInputAsHandled(); | ||||
|                 _selected?.GrabFocus(); | ||||
|             } | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     private void SetTooltipItem(InventorySlot slot) | ||||
|     { | ||||
|         GetNode<ItemTooltip>("%ItemTooltip").Item = slot?.Item; | ||||
| 
 | ||||
|         if (slot == _selected) | ||||
|         { | ||||
|             GetNode<Button>("%BuyButton").Disabled = false; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             GetNode<Button>("%BuyButton").Disabled = true; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public override void _UnhandledInput(InputEvent @event) | ||||
|     { | ||||
|         if (@event.IsActionPressed("ui_cancel")) | ||||
|         { | ||||
|             GetViewport().SetInputAsHandled(); | ||||
|             Close(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,217 @@ | |||
| [gd_scene load_steps=15 format=3 uid="uid://cyggkyqosjk36"] | ||||
| 
 | ||||
| [ext_resource type="StyleBox" uid="uid://bqhotx2ogucye" path="res://UI/Themes/Panel.tres" id="1_2ffty"] | ||||
| [ext_resource type="Script" path="res://UI/Inventory/ShopMenu.cs" id="1_8c1y7"] | ||||
| [ext_resource type="Shader" path="res://Shaders/WipeXY.gdshader" id="2_jqery"] | ||||
| [ext_resource type="FontFile" uid="uid://cgwa8bjiyv534" path="res://Assets/Fonts/alagard.ttf" id="3_aj4jx"] | ||||
| [ext_resource type="PackedScene" uid="uid://bsheehtfcdwhh" path="res://UI/Inventory/ItemTooltip.tscn" id="4_n61n7"] | ||||
| [ext_resource type="PackedScene" uid="uid://chmokkxsy5vas" path="res://UI/Inventory/InventoryGrid.tscn" id="4_sl632"] | ||||
| [ext_resource type="PackedScene" uid="uid://baawkwo8aiwbu" path="res://UI/Inventory/ShopSlot.tscn" id="5_kfyl0"] | ||||
| [ext_resource type="Theme" uid="uid://cksjbu3vrup5" path="res://UI/Themes/supalidl.tres" id="7_rvp1r"] | ||||
| 
 | ||||
| [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_gm1xk"] | ||||
| bg_color = Color(0, 0, 0, 0.784314) | ||||
| border_width_left = 16 | ||||
| border_width_top = 16 | ||||
| border_width_right = 16 | ||||
| border_width_bottom = 16 | ||||
| border_color = Color(0.145098, 0.145098, 0.145098, 0) | ||||
| border_blend = true | ||||
| corner_radius_top_left = 16 | ||||
| corner_radius_top_right = 16 | ||||
| corner_radius_bottom_right = 16 | ||||
| corner_radius_bottom_left = 16 | ||||
| 
 | ||||
| [sub_resource type="ShaderMaterial" id="ShaderMaterial_2hdh3"] | ||||
| shader = ExtResource("2_jqery") | ||||
| shader_parameter/x_amount = 1.0 | ||||
| shader_parameter/y_amount = 0.5 | ||||
| 
 | ||||
| [sub_resource type="Animation" id="Animation_yj24f"] | ||||
| length = 0.001 | ||||
| tracks/0/type = "value" | ||||
| tracks/0/imported = false | ||||
| tracks/0/enabled = true | ||||
| tracks/0/path = NodePath(".:modulate") | ||||
| tracks/0/interp = 1 | ||||
| tracks/0/loop_wrap = true | ||||
| tracks/0/keys = { | ||||
| "times": PackedFloat32Array(0), | ||||
| "transitions": PackedFloat32Array(1), | ||||
| "update": 0, | ||||
| "values": [Color(1, 1, 1, 1)] | ||||
| } | ||||
| tracks/1/type = "value" | ||||
| tracks/1/imported = false | ||||
| tracks/1/enabled = true | ||||
| tracks/1/path = NodePath("PanelContainer:material:shader_parameter/y_amount") | ||||
| tracks/1/interp = 1 | ||||
| tracks/1/loop_wrap = true | ||||
| tracks/1/keys = { | ||||
| "times": PackedFloat32Array(0), | ||||
| "transitions": PackedFloat32Array(1), | ||||
| "update": 0, | ||||
| "values": [0.5] | ||||
| } | ||||
| tracks/2/type = "value" | ||||
| tracks/2/imported = false | ||||
| tracks/2/enabled = true | ||||
| tracks/2/path = NodePath("PanelContainer:material:shader_parameter/x_amount") | ||||
| tracks/2/interp = 1 | ||||
| tracks/2/loop_wrap = true | ||||
| tracks/2/keys = { | ||||
| "times": PackedFloat32Array(0), | ||||
| "transitions": PackedFloat32Array(1), | ||||
| "update": 0, | ||||
| "values": [1.0] | ||||
| } | ||||
| 
 | ||||
| [sub_resource type="Animation" id="Animation_tm2as"] | ||||
| resource_name = "close" | ||||
| length = 0.5 | ||||
| step = 0.05 | ||||
| tracks/0/type = "value" | ||||
| tracks/0/imported = false | ||||
| tracks/0/enabled = true | ||||
| tracks/0/path = NodePath(".:modulate") | ||||
| tracks/0/interp = 1 | ||||
| tracks/0/loop_wrap = true | ||||
| tracks/0/keys = { | ||||
| "times": PackedFloat32Array(0, 0.4), | ||||
| "transitions": PackedFloat32Array(1, 1), | ||||
| "update": 0, | ||||
| "values": [Color(1, 1, 1, 1), Color(1, 1, 1, 0)] | ||||
| } | ||||
| tracks/1/type = "value" | ||||
| tracks/1/imported = false | ||||
| tracks/1/enabled = true | ||||
| tracks/1/path = NodePath("PanelContainer:material:shader_parameter/y_amount") | ||||
| tracks/1/interp = 1 | ||||
| tracks/1/loop_wrap = true | ||||
| tracks/1/keys = { | ||||
| "times": PackedFloat32Array(0, 0.1), | ||||
| "transitions": PackedFloat32Array(1, 1), | ||||
| "update": 0, | ||||
| "values": [0.5, 1.0] | ||||
| } | ||||
| tracks/2/type = "value" | ||||
| tracks/2/imported = false | ||||
| tracks/2/enabled = true | ||||
| tracks/2/path = NodePath("PanelContainer:material:shader_parameter/x_amount") | ||||
| tracks/2/interp = 1 | ||||
| tracks/2/loop_wrap = true | ||||
| tracks/2/keys = { | ||||
| "times": PackedFloat32Array(0.05, 0.15), | ||||
| "transitions": PackedFloat32Array(1, 1), | ||||
| "update": 0, | ||||
| "values": [1.0, 0.5] | ||||
| } | ||||
| 
 | ||||
| [sub_resource type="Animation" id="Animation_fgj27"] | ||||
| resource_name = "open" | ||||
| length = 0.5 | ||||
| tracks/0/type = "value" | ||||
| tracks/0/imported = false | ||||
| tracks/0/enabled = true | ||||
| tracks/0/path = NodePath(".:modulate") | ||||
| tracks/0/interp = 1 | ||||
| tracks/0/loop_wrap = true | ||||
| tracks/0/keys = { | ||||
| "times": PackedFloat32Array(0, 0.3), | ||||
| "transitions": PackedFloat32Array(1, 1), | ||||
| "update": 0, | ||||
| "values": [Color(1, 1, 1, 0), Color(1, 1, 1, 1)] | ||||
| } | ||||
| tracks/1/type = "value" | ||||
| tracks/1/imported = false | ||||
| tracks/1/enabled = true | ||||
| tracks/1/path = NodePath("PanelContainer:material:shader_parameter/y_amount") | ||||
| tracks/1/interp = 1 | ||||
| tracks/1/loop_wrap = true | ||||
| tracks/1/keys = { | ||||
| "times": PackedFloat32Array(0, 0.5), | ||||
| "transitions": PackedFloat32Array(1, 1), | ||||
| "update": 0, | ||||
| "values": [1.0, 0.5] | ||||
| } | ||||
| tracks/2/type = "value" | ||||
| tracks/2/imported = false | ||||
| tracks/2/enabled = true | ||||
| tracks/2/path = NodePath("PanelContainer:material:shader_parameter/x_amount") | ||||
| tracks/2/interp = 1 | ||||
| tracks/2/loop_wrap = true | ||||
| tracks/2/keys = { | ||||
| "times": PackedFloat32Array(0, 0.3), | ||||
| "transitions": PackedFloat32Array(1, 1), | ||||
| "update": 0, | ||||
| "values": [0.5, 1.0] | ||||
| } | ||||
| 
 | ||||
| [sub_resource type="AnimationLibrary" id="AnimationLibrary_0glcp"] | ||||
| _data = { | ||||
| "RESET": SubResource("Animation_yj24f"), | ||||
| "close": SubResource("Animation_tm2as"), | ||||
| "open": SubResource("Animation_fgj27") | ||||
| } | ||||
| 
 | ||||
| [node name="Panel" type="PanelContainer" node_paths=PackedStringArray("_inventoryGrid")] | ||||
| anchors_preset = 6 | ||||
| anchor_left = 1.0 | ||||
| anchor_top = 0.5 | ||||
| anchor_right = 1.0 | ||||
| anchor_bottom = 0.5 | ||||
| offset_left = -236.0 | ||||
| offset_top = -175.5 | ||||
| offset_bottom = 175.5 | ||||
| grow_horizontal = 0 | ||||
| grow_vertical = 2 | ||||
| theme_override_styles/panel = SubResource("StyleBoxFlat_gm1xk") | ||||
| script = ExtResource("1_8c1y7") | ||||
| _inventoryGrid = NodePath("PanelContainer/VBoxContainer/ScrollContainer/InventoryGrid") | ||||
| 
 | ||||
| [node name="PanelContainer" type="PanelContainer" parent="."] | ||||
| material = SubResource("ShaderMaterial_2hdh3") | ||||
| layout_mode = 2 | ||||
| theme_override_styles/panel = ExtResource("1_2ffty") | ||||
| 
 | ||||
| [node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer"] | ||||
| layout_mode = 2 | ||||
| 
 | ||||
| [node name="HBoxContainer" type="HBoxContainer" parent="PanelContainer/VBoxContainer"] | ||||
| layout_mode = 2 | ||||
| 
 | ||||
| [node name="Label" type="Label" parent="PanelContainer/VBoxContainer/HBoxContainer"] | ||||
| layout_mode = 2 | ||||
| theme_override_fonts/font = ExtResource("3_aj4jx") | ||||
| text = "Snus Dealer" | ||||
| 
 | ||||
| [node name="ScrollContainer" type="ScrollContainer" parent="PanelContainer/VBoxContainer"] | ||||
| layout_mode = 2 | ||||
| size_flags_vertical = 3 | ||||
| follow_focus = true | ||||
| horizontal_scroll_mode = 0 | ||||
| 
 | ||||
| [node name="InventoryGrid" parent="PanelContainer/VBoxContainer/ScrollContainer" instance=ExtResource("4_sl632")] | ||||
| layout_mode = 2 | ||||
| size_flags_vertical = 3 | ||||
| _slotScene = ExtResource("5_kfyl0") | ||||
| 
 | ||||
| [node name="ItemTooltip" parent="PanelContainer/VBoxContainer" instance=ExtResource("4_n61n7")] | ||||
| unique_name_in_owner = true | ||||
| layout_mode = 2 | ||||
| 
 | ||||
| [node name="HBoxContainer2" type="HBoxContainer" parent="PanelContainer/VBoxContainer"] | ||||
| layout_mode = 2 | ||||
| 
 | ||||
| [node name="BuyButton" type="Button" parent="PanelContainer/VBoxContainer/HBoxContainer2"] | ||||
| unique_name_in_owner = true | ||||
| layout_mode = 2 | ||||
| size_flags_horizontal = 3 | ||||
| theme = ExtResource("7_rvp1r") | ||||
| text = "Buy" | ||||
| 
 | ||||
| [node name="AnimationPlayer" type="AnimationPlayer" parent="."] | ||||
| unique_name_in_owner = true | ||||
| libraries = { | ||||
| "": SubResource("AnimationLibrary_0glcp") | ||||
| } | ||||
|  | @ -0,0 +1,14 @@ | |||
| [gd_scene load_steps=3 format=3 uid="uid://baawkwo8aiwbu"] | ||||
| 
 | ||||
| [ext_resource type="PackedScene" uid="uid://ctad0dkoyw8ad" path="res://UI/Inventory/InventorySlot.tscn" id="1_tdkv2"] | ||||
| [ext_resource type="Texture2D" uid="uid://b16461tjso0j7" path="res://Assets/Sprites/UI/hotbar-inactive.png" id="2_af1qu"] | ||||
| 
 | ||||
| [node name="ShopSlot" instance=ExtResource("1_tdkv2")] | ||||
| 
 | ||||
| [node name="Frame" parent="." index="1"] | ||||
| texture = ExtResource("2_af1qu") | ||||
| 
 | ||||
| [node name="SelectedFrame" type="NinePatchRect" parent="." index="2"] | ||||
| visible = false | ||||
| layout_mode = 2 | ||||
| texture = ExtResource("2_af1qu") | ||||
|  | @ -1,74 +0,0 @@ | |||
| using Godot; | ||||
| using GodotUtilities; | ||||
| using GodotUtilities.SourceGenerators; | ||||
| 
 | ||||
| namespace SupaLidlGame.UI; | ||||
| 
 | ||||
| [Scene] | ||||
| public partial class InventorySlot : ColorRect | ||||
| { | ||||
|     [Node("TextureRect")] | ||||
|     private TextureRect _textureRect; | ||||
| 
 | ||||
|     [Node("Selected")] | ||||
|     private NinePatchRect _selected; | ||||
| 
 | ||||
|     [Node("Unselected")] | ||||
|     private NinePatchRect _unselected; | ||||
| 
 | ||||
|     private static Texture2D _placeholderTexture; | ||||
| 
 | ||||
|     private Items.ItemMetadata _item; | ||||
| 
 | ||||
|     public Items.ItemMetadata Item | ||||
|     { | ||||
|         get => _item; | ||||
|         set | ||||
|         { | ||||
|             _item = value; | ||||
| 
 | ||||
|             if (_item is null) | ||||
|             { | ||||
|                 //_textureRect.Texture = _placeholderTexture; | ||||
|                 _textureRect.Texture = null; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 _textureRect.Texture = _item.Icon; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private bool _isSelected = false; | ||||
| 
 | ||||
|     public bool IsSelected | ||||
|     { | ||||
|         get => _isSelected; | ||||
|         set | ||||
|         { | ||||
|             _isSelected = value; | ||||
|             _selected.Visible = _isSelected; | ||||
|             _unselected.Visible = !_isSelected; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     static InventorySlot() | ||||
|     { | ||||
|         _placeholderTexture = ResourceLoader.Load<Texture2D>( | ||||
|             "res://Assets/Sprites/UI/hotbar-inactive.png"); | ||||
|     } | ||||
| 
 | ||||
|     public override void _Notification(int what) | ||||
|     { | ||||
|         if (what == NotificationSceneInstantiated) | ||||
|         { | ||||
|             WireNodes(); | ||||
|         } | ||||
|         base._Notification(what); | ||||
|     } | ||||
| 
 | ||||
|     public override void _Ready() | ||||
|     { | ||||
| 
 | ||||
|     } | ||||
| } | ||||
|  | @ -1,39 +0,0 @@ | |||
| [gd_scene load_steps=4 format=3 uid="uid://ctad0dkoyw8ad"] | ||||
| 
 | ||||
| [ext_resource type="Script" path="res://UI/InventorySlot.cs" id="1_llonk"] | ||||
| [ext_resource type="Texture2D" uid="uid://bd81g8aivb2ql" path="res://Assets/Sprites/UI/menu-rect-no-bg-32.png" id="2_vvog5"] | ||||
| [ext_resource type="Texture2D" uid="uid://b16461tjso0j7" path="res://Assets/Sprites/UI/hotbar-inactive.png" id="3_jr23q"] | ||||
| 
 | ||||
| [node name="InventorySlot" type="ColorRect"] | ||||
| custom_minimum_size = Vector2(32, 32) | ||||
| color = Color(1, 1, 1, 0) | ||||
| script = ExtResource("1_llonk") | ||||
| 
 | ||||
| [node name="TextureRect" type="TextureRect" parent="."] | ||||
| layout_mode = 1 | ||||
| anchors_preset = 15 | ||||
| anchor_right = 1.0 | ||||
| anchor_bottom = 1.0 | ||||
| grow_horizontal = 2 | ||||
| grow_vertical = 2 | ||||
| stretch_mode = 3 | ||||
| 
 | ||||
| [node name="Selected" type="NinePatchRect" parent="."] | ||||
| visible = false | ||||
| layout_mode = 1 | ||||
| anchors_preset = 15 | ||||
| anchor_right = 1.0 | ||||
| anchor_bottom = 1.0 | ||||
| grow_horizontal = 2 | ||||
| grow_vertical = 2 | ||||
| texture = ExtResource("2_vvog5") | ||||
| 
 | ||||
| [node name="Unselected" type="NinePatchRect" parent="."] | ||||
| self_modulate = Color(1, 1, 1, 0.5) | ||||
| layout_mode = 1 | ||||
| anchors_preset = 15 | ||||
| anchor_right = 1.0 | ||||
| anchor_bottom = 1.0 | ||||
| grow_horizontal = 2 | ||||
| grow_vertical = 2 | ||||
| texture = ExtResource("3_jr23q") | ||||
|  | @ -151,6 +151,7 @@ anchor_right = 1.0 | |||
| anchor_bottom = 1.0 | ||||
| grow_horizontal = 2 | ||||
| grow_vertical = 2 | ||||
| mouse_filter = 2 | ||||
| script = ExtResource("1_g1mdf") | ||||
| 
 | ||||
| [node name="VBoxContainer" type="VBoxContainer" parent="."] | ||||
|  |  | |||
|  | @ -107,6 +107,7 @@ anchor_right = 1.0 | |||
| anchor_bottom = 1.0 | ||||
| grow_horizontal = 2 | ||||
| grow_vertical = 2 | ||||
| mouse_filter = 2 | ||||
| script = ExtResource("1_mx0ay") | ||||
| 
 | ||||
| [node name="ColorRect" type="ColorRect" parent="."] | ||||
|  | @ -117,6 +118,7 @@ anchor_right = 1.0 | |||
| anchor_bottom = 1.0 | ||||
| grow_horizontal = 2 | ||||
| grow_vertical = 2 | ||||
| mouse_filter = 2 | ||||
| color = Color(0, 0, 0, 1) | ||||
| 
 | ||||
| [node name="AnimationPlayer" type="AnimationPlayer" parent="."] | ||||
|  |  | |||
|  | @ -0,0 +1,14 @@ | |||
| [gd_resource type="StyleBoxTexture" load_steps=2 format=3 uid="uid://nvb4etac7ee2"] | ||||
| 
 | ||||
| [ext_resource type="Texture2D" uid="uid://b16461tjso0j7" path="res://Assets/Sprites/UI/hotbar-inactive.png" id="1_el8jk"] | ||||
| 
 | ||||
| [resource] | ||||
| content_margin_left = 4.0 | ||||
| content_margin_top = 4.0 | ||||
| content_margin_right = 4.0 | ||||
| content_margin_bottom = 4.0 | ||||
| texture = ExtResource("1_el8jk") | ||||
| texture_margin_left = 4.0 | ||||
| texture_margin_top = 4.0 | ||||
| texture_margin_right = 4.0 | ||||
| texture_margin_bottom = 4.0 | ||||
|  | @ -0,0 +1,15 @@ | |||
| [gd_resource type="StyleBoxTexture" load_steps=2 format=3 uid="uid://cfqp0ycwvwx7c"] | ||||
| 
 | ||||
| [ext_resource type="Texture2D" uid="uid://b16461tjso0j7" path="res://Assets/Sprites/UI/hotbar-inactive.png" id="1_r3gsw"] | ||||
| 
 | ||||
| [resource] | ||||
| content_margin_left = 4.0 | ||||
| content_margin_top = 4.0 | ||||
| content_margin_right = 4.0 | ||||
| content_margin_bottom = 4.0 | ||||
| texture = ExtResource("1_r3gsw") | ||||
| texture_margin_left = 4.0 | ||||
| texture_margin_top = 4.0 | ||||
| texture_margin_right = 4.0 | ||||
| texture_margin_bottom = 4.0 | ||||
| modulate_color = Color(1, 1, 1, 0.498039) | ||||
|  | @ -0,0 +1,15 @@ | |||
| [gd_resource type="StyleBoxTexture" load_steps=2 format=3 uid="uid://pqtn0115bqtp"] | ||||
| 
 | ||||
| [ext_resource type="Texture2D" uid="uid://b16461tjso0j7" path="res://Assets/Sprites/UI/hotbar-inactive.png" id="1_1ahpp"] | ||||
| 
 | ||||
| [resource] | ||||
| content_margin_left = 4.0 | ||||
| content_margin_top = 4.0 | ||||
| content_margin_right = 4.0 | ||||
| content_margin_bottom = 4.0 | ||||
| texture = ExtResource("1_1ahpp") | ||||
| texture_margin_left = 4.0 | ||||
| texture_margin_top = 4.0 | ||||
| texture_margin_right = 4.0 | ||||
| texture_margin_bottom = 4.0 | ||||
| modulate_color = Color(0.941176, 0.843137, 0.470588, 1) | ||||
|  | @ -0,0 +1,14 @@ | |||
| [gd_resource type="StyleBoxTexture" load_steps=2 format=3 uid="uid://bqhotx2ogucye"] | ||||
| 
 | ||||
| [ext_resource type="Texture2D" uid="uid://uhmowtsi3wfh" path="res://Assets/Sprites/UI/menu-rect-no-bg-white.png" id="1_cb20l"] | ||||
| 
 | ||||
| [resource] | ||||
| content_margin_left = 16.0 | ||||
| content_margin_top = 16.0 | ||||
| content_margin_right = 16.0 | ||||
| content_margin_bottom = 16.0 | ||||
| texture = ExtResource("1_cb20l") | ||||
| texture_margin_left = 32.0 | ||||
| texture_margin_top = 32.0 | ||||
| texture_margin_right = 32.0 | ||||
| texture_margin_bottom = 32.0 | ||||
|  | @ -1,10 +1,58 @@ | |||
| [gd_resource type="Theme" load_steps=2 format=3 uid="uid://cksjbu3vrup5"] | ||||
| [gd_resource type="Theme" load_steps=11 format=3 uid="uid://cksjbu3vrup5"] | ||||
| 
 | ||||
| [ext_resource type="FontFile" uid="uid://6bvgjbmqovau" path="res://Assets/Fonts/calamity/calamity.ttf" id="1_334fe"] | ||||
| [ext_resource type="StyleBox" uid="uid://nvb4etac7ee2" path="res://UI/Themes/InventorySlotButtonFocus.tres" id="2_3w5h1"] | ||||
| [ext_resource type="Texture2D" uid="uid://b16461tjso0j7" path="res://Assets/Sprites/UI/hotbar-inactive.png" id="2_6sv27"] | ||||
| [ext_resource type="StyleBox" uid="uid://bqhotx2ogucye" path="res://UI/Themes/Panel.tres" id="2_jlgx8"] | ||||
| [ext_resource type="StyleBox" uid="uid://cfqp0ycwvwx7c" path="res://UI/Themes/InventorySlotButtonNormal.tres" id="3_nuiio"] | ||||
| [ext_resource type="StyleBox" uid="uid://pqtn0115bqtp" path="res://UI/Themes/InventorySlotButtonPressed.tres" id="4_mllnb"] | ||||
| 
 | ||||
| [sub_resource type="StyleBoxTexture" id="StyleBoxTexture_wk5ww"] | ||||
| content_margin_left = 4.0 | ||||
| content_margin_top = 4.0 | ||||
| content_margin_right = 4.0 | ||||
| content_margin_bottom = 4.0 | ||||
| texture = ExtResource("2_6sv27") | ||||
| texture_margin_left = 16.0 | ||||
| texture_margin_top = 16.0 | ||||
| texture_margin_right = 16.0 | ||||
| texture_margin_bottom = 16.0 | ||||
| modulate_color = Color(0.105882, 0.0470588, 0.117647, 1) | ||||
| 
 | ||||
| [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_5w4ck"] | ||||
| bg_color = Color(0.937255, 0.854902, 0.854902, 1) | ||||
| 
 | ||||
| [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_2qibe"] | ||||
| content_margin_left = 2.0 | ||||
| content_margin_top = 2.0 | ||||
| content_margin_right = 2.0 | ||||
| content_margin_bottom = 2.0 | ||||
| bg_color = Color(0.976471, 0.956863, 0.956863, 1) | ||||
| 
 | ||||
| [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_iyd8w"] | ||||
| bg_color = Color(0.105882, 0.0470588, 0.117647, 1) | ||||
| 
 | ||||
| [resource] | ||||
| default_font = ExtResource("1_334fe") | ||||
| default_font_size = 15 | ||||
| /colors/background = Color(0.105882, 0.0470588, 0.117647, 1) | ||||
| /font_sizes/Normal = 16 | ||||
| /font_sizes/Normal = 15 | ||||
| /fonts/Calamity = ExtResource("1_334fe") | ||||
| Button/colors/font_color = Color(0.105882, 0.0470588, 0.117647, 1) | ||||
| Button/colors/font_focus_color = Color(0.105882, 0.0470588, 0.117647, 1) | ||||
| Button/colors/font_hover_color = Color(0.105882, 0.0470588, 0.117647, 1) | ||||
| Button/colors/font_pressed_color = Color(0.976471, 0.956863, 0.956863, 1) | ||||
| Button/styles/focus = SubResource("StyleBoxTexture_wk5ww") | ||||
| Button/styles/hover = SubResource("StyleBoxFlat_5w4ck") | ||||
| Button/styles/normal = SubResource("StyleBoxFlat_2qibe") | ||||
| Button/styles/pressed = SubResource("StyleBoxFlat_iyd8w") | ||||
| InventorySlotButton/base_type = &"Button" | ||||
| InventorySlotButton/styles/focus = ExtResource("2_3w5h1") | ||||
| InventorySlotButton/styles/hover = ExtResource("2_3w5h1") | ||||
| InventorySlotButton/styles/normal = ExtResource("3_nuiio") | ||||
| InventorySlotButton/styles/pressed = ExtResource("4_mllnb") | ||||
| MarginContainer/constants/margin_bottom = 8 | ||||
| MarginContainer/constants/margin_left = 8 | ||||
| MarginContainer/constants/margin_right = 8 | ||||
| MarginContainer/constants/margin_top = 8 | ||||
| PanelContainer/styles/panel = ExtResource("2_jlgx8") | ||||
|  |  | |||
|  | @ -1,4 +1,5 @@ | |||
| using Godot; | ||||
| using System.Collections.Generic; | ||||
| 
 | ||||
| namespace SupaLidlGame.UI; | ||||
| 
 | ||||
|  | @ -9,4 +10,17 @@ public partial class UIController : Control | |||
| 
 | ||||
|     [Export] | ||||
|     public BossBar BossBar { get; set; } | ||||
| 
 | ||||
|     private Stack<IModal> _modals; | ||||
| 
 | ||||
|     public override void _Ready() | ||||
|     { | ||||
|         Events.EventBus.Instance.EnterShop += (string path) => | ||||
|         { | ||||
|             var shop = ResourceLoader.Load<Items.Shop>(path); | ||||
|             var shopMenu = GetNode<Inventory.ShopMenu>("%ShopMenu"); | ||||
|             shopMenu.Source = shop; | ||||
|             shopMenu.ShowModal(); | ||||
|         }; | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -1,4 +1,5 @@ | |||
| using Godot; | ||||
| using SupaLidlGame.Extensions; | ||||
| 
 | ||||
| namespace SupaLidlGame.Utils; | ||||
| 
 | ||||
|  | @ -52,8 +53,7 @@ public partial class CharacterStats : Node | |||
|         else | ||||
|         { | ||||
|             _shouldDecayStagger = false; | ||||
|             _staggerDecayTimer.Stop(); | ||||
|             _staggerDecayTimer.Start(1); | ||||
|             _staggerDecayTimer.Restart(1); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,13 +2,20 @@ using Godot; | |||
| 
 | ||||
| namespace SupaLidlGame.Utils; | ||||
| 
 | ||||
| [System.Flags] | ||||
| public enum FactionName | ||||
| { | ||||
|     Player = 1, | ||||
|     Doc = 2, | ||||
| } | ||||
| 
 | ||||
| public interface IFaction | ||||
| { | ||||
|     /// <summary> | ||||
|     /// The faction index that this entity belongs to. | ||||
|     /// </summary> | ||||
|     [Export] | ||||
|     public ushort Faction { get; set; } | ||||
|     public FactionName Faction { get; set; } | ||||
| 
 | ||||
|     public bool AlignsWith(IFaction other) | ||||
|     { | ||||
|  |  | |||
|  | @ -206,6 +206,13 @@ namespace DialogueManagerRuntime | |||
| 
 | ||||
|     public partial class DialogueLine : RefCounted | ||||
|     { | ||||
|         private string id = ""; | ||||
|         public string Id | ||||
|         { | ||||
|             get => id; | ||||
|             set => id = value; | ||||
|         } | ||||
| 
 | ||||
|         private string type = "dialogue"; | ||||
|         public string Type | ||||
|         { | ||||
|  |  | |||
|  | @ -70,26 +70,28 @@ func _ready() -> void: | |||
| 
 | ||||
| 
 | ||||
| func _gui_input(event: InputEvent) -> void: | ||||
| 	# Handle shortcuts that come from the editor | ||||
| 	if event is InputEventKey and event.is_pressed(): | ||||
| 		match event.as_text(): | ||||
| 			"Ctrl+Equal", "Command+Equal": | ||||
| 				self.font_size += 1 | ||||
| 				get_viewport().set_input_as_handled() | ||||
| 			"Ctrl+Minus", "Command+Minus": | ||||
| 				self.font_size -= 1 | ||||
| 				get_viewport().set_input_as_handled() | ||||
| 			"Ctrl+0", "Command+0": | ||||
| 				self.font_size = theme_overrides.font_size | ||||
| 				get_viewport().set_input_as_handled() | ||||
| 			"Ctrl+K", "Command+K": | ||||
| 		var shortcut: String = Engine.get_meta("DialogueManagerPlugin").get_editor_shortcut(event) | ||||
| 		match shortcut: | ||||
| 			"toggle_comment": | ||||
| 				toggle_comment() | ||||
| 				get_viewport().set_input_as_handled() | ||||
| 			"Alt+Up": | ||||
| 			"move_up": | ||||
| 				move_line(-1) | ||||
| 				get_viewport().set_input_as_handled() | ||||
| 			"Alt+Down": | ||||
| 			"move_down": | ||||
| 				move_line(1) | ||||
| 				get_viewport().set_input_as_handled() | ||||
| 			"text_size_increase": | ||||
| 				self.font_size += 1 | ||||
| 				get_viewport().set_input_as_handled() | ||||
| 			"text_size_decrease": | ||||
| 				self.font_size -= 1 | ||||
| 				get_viewport().set_input_as_handled() | ||||
| 			"text_size_reset": | ||||
| 				self.font_size = theme_overrides.font_size | ||||
| 				get_viewport().set_input_as_handled() | ||||
| 
 | ||||
| 	elif event is InputEventMouse: | ||||
| 		match event.as_text(): | ||||
|  |  | |||
|  | @ -26,11 +26,11 @@ var regex_dict: RegEx = RegEx.create_from_string("^\\{((?>[^\\{\\}]+|(?R))*)\\}$ | |||
| var regex_kvdict: RegEx = RegEx.create_from_string("^\\s*(?<left>.*?)\\s*(?<colon>:|=)\\s*(?<right>[^\\/]+)$") | ||||
| var regex_commas: RegEx = RegEx.create_from_string("([^,]+)(?:\\s*,\\s*)?") | ||||
| var regex_assignment: RegEx = RegEx.create_from_string("^\\s*(?<var>[a-zA-Z_][a-zA-Z_0-9]*)(?:(?<attr>(?:\\.[a-zA-Z_][a-zA-Z_0-9]*)+)|(?:\\[(?<key>[^\\]]+)\\]))?\\s*(?<op>(?:\\/|\\*|-|\\+)?=)\\s*(?<val>.*)$") | ||||
| var regex_varname: RegEx = RegEx.create_from_string("^\\s*(?!true|false|and|or|not|in|null)(?<var>[a-zA-Z_][a-zA-Z_0-9]*)(?:(?<attr>(?:\\.[a-zA-Z_][a-zA-Z_0-9]*)+)|(?:\\[(?<key>[^\\]]+)\\]))?\\s*$") | ||||
| var regex_varname: RegEx = RegEx.create_from_string("^\\s*(?!true|false|and|or|&&|\\|\\|not|in|null)(?<var>[a-zA-Z_][a-zA-Z_0-9]*)(?:(?<attr>(?:\\.[a-zA-Z_][a-zA-Z_0-9]*)+)|(?:\\[(?<key>[^\\]]+)\\]))?\\s*$") | ||||
| var regex_keyword: RegEx = RegEx.create_from_string("^\\s*(true|false|null)\\s*$") | ||||
| var regex_function: RegEx = RegEx.create_from_string("^\\s*([a-zA-Z_][a-zA-Z_0-9]*\\s*)\\(") | ||||
| var regex_comparison: RegEx = RegEx.create_from_string("^(?<left>.*?)\\s*(?<op>==|>=|<=|<|>|!=)\\s*(?<right>.*)$") | ||||
| var regex_blogical: RegEx = RegEx.create_from_string("^(?<left>.*?)\\s+(?<op>and|or|in)\\s+(?<right>.*)$") | ||||
| var regex_blogical: RegEx = RegEx.create_from_string("^(?<left>.*?)\\s+(?<op>and|or|in|&&|\\|\\|)\\s+(?<right>.*)$") | ||||
| var regex_ulogical: RegEx = RegEx.create_from_string("^\\s*(?<op>not)\\s+(?<right>.*)$") | ||||
| var regex_paren: RegEx = RegEx.create_from_string("\\((?<paren>((?:[^\\(\\)]*)|(?:\\((?1)\\)))*?)\\)") | ||||
| 
 | ||||
|  | @ -186,9 +186,9 @@ func _get_dialogue_syntax_highlighting(start_index: int, text: String) -> Dictio | |||
| 	for goto_match in goto_matches: | ||||
| 		colors[start_index + goto_match.get_start(0)] = {"color": text_edit.theme_overrides.jumps_color} | ||||
| 		if "file" in goto_match.names: | ||||
| 			colors[start_index + goto_match.get_start("file")] = {"color": text_edit.theme_overrides.members_color} | ||||
| 			colors[start_index + goto_match.get_start("file")] = {"color": text_edit.theme_overrides.jumps_color} | ||||
| 			colors[start_index + goto_match.get_end("file")] = {"color": text_edit.theme_overrides.symbols_color} | ||||
| 		colors[start_index + goto_match.get_start("title")] = {"color": text_edit.theme_overrides.titles_color} | ||||
| 		colors[start_index + goto_match.get_start("title")] = {"color": text_edit.theme_overrides.jumps_color} | ||||
| 		colors[start_index + goto_match.get_end("title")] = {"color": text_edit.theme_overrides.jumps_color} | ||||
| 		colors[start_index + goto_match.get_end(0)] = {"color": text_edit.theme_overrides.text_color} | ||||
| 
 | ||||
|  |  | |||
|  | @ -43,7 +43,7 @@ var TOKEN_DEFINITIONS: Dictionary = { | |||
| 	DialogueConstants.TOKEN_DOT: RegEx.create_from_string("^\\."), | ||||
| 	DialogueConstants.TOKEN_STRING: RegEx.create_from_string("^(\".*?\"|\'.*?\')"), | ||||
| 	DialogueConstants.TOKEN_NOT: RegEx.create_from_string("^(not( |$)|!)"), | ||||
| 	DialogueConstants.TOKEN_AND_OR: RegEx.create_from_string("^(and|or)( |$)"), | ||||
| 	DialogueConstants.TOKEN_AND_OR: RegEx.create_from_string("^(and|or|&&|\\|\\|)( |$)"), | ||||
| 	DialogueConstants.TOKEN_VARIABLE: RegEx.create_from_string("^[a-zA-Z_][a-zA-Z_0-9]*"), | ||||
| 	DialogueConstants.TOKEN_COMMENT: RegEx.create_from_string("^#.*"), | ||||
| 	DialogueConstants.TOKEN_CONDITION: RegEx.create_from_string("^(if|elif|else)"), | ||||
|  | @ -230,6 +230,7 @@ func parse(text: String, path: String) -> Error: | |||
| 					line["character"] = first_child.character | ||||
| 					line["character_replacements"] = first_child.character_replacements | ||||
| 					line["text"] = first_child.text | ||||
| 					line["text_replacements"] = extract_dialogue_replacements(line.text, indent_size + 2) | ||||
| 					line["translation_key"] = first_child.translation_key | ||||
| 					parsed_lines[str(id) + ".2"] = first_child | ||||
| 					line["next_id"] = str(id) + ".2" | ||||
|  | @ -691,12 +692,7 @@ func get_line_after_line(id: int, indent_size: int, line: Dictionary) -> String: | |||
| 	var next_nonempty_line_id = get_next_nonempty_line_id(id) | ||||
| 	if next_nonempty_line_id != DialogueConstants.ID_NULL \ | ||||
| 		and indent_size <= get_indent(raw_lines[next_nonempty_line_id.to_int()]): | ||||
| 		# The next line is a title so we need the next nonempty line after that | ||||
| 		if is_title_line(raw_lines[next_nonempty_line_id.to_int()]): | ||||
| 			return get_next_nonempty_line_id(next_nonempty_line_id.to_int()) | ||||
| 		# Otherwise it's a normal line | ||||
| 		else: | ||||
| 			return next_nonempty_line_id | ||||
| 		return next_nonempty_line_id | ||||
| 	# Otherwise, we grab the ID from the parents next ID after children | ||||
| 	elif line.has("parent_id") and parsed_lines.has(line.parent_id): | ||||
| 		return parsed_lines[line.parent_id].next_id_after | ||||
|  | @ -927,8 +923,8 @@ func find_next_line_after_responses(line_number: int) -> String: | |||
| 			if get_indent(line) <= expected_indent: | ||||
| 				return str(line_number) | ||||
| 
 | ||||
| 	# EOF so must be end of conversation | ||||
| 	return DialogueConstants.ID_END_CONVERSATION | ||||
| 	# EOF so it's also the end of a block | ||||
| 	return DialogueConstants.ID_END | ||||
| 
 | ||||
| 
 | ||||
| ## Get the names of any autoloads in the project | ||||
|  | @ -1529,10 +1525,15 @@ func build_token_tree(tokens: Array[Dictionary], line_type: String, expected_clo | |||
| 			DialogueConstants.TOKEN_ASSIGNMENT, \ | ||||
| 			DialogueConstants.TOKEN_OPERATOR, \ | ||||
| 			DialogueConstants.TOKEN_AND_OR, \ | ||||
| 			DialogueConstants.TOKEN_VARIABLE: \ | ||||
| 			DialogueConstants.TOKEN_VARIABLE: | ||||
| 				var value = token.value.strip_edges() | ||||
| 				if value == "&&": | ||||
| 					value = "and" | ||||
| 				elif value == "||": | ||||
| 					value = "or" | ||||
| 				tree.append({ | ||||
| 					type = token.type, | ||||
| 					value = token.value.strip_edges() | ||||
| 					value = value | ||||
| 				}) | ||||
| 
 | ||||
| 			DialogueConstants.TOKEN_STRING: | ||||
|  |  | |||
											
												
													File diff suppressed because it is too large
													Load Diff
												
											
										
									
								|  | @ -1,7 +1,7 @@ | |||
| @icon("./assets/responses_menu.svg") | ||||
| 
 | ||||
| ## A VBoxContainer for dialogue responses provided by [b]Dialogue Manager[/b]. | ||||
| class_name DialogueResponsesMenu extends VBoxContainer | ||||
| ## A [Container] for dialogue responses provided by [b]Dialogue Manager[/b]. | ||||
| class_name DialogueResponsesMenu extends Container | ||||
| 
 | ||||
| 
 | ||||
| ## Emitted when a response is selected. | ||||
|  | @ -14,8 +14,10 @@ signal response_selected(response) | |||
| ## The action for accepting a response (is possibly overridden by parent dialogue balloon). | ||||
| @export var next_action: StringName = &"" | ||||
| 
 | ||||
| # The list of dialogue responses. | ||||
| ## The list of dialogue responses. | ||||
| var responses: Array = []: | ||||
| 	get: | ||||
| 		return responses | ||||
| 	set(value): | ||||
| 		responses = value | ||||
| 
 | ||||
|  | @ -64,11 +66,25 @@ func _ready() -> void: | |||
| 		response_template.hide() | ||||
| 
 | ||||
| 
 | ||||
| # This is deprecated. | ||||
| ## Get the selectable items in the menu. | ||||
| func get_menu_items() -> Array: | ||||
| 	var items: Array = [] | ||||
| 	for child in get_children(): | ||||
| 		if not child.visible: continue | ||||
| 		if "Disallowed" in child.name: continue | ||||
| 		items.append(child) | ||||
| 
 | ||||
| 	return items | ||||
| 
 | ||||
| 
 | ||||
| ## [b]DEPRECATED[/b]. Do not use. | ||||
| func set_responses(next_responses: Array) -> void: | ||||
| 	self.responses = next_responses | ||||
| 
 | ||||
| 
 | ||||
| #region Internal | ||||
| 
 | ||||
| 
 | ||||
| # Prepare the menu for keyboard and mouse navigation. | ||||
| func _configure_focus() -> void: | ||||
| 	var items = get_menu_items() | ||||
|  | @ -100,18 +116,9 @@ func _configure_focus() -> void: | |||
| 	items[0].grab_focus() | ||||
| 
 | ||||
| 
 | ||||
| ## Get the selectable items in the menu. | ||||
| func get_menu_items() -> Array: | ||||
| 	var items: Array = [] | ||||
| 	for child in get_children(): | ||||
| 		if not child.visible: continue | ||||
| 		if "Disallowed" in child.name: continue | ||||
| 		items.append(child) | ||||
| #endregion | ||||
| 
 | ||||
| 	return items | ||||
| 
 | ||||
| 
 | ||||
| ### Signals | ||||
| #region Signals | ||||
| 
 | ||||
| 
 | ||||
| func _on_response_mouse_entered(item: Control) -> void: | ||||
|  | @ -129,3 +136,6 @@ func _on_response_gui_input(event: InputEvent, item: Control, response) -> void: | |||
| 		response_selected.emit(response) | ||||
| 	elif event.is_action_pressed(&"ui_accept" if next_action.is_empty() else next_action) and item in get_menu_items(): | ||||
| 		response_selected.emit(response) | ||||
| 
 | ||||
| 
 | ||||
| #endregion | ||||
|  |  | |||
|  | @ -105,6 +105,21 @@ namespace DialogueManagerRuntime | |||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     public override async void _Notification(int what) | ||||
|     { | ||||
|       // Detect a change of locale and update the current dialogue line to show the new language | ||||
|       if (what == NotificationTranslationChanged) | ||||
|       { | ||||
|         float visibleRatio = dialogueLabel.VisibleRatio; | ||||
|         DialogueLine = await DialogueManager.GetNextDialogueLine(resource, DialogueLine.Id, temporaryGameStates); | ||||
|         if (visibleRatio < 1.0f) | ||||
|         { | ||||
|           dialogueLabel.Call("skip_typing"); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     public async void Start(Resource dialogueResource, string title, Array<Variant> extraGameStates = null) | ||||
|     { | ||||
|       temporaryGameStates = extraGameStates ?? new Array<Variant>(); | ||||
|  |  | |||
|  | @ -89,6 +89,15 @@ func _unhandled_input(_event: InputEvent) -> void: | |||
| 	get_viewport().set_input_as_handled() | ||||
| 
 | ||||
| 
 | ||||
| func _notification(what: int) -> void: | ||||
| 	# Detect a change of locale and update the current dialogue line to show the new language | ||||
| 	if what == NOTIFICATION_TRANSLATION_CHANGED: | ||||
| 		var visible_ratio = dialogue_label.visible_ratio | ||||
| 		self.dialogue_line = await resource.get_next_dialogue_line(dialogue_line.id) | ||||
| 		if visible_ratio < 1: | ||||
| 			dialogue_label.skip_typing() | ||||
| 
 | ||||
| 
 | ||||
| ## Start some dialogue | ||||
| func start(dialogue_resource: DialogueResource, title: String, extra_game_states: Array = []) -> void: | ||||
| 	temporary_game_states =  [self] + extra_game_states | ||||
|  | @ -102,7 +111,7 @@ func next(next_id: String) -> void: | |||
| 	self.dialogue_line = await resource.get_next_dialogue_line(next_id, temporary_game_states) | ||||
| 
 | ||||
| 
 | ||||
| ### Signals | ||||
| #region Signals | ||||
| 
 | ||||
| 
 | ||||
| func _on_mutated(_mutation: Dictionary) -> void: | ||||
|  | @ -139,3 +148,6 @@ func _on_balloon_gui_input(event: InputEvent) -> void: | |||
| 
 | ||||
| func _on_responses_menu_response_selected(response: DialogueResponse) -> void: | ||||
| 	next(response.next_id) | ||||
| 
 | ||||
| 
 | ||||
| #endregion | ||||
|  |  | |||
|  | @ -8,7 +8,7 @@ signal compiled_resource(resource: Resource) | |||
| const DialogueResource = preload("./dialogue_resource.gd") | ||||
| const DialogueManagerParseResult = preload("./components/parse_result.gd") | ||||
| 
 | ||||
| const compiler_version = 11 | ||||
| const compiler_version = 12 | ||||
| 
 | ||||
| 
 | ||||
| func _get_importer_name() -> String: | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ msgstr "" | |||
| "POT-Creation-Date: \n" | ||||
| "PO-Revision-Date: \n" | ||||
| "Last-Translator: \n" | ||||
| "Language-Team: penghao123456、憨憨羊の宇航鸽鸽\n" | ||||
| "Language-Team: penghao123456、憨憨羊の宇航鸽鸽、ABShinri\n" | ||||
| "Language: zh\n" | ||||
| "MIME-Version: 1.0\n" | ||||
| "Content-Type: text/plain; charset=UTF-8\n" | ||||
|  | @ -29,6 +29,9 @@ msgstr "清空历史记录" | |||
| msgid "save_all_files" | ||||
| msgstr "保存所有文件" | ||||
| 
 | ||||
| msgid "find_in_files" | ||||
| msgstr "在文件中查找" | ||||
| 
 | ||||
| msgid "test_dialogue" | ||||
| msgstr "测试对话" | ||||
| 
 | ||||
|  | @ -51,10 +54,10 @@ msgid "docs" | |||
| msgstr "文档" | ||||
| 
 | ||||
| msgid "insert.wave_bbcode" | ||||
| msgstr "BBCode [lb]wave[rb]" | ||||
| msgstr "波浪效果" | ||||
| 
 | ||||
| msgid "insert.shake_bbcode" | ||||
| msgstr "BBCode [lb]wave[rb]" | ||||
| msgstr "抖动效果" | ||||
| 
 | ||||
| msgid "insert.typing_pause" | ||||
| msgstr "输入间隔" | ||||
|  | @ -95,6 +98,9 @@ msgstr "结束对话" | |||
| msgid "generate_line_ids" | ||||
| msgstr "生成行 ID" | ||||
| 
 | ||||
| msgid "save_characters_to_csv" | ||||
| msgstr "保存角色到 CSV" | ||||
| 
 | ||||
| msgid "save_to_csv" | ||||
| msgstr "生成 CSV" | ||||
| 
 | ||||
|  | @ -134,6 +140,9 @@ msgstr "在 Godot 侧边栏中显示" | |||
| msgid "settings.revert_to_default_test_scene" | ||||
| msgstr "重置测试场景设定" | ||||
| 
 | ||||
| msgid "settings.default_balloon_hint" | ||||
| msgstr "设置调用 \"DialogueManager.show_balloon()\" 时使用的对话框" | ||||
| 
 | ||||
| msgid "settings.autoload" | ||||
| msgstr "Autoload" | ||||
| 
 | ||||
|  | @ -150,10 +159,10 @@ msgid "settings.missing_keys_hint" | |||
| msgstr "如果你使用静态键,这将会帮助你寻找未添加至翻译文件的键。" | ||||
| 
 | ||||
| msgid "settings.characters_translations" | ||||
| msgstr "在翻译文件中导出角色名。" | ||||
| msgstr "在翻译文件中导出角色名" | ||||
| 
 | ||||
| msgid "settings.wrap_long_lines" | ||||
| msgstr "自动折行" | ||||
| msgstr "文本编辑器自动换行" | ||||
| 
 | ||||
| msgid "settings.include_failed_responses" | ||||
| msgstr "在判断条件失败时仍显示回复选项" | ||||
|  | @ -176,9 +185,39 @@ msgstr "当一个 Autoload 在这里被勾选,他的所有成员会被映射 | |||
| msgid "settings.states_hint" | ||||
| msgstr "比如,当你开启对于“Foo”的映射时,你可以将“Foo.bar”简写成“bar”。" | ||||
| 
 | ||||
| msgid "settings.recompile_warning" | ||||
| msgstr "更改这些选项会强制重新编译所有的对话框,当你清楚在做什么的时候更改。" | ||||
| 
 | ||||
| msgid "settings.create_lines_for_responses_with_characters" | ||||
| msgstr "回复项带角色名时(- char: response),会自动生成为选择后的下一句对话" | ||||
| 
 | ||||
| msgid "settings.include_characters_in_translations" | ||||
| msgstr "导出 CSV 时包括角色名" | ||||
| 
 | ||||
| msgid "settings.include_notes_in_translations" | ||||
| msgstr "导出 CSV 时包括注释(## comments)" | ||||
| 
 | ||||
| msgid "settings.check_for_updates" | ||||
| msgstr "检查升级" | ||||
| 
 | ||||
| msgid "n_of_n" | ||||
| msgstr "第{index}个,共{total}个" | ||||
| 
 | ||||
| msgid "search.find" | ||||
| msgstr "查找:" | ||||
| 
 | ||||
| msgid "search.find_all" | ||||
| msgstr "查找全部..." | ||||
| 
 | ||||
| msgid "search.placeholder" | ||||
| msgstr "请输入查找的内容" | ||||
| 
 | ||||
| msgid "search.replace_placeholder" | ||||
| msgstr "请输入替换的内容" | ||||
| 
 | ||||
| msgid "search.replace_selected" | ||||
| msgstr "替换勾选" | ||||
| 
 | ||||
| msgid "search.previous" | ||||
| msgstr "查找上一个" | ||||
| 
 | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ msgstr "" | |||
| "POT-Creation-Date: \n" | ||||
| "PO-Revision-Date: \n" | ||||
| "Last-Translator: \n" | ||||
| "Language-Team: 憨憨羊の宇航鴿鴿\n" | ||||
| "Language-Team: 憨憨羊の宇航鴿鴿、ABShinri\n" | ||||
| "Language: zh_TW\n" | ||||
| "MIME-Version: 1.0\n" | ||||
| "Content-Type: text/plain; charset=UTF-8\n" | ||||
|  | @ -29,6 +29,9 @@ msgstr "清空歷史記錄" | |||
| msgid "save_all_files" | ||||
| msgstr "儲存所有檔案" | ||||
| 
 | ||||
| msgid "find_in_files" | ||||
| msgstr "在檔案中查找" | ||||
| 
 | ||||
| msgid "test_dialogue" | ||||
| msgstr "測試對話" | ||||
| 
 | ||||
|  | @ -51,10 +54,10 @@ msgid "docs" | |||
| msgstr "文檔" | ||||
| 
 | ||||
| msgid "insert.wave_bbcode" | ||||
| msgstr "BBCode [lb]wave[rb]" | ||||
| msgstr "波浪特效" | ||||
| 
 | ||||
| msgid "insert.shake_bbcode" | ||||
| msgstr "BBCode [lb]wave[rb]" | ||||
| msgstr "震動特效" | ||||
| 
 | ||||
| msgid "insert.typing_pause" | ||||
| msgstr "輸入間隔" | ||||
|  | @ -95,6 +98,9 @@ msgstr "結束對話" | |||
| msgid "generate_line_ids" | ||||
| msgstr "生成行 ID" | ||||
| 
 | ||||
| msgid "save_characters_to_csv" | ||||
| msgstr "保存角色到 CSV" | ||||
| 
 | ||||
| msgid "save_to_csv" | ||||
| msgstr "生成 CSV" | ||||
| 
 | ||||
|  | @ -134,6 +140,9 @@ msgstr "在 Godot 側邊欄中顯示" | |||
| msgid "settings.revert_to_default_test_scene" | ||||
| msgstr "重置測試場景設定" | ||||
| 
 | ||||
| msgid "settings.default_balloon_hint" | ||||
| msgstr "設置使用 \"DialogueManager.show_balloon()\" 时的对话框" | ||||
| 
 | ||||
| msgid "settings.autoload" | ||||
| msgstr "Autoload" | ||||
| 
 | ||||
|  | @ -176,9 +185,39 @@ msgstr "當一個 Autoload 在這裏被勾選,他的所有成員會被映射 | |||
| msgid "settings.states_hint" | ||||
| msgstr "比如,當你開啓對於“Foo”的映射時,你可以將“Foo.bar”簡寫成“bar”。" | ||||
| 
 | ||||
| msgid "settings.recompile_warning" | ||||
| msgstr "更改這些選項會強制重新編譯所有的對話框,當你清楚在做什麼的時候更改。" | ||||
| 
 | ||||
| msgid "settings.create_lines_for_responses_with_characters" | ||||
| msgstr "回覆項目帶角色名稱時(- char: response),會自動產生為選擇後的下一句對話" | ||||
| 
 | ||||
| msgid "settings.include_characters_in_translations" | ||||
| msgstr "匯出 CSV 時包含角色名" | ||||
| 
 | ||||
| msgid "settings.include_notes_in_translations" | ||||
| msgstr "匯出 CSV 時包括註解(## comments)" | ||||
| 
 | ||||
| msgid "settings.check_for_updates" | ||||
| msgstr "檢查升級" | ||||
| 
 | ||||
| msgid "n_of_n" | ||||
| msgstr "第{index}個,共{total}個" | ||||
| 
 | ||||
| msgid "search.find" | ||||
| msgstr "搜尋:" | ||||
| 
 | ||||
| msgid "search.find_all" | ||||
| msgstr "搜尋全部..." | ||||
| 
 | ||||
| msgid "search.placeholder" | ||||
| msgstr "請輸入搜尋的內容" | ||||
| 
 | ||||
| msgid "search.replace_placeholder" | ||||
| msgstr "請輸入替換的內容" | ||||
| 
 | ||||
| msgid "search.replace_selected" | ||||
| msgstr "替換勾選" | ||||
| 
 | ||||
| msgid "search.previous" | ||||
| msgstr "搜尋上一個" | ||||
| 
 | ||||
|  |  | |||
|  | @ -3,5 +3,5 @@ | |||
| name="Dialogue Manager" | ||||
| description="A simple but powerful branching dialogue system" | ||||
| author="Nathan Hoad" | ||||
| version="2.38.0" | ||||
| version="2.39.1" | ||||
| script="plugin.gd" | ||||
|  |  | |||
|  | @ -145,6 +145,80 @@ func _build() -> bool: | |||
| 	return true | ||||
| 
 | ||||
| 
 | ||||
| ## Get the shortcuts used by the plugin | ||||
| func get_editor_shortcuts() -> Dictionary: | ||||
| 	var shortcuts: Dictionary = { | ||||
| 		toggle_comment = [ | ||||
| 			_create_event("Ctrl+K"), | ||||
| 			_create_event("Ctrl+Slash") | ||||
| 		], | ||||
| 		move_up = [ | ||||
| 			_create_event("Alt+Up") | ||||
| 		], | ||||
| 		move_down = [ | ||||
| 			_create_event("Alt+Down") | ||||
| 		], | ||||
| 		save = [ | ||||
| 			_create_event("Ctrl+Alt+S") | ||||
| 		], | ||||
| 		close_file = [ | ||||
| 			_create_event("Ctrl+W") | ||||
| 		], | ||||
| 		find_in_files = [ | ||||
| 			_create_event("Ctrl+Shift+F") | ||||
| 		], | ||||
| 
 | ||||
| 		run_test_scene = [ | ||||
| 			_create_event("Ctrl+F5") | ||||
| 		], | ||||
| 		text_size_increase = [ | ||||
| 			_create_event("Ctrl+Equal") | ||||
| 		], | ||||
| 		text_size_decrease = [ | ||||
| 			_create_event("Ctrl+Minus") | ||||
| 		], | ||||
| 		text_size_reset = [ | ||||
| 			_create_event("Ctrl+0") | ||||
| 		] | ||||
| 	} | ||||
| 
 | ||||
| 	var paths = get_editor_interface().get_editor_paths() | ||||
| 	var settings = load(paths.get_config_dir() + "/editor_settings-4.tres") | ||||
| 
 | ||||
| 	if not settings: return shortcuts | ||||
| 
 | ||||
| 	for s in settings.get("shortcuts"): | ||||
| 		for key in shortcuts: | ||||
| 			if s.name == "script_text_editor/%s" % key or s.name == "script_editor/%s" % key: | ||||
| 				shortcuts[key] = [] | ||||
| 				for event in s.shortcuts: | ||||
| 					if event is InputEventKey: | ||||
| 						shortcuts[key].append(event) | ||||
| 
 | ||||
| 	return shortcuts | ||||
| 
 | ||||
| 
 | ||||
| func _create_event(string: String) -> InputEventKey: | ||||
| 	var event: InputEventKey = InputEventKey.new() | ||||
| 	var bits = string.split("+") | ||||
| 	event.keycode = OS.find_keycode_from_string(bits[bits.size() - 1]) | ||||
| 	event.shift_pressed = bits.has("Shift") | ||||
| 	event.alt_pressed = bits.has("Alt") | ||||
| 	if bits.has("Ctrl") or bits.has("Command"): | ||||
| 		event.command_or_control_autoremap = true | ||||
| 	return event | ||||
| 
 | ||||
| 
 | ||||
| ## Get the editor shortcut that matches an event | ||||
| func get_editor_shortcut(event: InputEventKey) -> String: | ||||
| 	var shortcuts: Dictionary = get_editor_shortcuts() | ||||
| 	for key in shortcuts: | ||||
| 		for shortcut in shortcuts.get(key, []): | ||||
| 			if event.is_match(shortcut): | ||||
| 				return key | ||||
| 	return "" | ||||
| 
 | ||||
| 
 | ||||
| ## Get the current version | ||||
| func get_version() -> String: | ||||
| 	var config: ConfigFile = ConfigFile.new() | ||||
|  |  | |||
|  | @ -176,9 +176,5 @@ static func has_dotnet_solution() -> bool: | |||
| 		var has_dotnet_solution: bool = FileAccess.file_exists("res://%s/%s.sln" % [directory, file_name]) | ||||
| 		set_user_value("has_dotnet_solution", has_dotnet_solution) | ||||
| 		return has_dotnet_solution | ||||
| 	else: | ||||
| 		var plugin_path: String = new().get_script().resource_path.get_base_dir() | ||||
| 		if not ResourceLoader.exists(plugin_path + "/DialogueManager.cs"): return false | ||||
| 		if load(plugin_path + "/DialogueManager.cs") == null: return false | ||||
| 
 | ||||
| 	return true | ||||
| 	return false | ||||
|  |  | |||
|  | @ -11,7 +11,8 @@ const SUPPORTED_BUILTIN_TYPES = [ | |||
| 	TYPE_DICTIONARY, | ||||
| 	TYPE_QUATERNION, | ||||
| 	TYPE_COLOR, | ||||
| 	TYPE_SIGNAL | ||||
| 	TYPE_SIGNAL, | ||||
| 	TYPE_CALLABLE | ||||
| ] | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -181,19 +181,20 @@ func _unhandled_input(event: InputEvent) -> void: | |||
| 	if not visible: return | ||||
| 
 | ||||
| 	if event is InputEventKey and event.is_pressed(): | ||||
| 		match event.as_text(): | ||||
| 			"Ctrl+Alt+S", "Command+Alt+S": | ||||
| 				get_viewport().set_input_as_handled() | ||||
| 				save_file(current_file_path) | ||||
| 			"Ctrl+W", "Command+W": | ||||
| 		var shortcut: String = Engine.get_meta("DialogueManagerPlugin").get_editor_shortcut(event) | ||||
| 		match shortcut: | ||||
| 			"close_file": | ||||
| 				get_viewport().set_input_as_handled() | ||||
| 				close_file(current_file_path) | ||||
| 			"Ctrl+F5", "Command+F5": | ||||
| 			"save": | ||||
| 				get_viewport().set_input_as_handled() | ||||
| 				_on_test_button_pressed() | ||||
| 			"Ctrl+Shift+F", "Command+Shift+F": | ||||
| 				save_file(current_file_path) | ||||
| 			"find_in_files": | ||||
| 				get_viewport().set_input_as_handled() | ||||
| 				_on_find_in_files_button_pressed() | ||||
| 			"run_test_scene": | ||||
| 				get_viewport().set_input_as_handled() | ||||
| 				_on_test_button_pressed() | ||||
| 
 | ||||
| 
 | ||||
| func apply_changes() -> void: | ||||
|  | @ -1044,9 +1045,11 @@ func _on_files_list_file_middle_clicked(path: String): | |||
| func _on_files_popup_menu_about_to_popup() -> void: | ||||
| 	files_popup_menu.clear() | ||||
| 
 | ||||
| 	files_popup_menu.add_item(DialogueConstants.translate(&"buffer.save"), ITEM_SAVE, KEY_MASK_CTRL | KEY_MASK_ALT | KEY_S) | ||||
| 	var shortcuts: Dictionary = Engine.get_meta("DialogueManagerPlugin").get_editor_shortcuts() | ||||
| 
 | ||||
| 	files_popup_menu.add_item(DialogueConstants.translate(&"buffer.save"), ITEM_SAVE, OS.find_keycode_from_string(shortcuts.get("save")[0].as_text_keycode())) | ||||
| 	files_popup_menu.add_item(DialogueConstants.translate(&"buffer.save_as"), ITEM_SAVE_AS) | ||||
| 	files_popup_menu.add_item(DialogueConstants.translate(&"buffer.close"), ITEM_CLOSE, KEY_MASK_CTRL | KEY_W) | ||||
| 	files_popup_menu.add_item(DialogueConstants.translate(&"buffer.close"), ITEM_CLOSE, OS.find_keycode_from_string(shortcuts.get("close_file")[0].as_text_keycode())) | ||||
| 	files_popup_menu.add_item(DialogueConstants.translate(&"buffer.close_all"), ITEM_CLOSE_ALL) | ||||
| 	files_popup_menu.add_item(DialogueConstants.translate(&"buffer.close_other_files"), ITEM_CLOSE_OTHERS) | ||||
| 	files_popup_menu.add_separator() | ||||
|  |  | |||
|  | @ -1 +1 @@ | |||
| Subproject commit 36625398e3ad45306521926c5190141a7ac925b0 | ||||
| Subproject commit b265f5cebab07f3af55c92f642b458db9c973a7c | ||||
|  | @ -17,17 +17,16 @@ config/icon="res://icon.svg" | |||
| 
 | ||||
| [autoload] | ||||
| 
 | ||||
| DialogueManager="*res://addons/dialogue_manager/dialogue_manager.gd" | ||||
| GlobalState="*res://State/Global/GlobalState.cs" | ||||
| EventBus="*res://Events/EventBus.cs" | ||||
| BaseUI="*res://UI/Base.tscn" | ||||
| World="*res://Scenes/Level.tscn" | ||||
| AudioManager="*res://Audio/AudioManager.cs" | ||||
| DebugUi="*res://UI/Debug/DebugUI.tscn" | ||||
| DialogueManager="*res://addons/dialogue_manager/dialogue_manager.gd" | ||||
| 
 | ||||
| [dialogue_manager] | ||||
| 
 | ||||
| general/states=["GlobalState"] | ||||
| general/states=["GlobalState", "World", "EventBus"] | ||||
| general/wrap_lines=true | ||||
| 
 | ||||
| [display] | ||||
|  | @ -201,7 +200,7 @@ cast={ | |||
| 
 | ||||
| [internationalization] | ||||
| 
 | ||||
| locale/translations_pot_files=PackedStringArray("res://Assets/Dialog/doc.dialogue", "res://Assets/Dialogue/doc.dialogue", "res://Assets/Dialogue/spellbook.dialogue", "res://Assets/Dialogue/books.dialogue", "res://Assets/Dialogue/clone-machine.dialogue") | ||||
| locale/translations_pot_files=PackedStringArray("res://Assets/Dialogue/doc.dialogue", "res://Assets/Dialogue/books.dialogue", "res://Assets/Dialogue/clone-machine.dialogue", "res://Dialogue/snus-dealer.dialogue") | ||||
| 
 | ||||
| [layer_names] | ||||
| 
 | ||||
|  | @ -215,6 +214,10 @@ locale/translations_pot_files=PackedStringArray("res://Assets/Dialog/doc.dialogu | |||
| 2d_physics/layer_6="Interaction Receiver" | ||||
| 2d_physics/layer_7="Interaction Trigger" | ||||
| 
 | ||||
| [navigation] | ||||
| 
 | ||||
| 2d/default_cell_size=16.0 | ||||
| 
 | ||||
| [physics] | ||||
| 
 | ||||
| 2d/default_gravity=0.0 | ||||
|  | @ -222,7 +225,5 @@ locale/translations_pot_files=PackedStringArray("res://Assets/Dialog/doc.dialogu | |||
| [rendering] | ||||
| 
 | ||||
| textures/canvas_textures/default_texture_filter=0 | ||||
| renderer/rendering_method="gl_compatibility" | ||||
| renderer/rendering_method.mobile="gl_compatibility" | ||||
| environment/defaults/default_clear_color=Color(0.301961, 0.301961, 0.301961, 1) | ||||
| viewport/hdr_2d=true | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue