v1.1.4 - ajout de nouvelles features et correction de bugs.

This commit is contained in:
mickael pol 2026-03-26 20:18:55 +01:00
parent 389e47d24a
commit d9f1d7cbb7
8 changed files with 1497 additions and 48 deletions

View File

@ -70,6 +70,24 @@ dd-timer/
## Changelog ## Changelog
### v1.1.4
- ✨ **Cible serenite par DD** — champ cible avec ETA en temps reel (calcul automatique baffeur/caresseur necessaire)
- ✨ **Cible niveau par DD** — champ cible avec ETA en temps reel (modele XP lineaire base sur le tier de la mangeoire)
- ✨ **Inventaire ♂/♀** — saisie du nombre de males et femelles par race dans l'inventaire
- ✨ **Contraintes de genre** — le calcul d'inventaire respecte la regle ♂+♀ obligatoire (plus de croisement ♂+♂ ou ♀+♀)
- ✨ **Bouton "Reinitialiser l'inventaire"** — remet a zero tous les stocks de DD (avec confirmation native)
- ✨ **Affichage du sexe des parents** — les resultats de croisement indiquent quel parent est ♂ et lequel est ♀
- 🎨 Serenite et XP reintegrees dans la barre de pills (comme endurance, amour, etc.)
- 🎨 Lignes dediees cible + ETA pour serenite et niveau sous les pills
- 🎨 Badges compacts pour les noms de jauges longs (baffeur, caresseur, mangeoire) sur les cartes DD
- 🎨 Zone de clic elargie sur les inputs ♂/♀ de l'inventaire
- 🎨 Padding et espacement corriges sur les cartes inventaire
- 🔧 Correction timer XP incorrect — affichait ~23h au lieu du temps reel
- 🔧 Correction defocus input reproducteur — l'input perdait le focus car oninput declenchait un rebuild DOM
- 🔧 Correction bebes produits — les bebes issus de croisements sont maintenant neutres (sexe inconnu) au lieu d'etre assignes ♂/♀ arbitrairement
- ⚡ Cache DOM `gel()` — les ~500+ appels getElementById par tick passent par un cache Map
- ⚡ Verification de mise a jour uniquement au demarrage (suppression du check toutes les heures)
### v1.1.3 ### v1.1.3
- ✨ **Reinitialisation des statistiques** — bouton dans l'onglet Statistiques pour remettre a zero tous les bebes, historiques et stats archivees (avec confirmation) - ✨ **Reinitialisation des statistiques** — bouton dans l'onglet Statistiques pour remettre a zero tous les bebes, historiques et stats archivees (avec confirmation)
- ✨ **Inputs intelligents** — les champs numeriques se vident au clic pour saisir facilement, et restaurent la valeur precedente si rien n'est change - ✨ **Inputs intelligents** — les champs numeriques se vident au clic pour saisir facilement, et restaurent la valeur precedente si rien n'est change

255
algorithmes.md Normal file
View File

