defmodule Odinsea.Game.Shop do @moduledoc """ Manages NPC shops: buying, selling, and recharging items. Ported from src/server/MapleShop.java TODO: Full implementation requires: - Shop item database loading - Inventory system integration - Item information provider - Price calculations - Buyback system """ use GenServer require Logger # Shop item structure defmodule ShopItem do @moduledoc """ Represents an item in a shop. """ defstruct [ :item_id, :price, :pitch, :quantity, :max_per_slot, :req_item, :req_item_q, :category, :rank ] end # Rechargeable items (throwing stars and bullets) @rechargeable_items MapSet.new([ # Throwing stars 2_070_000, 2_070_001, 2_070_002, 2_070_003, 2_070_004, 2_070_005, 2_070_006, 2_070_007, 2_070_008, 2_070_009, 2_070_010, 2_070_011, 2_070_012, 2_070_013, 2_070_016, 2_070_018, 2_070_019, 2_070_023, 2_070_024, # Bullets 2_330_000, 2_330_001, 2_330_002, 2_330_003, 2_330_004, 2_330_005, 2_330_007, 2_330_008, 2_331_000, 2_332_000 ]) # GenServer client API def start_link(opts) do GenServer.start_link(__MODULE__, opts) end @doc """ Load a shop by NPC ID. """ def load_shop(npc_id) do # TODO: Load shop from database # For now, return a stub shop {:ok, %{id: npc_id, npc_id: npc_id, items: []}} end @doc """ Send shop to client. """ def send_shop(client_pid, shop, npc_id) do # TODO: Send shop packet to client # Should encode shop items and send OPEN_NPC_SHOP packet Logger.debug("Sending shop #{shop.id} to client #{inspect(client_pid)} (STUB)") :ok end @doc """ Handle buying an item from shop. """ def buy_item(client_pid, shop, item_id, quantity) do # TODO: Full buy implementation: # 1. Find shop item by item_id # 2. Validate mount item for job (if mount) # 3. Check inventory space # 4. Check mesos or required item # 5. Deduct cost and give item # 6. Send confirmation packet Logger.debug("Shop buy: item=#{item_id}, qty=#{quantity} (STUB)") :ok end @doc """ Handle selling an item to shop. """ def sell_item(client_pid, shop, inv_type, slot, quantity) do # TODO: Full sell implementation: # 1. Get item from inventory # 2. Validate item can be sold (not pet, not expiring, etc.) # 3. Add to buyback (if applicable) # 4. Remove item from inventory # 5. Calculate sell price # 6. Give mesos to player # 7. Send confirmation packet Logger.debug("Shop sell: type=#{inv_type}, slot=#{slot}, qty=#{quantity} (STUB)") :ok end @doc """ Handle recharging throwing stars or bullets. """ def recharge_item(client_pid, shop, slot) do # TODO: Full recharge implementation: # 1. Get item from USE inventory # 2. Validate item is rechargeable (stars/bullets) # 3. Validate shop sells this item # 4. Calculate recharge cost # 5. Check mesos # 6. Recharge to full quantity # 7. Send confirmation packet Logger.debug("Shop recharge: slot=#{slot} (STUB)") :ok end @doc """ Check if an item is rechargeable. """ def rechargeable?(item_id) do MapSet.member?(@rechargeable_items, item_id) end @doc """ Find a shop item by item ID. """ def find_shop_item(shop, item_id) do Enum.find(shop.items, fn item -> item.item_id == item_id end) end # GenServer callbacks @impl true def init(opts) do shop_id = Keyword.fetch!(opts, :shop_id) npc_id = Keyword.get(opts, :npc_id, shop_id) state = %{ id: shop_id, npc_id: npc_id, items: [] } {:ok, state} end @impl true def handle_call({:add_item, shop_item}, _from, state) do new_items = [shop_item | state.items] {:reply, :ok, %{state | items: new_items}} end @impl true def handle_call(:get_items, _from, state) do {:reply, state.items, state} end @impl true def handle_call(:get_npc_id, _from, state) do {:reply, state.npc_id, state} end end