feat: offline gains — courbe inversée 2h, cap 25%, écran résumé

offlineEfficiency() : 100% (0-15min) → 25% (1h) → 0% (2h).
computeOfflineGains() intègre la courbe par tranches de 1min.
GameState.lastOnline ajouté, store hydrate avec offline report.
OfflineReport.tsx affiché au retour si absence > 60s.
13 nouveaux tests (66 total, tous passent).
This commit is contained in:
2026-03-28 11:44:59 +01:00
parent 90761b3e13
commit 3ba10dad5f
5 changed files with 284 additions and 15 deletions

View File

@@ -0,0 +1,65 @@
// OfflineReport.tsx — Écran "Pendant ton absence..." affiché au retour offline
import { useGameStore } from "../store/useGameStore";
import { formatNumber } from "../utils/formatNumber";
function formatDuration(ms: number): string {
const minutes = Math.floor(ms / 60_000);
if (minutes < 60) return `${minutes}min`;
const hours = Math.floor(minutes / 60);
const remainMinutes = minutes % 60;
return remainMinutes > 0 ? `${hours}h${remainMinutes}min` : `${hours}h`;
}
export function OfflineReport() {
const report = useGameStore((s) => s.offlineReport);
const dismiss = useGameStore((s) => s.dismissOfflineReport);
if (!report || !report.wasOffline) return null;
const effPercent = Math.round(report.efficiency * 100);
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-sm">
<div className="gp max-w-sm w-full mx-4 text-center">
<span className="gp-title text-lg!">Pendant ton absence...</span>
<div className="flex flex-col gap-3 mt-2">
<div className="flex justify-between">
<span className="gp-label">Durée</span>
<span className="gp-value">{formatDuration(report.duration)}</span>
</div>
<div className="flex justify-between">
<span className="gp-label">Efficacité marais</span>
<span className={`gp-value ${effPercent > 50 ? "gp-accent-green" : "gp-accent-amber"}`}>
{effPercent}%
</span>
</div>
<div className="gp-sep" />
<div className="flex justify-between items-center">
<span className="gp-label">Têtards récoltés</span>
<span className="gp-value gp-accent-green text-lg!">
+{formatNumber(report.gains)}
</span>
</div>
{report.efficiency < 0.5 && (
<p className="gp-label text-center">
Le marais s'endort sans toi... Joue activement pour maximiser ta production !
</p>
)}
</div>
<button
onClick={dismiss}
className="gp-btn gp-btn--buy w-full mt-3 py-2!"
>
Retour au marais
</button>
</div>
</div>
);
}