diff --git a/forge-adventure/pom.xml b/forge-adventure/pom.xml index d2bc0fcd175..4d81b150e02 100644 --- a/forge-adventure/pom.xml +++ b/forge-adventure/pom.xml @@ -17,7 +17,7 @@ - src + src/main/java ${project.basedir} diff --git a/forge-adventure/src/main/java/forge/adventure/Main.java b/forge-adventure/src/main/java/forge/adventure/Main.java index e6f6d316166..04b20f7700e 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; @@ -107,7 +103,13 @@ public class Main { } }); - + for(int i=0;i BiomeEdit.this.updateTerrain())); + tilesetName.getDocument().addDocumentListener(new DocumentChangeListener(() -> BiomeEdit.this.updateTerrain())); + color.getDocument().addDocumentListener(new DocumentChangeListener(() -> BiomeEdit.this.updateTerrain())); + collision.addChangeListener(e -> BiomeEdit.this.updateTerrain()); + spriteNames.getEdit().getDocument().addDocumentListener(new DocumentChangeListener(() -> BiomeEdit.this.updateTerrain())); + enemies.getEdit().getDocument().addDocumentListener(new DocumentChangeListener(() -> BiomeEdit.this.updateTerrain())); + terrain.addChangeListener(e -> BiomeEdit.this.updateTerrain()); + + + startPointX.addChangeListener(e -> BiomeEdit.this.updateTerrain()); + startPointY.addChangeListener(e -> BiomeEdit.this.updateTerrain()); + noiseWeight.addChangeListener(e -> BiomeEdit.this.updateTerrain()); + distWeight.addChangeListener(e -> BiomeEdit.this.updateTerrain()); + tilesetAtlas.getEdit().getDocument().addDocumentListener(new DocumentChangeListener(() -> BiomeEdit.this.updateTerrain())); + width.addChangeListener(e -> BiomeEdit.this.updateTerrain()); + height.addChangeListener(e -> BiomeEdit.this.updateTerrain()); + refresh(); + } + + protected void updateTerrain() { + if(currentData==null||updating) + return; + currentData.startPointX = (Float) startPointX.getValue(); + currentData.startPointY = (Float) startPointY.getValue(); + currentData.noiseWeight = (Float) noiseWeight.getValue(); + currentData.distWeight = (Float)distWeight.getValue(); + currentData.name = name.getText(); + currentData.tilesetAtlas = tilesetAtlas.edit.getText(); + currentData.tilesetName = tilesetName.getText(); + currentData.terrain = terrain.getBiomeTerrainData(); + currentData.structures = structures.getBiomeStructureData(); + currentData.width = (Float) width.getValue(); + currentData.height = (Float) height.getValue(); + currentData.color = color.getText(); + currentData.collision = collision.isSelected(); + currentData.spriteNames = spriteNames.getList(); + currentData.enemies = enemies.getList(); + currentData.pointsOfInterest = pointsOfInterest.getList(); + } + + public void setCurrentBiome(BiomeData data) + { + currentData=data; + refresh(); + } + + private void refresh() { + setEnabled(currentData!=null); + if(currentData==null) + { + return; + } + updating=true; + startPointX.setValue(currentData.startPointX); + startPointY.setValue(currentData.startPointY); + noiseWeight.setValue(currentData.noiseWeight); + distWeight.setValue(currentData.distWeight); + name.setText(currentData.name); + tilesetAtlas.edit.setText( currentData.tilesetAtlas); + tilesetName.setText(currentData.tilesetName); + terrain.setTerrains(currentData); + structures.setStructures(currentData); + width.setValue(currentData.width); + height.setValue(currentData.height); + color.setText(currentData.color); + spriteNames.setText(currentData.spriteNames); + enemies.setText(currentData.enemies); + collision.setSelected(currentData.collision); + pointsOfInterest.setText(currentData.pointsOfInterest); + updating=false; + } +} 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..9ff1938ecf3 --- /dev/null +++ b/forge-adventure/src/main/java/forge/adventure/editor/BiomeStructureDataMappingEditor.java @@ -0,0 +1,171 @@ +package forge.adventure.editor; + +import forge.adventure.data.BiomeStructureData; +import forge.adventure.util.Config; + +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(); + if(data==null||data.mappingInfo==null) + return; + for(int i=0;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 FormPanel{ + BiomeStructureData.BiomeStructureDataMapping currentData; + + + public JTextField name=new JTextField(); + public JTextField color=new JTextField(); + public JCheckBox collision=new JCheckBox(); + private boolean updating=false; + + public BiomeStructureDataMappingEdit() + { + + + add("name:",name); + add("color:",color); + add("collision:",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 new file mode 100644 index 00000000000..3682fede08c --- /dev/null +++ b/forge-adventure/src/main/java/forge/adventure/editor/BiomeStructureEdit.java @@ -0,0 +1,136 @@ +package forge.adventure.editor; + +import forge.adventure.data.BiomeData; +import forge.adventure.data.BiomeStructureData; + +import javax.swing.*; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +public class BiomeStructureEdit extends FormPanel { + 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 width= new FloatSpinner(); + public FloatSpinner height= new FloatSpinner(); + public JCheckBox randomPosition=new JCheckBox(); + public IntSpinner N= new IntSpinner(); + public JTextField sourcePath= new JTextField(); + public JTextField maskPath= 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() + { + FormPanel center=new FormPanel(); + + center.add("structureAtlasPath:",structureAtlasPath); + center.add("x:",x); + center.add("y:",y); + center.add("width:",width); + center.add("height:",height); + center.add("N:",N); + center.add("sourcePath:",sourcePath); + center.add("maskPath:",maskPath); + center.add("periodicInput:",periodicInput); + center.add("ground:",ground); + center.add("symmetry:",symmetry); + center.add("periodicOutput:",periodicOutput); + + add(center); + add(data); + + structureAtlasPath.getDocument().addDocumentListener(new DocumentChangeListener(() -> BiomeStructureEdit.this.updateStructure())); + + + x.addChangeListener(e -> BiomeStructureEdit.this.updateStructure()); + y.addChangeListener(e -> BiomeStructureEdit.this.updateStructure()); + width.addChangeListener(e -> BiomeStructureEdit.this.updateStructure()); + height.addChangeListener(e -> BiomeStructureEdit.this.updateStructure()); + randomPosition.addChangeListener(e -> BiomeStructureEdit.this.updateStructure()); + + N.addChangeListener(e -> BiomeStructureEdit.this.updateStructure()); + sourcePath.getDocument().addDocumentListener(new DocumentChangeListener(() -> BiomeStructureEdit.this.updateStructure())); + maskPath.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() { + setEnabled(currentData!=null); + if(currentData==null) + { + data.setCurrent(null); + return; + } + updating=true; + structureAtlasPath.setText(currentData.structureAtlasPath); + x.setValue(currentData.x); + y.setValue(currentData.y); + width.setValue(currentData.width); + height.setValue(currentData.height); + randomPosition.setSelected(currentData.randomPosition); + N.setValue(currentData.N); + sourcePath.setText(currentData.sourcePath); + maskPath.setText(currentData.maskPath); + periodicInput.setSelected(currentData.periodicInput); + ground.setValue(currentData.ground); + symmetry.setValue(currentData.symmetry); + periodicOutput.setSelected(currentData.periodicOutput); + + data.setCurrent(currentData); + + + + updating=false; + } + public void updateStructure() + { + + if(currentData==null||updating) + return; + currentData.structureAtlasPath=structureAtlasPath.getText(); + + currentData.x= x.floatValue(); + currentData.y= y.floatValue(); + currentData.width= width.floatValue(); + currentData.height= height.floatValue(); + currentData.randomPosition=randomPosition.isSelected(); + currentData.mappingInfo= data.getCurrent(); + + currentData.N= N.intValue(); + currentData.sourcePath= sourcePath.getText(); + currentData.maskPath= maskPath.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) { + currentData =biomeTerrainData; + currentBiomeData=data; + refresh(); + } + + public void addChangeListener(ChangeListener listener) { + listenerList.add(ChangeListener.class, listener); + } + protected void emitChanged() { + ChangeListener[] listeners = listenerList.getListeners(ChangeListener.class); + if (listeners != null && listeners.length > 0) { + ChangeEvent evt = new ChangeEvent(this); + for (ChangeListener listener : listeners) { + listener.stateChanged(evt); + } + } + } + +} diff --git a/forge-adventure/src/main/java/forge/adventure/editor/BiomeTerrainEdit.java b/forge-adventure/src/main/java/forge/adventure/editor/BiomeTerrainEdit.java new file mode 100644 index 00000000000..86698e0fcdf --- /dev/null +++ b/forge-adventure/src/main/java/forge/adventure/editor/BiomeTerrainEdit.java @@ -0,0 +1,84 @@ +package forge.adventure.editor; + +import forge.adventure.data.BiomeData; +import forge.adventure.data.BiomeTerrainData; + +import javax.swing.*; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +public class BiomeTerrainEdit extends FormPanel { + SwingAtlasPreview preview=new SwingAtlasPreview(128); + private boolean updating=false; + BiomeTerrainData currentData; + BiomeData currentBiomeData; + public JTextField spriteName=new JTextField(); + public JSpinner min= new JSpinner(new SpinnerNumberModel(0.0f, 0.f, 1f, 0.1f)); + public JSpinner max= new JSpinner(new SpinnerNumberModel(0.0f, 0.f, 1f, 0.1f)); + public JSpinner resolution= new JSpinner(new SpinnerNumberModel(0.0f, 0.f, 1f, 0.1f)); + + public BiomeTerrainEdit() + { + FormPanel center=new FormPanel() { }; + + center.add("spriteName:",spriteName); + center.add("min:",min); + center.add("max:",max); + center.add("resolution:",resolution); + add(center,preview); + + spriteName.getDocument().addDocumentListener(new DocumentChangeListener(() -> BiomeTerrainEdit.this.updateTerrain())); + + min.addChangeListener(e -> BiomeTerrainEdit.this.updateTerrain()); + max.addChangeListener(e -> BiomeTerrainEdit.this.updateTerrain()); + resolution.addChangeListener(e -> BiomeTerrainEdit.this.updateTerrain()); + + + refresh(); + } + private void refresh() { + setEnabled(currentData!=null); + if(currentData==null) + { + return; + } + updating=true; + spriteName.setText(currentData.spriteName); + min.setValue(currentData.min); + max.setValue(currentData.max); + resolution.setValue(currentData.resolution); + if(currentBiomeData!=null&¤tData!= null) + preview.setSpritePath(currentBiomeData.tilesetAtlas,currentData.spriteName); + updating=false; + } + public void updateTerrain() + { + + if(currentData==null||updating) + return; + currentData.spriteName=spriteName.getText(); + currentData.min= (float) min.getValue(); + currentData.max= (float) max.getValue(); + currentData.resolution= (float) resolution.getValue(); + preview.setSpritePath(currentBiomeData.tilesetAtlas,currentData.spriteName); + emitChanged(); + } + public void setCurrentTerrain(BiomeTerrainData biomeTerrainData, BiomeData data) { + currentData =biomeTerrainData; + currentBiomeData=data; + refresh(); + } + + public void addChangeListener(ChangeListener listener) { + listenerList.add(ChangeListener.class, listener); + } + protected void emitChanged() { + ChangeListener[] listeners = listenerList.getListeners(ChangeListener.class); + if (listeners != null && listeners.length > 0) { + ChangeEvent evt = new ChangeEvent(this); + for (ChangeListener listener : listeners) { + listener.stateChanged(evt); + } + } + } +} 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 50f0d877cc9..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,8 +15,8 @@ public class EditorMainWindow extends JFrame { BorderLayout layout=new BorderLayout(); setLayout(layout); add(tabs); + tabs.addTab("World",worldEditor); tabs.addTab("POI",new PointOfInterestEditor()); - tabs.addTab("World",new WorldEditor()); tabs.addTab("Items",new ItemsEditor()); tabs.addTab("Enemies",new EnemyEditor()); setVisible(true); diff --git a/forge-adventure/src/main/java/forge/adventure/editor/EffectEditor.java b/forge-adventure/src/main/java/forge/adventure/editor/EffectEditor.java index d483f1fbb82..656e79651a1 100644 --- a/forge-adventure/src/main/java/forge/adventure/editor/EffectEditor.java +++ b/forge-adventure/src/main/java/forge/adventure/editor/EffectEditor.java @@ -5,7 +5,6 @@ import forge.adventure.data.EffectData; import javax.swing.*; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; -import java.awt.*; public class EffectEditor extends JComponent { EffectData currentData; @@ -25,16 +24,15 @@ public class EffectEditor extends JComponent { if(!isOpponentEffect) opponent=new EffectEditor(true); setLayout(new BoxLayout(this,BoxLayout.Y_AXIS)); - JPanel parameters=new JPanel(); + FormPanel parameters=new FormPanel(); parameters.setBorder(BorderFactory.createTitledBorder("Effect")); - parameters.setLayout(new GridLayout(7,2)) ; - parameters.add(new JLabel("Name:")); parameters.add(name); - parameters.add(new JLabel("Start with extra cards:")); parameters.add(changeStartCards); - parameters.add(new JLabel("Change life:")); parameters.add(lifeModifier); - parameters.add(new JLabel("Movement speed:")); parameters.add(moveSpeed); - parameters.add(new JLabel("Start battle with cards:")); parameters.add(startBattleWithCard); - parameters.add(new JLabel("color view:")); parameters.add(colorView); + parameters.add("Name:", name); + parameters.add("Start with extra cards:", changeStartCards); + parameters.add("Change life:", lifeModifier); + parameters.add("Movement speed:", moveSpeed); + parameters.add("Start battle with cards:", startBattleWithCard); + parameters.add("color view:", colorView); add(parameters); if(!isOpponentEffect) { add(new JLabel("Opponent:")); add(opponent);} diff --git a/forge-adventure/src/main/java/forge/adventure/editor/EnemyEdit.java b/forge-adventure/src/main/java/forge/adventure/editor/EnemyEdit.java index 399d7c88334..a8b67b43b27 100644 --- a/forge-adventure/src/main/java/forge/adventure/editor/EnemyEdit.java +++ b/forge-adventure/src/main/java/forge/adventure/editor/EnemyEdit.java @@ -3,21 +3,21 @@ package forge.adventure.editor; import forge.adventure.data.EnemyData; import javax.swing.*; -import java.awt.*; /** * Editor class to edit configuration, maybe moved or removed */ -public class EnemyEdit extends JComponent { +public class EnemyEdit extends FormPanel { EnemyData currentData; - - JTextField nameField=new JTextField(); JTextField colorField=new JTextField(); - JSpinner lifeFiled= new JSpinner(new SpinnerNumberModel(0, 0, 1000, 1)); - JSpinner spawnRate= new JSpinner(new SpinnerNumberModel(0.0, 0., 1, 0.1)); - JSpinner difficulty= new JSpinner(new SpinnerNumberModel(0.0, 0., 1, 0.1)); - JSpinner speed= new JSpinner(new SpinnerNumberModel(0.0, 0., 100., 1.0)); + JTextField ai=new JTextField(); + JCheckBox flying=new JCheckBox(); + JCheckBox boss=new JCheckBox(); + FloatSpinner lifeFiled= new FloatSpinner(0, 1000, 1); + FloatSpinner spawnRate= new FloatSpinner( 0.f, 1, 0.1f); + FloatSpinner difficulty= new FloatSpinner( 0.f, 1, 0.1f); + FloatSpinner speed= new FloatSpinner( 0.f, 100.f, 1.0f); FilePicker deck=new FilePicker(new String[]{"dck","json"}); FilePicker atlas=new FilePicker(new String[]{"atlas"}); JTextField equipment=new JTextField(); @@ -28,27 +28,34 @@ public class EnemyEdit extends JComponent { public EnemyEdit() { - JComponent center=new JComponent() { }; - center.setLayout(new GridLayout(9,2)); + FormPanel center=new FormPanel() { }; - center.add(new JLabel("Name:")); center.add(nameField); - center.add(new JLabel("Life:")); center.add(lifeFiled); - center.add(new JLabel("Spawn rate:")); center.add(spawnRate); - center.add(new JLabel("Difficulty:")); center.add(difficulty); - center.add(new JLabel("Speed:")); center.add(speed); - center.add(new JLabel("Deck:")); center.add(deck); - center.add(new JLabel("Sprite:")); center.add(atlas); - center.add(new JLabel("Equipment:")); center.add(equipment); - center.add(new JLabel("Colors:")); center.add(colorField); - BorderLayout layout=new BorderLayout(); - setLayout(layout); - add(center,BorderLayout.PAGE_START); - add(rewards,BorderLayout.CENTER); - add(preview,BorderLayout.LINE_START); + center.add("Name:",nameField); + center.add("Life:",lifeFiled); + center.add("Spawn rate:",spawnRate); + center.add("Difficulty:",difficulty); + center.add("Speed:",speed); + center.add("Deck:",deck); + center.add("Sprite:",atlas); + center.add("Equipment:",equipment); + center.add("Colors:",colorField); + + center.add("ai:",ai); + center.add("flying:",flying); + center.add("boss:",boss); + + + add(preview); + add(center); + add(rewards); equipment.getDocument().addDocumentListener(new DocumentChangeListener(() -> EnemyEdit.this.updateEnemy())); atlas.getEdit().getDocument().addDocumentListener(new DocumentChangeListener(() -> EnemyEdit.this.updateEnemy())); colorField.getDocument().addDocumentListener(new DocumentChangeListener(() -> EnemyEdit.this.updateEnemy())); + ai.getDocument().addDocumentListener(new DocumentChangeListener(() -> EnemyEdit.this.updateEnemy())); + flying.addChangeListener(e -> EnemyEdit.this.updateEnemy()); + boss.addChangeListener(e -> EnemyEdit.this.updateEnemy()); + nameField.getDocument().addDocumentListener(new DocumentChangeListener(() -> EnemyEdit.this.updateEnemy())); deck.getEdit().getDocument().addDocumentListener(new DocumentChangeListener(() -> EnemyEdit.this.updateEnemy())); lifeFiled.addChangeListener(e -> EnemyEdit.this.updateEnemy()); @@ -65,6 +72,9 @@ public class EnemyEdit extends JComponent { return; currentData.name=nameField.getText(); currentData.colors=colorField.getText(); + currentData.ai=ai.getText(); + currentData.flying=flying.isSelected(); + currentData.boss=boss.isSelected(); currentData.life= (int) lifeFiled.getValue(); currentData.sprite= atlas.getEdit().getText(); if(equipment.getText().isEmpty()) @@ -94,6 +104,9 @@ public class EnemyEdit extends JComponent { updating=true; nameField.setText(currentData.name); colorField.setText(currentData.colors); + ai.setText(currentData.ai); + boss.setSelected(currentData.boss); + flying.setSelected(currentData.flying); lifeFiled.setValue(currentData.life); atlas.getEdit().setText(currentData.sprite); if(currentData.equipment!=null) diff --git a/forge-adventure/src/main/java/forge/adventure/editor/FloatSpinner.java b/forge-adventure/src/main/java/forge/adventure/editor/FloatSpinner.java new file mode 100644 index 00000000000..7fad0e3bad3 --- /dev/null +++ b/forge-adventure/src/main/java/forge/adventure/editor/FloatSpinner.java @@ -0,0 +1,19 @@ +package forge.adventure.editor; + +import javax.swing.*; + +public class FloatSpinner extends JSpinner{ + + public FloatSpinner() + { + this( 0.f, 1f, 0.1f); + } + public FloatSpinner(float min,float max,float stepSize) + { + super(new SpinnerNumberModel(new Float(0.0f), new Float(min), new Float (max), new Float(stepSize))); + } + public float floatValue() + { + return ((Float)getValue()).floatValue(); + } +} diff --git a/forge-adventure/src/main/java/forge/adventure/editor/FormPanel.java b/forge-adventure/src/main/java/forge/adventure/editor/FormPanel.java new file mode 100644 index 00000000000..f28e3aea4aa --- /dev/null +++ b/forge-adventure/src/main/java/forge/adventure/editor/FormPanel.java @@ -0,0 +1,63 @@ +package forge.adventure.editor; + +import javax.swing.*; +import java.awt.*; + +public class FormPanel extends JPanel { + int row=0; + static final int MAXIMUM_LINES=300; + public FormPanel() + { + setLayout(new GridBagLayout()) ; + + + GridBagConstraints constraint=new GridBagConstraints(); + constraint.weightx = 1.0; + constraint.weighty = 1.0; + constraint.gridy=MAXIMUM_LINES; + constraint.gridx=0; + constraint.gridwidth=2; + add(Box.createVerticalGlue(),constraint); + row++; + } + public void add(JComponent name,JComponent element) + { + GridBagConstraints constraint=new GridBagConstraints(); + constraint.ipadx = 5; + constraint.ipady = 5; + constraint.weightx = 1.0; + constraint.weighty = 0.0; + constraint.gridy=row; + constraint.gridx=0; + constraint.anchor=GridBagConstraints.NORTHWEST; + add(name,constraint); + constraint.gridy=row; + constraint.gridx=1; + constraint.fill=GridBagConstraints.HORIZONTAL; + constraint.anchor=GridBagConstraints.NORTHEAST; + add(element,constraint); + + row++; + } + public void add(String name,JComponent element) + { + add(new JLabel(name),element); + } + public void add(JComponent element) + { + GridBagConstraints constraint=new GridBagConstraints(); + constraint.ipadx = 5; + constraint.ipady = 5; + constraint.weightx = 1.0; + constraint.weighty = 0.0; + constraint.gridy=row; + constraint.gridx=0; + constraint.gridwidth=2; + constraint.fill=GridBagConstraints.HORIZONTAL; + constraint.anchor=GridBagConstraints.NORTHEAST; + add(element,constraint); + + row++; + } + +} diff --git a/forge-adventure/src/main/java/forge/adventure/editor/IntSpinner.java b/forge-adventure/src/main/java/forge/adventure/editor/IntSpinner.java new file mode 100644 index 00000000000..132817d3891 --- /dev/null +++ b/forge-adventure/src/main/java/forge/adventure/editor/IntSpinner.java @@ -0,0 +1,20 @@ +package forge.adventure.editor; + +import javax.swing.*; + + +public class IntSpinner extends JSpinner { + + public IntSpinner() + { + this( 0, 100, 1); + } + public IntSpinner(int min,int max,int stepSize) + { + super(new SpinnerNumberModel(new Integer(0), new Integer(min), new Integer (max), new Integer(stepSize))); + } + public int intValue() + { + return ((Integer)getValue()).intValue(); + } +} \ No newline at end of file diff --git a/forge-adventure/src/main/java/forge/adventure/editor/ItemEdit.java b/forge-adventure/src/main/java/forge/adventure/editor/ItemEdit.java index 248bd275914..ea480107035 100644 --- a/forge-adventure/src/main/java/forge/adventure/editor/ItemEdit.java +++ b/forge-adventure/src/main/java/forge/adventure/editor/ItemEdit.java @@ -10,8 +10,6 @@ import java.awt.*; */ public class ItemEdit extends JComponent { ItemData currentData; - - JTextField nameField=new JTextField(); JTextField equipmentSlot=new JTextField(); JTextField iconName=new JTextField(); @@ -26,19 +24,19 @@ public class ItemEdit extends JComponent { { setLayout(new BoxLayout(this,BoxLayout.Y_AXIS)); - JPanel parameters=new JPanel(); + FormPanel parameters=new FormPanel(); parameters.setBorder(BorderFactory.createTitledBorder("Parameter")); - parameters.setLayout(new GridLayout(6,2)) ; - parameters.add(new JLabel("Name:")); parameters.add(nameField); - parameters.add(new JLabel("equipmentSlot:")); parameters.add(equipmentSlot); - parameters.add(new JLabel("description:")); parameters.add(description); - parameters.add(new JLabel("iconName")); parameters.add(iconName); - parameters.add(new JLabel("questItem")); parameters.add(questItem); - parameters.add(new JLabel("cost")); parameters.add(cost); + parameters.add("Name:",nameField); + parameters.add("equipmentSlot:",equipmentSlot); + parameters.add("description:",description); + parameters.add("iconName",iconName); + parameters.add("questItem",questItem); + parameters.add("cost",cost); add(parameters); add(effect); + add(new Box.Filler(new Dimension(0,0),new Dimension(0,Integer.MAX_VALUE),new Dimension(0,Integer.MAX_VALUE))); nameField.getDocument().addDocumentListener(new DocumentChangeListener(() -> ItemEdit.this.updateItem())); equipmentSlot.getDocument().addDocumentListener(new DocumentChangeListener(() -> ItemEdit.this.updateItem())); diff --git a/forge-adventure/src/main/java/forge/adventure/editor/PointOfInterestEdit.java b/forge-adventure/src/main/java/forge/adventure/editor/PointOfInterestEdit.java new file mode 100644 index 00000000000..2f883dae9db --- /dev/null +++ b/forge-adventure/src/main/java/forge/adventure/editor/PointOfInterestEdit.java @@ -0,0 +1,85 @@ +package forge.adventure.editor; + +import forge.adventure.data.PointOfInterestData; + +import javax.swing.*; + +public class PointOfInterestEdit extends JComponent { + + PointOfInterestData currentData; + + + JTextField name = new JTextField(); + JTextField type = new JTextField(); + JSpinner count = new JSpinner(new SpinnerNumberModel(0, 0, 1000, 1)); + FilePicker spriteAtlas = new FilePicker(new String[]{"atlas"}); + JTextField sprite = new JTextField(); + FilePicker map = new FilePicker(new String[]{"tmx"}); + JSpinner radiusFactor= new JSpinner(new SpinnerNumberModel(0.0f, 0.0f, 2.0f, 0.1f)); + + + private boolean updating=false; + + public PointOfInterestEdit() + { + + setLayout(new BoxLayout(this,BoxLayout.Y_AXIS)); + FormPanel parameters=new FormPanel(); + parameters.setBorder(BorderFactory.createTitledBorder("Parameter")); + + parameters.add("Name:",name); + parameters.add("Type:",type); + parameters.add("Count:",count); + parameters.add("Sprite atlas:",spriteAtlas); + parameters.add("Sprite:",sprite); + parameters.add("Map:",map); + parameters.add("Radius factor:",radiusFactor); + + add(parameters); + + name.getDocument().addDocumentListener(new DocumentChangeListener(() -> PointOfInterestEdit.this.updateItem())); + type.getDocument().addDocumentListener(new DocumentChangeListener(() -> PointOfInterestEdit.this.updateItem())); + count.addChangeListener(e -> PointOfInterestEdit.this.updateItem()); + spriteAtlas.getEdit().getDocument().addDocumentListener(new DocumentChangeListener(() -> PointOfInterestEdit.this.updateItem())); + sprite.getDocument().addDocumentListener(new DocumentChangeListener(() -> PointOfInterestEdit.this.updateItem())); + map.getEdit().getDocument().addDocumentListener(new DocumentChangeListener(() -> PointOfInterestEdit.this.updateItem())); + radiusFactor.addChangeListener(e -> PointOfInterestEdit.this.updateItem()); + refresh(); + } + + private void updateItem() { + if(currentData==null||updating) + return; + currentData.name=name.getText(); + currentData.type= type.getText(); + currentData.count= ((Integer) count.getValue()).intValue(); + currentData.spriteAtlas=spriteAtlas.getEdit().getText(); + currentData.sprite=sprite.getText(); + currentData.map=map.getEdit().getText(); + currentData.radiusFactor=((Float) radiusFactor.getValue()).floatValue(); + } + + public void setCurrent(PointOfInterestData data) + { + currentData=data; + refresh(); + } + + private void refresh() { + setEnabled(currentData!=null); + if(currentData==null) + { + return; + } + updating=true; + name.setText(currentData.name); + type.setText(currentData.type); + count.setValue(currentData.count); + spriteAtlas.getEdit().setText(currentData.spriteAtlas); + sprite.setText(currentData.sprite); + map.getEdit().setText(currentData.map); + radiusFactor.setValue(currentData.radiusFactor); + + updating=false; + } +} diff --git a/forge-adventure/src/main/java/forge/adventure/editor/PointOfInterestEditor.java b/forge-adventure/src/main/java/forge/adventure/editor/PointOfInterestEditor.java index 83ff548e68e..4fa4b0e9209 100644 --- a/forge-adventure/src/main/java/forge/adventure/editor/PointOfInterestEditor.java +++ b/forge-adventure/src/main/java/forge/adventure/editor/PointOfInterestEditor.java @@ -1,6 +1,131 @@ package forge.adventure.editor; -import java.awt.*; +import com.badlogic.gdx.files.FileHandle; +import com.badlogic.gdx.utils.Array; +import com.badlogic.gdx.utils.Json; +import com.badlogic.gdx.utils.JsonWriter; +import forge.adventure.data.PointOfInterestData; +import forge.adventure.util.Config; +import forge.adventure.util.Paths; -public class PointOfInterestEditor extends Component { +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionListener; +import java.util.HashMap; + +public class PointOfInterestEditor extends JComponent { + DefaultListModel model = new DefaultListModel<>(); + JList list = new JList<>(model); + JToolBar toolBar = new JToolBar("toolbar"); + PointOfInterestEdit edit=new PointOfInterestEdit(); + static HashMap atlas=new HashMap<>(); + + + + public class PointOfInterestRenderer extends DefaultListCellRenderer { + @Override + public Component getListCellRendererComponent( + JList list, Object value, int index, + boolean isSelected, boolean cellHasFocus) { + JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + if(!(value instanceof PointOfInterestData)) + return label; + PointOfInterestData poi=(PointOfInterestData) value; + // Get the renderer component from parent class + + label.setText(poi.name); + if(!atlas.containsKey(poi.spriteAtlas)) + atlas.put(poi.spriteAtlas,new SwingAtlas(Config.instance().getFile(poi.spriteAtlas))); + + SwingAtlas poiAtlas = atlas.get(poi.spriteAtlas); + + if(poiAtlas.has(poi.sprite)) + label.setIcon(poiAtlas.get(poi.sprite)); + else + { + ImageIcon img=poiAtlas.getAny(); + if(img!=null) + label.setIcon(img); + } + return label; + } + } + public void addButton(String name, ActionListener action) + { + JButton newButton=new JButton(name); + newButton.addActionListener(action); + toolBar.add(newButton); + + } + public PointOfInterestEditor() + { + + list.setCellRenderer(new PointOfInterestEditor.PointOfInterestRenderer()); + list.addListSelectionListener(e -> PointOfInterestEditor.this.updateEdit()); + addButton("add", e -> PointOfInterestEditor.this.addItem()); + addButton("remove", e -> PointOfInterestEditor.this.remove()); + addButton("copy", e -> PointOfInterestEditor.this.copy()); + addButton("load", e -> PointOfInterestEditor.this.load()); + addButton("save", e -> PointOfInterestEditor.this.save()); + BorderLayout layout=new BorderLayout(); + setLayout(layout); + add(new JScrollPane(list), BorderLayout.LINE_START); + add(toolBar, BorderLayout.PAGE_START); + add(edit,BorderLayout.CENTER); + load(); + } + private void copy() { + + int selected=list.getSelectedIndex(); + if(selected<0) + return; + PointOfInterestData data=new PointOfInterestData(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 save() + { + Array allEnemies=new Array<>(); + for(int i=0;i allEnemies=new Array<>(); + Json json = new Json(); + FileHandle handle = Config.instance().getFile(Paths.POINTS_OF_INTEREST); + if (handle.exists()) + { + Array readEnemies=json.fromJson(Array.class, PointOfInterestData.class, handle); + allEnemies = readEnemies; + } + for (int i=0;i RewardEdit.this.updateReward())); + probability.addChangeListener(e -> RewardEdit.this.updateReward()); + count.addChangeListener(e -> RewardEdit.this.updateReward()); + addMaxCount.addChangeListener(e -> RewardEdit.this.updateReward()); + cardName.getDocument().addDocumentListener(new DocumentChangeListener(() -> RewardEdit.this.updateReward())); + itemName.getDocument().addDocumentListener(new DocumentChangeListener(() -> RewardEdit.this.updateReward())); + editions.getEdit().getDocument().addDocumentListener(new DocumentChangeListener(() -> RewardEdit.this.updateReward())); + colors.getEdit().getDocument().addDocumentListener(new DocumentChangeListener(() -> RewardEdit.this.updateReward())); + rarity.getEdit().getDocument().addDocumentListener(new DocumentChangeListener(() -> RewardEdit.this.updateReward())); + subTypes.getEdit().getDocument().addDocumentListener(new DocumentChangeListener(() -> RewardEdit.this.updateReward())); + cardTypes.getEdit().getDocument().addDocumentListener(new DocumentChangeListener(() -> RewardEdit.this.updateReward())); + superTypes.getEdit().getDocument().addDocumentListener(new DocumentChangeListener(() -> RewardEdit.this.updateReward())); + manaCosts.getEdit().getDocument().addDocumentListener(new DocumentChangeListener(() -> RewardEdit.this.updateReward())); + keyWords.getEdit().getDocument().addDocumentListener(new DocumentChangeListener(() -> RewardEdit.this.updateReward())); + colorType.addActionListener((e -> RewardEdit.this.updateReward())); + cardText.getDocument().addDocumentListener(new DocumentChangeListener(() -> RewardEdit.this.updateReward())); } diff --git a/forge-adventure/src/main/java/forge/adventure/editor/RewardsEditor.java b/forge-adventure/src/main/java/forge/adventure/editor/RewardsEditor.java index a2d6cc0f617..786128f6bad 100644 --- a/forge-adventure/src/main/java/forge/adventure/editor/RewardsEditor.java +++ b/forge-adventure/src/main/java/forge/adventure/editor/RewardsEditor.java @@ -5,10 +5,7 @@ import forge.adventure.data.RewardData; import javax.swing.*; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; -import javax.swing.event.ListSelectionEvent; -import javax.swing.event.ListSelectionListener; import java.awt.*; -import java.awt.event.ActionEvent; import java.awt.event.ActionListener; /** @@ -59,30 +56,10 @@ public class RewardsEditor extends JComponent{ { list.setCellRenderer(new RewardDataRenderer()); - list.addListSelectionListener(new ListSelectionListener() { - @Override - public void valueChanged(ListSelectionEvent e) { - RewardsEditor.this.updateEdit(); - } - }); - addButton("add", new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - RewardsEditor.this.addReward(); - } - }); - addButton("remove", new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - RewardsEditor.this.remove(); - } - }); - addButton("copy", new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - RewardsEditor.this.copy(); - } - }); + list.addListSelectionListener(e -> RewardsEditor.this.updateEdit()); + addButton("add", e -> RewardsEditor.this.addReward()); + addButton("remove", e -> RewardsEditor.this.remove()); + addButton("copy", e -> RewardsEditor.this.copy()); BorderLayout layout=new BorderLayout(); setLayout(layout); add(list, BorderLayout.LINE_START); diff --git a/forge-adventure/src/main/java/forge/adventure/editor/StructureEditor.java b/forge-adventure/src/main/java/forge/adventure/editor/StructureEditor.java new file mode 100644 index 00000000000..9e9ad4733a4 --- /dev/null +++ b/forge-adventure/src/main/java/forge/adventure/editor/StructureEditor.java @@ -0,0 +1,222 @@ +package forge.adventure.editor; + +import com.badlogic.gdx.graphics.Color; +import forge.adventure.data.BiomeData; +import forge.adventure.data.BiomeStructureData; +import forge.adventure.util.Config; +import forge.adventure.world.BiomeStructure; +import forge.adventure.world.ColorMap; + +import javax.imageio.ImageIO; +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; +import java.io.File; +import java.io.IOException; + +/** + * Editor class to edit configuration, maybe moved or removed + */ +public class StructureEditor extends JComponent{ + DefaultListModel model = new DefaultListModel<>(); + JList list = new JList<>(model); + JToolBar toolBar = new JToolBar("toolbar"); + BiomeStructureEdit edit=new BiomeStructureEdit(); + + BiomeData currentData; + + public class StructureDataRenderer extends DefaultListCellRenderer { + @Override + public Component getListCellRendererComponent( + JList list, Object value, int index, + boolean isSelected, boolean cellHasFocus) { + JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + if(!(value instanceof BiomeStructureData)) + return label; + BiomeStructureData structureData=(BiomeStructureData) value; + label.setText("Structure"); + label.setIcon(new ImageIcon(Config.instance().getFilePath(structureData.sourcePath))); + return label; + } + } + public void addButton(String name, ActionListener action) + { + JButton newButton=new JButton(name); + newButton.addActionListener(action); + toolBar.add(newButton); + + } + + public StructureEditor() + { + + list.setCellRenderer(new StructureDataRenderer()); + list.addListSelectionListener(e -> StructureEditor.this.updateEdit()); + 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.WEST); + add(toolBar, BorderLayout.NORTH); + add(edit,BorderLayout.CENTER); + + + edit.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + emitChanged(); + } + }); + } + protected void emitChanged() { + ChangeListener[] listeners = listenerList.getListeners(ChangeListener.class); + if (listeners != null && listeners.length > 0) { + ChangeEvent evt = new ChangeEvent(this); + for (ChangeListener listener : listeners) { + listener.stateChanged(evt); + } + } + } + private void test() { + if (list.isSelectionEmpty()) + return; + long start = System.currentTimeMillis(); + BiomeStructureData data = model.get(list.getSelectedIndex()); + + try + { + + BiomeStructure struct = new BiomeStructure(data, System.currentTimeMillis(), + (int) (currentData.width * EditorMainWindow.worldEditor.width.intValue() ), + (int) (currentData.width * EditorMainWindow.worldEditor.height.intValue())); + + BufferedImage sourceImage= null; + try { + sourceImage = ImageIO.read(new File(struct.sourceImagePath())); + ColorMap sourceColorMap=new ColorMap(sourceImage.getWidth(),sourceImage.getHeight()); + for(int y=0;y 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,"Calculating took "+ calcTime+" seconds",JOptionPane.PLAIN_MESSAGE); + } + catch (Exception e) + { + JOptionPane.showMessageDialog(this, "WaveFunctionCollapse was not successful","can not calculate function "+e.getMessage(),JOptionPane.ERROR_MESSAGE); + } + + } + private void copy() { + + int selected=list.getSelectedIndex(); + if(selected<0) + return; + BiomeStructureData data=new BiomeStructureData(model.get(selected)); + model.add(model.size(),data); + } + + private void updateEdit() { + + int selected=list.getSelectedIndex(); + if(selected<0) + return; + edit.setCurrentStructure(model.get(selected),currentData); + } + + void addStructure() + { + BiomeStructureData data=new BiomeStructureData(); + model.add(model.size(),data); + } + void remove() + { + int selected=list.getSelectedIndex(); + if(selected<0) + return; + model.remove(selected); + } + public void setStructures(BiomeData data) { + + currentData=data; + model.clear(); + if(data==null||data.structures==null) + { + edit.setCurrentStructure(null,null); + return; + } + for (int i=0;i> images=new HashMap<>(); public HashMap> getImages() { return images; } - public SwingAtlas(FileHandle path) + public SwingAtlas(FileHandle path,int imageSize) { + this.imageSize=imageSize; if(!path.exists()||!path.toString().endsWith(".atlas")) return; TextureAtlas.TextureAtlasData data=new TextureAtlas.TextureAtlasData(path,path.parent(),false); @@ -37,20 +39,41 @@ public class SwingAtlas { images.put(name,new ArrayList<>()); } ArrayList imageList=images.get(name); - try { + try + { imageList.add(spriteToImage(region)); - } catch (IOException e) { + } + catch (IOException e) + { e.printStackTrace(); } } } - - private ImageIcon spriteToImage(TextureAtlas.TextureAtlasData.Region sprite) throws IOException { - BufferedImage img = ImageIO.read(sprite.page.textureFile.file()); - return new ImageIcon(img.getSubimage(sprite.left,sprite.top, sprite.width, sprite.height).getScaledInstance(32,32,SCALE_FAST)); + public SwingAtlas(FileHandle path) + { + this(path,32); } + private ImageIcon spriteToImage(TextureAtlas.TextureAtlasData.Region sprite) throws IOException { + try + { + BufferedImage img = ImageIO.read(sprite.page.textureFile.file()); + if(sprite.width== sprite.height) + return new ImageIcon(img.getSubimage(sprite.left,sprite.top, sprite.width, sprite.height).getScaledInstance(imageSize,imageSize,SCALE_FAST)); + if(sprite.width>sprite.height) + return new ImageIcon(img.getSubimage(sprite.left,sprite.top, sprite.width, sprite.height).getScaledInstance(imageSize, (int) (imageSize*(sprite.height/(float)sprite.width)),SCALE_FAST)); + return new ImageIcon(img.getSubimage(sprite.left,sprite.top, sprite.width, sprite.height).getScaledInstance((int) (imageSize*(sprite.width/(float)sprite.height)),imageSize,SCALE_FAST)); + + } + catch (IOException e) + { + return null; + } +} + public ImageIcon get(String name) { + if(images.get(name).size()==0) + return null; return images.get(name).get(0); } 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 3631e133a8c..31abaaef9a7 100644 --- a/forge-adventure/src/main/java/forge/adventure/editor/SwingAtlasPreview.java +++ b/forge-adventure/src/main/java/forge/adventure/editor/SwingAtlasPreview.java @@ -13,11 +13,12 @@ import java.util.Map; * Editor class to edit configuration, maybe moved or removed */ public class SwingAtlasPreview extends Box { + int imageSize=32; private String sprite=""; + private String spriteName=""; Timer timer; public SwingAtlasPreview() { super(BoxLayout.Y_AXIS); - timer = new Timer(200, new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { @@ -28,25 +29,50 @@ public class SwingAtlasPreview extends Box { } }); } + public SwingAtlasPreview(int size) { + this(); + imageSize=size; + } int counter=0; List>> labels=new ArrayList<>(); public void setSpritePath(String sprite) { - if(this.sprite==null||this.sprite.equals(sprite)) + setSpritePath(sprite,null); + } + public void setSpritePath(String sprite,String name) { + if(this.sprite==null||sprite==null||(this.sprite.equals(sprite)&&(spriteName!=null&&name!=null&&spriteName.equals(name)))) return; removeAll(); counter=0; labels.clear(); this.sprite=sprite; - SwingAtlas atlas=new SwingAtlas(Config.instance().getFile(sprite)); + this.spriteName=name; + SwingAtlas atlas=new SwingAtlas(Config.instance().getFile(sprite),imageSize); + int maxCount=0; for(Map.Entry> element:atlas.getImages().entrySet()) { - JLabel image=new JLabel(element.getValue().get(0)); - add(new JLabel(element.getKey())); - add(image); - labels.add(Pair.of(image, element.getValue())); + if(name==null||element.getKey().equals(name)) + { + JLabel image=new JLabel(element.getValue().get(0)); + if(maxCount model = new DefaultListModel<>(); + JList list = new JList<>(model); + JToolBar toolBar = new JToolBar("toolbar"); + BiomeTerrainEdit edit=new BiomeTerrainEdit(); + + BiomeData currentData; + + public class TerrainDataRenderer extends DefaultListCellRenderer { + @Override + public Component getListCellRendererComponent( + JList list, Object value, int index, + boolean isSelected, boolean cellHasFocus) { + JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + if(!(value instanceof BiomeTerrainData)) + return label; + BiomeTerrainData terrainData=(BiomeTerrainData) value; + StringBuilder builder=new StringBuilder(); + builder.append("Terrain"); + builder.append(" "); + builder.append(terrainData.spriteName); + label.setText(builder.toString()); + return label; + } + } + public void addButton(String name, ActionListener action) + { + JButton newButton=new JButton(name); + newButton.addActionListener(action); + toolBar.add(newButton); + + } + + public TerrainsEditor() + { + + list.setCellRenderer(new TerrainDataRenderer()); + list.addListSelectionListener(e -> TerrainsEditor.this.updateEdit()); + addButton("add", e -> TerrainsEditor.this.addTerrain()); + addButton("remove", e -> TerrainsEditor.this.remove()); + addButton("copy", e -> TerrainsEditor.this.copy()); + BorderLayout layout=new BorderLayout(); + setLayout(layout); + add(list, BorderLayout.LINE_START); + add(toolBar, BorderLayout.PAGE_START); + add(edit,BorderLayout.CENTER); + + + edit.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + emitChanged(); + } + }); + } + protected void emitChanged() { + ChangeListener[] listeners = listenerList.getListeners(ChangeListener.class); + if (listeners != null && listeners.length > 0) { + ChangeEvent evt = new ChangeEvent(this); + for (ChangeListener listener : listeners) { + listener.stateChanged(evt); + } + } + } + private void copy() { + + int selected=list.getSelectedIndex(); + if(selected<0) + return; + BiomeTerrainData data=new BiomeTerrainData(model.get(selected)); + model.add(model.size(),data); + } + + private void updateEdit() { + + int selected=list.getSelectedIndex(); + if(selected<0) + return; + edit.setCurrentTerrain(model.get(selected),currentData); + } + + void addTerrain() + { + BiomeTerrainData data=new BiomeTerrainData(); + model.add(model.size(),data); + } + void remove() + { + int selected=list.getSelectedIndex(); + if(selected<0) + return; + model.remove(selected); + } + public void setTerrains(BiomeData data) { + + currentData=data; + model.clear(); + if(data==null||data.terrain==null) + return; + for (int i=0;i itemNames) { + if(itemNames==null) + edit.setText(""); + else + edit.setText(String.join(";",itemNames)); + } public void setText(String[] itemName) { if(itemName==null) edit.setText(""); @@ -77,7 +86,7 @@ public class TextListEdit extends Box { { values.append(intValues[i]); if(intValues.length>i+2) - values.append(";"); + values.append("\n"); } edit.setText(values.toString()); } 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 221ac5d64ae..d2ee9ea1f9a 100644 --- a/forge-adventure/src/main/java/forge/adventure/editor/WorldEditor.java +++ b/forge-adventure/src/main/java/forge/adventure/editor/WorldEditor.java @@ -1,6 +1,226 @@ package forge.adventure.editor; -import java.awt.*; +import com.badlogic.gdx.files.FileHandle; +import com.badlogic.gdx.utils.Json; +import com.badlogic.gdx.utils.JsonWriter; +import forge.adventure.data.BiomeData; +import forge.adventure.data.WorldData; +import forge.adventure.util.Config; +import forge.adventure.util.Paths; -public class WorldEditor extends Component { +import javax.swing.*; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import java.awt.*; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; + +public class WorldEditor extends JComponent { + + WorldData currentData; + + + 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(); + FloatSpinner maxRoadDistance = new FloatSpinner( 0, 100000f, 1f); + TextListEdit biomesNames = new TextListEdit(); + + DefaultListModel model = new DefaultListModel<>(); + JList list = new JList<>(model); + BiomeEdit edit=new BiomeEdit(); + JTabbedPane tabs =new JTabbedPane(); + static HashMap atlas=new HashMap<>(); + + public class BiomeDataRenderer extends DefaultListCellRenderer { + @Override + public Component getListCellRendererComponent( + JList list, Object value, int index, + boolean isSelected, boolean cellHasFocus) { + JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + if(!(value instanceof BiomeData)) + return label; + BiomeData biome=(BiomeData) value; + // Get the renderer component from parent class + + label.setText(biome.name); + if(!atlas.containsKey(biome.tilesetAtlas)) + atlas.put(biome.tilesetAtlas,new SwingAtlas(Config.instance().getFile(biome.tilesetAtlas))); + + SwingAtlas poiAtlas = atlas.get(biome.tilesetAtlas); + + if(poiAtlas.has(biome.tilesetName)) + label.setIcon(poiAtlas.get(biome.tilesetName)); + else + { + ImageIcon img=poiAtlas.getAny(); + if(img!=null) + label.setIcon(img); + } + return label; + } + } + + /** + * + */ + private void updateBiome() { + + int selected=list.getSelectedIndex(); + if(selected<0) + return; + edit.setCurrentBiome(model.get(selected)); + } + + public WorldEditor() { + list.setCellRenderer(new BiomeDataRenderer()); + list.addListSelectionListener(new ListSelectionListener() { + @Override + public void valueChanged(ListSelectionEvent e) { + WorldEditor.this.updateBiome(); + } + }); + BorderLayout layout = new BorderLayout(); + setLayout(layout); + add(tabs); + JSplitPane biomeData=new JSplitPane(); + tabs.addTab("BiomeData", biomeData); + + + FormPanel worldPanel=new FormPanel(); + worldPanel.add("width:",width); + worldPanel.add("height:",height); + worldPanel.add("playerStartPosX:",playerStartPosX); + worldPanel.add("playerStartPosY:",playerStartPosY); + worldPanel.add("noiseZoomBiome:",noiseZoomBiome); + worldPanel.add("tileSize:",tileSize); + worldPanel.add("biomesSprites:",biomesSprites); + worldPanel.add("maxRoadDistance:",maxRoadDistance); + worldPanel.add("biomesNames:",biomesNames); + tabs.addTab("WorldData", worldPanel); + + + JScrollPane pane = new JScrollPane(edit); + biomeData.setLeftComponent(list); biomeData.setRightComponent(pane); + + load(); + + JToolBar toolBar = new JToolBar("toolbar"); + add(toolBar, BorderLayout.PAGE_START); + JButton newButton=new JButton("save"); + newButton.addActionListener(e -> WorldEditor.this.save()); + toolBar.add(newButton); + + newButton=new JButton("save selected biome"); + newButton.addActionListener(e -> WorldEditor.this.saveBiome()); + toolBar.add(newButton); + + newButton=new JButton("load"); + newButton.addActionListener(e -> WorldEditor.this.load()); + toolBar.add(newButton); + + toolBar.addSeparator(); + + newButton=new JButton("test map"); + newButton.addActionListener(e -> WorldEditor.this.test()); + toolBar.add(newButton); + } + + private void test() { + + String javaHome = System.getProperty("java.home"); + String javaBin = javaHome + File.separator + "bin" + File.separator + "java"; + String classpath = System.getProperty("java.class.path"); + String className = forge.adventure.Main.class.getName(); + + ArrayList command = new ArrayList<>(); + command.add(javaBin); + command.add("-cp"); + command.add(classpath); + command.add(className); + + command.add("testMap"); + + ProcessBuilder build= new ProcessBuilder(command); + build .redirectInput(ProcessBuilder.Redirect.INHERIT) + .redirectOutput(ProcessBuilder.Redirect.INHERIT) + .redirectError(ProcessBuilder.Redirect.INHERIT); + try { + Process process= build.start(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + void saveBiome() + { + + edit.updateTerrain(); + Json json = new Json(JsonWriter.OutputType.json); + FileHandle handle = Config.instance().getFile(currentData.biomesNames[list.getSelectedIndex()]); + handle.writeString(json.prettyPrint(json.toJson(edit.currentData, BiomeData.class)),false); + + } + 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= (biomesNames.getList()); + + Json json = new Json(JsonWriter.OutputType.json); + FileHandle handle = Config.instance().getFile(Paths.WORLD); + handle.writeString(json.prettyPrint(json.toJson(currentData, WorldData.class)),false); + + } + void load() + { + + + + model.clear(); + Json json = new Json(); + FileHandle handle = Config.instance().getFile(Paths.WORLD); + if (handle.exists()) + { + currentData=json.fromJson(WorldData.class, WorldData.class, handle); + } + update(); + } + + private void update() { + width.setValue(currentData.width); + height.setValue(currentData.height); + playerStartPosX.setValue(currentData.playerStartPosX); + playerStartPosY.setValue(currentData.playerStartPosY); + noiseZoomBiome.setValue(currentData.noiseZoomBiome); + tileSize.setValue(currentData.tileSize); + biomesSprites.setText(currentData.biomesSprites); + maxRoadDistance.setValue(currentData.maxRoadDistance); + biomesNames.setText(currentData.biomesNames); + + for(String path:currentData.biomesNames) + { + Json json = new Json(); + FileHandle handle = Config.instance().getFile(path); + if (handle.exists()) + { + BiomeData data=json.fromJson(BiomeData.class, BiomeData.class, handle); + model.addElement(data); + } + } + + } } diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java index 1c1b88a4313..e2c2998c1a7 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java @@ -778,9 +778,12 @@ public class ComputerUtilCost { // Check if the AI intends to play the card and if it can pay for it with the mana it has boolean willPlay = ComputerUtil.hasReasonToPlayCardThisTurn(payer, c); boolean canPay = c.getManaCost().canBePaidWithAvailable(ColorSet.fromNames(getAvailableManaColors(payer, source)).getColor()); - return canPay && willPlay; + if (canPay && willPlay) { + return true; + } } } + return false; } } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/AnimateEffectBase.java b/forge-game/src/main/java/forge/game/ability/effects/AnimateEffectBase.java index 6df27fae809..02df76cee8b 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/AnimateEffectBase.java +++ b/forge-game/src/main/java/forge/game/ability/effects/AnimateEffectBase.java @@ -65,10 +65,6 @@ public abstract class AnimateEffectBase extends SpellAbilityEffect { source.addRemembered(c); } - if ((power != null) || (toughness != null)) { - c.addNewPT(power, toughness, timestamp, 0); - } - if (!addType.isEmpty() || !removeType.isEmpty() || addAllCreatureTypes || removeSuperTypes || removeCardTypes || removeSubTypes || removeLandTypes || removeCreatureTypes || removeArtifactTypes || removeEnchantmentTypes) { c.addChangedCardTypes(addType, removeType, addAllCreatureTypes, removeSuperTypes, removeCardTypes, removeSubTypes, @@ -77,6 +73,11 @@ public abstract class AnimateEffectBase extends SpellAbilityEffect { c.addChangedCardKeywords(keywords, removeKeywords, removeAll, timestamp, 0); + // do this after changing types in case it wasn't a creature before + if (power != null || toughness != null) { + c.addNewPT(power, toughness, timestamp, 0); + } + if (sa.hasParam("CantHaveKeyword")) { c.addCantHaveKeyword(timestamp, Keyword.setValueOf(sa.getParam("CantHaveKeyword"))); } diff --git a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java index 0d68aae35db..551c18a6290 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java @@ -1131,7 +1131,7 @@ public class CardFactoryUtil { SpellAbility loseLifeSA = AbilityFactory.getAbility(loseLifeStr, card); AbilitySub gainLifeSA = (AbilitySub) AbilityFactory.getAbility(gainLifeStr, card); - gainLifeSA.setSVar("AFLifeLost", "Number$0"); + loseLifeSA.setSVar("AFLifeLost", "Number$0"); loseLifeSA.setSubAbility(gainLifeSA); loseLifeSA.setIntrinsic(intrinsic); diff --git a/forge-game/src/main/java/forge/game/card/CardView.java b/forge-game/src/main/java/forge/game/card/CardView.java index 4c31d2c1cd6..352ced47d8c 100644 --- a/forge-game/src/main/java/forge/game/card/CardView.java +++ b/forge-game/src/main/java/forge/game/card/CardView.java @@ -1247,12 +1247,17 @@ public class CardView extends GameEntityView { return get(TrackableProperty.Power); } void updatePower(Card c) { - if (c.getCurrentState().getView() == this || c.getAlternateState() == null) { - set(TrackableProperty.Power, c.getNetPower()); + int num; + if (getType().hasSubtype("Vehicle") && !isCreature()) { + // use printed value so user can still see it + num = c.getCurrentPower(); + } else { + num = c.getNetPower(); } - else { - set(TrackableProperty.Power, c.getNetPower() - c.getBasePower() + c.getAlternateState().getBasePower()); + if (c.getCurrentState().getView() != this && c.getAlternateState() != null) { + num -= c.getBasePower() + c.getAlternateState().getBasePower(); } + set(TrackableProperty.Power, num); } void updatePower(CardState c) { Card card = c.getCard(); @@ -1267,12 +1272,17 @@ public class CardView extends GameEntityView { return get(TrackableProperty.Toughness); } void updateToughness(Card c) { - if (c.getCurrentState().getView() == this || c.getAlternateState() == null) { - set(TrackableProperty.Toughness, c.getNetToughness()); + int num; + if (getType().hasSubtype("Vehicle") && !isCreature()) { + // use printed value so user can still see it + num = c.getCurrentToughness(); + } else { + num = c.getNetToughness(); } - else { - set(TrackableProperty.Toughness, c.getNetToughness() - c.getBaseToughness() + c.getAlternateState().getBaseToughness()); + if (c.getCurrentState().getView() != this && c.getAlternateState() != null) { + num -= c.getBaseToughness() + c.getAlternateState().getBaseToughness(); } + set(TrackableProperty.Toughness, num); } void updateToughness(CardState c) { Card card = c.getCard(); diff --git a/forge-gui-mobile/src/forge/Forge.java b/forge-gui-mobile/src/forge/Forge.java index 7da28d0c317..f08a4b9a1f3 100644 --- a/forge-gui-mobile/src/forge/Forge.java +++ b/forge-gui-mobile/src/forge/Forge.java @@ -123,6 +123,7 @@ public class Forge implements ApplicationListener { private static Cursor cursor0, cursor1, cursor2, cursorA0, cursorA1, cursorA2; public static boolean forcedEnglishonCJKMissing = false; public static boolean adventureLoaded = false; + public static boolean createNewAdventureMap = false; private static Localizer localizer; public static ApplicationListener getApp(Clipboard clipboard0, IDeviceAdapter deviceAdapter0, String assetDir0, boolean value, boolean androidOrientation, int totalRAM, boolean isTablet, int AndroidAPI, String AndroidRelease, String deviceName) { @@ -328,6 +329,9 @@ public class Forge implements ApplicationListener { altZoneTabs = true; //pixl cursor for adventure setCursor(null, "0"); + loadAdventureResources(true); + } + private static void loadAdventureResources(boolean startScene) { try { if(!adventureLoaded) { @@ -336,7 +340,8 @@ public class Forge implements ApplicationListener { } adventureLoaded=true; } - switchScene(SceneType.StartScene.instance); + if (startScene) + switchScene(SceneType.StartScene.instance); } catch (Exception e) { e.printStackTrace(); } @@ -362,19 +367,28 @@ public class Forge implements ApplicationListener { //load Drafts preloadBoosterDrafts(); FThreads.invokeInEdtLater(() -> { + if (selector.equals("Adventure")) { + //preload adventure resources to speedup startup if selector is adventure. Needs in edt when setting up worldstage + loadAdventureResources(false); + } //selection transition setTransitionScreen(new TransitionScreen(() -> { - if (selector.equals("Classic")) { - openHomeDefault(); - clearSplashScreen(); - } else if (selector.equals("Adventure")) { + if (createNewAdventureMap) { openAdventure(); clearSplashScreen(); - } else if (splashScreen != null) { - splashScreen.setShowModeSelector(true); - } else {//default mode in case splashscreen is null at some point as seen on resume.. - openHomeDefault(); - clearSplashScreen(); + } else { + if (selector.equals("Classic")) { + openHomeDefault(); + clearSplashScreen(); + } else if (selector.equals("Adventure")) { + openAdventure(); + clearSplashScreen(); + } else if (splashScreen != null) { + splashScreen.setShowModeSelector(true); + } else {//default mode in case splashscreen is null at some point as seen on resume.. + openHomeDefault(); + clearSplashScreen(); + } } //start background music SoundSystem.instance.setBackgroundMusic(MusicPlaylist.MENUS); @@ -887,6 +901,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(); @@ -944,10 +960,10 @@ public class Forge implements ApplicationListener { @Override public void dispose() { if (currentScreen != null) { - FOverlay.hideAll(); currentScreen.onClose(null); currentScreen = null; } + FOverlay.hideAll(); assets.dispose(); Dscreens.clear(); graphics.dispose(); diff --git a/forge-gui-mobile/src/forge/adventure/character/CharacterSprite.java b/forge-gui-mobile/src/forge/adventure/character/CharacterSprite.java index b71a6881cfd..8197085fffd 100644 --- a/forge-gui-mobile/src/forge/adventure/character/CharacterSprite.java +++ b/forge-gui-mobile/src/forge/adventure/character/CharacterSprite.java @@ -1,11 +1,6 @@ package forge.adventure.character; -import com.badlogic.gdx.graphics.g2d.Animation; -import com.badlogic.gdx.graphics.g2d.Batch; -import com.badlogic.gdx.graphics.g2d.Sprite; -import com.badlogic.gdx.graphics.g2d.TextureAtlas; -import com.badlogic.gdx.graphics.g2d.TextureRegion; -import com.badlogic.gdx.math.Rectangle; +import com.badlogic.gdx.graphics.g2d.*; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.scenes.scene2d.Actor; import com.badlogic.gdx.utils.Array; @@ -38,7 +33,7 @@ public class CharacterSprite extends MapActor { @Override void updateBoundingRect() { //We want a slimmer box for the player entity so it can navigate terrain without getting stuck. - boundingRect = new Rectangle(getX() + 4, getY(), getWidth() - 6, getHeight() * collisionHeight); + boundingRect.set(getX() + 4, getY(), getWidth() - 6, getHeight() * collisionHeight); } protected void load(String path) { @@ -61,8 +56,14 @@ public class CharacterSprite extends MapActor { anim = atlas.createSprites(stand.toString()); else anim = atlas.createSprites(stand.toString() + dir.toString()); + if (anim.size != 0) { dirs.put(dir, new Animation<>(0.2f, anim)); + if(getWidth()==0.0)//init size onload + { + setWidth(anim.first().getWidth()); + setHeight(anim.first().getHeight()); + } } } animations.put(stand, dirs); diff --git a/forge-gui-mobile/src/forge/adventure/character/EnemySprite.java b/forge-gui-mobile/src/forge/adventure/character/EnemySprite.java index 48792671778..19c2836f7d1 100644 --- a/forge-gui-mobile/src/forge/adventure/character/EnemySprite.java +++ b/forge-gui-mobile/src/forge/adventure/character/EnemySprite.java @@ -4,7 +4,6 @@ import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.g2d.Batch; import com.badlogic.gdx.graphics.g2d.TextureRegion; -import com.badlogic.gdx.math.Rectangle; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.scenes.scene2d.Actor; import com.badlogic.gdx.utils.Array; @@ -50,7 +49,7 @@ public class EnemySprite extends CharacterSprite { @Override void updateBoundingRect() { //We want enemies to take the full tile. - boundingRect = new Rectangle(getX(), getY(), getWidth(), getHeight()); + boundingRect.set(getX(), getY(), getWidth(), getHeight()); } public void moveTo(Actor other, float delta) { @@ -182,5 +181,8 @@ public class EnemySprite extends CharacterSprite { } } + public float speed() { + return data.speed; + } } diff --git a/forge-gui-mobile/src/forge/adventure/character/MapActor.java b/forge-gui-mobile/src/forge/adventure/character/MapActor.java index bbe8e4086ba..5bdfa8497cc 100644 --- a/forge-gui-mobile/src/forge/adventure/character/MapActor.java +++ b/forge-gui-mobile/src/forge/adventure/character/MapActor.java @@ -14,7 +14,7 @@ public class MapActor extends Actor { Texture debugTexture; - float collisionHeight=1.0f; + protected float collisionHeight=1.0f; final int objectId; public MapActor(int objectId) { @@ -35,7 +35,7 @@ public class MapActor extends Actor { } return debugTexture; } - Rectangle boundingRect; + final Rectangle boundingRect=new Rectangle(); boolean isCollidingWithPlayer=false; protected void onPlayerCollide() @@ -55,9 +55,8 @@ public class MapActor extends Actor { } @Override protected void positionChanged() { - - updateBoundingRect(); super.positionChanged(); + updateBoundingRect(); } @Override @@ -67,7 +66,7 @@ public class MapActor extends Actor { } void updateBoundingRect() { - boundingRect = new Rectangle(getX(), getY(), getWidth(), getHeight()*collisionHeight); + boundingRect.set(getX(), getY(), getWidth(), getHeight()*collisionHeight); } public Rectangle boundingRect() { @@ -106,4 +105,8 @@ public class MapActor extends Actor { return boundingRect.x < other.getX() + other.getWidth() && boundingRect.x + boundingRect.width > other.getX() && boundingRect.y < other.getY() + other.getHeight() && boundingRect.y + boundingRect.height > other.getY(); } + + public float getCollisionHeight() { + return collisionHeight; + } } diff --git a/forge-gui-mobile/src/forge/adventure/character/RewardSprite.java b/forge-gui-mobile/src/forge/adventure/character/RewardSprite.java index f1f0584b085..a6ba72c8b55 100644 --- a/forge-gui-mobile/src/forge/adventure/character/RewardSprite.java +++ b/forge-gui-mobile/src/forge/adventure/character/RewardSprite.java @@ -1,6 +1,5 @@ package forge.adventure.character; -import com.badlogic.gdx.math.Rectangle; import com.badlogic.gdx.utils.Array; import forge.adventure.data.RewardData; import forge.adventure.util.JSONStringLoader; @@ -40,7 +39,7 @@ public class RewardSprite extends CharacterSprite { @Override void updateBoundingRect() { //We want rewards to take a full tile. - boundingRect = new Rectangle(getX(), getY(), getWidth(), getHeight()); + boundingRect.set(getX(), getY(), getWidth(), getHeight()); } public Array getRewards() { //Get list of rewards. diff --git a/forge-gui-mobile/src/forge/adventure/data/BiomeData.java b/forge-gui-mobile/src/forge/adventure/data/BiomeData.java index 51176c3a8b4..1bd21bc2ecb 100644 --- a/forge-gui-mobile/src/forge/adventure/data/BiomeData.java +++ b/forge-gui-mobile/src/forge/adventure/data/BiomeData.java @@ -6,7 +6,6 @@ import forge.util.MyRandom; import java.io.Serializable; import java.util.ArrayList; -import java.util.List; import java.util.Random; /** @@ -15,7 +14,6 @@ import java.util.Random; * contains the information for the biomes */ public class BiomeData implements Serializable { - private final Random rand = MyRandom.getRandom(); public float startPointX; public float startPointY; public float noiseWeight; @@ -27,14 +25,17 @@ public class BiomeData implements Serializable { public float width; public float height; public String color; + public boolean collision; public boolean invertHeight; public String[] spriteNames; - public List enemies; - public List pointsOfInterest; + public String[] enemies; + public String[] pointsOfInterest; + public BiomeStructureData[] structures; private ArrayList enemyList; private ArrayList pointOfInterestList; + private final Random rand = MyRandom.getRandom(); public Color GetColor() { return Color.valueOf(color); } @@ -45,8 +46,13 @@ public class BiomeData implements Serializable { if (enemies == null) return enemyList; for (EnemyData data : new Array.ArrayIterator<>(WorldData.getAllEnemies())) { - if (enemies.contains(data.name)) { - enemyList.add(data); + for (String enemyName:enemies) + { + if(data.name.equals(enemyName)) + { + enemyList.add(data); + break; + } } } } @@ -60,8 +66,13 @@ public class BiomeData implements Serializable { return pointOfInterestList; Array allTowns = PointOfInterestData.getAllPointOfInterest(); for (PointOfInterestData data : new Array.ArrayIterator<>(allTowns)) { - if (pointsOfInterest.contains(data.name)) { - pointOfInterestList.add(data); + for (String poiName:pointsOfInterest) + { + if(data.name.equals(poiName)) + { + pointOfInterestList.add(data); + break; + } } } } diff --git a/forge-gui-mobile/src/forge/adventure/data/BiomeStructureData.java b/forge-gui-mobile/src/forge/adventure/data/BiomeStructureData.java new file mode 100644 index 00000000000..cc2271953be --- /dev/null +++ b/forge-gui-mobile/src/forge/adventure/data/BiomeStructureData.java @@ -0,0 +1,63 @@ +package forge.adventure.data; + + +public class BiomeStructureData { + + + static public class BiomeStructureDataMapping + { + public int getColor() { + return ((Integer.parseInt(color,16)<<8)|0xff); + } + 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 boolean randomPosition; + + public String structureAtlasPath; + public String sourcePath; + public String maskPath; + public boolean periodicInput=true; + public float height; + public float width; + public int ground; + public int symmetry=2; + public boolean periodicOutput=true; + public BiomeStructureDataMapping[] mappingInfo; + + public BiomeStructureData( ) { + + } + public BiomeStructureData(BiomeStructureData biomeStructureData) { + this.structureAtlasPath=biomeStructureData.structureAtlasPath; + this.sourcePath=biomeStructureData.sourcePath; + this.maskPath=biomeStructureData.maskPath; + this.x=biomeStructureData.x; + this.y=biomeStructureData.y; + this.width=biomeStructureData.width; + this.height=biomeStructureData.height; + this.randomPosition=biomeStructureData.randomPosition; + if(biomeStructureData.mappingInfo!=null) + { + this.mappingInfo=new BiomeStructureDataMapping[ biomeStructureData.mappingInfo.length]; + for(int i=0;i restrictedCards; - public List restrictedEditions; + public String[] restrictedCards; + public String[] restrictedEditions; } diff --git a/forge-gui-mobile/src/forge/adventure/data/EnemyData.java b/forge-gui-mobile/src/forge/adventure/data/EnemyData.java index 7256a8e8a3f..b7321491e94 100644 --- a/forge-gui-mobile/src/forge/adventure/data/EnemyData.java +++ b/forge-gui-mobile/src/forge/adventure/data/EnemyData.java @@ -15,6 +15,7 @@ public class EnemyData { public boolean copyPlayerDeck = false; public String ai; public boolean boss = false; + public boolean flying = false; public float spawnRate; public float difficulty; public float speed; @@ -30,6 +31,7 @@ public class EnemyData { deck = enemyData.deck; ai = enemyData.ai; boss = enemyData.boss; + flying = enemyData.flying; spawnRate = enemyData.spawnRate; copyPlayerDeck = enemyData.copyPlayerDeck; difficulty = enemyData.difficulty; diff --git a/forge-gui-mobile/src/forge/adventure/data/PointOfInterestData.java b/forge-gui-mobile/src/forge/adventure/data/PointOfInterestData.java index 06e285a9d86..f2921e8d32a 100644 --- a/forge-gui-mobile/src/forge/adventure/data/PointOfInterestData.java +++ b/forge-gui-mobile/src/forge/adventure/data/PointOfInterestData.java @@ -42,4 +42,18 @@ public class PointOfInterestData { } return null; } + public PointOfInterestData() + { + + } + public PointOfInterestData(PointOfInterestData other) + { + name=other.name; + type=other.type; + count=other.count; + spriteAtlas=other.spriteAtlas; + sprite=other.sprite; + map=other.map; + radiusFactor=other.radiusFactor; + } } diff --git a/forge-gui-mobile/src/forge/adventure/data/RewardData.java b/forge-gui-mobile/src/forge/adventure/data/RewardData.java index a968e4c8d09..0e3a58d7315 100644 --- a/forge-gui-mobile/src/forge/adventure/data/RewardData.java +++ b/forge-gui-mobile/src/forge/adventure/data/RewardData.java @@ -13,6 +13,7 @@ import forge.item.PaperCard; import forge.model.FModel; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** @@ -75,8 +76,8 @@ public class RewardData { @Override public boolean apply(PaperCard input){ if(input == null) return false; - if(Config.instance().getConfigData().restrictedEditions.contains(input.getEdition())) return false; - return !Config.instance().getConfigData().restrictedCards.contains(input.getName()); + if(Arrays.asList(Config.instance().getConfigData().restrictedEditions).contains(input.getEdition())) return false; + return !Arrays.asList(Config.instance().getConfigData().restrictedCards).contains(input.getName()); } }); //Filter AI cards for enemies. diff --git a/forge-gui-mobile/src/forge/adventure/data/SettingData.java b/forge-gui-mobile/src/forge/adventure/data/SettingData.java index 018d3d42abd..f656a46d107 100644 --- a/forge-gui-mobile/src/forge/adventure/data/SettingData.java +++ b/forge-gui-mobile/src/forge/adventure/data/SettingData.java @@ -14,4 +14,8 @@ public class SettingData { public boolean fullScreen; public String videomode; public String lastActiveSave; + public Float rewardCardAdj; + public Float cardTooltipAdj; + public Float rewardCardAdjLandscape; + public Float cardTooltipAdjLandscape; } diff --git a/forge-gui-mobile/src/forge/adventure/data/UIData.java b/forge-gui-mobile/src/forge/adventure/data/UIData.java index 2a95ded452a..a9d528ea818 100644 --- a/forge-gui-mobile/src/forge/adventure/data/UIData.java +++ b/forge-gui-mobile/src/forge/adventure/data/UIData.java @@ -1,6 +1,5 @@ package forge.adventure.data; -import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.OrderedMap; /** @@ -12,5 +11,5 @@ public class UIData { public int width; public int height; public boolean yDown; - public Array> elements; + public OrderedMap[] elements; } diff --git a/forge-gui-mobile/src/forge/adventure/data/WorldData.java b/forge-gui-mobile/src/forge/adventure/data/WorldData.java index 908c857fdba..16ae480c029 100644 --- a/forge-gui-mobile/src/forge/adventure/data/WorldData.java +++ b/forge-gui-mobile/src/forge/adventure/data/WorldData.java @@ -3,6 +3,7 @@ package forge.adventure.data; import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Json; +import com.badlogic.gdx.utils.SerializationException; import forge.adventure.util.Config; import forge.adventure.util.Paths; import forge.adventure.world.BiomeSprites; @@ -17,19 +18,22 @@ import java.util.List; */ public class WorldData implements Serializable { - static Array allEnemies; public int width; public int height; public float playerStartPosX; public float playerStartPosY; public float noiseZoomBiome; public int tileSize; - public List biomesNames; public BiomeData roadTileset; public String biomesSprites; public float maxRoadDistance; + public String[] biomesNames; + + private BiomeSprites sprites; private List biomes; + + private static Array allEnemies; private static Array shopList; @@ -77,10 +81,16 @@ public class WorldData implements Serializable { public List 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/player/AdventurePlayer.java b/forge-gui-mobile/src/forge/adventure/player/AdventurePlayer.java index 57db089e307..3c1301ae7e5 100644 --- a/forge-gui-mobile/src/forge/adventure/player/AdventurePlayer.java +++ b/forge-gui-mobile/src/forge/adventure/player/AdventurePlayer.java @@ -5,7 +5,10 @@ import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Null; import com.google.common.collect.Lists; -import forge.adventure.data.*; +import forge.adventure.data.DifficultyData; +import forge.adventure.data.EffectData; +import forge.adventure.data.HeroListData; +import forge.adventure.data.ItemData; import forge.adventure.util.*; import forge.adventure.world.WorldSave; import forge.deck.CardPool; @@ -18,7 +21,10 @@ import forge.util.ItemPool; import forge.util.MyRandom; import java.io.Serializable; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; /** * Class that represents the player (not the player sprite) diff --git a/forge-gui-mobile/src/forge/adventure/scene/AdventureDeckEditor.java b/forge-gui-mobile/src/forge/adventure/scene/AdventureDeckEditor.java index cff35135c80..8362057f3e0 100644 --- a/forge-gui-mobile/src/forge/adventure/scene/AdventureDeckEditor.java +++ b/forge-gui-mobile/src/forge/adventure/scene/AdventureDeckEditor.java @@ -9,18 +9,10 @@ import forge.adventure.player.AdventurePlayer; import forge.assets.FImage; import forge.assets.FSkinFont; import forge.assets.FSkinImage; -import forge.deck.CardPool; -import forge.deck.Deck; -import forge.deck.DeckFormat; -import forge.deck.DeckSection; -import forge.deck.FDeckViewer; +import forge.deck.*; import forge.item.InventoryItem; import forge.item.PaperCard; -import forge.itemmanager.CardManager; -import forge.itemmanager.ColumnDef; -import forge.itemmanager.ItemColumn; -import forge.itemmanager.ItemManager; -import forge.itemmanager.ItemManagerConfig; +import forge.itemmanager.*; import forge.itemmanager.filters.ItemFilter; import forge.localinstance.properties.ForgePreferences; import forge.menu.FCheckBoxMenuItem; diff --git a/forge-gui-mobile/src/forge/adventure/scene/DeckSelectScene.java b/forge-gui-mobile/src/forge/adventure/scene/DeckSelectScene.java index 2cc444e966a..c92ee4d4e4a 100644 --- a/forge-gui-mobile/src/forge/adventure/scene/DeckSelectScene.java +++ b/forge-gui-mobile/src/forge/adventure/scene/DeckSelectScene.java @@ -3,12 +3,7 @@ package forge.adventure.scene; import com.badlogic.gdx.Input; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.scenes.scene2d.InputEvent; -import com.badlogic.gdx.scenes.scene2d.ui.Dialog; -import com.badlogic.gdx.scenes.scene2d.ui.Label; -import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane; -import com.badlogic.gdx.scenes.scene2d.ui.Table; -import com.badlogic.gdx.scenes.scene2d.ui.TextButton; -import com.badlogic.gdx.scenes.scene2d.ui.TextField; +import com.badlogic.gdx.scenes.scene2d.ui.*; import com.badlogic.gdx.scenes.scene2d.utils.ClickListener; import com.badlogic.gdx.utils.Align; import com.badlogic.gdx.utils.IntMap; diff --git a/forge-gui-mobile/src/forge/adventure/scene/NewGameScene.java b/forge-gui-mobile/src/forge/adventure/scene/NewGameScene.java index 74e37b2d57a..4730ced2e6a 100644 --- a/forge-gui-mobile/src/forge/adventure/scene/NewGameScene.java +++ b/forge-gui-mobile/src/forge/adventure/scene/NewGameScene.java @@ -4,13 +4,7 @@ import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Input; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.scenes.scene2d.Actor; -import com.badlogic.gdx.scenes.scene2d.ui.CheckBox; -import com.badlogic.gdx.scenes.scene2d.ui.Image; -import com.badlogic.gdx.scenes.scene2d.ui.ImageButton; -import com.badlogic.gdx.scenes.scene2d.ui.Label; -import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane; -import com.badlogic.gdx.scenes.scene2d.ui.TextButton; -import com.badlogic.gdx.scenes.scene2d.ui.TextField; +import com.badlogic.gdx.scenes.scene2d.ui.*; import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener; import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable; import com.badlogic.gdx.utils.Array; @@ -228,6 +222,20 @@ public class NewGameScene extends UIScene { public void enter() { updateAvatar(); Gdx.input.setInputProcessor(stage); //Start taking input from the ui + + if(Forge.createNewAdventureMap) + { + FModel.getPreferences().setPref(ForgePreferences.FPref.UI_ENABLE_MUSIC, false); + WorldSave.generateNewWorld(selectedName.getText(), + gender.getCurrentIndex() == 0, + race.getCurrentIndex(), + avatarIndex, + deck.getCurrentIndex(), + Config.instance().getConfigData().difficulties[difficulty.getCurrentIndex()], + fantasyMode, easyMode, deck.getText(), 0); + GamePlayerUtil.getGuiPlayer().setName(selectedName.getText()); + Forge.switchScene(SceneType.GameScene.instance); + } } @Override diff --git a/forge-gui-mobile/src/forge/adventure/scene/PlayerStatisticScene.java b/forge-gui-mobile/src/forge/adventure/scene/PlayerStatisticScene.java index 68be744d25f..05a8a1ee183 100644 --- a/forge-gui-mobile/src/forge/adventure/scene/PlayerStatisticScene.java +++ b/forge-gui-mobile/src/forge/adventure/scene/PlayerStatisticScene.java @@ -4,11 +4,7 @@ import com.badlogic.gdx.Input; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.g2d.TextureRegion; -import com.badlogic.gdx.scenes.scene2d.ui.Image; -import com.badlogic.gdx.scenes.scene2d.ui.Label; -import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane; -import com.badlogic.gdx.scenes.scene2d.ui.Table; -import com.badlogic.gdx.scenes.scene2d.ui.TextButton; +import com.badlogic.gdx.scenes.scene2d.ui.*; import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable; import com.badlogic.gdx.utils.Align; import forge.Forge; diff --git a/forge-gui-mobile/src/forge/adventure/scene/RewardScene.java b/forge-gui-mobile/src/forge/adventure/scene/RewardScene.java index eee8d441ce3..6e708079824 100644 --- a/forge-gui-mobile/src/forge/adventure/scene/RewardScene.java +++ b/forge-gui-mobile/src/forge/adventure/scene/RewardScene.java @@ -13,10 +13,7 @@ import forge.adventure.character.ShopActor; import forge.adventure.player.AdventurePlayer; import forge.adventure.pointofintrest.PointOfInterestChanges; import forge.adventure.stage.GameHUD; -import forge.adventure.util.CardUtil; -import forge.adventure.util.Current; -import forge.adventure.util.Reward; -import forge.adventure.util.RewardActor; +import forge.adventure.util.*; import forge.adventure.world.WorldSave; import forge.assets.ImageCache; import forge.sound.SoundEffectType; @@ -222,21 +219,28 @@ public class RewardScene extends UIScene { float mul = fW/fH < AR ? AR/(fW/fH) : (fW/fH)/AR; if (fW/fH >= 2f) {//tall display mul = (fW/fH) - ((fW/fH)/AR); - if ((fW/fH) >= 2.1f && (fW/fH) < 2.3f) + if ((fW/fH) >= 2.1f && (fW/fH) < 2.2f) mul *= 0.9f; - else if ((fW/fH) > 2.3) //ultrawide 21:9 Galaxy Fold, Huawei X2, Xperia 1 + else if ((fW/fH) > 2.2f) //ultrawide 21:9 Galaxy Fold, Huawei X2, Xperia 1 mul *= 0.8f; } cardHeight = bestCardHeight * 0.90f ; - if (realX > x || realY > y) { - mul *= Forge.isLandscapeMode() ? 0.95f : 1.05f; + Float custom = Forge.isLandscapeMode() ? Config.instance().getSettingData().rewardCardAdjLandscape : Config.instance().getSettingData().rewardCardAdj; + if (custom != null && custom != 1f) { + mul *= custom; } else { - //immersive | no navigation and/or showing cutout cam - if (fW/fH > 2.3f) - mul *= Forge.isLandscapeMode() ? 1.1f : 1.6f; - else if (fW/fH > 2f) - mul *= Forge.isLandscapeMode() ? 1.1f : 1.5f; + if (realX > x || realY > y) { + mul *= Forge.isLandscapeMode() ? 0.95f : 1.05f; + } else { + //immersive | no navigation and/or showing cutout cam + if (fW/fH > 2.2f) + mul *= Forge.isLandscapeMode() ? 1.1f : 1.6f; + else if (fW/fH >= 2.1f) + mul *= Forge.isLandscapeMode() ? 1.05f : 1.5f; + else if (fW/fH >= 2f) + mul *= Forge.isLandscapeMode() ? 1f : 1.4f; + } } cardWidth = (cardHeight / CARD_WIDTH_TO_HEIGHT)*mul; diff --git a/forge-gui-mobile/src/forge/adventure/scene/SettingsScene.java b/forge-gui-mobile/src/forge/adventure/scene/SettingsScene.java index 4f7dae601c9..35917ff8bf1 100644 --- a/forge-gui-mobile/src/forge/adventure/scene/SettingsScene.java +++ b/forge-gui-mobile/src/forge/adventure/scene/SettingsScene.java @@ -16,8 +16,6 @@ import forge.gui.GuiBase; import forge.localinstance.properties.ForgePreferences; import forge.model.FModel; -import java.util.function.Function; - /** * Scene to handle settings of the base forge and adventure mode */ @@ -118,12 +116,7 @@ public class SettingsScene extends UIScene { private void addSettingField(String name, int value, ChangeListener change) { TextField text = Controls.newTextField(String.valueOf(value)); - text.setTextFieldFilter(new TextField.TextFieldFilter() { - @Override - public boolean acceptChar(TextField textField, char c) { - return Character.isDigit(c); - } - }); + text.setTextFieldFilter((textField, c) -> Character.isDigit(c)); text.addListener(change); addLabel(name); settingGroup.add(text).align(Align.right); @@ -145,49 +138,90 @@ public class SettingsScene extends UIScene { Preference = new ForgePreferences(); } - SelectBox plane = Controls.newComboBox(Config.instance().getAllAdventures(), Config.instance().getSettingData().plane, new Function() { - @Override - public Void apply(Object o) { - Config.instance().getSettingData().plane = (String) o; - Config.instance().saveSettings(); - return null; - } + SelectBox plane = Controls.newComboBox(Config.instance().getAllAdventures(), Config.instance().getSettingData().plane, o -> { + Config.instance().getSettingData().plane = (String) o; + Config.instance().saveSettings(); + return null; }); addLabel(Forge.getLocalizer().getMessage("lblWorld")); settingGroup.add(plane).align(Align.right).pad(2); if (!GuiBase.isAndroid()) { - SelectBox videomode = Controls.newComboBox(new String[]{"720p", "768p", "900p", "1080p"}, Config.instance().getSettingData().videomode, new Function() { - @Override - public Void apply(Object o) { - String mode = (String) o; - if (mode == null) - mode = "720p"; - Config.instance().getSettingData().videomode = mode; - if (mode.equalsIgnoreCase("768p")) { - Config.instance().getSettingData().width = 1366; - Config.instance().getSettingData().height = 768; - } else if (mode.equalsIgnoreCase("900p")) { - Config.instance().getSettingData().width = 1600; - Config.instance().getSettingData().height = 900; - } else if (mode.equalsIgnoreCase("1080p")) { - Config.instance().getSettingData().width = 1920; - Config.instance().getSettingData().height = 1080; - } else { - Config.instance().getSettingData().width = 1280; - Config.instance().getSettingData().height = 720; - } - Config.instance().saveSettings(); - //update preference for classic mode if needed - if (FModel.getPreferences().getPref(ForgePreferences.FPref.UI_VIDEO_MODE) != mode) { - FModel.getPreferences().setPref(ForgePreferences.FPref.UI_VIDEO_MODE, mode); - FModel.getPreferences().save(); - } - return null; + SelectBox videomode = Controls.newComboBox(new String[]{"720p", "768p", "900p", "1080p"}, Config.instance().getSettingData().videomode, o -> { + String mode = (String) o; + if (mode == null) + mode = "720p"; + Config.instance().getSettingData().videomode = mode; + if (mode.equalsIgnoreCase("768p")) { + Config.instance().getSettingData().width = 1366; + Config.instance().getSettingData().height = 768; + } else if (mode.equalsIgnoreCase("900p")) { + Config.instance().getSettingData().width = 1600; + Config.instance().getSettingData().height = 900; + } else if (mode.equalsIgnoreCase("1080p")) { + Config.instance().getSettingData().width = 1920; + Config.instance().getSettingData().height = 1080; + } else { + Config.instance().getSettingData().width = 1280; + Config.instance().getSettingData().height = 720; } + Config.instance().saveSettings(); + //update preference for classic mode if needed + if (FModel.getPreferences().getPref(ForgePreferences.FPref.UI_VIDEO_MODE) != mode) { + FModel.getPreferences().setPref(ForgePreferences.FPref.UI_VIDEO_MODE, mode); + FModel.getPreferences().save(); + } + return null; }); addLabel(Forge.getLocalizer().getMessage("lblVideoMode")); settingGroup.add(videomode).align(Align.right).pad(2); + } + if (Forge.isLandscapeMode()) { + //different adjustment to landscape + SelectBox rewardCardAdjLandscape = Controls.newComboBox(new Float[]{0.6f, 0.65f, 0.7f, 0.75f, 0.8f, 0.85f, 0.9f, 1f, 1.05f, 1.1f, 1.15f, 1.2f, 1.25f, 1.3f, 1.35f, 1.4f, 1.45f, 1.5f, 1.55f, 1.6f}, Config.instance().getSettingData().rewardCardAdjLandscape, o -> { + Float val = (Float) o; + if (val == null || val == 0f) + val = 1f; + Config.instance().getSettingData().rewardCardAdjLandscape = val; + Config.instance().saveSettings(); + return null; + }); + addLabel("Reward/Shop Card Display Ratio"); + settingGroup.add(rewardCardAdjLandscape).align(Align.right).pad(2); + SelectBox tooltipAdjLandscape = Controls.newComboBox(new Float[]{0.6f, 0.65f, 0.7f, 0.75f, 0.8f, 0.85f, 0.9f, 1f, 1.05f, 1.1f, 1.15f, 1.2f, 1.25f, 1.3f, 1.35f, 1.4f, 1.45f, 1.5f, 1.55f, 1.6f}, Config.instance().getSettingData().cardTooltipAdjLandscape, o -> { + Float val = (Float) o; + if (val == null || val == 0f) + val = 1f; + Config.instance().getSettingData().cardTooltipAdjLandscape = val; + Config.instance().saveSettings(); + return null; + }); + addLabel("Reward/Shop Card Tooltip Ratio"); + settingGroup.add(tooltipAdjLandscape).align(Align.right).pad(2); + } else { + //portrait adjustment + SelectBox rewardCardAdj = Controls.newComboBox(new Float[]{0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1f, 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f, 1.8f, 1.9f, 2f}, Config.instance().getSettingData().rewardCardAdj, o -> { + Float val = (Float) o; + if (val == null || val == 0f) + val = 1f; + Config.instance().getSettingData().rewardCardAdj = val; + Config.instance().saveSettings(); + return null; + }); + addLabel("Reward/Shop Card Display Ratio"); + settingGroup.add(rewardCardAdj).align(Align.right).pad(2); + SelectBox tooltipAdj = Controls.newComboBox(new Float[]{0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1f, 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f, 1.8f, 1.9f, 2f}, Config.instance().getSettingData().cardTooltipAdj, o -> { + Float val = (Float) o; + if (val == null || val == 0f) + val = 1f; + Config.instance().getSettingData().cardTooltipAdj = val; + Config.instance().saveSettings(); + return null; + }); + addLabel("Reward/Shop Card Tooltip Ratio"); + settingGroup.add(tooltipAdj).align(Align.right).pad(2); + } + if (!GuiBase.isAndroid()) { addSettingField(Forge.getLocalizer().getMessage("lblFullScreen"), Config.instance().getSettingData().fullScreen, new ChangeListener() { @Override public void changed(ChangeEvent event, Actor actor) { @@ -231,12 +265,7 @@ public class SettingsScene extends UIScene { settingGroup.row(); back = ui.findActor("return"); back.getLabel().setText(Forge.getLocalizer().getMessage("lblBack")); - ui.onButtonPress("return", new Runnable() { - @Override - public void run() { - SettingsScene.this.back(); - } - }); + ui.onButtonPress("return", () -> SettingsScene.this.back()); ScrollPane scrollPane = ui.findActor("settings"); scrollPane.setActor(settingGroup); diff --git a/forge-gui-mobile/src/forge/adventure/scene/SpellSmithScene.java b/forge-gui-mobile/src/forge/adventure/scene/SpellSmithScene.java index d56b2c8a3d5..c70a84a3db3 100644 --- a/forge-gui-mobile/src/forge/adventure/scene/SpellSmithScene.java +++ b/forge-gui-mobile/src/forge/adventure/scene/SpellSmithScene.java @@ -3,7 +3,10 @@ package forge.adventure.scene; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.scenes.scene2d.Actor; import com.badlogic.gdx.scenes.scene2d.InputEvent; -import com.badlogic.gdx.scenes.scene2d.ui.*; +import com.badlogic.gdx.scenes.scene2d.ui.Label; +import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane; +import com.badlogic.gdx.scenes.scene2d.ui.SelectBox; +import com.badlogic.gdx.scenes.scene2d.ui.TextButton; import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener; import com.badlogic.gdx.scenes.scene2d.utils.ClickListener; import forge.Forge; @@ -18,10 +21,7 @@ import forge.card.ColorSet; import forge.item.PaperCard; import forge.util.MyRandom; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.stream.Collectors; import java.util.stream.StreamSupport; @@ -142,7 +142,7 @@ public class SpellSmithScene extends UIScene { List editions = StaticData.instance().getSortedEditions(); editions = editions.stream().filter(input -> { if(input == null) return false; - return(!Config.instance().getConfigData().restrictedEditions.contains(input.getCode())); + return(!Arrays.asList(Config.instance().getConfigData().restrictedEditions).contains(input.getCode())); }).collect(Collectors.toList()); editionList = ui.findActor("BSelectPlane"); rewardDummy = ui.findActor("RewardDummy"); diff --git a/forge-gui-mobile/src/forge/adventure/scene/StartScene.java b/forge-gui-mobile/src/forge/adventure/scene/StartScene.java index 7157a93e541..238af360f14 100644 --- a/forge-gui-mobile/src/forge/adventure/scene/StartScene.java +++ b/forge-gui-mobile/src/forge/adventure/scene/StartScene.java @@ -7,9 +7,11 @@ import com.badlogic.gdx.scenes.scene2d.ui.TextButton; import com.badlogic.gdx.utils.Align; import forge.Forge; import forge.adventure.stage.GameHUD; +import forge.adventure.stage.GameStage; import forge.adventure.stage.MapStage; import forge.adventure.util.Config; import forge.adventure.util.Controls; +import forge.adventure.util.Current; import forge.adventure.world.WorldSave; import forge.screens.TransitionScreen; @@ -104,6 +106,13 @@ public class StartScene extends UIScene { } Gdx.input.setInputProcessor(stage); //Start taking input from the ui + + if(Forge.createNewAdventureMap) + { + this.NewGame(); + Current.setDebug(true); + GameStage.maximumScrollDistance=4f; + } } @Override diff --git a/forge-gui-mobile/src/forge/adventure/stage/GameHUD.java b/forge-gui-mobile/src/forge/adventure/stage/GameHUD.java index 537f3d755c3..59f94e605fb 100644 --- a/forge-gui-mobile/src/forge/adventure/stage/GameHUD.java +++ b/forge-gui-mobile/src/forge/adventure/stage/GameHUD.java @@ -3,19 +3,18 @@ package forge.adventure.stage; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Input; import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.graphics.Pixmap; import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.scenes.scene2d.Actor; import com.badlogic.gdx.scenes.scene2d.InputEvent; import com.badlogic.gdx.scenes.scene2d.Stage; import com.badlogic.gdx.scenes.scene2d.actions.Actions; -import com.badlogic.gdx.scenes.scene2d.ui.Image; -import com.badlogic.gdx.scenes.scene2d.ui.Label; -import com.badlogic.gdx.scenes.scene2d.ui.TextButton; -import com.badlogic.gdx.scenes.scene2d.ui.Touchpad; +import com.badlogic.gdx.scenes.scene2d.ui.*; import com.badlogic.gdx.scenes.scene2d.utils.ActorGestureListener; import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener; import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable; +import com.badlogic.gdx.utils.Align; import com.badlogic.gdx.utils.Scaling; import com.badlogic.gdx.utils.viewport.ScalingViewport; import forge.Forge; @@ -42,7 +41,8 @@ public class GameHUD extends Stage { private final Image miniMapPlayer; private final Label lifePoints; private final Label money; - private final Image miniMap, gamehud, mapborder, avatarborder, blank; + private final Image miniMap,miniMapTooltip, gamehud, mapborder, avatarborder, blank; + private Tooltip toolTip; private TextButton deckActor, menuActor, statsActor, inventoryActor; private UIActor ui; private Touchpad touchpad; @@ -63,6 +63,15 @@ public class GameHUD extends Stage { blank = ui.findActor("blank"); miniMap = ui.findActor("map"); mapborder = ui.findActor("mapborder"); + + miniMapTooltip=new Image(); + miniMapTooltip.setScaling(Scaling.contain); + miniMapTooltip.setSize(miniMap.getWidth()*3,miniMap.getHeight()*3); + miniMapTooltip.setPosition(0,0,Align.topLeft); + ui.addActor(miniMapTooltip); + toolTip=new Tooltip(miniMapTooltip); + toolTip.setInstant(true); + mapborder.addListener(toolTip); avatarborder = ui.findActor("avatarborder"); deckActor = ui.findActor("deck"); deckActor.getLabel().setText(Forge.getLocalizer().getMessage("lblDeck")); @@ -167,6 +176,7 @@ public class GameHUD extends Stage { return true; if(Current.isInDebug()) WorldStage.getInstance().GetPlayer().setPosition(x*WorldSave.getCurrentSave().getWorld().getWidthInPixels(),y*WorldSave.getCurrentSave().getWorld().getHeightInPixels()); + return true; } return super.touchDragged(screenX, screenY, pointer); @@ -230,12 +240,21 @@ public class GameHUD extends Stage { } Texture miniMapTexture; + Texture miniMapToolTipTexture; + Pixmap miniMapToolTipPixmap; public void enter() { if(miniMapTexture!=null) miniMapTexture.dispose(); miniMapTexture=new Texture(WorldSave.getCurrentSave().getWorld().getBiomeImage()); - + if(miniMapToolTipTexture!=null) + miniMapToolTipTexture.dispose(); + if(miniMapToolTipPixmap!=null) + miniMapToolTipPixmap.dispose(); + miniMapToolTipPixmap=new Pixmap((int) (miniMap.getWidth()*3), (int) (miniMap.getHeight()*3), Pixmap.Format.RGBA8888); + miniMapToolTipPixmap.drawPixmap(WorldSave.getCurrentSave().getWorld().getBiomeImage(),0,0,WorldSave.getCurrentSave().getWorld().getBiomeImage().getWidth(),WorldSave.getCurrentSave().getWorld().getBiomeImage().getHeight(),0,0,miniMapToolTipPixmap.getWidth(),miniMapToolTipPixmap.getHeight()); + miniMapToolTipTexture=new Texture(miniMapToolTipPixmap); miniMap.setDrawable(new TextureRegionDrawable(miniMapTexture)); + miniMapTooltip.setDrawable(new TextureRegionDrawable(miniMapToolTipTexture)); avatar.setDrawable(new TextureRegionDrawable(Current.player().avatar())); Deck deck = AdventurePlayer.current().getSelectedDeck(); if (deck == null || deck.isEmpty() || deck.getMain().toFlatList().size() < 30) { diff --git a/forge-gui-mobile/src/forge/adventure/stage/GameStage.java b/forge-gui-mobile/src/forge/adventure/stage/GameStage.java index d904c379224..daa2b3370f6 100644 --- a/forge-gui-mobile/src/forge/adventure/stage/GameStage.java +++ b/forge-gui-mobile/src/forge/adventure/stage/GameStage.java @@ -36,6 +36,8 @@ public abstract class GameStage extends Stage { private float touchY = -1; private final float timer = 0; private float animationTimeout = 0; + public static float maximumScrollDistance=1.5f; + public static float minimumScrollDistance=0.3f; public void startPause(float i) { startPause(i, null); @@ -222,10 +224,10 @@ public abstract class GameStage extends Stage { if (isPaused()) return true; camera.zoom += (amountY * 0.03); - if (camera.zoom < 0.3f) - camera.zoom = 0.3f; - if (camera.zoom > 1.5f) - camera.zoom = 1.5f; + if (camera.zoom < minimumScrollDistance) + camera.zoom = minimumScrollDistance; + if (camera.zoom > maximumScrollDistance) + camera.zoom = maximumScrollDistance; return super.scrolled(amountX, amountY); } diff --git a/forge-gui-mobile/src/forge/adventure/stage/MapStage.java b/forge-gui-mobile/src/forge/adventure/stage/MapStage.java index 1dc759ef348..4c84835fc91 100644 --- a/forge-gui-mobile/src/forge/adventure/stage/MapStage.java +++ b/forge-gui-mobile/src/forge/adventure/stage/MapStage.java @@ -41,7 +41,6 @@ import forge.screens.TransitionScreen; import forge.sound.SoundEffectType; import forge.sound.SoundSystem; - import java.util.HashMap; import java.util.Map; diff --git a/forge-gui-mobile/src/forge/adventure/stage/WorldStage.java b/forge-gui-mobile/src/forge/adventure/stage/WorldStage.java index c7e14a7f550..6c07f0c094b 100644 --- a/forge-gui-mobile/src/forge/adventure/stage/WorldStage.java +++ b/forge-gui-mobile/src/forge/adventure/stage/WorldStage.java @@ -11,11 +11,7 @@ import forge.adventure.character.EnemySprite; import forge.adventure.data.BiomeData; import forge.adventure.data.EnemyData; import forge.adventure.data.WorldData; -import forge.adventure.scene.DuelScene; -import forge.adventure.scene.RewardScene; -import forge.adventure.scene.Scene; -import forge.adventure.scene.SceneType; -import forge.adventure.scene.TileMapScene; +import forge.adventure.scene.*; import forge.adventure.util.Current; import forge.adventure.util.SaveFileContent; import forge.adventure.util.SaveFileData; @@ -62,6 +58,8 @@ public class WorldStage extends GameStage implements SaveFileContent { } + final Rectangle tempBoundingRect=new Rectangle(); + final Vector2 enemyMoveVector =new Vector2(); @Override protected void onActing(float delta) { if (player.isMoving()) { @@ -79,7 +77,33 @@ public class WorldStage extends GameStage implements SaveFileContent { continue; } EnemySprite mob=pair.getValue(); - mob.moveTo(player,delta); + + enemyMoveVector.set(player.getX(), player.getY()).sub(mob.pos()); + enemyMoveVector.setLength(mob.speed()*delta); + tempBoundingRect.set(mob.getX()+ enemyMoveVector.x,mob.getY()+ enemyMoveVector.y,mob.getWidth(),mob.getHeight()*mob.getCollisionHeight()); + + if(!mob.getData().flying && WorldSave.getCurrentSave().getWorld().collidingTile(tempBoundingRect))//if direct path is not possible + { + tempBoundingRect.set(mob.getX()+ enemyMoveVector.x,mob.getY(),mob.getWidth(),mob.getHeight()); + if(WorldSave.getCurrentSave().getWorld().collidingTile(tempBoundingRect))//if only x path is not possible + { + tempBoundingRect.set(mob.getX(),mob.getY()+ enemyMoveVector.y,mob.getWidth(),mob.getHeight()); + if(!WorldSave.getCurrentSave().getWorld().collidingTile(tempBoundingRect))//if y path is possible + { + mob.moveBy(0, enemyMoveVector.y); + } + } + else + { + + mob.moveBy(enemyMoveVector.x, 0); + } + } + else + { + mob.moveBy(enemyMoveVector.x, enemyMoveVector.y); + } + if (player.collideWith(mob)) { player.setAnimation(CharacterSprite.AnimationTypes.Attack); mob.setAnimation(CharacterSprite.AnimationTypes.Attack); @@ -176,19 +200,7 @@ public class WorldStage extends GameStage implements SaveFileContent { public boolean isColliding(Rectangle boundingRect) { - World world = WorldSave.getCurrentSave().getWorld(); - int currentBiome = World.highestBiome(world.getBiome((int) boundingRect.getX() / world.getTileSize(), (int) boundingRect.getY() / world.getTileSize())); - if(currentBiome==0) - return true; - currentBiome = World.highestBiome(world.getBiome((int) (boundingRect.getX()+boundingRect.getWidth()) / world.getTileSize(), (int) boundingRect.getY() / world.getTileSize())); - if(currentBiome==0) - return true; - currentBiome = World.highestBiome(world.getBiome((int) (boundingRect.getX()+boundingRect.getWidth())/ world.getTileSize(), (int) (boundingRect.getY()+boundingRect.getHeight()) / world.getTileSize())); - if(currentBiome==0) - return true; - currentBiome = World.highestBiome(world.getBiome((int) boundingRect.getX() / world.getTileSize(), (int) (boundingRect.getY()+boundingRect.getHeight()) / world.getTileSize())); - - return (currentBiome==0); + return WorldSave.getCurrentSave().getWorld().collidingTile(boundingRect); } private void HandleMonsterSpawn(float delta) { @@ -216,12 +228,25 @@ public class WorldStage extends GameStage implements SaveFileContent { EnemySprite sprite = new EnemySprite(enemyData); float unit = Scene.getIntendedHeight() / 6f; Vector2 spawnPos = new Vector2(1, 1); - spawnPos.setLength(unit + (unit * 3) * rand.nextFloat()); - spawnPos.setAngleDeg(360 * rand.nextFloat()); - sprite.setX(player.getX() + spawnPos.x); - sprite.setY(player.getY() + spawnPos.y); - enemies.add(Pair.of(globalTimer,sprite)); - foregroundSprites.addActor(sprite); + for(int j=0;j<10;j++) + { + spawnPos.setLength(unit + (unit * 3) * rand.nextFloat()); + spawnPos.setAngleDeg(360 * rand.nextFloat()); + for(int i=0;i<10;i++) + { + boolean enemyXIsBigger=sprite.getX()>player.getX(); + boolean enemyYIsBigger=sprite.getY()>player.getY(); + sprite.setX(player.getX() + spawnPos.x+(i*sprite.getWidth()*(enemyXIsBigger?1:-1)));//maybe find a better way to get spawn points + sprite.setY(player.getY() + spawnPos.y+(i*sprite.getHeight()*(enemyYIsBigger?1:-1))); + if(sprite.getData().flying || !WorldSave.getCurrentSave().getWorld().collidingTile(sprite.boundingRect())) + { + enemies.add(Pair.of(globalTimer,sprite)); + foregroundSprites.addActor(sprite); + return; + } + int g=0; + } + } } @Override diff --git a/forge-gui-mobile/src/forge/adventure/util/Config.java b/forge-gui-mobile/src/forge/adventure/util/Config.java index 9a2b7aa702d..eabc723e176 100644 --- a/forge-gui-mobile/src/forge/adventure/util/Config.java +++ b/forge-gui-mobile/src/forge/adventure/util/Config.java @@ -64,6 +64,18 @@ public class Config { } if(settingsData.videomode == null || settingsData.videomode.isEmpty()) settingsData.videomode="720p"; + //reward card display fine tune + if(settingsData.rewardCardAdj == null || settingsData.rewardCardAdj == 0f) + settingsData.rewardCardAdj=1f; + //tooltip fine tune + if(settingsData.cardTooltipAdj == null || settingsData.cardTooltipAdj == 0f) + settingsData.cardTooltipAdj=1f; + //reward card display fine tune landscape + if(settingsData.rewardCardAdjLandscape == null || settingsData.rewardCardAdjLandscape == 0f) + settingsData.rewardCardAdjLandscape=1f; + //tooltip fine tune landscape + if(settingsData.cardTooltipAdjLandscape == null || settingsData.cardTooltipAdjLandscape == 0f) + settingsData.cardTooltipAdjLandscape=1f; this.plane = settingsData.plane; currentConfig = this; diff --git a/forge-gui-mobile/src/forge/adventure/util/Controls.java b/forge-gui-mobile/src/forge/adventure/util/Controls.java index 88e5789fe86..6321f016c9e 100644 --- a/forge-gui-mobile/src/forge/adventure/util/Controls.java +++ b/forge-gui-mobile/src/forge/adventure/util/Controls.java @@ -52,6 +52,27 @@ public class Controls { return ret; } + static public SelectBox newComboBox(Float[] text, float item, Function func) { + SelectBox ret = new SelectBox(GetSkin()); + ret.getStyle().listStyle.selection.setTopHeight(4); + ret.setItems(text); + ret.addListener(new ChangeListener() { + @Override + public void changed(ChangeEvent event, Actor actor) { + try { + func.apply(((SelectBox) actor).getSelected()); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + func.apply(item); + ret.getList().setAlignment(Align.center); + ret.setSelected(item); + ret.setAlignment(Align.right); + return ret; + } + static public TextField newTextField(String text) { return new TextField(text, GetSkin()); } diff --git a/forge-gui-mobile/src/forge/adventure/util/DrawOnPixmap.java b/forge-gui-mobile/src/forge/adventure/util/DrawOnPixmap.java index dbc61e207eb..cd813bb9ba7 100644 --- a/forge-gui-mobile/src/forge/adventure/util/DrawOnPixmap.java +++ b/forge-gui-mobile/src/forge/adventure/util/DrawOnPixmap.java @@ -54,6 +54,8 @@ public abstract class DrawOnPixmap { frameBuffer.end(); texture.dispose(); batch.dispose(); + if (bigText) //don't know why this is needed to circumvent bug getting default size for the same pixelfont + Controls.getBitmapFont("default"); } } diff --git a/forge-gui-mobile/src/forge/adventure/util/MapDialog.java b/forge-gui-mobile/src/forge/adventure/util/MapDialog.java index f86b7fee73e..28d987171e2 100644 --- a/forge-gui-mobile/src/forge/adventure/util/MapDialog.java +++ b/forge-gui-mobile/src/forge/adventure/util/MapDialog.java @@ -1,6 +1,8 @@ package forge.adventure.util; -import com.badlogic.gdx.scenes.scene2d.ui.*; +import com.badlogic.gdx.scenes.scene2d.ui.Dialog; +import com.badlogic.gdx.scenes.scene2d.ui.Label; +import com.badlogic.gdx.scenes.scene2d.ui.TextButton; import com.badlogic.gdx.utils.Array; import forge.Forge; import forge.adventure.character.EnemySprite; diff --git a/forge-gui-mobile/src/forge/adventure/util/RewardActor.java b/forge-gui-mobile/src/forge/adventure/util/RewardActor.java index e19d9aa93ee..3871c6bef05 100644 --- a/forge-gui-mobile/src/forge/adventure/util/RewardActor.java +++ b/forge-gui-mobile/src/forge/adventure/util/RewardActor.java @@ -146,15 +146,20 @@ public class RewardActor extends Actor implements Disposable, ImageFetcher.Callb PaperCard card = ImageUtil.getPaperCardFromImageKey(reward.getCard().getImageKey(false)); File frontFace = ImageKeys.getImageFile(card.getCardImageKey()); if (frontFace != null) { - if (!Forge.getAssets().manager().contains(frontFace.getPath())) { - Forge.getAssets().manager().load(frontFace.getPath(), Texture.class, Forge.getAssets().getTextureFilter()); - Forge.getAssets().manager().finishLoadingAsset(frontFace.getPath()); - count+=1; - } - Texture front = Forge.getAssets().manager().get(frontFace.getPath(), Texture.class, false); - if (front != null) { - setCardImage(front); - } else { + try { + if (!Forge.getAssets().manager().contains(frontFace.getPath())) { + Forge.getAssets().manager().load(frontFace.getPath(), Texture.class, Forge.getAssets().getTextureFilter()); + Forge.getAssets().manager().finishLoadingAsset(frontFace.getPath()); + count+=1; + } + Texture front = Forge.getAssets().manager().get(frontFace.getPath(), Texture.class, false); + if (front != null) { + setCardImage(front); + } else { + loaded = false; + } + } catch (Exception e) { + System.err.println("Failed to load image: "+frontFace.getPath()); loaded = false; } } else { @@ -166,10 +171,14 @@ public class RewardActor extends Actor implements Disposable, ImageFetcher.Callb PaperCard cardBack = ImageUtil.getPaperCardFromImageKey(reward.getCard().getImageKey(true)); File backFace = ImageKeys.getImageFile(cardBack.getCardAltImageKey()); if (backFace != null) { - if (!Forge.getAssets().manager().contains(backFace.getPath())) { - Forge.getAssets().manager().load(backFace.getPath(), Texture.class, Forge.getAssets().getTextureFilter()); - Forge.getAssets().manager().finishLoadingAsset(backFace.getPath()); - ImageCache.updateSynqCount(backFace, 1); + try { + if (!Forge.getAssets().manager().contains(backFace.getPath())) { + Forge.getAssets().manager().load(backFace.getPath(), Texture.class, Forge.getAssets().getTextureFilter()); + Forge.getAssets().manager().finishLoadingAsset(backFace.getPath()); + ImageCache.updateSynqCount(backFace, 1); + } + } catch (Exception e) { + System.err.println("Failed to load image: "+backFace.getPath()); } } } @@ -178,18 +187,23 @@ public class RewardActor extends Actor implements Disposable, ImageFetcher.Callb File lookup = ImageKeys.hasSetLookup(imagePath) ? ImageKeys.setLookUpFile(imagePath, imagePath+"border") : null; int count = 0; if (lookup != null) { - if (!Forge.getAssets().manager().contains(lookup.getPath())) { - Forge.getAssets().manager().load(lookup.getPath(), Texture.class, Forge.getAssets().getTextureFilter()); - Forge.getAssets().manager().finishLoadingAsset(lookup.getPath()); - count+=1; - } - Texture replacement = Forge.getAssets().manager().get(lookup.getPath(), Texture.class, false); - if (replacement != null) { - setCardImage(replacement); - } else { + try { + if (!Forge.getAssets().manager().contains(lookup.getPath())) { + Forge.getAssets().manager().load(lookup.getPath(), Texture.class, Forge.getAssets().getTextureFilter()); + Forge.getAssets().manager().finishLoadingAsset(lookup.getPath()); + count += 1; + } + Texture replacement = Forge.getAssets().manager().get(lookup.getPath(), Texture.class, false); + if (replacement != null) { + setCardImage(replacement); + } else { + loaded = false; + } + ImageCache.updateSynqCount(lookup, count); + } catch (Exception e) { + System.err.println("Failed to load image: "+lookup.getPath()); loaded = false; } - ImageCache.updateSynqCount(lookup, count); } else if (!ImageCache.imageKeyFileExists(reward.getCard().getImageKey(false))) { //Cannot find an image file, set up a rendered card until (if) a file is downloaded. T = renderPlaceholder(getGraphics(), reward.getCard()); //Now we can render the card. @@ -361,12 +375,17 @@ public class RewardActor extends Actor implements Disposable, ImageFetcher.Callb float fW = x > y ? x : y; float fH = x > y ? y : x; float mul = fW/fH < AR ? AR/(fW/fH) : (fW/fH)/AR; - if (fW/fH >= 2f) {//tall display - mul = (fW/fH) - ((fW/fH)/AR); - if ((fW/fH) >= 2.1f && (fW/fH) < 2.3f) - mul *= 0.9f; - else if ((fW/fH) > 2.3) //ultrawide 21:9 Galaxy Fold, Huawei X2, Xperia 1 - mul *= 0.8f; + Float custom = Forge.isLandscapeMode() ? Config.instance().getSettingData().cardTooltipAdjLandscape : Config.instance().getSettingData().cardTooltipAdj; + if (custom != null && custom != 1f) { + mul *= custom; + } else { + if (fW/fH >= 2f) {//tall display + mul = (fW/fH) - ((fW/fH)/AR); + if ((fW/fH) >= 2.1f && (fW/fH) < 2.2f) + mul *= 0.9f; + else if ((fW/fH) > 2.2f) //ultrawide 21:9 Galaxy Fold, Huawei X2, Xperia 1 + mul *= 0.8f; + } } if (Forge.isLandscapeMode()) drawable.setMinSize(newW*mul, newH); diff --git a/forge-gui-mobile/src/forge/adventure/util/TemplateTmxMapLoader.java b/forge-gui-mobile/src/forge/adventure/util/TemplateTmxMapLoader.java index e98c42eb5cb..c0ee5ab05c5 100644 --- a/forge-gui-mobile/src/forge/adventure/util/TemplateTmxMapLoader.java +++ b/forge-gui-mobile/src/forge/adventure/util/TemplateTmxMapLoader.java @@ -10,11 +10,7 @@ import com.badlogic.gdx.maps.tiled.TiledMapTile; import com.badlogic.gdx.maps.tiled.TiledMapTileSet; import com.badlogic.gdx.maps.tiled.TmxMapLoader; import com.badlogic.gdx.maps.tiled.tiles.AnimatedTiledMapTile; -import com.badlogic.gdx.utils.Array; -import com.badlogic.gdx.utils.GdxRuntimeException; -import com.badlogic.gdx.utils.ObjectMap; -import com.badlogic.gdx.utils.SerializationException; -import com.badlogic.gdx.utils.XmlReader; +import com.badlogic.gdx.utils.*; import forge.Forge; import java.io.File; diff --git a/forge-gui-mobile/src/forge/adventure/util/UIActor.java b/forge-gui-mobile/src/forge/adventure/util/UIActor.java index 96556330bac..840fa8e4661 100644 --- a/forge-gui-mobile/src/forge/adventure/util/UIActor.java +++ b/forge-gui-mobile/src/forge/adventure/util/UIActor.java @@ -10,7 +10,6 @@ import com.badlogic.gdx.scenes.scene2d.InputEvent; import com.badlogic.gdx.scenes.scene2d.ui.*; import com.badlogic.gdx.scenes.scene2d.utils.ClickListener; import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable; -import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Json; import com.badlogic.gdx.utils.ObjectMap; import com.badlogic.gdx.utils.OrderedMap; @@ -29,7 +28,7 @@ public class UIActor extends Group { setWidth(data.width); setHeight(data.height); - for (OrderedMap element : new Array.ArrayIterator<>(data.elements)) { + for (OrderedMap element : data.elements) { String type = element.get("type"); Actor newActor; if (type == null) { diff --git a/forge-gui-mobile/src/forge/adventure/world/BiomeStructure.java b/forge-gui-mobile/src/forge/adventure/world/BiomeStructure.java new file mode 100644 index 00000000000..00c9760ea80 --- /dev/null +++ b/forge-gui-mobile/src/forge/adventure/world/BiomeStructure.java @@ -0,0 +1,154 @@ +package forge.adventure.world; + +import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.graphics.g2d.TextureAtlas; +import forge.adventure.data.BiomeStructureData; +import forge.adventure.util.Config; + +import java.util.HashMap; + +public class BiomeStructure { + + private BiomeStructureData data; + long seed; + private int biomeWidth; + private int biomeHeight; + private int dataMap[][]; + private boolean collisionMap[][]; + boolean init=false; + private TextureAtlas structureAtlas; + public ColorMap image; + private final static int MAXIMUM_WAVEFUNCTIONSIZE=50; + + public BiomeStructure(BiomeStructureData data,long seed,int width,int height) + { + this.data=data; + this.seed=seed; + this.biomeWidth = width; + this.biomeHeight = height; + } + public TextureAtlas atlas() { + if(structureAtlas==null) + { + try + { + structureAtlas = Config.instance().getAtlas(data.structureAtlasPath); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + return structureAtlas; + } + public int structureObjectCount() { + return data.mappingInfo.length; + } + + public int objectID(int x, int y) { + + if(!init) + { + initialize(); + } + if(x>=dataMap.length||x<0||y<0||y>=dataMap[0].length) + return -1; + return dataMap[x][y]; + } + + public void initialize(ColorMap sourceImage,ColorMap maskImage) { + long currentTime = System.currentTimeMillis(); + + init=true; + int targetWidth=(int) (data.width* biomeWidth); + int targetHeight=(int) (data.width* biomeWidth); + dataMap=new int[targetWidth][ targetHeight]; + collisionMap=new boolean[targetWidth][ targetHeight]; + ColorMap finalImage=new ColorMap(targetWidth, targetHeight); + HashMap colorIdMap=new HashMap<>(); + for(int i=0;i=collisionMap.length||x<0||y<0||y>=collisionMap[0].length) + return false; + return collisionMap[x][y]; + } + + public String maskImagePath() { + return (Config.instance().getFilePath(data.maskPath)); + } +} diff --git a/forge-gui-mobile/src/forge/adventure/world/BiomeTexture.java b/forge-gui-mobile/src/forge/adventure/world/BiomeTexture.java index c55b8374d60..0d70344d34d 100644 --- a/forge-gui-mobile/src/forge/adventure/world/BiomeTexture.java +++ b/forge-gui-mobile/src/forge/adventure/world/BiomeTexture.java @@ -5,6 +5,7 @@ import com.badlogic.gdx.graphics.Pixmap; import com.badlogic.gdx.graphics.g2d.TextureAtlas; import com.badlogic.gdx.utils.IntMap; import forge.adventure.data.BiomeData; +import forge.adventure.data.BiomeStructureData; import forge.adventure.data.BiomeTerrainData; import forge.adventure.util.Config; import forge.gui.FThreads; @@ -19,7 +20,7 @@ import java.util.ArrayList; public class BiomeTexture implements Serializable { private final BiomeData data; private final int tileSize; - public Pixmap emptyPixmap = new Pixmap(1, 1, Pixmap.Format.RGBA8888); + public static Pixmap emptyPixmap = null; ArrayList> images = new ArrayList<>(); ArrayList> smallImages = new ArrayList<>(); ArrayList> edgeImages = new ArrayList<>(); @@ -45,7 +46,6 @@ public class BiomeTexture implements Serializable { FThreads.invokeInEdtNowOrLater(new Runnable() { @Override public void run() { - Pixmap completePicture = null; if (images != null) { for (ArrayList val : images) { @@ -79,22 +79,49 @@ public class BiomeTexture implements Serializable { edgeImages = new ArrayList<>(); ArrayList regions =new ArrayList<>(); + ArrayList source =new ArrayList<>(); regions.add(Config.instance().getAtlas(data.tilesetAtlas).findRegion(data.tilesetName)); + source.add(Config.instance().getAtlas(data.tilesetAtlas)); if(data.terrain!=null) { for(BiomeTerrainData terrain:data.terrain) { - regions.add(Config.instance().getAtlas(data.tilesetAtlas).findRegion(terrain.spriteName)); + TextureAtlas.AtlasRegion region = Config.instance().getAtlas(data.tilesetAtlas).findRegion(terrain.spriteName); + if(region==null) + { + System.err.print("Can not find sprite "+terrain.spriteName); + continue; + } + regions.add(region); + source.add(Config.instance().getAtlas(data.tilesetAtlas)); } } + if(data.structures!=null) + { + for(BiomeStructureData structureData:data.structures) + { + BiomeStructure structure=new BiomeStructure(structureData,0,0,0); + TextureAtlas atlas=structure.atlas (); + for(BiomeStructureData.BiomeStructureDataMapping mapping:structure.mapping()) + { + TextureAtlas.AtlasRegion region = atlas.findRegion(mapping.name); + if(region==null) + { + System.err.print("Can not find sprite "+mapping.name); + continue; + } + regions.add(region); + source.add(atlas); + } + } + } + for (TextureAtlas.AtlasRegion region : regions) { ArrayList pics = new ArrayList<>(); ArrayList spics = new ArrayList<>(); - if (completePicture == null) { + if(!region.getTexture().getTextureData().isPrepared()) region.getTexture().getTextureData().prepare(); - completePicture = region.getTexture().getTextureData().consumePixmap(); - } - + Pixmap completePicture = region.getTexture().getTextureData().consumePixmap(); for (int y = 0; y < 4; y++) { for (int x = 0; x < 3; x++) { int px = region.getRegionX() + (x * tileSize); @@ -117,6 +144,7 @@ public class BiomeTexture implements Serializable { smallImages.add(spics); edgeImages.add(new IntMap<>()); + completePicture.dispose(); } } }); @@ -124,6 +152,8 @@ public class BiomeTexture implements Serializable { public Pixmap getPixmap(int biomeSubIndex) { if (biomeSubIndex >= edgeImages.size() || biomeSubIndex < 0) { + if(emptyPixmap==null) + emptyPixmap=new Pixmap(1, 1, Pixmap.Format.RGBA8888); return emptyPixmap; } return images.get(biomeSubIndex).get(BigPictures.Center.value); diff --git a/forge-gui-mobile/src/forge/adventure/world/ColorMap.java b/forge-gui-mobile/src/forge/adventure/world/ColorMap.java new file mode 100644 index 00000000000..eec74a70692 --- /dev/null +++ b/forge-gui-mobile/src/forge/adventure/world/ColorMap.java @@ -0,0 +1,49 @@ +package forge.adventure.world; + +import com.badlogic.gdx.files.FileHandle; +import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.graphics.Pixmap; + +public class ColorMap +{ + private final int width; + private final int height; + private final Color[] data; + public ColorMap(int w,int h) + { + width=w; + height=h; + data=new Color[w*h]; + for(int i=0;i 1 && entropy <= min) { + double noise = 1e-6 * this.random.nextDouble(); + if (entropy + noise < min) { + min = entropy + noise; + argmin = i; + } + } + } + + + if (argmin == -1) { + this.observed = new int[this.FMX * this.FMY]; + for (int i = 0; i < this.wave.length; i++) for (int t = 0; t < + this.T; t++) if (this.wave[i][t]) { + this.observed[i] = t; + break; + } + return true; + } + + for (int t = 0; t < this.T; t++) distribution[t] = + this.wave[argmin][t] ? this.weights[t] : 0; + + int r = Model.randomIndice(distribution, this.random.nextDouble()); + + + boolean[] w = this.wave[argmin]; + for (int t = 0; t < this.T; t++) if (w[t] != (t == r)) this.ban(argmin, t); + + return null; + } + + protected void ban(int i, int t) { + this.wave[i][t] = false; + + int[] comp = this.compatible[i][t]; + for (int d = 0; d < 4; d++) comp[d] = 0; + this.stack[this.stacksize]=i; + this.stack[this.stacksize+1]=t; + this.stacksize+=2; + + this.sumsOfOnes[i] -= 1; + this.sumsOfWeights[i] -= this.weights[t]; + this.sumsOfWeightLogWeights[i] -= this.weightLogWeights[t]; + + double sum = this.sumsOfWeights[i]; + this.entropies[i] = Math.log(sum) - this.sumsOfWeightLogWeights[i] / sum; + } + + protected void propagate() { + while (this.stacksize >= 2) { + + int i1 = this.stack[this.stacksize - 2]; + int x1 = i1 % this.FMX; + int y1 = i1 / this.FMX; + int stack2 = this.stack[this.stacksize - 1]; + this.stacksize-=2; + for (int d = 0; d < 4; d++) { + int dx = Model.DX[d], dy = Model.DY[d]; + int x2 = x1 + dx, y2 = y1 + dy; + + if (this.onBoundary(x2, y2)) continue; + + if (x2 < 0) x2 += this.FMX; else if (x2 >= this.FMX) x2 -= this.FMX; + if (y2 < 0) y2 += this.FMY; else if (y2 >= this.FMY) y2 -= this.FMY; + + int i2 = x2 + y2 * this.FMX; + int[] p = this.propagator[d][stack2]; + int[][] compat = this.compatible[i2]; + + for (int l = 0; l < p.length; l++) { + int t2 = p[l]; + int[] comp = compat[t2]; + + comp[d]--; + + if (comp[d] == 0) this.ban(i2, t2); + } + } + } + } + + public boolean run(int seed, int limit) { + if (this.wave == null) this.init(); + + this.Clear(); + this.random = new Random(seed); + + for (int l = 0; l < limit || limit == 0; l++) { + Boolean result = this.observe(); + if (result != null) + return (boolean) result; + this.propagate(); + } + + return true; + } + + protected void Clear() { + for (int i = 0; i < this.wave.length; i++) { + for (int t = 0; t < this.T; t++) { + this.wave[i][t] = true; + for (int d = 0; d < 4; d++) this.compatible[i][t][d] = + this.propagator[Model.oppposite[d]][t].length; + } + + this.sumsOfOnes[i] = this.weights.length; + this.sumsOfWeights[i] = this.sumOfWeights; + this.sumsOfWeightLogWeights[i] = this.sumOfWeightLogWeights; + this.entropies[i] = this.startingEntropy; + } + } +} diff --git a/forge-gui-mobile/src/forge/adventure/world/OverlappingModel.java b/forge-gui-mobile/src/forge/adventure/world/OverlappingModel.java new file mode 100644 index 00000000000..de18632dfa3 --- /dev/null +++ b/forge-gui-mobile/src/forge/adventure/world/OverlappingModel.java @@ -0,0 +1,293 @@ +package forge.adventure.world; + + +import com.badlogic.gdx.graphics.Color; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.function.BiFunction; +import java.util.function.Function; + +public class OverlappingModel extends Model { + int N; + Integer[][] patterns; + int ground; + List colors; + + @FunctionalInterface + interface Agrees { + public Five apply(One one, Two two, Three three, Four four); + } + + /** + * Creates a new instance of the Overlapping Model + * @param data BufferedImage data of source image. + * @param N Size of the patterns. + * @param width The width of the generation (in pixels). + * @param height The height of the generation (in pixels). + * @param periodicInput Whether the source image is to be considered as periodic (repeatable). + * @param periodicOutput Whether the generation should be periodic (repeatable). + * @param symmetry Allowed symmetries from 1 (no symmetry) to 8 (all mirrored / rotated variations). + * @param ground Id of the specific pattern to use as the bottom of the generation. + */ + public OverlappingModel( + ColorMap data, + int N, + int width, + int height, + boolean periodicInput, + boolean periodicOutput, + int symmetry, + int ground + ) { + super(width, height); + this.N = N; + this.periodic = periodicOutput; + + + int SMX = data.getWidth(), SMY = data.getHeight(); + Integer[][] sample = new Integer[SMX][SMY]; + + this.colors = new ArrayList(); + + for (int y = 0; y < SMY; y++) for (int x = 0; x < SMX; x++) { + Color color = data.getColor(x, y); + if(color==null) + break; + int i = 0; + for (Color c : colors) { + if (c.equals(color)) break; + i++; + } + if (i == colors.size()) colors.add(color); + sample[x][y] = i; + } + + int C = this.colors.size(); + long W = OverlappingModel.toPower(C, this.N * this.N); + + Function, Integer[]> pattern = + (BiFunction f) -> { + Integer[] result = new Integer[this.N * this.N]; + for (int y = 0; y < this.N; y++) for (int x = 0; x < + this.N; x++) result[x + y * this.N] = f.apply(x, y); + + return result; + }; + + BiFunction patternFromSample = + (Integer x, Integer y) -> pattern.apply( + (Integer dx, Integer dy) -> sample[(x + dx) % SMX][(y + dy) % SMY] + ); + + Function rotate = + (Integer[] p) -> pattern.apply( + (Integer x, Integer y) -> p[this.N - 1 - y + x * this.N] + ); + + Function reflect = + (Integer[] p) -> pattern.apply( + (Integer x, Integer y) -> p[this.N - 1 - x + y * this.N] + ); + + Function index = + (Integer[] p) -> { + long result = 0, power = 1; + for (int i = 0; i < p.length; i++) { + result += p[p.length - 1 - i] * power; + power *= C; + } + return result; + }; + + Function patternFromIndex = + (Long ind) -> { + long residue = ind, power = W; + Integer[] result = new Integer[this.N * this.N]; + + for (int i = 0; i < result.length; i++) { + power /= C; + int count = 0; + + while (residue >= power) { + residue -= power; + count++; + } + + result[i] = count; + } + + return result; + }; + + HashMap weights = new HashMap(); + List ordering = new ArrayList(); + + for (int y = 0; y < (periodicInput ? SMY : SMY - N + 1); y++) for (int x = + 0; x < (periodicInput ? SMX : SMX - this.N + 1); x++) { + Integer[][] ps = new Integer[8][]; + + ps[0] = patternFromSample.apply(x, y); + ps[1] = reflect.apply(ps[0]); + ps[2] = rotate.apply(ps[0]); + ps[3] = reflect.apply(ps[2]); + ps[4] = rotate.apply(ps[2]); + ps[5] = reflect.apply(ps[4]); + ps[6] = rotate.apply(ps[4]); + ps[7] = reflect.apply(ps[6]); + + for (int k = 0; k < symmetry; k++) { + long ind = index.apply(ps[k]); + if (weights.containsKey(ind)) weights.put( + ind, + weights.get(ind) + 1 + ); else { + weights.put(ind, 1); + ordering.add(ind); + } + } + } + + this.T = weights.size(); + this.ground = (ground + this.T) % this.T; + this.patterns = new Integer[this.T][]; + this.weights = new Double[this.T]; + + int counter = 0; + + for (long w : ordering) { + this.patterns[counter] = patternFromIndex.apply(w); + this.weights[counter] = (double) weights.get(w); +// System.out.println(this.weights[counter]); + + // weights[counter] = weights[(int) w]; + counter++; + } + + Agrees agrees = + (Integer[] p1, Integer[] p2, Integer dx, Integer dy) -> { + int xmin = dx < 0 ? 0 : dx; + int xmax = dx < 0 ? dx + N : N; + int ymin = dy < 0 ? 0 : dy; + int ymax = dy < 0 ? dy + N : N; + + for (int y = ymin; y < ymax; y++) for (int x = xmin; x < xmax; x++) if ( + p1[x + this.N * y] != p2[x - dx + this.N * (y - dy)] + ) return false; + return true; + }; + + this.propagator = new int[4][][]; + +// System.out.println(this.T); + + for (int d = 0; d < 4; d++) { + this.propagator[d] = new int[this.T][]; + for (int t = 0; t < this.T; t++) { + List list = new ArrayList(); + for (int t2 = 0; t2 < this.T; t2++) if ( + agrees.apply( + this.patterns[t], + this.patterns[t2], + Model.DX[d], + Model.DY[d] + ) + ) list.add(t2); + this.propagator[d][t] = new int[list.size()]; + for (int c = 0; c < list.size(); c++) this.propagator[d][t][c] = + list.get(c); + } + } + } + + @Override + protected boolean onBoundary(int x, int y) { + return ( + !this.periodic && + (x + this.N > this.FMX || y + this.N > this.FMY || x < 0 || y < 0) + ); + } + + /** + * Returns a new BufferedImage generated by the model. + * Requires Run() to have been run. + */ + @Override + public ColorMap graphics() { + ColorMap result = new ColorMap( + this.FMX, + this.FMY + ); + + if (this.observed != null) { + for (int y = 0; y < this.FMY; y++) { + int dy = y < this.FMY - this.N + 1 ? 0 : this.N - 1; + for (int x = 0; x < this.FMX; x++) { + int dx = x < this.FMX - this.N + 1 ? 0 : this.N - 1; + Color c = + this.colors.get( + this.patterns[this.observed[x - dx + (y - dy) * this.FMX]][dx + + dy * + this.N] + ); + + result.setColor(x, y, c); + } + } + } else { + for (int i = 0; i < this.wave.length; i++) { + float contributors = 0, r = 0, g = 0, b = 0; + int x = i % this.FMX, y = i / this.FMX; + + for (int dy = 0; dy < this.N; dy++) for (int dx = 0; dx < + this.N; dx++) { + int sx = x - dx; + if (sx < 0) sx += this.FMX; + + int sy = y - dy; + if (sy < 0) sy += this.FMY; + + int s = sx + sy * this.FMX; + if (this.onBoundary(sx, sy)) continue; + for (int t = 0; t < this.T; t++) if (wave[s][t]) { + contributors++; + Color color = this.colors.get(this.patterns[t][dx + dy * this.N]); + r += color.r; + g += color.g; + b += color.b; + } + } + + Color c = new Color( + r / contributors, + g / contributors, + b / contributors,1f + ); + result.setColor(x, y, c); + } + } + + return result; + } + + protected void Clear() { + super.Clear(); + + if (this.ground != 0) { + for (int x = 0; x < this.FMX; x++) { + for (int t = 0; t < this.T; t++) if (t != this.ground) this.ban( + x + (this.FMY - 1) * this.FMX, + t + ); + + for (int y = 0; y < this.FMY - 1; y++) this.ban( + x + y * this.FMX, + this.ground + ); + } + + this.propagate(); + } + } +} diff --git a/forge-gui-mobile/src/forge/adventure/world/SimpleTiledModel.java b/forge-gui-mobile/src/forge/adventure/world/SimpleTiledModel.java new file mode 100644 index 00000000000..8a3f651d7ee --- /dev/null +++ b/forge-gui-mobile/src/forge/adventure/world/SimpleTiledModel.java @@ -0,0 +1,337 @@ +package forge.adventure.world; + + +import com.badlogic.gdx.graphics.Color; + +import java.util.*; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.stream.IntStream; + +public class SimpleTiledModel extends Model { + List tiles; + List tilenames; + int tilesize; + boolean black; + + /** + * Create a new instance of a Simple Tiled Model. + * @param tilesize Size of the tile images in pixels. + * @param tileSymmetries Array of Map of tilenames and their symmetries. + * @param neighborData Array of Map of left and right neighbor combinations. + * @param subsetData Map of Subset definitions. + * @param tileData Map of tile image data indexed by tilename. + * @param subsetName Name of the subset in subsetData to use. + * @param width Output width in tiles. + * @param height Output height in tiles. + * @param periodic Should the output generation be tileable. + * @param black + * @param unique + */ + public SimpleTiledModel( + int tilesize, + List> tileSymmetries, + List> neighborData, + Map subsetData, + Map tileData, + String subsetName, + int width, + int height, + boolean periodic, + boolean black, + boolean unique + ) { + super(width, height); + this.periodic = periodic; + this.black = black; + this.tilesize = tilesize; + + List subset = null; + if ( + subsetName != null && + subsetData != null && + subsetData.containsKey(subsetName) + ) { + subset = Arrays.asList(subsetData.get(subsetName)); + } + + + Function, Color[]> tile = + (BiFunction f) -> { + Color[] result = new Color[this.tilesize * this.tilesize]; + for (int y = 0; y < this.tilesize; y++) for (int x = 0; x < + this.tilesize; x++) result[x + y * tilesize] = f.apply(x, y); + return result; + }; + + Function rotate = + (Color[] array) -> tile.apply( + (Integer x, Integer y) -> array[this.tilesize - + 1 - + y + + x * + this.tilesize] + ); + + this.tiles = new ArrayList(); + this.tilenames = new ArrayList(); + + List tempStationary = new ArrayList(); + List action = new ArrayList(); + HashMap firstOccurrence = new HashMap(); + + for (Map xtile : tileSymmetries) { + String tilename = xtile.get("name"); + + Function a, b; + int cardinality; + + String sym = xtile.getOrDefault("symmetry", "X"); + + if (subset != null && !subset.contains(tilename)) continue; + + switch (sym) { + case "L": + cardinality = 4; + a = (Integer i) -> (i + 1) % 4; + b = (Integer i) -> (i % 2) == 0 ? i + 1 : i - 1; + break; + case "T": + cardinality = 4; + a = (Integer i) -> (i + 1) % 4; + b = (Integer i) -> (i % 2) == 0 ? i : 4 - i; + break; + case "I": + cardinality = 2; + a = (Integer i) -> 1 - i; + b = (Integer i) -> i; + break; + case "\\": + cardinality = 2; + a = (Integer i) -> 1 - i; + b = (Integer i) -> 1 - i; + break; + default: + cardinality = 1; + a = (Integer i) -> i; + b = (Integer i) -> i; + break; + } + + this.T = action.size(); + firstOccurrence.put(tilename, this.T); + + Integer[][] map = new Integer[cardinality][]; + for (int t = 0; t < cardinality; t++) { + map[t] = new Integer[8]; + + map[t][0] = t; + map[t][1] = a.apply(t); + map[t][2] = a.apply(a.apply(t)); + map[t][3] = a.apply(a.apply(a.apply(t))); + map[t][4] = b.apply(t); + map[t][5] = b.apply(a.apply(t)); + map[t][6] = b.apply(a.apply(a.apply(t))); + map[t][7] = b.apply(a.apply(a.apply(a.apply(t)))); + + for (int s = 0; s < 8; s++) map[t][s] += this.T; + + action.add(map[t]); + } + + if (unique) { + for (int t = 0; t < cardinality; t++) { + ColorMap xtileData = tileData.get(tilename); + this.tiles.add( + tile.apply( + (Integer x, Integer y) -> (xtileData.getColor(x, y)) + ) + ); + this.tilenames.add(String.format("%s %s", tilename, t)); + } + } else { + ColorMap xtileData = tileData.get(tilename); + this.tiles.add( + tile.apply( + (Integer x, Integer y) -> (xtileData.getColor(x, y)) + ) + ); + + this.tilenames.add(String.format("%s 0", tilename)); + + for (int t = 1; t < cardinality; t++) { + this.tiles.add(rotate.apply(this.tiles.get(this.T + t - 1))); + this.tilenames.add(String.format("%s %s", tilename, t)); + } + } + + for (int t = 0; t < cardinality; t++) tempStationary.add( + Double.valueOf(xtile.getOrDefault("weight", "1.0")) + ); + } + + this.T = action.size(); + this.weights = tempStationary.toArray(new Double[0]); + + this.propagator = new int[4][][]; + boolean[][][] tempPropagator = new boolean[4][][]; + for (int d = 0; d < 4; d++) { + tempPropagator[d] = new boolean[this.T][]; + this.propagator[d] = new int[this.T][]; + for (int t = 0; t < this.T; t++) tempPropagator[d][t] = + new boolean[this.T]; + } + + + for (Map xneighbor : neighborData) { + + String[] left = Arrays + .stream(xneighbor.get("left").split(" ")) + .filter(x -> !x.isEmpty()) + .toArray(String[]::new); + + String[] right = Arrays + .stream(xneighbor.get("right").split(" ")) + .filter(x -> !x.isEmpty()) + .toArray(String[]::new); + + if ( + subset != null && + (!subset.contains(left[0]) || !subset.contains(right[0])) + ) continue; + + + int L = action.get(firstOccurrence.get(left[0]))[left.length == 1 ? 0 + : Integer.valueOf(left[1])]; + int D = action.get(L)[1]; + + int R = action.get(firstOccurrence.get(right[0]))[right.length == 1 ? 0 + : Integer.valueOf(right[1])]; + int U = action.get(R)[1]; + + + tempPropagator[0][R][L] = true; + tempPropagator[0][action.get(R)[6]][action.get(L)[6]] = true; + tempPropagator[0][action.get(L)[4]][action.get(R)[4]] = true; + tempPropagator[0][action.get(L)[2]][action.get(R)[2]] = true; + + tempPropagator[1][U][D] = true; + tempPropagator[1][action.get(D)[6]][action.get(U)[6]] = true; + tempPropagator[1][action.get(U)[4]][action.get(D)[4]] = true; + tempPropagator[1][action.get(D)[2]][action.get(U)[2]] = true; + } + + for (int t2 = 0; t2 < this.T; t2++) for (int t1 = 0; t1 < this.T; t1++) { + tempPropagator[2][t2][t1] = tempPropagator[0][t1][t2]; + tempPropagator[3][t2][t1] = tempPropagator[1][t1][t2]; + } + + ArrayList>> sparsePropagator = new ArrayList>>(); + + for(int d = 0; d < 4; d++) { + sparsePropagator.add(d, new ArrayList>()); + for (int t = 0; t < this.T; t++) + sparsePropagator.get(d).add(t, new ArrayList()); + } + + for (int d = 0; d < 4; d++) for (int t1 = 0; t1 < this.T; t1++) { + ArrayList sp = sparsePropagator.get(d).get(t1); + boolean[] tp = tempPropagator[d][t1]; + + for (int t2 = 0; t2 < this.T; t2++) { + if (tp[t2]) sp.add(t2); + } + + + int ST = sp.size(); + this.propagator[d][t1] = new int[ST]; + for (int st = 0; st < ST; st++) this.propagator[d][t1][st] = sp.get(st); + + } + } + + @Override + protected boolean onBoundary(int x, int y) { + return !this.periodic && (x < 0 || y < 0 || x >= this.FMX || y >= this.FMY); + } + + public String textOutput() { + StringBuilder result = new StringBuilder(); + + for (int y = 0; y < this.FMY; y++) { + for (int x = 0; x < this.FMX; x++) + result.append(String.format("{%s}, ", this.tilenames.get(this.observed[x + y * this.FMX]))); + result.append("\n"); + } + + return result.toString(); + } + + @Override + public ColorMap graphics() { + ColorMap result = new ColorMap( + this.FMX * this.tilesize, + this.FMY * this.tilesize + ); + +// System.out.println(this.observed); + + if (this.observed != null) { + for (int x = 0; x < this.FMX; x++) for (int y = 0; y < this.FMY; y++) { + Color[] tile = this.tiles.get(this.observed[x + y * this.FMX]); + for (int yt = 0; yt < this.tilesize; yt++) for (int xt = 0; xt < + this.tilesize; xt++) { + Color c = tile[xt + yt * this.tilesize]; + result.setColor( + x * this.tilesize + xt, + y * this.tilesize + yt, + c + ); + } + } + } else { + for (int x = 0; x < this.FMX; x++) for (int y = 0; y < this.FMY; y++) { + boolean[] a = this.wave[x + y * this.FMX]; + int amount = IntStream + .range(0, a.length) + .map(idx -> a[idx] ? 1 : 0) + .sum(); + + + double lambda = + 1.0 / + IntStream + .range(0, this.T) + .filter(idx -> a[idx]) + .mapToDouble(idx -> this.weights[idx]) + .sum(); + + for (int yt = 0; yt < this.tilesize; yt++) for (int xt = 0; xt < + this.tilesize; xt++) { + if (this.black && amount == this.T) result.setColor( + x * this.tilesize + xt, + y * this.tilesize * yt, + Color.BLACK + ); else { + double r = 0, g = 0, b = 0; + for (int t = 0; t < this.T; t++) if (a[t]) { + Color c = this.tiles.get(t)[xt + yt * this.tilesize]; + r += c.r * this.weights[t] * lambda; + g += c.g * this.weights[t] * lambda; + b += c.b * this.weights[t] * lambda; + } + + Color newColor = new Color((float) r, (float) g, (float) b,1); + result.setColor( + x * tilesize + xt, + y * tilesize + yt, + newColor + ); + } + } + } + } + + return result; + } +} diff --git a/forge-gui-mobile/src/forge/adventure/world/World.java b/forge-gui-mobile/src/forge/adventure/world/World.java index a7ecf515a65..5fb6432fa3c 100644 --- a/forge-gui-mobile/src/forge/adventure/world/World.java +++ b/forge-gui-mobile/src/forge/adventure/world/World.java @@ -9,11 +9,7 @@ import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.scenes.scene2d.Actor; import com.badlogic.gdx.utils.Disposable; import com.badlogic.gdx.utils.Json; -import forge.adventure.data.BiomeData; -import forge.adventure.data.BiomeSpriteData; -import forge.adventure.data.BiomeTerrainData; -import forge.adventure.data.PointOfInterestData; -import forge.adventure.data.WorldData; +import forge.adventure.data.*; import forge.adventure.pointofintrest.PointOfInterest; import forge.adventure.pointofintrest.PointOfInterestMap; import forge.adventure.scene.Scene; @@ -22,21 +18,24 @@ import forge.adventure.util.Config; import forge.adventure.util.Paths; import forge.adventure.util.SaveFileContent; import forge.adventure.util.SaveFileData; +import forge.gui.FThreads; +import forge.gui.GuiBase; +import forge.util.ThreadUtil; import org.apache.commons.lang3.tuple.Pair; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Random; +import java.util.*; /** * Class that will create the world from the configuration */ -public class World implements Disposable, SaveFileContent { +public class World implements Disposable, SaveFileContent { private WorldData data; private Pixmap biomeImage; private long[][] biomeMap; private int[][] terrainMap; + private static final int collisionBit = 0b10000000000000000000000000000000; + private static final int isStructureBit = 0b01000000000000000000000000000000; + private static final int terrainMask = collisionBit | isStructureBit; private int width; private int height; private SpritesDataMap mapObjectIds; @@ -44,19 +43,38 @@ public class World implements Disposable, SaveFileContent { private BiomeTexture[] biomeTexture; private long seed; private final Random random = new Random(); - private boolean worldDataLoaded=false; + private boolean worldDataLoaded = false; private Texture globalTexture = null; - public Random getRandom() - { + public Random getRandom() { return random; } + static public int highestBiome(long biome) { return (int) (Math.log(Long.highestOneBit(biome)) / Math.log(2)); } + public boolean collidingTile(Rectangle boundingRect) { + + int xLeft = (int) boundingRect.getX() / getTileSize(); + int yTop = (int) boundingRect.getY() / getTileSize(); + int xRight = (int) ((boundingRect.getX() + boundingRect.getWidth()) / getTileSize()); + int yBottom = (int) ((boundingRect.getY() + boundingRect.getHeight()) / getTileSize()); + + if (isColliding(xLeft, yTop)) + return true; + if (isColliding(xLeft, yBottom)) + return true; + if (isColliding(xRight, yBottom)) + return true; + if (isColliding(xRight, yTop)) + return true; + + return false; + } + public void loadWorldData() { - if(worldDataLoaded) + if (worldDataLoaded) return; FileHandle handle = Config.instance().getFile(Paths.WORLD); @@ -64,49 +82,51 @@ public class World implements Disposable, SaveFileContent { this.data = (new Json()).fromJson(WorldData.class, rawJson); biomeTexture = new BiomeTexture[data.GetBiomes().size() + 1]; - int biomeIndex=0; + int biomeIndex = 0; for (BiomeData biome : data.GetBiomes()) { biomeTexture[biomeIndex] = new BiomeTexture(biome, data.tileSize); biomeIndex++; } biomeTexture[biomeIndex] = new BiomeTexture(data.roadTileset, data.tileSize); - worldDataLoaded=true; + worldDataLoaded = true; } @Override public void load(SaveFileData saveFileData) { - if(biomeImage!=null) + if (biomeImage != null) biomeImage.dispose(); - loadWorldData(); + loadWorldData(); - biomeImage=saveFileData.readPixmap("biomeImage"); - biomeMap=(long[][])saveFileData.readObject("biomeMap"); - terrainMap=(int[][])saveFileData.readObject("terrainMap"); - width=saveFileData.readInt("width"); - height=saveFileData.readInt("height"); + biomeImage = saveFileData.readPixmap("biomeImage"); + biomeMap = (long[][]) saveFileData.readObject("biomeMap"); + terrainMap = (int[][]) saveFileData.readObject("terrainMap"); + + + width = saveFileData.readInt("width"); + height = saveFileData.readInt("height"); mapObjectIds = new SpritesDataMap(getChunkSize(), this.data.tileSize, this.data.width / getChunkSize()); mapObjectIds.load(saveFileData.readSubData("mapObjectIds")); - mapPoiIds = new PointOfInterestMap(getChunkSize(), this.data.tileSize, this.data.width / getChunkSize(),this.data.height / getChunkSize()); + mapPoiIds = new PointOfInterestMap(getChunkSize(), this.data.tileSize, this.data.width / getChunkSize(), this.data.height / getChunkSize()); mapPoiIds.load(saveFileData.readSubData("mapPoiIds")); - seed=saveFileData.readLong("seed"); + seed = saveFileData.readLong("seed"); } @Override public SaveFileData save() { - SaveFileData data=new SaveFileData(); + SaveFileData data = new SaveFileData(); - data.store("biomeImage",biomeImage); - data.storeObject("biomeMap",biomeMap); - data.storeObject("terrainMap",terrainMap); - data.store("width",width); - data.store("height",height); - data.store("mapObjectIds",mapObjectIds.save()); - data.store("mapPoiIds",mapPoiIds.save()); - data.store("seed",seed); + data.store("biomeImage", biomeImage); + data.storeObject("biomeMap", biomeMap); + data.storeObject("terrainMap", terrainMap); + data.store("width", width); + data.store("height", height); + data.store("mapObjectIds", mapObjectIds.save()); + data.store("mapPoiIds", mapPoiIds.save()); + data.store("seed", seed); return data; @@ -116,6 +136,7 @@ public class World implements Disposable, SaveFileContent { public BiomeSpriteData getObject(int id) { return mapObjectIds.get(id); } + private class DrawingInformation { private int neighbors; @@ -130,17 +151,18 @@ public class World implements Disposable, SaveFileContent { } public void draw(Pixmap drawingPixmap) { - regions.drawPixmapOn(terrain,neighbors,drawingPixmap); + regions.drawPixmapOn(terrain, neighbors, drawingPixmap); } } + public Pixmap getBiomeSprite(int x, int y) { if (x < 0 || y <= 0 || x >= width || y > height) return new Pixmap(data.tileSize, data.tileSize, Pixmap.Format.RGBA8888); long biomeIndex = getBiome(x, y); - int terrain = getTerrainIndex(x, y); + int biomeTerrain = getTerrainIndex(x, y); Pixmap drawingPixmap = new Pixmap(data.tileSize, data.tileSize, Pixmap.Format.RGBA8888); - ArrayList information=new ArrayList<>(); + ArrayList information = new ArrayList<>(); for (int i = 0; i < biomeTexture.length; i++) { if ((biomeIndex & 1L << i) == 0) { continue; @@ -148,9 +170,8 @@ public class World implements Disposable, SaveFileContent { BiomeTexture regions = biomeTexture[i]; if (x <= 0 || y <= 1 || x >= width - 1 || y >= height)//edge { - return regions.getPixmap(terrain); + return regions.getPixmap(biomeTerrain); } - int biomeTerrain=Math.min(regions.images.size()-1,terrain); int neighbors = 0b000_000_000; @@ -162,44 +183,40 @@ public class World implements Disposable, SaveFileContent { int otherTerrain = getTerrainIndex(x + nx, y + ny); - if ((otherBiome & 1L << i) != 0 && biomeTerrain <= otherTerrain) + if ((otherBiome & 1L << i) != 0 && (biomeTerrain == otherTerrain) | biomeTerrain == 0) neighbors |= (1 << bitIndex); bitIndex--; } } - if(biomeTerrain!=0&&neighbors!=0b111_111_111) - { - bitIndex = 8; - int baseNeighbors=0; + if (biomeTerrain != 0 && neighbors != 0b111_111_111) { + bitIndex = 8; + int baseNeighbors = 0; for (int ny = 1; ny > -2; ny--) { for (int nx = -1; nx < 2; nx++) { - if ((getBiome(x + nx, y + ny) & (1L << i)) != 0 ) + if ((getBiome(x + nx, y + ny) & (1L << i)) != 0) baseNeighbors |= (1 << bitIndex); bitIndex--; } } - information.add(new DrawingInformation(baseNeighbors,regions,0) ); + information.add(new DrawingInformation(baseNeighbors, regions, 0)); } - information.add(new DrawingInformation(neighbors,regions,biomeTerrain) ); + information.add(new DrawingInformation(neighbors, regions, biomeTerrain)); } - int lastFullNeighbour=-1; - int counter=0; - for(DrawingInformation info:information) - { - if(info.neighbors==0b111_111_111) - lastFullNeighbour= counter; + int lastFullNeighbour = -1; + int counter = 0; + for (DrawingInformation info : information) { + if (info.neighbors == 0b111_111_111) + lastFullNeighbour = counter; counter++; } - counter=0; - if(lastFullNeighbour<0&&information.size()!=0) - information.get(0).neighbors=0b111_111_111; - for(DrawingInformation info:information) - { - if(counter structureDataMap = new HashMap<>(); + + for (BiomeData biome : data.GetBiomes()) { - - biomeIndex++; - int biomeXStart = (int) Math.round(biome.startPointX * (double) width); - int biomeYStart = (int) Math.round(biome.startPointY * (double) height); - int biomeWidth = (int) Math.round(biome.width * (double) width); - int biomeHeight = (int) Math.round(biome.height * (double) height); - - int beginX = Math.max(biomeXStart - biomeWidth / 2, 0); - int beginY = Math.max(biomeYStart - biomeHeight / 2, 0); - int endX = Math.min(biomeXStart + biomeWidth, width); - int endY = Math.min(biomeYStart + biomeHeight, height); - if (biome.width == 1.0 && biome.height == 1.0) { - beginX = 0; - beginY = 0; - endX = width; - endY = height; - } - for (int x = beginX; x < endX; x++) { - for (int y = beginY; y < endY; y++) { - //value 0-1 based on noise - float noiseValue = ((float)noise.eval(x / (float) width * noiseZoom, y / (float) height * noiseZoom) + 1) / 2f; - noiseValue *= biome.noiseWeight; - //value 0-1 based on dist to origin - float distanceValue = ((float)Math.sqrt((x - biomeXStart) * (x - biomeXStart) + (y - biomeYStart) * (y - biomeYStart))) / (Math.max(biomeWidth, biomeHeight) / 2f); - distanceValue *= biome.distWeight; - if (noiseValue + distanceValue < 1.0 || biome.invertHeight && (1 - noiseValue) + distanceValue < 1.0) { - Color color = biome.GetColor(); - float[] hsv = new float[3]; - color.toHsv(hsv); - int count = (int) ((noiseValue - 0.5) * 10 / 4); - //hsv[2]+=(count*0.2); - color.fromHsv(hsv); - pix.setColor(color.r, color.g, color.b, 1); - pix.drawPixel(x, y); - biomeMap[x][y] |= (1L << biomeIndex); - int terrainCounter=1; - if(biome.terrain==null) - continue; - for(BiomeTerrainData terrain:biome.terrain) - { - float terrainNoise = ((float)noise.eval(x / (float) width * (noiseZoom*terrain.resolution), y / (float) height * (noiseZoom*terrain.resolution)) + 1) / 2; - if(terrainNoise>=terrain.min&&terrainNoise<=terrain.max) - { - terrainMap[x][y]=terrainCounter; - } - terrainCounter++; - } - } - + if (biome.structures != null) { + int biomeWidth = (int) Math.round(biome.width * (double) width); + int biomeHeight = (int) Math.round(biome.height * (double) height); + for (BiomeStructureData data : biome.structures) { + long localSeed = seed; + ThreadUtil.getServicePool().submit(() -> { + long threadStartTime = System.currentTimeMillis(); + BiomeStructure structure = new BiomeStructure(data, localSeed, biomeWidth, biomeHeight); + structure.initialize(); + structureDataMap.put(data, structure); + measureGenerationTime("wavefunctioncollapse " + data.sourcePath, threadStartTime); + }); } } } + FThreads.invokeInEdtNowOrLater(() -> { + for (BiomeData biome : data.GetBiomes()) { - mapPoiIds = new PointOfInterestMap(getChunkSize(), data.tileSize, data.width / getChunkSize(),data.height / getChunkSize()); - List towns = new ArrayList<>(); - List otherPoints = new ArrayList<>(); - otherPoints.add(new Rectangle(((float)data.width*data.playerStartPosX*(float)data.tileSize)-data.tileSize*3,((float)data.height*data.playerStartPosY*data.tileSize)-data.tileSize*3,data.tileSize*6,data.tileSize*6)); - int biomeIndex2=-1; - for (BiomeData biome : data.GetBiomes()) { - biomeIndex2++; - for (PointOfInterestData poi : biome.getPointsOfInterest()) { - for (int i = 0; i < poi.count; i++) { - for (int counter = 0; counter < 500; counter++)//tries 100 times to find a free point - { - float radius = (float) Math.sqrt(((random.nextDouble())/2 * poi.radiusFactor)); - float theta = (float) (random.nextDouble() * 2 * Math.PI); - float x = (float) (radius * Math.cos(theta)); - x *= (biome.width * width / 2); - x += (biome.startPointX * width); - float y = (float) (radius * Math.sin(theta)); - y *= (biome.height * height / 2); - y += (height - (biome.startPointY * height)); + biomeIndex[0]++; + int biomeXStart = (int) Math.round(biome.startPointX * (double) width); + int biomeYStart = (int) Math.round(biome.startPointY * (double) height); + int biomeWidth = (int) Math.round(biome.width * (double) width); + int biomeHeight = (int) Math.round(biome.height * (double) height); - if((int)x<0||(int)y<=0||(int)y>=height||(int)x>=width|| biomeIndex2!= highestBiome(getBiome((int)x,(int)y))) - { - continue; - } - - x*= data.tileSize; - y*= data.tileSize; - - boolean breakNextLoop = false; - for (Rectangle rect : otherPoints) { - if (rect.contains(x, y)) { - breakNextLoop = true; - break; + int beginX = Math.max(biomeXStart - biomeWidth / 2, 0); + int beginY = Math.max(biomeYStart - biomeHeight / 2, 0); + int endX = Math.min(biomeXStart + biomeWidth / 2, width); + int endY = Math.min(biomeYStart + biomeHeight / 2, height); + if (biome.width == 1.0 && biome.height == 1.0) { + beginX = 0; + beginY = 0; + endX = width; + endY = height; + } + for (int x = beginX; x < endX; x++) { + for (int y = beginY; y < endY; y++) { + //value 0-1 based on noise + float noiseValue = ((float) noise.eval(x / (float) width * noiseZoom, y / (float) height * noiseZoom) + 1) / 2f; + noiseValue *= biome.noiseWeight; + //value 0-1 based on dist to origin + float distanceValue = ((float) Math.sqrt((x - biomeXStart) * (x - biomeXStart) + (y - biomeYStart) * (y - biomeYStart))) / (Math.max(biomeWidth, biomeHeight) / 2f); + distanceValue *= biome.distWeight; + if (noiseValue + distanceValue < 1.0 || biome.invertHeight && (1 - noiseValue) + distanceValue < 1.0) { + Color color = biome.GetColor(); + float[] hsv = new float[3]; + color.toHsv(hsv); + int count = (int) ((noiseValue - 0.5) * 10 / 4); + //hsv[2]+=(count*0.2); + color.fromHsv(hsv); + pix.setColor(color.r, color.g, color.b, 1); + pix.drawPixel(x, y); + biomeMap[x][y] |= (1L << biomeIndex[0]); + int terrainCounter = 1; + terrainMap[x][y] = 0; + if (biome.terrain != null) { + for (BiomeTerrainData terrain : biome.terrain) { + float terrainNoise = ((float) noise.eval(x / (float) width * (noiseZoom * terrain.resolution), y / (float) height * (noiseZoom * terrain.resolution)) + 1) / 2; + if (terrainNoise >= terrain.min && terrainNoise <= terrain.max) { + terrainMap[x][y] = terrainCounter; + } + terrainCounter++; + } } - } - if (breakNextLoop) - { - boolean foundSolution=false; - boolean noSolution=false; - breakNextLoop=false; - for(int xi=-1;xi<2&&!foundSolution;xi++) - { - for(int yi=-1;yi<2&&!foundSolution;yi++) - { - for (Rectangle rect : otherPoints) { - if (rect.contains(x+xi*data.tileSize, y+yi*data.tileSize)) { - noSolution = true; - break; + if (biome.collision) + terrainMap[x][y] |= collisionBit; + if (biome.structures != null) { + for (BiomeStructureData data : biome.structures) { + while (!structureDataMap.containsKey(data)) { + try { + Thread.sleep(10); + } catch (InterruptedException e) { + throw new RuntimeException(e); } } - if(!noSolution) - { - foundSolution=true; - x=x+xi*data.tileSize; - y=y+yi*data.tileSize; + + BiomeStructure structure = structureDataMap.get(data); + int structureXStart = x - (biomeXStart - biomeWidth / 2) - (int) ((data.x * biomeWidth) - (data.width * biomeWidth / 2)); + int structureYStart = y - (biomeYStart - biomeHeight / 2) - (int) ((data.y * biomeHeight) - (data.height * biomeHeight / 2)); + + int structureIndex = structure.objectID(structureXStart, structureYStart); + if (structureIndex >= 0) { + pix.setColor(data.mappingInfo[structureIndex].getColor()); + pix.drawPixel(x, y); + terrainMap[x][y] = terrainCounter + structureIndex; + if (structure.collision(structureXStart, structureYStart)) + terrainMap[x][y] |= collisionBit; + terrainMap[x][y] |= isStructureBit; + } + + terrainCounter += structure.structureObjectCount(); } } - if(!foundSolution) - { - if(counter==499) - { - System.err.print("Can not place POI "+poi.name+"\n"); - } + } + + } + } + } + currentTime[0] = measureGenerationTime("biomes in total", currentTime[0]); + + mapPoiIds = new PointOfInterestMap(getChunkSize(), data.tileSize, data.width / getChunkSize(), data.height / getChunkSize()); + List towns = new ArrayList<>(); + List notTowns = new ArrayList<>(); + List otherPoints = new ArrayList<>(); + + clearTerrain((int) (data.width * data.playerStartPosX), (int) (data.height * data.playerStartPosY), 10); + otherPoints.add(new Rectangle(((float) data.width * data.playerStartPosX * (float) data.tileSize) - data.tileSize * 3, ((float) data.height * data.playerStartPosY * data.tileSize) - data.tileSize * 3, data.tileSize * 6, data.tileSize * 6)); + int biomeIndex2 = -1; + for (BiomeData biome : data.GetBiomes()) { + biomeIndex2++; + for (PointOfInterestData poi : biome.getPointsOfInterest()) { + for (int i = 0; i < poi.count; i++) { + for (int counter = 0; counter < 500; counter++)//tries 100 times to find a free point + { + float radius = (float) Math.sqrt(((random.nextDouble()) / 2 * poi.radiusFactor)); + float theta = (float) (random.nextDouble() * 2 * Math.PI); + float x = (float) (radius * Math.cos(theta)); + x *= (biome.width * width / 2); + x += (biome.startPointX * width); + float y = (float) (radius * Math.sin(theta)); + y *= (biome.height * height / 2); + y += (height - (biome.startPointY * height)); + + if ((int) x < 0 || (int) y <= 0 || (int) y >= height || (int) x >= width || biomeIndex2 != highestBiome(getBiome((int) x, (int) y))) { continue; } - } - otherPoints.add(new Rectangle(x - data.tileSize * 4, y - data.tileSize * 4, data.tileSize * 8, data.tileSize * 8)); - PointOfInterest newPoint = new PointOfInterest(poi, new Vector2(x, y), random); - mapPoiIds.add(newPoint); + x *= data.tileSize; + y *= data.tileSize; - - Color color = biome.GetColor(); - pix.setColor(color.r, 0.1f, 0.1f, 1); - pix.drawRectangle((int) x / data.tileSize - 3, height - (int) y / data.tileSize - 3, 6, 6); - - - if (poi.type!=null&&poi.type.equals("town")) { - towns.add(newPoint); - } - break; - } - - } - } - - } - - //sort towns - List> allSortedTowns = new ArrayList<>();//edge is first 32 bits id of first id and last 32 bits id of second - - HashSet usedEdges=new HashSet<>(); - for (int i = 0; i < towns.size() - 1; i++) { - - PointOfInterest current = towns.get(i); - int smallestIndex = -1; - float smallestDistance = Float.MAX_VALUE; - for (int j = 0; j < towns.size(); j++) { - - if(i==j||usedEdges.contains((long)i|((long)j<<32))) - continue; - float dist = current.getPosition().dst(towns.get(j).getPosition()); - if (dist < smallestDistance) { - smallestDistance = dist; - smallestIndex = j; - } - } - if (smallestIndex < 0) - continue; - if(smallestDistance>data.maxRoadDistance) - continue; - usedEdges.add((long)i|((long)smallestIndex<<32)); - usedEdges.add((long)i<<32|((long)smallestIndex)); - allSortedTowns.add(Pair.of(current, towns.get(smallestIndex))); - } - - biomeIndex++; - pix.setColor(1, 1, 1, 1); - for (Pair townPair : allSortedTowns) { - - Vector2 currentPoint = townPair.getKey().getTilePosition(data.tileSize); - Vector2 endPoint = townPair.getValue().getTilePosition(data.tileSize); - for (int x = (int) currentPoint.x - 1; x < currentPoint.x + 2; x++) { - for (int y = (int) currentPoint.y - 1; y < currentPoint.y + 2; y++) { - if(x<0||y<=0||x>=width||y>height)continue; - biomeMap[x][height - y] |= (1L << biomeIndex); - pix.drawPixel(x, height-y); - } - } - - while (!currentPoint.equals(endPoint)) { - float xDir = endPoint.x - currentPoint.x; - float yDir = endPoint.y - currentPoint.y; - - if (xDir == 0) { - if (yDir > 0) - currentPoint.y++; - else - currentPoint.y--; - } else if (yDir == 0) { - if (xDir > 0) - currentPoint.x++; - else - currentPoint.x--; - } else if (Math.abs(xDir) > Math.abs(yDir)) { - - if (xDir > 0) - currentPoint.x++; - else - currentPoint.x--; - } else { - if (yDir > 0) - currentPoint.y++; - else - currentPoint.y--; - } - - if( (int)currentPoint.x<0|| (int)currentPoint.y<=0|| (int)currentPoint.x>=width|| (int)currentPoint.y>height)continue; - biomeMap[(int) currentPoint.x][height - (int) currentPoint.y] |= (1L << biomeIndex); - pix.drawPixel((int) currentPoint.x, height - (int) currentPoint.y); - } - - } - - mapObjectIds = new SpritesDataMap(getChunkSize(), data.tileSize, data.width / getChunkSize()); - for (int x = 0; x < width; x++) { - for (int y = 0; y < height; y++) { - int invertedHeight = height - y - 1; - int currentBiome = highestBiome(biomeMap[x][invertedHeight]); - if (currentBiome >= data.GetBiomes().size()) - continue; - BiomeData biome = data.GetBiomes().get(currentBiome); - for (String name : biome.spriteNames) { - BiomeSpriteData sprite = data.GetBiomeSprites().getSpriteData(name); - double spriteNoise = (noise.eval(x / (double) width * noiseZoom*sprite.resolution, y / (double) invertedHeight * noiseZoom*sprite.resolution) + 1) / 2; - if (spriteNoise >= sprite.startArea && spriteNoise <= sprite.endArea) { - if (random.nextFloat() <= sprite.density) { - String spriteKey = sprite.key(); - int key; - if (!mapObjectIds.containsKey(spriteKey)) { - - key = mapObjectIds.put(sprite.key(), sprite, data.GetBiomeSprites()); - } else { - key = mapObjectIds.intKey(spriteKey); + boolean breakNextLoop = false; + for (Rectangle rect : otherPoints) { + if (rect.contains(x, y)) { + breakNextLoop = true; + break; + } } - mapObjectIds.putPosition(key, new Vector2((float) x * data.tileSize + (random.nextFloat() * data.tileSize), (float) y * data.tileSize + (random.nextFloat() * data.tileSize))); + if (breakNextLoop) { + boolean foundSolution = false; + boolean noSolution = false; + breakNextLoop = false; + for (int xi = -1; xi < 2 && !foundSolution; xi++) { + for (int yi = -1; yi < 2 && !foundSolution; yi++) { + for (Rectangle rect : otherPoints) { + if (rect.contains(x + xi * data.tileSize, y + yi * data.tileSize)) { + noSolution = true; + break; + } + } + if (!noSolution) { + foundSolution = true; + x = x + xi * data.tileSize; + y = y + yi * data.tileSize; + + } + } + } + if (!foundSolution) { + if (counter == 499) { + System.err.print("Can not place POI " + poi.name + "\n"); + } + continue; + } + } + otherPoints.add(new Rectangle(x - data.tileSize * 4, y - data.tileSize * 4, data.tileSize * 8, data.tileSize * 8)); + PointOfInterest newPoint = new PointOfInterest(poi, new Vector2(x, y), random); + clearTerrain((int) (x / data.tileSize), (int) (y / data.tileSize), 3); + mapPoiIds.add(newPoint); + + + Color color = biome.GetColor(); + pix.setColor(color.r, 0.1f, 0.1f, 1); + pix.fillRectangle((int) x / data.tileSize - 3, height - (int) y / data.tileSize - 3, 6, 6); + + + if (poi.type != null && poi.type.equals("town")) { + towns.add(newPoint); + } else { + notTowns.add(newPoint); + } + break; + } + + } + } + + } + currentTime[0] = measureGenerationTime("poi placement", currentTime[0]); + + //sort towns + List> allSortedTowns = new ArrayList<>(); + + HashSet usedEdges = new HashSet<>();//edge is first 32 bits id of first id and last 32 bits id of second + for (int i = 0; i < towns.size() - 1; i++) { + + PointOfInterest current = towns.get(i); + int smallestIndex = -1; + int secondSmallestIndex = -1; + float smallestDistance = Float.MAX_VALUE; + for (int j = 0; j < towns.size(); j++) { + + if (i == j || usedEdges.contains((long) i | ((long) j << 32))) + continue; + float dist = current.getPosition().dst(towns.get(j).getPosition()); + if (dist > data.maxRoadDistance) + continue; + if (dist < smallestDistance) { + smallestDistance = dist; + secondSmallestIndex = smallestIndex; + smallestIndex = j; + + } + } + if (smallestIndex < 0) + continue; + usedEdges.add((long) i | ((long) smallestIndex << 32)); + usedEdges.add((long) i << 32 | ((long) smallestIndex)); + allSortedTowns.add(Pair.of(current, towns.get(smallestIndex))); + + if (secondSmallestIndex < 0) + continue; + usedEdges.add((long) i | ((long) secondSmallestIndex << 32)); + usedEdges.add((long) i << 32 | ((long) secondSmallestIndex)); + //allSortedTowns.add(Pair.of(current, towns.get(secondSmallestIndex))); + } + List> allPOIPathsToNextTown = new ArrayList<>(); + for (int i = 0; i < notTowns.size() - 1; i++) { + + PointOfInterest poi = notTowns.get(i); + int smallestIndex = -1; + float smallestDistance = Float.MAX_VALUE; + for (int j = 0; j < towns.size(); j++) { + + float dist = poi.getPosition().dst(towns.get(j).getPosition()); + if (dist < smallestDistance) { + smallestDistance = dist; + smallestIndex = j; + + } + } + if (smallestIndex < 0) + continue; + allPOIPathsToNextTown.add(Pair.of(poi, towns.get(smallestIndex))); + } + biomeIndex[0]++; + pix.setColor(1, 1, 1, 1); + + //reset terrain path to the next town + for (Pair poiToTown : allPOIPathsToNextTown) { + + int startX = (int) poiToTown.getKey().getTilePosition(data.tileSize).x; + int startY = (int) poiToTown.getKey().getTilePosition(data.tileSize).y; + int x1 = (int) poiToTown.getValue().getTilePosition(data.tileSize).x; + int y1 = (int) poiToTown.getValue().getTilePosition(data.tileSize).y; + int dx = Math.abs(x1 - startX); + int dy = Math.abs(y1 - startY); + int sx = startX < x1 ? 1 : -1; + int sy = startY < y1 ? 1 : -1; + int err = dx - dy; + int e2; + while (true) { + if (startX < 0 || startY <= 0 || startX >= width || startY > height) continue; + if ((terrainMap[startX][height - startY] & collisionBit) != 0)//clear terrain if it has collision + terrainMap[startX][height - startY] = 0; + pix.drawPixel(startX, height - startY); + + if (startX == x1 && startY == y1) + break; + e2 = 2 * err; + if (e2 > -dy) { + err = err - dy; + startX = startX + sx; + } else if (e2 < dx) { + err = err + dx; + startY = startY + sy; + } + } + } + + for (Pair townPair : allSortedTowns) { + + int startX = (int) townPair.getKey().getTilePosition(data.tileSize).x; + int startY = (int) townPair.getKey().getTilePosition(data.tileSize).y; + int x1 = (int) townPair.getValue().getTilePosition(data.tileSize).x; + int y1 = (int) townPair.getValue().getTilePosition(data.tileSize).y; + for (int x = startX - 1; x < startX + 2; x++) { + for (int y = startY - 1; y < startY + 2; y++) { + if (x < 0 || y <= 0 || x >= width || y > height) continue; + biomeMap[x][height - y - 1] |= (1L << biomeIndex[0]); + terrainMap[x][height - y - 1] = 0; + + + pix.drawPixel(x, height - y); + } + } + int dx = Math.abs(x1 - startX); + int dy = Math.abs(y1 - startY); + int sx = startX < x1 ? 1 : -1; + int sy = startY < y1 ? 1 : -1; + int err = dx - dy; + int e2; + while (true) { + if (startX < 0 || startY <= 0 || startX >= width || startY > height) continue; + biomeMap[startX][height - startY] |= (1L << biomeIndex[0]); + terrainMap[startX][height - startY] = 0; + pix.drawPixel(startX, height - startY); + + if (startX == x1 && startY == y1) + break; + e2 = 2 * err; + if (e2 > -dy) { + err = err - dy; + startX = startX + sx; + } else if (e2 < dx) { + err = err + dx; + startY = startY + sy; + } + } + } + currentTime[0] = measureGenerationTime("roads", currentTime[0]); + + mapObjectIds = new SpritesDataMap(getChunkSize(), data.tileSize, data.width / getChunkSize()); + for (int x = 0; x < width; x++) { + for (int y = 0; y < height; y++) { + int invertedHeight = height - y - 1; + int currentBiome = highestBiome(biomeMap[x][invertedHeight]); + if (currentBiome >= data.GetBiomes().size()) + continue;//roads + if (isStructure(x, y)) + continue; + BiomeData biome = data.GetBiomes().get(currentBiome); + for (String name : biome.spriteNames) { + BiomeSpriteData sprite = data.GetBiomeSprites().getSpriteData(name); + double spriteNoise = (noise.eval(x / (double) width * noiseZoom * sprite.resolution, y / (double) invertedHeight * noiseZoom * sprite.resolution) + 1) / 2; + if (spriteNoise >= sprite.startArea && spriteNoise <= sprite.endArea) { + if (random.nextFloat() <= sprite.density) { + String spriteKey = sprite.key(); + int key; + if (!mapObjectIds.containsKey(spriteKey)) { + + key = mapObjectIds.put(sprite.key(), sprite, data.GetBiomeSprites()); + } else { + key = mapObjectIds.intKey(spriteKey); + } + mapObjectIds.putPosition(key, new Vector2((((float) x) + .25f + random.nextFloat() / 2) * data.tileSize, (((float) y + .25f) - random.nextFloat() / 2) * data.tileSize)); + break;//only on sprite per point + } } } } } - } - biomeImage = pix; - + biomeImage = pix; + measureGenerationTime("sprites", currentTime[0]); + }); + System.out.print("\nGenerating world took :\t\t" + ((System.currentTimeMillis() - startTime) / 1000f) + " s\n"); WorldStage.getInstance().clearCache(); + ThreadUtil.getServicePool().shutdownNow(); + ThreadUtil.refreshServicePool(); + if (GuiBase.isAndroid()) + GuiBase.getInterface().preventSystemSleep(false); return this; } @@ -549,10 +729,11 @@ public class World implements Disposable, SaveFileContent { } public PointOfInterest findPointsOfInterest(String name) { - return mapPoiIds.findPointsOfInterest(name); + return mapPoiIds.findPointsOfInterest(name); } + public int getChunkSize() { - return (Scene.getIntendedWidth()>Scene.getIntendedHeight()?Scene.getIntendedWidth():Scene.getIntendedHeight()) / data.tileSize; + return (Scene.getIntendedWidth() > Scene.getIntendedHeight() ? Scene.getIntendedWidth() : Scene.getIntendedHeight()) / data.tileSize; } public void dispose() { @@ -561,11 +742,11 @@ public class World implements Disposable, SaveFileContent { } public void setSeed(long seedOffset) { - random.setSeed(seedOffset+seed); + random.setSeed(seedOffset + seed); } public Texture getGlobalTexture() { - if(globalTexture == null){ + if (globalTexture == null) { globalTexture = new Texture(Config.instance().getFile("ui/sprite_markers.png")); System.out.print("Loading auxiliary sprites.\n"); } diff --git a/forge-gui-mobile/src/forge/adventure/world/WorldSave.java b/forge-gui-mobile/src/forge/adventure/world/WorldSave.java index 0bdbbd533c1..8ca0be56d17 100644 --- a/forge-gui-mobile/src/forge/adventure/world/WorldSave.java +++ b/forge-gui-mobile/src/forge/adventure/world/WorldSave.java @@ -21,51 +21,50 @@ import java.util.zip.InflaterInputStream; /** * Represents everything that will be saved, like the player and the world. */ -public class WorldSave { +public class WorldSave { - static final public int AUTO_SAVE_SLOT =-1; - static final public int QUICK_SAVE_SLOT =-2; - static final public int INVALID_SAVE_SLOT =-3; - static final WorldSave currentSave=new WorldSave(); + static final public int AUTO_SAVE_SLOT = -1; + static final public int QUICK_SAVE_SLOT = -2; + static final public int INVALID_SAVE_SLOT = -3; + static final WorldSave currentSave = new WorldSave(); public WorldSaveHeader header = new WorldSaveHeader(); - private final AdventurePlayer player=new AdventurePlayer(); - private final World world=new World(); - private final PointOfInterestChanges.Map pointOfInterestChanges= new PointOfInterestChanges.Map(); + private final AdventurePlayer player = new AdventurePlayer(); + private final World world = new World(); + private final PointOfInterestChanges.Map pointOfInterestChanges = new PointOfInterestChanges.Map(); - private final SignalList onLoadList=new SignalList(); + private final SignalList onLoadList = new SignalList(); - public final World getWorld() - { + public final World getWorld() { return world; } - public AdventurePlayer getPlayer() - { + + public AdventurePlayer getPlayer() { return player; } - public void onLoad(Runnable run) - { + public void onLoad(Runnable run) { onLoadList.add(run); } - public PointOfInterestChanges getPointOfInterestChanges(String id) - { - if(!pointOfInterestChanges.containsKey(id)) - pointOfInterestChanges.put(id,new PointOfInterestChanges()); + + public PointOfInterestChanges getPointOfInterestChanges(String id) { + if (!pointOfInterestChanges.containsKey(id)) + pointOfInterestChanges.put(id, new PointOfInterestChanges()); return pointOfInterestChanges.get(id); } static public boolean load(int currentSlot) { String fileName = WorldSave.getSaveFile(currentSlot); + if (!new File(fileName).exists()) + return false; new File(getSaveDir()).mkdirs(); try { - try(FileInputStream fos = new FileInputStream(fileName); - InflaterInputStream inf = new InflaterInputStream(fos); - ObjectInputStream oos = new ObjectInputStream(inf)) - { + try (FileInputStream fos = new FileInputStream(fileName); + InflaterInputStream inf = new InflaterInputStream(fos); + ObjectInputStream oos = new ObjectInputStream(inf)) { currentSave.header = (WorldSaveHeader) oos.readObject(); - SaveFileData mainData=(SaveFileData)oos.readObject(); + SaveFileData mainData = (SaveFileData) oos.readObject(); currentSave.player.load(mainData.readSubData("player")); GamePlayerUtil.getGuiPlayer().setName(currentSave.player.getName()); try { @@ -87,9 +86,11 @@ public class WorldSave { } return true; } + public static boolean isSafeFile(String name) { - return filenameToSlot(name)!= INVALID_SAVE_SLOT; + return filenameToSlot(name) != INVALID_SAVE_SLOT; } + static public int filenameToSlot(String name) { if (name.equals("auto_save.sav")) return AUTO_SAVE_SLOT; @@ -131,6 +132,7 @@ public class WorldSave { identity = dp.getColorIdentityforAdventure(); } else { starterDeck = isFantasy ? DeckgenUtil.getRandomOrPreconOrThemeDeck("", false, false, false) : Config.instance().starterDecks()[startingColorIdentity]; + identity = DeckProxy.getColorIdentityforAdventure(starterDeck); } currentSave.player.create(name, startingColorIdentity, starterDeck, male, race, avatarIndex, isFantasy, diff); currentSave.player.setWorldPosY((int) (currentSave.world.getData().playerStartPosY * currentSave.world.getData().height * currentSave.world.getTileSize())); @@ -144,14 +146,17 @@ public class WorldSave { } public boolean autoSave() { - return save("auto save",AUTO_SAVE_SLOT); + return save("auto save", AUTO_SAVE_SLOT); } + public boolean quickSave() { - return save("quick save",QUICK_SAVE_SLOT); + return save("quick save", QUICK_SAVE_SLOT); } + public boolean quickLoad() { return load(QUICK_SAVE_SLOT); } + public boolean save(String text, int currentSlot) { header.name = text; @@ -159,17 +164,16 @@ public class WorldSave { new File(getSaveDir()).mkdirs(); try { - try(FileOutputStream fos = new FileOutputStream(fileName); - DeflaterOutputStream def= new DeflaterOutputStream(fos); - ObjectOutputStream oos = new ObjectOutputStream(def)) - { - header.saveDate= new Date(); + try (FileOutputStream fos = new FileOutputStream(fileName); + DeflaterOutputStream def = new DeflaterOutputStream(fos); + ObjectOutputStream oos = new ObjectOutputStream(def)) { + header.saveDate = new Date(); oos.writeObject(header); - SaveFileData mainData=new SaveFileData(); - mainData.store("player",currentSave.player.save()); - mainData.store("world",currentSave.world.save()); + SaveFileData mainData = new SaveFileData(); + mainData.store("player", currentSave.player.save()); + mainData.store("world", currentSave.world.save()); mainData.store("worldStage", WorldStage.getInstance().save()); - mainData.store("pointOfInterestChanges",currentSave.pointOfInterestChanges.save()); + mainData.store("pointOfInterestChanges", currentSave.pointOfInterestChanges.save()); oos.writeObject(mainData); } diff --git a/forge-gui-mobile/src/forge/assets/Assets.java b/forge-gui-mobile/src/forge/assets/Assets.java index a75d067eebf..aea549c38fb 100644 --- a/forge-gui-mobile/src/forge/assets/Assets.java +++ b/forge-gui-mobile/src/forge/assets/Assets.java @@ -42,7 +42,7 @@ public class Assets implements Disposable { private ObjectMap tmxMap; private Texture defaultImage, dummy; private TextureParameter textureParameter; - private int cGen = 0, cGenVal = 0, cFB = 0, cFBVal = 0, cTM, cTMVal = 0, cSF = 0, cSFVal = 0, cCF = 0, cCFVal = 0, aDF = 0, cDFVal = 0; + private int cGen = 0, cGenVal = 0, cFB = 0, cFBVal = 0, cTM = 0, cTMVal = 0, cSF = 0, cSFVal = 0, cCF = 0, cCFVal = 0, aDF = 0, cDFVal = 0; public Assets() { //init titlebg fallback fallback_skins().put(0, new Texture(GuiBase.isAndroid() @@ -55,14 +55,18 @@ public class Assets implements Disposable { } @Override public void dispose() { - for (BitmapFont bitmapFont : counterFonts.values()) - bitmapFont.dispose(); - for (Texture texture : generatedCards.values()) - texture.dispose(); - for (Texture texture : fallback_skins.values()) - texture.dispose(); - for (Texture texture : tmxMap.values()) - texture.dispose(); + if (counterFonts != null) + for (BitmapFont bitmapFont : counterFonts.values()) + bitmapFont.dispose(); + if (generatedCards != null) + for (Texture texture : generatedCards.values()) + texture.dispose(); + if (fallback_skins != null) + for (Texture texture : fallback_skins.values()) + texture.dispose(); + if (tmxMap != null) + for (Texture texture : tmxMap.values()) + texture.dispose(); if (defaultImage != null) defaultImage.dispose(); if (dummy != null) diff --git a/forge-gui-mobile/src/forge/assets/ImageCache.java b/forge-gui-mobile/src/forge/assets/ImageCache.java index 3af05833443..25f2de49809 100644 --- a/forge-gui-mobile/src/forge/assets/ImageCache.java +++ b/forge-gui-mobile/src/forge/assets/ImageCache.java @@ -293,10 +293,14 @@ public class ImageCache { } String fileName = file.getPath(); //load to assetmanager - if (!Forge.getAssets().manager().contains(fileName, Texture.class)) { - Forge.getAssets().manager().load(fileName, Texture.class, Forge.getAssets().getTextureFilter()); - Forge.getAssets().manager().finishLoadingAsset(fileName); - counter+=1; + try { + if (!Forge.getAssets().manager().contains(fileName, Texture.class)) { + Forge.getAssets().manager().load(fileName, Texture.class, Forge.getAssets().getTextureFilter()); + Forge.getAssets().manager().finishLoadingAsset(fileName); + counter += 1; + } + } catch (Exception e) { + System.err.println("Failed to load image: "+fileName); } //return loaded assets @@ -305,7 +309,7 @@ public class ImageCache { } else { Texture cardTexture = Forge.getAssets().manager().get(fileName, Texture.class, false); //if full bordermasking is enabled, update the border color - if (Forge.enableUIMask.equals("Full")) { + if (cardTexture != null && Forge.enableUIMask.equals("Full")) { boolean borderless = isBorderless(imageKey); updateBorders(cardTexture.toString(), borderless ? Pair.of(Color.valueOf("#171717").toString(), false): isCloserToWhite(getpixelColor(cardTexture))); //if borderless, generate new texture from the asset and store @@ -327,6 +331,7 @@ public class ImageCache { syncQ.clear(); cardsLoaded.clear(); counter = 0; + CardRenderer.clearcardArtCache(); } catch (Exception e) { //e.printStackTrace(); } finally { diff --git a/forge-gui-mobile/src/forge/screens/match/views/VCardDisplayArea.java b/forge-gui-mobile/src/forge/screens/match/views/VCardDisplayArea.java index d88b05f1ded..f4a8bba7681 100644 --- a/forge-gui-mobile/src/forge/screens/match/views/VCardDisplayArea.java +++ b/forge-gui-mobile/src/forge/screens/match/views/VCardDisplayArea.java @@ -323,6 +323,8 @@ public abstract class VCardDisplayArea extends VDisplayArea implements ActivateH @Override public boolean tap(float x, float y, int count) { + if (count > 1) //prevent double choice lists or activate handle + return false; if (renderedCardContains(x, y)) { ThreadUtil.invokeInGameThread(new Runnable() { //must invoke in game thread in case a dialog needs to be shown @Override 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/base.json b/forge-gui/res/adventure/Shandalar/world/base.json index 96fc87eca0d..c4e41f7614d 100644 --- a/forge-gui/res/adventure/Shandalar/world/base.json +++ b/forge-gui/res/adventure/Shandalar/world/base.json @@ -1,5 +1,6 @@ { "invertHeight": true, + "collision": true, "name": "ocean", "startPointX": 0.5, "startPointY": 0.5, diff --git a/forge-gui/res/adventure/Shandalar/world/black.json b/forge-gui/res/adventure/Shandalar/world/black.json index 9a9c1d7f76f..4b689ea8ede 100644 --- a/forge-gui/res/adventure/Shandalar/world/black.json +++ b/forge-gui/res/adventure/Shandalar/world/black.json @@ -1,37 +1,145 @@ { - "startPointX": 0.70, - "startPointY": 0.78, - "name": "black", - "noiseWeight": 0.5, - "distWeight": 1.5, - "tilesetName":"Black", - "tilesetAtlas":"world/tilesets/terrain.atlas", - "terrain":[ - { - "spriteName":"Black_1", - "min": 0, - "max": 0.2, - "resolution": 10 - },{ - "spriteName":"Black_2", - "min": 0.8, - "max": 1.0, - "resolution": 10 - } - ], - "width": 0.7, - "height": 0.7, - "color": "110903", - "spriteNames":[ "SwampTree","SwampTree2","DarkGras","Skull","SwampRock","DarkWood","Reed","Waterlily","Shroom","Shroom2"] , - "enemies":[ "Beholder","Big Zombie","Black Wiz1","Black Wiz2","Black Wiz3","Dark Knight","Death Knight","Demon","Ghoul","Ghost","Harpy","Harpy 2","High Vampire","Lich","Rakdos Devil","Skeleton","Skeleton Soldier","Vampire","Zombie","Zombie Lord" ] , - "pointsOfInterest":[ - "Black Castle", - "Swamp Town","Swamp Town2", - "Zombie Town", - "Graveyard", "Graveyard1", "Graveyard2", "Graveyard3", "Graveyard4", - "VampireCastle", "VampireCastle1", "VampireCastle2", - "EvilGrove", "EvilGrove1", "EvilGrove2", "EvilGrove3", "EvilGrove4", - "SkullCaveB", "SkullCaveB1", "SkullCaveB2", - "CaveB", "CaveB1", "CaveB2", "CaveB3", "CaveB4", "CaveB5", "CaveB6", "CaveB8", "CaveBA" - ] +"startPointX": 0.7, +"startPointY": 0.78, +"noiseWeight": 0.5, +"distWeight": 1.5, +"name": "black", +"tilesetAtlas": "world/tilesets/terrain.atlas", +"tilesetName": "Black", +"terrain": [ + { + "spriteName": "Black_1", + "max": 0.2, + "resolution": 10 + }, + { + "spriteName": "Black_2", + "min": 0.8, + "max": 1, + "resolution": 10 + } +], +"width": 0.7, +"height": 0.7, +"color": "110903", +"spriteNames": [ + "SwampTree", + "SwampTree2", + "DarkGras", + "Skull", + "SwampRock", + "DarkWood", + "Reed", + "Waterlily", + "Shroom", + "Shroom2" +], +"enemies": [ + "Beholder", + "Big Zombie", + "Black Wiz1", + "Black Wiz2", + "Black Wiz3", + "Dark Knight", + "Death Knight", + "Demon", + "Ghoul", + "Ghost", + "Harpy", + "Harpy 2", + "High Vampire", + "Lich", + "Rakdos Devil", + "Skeleton", + "Skeleton Soldier", + "Vampire", + "Zombie", + "Zombie Lord" +], +"pointsOfInterest": [ + "Black Castle", + "Swamp Town", + "Swamp Town2", + "Zombie Town", + "Graveyard", + "Graveyard1", + "Graveyard2", + "Graveyard3", + "Graveyard4", + "VampireCastle", + "VampireCastle1", + "VampireCastle2", + "EvilGrove", + "EvilGrove1", + "EvilGrove2", + "EvilGrove3", + "EvilGrove4", + "SkullCaveB", + "SkullCaveB1", + "SkullCaveB2", + "CaveB", + "CaveB1", + "CaveB2", + "CaveB3", + "CaveB4", + "CaveB5", + "CaveB6", + "CaveB8", + "CaveBA" +], +"structures": [ + { + "N": 2, + "x": 0.5, + "y": 0.5, + "structureAtlasPath": "world/tilesets/structures.atlas", + "sourcePath": "world/tilesets/swamp_forest.png", + "maskPath": "world/tilesets/ring.png", + "height": 0.5, + "width": 0.5, + "symmetry": 8, + "periodicOutput": false, + "mappingInfo": [ + { + "name": "swamp_forest", + "color": "007000", + "collision": true + }, + { + "name": "swamp_water", + "color": "005050", + "collision": true + } + ] + }, + { + "N": 2, + "x": 0.5, + "y": 0.5, + "structureAtlasPath": "world/tilesets/structures.atlas", + "sourcePath": "world/tilesets/swamp_ruins.png", + "maskPath": "world/tilesets/circle.png", + "height": 0.20000002, + "width": 0.20000002, + "symmetry": 1, + "periodicOutput": false, + "mappingInfo": [ + { + "name": "deep_swamp", + "color": "002000", + "collision": true + }, + { + "name": "structure", + "color": "505050", + "collision": true + }, + { + "name": "swamp_forest2", + "color": "007000", + "collision": true + } + ] + } +] } \ No newline at end of file diff --git a/forge-gui/res/adventure/Shandalar/world/blue.json b/forge-gui/res/adventure/Shandalar/world/blue.json index bb8fc0927ec..a0d14a219d2 100644 --- a/forge-gui/res/adventure/Shandalar/world/blue.json +++ b/forge-gui/res/adventure/Shandalar/world/blue.json @@ -1,38 +1,126 @@ { - "startPointX": 0.79, - "startPointY": 0.43, - "name": "blue", - "noiseWeight": 0.5, - "distWeight": 1.5, - "tilesetName":"Blue", - "tilesetAtlas":"world/tilesets/terrain.atlas", - "terrain":[ - { - "spriteName":"Blue_1", - "min": 0, - "max": 0.2, - "resolution": 10 - }, { - "spriteName":"Blue_2", - "min": 0.8, - "max": 1.0, - "resolution": 10 - } - ], - "width": 0.7, - "height": 0.7, - "color": "10a2e0", - "spriteNames":["IslandTree" ,"Coral" ,"Shell" ], - "enemies":[ "Bird","Djinn","Elemental","Merfolk","Merfolk Avatar","Merfolk Fighter","Merfolk Lord","Merfolk Soldier","Merfolk warrior","Blue Wiz1","Blue Wiz2","Blue Wiz3","Geist","Rogue","Sea Monster","Tarkir Djinn","Doppelganger" ] , - "pointsOfInterest":[ - "Blue Castle", - "Island Town", - "NestU", - "MerfolkPool", "MerfolkPool1", "MerfolkPool2", "MerfolkPool3", "MerfolkPool4", - "DjinnPalace", "DjinnPalace1", - "Factory", "Factory1", - "MageTowerX", - "MageTowerU", "MageTowerU1", "MageTowerU2", "MageTowerU3", "MageTowerU4", "MageTowerU5", "MageTowerU6", "MageTowerU7", "MageTowerUD", - "CaveU", "CaveU1", "CaveU2", "CaveU3", "CaveU4" - ] +"startPointX": 0.79, +"startPointY": 0.43, +"noiseWeight": 0.5, +"distWeight": 1.5, +"name": "blue", +"tilesetAtlas": "world/tilesets/terrain.atlas", +"tilesetName": "Blue", +"terrain": [ + { + "spriteName": "Blue_1", + "max": 0.2, + "resolution": 10 + }, + { + "spriteName": "Blue_2", + "min": 0.8, + "max": 1, + "resolution": 10 + } +], +"width": 0.7, +"height": 0.7, +"color": "10a2e0", +"spriteNames": [ + "IslandTree", + "Coral", + "Shell" +], +"enemies": [ + "Bird", + "Djinn", + "Elemental", + "Merfolk", + "Merfolk Avatar", + "Merfolk Fighter", + "Merfolk Lord", + "Merfolk Soldier", + "Merfolk warrior", + "Blue Wiz1", + "Blue Wiz2", + "Blue Wiz3", + "Geist", + "Rogue", + "Sea Monster", + "Tarkir Djinn", + "Doppelganger" +], +"pointsOfInterest": [ + "Blue Castle", + "Island Town", + "NestU", + "MerfolkPool", + "MerfolkPool1", + "MerfolkPool2", + "MerfolkPool3", + "MerfolkPool4", + "DjinnPalace", + "DjinnPalace1", + "Factory", + "Factory1", + "MageTowerX", + "MageTowerU", + "MageTowerU1", + "MageTowerU2", + "MageTowerU3", + "MageTowerU4", + "MageTowerU5", + "MageTowerU6", + "MageTowerU7", + "MageTowerUD", + "CaveU", + "CaveU1", + "CaveU2", + "CaveU3", + "CaveU4" +], +"structures": [ + { + "x": 0.5, + "y": 0.5, + "structureAtlasPath": "world/tilesets/structures.atlas", + "sourcePath": "world/tilesets/water.png", + "maskPath": "world/tilesets/circle.png", + "height": 0.20000002, + "width": 0.20000002, + "symmetry": 8, + "periodicOutput": false, + "mappingInfo": [ + { + "name": "water", + "color": "0070a0", + "collision": true + }, + { + "name": "island_forest", + "color": "00a000", + "collision": true + } + ] + }, + { + "x": 0.5, + "y": 0.5, + "structureAtlasPath": "world/tilesets/structures.atlas", + "sourcePath": "world/tilesets/island_forest.png", + "maskPath": "world/tilesets/ring.png", + "height": 0.5, + "width": 0.5, + "symmetry": 8, + "periodicOutput": false, + "mappingInfo": [ + { + "name": "water", + "color": "0070a0", + "collision": true + }, + { + "name": "island_forest", + "color": "00a000", + "collision": true + } + ] + } +] } \ No newline at end of file diff --git a/forge-gui/res/adventure/Shandalar/world/enemies.json b/forge-gui/res/adventure/Shandalar/world/enemies.json index da825159683..ca225ead54a 100644 --- a/forge-gui/res/adventure/Shandalar/world/enemies.json +++ b/forge-gui/res/adventure/Shandalar/world/enemies.json @@ -3,7 +3,7 @@ "name": "Adventurer", "sprite": "sprites/swordsman_3.atlas", "deck": "decks/adventurer.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 25, "life": 13, @@ -27,7 +27,8 @@ "name": "Amonkhet Minotaur", "sprite": "sprites/warden.atlas", "deck": "decks/amonkhet_minotaur.dck", - "spawnRate": 1.0, + "ai": "", + "spawnRate": 1, "difficulty": 0.1, "speed": 25, "life": 16, @@ -51,7 +52,7 @@ "name": "Ape", "sprite": "sprites/behemoth.atlas", "deck": "decks/ape.json", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 24, "life": 16, @@ -75,7 +76,7 @@ "name": "Archer", "sprite": "sprites/archer_2.atlas", "deck": "decks/human_archer.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 25, "life": 11, @@ -99,7 +100,7 @@ "name": "Ashmouth Devil", "sprite": "sprites/devil.atlas", "deck": "decks/ashmouth_devil.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 31, "life": 18, @@ -123,7 +124,7 @@ "name": "Axgard Dwarf", "sprite": "sprites/dwarf_8.atlas", "deck": "decks/axgard_dwarf.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 22, "life": 13, @@ -147,7 +148,7 @@ "name": "Bandit", "sprite": "sprites/dwarf_7.atlas", "deck": "decks/bandit.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 24, "life": 13, @@ -171,7 +172,7 @@ "name": "Bear", "sprite": "sprites/bear.atlas", "deck": "decks/bear.json", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 25, "life": 16, @@ -195,7 +196,9 @@ "name": "Beholder", "sprite": "sprites/beholder.atlas", "deck": "decks/beholder.dck", - "spawnRate": 1.0, + "ai": "", + "flying": true, + "spawnRate": 1, "difficulty": 0.1, "speed": 31, "life": 15, @@ -219,7 +222,7 @@ "name": "Berserker", "sprite": "sprites/dwarf_5.atlas", "deck": "decks/berserker.json", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 22, "life": 13, @@ -243,7 +246,7 @@ "name": "Big Zombie", "sprite": "sprites/zombie_2.atlas", "deck": "decks/zombie_bad.json", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 15, "life": 13, @@ -278,7 +281,9 @@ "name": "Bird", "sprite": "sprites/griffin_2.atlas", "deck": "decks/bird_blue.json", - "spawnRate": 1.0, + "ai": "", + "flying": true, + "spawnRate": 1, "difficulty": 0.1, "speed": 31, "life": 11, @@ -302,7 +307,7 @@ "name": "Black Wiz1", "sprite": "sprites/black_wizard.atlas", "deck": "decks/black_bad.json", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 24, "life": 10, @@ -326,7 +331,7 @@ "name": "Black Wiz2", "sprite": "sprites/black_wiz2.atlas", "deck": "decks/fear.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 24, "life": 10, @@ -350,7 +355,7 @@ "name": "Black Wiz3", "sprite": "sprites/black_wiz3.atlas", "deck": "decks/black_wiz3.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 24, "life": 10, @@ -374,7 +379,9 @@ "name": "Blue Wiz1", "sprite": "sprites/mage.atlas", "deck": "decks/blue_bad.json", - "spawnRate": 1.0, + "ai": "", + "flying": true, + "spawnRate": 1, "difficulty": 0.1, "speed": 31, "life": 10, @@ -398,7 +405,7 @@ "name": "Blue Wiz2", "sprite": "sprites/blue_wiz2.atlas", "deck": "decks/mill.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 24, "life": 10, @@ -408,9 +415,10 @@ "probability": 1, "count": 4, "addMaxCount": 6 - },{ + }, + { "type": "gold", - "probability": 1.0, + "probability": 1, "count": 80, "addMaxCount": 40 } @@ -421,7 +429,9 @@ "name": "Blue Wiz3", "sprite": "sprites/mage_2.atlas", "deck": "decks/counter.dck", - "spawnRate": 1.0, + "ai": "", + "flying": true, + "spawnRate": 1, "difficulty": 0.1, "speed": 31, "life": 14, @@ -444,7 +454,7 @@ "name": "Boggart", "sprite": "sprites/goblin_2.atlas", "deck": "decks/eyeblight.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 27, "life": 8, @@ -479,7 +489,7 @@ "name": "Cat", "sprite": "sprites/lion.atlas", "deck": "decks/cat.json", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 27, "life": 15, @@ -503,7 +513,7 @@ "name": "Cathar", "sprite": "sprites/cathar.atlas", "deck": "decks/cathar.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 25, "life": 13, @@ -527,7 +537,7 @@ "name": "Centaur", "sprite": "sprites/centaur.atlas", "deck": "decks/centaur.json", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 30, "life": 16, @@ -551,7 +561,7 @@ "name": "Centaur Warrior", "sprite": "sprites/centaur_2.atlas", "deck": "decks/centaur_warrior.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 30, "life": 16, @@ -575,7 +585,7 @@ "name": "ClayGolem", "sprite": "sprites/golem_2.atlas", "deck": "decks/golem_good.json", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 19, "life": 12, @@ -608,7 +618,7 @@ "name": "Cleric", "sprite": "sprites/cleric.atlas", "deck": "decks/cleric.json", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 24, "life": 10, @@ -632,7 +642,7 @@ "name": "Construct", "sprite": "sprites/golem_3.atlas", "deck": "decks/artificer.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 21, "life": 14, @@ -656,7 +666,7 @@ "name": "Cyclops", "sprite": "sprites/cyclops.atlas", "deck": "decks/cyclops.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 16, "life": 19, @@ -680,7 +690,7 @@ "name": "Dark Knight", "sprite": "sprites/death_knight.atlas", "deck": "decks/death_knight.json", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 30, "life": 15, @@ -704,7 +714,7 @@ "name": "Dawnhart Witch", "sprite": "sprites/dawnhart_witch.atlas", "deck": "decks/dawnhart_witch.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 24, "life": 10, @@ -728,7 +738,7 @@ "name": "Death Knight", "sprite": "sprites/death_knight_2.atlas", "deck": "decks/death_knight.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 30, "life": 15, @@ -752,7 +762,7 @@ "name": "Demon", "sprite": "sprites/demon_3.atlas", "deck": "decks/demon.json", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 31, "life": 18, @@ -776,7 +786,7 @@ "name": "Devil", "sprite": "sprites/imp.atlas", "deck": "decks/devil.json", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 24, "life": 11, @@ -800,7 +810,7 @@ "name": "Dino", "sprite": "sprites/ancient.atlas", "deck": "decks/dinosaurs.json", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 15, "life": 19, @@ -837,7 +847,7 @@ "name": "Dinosaur", "sprite": "sprites/ancient_2.atlas", "deck": "decks/dinosaur_w_r.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 15, "life": 19, @@ -874,7 +884,9 @@ "name": "Djinn", "sprite": "sprites/djinn.atlas", "deck": "decks/djinn.json", - "spawnRate": 1.0, + "ai": "", + "flying": true, + "spawnRate": 1, "difficulty": 0.1, "speed": 32, "life": 18, @@ -908,7 +920,8 @@ "type": "deckCard", "probability": 1, "count": 1 - },{ + }, + { "type": "gold", "probability": 0.3, "count": 100, @@ -921,6 +934,8 @@ "name": "Dragon", "sprite": "sprites/dragon.atlas", "deck": "decks/dragon.dck", + "ai": "", + "flying": true, "spawnRate": 0.95, "difficulty": 0.1, "speed": 31, @@ -945,7 +960,7 @@ "name": "Dwarf", "sprite": "sprites/dwarf_2.atlas", "deck": "decks/dwarf.json", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 22, "life": 13, @@ -969,7 +984,9 @@ "name": "Efreet", "sprite": "sprites/efreet_2.atlas", "deck": "decks/efreet.dck", - "spawnRate": 1.0, + "ai": "", + "flying": true, + "spawnRate": 1, "difficulty": 0.1, "speed": 31, "life": 18, @@ -993,7 +1010,9 @@ "name": "Eldraine Faerie", "sprite": "sprites/pixie.atlas", "deck": "decks/eldraine_faerie.dck", - "spawnRate": 1.0, + "ai": "", + "flying": true, + "spawnRate": 1, "difficulty": 0.1, "speed": 31, "life": 8, @@ -1017,7 +1036,7 @@ "name": "Eldraine Knight", "sprite": "sprites/paladin_2.atlas", "deck": "decks/eldraine_knight.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 30, "life": 14, @@ -1041,7 +1060,9 @@ "name": "Eldrazi", "sprite": "sprites/mindelemental.atlas", "deck": "decks/eldrazi.json", - "spawnRate": 1.0, + "ai": "", + "flying": true, + "spawnRate": 1, "difficulty": 0.1, "speed": 15, "life": 19, @@ -1065,7 +1086,7 @@ "name": "Elemental", "sprite": "sprites/crystalelemental.atlas", "deck": "decks/elemental_blue.json", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 31, "life": 16, @@ -1089,7 +1110,7 @@ "name": "Elf", "sprite": "sprites/druid.atlas", "deck": "decks/elf_bad.json", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 24, "life": 10, @@ -1124,7 +1145,7 @@ "name": "Elf warrior", "sprite": "sprites/hunter.atlas", "deck": "decks/elf_mid.json", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 25, "life": 11, @@ -1159,7 +1180,7 @@ "name": "Elk", "sprite": "sprites/deer_2.atlas", "deck": "decks/elk.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 29, "life": 11, @@ -1183,7 +1204,9 @@ "name": "Faerie", "sprite": "sprites/pixie_2.atlas", "deck": "decks/faerie.json", - "spawnRate": 1.0, + "ai": "", + "flying": true, + "spawnRate": 1, "difficulty": 0.1, "speed": 31, "life": 8, @@ -1207,7 +1230,9 @@ "name": "Fire Elemental", "sprite": "sprites/fireelemental.atlas", "deck": "decks/fire_elemental.dck", - "spawnRate": 1.0, + "ai": "", + "flying": true, + "spawnRate": 1, "difficulty": 0.1, "speed": 24, "life": 16, @@ -1231,7 +1256,7 @@ "name": "Flame Elemental", "sprite": "sprites/magmaelemental.atlas", "deck": "decks/flame_elemental.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 23, "life": 17, @@ -1255,7 +1280,9 @@ "name": "Gargoyle", "sprite": "sprites/gargoyle.atlas", "deck": "decks/gargoyle.json", - "spawnRate": 1.0, + "ai": "", + "flying": true, + "spawnRate": 1, "difficulty": 0.1, "speed": 31, "life": 17, @@ -1279,7 +1306,9 @@ "name": "Gargoyle 2", "sprite": "sprites/gargoyle_2.atlas", "deck": "decks/gargoyle.dck", - "spawnRate": 1.0, + "ai": "", + "flying": true, + "spawnRate": 1, "difficulty": 0.1, "speed": 31, "life": 16, @@ -1303,7 +1332,9 @@ "name": "Geist", "sprite": "sprites/ghost.atlas", "deck": "decks/ghost_blue.dck", - "spawnRate": 1.0, + "ai": "", + "flying": true, + "spawnRate": 1, "difficulty": 0.1, "speed": 32, "life": 15, @@ -1327,7 +1358,7 @@ "name": "Ghoul", "sprite": "sprites/ghoul.atlas", "deck": "decks/ghoul.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 15, "life": 13, @@ -1351,7 +1382,9 @@ "name": "Ghost", "sprite": "sprites/ghost_2.atlas", "deck": "decks/ghost.json", - "spawnRate": 1.0, + "ai": "", + "flying": true, + "spawnRate": 1, "difficulty": 0.1, "speed": 31, "life": 14, @@ -1375,7 +1408,7 @@ "name": "Giant Spider", "sprite": "sprites/spider_2.atlas", "deck": "decks/spider_token.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 23, "life": 15, @@ -1399,7 +1432,7 @@ "name": "Goblin", "sprite": "sprites/goblin.atlas", "deck": "decks/goblin_bad.json", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 27, "life": 8, @@ -1434,7 +1467,7 @@ "name": "Goblin Chief", "sprite": "sprites/wolf_rider_2.atlas", "deck": "decks/goblin_good.json", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 29, "life": 10, @@ -1469,7 +1502,7 @@ "name": "Goblin Warrior", "sprite": "sprites/wolf_rider.atlas", "deck": "decks/goblin_mid.json", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 28, "life": 9, @@ -1594,7 +1627,7 @@ "name": "Gorgon", "sprite": "sprites/gorgone.atlas", "deck": "decks/gorgon.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 25, "life": 14, @@ -1618,7 +1651,7 @@ "name": "Gorgon 2", "sprite": "sprites/gorgonen.atlas", "deck": "decks/gorgon_2.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 25, "life": 14, @@ -1642,7 +1675,7 @@ "name": "Green Beast", "sprite": "sprites/basilisk.atlas", "deck": "decks/beast_green.json", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 22, "life": 18, @@ -1666,7 +1699,7 @@ "name": "Green Wiz1", "sprite": "sprites/green_wiz1.atlas", "deck": "decks/green_bad.json", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 24, "life": 10, @@ -1690,7 +1723,7 @@ "name": "Green Wiz2", "sprite": "sprites/green_wiz2.atlas", "deck": "decks/trample.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 24, "life": 10, @@ -1714,7 +1747,7 @@ "name": "Green Wiz3", "sprite": "sprites/green_wiz3.atlas", "deck": "decks/ramp.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 24, "life": 10, @@ -1738,7 +1771,9 @@ "name": "Griffin", "sprite": "sprites/griffin.atlas", "deck": "decks/griffin.json", - "spawnRate": 1.0, + "ai": "", + "flying": true, + "spawnRate": 1, "difficulty": 0.1, "speed": 32, "life": 15, @@ -1762,7 +1797,9 @@ "name": "Harpy", "sprite": "sprites/harpy.atlas", "deck": "decks/harpy.dck", - "spawnRate": 1.0, + "ai": "", + "flying": true, + "spawnRate": 1, "difficulty": 0.1, "speed": 31, "life": 13, @@ -1786,7 +1823,9 @@ "name": "Harpy 2", "sprite": "sprites/harpy_2.atlas", "deck": "decks/harpy_2.dck", - "spawnRate": 1.0, + "ai": "", + "flying": true, + "spawnRate": 1, "difficulty": 0.1, "speed": 31, "life": 13, @@ -1810,7 +1849,7 @@ "name": "Hellhound", "sprite": "sprites/hellhound_2.atlas", "deck": "decks/hellhound.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 25, "life": 17, @@ -1834,7 +1873,7 @@ "name": "High Elf", "sprite": "sprites/druid_2.atlas", "deck": "decks/elf_good.json", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 26, "life": 12, @@ -1869,7 +1908,9 @@ "name": "High Vampire", "sprite": "sprites/vampire_2.atlas", "deck": "decks/vampire.json", - "spawnRate": 1.0, + "ai": "", + "flying": true, + "spawnRate": 1, "difficulty": 0.1, "speed": 32, "life": 18, @@ -1893,7 +1934,7 @@ "name": "Horseman", "sprite": "sprites/cavalier_2.atlas", "deck": "decks/horsemanship.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 30, "life": 14, @@ -1928,7 +1969,7 @@ "name": "Human", "sprite": "sprites/pikeman.atlas", "deck": "decks/human_bad.json", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 23, "life": 11, @@ -1963,7 +2004,7 @@ "name": "Human elite", "sprite": "sprites/legionite.atlas", "deck": "decks/human_good.json", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 25, "life": 13, @@ -1998,7 +2039,7 @@ "name": "Human guard", "sprite": "sprites/swordsman.atlas", "deck": "decks/human_mid.json", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 24, "life": 12, @@ -2033,7 +2074,7 @@ "name": "Hydra", "sprite": "sprites/hydra.atlas", "deck": "decks/hydra.json", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 15, "life": 19, @@ -2068,7 +2109,7 @@ "name": "Immersturm Demon", "sprite": "sprites/devil_2.atlas", "deck": "decks/immersturm_demon.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 31, "life": 17, @@ -2092,7 +2133,7 @@ "name": "Khan", "sprite": "sprites/cavalier.atlas", "deck": "decks/mardu.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 30, "life": 14, @@ -2116,7 +2157,7 @@ "name": "Knight", "sprite": "sprites/paladin.atlas", "deck": "decks/knight.json", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 30, "life": 14, @@ -2151,7 +2192,7 @@ "name": "Lich", "sprite": "sprites/lich_2.atlas", "deck": "decks/lich.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 20, "life": 17, @@ -2175,7 +2216,7 @@ "name": "Merfolk", "sprite": "sprites/waterelemental.atlas", "deck": "decks/merfolk_bad.json", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 23, "life": 12, @@ -2210,7 +2251,9 @@ "name": "Merfolk Avatar", "sprite": "sprites/iceelemental.atlas", "deck": "decks/merfolk_good.json", - "spawnRate": 1.0, + "ai": "", + "flying": true, + "spawnRate": 1, "difficulty": 0.1, "speed": 25, "life": 14, @@ -2245,7 +2288,7 @@ "name": "Merfolk Fighter", "sprite": "sprites/merfolk.atlas", "deck": "decks/merfolk_lords.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.2, "speed": 22, "life": 16, @@ -2254,23 +2297,34 @@ "type": "deckCard", "probability": 1, "count": 1 - },{ + }, + { "type": "gold", "probability": 1, "count": 100, "addMaxCount": 150 - },{ + }, + { "type": "card", "probability": 0.65, "count": 2, - "colors": [ "Blue" ], - "rarity": [ "Rare" ] - },{ + "colors": [ + "Blue" + ], + "rarity": [ + "Rare" + ] + }, + { "type": "card", "probability": 0.0035, "count": 1, - "colors": [ "Blue" ], - "rarity": [ "Mythic Rare" ] + "colors": [ + "Blue" + ], + "rarity": [ + "Mythic Rare" + ] } ], "colors": "GU" @@ -2279,7 +2333,7 @@ "name": "Merfolk Lord", "sprite": "sprites/merfolk_lord.atlas", "deck": "decks/merfolk_lord2.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.25, "speed": 25, "life": 20, @@ -2288,23 +2342,34 @@ "type": "deckCard", "probability": 1, "count": 1 - },{ + }, + { "type": "gold", "probability": 1, "count": 200, "addMaxCount": 100 - },{ + }, + { "type": "card", "probability": 0.75, "count": 2, - "colors": [ "Blue" ], - "rarity": [ "Rare" ] - },{ + "colors": [ + "Blue" + ], + "rarity": [ + "Rare" + ] + }, + { "type": "card", "probability": 0.005, "count": 1, - "colors": [ "Blue" ], - "rarity": [ "Mythic Rare" ] + "colors": [ + "Blue" + ], + "rarity": [ + "Mythic Rare" + ] } ], "colors": "GU" @@ -2313,7 +2378,7 @@ "name": "Merfolk Soldier", "sprite": "sprites/mermaid.atlas", "deck": "decks/merfolk_v_goblins.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 22, "life": 13, @@ -2348,7 +2413,9 @@ "name": "Merfolk warrior", "sprite": "sprites/airelemental.atlas", "deck": "decks/merfolk_mid.json", - "spawnRate": 1.0, + "ai": "", + "flying": true, + "spawnRate": 1, "difficulty": 0.1, "speed": 24, "life": 13, @@ -2383,7 +2450,7 @@ "name": "Mimic", "sprite": "sprites/mimic.atlas", "deck": "decks/mimic.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 15, "life": 12, @@ -2407,7 +2474,7 @@ "name": "Minotaur", "sprite": "sprites/minotaur.atlas", "deck": "decks/minotaur.json", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 25, "life": 16, @@ -2431,7 +2498,7 @@ "name": "Minotaur Flayer", "sprite": "sprites/warden_2.atlas", "deck": "decks/minotaur.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 25, "life": 16, @@ -2455,7 +2522,7 @@ "name": "Monk", "sprite": "sprites/monk.atlas", "deck": "decks/angel.json", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 24, "life": 10, @@ -2479,7 +2546,7 @@ "name": "Rakdos Devil", "sprite": "sprites/juggler.atlas", "deck": "decks/rakdos_devil.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 24, "life": 11, @@ -2503,7 +2570,7 @@ "name": "Red Beast", "sprite": "sprites/basilisk_2.atlas", "deck": "decks/beast_red.json", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 23, "life": 17, @@ -2527,7 +2594,7 @@ "name": "Red Wiz1", "sprite": "sprites/enchanter.atlas", "deck": "decks/red_bad.json", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 24, "life": 8, @@ -2551,7 +2618,7 @@ "name": "Red Wiz2", "sprite": "sprites/red_wiz2.atlas", "deck": "decks/haste_burn.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 24, "life": 10, @@ -2574,7 +2641,7 @@ "name": "Red Wiz3", "sprite": "sprites/red_wiz3.atlas", "deck": "decks/lava_axe.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 24, "life": 14, @@ -2597,7 +2664,7 @@ "name": "Rogue", "sprite": "sprites/rogue.atlas", "deck": "decks/rogue.json", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 25, "life": 11, @@ -2618,7 +2685,7 @@ "name": "Satyr", "sprite": "sprites/satyr.atlas", "deck": "decks/satyr.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 25, "life": 8, @@ -2642,7 +2709,7 @@ "name": "Sea Monster", "sprite": "sprites/leech_2.atlas", "deck": "decks/sea_monster.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 15, "life": 19, @@ -2666,7 +2733,7 @@ "name": "Shaman", "sprite": "sprites/shaman_2.atlas", "deck": "decks/shaman.json", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 24, "life": 10, @@ -2690,7 +2757,7 @@ "name": "Skeleton", "sprite": "sprites/skeleton.atlas", "deck": "decks/skeleton.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 20, "life": 11, @@ -2713,7 +2780,7 @@ "name": "Skeleton Soldier", "sprite": "sprites/skeleton_2.atlas", "deck": "decks/skeleton_2.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 20, "life": 11, @@ -2736,7 +2803,7 @@ "name": "Sliver", "sprite": "sprites/sliver.atlas", "deck": "decks/sliver.json", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 24, "life": 15, @@ -2756,7 +2823,7 @@ "name": "Snake", "sprite": "sprites/big_snake.atlas", "deck": "decks/snake.json", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 25, "life": 10, @@ -2780,7 +2847,7 @@ "name": "Spider", "sprite": "sprites/spider.atlas", "deck": "decks/spider.json", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 22, "life": 10, @@ -2804,7 +2871,9 @@ "name": "Tarkir Djinn", "sprite": "sprites/djinn_2.atlas", "deck": "decks/djinn_tarkir.dck", - "spawnRate": 1.0, + "ai": "", + "flying": true, + "spawnRate": 1, "difficulty": 0.1, "speed": 31, "life": 18, @@ -2828,7 +2897,7 @@ "name": "Treefolk", "sprite": "sprites/treant.atlas", "deck": "decks/treefolk.json", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 16, "life": 17, @@ -2852,7 +2921,7 @@ "name": "Treefolk Guardian", "sprite": "sprites/treant_2.atlas", "deck": "decks/treefolk.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 16, "life": 17, @@ -2876,7 +2945,7 @@ "name": "Troll", "sprite": "sprites/troll.atlas", "deck": "decks/troll.json", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 25, "life": 18, @@ -2900,7 +2969,9 @@ "name": "Vampire", "sprite": "sprites/vampire.atlas", "deck": "decks/vampire.dck", - "spawnRate": 1.0, + "ai": "", + "flying": true, + "spawnRate": 1, "difficulty": 0.1, "speed": 31, "life": 17, @@ -2917,7 +2988,9 @@ "name": "Vampire Lord", "sprite": "sprites/vampire_3.atlas", "deck": "decks/vampire_blood_token_fly.dck", - "spawnRate": 1.0, + "ai": "", + "flying": true, + "spawnRate": 1, "difficulty": 0.1, "speed": 31, "life": 18, @@ -2934,7 +3007,7 @@ "name": "Viashino", "sprite": "sprites/battler.atlas", "deck": "decks/viashino.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 25, "life": 11, @@ -2958,7 +3031,7 @@ "name": "Viper", "sprite": "sprites/big_snake_2.atlas", "deck": "decks/snake.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 25, "life": 10, @@ -2982,7 +3055,7 @@ "name": "Werewolf", "sprite": "sprites/hellhound.atlas", "deck": "decks/werewolf.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 26, "life": 16, @@ -3005,7 +3078,7 @@ "name": "White Dwarf", "sprite": "sprites/dwarf_6.atlas", "deck": "decks/white_dwarf.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 22, "life": 13, @@ -3029,7 +3102,7 @@ "name": "White Wiz1", "sprite": "sprites/priest.atlas", "deck": "decks/white_bad.json", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 24, "life": 10, @@ -3053,7 +3126,7 @@ "name": "White Wiz2", "sprite": "sprites/white_wiz2.atlas", "deck": "decks/basri.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 24, "life": 10, @@ -3077,7 +3150,7 @@ "name": "White Wiz3", "sprite": "sprites/white_wiz3.atlas", "deck": "decks/human_soldier_token.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 24, "life": 10, @@ -3102,7 +3175,7 @@ "sprite": "sprites/leech.atlas", "deck": "decks/wurm.json", "ai": "reckless", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 15, "life": 19, @@ -3137,7 +3210,7 @@ "name": "Yeti", "sprite": "sprites/yeti_2.atlas", "deck": "decks/yeti.dck", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 25, "life": 16, @@ -3161,7 +3234,7 @@ "name": "Zombie", "sprite": "sprites/zombie.atlas", "deck": "decks/zombie_bad.json", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 15, "life": 12, @@ -3196,7 +3269,7 @@ "name": "Zombie Lord", "sprite": "sprites/lich.atlas", "deck": "decks/zombie_good.json", - "spawnRate": 1.0, + "spawnRate": 1, "difficulty": 0.1, "speed": 21, "life": 18, @@ -3227,18 +3300,13 @@ ], "colors": "B" }, - - - - { "name": "Wounded Sliver", "sprite": "sprites/boss/wsliver.atlas", "deck": "decks/miniboss/sliver_shandalar.dck", - "speed": 0, - "life": 28, - "boss": true, "ai": "reckless", + "boss": true, + "life": 28, "rewards": [ { "type": "deckCard", @@ -3254,17 +3322,15 @@ ], "colors": "GRW" }, - - - - { "name": "Griselbrand", "sprite": "sprites/boss/griselbrand.atlas", "deck": "decks/boss/griselbrand.dck", + "ai": "", + "boss": true, + "flying": true, "speed": 1, "life": 80, - "boss": true, "rewards": [ { "type": "card", @@ -3294,9 +3360,11 @@ "name": "Akroma", "sprite": "sprites/boss/akroma.atlas", "deck": "decks/boss/akroma.dck", + "ai": "", + "boss": true, + "flying": true, "speed": 1, "life": 80, - "boss": true, "rewards": [ { "type": "card", @@ -3326,9 +3394,9 @@ "name": "Ghalta", "sprite": "sprites/boss/ghalta.atlas", "deck": "decks/boss/ghalta.dck", + "boss": true, "speed": 1, "life": 80, - "boss": true, "rewards": [ { "type": "card", @@ -3358,9 +3426,9 @@ "name": "Lorthos", "sprite": "sprites/boss/lorthos.atlas", "deck": "decks/boss/lorthos.dck", + "boss": true, "speed": 1, "life": 80, - "boss": true, "rewards": [ { "type": "card", @@ -3390,9 +3458,11 @@ "name": "Lathliss", "sprite": "sprites/boss/lathiss.atlas", "deck": "decks/boss/lathliss.dck", + "ai": "", + "boss": true, + "flying": true, "speed": 1, "life": 80, - "boss": true, "rewards": [ { "type": "card", @@ -3422,9 +3492,11 @@ "name": "Emrakul", "sprite": "sprites/boss/emrakul.atlas", "deck": "decks/boss/emrakul.dck", + "ai": "", + "boss": true, + "flying": true, "speed": 1, "life": 100, - "boss": true, "rewards": [ { "type": "item", @@ -3448,9 +3520,9 @@ "name": "Sliver Queen", "sprite": "sprites/boss/sliver_queen.atlas", "deck": "decks/boss/sliver_queen.dck", + "boss": true, "speed": 1, "life": 100, - "boss": true, "rewards": [ { "type": "item", diff --git a/forge-gui/res/adventure/Shandalar/world/green.json b/forge-gui/res/adventure/Shandalar/world/green.json index bf0dca678ba..a33990b5b28 100644 --- a/forge-gui/res/adventure/Shandalar/world/green.json +++ b/forge-gui/res/adventure/Shandalar/world/green.json @@ -1,36 +1,135 @@ -{ - "startPointX": 0.22, - "startPointY": 0.43, - "name": "green", - "noiseWeight": 0.5, - "distWeight": 1.5, - "tilesetName":"Green", - "tilesetAtlas":"world/tilesets/terrain.atlas", - "terrain":[ - { - "spriteName":"Green_1", - "min": 0, - "max": 0.2, - "resolution": 10 - },{ - "spriteName":"Green_2", - "min": 0.8, - "max": 1.0, - "resolution": 10 - } - ], - "width": 0.7, - "height": 0.7, - "color": "59a650", - "spriteNames":[ "WoodTree","WoodTree2","Bush","Stump","Moss","Stone","Flower","Wood"] , - "enemies":[ "Ape","Bear","Centaur","Centaur Warrior","Dino","Eldraine Faerie","Elf","Elf warrior","Elk","Faerie","Giant Spider","Gorgon","Gorgon 2","Green Beast","Green Wiz1","Green Wiz2","Green Wiz3","High Elf","Hydra","Satyr","Snake","Spider","Treefolk","Treefolk Guardian","Viper","Werewolf","Wurm" ] , - "pointsOfInterest":[ - "Green Castle", - "Forest Town", - "ElfTown", - "WurmPond", - "Grove", "Grove1", "Grove2", "Grove3", "Grove4", "Grove5", "Grove6", "Grove7", "Grove8", - "CatLairG", "CatLairG1", "CatLairG2", - "CaveG", "CaveG1", "CaveG2", "CaveG3", "CaveG4", "CaveG5", "CaveG6", "CaveG8", "CaveG9", "CaveGB" - ] +{ +"startPointX": 0.22, +"startPointY": 0.43, +"noiseWeight": 0.5, +"distWeight": 1.5, +"name": "green", +"tilesetAtlas": "world/tilesets/terrain.atlas", +"tilesetName": "Green", +"terrain": [ + { + "spriteName": "Green_1", + "max": 0.2, + "resolution": 10 + }, + { + "spriteName": "Green_2", + "min": 0.8, + "max": 1, + "resolution": 10 + } +], +"width": 0.7, +"height": 0.7, +"color": "59a650", +"spriteNames": [ + "WoodTree", + "WoodTree2", + "Bush", + "Stump", + "Moss", + "Stone", + "Flower", + "Wood" +], +"enemies": [ + "Ape", + "Bear", + "Centaur", + "Centaur Warrior", + "Dino", + "Eldraine Faerie", + "Elf", + "Elf warrior", + "Elk", + "Faerie", + "Giant Spider", + "Gorgon", + "Gorgon 2", + "Green Beast", + "Green Wiz1", + "Green Wiz2", + "Green Wiz3", + "High Elf", + "Hydra", + "Satyr", + "Snake", + "Spider", + "Treefolk", + "Treefolk Guardian", + "Viper", + "Werewolf", + "Wurm" +], +"pointsOfInterest": [ + "Green Castle", + "Forest Town", + "ElfTown", + "WurmPond", + "Grove", + "Grove1", + "Grove2", + "Grove3", + "Grove4", + "Grove5", + "Grove6", + "Grove7", + "Grove8", + "CatLairG", + "CatLairG1", + "CatLairG2", + "CaveG", + "CaveG1", + "CaveG2", + "CaveG3", + "CaveG4", + "CaveG5", + "CaveG6", + "CaveG8", + "CaveG9", + "CaveGB" +], +"structures": [ + { + "N": 2, + "x": 0.5, + "y": 0.5, + "structureAtlasPath": "world/tilesets/structures.atlas", + "sourcePath": "world/tilesets/forest.png", + "maskPath": "world/tilesets/circle.png", + "height": 0.20000002, + "width": 0.20000002, + "symmetry": 1, + "mappingInfo": [ + { + "name": "forest", + "color": "007000", + "collision": true + } + ] + }, + { + "N": 2, + "x": 0.5, + "y": 0.5, + "structureAtlasPath": "world/tilesets/structures.atlas", + "sourcePath": "world/tilesets/lake.png", + "maskPath": "world/tilesets/ring.png", + "height": 0.5, + "width": 0.5, + "periodicOutput": false, + "mappingInfo": [ + { + "name": "lake", + "color": "0070a0", + "collision": true + }, + { + "name": "forest2", + "color": "009000", + "collision": true + } + ] + } +] } \ No newline at end of file diff --git a/forge-gui/res/adventure/Shandalar/world/red.json b/forge-gui/res/adventure/Shandalar/world/red.json index b91c3da9f19..b84f9361769 100644 --- a/forge-gui/res/adventure/Shandalar/world/red.json +++ b/forge-gui/res/adventure/Shandalar/world/red.json @@ -1,39 +1,144 @@ { - "startPointX": 0.31, - "startPointY": 0.78, - "name": "red", - "noiseWeight": 0.5, - "distWeight": 1.5, - "tilesetName":"Red", - "tilesetAtlas":"world/tilesets/terrain.atlas", - "terrain":[ - { - "spriteName":"Red_1", - "min": 0, - "max": 0.2, - "resolution": 10 - },{ - "spriteName":"Red_2", - "min": 0.8, - "max": 1.0, - "resolution": 10 - } - ], - "width": 0.7, - "height": 0.7, - "color": "b63729", - "spriteNames":[ "MountainTree","MountainTree2","MountainRock","LargeMountainRock","Gravel"] , - "enemies":[ "Amonkhet Minotaur","Ashmouth Devil","Axgard Dwarf","Berserker","Boggart","Cyclops","Devil","Dinosaur","Dragon","Dwarf","Efreet","Fire Elemental","Flame Elemental","Goblin","Goblin Chief","Goblin Warrior","Hellhound","Immersturm Demon","Khan","Minotaur","Minotaur Flayer","Red Beast","Red Wiz1","Red Wiz2","Red Wiz3","Shaman","Troll","Vampire Lord","Viashino","Yeti" ] , - "pointsOfInterest":[ - "Red Castle", - "Mountain Town", - "YuleTown", - "BarbarianCamp", "BarbarianCamp1", "BarbarianCamp2", - "Maze", "Maze1", "Maze2", "Maze3", - "Fort", "Fort5", - "Factory", "Factory2", "Factory3", - "SnowAbbey", "SnowAbbey1", "SnowAbbey2", - "SkullCaveR", "SkullCaveR1", "SkullCaveR2", - "CaveR", "CaveR1", "CaveR2", "CaveR3", "CaveR4", "CaveR5", "CaveR6", "CaveR7", "CaveR8", "CaveR9", "CaveRA", "CaveRB", "CaveRC", "CaveRE", "CaveRG", "CaveRH", "CaveRJ" - ] +"startPointX": 0.31, +"startPointY": 0.78, +"noiseWeight": 0.5, +"distWeight": 1.5, +"name": "red", +"tilesetAtlas": "world/tilesets/terrain.atlas", +"tilesetName": "Red", +"terrain": [ + { + "spriteName": "Red_1", + "max": 0.2, + "resolution": 10 + }, + { + "spriteName": "Red_2", + "min": 0.8, + "max": 1, + "resolution": 10 + } +], +"width": 0.7, +"height": 0.7, +"color": "b63729", +"spriteNames": [ + "MountainTree", + "MountainTree2", + "MountainRock", + "Gravel" +], +"enemies": [ + "Amonkhet Minotaur", + "Ashmouth Devil", + "Axgard Dwarf", + "Berserker", + "Boggart", + "Cyclops", + "Devil", + "Dinosaur", + "Dragon", + "Dwarf", + "Efreet", + "Fire Elemental", + "Flame Elemental", + "Goblin", + "Goblin Chief", + "Goblin Warrior", + "Hellhound", + "Immersturm Demon", + "Khan", + "Minotaur", + "Minotaur Flayer", + "Red Beast", + "Red Wiz1", + "Red Wiz2", + "Red Wiz3", + "Shaman", + "Troll", + "Vampire Lord", + "Viashino", + "Yeti" +], +"pointsOfInterest": [ + "Red Castle", + "Mountain Town", + "YuleTown", + "BarbarianCamp", + "BarbarianCamp1", + "BarbarianCamp2", + "Maze", + "Maze1", + "Maze2", + "Maze3", + "Fort", + "Fort5", + "Factory", + "Factory2", + "Factory3", + "SnowAbbey", + "SnowAbbey1", + "SnowAbbey2", + "SkullCaveR", + "SkullCaveR1", + "SkullCaveR2", + "CaveR", + "CaveR1", + "CaveR2", + "CaveR3", + "CaveR4", + "CaveR5", + "CaveR6", + "CaveR7", + "CaveR8", + "CaveR9", + "CaveRA", + "CaveRB", + "CaveRC", + "CaveRE", + "CaveRG", + "CaveRH", + "CaveRJ" +], +"structures": [ + { + "N": 2, + "x": 0.5, + "y": 0.5, + "structureAtlasPath": "world/tilesets/structures.atlas", + "sourcePath": "world/tilesets/mountain.png", + "maskPath": "world/tilesets/ring.png", + "height": 0.5, + "width": 0.5, + "periodicOutput": false, + "mappingInfo": [ + { + "name": "mountain", + "color": "a07020", + "collision": true + }, + { + "name": "mountain_forest", + "color": "007000", + "collision": true + } + ] + }, + { + "x": 0.5, + "y": 0.5, + "structureAtlasPath": "world/tilesets/structures.atlas", + "sourcePath": "world/tilesets/lava.png", + "maskPath": "world/tilesets/circle.png", + "height": 0.2, + "width": 0.2, + "mappingInfo": [ + { + "name": "lava", + "color": "ff5000", + "collision": true + } + ] + } +] } \ No newline at end of file diff --git a/forge-gui/res/adventure/Shandalar/world/tilesets/autotiles.png b/forge-gui/res/adventure/Shandalar/world/tilesets/autotiles.png index 8c651dffb08..f5f45fec030 100644 Binary files a/forge-gui/res/adventure/Shandalar/world/tilesets/autotiles.png and b/forge-gui/res/adventure/Shandalar/world/tilesets/autotiles.png differ diff --git a/forge-gui/res/adventure/Shandalar/world/tilesets/circle.png b/forge-gui/res/adventure/Shandalar/world/tilesets/circle.png new file mode 100644 index 00000000000..73f7a8e663b Binary files /dev/null and b/forge-gui/res/adventure/Shandalar/world/tilesets/circle.png differ diff --git a/forge-gui/res/adventure/Shandalar/world/tilesets/deep_swamp.png b/forge-gui/res/adventure/Shandalar/world/tilesets/deep_swamp.png new file mode 100644 index 00000000000..a97692a24ae Binary files /dev/null and b/forge-gui/res/adventure/Shandalar/world/tilesets/deep_swamp.png differ diff --git a/forge-gui/res/adventure/Shandalar/world/tilesets/forest.atlas b/forge-gui/res/adventure/Shandalar/world/tilesets/forest.atlas new file mode 100644 index 00000000000..bb08300ae21 --- /dev/null +++ b/forge-gui/res/adventure/Shandalar/world/tilesets/forest.atlas @@ -0,0 +1,20 @@ + +forest.png +size: 96,64 +format: RGBA8888 +filter: Nearest,Nearest +repeat: none +Source + rotate: false + xy: 0, 0 + size: 16, 16 + orig: 0, 0 + offset: 0, 0 + index: 0 +structure_000000 + rotate: false + xy: 48, 0 + size: 48, 64 + orig: 0, 0 + offset: 0, 0 + index: 0 \ No newline at end of file diff --git a/forge-gui/res/adventure/Shandalar/world/tilesets/forest.png b/forge-gui/res/adventure/Shandalar/world/tilesets/forest.png new file mode 100644 index 00000000000..8bcd553e663 Binary files /dev/null and b/forge-gui/res/adventure/Shandalar/world/tilesets/forest.png differ diff --git a/forge-gui/res/adventure/Shandalar/world/tilesets/hole.png b/forge-gui/res/adventure/Shandalar/world/tilesets/hole.png new file mode 100644 index 00000000000..0f9cd1e0e05 Binary files /dev/null and b/forge-gui/res/adventure/Shandalar/world/tilesets/hole.png differ diff --git a/forge-gui/res/adventure/Shandalar/world/tilesets/island_forest.png b/forge-gui/res/adventure/Shandalar/world/tilesets/island_forest.png new file mode 100644 index 00000000000..d9de72cb85b Binary files /dev/null and b/forge-gui/res/adventure/Shandalar/world/tilesets/island_forest.png differ diff --git a/forge-gui/res/adventure/Shandalar/world/tilesets/lake.png b/forge-gui/res/adventure/Shandalar/world/tilesets/lake.png new file mode 100644 index 00000000000..655270f1f03 Binary files /dev/null and b/forge-gui/res/adventure/Shandalar/world/tilesets/lake.png differ diff --git a/forge-gui/res/adventure/Shandalar/world/tilesets/lava.png b/forge-gui/res/adventure/Shandalar/world/tilesets/lava.png new file mode 100644 index 00000000000..c78188f8d90 Binary files /dev/null and b/forge-gui/res/adventure/Shandalar/world/tilesets/lava.png differ diff --git a/forge-gui/res/adventure/Shandalar/world/tilesets/moon.png b/forge-gui/res/adventure/Shandalar/world/tilesets/moon.png new file mode 100644 index 00000000000..6a1c6639c82 Binary files /dev/null and b/forge-gui/res/adventure/Shandalar/world/tilesets/moon.png differ diff --git a/forge-gui/res/adventure/Shandalar/world/tilesets/moon2.png b/forge-gui/res/adventure/Shandalar/world/tilesets/moon2.png new file mode 100644 index 00000000000..57c48364141 Binary files /dev/null and b/forge-gui/res/adventure/Shandalar/world/tilesets/moon2.png differ diff --git a/forge-gui/res/adventure/Shandalar/world/tilesets/moon3.png b/forge-gui/res/adventure/Shandalar/world/tilesets/moon3.png new file mode 100644 index 00000000000..2fe39f08bf1 Binary files /dev/null and b/forge-gui/res/adventure/Shandalar/world/tilesets/moon3.png differ diff --git a/forge-gui/res/adventure/Shandalar/world/tilesets/moon4.png b/forge-gui/res/adventure/Shandalar/world/tilesets/moon4.png new file mode 100644 index 00000000000..a124ae379ae Binary files /dev/null and b/forge-gui/res/adventure/Shandalar/world/tilesets/moon4.png differ diff --git a/forge-gui/res/adventure/Shandalar/world/tilesets/mountain.png b/forge-gui/res/adventure/Shandalar/world/tilesets/mountain.png new file mode 100644 index 00000000000..01d8d464fd4 Binary files /dev/null and b/forge-gui/res/adventure/Shandalar/world/tilesets/mountain.png differ diff --git a/forge-gui/res/adventure/Shandalar/world/tilesets/mountain_forest.png b/forge-gui/res/adventure/Shandalar/world/tilesets/mountain_forest.png new file mode 100644 index 00000000000..224e0d9138e Binary files /dev/null and b/forge-gui/res/adventure/Shandalar/world/tilesets/mountain_forest.png differ diff --git a/forge-gui/res/adventure/Shandalar/world/tilesets/plains_forest.png b/forge-gui/res/adventure/Shandalar/world/tilesets/plains_forest.png new file mode 100644 index 00000000000..98ebe05fbf6 Binary files /dev/null and b/forge-gui/res/adventure/Shandalar/world/tilesets/plains_forest.png differ diff --git a/forge-gui/res/adventure/Shandalar/world/tilesets/plateau.png b/forge-gui/res/adventure/Shandalar/world/tilesets/plateau.png new file mode 100644 index 00000000000..b415a0c76d9 Binary files /dev/null and b/forge-gui/res/adventure/Shandalar/world/tilesets/plateau.png differ diff --git a/forge-gui/res/adventure/Shandalar/world/tilesets/ring.png b/forge-gui/res/adventure/Shandalar/world/tilesets/ring.png new file mode 100644 index 00000000000..759eb1645c8 Binary files /dev/null and b/forge-gui/res/adventure/Shandalar/world/tilesets/ring.png differ diff --git a/forge-gui/res/adventure/Shandalar/world/tilesets/ring2.png b/forge-gui/res/adventure/Shandalar/world/tilesets/ring2.png new file mode 100644 index 00000000000..90636b7824d Binary files /dev/null and b/forge-gui/res/adventure/Shandalar/world/tilesets/ring2.png differ diff --git a/forge-gui/res/adventure/Shandalar/world/tilesets/structures.atlas b/forge-gui/res/adventure/Shandalar/world/tilesets/structures.atlas new file mode 100644 index 00000000000..9ea32b0053c --- /dev/null +++ b/forge-gui/res/adventure/Shandalar/world/tilesets/structures.atlas @@ -0,0 +1,60 @@ + +structures.png +size: 288,384 +format: RGBA8888 +filter: Nearest,Nearest +repeat: none +forest + xy: 0, 0 + size: 48, 64 +lake + xy: 48, 0 + size: 48, 64 +forest2 + xy: 96, 0 + size: 48, 64 +swamp_forest + xy: 0, 64 + size: 48, 64 +swamp_forest2 + xy: 48, 64 + size: 48, 64 +structure + xy: 96, 64 + size: 48, 64 +swamp_water + xy: 144, 64 + size: 48, 64 +deep_swamp + xy: 192, 64 + size: 48, 64 +mountain + xy: 0, 128 + size: 48, 64 +lava + xy: 48, 128 + size: 48, 64 +mountain_forest + xy: 96, 128 + size: 48, 64 +plateau + xy: 0, 192 + size: 48, 64 +plains_forest + xy: 48, 192 + size: 48, 64 +water + xy: 0, 256 + size: 48, 64 +island_forest + xy: 48, 256 + size: 48, 64 +hole + xy: 0, 320 + size: 48, 64 +waste_mountain + xy: 48, 320 + size: 48, 64 +waste_structure + xy: 96, 320 + size: 48, 64 \ No newline at end of file diff --git a/forge-gui/res/adventure/Shandalar/world/tilesets/structures.png b/forge-gui/res/adventure/Shandalar/world/tilesets/structures.png new file mode 100644 index 00000000000..b77300326d7 Binary files /dev/null and b/forge-gui/res/adventure/Shandalar/world/tilesets/structures.png differ diff --git a/forge-gui/res/adventure/Shandalar/world/tilesets/swamp.png b/forge-gui/res/adventure/Shandalar/world/tilesets/swamp.png new file mode 100644 index 00000000000..9cf0d6c324b Binary files /dev/null and b/forge-gui/res/adventure/Shandalar/world/tilesets/swamp.png differ diff --git a/forge-gui/res/adventure/Shandalar/world/tilesets/swamp_forest.png b/forge-gui/res/adventure/Shandalar/world/tilesets/swamp_forest.png new file mode 100644 index 00000000000..6ab68545d71 Binary files /dev/null and b/forge-gui/res/adventure/Shandalar/world/tilesets/swamp_forest.png differ diff --git a/forge-gui/res/adventure/Shandalar/world/tilesets/swamp_ruins.png b/forge-gui/res/adventure/Shandalar/world/tilesets/swamp_ruins.png new file mode 100644 index 00000000000..34904ade0c2 Binary files /dev/null and b/forge-gui/res/adventure/Shandalar/world/tilesets/swamp_ruins.png differ diff --git a/forge-gui/res/adventure/Shandalar/world/tilesets/waste_structure.png b/forge-gui/res/adventure/Shandalar/world/tilesets/waste_structure.png new file mode 100644 index 00000000000..a1f6fc80f99 Binary files /dev/null and b/forge-gui/res/adventure/Shandalar/world/tilesets/waste_structure.png differ diff --git a/forge-gui/res/adventure/Shandalar/world/tilesets/water.png b/forge-gui/res/adventure/Shandalar/world/tilesets/water.png new file mode 100644 index 00000000000..ed435a73e12 Binary files /dev/null and b/forge-gui/res/adventure/Shandalar/world/tilesets/water.png differ diff --git a/forge-gui/res/adventure/Shandalar/world/waste.json b/forge-gui/res/adventure/Shandalar/world/waste.json index 750ed106b10..c52a5808958 100644 --- a/forge-gui/res/adventure/Shandalar/world/waste.json +++ b/forge-gui/res/adventure/Shandalar/world/waste.json @@ -1,37 +1,141 @@ { - "startPointX": 0.5, - "startPointY": 0.5, - "name": "waste", - "noiseWeight": 0.3, - "distWeight": 1.0, - "tilesetName":"Waste", - "tilesetAtlas":"world/tilesets/terrain.atlas", - "terrain":[ - { - "spriteName":"Waste_1", - "min": 0, - "max": 0.2, - "resolution": 5 - }, { - "spriteName":"Waste_2", - "min": 0.8, - "max": 1.0, - "resolution": 5 - } - ], - "width": 0.85, - "height": 0.85, - "color": "aeaeae", - "spriteNames":[ "WasteTree","Stone","WasteRock"] , - "enemies":[ "Bandit","ClayGolem","Construct","Eldrazi","Gargoyle","Gargoyle 2","Golem","Sliver","Black Wiz1","Black Wiz2","Black Wiz3","Blue Wiz1","Blue Wiz2","Blue Wiz3","Green Wiz1","Green Wiz2","Green Wiz3","Red Wiz1","Red Wiz2","Red Wiz3","White Wiz1","White Wiz2","White Wiz3","Doppelganger" ] , - "pointsOfInterest":[ - "Final Castle", - "Colorless Castle", - "Skep", - "Waste Town", - "Fort", "Fort1", "Fort2", "Fort3", "Fort4", "Fort5", "Fort6", - "Factory", "Factory1", "Factory2", "Factory3", - "MageTowerC", "MageTowerC1", "MageTowerC2", "MageTowerC3", "MageTowerC4", "MageTowerC5", - "CaveC", "CaveC1", "CaveC2", "CaveC3", "CaveC4", "CaveC5", "CaveC6", "CaveC7", "CaveC8", "CaveC9", "CaveCA", "CaveCB", "CaveCD" - ] +"startPointX": 0.5, +"startPointY": 0.5, +"noiseWeight": 0.3, +"distWeight": 1, +"name": "waste", +"tilesetAtlas": "world/tilesets/terrain.atlas", +"tilesetName": "Waste", +"terrain": [ + { + "spriteName": "Waste_1", + "max": 0.2, + "resolution": 5 + }, + { + "spriteName": "Waste_2", + "min": 0.8, + "max": 1, + "resolution": 5 + } +], +"width": 0.85, +"height": 0.85, +"color": "aeaeae", +"spriteNames": [ + "WasteTree", + "Stone", + "WasteRock" +], +"enemies": [ + "Bandit", + "ClayGolem", + "Construct", + "Eldrazi", + "Gargoyle", + "Gargoyle 2", + "Golem", + "Sliver", + "Black Wiz1", + "Black Wiz2", + "Black Wiz3", + "Blue Wiz1", + "Blue Wiz2", + "Blue Wiz3", + "Green Wiz1", + "Green Wiz2", + "Green Wiz3", + "Red Wiz1", + "Red Wiz2", + "Red Wiz3", + "White Wiz1", + "White Wiz2", + "White Wiz3", + "Doppelganger" +], +"pointsOfInterest": [ + "Final Castle", + "Colorless Castle", + "Skep", + "Waste Town", + "Fort", + "Fort1", + "Fort2", + "Fort3", + "Fort4", + "Fort5", + "Fort6", + "Factory", + "Factory1", + "Factory2", + "Factory3", + "MageTowerC", + "MageTowerC1", + "MageTowerC2", + "MageTowerC3", + "MageTowerC4", + "MageTowerC5", + "CaveC", + "CaveC1", + "CaveC2", + "CaveC3", + "CaveC4", + "CaveC5", + "CaveC6", + "CaveC7", + "CaveC8", + "CaveC9", + "CaveCA", + "CaveCB", + "CaveCD" +], +"structures": [ + { + "N": 2, + "x": 0.5, + "y": 0.5, + "structureAtlasPath": "world/tilesets/structures.atlas", + "sourcePath": "world/tilesets/waste_structure.png", + "maskPath": "world/tilesets/circle.png", + "periodicInput": false, + "height": 0.20000002, + "width": 0.20000002, + "symmetry": 4, + "mappingInfo": [ + { + "name": "waste_structure", + "color": "444444", + "collision": true + }, + { + "name": "waste_mountain", + "color": "9a9a9a", + "collision": true + } + ] + }, + { + "x": 0.5, + "y": 0.5, + "structureAtlasPath": "world/tilesets/structures.atlas", + "sourcePath": "world/tilesets/hole.png", + "maskPath": "world/tilesets/ring.png", + "height": 0.5, + "width": 0.5, + "symmetry": 8, + "periodicOutput": false, + "mappingInfo": [ + { + "name": "hole", + "color": "111111", + "collision": true + }, + { + "name": "waste_mountain", + "color": "9a9a9a", + "collision": true + } + ] + } +] } \ No newline at end of file diff --git a/forge-gui/res/adventure/Shandalar/world/white.json b/forge-gui/res/adventure/Shandalar/world/white.json index 86b8b226a0a..e47b3757962 100644 --- a/forge-gui/res/adventure/Shandalar/world/white.json +++ b/forge-gui/res/adventure/Shandalar/world/white.json @@ -1,37 +1,114 @@ -{ - "startPointX": 0.5, - "startPointY": 0.22, - "name": "white", - "noiseWeight": 0.5, - "distWeight": 1.5, - "tilesetName":"White", - "tilesetAtlas":"world/tilesets/terrain.atlas", - "terrain":[ - { - "spriteName":"White_1", - "min": 0, - "max": 0.2, - "resolution": 10 - }, { - "spriteName":"White_2", - "min": 0.8, - "max": 1.0, - "resolution": 10 - } - ], - "width": 0.7, - "height": 0.7, - "color": "efe697", - "spriteNames":[ "PlainsTree", "Cactus" ,"PlainsRock" ,"LargePlainsRock" ,"DarkGras" ], - "enemies":[ "Adventurer","Archer","Cat","Cathar","Cleric","Dawnhart Witch","Eldraine Knight","Griffin","Horseman","Human","Human elite","Human guard","Knight","Monk","White Dwarf","White Wiz1","White Wiz2","White Wiz3" ] , - "pointsOfInterest":[ - "White Castle", - "Plains Town", - "Monastery", "Monastery1", "Monastery2", "Monastery3", "Monastery4", - "Castle", "Castle1", "Castle2", - "Fort7", "Fort8", "Fort9", - "CatLairW", "CatLairW1", "CatLairW2", - "NestW", - "CaveW", "CaveW1", "CaveW2", "CaveW3", "CaveW4", "CaveW5", "CaveW6" - ] +{ +"startPointX": 0.5, +"startPointY": 0.22, +"noiseWeight": 0.5, +"distWeight": 1.5, +"name": "white", +"tilesetAtlas": "world/tilesets/terrain.atlas", +"tilesetName": "White", +"terrain": [ + { + "spriteName": "White_1", + "max": 0.2, + "resolution": 10 + }, + { + "spriteName": "White_2", + "min": 0.8, + "max": 1, + "resolution": 10 + } +], +"width": 0.7, +"height": 0.7, +"color": "efe697", +"spriteNames": [ + "PlainsTree", + "Cactus", + "PlainsRock", + "DarkGras" +], +"enemies": [ + "Adventurer", + "Archer", + "Cat", + "Cathar", + "Cleric", + "Dawnhart Witch", + "Eldraine Knight", + "Griffin", + "Horseman", + "Human", + "Human elite", + "Human guard", + "Knight", + "Monk", + "White Dwarf", + "White Wiz1", + "White Wiz2", + "White Wiz3" +], +"pointsOfInterest": [ + "White Castle", + "Plains Town", + "Monastery", + "Monastery1", + "Monastery2", + "Monastery3", + "Monastery4", + "Castle", + "Castle1", + "Castle2", + "Fort7", + "Fort8", + "Fort9", + "CatLairW", + "CatLairW1", + "CatLairW2", + "NestW", + "CaveW", + "CaveW1", + "CaveW2", + "CaveW3", + "CaveW4", + "CaveW5", + "CaveW6" +], +"structures": [ + { + "N": 2, + "x": 0.5, + "y": 0.5, + "structureAtlasPath": "world/tilesets/structures.atlas", + "sourcePath": "world/tilesets/plains_forest.png", + "maskPath": "world/tilesets/circle.png", + "height": 0.20000002, + "width": 0.20000002, + "symmetry": 8, + "mappingInfo": [ + { + "name": "plains_forest", + "color": "9c4000", + "collision": true + } + ] + }, + { + "x": 0.5, + "y": 0.5, + "structureAtlasPath": "world/tilesets/structures.atlas", + "sourcePath": "world/tilesets/plateau.png", + "maskPath": "world/tilesets/ring.png", + "height": 0.5, + "width": 0.5, + "periodicOutput": false, + "mappingInfo": [ + { + "name": "plateau", + "color": "caaa66", + "collision": true + } + ] + } +] } \ No newline at end of file diff --git a/forge-gui/res/adventure/Shandalar/world/world.json b/forge-gui/res/adventure/Shandalar/world/world.json index b81be8b8a79..66fec550a30 100644 --- a/forge-gui/res/adventure/Shandalar/world/world.json +++ b/forge-gui/res/adventure/Shandalar/world/world.json @@ -1,16 +1,24 @@ { "width": 700, "height": 700, -"playerStartPosY": 0.495, "playerStartPosX": 0.5025, -"noiseZoomBiome": 30, -"tileSize": 16, -"maxRoadDistance": 1000, -"roadTileset":{ - "tilesetName":"Road", - "tilesetAtlas":"world/tilesets/terrain.atlas", - "color": "ffffff" +"playerStartPosY": 0.495, +"noiseZoomBiome": 30, +"tileSize": 16, +"roadTileset": { + "tilesetAtlas": "world/tilesets/terrain.atlas", + "tilesetName": "Road", + "color": "ffffff" }, -"biomesSprites":"world/sprites/map_sprites.json", -"biomesNames": [ "world/base.json","world/waste.json","world/white.json","world/blue.json","world/black.json","world/red.json","world/green.json"] +"biomesSprites": "world/sprites/map_sprites.json", +"maxRoadDistance": 1000, +"biomesNames": [ + "world/base.json", + "world/waste.json", + "world/white.json", + "world/blue.json", + "world/black.json", + "world/red.json", + "world/green.json" +] } \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/e/ersatz_gnomes.txt b/forge-gui/res/cardsfolder/e/ersatz_gnomes.txt index 8cae6a8194b..4a3dfbeab8a 100644 --- a/forge-gui/res/cardsfolder/e/ersatz_gnomes.txt +++ b/forge-gui/res/cardsfolder/e/ersatz_gnomes.txt @@ -2,7 +2,7 @@ Name:Ersatz Gnomes ManaCost:3 Types:Artifact Creature Gnome PT:1/1 -A:AB$ Animate | Cost$ T | Colors$ Colorless | OverwriteColors$ True | ValidTgts$ Card | TgtZone$ Stack | TgtPrompt$ Select target spell to make colorless | Duration$ Permanent | SpellDescription$ Target spell becomes colorless. +A:AB$ Animate | Cost$ T | Colors$ Colorless | OverwriteColors$ True | ValidTgts$ Card | TargetType$ Spell | TgtZone$ Stack | TgtPrompt$ Select target spell to make colorless | Duration$ Permanent | SpellDescription$ Target spell becomes colorless. A:AB$ Animate | Cost$ T | Colors$ Colorless | OverwriteColors$ True | ValidTgts$ Permanent | TgtPrompt$ Select target permanent to make colorless | SpellDescription$ Target permanent becomes colorless until end of turn. AI:RemoveDeck:Random AI:RemoveDeck:All diff --git a/forge-gui/res/cardsfolder/f/failure_comply.txt b/forge-gui/res/cardsfolder/f/failure_comply.txt index 78065f5ff40..e4e38eac007 100644 --- a/forge-gui/res/cardsfolder/f/failure_comply.txt +++ b/forge-gui/res/cardsfolder/f/failure_comply.txt @@ -1,7 +1,7 @@ Name:Failure ManaCost:1 U Types:Instant -A:SP$ ChangeZone | Cost$ 1 U | ValidTgts$ Card | TgtZone$ Stack | Origin$ Stack | Fizzle$ True | Destination$ Hand | SpellDescription$ Return target spell to its owner's hand. +A:SP$ ChangeZone | Cost$ 1 U | ValidTgts$ Card | TargetType$ Spell | TgtZone$ Stack | Origin$ Stack | Fizzle$ True | Destination$ Hand | SpellDescription$ Return target spell to its owner's hand. AlternateMode:Split Oracle:Return target spell to its owner's hand. diff --git a/forge-gui/res/cardsfolder/f/frost_bite.txt b/forge-gui/res/cardsfolder/f/frost_bite.txt index 66e39ee5bdb..933ec63b7a8 100644 --- a/forge-gui/res/cardsfolder/f/frost_bite.txt +++ b/forge-gui/res/cardsfolder/f/frost_bite.txt @@ -1,8 +1,8 @@ Name:Frost Bite ManaCost:R Types:Snow Instant -A:SP$ DealDamage | Cost$ R | ValidTgts$ Creature,Planeswalker | TgtPrompt$ Select target creature or planeswalker | NumDmg$ 2 | SubAbility$ DBDamage | ConditionCheckSVar$ X | ConditionSVarCompare$ LT3 | SpellDescription$ CARDNAME deals 2 damage to target creature or planeswalker. -SVar:DBDamage:DB$ DealDamage | Defined$ Targeted | NumDmg$ 3 | ConditionCheckSVar$ X | ConditionSVarCompare$ GE3 | SpellDescription$ If you control three or more snow permanents, it deals 3 damage instead. -SVar:X:Count$Valid Permanent.Snow+YouCtrl +A:SP$ DealDamage | ValidTgts$ Creature,Planeswalker | TgtPrompt$ Select target creature or planeswalker | NumDmg$ X | SpellDescription$ CARDNAME deals 2 damage to target creature or planeswalker. If you control three or more snow permanents, it deals 3 damage instead. +SVar:X:Count$Compare Y GE3.3.2 +SVar:Y:Count$Valid Permanent.Snow+YouCtrl DeckHints:Type$Snow Oracle:Frost Bite deals 2 damage to target creature or planeswalker. If you control three or more snow permanents, it deals 3 damage instead. diff --git a/forge-gui/res/cardsfolder/g/gales_redirection.txt b/forge-gui/res/cardsfolder/g/gales_redirection.txt index 2d85425ef4d..7ed1a535b27 100644 --- a/forge-gui/res/cardsfolder/g/gales_redirection.txt +++ b/forge-gui/res/cardsfolder/g/gales_redirection.txt @@ -1,7 +1,7 @@ Name:Gale's Redirection ManaCost:3 U U Types:Instant -A:SP$ ChangeZone | ValidTgts$ Card | TgtZone$ Stack | Origin$ Stack | Fizzle$ True | Destination$ Exile | TgtPrompt$ Choose target spell to exile | RememberChanged$ True | SubAbility$ DBRoll | SpellDescription$ Exile target spell. +A:SP$ ChangeZone | ValidTgts$ Card | TargetType$ Spell | TgtZone$ Stack | Origin$ Stack | Fizzle$ True | Destination$ Exile | TgtPrompt$ Choose target spell to exile | RememberChanged$ True | SubAbility$ DBRoll | SpellDescription$ Exile target spell. SVar:DBRoll:DB$ RollDice | Sides$ 20 | Modifier$ Y | ResultSubAbilities$ 1-14:DBMayPlay,Else:DBMayPlayWithoutCost | StackDescription$ SpellDescription | SpellDescription$ Roll a d20 and add that spell's mana value. SVar:DBMayPlay:DB$ Effect | StaticAbilities$ STPlay | RememberObjects$ Remembered | Duration$ Permanent | ExileOnMoved$ Exile | SubAbility$ DBCleanup | SpellDescription$ 1—14 VERT You may cast that card for as long as it remains exiled, and you may spend mana as though it were mana of any color to cast it. SVar:DBMayPlayWithoutCost:DB$ Effect | StaticAbilities$ STPlayWithoutCost | RememberObjects$ Remembered | Duration$ Permanent | ExileOnMoved$ Exile | SubAbility$ DBCleanup | SpellDescription$ 15+ VERT You may cast that card without paying its mana cost for as long as it remains exiled. diff --git a/forge-gui/res/cardsfolder/h/hullbreaker_horror.txt b/forge-gui/res/cardsfolder/h/hullbreaker_horror.txt index 2767f547ed7..3954ba50c29 100644 --- a/forge-gui/res/cardsfolder/h/hullbreaker_horror.txt +++ b/forge-gui/res/cardsfolder/h/hullbreaker_horror.txt @@ -6,6 +6,6 @@ K:Flash K:This spell can't be countered. T:Mode$ SpellCast | ValidCard$ Card | ValidActivatingPlayer$ You | Execute$ TrigCharm | TriggerZones$ Battlefield | TriggerDescription$ Whenever you cast a spell, ABILITY SVar:TrigCharm:DB$ Charm | Choices$ ControlReturn,ControlBounce | MinCharmNum$ 0 | CharmNum$ 1 -SVar:ControlReturn:DB$ ChangeZone | ValidTgts$ Card.YouDontCtrl | TgtPrompt$ Select target spell you don't control | TgtZone$ Stack | Origin$ Stack | Fizzle$ True | Destination$ Hand | SpellDescription$ Return target spell you don't control to its owner's hand. +SVar:ControlReturn:DB$ ChangeZone | ValidTgts$ Card.YouDontCtrl | TargetType$ Spell | TgtPrompt$ Select target spell you don't control | TgtZone$ Stack | Origin$ Stack | Fizzle$ True | Destination$ Hand | SpellDescription$ Return target spell you don't control to its owner's hand. SVar:ControlBounce:DB$ ChangeZone | ValidTgts$ Permanent.nonLand | TgtPrompt$ Select target nonland permanent | TgtZone$ Battlefield | Origin$ Battlefield | Destination$ Hand | SpellDescription$ Return target nonland permanent to its owner's hand. Oracle:Flash\nThis spell can't be countered.\nWhenever you cast a spell, choose up to one —\n• Return target spell you don't control to its owner's hand.\n• Return target nonland permanent to its owner's hand. diff --git a/forge-gui/res/cardsfolder/l/loose_in_the_park.txt b/forge-gui/res/cardsfolder/l/loose_in_the_park.txt new file mode 100644 index 00000000000..746f9eda835 --- /dev/null +++ b/forge-gui/res/cardsfolder/l/loose_in_the_park.txt @@ -0,0 +1,13 @@ +Name:Loose in the Park +ManaCost:1 G +Types:Enchantment Aura +K:Enchant land +A:SP$ Attach | ValidTgts$ Land | AILogic$ Pump +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ FreeCard | TriggerDescription$ When CARDNAME enters the battlefield, draw a card, then draft a card from CARDNAME's spellbook and exile it. +SVar:FreeCard:DB$ Draw | Defined$ You | NumCards$ 1 | SubAbility$ TrigDraft +SVar:TrigDraft:DB$ Draft | TriggerZones$ Battlefield | Spellbook$ Bristling Boar,Enraged Ceratok,Exuberant Wolfbear,Gaea's Protector,Master Symmetrist,Ornery Dilophosaur,Overgrown Armasaur,Predatory Wurm,Prized Unicorn,Sabertooth Mauler,Spike-Tailed Ceratops,Spore Crawler,Thrashing Brontodon,Wardscale Crocodile,World Shaper | Zone$ Exile | RememberDrafted$ True +A:AB$ Clone | Cost$ 3 | Defined$ Remembered | CloneTarget$ Enchanted | PumpKeywords$ Haste | AddTypes$ Land | Duration$ UntilEndOfTurn | SpellDescription$ Enchanted land becomes a copy of the exiled card until end of turn and gains haste. It's still a land. +SVar:NonStackingAttachEffect:True +DeckHas:Type$Boar|Rhino|Wolf|Bear|Elemental|Warrior|Rhino|Druid|Dinosaur|Wurm|Unicorn|Cat|Fungus|Crocodile|Merfolk|Shaman|Saproling & Ability$Mill|Graveyard|Sacrifice|Counters|Token +DeckHints:Type$Human|Garruk +Oracle:Enchant land\nWhen Loose in the Park enters the battlefield, draw a card, then draft a card from Loose in the Park's spellbook and exile it.\n{3}: Enchanted land becomes a copy of the exiled card until end of turn and gains haste. It's still a land. diff --git a/forge-gui/res/cardsfolder/m/menagerie_curator.txt b/forge-gui/res/cardsfolder/m/menagerie_curator.txt new file mode 100644 index 00000000000..7cd8c497590 --- /dev/null +++ b/forge-gui/res/cardsfolder/m/menagerie_curator.txt @@ -0,0 +1,8 @@ +Name:Menagerie Curator +ManaCost:1 G +Types:Creature Human Citizen +PT:1/3 +A:AB$ Mana | Cost$ T | Produced$ Any | Amount$ 1 | RestrictValid$ Spell.Creature | SpellDescription$ Add one mana of any color. Spend this mana only to cast creature spells. +T:Mode$ SpellCast | TriggerZones$ Battlefield | ValidCard$ Card.Creature+!sharesCreatureTypeWith ValidLibrary Creature.YouOwn | ValidActivatingPlayer$ You | NoResolvingCheck$ True | Execute$ TrigDraw | TriggerDescription$ Whenever you cast a creature spell that doesn't share a creature type with a creature type in your library, draw a card. +SVar:TrigDraw:DB$ Draw | Defined$ You | NumCards$ 1 +Oracle:{T}: Add one mana of any color. Spend this mana only to cast a creature spell.\nWhenever you cast a creature spell that doesn't share a creature type with a creature card in your library, draw a card. diff --git a/forge-gui/res/cardsfolder/o/obscura_interceptor.txt b/forge-gui/res/cardsfolder/o/obscura_interceptor.txt index ec64d336b6c..0d0d562f9ef 100644 --- a/forge-gui/res/cardsfolder/o/obscura_interceptor.txt +++ b/forge-gui/res/cardsfolder/o/obscura_interceptor.txt @@ -7,6 +7,6 @@ K:Lifelink T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigConnive | TriggerDescription$ When CARDNAME enters the battlefield, it connives. When it connives this way, return up to one target spell to its owner's hand. (To have a creature connive, draw a card, then discard a card. If you discarded a nonland card, put a +1/+1 counter on that creature.) SVar:TrigConnive:DB$ Connive | SubAbility$ DBImmediateTrigger SVar:DBImmediateTrigger:DB$ ImmediateTrigger | Execute$ TrigReturn | TriggerDescription$ When it connives this way, return up to one target spell to its owner's hand. -SVar:TrigReturn:DB$ ChangeZone | ValidTgts$ Card | TgtPrompt$ Select target spell | TargetMax$ 1 | TargetMin$ 0 | TgtZone$ Stack | Origin$ Stack | Fizzle$ True | Destination$ Hand +SVar:TrigReturn:DB$ ChangeZone | ValidTgts$ Card | TgtPrompt$ Select target spell | TargetMax$ 1 | TargetMin$ 0 | TgtZone$ Stack | TargetType$ Spell | Origin$ Stack | Fizzle$ True | Destination$ Hand DeckHas:Ability$LifeGain|Discard|Counters Oracle:Flash\nLifelink\nWhen Obscura Interceptor enters the battlefield, it connives. When it connives this way, return up to one target spell to its owner's hand. (To have a creature connive, draw a card, then discard a card. If you discarded a nonland card, put a +1/+1 counter on that creature.) diff --git a/forge-gui/res/cardsfolder/p/parallectric_feedback.txt b/forge-gui/res/cardsfolder/p/parallectric_feedback.txt index 09eb1bdd488..cbc1450ac24 100644 --- a/forge-gui/res/cardsfolder/p/parallectric_feedback.txt +++ b/forge-gui/res/cardsfolder/p/parallectric_feedback.txt @@ -1,7 +1,7 @@ Name:Parallectric Feedback ManaCost:3 R Types:Instant -A:SP$ Pump | Cost$ 3 R | ValidTgts$ Card | TgtZone$ Stack | TgtPrompt$ Select target spell | PumpZone$ Stack | StackDescription$ None | SubAbility$ DBDmg | SpellDescription$ CARDNAME deals damage to target spell's controller equal to that spell's mana value. +A:SP$ Pump | Cost$ 3 R | ValidTgts$ Card | TargetType$ Spell | TgtZone$ Stack | TgtPrompt$ Select target spell | PumpZone$ Stack | StackDescription$ None | SubAbility$ DBDmg | SpellDescription$ CARDNAME deals damage to target spell's controller equal to that spell's mana value. SVar:DBDmg:DB$ DealDamage | Defined$ TargetedController | NumDmg$ X SVar:X:Targeted$CardManaCost AI:RemoveDeck:All diff --git a/forge-gui/res/cardsfolder/r/refuse_cooperate.txt b/forge-gui/res/cardsfolder/r/refuse_cooperate.txt index d6c4e708630..6f1e84ff971 100644 --- a/forge-gui/res/cardsfolder/r/refuse_cooperate.txt +++ b/forge-gui/res/cardsfolder/r/refuse_cooperate.txt @@ -1,7 +1,7 @@ Name:Refuse ManaCost:3 R Types:Instant -A:SP$ Pump | Cost$ 3 R | ValidTgts$ Card | TgtZone$ Stack | TgtPrompt$ Select target spell | PumpZone$ Stack | StackDescription$ None | SubAbility$ DBDmg | SpellDescription$ CARDNAME deals damage to target spell's controller equal to that spell's mana value. +A:SP$ Pump | Cost$ 3 R | ValidTgts$ Card | TargetType$ Spell | TgtZone$ Stack | TgtPrompt$ Select target spell | PumpZone$ Stack | StackDescription$ None | SubAbility$ DBDmg | SpellDescription$ CARDNAME deals damage to target spell's controller equal to that spell's mana value. SVar:DBDmg:DB$ DealDamage | Defined$ TargetedController | NumDmg$ X SVar:X:Targeted$CardManaCost AI:RemoveDeck:All diff --git a/forge-gui/res/cardsfolder/s/syndicate_recruiter.txt b/forge-gui/res/cardsfolder/s/syndicate_recruiter.txt new file mode 100644 index 00000000000..3315aff5c26 --- /dev/null +++ b/forge-gui/res/cardsfolder/s/syndicate_recruiter.txt @@ -0,0 +1,11 @@ +Name:Syndicate Recruiter +ManaCost: 2 U B +Types:Creature Vampire Assassin +PT:4/5 +K:Flying +K:Ward:1 +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigMill | TriggerZones$ Battlefield | TriggerDescription$ When CARDNAME enters the battlefield, mill four cards. Then if there are five or more mana values among cards in your graveyard, conjure a card named Dig Up the Body into your hand. +SVar:TrigMill:DB$ Mill | NumCards$ 4 | Defined$ You | SubAbility$ DBConjure +SVar:DBConjure:DB$ MakeCard | ConditionCheckSVar$ X | ConditionSVarCompare$ GE5 | Name$ Dig Up the Body | Zone$ Hand +SVar:X:Count$ValidGraveyard Card.YouOwn$DifferentCMC +Oracle:Flying\nWard 1\nWhenever Syndicate Recruiter enters the battlefield, mill four cards. Then if there are five or more mana values among cards in your graveyard, conjure a card named Dig Up the Body into your hand. diff --git a/forge-gui/res/cardsfolder/upcoming/altar_of_bhaal_bone_offering.txt b/forge-gui/res/cardsfolder/upcoming/altar_of_bhaal_bone_offering.txt new file mode 100644 index 00000000000..e19fa86f284 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/altar_of_bhaal_bone_offering.txt @@ -0,0 +1,16 @@ +Name:Altar of Bhaal +ManaCost:1 B +Types:Artifact +AlternateMode:Adventure +A:AB$ ChangeZone | Cost$ 2 B T Exile<1/Creature> | TgtPrompt$ Choose target creature card in your graveyard | ValidTgts$ Creature.YouCtrl | Origin$ Graveyard | Destination$ Battlefield | SorcerySpeed$ True | SpellDescription$ Return target creature card from your graveyard to the battlefield. Activate only as a sorcery. +AI:RemoveDeck:Random +Oracle:{2}{B}, {T}, Exile a creature you control: Return target creature card from your graveyard to the battlefield. Activate only as a sorcery. + +ALTERNATE + +Name:Bone Offering +ManaCost:2 B +Types:Sorcery Adventure +A:SP$ Token | TokenAmount$ 1 | TokenScript$ b_4_1_skeleton_menace | TokenTapped$ True | TokenOwner$ You | SpellDescription$ Create a tapped 4/1 black Skeleton creature token with menace. +DeckHas:Ability$Token & Type$Skeleton +Oracle:Create a tapped 4/1 black Skeleton creature token with menace. diff --git a/forge-gui/res/cardsfolder/upcoming/big_spender.txt b/forge-gui/res/cardsfolder/upcoming/big_spender.txt new file mode 100644 index 00000000000..0c63a491e51 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/big_spender.txt @@ -0,0 +1,12 @@ +Name:Big Spender +ManaCost:1 R +Types:Creature Devil Citizen +PT:2/1 +K:Haste +T:Mode$ AttackerBlockedOnce | ValidCard$ Creature.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigTreasure | TriggerDescription$ Whenever one or more creatures you control become blocked, create a Treasure token. +SVar:TrigTreasure:DB$ Token | TokenScript$ c_a_treasure_sac +A:AB$ Draft | Cost$ Sac<2/Artifact> | Spellbook$ Arcane Encyclopedia,Daredevil Dragster,Diamond Mare,Filigree Familiar,Fountain of Renewal,Gilded Lotus,Golden Egg,Guild Globe,Heraldic Banner,Honored Heirloom,Key to the City,Prophetic Prism,Stuffed Bear,Treasure Vault,Zephyr Boots | SpellDescription$ Draft a card from CARDNAME's spellbook. +DeckHas:Ability$Sacrifice|Token|Discard|LifeGain & Type$Treasure|Artifact|Horse|Fox|Food|Equipment|Bear +DeckHints:Type$Treasure +SVar:AIPreference:SacCost$Treasure.Token,Artifact.Token +Oracle:Haste\nWhenever one or more creatures you control become blocked, create a Treasure token.\nSacrifice two artifacts: Draft a card from Big Spender's spellbook. diff --git a/forge-gui/src/main/java/forge/player/HumanCostDecision.java b/forge-gui/src/main/java/forge/player/HumanCostDecision.java index 10465f5805f..71fd421e317 100644 --- a/forge-gui/src/main/java/forge/player/HumanCostDecision.java +++ b/forge-gui/src/main/java/forge/player/HumanCostDecision.java @@ -1176,7 +1176,8 @@ public class HumanCostDecision extends CostDecisionMakerBase { else if (ability.getTargets() != null && ability.getTargets().isTargetingAnyCard() && ability.getTargets().size() == 1) cardView = CardView.get(ability.getTargetCard()); else if (cardView.getZone() == null || cardView.getZone().isHidden()) { - cardView = CardView.getCardForUi(ImageUtil.getPaperCardFromImageKey(cardView.getCurrentState().getImageKey())); + if (!cardView.hasAlternateState()) //don't override if it has alternatestate since it maybe showing alternate view + cardView = CardView.getCardForUi(ImageUtil.getPaperCardFromImageKey(cardView.getCurrentState().getImageKey())); } return controller.getGui().confirm(cardView, message.replaceAll("\n", " ")); } else {