Converting Tests back to C# but still with Playwright
This commit is contained in:
@@ -0,0 +1,227 @@
|
||||
# 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 |
|
||||
Reference in New Issue
Block a user