From f62a0e4c3258d4b572d598244c9a8ae8a7813a98 Mon Sep 17 00:00:00 2001 From: xp Date: Wed, 20 Jul 2022 15:22:51 -0700 Subject: [PATCH] Redesigned map tab --- .../gg/xp/xivsupport/gui/map/MapPanel.java | 214 ++++++++++++++++-- .../java/gg/xp/xivsupport/gui/map/MapTab.java | 143 +++++++++++- .../gui/tables/StandardColumns.java | 4 +- .../gui/tables/TableWithFilterAndDetails.java | 7 +- .../gui/tables/filters/GroovyFilter.java | 12 +- .../gg/xp/xivsupport/gui/GuiWithTestData.java | 64 +++--- 6 files changed, 381 insertions(+), 63 deletions(-) diff --git a/xivsupport/src/main/java/gg/xp/xivsupport/gui/map/MapPanel.java b/xivsupport/src/main/java/gg/xp/xivsupport/gui/map/MapPanel.java index bf95cf08dc1a..4c9ef8e89854 100644 --- a/xivsupport/src/main/java/gg/xp/xivsupport/gui/map/MapPanel.java +++ b/xivsupport/src/main/java/gg/xp/xivsupport/gui/map/MapPanel.java @@ -1,7 +1,6 @@ package gg.xp.xivsupport.gui.map; -import gg.xp.reevent.events.EventContext; -import gg.xp.reevent.scan.HandleEvents; +import com.formdev.flatlaf.util.ScaledImageIcon; import gg.xp.xivdata.data.ActionLibrary; import gg.xp.xivdata.data.Job; import gg.xp.xivdata.data.XivMap; @@ -25,6 +24,7 @@ import org.slf4j.LoggerFactory; import javax.swing.*; +import javax.swing.border.Border; import javax.swing.border.LineBorder; import java.awt.*; import java.awt.event.MouseEvent; @@ -32,12 +32,18 @@ import java.awt.event.MouseMotionListener; import java.awt.event.MouseWheelEvent; import java.awt.event.MouseWheelListener; +import java.awt.geom.AffineTransform; import java.io.Serial; import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Consumer; public class MapPanel extends JPanel implements MouseMotionListener, MouseListener, MouseWheelListener { @@ -56,6 +62,10 @@ public class MapPanel extends JPanel implements MouseMotionListener, MouseListen private final ActiveCastRepository acr; private XivMap map = XivMap.UNKNOWN; private Image backgroundImage; + // -1 indicates no selection + private volatile long selection = -1; + private Consumer<@Nullable XivCombatant> selectionCallback = l -> { + }; private static final Color enemyColor = new Color(128, 0, 0); private static final Color otherPlayerColor = new Color(82, 204, 82); @@ -97,8 +107,7 @@ protected void paintComponent(Graphics g) { } } - @HandleEvents - public void mapChange(EventContext context, MapChangeEvent event) { + public void mapChange(MapChangeEvent event) { setNewBackgroundImage(event.getMap()); resetPanAndZoom(); } @@ -124,10 +133,16 @@ private void triggerRefresh() { refresher.refreshNow(); } + private volatile List combatants = Collections.emptyList(); + + public void setCombatants(List combatants) { + this.combatants = new ArrayList<>(combatants); + SwingUtilities.invokeLater(this::refresh); + } private void refresh() { // log.info("Map refresh"); - List combatants = state.getCombatantsListCopy(); + List combatants = this.combatants; map = state.getMap(); combatants.stream() .filter(cbt -> { @@ -238,8 +253,27 @@ public void mouseClicked(MouseEvent e) { if (e.getButton() == MouseEvent.BUTTON2) { resetPanAndZoom(); } + else { + if (e.getComponent() instanceof PlayerDoohickey pd) { + selectionCallback.accept(pd.cbt); + } + else { + selectionCallback.accept(null); + } +// log.info("Clicked on {}", getComponentAt(e.getPoint())); + } } - +// +// @Override +// public Component getComponentAt(Point p) { +// for (Component component : getComponents()) { +// if (component.getBounds().contains(p)) { +// return component; +// } +// } +// return this; +// } +// @Override public void mousePressed(MouseEvent e) { // log.info("Pressed"); @@ -264,6 +298,42 @@ public void mouseExited(MouseEvent e) { } + public void setSelection(@Nullable XivCombatant selection) { + long newSelection; + if (selection == null) { + newSelection = -1; + } + else { + newSelection = selection.getId(); + } + if (newSelection != this.selection) { + log.info("New map selection: {} -> {}", + this.selection == -1 ? "none" : String.format("0x%X", this.selection), + newSelection == -1 ? "none" : String.format("0x%X", newSelection)); + this.selection = newSelection; + Component[] components = getComponents(); + removeAll(); + Arrays.stream(components).sorted(Comparator.comparing(comp -> { + if (comp instanceof PlayerDoohickey pd) { + if (pd.isSelected()) { + return 0; + } + else { + return 1; + } + } + else { + return -1; + } + })).forEach(this::add); + repaint(); + } + } + + public void setSelectionCallback(Consumer<@Nullable XivCombatant> selectionCallback) { + this.selectionCallback = selectionCallback; + } + private static String formatTooltip(XivCombatant cbt) { StringBuilder tt = new StringBuilder(); tt.append(cbt.getName()).append(" (").append(String.format("0x%X, %s)", cbt.getId(), cbt.getId())); @@ -280,10 +350,29 @@ private static String formatTooltip(XivCombatant cbt) { return tt.toString(); } + @Override + protected void paintChildren(Graphics g) { + super.paintChildren(g); + things.values().forEach(v -> { + if (v.isSelected()) { + v.repaint(); +// v.paintComponent(g); +// paintComponents(); +// v.paint(g); + } + }); + } + + // TODO: name.... private class PlayerDoohickey extends JPanel { + private static final Border selectionBorder = new LineBorder(Color.CYAN, 2); + private static final Color selectedBackground = new Color(192, 255, 255, 128); private final JLabel defaultLabel; + private final XivCombatant cbt; + // This red should never actually show up + private Color mainColor = new Color(255, 0, 0); private double x; private double y; private final JPanel inner; @@ -293,9 +382,18 @@ private class PlayerDoohickey extends JPanel { private final HpBar hpBar; private final JLabel nameLabel; private final JLabel idLabel; + private final long cbtId; + private double facing; public PlayerDoohickey(XivCombatant cbt) { - inner = new JPanel(); + this.cbt = cbt; + cbtId = cbt.getId(); + inner = new JPanel() { + @Override + public Color getBackground() { + return mainColor; + } + }; inner.setBorder(new LineBorder(Color.PINK, 2)); setLayout(null); inner.setLayout(new OverlapLayout()); @@ -313,7 +411,7 @@ public PlayerDoohickey(XivCombatant cbt) { setSize(outerSize, outerSize); int innerW = inner.getPreferredSize().width; int innerH = inner.getPreferredSize().height; - inner.setBounds(new Rectangle(center - (innerW / 2), center - (innerH / 2), innerW, innerH)); + inner.setBounds(new Rectangle(center - (innerW * 2), center - (innerH / 2), innerW, innerH)); this.castBar = new CastBarComponent() { @Override public void paint(Graphics g) { @@ -322,6 +420,11 @@ public void paint(Graphics g) { super.paint(g); } }; + + FacingAngleIndicator arrow = new FacingAngleIndicator(); + arrow.setBounds(center - 10, center - 10, 20, 20); + add(arrow); + castBar.setBounds(0, 81, 100, 19); add(castBar); this.hpBar = new HpBar(); @@ -367,6 +470,7 @@ public void update(XivCombatant cbt, @Nullable CastTracker castData) { if (pos != null) { this.x = pos.getX(); this.y = pos.getY(); + this.facing = pos.getHeading(); } if (cbt instanceof XivPlayerCharacter pc) { Job newJob = pc.getJob(); @@ -409,29 +513,35 @@ private void formatComponent(XivCombatant cbt) { if (cbt instanceof XivPlayerCharacter pc) { Job job = pc.getJob(); if (cbt.isThePlayer()) { - inner.setBorder(new LineBorder(localPcColor)); - inner.setBackground(localPcColor); + mainColor = localPcColor; +// inner.setBorder(new LineBorder(localPcColor)); +// inner.setBackground(localPcColor); } else if (state.getPartyList().contains(cbt)) { - inner.setBorder(new LineBorder(partyMemberColor)); - inner.setBackground(partyMemberColor); + mainColor = partyMemberColor; +// inner.setBorder(new LineBorder(partyMemberColor)); +// inner.setBackground(partyMemberColor); } else { - inner.setBorder(new LineBorder(otherPlayerColor)); - inner.setBackground(otherPlayerColor); + mainColor = otherPlayerColor; +// inner.setBorder(new LineBorder(otherPlayerColor)); +// inner.setBackground(otherPlayerColor); } icon = IconTextRenderer.getComponent(job, defaultLabel, true, false, true); - inner.setOpaque(true); +// inner.setOpaque(true); // TODO: this doesn't work because it hasn't been added to the container yet // MapPanel.this.setComponentZOrder(this, 0); } else { - inner.setBorder(new LineBorder(enemyColor)); - inner.setOpaque(false); + mainColor = enemyColor; +// inner.setBorder(new LineBorder(enemyColor)); +// inner.setOpaque(false); // TODO: find good icon icon = IconTextRenderer.getComponent(ActionLibrary.iconForId(2246), defaultLabel, true, false, true); // MapPanel.this.setComponentZOrder(this, 5); } + inner.setBorder(new LineBorder(mainColor)); + inner.setOpaque(true); inner.add(icon); validate(); } @@ -451,6 +561,76 @@ public int getY() { public Rectangle getBounds() { return new Rectangle(getX(), getY(), getWidth(), getHeight()); } + + private boolean isSelected() { + return this.cbtId == selection; + } + + @Override + protected void paintComponent(Graphics g) { + Rectangle bounds = getBounds(); + if (isSelected()) { + g.setColor(selectedBackground); + g.fillRect(0, 0, bounds.width, bounds.height); + } +// g.setColor(mainColor); +// int xCenter = bounds.width / 2; +// int yCenter = bounds.height / 2; +// int radius = bounds.width / 3; +// g.drawOval(xCenter - radius, yCenter - radius, radius * 2, radius * 2); +// super.paintComponent(g); + } + +// @Override +// public Color getBackground() { +// if (isSelected()) { +// return selectedBackground; +// } +// else { +// return normalBackground; +// } +// } + + @Override + public Border getBorder() { + if (isSelected()) { + return selectionBorder; + } + else { + return null; + } + } + + private class FacingAngleIndicator extends JComponent { + @Override + public void paintComponent(Graphics graph) { + Graphics2D g = (Graphics2D) graph; + AffineTransform origTrans = g.getTransform(); + AffineTransform newTrans = new AffineTransform(origTrans); + Rectangle bounds = getBounds(); + newTrans.translate(bounds.width / 2.0, bounds.height / 2.0); + newTrans.rotate(facing); + g.setTransform(newTrans); + g.setColor(mainColor); +// g.setColor(new Color(255, 0, 0)); + int sizeBasis = Math.min(bounds.width, bounds.height); + + Polygon poly = new Polygon( + new int[]{0, (int) (sizeBasis / -3.3), (int) (sizeBasis / 3.3)}, + new int[]{(int) (sizeBasis / 2.2), (int) (sizeBasis / -2.2), sizeBasis / -2}, + 3); + g.fillPolygon(poly); +// g.setColor(new Color(0, 255, 0)); +// g.fillRect(sizeBasis / -8, sizeBasis / -2, sizeBasis / 8, sizeBasis / 2); + g.setTransform(origTrans); + } + + @Override + public Border getBorder() { + return null; +// return new LineBorder(Color.BLACK, 1); + } + } } diff --git a/xivsupport/src/main/java/gg/xp/xivsupport/gui/map/MapTab.java b/xivsupport/src/main/java/gg/xp/xivsupport/gui/map/MapTab.java index b24ae62792f7..123bc02ca8a8 100644 --- a/xivsupport/src/main/java/gg/xp/xivsupport/gui/map/MapTab.java +++ b/xivsupport/src/main/java/gg/xp/xivsupport/gui/map/MapTab.java @@ -1,16 +1,147 @@ package gg.xp.xivsupport.gui.map; +import gg.xp.reevent.events.Event; +import gg.xp.reevent.events.EventContext; +import gg.xp.reevent.scan.HandleEvents; import gg.xp.reevent.scan.ScanMe; -import gg.xp.xivsupport.gui.TitleBorderFullsizePanel; +import gg.xp.reevent.util.Utils; +import gg.xp.xivsupport.events.actlines.events.MapChangeEvent; +import gg.xp.xivsupport.events.actlines.events.XivBuffsUpdatedEvent; +import gg.xp.xivsupport.events.actlines.events.XivStateRecalculatedEvent; +import gg.xp.xivsupport.events.state.XivState; +import gg.xp.xivsupport.events.state.combatstate.ActiveCastRepository; +import gg.xp.xivsupport.gui.overlay.RefreshLoop; +import gg.xp.xivsupport.gui.tables.StandardColumns; +import gg.xp.xivsupport.gui.tables.TableWithFilterAndDetails; +import gg.xp.xivsupport.gui.tables.filters.EventEntityFilter; +import gg.xp.xivsupport.gui.tables.filters.GroovyFilter; +import gg.xp.xivsupport.gui.tables.filters.NonCombatEntityFilter; +import gg.xp.xivsupport.models.XivCombatant; +import gg.xp.xivsupport.models.XivEntity; +import javax.swing.*; import java.awt.*; +import java.lang.reflect.Field; +import java.util.Collections; +import java.util.Comparator; +import java.util.Map; +import java.util.stream.Collectors; @ScanMe -public class MapTab extends TitleBorderFullsizePanel { - public MapTab(MapPanel panel) { - super("Map"); +public class MapTab extends JSplitPane { + + private final TableWithFilterAndDetails> table; + private final RefreshLoop mapRefresh; + private final MapPanel mapPanel; + private volatile boolean selectionRefreshPending; + + public MapTab(XivState state, StandardColumns columns, ActiveCastRepository acr) { +// super("Map"); + super(HORIZONTAL_SPLIT); + this.mapPanel = new MapPanel(state, acr); setPreferredSize(getMaximumSize()); - setLayout(new BorderLayout()); - add(panel); +// setLayout(new BorderLayout()); + setRightComponent(mapPanel); +// add(panel); + JPanel controls = new JPanel(new BorderLayout()); + + table = TableWithFilterAndDetails.builder("Combatants", + () -> state.getCombatantsListCopy().stream().sorted(Comparator.comparing(XivEntity::getId)).collect(Collectors.toList()), + combatant -> { + if (combatant == null) { + return Collections.emptyList(); + } + else { + return Utils.dumpAllFields(combatant) + .entrySet() + .stream() + .filter(e -> !"serialVersionUID".equals(e.getKey().getName())) + .collect(Collectors.toList()); + } + }) + .addMainColumn(StandardColumns.entityIdColumn) + .addMainColumn(StandardColumns.nameJobColumn) + .addMainColumn(columns.statusEffectsColumn()) +// .addMainColumn(StandardColumns.parentNameJobColumn) + .addMainColumn(StandardColumns.combatantTypeColumn) + .addMainColumn(columns.hpColumnWithUnresolved()) +// .addMainColumn(StandardColumns.mpColumn) +// .addMainColumn(StandardColumns.posColumn) + .addDetailsColumn(StandardColumns.fieldName) + .addDetailsColumn(StandardColumns.fieldValue) + .addDetailsColumn(StandardColumns.identity) + .addDetailsColumn(StandardColumns.fieldType) + .addDetailsColumn(StandardColumns.fieldDeclaredIn) + .setSelectionEquivalence((a, b) -> a.getId() == b.getId()) + .setDetailsSelectionEquivalence((a, b) -> a.getKey().equals(b.getKey())) + .addFilter(EventEntityFilter::selfFilter) + .addFilter(NonCombatEntityFilter::new) + .addFilter(GroovyFilter.forClass(XivCombatant.class, "it")) + .build(); + table.setBottomScroll(false); + mapRefresh = new RefreshLoop<>("MapTableRefresh", this, MapTab::updateMapPanel, u -> 100L); + + controls.add(table, BorderLayout.CENTER); + setLeftComponent(controls); + setDividerLocation(0.35); + setResizeWeight(0.25); + + table.getMainTable().getSelectionModel().addListSelectionListener(l -> { + if (l.getValueIsAdjusting()) { + return; + } + if (!selectionRefreshPending) { + selectionRefreshPending = true; + SwingUtilities.invokeLater(() -> { + mapPanel.setSelection(table.getCurrentSelection()); + selectionRefreshPending = false; + }); + } + }); + + //noinspection ConstantConditions + SwingUtilities.invokeLater(() -> table.getSplitPane().setDividerLocation(0.75)); + + mapPanel.setSelectionCallback(table::setAndScrollToSelection); + mapRefresh.start(); + } + + @Override + public void setVisible(boolean vis) { + if (vis) { + table.signalNewData(); + } + super.setVisible(vis); } + + private void refreshIfVisible() { + if (table.getMainTable().isShowing()) { + table.signalNewData(); + } + } + + + private void updateMapPanel() { + mapPanel.setCombatants(table.getMainModel().getData()); + } + + private void signalUpdate() { + refreshIfVisible(); + } + + @HandleEvents + public void stateRecalc(EventContext ctx, XivStateRecalculatedEvent event) { + signalUpdate(); + } + + @HandleEvents + public void buffRecalc(EventContext ctx, XivBuffsUpdatedEvent event) { + signalUpdate(); + } + + @HandleEvents + public void mapChange(EventContext context, MapChangeEvent event) { + mapPanel.mapChange(event); + } + } diff --git a/xivsupport/src/main/java/gg/xp/xivsupport/gui/tables/StandardColumns.java b/xivsupport/src/main/java/gg/xp/xivsupport/gui/tables/StandardColumns.java index 60be425e039b..a8a732530780 100644 --- a/xivsupport/src/main/java/gg/xp/xivsupport/gui/tables/StandardColumns.java +++ b/xivsupport/src/main/java/gg/xp/xivsupport/gui/tables/StandardColumns.java @@ -207,8 +207,8 @@ public Component getTableCellRendererComponent(JTable table, Object value, boole return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); } }); - c.setMinWidth(80); - c.setMaxWidth(80); + c.setMinWidth(50); + c.setMaxWidth(50); }); public static final CustomColumn combatantRawTypeColumn diff --git a/xivsupport/src/main/java/gg/xp/xivsupport/gui/tables/TableWithFilterAndDetails.java b/xivsupport/src/main/java/gg/xp/xivsupport/gui/tables/TableWithFilterAndDetails.java index 3604ceb0f372..2666c27079b2 100644 --- a/xivsupport/src/main/java/gg/xp/xivsupport/gui/tables/TableWithFilterAndDetails.java +++ b/xivsupport/src/main/java/gg/xp/xivsupport/gui/tables/TableWithFilterAndDetails.java @@ -37,6 +37,7 @@ public final class TableWithFilterAndDetails extends TitleBorderFullsizePa private final @Nullable JCheckBox stayAtBottom; private final JTable table; private final @Nullable AutoBottomScrollHelper scroller; + private @Nullable JSplitPane splitPane; private volatile X currentSelection; private List dataRaw = Collections.emptyList(); private List dataFiltered = Collections.emptyList(); @@ -177,7 +178,7 @@ private TableWithFilterAndDetails( } else { // Split pane - JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, true, scroller, detailsScroller); + splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, true, scroller, detailsScroller); add(splitPane, c); SwingUtilities.invokeLater(() -> { splitPane.setDividerLocation(0.7); @@ -232,6 +233,10 @@ public void setAndScrollToSelection(X item) { mainModel.scrollToSelectedValue(); } + public @Nullable JSplitPane getSplitPane() { + return this.splitPane; + } + private enum RefreshType { NONE, APPEND, diff --git a/xivsupport/src/main/java/gg/xp/xivsupport/gui/tables/filters/GroovyFilter.java b/xivsupport/src/main/java/gg/xp/xivsupport/gui/tables/filters/GroovyFilter.java index 37228648b2ac..d45f80a39e7c 100644 --- a/xivsupport/src/main/java/gg/xp/xivsupport/gui/tables/filters/GroovyFilter.java +++ b/xivsupport/src/main/java/gg/xp/xivsupport/gui/tables/filters/GroovyFilter.java @@ -29,7 +29,6 @@ public class GroovyFilter implements SplitVisualFilter { private static final Logger log = LoggerFactory.getLogger(GroovyFilter.class); private final GroovyShell shell; private final TextFieldWithValidation textBox; - private final String shortClassName; private final String longClassName; private final String varName; private @Nullable Predicate filterScript; @@ -39,16 +38,19 @@ public class GroovyFilter implements SplitVisualFilter { private final Class dataType; public static Function> forClass(Class dataType) { - return (filterUpdatedCallback) -> new GroovyFilter<>(filterUpdatedCallback, dataType); + return (filterUpdatedCallback) -> new GroovyFilter<>(filterUpdatedCallback, dataType, dataType.getSimpleName().toLowerCase(Locale.ROOT)); } - public GroovyFilter(Runnable filterUpdatedCallback, Class dataType) { + public static Function> forClass(Class dataType, String varName) { + return (filterUpdatedCallback) -> new GroovyFilter<>(filterUpdatedCallback, dataType, varName); + } + + public GroovyFilter(Runnable filterUpdatedCallback, Class dataType, String varName) { this.filterUpdatedCallback = filterUpdatedCallback; this.dataType = dataType; this.textBox = new TextFieldWithValidation<>(this::makeFilter, this::setFilter, ""); - this.shortClassName = dataType.getSimpleName(); this.longClassName = dataType.getCanonicalName(); - this.varName = shortClassName.toLowerCase(Locale.ROOT); + this.varName = varName; CompilerConfiguration compilerConfiguration = GroovyManager.getCompilerConfig(); // CompilerConfiguration compilerConfiguration = new CompilerConfiguration(); // ImportCustomizer importCustomizer = new ImportCustomizer(); diff --git a/xivsupport/src/test/java/gg/xp/xivsupport/gui/GuiWithTestData.java b/xivsupport/src/test/java/gg/xp/xivsupport/gui/GuiWithTestData.java index 43ccf19ab97d..de371cf13d22 100644 --- a/xivsupport/src/test/java/gg/xp/xivsupport/gui/GuiWithTestData.java +++ b/xivsupport/src/test/java/gg/xp/xivsupport/gui/GuiWithTestData.java @@ -122,10 +122,10 @@ public static void main(String[] args) { "MaxHP": 122700, "CurrentMP": 10000, "MaxMP": 10000, - "PosX": 114.926422, - "PosY": -83.86734, - "PosZ": 44.3433, - "Heading": -1.66136408 + "PosX": 100, + "PosY": 120, + "PosZ": 1.5, + "Heading": 0 }, { "CurrentWorldID": 65, @@ -144,10 +144,10 @@ public static void main(String[] args) { "MaxHP": 122700, "CurrentMP": 10000, "MaxMP": 10000, - "PosX": 114.926422, - "PosY": -83.86734, - "PosZ": 44.3433, - "Heading": -1.66136408 + "PosX": 120, + "PosY": 120, + "PosZ": 1.5, + "Heading": 1 }, { "CurrentWorldID": 65, @@ -166,10 +166,10 @@ public static void main(String[] args) { "MaxHP": 122700, "CurrentMP": 10000, "MaxMP": 10000, - "PosX": 114.926422, - "PosY": -83.86734, - "PosZ": 44.3433, - "Heading": -1.66136408 + "PosX": 120, + "PosY": 100, + "PosZ": 1.5, + "Heading": 2 }, { "CurrentWorldID": 65, @@ -188,10 +188,10 @@ public static void main(String[] args) { "MaxHP": 122700, "CurrentMP": 10000, "MaxMP": 10000, - "PosX": 114.926422, - "PosY": -83.86734, - "PosZ": 44.3433, - "Heading": -1.66136408 + "PosX": 120, + "PosY": 80, + "PosZ": 1.5, + "Heading": 3 }, { "CurrentWorldID": 65, @@ -210,10 +210,10 @@ public static void main(String[] args) { "MaxHP": 122700, "CurrentMP": 10000, "MaxMP": 10000, - "PosX": 114.926422, - "PosY": -83.86734, - "PosZ": 44.3433, - "Heading": -1.66136408 + "PosX": 100, + "PosY": 80, + "PosZ": 1.5, + "Heading": 4 }, { "CurrentWorldID": 65, @@ -232,10 +232,10 @@ public static void main(String[] args) { "MaxHP": 122700, "CurrentMP": 10000, "MaxMP": 10000, - "PosX": 114.926422, - "PosY": -83.86734, - "PosZ": 44.3433, - "Heading": -1.66136408 + "PosX": 80, + "PosY": 80, + "PosZ": 1.5, + "Heading": 5 }, { "CurrentWorldID": 65, @@ -254,10 +254,10 @@ public static void main(String[] args) { "MaxHP": 122700, "CurrentMP": 10000, "MaxMP": 10000, - "PosX": 114.926422, - "PosY": -83.86734, - "PosZ": 44.3433, - "Heading": -1.66136408 + "PosX": 80, + "PosY": 100, + "PosZ": 1.5, + "Heading": 6 }, { "CurrentWorldID": 65, @@ -276,10 +276,10 @@ public static void main(String[] args) { "MaxHP": 122700, "CurrentMP": 10000, "MaxMP": 10000, - "PosX": 114.926422, - "PosY": -83.86734, - "PosZ": 44.3433, - "Heading": -1.66136408 + "PosX": 80, + "PosY": 120, + "PosZ": 1.5, + "Heading": 7 } ], "rseq": 0