diff --git a/.gitattributes b/.gitattributes index 5c91ffc04dd..801449483f0 100644 --- a/.gitattributes +++ b/.gitattributes @@ -139,6 +139,7 @@ forge-game/src/main/java/forge/ai/ComputerUtilMana.java -text forge-game/src/main/java/forge/ai/LobbyPlayerAi.java -text forge-game/src/main/java/forge/ai/PlayerControllerAi.java -text forge-game/src/main/java/forge/ai/SpellAbilityAi.java -text +forge-game/src/main/java/forge/ai/SpellApiToAi.java -text forge-game/src/main/java/forge/ai/ability/AddPhaseAi.java -text forge-game/src/main/java/forge/ai/ability/AddTurnAi.java svneol=native#text/plain forge-game/src/main/java/forge/ai/ability/AlwaysPlayAi.java -text diff --git a/forge-game/src/main/java/forge/ai/AiController.java b/forge-game/src/main/java/forge/ai/AiController.java index 437ee595553..96d0ea89f64 100644 --- a/forge-game/src/main/java/forge/ai/AiController.java +++ b/forge-game/src/main/java/forge/ai/AiController.java @@ -616,7 +616,7 @@ public class AiController { return canPlaySa(((WrappedAbility) sa).getWrappedAbility()); } if( sa.getApi() != null ) { - boolean canPlay = sa.getApi().getAi().canPlayAIWithSubs(player, sa); + boolean canPlay = SpellApiToAi.Converter.get(sa.getApi()).canPlayAIWithSubs(player, sa); if(!canPlay) return AiPlayDecision.CantPlayAi; } @@ -876,7 +876,7 @@ public class AiController { throw new IllegalArgumentException(exMsg); } else - return api.getAi().confirmAction(player, sa, mode, message); + return SpellApiToAi.Converter.get(api).confirmAction(player, sa, mode, message); } public boolean confirmStaticApplication(Card hostCard, GameEntity affected, String logic, String message) { @@ -940,9 +940,9 @@ public class AiController { { boolean chance = false; if (withoutPayingManaCost) { - chance = spell.getApi().getAi().doTriggerNoCostWithSubs(player, spell, mandatory); + chance = SpellApiToAi.Converter.get(spell.getApi()).doTriggerNoCostWithSubs(player, spell, mandatory); } else { - chance = spell.getApi().getAi().doTriggerAI(player, spell, mandatory); + chance = SpellApiToAi.Converter.get(spell.getApi()).doTriggerAI(player, spell, mandatory); } if (!chance) return AiPlayDecision.TargetingFailed; @@ -1148,7 +1148,7 @@ public class AiController { public boolean doTrigger(SpellAbility spell, boolean mandatory) { if ( spell.getApi() != null ) - return spell.getApi().getAi().doTriggerAI(player, spell, mandatory); + return SpellApiToAi.Converter.get(spell.getApi()).doTriggerAI(player, spell, mandatory); if ( spell instanceof WrappedAbility ) return doTrigger(((WrappedAbility)spell).getWrappedAbility(), mandatory); diff --git a/forge-game/src/main/java/forge/ai/ComputerUtilMana.java b/forge-game/src/main/java/forge/ai/ComputerUtilMana.java index 0d587fc2922..43a7e40e862 100644 --- a/forge-game/src/main/java/forge/ai/ComputerUtilMana.java +++ b/forge-game/src/main/java/forge/ai/ComputerUtilMana.java @@ -3,6 +3,7 @@ package forge.ai; import com.google.common.base.Predicate; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; + import forge.card.MagicColor; import forge.card.mana.ManaAtom; import forge.card.mana.ManaCost; @@ -28,6 +29,7 @@ import forge.util.CollectionSuppliers; import forge.util.TextUtil; import forge.util.maps.EnumMapOfLists; import forge.util.maps.MapOfLists; + import org.apache.commons.lang3.StringUtils; import java.util.*; @@ -630,7 +632,7 @@ public class ComputerUtilMana { // don't use abilities with dangerous drawbacks AbilitySub sub = m.getSubAbility(); if (sub != null && !card.getName().equals("Pristine Talisman") && !card.getName().equals("Zhur-Taa Druid")) { - if (!sub.getApi().getAi().chkDrawbackWithSubs(ai, sub)) { + if (!SpellApiToAi.Converter.get(sub.getApi()).chkDrawbackWithSubs(ai, sub)) { continue; } needsLimitedResources = true; // TODO: check for good drawbacks (gainLife) @@ -688,7 +690,7 @@ public class ComputerUtilMana { // don't use abilities with dangerous drawbacks AbilitySub sub = m.getSubAbility(); if (sub != null) { - if (!sub.getApi().getAi().chkDrawbackWithSubs(ai, sub)) { + if (!SpellApiToAi.Converter.get(sub.getApi()).chkDrawbackWithSubs(ai, sub)) { continue; } } diff --git a/forge-game/src/main/java/forge/ai/PlayerControllerAi.java b/forge-game/src/main/java/forge/ai/PlayerControllerAi.java index c8a57d44447..107a6713266 100644 --- a/forge-game/src/main/java/forge/ai/PlayerControllerAi.java +++ b/forge-game/src/main/java/forge/ai/PlayerControllerAi.java @@ -134,7 +134,7 @@ public class PlayerControllerAi extends PlayerController { if (null == api) { throw new InvalidParameterException("SA is not api-based, this is not supported yet"); } - return api.getAi().chooseSingleEntity(player, sa, options, isOptional, targetedPlayer); + return SpellApiToAi.Converter.get(api).chooseSingleEntity(player, sa, options, isOptional, targetedPlayer); } @Override @@ -143,7 +143,7 @@ public class PlayerControllerAi extends PlayerController { if (null == api) { throw new InvalidParameterException("SA is not api-based, this is not supported yet"); } - return api.getAi().chooseSingleSpellAbility(player, sa, spells); + return SpellApiToAi.Converter.get(api).chooseSingleSpellAbility(player, sa, spells); } @Override diff --git a/forge-game/src/main/java/forge/ai/SpellAbilityAi.java b/forge-game/src/main/java/forge/ai/SpellAbilityAi.java index d6ea74a3c30..016086fdc87 100644 --- a/forge-game/src/main/java/forge/ai/SpellAbilityAi.java +++ b/forge-game/src/main/java/forge/ai/SpellAbilityAi.java @@ -2,6 +2,7 @@ package forge.ai; import com.google.common.collect.Iterables; + import forge.game.GameEntity; import forge.game.ability.SaTargetRoutines; import forge.game.card.Card; @@ -136,7 +137,7 @@ public abstract class SpellAbilityAi extends SaTargetRoutines { */ public boolean chkDrawbackWithSubs(Player aiPlayer, AbilitySub ab) { final AbilitySub subAb = ab.getSubAbility(); - return ab.getApi().getAi().chkAIDrawback(ab, aiPlayer) && (subAb == null || chkDrawbackWithSubs(aiPlayer, subAb)); + return SpellApiToAi.Converter.get(ab.getApi()).chkAIDrawback(ab, aiPlayer) && (subAb == null || chkDrawbackWithSubs(aiPlayer, subAb)); } public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) { diff --git a/forge-game/src/main/java/forge/ai/SpellApiToAi.java b/forge-game/src/main/java/forge/ai/SpellApiToAi.java new file mode 100644 index 00000000000..2e02f151104 --- /dev/null +++ b/forge-game/src/main/java/forge/ai/SpellApiToAi.java @@ -0,0 +1,122 @@ +package forge.ai; + +import java.util.EnumMap; + +import forge.ai.ability.*; +import forge.game.ability.ApiType; +import forge.util.ReflectionUtil; + +public enum SpellApiToAi { + Converter; + + private final static EnumMap> apiToClass = new EnumMap<>(ApiType.class); + private final EnumMap apiToInstance = new EnumMap<>(ApiType.class); + + static { + apiToClass.put(ApiType.DamageAll, DamageAllAi.class); + apiToClass.put(ApiType.DealDamage, DamageDealAi.class); + apiToClass.put(ApiType.Debuff, DebuffAi.class); + apiToClass.put(ApiType.DebuffAll, DebuffAllAi.class); + apiToClass.put(ApiType.DeclareCombatants, CannotPlayAi.class); + apiToClass.put(ApiType.DelayedTrigger, DelayedTriggerAi.class); + apiToClass.put(ApiType.Destroy, DestroyAi.class); + apiToClass.put(ApiType.DestroyAll, DestroyAllAi.class); + apiToClass.put(ApiType.Dig, DigAi.class); + apiToClass.put(ApiType.DigUntil, DigUntilAi.class); + apiToClass.put(ApiType.Discard, DiscardAi.class); + apiToClass.put(ApiType.DrainMana, DrainManaAi.class); + apiToClass.put(ApiType.Draw, DrawAi.class); + apiToClass.put(ApiType.EachDamage, DamageEachAi.class); + apiToClass.put(ApiType.Effect, EffectAi.class); + apiToClass.put(ApiType.Encode, EncodeAi.class); + apiToClass.put(ApiType.EndTurn, EndTurnAi.class); + apiToClass.put(ApiType.ExchangeLife, LifeExchangeAi.class); + apiToClass.put(ApiType.ExchangeControl, ControlExchangeAi.class); + apiToClass.put(ApiType.ExchangePower, PowerExchangeAi.class); + apiToClass.put(ApiType.ExchangeZone, ZoneExchangeAi.class); + apiToClass.put(ApiType.Fight, FightAi.class); + apiToClass.put(ApiType.FlipACoin, FlipACoinAi.class); + apiToClass.put(ApiType.Fog, FogAi.class); + apiToClass.put(ApiType.GainControl, ControlGainAi.class); + apiToClass.put(ApiType.GainLife, LifeGainAi.class); + apiToClass.put(ApiType.GainOwnership, CannotPlayAi.class); + apiToClass.put(ApiType.GenericChoice, ChooseGenericEffectAi.class); + apiToClass.put(ApiType.LoseLife, LifeLoseAi.class); + apiToClass.put(ApiType.LosesGame, GameLossAi.class); + apiToClass.put(ApiType.Mana, ManaEffectAi.class); + apiToClass.put(ApiType.ManaReflected, CannotPlayAi.class); + apiToClass.put(ApiType.Mill, MillAi.class); + apiToClass.put(ApiType.MoveCounter, CountersMoveAi.class); + apiToClass.put(ApiType.MultiplePiles, CannotPlayAi.class); + apiToClass.put(ApiType.MustAttack, MustAttackAi.class); + apiToClass.put(ApiType.MustBlock, MustBlockAi.class); + apiToClass.put(ApiType.NameCard, ChooseCardNameAi.class); + apiToClass.put(ApiType.PeekAndReveal, PeekAndRevealAi.class); + apiToClass.put(ApiType.PermanentCreature, PermanentCreatureAi.class); + apiToClass.put(ApiType.PermanentNoncreature, PermanentNoncreatureAi.class); + apiToClass.put(ApiType.Phases, PhasesAi.class); + apiToClass.put(ApiType.Planeswalk, AlwaysPlayAi.class); + apiToClass.put(ApiType.Play, PlayAi.class); + apiToClass.put(ApiType.Poison, PoisonAi.class); + apiToClass.put(ApiType.PreventDamage, DamagePreventAi.class); + apiToClass.put(ApiType.PreventDamageAll, DamagePreventAllAi.class); + apiToClass.put(ApiType.Proliferate, CountersProliferateAi.class); + apiToClass.put(ApiType.Protection, ProtectAi.class); + apiToClass.put(ApiType.ProtectionAll, ProtectAllAi.class); + apiToClass.put(ApiType.Pump, PumpAi.class); + apiToClass.put(ApiType.PumpAll, PumpAllAi.class); + apiToClass.put(ApiType.PutCounter, CountersPutAi.class); + apiToClass.put(ApiType.PutCounterAll, CountersPutAllAi.class); + apiToClass.put(ApiType.RearrangeTopOfLibrary, RearrangeTopOfLibraryAi.class); + apiToClass.put(ApiType.Regenerate, RegenerateAi.class); + apiToClass.put(ApiType.RegenerateAll, RegenerateAllAi.class); + apiToClass.put(ApiType.RemoveCounter, CountersRemoveAi.class); + apiToClass.put(ApiType.RemoveCounterAll, CannotPlayAi.class); + apiToClass.put(ApiType.RemoveFromCombat, RemoveFromCombatAi.class); + apiToClass.put(ApiType.ReorderZone, AlwaysPlayAi.class); + apiToClass.put(ApiType.Repeat, RepeatAi.class); + apiToClass.put(ApiType.RepeatEach, RepeatEachAi.class); + apiToClass.put(ApiType.RestartGame, RestartGameAi.class); + apiToClass.put(ApiType.Reveal, RevealAi.class); + apiToClass.put(ApiType.RevealHand, RevealHandAi.class); + apiToClass.put(ApiType.RollPlanarDice, RollPlanarDiceAi.class); + apiToClass.put(ApiType.RunSVarAbility, AlwaysPlayAi.class); + apiToClass.put(ApiType.Sacrifice, SacrificeAi.class); + apiToClass.put(ApiType.SacrificeAll, SacrificeAllAi.class); + apiToClass.put(ApiType.Scry, ScryAi.class); + apiToClass.put(ApiType.SetInMotion, AlwaysPlayAi.class); + apiToClass.put(ApiType.SetLife, LifeSetAi.class); + apiToClass.put(ApiType.SetState, SetStateAi.class); + apiToClass.put(ApiType.Shuffle, ShuffleAi.class); + apiToClass.put(ApiType.SkipTurn, CannotPlayAi.class); + apiToClass.put(ApiType.StoreSVar, StoreSVarAi.class); + apiToClass.put(ApiType.Tap, TapAi.class); + apiToClass.put(ApiType.TapAll, TapAllAi.class); + apiToClass.put(ApiType.TapOrUntap, TapOrUntapAi.class); + apiToClass.put(ApiType.TapOrUntapAll, TapOrUntapAllAi.class); + apiToClass.put(ApiType.Token, TokenAi.class); + apiToClass.put(ApiType.TwoPiles, TwoPilesAi.class); + apiToClass.put(ApiType.UnattachAll, UnattachAllAi.class); + apiToClass.put(ApiType.Untap, UntapAi.class); + apiToClass.put(ApiType.UntapAll, UntapAllAi.class); + apiToClass.put(ApiType.WinsGame, GameWinAi.class); + + apiToClass.put(ApiType.InternalEtbReplacement, CanPlayAsDrawbackAi.class); + apiToClass.put(ApiType.InternalLegendaryRule, LegendaryRuleAi.class); + apiToClass.put(ApiType.InternalHaunt, HauntAi.class); + } + + public SpellAbilityAi get(ApiType api) { + SpellAbilityAi result = apiToInstance.get(api); + if( null == result ) { + Class clz = apiToClass.get(api); + if(null == clz) { + System.err.println("No AI assigned for API: " + api); + clz = CannotPlayAi.class; + } + result = ReflectionUtil.makeDefaultInstanceOf(clz); + apiToInstance.put(api, result); + } + return result; + } +} diff --git a/forge-game/src/main/java/forge/ai/ability/ChangeZoneAi.java b/forge-game/src/main/java/forge/ai/ability/ChangeZoneAi.java index a647db9bcae..97bc2a7ff68 100644 --- a/forge-game/src/main/java/forge/ai/ability/ChangeZoneAi.java +++ b/forge-game/src/main/java/forge/ai/ability/ChangeZoneAi.java @@ -4,6 +4,7 @@ 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 forge.ai.*; import forge.card.MagicColor; import forge.game.Game; @@ -307,7 +308,7 @@ public class ChangeZoneAi extends SpellAbilityAi { } final AbilitySub subAb = sa.getSubAbility(); - return subAb == null || subAb.getApi().getAi().chkDrawbackWithSubs(ai, subAb); + return subAb == null || SpellApiToAi.Converter.get(subAb.getApi()).chkDrawbackWithSubs(ai, subAb); } /** @@ -645,7 +646,7 @@ public class ChangeZoneAi extends SpellAbilityAi { } final AbilitySub subAb = sa.getSubAbility(); - chance &= subAb == null || subAb.getApi().getAi().chkDrawbackWithSubs(ai, subAb); + chance &= subAb == null || SpellApiToAi.Converter.get(subAb.getApi()).chkDrawbackWithSubs(ai, subAb); return chance; } diff --git a/forge-game/src/main/java/forge/ai/ability/DelayedTriggerAi.java b/forge-game/src/main/java/forge/ai/ability/DelayedTriggerAi.java index 63bd72cfadd..2697407f86a 100644 --- a/forge-game/src/main/java/forge/ai/ability/DelayedTriggerAi.java +++ b/forge-game/src/main/java/forge/ai/ability/DelayedTriggerAi.java @@ -4,6 +4,7 @@ import forge.ai.AiController; import forge.ai.AiPlayDecision; import forge.ai.PlayerControllerAi; import forge.ai.SpellAbilityAi; +import forge.ai.SpellApiToAi; import forge.game.ability.AbilityFactory; import forge.game.player.Player; import forge.game.spellability.AbilitySub; @@ -18,7 +19,7 @@ public class DelayedTriggerAi extends SpellAbilityAi { trigsa.setActivatingPlayer(ai); if (trigsa instanceof AbilitySub) { - return ((AbilitySub) trigsa).getApi().getAi().chkDrawbackWithSubs(ai, (AbilitySub)trigsa); + return SpellApiToAi.Converter.get(((AbilitySub) trigsa).getApi()).chkDrawbackWithSubs(ai, (AbilitySub)trigsa); } else { return AiPlayDecision.WillPlay == ((PlayerControllerAi)ai.getController()).getAi().canPlaySa(trigsa); } diff --git a/forge-game/src/main/java/forge/ai/ability/PeekAndRevealAi.java b/forge-game/src/main/java/forge/ai/ability/PeekAndRevealAi.java index 1fe0c9b5f72..ad35001c375 100644 --- a/forge-game/src/main/java/forge/ai/ability/PeekAndRevealAi.java +++ b/forge-game/src/main/java/forge/ai/ability/PeekAndRevealAi.java @@ -1,6 +1,7 @@ package forge.ai.ability; import forge.ai.SpellAbilityAi; +import forge.ai.SpellApiToAi; import forge.game.player.Player; import forge.game.player.PlayerActionConfirmMode; import forge.game.spellability.AbilityStatic; @@ -32,7 +33,7 @@ public class PeekAndRevealAi extends SpellAbilityAi { @Override public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) { AbilitySub subAb = sa.getSubAbility(); - return subAb != null && subAb.getApi().getAi().chkDrawbackWithSubs(player, subAb); + return subAb != null && SpellApiToAi.Converter.get(subAb.getApi()).chkDrawbackWithSubs(player, subAb); } } diff --git a/forge-game/src/main/java/forge/game/ability/ApiType.java b/forge-game/src/main/java/forge/game/ability/ApiType.java index f8f86378496..ffa203bc15b 100644 --- a/forge-game/src/main/java/forge/game/ability/ApiType.java +++ b/forge-game/src/main/java/forge/game/ability/ApiType.java @@ -1,8 +1,6 @@ package forge.game.ability; -import forge.ai.SpellAbilityAi; -import forge.ai.ability.*; import forge.game.ability.effects.*; import forge.util.ReflectionUtil; @@ -14,156 +12,144 @@ import java.util.TreeMap; * */ public enum ApiType { + Abandon (AbandonEffect.class), + AddOrRemoveCounter (CountersPutOrRemoveEffect.class), + AddPhase (AddPhaseEffect.class), + AddTurn (AddTurnEffect.class), + Animate (AnimateEffect.class), + AnimateAll (AnimateAllEffect.class), + Attach (AttachEffect.class), + Balance (BalanceEffect.class), + BecomesBlocked (BecomesBlockedEffect.class), + Bond (BondEffect.class), + ChangeTargets (ChangeTargetsEffect.class), + ChangeZone (ChangeZoneEffect.class), + ChangeZoneAll (ChangeZoneAllEffect.class), + + Charm (CharmEffect.class), + ChooseCard (ChooseCardEffect.class), + ChooseColor (ChooseColorEffect.class), + ChooseNumber (ChooseNumberEffect.class), + ChoosePlayer (ChoosePlayerEffect.class), + ChooseSource (ChooseSourceEffect.class), + ChooseType (ChooseTypeEffect.class), + Clash (ClashEffect.class), + Cleanup (CleanUpEffect.class), + Clone (CloneEffect.class), + CopyPermanent (CopyPermanentEffect.class), + CopySpellAbility (CopySpellAbilityEffect.class), + ControlPlayer (ControlPlayerEffect.class), + Counter (CounterEffect.class), + DamageAll (DamageAllEffect.class), + DealDamage (DamageDealEffect.class), + Debuff (DebuffEffect.class), + DebuffAll (DebuffAllEffect.class), + DeclareCombatants (DeclareCombatantsEffect.class), + DelayedTrigger (DelayedTriggerEffect.class), + Destroy (DestroyEffect.class), + DestroyAll (DestroyAllEffect.class), + Dig (DigEffect.class), + DigUntil (DigUntilEffect.class), + Discard (DiscardEffect.class), + DrainMana (DrainManaEffect.class), + Draw (DrawEffect.class), + EachDamage (DamageEachEffect.class), + Effect (EffectEffect.class), + Encode (EncodeEffect.class), + EndTurn (EndTurnEffect.class), + ExchangeLife (LifeExchangeEffect.class), + ExchangeControl (ControlExchangeEffect.class), + ExchangePower (PowerExchangeEffect.class), + ExchangeZone (ZoneExchangeEffect.class), + Fight (FightEffect.class), + FlipACoin (FlipCoinEffect.class), + Fog (FogEffect.class), + GainControl (ControlGainEffect.class), + GainLife (LifeGainEffect.class), + GainOwnership (OwnershipGainEffect.class), + GenericChoice (ChooseGenericEffect.class), + LoseLife (LifeLoseEffect.class), + LosesGame (GameLossEffect.class), + Mana (ManaEffect.class), + ManaReflected (ManaReflectedEffect.class), + Mill (MillEffect.class), + MoveCounter (CountersMoveEffect.class), + MultiplePiles (MultiplePilesEffect.class), + MustAttack (MustAttackEffect.class), + MustBlock (MustBlockEffect.class), + NameCard (ChooseCardNameEffect.class), + PeekAndReveal (PeekAndRevealEffect.class), + PermanentCreature (PermanentCreatureEffect.class), + PermanentNoncreature (PermanentNoncreatureEffect.class), + Phases (PhasesEffect.class), + Planeswalk (PlaneswalkEffect.class), + Play (PlayEffect.class), + Poison (PoisonEffect.class), + PreventDamage (DamagePreventEffect.class), + PreventDamageAll (DamagePreventAllEffect.class), + Proliferate (CountersProliferateEffect.class), + Protection (ProtectEffect.class), + ProtectionAll (ProtectAllEffect.class), + Pump (PumpEffect.class), + PumpAll (PumpAllEffect.class), + PutCounter (CountersPutEffect.class), + PutCounterAll (CountersPutAllEffect.class), + RearrangeTopOfLibrary (RearrangeTopOfLibraryEffect.class), + Regenerate (RegenerateEffect.class), + RegenerateAll (RegenerateAllEffect.class), + RemoveCounter (CountersRemoveEffect.class), + RemoveCounterAll (CountersRemoveAllEffect.class), + RemoveFromCombat (RemoveFromCombatEffect.class), + ReorderZone (ReorderZoneEffect.class), + Repeat (RepeatEffect.class), + RepeatEach (RepeatEachEffect.class), + RestartGame (RestartGameEffect.class), + Reveal (RevealEffect.class), + RevealHand (RevealHandEffect.class), + RollPlanarDice (RollPlanarDiceEffect.class), + RunSVarAbility (RunSVarAbilityEffect.class), + Sacrifice (SacrificeEffect.class), + SacrificeAll (SacrificeAllEffect.class), + Scry (ScryEffect.class), + SetInMotion (SetInMotionEffect.class), + SetLife (LifeSetEffect.class), + SetState (SetStateEffect.class), + Shuffle (ShuffleEffect.class), + SkipTurn (SkipTurnEffect.class), + StoreSVar (StoreSVarEffect.class), + Tap (TapEffect.class), + TapAll (TapAllEffect.class), + TapOrUntap (TapOrUntapEffect.class), + TapOrUntapAll (TapOrUntapAllEffect.class), + Token (TokenEffect.class, false), + TwoPiles (TwoPilesEffect.class), + UnattachAll (UnattachAllEffect.class), + Untap (UntapEffect.class), + UntapAll (UntapAllEffect.class), + WinsGame (GameWinEffect.class), + + + InternalEtbReplacement (ETBReplacementEffect.class), + InternalLegendaryRule (CharmEffect.class), + InternalHaunt (CharmEffect.class); - Abandon (AbandonEffect.class, AlwaysPlayAi.class), - AddOrRemoveCounter (CountersPutOrRemoveEffect.class, CountersPutOrRemoveAi.class), - AddPhase (AddPhaseEffect.class, AddPhaseAi.class), - AddTurn (AddTurnEffect.class, AddTurnAi.class), - Animate (AnimateEffect.class, AnimateAi.class), - AnimateAll (AnimateAllEffect.class, AnimateAllAi.class), - Attach (AttachEffect.class, AttachAi.class), - Balance (BalanceEffect.class, BalanceAi.class), - BecomesBlocked (BecomesBlockedEffect.class, BecomesBlockedAi.class), - Bond (BondEffect.class, BondAi.class), - ChangeTargets(ChangeTargetsEffect.class, ChangeTargetsAi.class), - ChangeZone(ChangeZoneEffect.class, ChangeZoneAi.class), - ChangeZoneAll(ChangeZoneAllEffect.class, ChangeZoneAllAi.class), - /** This is Modal, like 'choose one - ' or 'choose two - '.
Might be great to rename this api and update all scripts.*/ - Charm(CharmEffect.class, CharmAi.class), - ChooseCard (ChooseCardEffect.class, ChooseCardAi.class), - ChooseColor (ChooseColorEffect.class, ChooseColorAi.class), - ChooseNumber (ChooseNumberEffect.class, CannotPlayAi.class), - ChoosePlayer (ChoosePlayerEffect.class, ChoosePlayerAi.class), - ChooseSource (ChooseSourceEffect.class, ChooseSourceAi.class), - ChooseType (ChooseTypeEffect.class, ChooseTypeAi.class), - Clash (ClashEffect.class, ClashAi.class), - Cleanup (CleanUpEffect.class, AlwaysPlayAi.class), - Clone (CloneEffect.class, CloneAi.class), - CopyPermanent (CopyPermanentEffect.class, CopyPermanentAi.class), - CopySpellAbility (CopySpellAbilityEffect.class, CanPlayAsDrawbackAi.class), - ControlPlayer(ControlPlayerEffect.class, CannotPlayAi.class), - Counter (CounterEffect.class, CounterAi.class), - DamageAll (DamageAllEffect.class, DamageAllAi.class), - DealDamage (DamageDealEffect.class, DamageDealAi.class), - Debuff (DebuffEffect.class, DebuffAi.class), - DebuffAll (DebuffAllEffect.class, DebuffAllAi.class), - DeclareCombatants(DeclareCombatantsEffect.class, CannotPlayAi.class), - DelayedTrigger (DelayedTriggerEffect.class, DelayedTriggerAi.class), - Destroy (DestroyEffect.class, DestroyAi.class), - DestroyAll (DestroyAllEffect.class, DestroyAllAi.class), - Dig (DigEffect.class, DigAi.class), - DigUntil (DigUntilEffect.class, DigUntilAi.class), - Discard (DiscardEffect.class, DiscardAi.class), - DrainMana (DrainManaEffect.class, DrainManaAi.class), - Draw (DrawEffect.class, DrawAi.class), - EachDamage (DamageEachEffect.class, DamageEachAi.class), - Effect (EffectEffect.class, EffectAi.class), - Encode (EncodeEffect.class, EncodeAi.class), - EndTurn (EndTurnEffect.class, EndTurnAi.class), - ExchangeLife (LifeExchangeEffect.class, LifeExchangeAi.class), - ExchangeControl (ControlExchangeEffect.class, ControlExchangeAi.class), - ExchangePower (PowerExchangeEffect.class, PowerExchangeAi.class), - ExchangeZone (ZoneExchangeEffect.class, ZoneExchangeAi.class), - Fight (FightEffect.class, FightAi.class), - FlipACoin (FlipCoinEffect.class, FlipACoinAi.class), - Fog (FogEffect.class, FogAi.class), - GainControl (ControlGainEffect.class, ControlGainAi.class), - GainLife (LifeGainEffect.class, LifeGainAi.class), - GainOwnership(OwnershipGainEffect.class, CannotPlayAi.class), - GenericChoice (ChooseGenericEffect.class, ChooseGenericEffectAi.class), - LoseLife (LifeLoseEffect.class, LifeLoseAi.class), - LosesGame (GameLossEffect.class, GameLossAi.class), - Mana (ManaEffect.class, ManaEffectAi.class), - ManaReflected (ManaReflectedEffect.class, CannotPlayAi.class), - Mill (MillEffect.class, MillAi.class), - MoveCounter (CountersMoveEffect.class, CountersMoveAi.class), - MultiplePiles (MultiplePilesEffect.class, CannotPlayAi.class), - MustAttack (MustAttackEffect.class, MustAttackAi.class), - MustBlock (MustBlockEffect.class, MustBlockAi.class), - NameCard (ChooseCardNameEffect.class, ChooseCardNameAi.class), - PeekAndReveal (PeekAndRevealEffect.class, PeekAndRevealAi.class), - PermanentCreature (PermanentCreatureEffect.class, PermanentCreatureAi.class), - PermanentNoncreature (PermanentNoncreatureEffect.class, PermanentNoncreatureAi.class), - Phases (PhasesEffect.class, PhasesAi.class), - Planeswalk(PlaneswalkEffect.class, AlwaysPlayAi.class), - Play (PlayEffect.class, PlayAi.class), - Poison (PoisonEffect.class, PoisonAi.class), - PreventDamage (DamagePreventEffect.class, DamagePreventAi.class), - PreventDamageAll (DamagePreventAllEffect.class, DamagePreventAllAi.class), - Proliferate (CountersProliferateEffect.class, CountersProliferateAi.class), - Protection (ProtectEffect.class, ProtectAi.class), - ProtectionAll (ProtectAllEffect.class, ProtectAllAi.class), - Pump (PumpEffect.class, PumpAi.class), - PumpAll (PumpAllEffect.class, PumpAllAi.class), - PutCounter (CountersPutEffect.class, CountersPutAi.class), - PutCounterAll (CountersPutAllEffect.class, CountersPutAllAi.class), - RearrangeTopOfLibrary (RearrangeTopOfLibraryEffect.class, RearrangeTopOfLibraryAi.class), - Regenerate (RegenerateEffect.class, RegenerateAi.class), - RegenerateAll (RegenerateAllEffect.class, RegenerateAllAi.class), - RemoveCounter (CountersRemoveEffect.class, CountersRemoveAi.class), - RemoveCounterAll (CountersRemoveAllEffect.class, CannotPlayAi.class), - RemoveFromCombat (RemoveFromCombatEffect.class, RemoveFromCombatAi.class), - ReorderZone (ReorderZoneEffect.class, AlwaysPlayAi.class), - Repeat (RepeatEffect.class, RepeatAi.class), - RepeatEach (RepeatEachEffect.class, RepeatEachAi.class), - RestartGame (RestartGameEffect.class, RestartGameAi.class), - Reveal (RevealEffect.class, RevealAi.class), - RevealHand (RevealHandEffect.class, RevealHandAi.class), - RollPlanarDice (RollPlanarDiceEffect.class, RollPlanarDiceAi.class), - RunSVarAbility (RunSVarAbilityEffect.class, AlwaysPlayAi.class), - Sacrifice (SacrificeEffect.class, SacrificeAi.class), - SacrificeAll (SacrificeAllEffect.class, SacrificeAllAi.class), - Scry (ScryEffect.class, ScryAi.class), - SetInMotion (SetInMotionEffect.class, AlwaysPlayAi.class), - SetLife (LifeSetEffect.class, LifeSetAi.class), - SetState (SetStateEffect.class, SetStateAi.class), - Shuffle (ShuffleEffect.class, ShuffleAi.class), - SkipTurn (SkipTurnEffect.class, CannotPlayAi.class), - StoreSVar (StoreSVarEffect.class, StoreSVarAi.class), - Tap (TapEffect.class, TapAi.class), - TapAll (TapAllEffect.class, TapAllAi.class), - TapOrUntap (TapOrUntapEffect.class, TapOrUntapAi.class), - TapOrUntapAll (TapOrUntapAllEffect.class, TapOrUntapAllAi.class), - Token (TokenEffect.class, TokenAi.class, false), // Token AI and effect classes have state, they have to be re-created on each request - TwoPiles (TwoPilesEffect.class, TwoPilesAi.class), - UnattachAll (UnattachAllEffect.class, UnattachAllAi.class), - Untap (UntapEffect.class, UntapAi.class), - UntapAll (UntapAllEffect.class, UntapAllAi.class), - WinsGame (GameWinEffect.class, GameWinAi.class), - - - InternalEtbReplacement(ETBReplacementEffect.class, CanPlayAsDrawbackAi.class), - InternalLegendaryRule(CharmEffect.class, LegendaryRuleAi.class), // Charm has empty resolve blocks, may act as a dummy - InternalHaunt(CharmEffect.class, HauntAi.class); - private final Class clsEffect; - private final Class clsAi; - private final SpellAbilityEffect instanceEffect; - private final SpellAbilityAi instanceAi; + private final Class clsEffect; - - private static final Map allValues = new TreeMap(String.CASE_INSENSITIVE_ORDER); - ApiType(Class clsEf, Class clsAi) { - this(clsEf, clsAi, true); - } - - ApiType(Class clsEf, Class clsAI, final boolean isStateLess) { + ApiType(Class clsEf) { this(clsEf, true); } + ApiType(Class clsEf, final boolean isStateLess) { clsEffect = clsEf; - clsAi = clsAI; - - instanceAi = isStateLess ? ReflectionUtil.makeDefaultInstanceOf(clsAi) : null; instanceEffect = isStateLess ? ReflectionUtil.makeDefaultInstanceOf(clsEf) : null; - } public static ApiType smartValueOf(String value) { if (allValues.isEmpty()) for(ApiType c : ApiType.values()) allValues.put(c.toString(), c); - + ApiType v = allValues.get(value); if ( v == null ) throw new RuntimeException("Element " + value + " not found in ApiType enum"); @@ -173,9 +159,4 @@ public enum ApiType { public SpellAbilityEffect getSpellEffect() { return instanceEffect != null ? instanceEffect : ReflectionUtil.makeDefaultInstanceOf(clsEffect); } - - public SpellAbilityAi getAi() { - return instanceAi != null ? instanceAi : ReflectionUtil.makeDefaultInstanceOf(clsAi); - } - } diff --git a/forge-gui/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java b/forge-gui/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java index e71d485d53b..5f4ff4199b9 100644 --- a/forge-gui/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java +++ b/forge-gui/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java @@ -6,6 +6,8 @@ import com.google.common.collect.Multimap; import forge.ai.ComputerUtil; import forge.ai.ComputerUtilMana; +import forge.ai.SpellAbilityAi; +import forge.ai.SpellApiToAi; import forge.ai.ability.ChangeZoneAi; import forge.ai.ability.DrawAi; import forge.ai.ability.GameWinAi; @@ -96,10 +98,11 @@ public class PlayerControllerForTests extends PlayerController { HumanPlay.playSpellAbilityNoStack(player, effectSA, !mayChoseNewTargets); return; } + SpellAbilityAi sai = SpellApiToAi.Converter.get(effectSA.getApi()); if ( - (effectSA.getHostCard().getName().equals("Nefarious Lich") && effectSA.getApi().getAi() instanceof DrawAi) || - (effectSA.getHostCard().getName().equals("Laboratory Maniac") && effectSA.getApi().getAi() instanceof GameWinAi) || - (effectSA.getHostCard().getName().equals("Nefarious Lich") && effectSA.getApi().getAi() instanceof ChangeZoneAi) + (effectSA.getHostCard().getName().equals("Nefarious Lich") && sai instanceof DrawAi) || + (effectSA.getHostCard().getName().equals("Laboratory Maniac") && sai instanceof GameWinAi) || + (effectSA.getHostCard().getName().equals("Nefarious Lich") && sai instanceof ChangeZoneAi) ) {//test_104_3f_if_a_player_would_win_and_lose_simultaneously_he_loses HumanPlay.playSpellAbilityNoStack(player, effectSA, !mayChoseNewTargets); return;