fix login issue*
This commit is contained in:
@@ -40,7 +40,7 @@ defmodule Odinsea.Login.Packets do
|
||||
|> Out.encode_buffer(recv_iv)
|
||||
|> Out.encode_buffer(send_iv)
|
||||
|> Out.encode_byte(Server.maple_locale())
|
||||
|> Out.to_data()
|
||||
|> Out.to_iodata()
|
||||
end
|
||||
|
||||
@doc """
|
||||
@@ -49,27 +49,26 @@ defmodule Odinsea.Login.Packets do
|
||||
"""
|
||||
def get_ping do
|
||||
Out.new(Opcodes.lp_alive_req())
|
||||
|> Out.to_data()
|
||||
|> Out.to_iodata()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Sends the login background image path to the client.
|
||||
"""
|
||||
def get_login_background(background_path) do
|
||||
# Note: In Java this uses LoopbackPacket.LOGIN_AUTH
|
||||
# Need to verify the correct opcode for this
|
||||
Out.new(Opcodes.lp_set_client_key()) # TODO: Verify opcode
|
||||
# Uses LOGIN_AUTH (0x17) opcode
|
||||
Out.new(Opcodes.lp_login_auth())
|
||||
|> Out.encode_string(background_path)
|
||||
|> Out.to_data()
|
||||
|> Out.to_iodata()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Sends the RSA public key to the client for password encryption.
|
||||
"""
|
||||
def get_rsa_key(public_key) do
|
||||
Out.new(Opcodes.lp_set_client_key())
|
||||
Out.new(Opcodes.lp_rsa_key())
|
||||
|> Out.encode_string(public_key)
|
||||
|> Out.to_data()
|
||||
|> Out.to_iodata()
|
||||
end
|
||||
|
||||
# ==================================================================================================
|
||||
@@ -104,13 +103,13 @@ defmodule Odinsea.Login.Packets do
|
||||
|
||||
reason == 7 ->
|
||||
# Already logged in
|
||||
Out.encode_bytes(packet, <<0, 0, 0, 0, 0>>)
|
||||
Out.encode_buffer(packet, <<0, 0, 0, 0, 0>>)
|
||||
|
||||
true ->
|
||||
packet
|
||||
end
|
||||
|
||||
Out.to_data(packet)
|
||||
Out.to_iodata(packet)
|
||||
end
|
||||
|
||||
@doc """
|
||||
@@ -122,7 +121,7 @@ defmodule Odinsea.Login.Packets do
|
||||
|> Out.encode_int(0)
|
||||
|> Out.encode_short(reason)
|
||||
|> Out.encode_buffer(<<1, 1, 1, 1, 0>>)
|
||||
|> Out.to_data()
|
||||
|> Out.to_iodata()
|
||||
end
|
||||
|
||||
@doc """
|
||||
@@ -134,7 +133,7 @@ defmodule Odinsea.Login.Packets do
|
||||
|> Out.encode_short(0)
|
||||
|> Out.encode_byte(reason)
|
||||
|> Out.encode_long(timestamp_till)
|
||||
|> Out.to_data()
|
||||
|> Out.to_iodata()
|
||||
end
|
||||
|
||||
@doc """
|
||||
@@ -147,26 +146,23 @@ defmodule Odinsea.Login.Packets do
|
||||
- `is_gm` - Admin/GM status
|
||||
- `second_password` - Second password (nil if not set)
|
||||
"""
|
||||
def get_auth_success(account_id, account_name, gender, is_gm, second_password) do
|
||||
def get_auth_success(account_id, account_name, gender, is_gm, _second_password) do
|
||||
admin_byte = if is_gm, do: 1, else: 0
|
||||
spw_byte = get_second_password_byte(second_password)
|
||||
|
||||
Out.new(Opcodes.lp_check_password_result())
|
||||
|> Out.encode_bytes(<<0, 0, 0, 0, 0, 0>>) # GMS specific
|
||||
|> Out.encode_byte(0) # MSEA: 1 byte padding (not 6 like GMS)
|
||||
|> Out.encode_int(account_id)
|
||||
|> Out.encode_byte(gender)
|
||||
|> Out.encode_byte(admin_byte) # Admin byte - Find, Trade, etc.
|
||||
|> Out.encode_short(2) # GMS: 2 for existing accounts, 0 for new
|
||||
# NO encode_short(2) for MSEA - this is GMS only!
|
||||
|> Out.encode_byte(admin_byte) # Admin byte - Commands
|
||||
|> Out.encode_string(account_name)
|
||||
|> Out.encode_int(3) # 3 for existing accounts, 0 for new
|
||||
|> Out.encode_bytes(<<0, 0, 0, 0, 0, 0>>)
|
||||
|> Out.encode_long(get_time(System.system_time(:millisecond))) # Account creation date
|
||||
|> Out.encode_int(4) # 4 for existing accounts, 0 for new
|
||||
|> Out.encode_byte(1) # 1 = PIN disabled, 0 = PIN enabled
|
||||
|> Out.encode_byte(spw_byte) # Second password status
|
||||
|> Out.encode_long(:rand.uniform(1_000_000_000_000_000_000)) # Random long for anti-hack
|
||||
|> Out.to_data()
|
||||
|> Out.encode_buffer(<<0, 0, 0, 0, 0, 0>>) # 6 bytes padding
|
||||
# MSEA ending (different from GMS - no time long, PIN/SPW bytes, random long)
|
||||
|> Out.encode_short(0)
|
||||
|> Out.encode_int(get_current_date())
|
||||
|> Out.to_iodata()
|
||||
end
|
||||
|
||||
# ==================================================================================================
|
||||
@@ -187,14 +183,14 @@ defmodule Odinsea.Login.Packets do
|
||||
last_channel = get_last_channel(channel_load)
|
||||
|
||||
packet =
|
||||
Out.new(Opcodes.lp_world_information())
|
||||
Out.new(Opcodes.lp_server_list())
|
||||
|> Out.encode_byte(server_id)
|
||||
|> Out.encode_string(world_name)
|
||||
|> Out.encode_byte(flag)
|
||||
|> Out.encode_string(event_message)
|
||||
|> Out.encode_short(100) # EXP rate display
|
||||
|> Out.encode_short(100) # Drop rate display
|
||||
|> Out.encode_byte(0) # GMS specific
|
||||
# NO encode_byte(0) for MSEA - this is GMS only!
|
||||
|> Out.encode_byte(last_channel)
|
||||
|
||||
# Encode channel list
|
||||
@@ -212,16 +208,16 @@ defmodule Odinsea.Login.Packets do
|
||||
packet
|
||||
|> Out.encode_short(0) # Balloon message size
|
||||
|> Out.encode_int(0)
|
||||
|> Out.to_data()
|
||||
|> Out.to_iodata()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Sends the end-of-server-list marker.
|
||||
"""
|
||||
def get_end_of_server_list do
|
||||
Out.new(Opcodes.lp_world_information())
|
||||
Out.new(Opcodes.lp_server_list())
|
||||
|> Out.encode_byte(0xFF)
|
||||
|> Out.to_data()
|
||||
|> Out.to_iodata()
|
||||
end
|
||||
|
||||
@doc """
|
||||
@@ -233,9 +229,9 @@ defmodule Odinsea.Login.Packets do
|
||||
- 2: Full
|
||||
"""
|
||||
def get_server_status(status) do
|
||||
Out.new(Opcodes.lp_select_world_result())
|
||||
Out.new(Opcodes.lp_server_status())
|
||||
|> Out.encode_short(status)
|
||||
|> Out.to_data()
|
||||
|> Out.to_iodata()
|
||||
end
|
||||
|
||||
@doc """
|
||||
@@ -244,7 +240,7 @@ defmodule Odinsea.Login.Packets do
|
||||
def get_latest_connected_world(world_id) do
|
||||
Out.new(Opcodes.lp_latest_connected_world())
|
||||
|> Out.encode_int(world_id)
|
||||
|> Out.to_data()
|
||||
|> Out.to_iodata()
|
||||
end
|
||||
|
||||
@doc """
|
||||
@@ -263,7 +259,7 @@ defmodule Odinsea.Login.Packets do
|
||||
Out.encode_byte(packet, 0)
|
||||
end
|
||||
|
||||
Out.to_data(packet)
|
||||
Out.to_iodata(packet)
|
||||
end
|
||||
|
||||
# ==================================================================================================
|
||||
@@ -272,6 +268,7 @@ defmodule Odinsea.Login.Packets do
|
||||
|
||||
@doc """
|
||||
Sends character list for selected world.
|
||||
MSEA v112.4 specific encoding.
|
||||
|
||||
## Parameters
|
||||
- `characters` - List of character maps
|
||||
@@ -279,55 +276,70 @@ defmodule Odinsea.Login.Packets do
|
||||
- `char_slots` - Number of character slots (default 3)
|
||||
"""
|
||||
def get_char_list(characters, second_password, char_slots \\ 3) do
|
||||
spw_byte = get_second_password_byte(second_password)
|
||||
# MSEA: no special handling for empty string SPW (GMS uses byte 2 for empty)
|
||||
spw_byte = if second_password != nil and second_password != "", do: 1, else: 0
|
||||
|
||||
packet =
|
||||
Out.new(Opcodes.lp_select_character_result())
|
||||
Out.new(Opcodes.lp_char_list())
|
||||
|> Out.encode_byte(0)
|
||||
|> Out.encode_byte(length(characters))
|
||||
|
||||
# TODO: Encode each character entry
|
||||
# For now, just encode empty list structure
|
||||
# Encode each character entry with MSEA-specific encoding
|
||||
packet =
|
||||
Enum.reduce(characters, packet, fn _char, acc ->
|
||||
# add_char_entry(acc, char)
|
||||
acc # TODO: Implement character encoding
|
||||
Enum.reduce(characters, packet, fn char, acc ->
|
||||
add_char_entry(acc, char)
|
||||
end)
|
||||
|
||||
packet
|
||||
|> Out.encode_byte(spw_byte)
|
||||
|> Out.encode_byte(0) # MSEA ONLY: extra byte after SPW
|
||||
|> Out.encode_long(char_slots)
|
||||
|> Out.to_data()
|
||||
|> Out.encode_long(-:rand.uniform(9_223_372_036_854_775_807)) # MSEA ONLY: negative random long
|
||||
|> Out.to_iodata()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Character name check response.
|
||||
"""
|
||||
def get_char_name_response(char_name, name_used) do
|
||||
Out.new(Opcodes.lp_check_duplicated_id_result())
|
||||
Out.new(Opcodes.lp_char_name_response())
|
||||
|> Out.encode_string(char_name)
|
||||
|> Out.encode_byte(if name_used, do: 1, else: 0)
|
||||
|> Out.to_data()
|
||||
|> Out.to_iodata()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Character creation response.
|
||||
"""
|
||||
def get_add_new_char_entry(character, worked) do
|
||||
Out.new(Opcodes.lp_create_new_character_result())
|
||||
# Uses LP_AddNewCharEntry (0x0A) opcode
|
||||
packet = Out.new(Opcodes.lp_add_new_char_entry())
|
||||
|> Out.encode_byte(if worked, do: 0, else: 1)
|
||||
# TODO: Add character entry if worked
|
||||
|> Out.to_data()
|
||||
|
||||
if worked do
|
||||
# Add character entry for new character (ranking = false for creation)
|
||||
packet = add_char_stats(packet, character)
|
||||
packet = add_char_look(packet, character)
|
||||
|
||||
# viewAll = false, ranking = false for char creation
|
||||
packet
|
||||
|> Out.encode_byte(0) # viewAll
|
||||
|> Out.encode_byte(0) # no ranking for new char
|
||||
else
|
||||
packet
|
||||
end
|
||||
|> Out.to_iodata()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Character deletion response.
|
||||
"""
|
||||
def get_delete_char_response(character_id, state) do
|
||||
Out.new(Opcodes.lp_delete_character_result())
|
||||
# Uses LP_DeleteCharResponse (0x0B) opcode
|
||||
Out.new(Opcodes.lp_delete_char_response())
|
||||
|> Out.encode_int(character_id)
|
||||
|> Out.encode_byte(state)
|
||||
|> Out.to_data()
|
||||
|> Out.to_iodata()
|
||||
end
|
||||
|
||||
# ==================================================================================================
|
||||
@@ -344,7 +356,7 @@ defmodule Odinsea.Login.Packets do
|
||||
def get_second_pw_error(mode) do
|
||||
Out.new(Opcodes.lp_check_spw_result())
|
||||
|> Out.encode_byte(mode)
|
||||
|> Out.to_data()
|
||||
|> Out.to_iodata()
|
||||
end
|
||||
|
||||
# ==================================================================================================
|
||||
@@ -361,28 +373,30 @@ defmodule Odinsea.Login.Packets do
|
||||
- `character_id` - Character ID for migration
|
||||
"""
|
||||
def get_server_ip(is_cash_shop, host, port, character_id) do
|
||||
# Uses LP_ServerIP (0x08) opcode
|
||||
# Parse IP address
|
||||
ip_parts = parse_ip(host)
|
||||
|
||||
Out.new(Opcodes.lp_migrate_command())
|
||||
Out.new(Opcodes.lp_server_ip())
|
||||
|> Out.encode_short(if is_cash_shop, do: 1, else: 0)
|
||||
|> encode_ip(ip_parts)
|
||||
|> Out.encode_short(port)
|
||||
|> Out.encode_int(character_id)
|
||||
|> Out.encode_bytes(<<0, 0>>)
|
||||
|> Out.to_data()
|
||||
|> Out.encode_buffer(<<0, 0>>)
|
||||
|> Out.to_iodata()
|
||||
end
|
||||
|
||||
# ==================================================================================================
|
||||
# Helper Functions
|
||||
# ==================================================================================================
|
||||
|
||||
defp get_second_password_byte(second_password) do
|
||||
cond do
|
||||
second_password == nil -> 0
|
||||
second_password == "" -> 2
|
||||
true -> 1
|
||||
end
|
||||
@doc """
|
||||
Returns current date in MSEA format: YYYYMMDD as integer.
|
||||
Used in authentication success packet.
|
||||
"""
|
||||
def get_current_date do
|
||||
{{year, month, day}, _} = :calendar.local_time()
|
||||
year * 10000 + month * 100 + day
|
||||
end
|
||||
|
||||
defp get_last_channel(channel_load) do
|
||||
@@ -431,4 +445,228 @@ defmodule Odinsea.Login.Packets do
|
||||
|> Out.encode_byte(c)
|
||||
|> Out.encode_byte(d)
|
||||
end
|
||||
|
||||
# ==============================================================================
|
||||
# Character Entry Encoding (MSEA v112.4)
|
||||
# ==============================================================================
|
||||
|
||||
@doc """
|
||||
Adds a character entry to the packet for character list.
|
||||
Ported from LoginPacket.addCharEntry() for MSEA.
|
||||
"""
|
||||
def add_char_entry(packet, character) do
|
||||
packet = add_char_stats(packet, character)
|
||||
packet = add_char_look(packet, character)
|
||||
|
||||
# viewAll = false, so encode byte 0
|
||||
packet = Out.encode_byte(packet, 0)
|
||||
|
||||
# Ranking (true if not GM and level >= 30)
|
||||
ranking = not Map.get(character, :is_gm, false) and Map.get(character, :level, 1) >= 30
|
||||
packet = Out.encode_byte(packet, if(ranking, do: 1, else: 0))
|
||||
|
||||
if ranking do
|
||||
packet
|
||||
|> Out.encode_int(Map.get(character, :rank, 0))
|
||||
|> Out.encode_int(Map.get(character, :rank_move, 0))
|
||||
|> Out.encode_int(Map.get(character, :job_rank, 0))
|
||||
|> Out.encode_int(Map.get(character, :job_rank_move, 0))
|
||||
else
|
||||
packet
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Encodes character stats for MSEA v112.4.
|
||||
Ported from PacketHelper.addCharStats() - MSEA path (GMS = false).
|
||||
|
||||
MSEA Differences from GMS:
|
||||
- NO 24 bytes padding after hair
|
||||
- encode_long(0) after Gach EXP
|
||||
- NO encode_int(0) after spawnpoint
|
||||
"""
|
||||
def add_char_stats(packet, character) do
|
||||
packet
|
||||
|> Out.encode_int(Map.get(character, :id, 0))
|
||||
|> Out.encode_string(Map.get(character, :name, ""), 13)
|
||||
|> Out.encode_byte(Map.get(character, :gender, 0))
|
||||
|> Out.encode_byte(Map.get(character, :skin_color, 0))
|
||||
|> Out.encode_int(Map.get(character, :face, 0))
|
||||
|> Out.encode_int(Map.get(character, :hair, 0))
|
||||
# MSEA: NO 24 bytes padding (GMS has it)
|
||||
|> Out.encode_byte(Map.get(character, :level, 1))
|
||||
|> Out.encode_short(Map.get(character, :job, 0))
|
||||
|> encode_char_stats_data(character)
|
||||
|> Out.encode_short(Map.get(character, :remaining_ap, 0))
|
||||
|> encode_remaining_sp(character)
|
||||
|> Out.encode_int(Map.get(character, :exp, 0))
|
||||
|> Out.encode_int(Map.get(character, :fame, 0))
|
||||
|> Out.encode_int(Map.get(character, :gach_exp, 0))
|
||||
# MSEA ONLY: encode_long(0) after Gach EXP
|
||||
|> Out.encode_long(0)
|
||||
|> Out.encode_int(Map.get(character, :map_id, 0))
|
||||
|> Out.encode_byte(Map.get(character, :spawnpoint, 0))
|
||||
# MSEA: NO encode_int(0) after spawnpoint (GMS has it)
|
||||
|> Out.encode_short(Map.get(character, :subcategory, 0))
|
||||
|> Out.encode_byte(Map.get(character, :fatigue, 0))
|
||||
|> Out.encode_int(get_current_date())
|
||||
|> encode_traits(character)
|
||||
|> Out.encode_buffer(<<0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0>>) # 12 bytes padding
|
||||
|> Out.encode_int(Map.get(character, :pvp_exp, 0))
|
||||
|> Out.encode_byte(Map.get(character, :pvp_rank, 0))
|
||||
|> Out.encode_int(Map.get(character, :battle_points, 0))
|
||||
|> Out.encode_byte(5)
|
||||
# MSEA: NO final encode_int(0) that GMS has
|
||||
end
|
||||
|
||||
# Encodes the main character stats (str, dex, int, luk, hp, mp, max hp, max mp)
|
||||
defp encode_char_stats_data(packet, character) do
|
||||
stats = Map.get(character, :stats, %{})
|
||||
|
||||
packet
|
||||
|> Out.encode_short(Map.get(stats, :str, 12))
|
||||
|> Out.encode_short(Map.get(stats, :dex, 5))
|
||||
|> Out.encode_short(Map.get(stats, :int, 4))
|
||||
|> Out.encode_short(Map.get(stats, :luk, 4))
|
||||
|> Out.encode_short(Map.get(stats, :hp, 50))
|
||||
|> Out.encode_short(Map.get(stats, :max_hp, 50))
|
||||
|> Out.encode_short(Map.get(stats, :mp, 5))
|
||||
|> Out.encode_short(Map.get(stats, :max_mp, 5))
|
||||
end
|
||||
|
||||
# Encodes remaining SP based on job type
|
||||
defp encode_remaining_sp(packet, character) do
|
||||
job = Map.get(character, :job, 0)
|
||||
sp_data = Map.get(character, :remaining_sp, %{type: :short, value: 0})
|
||||
|
||||
# Check for extended SP classes (Evan, Resistance, Mercedes)
|
||||
if is_extended_sp_job?(job) do
|
||||
sp_list = Map.get(character, :remaining_sps, [])
|
||||
packet = Out.encode_byte(packet, length(sp_list))
|
||||
|
||||
Enum.reduce(sp_list, packet, fn {sp_index, sp_value}, p ->
|
||||
p
|
||||
|> Out.encode_byte(sp_index)
|
||||
|> Out.encode_byte(sp_value)
|
||||
end)
|
||||
else
|
||||
Out.encode_short(packet, Map.get(sp_data, :value, 0))
|
||||
end
|
||||
end
|
||||
|
||||
# Jobs that use extended SP format
|
||||
defp is_extended_sp_job?(job) do
|
||||
# Evan: 2200-2218
|
||||
# Resistance: 3000-3512
|
||||
# Mercedes: 2300-2312
|
||||
(job >= 2200 and job <= 2218) or
|
||||
(job >= 3000 and job <= 3512) or
|
||||
(job >= 2300 and job <= 2312)
|
||||
end
|
||||
|
||||
# Encodes trait data (charisma, insight, will, craft, sense, charm)
|
||||
defp encode_traits(packet, character) do
|
||||
traits = Map.get(character, :traits, [0, 0, 0, 0, 0, 0])
|
||||
|
||||
Enum.reduce(traits, packet, fn trait_exp, p ->
|
||||
Out.encode_int(p, trait_exp)
|
||||
end)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Encodes character appearance (look) for MSEA v112.4.
|
||||
Ported from PacketHelper.addCharLook().
|
||||
|
||||
MSEA uses different equipment slot positions than GMS:
|
||||
- Mount: MSEA = -23/-24, GMS = -18/-19
|
||||
- Pendant: MSEA = -55, GMS = -59
|
||||
"""
|
||||
def add_char_look(packet, character) do
|
||||
mega = true # For character list, mega = true
|
||||
|
||||
packet =
|
||||
packet
|
||||
|> Out.encode_byte(Map.get(character, :gender, 0))
|
||||
|> Out.encode_byte(Map.get(character, :skin_color, 0))
|
||||
|> Out.encode_int(Map.get(character, :face, 0))
|
||||
|> Out.encode_int(Map.get(character, :job, 0))
|
||||
|> Out.encode_byte(if mega, do: 0, else: 1)
|
||||
|> Out.encode_int(Map.get(character, :hair, 0))
|
||||
|
||||
equipment = Map.get(character, :equipment, %{})
|
||||
|
||||
# Process equipment slots
|
||||
{visible_equip, masked_equip} = process_char_look_equipment(equipment)
|
||||
|
||||
# Encode visible equipment
|
||||
packet =
|
||||
Enum.reduce(visible_equip, packet, fn {slot, item_id}, p ->
|
||||
p
|
||||
|> Out.encode_byte(slot)
|
||||
|> Out.encode_int(item_id)
|
||||
end)
|
||||
|
||||
# End of visible items marker
|
||||
packet = Out.encode_byte(packet, 0xFF)
|
||||
|
||||
# Encode masked equipment (overrides visible)
|
||||
packet =
|
||||
Enum.reduce(masked_equip, packet, fn {slot, item_id}, p ->
|
||||
p
|
||||
|> Out.encode_byte(slot)
|
||||
|> Out.encode_int(item_id)
|
||||
end)
|
||||
|
||||
# End of masked items marker
|
||||
packet = Out.encode_byte(packet, 0xFF)
|
||||
|
||||
# cash weapon (slot -111)
|
||||
cash_weapon = Map.get(equipment, -111, 0)
|
||||
|
||||
packet
|
||||
|> Out.encode_int(cash_weapon)
|
||||
|> Out.encode_int(0) # Unknown/ears
|
||||
|> Out.encode_long(0) # Padding
|
||||
end
|
||||
|
||||
# Processes equipment for char look encoding
|
||||
# Returns {visible_equip_map, masked_equip_map}
|
||||
defp process_char_look_equipment(equipment) do
|
||||
equipment
|
||||
|> Enum.reduce({%{}, %{}}, fn {pos, item_id}, {visible, masked} = acc ->
|
||||
# Skip hidden equipment (slot < -127)
|
||||
if pos < -127 do
|
||||
acc
|
||||
else
|
||||
slot = abs(pos)
|
||||
|
||||
cond do
|
||||
# Normal visible equipment (slots 1-99)
|
||||
slot < 100 ->
|
||||
if Map.has_key?(visible, slot) do
|
||||
# Move existing to masked, put new in visible
|
||||
{Map.put(visible, slot, item_id), Map.put(masked, slot, visible[slot])}
|
||||
else
|
||||
{Map.put(visible, slot, item_id), masked}
|
||||
end
|
||||
|
||||
# Cash equipment (slots 100+, except 111)
|
||||
slot > 100 and slot != 111 ->
|
||||
actual_slot = slot - 100
|
||||
|
||||
if Map.has_key?(visible, actual_slot) do
|
||||
# Replace visible with cash, move old visible to masked
|
||||
{Map.put(visible, actual_slot, item_id),
|
||||
Map.put(masked, actual_slot, visible[actual_slot])}
|
||||
else
|
||||
{Map.put(visible, actual_slot, item_id), masked}
|
||||
end
|
||||
|
||||
# Other slots (111 = cash weapon, etc.) - skip for now
|
||||
true ->
|
||||
acc
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user