Giter Club home page Giter Club logo

inventory's Introduction

Preview.mp4

A Game Inventory being made in Godot 4 C#

The goal of this project is to attempt to perfect a game inventory system so it can be used across all games that make use of inventories.

As of writing this, this project requires Godot 4 Release Candidate 1 (v4.0.rc1.mono.official [8843d9ad3]). If you are unsure which version this project is on, please check the Inventory.csproj file, the version will be displayed in there.

Table of Contents

  1. Features
  2. Implemented Controls
  3. Todo
  4. Example Code
  5. Contributing
  6. Previews

Features

  • Inventories of varying sizes can be defined
  • Inventory slot size can be customized
  • Player Inventory Hotbar
  • Smooth player inventory open / close animations

Implemented Controls

  • Left Click to pick up / place item stack
  • Hold + Left Click to continuously pick up items of the same type
  • Shift + Left Click to transfer item stack from inventories A to B
  • Hold + Shift + Left Click to continuously transfer item stack from inventories A to B
  • Shift + Right Click to split stack
  • Right Click to pick up / place single item
  • Hold + Right Click to continuously place a single item
  • Double Click to pick up all items of the same type
  • Shift + R to sort items in all open inventories by descending item count
  • Shift + T to take all items from the 'other inventory' and put them in the players inventory
  • 1 2 3 4 5 6 7 8 9 to move / swap items to player hotbar slots
  • I to open the players inventory
  • E to interact with objects in the world

Todo

  • Hotkey to deposit all items from player inventory to 'other inventory'
  • Make item panel description popup only appear after small delay
  • Saving / loading inventory data
  • Allow items to define their own max stack sizes
  • Q = Drop Item (x1)
  • Ctrl + Q = Drop Stack
  • Maybe implementing ENet-CSharp and syncing all of this inventory stuff over the network. (There would be a demo where 2 players would see each other walking around and see the inventory update when the other player interacts with it)
  • Giving items a category property so the sorting hotkey can sort by category or any other item property for that matter
  • Add item rarities (maybe rare items have a glowing outline achieved used Godot Shaders)

Example Code

private Inventory ChestInv { get; set; }
private Inventory PlayerInv { get; set; }

public override void _Ready()
{
    ChestInv = new Inventory(this);
    ChestInv.SetAnchor(Control.LayoutPreset.CenterTop);

    for (int i = 0; i < 9; i++)
        ChestInv.SetItem(i, new Item(Items.Coin));

    ChestInv.SetItem(0, 2, new Item(Items.Coin, 3));
    ChestInv.SetItem(1, 2, new Item(Items.CoinSnowy));

    PlayerInv = new Inventory(this);
    PlayerInv.SetAnchor(Control.LayoutPreset.CenterBottom);

    for (int i = 18; i < 27; i++)
        PlayerInv.SetItem(i, new Item(Items.CoinPink));

    Inventory.PlayerInventory = PlayerInv;
    Inventory.OtherInventory = ChestInv;
}

Contributing

Please talk to me over Discord va#9904 before you do anything.

Have a look at the todo list and the projects issues for things to do. On top of all that, I'm always trying to find ways to clean up the code and make it more humanly readable.

Please follow this code style

Previews

Preview.mp4
Preview.mp4
1.mp4
2.mp4
3.mp4
4.mp4

inventory's People

Contributors

valkyrienyanko avatar vuelos avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

inventory's Issues

Maybe `ItemCursor` and `InventorySlot` could extend from an abstract class?

I have come to a point on my project where I need to make InventorySlot.cs and ItemCursor.cs share some kind of abstract class or interface. They both share the common function SetItem(Item item) as seen below.

InventorySlot.cs:

public void SetItem(Item item)

ItemCursor.cs:
public static void SetItem(Item item)

The problem is ItemCursor extends from Godot.Control and all the functions in ItemCursor are static while all the functions in InventorySlot are not static and InventorySlot extends from nothing. I need to figure out how to make ItemCursor ?not be static I think and not extend from Control. So I can get them extending from the same class and have public abstract void SetItem(Item item);.

