This commit is contained in:
2026-02-23 04:12:17 +04:00
parent 61f6808eb5
commit 4e3ef93cad
2 changed files with 405 additions and 52 deletions

View File

@@ -476,8 +476,7 @@ const manualLayer = [
align-items: center;
}
.med-tag {
font-size: 0.9rem;
filter: drop-shadow(0 0 2px rgba(255, 255, 255, 0.5));
font-size: 0.6rem;
cursor: help;
}
</style>

View File

@@ -4,23 +4,22 @@ import Layout from "../layouts/Layout.astro";
import Card from "../components/Card.astro";
import EventCard from "../components/EventCard.astro";
// 1. Récupération des deux types de contenu
// 1. Récupération des données
const allHumans = await getCollection("humans", ({ id }) => {
return id.startsWith("latchimynicolas/");
});
const allEvents = await getCollection("events");
// 2. Tri par date décroissante pour les humains
// 2. Tris temporels
const sortedAll = allHumans.sort(
(a, b) => new Date(b.data.date).getTime() - new Date(a.data.date).getTime(),
);
// 3. Récupération du dernier événement (ex: Camping Châteauroux)
const latestEvent = allEvents.sort(
(a, b) => new Date(b.data.date).getTime() - new Date(a.data.date).getTime(),
)[0];
// 4. Fonction de calcul de Streak (Série) [cite: 94]
// 3. Logique de Streak (Série)
const getStreak = (name, entries) => {
const userDates = entries
.filter((e) => e.data.name === name)
@@ -35,7 +34,6 @@ const getStreak = (name, entries) => {
.reverse();
let streak = 0;
let today = new Date();
// Utiliser Date.UTC pour aligner la date locale avec le format ISO UTC des collections
let checkDate = new Date(
Date.UTC(today.getFullYear(), today.getMonth(), today.getDate()),
);
@@ -49,8 +47,9 @@ const getStreak = (name, entries) => {
} else {
if (i === 0) {
checkDate.setUTCDate(checkDate.getUTCDate() - 1);
const yesterdayStr = checkDate.toISOString().split("T")[0];
if (uniqueDates.includes(yesterdayStr)) {
if (
uniqueDates.includes(checkDate.toISOString().split("T")[0])
) {
todaySkipped = true;
continue;
}
@@ -59,7 +58,6 @@ const getStreak = (name, entries) => {
}
}
// Malus HYG [cite: 103, 106]
const shiftDays = todaySkipped ? 1 : 0;
const last7Days = Array.from({ length: 7 }, (_, j) => {
let d = new Date(
@@ -77,7 +75,7 @@ const getStreak = (name, entries) => {
return streak;
};
// 5. Unicité par utilisateur [cite: 107]
// 4. Sélection de la carte principale
const latestPerUser = Array.from(
sortedAll
.reduce((map, obj) => {
@@ -86,10 +84,22 @@ const latestPerUser = Array.from(
}, new Map())
.values(),
);
// 5. CALCUL DE SYNERGIE
const activeSynergies = latestPerUser.map((user) => {
const matchingManifestation = user.data.manifestations?.find(
(m) => m.cercle === latestEvent?.data.circle,
);
return {
userName: user.data.name,
hasSynergy: !!matchingManifestation,
cercle: latestEvent?.data.circle,
type: matchingManifestation?.type,
};
});
---
<Layout title="Derniers Souffles">
<h1 style="text-align: center;">Ambient</h1>
<Layout title="Dashboard & Codex">
<header class="minimal-header">
<nav>
<a href="/collection" class="menu-link">
@@ -98,53 +108,312 @@ const latestPerUser = Array.from(
</nav>
</header>
<main class="hero-flex">
{/* Affichage des cartes Humains (Hebel) [cite: 110] */}
{
latestPerUser.map((entry) => {
const currentStreak = getStreak(entry.data.name, allHumans);
return (
<main>
<h1 class="main-title">Ambient</h1>
<section class="hero-flex">
{
latestPerUser.map((entry) => {
const currentStreak = getStreak(entry.data.name, allHumans);
return (
<div class="card-focus">
<Card
frontmatter={entry.data}
streakCount={currentStreak}
/>
<div class="entry-meta">
<p>
Dernier souffle :{" "}
{new Date(
entry.data.date,
).toLocaleDateString("fr-FR")}
</p>
</div>
</div>
);
})
}
{
latestEvent && (
<div class="card-focus">
<Card
frontmatter={entry.data}
streakCount={currentStreak}
/>
<EventCard frontmatter={latestEvent.data} />
<div class="entry-meta">
<p>
Dernier souffle :{" "}
{new Date(entry.data.date).toLocaleDateString(
"fr-FR",
)}
</p>
<p>Projet Actif : {latestEvent.data.type}</p>
</div>
</div>
);
})
}
)
}
</section>
{/* Insertion de la carte Événement */}
{
latestEvent && (
<div class="card-focus">
<EventCard frontmatter={latestEvent.data} />
<div class="entry-meta">
<p>Evénement, Projet, IRL</p>
<hr class="separator" />
<section class="system-rules">
<header class="rules-header">
<h2>📜 Codex des Mécaniques</h2>
<p>
Analyse de la cohésion entre Obsidian (Data) et Astro (TCG)
</p>
</header>
<div class="rules-layout">
<div class="rule-category">
<h3>🔄 Synergie Event & Card</h3>
<div class="synergy-list">
{
activeSynergies.map((s) => (
<div class="synergy-status">
<strong>{s.userName} :</strong>
{s.hasSynergy ? (
<span class="status-ok">
✅ Résonance via {s.cercle} (
{s.type})
</span>
) : (
<span class="status-ko">
❌ Pas de résonance avec{" "}
{latestEvent?.data.title}
</span>
)}
</div>
))
}
</div>
<p
class="rule-hint"
style="margin-top: 10px; font-style: italic; font-size: 0.85rem;"
>
Règle : Match de cercle = "Effet de Résonance" (+2 ATK).
</p>
</div>
)
}
<div class="rule-category">
<h3>🌍 Biomes Actifs</h3>
<ul>
<li>
<strong>ÉTANG :</strong> Somatique. Activé si <code
>Stress &gt; 7</code
>, <code>Hallu &ge; 2</code> ou <code
>Harmonie &le; -3</code
>.
</li>
<li>
<strong>BELOUVE :</strong> Harmonie. Activé si 3+ cercles
uniques et <code>SAN</code> présent.
</li>
<li>
<strong>OCEAN :</strong> Flux. Activé si <code
>SOC &gt; PRO</code
> ou <code>FLX &gt; 2</code>.
</li>
</ul>
</div>
<div class="rule-category">
<h3>🛠️ Établi de Crafting</h3>
<ul>
<li>
<strong>ItemCard :</strong> Vélo (Conversion Stress/Énergie),
Médication (Stabilité).
</li>
<li>
<strong>Global Buff :</strong> Streak 10+ = Mode "Clairvoyance".
</li>
<li>
<strong>Jauge :</strong> "Rupture de Routine" (Analyse
des déviances).
</li>
</ul>
</div>
</div>
</section>
<hr class="separator" />
<section class="system-rules">
<header class="rules-header">
<h2>📖 Manuel de Jeu</h2>
<p>Répertoire complet des mécaniques et calculs du moteur</p>
</header>
<div class="rules-layout">
<div class="rule-category">
<h3>✨ Rareté & Évolution</h3>
<ul>
<li>
<strong>SHINY :</strong> Streak &ge; 3 + 3 cercles uniques.
</li>
<li>
<strong>LÉGENDAIRE :</strong> Streak &ge; 10 + Harmonie
&ge; 4.
</li>
<li>
<strong>MYTHIQUE :</strong> Streak &ge; 20 + 0 Hallucinations.
</li>
</ul>
</div>
<div class="rule-category">
<h3>📉 Malus & Maintenance</h3>
<ul>
<li>
<strong>HYG-Penalty :</strong> Si moins de 3 logs "HYG"
sur 7 jours, le Streak diminue de 1 (Maintenance préventive).
</li>
<li>
<strong>Somatic-Lock :</strong> Si Stress &gt; 9, l'attaque
est bloquée à 1.
</li>
</ul>
</div>
<div class="rule-category">
<h3>🎭 Objets & Équipements</h3>
<ul>
<li>
<strong>VÉLO :</strong> Convertit 2 points de Stress en
1 point d'Énergie au petit matin.
</li>
<li>
<strong>ARIPIPRAZOLE :</strong> Fixe Hallucinations à
0. Ajoute le trait "Stabilité".
</li>
</ul>
</div>
</div>
</section>
<hr class="separator" />
<section class="system-rules">
<header class="rules-header">
<h2>📖 Manuel de Jeu : TCG (Ruleset Complet)</h2>
<p>Traduction technique des algorithmes du moteur de jeu</p>
</header>
<div class="rules-layout">
<div class="rule-category">
<h3>⚔️ Système de Combat</h3>
<ul>
<li>
<strong>Puissance (ATK) :</strong>
<code
>(PRO × 2) + (Motiv/4) + (Énergie/3) + Bonus
Harmonie + StreakBonus</code
>.
<em
>Le travail (PRO) est ton multiplicateur
principal.</em
>
</li>
<li>
<strong>Résilience (DEF) :</strong>
<code
>10 + (Hygiène/2) - Stress - Hallu - Pénalité
Sommeil + StreakBonus</code
>.
<em
>L'hygiène est ton armure, le stress ta faille.</em
>
</li>
<li>
<strong>Coût d'Invocation :</strong> Nombre total de manifestations
divisé par 2 (arrondi au supérieur).
</li>
</ul>
</div>
<div class="rule-category">
<h3>📈 Séries & Rareté (Streak)</h3>
<ul>
<li>
<strong>Streak Bonus :</strong> +1 ATK/DEF tous les 7
jours de série consécutifs.
</li>
<li>
<strong>Éveil Shiny :</strong> Activé dès 3 jours de série
+ 3 cercles différents activés.
</li>
<li>
<strong>Échelle de Rareté :</strong>
<br />• <strong>PEU COMMUNE :</strong> 3 jours de série.
<br />• <strong>RARE :</strong> 6 jours de série.
<br />• <strong>LÉGENDAIRE :</strong> 10 jours + Harmonie
&ge; 4.
<br />• <strong>MYTHIQUE :</strong> 20 jours + 0 Hallucinations.
</li>
</ul>
</div>
<div class="rule-category">
<h3>🌍 Influence des Biomes</h3>
<ul>
<li>
<strong>ÉTANG (L'Inerte) :</strong> Priorité Somatique.
S'active si <code>Stress &ge; 7</code>, <code
>Hallu &ge; 2</code
> ou <code>Harmonie &le; -3</code>.
</li>
<li>
<strong>BELOUVE (La Racine) :</strong> État d'Harmonie.
S'active si 3+ cercles sont présents avec au moins un
log <strong>SAN</strong>.
</li>
<li>
<strong>OCEAN (Le Flux) :</strong> S'active si le Social
(SOC) domine le Pro ou si le Flux (FLX) est &gt; 2.
</li>
<li>
<strong>FOURNAISE (La Forge) :</strong> S'active par dominance
technique (PRO &ge; 2 et PRO &ge; SOC).
</li>
<li>
<strong>SAVANE (La Clarté) :</strong> Biome d'équilibre
par défaut.
</li>
</ul>
</div>
<div class="rule-category">
<h3>🔧 Maintenance & Malus</h3>
<ul>
<li>
<strong>Pénalité Sommeil :</strong> Si (Sommeil - Malus
Travail) &lt; 6h, ta DEF baisse de 2.
<em>(Le malus travail s'active si PRO &ge; 5).</em>
</li>
<li>
<strong>Harmonie des Cercles :</strong> Posséder les 5
cercles (SAN, FLX, PRO, SOC, HYG) sur un log octroie un
bonus massif de **+2** au Score d'Harmonie.
</li>
<li>
<strong>Règle HYG (Prévention) :</strong> Si moins de
3 logs "HYG" sont détectés sur les 7 derniers jours, la
série (Streak) est amputée d'un point de pénalité.
</li>
</ul>
</div>
</div>
</section>
</main>
</Layout>
<style>
/* Tes styles restent identiques pour préserver la mise en page [cite: 115, 119, 120] */
.main-title {
text-align: center;
font-family: "Philosopher", serif;
font-size: 2.5rem;
margin-top: 40px;
color: #1e293b;
}
.minimal-header {
position: fixed;
top: 50px;
right: 0;
padding: 20px 30px;
top: 20px;
right: 20px;
z-index: 100;
}
.menu-link {
display: flex;
align-items: center;
@@ -158,22 +427,107 @@ const latestPerUser = Array.from(
border-radius: 50px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05);
backdrop-filter: blur(5px);
transition: transform 0.2s ease;
}
.hero-flex {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 20px;
padding: 100px 20px;
width: 100%;
min-height: 100vh;
gap: 40px;
padding: 60px 20px;
min-height: 60vh;
}
.card-focus {
display: flex;
flex-direction: column;
align-items: center;
gap: 15px;
flex: 0 1 320px;
transition: transform 0.3s ease;
}
.entry-meta {
font-family: "Philosopher", serif;
font-size: 0.9rem;
color: #64748b;
}
.separator {
border: 0;
height: 1px;
background-image: linear-gradient(
to right,
transparent,
rgba(0, 0, 0, 0.1),
transparent
);
margin: 40px 0;
}
/* Codex Style */
.codex {
max-width: 1100px;
margin: 0 auto;
padding: 40px;
background: #f8fafc;
border-radius: 24px;
border: 1px solid #e2e8f0;
}
.codex-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
}
.codex-box {
background: white;
padding: 20px;
border-radius: 16px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.02);
}
.synergy-active {
border: 2px solid #8b5cf6;
}
.status-ok {
color: #10b981;
font-weight: bold;
}
.status-ko {
color: #94a3b8;
}
/* System Rules Style */
.system-rules {
max-width: 1100px;
margin: 40px auto 100px auto;
padding: 20px;
font-family: "Philosopher", serif;
}
.rules-layout {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 30px;
margin-top: 20px;
}
.rule-category h3 {
border-bottom: 1px solid #cbd5e1;
padding-bottom: 5px;
margin-bottom: 15px;
color: #334155;
}
.rule-category ul {
list-style: none;
padding: 0;
}
.rule-category li {
margin-bottom: 12px;
font-size: 0.95rem;
line-height: 1.4;
}
</style>