449 lines
8.9 KiB
Markdown
449 lines
8.9 KiB
Markdown
# 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
|
|
|
|
```text
|
|
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:
|
|
|
|
```text
|
|
EncounterJournal/Utils/C_PlayerGuide.lua
|
|
EncounterJournal/Custom_EncounterJournal/PlayerGuide.lua
|
|
EncounterJournal/Custom_EncounterJournal/PlayerGuide.xml
|
|
```
|
|
|
|
Responsibilities:
|
|
|
|
```text
|
|
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 `Рейды`:
|
|
|
|
```text
|
|
Путеводитель | Подземелья | Рейды | Комплекты
|
|
```
|
|
|
|
The tab must live in:
|
|
|
|
```text
|
|
EncounterJournal/Custom_EncounterJournal/Custom_EncounterJournal.xml
|
|
```
|
|
|
|
Expected XML shape:
|
|
|
|
```xml
|
|
<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:
|
|
|
|
```lua
|
|
MW_PLAYER_GUIDE = "Путеводитель"
|
|
```
|
|
|
|
## Tab Selection Behavior
|
|
|
|
When the guide tab is selected:
|
|
|
|
```text
|
|
- 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:
|
|
|
|
```text
|
|
- hide PlayerGuideFrame
|
|
```
|
|
|
|
Do not route this tab through:
|
|
|
|
```lua
|
|
EJ_GetInstanceByIndex(index, isRaid)
|
|
```
|
|
|
|
That API is only for dungeon/raid instance lists.
|
|
|
|
## Client API
|
|
|
|
Create:
|
|
|
|
```lua
|
|
C_PlayerGuide = C_PlayerGuide or {}
|
|
```
|
|
|
|
Required methods:
|
|
|
|
```lua
|
|
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:
|
|
|
|
```text
|
|
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:
|
|
|
|
```lua
|
|
{
|
|
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:
|
|
|
|
```text
|
|
level
|
|
quest
|
|
quest_chain
|
|
dungeon
|
|
raid
|
|
boss
|
|
achievement
|
|
reputation
|
|
profession
|
|
item
|
|
currency
|
|
custom
|
|
```
|
|
|
|
Every objective should use the common fields:
|
|
|
|
```text
|
|
id
|
|
type
|
|
title
|
|
description
|
|
completed
|
|
progress
|
|
required
|
|
targetID
|
|
order
|
|
```
|
|
|
|
Optional type-specific fields:
|
|
|
|
```text
|
|
questID
|
|
questChainID
|
|
instanceID
|
|
encounterID
|
|
achievementID
|
|
factionID
|
|
professionID
|
|
itemID
|
|
currencyID
|
|
```
|
|
|
|
## Objective Click Behavior
|
|
|
|
Recommended behavior:
|
|
|
|
```text
|
|
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:
|
|
|
|
```text
|
|
MoonWellPlayerGuide
|
|
```
|
|
|
|
Server to client:
|
|
|
|
```lua
|
|
SetData(payload)
|
|
UpdateObjective(objective)
|
|
SetStage(stagePayload)
|
|
Notify(message)
|
|
```
|
|
|
|
Client to server:
|
|
|
|
```lua
|
|
RequestRefresh()
|
|
RequestTrackObjective(objectiveID)
|
|
RequestOpenTarget(objectiveID)
|
|
```
|
|
|
|
Minimum MVP only needs:
|
|
|
|
```lua
|
|
SetData(payload)
|
|
RequestRefresh()
|
|
```
|
|
|
|
## AzerothCore / mod-individual-progress Requirements
|
|
|
|
`mod-individual-progress` must:
|
|
|
|
```text
|
|
- 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:
|
|
|
|
```text
|
|
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:
|
|
|
|
```text
|
|
- guide tab
|
|
- stage title
|
|
- stage description
|
|
- total progress percentage
|
|
- objective list
|
|
- completed/incomplete visual state
|
|
- recommendation list
|
|
- manual refresh button
|
|
```
|
|
|
|
Later UI:
|
|
|
|
```text
|
|
- categories
|
|
- filters: all / active / completed
|
|
- tracked objective pinning
|
|
- reward preview
|
|
- requirement tooltips
|
|
- direct "Open in Journal" buttons
|
|
- server notifications
|
|
```
|
|
|
|
## Non-Goals
|
|
|
|
Do not:
|
|
|
|
```text
|
|
- 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:
|
|
|
|
```text
|
|
- 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:
|
|
|
|
```text
|
|
- 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
|
|
```
|