mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-14 09:48:02 +00:00
Compare commits
1 Commits
betterImag
...
forge-2.0.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
08719b1596 |
@@ -6,7 +6,7 @@ Dev instructions here: [Getting Started](https://github.com/Card-Forge/forge/wik
|
||||
|
||||
## Requirements / Tools
|
||||
|
||||
- your favourite Java IDE (IntelliJ, Eclipse, VSCodium, Emacs, Vi...)
|
||||
- you favourite Java IDE (IntelliJ, Eclipse, VSCodium, Emacs, Vi...)
|
||||
- Java JDK 17 or later
|
||||
- Git
|
||||
- Git client (optional)
|
||||
@@ -22,41 +22,42 @@ Dev instructions here: [Getting Started](https://github.com/Card-Forge/forge/wik
|
||||
|
||||
- Clone your forked project to your local machine
|
||||
|
||||
- Go to the project location on your machine. Run Maven to download all dependencies and build a snapshot. Example for Windows & Linux: `mvn -U -B clean -P windows-linux install`
|
||||
- Go to the project location on your machine. Run Maven to download all dependencies and build a snapshot. Example for Windows & Linux: `mvn -U -B clean -P windows-linux install`
|
||||
|
||||
## IntelliJ
|
||||
|
||||
IntelliJ is the recommended IDE for Forge development. Quick start guide for [setting up the Forge project within IntelliJ](https://github.com/Card-Forge/forge/wiki/IntelliJ-setup).
|
||||
|
||||
|
||||
## Eclipse
|
||||
|
||||
Eclipse includes Maven integration so a separate install is not necessary. For other IDEs, your mileage may vary.
|
||||
Eclipse includes Maven integration so a separate install is not necessary. For other IDEs, your mileage may vary.
|
||||
At this time, Eclipse is not the recommended IDE for Forge development.
|
||||
|
||||
### Project Setup
|
||||
|
||||
- Follow the instructions for cloning from GitHub. You'll need to setup an account and your SSH key.
|
||||
- Follow the instructions for cloning from GitHub. You'll need to setup an account and your SSH key.
|
||||
|
||||
If you are on a Windows machine you can use Putty with TortoiseGit for SSH keys. Run puttygen.exe to generate the key -- save the private key and export
|
||||
the OpenSSH public key. If you just leave the dialog open, you can copy and paste the key from it to your GitHub profile under
|
||||
"SSH keys". Run pageant.exe and add the private key generated earlier. TortoiseGit will use this for accessing GitHub.
|
||||
If you are on a Windows machine you can use Putty with TortoiseGit for SSH keys. Run puttygen.exe to generate the key -- save the private key and export
|
||||
the OpenSSH public key. If you just leave the dialog open, you can copy and paste the key from it to your GitHub profile under
|
||||
"SSH keys". Run pageant.exe and add the private key generated earlier. TortoiseGit will use this for accessing GitHub.
|
||||
|
||||
- Fork the Forge git repo to your GitHub account.
|
||||
|
||||
- Clone your forked repo to your local machine.
|
||||
|
||||
- Make sure the Java SDK is installed -- not just the JRE. Java 17 or newer required. If you execute `java -version` at the shell or command prompt, it should report version 17 or later.
|
||||
- Make sure the Java SDK is installed -- not just the JRE. Java 17 or newer required. If you execute `java -version` at the shell or command prompt, it should report version 17 or later.
|
||||
|
||||
- Install Eclipse 2021-12 or later for Java. Launch it.
|
||||
- Install Eclipse 2021-12 or later for Java. Launch it.
|
||||
|
||||
- Create a workspace. Go to the workbench. Right-click inside of Package Explorer > Import... > Maven > Existing Maven Projects > Navigate to root path of the local forge repo and
|
||||
- Create a workspace. Go to the workbench. Right-click inside of Package Explorer > Import... > Maven > Existing Maven Projects > Navigate to root path of the local forge repo and
|
||||
ensure everything is checked > Finish.
|
||||
|
||||
- Let Eclipse run through building the project. You may be prompted for resolving any missing Maven plugins -- accept the ones offered. You may see errors appear in the "Problems" tab. These should
|
||||
be automatically resolved as plug-ins are installed and Eclipse continues the build process. If this is the first time for some plug-in installs, Eclipse may prompt you to restart. Do so. Be patient
|
||||
- Let Eclipse run through building the project. You may be prompted for resolving any missing Maven plugins -- accept the ones offered. You may see errors appear in the "Problems" tab. These should
|
||||
be automatically resolved as plug-ins are installed and Eclipse continues the build process. If this is the first time for some plug-in installs, Eclipse may prompt you to restart. Do so. Be patient
|
||||
for this first time through.
|
||||
|
||||
- Once everything builds, all errors should disappear. You can now advance to Project launch.
|
||||
- Once everything builds, all errors should disappear. You can now advance to Project launch.
|
||||
|
||||
### Project Launch
|
||||
|
||||
@@ -66,15 +67,15 @@ This is the standard configuration used for releasing to Windows / Linux / MacOS
|
||||
|
||||
- Right-click on forge-gui-desktop > Run As... > Java Application > "Main - forge.view" > Ok
|
||||
|
||||
- The familiar Forge splash screen, etc. should appear. Enjoy!
|
||||
- The familiar Forge splash screen, etc. should appear. Enjoy!
|
||||
|
||||
#### Mobile (Desktop dev)
|
||||
|
||||
This is the configuration used for doing mobile development using the Windows / Linux / MacOS front-end. Knowledge of libgdx is helpful here.
|
||||
This is the configuration used for doing mobile development using the Windows / Linux / MacOS front-end. Knowledge of libgdx is helpful here.
|
||||
|
||||
- Right-click on forge-gui-mobile-dev > Run As... > Java Application > "Main - forge.app" > Ok.
|
||||
|
||||
- A view similar to a mobile phone should appear. Enjoy!
|
||||
- A view similar to a mobile phone should appear. Enjoy!
|
||||
|
||||
### Eclipse / Android SDK Integration
|
||||
|
||||
@@ -98,7 +99,7 @@ TBD
|
||||
|
||||
#### Android Platform
|
||||
|
||||
In Intellij, if the SDK Manager is not already running, go to Tools > Android > Android SDK Manager. Install the following options / versions:
|
||||
In Intellij, if the SDK Manager is not already running, go to Tools > Android > Android SDK Manager. Install the following options / versions:
|
||||
|
||||
- Android SDK Build-tools 35.0.0
|
||||
- Android 15 (API 35) SDK Platform
|
||||
@@ -123,11 +124,10 @@ TBD
|
||||
|
||||
SNAPSHOT builds can be built via the Maven integration in Eclipse.
|
||||
|
||||
1. Create a Maven build for the forge top-level project. Right-click on the forge project. Run as.. > Maven build...
|
||||
|
||||
1) Create a Maven build for the forge top-level project. Right-click on the forge project. Run as.. > Maven build...
|
||||
- On the Main tab, set Goals: clean install, set Profiles: windows-linux
|
||||
|
||||
2. Run forge Maven build. If everything built, you should see "BUILD SUCCESS" in the Console View.
|
||||
2) Run forge Maven build. If everything built, you should see "BUILD SUCCESS" in the Console View.
|
||||
|
||||
The resulting snapshot will be found at: forge-gui-desktop/target/forge-gui-desktop-[version]-SNAPSHOT
|
||||
|
||||
@@ -141,7 +141,7 @@ Card scripting resources are found in the forge-gui/res/ path.
|
||||
|
||||
### Project Hierarchy
|
||||
|
||||
Forge is divided into 4 primary projects with additional projects that target specific platform releases. The primary projects are:
|
||||
Forge is divided into 4 primary projects with additional projects that target specific platform releases. The primary projects are:
|
||||
|
||||
- forge-ai
|
||||
- forge-core
|
||||
@@ -158,38 +158,32 @@ The platform-specific projects are:
|
||||
|
||||
#### forge-ai
|
||||
|
||||
The forge-ai project contains the computer opponent logic for gameplay. It includes decision-making algorithms for specific abilities, cards and turn phases.
|
||||
|
||||
#### forge-core
|
||||
|
||||
The forge-core project contains the core game engine, card mechanics, rules engine, and fundamental game logic. It includes the implementation of Magic: The Gathering rules, card interactions, and the game state management system.
|
||||
|
||||
#### forge-game
|
||||
|
||||
The forge-game project handles the game session management, player interactions, and game flow control. It includes implementations for multiplayer support, game modes, matchmaking, and game state persistence. This module bridges the core game engine with the user interface and networking components.
|
||||
|
||||
#### forge-gui
|
||||
|
||||
The forge-gui project contains the user interface components and rendering logic for the game. It includes the main game window, card displays, player interactions, and the scripting resource definitions in the res/ path.
|
||||
The forge-gui project includes the scripting resource definitions in the res/ path.
|
||||
|
||||
#### forge-gui-android
|
||||
|
||||
Libgdx-based backend targeting Android. Requires Android SDK and relies on forge-gui-mobile for GUI logic.
|
||||
Libgdx-based backend targeting Android. Requires Android SDK and relies on forge-gui-mobile for GUI logic.
|
||||
|
||||
#### forge-gui-desktop
|
||||
|
||||
Java Swing based GUI targeting desktop machines.
|
||||
|
||||
Screen layout and game logic revolving around the GUI is found here. For example, the overlay arrows (when enabled) that indicate attackers and blockers, or the targets of the stack are defined and drawn by this.
|
||||
Screen layout and game logic revolving around the GUI is found here. For example, the overlay arrows (when enabled) that indicate attackers and blockers, or the targets of the stack are defined and drawn by this.
|
||||
|
||||
#### forge-gui-ios
|
||||
|
||||
Libgdx-based backend targeting iOS. Relies on forge-gui-mobile for GUI logic.
|
||||
Libgdx-based backend targeting iOS. Relies on forge-gui-mobile for GUI logic.
|
||||
|
||||
#### forge-gui-mobile
|
||||
|
||||
Mobile GUI game logic utilizing [libgdx](https://libgdx.badlogicgames.com/) library. Screen layout and game logic revolving around the GUI for the mobile platforms is found here.
|
||||
Mobile GUI game logic utilizing [libgdx](https://libgdx.badlogicgames.com/) library. Screen layout and game logic revolving around the GUI for the mobile platforms is found here.
|
||||
|
||||
#### forge-gui-mobile-dev
|
||||
|
||||
Libgdx backend for desktop development for mobile backends. Utilizes LWJGL. Relies on forge-gui-mobile for GUI logic.
|
||||
Libgdx backend for desktop development for mobile backends. Utilizes LWJGL. Relies on forge-gui-mobile for GUI logic.
|
||||
|
||||
@@ -32,7 +32,6 @@ Join the **Forge community** on [Discord](https://discord.gg/HcPJNyD66a)!
|
||||
4. **Java Requirement:** Ensure you have **Java 17 or later** installed.
|
||||
|
||||
### 📱 Android Installation
|
||||
- _(Note: **Android 11** is the minimum requirements with at least **6GB RAM** to run smoothly. You need to enable **"Install unknown apps"** for Forge to initialize and update itself)_
|
||||
- Download the **APK** from the [Snapshot Build](https://github.com/Card-Forge/forge/releases/tag/daily-snapshots). On the first launch, Forge will automatically download all necessary assets.
|
||||
|
||||
---
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>forge</artifactId>
|
||||
<groupId>forge</groupId>
|
||||
<version>${revision}</version>
|
||||
<version>2.0.04</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<artifactId>forge</artifactId>
|
||||
<groupId>forge</groupId>
|
||||
<version>${revision}</version>
|
||||
<version>2.0.04</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>forge-ai</artifactId>
|
||||
|
||||
@@ -359,28 +359,18 @@ public class SpecialCardAi {
|
||||
private static final int demonSacThreshold = Integer.MAX_VALUE; // if we're in dire conditions, sac everything from worst to best hoping to find an answer
|
||||
|
||||
public static boolean considerSacrificingCreature(final Player ai, final SpellAbility sa) {
|
||||
Card c = sa.getHostCard();
|
||||
|
||||
// Only check for sacrifice if it's the owner's turn, and it can attack.
|
||||
// TODO: Maybe check if sacrificing a creature allows AI to kill the opponent with the rest on their turn?
|
||||
if (!CombatUtil.canAttack(c) ||
|
||||
!ai.getGame().getPhaseHandler().isPlayerTurn(sa.getActivatingPlayer())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CardCollection flyingCreatures = CardLists.filter(ai.getCardsIn(ZoneType.Battlefield),
|
||||
CardPredicates.UNTAPPED.and(
|
||||
CardPredicates.hasKeyword(Keyword.FLYING).or(CardPredicates.hasKeyword(Keyword.REACH))));
|
||||
CardPredicates.UNTAPPED.and(
|
||||
CardPredicates.hasKeyword(Keyword.FLYING).or(CardPredicates.hasKeyword(Keyword.REACH))));
|
||||
boolean hasUsefulBlocker = false;
|
||||
|
||||
for (Card fc : flyingCreatures) {
|
||||
if (!ComputerUtilCard.isUselessCreature(ai, fc)) {
|
||||
for (Card c : flyingCreatures) {
|
||||
if (!ComputerUtilCard.isUselessCreature(ai, c)) {
|
||||
hasUsefulBlocker = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ai.getLife() <= c.getNetPower() && !hasUsefulBlocker;
|
||||
return ai.getLife() <= sa.getHostCard().getNetPower() && !hasUsefulBlocker;
|
||||
}
|
||||
|
||||
public static int getSacThreshold() {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<artifactId>forge</artifactId>
|
||||
<groupId>forge</groupId>
|
||||
<version>${revision}</version>
|
||||
<version>2.0.04</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>forge-core</artifactId>
|
||||
|
||||
@@ -39,9 +39,6 @@ public final class ImageKeys {
|
||||
public static final String SPECFACE_R = "$rspec";
|
||||
public static final String SPECFACE_G = "$gspec";
|
||||
|
||||
private static final String URL_SCRYFALL = "https://api.scryfall.com";
|
||||
public static final String URL_PIC_SCRYFALL_DOWNLOAD = URL_SCRYFALL + "/cards/";
|
||||
|
||||
private static String CACHE_CARD_PICS_DIR, CACHE_TOKEN_PICS_DIR, CACHE_ICON_PICS_DIR, CACHE_BOOSTER_PICS_DIR,
|
||||
CACHE_FATPACK_PICS_DIR, CACHE_BOOSTERBOX_PICS_DIR, CACHE_PRECON_PICS_DIR, CACHE_TOURNAMENTPACK_PICS_DIR;
|
||||
public static String ADVENTURE_CARD_PICS_DIR;
|
||||
|
||||
@@ -285,10 +285,7 @@ public final class CardRules implements ICardCharacteristics {
|
||||
return true;
|
||||
}
|
||||
CardType type = mainPart.getType();
|
||||
if (!type.isLegendary()) {
|
||||
return false;
|
||||
}
|
||||
if (canBeCreature() || type.isVehicle() || type.isSpacecraft()) {
|
||||
if (type.isLegendary() && canBeCreature()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -516,7 +516,7 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
||||
@Override
|
||||
public boolean isAttachment() { return isAura() || isEquipment() || isFortification(); }
|
||||
@Override
|
||||
public boolean isAura() { return hasSubtype("Aura"); }
|
||||
public boolean isAura() { return hasSubtype("Aura"); }
|
||||
@Override
|
||||
public boolean isEquipment() { return hasSubtype("Equipment"); }
|
||||
@Override
|
||||
@@ -529,9 +529,6 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
||||
return hasSubtype("Contraption");
|
||||
}
|
||||
|
||||
public boolean isVehicle() { return hasSubtype("Vehicle"); }
|
||||
public boolean isSpacecraft() { return hasSubtype("Spacecraft"); }
|
||||
|
||||
@Override
|
||||
public boolean isSaga() {
|
||||
return hasSubtype("Saga");
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
package forge.item;
|
||||
|
||||
public enum ArtStyle {
|
||||
Normal("fullborder", "normal"),
|
||||
Crop("artcrop", "art_crop");
|
||||
|
||||
public final String filename;
|
||||
public final String scryfall;
|
||||
ArtStyle(String filename, String scryfall) {
|
||||
this.filename = filename;
|
||||
this.scryfall = scryfall;
|
||||
}
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
package forge.item;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import forge.ImageKeys;
|
||||
import forge.StaticData;
|
||||
import forge.card.CardEdition;
|
||||
import forge.card.CardStateName;
|
||||
|
||||
public record ImageKey(String setCode, String name, String collectorNumber, String artistName, CardStateName state, ImageType type, boolean custom) implements Serializable
|
||||
{
|
||||
|
||||
public List<String> getFilename(ArtStyle art) {
|
||||
List<String> result = Lists.newArrayList();
|
||||
String cn = collectorNumber;
|
||||
if (type == ImageType.Token) {
|
||||
if (ImageKeys.HIDDEN_CARD.equals(name)) {
|
||||
// hidden only exist as png
|
||||
result.add("hidden.png");
|
||||
return result;
|
||||
}
|
||||
// TODO Token doesn't use fullborder or artcrop ArtStyle yet
|
||||
if (!StringUtils.isEmpty(setCode) && !setCode.equals(CardEdition.UNKNOWN_CODE)) {
|
||||
if (!StringUtils.isEmpty(cn) && !cn.equals(IPaperCard.NO_COLLECTOR_NUMBER)) {
|
||||
result.add(setCode + "/" + cn + "_" + name);
|
||||
}
|
||||
result.add(setCode + "/" + name);
|
||||
}
|
||||
result.add(name);
|
||||
} else if (type == ImageType.Card) {
|
||||
if (!StringUtils.isEmpty(setCode) && !setCode.equals(CardEdition.UNKNOWN_CODE)) {
|
||||
if (!StringUtils.isEmpty(cn) && !cn.equals(IPaperCard.NO_COLLECTOR_NUMBER)) {
|
||||
result.add(setCode + "/" + cn + "_" + name + "." + art.filename);
|
||||
}
|
||||
result.add(setCode + "/" + name + "." + art.filename);
|
||||
}
|
||||
result.add(name + "." + art.filename);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public Pair<String, String> getDownloadUrl(ArtStyle art) {
|
||||
if (custom) {
|
||||
return null;
|
||||
}
|
||||
if (type == ImageType.Token && ImageKeys.HIDDEN_CARD.equals(name)) {
|
||||
return Pair.of("hidden.png", "https://cards.scryfall.io/back.png");
|
||||
}
|
||||
if (StringUtils.isEmpty(collectorNumber) || collectorNumber.equals(IPaperCard.NO_COLLECTOR_NUMBER)) {
|
||||
return null;
|
||||
}
|
||||
// Scryfall only for Cards or Tokens
|
||||
if (type != ImageType.Card && type != ImageType.Token) {
|
||||
return null;
|
||||
}
|
||||
if (StringUtils.isEmpty(setCode) || setCode.equals(CardEdition.UNKNOWN_CODE)) {
|
||||
return null;
|
||||
}
|
||||
CardEdition edition = StaticData.instance().getCardEdition(setCode);
|
||||
if (edition == null || edition.getType() == CardEdition.Type.CUSTOM_SET) return null;
|
||||
// differ token code
|
||||
String setCode = type == ImageType.Card ? edition.getScryfallCode() : edition.getTokensCode();
|
||||
String langCode = edition.getCardsLangCode();
|
||||
String faceParam = "";
|
||||
switch(state) {
|
||||
case Modal:
|
||||
case Secondary:
|
||||
case Transformed:
|
||||
faceParam = "&face=back";
|
||||
break;
|
||||
default:
|
||||
faceParam = "&face=front";
|
||||
break;
|
||||
}
|
||||
|
||||
String ext = art.scryfall == "png" ? ".png" : ".jpg";
|
||||
String filepath = setCode + "/" + collectorNumber + "_" + name + ext;
|
||||
// TODO make scryfall art_crop of split cards separate
|
||||
String collectorNumberEncoded;
|
||||
try {
|
||||
// encode with Charset isn't supported on Android
|
||||
collectorNumberEncoded = URLEncoder.encode(collectorNumber, "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
collectorNumberEncoded = collectorNumber;
|
||||
}
|
||||
|
||||
String url = ImageKeys.URL_PIC_SCRYFALL_DOWNLOAD + String.format(
|
||||
"%s/%s/%s?format=image&version=%s%s", setCode, collectorNumberEncoded, langCode, art.scryfall, faceParam
|
||||
);
|
||||
return Pair.of(filepath, url);
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package forge.item;
|
||||
|
||||
public enum ImageType {
|
||||
Card,
|
||||
Token,
|
||||
Booster,
|
||||
FatPack,
|
||||
BoosterBox,
|
||||
Precon,
|
||||
TournamentPack,
|
||||
;
|
||||
}
|
||||
@@ -523,29 +523,6 @@ public class PaperCard implements Comparable<IPaperCard>, InventoryItemFromSet,
|
||||
return StaticData.instance().isRebalanced(name);
|
||||
}
|
||||
|
||||
public String getCollectorNumberSuffix(CardStateName state) {
|
||||
if (this.collectorNumber.equals(IPaperCard.NO_COLLECTOR_NUMBER)) {
|
||||
return "";
|
||||
}
|
||||
if (getRules().getSplitType() == CardSplitType.Meld) {
|
||||
return state == CardStateName.Meld ? "b" : "a";
|
||||
}
|
||||
switch(state) {
|
||||
case SpecializeB:
|
||||
return "b";
|
||||
case SpecializeG:
|
||||
return "g";
|
||||
case SpecializeR:
|
||||
return "r";
|
||||
case SpecializeU:
|
||||
return "u";
|
||||
case SpecializeW:
|
||||
return "w";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains properties of a card which distinguish it from an otherwise identical copy of the card with the same
|
||||
* name, edition, and collector number. Examples include permanent markings on the card, and flags for Adventure
|
||||
|
||||
@@ -401,68 +401,46 @@ public class BoosterGenerator {
|
||||
System.out.println(numCards + " of type " + slotType);
|
||||
|
||||
// For cards that end in '+', attempt to convert this card to foil.
|
||||
boolean convertAllToFoil = slotType.endsWith("+");
|
||||
if (convertAllToFoil) {
|
||||
boolean convertCardFoil = slotType.endsWith("+");
|
||||
if (convertCardFoil) {
|
||||
slotType = slotType.substring(0, slotType.length() - 1);
|
||||
}
|
||||
|
||||
// Unpack Base
|
||||
BoosterSlot boosterSlot = boosterSlots.get(slotType);
|
||||
Map<String, Integer> slotReplacementCount = bulkSlotReplacement(boosterSlot, numCards);
|
||||
String determineSheet = boosterSlot.replaceSlot();
|
||||
|
||||
List<PaperCard> paperCards = Lists.newArrayList();
|
||||
for(Map.Entry<String, Integer> entry : slotReplacementCount.entrySet()) {
|
||||
String determineSheet = entry.getKey();
|
||||
int numCardsToGenerate = entry.getValue();
|
||||
|
||||
if (determineSheet == null || determineSheet.isEmpty() || numCardsToGenerate == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the sheet ends with a '+', convert all cards in replacement section to foil
|
||||
boolean convertThisToFoil = false;
|
||||
if (determineSheet.endsWith("+")) {
|
||||
determineSheet = determineSheet.substring(0, determineSheet.length() - 1);
|
||||
convertThisToFoil = true;
|
||||
}
|
||||
|
||||
String setCode = template.getEdition();
|
||||
PrintSheet ps;
|
||||
try {
|
||||
// Apply the edition to the sheet name by default. We'll try again if thats not a real sheet
|
||||
ps = getPrintSheet(determineSheet + " " + setCode);
|
||||
} catch (Exception e) {
|
||||
ps = getPrintSheet(determineSheet);
|
||||
}
|
||||
if (convertAllToFoil || convertThisToFoil) {
|
||||
for (PaperCard pc : ps.random(numCardsToGenerate, true)) {
|
||||
paperCards.add(pc.getFoiled());
|
||||
}
|
||||
} else {
|
||||
paperCards.addAll(ps.random(numCardsToGenerate, true));
|
||||
}
|
||||
|
||||
result.addAll(paperCards);
|
||||
if (determineSheet.endsWith("+")) {
|
||||
determineSheet = determineSheet.substring(0, determineSheet.length() - 1);
|
||||
convertCardFoil = true;
|
||||
}
|
||||
|
||||
String setCode = template.getEdition();
|
||||
|
||||
// Ok, so we have a sheet now. Most should be standard sheets, but some named edition sheets
|
||||
List<PaperCard> paperCards;
|
||||
PrintSheet ps;
|
||||
try {
|
||||
// Apply the edition to the sheet name by default. We'll try again if thats not a real sheet
|
||||
ps = getPrintSheet(determineSheet + " " + setCode);
|
||||
} catch(Exception e) {
|
||||
ps = getPrintSheet(determineSheet);
|
||||
}
|
||||
if (convertCardFoil) {
|
||||
paperCards = Lists.newArrayList();
|
||||
for(PaperCard pc : ps.random(numCards, true)) {
|
||||
paperCards.add(pc.getFoiled());
|
||||
}
|
||||
} else {
|
||||
paperCards = ps.random(numCards, true);
|
||||
}
|
||||
|
||||
result.addAll(paperCards);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Map<String, Integer> bulkSlotReplacement(BoosterSlot boosterSlot, int numCards) {
|
||||
Map<String, Integer> slotReplacementCount = new HashMap<>();
|
||||
|
||||
for(int i = 0; i < numCards; i++) {
|
||||
String determineSheet = boosterSlot.replaceSlot();
|
||||
if (slotReplacementCount.containsKey(determineSheet)) {
|
||||
slotReplacementCount.put(determineSheet, slotReplacementCount.get(determineSheet) + 1);
|
||||
} else {
|
||||
slotReplacementCount.put(determineSheet, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return slotReplacementCount;
|
||||
}
|
||||
|
||||
private static void ensureGuaranteedCardInBooster(List<PaperCard> result, SealedTemplate template, String boosterMustContain) {
|
||||
// First, see if there's already a card of the given type
|
||||
String[] types = TextUtil.split(boosterMustContain, ' ');
|
||||
|
||||
@@ -9,8 +9,6 @@ import forge.item.IPaperCard;
|
||||
import forge.item.PaperCard;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
|
||||
public class ImageUtil {
|
||||
public static float getNearestHQSize(float baseSize, float actualSize) {
|
||||
//get nearest power of actualSize to baseSize so that the image renders good
|
||||
@@ -102,7 +100,7 @@ public class ImageUtil {
|
||||
|
||||
if (includeSet) {
|
||||
String editionAliased = isDownloadUrl ? StaticData.instance().getEditions().getCode2ByCode(edition) : ImageKeys.getSetFolder(edition);
|
||||
if (editionAliased.isEmpty()) //FIXME: Custom Cards Workaround
|
||||
if (editionAliased == "") //FIXME: Custom Cards Workaround
|
||||
editionAliased = edition;
|
||||
return TextUtil.concatNoSpace(editionAliased, "/", fname);
|
||||
} else {
|
||||
@@ -166,7 +164,7 @@ public class ImageUtil {
|
||||
|
||||
public static String getScryfallDownloadUrl(PaperCard cp, String face, String setCode, String langCode, boolean useArtCrop, boolean hyphenateAlchemy){
|
||||
String editionCode;
|
||||
if (setCode != null && !setCode.isEmpty())
|
||||
if ((setCode != null) && (setCode.length() > 0))
|
||||
editionCode = setCode;
|
||||
else
|
||||
editionCode = cp.getEdition().toLowerCase();
|
||||
@@ -194,24 +192,8 @@ public class ImageUtil {
|
||||
String faceParam = "";
|
||||
if (cp.getRules().getOtherPart() != null) {
|
||||
faceParam = (face.equals("back") ? "&face=back" : "&face=front");
|
||||
} else if (cp.getRules().getSplitType() == CardSplitType.Meld
|
||||
&& !cardCollectorNumber.endsWith("a")
|
||||
&& !cardCollectorNumber.endsWith("b")) {
|
||||
// Only the bottom half of a meld card shares a collector number.
|
||||
// Hanweir Garrison EMN already has a appended.
|
||||
cardCollectorNumber += face.equals("back") ? "b" : "a";
|
||||
}
|
||||
|
||||
String cardCollectorNumberEncoded;
|
||||
try {
|
||||
cardCollectorNumberEncoded = URLEncoder.encode(cardCollectorNumber, "UTF-8");
|
||||
} catch (Exception e) {
|
||||
// Unlikely, for the possibility that "UTF-8" is not supported.
|
||||
System.err.println("UTF-8 encoding not supported on this device.");
|
||||
cardCollectorNumberEncoded = cardCollectorNumber;
|
||||
}
|
||||
|
||||
return String.format("%s/%s/%s?format=image&version=%s%s", editionCode, cardCollectorNumberEncoded,
|
||||
return String.format("%s/%s/%s?format=image&version=%s%s", editionCode, cardCollectorNumber,
|
||||
langCode, versionParam, faceParam);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<artifactId>forge</artifactId>
|
||||
<groupId>forge</groupId>
|
||||
<version>${revision}</version>
|
||||
<version>2.0.04</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>forge-game</artifactId>
|
||||
|
||||
@@ -193,9 +193,7 @@ public class ForgeScript {
|
||||
return sa.isCrew();
|
||||
} else if (property.equals("Saddle")) {
|
||||
return sa.isKeyword(Keyword.SADDLE);
|
||||
} else if (property.equals("Station")) {
|
||||
return sa.isKeyword(Keyword.STATION);
|
||||
}else if (property.equals("Cycling")) {
|
||||
} else if (property.equals("Cycling")) {
|
||||
return sa.isCycling();
|
||||
} else if (property.equals("Dash")) {
|
||||
return sa.isDash();
|
||||
|
||||
@@ -519,7 +519,6 @@ public class GameAction {
|
||||
}
|
||||
card.setZone(zoneTo);
|
||||
}
|
||||
copied.clearMergedCards();
|
||||
} else {
|
||||
storeChangesZoneAll(copied, zoneFrom, zoneTo, params);
|
||||
// "enter the battlefield as a copy" - apply code here
|
||||
@@ -646,8 +645,7 @@ public class GameAction {
|
||||
// Ask controller if it wants to be on top or bottom of other meld.
|
||||
unmeldPosition++;
|
||||
}
|
||||
unmeld = changeZone(null, zoneTo, unmeld, position, cause, params);
|
||||
storeChangesZoneAll(unmeld, zoneFrom, zoneTo, params);
|
||||
changeZone(null, zoneTo, unmeld, position, cause, params);
|
||||
}
|
||||
} else if (toBattlefield) {
|
||||
for (Player p : game.getPlayers()) {
|
||||
|
||||
@@ -130,6 +130,7 @@ public enum AbilityKey {
|
||||
SourceSA("SourceSA"),
|
||||
SpellAbility("SpellAbility"),
|
||||
SpellAbilityTargets("SpellAbilityTargets"),
|
||||
StackInstance("StackInstance"),
|
||||
StackSa("StackSa"),
|
||||
SurveilNum("SurveilNum"),
|
||||
Target("Target"),
|
||||
@@ -139,7 +140,7 @@ public enum AbilityKey {
|
||||
Valiant("Valiant"),
|
||||
Won("Won"),
|
||||
|
||||
// below shared across different Replacements, don't reuse
|
||||
// below used across different Replacements, don't reuse
|
||||
InternalTriggerTable("InternalTriggerTable"),
|
||||
SimultaneousETB("SimultaneousETB"); // for CR 614.13c
|
||||
|
||||
|
||||
@@ -663,7 +663,7 @@ public class AbilityUtils {
|
||||
Object o = root.getTriggeringObject(AbilityKey.fromString(calcX[0].substring(9)));
|
||||
val = o instanceof Player ? playerXProperty((Player) o, calcX[1], card, ability) : 0;
|
||||
}
|
||||
else if (calcX[0].equals("TriggeredSpellAbility") || calcX[0].equals("SpellTargeted")) {
|
||||
else if (calcX[0].equals("TriggeredSpellAbility") || calcX[0].equals("TriggeredStackInstance") || calcX[0].equals("SpellTargeted")) {
|
||||
final SpellAbility sat = Iterables.getFirst(getDefinedSpellAbilities(card, calcX[0], sa), null);
|
||||
val = sat == null ? 0 : xCount(sat.getHostCard(), calcX[1], sat);
|
||||
}
|
||||
@@ -1281,6 +1281,8 @@ public class AbilityUtils {
|
||||
final Object o = root.getTriggeringObject(AbilityKey.fromString(triggeringType));
|
||||
if (o instanceof SpellAbility) {
|
||||
s = (SpellAbility) o;
|
||||
} else if (o instanceof SpellAbilityStackInstance) {
|
||||
s = ((SpellAbilityStackInstance) o).getSpellAbility();
|
||||
}
|
||||
} else if (defined.endsWith("Targeted") && sa instanceof SpellAbility) {
|
||||
final List<TargetChoices> targets = defined.startsWith("This") ? Arrays.asList(((SpellAbility)sa).getTargets()) : ((SpellAbility)sa).getAllTargetChoices();
|
||||
@@ -1683,11 +1685,11 @@ public class AbilityUtils {
|
||||
return doXMath(x, expr, c, ctb);
|
||||
} else if (TriggerType.SpellCast.equals(t.getMode())) {
|
||||
// Cast Trigger like Hydroid Krasis
|
||||
SpellAbility castSA = (SpellAbility) root.getTriggeringObject(AbilityKey.SpellAbility);
|
||||
if (castSA == null || castSA.getXManaCostPaid() == null) {
|
||||
SpellAbilityStackInstance castSI = (SpellAbilityStackInstance) root.getTriggeringObject(AbilityKey.StackInstance);
|
||||
if (castSI == null || castSI.getSpellAbility().getXManaCostPaid() == null) {
|
||||
return doXMath(0, expr, c, ctb);
|
||||
}
|
||||
return doXMath(castSA.getXManaCostPaid(), expr, c, ctb);
|
||||
return doXMath(castSI.getSpellAbility().getXManaCostPaid(), expr, c, ctb);
|
||||
} else if (TriggerType.Cycled.equals(t.getMode())) {
|
||||
SpellAbility cycleSA = (SpellAbility) sa.getTriggeringObject(AbilityKey.Cause);
|
||||
if (cycleSA == null || cycleSA.getXManaCostPaid() == null) {
|
||||
@@ -3749,10 +3751,6 @@ public class AbilityUtils {
|
||||
return Aggregates.max(paidList, Card::getNetToughness);
|
||||
}
|
||||
|
||||
if (string.startsWith("TapPowerValue")) {
|
||||
return CardLists.getTotalPower(paidList, ctb);
|
||||
}
|
||||
|
||||
if (string.startsWith("SumToughness")) {
|
||||
return Aggregates.sum(paidList, Card::getNetToughness);
|
||||
}
|
||||
|
||||
@@ -72,8 +72,8 @@ public class ChangeTargetsEffect extends SpellAbilityEffect {
|
||||
// 2. prepare new target choices
|
||||
SpellAbilityStackInstance replaceIn = chosenTarget.getKey();
|
||||
GameObject oldTarget = chosenTarget.getValue();
|
||||
TargetChoices newTargetBlock = replaceIn.getTargetChoices();
|
||||
TargetChoices oldTargetBlock = newTargetBlock.clone();
|
||||
TargetChoices oldTargetBlock = replaceIn.getTargetChoices();
|
||||
TargetChoices newTargetBlock = oldTargetBlock.clone();
|
||||
// gets the divided value from old target
|
||||
Integer div = oldTargetBlock.getDividedValue(oldTarget);
|
||||
// 3. test if updated choices would be correct.
|
||||
@@ -87,7 +87,7 @@ public class ChangeTargetsEffect extends SpellAbilityEffect {
|
||||
if (div != null) {
|
||||
newTargetBlock.addDividedAllocation(newTarget, div);
|
||||
}
|
||||
replaceIn.updateTarget(oldTargetBlock, sa.getHostCard());
|
||||
replaceIn.updateTarget(newTargetBlock, sa.getHostCard());
|
||||
}
|
||||
} else {
|
||||
while (changingTgtSI != null) {
|
||||
@@ -104,26 +104,25 @@ public class ChangeTargetsEffect extends SpellAbilityEffect {
|
||||
if (candidates.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
GameEntity choice = Aggregates.random(candidates);
|
||||
TargetChoices oldTarget = changingTgtSA.getTargets();
|
||||
changingTgtSA.resetTargets();
|
||||
GameEntity choice = Aggregates.random(candidates);
|
||||
changingTgtSA.getTargets().add(choice);
|
||||
if (changingTgtSA.isDividedAsYouChoose()) {
|
||||
changingTgtSA.addDividedAllocation(choice, div);
|
||||
}
|
||||
changingTgtSI.updateTarget(oldTarget, sa.getHostCard());
|
||||
|
||||
changingTgtSI.updateTarget(changingTgtSA.getTargets(), sa.getHostCard());
|
||||
}
|
||||
else if (sa.hasParam("DefinedMagnet")) {
|
||||
GameObject newTarget = Iterables.getFirst(getDefinedCardsOrTargeted(sa, "DefinedMagnet"), null);
|
||||
if (newTarget != null && changingTgtSA.canTarget(newTarget)) {
|
||||
int div = changingTgtSA.getTotalDividedValue();
|
||||
TargetChoices oldTarget = changingTgtSA.getTargets();
|
||||
changingTgtSA.resetTargets();
|
||||
changingTgtSA.getTargets().add(newTarget);
|
||||
changingTgtSI.updateTarget(changingTgtSA.getTargets(), sa.getHostCard());
|
||||
if (changingTgtSA.isDividedAsYouChoose()) {
|
||||
changingTgtSA.addDividedAllocation(newTarget, div);
|
||||
}
|
||||
changingTgtSI.updateTarget(oldTarget, sa.getHostCard());
|
||||
}
|
||||
} else {
|
||||
// Update targets, with a potential new target
|
||||
@@ -133,9 +132,8 @@ public class ChangeTargetsEffect extends SpellAbilityEffect {
|
||||
source = changingTgtSA.getTargetCard();
|
||||
}
|
||||
Predicate<GameObject> filter = sa.hasParam("TargetRestriction") ? GameObjectPredicates.restriction(sa.getParam("TargetRestriction").split(","), activator, source, sa) : null;
|
||||
TargetChoices oldTarget = changingTgtSA.getTargets();
|
||||
chooser.getController().chooseNewTargetsFor(changingTgtSA, filter, false);
|
||||
changingTgtSI.updateTarget(oldTarget, sa.getHostCard());
|
||||
TargetChoices newTarget = chooser.getController().chooseNewTargetsFor(changingTgtSA, filter, false);
|
||||
changingTgtSI.updateTarget(newTarget, sa.getHostCard());
|
||||
}
|
||||
}
|
||||
changingTgtSI = changingTgtSI.getSubInstance();
|
||||
|
||||
@@ -2,7 +2,6 @@ package forge.game.ability.effects;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import forge.GameCommand;
|
||||
import forge.game.Game;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
@@ -37,10 +36,6 @@ public class TextBoxExchangeEffect extends SpellAbilityEffect {
|
||||
|
||||
@Override
|
||||
public void resolve(final SpellAbility sa) {
|
||||
if (!checkValidDuration(sa.getParam("Duration"), sa)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final List<Card> tgtCards = getTargetCards(sa);
|
||||
if (tgtCards.size() < 2) {
|
||||
return;
|
||||
@@ -60,37 +55,6 @@ public class TextBoxExchangeEffect extends SpellAbilityEffect {
|
||||
swapTextBox(c1, data2, ts);
|
||||
swapTextBox(c2, data1, ts);
|
||||
|
||||
if (sa.hasParam("Duration")) {
|
||||
final GameCommand revertTextExchange = new GameCommand() {
|
||||
private static final long serialVersionUID = 5331255714437747836L;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
// Check if the cards are still there
|
||||
Card card1 = game.getCardState(c1, null);
|
||||
Card card2 = game.getCardState(c2, null);
|
||||
|
||||
if (card1 != null && c1.equalsWithGameTimestamp(card1)) {
|
||||
card1.removeChangedCardTraits(ts, 0);
|
||||
card1.removeChangedCardKeywords(ts, 0, false);
|
||||
card1.updateChangedText();
|
||||
card1.updateStateForView();
|
||||
game.fireEvent(new GameEventCardStatsChanged(card1));
|
||||
}
|
||||
|
||||
if (card2 != null && c2.equalsWithGameTimestamp(card2)) {
|
||||
card2.removeChangedCardTraits(ts, 0);
|
||||
card2.removeChangedCardKeywords(ts, 0, false);
|
||||
card2.updateChangedText();
|
||||
card2.updateStateForView();
|
||||
game.fireEvent(new GameEventCardStatsChanged(card2));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
addUntilCommand(sa, revertTextExchange);
|
||||
}
|
||||
|
||||
game.fireEvent(new GameEventCardStatsChanged(c1));
|
||||
game.fireEvent(new GameEventCardStatsChanged(c2));
|
||||
}
|
||||
|
||||
@@ -1407,10 +1407,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
|
||||
public final CardCollectionView getMergedCards() {
|
||||
return CardCollection.getView(mergedCards);
|
||||
}
|
||||
public final void setMergedCards(Iterable<Card> mc) {
|
||||
mergedCards = new CardCollection(mc);
|
||||
}
|
||||
|
||||
public final Card getTopMergedCard() {
|
||||
return mergedCards.get(0);
|
||||
}
|
||||
@@ -2743,8 +2739,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
|
||||
|| keyword.startsWith("Class") || keyword.startsWith("Blitz")
|
||||
|| keyword.startsWith("Specialize") || keyword.equals("Ravenous")
|
||||
|| keyword.equals("For Mirrodin") || keyword.equals("Job select") || keyword.startsWith("Craft")
|
||||
|| keyword.startsWith("Landwalk") || keyword.startsWith("Visit") || keyword.startsWith("Mobilize")
|
||||
|| keyword.startsWith("Station")) {
|
||||
|| keyword.startsWith("Landwalk") || keyword.startsWith("Visit") || keyword.startsWith("Mobilize")) {
|
||||
// keyword parsing takes care of adding a proper description
|
||||
} else if (keyword.equals("Read ahead")) {
|
||||
sb.append(Localizer.getInstance().getMessage("lblReadAhead")).append(" (").append(Localizer.getInstance().getMessage("lblReadAheadDesc"));
|
||||
|
||||
@@ -66,15 +66,8 @@ public class CardCopyService {
|
||||
out.setCollectible(copyFrom.isCollectible());
|
||||
|
||||
// this's necessary for forge.game.GameAction.unattachCardLeavingBattlefield(Card)
|
||||
if (copyFrom.hasCardAttachments()) {
|
||||
out.setAttachedCards(copyFrom.getAttachedCards());
|
||||
}
|
||||
if (copyFrom.isAttachedToEntity()) {
|
||||
out.setEntityAttachedTo(copyFrom.getEntityAttachedTo());
|
||||
}
|
||||
if (copyFrom.hasMergedCard()) {
|
||||
out.setMergedCards(copyFrom.getMergedCards());
|
||||
}
|
||||
out.setAttachedCards(copyFrom.getAttachedCards());
|
||||
out.setEntityAttachedTo(copyFrom.getEntityAttachedTo());
|
||||
|
||||
out.setLeavesPlayCommands(copyFrom.getLeavesPlayCommands());
|
||||
|
||||
|
||||
@@ -742,7 +742,7 @@ public class CardFactory {
|
||||
TextUtil.fastReplace(host.getName(), ",", ""),
|
||||
" ", "_").toLowerCase();
|
||||
String set = host.getSetCode().toLowerCase();
|
||||
state.setImageKey(ImageKeys.getTokenKey("offspring_" + name + "|" + set));
|
||||
state.setImageKey(ImageKeys.getTokenKey("offspring_" + name + "_" + set));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -2122,13 +2122,23 @@ public class CardFactoryUtil {
|
||||
} else if (keyword.startsWith("Amplify")) {
|
||||
final String[] ampString = keyword.split(":");
|
||||
final String amplifyMagnitude = ampString[1];
|
||||
final String ampTypes = ampString[2];
|
||||
String[] refinedTypes = ampTypes.split(",");
|
||||
final StringBuilder types = new StringBuilder();
|
||||
for (int i = 0; i < refinedTypes.length; i++) {
|
||||
types.append("Card.").append(refinedTypes[i]).append("+YouCtrl");
|
||||
if (i + 1 != refinedTypes.length) {
|
||||
types.append(",");
|
||||
}
|
||||
}
|
||||
|
||||
// Setup ETB replacement effects
|
||||
final String actualRep = "Event$ Moved | Destination$ Battlefield | ValidCard$ Card.Self |"
|
||||
+ " | ReplacementResult$ Updated | Description$ Amplify " + amplifyMagnitude + " ("
|
||||
+ inst.getReminderText() + ")";
|
||||
|
||||
final String abString = "DB$ Reveal | AnyNumber$ True | RevealValid$ Card.YouOwn+sharesCreatureTypeWith+Other+NotDefinedReplacedSimultaneousETB | RememberRevealed$ True";
|
||||
final String abString = "DB$ Reveal | AnyNumber$ True | RevealValid$ "
|
||||
+ types.toString() + " | RememberRevealed$ True";
|
||||
|
||||
SpellAbility saReveal = AbilityFactory.getAbility(abString, card);
|
||||
|
||||
@@ -2782,7 +2792,7 @@ public class CardFactoryUtil {
|
||||
final String[] k = keyword.split(":");
|
||||
final Cost blitzCost = new Cost(k[1], false);
|
||||
|
||||
final SpellAbility newSA = card.getFirstSpellAbilityWithFallback().copyWithManaCostReplaced(host.getController(), blitzCost);
|
||||
final SpellAbility newSA = card.getFirstSpellAbility().copyWithManaCostReplaced(host.getController(), blitzCost);
|
||||
|
||||
if (k.length > 2) {
|
||||
newSA.getMapParams().put("ValidAfterStack", k[2]);
|
||||
@@ -2895,7 +2905,7 @@ public class CardFactoryUtil {
|
||||
}
|
||||
desc += ")";
|
||||
|
||||
final SpellAbility sa = card.getFirstSpellAbilityWithFallback();
|
||||
final SpellAbility sa = card.getFirstSpellAbility();
|
||||
final SpellAbility newSA = sa.copyWithDefinedCost(new Cost(costStr, false));
|
||||
|
||||
newSA.getRestrictions().setIsPresent(validStr + ".YouCtrl+CanBeSacrificedBy");
|
||||
@@ -3581,15 +3591,6 @@ public class CardFactoryUtil {
|
||||
sa.setSVar("ScavengeX", "Exiled$CardPower");
|
||||
sa.setIntrinsic(intrinsic);
|
||||
inst.addSpellAbility(sa);
|
||||
} else if (keyword.startsWith("Station")) {
|
||||
String effect = "AB$ PutCounter | Cost$ tapXType<1/Creature.Other> | Defined$ Self " +
|
||||
"| CounterType$ CHARGE | CounterNum$ StationX | SorcerySpeed$ True " +
|
||||
"| CostDesc$ | SpellDescription$ Station (" + inst.getReminderText() + ")";
|
||||
|
||||
final SpellAbility sa = AbilityFactory.getAbility(effect, card);
|
||||
sa.setSVar("StationX", "TappedCards$TapPowerValue");
|
||||
sa.setIntrinsic(intrinsic);
|
||||
inst.addSpellAbility(sa);
|
||||
} else if (keyword.startsWith("Encore")) {
|
||||
final String[] k = keyword.split(":");
|
||||
final String manacost = k[1];
|
||||
|
||||
@@ -421,13 +421,13 @@ public class CardLists {
|
||||
* @param cardList the list of creature cards for which to sum the power
|
||||
* @param crew for cards that crew with toughness rather than power
|
||||
*/
|
||||
public static int getTotalPower(Iterable<Card> cardList, CardTraitBase ctb) {
|
||||
public static int getTotalPower(Iterable<Card> cardList, SpellAbility sa) {
|
||||
int total = 0;
|
||||
for (final Card crd : cardList) {
|
||||
if (StaticAbilityTapPowerValue.withToughness(crd, ctb)) {
|
||||
if (StaticAbilityTapPowerValue.withToughness(crd, sa)) {
|
||||
total += Math.max(0, crd.getNetToughness());
|
||||
} else {
|
||||
int m = StaticAbilityTapPowerValue.getMod(crd, ctb);
|
||||
int m = StaticAbilityTapPowerValue.getMod(crd, sa);
|
||||
total += Math.max(0, crd.getNetPower() + m);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,6 +101,7 @@ public class CardView extends GameEntityView {
|
||||
set(TrackableProperty.Controller, ownerAndController);
|
||||
set(TrackableProperty.ImageKey, imageKey);
|
||||
}
|
||||
|
||||
public PlayerView getOwner() {
|
||||
return get(TrackableProperty.Owner);
|
||||
}
|
||||
@@ -1060,8 +1061,6 @@ public class CardView extends GameEntityView {
|
||||
mergedCollection.add(card);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
set(TrackableProperty.MergedCards, null);
|
||||
}
|
||||
updateMergeCollections(mergedCollection);
|
||||
|
||||
|
||||
@@ -282,7 +282,7 @@ public class CostPayment extends ManaConversionMatrix {
|
||||
private static List<Pair<Mana, Integer>> selectManaToPayFor(final ManaPool manapool, final ManaCostShard shard,
|
||||
final SpellAbility saBeingPaidFor, final byte colorsPaid, Map<String, Integer> xManaCostPaidByColor) {
|
||||
final List<Pair<Mana, Integer>> weightedOptions = new ArrayList<>();
|
||||
for (final Mana thisMana : Lists.newArrayList(manapool)) {
|
||||
for (final Mana thisMana : manapool) {
|
||||
if (shard == ManaCostShard.COLORED_X && !ManaCostBeingPaid.canColoredXShardBePaidByColor(MagicColor.toShortString(thisMana.getColor()), xManaCostPaidByColor)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -178,7 +178,6 @@ public enum Keyword {
|
||||
SQUAD("Squad", KeywordWithCost.class, false, "As an additional cost to cast this spell, you may pay %s any number of times. When this creature enters, create that many tokens that are copies of it."),
|
||||
START_YOUR_ENGINES("Start your engines", SimpleKeyword.class, true, "If you have no speed, it starts at 1. It increases once on each of your turns when an opponent loses life. Max speed is 4."),
|
||||
STARTING_INTENSITY("Starting intensity", KeywordWithAmount.class, true, null),
|
||||
STATION("Station", KeywordWithAmount.class, false, "Tap another creature you control: Put charge counters equal to its power on this Spacecraft. Station only as a sorcery. It’s an artifact creature at %d+."),
|
||||
STORM("Storm", SimpleKeyword.class, false, "When you cast this spell, copy it for each other spell that was cast before it this turn. You may choose new targets for the copies."),
|
||||
STRIVE("Strive", KeywordWithCost.class, false, "CARDNAME costs %s more to cast for each target beyond the first."),
|
||||
SUNBURST("Sunburst", SimpleKeyword.class, false, "This enters with either a +1/+1 or charge counter on it for each color of mana spent to cast it based on whether it's a creature."),
|
||||
|
||||
@@ -133,16 +133,18 @@ public class SpellAbilityStackInstance implements IIdentifiable, IHasCardView {
|
||||
return ability.getTargets();
|
||||
}
|
||||
|
||||
public void updateTarget(TargetChoices oldTC, Card cause) {
|
||||
if (oldTC != null) {
|
||||
public void updateTarget(TargetChoices target, Card cause) {
|
||||
if (target != null) {
|
||||
TargetChoices oldTarget = ability.getTargets();
|
||||
ability.setTargets(target);
|
||||
stackDescription = ability.getStackDescription();
|
||||
view.updateTargetCards(this);
|
||||
view.updateTargetPlayers(this);
|
||||
view.updateText(this);
|
||||
|
||||
Set<GameObject> distinctObjects = Sets.newHashSet();
|
||||
for (final GameObject tgt : ability.getTargets()) {
|
||||
if (oldTC.contains(tgt)) {
|
||||
for (final GameObject tgt : target) {
|
||||
if (oldTarget != null && oldTarget.contains(tgt)) {
|
||||
// it was an old target, so don't trigger becomes target
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -43,7 +43,6 @@ import forge.util.TextUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@@ -274,7 +273,7 @@ public final class StaticAbilityContinuous {
|
||||
if (hostCard.hasChosenPlayer()) {
|
||||
Player cp = hostCard.getChosenPlayer();
|
||||
input = input.replaceAll("ChosenPlayerUID", String.valueOf(cp.getId()));
|
||||
input = input.replaceAll("ChosenPlayerName", Matcher.quoteReplacement(cp.getName()));
|
||||
input = input.replaceAll("ChosenPlayerName", cp.getName());
|
||||
}
|
||||
if (hostCard.hasNamedCard()) {
|
||||
final String chosenName = hostCard.getNamedCard().replace(",", ";");
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
package forge.game.staticability;
|
||||
|
||||
import forge.game.CardTraitBase;
|
||||
import forge.game.Game;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.zone.ZoneType;
|
||||
|
||||
public class StaticAbilityTapPowerValue {
|
||||
|
||||
public static boolean withToughness(final Card card, final CardTraitBase ctb) {
|
||||
public static boolean withToughness(final Card card, final SpellAbility sa) {
|
||||
final Game game = card.getGame();
|
||||
for (final Card ca : game.getCardsIn(ZoneType.STATIC_ABILITIES_SOURCE_ZONES)) {
|
||||
for (final StaticAbility stAb : ca.getStaticAbilities()) {
|
||||
if (!stAb.checkConditions(StaticAbilityMode.TapPowerValue)) {
|
||||
continue;
|
||||
}
|
||||
if (withToughness(stAb, card, ctb)) {
|
||||
if (withToughness(stAb, card, sa)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -22,20 +22,20 @@ public class StaticAbilityTapPowerValue {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean withToughness(final StaticAbility stAb, final Card card, final CardTraitBase ctb) {
|
||||
public static boolean withToughness(final StaticAbility stAb, final Card card, final SpellAbility sa) {
|
||||
if (!stAb.getParam("Value").equals("Toughness")) {
|
||||
return false;
|
||||
}
|
||||
if (!stAb.matchesValidParam("ValidCard", card)) {
|
||||
return false;
|
||||
}
|
||||
if (!stAb.matchesValidParam("ValidSA", ctb)) {
|
||||
if (!stAb.matchesValidParam("ValidSA", sa)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static int getMod(final Card card, final CardTraitBase ctb) {
|
||||
public static int getMod(final Card card, SpellAbility sa) {
|
||||
int i = 0;
|
||||
final Game game = card.getGame();
|
||||
for (final Card ca : game.getCardsIn(ZoneType.STATIC_ABILITIES_SOURCE_ZONES)) {
|
||||
@@ -46,10 +46,11 @@ public class StaticAbilityTapPowerValue {
|
||||
if (!stAb.matchesValidParam("ValidCard", card)) {
|
||||
continue;
|
||||
}
|
||||
if (!stAb.matchesValidParam("ValidSA", ctb)) {
|
||||
if (!stAb.matchesValidParam("ValidSA", sa)) {
|
||||
continue;
|
||||
}
|
||||
i += Integer.parseInt(stAb.getParam("Value"));
|
||||
int t = Integer.parseInt(stAb.getParam("Value"));
|
||||
i = i + t;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
|
||||
@@ -83,6 +83,7 @@ public class TriggerBecomesTarget extends Trigger {
|
||||
public final void setTriggeringObjects(final SpellAbility sa, Map<AbilityKey, Object> runParams) {
|
||||
sa.setTriggeringObject(AbilityKey.Source, ((SpellAbility) runParams.get(AbilityKey.SourceSA)).getHostCard());
|
||||
sa.setTriggeringObjectsFrom(runParams, AbilityKey.SourceSA, AbilityKey.Target);
|
||||
sa.setTriggeringObject(AbilityKey.StackInstance, sa.getHostCard().getGame().getStack().getInstanceMatchingSpellAbilityID((SpellAbility) runParams.get(AbilityKey.SourceSA)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -24,6 +24,7 @@ import java.util.Set;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import forge.card.ColorSet;
|
||||
import forge.game.Game;
|
||||
import forge.game.GameEntity;
|
||||
import forge.game.GameObject;
|
||||
import forge.game.ability.AbilityKey;
|
||||
@@ -34,6 +35,7 @@ import forge.game.card.CardUtil;
|
||||
import forge.game.mana.Mana;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.spellability.SpellAbilityStackInstance;
|
||||
import forge.game.spellability.TargetChoices;
|
||||
import forge.util.Expressions;
|
||||
import forge.util.Localizer;
|
||||
@@ -76,9 +78,22 @@ public class TriggerSpellAbilityCastOrCopy extends Trigger {
|
||||
return false;
|
||||
}
|
||||
final Card cast = spellAbility.getHostCard();
|
||||
final Game game = cast.getGame();
|
||||
final SpellAbilityStackInstance si = game.getStack().getInstanceMatchingSpellAbilityID(spellAbility);
|
||||
|
||||
if (!matchesValidParam("ValidPlayer", runParams.get(AbilityKey.Player))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hasParam("ValidActivatingPlayer")) {
|
||||
Player activator = (Player) runParams.get(AbilityKey.Activator);
|
||||
Player activator;
|
||||
if (spellAbility.isManaAbility()) {
|
||||
activator = (Player) runParams.get(AbilityKey.Activator);
|
||||
} else if (si == null) {
|
||||
return false;
|
||||
} else {
|
||||
activator = si.getSpellAbility().getActivatingPlayer();
|
||||
}
|
||||
|
||||
if (!matchesValidParam("ValidActivatingPlayer", activator)) {
|
||||
return false;
|
||||
@@ -128,6 +143,9 @@ public class TriggerSpellAbilityCastOrCopy extends Trigger {
|
||||
|
||||
if (hasParam("TargetsValid")) {
|
||||
SpellAbility sa = spellAbility;
|
||||
if (si != null) {
|
||||
sa = si.getSpellAbility();
|
||||
}
|
||||
|
||||
boolean validTgtFound = false;
|
||||
while (sa != null && !validTgtFound) {
|
||||
@@ -231,10 +249,13 @@ public class TriggerSpellAbilityCastOrCopy extends Trigger {
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public final void setTriggeringObjects(final SpellAbility sa, Map<AbilityKey, Object> runParams) {
|
||||
final SpellAbility cause = (SpellAbility) runParams.get(AbilityKey.SpellAbility);
|
||||
sa.setTriggeringObject(AbilityKey.Card, cause.getHostCard());
|
||||
sa.setTriggeringObject(AbilityKey.SpellAbility, cause);
|
||||
final List<TargetChoices> allTgts = cause.getAllTargetChoices();
|
||||
final SpellAbility castSA = (SpellAbility) runParams.get(AbilityKey.SpellAbility);
|
||||
final SpellAbilityStackInstance si = sa.getHostCard().getGame().getStack().getInstanceMatchingSpellAbilityID(castSA);
|
||||
final SpellAbility saForTargets = si != null ? si.getSpellAbility() : castSA;
|
||||
sa.setTriggeringObject(AbilityKey.Card, castSA.getHostCard());
|
||||
sa.setTriggeringObject(AbilityKey.SpellAbility, castSA);
|
||||
sa.setTriggeringObject(AbilityKey.StackInstance, si);
|
||||
final List<TargetChoices> allTgts = saForTargets.getAllTargetChoices();
|
||||
if (!allTgts.isEmpty()) {
|
||||
final FCollection<GameEntity> saTargets = new FCollection<>();
|
||||
for (TargetChoices tc : allTgts) {
|
||||
@@ -242,10 +263,11 @@ public class TriggerSpellAbilityCastOrCopy extends Trigger {
|
||||
}
|
||||
sa.setTriggeringObject(AbilityKey.SpellAbilityTargets, saTargets);
|
||||
}
|
||||
sa.setTriggeringObject(AbilityKey.LifeAmount, cause.getAmountLifePaid());
|
||||
sa.setTriggeringObject(AbilityKey.LifeAmount, castSA.getAmountLifePaid());
|
||||
sa.setTriggeringObjectsFrom(
|
||||
runParams,
|
||||
AbilityKey.CardLKI,
|
||||
AbilityKey.Player,
|
||||
AbilityKey.Activator,
|
||||
AbilityKey.CurrentStormCount,
|
||||
AbilityKey.CurrentCastSpells
|
||||
|
||||
@@ -267,7 +267,7 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
|
||||
addAbilityActivatedThisTurn(sp, source);
|
||||
}
|
||||
|
||||
Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
||||
Map<AbilityKey, Object> runParams = AbilityKey.mapFromPlayer(source.getController());
|
||||
runParams.put(AbilityKey.Activator, activator);
|
||||
runParams.put(AbilityKey.SpellAbility, sp);
|
||||
game.getTriggerHandler().runTrigger(TriggerType.SpellAbilityCast, runParams, true);
|
||||
@@ -355,7 +355,7 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
|
||||
si = push(sp, si, id);
|
||||
|
||||
// Copied spells aren't cast per se so triggers shouldn't run for them.
|
||||
Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
||||
Map<AbilityKey, Object> runParams = AbilityKey.mapFromPlayer(sp.getHostCard().getController());
|
||||
|
||||
if (sp.isSpell() && !sp.isCopied()) {
|
||||
final Card lki = CardCopyService.getLKICopy(sp.getHostCard());
|
||||
@@ -394,7 +394,7 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
|
||||
}
|
||||
|
||||
runParams.put(AbilityKey.Activator, sp.getActivatingPlayer());
|
||||
runParams.put(AbilityKey.SpellAbility, sp);
|
||||
runParams.put(AbilityKey.SpellAbility, si.getSpellAbility());
|
||||
runParams.put(AbilityKey.CurrentStormCount, thisTurnCast.size());
|
||||
runParams.put(AbilityKey.CurrentCastSpells, Lists.newArrayList(thisTurnCast));
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<parent>
|
||||
<artifactId>forge</artifactId>
|
||||
<groupId>forge</groupId>
|
||||
<version>${revision}</version>
|
||||
<version>2.0.04</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>forge-gui-android</artifactId>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>forge</artifactId>
|
||||
<groupId>forge</groupId>
|
||||
<version>${revision}</version>
|
||||
<version>2.0.04</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>forge-gui-desktop</artifactId>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<parent>
|
||||
<artifactId>forge</artifactId>
|
||||
<groupId>forge</groupId>
|
||||
<version>${revision}</version>
|
||||
<version>2.0.04</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>forge-gui-ios</artifactId>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<artifactId>forge</artifactId>
|
||||
<groupId>forge</groupId>
|
||||
<version>${revision}</version>
|
||||
<version>2.0.04</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>forge-gui-mobile-dev</artifactId>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>forge</artifactId>
|
||||
<groupId>forge</groupId>
|
||||
<version>${revision}</version>
|
||||
<version>2.0.04</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>forge-gui-mobile</artifactId>
|
||||
|
||||
@@ -274,14 +274,6 @@ public class Forge implements ApplicationListener {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean hasExternalInput() {
|
||||
return hasGamepad() || hasKeyboard();
|
||||
}
|
||||
|
||||
public static boolean hasKeyboard() {
|
||||
return !GuiBase.isAndroid();
|
||||
}
|
||||
|
||||
public static InputProcessor getInputProcessor() {
|
||||
return inputProcessor;
|
||||
}
|
||||
@@ -1243,15 +1235,7 @@ public class Forge implements ApplicationListener {
|
||||
if (keyInputAdapter != null) {
|
||||
return keyInputAdapter.keyUp(keyCode);
|
||||
}
|
||||
// if no active key input adapter, give current screen or overlay a chance to handle key
|
||||
FContainer container = FOverlay.getTopOverlay();
|
||||
if (container == null) {
|
||||
container = currentScreen;
|
||||
if (container == null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return container.keyUp(keyCode);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -448,7 +448,7 @@ public class EnemySprite extends CharacterSprite implements Steerable<Vector2> {
|
||||
if (data.copyPlayerDeck && Current.latestDeck() != null) {
|
||||
List<PaperCard> paperCardList = Current.latestDeck().getMain().toFlatList().stream()
|
||||
.filter(paperCard -> !paperCard.isVeryBasicLand())
|
||||
.collect(Collectors.toList());
|
||||
.toList();
|
||||
|
||||
if (paperCardList.size() < 6) {
|
||||
// Player trying to cheese doppleganger and farm cards. Sorry, the fun police have arrived
|
||||
|
||||
@@ -20,7 +20,6 @@ public class PointOfInterestChanges implements SaveFileContent {
|
||||
//private final java.util.Map<Integer, Float> shopModifiers = new HashMap<>();
|
||||
private final java.util.Map<Integer, Integer> reputation = new HashMap<>();
|
||||
private Boolean isBookmarked;
|
||||
private Boolean isVisited;
|
||||
|
||||
public static class Map extends HashMap<String,PointOfInterestChanges> implements SaveFileContent {
|
||||
@Override
|
||||
@@ -68,7 +67,6 @@ public class PointOfInterestChanges implements SaveFileContent {
|
||||
reputation.putAll((java.util.Map<Integer, Integer>) data.readObject("reputation"));
|
||||
}
|
||||
isBookmarked = (Boolean) data.readObject("isBookmarked");
|
||||
isVisited = (Boolean) data.readObject("isVisited");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -80,7 +78,6 @@ public class PointOfInterestChanges implements SaveFileContent {
|
||||
data.storeObject("shopSeeds", shopSeeds);
|
||||
data.storeObject("reputation", reputation);
|
||||
data.storeObject("isBookmarked", isBookmarked);
|
||||
data.storeObject("isVisited", isVisited);
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -180,12 +177,4 @@ public class PointOfInterestChanges implements SaveFileContent {
|
||||
// reset map when assigning as a quest target that needs enemies
|
||||
deletedObjects.clear();
|
||||
}
|
||||
public boolean isVisited() {
|
||||
if (isVisited ==null)
|
||||
return false;
|
||||
return isVisited;
|
||||
}
|
||||
public void visit() {
|
||||
isVisited = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -577,9 +577,6 @@ public class AdventureDeckEditor extends TabPageScreen<AdventureDeckEditor> {
|
||||
@Override
|
||||
protected void buildMenu() {
|
||||
addItem(new FMenuItem(Forge.getLocalizer().getMessage("btnCopyToClipboard"), Forge.hdbuttons ? FSkinImage.HDEXPORT : FSkinImage.BLANK, e1 -> FDeckViewer.copyDeckToClipboard(getDeck())));
|
||||
addItem(new FMenuItem(Forge.getLocalizer().getMessage("btnCopyCollectionToClipboard"), Forge.hdbuttons ? FSkinImage.HDEXPORT : FSkinImage.BLANK, e1 -> {
|
||||
FDeckViewer.copyCollectionToClipboard(AdventurePlayer.current().getCards());
|
||||
}));
|
||||
if (allowsAddBasic()) {
|
||||
FMenuItem addBasic = new FMenuItem(Forge.getLocalizer().getMessage("lblAddBasicLands"), FSkinImage.LANDLOGO, e1 -> launchBasicLandDialog());
|
||||
addItem(addBasic);
|
||||
@@ -653,7 +650,6 @@ public class AdventureDeckEditor extends TabPageScreen<AdventureDeckEditor> {
|
||||
for (CardEdition e : FModel.getMagicDb().getEditions()) {
|
||||
editionsByName.put(e.getName().toLowerCase(), e);
|
||||
editionsByName.put(e.getName().replace(":", "").toLowerCase(), e);
|
||||
editionsByName.put(e.getName().replace("'", "").toLowerCase(), e);
|
||||
}
|
||||
|
||||
String sketchbookPrefix = "landscape sketchbook - ";
|
||||
@@ -1616,13 +1612,5 @@ public class AdventureDeckEditor extends TabPageScreen<AdventureDeckEditor> {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean keyUp(int keyCode) {
|
||||
if (keyCode == Input.Keys.ESCAPE) {
|
||||
return this.tabHeader.btnBack.trigger();
|
||||
}
|
||||
return super.keyUp(keyCode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,20 +4,15 @@ import com.badlogic.gdx.graphics.Texture;
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
import com.badlogic.gdx.scenes.scene2d.Actor;
|
||||
import com.badlogic.gdx.scenes.scene2d.Group;
|
||||
import com.badlogic.gdx.scenes.scene2d.InputEvent;
|
||||
import com.badlogic.gdx.scenes.scene2d.InputListener;
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Image;
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane;
|
||||
import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable;
|
||||
import com.badlogic.gdx.utils.SnapshotArray;
|
||||
import com.github.tommyettinger.textra.TextraButton;
|
||||
import com.github.tommyettinger.textra.TypingLabel;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import forge.Forge;
|
||||
import forge.adventure.data.AdventureEventData;
|
||||
import forge.adventure.data.AdventureQuestData;
|
||||
import forge.adventure.player.AdventurePlayer;
|
||||
import forge.adventure.pointofintrest.PointOfInterest;
|
||||
import forge.adventure.stage.GameHUD;
|
||||
import forge.adventure.stage.WorldStage;
|
||||
@@ -42,9 +37,6 @@ public class MapViewScene extends UIScene {
|
||||
private int index = -1;
|
||||
private float avatarX = 0, avatarY = 0;
|
||||
private Set<Vector2> positions;
|
||||
private final List<TypingLabel> details;
|
||||
private final float maxZoom = 1.2f;
|
||||
private final float minZoom = 0.25f;
|
||||
private Set<PointOfInterest> bookmark;
|
||||
|
||||
public static MapViewScene instance() {
|
||||
@@ -57,25 +49,7 @@ public class MapViewScene extends UIScene {
|
||||
super(Forge.isLandscapeMode() ? "ui/map.json" : "ui/map_portrait.json");
|
||||
ui.onButtonPress("done", this::done);
|
||||
ui.onButtonPress("quest", this::scroll);
|
||||
//TODO:Add Translations for buttons
|
||||
ui.onButtonPress("details", this::details);
|
||||
ui.onButtonPress("events", this::events);
|
||||
ui.onButtonPress("reputation", this::reputation);
|
||||
ui.onButtonPress("names", this::names);
|
||||
ui.onButtonPress("zoomIn", this::zoomIn);
|
||||
ui.onButtonPress("zoomOut", this::zoomOut);
|
||||
scroll = new ScrollPane(null,Controls.getSkin()) {
|
||||
@Override
|
||||
public void addScrollListener() {
|
||||
return;
|
||||
}
|
||||
};
|
||||
scroll.setName("map");
|
||||
scroll.setActor(Controls.newTextraLabel(""));
|
||||
scroll.setWidth(ui.findActor("map").getWidth());
|
||||
scroll.setHeight(ui.findActor("map").getHeight());
|
||||
ui.addActor(scroll);
|
||||
scroll.setZIndex(1);
|
||||
scroll = ui.findActor("map");
|
||||
labels = Lists.newArrayList();
|
||||
positions = Sets.newHashSet();
|
||||
bookmark = Sets.newHashSet();
|
||||
@@ -86,53 +60,16 @@ public class MapViewScene extends UIScene {
|
||||
img.setPosition(0, 0);
|
||||
table.addActor(img);
|
||||
table.addActor(miniMapPlayer);
|
||||
miniMapPlayer.setZIndex(2);
|
||||
details = Lists.newArrayList();
|
||||
ui.addListener(new InputListener() {
|
||||
public boolean scrolled(InputEvent event, float x, float y, float scrollAmountX, float scrollAmountY) {
|
||||
event.cancel();
|
||||
scroll.setScrollbarsVisible(true);
|
||||
if (scrollAmountY > 0) {
|
||||
zoomOut();
|
||||
return true;
|
||||
} else if (scrollAmountY < 0) {
|
||||
zoomIn();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
stage.setScrollFocus(ui);
|
||||
|
||||
}
|
||||
|
||||
public void test() {
|
||||
img.setPosition((scroll.getScrollPercentX()*2334 +233)*0.1f + 0.9f*img.getX(),(2544-scroll.getScrollPercentY()*2544 +128)*0.1f + 0.9f*img.getY());
|
||||
img.setScale(img.getScaleX()*0.9f);
|
||||
miniMapPlayer.setPosition((scroll.getScrollPercentX()*2334 +233)*0.1f + 0.9f*miniMapPlayer.getX(),(2544-scroll.getScrollPercentY()*2544 +128)*0.1f + 0.9f*miniMapPlayer.getY());
|
||||
miniMapPlayer.setScale(miniMapPlayer.getScaleX()*0.9f);
|
||||
for(Actor actor : table.getChildren()) {
|
||||
if (actor instanceof TypingLabel) {
|
||||
actor.setPosition((scroll.getScrollPercentX() * 2334 + 233) * 0.1f + 0.9f * actor.getX(), (2544 - scroll.getScrollPercentY() * 2544 + 128) * 0.1f + 0.9f * actor.getY());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean done() {
|
||||
GameHUD.getInstance().getTouchpad().setVisible(false);
|
||||
SnapshotArray<Actor> allActors = table.getChildren();
|
||||
for (int i = 0; i < allActors.size; i++) {
|
||||
if (allActors.get(i) instanceof TypingLabel) {
|
||||
allActors.get(i).remove();
|
||||
i--;
|
||||
}
|
||||
for (Actor a : table.getChildren()) {
|
||||
if (a instanceof TypingLabel)
|
||||
a.remove();
|
||||
}
|
||||
labels.clear();
|
||||
positions.clear();
|
||||
details.clear();
|
||||
miniMapPlayer.setScale(1);
|
||||
img.setScale(1);
|
||||
img.setPosition(0,0);
|
||||
index = -1;
|
||||
Forge.switchToLast();
|
||||
return true;
|
||||
@@ -164,134 +101,6 @@ public class MapViewScene extends UIScene {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public void details() {
|
||||
TextraButton detailsButton = ui.findActor("details");
|
||||
if (detailsButton != null) {
|
||||
detailsButton.setVisible(false);
|
||||
detailsButton.setDisabled(true);
|
||||
}
|
||||
TextraButton eventButton = ui.findActor("events");
|
||||
if (eventButton != null) {
|
||||
eventButton.setVisible(true);
|
||||
eventButton.setDisabled(false);
|
||||
}
|
||||
List<PointOfInterest> allPois = Current.world().getAllPointOfInterest();
|
||||
for (PointOfInterest poi : allPois) {
|
||||
for (AdventureEventData data : AdventurePlayer.current().getEvents()) {
|
||||
if (data.sourceID.equals(poi.getID())) {
|
||||
TypingLabel label = Controls.newTypingLabel("[%?BLACKEN] " + data.getCardBlock());
|
||||
table.addActor(label);
|
||||
details.add(label);
|
||||
label.setPosition(img.getScaleX()*(getMapX(poi.getPosition().x) - label.getWidth() / 2) + img.getX(), img.getScaleY()*(getMapY(poi.getPosition().y) - label.getHeight() / 2) + img.getY());
|
||||
label.skipToTheEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void events() {
|
||||
TextraButton eventsButton = ui.findActor("events");
|
||||
if (eventsButton != null) {
|
||||
eventsButton.setVisible(false);
|
||||
eventsButton.setDisabled(true);
|
||||
}
|
||||
TextraButton repButton = ui.findActor("reputation");
|
||||
if (repButton != null) {
|
||||
repButton.setVisible(true);
|
||||
repButton.setDisabled(false);
|
||||
}
|
||||
for (TypingLabel detail : details) {
|
||||
table.removeActor(detail);
|
||||
}
|
||||
List<PointOfInterest> allPois = Current.world().getAllPointOfInterest();
|
||||
details.clear();
|
||||
for (PointOfInterest poi : allPois) {
|
||||
int rep = WorldSave.getCurrentSave().getPointOfInterestChanges(poi.getID()).getMapReputation();
|
||||
if (rep != 0) {
|
||||
TypingLabel label = Controls.newTypingLabel("[%?BLACKEN] " + rep);
|
||||
table.addActor(label);
|
||||
details.add(label);
|
||||
label.setPosition(img.getScaleX()*(getMapX(poi.getPosition().x) - label.getWidth() / 2) + img.getX(), img.getScaleY()*(getMapY(poi.getPosition().y) - label.getHeight() / 2) + img.getY());
|
||||
label.skipToTheEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void reputation() {
|
||||
TextraButton repButton = ui.findActor("reputation");
|
||||
if (repButton != null) {
|
||||
repButton.setVisible(false);
|
||||
repButton.setDisabled(true);
|
||||
}
|
||||
TextraButton namesButton = ui.findActor("names");
|
||||
if (namesButton != null) {
|
||||
namesButton.setVisible(true);
|
||||
namesButton.setDisabled(false);
|
||||
}
|
||||
for (TypingLabel detail : details) {
|
||||
table.removeActor(detail);
|
||||
}
|
||||
details.clear();
|
||||
List<PointOfInterest> allPois = Current.world().getAllPointOfInterest();
|
||||
for (PointOfInterest poi : allPois) {
|
||||
if (WorldSave.getCurrentSave().getPointOfInterestChanges(poi.getID()).isVisited()) {
|
||||
if ("cave".equalsIgnoreCase(poi.getData().type) || "dungeon".equalsIgnoreCase(poi.getData().type) || "castle".equalsIgnoreCase(poi.getData().type)) {
|
||||
TypingLabel label = Controls.newTypingLabel("[%?BLACKEN] " + poi.getDisplayName());
|
||||
table.addActor(label);
|
||||
details.add(label);
|
||||
label.setPosition(img.getScaleX()*(getMapX(poi.getPosition().x) - label.getWidth() / 2) + img.getX(), img.getScaleY()*(getMapY(poi.getPosition().y) - label.getHeight() / 2) + img.getY());
|
||||
label.skipToTheEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void names() {
|
||||
TextraButton namesButton = ui.findActor("names");
|
||||
if (namesButton != null) {
|
||||
namesButton.setVisible(false);
|
||||
namesButton.setDisabled(true);
|
||||
}
|
||||
TextraButton detailsButton = ui.findActor("details");
|
||||
if (detailsButton != null) {
|
||||
detailsButton.setVisible(true);
|
||||
detailsButton.setDisabled(false);
|
||||
}
|
||||
for (TypingLabel detail : details) {
|
||||
table.removeActor(detail);
|
||||
}
|
||||
details.clear();
|
||||
|
||||
}
|
||||
|
||||
public void zoomOut() {
|
||||
if (img.getScaleX()*0.9f > minZoom) {
|
||||
img.setPosition((scroll.getScrollX() + scroll.getWidth()/2) * 0.1f + 0.9f * img.getX(), (scroll.getMaxY() - scroll.getScrollY() + scroll.getHeight()/2) * 0.1f + 0.9f * img.getY());
|
||||
img.setScale(img.getScaleX() * 0.9f);
|
||||
miniMapPlayer.setPosition((scroll.getScrollX() + scroll.getWidth()/2) * 0.1f + 0.9f * miniMapPlayer.getX(), (scroll.getMaxY() - scroll.getScrollY() + scroll.getHeight()/2) * 0.1f + 0.9f * miniMapPlayer.getY());
|
||||
miniMapPlayer.setScale(miniMapPlayer.getScaleX() * 0.9f);
|
||||
for (Actor actor : table.getChildren()) {
|
||||
if (actor instanceof TypingLabel) {
|
||||
actor.setPosition((scroll.getScrollX() + scroll.getWidth()/2) * 0.1f + 0.9f * actor.getX(), (scroll.getMaxY() - scroll.getScrollY() + scroll.getHeight()/2) * 0.1f + 0.9f * actor.getY());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public void zoomIn() {
|
||||
if (img.getScaleX()*1.1f < maxZoom) {
|
||||
img.setPosition(-(scroll.getScrollX() + scroll.getWidth()/2) * 0.1f + 1.1f * img.getX(), -(scroll.getMaxY() - scroll.getScrollY() + scroll.getHeight()/2) * 0.1f + 1.1f * img.getY());
|
||||
img.setScale(img.getScaleX() * 1.1f);
|
||||
miniMapPlayer.setPosition(-(scroll.getScrollX() + scroll.getWidth()/2) * 0.1f + 1.1f * miniMapPlayer.getX(), -(scroll.getMaxY() - scroll.getScrollY() + scroll.getHeight()/2) * 0.1f + 1.1f * miniMapPlayer.getY());
|
||||
miniMapPlayer.setScale(miniMapPlayer.getScaleX() * 1.1f);
|
||||
for (Actor actor : table.getChildren()) {
|
||||
if (actor instanceof TypingLabel) {
|
||||
actor.setPosition(-(scroll.getScrollX() + scroll.getWidth()/2) * 0.1f + 1.1f * actor.getX(), -(scroll.getMaxY() - scroll.getScrollY() + scroll.getHeight()/2) * 0.1f + 1.1f * actor.getY());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enter() {
|
||||
if (miniMapTexture != null)
|
||||
@@ -326,37 +135,6 @@ public class MapViewScene extends UIScene {
|
||||
label.setPosition(getMapX(poi.getPosition().x) - label.getWidth() / 2, getMapY(poi.getPosition().y) - label.getHeight() / 2);
|
||||
label.skipToTheEnd();
|
||||
}
|
||||
|
||||
TextraButton detailsButton = ui.findActor("details");
|
||||
if (detailsButton != null) {
|
||||
detailsButton.setVisible(true);
|
||||
detailsButton.setDisabled(false);
|
||||
}
|
||||
TextraButton eventButton = ui.findActor("events");
|
||||
if (eventButton != null) {
|
||||
eventButton.setVisible(false);
|
||||
eventButton.setDisabled(true);
|
||||
}
|
||||
TextraButton repButton = ui.findActor("reputation");
|
||||
if (repButton != null) {
|
||||
repButton.setVisible(false);
|
||||
repButton.setDisabled(true);
|
||||
}
|
||||
TextraButton namesButton = ui.findActor("names");
|
||||
if (namesButton != null) {
|
||||
namesButton.setVisible(false);
|
||||
namesButton.setDisabled(true);
|
||||
}
|
||||
TextraButton zoomInButton = ui.findActor("zoomIn");
|
||||
if (zoomInButton != null) {
|
||||
zoomInButton.setVisible(true);
|
||||
zoomInButton.setDisabled(false);
|
||||
}
|
||||
TextraButton zoomOutButton = ui.findActor("zoomOut");
|
||||
if (zoomOutButton != null) {
|
||||
zoomOutButton.setVisible(true);
|
||||
zoomOutButton.setDisabled(false);
|
||||
}
|
||||
TextraButton questButton = ui.findActor("quest");
|
||||
if (questButton != null) {
|
||||
questButton.setDisabled(labels.isEmpty());
|
||||
|
||||
@@ -308,7 +308,7 @@ public class MenuScene extends UIScene {
|
||||
}
|
||||
dialog.show(stage, Actions.show());
|
||||
dialog.setPosition((stage.getWidth() - dialog.getWidth()) / 2, (stage.getHeight() - dialog.getHeight()) / 2);
|
||||
if (Forge.hasExternalInput() && !dialogButtonMap.isEmpty())
|
||||
if (Forge.hasGamepad() && !dialogButtonMap.isEmpty())
|
||||
stage.setKeyboardFocus(dialogButtonMap.first());
|
||||
}
|
||||
|
||||
|
||||
@@ -159,15 +159,18 @@ public class RewardScene extends UIScene {
|
||||
boolean done(boolean skipShowLoot) {
|
||||
GameHUD.getInstance().getTouchpad().setVisible(false);
|
||||
if (!skipShowLoot) {
|
||||
doneButton.setText("[+OK]");
|
||||
showLootOrDone();
|
||||
return true;
|
||||
}
|
||||
if (type != null) {
|
||||
switch (type) {
|
||||
case Shop:
|
||||
doneButton.setText("[+OK]");
|
||||
break;
|
||||
case QuestReward:
|
||||
case Loot:
|
||||
doneButton.setText("[+OK]");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -214,6 +217,7 @@ public class RewardScene extends UIScene {
|
||||
@Override
|
||||
public void enter() {
|
||||
autoSell = false;
|
||||
doneButton.setText("[+OK]");
|
||||
updateDetailButton();
|
||||
super.enter();
|
||||
}
|
||||
@@ -248,7 +252,7 @@ public class RewardScene extends UIScene {
|
||||
reward.flip();
|
||||
}
|
||||
}, delay);
|
||||
delay += 0.12f;
|
||||
delay += 0.15f;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -338,8 +342,6 @@ public class RewardScene extends UIScene {
|
||||
this.shopActor = shopActor;
|
||||
this.changes = shopActor.getMapStage().getChanges();
|
||||
addToSelectable(restockButton);
|
||||
} else {
|
||||
doneButton.setText("[+OK]");
|
||||
}
|
||||
for (Actor actor : new Array.ArrayIterator<>(generated)) {
|
||||
actor.remove();
|
||||
@@ -404,6 +406,7 @@ public class RewardScene extends UIScene {
|
||||
|
||||
switch (type) {
|
||||
case Shop:
|
||||
doneButton.setText("[+OK]");
|
||||
String shopName = shopActor.getDescription();
|
||||
if ((shopName != null && !shopName.isEmpty())) {
|
||||
headerLabel.setVisible(true);
|
||||
@@ -422,16 +425,19 @@ public class RewardScene extends UIScene {
|
||||
headerLabel.setVisible(false);
|
||||
headerLabel.setText("");
|
||||
restockButton.setVisible(false);
|
||||
doneButton.setText("[+OK]");
|
||||
break;
|
||||
case Loot:
|
||||
headerLabel.skipToTheEnd();
|
||||
headerLabel.setPosition(restockButton.getX(), restockButton.getY());
|
||||
headerLabel.setVisible(true);
|
||||
headerLabel.setText("[%?SHINY][;]\u2610 " + Forge.getLocalizer().getMessage("lblAll"));
|
||||
headerLabel.skipToTheEnd();
|
||||
restockButton.setVisible(false);
|
||||
doneButton.setText("[+OK]");
|
||||
break;
|
||||
case RewardChoice:
|
||||
restockButton.setVisible(false);
|
||||
doneButton.setText("[+OK]");
|
||||
headerLabel.setVisible(remainingSelections > 0);
|
||||
headerLabel.setText(Forge.getLocalizer().getMessage("lblSelectRewards", remainingSelections));
|
||||
doneButton.setDisabled(remainingSelections > 0);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package forge.adventure.scene;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.Input;
|
||||
import com.badlogic.gdx.controllers.Controller;
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.badlogic.gdx.graphics.g2d.TextureRegion;
|
||||
@@ -243,7 +242,6 @@ public class UIScene extends Scene {
|
||||
public Dialog createGenericDialog(String title, String label, String stringYes, String stringNo, Runnable runnableYes, Runnable runnableNo) {
|
||||
return createGenericDialog(title, label, stringYes, stringNo, runnableYes, runnableNo, false, "");
|
||||
}
|
||||
|
||||
public Dialog createGenericDialog(String title, String label, String stringYes, String stringNo, Runnable runnableYes, Runnable runnableNo, boolean cancelButton, String stringCancel) {
|
||||
Dialog dialog = new Dialog(title == null ? "" : title, Controls.getSkin());
|
||||
textboxOpen = true;
|
||||
@@ -342,15 +340,16 @@ public class UIScene extends Scene {
|
||||
}
|
||||
|
||||
public boolean keyPressed(int keycode) {
|
||||
ui.pressDown(keycode);
|
||||
|
||||
Selectable selection = getSelected();
|
||||
|
||||
if (KeyBinding.Use.isPressed(keycode)) {
|
||||
if (selection != null) {
|
||||
selection.onPressDown(this);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
ui.pressDown(keycode);
|
||||
if (stage.getKeyboardFocus() instanceof SelectBox) {
|
||||
SelectBox box = (SelectBox) stage.getKeyboardFocus();
|
||||
if (box.getScrollPane().hasParent()) {
|
||||
@@ -380,22 +379,17 @@ public class UIScene extends Scene {
|
||||
scroll.setScrollY(scroll.getScrollY() + 20);
|
||||
}
|
||||
}
|
||||
if (!textboxOpen) {
|
||||
//Allow letter S for TextField since this is binded on down keys
|
||||
if (KeyBinding.Down.isPressed(keycode, !(stage.getKeyboardFocus() instanceof TextField))
|
||||
|| KeyBinding.Down.isPressed(keycode, Input.Keys.S != keycode))
|
||||
if(!textboxOpen){
|
||||
if (KeyBinding.Down.isPressed(keycode))
|
||||
selectNextDown();
|
||||
//Allow letter W for TextField since this is binded on up keys
|
||||
if (KeyBinding.Up.isPressed(keycode, !(stage.getKeyboardFocus() instanceof TextField))
|
||||
|| KeyBinding.Up.isPressed(keycode, Input.Keys.W != keycode))
|
||||
if (KeyBinding.Up.isPressed(keycode))
|
||||
selectNextUp();
|
||||
// Allow Right & Left keybinds if not Selector, Slider or Textfield
|
||||
if (KeyBinding.Right.isPressed(keycode, !(stage.getKeyboardFocus() instanceof Selector)
|
||||
&& !(stage.getKeyboardFocus() instanceof TextField) && !(stage.getKeyboardFocus() instanceof Slider)))
|
||||
selectNextRight();
|
||||
if (KeyBinding.Left.isPressed(keycode, !(stage.getKeyboardFocus() instanceof Selector)
|
||||
&& !(stage.getKeyboardFocus() instanceof TextField) && !(stage.getKeyboardFocus() instanceof Slider)))
|
||||
selectNextLeft();
|
||||
if (!(stage.getKeyboardFocus() instanceof Selector) && !(stage.getKeyboardFocus() instanceof TextField) && !(stage.getKeyboardFocus() instanceof Slider)) {
|
||||
if (KeyBinding.Right.isPressed(keycode))
|
||||
selectNextRight();
|
||||
if (KeyBinding.Left.isPressed(keycode))
|
||||
selectNextLeft();
|
||||
}
|
||||
}
|
||||
if (!dialogShowing()) {
|
||||
Button pressedButton = ui.buttonPressed(keycode);
|
||||
|
||||
@@ -75,10 +75,6 @@ public class Console extends Window {
|
||||
}
|
||||
|
||||
public void command(String text) {
|
||||
if (text.equalsIgnoreCase("exit")) {
|
||||
toggle();
|
||||
return;
|
||||
}
|
||||
Cell<Label> newLine=content.add(text);
|
||||
newLine.getActor().setColor(1,1,1,1);
|
||||
newLine.growX().align(Align.left|Align.bottom).row();
|
||||
|
||||
@@ -786,7 +786,6 @@ public class GameHUD extends Stage {
|
||||
console.toggle();
|
||||
if (console.isVisible()) {
|
||||
clearAbility();
|
||||
console.setZIndex(ui.getChildren().size);
|
||||
} else {
|
||||
updateAbility();
|
||||
}
|
||||
@@ -795,12 +794,6 @@ public class GameHUD extends Stage {
|
||||
@Override
|
||||
public boolean keyUp(int keycode) {
|
||||
ui.pressUp(keycode);
|
||||
|
||||
Button pressedButton = ui.buttonPressed(keycode);
|
||||
if (pressedButton != null) {
|
||||
pressedButton.fire(eventTouchUp);
|
||||
}
|
||||
|
||||
return super.keyUp(keycode);
|
||||
}
|
||||
|
||||
@@ -814,17 +807,16 @@ public class GameHUD extends Stage {
|
||||
toggleConsole();
|
||||
return true;
|
||||
}
|
||||
if (KeyBinding.Back.isPressed(keycode)) {
|
||||
if (keycode == Input.Keys.BACK) {
|
||||
if (console.isVisible()) {
|
||||
toggleConsole();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (console.isVisible())
|
||||
return true;
|
||||
Button pressedButton = ui.buttonPressed(keycode);
|
||||
if (pressedButton != null) {
|
||||
pressedButton.fire(eventTouchDown);
|
||||
performTouch(pressedButton);
|
||||
}
|
||||
return super.keyDown(keycode);
|
||||
}
|
||||
@@ -885,7 +877,7 @@ public class GameHUD extends Stage {
|
||||
dialogOnlyInput = true;
|
||||
gameStage.hudIsShowingDialog(true);
|
||||
MapStage.getInstance().hudIsShowingDialog(true);
|
||||
if (Forge.hasExternalInput() && !dialogButtonMap.isEmpty())
|
||||
if (Forge.hasGamepad() && !dialogButtonMap.isEmpty())
|
||||
this.setKeyboardFocus(dialogButtonMap.first());
|
||||
}
|
||||
|
||||
|
||||
@@ -110,7 +110,7 @@ public abstract class GameStage extends Stage {
|
||||
dialog.setPosition((dialogStage.getWidth() - dialog.getWidth()) / 2, (dialogStage.getHeight() - dialog.getHeight()) / 2);
|
||||
dialogOnlyInput = true;
|
||||
|
||||
if (Forge.hasExternalInput() && !dialogButtonMap.isEmpty())
|
||||
if (Forge.hasGamepad() && !dialogButtonMap.isEmpty())
|
||||
dialogStage.setKeyboardFocus(dialogButtonMap.first());
|
||||
}
|
||||
|
||||
@@ -577,6 +577,9 @@ public abstract class GameStage extends Stage {
|
||||
if (!player.isMoving())
|
||||
stop();
|
||||
}
|
||||
if (KeyBinding.Menu.isPressed(keycode)) {
|
||||
openMenu();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -1132,10 +1132,8 @@ public class MapStage extends GameStage {
|
||||
dialog.show(dialogStage, Actions.show());
|
||||
dialog.setPosition((dialogStage.getWidth() - dialog.getWidth()) / 2, (dialogStage.getHeight() - dialog.getHeight()) / 2);
|
||||
dialogOnlyInput = true;
|
||||
|
||||
if (Forge.hasExternalInput() && !dialogButtonMap.isEmpty()) {
|
||||
if (Forge.hasGamepad() && !dialogButtonMap.isEmpty())
|
||||
dialogStage.setKeyboardFocus(dialogButtonMap.first());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -216,7 +216,6 @@ public class WorldStage extends GameStage implements SaveFileContent {
|
||||
WorldSave.getCurrentSave().autoSave();
|
||||
loadPOI(point.getPointOfInterest());
|
||||
point.getMapSprite().checkOut();
|
||||
WorldSave.getCurrentSave().getPointOfInterestChanges(point.getPointOfInterest().getID()).visit();
|
||||
return true;
|
||||
} else {
|
||||
if (point == collidingPoint) {
|
||||
@@ -366,7 +365,7 @@ public class WorldStage extends GameStage implements SaveFileContent {
|
||||
super.draw();
|
||||
if (WorldSave.getCurrentSave().getPlayer().hasAnnounceFantasy()) {
|
||||
MapStage.getInstance().showDeckAwardDialog("{BLINK=WHITE;RED}Chaos Mode!{ENDBLINK}\n" +
|
||||
"Enemy will use Preconstructed or Random Generated Decks. Genetic AI Decks will be available to some enemies on Hard difficulty.",
|
||||
"Enemy will use Preconstructed or Random Generated Decks. Genetic AI Decks will be available to some enemies on Hard difficulty.",
|
||||
WorldSave.getCurrentSave().getPlayer().getSelectedDeck());
|
||||
WorldSave.getCurrentSave().getPlayer().clearAnnounceFantasy();
|
||||
} else if (WorldSave.getCurrentSave().getPlayer().hasAnnounceCustom()) {
|
||||
|
||||
@@ -48,12 +48,6 @@ public class Controls {
|
||||
this.setWidth(this.layout.getWidth() + (this.style != null && this.style.background != null ? this.style.background.getLeftWidth() + this.style.background.getRightWidth() : 0.0F));
|
||||
layout();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Batch batch, float parentAlpha) {
|
||||
batch.setColor(1f, 1f, 1f, 1f); // Set color before drawing each actor, per libGDX docs
|
||||
super.draw(batch, parentAlpha);
|
||||
}
|
||||
}
|
||||
|
||||
static class TextButtonFix extends TextraButton {
|
||||
@@ -512,11 +506,10 @@ public class Controls {
|
||||
private String currencyIcon;
|
||||
private boolean isShards;
|
||||
private int currencyAmount;
|
||||
private float intermediateDuration = 2f; // Seconds to wait before replacing intermediate label
|
||||
private float animationDelay = 2f; //seconds to wait before replacing intermediate label
|
||||
private final String NEGDECOR = "[RED]-";
|
||||
private final String POSDECOR = "[GREEN]+";
|
||||
private final Timer t = new Timer();
|
||||
private boolean isInitializing = true;
|
||||
|
||||
public AccountingLabel(TextraLabel target, boolean isShards) {
|
||||
target.setVisible(false);
|
||||
@@ -528,72 +521,50 @@ public class Controls {
|
||||
if (isShards) {
|
||||
currencyAmount = Current.player().getShards();
|
||||
currencyIcon = "[+Shards]";
|
||||
Current.player().onShardsChange(() -> {
|
||||
if (!isInitializing) { // Avoid unwanted call to update() during scene initialization, triggering animation
|
||||
update(AdventurePlayer.current().getShards(), true);
|
||||
}
|
||||
});
|
||||
Current.player().onShardsChange(() -> update(AdventurePlayer.current().getShards(), true));
|
||||
} else {
|
||||
currencyAmount = Current.player().getGold();
|
||||
currencyIcon = "[+Gold] "; //fix space since gold sprite is wider than a single glyph
|
||||
Current.player().onGoldChange(() -> {
|
||||
if (!isInitializing) { // Avoid unwanted call to update() during scene initialization, triggering animation
|
||||
update(AdventurePlayer.current().getGold(), true);
|
||||
}
|
||||
});
|
||||
Current.player().onGoldChange(() -> update(AdventurePlayer.current().getGold(), true));
|
||||
}
|
||||
label.setText(getLabelText(currencyAmount));
|
||||
setName(label.getName());
|
||||
replaceLabel(label);
|
||||
|
||||
isInitializing = false; // Initialization complete
|
||||
}
|
||||
|
||||
public void setIntermediateDuration(float intermediateDuration) {
|
||||
this.intermediateDuration = intermediateDuration;
|
||||
public void setAnimationDelay(float animationDelay) {
|
||||
this.animationDelay = animationDelay;
|
||||
}
|
||||
|
||||
public float getIntermediateDuration() {
|
||||
return intermediateDuration;
|
||||
public float getAnimationDelay() {
|
||||
return animationDelay;
|
||||
}
|
||||
|
||||
public void update(int newAmount) {
|
||||
update(newAmount, false);
|
||||
}
|
||||
|
||||
public void update(int newAmount, boolean animateIntermediate) {
|
||||
public void update(int newAmount, boolean animate) {
|
||||
|
||||
if (animateIntermediate) {
|
||||
if (animate) {
|
||||
TextraLabel temporaryLabel = getUpdateLabel(newAmount);
|
||||
currencyAmount = newAmount;
|
||||
replaceLabel(temporaryLabel);
|
||||
|
||||
// Add a quick 'bump' animation to the temporary label
|
||||
SequenceAction sequence = new SequenceAction();
|
||||
sequence.addAction(Actions.alpha(0.25f));
|
||||
sequence.addAction(Actions.parallel(
|
||||
Actions.alpha(1f, 0.05f, Interpolation.pow2Out),
|
||||
Actions.moveBy(0f, 2f, 0.05f, Interpolation.pow2Out)
|
||||
));
|
||||
sequence.addAction(Actions.moveBy(0f, -2f, 0.05f, Interpolation.pow2Out));
|
||||
|
||||
temporaryLabel.addAction(sequence);
|
||||
|
||||
t.schedule(new AccountingLabelUpdater(temporaryLabel), intermediateDuration);
|
||||
t.schedule(new AccountingLabelUpdater(temporaryLabel), animationDelay);
|
||||
} else {
|
||||
currencyAmount = newAmount;
|
||||
drawFinalLabel(true); // Draw final label with animation since the intermediate label was not used.
|
||||
drawFinalLabel(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void drawFinalLabel(boolean animateFinal) {
|
||||
private void drawFinalLabel(boolean fadeIn) {
|
||||
|
||||
TextraLabel finalLabel = getDefaultLabel();
|
||||
if (animateFinal) {
|
||||
// Add a quick fade-in animation to the final label
|
||||
if (fadeIn) {
|
||||
SequenceAction sequence = new SequenceAction();
|
||||
sequence.addAction(Actions.alpha(0.25f));
|
||||
sequence.addAction(Actions.alpha(1f, 0.1f, Interpolation.pow2Out));
|
||||
sequence.addAction(Actions.alpha(0.5f));
|
||||
sequence.addAction(Actions.alpha(1f, 2f, Interpolation.pow2Out));
|
||||
finalLabel.addAction(sequence);
|
||||
}
|
||||
replaceLabel(finalLabel);
|
||||
@@ -606,7 +577,7 @@ public class Controls {
|
||||
private TextraLabel getUpdateLabel(int newAmount) {
|
||||
int delta = newAmount - currencyAmount;
|
||||
String updateText = delta == 0 ? "" : (delta < 0 ? NEGDECOR + delta * -1 : POSDECOR + delta);
|
||||
return Controls.newTextraLabel(getLabelText(newAmount, updateText));
|
||||
return Controls.newTextraLabel(getLabelText(currencyAmount, updateText));
|
||||
}
|
||||
|
||||
private String getLabelText(int amount) {
|
||||
@@ -629,15 +600,13 @@ public class Controls {
|
||||
label.remove();
|
||||
label = newLabel;
|
||||
placeholder.getStage().addActor(label);
|
||||
|
||||
label.setZIndex(placeholder.getZIndex() - 1); // Ensure the new label is behind any tooltips that were present
|
||||
}
|
||||
|
||||
private class AccountingLabelUpdater extends Timer.Task {
|
||||
@Override
|
||||
public void run() {
|
||||
if (label.equals(target)) {
|
||||
drawFinalLabel(false); // Passing false to avoid final animation since the intermediate label was animated
|
||||
drawFinalLabel(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,80 +8,82 @@ import forge.gui.GuiBase;
|
||||
|
||||
// The standard button has index 0, controller binding is 1. Others can be added if needed.
|
||||
public enum KeyBinding {
|
||||
Left("Left", new int[]{Input.Keys.LEFT, Input.Keys.DPAD_LEFT, Input.Keys.A}),
|
||||
Up("Up", new int[]{Input.Keys.UP, Input.Keys.DPAD_UP, Input.Keys.W}),
|
||||
Right("Right", new int[]{Input.Keys.RIGHT, Input.Keys.DPAD_RIGHT, Input.Keys.D}),
|
||||
Down("Down", new int[]{Input.Keys.DOWN, Input.Keys.DPAD_DOWN, Input.Keys.S}),
|
||||
Menu("Menu", new int[]{Input.Keys.ESCAPE, Input.Keys.BUTTON_START}),
|
||||
Inventory("Inventory", new int[]{Input.Keys.I, Input.Keys.BUTTON_X}),
|
||||
Status("Status", new int[]{Input.Keys.Q, Input.Keys.BUTTON_Y}),
|
||||
Deck("Deck", new int[]{Input.Keys.E, Input.Keys.BUTTON_A}),
|
||||
Map("Map", new int[]{Input.Keys.M, Input.Keys.BUTTON_SELECT}),
|
||||
Equip("Equip", new int[]{Input.Keys.E, Input.Keys.BUTTON_X}),
|
||||
ExitToWorldMap("ExitToWorldMap", new int[]{Input.Keys.F4, Input.Keys.BUTTON_L2}),
|
||||
Bookmark("Bookmark", new int[]{Input.Keys.B, Input.Keys.BUTTON_R2}),
|
||||
Use("Use", new int[]{Input.Keys.ENTER, Input.Keys.BUTTON_A}),
|
||||
Back("Back", new int[]{Input.Keys.ESCAPE, Input.Keys.BUTTON_B, Input.Keys.BACK}),
|
||||
ScrollUp("ScrollUp", new int[]{Input.Keys.PAGE_UP, Input.Keys.BUTTON_L1}),
|
||||
ScrollDown("ScrollDown", new int[]{Input.Keys.PAGE_DOWN, Input.Keys.BUTTON_R1}),
|
||||
Left("Left", new int[]{Input.Keys.LEFT,Input.Keys.DPAD_LEFT, Input.Keys.A}),
|
||||
Up("Up", new int[] {Input.Keys.UP,Input.Keys.DPAD_UP, Input.Keys.W}),
|
||||
Right("Right", new int[] {Input.Keys.RIGHT,Input.Keys.DPAD_RIGHT, Input.Keys.D}),
|
||||
Down("Down", new int[] {Input.Keys.DOWN,Input.Keys.DPAD_DOWN, Input.Keys.S}),
|
||||
Menu("Menu", new int[] {Input.Keys.ESCAPE,Input.Keys.BUTTON_START}),
|
||||
Inventory("Inventory", new int[] {Input.Keys.I,Input.Keys.BUTTON_X}),
|
||||
Status("Status", new int[] {Input.Keys.Q,Input.Keys.BUTTON_Y}),
|
||||
Deck("Deck", new int[] {Input.Keys.E,Input.Keys.BUTTON_A}),
|
||||
Map("Map", new int[] {Input.Keys.M,Input.Keys.BUTTON_SELECT}),
|
||||
Equip("Equip", new int[] {Input.Keys.E,Input.Keys.BUTTON_X}),
|
||||
ExitToWorldMap("ExitToWorldMap",new int[] {Input.Keys.F4,Input.Keys.BUTTON_L2}),
|
||||
Bookmark("Bookmark",new int[] {Input.Keys.B, Input.Keys.BUTTON_R2}),
|
||||
Use("Use", new int[] {Input.Keys.ENTER,Input.Keys.BUTTON_A}),
|
||||
Back("Back", new int[] {Input.Keys.ESCAPE,Input.Keys.BUTTON_B}),
|
||||
ScrollUp("ScrollUp", new int[] {Input.Keys.PAGE_UP,Input.Keys.BUTTON_L1}),
|
||||
ScrollDown("ScrollDown", new int[] {Input.Keys.PAGE_DOWN,Input.Keys.BUTTON_R1}),
|
||||
;
|
||||
final String name;
|
||||
final int[] bindings;
|
||||
String name;
|
||||
int [] bindings;
|
||||
|
||||
KeyBinding(String name, int[] bindings) {
|
||||
this.name = name;
|
||||
KeyBinding(String name, int [] bindings)
|
||||
{
|
||||
this.name=name;
|
||||
this.bindings = bindings;
|
||||
}
|
||||
|
||||
public boolean isPressed(int key) {
|
||||
return isPressed(key, null);
|
||||
}
|
||||
|
||||
public boolean isPressed(int key, Boolean requiredCondition) {
|
||||
if (requiredCondition == null || requiredCondition) {
|
||||
for (int i = 0; i < bindings.length; i++) {
|
||||
if (key == bindings[i]) {
|
||||
return true;
|
||||
}
|
||||
public boolean isPressed(int key)
|
||||
{
|
||||
for(int i = 0; i < bindings.length; i++){
|
||||
if(key == bindings[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// The controller binding always has index 1.
|
||||
final static String controllerPrefix = "XBox_";
|
||||
|
||||
static String controllerPrefix="XBox_";
|
||||
public String getLabelText(boolean pressed) {
|
||||
if (Controllers.getCurrent() != null) {
|
||||
return "[%120][+" + controllerPrefix + Input.Keys.toString(bindings[1]).replace(" Button", "") + (pressed ? "_pressed]" : "]");
|
||||
} else {
|
||||
if (GuiBase.isAndroid())
|
||||
if(Controllers.getCurrent()!=null)
|
||||
{
|
||||
return "[%120][+"+controllerPrefix+Input.Keys.toString(bindings[1]).replace(" Button","")+(pressed?"_pressed]":"]");
|
||||
}
|
||||
else
|
||||
{
|
||||
if(GuiBase.isAndroid())
|
||||
return "";
|
||||
return "[%120][+" + Input.Keys.toString(bindings[0]) + (pressed ? "_pressed]" : "]");
|
||||
return "[%120][+"+Input.Keys.toString(bindings[0])+(pressed?"_pressed]":"]");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static int controllerButtonToKey(Controller controller, int key) {
|
||||
ControllerMapping map = controller.getMapping();
|
||||
if (key == map.buttonA) return Input.Keys.BUTTON_A;
|
||||
if (key == map.buttonB) return Input.Keys.BUTTON_B;
|
||||
if (key == map.buttonX) return Input.Keys.BUTTON_X;
|
||||
if (key == map.buttonY) return Input.Keys.BUTTON_Y;
|
||||
public static int controllerButtonToKey(Controller controller,int key)
|
||||
{
|
||||
ControllerMapping map=controller.getMapping();
|
||||
if(key==map.buttonA) return Input.Keys.BUTTON_A;
|
||||
if(key==map.buttonB) return Input.Keys.BUTTON_B;
|
||||
if(key==map.buttonX) return Input.Keys.BUTTON_X;
|
||||
if(key==map.buttonY) return Input.Keys.BUTTON_Y;
|
||||
|
||||
if (key == map.buttonBack) return Input.Keys.BUTTON_SELECT;
|
||||
if (key == map.buttonStart) return Input.Keys.BUTTON_START;
|
||||
if (key == map.buttonL1) return Input.Keys.BUTTON_L1;
|
||||
if (key == map.buttonL2) return Input.Keys.BUTTON_L2;
|
||||
if (key == map.buttonR1) return Input.Keys.BUTTON_R1;
|
||||
if (key == map.buttonR2) return Input.Keys.BUTTON_R2;
|
||||
if (key == map.buttonDpadUp) return Input.Keys.DPAD_UP;
|
||||
if (key == map.buttonDpadDown) return Input.Keys.DPAD_DOWN;
|
||||
if (key == map.buttonDpadLeft) return Input.Keys.DPAD_LEFT;
|
||||
if (key == map.buttonDpadRight) return Input.Keys.DPAD_RIGHT;
|
||||
if (key == map.buttonLeftStick) return Input.Keys.BUTTON_THUMBL;
|
||||
if (key == map.buttonRightStick) return Input.Keys.BUTTON_THUMBR;
|
||||
if(key==map.buttonBack) return Input.Keys.BUTTON_SELECT;
|
||||
if(key==map.buttonStart) return Input.Keys.BUTTON_START;
|
||||
if(key==map.buttonL1) return Input.Keys.BUTTON_L1;
|
||||
if(key==map.buttonL2) return Input.Keys.BUTTON_L2;
|
||||
if(key==map.buttonR1) return Input.Keys.BUTTON_R1;
|
||||
if(key==map.buttonR2) return Input.Keys.BUTTON_R2;
|
||||
if(key==map.buttonDpadUp) return Input.Keys.DPAD_UP;
|
||||
if(key==map.buttonDpadDown) return Input.Keys.DPAD_DOWN;
|
||||
if(key==map.buttonDpadLeft) return Input.Keys.DPAD_LEFT;
|
||||
if(key==map.buttonDpadRight) return Input.Keys.DPAD_RIGHT;
|
||||
if(key==map.buttonLeftStick) return Input.Keys.BUTTON_THUMBL;
|
||||
if(key==map.buttonRightStick) return Input.Keys.BUTTON_THUMBR;
|
||||
|
||||
if(key==map.buttonDpadUp) return Input.Keys.DPAD_UP;
|
||||
if(key==map.buttonDpadDown) return Input.Keys.DPAD_DOWN;
|
||||
if(key==map.buttonDpadLeft) return Input.Keys.DPAD_LEFT;
|
||||
if(key==map.buttonDpadRight) return Input.Keys.DPAD_RIGHT;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,12 +14,9 @@ import com.badlogic.gdx.math.Vector3;
|
||||
import com.badlogic.gdx.scenes.scene2d.Actor;
|
||||
import com.badlogic.gdx.scenes.scene2d.Group;
|
||||
import com.badlogic.gdx.scenes.scene2d.InputEvent;
|
||||
import com.badlogic.gdx.scenes.scene2d.actions.Actions;
|
||||
import com.badlogic.gdx.scenes.scene2d.InputListener;
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Image;
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Label;
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Tooltip;
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.TooltipManager;
|
||||
import com.badlogic.gdx.scenes.scene2d.utils.ActorGestureListener;
|
||||
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
|
||||
import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable;
|
||||
@@ -123,6 +120,8 @@ public class RewardActor extends Actor implements Disposable, ImageFetcher.Callb
|
||||
public void onImageFetched() {
|
||||
ImageCache.getInstance().clear();
|
||||
|
||||
|
||||
|
||||
if(reward.type.equals(Reward.Type.Card)) {
|
||||
imageKey = reward.getCard().getImageKey(false);
|
||||
PaperCard card = ImageUtil.getPaperCardFromImageKey(imageKey);
|
||||
@@ -223,7 +222,6 @@ public class RewardActor extends Actor implements Disposable, ImageFetcher.Callb
|
||||
reward.setAutoSell(!reward.isAutoSell());
|
||||
String c = reward.isAutoSell() ? "[%85][GREEN]" : "[%85][GRAY]";
|
||||
autoSell.setText(c + "\uFF04");
|
||||
autoSell.getColor().a = reward.isAutoSell() ? 1f : 0.7f;
|
||||
}
|
||||
|
||||
public RewardActor(Reward reward, boolean flippable, RewardScene.Type type, boolean showOverlay) {
|
||||
@@ -239,17 +237,6 @@ public class RewardActor extends Actor implements Disposable, ImageFetcher.Callb
|
||||
case Card: {
|
||||
if (!reward.isNoSell) {
|
||||
autoSell = Controls.newTextButton("[%85][GRAY]\uFF04");
|
||||
autoSell.getColor().a = 0.7f; // semi-transparent by default
|
||||
autoSell.addListener(new InputListener() {
|
||||
@Override
|
||||
public void enter(InputEvent event, float x, float y, int pointer, Actor fromActor) {
|
||||
if (!reward.isAutoSell()) autoSell.getColor().a = 1f;
|
||||
}
|
||||
@Override
|
||||
public void exit(InputEvent event, float x, float y, int pointer, Actor toActor) {
|
||||
if (!reward.isAutoSell()) autoSell.getColor().a = 0.7f;
|
||||
}
|
||||
});
|
||||
float scale = autoSell.getWidth();
|
||||
autoSell.setSize(scale, scale * 1.2f);
|
||||
autoSell.addListener(new ClickListener() {
|
||||
@@ -582,8 +569,8 @@ public class RewardActor extends Actor implements Disposable, ImageFetcher.Callb
|
||||
TextureRegionDrawable drawable = new TextureRegionDrawable(ImageCache.getInstance().croppedBorderImage(texture));
|
||||
float origW = texture.getWidth();
|
||||
float origH = texture.getHeight();
|
||||
float boundW = GuiBase.isAndroid() ? Scene.getIntendedWidth() * 0.95f : Scene.getIntendedWidth() * 0.7f; // Use smaller size for Desktop
|
||||
float boundH = GuiBase.isAndroid() ? Scene.getIntendedHeight() * 0.95f : Scene.getIntendedHeight() * 0.7f; // Use smaller size for Desktop
|
||||
float boundW = Scene.getIntendedWidth() * 0.95f;
|
||||
float boundH = Scene.getIntendedHeight() * 0.95f;
|
||||
float newW = origW;
|
||||
float newH = origH;
|
||||
if (origW > boundW) {
|
||||
@@ -861,7 +848,7 @@ public class RewardActor extends Actor implements Disposable, ImageFetcher.Callb
|
||||
super.act(delta);
|
||||
if (clicked) {
|
||||
if (flipProcess < 1)
|
||||
flipProcess += delta * 4;
|
||||
flipProcess += delta * 2.4;
|
||||
else
|
||||
flipProcess = 1;
|
||||
|
||||
@@ -1127,17 +1114,11 @@ public class RewardActor extends Actor implements Disposable, ImageFetcher.Callb
|
||||
public TextraLabel getStoredLabel() {
|
||||
return cLabel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Batch batch, float parentAlpha) {
|
||||
batch.setColor(1f, 1f, 1f, 1f); // Set color before drawing each actor, per libGDX docs
|
||||
super.draw(batch, parentAlpha);
|
||||
}
|
||||
}
|
||||
|
||||
class ImageToolTip extends Tooltip<ComplexTooltip> {
|
||||
public ImageToolTip(ComplexTooltip contents) {
|
||||
super(contents, RewardTooltipManager.getInstance());
|
||||
super(contents);
|
||||
}
|
||||
|
||||
public Image getImage() {
|
||||
@@ -1277,30 +1258,4 @@ public class RewardActor extends Actor implements Disposable, ImageFetcher.Callb
|
||||
super.draw(batch, parentAlpha);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend and override TooltipManager to avoid the built-in default animations.
|
||||
*/
|
||||
static class RewardTooltipManager extends TooltipManager {
|
||||
private static RewardTooltipManager instance;
|
||||
|
||||
public static RewardTooltipManager getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new RewardTooltipManager();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void showAction(Tooltip tooltip) {
|
||||
// Overriding showAction for instant tooltip display
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void hideAction(Tooltip tooltip) {
|
||||
tooltip.getContainer().addAction(Actions.sequence(
|
||||
Actions.removeActor() // Remove tooltip without animation
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,7 +295,7 @@ public class ImageCache {
|
||||
image fetcher to update automatically after the card image/s are downloaded*/
|
||||
imageLoaded = false;
|
||||
if (image != null && imageRecord.get().get(image.toString()) == null)
|
||||
imageRecord.get().put(image.toString(), new ImageRecord(Color.valueOf("#171717").toString(), false, getRadius(image), image.toString().contains(".fullborder.") || image.toString().contains("tokens"))); //black border
|
||||
imageRecord.get().put(image.toString(), new ImageRecord(Color.valueOf("#171717").toString(), false, getRadius(image))); //black border
|
||||
}
|
||||
}
|
||||
return image;
|
||||
@@ -349,7 +349,7 @@ public class ImageCache {
|
||||
radius = 22;
|
||||
updateImageRecord(cardTexture.toString(),
|
||||
borderless ? Color.valueOf("#171717").toString() : isCloserToWhite(getpixelColor(cardTexture)).getLeft(),
|
||||
!borderless && isCloserToWhite(getpixelColor(cardTexture)).getRight(), radius, cardTexture.toString().contains(".fullborder.") || cardTexture.toString().contains("tokens"));
|
||||
!borderless && isCloserToWhite(getpixelColor(cardTexture)).getRight(), radius);
|
||||
}
|
||||
return cardTexture;
|
||||
}
|
||||
@@ -444,8 +444,8 @@ public class ImageCache {
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
public void updateImageRecord(String textureString, String colorValue, Boolean isClosertoWhite, int radius, boolean fullborder) {
|
||||
imageRecord.get().put(textureString, new ImageRecord(colorValue, isClosertoWhite, radius, fullborder));
|
||||
public void updateImageRecord(String textureString, String colorValue, Boolean isClosertoWhite, int radius) {
|
||||
imageRecord.get().put(textureString, new ImageRecord(colorValue, isClosertoWhite, radius));
|
||||
}
|
||||
|
||||
public int getRadius(Texture t) {
|
||||
@@ -460,15 +460,6 @@ public class ImageCache {
|
||||
return i;
|
||||
}
|
||||
|
||||
public boolean isFullBorder(Texture image) {
|
||||
if (image == null)
|
||||
return false;
|
||||
ImageRecord record = imageRecord.get().get(image.toString());
|
||||
if (record == null)
|
||||
return false;
|
||||
return record.isFullBorder;
|
||||
}
|
||||
|
||||
public FImage getBorder(String textureString) {
|
||||
ImageRecord record = imageRecord.get().get(textureString);
|
||||
if (record == null)
|
||||
@@ -553,13 +544,11 @@ public class ImageCache {
|
||||
String colorValue;
|
||||
Boolean isCloserToWhite;
|
||||
Integer cardRadius;
|
||||
boolean isFullBorder;
|
||||
|
||||
ImageRecord(String colorString, Boolean closetoWhite, int radius, boolean fullborder) {
|
||||
ImageRecord(String colorString, Boolean closetoWhite, int radius) {
|
||||
colorValue = colorString;
|
||||
isCloserToWhite = closetoWhite;
|
||||
cardRadius = radius;
|
||||
isFullBorder = fullborder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,22 +34,21 @@ public class CardImage implements FImage {
|
||||
|
||||
@Override
|
||||
public void draw(Graphics g, float x, float y, float w, float h) {
|
||||
CardView cv = CardView.getCardForUi(card);
|
||||
if (image == null) { //attempt to retrieve card image if needed
|
||||
image = ImageCache.getInstance().getImage(card);
|
||||
if (image == null) {
|
||||
if (!Forge.enableUIMask.equals("Off")) //render this if mask is still loading
|
||||
CardImageRenderer.drawCardImage(g, cv, false, x, y, w, h, CardStackPosition.Top, Forge.enableUIMask.equals("Art"), true);
|
||||
CardImageRenderer.drawCardImage(g, CardView.getCardForUi(card), false, x, y, w, h, CardStackPosition.Top, Forge.enableUIMask.equals("Art"), true);
|
||||
|
||||
return; //can't draw anything if can't be loaded yet
|
||||
}
|
||||
}
|
||||
|
||||
if (image == ImageCache.getInstance().getDefaultImage() || Forge.enableUIMask.equals("Art")) {
|
||||
CardImageRenderer.drawCardImage(g, cv, false, x, y, w, h, CardStackPosition.Top, true, true);
|
||||
CardImageRenderer.drawCardImage(g, CardView.getCardForUi(card), false, x, y, w, h, CardStackPosition.Top, true, true);
|
||||
} else {
|
||||
if (Forge.enableUIMask.equals("Full")) {
|
||||
if (ImageCache.getInstance().isFullBorder(image))
|
||||
if (image.toString().contains(".fullborder."))
|
||||
g.drawCardRoundRect(image, null, x, y, w, h, false, false);
|
||||
else {
|
||||
float radius = (h - w) / 8;
|
||||
|
||||
@@ -814,7 +814,7 @@ public class CardImageRenderer {
|
||||
}
|
||||
if (rotatePlane && (card.getCurrentState().isPhenomenon() || card.getCurrentState().isPlane() || (card.getCurrentState().isBattle() && !altState) || (card.getAlternateState() != null && card.getAlternateState().isBattle() && altState))) {
|
||||
if (Forge.enableUIMask.equals("Full")) {
|
||||
if (ImageCache.getInstance().isFullBorder(image))
|
||||
if (image.toString().contains(".fullborder."))
|
||||
g.drawCardRoundRect(image, new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, -90);
|
||||
else {
|
||||
g.drawRotatedImage(FSkin.getBorders().get(0), new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, -90);
|
||||
@@ -827,7 +827,7 @@ public class CardImageRenderer {
|
||||
} else if (rotateSplit && isCurrentCard && card.isSplitCard() && canshow && !card.isFaceDown()) {
|
||||
boolean isAftermath = card.getText().contains("Aftermath") || card.getAlternateState().getOracleText().contains("Aftermath");
|
||||
if (Forge.enableUIMask.equals("Full")) {
|
||||
if (ImageCache.getInstance().isFullBorder(image))
|
||||
if (image.toString().contains(".fullborder."))
|
||||
g.drawCardRoundRect(image, new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, isAftermath ? 90 : -90);
|
||||
else {
|
||||
g.drawRotatedImage(FSkin.getBorders().get(ImageCache.getInstance().getFSkinBorders(card)), new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, isAftermath ? 90 : -90);
|
||||
@@ -843,7 +843,7 @@ public class CardImageRenderer {
|
||||
if (card.isSplitCard() && rotateSplit && isCurrentCard) {
|
||||
boolean isAftermath = card.getText().contains("Aftermath") || card.getAlternateState().getOracleText().contains("Aftermath");
|
||||
if (Forge.enableUIMask.equals("Full")) {
|
||||
if (ImageCache.getInstance().isFullBorder(image))
|
||||
if (image.toString().contains(".fullborder."))
|
||||
g.drawCardRoundRect(image, new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, isAftermath ? 90 : -90);
|
||||
else {
|
||||
g.drawRotatedImage(FSkin.getBorders().get(ImageCache.getInstance().getFSkinBorders(card)), new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, isAftermath ? 90 : -90);
|
||||
@@ -855,7 +855,7 @@ public class CardImageRenderer {
|
||||
g.drawRotatedImage(image, new_x, new_y, new_w, new_h, new_x + new_w / 2, new_y + new_h / 2, isAftermath ? 90 : -90);
|
||||
} else {
|
||||
if (Forge.enableUIMask.equals("Full")) {
|
||||
if (ImageCache.getInstance().isFullBorder(image))
|
||||
if (image.toString().contains(".fullborder."))
|
||||
g.drawCardRoundRect(image, null, x, y, w, h, false, false);
|
||||
else {
|
||||
g.drawImage(ImageCache.getInstance().getBorderImage(image.toString()), ImageCache.getInstance().borderColor(image), x, y, w, h);
|
||||
@@ -872,7 +872,7 @@ public class CardImageRenderer {
|
||||
g.drawImage(sleeves, x, y, w, h);
|
||||
}
|
||||
} else if (Forge.enableUIMask.equals("Full") && canshow) {
|
||||
if (ImageCache.getInstance().isFullBorder(image))
|
||||
if (image.toString().contains(".fullborder."))
|
||||
g.drawCardRoundRect(image, null, x, y, w, h, false, false);
|
||||
else {
|
||||
g.drawImage(ImageCache.getInstance().getBorderImage(image.toString()), ImageCache.getInstance().borderColor(image), x, y, w, h);
|
||||
|
||||
@@ -597,7 +597,6 @@ public class CardRenderer {
|
||||
|
||||
public static void drawCard(Graphics g, IPaperCard pc, float x, float y, float w, float h, CardStackPosition pos) {
|
||||
Texture image = new RendererCachedCardImage(pc, false).getImage();
|
||||
final CardView card = CardView.getCardForUi(pc);
|
||||
float radius = (h - w) / 8;
|
||||
float croppedArea = isModernFrame(pc) ? CROP_MULTIPLIER : 0.97f;
|
||||
float minusxy = isModernFrame(pc) ? 0.0f : 0.13f * radius;
|
||||
@@ -610,7 +609,7 @@ public class CardRenderer {
|
||||
CardImageRenderer.drawCardImage(g, CardView.getCardForUi(pc), false, x, y, w, h, pos, true, true);
|
||||
} else {
|
||||
if (Forge.enableUIMask.equals("Full")) {
|
||||
if (ImageCache.getInstance().isFullBorder(image))
|
||||
if (image.toString().contains(".fullborder."))
|
||||
g.drawCardRoundRect(image, null, x, y, w, h, false, false);
|
||||
else {
|
||||
//tint the border
|
||||
@@ -623,6 +622,7 @@ public class CardRenderer {
|
||||
g.drawImage(image, x, y, w, h);
|
||||
}
|
||||
if (pc.isFoil()) { //draw foil effect if needed
|
||||
final CardView card = CardView.getCardForUi(pc);
|
||||
if (card.getCurrentState().getFoilIndex() == 0) { //if foil finish not yet established, assign a random one
|
||||
card.getCurrentState().setFoilIndexOverride(-1);
|
||||
}
|
||||
@@ -630,7 +630,7 @@ public class CardRenderer {
|
||||
}
|
||||
} else {
|
||||
//if card has invalid or no texture due to sudden changes in ImageCache, draw CardImageRenderer instead and wait for it to refresh automatically
|
||||
CardImageRenderer.drawCardImage(g, card, false, x, y, w, h, pos, true, true);
|
||||
CardImageRenderer.drawCardImage(g, CardView.getCardForUi(pc), false, x, y, w, h, pos, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -663,7 +663,7 @@ public class CardRenderer {
|
||||
if (FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.UI_ROTATE_PLANE_OR_PHENOMENON)
|
||||
&& (card.getCurrentState().isPhenomenon() || card.getCurrentState().isPlane() || (card.getCurrentState().isBattle() && !showAltState) || (card.getAlternateState() != null && card.getAlternateState().isBattle() && showAltState)) && rotate) {
|
||||
if (Forge.enableUIMask.equals("Full")) {
|
||||
if (ImageCache.getInstance().isFullBorder(image))
|
||||
if (image.toString().contains(".fullborder."))
|
||||
g.drawCardRoundRect(image, x, y, w, h, x + w / 2, y + h / 2, -90);
|
||||
else {
|
||||
g.drawRotatedImage(FSkin.getBorders().get(0), x, y, w, h, x + w / 2, y + h / 2, -90);
|
||||
@@ -675,7 +675,7 @@ public class CardRenderer {
|
||||
g.drawRotatedImage(image, x, y, w, h, x + w / 2, y + h / 2, -90);
|
||||
} else {
|
||||
if (Forge.enableUIMask.equals("Full") && canshow) {
|
||||
if (ImageCache.getInstance().isFullBorder(image))
|
||||
if (image.toString().contains(".fullborder."))
|
||||
g.drawCardRoundRect(image, crack_overlay, x, y, w, h, drawGray(card), magnify ? false : card.getDamage() > 0);
|
||||
else {
|
||||
//boolean t = (card.getCurrentState().getOriginalColors() != card.getCurrentState().getColors()) || card.getCurrentState().hasChangeColors();
|
||||
|
||||
@@ -1997,9 +1997,7 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
|
||||
@Override
|
||||
public void refresh() {
|
||||
BoosterDraft draft = parentScreen.getDraft();
|
||||
if (draft == null || !draft.hasNextChoice()) {
|
||||
return;
|
||||
}
|
||||
if (draft == null) { return; }
|
||||
|
||||
CardPool pool = draft.nextChoice();
|
||||
int packNumber = draft.getCurrentBoosterIndex() + 1;
|
||||
|
||||
@@ -72,21 +72,6 @@ public class FDeckViewer extends FScreen {
|
||||
FOptionPane.showMessageDialog(Forge.getLocalizer().getMessage("lblDeckListCopiedClipboard", deck.getName()));
|
||||
}
|
||||
|
||||
public static void copyCollectionToClipboard(CardPool pool) {
|
||||
final String nl = System.lineSeparator();
|
||||
final StringBuilder collectionList = new StringBuilder();
|
||||
Set<String> accounted = new HashSet<>();
|
||||
for (final Entry<PaperCard, Integer> entry : pool) {
|
||||
String cardName = entry.getKey().getCardName();
|
||||
if (!accounted.contains(cardName)) {
|
||||
collectionList.append(pool.countByName(cardName)).append(" ").append(cardName).append(nl);
|
||||
accounted.add(cardName);
|
||||
}
|
||||
}
|
||||
Forge.getClipboard().setContents(collectionList.toString());
|
||||
FOptionPane.showMessageDialog(Forge.getLocalizer().getMessage("lblCollectionCopiedClipboard"));
|
||||
}
|
||||
|
||||
private final Deck deck;
|
||||
private final CardManager cardManager;
|
||||
private DeckSection currentSection;
|
||||
|
||||
@@ -190,8 +190,4 @@ public abstract class FDisplayObject {
|
||||
public boolean keyDown(int keyCode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean keyUp(int keyCode) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>forge</artifactId>
|
||||
<groupId>forge</groupId>
|
||||
<version>${revision}</version>
|
||||
<version>2.0.04</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>forge-gui</artifactId>
|
||||
|
||||
@@ -20,7 +20,7 @@ Name=kavu
|
||||
1 Kavu Primarch|TSR|1
|
||||
4 Mountain|MH2|1
|
||||
3 Mountain|MH2|2
|
||||
2 Punishing Fire|CMD|1
|
||||
2 Punishing Fire|C11|1
|
||||
2 Raging Kavu|INV|1
|
||||
2 Ram Through|IKO|1
|
||||
2 Sacred Foundry|GRN|1
|
||||
|
||||
@@ -184,7 +184,7 @@
|
||||
"type": "item",
|
||||
"count": 1,
|
||||
"probability": 1,
|
||||
"itemName": "Lightbringers Boots"
|
||||
"itemName": "Lightbringers Boots'"
|
||||
}
|
||||
]</property>
|
||||
<property name="spawn.Easy" type="bool" value="true"/>
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
</object>
|
||||
<object id="58" template="../../obj/enemy.tx" x="153" y="516">
|
||||
<properties>
|
||||
<property name="effect" value="{ "startBattleWithCard": [ "Exquisite Blood" ] }"/>
|
||||
<property name="effect" value="{ "startBattleWithCard": [ "Exquisite Blood" }"/>
|
||||
<property name="enemy" value="Fallen Angel"/>
|
||||
<property name="threatRange" type="int" value="60"/>
|
||||
</properties>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<tileset firstgid="1" source="../../tileset/main.tsx"/>
|
||||
<tileset firstgid="10113" source="../../tileset/buildings.tsx"/>
|
||||
<tileset firstgid="11905" source="../../tileset/dungeon.tsx"/>
|
||||
<tileset firstgid="16385" source="../../tileset/main-nocollide.tsx"/>
|
||||
<tileset firstgid="16001" source="../../tileset/main-nocollide.tsx"/>
|
||||
<layer id="1" name="Background" width="35" height="25">
|
||||
<data encoding="base64" compression="zlib">
|
||||
eJybxsnAMG0AMDYwEO4YCm6hBxhIu9HBqFuwg8HmloFOsyMFqLIPtAsgAOSOUbdggsHijlEwCgYzmCM60C4YBaNg6AIAYHk9+Q==
|
||||
@@ -24,12 +24,12 @@
|
||||
</layer>
|
||||
<layer id="5" name="AboveSprites" width="35" height="25">
|
||||
<data encoding="base64" compression="zlib">
|
||||
eJzNlk9OAjEUhztI4gZczTQmBHbcwI0hSCIslC1HgmMQlYAu1AN4A5ccAFlwENHX0Ekfz762jMMwv+Rbdf58ffPaqRDF5inhKYuHjwioBLKSQnwBa/l37Cd233tWYG0ePe+YldglIjU7pcss2R+z5XNUHhecRVOIKgNNF/XzPNnNCffuA1kvHWmu6eh7e9LtMrnapw8MgBcYa58bfMF18dWQc9lIg8pYOxXtorJsGMaoPpwL53YqlxqM1YEL1Fd5fKPnxJD2is0F8wpjb8A749Il+3aoC+1dri6Yu5YQ98CwZXfBbOMwl1t4Vp9hoN9Dv1NIv2BC9passdXJ5rKN/XvKsUJdsq4lmsjRd+rscCMN6XVq/sdwudZ1/bjcQRNybppqR/yv+M//2uficrP1bJY+/o7dLq7k7aLqm/bDocnbhdsrQs773N6Lzw6HQNdEFpeicOUXefTD2g==
|
||||
eJzNlktuwjAQhp22Ujelq8SqVJUdN2BTIagESH0seyV6jKqAaLsADsAtOEDLgoPwGAtHHqYe26Qh5Je+lfP4PBk7FqLYDBKesnj4iICzQH6kEL/AQv4d28Tue88LrE3f845hiV0iUrNTugyT/TFbHl/L44LzeSfEBQNNE/XzKNnNCffuB1kvDWmuaeh7H6Tb5a2+TwfoAt8wVrs0+ILr4qsh57KUBpWediraRWV+a+ih+nAunNupXK5grAJco77K4xt9JYa0V2wumDGMTYAp49Ik+3aoC+1dri6Yp6oQz8BL1e6CWcdhLm14Voehq99Dv1NIv2BC9passdXJ5rKO/XvKsUJdsq4lmsjRd+rs0JKG9Do1/2O43Ou6zm520IScm961I/5X/Od/7XNxudl6Nksfr2K3iyt5u6j6pv1waPJ24faKkPM+t/fis8Mh0DWRxaUoXNkCTvTB1g==
|
||||
</data>
|
||||
</layer>
|
||||
<layer id="6" name="Clutter" width="35" height="25">
|
||||
<data encoding="base64" compression="zlib">
|
||||
eJxjYBgF1Ab/RUgTpxSsEIVgEMgF2rEcyA6XhPAXimLXAxIPCwSqA+KIQOq55aAEbjnHMAR7JZK7cLmRGm5Zicf/pIhTAuYAzWRCwotDUfl2YqgYJm4vhmkWTyj13YcM0N0CwxyyuPXA/EQIEKMGF6iwYGCotCBfPzXBDqA7dlLJLd+pmPeEceS9fQoMDPsVEDQuMDWI9m6JUWRgiFVE0LiAcTBl9iOnF2LDRZUdv5nMZJYNyOmF2HAh5BZalZkgALIbHRMCtCqXSHUHIZAogokpAdT0N7XcNNgBJfUArQE1y2NKATXLY0oBpeXxKKAcAABkFTay
|
||||
eJxjYBgF1Ab/RUgTpxSsEIVgEMgF2rEcyA6XhPAXimLXAxK/5s/AcB2Ib/hTzy0HJfDIhSDYK5HchcuN1HDLSjz+J0WcEjAHaCYTElYOQeXbiaFimLi9GKZZPcHUdx8yQHcLDHPI4tYD8xMhQIwaXKDCgoGh0oJ8/dQEO4Du2Eklt5QHUMccEBDGkff2KTAw7FdA0LiAaCDt3RKjyMAQq4igcYHNFLoFOb0QGy6q7PjNZCazbEBOL8SGCyG30KrMBAGQ3eiYEKBVuUSqOwiBRBFMTAmgpr+p5abBDiipB2gNqFkeUwqoWR5TCigtj0cB5QAAqOk3GA==
|
||||
</data>
|
||||
</layer>
|
||||
<layer id="7" name="AboveAboveClutter" width="35" height="25">
|
||||
@@ -52,7 +52,7 @@
|
||||
<object id="49" template="../../obj/gold.tx" x="48.3333" y="126.333"/>
|
||||
<object id="50" template="../../obj/enemy.tx" x="298.667" y="182">
|
||||
<properties>
|
||||
<property name="effect" value="{ "startBattleWithCard": ["Black Market"]}"/>
|
||||
<property name="effect" value="{ "startBattleWithCard": [ "Black Market" }"/>
|
||||
<property name="enemy" value="Cosmic Horror"/>
|
||||
<property name="threatRange" type="int" value="40"/>
|
||||
</properties>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<map version="1.10" tiledversion="1.10.1" orientation="orthogonal" renderorder="right-down" width="30" height="17" tilewidth="16" tileheight="16" infinite="0" nextlayerid="8" nextobjectid="138">
|
||||
<map version="1.10" tiledversion="1.10.1" orientation="orthogonal" renderorder="right-down" width="30" height="17" tilewidth="16" tileheight="16" infinite="0" nextlayerid="8" nextobjectid="113">
|
||||
<editorsettings>
|
||||
<export target="wastetown..tmx" format="tmx"/>
|
||||
</editorsettings>
|
||||
@@ -106,37 +106,36 @@
|
||||
<property name="threatRange" type="int" value="30"/>
|
||||
</properties>
|
||||
</object>
|
||||
<object id="87" template="../../obj/enemy.tx" x="276.711" y="206.275">
|
||||
<object id="87" template="../../obj/enemy.tx" x="278.667" y="203.667">
|
||||
<properties>
|
||||
<property name="enemy" value="Monk"/>
|
||||
<property name="threatRange" type="int" value="40"/>
|
||||
</properties>
|
||||
</object>
|
||||
<object id="88" template="../../obj/exit.tx" x="162.521" y="212.637" width="24.2714" height="6.20943"/>
|
||||
<object id="89" template="../../obj/exit.tx" x="163.046" y="180.468" width="24.2714" height="6.20943"/>
|
||||
<object id="90" template="../../obj/exit.tx" x="114.792" y="180.818" width="24.2714" height="6.20943"/>
|
||||
<object id="91" template="../../obj/exit.tx" x="114.792" y="116.48" width="24.2714" height="6.20943"/>
|
||||
<object id="92" template="../../obj/exit.tx" x="115.142" y="148.299" width="24.2714" height="6.20943"/>
|
||||
<object id="93" template="../../obj/exit.tx" x="162.696" y="117.179" width="24.2714" height="6.20943"/>
|
||||
<object id="94" template="../../obj/exit.tx" x="162.696" y="85.36" width="24.2714" height="6.20943"/>
|
||||
<object id="95" template="../../obj/exit.tx" x="114.792" y="85.36" width="24.2714" height="6.20943"/>
|
||||
<object id="96" template="../../obj/exit.tx" x="162.696" y="149.348" width="24.2714" height="6.20943"/>
|
||||
<object id="97" template="../../obj/exit.tx" x="227.034" y="148.299" width="24.2714" height="6.20943"/>
|
||||
<object id="98" template="../../obj/exit.tx" x="255.357" y="51.7924" width="15.5298" height="6.20943"/>
|
||||
<object id="99" template="../../obj/exit.tx" x="273.714" y="84.6607" width="24.621" height="6.20943"/>
|
||||
<object id="100" template="../../obj/exit.tx" x="274.413" y="117.179" width="24.621" height="6.20943"/>
|
||||
<object id="101" template="../../obj/exit.tx" x="226.16" y="212.288" width="24.621" height="6.20943"/>
|
||||
<object id="102" template="../../obj/exit.tx" x="275.113" y="148.299" width="24.621" height="6.20943"/>
|
||||
<object id="103" template="../../obj/exit.tx" x="227.558" y="180.468" width="24.621" height="6.20943"/>
|
||||
<object id="104" template="../../obj/exit.tx" x="226.16" y="116.83" width="24.621" height="6.20943"/>
|
||||
<object id="105" template="../../obj/exit.tx" x="225.81" y="84.6607" width="24.621" height="6.20943"/>
|
||||
<object id="106" template="../../obj/exit.tx" x="193.291" y="31.5119" width="29.5163" height="35.5811"/>
|
||||
<object id="107" template="../../obj/exit.tx" x="323.016" y="98.6472" width="8.18684" height="24.0422"/>
|
||||
<object id="108" template="../../obj/exit.tx" x="323.191" y="145.677" width="8.18684" height="24.0422"/>
|
||||
<object id="109" template="../../obj/exit.tx" x="98.3579" y="98.8221" width="8.18684" height="24.0422"/>
|
||||
<object id="110" template="../../obj/exit.tx" x="98.0082" y="148.474" width="8.18684" height="24.0422"/>
|
||||
<object id="111" template="../../obj/waypoint.tx" x="201.099" y="238.512"/>
|
||||
<object id="112" template="../../obj/waypoint.tx" x="201.448" y="105.641"/>
|
||||
<object id="113" template="../../obj/collision.tx" x="226.244" y="149.837" width="24.0078" height="5.4"/>
|
||||
<object id="114" template="../../obj/collision.tx" x="226.196" y="181.7" width="24.0078" height="5.4"/>
|
||||
<object id="115" template="../../obj/collision.tx" x="226.996" y="213.5" width="24.0078" height="5.4"/>
|
||||
<object id="116" template="../../obj/collision.tx" x="275.396" y="181.9" width="24.0078" height="5.4"/>
|
||||
<object id="117" template="../../obj/collision.tx" x="274.596" y="117.3" width="24.0078" height="5.4"/>
|
||||
<object id="118" template="../../obj/collision.tx" x="274.596" y="148.9" width="24.0078" height="5.4"/>
|
||||
<object id="119" template="../../obj/collision.tx" x="226.596" y="116.7" width="24.0078" height="5.4"/>
|
||||
<object id="120" template="../../obj/collision.tx" x="227.196" y="85.7" width="24.0078" height="5.4"/>
|
||||
<object id="121" template="../../obj/collision.tx" x="274.996" y="85.3" width="24.0078" height="5.4"/>
|
||||
<object id="122" template="../../obj/collision.tx" x="324.196" y="99.5" width="7.0078" height="23"/>
|
||||
<object id="123" template="../../obj/collision.tx" x="323.896" y="147.7" width="7.0078" height="23"/>
|
||||
<object id="124" template="../../obj/collision.tx" x="99.0961" y="147.9" width="7.0078" height="23"/>
|
||||
<object id="125" template="../../obj/collision.tx" x="98.4961" y="100.3" width="7.0078" height="23"/>
|
||||
<object id="126" template="../../obj/collision.tx" x="115.896" y="116.9" width="22.6078" height="6.6"/>
|
||||
<object id="127" template="../../obj/collision.tx" x="115.496" y="148.7" width="22.6078" height="6.6"/>
|
||||
<object id="128" template="../../obj/collision.tx" x="115.696" y="180.9" width="22.6078" height="6.6"/>
|
||||
<object id="129" template="../../obj/collision.tx" x="163.296" y="212.9" width="22.6078" height="6.6"/>
|
||||
<object id="130" template="../../obj/collision.tx" x="163.296" y="180.5" width="22.6078" height="6.6"/>
|
||||
<object id="131" template="../../obj/collision.tx" x="163.696" y="148.5" width="22.6078" height="6.6"/>
|
||||
<object id="132" template="../../obj/collision.tx" x="163.496" y="116.3" width="22.6078" height="6.6"/>
|
||||
<object id="133" template="../../obj/collision.tx" x="163.296" y="84.1" width="22.6078" height="6.6"/>
|
||||
<object id="134" template="../../obj/collision.tx" x="115.696" y="84.1" width="22.6078" height="6.6"/>
|
||||
<object id="135" template="../../obj/collision.tx" x="256.296" y="51.1" width="16.2078" height="8.4"/>
|
||||
<object id="136" template="../../obj/collision.tx" x="192.496" y="31.9" width="31.2078" height="36.8"/>
|
||||
</objectgroup>
|
||||
</map>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<map version="1.10" tiledversion="1.10.1" orientation="orthogonal" renderorder="right-down" width="60" height="50" tilewidth="16" tileheight="16" infinite="0" nextlayerid="13" nextobjectid="119">
|
||||
<map version="1.10" tiledversion="1.10.2" orientation="orthogonal" renderorder="right-down" width="60" height="50" tilewidth="16" tileheight="16" infinite="0" nextlayerid="13" nextobjectid="119">
|
||||
<editorsettings>
|
||||
<export target="wastetown..tmx" format="tmx"/>
|
||||
</editorsettings>
|
||||
@@ -173,7 +173,7 @@
|
||||
<property name="enemy" value="Greater Sandwurm"/>
|
||||
<property name="hidden" type="bool" value="true"/>
|
||||
<property name="speedModifier" type="float" value="40"/>
|
||||
<property name="threatRange" type="int" value="80"/>
|
||||
<property name="threatRange" type="int" value="30"/>
|
||||
</properties>
|
||||
</object>
|
||||
<object id="115" template="../../obj/enemy.tx" x="855.333" y="361.333">
|
||||
@@ -181,7 +181,7 @@
|
||||
<property name="enemy" value="Greater Sandwurm"/>
|
||||
<property name="hidden" type="bool" value="true"/>
|
||||
<property name="speedModifier" type="float" value="40"/>
|
||||
<property name="threatRange" type="int" value="70"/>
|
||||
<property name="threatRange" type="int" value="30"/>
|
||||
</properties>
|
||||
</object>
|
||||
<object id="116" template="../../obj/enemy.tx" x="294" y="367.333">
|
||||
@@ -197,7 +197,7 @@
|
||||
<property name="enemy" value="Scarab"/>
|
||||
<property name="hidden" type="bool" value="true"/>
|
||||
<property name="speedModifier" type="float" value="40"/>
|
||||
<property name="threatRange" type="int" value="60"/>
|
||||
<property name="threatRange" type="int" value="50"/>
|
||||
</properties>
|
||||
</object>
|
||||
<object id="118" template="../../obj/booster.tx" x="4" y="641.333">
|
||||
|
||||
@@ -83,7 +83,7 @@
|
||||
]
|
||||
}
|
||||
]</property>
|
||||
<property name="displayNameOverride" value="Tibalt's Torturer"/>
|
||||
<property name="displayNameOverride" value="Tibalt's Pet Torturer"/>
|
||||
<property name="effect">{ "startBattleWithCard": [ "Phyrexian Arena","Xander's Wake"]
|
||||
}</property>
|
||||
<property name="enemy" value="Torturer"/>
|
||||
|
||||
@@ -162,8 +162,8 @@
|
||||
"minHeight": 16,
|
||||
"rightWidth": 4,
|
||||
"leftWidth": 4,
|
||||
"bottomHeight": 8,
|
||||
"topHeight": 5,
|
||||
"bottomHeight": 7,
|
||||
"topHeight": 4,
|
||||
"offsetX": 0,
|
||||
"offsetY": 0,
|
||||
"offsetXspeed": 0,
|
||||
@@ -566,7 +566,7 @@
|
||||
"downFontColor": "RGBA_255_255_255_255",
|
||||
"overFontColor": "RGBA_255_255_255_255",
|
||||
"focusedFontColor": "RGBA_255_255_255_255",
|
||||
"disabledFontColor": "RGBA_204_200_200_255",
|
||||
"disabledFontColor": "RGBA_255_255_255_255",
|
||||
"checkedFontColor": "RGBA_255_255_255_255",
|
||||
"checkedDownFontColor": "RGBA_255_255_255_255",
|
||||
"checkedOverFontColor": "RGBA_255_255_255_255",
|
||||
|
||||
@@ -37,10 +37,10 @@
|
||||
{
|
||||
"type": "TextButton",
|
||||
"name": "done",
|
||||
"text": "Back",
|
||||
"text": "[+OK]",
|
||||
"width": 48,
|
||||
"height": 30,
|
||||
"binding": "Back",
|
||||
"binding": "Use",
|
||||
"x": 420,
|
||||
"y": 120
|
||||
} ,
|
||||
|
||||
@@ -28,60 +28,6 @@
|
||||
"height": 20,
|
||||
"x": 415,
|
||||
"y": 245
|
||||
},
|
||||
{
|
||||
"type": "TextButton",
|
||||
"name": "details",
|
||||
"text": "[%80] Details",
|
||||
"width": 60,
|
||||
"height": 20,
|
||||
"x": 210,
|
||||
"y": 245
|
||||
},
|
||||
{
|
||||
"type": "TextButton",
|
||||
"name": "events",
|
||||
"text": "[%80] Events",
|
||||
"width": 60,
|
||||
"height": 20,
|
||||
"x": 210,
|
||||
"y": 245
|
||||
},
|
||||
{
|
||||
"type": "TextButton",
|
||||
"name": "reputation",
|
||||
"text": "[%80] Reputation",
|
||||
"width": 60,
|
||||
"height": 20,
|
||||
"x": 210,
|
||||
"y": 245
|
||||
},
|
||||
{
|
||||
"type": "TextButton",
|
||||
"name": "names",
|
||||
"text": "[%80] Names",
|
||||
"width": 60,
|
||||
"height": 20,
|
||||
"x": 210,
|
||||
"y": 245
|
||||
},
|
||||
{
|
||||
"type": "TextButton",
|
||||
"name": "zoomIn",
|
||||
"text": "[%80]+",
|
||||
"width": 20,
|
||||
"height": 20,
|
||||
"x": 5,
|
||||
"y": 5
|
||||
},
|
||||
{
|
||||
"type": "TextButton",
|
||||
"name": "zoomOut",
|
||||
"text": "[%80]-",
|
||||
"width": 20,
|
||||
"height": 20,
|
||||
"x": 455,
|
||||
"y": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -26,60 +26,6 @@
|
||||
"height": 20,
|
||||
"x": 205,
|
||||
"y": 455
|
||||
},
|
||||
{
|
||||
"type": "TextButton",
|
||||
"name": "details",
|
||||
"text": "[%80] Details",
|
||||
"width": 60,
|
||||
"height": 20,
|
||||
"x": 105,
|
||||
"y": 245
|
||||
},
|
||||
{
|
||||
"type": "TextButton",
|
||||
"name": "events",
|
||||
"text": "[%80] Events",
|
||||
"width": 60,
|
||||
"height": 20,
|
||||
"x": 105,
|
||||
"y": 245
|
||||
},
|
||||
{
|
||||
"type": "TextButton",
|
||||
"name": "reputation",
|
||||
"text": "[%80] Reputation",
|
||||
"width": 60,
|
||||
"height": 20,
|
||||
"x": 105,
|
||||
"y": 245
|
||||
},
|
||||
{
|
||||
"type": "TextButton",
|
||||
"name": "names",
|
||||
"text": "[%80] Names",
|
||||
"width": 60,
|
||||
"height": 20,
|
||||
"x": 105,
|
||||
"y": 245
|
||||
},
|
||||
{
|
||||
"type": "TextButton",
|
||||
"name": "zoomIn",
|
||||
"text": "[%80]+",
|
||||
"width": 20,
|
||||
"height": 20,
|
||||
"x": 5,
|
||||
"y": 5
|
||||
},
|
||||
{
|
||||
"type": "TextButton",
|
||||
"name": "zoomOut",
|
||||
"text": "[%80]-",
|
||||
"width": 20,
|
||||
"height": 20,
|
||||
"x": 245,
|
||||
"y": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -5,8 +5,8 @@ PT:3/2
|
||||
K:Flying
|
||||
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigMill | TriggerDescription$ At the beginning of your upkeep, mill a card. If an instant or sorcery card was milled this way, transform CARDNAME.
|
||||
SVar:TrigMill:DB$ Mill | Defined$ You | RememberMilled$ True | SubAbility$ DBTransform
|
||||
SVar:DBTransform:DB$ SetState | Defined$ Self | ConditionDefined$ Remembered | ConditionPresent$ Card.Instant,Card.Sorcery | SubAbility$ DBCleanup | Mode$ Transform
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
SVar:DBTransform:DB$ SetState | Defined$ Self | ConditionDefined$ Remembered | ConditionPresent$ Card.Instant,Card.Sorcery | SubAbility$ Cleanup | Mode$ Transform
|
||||
SVar:Cleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
DeckHints:Ability$Delirium & Type$Instant|Sorcery
|
||||
AlternateMode:DoubleFaced
|
||||
Oracle:Flying\nAt the beginning of your upkeep, mill a card. If an instant or sorcery card was milled this way, transform Aberrant Researcher.
|
||||
|
||||
@@ -4,6 +4,6 @@ Types:Legendary Creature Spirit Soldier
|
||||
PT:3/4
|
||||
K:Vigilance
|
||||
T:Mode$ BecomesTarget | ValidTarget$ Card.Self | ValidSource$ Ability.numTargets EQ1 | TriggerZones$ Battlefield | Execute$ TrigCopy | TriggerDescription$ Whenever CARDNAME becomes the target of an ability that targets only it, you may pay {1}{R/W}. If you do, copy that ability for each other creature you control that ability could target. Each copy targets a different one of those creatures. ({R/W} can be paid with either {R} or {W}.)
|
||||
SVar:TrigCopy:AB$ CopySpellAbility | Cost$ 1 RW | Defined$ TriggeredSpellAbility | CopyForEachCanTarget$ Creature.YouCtrl
|
||||
SVar:TrigCopy:AB$ CopySpellAbility | Cost$ 1 RW | Defined$ TriggeredStackInstance | CopyForEachCanTarget$ Creature.YouCtrl
|
||||
AI:RemoveDeck:Random
|
||||
Oracle:Vigilance\nWhenever Agrus Kos, Eternal Soldier becomes the target of an ability that targets only it, you may pay {1}{R/W}. If you do, copy that ability for each other creature you control that ability could target. Each copy targets a different one of those creatures. ({R/W} can be paid with either {R} or {W}.)
|
||||
|
||||
@@ -9,5 +9,5 @@ SVar:Mana:AB$ Mana | Cost$ T Sac<1/CARDNAME/this artifact> | Produced$ Any | Amo
|
||||
K:Class:3:4 R:AddTrigger$ TriggerExplosion
|
||||
SVar:TriggerExplosion:Mode$ SpellCast | ValidCard$ Card | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | ValidSA$ Spell.ManaFromTreasure | Execute$ TrigDamage | TriggerDescription$ Whenever you cast a spell, if mana from a Treasure was spent to cast it, this Class deals damage equal to that spell's mana value to each opponent.
|
||||
SVar:TrigDamage:DB$ DealDamage | Defined$ Opponent | NumDmg$ X
|
||||
SVar:X:TriggeredSpellAbility$CardManaCostLKI
|
||||
SVar:X:TriggeredStackInstance$CardManaCostLKI
|
||||
Oracle:(Gain the next level as a sorcery to add its ability.)\nWhen Alchemist's Talent enters, create two tapped Treasure tokens.\n{1}{R}: Level 2\nTreasures you control have "{T}, Sacrifice this artifact: Add two mana of any one color."\n{4}{R}: Level 3\nWhenever you cast a spell, if mana from a Treasure was spent to cast it, this Class deals damage equal to that spell's mana value to each opponent.
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
Name:Ancient Vendetta
|
||||
ManaCost:3 B
|
||||
Types:Sorcery
|
||||
A:SP$ NameCard | Defined$ You | SubAbility$ ExileYard | StackDescription$ REP Choose a_{p:You} chooses a & Search target opponent's_{p:You} searches {p:Targeted}'s & and exile_and exiles & That player_{p:Targeted} | SpellDescription$ Choose a card name. Search target opponent's graveyard, hand, and library for up to four cards with that name and exile them. That player shuffles.
|
||||
SVar:ExileYard:DB$ ChangeZone | ValidTgts$ Opponent | ChangeType$ Card.NamedCard | Origin$ Graveyard | DefinedPlayer$ Targeted | Chooser$ You | Destination$ Exile | ChangeNum$ Y | Hidden$ True | RememberChanged$ True | SubAbility$ ExileHand | StackDescription$ None
|
||||
SVar:ExileHand:DB$ ChangeZone | Origin$ Hand | Destination$ Exile | DefinedPlayer$ Targeted | ChangeType$ Card.NamedCard | ChangeNum$ Y | Chooser$ You | SubAbility$ ExileLib | RememberChanged$ True | StackDescription$ None
|
||||
SVar:ExileLib:DB$ ChangeZone | Origin$ Library | Destination$ Exile | DefinedPlayer$ Targeted | ChangeType$ Card.NamedCard | ChangeNum$ Y | Chooser$ You | Shuffle$ True | SubAbility$ DBCleanup | StackDescription$ None
|
||||
A:SP$ NameCard | Defined$ You | SubAbility$ ExileYard | SpellDescription$ Choose a card name. Search target opponent's graveyard, hand, and library for up to four cards with that name and exile them. That player shuffles.
|
||||
SVar:ExileYard:DB$ ChangeZone | ValidTgts$ Opponent | ChangeType$ Card.NamedCard | Origin$ Graveyard | DefinedPlayer$ Targeted | Chooser$ You | Destination$ Exile | ChangeNum$ Y | Hidden$ True | RememberChanged$ True | SubAbility$ ExileHand
|
||||
SVar:ExileHand:DB$ ChangeZone | Origin$ Hand | Destination$ Exile | DefinedPlayer$ Targeted | ChangeType$ Card.NamedCard | ChangeNum$ Y | Chooser$ You | SubAbility$ ExileLib | RememberChanged$ True
|
||||
SVar:ExileLib:DB$ ChangeZone | Origin$ Library | Destination$ Exile | DefinedPlayer$ Targeted | ChangeType$ Card.NamedCard | ChangeNum$ Y | Chooser$ You | Shuffle$ True
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
SVar:X:Remembered$Amount
|
||||
SVar:Y:SVar$X/NMinus.4
|
||||
|
||||
@@ -4,8 +4,8 @@ Types:Legendary Creature Bear
|
||||
PT:2/2
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Bear.YouCtrl+Other | TriggerZones$ Battlefield | Execute$ TrigChoose | TriggerDescription$ Whenever another Bear you control enters, ABILITY
|
||||
SVar:TrigChoose:DB$ Charm | Choices$ DBCounter,DBPump
|
||||
SVar:DBCounter:DB$ PutCounter | ValidTgts$ Bear | CounterType$ P1P1 | CounterNum$ 2 | SpellDescription$ Put two +1/+1 counters on target Bear.
|
||||
SVar:DBPump:DB$ Pump | ValidTgts$ Bear.YouCtrl | TgtPrompt$ Select target Bear you control | SubAbility$ DBFight | SpellDescription$ Target Bear you control fights target creature you don't control.
|
||||
SVar:DBCounter:DB$ PutCounter | ValidTgts$ Bear | TgtPrompt$ Select target Bear | CounterType$ P1P1 | CounterNum$ 2 | SpellDescription$ Put two +1/+1 counters on target Bear.
|
||||
SVar:DBPump:DB$ Pump | ValidTgts$ Bear.YouCtrl | TgtPrompt$ Choose target creature you control | SubAbility$ DBFight | SpellDescription$ Target Bear you control fights target creature you don't control.
|
||||
SVar:DBFight:DB$ Fight | Defined$ ParentTarget | ValidTgts$ Creature.YouDontCtrl | TgtPrompt$ Choose target creature you don't control
|
||||
DeckHas:Ability$Counters
|
||||
DeckHints:Type$Bear
|
||||
|
||||
@@ -7,8 +7,8 @@ K:Class:2:R G:AddStaticAbility$ SReduceCost
|
||||
SVar:SReduceCost:Mode$ ReduceCost | ValidCard$ Legendary | Type$ Spell | Activator$ You | Amount$ 1 | Color$ R G | IgnoreGeneric$ True | Secondary$ True | Description$ Legendary spells you cast cost {R}{G} less to cast. This effect reduces only the amount of colored mana you pay.
|
||||
K:Class:3:3 R G:AddTrigger$ TriggerCast
|
||||
SVar:TriggerCast:Mode$ SpellCast | ValidCard$ Legendary | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigImpulsiveDraw | Secondary$ True | TriggerDescription$ Whenever you cast a legendary spell, exile the top two cards of your library. You may play them this turn.
|
||||
SVar:TrigImpulsiveDraw:DB$ Dig | Defined$ You | DigNum$ 2 | ChangeNum$ All | DestinationZone$ Exile | RememberChanged$ True | SubAbility$ DBEffect
|
||||
SVar:DBEffect:DB$ Effect | RememberObjects$ RememberedCard | StaticAbilities$ StPlay | SubAbility$ DBCleanup | ForgetOnMoved$ Exile
|
||||
SVar:TrigImpulsiveDraw:DB$ Dig | Defined$ TriggeredPlayer | DigNum$ 2 | ChangeNum$ All | DestinationZone$ Exile | RememberChanged$ True | SubAbility$ DBEffect
|
||||
SVar:DBEffect:DB$ Effect | EffectOwner$ TriggeredPlayer | RememberObjects$ RememberedCard | StaticAbilities$ StPlay | SubAbility$ DBCleanup | ForgetOnMoved$ Exile
|
||||
SVar:StPlay:Mode$ Continuous | MayPlay$ True | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ You may play them this turn.
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
Oracle:(Gain the next level as a sorcery to add its ability.)\nLegendary creatures you control enter with an additional +1/+1 counter on them.\n{R}{G}: Level 2\nLegendary spells you cast cost {R}{G} less to cast. This effect reduces only the amount of colored mana you pay.\n{3}{R}{G}: Level 3\nWhenever you cast a legendary spell, exile the top two cards of your library. You may play them this turn.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Name:Blot Out
|
||||
ManaCost:2 B
|
||||
Types:Instant
|
||||
A:SP$ ChooseCard | ValidTgts$ Opponent | Choices$ Creature.TargetedPlayerCtrl+cmcEQX,Planeswalker.TargetedPlayerCtrl+cmcEQX | ChoiceTitle$ Choose a creature or planeswalker with the highest mana value among creatures and planeswalkers you control | Mandatory$ True | SubAbility$ DBExile | StackDescription$ REP Target opponent_{p:Targeted} | SpellDescription$ Target opponent exiles a creature or planeswalker they control with the greatest mana value among creatures and planeswalkers they control.
|
||||
SVar:DBExile:DB$ ChangeZone | Defined$ ChosenCard | Origin$ Battlefield | Destination$ Exile | StackDescription$ None
|
||||
A:SP$ ChooseCard | ValidTgts$ Opponent | Choices$ Creature.TargetedPlayerCtrl+cmcEQX,Planeswalker.TargetedPlayerCtrl+cmcEQX | ChoiceTitle$ Choose a creature or planeswalker with the highest mana value to sacrifice | Mandatory$ True | SubAbility$ DBExile | SpellDescription$ Target opponent exiles a creature or planeswalker they control with the greatest mana value among creatures and planeswalkers they control.
|
||||
SVar:DBExile:DB$ ChangeZone | Defined$ ChosenCard | Origin$ Battlefield | Destination$ Exile
|
||||
SVar:X:Count$Valid Creature.TargetedPlayerCtrl,Planeswalker.TargetedPlayerCtrl$GreatestCMC
|
||||
Oracle:Target opponent exiles a creature or planeswalker they control with the greatest mana value among creatures and planeswalkers they control.
|
||||
|
||||
@@ -5,7 +5,8 @@ PT:5/5
|
||||
K:Flying
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigPump | TriggerDescription$ Lightning Breath — When CARDNAME enters, until your next turn, target creature an opponent controls gets -3/-0, up to one other target creature gets -2/-0, and up to one other target creature gets -1/-0.
|
||||
SVar:TrigPump:DB$ Pump | ValidTgts$ Creature.OppCtrl | TgtPrompt$ Choose target creature an opponent controls (-3/-0) | NumAtt$ -3 | Duration$ UntilYourNextTurn | SubAbility$ DBPump
|
||||
SVar:DBPump:DB$ Pump | ValidTgts$ Creature | TgtPrompt$ Choose up to one other target creature (-2/-0) | TargetMin$ 0 | TargetMax$ 1 | NumAtt$ -2 | TargetUnique$ True | Duration$ UntilYourNextTurn | SubAbility$ DBPump2
|
||||
SVar:DBPump2:DB$ Pump | ValidTgts$ Creature | TgtPrompt$ Choose up to one other target creature (-1/-0) | TargetMin$ 0 | TargetMax$ 1 | NumAtt$ -1 | TargetUnique$ True | Duration$ UntilYourNextTurn
|
||||
SVar:DBPump:DB$ Pump | ValidTgts$ Creature.OppCtrl | TgtPrompt$ Choose up to one other target creature an opponent controls (-2/-0) | TargetMin$ 0 | TargetMax$ 1 | NumAtt$ -2 | TargetUnique$ True | Duration$ UntilYourNextTurn | SubAbility$ DBPump2
|
||||
SVar:DBPump2:DB$ Pump | ValidTgts$ Creature.OppCtrl | TgtPrompt$ Choose up to one other target creature an opponent controls (-1/-0) | TargetMin$ 0 | TargetMax$ 1 | NumAtt$ -1 | TargetUnique$ True | Duration$ UntilYourNextTurn
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
SVar:PlayMain1:TRUE
|
||||
Oracle:Flying\nLightning Breath — When Blue Dragon enters, until your next turn, target creature an opponent controls gets -3/-0, up to one other target creature gets -2/-0, and up to one other target creature gets -1/-0.
|
||||
|
||||
@@ -5,5 +5,5 @@ PT:4/4
|
||||
K:Flying
|
||||
T:Mode$ SpellCast | ValidCard$ Spirit,Arcane | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | OptionalDecider$ You | Execute$ TrigGainLife | TriggerDescription$ Whenever you cast a Spirit or Arcane spell, you may gain life equal to that spell's mana value.
|
||||
SVar:TrigGainLife:DB$ GainLife | Defined$ You | LifeAmount$ X
|
||||
SVar:X:TriggeredSpellAbility$CardManaCostLKI
|
||||
SVar:X:TriggeredStackInstance$CardManaCostLKI
|
||||
Oracle:Flying\nWhenever you cast a Spirit or Arcane spell, you may gain life equal to that spell's mana value.
|
||||
|
||||
@@ -20,5 +20,5 @@ ManaCost:no cost
|
||||
Types:Legendary Land Cave
|
||||
A:AB$ Mana | Cost$ T | Produced$ R | SpellDescription$ Add {R}.
|
||||
T:Mode$ SpellCast | ValidCard$ Permanent | ValidSA$ Spell.ManaFromCard.StrictlySelf | ValidActivatingPlayer$ You | Execute$ TrigDiscover | TriggerDescription$ Whenever you cast a permanent spell using mana produced by CARDNAME, discover X, where X is that spell's mana value.
|
||||
SVar:TrigDiscover:DB$ Discover | Num$ TriggeredSpellAbility$CardManaCostLKI
|
||||
SVar:TrigDiscover:DB$ Discover | Num$ TriggeredStackInstance$CardManaCostLKI
|
||||
Oracle:(Transforms from Brass's Tunnel-Grinder.)\n{T}: Add {R}.\nWhenever you cast a permanent spell using mana produced by Tecutlan, the Searing Rift, discover X, where X is that spell's mana value.
|
||||
|
||||
@@ -7,7 +7,7 @@ T:Mode$ DamageDone | ValidSource$ Card.AttachedBy | ValidTarget$ Player | Combat
|
||||
SVar:TrigSacrifice:DB$ SacrificeAll | ValidCards$ Card.EnchantedBy | SubAbility$ StillFurious
|
||||
SVar:StillFurious:DB$ Attach | Object$ Self | Choices$ Creature.YouCtrl | ChoiceTitle$ Choose a creature you control to attach Breath of Fury to | RememberAttached$ True | SubAbility$ CatchBreath
|
||||
SVar:CatchBreath:DB$ UntapAll | ValidCards$ Creature.YouCtrl | SubAbility$ TheFuryContinues | ConditionDefined$ Remembered | ConditionPresent$ Card
|
||||
SVar:TheFuryContinues:DB$ AddPhase | ExtraPhase$ Combat | AfterPhase$ EndCombat | ConditionDefined$ Remembered | ConditionPresent$ Card | SubAbility$ DBCleanup
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
SVar:TheFuryContinues:DB$ AddPhase | ExtraPhase$ Combat | AfterPhase$ EndCombat | ConditionDefined$ Remembered | ConditionPresent$ Card | SubAbility$ Cleanup
|
||||
SVar:Cleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
AI:RemoveDeck:All
|
||||
Oracle:Enchant creature you control\nWhen enchanted creature deals combat damage to a player, sacrifice it and attach Breath of Fury to a creature you control. If you do, untap all creatures you control and after this phase, there is an additional combat phase.
|
||||
|
||||
@@ -6,7 +6,7 @@ T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.S
|
||||
SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ c_1_1_a_thopter_flying | TokenOwner$ You
|
||||
A:AB$ Charm | Cost$ T Sac<1/Artifact> | Choices$ DBImpulse,DBPump | Defined$ You
|
||||
SVar:DBImpulse:DB$ Dig | Defined$ You | DigNum$ 1 | ChangeNum$ All | DestinationZone$ Exile | RememberChanged$ True | SubAbility$ DBEffect | SpellDescription$ Exile the top card of your library. Until the end of your next turn, you may play that card.
|
||||
SVar:DBEffect:DB$ Effect | StaticAbilities$ StaticMayPlay | Duration$ UntilTheEndOfYourNextTurn | RememberObjects$ Remembered | ForgetOnMoved$ Exile | SubAbility$ DBCleanup
|
||||
SVar:DBEffect:DB$ Effect | StaticAbilities$ StaticMayPlay | Duration$ UntilTheEndOfYourNextTurn | RememberObjects$ Remembered | ForgetOnMoved$ Exile
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
SVar:StaticMayPlay:Mode$ Continuous | Affected$ Card.IsRemembered | AffectedZone$ Exile | MayPlay$ True | Description$ Until the end of your next turn, you may play that card.
|
||||
SVar:DBPump:DB$ Pump | ValidTgts$ Creature | NumAtt$ +2 | TgtPrompt$ Select target creature. | SpellDescription$ Target creature gets +2/+0 until end of turn.
|
||||
|
||||
@@ -3,7 +3,7 @@ ManaCost:2 W B
|
||||
Types:Legendary Creature Phyrexian Cat
|
||||
PT:3/4
|
||||
T:Mode$ SpellCast | ValidCard$ Creature.Phyrexian,Creature.Artifact | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigIncubate | TriggerDescription$ Whenever you cast a Phyrexian creature or artifact creature spell, incubate X, where X is that spell's mana value. (Create an Incubator token with X +1/+1 counters on it and "{2}: Transform this artifact." It transforms into a 0/0 Phyrexian artifact creature.)
|
||||
SVar:TrigIncubate:DB$ Incubate | Amount$ TriggeredSpellAbility$CardManaCostLKI
|
||||
SVar:TrigIncubate:DB$ Incubate | Amount$ TriggeredStackInstance$CardManaCostLKI
|
||||
T:Mode$ Phase | Phase$ End of Turn | TriggerZones$ Battlefield | CheckSVar$ X | SVarCompare$ GE1 | Execute$ TrigProliferate | TriggerDescription$ At the beginning of each end step, if a Phyrexian died under your control this turn, proliferate.
|
||||
SVar:TrigProliferate:DB$ Proliferate
|
||||
SVar:X:Count$ThisTurnEntered_Graveyard_from_Battlefield_Phyrexian.YouCtrl
|
||||
|
||||
@@ -4,8 +4,8 @@ Types:Legendary Planeswalker Calix
|
||||
Loyalty:4
|
||||
A:AB$ Dig | Cost$ AddCounter<1/LOYALTY> | Planeswalker$ True | DigNum$ 4 | ChangeNum$ 1 | Optional$ True | ForceRevealToController$ True | ChangeValid$ Enchantment | RestRandomOrder$ True | SpellDescription$ Look at the top four cards of your library. You may reveal an enchantment card from among them and put that card into your hand. Put the rest on the bottom of your library in a random order.
|
||||
A:AB$ ChangeZone | Cost$ SubCounter<3/LOYALTY> | Planeswalker$ True | ValidTgts$ Creature.YouDontCtrl,Enchantment.YouDontCtrl | TgtPrompt$ Select target creature or enchantment you don't control | Origin$ Battlefield | Destination$ Exile | RememberChanged$ True | SubAbility$ DBEffect | SpellDescription$ Exile target creature or enchantment you don't control until target enchantment you control leaves the battlefield.
|
||||
SVar:DBEffect:DB$ Effect | ValidTgts$ Enchantment.YouCtrl | TgtPrompt$ Select target enchantment you control | Triggers$ ComeBack | RememberObjects$ Remembered | ImprintCards$ ThisTargetedCard | ConditionPresent$ Card.Self | Duration$ Permanent | ForgetOnMoved$ Exile | SubAbility$ DBCleanup
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
SVar:DBEffect:DB$ Effect | ValidTgts$ Enchantment.YouCtrl | TgtPrompt$ Select target enchantment you control | Triggers$ ComeBack | RememberObjects$ Remembered | ImprintCards$ ThisTargetedCard | ConditionPresent$ Card.Self | Duration$ Permanent | ForgetOnMoved$ Exile | SubAbility$ Cleanup
|
||||
SVar:Cleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
SVar:ComeBack:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.IsImprinted | Execute$ TrigReturn | TriggerZones$ Command | TriggerController$ TriggeredCardController | Static$ True | TriggerDescription$ Target is exiled until target enchantment you control leaves the battlefield.
|
||||
SVar:TrigReturn:DB$ ChangeZone | Origin$ Exile | Destination$ Battlefield | Defined$ Remembered | SubAbility$ ExileSelf
|
||||
SVar:ExileSelf:DB$ ChangeZone | Origin$ Command | Destination$ Exile | Defined$ Self
|
||||
|
||||
@@ -5,5 +5,5 @@ K:Enchant:Creature.YouDontCtrl:creature you don't control
|
||||
SVar:AttachAILogic:Curse
|
||||
S:Mode$ CantAttack | ValidCard$ Creature.EnchantedBy | Description$ Enchanted creature can't attack.
|
||||
T:Mode$ SpellCast | ValidSA$ Spell.singleTarget | ValidActivatingPlayer$ Opponent | TriggerZones$ Battlefield | OrderDuplicates$ True | Execute$ TrigChangeTarget | TriggerDescription$ Whenever an opponent casts a spell, if it has a single target, change the target to enchanted creature if able.
|
||||
SVar:TrigChangeTarget:DB$ ChangeTargets | TargetType$ Spell | Defined$ TriggeredSourceSA | DefinedMagnet$ Enchanted
|
||||
SVar:TrigChangeTarget:DB$ ChangeTargets | TargetType$ Spell | Defined$ TriggeredStackInstance | DefinedMagnet$ Enchanted
|
||||
Oracle:Enchant creature you don't control\nEnchanted creature can't attack.\nWhenever an opponent casts a spell, if it has a single target, change the target to enchanted creature if able.
|
||||
|
||||
@@ -6,6 +6,6 @@ T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Any | Destination$ Battlefi
|
||||
SVar:TrigDiscard:DB$ Discard | AnyNumber$ True | Optional$ True | Mode$ TgtChoose | RememberDiscarded$ True | SubAbility$ DBDraw
|
||||
SVar:DBDraw:DB$ Draw | Defined$ You | NumCards$ Remembered$Amount/Plus.1 | SubAbility$ DBCleanup
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
T:Mode$ ChangesZoneAll | ValidCards$ Creature.Other+YouCtrl+wasCastFromGraveyard,Creature.Other+YouCtrl+ThisTurnEnteredFrom_Graveyard | Destination$ Battlefield | TriggerZones$ Battlefield | Execute$ TrigPutCounters | TriggerDescription$ Whenever one or more other creatures you control enter, if one or more of them entered from a graveyard or was cast from a graveyard, put a +1/+1 counter on each creature you control.
|
||||
T:Mode$ ChangesZoneAll | ValidCards$ Creature.YouCtrl+wasCastFromGraveyard,Creature.YouCtrl+ThisTurnEnteredFrom_Graveyard | Destination$ Battlefield | TriggerZones$ Battlefield | Execute$ TrigPutCounters | TriggerDescription$ Whenever one or more creatures you control enter, if one or more of them entered from a graveyard or was cast from a graveyard, put a +1/+1 counter on each creature you control.
|
||||
SVar:TrigPutCounters:DB$ PutCounterAll | ValidCards$ Creature.YouCtrl | CounterType$ P1P1 | CounterNum$ 1
|
||||
Oracle:When Celes enters, discard any number of cards, then draw that many cards plus one.\nWhenever one or more other creatures you control enter, if one or more of them entered from a graveyard or was cast from a graveyard, put a +1/+1 counter on each creature you control.
|
||||
Oracle:When Celes enters, discard any number of cards, then draw that many cards plus one.\nWhenever one or more creatures you control enter, if one or more of them entered from a graveyard or was cast from a graveyard, put a +1/+1 counter on each creature you control.
|
||||
|
||||
@@ -5,7 +5,7 @@ PT:3/3
|
||||
K:Flying
|
||||
T:Mode$ SpellCast | ValidCard$ Spirit,Arcane | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigDestroyAll | TriggerDescription$ Whenever you cast a Spirit or Arcane spell, destroy all permanents with that spell's mana value.
|
||||
SVar:TrigDestroyAll:DB$ DestroyAll | ValidCards$ Permanent.cmcEQX
|
||||
SVar:X:TriggeredSpellAbility$CardManaCostLKI
|
||||
SVar:X:TriggeredStackInstance$CardManaCostLKI
|
||||
AI:RemoveDeck:All
|
||||
AI:RemoveDeck:Random
|
||||
DeckHints:Type$Spirit|Arcane
|
||||
|
||||
@@ -4,7 +4,7 @@ Types:Creature Phyrexian Shark
|
||||
PT:2/4
|
||||
K:Flying
|
||||
T:Mode$ SpellCast | ValidCard$ Card.nonCreature | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigIncubate | TriggerDescription$ Whenever you cast a noncreature spell, incubate X, where X is that spell's mana value. (Create an Incubator token with X +1/+1 counters on it and "{2}: Transform this artifact." It transforms into a 0/0 Phyrexian artifact creature.)
|
||||
SVar:TrigIncubate:DB$ Incubate | Amount$ TriggeredSpellAbility$CardManaCostLKI
|
||||
SVar:TrigIncubate:DB$ Incubate | Amount$ TriggeredStackInstance$CardManaCostLKI
|
||||
DeckHints:Type$Instant|Sorcery
|
||||
DeckHas:Ability$Token|Counters & Type$Artifact
|
||||
Oracle:Flying\nWhenever you cast a noncreature spell, incubate X, where X is that spell's mana value. (Create an Incubator token with X +1/+1 counters on it and "{2}: Transform this artifact." It transforms into a 0/0 Phyrexian artifact creature.)
|
||||
|
||||
@@ -5,5 +5,5 @@ PT:4/4
|
||||
K:Flying
|
||||
T:Mode$ SpellCast | ValidCard$ Spirit,Arcane | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | OptionalDecider$ You | Execute$ TrigMill | TriggerDescription$ Whenever you cast a Spirit or Arcane spell, you may have target player mill X cards, where X is that spell's mana value.
|
||||
SVar:TrigMill:DB$ Mill | ValidTgts$ Player | TgtPrompt$ Select target player | NumCards$ X
|
||||
SVar:X:TriggeredSpellAbility$CardManaCostLKI
|
||||
SVar:X:TriggeredStackInstance$CardManaCostLKI
|
||||
Oracle:Flying\nWhenever you cast a Spirit or Arcane spell, you may have target player mill X cards, where X is that spell's mana value.
|
||||
|
||||
@@ -7,6 +7,6 @@ T:Mode$ AttackerBlockedByCreature | ValidCard$ Card.Self | ValidBlocker$ Creatur
|
||||
SVar:DelTrigAttacker:DB$ DelayedTrigger | Mode$ Phase | Phase$ EndCombat | ValidPlayer$ Player | Execute$ TrigRem | RememberObjects$ TriggeredAttackerLKICopy | TriggerDescription$ Destroy all Equipment attached to blocked creature at end of combat.
|
||||
SVar:DelTrigBlocker:DB$ DelayedTrigger | Mode$ Phase | Phase$ EndCombat | ValidPlayer$ Player | Execute$ TrigRem | RememberObjects$ TriggeredBlockerLKICopy | TriggerDescription$ Destroy all Equipment attached to blocking creature at end of combat.
|
||||
SVar:TrigRem:DB$ Pump | RememberObjects$ DelayTriggerRememberedLKI | SubAbility$ TrigDestroy
|
||||
SVar:TrigDestroy:DB$ DestroyAll | ValidCards$ Remembered.Equipment+Attached | SpellDescription$ Destroy all Equipment attached to that creature. | SubAbility$ DBCleanup
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
SVar:TrigDestroy:DB$ DestroyAll | ValidCards$ Remembered.Equipment+Attached | SpellDescription$ Destroy all Equipment attached to that creature. | SubAbility$ Cleanup
|
||||
SVar:Cleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
Oracle:Whenever Corrosive Ooze blocks or becomes blocked by an equipped creature, destroy all Equipment attached to that creature at end of combat.
|
||||
|
||||
@@ -4,7 +4,7 @@ Types:Legendary Creature Human Wizard
|
||||
PT:3/3
|
||||
T:Mode$ SpellCastOrCopy | ValidCard$ Instant,Sorcery | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigToken | TriggerDescription$ Magecraft — Whenever you cast or copy an instant or sorcery spell, create a 0/0 green and blue Fractal creature token. Put X +1/+1 counters on it, where X is that spell's mana value.
|
||||
SVar:TrigToken:DB$ Token | TokenScript$ gu_0_0_fractal | RememberTokens$ True | SubAbility$ DBPutCounter
|
||||
SVar:DBPutCounter:DB$ PutCounter | Defined$ Remembered | CounterType$ P1P1 | CounterNum$ TriggeredSpellAbility$CardManaCostLKI | SubAbility$ DBCleanup
|
||||
SVar:DBPutCounter:DB$ PutCounter | Defined$ Remembered | CounterType$ P1P1 | CounterNum$ TriggeredStackInstance$CardManaCostLKI | SubAbility$ DBCleanup
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
A:AB$ Effect | Cost$ 3 U | ValidTgts$ Creature.token | TgtPrompt$ Select target creature token | RememberObjects$ Targeted | ExileOnMoved$ Battlefield | StaticAbilities$ Unblockable | StackDescription$ {c:Targeted} can't be blocked this turn. | SpellDescription$ Target creature token can't be blocked this turn.
|
||||
SVar:Unblockable:Mode$ CantBlockBy | ValidAttacker$ Card.IsRemembered | Description$ This creature can't be blocked this turn.
|
||||
|
||||
@@ -5,9 +5,10 @@ PT:3/5
|
||||
K:Devoid
|
||||
K:Kicker:C
|
||||
T:Mode$ SpellCast | ValidCard$ Card.Self | Execute$ TrigCharm | TriggerDescription$ When you cast this spell, ABILITY
|
||||
SVar:TrigCharm:DB$ Charm | CharmNum$ Count$Kicked.2.1 | Choices$ DBChangeZone,DBDraw | AdditionalDescription$ If CARDNAME was kicked, choose both instead.
|
||||
SVar:TrigCharm:DB$ Charm | CharmNum$ Count$Compare X GE1.2.1 | Choices$ DBChangeZone,DBDraw | AdditionalDescription$ If CARDNAME was kicked, choose both instead.
|
||||
SVar:DBChangeZone:DB$ ChangeZone | ValidTgts$ Creature | TgtPrompt$ Select target creature | Origin$ Battlefield | Destination$ Hand | SpellDescription$ Return target creature to its owner's hand.
|
||||
SVar:DBDraw:DB$ Draw | NumCards$ 2 | ValidTgts$ Player | TgtPrompt$ Select target player to draw two cards, then discard a card | Defined$ ThisTargetedPlayer | SubAbility$ DBDiscard | SpellDescription$ Target player draws two cards, then discards a card.
|
||||
SVar:DBDiscard:DB$ Discard | Defined$ ParentTarget | NumCards$ 1 | Mode$ TgtChoose
|
||||
SVar:X:Count$Valid Card.Self+kicked
|
||||
SVar:PlayMain1:TRUE
|
||||
Oracle:Devoid (This card has no color.)\nKicker {C} (You may pay an additional {C} as you cast this spell.)\nWhen you cast this spell, choose one. If it was kicked, choose both instead.\n• Return target creature to its owner's hand.\n• Target player draws two cards, then discards a card.
|
||||
|
||||
@@ -4,5 +4,5 @@ Types:Enchantment
|
||||
T:Mode$ SpellCast | ValidCard$ Card.nonCreature | TriggerZones$ Battlefield | Execute$ TrigCounter | TriggerDescription$ Whenever a player casts a noncreature spell, counter that spell. That player creates X 1/1 white and blue Bird creature tokens with flying, where X is the spell's mana value.
|
||||
SVar:TrigCounter:DB$ Counter | Defined$ TriggeredSpellAbility | SubAbility$ DBToken
|
||||
SVar:DBToken:DB$ Token | TokenAmount$ X | TokenScript$ wu_1_1_bird_flying | TokenOwner$ TriggeredActivator
|
||||
SVar:X:TriggeredSpellAbility$CardManaCostLKI
|
||||
SVar:X:TriggeredStackInstance$CardManaCostLKI
|
||||
Oracle:({W/U} can be paid with either {W} or {U}.)\nWhenever a player casts a noncreature spell, counter that spell. That player creates X 1/1 white and blue Bird creature tokens with flying, where X is the spell's mana value.
|
||||
|
||||
@@ -3,7 +3,7 @@ ManaCost:1 R
|
||||
Types:Creature Ogre
|
||||
PT:3/3
|
||||
T:Mode$ SpellCast | ValidCard$ Artifact | Execute$ TrigControl | TriggerZones$ Battlefield | TriggerDescription$ Whenever a player casts an artifact spell, that player gains control of CARDNAME. (This effect lasts indefinitely.)
|
||||
SVar:TrigControl:DB$ GainControl | Defined$ Self | NewController$ TriggeredActivator
|
||||
SVar:TrigControl:DB$ GainControl | Defined$ Self | NewController$ TriggeredPlayer
|
||||
AI:RemoveDeck:Random
|
||||
SVar:AntiBuffedBy:Artifact
|
||||
Oracle:Whenever a player casts an artifact spell, that player gains control of Drooling Ogre. (This effect lasts indefinitely.)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user