mirror of
https://github.com/magefree/mage.git
synced 2025-12-28 14:32:06 -08:00
[CMR] implemented Opposition Agent and other changes:
* You may play cards and you may spend mana of any color - refactored cards to use same code; * Library search event allows to change searching controller (gives full game control for another player); * Library searched event allows to remove founded cards from result; * Improved library searching effects with Panglacial Wurm's effects; * Little changes to test framework;
This commit is contained in:
parent
13fa98ec44
commit
c2a636e2b2
22 changed files with 806 additions and 721 deletions
|
|
@ -1,10 +1,7 @@
|
|||
package mage.players;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import mage.ApprovingObject;
|
||||
import mage.ConditionalMana;
|
||||
import mage.MageObject;
|
||||
import mage.Mana;
|
||||
import mage.*;
|
||||
import mage.abilities.*;
|
||||
import mage.abilities.ActivatedAbility.ActivationStatus;
|
||||
import mage.abilities.common.PassAbility;
|
||||
|
|
@ -28,7 +25,6 @@ import mage.abilities.mana.ManaOptions;
|
|||
import mage.actions.MageDrawAction;
|
||||
import mage.cards.*;
|
||||
import mage.cards.decks.Deck;
|
||||
import mage.choices.ChoiceImpl;
|
||||
import mage.constants.*;
|
||||
import mage.counters.Counter;
|
||||
import mage.counters.CounterType;
|
||||
|
|
@ -38,6 +34,7 @@ import mage.designations.DesignationType;
|
|||
import mage.filter.FilterCard;
|
||||
import mage.filter.FilterMana;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.filter.common.FilterControlledPermanent;
|
||||
import mage.filter.common.FilterCreatureForCombat;
|
||||
import mage.filter.common.FilterCreatureForCombatBlock;
|
||||
|
|
@ -2609,73 +2606,97 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
@Override
|
||||
public boolean searchLibrary(TargetCardInLibrary target, Ability source, Game game, UUID targetPlayerId) {
|
||||
//20091005 - 701.14c
|
||||
Library searchedLibrary = null;
|
||||
String searchInfo = null;
|
||||
if (targetPlayerId.equals(playerId)) {
|
||||
searchInfo = getLogName() + " searches their library";
|
||||
searchedLibrary = library;
|
||||
|
||||
// searching control can be intercepted by another player, see Opposition Agent
|
||||
SearchLibraryEvent searchEvent = new SearchLibraryEvent(targetPlayerId, source.getSourceId(), playerId, Integer.MAX_VALUE);
|
||||
if (game.replaceEvent(searchEvent)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Player targetPlayer = game.getPlayer(targetPlayerId);
|
||||
Player searchingPlayer = this;
|
||||
Player searchingController = game.getPlayer(searchEvent.getSearchingControllerId());
|
||||
if (targetPlayer == null || searchingController == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String searchInfo = searchingPlayer.getLogName();
|
||||
if (!searchingPlayer.getId().equals(searchingController.getId())) {
|
||||
searchInfo = searchInfo + " under control of " + searchingPlayer.getLogName();
|
||||
}
|
||||
if (targetPlayer.getId().equals(searchingPlayer.getId())) {
|
||||
searchInfo = searchInfo + " searches their library";
|
||||
} else {
|
||||
Player targetPlayer = game.getPlayer(targetPlayerId);
|
||||
if (targetPlayer != null) {
|
||||
searchInfo = getLogName() + " searches the library of " + targetPlayer.getLogName();
|
||||
searchedLibrary = targetPlayer.getLibrary();
|
||||
}
|
||||
}
|
||||
if (searchedLibrary == null) {
|
||||
return false;
|
||||
}
|
||||
GameEvent event = GameEvent.getEvent(GameEvent.EventType.SEARCH_LIBRARY,
|
||||
targetPlayerId, source.getSourceId(), playerId, Integer.MAX_VALUE);
|
||||
if (game.replaceEvent(event)) {
|
||||
return false;
|
||||
searchInfo = searchInfo + " searches the library of " + targetPlayer.getLogName();
|
||||
}
|
||||
|
||||
if (!game.isSimulation()) {
|
||||
game.informPlayers(searchInfo);
|
||||
}
|
||||
|
||||
// https://www.reddit.com/r/magicTCG/comments/jj8gh9/opposition_agent_and_panglacial_wurm_interaction/
|
||||
// You must take full player control while searching, e.g. you can cast opponent's cards by Panglacial Wurm effect:
|
||||
// * While you’re searching your library, you may cast Panglacial Wurm from your library.
|
||||
// So use here same code as Word of Command
|
||||
// P.S. no needs in searchingController, but it helps with unit tests, see TakeControlWhileSearchingLibraryTest
|
||||
boolean takeControl = false;
|
||||
if (!searchingPlayer.getId().equals(searchingController.getId())) {
|
||||
CardUtil.takeControlUnderPlayerStart(game, searchingController, searchingPlayer, true);
|
||||
takeControl = true;
|
||||
}
|
||||
|
||||
Library searchingLibrary = targetPlayer.getLibrary();
|
||||
TargetCardInLibrary newTarget = target.copy();
|
||||
int count;
|
||||
int librarySearchLimit = event.getAmount();
|
||||
int librarySearchLimit = searchEvent.getAmount();
|
||||
List<Card> cardsFromTop = null;
|
||||
do {
|
||||
// TODO: prevent shuffling from moving the visualized cards
|
||||
if (librarySearchLimit == Integer.MAX_VALUE) {
|
||||
count = searchedLibrary.count(target.getFilter(), game);
|
||||
count = searchingLibrary.count(target.getFilter(), game);
|
||||
} else {
|
||||
Player targetPlayer = game.getPlayer(targetPlayerId);
|
||||
if (targetPlayer == null) {
|
||||
return false;
|
||||
}
|
||||
if (cardsFromTop == null) {
|
||||
cardsFromTop = new ArrayList<>(targetPlayer.getLibrary().getTopCards(game, librarySearchLimit));
|
||||
cardsFromTop = new ArrayList<>(searchingLibrary.getTopCards(game, librarySearchLimit));
|
||||
} else {
|
||||
cardsFromTop.retainAll(targetPlayer.getLibrary().getCards(game));
|
||||
cardsFromTop.retainAll(searchingLibrary.getCards(game));
|
||||
}
|
||||
newTarget.setCardLimit(Math.min(librarySearchLimit, cardsFromTop.size()));
|
||||
count = Math.min(searchedLibrary.count(target.getFilter(), game), librarySearchLimit);
|
||||
count = Math.min(searchingLibrary.count(target.getFilter(), game), librarySearchLimit);
|
||||
}
|
||||
|
||||
if (count < target.getNumberOfTargets()) {
|
||||
newTarget.setMinNumberOfTargets(count);
|
||||
}
|
||||
if (newTarget.choose(Outcome.Neutral, playerId, targetPlayerId, game)) {
|
||||
if (targetPlayerId.equals(playerId) && handleLibraryCastableCards(library,
|
||||
game, targetPlayerId)) { // for handling Panglacial Wurm
|
||||
|
||||
// handling Panglacial Wurm - cast cards while searching from own library
|
||||
if (targetPlayer.getId().equals(searchingPlayer.getId())) {
|
||||
if (handleCastableCardsWhileLibrarySearching(library, game, targetPlayer)) {
|
||||
// clear all choices to start from scratch (casted cards must be removed from library)
|
||||
newTarget.clearChosen();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (newTarget.choose(Outcome.Neutral, searchingController.getId(), targetPlayer.getId(), game)) {
|
||||
target.getTargets().clear();
|
||||
for (UUID targetId : newTarget.getTargets()) {
|
||||
target.add(targetId, game);
|
||||
}
|
||||
|
||||
} else if (targetPlayerId.equals(playerId) && handleLibraryCastableCards(library,
|
||||
game, targetPlayerId)) { // for handling Panglacial Wurm
|
||||
newTarget.clearChosen();
|
||||
continue;
|
||||
}
|
||||
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.LIBRARY_SEARCHED, targetPlayerId, playerId));
|
||||
|
||||
// END SEARCH
|
||||
if (takeControl) {
|
||||
CardUtil.takeControlUnderPlayerEnd(game, searchingController, searchingPlayer);
|
||||
game.informPlayers("Control of " + searchingPlayer.getLogName() + " is back");
|
||||
}
|
||||
|
||||
LibrarySearchedEvent searchedEvent = new LibrarySearchedEvent(targetPlayer.getId(), source.getSourceId(), searchingPlayer.getId(), target);
|
||||
if (game.replaceEvent(searchedEvent)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
} while (true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -2690,58 +2711,53 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
}
|
||||
}
|
||||
|
||||
private boolean handleLibraryCastableCards(Library library, Game game, UUID targetPlayerId) {
|
||||
// for handling Panglacial Wurm
|
||||
boolean alreadyChosenUse = false;
|
||||
Map<UUID, String> libraryCastableCardTracker = new HashMap<>();
|
||||
searchForCards:
|
||||
do {
|
||||
for (Card card : library.getCards(game)) {
|
||||
for (Ability ability : card.getAbilities()) {
|
||||
if (ability.getClass() == WhileSearchingPlayFromLibraryAbility.class) {
|
||||
libraryCastableCardTracker.put(card.getId(), card.getIdName());
|
||||
}
|
||||
}
|
||||
private boolean handleCastableCardsWhileLibrarySearching(Library library, Game game, Player targetPlayer) {
|
||||
// must return true after cast try (to restart searching process without casted cards)
|
||||
// uses for handling Panglacial Wurm:
|
||||
// * While you're searching your library, you may cast Panglacial Wurm from your library.
|
||||
|
||||
List<UUID> castableCards = library.getCards(game).stream()
|
||||
.filter(card -> card.getAbilities(game).containsClass(WhileSearchingPlayFromLibraryAbility.class))
|
||||
.map(MageItem::getId)
|
||||
.collect(Collectors.toList());
|
||||
if (castableCards.size() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// only humans can use it
|
||||
if (!targetPlayer.isHuman() && !targetPlayer.isTestMode()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!targetPlayer.chooseUse(Outcome.AIDontUseIt, "Library have " + castableCards.size() + " castable cards on searching. Do you want to cast it?", null, game)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean casted = false;
|
||||
TargetCard targetCard = new TargetCard(0, 1, Zone.LIBRARY, StaticFilters.FILTER_CARD);
|
||||
targetCard.setTargetName("card to cast from library");
|
||||
targetCard.setNotTarget(true);
|
||||
while (castableCards.size() > 0) {
|
||||
targetCard.clearChosen();
|
||||
if (!targetPlayer.choose(Outcome.AIDontUseIt, new CardsImpl(castableCards), targetCard, game)) {
|
||||
break;
|
||||
}
|
||||
if (!libraryCastableCardTracker.isEmpty()) {
|
||||
Player player = game.getPlayer(targetPlayerId);
|
||||
if (player != null) {
|
||||
if (player.isHuman() && (alreadyChosenUse || player.chooseUse(Outcome.AIDontUseIt,
|
||||
"Cast a creature card from your library? (choose \"No\" to finish search)", null, game))) {
|
||||
ChoiceImpl chooseCard = new ChoiceImpl();
|
||||
chooseCard.setMessage("Which creature do you wish to cast from your library?");
|
||||
Set<String> choice = new LinkedHashSet<>();
|
||||
for (Entry<UUID, String> entry : libraryCastableCardTracker.entrySet()) {
|
||||
choice.add(new AbstractMap.SimpleEntry<>(entry).getValue());
|
||||
}
|
||||
chooseCard.setChoices(choice);
|
||||
while (!choice.isEmpty()) {
|
||||
if (player.choose(Outcome.AIDontUseIt, chooseCard, game)) {
|
||||
String chosenCard = chooseCard.getChoice();
|
||||
for (Entry<UUID, String> entry : libraryCastableCardTracker.entrySet()) {
|
||||
if (chosenCard.equals(entry.getValue())) {
|
||||
Card card = game.getCard(entry.getKey());
|
||||
if (card != null) {
|
||||
// TODO: fix costs (why is Panglacial Wurm automatically accepting payment?)
|
||||
player.cast(card.getSpellAbility(), game, false, null);
|
||||
}
|
||||
chooseCard.clearChoice();
|
||||
libraryCastableCardTracker.clear();
|
||||
alreadyChosenUse = true;
|
||||
continue searchForCards;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Card card = game.getCard(targetCard.getFirstTarget());
|
||||
if (card == null) {
|
||||
break;
|
||||
}
|
||||
break;
|
||||
} while (alreadyChosenUse);
|
||||
return alreadyChosenUse;
|
||||
|
||||
// AI NOTICE: if you want AI implement here then remove selected card from castable after each
|
||||
// choice (otherwise you catch infinite freeze on uncastable use case)
|
||||
|
||||
// casting selected card
|
||||
// TODO: fix costs (why is Panglacial Wurm automatically accepting payment?)
|
||||
targetPlayer.cast(targetPlayer.chooseAbilityForCast(card, game, false), game, false, null);
|
||||
castableCards.remove(card.getId());
|
||||
casted = true;
|
||||
}
|
||||
return casted;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue