From 95fba3d0cbfd9a6df93ad83c2ab1fa59d301cfd0 Mon Sep 17 00:00:00 2001 From: Grimm Date: Tue, 2 Aug 2022 17:59:30 +0200 Subject: [PATCH] WaveFunctionCollapse editor --- .../src/main/java/forge/adventure/Main.java | 6 +- .../forge/adventure/editor/BiomeEdit.java | 4 +- .../BiomeStructureDataMappingEditor.java | 169 ++++++++++++++++++ .../adventure/editor/BiomeStructureEdit.java | 67 +++++-- .../adventure/editor/BiomeTerrainEdit.java | 5 +- .../adventure/editor/EditorMainWindow.java | 3 +- .../adventure/editor/StructureEditor.java | 62 +++++-- .../adventure/editor/SwingAtlasPreview.java | 2 +- .../forge/adventure/editor/WorldEditor.java | 34 ++-- forge-gui-mobile/src/forge/Forge.java | 2 + .../adventure/data/BiomeStructureData.java | 46 ++++- .../src/forge/adventure/data/WorldData.java | 15 +- .../forge/adventure/world/BiomeStructure.java | 65 ++++--- .../forge/adventure/world/BiomeTexture.java | 11 +- .../src/forge/adventure/world/World.java | 11 +- forge-gui/pom.xml | 6 + .../res/adventure/Shandalar/world/green.json | 12 +- .../Shandalar/world/tilesets/forest.png | Bin 16786 -> 0 bytes .../Shandalar/world/tilesets/forestSource.png | Bin 0 -> 8939 bytes .../{forest.atlas => structures.atlas} | 11 +- .../Shandalar/world/tilesets/structures.png | Bin 0 -> 10426 bytes 21 files changed, 417 insertions(+), 114 deletions(-) create mode 100644 forge-adventure/src/main/java/forge/adventure/editor/BiomeStructureDataMappingEditor.java delete mode 100644 forge-gui/res/adventure/Shandalar/world/tilesets/forest.png create mode 100644 forge-gui/res/adventure/Shandalar/world/tilesets/forestSource.png rename forge-gui/res/adventure/Shandalar/world/tilesets/{forest.atlas => structures.atlas} (56%) create mode 100644 forge-gui/res/adventure/Shandalar/world/tilesets/structures.png diff --git a/forge-adventure/src/main/java/forge/adventure/Main.java b/forge-adventure/src/main/java/forge/adventure/Main.java index 54809bbe61d..f5940d643a2 100644 --- a/forge-adventure/src/main/java/forge/adventure/Main.java +++ b/forge-adventure/src/main/java/forge/adventure/Main.java @@ -2,11 +2,7 @@ package forge.adventure; import com.badlogic.gdx.ApplicationListener; import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application; -import com.badlogic.gdx.backends.lwjgl3.Lwjgl3ApplicationConfiguration; -import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Clipboard; -import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Window; -import com.badlogic.gdx.backends.lwjgl3.Lwjgl3WindowListener; +import com.badlogic.gdx.backends.lwjgl3.*; import com.badlogic.gdx.graphics.glutils.HdpiMode; import forge.Forge; import forge.adventure.util.Config; diff --git a/forge-adventure/src/main/java/forge/adventure/editor/BiomeEdit.java b/forge-adventure/src/main/java/forge/adventure/editor/BiomeEdit.java index 569de3a09dc..cbf244b4a7e 100644 --- a/forge-adventure/src/main/java/forge/adventure/editor/BiomeEdit.java +++ b/forge-adventure/src/main/java/forge/adventure/editor/BiomeEdit.java @@ -46,8 +46,8 @@ public class BiomeEdit extends JComponent { center.add(new JLabel("enemies:")); center.add(enemies); center.add(new JLabel("pointsOfInterest:")); center.add(pointsOfInterest); center.add(new JLabel("color:")); center.add(color); - center.add(new JLabel("terrain/structures:")); - BorderLayout layout=new BorderLayout(); + center.add(new JLabel("terrain/structures:"));center.add(new JLabel("")); + BoxLayout layout=new BoxLayout(this, BoxLayout.Y_AXIS); setLayout(layout); add(center,BorderLayout.NORTH); add(terrain,BorderLayout.CENTER); diff --git a/forge-adventure/src/main/java/forge/adventure/editor/BiomeStructureDataMappingEditor.java b/forge-adventure/src/main/java/forge/adventure/editor/BiomeStructureDataMappingEditor.java new file mode 100644 index 00000000000..4b92f6d91cb --- /dev/null +++ b/forge-adventure/src/main/java/forge/adventure/editor/BiomeStructureDataMappingEditor.java @@ -0,0 +1,169 @@ +package forge.adventure.editor; + +import forge.adventure.data.BiomeStructureData; +import forge.adventure.util.Config; +import forge.adventure.world.BiomeStructure; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionListener; + +public class BiomeStructureDataMappingEditor extends JComponent { + DefaultListModel model = new DefaultListModel<>(); + JList list = new JList<>(model); + JToolBar toolBar = new JToolBar("toolbar"); + BiomeStructureDataMappingEdit edit=new BiomeStructureDataMappingEdit(); + private BiomeStructureData data; + + public void setCurrent(BiomeStructureData data) { + this.data=data; + model.clear(); + for(int i=0;data.mappingInfo!=null&&i BiomeStructureDataMappingEditor.this.updateEdit()); + addButton("add", e -> BiomeStructureDataMappingEditor.this.add()); + addButton("remove", e -> BiomeStructureDataMappingEditor.this.remove()); + addButton("copy", e -> BiomeStructureDataMappingEditor.this.copy()); + BorderLayout layout=new BorderLayout(); + setLayout(layout); + add(new JScrollPane(list), BorderLayout.WEST); + add(toolBar, BorderLayout.NORTH); + add(edit,BorderLayout.CENTER); + } + private void copy() { + + int selected=list.getSelectedIndex(); + if(selected<0) + return; + BiomeStructureData.BiomeStructureDataMapping data=new BiomeStructureData.BiomeStructureDataMapping(model.get(selected)); + model.add(model.size(),data); + } + private void updateEdit() { + + int selected=list.getSelectedIndex(); + if(selected<0) + return; + edit.setCurrent(model.get(selected)); + } + + void add() + { + BiomeStructureData.BiomeStructureDataMapping data=new BiomeStructureData.BiomeStructureDataMapping(); + data.name="Structure "+model.getSize(); + model.add(model.size(),data); + } + void remove() + { + int selected=list.getSelectedIndex(); + if(selected<0) + return; + model.remove(selected); + } + + private class BiomeStructureDataMappingEdit extends JComponent{ + BiomeStructureData.BiomeStructureDataMapping currentData; + + + public JTextField name=new JTextField(); + public JTextField color=new JTextField(); + public JCheckBox collision=new JCheckBox(); + private boolean updating=false; + + public BiomeStructureDataMappingEdit() + { + + setLayout(new GridLayout(3,2)); + + add(new JLabel("name:")); add(name); + add(new JLabel("color:")); add(color); + add(new JLabel("collision:")); add(collision); + + name.getDocument().addDocumentListener(new DocumentChangeListener(() -> BiomeStructureDataMappingEdit.this.update())); + color.getDocument().addDocumentListener(new DocumentChangeListener(() -> BiomeStructureDataMappingEdit.this.update())); + collision.addChangeListener(e -> BiomeStructureDataMappingEdit.this.update()); + refresh(); + } + + private void update() { + if(currentData==null||updating) + return; + currentData.name = name.getText(); + currentData.color = color.getText(); + currentData.collision = collision.isSelected(); + } + + public void setCurrent(BiomeStructureData.BiomeStructureDataMapping data) + { + currentData=data; + refresh(); + } + + private void refresh() { + setEnabled(currentData!=null); + if(currentData==null) + { + return; + } + updating=true; + name.setText(currentData.name); + color.setText(currentData.color); + collision.setSelected(currentData.collision); + updating=false; + } + } +} diff --git a/forge-adventure/src/main/java/forge/adventure/editor/BiomeStructureEdit.java b/forge-adventure/src/main/java/forge/adventure/editor/BiomeStructureEdit.java index 2fd8bcfb592..b6dc2875dfb 100644 --- a/forge-adventure/src/main/java/forge/adventure/editor/BiomeStructureEdit.java +++ b/forge-adventure/src/main/java/forge/adventure/editor/BiomeStructureEdit.java @@ -7,43 +7,61 @@ import javax.swing.*; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import java.awt.*; +import java.awt.image.BufferedImage; public class BiomeStructureEdit extends JComponent { - SwingAtlasPreview preview=new SwingAtlasPreview(128); private boolean updating=false; BiomeStructureData currentData; BiomeData currentBiomeData; public JTextField structureAtlasPath=new JTextField(); public FloatSpinner x= new FloatSpinner(); public FloatSpinner y= new FloatSpinner(); - public FloatSpinner size= new FloatSpinner(); + public FloatSpinner width= new FloatSpinner(); + public FloatSpinner height= new FloatSpinner(); public JCheckBox randomPosition=new JCheckBox(); - public JCheckBox collision=new JCheckBox(); - + public IntSpinner N= new IntSpinner(); + public JTextField sourcePath= new JTextField(); + public JCheckBox periodicInput= new JCheckBox(); + public IntSpinner ground= new IntSpinner(); + public IntSpinner symmetry= new IntSpinner(); + public JCheckBox periodicOutput= new JCheckBox(); + public BiomeStructureDataMappingEditor data=new BiomeStructureDataMappingEditor(); public BiomeStructureEdit() { JComponent center=new JComponent() { }; - center.setLayout(new GridLayout(6,2)); + center.setLayout(new GridLayout(11,2)); center.add(new JLabel("structureAtlasPath:")); center.add(structureAtlasPath); center.add(new JLabel("x:")); center.add(x); center.add(new JLabel("y:")); center.add(y); - center.add(new JLabel("size:")); center.add(size); - center.add(new JLabel("randomPosition:")); center.add(randomPosition); - center.add(new JLabel("collision:")); center.add(collision); + center.add(new JLabel("width:")); center.add(width); + center.add(new JLabel("height:")); center.add(height); + center.add(new JLabel("N:")); center.add(N); + center.add(new JLabel("sourcePath:")); center.add(sourcePath); + center.add(new JLabel("periodicInput:")); center.add(periodicInput); + center.add(new JLabel("ground:")); center.add(ground); + center.add(new JLabel("symmetry:")); center.add(symmetry); + center.add(new JLabel("periodicOutput:")); center.add(periodicOutput); BorderLayout layout=new BorderLayout(); setLayout(layout); - add(preview,BorderLayout.LINE_START); add(center,BorderLayout.CENTER); + add(data,BorderLayout.SOUTH); structureAtlasPath.getDocument().addDocumentListener(new DocumentChangeListener(() -> BiomeStructureEdit.this.updateStructure())); x.addChangeListener(e -> BiomeStructureEdit.this.updateStructure()); y.addChangeListener(e -> BiomeStructureEdit.this.updateStructure()); - size.addChangeListener(e -> BiomeStructureEdit.this.updateStructure()); + width.addChangeListener(e -> BiomeStructureEdit.this.updateStructure()); + height.addChangeListener(e -> BiomeStructureEdit.this.updateStructure()); randomPosition.addChangeListener(e -> BiomeStructureEdit.this.updateStructure()); - collision.addChangeListener(e -> BiomeStructureEdit.this.updateStructure()); + + N.addChangeListener(e -> BiomeStructureEdit.this.updateStructure()); + sourcePath.getDocument().addDocumentListener(new DocumentChangeListener(() -> BiomeStructureEdit.this.updateStructure())); + periodicInput.addChangeListener(e -> BiomeStructureEdit.this.updateStructure()); + ground.addChangeListener(e -> BiomeStructureEdit.this.updateStructure()); + symmetry.addChangeListener(e -> BiomeStructureEdit.this.updateStructure()); + periodicOutput.addChangeListener(e -> BiomeStructureEdit.this.updateStructure()); refresh(); } private void refresh() { @@ -56,10 +74,17 @@ public class BiomeStructureEdit extends JComponent { structureAtlasPath.setText(currentData.structureAtlasPath); x.setValue(currentData.x); y.setValue(currentData.y); - size.setValue(currentData.size); + width.setValue(currentData.width); + height.setValue(currentData.height); randomPosition.setSelected(currentData.randomPosition); - collision.setSelected(currentData.collision); - preview.setSpritePath(currentBiomeData.tilesetAtlas,currentData.structureAtlasPath); + N.setValue(currentData.N); + sourcePath.setText(currentData.sourcePath); + periodicInput.setSelected(currentData.periodicInput); + ground.setValue(currentData.ground); + symmetry.setValue(currentData.symmetry); + periodicOutput.setSelected(currentData.periodicOutput); + + data.setCurrent(currentData); updating=false; } public void updateStructure() @@ -71,10 +96,17 @@ public class BiomeStructureEdit extends JComponent { currentData.x= x.floatValue(); currentData.y= y.floatValue(); - currentData.size= size.floatValue(); + currentData.width= width.floatValue(); + currentData.height= height.floatValue(); currentData.randomPosition=randomPosition.isSelected(); - currentData.collision=collision.isSelected(); - preview.setSpritePath(currentBiomeData.tilesetAtlas,currentData.structureAtlasPath); + currentData.mappingInfo= data.getCurrent(); + + currentData.N= N.intValue(); + currentData.sourcePath= sourcePath.getText(); + currentData.periodicInput= periodicInput.isSelected(); + currentData.ground= ground.intValue(); + currentData.symmetry= symmetry.intValue(); + currentData.periodicOutput= periodicOutput.isSelected(); emitChanged(); } public void setCurrentStructure(BiomeStructureData biomeTerrainData, BiomeData data) { @@ -95,4 +127,5 @@ public class BiomeStructureEdit extends JComponent { } } } + } diff --git a/forge-adventure/src/main/java/forge/adventure/editor/BiomeTerrainEdit.java b/forge-adventure/src/main/java/forge/adventure/editor/BiomeTerrainEdit.java index 8986a2e6701..05a63ee58b7 100644 --- a/forge-adventure/src/main/java/forge/adventure/editor/BiomeTerrainEdit.java +++ b/forge-adventure/src/main/java/forge/adventure/editor/BiomeTerrainEdit.java @@ -29,7 +29,7 @@ public class BiomeTerrainEdit extends JComponent { center.add(new JLabel("resolution:")); center.add(resolution); BorderLayout layout=new BorderLayout(); setLayout(layout); - add(preview,BorderLayout.LINE_START); + add(preview,BorderLayout.WEST); add(center,BorderLayout.CENTER); spriteName.getDocument().addDocumentListener(new DocumentChangeListener(() -> BiomeTerrainEdit.this.updateTerrain())); @@ -52,7 +52,8 @@ public class BiomeTerrainEdit extends JComponent { min.setValue(currentData.min); max.setValue(currentData.max); resolution.setValue(currentData.resolution); - preview.setSpritePath(currentBiomeData.tilesetAtlas,currentData.spriteName); + if(currentBiomeData!=null&¤tData!= null) + preview.setSpritePath(currentBiomeData.tilesetAtlas,currentData.spriteName); updating=false; } public void updateTerrain() diff --git a/forge-adventure/src/main/java/forge/adventure/editor/EditorMainWindow.java b/forge-adventure/src/main/java/forge/adventure/editor/EditorMainWindow.java index fdfe775d914..4c4131f6345 100644 --- a/forge-adventure/src/main/java/forge/adventure/editor/EditorMainWindow.java +++ b/forge-adventure/src/main/java/forge/adventure/editor/EditorMainWindow.java @@ -7,6 +7,7 @@ import java.awt.*; * Editor class to edit configuration, maybe moved or removed */ public class EditorMainWindow extends JFrame { + public final static WorldEditor worldEditor = new WorldEditor(); JTabbedPane tabs =new JTabbedPane(); public EditorMainWindow() @@ -14,7 +15,7 @@ public class EditorMainWindow extends JFrame { BorderLayout layout=new BorderLayout(); setLayout(layout); add(tabs); - tabs.addTab("World",new WorldEditor()); + tabs.addTab("World",worldEditor); tabs.addTab("POI",new PointOfInterestEditor()); tabs.addTab("Items",new ItemsEditor()); tabs.addTab("Enemies",new EnemyEditor()); diff --git a/forge-adventure/src/main/java/forge/adventure/editor/StructureEditor.java b/forge-adventure/src/main/java/forge/adventure/editor/StructureEditor.java index 1b301ebebd7..367a7d35778 100644 --- a/forge-adventure/src/main/java/forge/adventure/editor/StructureEditor.java +++ b/forge-adventure/src/main/java/forge/adventure/editor/StructureEditor.java @@ -2,13 +2,18 @@ package forge.adventure.editor; import forge.adventure.data.BiomeData; import forge.adventure.data.BiomeStructureData; -import forge.adventure.data.BiomeTerrainData; +import forge.adventure.data.WorldData; +import forge.adventure.util.Config; +import forge.adventure.world.BiomeStructure; import javax.swing.*; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import java.awt.*; import java.awt.event.ActionListener; +import java.awt.geom.AffineTransform; +import java.awt.image.AffineTransformOp; +import java.awt.image.BufferedImage; /** * Editor class to edit configuration, maybe moved or removed @@ -27,14 +32,11 @@ public class StructureEditor extends JComponent{ JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); - if(!(value instanceof BiomeTerrainData)) + if(!(value instanceof BiomeStructureData)) return label; - BiomeTerrainData structureData=(BiomeTerrainData) value; - StringBuilder builder=new StringBuilder(); - builder.append("Structure"); - builder.append(" "); - builder.append(structureData.spriteName); - label.setText(builder.toString()); + BiomeStructureData structureData=(BiomeStructureData) value; + label.setText("Structure"); + label.setIcon(new ImageIcon(Config.instance().getFilePath(structureData.sourcePath))); return label; } } @@ -54,10 +56,11 @@ public class StructureEditor extends JComponent{ addButton("add", e -> StructureEditor.this.addStructure()); addButton("remove", e -> StructureEditor.this.remove()); addButton("copy", e -> StructureEditor.this.copy()); + addButton("test", e -> StructureEditor.this.test()); BorderLayout layout=new BorderLayout(); setLayout(layout); - add(list, BorderLayout.LINE_START); - add(toolBar, BorderLayout.PAGE_START); + add(list, BorderLayout.WEST); + add(toolBar, BorderLayout.NORTH); add(edit,BorderLayout.CENTER); @@ -77,6 +80,45 @@ public class StructureEditor extends JComponent{ } } } + private void test() { + if (list.isSelectionEmpty()) + return; + BiomeStructureData data = model.get(list.getSelectedIndex()); + + try { + + BiomeStructure struct = new BiomeStructure(data, System.currentTimeMillis(), + (int) (currentData.width * EditorMainWindow.worldEditor.width.intValue() * data.width), + (int) (currentData.width * EditorMainWindow.worldEditor.height.intValue() * data.height)); + struct.initialize(); + JLabel label = new JLabel(); + BufferedImage image = struct.image; + if (image.getWidth() < 640 | image.getHeight() < 640) { + if (image.getHeight() > image.getWidth()) { + BufferedImage nimage = new BufferedImage(640, 640 * (image.getWidth() / image.getHeight()), BufferedImage.TYPE_INT_ARGB); + AffineTransform at = new AffineTransform(); + at.scale(640 / image.getHeight(), 640 / image.getHeight()); + AffineTransformOp scaleOp = new AffineTransformOp(at, AffineTransformOp.TYPE_NEAREST_NEIGHBOR); + image = scaleOp.filter(image, nimage); + } else { + BufferedImage nimage = new BufferedImage(640 * (image.getHeight() / image.getWidth()), 640, BufferedImage.TYPE_INT_ARGB); + AffineTransform at = new AffineTransform(); + at.scale(640 / image.getWidth(), 640 / image.getWidth()); + AffineTransformOp scaleOp = new AffineTransformOp(at, AffineTransformOp.TYPE_NEAREST_NEIGHBOR); + image = scaleOp.filter(image, nimage); + } + + } + label.setIcon(new ImageIcon(image)); + label.setSize(640, 640); + JOptionPane.showMessageDialog(this, label); + } + catch (Exception e) + { + JOptionPane.showMessageDialog(this, "WaveFunctionCollapse was not successful"); + } + + } private void copy() { int selected=list.getSelectedIndex(); diff --git a/forge-adventure/src/main/java/forge/adventure/editor/SwingAtlasPreview.java b/forge-adventure/src/main/java/forge/adventure/editor/SwingAtlasPreview.java index 63d08c62f24..4a01fd30603 100644 --- a/forge-adventure/src/main/java/forge/adventure/editor/SwingAtlasPreview.java +++ b/forge-adventure/src/main/java/forge/adventure/editor/SwingAtlasPreview.java @@ -4,6 +4,7 @@ import forge.adventure.util.Config; import org.apache.commons.lang3.tuple.Pair; import javax.swing.*; +import java.awt.*; import java.awt.event.ActionEvent; import java.util.ArrayList; import java.util.List; @@ -40,7 +41,6 @@ public class SwingAtlasPreview extends Box { setSpritePath(sprite,null); } public void setSpritePath(String sprite,String name) { - if(this.sprite==null||name==null||sprite==null||(this.sprite.equals(sprite)&&(spriteName==null&&spriteName.equals(name)))) return; removeAll(); diff --git a/forge-adventure/src/main/java/forge/adventure/editor/WorldEditor.java b/forge-adventure/src/main/java/forge/adventure/editor/WorldEditor.java index 8bbde3bb51f..61f45ec0fa1 100644 --- a/forge-adventure/src/main/java/forge/adventure/editor/WorldEditor.java +++ b/forge-adventure/src/main/java/forge/adventure/editor/WorldEditor.java @@ -16,6 +16,7 @@ import java.awt.*; import java.io.File; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; public class WorldEditor extends JComponent { @@ -23,15 +24,15 @@ public class WorldEditor extends JComponent { WorldData currentData; - JSpinner width= new JSpinner(new SpinnerNumberModel(0, 0, 100000, 1)); - JSpinner height= new JSpinner(new SpinnerNumberModel(0, 0, 100000, 1)); - JSpinner playerStartPosX= new JSpinner(new SpinnerNumberModel(0, 0, 1, .1)); - JSpinner playerStartPosY= new JSpinner(new SpinnerNumberModel(0, 0, 1, .1)); - JSpinner noiseZoomBiome= new JSpinner(new SpinnerNumberModel(0, 0, 1000f, 1f)); - JSpinner tileSize= new JSpinner(new SpinnerNumberModel(0, 0, 100000, 1)); + IntSpinner width= new IntSpinner( 0, 100000, 1); + IntSpinner height= new IntSpinner( 0, 100000, 1); + FloatSpinner playerStartPosX= new FloatSpinner( 0, 1, .1f); + FloatSpinner playerStartPosY= new FloatSpinner(0, 1, .1f); + FloatSpinner noiseZoomBiome= new FloatSpinner( 0, 1000f, 1f); + IntSpinner tileSize= new IntSpinner( 0, 100000, 1); JTextField biomesSprites = new JTextField(); - JSpinner maxRoadDistance = new JSpinner(new SpinnerNumberModel(0, 0, 100000f, 1f)); + FloatSpinner maxRoadDistance = new FloatSpinner( 0, 100000f, 1f); TextListEdit biomesNames = new TextListEdit(); DefaultListModel model = new DefaultListModel<>(); @@ -92,7 +93,7 @@ public class WorldEditor extends JComponent { setLayout(layout); add(tabs); JPanel worldPanel=new JPanel(); - JPanel biomeData=new JPanel(); + JSplitPane biomeData=new JSplitPane(); tabs.addTab("BiomeData", biomeData); tabs.addTab("WorldData", worldPanel); @@ -116,8 +117,8 @@ public class WorldEditor extends JComponent { worldPanel.add(new Box.Filler(new Dimension(0,0),new Dimension(0,Integer.MAX_VALUE),new Dimension(0,Integer.MAX_VALUE))); - biomeData.setLayout(new GridLayout(1,2)) ; - biomeData.add(list); biomeData.add(edit); + JScrollPane pane = new JScrollPane(edit); + biomeData.setLeftComponent(list); biomeData.setRightComponent(pane); load(); @@ -166,6 +167,16 @@ public class WorldEditor extends JComponent { void save() { + currentData.width=width.intValue(); + currentData.height=height.intValue(); + currentData.playerStartPosX=playerStartPosX.floatValue(); + currentData.playerStartPosY=playerStartPosY.floatValue(); + currentData.noiseZoomBiome=noiseZoomBiome.floatValue(); + currentData.tileSize=tileSize.intValue(); + currentData.biomesSprites=biomesSprites.getText(); + currentData.maxRoadDistance=maxRoadDistance.floatValue(); + currentData.biomesNames= Arrays.asList(biomesNames.getList()); + Json json = new Json(JsonWriter.OutputType.json); FileHandle handle = Config.instance().getFile(Paths.WORLD); handle.writeString(json.prettyPrint(json.toJson(currentData,Array.class, WorldData.class)),false); @@ -173,6 +184,9 @@ public class WorldEditor extends JComponent { } void load() { + + + model.clear(); Json json = new Json(); FileHandle handle = Config.instance().getFile(Paths.WORLD); diff --git a/forge-gui-mobile/src/forge/Forge.java b/forge-gui-mobile/src/forge/Forge.java index 0cc6f9dabbc..830775aa7ed 100644 --- a/forge-gui-mobile/src/forge/Forge.java +++ b/forge-gui-mobile/src/forge/Forge.java @@ -888,6 +888,8 @@ public class Forge implements ApplicationListener { //check if sentry is enabled, if not it will call the gui interface but here we end the graphics so we only send it via sentry.. if (BugReporter.isSentryEnabled()) BugReporter.reportException(ex); + else + ex.printStackTrace(); } if (showFPS) frameRate.render(); diff --git a/forge-gui-mobile/src/forge/adventure/data/BiomeStructureData.java b/forge-gui-mobile/src/forge/adventure/data/BiomeStructureData.java index 50df63f6157..a91096d98b2 100644 --- a/forge-gui-mobile/src/forge/adventure/data/BiomeStructureData.java +++ b/forge-gui-mobile/src/forge/adventure/data/BiomeStructureData.java @@ -1,34 +1,62 @@ package forge.adventure.data; +import java.awt.*; import java.awt.image.BufferedImage; public class BiomeStructureData { + + static public class BiomeStructureDataMapping + { + public int getColor() { + return 0xff000000 |(Integer.parseInt(color,16)); + } + public String name; + public String color; + public boolean collision; + + public BiomeStructureDataMapping() { + + } + public BiomeStructureDataMapping(BiomeStructureDataMapping biomeStructureDataMapping) { + this.name=biomeStructureDataMapping.name; + this.color=biomeStructureDataMapping.color; + this.collision=biomeStructureDataMapping.collision; + } + } public int N = 3; public float x; public float y; - public float size; public boolean randomPosition; - public boolean collision; public String structureAtlasPath; - public boolean periodicInput; + public String sourcePath; + public boolean periodicInput=true; public float height; public float width; public int ground; - public int symmetry; - public boolean periodicOutput; + public int symmetry=2; + public boolean periodicOutput=true; + public BiomeStructureDataMapping[] mappingInfo; - public BiomeStructureData( ) - { + public BiomeStructureData( ) { } public BiomeStructureData(BiomeStructureData biomeStructureData) { this.structureAtlasPath=biomeStructureData.structureAtlasPath; + this.sourcePath=biomeStructureData.sourcePath; this.x=biomeStructureData.x; this.y=biomeStructureData.y; - this.size=biomeStructureData.size; + this.width=biomeStructureData.width; + this.height=biomeStructureData.height; this.randomPosition=biomeStructureData.randomPosition; - this.collision=biomeStructureData.collision; + if(biomeStructureData.mappingInfo!=null) + { + this.mappingInfo=new BiomeStructureDataMapping[ biomeStructureData.mappingInfo.length]; + for(int i=0;i GetBiomes() { if (biomes == null) { - biomes = new ArrayList(); - Json json = new Json(); - for (String name : biomesNames) { - biomes.add(json.fromJson(BiomeData.class, Config.instance().getFile(name))); + try + { + biomes = new ArrayList(); + Json json = new Json(); + for (String name : biomesNames) { + biomes.add(json.fromJson(BiomeData.class, Config.instance().getFile(name))); + } + } + catch (SerializationException ex) { + ex.printStackTrace(); } } return biomes; diff --git a/forge-gui-mobile/src/forge/adventure/world/BiomeStructure.java b/forge-gui-mobile/src/forge/adventure/world/BiomeStructure.java index e991e52f183..7c6736e88f1 100644 --- a/forge-gui-mobile/src/forge/adventure/world/BiomeStructure.java +++ b/forge-gui-mobile/src/forge/adventure/world/BiomeStructure.java @@ -20,6 +20,8 @@ public class BiomeStructure { private int dataMap[][]; boolean init=false; private TextureAtlas structureAtlas; + public BufferedImage image; + public BiomeStructure(BiomeStructureData data,long seed,int width,int height) { this.data=data; @@ -35,59 +37,42 @@ public class BiomeStructure { return structureAtlas; } public int structureObjectCount() { - int count=0; - for(TextureAtlas.AtlasRegion region:atlas ().getRegions()) - { - if(region.name.startsWith("structure")) - { - count++; - } - } - return count; + return data.mappingInfo.length; } public int objectID(int x, int y) { if(!init) { - init=true; initialize(); } - if(x>biomeWidth*data.width) - return -1; - if(y>biomeHeight*data.height) - return -1; - if(x=dataMap.length||x<0||y<0||y>=dataMap[0].length) return -1; return dataMap[x][y]; } - private void initialize() { + public void initialize() { + init=true; OverlappingModel model= new OverlappingModel(sourceImage(),data.N, (int) (data.width* biomeWidth), (int) (data.height*biomeHeight),data.periodicInput,data.periodicOutput,data.symmetry,data.ground); HashMap colorIdMap=new HashMap<>(); - int counter=0; - for(TextureAtlas.AtlasRegion region:atlas ().getRegions()) + for(int i=0;i=0) + { + pix.setColor(data.mappingInfo[structureIndex].getColor()); + pix.drawPixel(x, y); terrainMap[x][y]=terrainCounter+structureIndex; + } terrainCounter+=structure.structureObjectCount(); } @@ -395,6 +401,9 @@ public class World implements Disposable, SaveFileContent { foundSolution=true; x=x+xi*data.tileSize; y=y+yi*data.tileSize; + + + } } } diff --git a/forge-gui/pom.xml b/forge-gui/pom.xml index b81def5a7c3..43f21d8d837 100644 --- a/forge-gui/pom.xml +++ b/forge-gui/pom.xml @@ -67,6 +67,12 @@ 5.2.2 compile + + com.github.jetopto1.cling + cling-core + 1.0.0 + compile + diff --git a/forge-gui/res/adventure/Shandalar/world/green.json b/forge-gui/res/adventure/Shandalar/world/green.json index def5769876c..8cda1b185dd 100644 --- a/forge-gui/res/adventure/Shandalar/world/green.json +++ b/forge-gui/res/adventure/Shandalar/world/green.json @@ -21,10 +21,18 @@ ], "structures":[ { - "structureAtlasPath":"world/tilesets/forest.atlas", + "sourcePath" : "world/tilesets/forestSource.png", + "structureAtlasPath":"world/tilesets/structures.atlas", + "mappingInfo":[ + { + "name":"Forest", + "color":"007000" + } + ], "x": 0.5, "y": 0.5, - "size": 0.3 + "width": 0.3 , + "height": 0.3 } ], "width": 0.7, diff --git a/forge-gui/res/adventure/Shandalar/world/tilesets/forest.png b/forge-gui/res/adventure/Shandalar/world/tilesets/forest.png deleted file mode 100644 index cf8dd71e0c9d2cb2327c33398a99d087c459cf34..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16786 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 diff --git a/forge-gui/res/adventure/Shandalar/world/tilesets/forestSource.png b/forge-gui/res/adventure/Shandalar/world/tilesets/forestSource.png new file mode 100644 index 0000000000000000000000000000000000000000..bbae35add1f51c50a3a2bf1413d61ffe9d9fbf09 GIT binary patch literal 8939 zcmeHLc|6o>+qXrQEFt@8vPWZ9#!Pm?*q2fwHD-RpWEjn2Y$+s^v?6=UW`^B;BZ445uq|C7B-hp@X}8@xZsG%Ug&eTJ0Fth38#U{7Gr`Y$yZa!-2&RX?3N#Q$sck;`5K!42t$ zt-tCr29r!n*x%Bf%cdC;@>l+4*;RYwW=EgIs z*_#XM=7(j1G8fK>#y(_~hZ`6`8%{4)j2j&OCGg}V8+C3mjQ%ZfU&&Gs_F)LEECd$e zTQY(W@0{N@rjQ=lP!{Rm@x9`H$w#}iq|XOyH5$|7nVNf~Yx8LaqVFbhcK0uQ8J91} zv_Tw~6lfJCpJ9|)@k4FO(L2-}v-`6w#S^R+Rf{AqAOGq;ALO_FbZY(7ftfxTy~yw9 z!#pNgQ}+8ag)48|PSPl4rzO5?=hIxDPe`u#(6_^6KJ1NpI!A+tVVLXl^)fbU_Y>BA z@#!;-d)q*;?!#C569)sICBtTgjA8-MQY^ccJ3&ve6e>tJvzXAa-MZ1T~J9# z=1}fg3mH~rm)ghVL;1;G$*ij9>AaFrN@$<1V%xbXLG`km-R&i%w_c3N75Y9uRFKYo z&bslc-?ieRICbgq_``~!%+j-PHhoj|Xo9v@(v)nIT)J|HjErE{`A?oQ9FK0(19ojD zUcHa)c>a`Y*IBL#n?(7UL`ri$@-~eY=SDDw;b&>U145xYzeE zXinMZh5dG9*wNlnA!uQeLs;Jv(|4N}M$QvqJLcMK)Fxb{+rFk;WK$k!`);pD+g8jP z8iami_2};wH>ofoHx-G!Gw2v1XbUHIe>q-Ew+?Yrta_Osi_-8;mTvc$KDfcC1=sVs zzJTBP@=c4Fr{pJ)f5*N9u18EC~_7o$4!jWor8lUk#1$&o|}Sn^C(( z0fUNDfxDg-p!b9d%eLL@XQ{YVN>{`?O}K~PgPROQq5*$@z`}_8W=?AqTy=!MPbBEw zMC2=>kL-BL*{|1zJn|~bEntqV(%6pd%d%4LqeYL(=>a3per>Ww)UJsksd2&=%$WJM$Ee5=`#v&b zKp{{pp)T}ts*aFqRE^xaK$CWci)Q51D@OeFWb8VrdwVpkw#4t6%La}hI1YuW9jESd zeIGb{yh_GI1>V(Jq|G~H9rQN8S=hu<`mwykAStb6RJA@M`|;~%ekEx2E5&Dwg&Ccm z$rBx2@2B8RMdJK7eY&w4#z~Ce=87fGfmbk&Iy76s*<1vN3hdl)@pfN(q&HX5x)IY>8 z+ip|#c&4H*AWL89imYnwt%EZTy2p%KOq1y>UZuDvdud8K{%)FaVzNu#({D|>-`wS_ z5(D;sQnkqHA?w~VKX@g$a}QqSA%YLN{uM2LMmfmQFgP7m`2skae81p^-i2_jMkHok zeoZ&G2qIx{ca0sZ3%&Gqq%)SYe}JuVHSdEwb_n|kRkg8_{v?5}=|cUB)$)3-rg z%n%l;GCz@cIt_VY*Q9<)d-dXIW!mPL0q%t%uh?OsOP83Kc;(YOZakNu=MH{NF-0+K zrF{LyemN@i;*$0T%vAQRB9h3w{&UT`SMP6#l;2ihBj3X>@tc`Rujgy(Gc$^k+~=$t zqP&%f|b=gvCl4YxX64`?2Pm$H9@timaDrcZ7rOaQYhaw624QjRf&&20Lv`h) zgn^k-*qxI@_L<|})f;B^$vIZ&=zcOPq?~!Tv5UI?sLS|RX`y7yI_(Z+WcDA%h04|UG{piCp^kUeB-r_0b4)cN6wY!KbQRr5 z=#>)81J1&xD=-YGUwV6ArXDM3 z>!1s}tt7fSF4`C>c{(y~gokTqP7374aKPJ;lie46`F6a@xdkia^f~l7upwsnYvsh{ z{!g9Xy|V|r!l6fNo1)T-%H zPvwEGh^x43TUoMUH+$ZBKeN=CU9fwjTIq1*^Kq zR`HORI$!3Ynw1gEcA#eDd%geKwrxsg?%Q!UsjjbQjt~Iec2#GiB=YHVoAj)U%S`bV zsq_g>m21t2<#p`Z~WQ?eN9#UHWk}+irG_%O3vHQzg?CqGsnc zTI^!(5zPBGCgNHo@|}QFCj;*c#K6+qvbR}|mIcS>xejr)P| z*;&CS1MB;El`XKXrwSVQ)(KvpQ}1;id(th`@>z?6BGgNUpG-?^W$94OQhxHsm(Jv#vcXoV{ zR=@ouWAL&i;bfO~*{yOh|KYQD7CeSD*uUOCh@85J_#{QgH9axgw#!{OOw45ONDix% z3-g4tWxuF69j}sXy+`##Q=521i;E`ax@B*3dgC#oS%xl8*0cK_BMf)uKBT~1XJJvSZI!)&$ zt1}?+DG`P$1sCi10~%b{PdMTM+DA3{1E}!Hqe2e+E;QU#hcxuqYl~w6DZP`)f`tULjJ2{s22CIJGZQJ9K z5qxu1g8YN}(rb_?0n1!*>i0u6gEe_d<7JfLDy}w<9v~uapzw0v`DB;A7eTLL+r8l6lBXwK2x-H>Cu|>Vb%?OfwIch3jG9gDMEa$*d~rq zD>M1#lyLWRJo{TxhtoW+JZ|Vm35@JNRnX-!yrHw@eMF<6LSC^~LyVnk(yk5Q!8^gPKanhVs<$BSP&Wbq^= z@8;|Ys>y+49_zA6R1<0qXK!?XWmDB%@0gx=AzdCJAfc?|iO{`)a{dw7yBie%ZYD(h z(P>ZVtvU3_L&H_YN0pkRO1Ew+3l|8Mn#VtEz0lnv)ugNNkmV*NFZFghK+BSY;hv6+ z)7)}8!7h*6a?{bVvR768HMOwdCD^&yx6>oiFKcejHJ__7G?ZEq}(4Q#a7$7NU1fpCu+=SG4gF;#F)B?cd`WO*L}SEZr1o| zL~NW2wp!ocenOOAdN1RWZfER+kN0~FKDgY{yKt5|nik!wcC-=|Gw4zEZ5A`Pr$2tO z{F|!9egpLD(1mXf6)&q4URp*?1a=PwT3`1y=|I-C&NjGJxkWqOrK-NP%Kor*@m%(~ z7;5F@SyE&A?h5Q7F|UnnSlvU8qis+3SHZdGY#)%z=c zU8$afx_7~p0QX|ktCJB-^(zsYubVk8Qg?ey5F-@Y-;V@*Jdnxwc%SPo28(@ZmtQc# zzbh?afORhJSowliZ(r|wTwY%rf)rq6>_Qc}@byv@^hxgF7$qFSG;8}e8|rvue5u6K zw~j*$b-2v5>rOGMRf*cR2kzo29m*k5dK`Z6;)1&&+OTfhFRI_JZ#L6Qm2h1wUVJ5m z5v2+hb$Sci+peG6!&xqyr&M(hn&F3#6lgk3K;E-dA4%Dm$$e|!HM=!Z2*S97_H`VA ziOL;R`$gMUDEhED|M-i7R58c!sZa;;j@@p7a-GL-3)p2j>IT{3j7rRkU*^x2Pa=D^ z=yM5Al)=k@(!Jcb8Q`qc8${dUW9!6^WLo301g&P#`E@6+Y4US0j)gq8+ zo`6=MuRnO#z`>zs80b$R`T$IbC*Vc#(^vdZ)1U~Ukn|OIYumzY{Y?RHie)eza1OTH zNeuQOVo8dI1_FA4IFP^>U=kpKzEnR3E>K@_g%=0@UKYa?AuAB3kG|p_TL*|KjSfK2 zT4*gebVnd10I6so0MVn9$T%l6^B)x8lfI%ilj)Cx!PsoJ78|8SqkF*+SS%I>N5YUu zD2RYEg8Z0-K&T%>X_;b`!wg^$=@fq^h2{rY<|KI1SWJCIMQ|PRgB)BX?g#{hKMX8C zuh27?Bp4oiK!fps02mSu*M-87P$U+%Rv%oowf(8>$M}&&FrTnMf;$iLU`_yo#-bB}9RYwJQ)#VHe=3Wy)+UPqEQ?nBrjp1o(5V&6YjrH} zwhljamec4(@%3NPSVpf!l88TX{wz9m1w$gj04m@MI>G>%5r4olDdgWb=nwHMul$1} zpt+y?e?b41uN7Zb@`^K~5n0Pd@n-sp%l_g>G$MtBTluL?LXhCvIsg<+hQpy~A_)N{ zptKQCj5dNm)Bx^&_o`5bCjUb?*x;i8j z6bli363uB-7}=V0%&gNHjL%_obZ_U%;73SWX*4M;i-A zpb=Q4E*ybDtU9;C=mO9gV3{woBH&uOE7~L?&K#s9fYnCvC3pcae?PC4h2?$0fzg1O zB`h}wNW3BkgTa~70Rodo-$|oU^%a+?A7|D*$O_akZgM2wt#NEbO0w|AEQb zo5uG0-|_qo{mEiXXR>K@AA7pJ=RttT{O3G>2L8$91ddV$lOBZs7eW0uoZf0TSc1AV zde9pC&cLD7r`7aODJzo=0$G_cI0A8Xa~XsHfV8rMV3t>xh~5N0F900oKRWq0Ipt5f z#8U?eCzBBX6p2P+plCQ20o4V|9ZDwXBFF$i7YRqLmiA9}293;Q6X<}k7g%9n6@i_* zQbiE;AH}`(kKVAof#u2tiwO$HK-UzL;_7by80Y_?@Q`FU8G$82NdSfnMWYc!C>Dt& zKy{ErPaQ0oNG4!We{}c%79LV}Cjx~-VsP4+|CjJ|30N#RHK4klWGoa7jzy>^#uEc2 zVc?!{qAn7F#bEy3;c4R#Iye;axA62}%P-&GYE^If^<-;{TU*U~%damSetAeav;6(3 z6oCH6D*vtP{|DR}{U7t|U#YK!t!kUn{DZ&&@6EJl`~6e%e*#=(u%QqEKL+iexxN;% znwB+Z6wv2Y8F=;qk5aIoXQ>}O4))i7^6$s&_)nUEK>jxJm-72}y8cerU&_E=0{>lI zf2Zp&W#BJ?|E{ioTe<{(zl{O>z*9RLylcUFp4tfB)9`v)TbP00OB_=Y-tyVtFMfYZ zR|W@%@SWukXM(1%94O>t;%#^E4e`lu5QV&QYHb2V6ug=7PTh_&XJg?;$oibxzQtR%6vw7IgU3|SW+uakHOWGz6g38R?*nG_?bKYm$V&Iv<`02COW}bOw)~uPe_isfSUeKbZu0Kg>XXKFz(MtJkOSF~*`J8?AuP0yd(upX>mo4>VRE7jWhm1m^e^S90H4T(2Y zxA*5mwzooX$uAaau1PS$F2&Dx&-;qrk?l$6x!VcYdRk|JtDaD;c{90)o(lK|+RKgY zY<92uygGS~ua5q1tEGIJ2l>^~3#*^5ekhP{61-g4`399bz7`!cwTxyyIU65SthCq> z`$nQhNo8(tm4INWv6_8HvF8)g+LN#vl(%<_`&CI$apbJ%SD^*KiZ7I6#m_d* zKDumVk}Tl7xw7Lq)I}x@NQg~d%%L5sOdV*Y)11&Y>{9OXBbm{rSI#K1^Fqzmna!y~ zkl`}>6qQ%yU1<$DJvlS? z$hq#NWKeFo-{q0*vT-Tq=zHsz2x7Ies;90oKI>A}PML#nY&6*kDe8I4I@o>gRl$?@ z`wClYl#2^VR0EBSh3g7FgR0%fc3~D6DxO;B@Us&{M!{7)T4RHT zbViZG^iMye$!n4pRi?y|AX;F1QGx0h3XO#Z`~8Ni4!DWq@^1q@%f$KK&OKYO4Xc$k z8jX#zE2Z^L44vqx?8rEn7}~~=kx5Iy6bDNen%+?p5YOBZ z4F*WT&#(n_|He4*xENAcMy-`M;AnEeW1egFc*GFrakrPH$_+7#9GjOfOmu3yX4Vu7 zbutxJGMr(Tjtm66P$>2#+msf^#lVFC&xrgJRJG_P z1(KRoN)Y}o1Z2o_=OS!XLLpG!)(M<;qy0LtbF{*@i_A*X&hj*#h63N*Zc6CRZq}^i z9(w&jJ-{J6D$v?0Z}8%jICEN|sG8G95o0xQ-9w|1$398axjN81bz$0=K)yhm%GMLf zVsDLJk$q%y&jwg#Bt)=*%+>BG4*56Rt`wew*^q|GI-N7q-k#(1ijJCT8`-Ga8q{EG zP}OpZIIR@fQZPwoK|&qO*_;juzGb>Y7F|wX_)#nrT`HzPPg^`wZkE|_0#vlapBLyf zT5*AjZCFdJQd%R~4`fH{t6?2REwpv+ct746)jccCBN}(kVTzu!+dyu|Htd& z!eLWJm9`kwd!}w&H?uB#rtJ-8e&|b=X6!}c9V5;^FM-!W3mxZVqAK_p=Jd{==S+Lk zD^-gusvH?mZTV80 zwnSzt3({VfWM(w9+uSS7BU6UH+COA?HV>5_M|1N;lj(YcbFaLE@&}gi0D3q?J=A=g z2~qw&74HHXJ)f8$tJP(e-k7p0FmCNYo9#j{MnoBD42T|b8 zDGm-prt1gb18?F(c%?=MhM=`ch3_l!`jR~6h>eN+| zALkgi)I(=>+TqUGUe;k1Oo4*6=j$%yd{zHx-%N#y0{qz?bIwE-0kImm^iqNJhF00< z9BEvMjE>Q_M#DLJ4d3L9hX!H%aUva8*rF6NMXn-5HX<^=gl4n}L~YQhsW9GHadexi z3JwXb*W&ej5Dw74TVHaMPKpC$_VQ~Sl_cS_8{5O8jY)9ZZ%TUxY;(>V=g!4V$M3BB zQpMHA^&)-d0t2{T3arg;vT^(J+;9EZrWSVpW_!-Uc?;PG$1H;yZcftv+~n;f^(X8nY(*_(&2(y6)6+vdE#ZlArS z%8{5sN2s_AoR2ZGO}YH!^%r~LT5ZO+R?I7sCvR#&Mn7%+B>p}bSwL=;ZWs^AsVHsg0EQfvxj5SIKy?1QAXgBW|0FwzdsdETf1gUQ0c&N@y zG5G5VIJu5~d^fzCX&`)mw7WR6OF!kMY4|x>W9780+_P2zo@bbhHoY@L!n6sR`Q?}7 z1LeE|Lc1y)p{Z35r>V4WJ-r=|Uq7pcjl5XGeC@KO7G=sdOXEChD?*6U7^d|E zJ)_u$zlwF_^eb_Zq-ig4LbhCC+;1k_g7_4bl}~}fyicX*id0wcDoH>l{k!R)sg9c6 z{MwePn)}O?#hHakeRul`rk&*Br|4sa7ImOc6(RzFWEW5)W=aoe`(pt^;N&vC|k{6(jUm+mu;rh z$IN$ZUw-0whS-Kyb+7*|^;!=-g=pzh+AMR6?3UQHFOM6J#iW1A91JB))Z`_m|}@GJBae3e1cq<>U!Pm$wC3zH8sy zQ$u;U?gqN+xx)k-7{Cs-HRd8}bW9_jy)t;3g5AQ`;af#q9IqR*KI)3+EoFK|dhUw^ zAB(Z7zGTn%yXsc=D3L|J1&X zcA|e;hl^>|qwZHomTJ!rSu8fuUEjdB9AB&~eAG1XE2G50YTsXYBC6Z#+Nb~EiE;dE zah4i@822~o!+yB;F6Pf&Ap!4gi1+UIJ#j-pP54t>=Ez9ONMT%is@V<2c z!zyNe$%{6$=#xDvo2nD5&LiPUif%7HYYs?mNFSGp#82K3@B-3ehglEChe8rB*>$_QwWZLvRIRk9*nsQr#tTyUrU`xNVwm$x`6#7(a|GMiE3R@mmqMwO z^iK*Uvo!9l$df9JJ#9=9Z!01z|X8X(O zq1FC6pF7;1-u4n+Fl%dYNToUJS^DWw6xbWl|0dCta$i)J{VGMy)$BTrvq2;<>f2iP z#co-3Z96|t=c2*2)>^KGB>7fV{@XJR)Cn@N zI-m^FnbLSNYW{Z*LJ$Ql5CAzhi$h846D&mLUgkvlyBKYhtut93#-%_hO}Za*Sc z9L#Ni`|x@5NhcH?mF9ONi%&R{Q;^Q;4(NeOW|7sm&5Ya9ox3YGsPH13VhDn^aQ%J! zCFlAA$1*@jnUsOT<^~|SyZdWWQRPan5~qrbwg-VLFhAU z)4fis2gE!t`p9)yRgd1?WlY=qi#_-E>N-htMsKxhiW=?%t`&0D^%wE7H6Im7VK*z_ zxFQ|T&)QX{d(QuiSHfUf8vj%)qu6p$d4FlEuW(*7{bn(Ri-cyFPiOO-YD=NH?=$Ee z>qc2eaa<jo;jj;S8cR%l@3dae`YTq{M(o!F@T6c|)H}}(6d(uRvI_-x_ z2N;HFF{|Nd)j(VGw~hSASY$Uqp)sshkH)^fK<_4Je!)8>em!_&wUwIL#+ewyBO0YJ zCt@HH<%L%e5Z$<*U8`?AN1^L`?_nEb(uRz7ynIsT+7_Xqr`|&LoR3DpN1ql$W1*|~ zm!40!wVJ}To&9ehWl0>Od%C;FZVz7&kglOocqK%mbBd12t9jFJPVH>2PykC%9ZOGp z^J|?+!K}y9l_^8ubGK__YPVmgINm4snTYi4VJ+Lk{dTZQ6I7}+t7kSpxi8hTP4zij zNv-BV@Tg-}TGr;6XsD9omL}g^y+4_h$ea@$&r{C`>sKvyUJf^Mtk$kQERs(ot>Tw8 z+F1Y(aCWzQa|FQijwNMKc}~{ z>4WS~ZWwsRa1u49t(AzA8@ccnk}%#9qw655V|QkdkQx`cOFd2nyfsD1VCT#YuC{JqIRTjbm=ruk2a7e7?kKI<{?3W56!^oP0*?Ms~Y z2l}+VhltnUPKohR4Dsh

}^BIiguT4|5||WbObLN84_&&&RM!&8&n7`fC_MIMgs5 zRQY+cwicg_8{fT$#f$6+i^pH5c1nJqr_8Ijig1=X@Fo|ZL~_tBldzXY*mRTC=;nt>;gj+>g_!kK?$) ze)+lCi7k>+rNqzMBZ6*A0UNZT^@^_Z787-Q?Bs ziZ$Jkfk$7pH?05r<V#n7rofl$<-55QmYv3Xo-3WKsfN8z zYq*!)GdqJ<9-j>{5xC6=U#>}G2^Td+*zOhKfhlQUo94A^y!&+6q%bJYcTD}Zin{Ml zs%UN(>`chcItDgPCLEN>|ft60r?8YJ+HqWdDD5$)_1Jl)a+*5)8p@3 z${s(6nfW9=GS(Q<5iAUdZ_!eZenC-?737xPTbjb9xLvN{My>b2k27xawh1?(H`%h( zH}76SvcV_g%g(9$eVw^hqSnBJ2Z?QKL!Hs)t7;y+d-ioqO!%wF^WSK1O!QRF}3g%j@& z#UOs(BM8Ask>5h!kXOwOhvtP!KqbIH4IiwRB>y=|UIiQq12D2_zzbcwbin!UyPz7dWK&&Y_ORBXL-F0@lry_mC4| z=jKUJr69-V@e24&$Bp56Q1WN)XVW3~_iCTUA zpVqGUA6X>k6Xb(%2SFsjAQzWES>Op8UVr%eOAEXy@$?pCjK;fp;*e+!FSIK`;8&;a z&Yt*ReR|^2hoYmnolzJNF{mTYzuIVN>l^;GIZUHH*2VqE;t>5S5{3MUbN9qKA7M~P z5ZW2-LJR~?WQP0+PrzdSSfD@6=TP%M0wKEl$^R$x@A*243WY&~rGKH)cEuAAu1NGD z6_H#5OXRVGz@(&L5F}6zhLHwB?IdM^c6JbHAlS|hgG9p67%)QW7YYL$me>gh=U<~b zq(Tv?V6tF2Gy+Ym4e7&n{?vAwXaC^v8XA1YI<3)+N$I7}NvMj8eN%gREbP-%#y^!MP77|qc*Jh99V zSs`Evxg%>760S+4L=dYD>w>UHgWO&1j}(XN0w>Ofm|4VOa}bG-8HS`33KW!eV^? z9s00ic@=*2B3#c6c@)-BC}`A?@_R$8BJ4rmv7mni{vS*x4sPDA|9?DxK!38R;0WGs zI41*~ft@27N%+@1e+B-@WK7&j@dTW&_P?3bf5Rz!?}qb4TQ{8VFaAx?9^Xgb)8mXi z+Szz{kMSzUtS^lmP|l#Q%M@e{ARfndwQ&5I+#8EKo*HhB!T07y<~BhR6Y75aIKs>G#D{INZPW_2IuRx5DB64%a^HViQkycV{db z_vbSBy)FJ9xL@plH2A+W{}uM#TFuSfm$(Bu5DdIs|E2rC0DfoC!y?hHc(;EQ`md1h zY5C94#Dk0zcDh>XfP=%&gSy-VqJ+Zz zyd@q0plLe%9!nIX;Uo&l3EKJ^%uoOxpmfvZ^vn`lW3`NkjNZY6tSqF6nlD! zcK1Tqiuk3PY^gJwLMU%6&$h4qTi-?BMz96P@F?!#IUcSw@WjsbqNcA~*v~aKS=X0X z_%HEy$!whUkjBL_Q10_p*M!U7nv#iM>^v1Ym(USC4&7MvG`JOe1^emq#_{0c6usb4 zbD8yip$(=*&oxce<2$?&&r@-c8cEDNk-C>R<6WfWI238Cqv+>JE5^~G;;V%M4{E$^#>ELQ%Ds+2Y#*? zoWT3d&pPaYkDog!Qi@8Y0ViMD4wgoKd32}ONSuYQiES*1yf-PmW50Y-rOx~s9a4G6 zalW-T+NG!YmYLLpb0(_Yue_I+18-*)YqFiVC;o_AHkQ?Ls}ifwSuav)%F$c8 z>_tJIDKlGI9fpC}TGvhM`vAZT$CV4~ zRs<3ZcHZTq$noebQ0aMwP9!rzF%_NEpadyg)Q|AtGG8V zJGcC%i`0v1=W}bE&Oj4%GVnYoSNnc%x1Q_nhe4uC#X5_Xi6zw`;2V|og8hMYy%qA{ zVGaq_@muEyl1e_XylfB_wMa6Li!12ec}d$$`^N0PLG;B0x;no%HpIJ