Not really sure how to make ItemCursor not extend from Control though. Maybe make a ItemCursorManager.cs script that handles a ItemCursor? But there will only ever be one mouse cursor in the game? So it feels like a waste to not make everything in it static but making it not static prevents me from making them share a same abstract class.

Why do this?

These two functions are very similar in nature (HotbarInvSlot(...) and HotbarCursor(...)). The only difference is one is handling for if there is a item currently being held in the cursor and the other handles the inventory slot item under where the cursor is hovering.

I want to combine these functions into 1 function and just call that function 2 times with different params but because ItemCursor and InventorySlot are 2 different things, this proves to be difficult. Perhaps they could extend from some kind of abstract class?

private static void InputHotbar(int hotbar)
{
if (!Input.IsActionJustPressed($"inventory_hotbar_{hotbar + 1}"))
return;
var cursorItem = ItemCursor.GetItem();
if (cursorItem == null)
HotbarInvSlot(hotbar);
else
HotbarCursor(hotbar, cursorItem);
}

private static void HotbarCursor(int hotbar, Item cursorItem)
{
var playerInv = Player.Inventory;
var playerInvSlots = playerInv.InventorySlots;
var columns = playerInv.Columns;
if (columns <= hotbar)
return;
var hotbarSlot = playerInvSlots[playerInvSlots.Length - columns + hotbar];
ItemPanelDescription.Clear();
if (hotbarSlot.InventoryItem == null)
{
// Just move the item over
hotbarSlot.SetItem(cursorItem);
ItemCursor.RemoveItem();
}
else
{
if (hotbarSlot.InventoryItem.Item.Type == cursorItem.Type)
{
// Same type of item
// Add the item counts together
cursorItem.Count += hotbarSlot.InventoryItem.Item.Count;
// Just move the item over
hotbarSlot.SetItem(cursorItem);
ItemCursor.RemoveItem();
}
else
{
// Different type of item
// Swap the items
var hotbarItem = hotbarSlot.InventoryItem.Item;
hotbarSlot.SetItem(cursorItem);
ItemCursor.SetItem(hotbarItem);
}
}
}

private static void HotbarInvSlot(int hotbar)
{
var activeInvSlot = Inventory.ActiveInventorySlot;
if (activeInvSlot == null)
return;
var activeInvSlotItem = activeInvSlot.InventoryItem;
if (activeInvSlotItem == null)
return;
var playerInv = Player.Inventory;
var columns = playerInv.Columns;
var playerInvSlots = playerInv.InventorySlots;
if (columns <= hotbar)
return;
var hotbarSlot = playerInvSlots[playerInvSlots.Length - columns + hotbar];
if (activeInvSlot == hotbarSlot)
return;
ItemPanelDescription.Clear();
if (hotbarSlot.InventoryItem == null)
{
// Just move the item over
hotbarSlot.SetItem(activeInvSlotItem.Item);
activeInvSlot.RemoveItem();
}
else
{
if (hotbarSlot.InventoryItem.Item.Type == activeInvSlotItem.Item.Type)
{
// Same type of item
// Add the item counts together
activeInvSlotItem.Item.Count += hotbarSlot.InventoryItem.Item.Count;
// Just move the item over
hotbarSlot.SetItem(activeInvSlotItem.Item);
activeInvSlot.RemoveItem();
}
else
{
// Different type of item
// Swap the items
var hotbarItem = hotbarSlot.InventoryItem.Item;
hotbarSlot.SetItem(activeInvSlotItem.Item);
activeInvSlot.SetItem(hotbarItem);
}
}
}

Looking for someone to help migrate Newtonsoft.Json code to System.Text.Json for .Net 7

The following code makes use of the Newtonsoft.Json namespace and should be converted to make use of the newer System.Text.Json namespace. This way a 3rd party nuget package is no longer being used. Newtonsoft was used in the first place because Godot 3 had no support for newer .Net features but since Godot supports .Net 7 now, this has since been possible.

