mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-15 18:28:00 +00:00
Merge branch 'master' into master2
This commit is contained in:
@@ -436,6 +436,13 @@ public abstract class GameState {
|
||||
}
|
||||
}
|
||||
|
||||
if (!c.getUnlockedRooms().isEmpty()) {
|
||||
for (CardStateName stateName : c.getUnlockedRooms()) {
|
||||
newText.append("|UnlockedRoom:");
|
||||
newText.append(stateName.name());
|
||||
}
|
||||
}
|
||||
|
||||
cardTexts.put(zoneType, newText.toString());
|
||||
}
|
||||
|
||||
@@ -1401,6 +1408,8 @@ public abstract class GameState {
|
||||
c.setGamePieceType(GamePieceType.TOKEN);
|
||||
} else if (info.startsWith("ClassLevel:")) {
|
||||
c.setClassLevel(Integer.parseInt(info.substring(info.indexOf(':') + 1)));
|
||||
} else if (info.startsWith("UnlockedRoom:")) {
|
||||
c.unlockRoom(c.getController(), CardStateName.smartValueOf(info.substring(info.indexOf(':') + 1)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -160,6 +160,9 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
return SpecialCardAi.MazesEnd.consider(aiPlayer, sa);
|
||||
} else if (aiLogic.equals("Pongify")) {
|
||||
return sa.isTargetNumberValid(); // Pre-targeted in checkAiLogic
|
||||
} else if (aiLogic.equals("ReturnCastable")) {
|
||||
return !sa.getHostCard().getExiledCards().isEmpty()
|
||||
&& ComputerUtilMana.canPayManaCost(sa.getHostCard().getExiledCards().getFirst().getFirstSpellAbility(), aiPlayer, 0, false);
|
||||
}
|
||||
}
|
||||
if (sa.isHidden()) {
|
||||
|
||||
@@ -1,25 +1,12 @@
|
||||
package forge.ai.ability;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
import forge.ai.AiAttackController;
|
||||
import forge.ai.ComputerUtilAbility;
|
||||
import forge.ai.ComputerUtilCard;
|
||||
import forge.ai.ComputerUtilCombat;
|
||||
import forge.ai.SpellAbilityAi;
|
||||
import com.google.common.collect.Lists;
|
||||
import forge.ai.*;
|
||||
import forge.game.Game;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.card.CardPredicates;
|
||||
import forge.game.card.*;
|
||||
import forge.game.card.CardPredicates.Presets;
|
||||
import forge.game.card.CounterEnumType;
|
||||
import forge.game.combat.Combat;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.phase.PhaseHandler;
|
||||
@@ -30,6 +17,10 @@ import forge.game.spellability.SpellAbility;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.Aggregates;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class ChooseCardAi extends SpellAbilityAi {
|
||||
|
||||
/**
|
||||
@@ -58,11 +49,15 @@ public class ChooseCardAi extends SpellAbilityAi {
|
||||
protected boolean checkAiLogic(final Player ai, final SpellAbility sa, final String aiLogic) {
|
||||
final Card host = sa.getHostCard();
|
||||
final Game game = ai.getGame();
|
||||
ZoneType choiceZone = ZoneType.Battlefield;
|
||||
|
||||
List<ZoneType> choiceZone;
|
||||
if (sa.hasParam("ChoiceZone")) {
|
||||
choiceZone = ZoneType.smartValueOf(sa.getParam("ChoiceZone"));
|
||||
choiceZone = ZoneType.listValueOf(sa.getParam("ChoiceZone"));
|
||||
} else {
|
||||
choiceZone = Lists.newArrayList(ZoneType.Battlefield);
|
||||
}
|
||||
CardCollectionView choices = game.getCardsIn(choiceZone);
|
||||
|
||||
if (sa.hasParam("Choices")) {
|
||||
choices = CardLists.getValidCards(choices, sa.getParam("Choices"), host.getController(), host, sa);
|
||||
}
|
||||
@@ -129,6 +124,13 @@ public class ChooseCardAi extends SpellAbilityAi {
|
||||
ownChoices = CardLists.filter(choices, CardPredicates.isControlledByAnyOf(ai.getAllies()));
|
||||
}
|
||||
return !ownChoices.isEmpty();
|
||||
} else if (aiLogic.equals("GoodCreature")) {
|
||||
for (Card choice : choices) {
|
||||
if (choice.isCreature() && ComputerUtilCard.evaluateCreature(choice) >= 250) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -81,6 +81,13 @@ public class DrawAi extends SpellAbilityAi {
|
||||
if (!canLoot(ai, sa)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ComputerUtilCost.isSacrificeSelfCost(sa.getPayCosts())) {
|
||||
// Canopy lands and other cards that sacrifice themselves to draw cards
|
||||
return ai.getCardsIn(ZoneType.Hand).isEmpty()
|
||||
|| (sa.getHostCard().isLand() && ai.getLandsInPlay().size() >= 5); // TODO: make this configurable in the AI profile
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,9 @@ public class LifeGainAi extends SpellAbilityAi {
|
||||
|
||||
if (!lifeCritical) {
|
||||
// return super.willPayCosts(ai, sa, cost, source);
|
||||
if ("CriticalOnly".equals(sa.getParam("AILogic"))) {
|
||||
return false;
|
||||
}
|
||||
if (!ComputerUtilCost.checkSacrificeCost(ai, cost, source, sa, false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ Types:Legendary Creature Elf Warrior
|
||||
PT:3/3
|
||||
S:Mode$ Continuous | Affected$ Card.TopLibrary+YouCtrl | AffectedZone$ Library | MayLookAt$ You | Description$ You may look at the top card of your library any time.
|
||||
S:Mode$ Continuous | Affected$ Creature.TopLibrary+YouCtrl+nonLand | AffectedZone$ Library | MayPlay$ True | Description$ You may cast creature spells from the top of your library.
|
||||
A:AB$ ChooseCard | Cost$ G T tapXType<2/Creature> | ChoiceZone$ Hand,Library | PlayerTurn$ True | Reveal$ True | Choices$ Card.TopLibrary+YouOwn,Card.YouOwn+inZoneHand | SubAbility$ DBChangeZone | ChoiceTitle$ Reveal a card from your hand or the top of your library | SpellDescription$ Reveal a card from your hand or the top of your library. If you reveal a creature card this way, put it onto the battlefield. Activate only during your turn.
|
||||
A:AB$ ChooseCard | Cost$ G T tapXType<2/Creature> | ChoiceZone$ Hand,Library | PlayerTurn$ True | Reveal$ True | Choices$ Card.TopLibrary+YouOwn,Card.YouOwn+inZoneHand | SubAbility$ DBChangeZone | ChoiceTitle$ Reveal a card from your hand or the top of your library | AILogic$ GoodCreature | SpellDescription$ Reveal a card from your hand or the top of your library. If you reveal a creature card this way, put it onto the battlefield. Activate only during your turn.
|
||||
SVar:DBChangeZone:DB$ ChangeZone | Defined$ ChosenCard | Origin$ Library,Hand | ConditionDefined$ ChosenCard | ConditionPresent$ Creature | Destination$ Battlefield | SubAbility$ DBCleanup
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearChosenCard$ True
|
||||
Oracle:You may look at the top of your library at any time.\nYou may cast creature spells from the top of your library.\n{G}, {T}, Tap two untapped creatures you control: Reveal a card from your hand or the top of your library. If you reveal a creature card this way, put it onto the battlefield. Activate only during your turn.
|
||||
|
||||
@@ -4,7 +4,7 @@ Types:Land
|
||||
T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Any | Destination$ Battlefield | OptionalDecider$ You | Execute$ TrigExile | TriggerDescription$ Imprint — When CARDNAME enters, you may exile a colorless card with mana value 7 or greater from your hand.
|
||||
SVar:TrigExile:DB$ ChangeZone | Origin$ Hand | Destination$ Exile | ChangeType$ Card.Colorless+cmcGE7 | ChangeNum$ 1
|
||||
A:AB$ Mana | Cost$ T | Produced$ C | Amount$ X | SpellDescription$ Add {C}. If a card is exiled with CARDNAME, add {C}{C} instead.
|
||||
A:AB$ ChangeZone | Cost$ T | Defined$ ExiledWith | Origin$ Exile | Destination$ Hand | SpellDescription$ Return the exiled card to its owner's hand.
|
||||
A:AB$ ChangeZone | Cost$ T | Defined$ ExiledWith | Origin$ Exile | Destination$ Hand | AILogic$ ReturnCastable | SpellDescription$ Return the exiled card to its owner's hand.
|
||||
SVar:X:Count$Compare Y GE1.2.1
|
||||
SVar:Y:Count$ValidExile Card.ExiledWithSource
|
||||
Oracle:Imprint — When Ugin's Labyrinth enters, you may exile a colorless card with mana value 7 or greater from your hand.\n{T}: Add {C}. If a card is exiled with Ugin's Labyrinth, add {C}{C} instead.\n{T}: Return the exiled card to its owner's hand.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Name:Zuran Orb
|
||||
ManaCost:0
|
||||
Types:Artifact
|
||||
A:AB$ GainLife | Cost$ Sac<1/Land> | LifeAmount$ 2 | SpellDescription$ You gain 2 life.
|
||||
A:AB$ GainLife | Cost$ Sac<1/Land> | LifeAmount$ 2 | AILogic$ CriticalOnly | SpellDescription$ You gain 2 life.
|
||||
SVar:NonStackingEffect:True
|
||||
Oracle:Sacrifice a land: You gain 2 life.
|
||||
|
||||
17
forge-gui/res/puzzle/PS_DSK3.pzl
Normal file
17
forge-gui/res/puzzle/PS_DSK3.pzl
Normal file
@@ -0,0 +1,17 @@
|
||||
[metadata]
|
||||
Name:Possibility Storm - Duskmourn: House of Horror #03
|
||||
URL:https://i0.wp.com/www.possibilitystorm.com/wp-content/uploads/2024/10/latest-scaled.jpg?ssl=1
|
||||
Goal:Win
|
||||
Turns:1
|
||||
Difficulty:Uncommon
|
||||
Description:Win this turn. Smoky Lounge is unlocked, and you start the puzzle with its ability on the stack. (Misty Salon is locked.) Ensure your solution satisfies all possible blocking decisions. Good luck!
|
||||
[state]
|
||||
turn=1
|
||||
activeplayer=p0
|
||||
activephase=DRAW
|
||||
activephaseadvance=MAIN1
|
||||
p0life=20
|
||||
p0hand=Painter's Studio // Defaced Gallery;Song of Totentanz;Ghostly Dancers;Dollmaker's Shop // Porcelain Gallery
|
||||
p0battlefield=Marina Vendrell;T:everywhere;T:everywhere;T:everywhere;T:everywhere;T:everywhere;T:everywhere;Smoky Lounge // Misty Salon|UnlockedRoom:LeftSplit
|
||||
p1life=12
|
||||
p1battlefield=Miasma Demon;Miasma Demon
|
||||
@@ -68,14 +68,23 @@
|
||||
<file src="forge.sh" targetdir="$INSTALL_PATH/" override="true">
|
||||
<additionaldata key="permission.file" value="775"/>
|
||||
</file>
|
||||
<file src="forge.command" targetdir="$INSTALL_PATH/" override="true">
|
||||
<additionaldata key="permission.file" value="775"/>
|
||||
</file>
|
||||
<file src="forge-adventure.sh" targetdir="$INSTALL_PATH/" override="true">
|
||||
<additionaldata key="permission.file" value="775"/>
|
||||
</file>
|
||||
<file src="forge-adventure.command" targetdir="$INSTALL_PATH/" override="true">
|
||||
<additionaldata key="permission.file" value="775"/>
|
||||
</file>
|
||||
<file src="adventure-editor.sh" targetdir="$INSTALL_PATH/" override="true">
|
||||
<additionaldata key="permission.file" value="775"/>
|
||||
</file>
|
||||
<file src="adventure-editor.command" targetdir="$INSTALL_PATH/" override="true">
|
||||
<additionaldata key="permission.file" value="775"/>
|
||||
</file>
|
||||
<executable stage="never" failure="ignore" keep="true">
|
||||
<fileset targetdir="$INSTALL_PATH/" includes="forge.sh,forge-adventure.sh,adventure-editor.sh"/>
|
||||
<fileset targetdir="$INSTALL_PATH/" includes="forge.sh,forge.command,forge-adventure.sh,forge-adventure.command,adventure-editor.sh,adventure-editor.command"/>
|
||||
</executable>
|
||||
</pack>
|
||||
</packs>
|
||||
|
||||
@@ -206,13 +206,16 @@
|
||||
<chmod file="${project.build.directory}/${project.build.finalName}/forge-adventure.exe" perm="a+rx" />
|
||||
<chmod file="${project.build.directory}/${project.build.finalName}/adventure-editor.exe" perm="a+rx" />
|
||||
<copy todir="${basedir}/target">
|
||||
<fileset dir="${project.build.directory}/../../forge-gui-desktop/target" includes="forge.sh" />
|
||||
<fileset dir="${project.build.directory}/../../forge-gui-mobile-dev/target" includes="forge-adventure.sh" />
|
||||
<fileset dir="${project.build.directory}/../../adventure-editor/target" includes="adventure-editor.sh" />
|
||||
<fileset dir="${project.build.directory}/../../forge-gui-desktop/target" includes="forge.sh,forge.command" />
|
||||
<fileset dir="${project.build.directory}/../../forge-gui-mobile-dev/target" includes="forge-adventure.sh,forge-adventure.command" />
|
||||
<fileset dir="${project.build.directory}/../../adventure-editor/target" includes="adventure-editor.sh,adventure-editor.command" />
|
||||
</copy>
|
||||
<chmod file="${basedir}/target/forge.sh" perm="a+rx" />
|
||||
<chmod file="${basedir}/target/forge.command" perm="a+rx" />
|
||||
<chmod file="${basedir}/target/forge-adventure.sh" perm="a+rx" />
|
||||
<chmod file="${basedir}/target/forge-adventure.command" perm="a+rx" />
|
||||
<chmod file="${basedir}/target/adventure-editor.sh" perm="a+rx" />
|
||||
<chmod file="${basedir}/target/adventure-editor.command" perm="a+rx" />
|
||||
<tar destfile="${basedir}/target/${project.build.finalName}.tar.bz2" compression="bzip2">
|
||||
<tarfileset filemode="755" dir="${project.build.directory}/${project.build.finalName}">
|
||||
<include name="forge.sh" />
|
||||
|
||||
Reference in New Issue
Block a user