Rang {{ spell.rank }}/
diff --git a/pages/homebrew/[id]/edit.client.vue b/pages/homebrew/[id]/edit.client.vue
new file mode 100644
index 0000000..cbbea2e
--- /dev/null
+++ b/pages/homebrew/[id]/edit.client.vue
@@ -0,0 +1,57 @@
+
+
+
+
+ d[any] - Edition de {{ homebrew.name || 'nouvelle personnalisation' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/homebrew/list.client.vue b/pages/homebrew/list.client.vue
new file mode 100644
index 0000000..27e0f69
--- /dev/null
+++ b/pages/homebrew/list.client.vue
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/server/api/character/[id]/compiled.get.ts b/server/api/character/[id]/compiled.get.ts
index d529dd4..51da86f 100644
--- a/server/api/character/[id]/compiled.get.ts
+++ b/server/api/character/[id]/compiled.get.ts
@@ -1,9 +1,9 @@
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 { 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 defineEventHandler(async (e) => {
+export default defineCachedEventHandler(async (e) => {
const id = getRouterParam(e, "id");
if(!id)
{
@@ -53,7 +53,7 @@ export default defineEventHandler(async (e) => {
setResponseStatus(e, 404);
return;
-}/* , { name: "character", getKey: (e) => getRouterParam(e, "id") || 'error' } */);
+}, { name: "character", getKey: (e) => getRouterParam(e, "id") || 'error' });
function compileCharacter(character: Character & { username?: string }): CompiledCharacter
{
@@ -112,6 +112,7 @@ function compileCharacter(character: Character & { username?: string }): Compile
spells: character.spells ?? [],
speed: false,
defense: {
+ hardcap: Infinity,
static: 6,
activeparry: 0,
activedodge: 0,
@@ -126,7 +127,8 @@ function compileCharacter(character: Character & { username?: string }): Compile
multiattack: 1,
magicpower: 0,
magicspeed: 0,
- magicelement: 0
+ magicelement: 0,
+ magicinstinct: 0,
},
resistance: {
stun: [0, 0],
@@ -145,74 +147,73 @@ function compileCharacter(character: Character & { username?: string }): Compile
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]);
+ features.forEach(e => e[1].forEach(_e => _e.features?.forEach(f => applyFeature(compiled, f))));
return compiled;
}
-function applyTrainingOption(stat: MainStat, option: TrainingOption, character: CompiledCharacter, last: boolean)
+function applyFeature(character: CompiledCharacter, f: FeatureItem)
{
- 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)
+ switch(f.category)
{
- 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;
- }
+ 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;
}
-}/*
-function applyFeature(feature: Feature, character: CompiledCharacter)
-{
-
-} */
+}
export function getFeaturesOf(stat: MainStat, progression: DoubleIndex[]): TrainingOption[]
{
const config = characterData as CharacterConfig;
diff --git a/server/api/character/[id]/duplicate.post.ts b/server/api/character/[id]/duplicate.post.ts
index 4d35ca9..7b26f12 100644
--- a/server/api/character/[id]/duplicate.post.ts
+++ b/server/api/character/[id]/duplicate.post.ts
@@ -1,6 +1,6 @@
import { eq } from 'drizzle-orm';
import useDatabase from '~/composables/useDatabase';
-import { characterTable } from '~/db/schema';
+import { characterAbilitiesTable, characterLevelingTable, characterModifiersTable, characterSpellsTable, characterTable, characterTrainingTable } from '~/db/schema';
export default defineEventHandler(async (e) => {
const id = getRouterParam(e, "id");
@@ -25,13 +25,47 @@ export default defineEventHandler(async (e) => {
setResponseStatus(e, 401);
return;
}
+
+ try
+ {
+ const _id = db.transaction((tx) => {
+ const _id = tx.insert(characterTable).values({
+ name: old.name,
+ owner: session.user!.id,
+ people: old.people!,
+ level: old.level,
+ aspect: old.aspect,
+ notes: old.notes,
+ health: old.health,
+ mana: old.mana,
+ visibility: old.visibility,
+ thumbnail: old.thumbnail,
+ }).returning({ id: characterTable.id }).get().id;
+
+ const leveling = tx.select().from(characterLevelingTable).where(eq(characterLevelingTable.character, parseInt(id, 10))).all();
+ if(leveling.length > 0) tx.insert(characterLevelingTable).values(leveling.map(e => ({ character: _id, level: e.level, choice: e.choice }))).run();
- const returned = await db.insert(characterTable).values({
- name: `Copie de ${old.name}`,
- progress: old.progress,
- owner: session.user.id,
- }).returning({ id: characterTable.id });
+ const training = tx.select().from(characterTrainingTable).where(eq(characterTrainingTable.character, parseInt(id, 10))).all();
+ if(training.length > 0) tx.insert(characterTrainingTable).values(training.map(e => ({ character: _id, stat: e.stat, level: e.level, choice: e.choice }))).run();
- setResponseStatus(e, 201);
- return returned[0].id;
+ const modifiers = tx.select().from(characterModifiersTable).where(eq(characterModifiersTable.character, parseInt(id, 10))).all();
+ if(modifiers.length > 0) tx.insert(characterModifiersTable).values(modifiers.map(e => ({ character: _id, modifier: e.modifier, value: e.value }))).run();
+
+ const spells = tx.select().from(characterSpellsTable).where(eq(characterSpellsTable.character, parseInt(id, 10))).all();
+ if(spells.length > 0) tx.insert(characterSpellsTable).values(spells.map(e => ({ character: _id, value: e.value }))).run();
+
+ const abilities = tx.select().from(characterAbilitiesTable).where(eq(characterAbilitiesTable.character, parseInt(id, 10))).all();
+ if(abilities.length > 0) tx.insert(characterAbilitiesTable).values(abilities.map(e => ({ character: _id, ability: e.ability, value: e.value, max: e.max }))).run();
+
+ return _id;
+ });
+
+ setResponseStatus(e, 201);
+ return _id;
+ }
+ catch(_e)
+ {
+ setResponseStatus(e, 201);
+ throw _e;
+ }
});
\ No newline at end of file
diff --git a/server/api/character/[id]/values.get.ts b/server/api/character/[id]/values.get.ts
index 478f6a5..97d3b87 100644
--- a/server/api/character/[id]/values.get.ts
+++ b/server/api/character/[id]/values.get.ts
@@ -21,12 +21,13 @@ export default defineEventHandler(async (e) => {
const db = useDatabase();
const character = db.select({
- values: characterTable.values
+ health: characterTable.health,
+ mana: characterTable.mana,
}).from(characterTable).where(and(eq(characterTable.id, parseInt(id, 10)), eq(characterTable.owner, session.user.id))).get();
if(character !== undefined)
{
- return character.values as CharacterValues;
+ return character as CharacterValues;
}
setResponseStatus(e, 404);
diff --git a/server/api/character/[id]/values.post.ts b/server/api/character/[id]/values.post.ts
index d5d8d8e..35c7dc2 100644
--- a/server/api/character/[id]/values.post.ts
+++ b/server/api/character/[id]/values.post.ts
@@ -1,6 +1,7 @@
import { eq } from 'drizzle-orm';
import useDatabase from '~/composables/useDatabase';
import { characterTable } from '~/db/schema';
+import type { CharacterValues } from '~/types/character';
export default defineEventHandler(async (e) => {
const id = getRouterParam(e, "id");
@@ -10,7 +11,7 @@ export default defineEventHandler(async (e) => {
return;
}
- const body = await readBody(e);
+ const body = await readBody(e) as CharacterValues;
if(!body)
{
setResponseStatus(e, 400);
@@ -34,7 +35,8 @@ export default defineEventHandler(async (e) => {
}
db.update(characterTable).set({
- values: body,
+ health: body.health,
+ mana: body.mana,
}).where(eq(characterTable.id, parseInt(id, 10))).run();
setResponseStatus(e, 200);
diff --git a/server/api/homebrew/[id].get.ts b/server/api/homebrew/[id].get.ts
new file mode 100644
index 0000000..ab8367d
--- /dev/null
+++ b/server/api/homebrew/[id].get.ts
@@ -0,0 +1,22 @@
+export default defineEventHandler(async (e) => {
+ const id = getRouterParam(e, "id");
+
+ if(!id)
+ {
+ setResponseStatus(e, 400);
+ return;
+ }
+
+ const session = await getUserSession(e);
+
+ if(!session.user)
+ {
+ setResponseStatus(e, 401);
+ return;
+ }
+
+ console.log(id);
+
+ setResponseStatus(e, 200);
+ return {};
+});
\ No newline at end of file
diff --git a/server/api/homebrew/[id].post.ts b/server/api/homebrew/[id].post.ts
new file mode 100644
index 0000000..1b58876
--- /dev/null
+++ b/server/api/homebrew/[id].post.ts
@@ -0,0 +1,23 @@
+export default defineEventHandler(async (e) => {
+ const id = getRouterParam(e, "id");
+
+ if(!id)
+ {
+ setResponseStatus(e, 400);
+ return;
+ }
+
+ const session = await getUserSession(e);
+
+ if(!session.user)
+ {
+ setResponseStatus(e, 401);
+ return;
+ }
+
+ console.log(id);
+ console.log(await readBody(e));
+
+ setResponseStatus(e, 200);
+ return;
+});
\ No newline at end of file
diff --git a/shared/character-config.json b/shared/character-config.json
index 0c6dd04..2179476 100644
--- a/shared/character-config.json
+++ b/shared/character-config.json
@@ -81,6 +81,14 @@
"disposable": false,
"replaced": true
}
+ ],
+ "features": [
+ {
+ "category": "value",
+ "type": "set",
+ "property": "defense.hardcap",
+ "value": 3
+ }
]
}
],
@@ -92,7 +100,7 @@
"disposable": true
},
{
- "text": "Def max à 5.",
+ "text": "Def max à 6.",
"disposable": false,
"replaced": true
},
@@ -102,7 +110,20 @@
"replaced": true
}
],
- "speed": false
+ "features": [
+ {
+ "category": "value",
+ "type": "set",
+ "property": "defense.hardcap",
+ "value": 6
+ },
+ {
+ "category": "value",
+ "type": "set",
+ "property": "speed",
+ "value": false
+ }
+ ]
}
],
"2": [
@@ -122,8 +143,26 @@
"replaced": true
}
],
- "speed": 0,
- "mastery": "strength"
+ "features": [
+ {
+ "category": "value",
+ "type": "set",
+ "property": "defense.hardcap",
+ "value": 9999
+ },
+ {
+ "category": "value",
+ "type": "set",
+ "property": "speed",
+ "value": 0
+ },
+ {
+ "category": "value",
+ "type": "add",
+ "property": "mastery.strength",
+ "value": 1
+ }
+ ]
}
],
"3": [
@@ -142,9 +181,26 @@
"disposable": true
}
],
- "speed": 3,
- "mastery": "armor",
- "defense": ["activeparry"]
+ "features": [
+ {
+ "category": "value",
+ "type": "set",
+ "property": "speed",
+ "value": 3
+ },
+ {
+ "category": "value",
+ "type": "add",
+ "property": "mastery.armor",
+ "value": 1
+ },
+ {
+ "category": "value",
+ "type": "add",
+ "property": "defense.activeparry",
+ "value": 1
+ }
+ ]
}
],
"4": [
@@ -163,9 +219,32 @@
"disposable": true
}
],
- "speed": 6,
- "mastery": "strength",
- "defense": ["passiveparry", "activeparry"]
+ "features": [
+ {
+ "category": "value",
+ "type": "set",
+ "property": "speed",
+ "value": 6
+ },
+ {
+ "category": "value",
+ "type": "add",
+ "property": "mastery.strength",
+ "value": 1
+ },
+ {
+ "category": "value",
+ "type": "add",
+ "property": "defense.activeparry",
+ "value": 1
+ },
+ {
+ "category": "value",
+ "type": "add",
+ "property": "defense.passiveparry",
+ "value": 1
+ }
+ ]
}
],
"5": [
@@ -176,7 +255,14 @@
"disposable": true
}
],
- "mastery": "strength"
+ "features": [
+ {
+ "category": "value",
+ "type": "add",
+ "property": "mastery.strength",
+ "value": 1
+ }
+ ]
},
{
"description": [
@@ -184,6 +270,12 @@
"text": "Lorsque vous [[1. Règles/3. Le combat/2. Actions en combat#Intercepter|interceptez]] un adversaire, vous pouvez faire une attaque plutôt que de le contraindre.",
"disposable": false
}
+ ],
+ "features": [
+ {
+ "category": "misc",
+ "text": "Lorsque vous [[1. Règles/3. Le combat/2. Actions en combat#Intercepter|interceptez]] un adversaire, vous pouvez faire une attaque plutôt que de le contraindre."
+ }
]
},
{
@@ -193,7 +285,14 @@
"disposable": true
}
],
- "mastery": "shield"
+ "features": [
+ {
+ "category": "value",
+ "type": "add",
+ "property": "mastery.shield",
+ "value": 1
+ }
+ ]
}
],
"6": [
@@ -204,7 +303,14 @@
"disposable": true
}
],
- "mastery": "strength"
+ "features": [
+ {
+ "category": "value",
+ "type": "add",
+ "property": "mastery.strength",
+ "value": 1
+ }
+ ]
},
{
"description": [
@@ -212,6 +318,12 @@
"text": "En infligeant des dégâts critique, vous pouvez choisir d'ignorer l'armure adverse.",
"disposable": false
}
+ ],
+ "features": [
+ {
+ "category": "misc",
+ "text": "En infligeant des dégâts critique, vous pouvez choisir d'ignorer l'armure adverse."
+ }
]
},
{
@@ -221,7 +333,14 @@
"disposable": true
}
],
- "mastery": "armor"
+ "features": [
+ {
+ "category": "value",
+ "type": "add",
+ "property": "mastery.armor",
+ "value": 1
+ }
+ ]
}
],
"7": [
@@ -231,6 +350,12 @@
"text": "Utiliser la [[1. Règles/2. L'entrainement/1. Entrainement#La force|force]] pour frapper avec une arme augmente les dégâts infligés de 2.",
"disposable": false
}
+ ],
+ "features": [
+ {
+ "category": "misc",
+ "text": "Utiliser la [[1. Règles/2. L'entrainement/1. Entrainement#La force|force]] pour frapper avec une arme augmente les dégâts infligés de 2."
+ }
]
},
{
@@ -240,7 +365,14 @@
"disposable": true
}
],
- "mastery": "strength"
+ "features": [
+ {
+ "category": "value",
+ "type": "add",
+ "property": "mastery.strength",
+ "value": 1
+ }
+ ]
},
{
"description": [
@@ -249,7 +381,14 @@
"disposable": true
}
],
- "mastery": "armor"
+ "features": [
+ {
+ "category": "value",
+ "type": "add",
+ "property": "mastery.armor",
+ "value": 1
+ }
+ ]
}
],
"8": [
@@ -259,6 +398,14 @@
"text": "Frapper avec une [[1. Règles/99. Annexes/4. Équipement#Les armes|arme standard]], [[1. Règles/99. Annexes/4. Équipement#Les armes lourdes|lourdes]] ou [[1. Règles/99. Annexes/4. Équipement#Les armes à deux mains|à deux mains]] augmente les dégâts infligés de 2, mais réduit le [[1. Règles/99. Annexes/1. Les évolutions de valeur.canvas#Les niveaux de dé de dégâts|dé de dégâts]] au niveau inférieur.",
"disposable": false
}
+ ],
+ "features": [
+ {
+ "category": "value",
+ "type": "add",
+ "property": "mastery.armor",
+ "value": 1
+ }
]
},
{
@@ -268,6 +415,14 @@
"disposable": false,
"category": "action"
}
+ ],
+ "features": [
+ {
+ "category": "value",
+ "type": "add",
+ "property": "mastery.armor",
+ "value": 1
+ }
]
},
{
@@ -276,6 +431,12 @@
"text": "En vous [[1. Règles/3. Le combat/2. Actions en combat#S'interposer|interposant]], vous gagnez un bonus de +2 pour contraindre.",
"disposable": false
}
+ ],
+ "features": [
+ {
+ "category": "misc",
+ "text": "En vous [[1. Règles/3. Le combat/2. Actions en combat#S'interposer|interposant]], vous gagnez un bonus de +2 pour contraindre."
+ }
]
}
],
@@ -286,6 +447,12 @@
"text": "Au prix d'un point de [[1. Règles/99. Annexes/3. Fatigue et repos#Fatigue temporaire|fatigue temporaire]], durant votre tour, les dégâts que vous infligerez avec une [[1. Règles/99. Annexes/4. Équipement#Les armes|arme standard]], [[1. Règles/99. Annexes/4. Équipement#Les armes lourdes|lourdes]] ou [[1. Règles/99. Annexes/4. Équipement#Les armes à deux mains|à deux mains]] vous permet de lancer un second dé de dégâts de votre arme. *Ce dé peut être doublé en cas de dégâts critique.*",
"disposable": false
}
+ ],
+ "features": [
+ {
+ "category": "misc",
+ "text": "Au prix d'un point de [[1. Règles/99. Annexes/3. Fatigue et repos#Fatigue temporaire|fatigue temporaire]], durant votre tour, les dégâts que vous infligerez avec une [[1. Règles/99. Annexes/4. Équipement#Les armes|arme standard]], [[1. Règles/99. Annexes/4. Équipement#Les armes lourdes|lourdes]] ou [[1. Règles/99. Annexes/4. Équipement#Les armes à deux mains|à deux mains]] vous permet de lancer un second dé de dégâts de votre arme. *Ce dé peut être doublé en cas de dégâts critique.*"
+ }
]
},
{
@@ -294,6 +461,12 @@
"text": "Après avoir pris un adversaire en tenaille, si un allié parvient à le toucher, vous obtenez également un [[1. Règles/1. Introduction/2. Glossaire#Avantage et désavantage|avantage]] sur votre **première** attaque contre cet adversaire.",
"disposable": false
}
+ ],
+ "features": [
+ {
+ "category": "misc",
+ "text": "Après avoir pris un adversaire en tenaille, si un allié parvient à le toucher, vous obtenez également un [[1. Règles/1. Introduction/2. Glossaire#Avantage et désavantage|avantage]] sur votre **première** attaque contre cet adversaire."
+ }
]
},
{
@@ -303,7 +476,14 @@
"disposable": true
}
],
- "mastery": "shield"
+ "features": [
+ {
+ "category": "value",
+ "type": "add",
+ "property": "mastery.shield",
+ "value": 1
+ }
+ ]
}
],
"10": [
@@ -313,6 +493,12 @@
"text": "Au prix d'un point de [[1. Règles/99. Annexes/3. Fatigue et repos#Fatigue persistante|fatigue persistante]], durant tout votre tour, vous obtenez un bonus de +4 pour frapper avec une [[1. Règles/99. Annexes/4. Équipement#Les armes|arme standard]], [[1. Règles/99. Annexes/4. Équipement#Les armes lourdes|lourdes]] ou [[1. Règles/99. Annexes/4. Équipement#Les armes à deux mains|à deux mains]].",
"disposable": false
}
+ ],
+ "features": [
+ {
+ "category": "misc",
+ "text": "Au prix d'un point de [[1. Règles/99. Annexes/3. Fatigue et repos#Fatigue persistante|fatigue persistante]], durant tout votre tour, vous obtenez un bonus de +4 pour frapper avec une [[1. Règles/99. Annexes/4. Équipement#Les armes|arme standard]], [[1. Règles/99. Annexes/4. Équipement#Les armes lourdes|lourdes]] ou [[1. Règles/99. Annexes/4. Équipement#Les armes à deux mains|à deux mains]]."
+ }
]
},
{
@@ -321,6 +507,12 @@
"text": "Lorsque vous frappez en utilisant la [[1. Règles/2. L'entrainement/1. Entrainement#La force|force]], faire un 11 sur le lancer de d12 est considéré comme un coup critique. Cependant, vous subissez un malus de 1 point pour frapper.",
"disposable": false
}
+ ],
+ "features": [
+ {
+ "category": "misc",
+ "text": "Lorsque vous frappez en utilisant la [[1. Règles/2. L'entrainement/1. Entrainement#La force|force]], faire un 11 sur le lancer de d12 est considéré comme un coup critique. Cependant, vous subissez un malus de 1 point pour frapper."
+ }
]
},
{
@@ -329,6 +521,12 @@
"text": "Parer une attaque au corps à corps permet à **un seul** allié de saisir l'opportunité pour l'attaquer (au corps à corps).",
"disposable": false
}
+ ],
+ "features": [
+ {
+ "category": "misc",
+ "text": "Parer une attaque au corps à corps permet à **un seul** allié de saisir l'opportunité pour l'attaquer (au corps à corps)."
+ }
]
}
],
@@ -339,6 +537,12 @@
"text": "En frappant avec une [[1. Règles/99. Annexes/4. Équipement#Les armes|arme standard]], [[1. Règles/99. Annexes/4. Équipement#Les armes lourdes|lourde]] ou [[1. Règles/99. Annexes/4. Équipement#Les armes à deux mains|à deux mains]], vous pouvez choisir de subir un malus de -4 pour infliger 8 points de dégâts supplémentaires. *A annoncer avant le lancer de dé.*",
"disposable": false
}
+ ],
+ "features": [
+ {
+ "category": "misc",
+ "text": "En frappant avec une [[1. Règles/99. Annexes/4. Équipement#Les armes|arme standard]], [[1. Règles/99. Annexes/4. Équipement#Les armes lourdes|lourde]] ou [[1. Règles/99. Annexes/4. Équipement#Les armes à deux mains|à deux mains]], vous pouvez choisir de subir un malus de -4 pour infliger 8 points de dégâts supplémentaires. *A annoncer avant le lancer de dé.*"
+ }
]
},
{
@@ -347,6 +551,12 @@
"text": "Lorsque ce n'est pas votre tour, vous pouvez [[1. Règles/3. Le combat/2. Actions en combat#Saisir une opportunité|saisir l'opportunité]] pour frapper un adversaire lorsqu'il se déplace pour esquiver.",
"disposable": false
}
+ ],
+ "features": [
+ {
+ "category": "misc",
+ "text": "Lorsque ce n'est pas votre tour, vous pouvez [[1. Règles/3. Le combat/2. Actions en combat#Saisir une opportunité|saisir l'opportunité]] pour frapper un adversaire lorsqu'il se déplace pour esquiver."
+ }
]
},
{
@@ -355,6 +565,12 @@
"text": "En prenant en tenaille un adversaire, vous offrez l'[[1. Règles/1. Introduction/2. Glossaire#Avantage et désavantage|avantage]] aux attaques à distance et aux [[1. Règles/4. La magie/1. Magie#Les sorts de précision|sorts de précision]].",
"disposable": false
}
+ ],
+ "features": [
+ {
+ "category": "misc",
+ "text": "En prenant en tenaille un adversaire, vous offrez l'[[1. Règles/1. Introduction/2. Glossaire#Avantage et désavantage|avantage]] aux attaques à distance et aux [[1. Règles/4. La magie/1. Magie#Les sorts de précision|sorts de précision]]."
+ }
]
}
],
@@ -365,6 +581,12 @@
"text": "Vous êtes capable de tenir une [[1. Règles/99. Annexes/4. Équipement#Les armes à deux mains|arme à deux mains]] dans une seule main. Vous ne pouvez cependant pas tenir d'arme dans votre autre main, *même en ayant progressé dans l'[[1. Règles/99. Annexes/1. Les évolutions de valeur.canvas#Les armes multiples|arbre des armes multiples]]*.",
"disposable": true
}
+ ],
+ "features": [
+ {
+ "category": "misc",
+ "text": "Vous êtes capable de tenir une [[1. Règles/99. Annexes/4. Équipement#Les armes à deux mains|arme à deux mains]] dans une seule main. Vous ne pouvez cependant pas tenir d'arme dans votre autre main, *même en ayant progressé dans l'[[1. Règles/99. Annexes/1. Les évolutions de valeur.canvas#Les armes multiples|arbre des armes multiples]]*."
+ }
]
},
{
@@ -373,14 +595,26 @@
"text": "Au prix d'un point de [[1. Règles/99. Annexes/3. Fatigue et repos#Fatigue temporaire|fatigue temporaire]], durant tout un tour, faire une attaque ne demande que 1 point d'action.",
"disposable": false
}
+ ],
+ "features": [
+ {
+ "category": "misc",
+ "text": "Au prix d'un point de [[1. Règles/99. Annexes/3. Fatigue et repos#Fatigue temporaire|fatigue temporaire]], durant tout un tour, faire une attaque ne demande que 1 point d'action."
+ }
]
},
{
"description": [
{
"text": "Vous pouvez frapper, puis vous [[1. Règles/3. Le combat/2. Actions en combat#S'interposer|interposer]] en 3 points d'action.",
- "disposable": false,
- "category": "action"
+ "disposable": false
+ }
+ ],
+ "features": [
+ {
+ "category": "action",
+ "text": "Vous pouvez frapper, puis vous [[1. Règles/3. Le combat/2. Actions en combat#S'interposer|interposer]] en 3 points d'action.",
+ "cost": 3
}
]
}
@@ -392,7 +626,8 @@
"text": "En tenant une [[1. Règles/99. Annexes/4. Équipement#Les armes|arme standard]], [[1. Règles/99. Annexes/4. Équipement#Les armes lourdes|lourdes]] ou [[1. Règles/99. Annexes/4. Équipement#Les armes à deux mains|à deux mains]], vous gagnez un bonus de +3 pour résister aux désarmement. De plus, lorsque l'on rate à vous contraindre au corps à corps, vous pouvez gratuitement contre attaquer avec votre poing *même si vous tenez une arme*.",
"disposable": false
}
- ]
+ ],
+ "features": []
},
{
"description": [
@@ -400,7 +635,8 @@
"text": "En infligeant des [[1. Règles/3. Le combat/1. Combat#Réussite critique|dégâts critique]], vous pouvez choisir de doubler les dégâts fixes.",
"disposable": false
}
- ]
+ ],
+ "features": []
},
{
"description": [
@@ -408,7 +644,8 @@
"text": "Vous êtes capable de tenir un [[1. Règles/99. Annexes/4. Équipement#Les boucliers à deux mains|bouclier à deux mains]] dans une seule main. Vous pouvez **au mieux** tenir une [[1. Règles/99. Annexes/4. Équipement#Les armes légères|arme légère]] ou [[1. Règles/99. Annexes/4. Équipement#Les armes de jet|de jet]] dans l'autre main.",
"disposable": false
}
- ]
+ ],
+ "features": []
}
],
"14": [
@@ -418,7 +655,8 @@
"text": "",
"disposable": false
}
- ]
+ ],
+ "features": []
},
{
"description": [
@@ -426,7 +664,8 @@
"text": "Vous pouvez, jusqu'au début de votre prochain tour, réduire votre défense à 5. Cependant, si vous êtes frappé par une attaque au corps à corps, vous pouvez immédiatement contre attaquer **gratuitement** avec un [[1. Règles/1. Introduction/2. Glossaire#Avantage et désavantage absolu|avantage absolu]].",
"disposable": false
}
- ]
+ ],
+ "features": []
},
{
"description": [
@@ -435,7 +674,8 @@
"disposable": false,
"category": "reaction"
}
- ]
+ ],
+ "features": []
}
],
"15": [
@@ -445,7 +685,8 @@
"text": "",
"disposable": false
}
- ]
+ ],
+ "features": []
},
{
"description": [
@@ -453,7 +694,8 @@
"text": "Votre érudition du combat est légendaire. Vous êtes capable en [[1. Règles/3. Le combat/2. Actions en combat#Communiquer|communiquant]] d'offrir un bonus de +3 à un allié que vous voyez attaquer.",
"disposable": false
}
- ]
+ ],
+ "features": []
},
{
"description": [
@@ -461,7 +703,8 @@
"text": "Lorsque vous parez passivement, vous réduisez les dégâts d'un montant égal à votre bonus de parade passive.",
"disposable": false
}
- ]
+ ],
+ "features": []
}
]
},
@@ -479,7 +722,8 @@
"disposable": false,
"replaced": true
}
- ]
+ ],
+ "features": []
}
],
"1": [
@@ -500,7 +744,8 @@
"disposable": false,
"replaced": true
}
- ]
+ ],
+ "features": []
}
],
"2": [
@@ -516,7 +761,8 @@
"disposable": false,
"replaced": true
}
- ]
+ ],
+ "features": []
}
],
"3": [
@@ -532,7 +778,8 @@
"replaced": true
}
],
- "defense": ["activedodge"]
+ "defense": ["activedodge"],
+ "features": []
}
],
"4": [
@@ -551,7 +798,8 @@
"disposable": true
}
],
- "defense": ["passivedodge", "activedodge"]
+ "defense": ["passivedodge", "activedodge"],
+ "features": []
}
],
"5": [
@@ -561,7 +809,8 @@
"text": "Vous pouvez utiliser la dextérité pour frapper avec une [[1. Règles/99. Annexes/4. Équipement#Les armes naturelles|arme naturelle]]. Une arme naturelle est considéré comme une [[1. Règles/99. Annexes/4. Équipement#Les armes|arme]] et bénéficie des mêmes bonus.",
"disposable": false
}
- ]
+ ],
+ "features": []
},
{
"description": [
@@ -570,7 +819,8 @@
"disposable": true
}
],
- "mastery": "dexterity"
+ "mastery": "dexterity",
+ "features": []
},
{
"description": [
@@ -578,7 +828,8 @@
"text": "Vous progressez dans l'arbre des [[1. Règles/99. Annexes/1. Les évolutions de valeur.canvas#Les armes multiples|armes multiples]].",
"disposable": true
}
- ]
+ ],
+ "features": []
}
],
"6": [
@@ -593,7 +844,8 @@
"disposable": false
}
],
- "initiative": 1
+ "initiative": 1,
+ "features": []
},
{
"description": [
@@ -602,7 +854,8 @@
"disposable": true
}
],
- "mastery": "dexterity"
+ "mastery": "dexterity",
+ "features": []
},
{
"description": [
@@ -611,7 +864,8 @@
"disposable": true
}
],
- "resistance": [["precision", "defense"]]
+ "resistance": [["precision", "defense"]],
+ "features": []
}
],
"7": [
@@ -626,7 +880,8 @@
"disposable": true
}
],
- "defense": ["passivedodge", "activedodge"]
+ "defense": ["passivedodge", "activedodge"],
+ "features": []
},
{
"description": [
@@ -638,7 +893,8 @@
"text": "",
"disposable": false
}
- ]
+ ],
+ "features": []
},
{
"description": [
@@ -646,7 +902,8 @@
"text": "Frapper un ennemi au corps à corps dans le dos applique les même bonus que la [[1. Règles/3. Le combat/2. Actions en combat#Prendre en tenaille|prise en tenaille]]. Si l'ennemi a déjà été pris en tenaille ce tour ci, vous pouvez ignorer l'[[1. Règles/99. Annexes/4. Équipement#Les armures lourdes|armure lourde]].",
"disposable": false
}
- ]
+ ],
+ "features": []
}
],
"8": [
@@ -656,7 +913,8 @@
"text": "Vous pouvez utiliser 1 point d'action pour vous concentrer et viser, vous permettant de gagner un bonus de +2 pour votre prochaine attaque avec une [[1. Règles/99. Annexes/4. Équipement#Les armes à projectiles|arme à projectiles]], une [[1. Règles/99. Annexes/4. Équipement#Les armes de jet|arme de jet]] ou avec un [[1. Règles/4. La magie/1. Magie#Les sorts de précision|sort de précision]].",
"disposable": false
}
- ]
+ ],
+ "features": []
},
{
"description": [
@@ -664,7 +922,8 @@
"text": "Utiliser une [[1. Règles/99. Annexes/4. Équipement#Les armes à projectiles|arme à projectiles]] ou un [[1. Règles/4. La magie/1. Magie#Les sorts de précision|sort de précision]] au corps à corps ne provoque plus de [[1. Règles/1. Introduction/2. Glossaire#Avantage et désavantage|désavantage]].",
"disposable": false
}
- ]
+ ],
+ "features": []
},
{
"description": [
@@ -672,7 +931,8 @@
"text": "En frappant un adversaire en étant [[1. Règles/99. Annexes/6. Visibilité et lumière#Caché|caché]], vous pouvez subir un malus de -5 et déclencher un [[1. Règles/3. Le combat/1. Combat#Réussite critique|dégât critique]] si vous touchez.",
"disposable": false
}
- ]
+ ],
+ "features": []
}
],
"9": [
@@ -682,7 +942,8 @@
"text": "",
"disposable": false
}
- ]
+ ],
+ "features": []
},
{
"description": [
@@ -691,7 +952,8 @@
"disposable": true
}
],
- "resistance": [["precision", "attack"]]
+ "resistance": [["precision", "attack"]],
+ "features": []
},
{
"description": [
@@ -699,7 +961,8 @@
"text": "Vous progressez dans l'arbre des [[1. Règles/99. Annexes/1. Les évolutions de valeur.canvas#Les armes multiples|armes multiples]].",
"disposable": true
}
- ]
+ ],
+ "features": []
}
],
"10": [
@@ -715,7 +978,8 @@
}
],
"defense": ["activedodge"],
- "initiative": 1
+ "initiative": 1,
+ "features": []
},
{
"description": [
@@ -723,7 +987,8 @@
"text": "En attaquant avec une [[1. Règles/99. Annexes/4. Équipement#Les armes à projectiles|arme à projectiles]], vous pouvez choisir de subir un malus de -4 pour infliger 8 points de dégâts supplémentaires. *A annoncer avant le lancer de dé.*",
"disposable": false
}
- ]
+ ],
+ "features": []
},
{
"description": [
@@ -732,7 +997,8 @@
"disposable": true
}
],
- "resistance": [["bleed", "attack"]]
+ "resistance": [["bleed", "attack"]],
+ "features": []
}
],
"11": [
@@ -748,7 +1014,8 @@
}
],
"defense": ["passivedodge"],
- "resistance": [["precision", "defense"]]
+ "resistance": [["precision", "defense"]],
+ "features": []
},
{
"description": [
@@ -760,7 +1027,8 @@
"text": "vous pouvez tirer 2 projectiles en une attaque. Applique les règles de l'.",
"disposable": false
}
- ]
+ ],
+ "features": []
},
{
"description": [
@@ -768,7 +1036,8 @@
"text": "Votre malus d'attaque avec des armes multiples est réduit de 1.",
"disposable": false
}
- ]
+ ],
+ "features": []
}
],
"12": [
@@ -778,7 +1047,8 @@
"text": "Vous êtes capable d'esquiver passivement même lorsque vous êtes [[1. Règles/99. Annexes/2. Liste des effets#Agrippé|agrippé]]. Vous êtes capable d'esquiver activement sans vous déplacer.",
"disposable": false
}
- ]
+ ],
+ "features": []
},
{
"description": [
@@ -787,7 +1057,8 @@
"disposable": true
}
],
- "resistance": [["precision", "attack"]]
+ "resistance": [["precision", "attack"]],
+ "features": []
},
{
"description": [
@@ -795,7 +1066,8 @@
"text": "",
"disposable": false
}
- ]
+ ],
+ "features": []
}
],
"13": [
@@ -806,7 +1078,8 @@
"disposable": false,
"category": "reaction"
}
- ]
+ ],
+ "features": []
},
{
"description": [
@@ -814,7 +1087,8 @@
"text": "",
"disposable": false
}
- ]
+ ],
+ "features": []
},
{
"description": [
@@ -822,7 +1096,8 @@
"text": "Lorsque vous parvenez à parer activement avec une [[1. Règles/99. Annexes/4. Équipement#Les armes légères|arme légère]], vous pouvez gratuitement attaquer **une seule fois** avec cette même arme.",
"disposable": false
}
- ]
+ ],
+ "features": []
}
],
"14": [
@@ -833,7 +1108,8 @@
"disposable": true
}
],
- "defense": ["activedodge"]
+ "defense": ["activedodge"],
+ "features": []
},
{
"description": [
@@ -841,7 +1117,8 @@
"text": "",
"disposable": false
}
- ]
+ ],
+ "features": []
},
{
"description": [
@@ -849,7 +1126,8 @@
"text": "Vous progressez dans l'arbre des [[1. Règles/99. Annexes/1. Les évolutions de valeur.canvas#Les armes multiples|armes multiples]].",
"disposable": true
}
- ]
+ ],
+ "features": []
}
],
"15": [
@@ -859,7 +1137,8 @@
"text": "Vous ne pouvez plus esquiver activement. Tant que vous portez au mieux une [[1. Règles/99. Annexes/4. Équipement#Les armures légères|armure légère]], votre esquive passive est égale à votre esquive active.",
"disposable": false
}
- ]
+ ],
+ "features": []
},
{
"description": [
@@ -868,7 +1147,8 @@
"disposable": false,
"category": "action"
}
- ]
+ ],
+ "features": []
},
{
"description": [
@@ -876,7 +1156,8 @@
"text": "Au prix d'un point de [[1. Règles/99. Annexes/3. Fatigue et repos#Fatigue temporaire|fatigue temporaire]], votre malus d'attaque avec des armes multiples est réduit de 1 jusqu'à la fin de votre tour.",
"disposable": false
}
- ]
+ ],
+ "features": []
}
]
},
@@ -895,7 +1176,8 @@
"disposable": false,
"replaced": true
}
- ]
+ ],
+ "features": []
}
],
"1": [
@@ -907,7 +1189,8 @@
"disposable": false,
"replaced": true
}
- ]
+ ],
+ "features": []
}
],
"2": [
@@ -923,7 +1206,8 @@
"text": "+3 PV max.",
"disposable": true
}
- ]
+ ],
+ "features": []
}
],
"3": [
@@ -939,7 +1223,8 @@
"text": "+2 PV max.",
"disposable": true
}
- ]
+ ],
+ "features": []
}
],
"4": [
@@ -950,7 +1235,8 @@
"text": "+6 PV max.",
"disposable": true
}
- ]
+ ],
+ "features": []
}
],
"5": [
@@ -1594,6 +1880,14 @@
"text": "Vous apprenez le sort unique [[1. Règles/4. La magie/2. Liste des sorts#^484fc3|Dévastation]].",
"disposable": false
}
+ ],
+ "features": [
+ {
+ "category": "asset",
+ "type": "add",
+ "kind": "spells",
+ "asset": "special-1"
+ }
]
}
],
@@ -1652,7 +1946,7 @@
{
"description": [
{
- "text": "Vous apprenez le sort unique [[1. Règles/4. La magie/2. Liste des sorts#^73b8bd|Focalisation destructrice]].",
+ "text": "Vous apprenez le sort unique [[1. Règles/4. La magie/2. Liste des sorts#^73b8bd|Soin]].",
"disposable": false
}
]
@@ -3596,6 +3890,23 @@
"Dégats"
],
"id": "48"
+ },
+ {
+ "name": "Dévastation elementaire",
+ "rank": 4,
+ "type": "precision",
+ "cost": 8,
+ "speed": "action",
+ "elements": [
+ "fire",
+ "ice",
+ "thunder"
+ ],
+ "effect": "Faites un jet d'attaque soit la [[1. Entrainement#Dextérité|dextérité]], soit l'[[1. Entrainement#L'intelligence|intelligence]], soit la [[1. Entrainement#La psyché|psyché]]. Inflige 10+3d10 dégâts. Si vous attaquez avec la dextérité, vous infligez des dégâts de feu. Si vous attaquez avec l'intelligence, vous infligez des dégâts de glace et si vous attaquez avec la psyché, vous faites des dégâts de foudre.",
+ "tags": [
+ "Dégats"
+ ],
+ "id": "special-1"
}
]
}
\ No newline at end of file
diff --git a/types/character.ts b/types/character.ts
index 46f330f..03e2106 100644
--- a/types/character.ts
+++ b/types/character.ts
@@ -38,18 +38,18 @@ export const mainStatTexts: Record = {
"curiosity": "Curiosité",
"charisma": "Charisme",
"psyche": "Psyché",
-}
+};
export const elementTexts: Record = {
- fire: { class: 'text-light-red dark:text-dark-red', text: 'Feu' },
- ice: { class: 'text-light-blue dark:text-dark-blue', text: 'Glace' },
- thunder: { class: 'text-light-yellow dark:text-dark-yellow', text: 'Foudre' },
- earth: { class: 'text-light-orange dark:text-dark-orange', text: 'Terre' },
- arcana: { class: 'text-light-purple dark:text-dark-purple', text: 'Arcane' },
- air: { class: 'text-light-green dark:text-dark-green', text: 'Air' },
- nature: { class: 'text-light-green dark:text-dark-green', text: 'Nature' },
- light: { class: 'text-light-yellow dark:text-dark-yellow', text: 'Lumière' },
- psyche: { class: 'text-light-purple dark:text-dark-purple', text: 'Psy' },
-}
+ fire: { class: 'text-light-red dark:text-dark-red border-light-red dark:border-dark-red bg-light-red dark:bg-dark-red', text: 'Feu' },
+ ice: { class: 'text-light-blue dark:text-dark-blue border-light-blue dark:border-dark-blue bg-light-blue dark:bg-dark-blue', text: 'Glace' },
+ thunder: { class: 'text-light-yellow dark:text-dark-yellow border-light-yellow dark:border-dark-yellow bg-light-yellow dark:bg-dark-yellow', text: 'Foudre' },
+ earth: { class: 'text-light-orange dark:text-dark-orange border-light-orange dark:border-dark-orange bg-light-orange dark:bg-dark-orange', text: 'Terre' },
+ arcana: { class: 'text-light-indigo dark:text-dark-indigo border-light-indigo dark:border-dark-indigo bg-light-indigo dark:bg-dark-indigo', text: 'Arcane' },
+ air: { class: 'text-light-lime dark:text-dark-lime border-light-lime dark:border-dark-lime bg-light-lime dark:bg-dark-lime', text: 'Air' },
+ nature: { class: 'text-light-green dark:text-dark-green border-light-green dark:border-dark-green bg-light-green dark:bg-dark-green', text: 'Nature' },
+ light: { class: 'text-light-yellow dark:text-dark-yellow border-light-yellow dark:border-dark-yellow bg-light-yellow dark:bg-dark-yellow', text: 'Lumière' },
+ psyche: { class: 'text-light-purple dark:text-dark-purple border-light-purple dark:border-dark-purple bg-light-purple dark:bg-dark-purple', text: 'Psy' },
+};
export const spellTypeTexts: Record = { "instinct": "Instinct", "knowledge": "Savoir", "precision": "Précision", "arts": "Oeuvres" };
export const CharacterValidation = z.object({
@@ -79,7 +79,7 @@ export const CharacterValidation = z.object({
username: z.string().optional(),
visibility: z.enum(["public", "private"]),
thumbnail: z.any(),
-})
+});
export type Character = {
id: number;
@@ -115,7 +115,7 @@ export type CharacterConfig = {
export type SpellConfig = {
id: string;
name: string;
- rank: 1 | 2 | 3;
+ rank: 1 | 2 | 3 | 4;
type: SpellType;
cost: number;
speed: "action" | "reaction" | number;
@@ -149,31 +149,29 @@ export type RaceOption = {
abilities?: number;
spellslots?: number;
};
-export type Feature = {
- text?: string;
-} & (ActionFeature | ReactionFeature | FreeActionFeature | BonusFeature | MiscFeature);
-type ActionFeature = {
- type: "action";
+export type FeatureItem = {
+ category: "freeaction" | "misc";
+ text: string;
+} | {
+ category: "action" | "reaction";
cost: 1 | 2 | 3;
text: string;
-};
-type ReactionFeature = {
- type: "reaction";
- text: string;
-};
-type FreeActionFeature = {
- type: "freeaction";
- text: string;
-};
-type BonusFeature = {
- type: "bonus";
- action: "add" | "remove" | "set" | "cap";
- value: number;
+} | {
+ category: "value";
+ type: "add" | "remove" | "set";
+ value: number | false;
property: string;
-};
-type MiscFeature = {
- type: "misc";
- text: string;
+} | {
+ category: "asset";
+ type: "add" | "remove";
+ kind: "spells";
+ asset: string;
+}
+export type Feature = {
+ id: number;
+ name?: string
+ text?: string;
+ list?: FeatureItem[];
};
export type TrainingOption = {
description: Array<{
@@ -192,16 +190,16 @@ export type TrainingOption = {
spellrank?: SpellType;
defense?: Array;
resistance?: [Resistance, "attack" | "defense"][];
- spell?: string;
+ spell?: string;
//Used during character creation, not used by compiler
modifier?: number;
ability?: number;
- spec?: number;
+ spec?: number;
spellslot?: number | MainStat;
arts?: number | MainStat;
- features?: Feature[]; //TODO
+ features?: FeatureItem[]; //TODO
};
export type CompiledCharacter = {
id: number;
@@ -222,6 +220,7 @@ export type CompiledCharacter = {
values: CharacterValues,
defense: {
+ hardcap: number;
static: number;
activeparry: number;
activedodge: number;
@@ -238,6 +237,7 @@ export type CompiledCharacter = {
magicpower: number;
magicspeed: number;
magicelement: number;
+ magicinstinct: number;
};
//First is attack, second is defense