# 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 |
