defmodule Odinsea.Channel.Handler.MonsterCarnival do @moduledoc """ Handles Monster Carnival (CPQ - Carnival Party Quest) operations. Ported from: src/handling/channel/handler/MonsterCarnivalHandler.java CPQ is a PvP-style party quest where two parties compete: - Summon monsters to send to the opposing team - Use debuff skills on the opposing team - Deploy guardians for defense ## Tabs - 0: Summon monsters - 1: Use debuff skills - 2: Summon guardians ## Main Handlers - handle_monster_carnival/2 - All CPQ operations """ require Logger alias Odinsea.Net.Packet.In alias Odinsea.Game.Character alias Odinsea.Channel.Packets # ============================================================================ # CPQ Operations # ============================================================================ @doc """ Handles all Monster Carnival operations (CP_MONSTER_CARNIVAL / 0x125). Tabs: - 0: Summon monsters (mob list index) - 1: Use debuff skills (skill list index) - 2: Summon guardians (guardian index) Reference: MonsterCarnivalHandler.MonsterCarnival() """ def handle_monster_carnival(packet, client_pid) do case Character.get_state_by_client(client_pid) do {:ok, character_id, char_state} -> # Check if in carnival party if char_state.carnival_party == nil do Logger.debug("Monster Carnival rejected: character #{character_id} not in carnival party") send(client_pid, {:send_packet, Packets.enable_actions()}) :ok else tab = In.decode_byte(packet) num = In.decode_int(packet) handle_carnival_tab(tab, num, client_pid, character_id, char_state) end {:error, reason} -> Logger.warn("Failed to handle monster carnival: #{inspect(reason)}") end end # ============================================================================ # Tab Handlers # ============================================================================ # Tab 0: Summon monsters defp handle_carnival_tab(0, num, client_pid, character_id, char_state) do map_id = char_state.map team = char_state.carnival_party.team available_cp = char_state.carnival_party.available_cp # TODO: Get mob list for map # mobs = Map.get_mobs_to_spawn(map_id) # TODO: Validate num is valid index # TODO: Check available CP >= mob_cost # If valid: # - Spawn monster for opposing team # - Deduct CP # - Update CP displays # - Broadcast summon message Logger.debug("CPQ summon mob: index #{num}, team #{team}, map #{map_id}, character #{character_id}") # Send enable actions (success or failure) send(client_pid, {:send_packet, Packets.enable_actions()}) :ok end # Tab 1: Use debuff skills defp handle_carnival_tab(1, num, client_pid, character_id, char_state) do map_id = char_state.map team = char_state.carnival_party.team available_cp = char_state.carnival_party.available_cp # TODO: Get skill list for map # skills = Map.get_skill_ids(map_id) # TODO: Validate num is valid index # TODO: Get skill from MapleCarnivalFactory # TODO: Check available CP >= skill.cp_loss # If valid: # - Apply debuff to opposing team # - Deduct CP # - Update CP displays # - Broadcast skill usage Logger.debug("CPQ debuff: index #{num}, team #{team}, map #{map_id}, character #{character_id}") # Send enable actions (success or failure) send(client_pid, {:send_packet, Packets.enable_actions()}) :ok end # Tab 2: Summon guardians defp handle_carnival_tab(2, num, client_pid, character_id, char_state) do map_id = char_state.map team = char_state.carnival_party.team available_cp = char_state.carnival_party.available_cp # TODO: Get guardian skill from MapleCarnivalFactory # skill = MapleCarnivalFactory.getGuardian(num) # TODO: Check available CP >= skill.cp_loss # If valid: # - Spawn carnival reactor (guardian) # - Deduct CP # - Update CP displays # - Broadcast summon message Logger.debug("CPQ guardian: index #{num}, team #{team}, map #{map_id}, character #{character_id}") # Send enable actions (success or failure) send(client_pid, {:send_packet, Packets.enable_actions()}) :ok end # Unknown tab defp handle_carnival_tab(tab, num, client_pid, character_id, _char_state) do Logger.warning("Unknown CPQ tab #{tab} (num #{num}) from character #{character_id}") send(client_pid, {:send_packet, Packets.enable_actions()}) :ok end # ============================================================================ # CP Management # ============================================================================ @doc """ Updates CP display for a player. party_cp: true = show party CP, false = show personal CP """ def update_cp(client_pid, available_cp, total_cp, team, party_cp \\ false) do # TODO: Build CP update packet # packet = Packets.cp_update(available_cp, total_cp, team, party_cp) # send(client_pid, {:send_packet, packet}) :ok end @doc """ Broadcasts player summoned message to map. """ def broadcast_summon(map_id, player_name, tab, num) do # TODO: Build summon broadcast packet # packet = Packets.player_summoned(player_name, tab, num) # Map.broadcast_packet(map_id, packet, exclude: player_id) :ok end @doc """ Distributes CP to carnival party members. """ def distribute_cp(carnival_party, cp_amount) do # TODO: Add CP to party total # TODO: Update each member's display :ok end end