221 lines
7.2 KiB
TypeScript
221 lines
7.2 KiB
TypeScript
import useDatabase from '~/composables/useDatabase';
|
|
import { defaultCharacter, type Ability, type Character, type CharacterConfig, type CompiledCharacter, type DoubleIndex, type Feature, type FeatureItem, 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 defineCachedEventHandler(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<MainStat, DoubleIndex<TrainingLevel>[]>),
|
|
leveling: character.levels.map(e => [e.level as Level, e.choice] as DoubleIndex<Level>),
|
|
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<MainStat, number>),
|
|
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: {
|
|
hardcap: Infinity,
|
|
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,
|
|
magicinstinct: 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 => _e.features?.forEach(f => applyFeature(compiled, f))));
|
|
|
|
return compiled;
|
|
}
|
|
function applyFeature(character: CompiledCharacter, f: FeatureItem)
|
|
{
|
|
switch(f.category)
|
|
{
|
|
case "action":
|
|
character.features.action.push(f.text);
|
|
|
|
return;
|
|
case "reaction":
|
|
character.features.reaction.push(f.text);
|
|
|
|
return;
|
|
case "freeaction":
|
|
character.features.freeaction.push(f.text);
|
|
|
|
return;
|
|
case "misc":
|
|
character.features.misc.push(f.text);
|
|
|
|
return;
|
|
case "asset":
|
|
if(f.type === 'add')
|
|
character[f.kind].push(f.asset);
|
|
else
|
|
character[f.kind] = character[f.kind].filter(e => e !== f.asset);
|
|
|
|
return;
|
|
case "value":
|
|
const path = f.property.split(".");
|
|
const object = path.slice(0, -1).reduce((p, v) => p[v], character as any);
|
|
|
|
switch(f.type)
|
|
{
|
|
case "add":
|
|
if(!['number'].includes(typeof object[path[path.length - 1]]))
|
|
break;
|
|
|
|
object[path[path.length - 1]] += f.value;
|
|
|
|
break;
|
|
case "remove":
|
|
if(!['number'].includes(typeof object[path[path.length - 1]]))
|
|
break;
|
|
|
|
object[path[path.length - 1]] -= f.value as number;
|
|
|
|
break;
|
|
case "set":
|
|
if(!['number', 'boolean'].includes(typeof object[path[path.length - 1]]))
|
|
break;
|
|
|
|
object[path[path.length - 1]] = f.value;
|
|
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return;
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
export function getFeaturesOf(stat: MainStat, progression: DoubleIndex<TrainingLevel>[]): TrainingOption[]
|
|
{
|
|
const config = characterData as CharacterConfig;
|
|
return progression.map(e => config.training[stat][e[0]][e[1]]);
|
|
} |