mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-08-13 21:59:58 -05:00
Fix broken images
This commit is contained in:
@@ -4,11 +4,12 @@
|
||||
*/
|
||||
|
||||
import { useState } from "react";
|
||||
import { CV_ASSETS_URL } from "~/utils/urls";
|
||||
import { Ability } from "~/components/Ability";
|
||||
import type { CvAbility } from "../cv-types";
|
||||
|
||||
const ROW_LABELS = ["head", "clothes", "shoes"] as const;
|
||||
|
||||
export function AbilityGrid({ abilities }: { abilities: string[][] }) {
|
||||
export function AbilityGrid({ abilities }: { abilities: CvAbility[][] }) {
|
||||
return (
|
||||
<table className="players">
|
||||
<tbody>
|
||||
@@ -17,16 +18,7 @@ export function AbilityGrid({ abilities }: { abilities: string[][] }) {
|
||||
<td>{ROW_LABELS[i]}</td>
|
||||
{row.map((id, j) => (
|
||||
<td key={j}>
|
||||
<img
|
||||
className="weapon-icon"
|
||||
src={`${CV_ASSETS_URL}/abilities/${id}.png`}
|
||||
alt={id}
|
||||
title={id}
|
||||
style={{
|
||||
width: j === 0 ? 28 : 20,
|
||||
height: j === 0 ? 28 : 20,
|
||||
}}
|
||||
/>
|
||||
<Ability ability={id} size={j === 0 ? "SUBTINY" : "TINY"} />
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
@@ -40,7 +32,7 @@ export function AbilityGrid({ abilities }: { abilities: string[][] }) {
|
||||
* Click-to-toggle popover showing a player's ability grid; the trigger is
|
||||
* the head-main ability icon. Closes when the pointer leaves it.
|
||||
*/
|
||||
export function AbilityPopover({ abilities }: { abilities: string[][] }) {
|
||||
export function AbilityPopover({ abilities }: { abilities: CvAbility[][] }) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const trigger = abilities[0]?.[0];
|
||||
if (!trigger) return null;
|
||||
@@ -53,10 +45,7 @@ export function AbilityPopover({ abilities }: { abilities: string[][] }) {
|
||||
onClick={() => setOpen((o) => !o)}
|
||||
title="Show abilities (from death events)"
|
||||
>
|
||||
<img
|
||||
src={`${CV_ASSETS_URL}/abilities/${trigger}.png`}
|
||||
alt="abilities"
|
||||
/>
|
||||
<Ability ability={trigger} size="TINY" />
|
||||
</button>
|
||||
{open && (
|
||||
<div className="popover">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { CV_ASSETS_URL } from "~/utils/urls";
|
||||
import { WeaponImage } from "~/components/Image";
|
||||
import type { MainWeaponId } from "~/modules/in-game-lists/types";
|
||||
import type { DeathData } from "../core/detectors/death/index";
|
||||
import { AbilityGrid } from "./AbilityGrid";
|
||||
import { saveFixtureFromEvent } from "./fixture-export";
|
||||
@@ -52,14 +53,14 @@ export function DeathCard(props: {
|
||||
</div>
|
||||
<div className="teams">
|
||||
<div className="team">
|
||||
{data.weaponId !== null && data.weaponType === "MAIN" && (
|
||||
<img
|
||||
{data.weaponId !== null && data.weaponType === "MAIN" ? (
|
||||
<WeaponImage
|
||||
weaponSplId={data.weaponId as MainWeaponId}
|
||||
variant="build"
|
||||
size={28}
|
||||
className="weapon-icon"
|
||||
src={`${CV_ASSETS_URL}/main-weapons/${data.weaponId}.png`}
|
||||
alt={weaponName ?? String(data.weaponId)}
|
||||
title={`weapon ${data.weaponId}`}
|
||||
/>
|
||||
)}
|
||||
) : null}
|
||||
<AbilityGrid abilities={data.abilities} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,26 +1,21 @@
|
||||
import { CV_ASSETS_URL } from "~/utils/urls";
|
||||
import { Ability } from "~/components/Ability";
|
||||
import { WeaponImage } from "~/components/Image";
|
||||
import type {
|
||||
MinimapData,
|
||||
MinimapEnemy,
|
||||
MinimapTeammate,
|
||||
} from "../core/detectors/minimap/index";
|
||||
import type { CvAbility } from "../cv-types";
|
||||
import { saveFixtureFromEvent } from "./fixture-export";
|
||||
import { formatTime } from "./format";
|
||||
import { mainWeaponLabel, stageLabel } from "./labels";
|
||||
import { stageLabel } from "./labels";
|
||||
|
||||
function AbilityRow({ abilities }: { abilities: (string | null)[] }) {
|
||||
function AbilityRow({ abilities }: { abilities: (CvAbility | null)[] }) {
|
||||
return (
|
||||
<>
|
||||
{abilities.map((id, i) =>
|
||||
id ? (
|
||||
<img
|
||||
key={i}
|
||||
className="weapon-icon"
|
||||
src={`${CV_ASSETS_URL}/abilities/${id}.png`}
|
||||
alt={id}
|
||||
title={id}
|
||||
style={{ width: 22, height: 22 }}
|
||||
/>
|
||||
<Ability key={i} ability={id} size="TINY" />
|
||||
) : (
|
||||
<span key={i} title="unreadable badge">
|
||||
?
|
||||
@@ -44,11 +39,11 @@ function PlayerRow({
|
||||
<td>{player.name ?? ""}</td>
|
||||
<td>
|
||||
{player.weaponId !== null ? (
|
||||
<img
|
||||
<WeaponImage
|
||||
weaponSplId={player.weaponId}
|
||||
variant="build"
|
||||
size={28}
|
||||
className="weapon-icon"
|
||||
src={`${CV_ASSETS_URL}/main-weapons/${player.weaponId}.png`}
|
||||
alt={mainWeaponLabel(player.weaponId) ?? String(player.weaponId)}
|
||||
title={mainWeaponLabel(player.weaponId) ?? String(player.weaponId)}
|
||||
/>
|
||||
) : (
|
||||
"?"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CV_ASSETS_URL } from "~/utils/urls";
|
||||
import { WeaponImage } from "~/components/Image";
|
||||
import type { PlayerAbilityMap } from "../core/ability-harvest";
|
||||
import type {
|
||||
ScoreboardData,
|
||||
@@ -27,17 +27,17 @@ function PlayerRows({
|
||||
<tr key={i}>
|
||||
<td>
|
||||
<span className="weapon-cell">
|
||||
{p.weaponId !== null && (
|
||||
<img
|
||||
{p.weaponId !== null ? (
|
||||
<WeaponImage
|
||||
weaponSplId={p.weaponId}
|
||||
variant="build"
|
||||
size={28}
|
||||
className="weapon-icon"
|
||||
src={`${CV_ASSETS_URL}/main-weapons/${p.weaponId}.png`}
|
||||
alt={String(p.weaponId)}
|
||||
title={`weapon ${p.weaponId}`}
|
||||
/>
|
||||
)}
|
||||
{abilities?.has(offset + i) && (
|
||||
) : null}
|
||||
{abilities?.has(offset + i) ? (
|
||||
<AbilityPopover abilities={abilities.get(offset + i)!} />
|
||||
)}
|
||||
) : null}
|
||||
</span>
|
||||
</td>
|
||||
<td>{p.name || "?"}</td>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CV_ASSETS_URL } from "~/utils/urls";
|
||||
import { WeaponImage } from "~/components/Image";
|
||||
import type { ScoreboardOwnData } from "../core/detectors/scoreboard-own/index";
|
||||
import { AbilityGrid } from "./AbilityGrid";
|
||||
import { saveFixtureFromEvent } from "./fixture-export";
|
||||
@@ -59,14 +59,14 @@ export function ScoreboardOwnCard(props: {
|
||||
</div>
|
||||
<div className="teams">
|
||||
<div className="team">
|
||||
{data.weaponId !== null && (
|
||||
<img
|
||||
{data.weaponId !== null ? (
|
||||
<WeaponImage
|
||||
weaponSplId={data.weaponId}
|
||||
variant="build"
|
||||
size={28}
|
||||
className="weapon-icon"
|
||||
src={`${CV_ASSETS_URL}/main-weapons/${data.weaponId}.png`}
|
||||
alt={mainWeaponLabel(data.weaponId) ?? String(data.weaponId)}
|
||||
title={`weapon ${data.weaponId}`}
|
||||
/>
|
||||
)}
|
||||
) : null}
|
||||
<AbilityGrid abilities={data.abilities} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
* and weapon id.
|
||||
*/
|
||||
|
||||
import type { CvAbility } from "../cv-types";
|
||||
import { DEATH_EVENT_TYPE, type DeathData } from "./detectors/death/index";
|
||||
import { SCOREBOARD_EVENT_TYPES } from "./detectors/registry";
|
||||
import type {
|
||||
@@ -16,7 +17,7 @@ import type {
|
||||
import type { DetectedEvent } from "./detectors/types";
|
||||
|
||||
/** player row index (0-7) → [head, clothes, shoes] ability-id rows */
|
||||
export type PlayerAbilityMap = Map<number, string[][]>;
|
||||
export type PlayerAbilityMap = Map<number, CvAbility[][]>;
|
||||
|
||||
/**
|
||||
* Match a death's killer to a scoreboard player row. Both signals are OCR
|
||||
|
||||
@@ -17,7 +17,7 @@ export type CvLobby = (typeof CV_LOBBIES)[number];
|
||||
* unrecognized marker (the UNKNOWN template in assets/cv/abilities —
|
||||
* distinct from null, which means the badge was covered/absent).
|
||||
*/
|
||||
export type CvAbility = Ability | "UNKNOWN";
|
||||
export type CvAbility = Ability | "UNKNOWN"; // xxx: why not AbilityWithUnknown
|
||||
|
||||
const MAIN_WEAPON_ID_SET: ReadonlySet<number> = new Set(mainWeaponIds);
|
||||
const ABILITY_SET: ReadonlySet<string> = new Set(abilities.map((a) => a.name));
|
||||
|
||||
@@ -3,6 +3,13 @@ import type { MetaFunction } from "react-router";
|
||||
import { Placeholder } from "~/components/Placeholder";
|
||||
import { useHydrated } from "~/hooks/useHydrated";
|
||||
import { metaTags } from "~/utils/remix";
|
||||
import type { SendouRouteHandle } from "~/utils/remix.server";
|
||||
|
||||
// `builds` powers the empty/UNKNOWN ability label in <Ability />; the weapon
|
||||
// and game-misc namespaces the CV cards rely on are always loaded.
|
||||
export const handle: SendouRouteHandle = {
|
||||
i18n: ["builds"],
|
||||
};
|
||||
|
||||
export const meta: MetaFunction = (args) => {
|
||||
return metaTags({
|
||||
|
||||
Reference in New Issue
Block a user