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() {
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;
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
* BiomeSpriteData
* 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 double startArea;
public double endArea;
@@ -18,4 +19,26 @@ public class BiomeSpriteData implements Serializable {
public String key() {
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())
FModel.getQuest().getCards().addSingleCard(card, 1);
for (Map.Entry<PaperCard, Integer> card : AdventurePlayer.current().getCards())
FModel.getQuest().getCards().addSingleCard(card.getKey(), card.getValue());
Deck deck = AdventurePlayer.current().getDeck();

View File

@@ -1,5 +1,6 @@
package forge.adventure.scene;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
import forge.adventure.AdventureApplicationAdapter;
import forge.adventure.util.Current;
@@ -37,5 +38,14 @@ public class InnScene extends UIScene {
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;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
@@ -103,6 +104,15 @@ public class RewardScene extends UIScene {
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)

View File

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

View File

@@ -1,6 +1,7 @@
package forge.adventure.scene;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
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() {
AdventureApplicationAdapter.instance.switchToLast();
return true;

View File

@@ -1,6 +1,7 @@
package forge.adventure.scene;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.scenes.scene2d.Actor;
import forge.adventure.AdventureApplicationAdapter;
import forge.adventure.world.WorldSave;
@@ -18,7 +19,6 @@ public class StartScene extends UIScene {
super("ui/start_menu.json");
}
public boolean NewGame() {
AdventureApplicationAdapter.instance.switchScene(SceneType.NewGameScene.instance);
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
public void resLoaded() {
super.resLoaded();

View File

@@ -47,9 +47,19 @@ public class UIScene extends Scene{
{
return ui;
}
public boolean keyPressed(int keycode)
{
return true;
}
@Override
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));
screenImage=ui.findActor("lastScreen");
stage.addActor(ui);
@@ -75,6 +85,8 @@ public class UIScene extends Scene{
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());
screenImage.setDrawable(new TextureRegionDrawable(backgroundTexture));
pixmap.dispose();
potPixmap.dispose();
}
super.enter();

View File

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

View File

@@ -1,9 +1,7 @@
package forge.adventure.stage;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;
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.SceneType;
import forge.adventure.world.WorldSave;
import forge.adventure.world.WorldSaveHeader;
/**
* Base class to render a player sprite on a map
@@ -48,6 +45,13 @@ public abstract class GameStage extends Stage {
public GameStage() {
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();
backgroundSprites = new Group();
@@ -157,6 +161,18 @@ public abstract class GameStage extends Stage {
{
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)
{
debugCollision(true);
@@ -258,15 +274,8 @@ public abstract class GameStage extends Stage {
}
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);
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;
WorldSave.getCurrentSave().header.createPreview();
AdventureApplicationAdapter.instance.switchScene(SceneType.StartScene.instance);
}

View File

@@ -128,17 +128,45 @@ public class WorldBackground extends Actor {
public void initialize() {
tileSize = WorldSave.getCurrentSave().getWorld().getTileSize();
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()];
ArrayList[][] createChunks = new ArrayList[WorldSave.getCurrentSave().getWorld().getWidthInTiles()][WorldSave.getCurrentSave().getWorld().getHeightInTiles()];
chunksSprites = createChunks;
ArrayList[][] createSprites = new ArrayList[WorldSave.getCurrentSave().getWorld().getWidthInTiles()][WorldSave.getCurrentSave().getWorld().getHeightInTiles()];
chunksSpritesBackground = createSprites;
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);
if(loadingTexture==null)
{
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) {
return xy * tileSize * chunkSize;
}

View File

@@ -8,8 +8,11 @@ import forge.adventure.character.CharacterSprite;
import forge.adventure.character.EnemySprite;
import forge.adventure.data.BiomeData;
import forge.adventure.data.EnemyData;
import forge.adventure.data.WorldData;
import forge.adventure.scene.*;
import forge.adventure.util.Current;
import forge.adventure.util.SaveFileContent;
import forge.adventure.util.SaveFileData;
import forge.adventure.world.World;
import forge.adventure.world.WorldSave;
import org.apache.commons.lang3.tuple.Pair;
@@ -22,7 +25,7 @@ import java.util.Random;
/**
* 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;
protected EnemySprite currentMob;
@@ -75,6 +78,7 @@ public class WorldStage extends GameStage {
AdventureApplicationAdapter.instance.switchScene(SceneType.DuelScene.instance);
});
currentMob = mob;
WorldSave.getCurrentSave().autoSave();
break;
}
}
@@ -224,7 +228,6 @@ public class WorldStage extends GameStage {
}
}
setBounds(WorldSave.getCurrentSave().getWorld().getWidthInPixels(), WorldSave.getCurrentSave().getWorld().getHeightInPixels());
}
@@ -232,4 +235,55 @@ public class WorldStage extends GameStage {
public void leave() {
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:
return 500;
default:
return 90000;
return 600;
}
}

View File

@@ -46,6 +46,7 @@ public class RewardActor extends Actor implements Disposable, ImageFetcher.Callb
if(needsToBeDisposed)
image.dispose();
}
public Reward getReward() {
@@ -54,18 +55,9 @@ public class RewardActor extends Actor implements Disposable, ImageFetcher.Callb
@Override
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)
{

View File

@@ -1,13 +1,9 @@
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
*/
public interface SaveFileContent {
void writeToSaveFile(ObjectOutputStream saveFile) throws IOException ;
void readFromSaveFile(ObjectInputStream saveFile) throws IOException, ClassNotFoundException;
void load(SaveFileData data);
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) {
PixmapIO.PNG png = new PixmapIO.PNG();
png.setFlipY(b);
png.setFlipY(flip);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
png.write(stream, pixmap);
byte[] data = stream.toByteArray();

View File

@@ -2,25 +2,19 @@ package forge.adventure.world;
import com.badlogic.gdx.files.FileHandle;
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.HeroListData;
import forge.adventure.util.Config;
import forge.adventure.util.Reward;
import forge.adventure.util.SaveFileContent;
import forge.adventure.util.SignalList;
import forge.adventure.util.*;
import forge.deck.CardPool;
import forge.deck.Deck;
import forge.item.PaperCard;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
* 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 int avatarIndex;
private int heroRace;
@@ -31,20 +25,25 @@ public class AdventurePlayer implements Serializable, Disposable, SaveFileConten
private int gold=0;
private int maxLife=20;
private int life=20;
private DifficultyData difficultyData;
private final DifficultyData difficultyData=new DifficultyData();
static public AdventurePlayer current()
{
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) {
deck = startingDeck;
gold =difficultyData.staringMoney;
cards.addAll(deck.getAllCardsInASinglePool().toFlatList());
cards.clear();
cards.addAllFlat(deck.getAllCardsInASinglePool().toFlatList());
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;
avatarIndex = avatar;
heroRace = race;
@@ -57,7 +56,7 @@ public class AdventurePlayer implements Serializable, Disposable, SaveFileConten
public Deck getDeck() {
return deck;
}
public List<PaperCard> getCards() {
public CardPool getCards() {
return cards;
}
@@ -82,47 +81,71 @@ public class AdventurePlayer implements Serializable, Disposable, SaveFileConten
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
public void readFromSaveFile(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
name = in.readUTF();
worldPosX = in.readFloat();
worldPosY = in.readFloat();
public void load(SaveFileData data) {
avatarIndex = in.readInt();
heroRace = in.readInt();
isFemale = in.readBoolean();
gold = in.readInt();
life = in.readInt();
maxLife = in.readInt();
deck = (Deck) in.readObject();
cards = (List) in.readObject();
this.difficultyData.startingLife=data.readInt("startingLife");
this.difficultyData.staringMoney=data.readInt("staringMoney");
this.difficultyData.startingDifficulty=data.readBool("startingDifficulty");
this.difficultyData.name=data.readString("difficultyName");
this.difficultyData.enemyLifeFactor=data.readFloat("enemyLifeFactor");
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();
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() {
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.util.Config;
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;
/**
@@ -21,23 +18,28 @@ public class PointOfInterest implements SaveFileContent {
@Override
public void writeToSaveFile(ObjectOutputStream saveFile) throws IOException {
saveFile.writeUTF(data.name);
Serializer.writeVector(saveFile,position);
Serializer.writeRectangle(saveFile,rectangle);
saveFile.writeInt(spriteIndex);
public void load(SaveFileData saveFileData) {
data=PointOfInterestData.getPointOfInterest(saveFileData.readString("name"));
position.set(saveFileData.readVector2("position"));
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
public void readFromSaveFile(ObjectInputStream saveFile) throws IOException {
String name= saveFile.readUTF();
data=PointOfInterestData.getPointOfInterest(name);
Serializer.readVector(saveFile,position);
Serializer.readRectangle(saveFile,rectangle);
spriteIndex=saveFile.readInt();
oldMapId="";
Array<Sprite> textureAtlas = Config.instance().getAtlas(data.spriteAtlas).createSprites(data.sprite);
sprite = textureAtlas.get(spriteIndex);
public SaveFileData save() {
SaveFileData data=new SaveFileData();
data.store("name",this.data.name);
data.store("position",position);
data.store("rectangle",rectangle);
data.store("spriteIndex",spriteIndex);
return data;
}
PointOfInterestData data;

View File

@@ -1,10 +1,8 @@
package forge.adventure.world;
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.List;
@@ -48,42 +46,48 @@ public class PointOfInterestMap implements SaveFileContent {
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
public void readFromSaveFile(ObjectInputStream saveFile) throws IOException, ClassNotFoundException {
numberOfChunksX=saveFile.readInt();
numberOfChunksY=saveFile.readInt();
tileSize=saveFile.readInt();
chunkSize=saveFile.readInt();
public void load(SaveFileData data) {
numberOfChunksX=data.readInt("numberOfChunksX");
numberOfChunksY=data.readInt("numberOfChunksY");
tileSize=data.readInt("tileSize");
chunkSize=data.readInt("chunkSize");
mapObjects = new List[numberOfChunksX][numberOfChunksY];
for (int x = 0; x < numberOfChunksX; x++) {
for (int y = 0; y < numberOfChunksY; y++) {
mapObjects[x][y] = new ArrayList();
int arraySize=saveFile.readInt();
int arraySize=data.readInt("mapObjects["+x +"]["+y+"]");
for(int i=0;i<arraySize;i++)
{
PointOfInterest pointsOfInterest=new PointOfInterest();
pointsOfInterest.readFromSaveFile(saveFile);
pointsOfInterest.load(data.readSubData("mapObjects["+x +"]["+y+"]["+i+"]"));
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 forge.adventure.data.BiomeSpriteData;
import forge.adventure.util.SaveFileContent;
import forge.adventure.util.SaveFileData;
import org.apache.commons.lang3.tuple.Pair;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -13,10 +13,37 @@ import java.util.List;
/**
* 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;
HashMap<Integer, BiomeSpriteData> objectData = new HashMap<>();
BiomeSpriteDataMap objectData = new BiomeSpriteDataMap();
HashMap<String, Integer> objectKeys = new HashMap<>();
int tileSize;
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) {
return objectData.get(id);
@@ -86,4 +95,28 @@ public class SpritesDataMap implements Serializable {
return new ArrayList<>();
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.Paths;
import forge.adventure.util.SaveFileContent;
import forge.adventure.util.Serializer;
import forge.adventure.util.SaveFileData;
import org.apache.commons.lang3.tuple.Pair;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
@@ -39,6 +38,7 @@ public class World implements Disposable, SaveFileContent {
private BiomeTexture[] biomeTexture;
private long seed;
private final Random random = new Random();
private boolean worldDataLoaded=false;
public Random getRandom()
{
@@ -48,49 +48,65 @@ public class World implements Disposable, SaveFileContent {
return (int) (Math.log(Long.highestOneBit(biome)) / Math.log(2));
}
@Override
public void writeToSaveFile(java.io.ObjectOutputStream out) throws IOException {
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 {
public void loadWorldData()
{
if(worldDataLoaded)
return;
FileHandle handle = Config.instance().getFile(Paths.WORLD);
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();
biomeImage = Serializer.ReadPixmap(in);
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();
int biomeIndex=0;
for (BiomeData biome : data.GetBiomes()) {
biomeTexture = new BiomeTexture[data.GetBiomes().size()+1];
for(int i = 0; i<data.GetBiomes().size(); i++)
{
biomeTexture[i] = new BiomeTexture(data.GetBiomes().get(i), data.tileSize);
biomeTexture[biomeIndex] = new BiomeTexture(biome, data.tileSize);
biomeIndex++;
}
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) {
return mapObjectIds.get(id);
}
@@ -201,9 +217,7 @@ public class World implements Disposable, SaveFileContent {
public World generateNew(long seed) {
FileHandle handle = Config.instance().getFile(Paths.WORLD);
String rawJson = handle.readString();
data = (new Json()).fromJson(WorldData.class, rawJson);
loadWorldData();
if(seed==0)
{
seed=random.nextLong();
@@ -231,11 +245,9 @@ public class World implements Disposable, SaveFileContent {
pix.fill();
int biomeIndex = -1;
biomeTexture = new BiomeTexture[data.GetBiomes().size() + 1];
for (BiomeData biome : data.GetBiomes()) {
biomeIndex++;
biomeTexture[biomeIndex] = new BiomeTexture(biome, data.tileSize);
int biomeXStart = (int) Math.round(biome.startPointX * (double) width);
int biomeYStart = (int) Math.round(biome.startPointY * (double) height);
int biomeWidth = (int) Math.round(biome.width * (double) width);
@@ -380,7 +392,6 @@ public class World implements Disposable, SaveFileContent {
biomeIndex++;
pix.setColor(1, 1, 1, 1);
biomeTexture[biomeIndex] = new BiomeTexture(data.roadTileset, data.tileSize);
for (Pair<PointOfInterest, PointOfInterest> townPair : allSortedTowns) {
Vector2 currentPoint = townPair.getKey().getTilePosition(data.tileSize);

View File

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

View File

@@ -1,7 +1,9 @@
package forge.adventure.world;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.utils.Disposable;
import forge.adventure.scene.Scene;
import forge.adventure.util.Serializer;
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 {
name = in.readUTF();
if(preview!=null)
preview.dispose();
preview = Serializer.ReadPixmap(in);
saveDate = (Date) in.readObject();
@@ -35,4 +39,16 @@ public class WorldSaveHeader implements java.io.Serializable, Disposable {
public void 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;
}
// 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;
boolean xCost = sa.getPayCosts().hasXInAnyCostPart() || sa.getHostCard().hasStartOfKeyword("Strive");
if (!xCost) {
@@ -746,7 +770,7 @@ public class AiController {
// one is warded and can't be paid for.
if (sa.usesTargeting()) {
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;
Cost wardCost = ComputerUtilCard.getTotalWardCost(tgt);
if (wardCost.hasManaCost()) {

View File

@@ -1746,12 +1746,12 @@ public class ComputerUtilCard {
int att = 0;
if (stAb.hasParam("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;
if (stAb.hasParam("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());
}

View File

@@ -592,7 +592,7 @@ public class ComputerUtilCost {
// Ward - will be accounted for when rechecking a targeted ability
if (sa.usesTargeting()) {
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);
if (wardCost.hasManaCost()) {
extraManaNeeded += wardCost.getTotalMana().getCMC();

View File

@@ -1745,6 +1745,10 @@ public class AttachAi extends SpellAbilityAi {
sa.getTargets().add(tgt);
}
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;
}

View File

@@ -2,6 +2,8 @@ package forge.ai.ability;
import java.util.*;
import forge.game.card.*;
import forge.game.keyword.Keyword;
import org.apache.commons.lang3.StringUtils;
import com.google.common.base.Predicate;
@@ -32,14 +34,7 @@ import forge.game.GlobalRuleChange;
import forge.game.ability.AbilityKey;
import forge.game.ability.AbilityUtils;
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.CardUtil;
import forge.game.card.CounterEnumType;
import forge.game.combat.Combat;
import forge.game.cost.Cost;
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;
}

View File

@@ -66,6 +66,10 @@ public final class ImageKeys {
}
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) {
return cachedCards.get(key);
}
@@ -101,6 +105,9 @@ public final class ImageKeys {
dir = CACHE_CARD_PICS_DIR;
}
if (missingCards.contains(filename))
return null;
File cachedFile = cachedCards.get(filename);
if (cachedFile != null) {
return cachedFile;
@@ -237,6 +244,8 @@ public final class ImageKeys {
}
// System.out.println("File not found, no image created: " + key);
//add missing cards
missingCards.add(filename);
return null;
}

View File

@@ -178,7 +178,13 @@ public final class GameActionUtil {
final String[] k = keyword.split(":");
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.putParam("PrecostDesc", "Disturb —");
@@ -190,11 +196,6 @@ public final class GameActionUtil {
desc.append("(").append(inst.getReminderText()).append(")");
newSA.setDescription(desc.toString());
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.getRestrictions().setZone(ZoneType.Graveyard);
@@ -733,7 +734,7 @@ public final class GameActionUtil {
return list;
}
CardCollection completeList = new CardCollection();
PlayerCollection players = game.getPlayers();
PlayerCollection players = new PlayerCollection(game.getPlayers());
// CR 613.7k use APNAP
int indexAP = players.indexOf(game.getPhaseHandler().getPlayerTurn());
if (indexAP != -1) {

View File

@@ -36,7 +36,7 @@ public final class GameObjectPredicates {
return new Predicate<GameObject>() {
@Override
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) {
return new Predicate<Card>() {
@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")) {
if (!card.sharesPermanentTypeWith(source)) {
return false;

View File

@@ -832,12 +832,7 @@ public class Cost implements Serializable {
sb.append(Cost.NUM_NAMES[i]);
}
sb.append(" ");
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);
sb.append(" ").append(type);
if (1 != i) {
sb.append("s");
}

View File

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

View File

@@ -199,7 +199,9 @@ public class CostDiscard extends CostPartWithList {
// discard itself for cycling cost
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)
@@ -226,8 +228,7 @@ public class CostDiscard extends CostPartWithList {
protected void handleChangeZoneTrigger(Player payer, SpellAbility ability, CardCollectionView targetCards) {
super.handleChangeZoneTrigger(payer, ability, targetCards);
if (!targetCards.isEmpty())
{
if (!targetCards.isEmpty()) {
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
runParams.put(AbilityKey.Player, payer);
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) {
final Card source = ability.getHostCard();
if (!this.payCostFromSource()) {
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 true;

View File

@@ -121,7 +121,6 @@ public class CostGainLife extends CostPart {
return true;
}
public <T> T accept(ICostVisitor<T> visitor) {
return visitor.visit(this);
}

View File

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

View File

@@ -42,7 +42,6 @@ public class CostTap extends CostPart {
@Override
public boolean isUndoable() { return true; }
@Override
public boolean isReusable() { return true; }

View File

@@ -49,7 +49,6 @@ public class CostUntap extends CostPart {
@Override
public boolean isRenewable() { return true; }
/*
* (non-Javadoc)
*

View File

@@ -38,7 +38,7 @@ public class PaymentDecision {
this.counterTable = counterTable;
}
private PaymentDecision(Card chosen) {
private PaymentDecision(Card chosen) {
this(null, null, null, null, null);
cards.add(chosen);
}
@@ -81,7 +81,7 @@ public class PaymentDecision {
*/
@Override
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) {

View File

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

View File

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

View File

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

View File

@@ -227,7 +227,6 @@ public class Untap extends Phase {
for (final Card c : game.getCardsInGame()) {
c.removeExertedBy(player);
}
} // end doUntap
private static void optionalUntap(final Card c) {
@@ -261,7 +260,7 @@ public class Untap extends Phase {
@Override
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));
}
public static final Predicate<Player> hasCounters() {
return new Predicate<Player>() {
@Override

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -19,6 +19,7 @@ import forge.model.FModel;
import forge.player.GamePlayerUtil;
import forge.screens.deckeditor.CDeckEditorUI;
import forge.screens.deckeditor.controllers.CEditorTokenViewer;
import forge.sound.MusicPlaylist;
import forge.sound.SoundSystem;
import forge.toolbox.*;
import forge.util.Localizer;
@@ -27,6 +28,8 @@ import org.apache.commons.lang3.tuple.Pair;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.io.File;
@@ -262,6 +265,8 @@ public enum CSubmenuPreferences implements ICDoc {
initializeAutoUpdaterComboBox();
initializeMulliganRuleComboBox();
initializeAiProfilesComboBox();
initializeSoundSetsComboBox();
initializeMusicSetsComboBox();
initializeStackAdditionsComboBox();
initializeLandPlayedComboBox();
initializeColorIdentityCombobox();
@@ -466,6 +471,35 @@ public enum CSubmenuPreferences implements ICDoc {
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() {
final String latestOpt = Localizer.getInstance().getMessage("latestArtOpt");
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> cbpCardArtPreference = new FComboBoxPanel<>(localizer.getMessage("lblPreferredArt")+":");
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> cbpStackAdditions = new FComboBoxPanel<>(localizer.getMessage("cbpStackAdditions")+":");
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(new NoteLabel(localizer.getMessage("nlEnableSounds")), descriptionConstraints);
pnlPrefs.add(cbpSoundSets, comboBoxConstraints);
pnlPrefs.add(new NoteLabel(localizer.getMessage("nlpSoundSets")), descriptionConstraints);
pnlPrefs.add(cbEnableMusic, titleConstraints);
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(new NoteLabel(localizer.getMessage("nlAltSoundSystem")), descriptionConstraints);
pnlPrefs.add(cbSROptimize, titleConstraints);
@@ -740,6 +748,14 @@ public enum VSubmenuPreferences implements IVSubmenu<CSubmenuPreferences> {
return cbpMulliganRule;
}
public FComboBoxPanel<String> getSoundSetsComboBoxPanel() {
return cbpSoundSets;
}
public FComboBoxPanel<String> getMusicSetsComboBoxPanel() {
return cbpMusicSets;
}
public FComboBoxPanel<String> getAiProfilesComboBoxPanel() {
return cbpAiProfiles;
}

View File

@@ -40,9 +40,6 @@ import javax.sound.sampled.UnsupportedAudioFileException;
import com.google.common.io.Files;
import com.sipgate.mp3wav.Converter;
import forge.localinstance.properties.ForgeConstants;
/**
* SoundSystem - a simple sound playback system for Forge.
* 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) {
File fSound = new File(ForgeConstants.SOUND_DIR, fileName);
File fSound = new File(SoundSystem.instance.getSoundDirectory(), fileName);
return fSound.exists();
}
@@ -195,7 +192,7 @@ public class AudioClip implements IAudioClip {
}
private Clip createClip(String filename) {
File fSound = new File(ForgeConstants.SOUND_DIR, filename);
File fSound = new File(SoundSystem.instance.getSoundDirectory(), filename);
if (!fSound.exists()) {
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.enabledChildrenCount = enabledChildrenCount;
}
public boolean hasChildren() { return this.numberOfChildren > 0;}
public boolean allChildrenSelected(){ return this.numberOfChildren == this.selectedChildrenCount; };
public boolean allChildrenEnabled(){ return this.enabledChildrenCount == this.numberOfChildren; };
public boolean hasChildren() { return this.numberOfChildren > 0; }
public boolean allChildrenSelected() { return this.numberOfChildren == this.selectedChildrenCount; }
public boolean allChildrenEnabled() { return this.enabledChildrenCount == this.numberOfChildren; }
}
// == Fields of the FCheckboxTree class ==

View File

@@ -85,6 +85,7 @@ public class Forge implements ApplicationListener {
public static String CJK_Font = "";
public static int hoveredCount = 0;
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) {
if (GuiBase.getInterface() == null) {
@@ -771,6 +772,7 @@ public class Forge implements ApplicationListener {
}
}
}
mouseButtonID = 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.quest.QuestMenu;
import forge.screens.settings.GuiDownloader;
import forge.sound.AudioClip;
import forge.sound.AudioMusic;
import forge.sound.IAudioClip;
import forge.sound.IAudioMusic;
import forge.sound.*;
import forge.toolbox.FOptionPane;
import forge.toolbox.GuiChoose;
import forge.util.*;
@@ -272,7 +269,7 @@ public class GuiMobile implements IGuiBase {
@Override
public IAudioClip createAudioClip(final String filename) {
return AudioClip.createClip(ForgeConstants.SOUND_DIR + filename);
return AudioClip.createClip(SoundSystem.instance.getSoundDirectory() + filename);
}
@Override
@@ -288,6 +285,7 @@ public class GuiMobile implements IGuiBase {
@Override
public void clearImageCache() {
ImageCache.clear();
ImageKeys.clearMissingCards();
}
@Override

View File

@@ -113,6 +113,7 @@ public class ImageCache {
public static void clear() {
missingIconKeys.clear();
ImageKeys.clearMissingCards();
}
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 com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.math.Vector2;
import forge.Forge;
import forge.Graphics;
import forge.card.CardRenderer.CardStackPosition;
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
@Override
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
FThreads.invokeInEdtLater(new Runnable() {
@Override

View File

@@ -18,6 +18,7 @@ import forge.screens.TabPageScreen;
import forge.screens.TabPageScreen.TabPage;
import forge.screens.home.HomeScreen;
import forge.screens.match.MatchController;
import forge.sound.MusicPlaylist;
import forge.sound.SoundSystem;
import forge.toolbox.FCheckBox;
import forge.toolbox.FGroupList;
@@ -612,6 +613,29 @@ public class SettingsPage extends TabPage<SettingsScreen> {
localizer.getMessage("nlVibrateAfterLongPress")),
6);
//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,
localizer.getMessage("cbAdjustSoundsVolume"),
localizer.getMessage("nlAdjustSoundsVolume"),

View File

@@ -1,6 +1,8 @@
#Add one announcement per line
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?
Innistrad: Midnight Hunt (MID) and Innistrad: Midnight Hunt Commander (MIC) are 100% supported
Various improvements have been made to the desktop and mobile user interface to enhance the experience
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.
Planar Conquest now features a new plane - Forgotten Realms, based on the AFR and AFC sets.
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. ***

View File

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

View File

@@ -1,8 +1,8 @@
{
"activeFile": "map/swamp_town.tmx",
"activeFile": "map/island_town.tmx",
"expandedProjectPaths": [
"map",
"tileset",
"map",
"obj"
],
"file.lastUsedOpenFilter": "Alle Dateien (*)",
@@ -33,10 +33,10 @@
},
"map/island_town.tmx": {
"scale": 3,
"selectedLayer": 2,
"selectedLayer": 1,
"viewCenter": {
"x": 240,
"y": 136
"x": 251,
"y": 73
}
},
"map/mountain_town.tmx": {
@@ -100,9 +100,9 @@
"tileset/buildings.tsx",
"tileset/main.tsx",
"map/forest_town.tmx",
"map/island_town.tmx",
"map/mountain_town.tmx",
"map/swamp_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"?>
<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>
<export target="wastetown..tmx" format="tmx"/>
</editorsettings>
@@ -12,7 +12,7 @@
</layer>
<layer id="2" name="Ground" width="30" height="17">
<data encoding="base64" compression="zlib">
eJxjYBgFowA/KOWljz2T2BgYJkOxETtCXB8HmxxQRsAvZ9kJs2kBzrEj/J7LgWDncUDkCbmbXEArf2FLM/o4whNX/FIa19jsIpVNCchBisfzOOIXW1xTE9A63SKDEl4ETU97kcGovcQBAKfZHlY=
eJxjYBgFowA/KOWljz2T2BgYJkOxETtCXB8HmxxQRsAvZ9kJs2kBzrEj/J7LgWDncUDkCbmbXEArf2FLM/o4whMmjk5TCxCKU3SaWiAHKR7PY4lfGA2LY1oAWqdbZFDCi6DpaS8yGLWXOAAAywwdJA==
</data>
</layer>
<layer id="3" name="Foreground" width="30" height="17">
@@ -20,22 +20,22 @@
<property name="spriteLayer" type="bool" value="true"/>
</properties>
<data encoding="base64" compression="zlib">
eJxjYKAfKORgYCgC4mIOOloKBPycDAwCQCzISV97KQVzBRgY5gnQ396dQDt3AbHeIAkvdyEGBg8hTDalIJeLPLlRQB7AFXe44peacY0MRuN2FKCDK+wMDFfZ6W/vQNSHIAAACK8NIg==
eJxjYKAfKORgYCgC4mIOOloKBPycDAwCQCzISV97KQVzBRgY5gnQ396dQDt3AbHeIAkvdyEGBg8hTDalIJeLPLlRQB7AFXcwcXSaVmA0bkcBOrjCzsBwlZ3+9g5EfQgCABETDSI=
</data>
</layer>
<layer id="5" name="AboveSprites" width="30" height="17">
<data encoding="base64" compression="zlib">
eJxjYBgFgxXMFWBgmCcw0K5AAG0hBgYdIUz2KBhaAFfc4Yrf0bgeBaNgeAEArncEPg==
eJxjYBgFgxXMFWBgmCcw0K5AAG0hBgYdIUz2KBhaAFfcwcTR6VEwCkbB8AIAtDsEPg==
</data>
</layer>
<objectgroup id="4" name="Objects">
<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="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="44" template="../obj/shop.tx" x="208" 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"/>
</objectgroup>
</map>

View File

@@ -23,6 +23,6 @@
"height": 0.7,
"color": "10a2e0",
"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"]
}

View File

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

View File

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

View File

@@ -3,7 +3,7 @@ ManaCost:3 U
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.
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
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.

View File

@@ -2,7 +2,7 @@ Name:Awe for the Guilds
ManaCost:2 R
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.
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
AI:RemoveDeck:All
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
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.
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
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
ManaCost:1 B
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).
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.

View File

@@ -3,7 +3,7 @@ ManaCost:G
Types:Creature Human Druid
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.
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
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.

View File

@@ -2,8 +2,7 @@ Name:Followed Footsteps
ManaCost:3 U U
Types:Enchantment Aura
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.
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:Picture:http://www.wizards.com/global/images/magic/general/followed_footsteps.jpg
SVar:TrigCopy:DB$ CopyPermanent | Defined$ Enchanted | SpellDescription$ At 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.
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
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:PlayMain1:TRUE
DeckNeeds:Type$Dwarf

View File

@@ -2,9 +2,7 @@ Name:Nahiri, the Harbinger
ManaCost:2 R W
Types:Legendary Planeswalker Nahiri
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.
SVar:DBDraw:DB$ Draw | Defined$ You | NumCards$ 1 | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ GE1 | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
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.
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.
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
PT:3/3
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.
SVar:TrigDiscard:DB$ Discard | Defined$ You | NumCards$ 1 | Mode$ TgtChoose | RememberDiscarded$ True | SubAbility$ DBPutCounter
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 | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ GE1 | SubAbility$ DBAnimate
SVar:DBAnimate:DB$ Animate | Defined$ TriggeredCard | Types$ Vampire | Duration$ Permanent | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ GE1 | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
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:AB$ PutCounter | Cost$ Discard<1/Card> | Defined$ TriggeredCardLKICopy | CounterType$ P1P1 | CounterNum$ 1 | SubAbility$ DBPump
SVar:DBPump:DB$ Pump | Defined$ TriggeredCard | KW$ Haste | SubAbility$ DBAnimate
SVar:DBAnimate:DB$ Animate | Defined$ TriggeredCard | Types$ Vampire | Duration$ Permanent
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.

View File

@@ -3,7 +3,7 @@ ManaCost:3 R
Types:Creature Elemental Cat
PT:2/3
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
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.

View File

@@ -5,7 +5,7 @@ PT:3/4
K:Flash
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.
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: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.

View File

@@ -19,7 +19,7 @@ Colors:green
Types:Creature Werewolf
PT:2/1
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
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.)

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.

View File

@@ -0,0 +1,15 @@
Name:Cemetery Protector
ManaCost:2 W W
Types:Creature Human Soldier
PT:3/4
K:Flash
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigExile | TriggerDescription$ When CARDNAME enters the battlefield, exile a card from a graveyard.
SVar:TrigExile:DB$ ChangeZone | Origin$ Graveyard | Destination$ Exile | ChangeType$ Card | ChangeNum$ 1 | SelectPrompt$ Select a card from a graveyard | Mandatory$ True | Hidden$ True | Imprint$ True
T:Mode$ LandPlayed | ValidCard$ Land.YouOwn+sharesCardTypeWith Imprinted.ExiledWithSource | Execute$ TrigToken | TriggerZones$ Battlefield | TriggerDescription$ Whenever you play a land or cast a spell, if it shares a card type with the exiled card, create a 1/1 white Human creature token.
T:Mode$ SpellCast | ValidCard$ Card.sharesCardTypeWith Imprinted.ExiledWithSource | ValidActivatingPlayer$ You | Execute$ TrigToken | TriggerZones$ Battlefield | Secondary$ True | TriggerDescription$ Whenever you play a land or cast a spell, if it shares a card type with the exiled card, create a 1/1 white Human creature token.
SVar:TrigToken:DB$ Token | TokenScript$ w_1_1_human
T:Mode$ ChangesZone | Origin$ Battlefield | ValidCard$ Card.Self | Destination$ Any | Execute$ DBCleanup | Static$ True
SVar:DBCleanup:DB$ Cleanup | ClearImprinted$ True
DeckHas:Ability$Token
DeckHints:Ability$Graveyard & Ability$Discard
Oracle:Flash\nWhen Cemetery Protector enters the battlefield, exile a card from a graveyard.\nWhenever you play a land or cast a spell, if it shares a card type with the exiled card, create a 1/1 white Human creature token.

View File

@@ -0,0 +1,19 @@
Name:Chandra, Dressed to Kill
ManaCost:1 R R
Types:Legendary Planeswalker Chandra
Loyalty:3
A:AB$ Mana | Cost$ AddCounter<1/LOYALTY> | Planeswalker$ True | Produced$ R | SubAbility$ DBDealDamage | SpellDescription$ Add {R}. CARDNAME deals 1 damage to up to one target player or planeswalker.
SVar:DBDealDamage:DB$ DealDamage | ValidTgts$ Player,Planeswalker | TargetMin$ 0 | TargetMax$ 1 | TgtPrompt$ Select up to one target player or planeswalker | NumDmg$ 1
A:AB$ Dig | Cost$ AddCounter<1/LOYALTY> | Planeswalker$ True | Defined$ You | DigNum$ 1 | ChangeNum$ All | DestinationZone$ Exile | RememberChanged$ True | SubAbility$ DBEffect | StackDescription$ SpellDescription | SpellDescription$ Exile the top card of your library. If it's red, you may cast it this turn.
SVar:DBEffect:DB$ Effect | ConditionDefined$ Remembered | ConditionPresent$ Card.Red | ConditionCompare$ GE1 | RememberObjects$ RememberedCard | StaticAbilities$ STPlay | SubAbility$ DBCleanup | ForgetOnMoved$ Exile
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:STPlay:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered+nonLand | AffectedZone$ Exile | Description$ You may cast the exiled card this turn.
A:AB$ Dig | Cost$ SubCounter<7/LOYALTY> | Planeswalker$ True | Ultimate$ True | Defined$ You | DigNum$ 5 | ChangeNum$ All | DestinationZone$ Exile | RememberChanged$ True | SubAbility$ DBEffect2 | StackDescription$ SpellDescription | SpellDescription$ Exile the top five cards of your library. You may cast red spells from among them this turn. You get an emblem with "Whenever you cast a red spell, this emblem deals X damage to any target, where X is the amount of mana spent to cast that spell."
SVar:DBEffect2:DB$ Effect | RememberObjects$ RememberedCard | StaticAbilities$ STPlay2 | SubAbility$ DBEffect3 | ForgetOnMoved$ Exile
SVar:DBEffect3:DB$ Effect | Name$ Emblem - Chandra, Dressed to Kill | Triggers$ TRSpellCast | Duration$ Permanent | SubAbility$ DBCleanup
SVar:STPlay2:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered+Red+nonLand | AffectedZone$ Exile | Description$ You may cast red spells from among the exiled cards this turn.
SVar:TRSpellCast:Mode$ SpellCast | ValidCard$ Card.Red | ValidActivatingPlayer$ You | TriggerZones$ Command | Execute$ TrigDealDamage | TriggerDescription$ Whenever you cast a red spell, this emblem deals X damage to any target, where X is the amount of mana spent to cast that spell."
SVar:TrigDealDamage:DB$ DealDamage | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ X
SVar:X:Count$TriggeredManaSpent
SVar:BuffedBy:Card.Red
Oracle:[+1]: Add {R}. Chandra, Dressed to Kill deals 1 damage to up to one target player or planeswalker.\n[+1]: Exile the top card of your library. If it's red, you may cast it this turn.\n[-7]: Exile the top five cards of your library. You may cast red spells from among them this turn. You get an emblem with "Whenever you cast a red spell, this emblem deals X damage to any target, where X is the amount of mana spent to cast that spell."

View File

@@ -0,0 +1,21 @@
Name:Child of the Pack
ManaCost:2 R G
Types:Creature Human Werewolf
PT:2/5
A:AB$ Token | Cost$ 2 R G | TokenScript$ g_2_2_wolf | StackDescription$ SpellDescription | SpellDescription$ Create a 2/2 green Wolf creature token.
K:Daybound
AlternateMode:DoubleFaced
DeckHas:Ability$Token & Type$Wolf
Oracle:{2}{R}{G}: Create a 2/2 green Wolf creature token.\nDaybound (If a player casts no spells during their own turn, it becomes night next turn.)
ALTERNATE
Name:Savage Packmate
ManaCost:no cost
Colors:red,green
Types:Creature Werewolf
PT:5/5
K:Trample
S:Mode$ Continuous | Affected$ Creature.Other+YouCtrl | AddPower$ 1 | Description$ Other creatures you control get +1/+0.
K:Nightbound
Oracle:Trample\nOther creatures you control get +1/+0.\nNightbound (If a player casts at least two spells during their own turn, it becomes day next turn.)

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