228 lines
9.6 KiB
Markdown
228 lines
9.6 KiB
Markdown
# Feature Proposals: Glossary & Tech Tree
|
||
|
||
## Glossary / Mechanics Explainer
|
||
|
||
### Goal
|
||
|
||
Provide inline tooltips and a browsable reference for game-specific terms (resources, mechanics, roles, etc.) with automatic cross-linking whenever those terms appear in entity descriptions, notes, or tooltips.
|
||
|
||
### Data Model
|
||
|
||
Add a new model under `Model/`:
|
||
|
||
**`Model/Glossary/GlossaryTermModel.cs`**
|
||
```
|
||
- Id (string, e.g. "glossary_pyre")
|
||
- Term (string, e.g. "Pyre")
|
||
- ShortDefinition (string, ~1-2 sentences for tooltips)
|
||
- LongDefinition (string, markdown-capable for glossary page)
|
||
- Category (string, e.g. "Resource", "Mechanic", "Faction", "Role")
|
||
- RelatedTermIds (List<string> — cross-links to other glossary terms)
|
||
- RelatedEntityIds (List<string> — entities that exemplify this term)
|
||
```
|
||
|
||
**Term Catalog** — Seed ~20–40 terms covering:
|
||
- Resources: Alloy, Ether, Pyre, Energy, Supply
|
||
- Mechanics: Vanguard, Morph, Pyre Spells, Immortals, Suppression, Overgrowth, Shield
|
||
- Roles (from `DescriptiveType.cs`): Frontliner, Skirmisher, Support, Generalist, etc.
|
||
- Factions: Aru, Q'Rath, etc.
|
||
- Stats: Armor types (Light, Medium, Heavy, Etheric, Structure), Movement types
|
||
- RTS concepts: Tier, Tech, Morph, Harass, Timing
|
||
|
||
### Service Layer
|
||
|
||
**`Services/Website/GlossaryService.cs`** — implement `IGlossaryService` (add interface to `IServices.cs`):
|
||
- `GetTerm(string id) — GlossaryTermModel?`
|
||
- `SearchTerms(string query) — List<GlossaryTermModel>`
|
||
- `GetTermsByCategory(string category) — List<GlossaryTermModel>`
|
||
- `GetAllTerms() — List<GlossaryTermModel>`
|
||
- `LinkifyText(string text) — MarkupString` — scans text for known terms and wraps them in tooltip/clickable links
|
||
|
||
### Component Layer
|
||
|
||
**`Components/Display/GlossaryTooltipComponent.razor`**
|
||
- Wraps a term name with a hover tooltip showing `ShortDefinition`
|
||
- On click, opens the glossary detail (either a dialog or navigates to a glossary page)
|
||
- Reuses the existing `InfoTooltipComponent` pattern (css hover) but upgraded to be interactive
|
||
|
||
**`Components/Inputs/GlossaryLabelComponent.razor`**
|
||
- Like `EntityLabelComponent` but for glossary terms
|
||
- Renders a styled `<button>` or `<span>` with the term name
|
||
- On click, opens a `GlossaryDialogComponent` (or navigates to glossary page)
|
||
|
||
**Auto-linking in entity descriptions:**
|
||
- Add a `Linkify()` extension method or service call that processes entity descriptions/flavor text as they're rendered
|
||
- Scan for known term names (longest-match-first to avoid partial matches)
|
||
- Replace with `<GlossaryLabelComponent>` or `<GlossaryTooltipComponent>` tags
|
||
- Apply this in `EntityInfoComponent` where description/flavor text is rendered as `MarkupString`
|
||
|
||
**Pages:**
|
||
|
||
**`Web/Pages/Glossary/GlossaryPage.razor`** — route `/glossary`
|
||
- Category filter tabs (Resources, Mechanics, Factions, Roles)
|
||
- Search bar
|
||
- Grid/list of term cards
|
||
- Click term to expand or navigate to detail
|
||
|
||
**`Web/Pages/Glossary/GlossaryDetailPage.razor`** — route `/glossary/{termId}`
|
||
- Long definition rendered as markdown
|
||
- Related terms as clickable chips
|
||
- Related entities listed with `EntityLabelComponent`
|
||
|
||
**`Web/Dialog/GlossaryDialogComponent.razor`** — modal dialog version
|
||
- Reuses the pattern from `EntityDialogComponent`
|
||
- Shows term details in a side-panel or overlay
|
||
|
||
### Integration Points
|
||
|
||
| Component | Change |
|
||
|---|---|
|
||
| `EntityInfoComponent.razor` | Pipe description, flavor text, and notes through `GlossaryService.LinkifyText()` |
|
||
| `InfoTooltipComponent.razor` | Optionally accept glossary term ID to auto-populate tooltip text |
|
||
| `NoteService` / notes rendering | Apply `LinkifyText()` when rendering markdown notes |
|
||
| `SearchService` | Index glossary terms so they appear in global search results |
|
||
|
||
### Glossary Dialog Service
|
||
|
||
**`Services/Website/GlossaryDialogService.cs`** — implement `IGlossaryDialogService`:
|
||
- Mirrors `IEntityDialogService` pattern
|
||
- `AddDialog(string termId)`, `CloseDialog()`, `BackDialog()`, etc.
|
||
- Renders `GlossaryDialogComponent` alongside `EntityDialogComponent` in the portal
|
||
|
||
---
|
||
|
||
## Interactive Tech Tree
|
||
|
||
### Goal
|
||
|
||
Visualize entity dependencies as an interactive directed acyclic graph (DAG): production chains (what builds what), upgrade paths (what upgrades what), and requirement gates (what requires what building/research).
|
||
|
||
### Data Model — Build a Graph
|
||
|
||
The raw data already exists in the entity parts:
|
||
- `EntityProductionModel.ProducedBy` — which building trains/builds this entity
|
||
- `EntityRequirementModel.Id` + `RequirementType` — what entity is required (production building, research building, research upgrade, or morph target)
|
||
- `EntityIdUpgradeModel.Id` — which tech upgrades apply to this entity
|
||
- `EntityVanguardAddedModel.ReplaceId` — immortal replacement relationship
|
||
|
||
**`Services/Immortal/TechTreeService.cs`** — new service to build the graph:
|
||
|
||
```
|
||
class TechTreeService:
|
||
BuildGraph() -> GraphModel:
|
||
- Iterate all entities via EntityData.Get()
|
||
- For each entity, extract production/requirement/upgrade edges
|
||
- Build adjacency list (forward + reverse)
|
||
- Build reverse index: "what does this entity unlock?"
|
||
|
||
class GraphModel:
|
||
Nodes: List<NodeModel> (entity ID, name, entity type, faction)
|
||
Edges: List<EdgeModel> (source, target, relation type)
|
||
```
|
||
|
||
**`Model/TechTree/`** — new folder:
|
||
- `TechTreeGraphModel.cs` — NodeModel, EdgeModel, relation types (Produces, Requires, Upgrades, Replaces)
|
||
- `TechTreeEdgeType.cs` — enum: `Produces`, `RequiresProduction`, `RequiresResearch`, `Morph`, `Upgrades`, `VanguardReplaces`
|
||
|
||
### Service Layer — `TechTreeService`
|
||
|
||
```
|
||
BuildGraph(string? factionFilter = null, string? rootEntityId = null):
|
||
- Build full DAG from entity data
|
||
- Optionally filter to one faction
|
||
- Optionally compute the subgraph reachable from a root entity
|
||
|
||
GetUpgradePath(string entityId):
|
||
- Return the chain of upgrades for a specific unit
|
||
|
||
GetPrerequisites(string entityId):
|
||
- Return all direct requirements as a flat list
|
||
|
||
GetUnlocks(string entityId):
|
||
- Reverse-lookup: what entities does this building/upgrade unlock?
|
||
```
|
||
|
||
### Rendering Approaches
|
||
|
||
**Option A: SVG via Blazor components (lightweight, no dependencies)**
|
||
- Render nodes as positioned `<div>` or `<g>` elements using calculated layout
|
||
- Edges as SVG `<path>` or `<line>` elements
|
||
- Manual or grid-based layout (less optimal for complex graphs)
|
||
- Pros: Zero JS deps, works with Blazor lifecycle
|
||
- Cons: Layout algorithm must be custom
|
||
|
||
**Option B: Dagre + D3.js via JS interop (recommended)**
|
||
- Ship Dagre (MIT) for automatic DAG layout (layered, minimal crossing)
|
||
- Use D3.js or plain SVG for rendering
|
||
- Blazor calls `IJSRuntime.InvokeAsync<GraphLayout>("renderTechTree", graphData)`
|
||
- JS interop returns node positions; Blazor re-renders using those positions
|
||
- Pros: Mature layout algorithms, zoom/pan, animated
|
||
- Cons: JS dependency, async interop complexity
|
||
|
||
**Option C: vis.js / vis-network**
|
||
- Higher-level network visualization library
|
||
- JS interop to pass graph data, returns click events
|
||
- Pros: Feature-rich, less JS code
|
||
- Cons: Heavier dependency
|
||
|
||
### Component Layer
|
||
|
||
**`Components/TechTree/TechTreeGraphComponent.razor`** — main renderer:
|
||
- Parameter: `GraphModel Graph`
|
||
- Parameter: `string? FactionFilter`
|
||
- Parameter: `string? HighlightEntityId`
|
||
- Render modes: full tech tree, per-faction, per-entity subgraph
|
||
|
||
**`Components/TechTree/TechTreeNodeComponent.razor`** — individual node:
|
||
- Entity name, type icon, tier badge
|
||
- Color-coded by entity type (reuse `.army`, `.building`, `.ability` classes)
|
||
- Click opens `EntityDialogComponent`
|
||
- Hover shows mini tooltip with key stats (cost, HP, DPS)
|
||
|
||
**`Components/TechTree/TechTreeEdgeComponent.razor`** — SVG edge:
|
||
- Styled differently per relation type (solid for produces, dashed for requires, dotted for upgrade)
|
||
- Arrowhead at the target
|
||
- Hover shows relation type label
|
||
|
||
**`Web/Pages/TechTree/TechTreePage.razor`** — route `/tech-tree`:
|
||
- Faction selector (tabs or dropdown)
|
||
- Search box to focus on a specific entity
|
||
- Zoom controls (fit, zoom in, zoom out)
|
||
- Legend showing edge types and node colors
|
||
- Toggle: show all nodes vs. only reachable from selected root
|
||
|
||
### Layout Algorithm (if going pure Blazor/SVG)
|
||
|
||
Use a simple layered (Sugiyama-style) layout:
|
||
1. Assign all root nodes (no incoming edges) to Layer 0
|
||
2. BFS to assign each node to `max(parent layer) + 1`
|
||
3. Within each layer, space nodes evenly with tier grouping
|
||
4. Render edges between layers as SVG quadratic bezier curves
|
||
|
||
For a more polished result, integrate Dagre via JS interop — it handles cycle prevention, edge routing, and compact layout automatically.
|
||
|
||
### Implementation Roadmap
|
||
|
||
| Step | Scope | Effort |
|
||
|---|---|---|
|
||
| 1. `TechTreeService.BuildGraph()` | Model + Service | Small |
|
||
| 2. Reverse index (`GetUnlocks`) | Service | Small |
|
||
| 3. Prototype SVG render (static, grid layout) | Component | Medium |
|
||
| 4. Interactive features (zoom, pan, click) | Component | Medium |
|
||
| 5. Faction filtering + search highlight | Component | Small |
|
||
| 6. Dagre JS interop for auto-layout | JS + Blazor | Medium |
|
||
| 7. Node hover tooltip with stats | Component | Small |
|
||
| 8. Legend, edge styling, animations | CSS + Component | Small |
|
||
|
||
### Integration Points
|
||
|
||
| Service/Component | Change |
|
||
|---|---|
|
||
| `EntityRequirementModel` | Already sufficient — no changes needed |
|
||
| `EntityProductionModel.ProducedBy` | Already sufficient |
|
||
| `EntityIdUpgradeModel` | Used to add upgrade edges |
|
||
| `EntityLabelComponent` | Reused inside tech tree nodes for consistent look |
|
||
| `EntityDialogService` | Opened on node click for detail view |
|
||
| `EntityFilterService` | Reuse faction filter logic for tech tree page |
|
||
| `NavigationService` | Add `TechTree` section to navigation |
|