9.6 KiB
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
InfoTooltipComponentpattern (css hover) but upgraded to be interactive
Components/Inputs/GlossaryLabelComponent.razor
- Like
EntityLabelComponentbut 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
EntityInfoComponentwhere description/flavor text is rendered asMarkupString
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
IEntityDialogServicepattern AddDialog(string termId),CloseDialog(),BackDialog(), etc.- Renders
GlossaryDialogComponentalongsideEntityDialogComponentin 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 entityEntityRequirementModel.Id+RequirementType— what entity is required (production building, research building, research upgrade, or morph target)EntityIdUpgradeModel.Id— which tech upgrades apply to this entityEntityVanguardAddedModel.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,.abilityclasses) - 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:
- Assign all root nodes (no incoming edges) to Layer 0
- BFS to assign each node to
max(parent layer) + 1 - Within each layer, space nodes evenly with tier grouping
- 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 |