This commit is contained in:
ra
2026-02-14 19:36:59 -07:00
parent f5b8aeb39d
commit bbd205ecbe
19 changed files with 5191 additions and 554 deletions

View File

@@ -11,7 +11,8 @@ defmodule Odinsea.Database.Context do
import Ecto.Query
alias Odinsea.Repo
alias Odinsea.Database.Schema.{Account, Character}
alias Odinsea.Database.Schema.{Account, Character, InventoryItem}
alias Odinsea.Game.InventoryType
alias Odinsea.Net.Cipher.LoginCrypto
# ==================================================================================================
@@ -481,4 +482,113 @@ defmodule Odinsea.Database.Context do
:ok
end
# ==================================================================================================
# Inventory Operations
# ==================================================================================================
@doc """
Loads all inventory items for a character.
Returns a map of inventory types to lists of items.
Ported from ItemLoader.java
"""
def load_character_inventory(character_id) do
items =
InventoryItem
|> where([i], i.characterid == ^character_id)
|> Repo.all()
# Group by inventory type
items
|> Enum.map(&InventoryItem.to_game_item/1)
|> Enum.group_by(fn item ->
db_item = Enum.find(items, fn db -> db.inventoryitemid == item.id end)
InventoryType.from_type(db_item.inventorytype)
end)
end
@doc """
Gets items for a specific inventory type.
"""
def get_inventory_items(character_id, inv_type) do
type_value = InventoryType.type_value(inv_type)
InventoryItem
|> where([i], i.characterid == ^character_id and i.inventorytype == ^type_value)
|> Repo.all()
|> Enum.map(&InventoryItem.to_game_item/1)
end
@doc """
Gets a single inventory item by ID.
"""
def get_inventory_item(item_id) do
case Repo.get(InventoryItem, item_id) do
nil -> nil
db_item -> InventoryItem.to_game_item(db_item)
end
end
@doc """
Creates a new inventory item.
"""
def create_inventory_item(character_id, inv_type, item) do
attrs = InventoryItem.from_game_item(item, character_id, inv_type)
%InventoryItem{}
|> InventoryItem.changeset(attrs)
|> Repo.insert()
end
@doc """
Updates an existing inventory item.
"""
def update_inventory_item(item_id, updates) do
case Repo.get(InventoryItem, item_id) do
nil -> {:error, :not_found}
db_item ->
db_item
|> InventoryItem.changeset(updates)
|> Repo.update()
end
end
@doc """
Deletes an inventory item.
"""
def delete_inventory_item(item_id) do
Repo.delete_all(from(i in InventoryItem, where: i.inventoryitemid == ^item_id))
end
@doc """
Saves all inventory items for a character.
Used during character save.
"""
def save_character_inventory(character_id, inventories) do
Enum.each(inventories, fn {inv_type, inventory} ->
Enum.each(inventory.items, fn {_pos, item} ->
attrs = InventoryItem.from_game_item(item, character_id, inv_type)
if item.id do
# Update existing
update_inventory_item(item.id, attrs)
else
# Insert new
create_inventory_item(character_id, inv_type, item)
end
end)
end)
:ok
end
@doc """
Gets the count of items for a character.
"""
def count_inventory_items(character_id) do
InventoryItem
|> where([i], i.characterid == ^character_id)
|> Repo.aggregate(:count, :inventoryitemid)
end
end

View File

