mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-11 16:26:22 +00:00
Normalize line endings
This commit is contained in:
254
.fbprefs
254
.fbprefs
@@ -1,127 +1,127 @@
|
|||||||
#FindBugs User Preferences
|
#FindBugs User Preferences
|
||||||
#Wed Jul 27 18:41:56 EDT 2011
|
#Wed Jul 27 18:41:56 EDT 2011
|
||||||
detectorAppendingToAnObjectOutputStream=AppendingToAnObjectOutputStream|true
|
detectorAppendingToAnObjectOutputStream=AppendingToAnObjectOutputStream|true
|
||||||
detectorBadAppletConstructor=BadAppletConstructor|false
|
detectorBadAppletConstructor=BadAppletConstructor|false
|
||||||
detectorBadResultSetAccess=BadResultSetAccess|true
|
detectorBadResultSetAccess=BadResultSetAccess|true
|
||||||
detectorBadSyntaxForRegularExpression=BadSyntaxForRegularExpression|true
|
detectorBadSyntaxForRegularExpression=BadSyntaxForRegularExpression|true
|
||||||
detectorBadUseOfReturnValue=BadUseOfReturnValue|true
|
detectorBadUseOfReturnValue=BadUseOfReturnValue|true
|
||||||
detectorBadlyOverriddenAdapter=BadlyOverriddenAdapter|true
|
detectorBadlyOverriddenAdapter=BadlyOverriddenAdapter|true
|
||||||
detectorBooleanReturnNull=BooleanReturnNull|true
|
detectorBooleanReturnNull=BooleanReturnNull|true
|
||||||
detectorCallToUnsupportedMethod=CallToUnsupportedMethod|false
|
detectorCallToUnsupportedMethod=CallToUnsupportedMethod|false
|
||||||
detectorCheckImmutableAnnotation=CheckImmutableAnnotation|true
|
detectorCheckImmutableAnnotation=CheckImmutableAnnotation|true
|
||||||
detectorCheckTypeQualifiers=CheckTypeQualifiers|true
|
detectorCheckTypeQualifiers=CheckTypeQualifiers|true
|
||||||
detectorCloneIdiom=CloneIdiom|true
|
detectorCloneIdiom=CloneIdiom|true
|
||||||
detectorComparatorIdiom=ComparatorIdiom|true
|
detectorComparatorIdiom=ComparatorIdiom|true
|
||||||
detectorConfusedInheritance=ConfusedInheritance|true
|
detectorConfusedInheritance=ConfusedInheritance|true
|
||||||
detectorConfusionBetweenInheritedAndOuterMethod=ConfusionBetweenInheritedAndOuterMethod|true
|
detectorConfusionBetweenInheritedAndOuterMethod=ConfusionBetweenInheritedAndOuterMethod|true
|
||||||
detectorCrossSiteScripting=CrossSiteScripting|true
|
detectorCrossSiteScripting=CrossSiteScripting|true
|
||||||
detectorDoInsideDoPrivileged=DoInsideDoPrivileged|true
|
detectorDoInsideDoPrivileged=DoInsideDoPrivileged|true
|
||||||
detectorDontCatchIllegalMonitorStateException=DontCatchIllegalMonitorStateException|true
|
detectorDontCatchIllegalMonitorStateException=DontCatchIllegalMonitorStateException|true
|
||||||
detectorDontIgnoreResultOfPutIfAbsent=DontIgnoreResultOfPutIfAbsent|true
|
detectorDontIgnoreResultOfPutIfAbsent=DontIgnoreResultOfPutIfAbsent|true
|
||||||
detectorDontUseEnum=DontUseEnum|true
|
detectorDontUseEnum=DontUseEnum|true
|
||||||
detectorDroppedException=DroppedException|true
|
detectorDroppedException=DroppedException|true
|
||||||
detectorDumbMethodInvocations=DumbMethodInvocations|true
|
detectorDumbMethodInvocations=DumbMethodInvocations|true
|
||||||
detectorDumbMethods=DumbMethods|true
|
detectorDumbMethods=DumbMethods|true
|
||||||
detectorDuplicateBranches=DuplicateBranches|true
|
detectorDuplicateBranches=DuplicateBranches|true
|
||||||
detectorEmptyZipFileEntry=EmptyZipFileEntry|true
|
detectorEmptyZipFileEntry=EmptyZipFileEntry|true
|
||||||
detectorEqualsOperandShouldHaveClassCompatibleWithThis=EqualsOperandShouldHaveClassCompatibleWithThis|true
|
detectorEqualsOperandShouldHaveClassCompatibleWithThis=EqualsOperandShouldHaveClassCompatibleWithThis|true
|
||||||
detectorFinalizerNullsFields=FinalizerNullsFields|true
|
detectorFinalizerNullsFields=FinalizerNullsFields|true
|
||||||
detectorFindBadCast2=FindBadCast2|true
|
detectorFindBadCast2=FindBadCast2|true
|
||||||
detectorFindBadForLoop=FindBadForLoop|true
|
detectorFindBadForLoop=FindBadForLoop|true
|
||||||
detectorFindCircularDependencies=FindCircularDependencies|false
|
detectorFindCircularDependencies=FindCircularDependencies|false
|
||||||
detectorFindDeadLocalStores=FindDeadLocalStores|true
|
detectorFindDeadLocalStores=FindDeadLocalStores|true
|
||||||
detectorFindDoubleCheck=FindDoubleCheck|true
|
detectorFindDoubleCheck=FindDoubleCheck|true
|
||||||
detectorFindEmptySynchronizedBlock=FindEmptySynchronizedBlock|true
|
detectorFindEmptySynchronizedBlock=FindEmptySynchronizedBlock|true
|
||||||
detectorFindFieldSelfAssignment=FindFieldSelfAssignment|true
|
detectorFindFieldSelfAssignment=FindFieldSelfAssignment|true
|
||||||
detectorFindFinalizeInvocations=FindFinalizeInvocations|true
|
detectorFindFinalizeInvocations=FindFinalizeInvocations|true
|
||||||
detectorFindFloatEquality=FindFloatEquality|true
|
detectorFindFloatEquality=FindFloatEquality|true
|
||||||
detectorFindHEmismatch=FindHEmismatch|true
|
detectorFindHEmismatch=FindHEmismatch|true
|
||||||
detectorFindInconsistentSync2=FindInconsistentSync2|true
|
detectorFindInconsistentSync2=FindInconsistentSync2|true
|
||||||
detectorFindJSR166LockMonitorenter=FindJSR166LockMonitorenter|true
|
detectorFindJSR166LockMonitorenter=FindJSR166LockMonitorenter|true
|
||||||
detectorFindLocalSelfAssignment2=FindLocalSelfAssignment2|true
|
detectorFindLocalSelfAssignment2=FindLocalSelfAssignment2|true
|
||||||
detectorFindMaskedFields=FindMaskedFields|true
|
detectorFindMaskedFields=FindMaskedFields|true
|
||||||
detectorFindMismatchedWaitOrNotify=FindMismatchedWaitOrNotify|true
|
detectorFindMismatchedWaitOrNotify=FindMismatchedWaitOrNotify|true
|
||||||
detectorFindNakedNotify=FindNakedNotify|true
|
detectorFindNakedNotify=FindNakedNotify|true
|
||||||
detectorFindNonSerializableStoreIntoSession=FindNonSerializableStoreIntoSession|true
|
detectorFindNonSerializableStoreIntoSession=FindNonSerializableStoreIntoSession|true
|
||||||
detectorFindNonSerializableValuePassedToWriteObject=FindNonSerializableValuePassedToWriteObject|true
|
detectorFindNonSerializableValuePassedToWriteObject=FindNonSerializableValuePassedToWriteObject|true
|
||||||
detectorFindNonShortCircuit=FindNonShortCircuit|true
|
detectorFindNonShortCircuit=FindNonShortCircuit|true
|
||||||
detectorFindNullDeref=FindNullDeref|true
|
detectorFindNullDeref=FindNullDeref|true
|
||||||
detectorFindNullDerefsInvolvingNonShortCircuitEvaluation=FindNullDerefsInvolvingNonShortCircuitEvaluation|true
|
detectorFindNullDerefsInvolvingNonShortCircuitEvaluation=FindNullDerefsInvolvingNonShortCircuitEvaluation|true
|
||||||
detectorFindOpenStream=FindOpenStream|true
|
detectorFindOpenStream=FindOpenStream|true
|
||||||
detectorFindPuzzlers=FindPuzzlers|true
|
detectorFindPuzzlers=FindPuzzlers|true
|
||||||
detectorFindRefComparison=FindRefComparison|true
|
detectorFindRefComparison=FindRefComparison|true
|
||||||
detectorFindReturnRef=FindReturnRef|true
|
detectorFindReturnRef=FindReturnRef|true
|
||||||
detectorFindRunInvocations=FindRunInvocations|true
|
detectorFindRunInvocations=FindRunInvocations|true
|
||||||
detectorFindSelfComparison=FindSelfComparison|true
|
detectorFindSelfComparison=FindSelfComparison|true
|
||||||
detectorFindSelfComparison2=FindSelfComparison2|true
|
detectorFindSelfComparison2=FindSelfComparison2|true
|
||||||
detectorFindSleepWithLockHeld=FindSleepWithLockHeld|true
|
detectorFindSleepWithLockHeld=FindSleepWithLockHeld|true
|
||||||
detectorFindSpinLoop=FindSpinLoop|true
|
detectorFindSpinLoop=FindSpinLoop|true
|
||||||
detectorFindSqlInjection=FindSqlInjection|true
|
detectorFindSqlInjection=FindSqlInjection|true
|
||||||
detectorFindTwoLockWait=FindTwoLockWait|true
|
detectorFindTwoLockWait=FindTwoLockWait|true
|
||||||
detectorFindUncalledPrivateMethods=FindUncalledPrivateMethods|true
|
detectorFindUncalledPrivateMethods=FindUncalledPrivateMethods|true
|
||||||
detectorFindUnconditionalWait=FindUnconditionalWait|true
|
detectorFindUnconditionalWait=FindUnconditionalWait|true
|
||||||
detectorFindUninitializedGet=FindUninitializedGet|true
|
detectorFindUninitializedGet=FindUninitializedGet|true
|
||||||
detectorFindUnrelatedTypesInGenericContainer=FindUnrelatedTypesInGenericContainer|true
|
detectorFindUnrelatedTypesInGenericContainer=FindUnrelatedTypesInGenericContainer|true
|
||||||
detectorFindUnreleasedLock=FindUnreleasedLock|true
|
detectorFindUnreleasedLock=FindUnreleasedLock|true
|
||||||
detectorFindUnsatisfiedObligation=FindUnsatisfiedObligation|true
|
detectorFindUnsatisfiedObligation=FindUnsatisfiedObligation|true
|
||||||
detectorFindUnsyncGet=FindUnsyncGet|true
|
detectorFindUnsyncGet=FindUnsyncGet|true
|
||||||
detectorFindUselessControlFlow=FindUselessControlFlow|true
|
detectorFindUselessControlFlow=FindUselessControlFlow|true
|
||||||
detectorFormatStringChecker=FormatStringChecker|true
|
detectorFormatStringChecker=FormatStringChecker|true
|
||||||
detectorHugeSharedStringConstants=HugeSharedStringConstants|true
|
detectorHugeSharedStringConstants=HugeSharedStringConstants|true
|
||||||
detectorIDivResultCastToDouble=IDivResultCastToDouble|true
|
detectorIDivResultCastToDouble=IDivResultCastToDouble|true
|
||||||
detectorIncompatMask=IncompatMask|true
|
detectorIncompatMask=IncompatMask|true
|
||||||
detectorInconsistentAnnotations=InconsistentAnnotations|true
|
detectorInconsistentAnnotations=InconsistentAnnotations|true
|
||||||
detectorInefficientMemberAccess=InefficientMemberAccess|false
|
detectorInefficientMemberAccess=InefficientMemberAccess|false
|
||||||
detectorInefficientToArray=InefficientToArray|true
|
detectorInefficientToArray=InefficientToArray|true
|
||||||
detectorInfiniteLoop=InfiniteLoop|true
|
detectorInfiniteLoop=InfiniteLoop|true
|
||||||
detectorInfiniteRecursiveLoop=InfiniteRecursiveLoop|true
|
detectorInfiniteRecursiveLoop=InfiniteRecursiveLoop|true
|
||||||
detectorInfiniteRecursiveLoop2=InfiniteRecursiveLoop2|false
|
detectorInfiniteRecursiveLoop2=InfiniteRecursiveLoop2|false
|
||||||
detectorInheritanceUnsafeGetResource=InheritanceUnsafeGetResource|true
|
detectorInheritanceUnsafeGetResource=InheritanceUnsafeGetResource|true
|
||||||
detectorInitializationChain=InitializationChain|true
|
detectorInitializationChain=InitializationChain|true
|
||||||
detectorInstantiateStaticClass=InstantiateStaticClass|true
|
detectorInstantiateStaticClass=InstantiateStaticClass|true
|
||||||
detectorInvalidJUnitTest=InvalidJUnitTest|true
|
detectorInvalidJUnitTest=InvalidJUnitTest|true
|
||||||
detectorIteratorIdioms=IteratorIdioms|true
|
detectorIteratorIdioms=IteratorIdioms|true
|
||||||
detectorLazyInit=LazyInit|true
|
detectorLazyInit=LazyInit|true
|
||||||
detectorLoadOfKnownNullValue=LoadOfKnownNullValue|true
|
detectorLoadOfKnownNullValue=LoadOfKnownNullValue|true
|
||||||
detectorLostLoggerDueToWeakReference=LostLoggerDueToWeakReference|true
|
detectorLostLoggerDueToWeakReference=LostLoggerDueToWeakReference|true
|
||||||
detectorMethodReturnCheck=MethodReturnCheck|true
|
detectorMethodReturnCheck=MethodReturnCheck|true
|
||||||
detectorMultithreadedInstanceAccess=MultithreadedInstanceAccess|true
|
detectorMultithreadedInstanceAccess=MultithreadedInstanceAccess|true
|
||||||
detectorMutableLock=MutableLock|true
|
detectorMutableLock=MutableLock|true
|
||||||
detectorMutableStaticFields=MutableStaticFields|true
|
detectorMutableStaticFields=MutableStaticFields|true
|
||||||
detectorNaming=Naming|true
|
detectorNaming=Naming|true
|
||||||
detectorNumberConstructor=NumberConstructor|true
|
detectorNumberConstructor=NumberConstructor|true
|
||||||
detectorOverridingEqualsNotSymmetrical=OverridingEqualsNotSymmetrical|true
|
detectorOverridingEqualsNotSymmetrical=OverridingEqualsNotSymmetrical|true
|
||||||
detectorPreferZeroLengthArrays=PreferZeroLengthArrays|true
|
detectorPreferZeroLengthArrays=PreferZeroLengthArrays|true
|
||||||
detectorPublicSemaphores=PublicSemaphores|false
|
detectorPublicSemaphores=PublicSemaphores|false
|
||||||
detectorQuestionableBooleanAssignment=QuestionableBooleanAssignment|true
|
detectorQuestionableBooleanAssignment=QuestionableBooleanAssignment|true
|
||||||
detectorReadOfInstanceFieldInMethodInvokedByConstructorInSuperclass=ReadOfInstanceFieldInMethodInvokedByConstructorInSuperclass|true
|
detectorReadOfInstanceFieldInMethodInvokedByConstructorInSuperclass=ReadOfInstanceFieldInMethodInvokedByConstructorInSuperclass|true
|
||||||
detectorReadReturnShouldBeChecked=ReadReturnShouldBeChecked|true
|
detectorReadReturnShouldBeChecked=ReadReturnShouldBeChecked|true
|
||||||
detectorRedundantInterfaces=RedundantInterfaces|true
|
detectorRedundantInterfaces=RedundantInterfaces|true
|
||||||
detectorRepeatedConditionals=RepeatedConditionals|true
|
detectorRepeatedConditionals=RepeatedConditionals|true
|
||||||
detectorRuntimeExceptionCapture=RuntimeExceptionCapture|true
|
detectorRuntimeExceptionCapture=RuntimeExceptionCapture|true
|
||||||
detectorSerializableIdiom=SerializableIdiom|true
|
detectorSerializableIdiom=SerializableIdiom|true
|
||||||
detectorStartInConstructor=StartInConstructor|true
|
detectorStartInConstructor=StartInConstructor|true
|
||||||
detectorStaticCalendarDetector=StaticCalendarDetector|true
|
detectorStaticCalendarDetector=StaticCalendarDetector|true
|
||||||
detectorStringConcatenation=StringConcatenation|true
|
detectorStringConcatenation=StringConcatenation|true
|
||||||
detectorSuperfluousInstanceOf=SuperfluousInstanceOf|true
|
detectorSuperfluousInstanceOf=SuperfluousInstanceOf|true
|
||||||
detectorSuspiciousThreadInterrupted=SuspiciousThreadInterrupted|true
|
detectorSuspiciousThreadInterrupted=SuspiciousThreadInterrupted|true
|
||||||
detectorSwitchFallthrough=SwitchFallthrough|true
|
detectorSwitchFallthrough=SwitchFallthrough|true
|
||||||
detectorSynchronizeAndNullCheckField=SynchronizeAndNullCheckField|true
|
detectorSynchronizeAndNullCheckField=SynchronizeAndNullCheckField|true
|
||||||
detectorSynchronizeOnClassLiteralNotGetClass=SynchronizeOnClassLiteralNotGetClass|true
|
detectorSynchronizeOnClassLiteralNotGetClass=SynchronizeOnClassLiteralNotGetClass|true
|
||||||
detectorSynchronizingOnContentsOfFieldToProtectField=SynchronizingOnContentsOfFieldToProtectField|true
|
detectorSynchronizingOnContentsOfFieldToProtectField=SynchronizingOnContentsOfFieldToProtectField|true
|
||||||
detectorURLProblems=URLProblems|true
|
detectorURLProblems=URLProblems|true
|
||||||
detectorUncallableMethodOfAnonymousClass=UncallableMethodOfAnonymousClass|true
|
detectorUncallableMethodOfAnonymousClass=UncallableMethodOfAnonymousClass|true
|
||||||
detectorUnnecessaryMath=UnnecessaryMath|true
|
detectorUnnecessaryMath=UnnecessaryMath|true
|
||||||
detectorUnreadFields=UnreadFields|true
|
detectorUnreadFields=UnreadFields|true
|
||||||
detectorUseObjectEquals=UseObjectEquals|false
|
detectorUseObjectEquals=UseObjectEquals|false
|
||||||
detectorUselessSubclassMethod=UselessSubclassMethod|false
|
detectorUselessSubclassMethod=UselessSubclassMethod|false
|
||||||
detectorVarArgsProblems=VarArgsProblems|true
|
detectorVarArgsProblems=VarArgsProblems|true
|
||||||
detectorVolatileUsage=VolatileUsage|true
|
detectorVolatileUsage=VolatileUsage|true
|
||||||
detectorWaitInLoop=WaitInLoop|true
|
detectorWaitInLoop=WaitInLoop|true
|
||||||
detectorWrongMapIterator=WrongMapIterator|true
|
detectorWrongMapIterator=WrongMapIterator|true
|
||||||
detectorXMLFactoryBypass=XMLFactoryBypass|true
|
detectorXMLFactoryBypass=XMLFactoryBypass|true
|
||||||
detector_threshold=2
|
detector_threshold=2
|
||||||
effort=default
|
effort=default
|
||||||
filter_settings=Medium|BAD_PRACTICE,CORRECTNESS,MT_CORRECTNESS,PERFORMANCE,STYLE|false
|
filter_settings=Medium|BAD_PRACTICE,CORRECTNESS,MT_CORRECTNESS,PERFORMANCE,STYLE|false
|
||||||
filter_settings_neg=MALICIOUS_CODE,NOISE,I18N,SECURITY,EXPERIMENTAL|
|
filter_settings_neg=MALICIOUS_CODE,NOISE,I18N,SECURITY,EXPERIMENTAL|
|
||||||
run_at_full_build=false
|
run_at_full_build=false
|
||||||
|
|||||||
22393
.gitattributes
vendored
22393
.gitattributes
vendored
File diff suppressed because it is too large
Load Diff
@@ -1,284 +1,284 @@
|
|||||||
eclipse.preferences.version=1
|
eclipse.preferences.version=1
|
||||||
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
|
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
|
||||||
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
|
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
|
||||||
org.eclipse.jdt.core.compiler.compliance=1.7
|
org.eclipse.jdt.core.compiler.compliance=1.7
|
||||||
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
|
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
|
||||||
org.eclipse.jdt.core.compiler.debug.localVariable=generate
|
org.eclipse.jdt.core.compiler.debug.localVariable=generate
|
||||||
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
|
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
|
||||||
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||||
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||||
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
|
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
|
||||||
org.eclipse.jdt.core.compiler.source=1.7
|
org.eclipse.jdt.core.compiler.source=1.7
|
||||||
org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
|
org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
|
||||||
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
|
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
|
||||||
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
|
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
|
||||||
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
|
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
|
||||||
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
|
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
|
||||||
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
|
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
|
||||||
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
|
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
|
||||||
org.eclipse.jdt.core.formatter.alignment_for_assignment=0
|
org.eclipse.jdt.core.formatter.alignment_for_assignment=0
|
||||||
org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
|
org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
|
||||||
org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
|
org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
|
||||||
org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
|
org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
|
||||||
org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
|
org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
|
||||||
org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
|
org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
|
||||||
org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
|
org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
|
||||||
org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
|
org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
|
||||||
org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
|
org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
|
||||||
org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
|
org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
|
||||||
org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
|
org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
|
||||||
org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
|
org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
|
||||||
org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
|
org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
|
||||||
org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
|
org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
|
||||||
org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
|
org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
|
||||||
org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
|
org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
|
||||||
org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
|
org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
|
||||||
org.eclipse.jdt.core.formatter.blank_lines_after_package=1
|
org.eclipse.jdt.core.formatter.blank_lines_after_package=1
|
||||||
org.eclipse.jdt.core.formatter.blank_lines_before_field=0
|
org.eclipse.jdt.core.formatter.blank_lines_before_field=0
|
||||||
org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
|
org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
|
||||||
org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
|
org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
|
||||||
org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
|
org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
|
||||||
org.eclipse.jdt.core.formatter.blank_lines_before_method=1
|
org.eclipse.jdt.core.formatter.blank_lines_before_method=1
|
||||||
org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
|
org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
|
||||||
org.eclipse.jdt.core.formatter.blank_lines_before_package=0
|
org.eclipse.jdt.core.formatter.blank_lines_before_package=0
|
||||||
org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
|
org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
|
||||||
org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
|
org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
|
||||||
org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
|
org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
|
||||||
org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
|
org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
|
||||||
org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
|
org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
|
||||||
org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
|
org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
|
||||||
org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
|
org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
|
||||||
org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
|
org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
|
||||||
org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
|
org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
|
||||||
org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
|
org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
|
||||||
org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
|
org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
|
||||||
org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
|
org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
|
||||||
org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
|
org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
|
||||||
org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
|
org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
|
||||||
org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
|
org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
|
||||||
org.eclipse.jdt.core.formatter.comment.format_block_comments=true
|
org.eclipse.jdt.core.formatter.comment.format_block_comments=true
|
||||||
org.eclipse.jdt.core.formatter.comment.format_header=false
|
org.eclipse.jdt.core.formatter.comment.format_header=false
|
||||||
org.eclipse.jdt.core.formatter.comment.format_html=true
|
org.eclipse.jdt.core.formatter.comment.format_html=true
|
||||||
org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
|
org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
|
||||||
org.eclipse.jdt.core.formatter.comment.format_line_comments=true
|
org.eclipse.jdt.core.formatter.comment.format_line_comments=true
|
||||||
org.eclipse.jdt.core.formatter.comment.format_source_code=true
|
org.eclipse.jdt.core.formatter.comment.format_source_code=true
|
||||||
org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
|
org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
|
||||||
org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
|
org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
|
||||||
org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
|
org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
|
||||||
org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
|
org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
|
||||||
org.eclipse.jdt.core.formatter.comment.line_length=80
|
org.eclipse.jdt.core.formatter.comment.line_length=80
|
||||||
org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
|
org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
|
||||||
org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
|
org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
|
||||||
org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
|
org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
|
||||||
org.eclipse.jdt.core.formatter.compact_else_if=true
|
org.eclipse.jdt.core.formatter.compact_else_if=true
|
||||||
org.eclipse.jdt.core.formatter.continuation_indentation=2
|
org.eclipse.jdt.core.formatter.continuation_indentation=2
|
||||||
org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
|
org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
|
||||||
org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
|
org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
|
||||||
org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
|
org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
|
||||||
org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
|
org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
|
||||||
org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true
|
org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true
|
||||||
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
|
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
|
||||||
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
|
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
|
||||||
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
|
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
|
||||||
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
|
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
|
||||||
org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
|
org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
|
||||||
org.eclipse.jdt.core.formatter.indent_empty_lines=false
|
org.eclipse.jdt.core.formatter.indent_empty_lines=false
|
||||||
org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
|
org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
|
||||||
org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
|
org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
|
||||||
org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
|
org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
|
||||||
org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
|
org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
|
||||||
org.eclipse.jdt.core.formatter.indentation.size=4
|
org.eclipse.jdt.core.formatter.indentation.size=4
|
||||||
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
|
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
|
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
|
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
|
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
|
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
|
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
|
org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
|
org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
|
org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
|
org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
|
org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
|
org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
|
org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
|
org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
|
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
|
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
|
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
|
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
|
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
|
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
|
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
|
org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
|
org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
|
org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
|
org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
|
org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
|
org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
|
org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
|
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
|
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
|
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
|
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
|
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
|
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
|
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
|
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
|
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
|
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
|
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
|
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
|
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
|
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
|
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
|
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
|
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
|
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
|
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
|
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
|
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
|
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
|
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
|
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
|
org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
|
org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
|
org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
|
org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
|
org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
|
org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
|
org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
|
org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
|
org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
|
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
|
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
|
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
|
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
|
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
|
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
|
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
|
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
|
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
|
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
|
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
|
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
|
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
|
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
|
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
|
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
|
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
|
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
|
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
|
org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
|
org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
|
org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
|
||||||
org.eclipse.jdt.core.formatter.join_lines_in_comments=true
|
org.eclipse.jdt.core.formatter.join_lines_in_comments=true
|
||||||
org.eclipse.jdt.core.formatter.join_wrapped_lines=true
|
org.eclipse.jdt.core.formatter.join_wrapped_lines=true
|
||||||
org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
|
org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
|
||||||
org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
|
org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
|
||||||
org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
|
org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
|
||||||
org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
|
org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
|
||||||
org.eclipse.jdt.core.formatter.lineSplit=120
|
org.eclipse.jdt.core.formatter.lineSplit=120
|
||||||
org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
|
org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
|
||||||
org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
|
org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
|
||||||
org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
|
org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
|
||||||
org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
|
org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
|
||||||
org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
|
org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
|
||||||
org.eclipse.jdt.core.formatter.tabulation.char=space
|
org.eclipse.jdt.core.formatter.tabulation.char=space
|
||||||
org.eclipse.jdt.core.formatter.tabulation.size=4
|
org.eclipse.jdt.core.formatter.tabulation.size=4
|
||||||
org.eclipse.jdt.core.formatter.use_on_off_tags=false
|
org.eclipse.jdt.core.formatter.use_on_off_tags=false
|
||||||
org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=true
|
org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=true
|
||||||
org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
|
org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
|
||||||
org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
|
org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1,9 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<classpath>
|
<classpath>
|
||||||
<classpathentry kind="src" output="target/classes" path="src/main/java"/>
|
<classpathentry kind="src" output="target/classes" path="src/main/java"/>
|
||||||
<classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
|
<classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
|
||||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
|
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
|
||||||
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER"/>
|
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER"/>
|
||||||
<classpathentry kind="con" path="org.testng.TESTNG_CONTAINER"/>
|
<classpathentry kind="con" path="org.testng.TESTNG_CONTAINER"/>
|
||||||
<classpathentry kind="output" path="target/classes"/>
|
<classpathentry kind="output" path="target/classes"/>
|
||||||
</classpath>
|
</classpath>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
eclipse.preferences.version=1
|
eclipse.preferences.version=1
|
||||||
encoding//src/main/java=ISO-8859-1
|
encoding//src/main/java=ISO-8859-1
|
||||||
encoding//src/test/java=ISO-8859-1
|
encoding//src/test/java=ISO-8859-1
|
||||||
encoding/<project>=UTF-8
|
encoding/<project>=UTF-8
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,21 +1,21 @@
|
|||||||
package forge.ai;
|
package forge.ai;
|
||||||
|
|
||||||
public enum AiPlayDecision {
|
public enum AiPlayDecision {
|
||||||
WillPlay,
|
WillPlay,
|
||||||
CantPlaySa,
|
CantPlaySa,
|
||||||
CantPlayAi,
|
CantPlayAi,
|
||||||
CantAfford,
|
CantAfford,
|
||||||
CantAffordX,
|
CantAffordX,
|
||||||
WaitForMain2,
|
WaitForMain2,
|
||||||
AnotherTime,
|
AnotherTime,
|
||||||
MissingNeededCards,
|
MissingNeededCards,
|
||||||
NeedsToPlayCriteriaNotMet,
|
NeedsToPlayCriteriaNotMet,
|
||||||
TargetingFailed,
|
TargetingFailed,
|
||||||
CostNotAcceptable,
|
CostNotAcceptable,
|
||||||
WouldDestroyLegend,
|
WouldDestroyLegend,
|
||||||
WouldDestroyOtherPlaneswalker,
|
WouldDestroyOtherPlaneswalker,
|
||||||
WouldBecomeZeroToughnessCreature,
|
WouldBecomeZeroToughnessCreature,
|
||||||
WouldDestroyWorldEnchantment,
|
WouldDestroyWorldEnchantment,
|
||||||
BadEtbEffects,
|
BadEtbEffects,
|
||||||
CurseEffects;
|
CurseEffects;
|
||||||
}
|
}
|
||||||
@@ -1,122 +1,122 @@
|
|||||||
/*
|
/*
|
||||||
* Forge: Play Magic: the Gathering.
|
* Forge: Play Magic: the Gathering.
|
||||||
* Copyright (C) 2013 Forge Team
|
* Copyright (C) 2013 Forge Team
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful,
|
* This program is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
package forge.ai;
|
package forge.ai;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AI personality profile settings identifiers, and their default values.
|
* AI personality profile settings identifiers, and their default values.
|
||||||
* When this class is instantiated, these enum values are used
|
* When this class is instantiated, these enum values are used
|
||||||
* in a map that is populated with the current AI profile settings
|
* in a map that is populated with the current AI profile settings
|
||||||
* from the text file.
|
* from the text file.
|
||||||
*/
|
*/
|
||||||
public enum AiProps { /** */
|
public enum AiProps { /** */
|
||||||
DEFAULT_MAX_PLANAR_DIE_ROLLS_PER_TURN ("1"), /** */
|
DEFAULT_MAX_PLANAR_DIE_ROLLS_PER_TURN ("1"), /** */
|
||||||
DEFAULT_MIN_TURN_TO_ROLL_PLANAR_DIE ("3"), /** */
|
DEFAULT_MIN_TURN_TO_ROLL_PLANAR_DIE ("3"), /** */
|
||||||
DEFAULT_PLANAR_DIE_ROLL_CHANCE ("50"), /** */
|
DEFAULT_PLANAR_DIE_ROLL_CHANCE ("50"), /** */
|
||||||
MULLIGAN_THRESHOLD ("5"), /** */
|
MULLIGAN_THRESHOLD ("5"), /** */
|
||||||
PLANAR_DIE_ROLL_HESITATION_CHANCE ("10"),
|
PLANAR_DIE_ROLL_HESITATION_CHANCE ("10"),
|
||||||
HOLD_LAND_DROP_FOR_MAIN2_IF_UNUSED ("0"), /** */
|
HOLD_LAND_DROP_FOR_MAIN2_IF_UNUSED ("0"), /** */
|
||||||
HOLD_LAND_DROP_ONLY_IF_HAVE_OTHER_PERMS ("true"), /** */
|
HOLD_LAND_DROP_ONLY_IF_HAVE_OTHER_PERMS ("true"), /** */
|
||||||
CHEAT_WITH_MANA_ON_SHUFFLE ("false"),
|
CHEAT_WITH_MANA_ON_SHUFFLE ("false"),
|
||||||
MOVE_EQUIPMENT_TO_BETTER_CREATURES ("from_useless_only"),
|
MOVE_EQUIPMENT_TO_BETTER_CREATURES ("from_useless_only"),
|
||||||
MOVE_EQUIPMENT_CREATURE_EVAL_THRESHOLD ("60"),
|
MOVE_EQUIPMENT_CREATURE_EVAL_THRESHOLD ("60"),
|
||||||
PRIORITIZE_MOVE_EQUIPMENT_IF_USELESS ("true"),
|
PRIORITIZE_MOVE_EQUIPMENT_IF_USELESS ("true"),
|
||||||
PREDICT_SPELLS_FOR_MAIN2 ("true"), /** */
|
PREDICT_SPELLS_FOR_MAIN2 ("true"), /** */
|
||||||
RESERVE_MANA_FOR_MAIN2_CHANCE ("0"), /** */
|
RESERVE_MANA_FOR_MAIN2_CHANCE ("0"), /** */
|
||||||
PLAY_AGGRO ("false"),
|
PLAY_AGGRO ("false"),
|
||||||
CHANCE_TO_ATTACK_INTO_TRADE ("40"), /** */
|
CHANCE_TO_ATTACK_INTO_TRADE ("40"), /** */
|
||||||
RANDOMLY_ATKTRADE_ONLY_ON_LOWER_LIFE_PRESSURE ("true"), /** */
|
RANDOMLY_ATKTRADE_ONLY_ON_LOWER_LIFE_PRESSURE ("true"), /** */
|
||||||
ATTACK_INTO_TRADE_WHEN_TAPPED_OUT ("false"), /** */
|
ATTACK_INTO_TRADE_WHEN_TAPPED_OUT ("false"), /** */
|
||||||
CHANCE_TO_ATKTRADE_WHEN_OPP_HAS_MANA ("0"), /** */
|
CHANCE_TO_ATKTRADE_WHEN_OPP_HAS_MANA ("0"), /** */
|
||||||
TRY_TO_AVOID_ATTACKING_INTO_CERTAIN_BLOCK ("true"), /** */
|
TRY_TO_AVOID_ATTACKING_INTO_CERTAIN_BLOCK ("true"), /** */
|
||||||
TRY_TO_HOLD_COMBAT_TRICKS_UNTIL_BLOCK ("false"), /** */
|
TRY_TO_HOLD_COMBAT_TRICKS_UNTIL_BLOCK ("false"), /** */
|
||||||
CHANCE_TO_HOLD_COMBAT_TRICKS_UNTIL_BLOCK ("30"), /** */
|
CHANCE_TO_HOLD_COMBAT_TRICKS_UNTIL_BLOCK ("30"), /** */
|
||||||
ENABLE_RANDOM_FAVORABLE_TRADES_ON_BLOCK ("true"), /** */
|
ENABLE_RANDOM_FAVORABLE_TRADES_ON_BLOCK ("true"), /** */
|
||||||
RANDOMLY_TRADE_EVEN_WHEN_HAVE_LESS_CREATS ("false"), /** */
|
RANDOMLY_TRADE_EVEN_WHEN_HAVE_LESS_CREATS ("false"), /** */
|
||||||
MAX_DIFF_IN_CREATURE_COUNT_TO_TRADE ("1"), /** */
|
MAX_DIFF_IN_CREATURE_COUNT_TO_TRADE ("1"), /** */
|
||||||
ALSO_TRADE_WHEN_HAVE_A_REPLACEMENT_CREAT ("true"), /** */
|
ALSO_TRADE_WHEN_HAVE_A_REPLACEMENT_CREAT ("true"), /** */
|
||||||
MAX_DIFF_IN_CREATURE_COUNT_TO_TRADE_WITH_REPL ("1"), /** */
|
MAX_DIFF_IN_CREATURE_COUNT_TO_TRADE_WITH_REPL ("1"), /** */
|
||||||
MIN_CHANCE_TO_RANDOMLY_TRADE_ON_BLOCK ("30"), /** */
|
MIN_CHANCE_TO_RANDOMLY_TRADE_ON_BLOCK ("30"), /** */
|
||||||
MAX_CHANCE_TO_RANDOMLY_TRADE_ON_BLOCK ("70"), /** */
|
MAX_CHANCE_TO_RANDOMLY_TRADE_ON_BLOCK ("70"), /** */
|
||||||
CHANCE_DECREASE_TO_TRADE_VS_EMBALM ("30"), /** */
|
CHANCE_DECREASE_TO_TRADE_VS_EMBALM ("30"), /** */
|
||||||
CHANCE_TO_TRADE_TO_SAVE_PLANESWALKER ("70"), /** */
|
CHANCE_TO_TRADE_TO_SAVE_PLANESWALKER ("70"), /** */
|
||||||
CHANCE_TO_TRADE_DOWN_TO_SAVE_PLANESWALKER ("0"), /** */
|
CHANCE_TO_TRADE_DOWN_TO_SAVE_PLANESWALKER ("0"), /** */
|
||||||
THRESHOLD_TOKEN_CHUMP_TO_SAVE_PLANESWALKER ("135"), /** */
|
THRESHOLD_TOKEN_CHUMP_TO_SAVE_PLANESWALKER ("135"), /** */
|
||||||
THRESHOLD_NONTOKEN_CHUMP_TO_SAVE_PLANESWALKER ("110"), /** */
|
THRESHOLD_NONTOKEN_CHUMP_TO_SAVE_PLANESWALKER ("110"), /** */
|
||||||
CHUMP_TO_SAVE_PLANESWALKER_ONLY_ON_LETHAL ("true"), /** */
|
CHUMP_TO_SAVE_PLANESWALKER_ONLY_ON_LETHAL ("true"), /** */
|
||||||
MIN_SPELL_CMC_TO_COUNTER ("0"), /** */
|
MIN_SPELL_CMC_TO_COUNTER ("0"), /** */
|
||||||
CHANCE_TO_COUNTER_CMC_1 ("50"), /** */
|
CHANCE_TO_COUNTER_CMC_1 ("50"), /** */
|
||||||
CHANCE_TO_COUNTER_CMC_2 ("75"), /** */
|
CHANCE_TO_COUNTER_CMC_2 ("75"), /** */
|
||||||
CHANCE_TO_COUNTER_CMC_3 ("100"), /** */
|
CHANCE_TO_COUNTER_CMC_3 ("100"), /** */
|
||||||
ALWAYS_COUNTER_OTHER_COUNTERSPELLS ("true"), /** */
|
ALWAYS_COUNTER_OTHER_COUNTERSPELLS ("true"), /** */
|
||||||
ALWAYS_COUNTER_DAMAGE_SPELLS ("true"), /** */
|
ALWAYS_COUNTER_DAMAGE_SPELLS ("true"), /** */
|
||||||
ALWAYS_COUNTER_CMC_0_MANA_MAKING_PERMS ("true"), /** */
|
ALWAYS_COUNTER_CMC_0_MANA_MAKING_PERMS ("true"), /** */
|
||||||
ALWAYS_COUNTER_REMOVAL_SPELLS ("true"), /** */
|
ALWAYS_COUNTER_REMOVAL_SPELLS ("true"), /** */
|
||||||
ALWAYS_COUNTER_PUMP_SPELLS ("true"), /** */
|
ALWAYS_COUNTER_PUMP_SPELLS ("true"), /** */
|
||||||
ALWAYS_COUNTER_AURAS ("true"), /** */
|
ALWAYS_COUNTER_AURAS ("true"), /** */
|
||||||
ALWAYS_COUNTER_SPELLS_FROM_NAMED_CARDS (""), /** */
|
ALWAYS_COUNTER_SPELLS_FROM_NAMED_CARDS (""), /** */
|
||||||
ACTIVELY_DESTROY_ARTS_AND_NONAURA_ENCHS ("true"), /** */
|
ACTIVELY_DESTROY_ARTS_AND_NONAURA_ENCHS ("true"), /** */
|
||||||
ACTIVELY_DESTROY_IMMEDIATELY_UNBLOCKABLE ("false"), /** */
|
ACTIVELY_DESTROY_IMMEDIATELY_UNBLOCKABLE ("false"), /** */
|
||||||
DESTROY_IMMEDIATELY_UNBLOCKABLE_THRESHOLD ("2"), /** */
|
DESTROY_IMMEDIATELY_UNBLOCKABLE_THRESHOLD ("2"), /** */
|
||||||
DESTROY_IMMEDIATELY_UNBLOCKABLE_ONLY_IN_DNGR ("true"), /** */
|
DESTROY_IMMEDIATELY_UNBLOCKABLE_ONLY_IN_DNGR ("true"), /** */
|
||||||
DESTROY_IMMEDIATELY_UNBLOCKABLE_LIFE_IN_DNGR ("5"), /** */
|
DESTROY_IMMEDIATELY_UNBLOCKABLE_LIFE_IN_DNGR ("5"), /** */
|
||||||
PRIORITY_REDUCTION_FOR_STORM_SPELLS ("0"), /** */
|
PRIORITY_REDUCTION_FOR_STORM_SPELLS ("0"), /** */
|
||||||
USE_BERSERK_AGGRESSIVELY ("false"), /** */
|
USE_BERSERK_AGGRESSIVELY ("false"), /** */
|
||||||
MIN_COUNT_FOR_STORM_SPELLS ("0"), /** */
|
MIN_COUNT_FOR_STORM_SPELLS ("0"), /** */
|
||||||
STRIPMINE_MIN_LANDS_IN_HAND_TO_ACTIVATE ("1"), /** */
|
STRIPMINE_MIN_LANDS_IN_HAND_TO_ACTIVATE ("1"), /** */
|
||||||
STRIPMINE_MIN_LANDS_FOR_NO_TIMING_CHECK ("3"), /** */
|
STRIPMINE_MIN_LANDS_FOR_NO_TIMING_CHECK ("3"), /** */
|
||||||
STRIPMINE_MIN_LANDS_OTB_FOR_NO_TEMPO_CHECK ("6"), /** */
|
STRIPMINE_MIN_LANDS_OTB_FOR_NO_TEMPO_CHECK ("6"), /** */
|
||||||
STRIPMINE_MAX_LANDS_TO_ATTEMPT_MANALOCKING ("3"), /** */
|
STRIPMINE_MAX_LANDS_TO_ATTEMPT_MANALOCKING ("3"), /** */
|
||||||
STRIPMINE_HIGH_PRIORITY_ON_SKIPPED_LANDDROP ("false"),
|
STRIPMINE_HIGH_PRIORITY_ON_SKIPPED_LANDDROP ("false"),
|
||||||
TOKEN_GENERATION_ABILITY_CHANCE ("80"), /** */
|
TOKEN_GENERATION_ABILITY_CHANCE ("80"), /** */
|
||||||
TOKEN_GENERATION_ALWAYS_IF_FROM_PLANESWALKER ("true"), /** */
|
TOKEN_GENERATION_ALWAYS_IF_FROM_PLANESWALKER ("true"), /** */
|
||||||
TOKEN_GENERATION_ALWAYS_IF_OPP_ATTACKS ("true"), /** */
|
TOKEN_GENERATION_ALWAYS_IF_OPP_ATTACKS ("true"), /** */
|
||||||
SCRY_NUM_LANDS_TO_STILL_NEED_MORE ("4"), /** */
|
SCRY_NUM_LANDS_TO_STILL_NEED_MORE ("4"), /** */
|
||||||
SCRY_NUM_LANDS_TO_NOT_NEED_MORE ("7"), /** */
|
SCRY_NUM_LANDS_TO_NOT_NEED_MORE ("7"), /** */
|
||||||
SCRY_NUM_CREATURES_TO_NOT_NEED_SUBPAR_ONES ("4"), /** */
|
SCRY_NUM_CREATURES_TO_NOT_NEED_SUBPAR_ONES ("4"), /** */
|
||||||
SCRY_EVALTHR_CREATCOUNT_TO_SCRY_AWAY_LOWCMC ("3"), /** */
|
SCRY_EVALTHR_CREATCOUNT_TO_SCRY_AWAY_LOWCMC ("3"), /** */
|
||||||
SCRY_EVALTHR_TO_SCRY_AWAY_LOWCMC_CREATURE ("160"), /** */
|
SCRY_EVALTHR_TO_SCRY_AWAY_LOWCMC_CREATURE ("160"), /** */
|
||||||
SCRY_EVALTHR_CMC_THRESHOLD ("3"), /** */
|
SCRY_EVALTHR_CMC_THRESHOLD ("3"), /** */
|
||||||
SCRY_IMMEDIATELY_UNCASTABLE_TO_BOTTOM ("false"), /** */
|
SCRY_IMMEDIATELY_UNCASTABLE_TO_BOTTOM ("false"), /** */
|
||||||
SCRY_IMMEDIATELY_UNCASTABLE_CMC_DIFF ("1"), /** */
|
SCRY_IMMEDIATELY_UNCASTABLE_CMC_DIFF ("1"), /** */
|
||||||
COMBAT_ASSAULT_ATTACK_EVASION_PREDICTION ("true"), /** */
|
COMBAT_ASSAULT_ATTACK_EVASION_PREDICTION ("true"), /** */
|
||||||
COMBAT_ATTRITION_ATTACK_EVASION_PREDICTION ("true"), /** */
|
COMBAT_ATTRITION_ATTACK_EVASION_PREDICTION ("true"), /** */
|
||||||
CONSERVATIVE_ENERGY_PAYMENT_ONLY_IN_COMBAT ("true"), /** */
|
CONSERVATIVE_ENERGY_PAYMENT_ONLY_IN_COMBAT ("true"), /** */
|
||||||
CONSERVATIVE_ENERGY_PAYMENT_ONLY_DEFENSIVELY ("true"), /** */
|
CONSERVATIVE_ENERGY_PAYMENT_ONLY_DEFENSIVELY ("true"), /** */
|
||||||
BOUNCE_ALL_TO_HAND_CREAT_EVAL_DIFF ("200"), /** */
|
BOUNCE_ALL_TO_HAND_CREAT_EVAL_DIFF ("200"), /** */
|
||||||
BOUNCE_ALL_ELSEWHERE_CREAT_EVAL_DIFF ("200"), /** */
|
BOUNCE_ALL_ELSEWHERE_CREAT_EVAL_DIFF ("200"), /** */
|
||||||
BOUNCE_ALL_TO_HAND_NONCREAT_EVAL_DIFF ("3"), /** */
|
BOUNCE_ALL_TO_HAND_NONCREAT_EVAL_DIFF ("3"), /** */
|
||||||
BOUNCE_ALL_ELSEWHERE_NONCREAT_EVAL_DIFF ("3"), /** */
|
BOUNCE_ALL_ELSEWHERE_NONCREAT_EVAL_DIFF ("3"), /** */
|
||||||
INTUITION_ALTERNATIVE_LOGIC ("false"), /** */
|
INTUITION_ALTERNATIVE_LOGIC ("false"), /** */
|
||||||
EXPLORE_MAX_CMC_DIFF_TO_PUT_IN_GRAVEYARD ("2"),
|
EXPLORE_MAX_CMC_DIFF_TO_PUT_IN_GRAVEYARD ("2"),
|
||||||
EXPLORE_NUM_LANDS_TO_STILL_NEED_MORE ("2"); /** */
|
EXPLORE_NUM_LANDS_TO_STILL_NEED_MORE ("2"); /** */
|
||||||
// Experimental features, must be removed after extensive testing and, ideally, defaulting
|
// Experimental features, must be removed after extensive testing and, ideally, defaulting
|
||||||
// <-- There are no experimental options here -->
|
// <-- There are no experimental options here -->
|
||||||
|
|
||||||
private final String strDefaultVal;
|
private final String strDefaultVal;
|
||||||
|
|
||||||
/** @param s0   {@link java.lang.String} */
|
/** @param s0   {@link java.lang.String} */
|
||||||
AiProps(final String s0) {
|
AiProps(final String s0) {
|
||||||
this.strDefaultVal = s0;
|
this.strDefaultVal = s0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return {@link java.lang.String} */
|
/** @return {@link java.lang.String} */
|
||||||
public String getDefault() {
|
public String getDefault() {
|
||||||
return strDefaultVal;
|
return strDefaultVal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,174 +1,174 @@
|
|||||||
package forge.ai;
|
package forge.ai;
|
||||||
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
import forge.card.CardStateName;
|
import forge.card.CardStateName;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.GameActionUtil;
|
import forge.game.GameActionUtil;
|
||||||
import forge.game.ability.ApiType;
|
import forge.game.ability.ApiType;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardCollection;
|
import forge.game.card.CardCollection;
|
||||||
import forge.game.card.CardCollectionView;
|
import forge.game.card.CardCollectionView;
|
||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
import forge.game.card.CardPredicates.Presets;
|
import forge.game.card.CardPredicates.Presets;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.SpellAbilityStackInstance;
|
import forge.game.spellability.SpellAbilityStackInstance;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
|
|
||||||
public class ComputerUtilAbility {
|
public class ComputerUtilAbility {
|
||||||
public static CardCollection getAvailableLandsToPlay(final Game game, final Player player) {
|
public static CardCollection getAvailableLandsToPlay(final Game game, final Player player) {
|
||||||
if (!game.getStack().isEmpty() || !game.getPhaseHandler().getPhase().isMain()) {
|
if (!game.getStack().isEmpty() || !game.getPhaseHandler().getPhase().isMain()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
final CardCollection hand = new CardCollection(player.getCardsIn(ZoneType.Hand));
|
final CardCollection hand = new CardCollection(player.getCardsIn(ZoneType.Hand));
|
||||||
hand.addAll(player.getCardsIn(ZoneType.Exile));
|
hand.addAll(player.getCardsIn(ZoneType.Exile));
|
||||||
CardCollection landList = CardLists.filter(hand, Presets.LANDS);
|
CardCollection landList = CardLists.filter(hand, Presets.LANDS);
|
||||||
|
|
||||||
//filter out cards that can't be played
|
//filter out cards that can't be played
|
||||||
landList = CardLists.filter(landList, new Predicate<Card>() {
|
landList = CardLists.filter(landList, new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(final Card c) {
|
public boolean apply(final Card c) {
|
||||||
if (!c.getSVar("NeedsToPlay").isEmpty()) {
|
if (!c.getSVar("NeedsToPlay").isEmpty()) {
|
||||||
final String needsToPlay = c.getSVar("NeedsToPlay");
|
final String needsToPlay = c.getSVar("NeedsToPlay");
|
||||||
CardCollection list = CardLists.getValidCards(game.getCardsIn(ZoneType.Battlefield), needsToPlay.split(","), c.getController(), c, null);
|
CardCollection list = CardLists.getValidCards(game.getCardsIn(ZoneType.Battlefield), needsToPlay.split(","), c.getController(), c, null);
|
||||||
if (list.isEmpty()) {
|
if (list.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return player.canPlayLand(c);
|
return player.canPlayLand(c);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
final CardCollection landsNotInHand = new CardCollection(player.getCardsIn(ZoneType.Graveyard));
|
final CardCollection landsNotInHand = new CardCollection(player.getCardsIn(ZoneType.Graveyard));
|
||||||
landsNotInHand.addAll(game.getCardsIn(ZoneType.Exile));
|
landsNotInHand.addAll(game.getCardsIn(ZoneType.Exile));
|
||||||
if (!player.getCardsIn(ZoneType.Library).isEmpty()) {
|
if (!player.getCardsIn(ZoneType.Library).isEmpty()) {
|
||||||
landsNotInHand.add(player.getCardsIn(ZoneType.Library).get(0));
|
landsNotInHand.add(player.getCardsIn(ZoneType.Library).get(0));
|
||||||
}
|
}
|
||||||
for (final Card crd : landsNotInHand) {
|
for (final Card crd : landsNotInHand) {
|
||||||
if (!(crd.isLand() || (crd.isFaceDown() && crd.getState(CardStateName.Original).getType().isLand()))) {
|
if (!(crd.isLand() || (crd.isFaceDown() && crd.getState(CardStateName.Original).getType().isLand()))) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!crd.mayPlay(player).isEmpty()) {
|
if (!crd.mayPlay(player).isEmpty()) {
|
||||||
landList.add(crd);
|
landList.add(crd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (landList.isEmpty()) {
|
if (landList.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return landList;
|
return landList;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CardCollection getAvailableCards(final Game game, final Player player) {
|
public static CardCollection getAvailableCards(final Game game, final Player player) {
|
||||||
CardCollection all = new CardCollection(player.getCardsIn(ZoneType.Hand));
|
CardCollection all = new CardCollection(player.getCardsIn(ZoneType.Hand));
|
||||||
|
|
||||||
all.addAll(player.getCardsIn(ZoneType.Graveyard));
|
all.addAll(player.getCardsIn(ZoneType.Graveyard));
|
||||||
all.addAll(player.getCardsIn(ZoneType.Command));
|
all.addAll(player.getCardsIn(ZoneType.Command));
|
||||||
if (!player.getCardsIn(ZoneType.Library).isEmpty()) {
|
if (!player.getCardsIn(ZoneType.Library).isEmpty()) {
|
||||||
all.add(player.getCardsIn(ZoneType.Library).get(0));
|
all.add(player.getCardsIn(ZoneType.Library).get(0));
|
||||||
}
|
}
|
||||||
for(Player p : game.getPlayers()) {
|
for(Player p : game.getPlayers()) {
|
||||||
all.addAll(p.getCardsIn(ZoneType.Exile));
|
all.addAll(p.getCardsIn(ZoneType.Exile));
|
||||||
all.addAll(p.getCardsIn(ZoneType.Battlefield));
|
all.addAll(p.getCardsIn(ZoneType.Battlefield));
|
||||||
}
|
}
|
||||||
return all;
|
return all;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<SpellAbility> getSpellAbilities(final CardCollectionView l, final Player player) {
|
public static List<SpellAbility> getSpellAbilities(final CardCollectionView l, final Player player) {
|
||||||
final List<SpellAbility> spellAbilities = Lists.newArrayList();
|
final List<SpellAbility> spellAbilities = Lists.newArrayList();
|
||||||
for (final Card c : l) {
|
for (final Card c : l) {
|
||||||
for (final SpellAbility sa : c.getSpellAbilities()) {
|
for (final SpellAbility sa : c.getSpellAbilities()) {
|
||||||
spellAbilities.add(sa);
|
spellAbilities.add(sa);
|
||||||
}
|
}
|
||||||
if (c.isFaceDown() && c.isInZone(ZoneType.Exile) && !c.mayPlay(player).isEmpty()) {
|
if (c.isFaceDown() && c.isInZone(ZoneType.Exile) && !c.mayPlay(player).isEmpty()) {
|
||||||
for (final SpellAbility sa : c.getState(CardStateName.Original).getSpellAbilities()) {
|
for (final SpellAbility sa : c.getState(CardStateName.Original).getSpellAbilities()) {
|
||||||
spellAbilities.add(sa);
|
spellAbilities.add(sa);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return spellAbilities;
|
return spellAbilities;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<SpellAbility> getOriginalAndAltCostAbilities(final List<SpellAbility> originList, final Player player) {
|
public static List<SpellAbility> getOriginalAndAltCostAbilities(final List<SpellAbility> originList, final Player player) {
|
||||||
final List<SpellAbility> newAbilities = Lists.newArrayList();
|
final List<SpellAbility> newAbilities = Lists.newArrayList();
|
||||||
for (SpellAbility sa : originList) {
|
for (SpellAbility sa : originList) {
|
||||||
sa.setActivatingPlayer(player);
|
sa.setActivatingPlayer(player);
|
||||||
//add alternative costs as additional spell abilities
|
//add alternative costs as additional spell abilities
|
||||||
newAbilities.add(sa);
|
newAbilities.add(sa);
|
||||||
newAbilities.addAll(GameActionUtil.getAlternativeCosts(sa, player));
|
newAbilities.addAll(GameActionUtil.getAlternativeCosts(sa, player));
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<SpellAbility> result = Lists.newArrayList();
|
final List<SpellAbility> result = Lists.newArrayList();
|
||||||
for (SpellAbility sa : newAbilities) {
|
for (SpellAbility sa : newAbilities) {
|
||||||
sa.setActivatingPlayer(player);
|
sa.setActivatingPlayer(player);
|
||||||
result.addAll(GameActionUtil.getOptionalCosts(sa));
|
result.addAll(GameActionUtil.getOptionalCosts(sa));
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SpellAbility getTopSpellAbilityOnStack(Game game, SpellAbility sa) {
|
public static SpellAbility getTopSpellAbilityOnStack(Game game, SpellAbility sa) {
|
||||||
Iterator<SpellAbilityStackInstance> it = game.getStack().iterator();
|
Iterator<SpellAbilityStackInstance> it = game.getStack().iterator();
|
||||||
|
|
||||||
if (!it.hasNext()) {
|
if (!it.hasNext()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
SpellAbility tgtSA = it.next().getSpellAbility(true);
|
SpellAbility tgtSA = it.next().getSpellAbility(true);
|
||||||
// Grab the topmost spellability that isn't this SA and use that for comparisons
|
// Grab the topmost spellability that isn't this SA and use that for comparisons
|
||||||
if (sa.equals(tgtSA) && game.getStack().size() > 1) {
|
if (sa.equals(tgtSA) && game.getStack().size() > 1) {
|
||||||
if (!it.hasNext()) {
|
if (!it.hasNext()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
tgtSA = it.next().getSpellAbility(true);
|
tgtSA = it.next().getSpellAbility(true);
|
||||||
}
|
}
|
||||||
return tgtSA;
|
return tgtSA;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Card getAbilitySource(SpellAbility sa) {
|
public static Card getAbilitySource(SpellAbility sa) {
|
||||||
return sa.getOriginalHost() != null ? sa.getOriginalHost() : sa.getHostCard();
|
return sa.getOriginalHost() != null ? sa.getOriginalHost() : sa.getHostCard();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getAbilitySourceName(SpellAbility sa) {
|
public static String getAbilitySourceName(SpellAbility sa) {
|
||||||
final Card c = getAbilitySource(sa);
|
final Card c = getAbilitySource(sa);
|
||||||
return c != null ? c.getName() : "";
|
return c != null ? c.getName() : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CardCollection getCardsTargetedWithApi(Player ai, CardCollection cardList, SpellAbility sa, ApiType api) {
|
public static CardCollection getCardsTargetedWithApi(Player ai, CardCollection cardList, SpellAbility sa, ApiType api) {
|
||||||
// Returns a collection of cards which have already been targeted with the given API either in the parent ability,
|
// Returns a collection of cards which have already been targeted with the given API either in the parent ability,
|
||||||
// in the sub ability, or by something on stack. If "sa" is specified, the parent and sub abilities of this SA will
|
// in the sub ability, or by something on stack. If "sa" is specified, the parent and sub abilities of this SA will
|
||||||
// be checked for targets. If "sa" is null, only the stack instances will be checked.
|
// be checked for targets. If "sa" is null, only the stack instances will be checked.
|
||||||
CardCollection targeted = new CardCollection();
|
CardCollection targeted = new CardCollection();
|
||||||
if (sa != null) {
|
if (sa != null) {
|
||||||
SpellAbility saSub = sa.getRootAbility();
|
SpellAbility saSub = sa.getRootAbility();
|
||||||
while (saSub != null) {
|
while (saSub != null) {
|
||||||
if (saSub.getApi() == api && saSub.getTargets() != null) {
|
if (saSub.getApi() == api && saSub.getTargets() != null) {
|
||||||
for (Card c : cardList) {
|
for (Card c : cardList) {
|
||||||
if (saSub.getTargets().getTargetCards().contains(c)) {
|
if (saSub.getTargets().getTargetCards().contains(c)) {
|
||||||
// Was already targeted with this API in a parent or sub SA
|
// Was already targeted with this API in a parent or sub SA
|
||||||
targeted.add(c);
|
targeted.add(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
saSub = saSub.getSubAbility();
|
saSub = saSub.getSubAbility();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (SpellAbilityStackInstance si : ai.getGame().getStack()) {
|
for (SpellAbilityStackInstance si : ai.getGame().getStack()) {
|
||||||
SpellAbility ab = si.getSpellAbility(false);
|
SpellAbility ab = si.getSpellAbility(false);
|
||||||
if (ab != null && ab.getApi() == api && si.getTargetChoices() != null) {
|
if (ab != null && ab.getApi() == api && si.getTargetChoices() != null) {
|
||||||
for (Card c : cardList) {
|
for (Card c : cardList) {
|
||||||
// TODO: somehow ensure that the detected SA won't be countered
|
// TODO: somehow ensure that the detected SA won't be countered
|
||||||
if (si.getTargetChoices().getTargetCards().contains(c)) {
|
if (si.getTargetChoices().getTargetCards().contains(c)) {
|
||||||
// Was already targeted by a spell ability instance on stack
|
// Was already targeted by a spell ability instance on stack
|
||||||
targeted.add(c);
|
targeted.add(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return targeted;
|
return targeted;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,273 +1,273 @@
|
|||||||
package forge.ai;
|
package forge.ai;
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
|
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.ability.ApiType;
|
import forge.game.ability.ApiType;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CounterType;
|
import forge.game.card.CounterType;
|
||||||
import forge.game.cost.CostPayEnergy;
|
import forge.game.cost.CostPayEnergy;
|
||||||
import forge.game.keyword.KeywordInterface;
|
import forge.game.keyword.KeywordInterface;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
|
|
||||||
public class CreatureEvaluator implements Function<Card, Integer> {
|
public class CreatureEvaluator implements Function<Card, Integer> {
|
||||||
protected int getEffectivePower(final Card c) {
|
protected int getEffectivePower(final Card c) {
|
||||||
return c.getNetCombatDamage();
|
return c.getNetCombatDamage();
|
||||||
}
|
}
|
||||||
protected int getEffectiveToughness(final Card c) {
|
protected int getEffectiveToughness(final Card c) {
|
||||||
return c.getNetToughness();
|
return c.getNetToughness();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Integer apply(Card c) {
|
public Integer apply(Card c) {
|
||||||
return evaluateCreature(c);
|
return evaluateCreature(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int evaluateCreature(final Card c) {
|
public int evaluateCreature(final Card c) {
|
||||||
return evaluateCreature(c, true, true);
|
return evaluateCreature(c, true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int evaluateCreature(final Card c, final boolean considerPT, final boolean considerCMC) {
|
public int evaluateCreature(final Card c, final boolean considerPT, final boolean considerCMC) {
|
||||||
int value = 80;
|
int value = 80;
|
||||||
if (!c.isToken()) {
|
if (!c.isToken()) {
|
||||||
value += addValue(20, "non-token"); // tokens should be worth less than actual cards
|
value += addValue(20, "non-token"); // tokens should be worth less than actual cards
|
||||||
}
|
}
|
||||||
int power = getEffectivePower(c);
|
int power = getEffectivePower(c);
|
||||||
final int toughness = getEffectiveToughness(c);
|
final int toughness = getEffectiveToughness(c);
|
||||||
for (KeywordInterface kw : c.getKeywords()) {
|
for (KeywordInterface kw : c.getKeywords()) {
|
||||||
String keyword = kw.getOriginal();
|
String keyword = kw.getOriginal();
|
||||||
if (keyword.equals("Prevent all combat damage that would be dealt by CARDNAME.")
|
if (keyword.equals("Prevent all combat damage that would be dealt by CARDNAME.")
|
||||||
|| keyword.equals("Prevent all damage that would be dealt by CARDNAME.")
|
|| keyword.equals("Prevent all damage that would be dealt by CARDNAME.")
|
||||||
|| keyword.equals("Prevent all combat damage that would be dealt to and dealt by CARDNAME.")
|
|| keyword.equals("Prevent all combat damage that would be dealt to and dealt by CARDNAME.")
|
||||||
|| keyword.equals("Prevent all damage that would be dealt to and dealt by CARDNAME.")) {
|
|| keyword.equals("Prevent all damage that would be dealt to and dealt by CARDNAME.")) {
|
||||||
power = 0;
|
power = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (considerPT) {
|
if (considerPT) {
|
||||||
value += addValue(power * 15, "power");
|
value += addValue(power * 15, "power");
|
||||||
value += addValue(toughness * 10, "toughness: " + toughness);
|
value += addValue(toughness * 10, "toughness: " + toughness);
|
||||||
}
|
}
|
||||||
if (considerCMC) {
|
if (considerCMC) {
|
||||||
value += addValue(c.getCMC() * 5, "cmc");
|
value += addValue(c.getCMC() * 5, "cmc");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Evasion keywords
|
// Evasion keywords
|
||||||
if (c.hasKeyword("Flying")) {
|
if (c.hasKeyword("Flying")) {
|
||||||
value += addValue(power * 10, "flying");
|
value += addValue(power * 10, "flying");
|
||||||
}
|
}
|
||||||
if (c.hasKeyword("Horsemanship")) {
|
if (c.hasKeyword("Horsemanship")) {
|
||||||
value += addValue(power * 10, "horses");
|
value += addValue(power * 10, "horses");
|
||||||
}
|
}
|
||||||
if (c.hasKeyword("Unblockable")) {
|
if (c.hasKeyword("Unblockable")) {
|
||||||
value += addValue(power * 10, "unblockable");
|
value += addValue(power * 10, "unblockable");
|
||||||
} else {
|
} else {
|
||||||
if (c.hasKeyword("You may have CARDNAME assign its combat damage as though it weren't blocked.")) {
|
if (c.hasKeyword("You may have CARDNAME assign its combat damage as though it weren't blocked.")) {
|
||||||
value += addValue(power * 6, "thorns");
|
value += addValue(power * 6, "thorns");
|
||||||
}
|
}
|
||||||
if (c.hasKeyword("Fear")) {
|
if (c.hasKeyword("Fear")) {
|
||||||
value += addValue(power * 6, "fear");
|
value += addValue(power * 6, "fear");
|
||||||
}
|
}
|
||||||
if (c.hasKeyword("Intimidate")) {
|
if (c.hasKeyword("Intimidate")) {
|
||||||
value += addValue(power * 6, "intimidate");
|
value += addValue(power * 6, "intimidate");
|
||||||
}
|
}
|
||||||
if (c.hasStartOfKeyword("Menace")) {
|
if (c.hasStartOfKeyword("Menace")) {
|
||||||
value += addValue(power * 4, "menace");
|
value += addValue(power * 4, "menace");
|
||||||
}
|
}
|
||||||
if (c.hasStartOfKeyword("CantBeBlockedBy")) {
|
if (c.hasStartOfKeyword("CantBeBlockedBy")) {
|
||||||
value += addValue(power * 3, "block-restrict");
|
value += addValue(power * 3, "block-restrict");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Other good keywords
|
// Other good keywords
|
||||||
if (power > 0) {
|
if (power > 0) {
|
||||||
if (c.hasKeyword("Double Strike")) {
|
if (c.hasKeyword("Double Strike")) {
|
||||||
value += addValue(10 + (power * 15), "ds");
|
value += addValue(10 + (power * 15), "ds");
|
||||||
} else if (c.hasKeyword("First Strike")) {
|
} else if (c.hasKeyword("First Strike")) {
|
||||||
value += addValue(10 + (power * 5), "fs");
|
value += addValue(10 + (power * 5), "fs");
|
||||||
}
|
}
|
||||||
if (c.hasKeyword("Deathtouch")) {
|
if (c.hasKeyword("Deathtouch")) {
|
||||||
value += addValue(25, "dt");
|
value += addValue(25, "dt");
|
||||||
}
|
}
|
||||||
if (c.hasKeyword("Lifelink")) {
|
if (c.hasKeyword("Lifelink")) {
|
||||||
value += addValue(power * 10, "lifelink");
|
value += addValue(power * 10, "lifelink");
|
||||||
}
|
}
|
||||||
if (power > 1 && c.hasKeyword("Trample")) {
|
if (power > 1 && c.hasKeyword("Trample")) {
|
||||||
value += addValue((power - 1) * 5, "trample");
|
value += addValue((power - 1) * 5, "trample");
|
||||||
}
|
}
|
||||||
if (c.hasKeyword("Vigilance")) {
|
if (c.hasKeyword("Vigilance")) {
|
||||||
value += addValue((power * 5) + (toughness * 5), "vigilance");
|
value += addValue((power * 5) + (toughness * 5), "vigilance");
|
||||||
}
|
}
|
||||||
if (c.hasKeyword("Wither")) {
|
if (c.hasKeyword("Wither")) {
|
||||||
value += addValue(power * 10, "Wither");
|
value += addValue(power * 10, "Wither");
|
||||||
}
|
}
|
||||||
if (c.hasKeyword("Infect")) {
|
if (c.hasKeyword("Infect")) {
|
||||||
value += addValue(power * 15, "infect");
|
value += addValue(power * 15, "infect");
|
||||||
}
|
}
|
||||||
value += addValue(c.getKeywordMagnitude("Rampage"), "rampage");
|
value += addValue(c.getKeywordMagnitude("Rampage"), "rampage");
|
||||||
value += addValue(c.getKeywordMagnitude("Afflict") * 5, "afflict");
|
value += addValue(c.getKeywordMagnitude("Afflict") * 5, "afflict");
|
||||||
}
|
}
|
||||||
|
|
||||||
value += addValue(c.getKeywordMagnitude("Bushido") * 16, "bushido");
|
value += addValue(c.getKeywordMagnitude("Bushido") * 16, "bushido");
|
||||||
value += addValue(c.getAmountOfKeyword("Flanking") * 15, "flanking");
|
value += addValue(c.getAmountOfKeyword("Flanking") * 15, "flanking");
|
||||||
value += addValue(c.getAmountOfKeyword("Exalted") * 15, "exalted");
|
value += addValue(c.getAmountOfKeyword("Exalted") * 15, "exalted");
|
||||||
value += addValue(c.getKeywordMagnitude("Annihilator") * 50, "eldrazi");
|
value += addValue(c.getKeywordMagnitude("Annihilator") * 50, "eldrazi");
|
||||||
value += addValue(c.getKeywordMagnitude("Absorb") * 11, "absorb");
|
value += addValue(c.getKeywordMagnitude("Absorb") * 11, "absorb");
|
||||||
|
|
||||||
// Keywords that may produce temporary or permanent buffs over time
|
// Keywords that may produce temporary or permanent buffs over time
|
||||||
if (c.hasKeyword("Prowess")) {
|
if (c.hasKeyword("Prowess")) {
|
||||||
value += addValue(5, "prowess");
|
value += addValue(5, "prowess");
|
||||||
}
|
}
|
||||||
if (c.hasKeyword("Outlast")) {
|
if (c.hasKeyword("Outlast")) {
|
||||||
value += addValue(10, "outlast");
|
value += addValue(10, "outlast");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Defensive Keywords
|
// Defensive Keywords
|
||||||
if (c.hasKeyword("Reach") && !c.hasKeyword("Flying")) {
|
if (c.hasKeyword("Reach") && !c.hasKeyword("Flying")) {
|
||||||
value += addValue(5, "reach");
|
value += addValue(5, "reach");
|
||||||
}
|
}
|
||||||
if (c.hasKeyword("CARDNAME can block creatures with shadow as though they didn't have shadow.")) {
|
if (c.hasKeyword("CARDNAME can block creatures with shadow as though they didn't have shadow.")) {
|
||||||
value += addValue(3, "shadow-block");
|
value += addValue(3, "shadow-block");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Protection
|
// Protection
|
||||||
if (c.hasKeyword("Indestructible")) {
|
if (c.hasKeyword("Indestructible")) {
|
||||||
value += addValue(70, "darksteel");
|
value += addValue(70, "darksteel");
|
||||||
}
|
}
|
||||||
if (c.hasKeyword("Prevent all damage that would be dealt to CARDNAME.")) {
|
if (c.hasKeyword("Prevent all damage that would be dealt to CARDNAME.")) {
|
||||||
value += addValue(60, "cho-manno");
|
value += addValue(60, "cho-manno");
|
||||||
} else if (c.hasKeyword("Prevent all combat damage that would be dealt to CARDNAME.")) {
|
} else if (c.hasKeyword("Prevent all combat damage that would be dealt to CARDNAME.")) {
|
||||||
value += addValue(50, "fogbank");
|
value += addValue(50, "fogbank");
|
||||||
}
|
}
|
||||||
if (c.hasKeyword("Hexproof")) {
|
if (c.hasKeyword("Hexproof")) {
|
||||||
value += addValue(35, "hexproof");
|
value += addValue(35, "hexproof");
|
||||||
} else if (c.hasKeyword("Shroud")) {
|
} else if (c.hasKeyword("Shroud")) {
|
||||||
value += addValue(30, "shroud");
|
value += addValue(30, "shroud");
|
||||||
}
|
}
|
||||||
if (c.hasStartOfKeyword("Protection")) {
|
if (c.hasStartOfKeyword("Protection")) {
|
||||||
value += addValue(20, "protection");
|
value += addValue(20, "protection");
|
||||||
}
|
}
|
||||||
if (c.hasStartOfKeyword("PreventAllDamageBy")) {
|
if (c.hasStartOfKeyword("PreventAllDamageBy")) {
|
||||||
value += addValue(10, "prevent-dmg");
|
value += addValue(10, "prevent-dmg");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bad keywords
|
// Bad keywords
|
||||||
if (c.hasKeyword("Defender") || c.hasKeyword("CARDNAME can't attack.")) {
|
if (c.hasKeyword("Defender") || c.hasKeyword("CARDNAME can't attack.")) {
|
||||||
value -= subValue((power * 9) + 40, "defender");
|
value -= subValue((power * 9) + 40, "defender");
|
||||||
} else if (c.getSVar("SacrificeEndCombat").equals("True")) {
|
} else if (c.getSVar("SacrificeEndCombat").equals("True")) {
|
||||||
value -= subValue(40, "sac-end");
|
value -= subValue(40, "sac-end");
|
||||||
}
|
}
|
||||||
if (c.hasKeyword("CARDNAME can't block.")) {
|
if (c.hasKeyword("CARDNAME can't block.")) {
|
||||||
value -= subValue(10, "cant-block");
|
value -= subValue(10, "cant-block");
|
||||||
} else if (c.hasKeyword("CARDNAME attacks each turn if able.")
|
} else if (c.hasKeyword("CARDNAME attacks each turn if able.")
|
||||||
|| c.hasKeyword("CARDNAME attacks each combat if able.")) {
|
|| c.hasKeyword("CARDNAME attacks each combat if able.")) {
|
||||||
value -= subValue(10, "must-attack");
|
value -= subValue(10, "must-attack");
|
||||||
} else if (c.hasStartOfKeyword("CARDNAME attacks specific player each combat if able")) {
|
} else if (c.hasStartOfKeyword("CARDNAME attacks specific player each combat if able")) {
|
||||||
value -= subValue(10, "must-attack-player");
|
value -= subValue(10, "must-attack-player");
|
||||||
} else if (c.hasKeyword("CARDNAME can block only creatures with flying.")) {
|
} else if (c.hasKeyword("CARDNAME can block only creatures with flying.")) {
|
||||||
value -= subValue(toughness * 5, "reverse-reach");
|
value -= subValue(toughness * 5, "reverse-reach");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c.hasSVar("DestroyWhenDamaged")) {
|
if (c.hasSVar("DestroyWhenDamaged")) {
|
||||||
value -= subValue((toughness - 1) * 9, "dies-to-dmg");
|
value -= subValue((toughness - 1) * 9, "dies-to-dmg");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c.hasKeyword("CARDNAME can't attack or block.")) {
|
if (c.hasKeyword("CARDNAME can't attack or block.")) {
|
||||||
value = addValue(50 + (c.getCMC() * 5), "useless"); // reset everything - useless
|
value = addValue(50 + (c.getCMC() * 5), "useless"); // reset everything - useless
|
||||||
}
|
}
|
||||||
if (c.hasKeyword("CARDNAME doesn't untap during your untap step.")) {
|
if (c.hasKeyword("CARDNAME doesn't untap during your untap step.")) {
|
||||||
if (c.isTapped()) {
|
if (c.isTapped()) {
|
||||||
value = addValue(50 + (c.getCMC() * 5), "tapped-useless"); // reset everything - useless
|
value = addValue(50 + (c.getCMC() * 5), "tapped-useless"); // reset everything - useless
|
||||||
} else {
|
} else {
|
||||||
value -= subValue(50, "doesnt-untap");
|
value -= subValue(50, "doesnt-untap");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (c.hasSVar("EndOfTurnLeavePlay")) {
|
if (c.hasSVar("EndOfTurnLeavePlay")) {
|
||||||
value -= subValue(50, "eot-leaves");
|
value -= subValue(50, "eot-leaves");
|
||||||
} else if (c.hasStartOfKeyword("Cumulative upkeep")) {
|
} else if (c.hasStartOfKeyword("Cumulative upkeep")) {
|
||||||
value -= subValue(30, "cupkeep");
|
value -= subValue(30, "cupkeep");
|
||||||
} else if (c.hasStartOfKeyword("UpkeepCost")) {
|
} else if (c.hasStartOfKeyword("UpkeepCost")) {
|
||||||
value -= subValue(20, "sac-unless");
|
value -= subValue(20, "sac-unless");
|
||||||
} else if (c.hasStartOfKeyword("Echo") && c.cameUnderControlSinceLastUpkeep()) {
|
} else if (c.hasStartOfKeyword("Echo") && c.cameUnderControlSinceLastUpkeep()) {
|
||||||
value -= subValue(10, "echo-unpaid");
|
value -= subValue(10, "echo-unpaid");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c.hasStartOfKeyword("At the beginning of your upkeep, CARDNAME deals")) {
|
if (c.hasStartOfKeyword("At the beginning of your upkeep, CARDNAME deals")) {
|
||||||
value -= subValue(20, "upkeep-dmg");
|
value -= subValue(20, "upkeep-dmg");
|
||||||
}
|
}
|
||||||
if (c.hasStartOfKeyword("Fading")) {
|
if (c.hasStartOfKeyword("Fading")) {
|
||||||
value -= subValue(20, "fading");
|
value -= subValue(20, "fading");
|
||||||
}
|
}
|
||||||
if (c.hasStartOfKeyword("Vanishing")) {
|
if (c.hasStartOfKeyword("Vanishing")) {
|
||||||
value -= subValue(20, "vanishing");
|
value -= subValue(20, "vanishing");
|
||||||
}
|
}
|
||||||
if (c.getSVar("Targeting").equals("Dies")) {
|
if (c.getSVar("Targeting").equals("Dies")) {
|
||||||
value -= subValue(25, "dies");
|
value -= subValue(25, "dies");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (final SpellAbility sa : c.getSpellAbilities()) {
|
for (final SpellAbility sa : c.getSpellAbilities()) {
|
||||||
if (sa.isAbility()) {
|
if (sa.isAbility()) {
|
||||||
value += addValue(evaluateSpellAbility(sa), "sa: " + sa);
|
value += addValue(evaluateSpellAbility(sa), "sa: " + sa);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!c.getManaAbilities().isEmpty()) {
|
if (!c.getManaAbilities().isEmpty()) {
|
||||||
value += addValue(10, "manadork");
|
value += addValue(10, "manadork");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c.isUntapped()) {
|
if (c.isUntapped()) {
|
||||||
value += addValue(1, "untapped");
|
value += addValue(1, "untapped");
|
||||||
}
|
}
|
||||||
|
|
||||||
// paired creatures are more valuable because they grant a bonus to the other creature
|
// paired creatures are more valuable because they grant a bonus to the other creature
|
||||||
if (c.isPaired()) {
|
if (c.isPaired()) {
|
||||||
value += addValue(14, "paired");
|
value += addValue(14, "paired");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!c.getEncodedCards().isEmpty()) {
|
if (!c.getEncodedCards().isEmpty()) {
|
||||||
value += addValue(24, "encoded");
|
value += addValue(24, "encoded");
|
||||||
}
|
}
|
||||||
|
|
||||||
// card-specific evaluation modifier
|
// card-specific evaluation modifier
|
||||||
if (c.hasSVar("AIEvaluationModifier")) {
|
if (c.hasSVar("AIEvaluationModifier")) {
|
||||||
int mod = AbilityUtils.calculateAmount(c, c.getSVar("AIEvaluationModifier"), null);
|
int mod = AbilityUtils.calculateAmount(c, c.getSVar("AIEvaluationModifier"), null);
|
||||||
value += mod;
|
value += mod;
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int evaluateSpellAbility(SpellAbility sa) {
|
private int evaluateSpellAbility(SpellAbility sa) {
|
||||||
// Pump abilities
|
// Pump abilities
|
||||||
if (sa.getApi() == ApiType.Pump) {
|
if (sa.getApi() == ApiType.Pump) {
|
||||||
// Pump abilities that grant +X/+X to the card
|
// Pump abilities that grant +X/+X to the card
|
||||||
if ("+X".equals(sa.getParam("NumAtt"))
|
if ("+X".equals(sa.getParam("NumAtt"))
|
||||||
&& "+X".equals(sa.getParam("NumDef"))
|
&& "+X".equals(sa.getParam("NumDef"))
|
||||||
&& !sa.usesTargeting()
|
&& !sa.usesTargeting()
|
||||||
&& (!sa.hasParam("Defined") || "Self".equals(sa.getParam("Defined")))) {
|
&& (!sa.hasParam("Defined") || "Self".equals(sa.getParam("Defined")))) {
|
||||||
if (sa.getPayCosts() != null && sa.getPayCosts().hasOnlySpecificCostType(CostPayEnergy.class)) {
|
if (sa.getPayCosts() != null && sa.getPayCosts().hasOnlySpecificCostType(CostPayEnergy.class)) {
|
||||||
// Electrostatic Pummeler, can be expanded for similar cards
|
// Electrostatic Pummeler, can be expanded for similar cards
|
||||||
int initPower = getEffectivePower(sa.getHostCard());
|
int initPower = getEffectivePower(sa.getHostCard());
|
||||||
int pumpedPower = initPower;
|
int pumpedPower = initPower;
|
||||||
int energy = sa.getHostCard().getController().getCounters(CounterType.ENERGY);
|
int energy = sa.getHostCard().getController().getCounters(CounterType.ENERGY);
|
||||||
if (energy > 0) {
|
if (energy > 0) {
|
||||||
int numActivations = energy / 3;
|
int numActivations = energy / 3;
|
||||||
for (int i = 0; i < numActivations; i++) {
|
for (int i = 0; i < numActivations; i++) {
|
||||||
pumpedPower *= 2;
|
pumpedPower *= 2;
|
||||||
}
|
}
|
||||||
return (pumpedPower - initPower) * 15;
|
return (pumpedPower - initPower) * 15;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// default value
|
// default value
|
||||||
return 10;
|
return 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected int addValue(int value, String text) {
|
protected int addValue(int value, String text) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
protected int subValue(int value, String text) {
|
protected int subValue(int value, String text) {
|
||||||
return -addValue(-value, text);
|
return -addValue(-value, text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,71 +1,71 @@
|
|||||||
package forge.ai;
|
package forge.ai;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import forge.LobbyPlayer;
|
import forge.LobbyPlayer;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.player.IGameEntitiesFactory;
|
import forge.game.player.IGameEntitiesFactory;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.player.PlayerController;
|
import forge.game.player.PlayerController;
|
||||||
|
|
||||||
public class LobbyPlayerAi extends LobbyPlayer implements IGameEntitiesFactory {
|
public class LobbyPlayerAi extends LobbyPlayer implements IGameEntitiesFactory {
|
||||||
|
|
||||||
private String aiProfile = "";
|
private String aiProfile = "";
|
||||||
private boolean rotateProfileEachGame;
|
private boolean rotateProfileEachGame;
|
||||||
private boolean allowCheatShuffle;
|
private boolean allowCheatShuffle;
|
||||||
private boolean useSimulation;
|
private boolean useSimulation;
|
||||||
|
|
||||||
public LobbyPlayerAi(String name, Set<AIOption> options) {
|
public LobbyPlayerAi(String name, Set<AIOption> options) {
|
||||||
super(name);
|
super(name);
|
||||||
if (options != null && options.contains(AIOption.USE_SIMULATION)) {
|
if (options != null && options.contains(AIOption.USE_SIMULATION)) {
|
||||||
this.useSimulation = true;
|
this.useSimulation = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isAllowCheatShuffle() {
|
public boolean isAllowCheatShuffle() {
|
||||||
return allowCheatShuffle;
|
return allowCheatShuffle;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAllowCheatShuffle(boolean allowCheatShuffle) {
|
public void setAllowCheatShuffle(boolean allowCheatShuffle) {
|
||||||
this.allowCheatShuffle = allowCheatShuffle;
|
this.allowCheatShuffle = allowCheatShuffle;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAiProfile(String profileName) {
|
public void setAiProfile(String profileName) {
|
||||||
aiProfile = profileName;
|
aiProfile = profileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getAiProfile() {
|
public String getAiProfile() {
|
||||||
return aiProfile;
|
return aiProfile;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRotateProfileEachGame(boolean rotateProfileEachGame) {
|
public void setRotateProfileEachGame(boolean rotateProfileEachGame) {
|
||||||
this.rotateProfileEachGame = rotateProfileEachGame;
|
this.rotateProfileEachGame = rotateProfileEachGame;
|
||||||
}
|
}
|
||||||
|
|
||||||
private PlayerControllerAi createControllerFor(Player ai) {
|
private PlayerControllerAi createControllerFor(Player ai) {
|
||||||
PlayerControllerAi result = new PlayerControllerAi(ai.getGame(), ai, this);
|
PlayerControllerAi result = new PlayerControllerAi(ai.getGame(), ai, this);
|
||||||
result.setUseSimulation(useSimulation);
|
result.setUseSimulation(useSimulation);
|
||||||
result.allowCheatShuffle(allowCheatShuffle);
|
result.allowCheatShuffle(allowCheatShuffle);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PlayerController createMindSlaveController(Player master, Player slave) {
|
public PlayerController createMindSlaveController(Player master, Player slave) {
|
||||||
return createControllerFor(slave);
|
return createControllerFor(slave);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Player createIngamePlayer(Game game, final int id) {
|
public Player createIngamePlayer(Game game, final int id) {
|
||||||
Player ai = new Player(getName(), game, id);
|
Player ai = new Player(getName(), game, id);
|
||||||
ai.setFirstController(createControllerFor(ai));
|
ai.setFirstController(createControllerFor(ai));
|
||||||
|
|
||||||
if (rotateProfileEachGame) {
|
if (rotateProfileEachGame) {
|
||||||
setAiProfile(AiProfileUtil.getRandomProfile());
|
setAiProfile(AiProfileUtil.getRandomProfile());
|
||||||
/*System.out.println(String.format("AI profile %s was chosen for the lobby player %s.", getAiProfile(), getName()));*/
|
/*System.out.println(String.format("AI profile %s was chosen for the lobby player %s.", getAiProfile(), getName()));*/
|
||||||
}
|
}
|
||||||
return ai;
|
return ai;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void hear(LobbyPlayer player, String message) { /* Local AI is deaf. */ }
|
public void hear(LobbyPlayer player, String message) { /* Local AI is deaf. */ }
|
||||||
}
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,365 +1,365 @@
|
|||||||
package forge.ai;
|
package forge.ai;
|
||||||
|
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import forge.card.ICardFace;
|
import forge.card.ICardFace;
|
||||||
import forge.card.mana.ManaCost;
|
import forge.card.mana.ManaCost;
|
||||||
import forge.card.mana.ManaCostParser;
|
import forge.card.mana.ManaCostParser;
|
||||||
import forge.game.GameEntity;
|
import forge.game.GameEntity;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CounterType;
|
import forge.game.card.CounterType;
|
||||||
import forge.game.cost.Cost;
|
import forge.game.cost.Cost;
|
||||||
import forge.game.mana.ManaCostBeingPaid;
|
import forge.game.mana.ManaCostBeingPaid;
|
||||||
import forge.game.phase.PhaseHandler;
|
import forge.game.phase.PhaseHandler;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.player.PlayerActionConfirmMode;
|
import forge.game.player.PlayerActionConfirmMode;
|
||||||
import forge.game.player.PlayerController.BinaryChoiceType;
|
import forge.game.player.PlayerController.BinaryChoiceType;
|
||||||
import forge.game.spellability.AbilitySub;
|
import forge.game.spellability.AbilitySub;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.SpellAbilityCondition;
|
import forge.game.spellability.SpellAbilityCondition;
|
||||||
import forge.util.MyRandom;
|
import forge.util.MyRandom;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for API-specific AI logic
|
* Base class for API-specific AI logic
|
||||||
* <p>
|
* <p>
|
||||||
* The three main methods are canPlayAI(), chkAIDrawback and doTriggerAINoCost.
|
* The three main methods are canPlayAI(), chkAIDrawback and doTriggerAINoCost.
|
||||||
*/
|
*/
|
||||||
public abstract class SpellAbilityAi {
|
public abstract class SpellAbilityAi {
|
||||||
|
|
||||||
public final boolean canPlayAIWithSubs(final Player aiPlayer, final SpellAbility sa) {
|
public final boolean canPlayAIWithSubs(final Player aiPlayer, final SpellAbility sa) {
|
||||||
if (!canPlayAI(aiPlayer, sa)) {
|
if (!canPlayAI(aiPlayer, sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
final AbilitySub subAb = sa.getSubAbility();
|
final AbilitySub subAb = sa.getSubAbility();
|
||||||
return subAb == null || chkDrawbackWithSubs(aiPlayer, subAb);
|
return subAb == null || chkDrawbackWithSubs(aiPlayer, subAb);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the AI decision to play a "main" SpellAbility
|
* Handles the AI decision to play a "main" SpellAbility
|
||||||
*/
|
*/
|
||||||
protected boolean canPlayAI(final Player ai, final SpellAbility sa) {
|
protected boolean canPlayAI(final Player ai, final SpellAbility sa) {
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
|
|
||||||
if (sa.getRestrictions() != null && !sa.getRestrictions().canPlay(source, sa)) {
|
if (sa.getRestrictions() != null && !sa.getRestrictions().canPlay(source, sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return canPlayWithoutRestrict(ai, sa);
|
return canPlayWithoutRestrict(ai, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean canPlayWithoutRestrict(final Player ai, final SpellAbility sa) {
|
protected boolean canPlayWithoutRestrict(final Player ai, final SpellAbility sa) {
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
final Cost cost = sa.getPayCosts();
|
final Cost cost = sa.getPayCosts();
|
||||||
|
|
||||||
if (!checkConditions(ai, sa, sa.getConditions())) {
|
if (!checkConditions(ai, sa, sa.getConditions())) {
|
||||||
SpellAbility sub = sa.getSubAbility();
|
SpellAbility sub = sa.getSubAbility();
|
||||||
if (sub != null && !checkConditions(ai, sub, sub.getConditions())) {
|
if (sub != null && !checkConditions(ai, sub, sub.getConditions())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.hasParam("AILogic")) {
|
if (sa.hasParam("AILogic")) {
|
||||||
final String logic = sa.getParam("AILogic");
|
final String logic = sa.getParam("AILogic");
|
||||||
if (!checkAiLogic(ai, sa, logic)) {
|
if (!checkAiLogic(ai, sa, logic)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!checkPhaseRestrictions(ai, sa, ai.getGame().getPhaseHandler(), logic)) {
|
if (!checkPhaseRestrictions(ai, sa, ai.getGame().getPhaseHandler(), logic)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!checkPhaseRestrictions(ai, sa, ai.getGame().getPhaseHandler())) {
|
if (!checkPhaseRestrictions(ai, sa, ai.getGame().getPhaseHandler())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (cost != null && !willPayCosts(ai, sa, cost, source)) {
|
if (cost != null && !willPayCosts(ai, sa, cost, source)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return checkApiLogic(ai, sa);
|
return checkApiLogic(ai, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean checkConditions(final Player ai, final SpellAbility sa, SpellAbilityCondition con) {
|
protected boolean checkConditions(final Player ai, final SpellAbility sa, SpellAbilityCondition con) {
|
||||||
// copy it to disable some checks that the AI need to check extra
|
// copy it to disable some checks that the AI need to check extra
|
||||||
con = (SpellAbilityCondition) con.copy();
|
con = (SpellAbilityCondition) con.copy();
|
||||||
|
|
||||||
// if manaspent, check if AI can pay the colored mana as cost
|
// if manaspent, check if AI can pay the colored mana as cost
|
||||||
if (!con.getManaSpent().isEmpty()) {
|
if (!con.getManaSpent().isEmpty()) {
|
||||||
// need to use ManaCostBeingPaid check, can't use Cost#canPay
|
// need to use ManaCostBeingPaid check, can't use Cost#canPay
|
||||||
ManaCostBeingPaid paid = new ManaCostBeingPaid(new ManaCost(new ManaCostParser(con.getManaSpent())));
|
ManaCostBeingPaid paid = new ManaCostBeingPaid(new ManaCost(new ManaCostParser(con.getManaSpent())));
|
||||||
if (ComputerUtilMana.canPayManaCost(paid, sa, ai)) {
|
if (ComputerUtilMana.canPayManaCost(paid, sa, ai)) {
|
||||||
con.setManaSpent("");
|
con.setManaSpent("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return con.areMet(sa);
|
return con.areMet(sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the AI will play a SpellAbility with the specified AiLogic
|
* Checks if the AI will play a SpellAbility with the specified AiLogic
|
||||||
*/
|
*/
|
||||||
protected boolean checkAiLogic(final Player ai, final SpellAbility sa, final String aiLogic) {
|
protected boolean checkAiLogic(final Player ai, final SpellAbility sa, final String aiLogic) {
|
||||||
if (aiLogic.equals("CheckCondition")) {
|
if (aiLogic.equals("CheckCondition")) {
|
||||||
SpellAbility saCopy = sa.copy();
|
SpellAbility saCopy = sa.copy();
|
||||||
saCopy.setActivatingPlayer(ai);
|
saCopy.setActivatingPlayer(ai);
|
||||||
return saCopy.getConditions().areMet(saCopy);
|
return saCopy.getConditions().areMet(saCopy);
|
||||||
}
|
}
|
||||||
|
|
||||||
return !("Never".equals(aiLogic));
|
return !("Never".equals(aiLogic));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the AI is willing to pay for additional costs
|
* Checks if the AI is willing to pay for additional costs
|
||||||
* <p>
|
* <p>
|
||||||
* Evaluated costs are: life, discard, sacrifice and counter-removal
|
* Evaluated costs are: life, discard, sacrifice and counter-removal
|
||||||
*/
|
*/
|
||||||
protected boolean willPayCosts(final Player ai, final SpellAbility sa, final Cost cost, final Card source) {
|
protected boolean willPayCosts(final Player ai, final SpellAbility sa, final Cost cost, final Card source) {
|
||||||
if (!ComputerUtilCost.checkLifeCost(ai, cost, source, 4, sa)) {
|
if (!ComputerUtilCost.checkLifeCost(ai, cost, source, 4, sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!ComputerUtilCost.checkDiscardCost(ai, cost, source)) {
|
if (!ComputerUtilCost.checkDiscardCost(ai, cost, source)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!ComputerUtilCost.checkSacrificeCost(ai, cost, source, sa)) {
|
if (!ComputerUtilCost.checkSacrificeCost(ai, cost, source, sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!ComputerUtilCost.checkRemoveCounterCost(cost, source, sa)) {
|
if (!ComputerUtilCost.checkRemoveCounterCost(cost, source, sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the AI will play a SpellAbility based on its phase restrictions
|
* Checks if the AI will play a SpellAbility based on its phase restrictions
|
||||||
*/
|
*/
|
||||||
protected boolean checkPhaseRestrictions(final Player ai, final SpellAbility sa, final PhaseHandler ph) {
|
protected boolean checkPhaseRestrictions(final Player ai, final SpellAbility sa, final PhaseHandler ph) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean checkPhaseRestrictions(final Player ai, final SpellAbility sa, final PhaseHandler ph,
|
protected boolean checkPhaseRestrictions(final Player ai, final SpellAbility sa, final PhaseHandler ph,
|
||||||
final String logic) {
|
final String logic) {
|
||||||
return checkPhaseRestrictions(ai, sa, ph);
|
return checkPhaseRestrictions(ai, sa, ph);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* The rest of the logic not covered by the canPlayAI template is defined here
|
* The rest of the logic not covered by the canPlayAI template is defined here
|
||||||
*/
|
*/
|
||||||
protected boolean checkApiLogic(final Player ai, final SpellAbility sa) {
|
protected boolean checkApiLogic(final Player ai, final SpellAbility sa) {
|
||||||
if (ComputerUtil.preventRunAwayActivations(sa)) {
|
if (ComputerUtil.preventRunAwayActivations(sa)) {
|
||||||
return false; // prevent infinite loop
|
return false; // prevent infinite loop
|
||||||
}
|
}
|
||||||
return MyRandom.getRandom().nextFloat() < .8f; // random success
|
return MyRandom.getRandom().nextFloat() < .8f; // random success
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean doTriggerAI(final Player aiPlayer, final SpellAbility sa, final boolean mandatory) {
|
public final boolean doTriggerAI(final Player aiPlayer, final SpellAbility sa, final boolean mandatory) {
|
||||||
if (!ComputerUtilCost.canPayCost(sa, aiPlayer) && !mandatory) {
|
if (!ComputerUtilCost.canPayCost(sa, aiPlayer) && !mandatory) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// a mandatory SpellAbility with targeting but without candidates,
|
// a mandatory SpellAbility with targeting but without candidates,
|
||||||
// does not need to go any deeper
|
// does not need to go any deeper
|
||||||
if (sa.usesTargeting() && mandatory && !sa.getTargetRestrictions().hasCandidates(sa, true)) {
|
if (sa.usesTargeting() && mandatory && !sa.getTargetRestrictions().hasCandidates(sa, true)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return doTriggerNoCostWithSubs(aiPlayer, sa, mandatory);
|
return doTriggerNoCostWithSubs(aiPlayer, sa, mandatory);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean doTriggerNoCostWithSubs(final Player aiPlayer, final SpellAbility sa, final boolean mandatory)
|
public final boolean doTriggerNoCostWithSubs(final Player aiPlayer, final SpellAbility sa, final boolean mandatory)
|
||||||
{
|
{
|
||||||
if (!doTriggerAINoCost(aiPlayer, sa, mandatory)) {
|
if (!doTriggerAINoCost(aiPlayer, sa, mandatory)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
final AbilitySub subAb = sa.getSubAbility();
|
final AbilitySub subAb = sa.getSubAbility();
|
||||||
return subAb == null || chkDrawbackWithSubs(aiPlayer, subAb) || mandatory;
|
return subAb == null || chkDrawbackWithSubs(aiPlayer, subAb) || mandatory;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the AI decision to play a triggered SpellAbility
|
* Handles the AI decision to play a triggered SpellAbility
|
||||||
*/
|
*/
|
||||||
protected boolean doTriggerAINoCost(final Player aiPlayer, final SpellAbility sa, final boolean mandatory) {
|
protected boolean doTriggerAINoCost(final Player aiPlayer, final SpellAbility sa, final boolean mandatory) {
|
||||||
if (canPlayWithoutRestrict(aiPlayer, sa)) {
|
if (canPlayWithoutRestrict(aiPlayer, sa)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// not mandatory, short way out
|
// not mandatory, short way out
|
||||||
if (!mandatory) {
|
if (!mandatory) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// invalid target might prevent it
|
// invalid target might prevent it
|
||||||
if (sa.usesTargeting()) {
|
if (sa.usesTargeting()) {
|
||||||
// make list of players it does try to target
|
// make list of players it does try to target
|
||||||
List<Player> players = Lists.newArrayList();
|
List<Player> players = Lists.newArrayList();
|
||||||
players.addAll(aiPlayer.getOpponents());
|
players.addAll(aiPlayer.getOpponents());
|
||||||
players.addAll(aiPlayer.getAllies());
|
players.addAll(aiPlayer.getAllies());
|
||||||
players.add(aiPlayer);
|
players.add(aiPlayer);
|
||||||
|
|
||||||
// try to target opponent, then ally, then itself
|
// try to target opponent, then ally, then itself
|
||||||
for (final Player p : players) {
|
for (final Player p : players) {
|
||||||
if (p.canBeTargetedBy(sa) && sa.canTarget(p)) {
|
if (p.canBeTargetedBy(sa) && sa.canTarget(p)) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
sa.getTargets().add(p);
|
sa.getTargets().add(p);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the AI decision to play a sub-SpellAbility
|
* Handles the AI decision to play a sub-SpellAbility
|
||||||
*/
|
*/
|
||||||
public boolean chkAIDrawback(final SpellAbility sa, final Player aiPlayer) {
|
public boolean chkAIDrawback(final SpellAbility sa, final Player aiPlayer) {
|
||||||
// sub-SpellAbility might use targets too
|
// sub-SpellAbility might use targets too
|
||||||
if (sa.usesTargeting()) {
|
if (sa.usesTargeting()) {
|
||||||
// no Candidates, no adding to Stack
|
// no Candidates, no adding to Stack
|
||||||
if (!sa.getTargetRestrictions().hasCandidates(sa, true)) {
|
if (!sa.getTargetRestrictions().hasCandidates(sa, true)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// but if it does, it should override this function
|
// but if it does, it should override this function
|
||||||
System.err.println("Warning: default (ie. inherited from base class) implementation of chkAIDrawback is used by " + sa.getHostCard().getName() + " for " + this.getClass().getName() + ". Consider declaring an overloaded method");
|
System.err.println("Warning: default (ie. inherited from base class) implementation of chkAIDrawback is used by " + sa.getHostCard().getName() + " for " + this.getClass().getName() + ". Consider declaring an overloaded method");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* isSorcerySpeed.
|
* isSorcerySpeed.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param sa
|
* @param sa
|
||||||
* a {@link forge.game.spellability.SpellAbility} object.
|
* a {@link forge.game.spellability.SpellAbility} object.
|
||||||
* @return a boolean.
|
* @return a boolean.
|
||||||
*/
|
*/
|
||||||
protected static boolean isSorcerySpeed(final SpellAbility sa) {
|
protected static boolean isSorcerySpeed(final SpellAbility sa) {
|
||||||
return (sa.isSpell() && sa.getHostCard().isSorcery())
|
return (sa.isSpell() && sa.getHostCard().isSorcery())
|
||||||
|| (sa.isAbility() && sa.getRestrictions().isSorcerySpeed())
|
|| (sa.isAbility() && sa.getRestrictions().isSorcerySpeed())
|
||||||
|| (sa.getRestrictions().isPwAbility() && !sa.getHostCard().hasKeyword("CARDNAME's loyalty abilities can be activated at instant speed."));
|
|| (sa.getRestrictions().isPwAbility() && !sa.getHostCard().hasKeyword("CARDNAME's loyalty abilities can be activated at instant speed."));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* playReusable.
|
* playReusable.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param sa
|
* @param sa
|
||||||
* a {@link forge.game.spellability.SpellAbility} object.
|
* a {@link forge.game.spellability.SpellAbility} object.
|
||||||
* @return a boolean.
|
* @return a boolean.
|
||||||
*/
|
*/
|
||||||
protected static boolean playReusable(final Player ai, final SpellAbility sa) {
|
protected static boolean playReusable(final Player ai, final SpellAbility sa) {
|
||||||
PhaseHandler phase = ai.getGame().getPhaseHandler();
|
PhaseHandler phase = ai.getGame().getPhaseHandler();
|
||||||
|
|
||||||
// TODO probably also consider if winter orb or similar are out
|
// TODO probably also consider if winter orb or similar are out
|
||||||
|
|
||||||
if (sa.getPayCosts() == null || sa instanceof AbilitySub) {
|
if (sa.getPayCosts() == null || sa instanceof AbilitySub) {
|
||||||
return true; // This is only true for Drawbacks and triggers
|
return true; // This is only true for Drawbacks and triggers
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sa.getPayCosts().isReusuableResource()) {
|
if (!sa.getPayCosts().isReusuableResource()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ComputerUtil.playImmediately(ai, sa)) {
|
if (ComputerUtil.playImmediately(ai, sa)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.getRestrictions().isPwAbility() && phase.is(PhaseType.MAIN2)) {
|
if (sa.getRestrictions().isPwAbility() && phase.is(PhaseType.MAIN2)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (sa.isSpell() && !sa.isBuyBackAbility()) {
|
if (sa.isSpell() && !sa.isBuyBackAbility()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return phase.is(PhaseType.END_OF_TURN) && phase.getNextTurn().equals(ai);
|
return phase.is(PhaseType.END_OF_TURN) && phase.getNextTurn().equals(ai);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: Write javadoc for this method.
|
* TODO: Write javadoc for this method.
|
||||||
* @param aiPlayer
|
* @param aiPlayer
|
||||||
* @param ab
|
* @param ab
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public boolean chkDrawbackWithSubs(Player aiPlayer, AbilitySub ab) {
|
public boolean chkDrawbackWithSubs(Player aiPlayer, AbilitySub ab) {
|
||||||
final AbilitySub subAb = ab.getSubAbility();
|
final AbilitySub subAb = ab.getSubAbility();
|
||||||
return SpellApiToAi.Converter.get(ab.getApi()).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) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
||||||
System.err.println("Warning: default (ie. inherited from base class) implementation of confirmAction is used by " + sa.getHostCard().getName() + " for " + this.getClass().getName() + ". Consider declaring an overloaded method");
|
System.err.println("Warning: default (ie. inherited from base class) implementation of confirmAction is used by " + sa.getHostCard().getName() + " for " + this.getClass().getName() + ". Consider declaring an overloaded method");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public <T extends GameEntity> T chooseSingleEntity(Player ai, SpellAbility sa, Collection<T> options, boolean isOptional, Player targetedPlayer) {
|
public <T extends GameEntity> T chooseSingleEntity(Player ai, SpellAbility sa, Collection<T> options, boolean isOptional, Player targetedPlayer) {
|
||||||
boolean hasPlayer = false;
|
boolean hasPlayer = false;
|
||||||
boolean hasCard = false;
|
boolean hasCard = false;
|
||||||
boolean hasPlaneswalker = false;
|
boolean hasPlaneswalker = false;
|
||||||
|
|
||||||
for (T ent : options) {
|
for (T ent : options) {
|
||||||
if (ent instanceof Player) {
|
if (ent instanceof Player) {
|
||||||
hasPlayer = true;
|
hasPlayer = true;
|
||||||
} else if (ent instanceof Card) {
|
} else if (ent instanceof Card) {
|
||||||
hasCard = true;
|
hasCard = true;
|
||||||
if (((Card)ent).isPlaneswalker()) {
|
if (((Card)ent).isPlaneswalker()) {
|
||||||
hasPlaneswalker = true;
|
hasPlaneswalker = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasPlayer && hasPlaneswalker) {
|
if (hasPlayer && hasPlaneswalker) {
|
||||||
return (T) chooseSinglePlayerOrPlaneswalker(ai, sa, (Collection<GameEntity>) options);
|
return (T) chooseSinglePlayerOrPlaneswalker(ai, sa, (Collection<GameEntity>) options);
|
||||||
} else if (hasCard) {
|
} else if (hasCard) {
|
||||||
return (T) chooseSingleCard(ai, sa, (Collection<Card>) options, isOptional, targetedPlayer);
|
return (T) chooseSingleCard(ai, sa, (Collection<Card>) options, isOptional, targetedPlayer);
|
||||||
} else if (hasPlayer) {
|
} else if (hasPlayer) {
|
||||||
return (T) chooseSinglePlayer(ai, sa, (Collection<Player>) options);
|
return (T) chooseSinglePlayer(ai, sa, (Collection<Player>) options);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SpellAbility chooseSingleSpellAbility(Player player, SpellAbility sa, List<SpellAbility> spells) {
|
public SpellAbility chooseSingleSpellAbility(Player player, SpellAbility sa, List<SpellAbility> spells) {
|
||||||
System.err.println("Warning: default (ie. inherited from base class) implementation of chooseSingleSpellAbility is used by " + sa.getHostCard().getName() + " for " + this.getClass().getName() + ". Consider declaring an overloaded method");
|
System.err.println("Warning: default (ie. inherited from base class) implementation of chooseSingleSpellAbility is used by " + sa.getHostCard().getName() + " for " + this.getClass().getName() + ". Consider declaring an overloaded method");
|
||||||
return spells.get(0);
|
return spells.get(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
|
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
|
||||||
System.err.println("Warning: default (ie. inherited from base class) implementation of chooseSingleCard is used by " + sa.getHostCard().getName() + " for " + this.getClass().getName() + ". Consider declaring an overloaded method");
|
System.err.println("Warning: default (ie. inherited from base class) implementation of chooseSingleCard is used by " + sa.getHostCard().getName() + " for " + this.getClass().getName() + ". Consider declaring an overloaded method");
|
||||||
return Iterables.getFirst(options, null);
|
return Iterables.getFirst(options, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options) {
|
protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options) {
|
||||||
System.err.println("Warning: default (ie. inherited from base class) implementation of chooseSinglePlayer is used by " + sa.getHostCard().getName() + " for " + this.getClass().getName() + ". Consider declaring an overloaded method");
|
System.err.println("Warning: default (ie. inherited from base class) implementation of chooseSinglePlayer is used by " + sa.getHostCard().getName() + " for " + this.getClass().getName() + ". Consider declaring an overloaded method");
|
||||||
return Iterables.getFirst(options, null);
|
return Iterables.getFirst(options, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected GameEntity chooseSinglePlayerOrPlaneswalker(Player ai, SpellAbility sa, Iterable<GameEntity> options) {
|
protected GameEntity chooseSinglePlayerOrPlaneswalker(Player ai, SpellAbility sa, Iterable<GameEntity> options) {
|
||||||
System.err.println("Warning: default (ie. inherited from base class) implementation of chooseSinglePlayerOrPlaneswalker is used for " + this.getClass().getName() + ". Consider declaring an overloaded method");
|
System.err.println("Warning: default (ie. inherited from base class) implementation of chooseSinglePlayerOrPlaneswalker is used for " + this.getClass().getName() + ". Consider declaring an overloaded method");
|
||||||
return Iterables.getFirst(options, null);
|
return Iterables.getFirst(options, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String chooseCardName(Player ai, SpellAbility sa, List<ICardFace> faces) {
|
public String chooseCardName(Player ai, SpellAbility sa, List<ICardFace> faces) {
|
||||||
System.err.println("Warning: default (ie. inherited from base class) implementation of chooseCardName is used for " + this.getClass().getName() + ". Consider declaring an overloaded method");
|
System.err.println("Warning: default (ie. inherited from base class) implementation of chooseCardName is used for " + this.getClass().getName() + ". Consider declaring an overloaded method");
|
||||||
|
|
||||||
final ICardFace face = Iterables.getFirst(faces, null);
|
final ICardFace face = Iterables.getFirst(faces, null);
|
||||||
return face == null ? "" : face.getName();
|
return face == null ? "" : face.getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int chooseNumber(Player player, SpellAbility sa, int min, int max, Map<String, Object> params) {
|
public int chooseNumber(Player player, SpellAbility sa, int min, int max, Map<String, Object> params) {
|
||||||
return max;
|
return max;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CounterType chooseCounterType(List<CounterType> options, SpellAbility sa, Map<String, Object> params) {
|
public CounterType chooseCounterType(List<CounterType> options, SpellAbility sa, Map<String, Object> params) {
|
||||||
return Iterables.getFirst(options, null);
|
return Iterables.getFirst(options, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean chooseBinary(BinaryChoiceType kindOfChoice, SpellAbility sa, Map<String, Object> params) {
|
public boolean chooseBinary(BinaryChoiceType kindOfChoice, SpellAbility sa, Map<String, Object> params) {
|
||||||
return MyRandom.getRandom().nextBoolean();
|
return MyRandom.getRandom().nextBoolean();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,174 +1,174 @@
|
|||||||
package forge.ai;
|
package forge.ai;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import forge.ai.ability.*;
|
import forge.ai.ability.*;
|
||||||
import forge.game.ability.ApiType;
|
import forge.game.ability.ApiType;
|
||||||
import forge.util.ReflectionUtil;
|
import forge.util.ReflectionUtil;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public enum SpellApiToAi {
|
public enum SpellApiToAi {
|
||||||
Converter;
|
Converter;
|
||||||
|
|
||||||
private final Map<ApiType, SpellAbilityAi> apiToInstance = Maps.newEnumMap(ApiType.class);
|
private final Map<ApiType, SpellAbilityAi> apiToInstance = Maps.newEnumMap(ApiType.class);
|
||||||
|
|
||||||
// Do the extra copy to make an actual EnumMap (faster)
|
// Do the extra copy to make an actual EnumMap (faster)
|
||||||
private final Map<ApiType, Class<? extends SpellAbilityAi>> apiToClass = Maps.newEnumMap(ImmutableMap
|
private final Map<ApiType, Class<? extends SpellAbilityAi>> apiToClass = Maps.newEnumMap(ImmutableMap
|
||||||
.<ApiType, Class<? extends SpellAbilityAi>>builder()
|
.<ApiType, Class<? extends SpellAbilityAi>>builder()
|
||||||
.put(ApiType.Abandon, AlwaysPlayAi.class)
|
.put(ApiType.Abandon, AlwaysPlayAi.class)
|
||||||
.put(ApiType.ActivateAbility, ActivateAbilityAi.class)
|
.put(ApiType.ActivateAbility, ActivateAbilityAi.class)
|
||||||
.put(ApiType.AddOrRemoveCounter, CountersPutOrRemoveAi.class)
|
.put(ApiType.AddOrRemoveCounter, CountersPutOrRemoveAi.class)
|
||||||
.put(ApiType.AddPhase, AddPhaseAi.class)
|
.put(ApiType.AddPhase, AddPhaseAi.class)
|
||||||
.put(ApiType.AddTurn, AddTurnAi.class)
|
.put(ApiType.AddTurn, AddTurnAi.class)
|
||||||
.put(ApiType.Animate, AnimateAi.class)
|
.put(ApiType.Animate, AnimateAi.class)
|
||||||
.put(ApiType.AnimateAll, AnimateAllAi.class)
|
.put(ApiType.AnimateAll, AnimateAllAi.class)
|
||||||
.put(ApiType.Attach, AttachAi.class)
|
.put(ApiType.Attach, AttachAi.class)
|
||||||
.put(ApiType.Balance, BalanceAi.class)
|
.put(ApiType.Balance, BalanceAi.class)
|
||||||
.put(ApiType.BecomeMonarch, AlwaysPlayAi.class)
|
.put(ApiType.BecomeMonarch, AlwaysPlayAi.class)
|
||||||
.put(ApiType.BecomesBlocked, BecomesBlockedAi.class)
|
.put(ApiType.BecomesBlocked, BecomesBlockedAi.class)
|
||||||
.put(ApiType.BidLife, BidLifeAi.class)
|
.put(ApiType.BidLife, BidLifeAi.class)
|
||||||
.put(ApiType.Bond, BondAi.class)
|
.put(ApiType.Bond, BondAi.class)
|
||||||
.put(ApiType.Branch, AlwaysPlayAi.class)
|
.put(ApiType.Branch, AlwaysPlayAi.class)
|
||||||
.put(ApiType.ChangeCombatants, CannotPlayAi.class)
|
.put(ApiType.ChangeCombatants, CannotPlayAi.class)
|
||||||
.put(ApiType.ChangeTargets, ChangeTargetsAi.class)
|
.put(ApiType.ChangeTargets, ChangeTargetsAi.class)
|
||||||
.put(ApiType.ChangeZone, ChangeZoneAi.class)
|
.put(ApiType.ChangeZone, ChangeZoneAi.class)
|
||||||
.put(ApiType.ChangeZoneAll, ChangeZoneAllAi.class)
|
.put(ApiType.ChangeZoneAll, ChangeZoneAllAi.class)
|
||||||
.put(ApiType.Charm, CharmAi.class)
|
.put(ApiType.Charm, CharmAi.class)
|
||||||
.put(ApiType.ChooseCard, ChooseCardAi.class)
|
.put(ApiType.ChooseCard, ChooseCardAi.class)
|
||||||
.put(ApiType.ChooseColor, ChooseColorAi.class)
|
.put(ApiType.ChooseColor, ChooseColorAi.class)
|
||||||
.put(ApiType.ChooseDirection, ChooseDirectionAi.class)
|
.put(ApiType.ChooseDirection, ChooseDirectionAi.class)
|
||||||
.put(ApiType.ChooseNumber, ChooseNumberAi.class)
|
.put(ApiType.ChooseNumber, ChooseNumberAi.class)
|
||||||
.put(ApiType.ChoosePlayer, ChoosePlayerAi.class)
|
.put(ApiType.ChoosePlayer, ChoosePlayerAi.class)
|
||||||
.put(ApiType.ChooseSource, ChooseSourceAi.class)
|
.put(ApiType.ChooseSource, ChooseSourceAi.class)
|
||||||
.put(ApiType.ChooseType, ChooseTypeAi.class)
|
.put(ApiType.ChooseType, ChooseTypeAi.class)
|
||||||
.put(ApiType.Clash, ClashAi.class)
|
.put(ApiType.Clash, ClashAi.class)
|
||||||
.put(ApiType.Cleanup, AlwaysPlayAi.class)
|
.put(ApiType.Cleanup, AlwaysPlayAi.class)
|
||||||
.put(ApiType.Clone, CloneAi.class)
|
.put(ApiType.Clone, CloneAi.class)
|
||||||
.put(ApiType.CopyPermanent, CopyPermanentAi.class)
|
.put(ApiType.CopyPermanent, CopyPermanentAi.class)
|
||||||
.put(ApiType.CopySpellAbility, CopySpellAbilityAi.class)
|
.put(ApiType.CopySpellAbility, CopySpellAbilityAi.class)
|
||||||
.put(ApiType.ControlPlayer, CannotPlayAi.class)
|
.put(ApiType.ControlPlayer, CannotPlayAi.class)
|
||||||
.put(ApiType.ControlSpell, CannotPlayAi.class)
|
.put(ApiType.ControlSpell, CannotPlayAi.class)
|
||||||
.put(ApiType.Counter, CounterAi.class)
|
.put(ApiType.Counter, CounterAi.class)
|
||||||
.put(ApiType.DamageAll, DamageAllAi.class)
|
.put(ApiType.DamageAll, DamageAllAi.class)
|
||||||
.put(ApiType.DealDamage, DamageDealAi.class)
|
.put(ApiType.DealDamage, DamageDealAi.class)
|
||||||
.put(ApiType.Debuff, DebuffAi.class)
|
.put(ApiType.Debuff, DebuffAi.class)
|
||||||
.put(ApiType.DeclareCombatants, CannotPlayAi.class)
|
.put(ApiType.DeclareCombatants, CannotPlayAi.class)
|
||||||
.put(ApiType.DelayedTrigger, DelayedTriggerAi.class)
|
.put(ApiType.DelayedTrigger, DelayedTriggerAi.class)
|
||||||
.put(ApiType.Destroy, DestroyAi.class)
|
.put(ApiType.Destroy, DestroyAi.class)
|
||||||
.put(ApiType.DestroyAll, DestroyAllAi.class)
|
.put(ApiType.DestroyAll, DestroyAllAi.class)
|
||||||
.put(ApiType.Dig, DigAi.class)
|
.put(ApiType.Dig, DigAi.class)
|
||||||
.put(ApiType.DigUntil, DigUntilAi.class)
|
.put(ApiType.DigUntil, DigUntilAi.class)
|
||||||
.put(ApiType.Discard, DiscardAi.class)
|
.put(ApiType.Discard, DiscardAi.class)
|
||||||
.put(ApiType.DrainMana, DrainManaAi.class)
|
.put(ApiType.DrainMana, DrainManaAi.class)
|
||||||
.put(ApiType.Draw, DrawAi.class)
|
.put(ApiType.Draw, DrawAi.class)
|
||||||
.put(ApiType.EachDamage, DamageEachAi.class)
|
.put(ApiType.EachDamage, DamageEachAi.class)
|
||||||
.put(ApiType.Effect, EffectAi.class)
|
.put(ApiType.Effect, EffectAi.class)
|
||||||
.put(ApiType.Encode, EncodeAi.class)
|
.put(ApiType.Encode, EncodeAi.class)
|
||||||
.put(ApiType.EndTurn, EndTurnAi.class)
|
.put(ApiType.EndTurn, EndTurnAi.class)
|
||||||
.put(ApiType.ExchangeLife, LifeExchangeAi.class)
|
.put(ApiType.ExchangeLife, LifeExchangeAi.class)
|
||||||
.put(ApiType.ExchangeControl, ControlExchangeAi.class)
|
.put(ApiType.ExchangeControl, ControlExchangeAi.class)
|
||||||
.put(ApiType.ExchangeControlVariant, CannotPlayAi.class)
|
.put(ApiType.ExchangeControlVariant, CannotPlayAi.class)
|
||||||
.put(ApiType.ExchangePower, PowerExchangeAi.class)
|
.put(ApiType.ExchangePower, PowerExchangeAi.class)
|
||||||
.put(ApiType.ExchangeZone, ZoneExchangeAi.class)
|
.put(ApiType.ExchangeZone, ZoneExchangeAi.class)
|
||||||
.put(ApiType.Explore, ExploreAi.class)
|
.put(ApiType.Explore, ExploreAi.class)
|
||||||
.put(ApiType.Fight, FightAi.class)
|
.put(ApiType.Fight, FightAi.class)
|
||||||
.put(ApiType.FlipACoin, FlipACoinAi.class)
|
.put(ApiType.FlipACoin, FlipACoinAi.class)
|
||||||
.put(ApiType.Fog, FogAi.class)
|
.put(ApiType.Fog, FogAi.class)
|
||||||
.put(ApiType.GainControl, ControlGainAi.class)
|
.put(ApiType.GainControl, ControlGainAi.class)
|
||||||
.put(ApiType.GainLife, LifeGainAi.class)
|
.put(ApiType.GainLife, LifeGainAi.class)
|
||||||
.put(ApiType.GainOwnership, CannotPlayAi.class)
|
.put(ApiType.GainOwnership, CannotPlayAi.class)
|
||||||
.put(ApiType.GameDrawn, CannotPlayAi.class)
|
.put(ApiType.GameDrawn, CannotPlayAi.class)
|
||||||
.put(ApiType.GenericChoice, ChooseGenericEffectAi.class)
|
.put(ApiType.GenericChoice, ChooseGenericEffectAi.class)
|
||||||
.put(ApiType.Goad, GoadAi.class)
|
.put(ApiType.Goad, GoadAi.class)
|
||||||
.put(ApiType.Haunt, HauntAi.class)
|
.put(ApiType.Haunt, HauntAi.class)
|
||||||
.put(ApiType.LoseLife, LifeLoseAi.class)
|
.put(ApiType.LoseLife, LifeLoseAi.class)
|
||||||
.put(ApiType.LosesGame, GameLossAi.class)
|
.put(ApiType.LosesGame, GameLossAi.class)
|
||||||
.put(ApiType.Mana, ManaEffectAi.class)
|
.put(ApiType.Mana, ManaEffectAi.class)
|
||||||
.put(ApiType.ManaReflected, CannotPlayAi.class)
|
.put(ApiType.ManaReflected, CannotPlayAi.class)
|
||||||
.put(ApiType.Manifest, ManifestAi.class)
|
.put(ApiType.Manifest, ManifestAi.class)
|
||||||
.put(ApiType.Meld, MeldAi.class)
|
.put(ApiType.Meld, MeldAi.class)
|
||||||
.put(ApiType.Mill, MillAi.class)
|
.put(ApiType.Mill, MillAi.class)
|
||||||
.put(ApiType.MoveCounter, CountersMoveAi.class)
|
.put(ApiType.MoveCounter, CountersMoveAi.class)
|
||||||
.put(ApiType.MultiplePiles, CannotPlayAi.class)
|
.put(ApiType.MultiplePiles, CannotPlayAi.class)
|
||||||
.put(ApiType.MultiplyCounter, CountersMultiplyAi.class)
|
.put(ApiType.MultiplyCounter, CountersMultiplyAi.class)
|
||||||
.put(ApiType.MustAttack, MustAttackAi.class)
|
.put(ApiType.MustAttack, MustAttackAi.class)
|
||||||
.put(ApiType.MustBlock, MustBlockAi.class)
|
.put(ApiType.MustBlock, MustBlockAi.class)
|
||||||
.put(ApiType.NameCard, ChooseCardNameAi.class)
|
.put(ApiType.NameCard, ChooseCardNameAi.class)
|
||||||
.put(ApiType.NoteCounters, AlwaysPlayAi.class)
|
.put(ApiType.NoteCounters, AlwaysPlayAi.class)
|
||||||
.put(ApiType.PeekAndReveal, PeekAndRevealAi.class)
|
.put(ApiType.PeekAndReveal, PeekAndRevealAi.class)
|
||||||
.put(ApiType.PermanentCreature, PermanentCreatureAi.class)
|
.put(ApiType.PermanentCreature, PermanentCreatureAi.class)
|
||||||
.put(ApiType.PermanentNoncreature, PermanentNoncreatureAi.class)
|
.put(ApiType.PermanentNoncreature, PermanentNoncreatureAi.class)
|
||||||
.put(ApiType.Phases, PhasesAi.class)
|
.put(ApiType.Phases, PhasesAi.class)
|
||||||
.put(ApiType.Planeswalk, AlwaysPlayAi.class)
|
.put(ApiType.Planeswalk, AlwaysPlayAi.class)
|
||||||
.put(ApiType.Play, PlayAi.class)
|
.put(ApiType.Play, PlayAi.class)
|
||||||
.put(ApiType.PlayLandVariant, CannotPlayAi.class)
|
.put(ApiType.PlayLandVariant, CannotPlayAi.class)
|
||||||
.put(ApiType.Poison, PoisonAi.class)
|
.put(ApiType.Poison, PoisonAi.class)
|
||||||
.put(ApiType.PreventDamage, DamagePreventAi.class)
|
.put(ApiType.PreventDamage, DamagePreventAi.class)
|
||||||
.put(ApiType.PreventDamageAll, DamagePreventAllAi.class)
|
.put(ApiType.PreventDamageAll, DamagePreventAllAi.class)
|
||||||
.put(ApiType.Proliferate, CountersProliferateAi.class)
|
.put(ApiType.Proliferate, CountersProliferateAi.class)
|
||||||
.put(ApiType.Protection, ProtectAi.class)
|
.put(ApiType.Protection, ProtectAi.class)
|
||||||
.put(ApiType.ProtectionAll, ProtectAllAi.class)
|
.put(ApiType.ProtectionAll, ProtectAllAi.class)
|
||||||
.put(ApiType.Pump, PumpAi.class)
|
.put(ApiType.Pump, PumpAi.class)
|
||||||
.put(ApiType.PumpAll, PumpAllAi.class)
|
.put(ApiType.PumpAll, PumpAllAi.class)
|
||||||
.put(ApiType.PutCounter, CountersPutAi.class)
|
.put(ApiType.PutCounter, CountersPutAi.class)
|
||||||
.put(ApiType.PutCounterAll, CountersPutAllAi.class)
|
.put(ApiType.PutCounterAll, CountersPutAllAi.class)
|
||||||
.put(ApiType.RearrangeTopOfLibrary, RearrangeTopOfLibraryAi.class)
|
.put(ApiType.RearrangeTopOfLibrary, RearrangeTopOfLibraryAi.class)
|
||||||
.put(ApiType.Regenerate, RegenerateAi.class)
|
.put(ApiType.Regenerate, RegenerateAi.class)
|
||||||
.put(ApiType.RegenerateAll, RegenerateAllAi.class)
|
.put(ApiType.RegenerateAll, RegenerateAllAi.class)
|
||||||
.put(ApiType.RemoveCounter, CountersRemoveAi.class)
|
.put(ApiType.RemoveCounter, CountersRemoveAi.class)
|
||||||
.put(ApiType.RemoveCounterAll, CannotPlayAi.class)
|
.put(ApiType.RemoveCounterAll, CannotPlayAi.class)
|
||||||
.put(ApiType.RemoveFromCombat, RemoveFromCombatAi.class)
|
.put(ApiType.RemoveFromCombat, RemoveFromCombatAi.class)
|
||||||
.put(ApiType.ReorderZone, AlwaysPlayAi.class)
|
.put(ApiType.ReorderZone, AlwaysPlayAi.class)
|
||||||
.put(ApiType.Repeat, RepeatAi.class)
|
.put(ApiType.Repeat, RepeatAi.class)
|
||||||
.put(ApiType.RepeatEach, RepeatEachAi.class)
|
.put(ApiType.RepeatEach, RepeatEachAi.class)
|
||||||
.put(ApiType.ReplaceEffect, AlwaysPlayAi.class)
|
.put(ApiType.ReplaceEffect, AlwaysPlayAi.class)
|
||||||
.put(ApiType.ReplaceSplitDamage, AlwaysPlayAi.class)
|
.put(ApiType.ReplaceSplitDamage, AlwaysPlayAi.class)
|
||||||
.put(ApiType.RestartGame, RestartGameAi.class)
|
.put(ApiType.RestartGame, RestartGameAi.class)
|
||||||
.put(ApiType.Reveal, RevealAi.class)
|
.put(ApiType.Reveal, RevealAi.class)
|
||||||
.put(ApiType.RevealHand, RevealHandAi.class)
|
.put(ApiType.RevealHand, RevealHandAi.class)
|
||||||
.put(ApiType.ReverseTurnOrder, AlwaysPlayAi.class)
|
.put(ApiType.ReverseTurnOrder, AlwaysPlayAi.class)
|
||||||
.put(ApiType.RollPlanarDice, RollPlanarDiceAi.class)
|
.put(ApiType.RollPlanarDice, RollPlanarDiceAi.class)
|
||||||
.put(ApiType.RunSVarAbility, AlwaysPlayAi.class)
|
.put(ApiType.RunSVarAbility, AlwaysPlayAi.class)
|
||||||
.put(ApiType.Sacrifice, SacrificeAi.class)
|
.put(ApiType.Sacrifice, SacrificeAi.class)
|
||||||
.put(ApiType.SacrificeAll, SacrificeAllAi.class)
|
.put(ApiType.SacrificeAll, SacrificeAllAi.class)
|
||||||
.put(ApiType.Scry, ScryAi.class)
|
.put(ApiType.Scry, ScryAi.class)
|
||||||
.put(ApiType.SetInMotion, AlwaysPlayAi.class)
|
.put(ApiType.SetInMotion, AlwaysPlayAi.class)
|
||||||
.put(ApiType.SetLife, LifeSetAi.class)
|
.put(ApiType.SetLife, LifeSetAi.class)
|
||||||
.put(ApiType.SetState, SetStateAi.class)
|
.put(ApiType.SetState, SetStateAi.class)
|
||||||
.put(ApiType.Shuffle, ShuffleAi.class)
|
.put(ApiType.Shuffle, ShuffleAi.class)
|
||||||
.put(ApiType.SkipTurn, SkipTurnAi.class)
|
.put(ApiType.SkipTurn, SkipTurnAi.class)
|
||||||
.put(ApiType.StoreMap, StoreMapAi.class)
|
.put(ApiType.StoreMap, StoreMapAi.class)
|
||||||
.put(ApiType.StoreSVar, StoreSVarAi.class)
|
.put(ApiType.StoreSVar, StoreSVarAi.class)
|
||||||
.put(ApiType.Tap, TapAi.class)
|
.put(ApiType.Tap, TapAi.class)
|
||||||
.put(ApiType.TapAll, TapAllAi.class)
|
.put(ApiType.TapAll, TapAllAi.class)
|
||||||
.put(ApiType.TapOrUntap, TapOrUntapAi.class)
|
.put(ApiType.TapOrUntap, TapOrUntapAi.class)
|
||||||
.put(ApiType.TapOrUntapAll, TapOrUntapAllAi.class)
|
.put(ApiType.TapOrUntapAll, TapOrUntapAllAi.class)
|
||||||
.put(ApiType.Token, TokenAi.class)
|
.put(ApiType.Token, TokenAi.class)
|
||||||
.put(ApiType.TwoPiles, TwoPilesAi.class)
|
.put(ApiType.TwoPiles, TwoPilesAi.class)
|
||||||
.put(ApiType.Unattach, CannotPlayAi.class)
|
.put(ApiType.Unattach, CannotPlayAi.class)
|
||||||
.put(ApiType.UnattachAll, UnattachAllAi.class)
|
.put(ApiType.UnattachAll, UnattachAllAi.class)
|
||||||
.put(ApiType.Untap, UntapAi.class)
|
.put(ApiType.Untap, UntapAi.class)
|
||||||
.put(ApiType.UntapAll, UntapAllAi.class)
|
.put(ApiType.UntapAll, UntapAllAi.class)
|
||||||
.put(ApiType.Vote, VoteAi.class)
|
.put(ApiType.Vote, VoteAi.class)
|
||||||
.put(ApiType.WinsGame, GameWinAi.class)
|
.put(ApiType.WinsGame, GameWinAi.class)
|
||||||
|
|
||||||
.put(ApiType.InternalEtbReplacement, CanPlayAsDrawbackAi.class)
|
.put(ApiType.InternalEtbReplacement, CanPlayAsDrawbackAi.class)
|
||||||
.put(ApiType.InternalLegendaryRule, LegendaryRuleAi.class)
|
.put(ApiType.InternalLegendaryRule, LegendaryRuleAi.class)
|
||||||
.put(ApiType.InternalIgnoreEffect, CannotPlayAi.class)
|
.put(ApiType.InternalIgnoreEffect, CannotPlayAi.class)
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
public SpellAbilityAi get(final ApiType api) {
|
public SpellAbilityAi get(final ApiType api) {
|
||||||
SpellAbilityAi result = apiToInstance.get(api);
|
SpellAbilityAi result = apiToInstance.get(api);
|
||||||
if (null == result) {
|
if (null == result) {
|
||||||
Class<? extends SpellAbilityAi> clz = apiToClass.get(api);
|
Class<? extends SpellAbilityAi> clz = apiToClass.get(api);
|
||||||
if (null == clz) {
|
if (null == clz) {
|
||||||
System.err.println("No AI assigned for API: " + api);
|
System.err.println("No AI assigned for API: " + api);
|
||||||
clz = CannotPlayAi.class;
|
clz = CannotPlayAi.class;
|
||||||
}
|
}
|
||||||
result = ReflectionUtil.makeDefaultInstanceOf(clz);
|
result = ReflectionUtil.makeDefaultInstanceOf(clz);
|
||||||
apiToInstance.put(api, result);
|
apiToInstance.put(api, result);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,101 +1,101 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import forge.ai.ComputerUtil;
|
import forge.ai.ComputerUtil;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.TargetRestrictions;
|
import forge.game.spellability.TargetRestrictions;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
import forge.util.MyRandom;
|
import forge.util.MyRandom;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
public class ActivateAbilityAi extends SpellAbilityAi {
|
public class ActivateAbilityAi extends SpellAbilityAi {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
||||||
// AI cannot use this properly until he can use SAs during Humans turn
|
// AI cannot use this properly until he can use SAs during Humans turn
|
||||||
|
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
final Player opp = ComputerUtil.getOpponentFor(ai);
|
final Player opp = ComputerUtil.getOpponentFor(ai);
|
||||||
final Random r = MyRandom.getRandom();
|
final Random r = MyRandom.getRandom();
|
||||||
boolean randomReturn = r.nextFloat() <= Math.pow(.6667, sa.getActivationsThisTurn());
|
boolean randomReturn = r.nextFloat() <= Math.pow(.6667, sa.getActivationsThisTurn());
|
||||||
|
|
||||||
List<Card> list = CardLists.getType(opp.getCardsIn(ZoneType.Battlefield), sa.getParamOrDefault("Type", "Card"));
|
List<Card> list = CardLists.getType(opp.getCardsIn(ZoneType.Battlefield), sa.getParamOrDefault("Type", "Card"));
|
||||||
if (list.isEmpty()) {
|
if (list.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tgt == null) {
|
if (tgt == null) {
|
||||||
final List<Player> defined = AbilityUtils.getDefinedPlayers(source, sa.getParam("Defined"), sa);
|
final List<Player> defined = AbilityUtils.getDefinedPlayers(source, sa.getParam("Defined"), sa);
|
||||||
|
|
||||||
if (!defined.contains(opp)) {
|
if (!defined.contains(opp)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
sa.getTargets().add(opp);
|
sa.getTargets().add(opp);
|
||||||
}
|
}
|
||||||
|
|
||||||
return randomReturn;
|
return randomReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
||||||
final Player opp = ComputerUtil.getOpponentFor(ai);
|
final Player opp = ComputerUtil.getOpponentFor(ai);
|
||||||
|
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
|
|
||||||
if (null == tgt) {
|
if (null == tgt) {
|
||||||
if (mandatory) {
|
if (mandatory) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
final List<Player> defined = AbilityUtils.getDefinedPlayers(source, sa.getParam("Defined"), sa);
|
final List<Player> defined = AbilityUtils.getDefinedPlayers(source, sa.getParam("Defined"), sa);
|
||||||
|
|
||||||
if (!defined.contains(opp)) {
|
if (!defined.contains(opp)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
sa.getTargets().add(opp);
|
sa.getTargets().add(opp);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
||||||
// AI cannot use this properly until he can use SAs during Humans turn
|
// AI cannot use this properly until he can use SAs during Humans turn
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
|
|
||||||
boolean randomReturn = true;
|
boolean randomReturn = true;
|
||||||
|
|
||||||
if (tgt == null) {
|
if (tgt == null) {
|
||||||
final List<Player> defined = AbilityUtils.getDefinedPlayers(source, sa.getParam("Defined"), sa);
|
final List<Player> defined = AbilityUtils.getDefinedPlayers(source, sa.getParam("Defined"), sa);
|
||||||
|
|
||||||
if (defined.contains(ai)) {
|
if (defined.contains(ai)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
sa.getTargets().add(ComputerUtil.getOpponentFor(ai));
|
sa.getTargets().add(ComputerUtil.getOpponentFor(ai));
|
||||||
}
|
}
|
||||||
|
|
||||||
return randomReturn;
|
return randomReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SpellAbility chooseSingleSpellAbility(Player player, SpellAbility sa, List<SpellAbility> spells) {
|
public SpellAbility chooseSingleSpellAbility(Player player, SpellAbility sa, List<SpellAbility> spells) {
|
||||||
return spells.get(0);
|
return spells.get(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: Write javadoc for this type.
|
* TODO: Write javadoc for this type.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class AddPhaseAi extends SpellAbilityAi {
|
public class AddPhaseAi extends SpellAbilityAi {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
|
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.player.PlayerActionConfirmMode;
|
import forge.game.player.PlayerActionConfirmMode;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
|
|
||||||
public class AlwaysPlayAi extends SpellAbilityAi {
|
public class AlwaysPlayAi extends SpellAbilityAi {
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see forge.card.abilityfactory.SpellAiLogic#canPlayAI(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility)
|
* @see forge.card.abilityfactory.SpellAiLogic#canPlayAI(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,19 +1,19 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
|
|
||||||
public class AnimateAllAi extends SpellAbilityAi {
|
public class AnimateAllAi extends SpellAbilityAi {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
||||||
return false;
|
return false;
|
||||||
} // end animateAllCanPlayAI()
|
} // end animateAllCanPlayAI()
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // end class AbilityFactoryAnimate
|
} // end class AbilityFactoryAnimate
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,48 +1,48 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import forge.ai.ComputerUtil;
|
import forge.ai.ComputerUtil;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.card.CardCollectionView;
|
import forge.game.card.CardCollectionView;
|
||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
import forge.game.card.CardPredicates;
|
import forge.game.card.CardPredicates;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
import forge.util.MyRandom;
|
import forge.util.MyRandom;
|
||||||
|
|
||||||
public class BalanceAi extends SpellAbilityAi {
|
public class BalanceAi extends SpellAbilityAi {
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
||||||
String logic = sa.getParam("AILogic");
|
String logic = sa.getParam("AILogic");
|
||||||
|
|
||||||
int diff = 0;
|
int diff = 0;
|
||||||
// TODO Add support for multiplayer logic
|
// TODO Add support for multiplayer logic
|
||||||
final Player opp = ComputerUtil.getOpponentFor(aiPlayer);
|
final Player opp = ComputerUtil.getOpponentFor(aiPlayer);
|
||||||
final CardCollectionView humPerms = opp.getCardsIn(ZoneType.Battlefield);
|
final CardCollectionView humPerms = opp.getCardsIn(ZoneType.Battlefield);
|
||||||
final CardCollectionView compPerms = aiPlayer.getCardsIn(ZoneType.Battlefield);
|
final CardCollectionView compPerms = aiPlayer.getCardsIn(ZoneType.Battlefield);
|
||||||
|
|
||||||
if ("BalanceCreaturesAndLands".equals(logic)) {
|
if ("BalanceCreaturesAndLands".equals(logic)) {
|
||||||
// Copied over from hardcoded Balance. We should be checking value of the lands/creatures not just counting
|
// Copied over from hardcoded Balance. We should be checking value of the lands/creatures not just counting
|
||||||
diff += CardLists.filter(humPerms, CardPredicates.Presets.LANDS).size() -
|
diff += CardLists.filter(humPerms, CardPredicates.Presets.LANDS).size() -
|
||||||
CardLists.filter(compPerms, CardPredicates.Presets.LANDS).size();
|
CardLists.filter(compPerms, CardPredicates.Presets.LANDS).size();
|
||||||
diff += 1.5 * ( CardLists.filter(humPerms, CardPredicates.Presets.CREATURES).size() -
|
diff += 1.5 * ( CardLists.filter(humPerms, CardPredicates.Presets.CREATURES).size() -
|
||||||
CardLists.filter(compPerms, CardPredicates.Presets.CREATURES).size());
|
CardLists.filter(compPerms, CardPredicates.Presets.CREATURES).size());
|
||||||
}
|
}
|
||||||
else if ("BalancePermanents".equals(logic)) {
|
else if ("BalancePermanents".equals(logic)) {
|
||||||
// Don't cast if you have to sacrifice permanents
|
// Don't cast if you have to sacrifice permanents
|
||||||
diff += humPerms.size() - compPerms.size();
|
diff += humPerms.size() - compPerms.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (diff < 0) {
|
if (diff < 0) {
|
||||||
// Don't sacrifice permanents even if opponent has a ton of cards in hand
|
// Don't sacrifice permanents even if opponent has a ton of cards in hand
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final CardCollectionView humHand = opp.getCardsIn(ZoneType.Hand);
|
final CardCollectionView humHand = opp.getCardsIn(ZoneType.Hand);
|
||||||
final CardCollectionView compHand = aiPlayer.getCardsIn(ZoneType.Hand);
|
final CardCollectionView compHand = aiPlayer.getCardsIn(ZoneType.Hand);
|
||||||
diff += 0.5 * (humHand.size() - compHand.size());
|
diff += 0.5 * (humHand.size() - compHand.size());
|
||||||
|
|
||||||
// Larger differential == more chance to actually cast this spell
|
// Larger differential == more chance to actually cast this spell
|
||||||
return diff > 2 && MyRandom.getRandom().nextInt(100) < diff*10;
|
return diff > 2 && MyRandom.getRandom().nextInt(100) < diff*10;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,70 +1,70 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
|
|
||||||
import forge.ai.ComputerUtilCard;
|
import forge.ai.ComputerUtilCard;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardCollection;
|
import forge.game.card.CardCollection;
|
||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.TargetRestrictions;
|
import forge.game.spellability.TargetRestrictions;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
|
|
||||||
public class BecomesBlockedAi extends SpellAbilityAi {
|
public class BecomesBlockedAi extends SpellAbilityAi {
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
final Game game = aiPlayer.getGame();
|
final Game game = aiPlayer.getGame();
|
||||||
|
|
||||||
if (!game.getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS)
|
if (!game.getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS)
|
||||||
|| !game.getPhaseHandler().getPlayerTurn().isOpponentOf(aiPlayer)) {
|
|| !game.getPhaseHandler().getPlayerTurn().isOpponentOf(aiPlayer)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tgt != null) {
|
if (tgt != null) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
CardCollection list = CardLists.filterControlledBy(game.getCardsIn(ZoneType.Battlefield), aiPlayer.getOpponents());
|
CardCollection list = CardLists.filterControlledBy(game.getCardsIn(ZoneType.Battlefield), aiPlayer.getOpponents());
|
||||||
list = CardLists.getValidCards(list, tgt.getValidTgts(), source.getController(), source, sa);
|
list = CardLists.getValidCards(list, tgt.getValidTgts(), source.getController(), source, sa);
|
||||||
list = CardLists.getTargetableCards(list, sa);
|
list = CardLists.getTargetableCards(list, sa);
|
||||||
list = CardLists.getNotKeyword(list, "Trample");
|
list = CardLists.getNotKeyword(list, "Trample");
|
||||||
|
|
||||||
while (sa.getTargets().getNumTargeted() < tgt.getMaxTargets(source, sa)) {
|
while (sa.getTargets().getNumTargeted() < tgt.getMaxTargets(source, sa)) {
|
||||||
Card choice = null;
|
Card choice = null;
|
||||||
|
|
||||||
if (list.isEmpty()) {
|
if (list.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
choice = ComputerUtilCard.getBestCreatureAI(list);
|
choice = ComputerUtilCard.getBestCreatureAI(list);
|
||||||
|
|
||||||
if (choice == null) { // can't find anything left
|
if (choice == null) { // can't find anything left
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
list.remove(choice);
|
list.remove(choice);
|
||||||
sa.getTargets().add(choice);
|
sa.getTargets().add(choice);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean chkAIDrawback(SpellAbility sa, Player aiPlayer) {
|
public boolean chkAIDrawback(SpellAbility sa, Player aiPlayer) {
|
||||||
// TODO - implement AI
|
// TODO - implement AI
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
|
||||||
boolean chance;
|
boolean chance;
|
||||||
|
|
||||||
// TODO - implement AI
|
// TODO - implement AI
|
||||||
chance = false;
|
chance = false;
|
||||||
|
|
||||||
return chance;
|
return chance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,58 +1,58 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import forge.ai.ComputerUtil;
|
import forge.ai.ComputerUtil;
|
||||||
import forge.ai.ComputerUtilCard;
|
import forge.ai.ComputerUtilCard;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardFactoryUtil;
|
import forge.game.card.CardFactoryUtil;
|
||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.TargetRestrictions;
|
import forge.game.spellability.TargetRestrictions;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
import forge.util.MyRandom;
|
import forge.util.MyRandom;
|
||||||
|
|
||||||
public class BidLifeAi extends SpellAbilityAi {
|
public class BidLifeAi extends SpellAbilityAi {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
final Game game = source.getGame();
|
final Game game = source.getGame();
|
||||||
TargetRestrictions tgt = sa.getTargetRestrictions();
|
TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
if (tgt != null) {
|
if (tgt != null) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
if (tgt.canTgtCreature()) {
|
if (tgt.canTgtCreature()) {
|
||||||
List<Card> list = CardLists.getTargetableCards(ComputerUtil.getOpponentFor(aiPlayer).getCardsIn(ZoneType.Battlefield), sa);
|
List<Card> list = CardLists.getTargetableCards(ComputerUtil.getOpponentFor(aiPlayer).getCardsIn(ZoneType.Battlefield), sa);
|
||||||
list = CardLists.getValidCards(list, tgt.getValidTgts(), source.getController(), source, sa);
|
list = CardLists.getValidCards(list, tgt.getValidTgts(), source.getController(), source, sa);
|
||||||
if (list.isEmpty()) {
|
if (list.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Card c = ComputerUtilCard.getBestCreatureAI(list);
|
Card c = ComputerUtilCard.getBestCreatureAI(list);
|
||||||
if (sa.canTarget(c)) {
|
if (sa.canTarget(c)) {
|
||||||
sa.getTargets().add(c);
|
sa.getTargets().add(c);
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (tgt.getZone().contains(ZoneType.Stack)) {
|
} else if (tgt.getZone().contains(ZoneType.Stack)) {
|
||||||
if (game.getStack().isEmpty()) {
|
if (game.getStack().isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
final SpellAbility topSA = game.getStack().peekAbility();
|
final SpellAbility topSA = game.getStack().peekAbility();
|
||||||
if (!CardFactoryUtil.isCounterableBy(topSA.getHostCard(), sa) || aiPlayer.equals(topSA.getActivatingPlayer())) {
|
if (!CardFactoryUtil.isCounterableBy(topSA.getHostCard(), sa) || aiPlayer.equals(topSA.getActivatingPlayer())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (sa.canTargetSpellAbility(topSA)) {
|
if (sa.canTargetSpellAbility(topSA)) {
|
||||||
sa.getTargets().add(topSA);
|
sa.getTargets().add(topSA);
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
boolean chance = MyRandom.getRandom().nextFloat() <= Math.pow(.6667, sa.getActivationsThisTurn());
|
boolean chance = MyRandom.getRandom().nextFloat() <= Math.pow(.6667, sa.getActivationsThisTurn());
|
||||||
return chance;
|
return chance;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,61 +1,61 @@
|
|||||||
/*
|
/*
|
||||||
* Forge: Play Magic: the Gathering.
|
* Forge: Play Magic: the Gathering.
|
||||||
* Copyright (C) 2011 Forge Team
|
* Copyright (C) 2011 Forge Team
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful,
|
* This program is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import forge.ai.ComputerUtilCard;
|
import forge.ai.ComputerUtilCard;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* AbilityFactoryBond class.
|
* AbilityFactoryBond class.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @author Forge
|
* @author Forge
|
||||||
* @version $Id: AbilityFactoryBond.java 15090 2012-04-07 12:50:31Z Max mtg $
|
* @version $Id: AbilityFactoryBond.java 15090 2012-04-07 12:50:31Z Max mtg $
|
||||||
*/
|
*/
|
||||||
public final class BondAi extends SpellAbilityAi {
|
public final class BondAi extends SpellAbilityAi {
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* bondCanPlayAI.
|
* bondCanPlayAI.
|
||||||
* </p>
|
* </p>
|
||||||
* @param aiPlayer
|
* @param aiPlayer
|
||||||
* a {@link forge.game.player.Player} object.
|
* a {@link forge.game.player.Player} object.
|
||||||
* @param sa
|
* @param sa
|
||||||
* a {@link forge.game.spellability.SpellAbility} object.
|
* a {@link forge.game.spellability.SpellAbility} object.
|
||||||
*
|
*
|
||||||
* @return a boolean.
|
* @return a boolean.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
||||||
return true;
|
return true;
|
||||||
} // end bondCanPlayAI()
|
} // end bondCanPlayAI()
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
|
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
|
||||||
return ComputerUtilCard.getBestCreatureAI(options);
|
return ComputerUtilCard.getBestCreatureAI(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(final Player aiPlayer, final SpellAbility sa, final boolean mandatory) {
|
protected boolean doTriggerAINoCost(final Player aiPlayer, final SpellAbility sa, final boolean mandatory) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,44 +1,44 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
|
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class CanPlayAsDrawbackAi extends SpellAbilityAi {
|
public class CanPlayAsDrawbackAi extends SpellAbilityAi {
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see forge.card.abilityfactory.SpellAiLogic#canPlayAI(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility)
|
* @see forge.card.abilityfactory.SpellAiLogic#canPlayAI(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* copySpellTriggerAI.
|
* copySpellTriggerAI.
|
||||||
* </p>
|
* </p>
|
||||||
* @param sa
|
* @param sa
|
||||||
* a {@link forge.game.spellability.SpellAbility} object.
|
* a {@link forge.game.spellability.SpellAbility} object.
|
||||||
* @param mandatory
|
* @param mandatory
|
||||||
* a boolean.
|
* a boolean.
|
||||||
* @param af
|
* @param af
|
||||||
* a {@link forge.game.ability.AbilityFactory} object.
|
* a {@link forge.game.ability.AbilityFactory} object.
|
||||||
*
|
*
|
||||||
* @return a boolean.
|
* @return a boolean.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SpellAbility chooseSingleSpellAbility(Player player, SpellAbility sa, List<SpellAbility> spells) {
|
public SpellAbility chooseSingleSpellAbility(Player player, SpellAbility sa, List<SpellAbility> spells) {
|
||||||
// This might be called from CopySpellAbilityEffect - to hide warning (for having no overload) use this simple overload
|
// This might be called from CopySpellAbilityEffect - to hide warning (for having no overload) use this simple overload
|
||||||
return spells.get(0);
|
return spells.get(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
|
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
|
|
||||||
public class CannotPlayAi extends SpellAbilityAi {
|
public class CannotPlayAi extends SpellAbilityAi {
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see forge.card.abilityfactory.SpellAiLogic#canPlayAI(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility)
|
* @see forge.card.abilityfactory.SpellAiLogic#canPlayAI(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see forge.card.abilityfactory.SpellAiLogic#chkAIDrawback(java.util.Map, forge.card.spellability.SpellAbility, forge.game.player.Player)
|
* @see forge.card.abilityfactory.SpellAiLogic#chkAIDrawback(java.util.Map, forge.card.spellability.SpellAbility, forge.game.player.Player)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean chkAIDrawback(SpellAbility sa, Player aiPlayer) {
|
public boolean chkAIDrawback(SpellAbility sa, Player aiPlayer) {
|
||||||
return canPlayAI(aiPlayer, sa);
|
return canPlayAI(aiPlayer, sa);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,93 +1,93 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import forge.ai.ComputerUtil;
|
import forge.ai.ComputerUtil;
|
||||||
import forge.ai.ComputerUtilAbility;
|
import forge.ai.ComputerUtilAbility;
|
||||||
import forge.ai.ComputerUtilMana;
|
import forge.ai.ComputerUtilMana;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.card.mana.ManaCost;
|
import forge.card.mana.ManaCost;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.mana.ManaCostBeingPaid;
|
import forge.game.mana.ManaCostBeingPaid;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
|
|
||||||
public class ChangeTargetsAi extends SpellAbilityAi {
|
public class ChangeTargetsAi extends SpellAbilityAi {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
*
|
*
|
||||||
* @see forge.ai.SpellAbilityAi#checkApiLogic(forge.game.player.Player,
|
* @see forge.ai.SpellAbilityAi#checkApiLogic(forge.game.player.Player,
|
||||||
* forge.game.spellability.SpellAbility)
|
* forge.game.spellability.SpellAbility)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkApiLogic(Player ai, SpellAbility sa) {
|
protected boolean checkApiLogic(Player ai, SpellAbility sa) {
|
||||||
final Game game = sa.getHostCard().getGame();
|
final Game game = sa.getHostCard().getGame();
|
||||||
final SpellAbility topSa = game.getStack().isEmpty() ? null
|
final SpellAbility topSa = game.getStack().isEmpty() ? null
|
||||||
: ComputerUtilAbility.getTopSpellAbilityOnStack(game, sa);
|
: ComputerUtilAbility.getTopSpellAbilityOnStack(game, sa);
|
||||||
|
|
||||||
if ("Self".equals(sa.getParam("DefinedMagnet"))) {
|
if ("Self".equals(sa.getParam("DefinedMagnet"))) {
|
||||||
return doSpellMagnet(sa, topSa, ai);
|
return doSpellMagnet(sa, topSa, ai);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The AI can't otherwise play this ability, but should at least not
|
// The AI can't otherwise play this ability, but should at least not
|
||||||
// miss mandatory activations (e.g. triggers).
|
// miss mandatory activations (e.g. triggers).
|
||||||
return sa.isMandatory();
|
return sa.isMandatory();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean doSpellMagnet(SpellAbility sa, SpellAbility topSa, Player aiPlayer) {
|
private boolean doSpellMagnet(SpellAbility sa, SpellAbility topSa, Player aiPlayer) {
|
||||||
// For cards like Spellskite that retarget spells to itself
|
// For cards like Spellskite that retarget spells to itself
|
||||||
if (topSa == null) {
|
if (topSa == null) {
|
||||||
// nothing on stack, so nothing to target
|
// nothing on stack, so nothing to target
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.getTargets().getNumTargeted() != 0) {
|
if (sa.getTargets().getNumTargeted() != 0) {
|
||||||
// something was already chosen before (e.g. in response to a trigger - Mizzium Meddler), so just proceed
|
// something was already chosen before (e.g. in response to a trigger - Mizzium Meddler), so just proceed
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!topSa.usesTargeting() || topSa.getTargets().getTargetCards().contains(sa.getHostCard())) {
|
if (!topSa.usesTargeting() || topSa.getTargets().getTargetCards().contains(sa.getHostCard())) {
|
||||||
// if this does not target at all or already targets host, no need to redirect it again
|
// if this does not target at all or already targets host, no need to redirect it again
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Card tgt : topSa.getTargets().getTargetCards()) {
|
for (Card tgt : topSa.getTargets().getTargetCards()) {
|
||||||
if (ComputerUtilAbility.getAbilitySourceName(sa).equals(tgt.getName()) && tgt.getController().equals(aiPlayer)) {
|
if (ComputerUtilAbility.getAbilitySourceName(sa).equals(tgt.getName()) && tgt.getController().equals(aiPlayer)) {
|
||||||
// We are already targeting at least one card with the same name (e.g. in presence of 2+ Spellskites),
|
// We are already targeting at least one card with the same name (e.g. in presence of 2+ Spellskites),
|
||||||
// no need to retarget again to another one
|
// no need to retarget again to another one
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (topSa.getHostCard() != null && !topSa.getHostCard().getController().isOpponentOf(aiPlayer)) {
|
if (topSa.getHostCard() != null && !topSa.getHostCard().getController().isOpponentOf(aiPlayer)) {
|
||||||
// make sure not to redirect our own abilities
|
// make sure not to redirect our own abilities
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!topSa.canTarget(sa.getHostCard())) {
|
if (!topSa.canTarget(sa.getHostCard())) {
|
||||||
// don't try targeting it if we can't legally target the host card with it in the first place
|
// don't try targeting it if we can't legally target the host card with it in the first place
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!sa.canTarget(topSa)) {
|
if (!sa.canTarget(topSa)) {
|
||||||
// don't try retargeting a spell that the current card can't legally retarget (e.g. Muck Drubb + Lightning Bolt to the face)
|
// don't try retargeting a spell that the current card can't legally retarget (e.g. Muck Drubb + Lightning Bolt to the face)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.getPayCosts().getCostMana() != null && sa.getPayCosts().getCostMana().getMana().hasPhyrexian()) {
|
if (sa.getPayCosts().getCostMana() != null && sa.getPayCosts().getCostMana().getMana().hasPhyrexian()) {
|
||||||
ManaCost manaCost = sa.getPayCosts().getCostMana().getMana();
|
ManaCost manaCost = sa.getPayCosts().getCostMana().getMana();
|
||||||
int payDamage = manaCost.getPhyrexianCount() * 2;
|
int payDamage = manaCost.getPhyrexianCount() * 2;
|
||||||
// e.g. Spellskite or a creature receiving its ability that requires Phyrexian mana P/U
|
// e.g. Spellskite or a creature receiving its ability that requires Phyrexian mana P/U
|
||||||
int potentialDmg = ComputerUtil.predictDamageFromSpell(topSa, aiPlayer);
|
int potentialDmg = ComputerUtil.predictDamageFromSpell(topSa, aiPlayer);
|
||||||
ManaCost normalizedMana = manaCost.getNormalizedMana();
|
ManaCost normalizedMana = manaCost.getNormalizedMana();
|
||||||
boolean canPay = ComputerUtilMana.canPayManaCost(new ManaCostBeingPaid(normalizedMana), sa, aiPlayer);
|
boolean canPay = ComputerUtilMana.canPayManaCost(new ManaCostBeingPaid(normalizedMana), sa, aiPlayer);
|
||||||
if (potentialDmg != -1 && potentialDmg <= payDamage && !canPay
|
if (potentialDmg != -1 && potentialDmg <= payDamage && !canPay
|
||||||
&& topSa.getTargets().getTargets().contains(aiPlayer)) {
|
&& topSa.getTargets().getTargets().contains(aiPlayer)) {
|
||||||
// do not pay Phyrexian mana if the spell is a damaging one but it deals less damage or the same damage as we'll pay life
|
// do not pay Phyrexian mana if the spell is a damaging one but it deals less damage or the same damage as we'll pay life
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
sa.getTargets().add(topSa);
|
sa.getTargets().add(topSa);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,466 +1,466 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import forge.ai.*;
|
import forge.ai.*;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.card.*;
|
import forge.game.card.*;
|
||||||
import forge.game.cost.Cost;
|
import forge.game.cost.Cost;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.player.PlayerActionConfirmMode;
|
import forge.game.player.PlayerActionConfirmMode;
|
||||||
import forge.game.player.PlayerCollection;
|
import forge.game.player.PlayerCollection;
|
||||||
import forge.game.player.PlayerPredicates;
|
import forge.game.player.PlayerPredicates;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
import forge.util.MyRandom;
|
import forge.util.MyRandom;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
public class ChangeZoneAllAi extends SpellAbilityAi {
|
public class ChangeZoneAllAi extends SpellAbilityAi {
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
||||||
// Change Zone All, can be any type moving from one zone to another
|
// Change Zone All, can be any type moving from one zone to another
|
||||||
final Cost abCost = sa.getPayCosts();
|
final Cost abCost = sa.getPayCosts();
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
final String sourceName = ComputerUtilAbility.getAbilitySourceName(sa);
|
final String sourceName = ComputerUtilAbility.getAbilitySourceName(sa);
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
final ZoneType destination = ZoneType.smartValueOf(sa.getParam("Destination"));
|
final ZoneType destination = ZoneType.smartValueOf(sa.getParam("Destination"));
|
||||||
final ZoneType origin = ZoneType.listValueOf(sa.getParam("Origin")).get(0);
|
final ZoneType origin = ZoneType.listValueOf(sa.getParam("Origin")).get(0);
|
||||||
|
|
||||||
if (abCost != null) {
|
if (abCost != null) {
|
||||||
// AI currently disabled for these costs
|
// AI currently disabled for these costs
|
||||||
if (!ComputerUtilCost.checkLifeCost(ai, abCost, source, 4, sa)) {
|
if (!ComputerUtilCost.checkLifeCost(ai, abCost, source, 4, sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ComputerUtilCost.checkDiscardCost(ai, abCost, source)) {
|
if (!ComputerUtilCost.checkDiscardCost(ai, abCost, source)) {
|
||||||
boolean aiLogicAllowsDiscard = sa.hasParam("AILogic") && sa.getParam("AILogic").startsWith("DiscardAll");
|
boolean aiLogicAllowsDiscard = sa.hasParam("AILogic") && sa.getParam("AILogic").startsWith("DiscardAll");
|
||||||
|
|
||||||
if (!aiLogicAllowsDiscard) {
|
if (!aiLogicAllowsDiscard) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final Random r = MyRandom.getRandom();
|
final Random r = MyRandom.getRandom();
|
||||||
// prevent run-away activations - first time will always return true
|
// prevent run-away activations - first time will always return true
|
||||||
boolean chance = r.nextFloat() <= Math.pow(.6667, sa.getActivationsThisTurn());
|
boolean chance = r.nextFloat() <= Math.pow(.6667, sa.getActivationsThisTurn());
|
||||||
|
|
||||||
// TODO targeting with ChangeZoneAll
|
// TODO targeting with ChangeZoneAll
|
||||||
// really two types of targeting.
|
// really two types of targeting.
|
||||||
// Target Player has all their types change zones
|
// Target Player has all their types change zones
|
||||||
// or target permanent and do something relative to that permanent
|
// or target permanent and do something relative to that permanent
|
||||||
// ex. "Return all Auras attached to target"
|
// ex. "Return all Auras attached to target"
|
||||||
// ex. "Return all blocking/blocked by target creature"
|
// ex. "Return all blocking/blocked by target creature"
|
||||||
|
|
||||||
CardCollectionView oppType = CardLists.filterControlledBy(game.getCardsIn(origin), ai.getOpponents());
|
CardCollectionView oppType = CardLists.filterControlledBy(game.getCardsIn(origin), ai.getOpponents());
|
||||||
CardCollectionView computerType = ai.getCardsIn(origin);
|
CardCollectionView computerType = ai.getCardsIn(origin);
|
||||||
|
|
||||||
// Ugin check need to be done before filterListByType because of ChosenX
|
// Ugin check need to be done before filterListByType because of ChosenX
|
||||||
// Ugin AI: always try to sweep before considering +1
|
// Ugin AI: always try to sweep before considering +1
|
||||||
if (sourceName.equals("Ugin, the Spirit Dragon")) {
|
if (sourceName.equals("Ugin, the Spirit Dragon")) {
|
||||||
return SpecialCardAi.UginTheSpiritDragon.considerPWAbilityPriority(ai, sa, origin, oppType, computerType);
|
return SpecialCardAi.UginTheSpiritDragon.considerPWAbilityPriority(ai, sa, origin, oppType, computerType);
|
||||||
}
|
}
|
||||||
|
|
||||||
oppType = AbilityUtils.filterListByType(oppType, sa.getParam("ChangeType"), sa);
|
oppType = AbilityUtils.filterListByType(oppType, sa.getParam("ChangeType"), sa);
|
||||||
computerType = AbilityUtils.filterListByType(computerType, sa.getParam("ChangeType"), sa);
|
computerType = AbilityUtils.filterListByType(computerType, sa.getParam("ChangeType"), sa);
|
||||||
|
|
||||||
if ("LivingDeath".equals(sa.getParam("AILogic"))) {
|
if ("LivingDeath".equals(sa.getParam("AILogic"))) {
|
||||||
// Living Death AI
|
// Living Death AI
|
||||||
return SpecialCardAi.LivingDeath.consider(ai, sa);
|
return SpecialCardAi.LivingDeath.consider(ai, sa);
|
||||||
} else if ("Timetwister".equals(sa.getParam("AILogic"))) {
|
} else if ("Timetwister".equals(sa.getParam("AILogic"))) {
|
||||||
// Timetwister AI
|
// Timetwister AI
|
||||||
return SpecialCardAi.Timetwister.consider(ai, sa);
|
return SpecialCardAi.Timetwister.consider(ai, sa);
|
||||||
} else if ("RetDiscardedThisTurn".equals(sa.getParam("AILogic"))) {
|
} else if ("RetDiscardedThisTurn".equals(sa.getParam("AILogic"))) {
|
||||||
// e.g. Shadow of the Grave
|
// e.g. Shadow of the Grave
|
||||||
return ai.getNumDiscardedThisTurn() > 0 && ai.getGame().getPhaseHandler().is(PhaseType.END_OF_TURN);
|
return ai.getNumDiscardedThisTurn() > 0 && ai.getGame().getPhaseHandler().is(PhaseType.END_OF_TURN);
|
||||||
} else if ("ExileGraveyards".equals(sa.getParam("AILogic"))) {
|
} else if ("ExileGraveyards".equals(sa.getParam("AILogic"))) {
|
||||||
for (Player opp : ai.getOpponents()) {
|
for (Player opp : ai.getOpponents()) {
|
||||||
CardCollectionView cardsGY = opp.getCardsIn(ZoneType.Graveyard);
|
CardCollectionView cardsGY = opp.getCardsIn(ZoneType.Graveyard);
|
||||||
CardCollection creats = CardLists.filter(cardsGY, CardPredicates.Presets.CREATURES);
|
CardCollection creats = CardLists.filter(cardsGY, CardPredicates.Presets.CREATURES);
|
||||||
|
|
||||||
if (opp.hasDelirium() || opp.hasThreshold() || creats.size() >= 5) {
|
if (opp.hasDelirium() || opp.hasThreshold() || creats.size() >= 5) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
} else if ("ManifestCreatsFromGraveyard".equals(sa.getParam("AILogic"))) {
|
} else if ("ManifestCreatsFromGraveyard".equals(sa.getParam("AILogic"))) {
|
||||||
PlayerCollection players = new PlayerCollection();
|
PlayerCollection players = new PlayerCollection();
|
||||||
players.addAll(ai.getOpponents());
|
players.addAll(ai.getOpponents());
|
||||||
players.add(ai);
|
players.add(ai);
|
||||||
int maxSize = 1;
|
int maxSize = 1;
|
||||||
for (Player player : players) {
|
for (Player player : players) {
|
||||||
Player bestTgt = null;
|
Player bestTgt = null;
|
||||||
if (player.canBeTargetedBy(sa)) {
|
if (player.canBeTargetedBy(sa)) {
|
||||||
CardCollectionView cardsGY = CardLists.filter(player.getCardsIn(ZoneType.Graveyard),
|
CardCollectionView cardsGY = CardLists.filter(player.getCardsIn(ZoneType.Graveyard),
|
||||||
CardPredicates.Presets.CREATURES);
|
CardPredicates.Presets.CREATURES);
|
||||||
if (cardsGY.size() > maxSize) {
|
if (cardsGY.size() > maxSize) {
|
||||||
maxSize = cardsGY.size();
|
maxSize = cardsGY.size();
|
||||||
bestTgt = player;
|
bestTgt = player;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bestTgt != null) {
|
if (bestTgt != null) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
sa.getTargets().add(bestTgt);
|
sa.getTargets().add(bestTgt);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO improve restrictions on when the AI would want to use this
|
// TODO improve restrictions on when the AI would want to use this
|
||||||
// spBounceAll has some AI we can compare to.
|
// spBounceAll has some AI we can compare to.
|
||||||
if (origin.equals(ZoneType.Hand) || origin.equals(ZoneType.Library)) {
|
if (origin.equals(ZoneType.Hand) || origin.equals(ZoneType.Library)) {
|
||||||
if (!sa.usesTargeting()) {
|
if (!sa.usesTargeting()) {
|
||||||
// TODO: improve logic for non-targeted SAs of this type (most are currently RemAIDeck, e.g. Memory Jar)
|
// TODO: improve logic for non-targeted SAs of this type (most are currently RemAIDeck, e.g. Memory Jar)
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
// search targetable Opponents
|
// search targetable Opponents
|
||||||
final Iterable<Player> oppList = Iterables.filter(ai.getOpponents(), PlayerPredicates.isTargetableBy(sa));
|
final Iterable<Player> oppList = Iterables.filter(ai.getOpponents(), PlayerPredicates.isTargetableBy(sa));
|
||||||
|
|
||||||
// get the one with the most handsize
|
// get the one with the most handsize
|
||||||
Player oppTarget = Collections.max(Lists.newArrayList(oppList), PlayerPredicates.compareByZoneSize(origin));
|
Player oppTarget = Collections.max(Lists.newArrayList(oppList), PlayerPredicates.compareByZoneSize(origin));
|
||||||
|
|
||||||
// set the target
|
// set the target
|
||||||
if (oppTarget != null && !oppTarget.getCardsIn(ZoneType.Hand).isEmpty()) {
|
if (oppTarget != null && !oppTarget.getCardsIn(ZoneType.Hand).isEmpty()) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
sa.getTargets().add(oppTarget);
|
sa.getTargets().add(oppTarget);
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (origin.equals(ZoneType.Battlefield)) {
|
} else if (origin.equals(ZoneType.Battlefield)) {
|
||||||
// this statement is assuming the AI is trying to use this spell
|
// this statement is assuming the AI is trying to use this spell
|
||||||
// offensively
|
// offensively
|
||||||
// if the AI is using it defensively, then something else needs to
|
// if the AI is using it defensively, then something else needs to
|
||||||
// occur
|
// occur
|
||||||
// if only creatures are affected evaluate both lists and pass only
|
// if only creatures are affected evaluate both lists and pass only
|
||||||
// if human creatures are more valuable
|
// if human creatures are more valuable
|
||||||
if (sa.usesTargeting()) {
|
if (sa.usesTargeting()) {
|
||||||
// search targetable Opponents
|
// search targetable Opponents
|
||||||
final Iterable<Player> oppList = Iterables.filter(ai.getOpponents(),
|
final Iterable<Player> oppList = Iterables.filter(ai.getOpponents(),
|
||||||
PlayerPredicates.isTargetableBy(sa));
|
PlayerPredicates.isTargetableBy(sa));
|
||||||
|
|
||||||
// get the one with the most in graveyard
|
// get the one with the most in graveyard
|
||||||
// zone is visible so evaluate which would be hurt the most
|
// zone is visible so evaluate which would be hurt the most
|
||||||
Player oppTarget = Collections.max(Lists.newArrayList(oppList),
|
Player oppTarget = Collections.max(Lists.newArrayList(oppList),
|
||||||
PlayerPredicates.compareByZoneSize(origin));
|
PlayerPredicates.compareByZoneSize(origin));
|
||||||
|
|
||||||
// set the target
|
// set the target
|
||||||
if (oppTarget != null && !oppTarget.getCardsIn(ZoneType.Graveyard).isEmpty()) {
|
if (oppTarget != null && !oppTarget.getCardsIn(ZoneType.Graveyard).isEmpty()) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
sa.getTargets().add(oppTarget);
|
sa.getTargets().add(oppTarget);
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
computerType = new CardCollection();
|
computerType = new CardCollection();
|
||||||
}
|
}
|
||||||
|
|
||||||
int creatureEvalThreshold = 200; // value difference (in evaluateCreatureList units)
|
int creatureEvalThreshold = 200; // value difference (in evaluateCreatureList units)
|
||||||
int nonCreatureEvalThreshold = 3; // CMC difference
|
int nonCreatureEvalThreshold = 3; // CMC difference
|
||||||
if (ai.getController().isAI()) {
|
if (ai.getController().isAI()) {
|
||||||
AiController aic = ((PlayerControllerAi)ai.getController()).getAi();
|
AiController aic = ((PlayerControllerAi)ai.getController()).getAi();
|
||||||
if (destination == ZoneType.Hand) {
|
if (destination == ZoneType.Hand) {
|
||||||
creatureEvalThreshold = aic.getIntProperty(AiProps.BOUNCE_ALL_TO_HAND_CREAT_EVAL_DIFF);
|
creatureEvalThreshold = aic.getIntProperty(AiProps.BOUNCE_ALL_TO_HAND_CREAT_EVAL_DIFF);
|
||||||
nonCreatureEvalThreshold = aic.getIntProperty(AiProps.BOUNCE_ALL_TO_HAND_NONCREAT_EVAL_DIFF);
|
nonCreatureEvalThreshold = aic.getIntProperty(AiProps.BOUNCE_ALL_TO_HAND_NONCREAT_EVAL_DIFF);
|
||||||
} else {
|
} else {
|
||||||
creatureEvalThreshold = aic.getIntProperty(AiProps.BOUNCE_ALL_ELSEWHERE_CREAT_EVAL_DIFF);
|
creatureEvalThreshold = aic.getIntProperty(AiProps.BOUNCE_ALL_ELSEWHERE_CREAT_EVAL_DIFF);
|
||||||
nonCreatureEvalThreshold = aic.getIntProperty(AiProps.BOUNCE_ALL_ELSEWHERE_NONCREAT_EVAL_DIFF);
|
nonCreatureEvalThreshold = aic.getIntProperty(AiProps.BOUNCE_ALL_ELSEWHERE_NONCREAT_EVAL_DIFF);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// mass zone change for creatures: if in dire danger, do it; otherwise, only do it if the opponent's
|
// mass zone change for creatures: if in dire danger, do it; otherwise, only do it if the opponent's
|
||||||
// creatures are better in value
|
// creatures are better in value
|
||||||
if ((CardLists.getNotType(oppType, "Creature").size() == 0)
|
if ((CardLists.getNotType(oppType, "Creature").size() == 0)
|
||||||
&& (CardLists.getNotType(computerType, "Creature").size() == 0)) {
|
&& (CardLists.getNotType(computerType, "Creature").size() == 0)) {
|
||||||
if (game.getCombat() != null && ComputerUtilCombat.lifeInSeriousDanger(ai, game.getCombat())) {
|
if (game.getCombat() != null && ComputerUtilCombat.lifeInSeriousDanger(ai, game.getCombat())) {
|
||||||
if (game.getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS)
|
if (game.getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS)
|
||||||
&& game.getPhaseHandler().getPlayerTurn().isOpponentOf(ai)) {
|
&& game.getPhaseHandler().getPlayerTurn().isOpponentOf(ai)) {
|
||||||
// Life is in serious danger, return all creatures from the battlefield to wherever
|
// Life is in serious danger, return all creatures from the battlefield to wherever
|
||||||
// so they don't deal lethal damage
|
// so they don't deal lethal damage
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((ComputerUtilCard.evaluateCreatureList(computerType) + creatureEvalThreshold) >= ComputerUtilCard
|
if ((ComputerUtilCard.evaluateCreatureList(computerType) + creatureEvalThreshold) >= ComputerUtilCard
|
||||||
.evaluateCreatureList(oppType)) {
|
.evaluateCreatureList(oppType)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} // mass zone change for non-creatures: evaluate both lists by CMC and pass only if human
|
} // mass zone change for non-creatures: evaluate both lists by CMC and pass only if human
|
||||||
// permanents are more valuable
|
// permanents are more valuable
|
||||||
else if ((ComputerUtilCard.evaluatePermanentList(computerType) + nonCreatureEvalThreshold) >= ComputerUtilCard
|
else if ((ComputerUtilCard.evaluatePermanentList(computerType) + nonCreatureEvalThreshold) >= ComputerUtilCard
|
||||||
.evaluatePermanentList(oppType)) {
|
.evaluatePermanentList(oppType)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't cast during main1?
|
// Don't cast during main1?
|
||||||
if (game.getPhaseHandler().is(PhaseType.MAIN1, ai)) {
|
if (game.getPhaseHandler().is(PhaseType.MAIN1, ai)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (origin.equals(ZoneType.Graveyard)) {
|
} else if (origin.equals(ZoneType.Graveyard)) {
|
||||||
if (sa.usesTargeting()) {
|
if (sa.usesTargeting()) {
|
||||||
// search targetable Opponents
|
// search targetable Opponents
|
||||||
final Iterable<Player> oppList = Iterables.filter(ai.getOpponents(),
|
final Iterable<Player> oppList = Iterables.filter(ai.getOpponents(),
|
||||||
PlayerPredicates.isTargetableBy(sa));
|
PlayerPredicates.isTargetableBy(sa));
|
||||||
|
|
||||||
if (Iterables.isEmpty(oppList)) {
|
if (Iterables.isEmpty(oppList)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the one with the most in graveyard
|
// get the one with the most in graveyard
|
||||||
// zone is visible so evaluate which would be hurt the most
|
// zone is visible so evaluate which would be hurt the most
|
||||||
Player oppTarget = Collections.max(Lists.newArrayList(oppList),
|
Player oppTarget = Collections.max(Lists.newArrayList(oppList),
|
||||||
AiPlayerPredicates.compareByZoneValue(sa.getParam("ChangeType"), origin, sa));
|
AiPlayerPredicates.compareByZoneValue(sa.getParam("ChangeType"), origin, sa));
|
||||||
|
|
||||||
// set the target
|
// set the target
|
||||||
if (oppTarget != null && !oppTarget.getCardsIn(ZoneType.Graveyard).isEmpty()) {
|
if (oppTarget != null && !oppTarget.getCardsIn(ZoneType.Graveyard).isEmpty()) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
sa.getTargets().add(oppTarget);
|
sa.getTargets().add(oppTarget);
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (origin.equals(ZoneType.Exile)) {
|
} else if (origin.equals(ZoneType.Exile)) {
|
||||||
String logic = sa.getParam("AILogic");
|
String logic = sa.getParam("AILogic");
|
||||||
|
|
||||||
if (logic != null && logic.startsWith("DiscardAllAndRetExiled")) {
|
if (logic != null && logic.startsWith("DiscardAllAndRetExiled")) {
|
||||||
int numExiledWithSrc = CardLists.filter(ai.getCardsIn(ZoneType.Exile), CardPredicates.isExiledWith(source)).size();
|
int numExiledWithSrc = CardLists.filter(ai.getCardsIn(ZoneType.Exile), CardPredicates.isExiledWith(source)).size();
|
||||||
int curHandSize = ai.getCardsIn(ZoneType.Hand).size();
|
int curHandSize = ai.getCardsIn(ZoneType.Hand).size();
|
||||||
|
|
||||||
// minimum card advantage unless the hand will be fully reloaded
|
// minimum card advantage unless the hand will be fully reloaded
|
||||||
int minAdv = logic.contains(".minAdv") ? Integer.parseInt(logic.substring(logic.indexOf(".minAdv") + 7)) : 0;
|
int minAdv = logic.contains(".minAdv") ? Integer.parseInt(logic.substring(logic.indexOf(".minAdv") + 7)) : 0;
|
||||||
|
|
||||||
if (numExiledWithSrc > curHandSize) {
|
if (numExiledWithSrc > curHandSize) {
|
||||||
if (ComputerUtil.predictThreatenedObjects(ai, sa, true).contains(source)) {
|
if (ComputerUtil.predictThreatenedObjects(ai, sa, true).contains(source)) {
|
||||||
// Try to gain some card advantage if the card will die anyway
|
// Try to gain some card advantage if the card will die anyway
|
||||||
// TODO: ideally, should evaluate the hand value and not discard good hands to it
|
// TODO: ideally, should evaluate the hand value and not discard good hands to it
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (curHandSize + minAdv - 1 < numExiledWithSrc) || (numExiledWithSrc >= ai.getMaxHandSize());
|
return (curHandSize + minAdv - 1 < numExiledWithSrc) || (numExiledWithSrc >= ai.getMaxHandSize());
|
||||||
}
|
}
|
||||||
} else if (origin.equals(ZoneType.Stack)) {
|
} else if (origin.equals(ZoneType.Stack)) {
|
||||||
// time stop can do something like this:
|
// time stop can do something like this:
|
||||||
// Origin$ Stack | Destination$ Exile | SubAbility$ DBSkip
|
// Origin$ Stack | Destination$ Exile | SubAbility$ DBSkip
|
||||||
// DBSKipToPhase | DB$SkipToPhase | Phase$ Cleanup
|
// DBSKipToPhase | DB$SkipToPhase | Phase$ Cleanup
|
||||||
// otherwise, this situation doesn't exist
|
// otherwise, this situation doesn't exist
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (destination.equals(ZoneType.Battlefield)) {
|
if (destination.equals(ZoneType.Battlefield)) {
|
||||||
if (sa.getParam("GainControl") != null) {
|
if (sa.getParam("GainControl") != null) {
|
||||||
// Check if the cards are valuable enough
|
// Check if the cards are valuable enough
|
||||||
if ((CardLists.getNotType(oppType, "Creature").size() == 0)
|
if ((CardLists.getNotType(oppType, "Creature").size() == 0)
|
||||||
&& (CardLists.getNotType(computerType, "Creature").size() == 0)) {
|
&& (CardLists.getNotType(computerType, "Creature").size() == 0)) {
|
||||||
if ((ComputerUtilCard.evaluateCreatureList(computerType) + ComputerUtilCard
|
if ((ComputerUtilCard.evaluateCreatureList(computerType) + ComputerUtilCard
|
||||||
.evaluateCreatureList(oppType)) < 400) {
|
.evaluateCreatureList(oppType)) < 400) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} // otherwise evaluate both lists by CMC and pass only if human
|
} // otherwise evaluate both lists by CMC and pass only if human
|
||||||
// permanents are less valuable
|
// permanents are less valuable
|
||||||
else if ((ComputerUtilCard.evaluatePermanentList(computerType) + ComputerUtilCard
|
else if ((ComputerUtilCard.evaluatePermanentList(computerType) + ComputerUtilCard
|
||||||
.evaluatePermanentList(oppType)) < 6) {
|
.evaluatePermanentList(oppType)) < 6) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// don't activate if human gets more back than AI does
|
// don't activate if human gets more back than AI does
|
||||||
if ((CardLists.getNotType(oppType, "Creature").size() == 0)
|
if ((CardLists.getNotType(oppType, "Creature").size() == 0)
|
||||||
&& (CardLists.getNotType(computerType, "Creature").size() == 0)) {
|
&& (CardLists.getNotType(computerType, "Creature").size() == 0)) {
|
||||||
if (ComputerUtilCard.evaluateCreatureList(computerType) <= (ComputerUtilCard
|
if (ComputerUtilCard.evaluateCreatureList(computerType) <= (ComputerUtilCard
|
||||||
.evaluateCreatureList(oppType) + 100)) {
|
.evaluateCreatureList(oppType) + 100)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} // otherwise evaluate both lists by CMC and pass only if human
|
} // otherwise evaluate both lists by CMC and pass only if human
|
||||||
// permanents are less valuable
|
// permanents are less valuable
|
||||||
else if (ComputerUtilCard.evaluatePermanentList(computerType) <= (ComputerUtilCard
|
else if (ComputerUtilCard.evaluatePermanentList(computerType) <= (ComputerUtilCard
|
||||||
.evaluatePermanentList(oppType) + 2)) {
|
.evaluatePermanentList(oppType) + 2)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (((r.nextFloat() < .8) || sa.isTrigger()) && chance);
|
return (((r.nextFloat() < .8) || sa.isTrigger()) && chance);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* changeZoneAllPlayDrawbackAI.
|
* changeZoneAllPlayDrawbackAI.
|
||||||
* </p>
|
* </p>
|
||||||
* @param sa
|
* @param sa
|
||||||
* a {@link forge.game.spellability.SpellAbility} object.
|
* a {@link forge.game.spellability.SpellAbility} object.
|
||||||
* @param aiPlayer
|
* @param aiPlayer
|
||||||
* a {@link forge.game.player.Player} object.
|
* a {@link forge.game.player.Player} object.
|
||||||
*
|
*
|
||||||
* @return a boolean.
|
* @return a boolean.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean chkAIDrawback(SpellAbility sa, Player aiPlayer) {
|
public boolean chkAIDrawback(SpellAbility sa, Player aiPlayer) {
|
||||||
// if putting cards from hand to library and parent is drawing cards
|
// if putting cards from hand to library and parent is drawing cards
|
||||||
// make sure this will actually do something:
|
// make sure this will actually do something:
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.game.player.Player, forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.game.player.Player, forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player ai, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player ai, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
final String hostName = source.getName();
|
final String hostName = source.getName();
|
||||||
final ZoneType origin = ZoneType.listValueOf(sa.getParam("Origin")).get(0);
|
final ZoneType origin = ZoneType.listValueOf(sa.getParam("Origin")).get(0);
|
||||||
|
|
||||||
if (hostName.equals("Dawnbreak Reclaimer")) {
|
if (hostName.equals("Dawnbreak Reclaimer")) {
|
||||||
final CardCollectionView cards = AbilityUtils.filterListByType(ai.getGame().getCardsIn(origin), sa.getParam("ChangeType"), sa);
|
final CardCollectionView cards = AbilityUtils.filterListByType(ai.getGame().getCardsIn(origin), sa.getParam("ChangeType"), sa);
|
||||||
|
|
||||||
// AI gets nothing
|
// AI gets nothing
|
||||||
final CardCollection aiCards = CardLists.filterControlledBy(cards, ai);
|
final CardCollection aiCards = CardLists.filterControlledBy(cards, ai);
|
||||||
if (aiCards.isEmpty())
|
if (aiCards.isEmpty())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Human gets nothing
|
// Human gets nothing
|
||||||
final CardCollection humanCards = CardLists.filterControlledBy(cards, ai.getOpponents());
|
final CardCollection humanCards = CardLists.filterControlledBy(cards, ai.getOpponents());
|
||||||
if (humanCards.isEmpty())
|
if (humanCards.isEmpty())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// if AI creature is better than Human Creature
|
// if AI creature is better than Human Creature
|
||||||
if (ComputerUtilCard.evaluateCreatureList(aiCards) >= ComputerUtilCard
|
if (ComputerUtilCard.evaluateCreatureList(aiCards) >= ComputerUtilCard
|
||||||
.evaluateCreatureList(humanCards)) {
|
.evaluateCreatureList(humanCards)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player ai, final SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player ai, final SpellAbility sa, boolean mandatory) {
|
||||||
// Change Zone All, can be any type moving from one zone to another
|
// Change Zone All, can be any type moving from one zone to another
|
||||||
|
|
||||||
final ZoneType destination = ZoneType.smartValueOf(sa.getParam("Destination"));
|
final ZoneType destination = ZoneType.smartValueOf(sa.getParam("Destination"));
|
||||||
final ZoneType origin = ZoneType.listValueOf(sa.getParam("Origin")).get(0);
|
final ZoneType origin = ZoneType.listValueOf(sa.getParam("Origin")).get(0);
|
||||||
|
|
||||||
if (ComputerUtilAbility.getAbilitySourceName(sa).equals("Profaner of the Dead")) {
|
if (ComputerUtilAbility.getAbilitySourceName(sa).equals("Profaner of the Dead")) {
|
||||||
// TODO: this is a stub to prevent the AI from crashing the game when, for instance, playing the opponent's
|
// TODO: this is a stub to prevent the AI from crashing the game when, for instance, playing the opponent's
|
||||||
// Profaner from exile without paying its mana cost. Otherwise the card is marked RemAIDeck and there is no
|
// Profaner from exile without paying its mana cost. Otherwise the card is marked RemAIDeck and there is no
|
||||||
// specific AI to support playing it in a smarter way. Feel free to expand.
|
// specific AI to support playing it in a smarter way. Feel free to expand.
|
||||||
return !CardLists.filter(ai.getOpponents().getCardsIn(origin), CardPredicates.Presets.CREATURES).isEmpty();
|
return !CardLists.filter(ai.getOpponents().getCardsIn(origin), CardPredicates.Presets.CREATURES).isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
CardCollectionView humanType = CardLists.filterControlledBy(ai.getGame().getCardsIn(origin), ai.getOpponents());
|
CardCollectionView humanType = CardLists.filterControlledBy(ai.getGame().getCardsIn(origin), ai.getOpponents());
|
||||||
humanType = AbilityUtils.filterListByType(humanType, sa.getParam("ChangeType"), sa);
|
humanType = AbilityUtils.filterListByType(humanType, sa.getParam("ChangeType"), sa);
|
||||||
|
|
||||||
CardCollectionView computerType = ai.getCardsIn(origin);
|
CardCollectionView computerType = ai.getCardsIn(origin);
|
||||||
computerType = AbilityUtils.filterListByType(computerType, sa.getParam("ChangeType"), sa);
|
computerType = AbilityUtils.filterListByType(computerType, sa.getParam("ChangeType"), sa);
|
||||||
|
|
||||||
// TODO improve restrictions on when the AI would want to use this
|
// TODO improve restrictions on when the AI would want to use this
|
||||||
// spBounceAll has some AI we can compare to.
|
// spBounceAll has some AI we can compare to.
|
||||||
if (origin.equals(ZoneType.Hand) || origin.equals(ZoneType.Library)) {
|
if (origin.equals(ZoneType.Hand) || origin.equals(ZoneType.Library)) {
|
||||||
if (sa.usesTargeting()) {
|
if (sa.usesTargeting()) {
|
||||||
// search targetable Opponents
|
// search targetable Opponents
|
||||||
final Iterable<Player> oppList = Iterables.filter(ai.getOpponents(),
|
final Iterable<Player> oppList = Iterables.filter(ai.getOpponents(),
|
||||||
PlayerPredicates.isTargetableBy(sa));
|
PlayerPredicates.isTargetableBy(sa));
|
||||||
|
|
||||||
// get the one with the most handsize
|
// get the one with the most handsize
|
||||||
Player oppTarget = Collections.max(Lists.newArrayList(oppList),
|
Player oppTarget = Collections.max(Lists.newArrayList(oppList),
|
||||||
PlayerPredicates.compareByZoneSize(origin));
|
PlayerPredicates.compareByZoneSize(origin));
|
||||||
|
|
||||||
// set the target
|
// set the target
|
||||||
if (oppTarget != null && !oppTarget.getCardsIn(ZoneType.Hand).isEmpty()) {
|
if (oppTarget != null && !oppTarget.getCardsIn(ZoneType.Hand).isEmpty()) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
sa.getTargets().add(oppTarget);
|
sa.getTargets().add(oppTarget);
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (origin.equals(ZoneType.Battlefield)) {
|
} else if (origin.equals(ZoneType.Battlefield)) {
|
||||||
// if mandatory, no need to evaluate
|
// if mandatory, no need to evaluate
|
||||||
if (mandatory) {
|
if (mandatory) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// this statement is assuming the AI is trying to use this spell offensively
|
// this statement is assuming the AI is trying to use this spell offensively
|
||||||
// if the AI is using it defensively, then something else needs to occur
|
// if the AI is using it defensively, then something else needs to occur
|
||||||
// if only creatures are affected evaluate both lists and pass only
|
// if only creatures are affected evaluate both lists and pass only
|
||||||
// if human creatures are more valuable
|
// if human creatures are more valuable
|
||||||
if ((CardLists.getNotType(humanType, "Creature").isEmpty()) && (CardLists.getNotType(computerType, "Creature").isEmpty())) {
|
if ((CardLists.getNotType(humanType, "Creature").isEmpty()) && (CardLists.getNotType(computerType, "Creature").isEmpty())) {
|
||||||
if (ComputerUtilCard.evaluateCreatureList(computerType) >= ComputerUtilCard
|
if (ComputerUtilCard.evaluateCreatureList(computerType) >= ComputerUtilCard
|
||||||
.evaluateCreatureList(humanType)) {
|
.evaluateCreatureList(humanType)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} // otherwise evaluate both lists by CMC and pass only if human
|
} // otherwise evaluate both lists by CMC and pass only if human
|
||||||
// permanents are more valuable
|
// permanents are more valuable
|
||||||
else if (ComputerUtilCard.evaluatePermanentList(computerType) >= ComputerUtilCard
|
else if (ComputerUtilCard.evaluatePermanentList(computerType) >= ComputerUtilCard
|
||||||
.evaluatePermanentList(humanType)) {
|
.evaluatePermanentList(humanType)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (origin.equals(ZoneType.Graveyard)) {
|
} else if (origin.equals(ZoneType.Graveyard)) {
|
||||||
if (sa.usesTargeting()) {
|
if (sa.usesTargeting()) {
|
||||||
// search targetable Opponents
|
// search targetable Opponents
|
||||||
final Iterable<Player> oppList = Iterables.filter(ai.getOpponents(),
|
final Iterable<Player> oppList = Iterables.filter(ai.getOpponents(),
|
||||||
PlayerPredicates.isTargetableBy(sa));
|
PlayerPredicates.isTargetableBy(sa));
|
||||||
|
|
||||||
// get the one with the most in graveyard
|
// get the one with the most in graveyard
|
||||||
// zone is visible so evaluate which would be hurt the most
|
// zone is visible so evaluate which would be hurt the most
|
||||||
Player oppTarget = Collections.max(Lists.newArrayList(oppList),
|
Player oppTarget = Collections.max(Lists.newArrayList(oppList),
|
||||||
AiPlayerPredicates.compareByZoneValue(sa.getParam("ChangeType"), origin, sa));
|
AiPlayerPredicates.compareByZoneValue(sa.getParam("ChangeType"), origin, sa));
|
||||||
|
|
||||||
// set the target
|
// set the target
|
||||||
if (oppTarget != null && !oppTarget.getCardsIn(ZoneType.Graveyard).isEmpty()) {
|
if (oppTarget != null && !oppTarget.getCardsIn(ZoneType.Graveyard).isEmpty()) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
sa.getTargets().add(oppTarget);
|
sa.getTargets().add(oppTarget);
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (origin.equals(ZoneType.Exile)) {
|
} else if (origin.equals(ZoneType.Exile)) {
|
||||||
|
|
||||||
} else if (origin.equals(ZoneType.Stack)) {
|
} else if (origin.equals(ZoneType.Stack)) {
|
||||||
// time stop can do something like this:
|
// time stop can do something like this:
|
||||||
// Origin$ Stack | Destination$ Exile | SubAbility$ DBSkip
|
// Origin$ Stack | Destination$ Exile | SubAbility$ DBSkip
|
||||||
// DBSKipToPhase | DB$SkipToPhase | Phase$ Cleanup
|
// DBSKipToPhase | DB$SkipToPhase | Phase$ Cleanup
|
||||||
// otherwise, this situation doesn't exist
|
// otherwise, this situation doesn't exist
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (destination.equals(ZoneType.Battlefield)) {
|
if (destination.equals(ZoneType.Battlefield)) {
|
||||||
// if mandatory, no need to evaluate
|
// if mandatory, no need to evaluate
|
||||||
if (mandatory) {
|
if (mandatory) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (sa.getParam("GainControl") != null) {
|
if (sa.getParam("GainControl") != null) {
|
||||||
// Check if the cards are valuable enough
|
// Check if the cards are valuable enough
|
||||||
if ((CardLists.getNotType(humanType, "Creature").size() == 0) && (CardLists.getNotType(computerType, "Creature").size() == 0)) {
|
if ((CardLists.getNotType(humanType, "Creature").size() == 0) && (CardLists.getNotType(computerType, "Creature").size() == 0)) {
|
||||||
if ((ComputerUtilCard.evaluateCreatureList(computerType) + ComputerUtilCard
|
if ((ComputerUtilCard.evaluateCreatureList(computerType) + ComputerUtilCard
|
||||||
.evaluateCreatureList(humanType)) < 1) {
|
.evaluateCreatureList(humanType)) < 1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} // otherwise evaluate both lists by CMC and pass only if human
|
} // otherwise evaluate both lists by CMC and pass only if human
|
||||||
// permanents are less valuable
|
// permanents are less valuable
|
||||||
else if ((ComputerUtilCard.evaluatePermanentList(computerType) + ComputerUtilCard
|
else if ((ComputerUtilCard.evaluatePermanentList(computerType) + ComputerUtilCard
|
||||||
.evaluatePermanentList(humanType)) < 1) {
|
.evaluatePermanentList(humanType)) < 1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// don't activate if human gets more back than AI does
|
// don't activate if human gets more back than AI does
|
||||||
if ((CardLists.getNotType(humanType, "Creature").isEmpty()) && (CardLists.getNotType(computerType, "Creature").isEmpty())) {
|
if ((CardLists.getNotType(humanType, "Creature").isEmpty()) && (CardLists.getNotType(computerType, "Creature").isEmpty())) {
|
||||||
if (ComputerUtilCard.evaluateCreatureList(computerType) <= ComputerUtilCard
|
if (ComputerUtilCard.evaluateCreatureList(computerType) <= ComputerUtilCard
|
||||||
.evaluateCreatureList(humanType)) {
|
.evaluateCreatureList(humanType)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} // otherwise evaluate both lists by CMC and pass only if human
|
} // otherwise evaluate both lists by CMC and pass only if human
|
||||||
// permanents are less valuable
|
// permanents are less valuable
|
||||||
else if (ComputerUtilCard.evaluatePermanentList(computerType) <= ComputerUtilCard
|
else if (ComputerUtilCard.evaluatePermanentList(computerType) <= ComputerUtilCard
|
||||||
.evaluatePermanentList(humanType)) {
|
.evaluatePermanentList(humanType)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,241 +1,241 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import forge.ai.*;
|
import forge.ai.*;
|
||||||
import forge.game.ability.effects.CharmEffect;
|
import forge.game.ability.effects.CharmEffect;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.AbilitySub;
|
import forge.game.spellability.AbilitySub;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.util.Aggregates;
|
import forge.util.Aggregates;
|
||||||
import forge.util.MyRandom;
|
import forge.util.MyRandom;
|
||||||
import forge.util.collect.FCollection;
|
import forge.util.collect.FCollection;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
public class CharmAi extends SpellAbilityAi {
|
public class CharmAi extends SpellAbilityAi {
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkApiLogic(Player ai, SpellAbility sa) {
|
protected boolean checkApiLogic(Player ai, SpellAbility sa) {
|
||||||
// sa is Entwined, no need for extra logic
|
// sa is Entwined, no need for extra logic
|
||||||
if (sa.isEntwine()) {
|
if (sa.isEntwine()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Random r = MyRandom.getRandom();
|
final Random r = MyRandom.getRandom();
|
||||||
|
|
||||||
final int num = Integer.parseInt(sa.hasParam("CharmNum") ? sa.getParam("CharmNum") : "1");
|
final int num = Integer.parseInt(sa.hasParam("CharmNum") ? sa.getParam("CharmNum") : "1");
|
||||||
final int min = sa.hasParam("MinCharmNum") ? Integer.parseInt(sa.getParam("MinCharmNum")) : num;
|
final int min = sa.hasParam("MinCharmNum") ? Integer.parseInt(sa.getParam("MinCharmNum")) : num;
|
||||||
boolean timingRight = sa.isTrigger(); //is there a reason to play the charm now?
|
boolean timingRight = sa.isTrigger(); //is there a reason to play the charm now?
|
||||||
|
|
||||||
// Reset the chosen list otherwise it will be locked in forever by earlier calls
|
// Reset the chosen list otherwise it will be locked in forever by earlier calls
|
||||||
sa.setChosenList(null);
|
sa.setChosenList(null);
|
||||||
List<AbilitySub> choices = CharmEffect.makePossibleOptions(sa);
|
List<AbilitySub> choices = CharmEffect.makePossibleOptions(sa);
|
||||||
List<AbilitySub> chosenList;
|
List<AbilitySub> chosenList;
|
||||||
|
|
||||||
if (!ai.equals(sa.getActivatingPlayer())) {
|
if (!ai.equals(sa.getActivatingPlayer())) {
|
||||||
// This branch is for "An Opponent chooses" Charm spells from Alliances
|
// This branch is for "An Opponent chooses" Charm spells from Alliances
|
||||||
// Current just choose the first available spell, which seem generally less disastrous for the AI.
|
// Current just choose the first available spell, which seem generally less disastrous for the AI.
|
||||||
//return choices.subList(0, 1);
|
//return choices.subList(0, 1);
|
||||||
chosenList = choices.subList(1, choices.size());
|
chosenList = choices.subList(1, choices.size());
|
||||||
} else if ("Triskaidekaphobia".equals(ComputerUtilAbility.getAbilitySourceName(sa))) {
|
} else if ("Triskaidekaphobia".equals(ComputerUtilAbility.getAbilitySourceName(sa))) {
|
||||||
chosenList = chooseTriskaidekaphobia(choices, ai);
|
chosenList = chooseTriskaidekaphobia(choices, ai);
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
* The generic chooseOptionsAi uses canPlayAi() to determine good choices
|
* The generic chooseOptionsAi uses canPlayAi() to determine good choices
|
||||||
* which means most "bonus" effects like life-gain and random pumps will
|
* which means most "bonus" effects like life-gain and random pumps will
|
||||||
* usually not be chosen. This is designed to force the AI to only select
|
* usually not be chosen. This is designed to force the AI to only select
|
||||||
* the best choice(s) since it does not actually know if it can pay for
|
* the best choice(s) since it does not actually know if it can pay for
|
||||||
* "bonus" choices (eg. Entwine/Escalate).
|
* "bonus" choices (eg. Entwine/Escalate).
|
||||||
* chooseMultipleOptionsAi() uses "AILogic$Good" tags to manually identify
|
* chooseMultipleOptionsAi() uses "AILogic$Good" tags to manually identify
|
||||||
* bonus choice(s) for the AI otherwise it might be too hard to ever fulfil
|
* bonus choice(s) for the AI otherwise it might be too hard to ever fulfil
|
||||||
* minimum choice requirements with canPlayAi() alone.
|
* minimum choice requirements with canPlayAi() alone.
|
||||||
*/
|
*/
|
||||||
chosenList = min > 1 ? chooseMultipleOptionsAi(choices, ai, min)
|
chosenList = min > 1 ? chooseMultipleOptionsAi(choices, ai, min)
|
||||||
: chooseOptionsAi(choices, ai, timingRight, num, min, sa.hasParam("CanRepeatModes"));
|
: chooseOptionsAi(choices, ai, timingRight, num, min, sa.hasParam("CanRepeatModes"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chosenList.isEmpty()) {
|
if (chosenList.isEmpty()) {
|
||||||
if (timingRight) {
|
if (timingRight) {
|
||||||
// Set minimum choices for triggers where chooseMultipleOptionsAi() returns null
|
// Set minimum choices for triggers where chooseMultipleOptionsAi() returns null
|
||||||
chosenList = chooseOptionsAi(choices, ai, true, num, min, sa.hasParam("CanRepeatModes"));
|
chosenList = chooseOptionsAi(choices, ai, true, num, min, sa.hasParam("CanRepeatModes"));
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sa.setChosenList(chosenList);
|
sa.setChosenList(chosenList);
|
||||||
|
|
||||||
// prevent run-away activations - first time will always return true
|
// prevent run-away activations - first time will always return true
|
||||||
return r.nextFloat() <= Math.pow(.6667, sa.getActivationsThisTurn());
|
return r.nextFloat() <= Math.pow(.6667, sa.getActivationsThisTurn());
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<AbilitySub> chooseOptionsAi(List<AbilitySub> choices, final Player ai, boolean isTrigger, int num,
|
private List<AbilitySub> chooseOptionsAi(List<AbilitySub> choices, final Player ai, boolean isTrigger, int num,
|
||||||
int min, boolean allowRepeat) {
|
int min, boolean allowRepeat) {
|
||||||
List<AbilitySub> chosenList = Lists.newArrayList();
|
List<AbilitySub> chosenList = Lists.newArrayList();
|
||||||
AiController aic = ((PlayerControllerAi) ai.getController()).getAi();
|
AiController aic = ((PlayerControllerAi) ai.getController()).getAi();
|
||||||
// First pass using standard canPlayAi() for good choices
|
// First pass using standard canPlayAi() for good choices
|
||||||
for (AbilitySub sub : choices) {
|
for (AbilitySub sub : choices) {
|
||||||
sub.setActivatingPlayer(ai);
|
sub.setActivatingPlayer(ai);
|
||||||
sub.getRestrictions().setZone(sub.getParent().getRestrictions().getZone());
|
sub.getRestrictions().setZone(sub.getParent().getRestrictions().getZone());
|
||||||
if (AiPlayDecision.WillPlay == aic.canPlaySa(sub)) {
|
if (AiPlayDecision.WillPlay == aic.canPlaySa(sub)) {
|
||||||
chosenList.add(sub);
|
chosenList.add(sub);
|
||||||
if (chosenList.size() == num) {
|
if (chosenList.size() == num) {
|
||||||
return chosenList; // maximum choices reached
|
return chosenList; // maximum choices reached
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isTrigger && chosenList.size() < min) {
|
if (isTrigger && chosenList.size() < min) {
|
||||||
// Second pass using doTrigger(false) to fulfil minimum choice
|
// Second pass using doTrigger(false) to fulfil minimum choice
|
||||||
choices.removeAll(chosenList);
|
choices.removeAll(chosenList);
|
||||||
for (AbilitySub sub : choices) {
|
for (AbilitySub sub : choices) {
|
||||||
sub.setActivatingPlayer(ai);
|
sub.setActivatingPlayer(ai);
|
||||||
sub.getRestrictions().setZone(sub.getParent().getRestrictions().getZone());
|
sub.getRestrictions().setZone(sub.getParent().getRestrictions().getZone());
|
||||||
if (aic.doTrigger(sub, false)) {
|
if (aic.doTrigger(sub, false)) {
|
||||||
chosenList.add(sub);
|
chosenList.add(sub);
|
||||||
if (chosenList.size() == min) {
|
if (chosenList.size() == min) {
|
||||||
return chosenList;
|
return chosenList;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Third pass using doTrigger(true) to force fill minimum choices
|
// Third pass using doTrigger(true) to force fill minimum choices
|
||||||
if (chosenList.size() < min) {
|
if (chosenList.size() < min) {
|
||||||
choices.removeAll(chosenList);
|
choices.removeAll(chosenList);
|
||||||
for (AbilitySub sub : choices) {
|
for (AbilitySub sub : choices) {
|
||||||
sub.setActivatingPlayer(ai);
|
sub.setActivatingPlayer(ai);
|
||||||
sub.getRestrictions().setZone(sub.getParent().getRestrictions().getZone());
|
sub.getRestrictions().setZone(sub.getParent().getRestrictions().getZone());
|
||||||
if (aic.doTrigger(sub, true)) {
|
if (aic.doTrigger(sub, true)) {
|
||||||
chosenList.add(sub);
|
chosenList.add(sub);
|
||||||
if (chosenList.size() == min) {
|
if (chosenList.size() == min) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (chosenList.size() < min) {
|
if (chosenList.size() < min) {
|
||||||
chosenList.clear(); // not enough choices
|
chosenList.clear(); // not enough choices
|
||||||
}
|
}
|
||||||
return chosenList;
|
return chosenList;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<AbilitySub> chooseTriskaidekaphobia(List<AbilitySub> choices, final Player ai) {
|
private List<AbilitySub> chooseTriskaidekaphobia(List<AbilitySub> choices, final Player ai) {
|
||||||
List<AbilitySub> chosenList = Lists.newArrayList();
|
List<AbilitySub> chosenList = Lists.newArrayList();
|
||||||
if (choices == null || choices.isEmpty()) { return chosenList; }
|
if (choices == null || choices.isEmpty()) { return chosenList; }
|
||||||
|
|
||||||
AbilitySub gain = choices.get(0);
|
AbilitySub gain = choices.get(0);
|
||||||
AbilitySub lose = choices.get(1);
|
AbilitySub lose = choices.get(1);
|
||||||
FCollection<Player> opponents = ai.getOpponents();
|
FCollection<Player> opponents = ai.getOpponents();
|
||||||
|
|
||||||
boolean oppTainted = false;
|
boolean oppTainted = false;
|
||||||
boolean allyTainted = ai.isCardInPlay("Tainted Remedy");
|
boolean allyTainted = ai.isCardInPlay("Tainted Remedy");
|
||||||
final int aiLife = ai.getLife();
|
final int aiLife = ai.getLife();
|
||||||
|
|
||||||
//Check if Opponent controls Tainted Remedy
|
//Check if Opponent controls Tainted Remedy
|
||||||
for (Player p : opponents) {
|
for (Player p : opponents) {
|
||||||
if (p.isCardInPlay("Tainted Remedy")) {
|
if (p.isCardInPlay("Tainted Remedy")) {
|
||||||
oppTainted = true;
|
oppTainted = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// if ai or ally of ai does control Tainted Remedy, prefer gain life instead of lose
|
// if ai or ally of ai does control Tainted Remedy, prefer gain life instead of lose
|
||||||
if (!allyTainted) {
|
if (!allyTainted) {
|
||||||
for (Player p : ai.getAllies()) {
|
for (Player p : ai.getAllies()) {
|
||||||
if (p.isCardInPlay("Tainted Remedy")) {
|
if (p.isCardInPlay("Tainted Remedy")) {
|
||||||
allyTainted = true;
|
allyTainted = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ai.canLoseLife() || ai.cantLose()) {
|
if (!ai.canLoseLife() || ai.cantLose()) {
|
||||||
// ai cant lose life, or cant lose the game, don't think about others
|
// ai cant lose life, or cant lose the game, don't think about others
|
||||||
chosenList.add(allyTainted ? gain : lose);
|
chosenList.add(allyTainted ? gain : lose);
|
||||||
} else if (oppTainted || ai.getGame().isCardInPlay("Rain of Gore")) {
|
} else if (oppTainted || ai.getGame().isCardInPlay("Rain of Gore")) {
|
||||||
// Rain of Gore does negate lifegain, so don't benefit the others
|
// Rain of Gore does negate lifegain, so don't benefit the others
|
||||||
// same for if a oppoent does control Tainted Remedy
|
// same for if a oppoent does control Tainted Remedy
|
||||||
// but if ai cant gain life, the effects are negated
|
// but if ai cant gain life, the effects are negated
|
||||||
chosenList.add(ai.canGainLife() ? lose : gain);
|
chosenList.add(ai.canGainLife() ? lose : gain);
|
||||||
} else if (ai.getGame().isCardInPlay("Sulfuric Vortex")) {
|
} else if (ai.getGame().isCardInPlay("Sulfuric Vortex")) {
|
||||||
// no life gain, but extra life loss.
|
// no life gain, but extra life loss.
|
||||||
if (aiLife >= 17)
|
if (aiLife >= 17)
|
||||||
chosenList.add(lose);
|
chosenList.add(lose);
|
||||||
// try to prevent to get to 13 with extra lose
|
// try to prevent to get to 13 with extra lose
|
||||||
else if (aiLife < 13 || ((aiLife - 13) % 2) == 1) {
|
else if (aiLife < 13 || ((aiLife - 13) % 2) == 1) {
|
||||||
chosenList.add(gain);
|
chosenList.add(gain);
|
||||||
} else {
|
} else {
|
||||||
chosenList.add(lose);
|
chosenList.add(lose);
|
||||||
}
|
}
|
||||||
} else if (ai.canGainLife() && aiLife <= 5) {
|
} else if (ai.canGainLife() && aiLife <= 5) {
|
||||||
// critical Life try to gain more
|
// critical Life try to gain more
|
||||||
chosenList.add(gain);
|
chosenList.add(gain);
|
||||||
} else if(!ai.canGainLife() && aiLife == 14 ) {
|
} else if(!ai.canGainLife() && aiLife == 14 ) {
|
||||||
// ai cant gain life, but try to avoid falling to 13
|
// ai cant gain life, but try to avoid falling to 13
|
||||||
// but if a oppoent does control Tainted Remedy its irrelevant
|
// but if a oppoent does control Tainted Remedy its irrelevant
|
||||||
chosenList.add(oppTainted ? lose : gain);
|
chosenList.add(oppTainted ? lose : gain);
|
||||||
} else if (allyTainted) {
|
} else if (allyTainted) {
|
||||||
// Tainted Remedy negation logic, try gain instead of lose
|
// Tainted Remedy negation logic, try gain instead of lose
|
||||||
// because negation does turn it into lose for opponents
|
// because negation does turn it into lose for opponents
|
||||||
boolean oppCritical = false;
|
boolean oppCritical = false;
|
||||||
// an oppoent is Critical = 14, and can't gain life, try to lose life instead
|
// an oppoent is Critical = 14, and can't gain life, try to lose life instead
|
||||||
// but only if ai doesn't kill itself with that.
|
// but only if ai doesn't kill itself with that.
|
||||||
if (aiLife != 14) {
|
if (aiLife != 14) {
|
||||||
for (Player p : opponents) {
|
for (Player p : opponents) {
|
||||||
if (p.getLife() == 14 && !p.canGainLife() && p.canLoseLife()) {
|
if (p.getLife() == 14 && !p.canGainLife() && p.canLoseLife()) {
|
||||||
oppCritical = true;
|
oppCritical = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
chosenList.add(aiLife == 12 || oppCritical ? lose : gain);
|
chosenList.add(aiLife == 12 || oppCritical ? lose : gain);
|
||||||
} else {
|
} else {
|
||||||
// normal logic, try to gain life if its critical
|
// normal logic, try to gain life if its critical
|
||||||
boolean oppCritical = false;
|
boolean oppCritical = false;
|
||||||
// an oppoent is Critical = 12, and can gain life, try to gain life instead
|
// an oppoent is Critical = 12, and can gain life, try to gain life instead
|
||||||
// but only if ai doesn't kill itself with that.
|
// but only if ai doesn't kill itself with that.
|
||||||
if (aiLife != 12) {
|
if (aiLife != 12) {
|
||||||
for (Player p : opponents) {
|
for (Player p : opponents) {
|
||||||
if (p.getLife() == 12 && p.canGainLife()) {
|
if (p.getLife() == 12 && p.canGainLife()) {
|
||||||
oppCritical = true;
|
oppCritical = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
chosenList.add(aiLife == 14 || aiLife <= 10 || oppCritical ? gain : lose);
|
chosenList.add(aiLife == 14 || aiLife <= 10 || oppCritical ? gain : lose);
|
||||||
}
|
}
|
||||||
return chosenList;
|
return chosenList;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Choice selection for charms that require multiple choices (eg. Cryptic Command, DTK commands)
|
// Choice selection for charms that require multiple choices (eg. Cryptic Command, DTK commands)
|
||||||
private List<AbilitySub> chooseMultipleOptionsAi(List<AbilitySub> choices, final Player ai, int min) {
|
private List<AbilitySub> chooseMultipleOptionsAi(List<AbilitySub> choices, final Player ai, int min) {
|
||||||
AbilitySub goodChoice = null;
|
AbilitySub goodChoice = null;
|
||||||
List<AbilitySub> chosenList = Lists.newArrayList();
|
List<AbilitySub> chosenList = Lists.newArrayList();
|
||||||
AiController aic = ((PlayerControllerAi) ai.getController()).getAi();
|
AiController aic = ((PlayerControllerAi) ai.getController()).getAi();
|
||||||
for (AbilitySub sub : choices) {
|
for (AbilitySub sub : choices) {
|
||||||
sub.setActivatingPlayer(ai);
|
sub.setActivatingPlayer(ai);
|
||||||
// Assign generic good choice to fill up choices if necessary
|
// Assign generic good choice to fill up choices if necessary
|
||||||
if ("Good".equals(sub.getParam("AILogic")) && aic.doTrigger(sub, false)) {
|
if ("Good".equals(sub.getParam("AILogic")) && aic.doTrigger(sub, false)) {
|
||||||
goodChoice = sub;
|
goodChoice = sub;
|
||||||
} else {
|
} else {
|
||||||
// Standard canPlayAi()
|
// Standard canPlayAi()
|
||||||
if (AiPlayDecision.WillPlay == aic.canPlaySa(sub)) {
|
if (AiPlayDecision.WillPlay == aic.canPlaySa(sub)) {
|
||||||
chosenList.add(sub);
|
chosenList.add(sub);
|
||||||
if (chosenList.size() == min) {
|
if (chosenList.size() == min) {
|
||||||
break; // enough choices
|
break; // enough choices
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Add generic good choice if one more choice is needed
|
// Add generic good choice if one more choice is needed
|
||||||
if (chosenList.size() == min - 1 && goodChoice != null) {
|
if (chosenList.size() == min - 1 && goodChoice != null) {
|
||||||
chosenList.add(0, goodChoice); // hack to make Dromoka's Command fight targets work
|
chosenList.add(0, goodChoice); // hack to make Dromoka's Command fight targets work
|
||||||
}
|
}
|
||||||
if (chosenList.size() != min) {
|
if (chosenList.size() != min) {
|
||||||
chosenList.clear();
|
chosenList.clear();
|
||||||
}
|
}
|
||||||
return chosenList;
|
return chosenList;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> opponents) {
|
public Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> opponents) {
|
||||||
return Aggregates.random(opponents);
|
return Aggregates.random(opponents);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,269 +1,269 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
import forge.ai.ComputerUtil;
|
import forge.ai.ComputerUtil;
|
||||||
import forge.ai.ComputerUtilAbility;
|
import forge.ai.ComputerUtilAbility;
|
||||||
|
|
||||||
import forge.ai.ComputerUtilCard;
|
import forge.ai.ComputerUtilCard;
|
||||||
import forge.ai.ComputerUtilCombat;
|
import forge.ai.ComputerUtilCombat;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardCollection;
|
import forge.game.card.CardCollection;
|
||||||
import forge.game.card.CardCollectionView;
|
import forge.game.card.CardCollectionView;
|
||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
import forge.game.card.CardPredicates.Presets;
|
import forge.game.card.CardPredicates.Presets;
|
||||||
import forge.game.card.CounterType;
|
import forge.game.card.CounterType;
|
||||||
import forge.game.combat.Combat;
|
import forge.game.combat.Combat;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.player.PlayerPredicates;
|
import forge.game.player.PlayerPredicates;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
import forge.util.Aggregates;
|
import forge.util.Aggregates;
|
||||||
|
|
||||||
public class ChooseCardAi extends SpellAbilityAi {
|
public class ChooseCardAi extends SpellAbilityAi {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The rest of the logic not covered by the canPlayAI template is defined here
|
* The rest of the logic not covered by the canPlayAI template is defined here
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkApiLogic(final Player ai, final SpellAbility sa) {
|
protected boolean checkApiLogic(final Player ai, final SpellAbility sa) {
|
||||||
if (sa.usesTargeting()) {
|
if (sa.usesTargeting()) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
// search targetable Opponents
|
// search targetable Opponents
|
||||||
final List<Player> oppList = Lists.newArrayList(Iterables.filter(
|
final List<Player> oppList = Lists.newArrayList(Iterables.filter(
|
||||||
ai.getOpponents(), PlayerPredicates.isTargetableBy(sa)));
|
ai.getOpponents(), PlayerPredicates.isTargetableBy(sa)));
|
||||||
|
|
||||||
if (oppList.isEmpty()) {
|
if (oppList.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
sa.getTargets().add(Iterables.getFirst(oppList, null));
|
sa.getTargets().add(Iterables.getFirst(oppList, null));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the AI will play a SpellAbility with the specified AiLogic
|
* Checks if the AI will play a SpellAbility with the specified AiLogic
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkAiLogic(final Player ai, final SpellAbility sa, final String aiLogic) {
|
protected boolean checkAiLogic(final Player ai, final SpellAbility sa, final String aiLogic) {
|
||||||
final Card host = sa.getHostCard();
|
final Card host = sa.getHostCard();
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
ZoneType choiceZone = ZoneType.Battlefield;
|
ZoneType choiceZone = ZoneType.Battlefield;
|
||||||
if (sa.hasParam("ChoiceZone")) {
|
if (sa.hasParam("ChoiceZone")) {
|
||||||
choiceZone = ZoneType.smartValueOf(sa.getParam("ChoiceZone"));
|
choiceZone = ZoneType.smartValueOf(sa.getParam("ChoiceZone"));
|
||||||
}
|
}
|
||||||
CardCollectionView choices = ai.getGame().getCardsIn(choiceZone);
|
CardCollectionView choices = ai.getGame().getCardsIn(choiceZone);
|
||||||
if (sa.hasParam("Choices")) {
|
if (sa.hasParam("Choices")) {
|
||||||
choices = CardLists.getValidCards(choices, sa.getParam("Choices"), host.getController(), host);
|
choices = CardLists.getValidCards(choices, sa.getParam("Choices"), host.getController(), host);
|
||||||
}
|
}
|
||||||
if (sa.hasParam("TargetControls")) {
|
if (sa.hasParam("TargetControls")) {
|
||||||
choices = CardLists.filterControlledBy(choices, ai.getOpponents());
|
choices = CardLists.filterControlledBy(choices, ai.getOpponents());
|
||||||
}
|
}
|
||||||
if (aiLogic.equals("AtLeast1") || aiLogic.equals("OppPreferred")) {
|
if (aiLogic.equals("AtLeast1") || aiLogic.equals("OppPreferred")) {
|
||||||
if (choices.isEmpty()) {
|
if (choices.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (aiLogic.equals("AtLeast2") || aiLogic.equals("BestBlocker")) {
|
} else if (aiLogic.equals("AtLeast2") || aiLogic.equals("BestBlocker")) {
|
||||||
if (choices.size() < 2) {
|
if (choices.size() < 2) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (aiLogic.equals("Clone") || aiLogic.equals("Vesuva")) {
|
} else if (aiLogic.equals("Clone") || aiLogic.equals("Vesuva")) {
|
||||||
final String filter = aiLogic.equals("Clone") ? "Permanent.YouDontCtrl,Permanent.nonLegendary"
|
final String filter = aiLogic.equals("Clone") ? "Permanent.YouDontCtrl,Permanent.nonLegendary"
|
||||||
: "Permanent.YouDontCtrl+notnamedVesuva,Permanent.nonLegendary+notnamedVesuva";
|
: "Permanent.YouDontCtrl+notnamedVesuva,Permanent.nonLegendary+notnamedVesuva";
|
||||||
|
|
||||||
choices = CardLists.getValidCards(choices, filter, host.getController(), host);
|
choices = CardLists.getValidCards(choices, filter, host.getController(), host);
|
||||||
if (choices.isEmpty()) {
|
if (choices.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (aiLogic.equals("Never")) {
|
} else if (aiLogic.equals("Never")) {
|
||||||
return false;
|
return false;
|
||||||
} else if (aiLogic.equals("NeedsPrevention")) {
|
} else if (aiLogic.equals("NeedsPrevention")) {
|
||||||
if (!game.getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
|
if (!game.getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
final Combat combat = game.getCombat();
|
final Combat combat = game.getCombat();
|
||||||
choices = CardLists.filter(choices, new Predicate<Card>() {
|
choices = CardLists.filter(choices, new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(final Card c) {
|
public boolean apply(final Card c) {
|
||||||
if (!combat.isAttacking(c, ai) || !combat.isUnblocked(c)) {
|
if (!combat.isAttacking(c, ai) || !combat.isUnblocked(c)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
int ref = ComputerUtilAbility.getAbilitySourceName(sa).equals("Forcefield") ? 1 : 0;
|
int ref = ComputerUtilAbility.getAbilitySourceName(sa).equals("Forcefield") ? 1 : 0;
|
||||||
return ComputerUtilCombat.damageIfUnblocked(c, ai, combat, true) > ref;
|
return ComputerUtilCombat.damageIfUnblocked(c, ai, combat, true) > ref;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (choices.isEmpty()) {
|
if (choices.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (aiLogic.equals("Ashiok")) {
|
} else if (aiLogic.equals("Ashiok")) {
|
||||||
final int loyalty = host.getCounters(CounterType.LOYALTY) - 1;
|
final int loyalty = host.getCounters(CounterType.LOYALTY) - 1;
|
||||||
for (int i = loyalty; i >= 0; i--) {
|
for (int i = loyalty; i >= 0; i--) {
|
||||||
host.setSVar("ChosenX", "Number$" + i);
|
host.setSVar("ChosenX", "Number$" + i);
|
||||||
choices = ai.getGame().getCardsIn(choiceZone);
|
choices = ai.getGame().getCardsIn(choiceZone);
|
||||||
choices = CardLists.getValidCards(choices, sa.getParam("Choices"), host.getController(), host);
|
choices = CardLists.getValidCards(choices, sa.getParam("Choices"), host.getController(), host);
|
||||||
if (!choices.isEmpty()) {
|
if (!choices.isEmpty()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (choices.isEmpty()) {
|
if (choices.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (aiLogic.equals("RandomNonLand")) {
|
} else if (aiLogic.equals("RandomNonLand")) {
|
||||||
if (CardLists.getValidCards(choices, "Card.nonLand", host.getController(), host).isEmpty()) {
|
if (CardLists.getValidCards(choices, "Card.nonLand", host.getController(), host).isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (aiLogic.equals("Duneblast")) {
|
} else if (aiLogic.equals("Duneblast")) {
|
||||||
CardCollection aiCreatures = ai.getCreaturesInPlay();
|
CardCollection aiCreatures = ai.getCreaturesInPlay();
|
||||||
CardCollection oppCreatures = ComputerUtil.getOpponentFor(ai).getCreaturesInPlay();
|
CardCollection oppCreatures = ComputerUtil.getOpponentFor(ai).getCreaturesInPlay();
|
||||||
aiCreatures = CardLists.getNotKeyword(aiCreatures, "Indestructible");
|
aiCreatures = CardLists.getNotKeyword(aiCreatures, "Indestructible");
|
||||||
oppCreatures = CardLists.getNotKeyword(oppCreatures, "Indestructible");
|
oppCreatures = CardLists.getNotKeyword(oppCreatures, "Indestructible");
|
||||||
|
|
||||||
// Use it as a wrath, when the human creatures threat the ai's life
|
// Use it as a wrath, when the human creatures threat the ai's life
|
||||||
if (aiCreatures.isEmpty() && ComputerUtilCombat.sumDamageIfUnblocked(oppCreatures, ai) >= ai.getLife()) {
|
if (aiCreatures.isEmpty() && ComputerUtilCombat.sumDamageIfUnblocked(oppCreatures, ai) >= ai.getLife()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Card chosen = ComputerUtilCard.getBestCreatureAI(aiCreatures);
|
Card chosen = ComputerUtilCard.getBestCreatureAI(aiCreatures);
|
||||||
aiCreatures.remove(chosen);
|
aiCreatures.remove(chosen);
|
||||||
int minGain = 200;
|
int minGain = 200;
|
||||||
|
|
||||||
if ((ComputerUtilCard.evaluateCreatureList(aiCreatures) + minGain) >= ComputerUtilCard
|
if ((ComputerUtilCard.evaluateCreatureList(aiCreatures) + minGain) >= ComputerUtilCard
|
||||||
.evaluateCreatureList(oppCreatures)) {
|
.evaluateCreatureList(oppCreatures)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
||||||
if (sa.hasParam("AILogic") && !checkAiLogic(ai, sa, sa.getParam("AILogic"))) {
|
if (sa.hasParam("AILogic") && !checkAiLogic(ai, sa, sa.getParam("AILogic"))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return checkApiLogic(ai, sa);
|
return checkApiLogic(ai, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see forge.card.ability.SpellAbilityAi#chooseSingleCard(forge.card.spellability.SpellAbility, java.util.List, boolean)
|
* @see forge.card.ability.SpellAbilityAi#chooseSingleCard(forge.card.spellability.SpellAbility, java.util.List, boolean)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Card chooseSingleCard(final Player ai, final SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
|
public Card chooseSingleCard(final Player ai, final SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
|
||||||
final Card host = sa.getHostCard();
|
final Card host = sa.getHostCard();
|
||||||
final Player ctrl = host.getController();
|
final Player ctrl = host.getController();
|
||||||
final String logic = sa.getParam("AILogic");
|
final String logic = sa.getParam("AILogic");
|
||||||
Card choice = null;
|
Card choice = null;
|
||||||
if (logic == null) {
|
if (logic == null) {
|
||||||
// Base Logic is choose "best"
|
// Base Logic is choose "best"
|
||||||
choice = ComputerUtilCard.getBestAI(options);
|
choice = ComputerUtilCard.getBestAI(options);
|
||||||
} else if ("WorstCard".equals(logic)) {
|
} else if ("WorstCard".equals(logic)) {
|
||||||
choice = ComputerUtilCard.getWorstAI(options);
|
choice = ComputerUtilCard.getWorstAI(options);
|
||||||
} else if (logic.equals("BestBlocker")) {
|
} else if (logic.equals("BestBlocker")) {
|
||||||
if (!CardLists.filter(options, Presets.UNTAPPED).isEmpty()) {
|
if (!CardLists.filter(options, Presets.UNTAPPED).isEmpty()) {
|
||||||
options = CardLists.filter(options, Presets.UNTAPPED);
|
options = CardLists.filter(options, Presets.UNTAPPED);
|
||||||
}
|
}
|
||||||
choice = ComputerUtilCard.getBestCreatureAI(options);
|
choice = ComputerUtilCard.getBestCreatureAI(options);
|
||||||
} else if (logic.equals("Clone") || logic.equals("Vesuva")) {
|
} else if (logic.equals("Clone") || logic.equals("Vesuva")) {
|
||||||
final String filter = logic.equals("Clone") ? "Permanent.YouDontCtrl,Permanent.nonLegendary" :
|
final String filter = logic.equals("Clone") ? "Permanent.YouDontCtrl,Permanent.nonLegendary" :
|
||||||
"Permanent.YouDontCtrl+notnamedVesuva,Permanent.nonLegendary+notnamedVesuva";
|
"Permanent.YouDontCtrl+notnamedVesuva,Permanent.nonLegendary+notnamedVesuva";
|
||||||
|
|
||||||
CardCollection newOptions = CardLists.getValidCards(options, filter.split(","), ctrl, host, sa);
|
CardCollection newOptions = CardLists.getValidCards(options, filter.split(","), ctrl, host, sa);
|
||||||
if (!newOptions.isEmpty()) {
|
if (!newOptions.isEmpty()) {
|
||||||
options = newOptions;
|
options = newOptions;
|
||||||
}
|
}
|
||||||
choice = ComputerUtilCard.getBestAI(options);
|
choice = ComputerUtilCard.getBestAI(options);
|
||||||
if (logic.equals("Vesuva") && "Vesuva".equals(choice.getName())) {
|
if (logic.equals("Vesuva") && "Vesuva".equals(choice.getName())) {
|
||||||
choice = null;
|
choice = null;
|
||||||
}
|
}
|
||||||
} else if ("RandomNonLand".equals(logic)) {
|
} else if ("RandomNonLand".equals(logic)) {
|
||||||
options = CardLists.getValidCards(options, "Card.nonLand", host.getController(), host);
|
options = CardLists.getValidCards(options, "Card.nonLand", host.getController(), host);
|
||||||
choice = Aggregates.random(options);
|
choice = Aggregates.random(options);
|
||||||
} else if (logic.equals("Untap")) {
|
} else if (logic.equals("Untap")) {
|
||||||
final String filter = "Permanent.YouCtrl,Permanent.tapped";
|
final String filter = "Permanent.YouCtrl,Permanent.tapped";
|
||||||
CardCollection newOptions = CardLists.getValidCards(options, filter.split(","), ctrl, host, sa);
|
CardCollection newOptions = CardLists.getValidCards(options, filter.split(","), ctrl, host, sa);
|
||||||
if (!newOptions.isEmpty()) {
|
if (!newOptions.isEmpty()) {
|
||||||
options = newOptions;
|
options = newOptions;
|
||||||
}
|
}
|
||||||
choice = ComputerUtilCard.getBestAI(options);
|
choice = ComputerUtilCard.getBestAI(options);
|
||||||
} else if (logic.equals("NeedsPrevention")) {
|
} else if (logic.equals("NeedsPrevention")) {
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
final Combat combat = game.getCombat();
|
final Combat combat = game.getCombat();
|
||||||
CardCollectionView better = CardLists.filter(options, new Predicate<Card>() {
|
CardCollectionView better = CardLists.filter(options, new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(final Card c) {
|
public boolean apply(final Card c) {
|
||||||
if (combat == null || !combat.isAttacking(c, ai) || !combat.isUnblocked(c)) {
|
if (combat == null || !combat.isAttacking(c, ai) || !combat.isUnblocked(c)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
int ref = ComputerUtilAbility.getAbilitySourceName(sa).equals("Forcefield") ? 1 : 0;
|
int ref = ComputerUtilAbility.getAbilitySourceName(sa).equals("Forcefield") ? 1 : 0;
|
||||||
return ComputerUtilCombat.damageIfUnblocked(c, ai, combat, true) > ref;
|
return ComputerUtilCombat.damageIfUnblocked(c, ai, combat, true) > ref;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (!better.isEmpty()) {
|
if (!better.isEmpty()) {
|
||||||
choice = ComputerUtilCard.getBestAI(better);
|
choice = ComputerUtilCard.getBestAI(better);
|
||||||
} else {
|
} else {
|
||||||
choice = ComputerUtilCard.getBestAI(options);
|
choice = ComputerUtilCard.getBestAI(options);
|
||||||
}
|
}
|
||||||
} else if ("OppPreferred".equals(logic)) {
|
} else if ("OppPreferred".equals(logic)) {
|
||||||
CardCollectionView oppControlled = CardLists.filterControlledBy(options, ai.getOpponents());
|
CardCollectionView oppControlled = CardLists.filterControlledBy(options, ai.getOpponents());
|
||||||
if (!oppControlled.isEmpty()) {
|
if (!oppControlled.isEmpty()) {
|
||||||
choice = ComputerUtilCard.getBestAI(oppControlled);
|
choice = ComputerUtilCard.getBestAI(oppControlled);
|
||||||
} else {
|
} else {
|
||||||
CardCollectionView aiControlled = CardLists.filterControlledBy(options, ai);
|
CardCollectionView aiControlled = CardLists.filterControlledBy(options, ai);
|
||||||
choice = ComputerUtilCard.getWorstAI(aiControlled);
|
choice = ComputerUtilCard.getWorstAI(aiControlled);
|
||||||
}
|
}
|
||||||
} else if ("LowestCMCCreature".equals(logic)) {
|
} else if ("LowestCMCCreature".equals(logic)) {
|
||||||
CardCollection creats = CardLists.filter(options, Presets.CREATURES);
|
CardCollection creats = CardLists.filter(options, Presets.CREATURES);
|
||||||
creats = CardLists.filterToughness(creats, 1);
|
creats = CardLists.filterToughness(creats, 1);
|
||||||
if (creats.isEmpty()) {
|
if (creats.isEmpty()) {
|
||||||
choice = ComputerUtilCard.getWorstAI(options);
|
choice = ComputerUtilCard.getWorstAI(options);
|
||||||
} else {
|
} else {
|
||||||
CardLists.sortByCmcDesc(creats);
|
CardLists.sortByCmcDesc(creats);
|
||||||
Collections.reverse(creats);
|
Collections.reverse(creats);
|
||||||
choice = creats.get(0);
|
choice = creats.get(0);
|
||||||
}
|
}
|
||||||
} else if ("TangleWire".equals(logic)) {
|
} else if ("TangleWire".equals(logic)) {
|
||||||
CardCollectionView betterList = CardLists.filter(options, new Predicate<Card>() {
|
CardCollectionView betterList = CardLists.filter(options, new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(final Card c) {
|
public boolean apply(final Card c) {
|
||||||
if (c.isCreature()) {
|
if (c.isCreature()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (SpellAbility sa : c.getAllSpellAbilities()) {
|
for (SpellAbility sa : c.getAllSpellAbilities()) {
|
||||||
if (sa.getPayCosts() != null && sa.getPayCosts().hasTapCost()) {
|
if (sa.getPayCosts() != null && sa.getPayCosts().hasTapCost()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
System.out.println("Tangle Wire" + options + " - " + betterList);
|
System.out.println("Tangle Wire" + options + " - " + betterList);
|
||||||
if (!betterList.isEmpty()) {
|
if (!betterList.isEmpty()) {
|
||||||
choice = betterList.get(0);
|
choice = betterList.get(0);
|
||||||
} else {
|
} else {
|
||||||
choice = ComputerUtilCard.getWorstPermanentAI(options, false, false, false, false);
|
choice = ComputerUtilCard.getWorstPermanentAI(options, false, false, false, false);
|
||||||
}
|
}
|
||||||
} else if (logic.equals("Duneblast")) {
|
} else if (logic.equals("Duneblast")) {
|
||||||
CardCollectionView aiCreatures = ai.getCreaturesInPlay();
|
CardCollectionView aiCreatures = ai.getCreaturesInPlay();
|
||||||
aiCreatures = CardLists.getNotKeyword(aiCreatures, "Indestructible");
|
aiCreatures = CardLists.getNotKeyword(aiCreatures, "Indestructible");
|
||||||
|
|
||||||
if (aiCreatures.isEmpty()) {
|
if (aiCreatures.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Card chosen = ComputerUtilCard.getBestCreatureAI(aiCreatures);
|
Card chosen = ComputerUtilCard.getBestCreatureAI(aiCreatures);
|
||||||
return chosen;
|
return chosen;
|
||||||
} else {
|
} else {
|
||||||
choice = ComputerUtilCard.getBestAI(options);
|
choice = ComputerUtilCard.getBestAI(options);
|
||||||
}
|
}
|
||||||
return choice;
|
return choice;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,107 +1,107 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
import forge.StaticData;
|
import forge.StaticData;
|
||||||
import forge.ai.*;
|
import forge.ai.*;
|
||||||
import forge.card.CardDb;
|
import forge.card.CardDb;
|
||||||
import forge.card.CardRules;
|
import forge.card.CardRules;
|
||||||
import forge.card.CardSplitType;
|
import forge.card.CardSplitType;
|
||||||
import forge.card.CardStateName;
|
import forge.card.CardStateName;
|
||||||
import forge.card.ICardFace;
|
import forge.card.ICardFace;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardUtil;
|
import forge.game.card.CardUtil;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.TargetRestrictions;
|
import forge.game.spellability.TargetRestrictions;
|
||||||
import forge.item.PaperCard;
|
import forge.item.PaperCard;
|
||||||
|
|
||||||
public class ChooseCardNameAi extends SpellAbilityAi {
|
public class ChooseCardNameAi extends SpellAbilityAi {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
||||||
Card source = sa.getHostCard();
|
Card source = sa.getHostCard();
|
||||||
if (sa.hasParam("AILogic")) {
|
if (sa.hasParam("AILogic")) {
|
||||||
// Don't tap creatures that may be able to block
|
// Don't tap creatures that may be able to block
|
||||||
if (ComputerUtil.waitForBlocking(sa)) {
|
if (ComputerUtil.waitForBlocking(sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
String logic = sa.getParam("AILogic");
|
String logic = sa.getParam("AILogic");
|
||||||
if (logic.equals("MomirAvatar")) {
|
if (logic.equals("MomirAvatar")) {
|
||||||
return SpecialCardAi.MomirVigAvatar.consider(ai, sa);
|
return SpecialCardAi.MomirVigAvatar.consider(ai, sa);
|
||||||
} else if (logic.equals("CursedScroll")) {
|
} else if (logic.equals("CursedScroll")) {
|
||||||
return SpecialCardAi.CursedScroll.consider(ai, sa);
|
return SpecialCardAi.CursedScroll.consider(ai, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
if (tgt != null) {
|
if (tgt != null) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
if (tgt.canOnlyTgtOpponent()) {
|
if (tgt.canOnlyTgtOpponent()) {
|
||||||
sa.getTargets().add(ComputerUtil.getOpponentFor(ai));
|
sa.getTargets().add(ComputerUtil.getOpponentFor(ai));
|
||||||
} else {
|
} else {
|
||||||
sa.getTargets().add(ai);
|
sa.getTargets().add(ai);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
|
||||||
// TODO - there is no AILogic implemented yet
|
// TODO - there is no AILogic implemented yet
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see forge.card.ability.SpellAbilityAi#chooseSingleCard(forge.card.spellability.SpellAbility, java.util.List, boolean)
|
* @see forge.card.ability.SpellAbilityAi#chooseSingleCard(forge.card.spellability.SpellAbility, java.util.List, boolean)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Card chooseSingleCard(final Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
|
public Card chooseSingleCard(final Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
|
||||||
|
|
||||||
return ComputerUtilCard.getBestAI(options);
|
return ComputerUtilCard.getBestAI(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String chooseCardName(Player ai, SpellAbility sa, List<ICardFace> faces) {
|
public String chooseCardName(Player ai, SpellAbility sa, List<ICardFace> faces) {
|
||||||
// this function is only for "Alhammarret, High Arbiter"
|
// this function is only for "Alhammarret, High Arbiter"
|
||||||
|
|
||||||
if (faces.isEmpty()) {
|
if (faces.isEmpty()) {
|
||||||
return "";
|
return "";
|
||||||
} else if (faces.size() == 1) {
|
} else if (faces.size() == 1) {
|
||||||
return Iterables.getFirst(faces, null).getName();
|
return Iterables.getFirst(faces, null).getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Card> cards = Lists.newArrayList();
|
List<Card> cards = Lists.newArrayList();
|
||||||
final CardDb cardDb = StaticData.instance().getCommonCards();
|
final CardDb cardDb = StaticData.instance().getCommonCards();
|
||||||
|
|
||||||
for (ICardFace face : faces) {
|
for (ICardFace face : faces) {
|
||||||
final CardRules rules = cardDb.getRules(face.getName());
|
final CardRules rules = cardDb.getRules(face.getName());
|
||||||
boolean isOther = rules.getOtherPart() == face;
|
boolean isOther = rules.getOtherPart() == face;
|
||||||
final PaperCard paper = cardDb.getCard(rules.getName());
|
final PaperCard paper = cardDb.getCard(rules.getName());
|
||||||
final Card card = Card.fromPaperCard(paper, ai);
|
final Card card = Card.fromPaperCard(paper, ai);
|
||||||
|
|
||||||
if (rules.getSplitType() == CardSplitType.Split) {
|
if (rules.getSplitType() == CardSplitType.Split) {
|
||||||
Card copy = CardUtil.getLKICopy(card);
|
Card copy = CardUtil.getLKICopy(card);
|
||||||
// for calcing i need only one split side
|
// for calcing i need only one split side
|
||||||
if (isOther) {
|
if (isOther) {
|
||||||
copy.getCurrentState().copyFrom(card, card.getState(CardStateName.RightSplit));
|
copy.getCurrentState().copyFrom(card, card.getState(CardStateName.RightSplit));
|
||||||
} else {
|
} else {
|
||||||
copy.getCurrentState().copyFrom(card, card.getState(CardStateName.LeftSplit));
|
copy.getCurrentState().copyFrom(card, card.getState(CardStateName.LeftSplit));
|
||||||
}
|
}
|
||||||
copy.updateStateForView();
|
copy.updateStateForView();
|
||||||
|
|
||||||
cards.add(copy);
|
cards.add(copy);
|
||||||
} else if (!isOther) {
|
} else if (!isOther) {
|
||||||
// other can't be cast that way, not need to prevent that
|
// other can't be cast that way, not need to prevent that
|
||||||
cards.add(card);
|
cards.add(card);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ComputerUtilCard.getBestAI(cards).getName();
|
return ComputerUtilCard.getBestAI(cards).getName();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,97 +1,97 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import forge.ai.ComputerUtil;
|
import forge.ai.ComputerUtil;
|
||||||
import forge.ai.ComputerUtilAbility;
|
import forge.ai.ComputerUtilAbility;
|
||||||
import forge.ai.ComputerUtilCard;
|
import forge.ai.ComputerUtilCard;
|
||||||
import forge.ai.ComputerUtilMana;
|
import forge.ai.ComputerUtilMana;
|
||||||
import forge.ai.SpecialCardAi;
|
import forge.ai.SpecialCardAi;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.card.MagicColor;
|
import forge.card.MagicColor;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardCollectionView;
|
import forge.game.card.CardCollectionView;
|
||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
import forge.game.card.CardPredicates;
|
import forge.game.card.CardPredicates;
|
||||||
import forge.game.phase.PhaseHandler;
|
import forge.game.phase.PhaseHandler;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
import forge.util.MyRandom;
|
import forge.util.MyRandom;
|
||||||
|
|
||||||
public class ChooseColorAi extends SpellAbilityAi {
|
public class ChooseColorAi extends SpellAbilityAi {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
final String sourceName = ComputerUtilAbility.getAbilitySourceName(sa);
|
final String sourceName = ComputerUtilAbility.getAbilitySourceName(sa);
|
||||||
final PhaseHandler ph = game.getPhaseHandler();
|
final PhaseHandler ph = game.getPhaseHandler();
|
||||||
|
|
||||||
if (!sa.hasParam("AILogic")) {
|
if (!sa.hasParam("AILogic")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
final String logic = sa.getParam("AILogic");
|
final String logic = sa.getParam("AILogic");
|
||||||
|
|
||||||
if (ComputerUtil.preventRunAwayActivations(sa)) {
|
if (ComputerUtil.preventRunAwayActivations(sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("Nykthos, Shrine to Nyx".equals(sourceName)) {
|
if ("Nykthos, Shrine to Nyx".equals(sourceName)) {
|
||||||
return SpecialCardAi.NykthosShrineToNyx.consider(ai, sa);
|
return SpecialCardAi.NykthosShrineToNyx.consider(ai, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("Oona, Queen of the Fae".equals(sourceName)) {
|
if ("Oona, Queen of the Fae".equals(sourceName)) {
|
||||||
if (ph.isPlayerTurn(ai) || ph.getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS)) {
|
if (ph.isPlayerTurn(ai) || ph.getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Set PayX here to maximum value.
|
// Set PayX here to maximum value.
|
||||||
int x = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
int x = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
||||||
source.setSVar("PayX", Integer.toString(x));
|
source.setSVar("PayX", Integer.toString(x));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("Addle".equals(sourceName)) {
|
if ("Addle".equals(sourceName)) {
|
||||||
if (ph.getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS) || ComputerUtil.getOpponentFor(ai).getCardsIn(ZoneType.Hand).isEmpty()) {
|
if (ph.getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS) || ComputerUtil.getOpponentFor(ai).getCardsIn(ZoneType.Hand).isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (logic.equals("MostExcessOpponentControls")) {
|
if (logic.equals("MostExcessOpponentControls")) {
|
||||||
for (byte color : MagicColor.WUBRG) {
|
for (byte color : MagicColor.WUBRG) {
|
||||||
CardCollectionView ailist = ai.getCardsIn(ZoneType.Battlefield);
|
CardCollectionView ailist = ai.getCardsIn(ZoneType.Battlefield);
|
||||||
CardCollectionView opplist = ComputerUtil.getOpponentFor(ai).getCardsIn(ZoneType.Battlefield);
|
CardCollectionView opplist = ComputerUtil.getOpponentFor(ai).getCardsIn(ZoneType.Battlefield);
|
||||||
|
|
||||||
ailist = CardLists.filter(ailist, CardPredicates.isColor(color));
|
ailist = CardLists.filter(ailist, CardPredicates.isColor(color));
|
||||||
opplist = CardLists.filter(opplist, CardPredicates.isColor(color));
|
opplist = CardLists.filter(opplist, CardPredicates.isColor(color));
|
||||||
|
|
||||||
int excess = ComputerUtilCard.evaluatePermanentList(opplist) - ComputerUtilCard.evaluatePermanentList(ailist);
|
int excess = ComputerUtilCard.evaluatePermanentList(opplist) - ComputerUtilCard.evaluatePermanentList(ailist);
|
||||||
if (excess > 4) {
|
if (excess > 4) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (logic.equals("MostProminentInComputerDeck")) {
|
if (logic.equals("MostProminentInComputerDeck")) {
|
||||||
if ("Astral Cornucopia".equals(sourceName)) {
|
if ("Astral Cornucopia".equals(sourceName)) {
|
||||||
// activate in Main 2 hoping that the extra mana surplus will make a difference
|
// activate in Main 2 hoping that the extra mana surplus will make a difference
|
||||||
// if there are some nonland permanents in hand
|
// if there are some nonland permanents in hand
|
||||||
CardCollectionView permanents = CardLists.filter(ai.getCardsIn(ZoneType.Hand),
|
CardCollectionView permanents = CardLists.filter(ai.getCardsIn(ZoneType.Hand),
|
||||||
CardPredicates.Presets.NONLAND_PERMANENTS);
|
CardPredicates.Presets.NONLAND_PERMANENTS);
|
||||||
|
|
||||||
return permanents.size() > 0 && ph.is(PhaseType.MAIN2, ai);
|
return permanents.size() > 0 && ph.is(PhaseType.MAIN2, ai);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean chance = MyRandom.getRandom().nextFloat() <= Math.pow(.6667, sa.getActivationsThisTurn());
|
boolean chance = MyRandom.getRandom().nextFloat() <= Math.pow(.6667, sa.getActivationsThisTurn());
|
||||||
return chance;
|
return chance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
||||||
return mandatory || canPlayAI(ai, sa);
|
return mandatory || canPlayAI(ai, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,32 +1,32 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
|
|
||||||
public class ChooseDirectionAi extends SpellAbilityAi {
|
public class ChooseDirectionAi extends SpellAbilityAi {
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see forge.card.abilityfactory.SpellAiLogic#canPlayAI(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility)
|
* @see forge.card.abilityfactory.SpellAiLogic#canPlayAI(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
||||||
final String logic = sa.getParam("AILogic");
|
final String logic = sa.getParam("AILogic");
|
||||||
if (logic == null) {
|
if (logic == null) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
// TODO: default ai
|
// TODO: default ai
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
||||||
return canPlayAI(ai, sa);
|
return canPlayAI(ai, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
||||||
return canPlayAI(ai, sa);
|
return canPlayAI(ai, sa);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,342 +1,342 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import forge.ai.ComputerUtilAbility;
|
import forge.ai.ComputerUtilAbility;
|
||||||
|
|
||||||
import forge.ai.ComputerUtilCard;
|
import forge.ai.ComputerUtilCard;
|
||||||
import forge.ai.ComputerUtilCost;
|
import forge.ai.ComputerUtilCost;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.ai.SpellApiToAi;
|
import forge.ai.SpellApiToAi;
|
||||||
import forge.card.MagicColor;
|
import forge.card.MagicColor;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardCollection;
|
import forge.game.card.CardCollection;
|
||||||
import forge.game.card.CardCollectionView;
|
import forge.game.card.CardCollectionView;
|
||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
import forge.game.card.CardPredicates.Presets;
|
import forge.game.card.CardPredicates.Presets;
|
||||||
import forge.game.card.CardUtil;
|
import forge.game.card.CardUtil;
|
||||||
import forge.game.card.CounterType;
|
import forge.game.card.CounterType;
|
||||||
import forge.game.combat.Combat;
|
import forge.game.combat.Combat;
|
||||||
import forge.game.combat.CombatUtil;
|
import forge.game.combat.CombatUtil;
|
||||||
import forge.game.cost.Cost;
|
import forge.game.cost.Cost;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.AbilitySub;
|
import forge.game.spellability.AbilitySub;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
import forge.util.Aggregates;
|
import forge.util.Aggregates;
|
||||||
import forge.util.collect.FCollection;
|
import forge.util.collect.FCollection;
|
||||||
|
|
||||||
|
|
||||||
public class ChooseGenericEffectAi extends SpellAbilityAi {
|
public class ChooseGenericEffectAi extends SpellAbilityAi {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkAiLogic(final Player ai, final SpellAbility sa, final String aiLogic) {
|
protected boolean checkAiLogic(final Player ai, final SpellAbility sa, final String aiLogic) {
|
||||||
if ("Khans".equals(aiLogic) || "Dragons".equals(aiLogic)) {
|
if ("Khans".equals(aiLogic) || "Dragons".equals(aiLogic)) {
|
||||||
return true;
|
return true;
|
||||||
} else if (aiLogic.startsWith("Fabricate")) {
|
} else if (aiLogic.startsWith("Fabricate")) {
|
||||||
return true;
|
return true;
|
||||||
} else if ("Pump".equals(aiLogic) || "BestOption".equals(aiLogic)) {
|
} else if ("Pump".equals(aiLogic) || "BestOption".equals(aiLogic)) {
|
||||||
for (AbilitySub sb : sa.getAdditionalAbilityList("Choices")) {
|
for (AbilitySub sb : sa.getAdditionalAbilityList("Choices")) {
|
||||||
if (SpellApiToAi.Converter.get(sb.getApi()).canPlayAIWithSubs(ai, sb)) {
|
if (SpellApiToAi.Converter.get(sb.getApi()).canPlayAIWithSubs(ai, sb)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkApiLogic(final Player ai, final SpellAbility sa) {
|
protected boolean checkApiLogic(final Player ai, final SpellAbility sa) {
|
||||||
return sa.hasParam("AILogic");
|
return sa.hasParam("AILogic");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see forge.card.abilityfactory.SpellAiLogic#chkAIDrawback(java.util.Map, forge.card.spellability.SpellAbility, forge.game.player.Player)
|
* @see forge.card.abilityfactory.SpellAiLogic#chkAIDrawback(java.util.Map, forge.card.spellability.SpellAbility, forge.game.player.Player)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean chkAIDrawback(SpellAbility sa, Player aiPlayer) {
|
public boolean chkAIDrawback(SpellAbility sa, Player aiPlayer) {
|
||||||
return canPlayAI(aiPlayer, sa);
|
return canPlayAI(aiPlayer, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(final Player aiPlayer, final SpellAbility sa, final boolean mandatory) {
|
protected boolean doTriggerAINoCost(final Player aiPlayer, final SpellAbility sa, final boolean mandatory) {
|
||||||
if ("CombustibleGearhulk".equals(sa.getParam("AILogic"))) {
|
if ("CombustibleGearhulk".equals(sa.getParam("AILogic"))) {
|
||||||
for (final Player p : aiPlayer.getOpponents()) {
|
for (final Player p : aiPlayer.getOpponents()) {
|
||||||
if (p.canBeTargetedBy(sa)) {
|
if (p.canBeTargetedBy(sa)) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
sa.getTargets().add(p);
|
sa.getTargets().add(p);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true; // perhaps the opponent(s) had Sigarda, Heron's Grace or another effect giving hexproof in play, still play the creature as 6/6
|
return true; // perhaps the opponent(s) had Sigarda, Heron's Grace or another effect giving hexproof in play, still play the creature as 6/6
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.doTriggerAINoCost(aiPlayer, sa, mandatory);
|
return super.doTriggerAINoCost(aiPlayer, sa, mandatory);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SpellAbility chooseSingleSpellAbility(Player player, SpellAbility sa, List<SpellAbility> spells) {
|
public SpellAbility chooseSingleSpellAbility(Player player, SpellAbility sa, List<SpellAbility> spells) {
|
||||||
Card host = sa.getHostCard();
|
Card host = sa.getHostCard();
|
||||||
final String sourceName = ComputerUtilAbility.getAbilitySourceName(sa);
|
final String sourceName = ComputerUtilAbility.getAbilitySourceName(sa);
|
||||||
final Game game = host.getGame();
|
final Game game = host.getGame();
|
||||||
final Combat combat = game.getCombat();
|
final Combat combat = game.getCombat();
|
||||||
final String logic = sa.getParam("AILogic");
|
final String logic = sa.getParam("AILogic");
|
||||||
if (logic == null) {
|
if (logic == null) {
|
||||||
return spells.get(0);
|
return spells.get(0);
|
||||||
} else if ("Random".equals(logic)) {
|
} else if ("Random".equals(logic)) {
|
||||||
return Aggregates.random(spells);
|
return Aggregates.random(spells);
|
||||||
} else if ("Phasing".equals(logic)) { // Teferi's Realm : keep aggressive
|
} else if ("Phasing".equals(logic)) { // Teferi's Realm : keep aggressive
|
||||||
List<SpellAbility> filtered = Lists.newArrayList(Iterables.filter(spells, new Predicate<SpellAbility>() {
|
List<SpellAbility> filtered = Lists.newArrayList(Iterables.filter(spells, new Predicate<SpellAbility>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(final SpellAbility sp) {
|
public boolean apply(final SpellAbility sp) {
|
||||||
return !sp.getDescription().contains("Creature") && !sp.getDescription().contains("Land");
|
return !sp.getDescription().contains("Creature") && !sp.getDescription().contains("Land");
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
return Aggregates.random(filtered);
|
return Aggregates.random(filtered);
|
||||||
} else if ("PayUnlessCost".equals(logic)) {
|
} else if ("PayUnlessCost".equals(logic)) {
|
||||||
for (final SpellAbility sp : spells) {
|
for (final SpellAbility sp : spells) {
|
||||||
String unlessCost = sp.getParam("UnlessCost");
|
String unlessCost = sp.getParam("UnlessCost");
|
||||||
sp.setActivatingPlayer(sa.getActivatingPlayer());
|
sp.setActivatingPlayer(sa.getActivatingPlayer());
|
||||||
Cost unless = new Cost(unlessCost, false);
|
Cost unless = new Cost(unlessCost, false);
|
||||||
SpellAbility paycost = new SpellAbility.EmptySa(sa.getHostCard(), player);
|
SpellAbility paycost = new SpellAbility.EmptySa(sa.getHostCard(), player);
|
||||||
paycost.setPayCosts(unless);
|
paycost.setPayCosts(unless);
|
||||||
if (ComputerUtilCost.willPayUnlessCost(sp, player, unless, false, new FCollection<Player>(player))
|
if (ComputerUtilCost.willPayUnlessCost(sp, player, unless, false, new FCollection<Player>(player))
|
||||||
&& ComputerUtilCost.canPayCost(paycost, player)) {
|
&& ComputerUtilCost.canPayCost(paycost, player)) {
|
||||||
return sp;
|
return sp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return spells.get(0);
|
return spells.get(0);
|
||||||
} else if ("Khans".equals(logic) || "Dragons".equals(logic)) { // Fate Reforged sieges
|
} else if ("Khans".equals(logic) || "Dragons".equals(logic)) { // Fate Reforged sieges
|
||||||
for (final SpellAbility sp : spells) {
|
for (final SpellAbility sp : spells) {
|
||||||
if (sp.getDescription().equals(logic)) {
|
if (sp.getDescription().equals(logic)) {
|
||||||
return sp;
|
return sp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if ("SelfOthers".equals(logic)) {
|
} else if ("SelfOthers".equals(logic)) {
|
||||||
SpellAbility self = null, others = null;
|
SpellAbility self = null, others = null;
|
||||||
for (final SpellAbility sp : spells) {
|
for (final SpellAbility sp : spells) {
|
||||||
if (sp.getDescription().equals("Self")) {
|
if (sp.getDescription().equals("Self")) {
|
||||||
self = sp;
|
self = sp;
|
||||||
} else {
|
} else {
|
||||||
others = sp;
|
others = sp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
String hostname = host.getName();
|
String hostname = host.getName();
|
||||||
if (hostname.equals("May Civilization Collapse")) {
|
if (hostname.equals("May Civilization Collapse")) {
|
||||||
if (player.getLandsInPlay().isEmpty()) {
|
if (player.getLandsInPlay().isEmpty()) {
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
} else if (hostname.equals("Feed the Machine")) {
|
} else if (hostname.equals("Feed the Machine")) {
|
||||||
if (player.getCreaturesInPlay().isEmpty()) {
|
if (player.getCreaturesInPlay().isEmpty()) {
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
} else if (hostname.equals("Surrender Your Thoughts")) {
|
} else if (hostname.equals("Surrender Your Thoughts")) {
|
||||||
if (player.getCardsIn(ZoneType.Hand).isEmpty()) {
|
if (player.getCardsIn(ZoneType.Hand).isEmpty()) {
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
} else if (hostname.equals("The Fate of the Flammable")) {
|
} else if (hostname.equals("The Fate of the Flammable")) {
|
||||||
if (!player.canLoseLife()) {
|
if (!player.canLoseLife()) {
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return others;
|
return others;
|
||||||
} else if ("Fatespinner".equals(logic)) {
|
} else if ("Fatespinner".equals(logic)) {
|
||||||
SpellAbility skipDraw = null, /*skipMain = null,*/ skipCombat = null;
|
SpellAbility skipDraw = null, /*skipMain = null,*/ skipCombat = null;
|
||||||
for (final SpellAbility sp : spells) {
|
for (final SpellAbility sp : spells) {
|
||||||
if (sp.getDescription().equals("FatespinnerSkipDraw")) {
|
if (sp.getDescription().equals("FatespinnerSkipDraw")) {
|
||||||
skipDraw = sp;
|
skipDraw = sp;
|
||||||
} else if (sp.getDescription().equals("FatespinnerSkipMain")) {
|
} else if (sp.getDescription().equals("FatespinnerSkipMain")) {
|
||||||
//skipMain = sp;
|
//skipMain = sp;
|
||||||
} else {
|
} else {
|
||||||
skipCombat = sp;
|
skipCombat = sp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// FatespinnerSkipDraw,FatespinnerSkipMain,FatespinnerSkipCombat
|
// FatespinnerSkipDraw,FatespinnerSkipMain,FatespinnerSkipCombat
|
||||||
if (player.hasKeyword("Skip your draw step.")) {
|
if (player.hasKeyword("Skip your draw step.")) {
|
||||||
return skipDraw;
|
return skipDraw;
|
||||||
}
|
}
|
||||||
if (player.hasKeyword("Skip your next combat phase.")) {
|
if (player.hasKeyword("Skip your next combat phase.")) {
|
||||||
return skipCombat;
|
return skipCombat;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO If combat is poor, Skip Combat
|
// TODO If combat is poor, Skip Combat
|
||||||
// Todo if hand is empty or mostly empty, skip main phase
|
// Todo if hand is empty or mostly empty, skip main phase
|
||||||
// Todo if hand has gas, skip draw
|
// Todo if hand has gas, skip draw
|
||||||
return Aggregates.random(spells);
|
return Aggregates.random(spells);
|
||||||
|
|
||||||
} else if ("SinProdder".equals(logic)) {
|
} else if ("SinProdder".equals(logic)) {
|
||||||
SpellAbility allow = null, deny = null;
|
SpellAbility allow = null, deny = null;
|
||||||
for (final SpellAbility sp : spells) {
|
for (final SpellAbility sp : spells) {
|
||||||
if (sp.getDescription().equals("Allow")) {
|
if (sp.getDescription().equals("Allow")) {
|
||||||
allow = sp;
|
allow = sp;
|
||||||
} else {
|
} else {
|
||||||
deny = sp;
|
deny = sp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Card imprinted = host.getImprintedCards().getFirst();
|
Card imprinted = host.getImprintedCards().getFirst();
|
||||||
int dmg = imprinted.getCMC();
|
int dmg = imprinted.getCMC();
|
||||||
Player owner = imprinted.getOwner();
|
Player owner = imprinted.getOwner();
|
||||||
|
|
||||||
//useless cards in hand
|
//useless cards in hand
|
||||||
if (imprinted.getName().equals("Bridge from Below") ||
|
if (imprinted.getName().equals("Bridge from Below") ||
|
||||||
imprinted.getName().equals("Haakon, Stromgald Scourge")) {
|
imprinted.getName().equals("Haakon, Stromgald Scourge")) {
|
||||||
return allow;
|
return allow;
|
||||||
}
|
}
|
||||||
|
|
||||||
//bad cards when are thrown from the library to the graveyard, but Yixlid can prevent that
|
//bad cards when are thrown from the library to the graveyard, but Yixlid can prevent that
|
||||||
if (!player.getGame().isCardInPlay("Yixlid Jailer") && (
|
if (!player.getGame().isCardInPlay("Yixlid Jailer") && (
|
||||||
imprinted.getName().equals("Gaea's Blessing") ||
|
imprinted.getName().equals("Gaea's Blessing") ||
|
||||||
imprinted.getName().equals("Narcomoeba"))) {
|
imprinted.getName().equals("Narcomoeba"))) {
|
||||||
return allow;
|
return allow;
|
||||||
}
|
}
|
||||||
|
|
||||||
// milling against Tamiyo is pointless
|
// milling against Tamiyo is pointless
|
||||||
if (owner.isCardInCommand("Emblem - Tamiyo, the Moon Sage")) {
|
if (owner.isCardInCommand("Emblem - Tamiyo, the Moon Sage")) {
|
||||||
return allow;
|
return allow;
|
||||||
}
|
}
|
||||||
|
|
||||||
// milling a land against Gitrog result in card draw
|
// milling a land against Gitrog result in card draw
|
||||||
if (imprinted.isLand() && owner.isCardInPlay("The Gitrog Monster")) {
|
if (imprinted.isLand() && owner.isCardInPlay("The Gitrog Monster")) {
|
||||||
// try to mill owner
|
// try to mill owner
|
||||||
if (owner.getCardsIn(ZoneType.Library).size() < 5) {
|
if (owner.getCardsIn(ZoneType.Library).size() < 5) {
|
||||||
return deny;
|
return deny;
|
||||||
}
|
}
|
||||||
return allow;
|
return allow;
|
||||||
}
|
}
|
||||||
|
|
||||||
// milling a creature against Sidisi result in more creatures
|
// milling a creature against Sidisi result in more creatures
|
||||||
if (imprinted.isCreature() && owner.isCardInPlay("Sidisi, Brood Tyrant")) {
|
if (imprinted.isCreature() && owner.isCardInPlay("Sidisi, Brood Tyrant")) {
|
||||||
return allow;
|
return allow;
|
||||||
}
|
}
|
||||||
|
|
||||||
//if Iona does prevent from casting, allow it to draw
|
//if Iona does prevent from casting, allow it to draw
|
||||||
for (final Card io : player.getCardsIn(ZoneType.Battlefield, "Iona, Shield of Emeria")) {
|
for (final Card io : player.getCardsIn(ZoneType.Battlefield, "Iona, Shield of Emeria")) {
|
||||||
if (CardUtil.getColors(imprinted).hasAnyColor(MagicColor.fromName(io.getChosenColor()))) {
|
if (CardUtil.getColors(imprinted).hasAnyColor(MagicColor.fromName(io.getChosenColor()))) {
|
||||||
return allow;
|
return allow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dmg == 0) {
|
if (dmg == 0) {
|
||||||
// If CMC = 0, mill it!
|
// If CMC = 0, mill it!
|
||||||
return deny;
|
return deny;
|
||||||
} else if (dmg + 3 > player.getLife()) {
|
} else if (dmg + 3 > player.getLife()) {
|
||||||
// if low on life, do nothing.
|
// if low on life, do nothing.
|
||||||
return allow;
|
return allow;
|
||||||
} else if (player.getLife() - dmg > 15) {
|
} else if (player.getLife() - dmg > 15) {
|
||||||
// TODO Check "danger" level of card
|
// TODO Check "danger" level of card
|
||||||
// If lots of life, and card might be dangerous? Mill it!
|
// If lots of life, and card might be dangerous? Mill it!
|
||||||
return deny;
|
return deny;
|
||||||
}
|
}
|
||||||
// if unsure, random?
|
// if unsure, random?
|
||||||
return Aggregates.random(spells);
|
return Aggregates.random(spells);
|
||||||
} else if (logic.startsWith("Fabricate")) {
|
} else if (logic.startsWith("Fabricate")) {
|
||||||
final int n = Integer.valueOf(logic.substring("Fabricate".length()));
|
final int n = Integer.valueOf(logic.substring("Fabricate".length()));
|
||||||
SpellAbility counterSA = spells.get(0), tokenSA = spells.get(1);
|
SpellAbility counterSA = spells.get(0), tokenSA = spells.get(1);
|
||||||
|
|
||||||
// check for something which might prevent the counters to be placed on host
|
// check for something which might prevent the counters to be placed on host
|
||||||
if (!host.canReceiveCounters(CounterType.P1P1)) {
|
if (!host.canReceiveCounters(CounterType.P1P1)) {
|
||||||
return tokenSA;
|
return tokenSA;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if host would leave the play or if host is useless, create tokens
|
// if host would leave the play or if host is useless, create tokens
|
||||||
if (host.hasSVar("EndOfTurnLeavePlay") || ComputerUtilCard.isUselessCreature(player, host)) {
|
if (host.hasSVar("EndOfTurnLeavePlay") || ComputerUtilCard.isUselessCreature(player, host)) {
|
||||||
return tokenSA;
|
return tokenSA;
|
||||||
}
|
}
|
||||||
|
|
||||||
// need a copy for one with extra +1/+1 counter boost,
|
// need a copy for one with extra +1/+1 counter boost,
|
||||||
// without causing triggers to run
|
// without causing triggers to run
|
||||||
final Card copy = CardUtil.getLKICopy(host);
|
final Card copy = CardUtil.getLKICopy(host);
|
||||||
copy.setCounters(CounterType.P1P1, copy.getCounters(CounterType.P1P1) + n);
|
copy.setCounters(CounterType.P1P1, copy.getCounters(CounterType.P1P1) + n);
|
||||||
copy.setZone(host.getZone());
|
copy.setZone(host.getZone());
|
||||||
|
|
||||||
// if host would put into the battlefield attacking
|
// if host would put into the battlefield attacking
|
||||||
if (combat != null && combat.isAttacking(host)) {
|
if (combat != null && combat.isAttacking(host)) {
|
||||||
final Player defender = combat.getDefenderPlayerByAttacker(host);
|
final Player defender = combat.getDefenderPlayerByAttacker(host);
|
||||||
if (defender.canLoseLife() && !ComputerUtilCard.canBeBlockedProfitably(defender, copy)) {
|
if (defender.canLoseLife() && !ComputerUtilCard.canBeBlockedProfitably(defender, copy)) {
|
||||||
return counterSA;
|
return counterSA;
|
||||||
}
|
}
|
||||||
return tokenSA;
|
return tokenSA;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the host has haste and can attack
|
// if the host has haste and can attack
|
||||||
if (CombatUtil.canAttack(copy)) {
|
if (CombatUtil.canAttack(copy)) {
|
||||||
for (final Player opp : player.getOpponents()) {
|
for (final Player opp : player.getOpponents()) {
|
||||||
if (CombatUtil.canAttack(copy, opp) &&
|
if (CombatUtil.canAttack(copy, opp) &&
|
||||||
opp.canLoseLife() &&
|
opp.canLoseLife() &&
|
||||||
!ComputerUtilCard.canBeBlockedProfitably(opp, copy))
|
!ComputerUtilCard.canBeBlockedProfitably(opp, copy))
|
||||||
return counterSA;
|
return counterSA;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO check for trigger to turn token ETB into +1/+1 counter for host
|
// TODO check for trigger to turn token ETB into +1/+1 counter for host
|
||||||
// TODO check for trigger to turn token ETB into damage or life loss for opponent
|
// TODO check for trigger to turn token ETB into damage or life loss for opponent
|
||||||
// in this cases Token might be prefered even if they would not survive
|
// in this cases Token might be prefered even if they would not survive
|
||||||
final Card tokenCard = TokenAi.spawnToken(player, tokenSA, true);
|
final Card tokenCard = TokenAi.spawnToken(player, tokenSA, true);
|
||||||
|
|
||||||
// Token would not survive
|
// Token would not survive
|
||||||
if (tokenCard.getNetToughness() < 1) {
|
if (tokenCard.getNetToughness() < 1) {
|
||||||
return counterSA;
|
return counterSA;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Special Card logic, this one try to median its power with the number of artifacts
|
// Special Card logic, this one try to median its power with the number of artifacts
|
||||||
if ("Marionette Master".equals(sourceName)) {
|
if ("Marionette Master".equals(sourceName)) {
|
||||||
CardCollection list = CardLists.filter(player.getCardsIn(ZoneType.Battlefield), Presets.ARTIFACTS);
|
CardCollection list = CardLists.filter(player.getCardsIn(ZoneType.Battlefield), Presets.ARTIFACTS);
|
||||||
return list.size() >= copy.getNetPower() ? counterSA : tokenSA;
|
return list.size() >= copy.getNetPower() ? counterSA : tokenSA;
|
||||||
} else if ("Cultivator of Blades".equals(sourceName)) {
|
} else if ("Cultivator of Blades".equals(sourceName)) {
|
||||||
// Cultivator does try to median with number of Creatures
|
// Cultivator does try to median with number of Creatures
|
||||||
CardCollection list = player.getCreaturesInPlay();
|
CardCollection list = player.getCreaturesInPlay();
|
||||||
return list.size() >= copy.getNetPower() ? counterSA : tokenSA;
|
return list.size() >= copy.getNetPower() ? counterSA : tokenSA;
|
||||||
}
|
}
|
||||||
|
|
||||||
// evaluate Creature with +1/+1
|
// evaluate Creature with +1/+1
|
||||||
int evalCounter = ComputerUtilCard.evaluateCreature(copy);
|
int evalCounter = ComputerUtilCard.evaluateCreature(copy);
|
||||||
|
|
||||||
final CardCollection tokenList = new CardCollection(host);
|
final CardCollection tokenList = new CardCollection(host);
|
||||||
for (int i = 0; i < n; ++i) {
|
for (int i = 0; i < n; ++i) {
|
||||||
tokenList.add(TokenAi.spawnToken(player, tokenSA));
|
tokenList.add(TokenAi.spawnToken(player, tokenSA));
|
||||||
}
|
}
|
||||||
|
|
||||||
// evaluate Host with Tokens
|
// evaluate Host with Tokens
|
||||||
int evalToken = ComputerUtilCard.evaluateCreatureList(tokenList);
|
int evalToken = ComputerUtilCard.evaluateCreatureList(tokenList);
|
||||||
|
|
||||||
return evalToken >= evalCounter ? tokenSA : counterSA;
|
return evalToken >= evalCounter ? tokenSA : counterSA;
|
||||||
} else if ("CombustibleGearhulk".equals(logic)) {
|
} else if ("CombustibleGearhulk".equals(logic)) {
|
||||||
Player controller = sa.getActivatingPlayer();
|
Player controller = sa.getActivatingPlayer();
|
||||||
List<ZoneType> zones = ZoneType.listValueOf("Graveyard, Battlefield, Exile");
|
List<ZoneType> zones = ZoneType.listValueOf("Graveyard, Battlefield, Exile");
|
||||||
int life = player.getLife();
|
int life = player.getLife();
|
||||||
CardCollectionView revealedCards = controller.getCardsIn(zones);
|
CardCollectionView revealedCards = controller.getCardsIn(zones);
|
||||||
|
|
||||||
if (revealedCards.size() < 5) {
|
if (revealedCards.size() < 5) {
|
||||||
// Not very many revealed cards, just guess based on lifetotal
|
// Not very many revealed cards, just guess based on lifetotal
|
||||||
return life < 7 ? spells.get(0) : spells.get(1);
|
return life < 7 ? spells.get(0) : spells.get(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
int totalCMC = 0;
|
int totalCMC = 0;
|
||||||
for(Card c : revealedCards) {
|
for(Card c : revealedCards) {
|
||||||
totalCMC += c.getCMC();
|
totalCMC += c.getCMC();
|
||||||
}
|
}
|
||||||
|
|
||||||
int bestGuessDamage = totalCMC * 3 / revealedCards.size();
|
int bestGuessDamage = totalCMC * 3 / revealedCards.size();
|
||||||
return life <= bestGuessDamage ? spells.get(0) : spells.get(1);
|
return life <= bestGuessDamage ? spells.get(0) : spells.get(1);
|
||||||
} else if ("Pump".equals(logic) || "BestOption".equals(logic)) {
|
} else if ("Pump".equals(logic) || "BestOption".equals(logic)) {
|
||||||
List<SpellAbility> filtered = Lists.newArrayList();
|
List<SpellAbility> filtered = Lists.newArrayList();
|
||||||
// filter first for the spells which can be done
|
// filter first for the spells which can be done
|
||||||
for (SpellAbility sp : spells) {
|
for (SpellAbility sp : spells) {
|
||||||
if (SpellApiToAi.Converter.get(sp.getApi()).canPlayAIWithSubs(player, sp)) {
|
if (SpellApiToAi.Converter.get(sp.getApi()).canPlayAIWithSubs(player, sp)) {
|
||||||
filtered.add(sp);
|
filtered.add(sp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO find better way to check
|
// TODO find better way to check
|
||||||
if (!filtered.isEmpty()) {
|
if (!filtered.isEmpty()) {
|
||||||
return filtered.get(0);
|
return filtered.get(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return spells.get(0); // return first choice if no logic found
|
return spells.get(0); // return first choice if no logic found
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,36 +1,36 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import forge.ai.ComputerUtil;
|
import forge.ai.ComputerUtil;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.TargetRestrictions;
|
import forge.game.spellability.TargetRestrictions;
|
||||||
import forge.util.MyRandom;
|
import forge.util.MyRandom;
|
||||||
|
|
||||||
public class ChooseNumberAi extends SpellAbilityAi {
|
public class ChooseNumberAi extends SpellAbilityAi {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
||||||
if (!sa.hasParam("AILogic")) {
|
if (!sa.hasParam("AILogic")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
TargetRestrictions tgt = sa.getTargetRestrictions();
|
TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
if (tgt != null) {
|
if (tgt != null) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
Player opp = ComputerUtil.getOpponentFor(aiPlayer);
|
Player opp = ComputerUtil.getOpponentFor(aiPlayer);
|
||||||
if (sa.canTarget(opp)) {
|
if (sa.canTarget(opp)) {
|
||||||
sa.getTargets().add(opp);
|
sa.getTargets().add(opp);
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
boolean chance = MyRandom.getRandom().nextFloat() <= Math.pow(.6667, sa.getActivationsThisTurn());
|
boolean chance = MyRandom.getRandom().nextFloat() <= Math.pow(.6667, sa.getActivationsThisTurn());
|
||||||
return chance;
|
return chance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
||||||
return mandatory || canPlayAI(ai, sa);
|
return mandatory || canPlayAI(ai, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,81 +1,81 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import forge.ai.ComputerUtil;
|
import forge.ai.ComputerUtil;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class ChoosePlayerAi extends SpellAbilityAi {
|
public class ChoosePlayerAi extends SpellAbilityAi {
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
||||||
return canPlayAI(ai, sa);
|
return canPlayAI(ai, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
||||||
return canPlayAI(ai, sa);
|
return canPlayAI(ai, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> choices) {
|
public Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> choices) {
|
||||||
Player chosen = null;
|
Player chosen = null;
|
||||||
if ("Curse".equals(sa.getParam("AILogic"))) {
|
if ("Curse".equals(sa.getParam("AILogic"))) {
|
||||||
for (Player pc : choices) {
|
for (Player pc : choices) {
|
||||||
if (pc.isOpponentOf(ai)) {
|
if (pc.isOpponentOf(ai)) {
|
||||||
chosen = pc;
|
chosen = pc;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (chosen == null) {
|
if (chosen == null) {
|
||||||
chosen = Iterables.getFirst(choices, null);
|
chosen = Iterables.getFirst(choices, null);
|
||||||
System.out.println("No good curse choices. Picking first available: " + chosen);
|
System.out.println("No good curse choices. Picking first available: " + chosen);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ("Pump".equals(sa.getParam("AILogic"))) {
|
else if ("Pump".equals(sa.getParam("AILogic"))) {
|
||||||
chosen = Iterables.contains(choices, ai) ? ai : Iterables.getFirst(choices, null);
|
chosen = Iterables.contains(choices, ai) ? ai : Iterables.getFirst(choices, null);
|
||||||
}
|
}
|
||||||
else if ("BestAllyBoardPosition".equals(sa.getParam("AILogic"))) {
|
else if ("BestAllyBoardPosition".equals(sa.getParam("AILogic"))) {
|
||||||
List<Player> prefChoices = Lists.newArrayList(choices);
|
List<Player> prefChoices = Lists.newArrayList(choices);
|
||||||
prefChoices.removeAll(ai.getOpponents());
|
prefChoices.removeAll(ai.getOpponents());
|
||||||
if (!prefChoices.isEmpty()) {
|
if (!prefChoices.isEmpty()) {
|
||||||
chosen = ComputerUtil.evaluateBoardPosition(prefChoices);
|
chosen = ComputerUtil.evaluateBoardPosition(prefChoices);
|
||||||
}
|
}
|
||||||
if (chosen == null) {
|
if (chosen == null) {
|
||||||
chosen = Iterables.getFirst(choices, null);
|
chosen = Iterables.getFirst(choices, null);
|
||||||
System.out.println("No good curse choices. Picking first available: " + chosen);
|
System.out.println("No good curse choices. Picking first available: " + chosen);
|
||||||
}
|
}
|
||||||
} else if ("MostCardsInHand".equals(sa.getParam("AILogic"))) {
|
} else if ("MostCardsInHand".equals(sa.getParam("AILogic"))) {
|
||||||
int cardsInHand = 0;
|
int cardsInHand = 0;
|
||||||
for (final Player p : choices) {
|
for (final Player p : choices) {
|
||||||
int hand = p.getCardsIn(ZoneType.Hand).size();
|
int hand = p.getCardsIn(ZoneType.Hand).size();
|
||||||
if (hand >= cardsInHand) {
|
if (hand >= cardsInHand) {
|
||||||
chosen = p;
|
chosen = p;
|
||||||
cardsInHand = hand;
|
cardsInHand = hand;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if ("LeastCreatures".equals(sa.getParam("AILogic"))) {
|
} else if ("LeastCreatures".equals(sa.getParam("AILogic"))) {
|
||||||
int creats = 50;
|
int creats = 50;
|
||||||
for (final Player p : choices) {
|
for (final Player p : choices) {
|
||||||
int curr = p.getCreaturesInPlay().size();
|
int curr = p.getCreaturesInPlay().size();
|
||||||
if (curr <= creats) {
|
if (curr <= creats) {
|
||||||
chosen = p;
|
chosen = p;
|
||||||
creats = curr;
|
creats = curr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
System.out.println("Default player choice logic.");
|
System.out.println("Default player choice logic.");
|
||||||
chosen = Iterables.contains(choices, ai) ? ai : Iterables.getFirst(choices, null);
|
chosen = Iterables.contains(choices, ai) ? ai : Iterables.getFirst(choices, null);
|
||||||
}
|
}
|
||||||
return chosen;
|
return chosen;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,196 +1,196 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
import forge.ai.ComputerUtil;
|
import forge.ai.ComputerUtil;
|
||||||
import forge.ai.ComputerUtilCard;
|
import forge.ai.ComputerUtilCard;
|
||||||
import forge.ai.ComputerUtilCombat;
|
import forge.ai.ComputerUtilCombat;
|
||||||
import forge.ai.ComputerUtilCost;
|
import forge.ai.ComputerUtilCost;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.GameObject;
|
import forge.game.GameObject;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.ability.ApiType;
|
import forge.game.ability.ApiType;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardCollectionView;
|
import forge.game.card.CardCollectionView;
|
||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
import forge.game.combat.Combat;
|
import forge.game.combat.Combat;
|
||||||
import forge.game.cost.Cost;
|
import forge.game.cost.Cost;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.SpellAbilityStackInstance;
|
import forge.game.spellability.SpellAbilityStackInstance;
|
||||||
import forge.game.spellability.TargetRestrictions;
|
import forge.game.spellability.TargetRestrictions;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
|
|
||||||
public class ChooseSourceAi extends SpellAbilityAi {
|
public class ChooseSourceAi extends SpellAbilityAi {
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see forge.card.abilityfactory.SpellAiLogic#canPlayAI(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility)
|
* @see forge.card.abilityfactory.SpellAiLogic#canPlayAI(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(final Player ai, SpellAbility sa) {
|
protected boolean canPlayAI(final Player ai, SpellAbility sa) {
|
||||||
// TODO: AI Support! Currently this is copied from AF ChooseCard.
|
// TODO: AI Support! Currently this is copied from AF ChooseCard.
|
||||||
// When implementing AI, I believe AI also needs to be made aware of the damage sources chosen
|
// When implementing AI, I believe AI also needs to be made aware of the damage sources chosen
|
||||||
// to be prevented (e.g. so the AI doesn't attack with a creature that will not deal any damage
|
// to be prevented (e.g. so the AI doesn't attack with a creature that will not deal any damage
|
||||||
// to the player because a CoP was pre-activated on it - unless, of course, there's another
|
// to the player because a CoP was pre-activated on it - unless, of course, there's another
|
||||||
// possible reason to attack with that creature).
|
// possible reason to attack with that creature).
|
||||||
final Card host = sa.getHostCard();
|
final Card host = sa.getHostCard();
|
||||||
final Cost abCost = sa.getPayCosts();
|
final Cost abCost = sa.getPayCosts();
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
|
|
||||||
if (abCost != null) {
|
if (abCost != null) {
|
||||||
// AI currently disabled for these costs
|
// AI currently disabled for these costs
|
||||||
if (!ComputerUtilCost.checkLifeCost(ai, abCost, source, 4, sa)) {
|
if (!ComputerUtilCost.checkLifeCost(ai, abCost, source, 4, sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ComputerUtilCost.checkDiscardCost(ai, abCost, source)) {
|
if (!ComputerUtilCost.checkDiscardCost(ai, abCost, source)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ComputerUtilCost.checkSacrificeCost(ai, abCost, source, sa)) {
|
if (!ComputerUtilCost.checkSacrificeCost(ai, abCost, source, sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ComputerUtilCost.checkRemoveCounterCost(abCost, source)) {
|
if (!ComputerUtilCost.checkRemoveCounterCost(abCost, source)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
if (tgt != null) {
|
if (tgt != null) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
Player opp = ComputerUtil.getOpponentFor(ai);
|
Player opp = ComputerUtil.getOpponentFor(ai);
|
||||||
if (sa.canTarget(opp)) {
|
if (sa.canTarget(opp)) {
|
||||||
sa.getTargets().add(opp);
|
sa.getTargets().add(opp);
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (sa.hasParam("AILogic")) {
|
if (sa.hasParam("AILogic")) {
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
if (sa.getParam("AILogic").equals("NeedsPrevention")) {
|
if (sa.getParam("AILogic").equals("NeedsPrevention")) {
|
||||||
if (!game.getStack().isEmpty()) {
|
if (!game.getStack().isEmpty()) {
|
||||||
final SpellAbility topStack = game.getStack().peekAbility();
|
final SpellAbility topStack = game.getStack().peekAbility();
|
||||||
if (sa.hasParam("Choices") && !topStack.getHostCard().isValid(sa.getParam("Choices"), ai, source, sa)) {
|
if (sa.hasParam("Choices") && !topStack.getHostCard().isValid(sa.getParam("Choices"), ai, source, sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
final ApiType threatApi = topStack.getApi();
|
final ApiType threatApi = topStack.getApi();
|
||||||
if (threatApi != ApiType.DealDamage && threatApi != ApiType.DamageAll) {
|
if (threatApi != ApiType.DealDamage && threatApi != ApiType.DamageAll) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Card threatSource = topStack.getHostCard();
|
final Card threatSource = topStack.getHostCard();
|
||||||
List<? extends GameObject> objects = getTargets(topStack);
|
List<? extends GameObject> objects = getTargets(topStack);
|
||||||
if (!topStack.usesTargeting() && topStack.hasParam("ValidPlayers") && !topStack.hasParam("Defined")) {
|
if (!topStack.usesTargeting() && topStack.hasParam("ValidPlayers") && !topStack.hasParam("Defined")) {
|
||||||
objects = AbilityUtils.getDefinedPlayers(threatSource, topStack.getParam("ValidPlayers"), topStack);
|
objects = AbilityUtils.getDefinedPlayers(threatSource, topStack.getParam("ValidPlayers"), topStack);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!objects.contains(ai) || topStack.hasParam("NoPrevention")) {
|
if (!objects.contains(ai) || topStack.hasParam("NoPrevention")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
int dmg = AbilityUtils.calculateAmount(threatSource, topStack.getParam("NumDmg"), topStack);
|
int dmg = AbilityUtils.calculateAmount(threatSource, topStack.getParam("NumDmg"), topStack);
|
||||||
if (ComputerUtilCombat.predictDamageTo(ai, dmg, threatSource, false) <= 0) {
|
if (ComputerUtilCombat.predictDamageTo(ai, dmg, threatSource, false) <= 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (game.getPhaseHandler().getPhase() != PhaseType.COMBAT_DECLARE_BLOCKERS) {
|
if (game.getPhaseHandler().getPhase() != PhaseType.COMBAT_DECLARE_BLOCKERS) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
CardCollectionView choices = game.getCardsIn(ZoneType.Battlefield);
|
CardCollectionView choices = game.getCardsIn(ZoneType.Battlefield);
|
||||||
if (sa.hasParam("Choices")) {
|
if (sa.hasParam("Choices")) {
|
||||||
choices = CardLists.getValidCards(choices, sa.getParam("Choices"), host.getController(), host);
|
choices = CardLists.getValidCards(choices, sa.getParam("Choices"), host.getController(), host);
|
||||||
}
|
}
|
||||||
final Combat combat = game.getCombat();
|
final Combat combat = game.getCombat();
|
||||||
choices = CardLists.filter(choices, new Predicate<Card>() {
|
choices = CardLists.filter(choices, new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(final Card c) {
|
public boolean apply(final Card c) {
|
||||||
if (combat == null || !combat.isAttacking(c, ai) || !combat.isUnblocked(c)) {
|
if (combat == null || !combat.isAttacking(c, ai) || !combat.isUnblocked(c)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return ComputerUtilCombat.damageIfUnblocked(c, ai, combat, true) > 0;
|
return ComputerUtilCombat.damageIfUnblocked(c, ai, combat, true) > 0;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (choices.isEmpty()) {
|
if (choices.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Card chooseSingleCard(final Player aiChoser, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
|
public Card chooseSingleCard(final Player aiChoser, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
|
||||||
if ("NeedsPrevention".equals(sa.getParam("AILogic"))) {
|
if ("NeedsPrevention".equals(sa.getParam("AILogic"))) {
|
||||||
final Player ai = sa.getActivatingPlayer();
|
final Player ai = sa.getActivatingPlayer();
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
if (!game.getStack().isEmpty()) {
|
if (!game.getStack().isEmpty()) {
|
||||||
Card choseCard = chooseCardOnStack(sa, ai, game);
|
Card choseCard = chooseCardOnStack(sa, ai, game);
|
||||||
if (choseCard != null) {
|
if (choseCard != null) {
|
||||||
return choseCard;
|
return choseCard;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final Combat combat = game.getCombat();
|
final Combat combat = game.getCombat();
|
||||||
|
|
||||||
List<Card> permanentSources = CardLists.filter(options, new Predicate<Card>() {
|
List<Card> permanentSources = CardLists.filter(options, new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(final Card c) {
|
public boolean apply(final Card c) {
|
||||||
if (c == null || c.getZone() == null || c.getZone().getZoneType() != ZoneType.Battlefield
|
if (c == null || c.getZone() == null || c.getZone().getZoneType() != ZoneType.Battlefield
|
||||||
|| combat == null || !combat.isAttacking(c, ai) || !combat.isUnblocked(c)) {
|
|| combat == null || !combat.isAttacking(c, ai) || !combat.isUnblocked(c)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return ComputerUtilCombat.damageIfUnblocked(c, ai, combat, true) > 0;
|
return ComputerUtilCombat.damageIfUnblocked(c, ai, combat, true) > 0;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return ComputerUtilCard.getBestCreatureAI(permanentSources);
|
return ComputerUtilCard.getBestCreatureAI(permanentSources);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
return ComputerUtilCard.getBestAI(options);
|
return ComputerUtilCard.getBestAI(options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Card chooseCardOnStack(SpellAbility sa, Player ai, Game game) {
|
private Card chooseCardOnStack(SpellAbility sa, Player ai, Game game) {
|
||||||
for (SpellAbilityStackInstance si : game.getStack()) {
|
for (SpellAbilityStackInstance si : game.getStack()) {
|
||||||
final Card source = si.getSourceCard();
|
final Card source = si.getSourceCard();
|
||||||
final SpellAbility abilityOnStack = si.getSpellAbility(true);
|
final SpellAbility abilityOnStack = si.getSpellAbility(true);
|
||||||
|
|
||||||
if (sa.hasParam("Choices") && !abilityOnStack.getHostCard().isValid(sa.getParam("Choices"), ai, sa.getHostCard(), sa)) {
|
if (sa.hasParam("Choices") && !abilityOnStack.getHostCard().isValid(sa.getParam("Choices"), ai, sa.getHostCard(), sa)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
final ApiType threatApi = abilityOnStack.getApi();
|
final ApiType threatApi = abilityOnStack.getApi();
|
||||||
if (threatApi != ApiType.DealDamage && threatApi != ApiType.DamageAll) {
|
if (threatApi != ApiType.DealDamage && threatApi != ApiType.DamageAll) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<? extends GameObject> objects = getTargets(abilityOnStack);
|
List<? extends GameObject> objects = getTargets(abilityOnStack);
|
||||||
|
|
||||||
if (!abilityOnStack.usesTargeting() && !abilityOnStack.hasParam("Defined") && abilityOnStack.hasParam("ValidPlayers"))
|
if (!abilityOnStack.usesTargeting() && !abilityOnStack.hasParam("Defined") && abilityOnStack.hasParam("ValidPlayers"))
|
||||||
objects = AbilityUtils.getDefinedPlayers(source, abilityOnStack.getParam("ValidPlayers"), abilityOnStack);
|
objects = AbilityUtils.getDefinedPlayers(source, abilityOnStack.getParam("ValidPlayers"), abilityOnStack);
|
||||||
|
|
||||||
if (!objects.contains(ai) || abilityOnStack.hasParam("NoPrevention")) {
|
if (!objects.contains(ai) || abilityOnStack.hasParam("NoPrevention")) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
int dmg = AbilityUtils.calculateAmount(source, abilityOnStack.getParam("NumDmg"), abilityOnStack);
|
int dmg = AbilityUtils.calculateAmount(source, abilityOnStack.getParam("NumDmg"), abilityOnStack);
|
||||||
if (ComputerUtilCombat.predictDamageTo(ai, dmg, source, false) <= 0) {
|
if (ComputerUtilCombat.predictDamageTo(ai, dmg, source, false) <= 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
return source;
|
return source;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<GameObject> getTargets(final SpellAbility sa) {
|
private static List<GameObject> getTargets(final SpellAbility sa) {
|
||||||
return sa.usesTargeting() && (!sa.hasParam("Defined"))
|
return sa.usesTargeting() && (!sa.hasParam("Defined"))
|
||||||
? Lists.newArrayList(sa.getTargets().getTargets())
|
? Lists.newArrayList(sa.getTargets().getTargets())
|
||||||
: AbilityUtils.getDefinedObjects(sa.getHostCard(), sa.getParam("Defined"), sa);
|
: AbilityUtils.getDefinedObjects(sa.getHostCard(), sa.getParam("Defined"), sa);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,130 +1,130 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import com.google.common.base.Predicates;
|
import com.google.common.base.Predicates;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import forge.ai.AiCardMemory;
|
import forge.ai.AiCardMemory;
|
||||||
import forge.ai.ComputerUtilAbility;
|
import forge.ai.ComputerUtilAbility;
|
||||||
import forge.ai.ComputerUtilCard;
|
import forge.ai.ComputerUtilCard;
|
||||||
import forge.ai.ComputerUtilMana;
|
import forge.ai.ComputerUtilMana;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.card.CardType;
|
import forge.card.CardType;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardCollection;
|
import forge.game.card.CardCollection;
|
||||||
import forge.game.card.CardCollectionView;
|
import forge.game.card.CardCollectionView;
|
||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
import forge.game.card.CardPredicates;
|
import forge.game.card.CardPredicates;
|
||||||
import forge.game.keyword.Keyword;
|
import forge.game.keyword.Keyword;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
import forge.util.Aggregates;
|
import forge.util.Aggregates;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class ChooseTypeAi extends SpellAbilityAi {
|
public class ChooseTypeAi extends SpellAbilityAi {
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
||||||
if (!sa.hasParam("AILogic")) {
|
if (!sa.hasParam("AILogic")) {
|
||||||
return false;
|
return false;
|
||||||
} else if ("MostProminentComputerControls".equals(sa.getParam("AILogic"))) {
|
} else if ("MostProminentComputerControls".equals(sa.getParam("AILogic"))) {
|
||||||
if (ComputerUtilAbility.getAbilitySourceName(sa).equals("Mirror Entity Avatar")) {
|
if (ComputerUtilAbility.getAbilitySourceName(sa).equals("Mirror Entity Avatar")) {
|
||||||
return doMirrorEntityLogic(aiPlayer, sa);
|
return doMirrorEntityLogic(aiPlayer, sa);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return doTriggerAINoCost(aiPlayer, sa, false);
|
return doTriggerAINoCost(aiPlayer, sa, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean doMirrorEntityLogic(Player aiPlayer, SpellAbility sa) {
|
private boolean doMirrorEntityLogic(Player aiPlayer, SpellAbility sa) {
|
||||||
if (AiCardMemory.isRememberedCard(aiPlayer, sa.getHostCard(), AiCardMemory.MemorySet.ANIMATED_THIS_TURN)) {
|
if (AiCardMemory.isRememberedCard(aiPlayer, sa.getHostCard(), AiCardMemory.MemorySet.ANIMATED_THIS_TURN)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!aiPlayer.getGame().getPhaseHandler().is(PhaseType.MAIN1, aiPlayer)) {
|
if (!aiPlayer.getGame().getPhaseHandler().is(PhaseType.MAIN1, aiPlayer)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
CardCollectionView otb = aiPlayer.getCardsIn(ZoneType.Battlefield);
|
CardCollectionView otb = aiPlayer.getCardsIn(ZoneType.Battlefield);
|
||||||
List<String> valid = Lists.newArrayList(CardType.getAllCreatureTypes());
|
List<String> valid = Lists.newArrayList(CardType.getAllCreatureTypes());
|
||||||
|
|
||||||
String chosenType = ComputerUtilCard.getMostProminentType(otb, valid);
|
String chosenType = ComputerUtilCard.getMostProminentType(otb, valid);
|
||||||
if (chosenType.isEmpty()) {
|
if (chosenType.isEmpty()) {
|
||||||
// Account for the situation when only changelings are on the battlefield
|
// Account for the situation when only changelings are on the battlefield
|
||||||
boolean allChangeling = false;
|
boolean allChangeling = false;
|
||||||
for (Card c : otb) {
|
for (Card c : otb) {
|
||||||
if (c.isCreature() && c.hasStartOfKeyword(Keyword.CHANGELING.toString())) {
|
if (c.isCreature() && c.hasStartOfKeyword(Keyword.CHANGELING.toString())) {
|
||||||
chosenType = Aggregates.random(valid); // just choose a random type for changelings
|
chosenType = Aggregates.random(valid); // just choose a random type for changelings
|
||||||
allChangeling = true;
|
allChangeling = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!allChangeling) {
|
if (!allChangeling) {
|
||||||
// Still empty, probably no creatures on board
|
// Still empty, probably no creatures on board
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int maxX = ComputerUtilMana.determineMaxAffordableX(aiPlayer, sa);
|
int maxX = ComputerUtilMana.determineMaxAffordableX(aiPlayer, sa);
|
||||||
int avgPower = 0;
|
int avgPower = 0;
|
||||||
|
|
||||||
// predict the opposition
|
// predict the opposition
|
||||||
CardCollection oppCreatures = CardLists.filter(aiPlayer.getOpponents().getCreaturesInPlay(), CardPredicates.Presets.UNTAPPED);
|
CardCollection oppCreatures = CardLists.filter(aiPlayer.getOpponents().getCreaturesInPlay(), CardPredicates.Presets.UNTAPPED);
|
||||||
int maxOppPower = 0;
|
int maxOppPower = 0;
|
||||||
int maxOppToughness = 0;
|
int maxOppToughness = 0;
|
||||||
int oppUsefulCreatures = 0;
|
int oppUsefulCreatures = 0;
|
||||||
|
|
||||||
for (Card oppCre : oppCreatures) {
|
for (Card oppCre : oppCreatures) {
|
||||||
if (ComputerUtilCard.isUselessCreature(aiPlayer, oppCre)) {
|
if (ComputerUtilCard.isUselessCreature(aiPlayer, oppCre)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (oppCre.getNetPower() > maxOppPower) {
|
if (oppCre.getNetPower() > maxOppPower) {
|
||||||
maxOppPower = oppCre.getNetPower();
|
maxOppPower = oppCre.getNetPower();
|
||||||
}
|
}
|
||||||
if (oppCre.getNetToughness() > maxOppToughness) {
|
if (oppCre.getNetToughness() > maxOppToughness) {
|
||||||
maxOppToughness = oppCre.getNetToughness();
|
maxOppToughness = oppCre.getNetToughness();
|
||||||
}
|
}
|
||||||
oppUsefulCreatures++;
|
oppUsefulCreatures++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (maxX > 1) {
|
if (maxX > 1) {
|
||||||
CardCollection cre = CardLists.filter(aiPlayer.getCardsIn(ZoneType.Battlefield),
|
CardCollection cre = CardLists.filter(aiPlayer.getCardsIn(ZoneType.Battlefield),
|
||||||
Predicates.and(CardPredicates.isType(chosenType), CardPredicates.Presets.UNTAPPED));
|
Predicates.and(CardPredicates.isType(chosenType), CardPredicates.Presets.UNTAPPED));
|
||||||
if (!cre.isEmpty()) {
|
if (!cre.isEmpty()) {
|
||||||
for (Card c: cre) {
|
for (Card c: cre) {
|
||||||
avgPower += c.getNetPower();
|
avgPower += c.getNetPower();
|
||||||
}
|
}
|
||||||
avgPower /= cre.size();
|
avgPower /= cre.size();
|
||||||
|
|
||||||
boolean overpower = cre.size() > oppUsefulCreatures;
|
boolean overpower = cre.size() > oppUsefulCreatures;
|
||||||
if (!overpower) {
|
if (!overpower) {
|
||||||
maxX = Math.max(0, maxX - 3); // conserve some mana unless the board position looks overpowering
|
maxX = Math.max(0, maxX - 3); // conserve some mana unless the board position looks overpowering
|
||||||
}
|
}
|
||||||
|
|
||||||
if (maxX > avgPower && maxX > maxOppPower && maxX >= maxOppToughness) {
|
if (maxX > avgPower && maxX > maxOppPower && maxX >= maxOppToughness) {
|
||||||
sa.setSVar("PayX", String.valueOf(maxX));
|
sa.setSVar("PayX", String.valueOf(maxX));
|
||||||
AiCardMemory.rememberCard(aiPlayer, sa.getHostCard(), AiCardMemory.MemorySet.ANIMATED_THIS_TURN);
|
AiCardMemory.rememberCard(aiPlayer, sa.getHostCard(), AiCardMemory.MemorySet.ANIMATED_THIS_TURN);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
||||||
if (sa.usesTargeting()) {
|
if (sa.usesTargeting()) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
sa.getTargets().add(ai);
|
sa.getTargets().add(ai);
|
||||||
} else {
|
} else {
|
||||||
for (final Player p : AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("Defined"), sa)) {
|
for (final Player p : AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("Defined"), sa)) {
|
||||||
if (p.isOpponentOf(ai) && !mandatory) {
|
if (p.isOpponentOf(ai) && !mandatory) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,110 +1,110 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
|
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import forge.ai.ComputerUtilCard;
|
import forge.ai.ComputerUtilCard;
|
||||||
|
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardCollectionView;
|
import forge.game.card.CardCollectionView;
|
||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
import forge.game.card.CardPredicates;
|
import forge.game.card.CardPredicates;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.player.PlayerCollection;
|
import forge.game.player.PlayerCollection;
|
||||||
import forge.game.player.PlayerPredicates;
|
import forge.game.player.PlayerPredicates;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
|
|
||||||
public class ClashAi extends SpellAbilityAi {
|
public class ClashAi extends SpellAbilityAi {
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see forge.card.abilityfactory.SpellAiLogic#doTriggerAINoCost(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility, boolean)
|
* @see forge.card.abilityfactory.SpellAiLogic#doTriggerAINoCost(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility, boolean)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
|
||||||
boolean legalAction = true;
|
boolean legalAction = true;
|
||||||
|
|
||||||
if (sa.usesTargeting()) {
|
if (sa.usesTargeting()) {
|
||||||
legalAction = selectTarget(aiPlayer, sa);
|
legalAction = selectTarget(aiPlayer, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
return legalAction;
|
return legalAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
*
|
*
|
||||||
* @see forge.ai.SpellAbilityAi#checkApiLogic(forge.game.player.Player,
|
* @see forge.ai.SpellAbilityAi#checkApiLogic(forge.game.player.Player,
|
||||||
* forge.game.spellability.SpellAbility)
|
* forge.game.spellability.SpellAbility)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkApiLogic(Player ai, SpellAbility sa) {
|
protected boolean checkApiLogic(Player ai, SpellAbility sa) {
|
||||||
boolean legalAction = true;
|
boolean legalAction = true;
|
||||||
|
|
||||||
if (sa.usesTargeting()) {
|
if (sa.usesTargeting()) {
|
||||||
legalAction = selectTarget(ai, sa);
|
legalAction = selectTarget(ai, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
return legalAction;
|
return legalAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
*
|
*
|
||||||
* @see forge.ai.SpellAbilityAi#chooseSinglePlayer(forge.game.player.Player,
|
* @see forge.ai.SpellAbilityAi#chooseSinglePlayer(forge.game.player.Player,
|
||||||
* forge.game.spellability.SpellAbility, java.lang.Iterable)
|
* forge.game.spellability.SpellAbility, java.lang.Iterable)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options) {
|
protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options) {
|
||||||
for (Player p : options) {
|
for (Player p : options) {
|
||||||
if (p.getCardsIn(ZoneType.Library).size() == 0)
|
if (p.getCardsIn(ZoneType.Library).size() == 0)
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
CardCollectionView col = ai.getCardsIn(ZoneType.Library);
|
CardCollectionView col = ai.getCardsIn(ZoneType.Library);
|
||||||
if (!col.isEmpty() && col.getFirst().mayPlayerLook(ai)) {
|
if (!col.isEmpty() && col.getFirst().mayPlayerLook(ai)) {
|
||||||
final Card top = col.get(0);
|
final Card top = col.get(0);
|
||||||
for (Player p : options) {
|
for (Player p : options) {
|
||||||
final Card oppTop = p.getCardsIn(ZoneType.Library).getFirst();
|
final Card oppTop = p.getCardsIn(ZoneType.Library).getFirst();
|
||||||
// TODO add logic for SplitCards
|
// TODO add logic for SplitCards
|
||||||
if (top.getCMC() > oppTop.getCMC()) {
|
if (top.getCMC() > oppTop.getCMC()) {
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Iterables.getFirst(options, null);
|
return Iterables.getFirst(options, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean selectTarget(Player ai, SpellAbility sa) {
|
private boolean selectTarget(Player ai, SpellAbility sa) {
|
||||||
String valid = sa.getParam("ValidTgts");
|
String valid = sa.getParam("ValidTgts");
|
||||||
|
|
||||||
PlayerCollection players = ai.getOpponents().filter(PlayerPredicates.isTargetableBy(sa));
|
PlayerCollection players = ai.getOpponents().filter(PlayerPredicates.isTargetableBy(sa));
|
||||||
// use chooseSinglePlayer function to the select player
|
// use chooseSinglePlayer function to the select player
|
||||||
Player chosen = chooseSinglePlayer(ai, sa, players);
|
Player chosen = chooseSinglePlayer(ai, sa, players);
|
||||||
if (chosen != null) {
|
if (chosen != null) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
sa.getTargets().add(chosen);
|
sa.getTargets().add(chosen);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("Creature".equals(valid)) {
|
if ("Creature".equals(valid)) {
|
||||||
// Springjack Knight
|
// Springjack Knight
|
||||||
// TODO: Whirlpool Whelm also uses creature targeting but it's trickier to support
|
// TODO: Whirlpool Whelm also uses creature targeting but it's trickier to support
|
||||||
CardCollectionView aiCreats = CardLists.filter(ai.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.CREATURES);
|
CardCollectionView aiCreats = CardLists.filter(ai.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.CREATURES);
|
||||||
CardCollectionView oppCreats = CardLists.filter(ai.getOpponents().getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.CREATURES);
|
CardCollectionView oppCreats = CardLists.filter(ai.getOpponents().getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.CREATURES);
|
||||||
|
|
||||||
Card tgt = aiCreats.isEmpty() ? ComputerUtilCard.getWorstCreatureAI(oppCreats) : ComputerUtilCard.getBestCreatureAI(aiCreats);
|
Card tgt = aiCreats.isEmpty() ? ComputerUtilCard.getWorstCreatureAI(oppCreats) : ComputerUtilCard.getBestCreatureAI(aiCreats);
|
||||||
|
|
||||||
if (tgt != null) {
|
if (tgt != null) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
sa.getTargets().add(tgt);
|
sa.getTargets().add(tgt);
|
||||||
} else {
|
} else {
|
||||||
return false; // cut short if this part of the clause is not satisfiable (with current card pool should never get here)
|
return false; // cut short if this part of the clause is not satisfiable (with current card pool should never get here)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return sa.getTargets().getNumTargeted() > 0;
|
return sa.getTargets().getNumTargeted() > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,195 +1,195 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import forge.ai.ComputerUtilCard;
|
import forge.ai.ComputerUtilCard;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardCollection;
|
import forge.game.card.CardCollection;
|
||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
import forge.game.phase.PhaseHandler;
|
import forge.game.phase.PhaseHandler;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.player.PlayerActionConfirmMode;
|
import forge.game.player.PlayerActionConfirmMode;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.TargetRestrictions;
|
import forge.game.spellability.TargetRestrictions;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class CloneAi extends SpellAbilityAi {
|
public class CloneAi extends SpellAbilityAi {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
final Game game = source.getGame();
|
final Game game = source.getGame();
|
||||||
|
|
||||||
boolean useAbility = true;
|
boolean useAbility = true;
|
||||||
|
|
||||||
// if (card.getController().isComputer()) {
|
// if (card.getController().isComputer()) {
|
||||||
// final List<Card> creatures = AllZoneUtil.getCreaturesInPlay();
|
// final List<Card> creatures = AllZoneUtil.getCreaturesInPlay();
|
||||||
// if (!creatures.isEmpty()) {
|
// if (!creatures.isEmpty()) {
|
||||||
// cardToCopy = CardFactoryUtil.getBestCreatureAI(creatures);
|
// cardToCopy = CardFactoryUtil.getBestCreatureAI(creatures);
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// TODO - add some kind of check to answer
|
// TODO - add some kind of check to answer
|
||||||
// "Am I going to attack with this?"
|
// "Am I going to attack with this?"
|
||||||
// TODO - add some kind of check for during human turn to answer
|
// TODO - add some kind of check for during human turn to answer
|
||||||
// "Can I use this to block something?"
|
// "Can I use this to block something?"
|
||||||
|
|
||||||
PhaseHandler phase = game.getPhaseHandler();
|
PhaseHandler phase = game.getPhaseHandler();
|
||||||
// don't use instant speed clone abilities outside computers
|
// don't use instant speed clone abilities outside computers
|
||||||
// Combat_Begin step
|
// Combat_Begin step
|
||||||
if (!phase.is(PhaseType.COMBAT_BEGIN)
|
if (!phase.is(PhaseType.COMBAT_BEGIN)
|
||||||
&& phase.isPlayerTurn(ai) && !SpellAbilityAi.isSorcerySpeed(sa)
|
&& phase.isPlayerTurn(ai) && !SpellAbilityAi.isSorcerySpeed(sa)
|
||||||
&& !sa.hasParam("ActivationPhases") && !sa.hasParam("Permanent")) {
|
&& !sa.hasParam("ActivationPhases") && !sa.hasParam("Permanent")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't use instant speed clone abilities outside humans
|
// don't use instant speed clone abilities outside humans
|
||||||
// Combat_Declare_Attackers_InstantAbility step
|
// Combat_Declare_Attackers_InstantAbility step
|
||||||
if (!phase.is(PhaseType.COMBAT_DECLARE_ATTACKERS) || phase.isPlayerTurn(ai) || game.getCombat().getAttackers().isEmpty()) {
|
if (!phase.is(PhaseType.COMBAT_DECLARE_ATTACKERS) || phase.isPlayerTurn(ai) || game.getCombat().getAttackers().isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't activate during main2 unless this effect is permanent
|
// don't activate during main2 unless this effect is permanent
|
||||||
if (phase.is(PhaseType.MAIN2) && !sa.hasParam("Permanent")) {
|
if (phase.is(PhaseType.MAIN2) && !sa.hasParam("Permanent")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (null == tgt) {
|
if (null == tgt) {
|
||||||
final List<Card> defined = AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), sa);
|
final List<Card> defined = AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), sa);
|
||||||
|
|
||||||
boolean bFlag = false;
|
boolean bFlag = false;
|
||||||
for (final Card c : defined) {
|
for (final Card c : defined) {
|
||||||
bFlag |= (!c.isCreature() && !c.isTapped() && !(c.getTurnInZone() == phase.getTurn()));
|
bFlag |= (!c.isCreature() && !c.isTapped() && !(c.getTurnInZone() == phase.getTurn()));
|
||||||
|
|
||||||
// for creatures that could be improved (like Figure of Destiny)
|
// for creatures that could be improved (like Figure of Destiny)
|
||||||
if (c.isCreature() && (sa.hasParam("Permanent") || (!c.isTapped() && !c.isSick()))) {
|
if (c.isCreature() && (sa.hasParam("Permanent") || (!c.isTapped() && !c.isSick()))) {
|
||||||
int power = -5;
|
int power = -5;
|
||||||
if (sa.hasParam("Power")) {
|
if (sa.hasParam("Power")) {
|
||||||
power = AbilityUtils.calculateAmount(source, sa.getParam("Power"), sa);
|
power = AbilityUtils.calculateAmount(source, sa.getParam("Power"), sa);
|
||||||
}
|
}
|
||||||
int toughness = -5;
|
int toughness = -5;
|
||||||
if (sa.hasParam("Toughness")) {
|
if (sa.hasParam("Toughness")) {
|
||||||
toughness = AbilityUtils.calculateAmount(source, sa.getParam("Toughness"), sa);
|
toughness = AbilityUtils.calculateAmount(source, sa.getParam("Toughness"), sa);
|
||||||
}
|
}
|
||||||
if ((power + toughness) > (c.getCurrentPower() + c.getCurrentToughness())) {
|
if ((power + toughness) > (c.getCurrentPower() + c.getCurrentToughness())) {
|
||||||
bFlag = true;
|
bFlag = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!bFlag) { // All of the defined stuff is cloned, not very
|
if (!bFlag) { // All of the defined stuff is cloned, not very
|
||||||
// useful
|
// useful
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
useAbility &= cloneTgtAI(sa);
|
useAbility &= cloneTgtAI(sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
return useAbility;
|
return useAbility;
|
||||||
} // end cloneCanPlayAI()
|
} // end cloneCanPlayAI()
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean chkAIDrawback(SpellAbility sa, Player aiPlayer) {
|
public boolean chkAIDrawback(SpellAbility sa, Player aiPlayer) {
|
||||||
// AI should only activate this during Human's turn
|
// AI should only activate this during Human's turn
|
||||||
boolean chance = true;
|
boolean chance = true;
|
||||||
|
|
||||||
if (sa.usesTargeting()) {
|
if (sa.usesTargeting()) {
|
||||||
chance = cloneTgtAI(sa);
|
chance = cloneTgtAI(sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return chance;
|
return chance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
|
||||||
|
|
||||||
boolean chance = true;
|
boolean chance = true;
|
||||||
|
|
||||||
if (sa.usesTargeting()) {
|
if (sa.usesTargeting()) {
|
||||||
chance = cloneTgtAI(sa);
|
chance = cloneTgtAI(sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Improve AI for triggers. If source is a creature with:
|
// Improve AI for triggers. If source is a creature with:
|
||||||
// When ETB, sacrifice a creature. Check to see if the AI has something
|
// When ETB, sacrifice a creature. Check to see if the AI has something
|
||||||
// to sacrifice
|
// to sacrifice
|
||||||
|
|
||||||
// Eventually, we can call the trigger of ETB abilities with
|
// Eventually, we can call the trigger of ETB abilities with
|
||||||
// not mandatory as part of the checks to cast something
|
// not mandatory as part of the checks to cast something
|
||||||
|
|
||||||
return chance || mandatory;
|
return chance || mandatory;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* cloneTgtAI.
|
* cloneTgtAI.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param sa
|
* @param sa
|
||||||
* a {@link forge.game.spellability.SpellAbility} object.
|
* a {@link forge.game.spellability.SpellAbility} object.
|
||||||
* @return a boolean.
|
* @return a boolean.
|
||||||
*/
|
*/
|
||||||
private boolean cloneTgtAI(final SpellAbility sa) {
|
private boolean cloneTgtAI(final SpellAbility sa) {
|
||||||
// Specific logic for cards
|
// Specific logic for cards
|
||||||
if ("CloneAttacker".equals(sa.getParam("AILogic"))) {
|
if ("CloneAttacker".equals(sa.getParam("AILogic"))) {
|
||||||
CardCollection valid = CardLists.getValidCards(sa.getHostCard().getController().getCardsIn(ZoneType.Battlefield), sa.getParam("ValidTgts"), sa.getHostCard().getController(), sa.getHostCard());
|
CardCollection valid = CardLists.getValidCards(sa.getHostCard().getController().getCardsIn(ZoneType.Battlefield), sa.getParam("ValidTgts"), sa.getHostCard().getController(), sa.getHostCard());
|
||||||
sa.getTargets().add(ComputerUtilCard.getBestCreatureAI(valid));
|
sa.getTargets().add(ComputerUtilCard.getBestCreatureAI(valid));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default:
|
// Default:
|
||||||
// This is reasonable for now. Kamahl, Fist of Krosa and a sorcery or
|
// This is reasonable for now. Kamahl, Fist of Krosa and a sorcery or
|
||||||
// two are the only things
|
// two are the only things
|
||||||
// that clone a target. Those can just use SVar:RemAIDeck:True until
|
// that clone a target. Those can just use SVar:RemAIDeck:True until
|
||||||
// this can do a reasonably
|
// this can do a reasonably
|
||||||
// good job of picking a good target
|
// good job of picking a good target
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.game.player.Player, forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.game.player.Player, forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
||||||
// Didn't confirm in the original code
|
// Didn't confirm in the original code
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
*
|
*
|
||||||
* @see forge.ai.SpellAbilityAi#chooseSingleCard(forge.game.player.Player,
|
* @see forge.ai.SpellAbilityAi#chooseSingleCard(forge.game.player.Player,
|
||||||
* forge.game.spellability.SpellAbility, java.lang.Iterable, boolean,
|
* forge.game.spellability.SpellAbility, java.lang.Iterable, boolean,
|
||||||
* forge.game.player.Player)
|
* forge.game.player.Player)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional,
|
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional,
|
||||||
Player targetedPlayer) {
|
Player targetedPlayer) {
|
||||||
final Card host = sa.getHostCard();
|
final Card host = sa.getHostCard();
|
||||||
final Player ctrl = host.getController();
|
final Player ctrl = host.getController();
|
||||||
|
|
||||||
final boolean isVesuva = "Vesuva".equals(host.getName());
|
final boolean isVesuva = "Vesuva".equals(host.getName());
|
||||||
|
|
||||||
final String filter = !isVesuva ? "Permanent.YouDontCtrl,Permanent.nonLegendary"
|
final String filter = !isVesuva ? "Permanent.YouDontCtrl,Permanent.nonLegendary"
|
||||||
: "Permanent.YouDontCtrl+notnamedVesuva,Permanent.nonLegendary+notnamedVesuva";
|
: "Permanent.YouDontCtrl+notnamedVesuva,Permanent.nonLegendary+notnamedVesuva";
|
||||||
|
|
||||||
CardCollection newOptions = CardLists.getValidCards(options, filter.split(","), ctrl, host, sa);
|
CardCollection newOptions = CardLists.getValidCards(options, filter.split(","), ctrl, host, sa);
|
||||||
if (!newOptions.isEmpty()) {
|
if (!newOptions.isEmpty()) {
|
||||||
options = newOptions;
|
options = newOptions;
|
||||||
}
|
}
|
||||||
Card choice = ComputerUtilCard.getBestAI(options);
|
Card choice = ComputerUtilCard.getBestAI(options);
|
||||||
if (isVesuva && "Vesuva".equals(choice.getName())) {
|
if (isVesuva && "Vesuva".equals(choice.getName())) {
|
||||||
choice = null;
|
choice = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return choice;
|
return choice;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,113 +1,113 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
import forge.ai.ComputerUtil;
|
import forge.ai.ComputerUtil;
|
||||||
import forge.ai.ComputerUtilCard;
|
import forge.ai.ComputerUtilCard;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardCollection;
|
import forge.game.card.CardCollection;
|
||||||
import forge.game.card.CardCollectionView;
|
import forge.game.card.CardCollectionView;
|
||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.TargetRestrictions;
|
import forge.game.spellability.TargetRestrictions;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
import forge.util.MyRandom;
|
import forge.util.MyRandom;
|
||||||
|
|
||||||
public class ControlExchangeAi extends SpellAbilityAi {
|
public class ControlExchangeAi extends SpellAbilityAi {
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see forge.card.abilityfactory.SpellAiLogic#canPlayAI(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility)
|
* @see forge.card.abilityfactory.SpellAiLogic#canPlayAI(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player ai, final SpellAbility sa) {
|
protected boolean canPlayAI(Player ai, final SpellAbility sa) {
|
||||||
Card object1 = null;
|
Card object1 = null;
|
||||||
Card object2 = null;
|
Card object2 = null;
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
|
|
||||||
CardCollection list =
|
CardCollection list =
|
||||||
CardLists.getValidCards(ComputerUtil.getOpponentFor(ai).getCardsIn(ZoneType.Battlefield), tgt.getValidTgts(), ai, sa.getHostCard(), sa);
|
CardLists.getValidCards(ComputerUtil.getOpponentFor(ai).getCardsIn(ZoneType.Battlefield), tgt.getValidTgts(), ai, sa.getHostCard(), sa);
|
||||||
// AI won't try to grab cards that are filtered out of AI decks on
|
// AI won't try to grab cards that are filtered out of AI decks on
|
||||||
// purpose
|
// purpose
|
||||||
list = CardLists.filter(list, new Predicate<Card>() {
|
list = CardLists.filter(list, new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(final Card c) {
|
public boolean apply(final Card c) {
|
||||||
return !c.hasSVar("RemAIDeck") && c.canBeTargetedBy(sa);
|
return !c.hasSVar("RemAIDeck") && c.canBeTargetedBy(sa);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
object1 = ComputerUtilCard.getBestAI(list);
|
object1 = ComputerUtilCard.getBestAI(list);
|
||||||
if (sa.hasParam("Defined")) {
|
if (sa.hasParam("Defined")) {
|
||||||
object2 = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa).get(0);
|
object2 = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa).get(0);
|
||||||
} else if (tgt.getMinTargets(sa.getHostCard(), sa) > 1) {
|
} else if (tgt.getMinTargets(sa.getHostCard(), sa) > 1) {
|
||||||
CardCollectionView list2 = ai.getCardsIn(ZoneType.Battlefield);
|
CardCollectionView list2 = ai.getCardsIn(ZoneType.Battlefield);
|
||||||
list2 = CardLists.getValidCards(list2, tgt.getValidTgts(), ai, sa.getHostCard(), sa);
|
list2 = CardLists.getValidCards(list2, tgt.getValidTgts(), ai, sa.getHostCard(), sa);
|
||||||
object2 = ComputerUtilCard.getWorstAI(list2);
|
object2 = ComputerUtilCard.getWorstAI(list2);
|
||||||
sa.getTargets().add(object2);
|
sa.getTargets().add(object2);
|
||||||
}
|
}
|
||||||
if (object1 == null || object2 == null) {
|
if (object1 == null || object2 == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (ComputerUtilCard.evaluateCreature(object1) > ComputerUtilCard.evaluateCreature(object2) + 40) {
|
if (ComputerUtilCard.evaluateCreature(object1) > ComputerUtilCard.evaluateCreature(object2) + 40) {
|
||||||
sa.getTargets().add(object1);
|
sa.getTargets().add(object1);
|
||||||
return MyRandom.getRandom().nextFloat() <= Math.pow(.6667, sa.getActivationsThisTurn());
|
return MyRandom.getRandom().nextFloat() <= Math.pow(.6667, sa.getActivationsThisTurn());
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
|
||||||
if (!sa.usesTargeting()) {
|
if (!sa.usesTargeting()) {
|
||||||
if (mandatory) {
|
if (mandatory) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (mandatory) {
|
if (mandatory) {
|
||||||
return chkAIDrawback(sa, aiPlayer);
|
return chkAIDrawback(sa, aiPlayer);
|
||||||
} else {
|
} else {
|
||||||
return canPlayAI(aiPlayer, sa);
|
return canPlayAI(aiPlayer, sa);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean chkAIDrawback(SpellAbility sa, Player aiPlayer) {
|
public boolean chkAIDrawback(SpellAbility sa, Player aiPlayer) {
|
||||||
if (!sa.usesTargeting()) {
|
if (!sa.usesTargeting()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
|
|
||||||
CardCollection list = CardLists.getValidCards(aiPlayer.getGame().getCardsIn(ZoneType.Battlefield),
|
CardCollection list = CardLists.getValidCards(aiPlayer.getGame().getCardsIn(ZoneType.Battlefield),
|
||||||
tgt.getValidTgts(), aiPlayer, sa.getHostCard(), sa);
|
tgt.getValidTgts(), aiPlayer, sa.getHostCard(), sa);
|
||||||
|
|
||||||
// only select the cards that can be targeted
|
// only select the cards that can be targeted
|
||||||
list = CardLists.getTargetableCards(list, sa);
|
list = CardLists.getTargetableCards(list, sa);
|
||||||
|
|
||||||
if (list.isEmpty())
|
if (list.isEmpty())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Card best = ComputerUtilCard.getBestAI(list);
|
Card best = ComputerUtilCard.getBestAI(list);
|
||||||
|
|
||||||
// if Param has Defined, check if the best Target is better than the Defined
|
// if Param has Defined, check if the best Target is better than the Defined
|
||||||
if (sa.hasParam("Defined")) {
|
if (sa.hasParam("Defined")) {
|
||||||
final Card object = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa).get(0);
|
final Card object = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa).get(0);
|
||||||
// TODO add evaluate Land if able
|
// TODO add evaluate Land if able
|
||||||
final Card realBest = ComputerUtilCard.getBestAI(Lists.newArrayList(best, object));
|
final Card realBest = ComputerUtilCard.getBestAI(Lists.newArrayList(best, object));
|
||||||
|
|
||||||
// Defined card is better than this one, try to avoid trade
|
// Defined card is better than this one, try to avoid trade
|
||||||
if (!best.equals(realBest)) {
|
if (!best.equals(realBest)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// add best Target
|
// add best Target
|
||||||
sa.getTargets().add(best);
|
sa.getTargets().add(best);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,319 +1,319 @@
|
|||||||
/*
|
/*
|
||||||
* Forge: Play Magic: the Gathering.
|
* Forge: Play Magic: the Gathering.
|
||||||
* Copyright (C) 2011 Forge Team
|
* Copyright (C) 2011 Forge Team
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful,
|
* This program is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
import forge.ai.ComputerUtilCard;
|
import forge.ai.ComputerUtilCard;
|
||||||
import forge.ai.ComputerUtilCombat;
|
import forge.ai.ComputerUtilCombat;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardCollection;
|
import forge.game.card.CardCollection;
|
||||||
import forge.game.card.CardCollectionView;
|
import forge.game.card.CardCollectionView;
|
||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.player.PlayerPredicates;
|
import forge.game.player.PlayerPredicates;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.TargetRestrictions;
|
import forge.game.spellability.TargetRestrictions;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
import forge.util.Aggregates;
|
import forge.util.Aggregates;
|
||||||
import forge.util.collect.FCollectionView;
|
import forge.util.collect.FCollectionView;
|
||||||
|
|
||||||
|
|
||||||
//AB:GainControl|ValidTgts$Creature|TgtPrompt$Select target legendary creature|LoseControl$Untap,LoseControl|SpellDescription$Gain control of target xxxxxxx
|
//AB:GainControl|ValidTgts$Creature|TgtPrompt$Select target legendary creature|LoseControl$Untap,LoseControl|SpellDescription$Gain control of target xxxxxxx
|
||||||
|
|
||||||
//GainControl specific sa:
|
//GainControl specific sa:
|
||||||
// LoseControl - the lose control conditions (as a comma separated list)
|
// LoseControl - the lose control conditions (as a comma separated list)
|
||||||
// -Untap - source card becomes untapped
|
// -Untap - source card becomes untapped
|
||||||
// -LoseControl - you lose control of source card
|
// -LoseControl - you lose control of source card
|
||||||
// -LeavesPlay - source card leaves the battlefield
|
// -LeavesPlay - source card leaves the battlefield
|
||||||
// -PowerGT - (not implemented yet for Old Man of the Sea)
|
// -PowerGT - (not implemented yet for Old Man of the Sea)
|
||||||
// AddKWs - Keywords to add to the controlled card
|
// AddKWs - Keywords to add to the controlled card
|
||||||
// (as a "&"-separated list; like Haste, Sacrifice CARDNAME at EOT, any standard keyword)
|
// (as a "&"-separated list; like Haste, Sacrifice CARDNAME at EOT, any standard keyword)
|
||||||
// OppChoice - set to True if opponent chooses creature (for Preacher) - not implemented yet
|
// OppChoice - set to True if opponent chooses creature (for Preacher) - not implemented yet
|
||||||
// Untap - set to True if target card should untap when control is taken
|
// Untap - set to True if target card should untap when control is taken
|
||||||
// DestroyTgt - actions upon which the tgt should be destroyed. same list as LoseControl
|
// DestroyTgt - actions upon which the tgt should be destroyed. same list as LoseControl
|
||||||
// NoRegen - set if destroyed creature can't be regenerated. used only with DestroyTgt
|
// NoRegen - set if destroyed creature can't be regenerated. used only with DestroyTgt
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* AbilityFactory_GainControl class.
|
* AbilityFactory_GainControl class.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @author Forge
|
* @author Forge
|
||||||
* @version $Id: AbilityFactoryGainControl.java 17764 2012-10-29 11:04:18Z Sloth $
|
* @version $Id: AbilityFactoryGainControl.java 17764 2012-10-29 11:04:18Z Sloth $
|
||||||
*/
|
*/
|
||||||
public class ControlGainAi extends SpellAbilityAi {
|
public class ControlGainAi extends SpellAbilityAi {
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(final Player ai, final SpellAbility sa) {
|
protected boolean canPlayAI(final Player ai, final SpellAbility sa) {
|
||||||
|
|
||||||
final List<String> lose = Lists.newArrayList();
|
final List<String> lose = Lists.newArrayList();
|
||||||
|
|
||||||
if (sa.hasParam("LoseControl")) {
|
if (sa.hasParam("LoseControl")) {
|
||||||
lose.addAll(Lists.newArrayList(sa.getParam("LoseControl").split(",")));
|
lose.addAll(Lists.newArrayList(sa.getParam("LoseControl").split(",")));
|
||||||
}
|
}
|
||||||
|
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
final FCollectionView<Player> opponents = ai.getOpponents();
|
final FCollectionView<Player> opponents = ai.getOpponents();
|
||||||
|
|
||||||
// if Defined, then don't worry about targeting
|
// if Defined, then don't worry about targeting
|
||||||
if (tgt == null) {
|
if (tgt == null) {
|
||||||
if (sa.hasParam("AllValid")) {
|
if (sa.hasParam("AllValid")) {
|
||||||
CardCollectionView tgtCards = CardLists.filterControlledBy(game.getCardsIn(ZoneType.Battlefield), opponents);
|
CardCollectionView tgtCards = CardLists.filterControlledBy(game.getCardsIn(ZoneType.Battlefield), opponents);
|
||||||
tgtCards = AbilityUtils.filterListByType(tgtCards, sa.getParam("AllValid"), sa);
|
tgtCards = AbilityUtils.filterListByType(tgtCards, sa.getParam("AllValid"), sa);
|
||||||
if (tgtCards.isEmpty()) {
|
if (tgtCards.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
if (sa.hasParam("TargetingPlayer")) {
|
if (sa.hasParam("TargetingPlayer")) {
|
||||||
Player targetingPlayer = AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("TargetingPlayer"), sa).get(0);
|
Player targetingPlayer = AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("TargetingPlayer"), sa).get(0);
|
||||||
sa.setTargetingPlayer(targetingPlayer);
|
sa.setTargetingPlayer(targetingPlayer);
|
||||||
return targetingPlayer.getController().chooseTargetsFor(sa);
|
return targetingPlayer.getController().chooseTargetsFor(sa);
|
||||||
}
|
}
|
||||||
if (tgt.isRandomTarget()) {
|
if (tgt.isRandomTarget()) {
|
||||||
sa.getTargets().add(Aggregates.random(tgt.getAllCandidates(sa, false)));
|
sa.getTargets().add(Aggregates.random(tgt.getAllCandidates(sa, false)));
|
||||||
}
|
}
|
||||||
if (tgt.canOnlyTgtOpponent()) {
|
if (tgt.canOnlyTgtOpponent()) {
|
||||||
List<Player> oppList = Lists
|
List<Player> oppList = Lists
|
||||||
.newArrayList(Iterables.filter(opponents, PlayerPredicates.isTargetableBy(sa)));
|
.newArrayList(Iterables.filter(opponents, PlayerPredicates.isTargetableBy(sa)));
|
||||||
|
|
||||||
if (oppList.isEmpty()) {
|
if (oppList.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
sa.getTargets().add(oppList.get(0));
|
sa.getTargets().add(oppList.get(0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't steal something if I can't Attack without, or prevent it from
|
// Don't steal something if I can't Attack without, or prevent it from
|
||||||
// blocking at least
|
// blocking at least
|
||||||
if (lose.contains("EOT")
|
if (lose.contains("EOT")
|
||||||
&& ai.getGame().getPhaseHandler().getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
&& ai.getGame().getPhaseHandler().getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
||||||
&& !sa.isTrigger()) {
|
&& !sa.isTrigger()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.hasParam("Defined")) {
|
if (sa.hasParam("Defined")) {
|
||||||
// no need to target, we'll pick up the target from Defined
|
// no need to target, we'll pick up the target from Defined
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
CardCollection list = new CardCollection();
|
CardCollection list = new CardCollection();
|
||||||
for (Player pl : opponents) {
|
for (Player pl : opponents) {
|
||||||
list.addAll(pl.getCardsIn(ZoneType.Battlefield));
|
list.addAll(pl.getCardsIn(ZoneType.Battlefield));
|
||||||
}
|
}
|
||||||
|
|
||||||
list = CardLists.getValidCards(list, tgt.getValidTgts(), sa.getActivatingPlayer(), sa.getHostCard(), sa);
|
list = CardLists.getValidCards(list, tgt.getValidTgts(), sa.getActivatingPlayer(), sa.getHostCard(), sa);
|
||||||
|
|
||||||
if (list.isEmpty()) {
|
if (list.isEmpty()) {
|
||||||
// no valid targets, so we need to bail
|
// no valid targets, so we need to bail
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// AI won't try to grab cards that are filtered out of AI decks on purpose
|
// AI won't try to grab cards that are filtered out of AI decks on purpose
|
||||||
list = CardLists.filter(list, new Predicate<Card>() {
|
list = CardLists.filter(list, new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(final Card c) {
|
public boolean apply(final Card c) {
|
||||||
if (!c.canBeTargetedBy(sa)) {
|
if (!c.canBeTargetedBy(sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (sa.isTrigger()) {
|
if (sa.isTrigger()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// do not take perm control on something that leaves the play end of turn
|
// do not take perm control on something that leaves the play end of turn
|
||||||
if (!lose.contains("EOT") && c.hasSVar("EndOfTurnLeavePlay")) {
|
if (!lose.contains("EOT") && c.hasSVar("EndOfTurnLeavePlay")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c.isCreature()) {
|
if (c.isCreature()) {
|
||||||
if (c.getNetCombatDamage() <= 0) {
|
if (c.getNetCombatDamage() <= 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// can not attack any opponent
|
// can not attack any opponent
|
||||||
boolean found = false;
|
boolean found = false;
|
||||||
for (final Player opp : opponents) {
|
for (final Player opp : opponents) {
|
||||||
if (ComputerUtilCombat.canAttackNextTurn(c, opp)) {
|
if (ComputerUtilCombat.canAttackNextTurn(c, opp)) {
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!found) {
|
if (!found) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// do not take control on something it doesn't know how to use
|
// do not take control on something it doesn't know how to use
|
||||||
return !c.hasSVar("RemAIDeck");
|
return !c.hasSVar("RemAIDeck");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (list.isEmpty()) {
|
if (list.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int creatures = 0, artifacts = 0, planeswalkers = 0, lands = 0, enchantments = 0;
|
int creatures = 0, artifacts = 0, planeswalkers = 0, lands = 0, enchantments = 0;
|
||||||
|
|
||||||
for (final Card c : list) {
|
for (final Card c : list) {
|
||||||
if (c.isCreature()) {
|
if (c.isCreature()) {
|
||||||
creatures++;
|
creatures++;
|
||||||
}
|
}
|
||||||
if (c.isArtifact()) {
|
if (c.isArtifact()) {
|
||||||
artifacts++;
|
artifacts++;
|
||||||
}
|
}
|
||||||
if (c.isLand()) {
|
if (c.isLand()) {
|
||||||
lands++;
|
lands++;
|
||||||
}
|
}
|
||||||
if (c.isEnchantment()) {
|
if (c.isEnchantment()) {
|
||||||
enchantments++;
|
enchantments++;
|
||||||
}
|
}
|
||||||
if (c.isPlaneswalker()) {
|
if (c.isPlaneswalker()) {
|
||||||
planeswalkers++;
|
planeswalkers++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while (sa.getTargets().getNumTargeted() < tgt.getMaxTargets(sa.getHostCard(), sa)) {
|
while (sa.getTargets().getNumTargeted() < tgt.getMaxTargets(sa.getHostCard(), sa)) {
|
||||||
Card t = null;
|
Card t = null;
|
||||||
|
|
||||||
if (list.isEmpty()) {
|
if (list.isEmpty()) {
|
||||||
if ((sa.getTargets().getNumTargeted() < tgt.getMinTargets(sa.getHostCard(), sa)) || (sa.getTargets().getNumTargeted() == 0)) {
|
if ((sa.getTargets().getNumTargeted() < tgt.getMinTargets(sa.getHostCard(), sa)) || (sa.getTargets().getNumTargeted() == 0)) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
// TODO is this good enough? for up to amounts?
|
// TODO is this good enough? for up to amounts?
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while (t == null) {
|
while (t == null) {
|
||||||
if (planeswalkers > 0) {
|
if (planeswalkers > 0) {
|
||||||
t = ComputerUtilCard.getBestPlaneswalkerAI(list);
|
t = ComputerUtilCard.getBestPlaneswalkerAI(list);
|
||||||
} else if (creatures > 0) {
|
} else if (creatures > 0) {
|
||||||
t = ComputerUtilCard.getBestCreatureAI(list);
|
t = ComputerUtilCard.getBestCreatureAI(list);
|
||||||
} else if (artifacts > 0) {
|
} else if (artifacts > 0) {
|
||||||
t = ComputerUtilCard.getBestArtifactAI(list);
|
t = ComputerUtilCard.getBestArtifactAI(list);
|
||||||
} else if (lands > 0) {
|
} else if (lands > 0) {
|
||||||
t = ComputerUtilCard.getBestLandAI(list);
|
t = ComputerUtilCard.getBestLandAI(list);
|
||||||
} else if (enchantments > 0) {
|
} else if (enchantments > 0) {
|
||||||
t = ComputerUtilCard.getBestEnchantmentAI(list, sa, true);
|
t = ComputerUtilCard.getBestEnchantmentAI(list, sa, true);
|
||||||
} else {
|
} else {
|
||||||
t = ComputerUtilCard.getMostExpensivePermanentAI(list, sa, true);
|
t = ComputerUtilCard.getMostExpensivePermanentAI(list, sa, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (t != null) {
|
if (t != null) {
|
||||||
if (t.isCreature())
|
if (t.isCreature())
|
||||||
creatures--;
|
creatures--;
|
||||||
if (t.isPlaneswalker())
|
if (t.isPlaneswalker())
|
||||||
planeswalkers--;
|
planeswalkers--;
|
||||||
if (t.isLand())
|
if (t.isLand())
|
||||||
lands--;
|
lands--;
|
||||||
if (t.isArtifact())
|
if (t.isArtifact())
|
||||||
artifacts--;
|
artifacts--;
|
||||||
if (t.isEnchantment())
|
if (t.isEnchantment())
|
||||||
enchantments--;
|
enchantments--;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sa.canTarget(t)) {
|
if (!sa.canTarget(t)) {
|
||||||
list.remove(t);
|
list.remove(t);
|
||||||
t = null;
|
t = null;
|
||||||
if (list.isEmpty()) {
|
if (list.isEmpty()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (t != null) {
|
if (t != null) {
|
||||||
sa.getTargets().add(t);
|
sa.getTargets().add(t);
|
||||||
list.remove(t);
|
list.remove(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
||||||
if (sa.getTargetRestrictions() == null) {
|
if (sa.getTargetRestrictions() == null) {
|
||||||
if (mandatory) {
|
if (mandatory) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if(sa.hasParam("TargetingPlayer") || (!this.canPlayAI(ai, sa) && mandatory)) {
|
if(sa.hasParam("TargetingPlayer") || (!this.canPlayAI(ai, sa) && mandatory)) {
|
||||||
List<Card> list = CardLists.getTargetableCards(ai.getCardsIn(ZoneType.Battlefield), sa);
|
List<Card> list = CardLists.getTargetableCards(ai.getCardsIn(ZoneType.Battlefield), sa);
|
||||||
if (list.isEmpty()) {
|
if (list.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
sa.getTargets().add(ComputerUtilCard.getWorstAI(list));
|
sa.getTargets().add(ComputerUtilCard.getWorstAI(list));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean chkAIDrawback(SpellAbility sa, final Player ai) {
|
public boolean chkAIDrawback(SpellAbility sa, final Player ai) {
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
if ((sa.getTargetRestrictions() == null) || !sa.getTargetRestrictions().doesTarget()) {
|
if ((sa.getTargetRestrictions() == null) || !sa.getTargetRestrictions().doesTarget()) {
|
||||||
if (sa.hasParam("AllValid")) {
|
if (sa.hasParam("AllValid")) {
|
||||||
CardCollectionView tgtCards = CardLists.filterControlledBy(game.getCardsIn(ZoneType.Battlefield), ai.getOpponents());
|
CardCollectionView tgtCards = CardLists.filterControlledBy(game.getCardsIn(ZoneType.Battlefield), ai.getOpponents());
|
||||||
tgtCards = AbilityUtils.filterListByType(tgtCards, sa.getParam("AllValid"), sa);
|
tgtCards = AbilityUtils.filterListByType(tgtCards, sa.getParam("AllValid"), sa);
|
||||||
if (tgtCards.isEmpty()) {
|
if (tgtCards.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final List<String> lose = Lists.newArrayList();
|
final List<String> lose = Lists.newArrayList();
|
||||||
|
|
||||||
if (sa.hasParam("LoseControl")) {
|
if (sa.hasParam("LoseControl")) {
|
||||||
lose.addAll(Lists.newArrayList(sa.getParam("LoseControl").split(",")));
|
lose.addAll(Lists.newArrayList(sa.getParam("LoseControl").split(",")));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lose.contains("EOT")
|
if (lose.contains("EOT")
|
||||||
&& game.getPhaseHandler().getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)) {
|
&& game.getPhaseHandler().getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return this.canPlayAI(ai, sa);
|
return this.canPlayAI(ai, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} // pumpDrawbackAI()
|
} // pumpDrawbackAI()
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options) {
|
protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options) {
|
||||||
final List<Card> cards = Lists.newArrayList();
|
final List<Card> cards = Lists.newArrayList();
|
||||||
for (Player p : options) {
|
for (Player p : options) {
|
||||||
cards.addAll(p.getCreaturesInPlay());
|
cards.addAll(p.getCreaturesInPlay());
|
||||||
}
|
}
|
||||||
Card chosen = ComputerUtilCard.getBestCreatureAI(cards);
|
Card chosen = ComputerUtilCard.getBestCreatureAI(cards);
|
||||||
return chosen != null ? chosen.getController() : Iterables.getFirst(options, null);
|
return chosen != null ? chosen.getController() : Iterables.getFirst(options, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,162 +1,162 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.base.Predicates;
|
import com.google.common.base.Predicates;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import forge.ai.ComputerUtil;
|
import forge.ai.ComputerUtil;
|
||||||
import forge.ai.ComputerUtilCard;
|
import forge.ai.ComputerUtilCard;
|
||||||
import forge.ai.SpecialCardAi;
|
import forge.ai.SpecialCardAi;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.card.*;
|
import forge.game.card.*;
|
||||||
import forge.game.card.CardPredicates.Presets;
|
import forge.game.card.CardPredicates.Presets;
|
||||||
import forge.game.phase.PhaseHandler;
|
import forge.game.phase.PhaseHandler;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.player.PlayerActionConfirmMode;
|
import forge.game.player.PlayerActionConfirmMode;
|
||||||
import forge.game.player.PlayerCollection;
|
import forge.game.player.PlayerCollection;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class CopyPermanentAi extends SpellAbilityAi {
|
public class CopyPermanentAi extends SpellAbilityAi {
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
||||||
// Card source = sa.getHostCard();
|
// Card source = sa.getHostCard();
|
||||||
// TODO - I'm sure someone can do this AI better
|
// TODO - I'm sure someone can do this AI better
|
||||||
|
|
||||||
PhaseHandler ph = aiPlayer.getGame().getPhaseHandler();
|
PhaseHandler ph = aiPlayer.getGame().getPhaseHandler();
|
||||||
String aiLogic = sa.getParamOrDefault("AILogic", "");
|
String aiLogic = sa.getParamOrDefault("AILogic", "");
|
||||||
|
|
||||||
if (ComputerUtil.preventRunAwayActivations(sa)) {
|
if (ComputerUtil.preventRunAwayActivations(sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("MimicVat".equals(aiLogic)) {
|
if ("MimicVat".equals(aiLogic)) {
|
||||||
return SpecialCardAi.MimicVat.considerCopy(aiPlayer, sa);
|
return SpecialCardAi.MimicVat.considerCopy(aiPlayer, sa);
|
||||||
} else if ("AtEOT".equals(aiLogic)) {
|
} else if ("AtEOT".equals(aiLogic)) {
|
||||||
return ph.is(PhaseType.END_OF_TURN);
|
return ph.is(PhaseType.END_OF_TURN);
|
||||||
} else if ("AtOppEOT".equals(aiLogic)) {
|
} else if ("AtOppEOT".equals(aiLogic)) {
|
||||||
return ph.is(PhaseType.END_OF_TURN) && ph.getPlayerTurn() != aiPlayer;
|
return ph.is(PhaseType.END_OF_TURN) && ph.getPlayerTurn() != aiPlayer;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.hasParam("AtEOT") && !aiPlayer.getGame().getPhaseHandler().is(PhaseType.MAIN1)) {
|
if (sa.hasParam("AtEOT") && !aiPlayer.getGame().getPhaseHandler().is(PhaseType.MAIN1)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.hasParam("Defined")) {
|
if (sa.hasParam("Defined")) {
|
||||||
// If there needs to be an imprinted card, don't activate the ability if nothing was imprinted yet (e.g. Mimic Vat)
|
// If there needs to be an imprinted card, don't activate the ability if nothing was imprinted yet (e.g. Mimic Vat)
|
||||||
if (sa.getParam("Defined").equals("Imprinted.ExiledWithSource") && sa.getHostCard().getImprintedCards().isEmpty()) {
|
if (sa.getParam("Defined").equals("Imprinted.ExiledWithSource") && sa.getHostCard().getImprintedCards().isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.usesTargeting() && sa.hasParam("TargetingPlayer")) {
|
if (sa.usesTargeting() && sa.hasParam("TargetingPlayer")) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
Player targetingPlayer = AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("TargetingPlayer"), sa).get(0);
|
Player targetingPlayer = AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("TargetingPlayer"), sa).get(0);
|
||||||
sa.setTargetingPlayer(targetingPlayer);
|
sa.setTargetingPlayer(targetingPlayer);
|
||||||
return targetingPlayer.getController().chooseTargetsFor(sa);
|
return targetingPlayer.getController().chooseTargetsFor(sa);
|
||||||
} else {
|
} else {
|
||||||
return this.doTriggerAINoCost(aiPlayer, sa, false);
|
return this.doTriggerAINoCost(aiPlayer, sa, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(final Player aiPlayer, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(final Player aiPlayer, SpellAbility sa, boolean mandatory) {
|
||||||
// ////
|
// ////
|
||||||
// Targeting
|
// Targeting
|
||||||
if (sa.usesTargeting()) {
|
if (sa.usesTargeting()) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
|
|
||||||
CardCollection list = new CardCollection(CardUtil.getValidCardsToTarget(sa.getTargetRestrictions(), sa));
|
CardCollection list = new CardCollection(CardUtil.getValidCardsToTarget(sa.getTargetRestrictions(), sa));
|
||||||
|
|
||||||
list = CardLists.filter(list, Predicates.not(CardPredicates.hasSVar("RemAIDeck")));
|
list = CardLists.filter(list, Predicates.not(CardPredicates.hasSVar("RemAIDeck")));
|
||||||
//Nothing to target
|
//Nothing to target
|
||||||
if (list.isEmpty()) {
|
if (list.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Saheeli Rai + Felidar Guardian combo support
|
// Saheeli Rai + Felidar Guardian combo support
|
||||||
if (sa.getHostCard().getName().equals("Saheeli Rai")) {
|
if (sa.getHostCard().getName().equals("Saheeli Rai")) {
|
||||||
CardCollection felidarGuardian = CardLists.filter(list, CardPredicates.nameEquals("Felidar Guardian"));
|
CardCollection felidarGuardian = CardLists.filter(list, CardPredicates.nameEquals("Felidar Guardian"));
|
||||||
if (felidarGuardian.size() > 0) {
|
if (felidarGuardian.size() > 0) {
|
||||||
// can copy a Felidar Guardian and combo off, so let's do it
|
// can copy a Felidar Guardian and combo off, so let's do it
|
||||||
sa.getTargets().add(felidarGuardian.get(0));
|
sa.getTargets().add(felidarGuardian.get(0));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// target loop
|
// target loop
|
||||||
while (sa.canAddMoreTarget()) {
|
while (sa.canAddMoreTarget()) {
|
||||||
if (list.isEmpty()) {
|
if (list.isEmpty()) {
|
||||||
if (!sa.isTargetNumberValid() || (sa.getTargets().getNumTargeted() == 0)) {
|
if (!sa.isTargetNumberValid() || (sa.getTargets().getNumTargeted() == 0)) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
// TODO is this good enough? for up to amounts?
|
// TODO is this good enough? for up to amounts?
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
list = CardLists.filter(list, new Predicate<Card>() {
|
list = CardLists.filter(list, new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(final Card c) {
|
public boolean apply(final Card c) {
|
||||||
return !c.getType().isLegendary() || !c.getController().equals(aiPlayer);
|
return !c.getType().isLegendary() || !c.getController().equals(aiPlayer);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Card choice;
|
Card choice;
|
||||||
if (!CardLists.filter(list, Presets.CREATURES).isEmpty()) {
|
if (!CardLists.filter(list, Presets.CREATURES).isEmpty()) {
|
||||||
if (sa.hasParam("TargetingPlayer")) {
|
if (sa.hasParam("TargetingPlayer")) {
|
||||||
choice = ComputerUtilCard.getWorstCreatureAI(list);
|
choice = ComputerUtilCard.getWorstCreatureAI(list);
|
||||||
} else {
|
} else {
|
||||||
choice = ComputerUtilCard.getBestCreatureAI(list);
|
choice = ComputerUtilCard.getBestCreatureAI(list);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
choice = ComputerUtilCard.getMostExpensivePermanentAI(list, sa, true);
|
choice = ComputerUtilCard.getMostExpensivePermanentAI(list, sa, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (choice == null) { // can't find anything left
|
if (choice == null) { // can't find anything left
|
||||||
if (!sa.isTargetNumberValid() || (sa.getTargets().getNumTargeted() == 0)) {
|
if (!sa.isTargetNumberValid() || (sa.getTargets().getNumTargeted() == 0)) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
// TODO is this good enough? for up to amounts?
|
// TODO is this good enough? for up to amounts?
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
list.remove(choice);
|
list.remove(choice);
|
||||||
sa.getTargets().add(choice);
|
sa.getTargets().add(choice);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// if no targeting, it should always be ok
|
// if no targeting, it should always be ok
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.game.player.Player, forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.game.player.Player, forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
||||||
//TODO: add logic here
|
//TODO: add logic here
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see forge.card.ability.SpellAbilityAi#chooseSingleCard(forge.game.player.Player, forge.card.spellability.SpellAbility, java.util.List, boolean)
|
* @see forge.card.ability.SpellAbilityAi#chooseSingleCard(forge.game.player.Player, forge.card.spellability.SpellAbility, java.util.List, boolean)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
|
public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
|
||||||
// Select a card to attach to
|
// Select a card to attach to
|
||||||
return ComputerUtilCard.getBestAI(options);
|
return ComputerUtilCard.getBestAI(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options) {
|
protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options) {
|
||||||
final List<Card> cards = new PlayerCollection(options).getCreaturesInPlay();
|
final List<Card> cards = new PlayerCollection(options).getCreaturesInPlay();
|
||||||
Card chosen = ComputerUtilCard.getBestCreatureAI(cards);
|
Card chosen = ComputerUtilCard.getBestCreatureAI(cards);
|
||||||
return chosen != null ? chosen.getController() : Iterables.getFirst(options, null);
|
return chosen != null ? chosen.getController() : Iterables.getFirst(options, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,328 +1,328 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import forge.ai.*;
|
import forge.ai.*;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.ability.ApiType;
|
import forge.game.ability.ApiType;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardFactoryUtil;
|
import forge.game.card.CardFactoryUtil;
|
||||||
import forge.game.cost.Cost;
|
import forge.game.cost.Cost;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.SpellAbilityStackInstance;
|
import forge.game.spellability.SpellAbilityStackInstance;
|
||||||
import forge.game.spellability.TargetRestrictions;
|
import forge.game.spellability.TargetRestrictions;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
import forge.util.MyRandom;
|
import forge.util.MyRandom;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.tuple.ImmutablePair;
|
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
|
||||||
public class CounterAi extends SpellAbilityAi {
|
public class CounterAi extends SpellAbilityAi {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
||||||
boolean toReturn = true;
|
boolean toReturn = true;
|
||||||
final Cost abCost = sa.getPayCosts();
|
final Cost abCost = sa.getPayCosts();
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
final String sourceName = ComputerUtilAbility.getAbilitySourceName(sa);
|
final String sourceName = ComputerUtilAbility.getAbilitySourceName(sa);
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
int tgtCMC = 0;
|
int tgtCMC = 0;
|
||||||
SpellAbility tgtSA = null;
|
SpellAbility tgtSA = null;
|
||||||
|
|
||||||
if (game.getStack().isEmpty()) {
|
if (game.getStack().isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (abCost != null) {
|
if (abCost != null) {
|
||||||
// AI currently disabled for these costs
|
// AI currently disabled for these costs
|
||||||
if (!ComputerUtilCost.checkSacrificeCost(ai, abCost, source, sa)) {
|
if (!ComputerUtilCost.checkSacrificeCost(ai, abCost, source, sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!ComputerUtilCost.checkLifeCost(ai, abCost, source, 4, sa)) {
|
if (!ComputerUtilCost.checkLifeCost(ai, abCost, source, 4, sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("Force of Will".equals(sourceName)) {
|
if ("Force of Will".equals(sourceName)) {
|
||||||
if (!SpecialCardAi.ForceOfWill.consider(ai, sa)) {
|
if (!SpecialCardAi.ForceOfWill.consider(ai, sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
if (tgt != null) {
|
if (tgt != null) {
|
||||||
|
|
||||||
final SpellAbility topSA = ComputerUtilAbility.getTopSpellAbilityOnStack(game, sa);
|
final SpellAbility topSA = ComputerUtilAbility.getTopSpellAbilityOnStack(game, sa);
|
||||||
if (!CardFactoryUtil.isCounterableBy(topSA.getHostCard(), sa) || topSA.getActivatingPlayer() == ai
|
if (!CardFactoryUtil.isCounterableBy(topSA.getHostCard(), sa) || topSA.getActivatingPlayer() == ai
|
||||||
|| ai.getAllies().contains(topSA.getActivatingPlayer())) {
|
|| ai.getAllies().contains(topSA.getActivatingPlayer())) {
|
||||||
// might as well check for player's friendliness
|
// might as well check for player's friendliness
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if the top ability on the stack corresponds to the AI-specific targeting declaration, if provided
|
// check if the top ability on the stack corresponds to the AI-specific targeting declaration, if provided
|
||||||
if (sa.hasParam("AITgts") && (topSA.getHostCard() == null
|
if (sa.hasParam("AITgts") && (topSA.getHostCard() == null
|
||||||
|| !topSA.getHostCard().isValid(sa.getParam("AITgts"), sa.getActivatingPlayer(), source, sa))) {
|
|| !topSA.getHostCard().isValid(sa.getParam("AITgts"), sa.getActivatingPlayer(), source, sa))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.hasParam("CounterNoManaSpell") && topSA.getTotalManaSpent() > 0) {
|
if (sa.hasParam("CounterNoManaSpell") && topSA.getTotalManaSpent() > 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("OppDiscardsHand".equals(sa.getParam("AILogic"))) {
|
if ("OppDiscardsHand".equals(sa.getParam("AILogic"))) {
|
||||||
if (topSA.getActivatingPlayer().getCardsIn(ZoneType.Hand).size() < 2) {
|
if (topSA.getActivatingPlayer().getCardsIn(ZoneType.Hand).size() < 2) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
if (sa.canTargetSpellAbility(topSA)) {
|
if (sa.canTargetSpellAbility(topSA)) {
|
||||||
sa.getTargets().add(topSA);
|
sa.getTargets().add(topSA);
|
||||||
if (topSA.getPayCosts().getTotalMana() != null) {
|
if (topSA.getPayCosts().getTotalMana() != null) {
|
||||||
tgtSA = topSA;
|
tgtSA = topSA;
|
||||||
tgtCMC = topSA.getPayCosts().getTotalMana().getCMC();
|
tgtCMC = topSA.getPayCosts().getTotalMana().getCMC();
|
||||||
tgtCMC += topSA.getPayCosts().getTotalMana().countX() > 0 ? 3 : 0; // TODO: somehow determine the value of X paid and account for it?
|
tgtCMC += topSA.getPayCosts().getTotalMana().countX() > 0 ? 3 : 0; // TODO: somehow determine the value of X paid and account for it?
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
String unlessCost = sa.hasParam("UnlessCost") ? sa.getParam("UnlessCost").trim() : null;
|
String unlessCost = sa.hasParam("UnlessCost") ? sa.getParam("UnlessCost").trim() : null;
|
||||||
|
|
||||||
if (unlessCost != null && !unlessCost.endsWith(">")) {
|
if (unlessCost != null && !unlessCost.endsWith(">")) {
|
||||||
Player opp = tgtSA.getActivatingPlayer();
|
Player opp = tgtSA.getActivatingPlayer();
|
||||||
int usableManaSources = ComputerUtilMana.getAvailableManaEstimate(opp);
|
int usableManaSources = ComputerUtilMana.getAvailableManaEstimate(opp);
|
||||||
|
|
||||||
int toPay = 0;
|
int toPay = 0;
|
||||||
boolean setPayX = false;
|
boolean setPayX = false;
|
||||||
if (unlessCost.equals("X") && source.getSVar(unlessCost).equals("Count$xPaid")) {
|
if (unlessCost.equals("X") && source.getSVar(unlessCost).equals("Count$xPaid")) {
|
||||||
setPayX = true;
|
setPayX = true;
|
||||||
toPay = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
toPay = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
||||||
} else {
|
} else {
|
||||||
toPay = AbilityUtils.calculateAmount(source, unlessCost, sa);
|
toPay = AbilityUtils.calculateAmount(source, unlessCost, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toPay == 0) {
|
if (toPay == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toPay <= usableManaSources) {
|
if (toPay <= usableManaSources) {
|
||||||
// If this is a reusable Resource, feel free to play it most of
|
// If this is a reusable Resource, feel free to play it most of
|
||||||
// the time
|
// the time
|
||||||
if (!SpellAbilityAi.playReusable(ai,sa)) {
|
if (!SpellAbilityAi.playReusable(ai,sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (setPayX) {
|
if (setPayX) {
|
||||||
source.setSVar("PayX", Integer.toString(toPay));
|
source.setSVar("PayX", Integer.toString(toPay));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Improve AI
|
// TODO Improve AI
|
||||||
|
|
||||||
// Will return true if this spell can counter (or is Reusable and can
|
// Will return true if this spell can counter (or is Reusable and can
|
||||||
// force the Human into making decisions)
|
// force the Human into making decisions)
|
||||||
|
|
||||||
// But really it should be more picky about how it counters things
|
// But really it should be more picky about how it counters things
|
||||||
|
|
||||||
if (sa.hasParam("AILogic")) {
|
if (sa.hasParam("AILogic")) {
|
||||||
String logic = sa.getParam("AILogic");
|
String logic = sa.getParam("AILogic");
|
||||||
if ("Never".equals(logic)) {
|
if ("Never".equals(logic)) {
|
||||||
return false;
|
return false;
|
||||||
} else if (logic.startsWith("MinCMC.")) {
|
} else if (logic.startsWith("MinCMC.")) {
|
||||||
int minCMC = Integer.parseInt(logic.substring(7));
|
int minCMC = Integer.parseInt(logic.substring(7));
|
||||||
if (tgtCMC < minCMC) {
|
if (tgtCMC < minCMC) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if ("NullBrooch".equals(logic)) {
|
} else if ("NullBrooch".equals(logic)) {
|
||||||
if (!SpecialCardAi.NullBrooch.consider(ai, sa)) {
|
if (!SpecialCardAi.NullBrooch.consider(ai, sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Specific constraints for the AI to use/not use counterspells against specific groups of spells
|
// Specific constraints for the AI to use/not use counterspells against specific groups of spells
|
||||||
// (specified in the AI profile)
|
// (specified in the AI profile)
|
||||||
AiController aic = ((PlayerControllerAi)ai.getController()).getAi();
|
AiController aic = ((PlayerControllerAi)ai.getController()).getAi();
|
||||||
boolean ctrCmc0ManaPerms = aic.getBooleanProperty(AiProps.ALWAYS_COUNTER_CMC_0_MANA_MAKING_PERMS);
|
boolean ctrCmc0ManaPerms = aic.getBooleanProperty(AiProps.ALWAYS_COUNTER_CMC_0_MANA_MAKING_PERMS);
|
||||||
boolean ctrDamageSpells = aic.getBooleanProperty(AiProps.ALWAYS_COUNTER_DAMAGE_SPELLS);
|
boolean ctrDamageSpells = aic.getBooleanProperty(AiProps.ALWAYS_COUNTER_DAMAGE_SPELLS);
|
||||||
boolean ctrRemovalSpells = aic.getBooleanProperty(AiProps.ALWAYS_COUNTER_REMOVAL_SPELLS);
|
boolean ctrRemovalSpells = aic.getBooleanProperty(AiProps.ALWAYS_COUNTER_REMOVAL_SPELLS);
|
||||||
boolean ctrPumpSpells = aic.getBooleanProperty(AiProps.ALWAYS_COUNTER_PUMP_SPELLS);
|
boolean ctrPumpSpells = aic.getBooleanProperty(AiProps.ALWAYS_COUNTER_PUMP_SPELLS);
|
||||||
boolean ctrAuraSpells = aic.getBooleanProperty(AiProps.ALWAYS_COUNTER_AURAS);
|
boolean ctrAuraSpells = aic.getBooleanProperty(AiProps.ALWAYS_COUNTER_AURAS);
|
||||||
boolean ctrOtherCounters = aic.getBooleanProperty(AiProps.ALWAYS_COUNTER_OTHER_COUNTERSPELLS);
|
boolean ctrOtherCounters = aic.getBooleanProperty(AiProps.ALWAYS_COUNTER_OTHER_COUNTERSPELLS);
|
||||||
int ctrChanceCMC1 = aic.getIntProperty(AiProps.CHANCE_TO_COUNTER_CMC_1);
|
int ctrChanceCMC1 = aic.getIntProperty(AiProps.CHANCE_TO_COUNTER_CMC_1);
|
||||||
int ctrChanceCMC2 = aic.getIntProperty(AiProps.CHANCE_TO_COUNTER_CMC_2);
|
int ctrChanceCMC2 = aic.getIntProperty(AiProps.CHANCE_TO_COUNTER_CMC_2);
|
||||||
int ctrChanceCMC3 = aic.getIntProperty(AiProps.CHANCE_TO_COUNTER_CMC_3);
|
int ctrChanceCMC3 = aic.getIntProperty(AiProps.CHANCE_TO_COUNTER_CMC_3);
|
||||||
String ctrNamed = aic.getProperty(AiProps.ALWAYS_COUNTER_SPELLS_FROM_NAMED_CARDS);
|
String ctrNamed = aic.getProperty(AiProps.ALWAYS_COUNTER_SPELLS_FROM_NAMED_CARDS);
|
||||||
boolean dontCounter = false;
|
boolean dontCounter = false;
|
||||||
|
|
||||||
if (tgtCMC == 1 && !MyRandom.percentTrue(ctrChanceCMC1)) {
|
if (tgtCMC == 1 && !MyRandom.percentTrue(ctrChanceCMC1)) {
|
||||||
dontCounter = true;
|
dontCounter = true;
|
||||||
} else if (tgtCMC == 2 && !MyRandom.percentTrue(ctrChanceCMC2)) {
|
} else if (tgtCMC == 2 && !MyRandom.percentTrue(ctrChanceCMC2)) {
|
||||||
dontCounter = true;
|
dontCounter = true;
|
||||||
} else if (tgtCMC == 3 && !MyRandom.percentTrue(ctrChanceCMC3)) {
|
} else if (tgtCMC == 3 && !MyRandom.percentTrue(ctrChanceCMC3)) {
|
||||||
dontCounter = true;
|
dontCounter = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tgtSA != null && tgtCMC < aic.getIntProperty(AiProps.MIN_SPELL_CMC_TO_COUNTER)) {
|
if (tgtSA != null && tgtCMC < aic.getIntProperty(AiProps.MIN_SPELL_CMC_TO_COUNTER)) {
|
||||||
dontCounter = true;
|
dontCounter = true;
|
||||||
Card tgtSource = tgtSA.getHostCard();
|
Card tgtSource = tgtSA.getHostCard();
|
||||||
if ((tgtSource != null && tgtCMC == 0 && tgtSource.isPermanent() && !tgtSource.getManaAbilities().isEmpty() && ctrCmc0ManaPerms)
|
if ((tgtSource != null && tgtCMC == 0 && tgtSource.isPermanent() && !tgtSource.getManaAbilities().isEmpty() && ctrCmc0ManaPerms)
|
||||||
|| (tgtSA.getApi() == ApiType.DealDamage || tgtSA.getApi() == ApiType.LoseLife || tgtSA.getApi() == ApiType.DamageAll && ctrDamageSpells)
|
|| (tgtSA.getApi() == ApiType.DealDamage || tgtSA.getApi() == ApiType.LoseLife || tgtSA.getApi() == ApiType.DamageAll && ctrDamageSpells)
|
||||||
|| (tgtSA.getApi() == ApiType.Counter && ctrOtherCounters)
|
|| (tgtSA.getApi() == ApiType.Counter && ctrOtherCounters)
|
||||||
|| ((tgtSA.getApi() == ApiType.Pump || tgtSA.getApi() == ApiType.PumpAll) && ctrPumpSpells)
|
|| ((tgtSA.getApi() == ApiType.Pump || tgtSA.getApi() == ApiType.PumpAll) && ctrPumpSpells)
|
||||||
|| (tgtSA.getApi() == ApiType.Attach && ctrAuraSpells)
|
|| (tgtSA.getApi() == ApiType.Attach && ctrAuraSpells)
|
||||||
|| (tgtSA.getApi() == ApiType.Destroy || tgtSA.getApi() == ApiType.DestroyAll || tgtSA.getApi() == ApiType.Sacrifice
|
|| (tgtSA.getApi() == ApiType.Destroy || tgtSA.getApi() == ApiType.DestroyAll || tgtSA.getApi() == ApiType.Sacrifice
|
||||||
|| tgtSA.getApi() == ApiType.SacrificeAll && ctrRemovalSpells)) {
|
|| tgtSA.getApi() == ApiType.SacrificeAll && ctrRemovalSpells)) {
|
||||||
dontCounter = false;
|
dontCounter = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tgtSource != null && !ctrNamed.isEmpty() && !"none".equalsIgnoreCase(ctrNamed)) {
|
if (tgtSource != null && !ctrNamed.isEmpty() && !"none".equalsIgnoreCase(ctrNamed)) {
|
||||||
for (String name : StringUtils.split(ctrNamed, ";")) {
|
for (String name : StringUtils.split(ctrNamed, ";")) {
|
||||||
if (name.equals(tgtSource.getName())) {
|
if (name.equals(tgtSource.getName())) {
|
||||||
dontCounter = false;
|
dontCounter = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// should not refrain from countering a CMC X spell if that's the only CMC
|
// should not refrain from countering a CMC X spell if that's the only CMC
|
||||||
// counterable with that particular counterspell type (e.g. Mental Misstep vs. CMC 1 spells)
|
// counterable with that particular counterspell type (e.g. Mental Misstep vs. CMC 1 spells)
|
||||||
if (sa.getParamOrDefault("ValidTgts", "").startsWith("Card.cmcEQ")) {
|
if (sa.getParamOrDefault("ValidTgts", "").startsWith("Card.cmcEQ")) {
|
||||||
int validTgtCMC = AbilityUtils.calculateAmount(source, sa.getParam("ValidTgts").substring(10), sa);
|
int validTgtCMC = AbilityUtils.calculateAmount(source, sa.getParam("ValidTgts").substring(10), sa);
|
||||||
if (tgtCMC == validTgtCMC) {
|
if (tgtCMC == validTgtCMC) {
|
||||||
dontCounter = false;
|
dontCounter = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dontCounter) {
|
if (dontCounter) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return toReturn;
|
return toReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean chkAIDrawback(SpellAbility sa, Player aiPlayer) {
|
public boolean chkAIDrawback(SpellAbility sa, Player aiPlayer) {
|
||||||
return doTriggerAINoCost(aiPlayer, sa, true);
|
return doTriggerAINoCost(aiPlayer, sa, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
|
|
||||||
if (tgt != null) {
|
if (tgt != null) {
|
||||||
if (game.getStack().isEmpty()) {
|
if (game.getStack().isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
Pair<SpellAbility, Boolean> pair = chooseTargetSpellAbility(game, sa, ai, mandatory);
|
Pair<SpellAbility, Boolean> pair = chooseTargetSpellAbility(game, sa, ai, mandatory);
|
||||||
SpellAbility tgtSA = pair.getLeft();
|
SpellAbility tgtSA = pair.getLeft();
|
||||||
|
|
||||||
if (tgtSA == null) {
|
if (tgtSA == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
sa.getTargets().add(tgtSA);
|
sa.getTargets().add(tgtSA);
|
||||||
if (!mandatory && !pair.getRight()) {
|
if (!mandatory && !pair.getRight()) {
|
||||||
// If not mandatory and not preferred, bail out after setting target
|
// If not mandatory and not preferred, bail out after setting target
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
String unlessCost = sa.hasParam("UnlessCost") ? sa.getParam("UnlessCost").trim() : null;
|
String unlessCost = sa.hasParam("UnlessCost") ? sa.getParam("UnlessCost").trim() : null;
|
||||||
|
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
if (unlessCost != null) {
|
if (unlessCost != null) {
|
||||||
Player opp = tgtSA.getActivatingPlayer();
|
Player opp = tgtSA.getActivatingPlayer();
|
||||||
int usableManaSources = ComputerUtilMana.getAvailableManaEstimate(opp);
|
int usableManaSources = ComputerUtilMana.getAvailableManaEstimate(opp);
|
||||||
|
|
||||||
int toPay = 0;
|
int toPay = 0;
|
||||||
boolean setPayX = false;
|
boolean setPayX = false;
|
||||||
if (unlessCost.equals("X") && source.getSVar(unlessCost).equals("Count$xPaid")) {
|
if (unlessCost.equals("X") && source.getSVar(unlessCost).equals("Count$xPaid")) {
|
||||||
setPayX = true;
|
setPayX = true;
|
||||||
toPay = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
toPay = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
||||||
} else {
|
} else {
|
||||||
toPay = AbilityUtils.calculateAmount(source, unlessCost, sa);
|
toPay = AbilityUtils.calculateAmount(source, unlessCost, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mandatory) {
|
if (!mandatory) {
|
||||||
if (toPay == 0) {
|
if (toPay == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toPay <= usableManaSources) {
|
if (toPay <= usableManaSources) {
|
||||||
// If this is a reusable Resource, feel free to play it most
|
// If this is a reusable Resource, feel free to play it most
|
||||||
// of the time
|
// of the time
|
||||||
if (!SpellAbilityAi.playReusable(ai,sa) || (MyRandom.getRandom().nextFloat() < .4)) {
|
if (!SpellAbilityAi.playReusable(ai,sa) || (MyRandom.getRandom().nextFloat() < .4)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (setPayX) {
|
if (setPayX) {
|
||||||
source.setSVar("PayX", Integer.toString(toPay));
|
source.setSVar("PayX", Integer.toString(toPay));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Improve AI
|
// TODO Improve AI
|
||||||
|
|
||||||
// Will return true if this spell can counter (or is Reusable and can
|
// Will return true if this spell can counter (or is Reusable and can
|
||||||
// force the Human into making decisions)
|
// force the Human into making decisions)
|
||||||
|
|
||||||
// But really it should be more picky about how it counters things
|
// But really it should be more picky about how it counters things
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Pair<SpellAbility, Boolean> chooseTargetSpellAbility(Game game, SpellAbility sa, Player ai, boolean mandatory) {
|
public Pair<SpellAbility, Boolean> chooseTargetSpellAbility(Game game, SpellAbility sa, Player ai, boolean mandatory) {
|
||||||
SpellAbility tgtSA;
|
SpellAbility tgtSA;
|
||||||
SpellAbility leastBadOption = null;
|
SpellAbility leastBadOption = null;
|
||||||
SpellAbility bestOption = null;
|
SpellAbility bestOption = null;
|
||||||
|
|
||||||
Iterator<SpellAbilityStackInstance> it = game.getStack().iterator();
|
Iterator<SpellAbilityStackInstance> it = game.getStack().iterator();
|
||||||
SpellAbilityStackInstance si = null;
|
SpellAbilityStackInstance si = null;
|
||||||
while(it.hasNext()) {
|
while(it.hasNext()) {
|
||||||
si = it.next();
|
si = it.next();
|
||||||
tgtSA = si.getSpellAbility(true);
|
tgtSA = si.getSpellAbility(true);
|
||||||
if (!sa.canTargetSpellAbility(tgtSA)) {
|
if (!sa.canTargetSpellAbility(tgtSA)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (leastBadOption == null) {
|
if (leastBadOption == null) {
|
||||||
leastBadOption = tgtSA;
|
leastBadOption = tgtSA;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!CardFactoryUtil.isCounterableBy(tgtSA.getHostCard(), sa) ||
|
if (!CardFactoryUtil.isCounterableBy(tgtSA.getHostCard(), sa) ||
|
||||||
tgtSA.getActivatingPlayer() == ai ||
|
tgtSA.getActivatingPlayer() == ai ||
|
||||||
!tgtSA.getActivatingPlayer().isOpponentOf(ai)) {
|
!tgtSA.getActivatingPlayer().isOpponentOf(ai)) {
|
||||||
// Is this a "better" least bad option
|
// Is this a "better" least bad option
|
||||||
if (leastBadOption.getActivatingPlayer().isOpponentOf(ai)) {
|
if (leastBadOption.getActivatingPlayer().isOpponentOf(ai)) {
|
||||||
// NOOP
|
// NOOP
|
||||||
} else if (sa.getActivatingPlayer().isOpponentOf(ai)) {
|
} else if (sa.getActivatingPlayer().isOpponentOf(ai)) {
|
||||||
// Target opponents uncounterable stuff, before our own stuff
|
// Target opponents uncounterable stuff, before our own stuff
|
||||||
leastBadOption = tgtSA;
|
leastBadOption = tgtSA;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bestOption == null) {
|
if (bestOption == null) {
|
||||||
bestOption = tgtSA;
|
bestOption = tgtSA;
|
||||||
} else {
|
} else {
|
||||||
// TODO Determine if this option is better than the current best option
|
// TODO Determine if this option is better than the current best option
|
||||||
boolean betterThanBest = false;
|
boolean betterThanBest = false;
|
||||||
if (betterThanBest) {
|
if (betterThanBest) {
|
||||||
bestOption = tgtSA;
|
bestOption = tgtSA;
|
||||||
}
|
}
|
||||||
// Don't really need to keep updating leastBadOption once we have a bestOption
|
// Don't really need to keep updating leastBadOption once we have a bestOption
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ImmutablePair<>(bestOption != null ? bestOption : leastBadOption, bestOption != null);
|
return new ImmutablePair<>(bestOption != null ? bestOption : leastBadOption, bestOption != null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,489 +1,489 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import forge.ai.ComputerUtil;
|
import forge.ai.ComputerUtil;
|
||||||
import forge.ai.ComputerUtilCard;
|
import forge.ai.ComputerUtilCard;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.card.*;
|
import forge.game.card.*;
|
||||||
import forge.game.phase.PhaseHandler;
|
import forge.game.phase.PhaseHandler;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
import forge.util.MyRandom;
|
import forge.util.MyRandom;
|
||||||
import forge.util.collect.FCollection;
|
import forge.util.collect.FCollection;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class CountersMoveAi extends SpellAbilityAi {
|
public class CountersMoveAi extends SpellAbilityAi {
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkApiLogic(final Player ai, final SpellAbility sa) {
|
protected boolean checkApiLogic(final Player ai, final SpellAbility sa) {
|
||||||
|
|
||||||
if (sa.usesTargeting()) {
|
if (sa.usesTargeting()) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
if (!moveTgtAI(ai, sa)) {
|
if (!moveTgtAI(ai, sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!SpellAbilityAi.playReusable(ai, sa)) {
|
if (!SpellAbilityAi.playReusable(ai, sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return MyRandom.getRandom().nextFloat() < .8f; // random success
|
return MyRandom.getRandom().nextFloat() < .8f; // random success
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkPhaseRestrictions(final Player ai, final SpellAbility sa, final PhaseHandler ph) {
|
protected boolean checkPhaseRestrictions(final Player ai, final SpellAbility sa, final PhaseHandler ph) {
|
||||||
final Card host = sa.getHostCard();
|
final Card host = sa.getHostCard();
|
||||||
final String type = sa.getParam("CounterType");
|
final String type = sa.getParam("CounterType");
|
||||||
final CounterType cType = "Any".equals(type) ? null : CounterType.valueOf(type);
|
final CounterType cType = "Any".equals(type) ? null : CounterType.valueOf(type);
|
||||||
|
|
||||||
// Don't tap creatures that may be able to block
|
// Don't tap creatures that may be able to block
|
||||||
if (ComputerUtil.waitForBlocking(sa)) {
|
if (ComputerUtil.waitForBlocking(sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CounterType.P1P1.equals(cType) && sa.hasParam("Source")) {
|
if (CounterType.P1P1.equals(cType) && sa.hasParam("Source")) {
|
||||||
int amount = calcAmount(sa, cType);
|
int amount = calcAmount(sa, cType);
|
||||||
final List<Card> srcCards = AbilityUtils.getDefinedCards(host, sa.getParam("Source"), sa);
|
final List<Card> srcCards = AbilityUtils.getDefinedCards(host, sa.getParam("Source"), sa);
|
||||||
if (ph.getPlayerTurn().isOpponentOf(ai)) {
|
if (ph.getPlayerTurn().isOpponentOf(ai)) {
|
||||||
// opponent Creature with +1/+1 counter does attack
|
// opponent Creature with +1/+1 counter does attack
|
||||||
// try to steal counter from it to kill it
|
// try to steal counter from it to kill it
|
||||||
if (ph.inCombat() && ph.getPhase().isBefore(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
|
if (ph.inCombat() && ph.getPhase().isBefore(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
|
||||||
for (final Card c : srcCards) {
|
for (final Card c : srcCards) {
|
||||||
// source is not controlled by current player
|
// source is not controlled by current player
|
||||||
if (!ph.isPlayerTurn(c.getController())) {
|
if (!ph.isPlayerTurn(c.getController())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int a = c.getCounters(cType);
|
int a = c.getCounters(cType);
|
||||||
if (a < amount) {
|
if (a < amount) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (ph.getCombat().isAttacking(c)) {
|
if (ph.getCombat().isAttacking(c)) {
|
||||||
// get copy of creature with removed Counter
|
// get copy of creature with removed Counter
|
||||||
final Card cpy = CardUtil.getLKICopy(c);
|
final Card cpy = CardUtil.getLKICopy(c);
|
||||||
// cant use substract on Copy
|
// cant use substract on Copy
|
||||||
cpy.setCounters(cType, a - amount);
|
cpy.setCounters(cType, a - amount);
|
||||||
|
|
||||||
// a removed counter would kill it
|
// a removed counter would kill it
|
||||||
if (cpy.getNetToughness() <= cpy.getDamage()) {
|
if (cpy.getNetToughness() <= cpy.getDamage()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// something you can't block, try to reduce its
|
// something you can't block, try to reduce its
|
||||||
// attack
|
// attack
|
||||||
if (!ComputerUtilCard.canBeBlockedProfitably(ai, cpy)) {
|
if (!ComputerUtilCard.canBeBlockedProfitably(ai, cpy)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// for Simic Fluxmage and other
|
// for Simic Fluxmage and other
|
||||||
if (!ph.getNextTurn().equals(ai) || ph.getPhase().isBefore(PhaseType.END_OF_TURN)) {
|
if (!ph.getNextTurn().equals(ai) || ph.getPhase().isBefore(PhaseType.END_OF_TURN)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (CounterType.P1P1.equals(cType) && sa.hasParam("Defined")) {
|
} else if (CounterType.P1P1.equals(cType) && sa.hasParam("Defined")) {
|
||||||
// something like Cyptoplast Root-kin
|
// something like Cyptoplast Root-kin
|
||||||
if (ph.getPlayerTurn().isOpponentOf(ai)) {
|
if (ph.getPlayerTurn().isOpponentOf(ai)) {
|
||||||
if (ph.inCombat() && ph.getPhase().isBefore(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
|
if (ph.inCombat() && ph.getPhase().isBefore(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// for Simic Fluxmage and other
|
// for Simic Fluxmage and other
|
||||||
if (!ph.getNextTurn().equals(ai) || ph.getPhase().isBefore(PhaseType.END_OF_TURN)) {
|
if (!ph.getNextTurn().equals(ai) || ph.getPhase().isBefore(PhaseType.END_OF_TURN)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Make sure that removing the last counter doesn't kill the creature
|
// Make sure that removing the last counter doesn't kill the creature
|
||||||
if ("Self".equals(sa.getParam("Source"))) {
|
if ("Self".equals(sa.getParam("Source"))) {
|
||||||
if (host != null && host.getNetToughness() - 1 <= 0) {
|
if (host != null && host.getNetToughness() - 1 <= 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(final Player ai, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(final Player ai, SpellAbility sa, boolean mandatory) {
|
||||||
|
|
||||||
if (sa.usesTargeting()) {
|
if (sa.usesTargeting()) {
|
||||||
|
|
||||||
if (!moveTgtAI(ai, sa) && !mandatory) {
|
if (!moveTgtAI(ai, sa) && !mandatory) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sa.isTargetNumberValid() && mandatory) {
|
if (!sa.isTargetNumberValid() && mandatory) {
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
List<Card> tgtCards = CardLists.getTargetableCards(game.getCardsIn(ZoneType.Battlefield), sa);
|
List<Card> tgtCards = CardLists.getTargetableCards(game.getCardsIn(ZoneType.Battlefield), sa);
|
||||||
|
|
||||||
if (tgtCards.isEmpty()) {
|
if (tgtCards.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Card card = ComputerUtilCard.getWorstAI(tgtCards);
|
final Card card = ComputerUtilCard.getWorstAI(tgtCards);
|
||||||
sa.getTargets().add(card);
|
sa.getTargets().add(card);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
// no target Probably something like Graft
|
// no target Probably something like Graft
|
||||||
|
|
||||||
if (mandatory) {
|
if (mandatory) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Card host = sa.getHostCard();
|
final Card host = sa.getHostCard();
|
||||||
|
|
||||||
final String type = sa.getParam("CounterType");
|
final String type = sa.getParam("CounterType");
|
||||||
final CounterType cType = "Any".equals(type) ? null : CounterType.valueOf(type);
|
final CounterType cType = "Any".equals(type) ? null : CounterType.valueOf(type);
|
||||||
|
|
||||||
final List<Card> srcCards = AbilityUtils.getDefinedCards(host, sa.getParam("Source"), sa);
|
final List<Card> srcCards = AbilityUtils.getDefinedCards(host, sa.getParam("Source"), sa);
|
||||||
final List<Card> destCards = AbilityUtils.getDefinedCards(host, sa.getParam("Defined"), sa);
|
final List<Card> destCards = AbilityUtils.getDefinedCards(host, sa.getParam("Defined"), sa);
|
||||||
|
|
||||||
if (srcCards.isEmpty() || destCards.isEmpty()) {
|
if (srcCards.isEmpty() || destCards.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Card src = srcCards.get(0);
|
final Card src = srcCards.get(0);
|
||||||
final Card dest = destCards.get(0);
|
final Card dest = destCards.get(0);
|
||||||
|
|
||||||
// for such Trigger, do not move counter to another players creature
|
// for such Trigger, do not move counter to another players creature
|
||||||
if (!dest.getController().equals(ai)) {
|
if (!dest.getController().equals(ai)) {
|
||||||
return false;
|
return false;
|
||||||
} else if (ComputerUtilCard.isUselessCreature(ai, dest)) {
|
} else if (ComputerUtilCard.isUselessCreature(ai, dest)) {
|
||||||
return false;
|
return false;
|
||||||
} else if (dest.hasSVar("EndOfTurnLeavePlay")) {
|
} else if (dest.hasSVar("EndOfTurnLeavePlay")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cType != null) {
|
if (cType != null) {
|
||||||
if (!dest.canReceiveCounters(cType)) {
|
if (!dest.canReceiveCounters(cType)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
final int amount = calcAmount(sa, cType);
|
final int amount = calcAmount(sa, cType);
|
||||||
int a = src.getCounters(cType);
|
int a = src.getCounters(cType);
|
||||||
if (a < amount) {
|
if (a < amount) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Card srcCopy = CardUtil.getLKICopy(src);
|
final Card srcCopy = CardUtil.getLKICopy(src);
|
||||||
// cant use substract on Copy
|
// cant use substract on Copy
|
||||||
srcCopy.setCounters(cType, a - amount);
|
srcCopy.setCounters(cType, a - amount);
|
||||||
|
|
||||||
final Card destCopy = CardUtil.getLKICopy(dest);
|
final Card destCopy = CardUtil.getLKICopy(dest);
|
||||||
destCopy.setCounters(cType, dest.getCounters(cType) + amount);
|
destCopy.setCounters(cType, dest.getCounters(cType) + amount);
|
||||||
|
|
||||||
int oldEval = ComputerUtilCard.evaluateCreature(src) + ComputerUtilCard.evaluateCreature(dest);
|
int oldEval = ComputerUtilCard.evaluateCreature(src) + ComputerUtilCard.evaluateCreature(dest);
|
||||||
int newEval = ComputerUtilCard.evaluateCreature(srcCopy) + ComputerUtilCard.evaluateCreature(destCopy);
|
int newEval = ComputerUtilCard.evaluateCreature(srcCopy) + ComputerUtilCard.evaluateCreature(destCopy);
|
||||||
|
|
||||||
if (newEval < oldEval) {
|
if (newEval < oldEval) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for some specific AI preferences
|
// check for some specific AI preferences
|
||||||
if (src.hasStartOfKeyword("Graft") && "DontMoveCounterIfLethal".equals(src.getSVar("AIGraftPreference"))) {
|
if (src.hasStartOfKeyword("Graft") && "DontMoveCounterIfLethal".equals(src.getSVar("AIGraftPreference"))) {
|
||||||
if (cType == CounterType.P1P1 && src.getNetToughness() - src.getTempToughnessBoost() - 1 <= 0) {
|
if (cType == CounterType.P1P1 && src.getNetToughness() - src.getTempToughnessBoost() - 1 <= 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// no target
|
// no target
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
||||||
if (sa.usesTargeting()) {
|
if (sa.usesTargeting()) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
if (!moveTgtAI(ai, sa)) {
|
if (!moveTgtAI(ai, sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int calcAmount(final SpellAbility sa, final CounterType cType) {
|
private static int calcAmount(final SpellAbility sa, final CounterType cType) {
|
||||||
final Card host = sa.getHostCard();
|
final Card host = sa.getHostCard();
|
||||||
|
|
||||||
final String amountStr = sa.getParam("CounterNum");
|
final String amountStr = sa.getParam("CounterNum");
|
||||||
|
|
||||||
// TODO handle proper calculation of X values based on Cost
|
// TODO handle proper calculation of X values based on Cost
|
||||||
int amount = 0;
|
int amount = 0;
|
||||||
|
|
||||||
if (amountStr.equals("All") || amountStr.equals("Any")) {
|
if (amountStr.equals("All") || amountStr.equals("Any")) {
|
||||||
// sa has Source, otherwise Source is the Target
|
// sa has Source, otherwise Source is the Target
|
||||||
if (sa.hasParam("Source")) {
|
if (sa.hasParam("Source")) {
|
||||||
final List<Card> srcCards = AbilityUtils.getDefinedCards(host, sa.getParam("Source"), sa);
|
final List<Card> srcCards = AbilityUtils.getDefinedCards(host, sa.getParam("Source"), sa);
|
||||||
for (final Card c : srcCards) {
|
for (final Card c : srcCards) {
|
||||||
amount += c.getCounters(cType);
|
amount += c.getCounters(cType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
amount = AbilityUtils.calculateAmount(host, amountStr, sa);
|
amount = AbilityUtils.calculateAmount(host, amountStr, sa);
|
||||||
}
|
}
|
||||||
return amount;
|
return amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean moveTgtAI(final Player ai, final SpellAbility sa) {
|
private boolean moveTgtAI(final Player ai, final SpellAbility sa) {
|
||||||
|
|
||||||
final Card host = sa.getHostCard();
|
final Card host = sa.getHostCard();
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
final String type = sa.getParam("CounterType");
|
final String type = sa.getParam("CounterType");
|
||||||
final CounterType cType = "Any".equals(type) ? null : CounterType.valueOf(type);
|
final CounterType cType = "Any".equals(type) ? null : CounterType.valueOf(type);
|
||||||
|
|
||||||
List<Card> tgtCards = CardLists.getTargetableCards(game.getCardsIn(ZoneType.Battlefield), sa);
|
List<Card> tgtCards = CardLists.getTargetableCards(game.getCardsIn(ZoneType.Battlefield), sa);
|
||||||
|
|
||||||
if (sa.hasParam("Defined")) {
|
if (sa.hasParam("Defined")) {
|
||||||
final int amount = calcAmount(sa, cType);
|
final int amount = calcAmount(sa, cType);
|
||||||
tgtCards = CardLists.filter(tgtCards, CardPredicates.hasCounter(cType));
|
tgtCards = CardLists.filter(tgtCards, CardPredicates.hasCounter(cType));
|
||||||
|
|
||||||
// SA uses target for Source
|
// SA uses target for Source
|
||||||
// Target => Defined
|
// Target => Defined
|
||||||
final List<Card> destCards = AbilityUtils.getDefinedCards(host, sa.getParam("Defined"), sa);
|
final List<Card> destCards = AbilityUtils.getDefinedCards(host, sa.getParam("Defined"), sa);
|
||||||
|
|
||||||
if (destCards.isEmpty()) {
|
if (destCards.isEmpty()) {
|
||||||
// something went wrong
|
// something went wrong
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Card dest = destCards.get(0);
|
final Card dest = destCards.get(0);
|
||||||
|
|
||||||
// remove dest from targets, because move doesn't work that way
|
// remove dest from targets, because move doesn't work that way
|
||||||
tgtCards.remove(dest);
|
tgtCards.remove(dest);
|
||||||
|
|
||||||
if (cType != null && !dest.canReceiveCounters(cType)) {
|
if (cType != null && !dest.canReceiveCounters(cType)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// prefered logic for this: try to steal counter
|
// prefered logic for this: try to steal counter
|
||||||
List<Card> oppList = CardLists.filterControlledBy(tgtCards, ai.getOpponents());
|
List<Card> oppList = CardLists.filterControlledBy(tgtCards, ai.getOpponents());
|
||||||
if (!oppList.isEmpty()) {
|
if (!oppList.isEmpty()) {
|
||||||
List<Card> best = CardLists.filter(oppList, new Predicate<Card>() {
|
List<Card> best = CardLists.filter(oppList, new Predicate<Card>() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(Card card) {
|
public boolean apply(Card card) {
|
||||||
// do not weak a useless creature if able
|
// do not weak a useless creature if able
|
||||||
if (ComputerUtilCard.isUselessCreature(ai, card)) {
|
if (ComputerUtilCard.isUselessCreature(ai, card)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Card srcCardCpy = CardUtil.getLKICopy(card);
|
final Card srcCardCpy = CardUtil.getLKICopy(card);
|
||||||
// cant use substract on Copy
|
// cant use substract on Copy
|
||||||
srcCardCpy.setCounters(cType, srcCardCpy.getCounters(cType) - amount);
|
srcCardCpy.setCounters(cType, srcCardCpy.getCounters(cType) - amount);
|
||||||
|
|
||||||
// do not steal a P1P1 from Undying if it would die
|
// do not steal a P1P1 from Undying if it would die
|
||||||
// this way
|
// this way
|
||||||
if (CounterType.P1P1.equals(cType) && srcCardCpy.getNetToughness() <= 0) {
|
if (CounterType.P1P1.equals(cType) && srcCardCpy.getNetToughness() <= 0) {
|
||||||
if (srcCardCpy.getCounters(cType) > 0 || !card.hasKeyword("Undying") || card.isToken()) {
|
if (srcCardCpy.getCounters(cType) > 0 || !card.hasKeyword("Undying") || card.isToken()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// if no Prefered found, try normal list
|
// if no Prefered found, try normal list
|
||||||
if (best.isEmpty()) {
|
if (best.isEmpty()) {
|
||||||
best = oppList;
|
best = oppList;
|
||||||
}
|
}
|
||||||
|
|
||||||
Card card = ComputerUtilCard.getBestCreatureAI(best);
|
Card card = ComputerUtilCard.getBestCreatureAI(best);
|
||||||
|
|
||||||
if (card != null) {
|
if (card != null) {
|
||||||
sa.getTargets().add(card);
|
sa.getTargets().add(card);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// from your creature, try to take from the weakest
|
// from your creature, try to take from the weakest
|
||||||
FCollection<Player> ally = ai.getAllies();
|
FCollection<Player> ally = ai.getAllies();
|
||||||
ally.add(ai);
|
ally.add(ai);
|
||||||
|
|
||||||
List<Card> aiList = CardLists.filterControlledBy(tgtCards, ally);
|
List<Card> aiList = CardLists.filterControlledBy(tgtCards, ally);
|
||||||
if (!aiList.isEmpty()) {
|
if (!aiList.isEmpty()) {
|
||||||
List<Card> best = CardLists.filter(aiList, new Predicate<Card>() {
|
List<Card> best = CardLists.filter(aiList, new Predicate<Card>() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(Card card) {
|
public boolean apply(Card card) {
|
||||||
// gain from useless
|
// gain from useless
|
||||||
if (ComputerUtilCard.isUselessCreature(ai, card)) {
|
if (ComputerUtilCard.isUselessCreature(ai, card)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// source would leave the game
|
// source would leave the game
|
||||||
if (card.hasSVar("EndOfTurnLeavePlay")) {
|
if (card.hasSVar("EndOfTurnLeavePlay")) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// try to remove P1P1 from undying or evolve
|
// try to remove P1P1 from undying or evolve
|
||||||
if (CounterType.P1P1.equals(cType)) {
|
if (CounterType.P1P1.equals(cType)) {
|
||||||
if (card.hasKeyword("Undying") || card.hasKeyword("Evolve")) {
|
if (card.hasKeyword("Undying") || card.hasKeyword("Evolve")) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (CounterType.M1M1.equals(cType) && card.hasKeyword("Persist")) {
|
if (CounterType.M1M1.equals(cType) && card.hasKeyword("Persist")) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (best.isEmpty()) {
|
if (best.isEmpty()) {
|
||||||
best = aiList;
|
best = aiList;
|
||||||
}
|
}
|
||||||
|
|
||||||
Card card = ComputerUtilCard.getWorstCreatureAI(best);
|
Card card = ComputerUtilCard.getWorstCreatureAI(best);
|
||||||
|
|
||||||
if (card != null) {
|
if (card != null) {
|
||||||
sa.getTargets().add(card);
|
sa.getTargets().add(card);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
// SA uses target for Defined
|
// SA uses target for Defined
|
||||||
// Source => Targeted
|
// Source => Targeted
|
||||||
final List<Card> srcCards = AbilityUtils.getDefinedCards(host, sa.getParam("Source"), sa);
|
final List<Card> srcCards = AbilityUtils.getDefinedCards(host, sa.getParam("Source"), sa);
|
||||||
|
|
||||||
if (srcCards.isEmpty()) {
|
if (srcCards.isEmpty()) {
|
||||||
// something went wrong
|
// something went wrong
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Card src = srcCards.get(0);
|
final Card src = srcCards.get(0);
|
||||||
if (cType != null) {
|
if (cType != null) {
|
||||||
if (src.getCounters(cType) <= 0) {
|
if (src.getCounters(cType) <= 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Card> aiList = CardLists.filterControlledBy(tgtCards, ai);
|
List<Card> aiList = CardLists.filterControlledBy(tgtCards, ai);
|
||||||
if (!aiList.isEmpty()) {
|
if (!aiList.isEmpty()) {
|
||||||
List<Card> best = CardLists.filter(aiList, new Predicate<Card>() {
|
List<Card> best = CardLists.filter(aiList, new Predicate<Card>() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(Card card) {
|
public boolean apply(Card card) {
|
||||||
// gain from useless
|
// gain from useless
|
||||||
if (ComputerUtilCard.isUselessCreature(ai, card)) {
|
if (ComputerUtilCard.isUselessCreature(ai, card)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// source would leave the game
|
// source would leave the game
|
||||||
if (card.hasSVar("EndOfTurnLeavePlay")) {
|
if (card.hasSVar("EndOfTurnLeavePlay")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cType != null) {
|
if (cType != null) {
|
||||||
if (CounterType.P1P1.equals(cType) && card.hasKeyword("Undying")) {
|
if (CounterType.P1P1.equals(cType) && card.hasKeyword("Undying")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (CounterType.M1M1.equals(cType) && card.hasKeyword("Persist")) {
|
if (CounterType.M1M1.equals(cType) && card.hasKeyword("Persist")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!card.canReceiveCounters(cType)) {
|
if (!card.canReceiveCounters(cType)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (best.isEmpty()) {
|
if (best.isEmpty()) {
|
||||||
best = aiList;
|
best = aiList;
|
||||||
}
|
}
|
||||||
|
|
||||||
Card card = ComputerUtilCard.getBestCreatureAI(best);
|
Card card = ComputerUtilCard.getBestCreatureAI(best);
|
||||||
|
|
||||||
if (card != null) {
|
if (card != null) {
|
||||||
sa.getTargets().add(card);
|
sa.getTargets().add(card);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// move counter to opponents creature but only if you can not steal
|
// move counter to opponents creature but only if you can not steal
|
||||||
// them
|
// them
|
||||||
// try to move to something useless or something that would leave
|
// try to move to something useless or something that would leave
|
||||||
// play
|
// play
|
||||||
List<Card> oppList = CardLists.filterControlledBy(tgtCards, ai.getOpponents());
|
List<Card> oppList = CardLists.filterControlledBy(tgtCards, ai.getOpponents());
|
||||||
if (!oppList.isEmpty()) {
|
if (!oppList.isEmpty()) {
|
||||||
List<Card> best = CardLists.filter(oppList, new Predicate<Card>() {
|
List<Card> best = CardLists.filter(oppList, new Predicate<Card>() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(Card card) {
|
public boolean apply(Card card) {
|
||||||
// gain from useless
|
// gain from useless
|
||||||
if (!ComputerUtilCard.isUselessCreature(ai, card)) {
|
if (!ComputerUtilCard.isUselessCreature(ai, card)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// source would leave the game
|
// source would leave the game
|
||||||
if (!card.hasSVar("EndOfTurnLeavePlay")) {
|
if (!card.hasSVar("EndOfTurnLeavePlay")) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (best.isEmpty()) {
|
if (best.isEmpty()) {
|
||||||
best = aiList;
|
best = aiList;
|
||||||
}
|
}
|
||||||
|
|
||||||
Card card = ComputerUtilCard.getBestCreatureAI(best);
|
Card card = ComputerUtilCard.getBestCreatureAI(best);
|
||||||
|
|
||||||
if (card != null) {
|
if (card != null) {
|
||||||
sa.getTargets().add(card);
|
sa.getTargets().add(card);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// used for multiple sources -> defied
|
// used for multiple sources -> defied
|
||||||
// or for source -> multiple defined
|
// or for source -> multiple defined
|
||||||
@Override
|
@Override
|
||||||
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional,
|
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional,
|
||||||
Player targetedPlayer) {
|
Player targetedPlayer) {
|
||||||
if (sa.hasParam("AiLogic")) {
|
if (sa.hasParam("AiLogic")) {
|
||||||
String logic = sa.getParam("AiLogic");
|
String logic = sa.getParam("AiLogic");
|
||||||
|
|
||||||
if ("ToValid".equals(logic)) {
|
if ("ToValid".equals(logic)) {
|
||||||
// cards like Forgotten Ancient
|
// cards like Forgotten Ancient
|
||||||
// can put counter on any creature, but should only put one on
|
// can put counter on any creature, but should only put one on
|
||||||
// Ai controlled ones
|
// Ai controlled ones
|
||||||
List<Card> aiCards = CardLists.filterControlledBy(options, ai);
|
List<Card> aiCards = CardLists.filterControlledBy(options, ai);
|
||||||
return ComputerUtilCard.getBestCreatureAI(aiCards);
|
return ComputerUtilCard.getBestCreatureAI(aiCards);
|
||||||
} else if ("FromValid".equals(logic)) {
|
} else if ("FromValid".equals(logic)) {
|
||||||
// cards like Aetherborn Marauder
|
// cards like Aetherborn Marauder
|
||||||
return ComputerUtilCard.getWorstCreatureAI(options);
|
return ComputerUtilCard.getWorstCreatureAI(options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Iterables.getFirst(options, null);
|
return Iterables.getFirst(options, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// used when selecting how many counters to move
|
// used when selecting how many counters to move
|
||||||
@Override
|
@Override
|
||||||
public int chooseNumber(Player player, SpellAbility sa, int min, int max, Map<String, Object> params) {
|
public int chooseNumber(Player player, SpellAbility sa, int min, int max, Map<String, Object> params) {
|
||||||
// TODO improve logic behind it
|
// TODO improve logic behind it
|
||||||
// like keeping the last counter on a 0/0 creature
|
// like keeping the last counter on a 0/0 creature
|
||||||
return max;
|
return max;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,98 +1,98 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
import forge.ai.ComputerUtil;
|
import forge.ai.ComputerUtil;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
import forge.game.card.CounterType;
|
import forge.game.card.CounterType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
|
|
||||||
public class CountersProliferateAi extends SpellAbilityAi {
|
public class CountersProliferateAi extends SpellAbilityAi {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkApiLogic(Player ai, SpellAbility sa) {
|
protected boolean checkApiLogic(Player ai, SpellAbility sa) {
|
||||||
|
|
||||||
final List<Card> cperms = Lists.newArrayList();
|
final List<Card> cperms = Lists.newArrayList();
|
||||||
final List<Player> allies = ai.getAllies();
|
final List<Player> allies = ai.getAllies();
|
||||||
allies.add(ai);
|
allies.add(ai);
|
||||||
boolean allyExpOrEnergy = false;
|
boolean allyExpOrEnergy = false;
|
||||||
|
|
||||||
for (final Player p : allies) {
|
for (final Player p : allies) {
|
||||||
// player has experience or energy counter
|
// player has experience or energy counter
|
||||||
if (p.getCounters(CounterType.EXPERIENCE) + p.getCounters(CounterType.ENERGY) >= 1) {
|
if (p.getCounters(CounterType.EXPERIENCE) + p.getCounters(CounterType.ENERGY) >= 1) {
|
||||||
allyExpOrEnergy = true;
|
allyExpOrEnergy = true;
|
||||||
}
|
}
|
||||||
cperms.addAll(CardLists.filter(p.getCardsIn(ZoneType.Battlefield), new Predicate<Card>() {
|
cperms.addAll(CardLists.filter(p.getCardsIn(ZoneType.Battlefield), new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(final Card crd) {
|
public boolean apply(final Card crd) {
|
||||||
if (crd.hasCounters()) {
|
if (crd.hasCounters()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// iterate only over existing counters
|
// iterate only over existing counters
|
||||||
for (final Map.Entry<CounterType, Integer> e : crd.getCounters().entrySet()) {
|
for (final Map.Entry<CounterType, Integer> e : crd.getCounters().entrySet()) {
|
||||||
if (e.getValue() >= 1 && !ComputerUtil.isNegativeCounter(e.getKey(), crd)) {
|
if (e.getValue() >= 1 && !ComputerUtil.isNegativeCounter(e.getKey(), crd)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<Card> hperms = Lists.newArrayList();
|
final List<Card> hperms = Lists.newArrayList();
|
||||||
boolean opponentPoison = false;
|
boolean opponentPoison = false;
|
||||||
|
|
||||||
for (final Player o : ai.getOpponents()) {
|
for (final Player o : ai.getOpponents()) {
|
||||||
opponentPoison |= o.getPoisonCounters() >= 1;
|
opponentPoison |= o.getPoisonCounters() >= 1;
|
||||||
hperms.addAll(CardLists.filter(o.getCardsIn(ZoneType.Battlefield), new Predicate<Card>() {
|
hperms.addAll(CardLists.filter(o.getCardsIn(ZoneType.Battlefield), new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(final Card crd) {
|
public boolean apply(final Card crd) {
|
||||||
if (crd.hasCounters()) {
|
if (crd.hasCounters()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// iterate only over existing counters
|
// iterate only over existing counters
|
||||||
for (final Map.Entry<CounterType, Integer> e : crd.getCounters().entrySet()) {
|
for (final Map.Entry<CounterType, Integer> e : crd.getCounters().entrySet()) {
|
||||||
if (e.getValue() >= 1 && ComputerUtil.isNegativeCounter(e.getKey(), crd)) {
|
if (e.getValue() >= 1 && ComputerUtil.isNegativeCounter(e.getKey(), crd)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (cperms.isEmpty() && hperms.isEmpty() && !opponentPoison && !allyExpOrEnergy) {
|
if (cperms.isEmpty() && hperms.isEmpty() && !opponentPoison && !allyExpOrEnergy) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
|
||||||
boolean chance = true;
|
boolean chance = true;
|
||||||
|
|
||||||
// TODO Make sure Human has poison counters or there are some counters
|
// TODO Make sure Human has poison counters or there are some counters
|
||||||
// we want to proliferate
|
// we want to proliferate
|
||||||
return chance;
|
return chance;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see forge.card.abilityfactory.SpellAiLogic#chkAIDrawback(java.util.Map, forge.card.spellability.SpellAbility, forge.game.player.Player)
|
* @see forge.card.abilityfactory.SpellAiLogic#chkAIDrawback(java.util.Map, forge.card.spellability.SpellAbility, forge.game.player.Player)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
||||||
return canPlayAI(ai, sa);
|
return canPlayAI(ai, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,183 +1,183 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import forge.ai.ComputerUtil;
|
import forge.ai.ComputerUtil;
|
||||||
import forge.ai.ComputerUtilCost;
|
import forge.ai.ComputerUtilCost;
|
||||||
import forge.ai.ComputerUtilMana;
|
import forge.ai.ComputerUtilMana;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
import forge.game.cost.Cost;
|
import forge.game.cost.Cost;
|
||||||
import forge.game.phase.PhaseHandler;
|
import forge.game.phase.PhaseHandler;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.player.PlayerActionConfirmMode;
|
import forge.game.player.PlayerActionConfirmMode;
|
||||||
import forge.game.spellability.AbilitySub;
|
import forge.game.spellability.AbilitySub;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.TargetRestrictions;
|
import forge.game.spellability.TargetRestrictions;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
import forge.util.MyRandom;
|
import forge.util.MyRandom;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
public class CountersPutAllAi extends SpellAbilityAi {
|
public class CountersPutAllAi extends SpellAbilityAi {
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
||||||
// AI needs to be expanded, since this function can be pretty complex
|
// AI needs to be expanded, since this function can be pretty complex
|
||||||
// based on what
|
// based on what
|
||||||
// the expected targets could be
|
// the expected targets could be
|
||||||
final Random r = MyRandom.getRandom();
|
final Random r = MyRandom.getRandom();
|
||||||
final Cost abCost = sa.getPayCosts();
|
final Cost abCost = sa.getPayCosts();
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
List<Card> hList;
|
List<Card> hList;
|
||||||
List<Card> cList;
|
List<Card> cList;
|
||||||
final String type = sa.getParam("CounterType");
|
final String type = sa.getParam("CounterType");
|
||||||
final String amountStr = sa.getParam("CounterNum");
|
final String amountStr = sa.getParam("CounterNum");
|
||||||
final String valid = sa.getParam("ValidCards");
|
final String valid = sa.getParam("ValidCards");
|
||||||
final String logic = sa.getParamOrDefault("AILogic", "");
|
final String logic = sa.getParamOrDefault("AILogic", "");
|
||||||
final boolean curse = sa.isCurse();
|
final boolean curse = sa.isCurse();
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
|
|
||||||
hList = CardLists.getValidCards(ComputerUtil.getOpponentFor(ai).getCardsIn(ZoneType.Battlefield), valid, source.getController(), source);
|
hList = CardLists.getValidCards(ComputerUtil.getOpponentFor(ai).getCardsIn(ZoneType.Battlefield), valid, source.getController(), source);
|
||||||
cList = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), valid, source.getController(), source);
|
cList = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), valid, source.getController(), source);
|
||||||
|
|
||||||
if (abCost != null) {
|
if (abCost != null) {
|
||||||
// AI currently disabled for these costs
|
// AI currently disabled for these costs
|
||||||
if (!ComputerUtilCost.checkLifeCost(ai, abCost, source, 8, sa)) {
|
if (!ComputerUtilCost.checkLifeCost(ai, abCost, source, 8, sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ComputerUtilCost.checkDiscardCost(ai, abCost, source)) {
|
if (!ComputerUtilCost.checkDiscardCost(ai, abCost, source)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ComputerUtilCost.checkSacrificeCost(ai, abCost, source, sa)) {
|
if (!ComputerUtilCost.checkSacrificeCost(ai, abCost, source, sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (logic.equals("AtEOTOrBlock")) {
|
if (logic.equals("AtEOTOrBlock")) {
|
||||||
if (!ai.getGame().getPhaseHandler().is(PhaseType.END_OF_TURN) && !ai.getGame().getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
|
if (!ai.getGame().getPhaseHandler().is(PhaseType.END_OF_TURN) && !ai.getGame().getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (logic.equals("AtOppEOT")) {
|
} else if (logic.equals("AtOppEOT")) {
|
||||||
if (!(ai.getGame().getPhaseHandler().is(PhaseType.END_OF_TURN) && ai.getGame().getPhaseHandler().getNextTurn() == ai)) {
|
if (!(ai.getGame().getPhaseHandler().is(PhaseType.END_OF_TURN) && ai.getGame().getPhaseHandler().getNextTurn() == ai)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tgt != null) {
|
if (tgt != null) {
|
||||||
Player pl = curse ? ComputerUtil.getOpponentFor(ai) : ai;
|
Player pl = curse ? ComputerUtil.getOpponentFor(ai) : ai;
|
||||||
sa.getTargets().add(pl);
|
sa.getTargets().add(pl);
|
||||||
|
|
||||||
hList = CardLists.filterControlledBy(hList, pl);
|
hList = CardLists.filterControlledBy(hList, pl);
|
||||||
cList = CardLists.filterControlledBy(cList, pl);
|
cList = CardLists.filterControlledBy(cList, pl);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO improve X value to don't overpay when extra mana won't do
|
// TODO improve X value to don't overpay when extra mana won't do
|
||||||
// anything more useful
|
// anything more useful
|
||||||
final int amount;
|
final int amount;
|
||||||
if (amountStr.equals("X") && source.getSVar(amountStr).equals("Count$xPaid")) {
|
if (amountStr.equals("X") && source.getSVar(amountStr).equals("Count$xPaid")) {
|
||||||
// Set PayX here to maximum value.
|
// Set PayX here to maximum value.
|
||||||
amount = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
amount = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
||||||
source.setSVar("PayX", Integer.toString(amount));
|
source.setSVar("PayX", Integer.toString(amount));
|
||||||
} else {
|
} else {
|
||||||
amount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, sa);
|
amount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
// prevent run-away activations - first time will always return true
|
// prevent run-away activations - first time will always return true
|
||||||
boolean chance = r.nextFloat() <= Math.pow(.6667, sa.getActivationsThisTurn());
|
boolean chance = r.nextFloat() <= Math.pow(.6667, sa.getActivationsThisTurn());
|
||||||
|
|
||||||
if (curse) {
|
if (curse) {
|
||||||
if (type.equals("M1M1")) {
|
if (type.equals("M1M1")) {
|
||||||
final List<Card> killable = CardLists.filter(hList, new Predicate<Card>() {
|
final List<Card> killable = CardLists.filter(hList, new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(final Card c) {
|
public boolean apply(final Card c) {
|
||||||
return c.getNetToughness() <= amount;
|
return c.getNetToughness() <= amount;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (!(killable.size() > 2)) {
|
if (!(killable.size() > 2)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// make sure compy doesn't harm his stuff more than human's
|
// make sure compy doesn't harm his stuff more than human's
|
||||||
// stuff
|
// stuff
|
||||||
if (cList.size() > hList.size()) {
|
if (cList.size() > hList.size()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// human has more things that will benefit, don't play
|
// human has more things that will benefit, don't play
|
||||||
if (hList.size() >= cList.size()) {
|
if (hList.size() >= cList.size()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Check for cards that could profit from the ability
|
//Check for cards that could profit from the ability
|
||||||
PhaseHandler phase = ai.getGame().getPhaseHandler();
|
PhaseHandler phase = ai.getGame().getPhaseHandler();
|
||||||
if (type.equals("P1P1") && sa.isAbility() && source.isCreature()
|
if (type.equals("P1P1") && sa.isAbility() && source.isCreature()
|
||||||
&& sa.getPayCosts() != null && sa.getPayCosts().hasTapCost()
|
&& sa.getPayCosts() != null && sa.getPayCosts().hasTapCost()
|
||||||
&& sa instanceof AbilitySub
|
&& sa instanceof AbilitySub
|
||||||
&& (!phase.getNextTurn().equals(ai)
|
&& (!phase.getNextTurn().equals(ai)
|
||||||
|| phase.getPhase().isBefore(PhaseType.COMBAT_DECLARE_BLOCKERS))) {
|
|| phase.getPhase().isBefore(PhaseType.COMBAT_DECLARE_BLOCKERS))) {
|
||||||
boolean combatants = false;
|
boolean combatants = false;
|
||||||
for (Card c : hList) {
|
for (Card c : hList) {
|
||||||
if (!c.equals(source) && c.isUntapped()) {
|
if (!c.equals(source) && c.isUntapped()) {
|
||||||
combatants = true;
|
combatants = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!combatants) {
|
if (!combatants) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SpellAbilityAi.playReusable(ai, sa)) {
|
if (SpellAbilityAi.playReusable(ai, sa)) {
|
||||||
return chance;
|
return chance;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ((r.nextFloat() < .6667) && chance);
|
return ((r.nextFloat() < .6667) && chance);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
||||||
return canPlayAI(ai, sa);
|
return canPlayAI(ai, sa);
|
||||||
}
|
}
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.game.player.Player, forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.game.player.Player, forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
||||||
return player.getCreaturesInPlay().size() >= ComputerUtil.getOpponentFor(player).getCreaturesInPlay().size();
|
return player.getCreaturesInPlay().size() >= ComputerUtil.getOpponentFor(player).getCreaturesInPlay().size();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(final Player aiPlayer, final SpellAbility sa, final boolean mandatory) {
|
protected boolean doTriggerAINoCost(final Player aiPlayer, final SpellAbility sa, final boolean mandatory) {
|
||||||
if (sa.usesTargeting()) {
|
if (sa.usesTargeting()) {
|
||||||
List<Player> players = Lists.newArrayList();
|
List<Player> players = Lists.newArrayList();
|
||||||
if (!sa.isCurse()) {
|
if (!sa.isCurse()) {
|
||||||
players.add(aiPlayer);
|
players.add(aiPlayer);
|
||||||
}
|
}
|
||||||
players.addAll(aiPlayer.getOpponents());
|
players.addAll(aiPlayer.getOpponents());
|
||||||
players.addAll(aiPlayer.getAllies());
|
players.addAll(aiPlayer.getAllies());
|
||||||
if (sa.isCurse()) {
|
if (sa.isCurse()) {
|
||||||
players.add(aiPlayer);
|
players.add(aiPlayer);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (final Player p : players) {
|
for (final Player p : players) {
|
||||||
if (p.canBeTargetedBy(sa) && sa.canTarget(p)) {
|
if (p.canBeTargetedBy(sa) && sa.canTarget(p)) {
|
||||||
boolean preferred = false;
|
boolean preferred = false;
|
||||||
preferred = (sa.isCurse() && p.isOpponentOf(aiPlayer)) || (!sa.isCurse() && p == aiPlayer);
|
preferred = (sa.isCurse() && p.isOpponentOf(aiPlayer)) || (!sa.isCurse() && p == aiPlayer);
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
sa.getTargets().add(p);
|
sa.getTargets().add(p);
|
||||||
return preferred || mandatory;
|
return preferred || mandatory;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return mandatory;
|
return mandatory;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,287 +1,287 @@
|
|||||||
/*
|
/*
|
||||||
* Forge: Play Magic: the Gathering.
|
* Forge: Play Magic: the Gathering.
|
||||||
* Copyright (C) 2011 Forge Team
|
* Copyright (C) 2011 Forge Team
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful,
|
* This program is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import forge.ai.ComputerUtil;
|
import forge.ai.ComputerUtil;
|
||||||
import forge.ai.ComputerUtilCard;
|
import forge.ai.ComputerUtilCard;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.GlobalRuleChange;
|
import forge.game.GlobalRuleChange;
|
||||||
import forge.game.card.*;
|
import forge.game.card.*;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.player.PlayerController.BinaryChoiceType;
|
import forge.game.player.PlayerController.BinaryChoiceType;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.TargetRestrictions;
|
import forge.game.spellability.TargetRestrictions;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* AbilityFactory_PutOrRemoveCountersAi class.
|
* AbilityFactory_PutOrRemoveCountersAi class.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @author Forge
|
* @author Forge
|
||||||
* @version $Id$
|
* @version $Id$
|
||||||
*/
|
*/
|
||||||
public class CountersPutOrRemoveAi extends SpellAbilityAi {
|
public class CountersPutOrRemoveAi extends SpellAbilityAi {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
*
|
*
|
||||||
* @see forge.ai.SpellAbilityAi#checkApiLogic(forge.game.player.Player,
|
* @see forge.ai.SpellAbilityAi#checkApiLogic(forge.game.player.Player,
|
||||||
* forge.game.spellability.SpellAbility)
|
* forge.game.spellability.SpellAbility)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkApiLogic(Player ai, SpellAbility sa) {
|
protected boolean checkApiLogic(Player ai, SpellAbility sa) {
|
||||||
if (sa.usesTargeting()) {
|
if (sa.usesTargeting()) {
|
||||||
return doTgt(ai, sa, false);
|
return doTgt(ai, sa, false);
|
||||||
}
|
}
|
||||||
return super.checkApiLogic(ai, sa);
|
return super.checkApiLogic(ai, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean doTgt(Player ai, SpellAbility sa, boolean mandatory) {
|
private boolean doTgt(Player ai, SpellAbility sa, boolean mandatory) {
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
|
|
||||||
final int amount = Integer.valueOf(sa.getParam("CounterNum"));
|
final int amount = Integer.valueOf(sa.getParam("CounterNum"));
|
||||||
|
|
||||||
// remove counter with Time might use Exile Zone too
|
// remove counter with Time might use Exile Zone too
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
// need to targetable
|
// need to targetable
|
||||||
CardCollection list = CardLists.getTargetableCards(game.getCardsIn(tgt.getZone()), sa);
|
CardCollection list = CardLists.getTargetableCards(game.getCardsIn(tgt.getZone()), sa);
|
||||||
|
|
||||||
if (list.isEmpty()) {
|
if (list.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter AI-specific targets if provided
|
// Filter AI-specific targets if provided
|
||||||
list = ComputerUtil.filterAITgts(sa, ai, (CardCollection)list, false);
|
list = ComputerUtil.filterAITgts(sa, ai, (CardCollection)list, false);
|
||||||
|
|
||||||
if (sa.hasParam("CounterType")) {
|
if (sa.hasParam("CounterType")) {
|
||||||
// currently only Jhoira's Timebug
|
// currently only Jhoira's Timebug
|
||||||
final CounterType type = CounterType.valueOf(sa.getParam("CounterType"));
|
final CounterType type = CounterType.valueOf(sa.getParam("CounterType"));
|
||||||
|
|
||||||
CardCollection countersList = CardLists.filter(list, CardPredicates.hasCounter(type, amount));
|
CardCollection countersList = CardLists.filter(list, CardPredicates.hasCounter(type, amount));
|
||||||
|
|
||||||
if (countersList.isEmpty()) {
|
if (countersList.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// currently can only target cards you control or you own
|
// currently can only target cards you control or you own
|
||||||
final Card best = ComputerUtilCard.getBestAI(countersList);
|
final Card best = ComputerUtilCard.getBestAI(countersList);
|
||||||
|
|
||||||
// currently both cards only has one target
|
// currently both cards only has one target
|
||||||
sa.getTargets().add(best);
|
sa.getTargets().add(best);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
// currently only Clockspinning
|
// currently only Clockspinning
|
||||||
boolean noLegendary = game.getStaticEffects().getGlobalRuleChange(GlobalRuleChange.noLegendRule);
|
boolean noLegendary = game.getStaticEffects().getGlobalRuleChange(GlobalRuleChange.noLegendRule);
|
||||||
|
|
||||||
// logic to remove some counter
|
// logic to remove some counter
|
||||||
CardCollection countersList = CardLists.filter(list, CardPredicates.hasCounters());
|
CardCollection countersList = CardLists.filter(list, CardPredicates.hasCounters());
|
||||||
|
|
||||||
if (!countersList.isEmpty()) {
|
if (!countersList.isEmpty()) {
|
||||||
|
|
||||||
if (!ai.isCardInPlay("Marit Lage") || noLegendary) {
|
if (!ai.isCardInPlay("Marit Lage") || noLegendary) {
|
||||||
CardCollectionView depthsList = CardLists.filter(countersList,
|
CardCollectionView depthsList = CardLists.filter(countersList,
|
||||||
CardPredicates.nameEquals("Dark Depths"), CardPredicates.hasCounter(CounterType.ICE));
|
CardPredicates.nameEquals("Dark Depths"), CardPredicates.hasCounter(CounterType.ICE));
|
||||||
|
|
||||||
if (!depthsList.isEmpty()) {
|
if (!depthsList.isEmpty()) {
|
||||||
sa.getTargets().add(depthsList.getFirst());
|
sa.getTargets().add(depthsList.getFirst());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get rid of Planeswalkers, currently only if it can kill them
|
// Get rid of Planeswalkers, currently only if it can kill them
|
||||||
// with one touch
|
// with one touch
|
||||||
CardCollection planeswalkerList = CardLists.filter(
|
CardCollection planeswalkerList = CardLists.filter(
|
||||||
CardLists.filterControlledBy(countersList, ai.getOpponents()),
|
CardLists.filterControlledBy(countersList, ai.getOpponents()),
|
||||||
CardPredicates.Presets.PLANEWALKERS,
|
CardPredicates.Presets.PLANEWALKERS,
|
||||||
CardPredicates.hasLessCounter(CounterType.LOYALTY, amount));
|
CardPredicates.hasLessCounter(CounterType.LOYALTY, amount));
|
||||||
|
|
||||||
if (!planeswalkerList.isEmpty()) {
|
if (!planeswalkerList.isEmpty()) {
|
||||||
sa.getTargets().add(ComputerUtilCard.getBestPlaneswalkerAI(planeswalkerList));
|
sa.getTargets().add(ComputerUtilCard.getBestPlaneswalkerAI(planeswalkerList));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// do as M1M1 part
|
// do as M1M1 part
|
||||||
CardCollection aiList = CardLists.filterControlledBy(countersList, ai);
|
CardCollection aiList = CardLists.filterControlledBy(countersList, ai);
|
||||||
|
|
||||||
CardCollection aiM1M1List = CardLists.filter(aiList, CardPredicates.hasCounter(CounterType.M1M1));
|
CardCollection aiM1M1List = CardLists.filter(aiList, CardPredicates.hasCounter(CounterType.M1M1));
|
||||||
|
|
||||||
CardCollection aiPersistList = CardLists.getKeyword(aiM1M1List, "Persist");
|
CardCollection aiPersistList = CardLists.getKeyword(aiM1M1List, "Persist");
|
||||||
if (!aiPersistList.isEmpty()) {
|
if (!aiPersistList.isEmpty()) {
|
||||||
aiM1M1List = aiPersistList;
|
aiM1M1List = aiPersistList;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!aiM1M1List.isEmpty()) {
|
if (!aiM1M1List.isEmpty()) {
|
||||||
sa.getTargets().add(ComputerUtilCard.getBestCreatureAI(aiM1M1List));
|
sa.getTargets().add(ComputerUtilCard.getBestCreatureAI(aiM1M1List));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// do as P1P1 part
|
// do as P1P1 part
|
||||||
CardCollection aiP1P1List = CardLists.filter(aiList, CardPredicates.hasCounter(CounterType.P1P1));
|
CardCollection aiP1P1List = CardLists.filter(aiList, CardPredicates.hasCounter(CounterType.P1P1));
|
||||||
CardCollection aiUndyingList = CardLists.getKeyword(aiM1M1List, "Undying");
|
CardCollection aiUndyingList = CardLists.getKeyword(aiM1M1List, "Undying");
|
||||||
|
|
||||||
if (!aiUndyingList.isEmpty()) {
|
if (!aiUndyingList.isEmpty()) {
|
||||||
aiP1P1List = aiUndyingList;
|
aiP1P1List = aiUndyingList;
|
||||||
}
|
}
|
||||||
if (!aiP1P1List.isEmpty()) {
|
if (!aiP1P1List.isEmpty()) {
|
||||||
sa.getTargets().add(ComputerUtilCard.getBestCreatureAI(aiP1P1List));
|
sa.getTargets().add(ComputerUtilCard.getBestCreatureAI(aiP1P1List));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// fallback to remove any counter from opponent
|
// fallback to remove any counter from opponent
|
||||||
CardCollection oppList = CardLists.filterControlledBy(countersList, ai.getOpponents());
|
CardCollection oppList = CardLists.filterControlledBy(countersList, ai.getOpponents());
|
||||||
oppList = CardLists.filter(oppList, CardPredicates.hasCounters());
|
oppList = CardLists.filter(oppList, CardPredicates.hasCounters());
|
||||||
if (!oppList.isEmpty()) {
|
if (!oppList.isEmpty()) {
|
||||||
final Card best = ComputerUtilCard.getBestAI(oppList);
|
final Card best = ComputerUtilCard.getBestAI(oppList);
|
||||||
|
|
||||||
for (final CounterType aType : best.getCounters().keySet()) {
|
for (final CounterType aType : best.getCounters().keySet()) {
|
||||||
if (!ComputerUtil.isNegativeCounter(aType, best)) {
|
if (!ComputerUtil.isNegativeCounter(aType, best)) {
|
||||||
sa.getTargets().add(best);
|
sa.getTargets().add(best);
|
||||||
return true;
|
return true;
|
||||||
} else if (!ComputerUtil.isUselessCounter(aType)) {
|
} else if (!ComputerUtil.isUselessCounter(aType)) {
|
||||||
// whould remove positive counter
|
// whould remove positive counter
|
||||||
if (best.getCounters(aType) <= amount) {
|
if (best.getCounters(aType) <= amount) {
|
||||||
sa.getTargets().add(best);
|
sa.getTargets().add(best);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mandatory) {
|
if (mandatory) {
|
||||||
sa.getTargets().add(ComputerUtilCard.getWorstAI(list));
|
sa.getTargets().add(ComputerUtilCard.getWorstAI(list));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
||||||
return doTgt(ai, sa, true);
|
return doTgt(ai, sa, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
*
|
*
|
||||||
* @see forge.ai.SpellAbilityAi#chooseCounterType(java.util.List,
|
* @see forge.ai.SpellAbilityAi#chooseCounterType(java.util.List,
|
||||||
* forge.game.spellability.SpellAbility, java.util.Map)
|
* forge.game.spellability.SpellAbility, java.util.Map)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public CounterType chooseCounterType(List<CounterType> options, SpellAbility sa, Map<String, Object> params) {
|
public CounterType chooseCounterType(List<CounterType> options, SpellAbility sa, Map<String, Object> params) {
|
||||||
|
|
||||||
if (options.size() > 1) {
|
if (options.size() > 1) {
|
||||||
final Player ai = sa.getActivatingPlayer();
|
final Player ai = sa.getActivatingPlayer();
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
|
|
||||||
boolean noLegendary = game.getStaticEffects().getGlobalRuleChange(GlobalRuleChange.noLegendRule);
|
boolean noLegendary = game.getStaticEffects().getGlobalRuleChange(GlobalRuleChange.noLegendRule);
|
||||||
|
|
||||||
Card tgt = (Card) params.get("Target");
|
Card tgt = (Card) params.get("Target");
|
||||||
|
|
||||||
// planeswalker has high priority for loyalty counters
|
// planeswalker has high priority for loyalty counters
|
||||||
if (tgt.isPlaneswalker() && options.contains(CounterType.LOYALTY)) {
|
if (tgt.isPlaneswalker() && options.contains(CounterType.LOYALTY)) {
|
||||||
return CounterType.LOYALTY;
|
return CounterType.LOYALTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tgt.getController().isOpponentOf(ai)) {
|
if (tgt.getController().isOpponentOf(ai)) {
|
||||||
// creatures with BaseToughness below or equal zero might be
|
// creatures with BaseToughness below or equal zero might be
|
||||||
// killed if their counters are removed
|
// killed if their counters are removed
|
||||||
if (tgt.isCreature() && tgt.getBaseToughness() <= 0) {
|
if (tgt.isCreature() && tgt.getBaseToughness() <= 0) {
|
||||||
if (options.contains(CounterType.P1P1)) {
|
if (options.contains(CounterType.P1P1)) {
|
||||||
return CounterType.P1P1;
|
return CounterType.P1P1;
|
||||||
} else if (options.contains(CounterType.M1M1)) {
|
} else if (options.contains(CounterType.M1M1)) {
|
||||||
return CounterType.M1M1;
|
return CounterType.M1M1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// fallback logic, select positive counter to remove it
|
// fallback logic, select positive counter to remove it
|
||||||
for (final CounterType type : options) {
|
for (final CounterType type : options) {
|
||||||
if (!ComputerUtil.isNegativeCounter(type, tgt)) {
|
if (!ComputerUtil.isNegativeCounter(type, tgt)) {
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// this counters are treat first to be removed
|
// this counters are treat first to be removed
|
||||||
if ("Dark Depths".equals(tgt.getName()) && options.contains(CounterType.ICE)) {
|
if ("Dark Depths".equals(tgt.getName()) && options.contains(CounterType.ICE)) {
|
||||||
if (!ai.isCardInPlay("Marit Lage") || noLegendary) {
|
if (!ai.isCardInPlay("Marit Lage") || noLegendary) {
|
||||||
return CounterType.ICE;
|
return CounterType.ICE;
|
||||||
}
|
}
|
||||||
} else if (tgt.hasKeyword("Undying") && options.contains(CounterType.P1P1)) {
|
} else if (tgt.hasKeyword("Undying") && options.contains(CounterType.P1P1)) {
|
||||||
return CounterType.P1P1;
|
return CounterType.P1P1;
|
||||||
} else if (tgt.hasKeyword("Persist") && options.contains(CounterType.M1M1)) {
|
} else if (tgt.hasKeyword("Persist") && options.contains(CounterType.M1M1)) {
|
||||||
return CounterType.M1M1;
|
return CounterType.M1M1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// fallback logic, select positive counter to add more
|
// fallback logic, select positive counter to add more
|
||||||
for (final CounterType type : options) {
|
for (final CounterType type : options) {
|
||||||
if (!ComputerUtil.isNegativeCounter(type, tgt)) {
|
if (!ComputerUtil.isNegativeCounter(type, tgt)) {
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.chooseCounterType(options, sa, params);
|
return super.chooseCounterType(options, sa, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
*
|
*
|
||||||
* @see
|
* @see
|
||||||
* forge.ai.SpellAbilityAi#chooseBinary(forge.game.player.PlayerController.
|
* forge.ai.SpellAbilityAi#chooseBinary(forge.game.player.PlayerController.
|
||||||
* BinaryChoiceType, forge.game.spellability.SpellAbility, java.util.Map)
|
* BinaryChoiceType, forge.game.spellability.SpellAbility, java.util.Map)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean chooseBinary(BinaryChoiceType kindOfChoice, SpellAbility sa, Map<String, Object> params) {
|
public boolean chooseBinary(BinaryChoiceType kindOfChoice, SpellAbility sa, Map<String, Object> params) {
|
||||||
if (kindOfChoice.equals(BinaryChoiceType.AddOrRemove)) {
|
if (kindOfChoice.equals(BinaryChoiceType.AddOrRemove)) {
|
||||||
final Player ai = sa.getActivatingPlayer();
|
final Player ai = sa.getActivatingPlayer();
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
Card tgt = (Card) params.get("Target");
|
Card tgt = (Card) params.get("Target");
|
||||||
CounterType type = (CounterType) params.get("CounterType");
|
CounterType type = (CounterType) params.get("CounterType");
|
||||||
|
|
||||||
boolean noLegendary = game.getStaticEffects().getGlobalRuleChange(GlobalRuleChange.noLegendRule);
|
boolean noLegendary = game.getStaticEffects().getGlobalRuleChange(GlobalRuleChange.noLegendRule);
|
||||||
|
|
||||||
if (tgt.getController().isOpponentOf(ai)) {
|
if (tgt.getController().isOpponentOf(ai)) {
|
||||||
if (type.equals(CounterType.LOYALTY) && tgt.isPlaneswalker()) {
|
if (type.equals(CounterType.LOYALTY) && tgt.isPlaneswalker()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ComputerUtil.isNegativeCounter(type, tgt);
|
return ComputerUtil.isNegativeCounter(type, tgt);
|
||||||
} else {
|
} else {
|
||||||
if (type.equals(CounterType.ICE) && "Dark Depths".equals(tgt.getName())) {
|
if (type.equals(CounterType.ICE) && "Dark Depths".equals(tgt.getName())) {
|
||||||
if (!ai.isCardInPlay("Marit Lage") || noLegendary) {
|
if (!ai.isCardInPlay("Marit Lage") || noLegendary) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (type.equals(CounterType.M1M1) && tgt.hasKeyword("Persist")) {
|
} else if (type.equals(CounterType.M1M1) && tgt.hasKeyword("Persist")) {
|
||||||
return false;
|
return false;
|
||||||
} else if (type.equals(CounterType.P1P1) && tgt.hasKeyword("Undying")) {
|
} else if (type.equals(CounterType.P1P1) && tgt.hasKeyword("Undying")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return !ComputerUtil.isNegativeCounter(type, tgt);
|
return !ComputerUtil.isNegativeCounter(type, tgt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return super.chooseBinary(kindOfChoice, sa, params);
|
return super.chooseBinary(kindOfChoice, sa, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,374 +1,374 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import com.google.common.base.Predicates;
|
import com.google.common.base.Predicates;
|
||||||
import forge.ai.ComputerUtil;
|
import forge.ai.ComputerUtil;
|
||||||
import forge.ai.ComputerUtilCard;
|
import forge.ai.ComputerUtilCard;
|
||||||
import forge.ai.ComputerUtilMana;
|
import forge.ai.ComputerUtilMana;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.GlobalRuleChange;
|
import forge.game.GlobalRuleChange;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.card.*;
|
import forge.game.card.*;
|
||||||
import forge.game.phase.PhaseHandler;
|
import forge.game.phase.PhaseHandler;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.TargetRestrictions;
|
import forge.game.spellability.TargetRestrictions;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class CountersRemoveAi extends SpellAbilityAi {
|
public class CountersRemoveAi extends SpellAbilityAi {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
*
|
*
|
||||||
* @see
|
* @see
|
||||||
* forge.ai.SpellAbilityAi#checkPhaseRestrictions(forge.game.player.Player,
|
* forge.ai.SpellAbilityAi#checkPhaseRestrictions(forge.game.player.Player,
|
||||||
* forge.game.spellability.SpellAbility, forge.game.phase.PhaseHandler)
|
* forge.game.spellability.SpellAbility, forge.game.phase.PhaseHandler)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkPhaseRestrictions(Player ai, SpellAbility sa, PhaseHandler ph) {
|
protected boolean checkPhaseRestrictions(Player ai, SpellAbility sa, PhaseHandler ph) {
|
||||||
final String type = sa.getParam("CounterType");
|
final String type = sa.getParam("CounterType");
|
||||||
|
|
||||||
if (ph.getPhase().isBefore(PhaseType.MAIN2) && !sa.hasParam("ActivationPhases") && !type.equals("M1M1")) {
|
if (ph.getPhase().isBefore(PhaseType.MAIN2) && !sa.hasParam("ActivationPhases") && !type.equals("M1M1")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return super.checkPhaseRestrictions(ai, sa, ph);
|
return super.checkPhaseRestrictions(ai, sa, ph);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
*
|
*
|
||||||
* @see
|
* @see
|
||||||
* forge.ai.SpellAbilityAi#checkPhaseRestrictions(forge.game.player.Player,
|
* forge.ai.SpellAbilityAi#checkPhaseRestrictions(forge.game.player.Player,
|
||||||
* forge.game.spellability.SpellAbility, forge.game.phase.PhaseHandler,
|
* forge.game.spellability.SpellAbility, forge.game.phase.PhaseHandler,
|
||||||
* java.lang.String)
|
* java.lang.String)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkPhaseRestrictions(Player ai, SpellAbility sa, PhaseHandler ph, String logic) {
|
protected boolean checkPhaseRestrictions(Player ai, SpellAbility sa, PhaseHandler ph, String logic) {
|
||||||
if ("EndOfOpponentsTurn".equals(logic)) {
|
if ("EndOfOpponentsTurn".equals(logic)) {
|
||||||
if (!ph.is(PhaseType.END_OF_TURN) || !ph.getNextTurn().equals(ai)) {
|
if (!ph.is(PhaseType.END_OF_TURN) || !ph.getNextTurn().equals(ai)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return super.checkPhaseRestrictions(ai, sa, ph, logic);
|
return super.checkPhaseRestrictions(ai, sa, ph, logic);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
*
|
*
|
||||||
* @see forge.ai.SpellAbilityAi#checkApiLogic(forge.game.player.Player,
|
* @see forge.ai.SpellAbilityAi#checkApiLogic(forge.game.player.Player,
|
||||||
* forge.game.spellability.SpellAbility)
|
* forge.game.spellability.SpellAbility)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkApiLogic(Player ai, SpellAbility sa) {
|
protected boolean checkApiLogic(Player ai, SpellAbility sa) {
|
||||||
|
|
||||||
final String type = sa.getParam("CounterType");
|
final String type = sa.getParam("CounterType");
|
||||||
|
|
||||||
if (sa.usesTargeting()) {
|
if (sa.usesTargeting()) {
|
||||||
return doTgt(ai, sa, false);
|
return doTgt(ai, sa, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!type.matches("Any") && !type.matches("All")) {
|
if (!type.matches("Any") && !type.matches("All")) {
|
||||||
final int currCounters = sa.getHostCard().getCounters(CounterType.valueOf(type));
|
final int currCounters = sa.getHostCard().getCounters(CounterType.valueOf(type));
|
||||||
if (currCounters < 1) {
|
if (currCounters < 1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.checkApiLogic(ai, sa);
|
return super.checkApiLogic(ai, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean doTgt(Player ai, SpellAbility sa, boolean mandatory) {
|
private boolean doTgt(Player ai, SpellAbility sa, boolean mandatory) {
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
|
|
||||||
final String type = sa.getParam("CounterType");
|
final String type = sa.getParam("CounterType");
|
||||||
final String amountStr = sa.getParam("CounterNum");
|
final String amountStr = sa.getParam("CounterNum");
|
||||||
|
|
||||||
// remove counter with Time might use Exile Zone too
|
// remove counter with Time might use Exile Zone too
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
CardCollection list = new CardCollection(game.getCardsIn(tgt.getZone()));
|
CardCollection list = new CardCollection(game.getCardsIn(tgt.getZone()));
|
||||||
// need to targetable
|
// need to targetable
|
||||||
list = CardLists.getTargetableCards(list, sa);
|
list = CardLists.getTargetableCards(list, sa);
|
||||||
|
|
||||||
if (list.isEmpty()) {
|
if (list.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter AI-specific targets if provided
|
// Filter AI-specific targets if provided
|
||||||
list = ComputerUtil.filterAITgts(sa, ai, (CardCollection)list, false);
|
list = ComputerUtil.filterAITgts(sa, ai, (CardCollection)list, false);
|
||||||
|
|
||||||
boolean noLegendary = game.getStaticEffects().getGlobalRuleChange(GlobalRuleChange.noLegendRule);
|
boolean noLegendary = game.getStaticEffects().getGlobalRuleChange(GlobalRuleChange.noLegendRule);
|
||||||
|
|
||||||
if (type.matches("All")) {
|
if (type.matches("All")) {
|
||||||
// Logic Part for Vampire Hexmage
|
// Logic Part for Vampire Hexmage
|
||||||
// Break Dark Depths
|
// Break Dark Depths
|
||||||
if (!ai.isCardInPlay("Marit Lage") || noLegendary) {
|
if (!ai.isCardInPlay("Marit Lage") || noLegendary) {
|
||||||
CardCollectionView depthsList = ai.getCardsIn(ZoneType.Battlefield, "Dark Depths");
|
CardCollectionView depthsList = ai.getCardsIn(ZoneType.Battlefield, "Dark Depths");
|
||||||
depthsList = CardLists.filter(depthsList, CardPredicates.isTargetableBy(sa),
|
depthsList = CardLists.filter(depthsList, CardPredicates.isTargetableBy(sa),
|
||||||
CardPredicates.hasCounter(CounterType.ICE, 3));
|
CardPredicates.hasCounter(CounterType.ICE, 3));
|
||||||
|
|
||||||
if (!depthsList.isEmpty()) {
|
if (!depthsList.isEmpty()) {
|
||||||
sa.getTargets().add(depthsList.getFirst());
|
sa.getTargets().add(depthsList.getFirst());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get rid of Planeswalkers:
|
// Get rid of Planeswalkers:
|
||||||
list = ai.getOpponents().getCardsIn(ZoneType.Battlefield);
|
list = ai.getOpponents().getCardsIn(ZoneType.Battlefield);
|
||||||
list = CardLists.filter(list, CardPredicates.isTargetableBy(sa));
|
list = CardLists.filter(list, CardPredicates.isTargetableBy(sa));
|
||||||
|
|
||||||
CardCollection planeswalkerList = CardLists.filter(list, CardPredicates.Presets.PLANEWALKERS,
|
CardCollection planeswalkerList = CardLists.filter(list, CardPredicates.Presets.PLANEWALKERS,
|
||||||
CardPredicates.hasCounter(CounterType.LOYALTY, 5));
|
CardPredicates.hasCounter(CounterType.LOYALTY, 5));
|
||||||
|
|
||||||
if (!planeswalkerList.isEmpty()) {
|
if (!planeswalkerList.isEmpty()) {
|
||||||
sa.getTargets().add(ComputerUtilCard.getBestPlaneswalkerAI(planeswalkerList));
|
sa.getTargets().add(ComputerUtilCard.getBestPlaneswalkerAI(planeswalkerList));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (type.matches("Any")) {
|
} else if (type.matches("Any")) {
|
||||||
// variable amount for Hex Parasite
|
// variable amount for Hex Parasite
|
||||||
int amount;
|
int amount;
|
||||||
boolean xPay = false;
|
boolean xPay = false;
|
||||||
if (amountStr.equals("X") && source.getSVar("X").equals("Count$xPaid")) {
|
if (amountStr.equals("X") && source.getSVar("X").equals("Count$xPaid")) {
|
||||||
final int manaLeft = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
final int manaLeft = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
||||||
|
|
||||||
if (manaLeft == 0) {
|
if (manaLeft == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
amount = manaLeft;
|
amount = manaLeft;
|
||||||
xPay = true;
|
xPay = true;
|
||||||
} else {
|
} else {
|
||||||
amount = AbilityUtils.calculateAmount(source, amountStr, sa);
|
amount = AbilityUtils.calculateAmount(source, amountStr, sa);
|
||||||
}
|
}
|
||||||
// try to remove them from Dark Depths and Planeswalkers too
|
// try to remove them from Dark Depths and Planeswalkers too
|
||||||
|
|
||||||
if (!ai.isCardInPlay("Marit Lage") || noLegendary) {
|
if (!ai.isCardInPlay("Marit Lage") || noLegendary) {
|
||||||
CardCollectionView depthsList = ai.getCardsIn(ZoneType.Battlefield, "Dark Depths");
|
CardCollectionView depthsList = ai.getCardsIn(ZoneType.Battlefield, "Dark Depths");
|
||||||
depthsList = CardLists.filter(depthsList, CardPredicates.isTargetableBy(sa),
|
depthsList = CardLists.filter(depthsList, CardPredicates.isTargetableBy(sa),
|
||||||
CardPredicates.hasCounter(CounterType.ICE));
|
CardPredicates.hasCounter(CounterType.ICE));
|
||||||
|
|
||||||
if (!depthsList.isEmpty()) {
|
if (!depthsList.isEmpty()) {
|
||||||
Card depth = depthsList.getFirst();
|
Card depth = depthsList.getFirst();
|
||||||
int ice = depth.getCounters(CounterType.ICE);
|
int ice = depth.getCounters(CounterType.ICE);
|
||||||
if (amount >= ice) {
|
if (amount >= ice) {
|
||||||
sa.getTargets().add(depth);
|
sa.getTargets().add(depth);
|
||||||
if (xPay) {
|
if (xPay) {
|
||||||
source.setSVar("PayX", Integer.toString(ice));
|
source.setSVar("PayX", Integer.toString(ice));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get rid of Planeswalkers:
|
// Get rid of Planeswalkers:
|
||||||
list = game.getPlayers().getCardsIn(ZoneType.Battlefield);
|
list = game.getPlayers().getCardsIn(ZoneType.Battlefield);
|
||||||
list = CardLists.filter(list, CardPredicates.isTargetableBy(sa));
|
list = CardLists.filter(list, CardPredicates.isTargetableBy(sa));
|
||||||
|
|
||||||
CardCollection planeswalkerList = CardLists.filter(list,
|
CardCollection planeswalkerList = CardLists.filter(list,
|
||||||
Predicates.and(CardPredicates.Presets.PLANEWALKERS, CardPredicates.isControlledByAnyOf(ai.getOpponents())),
|
Predicates.and(CardPredicates.Presets.PLANEWALKERS, CardPredicates.isControlledByAnyOf(ai.getOpponents())),
|
||||||
CardPredicates.hasLessCounter(CounterType.LOYALTY, amount));
|
CardPredicates.hasLessCounter(CounterType.LOYALTY, amount));
|
||||||
|
|
||||||
if (!planeswalkerList.isEmpty()) {
|
if (!planeswalkerList.isEmpty()) {
|
||||||
Card best = ComputerUtilCard.getBestPlaneswalkerAI(planeswalkerList);
|
Card best = ComputerUtilCard.getBestPlaneswalkerAI(planeswalkerList);
|
||||||
sa.getTargets().add(best);
|
sa.getTargets().add(best);
|
||||||
if (xPay) {
|
if (xPay) {
|
||||||
source.setSVar("PayX", Integer.toString(best.getCurrentLoyalty()));
|
source.setSVar("PayX", Integer.toString(best.getCurrentLoyalty()));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// some rules only for amount = 1
|
// some rules only for amount = 1
|
||||||
if (!xPay) {
|
if (!xPay) {
|
||||||
// do as M1M1 part
|
// do as M1M1 part
|
||||||
CardCollection aiList = CardLists.filterControlledBy(list, ai);
|
CardCollection aiList = CardLists.filterControlledBy(list, ai);
|
||||||
|
|
||||||
CardCollection aiM1M1List = CardLists.filter(aiList, CardPredicates.hasCounter(CounterType.M1M1));
|
CardCollection aiM1M1List = CardLists.filter(aiList, CardPredicates.hasCounter(CounterType.M1M1));
|
||||||
|
|
||||||
CardCollection aiPersistList = CardLists.getKeyword(aiM1M1List, "Persist");
|
CardCollection aiPersistList = CardLists.getKeyword(aiM1M1List, "Persist");
|
||||||
if (!aiPersistList.isEmpty()) {
|
if (!aiPersistList.isEmpty()) {
|
||||||
aiM1M1List = aiPersistList;
|
aiM1M1List = aiPersistList;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!aiM1M1List.isEmpty()) {
|
if (!aiM1M1List.isEmpty()) {
|
||||||
sa.getTargets().add(ComputerUtilCard.getBestCreatureAI(aiM1M1List));
|
sa.getTargets().add(ComputerUtilCard.getBestCreatureAI(aiM1M1List));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// do as P1P1 part
|
// do as P1P1 part
|
||||||
CardCollection aiP1P1List = CardLists.filter(aiList, CardPredicates.hasCounter(CounterType.P1P1));
|
CardCollection aiP1P1List = CardLists.filter(aiList, CardPredicates.hasCounter(CounterType.P1P1));
|
||||||
CardCollection aiUndyingList = CardLists.getKeyword(aiM1M1List, "Undying");
|
CardCollection aiUndyingList = CardLists.getKeyword(aiM1M1List, "Undying");
|
||||||
|
|
||||||
if (!aiUndyingList.isEmpty()) {
|
if (!aiUndyingList.isEmpty()) {
|
||||||
aiP1P1List = aiUndyingList;
|
aiP1P1List = aiUndyingList;
|
||||||
}
|
}
|
||||||
if (!aiP1P1List.isEmpty()) {
|
if (!aiP1P1List.isEmpty()) {
|
||||||
sa.getTargets().add(ComputerUtilCard.getBestCreatureAI(aiP1P1List));
|
sa.getTargets().add(ComputerUtilCard.getBestCreatureAI(aiP1P1List));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// fallback to remove any counter from opponent
|
// fallback to remove any counter from opponent
|
||||||
CardCollection oppList = CardLists.filterControlledBy(list, ai.getOpponents());
|
CardCollection oppList = CardLists.filterControlledBy(list, ai.getOpponents());
|
||||||
oppList = CardLists.filter(oppList, CardPredicates.hasCounters());
|
oppList = CardLists.filter(oppList, CardPredicates.hasCounters());
|
||||||
if (!oppList.isEmpty()) {
|
if (!oppList.isEmpty()) {
|
||||||
final Card best = ComputerUtilCard.getBestAI(oppList);
|
final Card best = ComputerUtilCard.getBestAI(oppList);
|
||||||
|
|
||||||
for (final CounterType aType : best.getCounters().keySet()) {
|
for (final CounterType aType : best.getCounters().keySet()) {
|
||||||
if (!ComputerUtil.isNegativeCounter(aType, best)) {
|
if (!ComputerUtil.isNegativeCounter(aType, best)) {
|
||||||
sa.getTargets().add(best);
|
sa.getTargets().add(best);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (type.equals("M1M1")) {
|
} else if (type.equals("M1M1")) {
|
||||||
// no special amount for that one yet
|
// no special amount for that one yet
|
||||||
int amount = AbilityUtils.calculateAmount(source, amountStr, sa);
|
int amount = AbilityUtils.calculateAmount(source, amountStr, sa);
|
||||||
CardCollection aiList = CardLists.filterControlledBy(list, ai);
|
CardCollection aiList = CardLists.filterControlledBy(list, ai);
|
||||||
aiList = CardLists.filter(aiList, CardPredicates.hasCounter(CounterType.M1M1, amount));
|
aiList = CardLists.filter(aiList, CardPredicates.hasCounter(CounterType.M1M1, amount));
|
||||||
|
|
||||||
CardCollection aiPersist = CardLists.getKeyword(aiList, "Persist");
|
CardCollection aiPersist = CardLists.getKeyword(aiList, "Persist");
|
||||||
if (!aiPersist.isEmpty()) {
|
if (!aiPersist.isEmpty()) {
|
||||||
aiList = aiPersist;
|
aiList = aiPersist;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO do not remove -1/-1 counters from cards which does need
|
// TODO do not remove -1/-1 counters from cards which does need
|
||||||
// them for abilities
|
// them for abilities
|
||||||
|
|
||||||
if (!aiList.isEmpty()) {
|
if (!aiList.isEmpty()) {
|
||||||
sa.getTargets().add(ComputerUtilCard.getBestCreatureAI(aiList));
|
sa.getTargets().add(ComputerUtilCard.getBestCreatureAI(aiList));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (type.equals("P1P1")) {
|
} else if (type.equals("P1P1")) {
|
||||||
// no special amount for that one yet
|
// no special amount for that one yet
|
||||||
int amount = AbilityUtils.calculateAmount(source, amountStr, sa);
|
int amount = AbilityUtils.calculateAmount(source, amountStr, sa);
|
||||||
|
|
||||||
list = CardLists.filter(list, CardPredicates.hasCounter(CounterType.P1P1, amount));
|
list = CardLists.filter(list, CardPredicates.hasCounter(CounterType.P1P1, amount));
|
||||||
|
|
||||||
// currently only logic for Bloodcrazed Hoplite, but add logic for
|
// currently only logic for Bloodcrazed Hoplite, but add logic for
|
||||||
// targeting ai creatures too
|
// targeting ai creatures too
|
||||||
CardCollection aiList = CardLists.filterControlledBy(list, ai);
|
CardCollection aiList = CardLists.filterControlledBy(list, ai);
|
||||||
if (!aiList.isEmpty()) {
|
if (!aiList.isEmpty()) {
|
||||||
CardCollection aiListUndying = CardLists.getKeyword(aiList, "Undying");
|
CardCollection aiListUndying = CardLists.getKeyword(aiList, "Undying");
|
||||||
if (!aiListUndying.isEmpty()) {
|
if (!aiListUndying.isEmpty()) {
|
||||||
aiList = aiListUndying;
|
aiList = aiListUndying;
|
||||||
}
|
}
|
||||||
if (!aiList.isEmpty()) {
|
if (!aiList.isEmpty()) {
|
||||||
sa.getTargets().add(ComputerUtilCard.getBestCreatureAI(aiList));
|
sa.getTargets().add(ComputerUtilCard.getBestCreatureAI(aiList));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// need to target opponent creatures
|
// need to target opponent creatures
|
||||||
CardCollection oppList = CardLists.filterControlledBy(list, ai.getOpponents());
|
CardCollection oppList = CardLists.filterControlledBy(list, ai.getOpponents());
|
||||||
if (!oppList.isEmpty()) {
|
if (!oppList.isEmpty()) {
|
||||||
CardCollection oppListNotUndying = CardLists.getNotKeyword(oppList, "Undying");
|
CardCollection oppListNotUndying = CardLists.getNotKeyword(oppList, "Undying");
|
||||||
if (!oppListNotUndying.isEmpty()) {
|
if (!oppListNotUndying.isEmpty()) {
|
||||||
oppList = oppListNotUndying;
|
oppList = oppListNotUndying;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!oppList.isEmpty()) {
|
if (!oppList.isEmpty()) {
|
||||||
sa.getTargets().add(ComputerUtilCard.getWorstCreatureAI(oppList));
|
sa.getTargets().add(ComputerUtilCard.getWorstCreatureAI(oppList));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (type.equals("TIME")) {
|
} else if (type.equals("TIME")) {
|
||||||
int amount;
|
int amount;
|
||||||
boolean xPay = false;
|
boolean xPay = false;
|
||||||
// Timecrafting has X R
|
// Timecrafting has X R
|
||||||
if (amountStr.equals("X") && source.getSVar("X").equals("Count$xPaid")) {
|
if (amountStr.equals("X") && source.getSVar("X").equals("Count$xPaid")) {
|
||||||
final int manaLeft = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
final int manaLeft = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
||||||
|
|
||||||
if (manaLeft == 0) {
|
if (manaLeft == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
amount = manaLeft;
|
amount = manaLeft;
|
||||||
xPay = true;
|
xPay = true;
|
||||||
} else {
|
} else {
|
||||||
amount = AbilityUtils.calculateAmount(source, amountStr, sa);
|
amount = AbilityUtils.calculateAmount(source, amountStr, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
CardCollection timeList = CardLists.filter(list, CardPredicates.hasLessCounter(CounterType.TIME, amount));
|
CardCollection timeList = CardLists.filter(list, CardPredicates.hasLessCounter(CounterType.TIME, amount));
|
||||||
|
|
||||||
if (!timeList.isEmpty()) {
|
if (!timeList.isEmpty()) {
|
||||||
Card best = ComputerUtilCard.getBestAI(timeList);
|
Card best = ComputerUtilCard.getBestAI(timeList);
|
||||||
|
|
||||||
int timeCount = best.getCounters(CounterType.TIME);
|
int timeCount = best.getCounters(CounterType.TIME);
|
||||||
sa.getTargets().add(best);
|
sa.getTargets().add(best);
|
||||||
if (xPay) {
|
if (xPay) {
|
||||||
source.setSVar("PayX", Integer.toString(timeCount));
|
source.setSVar("PayX", Integer.toString(timeCount));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (mandatory) {
|
if (mandatory) {
|
||||||
sa.getTargets().add(ComputerUtilCard.getWorstAI(list));
|
sa.getTargets().add(ComputerUtilCard.getWorstAI(list));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
|
||||||
if (sa.usesTargeting()) {
|
if (sa.usesTargeting()) {
|
||||||
return doTgt(aiPlayer, sa, mandatory);
|
return doTgt(aiPlayer, sa, mandatory);
|
||||||
}
|
}
|
||||||
return mandatory;
|
return mandatory;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
*
|
*
|
||||||
* @see forge.ai.SpellAbilityAi#chooseNumber(forge.game.player.Player,
|
* @see forge.ai.SpellAbilityAi#chooseNumber(forge.game.player.Player,
|
||||||
* forge.game.spellability.SpellAbility, int, int, java.util.Map)
|
* forge.game.spellability.SpellAbility, int, int, java.util.Map)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public int chooseNumber(Player player, SpellAbility sa, int min, int max, Map<String, Object> params) {
|
public int chooseNumber(Player player, SpellAbility sa, int min, int max, Map<String, Object> params) {
|
||||||
// TODO Auto-generated method stub
|
// TODO Auto-generated method stub
|
||||||
return super.chooseNumber(player, sa, min, max, params);
|
return super.chooseNumber(player, sa, min, max, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
*
|
*
|
||||||
* @see forge.ai.SpellAbilityAi#chooseCounterType(java.util.List,
|
* @see forge.ai.SpellAbilityAi#chooseCounterType(java.util.List,
|
||||||
* forge.game.spellability.SpellAbility, java.util.Map)
|
* forge.game.spellability.SpellAbility, java.util.Map)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public CounterType chooseCounterType(List<CounterType> options, SpellAbility sa, Map<String, Object> params) {
|
public CounterType chooseCounterType(List<CounterType> options, SpellAbility sa, Map<String, Object> params) {
|
||||||
if (options.size() <= 1) {
|
if (options.size() <= 1) {
|
||||||
return super.chooseCounterType(options, sa, params);
|
return super.chooseCounterType(options, sa, params);
|
||||||
}
|
}
|
||||||
Player ai = sa.getActivatingPlayer();
|
Player ai = sa.getActivatingPlayer();
|
||||||
Card target = (Card) params.get("Target");
|
Card target = (Card) params.get("Target");
|
||||||
|
|
||||||
if (target.getController().isOpponentOf(ai)) {
|
if (target.getController().isOpponentOf(ai)) {
|
||||||
// if its a Planeswalker try to remove Loyality first
|
// if its a Planeswalker try to remove Loyality first
|
||||||
if (target.isPlaneswalker()) {
|
if (target.isPlaneswalker()) {
|
||||||
return CounterType.LOYALTY;
|
return CounterType.LOYALTY;
|
||||||
}
|
}
|
||||||
for (CounterType type : options) {
|
for (CounterType type : options) {
|
||||||
if (!ComputerUtil.isNegativeCounter(type, target)) {
|
if (!ComputerUtil.isNegativeCounter(type, target)) {
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (options.contains(CounterType.M1M1) && target.hasKeyword("Persist")) {
|
if (options.contains(CounterType.M1M1) && target.hasKeyword("Persist")) {
|
||||||
return CounterType.M1M1;
|
return CounterType.M1M1;
|
||||||
} else if (options.contains(CounterType.P1P1) && target.hasKeyword("Undying")) {
|
} else if (options.contains(CounterType.P1P1) && target.hasKeyword("Undying")) {
|
||||||
return CounterType.M1M1;
|
return CounterType.M1M1;
|
||||||
}
|
}
|
||||||
for (CounterType type : options) {
|
for (CounterType type : options) {
|
||||||
if (ComputerUtil.isNegativeCounter(type, target)) {
|
if (ComputerUtil.isNegativeCounter(type, target)) {
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return super.chooseCounterType(options, sa, params);
|
return super.chooseCounterType(options, sa, params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,103 +1,103 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import forge.ai.ComputerUtil;
|
import forge.ai.ComputerUtil;
|
||||||
import forge.ai.ComputerUtilCombat;
|
import forge.ai.ComputerUtilCombat;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.card.CardCollectionView;
|
import forge.game.card.CardCollectionView;
|
||||||
import forge.game.card.CardPredicates;
|
import forge.game.card.CardPredicates;
|
||||||
import forge.game.phase.PhaseHandler;
|
import forge.game.phase.PhaseHandler;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
import forge.util.MyRandom;
|
import forge.util.MyRandom;
|
||||||
|
|
||||||
public abstract class DamageAiBase extends SpellAbilityAi {
|
public abstract class DamageAiBase extends SpellAbilityAi {
|
||||||
protected boolean shouldTgtP(final Player comp, final SpellAbility sa, final int d, final boolean noPrevention) {
|
protected boolean shouldTgtP(final Player comp, final SpellAbility sa, final int d, final boolean noPrevention) {
|
||||||
int restDamage = d;
|
int restDamage = d;
|
||||||
final Game game = comp.getGame();
|
final Game game = comp.getGame();
|
||||||
Player enemy = ComputerUtil.getOpponentFor(comp);
|
Player enemy = ComputerUtil.getOpponentFor(comp);
|
||||||
boolean dmgByCardsInHand = false;
|
boolean dmgByCardsInHand = false;
|
||||||
|
|
||||||
if ("X".equals(sa.getParam("NumDmg")) && sa.getHostCard() != null && sa.hasSVar(sa.getParam("NumDmg")) &&
|
if ("X".equals(sa.getParam("NumDmg")) && sa.getHostCard() != null && sa.hasSVar(sa.getParam("NumDmg")) &&
|
||||||
sa.getHostCard().getSVar(sa.getParam("NumDmg")).equals("TargetedPlayer$CardsInHand")) {
|
sa.getHostCard().getSVar(sa.getParam("NumDmg")).equals("TargetedPlayer$CardsInHand")) {
|
||||||
dmgByCardsInHand = true;
|
dmgByCardsInHand = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sa.canTarget(enemy)) {
|
if (!sa.canTarget(enemy)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (sa.getTargets() != null && sa.getTargets().getTargets().contains(enemy)) {
|
if (sa.getTargets() != null && sa.getTargets().getTargets().contains(enemy)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// burn Planeswalkers
|
// burn Planeswalkers
|
||||||
if (Iterables.any(enemy.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.PLANEWALKERS)) {
|
if (Iterables.any(enemy.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.PLANEWALKERS)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!noPrevention) {
|
if (!noPrevention) {
|
||||||
restDamage = ComputerUtilCombat.predictDamageTo(enemy, restDamage, sa.getHostCard(), false);
|
restDamage = ComputerUtilCombat.predictDamageTo(enemy, restDamage, sa.getHostCard(), false);
|
||||||
} else {
|
} else {
|
||||||
restDamage = enemy.staticReplaceDamage(restDamage, sa.getHostCard(), false);
|
restDamage = enemy.staticReplaceDamage(restDamage, sa.getHostCard(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (restDamage == 0) {
|
if (restDamage == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!enemy.canLoseLife()) {
|
if (!enemy.canLoseLife()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final CardCollectionView hand = comp.getCardsIn(ZoneType.Hand);
|
final CardCollectionView hand = comp.getCardsIn(ZoneType.Hand);
|
||||||
|
|
||||||
if ((enemy.getLife() - restDamage) < 5) {
|
if ((enemy.getLife() - restDamage) < 5) {
|
||||||
// drop the human to less than 5
|
// drop the human to less than 5
|
||||||
// life
|
// life
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.isSpell()) {
|
if (sa.isSpell()) {
|
||||||
PhaseHandler phase = game.getPhaseHandler();
|
PhaseHandler phase = game.getPhaseHandler();
|
||||||
// If this is a spell, cast it instead of discarding
|
// If this is a spell, cast it instead of discarding
|
||||||
if ((phase.is(PhaseType.END_OF_TURN) || phase.is(PhaseType.MAIN2))
|
if ((phase.is(PhaseType.END_OF_TURN) || phase.is(PhaseType.MAIN2))
|
||||||
&& phase.isPlayerTurn(comp) && (hand.size() > comp.getMaxHandSize())) {
|
&& phase.isPlayerTurn(comp) && (hand.size() > comp.getMaxHandSize())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// chance to burn player based on current hand size
|
// chance to burn player based on current hand size
|
||||||
if (hand.size() > 2) {
|
if (hand.size() > 2) {
|
||||||
float value = 0;
|
float value = 0;
|
||||||
if (SpellAbilityAi.isSorcerySpeed(sa)) {
|
if (SpellAbilityAi.isSorcerySpeed(sa)) {
|
||||||
//lower chance for sorcery as other spells may be cast in main2
|
//lower chance for sorcery as other spells may be cast in main2
|
||||||
if (phase.isPlayerTurn(comp) && phase.is(PhaseType.MAIN2)) {
|
if (phase.isPlayerTurn(comp) && phase.is(PhaseType.MAIN2)) {
|
||||||
value = 1.0f * restDamage / enemy.getLife();
|
value = 1.0f * restDamage / enemy.getLife();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (phase.isPlayerTurn(enemy)) {
|
if (phase.isPlayerTurn(enemy)) {
|
||||||
if (phase.is(PhaseType.END_OF_TURN)
|
if (phase.is(PhaseType.END_OF_TURN)
|
||||||
|| ((dmgByCardsInHand && phase.getPhase().isAfter(PhaseType.UPKEEP)))) {
|
|| ((dmgByCardsInHand && phase.getPhase().isAfter(PhaseType.UPKEEP)))) {
|
||||||
value = 1.5f * restDamage / enemy.getLife();
|
value = 1.5f * restDamage / enemy.getLife();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (value > 0) { //more likely to burn with larger hand
|
if (value > 0) { //more likely to burn with larger hand
|
||||||
for (int i = 3; i < hand.size(); i++) {
|
for (int i = 3; i < hand.size(); i++) {
|
||||||
value *= 1.1f;
|
value *= 1.1f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (value < 0.2f) { //hard floor to reduce ridiculous odds for instants over time
|
if (value < 0.2f) { //hard floor to reduce ridiculous odds for instants over time
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
final float chance = MyRandom.getRandom().nextFloat();
|
final float chance = MyRandom.getRandom().nextFloat();
|
||||||
return chance < value;
|
return chance < value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,332 +1,332 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import forge.ai.*;
|
import forge.ai.*;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardCollection;
|
import forge.game.card.CardCollection;
|
||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
import forge.game.card.CounterType;
|
import forge.game.card.CounterType;
|
||||||
import forge.game.cost.Cost;
|
import forge.game.cost.Cost;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.TargetRestrictions;
|
import forge.game.spellability.TargetRestrictions;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
import forge.util.MyRandom;
|
import forge.util.MyRandom;
|
||||||
|
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
public class DamageAllAi extends SpellAbilityAi {
|
public class DamageAllAi extends SpellAbilityAi {
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
||||||
// AI needs to be expanded, since this function can be pretty complex
|
// AI needs to be expanded, since this function can be pretty complex
|
||||||
// based on what the expected targets could be
|
// based on what the expected targets could be
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
|
|
||||||
// prevent run-away activations - first time will always return true
|
// prevent run-away activations - first time will always return true
|
||||||
final Random r = MyRandom.getRandom();
|
final Random r = MyRandom.getRandom();
|
||||||
if (r.nextFloat() > Math.pow(.9, sa.getActivationsThisTurn())) {
|
if (r.nextFloat() > Math.pow(.9, sa.getActivationsThisTurn())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// abCost stuff that should probably be centralized...
|
// abCost stuff that should probably be centralized...
|
||||||
final Cost abCost = sa.getPayCosts();
|
final Cost abCost = sa.getPayCosts();
|
||||||
if (abCost != null) {
|
if (abCost != null) {
|
||||||
// AI currently disabled for some costs
|
// AI currently disabled for some costs
|
||||||
if (!ComputerUtilCost.checkLifeCost(ai, abCost, source, 4, sa)) {
|
if (!ComputerUtilCost.checkLifeCost(ai, abCost, source, 4, sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// wait until stack is empty (prevents duplicate kills)
|
// wait until stack is empty (prevents duplicate kills)
|
||||||
if (!ai.getGame().getStack().isEmpty()) {
|
if (!ai.getGame().getStack().isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int x = -1;
|
int x = -1;
|
||||||
final String damage = sa.getParam("NumDmg");
|
final String damage = sa.getParam("NumDmg");
|
||||||
int dmg = AbilityUtils.calculateAmount(sa.getHostCard(), damage, sa);
|
int dmg = AbilityUtils.calculateAmount(sa.getHostCard(), damage, sa);
|
||||||
if (damage.equals("X") && sa.getSVar(damage).equals("Count$Converge")) {
|
if (damage.equals("X") && sa.getSVar(damage).equals("Count$Converge")) {
|
||||||
dmg = ComputerUtilMana.getConvergeCount(sa, ai);
|
dmg = ComputerUtilMana.getConvergeCount(sa, ai);
|
||||||
}
|
}
|
||||||
if (damage.equals("X") && sa.getSVar(damage).equals("Count$xPaid")) {
|
if (damage.equals("X") && sa.getSVar(damage).equals("Count$xPaid")) {
|
||||||
x = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
x = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
||||||
}
|
}
|
||||||
if (damage.equals("ChosenX")) {
|
if (damage.equals("ChosenX")) {
|
||||||
x = source.getCounters(CounterType.LOYALTY);
|
x = source.getCounters(CounterType.LOYALTY);
|
||||||
}
|
}
|
||||||
if (x == -1) {
|
if (x == -1) {
|
||||||
Player bestOpp = determineOppToKill(ai, sa, source, dmg);
|
Player bestOpp = determineOppToKill(ai, sa, source, dmg);
|
||||||
if (determineOppToKill(ai, sa, source, dmg) != null) {
|
if (determineOppToKill(ai, sa, source, dmg) != null) {
|
||||||
// we already know we can kill a player, so go for it
|
// we already know we can kill a player, so go for it
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// look for other value in this (damaging creatures or
|
// look for other value in this (damaging creatures or
|
||||||
// creatures + player, e.g. Pestilence, etc.)
|
// creatures + player, e.g. Pestilence, etc.)
|
||||||
return evaluateDamageAll(ai, sa, source, dmg) > 0;
|
return evaluateDamageAll(ai, sa, source, dmg) > 0;
|
||||||
} else {
|
} else {
|
||||||
int best = -1, best_x = -1;
|
int best = -1, best_x = -1;
|
||||||
Player bestOpp = determineOppToKill(ai, sa, source, x);
|
Player bestOpp = determineOppToKill(ai, sa, source, x);
|
||||||
if (bestOpp != null) {
|
if (bestOpp != null) {
|
||||||
// we can finish off a player, so go for it
|
// we can finish off a player, so go for it
|
||||||
|
|
||||||
// TODO: improve this by possibly damaging more creatures
|
// TODO: improve this by possibly damaging more creatures
|
||||||
// on the battlefield belonging to other opponents at the same
|
// on the battlefield belonging to other opponents at the same
|
||||||
// time, if viable
|
// time, if viable
|
||||||
best_x = bestOpp.getLife();
|
best_x = bestOpp.getLife();
|
||||||
} else {
|
} else {
|
||||||
// see if it's possible to get value from killing off creatures
|
// see if it's possible to get value from killing off creatures
|
||||||
for (int i = 0; i <= x; i++) {
|
for (int i = 0; i <= x; i++) {
|
||||||
final int value = evaluateDamageAll(ai, sa, source, i);
|
final int value = evaluateDamageAll(ai, sa, source, i);
|
||||||
if (value > best) {
|
if (value > best) {
|
||||||
best = value;
|
best = value;
|
||||||
best_x = i;
|
best_x = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (best_x > 0) {
|
if (best_x > 0) {
|
||||||
if (sa.getSVar(damage).equals("Count$xPaid")) {
|
if (sa.getSVar(damage).equals("Count$xPaid")) {
|
||||||
source.setSVar("PayX", Integer.toString(best_x));
|
source.setSVar("PayX", Integer.toString(best_x));
|
||||||
}
|
}
|
||||||
if (damage.equals("ChosenX")) {
|
if (damage.equals("ChosenX")) {
|
||||||
source.setSVar("ChosenX", "Number$" + best_x);
|
source.setSVar("ChosenX", "Number$" + best_x);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Player determineOppToKill(Player ai, SpellAbility sa, Card source, int x) {
|
private Player determineOppToKill(Player ai, SpellAbility sa, Card source, int x) {
|
||||||
// Attempt to determine which opponent can be finished off such that the most players
|
// Attempt to determine which opponent can be finished off such that the most players
|
||||||
// are killed at the same time, given X damage tops
|
// are killed at the same time, given X damage tops
|
||||||
final String validP = sa.hasParam("ValidPlayers") ? sa.getParam("ValidPlayers") : "";
|
final String validP = sa.hasParam("ValidPlayers") ? sa.getParam("ValidPlayers") : "";
|
||||||
int aiLife = ai.getLife();
|
int aiLife = ai.getLife();
|
||||||
Player bestOpp = null; // default opponent, if all else fails
|
Player bestOpp = null; // default opponent, if all else fails
|
||||||
|
|
||||||
for (int dmg = 1; dmg <= x; dmg++) {
|
for (int dmg = 1; dmg <= x; dmg++) {
|
||||||
// Don't kill yourself in the process
|
// Don't kill yourself in the process
|
||||||
if (validP.equals("Player") && aiLife <= ComputerUtilCombat.predictDamageTo(ai, dmg, source, false)) {
|
if (validP.equals("Player") && aiLife <= ComputerUtilCombat.predictDamageTo(ai, dmg, source, false)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
for (Player opp : ai.getOpponents()) {
|
for (Player opp : ai.getOpponents()) {
|
||||||
if ((validP.equals("Player") || validP.contains("Opponent"))
|
if ((validP.equals("Player") || validP.contains("Opponent"))
|
||||||
&& (opp.getLife() <= ComputerUtilCombat.predictDamageTo(opp, dmg, source, false))) {
|
&& (opp.getLife() <= ComputerUtilCombat.predictDamageTo(opp, dmg, source, false))) {
|
||||||
bestOpp = opp;
|
bestOpp = opp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return bestOpp;
|
return bestOpp;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int evaluateDamageAll(Player ai, SpellAbility sa, final Card source, int dmg) {
|
private int evaluateDamageAll(Player ai, SpellAbility sa, final Card source, int dmg) {
|
||||||
final Player opp = ai.getWeakestOpponent();
|
final Player opp = ai.getWeakestOpponent();
|
||||||
final CardCollection humanList = getKillableCreatures(sa, opp, dmg);
|
final CardCollection humanList = getKillableCreatures(sa, opp, dmg);
|
||||||
CardCollection computerList = getKillableCreatures(sa, ai, dmg);
|
CardCollection computerList = getKillableCreatures(sa, ai, dmg);
|
||||||
|
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
if (tgt != null && sa.canTarget(opp)) {
|
if (tgt != null && sa.canTarget(opp)) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
sa.getTargets().add(opp);
|
sa.getTargets().add(opp);
|
||||||
computerList.clear();
|
computerList.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
final String validP = sa.hasParam("ValidPlayers") ? sa.getParam("ValidPlayers") : "";
|
final String validP = sa.hasParam("ValidPlayers") ? sa.getParam("ValidPlayers") : "";
|
||||||
// TODO: if damage is dependant on mana paid, maybe have X be human's max life
|
// TODO: if damage is dependant on mana paid, maybe have X be human's max life
|
||||||
// Don't kill yourself
|
// Don't kill yourself
|
||||||
if (validP.equals("Player") && (ai.getLife() <= ComputerUtilCombat.predictDamageTo(ai, dmg, source, false))) {
|
if (validP.equals("Player") && (ai.getLife() <= ComputerUtilCombat.predictDamageTo(ai, dmg, source, false))) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int minGain = 200; // The minimum gain in destroyed creatures
|
int minGain = 200; // The minimum gain in destroyed creatures
|
||||||
if (sa.getPayCosts() != null && sa.getPayCosts().isReusuableResource()) {
|
if (sa.getPayCosts() != null && sa.getPayCosts().isReusuableResource()) {
|
||||||
if (computerList.isEmpty()) {
|
if (computerList.isEmpty()) {
|
||||||
minGain = 10; // nothing to lose
|
minGain = 10; // nothing to lose
|
||||||
// no creatures to lose and player can be damaged
|
// no creatures to lose and player can be damaged
|
||||||
// so do it if it's helping!
|
// so do it if it's helping!
|
||||||
// ----------------------------
|
// ----------------------------
|
||||||
// needs future improvement on pestilence :
|
// needs future improvement on pestilence :
|
||||||
// what if we lose creatures but can win by repeated activations?
|
// what if we lose creatures but can win by repeated activations?
|
||||||
// that tactic only works if there are creatures left to keep pestilence in play
|
// that tactic only works if there are creatures left to keep pestilence in play
|
||||||
// and can kill the player in a reasonable amount of time (no more than 2-3 turns?)
|
// and can kill the player in a reasonable amount of time (no more than 2-3 turns?)
|
||||||
if (validP.equals("Player")) {
|
if (validP.equals("Player")) {
|
||||||
if (ComputerUtilCombat.predictDamageTo(opp, dmg, source, false) > 0) {
|
if (ComputerUtilCombat.predictDamageTo(opp, dmg, source, false) > 0) {
|
||||||
// When using Pestilence to hurt players, do it at
|
// When using Pestilence to hurt players, do it at
|
||||||
// the end of the opponent's turn only
|
// the end of the opponent's turn only
|
||||||
if ((!"DmgAllCreaturesAndPlayers".equals(sa.getParam("AILogic")))
|
if ((!"DmgAllCreaturesAndPlayers".equals(sa.getParam("AILogic")))
|
||||||
|| ((ai.getGame().getPhaseHandler().is(PhaseType.END_OF_TURN)
|
|| ((ai.getGame().getPhaseHandler().is(PhaseType.END_OF_TURN)
|
||||||
&& (ai.getGame().getNonactivePlayers().contains(ai)))))
|
&& (ai.getGame().getNonactivePlayers().contains(ai)))))
|
||||||
// Need further improvement : if able to kill immediately with repeated activations, do not wait
|
// Need further improvement : if able to kill immediately with repeated activations, do not wait
|
||||||
// for phases! Will also need to implement considering repeated activations for killed creatures!
|
// for phases! Will also need to implement considering repeated activations for killed creatures!
|
||||||
// || (ai.sa.getPayCosts(). ??? )
|
// || (ai.sa.getPayCosts(). ??? )
|
||||||
{
|
{
|
||||||
// would take zero damage, and hurt opponent, do it!
|
// would take zero damage, and hurt opponent, do it!
|
||||||
if (ComputerUtilCombat.predictDamageTo(ai, dmg, source, false)<1) {
|
if (ComputerUtilCombat.predictDamageTo(ai, dmg, source, false)<1) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
// enemy is expected to die faster than AI from damage if repeated
|
// enemy is expected to die faster than AI from damage if repeated
|
||||||
if (ai.getLife() > ComputerUtilCombat.predictDamageTo(ai, dmg, source, false)
|
if (ai.getLife() > ComputerUtilCombat.predictDamageTo(ai, dmg, source, false)
|
||||||
* ((opp.getLife() + ComputerUtilCombat.predictDamageTo(opp, dmg, source, false) - 1)
|
* ((opp.getLife() + ComputerUtilCombat.predictDamageTo(opp, dmg, source, false) - 1)
|
||||||
/ ComputerUtilCombat.predictDamageTo(opp, dmg, source, false))) {
|
/ ComputerUtilCombat.predictDamageTo(opp, dmg, source, false))) {
|
||||||
// enemy below 10 life, go for it!
|
// enemy below 10 life, go for it!
|
||||||
if ((opp.getLife() < 10)
|
if ((opp.getLife() < 10)
|
||||||
&& (ComputerUtilCombat.predictDamageTo(opp, dmg, source, false) >= 1)) {
|
&& (ComputerUtilCombat.predictDamageTo(opp, dmg, source, false) >= 1)) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
// At least half enemy remaining life can be removed in one go
|
// At least half enemy remaining life can be removed in one go
|
||||||
// worth doing even if enemy still has high health - one more copy of spell to win!
|
// worth doing even if enemy still has high health - one more copy of spell to win!
|
||||||
if (opp.getLife() <= 2 * ComputerUtilCombat.predictDamageTo(opp, dmg, source, false)) {
|
if (opp.getLife() <= 2 * ComputerUtilCombat.predictDamageTo(opp, dmg, source, false)) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
minGain = 100; // safety for errors in evaluate creature
|
minGain = 100; // safety for errors in evaluate creature
|
||||||
}
|
}
|
||||||
} else if (sa.getSubAbility() != null && ai.getGame().getPhaseHandler().isPreCombatMain() && computerList.isEmpty()
|
} else if (sa.getSubAbility() != null && ai.getGame().getPhaseHandler().isPreCombatMain() && computerList.isEmpty()
|
||||||
&& opp.getCreaturesInPlay().size() > 1 && !ai.getCreaturesInPlay().isEmpty()) {
|
&& opp.getCreaturesInPlay().size() > 1 && !ai.getCreaturesInPlay().isEmpty()) {
|
||||||
minGain = 126; // prepare for attack
|
minGain = 126; // prepare for attack
|
||||||
}
|
}
|
||||||
|
|
||||||
return ComputerUtilCard.evaluateCreatureList(humanList) - ComputerUtilCard.evaluateCreatureList(computerList)
|
return ComputerUtilCard.evaluateCreatureList(humanList) - ComputerUtilCard.evaluateCreatureList(computerList)
|
||||||
- minGain;
|
- minGain;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
String validP = "";
|
String validP = "";
|
||||||
|
|
||||||
final String damage = sa.getParam("NumDmg");
|
final String damage = sa.getParam("NumDmg");
|
||||||
int dmg = AbilityUtils.calculateAmount(sa.getHostCard(), damage, sa);
|
int dmg = AbilityUtils.calculateAmount(sa.getHostCard(), damage, sa);
|
||||||
|
|
||||||
if (damage.equals("X") && sa.getSVar(damage).equals("Count$xPaid")) {
|
if (damage.equals("X") && sa.getSVar(damage).equals("Count$xPaid")) {
|
||||||
// Set PayX here to maximum value.
|
// Set PayX here to maximum value.
|
||||||
dmg = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
dmg = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
||||||
source.setSVar("PayX", Integer.toString(dmg));
|
source.setSVar("PayX", Integer.toString(dmg));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.hasParam("ValidPlayers")) {
|
if (sa.hasParam("ValidPlayers")) {
|
||||||
validP = sa.getParam("ValidPlayers");
|
validP = sa.getParam("ValidPlayers");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate creatures getting killed
|
// Evaluate creatures getting killed
|
||||||
Player enemy = ComputerUtil.getOpponentFor(ai);
|
Player enemy = ComputerUtil.getOpponentFor(ai);
|
||||||
final CardCollection humanList = getKillableCreatures(sa, enemy, dmg);
|
final CardCollection humanList = getKillableCreatures(sa, enemy, dmg);
|
||||||
CardCollection computerList = getKillableCreatures(sa, ai, dmg);
|
CardCollection computerList = getKillableCreatures(sa, ai, dmg);
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
|
|
||||||
if (tgt != null && sa.canTarget(enemy)) {
|
if (tgt != null && sa.canTarget(enemy)) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
sa.getTargets().add(enemy);
|
sa.getTargets().add(enemy);
|
||||||
computerList.clear();
|
computerList.clear();
|
||||||
}
|
}
|
||||||
// Don't get yourself killed
|
// Don't get yourself killed
|
||||||
if (validP.equals("Player") && (ai.getLife() <= ComputerUtilCombat.predictDamageTo(ai, dmg, source, false))) {
|
if (validP.equals("Player") && (ai.getLife() <= ComputerUtilCombat.predictDamageTo(ai, dmg, source, false))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we can kill human, do it
|
// if we can kill human, do it
|
||||||
if ((validP.equals("Player") || validP.equals("Opponent") || validP.contains("Targeted"))
|
if ((validP.equals("Player") || validP.equals("Opponent") || validP.contains("Targeted"))
|
||||||
&& (enemy.getLife() <= ComputerUtilCombat.predictDamageTo(enemy, dmg, source, false))) {
|
&& (enemy.getLife() <= ComputerUtilCombat.predictDamageTo(enemy, dmg, source, false))) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!computerList.isEmpty() && ComputerUtilCard.evaluateCreatureList(computerList) > ComputerUtilCard
|
if (!computerList.isEmpty() && ComputerUtilCard.evaluateCreatureList(computerList) > ComputerUtilCard
|
||||||
.evaluateCreatureList(humanList)) {
|
.evaluateCreatureList(humanList)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* getKillableCreatures.
|
* getKillableCreatures.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param sa
|
* @param sa
|
||||||
* a {@link forge.game.spellability.SpellAbility} object.
|
* a {@link forge.game.spellability.SpellAbility} object.
|
||||||
* @param player
|
* @param player
|
||||||
* a {@link forge.game.player.Player} object.
|
* a {@link forge.game.player.Player} object.
|
||||||
* @param dmg
|
* @param dmg
|
||||||
* a int.
|
* a int.
|
||||||
* @return a {@link forge.game.card.CardCollection} object.
|
* @return a {@link forge.game.card.CardCollection} object.
|
||||||
*/
|
*/
|
||||||
private CardCollection getKillableCreatures(final SpellAbility sa, final Player player, final int dmg) {
|
private CardCollection getKillableCreatures(final SpellAbility sa, final Player player, final int dmg) {
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
String validC = sa.hasParam("ValidCards") ? sa.getParam("ValidCards") : "";
|
String validC = sa.hasParam("ValidCards") ? sa.getParam("ValidCards") : "";
|
||||||
|
|
||||||
// TODO: X may be something different than X paid
|
// TODO: X may be something different than X paid
|
||||||
CardCollection list =
|
CardCollection list =
|
||||||
CardLists.getValidCards(player.getCardsIn(ZoneType.Battlefield), validC.split(","), source.getController(), source, sa);
|
CardLists.getValidCards(player.getCardsIn(ZoneType.Battlefield), validC.split(","), source.getController(), source, sa);
|
||||||
|
|
||||||
final Predicate<Card> filterKillable = new Predicate<Card>() {
|
final Predicate<Card> filterKillable = new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(final Card c) {
|
public boolean apply(final Card c) {
|
||||||
return (ComputerUtilCombat.predictDamageTo(c, dmg, source, false) >= ComputerUtilCombat.getDamageToKill(c));
|
return (ComputerUtilCombat.predictDamageTo(c, dmg, source, false) >= ComputerUtilCombat.getDamageToKill(c));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
list = CardLists.getNotKeyword(list, "Indestructible");
|
list = CardLists.getNotKeyword(list, "Indestructible");
|
||||||
list = CardLists.filter(list, filterKillable);
|
list = CardLists.filter(list, filterKillable);
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
String validP = "";
|
String validP = "";
|
||||||
|
|
||||||
final String damage = sa.getParam("NumDmg");
|
final String damage = sa.getParam("NumDmg");
|
||||||
int dmg = AbilityUtils.calculateAmount(sa.getHostCard(), damage, sa);
|
int dmg = AbilityUtils.calculateAmount(sa.getHostCard(), damage, sa);
|
||||||
|
|
||||||
if (damage.equals("X") && sa.getSVar(damage).equals("Count$xPaid")) {
|
if (damage.equals("X") && sa.getSVar(damage).equals("Count$xPaid")) {
|
||||||
// Set PayX here to maximum value.
|
// Set PayX here to maximum value.
|
||||||
dmg = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
dmg = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
||||||
source.setSVar("PayX", Integer.toString(dmg));
|
source.setSVar("PayX", Integer.toString(dmg));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.hasParam("ValidPlayers")) {
|
if (sa.hasParam("ValidPlayers")) {
|
||||||
validP = sa.getParam("ValidPlayers");
|
validP = sa.getParam("ValidPlayers");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate creatures getting killed
|
// Evaluate creatures getting killed
|
||||||
Player enemy = ComputerUtil.getOpponentFor(ai);
|
Player enemy = ComputerUtil.getOpponentFor(ai);
|
||||||
final CardCollection humanList = getKillableCreatures(sa, enemy, dmg);
|
final CardCollection humanList = getKillableCreatures(sa, enemy, dmg);
|
||||||
CardCollection computerList = getKillableCreatures(sa, ai, dmg);
|
CardCollection computerList = getKillableCreatures(sa, ai, dmg);
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
|
|
||||||
if (tgt != null && sa.canTarget(enemy)) {
|
if (tgt != null && sa.canTarget(enemy)) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
sa.getTargets().add(enemy);
|
sa.getTargets().add(enemy);
|
||||||
computerList.clear();
|
computerList.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If it's not mandatory check a few things
|
// If it's not mandatory check a few things
|
||||||
if (mandatory) {
|
if (mandatory) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// Don't get yourself killed
|
// Don't get yourself killed
|
||||||
if (validP.equals("Player") && (ai.getLife() <= ComputerUtilCombat.predictDamageTo(ai, dmg, source, false))) {
|
if (validP.equals("Player") && (ai.getLife() <= ComputerUtilCombat.predictDamageTo(ai, dmg, source, false))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we can kill human, do it
|
// if we can kill human, do it
|
||||||
if ((validP.equals("Player") || validP.contains("Opponent") || validP.contains("Targeted"))
|
if ((validP.equals("Player") || validP.contains("Opponent") || validP.contains("Targeted"))
|
||||||
&& (enemy.getLife() <= ComputerUtilCombat.predictDamageTo(enemy, dmg, source, false))) {
|
&& (enemy.getLife() <= ComputerUtilCombat.predictDamageTo(enemy, dmg, source, false))) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!computerList.isEmpty() && ComputerUtilCard.evaluateCreatureList(computerList) + 50 >= ComputerUtilCard
|
if (!computerList.isEmpty() && ComputerUtilCard.evaluateCreatureList(computerList) + 50 >= ComputerUtilCard
|
||||||
.evaluateCreatureList(humanList)) {
|
.evaluateCreatureList(humanList)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,54 +1,54 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
|
|
||||||
import forge.ai.SpecialCardAi;
|
import forge.ai.SpecialCardAi;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.player.PlayerCollection;
|
import forge.game.player.PlayerCollection;
|
||||||
import forge.game.player.PlayerPredicates;
|
import forge.game.player.PlayerPredicates;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.TargetRestrictions;
|
import forge.game.spellability.TargetRestrictions;
|
||||||
|
|
||||||
public class DamageEachAi extends DamageAiBase {
|
public class DamageEachAi extends DamageAiBase {
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see forge.card.abilityfactory.SpellAiLogic#canPlayAI(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility)
|
* @see forge.card.abilityfactory.SpellAiLogic#canPlayAI(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
final String logic = sa.getParam("AILogic");
|
final String logic = sa.getParam("AILogic");
|
||||||
|
|
||||||
PlayerCollection targetableOpps = ai.getOpponents().filter(PlayerPredicates.isTargetableBy(sa));
|
PlayerCollection targetableOpps = ai.getOpponents().filter(PlayerPredicates.isTargetableBy(sa));
|
||||||
Player weakestOpp = targetableOpps.min(PlayerPredicates.compareByLife());
|
Player weakestOpp = targetableOpps.min(PlayerPredicates.compareByLife());
|
||||||
|
|
||||||
if (tgt != null && weakestOpp != null) {
|
if (tgt != null && weakestOpp != null) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
sa.getTargets().add(weakestOpp);
|
sa.getTargets().add(weakestOpp);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("MadSarkhanUltimate".equals(logic)) {
|
if ("MadSarkhanUltimate".equals(logic)) {
|
||||||
return SpecialCardAi.SarkhanTheMad.considerUltimate(ai, sa, weakestOpp);
|
return SpecialCardAi.SarkhanTheMad.considerUltimate(ai, sa, weakestOpp);
|
||||||
}
|
}
|
||||||
|
|
||||||
final String damage = sa.getParam("NumDmg");
|
final String damage = sa.getParam("NumDmg");
|
||||||
final int iDmg = AbilityUtils.calculateAmount(sa.getHostCard(), damage, sa);
|
final int iDmg = AbilityUtils.calculateAmount(sa.getHostCard(), damage, sa);
|
||||||
return this.shouldTgtP(ai, sa, iDmg, false);
|
return this.shouldTgtP(ai, sa, iDmg, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean chkAIDrawback(SpellAbility sa, Player aiPlayer) {
|
public boolean chkAIDrawback(SpellAbility sa, Player aiPlayer) {
|
||||||
// check AI life before playing this drawback?
|
// check AI life before playing this drawback?
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see forge.card.abilityfactory.SpellAiLogic#doTriggerAINoCost(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility, boolean)
|
* @see forge.card.abilityfactory.SpellAiLogic#doTriggerAINoCost(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility, boolean)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
||||||
|
|
||||||
return canPlayAI(ai, sa);
|
return canPlayAI(ai, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,224 +1,224 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import forge.ai.*;
|
import forge.ai.*;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.GameObject;
|
import forge.game.GameObject;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardCollection;
|
import forge.game.card.CardCollection;
|
||||||
import forge.game.card.CardCollectionView;
|
import forge.game.card.CardCollectionView;
|
||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
import forge.game.card.CardPredicates;
|
import forge.game.card.CardPredicates;
|
||||||
import forge.game.combat.Combat;
|
import forge.game.combat.Combat;
|
||||||
import forge.game.cost.Cost;
|
import forge.game.cost.Cost;
|
||||||
import forge.game.phase.PhaseHandler;
|
import forge.game.phase.PhaseHandler;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.TargetChoices;
|
import forge.game.spellability.TargetChoices;
|
||||||
import forge.game.spellability.TargetRestrictions;
|
import forge.game.spellability.TargetRestrictions;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class DamagePreventAi extends SpellAbilityAi {
|
public class DamagePreventAi extends SpellAbilityAi {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
||||||
final Card hostCard = sa.getHostCard();
|
final Card hostCard = sa.getHostCard();
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
final Combat combat = game.getCombat();
|
final Combat combat = game.getCombat();
|
||||||
boolean chance = false;
|
boolean chance = false;
|
||||||
|
|
||||||
final Cost cost = sa.getPayCosts();
|
final Cost cost = sa.getPayCosts();
|
||||||
|
|
||||||
// temporarily disabled until better AI
|
// temporarily disabled until better AI
|
||||||
if (!ComputerUtilCost.checkLifeCost(ai, cost, hostCard, 4, sa)) {
|
if (!ComputerUtilCost.checkLifeCost(ai, cost, hostCard, 4, sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ComputerUtilCost.checkDiscardCost(ai, cost, hostCard)) {
|
if (!ComputerUtilCost.checkDiscardCost(ai, cost, hostCard)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ComputerUtilCost.checkSacrificeCost(ai, cost, hostCard, sa)) {
|
if (!ComputerUtilCost.checkSacrificeCost(ai, cost, hostCard, sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ComputerUtilCost.checkRemoveCounterCost(cost, hostCard)) {
|
if (!ComputerUtilCost.checkRemoveCounterCost(cost, hostCard)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
if (tgt == null) {
|
if (tgt == null) {
|
||||||
// As far as I can tell these Defined Cards will only have one of
|
// As far as I can tell these Defined Cards will only have one of
|
||||||
// them
|
// them
|
||||||
final List<GameObject> objects = AbilityUtils.getDefinedObjects(sa.getHostCard(), sa.getParam("Defined"), sa);
|
final List<GameObject> objects = AbilityUtils.getDefinedObjects(sa.getHostCard(), sa.getParam("Defined"), sa);
|
||||||
|
|
||||||
// react to threats on the stack
|
// react to threats on the stack
|
||||||
if (!game.getStack().isEmpty()) {
|
if (!game.getStack().isEmpty()) {
|
||||||
final List<GameObject> threatenedObjects = ComputerUtil.predictThreatenedObjects(sa.getActivatingPlayer(), sa);
|
final List<GameObject> threatenedObjects = ComputerUtil.predictThreatenedObjects(sa.getActivatingPlayer(), sa);
|
||||||
for (final Object o : objects) {
|
for (final Object o : objects) {
|
||||||
if (threatenedObjects.contains(o)) {
|
if (threatenedObjects.contains(o)) {
|
||||||
chance = true;
|
chance = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
PhaseHandler handler = game.getPhaseHandler();
|
PhaseHandler handler = game.getPhaseHandler();
|
||||||
if (handler.is(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
|
if (handler.is(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
|
||||||
boolean flag = false;
|
boolean flag = false;
|
||||||
for (final Object o : objects) {
|
for (final Object o : objects) {
|
||||||
if (o instanceof Card) {
|
if (o instanceof Card) {
|
||||||
flag |= ComputerUtilCombat.combatantWouldBeDestroyed(ai, (Card) o, combat);
|
flag |= ComputerUtilCombat.combatantWouldBeDestroyed(ai, (Card) o, combat);
|
||||||
} else if (o instanceof Player) {
|
} else if (o instanceof Player) {
|
||||||
// Don't need to worry about Combat Damage during AI's turn
|
// Don't need to worry about Combat Damage during AI's turn
|
||||||
final Player p = (Player) o;
|
final Player p = (Player) o;
|
||||||
if (!handler.isPlayerTurn(p)) {
|
if (!handler.isPlayerTurn(p)) {
|
||||||
flag |= (p == ai && ((ComputerUtilCombat.wouldLoseLife(ai, combat) && sa
|
flag |= (p == ai && ((ComputerUtilCombat.wouldLoseLife(ai, combat) && sa
|
||||||
.isAbility()) || ComputerUtilCombat.lifeInDanger(ai, combat)));
|
.isAbility()) || ComputerUtilCombat.lifeInDanger(ai, combat)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
chance = flag;
|
chance = flag;
|
||||||
} else { // if nothing on the stack, and it's not declare
|
} else { // if nothing on the stack, and it's not declare
|
||||||
// blockers. no need to prevent
|
// blockers. no need to prevent
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // non-targeted
|
} // non-targeted
|
||||||
|
|
||||||
// react to threats on the stack
|
// react to threats on the stack
|
||||||
else if (!game.getStack().isEmpty()) {
|
else if (!game.getStack().isEmpty()) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
final TargetChoices tcs = sa.getTargets();
|
final TargetChoices tcs = sa.getTargets();
|
||||||
// check stack for something on the stack will kill anything i control
|
// check stack for something on the stack will kill anything i control
|
||||||
final List<GameObject> objects = ComputerUtil.predictThreatenedObjects(sa.getActivatingPlayer(), sa);
|
final List<GameObject> objects = ComputerUtil.predictThreatenedObjects(sa.getActivatingPlayer(), sa);
|
||||||
|
|
||||||
if (objects.contains(ai)) {
|
if (objects.contains(ai)) {
|
||||||
tcs.add(ai);
|
tcs.add(ai);
|
||||||
chance = true;
|
chance = true;
|
||||||
}
|
}
|
||||||
final List<Card> threatenedTargets = new ArrayList<Card>();
|
final List<Card> threatenedTargets = new ArrayList<Card>();
|
||||||
// filter AIs battlefield by what I can target
|
// filter AIs battlefield by what I can target
|
||||||
List<Card> targetables = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), tgt.getValidTgts(), ai, hostCard, sa);
|
List<Card> targetables = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), tgt.getValidTgts(), ai, hostCard, sa);
|
||||||
targetables = CardLists.getTargetableCards(targetables, sa);
|
targetables = CardLists.getTargetableCards(targetables, sa);
|
||||||
|
|
||||||
for (final Card c : targetables) {
|
for (final Card c : targetables) {
|
||||||
if (objects.contains(c)) {
|
if (objects.contains(c)) {
|
||||||
threatenedTargets.add(c);
|
threatenedTargets.add(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!threatenedTargets.isEmpty()) {
|
if (!threatenedTargets.isEmpty()) {
|
||||||
// Choose "best" of the remaining to save
|
// Choose "best" of the remaining to save
|
||||||
tcs.add(ComputerUtilCard.getBestCreatureAI(threatenedTargets));
|
tcs.add(ComputerUtilCard.getBestCreatureAI(threatenedTargets));
|
||||||
chance = true;
|
chance = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // Protect combatants
|
} // Protect combatants
|
||||||
else if (game.getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
|
else if (game.getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
final TargetChoices tcs = sa.getTargets();
|
final TargetChoices tcs = sa.getTargets();
|
||||||
if (sa.canTarget(ai) && ComputerUtilCombat.wouldLoseLife(ai, combat)
|
if (sa.canTarget(ai) && ComputerUtilCombat.wouldLoseLife(ai, combat)
|
||||||
&& (ComputerUtilCombat.lifeInDanger(ai, combat) || sa.isAbility() || sa.isTrigger())
|
&& (ComputerUtilCombat.lifeInDanger(ai, combat) || sa.isAbility() || sa.isTrigger())
|
||||||
&& game.getPhaseHandler().getPlayerTurn().isOpponentOf(ai)) {
|
&& game.getPhaseHandler().getPlayerTurn().isOpponentOf(ai)) {
|
||||||
tcs.add(ai);
|
tcs.add(ai);
|
||||||
chance = true;
|
chance = true;
|
||||||
} else {
|
} else {
|
||||||
// filter AIs battlefield by what I can target
|
// filter AIs battlefield by what I can target
|
||||||
CardCollectionView targetables = ai.getCardsIn(ZoneType.Battlefield);
|
CardCollectionView targetables = ai.getCardsIn(ZoneType.Battlefield);
|
||||||
targetables = CardLists.getValidCards(targetables, tgt.getValidTgts(), ai, hostCard, sa);
|
targetables = CardLists.getValidCards(targetables, tgt.getValidTgts(), ai, hostCard, sa);
|
||||||
targetables = CardLists.getTargetableCards(targetables, sa);
|
targetables = CardLists.getTargetableCards(targetables, sa);
|
||||||
|
|
||||||
if (targetables.isEmpty()) {
|
if (targetables.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
final CardCollection combatants = CardLists.filter(targetables, CardPredicates.Presets.CREATURES);
|
final CardCollection combatants = CardLists.filter(targetables, CardPredicates.Presets.CREATURES);
|
||||||
ComputerUtilCard.sortByEvaluateCreature(combatants);
|
ComputerUtilCard.sortByEvaluateCreature(combatants);
|
||||||
|
|
||||||
for (final Card c : combatants) {
|
for (final Card c : combatants) {
|
||||||
if (ComputerUtilCombat.combatantWouldBeDestroyed(ai, c, combat) && tcs.getNumTargeted() < tgt.getMaxTargets(hostCard, sa)) {
|
if (ComputerUtilCombat.combatantWouldBeDestroyed(ai, c, combat) && tcs.getNumTargeted() < tgt.getMaxTargets(hostCard, sa)) {
|
||||||
tcs.add(c);
|
tcs.add(c);
|
||||||
chance = true;
|
chance = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (tgt != null && sa.hasParam("DividedAsYouChoose") && sa.getTargets() != null && !sa.getTargets().getTargets().isEmpty()) {
|
if (tgt != null && sa.hasParam("DividedAsYouChoose") && sa.getTargets() != null && !sa.getTargets().getTargets().isEmpty()) {
|
||||||
tgt.addDividedAllocation(sa.getTargets().getTargets().get(0), AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("Amount"), sa));
|
tgt.addDividedAllocation(sa.getTargets().getTargets().get(0), AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("Amount"), sa));
|
||||||
}
|
}
|
||||||
|
|
||||||
return chance;
|
return chance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
||||||
boolean chance = false;
|
boolean chance = false;
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
if (tgt == null) {
|
if (tgt == null) {
|
||||||
// If there's no target on the trigger, just say yes.
|
// If there's no target on the trigger, just say yes.
|
||||||
chance = true;
|
chance = true;
|
||||||
} else {
|
} else {
|
||||||
chance = preventDamageMandatoryTarget(ai, sa, mandatory);
|
chance = preventDamageMandatoryTarget(ai, sa, mandatory);
|
||||||
}
|
}
|
||||||
|
|
||||||
return chance;
|
return chance;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* preventDamageMandatoryTarget.
|
* preventDamageMandatoryTarget.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param sa
|
* @param sa
|
||||||
* a {@link forge.game.spellability.SpellAbility} object.
|
* a {@link forge.game.spellability.SpellAbility} object.
|
||||||
* @param mandatory
|
* @param mandatory
|
||||||
* a boolean.
|
* a boolean.
|
||||||
* @return a boolean.
|
* @return a boolean.
|
||||||
*/
|
*/
|
||||||
private boolean preventDamageMandatoryTarget(final Player ai, final SpellAbility sa, final boolean mandatory) {
|
private boolean preventDamageMandatoryTarget(final Player ai, final SpellAbility sa, final boolean mandatory) {
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
// filter AIs battlefield by what I can target
|
// filter AIs battlefield by what I can target
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
CardCollectionView targetables = game.getCardsIn(ZoneType.Battlefield);
|
CardCollectionView targetables = game.getCardsIn(ZoneType.Battlefield);
|
||||||
targetables = CardLists.getValidCards(targetables, tgt.getValidTgts(), ai, sa.getHostCard(), sa);
|
targetables = CardLists.getValidCards(targetables, tgt.getValidTgts(), ai, sa.getHostCard(), sa);
|
||||||
final List<Card> compTargetables = CardLists.filterControlledBy(targetables, ai);
|
final List<Card> compTargetables = CardLists.filterControlledBy(targetables, ai);
|
||||||
Card target = null;
|
Card target = null;
|
||||||
|
|
||||||
if (targetables.isEmpty()) {
|
if (targetables.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mandatory && compTargetables.isEmpty()) {
|
if (!mandatory && compTargetables.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!compTargetables.isEmpty()) {
|
if (!compTargetables.isEmpty()) {
|
||||||
final CardCollection combatants = CardLists.filter(compTargetables, CardPredicates.Presets.CREATURES);
|
final CardCollection combatants = CardLists.filter(compTargetables, CardPredicates.Presets.CREATURES);
|
||||||
ComputerUtilCard.sortByEvaluateCreature(combatants);
|
ComputerUtilCard.sortByEvaluateCreature(combatants);
|
||||||
if (game.getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
|
if (game.getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
|
||||||
Combat combat = game.getCombat();
|
Combat combat = game.getCombat();
|
||||||
for (final Card c : combatants) {
|
for (final Card c : combatants) {
|
||||||
if (ComputerUtilCombat.combatantWouldBeDestroyed(ai, c, combat)) {
|
if (ComputerUtilCombat.combatantWouldBeDestroyed(ai, c, combat)) {
|
||||||
target = c;
|
target = c;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (target == null) {
|
if (target == null) {
|
||||||
target = combatants.get(0);
|
target = combatants.get(0);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
target = ComputerUtilCard.getCheapestPermanentAI(targetables, sa, true);
|
target = ComputerUtilCard.getCheapestPermanentAI(targetables, sa, true);
|
||||||
}
|
}
|
||||||
sa.getTargets().add(target);
|
sa.getTargets().add(target);
|
||||||
if (sa.hasParam("DividedAsYouChoose")) {
|
if (sa.hasParam("DividedAsYouChoose")) {
|
||||||
tgt.addDividedAllocation(target, AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("Amount"), sa));
|
tgt.addDividedAllocation(target, AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("Amount"), sa));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,59 +1,59 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
|
|
||||||
import forge.ai.ComputerUtilCost;
|
import forge.ai.ComputerUtilCost;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.cost.Cost;
|
import forge.game.cost.Cost;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
|
|
||||||
public class DamagePreventAllAi extends SpellAbilityAi {
|
public class DamagePreventAllAi extends SpellAbilityAi {
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see forge.card.abilityfactory.SpellAiLogic#canPlayAI(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility)
|
* @see forge.card.abilityfactory.SpellAiLogic#canPlayAI(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
||||||
final Card hostCard = sa.getHostCard();
|
final Card hostCard = sa.getHostCard();
|
||||||
boolean chance = false;
|
boolean chance = false;
|
||||||
|
|
||||||
final Cost cost = sa.getPayCosts();
|
final Cost cost = sa.getPayCosts();
|
||||||
|
|
||||||
// temporarily disabled until better AI
|
// temporarily disabled until better AI
|
||||||
if (!ComputerUtilCost.checkLifeCost(ai, cost, hostCard, 4, sa)) {
|
if (!ComputerUtilCost.checkLifeCost(ai, cost, hostCard, 4, sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ComputerUtilCost.checkDiscardCost(ai, cost, hostCard)) {
|
if (!ComputerUtilCost.checkDiscardCost(ai, cost, hostCard)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ComputerUtilCost.checkSacrificeCost(ai, cost, hostCard, sa)) {
|
if (!ComputerUtilCost.checkSacrificeCost(ai, cost, hostCard, sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ComputerUtilCost.checkRemoveCounterCost(cost, hostCard)) {
|
if (!ComputerUtilCost.checkRemoveCounterCost(cost, hostCard)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ai.getGame().getStack().isEmpty()) {
|
if (!ai.getGame().getStack().isEmpty()) {
|
||||||
// TODO check stack for something on the stack will kill anything i
|
// TODO check stack for something on the stack will kill anything i
|
||||||
// control
|
// control
|
||||||
|
|
||||||
} // Protect combatants
|
} // Protect combatants
|
||||||
else if (ai.getGame().getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
|
else if (ai.getGame().getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
return chance;
|
return chance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
|
||||||
boolean chance = true;
|
boolean chance = true;
|
||||||
|
|
||||||
return chance;
|
return chance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,283 +1,283 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
import forge.ai.ComputerUtil;
|
import forge.ai.ComputerUtil;
|
||||||
import forge.ai.ComputerUtilCard;
|
import forge.ai.ComputerUtilCard;
|
||||||
import forge.ai.ComputerUtilCost;
|
import forge.ai.ComputerUtilCost;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardCollection;
|
import forge.game.card.CardCollection;
|
||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
import forge.game.combat.Combat;
|
import forge.game.combat.Combat;
|
||||||
import forge.game.cost.Cost;
|
import forge.game.cost.Cost;
|
||||||
import forge.game.phase.PhaseHandler;
|
import forge.game.phase.PhaseHandler;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.TargetRestrictions;
|
import forge.game.spellability.TargetRestrictions;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class DebuffAi extends SpellAbilityAi {
|
public class DebuffAi extends SpellAbilityAi {
|
||||||
// *************************************************************************
|
// *************************************************************************
|
||||||
// ***************************** Debuff ************************************
|
// ***************************** Debuff ************************************
|
||||||
// *************************************************************************
|
// *************************************************************************
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(final Player ai, final SpellAbility sa) {
|
protected boolean canPlayAI(final Player ai, final SpellAbility sa) {
|
||||||
// if there is no target and host card isn't in play, don't activate
|
// if there is no target and host card isn't in play, don't activate
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
if ((sa.getTargetRestrictions() == null) && !source.isInPlay()) {
|
if ((sa.getTargetRestrictions() == null) && !source.isInPlay()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Cost cost = sa.getPayCosts();
|
final Cost cost = sa.getPayCosts();
|
||||||
|
|
||||||
// temporarily disabled until AI is improved
|
// temporarily disabled until AI is improved
|
||||||
if (!ComputerUtilCost.checkCreatureSacrificeCost(ai, cost, source, sa)) {
|
if (!ComputerUtilCost.checkCreatureSacrificeCost(ai, cost, source, sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ComputerUtilCost.checkLifeCost(ai, cost, source, 40, sa)) {
|
if (!ComputerUtilCost.checkLifeCost(ai, cost, source, 40, sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ComputerUtilCost.checkRemoveCounterCost(cost, source)) {
|
if (!ComputerUtilCost.checkRemoveCounterCost(cost, source)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final PhaseHandler ph = game.getPhaseHandler();
|
final PhaseHandler ph = game.getPhaseHandler();
|
||||||
|
|
||||||
// Phase Restrictions
|
// Phase Restrictions
|
||||||
if (ph.getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
if (ph.getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
||||||
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS)
|
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS)
|
||||||
|| !game.getStack().isEmpty()) {
|
|| !game.getStack().isEmpty()) {
|
||||||
// Instant-speed pumps should not be cast outside of combat when the
|
// Instant-speed pumps should not be cast outside of combat when the
|
||||||
// stack is empty
|
// stack is empty
|
||||||
if (!SpellAbilityAi.isSorcerySpeed(sa)) {
|
if (!SpellAbilityAi.isSorcerySpeed(sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sa.usesTargeting() || !sa.getTargetRestrictions().doesTarget()) {
|
if (!sa.usesTargeting() || !sa.getTargetRestrictions().doesTarget()) {
|
||||||
List<Card> cards = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa);
|
List<Card> cards = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa);
|
||||||
|
|
||||||
|
|
||||||
final Combat combat = game.getCombat();
|
final Combat combat = game.getCombat();
|
||||||
return Iterables.any(cards, new Predicate<Card>() {
|
return Iterables.any(cards, new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(final Card c) {
|
public boolean apply(final Card c) {
|
||||||
|
|
||||||
if (c.getController().equals(sa.getActivatingPlayer()) || combat == null)
|
if (c.getController().equals(sa.getActivatingPlayer()) || combat == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!combat.isBlocking(c) && !combat.isAttacking(c)) {
|
if (!combat.isBlocking(c) && !combat.isAttacking(c)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// don't add duplicate negative keywords
|
// don't add duplicate negative keywords
|
||||||
return sa.hasParam("Keywords") && c.hasAnyKeyword(Arrays.asList(sa.getParam("Keywords").split(" & ")));
|
return sa.hasParam("Keywords") && c.hasAnyKeyword(Arrays.asList(sa.getParam("Keywords").split(" & ")));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
return debuffTgtAI(ai, sa, sa.hasParam("Keywords") ? Arrays.asList(sa.getParam("Keywords").split(" & ")) : null, false);
|
return debuffTgtAI(ai, sa, sa.hasParam("Keywords") ? Arrays.asList(sa.getParam("Keywords").split(" & ")) : null, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
||||||
if ((sa.getTargetRestrictions() == null) || !sa.getTargetRestrictions().doesTarget()) {
|
if ((sa.getTargetRestrictions() == null) || !sa.getTargetRestrictions().doesTarget()) {
|
||||||
// TODO - copied from AF_Pump.pumpDrawbackAI() - what should be
|
// TODO - copied from AF_Pump.pumpDrawbackAI() - what should be
|
||||||
// here?
|
// here?
|
||||||
} else {
|
} else {
|
||||||
return debuffTgtAI(ai, sa, sa.hasParam("Keywords") ? Arrays.asList(sa.getParam("Keywords").split(" & ")) : null, false);
|
return debuffTgtAI(ai, sa, sa.hasParam("Keywords") ? Arrays.asList(sa.getParam("Keywords").split(" & ")) : null, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} // debuffDrawbackAI()
|
} // debuffDrawbackAI()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* debuffTgtAI.
|
* debuffTgtAI.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param sa
|
* @param sa
|
||||||
* a {@link forge.game.spellability.SpellAbility} object.
|
* a {@link forge.game.spellability.SpellAbility} object.
|
||||||
* @param kws
|
* @param kws
|
||||||
* a {@link java.util.ArrayList} object.
|
* a {@link java.util.ArrayList} object.
|
||||||
* @param mandatory
|
* @param mandatory
|
||||||
* a boolean.
|
* a boolean.
|
||||||
* @return a boolean.
|
* @return a boolean.
|
||||||
*/
|
*/
|
||||||
private boolean debuffTgtAI(final Player ai, final SpellAbility sa, final List<String> kws, final boolean mandatory) {
|
private boolean debuffTgtAI(final Player ai, final SpellAbility sa, final List<String> kws, final boolean mandatory) {
|
||||||
// this would be for evasive things like Flying, Unblockable, etc
|
// this would be for evasive things like Flying, Unblockable, etc
|
||||||
if (!mandatory && ai.getGame().getPhaseHandler().getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
|
if (!mandatory && ai.getGame().getPhaseHandler().getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
CardCollection list = getCurseCreatures(ai, sa, kws == null ? Lists.<String>newArrayList() : kws);
|
CardCollection list = getCurseCreatures(ai, sa, kws == null ? Lists.<String>newArrayList() : kws);
|
||||||
list = CardLists.getValidCards(list, tgt.getValidTgts(), sa.getActivatingPlayer(), sa.getHostCard(), sa);
|
list = CardLists.getValidCards(list, tgt.getValidTgts(), sa.getActivatingPlayer(), sa.getHostCard(), sa);
|
||||||
|
|
||||||
// several uses here:
|
// several uses here:
|
||||||
// 1. make human creatures lose evasion when they are attacking
|
// 1. make human creatures lose evasion when they are attacking
|
||||||
// 2. make human creatures lose Flying/Horsemanship/Shadow/etc. when
|
// 2. make human creatures lose Flying/Horsemanship/Shadow/etc. when
|
||||||
// Comp is attacking
|
// Comp is attacking
|
||||||
// 3. remove Indestructible keyword so it can be destroyed?
|
// 3. remove Indestructible keyword so it can be destroyed?
|
||||||
// 3a. remove Persist?
|
// 3a. remove Persist?
|
||||||
|
|
||||||
if (list.isEmpty()) {
|
if (list.isEmpty()) {
|
||||||
return mandatory && debuffMandatoryTarget(ai, sa, mandatory);
|
return mandatory && debuffMandatoryTarget(ai, sa, mandatory);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (sa.getTargets().getNumTargeted() < tgt.getMaxTargets(sa.getHostCard(), sa)) {
|
while (sa.getTargets().getNumTargeted() < tgt.getMaxTargets(sa.getHostCard(), sa)) {
|
||||||
Card t = null;
|
Card t = null;
|
||||||
// boolean goodt = false;
|
// boolean goodt = false;
|
||||||
|
|
||||||
if (list.isEmpty()) {
|
if (list.isEmpty()) {
|
||||||
if ((sa.getTargets().getNumTargeted() < tgt.getMinTargets(sa.getHostCard(), sa)) || (sa.getTargets().getNumTargeted() == 0)) {
|
if ((sa.getTargets().getNumTargeted() < tgt.getMinTargets(sa.getHostCard(), sa)) || (sa.getTargets().getNumTargeted() == 0)) {
|
||||||
if (mandatory) {
|
if (mandatory) {
|
||||||
return debuffMandatoryTarget(ai, sa, mandatory);
|
return debuffMandatoryTarget(ai, sa, mandatory);
|
||||||
}
|
}
|
||||||
|
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
// TODO is this good enough? for up to amounts?
|
// TODO is this good enough? for up to amounts?
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
t = ComputerUtilCard.getBestCreatureAI(list);
|
t = ComputerUtilCard.getBestCreatureAI(list);
|
||||||
sa.getTargets().add(t);
|
sa.getTargets().add(t);
|
||||||
list.remove(t);
|
list.remove(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} // pumpTgtAI()
|
} // pumpTgtAI()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* getCurseCreatures.
|
* getCurseCreatures.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param sa
|
* @param sa
|
||||||
* a {@link forge.game.spellability.SpellAbility} object.
|
* a {@link forge.game.spellability.SpellAbility} object.
|
||||||
* @param kws
|
* @param kws
|
||||||
* a {@link java.util.ArrayList} object.
|
* a {@link java.util.ArrayList} object.
|
||||||
* @return a CardCollection.
|
* @return a CardCollection.
|
||||||
*/
|
*/
|
||||||
private CardCollection getCurseCreatures(final Player ai, final SpellAbility sa, final List<String> kws) {
|
private CardCollection getCurseCreatures(final Player ai, final SpellAbility sa, final List<String> kws) {
|
||||||
final Player opp = ComputerUtil.getOpponentFor(ai);
|
final Player opp = ComputerUtil.getOpponentFor(ai);
|
||||||
CardCollection list = CardLists.getTargetableCards(opp.getCreaturesInPlay(), sa);
|
CardCollection list = CardLists.getTargetableCards(opp.getCreaturesInPlay(), sa);
|
||||||
if (!list.isEmpty()) {
|
if (!list.isEmpty()) {
|
||||||
list = CardLists.filter(list, new Predicate<Card>() {
|
list = CardLists.filter(list, new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(final Card c) {
|
public boolean apply(final Card c) {
|
||||||
return c.hasAnyKeyword(kws); // don't add duplicate negative
|
return c.hasAnyKeyword(kws); // don't add duplicate negative
|
||||||
// keywords
|
// keywords
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
} // getCurseCreatures()
|
} // getCurseCreatures()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* debuffMandatoryTarget.
|
* debuffMandatoryTarget.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param sa
|
* @param sa
|
||||||
* a {@link forge.game.spellability.SpellAbility} object.
|
* a {@link forge.game.spellability.SpellAbility} object.
|
||||||
* @param mandatory
|
* @param mandatory
|
||||||
* a boolean.
|
* a boolean.
|
||||||
* @return a boolean.
|
* @return a boolean.
|
||||||
*/
|
*/
|
||||||
private boolean debuffMandatoryTarget(final Player ai, final SpellAbility sa, final boolean mandatory) {
|
private boolean debuffMandatoryTarget(final Player ai, final SpellAbility sa, final boolean mandatory) {
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
CardCollection list = CardLists.getValidCards(ai.getGame().getCardsIn(ZoneType.Battlefield), tgt.getValidTgts(),
|
CardCollection list = CardLists.getValidCards(ai.getGame().getCardsIn(ZoneType.Battlefield), tgt.getValidTgts(),
|
||||||
sa.getActivatingPlayer(), sa.getHostCard(), sa);
|
sa.getActivatingPlayer(), sa.getHostCard(), sa);
|
||||||
|
|
||||||
if (list.size() < tgt.getMinTargets(sa.getHostCard(), sa)) {
|
if (list.size() < tgt.getMinTargets(sa.getHostCard(), sa)) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove anything that's already been targeted
|
// Remove anything that's already been targeted
|
||||||
for (final Card c : sa.getTargets().getTargetCards()) {
|
for (final Card c : sa.getTargets().getTargetCards()) {
|
||||||
list.remove(c);
|
list.remove(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
final CardCollection pref = CardLists.filterControlledBy(list, ComputerUtil.getOpponentFor(ai));
|
final CardCollection pref = CardLists.filterControlledBy(list, ComputerUtil.getOpponentFor(ai));
|
||||||
final CardCollection forced = CardLists.filterControlledBy(list, ai);
|
final CardCollection forced = CardLists.filterControlledBy(list, ai);
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
|
|
||||||
while (sa.getTargets().getNumTargeted() < tgt.getMaxTargets(source, sa)) {
|
while (sa.getTargets().getNumTargeted() < tgt.getMaxTargets(source, sa)) {
|
||||||
if (pref.isEmpty()) {
|
if (pref.isEmpty()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Card c;
|
Card c;
|
||||||
if (CardLists.getNotType(pref, "Creature").size() == 0) {
|
if (CardLists.getNotType(pref, "Creature").size() == 0) {
|
||||||
c = ComputerUtilCard.getBestCreatureAI(pref);
|
c = ComputerUtilCard.getBestCreatureAI(pref);
|
||||||
} else {
|
} else {
|
||||||
c = ComputerUtilCard.getMostExpensivePermanentAI(pref, sa, true);
|
c = ComputerUtilCard.getMostExpensivePermanentAI(pref, sa, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
pref.remove(c);
|
pref.remove(c);
|
||||||
|
|
||||||
sa.getTargets().add(c);
|
sa.getTargets().add(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (sa.getTargets().getNumTargeted() < tgt.getMinTargets(sa.getHostCard(), sa)) {
|
while (sa.getTargets().getNumTargeted() < tgt.getMinTargets(sa.getHostCard(), sa)) {
|
||||||
if (forced.isEmpty()) {
|
if (forced.isEmpty()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO - if forced targeting, just pick something without the given
|
// TODO - if forced targeting, just pick something without the given
|
||||||
// keyword
|
// keyword
|
||||||
Card c;
|
Card c;
|
||||||
if (CardLists.getNotType(forced, "Creature").size() == 0) {
|
if (CardLists.getNotType(forced, "Creature").size() == 0) {
|
||||||
c = ComputerUtilCard.getWorstCreatureAI(forced);
|
c = ComputerUtilCard.getWorstCreatureAI(forced);
|
||||||
} else {
|
} else {
|
||||||
c = ComputerUtilCard.getCheapestPermanentAI(forced, sa, true);
|
c = ComputerUtilCard.getCheapestPermanentAI(forced, sa, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
forced.remove(c);
|
forced.remove(c);
|
||||||
|
|
||||||
sa.getTargets().add(c);
|
sa.getTargets().add(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.getTargets().getNumTargeted() < tgt.getMinTargets(sa.getHostCard(), sa)) {
|
if (sa.getTargets().getNumTargeted() < tgt.getMinTargets(sa.getHostCard(), sa)) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} // pumpMandatoryTarget()
|
} // pumpMandatoryTarget()
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
||||||
final List<String> kws = sa.hasParam("Keywords") ? Arrays.asList(sa.getParam("Keywords").split(" & ")) : new ArrayList<String>();
|
final List<String> kws = sa.hasParam("Keywords") ? Arrays.asList(sa.getParam("Keywords").split(" & ")) : new ArrayList<String>();
|
||||||
|
|
||||||
if (sa.getTargetRestrictions() == null) {
|
if (sa.getTargetRestrictions() == null) {
|
||||||
if (mandatory) {
|
if (mandatory) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return debuffTgtAI(ai, sa, kws, mandatory);
|
return debuffTgtAI(ai, sa, kws, mandatory);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,66 +1,66 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import forge.ai.AiController;
|
import forge.ai.AiController;
|
||||||
import forge.ai.AiPlayDecision;
|
import forge.ai.AiPlayDecision;
|
||||||
import forge.ai.PlayerControllerAi;
|
import forge.ai.PlayerControllerAi;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.ai.SpellApiToAi;
|
import forge.ai.SpellApiToAi;
|
||||||
import forge.game.ability.AbilityFactory;
|
import forge.game.ability.AbilityFactory;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.AbilitySub;
|
import forge.game.spellability.AbilitySub;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
|
|
||||||
public class DelayedTriggerAi extends SpellAbilityAi {
|
public class DelayedTriggerAi extends SpellAbilityAi {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
||||||
if ("Always".equals(sa.getParam("AILogic"))) {
|
if ("Always".equals(sa.getParam("AILogic"))) {
|
||||||
// TODO: improve ai
|
// TODO: improve ai
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
SpellAbility trigsa = null;
|
SpellAbility trigsa = null;
|
||||||
if (sa.hasAdditionalAbility("Execute")) {
|
if (sa.hasAdditionalAbility("Execute")) {
|
||||||
trigsa = sa.getAdditionalAbility("Execute");
|
trigsa = sa.getAdditionalAbility("Execute");
|
||||||
} else {
|
} else {
|
||||||
trigsa = AbilityFactory.getAbility(sa.getHostCard(), sa.getParam("Execute"));
|
trigsa = AbilityFactory.getAbility(sa.getHostCard(), sa.getParam("Execute"));
|
||||||
}
|
}
|
||||||
trigsa.setActivatingPlayer(ai);
|
trigsa.setActivatingPlayer(ai);
|
||||||
|
|
||||||
if (trigsa instanceof AbilitySub) {
|
if (trigsa instanceof AbilitySub) {
|
||||||
return SpellApiToAi.Converter.get(((AbilitySub) trigsa).getApi()).chkDrawbackWithSubs(ai, (AbilitySub)trigsa);
|
return SpellApiToAi.Converter.get(((AbilitySub) trigsa).getApi()).chkDrawbackWithSubs(ai, (AbilitySub)trigsa);
|
||||||
} else {
|
} else {
|
||||||
return AiPlayDecision.WillPlay == ((PlayerControllerAi)ai.getController()).getAi().canPlaySa(trigsa);
|
return AiPlayDecision.WillPlay == ((PlayerControllerAi)ai.getController()).getAi().canPlaySa(trigsa);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
||||||
SpellAbility trigsa = null;
|
SpellAbility trigsa = null;
|
||||||
if (sa.hasAdditionalAbility("Execute")) {
|
if (sa.hasAdditionalAbility("Execute")) {
|
||||||
trigsa = sa.getAdditionalAbility("Execute");
|
trigsa = sa.getAdditionalAbility("Execute");
|
||||||
} else {
|
} else {
|
||||||
trigsa = AbilityFactory.getAbility(sa.getHostCard(), sa.getParam("Execute"));
|
trigsa = AbilityFactory.getAbility(sa.getHostCard(), sa.getParam("Execute"));
|
||||||
}
|
}
|
||||||
AiController aic = ((PlayerControllerAi)ai.getController()).getAi();
|
AiController aic = ((PlayerControllerAi)ai.getController()).getAi();
|
||||||
trigsa.setActivatingPlayer(ai);
|
trigsa.setActivatingPlayer(ai);
|
||||||
|
|
||||||
if (!sa.hasParam("OptionalDecider")) {
|
if (!sa.hasParam("OptionalDecider")) {
|
||||||
return aic.doTrigger(trigsa, true);
|
return aic.doTrigger(trigsa, true);
|
||||||
} else {
|
} else {
|
||||||
return aic.doTrigger(trigsa, !sa.getParam("OptionalDecider").equals("You"));
|
return aic.doTrigger(trigsa, !sa.getParam("OptionalDecider").equals("You"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
||||||
SpellAbility trigsa = null;
|
SpellAbility trigsa = null;
|
||||||
if (sa.hasAdditionalAbility("Execute")) {
|
if (sa.hasAdditionalAbility("Execute")) {
|
||||||
trigsa = sa.getAdditionalAbility("Execute");
|
trigsa = sa.getAdditionalAbility("Execute");
|
||||||
} else {
|
} else {
|
||||||
trigsa = AbilityFactory.getAbility(sa.getHostCard(), sa.getParam("Execute"));
|
trigsa = AbilityFactory.getAbility(sa.getHostCard(), sa.getParam("Execute"));
|
||||||
}
|
}
|
||||||
trigsa.setActivatingPlayer(ai);
|
trigsa.setActivatingPlayer(ai);
|
||||||
return AiPlayDecision.WillPlay == ((PlayerControllerAi)ai.getController()).getAi().canPlaySa(trigsa);
|
return AiPlayDecision.WillPlay == ((PlayerControllerAi)ai.getController()).getAi().canPlaySa(trigsa);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,439 +1,439 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import forge.ai.*;
|
import forge.ai.*;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.ability.ApiType;
|
import forge.game.ability.ApiType;
|
||||||
import forge.game.card.*;
|
import forge.game.card.*;
|
||||||
import forge.game.cost.Cost;
|
import forge.game.cost.Cost;
|
||||||
import forge.game.cost.CostPart;
|
import forge.game.cost.CostPart;
|
||||||
import forge.game.cost.CostSacrifice;
|
import forge.game.cost.CostSacrifice;
|
||||||
import forge.game.phase.PhaseHandler;
|
import forge.game.phase.PhaseHandler;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.TargetRestrictions;
|
import forge.game.spellability.TargetRestrictions;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
|
|
||||||
public class DestroyAi extends SpellAbilityAi {
|
public class DestroyAi extends SpellAbilityAi {
|
||||||
@Override
|
@Override
|
||||||
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
||||||
return canPlayAI(ai, sa);
|
return canPlayAI(ai, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(final Player ai, SpellAbility sa) {
|
protected boolean canPlayAI(final Player ai, SpellAbility sa) {
|
||||||
// AI needs to be expanded, since this function can be pretty complex
|
// AI needs to be expanded, since this function can be pretty complex
|
||||||
// based on what the expected targets could be
|
// based on what the expected targets could be
|
||||||
final Cost abCost = sa.getPayCosts();
|
final Cost abCost = sa.getPayCosts();
|
||||||
final TargetRestrictions abTgt = sa.getTargetRestrictions();
|
final TargetRestrictions abTgt = sa.getTargetRestrictions();
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
final boolean noRegen = sa.hasParam("NoRegen");
|
final boolean noRegen = sa.hasParam("NoRegen");
|
||||||
final String logic = sa.getParam("AILogic");
|
final String logic = sa.getParam("AILogic");
|
||||||
boolean hasXCost = false;
|
boolean hasXCost = false;
|
||||||
|
|
||||||
CardCollection list;
|
CardCollection list;
|
||||||
|
|
||||||
if (abCost != null) {
|
if (abCost != null) {
|
||||||
if (!ComputerUtilCost.checkSacrificeCost(ai, abCost, source, sa)) {
|
if (!ComputerUtilCost.checkSacrificeCost(ai, abCost, source, sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ComputerUtilCost.checkLifeCost(ai, abCost, source, 4, sa)) {
|
if (!ComputerUtilCost.checkLifeCost(ai, abCost, source, 4, sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ComputerUtilCost.checkDiscardCost(ai, abCost, source)) {
|
if (!ComputerUtilCost.checkDiscardCost(ai, abCost, source)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
hasXCost = abCost.getCostMana() != null ? abCost.getCostMana().getAmountOfX() > 0 : false;
|
hasXCost = abCost.getCostMana() != null ? abCost.getCostMana().getAmountOfX() > 0 : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("AtOpponentsCombatOrAfter".equals(sa.getParam("AILogic"))) {
|
if ("AtOpponentsCombatOrAfter".equals(sa.getParam("AILogic"))) {
|
||||||
PhaseHandler ph = ai.getGame().getPhaseHandler();
|
PhaseHandler ph = ai.getGame().getPhaseHandler();
|
||||||
if (ph.getPlayerTurn() == ai || ph.getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS)) {
|
if (ph.getPlayerTurn() == ai || ph.getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if ("AtEOT".equals(sa.getParam("AILogic"))) {
|
} else if ("AtEOT".equals(sa.getParam("AILogic"))) {
|
||||||
PhaseHandler ph = ai.getGame().getPhaseHandler();
|
PhaseHandler ph = ai.getGame().getPhaseHandler();
|
||||||
if (!ph.is(PhaseType.END_OF_TURN)) {
|
if (!ph.is(PhaseType.END_OF_TURN)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if ("AtEOTIfNotAttacking".equals(sa.getParam("AILogic"))) {
|
} else if ("AtEOTIfNotAttacking".equals(sa.getParam("AILogic"))) {
|
||||||
PhaseHandler ph = ai.getGame().getPhaseHandler();
|
PhaseHandler ph = ai.getGame().getPhaseHandler();
|
||||||
if (!ph.is(PhaseType.END_OF_TURN) || ai.getAttackedWithCreatureThisTurn()) {
|
if (!ph.is(PhaseType.END_OF_TURN) || ai.getAttackedWithCreatureThisTurn()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ComputerUtil.preventRunAwayActivations(sa)) {
|
if (ComputerUtil.preventRunAwayActivations(sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Targeting
|
// Targeting
|
||||||
if (abTgt != null) {
|
if (abTgt != null) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
if (sa.hasParam("TargetingPlayer")) {
|
if (sa.hasParam("TargetingPlayer")) {
|
||||||
Player targetingPlayer = AbilityUtils.getDefinedPlayers(source, sa.getParam("TargetingPlayer"), sa).get(0);
|
Player targetingPlayer = AbilityUtils.getDefinedPlayers(source, sa.getParam("TargetingPlayer"), sa).get(0);
|
||||||
sa.setTargetingPlayer(targetingPlayer);
|
sa.setTargetingPlayer(targetingPlayer);
|
||||||
return targetingPlayer.getController().chooseTargetsFor(sa);
|
return targetingPlayer.getController().chooseTargetsFor(sa);
|
||||||
}
|
}
|
||||||
if ("MadSarkhanDragon".equals(logic)) {
|
if ("MadSarkhanDragon".equals(logic)) {
|
||||||
return SpecialCardAi.SarkhanTheMad.considerMakeDragon(ai, sa);
|
return SpecialCardAi.SarkhanTheMad.considerMakeDragon(ai, sa);
|
||||||
} else if (logic != null && logic.startsWith("MinLoyalty.")) {
|
} else if (logic != null && logic.startsWith("MinLoyalty.")) {
|
||||||
int minLoyalty = Integer.parseInt(logic.substring(logic.indexOf(".") + 1));
|
int minLoyalty = Integer.parseInt(logic.substring(logic.indexOf(".") + 1));
|
||||||
if (source.getCounters(CounterType.LOYALTY) < minLoyalty) {
|
if (source.getCounters(CounterType.LOYALTY) < minLoyalty) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if ("Polymorph".equals(logic)) {
|
} else if ("Polymorph".equals(logic)) {
|
||||||
list = CardLists.getTargetableCards(ai.getCardsIn(ZoneType.Battlefield), sa);
|
list = CardLists.getTargetableCards(ai.getCardsIn(ZoneType.Battlefield), sa);
|
||||||
if (list.isEmpty()) {
|
if (list.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (Card c : list) {
|
for (Card c : list) {
|
||||||
if (c.hasKeyword("Indestructible")) {
|
if (c.hasKeyword("Indestructible")) {
|
||||||
sa.getTargets().add(c);
|
sa.getTargets().add(c);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Card worst = ComputerUtilCard.getWorstAI(list);
|
Card worst = ComputerUtilCard.getWorstAI(list);
|
||||||
if (worst.isCreature() && ComputerUtilCard.evaluateCreature(worst) >= 200) {
|
if (worst.isCreature() && ComputerUtilCard.evaluateCreature(worst) >= 200) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!worst.isCreature() && worst.getCMC() > 1) {
|
if (!worst.isCreature() && worst.getCMC() > 1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
sa.getTargets().add(worst);
|
sa.getTargets().add(worst);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
list = CardLists.getTargetableCards(ai.getOpponents().getCardsIn(ZoneType.Battlefield), sa);
|
list = CardLists.getTargetableCards(ai.getOpponents().getCardsIn(ZoneType.Battlefield), sa);
|
||||||
if ("FatalPush".equals(logic)) {
|
if ("FatalPush".equals(logic)) {
|
||||||
final int cmcMax = ai.hasRevolt() ? 4 : 2;
|
final int cmcMax = ai.hasRevolt() ? 4 : 2;
|
||||||
list = CardLists.filter(list, CardPredicates.lessCMC(cmcMax));
|
list = CardLists.filter(list, CardPredicates.lessCMC(cmcMax));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter AI-specific targets if provided
|
// Filter AI-specific targets if provided
|
||||||
list = ComputerUtil.filterAITgts(sa, ai, (CardCollection)list, true);
|
list = ComputerUtil.filterAITgts(sa, ai, (CardCollection)list, true);
|
||||||
|
|
||||||
list = CardLists.getNotKeyword(list, "Indestructible");
|
list = CardLists.getNotKeyword(list, "Indestructible");
|
||||||
if (CardLists.getNotType(list, "Creature").isEmpty()) {
|
if (CardLists.getNotType(list, "Creature").isEmpty()) {
|
||||||
list = ComputerUtilCard.prioritizeCreaturesWorthRemovingNow(ai, list, false);
|
list = ComputerUtilCard.prioritizeCreaturesWorthRemovingNow(ai, list, false);
|
||||||
}
|
}
|
||||||
if (!SpellAbilityAi.playReusable(ai, sa)) {
|
if (!SpellAbilityAi.playReusable(ai, sa)) {
|
||||||
list = CardLists.filter(list, new Predicate<Card>() {
|
list = CardLists.filter(list, new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(final Card c) {
|
public boolean apply(final Card c) {
|
||||||
//Check for cards that can be sacrificed in response
|
//Check for cards that can be sacrificed in response
|
||||||
for (final SpellAbility ability : c.getAllSpellAbilities()) {
|
for (final SpellAbility ability : c.getAllSpellAbilities()) {
|
||||||
if (ability.isAbility()) {
|
if (ability.isAbility()) {
|
||||||
final Cost cost = ability.getPayCosts();
|
final Cost cost = ability.getPayCosts();
|
||||||
for (final CostPart part : cost.getCostParts()) {
|
for (final CostPart part : cost.getCostParts()) {
|
||||||
if (!(part instanceof CostSacrifice)) {
|
if (!(part instanceof CostSacrifice)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
CostSacrifice sacCost = (CostSacrifice) part;
|
CostSacrifice sacCost = (CostSacrifice) part;
|
||||||
if (sacCost.payCostFromSource() && ComputerUtilCost.canPayCost(ability, c.getController())) {
|
if (sacCost.payCostFromSource() && ComputerUtilCost.canPayCost(ability, c.getController())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (c.hasSVar("SacMe")) {
|
if (c.hasSVar("SacMe")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
//Check for undying
|
//Check for undying
|
||||||
return (!c.hasKeyword("Undying") || c.getCounters(CounterType.P1P1) > 0);
|
return (!c.hasKeyword("Undying") || c.getCounters(CounterType.P1P1) > 0);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// If NoRegen is not set, filter out creatures that have a
|
// If NoRegen is not set, filter out creatures that have a
|
||||||
// regeneration shield
|
// regeneration shield
|
||||||
if (!noRegen) {
|
if (!noRegen) {
|
||||||
// TODO filter out things that might be tougher?
|
// TODO filter out things that might be tougher?
|
||||||
list = CardLists.filter(list, new Predicate<Card>() {
|
list = CardLists.filter(list, new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(final Card c) {
|
public boolean apply(final Card c) {
|
||||||
return (c.getShieldCount() == 0 && !ComputerUtil.canRegenerate(ai, c));
|
return (c.getShieldCount() == 0 && !ComputerUtil.canRegenerate(ai, c));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (list.isEmpty()) {
|
if (list.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int maxTargets = abTgt.getMaxTargets(sa.getHostCard(), sa);
|
int maxTargets = abTgt.getMaxTargets(sa.getHostCard(), sa);
|
||||||
|
|
||||||
if (hasXCost) {
|
if (hasXCost) {
|
||||||
// TODO: currently the AI will maximize mana spent on X, trying to maximize damage. This may need improvement.
|
// TODO: currently the AI will maximize mana spent on X, trying to maximize damage. This may need improvement.
|
||||||
maxTargets = Math.min(ComputerUtilMana.determineMaxAffordableX(ai, sa), abTgt.getMaxTargets(sa.getHostCard(), sa));
|
maxTargets = Math.min(ComputerUtilMana.determineMaxAffordableX(ai, sa), abTgt.getMaxTargets(sa.getHostCard(), sa));
|
||||||
}
|
}
|
||||||
if (sa.hasParam("AIMaxTgtsCount")) {
|
if (sa.hasParam("AIMaxTgtsCount")) {
|
||||||
// Cards that have confusing costs for the AI (e.g. Eliminate the Competition) can have forced max target constraints specified
|
// Cards that have confusing costs for the AI (e.g. Eliminate the Competition) can have forced max target constraints specified
|
||||||
// TODO: is there a better way to predict things like "sac X" costs without needing a special AI variable?
|
// TODO: is there a better way to predict things like "sac X" costs without needing a special AI variable?
|
||||||
maxTargets = Math.min(CardFactoryUtil.xCount(sa.getHostCard(), "Count$" + sa.getParam("AIMaxTgtsCount")), maxTargets);
|
maxTargets = Math.min(CardFactoryUtil.xCount(sa.getHostCard(), "Count$" + sa.getParam("AIMaxTgtsCount")), maxTargets);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (maxTargets == 0) {
|
if (maxTargets == 0) {
|
||||||
// can't afford X or otherwise target anything
|
// can't afford X or otherwise target anything
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// target loop
|
// target loop
|
||||||
while (sa.getTargets().getNumTargeted() < maxTargets) {
|
while (sa.getTargets().getNumTargeted() < maxTargets) {
|
||||||
if (list.isEmpty()) {
|
if (list.isEmpty()) {
|
||||||
if ((sa.getTargets().getNumTargeted() < abTgt.getMinTargets(sa.getHostCard(), sa))
|
if ((sa.getTargets().getNumTargeted() < abTgt.getMinTargets(sa.getHostCard(), sa))
|
||||||
|| (sa.getTargets().getNumTargeted() == 0)) {
|
|| (sa.getTargets().getNumTargeted() == 0)) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
// TODO is this good enough? for up to amounts?
|
// TODO is this good enough? for up to amounts?
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Card choice = null;
|
Card choice = null;
|
||||||
// If the targets are only of one type, take the best
|
// If the targets are only of one type, take the best
|
||||||
if (CardLists.getNotType(list, "Creature").isEmpty()) {
|
if (CardLists.getNotType(list, "Creature").isEmpty()) {
|
||||||
choice = ComputerUtilCard.getBestCreatureAI(list);
|
choice = ComputerUtilCard.getBestCreatureAI(list);
|
||||||
if ("OppDestroyYours".equals(logic)) {
|
if ("OppDestroyYours".equals(logic)) {
|
||||||
Card aiBest = ComputerUtilCard.getBestCreatureAI(ai.getCreaturesInPlay());
|
Card aiBest = ComputerUtilCard.getBestCreatureAI(ai.getCreaturesInPlay());
|
||||||
if (ComputerUtilCard.evaluateCreature(aiBest) > ComputerUtilCard.evaluateCreature(choice) - 40) {
|
if (ComputerUtilCard.evaluateCreature(aiBest) > ComputerUtilCard.evaluateCreature(choice) - 40) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ("Pongify".equals(logic)) {
|
if ("Pongify".equals(logic)) {
|
||||||
final Card token = TokenAi.spawnToken(choice.getController(), sa.getSubAbility());
|
final Card token = TokenAi.spawnToken(choice.getController(), sa.getSubAbility());
|
||||||
if (token == null) {
|
if (token == null) {
|
||||||
return true; // becomes Terminate
|
return true; // becomes Terminate
|
||||||
} else {
|
} else {
|
||||||
if (source.getGame().getPhaseHandler().getPhase()
|
if (source.getGame().getPhaseHandler().getPhase()
|
||||||
.isBefore(PhaseType.COMBAT_DECLARE_BLOCKERS) || // prevent surprise combatant
|
.isBefore(PhaseType.COMBAT_DECLARE_BLOCKERS) || // prevent surprise combatant
|
||||||
ComputerUtilCard.evaluateCreature(choice) < 1.5
|
ComputerUtilCard.evaluateCreature(choice) < 1.5
|
||||||
* ComputerUtilCard.evaluateCreature(token)) {
|
* ComputerUtilCard.evaluateCreature(token)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (CardLists.getNotType(list, "Land").isEmpty()) {
|
} else if (CardLists.getNotType(list, "Land").isEmpty()) {
|
||||||
choice = ComputerUtilCard.getBestLandAI(list);
|
choice = ComputerUtilCard.getBestLandAI(list);
|
||||||
|
|
||||||
if ("LandForLand".equals(logic) || "GhostQuarter".equals(logic)) {
|
if ("LandForLand".equals(logic) || "GhostQuarter".equals(logic)) {
|
||||||
// Strip Mine, Wasteland - cut short if the relevant logic fails
|
// Strip Mine, Wasteland - cut short if the relevant logic fails
|
||||||
if (!doLandForLandRemovalLogic(sa, ai, choice, logic)) {
|
if (!doLandForLandRemovalLogic(sa, ai, choice, logic)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
choice = ComputerUtilCard.getMostExpensivePermanentAI(list, sa, true);
|
choice = ComputerUtilCard.getMostExpensivePermanentAI(list, sa, true);
|
||||||
}
|
}
|
||||||
//option to hold removal instead only applies for single targeted removal
|
//option to hold removal instead only applies for single targeted removal
|
||||||
if (!sa.isTrigger() && abTgt.getMaxTargets(sa.getHostCard(), sa) == 1) {
|
if (!sa.isTrigger() && abTgt.getMaxTargets(sa.getHostCard(), sa) == 1) {
|
||||||
if (!ComputerUtilCard.useRemovalNow(sa, choice, 0, ZoneType.Graveyard)) {
|
if (!ComputerUtilCard.useRemovalNow(sa, choice, 0, ZoneType.Graveyard)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (choice == null) { // can't find anything left
|
if (choice == null) { // can't find anything left
|
||||||
if ((sa.getTargets().getNumTargeted() < abTgt.getMinTargets(sa.getHostCard(), sa))
|
if ((sa.getTargets().getNumTargeted() < abTgt.getMinTargets(sa.getHostCard(), sa))
|
||||||
|| (sa.getTargets().getNumTargeted() == 0)) {
|
|| (sa.getTargets().getNumTargeted() == 0)) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
// TODO is this good enough? for up to amounts?
|
// TODO is this good enough? for up to amounts?
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Don't destroy stolen permanents when the stealing aura can be destroyed
|
// Don't destroy stolen permanents when the stealing aura can be destroyed
|
||||||
if (choice.getOwner() == ai) {
|
if (choice.getOwner() == ai) {
|
||||||
for (Card aura : choice.getEnchantedBy(false)) {
|
for (Card aura : choice.getEnchantedBy(false)) {
|
||||||
SpellAbility sp = aura.getFirstSpellAbility();
|
SpellAbility sp = aura.getFirstSpellAbility();
|
||||||
if (sp != null && "GainControl".equals(sp.getParam("AILogic"))
|
if (sp != null && "GainControl".equals(sp.getParam("AILogic"))
|
||||||
&& aura.getController() != ai && sa.canTarget(aura)) {
|
&& aura.getController() != ai && sa.canTarget(aura)) {
|
||||||
choice = aura;
|
choice = aura;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
list.remove(choice);
|
list.remove(choice);
|
||||||
sa.getTargets().add(choice);
|
sa.getTargets().add(choice);
|
||||||
}
|
}
|
||||||
} else if (sa.hasParam("Defined")) {
|
} else if (sa.hasParam("Defined")) {
|
||||||
list = AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), sa);
|
list = AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), sa);
|
||||||
if ("WillSkipTurn".equals(logic) && (sa.getHostCard().getController().equals(ai)
|
if ("WillSkipTurn".equals(logic) && (sa.getHostCard().getController().equals(ai)
|
||||||
|| ai.getCreaturesInPlay().size() < ComputerUtil.getOpponentFor(ai).getCreaturesInPlay().size()
|
|| ai.getCreaturesInPlay().size() < ComputerUtil.getOpponentFor(ai).getCreaturesInPlay().size()
|
||||||
|| !source.getGame().getPhaseHandler().isPlayerTurn(ai)
|
|| !source.getGame().getPhaseHandler().isPlayerTurn(ai)
|
||||||
|| ai.getLife() <= 5)) {
|
|| ai.getLife() <= 5)) {
|
||||||
// Basic ai logic for Lethal Vapors
|
// Basic ai logic for Lethal Vapors
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (list.isEmpty()
|
if (list.isEmpty()
|
||||||
|| !CardLists.filterControlledBy(list, ai).isEmpty()
|
|| !CardLists.filterControlledBy(list, ai).isEmpty()
|
||||||
|| CardLists.getNotKeyword(list, "Indestructible").isEmpty()) {
|
|| CardLists.getNotKeyword(list, "Indestructible").isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
final boolean noRegen = sa.hasParam("NoRegen");
|
final boolean noRegen = sa.hasParam("NoRegen");
|
||||||
if (tgt != null) {
|
if (tgt != null) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
|
|
||||||
CardCollection list = CardLists.getTargetableCards(ai.getGame().getCardsIn(ZoneType.Battlefield), sa);
|
CardCollection list = CardLists.getTargetableCards(ai.getGame().getCardsIn(ZoneType.Battlefield), sa);
|
||||||
list = CardLists.getValidCards(list, tgt.getValidTgts(), source.getController(), source, sa);
|
list = CardLists.getValidCards(list, tgt.getValidTgts(), source.getController(), source, sa);
|
||||||
|
|
||||||
if (list.isEmpty() || list.size() < tgt.getMinTargets(sa.getHostCard(), sa)) {
|
if (list.isEmpty() || list.size() < tgt.getMinTargets(sa.getHostCard(), sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
CardCollection preferred = CardLists.getNotKeyword(list, "Indestructible");
|
CardCollection preferred = CardLists.getNotKeyword(list, "Indestructible");
|
||||||
preferred = CardLists.filterControlledBy(preferred, ai.getOpponents());
|
preferred = CardLists.filterControlledBy(preferred, ai.getOpponents());
|
||||||
if (CardLists.getNotType(preferred, "Creature").isEmpty()) {
|
if (CardLists.getNotType(preferred, "Creature").isEmpty()) {
|
||||||
preferred = ComputerUtilCard.prioritizeCreaturesWorthRemovingNow(ai, preferred, false);
|
preferred = ComputerUtilCard.prioritizeCreaturesWorthRemovingNow(ai, preferred, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If NoRegen is not set, filter out creatures that have a
|
// If NoRegen is not set, filter out creatures that have a
|
||||||
// regeneration shield
|
// regeneration shield
|
||||||
if (!noRegen) {
|
if (!noRegen) {
|
||||||
// TODO filter out things that could regenerate in response?
|
// TODO filter out things that could regenerate in response?
|
||||||
// might be tougher?
|
// might be tougher?
|
||||||
preferred = CardLists.filter(preferred, new Predicate<Card>() {
|
preferred = CardLists.filter(preferred, new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(final Card c) {
|
public boolean apply(final Card c) {
|
||||||
return c.getShieldCount() == 0;
|
return c.getShieldCount() == 0;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter AI-specific targets if provided
|
// Filter AI-specific targets if provided
|
||||||
preferred = ComputerUtil.filterAITgts(sa, ai, (CardCollection)preferred, true);
|
preferred = ComputerUtil.filterAITgts(sa, ai, (CardCollection)preferred, true);
|
||||||
|
|
||||||
for (final Card c : preferred) {
|
for (final Card c : preferred) {
|
||||||
list.remove(c);
|
list.remove(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (preferred.isEmpty() && !mandatory) {
|
if (preferred.isEmpty() && !mandatory) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (sa.getTargets().getNumTargeted() < tgt.getMaxTargets(sa.getHostCard(), sa)) {
|
while (sa.getTargets().getNumTargeted() < tgt.getMaxTargets(sa.getHostCard(), sa)) {
|
||||||
if (preferred.isEmpty()) {
|
if (preferred.isEmpty()) {
|
||||||
if (sa.getTargets().getNumTargeted() == 0
|
if (sa.getTargets().getNumTargeted() == 0
|
||||||
|| sa.getTargets().getNumTargeted() < tgt.getMinTargets(sa.getHostCard(), sa)) {
|
|| sa.getTargets().getNumTargeted() < tgt.getMinTargets(sa.getHostCard(), sa)) {
|
||||||
if (!mandatory) {
|
if (!mandatory) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Card c;
|
Card c;
|
||||||
if (CardLists.getNotType(preferred, "Creature").isEmpty()) {
|
if (CardLists.getNotType(preferred, "Creature").isEmpty()) {
|
||||||
c = ComputerUtilCard.getBestCreatureAI(preferred);
|
c = ComputerUtilCard.getBestCreatureAI(preferred);
|
||||||
} else if (CardLists.getNotType(preferred, "Land").isEmpty()) {
|
} else if (CardLists.getNotType(preferred, "Land").isEmpty()) {
|
||||||
c = ComputerUtilCard.getBestLandAI(preferred);
|
c = ComputerUtilCard.getBestLandAI(preferred);
|
||||||
} else {
|
} else {
|
||||||
c = ComputerUtilCard.getMostExpensivePermanentAI(preferred, sa, false);
|
c = ComputerUtilCard.getMostExpensivePermanentAI(preferred, sa, false);
|
||||||
}
|
}
|
||||||
sa.getTargets().add(c);
|
sa.getTargets().add(c);
|
||||||
preferred.remove(c);
|
preferred.remove(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while (sa.getTargets().getNumTargeted() < tgt.getMinTargets(sa.getHostCard(), sa)) {
|
while (sa.getTargets().getNumTargeted() < tgt.getMinTargets(sa.getHostCard(), sa)) {
|
||||||
if (list.isEmpty()) {
|
if (list.isEmpty()) {
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
Card c;
|
Card c;
|
||||||
if (CardLists.getNotType(list, "Creature").isEmpty()) {
|
if (CardLists.getNotType(list, "Creature").isEmpty()) {
|
||||||
if (!sa.getUniqueTargets().isEmpty() && sa.getParent().getApi() == ApiType.Destroy
|
if (!sa.getUniqueTargets().isEmpty() && sa.getParent().getApi() == ApiType.Destroy
|
||||||
&& sa.getUniqueTargets().get(0) instanceof Card) {
|
&& sa.getUniqueTargets().get(0) instanceof Card) {
|
||||||
// basic ai for Diaochan
|
// basic ai for Diaochan
|
||||||
c = (Card) sa.getUniqueTargets().get(0);
|
c = (Card) sa.getUniqueTargets().get(0);
|
||||||
} else {
|
} else {
|
||||||
c = ComputerUtilCard.getWorstCreatureAI(list);
|
c = ComputerUtilCard.getWorstCreatureAI(list);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
c = ComputerUtilCard.getCheapestPermanentAI(list, sa, false);
|
c = ComputerUtilCard.getCheapestPermanentAI(list, sa, false);
|
||||||
}
|
}
|
||||||
sa.getTargets().add(c);
|
sa.getTargets().add(c);
|
||||||
list.remove(c);
|
list.remove(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.getTargets().getNumTargeted() < tgt.getMinTargets(sa.getHostCard(), sa)) {
|
if (sa.getTargets().getNumTargeted() < tgt.getMinTargets(sa.getHostCard(), sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!mandatory) {
|
if (!mandatory) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean doLandForLandRemovalLogic(SpellAbility sa, Player ai, Card tgtLand, String logic) {
|
public boolean doLandForLandRemovalLogic(SpellAbility sa, Player ai, Card tgtLand, String logic) {
|
||||||
if (tgtLand == null) { return false; }
|
if (tgtLand == null) { return false; }
|
||||||
|
|
||||||
Player tgtPlayer = tgtLand.getController();
|
Player tgtPlayer = tgtLand.getController();
|
||||||
int oppLandsOTB = tgtPlayer.getLandsInPlay().size();
|
int oppLandsOTB = tgtPlayer.getLandsInPlay().size();
|
||||||
|
|
||||||
// AI profile-dependent properties
|
// AI profile-dependent properties
|
||||||
AiController aic = ((PlayerControllerAi)ai.getController()).getAi();
|
AiController aic = ((PlayerControllerAi)ai.getController()).getAi();
|
||||||
int amountNoTempoCheck = aic.getIntProperty(AiProps.STRIPMINE_MIN_LANDS_OTB_FOR_NO_TEMPO_CHECK);
|
int amountNoTempoCheck = aic.getIntProperty(AiProps.STRIPMINE_MIN_LANDS_OTB_FOR_NO_TEMPO_CHECK);
|
||||||
int amountNoTimingCheck = aic.getIntProperty(AiProps.STRIPMINE_MIN_LANDS_FOR_NO_TIMING_CHECK);
|
int amountNoTimingCheck = aic.getIntProperty(AiProps.STRIPMINE_MIN_LANDS_FOR_NO_TIMING_CHECK);
|
||||||
int amountLandsInHand = aic.getIntProperty(AiProps.STRIPMINE_MIN_LANDS_IN_HAND_TO_ACTIVATE);
|
int amountLandsInHand = aic.getIntProperty(AiProps.STRIPMINE_MIN_LANDS_IN_HAND_TO_ACTIVATE);
|
||||||
int amountLandsToManalock = aic.getIntProperty(AiProps.STRIPMINE_MAX_LANDS_TO_ATTEMPT_MANALOCKING);
|
int amountLandsToManalock = aic.getIntProperty(AiProps.STRIPMINE_MAX_LANDS_TO_ATTEMPT_MANALOCKING);
|
||||||
boolean highPriorityIfNoLandDrop = aic.getBooleanProperty(AiProps.STRIPMINE_HIGH_PRIORITY_ON_SKIPPED_LANDDROP);
|
boolean highPriorityIfNoLandDrop = aic.getBooleanProperty(AiProps.STRIPMINE_HIGH_PRIORITY_ON_SKIPPED_LANDDROP);
|
||||||
|
|
||||||
// if the opponent didn't play a land and has few lands OTB, might be worth mana-locking him
|
// if the opponent didn't play a land and has few lands OTB, might be worth mana-locking him
|
||||||
PhaseHandler ph = ai.getGame().getPhaseHandler();
|
PhaseHandler ph = ai.getGame().getPhaseHandler();
|
||||||
boolean oppSkippedLandDrop = (tgtPlayer.getLandsPlayedLastTurn() == 0 && ph.isPlayerTurn(ai))
|
boolean oppSkippedLandDrop = (tgtPlayer.getLandsPlayedLastTurn() == 0 && ph.isPlayerTurn(ai))
|
||||||
|| (tgtPlayer.getLandsPlayedThisTurn() == 0 && ph.isPlayerTurn(tgtPlayer) && ph.getPhase().isAfter(PhaseType.MAIN2));
|
|| (tgtPlayer.getLandsPlayedThisTurn() == 0 && ph.isPlayerTurn(tgtPlayer) && ph.getPhase().isAfter(PhaseType.MAIN2));
|
||||||
boolean canManaLock = oppLandsOTB <= amountLandsToManalock && oppSkippedLandDrop;
|
boolean canManaLock = oppLandsOTB <= amountLandsToManalock && oppSkippedLandDrop;
|
||||||
|
|
||||||
// Best target is a basic land, and there's only one of it, so destroying it may potentially color-lock the opponent
|
// Best target is a basic land, and there's only one of it, so destroying it may potentially color-lock the opponent
|
||||||
// (triggers either if the opponent skipped a land drop or if there are quite a few lands already in play but only one of the given type)
|
// (triggers either if the opponent skipped a land drop or if there are quite a few lands already in play but only one of the given type)
|
||||||
CardCollection oppLands = tgtPlayer.getLandsInPlay();
|
CardCollection oppLands = tgtPlayer.getLandsInPlay();
|
||||||
boolean canColorLock = (oppSkippedLandDrop || oppLands.size() > 3)
|
boolean canColorLock = (oppSkippedLandDrop || oppLands.size() > 3)
|
||||||
&& tgtLand.isBasicLand() && CardLists.filter(oppLands, CardPredicates.nameEquals(tgtLand.getName())).size() == 1;
|
&& tgtLand.isBasicLand() && CardLists.filter(oppLands, CardPredicates.nameEquals(tgtLand.getName())).size() == 1;
|
||||||
|
|
||||||
// Non-basic lands are currently not ranked in any way in ComputerUtilCard#getBestLandAI, so if a non-basic land is best target,
|
// Non-basic lands are currently not ranked in any way in ComputerUtilCard#getBestLandAI, so if a non-basic land is best target,
|
||||||
// consider killing it off unless there's too much potential tempo loss.
|
// consider killing it off unless there's too much potential tempo loss.
|
||||||
// TODO: actually rank non-basics in that method and then kill off the potentially dangerous (manlands, Valakut) or lucrative
|
// TODO: actually rank non-basics in that method and then kill off the potentially dangerous (manlands, Valakut) or lucrative
|
||||||
// (dual/triple mana that opens access to a certain color) lands
|
// (dual/triple mana that opens access to a certain color) lands
|
||||||
boolean nonBasicTgt = !tgtLand.isBasicLand();
|
boolean nonBasicTgt = !tgtLand.isBasicLand();
|
||||||
|
|
||||||
// Try not to lose tempo too much and not to mana-screw yourself when considering this logic
|
// Try not to lose tempo too much and not to mana-screw yourself when considering this logic
|
||||||
int numLandsInHand = CardLists.filter(ai.getCardsIn(ZoneType.Hand), CardPredicates.Presets.LANDS_PRODUCING_MANA).size();
|
int numLandsInHand = CardLists.filter(ai.getCardsIn(ZoneType.Hand), CardPredicates.Presets.LANDS_PRODUCING_MANA).size();
|
||||||
int numLandsOTB = CardLists.filter(ai.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.LANDS_PRODUCING_MANA).size();
|
int numLandsOTB = CardLists.filter(ai.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.LANDS_PRODUCING_MANA).size();
|
||||||
|
|
||||||
// If the opponent skipped a land drop, consider not looking at having the extra land in hand if the profile allows it
|
// If the opponent skipped a land drop, consider not looking at having the extra land in hand if the profile allows it
|
||||||
boolean isHighPriority = highPriorityIfNoLandDrop && oppSkippedLandDrop;
|
boolean isHighPriority = highPriorityIfNoLandDrop && oppSkippedLandDrop;
|
||||||
|
|
||||||
boolean timingCheck = canManaLock || canColorLock || nonBasicTgt;
|
boolean timingCheck = canManaLock || canColorLock || nonBasicTgt;
|
||||||
boolean tempoCheck = numLandsOTB >= amountNoTempoCheck
|
boolean tempoCheck = numLandsOTB >= amountNoTempoCheck
|
||||||
|| ((numLandsInHand >= amountLandsInHand || isHighPriority) && ((numLandsInHand + numLandsOTB >= amountNoTimingCheck) || timingCheck));
|
|| ((numLandsInHand >= amountLandsInHand || isHighPriority) && ((numLandsInHand + numLandsOTB >= amountNoTimingCheck) || timingCheck));
|
||||||
|
|
||||||
// For Ghost Quarter, only use it if you have either more lands in play than your opponent
|
// For Ghost Quarter, only use it if you have either more lands in play than your opponent
|
||||||
// or the same number of lands but an extra land in hand (otherwise the AI plays too suboptimally)
|
// or the same number of lands but an extra land in hand (otherwise the AI plays too suboptimally)
|
||||||
if ("GhostQuarter".equals(logic)) {
|
if ("GhostQuarter".equals(logic)) {
|
||||||
return tempoCheck && (numLandsOTB > oppLands.size() || (numLandsOTB == oppLands.size() && numLandsInHand > 0));
|
return tempoCheck && (numLandsOTB > oppLands.size() || (numLandsOTB == oppLands.size() && numLandsInHand > 0));
|
||||||
} else {
|
} else {
|
||||||
return tempoCheck;
|
return tempoCheck;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,158 +1,158 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
|
|
||||||
import forge.ai.*;
|
import forge.ai.*;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardCollection;
|
import forge.game.card.CardCollection;
|
||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
import forge.game.cost.Cost;
|
import forge.game.cost.Cost;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
import forge.game.combat.Combat;
|
import forge.game.combat.Combat;
|
||||||
|
|
||||||
public class DestroyAllAi extends SpellAbilityAi {
|
public class DestroyAllAi extends SpellAbilityAi {
|
||||||
|
|
||||||
private static final Predicate<Card> predicate = new Predicate<Card>() {
|
private static final Predicate<Card> predicate = new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(final Card c) {
|
public boolean apply(final Card c) {
|
||||||
return !(c.hasKeyword("Indestructible") || c.getSVar("SacMe").length() > 0);
|
return !(c.hasKeyword("Indestructible") || c.getSVar("SacMe").length() > 0);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see forge.card.abilityfactory.SpellAiLogic#doTriggerAINoCost(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility, boolean)
|
* @see forge.card.abilityfactory.SpellAiLogic#doTriggerAINoCost(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility, boolean)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
||||||
if (mandatory) {
|
if (mandatory) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return doMassRemovalLogic(ai, sa);
|
return doMassRemovalLogic(ai, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean chkAIDrawback(SpellAbility sa, Player aiPlayer) {
|
public boolean chkAIDrawback(SpellAbility sa, Player aiPlayer) {
|
||||||
//TODO: Check for bad outcome
|
//TODO: Check for bad outcome
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(final Player ai, SpellAbility sa) {
|
protected boolean canPlayAI(final Player ai, SpellAbility sa) {
|
||||||
// AI needs to be expanded, since this function can be pretty complex
|
// AI needs to be expanded, since this function can be pretty complex
|
||||||
// based on what the expected targets could be
|
// based on what the expected targets could be
|
||||||
final Cost abCost = sa.getPayCosts();
|
final Cost abCost = sa.getPayCosts();
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
|
|
||||||
if (abCost != null) {
|
if (abCost != null) {
|
||||||
// AI currently disabled for some costs
|
// AI currently disabled for some costs
|
||||||
if (!ComputerUtilCost.checkLifeCost(ai, abCost, source, 4, sa)) {
|
if (!ComputerUtilCost.checkLifeCost(ai, abCost, source, 4, sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// prevent run-away activations - first time will always return true
|
// prevent run-away activations - first time will always return true
|
||||||
if (ComputerUtil.preventRunAwayActivations(sa)) {
|
if (ComputerUtil.preventRunAwayActivations(sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return doMassRemovalLogic(ai, sa);
|
return doMassRemovalLogic(ai, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean doMassRemovalLogic(Player ai, SpellAbility sa) {
|
public boolean doMassRemovalLogic(Player ai, SpellAbility sa) {
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
Player opponent = ComputerUtil.getOpponentFor(ai); // TODO: how should this AI logic work for multiplayer and getOpponents()?
|
Player opponent = ComputerUtil.getOpponentFor(ai); // TODO: how should this AI logic work for multiplayer and getOpponents()?
|
||||||
|
|
||||||
final int CREATURE_EVAL_THRESHOLD = 200;
|
final int CREATURE_EVAL_THRESHOLD = 200;
|
||||||
|
|
||||||
String valid = "";
|
String valid = "";
|
||||||
if (sa.hasParam("ValidCards")) {
|
if (sa.hasParam("ValidCards")) {
|
||||||
valid = sa.getParam("ValidCards");
|
valid = sa.getParam("ValidCards");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (valid.contains("X") && source.getSVar("X").equals("Count$xPaid")) {
|
if (valid.contains("X") && source.getSVar("X").equals("Count$xPaid")) {
|
||||||
// Set PayX here to maximum value.
|
// Set PayX here to maximum value.
|
||||||
final int xPay = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
final int xPay = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
||||||
source.setSVar("PayX", Integer.toString(xPay));
|
source.setSVar("PayX", Integer.toString(xPay));
|
||||||
valid = valid.replace("X", Integer.toString(xPay));
|
valid = valid.replace("X", Integer.toString(xPay));
|
||||||
}
|
}
|
||||||
|
|
||||||
CardCollection opplist = CardLists.getValidCards(opponent.getCardsIn(ZoneType.Battlefield),
|
CardCollection opplist = CardLists.getValidCards(opponent.getCardsIn(ZoneType.Battlefield),
|
||||||
valid.split(","), source.getController(), source, sa);
|
valid.split(","), source.getController(), source, sa);
|
||||||
CardCollection ailist = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), valid.split(","),
|
CardCollection ailist = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), valid.split(","),
|
||||||
source.getController(), source, sa);
|
source.getController(), source, sa);
|
||||||
|
|
||||||
opplist = CardLists.filter(opplist, predicate);
|
opplist = CardLists.filter(opplist, predicate);
|
||||||
ailist = CardLists.filter(ailist, predicate);
|
ailist = CardLists.filter(ailist, predicate);
|
||||||
if (opplist.isEmpty()) {
|
if (opplist.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.usesTargeting()) {
|
if (sa.usesTargeting()) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
if (sa.canTarget(opponent)) {
|
if (sa.canTarget(opponent)) {
|
||||||
sa.getTargets().add(opponent);
|
sa.getTargets().add(opponent);
|
||||||
ailist.clear();
|
ailist.clear();
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if only creatures are affected evaluate both lists and pass only if
|
// if only creatures are affected evaluate both lists and pass only if
|
||||||
// human creatures are more valuable
|
// human creatures are more valuable
|
||||||
if (CardLists.getNotType(opplist, "Creature").isEmpty() && CardLists.getNotType(ailist, "Creature").isEmpty()) {
|
if (CardLists.getNotType(opplist, "Creature").isEmpty() && CardLists.getNotType(ailist, "Creature").isEmpty()) {
|
||||||
if (ComputerUtilCard.evaluateCreatureList(ailist) + CREATURE_EVAL_THRESHOLD < ComputerUtilCard.evaluateCreatureList(opplist)) {
|
if (ComputerUtilCard.evaluateCreatureList(ailist) + CREATURE_EVAL_THRESHOLD < ComputerUtilCard.evaluateCreatureList(opplist)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ai.getGame().getPhaseHandler().getPhase().isBefore(PhaseType.MAIN2)) {
|
if (ai.getGame().getPhaseHandler().getPhase().isBefore(PhaseType.MAIN2)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// test whether the human can kill the ai next turn
|
// test whether the human can kill the ai next turn
|
||||||
Combat combat = new Combat(opponent);
|
Combat combat = new Combat(opponent);
|
||||||
boolean containsAttacker = false;
|
boolean containsAttacker = false;
|
||||||
for (Card att : opponent.getCreaturesInPlay()) {
|
for (Card att : opponent.getCreaturesInPlay()) {
|
||||||
if (ComputerUtilCombat.canAttackNextTurn(att, ai)) {
|
if (ComputerUtilCombat.canAttackNextTurn(att, ai)) {
|
||||||
combat.addAttacker(att, ai);
|
combat.addAttacker(att, ai);
|
||||||
containsAttacker = containsAttacker | opplist.contains(att);
|
containsAttacker = containsAttacker | opplist.contains(att);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!containsAttacker) {
|
if (!containsAttacker) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
AiBlockController block = new AiBlockController(ai);
|
AiBlockController block = new AiBlockController(ai);
|
||||||
block.assignBlockersForCombat(combat);
|
block.assignBlockersForCombat(combat);
|
||||||
|
|
||||||
if (ComputerUtilCombat.lifeInSeriousDanger(ai, combat)) {
|
if (ComputerUtilCombat.lifeInSeriousDanger(ai, combat)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
} // only lands involved
|
} // only lands involved
|
||||||
else if (CardLists.getNotType(opplist, "Land").isEmpty() && CardLists.getNotType(ailist, "Land").isEmpty()) {
|
else if (CardLists.getNotType(opplist, "Land").isEmpty() && CardLists.getNotType(ailist, "Land").isEmpty()) {
|
||||||
if (ai.isCardInPlay("Crucible of Worlds") && !opponent.isCardInPlay("Crucible of Worlds") && !opplist.isEmpty()) {
|
if (ai.isCardInPlay("Crucible of Worlds") && !opponent.isCardInPlay("Crucible of Worlds") && !opplist.isEmpty()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// evaluate the situation with creatures on the battlefield separately, as that's where the AI typically makes mistakes
|
// evaluate the situation with creatures on the battlefield separately, as that's where the AI typically makes mistakes
|
||||||
CardCollection aiCreatures = ai.getCreaturesInPlay();
|
CardCollection aiCreatures = ai.getCreaturesInPlay();
|
||||||
CardCollection oppCreatures = opponent.getCreaturesInPlay();
|
CardCollection oppCreatures = opponent.getCreaturesInPlay();
|
||||||
if (!oppCreatures.isEmpty()) {
|
if (!oppCreatures.isEmpty()) {
|
||||||
if (ComputerUtilCard.evaluateCreatureList(aiCreatures) < ComputerUtilCard.evaluateCreatureList(oppCreatures) + CREATURE_EVAL_THRESHOLD) {
|
if (ComputerUtilCard.evaluateCreatureList(aiCreatures) < ComputerUtilCard.evaluateCreatureList(oppCreatures) + CREATURE_EVAL_THRESHOLD) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// check if the AI would lose more lands than the opponent would
|
// check if the AI would lose more lands than the opponent would
|
||||||
if (ComputerUtilCard.evaluatePermanentList(ailist) > ComputerUtilCard.evaluatePermanentList(opplist) + 1) {
|
if (ComputerUtilCard.evaluatePermanentList(ailist) > ComputerUtilCard.evaluatePermanentList(opplist) + 1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} // otherwise evaluate both lists by CMC and pass only if human permanents are more valuable
|
} // otherwise evaluate both lists by CMC and pass only if human permanents are more valuable
|
||||||
else if ((ComputerUtilCard.evaluatePermanentList(ailist) + 3) >= ComputerUtilCard.evaluatePermanentList(opplist)) {
|
else if ((ComputerUtilCard.evaluatePermanentList(ailist) + 3) >= ComputerUtilCard.evaluatePermanentList(opplist)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,155 +1,155 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import forge.ai.*;
|
import forge.ai.*;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.player.PlayerActionConfirmMode;
|
import forge.game.player.PlayerActionConfirmMode;
|
||||||
import forge.game.spellability.AbilitySub;
|
import forge.game.spellability.AbilitySub;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
import forge.util.TextUtil;
|
import forge.util.TextUtil;
|
||||||
|
|
||||||
|
|
||||||
public class DigAi extends SpellAbilityAi {
|
public class DigAi extends SpellAbilityAi {
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see forge.card.abilityfactory.SpellAiLogic#canPlayAI(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility)
|
* @see forge.card.abilityfactory.SpellAiLogic#canPlayAI(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
Player opp = ComputerUtil.getOpponentFor(ai);
|
Player opp = ComputerUtil.getOpponentFor(ai);
|
||||||
final Card host = sa.getHostCard();
|
final Card host = sa.getHostCard();
|
||||||
Player libraryOwner = ai;
|
Player libraryOwner = ai;
|
||||||
|
|
||||||
if (sa.usesTargeting()) {
|
if (sa.usesTargeting()) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
if (!opp.canBeTargetedBy(sa)) {
|
if (!opp.canBeTargetedBy(sa)) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
sa.getTargets().add(opp);
|
sa.getTargets().add(opp);
|
||||||
}
|
}
|
||||||
libraryOwner = opp;
|
libraryOwner = opp;
|
||||||
}
|
}
|
||||||
|
|
||||||
// return false if nothing to dig into
|
// return false if nothing to dig into
|
||||||
if (libraryOwner.getCardsIn(ZoneType.Library).isEmpty()) {
|
if (libraryOwner.getCardsIn(ZoneType.Library).isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("Never".equals(sa.getParam("AILogic"))) {
|
if ("Never".equals(sa.getParam("AILogic"))) {
|
||||||
return false;
|
return false;
|
||||||
} else if ("AtOppEndOfTurn".equals(sa.getParam("AILogic"))) {
|
} else if ("AtOppEndOfTurn".equals(sa.getParam("AILogic"))) {
|
||||||
return game.getPhaseHandler().getNextTurn() == ai && game.getPhaseHandler().is(PhaseType.END_OF_TURN);
|
return game.getPhaseHandler().getNextTurn() == ai && game.getPhaseHandler().is(PhaseType.END_OF_TURN);
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't deck yourself
|
// don't deck yourself
|
||||||
if (sa.hasParam("DestinationZone2") && !"Library".equals(sa.getParam("DestinationZone2"))) {
|
if (sa.hasParam("DestinationZone2") && !"Library".equals(sa.getParam("DestinationZone2"))) {
|
||||||
int numToDig = AbilityUtils.calculateAmount(host, sa.getParam("DigNum"), sa);
|
int numToDig = AbilityUtils.calculateAmount(host, sa.getParam("DigNum"), sa);
|
||||||
if (libraryOwner == ai && ai.getCardsIn(ZoneType.Library).size() <= numToDig + 2) {
|
if (libraryOwner == ai && ai.getCardsIn(ZoneType.Library).size() <= numToDig + 2) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't use draw abilities before main 2 if possible
|
// Don't use draw abilities before main 2 if possible
|
||||||
if (game.getPhaseHandler().getPhase().isBefore(PhaseType.MAIN2) && !sa.hasParam("ActivationPhases")
|
if (game.getPhaseHandler().getPhase().isBefore(PhaseType.MAIN2) && !sa.hasParam("ActivationPhases")
|
||||||
&& !sa.hasParam("DestinationZone") && !ComputerUtil.castSpellInMain1(ai, sa)) {
|
&& !sa.hasParam("DestinationZone") && !ComputerUtil.castSpellInMain1(ai, sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final String num = sa.getParam("DigNum");
|
final String num = sa.getParam("DigNum");
|
||||||
if (num != null && num.equals("X") && host.getSVar(num).equals("Count$xPaid")) {
|
if (num != null && num.equals("X") && host.getSVar(num).equals("Count$xPaid")) {
|
||||||
// By default, set PayX here to maximum value.
|
// By default, set PayX here to maximum value.
|
||||||
if (!(sa instanceof AbilitySub) || host.getSVar("PayX").equals("")) {
|
if (!(sa instanceof AbilitySub) || host.getSVar("PayX").equals("")) {
|
||||||
int manaToSave = 0;
|
int manaToSave = 0;
|
||||||
|
|
||||||
// Special logic that asks the AI to conserve a certain amount of mana when paying X
|
// Special logic that asks the AI to conserve a certain amount of mana when paying X
|
||||||
if (sa.hasParam("AILogic") && sa.getParam("AILogic").startsWith("PayXButSaveMana")) {
|
if (sa.hasParam("AILogic") && sa.getParam("AILogic").startsWith("PayXButSaveMana")) {
|
||||||
manaToSave = Integer.parseInt(TextUtil.split(sa.getParam("AILogic"), '.')[1]);
|
manaToSave = Integer.parseInt(TextUtil.split(sa.getParam("AILogic"), '.')[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
int numCards = ComputerUtilMana.determineLeftoverMana(sa, ai) - manaToSave;
|
int numCards = ComputerUtilMana.determineLeftoverMana(sa, ai) - manaToSave;
|
||||||
if (numCards <= 0) {
|
if (numCards <= 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
host.setSVar("PayX", Integer.toString(numCards));
|
host.setSVar("PayX", Integer.toString(numCards));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SpellAbilityAi.playReusable(ai, sa)) {
|
if (SpellAbilityAi.playReusable(ai, sa)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((!game.getPhaseHandler().getNextTurn().equals(ai)
|
if ((!game.getPhaseHandler().getNextTurn().equals(ai)
|
||||||
|| game.getPhaseHandler().getPhase().isBefore(PhaseType.END_OF_TURN))
|
|| game.getPhaseHandler().getPhase().isBefore(PhaseType.END_OF_TURN))
|
||||||
&& !sa.hasParam("PlayerTurn") && !SpellAbilityAi.isSorcerySpeed(sa)
|
&& !sa.hasParam("PlayerTurn") && !SpellAbilityAi.isSorcerySpeed(sa)
|
||||||
&& (ai.getCardsIn(ZoneType.Hand).size() > 1 || game.getPhaseHandler().getPhase().isBefore(PhaseType.DRAW))
|
&& (ai.getCardsIn(ZoneType.Hand).size() > 1 || game.getPhaseHandler().getPhase().isBefore(PhaseType.DRAW))
|
||||||
&& !ComputerUtil.activateForCost(sa, ai)) {
|
&& !ComputerUtil.activateForCost(sa, ai)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("MadSarkhanDigDmg".equals(sa.getParam("AILogic"))) {
|
if ("MadSarkhanDigDmg".equals(sa.getParam("AILogic"))) {
|
||||||
return SpecialCardAi.SarkhanTheMad.considerDig(ai, sa);
|
return SpecialCardAi.SarkhanTheMad.considerDig(ai, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
return !ComputerUtil.preventRunAwayActivations(sa);
|
return !ComputerUtil.preventRunAwayActivations(sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
||||||
final Player opp = ComputerUtil.getOpponentFor(ai);
|
final Player opp = ComputerUtil.getOpponentFor(ai);
|
||||||
if (sa.usesTargeting()) {
|
if (sa.usesTargeting()) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
if (mandatory && sa.canTarget(opp)) {
|
if (mandatory && sa.canTarget(opp)) {
|
||||||
sa.getTargets().add(opp);
|
sa.getTargets().add(opp);
|
||||||
} else if (mandatory && sa.canTarget(ai)) {
|
} else if (mandatory && sa.canTarget(ai)) {
|
||||||
sa.getTargets().add(ai);
|
sa.getTargets().add(ai);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Triggers that ask to pay {X} (e.g. Depala, Pilot Exemplar).
|
// Triggers that ask to pay {X} (e.g. Depala, Pilot Exemplar).
|
||||||
if (sa.hasParam("AILogic") && sa.getParam("AILogic").startsWith("PayXButSaveMana")) {
|
if (sa.hasParam("AILogic") && sa.getParam("AILogic").startsWith("PayXButSaveMana")) {
|
||||||
int manaToSave = Integer.parseInt(TextUtil.split(sa.getParam("AILogic"), '.')[1]);
|
int manaToSave = Integer.parseInt(TextUtil.split(sa.getParam("AILogic"), '.')[1]);
|
||||||
int numCards = ComputerUtilMana.determineLeftoverMana(sa, ai) - manaToSave;
|
int numCards = ComputerUtilMana.determineLeftoverMana(sa, ai) - manaToSave;
|
||||||
if (numCards <= 0) {
|
if (numCards <= 0) {
|
||||||
return mandatory;
|
return mandatory;
|
||||||
}
|
}
|
||||||
sa.getHostCard().setSVar("PayX", Integer.toString(numCards));
|
sa.getHostCard().setSVar("PayX", Integer.toString(numCards));
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> valid, boolean isOptional, Player relatedPlayer) {
|
public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> valid, boolean isOptional, Player relatedPlayer) {
|
||||||
Card chosen = ComputerUtilCard.getBestAI(valid);
|
Card chosen = ComputerUtilCard.getBestAI(valid);
|
||||||
if (sa.getActivatingPlayer().isOpponentOf(ai) && relatedPlayer.isOpponentOf(ai)) {
|
if (sa.getActivatingPlayer().isOpponentOf(ai) && relatedPlayer.isOpponentOf(ai)) {
|
||||||
return ComputerUtilCard.getWorstAI(valid);
|
return ComputerUtilCard.getWorstAI(valid);
|
||||||
}
|
}
|
||||||
return chosen;
|
return chosen;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
||||||
Card topc = player.getZone(ZoneType.Library).get(0);
|
Card topc = player.getZone(ZoneType.Library).get(0);
|
||||||
|
|
||||||
// AI actions for individual cards (until this AI can be generalized)
|
// AI actions for individual cards (until this AI can be generalized)
|
||||||
if (sa.getHostCard() != null) {
|
if (sa.getHostCard() != null) {
|
||||||
if (ComputerUtilAbility.getAbilitySourceName(sa).equals("Explorer's Scope")) {
|
if (ComputerUtilAbility.getAbilitySourceName(sa).equals("Explorer's Scope")) {
|
||||||
// for Explorer's Scope, always put a land on the battlefield tapped
|
// for Explorer's Scope, always put a land on the battlefield tapped
|
||||||
// (TODO: might not always be a good idea, e.g. when a land ETBing can have detrimental effects)
|
// (TODO: might not always be a good idea, e.g. when a land ETBing can have detrimental effects)
|
||||||
return true;
|
return true;
|
||||||
} else if ("AlwaysConfirm".equals(sa.getParam("AILogic"))) {
|
} else if ("AlwaysConfirm".equals(sa.getParam("AILogic"))) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// looks like perfect code for Delver of Secrets, but what about other cards?
|
// looks like perfect code for Delver of Secrets, but what about other cards?
|
||||||
return topc.isInstant() || topc.isSorcery();
|
return topc.isInstant() || topc.isSorcery();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,134 +1,134 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import forge.ai.ComputerUtil;
|
import forge.ai.ComputerUtil;
|
||||||
import forge.ai.ComputerUtilMana;
|
import forge.ai.ComputerUtilMana;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
import forge.game.card.CardPredicates;
|
import forge.game.card.CardPredicates;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.player.PlayerActionConfirmMode;
|
import forge.game.player.PlayerActionConfirmMode;
|
||||||
import forge.game.spellability.AbilitySub;
|
import forge.game.spellability.AbilitySub;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
import forge.util.MyRandom;
|
import forge.util.MyRandom;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
public class DigUntilAi extends SpellAbilityAi {
|
public class DigUntilAi extends SpellAbilityAi {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
||||||
Card source = sa.getHostCard();
|
Card source = sa.getHostCard();
|
||||||
final String logic = sa.getParamOrDefault("AILogic", "");
|
final String logic = sa.getParamOrDefault("AILogic", "");
|
||||||
double chance = .4; // 40 percent chance with instant speed stuff
|
double chance = .4; // 40 percent chance with instant speed stuff
|
||||||
if (SpellAbilityAi.isSorcerySpeed(sa)) {
|
if (SpellAbilityAi.isSorcerySpeed(sa)) {
|
||||||
chance = .667; // 66.7% chance for sorcery speed (since it will
|
chance = .667; // 66.7% chance for sorcery speed (since it will
|
||||||
// never activate EOT)
|
// never activate EOT)
|
||||||
}
|
}
|
||||||
final Random r = MyRandom.getRandom();
|
final Random r = MyRandom.getRandom();
|
||||||
final boolean randomReturn = r.nextFloat() <= Math.pow(chance, sa.getActivationsThisTurn() + 1);
|
final boolean randomReturn = r.nextFloat() <= Math.pow(chance, sa.getActivationsThisTurn() + 1);
|
||||||
|
|
||||||
Player libraryOwner = ai;
|
Player libraryOwner = ai;
|
||||||
Player opp = ComputerUtil.getOpponentFor(ai);
|
Player opp = ComputerUtil.getOpponentFor(ai);
|
||||||
|
|
||||||
if ("DontMillSelf".equals(logic)) {
|
if ("DontMillSelf".equals(logic)) {
|
||||||
// A card that digs for specific things and puts everything revealed before it into graveyard
|
// A card that digs for specific things and puts everything revealed before it into graveyard
|
||||||
// (e.g. Hermit Druid) - don't use it to mill itself and also make sure there's enough playable
|
// (e.g. Hermit Druid) - don't use it to mill itself and also make sure there's enough playable
|
||||||
// material in the library after using it several times.
|
// material in the library after using it several times.
|
||||||
// TODO: maybe this should happen for any DigUntil SA with RevealedDestination$ Graveyard?
|
// TODO: maybe this should happen for any DigUntil SA with RevealedDestination$ Graveyard?
|
||||||
if (ai.getCardsIn(ZoneType.Library).size() < 20) {
|
if (ai.getCardsIn(ZoneType.Library).size() < 20) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if ("Land.Basic".equals(sa.getParam("Valid"))
|
if ("Land.Basic".equals(sa.getParam("Valid"))
|
||||||
&& !CardLists.filter(ai.getCardsIn(ZoneType.Hand), CardPredicates.Presets.LANDS_PRODUCING_MANA).isEmpty()) {
|
&& !CardLists.filter(ai.getCardsIn(ZoneType.Hand), CardPredicates.Presets.LANDS_PRODUCING_MANA).isEmpty()) {
|
||||||
// We already have a mana-producing land in hand, so bail
|
// We already have a mana-producing land in hand, so bail
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.usesTargeting()) {
|
if (sa.usesTargeting()) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
if (!sa.canTarget(opp)) {
|
if (!sa.canTarget(opp)) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
sa.getTargets().add(opp);
|
sa.getTargets().add(opp);
|
||||||
}
|
}
|
||||||
libraryOwner = opp;
|
libraryOwner = opp;
|
||||||
} else {
|
} else {
|
||||||
if (sa.hasParam("Valid")) {
|
if (sa.hasParam("Valid")) {
|
||||||
final String valid = sa.getParam("Valid");
|
final String valid = sa.getParam("Valid");
|
||||||
if (CardLists.getValidCards(ai.getCardsIn(ZoneType.Library), valid.split(","), source.getController(), source, sa).isEmpty()) {
|
if (CardLists.getValidCards(ai.getCardsIn(ZoneType.Library), valid.split(","), source.getController(), source, sa).isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final String num = sa.getParam("Amount");
|
final String num = sa.getParam("Amount");
|
||||||
if ((num != null) && num.equals("X") && source.getSVar(num).equals("Count$xPaid")) {
|
if ((num != null) && num.equals("X") && source.getSVar(num).equals("Count$xPaid")) {
|
||||||
// Set PayX here to maximum value.
|
// Set PayX here to maximum value.
|
||||||
if (!(sa instanceof AbilitySub) || source.getSVar("PayX").equals("")) {
|
if (!(sa instanceof AbilitySub) || source.getSVar("PayX").equals("")) {
|
||||||
int numCards = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
int numCards = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
||||||
if (numCards <= 0) {
|
if (numCards <= 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
source.setSVar("PayX", Integer.toString(numCards));
|
source.setSVar("PayX", Integer.toString(numCards));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// return false if nothing to dig into
|
// return false if nothing to dig into
|
||||||
if (libraryOwner.getCardsIn(ZoneType.Library).isEmpty()) {
|
if (libraryOwner.getCardsIn(ZoneType.Library).isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return randomReturn;
|
return randomReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
||||||
|
|
||||||
if (sa.usesTargeting()) {
|
if (sa.usesTargeting()) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
if (sa.isCurse()) {
|
if (sa.isCurse()) {
|
||||||
for (Player opp : ai.getOpponents()) {
|
for (Player opp : ai.getOpponents()) {
|
||||||
if (sa.canTarget(opp)) {
|
if (sa.canTarget(opp)) {
|
||||||
sa.getTargets().add(opp);
|
sa.getTargets().add(opp);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (mandatory && sa.getTargets().isEmpty() && sa.canTarget(ai)) {
|
if (mandatory && sa.getTargets().isEmpty() && sa.canTarget(ai)) {
|
||||||
sa.getTargets().add(ai);
|
sa.getTargets().add(ai);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (sa.canTarget(ai)) {
|
if (sa.canTarget(ai)) {
|
||||||
sa.getTargets().add(ai);
|
sa.getTargets().add(ai);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
||||||
if (sa.hasParam("AILogic")) {
|
if (sa.hasParam("AILogic")) {
|
||||||
final String logic = sa.getParam("AILogic");
|
final String logic = sa.getParam("AILogic");
|
||||||
if ("OathOfDruids".equals(logic)) {
|
if ("OathOfDruids".equals(logic)) {
|
||||||
final List<Card> creaturesInLibrary =
|
final List<Card> creaturesInLibrary =
|
||||||
CardLists.filter(player.getCardsIn(ZoneType.Library), CardPredicates.Presets.CREATURES);
|
CardLists.filter(player.getCardsIn(ZoneType.Library), CardPredicates.Presets.CREATURES);
|
||||||
final List<Card> creaturesInBattlefield =
|
final List<Card> creaturesInBattlefield =
|
||||||
CardLists.filter(player.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.CREATURES);
|
CardLists.filter(player.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.CREATURES);
|
||||||
// if there are at least 3 creatures in library,
|
// if there are at least 3 creatures in library,
|
||||||
// or none in play with one in library, oath
|
// or none in play with one in library, oath
|
||||||
return creaturesInLibrary.size() > 2
|
return creaturesInLibrary.size() > 2
|
||||||
|| (creaturesInBattlefield.size() == 0 && creaturesInLibrary.size() > 0);
|
|| (creaturesInBattlefield.size() == 0 && creaturesInLibrary.size() > 0);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,224 +1,224 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import forge.ai.*;
|
import forge.ai.*;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardCollectionView;
|
import forge.game.card.CardCollectionView;
|
||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
import forge.game.card.CardPredicates;
|
import forge.game.card.CardPredicates;
|
||||||
import forge.game.cost.Cost;
|
import forge.game.cost.Cost;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.player.PlayerActionConfirmMode;
|
import forge.game.player.PlayerActionConfirmMode;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.TargetRestrictions;
|
import forge.game.spellability.TargetRestrictions;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
import forge.util.MyRandom;
|
import forge.util.MyRandom;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
public class DiscardAi extends SpellAbilityAi {
|
public class DiscardAi extends SpellAbilityAi {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
final String sourceName = ComputerUtilAbility.getAbilitySourceName(sa);
|
final String sourceName = ComputerUtilAbility.getAbilitySourceName(sa);
|
||||||
final Cost abCost = sa.getPayCosts();
|
final Cost abCost = sa.getPayCosts();
|
||||||
final String aiLogic = sa.getParamOrDefault("AILogic", "");
|
final String aiLogic = sa.getParamOrDefault("AILogic", "");
|
||||||
|
|
||||||
if (abCost != null) {
|
if (abCost != null) {
|
||||||
// AI currently disabled for these costs
|
// AI currently disabled for these costs
|
||||||
if (!ComputerUtilCost.checkSacrificeCost(ai, abCost, source, sa)) {
|
if (!ComputerUtilCost.checkSacrificeCost(ai, abCost, source, sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ComputerUtilCost.checkLifeCost(ai, abCost, source, 4, sa)) {
|
if (!ComputerUtilCost.checkLifeCost(ai, abCost, source, 4, sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ComputerUtilCost.checkDiscardCost(ai, abCost, source)) {
|
if (!ComputerUtilCost.checkDiscardCost(ai, abCost, source)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ComputerUtilCost.checkRemoveCounterCost(abCost, source)) {
|
if (!ComputerUtilCost.checkRemoveCounterCost(abCost, source)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("Chandra, Flamecaller".equals(sourceName)) {
|
if ("Chandra, Flamecaller".equals(sourceName)) {
|
||||||
final int hand = ai.getCardsIn(ZoneType.Hand).size();
|
final int hand = ai.getCardsIn(ZoneType.Hand).size();
|
||||||
return MyRandom.getRandom().nextFloat() < (1.0 / (1 + hand));
|
return MyRandom.getRandom().nextFloat() < (1.0 / (1 + hand));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aiLogic.equals("VolrathsShapeshifter")) {
|
if (aiLogic.equals("VolrathsShapeshifter")) {
|
||||||
return SpecialCardAi.VolrathsShapeshifter.consider(ai, sa);
|
return SpecialCardAi.VolrathsShapeshifter.consider(ai, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
final boolean humanHasHand = ComputerUtil.getOpponentFor(ai).getCardsIn(ZoneType.Hand).size() > 0;
|
final boolean humanHasHand = ComputerUtil.getOpponentFor(ai).getCardsIn(ZoneType.Hand).size() > 0;
|
||||||
|
|
||||||
if (tgt != null) {
|
if (tgt != null) {
|
||||||
if (!discardTargetAI(ai, sa)) {
|
if (!discardTargetAI(ai, sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// TODO: Add appropriate restrictions
|
// TODO: Add appropriate restrictions
|
||||||
final List<Player> players = AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("Defined"), sa);
|
final List<Player> players = AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("Defined"), sa);
|
||||||
|
|
||||||
if (players.size() == 1) {
|
if (players.size() == 1) {
|
||||||
if (players.get(0) == ai) {
|
if (players.get(0) == ai) {
|
||||||
// the ai should only be using something like this if he has
|
// the ai should only be using something like this if he has
|
||||||
// few cards in hand,
|
// few cards in hand,
|
||||||
// cards like this better have a good drawback to be in the
|
// cards like this better have a good drawback to be in the
|
||||||
// AIs deck
|
// AIs deck
|
||||||
} else {
|
} else {
|
||||||
// defined to the human, so that's fine as long the human
|
// defined to the human, so that's fine as long the human
|
||||||
// has cards
|
// has cards
|
||||||
if (!humanHasHand) {
|
if (!humanHasHand) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Both players discard, any restrictions?
|
// Both players discard, any restrictions?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.hasParam("NumCards")) {
|
if (sa.hasParam("NumCards")) {
|
||||||
if (sa.getParam("NumCards").equals("X") && source.getSVar("X").equals("Count$xPaid")) {
|
if (sa.getParam("NumCards").equals("X") && source.getSVar("X").equals("Count$xPaid")) {
|
||||||
// Set PayX here to maximum value.
|
// Set PayX here to maximum value.
|
||||||
final int cardsToDiscard = Math.min(ComputerUtilMana.determineLeftoverMana(sa, ai), ComputerUtil.getOpponentFor(ai)
|
final int cardsToDiscard = Math.min(ComputerUtilMana.determineLeftoverMana(sa, ai), ComputerUtil.getOpponentFor(ai)
|
||||||
.getCardsIn(ZoneType.Hand).size());
|
.getCardsIn(ZoneType.Hand).size());
|
||||||
if (cardsToDiscard < 1) {
|
if (cardsToDiscard < 1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
source.setSVar("PayX", Integer.toString(cardsToDiscard));
|
source.setSVar("PayX", Integer.toString(cardsToDiscard));
|
||||||
} else {
|
} else {
|
||||||
if (AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("NumCards"), sa) < 1) {
|
if (AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("NumCards"), sa) < 1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Improve support for Discard AI for cards with AnyNumber set to true.
|
// TODO: Improve support for Discard AI for cards with AnyNumber set to true.
|
||||||
if (sa.hasParam("AnyNumber")) {
|
if (sa.hasParam("AnyNumber")) {
|
||||||
if ("DiscardUncastableAndExcess".equals(aiLogic)) {
|
if ("DiscardUncastableAndExcess".equals(aiLogic)) {
|
||||||
final CardCollectionView inHand = ai.getCardsIn(ZoneType.Hand);
|
final CardCollectionView inHand = ai.getCardsIn(ZoneType.Hand);
|
||||||
final int numLandsOTB = CardLists.filter(ai.getCardsIn(ZoneType.Hand), CardPredicates.Presets.LANDS).size();
|
final int numLandsOTB = CardLists.filter(ai.getCardsIn(ZoneType.Hand), CardPredicates.Presets.LANDS).size();
|
||||||
int numDiscard = 0;
|
int numDiscard = 0;
|
||||||
int numOppInHand = 0;
|
int numOppInHand = 0;
|
||||||
for (Player p : ai.getGame().getPlayers()) {
|
for (Player p : ai.getGame().getPlayers()) {
|
||||||
if (p.getCardsIn(ZoneType.Hand).size() > numOppInHand) {
|
if (p.getCardsIn(ZoneType.Hand).size() > numOppInHand) {
|
||||||
numOppInHand = p.getCardsIn(ZoneType.Hand).size();
|
numOppInHand = p.getCardsIn(ZoneType.Hand).size();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (Card c : inHand) {
|
for (Card c : inHand) {
|
||||||
if (c.equals(sa.getHostCard())) { continue; }
|
if (c.equals(sa.getHostCard())) { continue; }
|
||||||
if (c.hasSVar("DoNotDiscardIfAble") || c.hasSVar("IsReanimatorCard")) { continue; }
|
if (c.hasSVar("DoNotDiscardIfAble") || c.hasSVar("IsReanimatorCard")) { continue; }
|
||||||
if (c.isCreature() && !ComputerUtilMana.hasEnoughManaSourcesToCast(c.getSpellPermanent(), ai)) {
|
if (c.isCreature() && !ComputerUtilMana.hasEnoughManaSourcesToCast(c.getSpellPermanent(), ai)) {
|
||||||
numDiscard++;
|
numDiscard++;
|
||||||
}
|
}
|
||||||
if ((c.isLand() && numLandsOTB >= 5) || (c.getFirstSpellAbility() != null && !ComputerUtilMana.hasEnoughManaSourcesToCast(c.getFirstSpellAbility(), ai))) {
|
if ((c.isLand() && numLandsOTB >= 5) || (c.getFirstSpellAbility() != null && !ComputerUtilMana.hasEnoughManaSourcesToCast(c.getFirstSpellAbility(), ai))) {
|
||||||
if (numDiscard + 1 <= numOppInHand) {
|
if (numDiscard + 1 <= numOppInHand) {
|
||||||
numDiscard++;
|
numDiscard++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (numDiscard == 0) {
|
if (numDiscard == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't use draw abilities before main 2 if possible
|
// Don't use draw abilities before main 2 if possible
|
||||||
if (ai.getGame().getPhaseHandler().getPhase().isBefore(PhaseType.MAIN2)
|
if (ai.getGame().getPhaseHandler().getPhase().isBefore(PhaseType.MAIN2)
|
||||||
&& !sa.hasParam("ActivationPhases")) {
|
&& !sa.hasParam("ActivationPhases")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't tap creatures that may be able to block
|
// Don't tap creatures that may be able to block
|
||||||
if (ComputerUtil.waitForBlocking(sa)) {
|
if (ComputerUtil.waitForBlocking(sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Random r = MyRandom.getRandom();
|
final Random r = MyRandom.getRandom();
|
||||||
boolean randomReturn = r.nextFloat() <= Math.pow(0.9, sa.getActivationsThisTurn());
|
boolean randomReturn = r.nextFloat() <= Math.pow(0.9, sa.getActivationsThisTurn());
|
||||||
|
|
||||||
// some other variables here, like handsize vs. maxHandSize
|
// some other variables here, like handsize vs. maxHandSize
|
||||||
|
|
||||||
return randomReturn;
|
return randomReturn;
|
||||||
} // discardCanPlayAI()
|
} // discardCanPlayAI()
|
||||||
|
|
||||||
private boolean discardTargetAI(final Player ai, final SpellAbility sa) {
|
private boolean discardTargetAI(final Player ai, final SpellAbility sa) {
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
Player opp = ComputerUtil.getOpponentFor(ai);
|
Player opp = ComputerUtil.getOpponentFor(ai);
|
||||||
if (opp.getCardsIn(ZoneType.Hand).isEmpty() && !ComputerUtil.activateForCost(sa, ai)) {
|
if (opp.getCardsIn(ZoneType.Hand).isEmpty() && !ComputerUtil.activateForCost(sa, ai)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (tgt != null) {
|
if (tgt != null) {
|
||||||
if (sa.canTarget(opp)) {
|
if (sa.canTarget(opp)) {
|
||||||
sa.getTargets().add(opp);
|
sa.getTargets().add(opp);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
} // discardTargetAI()
|
} // discardTargetAI()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
if (tgt != null) {
|
if (tgt != null) {
|
||||||
Player opp = ComputerUtil.getOpponentFor(ai);
|
Player opp = ComputerUtil.getOpponentFor(ai);
|
||||||
if (!discardTargetAI(ai, sa)) {
|
if (!discardTargetAI(ai, sa)) {
|
||||||
if (mandatory && sa.canTarget(opp)) {
|
if (mandatory && sa.canTarget(opp)) {
|
||||||
sa.getTargets().add(opp);
|
sa.getTargets().add(opp);
|
||||||
} else if (mandatory && sa.canTarget(ai)) {
|
} else if (mandatory && sa.canTarget(ai)) {
|
||||||
sa.getTargets().add(ai);
|
sa.getTargets().add(ai);
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (sa.hasParam("AILogic")) {
|
if (sa.hasParam("AILogic")) {
|
||||||
if ("AtLeast2".equals(sa.getParam("AILogic"))) {
|
if ("AtLeast2".equals(sa.getParam("AILogic"))) {
|
||||||
final List<Player> players = AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("Defined"), sa);
|
final List<Player> players = AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("Defined"), sa);
|
||||||
if (players.isEmpty() || players.get(0).getCardsIn(ZoneType.Hand).size() < 2) {
|
if (players.isEmpty() || players.get(0).getCardsIn(ZoneType.Hand).size() < 2) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ("X".equals(sa.getParam("RevealNumber")) && sa.getHostCard().getSVar("X").equals("Count$xPaid")) {
|
if ("X".equals(sa.getParam("RevealNumber")) && sa.getHostCard().getSVar("X").equals("Count$xPaid")) {
|
||||||
// Set PayX here to maximum value.
|
// Set PayX here to maximum value.
|
||||||
final int cardsToDiscard = Math.min(ComputerUtilMana.determineLeftoverMana(sa, ai), ComputerUtil.getOpponentFor(ai)
|
final int cardsToDiscard = Math.min(ComputerUtilMana.determineLeftoverMana(sa, ai), ComputerUtil.getOpponentFor(ai)
|
||||||
.getCardsIn(ZoneType.Hand).size());
|
.getCardsIn(ZoneType.Hand).size());
|
||||||
sa.getHostCard().setSVar("PayX", Integer.toString(cardsToDiscard));
|
sa.getHostCard().setSVar("PayX", Integer.toString(cardsToDiscard));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} // discardTrigger()
|
} // discardTrigger()
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
||||||
// Drawback AI improvements
|
// Drawback AI improvements
|
||||||
// if parent draws cards, make sure cards in hand + cards drawn > 0
|
// if parent draws cards, make sure cards in hand + cards drawn > 0
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
if (tgt != null) {
|
if (tgt != null) {
|
||||||
return discardTargetAI(ai, sa);
|
return discardTargetAI(ai, sa);
|
||||||
}
|
}
|
||||||
// TODO: check for some extra things
|
// TODO: check for some extra things
|
||||||
return true;
|
return true;
|
||||||
} // discardCheckDrawbackAI()
|
} // discardCheckDrawbackAI()
|
||||||
|
|
||||||
|
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
||||||
if ( mode == PlayerActionConfirmMode.Random ) { //
|
if ( mode == PlayerActionConfirmMode.Random ) { //
|
||||||
// TODO For now AI will always discard Random used currently with: Balduvian Horde and similar cards
|
// TODO For now AI will always discard Random used currently with: Balduvian Horde and similar cards
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return super.confirmAction(player, sa, mode, message);
|
return super.confirmAction(player, sa, mode, message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,92 +1,92 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import forge.ai.ComputerUtil;
|
import forge.ai.ComputerUtil;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.TargetRestrictions;
|
import forge.game.spellability.TargetRestrictions;
|
||||||
import forge.util.MyRandom;
|
import forge.util.MyRandom;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
public class DrainManaAi extends SpellAbilityAi {
|
public class DrainManaAi extends SpellAbilityAi {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
||||||
// AI cannot use this properly until he can use SAs during Humans turn
|
// AI cannot use this properly until he can use SAs during Humans turn
|
||||||
|
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
final Player opp = ComputerUtil.getOpponentFor(ai);
|
final Player opp = ComputerUtil.getOpponentFor(ai);
|
||||||
final Random r = MyRandom.getRandom();
|
final Random r = MyRandom.getRandom();
|
||||||
boolean randomReturn = r.nextFloat() <= Math.pow(.6667, sa.getActivationsThisTurn());
|
boolean randomReturn = r.nextFloat() <= Math.pow(.6667, sa.getActivationsThisTurn());
|
||||||
|
|
||||||
if (tgt == null) {
|
if (tgt == null) {
|
||||||
// assume we are looking to tap human's stuff
|
// assume we are looking to tap human's stuff
|
||||||
// TODO - check for things with untap abilities, and don't tap
|
// TODO - check for things with untap abilities, and don't tap
|
||||||
// those.
|
// those.
|
||||||
final List<Player> defined = AbilityUtils.getDefinedPlayers(source, sa.getParam("Defined"), sa);
|
final List<Player> defined = AbilityUtils.getDefinedPlayers(source, sa.getParam("Defined"), sa);
|
||||||
|
|
||||||
if (!defined.contains(opp)) {
|
if (!defined.contains(opp)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
sa.getTargets().add(opp);
|
sa.getTargets().add(opp);
|
||||||
}
|
}
|
||||||
|
|
||||||
return randomReturn;
|
return randomReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
||||||
final Player opp = ComputerUtil.getOpponentFor(ai);
|
final Player opp = ComputerUtil.getOpponentFor(ai);
|
||||||
|
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
|
|
||||||
if (null == tgt) {
|
if (null == tgt) {
|
||||||
if (mandatory) {
|
if (mandatory) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
final List<Player> defined = AbilityUtils.getDefinedPlayers(source, sa.getParam("Defined"), sa);
|
final List<Player> defined = AbilityUtils.getDefinedPlayers(source, sa.getParam("Defined"), sa);
|
||||||
|
|
||||||
if (!defined.contains(opp)) {
|
if (!defined.contains(opp)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
sa.getTargets().add(opp);
|
sa.getTargets().add(opp);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
||||||
// AI cannot use this properly until he can use SAs during Humans turn
|
// AI cannot use this properly until he can use SAs during Humans turn
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
|
|
||||||
boolean randomReturn = true;
|
boolean randomReturn = true;
|
||||||
|
|
||||||
if (tgt == null) {
|
if (tgt == null) {
|
||||||
final List<Player> defined = AbilityUtils.getDefinedPlayers(source, sa.getParam("Defined"), sa);
|
final List<Player> defined = AbilityUtils.getDefinedPlayers(source, sa.getParam("Defined"), sa);
|
||||||
|
|
||||||
if (defined.contains(ai)) {
|
if (defined.contains(ai)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
sa.getTargets().add(ComputerUtil.getOpponentFor(ai));
|
sa.getTargets().add(ComputerUtil.getOpponentFor(ai));
|
||||||
}
|
}
|
||||||
|
|
||||||
return randomReturn;
|
return randomReturn;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,334 +1,334 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.base.Predicates;
|
import com.google.common.base.Predicates;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
|
|
||||||
import forge.ai.ComputerUtil;
|
import forge.ai.ComputerUtil;
|
||||||
import forge.ai.ComputerUtilCard;
|
import forge.ai.ComputerUtilCard;
|
||||||
import forge.ai.ComputerUtilCombat;
|
import forge.ai.ComputerUtilCombat;
|
||||||
import forge.ai.SpecialCardAi;
|
import forge.ai.SpecialCardAi;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.ai.SpellApiToAi;
|
import forge.ai.SpellApiToAi;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.GlobalRuleChange;
|
import forge.game.GlobalRuleChange;
|
||||||
import forge.game.ability.ApiType;
|
import forge.game.ability.ApiType;
|
||||||
import forge.game.card.*;
|
import forge.game.card.*;
|
||||||
import forge.game.combat.CombatUtil;
|
import forge.game.combat.CombatUtil;
|
||||||
import forge.game.phase.PhaseHandler;
|
import forge.game.phase.PhaseHandler;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.SpellAbilityStackInstance;
|
import forge.game.spellability.SpellAbilityStackInstance;
|
||||||
import forge.game.spellability.TargetRestrictions;
|
import forge.game.spellability.TargetRestrictions;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
import forge.util.MyRandom;
|
import forge.util.MyRandom;
|
||||||
|
|
||||||
public class EffectAi extends SpellAbilityAi {
|
public class EffectAi extends SpellAbilityAi {
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(final Player ai,final SpellAbility sa) {
|
protected boolean canPlayAI(final Player ai,final SpellAbility sa) {
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
final Random r = MyRandom.getRandom();
|
final Random r = MyRandom.getRandom();
|
||||||
boolean randomReturn = r.nextFloat() <= .6667;
|
boolean randomReturn = r.nextFloat() <= .6667;
|
||||||
String logic = "";
|
String logic = "";
|
||||||
|
|
||||||
if (sa.hasParam("AILogic")) {
|
if (sa.hasParam("AILogic")) {
|
||||||
logic = sa.getParam("AILogic");
|
logic = sa.getParam("AILogic");
|
||||||
final PhaseHandler phase = game.getPhaseHandler();
|
final PhaseHandler phase = game.getPhaseHandler();
|
||||||
if (logic.equals("BeginningOfOppTurn")) {
|
if (logic.equals("BeginningOfOppTurn")) {
|
||||||
if (!phase.getPlayerTurn().isOpponentOf(ai) || phase.getPhase().isAfter(PhaseType.DRAW)) {
|
if (!phase.getPlayerTurn().isOpponentOf(ai) || phase.getPhase().isAfter(PhaseType.DRAW)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
randomReturn = true;
|
randomReturn = true;
|
||||||
} else if (logic.equals("EndOfOppTurn")) {
|
} else if (logic.equals("EndOfOppTurn")) {
|
||||||
if (!phase.getPlayerTurn().isOpponentOf(ai) || phase.getPhase().isBefore(PhaseType.END_OF_TURN)) {
|
if (!phase.getPlayerTurn().isOpponentOf(ai) || phase.getPhase().isBefore(PhaseType.END_OF_TURN)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
randomReturn = true;
|
randomReturn = true;
|
||||||
} else if (logic.equals("KeepOppCreatsLandsTapped")) {
|
} else if (logic.equals("KeepOppCreatsLandsTapped")) {
|
||||||
for (Player opp : ai.getOpponents()) {
|
for (Player opp : ai.getOpponents()) {
|
||||||
boolean worthHolding = false;
|
boolean worthHolding = false;
|
||||||
CardCollectionView oppCreatsLands = CardLists.filter(opp.getCardsIn(ZoneType.Battlefield),
|
CardCollectionView oppCreatsLands = CardLists.filter(opp.getCardsIn(ZoneType.Battlefield),
|
||||||
Predicates.or(CardPredicates.Presets.LANDS, CardPredicates.Presets.CREATURES));
|
Predicates.or(CardPredicates.Presets.LANDS, CardPredicates.Presets.CREATURES));
|
||||||
CardCollectionView oppCreatsLandsTapped = CardLists.filter(oppCreatsLands, CardPredicates.Presets.TAPPED);
|
CardCollectionView oppCreatsLandsTapped = CardLists.filter(oppCreatsLands, CardPredicates.Presets.TAPPED);
|
||||||
|
|
||||||
if (oppCreatsLandsTapped.size() >= 3 || oppCreatsLands.size() == oppCreatsLandsTapped.size()) {
|
if (oppCreatsLandsTapped.size() >= 3 || oppCreatsLands.size() == oppCreatsLandsTapped.size()) {
|
||||||
worthHolding = true;
|
worthHolding = true;
|
||||||
}
|
}
|
||||||
if (!worthHolding) {
|
if (!worthHolding) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
randomReturn = true;
|
randomReturn = true;
|
||||||
}
|
}
|
||||||
} else if (logic.equals("Fog")) {
|
} else if (logic.equals("Fog")) {
|
||||||
if (game.getPhaseHandler().isPlayerTurn(sa.getActivatingPlayer())) {
|
if (game.getPhaseHandler().isPlayerTurn(sa.getActivatingPlayer())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!game.getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
|
if (!game.getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!game.getStack().isEmpty()) {
|
if (!game.getStack().isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (game.getPhaseHandler().isPreventCombatDamageThisTurn()) {
|
if (game.getPhaseHandler().isPreventCombatDamageThisTurn()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!ComputerUtilCombat.lifeInDanger(ai, game.getCombat())) {
|
if (!ComputerUtilCombat.lifeInDanger(ai, game.getCombat())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
if (tgt != null) {
|
if (tgt != null) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
if (tgt.canOnlyTgtOpponent()) {
|
if (tgt.canOnlyTgtOpponent()) {
|
||||||
boolean canTgt = false;
|
boolean canTgt = false;
|
||||||
|
|
||||||
for (Player opp2 : ai.getOpponents()) {
|
for (Player opp2 : ai.getOpponents()) {
|
||||||
if (sa.canTarget(opp2)) {
|
if (sa.canTarget(opp2)) {
|
||||||
sa.getTargets().add(opp2);
|
sa.getTargets().add(opp2);
|
||||||
canTgt = true;
|
canTgt = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!canTgt) {
|
if (!canTgt) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
List<Card> list = game.getCombat().getAttackers();
|
List<Card> list = game.getCombat().getAttackers();
|
||||||
list = CardLists.getValidCards(list, tgt.getValidTgts(), sa.getActivatingPlayer(), sa.getHostCard(), sa);
|
list = CardLists.getValidCards(list, tgt.getValidTgts(), sa.getActivatingPlayer(), sa.getHostCard(), sa);
|
||||||
list = CardLists.getTargetableCards(list, sa);
|
list = CardLists.getTargetableCards(list, sa);
|
||||||
Card target = ComputerUtilCard.getBestCreatureAI(list);
|
Card target = ComputerUtilCard.getBestCreatureAI(list);
|
||||||
if (target == null) {
|
if (target == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
sa.getTargets().add(target);
|
sa.getTargets().add(target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
randomReturn = true;
|
randomReturn = true;
|
||||||
} else if (logic.equals("ChainVeil")) {
|
} else if (logic.equals("ChainVeil")) {
|
||||||
if (!phase.isPlayerTurn(ai) || !phase.getPhase().equals(PhaseType.MAIN2)
|
if (!phase.isPlayerTurn(ai) || !phase.getPhase().equals(PhaseType.MAIN2)
|
||||||
|| CardLists.getType(ai.getCardsIn(ZoneType.Battlefield), "Planeswalker").isEmpty()) {
|
|| CardLists.getType(ai.getCardsIn(ZoneType.Battlefield), "Planeswalker").isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
randomReturn = true;
|
randomReturn = true;
|
||||||
} else if (logic.equals("SpellCopy")) {
|
} else if (logic.equals("SpellCopy")) {
|
||||||
// fetch Instant or Sorcery and AI has reason to play this turn
|
// fetch Instant or Sorcery and AI has reason to play this turn
|
||||||
// does not try to get itself
|
// does not try to get itself
|
||||||
final int count = CardLists.count(ai.getCardsIn(ZoneType.Hand), new Predicate<Card>() {
|
final int count = CardLists.count(ai.getCardsIn(ZoneType.Hand), new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(final Card c) {
|
public boolean apply(final Card c) {
|
||||||
return (c.isInstant() || c.isSorcery()) && c != sa.getHostCard() && ComputerUtil.hasReasonToPlayCardThisTurn(ai, c);
|
return (c.isInstant() || c.isSorcery()) && c != sa.getHostCard() && ComputerUtil.hasReasonToPlayCardThisTurn(ai, c);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if(count == 0) {
|
if(count == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
randomReturn = true;
|
randomReturn = true;
|
||||||
} else if (logic.equals("NarsetRebound")) {
|
} else if (logic.equals("NarsetRebound")) {
|
||||||
// should be done in Main2, but it might broke for other cards
|
// should be done in Main2, but it might broke for other cards
|
||||||
//if (phase.getPhase().isBefore(PhaseType.MAIN2)) {
|
//if (phase.getPhase().isBefore(PhaseType.MAIN2)) {
|
||||||
// return false;
|
// return false;
|
||||||
//}
|
//}
|
||||||
|
|
||||||
// fetch Instant or Sorcery without Rebound and AI has reason to play this turn
|
// fetch Instant or Sorcery without Rebound and AI has reason to play this turn
|
||||||
// only need count, not the list
|
// only need count, not the list
|
||||||
final int count = CardLists.count(ai.getCardsIn(ZoneType.Hand), new Predicate<Card>() {
|
final int count = CardLists.count(ai.getCardsIn(ZoneType.Hand), new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(final Card c) {
|
public boolean apply(final Card c) {
|
||||||
return (c.isInstant() || c.isSorcery()) && !c.hasKeyword("Rebound") && ComputerUtil.hasReasonToPlayCardThisTurn(ai, c);
|
return (c.isInstant() || c.isSorcery()) && !c.hasKeyword("Rebound") && ComputerUtil.hasReasonToPlayCardThisTurn(ai, c);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if(count == 0) {
|
if(count == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
randomReturn = true;
|
randomReturn = true;
|
||||||
} else if (logic.equals("Always")) {
|
} else if (logic.equals("Always")) {
|
||||||
randomReturn = true;
|
randomReturn = true;
|
||||||
} else if (logic.equals("Main2")) {
|
} else if (logic.equals("Main2")) {
|
||||||
if (phase.getPhase().isBefore(PhaseType.MAIN2)) {
|
if (phase.getPhase().isBefore(PhaseType.MAIN2)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
randomReturn = true;
|
randomReturn = true;
|
||||||
} else if (logic.equals("Evasion")) {
|
} else if (logic.equals("Evasion")) {
|
||||||
|
|
||||||
if (!phase.isPlayerTurn(ai)) {
|
if (!phase.isPlayerTurn(ai)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean shouldPlay = false;
|
boolean shouldPlay = false;
|
||||||
|
|
||||||
List<Card> comp = ai.getCreaturesInPlay();
|
List<Card> comp = ai.getCreaturesInPlay();
|
||||||
|
|
||||||
for (final Player opp : ai.getOpponents()) {
|
for (final Player opp : ai.getOpponents()) {
|
||||||
List<Card> human = opp.getCreaturesInPlay();
|
List<Card> human = opp.getCreaturesInPlay();
|
||||||
|
|
||||||
// only count creatures that can attack or block
|
// only count creatures that can attack or block
|
||||||
comp = CardLists.filter(comp, new Predicate<Card>() {
|
comp = CardLists.filter(comp, new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(final Card c) {
|
public boolean apply(final Card c) {
|
||||||
return CombatUtil.canAttack(c, opp);
|
return CombatUtil.canAttack(c, opp);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (comp.size() < 2) {
|
if (comp.size() < 2) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
final List<Card> attackers = comp;
|
final List<Card> attackers = comp;
|
||||||
human = CardLists.filter(human, new Predicate<Card>() {
|
human = CardLists.filter(human, new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(final Card c) {
|
public boolean apply(final Card c) {
|
||||||
return CombatUtil.canBlockAtLeastOne(c, attackers);
|
return CombatUtil.canBlockAtLeastOne(c, attackers);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (human.isEmpty()) {
|
if (human.isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldPlay = true;
|
shouldPlay = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return shouldPlay;
|
return shouldPlay;
|
||||||
} else if (logic.equals("RedirectSpellDamageFromPlayer")) {
|
} else if (logic.equals("RedirectSpellDamageFromPlayer")) {
|
||||||
if (game.getStack().isEmpty()) {
|
if (game.getStack().isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
boolean threatened = false;
|
boolean threatened = false;
|
||||||
for (final SpellAbilityStackInstance stackInst : game.getStack()) {
|
for (final SpellAbilityStackInstance stackInst : game.getStack()) {
|
||||||
if (!stackInst.isSpell()) { continue; }
|
if (!stackInst.isSpell()) { continue; }
|
||||||
SpellAbility stackSpellAbility = stackInst.getSpellAbility(true);
|
SpellAbility stackSpellAbility = stackInst.getSpellAbility(true);
|
||||||
if (stackSpellAbility.getApi() == ApiType.DealDamage) {
|
if (stackSpellAbility.getApi() == ApiType.DealDamage) {
|
||||||
final SpellAbility saTargeting = stackSpellAbility.getSATargetingPlayer();
|
final SpellAbility saTargeting = stackSpellAbility.getSATargetingPlayer();
|
||||||
if (saTargeting != null && Iterables.contains(saTargeting.getTargets().getTargetPlayers(), ai)) {
|
if (saTargeting != null && Iterables.contains(saTargeting.getTargets().getTargetPlayers(), ai)) {
|
||||||
threatened = true;
|
threatened = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
randomReturn = threatened;
|
randomReturn = threatened;
|
||||||
} else if (logic.equals("Prevent")) { // prevent burn spell from opponent
|
} else if (logic.equals("Prevent")) { // prevent burn spell from opponent
|
||||||
if (game.getStack().isEmpty()) {
|
if (game.getStack().isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
final SpellAbility saTop = game.getStack().peekAbility();
|
final SpellAbility saTop = game.getStack().peekAbility();
|
||||||
final Card host = saTop.getHostCard();
|
final Card host = saTop.getHostCard();
|
||||||
if (saTop.getActivatingPlayer() != ai // from opponent
|
if (saTop.getActivatingPlayer() != ai // from opponent
|
||||||
&& !game.getStaticEffects().getGlobalRuleChange(GlobalRuleChange.noPrevention) // no prevent damage
|
&& !game.getStaticEffects().getGlobalRuleChange(GlobalRuleChange.noPrevention) // no prevent damage
|
||||||
&& host != null && (host.isInstant() || host.isSorcery())
|
&& host != null && (host.isInstant() || host.isSorcery())
|
||||||
&& !host.hasKeyword("Prevent all damage that would be dealt by CARDNAME.")) { // valid target
|
&& !host.hasKeyword("Prevent all damage that would be dealt by CARDNAME.")) { // valid target
|
||||||
final ApiType type = saTop.getApi();
|
final ApiType type = saTop.getApi();
|
||||||
if (type == ApiType.DealDamage || type == ApiType.DamageAll) { // burn spell
|
if (type == ApiType.DealDamage || type == ApiType.DamageAll) { // burn spell
|
||||||
sa.getTargets().add(host);
|
sa.getTargets().add(host);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
} else if (logic.equals("NoGain")) {
|
} else if (logic.equals("NoGain")) {
|
||||||
// basic logic to cancel GainLife on stack
|
// basic logic to cancel GainLife on stack
|
||||||
if (game.getStack().isEmpty()) {
|
if (game.getStack().isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
final SpellAbility topStack = game.getStack().peekAbility();
|
final SpellAbility topStack = game.getStack().peekAbility();
|
||||||
if (topStack.getActivatingPlayer().isOpponentOf(ai) && topStack.getApi() == ApiType.GainLife) {
|
if (topStack.getActivatingPlayer().isOpponentOf(ai) && topStack.getApi() == ApiType.GainLife) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (logic.equals("Fight")) {
|
} else if (logic.equals("Fight")) {
|
||||||
return FightAi.canFightAi(ai, sa, 0, 0);
|
return FightAi.canFightAi(ai, sa, 0, 0);
|
||||||
} else if (logic.equals("Burn")) {
|
} else if (logic.equals("Burn")) {
|
||||||
// for DamageDeal sub-abilities (eg. Wild Slash, Skullcrack)
|
// for DamageDeal sub-abilities (eg. Wild Slash, Skullcrack)
|
||||||
SpellAbility burn = sa.getSubAbility();
|
SpellAbility burn = sa.getSubAbility();
|
||||||
return SpellApiToAi.Converter.get(burn.getApi()).canPlayAIWithSubs(ai, burn);
|
return SpellApiToAi.Converter.get(burn.getApi()).canPlayAIWithSubs(ai, burn);
|
||||||
} else if (logic.equals("YawgmothsWill")) {
|
} else if (logic.equals("YawgmothsWill")) {
|
||||||
return SpecialCardAi.YawgmothsWill.consider(ai, sa);
|
return SpecialCardAi.YawgmothsWill.consider(ai, sa);
|
||||||
} else if (logic.startsWith("NeedCreatures")) {
|
} else if (logic.startsWith("NeedCreatures")) {
|
||||||
if (ai.getCreaturesInPlay().isEmpty()) {
|
if (ai.getCreaturesInPlay().isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (logic.contains(":")) {
|
if (logic.contains(":")) {
|
||||||
String k[] = logic.split(":");
|
String k[] = logic.split(":");
|
||||||
Integer i = Integer.valueOf(k[1]);
|
Integer i = Integer.valueOf(k[1]);
|
||||||
if (ai.getCreaturesInPlay().size() < i) {
|
if (ai.getCreaturesInPlay().size() < i) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} else if (logic.equals("CastFromGraveThisTurn")) {
|
} else if (logic.equals("CastFromGraveThisTurn")) {
|
||||||
CardCollection list = new CardCollection(game.getCardsIn(ZoneType.Graveyard));
|
CardCollection list = new CardCollection(game.getCardsIn(ZoneType.Graveyard));
|
||||||
list = CardLists.getValidCards(list, sa.getTargetRestrictions().getValidTgts(), ai, sa.getHostCard(), sa);
|
list = CardLists.getValidCards(list, sa.getTargetRestrictions().getValidTgts(), ai, sa.getHostCard(), sa);
|
||||||
if (!ComputerUtil.targetPlayableSpellCard(ai, list, sa, false)) {
|
if (!ComputerUtil.targetPlayableSpellCard(ai, list, sa, false)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else { //no AILogic
|
} else { //no AILogic
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("False".equals(sa.getParam("Stackable"))) {
|
if ("False".equals(sa.getParam("Stackable"))) {
|
||||||
String name = sa.getParam("Name");
|
String name = sa.getParam("Name");
|
||||||
if (name == null) {
|
if (name == null) {
|
||||||
name = sa.getHostCard().getName() + "'s Effect";
|
name = sa.getHostCard().getName() + "'s Effect";
|
||||||
}
|
}
|
||||||
if (sa.getActivatingPlayer().isCardInCommand(name)) {
|
if (sa.getActivatingPlayer().isCardInCommand(name)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
if (tgt != null && tgt.canTgtPlayer()) {
|
if (tgt != null && tgt.canTgtPlayer()) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
if (tgt.canOnlyTgtOpponent() || logic.equals("BeginningOfOppTurn")) {
|
if (tgt.canOnlyTgtOpponent() || logic.equals("BeginningOfOppTurn")) {
|
||||||
boolean canTgt = false;
|
boolean canTgt = false;
|
||||||
for (Player opp : ai.getOpponents()) {
|
for (Player opp : ai.getOpponents()) {
|
||||||
if (sa.canTarget(opp)) {
|
if (sa.canTarget(opp)) {
|
||||||
sa.getTargets().add(opp);
|
sa.getTargets().add(opp);
|
||||||
canTgt = true;
|
canTgt = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return canTgt;
|
return canTgt;
|
||||||
} else {
|
} else {
|
||||||
sa.getTargets().add(ai);
|
sa.getTargets().add(ai);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return randomReturn;
|
return randomReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(final Player aiPlayer, final SpellAbility sa, final boolean mandatory) {
|
protected boolean doTriggerAINoCost(final Player aiPlayer, final SpellAbility sa, final boolean mandatory) {
|
||||||
String aiLogic = sa.getParamOrDefault("AILogic", "");
|
String aiLogic = sa.getParamOrDefault("AILogic", "");
|
||||||
|
|
||||||
// E.g. Nova Pentacle
|
// E.g. Nova Pentacle
|
||||||
if (aiLogic.equals("RedirectFromOppToCreature")) {
|
if (aiLogic.equals("RedirectFromOppToCreature")) {
|
||||||
// try to target the opponent's best targetable permanent, if able
|
// try to target the opponent's best targetable permanent, if able
|
||||||
CardCollection oppPerms = CardLists.getValidCards(aiPlayer.getOpponents().getCardsIn(ZoneType.Battlefield), sa.getTargetRestrictions().getValidTgts(), aiPlayer, sa.getHostCard(), sa);
|
CardCollection oppPerms = CardLists.getValidCards(aiPlayer.getOpponents().getCardsIn(ZoneType.Battlefield), sa.getTargetRestrictions().getValidTgts(), aiPlayer, sa.getHostCard(), sa);
|
||||||
if (!oppPerms.isEmpty()) {
|
if (!oppPerms.isEmpty()) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
sa.getTargets().add(ComputerUtilCard.getBestAI(oppPerms));
|
sa.getTargets().add(ComputerUtilCard.getBestAI(oppPerms));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mandatory) {
|
if (mandatory) {
|
||||||
// try to target the AI's worst targetable permanent, if able
|
// try to target the AI's worst targetable permanent, if able
|
||||||
CardCollection aiPerms = CardLists.getValidCards(aiPlayer.getCardsIn(ZoneType.Battlefield), sa.getTargetRestrictions().getValidTgts(), aiPlayer, sa.getHostCard(), sa);
|
CardCollection aiPerms = CardLists.getValidCards(aiPlayer.getCardsIn(ZoneType.Battlefield), sa.getTargetRestrictions().getValidTgts(), aiPlayer, sa.getHostCard(), sa);
|
||||||
if (!aiPerms.isEmpty()) {
|
if (!aiPerms.isEmpty()) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
sa.getTargets().add(ComputerUtilCard.getWorstAI(aiPerms));
|
sa.getTargets().add(ComputerUtilCard.getWorstAI(aiPerms));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.doTriggerAINoCost(aiPlayer, sa, mandatory);
|
return super.doTriggerAINoCost(aiPlayer, sa, mandatory);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,124 +1,124 @@
|
|||||||
/*
|
/*
|
||||||
* Forge: Play Magic: the Gathering.
|
* Forge: Play Magic: the Gathering.
|
||||||
* Copyright (C) 2011 Forge Team
|
* Copyright (C) 2011 Forge Team
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful,
|
* This program is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
|
|
||||||
import forge.ai.ComputerUtilCard;
|
import forge.ai.ComputerUtilCard;
|
||||||
import forge.ai.ComputerUtilCombat;
|
import forge.ai.ComputerUtilCombat;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
import forge.game.combat.CombatUtil;
|
import forge.game.combat.CombatUtil;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.player.PlayerActionConfirmMode;
|
import forge.game.player.PlayerActionConfirmMode;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* AbilityFactoryBond class.
|
* AbilityFactoryBond class.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @author Forge
|
* @author Forge
|
||||||
* @version $Id: AbilityFactoryBond.java 15090 2012-04-07 12:50:31Z Max mtg $
|
* @version $Id: AbilityFactoryBond.java 15090 2012-04-07 12:50:31Z Max mtg $
|
||||||
*/
|
*/
|
||||||
public final class EncodeAi extends SpellAbilityAi {
|
public final class EncodeAi extends SpellAbilityAi {
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* bondCanPlayAI.
|
* bondCanPlayAI.
|
||||||
* </p>
|
* </p>
|
||||||
* @param sa
|
* @param sa
|
||||||
* a {@link forge.game.spellability.SpellAbility} object.
|
* a {@link forge.game.spellability.SpellAbility} object.
|
||||||
* @param af
|
* @param af
|
||||||
* a {@link forge.game.ability.AbilityFactory} object.
|
* a {@link forge.game.ability.AbilityFactory} object.
|
||||||
*
|
*
|
||||||
* @return a boolean.
|
* @return a boolean.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
*
|
*
|
||||||
* @see forge.ai.SpellAbilityAi#confirmAction(forge.game.player.Player,
|
* @see forge.ai.SpellAbilityAi#confirmAction(forge.game.player.Player,
|
||||||
* forge.game.spellability.SpellAbility,
|
* forge.game.spellability.SpellAbility,
|
||||||
* forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
* forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
||||||
// only try to encode if there is a creature it can be used on
|
// only try to encode if there is a creature it can be used on
|
||||||
return chooseCard(player, player.getCreaturesInPlay(), true) != null;
|
return chooseCard(player, player.getCreaturesInPlay(), true) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
*
|
*
|
||||||
* @see forge.ai.SpellAbilityAi#chooseSingleCard(forge.game.player.Player,
|
* @see forge.ai.SpellAbilityAi#chooseSingleCard(forge.game.player.Player,
|
||||||
* forge.game.spellability.SpellAbility, java.lang.Iterable, boolean,
|
* forge.game.spellability.SpellAbility, java.lang.Iterable, boolean,
|
||||||
* forge.game.player.Player)
|
* forge.game.player.Player)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Card chooseSingleCard(final Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
|
public Card chooseSingleCard(final Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
|
||||||
return chooseCard(ai, options, isOptional);
|
return chooseCard(ai, options, isOptional);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Card chooseCard(final Player ai, Iterable<Card> list, boolean isOptional) {
|
private Card chooseCard(final Player ai, Iterable<Card> list, boolean isOptional) {
|
||||||
Card choice = null;
|
Card choice = null;
|
||||||
// final String logic = sa.getParam("AILogic");
|
// final String logic = sa.getParam("AILogic");
|
||||||
// if (logic == null) {
|
// if (logic == null) {
|
||||||
final List<Card> attackers = CardLists.filter(list, new Predicate<Card>() {
|
final List<Card> attackers = CardLists.filter(list, new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(final Card c) {
|
public boolean apply(final Card c) {
|
||||||
return ComputerUtilCombat.canAttackNextTurn(c);
|
return ComputerUtilCombat.canAttackNextTurn(c);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
final List<Card> unblockables = CardLists.filter(attackers, new Predicate<Card>() {
|
final List<Card> unblockables = CardLists.filter(attackers, new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(final Card c) {
|
public boolean apply(final Card c) {
|
||||||
boolean canAttackOpponent = false;
|
boolean canAttackOpponent = false;
|
||||||
for (Player opp : ai.getOpponents()) {
|
for (Player opp : ai.getOpponents()) {
|
||||||
if (CombatUtil.canAttack(c, opp) && !CombatUtil.canBeBlocked(c, opp)) {
|
if (CombatUtil.canAttack(c, opp) && !CombatUtil.canBeBlocked(c, opp)) {
|
||||||
canAttackOpponent = true;
|
canAttackOpponent = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return canAttackOpponent;
|
return canAttackOpponent;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (!unblockables.isEmpty()) {
|
if (!unblockables.isEmpty()) {
|
||||||
choice = ComputerUtilCard.getBestAI(unblockables);
|
choice = ComputerUtilCard.getBestAI(unblockables);
|
||||||
} else if (!attackers.isEmpty()) {
|
} else if (!attackers.isEmpty()) {
|
||||||
choice = ComputerUtilCard.getBestAI(attackers);
|
choice = ComputerUtilCard.getBestAI(attackers);
|
||||||
} else if (!isOptional) {
|
} else if (!isOptional) {
|
||||||
choice = ComputerUtilCard.getBestAI(list);
|
choice = ComputerUtilCard.getBestAI(list);
|
||||||
}
|
}
|
||||||
// }
|
// }
|
||||||
return choice;
|
return choice;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,29 +1,29 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
|
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: Write javadoc for this type.
|
* TODO: Write javadoc for this type.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class EndTurnAi extends SpellAbilityAi {
|
public class EndTurnAi extends SpellAbilityAi {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
|
||||||
return mandatory;
|
return mandatory;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean chkAIDrawback(SpellAbility sa, Player aiPlayer) { return false; }
|
public boolean chkAIDrawback(SpellAbility sa, Player aiPlayer) { return false; }
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see forge.card.abilityfactory.SpellAiLogic#canPlayAI(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility)
|
* @see forge.card.abilityfactory.SpellAiLogic#canPlayAI(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,263 +1,263 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import forge.ai.*;
|
import forge.ai.*;
|
||||||
import forge.game.ability.AbilityFactory;
|
import forge.game.ability.AbilityFactory;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardCollection;
|
import forge.game.card.CardCollection;
|
||||||
import forge.game.card.CardCollectionView;
|
import forge.game.card.CardCollectionView;
|
||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.AbilitySub;
|
import forge.game.spellability.AbilitySub;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.trigger.Trigger;
|
import forge.game.trigger.Trigger;
|
||||||
import forge.game.trigger.TriggerType;
|
import forge.game.trigger.TriggerType;
|
||||||
import forge.util.MyRandom;
|
import forge.util.MyRandom;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
public class FightAi extends SpellAbilityAi {
|
public class FightAi extends SpellAbilityAi {
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkAiLogic(final Player ai, final SpellAbility sa, final String aiLogic) {
|
protected boolean checkAiLogic(final Player ai, final SpellAbility sa, final String aiLogic) {
|
||||||
if (sa.hasParam("FightWithToughness")) {
|
if (sa.hasParam("FightWithToughness")) {
|
||||||
// TODO: add ailogic
|
// TODO: add ailogic
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return super.checkAiLogic(ai, sa, aiLogic);
|
return super.checkAiLogic(ai, sa, aiLogic);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkApiLogic(final Player ai, final SpellAbility sa) {
|
protected boolean checkApiLogic(final Player ai, final SpellAbility sa) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
|
|
||||||
// Get creature lists
|
// Get creature lists
|
||||||
CardCollectionView aiCreatures = ai.getCreaturesInPlay();
|
CardCollectionView aiCreatures = ai.getCreaturesInPlay();
|
||||||
aiCreatures = CardLists.getTargetableCards(aiCreatures, sa);
|
aiCreatures = CardLists.getTargetableCards(aiCreatures, sa);
|
||||||
aiCreatures = ComputerUtil.getSafeTargets(ai, sa, aiCreatures);
|
aiCreatures = ComputerUtil.getSafeTargets(ai, sa, aiCreatures);
|
||||||
List<Card> humCreatures = ai.getOpponents().getCreaturesInPlay();
|
List<Card> humCreatures = ai.getOpponents().getCreaturesInPlay();
|
||||||
humCreatures = CardLists.getTargetableCards(humCreatures, sa);
|
humCreatures = CardLists.getTargetableCards(humCreatures, sa);
|
||||||
|
|
||||||
// assumes the triggered card belongs to the ai
|
// assumes the triggered card belongs to the ai
|
||||||
if (sa.hasParam("Defined")) {
|
if (sa.hasParam("Defined")) {
|
||||||
Card fighter1 = AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), sa).get(0);
|
Card fighter1 = AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), sa).get(0);
|
||||||
for (Card humanCreature : humCreatures) {
|
for (Card humanCreature : humCreatures) {
|
||||||
if (ComputerUtilCombat.getDamageToKill(humanCreature) <= fighter1.getNetPower()
|
if (ComputerUtilCombat.getDamageToKill(humanCreature) <= fighter1.getNetPower()
|
||||||
&& humanCreature.getNetPower() < ComputerUtilCombat.getDamageToKill(fighter1)) {
|
&& humanCreature.getNetPower() < ComputerUtilCombat.getDamageToKill(fighter1)) {
|
||||||
// todo: check min/max targets; see if we picked the best
|
// todo: check min/max targets; see if we picked the best
|
||||||
// matchup
|
// matchup
|
||||||
sa.getTargets().add(humanCreature);
|
sa.getTargets().add(humanCreature);
|
||||||
return true;
|
return true;
|
||||||
} else if (humanCreature.getSVar("Targeting").equals("Dies")) {
|
} else if (humanCreature.getSVar("Targeting").equals("Dies")) {
|
||||||
sa.getTargets().add(humanCreature);
|
sa.getTargets().add(humanCreature);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.hasParam("TargetsFromDifferentZone")) {
|
if (sa.hasParam("TargetsFromDifferentZone")) {
|
||||||
if (!(humCreatures.isEmpty() && aiCreatures.isEmpty())) {
|
if (!(humCreatures.isEmpty() && aiCreatures.isEmpty())) {
|
||||||
for (Card humanCreature : humCreatures) {
|
for (Card humanCreature : humCreatures) {
|
||||||
for (Card aiCreature : aiCreatures) {
|
for (Card aiCreature : aiCreatures) {
|
||||||
if (ComputerUtilCombat.getDamageToKill(humanCreature) <= aiCreature.getNetPower()
|
if (ComputerUtilCombat.getDamageToKill(humanCreature) <= aiCreature.getNetPower()
|
||||||
&& humanCreature.getNetPower() < ComputerUtilCombat.getDamageToKill(aiCreature)) {
|
&& humanCreature.getNetPower() < ComputerUtilCombat.getDamageToKill(aiCreature)) {
|
||||||
// todo: check min/max targets; see if we picked the
|
// todo: check min/max targets; see if we picked the
|
||||||
// best matchup
|
// best matchup
|
||||||
sa.getTargets().add(humanCreature);
|
sa.getTargets().add(humanCreature);
|
||||||
sa.getTargets().add(aiCreature);
|
sa.getTargets().add(aiCreature);
|
||||||
return true;
|
return true;
|
||||||
} else if (humanCreature.getSVar("Targeting").equals("Dies")) {
|
} else if (humanCreature.getSVar("Targeting").equals("Dies")) {
|
||||||
sa.getTargets().add(humanCreature);
|
sa.getTargets().add(humanCreature);
|
||||||
sa.getTargets().add(aiCreature);
|
sa.getTargets().add(aiCreature);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (Card creature1 : humCreatures) {
|
for (Card creature1 : humCreatures) {
|
||||||
for (Card creature2 : humCreatures) {
|
for (Card creature2 : humCreatures) {
|
||||||
if (creature1.equals(creature2)) {
|
if (creature1.equals(creature2)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (sa.hasParam("TargetsWithoutSameCreatureType") && creature1.sharesCreatureTypeWith(creature2)) {
|
if (sa.hasParam("TargetsWithoutSameCreatureType") && creature1.sharesCreatureTypeWith(creature2)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (ComputerUtilCombat.getDamageToKill(creature1) <= creature2.getNetPower()
|
if (ComputerUtilCombat.getDamageToKill(creature1) <= creature2.getNetPower()
|
||||||
&& creature1.getNetPower() >= ComputerUtilCombat.getDamageToKill(creature2)) {
|
&& creature1.getNetPower() >= ComputerUtilCombat.getDamageToKill(creature2)) {
|
||||||
// todo: check min/max targets; see if we picked the best
|
// todo: check min/max targets; see if we picked the best
|
||||||
// matchup
|
// matchup
|
||||||
sa.getTargets().add(creature1);
|
sa.getTargets().add(creature1);
|
||||||
sa.getTargets().add(creature2);
|
sa.getTargets().add(creature2);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean chkAIDrawback(final SpellAbility sa, final Player aiPlayer) {
|
public boolean chkAIDrawback(final SpellAbility sa, final Player aiPlayer) {
|
||||||
return checkApiLogic(aiPlayer, sa);
|
return checkApiLogic(aiPlayer, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
||||||
if (canPlayAI(ai, sa)) {
|
if (canPlayAI(ai, sa)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!mandatory) {
|
if (!mandatory) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//try to make a good trade or no trade
|
//try to make a good trade or no trade
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
List<Card> humCreatures = ai.getOpponents().getCreaturesInPlay();
|
List<Card> humCreatures = ai.getOpponents().getCreaturesInPlay();
|
||||||
humCreatures = CardLists.getTargetableCards(humCreatures, sa);
|
humCreatures = CardLists.getTargetableCards(humCreatures, sa);
|
||||||
if (humCreatures.isEmpty()) {
|
if (humCreatures.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
//assumes the triggered card belongs to the ai
|
//assumes the triggered card belongs to the ai
|
||||||
if (sa.hasParam("Defined")) {
|
if (sa.hasParam("Defined")) {
|
||||||
Card aiCreature = AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), sa).get(0);
|
Card aiCreature = AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), sa).get(0);
|
||||||
for (Card humanCreature : humCreatures) {
|
for (Card humanCreature : humCreatures) {
|
||||||
if (ComputerUtilCombat.getDamageToKill(humanCreature) <= aiCreature.getNetPower()
|
if (ComputerUtilCombat.getDamageToKill(humanCreature) <= aiCreature.getNetPower()
|
||||||
&& ComputerUtilCard.evaluateCreature(humanCreature) > ComputerUtilCard.evaluateCreature(aiCreature)) {
|
&& ComputerUtilCard.evaluateCreature(humanCreature) > ComputerUtilCard.evaluateCreature(aiCreature)) {
|
||||||
sa.getTargets().add(humanCreature);
|
sa.getTargets().add(humanCreature);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (Card humanCreature : humCreatures) {
|
for (Card humanCreature : humCreatures) {
|
||||||
if (ComputerUtilCombat.getDamageToKill(aiCreature) > humanCreature.getNetPower()) {
|
if (ComputerUtilCombat.getDamageToKill(aiCreature) > humanCreature.getNetPower()) {
|
||||||
sa.getTargets().add(humanCreature);
|
sa.getTargets().add(humanCreature);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sa.getTargets().add(humCreatures.get(0));
|
sa.getTargets().add(humCreatures.get(0));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logic for evaluating fight effects
|
* Logic for evaluating fight effects
|
||||||
* @param ai controlling player
|
* @param ai controlling player
|
||||||
* @param sa host SpellAbility
|
* @param sa host SpellAbility
|
||||||
* @param toughness bonus to toughness
|
* @param toughness bonus to toughness
|
||||||
* @param power bonus to power
|
* @param power bonus to power
|
||||||
* @return true if fight effect should be played, false otherwise
|
* @return true if fight effect should be played, false otherwise
|
||||||
*/
|
*/
|
||||||
public static boolean canFightAi(final Player ai, final SpellAbility sa, int power, int toughness) {
|
public static boolean canFightAi(final Player ai, final SpellAbility sa, int power, int toughness) {
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
final String sourceName = ComputerUtilAbility.getAbilitySourceName(sa);
|
final String sourceName = ComputerUtilAbility.getAbilitySourceName(sa);
|
||||||
final AbilitySub tgtFight = sa.getSubAbility();
|
final AbilitySub tgtFight = sa.getSubAbility();
|
||||||
final boolean isChandrasIgnition = "Chandra's Ignition".equals(sourceName); // TODO: generalize this for other "fake Fight" cases that do not target
|
final boolean isChandrasIgnition = "Chandra's Ignition".equals(sourceName); // TODO: generalize this for other "fake Fight" cases that do not target
|
||||||
if ("Savage Punch".equals(sourceName) && !ai.hasFerocious()) {
|
if ("Savage Punch".equals(sourceName) && !ai.hasFerocious()) {
|
||||||
power = 0;
|
power = 0;
|
||||||
toughness = 0;
|
toughness = 0;
|
||||||
}
|
}
|
||||||
// Get sorted creature lists
|
// Get sorted creature lists
|
||||||
CardCollection aiCreatures = ai.getCreaturesInPlay();
|
CardCollection aiCreatures = ai.getCreaturesInPlay();
|
||||||
CardCollection humCreatures = ai.getOpponents().getCreaturesInPlay();
|
CardCollection humCreatures = ai.getOpponents().getCreaturesInPlay();
|
||||||
if ("Time to Feed".equals(sourceName)) { // flip sa
|
if ("Time to Feed".equals(sourceName)) { // flip sa
|
||||||
aiCreatures = CardLists.getTargetableCards(aiCreatures, tgtFight);
|
aiCreatures = CardLists.getTargetableCards(aiCreatures, tgtFight);
|
||||||
aiCreatures = ComputerUtil.getSafeTargets(ai, tgtFight, aiCreatures);
|
aiCreatures = ComputerUtil.getSafeTargets(ai, tgtFight, aiCreatures);
|
||||||
humCreatures = CardLists.getTargetableCards(humCreatures, sa);
|
humCreatures = CardLists.getTargetableCards(humCreatures, sa);
|
||||||
} else {
|
} else {
|
||||||
aiCreatures = CardLists.getTargetableCards(aiCreatures, sa);
|
aiCreatures = CardLists.getTargetableCards(aiCreatures, sa);
|
||||||
aiCreatures = ComputerUtil.getSafeTargets(ai, sa, aiCreatures);
|
aiCreatures = ComputerUtil.getSafeTargets(ai, sa, aiCreatures);
|
||||||
humCreatures = CardLists.getTargetableCards(humCreatures, tgtFight);
|
humCreatures = CardLists.getTargetableCards(humCreatures, tgtFight);
|
||||||
}
|
}
|
||||||
ComputerUtilCard.sortByEvaluateCreature(aiCreatures);
|
ComputerUtilCard.sortByEvaluateCreature(aiCreatures);
|
||||||
ComputerUtilCard.sortByEvaluateCreature(humCreatures);
|
ComputerUtilCard.sortByEvaluateCreature(humCreatures);
|
||||||
if (humCreatures.isEmpty() || aiCreatures.isEmpty()) {
|
if (humCreatures.isEmpty() || aiCreatures.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Evaluate creature pairs
|
// Evaluate creature pairs
|
||||||
for (Card humanCreature : humCreatures) {
|
for (Card humanCreature : humCreatures) {
|
||||||
for (Card aiCreature : aiCreatures) {
|
for (Card aiCreature : aiCreatures) {
|
||||||
if (source.isSpell()) { // heroic triggers adding counters and prowess
|
if (source.isSpell()) { // heroic triggers adding counters and prowess
|
||||||
final int bonus = getSpellBonus(aiCreature);
|
final int bonus = getSpellBonus(aiCreature);
|
||||||
power += bonus;
|
power += bonus;
|
||||||
toughness += bonus;
|
toughness += bonus;
|
||||||
}
|
}
|
||||||
if ("PowerDmg".equals(sa.getParam("AILogic"))) {
|
if ("PowerDmg".equals(sa.getParam("AILogic"))) {
|
||||||
if (FightAi.canKill(aiCreature, humanCreature, power)) {
|
if (FightAi.canKill(aiCreature, humanCreature, power)) {
|
||||||
sa.getTargets().add(aiCreature);
|
sa.getTargets().add(aiCreature);
|
||||||
if (!isChandrasIgnition) {
|
if (!isChandrasIgnition) {
|
||||||
tgtFight.resetTargets();
|
tgtFight.resetTargets();
|
||||||
tgtFight.getTargets().add(humanCreature);
|
tgtFight.getTargets().add(humanCreature);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (FightAi.shouldFight(aiCreature, humanCreature, power, toughness)) {
|
if (FightAi.shouldFight(aiCreature, humanCreature, power, toughness)) {
|
||||||
if ("Time to Feed".equals(sourceName)) { // flip targets
|
if ("Time to Feed".equals(sourceName)) { // flip targets
|
||||||
final Card tmp = aiCreature;
|
final Card tmp = aiCreature;
|
||||||
aiCreature = humanCreature;
|
aiCreature = humanCreature;
|
||||||
humanCreature = tmp;
|
humanCreature = tmp;
|
||||||
}
|
}
|
||||||
sa.getTargets().add(aiCreature);
|
sa.getTargets().add(aiCreature);
|
||||||
tgtFight.resetTargets();
|
tgtFight.resetTargets();
|
||||||
tgtFight.getTargets().add(humanCreature);
|
tgtFight.getTargets().add(humanCreature);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compute the bonus from Heroic +1/+1 counters or Prowess
|
* Compute the bonus from Heroic +1/+1 counters or Prowess
|
||||||
*/
|
*/
|
||||||
private static int getSpellBonus(final Card aiCreature) {
|
private static int getSpellBonus(final Card aiCreature) {
|
||||||
for (Trigger t : aiCreature.getTriggers()) {
|
for (Trigger t : aiCreature.getTriggers()) {
|
||||||
if (t.getMode() == TriggerType.SpellCast) {
|
if (t.getMode() == TriggerType.SpellCast) {
|
||||||
final Map<String, String> params = t.getMapParams();
|
final Map<String, String> params = t.getMapParams();
|
||||||
if ("Card.Self".equals(params.get("TargetsValid")) && "You".equals(params.get("ValidActivatingPlayer"))
|
if ("Card.Self".equals(params.get("TargetsValid")) && "You".equals(params.get("ValidActivatingPlayer"))
|
||||||
&& params.containsKey("Execute")) {
|
&& params.containsKey("Execute")) {
|
||||||
SpellAbility heroic = AbilityFactory.getAbility(aiCreature.getSVar(params.get("Execute")),aiCreature);
|
SpellAbility heroic = AbilityFactory.getAbility(aiCreature.getSVar(params.get("Execute")),aiCreature);
|
||||||
if ("Self".equals(heroic.getParam("Defined")) && "P1P1".equals(heroic.getParam("CounterType"))) {
|
if ("Self".equals(heroic.getParam("Defined")) && "P1P1".equals(heroic.getParam("CounterType"))) {
|
||||||
return AbilityUtils.calculateAmount(aiCreature, heroic.getParam("CounterNum"), heroic);
|
return AbilityUtils.calculateAmount(aiCreature, heroic.getParam("CounterNum"), heroic);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if ("ProwessPump".equals(params.get("Execute"))) {
|
if ("ProwessPump".equals(params.get("Execute"))) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean shouldFight(Card fighter, Card opponent, int pumpAttack, int pumpDefense) {
|
private static boolean shouldFight(Card fighter, Card opponent, int pumpAttack, int pumpDefense) {
|
||||||
if (canKill(fighter, opponent, pumpAttack)) {
|
if (canKill(fighter, opponent, pumpAttack)) {
|
||||||
if (!canKill(opponent, fighter, -pumpDefense)) { // can survive
|
if (!canKill(opponent, fighter, -pumpDefense)) { // can survive
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
final Random r = MyRandom.getRandom();
|
final Random r = MyRandom.getRandom();
|
||||||
if (r.nextInt(20)<(opponent.getCMC() - fighter.getCMC())) { // trade
|
if (r.nextInt(20)<(opponent.getCMC() - fighter.getCMC())) { // trade
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
public static boolean canKill(Card fighter, Card opponent, int pumpAttack) {
|
public static boolean canKill(Card fighter, Card opponent, int pumpAttack) {
|
||||||
if (opponent.getSVar("Targeting").equals("Dies")) {
|
if (opponent.getSVar("Targeting").equals("Dies")) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (opponent.hasProtectionFrom(fighter) || !opponent.canBeDestroyed()
|
if (opponent.hasProtectionFrom(fighter) || !opponent.canBeDestroyed()
|
||||||
|| opponent.getShieldCount() > 0 || ComputerUtil.canRegenerate(opponent.getController(), opponent)) {
|
|| opponent.getShieldCount() > 0 || ComputerUtil.canRegenerate(opponent.getController(), opponent)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (fighter.hasKeyword("Deathtouch") || ComputerUtilCombat.getDamageToKill(opponent) <= fighter.getNetPower() + pumpAttack) {
|
if (fighter.hasKeyword("Deathtouch") || ComputerUtilCombat.getDamageToKill(opponent) <= fighter.getNetPower() + pumpAttack) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,47 +1,47 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import forge.ai.ComputerUtil;
|
import forge.ai.ComputerUtil;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
|
|
||||||
public class FlipACoinAi extends SpellAbilityAi {
|
public class FlipACoinAi extends SpellAbilityAi {
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see forge.card.abilityfactory.SpellAiLogic#canPlayAI(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility)
|
* @see forge.card.abilityfactory.SpellAiLogic#canPlayAI(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
||||||
|
|
||||||
if (sa.hasParam("AILogic")) {
|
if (sa.hasParam("AILogic")) {
|
||||||
String AILogic = sa.getParam("AILogic");
|
String AILogic = sa.getParam("AILogic");
|
||||||
if (AILogic.equals("Never")) {
|
if (AILogic.equals("Never")) {
|
||||||
return false;
|
return false;
|
||||||
} else if (AILogic.equals("PhaseOut")) {
|
} else if (AILogic.equals("PhaseOut")) {
|
||||||
if (!ComputerUtil.predictThreatenedObjects(sa.getActivatingPlayer(), sa).contains(sa.getHostCard())) {
|
if (!ComputerUtil.predictThreatenedObjects(sa.getActivatingPlayer(), sa).contains(sa.getHostCard())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (AILogic.equals("KillOrcs")) {
|
} else if (AILogic.equals("KillOrcs")) {
|
||||||
if (ai.getGame().getPhaseHandler().getPhase().isBefore(PhaseType.END_OF_TURN) ) {
|
if (ai.getGame().getPhaseHandler().getPhase().isBefore(PhaseType.END_OF_TURN) ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
for (Card c : ai.getOpponents().getCreaturesInPlay()) {
|
for (Card c : ai.getOpponents().getCreaturesInPlay()) {
|
||||||
if (sa.canTarget(c)) {
|
if (sa.canTarget(c)) {
|
||||||
sa.getTargets().add(c);
|
sa.getTargets().add(c);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
||||||
return canPlayAI(ai, sa);
|
return canPlayAI(ai, sa);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,92 +1,92 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
|
|
||||||
import forge.ai.ComputerUtil;
|
import forge.ai.ComputerUtil;
|
||||||
import forge.ai.ComputerUtilCombat;
|
import forge.ai.ComputerUtilCombat;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardPredicates;
|
import forge.game.card.CardPredicates;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.util.Aggregates;
|
import forge.util.Aggregates;
|
||||||
|
|
||||||
public class FogAi extends SpellAbilityAi {
|
public class FogAi extends SpellAbilityAi {
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see forge.card.abilityfactory.SpellAiLogic#canPlayAI(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility)
|
* @see forge.card.abilityfactory.SpellAiLogic#canPlayAI(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
// AI should only activate this during Human's Declare Blockers phase
|
// AI should only activate this during Human's Declare Blockers phase
|
||||||
if (game.getPhaseHandler().isPlayerTurn(sa.getActivatingPlayer())) {
|
if (game.getPhaseHandler().isPlayerTurn(sa.getActivatingPlayer())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!game.getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
|
if (!game.getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only cast when Stack is empty, so Human uses spells/abilities first
|
// Only cast when Stack is empty, so Human uses spells/abilities first
|
||||||
if (!game.getStack().isEmpty()) {
|
if (!game.getStack().isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't cast it, if the effect is already in place
|
// Don't cast it, if the effect is already in place
|
||||||
if (game.getPhaseHandler().isPreventCombatDamageThisTurn()) {
|
if (game.getPhaseHandler().isPreventCombatDamageThisTurn()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("SeriousDamage".equals(sa.getParam("AILogic")) && game.getCombat() != null) {
|
if ("SeriousDamage".equals(sa.getParam("AILogic")) && game.getCombat() != null) {
|
||||||
int dmg = 0;
|
int dmg = 0;
|
||||||
for (Card atk : game.getCombat().getAttackersOf(ai)) {
|
for (Card atk : game.getCombat().getAttackersOf(ai)) {
|
||||||
if (game.getCombat().isUnblocked(atk)) {
|
if (game.getCombat().isUnblocked(atk)) {
|
||||||
dmg += atk.getNetCombatDamage();
|
dmg += atk.getNetCombatDamage();
|
||||||
} else if (atk.hasKeyword("Trample")) {
|
} else if (atk.hasKeyword("Trample")) {
|
||||||
dmg += atk.getNetCombatDamage() - Aggregates.sum(game.getCombat().getBlockers(atk), CardPredicates.Accessors.fnGetNetToughness);
|
dmg += atk.getNetCombatDamage() - Aggregates.sum(game.getCombat().getBlockers(atk), CardPredicates.Accessors.fnGetNetToughness);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dmg > ai.getLife() / 4) {
|
if (dmg > ai.getLife() / 4) {
|
||||||
return true;
|
return true;
|
||||||
} else if (dmg >= 5) {
|
} else if (dmg >= 5) {
|
||||||
return true;
|
return true;
|
||||||
} else if (ai.getLife() < ai.getStartingLife() / 3) {
|
} else if (ai.getLife() < ai.getStartingLife() / 3) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cast it if life is in danger
|
// Cast it if life is in danger
|
||||||
return ComputerUtilCombat.lifeInDanger(ai, game.getCombat());
|
return ComputerUtilCombat.lifeInDanger(ai, game.getCombat());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
||||||
// AI should only activate this during Human's turn
|
// AI should only activate this during Human's turn
|
||||||
boolean chance;
|
boolean chance;
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
|
|
||||||
// should really check if other player is attacking this player
|
// should really check if other player is attacking this player
|
||||||
if (ai.isOpponentOf(game.getPhaseHandler().getPlayerTurn())) {
|
if (ai.isOpponentOf(game.getPhaseHandler().getPlayerTurn())) {
|
||||||
chance = game.getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_FIRST_STRIKE_DAMAGE);
|
chance = game.getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_FIRST_STRIKE_DAMAGE);
|
||||||
} else {
|
} else {
|
||||||
chance = game.getPhaseHandler().getPhase().isAfter(PhaseType.COMBAT_DAMAGE);
|
chance = game.getPhaseHandler().getPhase().isAfter(PhaseType.COMBAT_DAMAGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
return chance;
|
return chance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
|
||||||
final Game game = aiPlayer.getGame();
|
final Game game = aiPlayer.getGame();
|
||||||
boolean chance;
|
boolean chance;
|
||||||
if (game.getPhaseHandler().isPlayerTurn(ComputerUtil.getOpponentFor(sa.getActivatingPlayer()))) {
|
if (game.getPhaseHandler().isPlayerTurn(ComputerUtil.getOpponentFor(sa.getActivatingPlayer()))) {
|
||||||
chance = game.getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_FIRST_STRIKE_DAMAGE);
|
chance = game.getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_FIRST_STRIKE_DAMAGE);
|
||||||
} else {
|
} else {
|
||||||
chance = game.getPhaseHandler().getPhase().isAfter(PhaseType.COMBAT_DAMAGE);
|
chance = game.getPhaseHandler().getPhase().isAfter(PhaseType.COMBAT_DAMAGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
return chance;
|
return chance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,50 +1,50 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import forge.ai.ComputerUtil;
|
import forge.ai.ComputerUtil;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.TargetRestrictions;
|
import forge.game.spellability.TargetRestrictions;
|
||||||
|
|
||||||
public class GameLossAi extends SpellAbilityAi {
|
public class GameLossAi extends SpellAbilityAi {
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
||||||
final Player opp = ComputerUtil.getOpponentFor(ai);
|
final Player opp = ComputerUtil.getOpponentFor(ai);
|
||||||
if (opp.cantLose()) {
|
if (opp.cantLose()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only one SA Lose the Game card right now, which is Door to
|
// Only one SA Lose the Game card right now, which is Door to
|
||||||
// Nothingness
|
// Nothingness
|
||||||
|
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
if (tgt != null) {
|
if (tgt != null) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
sa.getTargets().add(opp);
|
sa.getTargets().add(opp);
|
||||||
}
|
}
|
||||||
|
|
||||||
// In general, don't return true.
|
// In general, don't return true.
|
||||||
// But this card wins the game, I can make an exception for that
|
// But this card wins the game, I can make an exception for that
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
||||||
|
|
||||||
// Phage the Untouchable
|
// Phage the Untouchable
|
||||||
// (Final Fortune would need to attach it's delayed trigger to a
|
// (Final Fortune would need to attach it's delayed trigger to a
|
||||||
// specific turn, which can't be done yet)
|
// specific turn, which can't be done yet)
|
||||||
|
|
||||||
if (!mandatory && ComputerUtil.getOpponentFor(ai).cantLose()) {
|
if (!mandatory && ComputerUtil.getOpponentFor(ai).cantLose()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
if (tgt != null) {
|
if (tgt != null) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
sa.getTargets().add(ComputerUtil.getOpponentFor(ai));
|
sa.getTargets().add(ComputerUtil.getOpponentFor(ai));
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,32 +1,32 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
|
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
|
|
||||||
public class GameWinAi extends SpellAbilityAi {
|
public class GameWinAi extends SpellAbilityAi {
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see forge.card.abilityfactory.SpellAiLogic#canPlayAI(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility)
|
* @see forge.card.abilityfactory.SpellAiLogic#canPlayAI(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
||||||
if (ai.cantWin()) {
|
if (ai.cantWin()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Check conditions are met on card (e.g. Coalition Victory)
|
// TODO Check conditions are met on card (e.g. Coalition Victory)
|
||||||
|
|
||||||
// TODO Consider likelihood of SA getting countered
|
// TODO Consider likelihood of SA getting countered
|
||||||
|
|
||||||
// In general, don't return true.
|
// In general, don't return true.
|
||||||
// But this card wins the game, I can make an exception for that
|
// But this card wins the game, I can make an exception for that
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,35 +1,35 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import forge.ai.ComputerUtilCard;
|
import forge.ai.ComputerUtilCard;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
import forge.game.card.CardPredicates;
|
import forge.game.card.CardPredicates;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
|
|
||||||
public class HauntAi extends SpellAbilityAi {
|
public class HauntAi extends SpellAbilityAi {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
||||||
final Card card = sa.getHostCard();
|
final Card card = sa.getHostCard();
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
if (sa.usesTargeting() && !card.isToken()) {
|
if (sa.usesTargeting() && !card.isToken()) {
|
||||||
final List<Card> creats = CardLists.filter(game.getCardsIn(ZoneType.Battlefield),
|
final List<Card> creats = CardLists.filter(game.getCardsIn(ZoneType.Battlefield),
|
||||||
CardPredicates.Presets.CREATURES);
|
CardPredicates.Presets.CREATURES);
|
||||||
|
|
||||||
// nothing to haunt
|
// nothing to haunt
|
||||||
if (creats.isEmpty()) {
|
if (creats.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<Card> oppCreats = CardLists.filterControlledBy(creats, ai.getOpponents());
|
final List<Card> oppCreats = CardLists.filterControlledBy(creats, ai.getOpponents());
|
||||||
sa.getTargets().add(ComputerUtilCard.getWorstCreatureAI(oppCreats.isEmpty() ? creats : oppCreats));
|
sa.getTargets().add(ComputerUtilCard.getWorstCreatureAI(oppCreats.isEmpty() ? creats : oppCreats));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,60 +1,60 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CounterType;
|
import forge.game.card.CounterType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: Write javadoc for this type.
|
* TODO: Write javadoc for this type.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class LegendaryRuleAi extends SpellAbilityAi {
|
public class LegendaryRuleAi extends SpellAbilityAi {
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see forge.card.ability.SpellAbilityAi#canPlayAI(forge.game.player.Player, forge.card.spellability.SpellAbility)
|
* @see forge.card.ability.SpellAbilityAi#canPlayAI(forge.game.player.Player, forge.card.spellability.SpellAbility)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
||||||
return false; // should not get here
|
return false; // should not get here
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
|
public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
|
||||||
// Choose a single legendary/planeswalker card to keep
|
// Choose a single legendary/planeswalker card to keep
|
||||||
Card firstOption = Iterables.getFirst(options, null);
|
Card firstOption = Iterables.getFirst(options, null);
|
||||||
boolean choosingFromPlanewalkers = firstOption.isPlaneswalker();
|
boolean choosingFromPlanewalkers = firstOption.isPlaneswalker();
|
||||||
|
|
||||||
if ( choosingFromPlanewalkers ) {
|
if ( choosingFromPlanewalkers ) {
|
||||||
// AI decision making - should AI compare counters?
|
// AI decision making - should AI compare counters?
|
||||||
} else {
|
} else {
|
||||||
// AI decision making - should AI compare damage and debuffs?
|
// AI decision making - should AI compare damage and debuffs?
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Can this be made more generic somehow?
|
// TODO: Can this be made more generic somehow?
|
||||||
if (firstOption.getName().equals("Dark Depths")) {
|
if (firstOption.getName().equals("Dark Depths")) {
|
||||||
Card best = firstOption;
|
Card best = firstOption;
|
||||||
for (Card c : options) {
|
for (Card c : options) {
|
||||||
if (c.getCounters(CounterType.ICE) < best.getCounters(CounterType.ICE)) {
|
if (c.getCounters(CounterType.ICE) < best.getCounters(CounterType.ICE)) {
|
||||||
best = c;
|
best = c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return best;
|
return best;
|
||||||
} else if (firstOption.getCounters(CounterType.KI) > 0) {
|
} else if (firstOption.getCounters(CounterType.KI) > 0) {
|
||||||
// Extra Rule for KI counter
|
// Extra Rule for KI counter
|
||||||
Card best = firstOption;
|
Card best = firstOption;
|
||||||
for (Card c : options) {
|
for (Card c : options) {
|
||||||
if (c.getCounters(CounterType.KI) > best.getCounters(CounterType.KI)) {
|
if (c.getCounters(CounterType.KI) > best.getCounters(CounterType.KI)) {
|
||||||
best = c;
|
best = c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return best;
|
return best;
|
||||||
}
|
}
|
||||||
|
|
||||||
return firstOption;
|
return firstOption;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,94 +1,94 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import forge.ai.ComputerUtil;
|
import forge.ai.ComputerUtil;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.TargetRestrictions;
|
import forge.game.spellability.TargetRestrictions;
|
||||||
import forge.util.MyRandom;
|
import forge.util.MyRandom;
|
||||||
|
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
public class LifeExchangeAi extends SpellAbilityAi {
|
public class LifeExchangeAi extends SpellAbilityAi {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
*
|
*
|
||||||
* @see
|
* @see
|
||||||
* forge.card.abilityfactory.AbilityFactoryAlterLife.SpellAiLogic#canPlayAI
|
* forge.card.abilityfactory.AbilityFactoryAlterLife.SpellAiLogic#canPlayAI
|
||||||
* (forge.game.player.Player, java.util.Map,
|
* (forge.game.player.Player, java.util.Map,
|
||||||
* forge.card.spellability.SpellAbility)
|
* forge.card.spellability.SpellAbility)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
||||||
final Random r = MyRandom.getRandom();
|
final Random r = MyRandom.getRandom();
|
||||||
final int myLife = aiPlayer.getLife();
|
final int myLife = aiPlayer.getLife();
|
||||||
Player opponent = ComputerUtil.getOpponentFor(aiPlayer);
|
Player opponent = ComputerUtil.getOpponentFor(aiPlayer);
|
||||||
final int hLife = opponent.getLife();
|
final int hLife = opponent.getLife();
|
||||||
|
|
||||||
if (!aiPlayer.canGainLife()) {
|
if (!aiPlayer.canGainLife()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// prevent run-away activations - first time will always return true
|
// prevent run-away activations - first time will always return true
|
||||||
boolean chance = r.nextFloat() <= Math.pow(.6667, sa.getActivationsThisTurn());
|
boolean chance = r.nextFloat() <= Math.pow(.6667, sa.getActivationsThisTurn());
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TODO - There is one card that takes two targets (Soul Conduit)
|
* TODO - There is one card that takes two targets (Soul Conduit)
|
||||||
* and one card that has a conditional (Psychic Transfer) that are
|
* and one card that has a conditional (Psychic Transfer) that are
|
||||||
* not currently handled
|
* not currently handled
|
||||||
*/
|
*/
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
if (tgt != null) {
|
if (tgt != null) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
if (opponent.canBeTargetedBy(sa)) {
|
if (opponent.canBeTargetedBy(sa)) {
|
||||||
// never target self, that would be silly for exchange
|
// never target self, that would be silly for exchange
|
||||||
sa.getTargets().add(opponent);
|
sa.getTargets().add(opponent);
|
||||||
if (!opponent.canLoseLife()) {
|
if (!opponent.canLoseLife()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if life is in danger, always activate
|
// if life is in danger, always activate
|
||||||
if ((myLife < 5) && (hLife > myLife)) {
|
if ((myLife < 5) && (hLife > myLife)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// cost includes sacrifice probably, so make sure it's worth it
|
// cost includes sacrifice probably, so make sure it's worth it
|
||||||
chance &= (hLife > (myLife + 8));
|
chance &= (hLife > (myLife + 8));
|
||||||
|
|
||||||
return ((r.nextFloat() < .6667) && chance);
|
return ((r.nextFloat() < .6667) && chance);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* exchangeLifeDoTriggerAINoCost.
|
* exchangeLifeDoTriggerAINoCost.
|
||||||
* </p>
|
* </p>
|
||||||
* @param sa
|
* @param sa
|
||||||
* a {@link forge.game.spellability.SpellAbility} object.
|
* a {@link forge.game.spellability.SpellAbility} object.
|
||||||
* @param mandatory
|
* @param mandatory
|
||||||
* a boolean.
|
* a boolean.
|
||||||
* @param af
|
* @param af
|
||||||
* a {@link forge.game.ability.AbilityFactory} object.
|
* a {@link forge.game.ability.AbilityFactory} object.
|
||||||
*
|
*
|
||||||
* @return a boolean.
|
* @return a boolean.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(final Player ai, final SpellAbility sa,
|
protected boolean doTriggerAINoCost(final Player ai, final SpellAbility sa,
|
||||||
final boolean mandatory) {
|
final boolean mandatory) {
|
||||||
|
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
Player opp = ComputerUtil.getOpponentFor(ai);
|
Player opp = ComputerUtil.getOpponentFor(ai);
|
||||||
if (tgt != null) {
|
if (tgt != null) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
if (sa.canTarget(opp) && (mandatory || ai.getLife() < opp.getLife())) {
|
if (sa.canTarget(opp) && (mandatory || ai.getLife() < opp.getLife())) {
|
||||||
sa.getTargets().add(opp);
|
sa.getTargets().add(opp);
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,313 +1,313 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import forge.ai.*;
|
import forge.ai.*;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.cost.Cost;
|
import forge.game.cost.Cost;
|
||||||
import forge.game.cost.CostRemoveCounter;
|
import forge.game.cost.CostRemoveCounter;
|
||||||
import forge.game.cost.CostSacrifice;
|
import forge.game.cost.CostSacrifice;
|
||||||
import forge.game.phase.PhaseHandler;
|
import forge.game.phase.PhaseHandler;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.player.PlayerCollection;
|
import forge.game.player.PlayerCollection;
|
||||||
import forge.game.player.PlayerPredicates;
|
import forge.game.player.PlayerPredicates;
|
||||||
import forge.game.spellability.AbilitySub;
|
import forge.game.spellability.AbilitySub;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.util.MyRandom;
|
import forge.util.MyRandom;
|
||||||
|
|
||||||
public class LifeGainAi extends SpellAbilityAi {
|
public class LifeGainAi extends SpellAbilityAi {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
*
|
*
|
||||||
* @see forge.ai.SpellAbilityAi#willPayCosts(forge.game.player.Player,
|
* @see forge.ai.SpellAbilityAi#willPayCosts(forge.game.player.Player,
|
||||||
* forge.game.spellability.SpellAbility, forge.game.cost.Cost,
|
* forge.game.spellability.SpellAbility, forge.game.cost.Cost,
|
||||||
* forge.game.card.Card)
|
* forge.game.card.Card)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean willPayCosts(Player ai, SpellAbility sa, Cost cost, Card source) {
|
protected boolean willPayCosts(Player ai, SpellAbility sa, Cost cost, Card source) {
|
||||||
final Game game = source.getGame();
|
final Game game = source.getGame();
|
||||||
final PhaseHandler ph = game.getPhaseHandler();
|
final PhaseHandler ph = game.getPhaseHandler();
|
||||||
final int life = ai.getLife();
|
final int life = ai.getLife();
|
||||||
|
|
||||||
boolean lifeCritical = life <= 5;
|
boolean lifeCritical = life <= 5;
|
||||||
lifeCritical |= ph.getPhase().isBefore(PhaseType.COMBAT_DAMAGE)
|
lifeCritical |= ph.getPhase().isBefore(PhaseType.COMBAT_DAMAGE)
|
||||||
&& ComputerUtilCombat.lifeInDanger(ai, game.getCombat());
|
&& ComputerUtilCombat.lifeInDanger(ai, game.getCombat());
|
||||||
|
|
||||||
if (!lifeCritical) {
|
if (!lifeCritical) {
|
||||||
// return super.willPayCosts(ai, sa, cost, source);
|
// return super.willPayCosts(ai, sa, cost, source);
|
||||||
if (!ComputerUtilCost.checkSacrificeCost(ai, cost, source, sa,false)) {
|
if (!ComputerUtilCost.checkSacrificeCost(ai, cost, source, sa,false)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!ComputerUtilCost.checkLifeCost(ai, cost, source, 4, sa)) {
|
if (!ComputerUtilCost.checkLifeCost(ai, cost, source, 4, sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ComputerUtilCost.checkDiscardCost(ai, cost, source)) {
|
if (!ComputerUtilCost.checkDiscardCost(ai, cost, source)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ComputerUtilCost.checkRemoveCounterCost(cost, source)) {
|
if (!ComputerUtilCost.checkRemoveCounterCost(cost, source)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// don't sac possible blockers
|
// don't sac possible blockers
|
||||||
if (!ph.getPhase().equals(PhaseType.COMBAT_DECLARE_BLOCKERS)
|
if (!ph.getPhase().equals(PhaseType.COMBAT_DECLARE_BLOCKERS)
|
||||||
|| !game.getCombat().getDefenders().contains(ai)) {
|
|| !game.getCombat().getDefenders().contains(ai)) {
|
||||||
boolean skipCheck = false;
|
boolean skipCheck = false;
|
||||||
// if it's a sac self cost and the effect source is not a
|
// if it's a sac self cost and the effect source is not a
|
||||||
// creature, skip this check
|
// creature, skip this check
|
||||||
// (e.g. Woodweaver's Puzzleknot)
|
// (e.g. Woodweaver's Puzzleknot)
|
||||||
skipCheck |= ComputerUtilCost.isSacrificeSelfCost(cost) && !source.isCreature();
|
skipCheck |= ComputerUtilCost.isSacrificeSelfCost(cost) && !source.isCreature();
|
||||||
|
|
||||||
if (!skipCheck) {
|
if (!skipCheck) {
|
||||||
if (!ComputerUtilCost.checkSacrificeCost(ai, cost, source, sa,false)) {
|
if (!ComputerUtilCost.checkSacrificeCost(ai, cost, source, sa,false)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkPhaseRestrictions(final Player ai, final SpellAbility sa, final PhaseHandler ph) {
|
protected boolean checkPhaseRestrictions(final Player ai, final SpellAbility sa, final PhaseHandler ph) {
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
final int life = ai.getLife();
|
final int life = ai.getLife();
|
||||||
boolean activateForCost = ComputerUtil.activateForCost(sa, ai);
|
boolean activateForCost = ComputerUtil.activateForCost(sa, ai);
|
||||||
|
|
||||||
boolean lifeCritical = life <= 5;
|
boolean lifeCritical = life <= 5;
|
||||||
lifeCritical |= ph.getPhase().isBefore(PhaseType.COMBAT_DAMAGE)
|
lifeCritical |= ph.getPhase().isBefore(PhaseType.COMBAT_DAMAGE)
|
||||||
&& ComputerUtilCombat.lifeInDanger(ai, game.getCombat());
|
&& ComputerUtilCombat.lifeInDanger(ai, game.getCombat());
|
||||||
|
|
||||||
// When life is critical but there is no immediate danger, try to wait until declare blockers
|
// When life is critical but there is no immediate danger, try to wait until declare blockers
|
||||||
// before using the lifegain ability if it's an ability on a creature with a detrimental activation cost
|
// before using the lifegain ability if it's an ability on a creature with a detrimental activation cost
|
||||||
if (lifeCritical
|
if (lifeCritical
|
||||||
&& sa.isAbility()
|
&& sa.isAbility()
|
||||||
&& sa.getHostCard() != null && sa.getHostCard().isCreature()
|
&& sa.getHostCard() != null && sa.getHostCard().isCreature()
|
||||||
&& sa.getPayCosts() != null
|
&& sa.getPayCosts() != null
|
||||||
&& (sa.getPayCosts().hasSpecificCostType(CostRemoveCounter.class) || sa.getPayCosts().hasSpecificCostType(CostSacrifice.class))) {
|
&& (sa.getPayCosts().hasSpecificCostType(CostRemoveCounter.class) || sa.getPayCosts().hasSpecificCostType(CostSacrifice.class))) {
|
||||||
if (!game.getStack().isEmpty()) {
|
if (!game.getStack().isEmpty()) {
|
||||||
SpellAbility saTop = game.getStack().peekAbility();
|
SpellAbility saTop = game.getStack().peekAbility();
|
||||||
if (saTop.getTargets() != null && Iterables.contains(saTop.getTargets().getTargetPlayers(), ai)) {
|
if (saTop.getTargets() != null && Iterables.contains(saTop.getTargets().getTargetPlayers(), ai)) {
|
||||||
return ComputerUtil.predictDamageFromSpell(saTop, ai) > 0;
|
return ComputerUtil.predictDamageFromSpell(saTop, ai) > 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (game.getCombat() == null) { return false; }
|
if (game.getCombat() == null) { return false; }
|
||||||
if (!ph.is(PhaseType.COMBAT_DECLARE_BLOCKERS)) { return false; }
|
if (!ph.is(PhaseType.COMBAT_DECLARE_BLOCKERS)) { return false; }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't use lifegain before main 2 if possible
|
// Don't use lifegain before main 2 if possible
|
||||||
if (!lifeCritical && ph.getPhase().isBefore(PhaseType.MAIN2) && !sa.hasParam("ActivationPhases")
|
if (!lifeCritical && ph.getPhase().isBefore(PhaseType.MAIN2) && !sa.hasParam("ActivationPhases")
|
||||||
&& !ComputerUtil.castSpellInMain1(ai, sa)) {
|
&& !ComputerUtil.castSpellInMain1(ai, sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!lifeCritical && !activateForCost
|
if (!lifeCritical && !activateForCost
|
||||||
&& (!ph.getNextTurn().equals(ai) || ph.getPhase().isBefore(PhaseType.END_OF_TURN))
|
&& (!ph.getNextTurn().equals(ai) || ph.getPhase().isBefore(PhaseType.END_OF_TURN))
|
||||||
&& !sa.hasParam("PlayerTurn") && !SpellAbilityAi.isSorcerySpeed(sa)) {
|
&& !sa.hasParam("PlayerTurn") && !SpellAbilityAi.isSorcerySpeed(sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
*
|
*
|
||||||
* @see forge.ai.SpellAbilityAi#checkApiLogic(forge.game.player.Player,
|
* @see forge.ai.SpellAbilityAi#checkApiLogic(forge.game.player.Player,
|
||||||
* forge.game.spellability.SpellAbility)
|
* forge.game.spellability.SpellAbility)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkApiLogic(Player ai, SpellAbility sa) {
|
protected boolean checkApiLogic(Player ai, SpellAbility sa) {
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
final String sourceName = ComputerUtilAbility.getAbilitySourceName(sa);
|
final String sourceName = ComputerUtilAbility.getAbilitySourceName(sa);
|
||||||
|
|
||||||
final int life = ai.getLife();
|
final int life = ai.getLife();
|
||||||
final String amountStr = sa.getParam("LifeAmount");
|
final String amountStr = sa.getParam("LifeAmount");
|
||||||
int lifeAmount = 0;
|
int lifeAmount = 0;
|
||||||
boolean activateForCost = ComputerUtil.activateForCost(sa, ai);
|
boolean activateForCost = ComputerUtil.activateForCost(sa, ai);
|
||||||
if (amountStr.equals("X") && source.getSVar(amountStr).equals("Count$xPaid")) {
|
if (amountStr.equals("X") && source.getSVar(amountStr).equals("Count$xPaid")) {
|
||||||
// Set PayX here to maximum value.
|
// Set PayX here to maximum value.
|
||||||
final int xPay = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
final int xPay = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
||||||
source.setSVar("PayX", Integer.toString(xPay));
|
source.setSVar("PayX", Integer.toString(xPay));
|
||||||
lifeAmount = xPay;
|
lifeAmount = xPay;
|
||||||
} else {
|
} else {
|
||||||
lifeAmount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, sa);
|
lifeAmount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ugin AI: always use ultimate
|
// Ugin AI: always use ultimate
|
||||||
if (sourceName.equals("Ugin, the Spirit Dragon")) {
|
if (sourceName.equals("Ugin, the Spirit Dragon")) {
|
||||||
// TODO: somehow link with DamageDealAi for cases where +1 = win
|
// TODO: somehow link with DamageDealAi for cases where +1 = win
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't use it if no life to gain
|
// don't use it if no life to gain
|
||||||
if (!activateForCost && lifeAmount <= 0) {
|
if (!activateForCost && lifeAmount <= 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// don't play if the conditions aren't met, unless it would trigger a
|
// don't play if the conditions aren't met, unless it would trigger a
|
||||||
// beneficial sub-condition
|
// beneficial sub-condition
|
||||||
if (!activateForCost && !sa.getConditions().areMet(sa)) {
|
if (!activateForCost && !sa.getConditions().areMet(sa)) {
|
||||||
final AbilitySub abSub = sa.getSubAbility();
|
final AbilitySub abSub = sa.getSubAbility();
|
||||||
if (abSub != null && !sa.isWrapper() && "True".equals(source.getSVar("AIPlayForSub"))) {
|
if (abSub != null && !sa.isWrapper() && "True".equals(source.getSVar("AIPlayForSub"))) {
|
||||||
if (!abSub.getConditions().areMet(abSub)) {
|
if (!abSub.getConditions().areMet(abSub)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!activateForCost && !ai.canGainLife()) {
|
if (!activateForCost && !ai.canGainLife()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// prevent run-away activations - first time will always return true
|
// prevent run-away activations - first time will always return true
|
||||||
if (ComputerUtil.preventRunAwayActivations(sa)) {
|
if (ComputerUtil.preventRunAwayActivations(sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.usesTargeting()) {
|
if (sa.usesTargeting()) {
|
||||||
if (!target(ai, sa, true)) {
|
if (!target(ai, sa, true)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ComputerUtil.playImmediately(ai, sa)) {
|
if (ComputerUtil.playImmediately(ai, sa)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SpellAbilityAi.isSorcerySpeed(sa)
|
if (SpellAbilityAi.isSorcerySpeed(sa)
|
||||||
|| sa.getSubAbility() != null || SpellAbilityAi.playReusable(ai, sa)) {
|
|| sa.getSubAbility() != null || SpellAbilityAi.playReusable(ai, sa)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save instant-speed life-gain unless it is really worth it
|
// Save instant-speed life-gain unless it is really worth it
|
||||||
final float value = 0.9f * lifeAmount / life;
|
final float value = 0.9f * lifeAmount / life;
|
||||||
if (value < 0.2f) {
|
if (value < 0.2f) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return MyRandom.getRandom().nextFloat() < value;
|
return MyRandom.getRandom().nextFloat() < value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* gainLifeDoTriggerAINoCost.
|
* gainLifeDoTriggerAINoCost.
|
||||||
* </p>
|
* </p>
|
||||||
* @param sa
|
* @param sa
|
||||||
* a {@link forge.game.spellability.SpellAbility} object.
|
* a {@link forge.game.spellability.SpellAbility} object.
|
||||||
* @param mandatory
|
* @param mandatory
|
||||||
* a boolean.
|
* a boolean.
|
||||||
*
|
*
|
||||||
* @return a boolean.
|
* @return a boolean.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(final Player ai, final SpellAbility sa,
|
protected boolean doTriggerAINoCost(final Player ai, final SpellAbility sa,
|
||||||
final boolean mandatory) {
|
final boolean mandatory) {
|
||||||
|
|
||||||
// If the Target is gaining life, target self.
|
// If the Target is gaining life, target self.
|
||||||
// if the Target is modifying how much life is gained, this needs to be
|
// if the Target is modifying how much life is gained, this needs to be
|
||||||
// handled better
|
// handled better
|
||||||
if (sa.usesTargeting()) {
|
if (sa.usesTargeting()) {
|
||||||
if (!target(ai, sa, mandatory)) {
|
if (!target(ai, sa, mandatory)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
final String amountStr = sa.getParam("LifeAmount");
|
final String amountStr = sa.getParam("LifeAmount");
|
||||||
if (amountStr.equals("X") && source.getSVar(amountStr).equals("Count$xPaid")) {
|
if (amountStr.equals("X") && source.getSVar(amountStr).equals("Count$xPaid")) {
|
||||||
// Set PayX here to maximum value.
|
// Set PayX here to maximum value.
|
||||||
final int xPay = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
final int xPay = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
||||||
source.setSVar("PayX", Integer.toString(xPay));
|
source.setSVar("PayX", Integer.toString(xPay));
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
||||||
return doTriggerAINoCost(ai, sa, true);
|
return doTriggerAINoCost(ai, sa, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean target(Player ai, SpellAbility sa, boolean mandatory) {
|
private boolean target(Player ai, SpellAbility sa, boolean mandatory) {
|
||||||
Card source = sa.getHostCard();
|
Card source = sa.getHostCard();
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
// TODO : add add even more logic into it
|
// TODO : add add even more logic into it
|
||||||
// try to target opponents first if that would kill them
|
// try to target opponents first if that would kill them
|
||||||
|
|
||||||
PlayerCollection opps = ai.getOpponents().filter(PlayerPredicates.isTargetableBy(sa));
|
PlayerCollection opps = ai.getOpponents().filter(PlayerPredicates.isTargetableBy(sa));
|
||||||
PlayerCollection allies = ai.getAllies().filter(PlayerPredicates.isTargetableBy(sa));
|
PlayerCollection allies = ai.getAllies().filter(PlayerPredicates.isTargetableBy(sa));
|
||||||
|
|
||||||
if (sa.canTarget(ai) && ComputerUtil.lifegainPositive(ai, source)) {
|
if (sa.canTarget(ai) && ComputerUtil.lifegainPositive(ai, source)) {
|
||||||
sa.getTargets().add(ai);
|
sa.getTargets().add(ai);
|
||||||
} else {
|
} else {
|
||||||
boolean hasTgt = false;
|
boolean hasTgt = false;
|
||||||
// check for Lifegain negative on opponents
|
// check for Lifegain negative on opponents
|
||||||
for (Player opp : opps) {
|
for (Player opp : opps) {
|
||||||
if (ComputerUtil.lifegainNegative(opp, source)) {
|
if (ComputerUtil.lifegainNegative(opp, source)) {
|
||||||
sa.getTargets().add(opp);
|
sa.getTargets().add(opp);
|
||||||
hasTgt = true;
|
hasTgt = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!hasTgt) {
|
if (!hasTgt) {
|
||||||
// lifegain on ally
|
// lifegain on ally
|
||||||
for (Player ally : allies) {
|
for (Player ally : allies) {
|
||||||
if (ComputerUtil.lifegainPositive(ally, source)) {
|
if (ComputerUtil.lifegainPositive(ally, source)) {
|
||||||
sa.getTargets().add(ally);
|
sa.getTargets().add(ally);
|
||||||
hasTgt = true;
|
hasTgt = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!hasTgt && mandatory) {
|
if (!hasTgt && mandatory) {
|
||||||
// need to target something but its neither negative against
|
// need to target something but its neither negative against
|
||||||
// opponents,
|
// opponents,
|
||||||
// nor posive against allies
|
// nor posive against allies
|
||||||
|
|
||||||
// hurting ally is probably better than healing opponent
|
// hurting ally is probably better than healing opponent
|
||||||
// look for Lifegain not Negative (case of lifegain negated)
|
// look for Lifegain not Negative (case of lifegain negated)
|
||||||
for (Player ally : allies) {
|
for (Player ally : allies) {
|
||||||
if (!ComputerUtil.lifegainNegative(ally, source)) {
|
if (!ComputerUtil.lifegainNegative(ally, source)) {
|
||||||
sa.getTargets().add(ally);
|
sa.getTargets().add(ally);
|
||||||
hasTgt = true;
|
hasTgt = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!hasTgt) {
|
if (!hasTgt) {
|
||||||
// same for opponent lifegain not positive
|
// same for opponent lifegain not positive
|
||||||
for (Player opp : opps) {
|
for (Player opp : opps) {
|
||||||
if (!ComputerUtil.lifegainPositive(opp, source)) {
|
if (!ComputerUtil.lifegainPositive(opp, source)) {
|
||||||
sa.getTargets().add(opp);
|
sa.getTargets().add(opp);
|
||||||
hasTgt = true;
|
hasTgt = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// still no luck, try to target ally with most life
|
// still no luck, try to target ally with most life
|
||||||
if (!allies.isEmpty()) {
|
if (!allies.isEmpty()) {
|
||||||
Player ally = allies.max(PlayerPredicates.compareByLife());
|
Player ally = allies.max(PlayerPredicates.compareByLife());
|
||||||
sa.getTargets().add(ally);
|
sa.getTargets().add(ally);
|
||||||
hasTgt = true;
|
hasTgt = true;
|
||||||
}
|
}
|
||||||
// better heal opponent which most life then the one with the
|
// better heal opponent which most life then the one with the
|
||||||
// lowest
|
// lowest
|
||||||
if (!hasTgt) {
|
if (!hasTgt) {
|
||||||
Player opp = opps.max(PlayerPredicates.compareByLife());
|
Player opp = opps.max(PlayerPredicates.compareByLife());
|
||||||
sa.getTargets().add(opp);
|
sa.getTargets().add(opp);
|
||||||
hasTgt = true;
|
hasTgt = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!hasTgt) {
|
if (!hasTgt) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,236 +1,236 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.google.common.base.Predicates;
|
import com.google.common.base.Predicates;
|
||||||
|
|
||||||
import forge.ai.ComputerUtil;
|
import forge.ai.ComputerUtil;
|
||||||
import forge.ai.ComputerUtilCost;
|
import forge.ai.ComputerUtilCost;
|
||||||
import forge.ai.ComputerUtilMana;
|
import forge.ai.ComputerUtilMana;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.cost.Cost;
|
import forge.game.cost.Cost;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.player.PlayerCollection;
|
import forge.game.player.PlayerCollection;
|
||||||
import forge.game.player.PlayerPredicates;
|
import forge.game.player.PlayerPredicates;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.util.collect.FCollection;
|
import forge.util.collect.FCollection;
|
||||||
|
|
||||||
public class LifeLoseAi extends SpellAbilityAi {
|
public class LifeLoseAi extends SpellAbilityAi {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
*
|
*
|
||||||
* @see forge.ai.SpellAbilityAi#chkAIDrawback(forge.game.spellability.
|
* @see forge.ai.SpellAbilityAi#chkAIDrawback(forge.game.spellability.
|
||||||
* SpellAbility, forge.game.player.Player)
|
* SpellAbility, forge.game.player.Player)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
||||||
|
|
||||||
final PlayerCollection tgtPlayers = getPlayers(ai, sa);
|
final PlayerCollection tgtPlayers = getPlayers(ai, sa);
|
||||||
|
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
final String amountStr = sa.getParam("LifeAmount");
|
final String amountStr = sa.getParam("LifeAmount");
|
||||||
int amount = 0;
|
int amount = 0;
|
||||||
if (amountStr.equals("X") && source.getSVar(amountStr).equals("Count$xPaid")) {
|
if (amountStr.equals("X") && source.getSVar(amountStr).equals("Count$xPaid")) {
|
||||||
// something already set PayX
|
// something already set PayX
|
||||||
if (source.hasSVar("PayX")) {
|
if (source.hasSVar("PayX")) {
|
||||||
amount = Integer.parseInt(source.getSVar("PayX"));
|
amount = Integer.parseInt(source.getSVar("PayX"));
|
||||||
} else {
|
} else {
|
||||||
// Set PayX here to maximum value.
|
// Set PayX here to maximum value.
|
||||||
final int xPay = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
final int xPay = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
||||||
source.setSVar("PayX", Integer.toString(xPay));
|
source.setSVar("PayX", Integer.toString(xPay));
|
||||||
amount = xPay;
|
amount = xPay;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
amount = AbilityUtils.calculateAmount(source, amountStr, sa);
|
amount = AbilityUtils.calculateAmount(source, amountStr, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tgtPlayers.contains(ai) && amount > 0 && amount + 3 > ai.getLife()) {
|
if (tgtPlayers.contains(ai) && amount > 0 && amount + 3 > ai.getLife()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
*
|
*
|
||||||
* @see forge.ai.SpellAbilityAi#willPayCosts(forge.game.player.Player,
|
* @see forge.ai.SpellAbilityAi#willPayCosts(forge.game.player.Player,
|
||||||
* forge.game.spellability.SpellAbility, forge.game.cost.Cost,
|
* forge.game.spellability.SpellAbility, forge.game.cost.Cost,
|
||||||
* forge.game.card.Card)
|
* forge.game.card.Card)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean willPayCosts(Player ai, SpellAbility sa, Cost cost, Card source) {
|
protected boolean willPayCosts(Player ai, SpellAbility sa, Cost cost, Card source) {
|
||||||
final String amountStr = sa.getParam("LifeAmount");
|
final String amountStr = sa.getParam("LifeAmount");
|
||||||
int amount = 0;
|
int amount = 0;
|
||||||
|
|
||||||
if (amountStr.equals("X") && source.getSVar(amountStr).equals("Count$xPaid")) {
|
if (amountStr.equals("X") && source.getSVar(amountStr).equals("Count$xPaid")) {
|
||||||
// Set PayX here to maximum value.
|
// Set PayX here to maximum value.
|
||||||
amount = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
amount = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
||||||
// source.setSVar("PayX", Integer.toString(amount));
|
// source.setSVar("PayX", Integer.toString(amount));
|
||||||
} else {
|
} else {
|
||||||
amount = AbilityUtils.calculateAmount(source, amountStr, sa);
|
amount = AbilityUtils.calculateAmount(source, amountStr, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
// special logic for checkLifeCost
|
// special logic for checkLifeCost
|
||||||
if (!ComputerUtilCost.checkLifeCost(ai, cost, source, amount, sa)) {
|
if (!ComputerUtilCost.checkLifeCost(ai, cost, source, amount, sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// other cost as the same
|
// other cost as the same
|
||||||
return super.willPayCosts(ai, sa, cost, source);
|
return super.willPayCosts(ai, sa, cost, source);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
*
|
*
|
||||||
* @see forge.ai.SpellAbilityAi#checkApiLogic(forge.game.player.Player,
|
* @see forge.ai.SpellAbilityAi#checkApiLogic(forge.game.player.Player,
|
||||||
* forge.game.spellability.SpellAbility)
|
* forge.game.spellability.SpellAbility)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkApiLogic(Player ai, SpellAbility sa) {
|
protected boolean checkApiLogic(Player ai, SpellAbility sa) {
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
final String amountStr = sa.getParam("LifeAmount");
|
final String amountStr = sa.getParam("LifeAmount");
|
||||||
int amount = 0;
|
int amount = 0;
|
||||||
|
|
||||||
if (amountStr.equals("X") && source.getSVar(amountStr).equals("Count$xPaid")) {
|
if (amountStr.equals("X") && source.getSVar(amountStr).equals("Count$xPaid")) {
|
||||||
// Set PayX here to maximum value.
|
// Set PayX here to maximum value.
|
||||||
amount = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
amount = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
||||||
source.setSVar("PayX", Integer.toString(amount));
|
source.setSVar("PayX", Integer.toString(amount));
|
||||||
} else {
|
} else {
|
||||||
amount = AbilityUtils.calculateAmount(source, amountStr, sa);
|
amount = AbilityUtils.calculateAmount(source, amountStr, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (amount <= 0) {
|
if (amount <= 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ComputerUtil.preventRunAwayActivations(sa)) {
|
if (ComputerUtil.preventRunAwayActivations(sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.usesTargeting()) {
|
if (sa.usesTargeting()) {
|
||||||
if (!doTgt(ai, sa, false)) {
|
if (!doTgt(ai, sa, false)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final PlayerCollection tgtPlayers = getPlayers(ai, sa);
|
final PlayerCollection tgtPlayers = getPlayers(ai, sa);
|
||||||
|
|
||||||
if (ComputerUtil.playImmediately(ai, sa)) {
|
if (ComputerUtil.playImmediately(ai, sa)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
PlayerCollection filteredPlayer = tgtPlayers
|
PlayerCollection filteredPlayer = tgtPlayers
|
||||||
.filter(Predicates.and(PlayerPredicates.isOpponentOf(ai), PlayerPredicates.lifeLessOrEqualTo(amount)));
|
.filter(Predicates.and(PlayerPredicates.isOpponentOf(ai), PlayerPredicates.lifeLessOrEqualTo(amount)));
|
||||||
// killing opponents asap
|
// killing opponents asap
|
||||||
if (!filteredPlayer.isEmpty()) {
|
if (!filteredPlayer.isEmpty()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't use loselife before main 2 if possible
|
// Don't use loselife before main 2 if possible
|
||||||
if (ai.getGame().getPhaseHandler().getPhase().isBefore(PhaseType.MAIN2) && !sa.hasParam("ActivationPhases")
|
if (ai.getGame().getPhaseHandler().getPhase().isBefore(PhaseType.MAIN2) && !sa.hasParam("ActivationPhases")
|
||||||
&& !ComputerUtil.castSpellInMain1(ai, sa)) {
|
&& !ComputerUtil.castSpellInMain1(ai, sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't tap creatures that may be able to block
|
// Don't tap creatures that may be able to block
|
||||||
if (ComputerUtil.waitForBlocking(sa)) {
|
if (ComputerUtil.waitForBlocking(sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SpellAbilityAi.isSorcerySpeed(sa) || sa.hasParam("ActivationPhases") || SpellAbilityAi.playReusable(ai, sa)
|
if (SpellAbilityAi.isSorcerySpeed(sa) || sa.hasParam("ActivationPhases") || SpellAbilityAi.playReusable(ai, sa)
|
||||||
|| ComputerUtil.activateForCost(sa, ai)) {
|
|| ComputerUtil.activateForCost(sa, ai)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
*
|
*
|
||||||
* @see forge.ai.SpellAbilityAi#doTriggerAINoCost(forge.game.player.Player,
|
* @see forge.ai.SpellAbilityAi#doTriggerAINoCost(forge.game.player.Player,
|
||||||
* forge.game.spellability.SpellAbility, boolean)
|
* forge.game.spellability.SpellAbility, boolean)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(final Player ai, final SpellAbility sa,
|
protected boolean doTriggerAINoCost(final Player ai, final SpellAbility sa,
|
||||||
final boolean mandatory) {
|
final boolean mandatory) {
|
||||||
if (sa.usesTargeting()) {
|
if (sa.usesTargeting()) {
|
||||||
if (!doTgt(ai, sa, mandatory)) {
|
if (!doTgt(ai, sa, mandatory)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
final String amountStr = sa.getParam("LifeAmount");
|
final String amountStr = sa.getParam("LifeAmount");
|
||||||
int amount = 0;
|
int amount = 0;
|
||||||
if (amountStr.equals("X") && source.getSVar(amountStr).equals("Count$xPaid")) {
|
if (amountStr.equals("X") && source.getSVar(amountStr).equals("Count$xPaid")) {
|
||||||
// Set PayX here to maximum value.
|
// Set PayX here to maximum value.
|
||||||
final int xPay = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
final int xPay = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
||||||
source.setSVar("PayX", Integer.toString(xPay));
|
source.setSVar("PayX", Integer.toString(xPay));
|
||||||
amount = xPay;
|
amount = xPay;
|
||||||
} else {
|
} else {
|
||||||
amount = AbilityUtils.calculateAmount(source, amountStr, sa);
|
amount = AbilityUtils.calculateAmount(source, amountStr, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<Player> tgtPlayers = sa.usesTargeting() && !sa.hasParam("Defined")
|
final List<Player> tgtPlayers = sa.usesTargeting() && !sa.hasParam("Defined")
|
||||||
? new FCollection<Player>(sa.getTargets().getTargetPlayers())
|
? new FCollection<Player>(sa.getTargets().getTargetPlayers())
|
||||||
: AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("Defined"), sa);
|
: AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("Defined"), sa);
|
||||||
|
|
||||||
if (!mandatory && tgtPlayers.contains(ai) && amount > 0 && amount + 3 > ai.getLife()) {
|
if (!mandatory && tgtPlayers.contains(ai) && amount > 0 && amount + 3 > ai.getLife()) {
|
||||||
// For cards like Foul Imp, ETB you lose life
|
// For cards like Foul Imp, ETB you lose life
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean doTgt(Player ai, SpellAbility sa, boolean mandatory) {
|
protected boolean doTgt(Player ai, SpellAbility sa, boolean mandatory) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
PlayerCollection opps = ai.getOpponents().filter(PlayerPredicates.isTargetableBy(sa));
|
PlayerCollection opps = ai.getOpponents().filter(PlayerPredicates.isTargetableBy(sa));
|
||||||
// try first to find Opponent that can lose life and lose the game
|
// try first to find Opponent that can lose life and lose the game
|
||||||
if (!opps.isEmpty()) {
|
if (!opps.isEmpty()) {
|
||||||
for (Player opp : opps) {
|
for (Player opp : opps) {
|
||||||
if (opp.canLoseLife() && !opp.cantLose()) {
|
if (opp.canLoseLife() && !opp.cantLose()) {
|
||||||
sa.getTargets().add(opp);
|
sa.getTargets().add(opp);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// do that only if needed
|
// do that only if needed
|
||||||
if (mandatory) {
|
if (mandatory) {
|
||||||
if (!opps.isEmpty()) {
|
if (!opps.isEmpty()) {
|
||||||
// try another opponent even if it can't lose life
|
// try another opponent even if it can't lose life
|
||||||
sa.getTargets().add(opps.getFirst());
|
sa.getTargets().add(opps.getFirst());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// try hit ally instead of itself
|
// try hit ally instead of itself
|
||||||
for (Player ally : ai.getAllies()) {
|
for (Player ally : ai.getAllies()) {
|
||||||
if (sa.canTarget(ally)) {
|
if (sa.canTarget(ally)) {
|
||||||
sa.getTargets().add(ally);
|
sa.getTargets().add(ally);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// need to hit itself
|
// need to hit itself
|
||||||
if (sa.canTarget(ai)) {
|
if (sa.canTarget(ai)) {
|
||||||
sa.getTargets().add(ai);
|
sa.getTargets().add(ai);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected PlayerCollection getPlayers(Player ai, SpellAbility sa) {
|
protected PlayerCollection getPlayers(Player ai, SpellAbility sa) {
|
||||||
Iterable<Player> it;
|
Iterable<Player> it;
|
||||||
if (sa.usesTargeting() && !sa.hasParam("Defined")) {
|
if (sa.usesTargeting() && !sa.hasParam("Defined")) {
|
||||||
it = sa.getTargets().getTargetPlayers();
|
it = sa.getTargets().getTargetPlayers();
|
||||||
} else {
|
} else {
|
||||||
it = AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("Defined"), sa);
|
it = AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("Defined"), sa);
|
||||||
}
|
}
|
||||||
return new PlayerCollection(it);
|
return new PlayerCollection(it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,160 +1,160 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import forge.ai.ComputerUtil;
|
import forge.ai.ComputerUtil;
|
||||||
import forge.ai.ComputerUtilAbility;
|
import forge.ai.ComputerUtilAbility;
|
||||||
import forge.ai.ComputerUtilMana;
|
import forge.ai.ComputerUtilMana;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CounterType;
|
import forge.game.card.CounterType;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.TargetRestrictions;
|
import forge.game.spellability.TargetRestrictions;
|
||||||
import forge.util.MyRandom;
|
import forge.util.MyRandom;
|
||||||
|
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
public class LifeSetAi extends SpellAbilityAi {
|
public class LifeSetAi extends SpellAbilityAi {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
||||||
final Random r = MyRandom.getRandom();
|
final Random r = MyRandom.getRandom();
|
||||||
// Ability_Cost abCost = sa.getPayCosts();
|
// Ability_Cost abCost = sa.getPayCosts();
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
final int myLife = ai.getLife();
|
final int myLife = ai.getLife();
|
||||||
final Player opponent = ComputerUtil.getOpponentFor(ai);
|
final Player opponent = ComputerUtil.getOpponentFor(ai);
|
||||||
final int hlife = opponent.getLife();
|
final int hlife = opponent.getLife();
|
||||||
final String amountStr = sa.getParam("LifeAmount");
|
final String amountStr = sa.getParam("LifeAmount");
|
||||||
|
|
||||||
if (!ai.canGainLife()) {
|
if (!ai.canGainLife()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't use setLife before main 2 if possible
|
// Don't use setLife before main 2 if possible
|
||||||
if (ai.getGame().getPhaseHandler().getPhase().isBefore(PhaseType.MAIN2)
|
if (ai.getGame().getPhaseHandler().getPhase().isBefore(PhaseType.MAIN2)
|
||||||
&& !sa.hasParam("ActivationPhases")) {
|
&& !sa.hasParam("ActivationPhases")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO add AI logic for that
|
// TODO add AI logic for that
|
||||||
if (sa.hasParam("Redistribute")) {
|
if (sa.hasParam("Redistribute")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO handle proper calculation of X values based on Cost and what
|
// TODO handle proper calculation of X values based on Cost and what
|
||||||
// would be paid
|
// would be paid
|
||||||
int amount;
|
int amount;
|
||||||
// we shouldn't have to worry too much about PayX for SetLife
|
// we shouldn't have to worry too much about PayX for SetLife
|
||||||
if (amountStr.equals("X") && source.getSVar(amountStr).equals("Count$xPaid")) {
|
if (amountStr.equals("X") && source.getSVar(amountStr).equals("Count$xPaid")) {
|
||||||
// Set PayX here to maximum value.
|
// Set PayX here to maximum value.
|
||||||
final int xPay = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
final int xPay = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
||||||
source.setSVar("PayX", Integer.toString(xPay));
|
source.setSVar("PayX", Integer.toString(xPay));
|
||||||
amount = xPay;
|
amount = xPay;
|
||||||
} else {
|
} else {
|
||||||
amount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, sa);
|
amount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
// prevent run-away activations - first time will always return true
|
// prevent run-away activations - first time will always return true
|
||||||
final boolean chance = r.nextFloat() <= Math.pow(.6667, sa.getActivationsThisTurn());
|
final boolean chance = r.nextFloat() <= Math.pow(.6667, sa.getActivationsThisTurn());
|
||||||
|
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
if (tgt != null) {
|
if (tgt != null) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
if (tgt.canOnlyTgtOpponent()) {
|
if (tgt.canOnlyTgtOpponent()) {
|
||||||
sa.getTargets().add(opponent);
|
sa.getTargets().add(opponent);
|
||||||
// if we can only target the human, and the Human's life
|
// if we can only target the human, and the Human's life
|
||||||
// would
|
// would
|
||||||
// go up, don't play it.
|
// go up, don't play it.
|
||||||
// possibly add a combo here for Magister Sphinx and
|
// possibly add a combo here for Magister Sphinx and
|
||||||
// Higedetsu's
|
// Higedetsu's
|
||||||
// (sp?) Second Rite
|
// (sp?) Second Rite
|
||||||
if ((amount > hlife) || !opponent.canLoseLife()) {
|
if ((amount > hlife) || !opponent.canLoseLife()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if ((amount > myLife) && (myLife <= 10)) {
|
if ((amount > myLife) && (myLife <= 10)) {
|
||||||
sa.getTargets().add(ai);
|
sa.getTargets().add(ai);
|
||||||
} else if (hlife > amount) {
|
} else if (hlife > amount) {
|
||||||
sa.getTargets().add(opponent);
|
sa.getTargets().add(opponent);
|
||||||
} else if (amount > myLife) {
|
} else if (amount > myLife) {
|
||||||
sa.getTargets().add(ai);
|
sa.getTargets().add(ai);
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (sa.getParam("Defined").equals("Player")) {
|
if (sa.getParam("Defined").equals("Player")) {
|
||||||
if (amount == 0) {
|
if (amount == 0) {
|
||||||
return false;
|
return false;
|
||||||
} else if (myLife > amount) { // will decrease computer's
|
} else if (myLife > amount) { // will decrease computer's
|
||||||
// life
|
// life
|
||||||
if ((myLife < 5) || ((myLife - amount) > (hlife - amount))) {
|
if ((myLife < 5) || ((myLife - amount) > (hlife - amount))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (amount <= myLife) {
|
if (amount <= myLife) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if life is in danger, always activate
|
// if life is in danger, always activate
|
||||||
if ((myLife < 3) && (amount > myLife)) {
|
if ((myLife < 3) && (amount > myLife)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ((r.nextFloat() < .6667) && chance);
|
return ((r.nextFloat() < .6667) && chance);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
||||||
final int myLife = ai.getLife();
|
final int myLife = ai.getLife();
|
||||||
final Player opponent = ComputerUtil.getOpponentFor(ai);
|
final Player opponent = ComputerUtil.getOpponentFor(ai);
|
||||||
final int hlife = opponent.getLife();
|
final int hlife = opponent.getLife();
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
final String sourceName = ComputerUtilAbility.getAbilitySourceName(sa);
|
final String sourceName = ComputerUtilAbility.getAbilitySourceName(sa);
|
||||||
|
|
||||||
final String amountStr = sa.getParam("LifeAmount");
|
final String amountStr = sa.getParam("LifeAmount");
|
||||||
|
|
||||||
int amount;
|
int amount;
|
||||||
if (amountStr.equals("X") && source.getSVar(amountStr).equals("Count$xPaid")) {
|
if (amountStr.equals("X") && source.getSVar(amountStr).equals("Count$xPaid")) {
|
||||||
// Set PayX here to maximum value.
|
// Set PayX here to maximum value.
|
||||||
final int xPay = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
final int xPay = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
||||||
source.setSVar("PayX", Integer.toString(xPay));
|
source.setSVar("PayX", Integer.toString(xPay));
|
||||||
amount = xPay;
|
amount = xPay;
|
||||||
} else {
|
} else {
|
||||||
amount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, sa);
|
amount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sourceName.equals("Eternity Vessel")
|
if (sourceName.equals("Eternity Vessel")
|
||||||
&& (opponent.isCardInPlay("Vampire Hexmage") || (source.getCounters(CounterType.CHARGE) == 0))) {
|
&& (opponent.isCardInPlay("Vampire Hexmage") || (source.getCounters(CounterType.CHARGE) == 0))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the Target is gaining life, target self.
|
// If the Target is gaining life, target self.
|
||||||
// if the Target is modifying how much life is gained, this needs to
|
// if the Target is modifying how much life is gained, this needs to
|
||||||
// be
|
// be
|
||||||
// handled better
|
// handled better
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
if (tgt != null) {
|
if (tgt != null) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
if (tgt.canOnlyTgtOpponent()) {
|
if (tgt.canOnlyTgtOpponent()) {
|
||||||
sa.getTargets().add(opponent);
|
sa.getTargets().add(opponent);
|
||||||
} else {
|
} else {
|
||||||
if ((amount > myLife) && (myLife <= 10)) {
|
if ((amount > myLife) && (myLife <= 10)) {
|
||||||
sa.getTargets().add(ai);
|
sa.getTargets().add(ai);
|
||||||
} else if (hlife > amount) {
|
} else if (hlife > amount) {
|
||||||
sa.getTargets().add(opponent);
|
sa.getTargets().add(opponent);
|
||||||
} else if (amount > myLife) {
|
} else if (amount > myLife) {
|
||||||
sa.getTargets().add(ai);
|
sa.getTargets().add(ai);
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,199 +1,199 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import com.google.common.base.Predicates;
|
import com.google.common.base.Predicates;
|
||||||
import forge.ai.*;
|
import forge.ai.*;
|
||||||
import forge.card.ColorSet;
|
import forge.card.ColorSet;
|
||||||
import forge.card.MagicColor;
|
import forge.card.MagicColor;
|
||||||
import forge.card.mana.ManaCost;
|
import forge.card.mana.ManaCost;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.card.*;
|
import forge.game.card.*;
|
||||||
import forge.game.cost.CostPart;
|
import forge.game.cost.CostPart;
|
||||||
import forge.game.cost.CostRemoveCounter;
|
import forge.game.cost.CostRemoveCounter;
|
||||||
import forge.game.phase.PhaseHandler;
|
import forge.game.phase.PhaseHandler;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class ManaEffectAi extends SpellAbilityAi {
|
public class ManaEffectAi extends SpellAbilityAi {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
*
|
*
|
||||||
* @see forge.ai.SpellAbilityAi#checkAiLogic(forge.game.player.Player,
|
* @see forge.ai.SpellAbilityAi#checkAiLogic(forge.game.player.Player,
|
||||||
* forge.game.spellability.SpellAbility, java.lang.String)
|
* forge.game.spellability.SpellAbility, java.lang.String)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkAiLogic(Player ai, SpellAbility sa, String aiLogic) {
|
protected boolean checkAiLogic(Player ai, SpellAbility sa, String aiLogic) {
|
||||||
if ("ManaRitual".equals(aiLogic)) {
|
if ("ManaRitual".equals(aiLogic)) {
|
||||||
return doManaRitualLogic(ai, sa);
|
return doManaRitualLogic(ai, sa);
|
||||||
}
|
}
|
||||||
return super.checkAiLogic(ai, sa, aiLogic);
|
return super.checkAiLogic(ai, sa, aiLogic);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
*
|
*
|
||||||
* @see
|
* @see
|
||||||
* forge.ai.SpellAbilityAi#checkPhaseRestrictions(forge.game.player.Player,
|
* forge.ai.SpellAbilityAi#checkPhaseRestrictions(forge.game.player.Player,
|
||||||
* forge.game.spellability.SpellAbility, forge.game.phase.PhaseHandler)
|
* forge.game.spellability.SpellAbility, forge.game.phase.PhaseHandler)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkPhaseRestrictions(Player ai, SpellAbility sa, PhaseHandler ph) {
|
protected boolean checkPhaseRestrictions(Player ai, SpellAbility sa, PhaseHandler ph) {
|
||||||
if (!ph.is(PhaseType.MAIN2) || !ComputerUtil.activateForCost(sa, ai)) {
|
if (!ph.is(PhaseType.MAIN2) || !ComputerUtil.activateForCost(sa, ai)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return super.checkPhaseRestrictions(ai, sa, ph);
|
return super.checkPhaseRestrictions(ai, sa, ph);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
*
|
*
|
||||||
* @see
|
* @see
|
||||||
* forge.ai.SpellAbilityAi#checkPhaseRestrictions(forge.game.player.Player,
|
* forge.ai.SpellAbilityAi#checkPhaseRestrictions(forge.game.player.Player,
|
||||||
* forge.game.spellability.SpellAbility, forge.game.phase.PhaseHandler,
|
* forge.game.spellability.SpellAbility, forge.game.phase.PhaseHandler,
|
||||||
* java.lang.String)
|
* java.lang.String)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkPhaseRestrictions(Player ai, SpellAbility sa, PhaseHandler ph, String logic) {
|
protected boolean checkPhaseRestrictions(Player ai, SpellAbility sa, PhaseHandler ph, String logic) {
|
||||||
if (logic.startsWith("ManaRitual")) {
|
if (logic.startsWith("ManaRitual")) {
|
||||||
return ph.is(PhaseType.MAIN2, ai) || ph.is(PhaseType.MAIN1, ai);
|
return ph.is(PhaseType.MAIN2, ai) || ph.is(PhaseType.MAIN1, ai);
|
||||||
}
|
}
|
||||||
return super.checkPhaseRestrictions(ai, sa, ph, logic);
|
return super.checkPhaseRestrictions(ai, sa, ph, logic);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
*
|
*
|
||||||
* @see forge.ai.SpellAbilityAi#checkApiLogic(forge.game.player.Player,
|
* @see forge.ai.SpellAbilityAi#checkApiLogic(forge.game.player.Player,
|
||||||
* forge.game.spellability.SpellAbility)
|
* forge.game.spellability.SpellAbility)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkApiLogic(Player ai, SpellAbility sa) {
|
protected boolean checkApiLogic(Player ai, SpellAbility sa) {
|
||||||
if (sa.hasParam("AILogic")) {
|
if (sa.hasParam("AILogic")) {
|
||||||
return true; // handled elsewhere, does not meet the standard requirements
|
return true; // handled elsewhere, does not meet the standard requirements
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(sa.getPayCosts() != null && sa.getPayCosts().hasNoManaCost() && sa.getPayCosts().isReusuableResource()
|
if (!(sa.getPayCosts() != null && sa.getPayCosts().hasNoManaCost() && sa.getPayCosts().isReusuableResource()
|
||||||
&& sa.getSubAbility() == null && ComputerUtil.playImmediately(ai, sa))) {
|
&& sa.getSubAbility() == null && ComputerUtil.playImmediately(ai, sa))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
// return super.checkApiLogic(ai, sa);
|
// return super.checkApiLogic(ai, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param aiPlayer
|
* @param aiPlayer
|
||||||
* the AI player.
|
* the AI player.
|
||||||
* @param sa
|
* @param sa
|
||||||
* a {@link forge.game.spellability.SpellAbility} object.
|
* a {@link forge.game.spellability.SpellAbility} object.
|
||||||
* @param mandatory
|
* @param mandatory
|
||||||
* a boolean.
|
* a boolean.
|
||||||
*
|
*
|
||||||
* @return a boolean.
|
* @return a boolean.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dark Ritual and other similar instants/sorceries that add mana to mana pool
|
// Dark Ritual and other similar instants/sorceries that add mana to mana pool
|
||||||
private boolean doManaRitualLogic(Player ai, SpellAbility sa) {
|
private boolean doManaRitualLogic(Player ai, SpellAbility sa) {
|
||||||
final Card host = sa.getHostCard();
|
final Card host = sa.getHostCard();
|
||||||
|
|
||||||
CardCollection manaSources = ComputerUtilMana.getAvailableManaSources(ai, true);
|
CardCollection manaSources = ComputerUtilMana.getAvailableManaSources(ai, true);
|
||||||
int numManaSrcs = manaSources.size();
|
int numManaSrcs = manaSources.size();
|
||||||
int manaReceived = sa.hasParam("Amount") ? AbilityUtils.calculateAmount(host, sa.getParam("Amount"), sa) : 1;
|
int manaReceived = sa.hasParam("Amount") ? AbilityUtils.calculateAmount(host, sa.getParam("Amount"), sa) : 1;
|
||||||
manaReceived *= sa.getParam("Produced").split(" ").length;
|
manaReceived *= sa.getParam("Produced").split(" ").length;
|
||||||
|
|
||||||
int selfCost = sa.getPayCosts().getCostMana() != null ? sa.getPayCosts().getCostMana().getMana().getCMC() : 0;
|
int selfCost = sa.getPayCosts().getCostMana() != null ? sa.getPayCosts().getCostMana().getMana().getCMC() : 0;
|
||||||
|
|
||||||
String produced = sa.getParam("Produced");
|
String produced = sa.getParam("Produced");
|
||||||
byte producedColor = produced.equals("Any") ? MagicColor.ALL_COLORS : MagicColor.fromName(produced);
|
byte producedColor = produced.equals("Any") ? MagicColor.ALL_COLORS : MagicColor.fromName(produced);
|
||||||
|
|
||||||
if ("ChosenX".equals(sa.getParam("Amount"))
|
if ("ChosenX".equals(sa.getParam("Amount"))
|
||||||
&& sa.getPayCosts() != null && sa.getPayCosts().hasSpecificCostType(CostRemoveCounter.class)) {
|
&& sa.getPayCosts() != null && sa.getPayCosts().hasSpecificCostType(CostRemoveCounter.class)) {
|
||||||
CounterType ctrType = CounterType.KI; // Petalmane Baku
|
CounterType ctrType = CounterType.KI; // Petalmane Baku
|
||||||
for (CostPart part : sa.getPayCosts().getCostParts()) {
|
for (CostPart part : sa.getPayCosts().getCostParts()) {
|
||||||
if (part instanceof CostRemoveCounter) {
|
if (part instanceof CostRemoveCounter) {
|
||||||
ctrType = ((CostRemoveCounter)part).counter;
|
ctrType = ((CostRemoveCounter)part).counter;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
manaReceived = host.getCounters(ctrType);
|
manaReceived = host.getCounters(ctrType);
|
||||||
}
|
}
|
||||||
|
|
||||||
int searchCMC = numManaSrcs - selfCost + manaReceived;
|
int searchCMC = numManaSrcs - selfCost + manaReceived;
|
||||||
|
|
||||||
if ("X".equals(sa.getParam("Produced"))) {
|
if ("X".equals(sa.getParam("Produced"))) {
|
||||||
String x = host.getSVar("X");
|
String x = host.getSVar("X");
|
||||||
if ("Count$CardsInYourHand".equals(x) && host.isInZone(ZoneType.Hand)) {
|
if ("Count$CardsInYourHand".equals(x) && host.isInZone(ZoneType.Hand)) {
|
||||||
searchCMC--; // the spell in hand will be used
|
searchCMC--; // the spell in hand will be used
|
||||||
} else if (x.startsWith("Count$NamedInAllYards") && host.isInZone(ZoneType.Graveyard)) {
|
} else if (x.startsWith("Count$NamedInAllYards") && host.isInZone(ZoneType.Graveyard)) {
|
||||||
searchCMC--; // the spell in graveyard will be used
|
searchCMC--; // the spell in graveyard will be used
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (searchCMC <= 0) {
|
if (searchCMC <= 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
String restrictValid = sa.hasParam("RestrictValid") ? sa.getParam("RestrictValid") : "Card";
|
String restrictValid = sa.hasParam("RestrictValid") ? sa.getParam("RestrictValid") : "Card";
|
||||||
|
|
||||||
CardCollection cardList = new CardCollection();
|
CardCollection cardList = new CardCollection();
|
||||||
List<SpellAbility> all = ComputerUtilAbility.getSpellAbilities(ai.getCardsIn(ZoneType.Hand), ai);
|
List<SpellAbility> all = ComputerUtilAbility.getSpellAbilities(ai.getCardsIn(ZoneType.Hand), ai);
|
||||||
for (final SpellAbility testSa : ComputerUtilAbility.getOriginalAndAltCostAbilities(all, ai)) {
|
for (final SpellAbility testSa : ComputerUtilAbility.getOriginalAndAltCostAbilities(all, ai)) {
|
||||||
ManaCost cost = testSa.getPayCosts().getTotalMana();
|
ManaCost cost = testSa.getPayCosts().getTotalMana();
|
||||||
boolean canPayWithAvailableColors = cost.canBePaidWithAvaliable(ColorSet.fromNames(
|
boolean canPayWithAvailableColors = cost.canBePaidWithAvaliable(ColorSet.fromNames(
|
||||||
ComputerUtilCost.getAvailableManaColors(ai, (List<Card>)null)).getColor());
|
ComputerUtilCost.getAvailableManaColors(ai, (List<Card>)null)).getColor());
|
||||||
|
|
||||||
if (cost.getCMC() == 0 && cost.countX() == 0) {
|
if (cost.getCMC() == 0 && cost.countX() == 0) {
|
||||||
// no mana cost, no need to activate this SA then (additional mana not needed)
|
// no mana cost, no need to activate this SA then (additional mana not needed)
|
||||||
continue;
|
continue;
|
||||||
} else if (cost.getColorProfile() != 0 && !canPayWithAvailableColors) {
|
} else if (cost.getColorProfile() != 0 && !canPayWithAvailableColors) {
|
||||||
// don't have one of each shard represented, may not be able to pay the cost
|
// don't have one of each shard represented, may not be able to pay the cost
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ComputerUtilAbility.getAbilitySourceName(testSa).equals(ComputerUtilAbility.getAbilitySourceName(sa))
|
if (ComputerUtilAbility.getAbilitySourceName(testSa).equals(ComputerUtilAbility.getAbilitySourceName(sa))
|
||||||
|| testSa.hasParam("AINoRecursiveCheck")) {
|
|| testSa.hasParam("AINoRecursiveCheck")) {
|
||||||
// prevent infinitely recursing mana ritual and other abilities with reentry
|
// prevent infinitely recursing mana ritual and other abilities with reentry
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
SpellAbility testSaNoCost = testSa.copyWithNoManaCost();
|
SpellAbility testSaNoCost = testSa.copyWithNoManaCost();
|
||||||
if (testSaNoCost == null) {
|
if (testSaNoCost == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
testSaNoCost.setActivatingPlayer(ai);
|
testSaNoCost.setActivatingPlayer(ai);
|
||||||
if (((PlayerControllerAi)ai.getController()).getAi().canPlaySa(testSaNoCost) == AiPlayDecision.WillPlay) {
|
if (((PlayerControllerAi)ai.getController()).getAi().canPlaySa(testSaNoCost) == AiPlayDecision.WillPlay) {
|
||||||
if (testSa.getHostCard().isPermanent() && !testSa.getHostCard().hasKeyword("Haste")
|
if (testSa.getHostCard().isPermanent() && !testSa.getHostCard().hasKeyword("Haste")
|
||||||
&& !ai.getGame().getPhaseHandler().is(PhaseType.MAIN2)) {
|
&& !ai.getGame().getPhaseHandler().is(PhaseType.MAIN2)) {
|
||||||
// AI will waste a ritual in Main 1 unless the casted permanent is a haste creature
|
// AI will waste a ritual in Main 1 unless the casted permanent is a haste creature
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (testSa.getHostCard().isInstant()) {
|
if (testSa.getHostCard().isInstant()) {
|
||||||
// AI is bad at choosing which instants are worth a Ritual
|
// AI is bad at choosing which instants are worth a Ritual
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// the AI is willing to play the spell
|
// the AI is willing to play the spell
|
||||||
if (!cardList.contains(testSa.getHostCard())) {
|
if (!cardList.contains(testSa.getHostCard())) {
|
||||||
cardList.add(testSa.getHostCard());
|
cardList.add(testSa.getHostCard());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CardCollection castableSpells = CardLists.filter(cardList,
|
CardCollection castableSpells = CardLists.filter(cardList,
|
||||||
Arrays.asList(
|
Arrays.asList(
|
||||||
CardPredicates.restriction(restrictValid.split(","), ai, host, sa),
|
CardPredicates.restriction(restrictValid.split(","), ai, host, sa),
|
||||||
CardPredicates.lessCMC(searchCMC),
|
CardPredicates.lessCMC(searchCMC),
|
||||||
Predicates.or(CardPredicates.isColorless(), CardPredicates.isColor(producedColor))));
|
Predicates.or(CardPredicates.isColorless(), CardPredicates.isColor(producedColor))));
|
||||||
|
|
||||||
// TODO: this will probably still waste the card from time to time. Somehow improve detection of castable material.
|
// TODO: this will probably still waste the card from time to time. Somehow improve detection of castable material.
|
||||||
return castableSpells.size() > 0;
|
return castableSpells.size() > 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,156 +1,156 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import forge.ai.ComputerUtil;
|
import forge.ai.ComputerUtil;
|
||||||
import forge.ai.ComputerUtilMana;
|
import forge.ai.ComputerUtilMana;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.card.CardStateName;
|
import forge.card.CardStateName;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardCollectionView;
|
import forge.game.card.CardCollectionView;
|
||||||
import forge.game.card.CardUtil;
|
import forge.game.card.CardUtil;
|
||||||
import forge.game.phase.PhaseHandler;
|
import forge.game.phase.PhaseHandler;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.player.PlayerActionConfirmMode;
|
import forge.game.player.PlayerActionConfirmMode;
|
||||||
import forge.game.replacement.ReplacementEffect;
|
import forge.game.replacement.ReplacementEffect;
|
||||||
import forge.game.replacement.ReplacementLayer;
|
import forge.game.replacement.ReplacementLayer;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
import forge.util.MyRandom;
|
import forge.util.MyRandom;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by friarsol on 1/23/15.
|
* Created by friarsol on 1/23/15.
|
||||||
*/
|
*/
|
||||||
public class ManifestAi extends SpellAbilityAi {
|
public class ManifestAi extends SpellAbilityAi {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
||||||
// Manifest doesn't have any "Pay X to manifest X triggers"
|
// Manifest doesn't have any "Pay X to manifest X triggers"
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.game.player.Player, forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.game.player.Player, forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the AI will play a SpellAbility based on its phase restrictions
|
* Checks if the AI will play a SpellAbility based on its phase restrictions
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkPhaseRestrictions(final Player ai, final SpellAbility sa, final PhaseHandler ph) {
|
protected boolean checkPhaseRestrictions(final Player ai, final SpellAbility sa, final PhaseHandler ph) {
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
// Only manifest things on your turn if sorcery speed, or would pump one of my creatures
|
// Only manifest things on your turn if sorcery speed, or would pump one of my creatures
|
||||||
if (ph.isPlayerTurn(ai)) {
|
if (ph.isPlayerTurn(ai)) {
|
||||||
if (ph.getPhase().isBefore(PhaseType.MAIN2)
|
if (ph.getPhase().isBefore(PhaseType.MAIN2)
|
||||||
&& !sa.hasParam("ActivationPhases")
|
&& !sa.hasParam("ActivationPhases")
|
||||||
&& !ComputerUtil.castSpellInMain1(ai, sa)) {
|
&& !ComputerUtil.castSpellInMain1(ai, sa)) {
|
||||||
boolean buff = false;
|
boolean buff = false;
|
||||||
for (Card c : ai.getCardsIn(ZoneType.Battlefield)) {
|
for (Card c : ai.getCardsIn(ZoneType.Battlefield)) {
|
||||||
if ("Creature".equals(c.getSVar("BuffedBy"))) {
|
if ("Creature".equals(c.getSVar("BuffedBy"))) {
|
||||||
buff = true;
|
buff = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!buff) {
|
if (!buff) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (!SpellAbilityAi.isSorcerySpeed(sa)) {
|
} else if (!SpellAbilityAi.isSorcerySpeed(sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// try to ambush attackers
|
// try to ambush attackers
|
||||||
if (ph.getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS)) {
|
if (ph.getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (source.getSVar("X").equals("Count$xPaid")) {
|
if (source.getSVar("X").equals("Count$xPaid")) {
|
||||||
// Handle either Manifest X cards, or Manifest 1 card and give it X P1P1s
|
// Handle either Manifest X cards, or Manifest 1 card and give it X P1P1s
|
||||||
// Set PayX here to maximum value.
|
// Set PayX here to maximum value.
|
||||||
int x = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
int x = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
||||||
source.setSVar("PayX", Integer.toString(x));
|
source.setSVar("PayX", Integer.toString(x));
|
||||||
if (x <= 0) {
|
if (x <= 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkApiLogic(final Player ai, final SpellAbility sa) {
|
protected boolean checkApiLogic(final Player ai, final SpellAbility sa) {
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
if (ComputerUtil.preventRunAwayActivations(sa)) {
|
if (ComputerUtil.preventRunAwayActivations(sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Library is empty, no Manifest
|
// Library is empty, no Manifest
|
||||||
final CardCollectionView library = ai.getCardsIn(ZoneType.Library);
|
final CardCollectionView library = ai.getCardsIn(ZoneType.Library);
|
||||||
if (library.isEmpty())
|
if (library.isEmpty())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// try not to mill himself with Manifest
|
// try not to mill himself with Manifest
|
||||||
if (library.size() < 5 && !ai.isCardInPlay("Laboratory Maniac")) {
|
if (library.size() < 5 && !ai.isCardInPlay("Laboratory Maniac")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check to ensure that there are no replacement effects that prevent creatures ETBing from library
|
// check to ensure that there are no replacement effects that prevent creatures ETBing from library
|
||||||
// (e.g. Grafdigger's Cage)
|
// (e.g. Grafdigger's Cage)
|
||||||
Card topCopy = CardUtil.getLKICopy(library.getFirst());
|
Card topCopy = CardUtil.getLKICopy(library.getFirst());
|
||||||
topCopy.setState(CardStateName.FaceDown, false);
|
topCopy.setState(CardStateName.FaceDown, false);
|
||||||
topCopy.setManifested(true);
|
topCopy.setManifested(true);
|
||||||
|
|
||||||
final Map<String, Object> repParams = Maps.newHashMap();
|
final Map<String, Object> repParams = Maps.newHashMap();
|
||||||
repParams.put("Event", "Moved");
|
repParams.put("Event", "Moved");
|
||||||
repParams.put("Affected", topCopy);
|
repParams.put("Affected", topCopy);
|
||||||
repParams.put("Origin", ZoneType.Library);
|
repParams.put("Origin", ZoneType.Library);
|
||||||
repParams.put("Destination", ZoneType.Battlefield);
|
repParams.put("Destination", ZoneType.Battlefield);
|
||||||
repParams.put("Source", sa.getHostCard());
|
repParams.put("Source", sa.getHostCard());
|
||||||
List<ReplacementEffect> list = game.getReplacementHandler().getReplacementList(repParams, ReplacementLayer.None);
|
List<ReplacementEffect> list = game.getReplacementHandler().getReplacementList(repParams, ReplacementLayer.None);
|
||||||
if (!list.isEmpty()) {
|
if (!list.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the AI can see the top card of the library, check it
|
// if the AI can see the top card of the library, check it
|
||||||
final Card topCard = library.getFirst();
|
final Card topCard = library.getFirst();
|
||||||
if (topCard.mayPlayerLook(ai)) {
|
if (topCard.mayPlayerLook(ai)) {
|
||||||
// try to avoid manifest a non Permanent
|
// try to avoid manifest a non Permanent
|
||||||
if (!topCard.isPermanent())
|
if (!topCard.isPermanent())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// do not manifest a card with X in its cost
|
// do not manifest a card with X in its cost
|
||||||
if (topCard.getManaCost().countX() > 0)
|
if (topCard.getManaCost().countX() > 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// try to avoid manifesting a creature with zero or less thoughness
|
// try to avoid manifesting a creature with zero or less thoughness
|
||||||
if (topCard.isCreature() && topCard.getNetToughness() <= 0)
|
if (topCard.isCreature() && topCard.getNetToughness() <= 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// card has ETBTrigger or ETBReplacement
|
// card has ETBTrigger or ETBReplacement
|
||||||
if (topCard.hasETBTrigger(false) || topCard.hasETBReplacement()) {
|
if (topCard.hasETBTrigger(false) || topCard.hasETBReplacement()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Probably should be a little more discerning on playing during OPPs turn
|
// Probably should be a little more discerning on playing during OPPs turn
|
||||||
if (SpellAbilityAi.playReusable(ai, sa)) {
|
if (SpellAbilityAi.playReusable(ai, sa)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (game.getPhaseHandler().is(PhaseType.COMBAT_DECLARE_ATTACKERS)) {
|
if (game.getPhaseHandler().is(PhaseType.COMBAT_DECLARE_ATTACKERS)) {
|
||||||
// Add blockers?
|
// Add blockers?
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (sa.isAbility()) {
|
if (sa.isAbility()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return MyRandom.getRandom().nextFloat() < .8;
|
return MyRandom.getRandom().nextFloat() < .8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,238 +1,238 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
|
|
||||||
import forge.ai.ComputerUtil;
|
import forge.ai.ComputerUtil;
|
||||||
import forge.ai.ComputerUtilMana;
|
import forge.ai.ComputerUtilMana;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardCollectionView;
|
import forge.game.card.CardCollectionView;
|
||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
import forge.game.card.CardPredicates;
|
import forge.game.card.CardPredicates;
|
||||||
import forge.game.phase.PhaseHandler;
|
import forge.game.phase.PhaseHandler;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.player.PlayerActionConfirmMode;
|
import forge.game.player.PlayerActionConfirmMode;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.TargetRestrictions;
|
import forge.game.spellability.TargetRestrictions;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
|
|
||||||
public class MillAi extends SpellAbilityAi {
|
public class MillAi extends SpellAbilityAi {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkAiLogic(final Player ai, final SpellAbility sa, final String aiLogic) {
|
protected boolean checkAiLogic(final Player ai, final SpellAbility sa, final String aiLogic) {
|
||||||
PhaseHandler ph = ai.getGame().getPhaseHandler();
|
PhaseHandler ph = ai.getGame().getPhaseHandler();
|
||||||
|
|
||||||
if (aiLogic.equals("Main1")) {
|
if (aiLogic.equals("Main1")) {
|
||||||
if (ph.getPhase().isBefore(PhaseType.MAIN2) && !sa.hasParam("ActivationPhases")
|
if (ph.getPhase().isBefore(PhaseType.MAIN2) && !sa.hasParam("ActivationPhases")
|
||||||
&& !ComputerUtil.castSpellInMain1(ai, sa)) {
|
&& !ComputerUtil.castSpellInMain1(ai, sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (aiLogic.equals("EndOfOppTurn")) {
|
} else if (aiLogic.equals("EndOfOppTurn")) {
|
||||||
if (!(ph.is(PhaseType.END_OF_TURN) && ph.getNextTurn().equals(ai))) {
|
if (!(ph.is(PhaseType.END_OF_TURN) && ph.getNextTurn().equals(ai))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (aiLogic.equals("LilianaMill")) {
|
} else if (aiLogic.equals("LilianaMill")) {
|
||||||
// Only mill if a "Raise Dead" target is available, in case of control decks with few creatures
|
// Only mill if a "Raise Dead" target is available, in case of control decks with few creatures
|
||||||
if (CardLists.filter(ai.getCardsIn(ZoneType.Graveyard), CardPredicates.Presets.CREATURES).size() < 1) {
|
if (CardLists.filter(ai.getCardsIn(ZoneType.Graveyard), CardPredicates.Presets.CREATURES).size() < 1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkPhaseRestrictions(final Player ai, final SpellAbility sa, final PhaseHandler ph) {
|
protected boolean checkPhaseRestrictions(final Player ai, final SpellAbility sa, final PhaseHandler ph) {
|
||||||
if ("ExileAndPlayUntilEOT".equals(sa.getParam("AILogic"))) {
|
if ("ExileAndPlayUntilEOT".equals(sa.getParam("AILogic"))) {
|
||||||
return ph.is(PhaseType.MAIN1) && ph.isPlayerTurn(ai); // try to maximize the chance of being able to play the card this turn
|
return ph.is(PhaseType.MAIN1) && ph.isPlayerTurn(ai); // try to maximize the chance of being able to play the card this turn
|
||||||
} else if ("ExileAndPlayOrDealDamage".equals(sa.getParam("AILogic"))) {
|
} else if ("ExileAndPlayOrDealDamage".equals(sa.getParam("AILogic"))) {
|
||||||
return (ph.is(PhaseType.MAIN1) || ph.is(PhaseType.MAIN2)) && ph.isPlayerTurn(ai); // Chandra, Torch of Defiance and similar
|
return (ph.is(PhaseType.MAIN1) || ph.is(PhaseType.MAIN2)) && ph.isPlayerTurn(ai); // Chandra, Torch of Defiance and similar
|
||||||
}
|
}
|
||||||
if ("You".equals(sa.getParam("Defined")) && !(!SpellAbilityAi.isSorcerySpeed(sa) && ph.is(PhaseType.END_OF_TURN)
|
if ("You".equals(sa.getParam("Defined")) && !(!SpellAbilityAi.isSorcerySpeed(sa) && ph.is(PhaseType.END_OF_TURN)
|
||||||
&& ph.getNextTurn().equals(ai))) {
|
&& ph.getNextTurn().equals(ai))) {
|
||||||
return false; // only self-mill at opponent EOT
|
return false; // only self-mill at opponent EOT
|
||||||
}
|
}
|
||||||
if (sa.getHostCard().isCreature() && sa.getPayCosts().hasTapCost()) {
|
if (sa.getHostCard().isCreature() && sa.getPayCosts().hasTapCost()) {
|
||||||
if (!(ph.is(PhaseType.END_OF_TURN) && ph.getNextTurn().equals(ai))) {
|
if (!(ph.is(PhaseType.END_OF_TURN) && ph.getNextTurn().equals(ai))) {
|
||||||
// creatures with a tap cost to mill (e.g. Doorkeeper) should be activated at the opponent's end step
|
// creatures with a tap cost to mill (e.g. Doorkeeper) should be activated at the opponent's end step
|
||||||
// because they are also potentially useful for combat
|
// because they are also potentially useful for combat
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkApiLogic(final Player ai, final SpellAbility sa) {
|
protected boolean checkApiLogic(final Player ai, final SpellAbility sa) {
|
||||||
/*
|
/*
|
||||||
* TODO:
|
* TODO:
|
||||||
* - logic in targetAI looks dodgy
|
* - logic in targetAI looks dodgy
|
||||||
* - decide whether to self-mill (eg. delirium, dredge, bad top card)
|
* - decide whether to self-mill (eg. delirium, dredge, bad top card)
|
||||||
* - interrupt opponent's top card (eg. Brainstorm, good top card)
|
* - interrupt opponent's top card (eg. Brainstorm, good top card)
|
||||||
* - check for Laboratory Maniac effect (needs to check for actual
|
* - check for Laboratory Maniac effect (needs to check for actual
|
||||||
* effect due to possibility of "lose abilities" effect)
|
* effect due to possibility of "lose abilities" effect)
|
||||||
*/
|
*/
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
if (ComputerUtil.preventRunAwayActivations(sa)) {
|
if (ComputerUtil.preventRunAwayActivations(sa)) {
|
||||||
return false; // prevents mill 0 infinite loop?
|
return false; // prevents mill 0 infinite loop?
|
||||||
}
|
}
|
||||||
|
|
||||||
if (("You".equals(sa.getParam("Defined")) || "Player".equals(sa.getParam("Defined")))
|
if (("You".equals(sa.getParam("Defined")) || "Player".equals(sa.getParam("Defined")))
|
||||||
&& ai.getCardsIn(ZoneType.Library).size() < 10) {
|
&& ai.getCardsIn(ZoneType.Library).size() < 10) {
|
||||||
return false; // prevent self and each player mill when library is small
|
return false; // prevent self and each player mill when library is small
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.getTargetRestrictions() != null && !targetAI(ai, sa, false)) {
|
if (sa.getTargetRestrictions() != null && !targetAI(ai, sa, false)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((sa.getParam("NumCards").equals("X") || sa.getParam("NumCards").equals("Z"))
|
if ((sa.getParam("NumCards").equals("X") || sa.getParam("NumCards").equals("Z"))
|
||||||
&& source.getSVar("X").startsWith("Count$xPaid")) {
|
&& source.getSVar("X").startsWith("Count$xPaid")) {
|
||||||
// Set PayX here to maximum value.
|
// Set PayX here to maximum value.
|
||||||
final int cardsToDiscard = getNumToDiscard(ai, sa);
|
final int cardsToDiscard = getNumToDiscard(ai, sa);
|
||||||
source.setSVar("PayX", Integer.toString(cardsToDiscard));
|
source.setSVar("PayX", Integer.toString(cardsToDiscard));
|
||||||
if (cardsToDiscard <= 0) {
|
if (cardsToDiscard <= 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean targetAI(final Player ai, final SpellAbility sa, final boolean mandatory) {
|
private boolean targetAI(final Player ai, final SpellAbility sa, final boolean mandatory) {
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
|
|
||||||
if (tgt != null) {
|
if (tgt != null) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
final Map<Player, Integer> list = Maps.newHashMap();
|
final Map<Player, Integer> list = Maps.newHashMap();
|
||||||
for (final Player o : ai.getOpponents()) {
|
for (final Player o : ai.getOpponents()) {
|
||||||
if (!sa.canTarget(o)) {
|
if (!sa.canTarget(o)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// need to set as target for some calcuate
|
// need to set as target for some calcuate
|
||||||
sa.getTargets().add(o);
|
sa.getTargets().add(o);
|
||||||
final int numCards = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("NumCards"), sa);
|
final int numCards = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("NumCards"), sa);
|
||||||
sa.getTargets().remove(o);
|
sa.getTargets().remove(o);
|
||||||
|
|
||||||
// if it would mill none, try other one
|
// if it would mill none, try other one
|
||||||
if (numCards <= 0) {
|
if (numCards <= 0) {
|
||||||
if ((sa.getParam("NumCards").equals("X") || sa.getParam("NumCards").equals("Z")))
|
if ((sa.getParam("NumCards").equals("X") || sa.getParam("NumCards").equals("Z")))
|
||||||
{
|
{
|
||||||
if (source.getSVar("X").startsWith("Count$xPaid")) {
|
if (source.getSVar("X").startsWith("Count$xPaid")) {
|
||||||
// Spell is PayX based
|
// Spell is PayX based
|
||||||
} else if (source.getSVar("X").startsWith("Remembered$ChromaSource")) {
|
} else if (source.getSVar("X").startsWith("Remembered$ChromaSource")) {
|
||||||
// Cards like Sanity Grinding
|
// Cards like Sanity Grinding
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final CardCollectionView pLibrary = o.getCardsIn(ZoneType.Library);
|
final CardCollectionView pLibrary = o.getCardsIn(ZoneType.Library);
|
||||||
if (pLibrary.isEmpty()) {
|
if (pLibrary.isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if that player can be miled, select this one.
|
// if that player can be miled, select this one.
|
||||||
if (numCards >= pLibrary.size()) {
|
if (numCards >= pLibrary.size()) {
|
||||||
sa.getTargets().add(o);
|
sa.getTargets().add(o);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
list.put(o, numCards);
|
list.put(o, numCards);
|
||||||
}
|
}
|
||||||
|
|
||||||
// can't target opponent?
|
// can't target opponent?
|
||||||
if (list.isEmpty()) {
|
if (list.isEmpty()) {
|
||||||
if (mandatory && sa.canTarget(ai)) {
|
if (mandatory && sa.canTarget(ai)) {
|
||||||
sa.getTargets().add(ai);
|
sa.getTargets().add(ai);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// TODO Obscure case when you know what your top card is so you might?
|
// TODO Obscure case when you know what your top card is so you might?
|
||||||
// want to mill yourself here
|
// want to mill yourself here
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// select Player which would cause the most damage
|
// select Player which would cause the most damage
|
||||||
// JAVA 1.8 use Map.Entry.comparingByValue()
|
// JAVA 1.8 use Map.Entry.comparingByValue()
|
||||||
Map.Entry<Player, Integer> max = Collections.max(list.entrySet(), new Comparator<Map.Entry<Player,Integer>>(){
|
Map.Entry<Player, Integer> max = Collections.max(list.entrySet(), new Comparator<Map.Entry<Player,Integer>>(){
|
||||||
@Override
|
@Override
|
||||||
public int compare(Map.Entry<Player, Integer> o1, Map.Entry<Player, Integer> o2) {
|
public int compare(Map.Entry<Player, Integer> o1, Map.Entry<Player, Integer> o2) {
|
||||||
return o1.getValue() - o2.getValue();
|
return o1.getValue() - o2.getValue();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
sa.getTargets().add(max.getKey());
|
sa.getTargets().add(max.getKey());
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean chkAIDrawback(SpellAbility sa, Player aiPlayer) {
|
public boolean chkAIDrawback(SpellAbility sa, Player aiPlayer) {
|
||||||
return targetAI(aiPlayer, sa, true);
|
return targetAI(aiPlayer, sa, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
|
||||||
if (!targetAI(aiPlayer, sa, mandatory)) {
|
if (!targetAI(aiPlayer, sa, mandatory)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
if (sa.getParam("NumCards").equals("X") && source.getSVar("X").equals("Count$xPaid")) {
|
if (sa.getParam("NumCards").equals("X") && source.getSVar("X").equals("Count$xPaid")) {
|
||||||
// Set PayX here to maximum value.
|
// Set PayX here to maximum value.
|
||||||
final int cardsToDiscard = getNumToDiscard(aiPlayer, sa);
|
final int cardsToDiscard = getNumToDiscard(aiPlayer, sa);
|
||||||
source.setSVar("PayX", Integer.toString(cardsToDiscard));
|
source.setSVar("PayX", Integer.toString(cardsToDiscard));
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.game.player.Player, forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.game.player.Player, forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* return num of cards to discard
|
* return num of cards to discard
|
||||||
*/
|
*/
|
||||||
private int getNumToDiscard(final Player ai, final SpellAbility sa) {
|
private int getNumToDiscard(final Player ai, final SpellAbility sa) {
|
||||||
// need list of affected players
|
// need list of affected players
|
||||||
List<Player> list = Lists.newArrayList();
|
List<Player> list = Lists.newArrayList();
|
||||||
if (sa.usesTargeting()) {
|
if (sa.usesTargeting()) {
|
||||||
list.addAll(Lists.newArrayList(sa.getTargets().getTargetPlayers()));
|
list.addAll(Lists.newArrayList(sa.getTargets().getTargetPlayers()));
|
||||||
} else {
|
} else {
|
||||||
list.addAll(AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("Defined"), sa));
|
list.addAll(AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("Defined"), sa));
|
||||||
}
|
}
|
||||||
|
|
||||||
// get targeted or defined Player with largest library
|
// get targeted or defined Player with largest library
|
||||||
// TODO in Java 8 find better way
|
// TODO in Java 8 find better way
|
||||||
final Player m = Collections.max(list, new Comparator<Player>() {
|
final Player m = Collections.max(list, new Comparator<Player>() {
|
||||||
@Override
|
@Override
|
||||||
public int compare(Player arg0, Player arg1) {
|
public int compare(Player arg0, Player arg1) {
|
||||||
return arg0.getCardsIn(ZoneType.Library).size() - arg1.getCardsIn(ZoneType.Library).size();
|
return arg0.getCardsIn(ZoneType.Library).size() - arg1.getCardsIn(ZoneType.Library).size();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
int cardsToDiscard = m.getCardsIn(ZoneType.Library).size();
|
int cardsToDiscard = m.getCardsIn(ZoneType.Library).size();
|
||||||
|
|
||||||
// if ai is in affected list too, try to not mill himself
|
// if ai is in affected list too, try to not mill himself
|
||||||
if (list.contains(ai)) {
|
if (list.contains(ai)) {
|
||||||
cardsToDiscard = Math.min(ai.getCardsIn(ZoneType.Library).size() - 5, cardsToDiscard);
|
cardsToDiscard = Math.min(ai.getCardsIn(ZoneType.Library).size() - 5, cardsToDiscard);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Math.min(ComputerUtilMana.determineLeftoverMana(sa, ai), cardsToDiscard);
|
return Math.min(ComputerUtilMana.determineLeftoverMana(sa, ai), cardsToDiscard);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,36 +1,36 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
|
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
|
|
||||||
public class MustAttackAi extends SpellAbilityAi {
|
public class MustAttackAi extends SpellAbilityAi {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
||||||
// disabled for the AI for now. Only for Gideon Jura at this time.
|
// disabled for the AI for now. Only for Gideon Jura at this time.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean chkAIDrawback(SpellAbility sa, Player aiPlayer) {
|
public boolean chkAIDrawback(SpellAbility sa, Player aiPlayer) {
|
||||||
// AI should only activate this during Human's turn
|
// AI should only activate this during Human's turn
|
||||||
// TODO - implement AI
|
// TODO - implement AI
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see forge.card.abilityfactory.SpellAiLogic#doTriggerAINoCost(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility, boolean)
|
* @see forge.card.abilityfactory.SpellAiLogic#doTriggerAINoCost(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility, boolean)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
|
||||||
|
|
||||||
boolean chance;
|
boolean chance;
|
||||||
|
|
||||||
// TODO - implement AI
|
// TODO - implement AI
|
||||||
chance = false;
|
chance = false;
|
||||||
|
|
||||||
return chance;
|
return chance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,101 +1,101 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
|
|
||||||
import forge.ai.ComputerUtil;
|
import forge.ai.ComputerUtil;
|
||||||
import forge.ai.ComputerUtilCard;
|
import forge.ai.ComputerUtilCard;
|
||||||
import forge.ai.ComputerUtilCombat;
|
import forge.ai.ComputerUtilCombat;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
import forge.game.card.CardPredicates;
|
import forge.game.card.CardPredicates;
|
||||||
import forge.game.combat.CombatUtil;
|
import forge.game.combat.CombatUtil;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.TargetRestrictions;
|
import forge.game.spellability.TargetRestrictions;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class MustBlockAi extends SpellAbilityAi {
|
public class MustBlockAi extends SpellAbilityAi {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
||||||
// disabled for the AI until he/she can make decisions about who to make
|
// disabled for the AI until he/she can make decisions about who to make
|
||||||
// block
|
// block
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean chkAIDrawback(SpellAbility sa, Player aiPlayer) {
|
public boolean chkAIDrawback(SpellAbility sa, Player aiPlayer) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(final Player ai, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(final Player ai, SpellAbility sa, boolean mandatory) {
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
final TargetRestrictions abTgt = sa.getTargetRestrictions();
|
final TargetRestrictions abTgt = sa.getTargetRestrictions();
|
||||||
|
|
||||||
// only use on creatures that can attack
|
// only use on creatures that can attack
|
||||||
if (!ai.getGame().getPhaseHandler().getPhase().isBefore(PhaseType.MAIN2)) {
|
if (!ai.getGame().getPhaseHandler().getPhase().isBefore(PhaseType.MAIN2)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Card attacker = null;
|
Card attacker = null;
|
||||||
if (sa.hasParam("DefinedAttacker")) {
|
if (sa.hasParam("DefinedAttacker")) {
|
||||||
final List<Card> cards = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("DefinedAttacker"), sa);
|
final List<Card> cards = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("DefinedAttacker"), sa);
|
||||||
if (cards.isEmpty()) {
|
if (cards.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
attacker = cards.get(0);
|
attacker = cards.get(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attacker == null) {
|
if (attacker == null) {
|
||||||
attacker = source;
|
attacker = source;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Card definedAttacker = attacker;
|
final Card definedAttacker = attacker;
|
||||||
|
|
||||||
boolean chance = false;
|
boolean chance = false;
|
||||||
|
|
||||||
if (abTgt != null) {
|
if (abTgt != null) {
|
||||||
List<Card> list = CardLists.filter(ComputerUtil.getOpponentFor(ai).getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.CREATURES);
|
List<Card> list = CardLists.filter(ComputerUtil.getOpponentFor(ai).getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.CREATURES);
|
||||||
list = CardLists.getTargetableCards(list, sa);
|
list = CardLists.getTargetableCards(list, sa);
|
||||||
list = CardLists.getValidCards(list, abTgt.getValidTgts(), source.getController(), source, sa);
|
list = CardLists.getValidCards(list, abTgt.getValidTgts(), source.getController(), source, sa);
|
||||||
list = CardLists.filter(list, new Predicate<Card>() {
|
list = CardLists.filter(list, new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(final Card c) {
|
public boolean apply(final Card c) {
|
||||||
boolean tapped = c.isTapped();
|
boolean tapped = c.isTapped();
|
||||||
c.setTapped(false);
|
c.setTapped(false);
|
||||||
if (!CombatUtil.canBlock(definedAttacker, c)) {
|
if (!CombatUtil.canBlock(definedAttacker, c)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (ComputerUtilCombat.canDestroyAttacker(ai, definedAttacker, c, null, false)) {
|
if (ComputerUtilCombat.canDestroyAttacker(ai, definedAttacker, c, null, false)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!ComputerUtilCombat.canDestroyBlocker(ai, c, definedAttacker, null, false)) {
|
if (!ComputerUtilCombat.canDestroyBlocker(ai, c, definedAttacker, null, false)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
c.setTapped(tapped);
|
c.setTapped(tapped);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (list.isEmpty()) {
|
if (list.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
final Card blocker = ComputerUtilCard.getBestCreatureAI(list);
|
final Card blocker = ComputerUtilCard.getBestCreatureAI(list);
|
||||||
if (blocker == null) {
|
if (blocker == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
sa.getTargets().add(blocker);
|
sa.getTargets().add(blocker);
|
||||||
chance = true;
|
chance = true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return chance;
|
return chance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,45 +1,45 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.ai.SpellApiToAi;
|
import forge.ai.SpellApiToAi;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.player.PlayerActionConfirmMode;
|
import forge.game.player.PlayerActionConfirmMode;
|
||||||
import forge.game.spellability.AbilityStatic;
|
import forge.game.spellability.AbilityStatic;
|
||||||
import forge.game.spellability.AbilitySub;
|
import forge.game.spellability.AbilitySub;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: Write javadoc for this type.
|
* TODO: Write javadoc for this type.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class PeekAndRevealAi extends SpellAbilityAi {
|
public class PeekAndRevealAi extends SpellAbilityAi {
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see forge.card.abilityfactory.SpellAiLogic#canPlayAI(forge.game.player.Player, forge.card.spellability.SpellAbility)
|
* @see forge.card.abilityfactory.SpellAiLogic#canPlayAI(forge.game.player.Player, forge.card.spellability.SpellAbility)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
||||||
if (sa instanceof AbilityStatic) {
|
if (sa instanceof AbilityStatic) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if ("Main2".equals(sa.getParam("AILogic"))) {
|
if ("Main2".equals(sa.getParam("AILogic"))) {
|
||||||
if (aiPlayer.getGame().getPhaseHandler().getPhase().isBefore(PhaseType.MAIN2)) {
|
if (aiPlayer.getGame().getPhaseHandler().getPhase().isBefore(PhaseType.MAIN2)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// So far this only appears on Triggers, but will expand
|
// So far this only appears on Triggers, but will expand
|
||||||
// once things get converted from Dig + NoMove
|
// once things get converted from Dig + NoMove
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.game.player.Player, forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.game.player.Player, forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
||||||
AbilitySub subAb = sa.getSubAbility();
|
AbilitySub subAb = sa.getSubAbility();
|
||||||
return subAb != null && SpellApiToAi.Converter.get(subAb.getApi()).chkDrawbackWithSubs(player, subAb);
|
return subAb != null && SpellApiToAi.Converter.get(subAb.getApi()).chkDrawbackWithSubs(player, subAb);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,132 +1,132 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import forge.ai.ComputerUtil;
|
import forge.ai.ComputerUtil;
|
||||||
import forge.ai.ComputerUtilCard;
|
import forge.ai.ComputerUtilCard;
|
||||||
import forge.ai.ComputerUtilCost;
|
import forge.ai.ComputerUtilCost;
|
||||||
import forge.card.mana.ManaCost;
|
import forge.card.mana.ManaCost;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardUtil;
|
import forge.game.card.CardUtil;
|
||||||
import forge.game.phase.PhaseHandler;
|
import forge.game.phase.PhaseHandler;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AbilityFactory for Creature Spells.
|
* AbilityFactory for Creature Spells.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class PermanentCreatureAi extends PermanentAi {
|
public class PermanentCreatureAi extends PermanentAi {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the AI will play a SpellAbility with the specified AiLogic
|
* Checks if the AI will play a SpellAbility with the specified AiLogic
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkAiLogic(final Player ai, final SpellAbility sa, final String aiLogic) {
|
protected boolean checkAiLogic(final Player ai, final SpellAbility sa, final String aiLogic) {
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
|
|
||||||
if ("Never".equals(aiLogic)) {
|
if ("Never".equals(aiLogic)) {
|
||||||
return false;
|
return false;
|
||||||
} else if ("ZeroToughness".equals(aiLogic)) {
|
} else if ("ZeroToughness".equals(aiLogic)) {
|
||||||
// If Creature has Zero Toughness, make sure some static ability is in play
|
// If Creature has Zero Toughness, make sure some static ability is in play
|
||||||
// That will grant a toughness bonus
|
// That will grant a toughness bonus
|
||||||
|
|
||||||
final Card copy = CardUtil.getLKICopy(sa.getHostCard());
|
final Card copy = CardUtil.getLKICopy(sa.getHostCard());
|
||||||
|
|
||||||
ComputerUtilCard.applyStaticContPT(game, copy, null);
|
ComputerUtilCard.applyStaticContPT(game, copy, null);
|
||||||
|
|
||||||
if (copy.getNetToughness() <= 0) {
|
if (copy.getNetToughness() <= 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the AI will play a SpellAbility based on its phase restrictions
|
* Checks if the AI will play a SpellAbility based on its phase restrictions
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkPhaseRestrictions(final Player ai, final SpellAbility sa, final PhaseHandler ph) {
|
protected boolean checkPhaseRestrictions(final Player ai, final SpellAbility sa, final PhaseHandler ph) {
|
||||||
|
|
||||||
final Card card = sa.getHostCard();
|
final Card card = sa.getHostCard();
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
|
|
||||||
// FRF Dash Keyword
|
// FRF Dash Keyword
|
||||||
if (sa.isDash()) {
|
if (sa.isDash()) {
|
||||||
//only checks that the dashed creature will attack
|
//only checks that the dashed creature will attack
|
||||||
if (ph.isPlayerTurn(ai) && ph.getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS)) {
|
if (ph.isPlayerTurn(ai) && ph.getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS)) {
|
||||||
if (ai.hasKeyword("Skip your next combat phase."))
|
if (ai.hasKeyword("Skip your next combat phase."))
|
||||||
return false;
|
return false;
|
||||||
if (ComputerUtilCost.canPayCost(sa.getHostCard().getSpellPermanent(), ai)) {
|
if (ComputerUtilCost.canPayCost(sa.getHostCard().getSpellPermanent(), ai)) {
|
||||||
//do not dash if creature can be played normally
|
//do not dash if creature can be played normally
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Card dashed = CardUtil.getLKICopy(sa.getHostCard());
|
Card dashed = CardUtil.getLKICopy(sa.getHostCard());
|
||||||
dashed.setSickness(false);
|
dashed.setSickness(false);
|
||||||
return ComputerUtilCard.doesSpecifiedCreatureAttackAI(ai, dashed);
|
return ComputerUtilCard.doesSpecifiedCreatureAttackAI(ai, dashed);
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prevent the computer from summoning Ball Lightning type creatures
|
// Prevent the computer from summoning Ball Lightning type creatures
|
||||||
// after attacking
|
// after attacking
|
||||||
if (card.hasSVar("EndOfTurnLeavePlay")
|
if (card.hasSVar("EndOfTurnLeavePlay")
|
||||||
&& (!ph.isPlayerTurn(ai) || ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
&& (!ph.isPlayerTurn(ai) || ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
||||||
|| ai.hasKeyword("Skip your next combat phase."))) {
|
|| ai.hasKeyword("Skip your next combat phase."))) {
|
||||||
// AiPlayDecision.AnotherTime
|
// AiPlayDecision.AnotherTime
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// save cards with flash for surprise blocking
|
// save cards with flash for surprise blocking
|
||||||
if (card.hasKeyword("Flash")
|
if (card.hasKeyword("Flash")
|
||||||
&& (ai.isUnlimitedHandSize() || ai.getCardsIn(ZoneType.Hand).size() <= ai.getMaxHandSize()
|
&& (ai.isUnlimitedHandSize() || ai.getCardsIn(ZoneType.Hand).size() <= ai.getMaxHandSize()
|
||||||
|| ph.getPhase().isBefore(PhaseType.END_OF_TURN))
|
|| ph.getPhase().isBefore(PhaseType.END_OF_TURN))
|
||||||
&& ai.getManaPool().totalMana() <= 0
|
&& ai.getManaPool().totalMana() <= 0
|
||||||
&& (ph.isPlayerTurn(ai) || ph.getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS))
|
&& (ph.isPlayerTurn(ai) || ph.getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS))
|
||||||
&& (!card.hasETBTrigger(true) || card.hasSVar("AmbushAI")) && game.getStack().isEmpty()
|
&& (!card.hasETBTrigger(true) || card.hasSVar("AmbushAI")) && game.getStack().isEmpty()
|
||||||
&& !ComputerUtil.castPermanentInMain1(ai, sa)) {
|
&& !ComputerUtil.castPermanentInMain1(ai, sa)) {
|
||||||
// AiPlayDecision.AnotherTime;
|
// AiPlayDecision.AnotherTime;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.checkPhaseRestrictions(ai, sa, ph);
|
return super.checkPhaseRestrictions(ai, sa, ph);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkApiLogic(Player ai, SpellAbility sa) {
|
protected boolean checkApiLogic(Player ai, SpellAbility sa) {
|
||||||
|
|
||||||
if (!super.checkApiLogic(ai, sa)) {
|
if (!super.checkApiLogic(ai, sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Card card = sa.getHostCard();
|
final Card card = sa.getHostCard();
|
||||||
final ManaCost mana = card.getManaCost();
|
final ManaCost mana = card.getManaCost();
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Checks if the creature will have non-positive toughness after
|
* Checks if the creature will have non-positive toughness after
|
||||||
* applying static effects. Exceptions: 1. has "etbCounter" keyword (eg.
|
* applying static effects. Exceptions: 1. has "etbCounter" keyword (eg.
|
||||||
* Endless One) 2. paid non-zero for X cost 3. has ETB trigger 4. has
|
* Endless One) 2. paid non-zero for X cost 3. has ETB trigger 4. has
|
||||||
* ETB replacement 5. has NoZeroToughnessAI svar (eg. Veteran Warleader)
|
* ETB replacement 5. has NoZeroToughnessAI svar (eg. Veteran Warleader)
|
||||||
*
|
*
|
||||||
* 1. and 2. should probably be merged and applied on the card after
|
* 1. and 2. should probably be merged and applied on the card after
|
||||||
* checking for effects like Doubling Season for getNetToughness to see
|
* checking for effects like Doubling Season for getNetToughness to see
|
||||||
* the true value. 3. currently allows the AI to suicide creatures as
|
* the true value. 3. currently allows the AI to suicide creatures as
|
||||||
* long as it has an ETB. Maybe it should check if said ETB is actually
|
* long as it has an ETB. Maybe it should check if said ETB is actually
|
||||||
* worth it. Not sure what 4. is for. 5. needs to be updated to ensure
|
* worth it. Not sure what 4. is for. 5. needs to be updated to ensure
|
||||||
* that the net toughness is still positive after static effects.
|
* that the net toughness is still positive after static effects.
|
||||||
*/
|
*/
|
||||||
final Card copy = CardUtil.getLKICopy(sa.getHostCard());
|
final Card copy = CardUtil.getLKICopy(sa.getHostCard());
|
||||||
ComputerUtilCard.applyStaticContPT(game, copy, null);
|
ComputerUtilCard.applyStaticContPT(game, copy, null);
|
||||||
if (copy.getNetToughness() <= 0 && !copy.hasStartOfKeyword("etbCounter") && mana.countX() == 0
|
if (copy.getNetToughness() <= 0 && !copy.hasStartOfKeyword("etbCounter") && mana.countX() == 0
|
||||||
&& !copy.hasETBTrigger(false) && !copy.hasETBReplacement() && !copy.hasSVar("NoZeroToughnessAI")) {
|
&& !copy.hasETBTrigger(false) && !copy.hasETBReplacement() && !copy.hasSVar("NoZeroToughnessAI")) {
|
||||||
// AiPlayDecision.WouldBecomeZeroToughnessCreature
|
// AiPlayDecision.WouldBecomeZeroToughnessCreature
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,64 +1,64 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import forge.ai.ComputerUtilAbility;
|
import forge.ai.ComputerUtilAbility;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.ability.AbilityFactory;
|
import forge.game.ability.AbilityFactory;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardCollection;
|
import forge.game.card.CardCollection;
|
||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.TargetRestrictions;
|
import forge.game.spellability.TargetRestrictions;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AbilityFactory for Creature Spells.
|
* AbilityFactory for Creature Spells.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class PermanentNoncreatureAi extends PermanentAi {
|
public class PermanentNoncreatureAi extends PermanentAi {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkAiLogic(final Player ai, final SpellAbility sa, final String aiLogic) {
|
protected boolean checkAiLogic(final Player ai, final SpellAbility sa, final String aiLogic) {
|
||||||
if ("Never".equals(aiLogic) || "DontCast".equals(aiLogic)) {
|
if ("Never".equals(aiLogic) || "DontCast".equals(aiLogic)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The rest of the logic not covered by the canPlayAI template is defined
|
* The rest of the logic not covered by the canPlayAI template is defined
|
||||||
* here
|
* here
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkApiLogic(final Player ai, final SpellAbility sa) {
|
protected boolean checkApiLogic(final Player ai, final SpellAbility sa) {
|
||||||
|
|
||||||
if (!super.checkApiLogic(ai, sa))
|
if (!super.checkApiLogic(ai, sa))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
final Card host = sa.getHostCard();
|
final Card host = sa.getHostCard();
|
||||||
final String sourceName = ComputerUtilAbility.getAbilitySourceName(sa);
|
final String sourceName = ComputerUtilAbility.getAbilitySourceName(sa);
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
|
|
||||||
// Check for valid targets before casting
|
// Check for valid targets before casting
|
||||||
if (host.hasSVar("OblivionRing")) {
|
if (host.hasSVar("OblivionRing")) {
|
||||||
SpellAbility effectExile = AbilityFactory.getAbility(host.getSVar("TrigExile"), host);
|
SpellAbility effectExile = AbilityFactory.getAbility(host.getSVar("TrigExile"), host);
|
||||||
final ZoneType origin = ZoneType.listValueOf(effectExile.getParam("Origin")).get(0);
|
final ZoneType origin = ZoneType.listValueOf(effectExile.getParam("Origin")).get(0);
|
||||||
final TargetRestrictions tgt = effectExile.getTargetRestrictions();
|
final TargetRestrictions tgt = effectExile.getTargetRestrictions();
|
||||||
final CardCollection list = CardLists.getValidCards(game.getCardsIn(origin), tgt.getValidTgts(), ai, host,
|
final CardCollection list = CardLists.getValidCards(game.getCardsIn(origin), tgt.getValidTgts(), ai, host,
|
||||||
effectExile);
|
effectExile);
|
||||||
CardCollection targets = CardLists.getTargetableCards(list, sa);
|
CardCollection targets = CardLists.getTargetableCards(list, sa);
|
||||||
if (sourceName.equals("Suspension Field")
|
if (sourceName.equals("Suspension Field")
|
||||||
|| sourceName.equals("Detention Sphere")) {
|
|| sourceName.equals("Detention Sphere")) {
|
||||||
// existing "exile until leaves" enchantments only target
|
// existing "exile until leaves" enchantments only target
|
||||||
// opponent's permanents
|
// opponent's permanents
|
||||||
// TODO: consider replacing the condition with host.hasSVar("OblivionRing")
|
// TODO: consider replacing the condition with host.hasSVar("OblivionRing")
|
||||||
targets = CardLists.filterControlledBy(targets, ai.getOpponents());
|
targets = CardLists.filterControlledBy(targets, ai.getOpponents());
|
||||||
}
|
}
|
||||||
if (targets.isEmpty()) {
|
if (targets.isEmpty()) {
|
||||||
// AiPlayDecision.AnotherTime
|
// AiPlayDecision.AnotherTime
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,132 +1,132 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import com.google.common.base.Predicates;
|
import com.google.common.base.Predicates;
|
||||||
import forge.ai.ComputerUtil;
|
import forge.ai.ComputerUtil;
|
||||||
import forge.ai.ComputerUtilCard;
|
import forge.ai.ComputerUtilCard;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardCollectionView;
|
import forge.game.card.CardCollectionView;
|
||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
import forge.game.card.CardPredicates;
|
import forge.game.card.CardPredicates;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.TargetRestrictions;
|
import forge.game.spellability.TargetRestrictions;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
import forge.util.MyRandom;
|
import forge.util.MyRandom;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
public class PhasesAi extends SpellAbilityAi {
|
public class PhasesAi extends SpellAbilityAi {
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
||||||
// This still needs to be fleshed out
|
// This still needs to be fleshed out
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
|
|
||||||
final Random r = MyRandom.getRandom();
|
final Random r = MyRandom.getRandom();
|
||||||
boolean randomReturn = r.nextFloat() <= Math.pow(.6667, sa.getActivationsThisTurn());
|
boolean randomReturn = r.nextFloat() <= Math.pow(.6667, sa.getActivationsThisTurn());
|
||||||
|
|
||||||
List<Card> tgtCards;
|
List<Card> tgtCards;
|
||||||
if (tgt == null) {
|
if (tgt == null) {
|
||||||
tgtCards = AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), sa);
|
tgtCards = AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), sa);
|
||||||
if (tgtCards.contains(source)) {
|
if (tgtCards.contains(source)) {
|
||||||
// Protect it from something
|
// Protect it from something
|
||||||
final boolean isThreatened = ComputerUtil.predictThreatenedObjects(aiPlayer, null, true).contains(source);
|
final boolean isThreatened = ComputerUtil.predictThreatenedObjects(aiPlayer, null, true).contains(source);
|
||||||
if (isThreatened) {
|
if (isThreatened) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Card def = tgtCards.get(0);
|
// Card def = tgtCards.get(0);
|
||||||
// Phase this out if it might attack me, or before it can be
|
// Phase this out if it might attack me, or before it can be
|
||||||
// declared as a blocker
|
// declared as a blocker
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
if (!phasesPrefTargeting(tgt, sa, false)) {
|
if (!phasesPrefTargeting(tgt, sa, false)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return randomReturn;
|
return randomReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
|
|
||||||
if (tgt == null) {
|
if (tgt == null) {
|
||||||
return mandatory;
|
return mandatory;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (phasesPrefTargeting(tgt, sa, mandatory)) {
|
if (phasesPrefTargeting(tgt, sa, mandatory)) {
|
||||||
return true;
|
return true;
|
||||||
} else if (mandatory) {
|
} else if (mandatory) {
|
||||||
// not enough preferred targets, but mandatory so keep going:
|
// not enough preferred targets, but mandatory so keep going:
|
||||||
return phasesUnpreferredTargeting(aiPlayer.getGame(), sa, mandatory);
|
return phasesUnpreferredTargeting(aiPlayer.getGame(), sa, mandatory);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean chkAIDrawback(SpellAbility sa, Player aiPlayer) {
|
public boolean chkAIDrawback(SpellAbility sa, Player aiPlayer) {
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
|
|
||||||
boolean randomReturn = true;
|
boolean randomReturn = true;
|
||||||
|
|
||||||
if (tgt == null) {
|
if (tgt == null) {
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (!phasesPrefTargeting(tgt, sa, false)) {
|
if (!phasesPrefTargeting(tgt, sa, false)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return randomReturn;
|
return randomReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean phasesPrefTargeting(final TargetRestrictions tgt, final SpellAbility sa,
|
private boolean phasesPrefTargeting(final TargetRestrictions tgt, final SpellAbility sa,
|
||||||
final boolean mandatory) {
|
final boolean mandatory) {
|
||||||
// Card source = sa.getHostCard();
|
// Card source = sa.getHostCard();
|
||||||
|
|
||||||
// List<Card> phaseList =
|
// List<Card> phaseList =
|
||||||
// AllZoneUtil.getCardsIn(Zone.Battlefield).getTargetableCards(source)
|
// AllZoneUtil.getCardsIn(Zone.Battlefield).getTargetableCards(source)
|
||||||
// .getValidCards(tgt.getValidTgts(), source.getController(), source);
|
// .getValidCards(tgt.getValidTgts(), source.getController(), source);
|
||||||
|
|
||||||
// List<Card> aiPhaseList =
|
// List<Card> aiPhaseList =
|
||||||
// phaseList.getController(AllZone.getComputerPlayer());
|
// phaseList.getController(AllZone.getComputerPlayer());
|
||||||
|
|
||||||
// If Something in the Phase List might die from a bad combat, or a
|
// If Something in the Phase List might die from a bad combat, or a
|
||||||
// spell on the stack save it
|
// spell on the stack save it
|
||||||
|
|
||||||
// List<Card> humanPhaseList =
|
// List<Card> humanPhaseList =
|
||||||
// phaseList.getController(AllZone.getHumanPlayer());
|
// phaseList.getController(AllZone.getHumanPlayer());
|
||||||
|
|
||||||
// If something in the Human List is causing issues, phase it out
|
// If something in the Human List is causing issues, phase it out
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean phasesUnpreferredTargeting(final Game game, final SpellAbility sa, final boolean mandatory) {
|
private boolean phasesUnpreferredTargeting(final Game game, final SpellAbility sa, final boolean mandatory) {
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
|
|
||||||
CardCollectionView list = game.getCardsIn(ZoneType.Battlefield);
|
CardCollectionView list = game.getCardsIn(ZoneType.Battlefield);
|
||||||
list = CardLists.getTargetableCards(CardLists.getValidCards(list, tgt.getValidTgts(), source.getController(), source, sa), sa);
|
list = CardLists.getTargetableCards(CardLists.getValidCards(list, tgt.getValidTgts(), source.getController(), source, sa), sa);
|
||||||
|
|
||||||
// in general, if it's our own creature, choose the weakest one, if it's the opponent's creature,
|
// in general, if it's our own creature, choose the weakest one, if it's the opponent's creature,
|
||||||
// choose the strongest one
|
// choose the strongest one
|
||||||
if (!list.isEmpty()) {
|
if (!list.isEmpty()) {
|
||||||
CardCollectionView oppList = CardLists.filter(list, Predicates.not(CardPredicates.isController(source.getController())));
|
CardCollectionView oppList = CardLists.filter(list, Predicates.not(CardPredicates.isController(source.getController())));
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
sa.getTargets().add(!oppList.isEmpty() ? ComputerUtilCard.getBestAI(oppList) : ComputerUtilCard.getWorstAI(list));
|
sa.getTargets().add(!oppList.isEmpty() ? ComputerUtilCard.getBestAI(oppList) : ComputerUtilCard.getWorstAI(list));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,142 +1,142 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import forge.ai.*;
|
import forge.ai.*;
|
||||||
import forge.card.CardStateName;
|
import forge.card.CardStateName;
|
||||||
import forge.card.CardTypeView;
|
import forge.card.CardTypeView;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardCollection;
|
import forge.game.card.CardCollection;
|
||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
import forge.game.cost.Cost;
|
import forge.game.cost.Cost;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.player.PlayerActionConfirmMode;
|
import forge.game.player.PlayerActionConfirmMode;
|
||||||
import forge.game.spellability.Spell;
|
import forge.game.spellability.Spell;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.TargetRestrictions;
|
import forge.game.spellability.TargetRestrictions;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class PlayAi extends SpellAbilityAi {
|
public class PlayAi extends SpellAbilityAi {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkApiLogic(final Player ai, final SpellAbility sa) {
|
protected boolean checkApiLogic(final Player ai, final SpellAbility sa) {
|
||||||
final String logic = sa.hasParam("AILogic") ? sa.getParam("AILogic") : "";
|
final String logic = sa.hasParam("AILogic") ? sa.getParam("AILogic") : "";
|
||||||
|
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
// don't use this as a response (ReplaySpell logic is an exception, might be called from a subability
|
// don't use this as a response (ReplaySpell logic is an exception, might be called from a subability
|
||||||
// while the trigger is on stack)
|
// while the trigger is on stack)
|
||||||
if (!game.getStack().isEmpty() && !"ReplaySpell".equals(logic)) {
|
if (!game.getStack().isEmpty() && !"ReplaySpell".equals(logic)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ComputerUtil.preventRunAwayActivations(sa)) {
|
if (ComputerUtil.preventRunAwayActivations(sa)) {
|
||||||
return false; // prevent infinite loop
|
return false; // prevent infinite loop
|
||||||
}
|
}
|
||||||
|
|
||||||
CardCollection cards = null;
|
CardCollection cards = null;
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
if (tgt != null) {
|
if (tgt != null) {
|
||||||
ZoneType zone = tgt.getZone().get(0);
|
ZoneType zone = tgt.getZone().get(0);
|
||||||
cards = CardLists.getValidCards(game.getCardsIn(zone), tgt.getValidTgts(), ai, source, sa);
|
cards = CardLists.getValidCards(game.getCardsIn(zone), tgt.getValidTgts(), ai, source, sa);
|
||||||
if (cards.isEmpty()) {
|
if (cards.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (!sa.hasParam("Valid")) {
|
} else if (!sa.hasParam("Valid")) {
|
||||||
cards = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa);
|
cards = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa);
|
||||||
if (cards.isEmpty()) {
|
if (cards.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("ReplaySpell".equals(logic)) {
|
if ("ReplaySpell".equals(logic)) {
|
||||||
return ComputerUtil.targetPlayableSpellCard(ai, cards, sa, sa.hasParam("WithoutManaCost"));
|
return ComputerUtil.targetPlayableSpellCard(ai, cards, sa, sa.hasParam("WithoutManaCost"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (source != null && source.hasKeyword("Hideaway") && source.hasRemembered()) {
|
if (source != null && source.hasKeyword("Hideaway") && source.hasRemembered()) {
|
||||||
// AI is not very good at playing non-permanent spells this way, at least yet
|
// AI is not very good at playing non-permanent spells this way, at least yet
|
||||||
// (might be possible to enable it for Sorceries in Main1/Main2 if target is available,
|
// (might be possible to enable it for Sorceries in Main1/Main2 if target is available,
|
||||||
// but definitely not for most Instants)
|
// but definitely not for most Instants)
|
||||||
Card rem = (Card) source.getFirstRemembered();
|
Card rem = (Card) source.getFirstRemembered();
|
||||||
CardTypeView t = rem.getState(CardStateName.Original).getType();
|
CardTypeView t = rem.getState(CardStateName.Original).getType();
|
||||||
|
|
||||||
return t.isPermanent() && !t.isLand();
|
return t.isPermanent() && !t.isLand();
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* doTriggerAINoCost
|
* doTriggerAINoCost
|
||||||
* </p>
|
* </p>
|
||||||
* @param sa
|
* @param sa
|
||||||
* a {@link forge.game.spellability.SpellAbility} object.
|
* a {@link forge.game.spellability.SpellAbility} object.
|
||||||
* @param mandatory
|
* @param mandatory
|
||||||
* a boolean.
|
* a boolean.
|
||||||
*
|
*
|
||||||
* @return a boolean.
|
* @return a boolean.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(final Player ai, final SpellAbility sa, final boolean mandatory) {
|
protected boolean doTriggerAINoCost(final Player ai, final SpellAbility sa, final boolean mandatory) {
|
||||||
if (sa.usesTargeting()) {
|
if (sa.usesTargeting()) {
|
||||||
if (!sa.hasParam("AILogic")) {
|
if (!sa.hasParam("AILogic")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return checkApiLogic(ai, sa);
|
return checkApiLogic(ai, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.game.player.Player, forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.game.player.Player, forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
||||||
// as called from PlayEffect:173
|
// as called from PlayEffect:173
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see forge.card.ability.SpellAbilityAi#chooseSingleCard(forge.game.player.Player, forge.card.spellability.SpellAbility, java.util.List, boolean)
|
* @see forge.card.ability.SpellAbilityAi#chooseSingleCard(forge.game.player.Player, forge.card.spellability.SpellAbility, java.util.List, boolean)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Card chooseSingleCard(final Player ai, final SpellAbility sa, Iterable<Card> options,
|
public Card chooseSingleCard(final Player ai, final SpellAbility sa, Iterable<Card> options,
|
||||||
final boolean isOptional,
|
final boolean isOptional,
|
||||||
Player targetedPlayer) {
|
Player targetedPlayer) {
|
||||||
List<Card> tgtCards = CardLists.filter(options, new Predicate<Card>() {
|
List<Card> tgtCards = CardLists.filter(options, new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(final Card c) {
|
public boolean apply(final Card c) {
|
||||||
for (SpellAbility s : c.getBasicSpells(c.getState(CardStateName.Original))) {
|
for (SpellAbility s : c.getBasicSpells(c.getState(CardStateName.Original))) {
|
||||||
Spell spell = (Spell) s;
|
Spell spell = (Spell) s;
|
||||||
s.setActivatingPlayer(ai);
|
s.setActivatingPlayer(ai);
|
||||||
// timing restrictions still apply
|
// timing restrictions still apply
|
||||||
if (!s.getRestrictions().checkTimingRestrictions(c, s))
|
if (!s.getRestrictions().checkTimingRestrictions(c, s))
|
||||||
continue;
|
continue;
|
||||||
if (sa.hasParam("WithoutManaCost")) {
|
if (sa.hasParam("WithoutManaCost")) {
|
||||||
spell = (Spell) spell.copyWithNoManaCost();
|
spell = (Spell) spell.copyWithNoManaCost();
|
||||||
} else if (sa.hasParam("PlayCost")) {
|
} else if (sa.hasParam("PlayCost")) {
|
||||||
Cost abCost;
|
Cost abCost;
|
||||||
if ("ManaCost".equals(sa.getParam("PlayCost"))) {
|
if ("ManaCost".equals(sa.getParam("PlayCost"))) {
|
||||||
abCost = new Cost(c.getManaCost(), false);
|
abCost = new Cost(c.getManaCost(), false);
|
||||||
} else {
|
} else {
|
||||||
abCost = new Cost(sa.getParam("PlayCost"), false);
|
abCost = new Cost(sa.getParam("PlayCost"), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
spell = (Spell) spell.copyWithDefinedCost(abCost);
|
spell = (Spell) spell.copyWithDefinedCost(abCost);
|
||||||
}
|
}
|
||||||
if( AiPlayDecision.WillPlay == ((PlayerControllerAi)ai.getController()).getAi().canPlayFromEffectAI(spell, !isOptional, true)) {
|
if( AiPlayDecision.WillPlay == ((PlayerControllerAi)ai.getController()).getAi().canPlayFromEffectAI(spell, !isOptional, true)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return ComputerUtilCard.getBestAI(tgtCards);
|
return ComputerUtilCard.getBestAI(tgtCards);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,163 +1,163 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
|
|
||||||
import forge.ai.ComputerUtil;
|
import forge.ai.ComputerUtil;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.card.CounterType;
|
import forge.game.card.CounterType;
|
||||||
import forge.game.phase.PhaseHandler;
|
import forge.game.phase.PhaseHandler;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.player.PlayerCollection;
|
import forge.game.player.PlayerCollection;
|
||||||
import forge.game.player.PlayerPredicates;
|
import forge.game.player.PlayerPredicates;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
|
|
||||||
public class PoisonAi extends SpellAbilityAi {
|
public class PoisonAi extends SpellAbilityAi {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
*
|
*
|
||||||
* @see
|
* @see
|
||||||
* forge.ai.SpellAbilityAi#checkPhaseRestrictions(forge.game.player.Player,
|
* forge.ai.SpellAbilityAi#checkPhaseRestrictions(forge.game.player.Player,
|
||||||
* forge.game.spellability.SpellAbility, forge.game.phase.PhaseHandler)
|
* forge.game.spellability.SpellAbility, forge.game.phase.PhaseHandler)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkPhaseRestrictions(final Player ai, final SpellAbility sa, final PhaseHandler ph) {
|
protected boolean checkPhaseRestrictions(final Player ai, final SpellAbility sa, final PhaseHandler ph) {
|
||||||
if (ph.getPhase().isBefore(PhaseType.MAIN2)
|
if (ph.getPhase().isBefore(PhaseType.MAIN2)
|
||||||
&& !sa.hasParam("ActivationPhases")) {
|
&& !sa.hasParam("ActivationPhases")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
*
|
*
|
||||||
* @see forge.ai.SpellAbilityAi#checkApiLogic(forge.game.player.Player,
|
* @see forge.ai.SpellAbilityAi#checkApiLogic(forge.game.player.Player,
|
||||||
* forge.game.spellability.SpellAbility)
|
* forge.game.spellability.SpellAbility)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkApiLogic(Player ai, SpellAbility sa) {
|
protected boolean checkApiLogic(Player ai, SpellAbility sa) {
|
||||||
// Don't tap creatures that may be able to block
|
// Don't tap creatures that may be able to block
|
||||||
if (ComputerUtil.waitForBlocking(sa)) {
|
if (ComputerUtil.waitForBlocking(sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.usesTargeting()) {
|
if (sa.usesTargeting()) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
return tgtPlayer(ai, sa, true);
|
return tgtPlayer(ai, sa, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
*
|
*
|
||||||
* @see forge.ai.SpellAbilityAi#doTriggerAINoCost(forge.game.player.Player,
|
* @see forge.ai.SpellAbilityAi#doTriggerAINoCost(forge.game.player.Player,
|
||||||
* forge.game.spellability.SpellAbility, boolean)
|
* forge.game.spellability.SpellAbility, boolean)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
||||||
if (sa.usesTargeting()) {
|
if (sa.usesTargeting()) {
|
||||||
return tgtPlayer(ai, sa, mandatory);
|
return tgtPlayer(ai, sa, mandatory);
|
||||||
} else if (mandatory || !ai.canReceiveCounters(CounterType.POISON)) {
|
} else if (mandatory || !ai.canReceiveCounters(CounterType.POISON)) {
|
||||||
// mandatory or ai is uneffected
|
// mandatory or ai is uneffected
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
// currently there are no optional Trigger
|
// currently there are no optional Trigger
|
||||||
final PlayerCollection players = AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("Defined"),
|
final PlayerCollection players = AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("Defined"),
|
||||||
sa);
|
sa);
|
||||||
if (players.isEmpty()) {
|
if (players.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// not affected, don't care
|
// not affected, don't care
|
||||||
if (!players.contains(ai)) {
|
if (!players.contains(ai)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Player max = players.max(PlayerPredicates.compareByPoison());
|
Player max = players.max(PlayerPredicates.compareByPoison());
|
||||||
if (ai.getPoisonCounters() == max.getPoisonCounters()) {
|
if (ai.getPoisonCounters() == max.getPoisonCounters()) {
|
||||||
// ai is one of the max
|
// ai is one of the max
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean tgtPlayer(Player ai, SpellAbility sa, boolean mandatory) {
|
private boolean tgtPlayer(Player ai, SpellAbility sa, boolean mandatory) {
|
||||||
PlayerCollection tgts = ai.getOpponents().filter(PlayerPredicates.isTargetableBy(sa));
|
PlayerCollection tgts = ai.getOpponents().filter(PlayerPredicates.isTargetableBy(sa));
|
||||||
if (!tgts.isEmpty()) {
|
if (!tgts.isEmpty()) {
|
||||||
// try to select a opponent that can lose through poison counters
|
// try to select a opponent that can lose through poison counters
|
||||||
PlayerCollection betterTgts = tgts.filter(new Predicate<Player>() {
|
PlayerCollection betterTgts = tgts.filter(new Predicate<Player>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(Player input) {
|
public boolean apply(Player input) {
|
||||||
if (input.cantLose()) {
|
if (input.cantLose()) {
|
||||||
return false;
|
return false;
|
||||||
} else if (!input.canReceiveCounters(CounterType.POISON)) {
|
} else if (!input.canReceiveCounters(CounterType.POISON)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!betterTgts.isEmpty()) {
|
if (!betterTgts.isEmpty()) {
|
||||||
tgts = betterTgts;
|
tgts = betterTgts;
|
||||||
} else if (mandatory) {
|
} else if (mandatory) {
|
||||||
// no better choice but better than hiting himself
|
// no better choice but better than hiting himself
|
||||||
sa.getTargets().add(tgts.getFirst());
|
sa.getTargets().add(tgts.getFirst());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// no opponent can be killed with that
|
// no opponent can be killed with that
|
||||||
if (tgts.isEmpty()) {
|
if (tgts.isEmpty()) {
|
||||||
if (mandatory) {
|
if (mandatory) {
|
||||||
// AI is uneffected
|
// AI is uneffected
|
||||||
if (ai.canBeTargetedBy(sa) && ai.canReceiveCounters(CounterType.POISON)) {
|
if (ai.canBeTargetedBy(sa) && ai.canReceiveCounters(CounterType.POISON)) {
|
||||||
sa.getTargets().add(ai);
|
sa.getTargets().add(ai);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// need to target something, try to target allies
|
// need to target something, try to target allies
|
||||||
PlayerCollection allies = ai.getAllies().filter(PlayerPredicates.isTargetableBy(sa));
|
PlayerCollection allies = ai.getAllies().filter(PlayerPredicates.isTargetableBy(sa));
|
||||||
if (!allies.isEmpty()) {
|
if (!allies.isEmpty()) {
|
||||||
// some ally would be uneffected
|
// some ally would be uneffected
|
||||||
PlayerCollection betterAllies = allies.filter(new Predicate<Player>() {
|
PlayerCollection betterAllies = allies.filter(new Predicate<Player>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(Player input) {
|
public boolean apply(Player input) {
|
||||||
if (input.cantLose()) {
|
if (input.cantLose()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!input.canReceiveCounters(CounterType.POISON)) {
|
if (!input.canReceiveCounters(CounterType.POISON)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
if (!betterAllies.isEmpty()) {
|
if (!betterAllies.isEmpty()) {
|
||||||
allies = betterAllies;
|
allies = betterAllies;
|
||||||
}
|
}
|
||||||
|
|
||||||
Player min = allies.min(PlayerPredicates.compareByPoison());
|
Player min = allies.min(PlayerPredicates.compareByPoison());
|
||||||
sa.getTargets().add(min);
|
sa.getTargets().add(min);
|
||||||
return true;
|
return true;
|
||||||
} else if (ai.canBeTargetedBy(sa)) {
|
} else if (ai.canBeTargetedBy(sa)) {
|
||||||
// need to target himself
|
// need to target himself
|
||||||
sa.getTargets().add(ai);
|
sa.getTargets().add(ai);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// find player with max poison to kill
|
// find player with max poison to kill
|
||||||
Player max = tgts.max(PlayerPredicates.compareByPoison());
|
Player max = tgts.max(PlayerPredicates.compareByPoison());
|
||||||
sa.getTargets().add(max);
|
sa.getTargets().add(max);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,81 +1,81 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
|
|
||||||
import forge.ai.ComputerUtil;
|
import forge.ai.ComputerUtil;
|
||||||
import forge.ai.ComputerUtilCard;
|
import forge.ai.ComputerUtilCard;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardCollection;
|
import forge.game.card.CardCollection;
|
||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.TargetRestrictions;
|
import forge.game.spellability.TargetRestrictions;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
import forge.util.MyRandom;
|
import forge.util.MyRandom;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class PowerExchangeAi extends SpellAbilityAi {
|
public class PowerExchangeAi extends SpellAbilityAi {
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see forge.card.abilityfactory.SpellAiLogic#canPlayAI(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility)
|
* @see forge.card.abilityfactory.SpellAiLogic#canPlayAI(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player ai, final SpellAbility sa) {
|
protected boolean canPlayAI(Player ai, final SpellAbility sa) {
|
||||||
Card c1 = null;
|
Card c1 = null;
|
||||||
Card c2 = null;
|
Card c2 = null;
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
|
|
||||||
List<Card> list =
|
List<Card> list =
|
||||||
CardLists.getValidCards(ComputerUtil.getOpponentFor(ai).getCardsIn(ZoneType.Battlefield), tgt.getValidTgts(), ai, sa.getHostCard(), sa);
|
CardLists.getValidCards(ComputerUtil.getOpponentFor(ai).getCardsIn(ZoneType.Battlefield), tgt.getValidTgts(), ai, sa.getHostCard(), sa);
|
||||||
// AI won't try to grab cards that are filtered out of AI decks on
|
// AI won't try to grab cards that are filtered out of AI decks on
|
||||||
// purpose
|
// purpose
|
||||||
list = CardLists.filter(list, new Predicate<Card>() {
|
list = CardLists.filter(list, new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(final Card c) {
|
public boolean apply(final Card c) {
|
||||||
final Map<String, String> vars = c.getSVars();
|
final Map<String, String> vars = c.getSVars();
|
||||||
return !vars.containsKey("RemAIDeck") && c.canBeTargetedBy(sa);
|
return !vars.containsKey("RemAIDeck") && c.canBeTargetedBy(sa);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
CardLists.sortByPowerAsc(list);
|
CardLists.sortByPowerAsc(list);
|
||||||
c1 = list.isEmpty() ? null : list.get(0);
|
c1 = list.isEmpty() ? null : list.get(0);
|
||||||
if (sa.hasParam("Defined")) {
|
if (sa.hasParam("Defined")) {
|
||||||
c2 = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa).get(0);
|
c2 = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa).get(0);
|
||||||
}
|
}
|
||||||
else if (tgt.getMinTargets(sa.getHostCard(), sa) > 1) {
|
else if (tgt.getMinTargets(sa.getHostCard(), sa) > 1) {
|
||||||
CardCollection list2 = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), tgt.getValidTgts(), ai, sa.getHostCard(), sa);
|
CardCollection list2 = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), tgt.getValidTgts(), ai, sa.getHostCard(), sa);
|
||||||
CardLists.sortByPowerAsc(list2);
|
CardLists.sortByPowerAsc(list2);
|
||||||
Collections.reverse(list2);
|
Collections.reverse(list2);
|
||||||
c2 = list2.isEmpty() ? null : list2.get(0);
|
c2 = list2.isEmpty() ? null : list2.get(0);
|
||||||
sa.getTargets().add(c2);
|
sa.getTargets().add(c2);
|
||||||
}
|
}
|
||||||
if (c1 == null || c2 == null) {
|
if (c1 == null || c2 == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (ComputerUtilCard.evaluateCreature(c1) > ComputerUtilCard.evaluateCreature(c2) + 40) {
|
if (ComputerUtilCard.evaluateCreature(c1) > ComputerUtilCard.evaluateCreature(c2) + 40) {
|
||||||
sa.getTargets().add(c1);
|
sa.getTargets().add(c1);
|
||||||
return MyRandom.getRandom().nextFloat() <= Math.pow(.6667, sa.getActivationsThisTurn());
|
return MyRandom.getRandom().nextFloat() <= Math.pow(.6667, sa.getActivationsThisTurn());
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see forge.card.abilityfactory.SpellAiLogic#doTriggerAINoCost(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility, boolean)
|
* @see forge.card.abilityfactory.SpellAiLogic#doTriggerAINoCost(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility, boolean)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
|
||||||
if (sa.getTargetRestrictions() == null) {
|
if (sa.getTargetRestrictions() == null) {
|
||||||
if (mandatory) {
|
if (mandatory) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return canPlayAI(aiPlayer, sa);
|
return canPlayAI(aiPlayer, sa);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,375 +1,375 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
|
|
||||||
import forge.ai.AiAttackController;
|
import forge.ai.AiAttackController;
|
||||||
import forge.ai.ComputerUtil;
|
import forge.ai.ComputerUtil;
|
||||||
import forge.ai.ComputerUtilCard;
|
import forge.ai.ComputerUtilCard;
|
||||||
import forge.ai.ComputerUtilCombat;
|
import forge.ai.ComputerUtilCombat;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.card.MagicColor;
|
import forge.card.MagicColor;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.GameObject;
|
import forge.game.GameObject;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.ability.ApiType;
|
import forge.game.ability.ApiType;
|
||||||
import forge.game.ability.effects.ProtectEffect;
|
import forge.game.ability.effects.ProtectEffect;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardCollection;
|
import forge.game.card.CardCollection;
|
||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
import forge.game.combat.Combat;
|
import forge.game.combat.Combat;
|
||||||
import forge.game.phase.PhaseHandler;
|
import forge.game.phase.PhaseHandler;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.TargetRestrictions;
|
import forge.game.spellability.TargetRestrictions;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
import forge.util.MyRandom;
|
import forge.util.MyRandom;
|
||||||
|
|
||||||
public class ProtectAi extends SpellAbilityAi {
|
public class ProtectAi extends SpellAbilityAi {
|
||||||
private static boolean hasProtectionFrom(final Card card, final String color) {
|
private static boolean hasProtectionFrom(final Card card, final String color) {
|
||||||
final List<String> onlyColors = new ArrayList<String>(MagicColor.Constant.ONLY_COLORS);
|
final List<String> onlyColors = new ArrayList<String>(MagicColor.Constant.ONLY_COLORS);
|
||||||
|
|
||||||
// make sure we have a valid color
|
// make sure we have a valid color
|
||||||
if (!onlyColors.contains(color)) {
|
if (!onlyColors.contains(color)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final String protection = "Protection from " + color;
|
final String protection = "Protection from " + color;
|
||||||
|
|
||||||
return card.hasKeyword(protection);
|
return card.hasKeyword(protection);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean hasProtectionFromAny(final Card card, final Iterable<String> colors) {
|
private static boolean hasProtectionFromAny(final Card card, final Iterable<String> colors) {
|
||||||
boolean protect = false;
|
boolean protect = false;
|
||||||
for (final String color : colors) {
|
for (final String color : colors) {
|
||||||
protect |= hasProtectionFrom(card, color);
|
protect |= hasProtectionFrom(card, color);
|
||||||
}
|
}
|
||||||
return protect;
|
return protect;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean hasProtectionFromAll(final Card card, final Iterable<String> colors) {
|
private static boolean hasProtectionFromAll(final Card card, final Iterable<String> colors) {
|
||||||
boolean protect = true;
|
boolean protect = true;
|
||||||
boolean isEmpty = true;
|
boolean isEmpty = true;
|
||||||
for (final String color : colors) {
|
for (final String color : colors) {
|
||||||
protect &= hasProtectionFrom(card, color);
|
protect &= hasProtectionFrom(card, color);
|
||||||
isEmpty = false;
|
isEmpty = false;
|
||||||
}
|
}
|
||||||
return protect && !isEmpty;
|
return protect && !isEmpty;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Find a choice for a Protect SpellAbility that protects from a specific threat card.
|
* \brief Find a choice for a Protect SpellAbility that protects from a specific threat card.
|
||||||
* @param threat Card to protect against
|
* @param threat Card to protect against
|
||||||
* @param sa Protect SpellAbility
|
* @param sa Protect SpellAbility
|
||||||
* @return choice that can protect against the given threat, null if no such choice exists
|
* @return choice that can protect against the given threat, null if no such choice exists
|
||||||
*/
|
*/
|
||||||
public static String toProtectFrom(final Card threat, SpellAbility sa) {
|
public static String toProtectFrom(final Card threat, SpellAbility sa) {
|
||||||
if (sa.getApi() != ApiType.Protection) {
|
if (sa.getApi() != ApiType.Protection) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
final List<String> choices = ProtectEffect.getProtectionList(sa);
|
final List<String> choices = ProtectEffect.getProtectionList(sa);
|
||||||
if (threat.isArtifact() && choices.contains("artifacts")) {
|
if (threat.isArtifact() && choices.contains("artifacts")) {
|
||||||
return "artifacts";
|
return "artifacts";
|
||||||
}
|
}
|
||||||
if (threat.isBlack() && choices.contains("black")) {
|
if (threat.isBlack() && choices.contains("black")) {
|
||||||
return "black";
|
return "black";
|
||||||
}
|
}
|
||||||
if (threat.isBlue() && choices.contains("blue")) {
|
if (threat.isBlue() && choices.contains("blue")) {
|
||||||
return "blue";
|
return "blue";
|
||||||
}
|
}
|
||||||
if (threat.isGreen() && choices.contains("green")) {
|
if (threat.isGreen() && choices.contains("green")) {
|
||||||
return "green";
|
return "green";
|
||||||
}
|
}
|
||||||
if (threat.isRed() && choices.contains("red")) {
|
if (threat.isRed() && choices.contains("red")) {
|
||||||
return "red";
|
return "red";
|
||||||
}
|
}
|
||||||
if (threat.isWhite() && choices.contains("white")) {
|
if (threat.isWhite() && choices.contains("white")) {
|
||||||
return "white";
|
return "white";
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* getProtectCreatures.
|
* getProtectCreatures.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public static CardCollection getProtectCreatures(final Player ai, final SpellAbility sa) {
|
public static CardCollection getProtectCreatures(final Player ai, final SpellAbility sa) {
|
||||||
final List<String> gains = ProtectEffect.getProtectionList(sa);
|
final List<String> gains = ProtectEffect.getProtectionList(sa);
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
final Combat combat = game.getCombat();
|
final Combat combat = game.getCombat();
|
||||||
final PhaseHandler ph = game.getPhaseHandler();
|
final PhaseHandler ph = game.getPhaseHandler();
|
||||||
|
|
||||||
CardCollection list = ai.getCreaturesInPlay();
|
CardCollection list = ai.getCreaturesInPlay();
|
||||||
final List<GameObject> threatenedObjects = ComputerUtil.predictThreatenedObjects(sa.getActivatingPlayer(), sa, true);
|
final List<GameObject> threatenedObjects = ComputerUtil.predictThreatenedObjects(sa.getActivatingPlayer(), sa, true);
|
||||||
list = CardLists.filter(list, new Predicate<Card>() {
|
list = CardLists.filter(list, new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(final Card c) {
|
public boolean apply(final Card c) {
|
||||||
if (!c.canBeTargetedBy(sa)) {
|
if (!c.canBeTargetedBy(sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't add duplicate protections
|
// Don't add duplicate protections
|
||||||
if (hasProtectionFromAll(c, gains)) {
|
if (hasProtectionFromAll(c, gains)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (threatenedObjects.contains(c)) {
|
if (threatenedObjects.contains(c)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (combat != null) {
|
if (combat != null) {
|
||||||
//creature is blocking and would be destroyed itself
|
//creature is blocking and would be destroyed itself
|
||||||
if (combat.isBlocking(c) && ComputerUtilCombat.blockerWouldBeDestroyed(ai, c, combat)) {
|
if (combat.isBlocking(c) && ComputerUtilCombat.blockerWouldBeDestroyed(ai, c, combat)) {
|
||||||
List<Card> threats = combat.getAttackersBlockedBy(c);
|
List<Card> threats = combat.getAttackersBlockedBy(c);
|
||||||
return threats != null && !threats.isEmpty() && ProtectAi.toProtectFrom(threats.get(0), sa) != null;
|
return threats != null && !threats.isEmpty() && ProtectAi.toProtectFrom(threats.get(0), sa) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
//creature is attacking and would be destroyed itself
|
//creature is attacking and would be destroyed itself
|
||||||
if (combat.isAttacking(c) && combat.isBlocked(c) && ComputerUtilCombat.attackerWouldBeDestroyed(ai, c, combat)) {
|
if (combat.isAttacking(c) && combat.isBlocked(c) && ComputerUtilCombat.attackerWouldBeDestroyed(ai, c, combat)) {
|
||||||
CardCollection threats = combat.getBlockers(c);
|
CardCollection threats = combat.getBlockers(c);
|
||||||
if (threats != null && !threats.isEmpty()) {
|
if (threats != null && !threats.isEmpty()) {
|
||||||
ComputerUtilCard.sortByEvaluateCreature(threats);
|
ComputerUtilCard.sortByEvaluateCreature(threats);
|
||||||
return ProtectAi.toProtectFrom(threats.get(0), sa) != null;
|
return ProtectAi.toProtectFrom(threats.get(0), sa) != null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//make unblockable
|
//make unblockable
|
||||||
if (ph.getPlayerTurn() == ai && ph.getPhase() == PhaseType.MAIN1) {
|
if (ph.getPlayerTurn() == ai && ph.getPhase() == PhaseType.MAIN1) {
|
||||||
AiAttackController aiAtk = new AiAttackController(ai, c);
|
AiAttackController aiAtk = new AiAttackController(ai, c);
|
||||||
String s = aiAtk.toProtectAttacker(sa);
|
String s = aiAtk.toProtectAttacker(sa);
|
||||||
if (s==null) {
|
if (s==null) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
Player opponent = ComputerUtil.getOpponentFor(ai);
|
Player opponent = ComputerUtil.getOpponentFor(ai);
|
||||||
Combat combat = ai.getGame().getCombat();
|
Combat combat = ai.getGame().getCombat();
|
||||||
int dmg = ComputerUtilCombat.damageIfUnblocked(c, opponent, combat, true);
|
int dmg = ComputerUtilCombat.damageIfUnblocked(c, opponent, combat, true);
|
||||||
float ratio = 1.0f * dmg / opponent.getLife();
|
float ratio = 1.0f * dmg / opponent.getLife();
|
||||||
Random r = MyRandom.getRandom();
|
Random r = MyRandom.getRandom();
|
||||||
return r.nextFloat() < ratio;
|
return r.nextFloat() < ratio;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return list;
|
return list;
|
||||||
} // getProtectCreatures()
|
} // getProtectCreatures()
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkPhaseRestrictions(final Player ai, final SpellAbility sa, final PhaseHandler ph) {
|
protected boolean checkPhaseRestrictions(final Player ai, final SpellAbility sa, final PhaseHandler ph) {
|
||||||
final boolean notAiMain1 = !(ph.getPlayerTurn() == ai && ph.getPhase() == PhaseType.MAIN1);
|
final boolean notAiMain1 = !(ph.getPlayerTurn() == ai && ph.getPhase() == PhaseType.MAIN1);
|
||||||
if (SpellAbilityAi.isSorcerySpeed(sa) && notAiMain1) {
|
if (SpellAbilityAi.isSorcerySpeed(sa) && notAiMain1) {
|
||||||
// sorceries can only give protection in order to create an unblockable attacker
|
// sorceries can only give protection in order to create an unblockable attacker
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkApiLogic(final Player ai, final SpellAbility sa) {
|
protected boolean checkApiLogic(final Player ai, final SpellAbility sa) {
|
||||||
if ((sa.getTargetRestrictions() == null) || !sa.getTargetRestrictions().doesTarget()) {
|
if ((sa.getTargetRestrictions() == null) || !sa.getTargetRestrictions().doesTarget()) {
|
||||||
final List<Card> cards = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa);
|
final List<Card> cards = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa);
|
||||||
if (cards.size() == 0) {
|
if (cards.size() == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* when this happens we need to expand AI to consider if its ok
|
* when this happens we need to expand AI to consider if its ok
|
||||||
* for everything? for (Card card : cards) { // TODO if AI doesn't
|
* for everything? for (Card card : cards) { // TODO if AI doesn't
|
||||||
* control Card and Pump is a Curse, than maybe use?
|
* control Card and Pump is a Curse, than maybe use?
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
} else {
|
} else {
|
||||||
return protectTgtAI(ai, sa, false);
|
return protectTgtAI(ai, sa, false);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean protectTgtAI(final Player ai, final SpellAbility sa, final boolean mandatory) {
|
private boolean protectTgtAI(final Player ai, final SpellAbility sa, final boolean mandatory) {
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
if (!mandatory && game.getPhaseHandler().getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS)
|
if (!mandatory && game.getPhaseHandler().getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS)
|
||||||
&& game.getStack().isEmpty()) {
|
&& game.getStack().isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
|
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
CardCollection list = getProtectCreatures(ai, sa);
|
CardCollection list = getProtectCreatures(ai, sa);
|
||||||
|
|
||||||
list = CardLists.getValidCards(list, tgt.getValidTgts(), sa.getActivatingPlayer(), sa.getHostCard(), sa);
|
list = CardLists.getValidCards(list, tgt.getValidTgts(), sa.getActivatingPlayer(), sa.getHostCard(), sa);
|
||||||
|
|
||||||
if (game.getStack().isEmpty()) {
|
if (game.getStack().isEmpty()) {
|
||||||
// If the cost is tapping, don't activate before declare
|
// If the cost is tapping, don't activate before declare
|
||||||
// attack/block
|
// attack/block
|
||||||
if ((sa.getPayCosts() != null) && sa.getPayCosts().hasTapCost()) {
|
if ((sa.getPayCosts() != null) && sa.getPayCosts().hasTapCost()) {
|
||||||
if (game.getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
if (game.getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
||||||
&& game.getPhaseHandler().isPlayerTurn(ai)) {
|
&& game.getPhaseHandler().isPlayerTurn(ai)) {
|
||||||
list.remove(sa.getHostCard());
|
list.remove(sa.getHostCard());
|
||||||
}
|
}
|
||||||
if (game.getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_DECLARE_BLOCKERS)
|
if (game.getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_DECLARE_BLOCKERS)
|
||||||
&& game.getPhaseHandler().isPlayerTurn(ai)) {
|
&& game.getPhaseHandler().isPlayerTurn(ai)) {
|
||||||
list.remove(sa.getHostCard());
|
list.remove(sa.getHostCard());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't target cards that will die.
|
// Don't target cards that will die.
|
||||||
list = ComputerUtil.getSafeTargets(ai, sa, list);
|
list = ComputerUtil.getSafeTargets(ai, sa, list);
|
||||||
|
|
||||||
if (list.isEmpty()) {
|
if (list.isEmpty()) {
|
||||||
return mandatory && protectMandatoryTarget(ai, sa, mandatory);
|
return mandatory && protectMandatoryTarget(ai, sa, mandatory);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (sa.getTargets().getNumTargeted() < tgt.getMaxTargets(source, sa)) {
|
while (sa.getTargets().getNumTargeted() < tgt.getMaxTargets(source, sa)) {
|
||||||
Card t = null;
|
Card t = null;
|
||||||
// boolean goodt = false;
|
// boolean goodt = false;
|
||||||
|
|
||||||
if (list.isEmpty()) {
|
if (list.isEmpty()) {
|
||||||
if ((sa.getTargets().getNumTargeted() < tgt.getMinTargets(source, sa)) || sa.getTargets().getNumTargeted() == 0) {
|
if ((sa.getTargets().getNumTargeted() < tgt.getMinTargets(source, sa)) || sa.getTargets().getNumTargeted() == 0) {
|
||||||
if (mandatory) {
|
if (mandatory) {
|
||||||
return protectMandatoryTarget(ai, sa, mandatory);
|
return protectMandatoryTarget(ai, sa, mandatory);
|
||||||
}
|
}
|
||||||
|
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
// TODO is this good enough? for up to amounts?
|
// TODO is this good enough? for up to amounts?
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
t = ComputerUtilCard.getBestCreatureAI(list);
|
t = ComputerUtilCard.getBestCreatureAI(list);
|
||||||
sa.getTargets().add(t);
|
sa.getTargets().add(t);
|
||||||
list.remove(t);
|
list.remove(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} // protectTgtAI()
|
} // protectTgtAI()
|
||||||
|
|
||||||
private static boolean protectMandatoryTarget(final Player ai, final SpellAbility sa, final boolean mandatory) {
|
private static boolean protectMandatoryTarget(final Player ai, final SpellAbility sa, final boolean mandatory) {
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
|
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
CardCollection list = CardLists.getValidCards(game.getCardsIn(ZoneType.Battlefield), tgt.getValidTgts(), sa.getActivatingPlayer(), sa.getHostCard(), sa);
|
CardCollection list = CardLists.getValidCards(game.getCardsIn(ZoneType.Battlefield), tgt.getValidTgts(), sa.getActivatingPlayer(), sa.getHostCard(), sa);
|
||||||
|
|
||||||
if (list.size() < tgt.getMinTargets(sa.getHostCard(), sa)) {
|
if (list.size() < tgt.getMinTargets(sa.getHostCard(), sa)) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove anything that's already been targeted
|
// Remove anything that's already been targeted
|
||||||
for (final Card c : sa.getTargets().getTargetCards()) {
|
for (final Card c : sa.getTargets().getTargetCards()) {
|
||||||
list.remove(c);
|
list.remove(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
CardCollection pref = CardLists.filterControlledBy(list, ai);
|
CardCollection pref = CardLists.filterControlledBy(list, ai);
|
||||||
pref = CardLists.filter(pref, new Predicate<Card>() {
|
pref = CardLists.filter(pref, new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(final Card c) {
|
public boolean apply(final Card c) {
|
||||||
return !hasProtectionFromAll(c, ProtectEffect.getProtectionList(sa));
|
return !hasProtectionFromAll(c, ProtectEffect.getProtectionList(sa));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
final CardCollection pref2 = CardLists.filterControlledBy(list, ai);
|
final CardCollection pref2 = CardLists.filterControlledBy(list, ai);
|
||||||
pref = CardLists.filter(pref, new Predicate<Card>() {
|
pref = CardLists.filter(pref, new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(final Card c) {
|
public boolean apply(final Card c) {
|
||||||
return !hasProtectionFromAny(c, ProtectEffect.getProtectionList(sa));
|
return !hasProtectionFromAny(c, ProtectEffect.getProtectionList(sa));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
final List<Card> forced = CardLists.filterControlledBy(list, ai);
|
final List<Card> forced = CardLists.filterControlledBy(list, ai);
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
|
|
||||||
while (sa.getTargets().getNumTargeted() < tgt.getMaxTargets(source, sa)) {
|
while (sa.getTargets().getNumTargeted() < tgt.getMaxTargets(source, sa)) {
|
||||||
if (pref.isEmpty()) {
|
if (pref.isEmpty()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Card c;
|
Card c;
|
||||||
if (CardLists.getNotType(pref, "Creature").size() == 0) {
|
if (CardLists.getNotType(pref, "Creature").size() == 0) {
|
||||||
c = ComputerUtilCard.getBestCreatureAI(pref);
|
c = ComputerUtilCard.getBestCreatureAI(pref);
|
||||||
} else {
|
} else {
|
||||||
c = ComputerUtilCard.getMostExpensivePermanentAI(pref, sa, true);
|
c = ComputerUtilCard.getMostExpensivePermanentAI(pref, sa, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
pref.remove(c);
|
pref.remove(c);
|
||||||
|
|
||||||
sa.getTargets().add(c);
|
sa.getTargets().add(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (sa.getTargets().getNumTargeted() < tgt.getMaxTargets(source, sa)) {
|
while (sa.getTargets().getNumTargeted() < tgt.getMaxTargets(source, sa)) {
|
||||||
if (pref2.isEmpty()) {
|
if (pref2.isEmpty()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Card c;
|
Card c;
|
||||||
if (CardLists.getNotType(pref2, "Creature").size() == 0) {
|
if (CardLists.getNotType(pref2, "Creature").size() == 0) {
|
||||||
c = ComputerUtilCard.getBestCreatureAI(pref2);
|
c = ComputerUtilCard.getBestCreatureAI(pref2);
|
||||||
} else {
|
} else {
|
||||||
c = ComputerUtilCard.getMostExpensivePermanentAI(pref2, sa, true);
|
c = ComputerUtilCard.getMostExpensivePermanentAI(pref2, sa, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
pref2.remove(c);
|
pref2.remove(c);
|
||||||
|
|
||||||
sa.getTargets().add(c);
|
sa.getTargets().add(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (sa.getTargets().getNumTargeted() < tgt.getMinTargets(source, sa)) {
|
while (sa.getTargets().getNumTargeted() < tgt.getMinTargets(source, sa)) {
|
||||||
if (forced.isEmpty()) {
|
if (forced.isEmpty()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Card c;
|
Card c;
|
||||||
if (CardLists.getNotType(forced, "Creature").size() == 0) {
|
if (CardLists.getNotType(forced, "Creature").size() == 0) {
|
||||||
c = ComputerUtilCard.getWorstCreatureAI(forced);
|
c = ComputerUtilCard.getWorstCreatureAI(forced);
|
||||||
} else {
|
} else {
|
||||||
c = ComputerUtilCard.getCheapestPermanentAI(forced, sa, true);
|
c = ComputerUtilCard.getCheapestPermanentAI(forced, sa, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
forced.remove(c);
|
forced.remove(c);
|
||||||
|
|
||||||
sa.getTargets().add(c);
|
sa.getTargets().add(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.getTargets().getNumTargeted() < tgt.getMinTargets(sa.getHostCard(), sa)) {
|
if (sa.getTargets().getNumTargeted() < tgt.getMinTargets(sa.getHostCard(), sa)) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} // protectMandatoryTarget()
|
} // protectMandatoryTarget()
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
||||||
if (sa.getTargetRestrictions() == null) {
|
if (sa.getTargetRestrictions() == null) {
|
||||||
if (mandatory) {
|
if (mandatory) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return protectTgtAI(ai, sa, mandatory);
|
return protectTgtAI(ai, sa, mandatory);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} // protectTriggerAI
|
} // protectTriggerAI
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
||||||
final Card host = sa.getHostCard();
|
final Card host = sa.getHostCard();
|
||||||
if ((sa.getTargetRestrictions() == null) || !sa.getTargetRestrictions().doesTarget()) {
|
if ((sa.getTargetRestrictions() == null) || !sa.getTargetRestrictions().doesTarget()) {
|
||||||
if (host.isCreature()) {
|
if (host.isCreature()) {
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return protectTgtAI(ai, sa, false);
|
return protectTgtAI(ai, sa, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} // protectDrawbackAI()
|
} // protectDrawbackAI()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,48 +1,48 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
|
|
||||||
import forge.ai.ComputerUtilCost;
|
import forge.ai.ComputerUtilCost;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.cost.Cost;
|
import forge.game.cost.Cost;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
|
|
||||||
public class ProtectAllAi extends SpellAbilityAi {
|
public class ProtectAllAi extends SpellAbilityAi {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
||||||
final Card hostCard = sa.getHostCard();
|
final Card hostCard = sa.getHostCard();
|
||||||
// if there is no target and host card isn't in play, don't activate
|
// if there is no target and host card isn't in play, don't activate
|
||||||
if ((sa.getTargetRestrictions() == null) && !hostCard.isInPlay()) {
|
if ((sa.getTargetRestrictions() == null) && !hostCard.isInPlay()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Cost cost = sa.getPayCosts();
|
final Cost cost = sa.getPayCosts();
|
||||||
|
|
||||||
// temporarily disabled until better AI
|
// temporarily disabled until better AI
|
||||||
if (!ComputerUtilCost.checkLifeCost(ai, cost, hostCard, 4, sa)) {
|
if (!ComputerUtilCost.checkLifeCost(ai, cost, hostCard, 4, sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ComputerUtilCost.checkDiscardCost(ai, cost, hostCard)) {
|
if (!ComputerUtilCost.checkDiscardCost(ai, cost, hostCard)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ComputerUtilCost.checkSacrificeCost(ai, cost, hostCard, sa)) {
|
if (!ComputerUtilCost.checkSacrificeCost(ai, cost, hostCard, sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ComputerUtilCost.checkRemoveCounterCost(cost, hostCard)) {
|
if (!ComputerUtilCost.checkRemoveCounterCost(cost, hostCard)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
} // protectAllCanPlayAI()
|
} // protectAllCanPlayAI()
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user