mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-15 18:28:00 +00:00
- Added very basic AddPhase AF
- Added support in PhaseHandler for Additional phases - Convert Finest Hour to AddPhase
This commit is contained in:
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -12918,6 +12918,7 @@ src/main/java/forge/card/abilityfactory/CommonDrawback.java svneol=native#text/p
|
||||
src/main/java/forge/card/abilityfactory/CommonSpell.java -text
|
||||
src/main/java/forge/card/abilityfactory/SpellAiLogic.java -text
|
||||
src/main/java/forge/card/abilityfactory/SpellEffect.java -text
|
||||
src/main/java/forge/card/abilityfactory/ai/AddPhaseAi.java -text
|
||||
src/main/java/forge/card/abilityfactory/ai/AddTurnAi.java svneol=native#text/plain
|
||||
src/main/java/forge/card/abilityfactory/ai/AlwaysPlayAi.java -text
|
||||
src/main/java/forge/card/abilityfactory/ai/AnimateAi.java -text
|
||||
@@ -13013,6 +13014,7 @@ src/main/java/forge/card/abilityfactory/ai/UnattachAllAi.java -text
|
||||
src/main/java/forge/card/abilityfactory/ai/UntapAi.java -text
|
||||
src/main/java/forge/card/abilityfactory/ai/UntapAllAi.java -text
|
||||
src/main/java/forge/card/abilityfactory/effects/AbandonEffect.java -text
|
||||
src/main/java/forge/card/abilityfactory/effects/AddPhaseEffect.java -text
|
||||
src/main/java/forge/card/abilityfactory/effects/AddTurnEffect.java -text
|
||||
src/main/java/forge/card/abilityfactory/effects/AnimateAllEffect.java -text
|
||||
src/main/java/forge/card/abilityfactory/effects/AnimateEffect.java -text
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
Name:Finest Hour
|
||||
ManaCost:2 G W U
|
||||
Types:Enchantment
|
||||
Text:Whenever a creature you control attacks alone, if it's the first combat phase of the turn, untap that creature. After this phase, there is an additional combat phase.
|
||||
Text:no text
|
||||
K:Exalted
|
||||
T:Mode$ Attacks | ValidCard$ Creature.YouCtrl | Alone$ True | TriggerZones$ Battlefield | Execute$ TrigUntap | FirstCombat$ True | TriggerDescription$ Whenever a creature you control attacks alone, if it's the first combat phase of the turn, untap that creature. After this phase, there is an additional combat phase.
|
||||
SVar:TrigUntap:DB$ Untap | Defined$ TriggeredAttacker | SubAbility$ DBAddCombat
|
||||
SVar:DBAddCombat:DB$ AddPhase | ExtraPhase$ BeginCombat | AfterPhase$ EndCombat
|
||||
SVar:PlayMain1:TRUE
|
||||
SVar:Rarity:Rare
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/finest_hour.jpg
|
||||
|
||||
@@ -11,6 +11,7 @@ import forge.card.abilityfactory.effects.*;
|
||||
public enum ApiType {
|
||||
|
||||
Abandon (AbandonEffect.class, AlwaysPlayAi.class),
|
||||
AddPhase (AddPhaseEffect.class, AddPhaseAi.class),
|
||||
AddTurn (AddTurnEffect.class, AddTurnAi.class),
|
||||
Animate (AnimateEffect.class, AnimateAi.class),
|
||||
AnimateAll (AnimateAllEffect.class, AnimateAllAi.class),
|
||||
|
||||
18
src/main/java/forge/card/abilityfactory/ai/AddPhaseAi.java
Normal file
18
src/main/java/forge/card/abilityfactory/ai/AddPhaseAi.java
Normal file
@@ -0,0 +1,18 @@
|
||||
package forge.card.abilityfactory.ai;
|
||||
|
||||
import forge.card.abilityfactory.SpellAiLogic;
|
||||
import forge.card.spellability.SpellAbility;
|
||||
import forge.game.player.Player;
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this type.
|
||||
*
|
||||
*/
|
||||
public class AddPhaseAi extends SpellAiLogic {
|
||||
|
||||
@Override
|
||||
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package forge.card.abilityfactory.effects;
|
||||
|
||||
import forge.Singletons;
|
||||
import forge.card.abilityfactory.SpellEffect;
|
||||
import forge.card.spellability.SpellAbility;
|
||||
import forge.game.phase.PhaseHandler;
|
||||
import forge.game.phase.PhaseType;
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this type.
|
||||
*
|
||||
*/
|
||||
public class AddPhaseEffect extends SpellEffect {
|
||||
|
||||
@Override
|
||||
public void resolve(SpellAbility sa) {
|
||||
PhaseHandler phaseHandler = Singletons.getModel().getGame().getPhaseHandler();
|
||||
PhaseType extra = PhaseType.smartValueOf(sa.getParam("ExtraPhase"));
|
||||
|
||||
PhaseType after;
|
||||
if (sa.hasParam("AfterPhase")) {
|
||||
after = PhaseType.smartValueOf(sa.getParam("AfterPhase"));
|
||||
}
|
||||
else {
|
||||
after = phaseHandler.getPhase();
|
||||
}
|
||||
|
||||
phaseHandler.addExtraPhase(after, extra);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -32,6 +32,7 @@ import forge.card.abilityfactory.AbilityFactory;
|
||||
import forge.card.cardfactory.CardFactoryUtil;
|
||||
import forge.card.spellability.Ability;
|
||||
import forge.card.spellability.SpellAbility;
|
||||
import forge.game.phase.PhaseHandler;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.Expressions;
|
||||
@@ -227,20 +228,27 @@ public abstract class Trigger extends TriggerReplacementBase {
|
||||
* @return a boolean.
|
||||
*/
|
||||
public final boolean phasesCheck() {
|
||||
PhaseHandler phaseHandler = Singletons.getModel().getGame().getPhaseHandler();
|
||||
if (null != validPhases) {
|
||||
if (!validPhases.contains(Singletons.getModel().getGame().getPhaseHandler().getPhase())) {
|
||||
if (!validPhases.contains(phaseHandler.getPhase())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.getMapParams().containsKey("PlayerTurn")) {
|
||||
if (!Singletons.getModel().getGame().getPhaseHandler().isPlayerTurn(this.getHostCard().getController())) {
|
||||
if (!phaseHandler.isPlayerTurn(this.getHostCard().getController())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.getMapParams().containsKey("OpponentTurn")) {
|
||||
if (Singletons.getModel().getGame().getPhaseHandler().isPlayerTurn(this.getHostCard().getController())) {
|
||||
if (phaseHandler.isPlayerTurn(this.getHostCard().getController())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.getMapParams().containsKey("FirstCombat")) {
|
||||
if (!phaseHandler.isFirstCombat()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3092,41 +3092,6 @@ public class CombatUtil {
|
||||
}
|
||||
|
||||
final Player phasingPlayer = c.getController();
|
||||
// Finest Hour untaps the creature on the first combat phase
|
||||
if ((phasingPlayer.getCardsIn(ZoneType.Battlefield, "Finest Hour").size() > 0)
|
||||
&& Singletons.getModel().getGame().getPhaseHandler().isFirstCombat()) {
|
||||
// Untap the attacking creature
|
||||
final Ability fhUntap = new Ability(c, "0") {
|
||||
@Override
|
||||
public void resolve() {
|
||||
crd.untap();
|
||||
}
|
||||
};
|
||||
|
||||
final StringBuilder sbUntap = new StringBuilder();
|
||||
sbUntap.append(c).append(" - (Finest Hour) untap.");
|
||||
fhUntap.setDescription(sbUntap.toString());
|
||||
fhUntap.setStackDescription(sbUntap.toString());
|
||||
|
||||
Singletons.getModel().getGame().getStack().addSimultaneousStackEntry(fhUntap);
|
||||
|
||||
// If any Finest Hours, queue up a new combat phase
|
||||
for (int ix = 0; ix < phasingPlayer.getCardsIn(ZoneType.Battlefield, "Finest Hour").size(); ix++) {
|
||||
final Ability fhAddCombat = new Ability(c, "0") {
|
||||
@Override
|
||||
public void resolve() {
|
||||
Singletons.getModel().getGame().getPhaseHandler().addExtraCombat();
|
||||
}
|
||||
};
|
||||
|
||||
final StringBuilder sbACom = new StringBuilder();
|
||||
sbACom.append(c).append(" - (Finest Hour) ").append(phasingPlayer).append(" gets Extra Combat Phase.");
|
||||
fhAddCombat.setDescription(sbACom.toString());
|
||||
fhAddCombat.setStackDescription(sbACom.toString());
|
||||
|
||||
Singletons.getModel().getGame().getStack().addSimultaneousStackEntry(fhAddCombat);
|
||||
}
|
||||
}
|
||||
|
||||
if (phasingPlayer.getCardsIn(ZoneType.Battlefield, "Sovereigns of Lost Alara").size() > 0) {
|
||||
for (int i = 0; i < phasingPlayer.getCardsIn(ZoneType.Battlefield, "Sovereigns of Lost Alara").size(); i++) {
|
||||
|
||||
@@ -19,6 +19,7 @@ package forge.game.phase;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Stack;
|
||||
|
||||
import com.esotericsoftware.minlog.Log;
|
||||
@@ -59,8 +60,7 @@ public class PhaseHandler extends MyObservable implements java.io.Serializable {
|
||||
// Start turn at 0, so first untap step will turn it to 1
|
||||
|
||||
private final transient Stack<ExtraTurn> extraTurns = new Stack<ExtraTurn>();
|
||||
|
||||
private int extraCombats = 0;
|
||||
private final transient Map<PhaseType, Stack<PhaseType>> extraPhases = new HashMap<PhaseType, Stack<PhaseType>>();
|
||||
|
||||
private int nCombatsThisTurn = 0;
|
||||
private boolean bPreventCombatDamageThisTurn = false;
|
||||
@@ -465,17 +465,6 @@ public class PhaseHandler extends MyObservable implements java.io.Serializable {
|
||||
this.resetAttackedThisCombat(this.getPlayerTurn());
|
||||
this.bCombat = false;
|
||||
|
||||
// TODO: ExtraCombat needs to be changed for other spell/abilities
|
||||
// that give extra combat can do it like ExtraTurn stack ExtraPhases
|
||||
if (this.extraCombats > 0) {
|
||||
final Player player = this.getPlayerTurn();
|
||||
|
||||
this.bCombat = true;
|
||||
this.extraCombats--;
|
||||
game.getCombat().reset();
|
||||
game.getCombat().setAttackingPlayer(player);
|
||||
this.phase = PhaseType.COMBAT_BEGIN;
|
||||
}
|
||||
break;
|
||||
|
||||
case CLEANUP:
|
||||
@@ -489,13 +478,29 @@ public class PhaseHandler extends MyObservable implements java.io.Serializable {
|
||||
default: // no action
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (this.bRepeat) { // for when Cleanup needs to repeat itself
|
||||
this.bRepeat = false;
|
||||
sb.append("Repeat Phase");
|
||||
} else {
|
||||
this.phase = phase.getNextPhase();
|
||||
// If the phase that's ending has a stack of additional phases
|
||||
// Take the LIFO one and move to that instead of the normal one
|
||||
if (this.extraPhases.containsKey(phase)) {
|
||||
PhaseType nextPhase = this.extraPhases.get(phase).pop();
|
||||
// If no more additional phases are available, remove it from the map
|
||||
// and let the next add, reput the key
|
||||
if (this.extraPhases.get(phase).isEmpty()) {
|
||||
this.extraPhases.remove(phase);
|
||||
}
|
||||
this.phase = nextPhase;
|
||||
sb.append("Additional Phase");
|
||||
} else {
|
||||
this.phase = phase.getNextPhase();
|
||||
sb.append("Phase");
|
||||
}
|
||||
}
|
||||
|
||||
game.getGameLog().add("Phase", this.getPlayerTurn() + " " + this.getPhase().Name, 6);
|
||||
game.getGameLog().add(sb.toString(), this.getPlayerTurn() + " " + this.getPhase().Name, 6);
|
||||
|
||||
// **** Anything BELOW Here is actually in the next phase. Maybe move
|
||||
// this to handleBeginPhase
|
||||
@@ -659,15 +664,20 @@ public class PhaseHandler extends MyObservable implements java.io.Serializable {
|
||||
return this.extraTurns.push(new ExtraTurn(player));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* addExtraCombat.
|
||||
* addExtraPhase.
|
||||
* </p>
|
||||
*
|
||||
*/
|
||||
public final void addExtraCombat() {
|
||||
// Extra combats can only happen
|
||||
this.extraCombats++;
|
||||
public final void addExtraPhase(final PhaseType afterPhase, final PhaseType extraPhase) {
|
||||
// 300.7. Some effects can add phases to a turn. They do this by adding the phases directly after the specified phase.
|
||||
// If multiple extra phases are created after the same phase, the most recently created phase will occur first.
|
||||
if (!this.extraPhases.containsKey(afterPhase)) {
|
||||
this.extraPhases.put(afterPhase, new Stack<PhaseType>());
|
||||
}
|
||||
|
||||
this.extraPhases.get(afterPhase).push(extraPhase);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -105,7 +105,7 @@ public enum PhaseType {
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this method.
|
||||
* Get the next PhaseType in turn order.
|
||||
* @return
|
||||
*/
|
||||
public PhaseType getNextPhase() {
|
||||
|
||||
Reference in New Issue
Block a user