feat(@projects/@magic-civilization): add core unit data schema

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
Natalie 2026-04-26 16:03:12 -07:00
parent 649178d745
commit 8163fb8fcb
6 changed files with 210 additions and 0 deletions

View file

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Age of Dwarves — Design System</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link href="https://fonts.googleapis.com/css2?family=Grenze+Gotisch:wght@900&family=Bitter:wght@700&display=swap" rel="stylesheet" />
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body { background: #171219; color: #e0d8c8; font-family: 'Bitter', serif; }
#root { min-height: 100vh; }
</style>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

View file

@ -0,0 +1,25 @@
{
"name": "@magic-civ/designs",
"private": true,
"version": "0.0.1",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"preview": "vite preview"
},
"dependencies": {
"react": "^19.1.0",
"react-dom": "^19.1.0",
"react-router-dom": "^7.5.3",
"styled-components": "^6.1.18"
},
"devDependencies": {
"@types/react": "^19.1.2",
"@types/react-dom": "^19.1.2",
"@types/styled-components": "^5.1.34",
"@vitejs/plugin-react": "^4.4.1",
"typescript": "^5.8.3",
"vite": "^6.3.3"
}
}

View file

@ -0,0 +1,88 @@
export type AttackType = "blade" | "pierce" | "crush" | "trample" | "siege";
export type ArmorType = "unarmored" | "light" | "medium" | "armored" | "heavy" | "plate" | "fortified";
export interface Unit {
id: string;
name: string;
tier: number;
archetype: string;
attackType: AttackType;
armor: ArmorType;
hp: number;
attack: number;
defense: number;
rangedAttack: number;
range: number;
movement: number;
keywords: string[];
race?: string;
unique?: boolean;
}
// Sourced from public/games/age-of-dwarves/data/units/*.json
export const UNITS: Record<string, Unit> = {
warrior: {
id: "warrior", name: "Warrior", tier: 1, archetype: "Light Melee",
attackType: "blade", armor: "light",
hp: 80, attack: 14, defense: 8, rangedAttack: 0, range: 0, movement: 2,
keywords: [],
},
berserker: {
id: "berserker", name: "Berserker", tier: 2, archetype: "Light Melee",
attackType: "blade", armor: "light",
hp: 90, attack: 20, defense: 6, rangedAttack: 0, range: 0, movement: 2,
keywords: ["rage", "no_shield"],
race: "dwarf", unique: true,
},
ironwarden: {
id: "ironwarden", name: "Ironwarden", tier: 4, archetype: "Heavy Melee",
attackType: "blade", armor: "heavy",
hp: 110, attack: 22, defense: 18, rangedAttack: 0, range: 0, movement: 2,
keywords: ["formation", "shield_wall"],
race: "dwarf", unique: true,
},
spearmen: {
id: "spearmen", name: "Spearmen", tier: 1, archetype: "Anti-Cavalry",
attackType: "pierce", armor: "medium",
hp: 60, attack: 8, defense: 8, rangedAttack: 0, range: 0, movement: 1,
keywords: ["reach"],
},
pikeman: {
id: "pikeman", name: "Pikeman", tier: 2, archetype: "Anti-Cavalry",
attackType: "pierce", armor: "armored",
hp: 100, attack: 10, defense: 14, rangedAttack: 0, range: 0, movement: 2,
keywords: ["zoc", "anti_cavalry"],
},
cavalry: {
id: "cavalry", name: "Cavalry", tier: 3, archetype: "Cavalry",
attackType: "blade", armor: "light",
hp: 70, attack: 16, defense: 6, rangedAttack: 0, range: 0, movement: 4,
keywords: ["fast", "flanking"],
},
archer: {
id: "archer", name: "Archer", tier: 1, archetype: "Ranged",
attackType: "pierce", armor: "light",
hp: 50, attack: 10, defense: 4, rangedAttack: 12, range: 2, movement: 2,
keywords: ["ranged"],
},
runesmith: {
id: "runesmith", name: "Runesmith", tier: 2, archetype: "Ranged",
attackType: "pierce", armor: "armored",
hp: 70, attack: 8, defense: 12, rangedAttack: 18, range: 2, movement: 1,
keywords: ["ranged", "heavy_armor"],
race: "dwarf", unique: true,
},
};
// From COMBAT_SYSTEM.md
export const DAMAGE_MATRIX: Record<AttackType, Record<ArmorType, number>> = {
blade: { unarmored: 1.25, light: 1.25, medium: 1.00, armored: 0.75, heavy: 0.75, plate: 0.50, fortified: 0.25 },
pierce: { unarmored: 1.00, light: 1.00, medium: 1.25, armored: 1.00, heavy: 1.00, plate: 0.75, fortified: 0.50 },
crush: { unarmored: 0.75, light: 0.75, medium: 1.00, armored: 1.25, heavy: 1.25, plate: 1.50, fortified: 1.75 },
trample: { unarmored: 2.00, light: 1.50, medium: 1.00, armored: 0.50, heavy: 0.50, plate: 0.25, fortified: 0.00 },
siege: { unarmored: 0.50, light: 0.50, medium: 0.75, armored: 1.00, heavy: 1.00, plate: 1.00, fortified: 2.00 },
};
export function matrixMultiplier(atk: AttackType, armor: ArmorType): number {
return DAMAGE_MATRIX[atk][armor];
}

View file

@ -0,0 +1,56 @@
export const t = {
bg: {
deepest: "#171219",
panel: "#17121e",
surface: "#221a14",
raised: "#2a2018",
list: "#120e1e",
listSel: "#3f2d0d",
menu: "#0e0a17",
btnNormal: "#1f1733",
btnHover: "#331a0d",
btnPressed: "#472f0f",
},
text: {
title: "#f2d973",
primary: "#e0d8c8",
secondary: "#bfb7a6",
muted: "#b2b2b2",
disabled: "#80806699",
btn: "#e0d199",
btnHover: "#ffeb80",
btnPressed: "#ffffb3",
},
accent: {
gold: "#d9a020",
goldBright: "#d9b33f",
goldPress: "#ffd14d",
goldRes: "#f2d133",
science: "#66bfff",
sage: "#66b866",
ping: "#ffd973",
},
sem: {
positive: "#66e666",
goldenAge: "#ffeb66",
negative: "#d95940",
warning: "#e69933",
diplomacy: "#e68c73",
trade: "#ccbf73",
},
border: {
panel: "#73591fcc",
focus: "#d9b340",
listSel: "#d9b340cc",
divider: "#73591f33",
},
font: {
heading: "'Grenze Gotisch', serif",
body: "'Bitter', serif",
mono: "monospace",
},
radius: { panel: "4px", btn: "3px", list: "2px" },
space: { 1: "4px", 2: "8px", 3: "12px", 4: "14px", 5: "18px", 6: "24px" },
} as const;
export type Theme = typeof t;

View file

@ -0,0 +1,15 @@
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"module": "ESNext",
"moduleResolution": "bundler",
"jsx": "react-jsx",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"skipLibCheck": true
},
"include": ["src"]
}

View file

@ -0,0 +1,7 @@
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [react()],
server: { port: 7777 },
});