You've already forked obsidian-visualiser
Mass updates
This commit is contained in:
@@ -15,11 +15,13 @@
|
||||
import { Content } from '#shared/content.util';
|
||||
import * as Floating from '#shared/floating.util';
|
||||
import { Toaster } from '#shared/components.util';
|
||||
import { init } from '#shared/i18n';
|
||||
|
||||
onBeforeMount(() => {
|
||||
Content.init();
|
||||
Floating.init();
|
||||
Toaster.init();
|
||||
init()
|
||||
|
||||
const unmount = useRouter().afterEach((to, from, failure) => {
|
||||
if(failure) return;
|
||||
@@ -183,6 +185,10 @@ iconify-icon
|
||||
|
||||
@apply text-light-100 dark:text-dark-100;
|
||||
}
|
||||
.cm-focused
|
||||
{
|
||||
@apply outline-none;
|
||||
}
|
||||
.cm-editor .cm-content
|
||||
{
|
||||
@apply caret-light-100 dark:caret-dark-100;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { relations } from 'drizzle-orm';
|
||||
import { relations, sql } from 'drizzle-orm';
|
||||
import { int, text, sqliteTable as table, primaryKey, blob } from 'drizzle-orm/sqlite-core';
|
||||
import type { ItemState } from '~/types/character';
|
||||
|
||||
export const usersTable = table("users", {
|
||||
id: int().primaryKey({ autoIncrement: true }),
|
||||
@@ -87,8 +88,8 @@ export const campaignTable = table("campaign", {
|
||||
owner: int().notNull().references(() => usersTable.id, { onDelete: 'cascade', onUpdate: 'cascade' }),
|
||||
link: text().notNull(),
|
||||
status: text({ enum: ['PREPARING', 'PLAYING', 'ARCHIVED'] }).default('PREPARING'),
|
||||
settings: text({ mode: 'json' }).default('{}'),
|
||||
inventory: text({ mode: 'json' }).default('[]'),
|
||||
settings: text({ mode: 'json' }).default({}).$type<{}>(),
|
||||
items: text({ mode: 'json' }).default([]).$type<ItemState[]>(),
|
||||
money: int().default(0),
|
||||
public_notes: text().default(''),
|
||||
dm_notes: text().default(''),
|
||||
@@ -101,13 +102,6 @@ export const campaignCharactersTable = table("campaign_characters", {
|
||||
id: int().references(() => campaignTable.id, { onDelete: 'cascade', onUpdate: 'cascade' }),
|
||||
character: int().references(() => characterTable.id, { onDelete: 'cascade', onUpdate: 'cascade' }),
|
||||
}, (table) => [primaryKey({ columns: [table.id, table.character] })]);
|
||||
export const campaignLogsTable = table("campaign_logs", {
|
||||
id: int().references(() => campaignTable.id, { onDelete: 'cascade', onUpdate: 'cascade' }),
|
||||
target: int(),
|
||||
timestamp: int({ mode: 'timestamp_ms' }).notNull(),
|
||||
type: text({ enum: ['ITEM', 'CHARACTER', 'PLACE', 'EVENT', 'FIGHT', 'TEXT'] }),
|
||||
details: text().notNull(),
|
||||
}, (table) => [primaryKey({ columns: [table.id, table.target, table.timestamp] })]);
|
||||
|
||||
export const usersRelation = relations(usersTable, ({ one, many }) => ({
|
||||
data: one(usersDataTable, { fields: [usersTable.id], references: [usersDataTable.id], }),
|
||||
@@ -153,7 +147,6 @@ export const characterChoicesRelation = relations(characterChoicesTable, ({ one
|
||||
export const campaignRelation = relations(campaignTable, ({ one, many }) => ({
|
||||
members: many(campaignMembersTable),
|
||||
characters: many(campaignCharactersTable),
|
||||
logs: many(campaignLogsTable),
|
||||
owner: one(usersTable, { fields: [campaignTable.owner], references: [usersTable.id], }),
|
||||
}));
|
||||
export const campaignMembersRelation = relations(campaignMembersTable, ({ one }) => ({
|
||||
@@ -163,7 +156,4 @@ export const campaignMembersRelation = relations(campaignMembersTable, ({ one })
|
||||
export const campaignCharacterRelation = relations(campaignCharactersTable, ({ one }) => ({
|
||||
campaign: one(campaignTable, { fields: [campaignCharactersTable.id], references: [campaignTable.id], }),
|
||||
character: one(characterTable, { fields: [campaignCharactersTable.character], references: [characterTable.id], }),
|
||||
}));
|
||||
export const campaignLogsRelation = relations(campaignLogsTable, ({ one }) => ({
|
||||
campaign: one(campaignTable, { fields: [campaignLogsTable.id], references: [campaignTable.id], }),
|
||||
}));
|
||||
@@ -98,7 +98,7 @@ onMounted(() => {
|
||||
}, (item) => item.navigable);
|
||||
(path.value?.split('/').map((e, i, a) => a.slice(0, i).join('/')) ?? []).forEach(e => tree?.toggle(tree.tree.search('path', e)[0], true));
|
||||
|
||||
treeParent.value!.replaceChildren(tree.container);
|
||||
treeParent.value?.replaceChildren(tree.container);
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -4,6 +4,7 @@ import { unifySlug } from '#shared/general.util';
|
||||
|
||||
definePageMeta({
|
||||
guestsGoesTo: '/user/login',
|
||||
validState: true,
|
||||
});
|
||||
const id = unifySlug(useRouter().currentRoute.value.params.id ?? "new");
|
||||
const container = useTemplateRef('container');
|
||||
|
||||
@@ -9,6 +9,8 @@ definePageMeta({
|
||||
const { data: characters, error, status } = await useFetch(`/api/character`);
|
||||
const config = characterConfig as CharacterConfig;
|
||||
|
||||
const { user } = useUserSession();
|
||||
|
||||
async function deleteCharacter(id: number)
|
||||
{
|
||||
status.value = "pending";
|
||||
@@ -78,10 +80,11 @@ async function duplicateCharacter(id: number)
|
||||
</div>
|
||||
<div v-else class="flex flex-col gap-2 items-center flex-1">
|
||||
<span class="text-lg font-bold">Vous n'avez pas encore de personnage</span>
|
||||
<NuxtLink class="inline-flex justify-center items-center outline-none leading-none transition-[box-shadow]
|
||||
<NuxtLink v-if="user && user.state === 1" class="inline-flex justify-center items-center outline-none leading-none transition-[box-shadow]
|
||||
text-light-100 dark:text-dark-100 bg-light-20 dark:bg-dark-20 border border-light-40 dark:border-dark-40
|
||||
hover:bg-light-25 dark:hover:bg-dark-25 hover:border-light-50 dark:hover:border-dark-50
|
||||
focus:bg-light-30 dark:focus:bg-dark-30 focus:border-light-50 dark:focus:border-dark-50 focus:shadow-raw focus:shadow-light-50 dark:focus:shadow-dark-50 py-2 px-4" :to="{ name: 'character-id-edit', params: { id: 'new' } }">Nouveau personnage</NuxtLink>
|
||||
<div v-else>Veuillez validez votre adresse mail pour pouvoir créer des personnages.</div>
|
||||
<NuxtLink class="inline-flex justify-center items-center outline-none leading-none transition-[box-shadow]
|
||||
text-light-100 dark:text-dark-100 bg-light-20 dark:bg-dark-20 border border-light-40 dark:border-dark-40
|
||||
hover:bg-light-25 dark:hover:bg-dark-25 hover:border-light-50 dark:hover:border-dark-50
|
||||
|
||||
@@ -3,6 +3,8 @@ import characterConfig from '#shared/character-config.json';
|
||||
import type { CharacterConfig } from '~/types/character';
|
||||
const { data: characters, error, status } = await useFetch(`/api/character`, { params: { visibility: "public" } });
|
||||
const config = characterConfig as CharacterConfig;
|
||||
|
||||
const { user } = useUserSession();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -34,11 +36,14 @@ const config = characterConfig as CharacterConfig;
|
||||
</div>
|
||||
<div v-else class="flex flex-col gap-2 items-center flex-1">
|
||||
<span class="text-lg font-bold">Il n'existe pas encore de personnage public</span>
|
||||
Soyez le premier à partager vos créations !
|
||||
<NuxtLink class="inline-flex justify-center items-center outline-none leading-none transition-[box-shadow]
|
||||
text-light-100 dark:text-dark-100 bg-light-20 dark:bg-dark-20 border border-light-40 dark:border-dark-40
|
||||
hover:bg-light-25 dark:hover:bg-dark-25 hover:border-light-50 dark:hover:border-dark-50
|
||||
focus:bg-light-30 dark:focus:bg-dark-30 focus:border-light-50 dark:focus:border-dark-50 focus:shadow-raw focus:shadow-light-50 dark:focus:shadow-dark-50 py-2 px-4" :to="{ name: 'character-id-edit', params: { id: 'new' } }">Nouveau personnage</NuxtLink>
|
||||
<template v-if="user && user.state === 1">
|
||||
Soyez le premier à partager vos créations !
|
||||
<NuxtLink class="inline-flex justify-center items-center outline-none leading-none transition-[box-shadow]
|
||||
text-light-100 dark:text-dark-100 bg-light-20 dark:bg-dark-20 border border-light-40 dark:border-dark-40
|
||||
hover:bg-light-25 dark:hover:bg-dark-25 hover:border-light-50 dark:hover:border-dark-50
|
||||
focus:bg-light-30 dark:focus:bg-dark-30 focus:border-light-50 dark:focus:border-dark-50 focus:shadow-raw focus:shadow-light-50 dark:focus:shadow-dark-50 py-2 px-4" :to="{ name: 'character-id-edit', params: { id: 'new' } }">Nouveau personnage</NuxtLink>
|
||||
</template>
|
||||
<div v-else>Veuillez valider votre adresse mail pour pouvoir créer des personnages.</div>
|
||||
</div>
|
||||
</template>
|
||||
<div v-else>
|
||||
|
||||
@@ -100,7 +100,7 @@ onMounted(async () => {
|
||||
|
||||
editor = new Editor();
|
||||
|
||||
Content.ready.then(() => tree.value!.replaceChild(editor.tree.container, load));
|
||||
Content.ready.then(() => tree.value?.replaceChild(editor.tree.container, load));
|
||||
container.value.appendChild(editor.container);
|
||||
}
|
||||
});
|
||||
|
||||
11
app/types/campaign.d.ts
vendored
11
app/types/campaign.d.ts
vendored
@@ -4,7 +4,7 @@ import type { Serialize } from 'nitropack';
|
||||
|
||||
export type CampaignVariables = {
|
||||
money: number;
|
||||
inventory: ItemState[];
|
||||
items: ItemState[];
|
||||
};
|
||||
export type Campaign = {
|
||||
id: number;
|
||||
@@ -16,11 +16,4 @@ export type Campaign = {
|
||||
characters: Array<Partial<{ character: { id: number, name: string, owner: number } }>>;
|
||||
public_notes: string;
|
||||
dm_notes: string;
|
||||
logs: CampaignLog[];
|
||||
} & CampaignVariables;
|
||||
export type CampaignLog = {
|
||||
target: number;
|
||||
timestamp: Serialize<Date>;
|
||||
type: 'ITEM' | 'CHARACTER' | 'PLACE' | 'FIGHT' | 'TEXT';
|
||||
details: string;
|
||||
};
|
||||
} & CampaignVariables;
|
||||
60
app/types/character.d.ts
vendored
60
app/types/character.d.ts
vendored
@@ -57,39 +57,54 @@ export type CharacterVariables = {
|
||||
|
||||
money: number;
|
||||
};
|
||||
type CommonState = {
|
||||
capacity?: number;
|
||||
powercost?: number;
|
||||
};
|
||||
type ArmorState = { health?: number };
|
||||
type WeaponState = { attack?: number | string, hit?: number };
|
||||
type WondrousState = { };
|
||||
type MundaneState = { };
|
||||
type ItemState = {
|
||||
id: string;
|
||||
amount: number;
|
||||
enchantments?: string[];
|
||||
charges?: number;
|
||||
equipped?: boolean;
|
||||
state?: any;
|
||||
state?: (ArmorState | WeaponState | WondrousState | MundaneState) & CommonState;
|
||||
};
|
||||
export type CharacterConfig = {
|
||||
peoples: Record<string, RaceConfig>;
|
||||
training: Record<MainStat, Record<TrainingLevel, FeatureID[]>>;
|
||||
spells: Record<string, SpellConfig>;
|
||||
spells: Record<string, SpellConfig | ArtConfig>;
|
||||
aspects: Record<string, AspectConfig>;
|
||||
features: Record<FeatureID, Feature>;
|
||||
enchantments: Record<string, EnchantementConfig>; //TODO
|
||||
enchantments: Record<string, EnchantementConfig>;
|
||||
items: Record<string, ItemConfig>;
|
||||
sickness: Record<string, { id: string, name: string, description: string, effect: FeatureID[] }>;
|
||||
action: Record<string, { id: string, name: string, description: string, cost: number }>;
|
||||
reaction: Record<string, { id: string, name: string, description: string, cost: number }>;
|
||||
freeaction: Record<string, { id: string, name: string, description: string }>;
|
||||
passive: Record<string, { id: string, name: string, description: string }>;
|
||||
texts: Record<i18nID, Localized>;
|
||||
|
||||
//Each of these groups extend an existing feature as they all use the same properties
|
||||
sickness: Record<FeatureID, { stage: number }>; //TODO
|
||||
poisons: Record<FeatureID, { difficulty: number, efficienty: number, solubility: number }>; //TODO
|
||||
dedications: Record<FeatureID, { id: string, name: string, description: i18nID, effect: FeatureID[], requirement: Array<{ stat: MainStat, amount: number }> }>; //TODO
|
||||
};
|
||||
export type EnchantementConfig = {
|
||||
id: string;
|
||||
name: string; //TODO -> TextID
|
||||
description: i18nID;
|
||||
effect: Array<FeatureEquipment | FeatureValue | FeatureList>;
|
||||
power: number;
|
||||
restrictions?: Array<'armor' | 'mundane' | 'wondrous' | 'weapon' | `armor/${ArmorConfig['type']}` | `weapon/${WeaponConfig['type'][number]}`>; // Need to respect *any* of the restriction, not every restrictions.
|
||||
}
|
||||
export type ItemConfig = CommonItemConfig & (ArmorConfig | WeaponConfig | WondrousConfig | MundaneConfig);
|
||||
type CommonItemConfig = {
|
||||
id: string;
|
||||
name: string; //TODO -> TextID
|
||||
flavoring: i18nID;
|
||||
flavoring?: i18nID;
|
||||
description: i18nID;
|
||||
rarity: 'common' | 'uncommon' | 'rare' | 'legendary';
|
||||
weight?: number; //Optionnal but highly recommended
|
||||
@@ -101,6 +116,7 @@ type CommonItemConfig = {
|
||||
effects?: Array<FeatureValue | FeatureEquipment | FeatureList>;
|
||||
equippable: boolean;
|
||||
consummable: boolean;
|
||||
craft?: { mineral: number, natural: number, processed: number, magical: number };
|
||||
}
|
||||
type ArmorConfig = {
|
||||
category: 'armor';
|
||||
@@ -126,7 +142,7 @@ export type SpellConfig = {
|
||||
id: string;
|
||||
name: string; //TODO -> TextID
|
||||
rank: 1 | 2 | 3 | 4;
|
||||
type: SpellType;
|
||||
type: Exclude<SpellType, "arts">;
|
||||
cost: number;
|
||||
speed: "action" | "reaction" | number;
|
||||
elements: Array<SpellElement>;
|
||||
@@ -135,6 +151,15 @@ export type SpellConfig = {
|
||||
range: 'personnal' | number;
|
||||
tags?: string[];
|
||||
};
|
||||
export type ArtConfig = {
|
||||
id: string;
|
||||
name: string; //TODO -> TextID
|
||||
rank: 1 | 2 | 3;
|
||||
type: "arts";
|
||||
difficulty: number;
|
||||
description: string; //TODO -> TextID
|
||||
tags?: string[];
|
||||
};
|
||||
export type RaceConfig = {
|
||||
id: string;
|
||||
name: string; //TODO -> TextID
|
||||
@@ -204,16 +229,19 @@ export type CompiledCharacter = {
|
||||
spellslots: number; //Max
|
||||
artslots: number; //Max
|
||||
spellranks: Record<SpellType, 0 | 1 | 2 | 3>;
|
||||
aspect: string; //ID
|
||||
aspect: {
|
||||
id: string,
|
||||
amount: number;
|
||||
duration: number;
|
||||
bonus: number;
|
||||
tier: 0 | 1 | 2;
|
||||
};
|
||||
speed: number | false;
|
||||
capacity: number | false;
|
||||
initiative: number;
|
||||
exhaust: number;
|
||||
itempower: number;
|
||||
|
||||
action: number;
|
||||
reaction: number;
|
||||
|
||||
variables: CharacterVariables,
|
||||
|
||||
defense: {
|
||||
@@ -238,10 +266,18 @@ export type CompiledCharacter = {
|
||||
};
|
||||
|
||||
bonus: {
|
||||
defense: Partial<Record<MainStat, number>>;
|
||||
defense: Partial<Record<MainStat, number>>; //Defense aux jets de resistance
|
||||
abilities: Partial<Record<Ability, number>>;
|
||||
spells: {
|
||||
type: Partial<Record<SpellType, number>>;
|
||||
rank: Partial<Record<1 | 2 | 3 | 4, number>>;
|
||||
elements: Partial<Record<SpellElement, number>>;
|
||||
};
|
||||
weapon: Partial<Record<WeaponType, number>>;
|
||||
}; //Any special bonus goes here
|
||||
resistance: Record<string, number>;
|
||||
resistance: Partial<Record<Resistance, number>>; //Bonus à l'attaque
|
||||
|
||||
craft: { level: number, bonus: number };
|
||||
|
||||
modifier: Record<MainStat, number>;
|
||||
abilities: Partial<Record<Ability, number>>;
|
||||
|
||||
1
app/types/general.d.ts
vendored
1
app/types/general.d.ts
vendored
@@ -17,5 +17,4 @@ type CanvasPreferences = {
|
||||
export type Localized = {
|
||||
fr_FR?: string;
|
||||
en_US?: string;
|
||||
default: string;
|
||||
}
|
||||
Reference in New Issue
Block a user