/// <summary>
/// Prints the entire object in a readable format (supports Godot properties)
/// If you should ever run into a problem, see the IgnorePropsResolver class to ignore more
/// properties.
/// </summary>
public static string PrintFull(this object v) =>
JsonConvert.SerializeObject(v, Formatting.Indented, new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
ContractResolver = new IgnorePropsResolver() // ignore all Godot props
});

/// <summary>
/// Used when doing JsonConvert.SerializeObject to ignore Godot properties
/// as these are massive.
/// </summary>
private class IgnorePropsResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var prop = base.CreateProperty(member, memberSerialization);
// Ignored properties (prevents crashes)
var ignoredProps = new Type[]
{
typeof(GodotObject),
typeof(Node),
typeof(NodePath)
};
foreach (var ignoredProp in ignoredProps)
{
if (ignoredProp.GetProperties().Contains(member))
prop.Ignored = true;
if (prop.PropertyType == ignoredProp || prop.PropertyType.IsSubclassOf(ignoredProp))
prop.Ignored = true;
}
return prop;
}
}

Tests should be performed after the code has been converted. For example GD.Print(someNode.PrintFull()); should only print the custom user defined props and not crash the game.

The new code should look something like this

using System.Text.Json;
using System.Text.Json.Serialization;

public static string PrintFull(this object v) =>
	JsonSerializer.Serialize(v, new JsonSerializerOptions
	{
		WriteIndented = true,
		ReferenceHandler = ReferenceHandler.IgnoreCycles,
		TypeInfoResolver = ???
	})

The ??? is what I don't understand on what to do next.

Item Duplication Glitches

  1. Place 1 item of type A into hotbar slot 1
  2. Place 1 item of type B into hotbar slot 2
  3. Spam (Left Click or Right Click) and 1 2 constantly while hovering the mouse over hotbar slot 1
7d846b5b9aea03af024f40f6f1a41444.mp4

Abstract item animations to every item movement

Currently item animations are only implemented for when Shift + Clicking to transfer items from one inventory to another. This should eventually be abstracted to all inventory manipulations.

Item animations done so far

  • Shift + Click
  • Double Click
  • Left Click
  • Right Click
  • Hold + Left Click
  • Hold + Right Click
  • 1 2 3 ... 7 8 9
  • Shift + R
  • Shift + T

For reference here is the code for transferring a item.

// Transfer the item in the inventory slot we are currently hovering over
private void TransferItem()
{
// Do not transfer item if this item is currently being animated
if (CurrentlyAnimating)
return;
// There is no item here thus no item to transfer
if (InventoryItem == null)
return;
// Get the 'other inventory'
var targetInv = GetOtherInventory();
// No 'other inventory' is open, lets use the player inventory so the
// item gets transfered to the same inventory instead of doing nothing
if (targetInv == null)
targetInv = Player.Inventory;
// Try to find a empty slot in the target inventory
var emptySlot = targetInv.TryGetEmptyOrSameTypeSlot(InventoryItem.Item.Type);
// No empty slot was found!
if (emptySlot == -1)
return;
// Store temporary reference to the item in this inventory slot
var itemRef = InventoryItem.Item;
// Get a copy of the item sprite that is being transfered
// This is purely for visuals and does not effect the item logic
Graphic = InventoryItem.GenerateGraphic();
Graphic.GlobalPosition = Position;
// Get the other slot this item is being transfered to
var otherInvSlot = targetInv.InventorySlots[emptySlot];
//if (otherInvSlot.CurrentlyAnimating)
// return;
// Remove the item before it gets transfered
this.RemoveItem();
var otherInvSlotItem = otherInvSlot.InventoryItem;
var hide = false;
// If the other inventory slot has no item in it, then hide the item that
// gets transfered over. So it does not look like there is a duplicate
// when the graphic sprite is animated over
if (otherInvSlot.InventoryItem == null)
hide = true;
// Set the other inventory slot item to the item that is being transfered over
if (otherInvSlotItem == null)
{
otherInvSlot.SetItem(itemRef);
}
else
// If the item transfered has more than one than add that
{
itemRef.Count += otherInvSlotItem.Item.Count;
otherInvSlot.SetItem(itemRef);
}
OtherInventorySlot = otherInvSlot;
// Hide the other item
if (hide)
otherInvSlot.InventoryItem.Hide();
// Start animation
CurrentlyAnimating = true;
otherInvSlot.CurrentlyAnimating = true;
// Add graphic to the world
Main.AddToCanvasLayer(Graphic);
Tween = Panel.GetTree().CreateTween();
Tween.TweenProperty(Graphic, "global_position", otherInvSlot.Position, 1)
.SetEase(Tween.EaseType.Out)
.SetTrans(Tween.TransitionType.Cubic);
Tween.TweenCallback(Callable.From(() =>
{
// Sprite graphic reached its destination, lets show the other inventory slot item now
// For the love of god I have no fucking idea why I need a
// null check here. I spent 2 hours trying to figure out
// why InventoryItem becomes null. And with the null check
// earlier it was just hiding items. But now it all magically
// works all of a sudden wtf?
// Please just stay working okay?
// Thank you code
// I love you code
otherInvSlot.InventoryItem?.Show();
CurrentlyAnimating = false;
otherInvSlot.CurrentlyAnimating = false;
Graphic.QueueFree();
}));
}

