Merge remote-tracking branch 'upstream/master' into smart-card-art-deck-import-desktop-and-mobile

This commit is contained in:
leriomaggio
2021-11-08 07:20:07 +00:00
413 changed files with 10299 additions and 367 deletions

View File

@@ -83,7 +83,11 @@ public class AdventureApplicationAdapter extends ApplicationAdapter {
private void storeScreen() { private void storeScreen() {
if(!(currentScene instanceof ForgeScene)) if(!(currentScene instanceof ForgeScene))
lastScreenTexture = ScreenUtils.getFrameBufferTexture(); {
if(lastScreenTexture!=null)
lastScreenTexture.getTexture().dispose();
lastScreenTexture = ScreenUtils.getFrameBufferTexture();
}
} }

View File

@@ -1,13 +1,14 @@
package forge.adventure.data; package forge.adventure.data;
import java.io.Serializable; import forge.adventure.util.SaveFileContent;
import forge.adventure.util.SaveFileData;
/** /**
* Data class that will be used to read Json configuration files * Data class that will be used to read Json configuration files
* BiomeSpriteData * BiomeSpriteData
* contains the information for the sprites on the map like trees and rocks * contains the information for the sprites on the map like trees and rocks
*/ */
public class BiomeSpriteData implements Serializable { public class BiomeSpriteData implements SaveFileContent {
public String name; public String name;
public double startArea; public double startArea;
public double endArea; public double endArea;
@@ -18,4 +19,26 @@ public class BiomeSpriteData implements Serializable {
public String key() { public String key() {
return "BiomeSprite&" + name; return "BiomeSprite&" + name;
} }
@Override
public void load(SaveFileData data) {
name=data.readString("name");
startArea=data.readDouble("startArea");
endArea=data.readDouble("endArea");
density=data.readDouble("density");
resolution=data.readDouble("resolution");;
layer=data.readInt("layer");
}
@Override
public SaveFileData save() {
SaveFileData data=new SaveFileData();
data.store("name",name);
data.store("startArea",startArea);
data.store("endArea",endArea);
data.store("density",density);
data.store("resolution",resolution);
data.store("layer",layer);
return data;
}
} }

View File

@@ -92,8 +92,8 @@ public class DeckEditScene extends ForgeScene {
for (PaperCard card : AdventurePlayer.current().getCards()) for (Map.Entry<PaperCard, Integer> card : AdventurePlayer.current().getCards())
FModel.getQuest().getCards().addSingleCard(card, 1); FModel.getQuest().getCards().addSingleCard(card.getKey(), card.getValue());
Deck deck = AdventurePlayer.current().getDeck(); Deck deck = AdventurePlayer.current().getDeck();

View File

@@ -1,5 +1,6 @@
package forge.adventure.scene; package forge.adventure.scene;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.scenes.scene2d.ui.TextButton; import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
import forge.adventure.AdventureApplicationAdapter; import forge.adventure.AdventureApplicationAdapter;
import forge.adventure.util.Current; import forge.adventure.util.Current;
@@ -37,5 +38,14 @@ public class InnScene extends UIScene {
TextButton doneButton = ui.findActor("done"); TextButton doneButton = ui.findActor("done");
} }
@Override
public boolean keyPressed(int keycode)
{
if (keycode == Input.Keys.ESCAPE)
{
done();
}
return true;
}
} }

View File

@@ -1,6 +1,7 @@
package forge.adventure.scene; package forge.adventure.scene;
import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.scenes.scene2d.Actor; import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.InputEvent; import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.ui.TextButton; import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
@@ -103,6 +104,15 @@ public class RewardScene extends UIScene {
doneButton=ui.findActor("done"); doneButton=ui.findActor("done");
} }
@Override
public boolean keyPressed(int keycode)
{
if (keycode == Input.Keys.ESCAPE)
{
done();
}
return true;
}
public void loadRewards(Array<Reward> newRewards, Type type, ShopActor shopActor) public void loadRewards(Array<Reward> newRewards, Type type, ShopActor shopActor)

View File

