port over some more
This commit is contained in:
@@ -167,6 +167,17 @@ defmodule Odinsea.Game.Inventory do
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets the next available slot number.
|
||||
Returns nil if the inventory is full (Elixir-style).
|
||||
"""
|
||||
def get_next_free_slot(%__MODULE__{} = inv) do
|
||||
case next_free_slot(inv) do
|
||||
-1 -> nil
|
||||
slot -> slot
|
||||
end
|
||||
end
|
||||
|
||||
defp find_next_slot(items, limit, slot) when slot > limit, do: -1
|
||||
|
||||
defp find_next_slot(items, limit, slot) do
|
||||
@@ -205,6 +216,23 @@ defmodule Odinsea.Game.Inventory do
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Adds a plain map item to the inventory (used for drops).
|
||||
Returns {:ok, new_inventory, assigned_item} or {:error, :inventory_full}.
|
||||
"""
|
||||
def add_item(%__MODULE__{} = inv, %{} = item_map) when not is_struct(item_map) do
|
||||
slot = next_free_slot(inv)
|
||||
|
||||
if slot < 0 do
|
||||
{:error, :inventory_full}
|
||||
else
|
||||
# Convert map to item with assigned position
|
||||
assigned_item = Map.put(item_map, :position, slot)
|
||||
new_items = Map.put(inv.items, slot, assigned_item)
|
||||
{:ok, %{inv | items: new_items}, assigned_item}
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Adds an item from the database (preserves position).
|
||||
"""
|
||||
@@ -391,4 +419,103 @@ defmodule Odinsea.Game.Inventory do
|
||||
end
|
||||
|
||||
def equipped_items(%__MODULE__{}), do: []
|
||||
|
||||
@doc """
|
||||
Gets the inventory type based on item ID.
|
||||
"""
|
||||
def get_type_by_item_id(item_id) do
|
||||
InventoryType.from_item_id(item_id)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Checks if inventory has at least the specified quantity of an item.
|
||||
"""
|
||||
def has_item_count(%__MODULE__{} = inv, item_id, quantity) do
|
||||
count_by_id(inv, item_id) >= quantity
|
||||
end
|
||||
|
||||
@doc """
|
||||
Checks if there's at least one free slot in the inventory.
|
||||
"""
|
||||
def has_free_slot(%__MODULE__{} = inv) do
|
||||
next_free_slot(inv) >= 0
|
||||
end
|
||||
|
||||
@doc """
|
||||
Checks if inventory can hold the specified quantity of an item.
|
||||
For stackable items, checks if there's room to stack or a free slot.
|
||||
"""
|
||||
def can_hold_quantity(%__MODULE__{} = inv, item_id, quantity) do
|
||||
# Find existing item to check stack space
|
||||
existing = find_by_id(inv, item_id)
|
||||
slot_max = InventoryType.slot_limit(inv.type)
|
||||
|
||||
if existing do
|
||||
# Check if we can stack
|
||||
space_in_stack = slot_max - existing.quantity
|
||||
remaining = quantity - space_in_stack
|
||||
|
||||
if remaining <= 0 do
|
||||
true
|
||||
else
|
||||
# Need additional slots
|
||||
free_slots = count_free_slots(inv)
|
||||
slots_needed = div(remaining, slot_max) + if rem(remaining, slot_max) > 0, do: 1, else: 0
|
||||
free_slots >= slots_needed
|
||||
end
|
||||
else
|
||||
# Need new slot(s)
|
||||
free_slots = count_free_slots(inv)
|
||||
slots_needed = div(quantity, slot_max) + if rem(quantity, slot_max) > 0, do: 1, else: 0
|
||||
free_slots >= slots_needed
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Removes items by item ID.
|
||||
Returns {:ok, new_inventory, removed_count} or {:error, reason}.
|
||||
"""
|
||||
def remove_by_id(%__MODULE__{} = inv, item_id, quantity) do
|
||||
items_with_id =
|
||||
inv.items
|
||||
|> Map.values()
|
||||
|> Enum.filter(fn item -> item.item_id == item_id end)
|
||||
|> Enum.sort_by(fn item -> item.position end)
|
||||
|
||||
total_available = Enum.map(items_with_id, fn i -> i.quantity end) |> Enum.sum()
|
||||
|
||||
if total_available < quantity do
|
||||
{:error, :insufficient_quantity}
|
||||
else
|
||||
{new_items, removed} = do_remove_by_id(inv.items, items_with_id, quantity, 0)
|
||||
{:ok, %{inv | items: new_items}, removed}
|
||||
end
|
||||
end
|
||||
|
||||
defp do_remove_by_id(items, _items_to_remove, 0, removed), do: {items, removed}
|
||||
defp do_remove_by_id(items, [], _quantity, removed), do: {items, removed}
|
||||
defp do_remove_by_id(items, [item | rest], quantity, removed) do
|
||||
if quantity <= 0 do
|
||||
{items, removed}
|
||||
else
|
||||
to_remove = min(item.quantity, quantity)
|
||||
new_quantity = item.quantity - to_remove
|
||||
|
||||
new_items = if new_quantity <= 0 do
|
||||
Map.delete(items, item.position)
|
||||
else
|
||||
Map.put(items, item.position, %{item | quantity: new_quantity})
|
||||
end
|
||||
|
||||
do_remove_by_id(new_items, rest, quantity - to_remove, removed + to_remove)
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Counts free slots in the inventory.
|
||||
"""
|
||||
def count_free_slots(%__MODULE__{} = inv) do
|
||||
used_slots = map_size(inv.items)
|
||||
inv.slot_limit - used_slots
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user