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