...
 
Commits (410)

Too many changes to show.

To preserve performance only 1000 of 1000+ files are displayed.

Summary
(Summarize the bug encountered concisely)
Steps to reproduce
(How one can reproduce the issue - this is very important. Specific cards and specific actions especially)
Which version of Forge are you on (Release, Snapshot? Desktop, Android?)
What is the current bug behavior?
(What actually happens)
What is the expected correct behavior?
(What you should see instead)
Relevant logs and/or screenshots
(Paste/Attach your game.log from the crash - please use code blocks (```)) Also, provide screenshots of the current state.
Possible fixes
(If you can, link to the line of code that might be responsible for the problem)
/label ~needs-investigation
\ No newline at end of file
Summary
(Summarize the feature you wish concisely)
Example screenshots
(If this is a UI change, please provide an example screenshot of how this feature might work)
Feature type
(Where in Forge does this belong? e.g. Quest Mode, Deck Editor, Limited, Constructed, etc.)
/label ~feature request
\ No newline at end of file
......@@ -121,13 +121,17 @@ The Proguard included with the Android SDK Build-tools is outdated and does not
The Eclipse plug-ins do NOT support building things for Android. They do however allow you to use the debugger so you can still set breakpoints and trace
things out. The steps below show how to generate a debug Android build.
Right-click on the forge-gui-android 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
On the Main tab, set Goals: install, Profiles: android-debug
On the Environment tab, you may need to define the variable ANDROID_HOME with the value containing the path to your Android SDK installation.
For example, Variable: ANDROID_HOME, Value: Your Android SDK install path here.
2) Run forge Maven build. If everything built, you should see "BUILD SUCCESS" in the Console View.
You should now be able to "run" the forge-gui-android Maven build. This may take a few minutes. If everything worked, you should see "BUILD SUCCESS" in the Console View.
3) Right-click on the forge-gui-android project. Run as.. > Maven build...
- On the Main tab, set Goals: install, Profiles: android-debug
- On the Environment tab, you may need to define the variable ANDROID_HOME with the value containing the path to your Android SDK installation. For example, Variable: ANDROID_HOME, Value: Your Android SDK install path here.
4) Run the forge-gui-android Maven build. This may take a few minutes. If everything worked, you should see "BUILD SUCCESS" in the Console View.
Assuming you got this far, you should have an Android forge-android-[version].apk in the forge-gui-android/target path.
......@@ -152,6 +156,17 @@ Assuming the apk is installed, launch it from the device.
In Eclipse, launch the DDMS. Window > Perspective > Open Perspective > Other... > DDMS. You should see the forge app in the list. Highlight the app, click on the green debug button and a
green debug button should appear next to the app's name. You can now set breakpoints and step through the source code.
## Windows / Linux SNAPSHOT build
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...
- 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.
The resulting snapshot will be found at: forge-gui-desktop/target/forge-gui-desktop-[version]-SNAPSHOT
# IntelliJ
TBD
......
......@@ -6,7 +6,7 @@
<parent>
<artifactId>forge</artifactId>
<groupId>forge</groupId>
<version>1.6.22-SNAPSHOT</version>
<version>1.6.24-SNAPSHOT</version>
</parent>
<artifactId>forge-ai</artifactId>
......
......@@ -18,12 +18,10 @@
package forge.ai;
import com.esotericsoftware.minlog.Log;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import forge.ai.ability.ChangeZoneAi;
import forge.ai.ability.ExploreAi;
import forge.ai.simulation.SpellAbilityPicker;
......@@ -1784,75 +1782,6 @@ public class AiController {
throw new UnsupportedOperationException("AI is not supposed to reach this code at the moment");
}
public Map<GameEntity, CounterType> chooseProliferation(final SpellAbility sa) {
final Map<GameEntity, CounterType> result = Maps.newHashMap();
final List<Player> allies = player.getAllies();
allies.add(player);
final List<Player> enemies = player.getOpponents();
final Function<Card, CounterType> predProliferate = new Function<Card, CounterType>() {
@Override
public CounterType apply(Card crd) {
//fast way out, no need to check other stuff
if (!crd.hasCounters()) {
return null;
}
// cards controlled by ai or ally with Vanishing or Fading
// and exaclty one counter of the specifice type gets high priority to keep the card
if (allies.contains(crd.getController())) {
// except if its a Chronozoa, because it WANTS to be removed to make more
if (crd.hasKeyword(Keyword.VANISHING) && !"Chronozoa".equals(crd.getName())) {
if (crd.getCounters(CounterType.TIME) == 1) {
return CounterType.TIME;
}
} else if (crd.hasKeyword(Keyword.FADING)) {
if (crd.getCounters(CounterType.FADE) == 1) {
return CounterType.FADE;
}
}
}
for (final Entry<CounterType, Integer> c1 : crd.getCounters().entrySet()) {
// if card can not recive the given counter, try another one
if (!crd.canReceiveCounters(c1.getKey())) {
continue;
}
if (ComputerUtil.isNegativeCounter(c1.getKey(), crd) && enemies.contains(crd.getController())) {
return c1.getKey();
}
if (!ComputerUtil.isNegativeCounter(c1.getKey(), crd) && allies.contains(crd.getController())) {
return c1.getKey();
}
}
return null;
}
};
for (Card c : game.getCardsIn(ZoneType.Battlefield)) {
CounterType ct = predProliferate.apply(c);
if (ct != null)
result.put(c, ct);
}
for (Player e : enemies) {
// TODO In the future check of enemies can get poison counters and give them some other bad counter type
if (e.getCounters(CounterType.POISON) > 0) {
result.put(e, CounterType.POISON);
}
}
for (Player pl : allies) {
if (pl.getCounters(CounterType.EXPERIENCE) > 0) {
result.put(pl, CounterType.EXPERIENCE);
} else if (pl.getCounters(CounterType.ENERGY) > 0) {
result.put(pl, CounterType.ENERGY);
}
}
return result;
}
public CardCollection chooseCardsForEffect(CardCollectionView pool, SpellAbility sa, int min, int max, boolean isOptional) {
if (sa == null || sa.getApi() == null) {
throw new UnsupportedOperationException();
......
......@@ -1455,7 +1455,7 @@ public class ComputerUtilCombat {
continue;
}
if (ability.hasParam("Adapt") && blocker != null && blocker.getCounters(CounterType.P1P1) > 0) {
if (ability.hasParam("Adapt") && attacker.getCounters(CounterType.P1P1) > 0) {
continue;
}
......@@ -1692,7 +1692,7 @@ public class ComputerUtilCombat {
continue;
}
if (ability.hasParam("Adapt") && blocker.getCounters(CounterType.P1P1) > 0) {
if (ability.hasParam("Adapt") && attacker.getCounters(CounterType.P1P1) > 0) {
continue;
}
......
......@@ -74,6 +74,7 @@ public abstract class GameState {
private final Map<Card, Integer> cardToEnchantPlayerId = new HashMap<>();
private final Map<Card, Integer> markedDamage = new HashMap<>();
private final Map<Card, List<String>> cardToChosenClrs = new HashMap<>();
private final Map<Card, CardCollection> cardToChosenCards = new HashMap<>();
private final Map<Card, String> cardToChosenType = new HashMap<>();
private final Map<Card, List<String>> cardToRememberedId = new HashMap<>();
private final Map<Card, List<String>> cardToImprintedId = new HashMap<>();
......@@ -98,6 +99,8 @@ public abstract class GameState {
private int turn = 1;
private boolean removeSummoningSickness = false;
// Targeting for precast spells in a game state (mostly used by Puzzle Mode game states)
private final int TARGET_NONE = -1; // untargeted spell (e.g. Joraga Invocation)
private final int TARGET_HUMAN = -2;
......@@ -211,6 +214,10 @@ public abstract class GameState {
// Remember the IDs of imprinted cards
cardsReferencedByID.add(i);
}
for (Card i : card.getChosenCards()) {
// Remember the IDs of chosen cards
cardsReferencedByID.add(i);
}
if (game.getCombat() != null && game.getCombat().isAttacking(card)) {
// Remember the IDs of attacked planeswalkers
GameEntity def = game.getCombat().getDefenderByAttacker(card);
......@@ -312,6 +319,17 @@ public abstract class GameState {
newText.append("|NamedCard:").append(c.getNamedCard());
}
List<String> chosenCardIds = Lists.newArrayList();
for (Object obj : c.getChosenCards()) {
if (obj instanceof Card) {
int id = ((Card)obj).getId();
chosenCardIds.add(String.valueOf(id));
}
}
if (!chosenCardIds.isEmpty()) {
newText.append("|ChosenCards:").append(TextUtil.join(chosenCardIds, ","));
}
List<String> rememberedCardIds = Lists.newArrayList();
for (Object obj : c.getRemembered()) {
if (obj instanceof Card) {
......@@ -432,6 +450,10 @@ public abstract class GameState {
turn = Integer.parseInt(categoryValue);
}
else if (categoryName.equals("removesummoningsickness")) {
removeSummoningSickness = categoryValue.equalsIgnoreCase("true");
}
else if (categoryName.endsWith("life")) {
if (isHuman)
humanLife = Integer.parseInt(categoryValue);
......@@ -552,6 +574,7 @@ public abstract class GameState {
cardToExiledWithId.clear();
markedDamage.clear();
cardToChosenClrs.clear();
cardToChosenCards.clear();
cardToChosenType.clear();
cardToScript.clear();
cardAttackMap.clear();
......@@ -605,6 +628,12 @@ public abstract class GameState {
game.getPhaseHandler().devAdvanceToPhase(advPhase);
}
if (removeSummoningSickness) {
for (Card card : game.getCardsInGame()) {
card.setSickness(false);
}
}
game.getAction().checkStateEffects(true); //ensure state based effects and triggers are updated
}
......@@ -947,6 +976,12 @@ public abstract class GameState {
Card c = entry.getKey();
c.setNamedCard(entry.getValue());
}
// Chosen cards
for (Entry<Card, CardCollection> entry : cardToChosenCards.entrySet()) {
Card c = entry.getKey();
c.setChosenCards(entry.getValue());
}
}
private void handleCardAttachments() {
......@@ -1047,7 +1082,6 @@ public abstract class GameState {
zone.setCards(kv.getValue());
}
}
}
/**
......@@ -1143,6 +1177,13 @@ public abstract class GameState {
cardToChosenClrs.put(c, Arrays.asList(info.substring(info.indexOf(':') + 1).split(",")));
} else if (info.startsWith("ChosenType:")) {
cardToChosenType.put(c, info.substring(info.indexOf(':') + 1));
} else if (info.startsWith("ChosenCards:")) {
CardCollection chosen = new CardCollection();
String[] idlist = info.substring(info.indexOf(':') + 1).split(",");
for (String id : idlist) {
chosen.add(idToCard.get(Integer.parseInt(id)));
}
cardToChosenCards.put(c, chosen);
} else if (info.startsWith("NamedCard:")) {
cardToNamedCard.put(c, info.substring(info.indexOf(':') + 1));
} else if (info.startsWith("ExecuteScript:")) {
......
......@@ -953,11 +953,6 @@ public class PlayerControllerAi extends PlayerController {
return true;
}
@Override
public Map<GameEntity, CounterType> chooseProliferation(SpellAbility sa) {
return brains.chooseProliferation(sa);
}
@Override
public boolean chooseTargetsFor(SpellAbility currentAbility) {
return brains.doTrigger(currentAbility, true);
......
......@@ -21,6 +21,7 @@ public enum SpellApiToAi {
.put(ApiType.AddOrRemoveCounter, CountersPutOrRemoveAi.class)
.put(ApiType.AddPhase, AddPhaseAi.class)
.put(ApiType.AddTurn, AddTurnAi.class)
.put(ApiType.Amass, AmassAi.class)
.put(ApiType.Animate, AnimateAi.class)
.put(ApiType.AnimateAll, AnimateAllAi.class)
.put(ApiType.Attach, AttachAi.class)
......
package forge.ai.ability;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import forge.ai.ComputerUtilCard;
import forge.ai.SpellAbilityAi;
import forge.game.Game;
import forge.game.ability.AbilityUtils;
import forge.game.card.*;
import forge.game.card.token.TokenInfo;
import forge.game.phase.PhaseHandler;
import forge.game.player.Player;
import forge.game.player.PlayerActionConfirmMode;
import forge.game.spellability.SpellAbility;
import forge.game.zone.ZoneType;
public class AmassAi extends SpellAbilityAi {
@Override
protected boolean checkApiLogic(Player ai, final SpellAbility sa) {
CardCollection aiArmies = CardLists.filter(ai.getCardsIn(ZoneType.Battlefield), CardPredicates.isType("Army"));
Card host = sa.getHostCard();
final Game game = ai.getGame();
if (!aiArmies.isEmpty()) {
if (CardLists.count(aiArmies, CardPredicates.canReceiveCounters(CounterType.P1P1)) <= 0) {
return false;
}
} else {
final String tokenScript = "b_0_0_zombie_army";
final int amount = AbilityUtils.calculateAmount(host, sa.getParamOrDefault("Num", "1"), sa);
Card token = TokenInfo.getProtoType(tokenScript, sa);
if (token == null) {
return false;
}
token.setController(ai, 0);
token.setLastKnownZone(ai.getZone(ZoneType.Battlefield));
boolean result = true;
// need to check what the cards would be on the battlefield
// do not attach yet, that would cause Events
CardCollection preList = new CardCollection(token);
game.getAction().checkStaticAbilities(false, Sets.newHashSet(token), preList);
if (token.canReceiveCounters(CounterType.P1P1)) {
token.setCounters(CounterType.P1P1, amount);
}
if (token.isCreature() && token.getNetToughness() < 1) {
result = false;
}
//reset static abilities
game.getAction().checkStaticAbilities(false);
if (!result) {
<