283 lines
9.2 KiB
Elixir
283 lines
9.2 KiB
Elixir
defmodule Odinsea.Channel.Handler.Alliance do
|
|
@moduledoc """
|
|
Handles Guild Alliance operations.
|
|
|
|
Ported from: src/handling/channel/handler/AllianceHandler.java
|
|
|
|
Guild alliances allow multiple guilds to:
|
|
- Share a common chat channel
|
|
- Display alliance information
|
|
- Coordinate activities
|
|
|
|
## Operations
|
|
- 1: Load alliance info
|
|
- 2: Leave alliance
|
|
- 3: Invite guild to alliance
|
|
- 4: Accept alliance invitation
|
|
- 6: Expel guild from alliance
|
|
- 7: Change alliance leader
|
|
- 8: Update alliance titles (ranks)
|
|
- 9: Change member guild rank
|
|
- 10: Update alliance notice
|
|
- 22: Deny alliance invitation
|
|
|
|
## Main Handlers
|
|
- handle_alliance/2 - All alliance operations
|
|
- handle_deny_invite/2 - Deny alliance invitation
|
|
"""
|
|
|
|
require Logger
|
|
|
|
alias Odinsea.Net.Packet.In
|
|
alias Odinsea.Game.Character
|
|
alias Odinsea.World.Guild
|
|
alias Odinsea.Channel.Packets
|
|
|
|
# ============================================================================
|
|
# Alliance Operations
|
|
# ============================================================================
|
|
|
|
@doc """
|
|
Handles all alliance operations (CP_ALLIANCE_OPERATION / 0xBA).
|
|
|
|
Reference: AllianceHandler.HandleAlliance()
|
|
"""
|
|
def handle_alliance(packet, client_pid, denied \\ false) do
|
|
case Character.get_state_by_client(client_pid) do
|
|
{:ok, character_id, char_state} ->
|
|
guild_id = char_state.guild_id
|
|
|
|
# Check if in guild
|
|
if guild_id <= 0 do
|
|
send(client_pid, {:send_packet, Packets.enable_actions()})
|
|
:ok
|
|
else
|
|
# Get guild info
|
|
# guild = World.Guild.get_guild(guild_id)
|
|
|
|
op = In.decode_byte(packet)
|
|
|
|
# Handle deny separately
|
|
if op == 22 do
|
|
handle_deny_invite(client_pid, character_id, char_state, guild_id)
|
|
else
|
|
handle_alliance_op(op, packet, client_pid, character_id, char_state, guild_id)
|
|
end
|
|
end
|
|
|
|
{:error, reason} ->
|
|
Logger.warn("Failed to handle alliance: #{inspect(reason)}")
|
|
end
|
|
end
|
|
|
|
# ============================================================================
|
|
# Individual Operations
|
|
# ============================================================================
|
|
|
|
# Operation 1: Load alliance info
|
|
defp handle_alliance_op(1, _packet, client_pid, character_id, char_state, guild_id) do
|
|
alliance_id = char_state.alliance_id
|
|
|
|
if alliance_id > 0 do
|
|
# TODO: Get alliance info from World.Alliance
|
|
# packets = World.Alliance.get_alliance_info(alliance_id, false)
|
|
# Enum.each(packets, fn packet -> send(client_pid, {:send_packet, packet}) end)
|
|
|
|
Logger.debug("Alliance load: alliance #{alliance_id}, character #{character_id}")
|
|
end
|
|
|
|
:ok
|
|
end
|
|
|
|
# Operation 2: Leave alliance / Operation 6: Expel guild
|
|
defp handle_alliance_op(op, packet, client_pid, character_id, char_state, guild_id)
|
|
when op in [2, 6] do
|
|
alliance_id = char_state.alliance_id
|
|
guild_rank = char_state.guild_rank
|
|
|
|
# Get target guild ID
|
|
target_guild_id = if op == 6 and byte_size(packet.data) >= 4 do
|
|
In.decode_int(packet)
|
|
else
|
|
guild_id
|
|
end
|
|
|
|
# Permission check: alliance rank <= 2, or own guild
|
|
if guild_rank <= 2 or target_guild_id == guild_id do
|
|
# TODO: Remove guild from alliance
|
|
# World.Alliance.remove_guild_from_alliance(alliance_id, target_guild_id, target_guild_id != guild_id)
|
|
|
|
Logger.debug("Alliance remove: guild #{target_guild_id} from alliance #{alliance_id}, character #{character_id}")
|
|
end
|
|
|
|
:ok
|
|
end
|
|
|
|
# Operation 3: Invite guild to alliance
|
|
defp handle_alliance_op(3, packet, client_pid, character_id, char_state, guild_id) do
|
|
alliance_id = char_state.alliance_id
|
|
alliance_rank = char_state.alliance_rank
|
|
|
|
# Get guild leader name to invite
|
|
target_leader_name = In.decode_string(packet)
|
|
|
|
# Only alliance leader (rank 1) can invite
|
|
if alliance_rank == 1 do
|
|
# TODO: Get target guild leader ID
|
|
# target_leader_id = World.Guild.get_guild_leader(target_leader_name)
|
|
|
|
# TODO: Get target character
|
|
# target_char = ChannelServer.get_player_storage().get_character_by_id(target_leader_id)
|
|
|
|
# TODO: Check if can invite
|
|
# if World.Alliance.can_invite(alliance_id) do
|
|
# # Send invite
|
|
# target_char.client.send_packet(Packets.alliance_invite(alliance_name, character_id))
|
|
# World.Guild.set_invited_id(target_char.guild_id, alliance_id)
|
|
# end
|
|
|
|
Logger.debug("Alliance invite: to leader #{target_leader_name}, alliance #{alliance_id}, character #{character_id}")
|
|
end
|
|
|
|
:ok
|
|
end
|
|
|
|
# Operation 4: Accept alliance invitation
|
|
defp handle_alliance_op(4, _packet, client_pid, character_id, char_state, guild_id) do
|
|
# Get invited alliance ID
|
|
# invited_alliance_id = World.Guild.get_invited_id(guild_id)
|
|
|
|
# if invited_alliance_id > 0 do
|
|
# # Add guild to alliance
|
|
# success = World.Alliance.add_guild_to_alliance(invited_alliance_id, guild_id)
|
|
# if not success do
|
|
# # Send error message
|
|
# end
|
|
#
|
|
# # Clear invited ID
|
|
# World.Guild.set_invited_id(guild_id, 0)
|
|
# end
|
|
|
|
Logger.debug("Alliance accept: guild #{guild_id}, character #{character_id}")
|
|
:ok
|
|
end
|
|
|
|
# Operation 7: Change alliance leader
|
|
defp handle_alliance_op(7, packet, client_pid, character_id, char_state, guild_id) do
|
|
alliance_id = char_state.alliance_id
|
|
alliance_rank = char_state.alliance_rank
|
|
|
|
# Only alliance leader can change leader
|
|
if alliance_rank == 1 do
|
|
new_leader_id = In.decode_int(packet)
|
|
|
|
# TODO: Change alliance leader
|
|
# World.Alliance.change_alliance_leader(alliance_id, new_leader_id)
|
|
|
|
Logger.debug("Alliance leader change: to #{new_leader_id}, alliance #{alliance_id}, character #{character_id}")
|
|
end
|
|
|
|
:ok
|
|
end
|
|
|
|
# Operation 8: Update alliance titles/ranks
|
|
defp handle_alliance_op(8, packet, client_pid, character_id, char_state, guild_id) do
|
|
alliance_id = char_state.alliance_id
|
|
alliance_rank = char_state.alliance_rank
|
|
|
|
# Only alliance leader can update titles
|
|
if alliance_rank == 1 do
|
|
# Read 5 rank titles
|
|
ranks = Enum.map(1..5, fn _ -> In.decode_string(packet) end)
|
|
|
|
# TODO: Update alliance ranks
|
|
# World.Alliance.update_alliance_ranks(alliance_id, ranks)
|
|
|
|
Logger.debug("Alliance ranks update: alliance #{alliance_id}, character #{character_id}")
|
|
end
|
|
|
|
:ok
|
|
end
|
|
|
|
# Operation 9: Change member guild rank
|
|
defp handle_alliance_op(9, packet, client_pid, character_id, char_state, guild_id) do
|
|
alliance_id = char_state.alliance_id
|
|
alliance_rank = char_state.alliance_rank
|
|
|
|
# Alliance rank <= 2 can change ranks
|
|
if alliance_rank <= 2 do
|
|
target_guild_id = In.decode_int(packet)
|
|
new_rank = In.decode_byte(packet)
|
|
|
|
# TODO: Change guild rank in alliance
|
|
# World.Alliance.change_alliance_rank(alliance_id, target_guild_id, new_rank)
|
|
|
|
Logger.debug("Alliance rank change: guild #{target_guild_id} to rank #{new_rank}, alliance #{alliance_id}, character #{character_id}")
|
|
end
|
|
|
|
:ok
|
|
end
|
|
|
|
# Operation 10: Update alliance notice
|
|
defp handle_alliance_op(10, packet, client_pid, character_id, char_state, guild_id) do
|
|
alliance_id = char_state.alliance_id
|
|
alliance_rank = char_state.alliance_rank
|
|
|
|
# Alliance rank <= 2 can update notice
|
|
if alliance_rank <= 2 do
|
|
notice = In.decode_string(packet)
|
|
|
|
# Check notice length (max 100)
|
|
if String.length(notice) <= 100 do
|
|
# TODO: Update alliance notice
|
|
# World.Alliance.update_alliance_notice(alliance_id, notice)
|
|
|
|
Logger.debug("Alliance notice update: alliance #{alliance_id}, character #{character_id}")
|
|
end
|
|
end
|
|
|
|
:ok
|
|
end
|
|
|
|
# Unknown operation
|
|
defp handle_alliance_op(op, _packet, _client_pid, character_id, _char_state, guild_id) do
|
|
Logger.warning("Unknown alliance operation #{op} from character #{character_id}, guild #{guild_id}")
|
|
:ok
|
|
end
|
|
|
|
# ============================================================================
|
|
# Deny Invite Handler
|
|
# ============================================================================
|
|
|
|
@doc """
|
|
Handles deny alliance invitation (CP_DENY_ALLIANCE_REQUEST / 0xBB).
|
|
|
|
Also called when op == 22 in alliance operation.
|
|
|
|
Reference: AllianceHandler.DenyInvite()
|
|
"""
|
|
def handle_deny_invite(client_pid, character_id, char_state, guild_id) do
|
|
# Get invited alliance ID
|
|
# invited_alliance_id = World.Guild.get_invited_id(guild_id)
|
|
|
|
# if invited_alliance_id > 0 do
|
|
# # Get alliance leader
|
|
# leader_id = World.Alliance.get_alliance_leader(invited_alliance_id)
|
|
#
|
|
# if leader_id > 0 do
|
|
# # Notify leader of rejection
|
|
# leader = ChannelServer.get_player_storage().get_character_by_id(leader_id)
|
|
# if leader do
|
|
# leader.drop_message(5, "#{guild.name} Guild has rejected the Guild Union invitation.")
|
|
# end
|
|
# end
|
|
#
|
|
# # Clear invited ID
|
|
# World.Guild.set_invited_id(guild_id, 0)
|
|
# end
|
|
|
|
Logger.debug("Alliance invite denied: guild #{guild_id}, character #{character_id}")
|
|
:ok
|
|
end
|
|
end
|