Files
odinsea-elixir/lib/odinsea/game/drop.ex
2026-02-14 23:12:33 -07:00

201 lines
5.3 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)
"""
@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
}
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
]
# 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)
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
}
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
}
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.
"""
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
true
end
end
@doc """
Checks if this is a meso drop.
"""
def meso?(%__MODULE__{} = drop) do
drop.meso > 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.
"""
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
end