import useDatabase from '~/composables/useDatabase'; import { defaultCharacter, type Ability, type Character, type CharacterConfig, type CompiledCharacter, type DoubleIndex, type Feature, type Level, type MainStat, type TrainingLevel, type TrainingOption } from '~/types/character'; import characterData from '#shared/character-config.json'; import { group } from '~/shared/general.util'; export default defineEventHandler(async (e) => { const id = getRouterParam(e, "id"); if(!id) { setResponseStatus(e, 400); return; } const db = useDatabase(); const character = db.query.characterTable.findFirst({ with: { abilities: true, levels: true, modifiers: true, spells: true, training: true, user: { columns: { username: true } } }, where: (character, { eq }) => eq(character.id, parseInt(id, 10)), }).sync(); if(character !== undefined) { return compileCharacter(Object.assign(defaultCharacter, { id: character.id, name: character.name, people: character.people, level: character.level, aspect: character.aspect, notes: character.notes, health: character.health, mana: character.mana, training: character.training.reduce((p, v) => { if(!(v.stat in p)) p[v.stat] = []; p[v.stat].push([v.level as TrainingLevel, v.choice]); return p; }, {} as Record[]>), leveling: character.levels.map(e => [e.level as Level, e.choice] as DoubleIndex), abilities: group(character.abilities.map(e => ({ ...e, value: [e.value, e.max] as [number, number] })), "ability", "value"), spells: character.spells.map(e => e.value), modifiers: group(character.modifiers, "modifier", "value"), owner: character.owner, username: character.user.username, visibility: character.visibility, } as Character) as Character); } setResponseStatus(e, 404); return; }/* , { name: "character", getKey: (e) => getRouterParam(e, "id") || 'error' } */); function compileCharacter(character: Character & { username?: string }): CompiledCharacter { const config = characterData as CharacterConfig; const race = character.people !== undefined ? config.peoples[character.people] : undefined; const raceOptions = race ? character.leveling!.map(e => race.options[e[0]][e[1]]) : []; const features = Object.entries(config.training).map(e => [e[0], getFeaturesOf(e[0] as MainStat, character.training[e[0] as MainStat])]) as [MainStat, TrainingOption[]][]; const compiled: CompiledCharacter = { id: character.id, owner: character.owner, username: character.username, name: character.name, health: raceOptions.reduce((p, v) => p + (v.health ?? 0), 0), mana: raceOptions.reduce((p, v) => p + (v.mana ?? 0), 0), race: character.people!, modifier: features.map(e => [e[0], Math.floor((e[1].length - 1) / 3) + (character.modifiers[e[0]] ?? 0)] as [MainStat, number]).reduce((p, v) => { p[v[0]] = v[1]; return p }, {} as Record), level: character.level, values: { health: character.health, mana: character.mana }, features: { action: [], reaction: [], freeaction: [], misc: [], }, abilities: { athletics: 0, acrobatics: 0, intimidation: 0, sleightofhand: 0, stealth: 0, survival: 0, investigation: 0, history: 0, religion: 0, arcana: 0, understanding: 0, perception: 0, performance: 0, medecine: 0, persuasion: 0, animalhandling: 0, deception: 0 }, spellslots: 0, artslots: 0, spellranks: { instinct: 0, knowledge: 0, precision: 0, arts: 0, }, spells: character.spells ?? [], speed: false, defense: { static: 6, activeparry: 0, activedodge: 0, passiveparry: 0, passivedodge: 0, }, mastery: { strength: 0, dexterity: 0, shield: 0, armor: 0, multiattack: 1, magicpower: 0, magicspeed: 0, magicelement: 0 }, resistance: { stun: [0, 0], bleed: [0, 0], poison: [0, 0], fear: [0, 0], influence: [0, 0], charm: [0, 0], possesion: [0, 0], precision: [0, 0], knowledge: [0, 0], instinct: [0, 0] }, initiative: 0, aspect: "", notes: character.notes ?? "", }; features.forEach(e => e[1].forEach((_e, i) => applyTrainingOption(e[0], _e, compiled, i === e[1].length - 1))); specialFeatures(compiled, character.training); Object.entries(character.abilities).forEach(e => compiled.abilities[e[0] as Ability]! += e[1][0]); return compiled; } function applyTrainingOption(stat: MainStat, option: TrainingOption, character: CompiledCharacter, last: boolean) { if(option.health) character.health += option.health; if(option.mana) character.mana += option.mana; if(option.mastery) character.mastery[option.mastery]++; if(option.speed) character.speed = option.speed; if(option.initiative) character.initiative += option.initiative; if(option.spellrank) character.spellranks[option.spellrank]++; if(option.defense) option.defense.forEach(e => character.defense[e]++); if(option.resistance) option.resistance.forEach(e => character.resistance[e[0]][e[1] === "attack" ? 0 : 1]++); if(option.spellslot) character.spellslots += option.spellslot in character.modifier ? character.modifier[option.spellslot as MainStat] : option.spellslot as number; if(option.arts) character.artslots += option.arts in character.modifier ? character.modifier[option.arts as MainStat] : option.arts as number; if(option.spell) character.spells.push(option.spell); option.description.forEach(line => !line.disposable && (last || !line.replaced) && character.features[line.category ?? "misc"].push(line.text)); //if(option.features) option.features.forEach(e => applyFeature(e, character)); } function specialFeatures(character: CompiledCharacter, levels: Record[]>) { //Cap la défense const strengthCap3 = levels.strength.some(e => e[0] === 0); const strengthCap6 = levels.strength.some(e => e[0] === 1); const strengthUncapped = levels.strength.some(e => e[0] === 2); const dexterityCap3 = levels.dexterity.some(e => e[0] === 0); const dexterityCap3Stat = levels.dexterity.some(e => e[0] === 1); const dexterityUncapped = levels.dexterity.some(e => e[0] === 2); if(!strengthUncapped || !dexterityUncapped) { if(strengthCap6) { character.defense = { static: 6, activeparry: 0, activedodge: 0, passiveparry: 0, passivedodge: 0, }; } else if(strengthCap3 || dexterityCap3) { character.defense = { static: 3, activeparry: 0, activedodge: 0, passiveparry: 0, passivedodge: 0, }; } else if(dexterityCap3Stat) { character.defense.static = 3; } } }/* function applyFeature(feature: Feature, character: CompiledCharacter) { } */ export function getFeaturesOf(stat: MainStat, progression: DoubleIndex[]): TrainingOption[] { const config = characterData as CharacterConfig; return progression.map(e => config.training[stat][e[0]][e[1]]); }