From a6e3d4ca20de7d095020942f774a847be6f4a2b4 Mon Sep 17 00:00:00 2001 From: Hydrox6 Date: Mon, 5 Jul 2021 22:41:47 +0100 Subject: [PATCH] plugins: add roof removal plugin --- .../roofremoval/RoofRemovalConfig.java | 75 + .../roofremoval/RoofRemovalPlugin.java | 267 +++ .../client/plugins/roofremoval/overrides.json | 2121 +++++++++++++++++ 3 files changed, 2463 insertions(+) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/roofremoval/RoofRemovalConfig.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/roofremoval/RoofRemovalPlugin.java create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/roofremoval/overrides.json diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/roofremoval/RoofRemovalConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/roofremoval/RoofRemovalConfig.java new file mode 100644 index 0000000000..39bf615853 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/roofremoval/RoofRemovalConfig.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2021, Hydrox6 + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.roofremoval; + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup(RoofRemovalConfig.CONFIG_GROUP) +public interface RoofRemovalConfig extends Config +{ + String CONFIG_GROUP = "roofremoval"; + + @ConfigItem( + keyName = "removePosition", + name = "Player's position", + description = "Remove roofs above the player's position" + ) + default boolean removePosition() + { + return true; + } + + @ConfigItem( + keyName = "removeHovered", + name = "Hovered tile", + description = "Remove roofs above the hovered tile" + ) + default boolean removeHovered() + { + return true; + } + + @ConfigItem( + keyName = "removeDestination", + name = "Destination tile", + description = "Remove roofs above the destination tile" + ) + default boolean removeDestination() + { + return true; + } + + @ConfigItem( + keyName = "removeBetween", + name = "Between camera & player", + description = "Remove roofs between the camera and the player at low camera angles" + ) + default boolean removeBetween() + { + return true; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/roofremoval/RoofRemovalPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/roofremoval/RoofRemovalPlugin.java new file mode 100644 index 0000000000..67700457ec --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/roofremoval/RoofRemovalPlugin.java @@ -0,0 +1,267 @@ +/* + * Copyright (c) 2021 Hydrox6 + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.roofremoval; + +import com.google.common.base.Stopwatch; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import com.google.inject.Provides; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.inject.Inject; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Client; +import net.runelite.api.Constants; +import static net.runelite.api.Constants.ROOF_FLAG_BETWEEN; +import static net.runelite.api.Constants.ROOF_FLAG_DESTINATION; +import static net.runelite.api.Constants.ROOF_FLAG_HOVERED; +import static net.runelite.api.Constants.ROOF_FLAG_POSITION; +import net.runelite.api.GameState; +import net.runelite.api.Tile; +import net.runelite.api.events.GameStateChanged; +import net.runelite.client.callback.ClientThread; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.events.ConfigChanged; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; + +@PluginDescriptor( + name = "Roof Removal", + description = "Remove only the needed roofs above your player, hovered tile, or destination", + enabledByDefault = false +) +@Slf4j +public class RoofRemovalPlugin extends Plugin +{ + private static class FlaggedArea + { + int rx1; + int ry1; + int rx2; + int ry2; + int z1; + int z2; + } + + @Inject + private Client client; + + @Inject + private ClientThread clientThread; + + @Inject + private Gson gson; + + @Inject + private RoofRemovalConfig config; + + private final Map overrides = new HashMap<>(); + + @Provides + RoofRemovalConfig getConfig(ConfigManager configManager) + { + return configManager.getConfig(RoofRemovalConfig.class); + } + + @Override + public void startUp() throws IOException + { + loadRoofOverrides(); + clientThread.invoke(() -> + { + if (client.getGameState() == GameState.LOGGED_IN) + { + performRoofRemoval(); + } + client.getScene().setRoofRemovalMode(buildRoofRemovalFlags()); + }); + } + + @Override + public void shutDown() + { + overrides.clear(); + clientThread.invoke(() -> + { + client.getScene().setRoofRemovalMode(0); + // Reload the scene to clear roof flag overrides + if (client.getGameState() == GameState.LOGGED_IN) + { + client.setGameState(GameState.LOADING); + } + }); + } + + @Subscribe + public void onGameStateChanged(GameStateChanged e) + { + if (e.getGameState() == GameState.LOGGED_IN) + { + performRoofRemoval(); + } + } + + @Subscribe + public void onConfigChanged(ConfigChanged e) + { + if (!e.getGroup().equals(RoofRemovalConfig.CONFIG_GROUP)) + { + return; + } + + client.getScene().setRoofRemovalMode(buildRoofRemovalFlags()); + } + + private int buildRoofRemovalFlags() + { + int roofRemovalMode = 0; + if (config.removePosition()) + { + roofRemovalMode |= ROOF_FLAG_POSITION; + } + if (config.removeHovered()) + { + roofRemovalMode |= ROOF_FLAG_HOVERED; + } + if (config.removeDestination()) + { + roofRemovalMode |= ROOF_FLAG_DESTINATION; + } + if (config.removeBetween()) + { + roofRemovalMode |= ROOF_FLAG_BETWEEN; + } + return roofRemovalMode; + } + + private void performRoofRemoval() + { + assert client.isClientThread(); + applyRoofOverrides(); + + Stopwatch sw = Stopwatch.createStarted(); + client.getScene().generateHouses(); + log.debug("House generation duration: {}", sw.stop()); + } + + private void loadRoofOverrides() throws IOException + { + try (InputStream in = getClass().getResourceAsStream("overrides.json")) + { + final InputStreamReader data = new InputStreamReader(in, StandardCharsets.UTF_8); + //CHECKSTYLE:OFF + final Type type = new TypeToken>>() {}.getType(); + //CHECKSTYLE:ON + Map> parsed = gson.fromJson(data, type); + overrides.clear(); + for (Map.Entry> entry : parsed.entrySet()) + { + for (FlaggedArea fla : entry.getValue()) + { + for (int z = fla.z1; z <= fla.z2; z++) + { + // Given that each region is 64x64, and the override data is a boolean, one of the axis can be stored as + // bits in a long. This removes the need for a boolean[64][64] and an extra array lookup in favour of + // a bitwise &, and results in a consistently smaller amount of memory required to store the overrides. + int packedRegion = entry.getKey() << 2 | z; + long[] regionData = overrides.computeIfAbsent(packedRegion, k -> new long[Constants.REGION_SIZE]); + for (int y = fla.ry1; y <= fla.ry2; y++) + { + long row = regionData[y]; + for (int x = fla.rx1; x <= fla.rx2; x++) + { + row |= (1L << x); + } + regionData[y] = row; + } + } + } + } + } + } + + private void applyRoofOverrides() + { + Stopwatch sw = Stopwatch.createStarted(); + boolean regionsHaveOverrides = false; + + outer: + for (int regionID : client.getMapRegions()) + { + for (int z = 0; z < Constants.MAX_Z; z++) + { + if (overrides.containsKey(regionID << 2 | z)) + { + regionsHaveOverrides = true; + break outer; + } + } + } + if (!regionsHaveOverrides) + { + return; + } + + Tile[][][] tiles = client.getScene().getTiles(); + byte[][][] settings = client.getTileSettings(); + + for (int z = 0; z < Constants.MAX_Z; z++) + { + for (int x = 0; x < Constants.SCENE_SIZE; x++) + { + for (int y = 0; y < Constants.SCENE_SIZE; y++) + { + Tile tile = tiles[z][x][y]; + if (tile == null) + { + continue; + } + + int regionID = tile.getWorldLocation().getRegionID() << 2 | z; + if (!overrides.containsKey(regionID)) + { + continue; + } + + int rx = tile.getWorldLocation().getRegionX(); + int ry = tile.getWorldLocation().getRegionY(); + long[] region = overrides.get(regionID); + if ((region[ry] & (1L << rx)) != 0) + { + settings[z][x][y] |= Constants.TILE_FLAG_UNDER_ROOF; + } + } + } + } + log.debug("Roof override duration: {}", sw.stop()); + } +} diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/roofremoval/overrides.json b/runelite-client/src/main/resources/net/runelite/client/plugins/roofremoval/overrides.json new file mode 100644 index 0000000000..ee4851899d --- /dev/null +++ b/runelite-client/src/main/resources/net/runelite/client/plugins/roofremoval/overrides.json @@ -0,0 +1,2121 @@ +{ + "12946": [ // Smoke Dungeon + { + "rx1": 0, + "ry1": 0, + "rx2": 35, + "ry2": 63, + "z1": 0, + "z2": 0 + } + ], + "13203": [ // Desert Mining Camp Underground + { + "rx1": 11, + "ry1": 15, + "rx2": 14, + "ry2": 18, + "z1": 0, + "z2": 0 + }, + { + "rx1": 16, + "ry1": 9, + "rx2": 20, + "ry2": 19, + "z1": 0, + "z2": 0 + } + ], + "13358": [ // Polnivneach + { + "rx1": 33, + "ry1": 49, + "rx2": 34, + "ry2": 50, + "z1": 0, + "z2": 0 + }, + { + "rx1": 24, + "ry1": 11, + "rx2": 25, + "ry2": 11, + "z1": 0, + "z2": 0 + } + ], + "11828": [ // Falador Castle Gate + { + "rx1": 18, + "ry1": 21, + "rx2": 21, + "ry2": 22, + "z1": 0, + "z2": 1 + }, + { + "rx1": 17, + "ry1": 17, + "rx2": 17, + "ry2": 21, + "z1": 0, + "z2": 0 + } + ], + "12895":[ // Prif NW + { // Entrance North + "rx1": 61, + "ry1": 14, + "rx2": 63, + "ry2": 17, + "z1": 0, + "z2": 2 + }, + { // Entrance West + "rx1": 49, + "ry1": 0, + "rx2": 52, + "ry2": 5, + "z1": 0, + "z2": 2 + }, + { // Balcony North + "rx1": 57, + "ry1": 12, + "rx2": 59, + "ry2": 15, + "z1": 0, + "z2": 0 + }, + { // Balcony West + "rx1": 51, + "ry1": 7, + "rx2": 54, + "ry2": 9, + "z1": 0, + "z2": 0 + }, + { // Roof Peak Corner + "rx1": 60, + "ry1": 5, + "rx2": 61, + "ry2": 6, + "z1": 0, + "z2": 0 + }, + { // Roof Peak Centre + "rx1": 61, + "ry1": 0, + "rx2": 63, + "ry2": 4, + "z1": 0, + "z2": 2 + }, + { // Upper Floor Roof + "rx1": 56, + "ry1": 8, + "rx2": 58, + "ry2": 10, + "z1": 2, + "z2": 2 + }, + { // Amlodd Tower Upper Floor + "rx1": 18, + "ry1": 17, + "rx2": 24, + "ry2": 22, + "z1": 2, + "z2": 2 + }, + { // Hefin Tower Upper Floor + "rx1": 44, + "ry1": 42, + "rx2": 49, + "ry2": 48, + "z1": 2, + "z2": 2 + } + ], + "13151":[ // Prif NE + { // Entrance North + "rx1": 0, + "ry1": 14, + "rx2": 2, + "ry2": 17, + "z1": 0, + "z2": 2 + }, + { // Entrance East + "rx1": 11, + "ry1": 0, + "rx2": 14, + "ry2": 5, + "z1": 0, + "z2": 2 + }, + { // Balcony North + "rx1": 4, + "ry1": 12, + "rx2": 6, + "ry2": 15, + "z1": 0, + "z2": 0 + }, + { // Balcony East + "rx1": 9, + "ry1": 7, + "rx2": 12, + "ry2": 9, + "z1": 0, + "z2": 0 + }, + { // Roof Peak Corner + "rx1": 2, + "ry1": 5, + "rx2": 3, + "ry2": 6, + "z1": 0, + "z2": 0 + }, + { // Roof Peak Centre + "rx1": 0, + "ry1": 0, + "rx2": 2, + "ry2": 4, + "z1": 0, + "z2": 2 + }, + { // Upper Floor Roof + "rx1": 5, + "ry1": 8, + "rx2": 7, + "ry2": 10, + "z1": 2, + "z2": 2 + }, + { // Meilyr Tower Upper Floor + "rx1": 14, + "ry1": 42, + "rx2": 19, + "ry2": 48, + "z1": 2, + "z2": 2 + }, + { // Cryws Tower Upper Floor + "rx1": 39, + "ry1": 17, + "rx2": 45, + "ry2": 22, + "z1": 2, + "z2": 2 + } + ], + "12894": [ // Prif SW + { // Entrance South + "rx1": 61, + "ry1": 52, + "rx2": 63, + "ry2": 55, + "z1": 0, + "z2": 2 + }, + { // Balcony South + "rx1": 57, + "ry1": 54, + "rx2": 59, + "ry2": 57, + "z1": 0, + "z2": 0 + }, + { // Balcony West + "rx1": 51, + "ry1": 60, + "rx2": 54, + "ry2": 62, + "z1": 0, + "z2": 0 + }, + { // Roof Peak Corner + "rx1": 60, + "ry1": 63, + "rx2": 61, + "ry2": 63, + "z1": 0, + "z2": 0 + }, + { // Upper Floor Roof + "rx1": 56, + "ry1": 59, + "rx2": 58, + "ry2": 61, + "z1": 2, + "z2": 2 + }, + { // Iowerth Tower Upper Floor + "rx1": 44, + "ry1": 21, + "rx2": 49, + "ry2": 27, + "z1": 2, + "z2": 2 + }, + { // Ithell Tower Upper Floor + "rx1": 18, + "ry1": 47, + "rx2": 24, + "ry2": 52, + "z1": 2, + "z2": 2 + } + ], + "13150": [ // Prif SE + { // Entrance South + "rx1": 0, + "ry1": 52, + "rx2": 2, + "ry2": 55, + "z1": 0, + "z2": 2 + }, + { // Balcony South + "rx1": 4, + "ry1": 54, + "rx2": 6, + "ry2": 57, + "z1": 0, + "z2": 0 + }, + { // Balcony East + "rx1": 9, + "ry1": 60, + "rx2": 12, + "ry2": 62, + "z1": 0, + "z2": 0 + }, + { // Upper Floor Roof + "rx1": 5, + "ry1": 59, + "rx2": 7, + "ry2": 61, + "z1": 2, + "z2": 2 + }, + { // Cadarn Tower Upper Floor + "rx1": 39, + "ry1": 47, + "rx2": 45, + "ry2": 52, + "z1": 2, + "z2": 2 + }, + { // Traehaearn Tower Upper Floor + "rx1": 14, + "ry1": 21, + "rx2": 19, + "ry2": 27, + "z1": 2, + "z2": 2 + } + ], + "14388": [ // Darkmeyer + { + "rx1": 34, + "ry1": 54, + "rx2": 36, + "ry2": 56, + "z1": 0, + "z2": 0 + }, + { + "rx1": 58, + "ry1": 53, + "rx2": 60, + "ry2": 55, + "z1": 0, + "z2": 0 + }, + { + "rx1": 40, + "ry1": 25, + "rx2": 49, + "ry2": 42, + "z1": 0, + "z2": 0 + }, + { + "rx1": 39, + "ry1": 26, + "rx2": 50, + "ry2": 41, + "z1": 0, + "z2": 0 + }, + { + "rx1": 38, + "ry1": 27, + "rx2": 51, + "ry2": 40, + "z1": 0, + "z2": 0 + }, + { + "rx1": 37, + "ry1": 28, + "rx2": 52, + "ry2": 39, + "z1": 0, + "z2": 0 + }, + { + "rx1": 36, + "ry1": 29, + "rx2": 53, + "ry2": 38, + "z1": 0, + "z2": 0 + }, + { + "rx1": 35, + "ry1": 30, + "rx2": 35, + "ry2": 37, + "z1": 0, + "z2": 0 + } + ], + "10291": [ // Ardougne + { + "rx1": 27, + "ry1": 30, + "rx2": 29, + "ry2": 35, + "z1": 0, + "z2": 0 + }, + { + "rx1": 16, + "ry1": 32, + "rx2": 19, + "ry2": 37, + "z1": 0, + "z2": 0 + } + ], + "12854": [ // Varrock Castle + { + "rx1": 0, + "ry1": 11, + "rx2": 23, + "ry2": 11, + "z1": 0, + "z2": 0 + } + ], + "12852": [ // South Varrock + { + "rx1": 42, + "ry1": 58, + "rx2": 52, + "ry2": 58, + "z1": 0, + "z2": 0 + }, + { + "rx1": 53, + "ry1": 63, + "rx2": 54, + "ry2": 63, + "z1": 0, + "z2": 0 + } + ], + "12596": [ // Champion's Guild + { + "rx1": 52, + "ry1": 22, + "rx2": 58, + "ry2": 23, + "z1": 0, + "z2": 0 + } + ], + "8253": [ // Lunar Isle + { + "rx1": 49, + "ry1": 12, + "rx2": 56, + "ry2": 12, + "z1": 0, + "z2": 0 + }, + { + "rx1": 41, + "ry1": 18, + "rx2": 44, + "ry2": 18, + "z1": 0, + "z2": 0 + }, + { + "rx1": 34, + "ry1": 19, + "rx2": 36, + "ry2": 19, + "z1": 0, + "z2": 0 + }, + { + "rx1": 28, + "ry1": 15, + "rx2": 29, + "ry2": 18, + "z1": 0, + "z2": 0 + }, + { + "rx1": 25, + "ry1": 0, + "rx2": 29, + "ry2": 1, + "z1": 0, + "z2": 0 + }, + { + "rx1": 34, + "ry1": 4, + "rx2": 37, + "ry2": 5, + "z1": 0, + "z2": 0 + }, + { + "rx1": 46, + "ry1": 5, + "rx2": 50, + "ry2": 6, + "z1": 0, + "z2": 0 + } + ], + "8252": [ // Lunar Isle South + { + "rx1": 23, + "ry1": 62, + "rx2": 28, + "ry2": 62, + "z1": 0, + "z2": 0 + } + ], + "6456": [ // Mess Hall + { + "rx1": 47, + "ry1": 38, + "rx2": 48, + "ry2": 47, + "z1": 0, + "z2": 0 + } + ], + "6713": [ // Kourend Castle East + { + "rx1": 28, + "ry1": 20, + "rx2": 28, + "ry2": 21, + "z1": 0, + "z2": 0 + }, + { + "rx1": 28, + "ry1": 29, + "rx2": 28, + "ry2": 30, + "z1": 0, + "z2": 0 + } + ], + "6715": [ // North East Arceuus + { + "rx1": 20, + "ry1": 5, + "rx2": 50, + "ry2": 29, + "z1": 0, + "z2": 0 + }, + { + "rx1": 13, + "ry1": 5, + "rx2": 19, + "ry2": 12, + "z1": 0, + "z2": 0 + }, + { + "rx1": 13, + "ry1": 24, + "rx2": 19, + "ry2": 29, + "z1": 0, + "z2": 0 + } + ], + "6459": [ // Arceuus Library + { + "rx1": 7, + "ry1": 8, + "rx2": 26, + "ry2": 25, + "z1": 0, + "z2": 1 + }, + { + "rx1": 7, + "ry1": 38, + "rx2": 26, + "ry2": 55, + "z1": 0, + "z2": 1 + }, + { + "rx1": 7, + "ry1": 8, + "rx2": 26, + "ry2": 25, + "z1": 0, + "z2": 1 + }, + { + "rx1": 13, + "ry1": 27, + "rx2": 13, + "ry2": 36, + "z1": 0, + "z2": 1 + }, + { + "rx1": 39, + "ry1": 38, + "rx2": 58, + "ry2": 55, + "z1": 0, + "z2": 1 + }, + { + "rx1": 31, + "ry1": 44, + "rx2": 34, + "ry2": 50, + "z1": 0, + "z2": 1 + }, + { + "rx1": 40, + "ry1": 21, + "rx2": 43, + "ry2": 24, + "z1": 0, + "z2": 2 + }, + { + "rx1": 27, + "ry1": 41, + "rx2": 38, + "ry2": 50, + "z1": 1, + "z2": 1 + }, + { + "rx1": 13, + "ry1": 26, + "rx2": 24, + "ry2": 37, + "z1": 1, + "z2": 1 + } + ], + "6970": [ // Piscarilius West + { + "rx1": 60, + "ry1": 8, + "rx2": 60, + "ry2": 8, + "z1": 0, + "z2": 0 + }, + { + "rx1": 43, + "ry1": 41, + "rx2": 45, + "ry2": 42, + "z1": 0, + "z2": 0 + }, + { + "rx1": 23, + "ry1": 17, + "rx2": 24, + "ry2": 19, + "z1": 0, + "z2": 0 + }, + { + "rx1": 56, + "ry1": 49, + "rx2": 59, + "ry2": 60, + "z1": 0, + "z2": 0 + } + ], + "7226": [ // Piscarilius East + { + "rx1": 5, + "ry1": 6, + "rx2": 5, + "ry2": 13, + "z1": 0, + "z2": 0 + }, + { + "rx1": 35, + "ry1": 9, + "rx2": 36, + "ry2": 11, + "z1": 0, + "z2": 0 + }, + { + "rx1": 14, + "ry1": 28, + "rx2": 16, + "ry2": 29, + "z1": 0, + "z2": 0 + } + ], + "6203": [ // Lovakenj North East + { + "rx1": 19, + "ry1": 13, + "rx2": 21, + "ry2": 16, + "z1": 0, + "z2": 0 + }, + { + "rx1": 13, + "ry1": 4, + "rx2": 15, + "ry2": 6, + "z1": 0, + "z2": 0 + } + ], + "10288": [ // Yanille East + { + "rx1": 47, + "ry1": 19, + "rx2": 48, + "ry2": 22, + "z1": 0, + "z2": 0 + }, + { + "rx1": 52, + "ry1": 32, + "rx2": 54, + "ry2": 34, + "z1": 0, + "z2": 0 + }, + { + "rx1": 53, + "ry1": 31, + "rx2": 55, + "ry2": 33, + "z1": 0, + "z2": 0 + }, + { + "rx1": 54, + "ry1": 30, + "rx2": 56, + "ry2": 32, + "z1": 0, + "z2": 0 + }, + { + "rx1": 55, + "ry1": 29, + "rx2": 57, + "ry2": 31, + "z1": 0, + "z2": 0 + }, + { + "rx1": 48, + "ry1": 8, + "rx2": 48, + "ry2": 8, + "z1": 0, + "z2": 0 + } + ], + "10032": [ // Yanille West + { + "rx1": 35, + "ry1": 23, + "rx2": 36, + "ry2": 25, + "z1": 0, + "z2": 0 + }, + { + "rx1": 35, + "ry1": 14, + "rx2": 36, + "ry2": 16, + "z1": 0, + "z2": 0 + } + ], + "9775": [ // Jigjig + { + "rx1": 50, + "ry1": 32, + "rx2": 50, + "ry2": 32, + "z1": 0, + "z2": 0 + }, + { + "rx1": 56, + "ry1": 33, + "rx2": 56, + "ry2": 33, + "z1": 0, + "z2": 0 + }, + { + "rx1": 55, + "ry1": 43, + "rx2": 55, + "ry2": 43, + "z1": 0, + "z2": 0 + }, + { + "rx1": 45, + "ry1": 41, + "rx2": 46, + "ry2": 41, + "z1": 0, + "z2": 0 + }, + { + "rx1": 49, + "ry1": 43, + "rx2": 51, + "ry2": 45, + "z1": 0, + "z2": 0 + }, + { + "rx1": 45, + "ry1": 36, + "rx2": 45, + "ry2": 36, + "z1": 0, + "z2": 0 + } + ], + "10806": [ // Seers' Village + { + "rx1": 22, + "ry1": 41, + "rx2": 23, + "ry2": 42, + "z1": 0, + "z2": 0 + }, + { + "rx1": 28, + "ry1": 14, + "rx2": 28, + "ry2": 17, + "z1": 0, + "z2": 0 + }, + { + "rx1": 11, + "ry1": 13, + "rx2": 17, + "ry2": 13, + "z1": 0, + "z2": 0 + } + ], + "10549": [ // Ranging Guild + { + "rx1": 42, + "ry1": 49, + "rx2": 46, + "ry2": 53, + "z1": 0, + "z2": 0 + }, + { + "rx1": 57, + "ry1": 34, + "rx2": 61, + "ry2": 38, + "z1": 0, + "z2": 0 + }, + { + "rx1": 27, + "ry1": 34, + "rx2": 31, + "ry2": 38, + "z1": 0, + "z2": 0 + }, + { + "rx1": 49, + "ry1": 40, + "rx2": 49, + "ry2": 43, + "z1": 0, + "z2": 0 + }, + { + "rx1": 47, + "ry1": 44, + "rx2": 48, + "ry2": 44, + "z1": 0, + "z2": 0 + }, + { + "rx1": 50, + "ry1": 41, + "rx2": 50, + "ry2": 42, + "z1": 0, + "z2": 0 + }, + { + "rx1": 11, + "ry1": 55, + "rx2": 16, + "ry2": 56, + "z1": 0, + "z2": 0 + } + ], + "10293": [ // Fishing Guild + { + "rx1": 57, + "ry1": 2, + "rx2": 58, + "ry2": 6, + "z1": 0, + "z2": 0 + } + ], + "9781": [ // Gnome Stronghold SE + { + "rx1": 42, + "ry1": 5, + "rx2": 45, + "ry2": 8, + "z1": 0, + "z2": 0 + }, + { + "rx1": 39, + "ry1": 14, + "rx2": 41, + "ry2": 16, + "z1": 0, + "z2": 0 + }, + { + "rx1": 45, + "ry1": 14, + "rx2": 50, + "ry2": 16, + "z1": 0, + "z2": 0 + }, + { + "rx1": 46, + "ry1": 13, + "rx2": 50, + "ry2": 13, + "z1": 0, + "z2": 0 + }, + { + "rx1": 42, + "ry1": 15, + "rx2": 44, + "ry2": 15, + "z1": 0, + "z2": 0 + }, + { + "rx1": 55, + "ry1": 16, + "rx2": 58, + "ry2": 19, + "z1": 0, + "z2": 0 + }, + { + "rx1": 26, + "ry1": 24, + "rx2": 28, + "ry2": 26, + "z1": 0, + "z2": 0 + }, + { + "rx1": 25, + "ry1": 25, + "rx2": 25, + "ry2": 25, + "z1": 0, + "z2": 0 + }, + { + "rx1": 12, + "ry1": 23, + "rx2": 15, + "ry2": 26, + "z1": 0, + "z2": 0 + }, + { + "rx1": 11, + "ry1": 30, + "rx2": 17, + "ry2": 36, + "z1": 0, + "z2": 1 + }, + { + "rx1": 12, + "ry1": 39, + "rx2": 15, + "ry2": 42, + "z1": 0, + "z2": 0 + }, + { + "rx1": 13, + "ry1": 27, + "rx2": 14, + "ry2": 38, + "z1": 0, + "z2": 0 + }, + { + "rx1": 2, + "ry1": 4, + "rx2": 4, + "ry2": 6, + "z1": 0, + "z2": 0 + }, + { + "rx1": 2, + "ry1": 10, + "rx2": 4, + "ry2": 12, + "z1": 0, + "z2": 0 + }, + { + "rx1": 7, + "ry1": 9, + "rx2": 10, + "ry2": 12, + "z1": 0, + "z2": 0 + }, + { + "rx1": 3, + "ry1": 7, + "rx2": 3, + "ry2": 9, + "z1": 0, + "z2": 0 + }, + { + "rx1": 5, + "ry1": 11, + "rx2": 6, + "ry2": 11, + "z1": 0, + "z2": 0 + }, + { + "rx1": 40, + "ry1": 26, + "rx2": 43, + "ry2": 28, + "z1": 0, + "z2": 0 + }, + { + "rx1": 53, + "ry1": 26, + "rx2": 56, + "ry2": 29, + "z1": 0, + "z2": 0 + }, + { + "rx1": 44, + "ry1": 27, + "rx2": 44, + "ry2": 28, + "z1": 0, + "z2": 0 + }, + { + "rx1": 52, + "ry1": 27, + "rx2": 52, + "ry2": 28, + "z1": 0, + "z2": 0 + } + ], + "9525": [ // Gnome Stronghold SW + { + "rx1": 46, + "ry1": 22, + "rx2": 49, + "ry2": 25, + "z1": 0, + "z2": 0 + }, + { + "rx1": 50, + "ry1": 23, + "rx2": 51, + "ry2": 25, + "z1": 0, + "z2": 0 + }, + { + "rx1": 51, + "ry1": 28, + "rx2": 56, + "ry2": 33, + "z1": 0, + "z2": 2 + }, + { + "rx1": 53, + "ry1": 30, + "rx2": 58, + "ry2": 34, + "z1": 0, + "z2": 0 + }, + { + "rx1": 53, + "ry1": 48, + "rx2": 57, + "ry2": 49, + "z1": 0, + "z2": 0 + }, + { + "rx1": 54, + "ry1": 49, + "rx2": 58, + "ry2": 51, + "z1": 0, + "z2": 0 + }, + { + "rx1": 56, + "ry1": 35, + "rx2": 56, + "ry2": 47, + "z1": 0, + "z2": 0 + }, + { + "rx1": 46, + "ry1": 40, + "rx2": 49, + "ry2": 43, + "z1": 0, + "z2": 0 + }, + { + "rx1": 39, + "ry1": 42, + "rx2": 41, + "ry2": 44, + "z1": 0, + "z2": 0 + }, + { + "rx1": 40, + "ry1": 45, + "rx2": 41, + "ry2": 47, + "z1": 0, + "z2": 0 + }, + { + "rx1": 44, + "ry1": 53, + "rx2": 48, + "ry2": 56, + "z1": 0, + "z2": 0 + }, + { + "rx1": 20, + "ry1": 57, + "rx2": 24, + "ry2": 60, + "z1": 0, + "z2": 0 + }, + { + "rx1": 29, + "ry1": 57, + "rx2": 33, + "ry2": 60, + "z1": 0, + "z2": 0 + }, + { + "rx1": 25, + "ry1": 59, + "rx2": 28, + "ry2": 59, + "z1": 0, + "z2": 0 + }, + { + "rx1": 23, + "ry1": 55, + "rx2": 31, + "ry2": 62, + "z1": 0, + "z2": 1 + } + ], + "9526": [ // Gnome Stronghold NE + { + "rx1": 40, + "ry1": 13, + "rx2": 46, + "ry2": 15, + "z1": 0, + "z2": 0 + }, + { + "rx1": 48, + "ry1": 9, + "rx2": 52, + "ry2": 12, + "z1": 0, + "z2": 0 + }, + { + "rx1": 49, + "ry1": 16, + "rx2": 54, + "ry2": 20, + "z1": 0, + "z2": 0 + }, + { + "rx1": 50, + "ry1": 13, + "rx2": 50, + "ry2": 15, + "z1": 0, + "z2": 0 + }, + { + "rx1": 47, + "ry1": 14, + "rx2": 49, + "ry2": 14, + "z1": 0, + "z2": 0 + }, + { + "rx1": 43, + "ry1": 15, + "rx2": 51, + "ry2": 21, + "z1": 0, + "z2": 1 + }, + { + "rx1": 48, + "ry1": 26, + "rx2": 51, + "ry2": 30, + "z1": 0, + "z2": 0 + }, + { + "rx1": 48, + "ry1": 39, + "rx2": 51, + "ry2": 43, + "z1": 0, + "z2": 0 + }, + { + "rx1": 49, + "ry1": 31, + "rx2": 50, + "ry2": 38, + "z1": 0, + "z2": 0 + }, + { + "rx1": 43, + "ry1": 29, + "rx2": 44, + "ry2": 37, + "z1": 0, + "z2": 0 + }, + { + "rx1": 24, + "ry1": 20, + "rx2": 32, + "ry2": 21, + "z1": 0, + "z2": 0 + }, + { + "rx1": 10, + "ry1": 30, + "rx2": 11, + "ry2": 37, + "z1": 0, + "z2": 0 + }, + { + "rx1": 24, + "ry1": 43, + "rx2": 32, + "ry2": 44, + "z1": 0, + "z2": 0 + }, + { + "rx1": 12, + "ry1": 48, + "rx2": 16, + "ry2": 53, + "z1": 0, + "z2": 0 + }, + { + "rx1": 19, + "ry1": 57, + "rx2": 23, + "ry2": 60, + "z1": 0, + "z2": 0 + }, + { + "rx1": 28, + "ry1": 57, + "rx2": 32, + "ry2": 60, + "z1": 0, + "z2": 0 + } + ], + "9782": [ // Gnome Stronghold NE + { + "rx1": 4, + "ry1": 7, + "rx2": 7, + "ry2": 10, + "z1": 0, + "z2": 0 + }, + { + "rx1": 10, + "ry1": 6, + "rx2": 13, + "ry2": 12, + "z1": 0, + "z2": 0 + }, + { + "rx1": 10, + "ry1": 9, + "rx2": 14, + "ry2": 13, + "z1": 0, + "z2": 1 + }, + { + "rx1": 16, + "ry1": 7, + "rx2": 19, + "ry2": 10, + "z1": 0, + "z2": 0 + }, + { + "rx1": 43, + "ry1": 6, + "rx2": 47, + "ry2": 9, + "z1": 0, + "z2": 0 + }, + { + "rx1": 48, + "ry1": 6, + "rx2": 52, + "ry2": 7, + "z1": 0, + "z2": 0 + }, + { + "rx1": 53, + "ry1": 7, + "rx2": 56, + "ry2": 11, + "z1": 0, + "z2": 0 + }, + + { + "rx1": 16, + "ry1": 22, + "rx2": 18, + "ry2": 26, + "z1": 0, + "z2": 0 + }, + { + "rx1": 6, + "ry1": 31, + "rx2": 10, + "ry2": 33, + "z1": 0, + "z2": 0 + }, + { + "rx1": 15, + "ry1": 30, + "rx2": 19, + "ry2": 34, + "z1": 0, + "z2": 0 + }, + { + "rx1": 11, + "ry1": 32, + "rx2": 14, + "ry2": 32, + "z1": 0, + "z2": 0 + }, + { + "rx1": 17, + "ry1": 27, + "rx2": 17, + "ry2": 29, + "z1": 0, + "z2": 0 + }, + { + "rx1": 15, + "ry1": 52, + "rx2": 19, + "ry2": 56, + "z1": 0, + "z2": 0 + }, + { + "rx1": 15, + "ry1": 44, + "rx2": 19, + "ry2": 48, + "z1": 0, + "z2": 0 + }, + { + "rx1": 17, + "ry1": 49, + "rx2": 17, + "ry2": 51, + "z1": 0, + "z2": 0 + }, + { + "rx1": 12, + "ry1": 46, + "rx2": 14, + "ry2": 46, + "z1": 0, + "z2": 0 + }, + { + "rx1": 17, + "ry1": 35, + "rx2": 17, + "ry2": 43, + "z1": 0, + "z2": 0 + }, + { + "rx1": 20, + "ry1": 46, + "rx2": 25, + "ry2": 46, + "z1": 0, + "z2": 0 + }, + { + "rx1": 25, + "ry1": 33, + "rx2": 25, + "ry2": 45, + "z1": 0, + "z2": 0 + }, + { + "rx1": 20, + "ry1": 32, + "rx2": 25, + "ry2": 32, + "z1": 0, + "z2": 0 + }, + { + "rx1": 26, + "ry1": 39, + "rx2": 29, + "ry2": 39, + "z1": 0, + "z2": 0 + }, + { + "rx1": 16, + "ry1": 38, + "rx2": 18, + "ry2": 42, + "z1": 0, + "z2": 1 + }, + { + "rx1": 19, + "ry1": 40, + "rx2": 29, + "ry2": 40, + "z1": 0, + "z2": 1 + }, + { + "rx1": 15, + "ry1": 21, + "rx2": 19, + "ry2": 25, + "z1": 0, + "z2": 1 + }, + { + "rx1": 31, + "ry1": 24, + "rx2": 35, + "ry2": 26, + "z1": 0, + "z2": 1 + }, + { + "rx1": 28, + "ry1": 31, + "rx2": 30, + "ry2": 33, + "z1": 0, + "z2": 1 + }, + { + "rx1": 36, + "ry1": 31, + "rx2": 38, + "ry2": 33, + "z1": 0, + "z2": 1 + }, + { + "rx1": 33, + "ry1": 27, + "rx2": 33, + "ry2": 35, + "z1": 0, + "z2": 1 + }, + { + "rx1": 31, + "ry1": 32, + "rx2": 35, + "ry2": 32, + "z1": 0, + "z2": 1 + }, + { + "rx1": 34, + "ry1": 44, + "rx2": 34, + "ry2": 51, + "z1": 0, + "z2": 1 + }, + { + "rx1": 32, + "ry1": 52, + "rx2": 36, + "ry2": 54, + "z1": 0, + "z2": 1 + }, + { + "rx1": 37, + "ry1": 47, + "rx2": 39, + "ry2": 49, + "z1": 0, + "z2": 1 + }, + { + "rx1": 30, + "ry1": 47, + "rx2": 32, + "ry2": 49, + "z1": 0, + "z2": 1 + }, + { + "rx1": 33, + "ry1": 48, + "rx2": 36, + "ry2": 48, + "z1": 0, + "z2": 1 + }, + { + "rx1": 31, + "ry1": 43, + "rx2": 33, + "ry2": 46, + "z1": 0, + "z2": 2 + }, + { + "rx1": 31, + "ry1": 47, + "rx2": 31, + "ry2": 49, + "z1": 0, + "z2": 2 + }, + { + "rx1": 49, + "ry1": 22, + "rx2": 51, + "ry2": 26, + "z1": 0, + "z2": 0 + }, + { + "rx1": 48, + "ry1": 30, + "rx2": 52, + "ry2": 34, + "z1": 0, + "z2": 0 + }, + { + "rx1": 57, + "ry1": 31, + "rx2": 61, + "ry2": 33, + "z1": 0, + "z2": 0 + }, + { + "rx1": 49, + "ry1": 38, + "rx2": 51, + "ry2": 42, + "z1": 0, + "z2": 1 + }, + { + "rx1": 48, + "ry1": 44, + "rx2": 52, + "ry2": 48, + "z1": 0, + "z2": 0 + }, + { + "rx1": 57, + "ry1": 45, + "rx2": 61, + "ry2": 47, + "z1": 0, + "z2": 0 + }, + { + "rx1": 49, + "ry1": 52, + "rx2": 51, + "ry2": 56, + "z1": 0, + "z2": 0 + }, + { + "rx1": 50, + "ry1": 52, + "rx2": 54, + "ry2": 56, + "z1": 0, + "z2": 1 + }, + { + "rx1": 53, + "ry1": 37, + "rx2": 57, + "ry2": 41, + "z1": 0, + "z2": 0 + }, + { + "rx1": 50, + "ry1": 27, + "rx2": 50, + "ry2": 29, + "z1": 0, + "z2": 0 + }, + { + "rx1": 53, + "ry1": 32, + "rx2": 56, + "ry2": 32, + "z1": 0, + "z2": 0 + }, + { + "rx1": 50, + "ry1": 35, + "rx2": 50, + "ry2": 43, + "z1": 0, + "z2": 0 + }, + { + "rx1": 53, + "ry1": 46, + "rx2": 56, + "ry2": 46, + "z1": 0, + "z2": 0 + }, + { + "rx1": 50, + "ry1": 49, + "rx2": 50, + "ry2": 51, + "z1": 0, + "z2": 0 + }, + { + "rx1": 42, + "ry1": 32, + "rx2": 47, + "ry2": 32, + "z1": 0, + "z2": 0 + }, + { + "rx1": 42, + "ry1": 46, + "rx2": 47, + "ry2": 46, + "z1": 0, + "z2": 0 + }, + { + "rx1": 42, + "ry1": 33, + "rx2": 42, + "ry2": 45, + "z1": 0, + "z2": 0 + }, + { + "rx1": 38, + "ry1": 39, + "rx2": 41, + "ry2": 39, + "z1": 0, + "z2": 0 + }, + { + "rx1": 38, + "ry1": 40, + "rx2": 48, + "ry2": 40, + "z1": 0, + "z2": 1 + } + ], + "9531": [ // Jatiszo + { + "rx1": 4, + "ry1": 23, + "rx2": 6, + "ry2": 25, + "z1": 0, + "z2": 0 + } + ], + "9275": [ // Neitiznot + { + "rx1": 58, + "ry1": 22, + "rx2": 60, + "ry2": 24, + "z1": 0, + "z2": 0 + } + ], + "11575": [ // Burthorpe + { + "rx1": 25, + "ry1": 24, + "rx2": 35, + "ry2": 24, + "z1": 0, + "z2": 0 + } + ], + "11319": [ // Warrior's Guild + { + "rx1": 21, + "ry1": 15, + "rx2": 61, + "ry2": 37, + "z1": 0, + "z2": 0 + }, + { + "rx1": 32, + "ry1": 13, + "rx2": 44, + "ry2": 15, + "z1": 0, + "z2": 0 + } + ], + "11061": [ // West Catherby + { + "rx1": 55, + "ry1": 20, + "rx2": 59, + "ry2": 38, + "z1": 0, + "z2": 0 + } + ], + "11317": [ // East Catherby + { + "rx1": 5, + "ry1": 48, + "rx2": 7, + "ry2": 48, + "z1": 0, + "z2": 0 + } + ], + "5945": [ // West Shayzien Encampment + { + "rx1": 1, + "ry1": 0, + "rx2": 2, + "ry2": 3, + "z1": 0, + "z2": 1 + } + ], + "10040": [ // Lighthouse + { + "rx1": 9, + "ry1": 53, + "rx2": 16, + "ry2": 60, + "z1": 1, + "z2": 2 + } + ], + "10547": [ // SE Ardy + { + "rx1": 53, + "ry1": 1, + "rx2": 63, + "ry2": 5, + "z1": 0, + "z2": 0 + }, + { + "rx1": 34, + "ry1": 54, + "rx2": 36, + "ry2": 54, + "z1": 0, + "z2": 1 + } + ], + "11570": [ // Rimmington Dock + { + "rx1": 28, + "ry1": 19, + "rx2": 39, + "ry2": 23, + "z1": 0, + "z2": 0 + }, + { + "rx1": 17, + "ry1": 28, + "rx2": 36, + "ry2": 32, + "z1": 0, + "z2": 0 + } + ], + "11058": [ // Brimhaven Docks + { + "rx1": 22, + "ry1": 32, + "rx2": 26, + "ry2": 46, + "z1": 0, + "z2": 0 + } + ], + "10284": [ // Corsair's Cove East + { + "rx1": 7, + "ry1": 47, + "rx2": 12, + "ry2": 52, + "z1": 0, + "z2": 0 + } + ], + "10028": [ // Corsair's Cove West + { + "rx1": 47, + "ry1": 44, + "rx2": 51, + "ry2": 48, + "z1": 0, + "z2": 0 + } + ], + "10545": [ // Port Khazard + { + "rx1": 45, + "ry1": 31, + "rx2": 49, + "ry2": 42, + "z1": 0, + "z2": 0 + } + ], + "11825": [ // Musa Point Docks + { + "rx1": 1, + "ry1": 5, + "rx2": 7, + "ry2": 5, + "z1": 0, + "z2": 0 + }, + { + "rx1": 7, + "ry1": 4, + "rx2": 7, + "ry2": 6, + "z1": 0, + "z2": 0 + } + ], + "14902": [ // Bill Teach's Ship at Port Phasmatys + { + "rx1": 0, + "ry1": 33, + "rx2": 4, + "ry2": 56, + "z1": 0, + "z2": 0 + } + ], + "14646": [ // Port Phasmatys + { + "rx1": 46, + "ry1": 47, + "rx2": 50, + "ry2": 63, + "z1": 0, + "z2": 0 + } + ], + "14647": [ // Ectofuntus + { + "rx1": 46, + "ry1": 0, + "rx2": 50, + "ry2": 11, + "z1": 0, + "z2": 0 + } + ], + "14638": [ // Mos Le'Harmless Port + { + "rx1": 20, + "ry1": 2, + "rx2": 43, + "ry2": 6, + "z1": 0, + "z2": 0 + } + ], + "6968": [ // Hosidius Town + { + "rx1": 17, + "ry1": 26, + "rx2": 18, + "ry2": 38, + "z1": 0, + "z2": 0 + } + ], + "6995": [ // Ancient Cavern + { + "rx1": 42, + "ry1": 15, + "rx2": 47, + "ry2": 20, + "z1": 0, + "z2": 0 + }, + { + "rx1": 31, + "ry1": 26, + "rx2": 43, + "ry2": 41, + "z1": 0, + "z2": 0 + }, + { + "rx1": 25, + "ry1": 33, + "rx2": 30, + "ry2": 35, + "z1": 0, + "z2": 0 + }, + { + "rx1": 29, + "ry1": 27, + "rx2": 30, + "ry2": 39, + "z1": 0, + "z2": 0 + }, + { + "rx1": 50, + "ry1": 33, + "rx2": 50, + "ry2": 33, + "z1": 0, + "z2": 0 + }, + { + "rx1": 35, + "ry1": 53, + "rx2": 41, + "ry2": 54, + "z1": 0, + "z2": 0 + } + ], + "11056": [ // North Tai Bwo Wannai + { + "rx1": 32, + "ry1": 1, + "rx2": 36, + "ry2": 5, + "z1": 0, + "z2": 0 + }, + { + "rx1": 48, + "ry1": 1, + "rx2": 52, + "ry2": 5, + "z1": 0, + "z2": 0 + } + ], + "11055": [ // South Tai Bwo Wannai + { + "rx1": 55, + "ry1": 56, + "rx2": 58, + "ry2": 59, + "z1": 0, + "z2": 0 + }, + { + "rx1": 36, + "ry1": 29, + "rx2": 39, + "ry2": 34, + "z1": 0, + "z2": 0 + }, + { + "rx1": 35, + "ry1": 30, + "rx2": 40, + "ry2": 33, + "z1": 0, + "z2": 0 + } + ] +} \ No newline at end of file