@@ -1,5 +1,6 @@
package forge.adventure.scene; package forge.adventure.scene;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.scenes.scene2d.InputEvent; import com.badlogic.gdx.scenes.scene2d.InputEvent;
@@ -36,6 +37,8 @@ public class SaveLoadScene extends UIScene {
int currentSlot = -3; int currentSlot = -3;
Image previewImage; Image previewImage;
TextButton saveLoadButton; TextButton saveLoadButton;
TextButton quickSave;
TextButton autoSave;
public SaveLoadScene() { public SaveLoadScene() {
super("ui/save_load.json"); super("ui/save_load.json");
@@ -44,14 +47,15 @@ public class SaveLoadScene extends UIScene {
private void addSaveSlot(String name, int i) { private TextButton addSaveSlot(String name, int i) {
layout.add(Controls.newLabel(name)); layout.add(Controls.newLabel(name));
TextButton button = Controls.newTextButton("..."); TextButton button = Controls.newTextButton("...");
button.addListener(new ClickListener() { button.addListener(new ClickListener() {
@Override @Override
public void clicked(InputEvent event, float x, float y) { public void clicked(InputEvent event, float x, float y) {
try { try {
select(i); if(!button.isDisabled())
select(i);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
@@ -60,6 +64,7 @@ public class SaveLoadScene extends UIScene {
layout.add(button).expandX(); layout.add(button).expandX();
buttons.put(i, button); buttons.put(i, button);
layout.row(); layout.row();
return button;
} }
@@ -105,6 +110,15 @@ public class SaveLoadScene extends UIScene {
return true; return true;
} }
@Override
public boolean keyPressed(int keycode)
{
if (keycode == Input.Keys.ESCAPE)
{
back();
}
return true;
}
public void save() { public void save() {
dialog.hide(); dialog.hide();
if( WorldSave.getCurrentSave().save(textInput.getText(), currentSlot)) if( WorldSave.getCurrentSave().save(textInput.getText(), currentSlot))
@@ -157,6 +171,8 @@ public class SaveLoadScene extends UIScene {
header.setText("Load game"); header.setText("Load game");
saveLoadButton.setText("Load"); saveLoadButton.setText("Load");
} }
autoSave.setDisabled(save);
quickSave.setDisabled(save);
this.save = save; this.save = save;
} }
@@ -188,8 +204,8 @@ public class SaveLoadScene extends UIScene {
header.setHeight(header.getHeight() * 2); header.setHeight(header.getHeight() * 2);
layout.add(header).colspan(2).align(Align.center); layout.add(header).colspan(2).align(Align.center);
layout.row(); layout.row();
addSaveSlot("Auto save", -2); autoSave=addSaveSlot("Auto save", WorldSave.AUTO_SAVE_SLOT);
addSaveSlot("Quick save", -1); quickSave=addSaveSlot("Quick save", WorldSave.QUICK_SAVE_SLOT);
for (int i = 1; i < 11; i++) for (int i = 1; i < 11; i++)
addSaveSlot("Slot:" + i, i); addSaveSlot("Slot:" + i, i);

View File

@@ -1,6 +1,7 @@
package forge.adventure.scene; package forge.adventure.scene;
import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.scenes.scene2d.Actor; import com.badlogic.gdx.scenes.scene2d.Actor;
@@ -53,6 +54,15 @@ public class SettingsScene extends UIScene {
} }
@Override
public boolean keyPressed(int keycode)
{
if (keycode == Input.Keys.ESCAPE)
{
back();
}
return true;
}
public boolean back() { public boolean back() {
AdventureApplicationAdapter.instance.switchToLast(); AdventureApplicationAdapter.instance.switchToLast();
return true; return true;

View File

@@ -1,6 +1,7 @@
package forge.adventure.scene; package forge.adventure.scene;
import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.scenes.scene2d.Actor; import com.badlogic.gdx.scenes.scene2d.Actor;
import forge.adventure.AdventureApplicationAdapter; import forge.adventure.AdventureApplicationAdapter;
import forge.adventure.world.WorldSave; import forge.adventure.world.WorldSave;
@@ -18,7 +19,6 @@ public class StartScene extends UIScene {
super("ui/start_menu.json"); super("ui/start_menu.json");
} }
public boolean NewGame() { public boolean NewGame() {
AdventureApplicationAdapter.instance.switchScene(SceneType.NewGameScene.instance); AdventureApplicationAdapter.instance.switchScene(SceneType.NewGameScene.instance);
return true; return true;
@@ -67,6 +67,16 @@ public class StartScene extends UIScene {
} }
@Override
public boolean keyPressed(int keycode)
{
if (keycode == Input.Keys.ESCAPE)
{
if(WorldSave.getCurrentSave().getWorld().getData() != null)
Resume();
}
return true;
}
@Override @Override
public void resLoaded() { public void resLoaded() {
super.resLoaded(); super.resLoaded();

View File

@@ -47,9 +47,19 @@ public class UIScene extends Scene{
{ {
return ui; return ui;
} }
public boolean keyPressed(int keycode)
{
return true;
}
@Override @Override
public void resLoaded() { public void resLoaded() {
stage = new Stage(new StretchViewport(GetIntendedWidth(), GetIntendedHeight())); stage = new Stage(new StretchViewport(GetIntendedWidth(), GetIntendedHeight())){
@Override
public boolean keyUp(int keycode) {
return keyPressed(keycode);
}
};
ui = new UIActor(Config.instance().getFile(uiFile)); ui = new UIActor(Config.instance().getFile(uiFile));
screenImage=ui.findActor("lastScreen"); screenImage=ui.findActor("lastScreen");
stage.addActor(ui); stage.addActor(ui);
@@ -75,6 +85,8 @@ public class UIScene extends Scene{
potPixmap.fillRectangle(0,0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); potPixmap.fillRectangle(0,0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
backgroundTexture = new TextureRegion(new Texture(potPixmap), 0, Gdx.graphics.getHeight(), Gdx.graphics.getWidth(), -Gdx.graphics.getHeight()); backgroundTexture = new TextureRegion(new Texture(potPixmap), 0, Gdx.graphics.getHeight(), Gdx.graphics.getWidth(), -Gdx.graphics.getHeight());
screenImage.setDrawable(new TextureRegionDrawable(backgroundTexture)); screenImage.setDrawable(new TextureRegionDrawable(backgroundTexture));
pixmap.dispose();
potPixmap.dispose();
} }
super.enter(); super.enter();

View File

@@ -57,6 +57,7 @@ public class GameHUD extends Stage {
addActor(ui); addActor(ui);
addActor(miniMapPlayer); addActor(miniMapPlayer);
WorldSave.getCurrentSave().onLoad(() -> enter());
} }
public static GameHUD getInstance() { public static GameHUD getInstance() {
@@ -94,10 +95,9 @@ public class GameHUD extends Stage {
Texture miniMapTexture; Texture miniMapTexture;
public void enter() { public void enter() {
if(miniMapTexture==null) if(miniMapTexture!=null)
{ miniMapTexture.dispose();
miniMapTexture=new Texture(WorldSave.getCurrentSave().getWorld().getBiomeImage()); miniMapTexture=new Texture(WorldSave.getCurrentSave().getWorld().getBiomeImage());
}
miniMap.setDrawable(new TextureRegionDrawable(miniMapTexture)); miniMap.setDrawable(new TextureRegionDrawable(miniMapTexture));
avatar.setDrawable(new TextureRegionDrawable(Current.player().avatar())); avatar.setDrawable(new TextureRegionDrawable(Current.player().avatar()));

View File

@@ -1,9 +1,7 @@
package forge.adventure.stage; package forge.adventure.stage;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input; import com.badlogic.gdx.Input;
import com.badlogic.gdx.graphics.OrthographicCamera; import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.math.Rectangle; import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.scenes.scene2d.Actor; import com.badlogic.gdx.scenes.scene2d.Actor;
@@ -16,7 +14,6 @@ import forge.adventure.character.PlayerSprite;
import forge.adventure.scene.Scene; import forge.adventure.scene.Scene;
import forge.adventure.scene.SceneType; import forge.adventure.scene.SceneType;
import forge.adventure.world.WorldSave; import forge.adventure.world.WorldSave;
import forge.adventure.world.WorldSaveHeader;
/** /**
* Base class to render a player sprite on a map * Base class to render a player sprite on a map
@@ -48,6 +45,13 @@ public abstract class GameStage extends Stage {
public GameStage() { public GameStage() {
super(new StretchViewport(Scene.GetIntendedWidth(), Scene.GetIntendedHeight(), new OrthographicCamera())); super(new StretchViewport(Scene.GetIntendedWidth(), Scene.GetIntendedHeight(), new OrthographicCamera()));
WorldSave.getCurrentSave().onLoad(() -> {
if(player==null)
return;
foregroundSprites.removeActor(player);
player=null;
GetPlayer();
});
camera = (OrthographicCamera) getCamera(); camera = (OrthographicCamera) getCamera();
backgroundSprites = new Group(); backgroundSprites = new Group();
@@ -157,6 +161,18 @@ public abstract class GameStage extends Stage {
{ {
player.getMovementDirection().y = -1; player.getMovementDirection().y = -1;
} }
if (keycode == Input.Keys.F5)//todo config
{
GetPlayer().storePos();
WorldSave.getCurrentSave().header.createPreview();
WorldSave.getCurrentSave().quickSave();
}
if (keycode == Input.Keys.F8)//todo config
{
WorldSave.getCurrentSave().quickLoad();
enter();
}
if (keycode == Input.Keys.F12) if (keycode == Input.Keys.F12)
{ {
debugCollision(true); debugCollision(true);
@@ -258,15 +274,8 @@ public abstract class GameStage extends Stage {
} }
public void openMenu() { public void openMenu() {
Pixmap pixmap = Pixmap.createFromFrameBuffer(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
Pixmap scaled = new Pixmap(WorldSaveHeader.previewImageWidth, (int) (WorldSaveHeader.previewImageWidth / (Scene.GetIntendedWidth() / (float) Scene.GetIntendedHeight())), Pixmap.Format.RGB888); WorldSave.getCurrentSave().header.createPreview();
scaled.drawPixmap(pixmap,
0, 0, pixmap.getWidth(), pixmap.getHeight(),
0, 0, scaled.getWidth(), scaled.getHeight());
pixmap.dispose();
if (WorldSave.getCurrentSave().header.preview != null)
WorldSave.getCurrentSave().header.preview.dispose();
WorldSave.getCurrentSave().header.preview = scaled;
AdventureApplicationAdapter.instance.switchScene(SceneType.StartScene.instance); AdventureApplicationAdapter.instance.switchScene(SceneType.StartScene.instance);
} }

View File

@@ -128,17 +128,45 @@ public class WorldBackground extends Actor {
public void initialize() { public void initialize() {
tileSize = WorldSave.getCurrentSave().getWorld().getTileSize(); tileSize = WorldSave.getCurrentSave().getWorld().getTileSize();
chunkSize = WorldSave.getCurrentSave().getWorld().getChunkSize(); chunkSize = WorldSave.getCurrentSave().getWorld().getChunkSize();
if(chunks!=null)
{
stage.GetSpriteGroup().clear();
for(int i=0;i<chunks.length;i++)
for(int j=0;j<chunks[i].length;j++)
if(chunks[i][j]!=null)
chunks[i][j].dispose();
}
chunks = new Texture[WorldSave.getCurrentSave().getWorld().getWidthInTiles()][WorldSave.getCurrentSave().getWorld().getHeightInTiles()]; chunks = new Texture[WorldSave.getCurrentSave().getWorld().getWidthInTiles()][WorldSave.getCurrentSave().getWorld().getHeightInTiles()];
ArrayList[][] createChunks = new ArrayList[WorldSave.getCurrentSave().getWorld().getWidthInTiles()][WorldSave.getCurrentSave().getWorld().getHeightInTiles()]; ArrayList[][] createChunks = new ArrayList[WorldSave.getCurrentSave().getWorld().getWidthInTiles()][WorldSave.getCurrentSave().getWorld().getHeightInTiles()];
chunksSprites = createChunks; chunksSprites = createChunks;
ArrayList[][] createSprites = new ArrayList[WorldSave.getCurrentSave().getWorld().getWidthInTiles()][WorldSave.getCurrentSave().getWorld().getHeightInTiles()]; ArrayList[][] createSprites = new ArrayList[WorldSave.getCurrentSave().getWorld().getWidthInTiles()][WorldSave.getCurrentSave().getWorld().getHeightInTiles()];
chunksSpritesBackground = createSprites; chunksSpritesBackground = createSprites;
Pixmap loadPix = new Pixmap(chunkSize * tileSize, chunkSize * tileSize, Pixmap.Format.RGB565);
loadPix.setColor(0.5f, 0.5f, 0.5f, 1);
loadPix.fill(); if(loadingTexture==null)
loadingTexture = new Texture(loadPix); {
Pixmap loadPix = new Pixmap(chunkSize * tileSize, chunkSize * tileSize, Pixmap.Format.RGB565);
loadPix.setColor(0.5f, 0.5f, 0.5f, 1);
loadPix.fill();
loadingTexture = new Texture(loadPix);
}
for (int x = -1; x < 2; x++) {
for (int y = -1; y < 2; y++) {
Point point = new Point(currentChunkX + x, currentChunkY + y);
if (point.y < 0 || point.x < 0 || point.y >= chunks[0].length || point.x >= chunks.length)
continue;
loadChunk(point.x, point.y);
}
}
} }
@Override
public void clear() {
super.clear();
initialize();
}
int transChunkToWorld(int xy) { int transChunkToWorld(int xy) {
return xy * tileSize * chunkSize; return xy * tileSize * chunkSize;
} }

View File

@@ -8,8 +8,11 @@ import forge.adventure.character.CharacterSprite;
import forge.adventure.character.EnemySprite; import forge.adventure.character.EnemySprite;
import forge.adventure.data.BiomeData; import forge.adventure.data.BiomeData;
import forge.adventure.data.EnemyData; import forge.adventure.data.EnemyData;
import forge.adventure.data.WorldData;
import forge.adventure.scene.*; import forge.adventure.scene.*;
import forge.adventure.util.Current; import forge.adventure.util.Current;
import forge.adventure.util.SaveFileContent;
import forge.adventure.util.SaveFileData;
import forge.adventure.world.World; import forge.adventure.world.World;
import forge.adventure.world.WorldSave; import forge.adventure.world.WorldSave;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
@@ -22,7 +25,7 @@ import java.util.Random;
/** /**
* Stage for the over world. Will handle monster spawns * Stage for the over world. Will handle monster spawns
*/ */
public class WorldStage extends GameStage { public class WorldStage extends GameStage implements SaveFileContent {
private static WorldStage instance=null; private static WorldStage instance=null;
protected EnemySprite currentMob; protected EnemySprite currentMob;
@@ -75,6 +78,7 @@ public class WorldStage extends GameStage {
AdventureApplicationAdapter.instance.switchScene(SceneType.DuelScene.instance); AdventureApplicationAdapter.instance.switchScene(SceneType.DuelScene.instance);
}); });
currentMob = mob; currentMob = mob;
WorldSave.getCurrentSave().autoSave();
break; break;
} }
} }
@@ -224,7 +228,6 @@ public class WorldStage extends GameStage {
} }
} }
setBounds(WorldSave.getCurrentSave().getWorld().getWidthInPixels(), WorldSave.getCurrentSave().getWorld().getHeightInPixels()); setBounds(WorldSave.getCurrentSave().getWorld().getWidthInPixels(), WorldSave.getCurrentSave().getWorld().getHeightInPixels());
} }
@@ -232,4 +235,55 @@ public class WorldStage extends GameStage {
public void leave() { public void leave() {
GetPlayer().storePos(); GetPlayer().storePos();
} }
@Override
public void load(SaveFileData data) {
try {
for(Pair<Float, EnemySprite> enemy:enemies)
foregroundSprites.removeActor(enemy.getValue());
enemies.clear();
background.clear();
List<Float> timeouts= (List<Float>) data.readObject("timeouts");
List<String> names = (List<String>) data.readObject("names");
List<Float> x = (List<Float>) data.readObject("x");
List<Float> y = (List<Float>) data.readObject("y");
for(int i=0;i<timeouts.size();i++)
{
EnemySprite sprite = new EnemySprite(WorldData.getEnemy(names.get(i)));
sprite.setX(x.get(i));
sprite.setY(y.get(i));
enemies.add(Pair.of(timeouts.get(i),sprite));
foregroundSprites.addActor(sprite);
}
globalTimer=data.readFloat("globalTimer");
}
catch (Exception e)
{
}
}
@Override
public SaveFileData save() {
SaveFileData data=new SaveFileData();
List<Float> timeouts=new ArrayList<>();
List<String> names=new ArrayList<>();
List<Float> x=new ArrayList<>();
List<Float> y=new ArrayList<>();
for(Pair<Float, EnemySprite> enemy:enemies)
{
timeouts.add(enemy.getKey());
names.add(enemy.getValue().getData().name);
x.add(enemy.getValue().getX());
y.add(enemy.getValue().getY());
}
data.storeObject("timeouts",timeouts);
data.storeObject("names",names);
data.storeObject("x",x);
data.storeObject("y",y);
data.store("globalTimer",globalTimer);
return data;
}
} }

View File

@@ -266,7 +266,7 @@ public class CardUtil {
case MythicRare: case MythicRare:
return 500; return 500;
default: default:
return 90000; return 600;
} }
} }

View File

@@ -46,6 +46,7 @@ public class RewardActor extends Actor implements Disposable, ImageFetcher.Callb
if(needsToBeDisposed) if(needsToBeDisposed)
image.dispose(); image.dispose();
} }
public Reward getReward() { public Reward getReward() {
@@ -54,18 +55,9 @@ public class RewardActor extends Actor implements Disposable, ImageFetcher.Callb
@Override @Override
public void onImageFetched() { public void onImageFetched() {
if(ImageCache.imageKeyFileExists(reward.getCard().getImageKey(false))) setCardImage(ImageCache.getImage(reward.getCard().getImageKey(false),false));
{
setCardImage(ImageCache.getImage(reward.getCard().getImageKey(false),false));
}
} }
public enum Type
{
Shop,
Loot
}
public RewardActor(Reward reward,boolean flippable) public RewardActor(Reward reward,boolean flippable)
{ {

View File

@@ -1,13 +1,9 @@
package forge.adventure.util; package forge.adventure.util;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
/** /**
* Interface to save the content the the save game file * Interface to save the content the the save game file
*/ */
public interface SaveFileContent { public interface SaveFileContent {
void writeToSaveFile(ObjectOutputStream saveFile) throws IOException ; void load(SaveFileData data);
void readFromSaveFile(ObjectInputStream saveFile) throws IOException, ClassNotFoundException; SaveFileData save();
} }

View File

@@ -0,0 +1,325 @@
package forge.adventure.util;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.PixmapIO;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;
import java.io.*;
import java.util.HashMap;
public class SaveFileData extends HashMap<String,byte[]>
{
public void store(String key,SaveFileData subData)
{
try {
ByteArrayOutputStream stream=new ByteArrayOutputStream();
ObjectOutputStream objStream=new ObjectOutputStream(stream);
objStream.writeObject(subData);
objStream.flush();
put(key,stream.toByteArray());
} catch (IOException e) {
e.printStackTrace();
}
}
public void store(String key,float subData)
{
try {
ByteArrayOutputStream stream=new ByteArrayOutputStream();
ObjectOutputStream objStream=new ObjectOutputStream(stream);
objStream.writeFloat(subData);
objStream.flush();
put(key,stream.toByteArray());
} catch (IOException e) {
e.printStackTrace();
}
}
public void store(String key,double subData)
{
try {
ByteArrayOutputStream stream=new ByteArrayOutputStream();
ObjectOutputStream objStream=new ObjectOutputStream(stream);
objStream.writeDouble(subData);
objStream.flush();
put(key,stream.toByteArray());
} catch (IOException e) {
e.printStackTrace();
}
}
public void store(String key,int subData)
{
try {
ByteArrayOutputStream stream=new ByteArrayOutputStream();
ObjectOutputStream objStream=new ObjectOutputStream(stream);
objStream.writeInt(subData);
objStream.flush();
put(key,stream.toByteArray());
} catch (IOException e) {
e.printStackTrace();
}
}
public void store(String key,long subData)
{
try {
ByteArrayOutputStream stream=new ByteArrayOutputStream();
ObjectOutputStream objStream=new ObjectOutputStream(stream);
objStream.writeLong(subData);
objStream.flush();
put(key,stream.toByteArray());
} catch (IOException e) {
e.printStackTrace();
}
}
public void store(String key,boolean subData)
{
try {
ByteArrayOutputStream stream=new ByteArrayOutputStream();
ObjectOutputStream objStream=new ObjectOutputStream(stream);
objStream.writeBoolean(subData);
objStream.flush();
put(key,stream.toByteArray());
} catch (IOException e) {
e.printStackTrace();
}
}
public void store(String key, Pixmap pixmap)
{
try {
ByteArrayOutputStream stream=new ByteArrayOutputStream();
PixmapIO.PNG png = new PixmapIO.PNG();
png.setFlipY(false);
png.write(stream, pixmap);
stream.flush();
put(key,stream.toByteArray());
} catch (IOException e) {
e.printStackTrace();
}
}
public void storeObject(String key,Object subData)
{
try {
ByteArrayOutputStream stream=new ByteArrayOutputStream();
ObjectOutputStream objStream=new ObjectOutputStream(stream);
objStream.writeObject(subData);
objStream.flush();
put(key,stream.toByteArray());
} catch (IOException e) {
e.printStackTrace();
}
}
public void store(String key,String subData)
{
try {
ByteArrayOutputStream stream=new ByteArrayOutputStream();
ObjectOutputStream objStream=new ObjectOutputStream(stream);
objStream.writeUTF(subData);
objStream.flush();
put(key,stream.toByteArray());
} catch (IOException e) {
e.printStackTrace();
}
}
public void store(String key, Vector2 vector) {
try {
ByteArrayOutputStream stream=new ByteArrayOutputStream();
ObjectOutputStream objStream=new ObjectOutputStream(stream);
objStream.writeFloat(vector.x);
objStream.writeFloat(vector.y);
objStream.flush();
put(key,stream.toByteArray());
} catch (IOException e) {
e.printStackTrace();
}
}
public void store(String key, Rectangle rectangle) {
try {
ByteArrayOutputStream stream=new ByteArrayOutputStream();
ObjectOutputStream objStream=new ObjectOutputStream(stream);
objStream.writeFloat(rectangle.x);
objStream.writeFloat(rectangle.y);
objStream.writeFloat(rectangle.width);
objStream.writeFloat(rectangle.height);
objStream.flush();
put(key,stream.toByteArray());
} catch (IOException e) {
e.printStackTrace();
}
}
public SaveFileData readSubData(String key)
{
if(!containsKey(key))
return null;
try {
ByteArrayInputStream stream=new ByteArrayInputStream(get(key));
ObjectInputStream objStream=new ObjectInputStream(stream);
return (SaveFileData)objStream.readObject();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
public Object readObject(String key)
{
if(!containsKey(key))
return null;
try {
ByteArrayInputStream stream=new ByteArrayInputStream(get(key));
ObjectInputStream objStream=new ObjectInputStream(stream);
return objStream.readObject();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
public String readString(String key)
{
if(!containsKey(key))
return null;
try {
ByteArrayInputStream stream=new ByteArrayInputStream(get(key));
ObjectInputStream objStream=new ObjectInputStream(stream);
return objStream.readUTF();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public long readLong(String key) {
if(!containsKey(key))
return 0;
try {
ByteArrayInputStream stream=new ByteArrayInputStream(get(key));
ObjectInputStream objStream=new ObjectInputStream(stream);
return objStream.readLong();
} catch (IOException e) {
e.printStackTrace();
}
return 0;
}
public float readFloat(String key)
{
if(!containsKey(key))
return 0.0f;
try {
ByteArrayInputStream stream=new ByteArrayInputStream(get(key));
ObjectInputStream objStream=new ObjectInputStream(stream);
return objStream.readFloat();
} catch (IOException e) {
e.printStackTrace();
}
return 0.0f;
}
public double readDouble(String key)
{
if(!containsKey(key))
return 0.0;
try {
ByteArrayInputStream stream=new ByteArrayInputStream(get(key));
ObjectInputStream objStream=new ObjectInputStream(stream);
return objStream.readDouble();
} catch (IOException e) {
e.printStackTrace();
}
return 0.0;
}
public Vector2 readVector2(String key)
{
if(!containsKey(key))
return new Vector2();
try {
ByteArrayInputStream stream=new ByteArrayInputStream(get(key));
ObjectInputStream objStream=new ObjectInputStream(stream);
float x= objStream.readFloat();
float y= objStream.readFloat();
return new Vector2(x,y);
} catch (IOException e) {
e.printStackTrace();
}
return new Vector2();
}
public Rectangle readRectangle(String key)
{
if(!containsKey(key))
return new Rectangle();
try {
ByteArrayInputStream stream=new ByteArrayInputStream(get(key));
ObjectInputStream objStream=new ObjectInputStream(stream);
float x= objStream.readFloat();
float y= objStream.readFloat();
float width= objStream.readFloat();
float height= objStream.readFloat();
return new Rectangle(x,y,width,height);
} catch (IOException e) {
e.printStackTrace();
}
return new Rectangle();
}
public Pixmap readPixmap(String key)
{
if(!containsKey(key))
return null;
return new Pixmap(get(key), 0, get(key).length);
}
public int readInt(String key)
{
if(!containsKey(key))
return 0;
try {
ByteArrayInputStream stream=new ByteArrayInputStream(get(key));
ObjectInputStream objStream=new ObjectInputStream(stream);
return objStream.readInt();
} catch (IOException e) {
e.printStackTrace();
}
return 0;
}
public boolean readBool(String key)
{
if(!containsKey(key))
return false;
try {
ByteArrayInputStream stream=new ByteArrayInputStream(get(key));
ObjectInputStream objStream=new ObjectInputStream(stream);
return objStream.readBoolean();
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
}

View File

@@ -44,10 +44,10 @@ public abstract class Serializer {
} }
public static void WritePixmap(ObjectOutputStream out, Pixmap pixmap, boolean b) throws IOException { public static void WritePixmap(ObjectOutputStream out, Pixmap pixmap, boolean flip) throws IOException {
if (pixmap != null) { if (pixmap != null) {
PixmapIO.PNG png = new PixmapIO.PNG(); PixmapIO.PNG png = new PixmapIO.PNG();
png.setFlipY(b); png.setFlipY(flip);
ByteArrayOutputStream stream = new ByteArrayOutputStream(); ByteArrayOutputStream stream = new ByteArrayOutputStream();
png.write(stream, pixmap); png.write(stream, pixmap);
byte[] data = stream.toByteArray(); byte[] data = stream.toByteArray();

View File

@@ -2,25 +2,19 @@ package forge.adventure.world;
import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.utils.Disposable; import com.google.common.collect.Lists;
import forge.adventure.data.DifficultyData; import forge.adventure.data.DifficultyData;
import forge.adventure.data.HeroListData; import forge.adventure.data.HeroListData;
import forge.adventure.util.Config; import forge.adventure.util.*;
import forge.adventure.util.Reward; import forge.deck.CardPool;
import forge.adventure.util.SaveFileContent;
import forge.adventure.util.SignalList;
import forge.deck.Deck; import forge.deck.Deck;
import forge.item.PaperCard;
import java.io.IOException;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/** /**
* Class that represents the player (not the player sprite) * Class that represents the player (not the player sprite)
*/ */
public class AdventurePlayer implements Serializable, Disposable, SaveFileContent { public class AdventurePlayer implements Serializable, SaveFileContent {
private Deck deck; private Deck deck;
private int avatarIndex; private int avatarIndex;
private int heroRace; private int heroRace;
@@ -31,20 +25,25 @@ public class AdventurePlayer implements Serializable, Disposable, SaveFileConten
private int gold=0; private int gold=0;
private int maxLife=20; private int maxLife=20;
private int life=20; private int life=20;
private DifficultyData difficultyData; private final DifficultyData difficultyData=new DifficultyData();
static public AdventurePlayer current() static public AdventurePlayer current()
{ {
return WorldSave.currentSave.getPlayer(); return WorldSave.currentSave.getPlayer();
} }
private List<PaperCard> cards=new ArrayList<>(); private final CardPool cards=new CardPool();
public void create(String n, Deck startingDeck, boolean male, int race, int avatar,DifficultyData difficultyData) { public void create(String n, Deck startingDeck, boolean male, int race, int avatar,DifficultyData difficultyData) {
deck = startingDeck; deck = startingDeck;
gold =difficultyData.staringMoney; gold =difficultyData.staringMoney;
cards.addAll(deck.getAllCardsInASinglePool().toFlatList()); cards.clear();
cards.addAllFlat(deck.getAllCardsInASinglePool().toFlatList());
maxLife=difficultyData.startingLife; maxLife=difficultyData.startingLife;
this.difficultyData=difficultyData; this.difficultyData.startingLife=difficultyData.startingLife;
this.difficultyData.staringMoney=difficultyData.staringMoney;
this.difficultyData.startingDifficulty=difficultyData.startingDifficulty;
this.difficultyData.name=difficultyData.name;
this.difficultyData.enemyLifeFactor=difficultyData.enemyLifeFactor;
life=maxLife; life=maxLife;
avatarIndex = avatar; avatarIndex = avatar;
heroRace = race; heroRace = race;
@@ -57,7 +56,7 @@ public class AdventurePlayer implements Serializable, Disposable, SaveFileConten
public Deck getDeck() { public Deck getDeck() {
return deck; return deck;
} }
public List<PaperCard> getCards() { public CardPool getCards() {
return cards; return cards;
} }
@@ -82,47 +81,71 @@ public class AdventurePlayer implements Serializable, Disposable, SaveFileConten
this.worldPosY = worldPosY; this.worldPosY = worldPosY;
} }
@Override
public void writeToSaveFile(java.io.ObjectOutputStream out) throws IOException {
out.writeUTF(name);
out.writeFloat(worldPosX);
out.writeFloat(worldPosY);
out.writeInt(avatarIndex);
out.writeInt(heroRace);
out.writeBoolean(isFemale);
out.writeInt(gold);
out.writeInt(life);
out.writeInt(maxLife);
out.writeObject(deck);
out.writeObject(cards);
}
@Override @Override
public void readFromSaveFile(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { public void load(SaveFileData data) {
name = in.readUTF();
worldPosX = in.readFloat();
worldPosY = in.readFloat();
avatarIndex = in.readInt(); this.difficultyData.startingLife=data.readInt("startingLife");
heroRace = in.readInt(); this.difficultyData.staringMoney=data.readInt("staringMoney");
isFemale = in.readBoolean(); this.difficultyData.startingDifficulty=data.readBool("startingDifficulty");
gold = in.readInt(); this.difficultyData.name=data.readString("difficultyName");
life = in.readInt(); this.difficultyData.enemyLifeFactor=data.readFloat("enemyLifeFactor");
maxLife = in.readInt();
deck = (Deck) in.readObject();
cards = (List) in.readObject();
name = data.readString("name");
worldPosX = data.readFloat("worldPosX");
worldPosY = data.readFloat("worldPosY");
avatarIndex = data.readInt("avatarIndex");
heroRace = data.readInt("heroRace");
isFemale = data.readBool("isFemale");
gold = data.readInt("gold");
life = data.readInt("life");
maxLife = data.readInt("maxLife");
deck = new Deck(data.readString("deckName"));
deck.getMain().addAll(CardPool.fromCardList(Lists.newArrayList((String[])data.readObject("deckCards"))));
cards.clear();
cards.addAll(CardPool.fromCardList(Lists.newArrayList((String[])data.readObject("cards"))));
onLifeTotalChangeList.emit(); onLifeTotalChangeList.emit();
onGoldChangeList.emit(); onGoldChangeList.emit();
} }
public void dispose() { @Override
public SaveFileData save() {
SaveFileData data= new SaveFileData();
data.store("startingLife",this.difficultyData.startingLife);
data.store("staringMoney",this.difficultyData.staringMoney);
data.store("startingDifficulty",this.difficultyData.startingDifficulty);
data.store("difficultyName",this.difficultyData.name);
data.store("enemyLifeFactor",this.difficultyData.enemyLifeFactor);
data.store("name",name);
data.store("worldPosX",worldPosX);
data.store("worldPosY",worldPosY);
data.store("avatarIndex",avatarIndex);
data.store("heroRace",heroRace);
data.store("isFemale",isFemale);
data.store("gold",gold);
data.store("life",life);
data.store("maxLife",maxLife);
data.store("deckName",deck.getName());
data.storeObject("deckCards",deck.getMain().toCardList("\n").split("\n"));
data.storeObject("cards",cards.toCardList("\n").split("\n"));
return data;
} }
public String spriteName() { public String spriteName() {
return HeroListData.getHero(heroRace, isFemale); return HeroListData.getHero(heroRace, isFemale);
} }

View File

@@ -7,11 +7,8 @@ import com.badlogic.gdx.utils.Array;
import forge.adventure.data.PointOfInterestData; import forge.adventure.data.PointOfInterestData;
import forge.adventure.util.Config; import forge.adventure.util.Config;
import forge.adventure.util.SaveFileContent; import forge.adventure.util.SaveFileContent;
import forge.adventure.util.Serializer; import forge.adventure.util.SaveFileData;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Random; import java.util.Random;
/** /**
@@ -21,23 +18,28 @@ public class PointOfInterest implements SaveFileContent {
@Override @Override
public void writeToSaveFile(ObjectOutputStream saveFile) throws IOException { public void load(SaveFileData saveFileData) {
saveFile.writeUTF(data.name);
Serializer.writeVector(saveFile,position); data=PointOfInterestData.getPointOfInterest(saveFileData.readString("name"));
Serializer.writeRectangle(saveFile,rectangle); position.set(saveFileData.readVector2("position"));
saveFile.writeInt(spriteIndex); rectangle.set(saveFileData.readRectangle("rectangle"));
spriteIndex=saveFileData.readInt("spriteIndex");
oldMapId="";
Array<Sprite> textureAtlas = Config.instance().getAtlas(this.data.spriteAtlas).createSprites(this.data.sprite);
sprite = textureAtlas.get(spriteIndex);
} }
@Override @Override
public void readFromSaveFile(ObjectInputStream saveFile) throws IOException { public SaveFileData save() {
String name= saveFile.readUTF();
data=PointOfInterestData.getPointOfInterest(name); SaveFileData data=new SaveFileData();
Serializer.readVector(saveFile,position); data.store("name",this.data.name);
Serializer.readRectangle(saveFile,rectangle); data.store("position",position);
spriteIndex=saveFile.readInt(); data.store("rectangle",rectangle);
oldMapId=""; data.store("spriteIndex",spriteIndex);
Array<Sprite> textureAtlas = Config.instance().getAtlas(data.spriteAtlas).createSprites(data.sprite); return data;
sprite = textureAtlas.get(spriteIndex);
} }
PointOfInterestData data; PointOfInterestData data;

View File

@@ -1,10 +1,8 @@
package forge.adventure.world; package forge.adventure.world;
import forge.adventure.util.SaveFileContent; import forge.adventure.util.SaveFileContent;
import forge.adventure.util.SaveFileData;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -48,42 +46,48 @@ public class PointOfInterestMap implements SaveFileContent {
return mapObjects[chunkX][chunkY]; return mapObjects[chunkX][chunkY];
} }
@Override
public void writeToSaveFile(ObjectOutputStream saveFile) throws IOException {
saveFile.writeInt(numberOfChunksX);
saveFile.writeInt(numberOfChunksY);
saveFile.writeInt(tileSize);
saveFile.writeInt(chunkSize);
for (int x = 0; x < numberOfChunksX; x++) {
for (int y = 0; y < numberOfChunksY; y++) {
saveFile.writeInt(mapObjects[x][y].size());
for(PointOfInterest poi:mapObjects[x][y])
{
poi.writeToSaveFile(saveFile);
}
}
}
}
@Override @Override
public void readFromSaveFile(ObjectInputStream saveFile) throws IOException, ClassNotFoundException { public void load(SaveFileData data) {
numberOfChunksX=saveFile.readInt(); numberOfChunksX=data.readInt("numberOfChunksX");
numberOfChunksY=saveFile.readInt(); numberOfChunksY=data.readInt("numberOfChunksY");
tileSize=saveFile.readInt(); tileSize=data.readInt("tileSize");
chunkSize=saveFile.readInt(); chunkSize=data.readInt("chunkSize");
mapObjects = new List[numberOfChunksX][numberOfChunksY]; mapObjects = new List[numberOfChunksX][numberOfChunksY];
for (int x = 0; x < numberOfChunksX; x++) { for (int x = 0; x < numberOfChunksX; x++) {
for (int y = 0; y < numberOfChunksY; y++) { for (int y = 0; y < numberOfChunksY; y++) {
mapObjects[x][y] = new ArrayList(); mapObjects[x][y] = new ArrayList();
int arraySize=saveFile.readInt(); int arraySize=data.readInt("mapObjects["+x +"]["+y+"]");
for(int i=0;i<arraySize;i++) for(int i=0;i<arraySize;i++)
{ {
PointOfInterest pointsOfInterest=new PointOfInterest(); PointOfInterest pointsOfInterest=new PointOfInterest();
pointsOfInterest.readFromSaveFile(saveFile); pointsOfInterest.load(data.readSubData("mapObjects["+x +"]["+y+"]["+i+"]"));
mapObjects[x][y].add(pointsOfInterest); mapObjects[x][y].add(pointsOfInterest);
} }
} }
} }
} }
@Override
public SaveFileData save() {
SaveFileData data=new SaveFileData();
data.store("numberOfChunksX",numberOfChunksX);
data.store("numberOfChunksY",numberOfChunksY);
data.store("tileSize",tileSize);
data.store("chunkSize",chunkSize);
data.store("numberOfChunksX",numberOfChunksX);
for (int x = 0; x < numberOfChunksX; x++) {
for (int y = 0; y < numberOfChunksY; y++) {
data.store("mapObjects["+x +"]["+y+"]",mapObjects[x][y].size());
for(int i=0;i<mapObjects[x][y].size();i++)
{
data.store("mapObjects["+x +"]["+y+"]["+i+"]",mapObjects[x][y].get(i).save());
}
}
}
return data;
}
} }

View File

@@ -2,10 +2,10 @@ package forge.adventure.world;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector2;
import forge.adventure.data.BiomeSpriteData; import forge.adventure.data.BiomeSpriteData;
import forge.adventure.util.SaveFileContent;
import forge.adventure.util.SaveFileData;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@@ -13,10 +13,37 @@ import java.util.List;
/** /**
* Class that hold all sprites as a list for each chunk * Class that hold all sprites as a list for each chunk
*/ */
public class SpritesDataMap implements Serializable { public class SpritesDataMap implements SaveFileContent {
public class BiomeSpriteDataMap extends HashMap<Integer, BiomeSpriteData> implements SaveFileContent
{
@Override
public void load(SaveFileData data) {
clear();
List<Integer> keyList=(List<Integer>)data.readObject("keyList");
for(Integer key:keyList)
{
BiomeSpriteData biomeData=new BiomeSpriteData();
biomeData.load(data.readSubData(key.toString()));
put(key,biomeData);
}
}
@Override
public SaveFileData save() {
SaveFileData data = new SaveFileData();
List<Integer> keyList=new ArrayList<>();
for(Entry<Integer, BiomeSpriteData> entry:this.entrySet())
{
keyList.add(entry.getKey());
data.store(entry.getKey().toString(),entry.getValue().save());
}
data.storeObject("keyList",keyList);
return data;
}
}
private final int numberOfChunks; private final int numberOfChunks;
HashMap<Integer, BiomeSpriteData> objectData = new HashMap<>(); BiomeSpriteDataMap objectData = new BiomeSpriteDataMap();
HashMap<String, Integer> objectKeys = new HashMap<>(); HashMap<String, Integer> objectKeys = new HashMap<>();
int tileSize; int tileSize;
int chunkSize; int chunkSize;
@@ -34,25 +61,7 @@ public class SpritesDataMap implements Serializable {
} }
} }
private void writeObject(java.io.ObjectOutputStream out) throws IOException {
out.writeObject(mapObjects);
out.writeObject(objectData);
out.writeObject(objectKeys);
out.writeInt(tileSize);
out.writeInt(chunkSize);
}
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
mapObjects = (List<Pair<Vector2, Integer>>[][]) in.readObject();
objectData = (HashMap<Integer, BiomeSpriteData>) in.readObject();
objectKeys = (HashMap<String, Integer>) in.readObject();
tileSize = in.readInt();
chunkSize = in.readInt();
}
public BiomeSpriteData get(int id) { public BiomeSpriteData get(int id) {
return objectData.get(id); return objectData.get(id);
@@ -86,4 +95,28 @@ public class SpritesDataMap implements Serializable {
return new ArrayList<>(); return new ArrayList<>();
return mapObjects[chunkX][chunkY]; return mapObjects[chunkX][chunkY];
} }
@Override
public void load(SaveFileData data) {
objectData.load(data.readSubData("objectData"));
mapObjects = (List<Pair<Vector2, Integer>>[][])data.readObject("mapObjects");
objectKeys = (HashMap<String, Integer>)data.readObject("objectKeys");
tileSize = data.readInt("tileSize");
chunkSize = data.readInt("chunkSize");
}
@Override
public SaveFileData save() {
SaveFileData data=new SaveFileData();
data.store("objectData",objectData.save());
data.storeObject("mapObjects",mapObjects);
data.storeObject("objectKeys",objectKeys);
data.store("tileSize",tileSize);
data.store("chunkSize",chunkSize);
return data;
}
} }

View File

@@ -13,10 +13,9 @@ import forge.adventure.scene.Scene;
import forge.adventure.util.Config; import forge.adventure.util.Config;
import forge.adventure.util.Paths; import forge.adventure.util.Paths;
import forge.adventure.util.SaveFileContent; import forge.adventure.util.SaveFileContent;
import forge.adventure.util.Serializer; import forge.adventure.util.SaveFileData;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@@ -39,6 +38,7 @@ public class World implements Disposable, SaveFileContent {
private BiomeTexture[] biomeTexture; private BiomeTexture[] biomeTexture;
private long seed; private long seed;
private final Random random = new Random(); private final Random random = new Random();
private boolean worldDataLoaded=false;
public Random getRandom() public Random getRandom()
{ {
@@ -48,49 +48,65 @@ public class World implements Disposable, SaveFileContent {
return (int) (Math.log(Long.highestOneBit(biome)) / Math.log(2)); return (int) (Math.log(Long.highestOneBit(biome)) / Math.log(2));
} }
@Override public void loadWorldData()
public void writeToSaveFile(java.io.ObjectOutputStream out) throws IOException { {
if(worldDataLoaded)
return;
Serializer.WritePixmap(out, biomeImage);
out.writeObject(biomeMap);
out.writeObject(terrainMap);
out.writeInt(width);
out.writeInt(height);
out.writeObject(mapObjectIds);
mapPoiIds.writeToSaveFile(out);
out.writeLong(seed);
}
@Override
public void readFromSaveFile(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
FileHandle handle = Config.instance().getFile(Paths.WORLD); FileHandle handle = Config.instance().getFile(Paths.WORLD);
String rawJson = handle.readString(); String rawJson = handle.readString();
data = (new Json()).fromJson(WorldData.class, rawJson); this.data = (new Json()).fromJson(WorldData.class, rawJson);
biomeTexture = new BiomeTexture[data.GetBiomes().size() + 1];
if (biomeImage != null) biomeImage.dispose(); int biomeIndex=0;
biomeImage = Serializer.ReadPixmap(in); for (BiomeData biome : data.GetBiomes()) {
biomeMap = (long[][]) in.readObject();
terrainMap = (int[][]) in.readObject();
width = in.readInt();
height = in.readInt();
mapObjectIds = (SpritesDataMap) in.readObject();
if(mapPoiIds==null)mapPoiIds=new PointOfInterestMap(1,1,1,1);
mapPoiIds.readFromSaveFile(in);
seed = in.readLong();
biomeTexture = new BiomeTexture[data.GetBiomes().size()+1]; biomeTexture[biomeIndex] = new BiomeTexture(biome, data.tileSize);
for(int i = 0; i<data.GetBiomes().size(); i++) biomeIndex++;
{
biomeTexture[i] = new BiomeTexture(data.GetBiomes().get(i), data.tileSize);
} }
biomeTexture[data.GetBiomes().size()] = new BiomeTexture(data.roadTileset, data.tileSize); biomeTexture[biomeIndex] = new BiomeTexture(data.roadTileset, data.tileSize);
worldDataLoaded=true;
} }
@Override
public void load(SaveFileData saveFileData) {
if(biomeImage!=null)
biomeImage.dispose();
loadWorldData();
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.load(saveFileData.readSubData("mapPoiIds"));
seed=saveFileData.readLong("seed");
}
@Override
public SaveFileData save() {
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);
return data;
}
public BiomeSpriteData getObject(int id) { public BiomeSpriteData getObject(int id) {
return mapObjectIds.get(id); return mapObjectIds.get(id);
} }
@@ -201,9 +217,7 @@ public class World implements Disposable, SaveFileContent {
public World generateNew(long seed) { public World generateNew(long seed) {
FileHandle handle = Config.instance().getFile(Paths.WORLD); loadWorldData();
String rawJson = handle.readString();
data = (new Json()).fromJson(WorldData.class, rawJson);
if(seed==0) if(seed==0)
{ {
seed=random.nextLong(); seed=random.nextLong();
@@ -231,11 +245,9 @@ public class World implements Disposable, SaveFileContent {
pix.fill(); pix.fill();
int biomeIndex = -1; int biomeIndex = -1;
biomeTexture = new BiomeTexture[data.GetBiomes().size() + 1];
for (BiomeData biome : data.GetBiomes()) { for (BiomeData biome : data.GetBiomes()) {
biomeIndex++; biomeIndex++;
biomeTexture[biomeIndex] = new BiomeTexture(biome, data.tileSize);
int biomeXStart = (int) Math.round(biome.startPointX * (double) width); int biomeXStart = (int) Math.round(biome.startPointX * (double) width);
int biomeYStart = (int) Math.round(biome.startPointY * (double) height); int biomeYStart = (int) Math.round(biome.startPointY * (double) height);
int biomeWidth = (int) Math.round(biome.width * (double) width); int biomeWidth = (int) Math.round(biome.width * (double) width);
@@ -380,7 +392,6 @@ public class World implements Disposable, SaveFileContent {
biomeIndex++; biomeIndex++;
pix.setColor(1, 1, 1, 1); pix.setColor(1, 1, 1, 1);
biomeTexture[biomeIndex] = new BiomeTexture(data.roadTileset, data.tileSize);
for (Pair<PointOfInterest, PointOfInterest> townPair : allSortedTowns) { for (Pair<PointOfInterest, PointOfInterest> townPair : allSortedTowns) {
Vector2 currentPoint = townPair.getKey().getTilePosition(data.tileSize); Vector2 currentPoint = townPair.getKey().getTilePosition(data.tileSize);

View File

@@ -1,7 +1,10 @@
package forge.adventure.world; package forge.adventure.world;
import forge.adventure.data.DifficultyData; import forge.adventure.data.DifficultyData;
import forge.adventure.stage.WorldStage;
import forge.adventure.util.Config; import forge.adventure.util.Config;
import forge.adventure.util.SaveFileData;
import forge.adventure.util.SignalList;
import forge.deck.Deck; import forge.deck.Deck;
import forge.localinstance.properties.ForgeProfileProperties; import forge.localinstance.properties.ForgeProfileProperties;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -16,16 +19,17 @@ import java.util.zip.InflaterInputStream;
*/ */
public class WorldSave { public class WorldSave {
static final int AUTO_SAVE_SLOT =-1; static final public int AUTO_SAVE_SLOT =-1;
static final int QUICK_SAVE_SLOT =-2; static final public int QUICK_SAVE_SLOT =-2;
static final int INVALID_SAVE_SLOT =-3; static final public int INVALID_SAVE_SLOT =-3;
static WorldSave currentSave=new WorldSave(); static final WorldSave currentSave=new WorldSave();
public WorldSaveHeader header = new WorldSaveHeader(); public WorldSaveHeader header = new WorldSaveHeader();
private final AdventurePlayer player=new AdventurePlayer(); private final AdventurePlayer player=new AdventurePlayer();
private final World world=new World(); private final World world=new World();
private final HashMap<String,PointOfInterestChanges> pointOfInterestChanges=new HashMap<>(); private final HashMap<String,PointOfInterestChanges> pointOfInterestChanges=new HashMap<>();
private final SignalList onLoadList=new SignalList();
public final World getWorld() public final World getWorld()
{ {
@@ -36,6 +40,10 @@ public class WorldSave {
return player; return player;
} }
public void onLoad(Runnable run)
{
onLoadList.add(run);
}
public PointOfInterestChanges getPointOfInterestChanges(String id) public PointOfInterestChanges getPointOfInterestChanges(String id)
{ {
if(!pointOfInterestChanges.containsKey(id)) if(!pointOfInterestChanges.containsKey(id))
@@ -53,8 +61,13 @@ public class WorldSave {
ObjectInputStream oos = new ObjectInputStream(inf)) ObjectInputStream oos = new ObjectInputStream(inf))
{ {
currentSave.header = (WorldSaveHeader) oos.readObject(); currentSave.header = (WorldSaveHeader) oos.readObject();
currentSave.player.readFromSaveFile(oos); SaveFileData mainData=(SaveFileData)oos.readObject();
currentSave.world.readFromSaveFile(oos); currentSave.player.load(mainData.readSubData("player"));
currentSave.world.load(mainData.readSubData("world"));
WorldStage.getInstance().load(mainData.readSubData("worldStage"));
currentSave.onLoadList.emit();
} }
} catch (ClassNotFoundException | IOException e) { } catch (ClassNotFoundException | IOException e) {
e.printStackTrace(); e.printStackTrace();
@@ -105,10 +118,19 @@ public class WorldSave {
currentSave.player.create(name, starterDeck, male, race, avatarIndex,diff); currentSave.player.create(name, starterDeck, male, race, avatarIndex,diff);
currentSave.player.setWorldPosY((int) (currentSave.world.getData().playerStartPosY * currentSave.world.getData().height * currentSave.world.getTileSize())); currentSave.player.setWorldPosY((int) (currentSave.world.getData().playerStartPosY * currentSave.world.getData().height * currentSave.world.getTileSize()));
currentSave.player.setWorldPosX((int) (currentSave.world.getData().playerStartPosX * currentSave.world.getData().width * currentSave.world.getTileSize())); currentSave.player.setWorldPosX((int) (currentSave.world.getData().playerStartPosX * currentSave.world.getData().width * currentSave.world.getTileSize()));
currentSave.onLoadList.emit();
return currentSave; return currentSave;
//return currentSave = ret; //return currentSave = ret;
} }
public boolean autoSave() {
return save("auto save",AUTO_SAVE_SLOT);
}
public boolean quickSave() {
return save("quick save",QUICK_SAVE_SLOT);
}
public boolean quickLoad() {
return load(QUICK_SAVE_SLOT);
}
public boolean save(String text, int currentSlot) { public boolean save(String text, int currentSlot) {
header.name = text; header.name = text;
@@ -121,8 +143,12 @@ public class WorldSave {
ObjectOutputStream oos = new ObjectOutputStream(def)) ObjectOutputStream oos = new ObjectOutputStream(def))
{ {
oos.writeObject(header); oos.writeObject(header);
player.writeToSaveFile(oos); SaveFileData mainData=new SaveFileData();
world.writeToSaveFile(oos); mainData.store("player",currentSave.player.save());
mainData.store("world",currentSave.world.save());
mainData.store("worldStage", WorldStage.getInstance().save());
oos.writeObject(mainData);
} }
} catch (IOException e) { } catch (IOException e) {
@@ -132,11 +158,4 @@ public class WorldSave {
return true; return true;
} }
private void dispose() {
header.dispose();
player.dispose();
world.dispose();
}
} }

View File

@@ -1,7 +1,9 @@
package forge.adventure.world; package forge.adventure.world;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Pixmap; import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.utils.Disposable; import com.badlogic.gdx.utils.Disposable;
import forge.adventure.scene.Scene;
import forge.adventure.util.Serializer; import forge.adventure.util.Serializer;
import java.io.IOException; import java.io.IOException;
@@ -27,6 +29,8 @@ public class WorldSaveHeader implements java.io.Serializable, Disposable {
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
name = in.readUTF(); name = in.readUTF();
if(preview!=null)
preview.dispose();
preview = Serializer.ReadPixmap(in); preview = Serializer.ReadPixmap(in);
saveDate = (Date) in.readObject(); saveDate = (Date) in.readObject();
@@ -35,4 +39,16 @@ public class WorldSaveHeader implements java.io.Serializable, Disposable {
public void dispose() { public void dispose() {
preview.dispose(); preview.dispose();
} }
public void createPreview() {
Pixmap pixmap = Pixmap.createFromFrameBuffer(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
Pixmap scaled = new Pixmap(WorldSaveHeader.previewImageWidth, (int) (WorldSaveHeader.previewImageWidth / (Scene.GetIntendedWidth() / (float) Scene.GetIntendedHeight())), Pixmap.Format.RGB888);
scaled.drawPixmap(pixmap,
0, 0, pixmap.getWidth(), pixmap.getHeight(),
0, 0, scaled.getWidth(), scaled.getHeight());
pixmap.dispose();
if (preview != null)
preview.dispose();
preview = scaled;
}
} }

View File

@@ -712,6 +712,30 @@ public class AiController {
return AiPlayDecision.CantPlaySa; return AiPlayDecision.CantPlaySa;
} }
// Check a predefined condition
if (sa.hasParam("AICheckSVar")) {
final Card host = sa.getHostCard();
final String svarToCheck = sa.getParam("AICheckSVar");
String comparator = "GE";
int compareTo = 1;
if (sa.hasParam("AISVarCompare")) {
final String fullCmp = sa.getParam("AISVarCompare");
comparator = fullCmp.substring(0, 2);
final String strCmpTo = fullCmp.substring(2);
try {
compareTo = Integer.parseInt(strCmpTo);
} catch (final Exception ignored) {
compareTo = AbilityUtils.calculateAmount(host, host.getSVar(strCmpTo), sa);
}
}
int left = AbilityUtils.calculateAmount(host, svarToCheck, sa);
if (!Expressions.compare(left, comparator, compareTo)) {
return AiPlayDecision.AnotherTime;
}
}
int oldCMC = -1; int oldCMC = -1;
boolean xCost = sa.getPayCosts().hasXInAnyCostPart() || sa.getHostCard().hasStartOfKeyword("Strive"); boolean xCost = sa.getPayCosts().hasXInAnyCostPart() || sa.getHostCard().hasStartOfKeyword("Strive");
if (!xCost) { if (!xCost) {
@@ -746,7 +770,7 @@ public class AiController {
// one is warded and can't be paid for. // one is warded and can't be paid for.
if (sa.usesTargeting()) { if (sa.usesTargeting()) {
for (Card tgt : sa.getTargets().getTargetCards()) { for (Card tgt : sa.getTargets().getTargetCards()) {
if (tgt.hasKeyword(Keyword.WARD) && tgt.getController().isOpponentOf(sa.getHostCard().getController())) { if (tgt.hasKeyword(Keyword.WARD) && tgt.isInPlay() && tgt.getController().isOpponentOf(sa.getHostCard().getController())) {
int amount = 0; int amount = 0;
Cost wardCost = ComputerUtilCard.getTotalWardCost(tgt); Cost wardCost = ComputerUtilCard.getTotalWardCost(tgt);
if (wardCost.hasManaCost()) { if (wardCost.hasManaCost()) {

View File

@@ -1746,12 +1746,12 @@ public class ComputerUtilCard {
int att = 0; int att = 0;
if (stAb.hasParam("AddPower")) { if (stAb.hasParam("AddPower")) {
String addP = stAb.getParam("AddPower"); String addP = stAb.getParam("AddPower");
att = AbilityUtils.calculateAmount(addP.startsWith("Affected") ? vCard : c, addP, stAb, true); att = AbilityUtils.calculateAmount(addP.contains("Affected") ? vCard : c, addP, stAb, true);
} }
int def = 0; int def = 0;
if (stAb.hasParam("AddToughness")) { if (stAb.hasParam("AddToughness")) {
String addT = stAb.getParam("AddToughness"); String addT = stAb.getParam("AddToughness");
def = AbilityUtils.calculateAmount(addT.startsWith("Affected") ? vCard : c, addT, stAb, true); def = AbilityUtils.calculateAmount(addT.contains("Affected") ? vCard : c, addT, stAb, true);
} }
vCard.addPTBoost(att, def, c.getTimestamp(), stAb.getId()); vCard.addPTBoost(att, def, c.getTimestamp(), stAb.getId());
} }

View File

@@ -592,7 +592,7 @@ public class ComputerUtilCost {
// Ward - will be accounted for when rechecking a targeted ability // Ward - will be accounted for when rechecking a targeted ability
if (sa.usesTargeting()) { if (sa.usesTargeting()) {
for (Card tgt : sa.getTargets().getTargetCards()) { for (Card tgt : sa.getTargets().getTargetCards()) {
if (tgt.hasKeyword(Keyword.WARD) && tgt.getController().isOpponentOf(sa.getHostCard().getController())) { if (tgt.hasKeyword(Keyword.WARD) && tgt.isInPlay() && tgt.getController().isOpponentOf(sa.getHostCard().getController())) {
Cost wardCost = ComputerUtilCard.getTotalWardCost(tgt); Cost wardCost = ComputerUtilCard.getTotalWardCost(tgt);
if (wardCost.hasManaCost()) { if (wardCost.hasManaCost()) {
extraManaNeeded += wardCost.getTotalMana().getCMC(); extraManaNeeded += wardCost.getTotalMana().getCMC();

View File

@@ -1745,6 +1745,10 @@ public class AttachAi extends SpellAbilityAi {
sa.getTargets().add(tgt); sa.getTargets().add(tgt);
} }
return sa.isTargetNumberValid(); return sa.isTargetNumberValid();
} else if ("Remembered".equals(sa.getParam("Defined")) && sa.getParent() != null
&& sa.getParent().getApi() == ApiType.Token && sa.getParent().hasParam("RememberTokens")) {
// Living Weapon or similar
return true;
} }
return false; return false;
} }

View File

@@ -2,6 +2,8 @@ package forge.ai.ability;
import java.util.*; import java.util.*;
import forge.game.card.*;
import forge.game.keyword.Keyword;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
@@ -32,14 +34,7 @@ import forge.game.GlobalRuleChange;
import forge.game.ability.AbilityKey; import forge.game.ability.AbilityKey;
import forge.game.ability.AbilityUtils; import forge.game.ability.AbilityUtils;
import forge.game.ability.ApiType; import forge.game.ability.ApiType;
import forge.game.card.Card;
import forge.game.card.CardCollection;
import forge.game.card.CardCollectionView;
import forge.game.card.CardLists;
import forge.game.card.CardPredicates;
import forge.game.card.CardPredicates.Presets; import forge.game.card.CardPredicates.Presets;
import forge.game.card.CardUtil;
import forge.game.card.CounterEnumType;
import forge.game.combat.Combat; import forge.game.combat.Combat;
import forge.game.cost.Cost; import forge.game.cost.Cost;
import forge.game.cost.CostDiscard; import forge.game.cost.CostDiscard;
@@ -1397,6 +1392,57 @@ public class ChangeZoneAi extends SpellAbilityAi {
} }
} }
} }
// Reload Undying and Persist, get rid of -1/-1 counters, get rid of enemy auras if able
Card bestChoice = null;
int bestEval = 0;
for (Card c : aiPermanents) {
if (c.isCreature()) {
boolean hasValuableAttachments = false;
boolean hasOppAttachments = false;
int numNegativeCounters = 0;
int numTotalCounters = 0;
for (Card attached : c.getAttachedCards()) {
if (attached.isAura()) {
if (attached.getController() == c.getController()) {
hasValuableAttachments = true;
} else if (attached.getController().isOpponentOf(c.getController())) {
hasOppAttachments = true;
}
}
}
Map<CounterType, Integer> counters = c.getCounters();
for (CounterType ct : counters.keySet()) {
int amount = counters.get(ct);
if (ComputerUtil.isNegativeCounter(ct, c)) {
numNegativeCounters += amount;
}
numTotalCounters += amount;
}
if (hasValuableAttachments || (ComputerUtilCard.isUselessCreature(ai, c) && !hasOppAttachments)) {
continue;
}
Card considered = null;
if ((c.hasKeyword(Keyword.PERSIST) || c.hasKeyword(Keyword.UNDYING))
&& !ComputerUtilCard.hasActiveUndyingOrPersist(c)) {
considered = c;
} else if (hasOppAttachments || (numTotalCounters > 0 && numNegativeCounters > numTotalCounters / 2)) {
considered = c;
}
if (considered != null) {
int eval = ComputerUtilCard.evaluateCreature(c);
if (eval > bestEval) {
bestEval = eval;
bestChoice = considered;
}
}
}
}
if (bestChoice != null) {
return bestChoice;
}
return null; return null;
} }

View File

@@ -66,6 +66,10 @@ public final class ImageKeys {
} }
private static final Map<String, File> cachedCards = new HashMap<>(50000); private static final Map<String, File> cachedCards = new HashMap<>(50000);
private static HashSet<String> missingCards = new HashSet<>();
public static void clearMissingCards() {
missingCards.clear();
}
public static File getCachedCardsFile(String key) { public static File getCachedCardsFile(String key) {
return cachedCards.get(key); return cachedCards.get(key);
} }
@@ -101,6 +105,9 @@ public final class ImageKeys {
dir = CACHE_CARD_PICS_DIR; dir = CACHE_CARD_PICS_DIR;
} }
if (missingCards.contains(filename))
return null;
File cachedFile = cachedCards.get(filename); File cachedFile = cachedCards.get(filename);
if (cachedFile != null) { if (cachedFile != null) {
return cachedFile; return cachedFile;
@@ -237,6 +244,8 @@ public final class ImageKeys {
} }
// System.out.println("File not found, no image created: " + key); // System.out.println("File not found, no image created: " + key);
//add missing cards
missingCards.add(filename);
return null; return null;
} }

View File

@@ -178,7 +178,13 @@ public final class GameActionUtil {
final String[] k = keyword.split(":"); final String[] k = keyword.split(":");
final Cost disturbCost = new Cost(k[1], true); final Cost disturbCost = new Cost(k[1], true);
final SpellAbility newSA = sa.copyWithManaCostReplaced(activator, disturbCost); SpellAbility newSA;
if (source.getAlternateState().getType().hasSubtype("Aura")) {
newSA = source.getAlternateState().getFirstAbility().copyWithManaCostReplaced(activator,
disturbCost);
} else {
newSA = sa.copyWithManaCostReplaced(activator, disturbCost);
}
newSA.setActivatingPlayer(activator); newSA.setActivatingPlayer(activator);
newSA.putParam("PrecostDesc", "Disturb —"); newSA.putParam("PrecostDesc", "Disturb —");
@@ -190,11 +196,6 @@ public final class GameActionUtil {
desc.append("(").append(inst.getReminderText()).append(")"); desc.append("(").append(inst.getReminderText()).append(")");
newSA.setDescription(desc.toString()); newSA.setDescription(desc.toString());
newSA.putParam("AfterDescription", "(Disturbed)"); newSA.putParam("AfterDescription", "(Disturbed)");
final String type = source.getAlternateState().getType().toString();
if (!type.contains("Creature")) {
final String name = source.getAlternateState().getName();
newSA.putParam("StackDescription", name + "" + type + " (Disturbed)");
}
newSA.setAlternativeCost(AlternativeCost.Disturb); newSA.setAlternativeCost(AlternativeCost.Disturb);
newSA.getRestrictions().setZone(ZoneType.Graveyard); newSA.getRestrictions().setZone(ZoneType.Graveyard);
@@ -733,7 +734,7 @@ public final class GameActionUtil {
return list; return list;
} }
CardCollection completeList = new CardCollection(); CardCollection completeList = new CardCollection();
PlayerCollection players = game.getPlayers(); PlayerCollection players = new PlayerCollection(game.getPlayers());
// CR 613.7k use APNAP // CR 613.7k use APNAP
int indexAP = players.indexOf(game.getPhaseHandler().getPlayerTurn()); int indexAP = players.indexOf(game.getPhaseHandler().getPlayerTurn());
if (indexAP != -1) { if (indexAP != -1) {

View File

@@ -36,7 +36,7 @@ public final class GameObjectPredicates {
return new Predicate<GameObject>() { return new Predicate<GameObject>() {
@Override @Override
public boolean apply(final GameObject c) { public boolean apply(final GameObject c) {
return (c != null) && c.isValid(restrictions, sourceController, source, spellAbility); return c != null && c.isValid(restrictions, sourceController, source, spellAbility);
} }
}; };
} }

View File

@@ -197,6 +197,15 @@ public final class CardPredicates {
}; };
} }
public static Predicate<Card> sharesLandTypeWith(final Card card) {
return new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
return c.sharesLandTypeWith(card);
}
};
}
public static final Predicate<Card> possibleBlockers(final Card attacker) { public static final Predicate<Card> possibleBlockers(final Card attacker) {
return new Predicate<Card>() { return new Predicate<Card>() {
@Override @Override

View File

@@ -825,6 +825,11 @@ public class CardProperty {
} }
} }
} }
} else if (property.startsWith("sharesLandTypeWith")) {
final String restriction = property.split("sharesLandTypeWith ")[1];
if (!Iterables.any(AbilityUtils.getDefinedCards(source, restriction, spellAbility), CardPredicates.sharesLandTypeWith(card))) {
return false;
}
} else if (property.equals("sharesPermanentTypeWith")) { } else if (property.equals("sharesPermanentTypeWith")) {
if (!card.sharesPermanentTypeWith(source)) { if (!card.sharesPermanentTypeWith(source)) {
return false; return false;

View File

@@ -832,12 +832,7 @@ public class Cost implements Serializable {
sb.append(Cost.NUM_NAMES[i]); sb.append(Cost.NUM_NAMES[i]);
} }
sb.append(" "); sb.append(" ").append(type);
char firstChar = type.charAt(0);
if (Character.isUpperCase(firstChar)) { //fix case of type before appending
type = Character.toLowerCase(firstChar) + type.substring(1);
}
sb.append(type);
if (1 != i) { if (1 != i) {
sb.append("s"); sb.append("s");
} }

View File

@@ -2,7 +2,7 @@ package forge.game.cost;
import forge.game.player.Player; import forge.game.player.Player;
public abstract class CostDecisionMakerBase implements ICostVisitor<PaymentDecision> { public abstract class CostDecisionMakerBase implements ICostVisitor<PaymentDecision> {
protected final Player player; protected final Player player;
public CostDecisionMakerBase(Player player0) { public CostDecisionMakerBase(Player player0) {

View File

@@ -199,7 +199,9 @@ public class CostDiscard extends CostPartWithList {
// discard itself for cycling cost // discard itself for cycling cost
runParams.put(AbilityKey.Cycling, true); runParams.put(AbilityKey.Cycling, true);
} }
return targetCard.getController().discard(targetCard, null, null, runParams); // if this is caused by 118.12 it's also an effect
SpellAbility cause = targetCard.getGame().getStack().isResolving(ability.getHostCard()) ? ability : null;
return targetCard.getController().discard(targetCard, cause, null, runParams);
} }
/* (non-Javadoc) /* (non-Javadoc)
@@ -226,8 +228,7 @@ public class CostDiscard extends CostPartWithList {
protected void handleChangeZoneTrigger(Player payer, SpellAbility ability, CardCollectionView targetCards) { protected void handleChangeZoneTrigger(Player payer, SpellAbility ability, CardCollectionView targetCards) {
super.handleChangeZoneTrigger(payer, ability, targetCards); super.handleChangeZoneTrigger(payer, ability, targetCards);
if (!targetCards.isEmpty()) if (!targetCards.isEmpty()) {
{
final Map<AbilityKey, Object> runParams = AbilityKey.newMap(); final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
runParams.put(AbilityKey.Player, payer); runParams.put(AbilityKey.Player, payer);
runParams.put(AbilityKey.Cards, new CardCollection(targetCards)); runParams.put(AbilityKey.Cards, new CardCollection(targetCards));

View File

@@ -84,7 +84,6 @@ public class CostExert extends CostPartWithList {
public final boolean canPay(final SpellAbility ability, final Player payer) { public final boolean canPay(final SpellAbility ability, final Player payer) {
final Card source = ability.getHostCard(); final Card source = ability.getHostCard();
if (!this.payCostFromSource()) { if (!this.payCostFromSource()) {
boolean needsAnnoucement = ability.hasParam("Announce") && this.getType().contains(ability.getParam("Announce")); boolean needsAnnoucement = ability.hasParam("Announce") && this.getType().contains(ability.getParam("Announce"));
@@ -94,7 +93,6 @@ public class CostExert extends CostPartWithList {
return needsAnnoucement || (amount == null) || (typeList.size() >= amount); return needsAnnoucement || (amount == null) || (typeList.size() >= amount);
} }
return true; return true;

View File

@@ -120,5 +120,5 @@ public class CostExileFromStack extends CostPart {
public <T> T accept(ICostVisitor<T> visitor) { public <T> T accept(ICostVisitor<T> visitor) {
return visitor.visit(this); return visitor.visit(this);
} }
} }

View File

@@ -63,7 +63,7 @@ public class CostGainLife extends CostPart {
sb.append("Have an opponent gain ").append(this.getAmount()).append(" life"); sb.append("Have an opponent gain ").append(this.getAmount()).append(" life");
return sb.toString(); return sb.toString();
} }
public List<Player> getPotentialTargets(final Player payer, final Card source) { public List<Player> getPotentialTargets(final Player payer, final Card source) {
List<Player> res = new ArrayList<>(); List<Player> res = new ArrayList<>();
for (Player p : payer.getGame().getPlayers()) { for (Player p : payer.getGame().getPlayers()) {
@@ -121,7 +121,6 @@ public class CostGainLife extends CostPart {
return true; return true;
} }
public <T> T accept(ICostVisitor<T> visitor) { public <T> T accept(ICostVisitor<T> visitor) {
return visitor.visit(this); return visitor.visit(this);
} }

View File

@@ -57,7 +57,7 @@ public class CostPayment extends ManaConversionMatrix {
public final SpellAbility getAbility() { public final SpellAbility getAbility() {
return this.ability; return this.ability;
} }
/** /**
* <p> * <p>
* Constructor for Cost_Payment. * Constructor for Cost_Payment.

View File

@@ -151,7 +151,6 @@ public class CostPutCounter extends CostPartWithList {
return !typeList.isEmpty(); return !typeList.isEmpty();
} }
} }
/* /*
@@ -204,7 +203,6 @@ public class CostPutCounter extends CostPartWithList {
tempTable.triggerCountersPutAll(ability.getHostCard().getGame()); tempTable.triggerCountersPutAll(ability.getHostCard().getGame());
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see forge.game.cost.CostPartWithList#resetLists() * @see forge.game.cost.CostPartWithList#resetLists()
*/ */

View File

@@ -38,17 +38,16 @@ public class CostTap extends CostPart {
} }
public int paymentOrder() { return -1; } public int paymentOrder() { return -1; }
@Override @Override
public boolean isUndoable() { return true; } public boolean isUndoable() { return true; }
@Override @Override
public boolean isReusable() { return true; } public boolean isReusable() { return true; }
@Override @Override
public boolean isRenewable() { return true; } public boolean isRenewable() { return true; }
@Override @Override
public final String toString() { public final String toString() {
return "{T}"; return "{T}";
@@ -70,7 +69,7 @@ public class CostTap extends CostPart {
ability.getHostCard().tap(true); ability.getHostCard().tap(true);
return true; return true;
} }
public <T> T accept(ICostVisitor<T> visitor) { public <T> T accept(ICostVisitor<T> visitor) {
return visitor.visit(this); return visitor.visit(this);
} }

View File

@@ -78,7 +78,7 @@ public class CostTapType extends CostPartWithList {
@Override @Override
public boolean isRenewable() { return true; } public boolean isRenewable() { return true; }
/* /*
* (non-Javadoc) * (non-Javadoc)
* *
@@ -131,7 +131,7 @@ public class CostTapType extends CostPartWithList {
String type = this.getType(); String type = this.getType();
boolean sameType = false; boolean sameType = false;
if (type.contains(".sharesCreatureTypeWith")) { if (type.contains(".sharesCreatureTypeWith")) {
sameType = true; sameType = true;
type = TextUtil.fastReplace(type, ".sharesCreatureTypeWith", ""); type = TextUtil.fastReplace(type, ".sharesCreatureTypeWith", "");

View File

@@ -45,11 +45,10 @@ public class CostUntap extends CostPart {
@Override @Override
public boolean isUndoable() { return true; } public boolean isUndoable() { return true; }
@Override @Override
public boolean isRenewable() { return true; } public boolean isRenewable() { return true; }
/* /*
* (non-Javadoc) * (non-Javadoc)
* *
@@ -88,7 +87,7 @@ public class CostUntap extends CostPart {
ability.getHostCard().untap(true); ability.getHostCard().untap(true);
return true; return true;
} }
public <T> T accept(ICostVisitor<T> visitor) { public <T> T accept(ICostVisitor<T> visitor) {
return visitor.visit(this); return visitor.visit(this);
} }

View File

@@ -38,7 +38,7 @@ public class PaymentDecision {
this.counterTable = counterTable; this.counterTable = counterTable;
} }
private PaymentDecision(Card chosen) { private PaymentDecision(Card chosen) {
this(null, null, null, null, null); this(null, null, null, null, null);
cards.add(chosen); cards.add(chosen);
} }
@@ -81,7 +81,7 @@ public class PaymentDecision {
*/ */
@Override @Override
public String toString() { public String toString() {
return TextUtil.concatWithSpace("Payment Decision:", TextUtil.addSuffix(String.valueOf(c),","), cards.toString()); return TextUtil.concatWithSpace("Payment Decision:", TextUtil.addSuffix(String.valueOf(c),","), cards.toString());
} }
public static PaymentDecision type(String choice) { public static PaymentDecision type(String choice) {

View File

@@ -45,7 +45,6 @@ public class Mana {
return result; return result;
} }
@Override @Override
public boolean equals(Object other) { public boolean equals(Object other) {
if (!(other instanceof Mana)) { if (!(other instanceof Mana)) {
@@ -89,12 +88,10 @@ public class Mana {
return this.manaAbility != null && (!manaAbility.getManaRestrictions().isEmpty() || !manaAbility.getExtraManaRestriction().isEmpty()); return this.manaAbility != null && (!manaAbility.getManaRestrictions().isEmpty() || !manaAbility.getExtraManaRestriction().isEmpty());
} }
public final boolean addsNoCounterMagic(SpellAbility saBeingPaid) { public final boolean addsNoCounterMagic(SpellAbility saBeingPaid) {
return this.manaAbility != null && manaAbility.cannotCounterPaidWith(saBeingPaid); return this.manaAbility != null && manaAbility.cannotCounterPaidWith(saBeingPaid);
} }
public final boolean addsCounters(SpellAbility saBeingPaid) { public final boolean addsCounters(SpellAbility saBeingPaid) {
return this.manaAbility != null && manaAbility.addsCounters(saBeingPaid); return this.manaAbility != null && manaAbility.addsCounters(saBeingPaid);
} }

View File

@@ -154,8 +154,7 @@ public class ManaCostBeingPaid {
for (ManaCostShard shard : manaCost) { for (ManaCostShard shard : manaCost) {
if (shard == ManaCostShard.X) { if (shard == ManaCostShard.X) {
cntX++; cntX++;
} } else {
else {
increaseShard(shard, 1, false); increaseShard(shard, 1, false);
} }
} }
@@ -588,8 +587,7 @@ public class ManaCostBeingPaid {
for (ManaCostShard shard : extra) { for (ManaCostShard shard : extra) {
if (shard == ManaCostShard.X) { if (shard == ManaCostShard.X) {
cntX++; cntX++;
} } else {
else {
increaseShard(shard, 1, false); increaseShard(shard, 1, false);
} }
} }

View File

@@ -119,4 +119,4 @@ public class ExtraTurn {
this.cantSetSchemesInMotion = noSchemes; this.cantSetSchemesInMotion = noSchemes;
} }
} //end class Untap }

View File

@@ -226,8 +226,7 @@ public class Untap extends Phase {
// even if they are not creatures // even if they are not creatures
for (final Card c : game.getCardsInGame()) { for (final Card c : game.getCardsInGame()) {
c.removeExertedBy(player); c.removeExertedBy(player);
} }
} // end doUntap } // end doUntap
private static void optionalUntap(final Card c) { private static void optionalUntap(final Card c) {
@@ -261,7 +260,7 @@ public class Untap extends Phase {
@Override @Override
public boolean apply(final Card c) { public boolean apply(final Card c) {
return ((c.isPhasedOut() && c.isDirectlyPhasedOut()) || c.hasKeyword(Keyword.PHASING)); return (c.isPhasedOut() && c.isDirectlyPhasedOut()) || c.hasKeyword(Keyword.PHASING);
} }
}); });

View File

@@ -63,7 +63,6 @@ public final class PlayerPredicates {
return Predicates.not(isCardInPlay(cardName)); return Predicates.not(isCardInPlay(cardName));
} }
public static final Predicate<Player> hasCounters() { public static final Predicate<Player> hasCounters() {
return new Predicate<Player>() { return new Predicate<Player>() {
@Override @Override

View File

@@ -350,7 +350,6 @@ public class AbilityManaPart implements java.io.Serializable {
} }
} }
if (sa.getHostCard() != null) { if (sa.getHostCard() != null) {
if (sa.getHostCard().isValid(restriction, this.getSourceCard().getController(), this.getSourceCard(), null)) { if (sa.getHostCard().isValid(restriction, this.getSourceCard().getController(), this.getSourceCard(), null)) {
return true; return true;
@@ -583,7 +582,6 @@ public class AbilityManaPart implements java.io.Serializable {
public Card getSourceCard() { public Card getSourceCard() {
return sourceCard; return sourceCard;
} }
public void setSourceCard(final Card host) { public void setSourceCard(final Card host) {
sourceCard = host; sourceCard = host;
} }

View File

@@ -682,12 +682,12 @@ public final class StaticAbilityContinuous {
// set P/T // set P/T
if (layer == StaticAbilityLayer.SETPT) { if (layer == StaticAbilityLayer.SETPT) {
if ((setPower != null) || (setToughness != null)) { if (setPower != null || setToughness != null) {
// non CharacteristicDefining // non CharacteristicDefining
if (setP.startsWith("Affected")) { if (setP.contains("Affected")) {
setPower = AbilityUtils.calculateAmount(affectedCard, setP, stAb, true); setPower = AbilityUtils.calculateAmount(affectedCard, setP, stAb, true);
} }
if (setT.startsWith("Affected")) { if (setT.contains("Affected")) {
setToughness = AbilityUtils.calculateAmount(affectedCard, setT, stAb, true); setToughness = AbilityUtils.calculateAmount(affectedCard, setT, stAb, true);
} }
affectedCard.addNewPT(setPower, setToughness, affectedCard.addNewPT(setPower, setToughness,
@@ -697,10 +697,10 @@ public final class StaticAbilityContinuous {
// add P/T bonus // add P/T bonus
if (layer == StaticAbilityLayer.MODIFYPT) { if (layer == StaticAbilityLayer.MODIFYPT) {
if (addP.startsWith("Affected")) { if (addP.contains("Affected")) {
powerBonus = AbilityUtils.calculateAmount(affectedCard, addP, stAb, true); powerBonus = AbilityUtils.calculateAmount(affectedCard, addP, stAb, true);
} }
if (addT.startsWith("Affected")) { if (addT.contains("Affected")) {
toughnessBonus = AbilityUtils.calculateAmount(affectedCard, addT, stAb, true); toughnessBonus = AbilityUtils.calculateAmount(affectedCard, addT, stAb, true);
} }
affectedCard.addPTBoost(powerBonus, toughnessBonus, se.getTimestamp(), stAb.getId()); affectedCard.addPTBoost(powerBonus, toughnessBonus, se.getTimestamp(), stAb.getId());
@@ -709,7 +709,7 @@ public final class StaticAbilityContinuous {
// add keywords // add keywords
// TODO regular keywords currently don't try to use keyword multiplier // TODO regular keywords currently don't try to use keyword multiplier
// (Although nothing uses it at this time) // (Although nothing uses it at this time)
if ((addKeywords != null) || (removeKeywords != null) || removeAllAbilities) { if (addKeywords != null || removeKeywords != null || removeAllAbilities) {
List<String> newKeywords = null; List<String> newKeywords = null;
if (addKeywords != null) { if (addKeywords != null) {
newKeywords = Lists.newArrayList(addKeywords); newKeywords = Lists.newArrayList(addKeywords);

View File

@@ -84,6 +84,5 @@ public abstract class Expressions {
} }
return " ? "; return " ? ";
} }
}
} // end class AllZoneUtil

View File

@@ -290,6 +290,7 @@ public class GuiDesktop implements IGuiBase {
@Override @Override
public void clearImageCache() { public void clearImageCache() {
ImageCache.clear(); ImageCache.clear();
ImageKeys.clearMissingCards();
} }
@Override @Override

View File

@@ -96,6 +96,7 @@ public class ImageCache {
public static void clear() { public static void clear() {
_CACHE.invalidateAll(); _CACHE.invalidateAll();
_missingIconKeys.clear(); _missingIconKeys.clear();
ImageKeys.clearMissingCards();
} }
/** /**

View File

@@ -19,6 +19,7 @@ import forge.model.FModel;
import forge.player.GamePlayerUtil; import forge.player.GamePlayerUtil;
import forge.screens.deckeditor.CDeckEditorUI; import forge.screens.deckeditor.CDeckEditorUI;
import forge.screens.deckeditor.controllers.CEditorTokenViewer; import forge.screens.deckeditor.controllers.CEditorTokenViewer;
import forge.sound.MusicPlaylist;
import forge.sound.SoundSystem; import forge.sound.SoundSystem;
import forge.toolbox.*; import forge.toolbox.*;
import forge.util.Localizer; import forge.util.Localizer;
@@ -27,6 +28,8 @@ import org.apache.commons.lang3.tuple.Pair;
import javax.swing.*; import javax.swing.*;
import java.awt.*; import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent; import java.awt.event.ItemEvent;
import java.awt.event.ItemListener; import java.awt.event.ItemListener;
import java.io.File; import java.io.File;
@@ -262,6 +265,8 @@ public enum CSubmenuPreferences implements ICDoc {
initializeAutoUpdaterComboBox(); initializeAutoUpdaterComboBox();
initializeMulliganRuleComboBox(); initializeMulliganRuleComboBox();
initializeAiProfilesComboBox(); initializeAiProfilesComboBox();
initializeSoundSetsComboBox();
initializeMusicSetsComboBox();
initializeStackAdditionsComboBox(); initializeStackAdditionsComboBox();
initializeLandPlayedComboBox(); initializeLandPlayedComboBox();
initializeColorIdentityCombobox(); initializeColorIdentityCombobox();
@@ -466,6 +471,35 @@ public enum CSubmenuPreferences implements ICDoc {
panel.setComboBox(comboBox, selectedItem); panel.setComboBox(comboBox, selectedItem);
} }
private void initializeSoundSetsComboBox() {
final FPref userSetting = FPref.UI_CURRENT_SOUND_SET;
final FComboBoxPanel<String> panel = this.view.getSoundSetsComboBoxPanel();
final FComboBox<String> comboBox = createComboBox(SoundSystem.instance.getAvailableSoundSets(), userSetting);
final String selectedItem = this.prefs.getPref(userSetting);
panel.setComboBox(comboBox, selectedItem);
comboBox.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent actionEvent) {
SoundSystem.instance.invalidateSoundCache();
}
});
}
private void initializeMusicSetsComboBox() {
final FPref userSetting = FPref.UI_CURRENT_MUSIC_SET;
final FComboBoxPanel<String> panel = this.view.getMusicSetsComboBoxPanel();
final FComboBox<String> comboBox = createComboBox(SoundSystem.instance.getAvailableMusicSets(), userSetting);
final String selectedItem = this.prefs.getPref(userSetting);
panel.setComboBox(comboBox, selectedItem);
comboBox.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent actionEvent) {
MusicPlaylist.invalidateMusicPlaylist();
SoundSystem.instance.changeBackgroundTrack();
}
});
}
private void initializeCardArtPreference() { private void initializeCardArtPreference() {
final String latestOpt = Localizer.getInstance().getMessage("latestArtOpt"); final String latestOpt = Localizer.getInstance().getMessage("latestArtOpt");
final String originalOpt = Localizer.getInstance().getMessage("originalArtOpt"); final String originalOpt = Localizer.getInstance().getMessage("originalArtOpt");

View File

@@ -128,6 +128,8 @@ public enum VSubmenuPreferences implements IVSubmenu<CSubmenuPreferences> {
private final FComboBoxPanel<String> cbpCardArtFormat = new FComboBoxPanel<>(localizer.getMessage("cbpCardArtFormat")+":"); private final FComboBoxPanel<String> cbpCardArtFormat = new FComboBoxPanel<>(localizer.getMessage("cbpCardArtFormat")+":");
private final FComboBoxPanel<String> cbpCardArtPreference = new FComboBoxPanel<>(localizer.getMessage("lblPreferredArt")+":"); private final FComboBoxPanel<String> cbpCardArtPreference = new FComboBoxPanel<>(localizer.getMessage("lblPreferredArt")+":");
private final FComboBoxPanel<String> cbpMulliganRule = new FComboBoxPanel<>(localizer.getMessage("cbpMulliganRule")+":"); private final FComboBoxPanel<String> cbpMulliganRule = new FComboBoxPanel<>(localizer.getMessage("cbpMulliganRule")+":");
private final FComboBoxPanel<String> cbpSoundSets = new FComboBoxPanel<>(localizer.getMessage("cbpSoundSets")+":");
private final FComboBoxPanel<String> cbpMusicSets = new FComboBoxPanel<>(localizer.getMessage("cbpMusicSets")+":");
private final FComboBoxPanel<String> cbpAiProfiles = new FComboBoxPanel<>(localizer.getMessage("cbpAiProfiles")+":"); private final FComboBoxPanel<String> cbpAiProfiles = new FComboBoxPanel<>(localizer.getMessage("cbpAiProfiles")+":");
private final FComboBoxPanel<String> cbpStackAdditions = new FComboBoxPanel<>(localizer.getMessage("cbpStackAdditions")+":"); private final FComboBoxPanel<String> cbpStackAdditions = new FComboBoxPanel<>(localizer.getMessage("cbpStackAdditions")+":");
private final FComboBoxPanel<String> cbpLandPlayed = new FComboBoxPanel<>(localizer.getMessage("cbpLandPlayed")+":"); private final FComboBoxPanel<String> cbpLandPlayed = new FComboBoxPanel<>(localizer.getMessage("cbpLandPlayed")+":");
@@ -414,9 +416,15 @@ public enum VSubmenuPreferences implements IVSubmenu<CSubmenuPreferences> {
pnlPrefs.add(cbEnableSounds, titleConstraints); pnlPrefs.add(cbEnableSounds, titleConstraints);
pnlPrefs.add(new NoteLabel(localizer.getMessage("nlEnableSounds")), descriptionConstraints); pnlPrefs.add(new NoteLabel(localizer.getMessage("nlEnableSounds")), descriptionConstraints);
pnlPrefs.add(cbpSoundSets, comboBoxConstraints);
pnlPrefs.add(new NoteLabel(localizer.getMessage("nlpSoundSets")), descriptionConstraints);
pnlPrefs.add(cbEnableMusic, titleConstraints); pnlPrefs.add(cbEnableMusic, titleConstraints);
pnlPrefs.add(new NoteLabel(localizer.getMessage("nlEnableMusic")), descriptionConstraints); pnlPrefs.add(new NoteLabel(localizer.getMessage("nlEnableMusic")), descriptionConstraints);
pnlPrefs.add(cbpMusicSets, comboBoxConstraints);
pnlPrefs.add(new NoteLabel(localizer.getMessage("nlpMusicSets")), descriptionConstraints);
pnlPrefs.add(cbAltSoundSystem, titleConstraints); pnlPrefs.add(cbAltSoundSystem, titleConstraints);
pnlPrefs.add(new NoteLabel(localizer.getMessage("nlAltSoundSystem")), descriptionConstraints); pnlPrefs.add(new NoteLabel(localizer.getMessage("nlAltSoundSystem")), descriptionConstraints);
pnlPrefs.add(cbSROptimize, titleConstraints); pnlPrefs.add(cbSROptimize, titleConstraints);
@@ -740,6 +748,14 @@ public enum VSubmenuPreferences implements IVSubmenu<CSubmenuPreferences> {
return cbpMulliganRule; return cbpMulliganRule;
} }
public FComboBoxPanel<String> getSoundSetsComboBoxPanel() {
return cbpSoundSets;
}
public FComboBoxPanel<String> getMusicSetsComboBoxPanel() {
return cbpMusicSets;
}
public FComboBoxPanel<String> getAiProfilesComboBoxPanel() { public FComboBoxPanel<String> getAiProfilesComboBoxPanel() {
return cbpAiProfiles; return cbpAiProfiles;
} }

View File

@@ -40,9 +40,6 @@ import javax.sound.sampled.UnsupportedAudioFileException;
import com.google.common.io.Files; import com.google.common.io.Files;
import com.sipgate.mp3wav.Converter; import com.sipgate.mp3wav.Converter;
import forge.localinstance.properties.ForgeConstants;
/** /**
* SoundSystem - a simple sound playback system for Forge. * SoundSystem - a simple sound playback system for Forge.
* Do not use directly. Instead, use the {@link forge.sound.SoundEffectType} enumeration. * Do not use directly. Instead, use the {@link forge.sound.SoundEffectType} enumeration.
@@ -64,7 +61,7 @@ public class AudioClip implements IAudioClip {
} }
public static boolean fileExists(String fileName) { public static boolean fileExists(String fileName) {
File fSound = new File(ForgeConstants.SOUND_DIR, fileName); File fSound = new File(SoundSystem.instance.getSoundDirectory(), fileName);
return fSound.exists(); return fSound.exists();
} }
@@ -195,7 +192,7 @@ public class AudioClip implements IAudioClip {
} }
private Clip createClip(String filename) { private Clip createClip(String filename) {
File fSound = new File(ForgeConstants.SOUND_DIR, filename); File fSound = new File(SoundSystem.instance.getSoundDirectory(), filename);
if (!fSound.exists()) { if (!fSound.exists()) {
throw new IllegalArgumentException("Sound file " + fSound.toString() + " does not exist, cannot make a clip of it"); throw new IllegalArgumentException("Sound file " + fSound.toString() + " does not exist, cannot make a clip of it");
} }

View File

@@ -132,9 +132,9 @@ public class FCheckBoxTree extends JTree {
this.selectedChildrenCount = selectedChildrenCount; this.selectedChildrenCount = selectedChildrenCount;
this.enabledChildrenCount = enabledChildrenCount; this.enabledChildrenCount = enabledChildrenCount;
} }
public boolean hasChildren() { return this.numberOfChildren > 0;} public boolean hasChildren() { return this.numberOfChildren > 0; }
public boolean allChildrenSelected(){ return this.numberOfChildren == this.selectedChildrenCount; }; public boolean allChildrenSelected() { return this.numberOfChildren == this.selectedChildrenCount; }
public boolean allChildrenEnabled(){ return this.enabledChildrenCount == this.numberOfChildren; }; public boolean allChildrenEnabled() { return this.enabledChildrenCount == this.numberOfChildren; }
} }
// == Fields of the FCheckboxTree class == // == Fields of the FCheckboxTree class ==

View File

@@ -85,6 +85,7 @@ public class Forge implements ApplicationListener {
public static String CJK_Font = ""; public static String CJK_Font = "";
public static int hoveredCount = 0; public static int hoveredCount = 0;
public static boolean afterDBloaded = false; public static boolean afterDBloaded = false;
public static int mouseButtonID = 0;
public static ApplicationListener getApp(Clipboard clipboard0, IDeviceAdapter deviceAdapter0, String assetDir0, boolean value, boolean androidOrientation, int totalRAM, boolean isTablet, int AndroidAPI, String AndroidRelease, String deviceName) { public static ApplicationListener getApp(Clipboard clipboard0, IDeviceAdapter deviceAdapter0, String assetDir0, boolean value, boolean androidOrientation, int totalRAM, boolean isTablet, int AndroidAPI, String AndroidRelease, String deviceName) {
if (GuiBase.getInterface() == null) { if (GuiBase.getInterface() == null) {
@@ -771,6 +772,7 @@ public class Forge implements ApplicationListener {
} }
} }
} }
mouseButtonID = button;
return super.touchDown(x, y, pointer, button); return super.touchDown(x, y, pointer, button);
} }

View File

@@ -21,10 +21,7 @@ import forge.screens.LoadingOverlay;
import forge.screens.match.MatchController; import forge.screens.match.MatchController;
import forge.screens.quest.QuestMenu; import forge.screens.quest.QuestMenu;
import forge.screens.settings.GuiDownloader; import forge.screens.settings.GuiDownloader;
import forge.sound.AudioClip; import forge.sound.*;
import forge.sound.AudioMusic;
import forge.sound.IAudioClip;
import forge.sound.IAudioMusic;
import forge.toolbox.FOptionPane; import forge.toolbox.FOptionPane;
import forge.toolbox.GuiChoose; import forge.toolbox.GuiChoose;
import forge.util.*; import forge.util.*;
@@ -272,7 +269,7 @@ public class GuiMobile implements IGuiBase {
@Override @Override
public IAudioClip createAudioClip(final String filename) { public IAudioClip createAudioClip(final String filename) {
return AudioClip.createClip(ForgeConstants.SOUND_DIR + filename); return AudioClip.createClip(SoundSystem.instance.getSoundDirectory() + filename);
} }
@Override @Override
@@ -288,6 +285,7 @@ public class GuiMobile implements IGuiBase {
@Override @Override
public void clearImageCache() { public void clearImageCache() {
ImageCache.clear(); ImageCache.clear();
ImageKeys.clearMissingCards();
} }
@Override @Override

View File

@@ -113,6 +113,7 @@ public class ImageCache {
public static void clear() { public static void clear() {
missingIconKeys.clear(); missingIconKeys.clear();
ImageKeys.clearMissingCards();
} }
public static void disposeTexture(){ public static void disposeTexture(){

View File

@@ -113,7 +113,7 @@ public class NewGauntletScreen extends LaunchScreen {
}); });
} }
}); });
chooser.show(null, true); chooser.show(null, false); /*setting selectMax to true will select all available option*/
} }
}); });
} }

View File

@@ -6,8 +6,10 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector2;
import forge.Forge;
import forge.Graphics; import forge.Graphics;
import forge.card.CardRenderer.CardStackPosition; import forge.card.CardRenderer.CardStackPosition;
import forge.card.CardZoom; import forge.card.CardZoom;
@@ -324,7 +326,15 @@ public abstract class VCardDisplayArea extends VDisplayArea implements ActivateH
ThreadUtil.invokeInGameThread(new Runnable() { //must invoke in game thread in case a dialog needs to be shown ThreadUtil.invokeInGameThread(new Runnable() { //must invoke in game thread in case a dialog needs to be shown
@Override @Override
public void run() { public void run() {
if (!selectCard(false)) { if (GuiBase.getInterface().isRunningOnDesktop() && Forge.mouseButtonID == Input.Buttons.RIGHT) {
FThreads.invokeInEdtLater(new Runnable() {
@Override
public void run() {
showZoom();
}
});
return;
} else if (!selectCard(false)) {
//if no cards in stack can be selected, just show zoom/details for card //if no cards in stack can be selected, just show zoom/details for card
FThreads.invokeInEdtLater(new Runnable() { FThreads.invokeInEdtLater(new Runnable() {
@Override @Override

View File

@@ -18,6 +18,7 @@ import forge.screens.TabPageScreen;
import forge.screens.TabPageScreen.TabPage; import forge.screens.TabPageScreen.TabPage;
import forge.screens.home.HomeScreen; import forge.screens.home.HomeScreen;
import forge.screens.match.MatchController; import forge.screens.match.MatchController;
import forge.sound.MusicPlaylist;
import forge.sound.SoundSystem; import forge.sound.SoundSystem;
import forge.toolbox.FCheckBox; import forge.toolbox.FCheckBox;
import forge.toolbox.FGroupList; import forge.toolbox.FGroupList;
@@ -612,6 +613,29 @@ public class SettingsPage extends TabPage<SettingsScreen> {
localizer.getMessage("nlVibrateAfterLongPress")), localizer.getMessage("nlVibrateAfterLongPress")),
6); 6);
//Sound Options //Sound Options
lstSettings.addItem(new CustomSelectSetting(FPref.UI_CURRENT_SOUND_SET,
localizer.getMessage("cbpSoundSets"),
localizer.getMessage("nlpSoundSets"),
SoundSystem.instance.getAvailableSoundSets()) {
@Override
public void valueChanged(String newValue) {
super.valueChanged(newValue);
SoundSystem.instance.invalidateSoundCache();
}
},
7);
lstSettings.addItem(new CustomSelectSetting(FPref.UI_CURRENT_MUSIC_SET,
localizer.getMessage("cbpMusicSets"),
localizer.getMessage("nlpMusicSets"),
SoundSystem.getAvailableMusicSets()) {
@Override
public void valueChanged(String newValue) {
super.valueChanged(newValue);
MusicPlaylist.invalidateMusicPlaylist();
SoundSystem.instance.changeBackgroundTrack();
}
},
7);
lstSettings.addItem(new CustomSelectSetting(FPref.UI_VOL_SOUNDS, lstSettings.addItem(new CustomSelectSetting(FPref.UI_VOL_SOUNDS,
localizer.getMessage("cbAdjustSoundsVolume"), localizer.getMessage("cbAdjustSoundsVolume"),
localizer.getMessage("nlAdjustSoundsVolume"), localizer.getMessage("nlAdjustSoundsVolume"),

View File

@@ -1,6 +1,8 @@
#Add one announcement per line #Add one announcement per line
Get in the discord if you aren't yet. https://discord.gg/3v9JCVr Get in the discord if you aren't yet. https://discord.gg/3v9JCVr
The Throne of Eldraine quest world is the largest and most meticulously developed quest world to date, combining fascinating fairytales and interesting interactions. If you've never baked a pie into a pie, have you really played Magic? Forge now supports 100% of core and expansion set cards from Limited Edition Alpha to Innistrad: Midnight Hunt. This includes Equinox, Chaos Orb, and Falling Star.
Innistrad: Midnight Hunt (MID) and Innistrad: Midnight Hunt Commander (MIC) are 100% supported Planar Conquest now features a new plane - Forgotten Realms, based on the AFR and AFC sets.
Various improvements have been made to the desktop and mobile user interface to enhance the experience Several planes are now available in Planar Conquest in their Classic form, as originally intended by the author, without support for the newer sets and with the original events not modified with newer cards. These planes are available in addition to the contemporary versions of the planes and are marked as "Classic".
Sound sets are now configurable - you can place your sound sets under "sound" in your Forge cache (as subfolders) and then select them with the "Sound set" option.
Music sets are now configurable - you can place your music sets under "music" in your Forge cache (as subfolders) and then select them with the "Music set" option. The music set folder must have the same structure as "res/music", with "match" and "menus" subfolders containing relevant music tracks.
*** Android 7 & 8 support is now deprecated. Support will be dropped in an upcoming release. *** *** Android 7 & 8 support is now deprecated. Support will be dropped in an upcoming release. ***

View File

@@ -1,10 +1,10 @@
{ {
"name":"Djinni", "name":"Djinn",
"template": "template":
{ {
"count":60, "count":60,
"colors":["Blue","Red"], "colors":["Blue","Red"],
"tribe":"Djinni", "tribe":"Djinn",
"tribeCards":0.7, "tribeCards":0.7,
"tribeSynergyCards":0.0, "tribeSynergyCards":0.0,
"rares":0.5 "rares":0.5

View File

@@ -1,8 +1,8 @@
{ {
"activeFile": "map/swamp_town.tmx", "activeFile": "map/island_town.tmx",
"expandedProjectPaths": [ "expandedProjectPaths": [
"map",
"tileset", "tileset",
"map",
"obj" "obj"
], ],
"file.lastUsedOpenFilter": "Alle Dateien (*)", "file.lastUsedOpenFilter": "Alle Dateien (*)",
@@ -33,10 +33,10 @@
}, },
"map/island_town.tmx": { "map/island_town.tmx": {
"scale": 3, "scale": 3,
"selectedLayer": 2, "selectedLayer": 1,
"viewCenter": { "viewCenter": {
"x": 240, "x": 251,
"y": 136 "y": 73
} }
}, },
"map/mountain_town.tmx": { "map/mountain_town.tmx": {
@@ -100,9 +100,9 @@
"tileset/buildings.tsx", "tileset/buildings.tsx",
"tileset/main.tsx", "tileset/main.tsx",
"map/forest_town.tmx", "map/forest_town.tmx",
"map/island_town.tmx", "map/swamp_town.tmx",
"map/mountain_town.tmx",
"map/plains_town.tmx", "map/plains_town.tmx",
"map/swamp_town.tmx" "map/mountain_town.tmx",
"map/island_town.tmx"
] ]
} }

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<map version="1.5" tiledversion="1.7.2" orientation="orthogonal" renderorder="right-down" width="30" height="17" tilewidth="16" tileheight="16" infinite="0" nextlayerid="6" nextobjectid="48"> <map version="1.5" tiledversion="1.7.1" orientation="orthogonal" renderorder="right-down" width="30" height="17" tilewidth="16" tileheight="16" infinite="0" nextlayerid="6" nextobjectid="48">
<editorsettings> <editorsettings>
<export target="wastetown..tmx" format="tmx"/> <export target="wastetown..tmx" format="tmx"/>
</editorsettings> </editorsettings>
@@ -12,7 +12,7 @@
</layer> </layer>
<layer id="2" name="Ground" width="30" height="17"> <layer id="2" name="Ground" width="30" height="17">
<data encoding="base64" compression="zlib"> <data encoding="base64" compression="zlib">
eJxjYBgFowA/KOWljz2T2BgYJkOxETtCXB8HmxxQRsAvZ9kJs2kBzrEj/J7LgWDncUDkCbmbXEArf2FLM/o4whNX/FIa19jsIpVNCchBisfzOOIXW1xTE9A63SKDEl4ETU97kcGovcQBAKfZHlY= eJxjYBgFowA/KOWljz2T2BgYJkOxETtCXB8HmxxQRsAvZ9kJs2kBzrEj/J7LgWDncUDkCbmbXEArf2FLM/o4whMmjk5TCxCKU3SaWiAHKR7PY4lfGA2LY1oAWqdbZFDCi6DpaS8yGLWXOAAAywwdJA==
</data> </data>
</layer> </layer>
<layer id="3" name="Foreground" width="30" height="17"> <layer id="3" name="Foreground" width="30" height="17">
@@ -20,22 +20,22 @@
<property name="spriteLayer" type="bool" value="true"/> <property name="spriteLayer" type="bool" value="true"/>
</properties> </properties>
<data encoding="base64" compression="zlib"> <data encoding="base64" compression="zlib">
eJxjYKAfKORgYCgC4mIOOloKBPycDAwCQCzISV97KQVzBRgY5gnQ396dQDt3AbHeIAkvdyEGBg8hTDalIJeLPLlRQB7AFXe44peacY0MRuN2FKCDK+wMDFfZ6W/vQNSHIAAACK8NIg== eJxjYKAfKORgYCgC4mIOOloKBPycDAwCQCzISV97KQVzBRgY5gnQ396dQDt3AbHeIAkvdyEGBg8hTDalIJeLPLlRQB7AFXcwcXSaVmA0bkcBOrjCzsBwlZ3+9g5EfQgCABETDSI=
</data> </data>
</layer> </layer>
<layer id="5" name="AboveSprites" width="30" height="17"> <layer id="5" name="AboveSprites" width="30" height="17">
<data encoding="base64" compression="zlib"> <data encoding="base64" compression="zlib">
eJxjYBgFgxXMFWBgmCcw0K5AAG0hBgYdIUz2KBhaAFfc4Yrf0bgeBaNgeAEArncEPg== eJxjYBgFgxXMFWBgmCcw0K5AAG0hBgYdIUz2KBhaAFfcwcTR6VEwCkbB8AIAtDsEPg==
</data> </data>
</layer> </layer>
<objectgroup id="4" name="Objects"> <objectgroup id="4" name="Objects">
<object id="38" template="../obj/entry_up.tx" x="256" y="271"/> <object id="38" template="../obj/entry_up.tx" x="256" y="271"/>
<object id="41" template="../obj/shop.tx" x="304" y="98"/> <object id="41" template="../obj/shop.tx" x="304" y="98"/>
<object id="42" template="../obj/shop.tx" x="400" y="162"/> <object id="42" template="../obj/shop.tx" x="368" y="162"/>
<object id="43" template="../obj/shop.tx" x="353" y="98"/> <object id="43" template="../obj/shop.tx" x="353" y="98"/>
<object id="44" template="../obj/shop.tx" x="208" y="162"/> <object id="44" template="../obj/shop.tx" x="208" y="162"/>
<object id="45" template="../obj/shop.tx" x="304" y="162"/> <object id="45" template="../obj/shop.tx" x="304" y="162"/>
<object id="46" template="../obj/shop.tx" x="352" y="162"/> <object id="46" template="../obj/shop.tx" x="336" y="162"/>
<object id="47" template="../obj/inn.tx" x="215" y="82"/> <object id="47" template="../obj/inn.tx" x="215" y="82"/>
</objectgroup> </objectgroup>
</map> </map>

View File

@@ -23,6 +23,6 @@
"height": 0.7, "height": 0.7,
"color": "10a2e0", "color": "10a2e0",
"spriteNames":["IslandTree","Coral","Shell"] , "spriteNames":["IslandTree","Coral","Shell"] ,
"enemies":[ "Merfok","Merfok warrior","Merfok Avatar","Djinn","Blue Wiz1"] , "enemies":[ "Merfolk","Merfolk warrior","Merfolk Avatar","Djinn","Blue Wiz1"] ,
"pointsOfInterest":[ "Island Town"] "pointsOfInterest":[ "Island Town"]
} }

View File

@@ -872,7 +872,7 @@
{ {
"name": "Djinn", "name": "Djinn",
"sprite": "sprites/djinn.atlas", "sprite": "sprites/djinn.atlas",
"deck": "decks/Djinni.json", "deck": "decks/Djinn.json",
"spawnRate": 0.2, "spawnRate": 0.2,
"difficulty": 0.1, "difficulty": 0.1,
"speed": 30, "speed": 30,

View File

@@ -51,7 +51,7 @@
"maleAvatar":"Viashino_m" "maleAvatar":"Viashino_m"
} , } ,
{ {
"name":"Drawf", "name":"Dwarf",
"female":"sprites/heroes/dwarf_f.atlas", "female":"sprites/heroes/dwarf_f.atlas",
"male":"sprites/heroes/dwarf_m.atlas", "male":"sprites/heroes/dwarf_m.atlas",
"femaleAvatar":"Dwarf_f", "femaleAvatar":"Dwarf_f",

View File

@@ -3,7 +3,7 @@ ManaCost:3 U
Types:Enchantment Types:Enchantment
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigToken | TriggerDescription$ When CARDNAME enters the battlefield, create two 1/1 blue Merfolk creature tokens with hexproof. T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigToken | TriggerDescription$ When CARDNAME enters the battlefield, create two 1/1 blue Merfolk creature tokens with hexproof.
SVar:TrigToken:DB$ Token | TokenAmount$ 2 | LegacyImage$ u 1 1 merfolk hexproof rix | TokenScript$ u_1_1_merfolk_hexproof | TokenOwner$ You SVar:TrigToken:DB$ Token | TokenAmount$ 2 | LegacyImage$ u 1 1 merfolk hexproof rix | TokenScript$ u_1_1_merfolk_hexproof | TokenOwner$ You
A:AB$ Pump | Cost$ 3 U | ValidTgts$ Merfolk | TgtPrompt$ Select target Merfolk | KW$ HIDDEN Unblockable | SpellDescription$ Target Merfolk can't be blocked this turn. A:AB$ Pump | Cost$ 3 U | ValidTgts$ Merfolk | TgtPrompt$ Select target Merfolk | KW$ HIDDEN Unblockable | SpellDescription$ Target Merfolk can't be blocked this turn.
DeckHints:Type$Merfolk DeckHints:Type$Merfolk
SVar:Picture:http://www.wizards.com/global/images/magic/general/aquatic_incursion.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/aquatic_incursion.jpg
Oracle:When Aquatic Incursion enters the battlefield, create two 1/1 blue Merfolk creature tokens with hexproof. (They can't be the targets of spells or abilities your opponents control.)\n{3}{U}: Target Merfolk can't be blocked this turn. Oracle:When Aquatic Incursion enters the battlefield, create two 1/1 blue Merfolk creature tokens with hexproof. (They can't be the targets of spells or abilities your opponents control.)\n{3}{U}: Target Merfolk can't be blocked this turn.

View File

@@ -2,7 +2,7 @@ Name:Awe for the Guilds
ManaCost:2 R ManaCost:2 R
Types:Sorcery Types:Sorcery
A:SP$ Effect | Cost$ 2 R | Name$ Awe for the Guilds Effect | StaticAbilities$ KWPump | AILogic$ Evasion | SpellDescription$ Monocolored creatures can't block this turn. A:SP$ Effect | Cost$ 2 R | Name$ Awe for the Guilds Effect | StaticAbilities$ KWPump | AILogic$ Evasion | SpellDescription$ Monocolored creatures can't block this turn.
SVar:KWPump:Mode$ Continuous | EffectZone$ Command | AffectedZone$ Battlefield | Affected$ Creature.MonoColor| AddHiddenKeyword$ CARDNAME can't block. | Description$ Monocolored creatures can't block this turn. SVar:KWPump:Mode$ Continuous | EffectZone$ Command | AffectedZone$ Battlefield | Affected$ Creature.MonoColor | AddHiddenKeyword$ CARDNAME can't block. | Description$ Monocolored creatures can't block this turn.
SVar:PlayMain1:TRUE SVar:PlayMain1:TRUE
AI:RemoveDeck:All AI:RemoveDeck:All
SVar:Picture:http://www.wizards.com/global/images/magic/general/awe_for_the_guilds.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/awe_for_the_guilds.jpg

View File

@@ -4,6 +4,6 @@ Types:Creature Cat Cleric
PT:2/3 PT:2/3
K:Lifelink K:Lifelink
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigPutCounters | TriggerDescription$ When CARDNAME enters the battlefield, put a +1/+1 counter on each of up to two other target creatures you control. T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigPutCounters | TriggerDescription$ When CARDNAME enters the battlefield, put a +1/+1 counter on each of up to two other target creatures you control.
SVar:TrigPutCounters:DB$ PutCounter | CounterType$ P1P1 | CounterNum$ 1 | TargetMin$ 0 | TargetMax$ 2 | ValidTgts$ Creature.YouCtrl+Other | TgtPrompt$ Select target creature you control SVar:TrigPutCounters:DB$ PutCounter | CounterType$ P1P1 | CounterNum$ 1 | TargetMin$ 0 | TargetMax$ 2 | ValidTgts$ Creature.YouCtrl+Other | TgtPrompt$ Select up to two other target creatures you control
DeckHas:Ability$LifeGain & Ability$Counters DeckHas:Ability$LifeGain & Ability$Counters
Oracle:Lifelink (Damage dealt by this creature also causes you to gain that much life.)\nWhen Basri's Acolyte enters the battlefield, put a +1/+1 counter on each of up to two other target creatures you control. Oracle:Lifelink (Damage dealt by this creature also causes you to gain that much life.)\nWhen Basri's Acolyte enters the battlefield, put a +1/+1 counter on each of up to two other target creatures you control.

View File

@@ -1,8 +1,7 @@
Name:Demonic Tutor Name:Demonic Tutor
ManaCost:1 B ManaCost:1 B
Types:Sorcery Types:Sorcery
A:SP$ ChangeZone | Cost$ 1 B | Origin$ Library | Destination$ Hand | ChangeType$ Card | ChangeNum$ 1 | Mandatory$ True | SpellDescription$ Search your library for a card, put that card into your hand, then shuffle. A:SP$ ChangeZone | Origin$ Library | Destination$ Hand | ChangeType$ Card | ChangeNum$ 1 | Mandatory$ True | StackDescription$ SpellDescription | SpellDescription$ Search your library for a card, put that card into your hand, then shuffle.
#TODO: Improve the tutoring logic for the AI. Currently will generally look for the most expensive castable thing in the library (which can, of course, be used to advantage in properly constructed AI decks). #TODO: Improve the tutoring logic for the AI. Currently will generally look for the most expensive castable thing in the library (which can, of course, be used to advantage in properly constructed AI decks).
AI:RemoveDeck:Random AI:RemoveDeck:Random
SVar:Picture:http://resources.wizards.com/magic/cards/3e/en-us/card1155.jpg
Oracle:Search your library for a card, put that card into your hand, then shuffle. Oracle:Search your library for a card, put that card into your hand, then shuffle.

View File

@@ -3,7 +3,7 @@ ManaCost:G
Types:Creature Human Druid Types:Creature Human Druid
PT:1/1 PT:1/1
A:AB$ ChangeZone | Cost$ 1 G Sac<1/CARDNAME> | Origin$ Library | Destination$ Battlefield | Tapped$ True | ChangeType$ Land.Basic | ChangeNum$ 1 | SpellDescription$ Search your library for a basic land card, put it onto the battlefield tapped, then shuffle. A:AB$ ChangeZone | Cost$ 1 G Sac<1/CARDNAME> | Origin$ Library | Destination$ Battlefield | Tapped$ True | ChangeType$ Land.Basic | ChangeNum$ 1 | SpellDescription$ Search your library for a basic land card, put it onto the battlefield tapped, then shuffle.
S:Mode$ Continuous | Affected$ Card.Self | EffectZone$ Graveyard | AddHiddenKeyword$ CARDNAME count as Muscle Burst. | Description$ If CARDNAME is in a graveyard, effects from spells named Muscle Burst count it as a card named Muscle Burst. S:Mode$ Continuous | Affected$ Card.Self | EffectZone$ Graveyard | AffectedZone$ Graveyard | AddHiddenKeyword$ CARDNAME count as Muscle Burst. | Description$ If CARDNAME is in a graveyard, effects from spells named Muscle Burst count it as a card named Muscle Burst.
DeckHints:Name$Diligent Farmhand|Muscle Burst DeckHints:Name$Diligent Farmhand|Muscle Burst
SVar:Picture:http://www.wizards.com/global/images/magic/general/diligent_farmhand.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/diligent_farmhand.jpg
Oracle:{1}{G}, Sacrifice Diligent Farmhand: Search your library for a basic land card, put that card onto the battlefield tapped, then shuffle.\nIf Diligent Farmhand is in a graveyard, effects from spells named Muscle Burst count it as a card named Muscle Burst. Oracle:{1}{G}, Sacrifice Diligent Farmhand: Search your library for a basic land card, put that card onto the battlefield tapped, then shuffle.\nIf Diligent Farmhand is in a graveyard, effects from spells named Muscle Burst count it as a card named Muscle Burst.

View File

@@ -2,8 +2,7 @@ Name:Followed Footsteps
ManaCost:3 U U ManaCost:3 U U
Types:Enchantment Aura Types:Enchantment Aura
K:Enchant creature K:Enchant creature
A:SP$ Attach | Cost$ 3 U U | ValidTgts$ Creature | AILogic$ Curse A:SP$ Attach | ValidTgts$ Creature | AILogic$ HighestEvaluation
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | Execute$ TrigCopy | TriggerZones$ Battlefield | TriggerDescription$ At the beginning of your upkeep, create a token that's a copy of enchanted creature. T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | Execute$ TrigCopy | TriggerZones$ Battlefield | TriggerDescription$ At the beginning of your upkeep, create a token that's a copy of enchanted creature.
SVar:TrigCopy:DB$ CopyPermanent | Defined$ Enchanted | SpellDescription$ At the beginning of your upkeep, put a token that's a copy of enchanted creature onto the battlefield. SVar:TrigCopy:DB$ CopyPermanent | Defined$ Enchanted | SpellDescription$ At the beginning of your upkeep, create a token that's a copy of enchanted creature.
SVar:Picture:http://www.wizards.com/global/images/magic/general/followed_footsteps.jpg
Oracle:Enchant creature\nAt the beginning of your upkeep, create a token that's a copy of enchanted creature. Oracle:Enchant creature\nAt the beginning of your upkeep, create a token that's a copy of enchanted creature.

View File

@@ -5,7 +5,7 @@ PT:2/1
S:Mode$ Continuous | Affected$ Dwarf.Other+YouCtrl | AddPower$ 1 | Description$ Other Dwarves you control get +1/+0. S:Mode$ Continuous | Affected$ Dwarf.Other+YouCtrl | AddPower$ 1 | Description$ Other Dwarves you control get +1/+0.
T:Mode$ Taps | ValidCard$ Dwarf.YouCtrl | Execute$ TrigToken | TriggerZones$ Battlefield | TriggerDescription$ Whenever a Dwarf you control becomes tapped, create a Treasure token. T:Mode$ Taps | ValidCard$ Dwarf.YouCtrl | Execute$ TrigToken | TriggerZones$ Battlefield | TriggerDescription$ Whenever a Dwarf you control becomes tapped, create a Treasure token.
SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ c_a_treasure_sac | TokenOwner$ You SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ c_a_treasure_sac | TokenOwner$ You
A:AB$ ChangeZone | Cost$ Sac<5/Treasure> | CostDesc$ Sacrifice five Treasures: | Origin$ Library | Destination$ Battlefield | ChangeType$ Card.Artifact,Card.Dragon | ChangeNum$ 1 | Mandatory$ True | StackDescription$ {p:You} searches their library for an Artifact or Dragon card, puts that card onto the battlefield, then shuffles their library. | SpellDescription$ Search your library for an artifact or Dragon card, put that card onto the battlefield, then shuffle. A:AB$ ChangeZone | Cost$ Sac<5/Treasure> | Origin$ Library | Destination$ Battlefield | ChangeType$ Card.Artifact,Card.Dragon | ChangeNum$ 1 | Mandatory$ True | StackDescription$ {p:You} searches their library for an Artifact or Dragon card, puts that card onto the battlefield, then shuffles their library. | SpellDescription$ Search your library for an artifact or Dragon card, put that card onto the battlefield, then shuffle.
SVar:BuffedBy:Dwarf SVar:BuffedBy:Dwarf
SVar:PlayMain1:TRUE SVar:PlayMain1:TRUE
DeckNeeds:Type$Dwarf DeckNeeds:Type$Dwarf

View File

@@ -2,9 +2,7 @@ Name:Nahiri, the Harbinger
ManaCost:2 R W ManaCost:2 R W
Types:Legendary Planeswalker Nahiri Types:Legendary Planeswalker Nahiri
Loyalty:4 Loyalty:4
A:AB$ Discard | Cost$ AddCounter<2/LOYALTY> | Planeswalker$ True | NumCards$ 1 | Optional$ True | Mode$ TgtChoose | RememberDiscarded$ True | SubAbility$ DBDraw | SpellDescription$ You may discard a card. If you do, draw a card. A:AB$ Draw | Cost$ AddCounter<2/LOYALTY> | Planeswalker$ True | Defined$ You | NumCards$ 1 | UnlessCost$ Discard<1/Card> | UnlessSwitched$ True | UnlessPayer$ You | SpellDescription$ You may discard a card. If you do, draw a card.
SVar:DBDraw:DB$ Draw | Defined$ You | NumCards$ 1 | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ GE1 | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
A:AB$ ChangeZone | Cost$ SubCounter<2/LOYALTY> | Planeswalker$ True | ValidTgts$ Enchantment,Artifact.tapped,Creature.tapped | TgtPrompt$ Select target enchantment, tapped artifact, or tapped creature | Origin$ Battlefield | Destination$ Exile | SpellDescription$ Exile target enchantment, tapped artifact, or tapped creature. A:AB$ ChangeZone | Cost$ SubCounter<2/LOYALTY> | Planeswalker$ True | ValidTgts$ Enchantment,Artifact.tapped,Creature.tapped | TgtPrompt$ Select target enchantment, tapped artifact, or tapped creature | Origin$ Battlefield | Destination$ Exile | SpellDescription$ Exile target enchantment, tapped artifact, or tapped creature.
A:AB$ ChangeZone | Cost$ SubCounter<8/LOYALTY> | Planeswalker$ True | Ultimate$ True | Origin$ Library | Destination$ Battlefield | ChangeType$ Artifact,Creature | ChangeNum$ 1 | RememberChanged$ True | SubAbility$ DBPump | SpellDescription$ Search your library for an artifact or creature card, put it onto the battlefield, then shuffle. It gains haste. Return it to your hand at the beginning of the next end step. A:AB$ ChangeZone | Cost$ SubCounter<8/LOYALTY> | Planeswalker$ True | Ultimate$ True | Origin$ Library | Destination$ Battlefield | ChangeType$ Artifact,Creature | ChangeNum$ 1 | RememberChanged$ True | SubAbility$ DBPump | SpellDescription$ Search your library for an artifact or creature card, put it onto the battlefield, then shuffle. It gains haste. Return it to your hand at the beginning of the next end step.
SVar:DBPump:DB$ Animate | Keywords$ Haste | Duration$ Permanent | AtEOT$ Hand | Defined$ Remembered | SubAbility$ DBCleanup SVar:DBPump:DB$ Animate | Keywords$ Haste | Duration$ Permanent | AtEOT$ Hand | Defined$ Remembered | SubAbility$ DBCleanup

View File

@@ -3,11 +3,9 @@ ManaCost:1 B R
Types:Legendary Creature Vampire Knight Types:Legendary Creature Vampire Knight
PT:3/3 PT:3/3
K:Flying K:Flying
T:Mode$ ChangesZone | ValidCard$ Creature.Other+YouCtrl | Origin$ Any | Destination$ Battlefield | Execute$ TrigDiscard | TriggerZones$ Battlefield | OptionalDecider$ You | TriggerDescription$ Whenever another creature enters the battlefield under your control, you may discard a card. If you do, put a +1/+1 counter on that creature, it gains haste until end of turn, and it becomes a vampire in addition to its other types. T:Mode$ ChangesZone | ValidCard$ Creature.Other+YouCtrl | Origin$ Any | Destination$ Battlefield | Execute$ TrigDiscard | TriggerZones$ Battlefield | TriggerDescription$ Whenever another creature enters the battlefield under your control, you may discard a card. If you do, put a +1/+1 counter on that creature, it gains haste until end of turn, and it becomes a vampire in addition to its other types.
SVar:TrigDiscard:DB$ Discard | Defined$ You | NumCards$ 1 | Mode$ TgtChoose | RememberDiscarded$ True | SubAbility$ DBPutCounter SVar:TrigDiscard:AB$ PutCounter | Cost$ Discard<1/Card> | Defined$ TriggeredCardLKICopy | CounterType$ P1P1 | CounterNum$ 1 | SubAbility$ DBPump
SVar:DBPutCounter:DB$ PutCounter | Defined$ TriggeredCardLKICopy | CounterType$ P1P1 | CounterNum$ 1 | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ GE1 | SubAbility$ DBPump SVar:DBPump:DB$ Pump | Defined$ TriggeredCard | KW$ Haste | SubAbility$ DBAnimate
SVar:DBPump:DB$ Pump | Defined$ TriggeredCard | KW$ Haste | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ GE1 | SubAbility$ DBAnimate SVar:DBAnimate:DB$ Animate | Defined$ TriggeredCard | Types$ Vampire | Duration$ Permanent
SVar:DBAnimate:DB$ Animate | Defined$ TriggeredCard | Types$ Vampire | Duration$ Permanent | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ GE1 | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:Picture:http://www.wizards.com/global/images/magic/general/olivia_mobilized_for_war.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/olivia_mobilized_for_war.jpg
Oracle:Flying\nWhenever another creature enters the battlefield under your control, you may discard a card. If you do, put a +1/+1 counter on that creature, it gains haste until end of turn, and it becomes a Vampire in addition to its other types. Oracle:Flying\nWhenever another creature enters the battlefield under your control, you may discard a card. If you do, put a +1/+1 counter on that creature, it gains haste until end of turn, and it becomes a Vampire in addition to its other types.

View File

@@ -3,7 +3,7 @@ ManaCost:3 R
Types:Creature Elemental Cat Types:Creature Elemental Cat
PT:2/3 PT:2/3
K:Haste K:Haste
S:Mode$ Continuous | Affected$ Card.Self | EffectZone$ Graveyard | AddHiddenKeyword$ CARDNAME count as Flame Burst. | Description$ If CARDNAME is in a graveyard, effects from spells named Flame Burst count it as a card named Flame Burst. S:Mode$ Continuous | Affected$ Card.Self | EffectZone$ Graveyard | AffectedZone$ Graveyard | AddHiddenKeyword$ CARDNAME count as Flame Burst. | Description$ If CARDNAME is in a graveyard, effects from spells named Flame Burst count it as a card named Flame Burst.
DeckHints:Name$Flame Burst|Pardic Firecat DeckHints:Name$Flame Burst|Pardic Firecat
SVar:Picture:http://www.wizards.com/global/images/magic/general/pardic_firecat.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/pardic_firecat.jpg
Oracle:Haste\nIf Pardic Firecat is in a graveyard, effects from spells named Flame Burst count it as a card named Flame Burst. Oracle:Haste\nIf Pardic Firecat is in a graveyard, effects from spells named Flame Burst count it as a card named Flame Burst.

View File

@@ -5,7 +5,7 @@ PT:3/4
K:Flash K:Flash
K:Flying K:Flying
T:Mode$ ChangesZone | ValidCard$ Creature.Self | Origin$ Any | Destination$ Battlefield | Execute$ RestorationExile | OptionalDecider$ You | TriggerDescription$ When CARDNAME enters the battlefield, you may exile target non-Angel creature you control, then return that creature to the battlefield under your control. T:Mode$ ChangesZone | ValidCard$ Creature.Self | Origin$ Any | Destination$ Battlefield | Execute$ RestorationExile | OptionalDecider$ You | TriggerDescription$ When CARDNAME enters the battlefield, you may exile target non-Angel creature you control, then return that creature to the battlefield under your control.
SVar:RestorationExile:DB$ ChangeZone | ValidTgts$ Creature.nonAngel+YouCtrl | TgtPrompt$ Select target non-Angel creature you control | Origin$ Battlefield | Destination$ Exile | RememberTargets$ True | ForgetOtherTargets$ True | AILogic$ BounceOnce | SubAbility$ RestorationReturn SVar:RestorationExile:DB$ ChangeZone | ValidTgts$ Creature.nonAngel+YouCtrl | TgtPrompt$ Select target non-Angel creature you control | Origin$ Battlefield | Destination$ Exile | RememberTargets$ True | ForgetOtherTargets$ True | SubAbility$ RestorationReturn
SVar:RestorationReturn:DB$ ChangeZone | Defined$ Remembered | Origin$ Exile | Destination$ Battlefield | GainControl$ True SVar:RestorationReturn:DB$ ChangeZone | Defined$ Remembered | Origin$ Exile | Destination$ Battlefield | GainControl$ True
SVar:Picture:http://www.wizards.com/global/images/magic/general/restoration_angel.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/restoration_angel.jpg
Oracle:Flash\nFlying\nWhen Restoration Angel enters the battlefield, you may exile target non-Angel creature you control, then return that card to the battlefield under your control. Oracle:Flash\nFlying\nWhen Restoration Angel enters the battlefield, you may exile target non-Angel creature you control, then return that card to the battlefield under your control.

View File

@@ -19,7 +19,7 @@ Colors:green
Types:Creature Werewolf Types:Creature Werewolf
PT:2/1 PT:2/1
K:Unblockable K:Unblockable
T:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Player | CombatDamage$ True | Execute$ TrigDrawN | TriggerZones$ Battlefield | TriggerDescription$ Whenever CARDNAME deals combat damage to a player, draw a card, then discard a card. T:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Player | CombatDamage$ True | Execute$ TrigDrawN | TriggerZones$ Battlefield | TriggerDescription$ Whenever CARDNAME deals combat damage to a player, draw a card.
SVar:TrigDrawN:DB$ Draw | NumCards$ 1 | Defined$ You SVar:TrigDrawN:DB$ Draw | NumCards$ 1 | Defined$ You
K:Nightbound K:Nightbound
Oracle:Seafaring Werewolf can't be blocked.\nWhenever Seafaring Werewolf deals combat damage to a player, draw a card.\nNightbound (If a player casts at least two spells during their own turn, it becomes day next turn.) Oracle:Seafaring Werewolf can't be blocked.\nWhenever Seafaring Werewolf deals combat damage to a player, draw a card.\nNightbound (If a player casts at least two spells during their own turn, it becomes day next turn.)

View File

@@ -0,0 +1,22 @@
Name:Alluring Suitor
ManaCost:2 R
Types:Creature Vampire
PT:2/3
T:Mode$ AttackersDeclared | Execute$ TrigTransform | IsPresent$ Creature.attacking | PresentCompare$ EQ2 | NoResolvingCheck$ True | TriggerZones$ Battlefield | AttackingPlayer$ You | TriggerDescription$ When you attack with exactly two creatures, transform CARDNAME.
SVar:TrigTransform:DB$ SetState | Defined$ Self | Mode$ Transform
AlternateMode:DoubleFaced
Oracle:When you attack with exactly two creatures, transform Alluring Suitor.
ALTERNATE
Name:Deadly Dancer
ManaCost:no cost
Colors:red
Types:Creature Vampire
PT:3/3
K:Trample
T:Mode$ Transformed | ValidCard$ Card.Self | Execute$ TrigMana | TriggerDescription$ When this creature transforms into CARDNAME, add {R}{R}. Until end of turn, you don't lose this mana as steps and phases end.
SVar:TrigMana:DB$ Mana | Produced$ R | Amount$ 2 | PersistentMana$ True
A:AB$ Pump | Cost$ R R | Defined$ Self | NumAtt$ +1 | SubAbility$ DBPump | StackDescription$ CARDNAME and | SpellDescription$ CARDNAME and another target creature each get +1/+0 until end of turn.
SVar:DBPump:DB$ Pump | ValidTgts$ Creature.Other | TgtPrompt$ Select another target creature | NumAtt$ +1 | StackDescription$ {c:Targeted} each get +1/+0 until end of turn.
Oracle:Trample\nWhen this creature transforms into Deadly Dancer, add {R}{R}. Until end of turn, you don't lose this mana as steps and phases end.\n{R}{R}: Deadly Dancer and another target creature each get +1/+0 until end of turn.

View File

@@ -0,0 +1,6 @@
Name:Ancient Lumberknot
ManaCost:2 B G
Types:Creature Treefolk
PT:1/4
S:Mode$ Continuous | Affected$ Creature.powerLTtoughness+YouCtrl | AddHiddenKeyword$ CARDNAME assigns combat damage equal to its toughness rather than its power | Description$ Each creature you control with toughness greater than its power assigns combat damage equal to its toughness rather than its power.
Oracle:Each creature you control with toughness greater than its power assigns combat damage equal to its toughness rather than its power.

View File

@@ -0,0 +1,9 @@
Name:Angelic Quartermaster
ManaCost:3 W W
Types:Creature Angel Soldier
PT:3/3
K:Flying
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigPutCounters | TriggerDescription$ When CARDNAME enters the battlefield, put a +1/+1 counter on each of up to two other target creatures.
SVar:TrigPutCounters:DB$ PutCounter | CounterType$ P1P1 | CounterNum$ 1 | TargetMin$ 0 | TargetMax$ 2 | ValidTgts$ Creature.Other | TgtPrompt$ Select up to two other target creatures
DeckHas:Ability$Counters
Oracle:Flying\nWhen Angelic Quartermaster enters the battlefield, put a +1/+1 counter on each of up to two other target creatures.

View File

@@ -0,0 +1,8 @@
Name:Arm the Cathars
ManaCost:1 W W
Types:Sorcery
A:SP$ Pump | ValidTgts$ Creature | TgtPrompt$ Select target creature (+3/+3) | NumAtt$ 3 | NumDef$ 3 | KW$ Vigilance | SubAbility$ DBPump | StackDescription$ Until end of turn, {c:ThisTargetedCard} gets +3/+3, | SpellDescription$ Until end of turn, target creature gets +3/+3,
SVar:DBPump:DB$ Pump | ValidTgts$ Creature | TgtPrompt$ Select up to one other target creature (+2/+2) | TargetMin$ 0 | TargetMax$ 1 | NumAtt$ 2 | NumDef$ 2 | KW$ Vigilance | TargetUnique$ True | SubAbility$ DBPump2 | StackDescription$ SpellDescription | SpellDescription$ up to one other target creature gets +2/+2,
SVar:DBPump2:DB$ Pump | ValidTgts$ Creature | TgtPrompt$ Select up to one other target creature (+1/+1) | TargetMin$ 0 | TargetMax$ 1 | NumAtt$ 1 | NumDef$ 1 | KW$ Vigilance | TargetUnique$ True | SubAbility$ DBPump3 | StackDescription$ SpellDescription | SpellDescription$ and up to one other target creature gets +1/1.
SVar:DBPump3:DB$ Pump | Defined$ Targeted | KW$ Vigilance | StackDescription$ SpellDescription | SpellDescription$ Those creatures gain vigilance until end of turn.
Oracle:Until end of turn, target creature gets +3/+3, up to one other target creature gets +2/+2, and up to one other target creature gets +1/+1. Those creatures gain vigilance until end of turn.

View File

@@ -0,0 +1,10 @@
Name:Ascendant Packleader
ManaCost:G
Types:Creature Wolf
PT:2/1
K:etbCounter:P1P1:1:IsPresent$ Permanent.YouCtrl+cmcGE4:CARDNAME enters the battlefield with a +1/+1 counter on it if you control a permanent with mana value 4 or greater.
T:Mode$ SpellCast | ValidCard$ Card.cmcGE4 | ValidActivatingPlayer$ You | Execute$ TrigCounter | TriggerZones$ Battlefield | TriggerDescription$ Whenever you cast a spell with mana value 4 or greater, put a +1/+1 counter on CARDNAME.
SVar:TrigCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1
DeckHas:Ability$Counters
SVar:BuffedBy:Permanent.cmcGE4
Oracle:Ascendant Packleader enters the battlefield with a +1/+1 counter on it if you control a permanent with mana value 4 or greater.\nWhenever you cast a spell with mana value 4 or greater, put a +1/+1 counter on Ascendant Packleader.

View File

@@ -0,0 +1,9 @@
Name:Belligerent Guest
ManaCost:2 R
Types:Creature Vampire
PT:3/2
K:Trample
T:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Player | CombatDamage$ True | Execute$ TrigToken | TriggerDescription$ Whenever CARDNAME deals combat damage to a player, create a Blood token. (It's an artifact with "{1}, {T}, Discard a card, Sacrifice this artifact: Draw a card.")
SVar:TrigToken:DB$ Token | TokenScript$ c_a_blood_draw
DeckHas:Ability$Token & Ability$Sacrifice & Type$Blood
Oracle:Trample\nWhenever Belligerent Guest deals combat damage to a player, create a Blood token. (It's an artifact with "{1}, {T}, Discard a card, Sacrifice this artifact: Draw a card.")

View File

@@ -0,0 +1,9 @@
Name:Blood Hypnotist
ManaCost:2 R
Types:Creature Vampire
PT:3/3
K:CARDNAME can't block.
T:Mode$ Sacrificed | ValidCard$ Blood.token+YouCtrl | TriggerZone$ Battlefield | Execute$ TrigPump | ActivationLimit$ 1 | TriggerDescription$ Whenever you sacrifice one or more Blood tokens, target creature can't block this turn. This ability triggers only once each turn.
SVar:TrigPump:DB$ Pump | ValidTgts$ Creature | TgtPrompt$ Select target creature | KW$ HIDDEN CARDNAME can't block. | IsCurse$ True
DeckNeeds:Type$Blood
Oracle:Blood Hypnotist can't block.\nWhenever you sacrifice one or more Blood tokens, target creature can't block this turn. This ability triggers only once each turn.

View File

@@ -0,0 +1,28 @@
Name:Bloodsworn Squire
ManaCost:3 B
Types:Creature Vampire Soldier
PT:3/3
A:AB$ Pump | Cost$ 1 B Discard<1/Card> | Defined$ Self | KW$ Indestructible | SubAbility$ DBTap | SpellDescription$ CARDNAME gains indestructible until end of turn.
SVar:DBTap:DB$ Tap | Defined$ Self | SubAbility$ DBTransform | StackDescription$ SpellDescription | SpellDescription$ Tap it.
SVar:DBTransform:DB$ SetState | Defined$ Self | Mode$ Transform | ConditionPresent$ Creature.YouOwn | ConditionCompare$ GE4 | ConditionZone$ Graveyard | StackDescription$ SpellDescription | SpellDescription$ Then if there are four or more creature cards in your graveyard, transform CARDNAME.
AlternateMode:DoubleFaced
SVar:AIPreference:DiscardCost$Creature
DeckHints:Ability$Graveyard
DeckHas:Ability$Discard
Oracle:{1}{B}, Discard a card: Bloodsworn Squire gains indestructible until end of turn. Tap it. Then if there are four or more creature cards in your graveyard, transform Bloodsworn Squire. (Damage and effects that say "destroy" don't destroy it.)
ALTERNATE
Name:Bloodsworn Knight
ManaCost:no cost
Colors:black
Types:Creature Vampire Knight
PT:*/*
S:Mode$ Continuous | Affected$ Card.Self | CharacteristicDefining$ True | SetPower$ X | SetToughness$ X | Description$ CARDNAME's power and toughness are each equal to the number of creature cards in your graveyard.
SVar:X:Count$TypeInYourYard.Creature
A:AB$ Pump | Cost$ 1 B Discard<1/Card> | Defined$ Self | KW$ Indestructible | SubAbility$ DBTap | SpellDescription$ CARDNAME gains indestructible until end of turn.
SVar:DBTap:DB$ Tap | Defined$ Self | StackDescription$ SpellDescription | SpellDescription$ Tap it.
SVar:AIPreference:DiscardCost$Creature
DeckHints:Ability$Graveyard
DeckHas:Ability$Discard
Oracle:Bloodsworn Knight's power and toughness are each equal to the number of creature cards in your graveyard.\n{1}{B}, Discard a card: Bloodsworn Knight gains indestructible until end of turn. Tap it. (Damage and effects that say "destroy" don't destroy it.)

View File

@@ -0,0 +1,10 @@
Name:Bloodtithe Harvester
ManaCost:B R
Types:Creature Vampire
PT:3/2
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigToken | TriggerDescription$ When CARDNAME enters the battlefield, create a Blood token. (It's an artifact with "{1}, {T}, Discard a card, Sacrifice this artifact: Draw a card.")
SVar:TrigToken:DB$ Token | TokenScript$ c_a_blood_draw
A:AB$ Pump | Cost$ T Sac<1/CARDNAME> | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumAtt$ -X | NumDef$ -X | SorcerySpeed$ True | AILogic$ Curse | SpellDescription$ Target creature gets -X/-X until end of turn, where X is twice the number of Blood tokens you control. Activate only as a sorcery.
SVar:X:Count$Valid Blood.token+YouCtrl/Twice
DeckHas:Ability$Token & Ability$Sacrifice & Type$Blood
Oracle:When Bloodtithe Harvester enters the battlefield, create a Blood token. (It's an artifact with "{1}, {T}, Discard a card, Sacrifice this artifact: Draw a card.")\n{T}, Sacrifice Bloodtithe Harvester: Target creature gets -X/-X until end of turn, where X is twice the number of Blood tokens you control. Activate only as a sorcery.

View File

@@ -0,0 +1,13 @@
Name:Bloodvial Purveyor
ManaCost:2 B B
Types:Creature Vampire
PT:5/6
K:Flying
K:Trample
T:Mode$ SpellCast | ValidActivatingPlayer$ Opponent | TriggerZones$ Battlefield | Execute$ TrigToken | TriggerDescription$ Whenever an opponent casts a spell, that player creates a Blood token. (It's an artifact with "{1}, {T}, Discard a card, Sacrifice this artifact: Draw a card.")
SVar:TrigToken:DB$ Token | TokenScript$ c_a_blood_draw | TokenOwner$ TriggeredActivator
T:Mode$ Attacks | ValidCard$ Card.Self | IsPresent$ Blood.token+DefendingPlayerCtrl | Execute$ TrigPump | TriggerDescription$ Whenever CARDNAME attacks, it gets +1/+0 until end of turn for each Blood token defending player controls.
SVar:TrigPump:DB$ Pump | Defined$ Self | NumAtt$ X
SVar:X:Count$Valid Blood.token+DefendingPlayerCtrl
SVar:HasAttackEffect:TRUE
Oracle:Flying, trample\nWhenever an opponent casts a spell, that player creates a Blood token. (It's an artifact with "{1}, {T}, Discard a card, Sacrifice this artifact: Draw a card.")\nWhenever Bloodvial Purveyor attacks, it gets +1/+0 until end of turn for each Blood token defending player controls.

View File

@@ -0,0 +1,8 @@
Name:Boarded Window
ManaCost:3
Types:Artifact
S:Mode$ Continuous | Affected$ Creature.attackingYou | AddPower$ -1 | Description$ Creatures attacking you get -1/-0.
T:Mode$ Phase | Phase$ End of Turn | TriggerZones$ Battlefield | CheckSVar$ X | SVarCompare$ GE4 | Execute$ TrigExile | TriggerDescription$ At the beginning of each end step, if you were dealt 4 or more damage this turn, exile CARDNAME.
SVar:TrigExile:DB$ ChangeZone | Defined$ Self | Origin$ Battlefield | Destination$ Exile
SVar:X:Count$LifeYouLostThisTurn
Oracle:Creatures attacking you get -1/-0.\nAt the beginning of each end step, if you were dealt 4 or more damage this turn, exile Boarded Window.

View File

@@ -0,0 +1,11 @@
Name:Bramble Wurm
ManaCost:6 G
Types:Creature Wurm
PT:7/6
K:Reach
K:Trample
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigGainLife | TriggerDescription$ When CARDNAME enters the battlefield, you gain 5 life.
SVar:TrigGainLife:DB$ GainLife | LifeAmount$ 5
A:AB$ GainLife | Cost$ 2 G ExileFromGrave<1/CARDNAME> | ActivationZone$ Graveyard | LifeAmount$ 5 | SpellDescription$ You gain 5 life.
DeckHas:Ability$LifeGain
Oracle:Reach, trample\nWhen Bramble Wurm enters the battlefield, you gain 5 life.\n{2}{G}, Exile Bramble Wurm from your graveyard: You gain 5 life.

View File

@@ -0,0 +1,11 @@
Name:Breathkeeper Seraph
ManaCost:4 W W
Types:Creature Angel
PT:4/4
K:Flying
K:Soulbond
S:Mode$ Continuous | Affected$ Creature.PairedWith,Creature.Self+Paired | AddTrigger$ DeathTrigger | AddSVar$ DelayedReturn,TrigReturn | Description$ As long as CARDNAME is paired with another creature, each of those creatures have "When this creature dies, you may return it to the battlefield under its owner's control at the beginning of your next upkeep."
SVar:DeathTrigger:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ DelayedReturn | TriggerDescription$ When this creature dies, you may return it to the battlefield under its owner's control at the beginning of your next upkeep.
SVar:DelayedReturn:DB$ DelayedTrigger | Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | OptionalDecider$ You | Execute$ TrigReturn | RememberObjects$ TriggeredNewCardLKICopy | TriggerDescription$ You may return it to the battlefield under its owner's control at the beginning of your next upkeep.
SVar:TrigReturn:DB$ ChangeZone | Defined$ DelayTriggerRememberedLKI | Origin$ Graveyard | Destination$ Battlefield
Oracle:Flying, soulbond (You may pair this creature with another unpaired creature when either enters the battlefield. They remain paired for as long as you control both of them.)\nAs long as Breathkeeper Seraph is paired with another creature, each of those creatures has "When this creature dies, you may return it to the battlefield under its owner's control at the beginning of your next upkeep."

View File

@@ -0,0 +1,32 @@
Name:Brine Comber
ManaCost:1 W U
Types:Creature Spirit
PT:1/1
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigToken | TriggerDescription$ When CARDNAME enters the battlefield or becomes the target of an Aura spell, create a 1/1 white Spirit creature token with flying.
T:Mode$ BecomesTarget | ValidTarget$ Card.Self | ValidSource$ Aura | SourceType$ Spell | TriggerZones$ Battlefield | Execute$ TrigToken | Secondary$ True | TriggerDescription$ When CARDNAME enters the battlefield or becomes the target of an Aura spell, create a 1/1 white Spirit creature token with flying.
SVar:TrigToken:DB$ Token | TokenScript$ w_1_1_spirit_flying
K:Disturb:W U
AlternateMode:DoubleFaced
SVar:EnchantMe:Multiple
DeckHas:Ability$Graveyard & Ability$Token
DeckHints:Type$Enchantment
Oracle:Whenever Brine Comber enters the battlefield or becomes the target of an Aura spell, create a 1/1 white Spirit creature token with flying.\nDisturb {W}{U} (You may cast this card from your graveyard transformed for its disturb cost.)
ALTERNATE
Name:Brinebound Gift
ManaCost:no cost
Colors:white,blue
Types:Enchantment Aura
K:Enchant creature
A:SP$ Attach | ValidTgts$ Creature | TgtPrompt$ Select target creature | AILogic$ Pump
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigToken | TriggerDescription$ When CARDNAME enters the battlefield or enchanted creature becomes the target of an Aura spell, create a 1/1 white Spirit creature token with flying.
T:Mode$ BecomesTarget | ValidTarget$ Creature.EnchantedBy | ValidSource$ Aura | SourceType$ Spell | TriggerZones$ Battlefield | Execute$ TrigToken | Secondary$ True | TriggerDescription$ When CARDNAME enters the battlefield or enchanted creature becomes the target of an Aura spell, create a 1/1 white Spirit creature token with flying.
SVar:TrigToken:DB$ Token | TokenScript$ w_1_1_spirit_flying
R:Event$ Moved | ValidCard$ Card.Self | Destination$ Graveyard | ReplaceWith$ Exile | Description$ If CARDNAME would be put into a graveyard from anywhere, exile it instead.
SVar:Exile:DB$ ChangeZone | Hidden$ True | Origin$ All | Destination$ Exile | Defined$ ReplacedCard
S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddSVar$ EnchantTarget | Secondary$ True
SVar:EnchantTarget:SVar:EnchantMe:Multiple
DeckHas:Ability$Token
DeckHints:Type$Enchantment
Oracle:Enchant creature\nWhenever Brinebound Gift enters the battlefield or enchanted creature becomes the target of an Aura spell, create a 1/1 white Spirit creature token with flying.\nIf Brinebound Gift would be put into a graveyard from anywhere, exile it instead.

Some files were not shown because too many files have changed in this diff Show More