commit 98a1792106b3b241abda67a24b46ffb5f05d8c46 Author: Vantz Stockwell Date: Sat May 23 07:30:37 2026 -0400 Add Dune Awakening character builder + initial project scaffolding - character-builder/: Vue 3 + NestJS + Valkey app for planning house, class, character XP, 5 spec tracks, faction standing, and skill trees. Shareable via short link (POST /api/builds → 8-char nanoid). - character-builder/data/: parsed JSON tables (character XP through L200, 5 specs to L100, 2 faction standing tables, 5 class skill trees). - character-builder/scripts/extract.py: parser that regenerates data/*.json from the gitignored sample-data/*.html snapshots. - Dockerfile + docker-compose.yml: two-container deploy (app + Valkey). - specialization-calculator/: pre-existing single-file XP/quest calculator, carried into the repo. Co-Authored-By: Claude Opus 4.7 (1M context) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f35395f --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +# raw HTML snapshots from dune.gaming.tools — not committed (large, regenerable) +sample-data/ + +# node +**/node_modules/ +**/dist/ +**/.vite/ + +# editor / OS +.DS_Store +*.log +.idea/ +.vscode/ + +# python tooling artifacts +__pycache__/ +*.pyc diff --git a/README.md b/README.md new file mode 100644 index 0000000..7bb4038 --- /dev/null +++ b/README.md @@ -0,0 +1,31 @@ +# dune-tools + +Fan-made tooling for Dune Awakening. + +## Projects + +- **[character-builder/](character-builder/)** — Vue 3 + NestJS + Valkey app to + plan a character (house, class, character XP, 5 specialization tracks, faction + standing, skill trees). Shareable via short link. Runs as two Docker + containers. +- **[specialization-calculator/](specialization-calculator/)** — single-file + HTML calculator for spec-track XP / quests / days. + +## Source data + +The character builder is built from saved HTML snapshots of +[dune.gaming.tools](https://dune.gaming.tools) that live in `sample-data/`. +That directory is **gitignored** (~19 MB of raw HTML). + +To regenerate `character-builder/data/*.json`: + +1. Save the relevant pages (right-click → "Save Page As → Web Page Complete") + into `sample-data/`. +2. From `character-builder/`, run `uv run python3 scripts/extract.py`. + +The runtime JSON (`character-builder/data/*.json`) **is** committed so the app +builds without the raw HTML. + +## Disclaimer + +Unofficial fan project. Not affiliated with Funcom or any rights holder. diff --git a/character-builder/.dockerignore b/character-builder/.dockerignore new file mode 100644 index 0000000..c0ed4f0 --- /dev/null +++ b/character-builder/.dockerignore @@ -0,0 +1,5 @@ +**/node_modules +**/dist +**/.DS_Store +.git +*.log diff --git a/character-builder/Dockerfile b/character-builder/Dockerfile new file mode 100644 index 0000000..7a909ba --- /dev/null +++ b/character-builder/Dockerfile @@ -0,0 +1,37 @@ +# syntax=docker/dockerfile:1.7 + +# ---------- frontend build ---------- +FROM node:22-alpine AS frontend-build +WORKDIR /app/frontend +COPY frontend/package.json ./ +RUN npm install --no-audit --no-fund +COPY frontend/ ./ +RUN npm run build + +# ---------- backend build ---------- +FROM node:22-alpine AS backend-build +WORKDIR /app/backend +COPY backend/package.json ./ +RUN npm install --no-audit --no-fund +COPY backend/ ./ +RUN npm run build + +# ---------- runtime ---------- +FROM node:22-alpine AS runtime +ENV NODE_ENV=production +WORKDIR /app + +# Backend prod deps only. +COPY backend/package.json ./ +RUN npm install --omit=dev --no-audit --no-fund + +# Compiled backend, built frontend, baked data. +COPY --from=backend-build /app/backend/dist ./dist +COPY --from=frontend-build /app/frontend/dist ./public +COPY data ./data + +ENV STATIC_ROOT=/app/public +ENV DATA_ROOT=/app/data +ENV PORT=3000 +EXPOSE 3000 +CMD ["node", "dist/main.js"] diff --git a/character-builder/README.md b/character-builder/README.md new file mode 100644 index 0000000..3859685 --- /dev/null +++ b/character-builder/README.md @@ -0,0 +1,56 @@ +# Dune Awakening — Character Builder + +Plan your character across House, Class, Character XP, Specializations, Faction +Standing, and Skill Trees. Shareable via short link. + +## Stack + +- **Frontend**: Vue 3 + Vite + TypeScript +- **Backend**: NestJS (Node 22, TypeScript) +- **Storage**: Valkey (Redis-compatible) for build sharing +- **Data**: extracted from `../sample-data/*.html` into `./data/*.json` via `scripts/extract.py` + +Two containers: `app` (NestJS serving the SPA + API) and `valkey`. Wire-up in +`docker-compose.yml`. + +## Run + +```sh +docker compose up --build +``` + +Then open . + +## Update data + +If you save new HTML snapshots into `../sample-data/`, re-run the extractor: + +```sh +uv run python3 scripts/extract.py +``` + +This regenerates `./data/*.json`, which the container picks up on next build. + +## API + +- `POST /api/builds` — body is a build payload (any JSON ≤16 KB). Returns + `{ code }` (8-char URL-safe id). +- `GET /api/builds/:code` — returns `{ code, build }`. +- `GET /api/data/:file` — serves the static JSON tables (allow-listed). + +Builds are stored in Valkey with a rolling 1-year TTL (refreshed on each +fetch), so popular shared builds don't expire. + +## Dev (without Docker) + +In two terminals: + +```sh +# backend +cd backend && npm install && VALKEY_URL=redis://localhost:6379 npm run start:dev + +# frontend +cd frontend && npm install && npm run dev +``` + +The Vite dev server proxies `/api/*` to `localhost:3000`. diff --git a/character-builder/backend/nest-cli.json b/character-builder/backend/nest-cli.json new file mode 100644 index 0000000..f9aa683 --- /dev/null +++ b/character-builder/backend/nest-cli.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://json.schemastore.org/nest-cli", + "collection": "@nestjs/schematics", + "sourceRoot": "src", + "compilerOptions": { + "deleteOutDir": true + } +} diff --git a/character-builder/backend/package.json b/character-builder/backend/package.json new file mode 100644 index 0000000..55498fc --- /dev/null +++ b/character-builder/backend/package.json @@ -0,0 +1,27 @@ +{ + "name": "dune-character-builder-backend", + "version": "0.1.0", + "private": true, + "scripts": { + "build": "nest build", + "start": "node dist/main.js", + "start:dev": "nest start --watch", + "lint": "eslint \"src/**/*.ts\"" + }, + "dependencies": { + "@nestjs/common": "^10.4.15", + "@nestjs/core": "^10.4.15", + "@nestjs/platform-express": "^10.4.15", + "@nestjs/serve-static": "^4.0.2", + "ioredis": "^5.4.2", + "nanoid": "^3.3.8", + "reflect-metadata": "^0.2.2", + "rxjs": "^7.8.1" + }, + "devDependencies": { + "@nestjs/cli": "^10.4.9", + "@types/express": "^5.0.0", + "@types/node": "^22.10.5", + "typescript": "^5.7.3" + } +} diff --git a/character-builder/backend/src/app.module.ts b/character-builder/backend/src/app.module.ts new file mode 100644 index 0000000..c20dfed --- /dev/null +++ b/character-builder/backend/src/app.module.ts @@ -0,0 +1,19 @@ +import { Module } from '@nestjs/common'; +import { ServeStaticModule } from '@nestjs/serve-static'; +import { join } from 'path'; +import { BuildsModule } from './builds/builds.module'; +import { DataModule } from './data/data.module'; + +const staticRoot = process.env.STATIC_ROOT || join(__dirname, '..', 'public'); + +@Module({ + imports: [ + ServeStaticModule.forRoot({ + rootPath: staticRoot, + exclude: ['/api/(.*)'], + }), + BuildsModule, + DataModule, + ], +}) +export class AppModule {} diff --git a/character-builder/backend/src/builds/builds.controller.ts b/character-builder/backend/src/builds/builds.controller.ts new file mode 100644 index 0000000..0093cc5 --- /dev/null +++ b/character-builder/backend/src/builds/builds.controller.ts @@ -0,0 +1,18 @@ +import { Body, Controller, Get, Param, Post } from '@nestjs/common'; +import { BuildsService } from './builds.service'; + +@Controller('builds') +export class BuildsController { + constructor(private readonly builds: BuildsService) {} + + @Post() + create(@Body() body: unknown) { + return this.builds.save(body); + } + + @Get(':code') + async fetch(@Param('code') code: string) { + const build = await this.builds.load(code); + return { code, build }; + } +} diff --git a/character-builder/backend/src/builds/builds.module.ts b/character-builder/backend/src/builds/builds.module.ts new file mode 100644 index 0000000..57fb1ab --- /dev/null +++ b/character-builder/backend/src/builds/builds.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { BuildsController } from './builds.controller'; +import { BuildsService } from './builds.service'; +import { ValkeyProvider } from '../valkey.provider'; + +@Module({ + controllers: [BuildsController], + providers: [BuildsService, ValkeyProvider], +}) +export class BuildsModule {} diff --git a/character-builder/backend/src/builds/builds.service.ts b/character-builder/backend/src/builds/builds.service.ts new file mode 100644 index 0000000..5ecc293 --- /dev/null +++ b/character-builder/backend/src/builds/builds.service.ts @@ -0,0 +1,56 @@ +import { + BadRequestException, + Inject, + Injectable, + NotFoundException, +} from '@nestjs/common'; +import { customAlphabet } from 'nanoid'; +import type Redis from 'ioredis'; +import { VALKEY } from '../valkey.provider'; + +// URL-safe, unambiguous-ish alphabet (no 0/O/1/I/l). 8 chars = 38^8 ≈ 4.4e12 codes. +const nanoid = customAlphabet( + '23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz', + 8, +); + +const MAX_BYTES = 16 * 1024; // 16 KB cap per shared build +const TTL_SECONDS = 60 * 60 * 24 * 365; // 1 year + +@Injectable() +export class BuildsService { + constructor(@Inject(VALKEY) private readonly redis: Redis) {} + + async save(payload: unknown): Promise<{ code: string }> { + const json = JSON.stringify(payload); + if (!json || json === 'null') { + throw new BadRequestException('empty build'); + } + if (Buffer.byteLength(json, 'utf8') > MAX_BYTES) { + throw new BadRequestException(`build too large (>${MAX_BYTES} bytes)`); + } + // Up to 5 retries on collision. + for (let i = 0; i < 5; i++) { + const code = nanoid(); + const key = `build:${code}`; + const ok = await this.redis.set(key, json, 'EX', TTL_SECONDS, 'NX'); + if (ok === 'OK') return { code }; + } + throw new BadRequestException('failed to allocate code'); + } + + async load(code: string): Promise { + if (!/^[2-9A-HJ-NP-Za-km-z]{8}$/.test(code)) { + throw new BadRequestException('invalid code'); + } + const json = await this.redis.get(`build:${code}`); + if (!json) throw new NotFoundException('build not found'); + // refresh TTL so popular builds stay alive + await this.redis.expire(`build:${code}`, TTL_SECONDS); + try { + return JSON.parse(json); + } catch { + throw new BadRequestException('corrupt build'); + } + } +} diff --git a/character-builder/backend/src/data/data.controller.ts b/character-builder/backend/src/data/data.controller.ts new file mode 100644 index 0000000..ef72505 --- /dev/null +++ b/character-builder/backend/src/data/data.controller.ts @@ -0,0 +1,49 @@ +import { + BadRequestException, + Controller, + Get, + Header, + NotFoundException, + Param, + Res, +} from '@nestjs/common'; +import type { Response } from 'express'; +import { createReadStream, existsSync } from 'fs'; +import { join } from 'path'; + +const DATA_ROOT = process.env.DATA_ROOT || join(__dirname, '..', '..', 'data'); + +// Allow-list of known filenames — guards against path traversal. +const ALLOWED = new Set([ + 'index.json', + 'character-xp.json', + 'spec-combat.json', + 'spec-crafting.json', + 'spec-exploration.json', + 'spec-gathering.json', + 'spec-sabotage.json', + 'faction-atreides.json', + 'faction-harkonnen.json', + 'skills-benegesserit.json', + 'skills-mentat.json', + 'skills-planetologist.json', + 'skills-swordmaster.json', + 'skills-trooper.json', +]); + +@Controller('data') +export class DataController { + @Get(':file') + @Header('Content-Type', 'application/json; charset=utf-8') + @Header('Cache-Control', 'public, max-age=3600') + one(@Param('file') file: string, @Res() res: Response) { + if (!ALLOWED.has(file)) { + throw new BadRequestException('unknown data file'); + } + const path = join(DATA_ROOT, file); + if (!existsSync(path)) { + throw new NotFoundException(); + } + createReadStream(path).pipe(res); + } +} diff --git a/character-builder/backend/src/data/data.module.ts b/character-builder/backend/src/data/data.module.ts new file mode 100644 index 0000000..481c82f --- /dev/null +++ b/character-builder/backend/src/data/data.module.ts @@ -0,0 +1,7 @@ +import { Module } from '@nestjs/common'; +import { DataController } from './data.controller'; + +@Module({ + controllers: [DataController], +}) +export class DataModule {} diff --git a/character-builder/backend/src/main.ts b/character-builder/backend/src/main.ts new file mode 100644 index 0000000..e594d60 --- /dev/null +++ b/character-builder/backend/src/main.ts @@ -0,0 +1,12 @@ +import 'reflect-metadata'; +import { NestFactory } from '@nestjs/core'; +import { AppModule } from './app.module'; + +async function bootstrap() { + const app = await NestFactory.create(AppModule); + app.setGlobalPrefix('api', { exclude: [] }); + const port = Number(process.env.PORT) || 3000; + await app.listen(port, '0.0.0.0'); + console.log(`[dune-character-builder] listening on :${port}`); +} +bootstrap(); diff --git a/character-builder/backend/src/valkey.provider.ts b/character-builder/backend/src/valkey.provider.ts new file mode 100644 index 0000000..14f8abb --- /dev/null +++ b/character-builder/backend/src/valkey.provider.ts @@ -0,0 +1,19 @@ +import { Logger, Provider } from '@nestjs/common'; +import Redis from 'ioredis'; + +export const VALKEY = Symbol('VALKEY'); + +export const ValkeyProvider: Provider = { + provide: VALKEY, + useFactory: (): Redis => { + const url = process.env.VALKEY_URL || 'redis://valkey:6379'; + const logger = new Logger('Valkey'); + const client = new Redis(url, { + lazyConnect: false, + maxRetriesPerRequest: 3, + }); + client.on('connect', () => logger.log(`connected ${url}`)); + client.on('error', (err) => logger.warn(`error: ${err.message}`)); + return client; + }, +}; diff --git a/character-builder/backend/tsconfig.json b/character-builder/backend/tsconfig.json new file mode 100644 index 0000000..da89f51 --- /dev/null +++ b/character-builder/backend/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "ES2022", + "moduleResolution": "node", + "outDir": "./dist", + "baseUrl": "./", + "esModuleInterop": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "skipLibCheck": true, + "strict": true, + "strictPropertyInitialization": false, + "noImplicitAny": false, + "resolveJsonModule": true, + "declaration": false, + "sourceMap": true, + "removeComments": true, + "incremental": true + }, + "include": ["src/**/*.ts"], + "exclude": ["node_modules", "dist"] +} diff --git a/character-builder/data/character-xp.json b/character-builder/data/character-xp.json new file mode 100644 index 0000000..aaceb67 --- /dev/null +++ b/character-builder/data/character-xp.json @@ -0,0 +1,1813 @@ +{ + "header": [ + "Level", + "Required XP", + "Cumulative", + "Skill Points", + "Cumulative", + "Intel Points", + "Cumulative" + ], + "rows": [ + { + "level": 1, + "xpRequired": 40, + "totalXp": 40, + "skillPoints": 0, + "totalSkillPoints": 1, + "intelPoints": 4, + "totalIntelPoints": 4 + }, + { + "level": 2, + "xpRequired": 175, + "totalXp": 215, + "skillPoints": 1, + "totalSkillPoints": 2, + "intelPoints": 2, + "totalIntelPoints": 6 + }, + { + "level": 3, + "xpRequired": 225, + "totalXp": 440, + "skillPoints": 1, + "totalSkillPoints": 3, + "intelPoints": 2, + "totalIntelPoints": 8 + }, + { + "level": 4, + "xpRequired": 300, + "totalXp": 740, + "skillPoints": 1, + "totalSkillPoints": 4, + "intelPoints": 3, + "totalIntelPoints": 11 + }, + { + "level": 5, + "xpRequired": 500, + "totalXp": 1240, + "skillPoints": 1, + "totalSkillPoints": 5, + "intelPoints": 3, + "totalIntelPoints": 14 + }, + { + "level": 6, + "xpRequired": 550, + "totalXp": 1790, + "skillPoints": 1, + "totalSkillPoints": 6, + "intelPoints": 3, + "totalIntelPoints": 17 + }, + { + "level": 7, + "xpRequired": 600, + "totalXp": 2390, + "skillPoints": 1, + "totalSkillPoints": 7, + "intelPoints": 3, + "totalIntelPoints": 20 + }, + { + "level": 8, + "xpRequired": 600, + "totalXp": 2990, + "skillPoints": 1, + "totalSkillPoints": 8, + "intelPoints": 3, + "totalIntelPoints": 23 + }, + { + "level": 9, + "xpRequired": 600, + "totalXp": 3590, + "skillPoints": 1, + "totalSkillPoints": 9, + "intelPoints": 3, + "totalIntelPoints": 26 + }, + { + "level": 10, + "xpRequired": 600, + "totalXp": 4190, + "skillPoints": 1, + "totalSkillPoints": 10, + "intelPoints": 3, + "totalIntelPoints": 29 + }, + { + "level": 11, + "xpRequired": 600, + "totalXp": 4790, + "skillPoints": 1, + "totalSkillPoints": 11, + "intelPoints": 3, + "totalIntelPoints": 32 + }, + { + "level": 12, + "xpRequired": 600, + "totalXp": 5390, + "skillPoints": 1, + "totalSkillPoints": 12, + "intelPoints": 3, + "totalIntelPoints": 35 + }, + { + "level": 13, + "xpRequired": 600, + "totalXp": 5990, + "skillPoints": 1, + "totalSkillPoints": 13, + "intelPoints": 3, + "totalIntelPoints": 38 + }, + { + "level": 14, + "xpRequired": 600, + "totalXp": 6590, + "skillPoints": 1, + "totalSkillPoints": 14, + "intelPoints": 3, + "totalIntelPoints": 41 + }, + { + "level": 15, + "xpRequired": 600, + "totalXp": 7190, + "skillPoints": 1, + "totalSkillPoints": 15, + "intelPoints": 3, + "totalIntelPoints": 44 + }, + { + "level": 16, + "xpRequired": 600, + "totalXp": 7790, + "skillPoints": 1, + "totalSkillPoints": 16, + "intelPoints": 5, + "totalIntelPoints": 49 + }, + { + "level": 17, + "xpRequired": 600, + "totalXp": 8390, + "skillPoints": 1, + "totalSkillPoints": 17, + "intelPoints": 5, + "totalIntelPoints": 54 + }, + { + "level": 18, + "xpRequired": 600, + "totalXp": 8990, + "skillPoints": 1, + "totalSkillPoints": 18, + "intelPoints": 5, + "totalIntelPoints": 59 + }, + { + "level": 19, + "xpRequired": 600, + "totalXp": 9590, + "skillPoints": 1, + "totalSkillPoints": 19, + "intelPoints": 5, + "totalIntelPoints": 64 + }, + { + "level": 20, + "xpRequired": 600, + "totalXp": 10190, + "skillPoints": 1, + "totalSkillPoints": 20, + "intelPoints": 5, + "totalIntelPoints": 69 + }, + { + "level": 21, + "xpRequired": 600, + "totalXp": 10790, + "skillPoints": 1, + "totalSkillPoints": 21, + "intelPoints": 5, + "totalIntelPoints": 74 + }, + { + "level": 22, + "xpRequired": 600, + "totalXp": 11390, + "skillPoints": 1, + "totalSkillPoints": 22, + "intelPoints": 5, + "totalIntelPoints": 79 + }, + { + "level": 23, + "xpRequired": 600, + "totalXp": 11990, + "skillPoints": 1, + "totalSkillPoints": 23, + "intelPoints": 5, + "totalIntelPoints": 84 + }, + { + "level": 24, + "xpRequired": 600, + "totalXp": 12590, + "skillPoints": 1, + "totalSkillPoints": 24, + "intelPoints": 5, + "totalIntelPoints": 89 + }, + { + "level": 25, + "xpRequired": 600, + "totalXp": 13190, + "skillPoints": 1, + "totalSkillPoints": 25, + "intelPoints": 5, + "totalIntelPoints": 94 + }, + { + "level": 26, + "xpRequired": 600, + "totalXp": 13790, + "skillPoints": 1, + "totalSkillPoints": 26, + "intelPoints": 5, + "totalIntelPoints": 99 + }, + { + "level": 27, + "xpRequired": 600, + "totalXp": 14390, + "skillPoints": 1, + "totalSkillPoints": 27, + "intelPoints": 5, + "totalIntelPoints": 104 + }, + { + "level": 28, + "xpRequired": 600, + "totalXp": 14990, + "skillPoints": 1, + "totalSkillPoints": 28, + "intelPoints": 5, + "totalIntelPoints": 109 + }, + { + "level": 29, + "xpRequired": 600, + "totalXp": 15590, + "skillPoints": 1, + "totalSkillPoints": 29, + "intelPoints": 5, + "totalIntelPoints": 114 + }, + { + "level": 30, + "xpRequired": 600, + "totalXp": 16190, + "skillPoints": 1, + "totalSkillPoints": 30, + "intelPoints": 5, + "totalIntelPoints": 119 + }, + { + "level": 31, + "xpRequired": 600, + "totalXp": 16790, + "skillPoints": 1, + "totalSkillPoints": 31, + "intelPoints": 10, + "totalIntelPoints": 129 + }, + { + "level": 32, + "xpRequired": 600, + "totalXp": 17390, + "skillPoints": 1, + "totalSkillPoints": 32, + "intelPoints": 10, + "totalIntelPoints": 139 + }, + { + "level": 33, + "xpRequired": 600, + "totalXp": 17990, + "skillPoints": 1, + "totalSkillPoints": 33, + "intelPoints": 10, + "totalIntelPoints": 149 + }, + { + "level": 34, + "xpRequired": 600, + "totalXp": 18590, + "skillPoints": 1, + "totalSkillPoints": 34, + "intelPoints": 10, + "totalIntelPoints": 159 + }, + { + "level": 35, + "xpRequired": 600, + "totalXp": 19190, + "skillPoints": 1, + "totalSkillPoints": 35, + "intelPoints": 10, + "totalIntelPoints": 169 + }, + { + "level": 36, + "xpRequired": 600, + "totalXp": 19790, + "skillPoints": 1, + "totalSkillPoints": 36, + "intelPoints": 10, + "totalIntelPoints": 179 + }, + { + "level": 37, + "xpRequired": 600, + "totalXp": 20390, + "skillPoints": 1, + "totalSkillPoints": 37, + "intelPoints": 10, + "totalIntelPoints": 189 + }, + { + "level": 38, + "xpRequired": 600, + "totalXp": 20990, + "skillPoints": 1, + "totalSkillPoints": 38, + "intelPoints": 10, + "totalIntelPoints": 199 + }, + { + "level": 39, + "xpRequired": 600, + "totalXp": 21590, + "skillPoints": 1, + "totalSkillPoints": 39, + "intelPoints": 10, + "totalIntelPoints": 209 + }, + { + "level": 40, + "xpRequired": 600, + "totalXp": 22190, + "skillPoints": 1, + "totalSkillPoints": 40, + "intelPoints": 10, + "totalIntelPoints": 219 + }, + { + "level": 41, + "xpRequired": 600, + "totalXp": 22790, + "skillPoints": 1, + "totalSkillPoints": 41, + "intelPoints": 10, + "totalIntelPoints": 229 + }, + { + "level": 42, + "xpRequired": 600, + "totalXp": 23390, + "skillPoints": 1, + "totalSkillPoints": 42, + "intelPoints": 10, + "totalIntelPoints": 239 + }, + { + "level": 43, + "xpRequired": 600, + "totalXp": 23990, + "skillPoints": 1, + "totalSkillPoints": 43, + "intelPoints": 10, + "totalIntelPoints": 249 + }, + { + "level": 44, + "xpRequired": 600, + "totalXp": 24590, + "skillPoints": 1, + "totalSkillPoints": 44, + "intelPoints": 10, + "totalIntelPoints": 259 + }, + { + "level": 45, + "xpRequired": 600, + "totalXp": 25190, + "skillPoints": 1, + "totalSkillPoints": 45, + "intelPoints": 10, + "totalIntelPoints": 269 + }, + { + "level": 46, + "xpRequired": 600, + "totalXp": 25790, + "skillPoints": 1, + "totalSkillPoints": 46, + "intelPoints": 10, + "totalIntelPoints": 279 + }, + { + "level": 47, + "xpRequired": 600, + "totalXp": 26390, + "skillPoints": 1, + "totalSkillPoints": 47, + "intelPoints": 10, + "totalIntelPoints": 289 + }, + { + "level": 48, + "xpRequired": 600, + "totalXp": 26990, + "skillPoints": 1, + "totalSkillPoints": 48, + "intelPoints": 10, + "totalIntelPoints": 299 + }, + { + "level": 49, + "xpRequired": 600, + "totalXp": 27590, + "skillPoints": 1, + "totalSkillPoints": 49, + "intelPoints": 10, + "totalIntelPoints": 309 + }, + { + "level": 50, + "xpRequired": 600, + "totalXp": 28190, + "skillPoints": 1, + "totalSkillPoints": 50, + "intelPoints": 10, + "totalIntelPoints": 319 + }, + { + "level": 51, + "xpRequired": 600, + "totalXp": 28790, + "skillPoints": 1, + "totalSkillPoints": 51, + "intelPoints": 20, + "totalIntelPoints": 339 + }, + { + "level": 52, + "xpRequired": 600, + "totalXp": 29390, + "skillPoints": 1, + "totalSkillPoints": 52, + "intelPoints": 20, + "totalIntelPoints": 359 + }, + { + "level": 53, + "xpRequired": 600, + "totalXp": 29990, + "skillPoints": 1, + "totalSkillPoints": 53, + "intelPoints": 20, + "totalIntelPoints": 379 + }, + { + "level": 54, + "xpRequired": 600, + "totalXp": 30590, + "skillPoints": 1, + "totalSkillPoints": 54, + "intelPoints": 20, + "totalIntelPoints": 399 + }, + { + "level": 55, + "xpRequired": 600, + "totalXp": 31190, + "skillPoints": 1, + "totalSkillPoints": 55, + "intelPoints": 20, + "totalIntelPoints": 419 + }, + { + "level": 56, + "xpRequired": 600, + "totalXp": 31790, + "skillPoints": 1, + "totalSkillPoints": 56, + "intelPoints": 20, + "totalIntelPoints": 439 + }, + { + "level": 57, + "xpRequired": 600, + "totalXp": 32390, + "skillPoints": 1, + "totalSkillPoints": 57, + "intelPoints": 20, + "totalIntelPoints": 459 + }, + { + "level": 58, + "xpRequired": 600, + "totalXp": 32990, + "skillPoints": 1, + "totalSkillPoints": 58, + "intelPoints": 20, + "totalIntelPoints": 479 + }, + { + "level": 59, + "xpRequired": 600, + "totalXp": 33590, + "skillPoints": 1, + "totalSkillPoints": 59, + "intelPoints": 20, + "totalIntelPoints": 499 + }, + { + "level": 60, + "xpRequired": 600, + "totalXp": 34190, + "skillPoints": 1, + "totalSkillPoints": 60, + "intelPoints": 20, + "totalIntelPoints": 519 + }, + { + "level": 61, + "xpRequired": 600, + "totalXp": 34790, + "skillPoints": 1, + "totalSkillPoints": 61, + "intelPoints": 20, + "totalIntelPoints": 539 + }, + { + "level": 62, + "xpRequired": 600, + "totalXp": 35390, + "skillPoints": 1, + "totalSkillPoints": 62, + "intelPoints": 20, + "totalIntelPoints": 559 + }, + { + "level": 63, + "xpRequired": 600, + "totalXp": 35990, + "skillPoints": 1, + "totalSkillPoints": 63, + "intelPoints": 20, + "totalIntelPoints": 579 + }, + { + "level": 64, + "xpRequired": 600, + "totalXp": 36590, + "skillPoints": 1, + "totalSkillPoints": 64, + "intelPoints": 20, + "totalIntelPoints": 599 + }, + { + "level": 65, + "xpRequired": 600, + "totalXp": 37190, + "skillPoints": 1, + "totalSkillPoints": 65, + "intelPoints": 20, + "totalIntelPoints": 619 + }, + { + "level": 66, + "xpRequired": 600, + "totalXp": 37790, + "skillPoints": 1, + "totalSkillPoints": 66, + "intelPoints": 20, + "totalIntelPoints": 639 + }, + { + "level": 67, + "xpRequired": 600, + "totalXp": 38390, + "skillPoints": 1, + "totalSkillPoints": 67, + "intelPoints": 20, + "totalIntelPoints": 659 + }, + { + "level": 68, + "xpRequired": 600, + "totalXp": 38990, + "skillPoints": 1, + "totalSkillPoints": 68, + "intelPoints": 20, + "totalIntelPoints": 679 + }, + { + "level": 69, + "xpRequired": 600, + "totalXp": 39590, + "skillPoints": 1, + "totalSkillPoints": 69, + "intelPoints": 20, + "totalIntelPoints": 699 + }, + { + "level": 70, + "xpRequired": 600, + "totalXp": 40190, + "skillPoints": 1, + "totalSkillPoints": 70, + "intelPoints": 30, + "totalIntelPoints": 729 + }, + { + "level": 71, + "xpRequired": 600, + "totalXp": 40790, + "skillPoints": 1, + "totalSkillPoints": 71, + "intelPoints": 30, + "totalIntelPoints": 759 + }, + { + "level": 72, + "xpRequired": 600, + "totalXp": 41390, + "skillPoints": 1, + "totalSkillPoints": 72, + "intelPoints": 30, + "totalIntelPoints": 789 + }, + { + "level": 73, + "xpRequired": 600, + "totalXp": 41990, + "skillPoints": 1, + "totalSkillPoints": 73, + "intelPoints": 30, + "totalIntelPoints": 819 + }, + { + "level": 74, + "xpRequired": 600, + "totalXp": 42590, + "skillPoints": 1, + "totalSkillPoints": 74, + "intelPoints": 30, + "totalIntelPoints": 849 + }, + { + "level": 75, + "xpRequired": 600, + "totalXp": 43190, + "skillPoints": 1, + "totalSkillPoints": 75, + "intelPoints": 30, + "totalIntelPoints": 879 + }, + { + "level": 76, + "xpRequired": 600, + "totalXp": 43790, + "skillPoints": 1, + "totalSkillPoints": 76, + "intelPoints": 30, + "totalIntelPoints": 909 + }, + { + "level": 77, + "xpRequired": 600, + "totalXp": 44390, + "skillPoints": 1, + "totalSkillPoints": 77, + "intelPoints": 30, + "totalIntelPoints": 939 + }, + { + "level": 78, + "xpRequired": 600, + "totalXp": 44990, + "skillPoints": 1, + "totalSkillPoints": 78, + "intelPoints": 30, + "totalIntelPoints": 969 + }, + { + "level": 79, + "xpRequired": 600, + "totalXp": 45590, + "skillPoints": 1, + "totalSkillPoints": 79, + "intelPoints": 30, + "totalIntelPoints": 999 + }, + { + "level": 80, + "xpRequired": 600, + "totalXp": 46190, + "skillPoints": 1, + "totalSkillPoints": 80, + "intelPoints": 30, + "totalIntelPoints": 1029 + }, + { + "level": 81, + "xpRequired": 600, + "totalXp": 46790, + "skillPoints": 1, + "totalSkillPoints": 81, + "intelPoints": 30, + "totalIntelPoints": 1059 + }, + { + "level": 82, + "xpRequired": 600, + "totalXp": 47390, + "skillPoints": 1, + "totalSkillPoints": 82, + "intelPoints": 30, + "totalIntelPoints": 1089 + }, + { + "level": 83, + "xpRequired": 600, + "totalXp": 47990, + "skillPoints": 1, + "totalSkillPoints": 83, + "intelPoints": 30, + "totalIntelPoints": 1119 + }, + { + "level": 84, + "xpRequired": 600, + "totalXp": 48590, + "skillPoints": 1, + "totalSkillPoints": 84, + "intelPoints": 30, + "totalIntelPoints": 1149 + }, + { + "level": 85, + "xpRequired": 600, + "totalXp": 49190, + "skillPoints": 1, + "totalSkillPoints": 85, + "intelPoints": 30, + "totalIntelPoints": 1179 + }, + { + "level": 86, + "xpRequired": 600, + "totalXp": 49790, + "skillPoints": 1, + "totalSkillPoints": 86, + "intelPoints": 40, + "totalIntelPoints": 1219 + }, + { + "level": 87, + "xpRequired": 600, + "totalXp": 50390, + "skillPoints": 1, + "totalSkillPoints": 87, + "intelPoints": 40, + "totalIntelPoints": 1259 + }, + { + "level": 88, + "xpRequired": 600, + "totalXp": 50990, + "skillPoints": 1, + "totalSkillPoints": 88, + "intelPoints": 40, + "totalIntelPoints": 1299 + }, + { + "level": 89, + "xpRequired": 600, + "totalXp": 51590, + "skillPoints": 1, + "totalSkillPoints": 89, + "intelPoints": 40, + "totalIntelPoints": 1339 + }, + { + "level": 90, + "xpRequired": 600, + "totalXp": 52190, + "skillPoints": 1, + "totalSkillPoints": 90, + "intelPoints": 40, + "totalIntelPoints": 1379 + }, + { + "level": 91, + "xpRequired": 600, + "totalXp": 52790, + "skillPoints": 1, + "totalSkillPoints": 91, + "intelPoints": 40, + "totalIntelPoints": 1419 + }, + { + "level": 92, + "xpRequired": 600, + "totalXp": 53390, + "skillPoints": 1, + "totalSkillPoints": 92, + "intelPoints": 40, + "totalIntelPoints": 1459 + }, + { + "level": 93, + "xpRequired": 600, + "totalXp": 53990, + "skillPoints": 1, + "totalSkillPoints": 93, + "intelPoints": 40, + "totalIntelPoints": 1499 + }, + { + "level": 94, + "xpRequired": 600, + "totalXp": 54590, + "skillPoints": 1, + "totalSkillPoints": 94, + "intelPoints": 40, + "totalIntelPoints": 1539 + }, + { + "level": 95, + "xpRequired": 600, + "totalXp": 55190, + "skillPoints": 1, + "totalSkillPoints": 95, + "intelPoints": 40, + "totalIntelPoints": 1579 + }, + { + "level": 96, + "xpRequired": 600, + "totalXp": 55790, + "skillPoints": 1, + "totalSkillPoints": 96, + "intelPoints": 40, + "totalIntelPoints": 1619 + }, + { + "level": 97, + "xpRequired": 600, + "totalXp": 56390, + "skillPoints": 1, + "totalSkillPoints": 97, + "intelPoints": 40, + "totalIntelPoints": 1659 + }, + { + "level": 98, + "xpRequired": 600, + "totalXp": 56990, + "skillPoints": 1, + "totalSkillPoints": 98, + "intelPoints": 40, + "totalIntelPoints": 1699 + }, + { + "level": 99, + "xpRequired": 600, + "totalXp": 57590, + "skillPoints": 1, + "totalSkillPoints": 99, + "intelPoints": 40, + "totalIntelPoints": 1739 + }, + { + "level": 100, + "xpRequired": 600, + "totalXp": 58190, + "skillPoints": 1, + "totalSkillPoints": 100, + "intelPoints": 40, + "totalIntelPoints": 1779 + }, + { + "level": 101, + "xpRequired": 650, + "totalXp": 58840, + "skillPoints": 1, + "totalSkillPoints": 101, + "intelPoints": 40, + "totalIntelPoints": 1819 + }, + { + "level": 102, + "xpRequired": 650, + "totalXp": 59490, + "skillPoints": 1, + "totalSkillPoints": 102, + "intelPoints": 40, + "totalIntelPoints": 1859 + }, + { + "level": 103, + "xpRequired": 650, + "totalXp": 60140, + "skillPoints": 1, + "totalSkillPoints": 103, + "intelPoints": 40, + "totalIntelPoints": 1899 + }, + { + "level": 104, + "xpRequired": 650, + "totalXp": 60790, + "skillPoints": 1, + "totalSkillPoints": 104, + "intelPoints": 40, + "totalIntelPoints": 1939 + }, + { + "level": 105, + "xpRequired": 650, + "totalXp": 61440, + "skillPoints": 1, + "totalSkillPoints": 105, + "intelPoints": 40, + "totalIntelPoints": 1979 + }, + { + "level": 106, + "xpRequired": 650, + "totalXp": 62090, + "skillPoints": 1, + "totalSkillPoints": 106, + "intelPoints": 40, + "totalIntelPoints": 2019 + }, + { + "level": 107, + "xpRequired": 650, + "totalXp": 62740, + "skillPoints": 1, + "totalSkillPoints": 107, + "intelPoints": 40, + "totalIntelPoints": 2059 + }, + { + "level": 108, + "xpRequired": 650, + "totalXp": 63390, + "skillPoints": 1, + "totalSkillPoints": 108, + "intelPoints": 40, + "totalIntelPoints": 2099 + }, + { + "level": 109, + "xpRequired": 650, + "totalXp": 64040, + "skillPoints": 1, + "totalSkillPoints": 109, + "intelPoints": 40, + "totalIntelPoints": 2139 + }, + { + "level": 110, + "xpRequired": 650, + "totalXp": 64690, + "skillPoints": 1, + "totalSkillPoints": 110, + "intelPoints": 40, + "totalIntelPoints": 2179 + }, + { + "level": 111, + "xpRequired": 650, + "totalXp": 65340, + "skillPoints": 1, + "totalSkillPoints": 111, + "intelPoints": 40, + "totalIntelPoints": 2219 + }, + { + "level": 112, + "xpRequired": 650, + "totalXp": 65990, + "skillPoints": 1, + "totalSkillPoints": 112, + "intelPoints": 40, + "totalIntelPoints": 2259 + }, + { + "level": 113, + "xpRequired": 650, + "totalXp": 66640, + "skillPoints": 1, + "totalSkillPoints": 113, + "intelPoints": 40, + "totalIntelPoints": 2299 + }, + { + "level": 114, + "xpRequired": 650, + "totalXp": 67290, + "skillPoints": 1, + "totalSkillPoints": 114, + "intelPoints": 40, + "totalIntelPoints": 2339 + }, + { + "level": 115, + "xpRequired": 650, + "totalXp": 67940, + "skillPoints": 1, + "totalSkillPoints": 115, + "intelPoints": 40, + "totalIntelPoints": 2379 + }, + { + "level": 116, + "xpRequired": 650, + "totalXp": 68590, + "skillPoints": 1, + "totalSkillPoints": 116, + "intelPoints": 40, + "totalIntelPoints": 2419 + }, + { + "level": 117, + "xpRequired": 650, + "totalXp": 69240, + "skillPoints": 1, + "totalSkillPoints": 117, + "intelPoints": 40, + "totalIntelPoints": 2459 + }, + { + "level": 118, + "xpRequired": 650, + "totalXp": 69890, + "skillPoints": 1, + "totalSkillPoints": 118, + "intelPoints": 40, + "totalIntelPoints": 2499 + }, + { + "level": 119, + "xpRequired": 650, + "totalXp": 70540, + "skillPoints": 1, + "totalSkillPoints": 119, + "intelPoints": 40, + "totalIntelPoints": 2539 + }, + { + "level": 120, + "xpRequired": 650, + "totalXp": 71190, + "skillPoints": 1, + "totalSkillPoints": 120, + "intelPoints": 40, + "totalIntelPoints": 2579 + }, + { + "level": 121, + "xpRequired": 650, + "totalXp": 71840, + "skillPoints": 1, + "totalSkillPoints": 121, + "intelPoints": 40, + "totalIntelPoints": 2619 + }, + { + "level": 122, + "xpRequired": 650, + "totalXp": 72490, + "skillPoints": 1, + "totalSkillPoints": 122, + "intelPoints": 40, + "totalIntelPoints": 2659 + }, + { + "level": 123, + "xpRequired": 650, + "totalXp": 73140, + "skillPoints": 1, + "totalSkillPoints": 123, + "intelPoints": 40, + "totalIntelPoints": 2699 + }, + { + "level": 124, + "xpRequired": 650, + "totalXp": 73790, + "skillPoints": 1, + "totalSkillPoints": 124, + "intelPoints": 40, + "totalIntelPoints": 2739 + }, + { + "level": 125, + "xpRequired": 650, + "totalXp": 74440, + "skillPoints": 1, + "totalSkillPoints": 125, + "intelPoints": 40, + "totalIntelPoints": 2779 + }, + { + "level": 126, + "xpRequired": 650, + "totalXp": 75090, + "skillPoints": 1, + "totalSkillPoints": 126, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 127, + "xpRequired": 650, + "totalXp": 75740, + "skillPoints": 1, + "totalSkillPoints": 127, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 128, + "xpRequired": 651, + "totalXp": 76391, + "skillPoints": 1, + "totalSkillPoints": 128, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 129, + "xpRequired": 653, + "totalXp": 77044, + "skillPoints": 1, + "totalSkillPoints": 129, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 130, + "xpRequired": 655, + "totalXp": 77699, + "skillPoints": 1, + "totalSkillPoints": 130, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 131, + "xpRequired": 658, + "totalXp": 78357, + "skillPoints": 1, + "totalSkillPoints": 131, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 132, + "xpRequired": 661, + "totalXp": 79018, + "skillPoints": 1, + "totalSkillPoints": 132, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 133, + "xpRequired": 665, + "totalXp": 79683, + "skillPoints": 1, + "totalSkillPoints": 133, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 134, + "xpRequired": 670, + "totalXp": 80353, + "skillPoints": 1, + "totalSkillPoints": 134, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 135, + "xpRequired": 677, + "totalXp": 81030, + "skillPoints": 1, + "totalSkillPoints": 135, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 136, + "xpRequired": 684, + "totalXp": 81714, + "skillPoints": 1, + "totalSkillPoints": 136, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 137, + "xpRequired": 693, + "totalXp": 82407, + "skillPoints": 1, + "totalSkillPoints": 137, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 138, + "xpRequired": 703, + "totalXp": 83110, + "skillPoints": 1, + "totalSkillPoints": 138, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 139, + "xpRequired": 715, + "totalXp": 83825, + "skillPoints": 1, + "totalSkillPoints": 139, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 140, + "xpRequired": 729, + "totalXp": 84554, + "skillPoints": 1, + "totalSkillPoints": 140, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 141, + "xpRequired": 744, + "totalXp": 85298, + "skillPoints": 1, + "totalSkillPoints": 141, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 142, + "xpRequired": 762, + "totalXp": 86060, + "skillPoints": 1, + "totalSkillPoints": 142, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 143, + "xpRequired": 782, + "totalXp": 86842, + "skillPoints": 1, + "totalSkillPoints": 143, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 144, + "xpRequired": 804, + "totalXp": 87646, + "skillPoints": 1, + "totalSkillPoints": 144, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 145, + "xpRequired": 829, + "totalXp": 88475, + "skillPoints": 1, + "totalSkillPoints": 145, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 146, + "xpRequired": 857, + "totalXp": 89332, + "skillPoints": 1, + "totalSkillPoints": 146, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 147, + "xpRequired": 888, + "totalXp": 90220, + "skillPoints": 1, + "totalSkillPoints": 147, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 148, + "xpRequired": 921, + "totalXp": 91141, + "skillPoints": 1, + "totalSkillPoints": 148, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 149, + "xpRequired": 959, + "totalXp": 92100, + "skillPoints": 1, + "totalSkillPoints": 149, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 150, + "xpRequired": 999, + "totalXp": 93099, + "skillPoints": 1, + "totalSkillPoints": 150, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 151, + "xpRequired": 1044, + "totalXp": 94143, + "skillPoints": 1, + "totalSkillPoints": 151, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 152, + "xpRequired": 1092, + "totalXp": 95235, + "skillPoints": 1, + "totalSkillPoints": 152, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 153, + "xpRequired": 1145, + "totalXp": 96380, + "skillPoints": 1, + "totalSkillPoints": 153, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 154, + "xpRequired": 1202, + "totalXp": 97582, + "skillPoints": 1, + "totalSkillPoints": 154, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 155, + "xpRequired": 1263, + "totalXp": 98845, + "skillPoints": 1, + "totalSkillPoints": 155, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 156, + "xpRequired": 1330, + "totalXp": 100175, + "skillPoints": 1, + "totalSkillPoints": 156, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 157, + "xpRequired": 1401, + "totalXp": 101576, + "skillPoints": 1, + "totalSkillPoints": 157, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 158, + "xpRequired": 1478, + "totalXp": 103054, + "skillPoints": 1, + "totalSkillPoints": 158, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 159, + "xpRequired": 1560, + "totalXp": 104614, + "skillPoints": 1, + "totalSkillPoints": 159, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 160, + "xpRequired": 1649, + "totalXp": 106263, + "skillPoints": 1, + "totalSkillPoints": 160, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 161, + "xpRequired": 1743, + "totalXp": 108006, + "skillPoints": 1, + "totalSkillPoints": 161, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 162, + "xpRequired": 1843, + "totalXp": 109849, + "skillPoints": 1, + "totalSkillPoints": 162, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 163, + "xpRequired": 1950, + "totalXp": 111799, + "skillPoints": 1, + "totalSkillPoints": 163, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 164, + "xpRequired": 2063, + "totalXp": 113862, + "skillPoints": 1, + "totalSkillPoints": 164, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 165, + "xpRequired": 2184, + "totalXp": 116046, + "skillPoints": 1, + "totalSkillPoints": 165, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 166, + "xpRequired": 2312, + "totalXp": 118358, + "skillPoints": 1, + "totalSkillPoints": 166, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 167, + "xpRequired": 2448, + "totalXp": 120806, + "skillPoints": 1, + "totalSkillPoints": 167, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 168, + "xpRequired": 2591, + "totalXp": 123397, + "skillPoints": 1, + "totalSkillPoints": 168, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 169, + "xpRequired": 2742, + "totalXp": 126139, + "skillPoints": 1, + "totalSkillPoints": 169, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 170, + "xpRequired": 2902, + "totalXp": 129041, + "skillPoints": 1, + "totalSkillPoints": 170, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 171, + "xpRequired": 3071, + "totalXp": 132112, + "skillPoints": 1, + "totalSkillPoints": 171, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 172, + "xpRequired": 3248, + "totalXp": 135360, + "skillPoints": 1, + "totalSkillPoints": 172, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 173, + "xpRequired": 3435, + "totalXp": 138795, + "skillPoints": 1, + "totalSkillPoints": 173, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 174, + "xpRequired": 3631, + "totalXp": 142426, + "skillPoints": 1, + "totalSkillPoints": 174, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 175, + "xpRequired": 3837, + "totalXp": 146263, + "skillPoints": 1, + "totalSkillPoints": 175, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 176, + "xpRequired": 4053, + "totalXp": 150316, + "skillPoints": 1, + "totalSkillPoints": 176, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 177, + "xpRequired": 4280, + "totalXp": 154596, + "skillPoints": 1, + "totalSkillPoints": 177, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 178, + "xpRequired": 4518, + "totalXp": 159114, + "skillPoints": 1, + "totalSkillPoints": 178, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 179, + "xpRequired": 4766, + "totalXp": 163880, + "skillPoints": 1, + "totalSkillPoints": 179, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 180, + "xpRequired": 5026, + "totalXp": 168906, + "skillPoints": 1, + "totalSkillPoints": 180, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 181, + "xpRequired": 5297, + "totalXp": 174203, + "skillPoints": 1, + "totalSkillPoints": 181, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 182, + "xpRequired": 5581, + "totalXp": 179784, + "skillPoints": 1, + "totalSkillPoints": 182, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 183, + "xpRequired": 5877, + "totalXp": 185661, + "skillPoints": 1, + "totalSkillPoints": 183, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 184, + "xpRequired": 6185, + "totalXp": 191846, + "skillPoints": 1, + "totalSkillPoints": 184, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 185, + "xpRequired": 6507, + "totalXp": 198353, + "skillPoints": 1, + "totalSkillPoints": 185, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 186, + "xpRequired": 6842, + "totalXp": 205195, + "skillPoints": 1, + "totalSkillPoints": 186, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 187, + "xpRequired": 7190, + "totalXp": 212385, + "skillPoints": 1, + "totalSkillPoints": 187, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 188, + "xpRequired": 7553, + "totalXp": 219938, + "skillPoints": 1, + "totalSkillPoints": 188, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 189, + "xpRequired": 7930, + "totalXp": 227868, + "skillPoints": 1, + "totalSkillPoints": 189, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 190, + "xpRequired": 8322, + "totalXp": 236190, + "skillPoints": 1, + "totalSkillPoints": 190, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 191, + "xpRequired": 8728, + "totalXp": 244918, + "skillPoints": 1, + "totalSkillPoints": 191, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 192, + "xpRequired": 9151, + "totalXp": 254069, + "skillPoints": 1, + "totalSkillPoints": 192, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 193, + "xpRequired": 9588, + "totalXp": 263657, + "skillPoints": 1, + "totalSkillPoints": 193, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 194, + "xpRequired": 10043, + "totalXp": 273700, + "skillPoints": 1, + "totalSkillPoints": 194, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 195, + "xpRequired": 10513, + "totalXp": 284213, + "skillPoints": 1, + "totalSkillPoints": 195, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 196, + "xpRequired": 11001, + "totalXp": 295214, + "skillPoints": 1, + "totalSkillPoints": 196, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 197, + "xpRequired": 11505, + "totalXp": 306719, + "skillPoints": 1, + "totalSkillPoints": 197, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 198, + "xpRequired": 12027, + "totalXp": 318746, + "skillPoints": 1, + "totalSkillPoints": 198, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 199, + "xpRequired": 12568, + "totalXp": 331314, + "skillPoints": 1, + "totalSkillPoints": 199, + "intelPoints": 0, + "totalIntelPoints": 2779 + }, + { + "level": 200, + "xpRequired": 13126, + "totalXp": 344440, + "skillPoints": 1, + "totalSkillPoints": 200, + "intelPoints": 0, + "totalIntelPoints": 2779 + } + ] +} \ No newline at end of file diff --git a/character-builder/data/faction-atreides.json b/character-builder/data/faction-atreides.json new file mode 100644 index 0000000..c706ddd --- /dev/null +++ b/character-builder/data/faction-atreides.json @@ -0,0 +1,136 @@ +{ + "header": [ + "Tier", + "Name", + "Required Rep", + "Cumulative" + ], + "tiers": [ + { + "tier": 0, + "name": "Outsider", + "standingRequired": 0, + "totalStanding": 0 + }, + { + "tier": 1, + "name": "Mercenary", + "standingRequired": 99, + "totalStanding": 99 + }, + { + "tier": 2, + "name": "Recruit", + "standingRequired": 150, + "totalStanding": 249 + }, + { + "tier": 3, + "name": "Contractor", + "standingRequired": 250, + "totalStanding": 499 + }, + { + "tier": 4, + "name": "Agent", + "standingRequired": 500, + "totalStanding": 999 + }, + { + "tier": 5, + "name": "House Operator", + "standingRequired": 1000, + "totalStanding": 1999 + }, + { + "tier": 6, + "name": "-", + "standingRequired": 225, + "totalStanding": 2224 + }, + { + "tier": 7, + "name": "-", + "standingRequired": 300, + "totalStanding": 2524 + }, + { + "tier": 8, + "name": "-", + "standingRequired": 375, + "totalStanding": 2899 + }, + { + "tier": 9, + "name": "-", + "standingRequired": 450, + "totalStanding": 3349 + }, + { + "tier": 10, + "name": "-", + "standingRequired": 525, + "totalStanding": 3874 + }, + { + "tier": 11, + "name": "-", + "standingRequired": 600, + "totalStanding": 4474 + }, + { + "tier": 12, + "name": "-", + "standingRequired": 675, + "totalStanding": 5149 + }, + { + "tier": 13, + "name": "-", + "standingRequired": 750, + "totalStanding": 5899 + }, + { + "tier": 14, + "name": "-", + "standingRequired": 825, + "totalStanding": 6724 + }, + { + "tier": 15, + "name": "-", + "standingRequired": 900, + "totalStanding": 7624 + }, + { + "tier": 16, + "name": "-", + "standingRequired": 975, + "totalStanding": 8599 + }, + { + "tier": 17, + "name": "-", + "standingRequired": 1050, + "totalStanding": 9649 + }, + { + "tier": 18, + "name": "-", + "standingRequired": 1125, + "totalStanding": 10774 + }, + { + "tier": 19, + "name": "-", + "standingRequired": 1200, + "totalStanding": 11974 + }, + { + "tier": 20, + "name": "Envoy", + "standingRequired": 500, + "totalStanding": 12474 + } + ] +} \ No newline at end of file diff --git a/character-builder/data/faction-harkonnen.json b/character-builder/data/faction-harkonnen.json new file mode 100644 index 0000000..583908c --- /dev/null +++ b/character-builder/data/faction-harkonnen.json @@ -0,0 +1,136 @@ +{ + "header": [ + "Tier", + "Name", + "Required Rep", + "Cumulative" + ], + "tiers": [ + { + "tier": 0, + "name": "Outsider", + "standingRequired": 0, + "totalStanding": 0 + }, + { + "tier": 1, + "name": "Mercenary", + "standingRequired": 99, + "totalStanding": 99 + }, + { + "tier": 2, + "name": "Recruit", + "standingRequired": 150, + "totalStanding": 249 + }, + { + "tier": 3, + "name": "Contractor", + "standingRequired": 250, + "totalStanding": 499 + }, + { + "tier": 4, + "name": "Agent", + "standingRequired": 500, + "totalStanding": 999 + }, + { + "tier": 5, + "name": "House Operator", + "standingRequired": 1000, + "totalStanding": 1999 + }, + { + "tier": 6, + "name": "-", + "standingRequired": 225, + "totalStanding": 2224 + }, + { + "tier": 7, + "name": "-", + "standingRequired": 300, + "totalStanding": 2524 + }, + { + "tier": 8, + "name": "-", + "standingRequired": 375, + "totalStanding": 2899 + }, + { + "tier": 9, + "name": "-", + "standingRequired": 450, + "totalStanding": 3349 + }, + { + "tier": 10, + "name": "-", + "standingRequired": 525, + "totalStanding": 3874 + }, + { + "tier": 11, + "name": "-", + "standingRequired": 600, + "totalStanding": 4474 + }, + { + "tier": 12, + "name": "-", + "standingRequired": 675, + "totalStanding": 5149 + }, + { + "tier": 13, + "name": "-", + "standingRequired": 750, + "totalStanding": 5899 + }, + { + "tier": 14, + "name": "-", + "standingRequired": 825, + "totalStanding": 6724 + }, + { + "tier": 15, + "name": "-", + "standingRequired": 900, + "totalStanding": 7624 + }, + { + "tier": 16, + "name": "-", + "standingRequired": 975, + "totalStanding": 8599 + }, + { + "tier": 17, + "name": "-", + "standingRequired": 1050, + "totalStanding": 9649 + }, + { + "tier": 18, + "name": "-", + "standingRequired": 1125, + "totalStanding": 10774 + }, + { + "tier": 19, + "name": "-", + "standingRequired": 1200, + "totalStanding": 11974 + }, + { + "tier": 20, + "name": "Enforcer", + "standingRequired": 500, + "totalStanding": 12474 + } + ] +} \ No newline at end of file diff --git a/character-builder/data/index.json b/character-builder/data/index.json new file mode 100644 index 0000000..23dc195 --- /dev/null +++ b/character-builder/data/index.json @@ -0,0 +1,51 @@ +{ + "xp": { + "character": "character-xp.json", + "combat": "spec-combat.json", + "crafting": "spec-crafting.json", + "exploration": "spec-exploration.json", + "gathering": "spec-gathering.json", + "sabotage": "spec-sabotage.json" + }, + "factions": { + "atreides": "faction-atreides.json", + "harkonnen": "faction-harkonnen.json" + }, + "skills": [ + { + "id": "benegesserit", + "name": "Bene Gesserit", + "file": "skills-benegesserit.json", + "nodes": 22, + "edges": 23 + }, + { + "id": "mentat", + "name": "Mentat", + "file": "skills-mentat.json", + "nodes": 22, + "edges": 22 + }, + { + "id": "planetologist", + "name": "Planetologist", + "file": "skills-planetologist.json", + "nodes": 20, + "edges": 10 + }, + { + "id": "swordmaster", + "name": "Swordmaster", + "file": "skills-swordmaster.json", + "nodes": 22, + "edges": 22 + }, + { + "id": "trooper", + "name": "Trooper", + "file": "skills-trooper.json", + "nodes": 22, + "edges": 22 + } + ] +} \ No newline at end of file diff --git a/character-builder/data/skills-benegesserit.json b/character-builder/data/skills-benegesserit.json new file mode 100644 index 0000000..819775d --- /dev/null +++ b/character-builder/data/skills-benegesserit.json @@ -0,0 +1,342 @@ +{ + "id": "benegesserit", + "name": "Bene Gesserit", + "nodes": [ + { + "tag": "Skills.Spice.BinduDodge", + "id": "BinduDodge", + "name": "Bindu Dodge", + "kind": "Spice", + "row": 1, + "col": 2, + "maxPoints": 1, + "icon": "t_ui_iconskilltreebindudodge_d.webp", + "url": "https://dune.gaming.tools/skills/skills-spice-bindudodge" + }, + { + "tag": "Skills.Ability.BinduNerveStrike", + "id": "BinduNerveStrike", + "name": "Prana-Bindu Strikes", + "kind": "Ability", + "row": 2, + "col": 1, + "maxPoints": 1, + "icon": "t_ui_iconabilitybindunervestrike_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-bindunervestrike" + }, + { + "tag": "Skills.Ability.WeirdingStep", + "id": "WeirdingStep", + "name": "Weirding Step", + "kind": "Ability", + "row": 2, + "col": 3, + "maxPoints": 1, + "icon": "t_ui_iconabilityweirdingstep_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-weirdingstep" + }, + { + "tag": "Skills.Attribute.WeirdingWay2", + "id": "WeirdingWay2", + "name": "Short Blade Damage", + "kind": "Attribute", + "row": 3, + "col": 2, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeskillbrawler_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-weirdingway2" + }, + { + "tag": "Skills.Perk.Backstabber", + "id": "Backstabber", + "name": "Manipulate Instability", + "kind": "Perk", + "row": 4, + "col": 1, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeperkbackstabber_d.webp", + "url": "https://dune.gaming.tools/skills/skills-perk-backstabber" + }, + { + "tag": "Skills.Attribute.WeirdingWay1", + "id": "WeirdingWay1", + "name": "Blade Damage", + "kind": "Attribute", + "row": 4, + "col": 3, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeskillbrawler_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-weirdingway1" + }, + { + "tag": "Skills.Ability.Hypersprint", + "id": "Hypersprint", + "name": "Bindu Sprint", + "kind": "Ability", + "row": 5, + "col": 2, + "maxPoints": 3, + "icon": "t_ui_iconabilitydash_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-hypersprint" + }, + { + "tag": "Skills.Spice.VoiceSplash", + "id": "VoiceSplash", + "name": "Screech", + "kind": "Spice", + "row": 1, + "col": 2, + "maxPoints": 1, + "icon": "t_ui_iconskilltreescreech_d.webp", + "url": "https://dune.gaming.tools/skills/skills-spice-voicesplash" + }, + { + "tag": "Skills.Perk.VoiceAnalysis", + "id": "VoiceAnalysis", + "name": "Rapid Register", + "kind": "Perk", + "row": 2, + "col": 1, + "maxPoints": 1, + "icon": "t_ui_iconskilltreevoiceanalysis_d.webp", + "url": "https://dune.gaming.tools/skills/skills-perk-voiceanalysis" + }, + { + "tag": "Skills.Ability.VoiceStop", + "id": "VoiceStop", + "name": "Stop", + "kind": "Ability", + "row": 2, + "col": 3, + "maxPoints": 1, + "icon": "t_ui_iconabilityvoicestop_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-voicestop" + }, + { + "tag": "Skills.Ability.Blindspot", + "id": "Blindspot", + "name": "Ignore", + "kind": "Ability", + "row": 4, + "col": 1, + "maxPoints": 1, + "icon": "t_ui_iconabilityblindspot_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-blindspot" + }, + { + "tag": "Skills.Attribute.Manipulation1", + "id": "Manipulation1", + "name": "Voice Training", + "kind": "Attribute", + "row": 4, + "col": 3, + "maxPoints": 3, + "icon": "t_ui_iconskilltreebenegesseritcooldown_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-manipulation1" + }, + { + "tag": "Skills.Ability.VoiceCompel", + "id": "VoiceCompel", + "name": "Compel", + "kind": "Ability", + "row": 5, + "col": 2, + "maxPoints": 1, + "icon": "t_ui_iconabilitythevoicecompel_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-voicecompel" + }, + { + "tag": "Skills.Ability.LitanyAgainstFear", + "id": "LitanyAgainstFear", + "name": "Litany Against Fear", + "kind": "Ability", + "row": 1, + "col": 3, + "maxPoints": 3, + "icon": "t_ui_iconabilitylitanyagainstfear_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-litanyagainstfear" + }, + { + "tag": "Skills.Perk.BinduStability", + "id": "BinduStability", + "name": "Prana-Bindu Stability", + "kind": "Perk", + "row": 2, + "col": 2, + "maxPoints": 3, + "icon": "t_ui_iconskilltreebindustability_d.webp", + "url": "https://dune.gaming.tools/skills/skills-perk-bindustability" + }, + { + "tag": "Skills.Perk.MetabolizePoison", + "id": "MetabolizePoison", + "name": "Metabolize Poison", + "kind": "Perk", + "row": 2, + "col": 4, + "maxPoints": 1, + "icon": "t_ui_iconskilltreemetabolizeposion_d.webp", + "url": "https://dune.gaming.tools/skills/skills-perk-metabolizepoison" + }, + { + "tag": "Skills.Attribute.SelfControl3", + "id": "SelfControl3", + "name": "Vitality", + "kind": "Attribute", + "row": 3, + "col": 1, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributemaxhpbonus_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-selfcontrol3" + }, + { + "tag": "Skills.Attribute.SelfControl4", + "id": "SelfControl4", + "name": "Self-Healing", + "kind": "Attribute", + "row": 3, + "col": 3, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeskillmaxhealth_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-selfcontrol4" + }, + { + "tag": "Skills.Attribute.SelfControl5", + "id": "SelfControl5", + "name": "Poison Tolerance", + "kind": "Attribute", + "row": 3, + "col": 5, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributepoisondefense_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-selfcontrol5" + }, + { + "tag": "Skills.Perk.RegenCap", + "id": "RegenCap", + "name": "Trauma Recovery", + "kind": "Perk", + "row": 4, + "col": 2, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeperkhealingfactor_d.webp", + "url": "https://dune.gaming.tools/skills/skills-perk-regencap" + }, + { + "tag": "Skills.Attribute.SelfControl2", + "id": "SelfControl2", + "name": "Sun Tolerance", + "kind": "Attribute", + "row": 4, + "col": 4, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributesundefense_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-selfcontrol2" + }, + { + "tag": "Skills.Attribute.SelfControl1", + "id": "SelfControl1", + "name": "Recovery", + "kind": "Attribute", + "row": 5, + "col": 3, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeskillhealingmultiplier_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-selfcontrol1" + } + ], + "edges": [ + { + "from": "Skills.Ability.BinduNerveStrike", + "to": "Skills.Spice.BinduDodge" + }, + { + "from": "Skills.Ability.WeirdingStep", + "to": "Skills.Spice.BinduDodge" + }, + { + "from": "Skills.Ability.BinduNerveStrike", + "to": "Skills.Attribute.WeirdingWay2" + }, + { + "from": "Skills.Ability.BinduNerveStrike", + "to": "Skills.Perk.Backstabber" + }, + { + "from": "Skills.Ability.WeirdingStep", + "to": "Skills.Attribute.WeirdingWay2" + }, + { + "from": "Skills.Ability.WeirdingStep", + "to": "Skills.Attribute.WeirdingWay1" + }, + { + "from": "Skills.Attribute.WeirdingWay2", + "to": "Skills.Perk.Backstabber" + }, + { + "from": "Skills.Attribute.WeirdingWay1", + "to": "Skills.Attribute.WeirdingWay2" + }, + { + "from": "Skills.Ability.Hypersprint", + "to": "Skills.Perk.Backstabber" + }, + { + "from": "Skills.Attribute.WeirdingWay1", + "to": "Skills.Perk.Backstabber" + }, + { + "from": "Skills.Ability.Hypersprint", + "to": "Skills.Attribute.WeirdingWay1" + }, + { + "from": "Skills.Ability.LitanyAgainstFear", + "to": "Skills.Perk.BinduStability" + }, + { + "from": "Skills.Ability.LitanyAgainstFear", + "to": "Skills.Perk.MetabolizePoison" + }, + { + "from": "Skills.Attribute.SelfControl3", + "to": "Skills.Perk.BinduStability" + }, + { + "from": "Skills.Attribute.SelfControl4", + "to": "Skills.Perk.BinduStability" + }, + { + "from": "Skills.Attribute.SelfControl4", + "to": "Skills.Perk.MetabolizePoison" + }, + { + "from": "Skills.Attribute.SelfControl5", + "to": "Skills.Perk.MetabolizePoison" + }, + { + "from": "Skills.Attribute.SelfControl3", + "to": "Skills.Perk.RegenCap" + }, + { + "from": "Skills.Attribute.SelfControl4", + "to": "Skills.Perk.RegenCap" + }, + { + "from": "Skills.Attribute.SelfControl2", + "to": "Skills.Attribute.SelfControl4" + }, + { + "from": "Skills.Attribute.SelfControl2", + "to": "Skills.Attribute.SelfControl5" + }, + { + "from": "Skills.Attribute.SelfControl1", + "to": "Skills.Perk.RegenCap" + }, + { + "from": "Skills.Attribute.SelfControl1", + "to": "Skills.Attribute.SelfControl2" + } + ] +} \ No newline at end of file diff --git a/character-builder/data/skills-mentat.json b/character-builder/data/skills-mentat.json new file mode 100644 index 0000000..74e1c26 --- /dev/null +++ b/character-builder/data/skills-mentat.json @@ -0,0 +1,338 @@ +{ + "id": "mentat", + "name": "Mentat", + "nodes": [ + { + "tag": "Skills.Perk.ShieldWeakpoint", + "id": "ShieldWeakpoint", + "name": "Shield Overcharge", + "kind": "Perk", + "row": 1, + "col": 3, + "maxPoints": 1, + "icon": "t_ui_iconskilltreeshieldovercharge_d.webp", + "url": "https://dune.gaming.tools/skills/skills-perk-shieldweakpoint" + }, + { + "tag": "Skills.Perk.ExploitWeakness", + "id": "ExploitWeakness", + "name": "Exploit Weakness", + "kind": "Perk", + "row": 2, + "col": 2, + "maxPoints": 1, + "icon": "t_ui_iconskilltreespiceeffectexploitweakness_d.webp", + "url": "https://dune.gaming.tools/skills/skills-perk-exploitweakness" + }, + { + "tag": "Skills.Attribute.MentalCalculus5", + "id": "MentalCalculus5", + "name": "Rifle Damage", + "kind": "Attribute", + "row": 2, + "col": 4, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributedamagebonusscattergun_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-mentalcalculus5" + }, + { + "tag": "Skills.Attribute.MentalCalculus3", + "id": "MentalCalculus3", + "name": "Tailoring", + "kind": "Attribute", + "row": 3, + "col": 1, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributerepairefficiency_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-mentalcalculus3" + }, + { + "tag": "Skills.Perk.HeadShots", + "id": "HeadShots", + "name": "Marksman", + "kind": "Perk", + "row": 3, + "col": 3, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeperkmarksman_d.webp", + "url": "https://dune.gaming.tools/skills/skills-perk-headshots" + }, + { + "tag": "Skills.Attribute.MentalCalculus4", + "id": "MentalCalculus4", + "name": "Pistol Damage", + "kind": "Attribute", + "row": 3, + "col": 5, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributedamagebonusgun_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-mentalcalculus4" + }, + { + "tag": "Skills.Attribute.MentalCalculus1", + "id": "MentalCalculus1", + "name": "Garment Keeper", + "kind": "Attribute", + "row": 4, + "col": 2, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributerepair_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-mentalcalculus1" + }, + { + "tag": "Skills.Attribute.MentalCalculus2", + "id": "MentalCalculus2", + "name": "Ranged Damage", + "kind": "Attribute", + "row": 4, + "col": 4, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributedamage_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-mentalcalculus2" + }, + { + "tag": "Skills.Ability.TurretSeeker", + "id": "TurretSeeker", + "name": "The Sentinel", + "kind": "Ability", + "row": 5, + "col": 3, + "maxPoints": 3, + "icon": "t_ui_iconabilityturretseeker_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-turretseeker" + }, + { + "tag": "Skills.Ability.HunterSeeker", + "id": "HunterSeeker", + "name": "Hunter-Seeker", + "kind": "Ability", + "row": 1, + "col": 2, + "maxPoints": 1, + "icon": "t_ui_icongadgethunterseeker_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-hunterseeker" + }, + { + "tag": "Skills.Perk.PoisonTooth", + "id": "PoisonTooth", + "name": "Poison Tooth", + "kind": "Perk", + "row": 2, + "col": 1, + "maxPoints": 3, + "icon": "t_ui_iconskilltreepoisontooth_d.webp", + "url": "https://dune.gaming.tools/skills/skills-perk-poisontooth" + }, + { + "tag": "Skills.Ability.StunDart", + "id": "StunDart", + "name": "Stunner", + "kind": "Ability", + "row": 2, + "col": 3, + "maxPoints": 1, + "icon": "t_ui_iconabilitystunnerdart_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-stundart" + }, + { + "tag": "Skills.Attribute.Assassination2", + "id": "Assassination2", + "name": "Assassin's Shot", + "kind": "Attribute", + "row": 3, + "col": 2, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributedamage_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-assassination2" + }, + { + "tag": "Skills.Ability.PoisonMine", + "id": "PoisonMine", + "name": "Poison Mine", + "kind": "Ability", + "row": 4, + "col": 1, + "maxPoints": 3, + "icon": "t_ui_iconabilitypoisonmine_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-poisonmine" + }, + { + "tag": "Skills.Attribute.Assassination1", + "id": "Assassination1", + "name": "Headshot Damage", + "kind": "Attribute", + "row": 4, + "col": 3, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributeheadshotbonus_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-assassination1" + }, + { + "tag": "Skills.Ability.PoisonCapsuleLauncher", + "id": "PoisonCapsuleLauncher", + "name": "Poison Capsule", + "kind": "Ability", + "row": 5, + "col": 2, + "maxPoints": 3, + "icon": "t_ui_icongadgetpoisoncapsulelauncher_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-poisoncapsulelauncher" + }, + { + "tag": "Skills.Ability.PortableGenerator", + "id": "PortableGenerator", + "name": "Source of Power", + "kind": "Ability", + "row": 1, + "col": 2, + "maxPoints": 1, + "icon": "t_ui_iconabilityportablegenerator_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-portablegenerator" + }, + { + "tag": "Skills.Ability.SuspensorMine_Reduction", + "id": "SuspensorMine_Reduction", + "name": "Anti-gravity Mine", + "kind": "Ability", + "row": 2, + "col": 1, + "maxPoints": 1, + "icon": "t_ui_icongadgetreductionremotemine_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-suspensormine_reduction" + }, + { + "tag": "Skills.Perk.IronWill", + "id": "IronWill", + "name": "Iron Will", + "kind": "Perk", + "row": 2, + "col": 3, + "maxPoints": 1, + "icon": "t_ui_iconskilltreeironwill_d.webp", + "url": "https://dune.gaming.tools/skills/skills-perk-ironwill" + }, + { + "tag": "Skills.Ability.SuspensorMine_Amplification", + "id": "SuspensorMine_Amplification", + "name": "Gravity Mine", + "kind": "Ability", + "row": 4, + "col": 1, + "maxPoints": 1, + "icon": "t_ui_icongadgetamplificationremotemine_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-suspensormine_amplification" + }, + { + "tag": "Skills.Ability.SolidoDecoy", + "id": "SolidoDecoy", + "name": "Solido Decoy", + "kind": "Ability", + "row": 4, + "col": 3, + "maxPoints": 1, + "icon": "t_ui_iconabilitysolidodecoy_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-solidodecoy" + }, + { + "tag": "Skills.Ability.SuspensorWall", + "id": "SuspensorWall", + "name": "Shield Wall", + "kind": "Ability", + "row": 5, + "col": 2, + "maxPoints": 3, + "icon": "t_ui_iconabilitysuspensorwall_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-suspensorwall" + } + ], + "edges": [ + { + "from": "Skills.Perk.ExploitWeakness", + "to": "Skills.Perk.ShieldWeakpoint" + }, + { + "from": "Skills.Attribute.MentalCalculus5", + "to": "Skills.Perk.ShieldWeakpoint" + }, + { + "from": "Skills.Attribute.MentalCalculus3", + "to": "Skills.Perk.ExploitWeakness" + }, + { + "from": "Skills.Perk.ExploitWeakness", + "to": "Skills.Perk.HeadShots" + }, + { + "from": "Skills.Attribute.MentalCalculus5", + "to": "Skills.Perk.HeadShots" + }, + { + "from": "Skills.Attribute.MentalCalculus4", + "to": "Skills.Attribute.MentalCalculus5" + }, + { + "from": "Skills.Attribute.MentalCalculus1", + "to": "Skills.Attribute.MentalCalculus3" + }, + { + "from": "Skills.Attribute.MentalCalculus1", + "to": "Skills.Perk.HeadShots" + }, + { + "from": "Skills.Attribute.MentalCalculus2", + "to": "Skills.Perk.HeadShots" + }, + { + "from": "Skills.Attribute.MentalCalculus2", + "to": "Skills.Attribute.MentalCalculus4" + }, + { + "from": "Skills.Ability.TurretSeeker", + "to": "Skills.Attribute.MentalCalculus1" + }, + { + "from": "Skills.Ability.TurretSeeker", + "to": "Skills.Attribute.MentalCalculus2" + }, + { + "from": "Skills.Ability.HunterSeeker", + "to": "Skills.Perk.PoisonTooth" + }, + { + "from": "Skills.Ability.HunterSeeker", + "to": "Skills.Ability.StunDart" + }, + { + "from": "Skills.Attribute.Assassination2", + "to": "Skills.Perk.PoisonTooth" + }, + { + "from": "Skills.Ability.PoisonMine", + "to": "Skills.Perk.PoisonTooth" + }, + { + "from": "Skills.Ability.StunDart", + "to": "Skills.Attribute.Assassination2" + }, + { + "from": "Skills.Ability.StunDart", + "to": "Skills.Attribute.Assassination1" + }, + { + "from": "Skills.Ability.PoisonMine", + "to": "Skills.Attribute.Assassination2" + }, + { + "from": "Skills.Attribute.Assassination1", + "to": "Skills.Attribute.Assassination2" + }, + { + "from": "Skills.Ability.PoisonCapsuleLauncher", + "to": "Skills.Ability.PoisonMine" + }, + { + "from": "Skills.Ability.PoisonCapsuleLauncher", + "to": "Skills.Attribute.Assassination1" + } + ] +} \ No newline at end of file diff --git a/character-builder/data/skills-planetologist.json b/character-builder/data/skills-planetologist.json new file mode 100644 index 0000000..7ea223a --- /dev/null +++ b/character-builder/data/skills-planetologist.json @@ -0,0 +1,268 @@ +{ + "id": "planetologist", + "name": "Planetologist", + "nodes": [ + { + "tag": "Skills.Perk.BatteryExpert", + "id": "BatteryExpert", + "name": "Conservation of Energy", + "kind": "Perk", + "row": 1, + "col": 2, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeperkbatteryexpert_d.webp", + "url": "https://dune.gaming.tools/skills/skills-perk-batteryexpert" + }, + { + "tag": "Skills.Attribute.Scientist5", + "id": "Scientist5", + "name": "Compaction", + "kind": "Attribute", + "row": 2, + "col": 1, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributespiceyield_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-scientist5" + }, + { + "tag": "Skills.Science.m_PowerMax", + "id": "m_PowerMax", + "name": "Overcharge", + "kind": "Science", + "row": 2, + "col": 3, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributemineralyield_d.webp", + "url": "https://dune.gaming.tools/skills/skills-science-m_powermax" + }, + { + "tag": "Skills.Attribute.Scientist4", + "id": "Scientist4", + "name": "Deep Analysis", + "kind": "Attribute", + "row": 3, + "col": 2, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributemineralyield_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-scientist4" + }, + { + "tag": "Skills.Attribute.Scientist2", + "id": "Scientist2", + "name": "Dew Gathering", + "kind": "Attribute", + "row": 4, + "col": 1, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributewatheryield_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-scientist2" + }, + { + "tag": "Skills.Attribute.Scientist3", + "id": "Scientist3", + "name": "Rerouting", + "kind": "Attribute", + "row": 4, + "col": 3, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeskillpowerefficiency_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-scientist3" + }, + { + "tag": "Skills.Attribute.Scientist1", + "id": "Scientist1", + "name": "Cutteray Mining", + "kind": "Attribute", + "row": 5, + "col": 2, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributemineralyield_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-scientist1" + }, + { + "tag": "Skills.Attribute.Explorer5", + "id": "Explorer5", + "name": "Spice Surveyor", + "kind": "Attribute", + "row": 1, + "col": 2, + "maxPoints": 1, + "icon": "t_ui_iconskilltreeattributespice_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-explorer5" + }, + { + "tag": "Skills.Attribute.Explorer3", + "id": "Explorer3", + "name": "Scanner Mastery", + "kind": "Attribute", + "row": 2, + "col": 1, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributescanningbonus_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-explorer3" + }, + { + "tag": "Skills.Attribute.Explorer4", + "id": "Explorer4", + "name": "Stillsuit Seals", + "kind": "Attribute", + "row": 2, + "col": 3, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeskillhydration_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-explorer4" + }, + { + "tag": "Skills.Attribute.Explorer1", + "id": "Explorer1", + "name": "Cartographer", + "kind": "Attribute", + "row": 4, + "col": 1, + "maxPoints": 1, + "icon": "t_ui_iconskilltreeskillobservation_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-explorer1" + }, + { + "tag": "Skills.Attribute.Explorer2", + "id": "Explorer2", + "name": "Mountaineer", + "kind": "Attribute", + "row": 4, + "col": 3, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeskillclimber_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-explorer2" + }, + { + "tag": "Skills.Ability.SuspensorPad", + "id": "SuspensorPad", + "name": "Suspensor Pad", + "kind": "Ability", + "row": 5, + "col": 2, + "maxPoints": 1, + "icon": "t_ui_iconabilitysuspensorpad_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-suspensorpad" + }, + { + "tag": "Skills.Spice.VehicleHeat", + "id": "VehicleHeat", + "name": "Heat Management", + "kind": "Spice", + "row": 1, + "col": 2, + "maxPoints": 1, + "icon": "t_ui_iconskilltreeheatmanagement_d.webp", + "url": "https://dune.gaming.tools/skills/skills-spice-vehicleheat" + }, + { + "tag": "Skills.Attribute.Driver5", + "id": "Driver5", + "name": "Fuel Efficient Pilot", + "kind": "Attribute", + "row": 2, + "col": 1, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributevehicle_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-driver5" + }, + { + "tag": "Skills.Attribute.Driver6", + "id": "Driver6", + "name": "Sandcrawler Yield", + "kind": "Attribute", + "row": 2, + "col": 3, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributespiceyield_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-driver6" + }, + { + "tag": "Skills.Attribute.Driver4", + "id": "Driver4", + "name": "Vehicle Scanning", + "kind": "Attribute", + "row": 3, + "col": 2, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributescanningbonus_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-driver4" + }, + { + "tag": "Skills.Attribute.Driver2", + "id": "Driver2", + "name": "Fuel Efficient Driver", + "kind": "Attribute", + "row": 4, + "col": 1, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributevehicle_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-driver2" + }, + { + "tag": "Skills.Attribute.Driver3", + "id": "Driver3", + "name": "Vehicle Mining", + "kind": "Attribute", + "row": 4, + "col": 3, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributemineralyield_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-driver3" + }, + { + "tag": "Skills.Attribute.Driver1", + "id": "Driver1", + "name": "Vehicle Repair", + "kind": "Attribute", + "row": 5, + "col": 2, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributerepairefficiency_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-driver1" + } + ], + "edges": [ + { + "from": "Skills.Attribute.Scientist5", + "to": "Skills.Perk.BatteryExpert" + }, + { + "from": "Skills.Perk.BatteryExpert", + "to": "Skills.Science.m_PowerMax" + }, + { + "from": "Skills.Attribute.Scientist4", + "to": "Skills.Attribute.Scientist5" + }, + { + "from": "Skills.Attribute.Scientist2", + "to": "Skills.Attribute.Scientist5" + }, + { + "from": "Skills.Attribute.Scientist4", + "to": "Skills.Science.m_PowerMax" + }, + { + "from": "Skills.Attribute.Scientist3", + "to": "Skills.Science.m_PowerMax" + }, + { + "from": "Skills.Attribute.Scientist2", + "to": "Skills.Attribute.Scientist4" + }, + { + "from": "Skills.Attribute.Scientist3", + "to": "Skills.Attribute.Scientist4" + }, + { + "from": "Skills.Attribute.Scientist1", + "to": "Skills.Attribute.Scientist2" + }, + { + "from": "Skills.Attribute.Scientist1", + "to": "Skills.Attribute.Scientist3" + } + ] +} \ No newline at end of file diff --git a/character-builder/data/skills-swordmaster.json b/character-builder/data/skills-swordmaster.json new file mode 100644 index 0000000..81def91 --- /dev/null +++ b/character-builder/data/skills-swordmaster.json @@ -0,0 +1,338 @@ +{ + "id": "swordmaster", + "name": "Swordmaster", + "nodes": [ + { + "tag": "Skills.Spice.ParryBoost", + "id": "ParryBoost", + "name": "Precise Parry", + "kind": "Spice", + "row": 1, + "col": 2, + "maxPoints": 3, + "icon": "t_ui_iconskilltreepreciseparry_d.webp", + "url": "https://dune.gaming.tools/skills/skills-spice-parryboost" + }, + { + "tag": "Skills.Ability.Whirlwind", + "id": "Whirlwind", + "name": "Eye of the Storm", + "kind": "Ability", + "row": 2, + "col": 1, + "maxPoints": 3, + "icon": "t_ui_iconabilitywhirlwind_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-whirlwind" + }, + { + "tag": "Skills.Ability.RiposteBreak", + "id": "RiposteBreak", + "name": "Foil", + "kind": "Ability", + "row": 2, + "col": 3, + "maxPoints": 1, + "icon": "t_ui_iconabilitybreakingreposte_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-ripostebreak" + }, + { + "tag": "Skills.Attribute.Blade2", + "id": "Blade2", + "name": "Long Blade Damage", + "kind": "Attribute", + "row": 3, + "col": 2, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeskillbrawler_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-blade2" + }, + { + "tag": "Skills.Perk.MeleeChain", + "id": "MeleeChain", + "name": "Dance of Blades", + "kind": "Perk", + "row": 4, + "col": 1, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeperkbladechaining_d.webp", + "url": "https://dune.gaming.tools/skills/skills-perk-meleechain" + }, + { + "tag": "Skills.Ability.RiposteInjure", + "id": "RiposteInjure", + "name": "Retaliate", + "kind": "Ability", + "row": 4, + "col": 3, + "maxPoints": 1, + "icon": "t_ui_iconabilityinjuringreposte_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-riposteinjure" + }, + { + "tag": "Skills.Attribute.Blade1", + "id": "Blade1", + "name": "Blade Damage", + "kind": "Attribute", + "row": 5, + "col": 2, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeskillbrawler_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-blade1" + }, + { + "tag": "Skills.Perk.ThriveOnDanger", + "id": "ThriveOnDanger", + "name": "Thrive on Danger", + "kind": "Perk", + "row": 1, + "col": 2, + "maxPoints": 1, + "icon": "t_ui_iconskilltreethriveondanger_d.webp", + "url": "https://dune.gaming.tools/skills/skills-perk-thriveondanger" + }, + { + "tag": "Skills.Attribute.Resolve2", + "id": "Resolve2", + "name": "Solid Stance", + "kind": "Attribute", + "row": 2, + "col": 1, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributepoisedefense_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-resolve2" + }, + { + "tag": "Skills.Attribute.UnstoppableAttacks", + "id": "UnstoppableAttacks", + "name": "Confidence", + "kind": "Attribute", + "row": 2, + "col": 3, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributedamagemitigation_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-unstoppableattacks" + }, + { + "tag": "Skills.Attribute.Resolve1", + "id": "Resolve1", + "name": "Bleed Tolerance", + "kind": "Attribute", + "row": 4, + "col": 1, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeskillmaxhealth_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-resolve1" + }, + { + "tag": "Skills.Perk.ToughLunge", + "id": "ToughLunge", + "name": "Reckless Lunge", + "kind": "Perk", + "row": 4, + "col": 3, + "maxPoints": 3, + "icon": "t_ui_iconskilltreetoughlunge_d.webp", + "url": "https://dune.gaming.tools/skills/skills-perk-toughlunge" + }, + { + "tag": "Skills.Ability.DeflectionSlow", + "id": "DeflectionSlow", + "name": "Deflection", + "kind": "Ability", + "row": 5, + "col": 2, + "maxPoints": 1, + "icon": "t_ui_iconabilitydeflection_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-deflectionslow" + }, + { + "tag": "Skills.Spice.ShadowStrike", + "id": "ShadowStrike", + "name": "Prescient Strike", + "kind": "Spice", + "row": 1, + "col": 3, + "maxPoints": 1, + "icon": "t_ui_iconskilltreeprescientstrike_d.webp", + "url": "https://dune.gaming.tools/skills/skills-spice-shadowstrike" + }, + { + "tag": "Skills.Attribute.Aggression3", + "id": "Aggression3", + "name": "General Conditioning", + "kind": "Attribute", + "row": 2, + "col": 2, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributestamina_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-aggression3" + }, + { + "tag": "Skills.Attribute.Aggression4", + "id": "Aggression4", + "name": "Desert Conditioning", + "kind": "Attribute", + "row": 2, + "col": 4, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributewatherdefense_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-aggression4" + }, + { + "tag": "Skills.Ability.CripplingStrike", + "id": "CripplingStrike", + "name": "Crippling Strike", + "kind": "Ability", + "row": 3, + "col": 1, + "maxPoints": 1, + "icon": "t_ui_iconabilitycripplingstrike_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-cripplingstrike" + }, + { + "tag": "Skills.Perk.SprintStamina", + "id": "SprintStamina", + "name": "Disciplined Breathing", + "kind": "Perk", + "row": 3, + "col": 3, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeperkrunner_d.webp", + "url": "https://dune.gaming.tools/skills/skills-perk-sprintstamina" + }, + { + "tag": "Skills.Ability.BattleCry", + "id": "BattleCry", + "name": "Inspiration", + "kind": "Ability", + "row": 3, + "col": 5, + "maxPoints": 3, + "icon": "t_ui_iconabilitybattlecry_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-battlecry" + }, + { + "tag": "Skills.Attribute.Aggression1", + "id": "Aggression1", + "name": "Field Medicine", + "kind": "Attribute", + "row": 4, + "col": 2, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeskillhealingmultiplier_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-aggression1" + }, + { + "tag": "Skills.Attribute.Aggression2", + "id": "Aggression2", + "name": "Optimized Hydration", + "kind": "Attribute", + "row": 4, + "col": 4, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributewatherbonus_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-aggression2" + }, + { + "tag": "Skills.Ability.KneeCharge", + "id": "KneeCharge", + "name": "Knee Charge", + "kind": "Ability", + "row": 5, + "col": 3, + "maxPoints": 3, + "icon": "t_ui_iconabilitykneecharge_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-kneecharge" + } + ], + "edges": [ + { + "from": "Skills.Ability.Whirlwind", + "to": "Skills.Spice.ParryBoost" + }, + { + "from": "Skills.Ability.RiposteBreak", + "to": "Skills.Spice.ParryBoost" + }, + { + "from": "Skills.Ability.Whirlwind", + "to": "Skills.Attribute.Blade2" + }, + { + "from": "Skills.Ability.Whirlwind", + "to": "Skills.Perk.MeleeChain" + }, + { + "from": "Skills.Ability.RiposteBreak", + "to": "Skills.Attribute.Blade2" + }, + { + "from": "Skills.Ability.RiposteBreak", + "to": "Skills.Ability.RiposteInjure" + }, + { + "from": "Skills.Attribute.Blade2", + "to": "Skills.Perk.MeleeChain" + }, + { + "from": "Skills.Ability.RiposteInjure", + "to": "Skills.Attribute.Blade2" + }, + { + "from": "Skills.Attribute.Blade1", + "to": "Skills.Perk.MeleeChain" + }, + { + "from": "Skills.Ability.RiposteInjure", + "to": "Skills.Attribute.Blade1" + }, + { + "from": "Skills.Attribute.Aggression3", + "to": "Skills.Spice.ShadowStrike" + }, + { + "from": "Skills.Attribute.Aggression4", + "to": "Skills.Spice.ShadowStrike" + }, + { + "from": "Skills.Ability.CripplingStrike", + "to": "Skills.Attribute.Aggression3" + }, + { + "from": "Skills.Attribute.Aggression3", + "to": "Skills.Perk.SprintStamina" + }, + { + "from": "Skills.Attribute.Aggression4", + "to": "Skills.Perk.SprintStamina" + }, + { + "from": "Skills.Ability.BattleCry", + "to": "Skills.Attribute.Aggression4" + }, + { + "from": "Skills.Ability.CripplingStrike", + "to": "Skills.Attribute.Aggression1" + }, + { + "from": "Skills.Attribute.Aggression1", + "to": "Skills.Perk.SprintStamina" + }, + { + "from": "Skills.Attribute.Aggression2", + "to": "Skills.Perk.SprintStamina" + }, + { + "from": "Skills.Ability.BattleCry", + "to": "Skills.Attribute.Aggression2" + }, + { + "from": "Skills.Ability.KneeCharge", + "to": "Skills.Attribute.Aggression1" + }, + { + "from": "Skills.Ability.KneeCharge", + "to": "Skills.Attribute.Aggression2" + } + ] +} \ No newline at end of file diff --git a/character-builder/data/skills-trooper.json b/character-builder/data/skills-trooper.json new file mode 100644 index 0000000..15f8828 --- /dev/null +++ b/character-builder/data/skills-trooper.json @@ -0,0 +1,338 @@ +{ + "id": "trooper", + "name": "Trooper", + "nodes": [ + { + "tag": "Skills.Ability.EnergyCapsule", + "id": "EnergyCapsule", + "name": "Energy Capsule", + "kind": "Ability", + "row": 1, + "col": 3, + "maxPoints": 1, + "icon": "t_ui_iconabilityenergycapsule_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-energycapsule" + }, + { + "tag": "Skills.Attribute.Weaponry5", + "id": "Weaponry5", + "name": "Heavy Weapon Damage", + "kind": "Attribute", + "row": 2, + "col": 2, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributedamagebonusheavyweapon_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-weaponry5" + }, + { + "tag": "Skills.Attribute.Weaponry6", + "id": "Weaponry6", + "name": "Gunsmith", + "kind": "Attribute", + "row": 2, + "col": 4, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributerepairefficiency_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-weaponry6" + }, + { + "tag": "Skills.Perk.HeavyWeaponNaib", + "id": "HeavyWeaponNaib", + "name": "Heavy Weapon Agility", + "kind": "Perk", + "row": 3, + "col": 1, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeperkheavyweaponnaib_d.webp", + "url": "https://dune.gaming.tools/skills/skills-perk-heavyweaponnaib" + }, + { + "tag": "Skills.Attribute.Weaponry3", + "id": "Weaponry3", + "name": "Scattergun Damage", + "kind": "Attribute", + "row": 3, + "col": 3, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributedamagebonusrifle_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-weaponry3" + }, + { + "tag": "Skills.Attribute.Weaponry4", + "id": "Weaponry4", + "name": "Field Maintenance", + "kind": "Attribute", + "row": 3, + "col": 5, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributerepair_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-weaponry4" + }, + { + "tag": "Skills.Attribute.Weaponry2", + "id": "Weaponry2", + "name": "Disruptor Damage", + "kind": "Attribute", + "row": 4, + "col": 2, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributedamagebonussmg_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-weaponry2" + }, + { + "tag": "Skills.Perk.BodyShots", + "id": "BodyShots", + "name": "Center of Mass", + "kind": "Perk", + "row": 4, + "col": 4, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeperkcentralaim_d.webp", + "url": "https://dune.gaming.tools/skills/skills-perk-bodyshots" + }, + { + "tag": "Skills.Attribute.Weaponry1", + "id": "Weaponry1", + "name": "Ranged Damage", + "kind": "Attribute", + "row": 5, + "col": 3, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributedamage_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-weaponry1" + }, + { + "tag": "Skills.Ability.SuspensorBlast", + "id": "SuspensorBlast", + "name": "Suspensor Blast", + "kind": "Ability", + "row": 1, + "col": 2, + "maxPoints": 1, + "icon": "t_ui_iconabilitysuspensorblast_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-suspensorblast" + }, + { + "tag": "Skills.Perk.DeathFromAbove", + "id": "DeathFromAbove", + "name": "Death from Above", + "kind": "Perk", + "row": 2, + "col": 1, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeperkdeathfromabove_d.webp", + "url": "https://dune.gaming.tools/skills/skills-perk-deathfromabove" + }, + { + "tag": "Skills.Ability.CollapseGrenade", + "id": "CollapseGrenade", + "name": "Collapse Grenade", + "kind": "Ability", + "row": 2, + "col": 3, + "maxPoints": 1, + "icon": "t_ui_iconabilitycollapsegrenade_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-collapsegrenade" + }, + { + "tag": "Skills.Attribute.SuspensorTech1", + "id": "SuspensorTech1", + "name": "Suspensor Efficiency", + "kind": "Attribute", + "row": 3, + "col": 2, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeskillpowerefficiency_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-suspensortech1" + }, + { + "tag": "Skills.Perk.SuspensorDash", + "id": "SuspensorDash", + "name": "Suspensor Dash", + "kind": "Perk", + "row": 4, + "col": 1, + "maxPoints": 1, + "icon": "t_ui_iconskilltreesuspensordash_d.webp", + "url": "https://dune.gaming.tools/skills/skills-perk-suspensordash" + }, + { + "tag": "Skills.Ability.SuspensorGrenade_Amplification", + "id": "SuspensorGrenade_Amplification", + "name": "Gravity Field", + "kind": "Ability", + "row": 4, + "col": 3, + "maxPoints": 1, + "icon": "t_ui_icongadgetamplificationgrenade_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-suspensorgrenade_amplification" + }, + { + "tag": "Skills.Ability.SuspensorGrenade_Reduction", + "id": "SuspensorGrenade_Reduction", + "name": "Anti-gravity Field", + "kind": "Ability", + "row": 5, + "col": 2, + "maxPoints": 1, + "icon": "t_ui_icongadgetreductionsuspensorgrenade_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-suspensorgrenade_reduction" + }, + { + "tag": "Skills.Spice.GadgetReload", + "id": "GadgetReload", + "name": "Reflexive Reload", + "kind": "Spice", + "row": 1, + "col": 2, + "maxPoints": 1, + "icon": "t_ui_iconskilltreereflexivereload_d.webp", + "url": "https://dune.gaming.tools/skills/skills-spice-gadgetreload" + }, + { + "tag": "Skills.Ability.AssaultSeeker", + "id": "AssaultSeeker", + "name": "Assault Seeker", + "kind": "Ability", + "row": 2, + "col": 1, + "maxPoints": 3, + "icon": "t_ui_iconabilityassaultseeker_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-assaultseeker" + }, + { + "tag": "Skills.Ability.MagneticAttractor", + "id": "MagneticAttractor", + "name": "Attractor Field", + "kind": "Ability", + "row": 2, + "col": 3, + "maxPoints": 1, + "icon": "t_ui_icongadgetshigmultitoolmagneticattractor_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-magneticattractor" + }, + { + "tag": "Skills.Ability.FragGrenade", + "id": "FragGrenade", + "name": "Explosive Grenade", + "kind": "Ability", + "row": 4, + "col": 1, + "maxPoints": 3, + "icon": "t_ui_icongadgetfraggrenades_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-fraggrenade" + }, + { + "tag": "Skills.Perk.TrooperCooldowns", + "id": "TrooperCooldowns", + "name": "Battle Hardened", + "kind": "Perk", + "row": 4, + "col": 3, + "maxPoints": 3, + "icon": "t_ui_iconskilltreetroopercooldown_d.webp", + "url": "https://dune.gaming.tools/skills/skills-perk-troopercooldowns" + }, + { + "tag": "Skills.Ability.CablePull", + "id": "CablePull", + "name": "Shigawire Claw", + "kind": "Ability", + "row": 5, + "col": 2, + "maxPoints": 3, + "icon": "t_ui_icongadgetshigmultitoolsardaukarpull_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-cablepull" + } + ], + "edges": [ + { + "from": "Skills.Ability.EnergyCapsule", + "to": "Skills.Attribute.Weaponry5" + }, + { + "from": "Skills.Ability.EnergyCapsule", + "to": "Skills.Attribute.Weaponry6" + }, + { + "from": "Skills.Attribute.Weaponry5", + "to": "Skills.Perk.HeavyWeaponNaib" + }, + { + "from": "Skills.Attribute.Weaponry3", + "to": "Skills.Attribute.Weaponry5" + }, + { + "from": "Skills.Attribute.Weaponry3", + "to": "Skills.Attribute.Weaponry6" + }, + { + "from": "Skills.Attribute.Weaponry4", + "to": "Skills.Attribute.Weaponry6" + }, + { + "from": "Skills.Attribute.Weaponry2", + "to": "Skills.Perk.HeavyWeaponNaib" + }, + { + "from": "Skills.Attribute.Weaponry2", + "to": "Skills.Attribute.Weaponry3" + }, + { + "from": "Skills.Attribute.Weaponry3", + "to": "Skills.Perk.BodyShots" + }, + { + "from": "Skills.Attribute.Weaponry4", + "to": "Skills.Perk.BodyShots" + }, + { + "from": "Skills.Attribute.Weaponry1", + "to": "Skills.Attribute.Weaponry2" + }, + { + "from": "Skills.Attribute.Weaponry1", + "to": "Skills.Perk.BodyShots" + }, + { + "from": "Skills.Ability.SuspensorBlast", + "to": "Skills.Perk.DeathFromAbove" + }, + { + "from": "Skills.Ability.CollapseGrenade", + "to": "Skills.Ability.SuspensorBlast" + }, + { + "from": "Skills.Attribute.SuspensorTech1", + "to": "Skills.Perk.DeathFromAbove" + }, + { + "from": "Skills.Perk.DeathFromAbove", + "to": "Skills.Perk.SuspensorDash" + }, + { + "from": "Skills.Ability.CollapseGrenade", + "to": "Skills.Attribute.SuspensorTech1" + }, + { + "from": "Skills.Ability.CollapseGrenade", + "to": "Skills.Ability.SuspensorGrenade_Amplification" + }, + { + "from": "Skills.Attribute.SuspensorTech1", + "to": "Skills.Perk.SuspensorDash" + }, + { + "from": "Skills.Ability.SuspensorGrenade_Amplification", + "to": "Skills.Attribute.SuspensorTech1" + }, + { + "from": "Skills.Ability.SuspensorGrenade_Reduction", + "to": "Skills.Perk.SuspensorDash" + }, + { + "from": "Skills.Ability.SuspensorGrenade_Amplification", + "to": "Skills.Ability.SuspensorGrenade_Reduction" + } + ] +} \ No newline at end of file diff --git a/character-builder/data/spec-combat.json b/character-builder/data/spec-combat.json new file mode 100644 index 0000000..d09f120 --- /dev/null +++ b/character-builder/data/spec-combat.json @@ -0,0 +1,610 @@ +{ + "header": [ + "Level", + "Required XP", + "Cumulative", + "Rewards" + ], + "rows": [ + { + "level": 1, + "xpRequired": 100, + "totalXp": 100, + "intelPoints": 23 + }, + { + "level": 2, + "xpRequired": 105, + "totalXp": 205, + "intelPoints": 25 + }, + { + "level": 3, + "xpRequired": 110, + "totalXp": 315, + "intelPoints": 0 + }, + { + "level": 4, + "xpRequired": 116, + "totalXp": 431, + "intelPoints": 21 + }, + { + "level": 5, + "xpRequired": 122, + "totalXp": 553, + "intelPoints": 0 + }, + { + "level": 6, + "xpRequired": 128, + "totalXp": 681, + "intelPoints": 215 + }, + { + "level": 7, + "xpRequired": 135, + "totalXp": 816, + "intelPoints": 0 + }, + { + "level": 8, + "xpRequired": 142, + "totalXp": 958, + "intelPoints": 23 + }, + { + "level": 9, + "xpRequired": 149, + "totalXp": 1107, + "intelPoints": 0 + }, + { + "level": 10, + "xpRequired": 157, + "totalXp": 1264, + "intelPoints": 0 + }, + { + "level": 11, + "xpRequired": 164, + "totalXp": 1428, + "intelPoints": 51 + }, + { + "level": 12, + "xpRequired": 171, + "totalXp": 1599, + "intelPoints": 0 + }, + { + "level": 13, + "xpRequired": 178, + "totalXp": 1777, + "intelPoints": 0 + }, + { + "level": 14, + "xpRequired": 186, + "totalXp": 1963, + "intelPoints": 51 + }, + { + "level": 15, + "xpRequired": 194, + "totalXp": 2157, + "intelPoints": 0 + }, + { + "level": 16, + "xpRequired": 202, + "totalXp": 2359, + "intelPoints": 520 + }, + { + "level": 17, + "xpRequired": 211, + "totalXp": 2570, + "intelPoints": 0 + }, + { + "level": 18, + "xpRequired": 220, + "totalXp": 2790, + "intelPoints": 53 + }, + { + "level": 19, + "xpRequired": 229, + "totalXp": 3019, + "intelPoints": 0 + }, + { + "level": 20, + "xpRequired": 239, + "totalXp": 3258, + "intelPoints": 0 + }, + { + "level": 21, + "xpRequired": 246, + "totalXp": 3504, + "intelPoints": 51 + }, + { + "level": 22, + "xpRequired": 253, + "totalXp": 3757, + "intelPoints": 0 + }, + { + "level": 23, + "xpRequired": 260, + "totalXp": 4017, + "intelPoints": 0 + }, + { + "level": 24, + "xpRequired": 268, + "totalXp": 4285, + "intelPoints": 101 + }, + { + "level": 25, + "xpRequired": 276, + "totalXp": 4561, + "intelPoints": 0 + }, + { + "level": 26, + "xpRequired": 284, + "totalXp": 4845, + "intelPoints": 105 + }, + { + "level": 27, + "xpRequired": 292, + "totalXp": 5137, + "intelPoints": 0 + }, + { + "level": 28, + "xpRequired": 301, + "totalXp": 5438, + "intelPoints": 103 + }, + { + "level": 29, + "xpRequired": 310, + "totalXp": 5748, + "intelPoints": 0 + }, + { + "level": 30, + "xpRequired": 319, + "totalXp": 6067, + "intelPoints": 0 + }, + { + "level": 31, + "xpRequired": 326, + "totalXp": 6393, + "intelPoints": 101 + }, + { + "level": 32, + "xpRequired": 334, + "totalXp": 6727, + "intelPoints": 0 + }, + { + "level": 33, + "xpRequired": 342, + "totalXp": 7069, + "intelPoints": 0 + }, + { + "level": 34, + "xpRequired": 350, + "totalXp": 7419, + "intelPoints": 101 + }, + { + "level": 35, + "xpRequired": 358, + "totalXp": 7777, + "intelPoints": 0 + }, + { + "level": 36, + "xpRequired": 366, + "totalXp": 8143, + "intelPoints": 155 + }, + { + "level": 37, + "xpRequired": 375, + "totalXp": 8518, + "intelPoints": 0 + }, + { + "level": 38, + "xpRequired": 384, + "totalXp": 8902, + "intelPoints": 153 + }, + { + "level": 39, + "xpRequired": 393, + "totalXp": 9295, + "intelPoints": 0 + }, + { + "level": 40, + "xpRequired": 402, + "totalXp": 9697, + "intelPoints": 0 + }, + { + "level": 41, + "xpRequired": 410, + "totalXp": 10107, + "intelPoints": 151 + }, + { + "level": 42, + "xpRequired": 418, + "totalXp": 10525, + "intelPoints": 0 + }, + { + "level": 43, + "xpRequired": 426, + "totalXp": 10951, + "intelPoints": 0 + }, + { + "level": 44, + "xpRequired": 434, + "totalXp": 11385, + "intelPoints": 151 + }, + { + "level": 45, + "xpRequired": 442, + "totalXp": 11827, + "intelPoints": 0 + }, + { + "level": 46, + "xpRequired": 450, + "totalXp": 12277, + "intelPoints": 0 + }, + { + "level": 47, + "xpRequired": 459, + "totalXp": 12736, + "intelPoints": 0 + }, + { + "level": 48, + "xpRequired": 468, + "totalXp": 13204, + "intelPoints": 153 + }, + { + "level": 49, + "xpRequired": 477, + "totalXp": 13681, + "intelPoints": 0 + }, + { + "level": 50, + "xpRequired": 486, + "totalXp": 14167, + "intelPoints": 0 + }, + { + "level": 51, + "xpRequired": 494, + "totalXp": 14661, + "intelPoints": 201 + }, + { + "level": 52, + "xpRequired": 502, + "totalXp": 15163, + "intelPoints": 0 + }, + { + "level": 53, + "xpRequired": 510, + "totalXp": 15673, + "intelPoints": 0 + }, + { + "level": 54, + "xpRequired": 518, + "totalXp": 16191, + "intelPoints": 201 + }, + { + "level": 55, + "xpRequired": 526, + "totalXp": 16717, + "intelPoints": 0 + }, + { + "level": 56, + "xpRequired": 534, + "totalXp": 17251, + "intelPoints": 205 + }, + { + "level": 57, + "xpRequired": 543, + "totalXp": 17794, + "intelPoints": 0 + }, + { + "level": 58, + "xpRequired": 552, + "totalXp": 18346, + "intelPoints": 203 + }, + { + "level": 59, + "xpRequired": 561, + "totalXp": 18907, + "intelPoints": 0 + }, + { + "level": 60, + "xpRequired": 570, + "totalXp": 19477, + "intelPoints": 0 + }, + { + "level": 61, + "xpRequired": 575, + "totalXp": 20052, + "intelPoints": 201 + }, + { + "level": 62, + "xpRequired": 580, + "totalXp": 20632, + "intelPoints": 0 + }, + { + "level": 63, + "xpRequired": 585, + "totalXp": 21217, + "intelPoints": 0 + }, + { + "level": 64, + "xpRequired": 590, + "totalXp": 21807, + "intelPoints": 251 + }, + { + "level": 65, + "xpRequired": 595, + "totalXp": 22402, + "intelPoints": 0 + }, + { + "level": 66, + "xpRequired": 600, + "totalXp": 23002, + "intelPoints": 255 + }, + { + "level": 67, + "xpRequired": 606, + "totalXp": 23608, + "intelPoints": 0 + }, + { + "level": 68, + "xpRequired": 612, + "totalXp": 24220, + "intelPoints": 253 + }, + { + "level": 69, + "xpRequired": 618, + "totalXp": 24838, + "intelPoints": 0 + }, + { + "level": 70, + "xpRequired": 624, + "totalXp": 25462, + "intelPoints": 0 + }, + { + "level": 71, + "xpRequired": 624, + "totalXp": 26086, + "intelPoints": 251 + }, + { + "level": 72, + "xpRequired": 624, + "totalXp": 26710, + "intelPoints": 0 + }, + { + "level": 73, + "xpRequired": 624, + "totalXp": 27334, + "intelPoints": 0 + }, + { + "level": 74, + "xpRequired": 624, + "totalXp": 27958, + "intelPoints": 251 + }, + { + "level": 75, + "xpRequired": 624, + "totalXp": 28582, + "intelPoints": 30 + }, + { + "level": 76, + "xpRequired": 624, + "totalXp": 29206, + "intelPoints": 0 + }, + { + "level": 77, + "xpRequired": 624, + "totalXp": 29830, + "intelPoints": 3025 + }, + { + "level": 78, + "xpRequired": 624, + "totalXp": 30454, + "intelPoints": 0 + }, + { + "level": 79, + "xpRequired": 624, + "totalXp": 31078, + "intelPoints": 303 + }, + { + "level": 80, + "xpRequired": 624, + "totalXp": 31702, + "intelPoints": 0 + }, + { + "level": 81, + "xpRequired": 624, + "totalXp": 32326, + "intelPoints": 0 + }, + { + "level": 82, + "xpRequired": 624, + "totalXp": 32950, + "intelPoints": 301 + }, + { + "level": 83, + "xpRequired": 624, + "totalXp": 33574, + "intelPoints": 0 + }, + { + "level": 84, + "xpRequired": 624, + "totalXp": 34198, + "intelPoints": 0 + }, + { + "level": 85, + "xpRequired": 624, + "totalXp": 34822, + "intelPoints": 301 + }, + { + "level": 86, + "xpRequired": 624, + "totalXp": 35446, + "intelPoints": 0 + }, + { + "level": 87, + "xpRequired": 624, + "totalXp": 36070, + "intelPoints": 0 + }, + { + "level": 88, + "xpRequired": 624, + "totalXp": 36694, + "intelPoints": 0 + }, + { + "level": 89, + "xpRequired": 624, + "totalXp": 37318, + "intelPoints": 353 + }, + { + "level": 90, + "xpRequired": 624, + "totalXp": 37942, + "intelPoints": 0 + }, + { + "level": 91, + "xpRequired": 624, + "totalXp": 38566, + "intelPoints": 355 + }, + { + "level": 92, + "xpRequired": 624, + "totalXp": 39190, + "intelPoints": 0 + }, + { + "level": 93, + "xpRequired": 624, + "totalXp": 39814, + "intelPoints": 351 + }, + { + "level": 94, + "xpRequired": 624, + "totalXp": 40438, + "intelPoints": 0 + }, + { + "level": 95, + "xpRequired": 624, + "totalXp": 41062, + "intelPoints": 3520 + }, + { + "level": 96, + "xpRequired": 624, + "totalXp": 41686, + "intelPoints": 0 + }, + { + "level": 97, + "xpRequired": 624, + "totalXp": 42310, + "intelPoints": 351 + }, + { + "level": 98, + "xpRequired": 624, + "totalXp": 42934, + "intelPoints": 0 + }, + { + "level": 99, + "xpRequired": 624, + "totalXp": 43558, + "intelPoints": 0 + }, + { + "level": 100, + "xpRequired": 624, + "totalXp": 44182, + "intelPoints": 405 + } + ] +} \ No newline at end of file diff --git a/character-builder/data/spec-crafting.json b/character-builder/data/spec-crafting.json new file mode 100644 index 0000000..c407e85 --- /dev/null +++ b/character-builder/data/spec-crafting.json @@ -0,0 +1,610 @@ +{ + "header": [ + "Level", + "Required XP", + "Cumulative", + "Rewards" + ], + "rows": [ + { + "level": 1, + "xpRequired": 100, + "totalXp": 100, + "intelPoints": 1 + }, + { + "level": 2, + "xpRequired": 105, + "totalXp": 205, + "intelPoints": 0 + }, + { + "level": 3, + "xpRequired": 110, + "totalXp": 315, + "intelPoints": 21 + }, + { + "level": 4, + "xpRequired": 116, + "totalXp": 431, + "intelPoints": 0 + }, + { + "level": 5, + "xpRequired": 122, + "totalXp": 553, + "intelPoints": 0 + }, + { + "level": 6, + "xpRequired": 128, + "totalXp": 681, + "intelPoints": 0 + }, + { + "level": 7, + "xpRequired": 135, + "totalXp": 816, + "intelPoints": 0 + }, + { + "level": 8, + "xpRequired": 142, + "totalXp": 958, + "intelPoints": 2250 + }, + { + "level": 9, + "xpRequired": 149, + "totalXp": 1107, + "intelPoints": 0 + }, + { + "level": 10, + "xpRequired": 157, + "totalXp": 1264, + "intelPoints": 21 + }, + { + "level": 11, + "xpRequired": 164, + "totalXp": 1428, + "intelPoints": 0 + }, + { + "level": 12, + "xpRequired": 171, + "totalXp": 1599, + "intelPoints": 5100 + }, + { + "level": 13, + "xpRequired": 178, + "totalXp": 1777, + "intelPoints": 0 + }, + { + "level": 14, + "xpRequired": 186, + "totalXp": 1963, + "intelPoints": 5333 + }, + { + "level": 15, + "xpRequired": 194, + "totalXp": 2157, + "intelPoints": 0 + }, + { + "level": 16, + "xpRequired": 202, + "totalXp": 2359, + "intelPoints": 550 + }, + { + "level": 17, + "xpRequired": 211, + "totalXp": 2570, + "intelPoints": 0 + }, + { + "level": 18, + "xpRequired": 220, + "totalXp": 2790, + "intelPoints": 5 + }, + { + "level": 19, + "xpRequired": 229, + "totalXp": 3019, + "intelPoints": 0 + }, + { + "level": 20, + "xpRequired": 239, + "totalXp": 3258, + "intelPoints": 550 + }, + { + "level": 21, + "xpRequired": 246, + "totalXp": 3504, + "intelPoints": 0 + }, + { + "level": 22, + "xpRequired": 253, + "totalXp": 3757, + "intelPoints": 10300 + }, + { + "level": 23, + "xpRequired": 260, + "totalXp": 4017, + "intelPoints": 0 + }, + { + "level": 24, + "xpRequired": 268, + "totalXp": 4285, + "intelPoints": 0 + }, + { + "level": 25, + "xpRequired": 276, + "totalXp": 4561, + "intelPoints": 0 + }, + { + "level": 26, + "xpRequired": 284, + "totalXp": 4845, + "intelPoints": 0 + }, + { + "level": 27, + "xpRequired": 292, + "totalXp": 5137, + "intelPoints": 0 + }, + { + "level": 28, + "xpRequired": 301, + "totalXp": 5438, + "intelPoints": 0 + }, + { + "level": 29, + "xpRequired": 310, + "totalXp": 5748, + "intelPoints": 0 + }, + { + "level": 30, + "xpRequired": 319, + "totalXp": 6067, + "intelPoints": 101 + }, + { + "level": 31, + "xpRequired": 326, + "totalXp": 6393, + "intelPoints": 101 + }, + { + "level": 32, + "xpRequired": 334, + "totalXp": 6727, + "intelPoints": 0 + }, + { + "level": 33, + "xpRequired": 342, + "totalXp": 7069, + "intelPoints": 1525 + }, + { + "level": 34, + "xpRequired": 350, + "totalXp": 7419, + "intelPoints": 0 + }, + { + "level": 35, + "xpRequired": 358, + "totalXp": 7777, + "intelPoints": 1550 + }, + { + "level": 36, + "xpRequired": 366, + "totalXp": 8143, + "intelPoints": 0 + }, + { + "level": 37, + "xpRequired": 375, + "totalXp": 8518, + "intelPoints": 0 + }, + { + "level": 38, + "xpRequired": 384, + "totalXp": 8902, + "intelPoints": 15 + }, + { + "level": 39, + "xpRequired": 393, + "totalXp": 9295, + "intelPoints": 0 + }, + { + "level": 40, + "xpRequired": 402, + "totalXp": 9697, + "intelPoints": 15333 + }, + { + "level": 41, + "xpRequired": 410, + "totalXp": 10107, + "intelPoints": 0 + }, + { + "level": 42, + "xpRequired": 418, + "totalXp": 10525, + "intelPoints": 151 + }, + { + "level": 43, + "xpRequired": 426, + "totalXp": 10951, + "intelPoints": 0 + }, + { + "level": 44, + "xpRequired": 434, + "totalXp": 11385, + "intelPoints": 0 + }, + { + "level": 45, + "xpRequired": 442, + "totalXp": 11827, + "intelPoints": 20100 + }, + { + "level": 46, + "xpRequired": 450, + "totalXp": 12277, + "intelPoints": 0 + }, + { + "level": 47, + "xpRequired": 459, + "totalXp": 12736, + "intelPoints": 0 + }, + { + "level": 48, + "xpRequired": 468, + "totalXp": 13204, + "intelPoints": 0 + }, + { + "level": 49, + "xpRequired": 477, + "totalXp": 13681, + "intelPoints": 0 + }, + { + "level": 50, + "xpRequired": 486, + "totalXp": 14167, + "intelPoints": 0 + }, + { + "level": 51, + "xpRequired": 494, + "totalXp": 14661, + "intelPoints": 0 + }, + { + "level": 52, + "xpRequired": 502, + "totalXp": 15163, + "intelPoints": 20 + }, + { + "level": 53, + "xpRequired": 510, + "totalXp": 15673, + "intelPoints": 0 + }, + { + "level": 54, + "xpRequired": 518, + "totalXp": 16191, + "intelPoints": 0 + }, + { + "level": 55, + "xpRequired": 526, + "totalXp": 16717, + "intelPoints": 2025 + }, + { + "level": 56, + "xpRequired": 534, + "totalXp": 17251, + "intelPoints": 0 + }, + { + "level": 57, + "xpRequired": 543, + "totalXp": 17794, + "intelPoints": 0 + }, + { + "level": 58, + "xpRequired": 552, + "totalXp": 18346, + "intelPoints": 0 + }, + { + "level": 59, + "xpRequired": 561, + "totalXp": 18907, + "intelPoints": 0 + }, + { + "level": 60, + "xpRequired": 570, + "totalXp": 19477, + "intelPoints": 0 + }, + { + "level": 61, + "xpRequired": 575, + "totalXp": 20052, + "intelPoints": 25150 + }, + { + "level": 62, + "xpRequired": 580, + "totalXp": 20632, + "intelPoints": 0 + }, + { + "level": 63, + "xpRequired": 585, + "totalXp": 21217, + "intelPoints": 25300 + }, + { + "level": 64, + "xpRequired": 590, + "totalXp": 21807, + "intelPoints": 0 + }, + { + "level": 65, + "xpRequired": 595, + "totalXp": 22402, + "intelPoints": 0 + }, + { + "level": 66, + "xpRequired": 600, + "totalXp": 23002, + "intelPoints": 0 + }, + { + "level": 67, + "xpRequired": 606, + "totalXp": 23608, + "intelPoints": 0 + }, + { + "level": 68, + "xpRequired": 612, + "totalXp": 24220, + "intelPoints": 0 + }, + { + "level": 69, + "xpRequired": 618, + "totalXp": 24838, + "intelPoints": 2550 + }, + { + "level": 70, + "xpRequired": 624, + "totalXp": 25462, + "intelPoints": 0 + }, + { + "level": 71, + "xpRequired": 624, + "totalXp": 26086, + "intelPoints": 0 + }, + { + "level": 72, + "xpRequired": 624, + "totalXp": 26710, + "intelPoints": 25 + }, + { + "level": 73, + "xpRequired": 624, + "totalXp": 27334, + "intelPoints": 0 + }, + { + "level": 74, + "xpRequired": 624, + "totalXp": 27958, + "intelPoints": 0 + }, + { + "level": 75, + "xpRequired": 624, + "totalXp": 28582, + "intelPoints": 30 + }, + { + "level": 76, + "xpRequired": 624, + "totalXp": 29206, + "intelPoints": 0 + }, + { + "level": 77, + "xpRequired": 624, + "totalXp": 29830, + "intelPoints": 0 + }, + { + "level": 78, + "xpRequired": 624, + "totalXp": 30454, + "intelPoints": 3050 + }, + { + "level": 79, + "xpRequired": 624, + "totalXp": 31078, + "intelPoints": 0 + }, + { + "level": 80, + "xpRequired": 624, + "totalXp": 31702, + "intelPoints": 30333 + }, + { + "level": 81, + "xpRequired": 624, + "totalXp": 32326, + "intelPoints": 0 + }, + { + "level": 82, + "xpRequired": 624, + "totalXp": 32950, + "intelPoints": 0 + }, + { + "level": 83, + "xpRequired": 624, + "totalXp": 33574, + "intelPoints": 0 + }, + { + "level": 84, + "xpRequired": 624, + "totalXp": 34198, + "intelPoints": 0 + }, + { + "level": 85, + "xpRequired": 624, + "totalXp": 34822, + "intelPoints": 30100 + }, + { + "level": 86, + "xpRequired": 624, + "totalXp": 35446, + "intelPoints": 0 + }, + { + "level": 87, + "xpRequired": 624, + "totalXp": 36070, + "intelPoints": 351 + }, + { + "level": 88, + "xpRequired": 624, + "totalXp": 36694, + "intelPoints": 351 + }, + { + "level": 89, + "xpRequired": 624, + "totalXp": 37318, + "intelPoints": 0 + }, + { + "level": 90, + "xpRequired": 624, + "totalXp": 37942, + "intelPoints": 0 + }, + { + "level": 91, + "xpRequired": 624, + "totalXp": 38566, + "intelPoints": 0 + }, + { + "level": 92, + "xpRequired": 624, + "totalXp": 39190, + "intelPoints": 0 + }, + { + "level": 93, + "xpRequired": 624, + "totalXp": 39814, + "intelPoints": 35 + }, + { + "level": 94, + "xpRequired": 624, + "totalXp": 40438, + "intelPoints": 0 + }, + { + "level": 95, + "xpRequired": 624, + "totalXp": 41062, + "intelPoints": 0 + }, + { + "level": 96, + "xpRequired": 624, + "totalXp": 41686, + "intelPoints": 3550 + }, + { + "level": 97, + "xpRequired": 624, + "totalXp": 42310, + "intelPoints": 0 + }, + { + "level": 98, + "xpRequired": 624, + "totalXp": 42934, + "intelPoints": 0 + }, + { + "level": 99, + "xpRequired": 624, + "totalXp": 43558, + "intelPoints": 0 + }, + { + "level": 100, + "xpRequired": 624, + "totalXp": 44182, + "intelPoints": 40150 + } + ] +} \ No newline at end of file diff --git a/character-builder/data/spec-exploration.json b/character-builder/data/spec-exploration.json new file mode 100644 index 0000000..66c635b --- /dev/null +++ b/character-builder/data/spec-exploration.json @@ -0,0 +1,610 @@ +{ + "header": [ + "Level", + "Required XP", + "Cumulative", + "Rewards" + ], + "rows": [ + { + "level": 1, + "xpRequired": 100, + "totalXp": 100, + "intelPoints": 215 + }, + { + "level": 2, + "xpRequired": 105, + "totalXp": 205, + "intelPoints": 0 + }, + { + "level": 3, + "xpRequired": 110, + "totalXp": 315, + "intelPoints": 0 + }, + { + "level": 4, + "xpRequired": 116, + "totalXp": 431, + "intelPoints": 0 + }, + { + "level": 5, + "xpRequired": 122, + "totalXp": 553, + "intelPoints": 0 + }, + { + "level": 6, + "xpRequired": 128, + "totalXp": 681, + "intelPoints": 250 + }, + { + "level": 7, + "xpRequired": 135, + "totalXp": 816, + "intelPoints": 0 + }, + { + "level": 8, + "xpRequired": 142, + "totalXp": 958, + "intelPoints": 0 + }, + { + "level": 9, + "xpRequired": 149, + "totalXp": 1107, + "intelPoints": 0 + }, + { + "level": 10, + "xpRequired": 157, + "totalXp": 1264, + "intelPoints": 2 + }, + { + "level": 11, + "xpRequired": 164, + "totalXp": 1428, + "intelPoints": 0 + }, + { + "level": 12, + "xpRequired": 171, + "totalXp": 1599, + "intelPoints": 0 + }, + { + "level": 13, + "xpRequired": 178, + "totalXp": 1777, + "intelPoints": 0 + }, + { + "level": 14, + "xpRequired": 186, + "totalXp": 1963, + "intelPoints": 0 + }, + { + "level": 15, + "xpRequired": 194, + "totalXp": 2157, + "intelPoints": 0 + }, + { + "level": 16, + "xpRequired": 202, + "totalXp": 2359, + "intelPoints": 0 + }, + { + "level": 17, + "xpRequired": 211, + "totalXp": 2570, + "intelPoints": 5150 + }, + { + "level": 18, + "xpRequired": 220, + "totalXp": 2790, + "intelPoints": 0 + }, + { + "level": 19, + "xpRequired": 229, + "totalXp": 3019, + "intelPoints": 0 + }, + { + "level": 20, + "xpRequired": 239, + "totalXp": 3258, + "intelPoints": 5150 + }, + { + "level": 21, + "xpRequired": 246, + "totalXp": 3504, + "intelPoints": 0 + }, + { + "level": 22, + "xpRequired": 253, + "totalXp": 3757, + "intelPoints": 0 + }, + { + "level": 23, + "xpRequired": 260, + "totalXp": 4017, + "intelPoints": 1050 + }, + { + "level": 24, + "xpRequired": 268, + "totalXp": 4285, + "intelPoints": 0 + }, + { + "level": 25, + "xpRequired": 276, + "totalXp": 4561, + "intelPoints": 1015 + }, + { + "level": 26, + "xpRequired": 284, + "totalXp": 4845, + "intelPoints": 0 + }, + { + "level": 27, + "xpRequired": 292, + "totalXp": 5137, + "intelPoints": 0 + }, + { + "level": 28, + "xpRequired": 301, + "totalXp": 5438, + "intelPoints": 0 + }, + { + "level": 29, + "xpRequired": 310, + "totalXp": 5748, + "intelPoints": 0 + }, + { + "level": 30, + "xpRequired": 319, + "totalXp": 6067, + "intelPoints": 0 + }, + { + "level": 31, + "xpRequired": 326, + "totalXp": 6393, + "intelPoints": 0 + }, + { + "level": 32, + "xpRequired": 334, + "totalXp": 6727, + "intelPoints": 0 + }, + { + "level": 33, + "xpRequired": 342, + "totalXp": 7069, + "intelPoints": 10100 + }, + { + "level": 34, + "xpRequired": 350, + "totalXp": 7419, + "intelPoints": 0 + }, + { + "level": 35, + "xpRequired": 358, + "totalXp": 7777, + "intelPoints": 1550 + }, + { + "level": 36, + "xpRequired": 366, + "totalXp": 8143, + "intelPoints": 0 + }, + { + "level": 37, + "xpRequired": 375, + "totalXp": 8518, + "intelPoints": 0 + }, + { + "level": 38, + "xpRequired": 384, + "totalXp": 8902, + "intelPoints": 15500 + }, + { + "level": 39, + "xpRequired": 393, + "totalXp": 9295, + "intelPoints": 0 + }, + { + "level": 40, + "xpRequired": 402, + "totalXp": 9697, + "intelPoints": 15 + }, + { + "level": 41, + "xpRequired": 410, + "totalXp": 10107, + "intelPoints": 0 + }, + { + "level": 42, + "xpRequired": 418, + "totalXp": 10525, + "intelPoints": 0 + }, + { + "level": 43, + "xpRequired": 426, + "totalXp": 10951, + "intelPoints": 0 + }, + { + "level": 44, + "xpRequired": 434, + "totalXp": 11385, + "intelPoints": 0 + }, + { + "level": 45, + "xpRequired": 442, + "totalXp": 11827, + "intelPoints": 0 + }, + { + "level": 46, + "xpRequired": 450, + "totalXp": 12277, + "intelPoints": 0 + }, + { + "level": 47, + "xpRequired": 459, + "totalXp": 12736, + "intelPoints": 0 + }, + { + "level": 48, + "xpRequired": 468, + "totalXp": 13204, + "intelPoints": 2050 + }, + { + "level": 49, + "xpRequired": 477, + "totalXp": 13681, + "intelPoints": 0 + }, + { + "level": 50, + "xpRequired": 486, + "totalXp": 14167, + "intelPoints": 2015 + }, + { + "level": 51, + "xpRequired": 494, + "totalXp": 14661, + "intelPoints": 0 + }, + { + "level": 52, + "xpRequired": 502, + "totalXp": 15163, + "intelPoints": 0 + }, + { + "level": 53, + "xpRequired": 510, + "totalXp": 15673, + "intelPoints": 0 + }, + { + "level": 54, + "xpRequired": 518, + "totalXp": 16191, + "intelPoints": 0 + }, + { + "level": 55, + "xpRequired": 526, + "totalXp": 16717, + "intelPoints": 2050 + }, + { + "level": 56, + "xpRequired": 534, + "totalXp": 17251, + "intelPoints": 0 + }, + { + "level": 57, + "xpRequired": 543, + "totalXp": 17794, + "intelPoints": 0 + }, + { + "level": 58, + "xpRequired": 552, + "totalXp": 18346, + "intelPoints": 0 + }, + { + "level": 59, + "xpRequired": 561, + "totalXp": 18907, + "intelPoints": 0 + }, + { + "level": 60, + "xpRequired": 570, + "totalXp": 19477, + "intelPoints": 0 + }, + { + "level": 61, + "xpRequired": 575, + "totalXp": 20052, + "intelPoints": 0 + }, + { + "level": 62, + "xpRequired": 580, + "totalXp": 20632, + "intelPoints": 2550 + }, + { + "level": 63, + "xpRequired": 585, + "totalXp": 21217, + "intelPoints": 0 + }, + { + "level": 64, + "xpRequired": 590, + "totalXp": 21807, + "intelPoints": 0 + }, + { + "level": 65, + "xpRequired": 595, + "totalXp": 22402, + "intelPoints": 2550 + }, + { + "level": 66, + "xpRequired": 600, + "totalXp": 23002, + "intelPoints": 0 + }, + { + "level": 67, + "xpRequired": 606, + "totalXp": 23608, + "intelPoints": 0 + }, + { + "level": 68, + "xpRequired": 612, + "totalXp": 24220, + "intelPoints": 2550 + }, + { + "level": 69, + "xpRequired": 618, + "totalXp": 24838, + "intelPoints": 0 + }, + { + "level": 70, + "xpRequired": 624, + "totalXp": 25462, + "intelPoints": 0 + }, + { + "level": 71, + "xpRequired": 624, + "totalXp": 26086, + "intelPoints": 25350 + }, + { + "level": 72, + "xpRequired": 624, + "totalXp": 26710, + "intelPoints": 0 + }, + { + "level": 73, + "xpRequired": 624, + "totalXp": 27334, + "intelPoints": 0 + }, + { + "level": 74, + "xpRequired": 624, + "totalXp": 27958, + "intelPoints": 0 + }, + { + "level": 75, + "xpRequired": 624, + "totalXp": 28582, + "intelPoints": 30 + }, + { + "level": 76, + "xpRequired": 624, + "totalXp": 29206, + "intelPoints": 0 + }, + { + "level": 77, + "xpRequired": 624, + "totalXp": 29830, + "intelPoints": 3015 + }, + { + "level": 78, + "xpRequired": 624, + "totalXp": 30454, + "intelPoints": 0 + }, + { + "level": 79, + "xpRequired": 624, + "totalXp": 31078, + "intelPoints": 0 + }, + { + "level": 80, + "xpRequired": 624, + "totalXp": 31702, + "intelPoints": 0 + }, + { + "level": 81, + "xpRequired": 624, + "totalXp": 32326, + "intelPoints": 0 + }, + { + "level": 82, + "xpRequired": 624, + "totalXp": 32950, + "intelPoints": 0 + }, + { + "level": 83, + "xpRequired": 624, + "totalXp": 33574, + "intelPoints": 3050 + }, + { + "level": 84, + "xpRequired": 624, + "totalXp": 34198, + "intelPoints": 0 + }, + { + "level": 85, + "xpRequired": 624, + "totalXp": 34822, + "intelPoints": 3050 + }, + { + "level": 86, + "xpRequired": 624, + "totalXp": 35446, + "intelPoints": 0 + }, + { + "level": 87, + "xpRequired": 624, + "totalXp": 36070, + "intelPoints": 0 + }, + { + "level": 88, + "xpRequired": 624, + "totalXp": 36694, + "intelPoints": 35100 + }, + { + "level": 89, + "xpRequired": 624, + "totalXp": 37318, + "intelPoints": 0 + }, + { + "level": 90, + "xpRequired": 624, + "totalXp": 37942, + "intelPoints": 0 + }, + { + "level": 91, + "xpRequired": 624, + "totalXp": 38566, + "intelPoints": 0 + }, + { + "level": 92, + "xpRequired": 624, + "totalXp": 39190, + "intelPoints": 0 + }, + { + "level": 93, + "xpRequired": 624, + "totalXp": 39814, + "intelPoints": 0 + }, + { + "level": 94, + "xpRequired": 624, + "totalXp": 40438, + "intelPoints": 0 + }, + { + "level": 95, + "xpRequired": 624, + "totalXp": 41062, + "intelPoints": 3550 + }, + { + "level": 96, + "xpRequired": 624, + "totalXp": 41686, + "intelPoints": 0 + }, + { + "level": 97, + "xpRequired": 624, + "totalXp": 42310, + "intelPoints": 0 + }, + { + "level": 98, + "xpRequired": 624, + "totalXp": 42934, + "intelPoints": 0 + }, + { + "level": 99, + "xpRequired": 624, + "totalXp": 43558, + "intelPoints": 0 + }, + { + "level": 100, + "xpRequired": 624, + "totalXp": 44182, + "intelPoints": 4015 + } + ] +} \ No newline at end of file diff --git a/character-builder/data/spec-gathering.json b/character-builder/data/spec-gathering.json new file mode 100644 index 0000000..8dae2db --- /dev/null +++ b/character-builder/data/spec-gathering.json @@ -0,0 +1,610 @@ +{ + "header": [ + "Level", + "Required XP", + "Cumulative", + "Rewards" + ], + "rows": [ + { + "level": 1, + "xpRequired": 100, + "totalXp": 100, + "intelPoints": 0 + }, + { + "level": 2, + "xpRequired": 105, + "totalXp": 205, + "intelPoints": 2200 + }, + { + "level": 3, + "xpRequired": 110, + "totalXp": 315, + "intelPoints": 21000 + }, + { + "level": 4, + "xpRequired": 116, + "totalXp": 431, + "intelPoints": 0 + }, + { + "level": 5, + "xpRequired": 122, + "totalXp": 553, + "intelPoints": 0 + }, + { + "level": 6, + "xpRequired": 128, + "totalXp": 681, + "intelPoints": 0 + }, + { + "level": 7, + "xpRequired": 135, + "totalXp": 816, + "intelPoints": 250 + }, + { + "level": 8, + "xpRequired": 142, + "totalXp": 958, + "intelPoints": 0 + }, + { + "level": 9, + "xpRequired": 149, + "totalXp": 1107, + "intelPoints": 0 + }, + { + "level": 10, + "xpRequired": 157, + "totalXp": 1264, + "intelPoints": 525 + }, + { + "level": 11, + "xpRequired": 164, + "totalXp": 1428, + "intelPoints": 0 + }, + { + "level": 12, + "xpRequired": 171, + "totalXp": 1599, + "intelPoints": 0 + }, + { + "level": 13, + "xpRequired": 178, + "totalXp": 1777, + "intelPoints": 5100 + }, + { + "level": 14, + "xpRequired": 186, + "totalXp": 1963, + "intelPoints": 0 + }, + { + "level": 15, + "xpRequired": 194, + "totalXp": 2157, + "intelPoints": 51000 + }, + { + "level": 16, + "xpRequired": 202, + "totalXp": 2359, + "intelPoints": 0 + }, + { + "level": 17, + "xpRequired": 211, + "totalXp": 2570, + "intelPoints": 550 + }, + { + "level": 18, + "xpRequired": 220, + "totalXp": 2790, + "intelPoints": 0 + }, + { + "level": 19, + "xpRequired": 229, + "totalXp": 3019, + "intelPoints": 0 + }, + { + "level": 20, + "xpRequired": 239, + "totalXp": 3258, + "intelPoints": 1025 + }, + { + "level": 21, + "xpRequired": 246, + "totalXp": 3504, + "intelPoints": 0 + }, + { + "level": 22, + "xpRequired": 253, + "totalXp": 3757, + "intelPoints": 0 + }, + { + "level": 23, + "xpRequired": 260, + "totalXp": 4017, + "intelPoints": 1050 + }, + { + "level": 24, + "xpRequired": 268, + "totalXp": 4285, + "intelPoints": 0 + }, + { + "level": 25, + "xpRequired": 276, + "totalXp": 4561, + "intelPoints": 0 + }, + { + "level": 26, + "xpRequired": 284, + "totalXp": 4845, + "intelPoints": 0 + }, + { + "level": 27, + "xpRequired": 292, + "totalXp": 5137, + "intelPoints": 0 + }, + { + "level": 28, + "xpRequired": 301, + "totalXp": 5438, + "intelPoints": 1050 + }, + { + "level": 29, + "xpRequired": 310, + "totalXp": 5748, + "intelPoints": 0 + }, + { + "level": 30, + "xpRequired": 319, + "totalXp": 6067, + "intelPoints": 1025 + }, + { + "level": 31, + "xpRequired": 326, + "totalXp": 6393, + "intelPoints": 0 + }, + { + "level": 32, + "xpRequired": 334, + "totalXp": 6727, + "intelPoints": 0 + }, + { + "level": 33, + "xpRequired": 342, + "totalXp": 7069, + "intelPoints": 15100 + }, + { + "level": 34, + "xpRequired": 350, + "totalXp": 7419, + "intelPoints": 0 + }, + { + "level": 35, + "xpRequired": 358, + "totalXp": 7777, + "intelPoints": 0 + }, + { + "level": 36, + "xpRequired": 366, + "totalXp": 8143, + "intelPoints": 0 + }, + { + "level": 37, + "xpRequired": 375, + "totalXp": 8518, + "intelPoints": 0 + }, + { + "level": 38, + "xpRequired": 384, + "totalXp": 8902, + "intelPoints": 1550 + }, + { + "level": 39, + "xpRequired": 393, + "totalXp": 9295, + "intelPoints": 0 + }, + { + "level": 40, + "xpRequired": 402, + "totalXp": 9697, + "intelPoints": 1525 + }, + { + "level": 41, + "xpRequired": 410, + "totalXp": 10107, + "intelPoints": 0 + }, + { + "level": 42, + "xpRequired": 418, + "totalXp": 10525, + "intelPoints": 0 + }, + { + "level": 43, + "xpRequired": 426, + "totalXp": 10951, + "intelPoints": 1550 + }, + { + "level": 44, + "xpRequired": 434, + "totalXp": 11385, + "intelPoints": 0 + }, + { + "level": 45, + "xpRequired": 442, + "totalXp": 11827, + "intelPoints": 0 + }, + { + "level": 46, + "xpRequired": 450, + "totalXp": 12277, + "intelPoints": 0 + }, + { + "level": 47, + "xpRequired": 459, + "totalXp": 12736, + "intelPoints": 0 + }, + { + "level": 48, + "xpRequired": 468, + "totalXp": 13204, + "intelPoints": 2050 + }, + { + "level": 49, + "xpRequired": 477, + "totalXp": 13681, + "intelPoints": 0 + }, + { + "level": 50, + "xpRequired": 486, + "totalXp": 14167, + "intelPoints": 2025 + }, + { + "level": 51, + "xpRequired": 494, + "totalXp": 14661, + "intelPoints": 0 + }, + { + "level": 52, + "xpRequired": 502, + "totalXp": 15163, + "intelPoints": 0 + }, + { + "level": 53, + "xpRequired": 510, + "totalXp": 15673, + "intelPoints": 2050 + }, + { + "level": 54, + "xpRequired": 518, + "totalXp": 16191, + "intelPoints": 0 + }, + { + "level": 55, + "xpRequired": 526, + "totalXp": 16717, + "intelPoints": 201000 + }, + { + "level": 56, + "xpRequired": 534, + "totalXp": 17251, + "intelPoints": 0 + }, + { + "level": 57, + "xpRequired": 543, + "totalXp": 17794, + "intelPoints": 0 + }, + { + "level": 58, + "xpRequired": 552, + "totalXp": 18346, + "intelPoints": 0 + }, + { + "level": 59, + "xpRequired": 561, + "totalXp": 18907, + "intelPoints": 0 + }, + { + "level": 60, + "xpRequired": 570, + "totalXp": 19477, + "intelPoints": 0 + }, + { + "level": 61, + "xpRequired": 575, + "totalXp": 20052, + "intelPoints": 2525 + }, + { + "level": 62, + "xpRequired": 580, + "totalXp": 20632, + "intelPoints": 0 + }, + { + "level": 63, + "xpRequired": 585, + "totalXp": 21217, + "intelPoints": 25100 + }, + { + "level": 64, + "xpRequired": 590, + "totalXp": 21807, + "intelPoints": 0 + }, + { + "level": 65, + "xpRequired": 595, + "totalXp": 22402, + "intelPoints": 0 + }, + { + "level": 66, + "xpRequired": 600, + "totalXp": 23002, + "intelPoints": 0 + }, + { + "level": 67, + "xpRequired": 606, + "totalXp": 23608, + "intelPoints": 0 + }, + { + "level": 68, + "xpRequired": 612, + "totalXp": 24220, + "intelPoints": 2550 + }, + { + "level": 69, + "xpRequired": 618, + "totalXp": 24838, + "intelPoints": 0 + }, + { + "level": 70, + "xpRequired": 624, + "totalXp": 25462, + "intelPoints": 0 + }, + { + "level": 71, + "xpRequired": 624, + "totalXp": 26086, + "intelPoints": 3025 + }, + { + "level": 72, + "xpRequired": 624, + "totalXp": 26710, + "intelPoints": 0 + }, + { + "level": 73, + "xpRequired": 624, + "totalXp": 27334, + "intelPoints": 0 + }, + { + "level": 74, + "xpRequired": 624, + "totalXp": 27958, + "intelPoints": 30400 + }, + { + "level": 75, + "xpRequired": 624, + "totalXp": 28582, + "intelPoints": 30 + }, + { + "level": 76, + "xpRequired": 624, + "totalXp": 29206, + "intelPoints": 0 + }, + { + "level": 77, + "xpRequired": 624, + "totalXp": 29830, + "intelPoints": 0 + }, + { + "level": 78, + "xpRequired": 624, + "totalXp": 30454, + "intelPoints": 30100 + }, + { + "level": 79, + "xpRequired": 624, + "totalXp": 31078, + "intelPoints": 0 + }, + { + "level": 80, + "xpRequired": 624, + "totalXp": 31702, + "intelPoints": 0 + }, + { + "level": 81, + "xpRequired": 624, + "totalXp": 32326, + "intelPoints": 3025 + }, + { + "level": 82, + "xpRequired": 624, + "totalXp": 32950, + "intelPoints": 0 + }, + { + "level": 83, + "xpRequired": 624, + "totalXp": 33574, + "intelPoints": 3550 + }, + { + "level": 84, + "xpRequired": 624, + "totalXp": 34198, + "intelPoints": 0 + }, + { + "level": 85, + "xpRequired": 624, + "totalXp": 34822, + "intelPoints": 0 + }, + { + "level": 86, + "xpRequired": 624, + "totalXp": 35446, + "intelPoints": 0 + }, + { + "level": 87, + "xpRequired": 624, + "totalXp": 36070, + "intelPoints": 0 + }, + { + "level": 88, + "xpRequired": 624, + "totalXp": 36694, + "intelPoints": 0 + }, + { + "level": 89, + "xpRequired": 624, + "totalXp": 37318, + "intelPoints": 0 + }, + { + "level": 90, + "xpRequired": 624, + "totalXp": 37942, + "intelPoints": 3525 + }, + { + "level": 91, + "xpRequired": 624, + "totalXp": 38566, + "intelPoints": 0 + }, + { + "level": 92, + "xpRequired": 624, + "totalXp": 39190, + "intelPoints": 0 + }, + { + "level": 93, + "xpRequired": 624, + "totalXp": 39814, + "intelPoints": 0 + }, + { + "level": 94, + "xpRequired": 624, + "totalXp": 40438, + "intelPoints": 0 + }, + { + "level": 95, + "xpRequired": 624, + "totalXp": 41062, + "intelPoints": 0 + }, + { + "level": 96, + "xpRequired": 624, + "totalXp": 41686, + "intelPoints": 0 + }, + { + "level": 97, + "xpRequired": 624, + "totalXp": 42310, + "intelPoints": 0 + }, + { + "level": 98, + "xpRequired": 624, + "totalXp": 42934, + "intelPoints": 0 + }, + { + "level": 99, + "xpRequired": 624, + "totalXp": 43558, + "intelPoints": 0 + }, + { + "level": 100, + "xpRequired": 624, + "totalXp": 44182, + "intelPoints": 4025 + } + ] +} \ No newline at end of file diff --git a/character-builder/data/spec-sabotage.json b/character-builder/data/spec-sabotage.json new file mode 100644 index 0000000..c0ff10f --- /dev/null +++ b/character-builder/data/spec-sabotage.json @@ -0,0 +1,610 @@ +{ + "header": [ + "Level", + "Required XP", + "Cumulative", + "Rewards" + ], + "rows": [ + { + "level": 1, + "xpRequired": 100, + "totalXp": 100, + "intelPoints": 250 + }, + { + "level": 2, + "xpRequired": 105, + "totalXp": 205, + "intelPoints": 21000 + }, + { + "level": 3, + "xpRequired": 110, + "totalXp": 315, + "intelPoints": 250 + }, + { + "level": 4, + "xpRequired": 116, + "totalXp": 431, + "intelPoints": 0 + }, + { + "level": 5, + "xpRequired": 122, + "totalXp": 553, + "intelPoints": 0 + }, + { + "level": 6, + "xpRequired": 128, + "totalXp": 681, + "intelPoints": 0 + }, + { + "level": 7, + "xpRequired": 135, + "totalXp": 816, + "intelPoints": 0 + }, + { + "level": 8, + "xpRequired": 142, + "totalXp": 958, + "intelPoints": 21000 + }, + { + "level": 9, + "xpRequired": 149, + "totalXp": 1107, + "intelPoints": 0 + }, + { + "level": 10, + "xpRequired": 157, + "totalXp": 1264, + "intelPoints": 550 + }, + { + "level": 11, + "xpRequired": 164, + "totalXp": 1428, + "intelPoints": 0 + }, + { + "level": 12, + "xpRequired": 171, + "totalXp": 1599, + "intelPoints": 5150 + }, + { + "level": 13, + "xpRequired": 178, + "totalXp": 1777, + "intelPoints": 0 + }, + { + "level": 14, + "xpRequired": 186, + "totalXp": 1963, + "intelPoints": 0 + }, + { + "level": 15, + "xpRequired": 194, + "totalXp": 2157, + "intelPoints": 0 + }, + { + "level": 16, + "xpRequired": 202, + "totalXp": 2359, + "intelPoints": 0 + }, + { + "level": 17, + "xpRequired": 211, + "totalXp": 2570, + "intelPoints": 0 + }, + { + "level": 18, + "xpRequired": 220, + "totalXp": 2790, + "intelPoints": 0 + }, + { + "level": 19, + "xpRequired": 229, + "totalXp": 3019, + "intelPoints": 550 + }, + { + "level": 20, + "xpRequired": 239, + "totalXp": 3258, + "intelPoints": 0 + }, + { + "level": 21, + "xpRequired": 246, + "totalXp": 3504, + "intelPoints": 0 + }, + { + "level": 22, + "xpRequired": 253, + "totalXp": 3757, + "intelPoints": 1020 + }, + { + "level": 23, + "xpRequired": 260, + "totalXp": 4017, + "intelPoints": 0 + }, + { + "level": 24, + "xpRequired": 268, + "totalXp": 4285, + "intelPoints": 0 + }, + { + "level": 25, + "xpRequired": 276, + "totalXp": 4561, + "intelPoints": 0 + }, + { + "level": 26, + "xpRequired": 284, + "totalXp": 4845, + "intelPoints": 0 + }, + { + "level": 27, + "xpRequired": 292, + "totalXp": 5137, + "intelPoints": 1050 + }, + { + "level": 28, + "xpRequired": 301, + "totalXp": 5438, + "intelPoints": 0 + }, + { + "level": 29, + "xpRequired": 310, + "totalXp": 5748, + "intelPoints": 0 + }, + { + "level": 30, + "xpRequired": 319, + "totalXp": 6067, + "intelPoints": 0 + }, + { + "level": 31, + "xpRequired": 326, + "totalXp": 6393, + "intelPoints": 0 + }, + { + "level": 32, + "xpRequired": 334, + "totalXp": 6727, + "intelPoints": 10150 + }, + { + "level": 33, + "xpRequired": 342, + "totalXp": 7069, + "intelPoints": 0 + }, + { + "level": 34, + "xpRequired": 350, + "totalXp": 7419, + "intelPoints": 0 + }, + { + "level": 35, + "xpRequired": 358, + "totalXp": 7777, + "intelPoints": 0 + }, + { + "level": 36, + "xpRequired": 366, + "totalXp": 8143, + "intelPoints": 0 + }, + { + "level": 37, + "xpRequired": 375, + "totalXp": 8518, + "intelPoints": 1550 + }, + { + "level": 38, + "xpRequired": 384, + "totalXp": 8902, + "intelPoints": 0 + }, + { + "level": 39, + "xpRequired": 393, + "totalXp": 9295, + "intelPoints": 0 + }, + { + "level": 40, + "xpRequired": 402, + "totalXp": 9697, + "intelPoints": 15 + }, + { + "level": 41, + "xpRequired": 410, + "totalXp": 10107, + "intelPoints": 0 + }, + { + "level": 42, + "xpRequired": 418, + "totalXp": 10525, + "intelPoints": 0 + }, + { + "level": 43, + "xpRequired": 426, + "totalXp": 10951, + "intelPoints": 15100 + }, + { + "level": 44, + "xpRequired": 434, + "totalXp": 11385, + "intelPoints": 0 + }, + { + "level": 45, + "xpRequired": 442, + "totalXp": 11827, + "intelPoints": 1520 + }, + { + "level": 46, + "xpRequired": 450, + "totalXp": 12277, + "intelPoints": 0 + }, + { + "level": 47, + "xpRequired": 459, + "totalXp": 12736, + "intelPoints": 0 + }, + { + "level": 48, + "xpRequired": 468, + "totalXp": 13204, + "intelPoints": 0 + }, + { + "level": 49, + "xpRequired": 477, + "totalXp": 13681, + "intelPoints": 0 + }, + { + "level": 50, + "xpRequired": 486, + "totalXp": 14167, + "intelPoints": 2050 + }, + { + "level": 51, + "xpRequired": 494, + "totalXp": 14661, + "intelPoints": 0 + }, + { + "level": 52, + "xpRequired": 502, + "totalXp": 15163, + "intelPoints": 0 + }, + { + "level": 53, + "xpRequired": 510, + "totalXp": 15673, + "intelPoints": 2050 + }, + { + "level": 54, + "xpRequired": 518, + "totalXp": 16191, + "intelPoints": 0 + }, + { + "level": 55, + "xpRequired": 526, + "totalXp": 16717, + "intelPoints": 20150 + }, + { + "level": 56, + "xpRequired": 534, + "totalXp": 17251, + "intelPoints": 0 + }, + { + "level": 57, + "xpRequired": 543, + "totalXp": 17794, + "intelPoints": 0 + }, + { + "level": 58, + "xpRequired": 552, + "totalXp": 18346, + "intelPoints": 0 + }, + { + "level": 59, + "xpRequired": 561, + "totalXp": 18907, + "intelPoints": 0 + }, + { + "level": 60, + "xpRequired": 570, + "totalXp": 19477, + "intelPoints": 2550 + }, + { + "level": 61, + "xpRequired": 575, + "totalXp": 20052, + "intelPoints": 0 + }, + { + "level": 62, + "xpRequired": 580, + "totalXp": 20632, + "intelPoints": 0 + }, + { + "level": 63, + "xpRequired": 585, + "totalXp": 21217, + "intelPoints": 0 + }, + { + "level": 64, + "xpRequired": 590, + "totalXp": 21807, + "intelPoints": 0 + }, + { + "level": 65, + "xpRequired": 595, + "totalXp": 22402, + "intelPoints": 0 + }, + { + "level": 66, + "xpRequired": 600, + "totalXp": 23002, + "intelPoints": 0 + }, + { + "level": 67, + "xpRequired": 606, + "totalXp": 23608, + "intelPoints": 0 + }, + { + "level": 68, + "xpRequired": 612, + "totalXp": 24220, + "intelPoints": 0 + }, + { + "level": 69, + "xpRequired": 618, + "totalXp": 24838, + "intelPoints": 25150 + }, + { + "level": 70, + "xpRequired": 624, + "totalXp": 25462, + "intelPoints": 0 + }, + { + "level": 71, + "xpRequired": 624, + "totalXp": 26086, + "intelPoints": 0 + }, + { + "level": 72, + "xpRequired": 624, + "totalXp": 26710, + "intelPoints": 2550 + }, + { + "level": 73, + "xpRequired": 624, + "totalXp": 27334, + "intelPoints": 0 + }, + { + "level": 74, + "xpRequired": 624, + "totalXp": 27958, + "intelPoints": 3050 + }, + { + "level": 75, + "xpRequired": 624, + "totalXp": 28582, + "intelPoints": 30 + }, + { + "level": 76, + "xpRequired": 624, + "totalXp": 29206, + "intelPoints": 0 + }, + { + "level": 77, + "xpRequired": 624, + "totalXp": 29830, + "intelPoints": 0 + }, + { + "level": 78, + "xpRequired": 624, + "totalXp": 30454, + "intelPoints": 0 + }, + { + "level": 79, + "xpRequired": 624, + "totalXp": 31078, + "intelPoints": 0 + }, + { + "level": 80, + "xpRequired": 624, + "totalXp": 31702, + "intelPoints": 0 + }, + { + "level": 81, + "xpRequired": 624, + "totalXp": 32326, + "intelPoints": 0 + }, + { + "level": 82, + "xpRequired": 624, + "totalXp": 32950, + "intelPoints": 0 + }, + { + "level": 83, + "xpRequired": 624, + "totalXp": 33574, + "intelPoints": 0 + }, + { + "level": 84, + "xpRequired": 624, + "totalXp": 34198, + "intelPoints": 0 + }, + { + "level": 85, + "xpRequired": 624, + "totalXp": 34822, + "intelPoints": 3050 + }, + { + "level": 86, + "xpRequired": 624, + "totalXp": 35446, + "intelPoints": 0 + }, + { + "level": 87, + "xpRequired": 624, + "totalXp": 36070, + "intelPoints": 0 + }, + { + "level": 88, + "xpRequired": 624, + "totalXp": 36694, + "intelPoints": 3550 + }, + { + "level": 89, + "xpRequired": 624, + "totalXp": 37318, + "intelPoints": 0 + }, + { + "level": 90, + "xpRequired": 624, + "totalXp": 37942, + "intelPoints": 0 + }, + { + "level": 91, + "xpRequired": 624, + "totalXp": 38566, + "intelPoints": 0 + }, + { + "level": 92, + "xpRequired": 624, + "totalXp": 39190, + "intelPoints": 0 + }, + { + "level": 93, + "xpRequired": 624, + "totalXp": 39814, + "intelPoints": 0 + }, + { + "level": 94, + "xpRequired": 624, + "totalXp": 40438, + "intelPoints": 0 + }, + { + "level": 95, + "xpRequired": 624, + "totalXp": 41062, + "intelPoints": 0 + }, + { + "level": 96, + "xpRequired": 624, + "totalXp": 41686, + "intelPoints": 0 + }, + { + "level": 97, + "xpRequired": 624, + "totalXp": 42310, + "intelPoints": 0 + }, + { + "level": 98, + "xpRequired": 624, + "totalXp": 42934, + "intelPoints": 35150 + }, + { + "level": 99, + "xpRequired": 624, + "totalXp": 43558, + "intelPoints": 0 + }, + { + "level": 100, + "xpRequired": 624, + "totalXp": 44182, + "intelPoints": 4050 + } + ] +} \ No newline at end of file diff --git a/character-builder/docker-compose.yml b/character-builder/docker-compose.yml new file mode 100644 index 0000000..1bfb263 --- /dev/null +++ b/character-builder/docker-compose.yml @@ -0,0 +1,38 @@ +services: + app: + build: + context: . + dockerfile: Dockerfile + image: dune-character-builder:latest + container_name: dune-builder-app + restart: unless-stopped + environment: + VALKEY_URL: redis://valkey:6379 + PORT: "3000" + depends_on: + valkey: + condition: service_healthy + ports: + - "8080:3000" + + valkey: + image: valkey/valkey:8-alpine + container_name: dune-builder-valkey + restart: unless-stopped + command: + - valkey-server + - --appendonly + - "yes" + - --save + - "60" + - "1" + volumes: + - valkey-data:/data + healthcheck: + test: ["CMD", "valkey-cli", "ping"] + interval: 5s + timeout: 3s + retries: 5 + +volumes: + valkey-data: diff --git a/character-builder/frontend/index.html b/character-builder/frontend/index.html new file mode 100644 index 0000000..919b623 --- /dev/null +++ b/character-builder/frontend/index.html @@ -0,0 +1,18 @@ + + + + + + Dune Awakening — Character Builder + + + + + +
+ + + diff --git a/character-builder/frontend/package.json b/character-builder/frontend/package.json new file mode 100644 index 0000000..631b5dd --- /dev/null +++ b/character-builder/frontend/package.json @@ -0,0 +1,20 @@ +{ + "name": "dune-character-builder-frontend", + "version": "0.1.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "vue-tsc -b && vite build", + "preview": "vite preview" + }, + "dependencies": { + "vue": "^3.5.13" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.2.1", + "typescript": "^5.7.3", + "vite": "^6.0.7", + "vue-tsc": "^2.2.0" + } +} diff --git a/character-builder/frontend/src/App.vue b/character-builder/frontend/src/App.vue new file mode 100644 index 0000000..580f98d --- /dev/null +++ b/character-builder/frontend/src/App.vue @@ -0,0 +1,345 @@ + + + diff --git a/character-builder/frontend/src/components/FactionTrack.vue b/character-builder/frontend/src/components/FactionTrack.vue new file mode 100644 index 0000000..6e4c45f --- /dev/null +++ b/character-builder/frontend/src/components/FactionTrack.vue @@ -0,0 +1,94 @@ + + + diff --git a/character-builder/frontend/src/components/SkillTree.vue b/character-builder/frontend/src/components/SkillTree.vue new file mode 100644 index 0000000..981c614 --- /dev/null +++ b/character-builder/frontend/src/components/SkillTree.vue @@ -0,0 +1,190 @@ + + + diff --git a/character-builder/frontend/src/components/XpProgressCard.vue b/character-builder/frontend/src/components/XpProgressCard.vue new file mode 100644 index 0000000..afc5b33 --- /dev/null +++ b/character-builder/frontend/src/components/XpProgressCard.vue @@ -0,0 +1,87 @@ + + + diff --git a/character-builder/frontend/src/data.ts b/character-builder/frontend/src/data.ts new file mode 100644 index 0000000..563868e --- /dev/null +++ b/character-builder/frontend/src/data.ts @@ -0,0 +1,53 @@ +import type { + ClassId, + FactionTable, + House, + SkillTree, + SpecId, + XpTable, +} from './types'; + +const API = '/api/data'; + +async function getJSON(file: string): Promise { + const res = await fetch(`${API}/${file}`); + if (!res.ok) throw new Error(`fetch ${file}: ${res.status}`); + return res.json() as Promise; +} + +export const SPECS: SpecId[] = [ + 'combat', + 'crafting', + 'exploration', + 'gathering', + 'sabotage', +]; + +export const CLASSES: { id: ClassId; name: string }[] = [ + { id: 'benegesserit', name: 'Bene Gesserit' }, + { id: 'mentat', name: 'Mentat' }, + { id: 'planetologist', name: 'Planetologist' }, + { id: 'swordmaster', name: 'Swordmaster' }, + { id: 'trooper', name: 'Trooper' }, +]; + +export const HOUSES: { id: House; name: string }[] = [ + { id: 'atreides', name: 'House Atreides' }, + { id: 'harkonnen', name: 'House Harkonnen' }, +]; + +export async function loadCharacterXp(): Promise { + return getJSON('character-xp.json'); +} + +export async function loadSpec(id: SpecId): Promise { + return getJSON(`spec-${id}.json`); +} + +export async function loadFaction(house: House): Promise { + return getJSON(`faction-${house}.json`); +} + +export async function loadSkillTree(id: ClassId): Promise { + return getJSON(`skills-${id}.json`); +} diff --git a/character-builder/frontend/src/main.ts b/character-builder/frontend/src/main.ts new file mode 100644 index 0000000..27a79bf --- /dev/null +++ b/character-builder/frontend/src/main.ts @@ -0,0 +1,5 @@ +import { createApp } from 'vue'; +import App from './App.vue'; +import './styles.css'; + +createApp(App).mount('#app'); diff --git a/character-builder/frontend/src/store.ts b/character-builder/frontend/src/store.ts new file mode 100644 index 0000000..0dca783 --- /dev/null +++ b/character-builder/frontend/src/store.ts @@ -0,0 +1,86 @@ +import { reactive, watch } from 'vue'; +import type { BuildState, ClassId, House, SpecId } from './types'; + +const STORAGE_KEY = 'dune-builder-v1'; + +export function defaultBuild(): BuildState { + const specs: BuildState['specs'] = { + combat: { level: 0, xpInto: 0 }, + crafting: { level: 0, xpInto: 0 }, + exploration: { level: 0, xpInto: 0 }, + gathering: { level: 0, xpInto: 0 }, + sabotage: { level: 0, xpInto: 0 }, + }; + return { + v: 1, + house: 'atreides', + classId: 'swordmaster', + character: { level: 0, xpInto: 0 }, + specs, + faction: { tier: 0, standingInto: 0 }, + skills: {}, + }; +} + +function migrate(raw: any): BuildState { + const def = defaultBuild(); + if (!raw || typeof raw !== 'object') return def; + // Shallow merge with defaults; type-safe enough for v1. + const out: BuildState = { + ...def, + ...raw, + character: { ...def.character, ...(raw.character || {}) }, + specs: { ...def.specs }, + faction: { ...def.faction, ...(raw.faction || {}) }, + skills: { ...(raw.skills || {}) }, + }; + if (raw.specs && typeof raw.specs === 'object') { + for (const k of Object.keys(out.specs) as SpecId[]) { + if (raw.specs[k]) out.specs[k] = { ...out.specs[k], ...raw.specs[k] }; + } + } + return out; +} + +function loadFromStorage(): BuildState { + try { + const raw = JSON.parse(localStorage.getItem(STORAGE_KEY) || 'null'); + return migrate(raw); + } catch { + return defaultBuild(); + } +} + +export const build = reactive(loadFromStorage()); + +watch( + build, + (val) => { + try { + localStorage.setItem(STORAGE_KEY, JSON.stringify(val)); + } catch { + // ignore (quota, private mode, etc.) + } + }, + { deep: true }, +); + +export function applyBuild(next: BuildState) { + const merged = migrate(next); + Object.assign(build, merged); +} + +export function resetBuild() { + applyBuild(defaultBuild()); +} + +export function setHouse(h: House) { + build.house = h; + // resetting faction progress when house changes is the safest default + build.faction = { tier: 0, standingInto: 0 }; +} + +export function setClass(c: ClassId) { + build.classId = c; + build.skills = {}; // skills are per-class +} diff --git a/character-builder/frontend/src/styles.css b/character-builder/frontend/src/styles.css new file mode 100644 index 0000000..1129343 --- /dev/null +++ b/character-builder/frontend/src/styles.css @@ -0,0 +1,546 @@ +:root { + --bg: #1a1410; + --bg-2: #221a14; + --panel: #2b2018; + --panel-2: #352720; + --line: #4a3628; + --line-soft: #3a2a1f; + --ink: #f4e9d8; + --ink-dim: #c9b89c; + --ink-muted: #8a7560; + --sand: #e6c98a; + --sand-2: #d4a85a; + --ember: #c8643a; + --spice: #e08a3c; + --green: #8fb87a; + --atreides: #4a86c5; + --harkonnen: #a93832; + --shadow: 0 1px 0 rgba(255, 255, 255, 0.04), + 0 12px 32px -16px rgba(0, 0, 0, 0.6); +} + +* { + box-sizing: border-box; +} +html, +body { + margin: 0; + padding: 0; + background: var(--bg); + color: var(--ink); +} +body { + font-family: 'Inter', system-ui, sans-serif; + font-size: 15px; + line-height: 1.5; + background: radial-gradient( + 1200px 600px at 80% -10%, + rgba(224, 138, 60, 0.1), + transparent 60% + ), + radial-gradient( + 900px 500px at -10% 110%, + rgba(212, 168, 90, 0.06), + transparent 60% + ), + var(--bg); + min-height: 100vh; +} +a { color: var(--sand); } + +.wrap { + max-width: 1240px; + margin: 0 auto; + padding: 48px 28px 96px; +} +header.hero { + margin-bottom: 32px; +} +.eyebrow { + font-family: 'JetBrains Mono', monospace; + font-size: 11px; + letter-spacing: 0.22em; + text-transform: uppercase; + color: var(--sand-2); + margin-bottom: 12px; + display: flex; + align-items: center; + gap: 12px; +} +.eyebrow::after { + content: ''; + flex: 1; + height: 1px; + background: linear-gradient(90deg, var(--line) 0%, transparent 100%); +} +h1 { + font-family: 'Cormorant Garamond', serif; + font-weight: 500; + font-size: clamp(36px, 5vw, 56px); + line-height: 1; + margin: 0 0 14px; + letter-spacing: -0.01em; +} +h1 em { + font-style: italic; + color: var(--sand); + font-weight: 500; +} +.lede { + color: var(--ink-dim); + max-width: 640px; + font-size: 16px; +} + +.panel { + background: linear-gradient(180deg, var(--panel) 0%, var(--panel-2) 100%); + border: 1px solid var(--line-soft); + border-radius: 4px; + padding: 22px 26px; + margin-bottom: 22px; + box-shadow: var(--shadow); +} +.panel-head { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 18px; + padding-bottom: 14px; + border-bottom: 1px dashed var(--line-soft); +} +.panel-head h2 { + font-family: 'Cormorant Garamond', serif; + font-weight: 500; + font-size: 26px; + margin: 0; + letter-spacing: 0.01em; +} +.panel-head .sub { + font-family: 'JetBrains Mono', monospace; + font-size: 10.5px; + letter-spacing: 0.2em; + text-transform: uppercase; + color: var(--ink-muted); +} + +.row-grid { + display: grid; + gap: 18px; +} +.cards { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + gap: 16px; +} +.card { + background: var(--bg-2); + border: 1px solid var(--line-soft); + border-radius: 4px; + padding: 18px 20px; + position: relative; +} +.card h3 { + font-family: 'Cormorant Garamond', serif; + font-size: 22px; + font-weight: 500; + margin: 0 0 12px; +} +.card .sym { + font-family: 'JetBrains Mono', monospace; + font-size: 10px; + letter-spacing: 0.2em; + color: var(--ink-muted); + text-transform: uppercase; + margin-bottom: 4px; +} + +.field { + display: flex; + flex-direction: column; + gap: 6px; +} +.field label { + font-family: 'JetBrains Mono', monospace; + font-size: 10.5px; + letter-spacing: 0.18em; + text-transform: uppercase; + color: var(--ink-muted); +} +.field input[type='number'], +.field select { + background: var(--bg); + border: 1px solid var(--line-soft); + color: var(--ink); + font-family: 'JetBrains Mono', monospace; + font-size: 15px; + padding: 8px 12px; + border-radius: 3px; + width: 100%; +} +.field input[type='number']:focus, +.field select:focus { + outline: none; + border-color: var(--sand-2); + background: #1f1812; +} +input[type='number']::-webkit-inner-spin-button, +input[type='number']::-webkit-outer-spin-button { + -webkit-appearance: none; + margin: 0; +} +input[type='number'] { + -moz-appearance: textfield; +} + +.row { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 12px; + margin-bottom: 12px; +} +.row.single { + grid-template-columns: 1fr; +} + +.progress { + margin-top: 10px; + height: 4px; + background: var(--bg); + border-radius: 999px; + overflow: hidden; + position: relative; +} +.progress::before { + content: ''; + position: absolute; + inset: 0; + width: var(--pct, 0%); + background: linear-gradient( + 90deg, + var(--ember) 0%, + var(--spice) 60%, + var(--sand) 100% + ); + transition: width 0.25s ease; +} +.progress-meta { + display: flex; + justify-content: space-between; + font-family: 'JetBrains Mono', monospace; + font-size: 10px; + color: var(--ink-muted); + margin-top: 6px; + letter-spacing: 0.1em; +} + +.house-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 14px; +} +.house-pick { + border: 1px solid var(--line-soft); + border-radius: 4px; + background: var(--bg-2); + padding: 18px 22px; + cursor: pointer; + transition: border-color 0.15s, background 0.15s, transform 0.15s; + display: flex; + flex-direction: column; + gap: 6px; +} +.house-pick:hover { + border-color: var(--line); +} +.house-pick.active { + background: linear-gradient(180deg, var(--panel) 0%, var(--panel-2) 100%); + border-color: var(--sand-2); +} +.house-pick h3 { + font-family: 'Cormorant Garamond', serif; + font-size: 24px; + margin: 0; + font-weight: 500; +} +.house-pick.active.atreides h3 { color: var(--atreides); } +.house-pick.active.harkonnen h3 { color: var(--harkonnen); } +.house-pick .meta { + font-family: 'JetBrains Mono', monospace; + font-size: 10.5px; + letter-spacing: 0.18em; + text-transform: uppercase; + color: var(--ink-muted); +} + +.class-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); + gap: 10px; +} +.class-pick { + border: 1px solid var(--line-soft); + border-radius: 4px; + background: var(--bg-2); + padding: 14px 12px; + cursor: pointer; + text-align: center; + font-family: 'JetBrains Mono', monospace; + font-size: 11px; + letter-spacing: 0.16em; + text-transform: uppercase; + color: var(--ink-dim); + transition: border-color 0.15s, color 0.15s, background 0.15s; +} +.class-pick:hover { + color: var(--sand); + border-color: var(--line); +} +.class-pick.active { + color: var(--sand); + border-color: var(--sand-2); + background: rgba(224, 138, 60, 0.06); +} + +/* Buttons */ +button { + background: transparent; + border: 1px solid var(--line); + color: var(--ink-dim); + font-family: 'JetBrains Mono', monospace; + font-size: 11px; + letter-spacing: 0.18em; + text-transform: uppercase; + padding: 8px 16px; + border-radius: 3px; + cursor: pointer; + transition: border-color 0.15s, color 0.15s, background 0.15s; +} +button:hover { + border-color: var(--sand-2); + color: var(--sand); + background: rgba(224, 138, 60, 0.06); +} +button.primary { + background: var(--spice); + color: #1f1812; + border-color: var(--spice); + font-weight: 600; +} +button.primary:hover { + background: var(--sand); + border-color: var(--sand); +} +button:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.totals { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); + border: 1px solid var(--line-soft); + border-radius: 4px; + overflow: hidden; + background: var(--bg-2); + margin-bottom: 22px; +} +.total { + padding: 20px 22px; + border-right: 1px solid var(--line-soft); + background: linear-gradient(180deg, rgba(255, 255, 255, 0.015), transparent); +} +.total:last-child { border-right: none; } +.total .lbl { + font-family: 'JetBrains Mono', monospace; + font-size: 10.5px; + letter-spacing: 0.2em; + text-transform: uppercase; + color: var(--ink-muted); + margin-bottom: 6px; +} +.total .val { + font-family: 'Cormorant Garamond', serif; + font-weight: 500; + font-size: 36px; + line-height: 1; + color: var(--sand); +} +.total .val .unit { + font-family: 'JetBrains Mono', monospace; + font-size: 11px; + color: var(--ink-muted); + letter-spacing: 0.15em; + margin-left: 6px; +} + +/* Share bar */ +.share-bar { + position: sticky; + top: 0; + z-index: 50; + background: rgba(26, 20, 16, 0.92); + backdrop-filter: blur(8px); + border-bottom: 1px solid var(--line-soft); + padding: 12px 0; + margin: -48px 0 32px; +} +.share-inner { + max-width: 1240px; + margin: 0 auto; + padding: 0 28px; + display: flex; + align-items: center; + gap: 14px; + flex-wrap: wrap; +} +.share-inner .grow { flex: 1; } +.share-link { + font-family: 'JetBrains Mono', monospace; + font-size: 12px; + color: var(--sand); + background: var(--bg-2); + border: 1px solid var(--line-soft); + border-radius: 3px; + padding: 6px 10px; + user-select: all; + word-break: break-all; +} +.toast { + font-family: 'JetBrains Mono', monospace; + font-size: 11px; + letter-spacing: 0.18em; + text-transform: uppercase; + color: var(--green); +} + +/* Skill tree */ +.tree-wrap { + position: relative; + background: var(--bg); + border: 1px solid var(--line-soft); + border-radius: 4px; + padding: 24px; + overflow: auto; + min-height: 520px; +} +.tree-grid { + position: relative; + display: grid; + gap: 12px; + margin: 0 auto; +} +.tree-cell { + width: 96px; + height: 96px; + position: relative; +} +.tree-node { + width: 96px; + height: 96px; + border: 2px solid var(--line); + border-radius: 8px; + background: var(--bg-2); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + cursor: pointer; + position: relative; + transition: border-color 0.15s, background 0.15s, transform 0.1s; +} +.tree-node:hover { border-color: var(--sand-2); } +.tree-node.allocated { border-color: var(--spice); background: rgba(224, 138, 60, 0.1); } +.tree-node.maxed { border-color: var(--sand); background: rgba(230, 201, 138, 0.14); } +.tree-node.kind-ability { border-style: solid; } +.tree-node.kind-attribute { border-style: dashed; } +.tree-node.kind-perk { border-radius: 50%; } +.tree-node.kind-spice { border-style: double; } +.tree-node .label { + position: absolute; + bottom: -22px; + left: 50%; + transform: translateX(-50%); + font-family: 'JetBrains Mono', monospace; + font-size: 9.5px; + letter-spacing: 0.1em; + color: var(--ink-dim); + text-align: center; + width: 130px; + pointer-events: none; + text-shadow: 0 0 4px var(--bg); +} +.tree-node .pts { + position: absolute; + bottom: 4px; + right: 4px; + font-family: 'JetBrains Mono', monospace; + font-size: 10px; + background: rgba(0, 0, 0, 0.6); + padding: 1px 5px; + border-radius: 2px; + color: var(--sand); +} +.tree-node .name { + font-family: 'Cormorant Garamond', serif; + font-size: 14px; + text-align: center; + line-height: 1.1; + padding: 4px; + color: var(--ink-dim); +} +.tree-edges { + position: absolute; + inset: 0; + pointer-events: none; + z-index: 0; +} +.tree-node-wrap { position: relative; z-index: 1; } + +/* Faction tiers list */ +.tier-list { + display: grid; + gap: 6px; + margin-top: 10px; +} +.tier-row { + display: grid; + grid-template-columns: 36px 1fr auto auto; + align-items: center; + gap: 12px; + padding: 8px 10px; + border: 1px solid var(--line-soft); + border-radius: 3px; + background: var(--bg-2); + font-family: 'JetBrains Mono', monospace; + font-size: 12px; + color: var(--ink-dim); +} +.tier-row.reached { border-color: var(--sand-2); color: var(--sand); } +.tier-row.current { background: rgba(224, 138, 60, 0.08); border-color: var(--spice); } +.tier-row .num { + font-family: 'Cormorant Garamond', serif; + font-size: 18px; + color: var(--sand); +} + +.footer-bar { + display: flex; + justify-content: space-between; + font-family: 'JetBrains Mono', monospace; + font-size: 11px; + color: var(--ink-muted); + letter-spacing: 0.1em; + border-top: 1px solid var(--line-soft); + padding-top: 18px; + margin-top: 40px; + gap: 14px; + flex-wrap: wrap; +} + +@media (max-width: 720px) { + .wrap { padding: 32px 18px 64px; } + .house-grid { grid-template-columns: 1fr; } + .totals { grid-template-columns: 1fr; } + .total { border-right: none; border-bottom: 1px solid var(--line-soft); } + .total:last-child { border-bottom: none; } +} diff --git a/character-builder/frontend/src/types.ts b/character-builder/frontend/src/types.ts new file mode 100644 index 0000000..9af3016 --- /dev/null +++ b/character-builder/frontend/src/types.ts @@ -0,0 +1,79 @@ +export type House = 'atreides' | 'harkonnen'; +export type ClassId = + | 'benegesserit' + | 'mentat' + | 'planetologist' + | 'swordmaster' + | 'trooper'; +export type SpecId = + | 'combat' + | 'crafting' + | 'exploration' + | 'gathering' + | 'sabotage'; + +export interface XpRow { + level: number; + xpRequired: number; + totalXp: number; + skillPoints?: number; + totalSkillPoints?: number; + intelPoints?: number; + totalIntelPoints?: number; +} + +export interface XpTable { + header: string[]; + rows: XpRow[]; +} + +export interface FactionTier { + tier: number; + name: string; + standingRequired: number; + totalStanding: number; +} + +export interface FactionTable { + header: string[]; + tiers: FactionTier[]; +} + +export interface SkillNode { + tag: string; + id: string; + name: string; + kind: 'Ability' | 'Attribute' | 'Perk' | 'Spice' | string; + row: number; + col: number; + maxPoints: number; + icon: string | null; + url: string | null; +} + +export interface SkillEdge { + from: string; + to: string; +} + +export interface SkillTree { + id: ClassId; + name: string; + nodes: SkillNode[]; + edges: SkillEdge[]; +} + +export interface SpecProgress { + level: number; + xpInto: number; +} + +export interface BuildState { + v: 1; // schema version + house: House; + classId: ClassId; + character: { level: number; xpInto: number }; + specs: Record; + faction: { tier: number; standingInto: number }; + skills: Record; // tag -> allocated points +} diff --git a/character-builder/frontend/tsconfig.app.json b/character-builder/frontend/tsconfig.app.json new file mode 100644 index 0000000..4cfaaa6 --- /dev/null +++ b/character-builder/frontend/tsconfig.app.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "ES2022", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "preserve", + "strict": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "noFallthroughCasesInSwitch": true, + "resolveJsonModule": true + }, + "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"] +} diff --git a/character-builder/frontend/tsconfig.json b/character-builder/frontend/tsconfig.json new file mode 100644 index 0000000..1ffef60 --- /dev/null +++ b/character-builder/frontend/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/character-builder/frontend/tsconfig.node.json b/character-builder/frontend/tsconfig.node.json new file mode 100644 index 0000000..fe1e070 --- /dev/null +++ b/character-builder/frontend/tsconfig.node.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "ES2022", + "lib": ["ES2023"], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "noEmit": true, + "strict": true + }, + "include": ["vite.config.ts"] +} diff --git a/character-builder/frontend/vite.config.ts b/character-builder/frontend/vite.config.ts new file mode 100644 index 0000000..55260b2 --- /dev/null +++ b/character-builder/frontend/vite.config.ts @@ -0,0 +1,18 @@ +import { defineConfig } from 'vite'; +import vue from '@vitejs/plugin-vue'; + +export default defineConfig({ + plugins: [vue()], + server: { + host: '0.0.0.0', + port: 5173, + proxy: { + '/api': 'http://localhost:3000', + }, + }, + build: { + target: 'es2022', + outDir: 'dist', + emptyOutDir: true, + }, +}); diff --git a/character-builder/scripts/extract.py b/character-builder/scripts/extract.py new file mode 100644 index 0000000..aa9c0f3 --- /dev/null +++ b/character-builder/scripts/extract.py @@ -0,0 +1,319 @@ +#!/usr/bin/env python3 +"""Extract Dune Awakening game data from saved dune.gaming.tools HTML pages. + +Outputs JSON files into ../data/: + - character-xp.json + - spec-{combat,crafting,exploration,gathering,sabotage}.json + - faction-{atreides,harkonnen}.json + - skills-{benegesserit,mentat,planetologist,swordmaster,trooper}.json + - index.json (manifest) +""" +import json +import re +from html.parser import HTMLParser +from pathlib import Path + +SAMPLE = Path(__file__).resolve().parents[2] / "sample-data" +OUT = Path(__file__).resolve().parents[1] / "data" +OUT.mkdir(parents=True, exist_ok=True) + + +# ---------- generic extractor ---------- +class TableExtractor(HTMLParser): + def __init__(self): + super().__init__() + self.in_table = False + self.in_row = False + self.in_cell = False + self.current_row = [] + self.current_cell = "" + self.rows = [] + self.header = [] + + def handle_starttag(self, tag, attrs): + attrs = dict(attrs) + if tag == "table" and "datatable" in (attrs.get("class") or ""): + self.in_table = True + elif self.in_table and tag == "tr": + self.in_row = True + self.current_row = [] + elif self.in_table and tag in ("td", "th"): + self.in_cell = True + self.current_cell = "" + + def handle_endtag(self, tag): + if tag == "table": + self.in_table = False + elif tag == "tr" and self.in_row: + if self.current_row: + self.rows.append(self.current_row) + self.in_row = False + elif tag in ("td", "th") and self.in_cell: + self.current_row.append(self.current_cell.strip()) + self.in_cell = False + + def handle_data(self, data): + if self.in_cell: + self.current_cell += data + + +def parse_table(path: Path): + p = TableExtractor() + p.feed(path.read_text()) + return p.rows + + +def to_int(s: str) -> int: + cleaned = re.sub(r"[^\d-]", "", s or "") + if not cleaned or cleaned == "-": + return 0 + try: + return int(cleaned) + except ValueError: + return 0 + + +def extract_xp_table(path: Path, value_keys: list[str]) -> list[dict]: + """For tables shaped: [Level | XP Required | Total XP | ...]. + value_keys names the columns after Level.""" + rows = parse_table(path) + if not rows: + return [] + header = rows[0] + out = [] + for r in rows[1:]: + if not r or not r[0].strip(): + continue + try: + lvl = to_int(r[0]) + except Exception: + continue + entry = {"level": lvl} + for i, key in enumerate(value_keys, start=1): + if i < len(r): + entry[key] = to_int(r[i]) + out.append(entry) + return {"header": header, "rows": out} + + +# ---------- skill tree extractor ---------- +NODE_RE = re.compile( + r']*class="node[^"]*"[^>]*data-tag="(?PSkills\.[^"]+)"[^>]*' + r'style="(?P + + +
+
+
Lansraad — Specialization Ledger
+

Calculate the XP, quests, and days to your next rank.

+

Plug in your current level for each specialization and where you want to be. The ledger handles the math — total XP needed, quests to clear it, and how many days that takes at your pace.

+
+ +
+
Pace
& Rate
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
Effective
+
125 XP/Q
+
+
+ +
+
+
Total XP Needed
+
0
+
+
+
Total Quests
+
0
+
+
+
Total Days
+
0@ pace
+
+
+ +
+ +
+
Saved locally · refresh-safe
+ +
+ +
+
Lansraad XP table · Levels 1–100
+
Unofficial fan-made calculator · not affiliated with the game's publisher
+
+
+ + + +