From 27c17a47267689a9eccd6c0529bbea4dfad4d3e4 Mon Sep 17 00:00:00 2001 From: Felix Lee Date: Mon, 20 Jul 2026 02:36:41 -0700 Subject: [PATCH] saws preset + editor updates --- src/components/LayoutSelector.js | 10 + src/layouts/HashFrogSAWS.json | 428 ++++++++++++++++++++++ src/scenes/Editor/Editor.js | 2 +- src/scenes/Editor/EditorComponentsList.js | 30 +- src/utils/logic-helper.js | 8 +- 5 files changed, 474 insertions(+), 4 deletions(-) create mode 100644 src/layouts/HashFrogSAWS.json diff --git a/src/components/LayoutSelector.js b/src/components/LayoutSelector.js index 14d7892..c9a6c5e 100644 --- a/src/components/LayoutSelector.js +++ b/src/components/LayoutSelector.js @@ -6,6 +6,7 @@ import { readFileAsText } from "../utils/utils"; // Layouts import hashfrogJSON from "../layouts/hashfrog.json"; +import hashfrogSawsJSON from "../layouts/HashFrogSAWS.json"; import linsoJSON from "../layouts/linso.json"; import escapefromkakJSON from "../layouts/escapefromkak.json"; @@ -48,6 +49,9 @@ const LayoutSelector = () => { case "escapefromkak": selectedLayout = escapefromkakJSON; break; + case "hashfrogSaws": + selectedLayout = hashfrogSawsJSON; + break; default: selectedLayout = hashfrogJSON; break; @@ -102,6 +106,12 @@ const LayoutSelector = () => { EscapeFromKak +
  • |
  • +
  • + +
  • ); diff --git a/src/layouts/HashFrogSAWS.json b/src/layouts/HashFrogSAWS.json new file mode 100644 index 0000000..0f9a612 --- /dev/null +++ b/src/layouts/HashFrogSAWS.json @@ -0,0 +1,428 @@ +{ + "id": "1d5a40bc94254c548b2cd1ed77e3ea5d", + "layoutConfig": { + "name": "HashFrogSAWS", + "backgroundColor": "#000", + "width": 325, + "height": 650, + "fontFamily": null, + "fontSize": null, + "fontColor": null + }, + "components": [ + { + "id": "80104bad45684265a7c638b0af398c6d", + "type": "table", + "position": [ + 5, + 5 + ], + "columns": 10, + "padding": "3px", + "elements": [ + "kokiri_sword", + "master_sword", + "biggoron_sword", + "sticks", + "nuts", + "bombs", + "bow", + "fire_arrows", + "dins", + "rutos_letter", + "deku_shield", + "hylian_shield", + "mirror_shield", + "slingshot", + "progressive_ocarina", + "bombchus", + "hookshot", + "ice_arrows", + "farores", + "bottle_all", + "strength", + "tunic_goron", + "tunic_zora", + "boomerang", + "lens", + "beans", + "hammer", + "light_arrows", + "nayrus", + "wallet", + "magic", + "boots_iron", + "boots_hover", + "scale", + "gerudo_card", + "weird_egg", + "zeldas_letter", + "mask_of_truth" + ], + "elementsSize": [ + 25, + 25 + ], + "displayName": "Items & Equipment", + "elementId": "a081121b16f84366bf16e16ca90cd23f" + }, + { + "id": "6d98c18059154e28a7ded50cfec565c5", + "type": "table", + "position": [ + 132, + 5 + ], + "columns": 10, + "padding": "3px", + "elements": [ + "pocket_egg", + "cojiro", + "odd_mushroom", + "odd_potion", + "poachers_saw", + "broken_sword", + "prescription", + "hash_frog", + "eye_drops" + ], + "elementsSize": [ + 25, + 25 + ], + "displayName": "Trade Items", + "hidden": false, + "elementId": "a081121b16f84366bf16e16ca90cd23f" + }, + { + "id": "af8a06b46398414abe4f59668e115d2c", + "type": "table", + "position": [ + 166, + 5 + ], + "columns": 5, + "padding": "3px", + "elements": [ + "ocarina_a_button", + "ocarina_c_up_button", + "ocarina_c_down_button", + "ocarina_c_left_button", + "ocarina_c_right_button" + ], + "elementsSize": [ + 25, + 25 + ], + "displayName": "Ocarina Buttons", + "hidden": false, + "elementId": "a081121b16f84366bf16e16ca90cd23f" + }, + { + "id": "7717af26740543a88a97e44df5f73a9f", + "type": "table", + "position": [ + 200, + 4 + ], + "columns": 6, + "padding": "4px 3px", + "elements": [ + "song_lullaby", + "song_epona", + "song_saria", + "song_suns", + "song_time", + "song_storms", + "song_minuet", + "song_bolero", + "song_serenade", + "song_requiem", + "song_nocturne", + "song_prelude" + ], + "elementsSize": [ + 25, + 25 + ], + "displayName": "Songs", + "elementId": "a081121b16f84366bf16e16ca90cd23f" + }, + { + "id": "e5af8c00cfaf4305aabdfe12f61f95f8", + "type": "table", + "position": [ + 160, + 191 + ], + "columns": 3, + "padding": "8px", + "elements": [ + "stone_kokiri", + "stone_goron", + "stone_zora" + ], + "elementsSize": [ + 25, + 25 + ], + "displayName": "Spiritual Stones", + "elementId": "a081121b16f84366bf16e16ca90cd23f" + }, + { + "id": "d64023910f6844e5a29bdb01ebb83fa7", + "type": "hinttable", + "elementId": "4c1b24c3e3954038b14f4daa3656e0b5", + "position": [ + 273, + 4 + ], + "hintType": "sometimes", + "hintNumber": 5, + "columns": 1, + "width": 175, + "padding": "2px", + "labels": "sometimes", + "color": "#ffffff", + "backgroundColor": "#333333", + "showIcon": true, + "inverted": false, + "showBoss": true, + "showItems": true, + "displayName": "Sometimes Hints", + "dual": false + }, + { + "id": "f8f2fc4d1ff648f583679a00dfbe97c8", + "type": "sometimeshint", + "displayName": "Dual Hint", + "elementId": "4c1b24c3e3954038b14f4daa3656e0b5", + "position": [ + 405, + 6 + ], + "labels": "sometimes", + "width": 199, + "color": "#ffffff", + "backgroundColor": "#333333", + "showIcon": true, + "inverted": false, + "dual": true, + "hidden": false + }, + { + "id": "273870ce25414299a6305db007807b4e", + "type": "hinttable", + "elementId": "4c1b24c3e3954038b14f4daa3656e0b5", + "position": [ + 430, + 4 + ], + "hintType": "location", + "hintNumber": 5, + "columns": 1, + "width": 310, + "padding": "2px", + "labels": "locations", + "color": "#ffffff", + "backgroundColor": "#2082aa", + "showIcon": true, + "inverted": false, + "showBoss": true, + "showItems": true, + "displayName": "Path Hints", + "bossElements": [ + "saws_goals" + ] + }, + { + "id": "9b4efc9066574c9ab8a46d79b6349e26", + "type": "hinttable", + "elementId": "4c1b24c3e3954038b14f4daa3656e0b5", + "position": [ + 562, + 4 + ], + "hintType": "location", + "hintNumber": 3, + "columns": 1, + "width": 310, + "padding": "2px", + "labels": "locations", + "color": "#ffffff", + "backgroundColor": "#c20f0f", + "showIcon": true, + "inverted": false, + "showBoss": false, + "showItems": false, + "displayName": "Foolish Locations" + }, + { + "id": "285d253c2b344e2798bf4e7a11565ff4", + "type": "label", + "displayName": "House Cow Label", + "position": [ + 247, + 190 + ], + "color": "#ffffff", + "backgroundColor": "#333333", + "padding": "2px", + "text": "House Cow", + "fontSize": "12px", + "hidden": false, + "elementId": "a081121b16f84366bf16e16ca90cd23f" + }, + { + "id": "7c07f92746d146249755ec5f853483e3", + "type": "label", + "displayName": "ZL Label", + "position": [ + 273, + 190 + ], + "color": "#ffffff", + "backgroundColor": "#333333", + "padding": "2px", + "text": "Zelda's Letter", + "fontSize": "12px", + "hidden": false, + "elementId": "a081121b16f84366bf16e16ca90cd23f" + }, + { + "id": "3b37df03a4ba4b84a210d7ae9cc453bf", + "type": "label", + "displayName": "ZF Cave GS", + "position": [ + 299, + 190 + ], + "color": "#ffffff", + "backgroundColor": "#333333", + "padding": "2px", + "text": "ZF Cave GS", + "fontSize": "12px", + "hidden": false, + "elementId": "a081121b16f84366bf16e16ca90cd23f" + }, + { + "id": "f7cd770eb44e4d95b1af57db2e37a175", + "type": "label", + "displayName": "OoT Label", + "position": [ + 325, + 190 + ], + "color": "#ffffff", + "backgroundColor": "#333333", + "padding": "2px", + "text": "OoT", + "fontSize": "12px", + "hidden": false, + "elementId": "a081121b16f84366bf16e16ca90cd23f" + }, + { + "id": "e664dd673cc84070a829575298cd7171", + "type": "label", + "displayName": "DMC Dual Label", + "position": [ + 351, + 190 + ], + "color": "#ffffff", + "backgroundColor": "#333333", + "padding": "2px", + "text": "DMC GS+Scr", + "fontSize": "12px", + "hidden": false, + "elementId": "a081121b16f84366bf16e16ca90cd23f" + }, + { + "id": "5de523e52d64426eb7d9edbab2e305bc", + "type": "label", + "displayName": "HF Cow+GS Label", + "position": [ + 377, + 190 + ], + "color": "#ffffff", + "backgroundColor": "#333333", + "padding": "2px", + "text": "HF Cow+GS", + "fontSize": "12px", + "hidden": false, + "elementId": "a081121b16f84366bf16e16ca90cd23f" + }, + { + "id": "ff7d937930a04642b02ca636f0c4395a", + "type": "table", + "displayName": "Always Frogs 1", + "position": [ + 247, + 293 + ], + "columns": 1, + "padding": "3px", + "elements": [ + "sometimes", + "sometimes", + "sometimes", + "sometimes", + "sometimes", + "sometimes" + ], + "elementsSize": [ + 20, + 20 + ], + "hidden": false, + "elementId": "a081121b16f84366bf16e16ca90cd23f" + }, + { + "id": "44a89ae2efac44dca81a7f50e62e66fb", + "type": "table", + "displayName": "Always Frogs 2", + "position": [ + 351, + 268 + ], + "columns": 1, + "padding": "3px", + "elements": [ + "sometimes", + "sometimes" + ], + "elementsSize": [ + 20, + 20 + ], + "hidden": false, + "elementId": "a081121b16f84366bf16e16ca90cd23f" + } + ], + "elements": [ + { + "id": "f89bfeec40d142ee8e5cdac808f69203", + "type": "simple", + "name": "saws_goals", + "displayName": "SAWS Goals", + "label": "", + "labelStartingIndex": 0, + "size": [ + 25, + 25 + ], + "icons": [ + "boss_na_25x25.png", + "song_time_32x32.png", + "light_arrows_32x32.png" + ], + "countConfig": [ + 0, + 1 + ], + "receiver": false, + "selectedStartingIndex": 0 + } + ] +} \ No newline at end of file diff --git a/src/scenes/Editor/Editor.js b/src/scenes/Editor/Editor.js index bf2e2a9..4aa2229 100644 --- a/src/scenes/Editor/Editor.js +++ b/src/scenes/Editor/Editor.js @@ -145,7 +145,7 @@ const Editor = () => { if (layout.layoutConfig.name) { filename = layout.layoutConfig.name.replace(/ /g, "_"); } - const jsonBlob = new Blob([JSON.stringify(layout)], { type: "text/plain" }); + const jsonBlob = new Blob([JSON.stringify(layout, null, 2)], { type: "text/plain" }); FileSaver.saveAs(jsonBlob, `${filename}.json`); }, [layout]); diff --git a/src/scenes/Editor/EditorComponentsList.js b/src/scenes/Editor/EditorComponentsList.js index 2ccc7a8..8bb6e78 100644 --- a/src/scenes/Editor/EditorComponentsList.js +++ b/src/scenes/Editor/EditorComponentsList.js @@ -6,6 +6,7 @@ import EditorComponent from "./EditorComponent"; const EditorComponentsList = ({ components, setLayout, customElements }) => { const [component, setComponent] = useState(null); + const [draggedId, setDraggedId] = useState(null); const addComponent = () => { setComponent(prev => ({ @@ -54,6 +55,21 @@ const EditorComponentsList = ({ components, setLayout, customElements }) => { const duplicateComponent = useCallback(() => duplicate(component, components, setComponent), [component, components]); + const onDragOver = useCallback( + index => { + if (!draggedId) { return; } + setLayout(prev => { + if (prev.components[index]?.id === draggedId) { return prev; } + const dragged = prev.components.find(x => x.id === draggedId); + if (!dragged) { return prev; } + const updated = prev.components.filter(x => x.id !== draggedId); + updated.splice(index, 0, dragged); + return { ...prev, components: updated }; + }); + }, + [draggedId, setLayout], + ); + return (
    Components
    @@ -75,12 +91,22 @@ const EditorComponentsList = ({ components, setLayout, customElements }) => { )} {!component && components.length < 1 &&

    Add a component to start

    } + {!component && components.length > 0 && ( +

    Drag to re-order.

    + )} {!component && components.length > 0 && (
      {!component && components.length > 0 && - components.map(comp => ( -
    • + components.map((comp, index) => ( +
    • setDraggedId(comp.id)} + onDragEnd={() => setDraggedId(null)} + onDragOver={() => onDragOver(index)} + style={{ cursor: "move" }} + > diff --git a/src/utils/logic-helper.js b/src/utils/logic-helper.js index bee432e..afdd237 100644 --- a/src/utils/logic-helper.js +++ b/src/utils/logic-helper.js @@ -775,7 +775,13 @@ class LogicHelper { } if (name in MASK_LOCATIONS) { - return this.items[name] > 0 || this.isLocationAvailable(MASK_LOCATIONS[name]); + if (this.items[name] > 0) { + return true; + } + if (SettingsHelper.hasShuffleChildTrade(name.replace(/_/g, " "))) { + return false; + } + return this.isLocationAvailable(MASK_LOCATIONS[name]); } if (name in ADULT_TRADE_LOOKUP) {