classe et espece

This commit is contained in:
2026-02-16 01:38:49 +04:00
parent 89c6dfeab4
commit 93747cbc93
7 changed files with 215 additions and 38 deletions

View File

@@ -41,4 +41,23 @@ const especes = defineCollection({
.passthrough(),
});
export const collections = { journal, classes, especes };
const personnages = defineCollection({
loader: obsidianLoader("personnages"),
schema: z
.object({
nom: z.string(),
espece: z.string(),
niveau_global: z.number().default(1),
classes_detail: z
.array(
z.object({
nom: z.string(),
niveau: z.number(),
}),
)
.default([]),
})
.passthrough(),
});
export const collections = { journal, classes, especes, personnages };

View File

@@ -0,0 +1,10 @@
---
nom: "Bulle"
espece: "fee"
niveau_global: 2
classes_detail:
- nom: "clerc"
niveau: 1
---
Fille de la lune et du savoir ancestral.

View File

@@ -0,0 +1,10 @@
---
nom: "G'Mas"
espece: "grung"
niveau_global: 2
classes_detail:
- nom: "moine"
niveau: 1
---
Fille de la lune et du savoir ancestral.

View File

@@ -0,0 +1,10 @@
---
nom: "Gurdill"
espece: "nain"
niveau_global: 2
classes_detail:
- nom: "barbare"
niveau: 1
---
Brute au grand coeur, forgé dans les montagnes.

View File

@@ -0,0 +1,10 @@
---
nom: "Jinn"
espece: "genasi"
niveau_global: 2
classes_detail:
- nom: "barde"
niveau: 1
---
Brute au grand coeur, forgé dans les montagnes.

View File

@@ -0,0 +1,10 @@
---
nom: "Nyrae"
espece: "tabaxi"
niveau_global: 2
classes_detail:
- nom: "druide"
niveau: 1
---
Brute au grand coeur, forgé dans les montagnes.

View File

@@ -4,22 +4,80 @@ import GameLayout from "../layouts/GameLayout.astro";
const classes = await getCollection("classes");
const especes = await getCollection("especes");
const personnages = await getCollection("personnages");
// Groupement pour les classes (Gestion multiclassage)
const classData = personnages.reduce((acc, p) => {
// On vérifie si classes_detail existe, sinon on simule un tableau avec la classe de base
const details = p.data.classes_detail || [
{ nom: p.data.classe, niveau: p.data.niveau_global || 1 },
];
details.forEach((c) => {
if (!c.nom) return;
const key = c.nom.toLowerCase();
if (!acc[key]) acc[key] = [];
acc[key].push({
nom: p.data.nom,
niveau_classe: c.niveau,
niveau_total: p.data.niveau_global || c.niveau,
});
});
return acc;
}, {});
// Groupement pour l'espèce
const especeData = personnages.reduce((acc, p) => {
if (!p.data.espece) return acc;
const key = p.data.espece.toLowerCase();
if (!acc[key]) acc[key] = [];
acc[key].push({
nom: p.data.nom,
niveau: p.data.niveau_global || 1,
});
return acc;
}, {});
---
<GameLayout title="Création de Personnage">
<div class="creation-container">
<!-- Section for Classes -->
<section class="character-select">
<h2 class="dnd-title">Choisissez votre Classe</h2>
<div class="dnd-bar"></div>
<p class="dnd-intro">
La classe définit vos compétences et votre rôle dans l'aventure.
</p>
<div class="class-grid">
{
classes.map((charClass) => (
classes.map((charClass) => {
const charactersInClass =
classData[charClass.id.toLowerCase()] || [];
return (
<div class="class-card">
<h3>{charClass.data.title}</h3>
<div class="bubble-container">
{charactersInClass.map((p) => (
<div class="notification-bubble">
<span class="p-name">{p.nom}</span>
<span class="p-lvl">
Niv.{p.niveau_classe}
</span>
{p.niveau_total >
p.niveau_classe && (
<span
class="multiclass-dot"
title="Multiclassé"
>
+
</span>
)}
</div>
))}
</div>
<p>{charClass.data.description}</p>
<a
href={`/classes/${charClass.id}`}
@@ -28,24 +86,39 @@ const especes = await getCollection("especes");
Détails
</a>
</div>
))
);
})
}
</div>
</section>
<!-- Section for Species -->
<section class="character-select">
<h2 class="dnd-title">Choisissez votre Espèce</h2>
<div class="dnd-bar"></div>
<p class="dnd-intro">
L'espèce influence votre apparence, votre histoire et certaines
de vos aptitudes.
L'espèce influence votre apparence et votre histoire.
</p>
<div class="class-grid">
{
especes.map((espece) => (
especes.map((espece) => {
const charactersInEspece =
especeData[espece.id.toLowerCase()] || [];
return (
<div class="class-card">
<h3>{espece.data.title}</h3>
<div class="bubble-container">
{charactersInEspece.map((p) => (
<div class="notification-bubble species">
<span class="p-name">{p.nom}</span>
<span class="p-lvl">
Niv.{p.niveau}
</span>
</div>
))}
</div>
<p>{espece.data.description}</p>
<a
href={`/especes/${espece.id}`}
@@ -54,7 +127,8 @@ const especes = await getCollection("especes");
Détails
</a>
</div>
))
);
})
}
</div>
</section>
@@ -95,12 +169,10 @@ const especes = await getCollection("especes");
.dnd-intro {
font-family: "Cinzel", serif;
font-size: 0.9rem;
margin-top: 1rem;
margin: 1rem auto;
opacity: 0.8;
text-align: center;
max-width: 600px;
margin-left: auto;
margin-right: auto;
}
.class-grid {
@@ -133,7 +205,47 @@ const especes = await getCollection("especes");
font-family: "Playfair Display", serif;
font-size: 1.8rem;
color: #c89b3c;
margin: 0 0 1rem 0;
margin: 0 0 0.5rem 0;
}
.bubble-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 6px;
margin-bottom: 1.5rem;
min-height: 28px;
}
.notification-bubble {
background: #3a352a;
color: white;
font-family: "Cinzel", serif;
font-size: 0.7rem;
padding: 2px 10px;
border-radius: 20px;
display: inline-flex;
align-items: center;
gap: 5px;
transition: transform 0.2s ease;
}
.notification-bubble:hover {
transform: scale(1.05);
cursor: pointer;
}
.p-lvl {
font-size: 0.6rem;
opacity: 0.8;
padding-left: 5px;
border-left: 1px solid rgba(255, 255, 255, 0.3);
}
.multiclass-dot {
color: #c89b3c;
font-weight: bold;
margin-left: 2px;
}
.class-card p {
@@ -152,12 +264,8 @@ const especes = await getCollection("especes");
border: 1px solid #c89b3c;
padding: 0.5rem 1rem;
border-radius: 3px;
cursor: pointer;
transition:
background-color 0.3s,
color 0.3s;
text-decoration: none;
display: inline-block;
transition: all 0.3s;
}
.select-button:hover {