item-info wip
							parent
							
								
									ae5767e1fd
								
							
						
					
					
						commit
						f0a33b0c77
					
				|  | @ -159,7 +159,8 @@ namespace SupaLidlGame.Characters | |||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             if (Inventory.SelectedItem is Weapon weapon) | ||||
|             // TODO: support for offhand items | ||||
|             if (Inventory.PrimaryItem is Weapon weapon) | ||||
|             { | ||||
|                 weapon.Use(); | ||||
|             } | ||||
|  |  | |||
|  | @ -7,7 +7,8 @@ namespace SupaLidlGame.Characters | |||
|     { | ||||
|         public override void _Ready() | ||||
|         { | ||||
|             Inventory.SelectedItem = Inventory.GetNode<Items.Item>("Sword"); | ||||
|             //Inventory.SelectedItem = Inventory.GetNode<Items.Item>("Sword"); | ||||
|             Inventory.SelectFirstItem(); | ||||
|             base._Ready(); | ||||
|         } | ||||
| 
 | ||||
|  |  | |||
|  | @ -165,7 +165,6 @@ y_sort_enabled = true | |||
| script = ExtResource("7_43gq8") | ||||
| 
 | ||||
| [node name="Sword" parent="Inventory" instance=ExtResource("8_s3c8r")] | ||||
| Knockback = 40.0 | ||||
| 
 | ||||
| [node name="FlashAnimation" type="AnimationPlayer" parent="."] | ||||
| libraries = { | ||||
|  |  | |||
|  | @ -288,7 +288,8 @@ namespace SupaLidlGame.Characters | |||
| 
 | ||||
|                 if (Target.LengthSquared() < 1024) | ||||
|                 { | ||||
|                     if (Inventory.SelectedItem is Weapon weapon) | ||||
|                     // TODO: offhand items | ||||
|                     if (Inventory.PrimaryItem is Weapon weapon) | ||||
|                     { | ||||
|                         UseCurrentItem(); | ||||
|                     } | ||||
|  |  | |||
|  | @ -61,5 +61,19 @@ namespace SupaLidlGame.Characters | |||
|             GD.Print("died"); | ||||
|             //base.Die(); | ||||
|         } | ||||
| 
 | ||||
|         public bool IsUsingAnyWeapon() | ||||
|         { | ||||
|             bool checkItem(Items.Item item) | ||||
|             { | ||||
|                 if (item is Items.Weapon weapon) | ||||
|                 { | ||||
|                     return weapon.IsUsing; | ||||
|                 } | ||||
|                 return false; | ||||
|             } | ||||
|             return checkItem(Inventory.PrimaryItem) || | ||||
|                 checkItem(Inventory.OffhandItem); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -8,7 +8,6 @@ | |||
| [ext_resource type="PackedScene" uid="uid://cl56eadpklnbo" path="res://Utils/PlayerCamera.tscn" id="4_ym125"] | ||||
| [ext_resource type="Script" path="res://Characters/States/PlayerMoveState.cs" id="5_tx5rw"] | ||||
| [ext_resource type="Script" path="res://Characters/States/PlayerRollState.cs" id="6_6bgrj"] | ||||
| [ext_resource type="PackedScene" uid="uid://d72ehtv1ks0e" path="res://Items/Weapons/Sword.tscn" id="7_4rxuv"] | ||||
| [ext_resource type="Script" path="res://Items/Inventory.cs" id="7_xyenu"] | ||||
| [ext_resource type="PackedScene" uid="uid://cjgxyhgcyvsv7" path="res://BoundingBoxes/Hurtbox.tscn" id="9_avyu4"] | ||||
| [ext_resource type="AudioStream" uid="uid://njun3e6v4854" path="res://Assets/Sounds/hurt.wav" id="12_h0x0g"] | ||||
|  | @ -86,6 +85,69 @@ size = Vector2(16, 8) | |||
| [sub_resource type="LabelSettings" id="LabelSettings_q5h1n"] | ||||
| font_size = 24 | ||||
| 
 | ||||
| [sub_resource type="CSharpScript" id="CSharpScript_rcje3"] | ||||
| script/source = "using Godot; | ||||
| using SupaLidlGame.Characters; | ||||
| 
 | ||||
| namespace SupaLidlGame.Items | ||||
| { | ||||
|     public partial class ItemInfo : Resource | ||||
|     { | ||||
|         [Export] | ||||
|         public string ItemName { get; set; } = \"Item Name\"; | ||||
| 
 | ||||
|         [Export] | ||||
|         public string Description { get; set; } = \"Item description.\"; | ||||
| 
 | ||||
|         [Export] | ||||
|         public bool CanStack { get; set; } = false; | ||||
| 
 | ||||
|         [Export] | ||||
|         public int Count { get; set; } = 1; | ||||
| 
 | ||||
|         [Export] | ||||
|         public bool IsOneHanded { get; set; } = false; | ||||
| 
 | ||||
|         [Export] | ||||
|         public Character CharacterOwner { get; set; } = null; | ||||
| 
 | ||||
|         [Export] | ||||
|         public Texture2D Texture { get; set; } | ||||
| 
 | ||||
|         [Export] | ||||
|         public string ScenePath { get; set; } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Determines if this item can directly stack with other items | ||||
|         /// </summary> | ||||
|         public virtual bool StacksWith(ItemInfo itemInfo) | ||||
|         { | ||||
|             if (!CanStack) | ||||
|             { | ||||
|                 return false; | ||||
|             } | ||||
| 
 | ||||
|             if (ItemName != itemInfo.ItemName) | ||||
|             { | ||||
|                 return false; | ||||
|             } | ||||
| 
 | ||||
|             // several more conditions may be added soon | ||||
| 
 | ||||
|             return true; | ||||
|         } | ||||
| 
 | ||||
|         public Item InstantiateItem(string name = \"Primary\") | ||||
|         { | ||||
|             var scene = ResourceLoader.Load<PackedScene>(ScenePath); | ||||
|             var instance = scene.Instantiate<Item>(); | ||||
|             instance.Name = name; | ||||
|             return instance; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| " | ||||
| 
 | ||||
| [sub_resource type="RectangleShape2D" id="RectangleShape2D_cjk6b"] | ||||
| size = Vector2(16, 24) | ||||
| 
 | ||||
|  | @ -193,8 +255,7 @@ horizontal_alignment = 1 | |||
| y_sort_enabled = true | ||||
| position = Vector2(0, 2) | ||||
| script = ExtResource("7_xyenu") | ||||
| 
 | ||||
| [node name="Sword" parent="Inventory" instance=ExtResource("7_4rxuv")] | ||||
| Items = Array[Resource]([SubResource("CSharpScript_rcje3")]) | ||||
| 
 | ||||
| [node name="Hurtbox" parent="." instance=ExtResource("9_avyu4")] | ||||
| Faction = 1 | ||||
|  |  | |||
|  | @ -9,11 +9,11 @@ namespace SupaLidlGame.Characters.State | |||
| 
 | ||||
|         public override CharacterState Enter(CharacterState previousState) | ||||
|         { | ||||
|             if (Character.Inventory.SelectedItem is Weapon weapon) | ||||
|             if (Character.Inventory.PrimaryItem is Weapon weapon) | ||||
|             { | ||||
|                 _attackTime = weapon.UseTime; | ||||
|                 _attackTime = weapon.Info.UseTime; | ||||
|                 weapon.Visible = true; | ||||
|                 Character.Inventory.SelectedItem.Use(); | ||||
|                 Character.Inventory.PrimaryItem.Use(); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|  | @ -24,12 +24,12 @@ namespace SupaLidlGame.Characters.State | |||
| 
 | ||||
|         public override void Exit(CharacterState nextState) | ||||
|         { | ||||
|             if (Character.Inventory.SelectedItem is null) | ||||
|             if (Character.Inventory.PrimaryItem is null) | ||||
|             { | ||||
| 
 | ||||
|             } | ||||
|             Character.Inventory.SelectedItem.Deuse(); | ||||
|             if (Character.Inventory.SelectedItem is Weapon weapon) | ||||
|             Character.Inventory.PrimaryItem.Deuse(); | ||||
|             if (Character.Inventory.PrimaryItem is Weapon weapon) | ||||
|             { | ||||
|                 //weapon.Visible = false; | ||||
|             } | ||||
|  |  | |||
|  | @ -29,14 +29,7 @@ namespace SupaLidlGame.Characters.State | |||
|         { | ||||
|             if (@event.IsActionPressed("roll")) | ||||
|             { | ||||
|                 if (Character.Inventory.SelectedItem is Weapon weapon) | ||||
|                 { | ||||
|                     if (!weapon.IsUsing) | ||||
|                     { | ||||
|                         return RollState; | ||||
|                     } | ||||
|                 } | ||||
|                 else | ||||
|                 if (!_player.IsUsingAnyWeapon()) | ||||
|                 { | ||||
|                     return RollState; | ||||
|                 } | ||||
|  |  | |||
|  | @ -16,7 +16,9 @@ namespace SupaLidlGame.Characters.State | |||
|             #if DEBUG | ||||
|             if (@event.IsActionPressed("equip")) | ||||
|             { | ||||
|                 Character.Inventory.SelectedItem = Character.Inventory.GetNode<Items.Item>("Sword"); | ||||
|                 //Character.Inventory.SelectedItem = Character.Inventory.GetNode<Items.Item>("Sword"); | ||||
|                 //Character.Inventory.SelectedItem = Character. | ||||
|                 Character.Inventory.SelectFirstItem(); | ||||
|             } | ||||
|             #endif | ||||
| 
 | ||||
|  | @ -29,7 +31,7 @@ namespace SupaLidlGame.Characters.State | |||
|                                                         "ui_up", "ui_down"); | ||||
|             Vector2 mousePos = Character.GetGlobalMousePosition(); | ||||
|             Vector2 dirToMouse = Character.GlobalPosition.DirectionTo(mousePos); | ||||
|             if (Character.Inventory.SelectedItem is Weapon weapon) | ||||
|             if (Character.Inventory.PrimaryItem is Weapon weapon) | ||||
|             { | ||||
|                 if (!weapon.IsUsing) | ||||
|                 { | ||||
|  | @ -39,7 +41,7 @@ namespace SupaLidlGame.Characters.State | |||
| 
 | ||||
|             if (Godot.Input.IsActionPressed("attack1")) | ||||
|             { | ||||
|                 if (Character.Inventory.SelectedItem is not null) | ||||
|                 if (Character.Inventory.PrimaryItem is not null) | ||||
|                 { | ||||
|                     Character.UseCurrentItem(); | ||||
|                 } | ||||
|  |  | |||
|  | @ -0,0 +1,12 @@ | |||
| using Godot; | ||||
| 
 | ||||
| namespace SupaLidlGame.Extensions | ||||
| { | ||||
|     public static class ResourceExtensions | ||||
|     { | ||||
|         public static string GetFileName(this Resource resource) | ||||
|         { | ||||
|             return resource.ResourcePath.GetFile(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -8,39 +8,96 @@ namespace SupaLidlGame.Items | |||
|     { | ||||
|         public Character Character { get; private set; } | ||||
| 
 | ||||
|         public List<Item> Items { get; private set; } = new List<Item>(); | ||||
|         [Export] | ||||
|         public Godot.Collections.Array<ItemInfo> Items { get; private set; } | ||||
| 
 | ||||
|         [Export] | ||||
|         public ItemInfo Test { get; set; } | ||||
| 
 | ||||
|         public const int MaxCapacity = 32; | ||||
| 
 | ||||
|         private Item _selectedItem; | ||||
|         private Item _primaryItem; | ||||
| 
 | ||||
|         private Item _offhandItem; | ||||
| 
 | ||||
|         public Item SelectedItem | ||||
|         public Item PrimaryItem | ||||
|         { | ||||
|             get => _selectedItem; | ||||
|             set => EquipItem(value, ref _selectedItem); | ||||
|             get => _primaryItem; | ||||
|             private set => _primaryItem = value; | ||||
|         } | ||||
| 
 | ||||
|         public Item OffhandItem | ||||
|         { | ||||
|             get => _selectedItem; | ||||
|             set => EquipItem(value, ref _offhandItem); | ||||
|             get => _offhandItem; | ||||
|             private set => _offhandItem = value; | ||||
|         } | ||||
| 
 | ||||
|         public Inventory() | ||||
|         { | ||||
|             Items = new Godot.Collections.Array<ItemInfo>(); | ||||
|         } | ||||
| 
 | ||||
|         public bool EquipItem(ItemInfo info, bool offhand = false) | ||||
|         { | ||||
|             if (UnequipItem() == info) | ||||
|             { | ||||
|                 // if the item that was unequipped was our item, then we stop | ||||
|                 return true; | ||||
|             } | ||||
| 
 | ||||
|             var idx = Items.IndexOf(info); | ||||
| 
 | ||||
|             if (idx < 0) | ||||
|             { | ||||
|                 return false; | ||||
|             } | ||||
| 
 | ||||
|             ItemInfo item = Items[idx]; | ||||
| 
 | ||||
|             string name = offhand ? "Offhand" : "Primary"; | ||||
|             PrimaryItem = item.InstantiateItem(name); | ||||
|             PrimaryItem.Equip(Character); | ||||
|             PrimaryItem.CharacterOwner = Character; | ||||
|             //PrimaryItem.CharacterOwner | ||||
|             AddChild(PrimaryItem); | ||||
|             return true; | ||||
|         } | ||||
| 
 | ||||
|         public ItemInfo UnequipItem(bool offhand = false) | ||||
|         { | ||||
|             Item item; | ||||
|             if (!offhand) | ||||
|             { | ||||
|                 item = Character.Inventory.GetNode<Item>("Primary"); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 item = Character.Inventory.GetNode<Item>("Offhand"); | ||||
|             } | ||||
| 
 | ||||
|             if (item is null) | ||||
|             { | ||||
|                 return null; | ||||
|             } | ||||
| 
 | ||||
|             item.QueueFree(); | ||||
|             return item.Info; | ||||
|         } | ||||
| 
 | ||||
|         /* | ||||
|         private bool EquipItem(Item item, ref Item slot) | ||||
|         { | ||||
|             if (item is not null && item.IsOneHanded) | ||||
|             if (item is not null && item.Info.IsOneHanded) | ||||
|             { | ||||
|                 // we can not equip this if either hand is occupied by | ||||
|                 // two-handed item | ||||
| 
 | ||||
|                 if (_selectedItem is not null && !_selectedItem.IsOneHanded) | ||||
|                 if (_selectedItem is not null && !_selectedItem.Info.IsOneHanded) | ||||
|                 { | ||||
|                     return false; | ||||
|                 } | ||||
| 
 | ||||
|                 if (_offhandItem is not null && !_offhandItem.IsOneHanded) | ||||
|                 if (_offhandItem is not null && !_offhandItem.Info.IsOneHanded) | ||||
|                 { | ||||
|                     return false; | ||||
|                 } | ||||
|  | @ -66,39 +123,59 @@ namespace SupaLidlGame.Items | |||
| 
 | ||||
|             return true; | ||||
|         } | ||||
|         */ | ||||
| 
 | ||||
|         public Item AddItem(Item item) | ||||
|         public ItemInfo AddItem(ItemInfo info) | ||||
|         { | ||||
|             if (Items.Count >= MaxCapacity) | ||||
|             { | ||||
|                 return null; | ||||
|             } | ||||
| 
 | ||||
|             item.CharacterOwner = Character; | ||||
|             item.Visible = false; | ||||
|             Items.Add(item); | ||||
|             return item; | ||||
|             info.CharacterOwner = Character; | ||||
|             //item.Visible = false; | ||||
|             Items.Add(info); | ||||
|             return info; | ||||
|         } | ||||
| 
 | ||||
|         public Item DropItem(Item item) | ||||
|         public Item DropItem(ItemInfo item) | ||||
|         { | ||||
|             item.CharacterOwner = null; | ||||
|             item.Visible = true; | ||||
|             var e = SelectedItem = item; | ||||
|             //item.Visible = true; | ||||
|             //var e = SelectedItem = item; | ||||
|             throw new System.NotImplementedException(); | ||||
|         } | ||||
| 
 | ||||
|         public override void _Ready() | ||||
|         { | ||||
|             Character = GetParent<Character>(); | ||||
|             EnsureChildrenItems(); | ||||
|             base._Ready(); | ||||
|         } | ||||
| 
 | ||||
|         private void EnsureChildrenItems() | ||||
|         { | ||||
|             foreach (Node child in GetChildren()) | ||||
|             { | ||||
|                 if (child is Item item) | ||||
|                 { | ||||
|                     AddItem(item); | ||||
|                     GD.Print(item.Info.Count); | ||||
|                     if (Items.IndexOf(item.Info) < 0) | ||||
|                     { | ||||
|                         AddItem(item.Info); | ||||
|                         PrimaryItem = item; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             base._Ready(); | ||||
|         } | ||||
| 
 | ||||
|         public void SelectFirstItem() | ||||
|         { | ||||
|             EnsureChildrenItems(); | ||||
|             if (Items.Count > 0) | ||||
|             { | ||||
|                 EquipItem(Items[0]); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -6,17 +6,7 @@ namespace SupaLidlGame.Items | |||
|     public abstract partial class Item : Node2D | ||||
|     { | ||||
|         [Export] | ||||
|         public string ItemName { get; set; } | ||||
| 
 | ||||
|         [Export] | ||||
|         public string Description { get; set; } | ||||
| 
 | ||||
|         [Export] | ||||
|         public bool CanStack { get; set; } = false; | ||||
| 
 | ||||
|         public int Count { get; set; } = 1; | ||||
| 
 | ||||
|         public bool IsOneHanded { get; set; } = false; | ||||
|         public ItemInfo Info { get; set; } | ||||
| 
 | ||||
|         public Character CharacterOwner { get; set; } | ||||
| 
 | ||||
|  | @ -25,12 +15,12 @@ namespace SupaLidlGame.Items | |||
|         /// </summary> | ||||
|         public virtual bool StacksWith(Item item) | ||||
|         { | ||||
|             if (!CanStack) | ||||
|             if (!Info.CanStack) | ||||
|             { | ||||
|                 return false; | ||||
|             } | ||||
| 
 | ||||
|             if (ItemName != item.ItemName) | ||||
|             if (Info.ItemName != item.Info.ItemName) | ||||
|             { | ||||
|                 return false; | ||||
|             } | ||||
|  |  | |||
|  | @ -0,0 +1,7 @@ | |||
| namespace SupaLidlGame | ||||
| { | ||||
|     public class ItemDatabase | ||||
|     { | ||||
| 
 | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,63 @@ | |||
| using Godot; | ||||
| using SupaLidlGame.Characters; | ||||
| using MonoCustomResourceRegistry; | ||||
| 
 | ||||
| namespace SupaLidlGame.Items | ||||
| { | ||||
|     [RegisteredType(nameof(ItemInfo))] | ||||
|     public partial class ItemInfo : Resource | ||||
|     { | ||||
|         [Export] | ||||
|         public string ItemName { get; set; } = "Item Name"; | ||||
| 
 | ||||
|         [Export] | ||||
|         public string Description { get; set; } = "Item description."; | ||||
| 
 | ||||
|         [Export] | ||||
|         public bool CanStack { get; set; } = false; | ||||
| 
 | ||||
|         [Export] | ||||
|         public int Count { get; set; } = 1; | ||||
| 
 | ||||
|         [Export] | ||||
|         public bool IsOneHanded { get; set; } = false; | ||||
| 
 | ||||
|         [Export] | ||||
|         public Character CharacterOwner { get; set; } = null; | ||||
| 
 | ||||
|         [Export] | ||||
|         public Texture2D Texture { get; set; } | ||||
| 
 | ||||
|         [Export] | ||||
|         public string ScenePath { get; set; } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Determines if this item can directly stack with other items | ||||
|         /// </summary> | ||||
|         public virtual bool StacksWith(ItemInfo itemInfo) | ||||
|         { | ||||
|             if (!CanStack) | ||||
|             { | ||||
|                 return false; | ||||
|             } | ||||
| 
 | ||||
|             if (ItemName != itemInfo.ItemName) | ||||
|             { | ||||
|                 return false; | ||||
|             } | ||||
| 
 | ||||
|             // several more conditions may be added soon | ||||
| 
 | ||||
|             return true; | ||||
|         } | ||||
| 
 | ||||
|         public Item InstantiateItem(string name = "Primary") | ||||
|         { | ||||
|             var scene = ResourceLoader.Load<PackedScene>(ScenePath); | ||||
|             var instance = scene.Instantiate<Item>(); | ||||
|             instance.Name = name; | ||||
|             instance.Info = this; | ||||
|             return instance; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,17 @@ | |||
| using Godot; | ||||
| 
 | ||||
| namespace SupaLidlGame.Items.Weapons | ||||
| { | ||||
|     public partial class MeleeInfo : WeaponInfo | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// The time frame in seconds for which the weapon will deal damage. | ||||
|         /// </summary> | ||||
|         /// <remarks> | ||||
|         /// The value of <c>AttackTime</c> should be less than the | ||||
|         /// value of <c>UseTime</c> | ||||
|         /// </remarks> | ||||
|         [Export] | ||||
|         public double AttackTime { get; set; } = 0; | ||||
|     } | ||||
| } | ||||
|  | @ -10,30 +10,7 @@ namespace SupaLidlGame.Items | |||
| 
 | ||||
|         public bool IsUsing => RemainingUseTime > 0; | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// How much damage in HP that this weapon deals. | ||||
|         /// </summary> | ||||
|         [Export] | ||||
|         public float Damage { get; set; } = 0; | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// The time in seconds it takes for this weapon to become available | ||||
|         /// again after using. | ||||
|         /// </summary> | ||||
|         [Export] | ||||
|         public double UseTime { get; set; } = 0; | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// The magnitude of the knockback force of the weapon. | ||||
|         /// </summary> | ||||
|         [Export] | ||||
|         public float Knockback { get; set; } = 0; | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// The initial velocity of any projectile the weapon may spawn. | ||||
|         /// </summary> | ||||
|         [Export] | ||||
|         public float InitialVelocity { get; set; } = 0; | ||||
|         public new WeaponInfo Info => base.Info as WeaponInfo; | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Whether or not the weapon can parry other weapons and is | ||||
|  | @ -51,6 +28,7 @@ namespace SupaLidlGame.Items | |||
| 
 | ||||
|         public override void Equip(Character character) | ||||
|         { | ||||
|             GD.Print("Equipped by " + character.Name); | ||||
|             Character = character; | ||||
|         } | ||||
| 
 | ||||
|  | @ -61,7 +39,7 @@ namespace SupaLidlGame.Items | |||
| 
 | ||||
|         public override void Use() | ||||
|         { | ||||
|             RemainingUseTime = UseTime; | ||||
|             RemainingUseTime = Info.UseTime; | ||||
|         } | ||||
| 
 | ||||
|         public override void Deuse() | ||||
|  | @ -84,7 +62,7 @@ namespace SupaLidlGame.Items | |||
|         { | ||||
|             if (box is Hurtbox hurtbox) | ||||
|             { | ||||
|                 hurtbox.InflictDamage(Damage, Character, Knockback); | ||||
|                 hurtbox.InflictDamage(Info.Damage, Character, Info.Knockback); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  |  | |||
|  | @ -0,0 +1,33 @@ | |||
| using Godot; | ||||
| 
 | ||||
| namespace SupaLidlGame.Items | ||||
| { | ||||
|     public partial class WeaponInfo : ItemInfo | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// How much damage in HP that this weapon deals. | ||||
|         /// </summary> | ||||
|         [Export] | ||||
|         public float Damage { get; set; } = 0; | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// The time in seconds it takes for this weapon to become available | ||||
|         /// again after using. | ||||
|         /// </summary> | ||||
|         [Export] | ||||
|         public double UseTime { get; set; } = 0; | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// The magnitude of the knockback force of the weapon. | ||||
|         /// </summary> | ||||
|         [Export] | ||||
|         public float Knockback { get; set; } = 0; | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// The initial velocity of any projectile the weapon may spawn. | ||||
|         /// </summary> | ||||
|         [Export] | ||||
|         public float InitialVelocity { get; set; } = 0; | ||||
| 
 | ||||
|     } | ||||
| } | ||||
|  | @ -15,16 +15,6 @@ namespace SupaLidlGame.Items.Weapons | |||
|         [Export] | ||||
|         public AnimationPlayer AnimationPlayer { get; set; } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// The time frame in seconds for which the weapon will deal damage. | ||||
|         /// </summary> | ||||
|         /// <remarks> | ||||
|         /// The value of <c>AttackTime</c> should be less than the | ||||
|         /// value of <c>UseTime</c> | ||||
|         /// </remarks> | ||||
|         [Export] | ||||
|         public double AttackTime { get; set; } = 0; | ||||
| 
 | ||||
|         [Export] | ||||
|         public CpuParticles2D ParryParticles { get; set; } | ||||
| 
 | ||||
|  | @ -103,7 +93,7 @@ namespace SupaLidlGame.Items.Weapons | |||
| 
 | ||||
|         public override void _Ready() | ||||
|         { | ||||
|             Hitbox.Damage = Damage; | ||||
|             Hitbox.Damage = Info.Damage; | ||||
|         } | ||||
| 
 | ||||
|         public override void _Process(double delta) | ||||
|  | @ -123,7 +113,10 @@ namespace SupaLidlGame.Items.Weapons | |||
|                 GD.Print("processing hit"); | ||||
|                 if (box is Hurtbox hurtbox) | ||||
|                 { | ||||
|                     hurtbox.InflictDamage(Damage, Character, Knockback); | ||||
|                     hurtbox.InflictDamage( | ||||
|                         Info.Damage, | ||||
|                         Character, | ||||
|                         Info.Knockback); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | @ -165,10 +158,13 @@ namespace SupaLidlGame.Items.Weapons | |||
|             { | ||||
|                 if (hurt.GetParent() is Character c) | ||||
|                 { | ||||
|                     var item = c.Inventory.SelectedItem; | ||||
|                     if (item is Weapon w) | ||||
|                     if (c.Inventory.PrimaryItem is Weapon primary) | ||||
|                     { | ||||
|                         AttemptParry(w); | ||||
|                         AttemptParry(primary); | ||||
|                     } | ||||
|                     if (c.Inventory.OffhandItem is Weapon offhand) | ||||
|                     { | ||||
|                         AttemptParry(offhand); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  |  | |||
|  | @ -0,0 +1,17 @@ | |||
| [gd_resource type="Resource" load_steps=2 format=3 uid="uid://dbqk11m54e72e"] | ||||
| 
 | ||||
| [ext_resource type="Script" path="res://Items/MeleeInfo.cs" id="1_w5yvw"] | ||||
| 
 | ||||
| [resource] | ||||
| script = ExtResource("1_w5yvw") | ||||
| AttackTime = 0.1 | ||||
| Damage = 20.0 | ||||
| UseTime = 0.8 | ||||
| Knockback = 80.0 | ||||
| InitialVelocity = 0.0 | ||||
| ItemName = "Sword" | ||||
| Description = "A basic sword." | ||||
| CanStack = false | ||||
| Count = 1 | ||||
| IsOneHanded = false | ||||
| ScenePath = "" | ||||
|  | @ -1,6 +1,7 @@ | |||
| [gd_scene load_steps=20 format=3 uid="uid://d72ehtv1ks0e"] | ||||
| [gd_scene load_steps=21 format=3 uid="uid://d72ehtv1ks0e"] | ||||
| 
 | ||||
| [ext_resource type="Script" path="res://Items/Weapons/Sword.cs" id="1_mlo73"] | ||||
| [ext_resource type="Resource" uid="uid://dbqk11m54e72e" path="res://Items/Weapons/Sword.tres" id="2_5vlf6"] | ||||
| [ext_resource type="Texture2D" uid="uid://dt6u8p4h6g7le" path="res://Assets/Sprites/knife.png" id="2_rnfo4"] | ||||
| [ext_resource type="PackedScene" uid="uid://du5vhccg75nrq" path="res://BoundingBoxes/Hitbox.tscn" id="3_up3ob"] | ||||
| [ext_resource type="PackedScene" uid="uid://cojxmcin13ihm" path="res://Utils/Trail.tscn" id="4_pt6lq"] | ||||
|  | @ -302,13 +303,8 @@ position = Vector2(2, 0) | |||
| script = ExtResource("1_mlo73") | ||||
| Hitbox = NodePath("Hitbox") | ||||
| AnimationPlayer = NodePath("AnimationPlayer") | ||||
| AttackTime = 0.1 | ||||
| ParryParticles = NodePath("Anchor/Sprite2D/ParryParticles") | ||||
| Damage = 20.0 | ||||
| UseTime = 0.8 | ||||
| Knockback = 80.0 | ||||
| ItemName = "Sword" | ||||
| Description = "A basic sword." | ||||
| Info = ExtResource("2_5vlf6") | ||||
| 
 | ||||
| [node name="Anchor" type="Node2D" parent="."] | ||||
| y_sort_enabled = true | ||||
|  |  | |||
|  | @ -0,0 +1,14 @@ | |||
| using Godot; | ||||
| 
 | ||||
| namespace SupaLidlGame.Utils | ||||
| { | ||||
|     public class AudioBuilder | ||||
|     { | ||||
|         private AudioStreamPlayer2D _audio; | ||||
| 
 | ||||
|         public AudioBuilder(AudioStreamPlayer2D baseAudio) | ||||
|         { | ||||
|             _audio = baseAudio; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,208 @@ | |||
| using Godot; | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using System.Reflection; | ||||
| using System.Text; | ||||
| 
 | ||||
| // Originally written by wmigor | ||||
| // Edited by Atlinx to recursively search for files. | ||||
| // Edited by bls220 to update for Godot 4.0 | ||||
| // wmigor's Public Repo: https://github.com/wmigor/godot-mono-custom-resource-register | ||||
| namespace MonoCustomResourceRegistry | ||||
| { | ||||
| #if TOOLS | ||||
|     [Tool] | ||||
|     public partial class Plugin : EditorPlugin | ||||
|     { | ||||
|         // We're not going to hijack the Mono Build button since it actually takes time to build | ||||
|         // and we can't be sure how long that is. I guess we have to leave refreshing to the user for now. | ||||
|         // There isn't any automation we can do to fix that. | ||||
|         // private Button MonoBuildButton => GetNode<Button>("/root/EditorNode/@@580/@@581/@@589/@@590/Button"); | ||||
|         private readonly List<string> customTypes = new List<string>(); | ||||
|         private Button? refreshButton; | ||||
| 
 | ||||
|         public override void _EnterTree() | ||||
|         { | ||||
|             refreshButton = new Button(); | ||||
|             refreshButton.Text = "CCR"; | ||||
| 
 | ||||
|             AddControlToContainer(CustomControlContainer.Toolbar, refreshButton); | ||||
|             refreshButton.Icon = GetEditorInterface().GetBaseControl().GetThemeIcon("Reload", "EditorIcons"); | ||||
|             refreshButton.Pressed += OnRefreshPressed; | ||||
| 
 | ||||
|             Settings.Init(); | ||||
|             RefreshCustomClasses(); | ||||
|             GD.PushWarning("You may change any setting for MonoCustomResourceRegistry in Project -> ProjectSettings -> General -> MonoCustomResourceRegistry"); | ||||
|         } | ||||
| 
 | ||||
|         public override void _ExitTree() | ||||
|         { | ||||
|             UnregisterCustomClasses(); | ||||
|             RemoveControlFromContainer(CustomControlContainer.Toolbar, refreshButton); | ||||
|             refreshButton?.QueueFree(); | ||||
|         } | ||||
| 
 | ||||
|         public void RefreshCustomClasses() | ||||
|         { | ||||
|             GD.Print("\nRefreshing Registered Resources..."); | ||||
|             UnregisterCustomClasses(); | ||||
|             RegisterCustomClasses(); | ||||
|         } | ||||
| 
 | ||||
|         private void RegisterCustomClasses() | ||||
|         { | ||||
|             customTypes.Clear(); | ||||
| 
 | ||||
|             foreach (Type type in GetCustomRegisteredTypes()) | ||||
|                 if (type.IsSubclassOf(typeof(Resource))) | ||||
|                     AddRegisteredType(type, nameof(Resource)); | ||||
|                 else | ||||
|                     AddRegisteredType(type, nameof(Node)); | ||||
|         } | ||||
| 
 | ||||
|         private void AddRegisteredType(Type type, string defaultBaseTypeName) | ||||
|         { | ||||
|             RegisteredTypeAttribute? attribute = Attribute.GetCustomAttribute(type, typeof(RegisteredTypeAttribute)) as RegisteredTypeAttribute; | ||||
|             string? path = FindClassPath(type); | ||||
|             if (path == null && !FileAccess.FileExists(path)) | ||||
|                 return; | ||||
|             Script script = GD.Load<Script>(path); | ||||
|             if (script == null) | ||||
|                 return; | ||||
|             string baseType = defaultBaseTypeName; | ||||
|             if (attribute is not null && attribute.baseType != "") | ||||
|                 baseType = attribute.baseType; | ||||
|             ImageTexture? icon = null; | ||||
|             if (attribute is not null && attribute.iconPath != "") | ||||
|             { | ||||
|                 if (FileAccess.FileExists(attribute.iconPath)) | ||||
|                 { | ||||
|                     Texture2D rawIcon = ResourceLoader.Load<Texture2D>(attribute.iconPath); | ||||
|                     if (rawIcon != null) | ||||
|                     { | ||||
|                         Image image = rawIcon.GetImage(); | ||||
|                         int length = (int)Mathf.Round(16 * GetEditorInterface().GetEditorScale()); | ||||
|                         image.Resize(length, length); | ||||
|                         icon = ImageTexture.CreateFromImage(image); | ||||
|                     } | ||||
|                     else | ||||
|                         GD.PushError($"Could not load the icon for the registered type \"{type.FullName}\" at path \"{path}\"."); | ||||
|                 } | ||||
|                 else | ||||
|                     GD.PushError($"The icon path of \"{path}\" for the registered type \"{type.FullName}\" does not exist."); | ||||
|             } | ||||
|             AddCustomType($"{Settings.ClassPrefix}{type.Name}", baseType, script, icon); | ||||
|             customTypes.Add($"{Settings.ClassPrefix}{type.Name}"); | ||||
|             GD.Print($"Registered custom type: {type.Name} -> {path}"); | ||||
|         } | ||||
| 
 | ||||
|         private static string? FindClassPath(Type type) | ||||
|         { | ||||
|             switch (Settings.SearchType) | ||||
|             { | ||||
|                 case Settings.ResourceSearchType.Recursive: | ||||
|                     return FindClassPathRecursive(type); | ||||
|                 case Settings.ResourceSearchType.Namespace: | ||||
|                     return FindClassPathNamespace(type); | ||||
|                 default: | ||||
|                     throw new Exception($"ResourceSearchType {Settings.SearchType} not implemented!"); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         private static string? FindClassPathNamespace(Type type) | ||||
|         { | ||||
|             foreach (string dir in Settings.ResourceScriptDirectories) | ||||
|             { | ||||
|                 StringBuilder builder = new(dir); | ||||
|                 if (!dir.EndsWith('/')) | ||||
|                 { | ||||
|                     builder.Append('/'); | ||||
|                 } | ||||
|                 if (type.Namespace is not null) | ||||
|                 { | ||||
|                     builder | ||||
|                         .Append(type.Namespace.Replace(".", "/")) | ||||
|                         .Append('/'); | ||||
|                 } | ||||
|                 builder | ||||
|                     .Append(type.Name) | ||||
|                     .Append(".cs"); | ||||
|                 string filePath = builder.ToString(); | ||||
|                 if (FileAccess.FileExists(filePath)) | ||||
|                     return filePath; | ||||
|             } | ||||
|             return null; | ||||
|         } | ||||
| 
 | ||||
|         private static string? FindClassPathRecursive(Type type) | ||||
|         { | ||||
|             foreach (string directory in Settings.ResourceScriptDirectories) | ||||
|             { | ||||
|                 string? fileFound = FindClassPathRecursiveHelper(type, directory); | ||||
|                 if (fileFound != null) | ||||
|                     return fileFound; | ||||
|             } | ||||
|             return null; | ||||
|         } | ||||
| 
 | ||||
|         private static string? FindClassPathRecursiveHelper(Type type, string directory) | ||||
|         { | ||||
|             var dir = DirAccess.Open(directory); | ||||
| 
 | ||||
|             if (DirAccess.GetOpenError() == Error.Ok) | ||||
|             { | ||||
|                 dir.ListDirBegin(); | ||||
| 
 | ||||
|                 while (true) | ||||
|                 { | ||||
|                     var fileOrDirName = dir.GetNext(); | ||||
| 
 | ||||
|                     // Skips hidden files like . | ||||
|                     if (fileOrDirName == "") | ||||
|                         break; | ||||
|                     else if (fileOrDirName.StartsWith(".")) | ||||
|                         continue; | ||||
|                     else if (dir.CurrentIsDir()) | ||||
|                     { | ||||
|                         string? foundFilePath = FindClassPathRecursiveHelper(type, dir.GetCurrentDir() + "/" + fileOrDirName); | ||||
|                         if (foundFilePath != null) | ||||
|                         { | ||||
|                             dir.ListDirEnd(); | ||||
|                             return foundFilePath; | ||||
|                         } | ||||
|                     } | ||||
|                     else if (fileOrDirName == $"{type.Name}.cs") | ||||
|                         return dir.GetCurrentDir() + "/" + fileOrDirName; | ||||
|                 } | ||||
|             } | ||||
|             return null; | ||||
|         } | ||||
| 
 | ||||
|         private static IEnumerable<Type> GetCustomRegisteredTypes() | ||||
|         { | ||||
|             var assembly = Assembly.GetAssembly(typeof(Plugin)); | ||||
|             return assembly?.GetTypes().Where(t => !t.IsAbstract | ||||
|                 && Attribute.IsDefined(t, typeof(RegisteredTypeAttribute)) | ||||
|                 && (t.IsSubclassOf(typeof(Node)) || t.IsSubclassOf(typeof(Resource))) | ||||
|                 ) ?? Enumerable.Empty<Type>(); | ||||
|         } | ||||
| 
 | ||||
|         private void UnregisterCustomClasses() | ||||
|         { | ||||
|             foreach (var script in customTypes) | ||||
|             { | ||||
|                 RemoveCustomType(script); | ||||
|                 GD.Print($"Unregister custom resource: {script}"); | ||||
|             } | ||||
| 
 | ||||
|             customTypes.Clear(); | ||||
|         } | ||||
| 
 | ||||
|         private void OnRefreshPressed() | ||||
|         { | ||||
|             RefreshCustomClasses(); | ||||
|         } | ||||
|     } | ||||
| #endif | ||||
| } | ||||
|  | @ -0,0 +1,20 @@ | |||
| using Godot; | ||||
| using System; | ||||
| 
 | ||||
| namespace MonoCustomResourceRegistry | ||||
| { | ||||
| 	[AttributeUsage(System.AttributeTargets.Class)] | ||||
| 	public partial class RegisteredTypeAttribute : System.Attribute | ||||
| 	{ | ||||
| 		public string name; | ||||
| 		public string iconPath; | ||||
| 		public string baseType; | ||||
| 
 | ||||
| 		public RegisteredTypeAttribute(string name, string iconPath = "", string baseType = "") | ||||
| 		{ | ||||
| 			this.name = name; | ||||
| 			this.iconPath = iconPath; | ||||
| 			this.baseType = baseType; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | @ -0,0 +1,57 @@ | |||
| using Godot; | ||||
| using Godot.Collections; | ||||
| using System.Collections.ObjectModel; | ||||
| using System.Linq; | ||||
| 
 | ||||
| namespace MonoCustomResourceRegistry | ||||
| { | ||||
|     public static class Settings | ||||
|     { | ||||
|         public enum ResourceSearchType | ||||
|         { | ||||
|             Recursive = 0, | ||||
|             Namespace = 1, | ||||
|         } | ||||
| 
 | ||||
|         public static string ClassPrefix => GetSettings(nameof(ClassPrefix)).AsString(); | ||||
|         public static ResourceSearchType SearchType => (ResourceSearchType)GetSettings(nameof(SearchType)).AsInt32(); | ||||
|         public static ReadOnlyCollection<string> ResourceScriptDirectories | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 Array array = (Array)GetSettings(nameof(ResourceScriptDirectories)) ?? new Array(); | ||||
|                 return new(array.Select(v => v.AsString()).ToList()); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         public static void Init() | ||||
|         { | ||||
|             AddSetting(nameof(ClassPrefix), Variant.Type.String, ""); | ||||
|             AddSetting(nameof(SearchType), Variant.Type.Int, ResourceSearchType.Recursive, PropertyHint.Enum, "Recursive,Namespace"); | ||||
|             AddSetting(nameof(ResourceScriptDirectories), Variant.Type.Array, new Array<string>(new string[] { "res://" })); | ||||
|         } | ||||
| 
 | ||||
|         private static Variant GetSettings(string title) | ||||
|         { | ||||
|             return ProjectSettings.GetSetting($"{nameof(MonoCustomResourceRegistry)}/{title}"); | ||||
|         } | ||||
| 
 | ||||
|         private static void AddSetting<T>(string title, Variant.Type type, T value, PropertyHint hint = PropertyHint.None, string hintString = "") | ||||
|         { | ||||
|             title = SettingPath(title); | ||||
|             if (!ProjectSettings.HasSetting(title)) | ||||
|                 ProjectSettings.SetSetting(title, Variant.From(value)); | ||||
|             var info = new Dictionary | ||||
|             { | ||||
|                 ["name"] = title, | ||||
|                 ["type"] = Variant.From(type), | ||||
|                 ["hint"] = Variant.From(hint), | ||||
|                 ["hint_string"] = hintString, | ||||
|             }; | ||||
|             ProjectSettings.AddPropertyInfo(info); | ||||
|             GD.Print("Successfully added property: " + title); | ||||
|         } | ||||
| 
 | ||||
|         private static string SettingPath(string title) => $"{nameof(MonoCustomResourceRegistry)}/{title}"; | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,7 @@ | |||
| [plugin] | ||||
| 
 | ||||
| name="Mono Custom Resource Registry" | ||||
| description="Registers custom C# resources for Godot" | ||||
| author="Atlinx, wmigor, rob-mur" | ||||
| version="3.0.0" | ||||
| script="Plugin.cs" | ||||
|  | @ -8,6 +8,12 @@ | |||
| 
 | ||||
| config_version=5 | ||||
| 
 | ||||
| [MonoCustomResourceRegistry] | ||||
| 
 | ||||
| ClassPrefix="" | ||||
| SearchType=0 | ||||
| ResourceScriptDirectories=["res://"] | ||||
| 
 | ||||
| [application] | ||||
| 
 | ||||
| config/name="SupaLidlGame" | ||||
|  | @ -19,6 +25,10 @@ config/icon="res://icon.svg" | |||
| 
 | ||||
| project/assembly_name="SupaLidlGame" | ||||
| 
 | ||||
| [editor_plugins] | ||||
| 
 | ||||
| enabled=PackedStringArray("res://addons/MonoCustomResourceRegistry/plugin.cfg") | ||||
| 
 | ||||
| [input] | ||||
| 
 | ||||
| ui_left={ | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue