Files
moonwell-client/docs/player-guide-requirements.md

8.9 KiB

Player Guide Requirements

Goal

Create a Путеводитель tab inside the existing Encounter Journal UI.

The guide must show personal character progress calculated by mod-individual-progress and recommend the next relevant content: quests, dungeons, raids, bosses, requirements, activities, and rewards.

This feature is not a third Encounter Journal instance type. It is a separate panel inside the same interface.

High-Level Architecture

AzerothCore / mod-individual-progress
    -> calculates player progress
    -> builds a guide payload

AIO / MoonWell bridge
    -> sends guide data to the client

MoonWellClient / EncounterJournal
    -> receives the payload
    -> stores it in C_PlayerGuide
    -> renders the Player Guide tab

Client Components

Add separate client-side files:

EncounterJournal/Utils/C_PlayerGuide.lua
EncounterJournal/Custom_EncounterJournal/PlayerGuide.lua
EncounterJournal/Custom_EncounterJournal/PlayerGuide.xml

Responsibilities:

C_PlayerGuide.lua
- stores current guide data
- exposes a small UI-facing API
- receives server payloads
- requests refreshes from the server

PlayerGuide.lua
- renders guide data
- refreshes objective rows
- handles clicks on objectives/recommendations

PlayerGuide.xml
- defines the guide panel
- defines static frames, headers, scroll area, row templates

Register these files in MoonWellClient.toc before Custom_EncounterJournal.lua.

Encounter Journal Tab

Add a new top-level tab near Подземелья and Рейды:

Путеводитель | Подземелья | Рейды | Комплекты

The tab must live in:

EncounterJournal/Custom_EncounterJournal/Custom_EncounterJournal.xml

Expected XML shape:

<Button name="$parentGuideTab"
        inherits="EncounterTierTabTemplate"
        text="MW_PLAYER_GUIDE"
        parentKey="guideTab"
        id="5">
    <Anchors>
        <Anchor point="BOTTOMLEFT" relativeTo="$parentSuggestTab" relativePoint="BOTTOMRIGHT" x="35" y="0"/>
    </Anchors>
    <Scripts>
        <OnClick function="EJ_ContentTab_OnClick"/>
    </Scripts>
</Button>

If the guide tab is inserted before existing tabs, anchors for DungeonTab, RaidTab, and LootJournalTab must be adjusted.

Add a localized string:

MW_PLAYER_GUIDE = "Путеводитель"

Tab Selection Behavior

When the guide tab is selected:

- hide instance cards
- hide Encounter Journal detail panel
- hide suggested content panel
- hide loot journal panel
- hide or disable the expansion/tier dropdown unless explicitly needed
- show PlayerGuideFrame

When any other top-level tab is selected:

- hide PlayerGuideFrame

Do not route this tab through:

EJ_GetInstanceByIndex(index, isRaid)

That API is only for dungeon/raid instance lists.

Client API

Create:

C_PlayerGuide = C_PlayerGuide or {}

Required methods:

C_PlayerGuide.SetData(data)
C_PlayerGuide.GetStageInfo()
C_PlayerGuide.GetNumObjectives()
C_PlayerGuide.GetObjectiveInfo(index)
C_PlayerGuide.GetNumRecommendations()
C_PlayerGuide.GetRecommendationInfo(index)
C_PlayerGuide.GetNumRewards()
C_PlayerGuide.GetRewardInfo(index)
C_PlayerGuide.RequestRefresh()

Minimum behavior:

SetData(data)
- validate payload is a table
- replace current data
- fire/trigger a UI refresh if the guide panel is visible

GetStageInfo()
- returns stageID, stageName, stageDescription, progress

GetObjectiveInfo(index)
- returns a normalized objective record or unpacked objective fields

RequestRefresh()
- asks the server to resend the current payload

Server Payload

The server should send one normalized payload per character.

Minimum shape:

