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:
65
Frontend/src/components/OfflineReport.tsx
Normal file
65
Frontend/src/components/OfflineReport.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user