mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 03:08:02 +00:00
Merge remote-tracking branch 'upstream/master' into smart-card-art-deck-import-desktop-and-mobile
This commit is contained in:
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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()));
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,7 +266,7 @@ public class CardUtil {
|
||||
case MythicRare:
|
||||
return 500;
|
||||
default:
|
||||
return 90000;
|
||||
return 600;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -120,5 +120,5 @@ public class CostExileFromStack extends CostPart {
|
||||
public <T> T accept(ICostVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ public class CostGainLife extends CostPart {
|
||||
sb.append("Have an opponent gain ").append(this.getAmount()).append(" life");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
public List<Player> getPotentialTargets(final Player payer, final Card source) {
|
||||
List<Player> res = new ArrayList<>();
|
||||
for (Player p : payer.getGame().getPlayers()) {
|
||||
@@ -121,7 +121,6 @@ public class CostGainLife extends CostPart {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public <T> T accept(ICostVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ public class CostPayment extends ManaConversionMatrix {
|
||||
public final SpellAbility getAbility() {
|
||||
return this.ability;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Constructor for Cost_Payment.
|
||||
|
||||
@@ -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()
|
||||
*/
|
||||
|
||||
@@ -38,17 +38,16 @@ public class CostTap extends CostPart {
|
||||
}
|
||||
|
||||
public int paymentOrder() { return -1; }
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isUndoable() { return true; }
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isReusable() { return true; }
|
||||
|
||||
@Override
|
||||
public boolean isRenewable() { return true; }
|
||||
|
||||
|
||||
@Override
|
||||
public final String toString() {
|
||||
return "{T}";
|
||||
@@ -70,7 +69,7 @@ public class CostTap extends CostPart {
|
||||
ability.getHostCard().tap(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public <T> T accept(ICostVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ public class CostTapType extends CostPartWithList {
|
||||
|
||||
@Override
|
||||
public boolean isRenewable() { return true; }
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
@@ -131,7 +131,7 @@ public class CostTapType extends CostPartWithList {
|
||||
|
||||
String type = this.getType();
|
||||
boolean sameType = false;
|
||||
|
||||
|
||||
if (type.contains(".sharesCreatureTypeWith")) {
|
||||
sameType = true;
|
||||
type = TextUtil.fastReplace(type, ".sharesCreatureTypeWith", "");
|
||||
|
||||
@@ -45,11 +45,10 @@ public class CostUntap extends CostPart {
|
||||
|
||||
@Override
|
||||
public boolean isUndoable() { return true; }
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isRenewable() { return true; }
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
@@ -88,7 +87,7 @@ public class CostUntap extends CostPart {
|
||||
ability.getHostCard().untap(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public <T> T accept(ICostVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,4 +119,4 @@ public class ExtraTurn {
|
||||
this.cantSetSchemesInMotion = noSchemes;
|
||||
}
|
||||
|
||||
} //end class Untap
|
||||
}
|
||||
|
||||
@@ -226,8 +226,7 @@ public class Untap extends Phase {
|
||||
// even if they are not creatures
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -63,7 +63,6 @@ public final class PlayerPredicates {
|
||||
return Predicates.not(isCardInPlay(cardName));
|
||||
}
|
||||
|
||||
|
||||
public static final Predicate<Player> hasCounters() {
|
||||
return new Predicate<Player>() {
|
||||
@Override
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -84,6 +84,5 @@ public abstract class Expressions {
|
||||
}
|
||||
return " ? ";
|
||||
}
|
||||
|
||||
|
||||
} // end class AllZoneUtil
|
||||
|
||||
}
|
||||
|
||||
@@ -290,6 +290,7 @@ public class GuiDesktop implements IGuiBase {
|
||||
@Override
|
||||
public void clearImageCache() {
|
||||
ImageCache.clear();
|
||||
ImageKeys.clearMissingCards();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -96,6 +96,7 @@ public class ImageCache {
|
||||
public static void clear() {
|
||||
_CACHE.invalidateAll();
|
||||
_missingIconKeys.clear();
|
||||
ImageKeys.clearMissingCards();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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 ==
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -113,6 +113,7 @@ public class ImageCache {
|
||||
|
||||
public static void clear() {
|
||||
missingIconKeys.clear();
|
||||
ImageKeys.clearMissingCards();
|
||||
}
|
||||
|
||||
public static void disposeTexture(){
|
||||
|
||||
@@ -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*/
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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. ***
|
||||
|
||||
@@ -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
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"]
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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.
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.)
|
||||
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
8
forge-gui/res/cardsfolder/upcoming/arm_the_cathars.txt
Normal file
8
forge-gui/res/cardsfolder/upcoming/arm_the_cathars.txt
Normal 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.
|
||||
10
forge-gui/res/cardsfolder/upcoming/ascendant_packleader.txt
Normal file
10
forge-gui/res/cardsfolder/upcoming/ascendant_packleader.txt
Normal 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.
|
||||
9
forge-gui/res/cardsfolder/upcoming/belligerent_guest.txt
Normal file
9
forge-gui/res/cardsfolder/upcoming/belligerent_guest.txt
Normal 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.")
|
||||
9
forge-gui/res/cardsfolder/upcoming/blood_hypnotist.txt
Normal file
9
forge-gui/res/cardsfolder/upcoming/blood_hypnotist.txt
Normal 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.
|
||||
@@ -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.)
|
||||
10
forge-gui/res/cardsfolder/upcoming/bloodtithe_harvester.txt
Normal file
10
forge-gui/res/cardsfolder/upcoming/bloodtithe_harvester.txt
Normal 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.
|
||||
13
forge-gui/res/cardsfolder/upcoming/bloodvial_purveyor.txt
Normal file
13
forge-gui/res/cardsfolder/upcoming/bloodvial_purveyor.txt
Normal 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.
|
||||
8
forge-gui/res/cardsfolder/upcoming/boarded_window.txt
Normal file
8
forge-gui/res/cardsfolder/upcoming/boarded_window.txt
Normal 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.
|
||||
11
forge-gui/res/cardsfolder/upcoming/bramble_wurm.txt
Normal file
11
forge-gui/res/cardsfolder/upcoming/bramble_wurm.txt
Normal 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.
|
||||
11
forge-gui/res/cardsfolder/upcoming/breathkeeper_seraph.txt
Normal file
11
forge-gui/res/cardsfolder/upcoming/breathkeeper_seraph.txt
Normal 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."
|
||||
@@ -0,0 +1,32 @@
|
||||
Name:Brine Comber
|
||||
ManaCost:1 W U
|
||||
Types:Creature Spirit
|
||||
PT:1/1
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigToken | TriggerDescription$ When CARDNAME enters the battlefield or becomes the target of an Aura spell, create a 1/1 white Spirit creature token with flying.
|
||||
T:Mode$ BecomesTarget | ValidTarget$ Card.Self | ValidSource$ Aura | SourceType$ Spell | TriggerZones$ Battlefield | Execute$ TrigToken | Secondary$ True | TriggerDescription$ When CARDNAME enters the battlefield or becomes the target of an Aura spell, create a 1/1 white Spirit creature token with flying.
|
||||
SVar:TrigToken:DB$ Token | TokenScript$ w_1_1_spirit_flying
|
||||
K:Disturb:W U
|
||||
AlternateMode:DoubleFaced
|
||||
SVar:EnchantMe:Multiple
|
||||
DeckHas:Ability$Graveyard & Ability$Token
|
||||
DeckHints:Type$Enchantment
|
||||
Oracle:Whenever Brine Comber enters the battlefield or becomes the target of an Aura spell, create a 1/1 white Spirit creature token with flying.\nDisturb {W}{U} (You may cast this card from your graveyard transformed for its disturb cost.)
|
||||
|
||||
ALTERNATE
|
||||
|
||||
Name:Brinebound Gift
|
||||
ManaCost:no cost
|
||||
Colors:white,blue
|
||||
Types:Enchantment Aura
|
||||
K:Enchant creature
|
||||
A:SP$ Attach | ValidTgts$ Creature | TgtPrompt$ Select target creature | AILogic$ Pump
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigToken | TriggerDescription$ When CARDNAME enters the battlefield or enchanted creature becomes the target of an Aura spell, create a 1/1 white Spirit creature token with flying.
|
||||
T:Mode$ BecomesTarget | ValidTarget$ Creature.EnchantedBy | ValidSource$ Aura | SourceType$ Spell | TriggerZones$ Battlefield | Execute$ TrigToken | Secondary$ True | TriggerDescription$ When CARDNAME enters the battlefield or enchanted creature becomes the target of an Aura spell, create a 1/1 white Spirit creature token with flying.
|
||||
SVar:TrigToken:DB$ Token | TokenScript$ w_1_1_spirit_flying
|
||||
R:Event$ Moved | ValidCard$ Card.Self | Destination$ Graveyard | ReplaceWith$ Exile | Description$ If CARDNAME would be put into a graveyard from anywhere, exile it instead.
|
||||
SVar:Exile:DB$ ChangeZone | Hidden$ True | Origin$ All | Destination$ Exile | Defined$ ReplacedCard
|
||||
S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddSVar$ EnchantTarget | Secondary$ True
|
||||
SVar:EnchantTarget:SVar:EnchantMe:Multiple
|
||||
DeckHas:Ability$Token
|
||||
DeckHints:Type$Enchantment
|
||||
Oracle:Enchant creature\nWhenever Brinebound Gift enters the battlefield or enchanted creature becomes the target of an Aura spell, create a 1/1 white Spirit creature token with flying.\nIf Brinebound Gift would be put into a graveyard from anywhere, exile it instead.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user