{
    version = 1,
    characterGuid = 123,

    stageID = 10,
    stageName = "Подготовка к Нордсколу",
    stageDescription = "Завершите ключевые шаги перед переходом дальше.",
    progress = 40,

    objectives = {
        {
            id = 1,
            type = "level",
            title = "Достигнуть 68 уровня",
            description = "",
            completed = true,
            progress = 68,
            required = 68,
            targetID = 68,
            order = 1,
        },
        {
            id = 2,
            type = "quest",
            title = "Завершить цепочку Темного портала",
            description = "",
            completed = false,
            progress = 3,
            required = 8,
            targetID = 10119,
            order = 2,
        },
        {
            id = 3,
            type = "dungeon",
            title = "Пройти Кузню Крови",
            description = "",
            completed = false,
            progress = 0,
            required = 1,
            targetID = 256,
            instanceID = 256,
            order = 3,
        },
    },

    recommendations = {
        {
            id = 1,
            type = "dungeon",
            title = "Кузня Крови",
            description = "Подходит для текущего этапа.",
            instanceID = 256,
            priority = 100,
        },
    },

    rewards = {
        {
            type = "item",
            itemID = 12345,
            count = 1,
        },
    },
}

Objective Types

Support at least:

level
quest
quest_chain
dungeon
raid
boss
achievement
reputation
profession
item
currency
custom

Every objective should use the common fields:

id
type
title
description
completed
progress
required
targetID
order

Optional type-specific fields:

questID
questChainID
instanceID
encounterID
achievementID
factionID
professionID
itemID
currencyID

Objective Click Behavior

Recommended behavior:

quest
- show quest link or quest hint if available

dungeon
- open Encounter Journal on instanceID

raid
- open Encounter Journal on instanceID

boss
- open Encounter Journal on encounterID

item
- show item tooltip

achievement
- show achievement UI/link

custom
- show text-only detail or request a server action

If a target cannot be opened, the UI should fail quietly and keep the guide visible.

AIO Contract

Use a separate handler namespace:

MoonWellPlayerGuide

Server to client:

SetData(payload)
UpdateObjective(objective)
SetStage(stagePayload)
Notify(message)

Client to server:

RequestRefresh()
RequestTrackObjective(objectiveID)
RequestOpenTarget(objectiveID)

Minimum MVP only needs:

SetData(payload)
RequestRefresh()

AzerothCore / mod-individual-progress Requirements

mod-individual-progress must:

- determine the player's current progression stage
- calculate objective completion state
- calculate objective progress values
- build the guide payload
- send the payload on login
- send updates when progress changes
- respond to explicit client refresh requests

Recommended update events:

login
level up
quest accepted
quest completed
quest rewarded
achievement earned
boss kill
dungeon completed
reputation changed
profession changed
item acquired, if item objectives are used

UI Requirements

MVP UI:

- guide tab
- stage title
- stage description
- total progress percentage
- objective list
- completed/incomplete visual state
- recommendation list
- manual refresh button

Later UI:

- categories
- filters: all / active / completed
- tracked objective pinning
- reward preview
- requirement tooltips
- direct "Open in Journal" buttons
- server notifications

Non-Goals

Do not:

- store guide progress in JOURNALINSTANCE
- make the guide a third dungeon/raid instance category
- overload EJ_GetInstanceByIndex(index, isRaid)
- depend on Sirus-only globals or realm constants
- block the old dungeon/raid Encounter Journal if guide data is missing

MVP Implementation Order

  1. Add the Путеводитель tab.
  2. Add an empty PlayerGuideFrame.
  3. Add C_PlayerGuide.lua with static test data.
  4. Render the stage title and three test objectives.
  5. Wire tab switching and hide/show behavior.
  6. Add AIO MoonWellPlayerGuide.SetData.
  7. Send test payload from the server.
  8. Replace static data with real mod-individual-progress data.
  9. Add click behavior for dungeon/raid/boss objectives.
  10. Add polish: progress bar, icons, rewards, filters.

Validation

Client should tolerate missing or malformed fields:

- missing payload -> show empty state
- missing objectives -> show "no active objectives"
- missing recommendations -> hide recommendations block
- unknown objective type -> render as text-only custom objective
- invalid instanceID/encounterID -> do not throw Lua errors

Server should validate:

- objective ids are unique within the payload
- order is stable
- type is known or custom
- progress <= required when required is present
- dungeon/raid/boss targets exist in Encounter Journal data when used