@ -0,0 +1,255 @@
# Algorithmes de calcul — Minuteur Dragodinde
Ce document décrit tous les algorithmes utilisés dans l'application, expliqués simplement.
---
## 1. Système de tiers des jauges
Chaque jauge (baffeur, caresseur, foudroyeur, abreuvoir, dragofesse, mangeoire) a un **niveau** entre 0 et 100 000.
Ce niveau détermine un **tier** qui fixe la vitesse de conversion :
| Niveau de jauge | Tier | Taux (pts / 10 sec) |
|-----------------|------|---------------------|
| 0 40 000 | 1 | 10 |
| 40 001 70 000 | 2 | 20 |
| 70 001 90 000 | 3 | 30 |
| 90 001 100 000 | 4 | 40 |
**Principe** : La jauge se **vide** en donnant des points à la DD. Plus la jauge est haute, plus elle se vide vite (tier élevé = plus de points par tick).
Un "tick" = 10 secondes de timer.
---
## 2. Calcul des points gagnés dans le temps — `gainedIn(lvl, sec)`
**Question** : "Si ma jauge démarre au niveau `lvl` et tourne pendant `sec` secondes, combien de points ma DD gagne-t-elle ?"
**Algorithme** :
1. On part du haut (tier 4) et on descend
2. Pour chaque palier, on calcule combien de ticks on peut consommer avant de passer au palier en-dessous
3. On additionne les points de chaque palier traversé
**Exemple** : Jauge à 95 000, timer 60 sec (= 6 ticks)
- Tier 4 (90k100k) : 5 000 pts dispo ÷ 40/tick = 125 ticks possibles → on en utilise 6 → gain = 6 × 40 = **240 pts**
- Jauge restante : 95 000 - 240 = 94 760
La jauge descend au fur et à mesure qu'elle donne des points. Quand elle passe sous un seuil de tier, le taux ralentit.
---
## 3. Temps pour gagner X points — `timeToGain(lvl, pts)`
**Question** : "Combien de temps faut-il pour gagner `pts` points depuis une jauge au niveau `lvl` ?"
C'est l'inverse de `gainedIn`. On parcourt les paliers du haut vers le bas :
1. Pour chaque tier, combien de points peut-on donner avant de descendre au palier suivant ?
2. On prend le minimum entre les points disponibles et les points restants à donner
3. On calcule le temps correspondant : `ceil(points / taux) × 10 sec`
Si la jauge se vide complètement avant d'atteindre l'objectif → retourne `Infinity` (impossible).
---
## 4. Niveau de jauge après X secondes — `gaugeAfter(lvl, sec)`
**Question** : "Si ma jauge démarre à `lvl` et tourne `sec` secondes, à quel niveau sera-t-elle ?"
Même logique que `gainedIn`, mais au lieu de compter les points donnés, on soustrait directement du niveau de la jauge.
---
## 5. Temps écoulé — `elapsed(enc)`
Calcule les secondes écoulées depuis le démarrage du timer d'un enclos, en excluant les pauses :
```
Si en cours : (maintenant - démarrage - temps_en_pause) / 1000
Si en pause : (moment_pause - démarrage - temps_en_pause) / 1000
Si non démarré : 0
```
---
## 6. Calcul unifié d'une jauge — `computeGaugeLive(enc, dd, gid, el, started)`
C'est le **cœur du système**. Pour chaque DD et chaque jauge active, il calcule en temps réel :
- La stat estimée actuelle
- Si la cible est atteinte
- Le % de progression
- Le countdown restant
### Pour les jauges normales (sérénité, endurance, maturité, amour) :
1. On récupère le **snapshot** de la jauge et de la stat au moment du démarrage du timer
2. On calcule les points gagnés depuis le snapshot : `gainedIn(snapshotJauge, tempsÉcoulé)`
3. On applique la direction (`dir`) :
- `dir = +1` (caresseur, foudroyeur, abreuvoir, dragofesse) : stat monte
- `dir = -1` (baffeur) : stat descend
4. On clamp la stat dans ses bornes (ex: sérénité entre -5000 et +5000)
5. On calcule le temps total et le countdown via `timeToGain`
### Pour la mangeoire (XP) — modèle spécial :
L'XP utilise un **modèle linéaire constant** car le joueur est supposé **recharger** la jauge manuellement :
1. On détermine le taux fixe = `tierRate(snapshotJauge)` au démarrage
2. XP gagnée = `nombre_de_ticks × taux` (pas de dégression)
3. XP nécessaire = `xpForLevel(cible) - xpForLevel(départ)`
4. Countdown = `ceil(xp_restante / taux) × 10 sec`
**Pourquoi un modèle différent ?** Parce que pour les stats, la jauge se vide naturellement. Pour l'XP, le joueur remet des croquettes (recharge la mangeoire), donc le taux reste constant.
---
## 7. Table d'XP et niveaux
### Table XP_RAW
Dictionnaire de 200 entrées : `niveau → XP cumulatif total`.
Exemples :
- Niveau 1 → 0 XP
- Niveau 10 → 809 XP
- Niveau 50 → 34 365 XP
- Niveau 100 → 172 668 XP
- Niveau 200 → 867 582 XP
Les valeurs sont **cumulatives** (pas incrémentales).
### `xpForLevel(lvl)` : Retourne l'XP cumulatif pour atteindre le niveau `lvl`.
### `levelFromXp(xp)` : Retourne le plus haut niveau atteint avec `xp` points d'XP cumulatifs. Parcourt la table de 200 à 1 pour trouver le seuil.
---
## 8. ETA Sérénité — `calcSerenEta` / `calcSerenEtaLive`
### Version statique (`calcSerenEta`)
1. Calcule `diff = cible - sérénité actuelle`
2. Si `diff > 0` → besoin du caresseur (monte la sérénité)
3. Si `diff < 0` → besoin du baffeur (baisse la sérénité)
4. Temps = `timeToGain(niveauJauge, |diff|)`
### Version live (`calcSerenEtaLive`)
1. Utilise `computeGaugeLive` pour obtenir la sérénité estimée en temps réel
2. Recalcule le diff depuis cette estimation
3. Calcule le temps restant avec les points encore à parcourir
---
## 9. ETA Niveau — `calcLevelEta` / `calcLevelEtaLive`
### Version statique (`calcLevelEta`)
1. XP nécessaire = `xpForLevel(cible) - xpForLevel(niveauActuel)`
2. Taux fixe = `tierRate(niveauMangeoire)`
3. Temps = `ceil(xpNécessaire / taux) × 10 sec`
### Version live (`calcLevelEtaLive`)
1. Utilise `computeGaugeLive` pour obtenir le niveau estimé actuel
2. XP restante = `xpForLevel(cible) - xpForLevel(niveauEstimé)`
3. Taux fixe = `tierRate(snapshotMangeoire)`
4. Countdown = `ceil(xpRestante / taux) × 10 sec`
---
## 10. Countdown global d'un enclos — `enclosGlobalState(enc)`
**Question** : "Dans combien de temps TOUTES les DD de cet enclos auront atteint TOUTES leurs cibles ?"
1. Pour chaque DD × chaque jauge active → appeler `computeGaugeLive`
2. Prendre le **maximum** de tous les countdowns = le plus long à terminer
3. Compter combien de DD ont TOUTES leurs cibles atteintes (`ddDone`)
4. `allDone = true` si toutes les DD sont terminées
---
## 11. Arbre de réapprovisionnement — `calcAppro()`
**Question** : "Pour produire Q exemplaires de la race X, de quelles races et en quelles quantités ai-je besoin ?"
### Principe : décomposition récursive par génération
Chaque race de génération ≥ 2 est produite par le croisement de 2 races parentes (table `BREEDING_RECIPES`).
**Algorithme** :
1. On part de la race cible et de la quantité voulue
2. Pour chaque génération (de la plus haute à gen 2) :
- Pour chaque race nécessaire à cette génération :
- Calculer le nombre de couples nécessaires (voir §11.1)
- Ajouter les parents nécessaires (chacun × nombre de couples) dans le pool
3. Les races gen 1 restantes = matières premières
### 11.1 Mécanisme du reproducteur
Un reproducteur est une DD réutilisable : elle peut faire plusieurs bébés.
```
Si 2×R ≥ Q → couples = ceil(Q / 2)
Sinon → couples = Q - R
```
`R` = nombre de reproducteurs, `Q` = quantité nécessaire.
**Explication** :
- Si on a assez de reproducteurs (≥ moitié de Q), chaque reproducteur fait 2 bébés → on divise par 2
- Sinon, chaque reproducteur fait sa part et les autres sont des "one-shot"
---
## 12. Calcul d'inventaire avec contraintes ♂/♀ — `calcInventaire()`
**Question** : "Avec mon stock actuel de DD (mâles et femelles), quels croisements puis-je réaliser ?"
### Modèle de données
Chaque race dans l'inventaire : `{ m: mâles, f: femelles, n: neutres }`
- `m` et `f` = stock réel renseigné par le joueur
- `n` = bébés produits par le calcul (sexe inconnu)
### Contrainte fondamentale
Un croisement nécessite **1 mâle + 1 femelle**. On ne peut pas croiser ♂+♂ ou ♀+♀.
### Algorithme : round-robin par génération
Pour chaque génération (2 → 10) :
1. Lister tous les croisements possibles à cette génération
2. **Boucle round-robin** : tant qu'au moins un croisement est possible :
- Pour chaque croisement [Parent A + Parent B → Bébé] :
- Vérifier qu'on a 1 mâle-capable chez A ET 1 femelle-capable chez B (ou l'inverse)
- Si même race (A = B) : vérifier qu'on a ≥ 2 individus dont au moins 1 ♂ et 1 ♀
- Consommer les parents (priorité au stock réel m/f, puis les neutres n)
- Ajouter 1 neutre (`n++`) à la race du bébé produit
3. Les bébés produits (neutres) sont disponibles pour les générations suivantes
### Priorité de consommation
```
takeMale : si m > 0 → m--, sinon n--
takeFemale : si f > 0 → f--, sinon n--
```
Les neutres (`n`) peuvent jouer le rôle de ♂ ou ♀ selon le besoin.
### Pourquoi round-robin ?
Pour **répartir équitablement** les ressources entre les croisements possibles, plutôt que de tout donner au premier croisement trouvé.
---
## 13. Constantes des stats
| Stat | Min | Max | Jauge(s) associée(s) |
|-----------|--------|--------|----------------------|
| Sérénité | -5 000 | 5 000 | Baffeur (↓), Caresseur (↑) |
| Endurance | 0 | 20 000 | Foudroyeur (↑) |
| Maturité | 0 | 20 000 | Abreuvoir (↑) |
| Amour | 0 | 20 000 | Dragofesse (↑) |
| Niveau/XP | 1 | 200 | Mangeoire (↑) |
---
## 14. Système de snapshots
Quand le timer démarre, on prend un **snapshot** de l'état :
- `snapGauges` : niveaux de toutes les jauges au moment du démarrage
- `snapStats[dd.id]` : stats de chaque DD au moment du démarrage
Tous les calculs utilisent ces snapshots comme point de départ, pas l'état en temps réel. Cela évite les dérives de calcul si les valeurs sont modifiées pendant que le timer tourne.

