262 lines
7.0 KiB
Elixir
262 lines
7.0 KiB
Elixir
defmodule Odinsea.Game.Drop do
|
|
@moduledoc """
|
|
Represents a drop item on a map.
|
|
Ported from Java server.maps.MapleMapItem
|
|
|
|
Drops can be:
|
|
- Item drops (equipment, use, setup, etc items)
|
|
- Meso drops (gold/money)
|
|
|
|
Drop ownership determines who can loot:
|
|
- Type 0: Timeout for non-owner only
|
|
- Type 1: Timeout for non-owner's party
|
|
- Type 2: Free-for-all (FFA)
|
|
- Type 3: Explosive/FFA (instant FFA)
|
|
"""
|
|
|
|
alias Odinsea.Game.Item
|
|
|
|
@type t :: %__MODULE__{
|
|
oid: integer(),
|
|
item_id: integer(),
|
|
quantity: integer(),
|
|
meso: integer(),
|
|
owner_id: integer(),
|
|
drop_type: integer(),
|
|
position: %{x: integer(), y: integer()},
|
|
source_position: %{x: integer(), y: integer()} | nil,
|
|
quest_id: integer(),
|
|
player_drop: boolean(),
|
|
individual_reward: boolean(),
|
|
picked_up: boolean(),
|
|
created_at: integer(),
|
|
expire_time: integer() | nil,
|
|
public_time: integer() | nil,
|
|
dropper_oid: integer() | nil,
|
|
# Item struct for item drops (nil for meso drops)
|
|
item: Item.t() | nil
|
|
}
|
|
|
|
defstruct [
|
|
:oid,
|
|
:item_id,
|
|
:quantity,
|
|
:meso,
|
|
:owner_id,
|
|
:drop_type,
|
|
:position,
|
|
:source_position,
|
|
:quest_id,
|
|
:player_drop,
|
|
:individual_reward,
|
|
:picked_up,
|
|
:created_at,
|
|
:expire_time,
|
|
:public_time,
|
|
:dropper_oid,
|
|
:item
|
|
]
|
|
|
|
# Default drop expiration times (milliseconds)
|
|
@default_expire_time 120_000 # 2 minutes
|
|
@default_public_time 60_000 # 1 minute until FFA
|
|
|
|
@doc """
|
|
Creates a new item drop.
|
|
"""
|
|
def new_item_drop(oid, item_id, quantity, owner_id, position, opts \\ []) do
|
|
drop_type = Keyword.get(opts, :drop_type, 0)
|
|
quest_id = Keyword.get(opts, :quest_id, -1)
|
|
individual_reward = Keyword.get(opts, :individual_reward, false)
|
|
dropper_oid = Keyword.get(opts, :dropper_oid, nil)
|
|
source_position = Keyword.get(opts, :source_position, nil)
|
|
item = Keyword.get(opts, :item, nil)
|
|
now = System.system_time(:millisecond)
|
|
|
|
%__MODULE__{
|
|
oid: oid,
|
|
item_id: item_id,
|
|
quantity: quantity,
|
|
meso: 0,
|
|
owner_id: owner_id,
|
|
drop_type: drop_type,
|
|
position: position,
|
|
source_position: source_position,
|
|
quest_id: quest_id,
|
|
player_drop: false,
|
|
individual_reward: individual_reward,
|
|
picked_up: false,
|
|
created_at: now,
|
|
expire_time: now + @default_expire_time,
|
|
public_time: if(drop_type < 2, do: now + @default_public_time, else: 0),
|
|
dropper_oid: dropper_oid,
|
|
item: item
|
|
}
|
|
end
|
|
|
|
@doc """
|
|
Creates a new meso drop.
|
|
"""
|
|
def new_meso_drop(oid, amount, owner_id, position, opts \\ []) do
|
|
drop_type = Keyword.get(opts, :drop_type, 0)
|
|
individual_reward = Keyword.get(opts, :individual_reward, false)
|
|
dropper_oid = Keyword.get(opts, :dropper_oid, nil)
|
|
source_position = Keyword.get(opts, :source_position, nil)
|
|
now = System.system_time(:millisecond)
|
|
|
|
%__MODULE__{
|
|
oid: oid,
|
|
item_id: 0,
|
|
quantity: 0,
|
|
meso: amount,
|
|
owner_id: owner_id,
|
|
drop_type: drop_type,
|
|
position: position,
|
|
source_position: source_position,
|
|
quest_id: -1,
|
|
player_drop: false,
|
|
individual_reward: individual_reward,
|
|
picked_up: false,
|
|
created_at: now,
|
|
expire_time: now + @default_expire_time,
|
|
public_time: if(drop_type < 2, do: now + @default_public_time, else: 0),
|
|
dropper_oid: dropper_oid,
|
|
item: nil
|
|
}
|
|
end
|
|
|
|
@doc """
|
|
Marks the drop as picked up.
|
|
"""
|
|
def mark_picked_up(%__MODULE__{} = drop) do
|
|
%{drop | picked_up: true}
|
|
end
|
|
|
|
@doc """
|
|
Checks if the drop should expire based on current time.
|
|
"""
|
|
def should_expire?(%__MODULE__{} = drop, now) do
|
|
not drop.picked_up and drop.expire_time != nil and drop.expire_time < now
|
|
end
|
|
|
|
@doc """
|
|
Checks if the drop has become public (FFA) based on current time.
|
|
"""
|
|
def is_public_time?(%__MODULE__{} = drop, now) do
|
|
not drop.picked_up and drop.public_time != nil and drop.public_time < now
|
|
end
|
|
|
|
@doc """
|
|
Checks if a drop is visible to a specific character.
|
|
Considers quest requirements and individual rewards.
|
|
|
|
For quest items, the character must have the quest started.
|
|
For individual rewards, only the owner can see the drop.
|
|
"""
|
|
def visible_to?(%__MODULE__{} = drop, character_id, quest_status) do
|
|
# Individual rewards only visible to owner
|
|
if drop.individual_reward do
|
|
drop.owner_id == character_id
|
|
else
|
|
# Check quest requirement
|
|
if drop.quest_id > 0 do
|
|
# Only visible if character has quest started (status 1)
|
|
Map.get(quest_status, drop.quest_id, 0) == 1
|
|
else
|
|
true
|
|
end
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Checks if this is a meso drop.
|
|
"""
|
|
def meso?(%__MODULE__{} = drop) do
|
|
drop.meso > 0
|
|
end
|
|
|
|
@doc """
|
|
Checks if this is an item drop.
|
|
"""
|
|
def item?(%__MODULE__{} = drop) do
|
|
drop.meso == 0 and drop.item_id > 0
|
|
end
|
|
|
|
@doc """
|
|
Gets the display ID (item_id for items, meso amount for meso).
|
|
"""
|
|
def display_id(%__MODULE__{} = drop) do
|
|
if drop.meso > 0 do
|
|
drop.meso
|
|
else
|
|
drop.item_id
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Checks if a character can loot this drop based on ownership rules.
|
|
|
|
Drop types:
|
|
- 0: Owner only (until timeout)
|
|
- 1: Owner's party (until timeout)
|
|
- 2: Free-for-all (FFA)
|
|
- 3: Explosive (instant FFA)
|
|
"""
|
|
def can_loot?(%__MODULE__{} = drop, character_id, now) do
|
|
# If already picked up, can't loot
|
|
if drop.picked_up do
|
|
false
|
|
else
|
|
# Check ownership rules based on drop type
|
|
case drop.drop_type do
|
|
0 ->
|
|
# Timeout for non-owner only
|
|
drop.owner_id == character_id or is_public_time?(drop, now)
|
|
1 ->
|
|
# Timeout for non-owner's party (simplified - treat as FFA after timeout)
|
|
drop.owner_id == character_id or is_public_time?(drop, now)
|
|
2 ->
|
|
# FFA
|
|
true
|
|
3 ->
|
|
# Explosive/FFA (instant FFA)
|
|
true
|
|
_ ->
|
|
# Default to owner-only
|
|
drop.owner_id == character_id
|
|
end
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Checks if a character can loot this drop, including party check.
|
|
Requires party information to validate party drops.
|
|
"""
|
|
def can_loot_with_party?(%__MODULE__{} = drop, character_id, party_id, party_members, now) do
|
|
if drop.picked_up do
|
|
false
|
|
else
|
|
case drop.drop_type do
|
|
0 ->
|
|
# Timeout for non-owner only
|
|
drop.owner_id == character_id or is_public_time?(drop, now)
|
|
1 ->
|
|
# Timeout for non-owner's party
|
|
owner_in_same_party = drop.owner_id in party_members
|
|
(owner_in_same_party and party_id != nil) or
|
|
drop.owner_id == character_id or
|
|
is_public_time?(drop, now)
|
|
2 ->
|
|
# FFA
|
|
true
|
|
3 ->
|
|
# Explosive/FFA (instant FFA)
|
|
true
|
|
_ ->
|
|
# Default to owner-only
|
|
drop.owner_id == character_id
|
|
end
|
|
end
|
|
end
|
|
end
|