off: { x: 0, y: 0 },
scale: 1.3,
},
+ labelPos: { x: 90, y: 70 },
},
entrances: [
{
off: { x: 0, y: 0 },
scale: 1.3,
},
+ labelPos: { x: 40, y: 85 },
},
entrances: [
{
off: { x: -20, y: 0 },
scale: 4.3,
},
+ labelPos: { x: 15, y: 110 },
},
entrances: [
{
off: { x: 0, y: -5 },
scale: 1.5,
},
+ labelPos: { x: 65, y: 35 },
},
entrances: [
{
off: { x: -7, y: 0 },
scale: 1.7,
},
+ labelPos: { x: 20, y: 95 },
},
entrances: [
{
off: { x: 0, y: -6 },
scale: 1.0,
},
+ labelPos: { x: 38, y: 30 },
},
entrances: [
{
off: { x: -3, y: -3 },
scale: 3.6,
},
+ labelPos: { x: -2, y: 25 },
},
entrances: [
{
off: { x: 0, y: 0 },
scale: 1.2,
},
+ labelPos: { x: 25, y: 65 },
},
entrances: [
{
off: { x: -16, y: 0 },
scale: 1.8,
},
+ labelPos: { x: 100, y: 10 },
},
entrances: [
{
off: { x: 0, y: 0 },
scale: 1.3,
},
+ labelPos: { x: 60, y: 40 },
},
entrances: [
{
off: { x: -15, y: -5 },
scale: 2.3,
},
+ labelPos: { x: 18, y: 40 },
},
entrances: [
{
off: { x: -5, y: 0 },
scale: 2.1,
},
+ labelPos: { x: 60, y: 35 },
},
entrances: [
{
off: { x: -15, y: -5 },
scale: 1.9,
},
+ labelPos: { x: 70, y: 65 },
},
entrances: [
{
off: { x: -22, y: -6 },
scale: 2.6,
},
+ labelPos: { x: 40, y: 50 },
},
entrances: [
{
off: { x: -17, y: -5 },
scale: 1.9,
},
+ labelPos: { x: 20, y: 55 },
},
entrances: [
{
off: { x: -5, y: -35 },
scale: 2.5,
},
+ labelPos: { x: 15, y: 50 },
},
entrances: [
{
off: { x: 0, y: -2 },
scale: 2.0,
},
+ labelPos: { x: 50, y: 50 },
},
entrances: [
{
off: { x: -15, y: -3 },
scale: 1.9,
},
+ labelPos: { x: 52, y: 62 },
},
entrances: [
{
off: { x: -12, y: 0 },
scale: 3.8,
},
+ labelPos: { x: -10, y: 50 },
},
entrances: [
{
off: { x: -2, y: 0 },
scale: 2.0,
},
+ labelPos: { x: 25, y: 105 },
},
entrances: [
{
short: 'Minuet',
oneway: true,
type: 'WarpSong',
+ icon: '/media/oot/icons/song-minuet.png',
},
{
id: 'bolero',
short: 'Bolero',
oneway: true,
type: 'WarpSong',
+ icon: '/media/oot/icons/song-bolero.png',
},
{
id: 'serenade',
short: 'Serenade',
oneway: true,
type: 'WarpSong',
+ icon: '/media/oot/icons/song-serenade.png',
},
{
id: 'nocturne',
short: 'Nocturne',
oneway: true,
type: 'WarpSong',
+ icon: '/media/oot/icons/song-nocturne.png',
},
{
id: 'requiem',
short: 'Requiem',
oneway: true,
type: 'WarpSong',
+ icon: '/media/oot/icons/song-requiem.png',
},
{
id: 'prelude',
short: 'Prelude',
oneway: true,
type: 'WarpSong',
+ icon: '/media/oot/icons/song-prelude.png',
},
],
},
short: 'Child',
oneway: true,
type: 'Spawn',
+ icon: '/media/oot/icons/link-child.png',
+ iconSize: 10,
},
{
id: 'adult',
short: 'Adult',
oneway: true,
type: 'Spawn',
+ icon: '/media/oot/icons/link-adult.png',
},
],
},
short: 'LH Owl',
oneway: true,
type: 'OwlDrop',
+ icon: '/media/oot/icons/owl-lake.png',
},
{
id: 'dmtowl',
short: 'Trail Owl',
oneway: true,
type: 'OwlDrop',
+ icon: '/media/oot/icons/owl-trail.png',
},
],
},
const MAPS = AREAS
.filter((area) => !!area.map)
.map((area) => ({
+ color: area.bgColor,
id: area.id,
+ labelPos: area.map.labelPos ? vecAdd(area.map.pos, area.map.labelPos) : null,
name: area.name,
pos: area.map.pos,
+ short: area.short,
size: area.map.size,
bg: {
src: area.map.bg.src,
})
};
+const MapAnnotation = ({ annotation }) => {
+ return <image
+ className="annotation"
+ href={annotation.icon}
+ x={annotation.pos.x}
+ y={annotation.pos.y}
+ width={annotation.size}
+ height={annotation.size}
+ >
+ <title>{annotation.name}</title>
+ </image>;
+};
+
+MapAnnotation.propTypes = {
+ annotation: PropTypes.shape({
+ icon: PropTypes.string,
+ name: PropTypes.string,
+ pos: PropTypes.shape({
+ x: PropTypes.number,
+ y: PropTypes.number,
+ }),
+ size: PropTypes.number,
+ }),
+};
+
const MixedPoolsTracker = () => {
const [connections, setConnections] = React.useState({});
const [dragging, setDragging] = React.useState(null);
return cs;
}, [connections]);
+ const annotations = React.useMemo(() => {
+ const annotate = [
+ 'songs.minuet',
+ 'songs.bolero',
+ 'songs.serenade',
+ 'songs.nocturne',
+ 'songs.requiem',
+ 'songs.prelude',
+ 'spawns.child',
+ 'spawns.adult',
+ 'owls.lhowl',
+ 'owls.dmtowl',
+ ];
+ const ans = [];
+ annotate.forEach((id) => {
+ if (!connections[id]) return;
+ const srcEntrance = getEntrance(id);
+ if (!srcEntrance) return;
+ const dstMap = getMapEntrance(connections[id]);
+ if (!dstMap) return;
+ ans.push({
+ icon: srcEntrance.icon,
+ name: srcEntrance.name,
+ pos: vecAdd(dstMap.pos, dstMap.annotationOffset || { x: 0, y: 0 }),
+ size: srcEntrance.iconSize || 8,
+ });
+ });
+ return ans;
+ }, [connections]);
+
return <CONTEXT.Provider value={context}>
<div className="mixed-pools-tracker">
<div className="columns">
onContextMenu={(e) => { e.preventDefault(); e.stopPropagation(); }}
>
{MAPS.map((map) =>
- <g key={map.id} title={map.name}>
+ <g className="area" key={map.id} title={map.name}>
<image
href={map.bg.src}
pointerEvents="none"
x={map.bg.pos.x} y={map.bg.pos.y}
width={map.bg.size.x}
/>
+ {map.labelPos ?
+ <text
+ className="area-label"
+ x={map.labelPos.x}
+ y={map.labelPos.y}
+ fill={map.color}
+ >
+ {map.short}
+ </text>
+ : null}
{map.entrances.map((entrance) =>
<MapEntrance key={entrance.id} entrance={entrance} />
)}
/>
)}
</g>
+ <g title="anotations">
+ {annotations.map((a) =>
+ <MapAnnotation
+ key={`${a.id}`}
+ annotation={a}
+ />
+ )}
+ </g>
</svg>
</div>
</div>