fix(eod): unlock custom minefields after clearing two sectors
This commit is contained in:
parent
fdf6da5c5b
commit
0dd9673690
4 changed files with 22 additions and 17 deletions
|
|
@ -34,7 +34,6 @@ export function EodArena({
|
||||||
const clearedSectors = [
|
const clearedSectors = [
|
||||||
initialStats.bestTrainingTime,
|
initialStats.bestTrainingTime,
|
||||||
initialStats.bestPerimeterTime,
|
initialStats.bestPerimeterTime,
|
||||||
initialStats.bestSupplyRoadTime,
|
|
||||||
].filter((time) => time > 0).length;
|
].filter((time) => time > 0).length;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -55,8 +54,8 @@ export function EodArena({
|
||||||
{!customUnlocked && (
|
{!customUnlocked && (
|
||||||
<UnlockNotice
|
<UnlockNotice
|
||||||
title="custom minefields"
|
title="custom minefields"
|
||||||
requirement="you have cleared Training Pad, FOB Perimeter, and Main Supply Road"
|
requirement="you have cleared Training Pad and FOB Perimeter"
|
||||||
progress={`${clearedSectors} of 3 sectors cleared`}
|
progress={`${clearedSectors} of 2 sectors cleared`}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<MinesweeperBoard
|
<MinesweeperBoard
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import { PROFILE_UPDATE_EVENT } from "@/hooks/useGameTick";
|
||||||
import { useSessionExpired } from "@/hooks/useSessionExpired";
|
import { useSessionExpired } from "@/hooks/useSessionExpired";
|
||||||
import { LoginLink } from "@/components/frontend/auth/LoginLink";
|
import { LoginLink } from "@/components/frontend/auth/LoginLink";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
import { isEodUnlocked } from "@/lib/minigames/custom/eod";
|
||||||
import { playClear, playExplosion, playFlag, playReveal, playXp } from "./sounds";
|
import { playClear, playExplosion, playFlag, playReveal, playXp } from "./sounds";
|
||||||
|
|
||||||
type Phase = "ready" | "playing" | "over";
|
type Phase = "ready" | "playing" | "over";
|
||||||
|
|
@ -227,8 +228,11 @@ export function MinesweeperBoard({
|
||||||
const longPressTimerRef = useRef<number | null>(null);
|
const longPressTimerRef = useRef<number | null>(null);
|
||||||
const baseConfig: GameConfig = difficulty === "custom" ? customConfig : DIFFICULTIES[difficulty];
|
const baseConfig: GameConfig = difficulty === "custom" ? customConfig : DIFFICULTIES[difficulty];
|
||||||
const config = isMobile ? getMobileConfig(baseConfig, isLandscape) : baseConfig;
|
const config = isMobile ? getMobileConfig(baseConfig, isLandscape) : baseConfig;
|
||||||
const customUnlocked =
|
const customUnlocked = isEodUnlocked({
|
||||||
stats.bestTrainingTime > 0 && stats.bestPerimeterTime > 0 && stats.bestSupplyRoadTime > 0;
|
bestTrainingTime: stats.bestTrainingTime,
|
||||||
|
bestPerimeterTime: stats.bestPerimeterTime,
|
||||||
|
bestSupplyRoadTime: stats.bestSupplyRoadTime,
|
||||||
|
});
|
||||||
const isLargeCustomGame =
|
const isLargeCustomGame =
|
||||||
difficulty === "custom" && config.rows * config.cols > CUSTOM_FULLSCREEN_CELL_THRESHOLD;
|
difficulty === "custom" && config.rows * config.cols > CUSTOM_FULLSCREEN_CELL_THRESHOLD;
|
||||||
const flaggedCount = board.reduce((count, cell) => count + (cell.flagged ? 1 : 0), 0);
|
const flaggedCount = board.reduce((count, cell) => count + (cell.flagged ? 1 : 0), 0);
|
||||||
|
|
@ -812,7 +816,7 @@ export function MinesweeperBoard({
|
||||||
</div>
|
</div>
|
||||||
) : !customUnlocked ? (
|
) : !customUnlocked ? (
|
||||||
<p className="text-xs text-muted-foreground">
|
<p className="text-xs text-muted-foreground">
|
||||||
Clear all three standard locations to unlock custom minefields.
|
Clear the Training Pad and FOB Perimeter to unlock custom minefields.
|
||||||
</p>
|
</p>
|
||||||
) : null}
|
) : null}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
|
||||||
|
|
@ -23,13 +23,9 @@ export interface EodUnlockStats {
|
||||||
bestSupplyRoadTime: number;
|
bestSupplyRoadTime: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A player unlocks the custom minefield editor once they cleared all three standard difficulties. */
|
/** A player unlocks the custom minefield editor once they cleared Training Pad and FOB Perimeter. */
|
||||||
export function isEodUnlocked(stats: EodUnlockStats): boolean {
|
export function isEodUnlocked(stats: EodUnlockStats): boolean {
|
||||||
return (
|
return stats.bestTrainingTime > 0 && stats.bestPerimeterTime > 0;
|
||||||
stats.bestTrainingTime > 0 &&
|
|
||||||
stats.bestPerimeterTime > 0 &&
|
|
||||||
stats.bestSupplyRoadTime > 0
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function validateMinefieldName(name: unknown): { error?: string; name: string } {
|
export function validateMinefieldName(name: unknown): { error?: string; name: string } {
|
||||||
|
|
|
||||||
|
|
@ -465,7 +465,7 @@ describe("Custom minefields", () => {
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
describe("Unlock gating", () => {
|
describe("Unlock gating", () => {
|
||||||
it("locks custom minefields until a win is recorded on all three difficulties", () => {
|
it("locks custom minefields until Training Pad and FOB Perimeter are cleared", () => {
|
||||||
expect(
|
expect(
|
||||||
isEodUnlocked({ bestTrainingTime: 0, bestPerimeterTime: 0, bestSupplyRoadTime: 0 }),
|
isEodUnlocked({ bestTrainingTime: 0, bestPerimeterTime: 0, bestSupplyRoadTime: 0 }),
|
||||||
).toBe(false);
|
).toBe(false);
|
||||||
|
|
@ -473,18 +473,24 @@ describe("Custom minefields", () => {
|
||||||
isEodUnlocked({ bestTrainingTime: 45, bestPerimeterTime: 0, bestSupplyRoadTime: 0 }),
|
isEodUnlocked({ bestTrainingTime: 45, bestPerimeterTime: 0, bestSupplyRoadTime: 0 }),
|
||||||
).toBe(false);
|
).toBe(false);
|
||||||
expect(
|
expect(
|
||||||
isEodUnlocked({ bestTrainingTime: 45, bestPerimeterTime: 60, bestSupplyRoadTime: 0 }),
|
isEodUnlocked({ bestTrainingTime: 0, bestPerimeterTime: 60, bestSupplyRoadTime: 0 }),
|
||||||
).toBe(false);
|
).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("unlocks once every difficulty has a best time above zero", () => {
|
it("unlocks once Training Pad and FOB Perimeter have best times above zero", () => {
|
||||||
expect(
|
expect(
|
||||||
isEodUnlocked({ bestTrainingTime: 1, bestPerimeterTime: 1, bestSupplyRoadTime: 1 }),
|
isEodUnlocked({ bestTrainingTime: 1, bestPerimeterTime: 1, bestSupplyRoadTime: 0 }),
|
||||||
).toBe(true);
|
).toBe(true);
|
||||||
expect(
|
expect(
|
||||||
isEodUnlocked({ bestTrainingTime: 45, bestPerimeterTime: 60, bestSupplyRoadTime: 120 }),
|
isEodUnlocked({ bestTrainingTime: 45, bestPerimeterTime: 60, bestSupplyRoadTime: 0 }),
|
||||||
).toBe(true);
|
).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("does not let a Main Supply Road win substitute for the required sectors", () => {
|
||||||
|
expect(
|
||||||
|
isEodUnlocked({ bestTrainingTime: 0, bestPerimeterTime: 0, bestSupplyRoadTime: 120 }),
|
||||||
|
).toBe(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue