feat(routines): Wuthering Waves' shop pulls, each version

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Lucas WintherandClaude Opus 5.5 committed 2026-10-22 17:38:41 +02:00
1 parent b3a84757a1
commit 8cf01b427f
8 files changed
+136 -12

No files matched your search

+10 -8
View File
@@ -7,7 +7,7 @@ import { cadenceLabel, EventForm, GameForm } from "./CustomForms.tsx";
import { taskCaption, type Task } from "../../shared/task.ts";
import type { TaskDraft } from "../state/useTasks.ts";
import { TaskForm } from "./TaskForm.tsx";
import { asOfLabel, type Routine } from "../../shared/routines.ts";
import { asOfLabel, ownable, type Routine } from "../../shared/routines.ts";
/**
* The reader's own games and events — a group of the settings panel (PRD F13).
@@ -349,13 +349,15 @@ export function YourOwn({
>
as of {asOfLabel(routine.asOf)}
</a>
<button
type="button"
onClick={() => onMakeItMine(routine)}
className="shrink-0 text-xs text-faint transition-colors hover:text-ink"
>
Make it mine
</button>
{ownable(routine) && (
<button
type="button"
onClick={() => onMakeItMine(routine)}
className="shrink-0 text-xs text-faint transition-colors hover:text-ink"
>
Make it mine
</button>
)}
{onTrackRoutine !== undefined && (
<button
type="button"
@@ -6,7 +6,12 @@ import type { CSSProperties } from "react";
import { RoutineHistory } from "./RoutineHistory.tsx";
import { useState } from "react";
import { routinesFor, mineFrom } from "../../../shared/routines.ts";
import { routinesFor, mineFrom, ownable } from "../../../shared/routines.ts";
const builtinOwnable = (game: string, id: string) => {
const routine = routinesFor(game).find((r) => r.id === id);
return routine !== undefined && ownable(routine);
};
import { mintTaskId, type Task } from "../../../shared/task.ts";
import {
RoutineDrawer,
@@ -488,9 +493,11 @@ export function RoutinesPage({
</a>
)}
{row.id.startsWith("routine:") ? (
<button onClick={() => void copy(row)}>
Make it mine
</button>
builtinOwnable(row.game, row.id) && (
<button onClick={() => void copy(row)}>
Make it mine
</button>
)
) : (
row.task && (
<>
+46
View File
@@ -457,6 +457,45 @@ export const ROUTINES: readonly Routine[] = [
checked: "2026-10-16",
note: "The Hazard Zone; the source says roughly 28 days.",
},
// The Oscillated Coral shop's pulls: seven of each, restocked with each
// version (owner, in game, 2026-10-22; spec 2026-10-22). The page names the
// shop and its three Tides, but is from 2024 and says six a month.
{
id: "routine:wuwa:shop-radiant-tide",
game: "wuwa",
title: "Oscillated Coral: Radiant Tide",
period: { unit: "version" },
pulls: "premium",
measure: { kind: "amount", target: 7, unit: "Radiant Tide" },
source: "https://samurai-gamers.com/wuthering-waves/wuwa-how-to-get-oscillated-coral/",
asOf: "2024-05-31",
checked: "2026-10-22",
note: "The 2024 page says six a month; the shop now sells seven a version. Confirmed in game, 2026-10-22.",
},
{
id: "routine:wuwa:shop-forging-tide",
game: "wuwa",
title: "Oscillated Coral: Forging Tide",
period: { unit: "version" },
pulls: "premium",
measure: { kind: "amount", target: 7, unit: "Forging Tide" },
source: "https://samurai-gamers.com/wuthering-waves/wuwa-how-to-get-oscillated-coral/",
asOf: "2024-05-31",
checked: "2026-10-22",
note: "The 2024 page says six a month; the shop now sells seven a version. Confirmed in game, 2026-10-22.",
},
{
id: "routine:wuwa:shop-lustrous-tide",
game: "wuwa",
title: "Oscillated Coral: Lustrous Tide",
period: { unit: "version" },
pulls: "standard",
measure: { kind: "amount", target: 7, unit: "Lustrous Tide" },
source: "https://samurai-gamers.com/wuthering-waves/wuwa-how-to-get-oscillated-coral/",
asOf: "2024-05-31",
checked: "2026-10-22",
note: "The 2024 page says six a month; the shop now sells seven a version. Confirmed in game, 2026-10-22.",
},
// Infinity Nikki. The wiki's Reset page puts the weekly reset on Monday at
// 04:00 server time and lists both of these among what it renews.
@@ -673,6 +712,13 @@ export function routineToAsk(
* A reader's own task copied from a built-in, remembering where it came from.
* The copy is theirs to edit; the original stays hidden while it exists.
*/
/**
* Whether *Make it mine* is offered: not for a routine that resets with the
* version, since a reader's task cannot hold that period (spec 2026-10-22).
*/
export const ownable = (routine: Pick<Routine, "period">): boolean =>
routine.period.unit !== "version";
export function mineFrom(routine: Routine): {
game: string;
title: string;