116 lines
4.1 KiB
Markdown
116 lines
4.1 KiB
Markdown
|
|
---
|
||
|
|
description: Skill Tree UI component - interactive progression tree for Explorer units
|
||
|
|
globs: src/ui/components/SkillTreeUI.js, src/ui/components/SkillTreeUI.ts, src/types/SkillTreeUI.ts
|
||
|
|
alwaysApply: false
|
||
|
|
---
|
||
|
|
|
||
|
|
# **Skill Tree UI Rule**
|
||
|
|
|
||
|
|
This rule defines the technical implementation for the SkillTreeUI component. This component renders the interactive progression tree for a specific Explorer.
|
||
|
|
|
||
|
|
## **1. Visual Architecture**
|
||
|
|
|
||
|
|
**Style:** "Voxel-Web". We will use **CSS 3D Transforms** to render the nodes as rotating cubes, keeping the UI lightweight but consistent with the game's aesthetic.
|
||
|
|
|
||
|
|
### **A. The Tree Container (Scroll View)**
|
||
|
|
|
||
|
|
- **Layout:** A vertical flex container
|
||
|
|
- **Tiers:** Each "Rank" (Novice, Apprentice, etc.) is a horizontal row (Flexbox)
|
||
|
|
- **Connections:** An `<svg>` overlay sits behind the nodes to draw connecting lines (cables)
|
||
|
|
|
||
|
|
### **B. The Node (CSS Voxel)**
|
||
|
|
|
||
|
|
- **Structure:** A div with `preserve-3d` containing 6 faces
|
||
|
|
- **Animation:**
|
||
|
|
- _Locked:_ Static grey cube
|
||
|
|
- _Available:_ Slowly bobbing, pulsing color
|
||
|
|
- _Unlocked:_ Rotating slowly, emitting a glow (box-shadow)
|
||
|
|
- **Content:** An icon (`<img>` or FontAwesome) is mapped to the Front face
|
||
|
|
|
||
|
|
### **C. The Inspector (Footer)**
|
||
|
|
|
||
|
|
- A slide-up panel showing details for the _selected_ node
|
||
|
|
- Contains the "Unlock" button
|
||
|
|
|
||
|
|
## **2. TypeScript Interfaces (Data Model)**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// src/types/SkillTreeUI.ts
|
||
|
|
|
||
|
|
export interface SkillTreeProps {
|
||
|
|
/** The Unit object (source of state) */
|
||
|
|
unit: Explorer;
|
||
|
|
/** The Tree Definition (source of layout) */
|
||
|
|
treeDef: SkillTreeDefinition;
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface SkillNodeState {
|
||
|
|
id: string;
|
||
|
|
def: SkillNodeDefinition;
|
||
|
|
status: 'LOCKED' | 'AVAILABLE' | 'UNLOCKED';
|
||
|
|
/** Calculated position for drawing lines */
|
||
|
|
domRect?: DOMRect;
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface SkillTreeEvents {
|
||
|
|
/** Dispatched when user attempts to spend SP */
|
||
|
|
'unlock-request': {
|
||
|
|
nodeId: string;
|
||
|
|
cost: number;
|
||
|
|
};
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## **3. Interaction Logic**
|
||
|
|
|
||
|
|
### **A. Node Status Calculation**
|
||
|
|
|
||
|
|
The UI must determine the state of every node on render:
|
||
|
|
|
||
|
|
1. **UNLOCKED:** `unit.classMastery.unlockedNodes.includes(node.id)`
|
||
|
|
2. **AVAILABLE:** Not unlocked AND `parent` is Unlocked AND `unit.level >= node.req`
|
||
|
|
3. **LOCKED:** Everything else
|
||
|
|
|
||
|
|
### **B. Connection Drawing**
|
||
|
|
|
||
|
|
Since nodes are DOM elements, we need a `ResizeObserver` to track their positions.
|
||
|
|
|
||
|
|
- **Logic:** Calculate center `(x, y)` of Parent Node and Child Node relative to the Container
|
||
|
|
- **Drawing:** Draw a `<path>` or `<line>` in the SVG layer with a "Circuit Board" style (90-degree bends)
|
||
|
|
- **Styling:**
|
||
|
|
- If Child is Unlocked: Line is **Bright Blue/Gold** (Neon)
|
||
|
|
- If Child is Available: Line is **Dim**
|
||
|
|
- If Child is Locked: Line is **Dark Grey**
|
||
|
|
|
||
|
|
## **4. Conditions of Acceptance (CoA)**
|
||
|
|
|
||
|
|
**CoA 1: Dynamic Rendering**
|
||
|
|
|
||
|
|
- The Tree must handle variable depths (Tier 1 to Tier 5)
|
||
|
|
- Nodes must visibly update state immediately when `unit` prop changes (e.g., after unlocking)
|
||
|
|
|
||
|
|
**CoA 2: Validation Feedback**
|
||
|
|
|
||
|
|
- Clicking a "LOCKED" node should show the inspector but disable the button with a reason (e.g., "Requires: Shield Bash")
|
||
|
|
- Clicking an "AVAILABLE" node with 0 SP should show "Insufficient Points"
|
||
|
|
|
||
|
|
**CoA 3: Responsive Lines**
|
||
|
|
|
||
|
|
- If the window resizes, the SVG connecting lines must redraw to connect the centers of the cubes accurately
|
||
|
|
|
||
|
|
**CoA 4: Scroll Position**
|
||
|
|
|
||
|
|
- On open, the view should automatically scroll to center on the _highest tier_ that has an "Available" node, so the player sees their next step
|
||
|
|
|
||
|
|
## **5. Implementation Requirements**
|
||
|
|
|
||
|
|
Create `src/ui/components/SkillTreeUI.js` as a LitElement:
|
||
|
|
|
||
|
|
1. **CSS 3D:** Implement a `.voxel-node` class using `transform-style: preserve-3d` to create a cube. Use keyframes for rotation
|
||
|
|
2. **Layout:** Render the tree tiers using `flex-direction: column-reverse` (Tier 1 at bottom)
|
||
|
|
3. **SVG Lines:** Implement a `_updateConnections()` method that uses `getBoundingClientRect()` to draw lines between nodes in an absolute-positioned `<svg>`. Call this on resize and first render
|
||
|
|
4. **Interactivity:** Clicking a node selects it. Show details in a fixed footer
|
||
|
|
5. **Logic:** Calculate `LOCKED/AVAILABLE/UNLOCKED` state based on `this.unit.unlockedNodes`
|
||
|
|
|
||
|
|
|