220 lines
8.0 KiB
TypeScript
220 lines
8.0 KiB
TypeScript
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<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: {
|
|
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<MainStat, DoubleIndex<TrainingLevel>[]>)
|
|
{
|
|
//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<TrainingLevel>[]): TrainingOption[]
|
|
{
|
|
const config = characterData as CharacterConfig;
|
|
return progression.map(e => config.training[stat][e[0]][e[1]]);
|
|
} |