kimi gone wild
This commit is contained in:
186
lib/odinsea/shop/cash_item.ex
Normal file
186
lib/odinsea/shop/cash_item.ex
Normal file
@@ -0,0 +1,186 @@
|
||||
defmodule Odinsea.Shop.CashItem do
|
||||
@moduledoc """
|
||||
Cash Shop Item struct and utilities.
|
||||
|
||||
Represents an item available for purchase in the Cash Shop.
|
||||
Ported from server/CashItemInfo.java and server/cash/CashCommodity.java
|
||||
"""
|
||||
|
||||
@type t :: %__MODULE__{
|
||||
sn: integer(),
|
||||
item_id: integer(),
|
||||
price: integer(),
|
||||
count: integer(),
|
||||
period: integer(),
|
||||
gender: integer(),
|
||||
on_sale: boolean(),
|
||||
class: integer(),
|
||||
priority: integer(),
|
||||
is_package: boolean(),
|
||||
meso_price: integer(),
|
||||
bonus: integer(),
|
||||
for_premium_user: integer(),
|
||||
limit: integer(),
|
||||
extra_flags: integer()
|
||||
}
|
||||
|
||||
defstruct [
|
||||
:sn,
|
||||
:item_id,
|
||||
:price,
|
||||
:count,
|
||||
:period,
|
||||
:gender,
|
||||
:on_sale,
|
||||
:class,
|
||||
:priority,
|
||||
:is_package,
|
||||
:meso_price,
|
||||
:bonus,
|
||||
:for_premium_user,
|
||||
:limit,
|
||||
:extra_flags
|
||||
]
|
||||
|
||||
@doc """
|
||||
Creates a new CashItem struct from parsed data.
|
||||
"""
|
||||
@spec new(map()) :: t()
|
||||
def new(attrs) do
|
||||
%__MODULE__{
|
||||
sn: Map.get(attrs, :sn, 0),
|
||||
item_id: Map.get(attrs, :item_id, 0),
|
||||
price: Map.get(attrs, :price, 0),
|
||||
count: Map.get(attrs, :count, 1),
|
||||
period: Map.get(attrs, :period, 0),
|
||||
gender: Map.get(attrs, :gender, 2),
|
||||
on_sale: Map.get(attrs, :on_sale, false),
|
||||
class: Map.get(attrs, :class, 0),
|
||||
priority: Map.get(attrs, :priority, 0),
|
||||
is_package: Map.get(attrs, :is_package, false),
|
||||
meso_price: Map.get(attrs, :meso_price, 0),
|
||||
bonus: Map.get(attrs, :bonus, 0),
|
||||
for_premium_user: Map.get(attrs, :for_premium_user, 0),
|
||||
limit: Map.get(attrs, :limit, 0),
|
||||
extra_flags: Map.get(attrs, :extra_flags, 0)
|
||||
}
|
||||
end
|
||||
|
||||
@doc """
|
||||
Checks if the item gender matches the player's gender.
|
||||
Gender: 0 = male, 1 = female, 2 = both
|
||||
"""
|
||||
@spec gender_matches?(t(), integer()) :: boolean()
|
||||
def gender_matches?(%__MODULE__{gender: 2}, _player_gender), do: true
|
||||
def gender_matches?(%__MODULE__{gender: gender}, player_gender), do: gender == player_gender
|
||||
|
||||
@doc """
|
||||
Calculates the flags value for packet encoding.
|
||||
This follows the Java CashCommodity flag calculation.
|
||||
"""
|
||||
@spec calculate_flags(t()) :: integer()
|
||||
def calculate_flags(item) do
|
||||
flags = item.extra_flags || 0
|
||||
|
||||
flags = if item.item_id > 0, do: Bitwise.bor(flags, 0x1), else: flags
|
||||
flags = if item.count > 0, do: Bitwise.bor(flags, 0x2), else: flags
|
||||
flags = if item.price > 0, do: Bitwise.bor(flags, 0x4), else: flags
|
||||
flags = if item.bonus > 0, do: Bitwise.bor(flags, 0x8), else: flags
|
||||
flags = if item.priority >= 0, do: Bitwise.bor(flags, 0x10), else: flags
|
||||
flags = if item.period > 0, do: Bitwise.bor(flags, 0x20), else: flags
|
||||
# 0x40 = nMaplePoint (not used)
|
||||
flags = if item.meso_price > 0, do: Bitwise.bor(flags, 0x80), else: flags
|
||||
flags = if item.for_premium_user > 0, do: Bitwise.bor(flags, 0x100), else: flags
|
||||
flags = if item.gender >= 0, do: Bitwise.bor(flags, 0x200), else: flags
|
||||
flags = if item.on_sale, do: Bitwise.bor(flags, 0x400), else: flags
|
||||
flags = if item.class >= -1 && item.class <= 3, do: Bitwise.bor(flags, 0x800), else: flags
|
||||
flags = if item.limit > 0, do: Bitwise.bor(flags, 0x1000), else: flags
|
||||
# 0x2000, 0x4000, 0x8000 = nPbCash, nPbPoint, nPbGift (not used)
|
||||
flags = if item.is_package, do: Bitwise.bor(flags, 0x40000), else: flags
|
||||
# 0x80000, 0x100000 = term start/end (not used)
|
||||
|
||||
flags
|
||||
end
|
||||
|
||||
@doc """
|
||||
Checks if this is a cash item (premium currency item).
|
||||
"""
|
||||
@spec cash_item?(integer()) :: boolean()
|
||||
def cash_item?(item_id) do
|
||||
# Cash items typically have IDs in certain ranges
|
||||
# This is a simplified check - full implementation would check WZ data
|
||||
item_id >= 500_000 && item_id < 600_000
|
||||
end
|
||||
|
||||
@doc """
|
||||
Checks if this item is a pet.
|
||||
"""
|
||||
@spec pet?(t()) :: boolean()
|
||||
def pet?(%__MODULE__{item_id: item_id}) do
|
||||
item_id >= 5_000_000 && item_id < 5_100_000
|
||||
end
|
||||
|
||||
@doc """
|
||||
Checks if this is a permanent pet.
|
||||
"""
|
||||
@spec permanent_pet?(t()) :: boolean()
|
||||
def permanent_pet?(%__MODULE__{item_id: item_id}) do
|
||||
item_id >= 5_000_100 && item_id < 5_000_200
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets the effective period for this item.
|
||||
Returns period in days, or special values for permanent items.
|
||||
"""
|
||||
@spec effective_period(t()) :: integer()
|
||||
def effective_period(%__MODULE__{period: period} = item) do
|
||||
cond do
|
||||
# Permanent pets have special handling
|
||||
permanent_pet?(item) -> 20_000
|
||||
# Default period for non-equip cash items that aren't permanent
|
||||
period <= 0 && !equip_item?(item) -> 90
|
||||
true -> period
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Checks if this item is equipment.
|
||||
"""
|
||||
@spec equip_item?(t()) :: boolean()
|
||||
def equip_item?(%__MODULE__{item_id: item_id}) do
|
||||
item_id >= 1_000_000
|
||||
end
|
||||
|
||||
@doc """
|
||||
Calculates the expiration timestamp for this item.
|
||||
"""
|
||||
@spec expiration_time(t()) :: integer()
|
||||
def expiration_time(item) do
|
||||
period = effective_period(item)
|
||||
|
||||
if period > 0 do
|
||||
Odinsea.now() + period * 24 * 60 * 60 * 1000
|
||||
else
|
||||
-1
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Applies modified item info (from cashshop_modified_items table).
|
||||
"""
|
||||
@spec apply_mods(t(), map()) :: t()
|
||||
def apply_mods(item, mods) do
|
||||
%__MODULE__{
|
||||
item
|
||||
| item_id: Map.get(mods, :item_id, item.item_id),
|
||||
price: Map.get(mods, :price, item.price),
|
||||
count: Map.get(mods, :count, item.count),
|
||||
period: Map.get(mods, :period, item.period),
|
||||
gender: Map.get(mods, :gender, item.gender),
|
||||
on_sale: Map.get(mods, :on_sale, item.on_sale),
|
||||
class: Map.get(mods, :class, item.class),
|
||||
priority: Map.get(mods, :priority, item.priority),
|
||||
is_package: Map.get(mods, :is_package, item.is_package)
|
||||
}
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user