Skip to content

Commit

Permalink
Room: First Spell Part (#6044)
Browse files Browse the repository at this point in the history
* Room: First Spell 

----

Co-authored-by: tool4ever <[email protected]>
Co-authored-by: tool4EvEr <[email protected]>
Co-authored-by: TRT <>
Co-authored-by: Anthony Calosa <[email protected]>
  • Loading branch information
4 people authored Oct 6, 2024
1 parent 88006eb commit 80b30d9
Show file tree
Hide file tree
Showing 30 changed files with 527 additions and 34 deletions.
18 changes: 18 additions & 0 deletions forge-ai/src/main/java/forge/ai/PlayerControllerAi.java
Original file line number Diff line number Diff line change
Expand Up @@ -1530,6 +1530,24 @@ public String chooseCardName(SpellAbility sa, List<ICardFace> faces, String mess
return SpellApiToAi.Converter.get(api).chooseCardName(player, sa, faces);
}

@Override
public ICardFace chooseSingleCardFace(SpellAbility sa, List<ICardFace> faces, String message) {
ApiType api = sa.getApi();
if (null == api) {
throw new InvalidParameterException("SA is not api-based, this is not supported yet");
}
return SpellApiToAi.Converter.get(api).chooseCardFace(player, sa, faces);
}

@Override
public CardState chooseSingleCardState(SpellAbility sa, List<CardState> states, String message, Map<String, Object> params) {
ApiType api = sa.getApi();
if (null == api) {
throw new InvalidParameterException("SA is not api-based, this is not supported yet");
}
return SpellApiToAi.Converter.get(api).chooseCardState(player, sa, states, params);
}

@Override
public Card chooseDungeon(Player ai, List<PaperCard> dungeonCards, String message) {
// TODO: improve the conditions that define which dungeon is a viable option to choose
Expand Down
13 changes: 13 additions & 0 deletions forge-ai/src/main/java/forge/ai/SpellAbilityAi.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import forge.card.mana.ManaCostParser;
import forge.game.GameEntity;
import forge.game.card.Card;
import forge.game.card.CardState;
import forge.game.card.CounterType;
import forge.game.cost.Cost;
import forge.game.mana.ManaCostBeingPaid;
Expand Down Expand Up @@ -365,6 +366,18 @@ public String chooseCardName(Player ai, SpellAbility sa, List<ICardFace> faces)
return face == null ? "" : face.getName();
}

public ICardFace chooseCardFace(Player ai, SpellAbility sa, List<ICardFace> faces) {
System.err.println("Warning: default (ie. inherited from base class) implementation of chooseCardFace is used for " + this.getClass().getName() + ". Consider declaring an overloaded method");

return Iterables.getFirst(faces, null);
}

public CardState chooseCardState(Player ai, SpellAbility sa, List<CardState> faces, Map<String, Object> params) {
System.err.println("Warning: default (ie. inherited from base class) implementation of chooseCardState is used for " + this.getClass().getName() + ". Consider declaring an overloaded method");

return Iterables.getFirst(faces, null);
}

public int chooseNumber(Player player, SpellAbility sa, int min, int max, Map<String, Object> params) {
return max;
}
Expand Down
1 change: 1 addition & 0 deletions forge-ai/src/main/java/forge/ai/SpellApiToAi.java
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ public enum SpellApiToAi {
.put(ApiType.TwoPiles, TwoPilesAi.class)
.put(ApiType.Unattach, CannotPlayAi.class)
.put(ApiType.UnattachAll, UnattachAllAi.class)
.put(ApiType.UnlockDoor, AlwaysPlayAi.class)
.put(ApiType.Untap, UntapAi.class)
.put(ApiType.UntapAll, UntapAllAi.class)
.put(ApiType.Venture, VentureAi.class)
Expand Down
1 change: 1 addition & 0 deletions forge-core/src/main/java/forge/card/CardStateName.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public enum CardStateName {
RightSplit,
Adventure,
Modal,
EmptyRoom,
SpecializeW,
SpecializeU,
SpecializeB,
Expand Down
10 changes: 9 additions & 1 deletion forge-game/src/main/java/forge/game/GameAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,12 @@ private Card changeZone(final Zone zoneFrom, Zone zoneTo, final Card c, Integer
// Make sure the card returns from the battlefield as the original card with two halves
resetToOriginal = true;
}
} else if (!zoneTo.is(ZoneType.Stack)) {
} else if (zoneTo.is(ZoneType.Battlefield) && c.isRoom()) {
if (c.getCastSA() == null) {
// need to set as empty room
c.updateRooms();
}
} else if (!zoneTo.is(ZoneType.Stack) && !zoneTo.is(ZoneType.Battlefield)) {
// For regular splits, recreate the original state unless the card is going to stack as one half
resetToOriginal = true;
}
Expand Down Expand Up @@ -604,6 +609,9 @@ private Card changeZone(final Zone zoneFrom, Zone zoneTo, final Card c, Integer
// CR 603.6b
if (toBattlefield) {
zoneTo.saveLKI(copied, lastKnownInfo);
if (copied.isRoom() && copied.getCastSA() != null) {
copied.unlockRoom(copied.getCastSA().getActivatingPlayer(), copied.getCastSA().getCardStateName());
}
}

// only now that the LKI preserved it
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public enum AbilityKey {
Blockers("Blockers"),
CanReveal("CanReveal"),
Card("Card"),
CardState("CardState"),
Cards("Cards"),
CardsFiltered("CardsFiltered"),
CardLKI("CardLKI"),
Expand Down
1 change: 1 addition & 0 deletions forge-game/src/main/java/forge/game/ability/ApiType.java
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ public enum ApiType {
TwoPiles (TwoPilesEffect.class),
Unattach (UnattachEffect.class),
UnattachAll (UnattachAllEffect.class),
UnlockDoor (UnlockDoorEffect.class),
Untap (UntapEffect.class),
UntapAll (UntapAllEffect.class),
Venture (VentureEffect.class),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ public void resolve(SpellAbility sa) {

final long ts = game.getNextTimestamp();
tgtCard.addCloneState(CardFactory.getCloneStates(cardToCopy, tgtCard, sa), ts);
tgtCard.updateRooms();

// set ETB tapped of clone
if (sa.hasParam("IntoPlayTapped")) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package forge.game.ability.effects;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

import forge.card.CardStateName;
import forge.game.Game;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card;
import forge.game.card.CardCollection;
import forge.game.card.CardLists;
import forge.game.card.CardState;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.zone.ZoneType;
import forge.util.Localizer;

public class UnlockDoorEffect extends SpellAbilityEffect {

@Override
public void resolve(SpellAbility sa) {
final Card source = sa.getHostCard();
final Game game = source.getGame();
final Player activator = sa.getActivatingPlayer();

CardCollection list;

if (sa.hasParam("Choices")) {
Player chooser = activator;
String title = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : Localizer.getInstance().getMessage("lblChoose") + " ";

CardCollection choices = CardLists.getValidCards(game.getCardsIn(ZoneType.Battlefield), sa.getParam("Choices"), activator, source, sa);

Card c = chooser.getController().chooseSingleEntityForEffect(choices, sa, title, Maps.newHashMap());
if (c == null) {
return;
}
list = new CardCollection(c);
} else {
list = getTargetCards(sa);
}

for (Card c : list) {
Map<String, Object> params = Maps.newHashMap();
params.put("Object", c);
switch (sa.getParamOrDefault("Mode", "ThisDoor")) {
case "ThisDoor":
c.unlockRoom(activator, sa.getCardStateName());
break;
case "Unlock":
List<CardState> states = c.getLockedRooms().stream().map(stateName -> c.getState(stateName)).collect(Collectors.toList());

// need to choose Room Name
CardState chosen = activator.getController().chooseSingleCardState(sa, states, "Choose Room to unlock", params);
if (chosen == null) {
continue;
}
c.unlockRoom(activator, chosen.getStateName());
break;
case "LockOrUnlock":
switch (c.getLockedRooms().size()) {
case 0:
// no locked, all unlocked, can only lock door
List<CardState> unlockStates = c.getUnlockedRooms().stream().map(stateName -> c.getState(stateName)).collect(Collectors.toList());
CardState chosenUnlock = activator.getController().chooseSingleCardState(sa, unlockStates, "Choose Room to lock", params);
if (chosenUnlock == null) {
continue;
}
c.lockRoom(activator, chosenUnlock.getStateName());
break;
case 1:
// TODO check for Lock vs Unlock first?
List<CardState> bothStates = Lists.newArrayList();
bothStates.add(c.getState(CardStateName.LeftSplit));
bothStates.add(c.getState(CardStateName.RightSplit));
CardState chosenBoth = activator.getController().chooseSingleCardState(sa, bothStates, "Choose Room to lock or unlock", params);
if (chosenBoth == null) {
continue;
}
if (c.getLockedRooms().contains(chosenBoth.getStateName())) {
c.unlockRoom(activator, chosenBoth.getStateName());
} else {
c.lockRoom(activator, chosenBoth.getStateName());
}
break;
case 2:
List<CardState> lockStates = c.getLockedRooms().stream().map(stateName -> c.getState(stateName)).collect(Collectors.toList());

// need to choose Room Name
CardState chosenLock = activator.getController().chooseSingleCardState(sa, lockStates, "Choose Room to unlock", params);
if (chosenLock == null) {
continue;
}
c.unlockRoom(activator, chosenLock.getStateName());
break;
}
break;
}
}
}

}
Loading

0 comments on commit 80b30d9

Please sign in to comment.