184 lines
4.0 KiB
Elixir
184 lines
4.0 KiB
Elixir
defmodule Odinsea.Game.Storage do
|
|
@moduledoc """
|
|
Manages personal storage (bank) for characters.
|
|
Ported from src/server/MapleStorage.java
|
|
|
|
TODO: Full implementation requires:
|
|
- Database persistence
|
|
- Inventory system integration
|
|
- Item serialization
|
|
- Slot management
|
|
- Meso storage
|
|
"""
|
|
|
|
use GenServer
|
|
require Logger
|
|
|
|
@default_slots 4
|
|
@max_slots 48
|
|
@storage_fee 100
|
|
|
|
defstruct [
|
|
:character_id,
|
|
:account_id,
|
|
:slots,
|
|
:meso,
|
|
items: %{}
|
|
]
|
|
|
|
# Client API
|
|
|
|
def start_link(opts) do
|
|
GenServer.start_link(__MODULE__, opts)
|
|
end
|
|
|
|
@doc """
|
|
Load storage for a character.
|
|
"""
|
|
def load_storage(account_id) do
|
|
# TODO: Load from database
|
|
{:ok, %__MODULE__{account_id: account_id, slots: @default_slots, meso: 0, items: %{}}}
|
|
end
|
|
|
|
@doc """
|
|
Send storage to client.
|
|
"""
|
|
def send_storage(client_pid, storage) do
|
|
# TODO: Send OPEN_STORAGE packet
|
|
Logger.debug("Sending storage to client #{inspect(client_pid)} (STUB)")
|
|
:ok
|
|
end
|
|
|
|
@doc """
|
|
Take out an item from storage.
|
|
"""
|
|
def take_out_item(storage, slot) do
|
|
# TODO: Full implementation:
|
|
# 1. Validate slot
|
|
# 2. Get item from storage
|
|
# 3. Check inventory space
|
|
# 4. Remove from storage
|
|
# 5. Add to inventory
|
|
# 6. Send update packet
|
|
|
|
Logger.debug("Storage take out: slot=#{slot} (STUB)")
|
|
{:ok, storage}
|
|
end
|
|
|
|
@doc """
|
|
Store an item in storage.
|
|
"""
|
|
def store_item(storage, item, slot) do
|
|
# TODO: Full implementation:
|
|
# 1. Check storage is not full
|
|
# 2. Validate item (not pet, not trade-restricted)
|
|
# 3. Charge storage fee (100 mesos)
|
|
# 4. Remove karma flags if applicable
|
|
# 5. Remove from inventory
|
|
# 6. Add to storage
|
|
# 7. Send update packet
|
|
|
|
Logger.debug("Storage store: item=#{inspect(item)}, slot=#{slot} (STUB)")
|
|
{:ok, storage}
|
|
end
|
|
|
|
@doc """
|
|
Arrange/sort storage items.
|
|
"""
|
|
def arrange(storage) do
|
|
# TODO: Sort items by type/ID
|
|
Logger.debug("Storage arrange (STUB)")
|
|
{:ok, storage}
|
|
end
|
|
|
|
@doc """
|
|
Deposit or withdraw mesos.
|
|
"""
|
|
def transfer_meso(storage, amount) do
|
|
# TODO: Full implementation:
|
|
# 1. Validate amount (positive = withdraw, negative = deposit)
|
|
# 2. Check character has mesos (if depositing)
|
|
# 3. Check storage has mesos (if withdrawing)
|
|
# 4. Handle overflow protection
|
|
# 5. Transfer mesos
|
|
# 6. Send update packet
|
|
|
|
Logger.debug("Storage meso transfer: #{amount} (STUB)")
|
|
{:ok, storage}
|
|
end
|
|
|
|
@doc """
|
|
Close storage.
|
|
"""
|
|
def close(storage) do
|
|
# TODO: Save to database
|
|
Logger.debug("Storage close (STUB)")
|
|
:ok
|
|
end
|
|
|
|
@doc """
|
|
Check if storage is full.
|
|
"""
|
|
def full?(storage) do
|
|
map_size(storage.items) >= storage.slots
|
|
end
|
|
|
|
@doc """
|
|
Get next available slot.
|
|
"""
|
|
def next_slot(storage) do
|
|
Enum.find(1..storage.slots, fn slot -> !Map.has_key?(storage.items, slot) end)
|
|
end
|
|
|
|
@doc """
|
|
Find item by ID in storage.
|
|
"""
|
|
def find_by_id(storage, item_id) do
|
|
Enum.find(storage.items, fn {_slot, item} -> item.item_id == item_id end)
|
|
end
|
|
|
|
# GenServer callbacks
|
|
|
|
@impl true
|
|
def init(opts) do
|
|
account_id = Keyword.fetch!(opts, :account_id)
|
|
|
|
state = %__MODULE__{
|
|
account_id: account_id,
|
|
slots: @default_slots,
|
|
meso: 0,
|
|
items: %{}
|
|
}
|
|
|
|
{:ok, state}
|
|
end
|
|
|
|
@impl true
|
|
def handle_call({:add_item, slot, item}, _from, state) do
|
|
if slot <= state.slots and !Map.has_key?(state.items, slot) do
|
|
new_items = Map.put(state.items, slot, item)
|
|
{:reply, :ok, %{state | items: new_items}}
|
|
else
|
|
{:reply, {:error, :invalid_slot}, state}
|
|
end
|
|
end
|
|
|
|
@impl true
|
|
def handle_call({:remove_item, slot}, _from, state) do
|
|
case Map.pop(state.items, slot) do
|
|
{nil, _} -> {:reply, {:error, :no_item}, state}
|
|
{item, new_items} -> {:reply, {:ok, item}, %{state | items: new_items}}
|
|
end
|
|
end
|
|
|
|
@impl true
|
|
def handle_call({:set_meso, meso}, _from, state) do
|
|
{:reply, :ok, %{state | meso: meso}}
|
|
end
|
|
|
|
@impl true
|
|
def handle_call(:get_meso, _from, state) do
|
|
{:reply, state.meso, state}
|
|
end
|
|
end
|