191 lines
4.7 KiB
Elixir
191 lines
4.7 KiB
Elixir
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
|