Files

9.6 KiB
Raw Permalink Blame History

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 ~2040 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