And here is the code a chest calls when it is closed. (relevant because the chest cancels all the animations on closing the chest -- perhaps this code should be moved else where)

public void Close()
{
IsOpen = false;
// Clean up item animations
foreach (var slot in Inventory.OtherInventory.InventorySlots)
slot.ResetCleanUpAnimations();
foreach (var slot in Player.Inventory.InventorySlots)
slot.ResetCleanUpAnimations();
Inventory.OtherInventory = null;
Inventory.Close();
if (!Player.Inventory.IsHotbar)
Player.Inventory.SwitchToHotbarAnimated();
AnimatedSprite2D.Play("close");
ItemPanelDescription.Clear();
}

Implement `SwapItem(Slot a, Slot b, Item item)`

As mentioned in #4 this seems hard to implement because a Slot could either be a InventorySlot or be from the ItemCursor. But if this does get implemented I think it could really clean up a lot of the code.

Item Panel Descriptions Keep Stacking On Top of Each Other

As seen in the video in #7 the item panel description popups keep appearing on top of each other. This is unwanted. There should only be one panel description popup present at any time and it should be displaying the info for the item you are hovering over.

Tween animation is locking the hotbar position preventing the hotbar from hugging the bottom center of the window on window resize

I am trying to animate the hotbar to move downwards out of view, however resizing the window while this animation is in play has a unwanted consequence. (see attached video below)

Video Preview of Issue

35da49c8a78b5a5960ef662df7ec54c0.mp4

Steps to Reproduce

  1. Replace this code
    Player.Inventory.SwitchToFullInventory();

with the following code.

var tween = node.GetTree().CreateTween();

var windowHeight = DisplayServer.WindowGetSize().Y;

tween.TweenProperty(Player.Inventory.PanelContainer, "position:y", windowHeight, 0.5);
  1. Run the game and then press I
  2. While the animation is playing, resize the window

Potential Solution A

  1. Keep track of the tween
  2. Subscribe to on game window resize event
  3. Restart the tween with the remaining duration

Potential Solution B

Capture

Potential Solution C

Constantly set the position with Position = DisplayServer.GetWindowHeight() - Y in _PhysicsProcess(double delta)

Abstract inventory code to allow defining a player inventory on the top instead of the bottom

The code should be made to easily switch between the 2 depending on what kind of game you want to make.

If the player inventory is defined to be at the top then...

  • the chest inventory is always defined at the bottom
  • the hotbar should be defined to be the very first row instead of the very last row
  • the inventory exit animations should initially move upward instead of downward (and so on for the re-entry animation)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.