284 lines
12 KiB
Elixir
284 lines
12 KiB
Elixir
defmodule Odinsea.Game.Events.OxQuizQuestions do
|
|
@moduledoc """
|
|
OX Quiz Question Database.
|
|
Ported from Java `server.events.MapleOxQuizFactory`.
|
|
|
|
Stores true/false questions loaded from database or fallback data.
|
|
Questions are organized into sets and IDs for efficient lookup.
|
|
|
|
## Question Format
|
|
- question: The question text
|
|
- display: How to display the answer (O/X)
|
|
- answer: :o for true, :x for false
|
|
- question_set: Category/set number
|
|
- question_id: ID within the set
|
|
"""
|
|
|
|
require Logger
|
|
|
|
# ============================================================================
|
|
# Types
|
|
# ============================================================================
|
|
|
|
@typedoc "OX Quiz question struct"
|
|
@type question :: %{
|
|
question: String.t(),
|
|
display: String.t(),
|
|
answer: :o | :x,
|
|
ids: {non_neg_integer(), non_neg_integer()} # {question_set, question_id}
|
|
}
|
|
|
|
# ============================================================================
|
|
# GenServer State
|
|
# ============================================================================
|
|
|
|
use GenServer
|
|
|
|
defstruct [
|
|
:questions, # Map of {{set, id} => question}
|
|
:ets_table
|
|
]
|
|
|
|
# ============================================================================
|
|
# Client API
|
|
# ============================================================================
|
|
|
|
@doc """
|
|
Starts the OX Quiz question cache.
|
|
"""
|
|
def start_link(_opts) do
|
|
GenServer.start_link(__MODULE__, [], name: __MODULE__)
|
|
end
|
|
|
|
@doc """
|
|
Gets a random question from the database.
|
|
"""
|
|
def get_random_question do
|
|
GenServer.call(__MODULE__, :get_random_question)
|
|
end
|
|
|
|
@doc """
|
|
Gets a specific question by set and ID.
|
|
"""
|
|
def get_question(question_set, question_id) do
|
|
GenServer.call(__MODULE__, {:get_question, question_set, question_id})
|
|
end
|
|
|
|
@doc """
|
|
Gets all questions.
|
|
"""
|
|
def get_all_questions do
|
|
GenServer.call(__MODULE__, :get_all_questions)
|
|
end
|
|
|
|
@doc """
|
|
Gets the total number of questions.
|
|
"""
|
|
def question_count do
|
|
GenServer.call(__MODULE__, :question_count)
|
|
end
|
|
|
|
@doc """
|
|
Reloads questions from database.
|
|
"""
|
|
def reload do
|
|
GenServer.cast(__MODULE__, :reload)
|
|
end
|
|
|
|
# ============================================================================
|
|
# Server Callbacks
|
|
# ============================================================================
|
|
|
|
@impl true
|
|
def init(_) do
|
|
# Create ETS table for fast lookups
|
|
ets = :ets.new(:ox_quiz_questions, [:set, :protected, :named_table])
|
|
|
|
# Load initial questions
|
|
questions = load_questions()
|
|
|
|
# Store in ETS
|
|
Enum.each(questions, fn {key, q} ->
|
|
:ets.insert(ets, {key, q})
|
|
end)
|
|
|
|
Logger.info("OX Quiz Questions loaded: #{map_size(questions)} questions")
|
|
|
|
{:ok, %__MODULE__{questions: questions, ets_table: ets}}
|
|
end
|
|
|
|
@impl true
|
|
def handle_call(:get_random_question, _from, state) do
|
|
question = get_random_question_impl(state)
|
|
{:reply, question, state}
|
|
end
|
|
|
|
@impl true
|
|
def handle_call({:get_question, set, id}, _from, state) do
|
|
question = Map.get(state.questions, {set, id})
|
|
{:reply, question, state}
|
|
end
|
|
|
|
@impl true
|
|
def handle_call(:get_all_questions, _from, state) do
|
|
{:reply, Map.values(state.questions), state}
|
|
end
|
|
|
|
@impl true
|
|
def handle_call(:question_count, _from, state) do
|
|
{:reply, map_size(state.questions), state}
|
|
end
|
|
|
|
@impl true
|
|
def handle_cast(:reload, state) do
|
|
# Clear ETS
|
|
:ets.delete_all_objects(state.ets_table)
|
|
|
|
# Reload questions
|
|
questions = load_questions()
|
|
|
|
# Store in ETS
|
|
Enum.each(questions, fn {key, q} ->
|
|
:ets.insert(state.ets_table, {key, q})
|
|
end)
|
|
|
|
Logger.info("OX Quiz Questions reloaded: #{map_size(questions)} questions")
|
|
|
|
{:noreply, %{state | questions: questions}}
|
|
end
|
|
|
|
# ============================================================================
|
|
# Private Functions
|
|
# ============================================================================
|
|
|
|
defp get_random_question_impl(state) do
|
|
questions = Map.values(state.questions)
|
|
|
|
if length(questions) > 0 do
|
|
Enum.random(questions)
|
|
else
|
|
# Return fallback question if none loaded
|
|
fallback_question()
|
|
end
|
|
end
|
|
|
|
defp load_questions do
|
|
# Try to load from database
|
|
# In real implementation:
|
|
# - Query wz_oxdata table
|
|
# - Parse each row into question struct
|
|
|
|
# For now, use fallback questions
|
|
fallback_questions()
|
|
|> Enum.map(fn q -> {{elem(q.ids, 0), elem(q.ids, 1)}, q} end)
|
|
|> Map.new()
|
|
end
|
|
|
|
defp parse_answer("o"), do: :o
|
|
defp parse_answer("O"), do: :o
|
|
defp parse_answer("x"), do: :x
|
|
defp parse_answer("X"), do: :x
|
|
defp parse_answer(_), do: :o # Default to true
|
|
|
|
# ============================================================================
|
|
# Fallback Questions
|
|
# ============================================================================
|
|
|
|
defp fallback_question do
|
|
%{
|
|
question: "MapleStory was first released in 2003?",
|
|
display: "O",
|
|
answer: :o,
|
|
ids: {0, 0}
|
|
}
|
|
end
|
|
|
|
defp fallback_questions do
|
|
[
|
|
# Set 1: General MapleStory Knowledge
|
|
%{question: "MapleStory was first released in 2003?", display: "O", answer: :o, ids: {1, 1}},
|
|
%{question: "The maximum level in MapleStory is 200?", display: "O", answer: :o, ids: {1, 2}},
|
|
%{question: "Henesys is the starting town for all beginners?", display: "X", answer: :x, ids: {1, 3}},
|
|
%{question: "The Pink Bean is a boss monster?", display: "O", answer: :o, ids: {1, 4}},
|
|
%{question: "Magicians use swords as their primary weapon?", display: "X", answer: :x, ids: {1, 5}},
|
|
%{question: "The EXP curve gets steeper at higher levels?", display: "O", answer: :o, ids: {1, 6}},
|
|
%{question: "Gachapon gives random items for NX?", display: "O", answer: :o, ids: {1, 7}},
|
|
%{question: "Warriors have the highest INT growth?", display: "X", answer: :x, ids: {1, 8}},
|
|
%{question: "The Cash Shop sells permanent pets?", display: "O", answer: :o, ids: {1, 9}},
|
|
%{question: "All monsters in Maple Island are passive?", display: "O", answer: :o, ids: {1, 10}},
|
|
|
|
# Set 2: Classes and Jobs
|
|
%{question: "Beginners can use the Three Snails skill?", display: "O", answer: :o, ids: {2, 1}},
|
|
%{question: "Magicians require the most DEX to advance?", display: "X", answer: :x, ids: {2, 2}},
|
|
%{question: "Thieves can use claws and daggers?", display: "O", answer: :o, ids: {2, 3}},
|
|
%{question: "Pirates are the only class that can use guns?", display: "O", answer: :o, ids: {2, 4}},
|
|
%{question: "Archers specialize in close-range combat?", display: "X", answer: :x, ids: {2, 5}},
|
|
%{question: "First job advancement happens at level 10?", display: "O", answer: :o, ids: {2, 6}},
|
|
%{question: "All classes can use magic attacks?", display: "X", answer: :x, ids: {2, 7}},
|
|
%{question: "Bowmen require arrows to attack?", display: "O", answer: :o, ids: {2, 8}},
|
|
%{question: "Warriors have the highest HP pool?", display: "O", answer: :o, ids: {2, 9}},
|
|
%{question: "Cygnus Knights are available at level 1?", display: "X", answer: :x, ids: {2, 10}},
|
|
|
|
# Set 3: Monsters and Maps
|
|
%{question: "Blue Snails are found on Maple Island?", display: "O", answer: :o, ids: {3, 1}},
|
|
%{question: "Zakum is located in the Dead Mine?", display: "O", answer: :o, ids: {3, 2}},
|
|
%{question: "Pigs drop pork items?", display: "O", answer: :o, ids: {3, 3}},
|
|
%{question: "The highest level map is Victoria Island?", display: "X", answer: :x, ids: {3, 4}},
|
|
%{question: "Balrog is a level 100 boss?", display: "O", answer: :o, ids: {3, 5}},
|
|
%{question: "Mushmom is a giant mushroom monster?", display: "O", answer: :o, ids: {3, 6}},
|
|
%{question: "All monsters respawn immediately after death?", display: "X", answer: :x, ids: {3, 7}},
|
|
%{question: "Jr. Balrog spawns in Sleepywood Dungeon?", display: "O", answer: :o, ids: {3, 8}},
|
|
%{question: "Orbis Tower connects Orbis to El Nath?", display: "O", answer: :o, ids: {3, 9}},
|
|
%{question: "Ludibrium is a town made of toys?", display: "O", answer: :o, ids: {3, 10}},
|
|
|
|
# Set 4: Items and Equipment
|
|
%{question: "Equipment can have potential stats?", display: "O", answer: :o, ids: {4, 1}},
|
|
%{question: "Mesos are the currency of MapleStory?", display: "O", answer: :o, ids: {4, 2}},
|
|
%{question: "Scrolls always succeed?", display: "X", answer: :x, ids: {4, 3}},
|
|
%{question: "Potions restore HP and MP?", display: "O", answer: :o, ids: {4, 4}},
|
|
%{question: " NX Cash is required to buy Cash Shop items?", display: "O", answer: :o, ids: {4, 5}},
|
|
%{question: "All equipment can be traded?", display: "X", answer: :x, ids: {4, 6}},
|
|
%{question: "Stars are thrown by Night Lords?", display: "O", answer: :o, ids: {4, 7}},
|
|
%{question: "Beginners can equip level 100 items?", display: "X", answer: :x, ids: {4, 8}},
|
|
%{question: "Clean Slate Scrolls remove failed slots?", display: "O", answer: :o, ids: {4, 9}},
|
|
%{question: "Chaos Scrolls randomize item stats?", display: "O", answer: :o, ids: {4, 10}},
|
|
|
|
# Set 5: Quests and NPCs
|
|
%{question: "Mai is the first quest NPC beginners meet?", display: "O", answer: :o, ids: {5, 1}},
|
|
%{question: "All quests can be repeated?", display: "X", answer: :x, ids: {5, 2}},
|
|
%{question: "NPCs with \"!\" above them give quests?", display: "O", answer: :o, ids: {5, 3}},
|
|
%{question: "Party quests require exactly 6 players?", display: "X", answer: :x, ids: {5, 4}},
|
|
%{question: "Roger sells potions in Henesys?", display: "X", answer: :x, ids: {5, 5}},
|
|
%{question: "The Lost City is another name for Kerning City?", display: "X", answer: :x, ids: {5, 6}},
|
|
%{question: "Guilds can have up to 200 members?", display: "O", answer: :o, ids: {5, 7}},
|
|
%{question: "All NPCs can be attacked?", display: "X", answer: :x, ids: {5, 8}},
|
|
%{question: "Big Headward sells hairstyles?", display: "O", answer: :o, ids: {5, 9}},
|
|
%{question: "The Storage Keeper stores items for free?", display: "X", answer: :x, ids: {5, 10}},
|
|
|
|
# Set 6: Game Mechanics
|
|
%{question: "Fame can be given or taken once per day?", display: "O", answer: :o, ids: {6, 1}},
|
|
%{question: "Party play gives bonus EXP?", display: "O", answer: :o, ids: {6, 2}},
|
|
%{question: "Dying causes EXP loss?", display: "O", answer: :o, ids: {6, 3}},
|
|
%{question: "All skills have no cooldown?", display: "X", answer: :x, ids: {6, 4}},
|
|
%{question: "Trade window allows up to 9 items?", display: "O", answer: :o, ids: {6, 5}},
|
|
%{question: "Mounting a pet requires level 70?", display: "X", answer: :x, ids: {6, 6}},
|
|
%{question: "Monster Book tracks monster information?", display: "O", answer: :o, ids: {6, 7}},
|
|
%{question: "Bosses have purple health bars?", display: "O", answer: :o, ids: {6, 8}},
|
|
%{question: "Channel changing is instant?", display: "X", answer: :x, ids: {6, 9}},
|
|
%{question: "Expedition mode is for large boss fights?", display: "O", answer: :o, ids: {6, 10}},
|
|
|
|
# Set 7: Trivia
|
|
%{question: "MapleStory is developed by Nexon?", display: "O", answer: :o, ids: {7, 1}},
|
|
%{question: "The Black Mage is the main antagonist?", display: "O", answer: :o, ids: {7, 2}},
|
|
%{question: "Elvis is a monster in MapleStory?", display: "X", answer: :x, ids: {7, 3}},
|
|
%{question: "Golems are made of rock?", display: "O", answer: :o, ids: {7, 4}},
|
|
%{question: "Maple Island is shaped like a maple leaf?", display: "O", answer: :o, ids: {7, 5}},
|
|
%{question: "All classes can fly?", display: "X", answer: :x, ids: {7, 6}},
|
|
%{question: "The Moon Bunny is a boss?", display: "X", answer: :x, ids: {7, 7}},
|
|
%{question: "Scissors of Karma make items tradable?", display: "O", answer: :o, ids: {7, 8}},
|
|
%{question: "Monster Life is a farming minigame?", display: "O", answer: :o, ids: {7, 9}},
|
|
%{question: "FM stands for Free Market?", display: "O", answer: :o, ids: {7, 10}}
|
|
]
|
|
end
|
|
end
|