Map Planner: TC Blueprint backgrounds
|
|
@ -6,9 +6,9 @@ import {
|
|||
Tldraw,
|
||||
type TldrawApp,
|
||||
} from "@tldraw/tldraw";
|
||||
import clsx from "clsx";
|
||||
import randomInt from "just-random-integer";
|
||||
import * as React from "react";
|
||||
import invariant from "tiny-invariant";
|
||||
import { useForceRefreshOnMount } from "~/hooks/useForceRefresh";
|
||||
import { useTranslation } from "~/hooks/useTranslation";
|
||||
import type { LanguageCode } from "~/modules/i18n";
|
||||
|
|
@ -18,7 +18,6 @@ import { modesShort } from "~/modules/in-game-lists/modes";
|
|||
import { semiRandomId } from "~/utils/strings";
|
||||
import {
|
||||
mainWeaponImageUrl,
|
||||
modeImageUrl,
|
||||
outlinedMainWeaponImageUrl,
|
||||
stageMinimapImageUrlWithEnding,
|
||||
TLDRAW_URL,
|
||||
|
|
@ -26,6 +25,12 @@ import {
|
|||
import { Button } from "./Button";
|
||||
import { Image } from "./Image";
|
||||
|
||||
export type StageBackgroundStyle = ModeShort | "TCO";
|
||||
|
||||
const BLUEPRINTS_AVAILABLE: Partial<Record<ModeShort, Array<StageId>>> = {
|
||||
TC: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
|
||||
};
|
||||
|
||||
export default function Planner() {
|
||||
const { t } = useTranslation(["common", "weapons"]);
|
||||
const { i18n } = useTranslation();
|
||||
|
|
@ -107,10 +112,10 @@ export default function Planner() {
|
|||
);
|
||||
|
||||
const handleAddBackgroundImage = React.useCallback(
|
||||
({ stageId, modeShort }: { stageId: StageId; modeShort: ModeShort }) => {
|
||||
({ stageId, style }: { stageId: StageId; style: StageBackgroundStyle }) => {
|
||||
app.resetDocument();
|
||||
handleAddImage({
|
||||
src: stageMinimapImageUrlWithEnding({ stageId, modeShort }),
|
||||
src: stageMinimapImageUrlWithEnding({ stageId, style }),
|
||||
size: [1600, 900],
|
||||
isLocked: true,
|
||||
point: [65, 20],
|
||||
|
|
@ -156,15 +161,27 @@ function StageBackgroundSelector({
|
|||
}: {
|
||||
onAddBackground: ({
|
||||
stageId,
|
||||
modeShort,
|
||||
style,
|
||||
}: {
|
||||
stageId: StageId;
|
||||
modeShort: ModeShort;
|
||||
style: StageBackgroundStyle;
|
||||
}) => void;
|
||||
}) {
|
||||
const { t } = useTranslation(["game-misc", "common"]);
|
||||
const [stageId, setStageId] = React.useState<StageId>(stageIds[0]);
|
||||
const [selectedMode, setSelectedMode] = React.useState<ModeShort>("SZ");
|
||||
const [backgroundStyle, setBackgroundStyle] =
|
||||
React.useState<StageBackgroundStyle>("SZ");
|
||||
|
||||
const imgExists = () => {
|
||||
// normal background image
|
||||
if (backgroundStyle.length === 2) return true;
|
||||
|
||||
const stageIds =
|
||||
BLUEPRINTS_AVAILABLE[backgroundStyle.replace("O", "") as ModeShort];
|
||||
invariant(stageIds);
|
||||
|
||||
return stageIds.includes(stageId);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="plans__top-section">
|
||||
|
|
@ -182,40 +199,38 @@ function StageBackgroundSelector({
|
|||
);
|
||||
})}
|
||||
</select>
|
||||
<div className="plans__mode-buttons">
|
||||
<select
|
||||
className="w-max"
|
||||
value={backgroundStyle}
|
||||
onChange={(e) =>
|
||||
setBackgroundStyle(e.target.value as StageBackgroundStyle)
|
||||
}
|
||||
>
|
||||
{modesShort.map((mode) => {
|
||||
const selected = mode === selectedMode;
|
||||
return (
|
||||
<button
|
||||
key={mode}
|
||||
className={clsx("plans__mode-button", "outline-theme", {
|
||||
selected,
|
||||
})}
|
||||
onClick={() => setSelectedMode(mode)}
|
||||
type="button"
|
||||
title={t(`game-misc:MODE_LONG_${mode}`)}
|
||||
aria-pressed={selected}
|
||||
>
|
||||
<Image
|
||||
className={clsx("plans__mode-img", {
|
||||
selected,
|
||||
})}
|
||||
alt={t(`game-misc:MODE_LONG_${mode}`)}
|
||||
path={modeImageUrl(mode)}
|
||||
width={20}
|
||||
height={20}
|
||||
/>
|
||||
</button>
|
||||
<React.Fragment key={mode}>
|
||||
<option value={mode}>{t(`game-misc:MODE_SHORT_${mode}`)}</option>
|
||||
{Object.keys(BLUEPRINTS_AVAILABLE).includes(mode as any) ? (
|
||||
<option value={`${mode}O`}>
|
||||
{t(`game-misc:MODE_SHORT_${mode}`)} (
|
||||
{t("common:plans.blueprint")})
|
||||
</option>
|
||||
) : null}
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<Button
|
||||
tiny
|
||||
onClick={() => onAddBackground({ modeShort: selectedMode, stageId })}
|
||||
className="w-max"
|
||||
>
|
||||
{t("common:actions.setBg")}
|
||||
</Button>
|
||||
</select>
|
||||
{imgExists() ? (
|
||||
<Button
|
||||
tiny
|
||||
onClick={() => onAddBackground({ style: backgroundStyle, stageId })}
|
||||
className="w-max"
|
||||
>
|
||||
{t("common:actions.setBg")}
|
||||
</Button>
|
||||
) : (
|
||||
<span className="plans__no-img-text">{t("common:plans.noImg")}</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,33 +47,6 @@ button[data-state="closed"][aria-haspopup="dialog"] {
|
|||
transform: translate(-50%, -42%);
|
||||
}
|
||||
|
||||
.plans__mode-buttons {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.plans__mode-button {
|
||||
padding: 0;
|
||||
padding: var(--s-1-5);
|
||||
border: none;
|
||||
border-radius: var(--rounded-full);
|
||||
background-color: transparent;
|
||||
color: var(--theme);
|
||||
opacity: 1 !important;
|
||||
outline: initial;
|
||||
}
|
||||
|
||||
.plans__mode-button.selected {
|
||||
background-color: var(--bg-mode-active);
|
||||
}
|
||||
|
||||
.plans__mode-img:not(.selected) {
|
||||
filter: var(--inactive-image-filter);
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.plans__weapons-section {
|
||||
position: fixed;
|
||||
z-index: 10;
|
||||
|
|
@ -99,3 +72,8 @@ button[data-state="closed"][aria-haspopup="dialog"] {
|
|||
color: var(--text-lighter);
|
||||
font-size: var(--fonts-xxxs);
|
||||
}
|
||||
|
||||
.plans__no-img-text {
|
||||
color: var(--theme-error);
|
||||
font-size: var(--fonts-xs);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import type {
|
|||
} from "~/modules/in-game-lists/types";
|
||||
import type navItems from "~/components/layout/nav-items.json";
|
||||
import { type AuthErrorCode } from "~/modules/auth";
|
||||
import type { StageBackgroundStyle } from "~/components/Planner";
|
||||
|
||||
export const SPLATOON_2_SENDOU_IN_URL = "https://spl2.sendou.ink";
|
||||
export const PLUS_SERVER_DISCORD_URL = "https://discord.gg/FW4dKrY";
|
||||
|
|
@ -164,11 +165,11 @@ export const stageImageUrl = (stageId: StageId) =>
|
|||
`/static-assets/img/stages/${stageId}`;
|
||||
export const stageMinimapImageUrlWithEnding = ({
|
||||
stageId,
|
||||
modeShort,
|
||||
style,
|
||||
}: {
|
||||
stageId: StageId;
|
||||
modeShort: ModeShort;
|
||||
}) => `/static-assets/img/stage-minimaps/${stageId}-${modeShort}.jpeg`;
|
||||
style: StageBackgroundStyle;
|
||||
}) => `/static-assets/img/stage-minimaps/${stageId}-${style}.jpeg`;
|
||||
|
||||
export function resolveBaseUrl(url: string) {
|
||||
return new URL(url).host;
|
||||
|
|
|
|||
|
|
@ -107,6 +107,8 @@
|
|||
"weapon.category.SPLATANAS": "Splatanas",
|
||||
|
||||
"plans.poweredBy": "Powered by {{name}}",
|
||||
"plans.blueprint": "Blueprint",
|
||||
"plans.noImg": "No image available",
|
||||
|
||||
"articles.by": "by {{author}}",
|
||||
|
||||
|
|
|
|||
BIN
public/static-assets/img/stage-minimaps/0-TCO.jpeg
Normal file
|
After Width: | Height: | Size: 2.1 MiB |
BIN
public/static-assets/img/stage-minimaps/1-TCO.jpeg
Normal file
|
After Width: | Height: | Size: 2.1 MiB |
BIN
public/static-assets/img/stage-minimaps/10-TCO.jpeg
Normal file
|
After Width: | Height: | Size: 2.0 MiB |
BIN
public/static-assets/img/stage-minimaps/11-TCO.jpeg
Normal file
|
After Width: | Height: | Size: 1.9 MiB |
BIN
public/static-assets/img/stage-minimaps/2-TCO.jpeg
Normal file
|
After Width: | Height: | Size: 2.0 MiB |
BIN
public/static-assets/img/stage-minimaps/3-TCO.jpeg
Normal file
|
After Width: | Height: | Size: 2.0 MiB |
BIN
public/static-assets/img/stage-minimaps/4-TCO.jpeg
Normal file
|
After Width: | Height: | Size: 2.0 MiB |
BIN
public/static-assets/img/stage-minimaps/5-TCO.jpeg
Normal file
|
After Width: | Height: | Size: 2.1 MiB |
BIN
public/static-assets/img/stage-minimaps/6-TCO.jpeg
Normal file
|
After Width: | Height: | Size: 2.1 MiB |
BIN
public/static-assets/img/stage-minimaps/7-TCO.jpeg
Normal file
|
After Width: | Height: | Size: 1.9 MiB |
BIN
public/static-assets/img/stage-minimaps/8-TCO.jpeg
Normal file
|
After Width: | Height: | Size: 2.1 MiB |
BIN
public/static-assets/img/stage-minimaps/9-TCO.jpeg
Normal file
|
After Width: | Height: | Size: 2.1 MiB |