@@ -0,0 +1,238 @@
defmodule Odinsea.Database.Schema.InventoryItem do
@moduledoc """
Ecto schema for the inventoryitems table.
Stores all items for all characters.
"""
use Ecto.Schema
import Ecto.Changeset
@primary_key {:inventoryitemid, :integer, autogenerate: false}
schema "inventoryitems" do
field :characterid, :integer
field :itemid, :integer
field :inventorytype, :integer
field :position, :integer
field :quantity, :integer
field :owner, :string, default: ""
field :petid, :integer, default: -1
field :flag, :integer, default: 0
field :expiration, :integer, default: -1
field :gift_from, :string, source: :giftFrom, default: ""
field :uniqueid, :integer, default: -1
# Equipment-specific fields (stored in the same table)
field :upgradeslots, :integer, default: 0
field :level, :integer, default: 0
field :str, :integer, default: 0
field :dex, :integer, default: 0
field :int, :integer, default: 0
field :luk, :integer, default: 0
field :hp, :integer, default: 0
field :mp, :integer, default: 0
field :watk, :integer, default: 0
field :matk, :integer, default: 0
field :wdef, :integer, default: 0
field :mdef, :integer, default: 0
field :acc, :integer, default: 0
field :avoid, :integer, default: 0
field :hands, :integer, default: 0
field :speed, :integer, default: 0
field :jump, :integer, default: 0
field :locked, :integer, default: 0
field :vicioushammer, :integer, default: 0
field :itemexp, :integer, default: 0
field :durability, :integer, default: -1
field :enhance, :integer, default: 0
field :potential1, :integer, default: 0
field :potential2, :integer, default: 0
field :potential3, :integer, default: 0
field :hp_r, :integer, source: :hpR, default: 0
field :mp_r, :integer, source: :mpR, default: 0
field :incskill, :integer, default: -1
field :charmexp, :integer, default: -1
field :pvpdamage, :integer, default: 0
end
@doc """
Changeset for creating a new inventory item.
"""
def changeset(item, attrs) do
item
|> cast(attrs, [
:inventoryitemid,
:characterid,
:itemid,
:inventorytype,
:position,
:quantity,
:owner,
:petid,
:flag,
:expiration,
:gift_from,
:uniqueid,
:upgradeslots,
:level,
:str,
:dex,
:int,
:luk,
:hp,
:mp,
:watk,
:matk,
:wdef,
:mdef,
:acc,
:avoid,
:hands,
:speed,
:jump,
:vicioushammer,
:itemexp,
:durability,
:enhance,
:potential1,
:potential2,
:potential3,
:hp_r,
:mp_r,
:incskill,
:charmexp,
:pvpdamage
])
|> validate_required([:characterid, :itemid, :inventorytype, :position, :quantity])
end
@doc """
Converts a database item to a game Item or Equip struct.
"""
def to_game_item(%__MODULE__{} = db_item) do
base_fields = %{
id: db_item.inventoryitemid,
item_id: db_item.itemid,
position: db_item.position,
quantity: db_item.quantity,
flag: db_item.flag,
unique_id: db_item.uniqueid,
expiration: db_item.expiration,
owner: db_item.owner || "",
gift_from: db_item.gift_from || "",
gm_log: "",
inventory_id: db_item.inventoryitemid,
pet: nil
}
if is_equip?(db_item) do
struct(Odinsea.Game.Equip, Map.merge(base_fields, equip_fields(db_item)))
else
struct(Odinsea.Game.Item, base_fields)
end
end
defp is_equip?(db_item) do
# Equipment items have inventory type 1 (EQUIP) or -1 (EQUIPPED)
# or have non-zero equip stats
db_item.inventorytype in [1, -1] or
db_item.upgradeslots > 0 or
db_item.str > 0 or
db_item.dex > 0 or
db_item.int > 0 or
db_item.luk > 0 or
db_item.watk > 0 or
db_item.wdef > 0
end
defp equip_fields(db_item) do
%{
upgrade_slots: db_item.upgradeslots,
level: db_item.level,
vicious_hammer: db_item.vicioushammer,
enhance: db_item.enhance,
str: db_item.str,
dex: db_item.dex,
int: db_item.int,
luk: db_item.luk,
hp: db_item.hp,
mp: db_item.mp,
watk: db_item.watk,
matk: db_item.matk,
wdef: db_item.wdef,
mdef: db_item.mdef,
acc: db_item.acc,
avoid: db_item.avoid,
hands: db_item.hands,
speed: db_item.speed,
jump: db_item.jump,
hp_r: db_item.hp_r,
mp_r: db_item.mp_r,
charm_exp: db_item.charmexp,
pvp_damage: db_item.pvpdamage,
item_exp: db_item.itemexp,
durability: db_item.durability,
inc_skill: db_item.incskill,
potential1: db_item.potential1,
potential2: db_item.potential2,
potential3: db_item.potential3,
ring: nil,
android: nil
}
end
@doc """
Converts a game Item or Equip to database attributes.
"""
def from_game_item(%Odinsea.Game.Item{} = item, character_id, inv_type) do
%{
inventoryitemid: item.id,
characterid: character_id,
itemid: item.item_id,
inventorytype: Odinsea.Game.InventoryType.type_value(inv_type),
position: item.position,
quantity: item.quantity,
owner: item.owner,
petid: if(item.pet, do: item.pet.unique_id, else: -1),
flag: item.flag,
expiration: item.expiration,
gift_from: item.gift_from,
uniqueid: item.unique_id
}
end
def from_game_item(%Odinsea.Game.Equip{} = equip, character_id, inv_type) do
base = from_game_item(%Odinsea.Game.Item{} = equip, character_id, inv_type)
Map.merge(base, %{
upgradeslots: equip.upgrade_slots,
level: equip.level,
str: equip.str,
dex: equip.dex,
int: equip.int,
luk: equip.luk,
hp: equip.hp,
mp: equip.mp,
watk: equip.watk,
matk: equip.matk,
wdef: equip.wdef,
mdef: equip.mdef,
acc: equip.acc,
avoid: equip.avoid,
hands: equip.hands,
speed: equip.speed,
jump: equip.jump,
vicioushammer: equip.vicious_hammer,
itemexp: equip.item_exp,
durability: equip.durability,
enhance: equip.enhance,
potential1: equip.potential1,
potential2: equip.potential2,
potential3: equip.potential3,
hp_r: equip.hp_r,
mp_r: equip.mp_r,
incskill: equip.inc_skill,
charmexp: equip.charm_exp,
pvpdamage: equip.pvp_damage
})
end
end