1
0
Fork 0
mirror of https://github.com/em-squared/5e-drs.git synced 2025-10-29 04:34:19 +00:00

outils de création de sorts/monstres/objets

This commit is contained in:
Maxime Moraine 2020-04-22 11:17:05 +02:00
parent 6237f0e6a2
commit a6986c42c6
48 changed files with 2743 additions and 259 deletions

View file

@ -89,17 +89,17 @@ module.exports = {
hostname: 'https://heros-et-dragons.fr'
}
],
[
'@vuepress/pwa',
{
serviceWorker: true,
updatePopup: true,
generateSWConfig: {
cacheId: 'hddrs',
globIgnores: ['**/*.js', '**/*.css', '**/*.html']
}
}
],
// [
// '@vuepress/pwa',
// {
// serviceWorker: true,
// updatePopup: true,
// generateSWConfig: {
// cacheId: 'hddrs',
// globIgnores: ['**/*.js', '**/*.css', '**/*.html']
// }
// }
// ],
[
'seo',
{
@ -117,10 +117,16 @@ module.exports = {
// modifiedAt: $page => $page.lastUpdated && new Date($page.lastUpdated),
}
],
// [
// '@vuepress/google-analytics',
// {
// 'ga': '' // UA-00000000-0
// }
// ]
],
// extendPageData ($page) {
// $page.rawContent = ($page._strippedContent)
// },
extendPageData ($page) {
$page.rawContent = ($page._strippedContent)
},
markdown: {
anchor: {
permalinkBefore: false,
@ -135,6 +141,44 @@ module.exports = {
})
}
},
additionalPages: [
{
path: '/mon-grimoire/',
frontmatter: {
layout: 'MySpellsLayout'
}
},
{
path: '/mon-bestiaire/',
frontmatter: {
layout: 'MyMonstersLayout'
}
},
{
path: '/mes-objets-magiques/',
frontmatter: {
layout: 'MyMagicItemsLayout'
}
},
{
path: '/creation-de-sort/',
frontmatter: {
layout: 'CreateSpellLayout'
}
},
{
path: '/creation-de-monstre-pnj/',
frontmatter: {
layout: 'CreateMonsterLayout'
}
},
{
path: '/creation-d-objet-magique/',
frontmatter: {
layout: 'CreateMagicItemLayout'
}
}
],
themeConfig: {
domain: 'https://staging.heros-et-dragons.fr',
repository: 'https://github.com/em-squared/heros-et-dragons-drs',
@ -150,7 +194,7 @@ module.exports = {
sidebar: [
{
title: 'Pour les joueurs',
expanded: true,
icon: 'mdi-book-open-variant',
children: [
{
title: "Création du personnage",
@ -426,6 +470,7 @@ module.exports = {
},
{
title: 'Pour les meneurs',
icon: 'mdi-map',
children: [
{
title: "Créatures & oppositions",
@ -487,6 +532,46 @@ module.exports = {
]
},
{type: 'divider'},
{
title: 'Bibliothèques',
icon: 'mdi-book',
children: [
{
title: 'Mon grimoire',
path: '/mon-grimoire/',
badge: 'mySpells'
},
{
title: 'Mon bestiaire',
path: '/mon-bestiaire/',
badge: 'myMonsters'
},
{
title: 'Mes objets magiques',
path: '/mes-objets-magiques/',
badge: 'myMagicItems'
}
]
},
{
title: 'Outils de création',
icon: 'mdi-tools',
children: [
{
title: 'Création de sort',
path: '/creation-de-sort/'
},
{
title: 'Création de monstre/PNJ',
path: '/creation-de-monstre-pnj/'
},
{
title: "Création d'objet magique",
path: '/creation-d-objet-magique/'
},
]
},
{type: 'divider'},
{
title: "Licence OGL",
path: '/licence-ogl/',

View file

@ -12,3 +12,22 @@ export const armorTypes = {
"clibanion": { value: 17, maxDex: 0 },
"harnois": { value: 18, maxDex: 0 },
}
export const ARMORTYPES = [
{ label: "Aucune", value: null },
{ label: "Armure du mage", value: 'armure du mage' },
{ label: "Armure naturelle", value: 'armure naturelle' },
{ label: "Armure matelassée", value: 'armure matelassée' },
{ label: "Armure de cuir", value: 'armure de cuir' },
{ label: "Armure de cuir clouté", value: 'armure de cuir clouté' },
{ label: "Armure de peau", value: 'armure de peau' },
{ label: "Chemise de maille", value: 'chemise de maille' },
{ label: "Armure d'écailles", value: "armure d'écailles" },
{ label: "Cuirasse", value: 'cuirasse' },
{ label: "Demi-plate", value: 'demi-plate' },
{ label: "Armure annelée", value: 'armure annelée' },
{ label: "Cotte de mailles", value: 'cotte de mailles' },
{ label: "Clibanion", value: 'clibanion' },
{ label: "Harnois", value: 'harnois' },
{ label: "Autre", value: 'custom' },
]

View file

@ -0,0 +1,11 @@
export const CLASSES = [
"Barde",
"Clerc",
"Druide",
"Ensorceleur/Sorcelame",
"Magicien",
"Ombrelame",
"Paladin",
"Rôdeur",
"Sorcier",
]

View file

@ -0,0 +1,17 @@
export const CONDITIONS = [
{ text: "À terre", value:'a-terre' },
{ text: "Assourdi", value: 'assourdi' },
{ text: "Aveuglé", value: 'aveugle' },
{ text: "Charmé", value: 'charme' },
{ text: "Épuisé", value: 'epuise' },
{ text: "Empoigné", value: 'empoigne' },
{ text: "Empoisonné", value: 'empoisonne' },
{ text: "Entravé", value: 'entrave' },
{ text: "Étourdi", value: 'etourdi' },
{ text: "Inconscient", value: 'inconscient' },
{ text: "Invisible", value: 'invisible' },
{ text: "Neutralisé", value: 'neutralise' },
{ text: "Paralysé", value: 'paralyse' },
{ text: "Pétrifié", value: 'petrifie' },
{ text: "Terrorisé", value: 'terrorise' },
]

View file

@ -0,0 +1,36 @@
export const DAMAGETYPES = [
{ text: "contondants, perforants et tranchants infligés par des attaques non-magiques", value: 'c-p-t-non-magique' },
{ text: "contondants, perforants et tranchants infligés par des attaques non-magiques qui ne sont pas en argent", value: 'c-p-t-non-argent' },
{ text: "contondants, perforants et tranchants infligés par des attaques non-magiques qui ne sont pas en adamantite", value: 'c-p-t-non-adamantite' },
{ text: "contondants et perforants infligés par des attaques non-magiques qui ne sont pas en argent", value: 'c-p-non-argent' },
{ text: "contondants et perforants infligés par des attaques non-magiques qui ne sont pas en adamantite", value: 'c-p-non-adamantite' },
{ text: "contondants et perforants infligés par des attaques non-magiques", value: 'c-p-non-magique' },
{ text: "contondants et tranchants infligés par des attaques non-magiques qui ne sont pas en argent", value: 'c-t-non-argent' },
{ text: "contondants et tranchants infligés par des attaques non-magiques qui ne sont pas en adamantite", value: 'c-t-non-adamantite' },
{ text: "contondants et tranchants infligés par des attaques non-magiques", value: 'c-t-non-magique' },
{ text: "perforants et tranchants infligés par des attaques non-magiques qui ne sont pas en argent", value: 'p-t-non-argent' },
{ text: "perforants et tranchants infligés par des attaques non-magiques qui ne sont pas en adamantite", value: 'p-t-non-adamantite' },
{ text: "perforants et tranchants infligés par des attaques non-magiques", value: 'p-t-non-magique' },
{ text: "tranchants infligés par des attaques non-magiques qui ne sont pas en argent", value: 't-non-argent' },
{ text: "tranchants infligés par des attaques non-magiques qui ne sont pas en adamantite", value: 't-non-adamantite' },
{ text: "tranchants infligés par des attaques non-magiques", value: 't-non-magique' },
{ text: "perforants infligés par des attaques non-magiques qui ne sont pas en argent", value: 'p-non-argent' },
{ text: "perforants infligés par des attaques non-magiques qui ne sont pas en adamantite", value: 'p-non-adamantite' },
{ text: "perforants infligés par des attaques non-magiques", value: 'p-non-magique' },
{ text: "contondants infligés par des attaques non-magiques qui ne sont pas en argent", value: 'c-non-argent' },
{ text: "contondants infligés par des attaques non-magiques qui ne sont pas en adamantite", value: 'c-non-adamantite' },
{ text: "contondants infligés par des attaques non-magiques", value: 'c-non-magique' },
{ text: "d'acide", value: 'acide' },
{ text: "contondants", value: 'contondant' },
{ text: "de feu", value: 'feu' },
{ text: "de force", value: 'force' },
{ text: "de foudre", value: 'foudre' },
{ text: "de froid", value: 'froid' },
{ text: "nécrotiques", value: 'necrotique' },
{ text: "perforants", value: 'perforant' },
{ text: "de poison", value: 'poison' },
{ text: "psychiques", value: 'psychique' },
{ text: "radiants", value: 'radiant' },
{ text: "de tonnerre", value: 'tonnerre' },
{ text: "tranchants", value: 'tranchant' },
]

View file

@ -0,0 +1,19 @@
export const LANGUAGES = [
{ text: 'Commun', value: 'commun' },
{ text: 'Elfe', value: 'elfe' },
{ text: 'Géant', value: 'géant' },
{ text: 'Gnome', value: 'gnome' },
{ text: 'Gobelin', value: 'gobelin' },
{ text: 'Halfelin', value: 'halfelin' },
{ text: 'Nain', value: 'nain' },
{ text: 'Orc', value: 'orc' },
{ text: 'Abyssal', value: 'abyssal' },
{ text: 'Céleste', value: 'céleste' },
{ text: 'Commun des profondeurs', value: 'commun des profondeurs' },
{ text: 'Draconique', value: 'draconique' },
{ text: 'Infernal', value: 'infernal' },
{ text: 'Primordial', value: 'primordial' },
{ text: 'Noir parler', value: 'noir parler' },
{ text: 'Sselish', value: 'sselish' },
{ text: 'Sylvestre', value: 'sylvestre' },
]

View file

@ -0,0 +1,20 @@
export const MAGICITEMTYPES = [
"Anneau",
"Arme",
"Armure",
"Baguette",
"Bâton",
"Objet merveilleux",
"Parchemin",
"Potion",
"Sceptre",
]
export const RARITIES = [
"Courant",
"Peu courant",
"Rare",
"Très rare",
"Légendaire",
"Artefact",
]

View file

@ -0,0 +1,92 @@
export const MONSTERTYPES = [
"Aberration",
"Bête",
"Céleste",
"Créature artificielle",
"Créature monstrueuse",
"Dragon",
"Élémentaire",
"Fées",
"Fiélon",
"Géant",
"Humanoide",
"Mort-vivant",
"Plante",
"Vase",
]
export const MONSTERSIZES = [
{ label: "Très petit", abbr: "TP", hitDie: 4 },
{ label: "Petit", abbr: "P", hitDie: 6 },
{ label: "Moyen", abbr: "M", hitDie: 8 },
{ label: "Grand", abbr: "G", hitDie: 10 },
{ label: "Très grand", abbr: "TG", hitDie: 12 },
{ label: "Gigantesque", abbr: "Gig", hitDie: 20 },
]
export const CHALLENGES = [
{ label: "0", value:'0', xp: "0 ou 10" },
{ label: "1/8", value:'0.125', xp: 25 },
{ label: "1/4", value:'0.25', xp: 50 },
{ label: "1/2", value:'0.5', xp: 100 },
{ label: "1", value:'1', xp: 200 },
{ label: "2", value:'2', xp: 450 },
{ label: "3", value:'3', xp: 700 },
{ label: "4", value:'4', xp: 1100 },
{ label: "5", value:'5', xp: 1800 },
{ label: "6", value:'6', xp: 2300 },
{ label: "7", value:'7', xp: 2900 },
{ label: "8", value:'8', xp: 3900 },
{ label: "9", value:'9', xp: 5000 },
{ label: "10", value:'10', xp: 5900 },
{ label: "11", value:'11', xp: 7200 },
{ label: "12", value:'12', xp: 8400 },
{ label: "13", value:'13', xp: 10000 },
{ label: "14", value:'14', xp: 11500 },
{ label: "15", value:'15', xp: 13000 },
{ label: "16", value:'16', xp: 15000 },
{ label: "17", value:'17', xp: 18000 },
{ label: "18", value:'18', xp: 20000 },
{ label: "19", value:'19', xp: 22000 },
{ label: "20", value:'20', xp: 25000 },
{ label: "21", value:'21', xp: 33000 },
{ label: "22", value:'22', xp: 41000 },
{ label: "23", value:'23', xp: 50000 },
{ label: "24", value:'24', xp: 62000 },
{ label: "25", value:'25', xp: 75000 },
{ label: "26", value:'26', xp: 90000 },
{ label: "27", value:'27', xp: 105000 },
{ label: "28", value:'28', xp: 120000 },
{ label: "29", value:'29', xp: 135000 },
{ label: "30", value:'30', xp: 155000 },
]
export const ABILITIES = [
{ label: 'Force', value: 'for', abbr: 'For' },
{ label: 'Dextérité', value: 'dex', abbr: 'Dex' },
{ label: 'Constitution', value: 'con', abbr: 'Con' },
{ label: 'Intelligence', value: 'int', abbr: 'Int' },
{ label: 'Sagesse', value: 'sag', abbr: 'Sag' },
{ label: 'Charisme', value: 'cha', abbr: 'Cha' },
]
export const SKILLS = [
{ label: "Acrobaties", value:"acrobaties", ability: "dex" },
{ label: "Arcanes", value:"arcanes", ability: "int" },
{ label: "Athlétisme", value:"athletisme", ability: "for" },
{ label: "Discrétion", value:"discretion", ability: "dex" },
{ label: "Dressage", value:"dressage", ability: "sag" },
{ label: "Escamotage", value:"escamotage", ability: "dex" },
{ label: "Histoire", value:"histoire", ability: "int" },
{ label: "Intimidation", value:"intimidation", ability: "cha" },
{ label: "Investigation", value:"investigation", ability: "int" },
{ label: "Médecine", value:"medecine", ability: "sag" },
{ label: "Nature", value:"nature", ability: "int" },
{ label: "Perception", value:"perception", ability: "sag" },
{ label: "Perspicacité", value:"perspicacite", ability: "sag" },
{ label: "Persuasion", value:"persuasion", ability: "cha" },
{ label: "Religion", value:"religion", ability: "int" },
{ label: "Représentation", value:"representation", ability: "cha" },
{ label: "Supercherie", value:"supercherie", ability: "cha" },
{ label: "Survie", value:"survie", ability: "sag" },
]

View file

@ -0,0 +1,23 @@
export const SPELLSCHOOLS = [
"Abjuration",
"Enchantement",
"Divination",
"Évocation",
"Illusion",
"Invocation",
"Nécromancie",
"Transmutation",
]
export const SPELLLEVELS = [
{ value: 0, label: "Tour de magie" },
{ value: 1, label: "1" },
{ value: 2, label: "2" },
{ value: 3, label: "3" },
{ value: 4, label: "4" },
{ value: 5, label: "5" },
{ value: 6, label: "6" },
{ value: 7, label: "7" },
{ value: 8, label: "8" },
{ value: 9, label: "9" },
]

View file

@ -1,19 +1,19 @@
export const stats = {
monsterTypes: {
"Aberration": { plural: "Aberrations", swarm: "d'aberrations" },
"Bête": { plural: "Bêtes", swarm: "de bêtes"},
"Céleste": { plural: "Célestes", swarm: "de célestes" },
"Créature artificielle": { plural: "Créatures artificielles", swarm: "de créatures artificielles"},
"Créature monstrueuse": { plural: "Créatures monstrueuses", swarm: "de créatures monstrueuses"},
"Dragon": { plural: "Dragons", swarm: "de dragons"},
"Élémentaire": { plural: "Élémentaires", swarm: "d'élémentaires"},
"Fées": { plural: "Fées", swarm: "de fées"},
"Fiélon": { plural: "Fiélons", swarm: "de fiélons"},
"Géant": { plural: "Géants", swarm: "de géants"},
"Humanoide": { plural: "Humanoïdes", swarm: "d'humanoïdes"},
"Mort-vivant": { plural: "Morts-vivants", swarm: "de morts-vivants"},
"Plante": { plural: "Plantes", swarm: "de plantes"},
"Vase": { plural: "Vases", swarm: "de vases"}
"Aberration": { plural: "Aberrations", swarm: "d'aberrations" , label: "Aberration"},
"Bête": { plural: "Bêtes", swarm: "de bêtes", label: "Bête"},
"Céleste": { plural: "Célestes", swarm: "de célestes" , label: "Céleste"},
"Créature artificielle": { plural: "Créatures artificielles", swarm: "de créatures artificielles", label: "Créature artificielle"},
"Créature monstrueuse": { plural: "Créatures monstrueuses", swarm: "de créatures monstrueuses", label: "Créature monstrueuse"},
"Dragon": { plural: "Dragons", swarm: "de dragons", label: "Dragon"},
"Élémentaire": { plural: "Élémentaires", swarm: "d'élémentaires", label: "Élémentaire"},
"Fées": { plural: "Fées", swarm: "de fées", label: "Fées"},
"Fiélon": { plural: "Fiélons", swarm: "de fiélons", label: "Fiélon"},
"Géant": { plural: "Géants", swarm: "de géants", label: "Géant"},
"Humanoide": { plural: "Humanoïdes", swarm: "d'humanoïdes", label: "Humanoide"},
"Mort-vivant": { plural: "Morts-vivants", swarm: "de morts-vivants", label: "Mort-vivant"},
"Plante": { plural: "Plantes", swarm: "de plantes", label: "Plante"},
"Vase": { plural: "Vases", swarm: "de vases", label: "Vase"},
},
abilities: {

View file

@ -5,6 +5,10 @@ import spellFilters from './modules/spellFilters'
import magicItemFilters from './modules/magicItemFilters'
import monsterFilters from './modules/monsterFilters'
import mySpells from './modules/mySpells'
import myMonsters from './modules/myMonsters'
import myMagicItems from './modules/myMagicItems'
Vue.use(Vuex)
import Cookies from 'js-cookie'
@ -13,7 +17,10 @@ export default new Vuex.Store({
modules: {
spellFilters,
magicItemFilters,
monsterFilters
monsterFilters,
mySpells,
myMonsters,
myMagicItems
},
state: {

View file

@ -0,0 +1,63 @@
import {sortByString} from '@theme/util/filterHelpers'
export default {
namespaced: true,
state: {
magicItems: [],
},
getters: {
magicItems: state => state.magicItems,
},
actions: {
reset: ({ commit }) => {
commit('resetMagicItems')
},
updateMagicItems: ({ commit }, payload) => {
commit('setMagicItems', payload)
},
addMagicItem: ({ commit }, payload) => {
commit('addMagicItem', payload)
},
removeMagicItem: ({ commit }, payload) => {
commit('removeMagicItem', payload)
},
},
mutations: {
initialiseStore (state) {
// Récupération des données utilisateurs depuis le navigateur
if(localStorage.getItem('myMagicItems') && localStorage.getItem('myMagicItems') !== undefined) {
let localStorageData = JSON.parse(localStorage.getItem('myMagicItems'))
state.magicItems = localStorageData.magicItems
}
},
setMagicItems: (state, payload) => {
state.magicItems = payload
},
resetMagicItems: (state) => {
state.magicItems = []
},
addMagicItem: (state, payload) => {
state.magicItems.push(payload)
state.magicItems.sort((a, b) => { return sortByString(a.title, b.title) })
},
updateMagicItem: (state, payload) => {
state.magicItems.forEach((magicItem, idx) => {
if (magicItem.key == payload.key) {
state.magicItems[idx] = payload
}
})
},
removeMagicItem: (state, payload) => {
state.magicItems.forEach((magicItem, idx) => {
if (magicItem.key == payload.key) {
state.magicItems.splice(idx, 1)
}
})
},
}
}

View file

@ -0,0 +1,63 @@
import {sortByString} from '@theme/util/filterHelpers'
export default {
namespaced: true,
state: {
monsters: [],
},
getters: {
monsters: state => state.monsters,
},
actions: {
reset: ({ commit }) => {
commit('resetMonsters')
},
updateMonsters: ({ commit }, payload) => {
commit('setMonsters', payload)
},
addMonster: ({ commit }, payload) => {
commit('addMonster', payload)
},
removeMonster: ({ commit }, payload) => {
commit('removeMonster', payload)
},
},
mutations: {
initialiseStore (state) {
// Récupération des données utilisateurs depuis le navigateur
if(localStorage.getItem('myMonsters') && localStorage.getItem('myMonsters') !== undefined) {
let localStorageData = JSON.parse(localStorage.getItem('myMonsters'))
state.monsters = localStorageData.monsters
}
},
setMonsters: (state, payload) => {
state.monsters = payload
},
resetMonsters: (state) => {
state.monsters = []
},
addMonster: (state, payload) => {
state.monsters.push(payload)
state.monsters.sort((a, b) => { return sortByString(a.title, b.title) })
},
updateMonster: (state, payload) => {
state.monsters.forEach((monster, idx) => {
if (monster.key == payload.key) {
state.monsters[idx] = payload
}
})
},
removeMonster: (state, payload) => {
state.monsters.forEach((monster, idx) => {
if (monster.key == payload.key) {
state.monsters.splice(idx, 1)
}
})
},
}
}

View file

@ -0,0 +1,63 @@
import {sortByString} from '@theme/util/filterHelpers'
export default {
namespaced: true,
state: {
spells: [],
},
getters: {
spells: state => state.spells,
},
actions: {
reset: ({ commit }) => {
commit('resetSpells')
},
updateSpells: ({ commit }, payload) => {
commit('setSpells', payload)
},
addSpell: ({ commit }, payload) => {
commit('addSpell', payload)
},
removeSpell: ({ commit }, payload) => {
commit('removeSpell', payload)
},
},
mutations: {
initialiseStore (state) {
// Récupération des données utilisateurs depuis le navigateur
if(localStorage.getItem('mySpells') && localStorage.getItem('mySpells') !== undefined) {
let localStorageData = JSON.parse(localStorage.getItem('mySpells'))
state.spells = localStorageData.spells
}
},
setSpells: (state, payload) => {
state.spells = payload
},
resetSpells: (state) => {
state.spells = []
},
addSpell: (state, payload) => {
state.spells.push(payload)
state.spells.sort((a, b) => { return sortByString(a.title, b.title) })
},
updateSpell: (state, payload) => {
state.spells.forEach((spell, idx) => {
if (spell.key == payload.key) {
state.spells[idx] = payload
}
})
},
removeSpell: (state, payload) => {
state.spells.forEach((spell, idx) => {
if (spell.key == payload.key) {
state.spells.splice(idx, 1)
}
})
},
}
}

View file

@ -43,6 +43,16 @@
crumbs.push({to: page.path, disabled: disabled, text: 'Bestiaire'})
} else if (page.path == '/liste-objets-magiques/') {
crumbs.push({to: page.path, disabled: disabled, text: 'Liste des objets magiques'})
} else if (page.path == '/mon-grimoire/') {
crumbs.push({to: page.path, disabled: disabled, text: 'Mon grimoire'})
} else if (page.path == '/mon-bestiaire/') {
crumbs.push({to: page.path, disabled: disabled, text: 'Mon bestiaire'})
} else if (page.path == '/mes-objets-magiques/') {
crumbs.push({to: page.path, disabled: disabled, text: 'Mes objets magiques'})
} else if (page.path == '/creation-de-sort/') {
crumbs.push({to: page.path, disabled: disabled, text: 'Création de sort'})
} else if (page.path == '/creation-de-monstre-pnj/') {
crumbs.push({to: page.path, disabled: disabled, text: 'Création de monstre ou PNJ'})
} else {
crumbs.push({to: page.path, disabled: disabled, text: page.frontmatter.breadcrumb || page.title})
}

View file

@ -2,13 +2,17 @@
<main class="page content">
<div class="theme-default-content">
<h1>{{ $page.title }}</h1>
<div class="magic-item-details">
<template v-if="!hideTitle">
<h1 v-if="!isList">{{ magicItem.title }}</h1>
<h2 v-else>{{ magicItem.title }}</h2>
</template>
<div class="magic-item-details title">
{{displayItemMeta()}}
</div>
</div>
<Content class="mt-4" />
<Content v-if="!magicItem.custom" :pageKey="magicItem.key" class="mt-4" />
<div v-else v-html="md.render(magicItem.content)" class="mt-4"></div>
<p v-if="$page.frontmatter.source" class="source">Source : <em>{{ $page.frontmatter.source }}</em></p>
@ -17,11 +21,22 @@
<script>
import {displayItemMeta} from '@theme/util/magicItemHelpers'
import MarkdownIt from 'markdown-it'
export default {
name: 'MagicItem',
data () {
return {
md: new MarkdownIt()
}
},
props: ['magicItem', 'isList', 'hideTitle'],
methods : {
displayItemMeta () {
return displayItemMeta(this.$page.frontmatter)
return displayItemMeta(this.magicItem.frontmatter)
}
}
}

View file

@ -0,0 +1,64 @@
<template>
<v-card class="d-flex flex-column mb-6 magic-item-card">
<v-card-title>
{{ magicItem.title }}
<v-spacer></v-spacer>
<v-card-actions v-if="showActions">
<v-btn dark icon class="d-print-none mr-2" link :to="{ path: '/creation-d-objet-magique/', query: { key: magicItem.key } }"><v-icon>mdi-pencil</v-icon></v-btn>
<v-btn dark icon class="d-print-none" @click="$store.commit('myMagicItems/removeMagicItem', magicItem)"><v-icon>mdi-delete</v-icon></v-btn>
</v-card-actions>
</v-card-title>
<v-card-text>
<div class="magic-item-details mt-3">
<div class="magic-item-rarity-attunement title" v-html="displayItemMeta()"></div>
</div>
<v-divider class="mt-3" />
<Content v-if="!magicItem.custom" :pageKey="magicItem.key" class="mt-4" />
<div v-else v-html="md.render(magicItem.content)" class="mt-4"></div>
<p v-if="magicItem.frontmatter.source" class="source">Source : <em>{{ magicItem.frontmatter.source }}</em></p>
</v-card-text>
</v-card>
</template>
<script>
import { displayItemMeta } from '@theme/util/magicItemHelpers'
import MarkdownIt from 'markdown-it'
export default {
name: 'MagicItemCard',
props: ['magicItem', 'showActions', 'isShort'],
data () {
return {
md: new MarkdownIt()
}
},
methods: {
displayItemMeta () {
return displayItemMeta(this.magicItem.frontmatter)
}
},
}
</script>
<style lang="scss">
@import '../styles/colors';
.magic-item-card {
.v-card__title {
background-color: $color-dragon;
color: #fff;
padding-top: 0;
padding-bottom: 0;
}
p.source {
margin-bottom: 0;
}
}
</style>

View file

@ -2,133 +2,140 @@
<main class="page content">
<div class="theme-default-content">
<h1>{{ $page.title }}</h1>
<div class="monster-type-size-alignment">
<template v-if="!hideTitle">
<h1 v-if="!isList">{{ monster.title }}</h1>
<h2 v-else>{{ monster.title }}</h2>
</template>
<div class="monster-type-size-alignment title">
{{ displayMonsterTypeSizeAlignment() }}
</div>
<div class="monster-details">
<!-- Statblock -->
<div class="monster-armor-class">
<strong>Classe d'armure</strong>
<span>{{ displayAC() }}</span>
</div>
<div class="monster-hit-points">
<strong>Points de vie</strong>
<span>{{ displayHP() }}</span>
</div>
<div class="monster-movement">
<strong>Vitesse</strong>
<template>{{ displayMovement() }}</template>
</div>
<div class="monster-ability-scores d-flex">
<div class="monster-ability-scores-physical d-flex">
<div class="ability-str text-center">
<div class="ability-label">
<strong>For</strong>
</div>
<div class="ability-score">
{{ displayAbilityScore(monster.abilityScores.for) }}
</div>
</div>
<div class="ability-dex text-center">
<div class="ability-label">
<strong>Dex</strong>
</div>
<div class="ability-score">
{{ displayAbilityScore(monster.abilityScores.dex) }}
</div>
</div>
<div class="ability-con text-center">
<div class="ability-label">
<strong>Con</strong>
</div>
<div class="ability-score">
{{ displayAbilityScore(monster.abilityScores.con) }}
</div>
</div>
<div class="break-avoid">
<!-- Statblock -->
<div class="monster-armor-class">
<strong>Classe d'armure</strong>
<span>{{ displayAC() }}</span>
</div>
<div class="monster-ability-scores-mental d-flex">
<div class="ability-int text-center">
<div class="ability-label">
<strong>Int</strong>
<div class="monster-hit-points">
<strong>Points de vie</strong>
<span>{{ hp }}</span>
</div>
<div class="monster-movement">
<strong>Vitesse</strong>
<template>{{ displayMovement() }}</template>
</div>
<div class="monster-ability-scores d-flex">
<div class="monster-ability-scores-physical d-flex">
<div class="ability-str text-center">
<div class="ability-label">
<strong>For</strong>
</div>
<div class="ability-score">
{{ displayAbilityScore(monsterStats.abilityScores.for) }}
</div>
</div>
<div class="ability-score">
{{ displayAbilityScore(monster.abilityScores.int) }}
<div class="ability-dex text-center">
<div class="ability-label">
<strong>Dex</strong>
</div>
<div class="ability-score">
{{ displayAbilityScore(monsterStats.abilityScores.dex) }}
</div>
</div>
<div class="ability-con text-center">
<div class="ability-label">
<strong>Con</strong>
</div>
<div class="ability-score">
{{ displayAbilityScore(monsterStats.abilityScores.con) }}
</div>
</div>
</div>
<div class="ability-wis text-center">
<div class="ability-label">
<strong>Sag</strong>
<div class="monster-ability-scores-mental d-flex">
<div class="ability-int text-center">
<div class="ability-label">
<strong>Int</strong>
</div>
<div class="ability-score">
{{ displayAbilityScore(monsterStats.abilityScores.int) }}
</div>
</div>
<div class="ability-score">
{{ displayAbilityScore(monster.abilityScores.sag) }}
<div class="ability-wis text-center">
<div class="ability-label">
<strong>Sag</strong>
</div>
<div class="ability-score">
{{ displayAbilityScore(monsterStats.abilityScores.sag) }}
</div>
</div>
</div>
<div class="ability-cha text-center">
<div class="ability-label">
<strong>Cha</strong>
</div>
<div class="ability-score">
{{ displayAbilityScore(monster.abilityScores.cha) }}
<div class="ability-cha text-center">
<div class="ability-label">
<strong>Cha</strong>
</div>
<div class="ability-score">
{{ displayAbilityScore(monsterStats.abilityScores.cha) }}
</div>
</div>
</div>
</div>
</div>
<div class="monster-saving-throws" v-if="monster.savingThrows">
<strong>Jets de sauvegarde</strong>
<span class="monster-saving-throw" v-for="(savingThrow, idx) in monster.savingThrows">
<template>{{displaySavingThrowBonus(savingThrow)}}</template><template v-if="idx < monster.savingThrows.length - 1">, </template>
</span>
</div>
<div class="monster-skills" v-if="monster.skills">
<strong>Compétences</strong>
<span class="monster-skill" v-for="(skill, idx) in monster.skills">
<template>{{displaySkillBonus(skill)}}</template><template v-if="idx < monster.skills.length - 1">, </template>
</span>
</div>
<div class="monster-damage-type-vulnerabilities" v-if="monster.damageTypeVulnerabilities">
<strong>Vulnérabilité aux dégâts</strong>
<span v-html="displayDamageTypes(monster.damageTypeVulnerabilities)"></span>
</div>
<div class="monster-damage-type-resistances" v-if="monster.damageTypeResistances">
<strong>Résistance aux dégâts</strong>
<span v-html="displayDamageTypes(monster.damageTypeResistances)"></span>
</div>
<div class="monster-damage-type-immunities" v-if="monster.damageTypeImmunities">
<strong>Immunité contre les dégâts</strong>
<span v-html="displayDamageTypes(monster.damageTypeImmunities)"></span>
</div>
<div class="monster-condition-immunities" v-if="monster.conditionImmunities">
<strong>Immunité contre les états</strong>
<span v-html="displayConditionImmunities()"></span>
</div>
<div class="monster-senses">
<strong>Sens</strong>
<template>{{ displaySenses() }}</template>
</div>
<div class="monster-languages">
<strong>Langes</strong>
<template v-if="monster.languages">{{ displayLanguages() }}</template>
<template v-else></template>
</div>
<div class="monster-challenge">
<strong>Dangerosité</strong>
<template>{{ displayChallenge() }}</template>
</div>
<div class="monster-environments" v-if="monster.environments">
<strong>Environnements :</strong>
<span v-html="displayList(monster.environments)"></span>
</div>
<div class="monster-dungeon-types" v-if="monster.dungeonTypes">
<strong>Types de donjons :</strong>
<span v-html="displayList(monster.dungeonTypes)"></span>
<div class="break-avoid">
<div class="monster-saving-throws" v-if="monsterStats.savingThrows && monsterStats.savingThrows.length > 0">
<strong>Jets de sauvegarde</strong>
<span class="monster-saving-throw" v-for="(savingThrow, idx) in monsterStats.savingThrows">
<template>{{displaySavingThrowBonus(savingThrow)}}</template><template v-if="idx < monsterStats.savingThrows.length - 1">, </template>
</span>
</div>
<div class="monster-skills" v-if="monsterStats.skills && monsterStats.skills.length > 0">
<strong>Compétences</strong>
<span class="monster-skill" v-for="(skill, idx) in monsterStats.skills">
<template>{{displaySkillBonus(skill)}}</template><template v-if="idx < monsterStats.skills.length - 1">, </template>
</span>
</div>
<div class="monster-damage-type-vulnerabilities" v-if="monsterStats.damageTypeVulnerabilities && monsterStats.damageTypeVulnerabilities.length > 0">
<strong>Vulnérabilité aux dégâts</strong>
<span v-html="displayDamageTypes(monsterStats.damageTypeVulnerabilities)"></span>
</div>
<div class="monster-damage-type-resistances" v-if="monsterStats.damageTypeResistances && monsterStats.damageTypeResistances.length > 0">
<strong>Résistance aux dégâts</strong>
<span v-html="displayDamageTypes(monsterStats.damageTypeResistances)"></span>
</div>
<div class="monster-damage-type-immunities" v-if="monsterStats.damageTypeImmunities && monsterStats.damageTypeImmunities.length > 0">
<strong>Immunité contre les dégâts</strong>
<span v-html="displayDamageTypes(monsterStats.damageTypeImmunities)"></span>
</div>
<div class="monster-condition-immunities" v-if="monsterStats.conditionImmunities && monsterStats.conditionImmunities.length > 0">
<strong>Immunité contre les états</strong>
<span v-html="displayConditionImmunities()"></span>
</div>
<div class="monster-senses">
<strong>Sens</strong>
<template>{{ displaySenses() }}</template>
</div>
<div class="monster-languages">
<strong>Langes</strong>
<template>{{ languages }}</template>
</div>
<div class="monster-challenge">
<strong>Dangerosité</strong>
<template>{{ displayChallenge() }}</template>
</div>
<div class="monster-environments" v-if="monsterStats.environments">
<strong>Environnements :</strong>
<span v-html="displayList(monsterStats.environments)"></span>
</div>
<div class="monster-dungeon-types" v-if="monsterStats.dungeonTypes">
<strong>Types de donjons :</strong>
<span v-html="displayList(monsterStats.dungeonTypes)"></span>
</div>
</div>
</div>
</div>
<Content class="mt-4"/>
<Content v-if="!monster.custom" :pageKey="monster.key" class="mt-4" />
<div v-else v-html="md.render(monster.content)" class="mt-4"></div>
<p v-if="$page.frontmatter.source" class="source">Source : <em>{{ monster.source }}</em></p>
<p v-if="$page.frontmatter.source" class="source">Source : <em>{{ monsterStats.source }}</em><template v-if="monsterStats.source_page">, page {{ monsterStats.source_page }}</template></p>
</main>
</template>
@ -145,26 +152,29 @@ import {
import {stats} from '../../data/stats.js'
import {armorTypes} from '../../data/armorTypes.js'
import MarkdownIt from 'markdown-it'
export default {
props: ['monster', 'isList', 'hideTitle'],
data () {
return {
md: new MarkdownIt()
}
},
computed: {
monster () {
return this.$page.frontmatter
monsterStats () {
return this.monster.frontmatter
},
proficiencyBonus () {
return this.getProficiencyBonus()
},
passivePerception () {
let result = 10 + getModifier(this.monster.abilityScores.sag)
if (this.monster.skills) {
this.monster.skills.forEach((skill, idx) => {
let result = 10 + getModifier(this.monsterStats.abilityScores.sag)
if (this.monsterStats.skills) {
this.monsterStats.skills.forEach((skill, idx) => {
if (skill.name == 'perception') {
if (skill.isExpert) {
result += this.proficiencyBonus * 2
@ -176,35 +186,73 @@ export default {
}
return result
},
hp () {
if (this.monsterStats.customHP) {
return this.monsterStats.customHP
} else if (this.monsterStats.hitDiceCount) {
let hitDieSize = 8 // Dé de vie moyen par défaut
if (this.monsterStats.hitDieSize) {
hitDieSize = this.monsterStats.hitDieSize
} else if (this.monsterStats.size) {
hitDieSize = stats.sizes[this.monsterStats.size].hitDie
}
let hitPointsBonus = 1
if (this.monsterStats.hitDiceCount > 1) {
hitPointsBonus = Math.floor(this.monsterStats.hitDiceCount / 2)
}
let averageHP = this.monsterStats.hitDiceCount * (hitDieSize / 2) + this.monsterStats.hitDiceCount * getModifier(this.monsterStats.abilityScores.con) + hitPointsBonus
let conMod = ""
if (getModifier(this.monsterStats.abilityScores.con) != 0) {
conMod = this.monsterStats.hitDiceCount * getModifier(this.monsterStats.abilityScores.con)
conMod = displayBonus(conMod)
}
return averageHP + ' (' + this.monsterStats.hitDiceCount + "d" + hitDieSize + conMod + ')'
}
return ""
},
languages () {
let result = this.monsterStats.languages.join(', ')
if (this.monsterStats.customLanguage) {
if (result != '') {
result += ', '
}
result += this.monsterStats.customLanguage
}
if (this.monsterStats.telepathy) {
if (result != '') {
result += ', '
}
result += 'télépathie ' + this.monsterStats.telepathy + ' m'
}
if (result == '') {
return '—'
}
return result
},
},
methods: {
displayList (list) { return list.join(', ') },
displayAbilityScore (value) { return displayAbilityScore(value) },
getModifier (value) { return getModifier(value) },
getProficiencyBonus () { return getProficiencyBonus(this.monster.challenge) },
getProficiencyBonus () { return getProficiencyBonus(this.monsterStats.challenge) },
displayMonsterTypeSizeAlignment () {
return displayMonsterTypeSizeAlignment(this.monster)
return displayMonsterTypeSizeAlignment(this.monsterStats)
},
displaySavingThrowBonus (ability) {
let result = stats.abilities[ability].abbr
let bonus = displayBonus(getModifier(this.monster.abilityScores[ability]) + this.proficiencyBonus)
let bonus = displayBonus(getModifier(this.monsterStats.abilityScores[ability]) + this.proficiencyBonus)
result += ' ' + bonus
return result
},
displaySkillBonus (skill) {
if (skill.name == 'Perception') {
if (skill.isExpert) {
this.monster.perceptionProficiency == 'expert'
} else {
this.monster.perceptionProficiency == 'proficient'
}
}
let result = stats.skills[skill.name].label
let bonus = getModifier(this.monster.abilityScores[stats.skills[skill.name].ability]) + this.proficiencyBonus
let bonus = getModifier(this.monsterStats.abilityScores[stats.skills[skill.name].ability]) + this.proficiencyBonus
if (skill.isExpert) {
bonus += this.proficiencyBonus // Bonus de maître doublé pour les experts
}
@ -213,66 +261,41 @@ export default {
return result
},
displayHP () {
if (this.monster.customHP) {
return this.monster.customHP
} else if (this.monster.hitDiceCount) {
let hitDieSize = 8 // Dé de vie moyen par défaut
if (this.monster.hitDieSize) {
hitDieSize = this.monster.hitDieSize
} else if (this.monster.size) {
hitDieSize = stats.sizes[this.monster.size].hitDie
}
let hitPointsBonus = 1
if (this.monster.hitDiceCount > 1) {
hitPointsBonus = Math.floor(this.monster.hitDiceCount / 2)
}
let averageHP = this.monster.hitDiceCount * (hitDieSize / 2) + this.monster.hitDiceCount * getModifier(this.monster.abilityScores.con) + hitPointsBonus
let conMod = ""
if (getModifier(this.monster.abilityScores.con) != 0) {
conMod = this.monster.hitDiceCount * getModifier(this.monster.abilityScores.con)
conMod = displayBonus(conMod)
}
return averageHP + ' (' + this.monster.hitDiceCount + "d" + hitDieSize + conMod + ')'
}
return ""
},
displayChallenge () {
return displayChallenge(this.monster.challenge, true)
return displayChallenge(this.monsterStats.challenge, true)
},
displayMovement () {
let result = ''
if (this.monster.movement.walk) {
result += this.monster.movement.walk + ' m'
if (this.monsterStats.movement.walk) {
result += this.monsterStats.movement.walk + ' m'
} else {
result += '0 m'
}
if (this.monster.movement.climb) {
if (this.monsterStats.movement.climb) {
if (result != '') {
result += ', '
}
result += 'escalade ' + this.monster.movement.climb + ' m'
result += 'escalade ' + this.monsterStats.movement.climb + ' m'
}
if (this.monster.movement.burrow) {
if (this.monsterStats.movement.burrow) {
if (result != '') {
result += ', '
}
result += 'fouissement ' + this.monster.movement.burrow + ' m'
result += 'fouissement ' + this.monsterStats.movement.burrow + ' m'
}
if (this.monster.movement.swim) {
if (this.monsterStats.movement.swim) {
if (result != '') {
result += ', '
}
result += 'nage ' + this.monster.movement.swim + ' m'
result += 'nage ' + this.monsterStats.movement.swim + ' m'
}
if (this.monster.movement.fly) {
if (this.monsterStats.movement.fly) {
if (result != '') {
result += ', '
}
result += 'vol ' + this.monster.movement.fly + ' m'
if (this.monster.movement.hover) {
result += 'vol ' + this.monsterStats.movement.fly + ' m'
if (this.monsterStats.movement.hover) {
result += ' (vol stationnaire)'
}
}
@ -287,44 +310,48 @@ export default {
let armor = ''
// Le monstre n'a pas d'armure.
// CA = 10 + Dex
if (!this.monster.ac.armorType) {
ac = 10 + getModifier(this.monster.abilityScores.dex)
if (!this.monsterStats.ac.armorType) {
ac = 10 + getModifier(this.monsterStats.abilityScores.dex)
} else {
// Le type d'armure n'est pas formalisé. On prend la valeur brute
if (this.monster.ac.armorType == 'custom') {
return this.monster.ac.value
if (this.monsterStats.ac.armorType == 'custom') {
return this.monsterStats.ac.value
}
// Le monstre a une armure naturelle.
// CA = 10 + Armure naturelle + Dex
if (this.monster.ac.armorType == 'armure naturelle') {
armor = this.monster.ac.armorType
ac = ac + this.monster.ac.value + getModifier(this.monster.abilityScores.dex)
} else if (this.monster.ac.armorType == 'armure du mage') {
if (this.monsterStats.ac.armorType == 'armure naturelle') {
armor = this.monsterStats.ac.armorType
if (parseInt(this.monsterStats.ac.value)) {
ac = ac + parseInt(this.monsterStats.ac.value) + getModifier(this.monsterStats.abilityScores.dex)
} else {
ac = ac + getModifier(this.monsterStats.abilityScores.dex)
}
} else if (this.monsterStats.ac.armorType == 'armure du mage') {
hasMageArmor = true
ac = ac + getModifier(this.monster.abilityScores.dex)
mageArmorAc = mageArmorAc + getModifier(this.monster.abilityScores.dex)
ac = ac + getModifier(this.monsterStats.abilityScores.dex)
mageArmorAc = mageArmorAc + getModifier(this.monsterStats.abilityScores.dex)
armor = mageArmorAc + ' avec armure du mage'
} else {
// Le monstre a un type d'armure défini.
// On calcule sa CA selon le type
let armorType = armorTypes[this.monster.ac.armorType]
let armorType = armorTypes[this.monsterStats.ac.armorType]
// Le type d'armure n'existe pas. On l'ignore.
// CA = 10 + Dex
if (!armorType) {
ac = ac + getModifier(this.monster.abilityScores.dex)
ac = ac + getModifier(this.monsterStats.abilityScores.dex)
} else {
// L'armure n'impose pas de limite de Dex
armor = this.monster.ac.armorType
armor = this.monsterStats.ac.armorType
if (armorType.maxDex === false) {
ac = armorType.value + getModifier(this.monster.abilityScores.dex)
ac = armorType.value + getModifier(this.monsterStats.abilityScores.dex)
} else {
// La limite de Dex de l'armure est inférieure à la Dex du monstre
if (armorType.maxDex <= getModifier(this.monster.abilityScores.dex)) {
if (armorType.maxDex <= getModifier(this.monsterStats.abilityScores.dex)) {
ac = armorType.value + armorType.maxDex
} else {
ac = armorType.value + getModifier(this.monster.abilityScores.dex)
ac = armorType.value + getModifier(this.monsterStats.abilityScores.dex)
}
}
}
@ -333,7 +360,7 @@ export default {
// Le monstre a un bouclier. Sa CA augmente de 2.
if (this.monster.ac.hasShield) {
if (this.monsterStats.ac.hasShield) {
ac = ac + 2
mageArmorAc = mageArmorAc + 2
if (armor != '') {
@ -353,33 +380,29 @@ export default {
return ac
},
displayLanguages () {
return this.monster.languages.join(', ')
},
displaySenses () {
let result = ''
if (this.monster.senses) {
if (this.monster.senses.tremorsense) {
result += 'perception des vibrations ' + this.monster.senses.tremorsense + ' m'
if (this.monsterStats.senses) {
if (this.monsterStats.senses.tremorsense) {
result += 'perception des vibrations ' + this.monsterStats.senses.tremorsense + ' m'
}
if (this.monster.senses.blindsight) {
if (this.monsterStats.senses.blindsight) {
if (result != '') {
result += ', '
}
result += 'vision aveugle ' + this.monster.senses.blindsight + ' m'
result += 'vision aveugle ' + this.monsterStats.senses.blindsight + ' m'
}
if (this.monster.senses.darkvision) {
if (this.monsterStats.senses.darkvision) {
if (result != '') {
result += ', '
}
result += 'vision dans le noir ' + this.monster.senses.darkvision + ' m'
result += 'vision dans le noir ' + this.monsterStats.senses.darkvision + ' m'
}
if (this.monster.senses.truesight) {
if (this.monsterStats.senses.truesight) {
if (result != '') {
result += ', '
}
result += 'vision parfaite ' + this.monster.senses.truesight + ' m'
result += 'vision parfaite ' + this.monsterStats.senses.truesight + ' m'
}
if (result != '') {
result += ', '
@ -392,9 +415,9 @@ export default {
displayConditionImmunities () {
let result = ''
this.monster.conditionImmunities.forEach((condition, idx) => {
this.monsterStats.conditionImmunities.forEach((condition, idx) => {
if (result != '') {
if (idx == this.monster.conditionImmunities.length - 1) {
if (idx == this.monsterStats.conditionImmunities.length - 1) {
result += ' et '
} else {
result += ', '
@ -432,7 +455,7 @@ export default {
.monster-ability-scores {
margin: 12px 0;
.ability-label, .ability-score {
padding: 0 8px;
}

View file

@ -0,0 +1,76 @@
<template>
<v-card class="d-flex flex-column mb-6 monster-card">
<v-card-title>
{{ monster.title }}
<v-spacer></v-spacer>
<v-card-actions v-if="showActions">
<v-btn dark icon class="d-print-none mr-2" link :to="{ path: '/creation-de-monstre-pnj/', query: { key: monster.key } }"><v-icon>mdi-pencil</v-icon></v-btn>
<v-btn dark icon class="d-print-none" @click="$store.commit('myMonsters/removeMonster', monster)"><v-icon>mdi-delete</v-icon></v-btn>
</v-card-actions>
</v-card-title>
<v-card-text>
<div class="monster-card-details mt-3">
<Monster :monster="monster" :hideTitle="true" />
</div>
<v-divider class="mt-3" />
<p v-if="monster.frontmatter.source" class="source">Source : <em>{{ monster.frontmatter.source }}</em></p>
</v-card-text>
</v-card>
</template>
<script>
import Monster from '@theme/components/Monster'
import { displaySchoolLevel } from '@theme/util/monsterHelpers'
import MarkdownIt from 'markdown-it'
export default {
name: 'MonsterCard',
components: { Monster },
props: ['monster', 'showActions', 'isShort'],
data () {
return {
md: new MarkdownIt()
}
},
methods: {
displaySchoolLevel () {
return displaySchoolLevel(this.monster.frontmatter)
}
},
}
</script>
<style lang="scss">
@import '../styles/colors';
.monster-card {
.v-card__title {
background-color: $color-dragon;
color: #fff;
padding-top: 0;
padding-bottom: 0;
}
.monster-card-details {
column-count: 2;
.monster-ability-scores, .break-avoid {
break-inside: avoid;
}
}
p.source {
margin-bottom: 0;
}
.break-avoid {
break-inside: avoid;
}
}
</style>

View file

@ -0,0 +1,58 @@
<template>
<main class="page content">
<div class="theme-default-content">
<div v-if="magicItems.length > 0">
<masonry class="d-print-none" :cols="{'default': 2, 960: 1}" :gutter="24">
<MagicItemCard v-for="(magicItem, idx) in magicItems" :magicItem="magicItem" :showActions="true" :key="idx">
</MagicItemCard>
</masonry>
<div class="d-none d-print-block" v-for="magicItem in magicItems">
<div>
<h2 class="d-flex align-center">
<div class="mr-4">{{ magicItem.title }}</div>
<v-btn class="d-print-none mr-2" small depressed link :to="{ path: '/creation-de-sort/', query: { key: magicItem.key } }"><v-icon left>mdi-pencil</v-icon> Modifier</v-btn>
<v-btn color="error" class="d-print-none" small depressed @click="$store.commit('myMagicItems/removeMagicItem', magicItem)"><v-icon left>mdi-delete</v-icon> Supprimer</v-btn>
</h2>
</div>
<MagicItem :magicItem="magicItem" :isList="true" :hideTitle="true" />
</div>
</div>
<template v-else>
Vous n'avez aucun objet magique.
</template>
</div>
</main>
</template>
<script>
import MagicItem from '@theme/components/MagicItem'
import MagicItemCard from '@theme/components/MagicItemCard'
export default {
name: 'MyMagicItems',
components: {
MagicItem,
MagicItemCard
},
data () {
return {
}
},
computed: {
magicItems () {
return this.$store.state.myMagicItems.magicItems
}
},
methods: {
}
}
</script>
<style lang="scss">
</style>

View file

@ -0,0 +1,56 @@
<template>
<main class="page content">
<div class="theme-default-content">
<template v-if="monsters.length > 0">
<MonsterCard v-for="(monster, idx) in monsters" :monster="monster" :showActions="true" :key="idx">
</MonsterCard>
<div class="d-none d-print-block" v-for="monster in monsters">
<div>
<h1 class="d-flex align-center">
<div class="mr-4">{{ monster.title }}</div>
<v-btn class="d-print-none mr-2" small depressed link :to="{ path: '/creation-de-monstre-pnj/', query: { key: monster.key } }"><v-icon left>mdi-pencil</v-icon> Modifier</v-btn>
<v-btn color="error" class="d-print-none" small depressed @click="$store.commit('myMonsters/removeMonster', monster)"><v-icon left>mdi-delete</v-icon> Supprimer</v-btn>
</h1>
</div>
<Monster :monster="monster" :isList="true" :hideTitle="true" />
</div>
</template>
<template v-else>
Vous n'avez recensé aucun monstre dans votre bestiaire.
</template>
</div>
</main>
</template>
<script>
import Monster from '@theme/components/Monster'
import MonsterCard from '@theme/components/MonsterCard'
export default {
name: 'MyMonsters',
components: {
Monster,
MonsterCard
},
data () {
return {
}
},
computed: {
monsters () {
return this.$store.state.myMonsters.monsters
}
},
methods: {
}
}
</script>
<style lang="scss">
</style>

View file

@ -0,0 +1,58 @@
<template>
<main class="page content">
<div class="theme-default-content">
<div v-if="spells.length > 0">
<masonry class="d-print-none" :cols="{'default': 2, 960: 1}" :gutter="24">
<SpellCard v-for="(spell, idx) in spells" :spell="spell" :showActions="true" :key="idx">
</SpellCard>
</masonry>
<div class="d-none d-print-block" v-for="spell in spells">
<div>
<h2 class="d-flex align-center">
<div class="mr-4">{{ spell.title }}</div>
<v-btn class="d-print-none mr-2" small depressed link :to="{ path: '/creation-de-sort/', query: { key: spell.key } }"><v-icon left>mdi-pencil</v-icon> Modifier</v-btn>
<v-btn color="error" class="d-print-none" small depressed @click="$store.commit('mySpells/removeSpell', spell)"><v-icon left>mdi-delete</v-icon> Supprimer</v-btn>
</h2>
</div>
<Spell :spell="spell" :isList="true" :hideTitle="true" />
</div>
</div>
<template v-else>
Vous n'avez écrit aucun sort dans votre grimoire.
</template>
</div>
</main>
</template>
<script>
import Spell from '@theme/components/Spell'
import SpellCard from '@theme/components/SpellCard'
export default {
name: 'MySpells',
components: {
Spell,
SpellCard
},
data () {
return {
}
},
computed: {
spells () {
return this.$store.state.mySpells.spells
}
},
methods: {
}
}
</script>
<style lang="scss">
</style>

View file

@ -16,6 +16,9 @@
<template v-for="item in items">
<v-list-group v-if="item.children" :key="item.title" :value="isExpanded(item)" color="accent">
<template v-slot:activator>
<v-list-item-icon v-if="item.icon">
<v-icon v-text="item.icon"></v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title>
{{ item.title }}
@ -26,25 +29,37 @@
<template v-for="child in item.children">
<v-list-group v-if="child.children" :key="child.title" sub-group :value="isExpanded(child)" color="accent">
<template v-slot:activator>
<v-list-item-icon v-if="child.icon">
<v-icon v-text="child.icon"></v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title>
{{ child.title }}
<v-chip v-if="child.badge" class="ml-2" color="primary" x-small label v-html="displayBadge(child.badge)"></v-chip>
</v-list-item-title>
</v-list-item-content>
</template>
<v-list-item v-for="subchild in child.children" link :to="{path: subchild.path}" :exact="subchild.exact">
<v-list-item-icon v-if="subchild.icon">
<v-icon v-text="subchild.icon"></v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title>
{{ subchild.title }}
<v-chip v-if="subchild.badge" class="ml-2" color="primary" x-small label v-html="displayBadge(subchild.badge)"></v-chip>
</v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list-group>
<v-divider v-else-if="child.type == 'divider'" />
<v-list-item v-else :key="child.title" link :to="{path: child.path}" :exact="child.exact">
<v-list-item-icon v-if="child.icon">
<v-icon v-text="child.icon"></v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title>
{{ child.title }}
<v-chip v-if="child.badge" class="ml-2" color="primary" x-small label v-html="displayBadge(child.badge)"></v-chip>
</v-list-item-title>
</v-list-item-content>
</v-list-item>
@ -195,6 +210,15 @@ export default {
setIsThemeDark () {
this.$vuetify.theme.dark = !this.$vuetify.theme.dark
this.$store.commit('setIsThemeDark', this.$vuetify.theme.dark)
},
displayBadge (badge) {
if (badge == 'mySpells') {
return this.$store.state.mySpells.spells.length
} else if (badge == 'myMonsters') {
return this.$store.state.myMonsters.monsters.length
} else if (badge == 'myMagicItems') {
return this.$store.state.myMagicItems.magicItems.length
}
}
}
}

View file

@ -5,7 +5,7 @@
<v-btn class="hidden-sm-and-down site-title" text link :to="{ path: '/' }"><img src="/dragon_blanc.svg" />&nbsp;{{ $site.title }}</v-btn>
</v-toolbar-title>
<SRDSearchBox v-if="$site.themeConfig.search !== false && $page.frontmatter.search !== false" />
<v-spacer />
<v-spacer class="hidden-sm-and-down" />
<v-btn class="hidden-sm-and-down" @click.stop="setIsThemeDark" icon>
<v-icon v-html="$vuetify.theme.dark ? 'mdi-brightness-4' : 'mdi-brightness-7'"></v-icon>
</v-btn>

View file

@ -2,37 +2,64 @@
<main class="page content">
<div class="theme-default-content">
<h1>{{ $page.title }}</h1>
<template v-if="!hideTitle">
<h1 v-if="!isList">{{ spell.title }}</h1>
<h2 v-else>{{ spell.title }}</h2>
</template>
<div class="spell-details">
<div class="spell-school-level" v-html="displaySchoolLevel()"></div>
<div class="spell-casting-time"><strong>Temps d'incantation</strong> : {{ $page.frontmatter.casting_time }}</div>
<div class="spell-range"><strong>Portée</strong> : {{ $page.frontmatter.range }}</div>
<div class="spell-school-level title" v-html="displaySchoolLevel()"></div>
<div class="spell-casting-time"><strong>Temps d'incantation</strong> : {{ spell.frontmatter.casting_time }}</div>
<div class="spell-range"><strong>Portée</strong> : {{ spell.frontmatter.range }}</div>
<div class="spell-components"><strong>Composantes</strong> :
<template v-if="$page.frontmatter.components.verbal">V</template><template v-if="$page.frontmatter.components.somatic || $page.frontmatter.components.material">,</template>
<template v-if="$page.frontmatter.components.somatic">S</template><template v-if="$page.frontmatter.components.material">,</template>
<template v-if="$page.frontmatter.components.material">M</template>
<template v-if="$page.frontmatter.components.materials">({{$page.frontmatter.components.materials}})</template>
<template v-if="spell.frontmatter.components.verbal">V</template><template v-if="spell.frontmatter.components.somatic || spell.frontmatter.components.material">,</template>
<template v-if="spell.frontmatter.components.somatic">S</template><template v-if="spell.frontmatter.components.material">,</template>
<template v-if="spell.frontmatter.components.material">M</template>
<template v-if="spell.frontmatter.components.material && spell.frontmatter.components.materials">({{spell.frontmatter.components.materials}})</template>
</div>
<div class="spell-duration"><strong>Durée</strong> : <span v-if="spell.frontmatter.concentration">concentration, </span>{{ spell.frontmatter.duration }}</div>
<div class="spell-classes" v-if="!isShort">
<strong>Classe</strong> : <span v-for="(c, idx) in spell.frontmatter.classes" :key="idx">{{c}}<template v-if="idx != spell.frontmatter.classes.length-1">, </template><template v-if="idx == spell.frontmatter.classes.length-1 && spell.frontmatter.customClasses">, </template></span>
<template v-if="spell.frontmatter.customClasses">
<template>{{ spell.frontmatter.customClasses }}</template>
</template>
</div>
<div class="spell-duration"><strong>Durée</strong> : <span v-if="$page.frontmatter.concentration">concentration, </span>{{ $page.frontmatter.duration }}</div>
<div class="spell-classes"><strong>Classe</strong> : <span v-for="(c, idx) in $page.frontmatter.classes" :key="idx">{{c}}<template v-if="idx != $page.frontmatter.classes.length-1">, </template></span></div>
</div>
</div>
<Content class="mt-4" />
<template v-if="!isShort">
<Content v-if="!spell.custom" :pageKey="spell.key" class="mt-4" />
<div v-else v-html="md.render(spell.content)" class="mt-4"></div>
</template>
<div v-else v-html="spell.frontmatter.description" class="mt-4"></div>
<p v-if="$page.frontmatter.source" class="source">Source : <em>{{ $page.frontmatter.source }}</em></p>
<p v-if="spell.frontmatter.source" class="source">Source : <em>{{ spell.frontmatter.source }}</em></p>
</main>
</template>
<script>
import { displaySchoolLevel } from '@theme/util/spellHelpers'
import MarkdownIt from 'markdown-it'
export default {
name: 'Spell',
props: ['spell', 'isList', 'hideTitle', 'isShort'],
data () {
return {
md: new MarkdownIt()
}
},
methods: {
displaySchoolLevel () {
return displaySchoolLevel(this.$page.frontmatter)
return displaySchoolLevel(this.spell.frontmatter)
}
},
mounted () {
// console.log(this.spell)
}
}
</script>

View file

@ -0,0 +1,91 @@
<template>
<v-card class="d-flex flex-column mb-6 spell-card">
<v-card-title>
{{ spell.title }}
<v-spacer></v-spacer>
<v-card-actions v-if="showActions">
<v-btn dark icon class="d-print-none mr-2" link :to="{ path: '/creation-de-sort/', query: { key: spell.key } }"><v-icon>mdi-pencil</v-icon></v-btn>
<v-btn dark icon class="d-print-none" @click="$store.commit('mySpells/removeSpell', spell)"><v-icon>mdi-delete</v-icon></v-btn>
</v-card-actions>
</v-card-title>
<v-card-text>
<div class="spell-details mt-3">
<div class="spell-school-level title" v-html="displaySchoolLevel()"></div>
<v-row>
<v-col cols="12" md="6" class="pb-0">
<div class="spell-casting-time"><strong>Temps d'incantation</strong> : {{ spell.frontmatter.casting_time }}</div>
</v-col>
<v-col cols="12" md="6" class="pb-0">
<div class="spell-range"><strong>Portée</strong> : {{ spell.frontmatter.range }}</div>
</v-col>
</v-row>
<v-row>
<v-col cols="12" md="6" class="pb-0">
<div class="spell-components"><strong>Composantes</strong> :
<template v-if="spell.frontmatter.components.verbal">V</template><template v-if="spell.frontmatter.components.somatic || spell.frontmatter.components.material">,</template>
<template v-if="spell.frontmatter.components.somatic">S</template><template v-if="spell.frontmatter.components.material">,</template>
<template v-if="spell.frontmatter.components.material">M</template>
<template v-if="spell.frontmatter.components.material && spell.frontmatter.components.materials">({{spell.frontmatter.components.materials}})</template>
</div>
</v-col>
<v-col cols="12" md="6" class="pb-0">
<div class="spell-duration"><strong>Durée</strong> : <span v-if="spell.frontmatter.concentration">concentration, </span>{{ spell.frontmatter.duration }}</div>
</v-col>
</v-row>
</div>
<v-divider class="mt-3" />
<Content v-if="!spell.custom" :pageKey="spell.key" class="mt-4" />
<div v-else v-html="md.render(spell.content)" class="mt-4"></div>
<div class="spell-classes" v-if="!isShort">
<strong>Classe</strong> : <span v-for="(c, idx) in spell.frontmatter.classes" :key="idx">{{c}}<template v-if="idx != spell.frontmatter.classes.length-1">, </template><template v-if="idx == spell.frontmatter.classes.length-1 && spell.frontmatter.customClasses">, </template></span>
<template v-if="spell.frontmatter.customClasses">
<template>{{ spell.frontmatter.customClasses }}</template>
</template>
</div>
<p v-if="spell.frontmatter.source" class="source">Source : <em>{{ spell.frontmatter.source }}</em></p>
</v-card-text>
</v-card>
</template>
<script>
import { displaySchoolLevel } from '@theme/util/spellHelpers'
import MarkdownIt from 'markdown-it'
export default {
name: 'SpellCard',
props: ['spell', 'showActions', 'isShort'],
data () {
return {
md: new MarkdownIt()
}
},
methods: {
displaySchoolLevel () {
return displaySchoolLevel(this.spell.frontmatter)
}
},
}
</script>
<style lang="scss">
@import '../styles/colors';
.spell-card {
.v-card__title {
background-color: $color-dragon;
color: #fff;
padding-top: 0;
padding-bottom: 0;
}
p.source {
margin-bottom: 0;
}
}
</style>

View file

@ -4,9 +4,21 @@ import 'vuetify/dist/vuetify.min.css'
import '@mdi/font/css/materialdesignicons.css'
import Vuetify from 'vuetify'
import fr from 'vuetify/es5/locale/fr'
import VueMasonry from 'vue-masonry-css'
import colors from 'vuetify/es5/util/colors'
// Stockage des donneés utilisateur dans le navigateur
store.subscribe((mutation, state) => {
if (mutation.type.includes('mySpells')) {
localStorage.setItem('mySpells', JSON.stringify(state.mySpells))
} else if (mutation.type.includes('myMonsters')) {
localStorage.setItem('myMonsters', JSON.stringify(state.myMonsters))
} else if (mutation.type.includes('myMagicItems')) {
localStorage.setItem('myMagicItems', JSON.stringify(state.myMagicItems))
}
})
export default ({
Vue,
options,
@ -15,7 +27,10 @@ export default ({
}) => {
require('./styles/main.scss')
Vue.use(Vuex)
Vue.mixin({ store: store })
Vue.use(VueMasonry)
Vue.mixin({
store: store
})
Vue.use(Vuetify)
options.vuetify = new Vuetify({
theme: {

View file

@ -0,0 +1,231 @@
<template>
<div class="create-magic-item">
<div class="d-flex align-center mb-4 d-print-none">
<Breadcrumb class="mr-auto" />
<v-btn color="primary" class="mr-4" depressed link to="/mes-objets-magiques/">Mes objets magiques</v-btn>
<v-btn color="primary" depressed link to="/liste-objets-magiques/">Liste des objets magiques</v-btn>
</div>
<h1 class="d-print-none">Création de sort</h1>
<div class="my-4 d-flex flex-wrap d-print-none">
<v-btn outlined color="accent" class="mr-1 mb-1" depressed @click="print"><v-icon>mdi-printer</v-icon> Imprimer</v-btn>
<v-btn outlined color="accent" class="mr-1 mb-1" depressed @click="download"><v-icon>mdi-file-download</v-icon> Sauvegarder</v-btn>
<v-btn outlined color="accent" class="mr-1 mb-1" depressed :loading="isUploading" @click="onUploadClick">
<v-icon left>mdi-file-upload</v-icon> Charger
</v-btn>
<input ref="uploader" class="d-none" type="file" @change="upload">
<v-btn :outlined="!isMagicItemInTreasureChest" color="accent" class="mr-1 mb-1" depressed @click="toggleMagicItemInTreasureChest"><v-icon>mdi-book</v-icon> {{ displayToggleMagicItemButton }}</v-btn>
<v-btn :disabled="!isMagicItemInTreasureChest" outlined color="accent" class="mr-1 mb-1" depressed @click="updateMagicItemInTreasureChest"><v-icon>mdi-update</v-icon> MàJ dans le grimoire</v-btn>
<v-btn outlined color="error" class="mb-1" depressed @click="reset"><v-icon>mdi-eraser</v-icon> Réinitialiser</v-btn>
</div>
<v-row>
<v-col class="d-print-none" :col="6">
<v-row>
<v-col>
<v-text-field label="Nom" outlined dense v-model="magicItem.title"></v-text-field>
</v-col>
<v-col>
<v-select :items="types" label="Type" outlined dense v-model="magicItem.frontmatter.type"></v-select>
</v-col>
<v-col>
<v-select :items="rarities" label="Rareté" outlined dense v-model="magicItem.frontmatter.rarity"></v-select>
</v-col>
</v-row>
<v-row>
<v-col>
<v-text-field label="Sous-type" outlined dense v-model="magicItem.frontmatter.subtype"></v-text-field>
</v-col>
<v-col>
<v-switch class="my-0" v-model="magicItem.hasAttunement" label="Harmonisation" @change="switchAttunement" dense></v-switch>
</v-col>
<v-col v-if="magicItem.hasAttunement">
<v-text-field label="Type d'armonisation" outlined dense v-model="magicItem.frontmatter.attunement"></v-text-field>
</v-col>
</v-row>
<v-row>
<v-col>
<v-textarea outlined label="Description" v-model="magicItem.content"></v-textarea>
</v-col>
</v-row>
</v-col>
<v-col :col="6">
<MagicItem :magicItem="magicItem" />
</v-col>
</v-row>
</div>
</template>
<script>
import Breadcrumb from '@theme/components/Breadcrumb'
import MagicItem from '@theme/components/MagicItem'
import { saveAs } from 'file-saver'
import { MAGICITEMTYPES, RARITIES } from '../../data/magicItems'
import { getUrlParameter } from '@theme/util/filterHelpers'
export default {
name: 'CreateMagicItemLayout',
components: {
Breadcrumb,
MagicItem
},
computed: {
isMagicItemInTreasureChest () {
let isInTreasureChest = false
for (let s of this.$store.state.myMagicItems.magicItems) {
if (s.key == this.magicItem.key) {
isInTreasureChest = true
}
}
return isInTreasureChest
},
displayToggleMagicItemButton () {
if (this.isMagicItemInTreasureChest) {
return 'Supprimer de mes objets magiques'
}
return 'Ajouter à mes objets magiques'
},
displayAttunement () {
if (this.customAttunement !== "") {
return this.customAttunement
} else {
return 'harmonisation requise'
}
}
},
data () {
return {
isUploading: false,
types: MAGICITEMTYPES,
rarities: RARITIES,
customAttunement: '',
magicItem: {
custom: true,
pid: 'magicItem',
key: null,
title: '',
content: '',
hasAttunement: false,
frontmatter: {
type: '',
subtype: '',
rarity: '',
attunement: '',
}
}
}
},
methods: {
download () {
saveAs(new Blob([JSON.stringify(this.magicItem)], {
type: "text/plain;charset=utf-8"
}), "objet-magique.json")
},
upload (e) {
let file = e.target.files[0]
let reader = new FileReader()
let self = this
reader.onload = function() {
let result = JSON.parse(reader.result)
if (result.pid == 'magicItem') {
self.magicItem = result
}
}
reader.readAsText(file)
},
onUploadClick () {
this.isUploading = true
window.addEventListener('focus', () => {
this.isUploading = false
}, { once: true })
this.$refs.uploader.click()
},
print () {
window.print()
},
toggleMagicItemInTreasureChest () {
if (this.isMagicItemInTreasureChest) {
this.$store.commit('myMagicItems/removeMagicItem', this.magicItem)
} else {
this.$store.commit('myMagicItems/addMagicItem', this.magicItem)
}
},
updateMagicItemInTreasureChest () {
if (this.isMagicItemInTreasureChest) {
this.$store.commit('myMagicItems/updateMagicItem', this.magicItem)
}
},
switchAttunement () {
if (this.magicItem.hasAttunement) {
this.magicItem.frontmatter.attunement = "harmonisation requise"
} else {
this.magicItem.frontmatter.attunement = ""
}
},
reset () {
this.customAttunement = '',
this.magicItem = {
custom: true,
pid: 'magicItem',
key: Math.random().toString(36).substr(2, 9),
title: '',
content: '',
hasAttunement: false,
frontmatter: {
type: '',
subtype: '',
rarity: '',
attunement: '',
}
}
}
},
mounted () {
this.$store.commit('setHasRightDrawer', false)
this.$store.commit('setRightDrawer', this.$vuetify.breakpoint.lgAndUp)
this.$store.commit('setInRightDrawer', null)
let magicItemKey = getUrlParameter(window.location.href, "key")
if (magicItemKey) {
for (let magicItem of this.$store.state.myMagicItems.magicItems) {
if (magicItem.key == magicItemKey) {
this.magicItem = magicItem
if (!this.magicItem.custom) {
this.magicItem.content = magicItem.rawContent
this.magicItem.custom = true
}
}
}
} else {
this.magicItem.key = Math.random().toString(36).substr(2, 9)
}
}
}
</script>
<style>
</style>

View file

@ -0,0 +1,402 @@
<template>
<div class="create-monster">
<div class="d-flex align-center mb-4 d-print-none">
<Breadcrumb class="mr-auto" />
<v-btn color="primary" class="mr-4" depressed link to="/mon-bestiaire/">Mon Bestiaire</v-btn>
<v-btn color="primary" depressed link to="/bestiaire/">Bestiaire</v-btn>
</div>
<h1 class="d-print-none">Création de monstre/PNJ</h1>
<div class="my-4 d-flex flex-wrap d-print-none">
<v-btn outlined color="accent" class="mr-1 mb-1" depressed @click="print"><v-icon>mdi-printer</v-icon> Imprimer</v-btn>
<v-btn outlined color="accent" class="mr-1 mb-1" depressed @click="download"><v-icon>mdi-file-download</v-icon> Sauvegarder</v-btn>
<v-btn outlined color="accent" class="mr-1 mb-1" depressed :loading="isUploading" @click="onUploadClick">
<v-icon left>mdi-file-upload</v-icon> Charger
</v-btn>
<input ref="uploader" class="d-none" type="file" @change="upload">
<v-btn :outlined="!isMonsterInBestiary" color="accent" class="mr-1 mb-1" depressed @click="toggleMonsterInBestiary"><v-icon>mdi-book</v-icon> {{ displayToggleMonsterButton }}</v-btn>
<v-btn :disabled="!isMonsterInBestiary" outlined color="accent" class="mr-1 mb-1" depressed @click="updateMonsterInBestiary"><v-icon>mdi-update</v-icon> MàJ dans le bestiaire</v-btn>
<v-btn outlined color="error" class="mb-1" depressed @click="reset"><v-icon>mdi-eraser</v-icon> Réinitialiser</v-btn>
</div>
<v-row>
<v-col class="d-print-none" :col="6">
<v-row>
<v-col>
<v-text-field label="Nom" placeholder="Elfe noir" outlined dense v-model="monster.title"></v-text-field>
</v-col>
<v-col>
<v-select :items="sizes" item-text="label" item-value="abbr" label="Taille" outlined dense v-model="monster.frontmatter.size" @change="selectMonsterSize"></v-select>
</v-col>
<v-col>
<v-select :items="challenges" item-text="label" item-value="value" label="Dangerosité" persistent-hint :hint="challengeHint" outlined dense v-model="monster.frontmatter.challenge"></v-select>
</v-col>
</v-row>
<v-row>
<v-col>
<v-select :items="monsterTypes" label="Type" outlined dense v-model="monster.frontmatter.type"></v-select>
</v-col>
<v-col>
<v-text-field label="Sous-type" outlined dense v-model="monster.frontmatter.subtype"></v-text-field>
</v-col>
<v-col>
<v-text-field label="Alignement" placeholder="Chaotique mauvais" outlined dense v-model="monster.frontmatter.alignment"></v-text-field>
</v-col>
</v-row>
<v-row>
<v-col class="d-flex justify-space-between">
<v-switch :disabled="!monster.frontmatter.type" class="my-0 mr-4" v-model="monster.frontmatter.isSwarm" label="Nuée" dense @change="switchIsSwarm"></v-switch>
<v-select :disabled="!monster.frontmatter.isSwarm" :items="sizes" item-text="label" item-value="abbr" label="Taille des créatures" outlined dense v-model="monster.frontmatter.swarmSize"></v-select>
</v-col>
</v-row>
<v-row>
<v-col class="d-flex align-center">
<v-text-field class="mr-1" label="Force" type="number" min="1" outlined dense v-model="monster.frontmatter.abilityScores.for"></v-text-field>
<v-text-field class="mr-1" label="Dextérité" type="number" min="1" outlined dense v-model="monster.frontmatter.abilityScores.dex"></v-text-field>
<v-text-field class="mr-1" label="Constitution" type="number" min="1" outlined dense v-model="monster.frontmatter.abilityScores.con"></v-text-field>
<v-text-field class="mr-1" label="Intelligence" type="number" min="1" outlined dense v-model="monster.frontmatter.abilityScores.int"></v-text-field>
<v-text-field class="mr-1" label="Sagesse" type="number" min="1" outlined dense v-model="monster.frontmatter.abilityScores.sag"></v-text-field>
<v-text-field class="mr-0" label="Charisme" type="number" min="1" outlined dense v-model="monster.frontmatter.abilityScores.cha"></v-text-field>
</v-col>
</v-row>
<v-row>
<v-col class="d-flex align-center">
<v-text-field label="Dés de vie" type="number" min="1" outlined dense v-model="monster.frontmatter.hitDiceCount"></v-text-field>
<v-switch class="my-0 mx-1" v-model="customHitDieSize" label="Dé de vie personnalisé" dense @change="switchCustomHitDizeSize"></v-switch>
<v-select :disabled="!customHitDieSize" :items="sizes" item-text="hitDie" item-value="hitDie" label="Taille du dé de vie" outlined dense v-model="monster.frontmatter.hitDieSize"></v-select>
</v-col>
</v-row>
<v-row>
<v-col class="d-flex align-center">
<v-select class="mr-1" :items="armorTypes" item-text="label" item-value="value" label="Type d'armure" outlined dense v-model="monster.frontmatter.ac.armorType" @change="selectArmorType"></v-select>
<v-text-field v-if="monster.frontmatter.ac.armorType == 'armure naturelle'" label="Armure naturelle" type="number" min="0" outlined dense v-model="monster.frontmatter.ac.value"></v-text-field>
<v-text-field v-if="monster.frontmatter.ac.armorType == 'custom'" label="Armure personnalisée" outlined dense v-model="monster.frontmatter.ac.value"></v-text-field>
<v-switch class="my-0 ml-1" v-if="monster.frontmatter.ac.armorType != 'custom'" v-model="monster.frontmatter.ac.hasShield" label="Bouclier" dense></v-switch>
</v-col>
</v-row>
<v-row>
<v-col class="d-flex align-center">
<v-select class="mr-1" multiple clearable :items="abilities" item-text="label" item-value="value" label="Jets de sauvegarde" outlined dense v-model="monster.frontmatter.savingThrows"></v-select>
<v-select class="mr-1" multiple clearable return-object :items="skills" item-text="label" item-value="value" label="Compétences" outlined dense v-model="selectedSkillsProficient" @change="selectSkill"></v-select>
<v-select class="mr-1" multiple clearable :items="selectedSkillsProficient" item-text="label" item-value="value" label="Compétences expert" outlined dense v-model="selectedSkillsExpert" @change="selectSkillExpert"></v-select>
</v-col>
</v-row>
<v-row>
<v-col class="d-flex align-center flex-wrap">
<v-select class="mr-1" multiple clearable :items="conditions" label="Immunité états spéciaux" outlined dense v-model="monster.frontmatter.conditionImmunities"></v-select>
<v-select class="mr-1" multiple clearable :items="damageTypes" label="Vulnérabilité au dégâts" outlined dense v-model="monster.frontmatter.damageTypeVulnerabilities"></v-select>
<v-select class="mr-1" multiple clearable :items="damageTypes" label="Résistance au dégâts" outlined dense v-model="monster.frontmatter.damageTypeResistances"></v-select>
<v-select class="mr-1" multiple clearable :items="damageTypes" label="Immunité au dégâts" outlined dense v-model="monster.frontmatter.damageTypeImmunities"></v-select>
</v-col>
</v-row>
<v-row>
<v-col class="d-flex align-center">
<v-text-field class="mr-1" label="Au sol" type="number" min="0" outlined dense v-model="monster.frontmatter.movement.walk"></v-text-field>
<v-text-field class="mr-1" label="Escalade" type="number" min="0" outlined dense v-model="monster.frontmatter.movement.climb"></v-text-field>
<v-text-field class="mr-1" label="Fouissement" type="number" min="0" outlined dense v-model="monster.frontmatter.movement.burrow"></v-text-field>
<v-text-field class="mr-1" label="Nage" type="number" min="0" outlined dense v-model="monster.frontmatter.movement.swim"></v-text-field>
<v-text-field class="mr-1" label="Vol" type="number" min="0" outlined dense v-model="monster.frontmatter.movement.fly"></v-text-field>
<v-switch class="my-0" v-model="monster.frontmatter.movement.hover" label="Vol stationnaire" dense></v-switch>
</v-col>
</v-row>
<v-row>
<v-col class="d-flex align-center">
<v-text-field class="mr-1" label="Perception des vibrations" type="number" min="0" outlined dense v-model="monster.frontmatter.senses.tremorsense"></v-text-field>
<v-text-field class="mr-1" label="Vision aveugle" type="number" min="0" outlined dense v-model="monster.frontmatter.senses.blindsight"></v-text-field>
<v-text-field class="mr-1" label="Vision dans le noir" type="number" min="0" outlined dense v-model="monster.frontmatter.senses.darkvision"></v-text-field>
<v-text-field class="mr-0" label="Vision parfaite" type="number" min="0" outlined dense v-model="monster.frontmatter.senses.truesight"></v-text-field>
</v-col>
</v-row>
<v-row>
<v-col class="d-flex align-center">
<v-select class="mr-1" multiple clearable :items="languages" label="Langues" outlined dense v-model="monster.frontmatter.languages"></v-select>
<v-text-field class="mr-1" label="Langue personnalisée" outlined dense v-model="monster.frontmatter.customLanguage"></v-text-field>
<v-text-field label="Télépathie" type="number" min="0" outlined dense v-model="monster.frontmatter.telepathy"></v-text-field>
</v-col>
</v-row>
<v-row>
<v-col>
<v-textarea outlined label="Capacités, Actions, Réactions, Actions légendaires" v-model="monster.content"></v-textarea>
</v-col>
</v-row>
</v-col>
<v-col :col="6">
<Monster :monster="monster" />
</v-col>
</v-row>
</div>
</template>
<script>
import Breadcrumb from '@theme/components/Breadcrumb'
import Monster from '@theme/components/Monster'
import { saveAs } from 'file-saver'
import { MONSTERTYPES, MONSTERSIZES, CHALLENGES, ABILITIES, SKILLS } from '../../data/monsters'
import { stats } from '../../data/stats'
import { ARMORTYPES } from '../../data/armorTypes'
import { CONDITIONS } from '../../data/conditions'
import { DAMAGETYPES } from '../../data/damageTypes'
import { LANGUAGES } from '../../data/languages'
import { getUrlParameter } from '@theme/util/filterHelpers'
import { getProficiencyBonus, displayBonus } from '@theme/util/monsterHelpers'
export default {
name: 'CreateMonsterLayout',
components: {
Breadcrumb,
Monster
},
computed: {
isMonsterInBestiary () {
let isInBestiary = false
for (let s of this.$store.state.myMonsters.monsters) {
if (s.key == this.monster.key) {
isInBestiary = true
}
}
return isInBestiary
},
displayToggleMonsterButton () {
if (this.isMonsterInBestiary) {
return 'Supprimer de mon bestiaire'
}
return 'Ajouter à mon bestiaire'
},
challengeHint () {
return 'Bonus de maîtrise : ' + displayBonus(getProficiencyBonus(this.monster.frontmatter.challenge))
},
},
data () {
return {
isUploading: false,
monsterTypes: MONSTERTYPES,
sizes: MONSTERSIZES,
armorTypes: ARMORTYPES,
abilities: ABILITIES,
skills: SKILLS,
skillObjects: stats.skills,
conditions: CONDITIONS,
damageTypes: DAMAGETYPES,
challenges: CHALLENGES,
languages: LANGUAGES,
customHitDieSize: false,
selectedSkillsProficient: [],
selectedSkillsExpert: [],
monster: {
custom: true,
pid: 'monster',
key: null,
title: '',
content: '',
frontmatter: {
type: '',
subtype: '',
size: '',
challenge: '0',
alignment: '',
isSwarm: false,
swarmSize: '',
hitDiceCount: 1,
hitDieSize: '',
abilityScores: {
for: 10,
dex: 10,
con: 10,
int: 10,
sag: 10,
cha: 10,
},
ac: {
armorType: null,
value: null, // Dans le cas du type d'armure "Armure naturelle"
hasShield: false,
},
savingThrow: null,
skills: null,
movement: {
walk: '',
climb: '',
burrow: '',
swim: '',
fly: '',
hover: false,
},
senses: {
tremorsense: '',
blindsight: '',
darkvision: '',
truesight: '',
},
conditionImmunities: [],
damageTypeVulnerabilities: [],
damageTypeResistances: [],
damageTypeImmunities: [],
languages: [],
customLanguage: '',
telepathy: null
}
}
}
},
methods: {
download () {
saveAs(new Blob([JSON.stringify(this.monster)], {
type: "text/plain;charset=utf-8"
}), "monstre.json")
},
upload (e) {
let file = e.target.files[0]
let reader = new FileReader()
let self = this
reader.onload = function() {
let result = JSON.parse(reader.result)
if (result.pid == 'monster') {
self.monster = result
}
}
reader.readAsText(file)
},
onUploadClick () {
this.isUploading = true
window.addEventListener('focus', () => {
this.isUploading = false
}, { once: true })
this.$refs.uploader.click()
},
print () {
window.print()
},
toggleMonsterInBestiary () {
if (this.isMonsterInBestiary) {
this.$store.commit('myMonsters/removeMonster', this.monster)
} else {
this.$store.commit('myMonsters/addMonster', this.monster)
}
},
updateMonsterInBestiary () {
if (this.isMonsterInBestiary) {
this.$store.commit('myMonsters/updateMonster', this.monster)
}
},
reset () {
this.monster = {
custom: true,
pid: 'monster',
key: Math.random().toString(36).substr(2, 9),
title: '',
content: '',
frontmatter: {
}
}
},
switchIsSwarm () {
if (!this.monster.frontmatter.isSwarm) {
this.monster.frontmatter.swarmSize = ''
}
},
selectMonsterSize (selected) {
},
switchCustomHitDizeSize () {
if (!this.customHitDieSize) {
this.monster.frontmatter.hitDieSize = ''
}
},
selectArmorType () {
if (this.monster.frontmatter.ac.armorType == 'armure naturelle' || this.monster.frontmatter.ac.armorType == 'custom') {
this.monster.frontmatter.ac.value = null
}
},
selectSkill () {
this.monster.frontmatter.skills = []
this.selectedSkillsProficient.forEach((skill, idx) => {
this.monster.frontmatter.skills.push({name: skill.value})
})
this.setSkillsExpert()
},
selectSkillExpert () {
this.selectSkill()
this.setSkillsExpert()
},
setSkillsExpert () {
this.monster.frontmatter.skills.forEach((mskill, idx) => {
this.monster.frontmatter.skills[idx].isExpert = this.selectedSkillsExpert.indexOf(mskill.name) >= 0
})
},
},
mounted () {
this.$store.commit('setHasRightDrawer', false)
this.$store.commit('setRightDrawer', this.$vuetify.breakpoint.lgAndUp)
this.$store.commit('setInRightDrawer', null)
let monsterKey = getUrlParameter(window.location.href, "key")
if (monsterKey) {
for (let monster of this.$store.state.myMonsters.monsters) {
if (monster.key == monsterKey) {
this.monster = monster
if (!this.monster.custom) {
this.monster.content = monster.rawContent
this.monster.custom = true
}
if (this.monster.frontmatter.skills) {
this.monster.frontmatter.skills.forEach((mskill, idx) => {
SKILLS.forEach((skill, jdx) => {
if (mskill.name == skill.value) {
this.selectedSkillsProficient.push(skill)
if (mskill.isExpert) {
this.selectedSkillsExpert.push(skill.value)
}
}
})
})
}
}
}
} else {
this.monster.key = Math.random().toString(36).substr(2, 9)
}
}
}
</script>
<style>
</style>

View file

@ -0,0 +1,276 @@
<template>
<div class="create-spell">
<div class="d-flex align-center mb-4 d-print-none">
<Breadcrumb class="mr-auto" />
<v-btn color="primary" class="mr-4" depressed link to="/mon-grimoire/">Mon Grimoire</v-btn>
<v-btn color="primary" depressed link to="/grimoire/">Grimoire</v-btn>
</div>
<h1 class="d-print-none">Création de sort</h1>
<div class="my-4 d-flex flex-wrap d-print-none">
<v-btn outlined color="accent" class="mr-1 mb-1" depressed @click="print"><v-icon>mdi-printer</v-icon> Imprimer</v-btn>
<v-btn outlined color="accent" class="mr-1 mb-1" depressed @click="download"><v-icon>mdi-file-download</v-icon> Sauvegarder</v-btn>
<v-btn outlined color="accent" class="mr-1 mb-1" depressed :loading="isUploading" @click="onUploadClick">
<v-icon left>mdi-file-upload</v-icon> Charger
</v-btn>
<input ref="uploader" class="d-none" type="file" @change="upload">
<v-btn :outlined="!isSpellInSpellBook" color="accent" class="mr-1 mb-1" depressed @click="toggleSpellInSpellBook"><v-icon>mdi-book</v-icon> {{ displayToggleSpellButton }}</v-btn>
<v-btn :disabled="!isSpellInSpellBook" outlined color="accent" class="mr-1 mb-1" depressed @click="updateSpellInSpellBook"><v-icon>mdi-update</v-icon> MàJ dans le grimoire</v-btn>
<v-btn outlined color="error" class="mb-1" depressed @click="reset"><v-icon>mdi-eraser</v-icon> Réinitialiser</v-btn>
</div>
<v-row>
<v-col class="d-print-none" :col="6">
<v-row>
<v-col>
<v-text-field label="Nom" placeholder="Projectile magique" outlined dense v-model="spell.title"></v-text-field>
</v-col>
<v-col>
<v-select :items="schools" label="École" outlined dense v-model="spell.frontmatter.school"></v-select>
</v-col>
<v-col>
<v-select :items="levels" item-text="label" item-value="value" label="Niveau" outlined dense v-model="spell.frontmatter.level"></v-select>
</v-col>
<v-col>
<v-switch class="my-0" v-model="spell.frontmatter.ritual" label="Rituel" dense></v-switch>
</v-col>
</v-row>
<v-row>
<v-col>
<v-text-field label="Temps d'incantation" placeholder="1 action" outlined dense v-model="spell.frontmatter.casting_time"></v-text-field>
</v-col>
<v-col>
<v-text-field label="Portée" placeholder="36 m" outlined dense v-model="spell.frontmatter.range"></v-text-field>
</v-col>
<v-col>
<v-text-field label="Duration" placeholder="instantanée" outlined dense v-model="spell.frontmatter.duration"></v-text-field>
</v-col>
<v-col>
<v-switch class="my-0" v-model="spell.frontmatter.concentration" label="Concentration" dense></v-switch>
</v-col>
</v-row>
<v-row>
<v-col>
<v-switch class="my-0" v-model="spell.frontmatter.components.verbal" label="Verbale" dense></v-switch>
</v-col>
<v-col>
<v-switch class="my-0" v-model="spell.frontmatter.components.somatic" label="Somatique" dense></v-switch>
</v-col>
<v-col>
<v-switch class="my-0" v-model="spell.frontmatter.components.material" label="Matérielle" dense></v-switch>
</v-col>
<v-col v-if="spell.frontmatter.components.material">
<v-text-field label="Composantes matérielles" outlined dense v-model="spell.frontmatter.components.materials"></v-text-field>
</v-col>
</v-row>
<v-row>
<v-col>
<v-select :items="classes" multiple clearable label="Disponible pour les classes de :" outlined dense v-model="spell.frontmatter.classes"></v-select>
</v-col>
</v-row>
<v-row>
<v-col>
<v-text-field label="Classes personnalisées" placeholder="Forgesort" outlined dense v-model="spell.frontmatter.customClasses"></v-text-field>
</v-col>
</v-row>
<v-row>
<v-col>
<v-text-field outlined label="Résumé" v-model="spell.frontmatter.description"></v-text-field>
</v-col>
</v-row>
<v-row>
<v-col>
<v-textarea outlined label="Description" v-model="spell.content"></v-textarea>
</v-col>
</v-row>
</v-col>
<v-col :col="6">
<Spell :spell="spell" />
</v-col>
</v-row>
</div>
</template>
<script>
import Breadcrumb from '@theme/components/Breadcrumb'
import Spell from '@theme/components/Spell'
import { saveAs } from 'file-saver'
import { CLASSES } from '../../data/classes'
import { SPELLSCHOOLS, SPELLLEVELS } from '../../data/spells'
import { getUrlParameter } from '@theme/util/filterHelpers'
export default {
name: 'CreateSpellLayout',
components: {
Breadcrumb,
Spell
},
computed: {
isSpellInSpellBook () {
let isInSpellBook = false
for (let s of this.$store.state.mySpells.spells) {
if (s.key == this.spell.key) {
isInSpellBook = true
}
}
return isInSpellBook
},
displayToggleSpellButton () {
if (this.isSpellInSpellBook) {
return 'Supprimer de mon grimoire'
}
return 'Ajouter à mon grimoire'
}
},
data () {
return {
isUploading: false,
schools: SPELLSCHOOLS,
levels: SPELLLEVELS,
classes: CLASSES,
spell: {
custom: true,
pid: 'spell',
key: null,
title: '',
content: '',
frontmatter: {
description: '',
classes: '',
customClasses: '',
school: '',
level: '',
ritual: false,
casting_time: '',
range: '',
duration: '',
concentration: false,
components: {
verbal: false,
somatic: false,
material: false,
materials: null
}
}
}
}
},
methods: {
download () {
saveAs(new Blob([JSON.stringify(this.spell)], {
type: "text/plain;charset=utf-8"
}), "sort.json")
},
upload (e) {
let file = e.target.files[0]
let reader = new FileReader()
let self = this
reader.onload = function() {
let result = JSON.parse(reader.result)
if (result.pid == 'spell') {
self.spell = result
}
}
reader.readAsText(file)
},
onUploadClick () {
this.isUploading = true
window.addEventListener('focus', () => {
this.isUploading = false
}, { once: true })
this.$refs.uploader.click()
},
print () {
window.print()
},
toggleSpellInSpellBook () {
if (this.isSpellInSpellBook) {
this.$store.commit('mySpells/removeSpell', this.spell)
} else {
this.$store.commit('mySpells/addSpell', this.spell)
}
},
updateSpellInSpellBook () {
if (this.isSpellInSpellBook) {
this.$store.commit('mySpells/updateSpell', this.spell)
}
},
reset () {
this.spell = {
custom: true,
pid: 'spell',
key: Math.random().toString(36).substr(2, 9),
title: '',
content: '',
frontmatter: {
description: '',
classes: '',
customClasses: '',
school: '',
level: '',
ritual: false,
casting_time: '',
range: '',
duration: '',
concentration: false,
components: {
verbal: false,
somatic: false,
material: false,
materials: null
}
}
}
}
},
mounted () {
this.$store.commit('setHasRightDrawer', false)
this.$store.commit('setRightDrawer', this.$vuetify.breakpoint.lgAndUp)
this.$store.commit('setInRightDrawer', null)
let spellKey = getUrlParameter(window.location.href, "key")
if (spellKey) {
for (let spell of this.$store.state.mySpells.spells) {
if (spell.key == spellKey) {
this.spell = spell
if (!this.spell.custom) {
this.spell.content = spell.rawContent
this.spell.custom = true
}
}
}
} else {
this.spell.key = Math.random().toString(36).substr(2, 9)
}
}
}
</script>
<style>
</style>

View file

@ -101,6 +101,12 @@ export default {
if (THEMEISDARK === 'true') {
this.$vuetify.theme.dark = true
}
// Chargement des donées utilisateur depuis le navigateur
this.$store.commit('mySpells/initialiseStore')
this.$store.commit('myMonsters/initialiseStore')
this.$store.commit('myMagicItems/initialiseStore')
// this.$vuetify.theme.dark = this.$store.state.isThemeDark
// let conditionLinks = document.links

View file

@ -1,7 +1,12 @@
<template>
<div class="magic-item">
<Breadcrumb />
<MagicItem />
<div class="d-flex align-center mb-4 d-print-none">
<Breadcrumb class="mr-auto" />
<v-btn color="primary" class="mr-4" depressed link to="/creation-d-objet-magique/"><v-icon left>mdi-plus</v-icon> Créer un objet magique</v-btn>
<v-btn :outlined="!isMagicItemInTreasureChest" color="accent" class="mr-4" depressed @click="toggleMagicItemInTreasureChest"><v-icon>mdi-book</v-icon> {{ displayToggleMagicItemButton }}</v-btn>
<v-btn color="primary" class="mr-4" depressed link to="/mon-grimoire/">Mes objets magiques</v-btn>
</div>
<MagicItem :magicItem="$page" />
<Edit />
</div>
</template>
@ -20,6 +25,35 @@ export default {
Edit
},
computed: {
isMagicItemInTreasureChest () {
let isInTreasureChest = false
for (let s of this.$store.state.myMagicItems.magicItems) {
if (s.key == this.$page.key) {
isInTreasureChest = true
}
}
return isInTreasureChest
},
displayToggleMagicItemButton () {
if (this.isMagicItemInTreasureChest) {
return 'Supprimer de mes objets magiques'
}
return 'Ajouter à mes objets magiques'
}
},
methods: {
toggleMagicItemInTreasureChest () {
if (this.isMagicItemInTreasureChest) {
this.$store.commit('myMagicItems/removeMagicItem', this.$page)
} else {
this.$store.commit('myMagicItems/addMagicItem', this.$page)
}
}
},
mounted () {
this.$store.commit('setHasRightDrawer', false)
this.$store.commit('setRightDrawer', false)

View file

@ -1,7 +1,11 @@
<template>
<div class="magic-items">
<Breadcrumb />
<div class="d-flex align-center mb-4">
<Breadcrumb class="mr-auto" />
<v-btn color="primary" class="mr-4" depressed link to="/creation-d-objet-magique/"><v-icon left>mdi-plus</v-icon> Créer un objet magique</v-btn>
<v-btn color="primary" depressed link to="/mes-objets-magiques/">Mes objets magiques</v-btn>
</div>
<h1>Liste des objets magiques</h1>
@ -16,6 +20,10 @@
:search="search"
>
<template v-slot:item.isInTreasureChest="{ item }">
<v-simple-checkbox off-icon="mdi-bookmark-outline" on-icon="mdi-bookmark" @input="toggleItemInTreasureChest(item)" :value="isItemInTreasureChest(item)"></v-simple-checkbox>
</template>
<template v-slot:item.title="{ item }">
<router-link :to="{ path: item.path }" class="subtitle-2">{{ item.title }}</router-link>
</template>
@ -40,6 +48,7 @@ export default {
sortBy: 'title',
sortDesc: false,
headers: [
{ text: "", align: 'center', sortable: false, value: 'isInTreasureChest' },
{ text: "Nom", align: 'start', sortable: true, value: 'title' },
{ text: "Type", align: 'start', sortable: false, value: 'frontmatter.type' },
{ text: "Rareté", align: 'start', sortable: false, value: 'frontmatter.rarity' },
@ -102,6 +111,25 @@ export default {
}
},
methods: {
isItemInTreasureChest (magicItem) {
let isInTreasureChest = false
for (let mi of this.$store.state.myMagicItems.magicItems) {
if (mi.key == magicItem.key) {
isInTreasureChest = true
}
}
return isInTreasureChest
},
toggleItemInTreasureChest (magicItem) {
if (this.isItemInTreasureChest(magicItem)) {
this.$store.commit('myMagicItems/removeMagicItem', magicItem)
} else {
this.$store.commit('myMagicItems/addMagicItem', magicItem)
}
}
},
mounted () {
this.$store.commit('setHasRightDrawer', true)
this.$store.commit('setRightDrawer', this.$vuetify.breakpoint.lgAndUp)

View file

@ -1,20 +1,57 @@
<template>
<div class="monster">
<Breadcrumb />
<Monster />
<div class="d-flex align-center mb-4 d-print-none">
<Breadcrumb class="mr-auto" />
<v-btn color="primary" class="mr-4" depressed link to="/creation-de-monstre-pnj/"><v-icon left>mdi-plus</v-icon> Créer un monstre</v-btn>
<v-btn :outlined="!isMonsterInBestiary" color="accent" class="mr-4" depressed @click="toggleMonsterInBestiary"><v-icon>mdi-book</v-icon> {{ displayToggleMonsterButton }}</v-btn>
<v-btn color="primary" class="mr-4" depressed link to="/mon-bestiaire/">Mon Bestiaire</v-btn>
</div>
<Monster :monster="$page" />
<Edit />
</div>
</template>
<script>
import Breadcrumb from '@theme/components/Breadcrumb'
import Monster from '@theme/components/Monster'
import Edit from '@theme/components/Edit'
export default {
name: 'MonsterLayout',
components: {
Breadcrumb,
Monster
Monster,
Edit
},
computed: {
isMonsterInBestiary () {
let isInBestiary = false
for (let s of this.$store.state.myMonsters.monsters) {
if (s.key == this.$page.key) {
isInBestiary = true
}
}
return isInBestiary
},
displayToggleMonsterButton () {
if (this.isMonsterInBestiary) {
return 'Supprimer de mon grimoire'
}
return 'Ajouter à mon grimoire'
}
},
methods: {
toggleMonsterInBestiary () {
if (this.isMonsterInBestiary) {
this.$store.commit('myMonsters/removeMonster', this.$page)
} else {
this.$store.commit('myMonsters/addMonster', this.$page)
}
}
},
mounted () {

View file

@ -1,7 +1,11 @@
<template>
<div class="monsters">
<Breadcrumb />
<div class="d-flex align-center mb-4">
<Breadcrumb class="mr-auto" />
<v-btn color="primary" class="mr-4" depressed link to="/creation-de-monstre-pnj/"><v-icon left>mdi-plus</v-icon> Créer un monstre</v-btn>
<v-btn color="primary" depressed link to="/mon-bestiaire/">Mon bestiaire</v-btn>
</div>
<h1>Bestiaire</h1>
@ -16,6 +20,10 @@
:search="search"
>
<template v-slot:item.isInBestiary="{ item }">
<v-simple-checkbox off-icon="mdi-bookmark-outline" on-icon="mdi-bookmark" @input="toggleMonsterInBestiary(item)" :value="isMonsterInBestiary(item)"></v-simple-checkbox>
</template>
<template v-slot:item.title="{ item }">
<router-link :to="{ path: item.path }" class="subtitle-2">{{ item.title }}</router-link>
</template>
@ -51,6 +59,7 @@ export default {
sortBy: 'title',
sortDesc: false,
headers: [
{ text: "", align: 'center', sortable: false, value: 'isInBestiary' },
{ text: "Nom", align: 'start', sortable: true, value: 'title' },
{ text: "ID", align: 'center', sortable: true, value: 'frontmatter.challenge' },
{ text: "Type", align: 'start', sortable: false, value: 'frontmatter.type' },
@ -153,6 +162,22 @@ export default {
methods: {
displayList (list) { return list.join(', ') },
displayChallenge (challenge) { return displayChallenge(challenge) },
isMonsterInBestiary (monster) {
let isInBestiary = false
for (let m of this.$store.state.myMonsters.monsters) {
if (m.key == monster.key) {
isInBestiary = true
}
}
return isInBestiary
},
toggleMonsterInBestiary (monster) {
if (this.isMonsterInBestiary(monster)) {
this.$store.commit('myMonsters/removeMonster', monster)
} else {
this.$store.commit('myMonsters/addMonster', monster)
}
}
},
mounted () {

View file

@ -0,0 +1,96 @@
<template>
<div class="my-magic-items">
<div class="d-flex align-center mb-4 d-print-none">
<Breadcrumb class="mr-auto" />
<v-btn color="primary" depressed link to="/liste-objets-magiques/">Liste des objets magiques</v-btn>
</div>
<h1>Mes objets magiques</h1>
<div class="my-4 d-flex flex-wrap d-print-none">
<v-btn outlined color="accent" class="mr-1 mb-1" depressed @click="print"><v-icon>mdi-printer</v-icon> Imprimer</v-btn>
<v-btn outlined color="accent" class="mr-1 mb-1" depressed @click="download"><v-icon>mdi-file-download</v-icon> Sauvegarder</v-btn>
<v-btn outlined color="accent" class="mr-1 mb-1" depressed :loading="isUploading" @click="onUploadClick">
<v-icon left>mdi-file-upload</v-icon> Charger
</v-btn>
<input ref="uploader" class="d-none" type="file" @change="upload">
<v-btn outlined color="error" class="mb-1" depressed @click="$store.commit('myMagicItems/resetMagicItems')"><v-icon>mdi-delete</v-icon> Effacer les objets magiques</v-btn>
</div>
<MyMagicItems />
</div>
</template>
<script>
import Breadcrumb from '@theme/components/Breadcrumb'
import MyMagicItems from '@theme/components/MyMagicItems'
import { saveAs } from 'file-saver'
export default {
name: 'MyMagicItemsLayout',
components: {
Breadcrumb,
MyMagicItems
},
data () {
return {
isUploading: false
}
},
methods: {
download () {
saveAs(new Blob([JSON.stringify(this.$store.state.myMagicItems.magicItems)], {
type: "text/plain;charset=utf-8"
}), "objets-magiques.json")
},
upload (e) {
let file = e.target.files[0]
let reader = new FileReader()
let self = this
reader.onload = function() {
let result = JSON.parse(reader.result)
let isValid = true
if (result.length >= 1) {
for (var s of result) {
if (s.pid !== 'magicItem') {
isValid = false
}
}
}
if (isValid) {
self.$store.commit('myMagicItems/setMagicItems', result)
}
}
reader.readAsText(file)
},
onUploadClick () {
this.isUploading = true
window.addEventListener('focus', () => {
this.isUploading = false
}, { once: true })
this.$refs.uploader.click()
},
print () {
window.print()
}
},
mounted () {
this.$store.commit('setHasRightDrawer', false)
this.$store.commit('setRightDrawer', this.$vuetify.breakpoint.lgAndUp)
this.$store.commit('setInRightDrawer', null)
}
}
</script>
<style>
</style>

View file

@ -0,0 +1,96 @@
<template>
<div class="my-monsters">
<div class="d-flex align-center mb-4 d-print-none">
<Breadcrumb class="mr-auto" />
<v-btn color="primary" depressed link to="/bestiaire/">Bestiaire</v-btn>
</div>
<h1>Mon bestiaire</h1>
<div class="my-4 d-flex flex-wrap d-print-none">
<v-btn outlined color="accent" class="mr-1 mb-1" depressed @click="print"><v-icon>mdi-printer</v-icon> Imprimer</v-btn>
<v-btn outlined color="accent" class="mr-1 mb-1" depressed @click="download"><v-icon>mdi-file-download</v-icon> Sauvegarder</v-btn>
<v-btn outlined color="accent" class="mr-1 mb-1" depressed :loading="isUploading" @click="onUploadClick">
<v-icon left>mdi-file-upload</v-icon> Charger
</v-btn>
<input ref="uploader" class="d-none" type="file" @change="upload">
<v-btn outlined color="error" class="mb-1" depressed @click="$store.commit('myMonsters/resetMonsters')"><v-icon>mdi-delete</v-icon> Effacer le bestiaire</v-btn>
</div>
<MyMonsters />
</div>
</template>
<script>
import Breadcrumb from '@theme/components/Breadcrumb'
import MyMonsters from '@theme/components/MyMonsters'
import { saveAs } from 'file-saver'
export default {
name: 'MyMonstersLayout',
components: {
Breadcrumb,
MyMonsters
},
data () {
return {
isUploading: false
}
},
methods: {
download () {
saveAs(new Blob([JSON.stringify(this.$store.state.myMonsters.monsters)], {
type: "text/plain;charset=utf-8"
}), "bestiaire.json")
},
upload (e) {
let file = e.target.files[0]
let reader = new FileReader()
let self = this
reader.onload = function() {
let result = JSON.parse(reader.result)
let isValid = true
if (result.length >= 1) {
for (var s of result) {
if (s.pid !== 'monster') {
isValid = false
}
}
}
if (isValid) {
self.$store.commit('myMonsters/setMonsters', result)
}
}
reader.readAsText(file)
},
onUploadClick () {
this.isUploading = true
window.addEventListener('focus', () => {
this.isUploading = false
}, { once: true })
this.$refs.uploader.click()
},
print () {
window.print()
}
},
mounted () {
this.$store.commit('setHasRightDrawer', false)
this.$store.commit('setRightDrawer', this.$vuetify.breakpoint.lgAndUp)
this.$store.commit('setInRightDrawer', null)
}
}
</script>
<style>
</style>

View file

@ -0,0 +1,96 @@
<template>
<div class="my-spells">
<div class="d-flex align-center mb-4 d-print-none">
<Breadcrumb class="mr-auto" />
<v-btn color="primary" depressed link to="/grimoire/">Grimoire</v-btn>
</div>
<h1>Mon grimoire</h1>
<div class="my-4 d-flex flex-wrap d-print-none">
<v-btn outlined color="accent" class="mr-1 mb-1" depressed @click="print"><v-icon>mdi-printer</v-icon> Imprimer</v-btn>
<v-btn outlined color="accent" class="mr-1 mb-1" depressed @click="download"><v-icon>mdi-file-download</v-icon> Sauvegarder</v-btn>
<v-btn outlined color="accent" class="mr-1 mb-1" depressed :loading="isUploading" @click="onUploadClick">
<v-icon left>mdi-file-upload</v-icon> Charger
</v-btn>
<input ref="uploader" class="d-none" type="file" @change="upload">
<v-btn outlined color="error" class="mb-1" depressed @click="$store.commit('mySpells/resetSpells')"><v-icon>mdi-delete</v-icon> Effacer le grimoire</v-btn>
</div>
<MySpells />
</div>
</template>
<script>
import Breadcrumb from '@theme/components/Breadcrumb'
import MySpells from '@theme/components/MySpells'
import { saveAs } from 'file-saver'
export default {
name: 'MySpellsLayout',
components: {
Breadcrumb,
MySpells
},
data () {
return {
isUploading: false
}
},
methods: {
download () {
saveAs(new Blob([JSON.stringify(this.$store.state.mySpells.spells)], {
type: "text/plain;charset=utf-8"
}), "grimoire.json")
},
upload (e) {
let file = e.target.files[0]
let reader = new FileReader()
let self = this
reader.onload = function() {
let result = JSON.parse(reader.result)
let isValid = true
if (result.length >= 1) {
for (var s of result) {
if (s.pid !== 'spell') {
isValid = false
}
}
}
if (isValid) {
self.$store.commit('mySpells/setSpells', result)
}
}
reader.readAsText(file)
},
onUploadClick () {
this.isUploading = true
window.addEventListener('focus', () => {
this.isUploading = false
}, { once: true })
this.$refs.uploader.click()
},
print () {
window.print()
}
},
mounted () {
this.$store.commit('setHasRightDrawer', false)
this.$store.commit('setRightDrawer', this.$vuetify.breakpoint.lgAndUp)
this.$store.commit('setInRightDrawer', null)
}
}
</script>
<style>
</style>

View file

@ -1,7 +1,12 @@
<template>
<div class="spell">
<Breadcrumb />
<Spell />
<div class="d-flex align-center mb-4 d-print-none">
<Breadcrumb class="mr-auto" />
<v-btn color="primary" class="mr-4" depressed link to="/creation-de-sort/"><v-icon left>mdi-plus</v-icon> Créer un sort</v-btn>
<v-btn :outlined="!isSpellInSpellBook" color="accent" class="mr-4" depressed @click="toggleSpellInSpellBook"><v-icon>mdi-book</v-icon> {{ displayToggleSpellButton }}</v-btn>
<v-btn color="primary" class="mr-4" depressed link to="/mon-grimoire/">Mon Grimoire</v-btn>
</div>
<Spell :spell="$page" />
<Edit />
</div>
</template>
@ -20,6 +25,35 @@ export default {
Edit
},
computed: {
isSpellInSpellBook () {
let isInSpellBook = false
for (let s of this.$store.state.mySpells.spells) {
if (s.key == this.$page.key) {
isInSpellBook = true
}
}
return isInSpellBook
},
displayToggleSpellButton () {
if (this.isSpellInSpellBook) {
return 'Supprimer de mon grimoire'
}
return 'Ajouter à mon grimoire'
}
},
methods: {
toggleSpellInSpellBook () {
if (this.isSpellInSpellBook) {
this.$store.commit('mySpells/removeSpell', this.$page)
} else {
this.$store.commit('mySpells/addSpell', this.$page)
}
}
},
mounted () {
this.$store.commit('setHasRightDrawer', false)
this.$store.commit('setRightDrawer', this.$vuetify.breakpoint.lgAndUp)

View file

@ -1,10 +1,15 @@
<template>
<div class="spells">
<Breadcrumb />
<div class="d-flex align-center mb-4">
<Breadcrumb class="mr-auto" />
<v-btn color="primary" class="mr-4" depressed link to="/creation-de-sort/"><v-icon left>mdi-plus</v-icon> Créer un sort</v-btn>
<v-btn color="primary" depressed link to="/mon-grimoire/">Mon grimoire</v-btn>
</div>
<h1>Grimoire</h1>
<v-data-table
class="data-table"
:headers="headers"
@ -16,6 +21,10 @@
:search="search"
>
<template v-slot:item.isInSpellBook="{ item }">
<v-simple-checkbox off-icon="mdi-bookmark-outline" on-icon="mdi-bookmark" @input="toggleSpellInSpellBook(item)" :value="isSpellInSpellBook(item)"></v-simple-checkbox>
</template>
<template v-slot:item.title="{ item }">
<router-link :to="{ path: item.path }" class="subtitle-2">{{ item.title }}</router-link>
</template>
@ -61,6 +70,7 @@ export default {
sortBy: 'title',
sortDesc: false,
headers: [
{ text: "", align: 'center', sortable: false, value: 'isInSpellBook' },
{ text: "Nom", align: 'start', sortable: true, value: 'title' },
{ text: "Niveau", align: 'center', sortable: true, value: 'frontmatter.level' },
{ text: "École", align: 'start', sortable: false, value: 'frontmatter.school' },
@ -172,6 +182,25 @@ export default {
}
},
methods: {
isSpellInSpellBook (spell) {
let isInSpellBook = false
for (let s of this.$store.state.mySpells.spells) {
if (s.key == spell.key) {
isInSpellBook = true
}
}
return isInSpellBook
},
toggleSpellInSpellBook (spell) {
if (this.isSpellInSpellBook(spell)) {
this.$store.commit('mySpells/removeSpell', spell)
} else {
this.$store.commit('mySpells/addSpell', spell)
}
}
},
mounted () {
this.$store.commit('setDrawer', this.$vuetify.breakpoint.lgAndUp)
this.$store.commit('setHasRightDrawer', true)

View file

@ -4,6 +4,27 @@
font-family: serif;
padding-top: 0;
margin-top: 0;
overflow: visible !important;
height: auto !important;
}
.d-flex {
display: block !important;
}
.scroll-y {
overflow: visible !important;
height: auto !important;
}
.application {
display: block !important;
}
.application--wrap {
display: block !important;
}
.content--wrap {
display: block !important;
}
.p2c {

View file

@ -16,7 +16,7 @@
}
.theme--light {
h1, h2 {
h1, h2, .v-card__title {
color: $color-dragon;
}

View file

@ -73,3 +73,19 @@ export function setBooleanMutation (param, store, mutation) {
store.commit(mutation, value)
}
}
export function sortByString (a, b, direction = 'ASC') {
const titleA = a.toUpperCase()
const titleB = b.toUpperCase()
let comparison = 0
if (titleA > titleB) {
comparison = 1
} else if (titleA < titleB) {
comparison = -1
}
if (direction == 'ASC') {
return comparison
} else {
return comparison * -1
}
}

View file

@ -57,7 +57,11 @@ export function displayMonsterTypeSizeAlignment (monster, hideAlignment = false,
result += ' de taille ' + monster.size
}
if (!hideAlignment) {
result += ', ' + monster.alignment
if (monster.alignment) {
result += ', ' + monster.alignment
} else {
result += ', non alignée'
}
}
if (showChallenge) {
result += ', Dangerosité : ' + displayChallenge(monster.challenge)

View file

@ -13,7 +13,7 @@ title: Magicien
|**4**|+2|[Amélioration de caractéristiques](#amelioration-de-caracteristiques)|4|4|3|-|-|-|-|-|-|-|
|**5**|+3|-|4|4|3|2|-|-|-|-|-|-|
|**6**|+3|[Aptitude de tradition arcanique](#tradition-arcanique)|4|4|3|3|-|-|-|-|-|-|
|**7**|+3|-|3|4|3|3|1|-|-|-|-|-|
|**7**|+3|-|4|4|3|3|1|-|-|-|-|-|
|**8**|+3|[Amélioration de caractéristiques](#amelioration-de-caracteristiques)|4|4|3|3|2|-|-|-|-|-|
|**9**|+4|-|4|4|3|3|3|1|-|-|-|-|
|**10**|+4|[Aptitude de tradition arcanique](#tradition-arcanique)|5|4|3|3|3|2|-|-|-|-|

View file

@ -14,7 +14,7 @@ title: Rôdeur
|**5**|+3|[Attaque supplémentaire](#attaque-supplementaire)|4|4|2|-|-|-|
|**6**|+3|[Amélioration d'ennemi juré](#ennemi-jure)|4|4|2|-|-|-|
|**7**|+3|[Aptitude d'archétype de rôdeur](#archetype-de-rodeur)|5|4|3|-|-|-|
|**8**|+3|[Amélioration de caractéristiques](#amelioration-de-caracteristiques)|5|4|3|-|-|-|
|**8**|+3|[Amélioration de caractéristiques](#amelioration-de-caracteristiques), [Traversée des terrains](#traversee-des-terrains)|5|4|3|-|-|-|
|**9**|+4|-|6|4|3|2|-|-|
|**10**|+4|[Amélioration d'explorateur né](#explorateur-ne)|6|4|3|2|-|-|
|**11**|+4|[Aptitude d'archétype de rôdeur](#archetype-de-rodeur)|7|4|3|3|-|-|

View file

@ -12,7 +12,9 @@
"dependencies": {
"@mdi/font": "^5.0.45",
"@vuepress/plugin-blog": "^1.9.2",
"@vuepress/plugin-google-analytics": "^1.4.1",
"@vuepress/plugin-pwa": "^1.4.1",
"file-saver": "^2.0.2",
"flexsearch": "nextapps-de/flexsearch",
"js-cookie": "^2.2.1",
"markdown-it-div": "^1.1.0",
@ -22,6 +24,7 @@
"sass-loader": "^8.0.2",
"slugify": "^1.4.0",
"vue": "^2.6.11",
"vue-masonry-css": "^1.0.3",
"vuepress": "^1.4.0",
"vuepress-plugin-clean-urls": "^1.1.1",
"vuepress-plugin-dehydrate": "^1.1.3",

View file

@ -1110,6 +1110,11 @@
vuepress-plugin-mailchimp "^1.4.1"
vuepress-plugin-sitemap "^2.3.1"
"@vuepress/plugin-google-analytics@^1.4.1":
version "1.4.1"
resolved "https://registry.yarnpkg.com/@vuepress/plugin-google-analytics/-/plugin-google-analytics-1.4.1.tgz#d537e7ed026e7808d1d9d186880e595b04fe0868"
integrity sha512-s43V5QHdTz0ayfy5vZrfMPpZzJBsj9L79TaxyMux1jOviS7oeWqkvNSblaHwP4Y8BxISehsKte8qsblQEN3zvQ==
"@vuepress/plugin-last-updated@1.4.1":
version "1.4.1"
resolved "https://registry.yarnpkg.com/@vuepress/plugin-last-updated/-/plugin-last-updated-1.4.1.tgz#89a9a4cbc242f9e0c5d380f73edb85ac4a7186c6"
@ -3498,6 +3503,11 @@ file-loader@^3.0.1:
loader-utils "^1.0.2"
schema-utils "^1.0.0"
file-saver@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/file-saver/-/file-saver-2.0.2.tgz#06d6e728a9ea2df2cce2f8d9e84dfcdc338ec17a"
integrity sha512-Wz3c3XQ5xroCxd1G8b7yL0Ehkf0TC9oYC6buPFkNnU9EnaPlifeAFCyCh+iewXTyFRcg0a6j3J7FmJsIhlhBdw==
file-uri-to-path@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd"
@ -8310,6 +8320,11 @@ vue-loader@^15.7.1:
vue-hot-reload-api "^2.3.0"
vue-style-loader "^4.1.0"
vue-masonry-css@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/vue-masonry-css/-/vue-masonry-css-1.0.3.tgz#a5e7bb248570eeedcd358637d3f1f4fc7f0c5f86"
integrity sha512-viecHQiHVLez7HlYUQsv1wJb2MT/RDSzkDp6m3In41vPrk6OsBmT2qRE8LZqYIA4daIwrnx/Xm8h4fjOpuE3hw==
vue-property-decorator@^8.1.1:
version "8.4.1"
resolved "https://registry.yarnpkg.com/vue-property-decorator/-/vue-property-decorator-8.4.1.tgz#fd8045b8678e1348fed57f9149279e00e404ed38"