28
findings.md Normal file
View File

@ -0,0 +1,28 @@
# Findings — Audit technique
## Variables CSS existantes (lignes 9-14)
```
--bg:#0b0b14 --bg2:#111120 --bg3:#181828 --bg4:#20203a
--border:#2a2a45 --text:#dddaf8 --muted:#6868a0
--ser:#c060ff --end:#f0bf30 --mat:#28c8f0 --amour:#ff5070 --xp:#ffa040
--ok:#28e888 --warn:#ff9820 --r:10px
```
La majorité du CSS utilise déjà `var()`, mais il y a ~320 occurrences de couleurs inline dans le JS (rgb/rgba dans les templates littéraux). Les couleurs de jauges (--ser, --end, etc.) devront rester saturées même en mode clair.
## Progression enclos — données disponibles
`enclosGlobalState(enc)` retourne `{globalMax, allDone, started, el, ddDone}`.
- `ddDone / enc.dragodindes.length` = % progression simple
- Fonctionne déjà, juste pas affiché visuellement sur les tabs
## Timer pause — détection retard
- `enc.timer.pausedAt` = timestamp de mise en pause
- `Date.now() - enc.timer.pausedAt` = durée de pause en ms
- Seuil suggéré : 5 minutes (300 000 ms)
## Raccourcis clavier
- Aucun listener keyboard existant
- Attention aux inputs focus : vérifier `document.activeElement.tagName !== 'INPUT'`
## Animations
- Transitions CSS existent sur tabs, gauges, pills (partielles)
- Aucune transition de contenu (#enclos-content) lors du changement d'onglet

View File

@ -407,12 +407,9 @@ app.whenReady().then(() => {
createWindow(); createWindow();
createTray(); createTray();
// Vérifier les mises à jour au démarrage (silencieux) // Vérifier les mises à jour uniquement au démarrage (silencieux)
setTimeout(() => checkForUpdates(true), 3000); setTimeout(() => checkForUpdates(true), 3000);
// Revérifier toutes les heures
setInterval(() => checkForUpdates(true), 60 * 60 * 1000);
app.on('activate', () => { app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow(); if (BrowserWindow.getAllWindows().length === 0) createWindow();
}); });

View File

@ -1,6 +1,6 @@
{ {
"name": "minuteur-dragodinde", "name": "minuteur-dragodinde",
"version": "1.1.3", "version": "1.1.4",
"description": "Minuteur elevage Dragodinde Dofus 3", "description": "Minuteur elevage Dragodinde Dofus 3",
"main": "main.js", "main": "main.js",
"author": "Mickael", "author": "Mickael",

12
progress.md Normal file
View File

@ -0,0 +1,12 @@
# Progress Log
## Session 2026-03-26
- [x] Audit technique terminé
- [x] Plan rédigé et soumis à validation
- [ ] Phase 1.1 — Tooltips pills
- [ ] Phase 1.2 — Indication retard
- [ ] Phase 2.1 — Barre progression tabs
- [ ] Phase 2.2 — Animations transition
- [ ] Phase 4.1 — Raccourcis clavier
- [ ] Phase 3.1 — Thème clair/sombre
- [ ] Phase 5.1 — Planificateur journalier

File diff suppressed because it is too large Load Diff

114
task_plan.md Normal file
View File

@ -0,0 +1,114 @@
# Plan — Polish UI & Features v1.2.0
## Vue d'ensemble
7 améliorations validées par l'utilisateur pour le Minuteur Dragodinde.
---
## Phase 1 — Fondations visuelles (rapide, 0 risque)
### 1.1 Tooltips sur les pills de stats
- **Quoi** : Ajouter un `title="Sérénité"`, `title="Endurance"` etc. sur chaque pill
- **Impact** : Ligne ~1654 (PILL_DEFS forEach) — ajouter un champ `label` et l'injecter en `title=`
- **Complexité** : Très faible (~5 min)
- **Risque** : Aucun
### 1.2 Indication visuelle "en retard" (pause longue)
- **Quoi** : Si un timer est en pause depuis > 5 min, colorer le tab en orange/rouge
- **Impact** :
- Lignes 1386, 1985 (updateTabDots) — ajouter classe `.paused-long`
- Détection : `enc.timer.pausedAt && (Date.now() - enc.timer.pausedAt) > 300000`
- CSS : nouveau style `.tab.paused-long`
- **Complexité** : Faible (~15 min)
- **Risque** : Aucun
---
## Phase 2 — Barre de progression & animations (~1h)
### 2.1 Barre de progression sous chaque tab d'enclos
- **Quoi** : Fine barre colorée (3-4px) sous chaque onglet montrant le % d'avancement global
- **Impact** :
- Lignes 1341-1420 (renderTabs) — ajouter un `<div class="tab-progress">` dans chaque tab
- Lignes 1981-1986 (updateTabDots) — mettre à jour la largeur via `gel()`
- `enclosGlobalState()` fournit déjà `globalMax`, `allDone`, `ddDone`
- Calcul % : `ddDone / enc.dragodindes.length` ou basé sur le temps écoulé vs totalSec
- CSS : `.tab-progress { height:3px; background:var(--ok); transition:width .5s }`
- **Complexité** : Moyenne (~30 min)
- **Risque** : Faible — touche le rendu des tabs mais pas la logique
### 2.2 Animations de transition entre onglets
- **Quoi** : Fade-in quand on switch de tab (opacity 0→1)
- **Impact** :
- Ligne 40 : CSS `.enclos-content` — ajouter `transition: opacity .2s`
- Ligne 1465 (renderContent) — set opacity 0 avant innerHTML, puis 1 après un rAF
- Alternative plus simple : classe `.fade-in` avec animation CSS
- **Complexité** : Faible (~20 min)
- **Risque** : Faible — purement visuel, pas de logique métier
---
## Phase 3 — Thème clair/sombre (~1h30)
### 3.1 Système de thème
- **Quoi** : Toggle dans le header pour basculer entre dark (actuel) et light
- **Impact** :
- Lignes 9-14 : Dupliquer les variables dans `[data-theme="light"]` avec des couleurs claires
- ~320 occurrences de couleurs dans le fichier, MAIS la majorité utilise déjà `var(--xxx)`
- **Problème principal** : les couleurs inline dans le JS (ex: `style="color:rgb(192,96,255)"`) ne changent PAS avec les variables CSS
- Il faudra auditer les styles inline dans renderDDs, renderDashboard, etc.
- Sauvegarde du choix dans `S.theme` (persisté via save())
- Toggle : bouton 🌙/☀ dans le header
- **Complexité** : Moyenne-élevée (~1h30)
- **Risque** : Moyen — beaucoup de couleurs inline à vérifier, risque d'oublis visuels
- **Effet de bord** : Les couleurs des jauges (--ser, --end, --mat, --amour, --xp) doivent rester lisibles sur fond clair
---
## Phase 4 — Raccourcis clavier (~30 min)
### 4.1 Raccourcis globaux
- **Quoi** :
- `Espace` : Play/Pause le timer de l'enclos actif
- `1-6` : Switch vers l'enclos 1 à 6
- `D` : Dashboard
- `N` : Ajouter une DD dans l'enclos actif
- **Impact** :
- Ajouter un `document.addEventListener('keydown', ...)` en fin de fichier
- Vérifier que `document.activeElement` n'est pas un input (sinon les raccourcis interfèrent avec la saisie)
- **Complexité** : Faible (~30 min)
- **Risque** : Moyen — doit ignorer les frappes quand un input est focus
---
## Phase 5 — Planificateur journalier (~2h)
### 5.1 Objectifs du jour
- **Quoi** : Nouvel onglet "Objectifs" où l'utilisateur définit ce qu'il veut accomplir aujourd'hui
- Ex: "Monter 5 DD au level 34", "Baisser sérénité de 3 DD à -5000"
- Progression auto-calculée à partir des timers actifs
- Récap fin de journée
- **Impact** :
- Nouvel onglet dans SPECIAL_TABS
- Nouveau champ `S.dailyGoals` dans l'état
- Calcul de progression basé sur les stats actuelles vs objectifs
- Interface : liste d'objectifs avec barres de progression
- **Complexité** : Élevée (~2h)
- **Risque** : Moyen — nouvelle feature isolée, mais il faut définir précisément ce qu'est "faisable en une journée humaine" (calcul basé sur les taux de jauges)
- **Note utilisateur** : "il faut calculer ce qui est faisable et productible en une journée humaine"
---
## Ordre d'exécution recommandé
| Ordre | Phase | Temps estimé | Dépendances |
|-------|-------|-------------|-------------|
| 1 | 1.1 Tooltips pills | ~5 min | Aucune |
| 2 | 1.2 Indication retard | ~15 min | Aucune |
| 3 | 2.1 Barre progression tabs | ~30 min | Aucune |
| 4 | 2.2 Animations transition | ~20 min | Aucune |
| 5 | 4.1 Raccourcis clavier | ~30 min | Aucune |
| 6 | 3.1 Thème clair/sombre | ~1h30 | Phases 1-2 (pour vérifier le rendu) |
| 7 | 5.1 Planificateur journalier | ~2h | Aucune |
**Total estimé : ~5h**