Mulitstate cards test drive
Post MTG Forge Related Programming Questions Here
Moderators: timmermac, Blacksmith, KrazyTheFox, Agetian, friarsol, CCGHQ Admins
Mulitstate cards test drive
by Hellfish » 20 Oct 2011, 21:53
EDIT: Screw preservation.
Take 2, this time with Characteristics!
The way it works this time is that a Card can have one OR two CardCharacteristic objects. These objects contain Name, Type, ManaCost and everything else that would change between states of multistate cards. Card.getName(),getType() etc. all delegate to the currently active CardCharacteristic object. No more swapping cards in and out of the game, all that happens now is an Integer changes 1->0 or 0->.
I'd like you to keep an especially close eye on Copy/Clone effects on Flip or Doublefaced cards. Also try the Deck Editor.
Patch (for r11379,): http://www.mediafire.com/?a1hb6yydhlsa3jg
Awww yeah, full archive incoming!
http://www.mediafire.com/?jjfjj5u6lzli6jb
UPDATE:Fixes unblocked attackers crash.
Obviously, please keep bug reports in this thread.
Take 2, this time with Characteristics!
The way it works this time is that a Card can have one OR two CardCharacteristic objects. These objects contain Name, Type, ManaCost and everything else that would change between states of multistate cards. Card.getName(),getType() etc. all delegate to the currently active CardCharacteristic object. No more swapping cards in and out of the game, all that happens now is an Integer changes 1->0 or 0->.

I'd like you to keep an especially close eye on Copy/Clone effects on Flip or Doublefaced cards. Also try the Deck Editor.

Patch (for r11379,): http://www.mediafire.com/?a1hb6yydhlsa3jg
Awww yeah, full archive incoming!

http://www.mediafire.com/?jjfjj5u6lzli6jb
UPDATE:Fixes unblocked attackers crash.
Obviously, please keep bug reports in this thread.

Last edited by Hellfish on 24 Oct 2011, 19:43, edited 17 times in total.
So now you're
Screaming for the blood of the cookie monster
Evil puppet demon of obesity
Time to change the tune of his fearful ballad
C is for "Lettuce," that's good enough for me
Screaming for the blood of the cookie monster
Evil puppet demon of obesity
Time to change the tune of his fearful ballad
C is for "Lettuce," that's good enough for me
-
Hellfish - Programmer
- Posts: 1297
- Joined: 07 Jun 2009, 10:41
- Location: South of the Pumphouse
- Has thanked: 110 times
- Been thanked: 169 times
Re: Mulitstate cards test drive
by jeffwadsworth » 20 Oct 2011, 21:55
WOOT!! Never thought I would see anything done on this one.
- jeffwadsworth
- Super Tester Elite
- Posts: 1172
- Joined: 20 Oct 2010, 04:47
- Location: USA
- Has thanked: 287 times
- Been thanked: 70 times
Re: Mulitstate cards test drive
by Hellfish » 20 Oct 2011, 22:04
It's not out of the woods yet, it's gotta survive THE TWO TRIALS OF USABILITY AND MAINTAINABILITY! (ominous echo)
So now you're
Screaming for the blood of the cookie monster
Evil puppet demon of obesity
Time to change the tune of his fearful ballad
C is for "Lettuce," that's good enough for me
Screaming for the blood of the cookie monster
Evil puppet demon of obesity
Time to change the tune of his fearful ballad
C is for "Lettuce," that's good enough for me
-
Hellfish - Programmer
- Posts: 1297
- Joined: 07 Jun 2009, 10:41
- Location: South of the Pumphouse
- Has thanked: 110 times
- Been thanked: 169 times
Re: Mulitstate cards test drive
by jeffwadsworth » 20 Oct 2011, 22:45
Screeching Bats transforms into Stalking Vampire. It has summoning sickness. To my knowledge, it should not.
Playing with Kenzo the Hardhearted that was correctly flipped following it delivering damage to a creature that died on the previous turn. I attacked with it and was blocked by an Ancient Hydra. This error popped up:
Playing with Kenzo the Hardhearted that was correctly flipped following it delivering damage to a creature that died on the previous turn. I attacked with it and was blocked by an Ancient Hydra. This error popped up:
- | Open
- java.lang.NullPointerException
at forge.card.MultiStateHandler.changeState(MultiStateHandler.java:74)
at forge.card.abilityFactory.AbilityFactory_ChangeState.changeStateResolve(AbilityFactory_ChangeState.java:82)
at forge.card.abilityFactory.AbilityFactory_ChangeState.access$0(AbilityFactory_ChangeState.java:66)
at forge.card.abilityFactory.AbilityFactory_ChangeState$1.resolve(AbilityFactory_ChangeState.java:24)
at forge.card.abilityFactory.AbilityFactory.resolve(AbilityFactory.java:2152)
at forge.card.spellability.SpellAbility_Requirements.finishPaying(SpellAbility_Requirements.java:141)
at forge.card.cost.Cost_Payment.payCost(Cost_Payment.java:160)
at forge.card.spellability.SpellAbility_Requirements.startPaying(SpellAbility_Requirements.java:132)
at forge.card.spellability.SpellAbility_Requirements.needPayment(SpellAbility_Requirements.java:121)
at forge.card.spellability.SpellAbility_Requirements.fillRequirements(SpellAbility_Requirements.java:92)
at forge.GameAction.playSpellAbility_NoStack(GameAction.java:2281)
at forge.card.trigger.TriggerHandler$2.resolve(TriggerHandler.java:994)
at forge.card.abilityFactory.AbilityFactory.resolve(AbilityFactory.java:2152)
at forge.MagicStack.resolveStack(MagicStack.java:780)
at forge.Phase.passPriority(Phase.java:686)
at forge.ComputerAI_General.stackResponse(ComputerAI_General.java:473)
at forge.ComputerAI_General.stack_not_empty(ComputerAI_General.java:417)
at forge.gui.input.InputControl.updateInput(InputControl.java:200)
at forge.GuiInput.update(GuiInput.java:30)
at java.util.Observable.notifyObservers(Unknown Source)
at java.util.Observable.notifyObservers(Unknown Source)
at forge.MyObservable.updateObservers(MyObservable.java:17)
at forge.gui.input.InputControl.resetInput(InputControl.java:106)
at forge.Phase.passPriority(Phase.java:677)
at forge.gui.input.Input_PassPriority.selectButtonOK(Input_PassPriority.java:46)
at forge.GuiInput.selectButtonOK(GuiInput.java:57)
at forge.GuiDisplay4.okButtonActionPerformed(GuiDisplay4.java:1405)
at forge.GuiDisplay4.access$25(GuiDisplay4.java:1404)
at forge.GuiDisplay4$33.actionPerformed(GuiDisplay4.java:1142)
at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.setPressed(Unknown Source)
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)
at java.awt.Component.processMouseEvent(Unknown Source)
at javax.swing.JComponent.processMouseEvent(Unknown Source)
at java.awt.Component.processEvent(Unknown Source)
at java.awt.Container.processEvent(Unknown Source)
at java.awt.Component.dispatchEventImpl(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Window.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$000(Unknown Source)
at java.awt.EventQueue$1.run(Unknown Source)
at java.awt.EventQueue$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.AccessControlContext$1.doIntersectionPrivilege(Unknown Source)
at java.security.AccessControlContext$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue$2.run(Unknown Source)
at java.awt.EventQueue$2.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.AccessControlContext$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)
- jeffwadsworth
- Super Tester Elite
- Posts: 1172
- Joined: 20 Oct 2010, 04:47
- Location: USA
- Has thanked: 287 times
- Been thanked: 70 times
Re: Mulitstate cards test drive
by Hellfish » 21 Oct 2011, 08:19
Thanks,Jeff. Fixed those two, here's a new patch (for r11320)
EDIT: Removed link, newer patch forthcoming which fixes changing state during combat.
EDIT: <see first post>
Includes Cloistered Youth / Unholy Fiend and AF_ChangeStateAll and Moonmist.
EDIT:Will keep patch updates in the first post.
EDIT: Removed link, newer patch forthcoming which fixes changing state during combat.
EDIT: <see first post>
Includes Cloistered Youth / Unholy Fiend and AF_ChangeStateAll and Moonmist.
EDIT:Will keep patch updates in the first post.
So now you're
Screaming for the blood of the cookie monster
Evil puppet demon of obesity
Time to change the tune of his fearful ballad
C is for "Lettuce," that's good enough for me
Screaming for the blood of the cookie monster
Evil puppet demon of obesity
Time to change the tune of his fearful ballad
C is for "Lettuce," that's good enough for me
-
Hellfish - Programmer
- Posts: 1297
- Joined: 07 Jun 2009, 10:41
- Location: South of the Pumphouse
- Has thanked: 110 times
- Been thanked: 169 times
Re: Mulitstate cards test drive
by Hellfish » 21 Oct 2011, 12:21
New patch, see first post.
Added condition parameters for use with ISD Werewolves. Added Gatstaf Shepherd / Gatstaf Howler as an example. Alternate state card images now work.
Added condition parameters for use with ISD Werewolves. Added Gatstaf Shepherd / Gatstaf Howler as an example. Alternate state card images now work.
So now you're
Screaming for the blood of the cookie monster
Evil puppet demon of obesity
Time to change the tune of his fearful ballad
C is for "Lettuce," that's good enough for me
Screaming for the blood of the cookie monster
Evil puppet demon of obesity
Time to change the tune of his fearful ballad
C is for "Lettuce," that's good enough for me
-
Hellfish - Programmer
- Posts: 1297
- Joined: 07 Jun 2009, 10:41
- Location: South of the Pumphouse
- Has thanked: 110 times
- Been thanked: 169 times
Re: Mulitstate cards test drive
by jeffwadsworth » 21 Oct 2011, 15:10
I believe Moonmist should be an effect. Right now, it affects cards that were present when it was cast. Anything new to the battlefield is not affected.
My creature Gatstaf Howler. During the opponents upkeep, its transform trigger hit the stack due to my casting 2 spells during my turn. This error occurred.
Unholy Fiend. You should only lose 1 life during your end step. Right now, you lose it on your opponents end step also.
Test game with Soul Foundry and Gatstaf Shepard. Imprinted Gatstaf Shepard with Soul Foundry. The transform trigger hits the stack and fizzles on resolution. Gatstaf Shepard does not transform.
My creature Gatstaf Howler. During the opponents upkeep, its transform trigger hit the stack due to my casting 2 spells during my turn. This error occurred.
- | Open
- This is a Crash Report. An error has occurred. Please save this message to a file.
Please follow the instructions at this address to submit this Crash Report, plus what you were doing at the time:
http://tinyurl.com/3zzrnyb
Reporting bugs in Forge is very important. We thank you for your time.
null
Version:
Forge version SVN, build ID Unknown
OS: Windows 7 Version: 6.1 Architecture: x86
Java Version: 1.6.0_27 Vendor: Sun Microsystems Inc.
Detailed error trace:
java.lang.NullPointerException
at forge.card.MultiStateHandler.changeState(MultiStateHandler.java:90)
at forge.card.abilityFactory.AbilityFactory_ChangeState.changeStateResolve(AbilityFactory_ChangeState.java:107)
at forge.card.abilityFactory.AbilityFactory_ChangeState.access$0(AbilityFactory_ChangeState.java:86)
at forge.card.abilityFactory.AbilityFactory_ChangeState$1.resolve(AbilityFactory_ChangeState.java:31)
at forge.card.abilityFactory.AbilityFactory.resolve(AbilityFactory.java:2170)
at forge.card.spellability.SpellAbility_Requirements.finishPaying(SpellAbility_Requirements.java:141)
at forge.card.cost.Cost_Payment.payCost(Cost_Payment.java:160)
at forge.card.spellability.SpellAbility_Requirements.startPaying(SpellAbility_Requirements.java:132)
at forge.card.spellability.SpellAbility_Requirements.needPayment(SpellAbility_Requirements.java:121)
at forge.card.spellability.SpellAbility_Requirements.fillRequirements(SpellAbility_Requirements.java:92)
at forge.GameAction.playSpellAbility_NoStack(GameAction.java:2281)
at forge.card.trigger.TriggerHandler$2.resolve(TriggerHandler.java:994)
at forge.card.abilityFactory.AbilityFactory.resolve(AbilityFactory.java:2170)
at forge.MagicStack.resolveStack(MagicStack.java:781)
at forge.Phase.passPriority(Phase.java:685)
at forge.ComputerAI_General.stackResponse(ComputerAI_General.java:473)
at forge.ComputerAI_General.stack_not_empty(ComputerAI_General.java:417)
at forge.gui.input.InputControl.updateInput(InputControl.java:200)
at forge.GuiInput.update(GuiInput.java:30)
at java.util.Observable.notifyObservers(Unknown Source)
at java.util.Observable.notifyObservers(Unknown Source)
at forge.MyObservable.updateObservers(MyObservable.java:17)
at forge.gui.input.InputControl.resetInput(InputControl.java:106)
at forge.Phase.passPriority(Phase.java:676)
at forge.gui.input.Input_PassPriority.selectButtonOK(Input_PassPriority.java:46)
at forge.GuiInput.selectButtonOK(GuiInput.java:57)
at forge.GuiDisplay4.okButtonActionPerformed(GuiDisplay4.java:1405)
at forge.GuiDisplay4.access$25(GuiDisplay4.java:1404)
at forge.GuiDisplay4$33.actionPerformed(GuiDisplay4.java:1142)
at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.setPressed(Unknown Source)
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)
at java.awt.Component.processMouseEvent(Unknown Source)
at javax.swing.JComponent.processMouseEvent(Unknown Source)
at java.awt.Component.processEvent(Unknown Source)
at java.awt.Container.processEvent(Unknown Source)
at java.awt.Component.dispatchEventImpl(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Window.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$000(Unknown Source)
at java.awt.EventQueue$1.run(Unknown Source)
at java.awt.EventQueue$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.AccessControlContext$1.doIntersectionPrivilege(Unknown Source)
at java.security.AccessControlContext$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue$2.run(Unknown Source)
at java.awt.EventQueue$2.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.AccessControlContext$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)
Unholy Fiend. You should only lose 1 life during your end step. Right now, you lose it on your opponents end step also.
Last edited by jeffwadsworth on 21 Oct 2011, 16:54, edited 1 time in total.
- jeffwadsworth
- Super Tester Elite
- Posts: 1172
- Joined: 20 Oct 2010, 04:47
- Location: USA
- Has thanked: 287 times
- Been thanked: 70 times
Re: Mulitstate cards test drive
by Hellfish » 21 Oct 2011, 16:38
Re: The Soul Foundry bit.
Copies of DFC cards only copy the state that was active at the time, and can't transform since they aren't actual double-faced card. This is according to extra rulings made.
I will look into your other points. Thanks!
Copies of DFC cards only copy the state that was active at the time, and can't transform since they aren't actual double-faced card. This is according to extra rulings made.
I will look into your other points. Thanks!
So now you're
Screaming for the blood of the cookie monster
Evil puppet demon of obesity
Time to change the tune of his fearful ballad
C is for "Lettuce," that's good enough for me
Screaming for the blood of the cookie monster
Evil puppet demon of obesity
Time to change the tune of his fearful ballad
C is for "Lettuce," that's good enough for me
-
Hellfish - Programmer
- Posts: 1297
- Joined: 07 Jun 2009, 10:41
- Location: South of the Pumphouse
- Has thanked: 110 times
- Been thanked: 169 times
Re: Mulitstate cards test drive
by Sloth » 21 Oct 2011, 18:17
I've looked at the code Hellfish and I think it would be better to not create a new card object when the card transforms. All the swapping of cards for targets and combat would have to be done for other things like remembered, receivedDamageFromThisTurn, dealtDamageToThisTurn, assignedDamageMap, gainControlTargets, equipping, ... and most importantly for things that we might add in the future.
Isn't it easier to just overwrite the Name, type, P/T and other printed stuff?
Isn't it easier to just overwrite the Name, type, P/T and other printed stuff?
-
Sloth - Programmer
- Posts: 3498
- Joined: 23 Jun 2009, 19:40
- Has thanked: 125 times
- Been thanked: 507 times
Re: Mulitstate cards test drive
by Hellfish » 21 Oct 2011, 18:29
It doesn't create a new card object on transform, it swaps in a preexisting card object. But that's beside the point.
I can modify this method to overwrite characteristics with the alternate's values instead.Occam's Razor and all that.
I'll give it a whirl!
EDIT: One problem I see with it is that keywords handled in postFactoryKeywords wouldn't function correctly if the keyword list is simply swapped in.
I can modify this method to overwrite characteristics with the alternate's values instead.Occam's Razor and all that.

I'll give it a whirl!
EDIT: One problem I see with it is that keywords handled in postFactoryKeywords wouldn't function correctly if the keyword list is simply swapped in.
So now you're
Screaming for the blood of the cookie monster
Evil puppet demon of obesity
Time to change the tune of his fearful ballad
C is for "Lettuce," that's good enough for me
Screaming for the blood of the cookie monster
Evil puppet demon of obesity
Time to change the tune of his fearful ballad
C is for "Lettuce," that's good enough for me
-
Hellfish - Programmer
- Posts: 1297
- Joined: 07 Jun 2009, 10:41
- Location: South of the Pumphouse
- Has thanked: 110 times
- Been thanked: 169 times
Re: Mulitstate cards test drive
by Sloth » 21 Oct 2011, 19:10
Well those are only for printed keywords, are there any transforming cards with one of those?Hellfish wrote:EDIT: One problem I see with it is that keywords handled in postFactoryKeywords wouldn't function correctly if the keyword list is simply swapped in.
-
Sloth - Programmer
- Posts: 3498
- Joined: 23 Jun 2009, 19:40
- Has thanked: 125 times
- Been thanked: 507 times
Re: Mulitstate cards test drive
by Hellfish » 21 Oct 2011, 19:37
Hmm, there is not.There could be,though, in Dark Ascension or Roll.
And that's not considering Custom cards.
And that's not considering Custom cards.
So now you're
Screaming for the blood of the cookie monster
Evil puppet demon of obesity
Time to change the tune of his fearful ballad
C is for "Lettuce," that's good enough for me
Screaming for the blood of the cookie monster
Evil puppet demon of obesity
Time to change the tune of his fearful ballad
C is for "Lettuce," that's good enough for me
-
Hellfish - Programmer
- Posts: 1297
- Joined: 07 Jun 2009, 10:41
- Location: South of the Pumphouse
- Has thanked: 110 times
- Been thanked: 169 times
Re: Mulitstate cards test drive
by Sloth » 21 Oct 2011, 19:54
I'm not so happy about these keywords anyway. If Flashback would be read during runtime Snapcaster Mage and Past in Flames would be scriptable. Fortunately a lot of them are just shortcuts for triggered, activated or static abilities that can always be written out if necessary (Graft, Modular, Provoke, etc.).Hellfish wrote:Hmm, there is not.There could be,though, in Dark Ascension or Roll.
And that's not considering Custom cards.
-
Sloth - Programmer
- Posts: 3498
- Joined: 23 Jun 2009, 19:40
- Has thanked: 125 times
- Been thanked: 507 times
Re: Mulitstate cards test drive
by Milod » 22 Oct 2011, 07:45
some1 explain where this .text patch file gos and where wat to copy exacly? i want test it
Re: Mulitstate cards test drive
by Hellfish » 22 Oct 2011, 08:57
Last patch for right now, still using the Card-swapping system. Addresses Multistate planeswalkers, adds Garruk Relentless / Garruk, the Veil-Cursed. I will focus on trying to implement a similar system based on Sloth's suggestion.
Milod: This patch requires access to the source code in Eclipse. I will try to provide a compiled version. I'll post here if I manage it.
Milod: This patch requires access to the source code in Eclipse. I will try to provide a compiled version. I'll post here if I manage it.

So now you're
Screaming for the blood of the cookie monster
Evil puppet demon of obesity
Time to change the tune of his fearful ballad
C is for "Lettuce," that's good enough for me
Screaming for the blood of the cookie monster
Evil puppet demon of obesity
Time to change the tune of his fearful ballad
C is for "Lettuce," that's good enough for me
-
Hellfish - Programmer
- Posts: 1297
- Joined: 07 Jun 2009, 10:41
- Location: South of the Pumphouse
- Has thanked: 110 times
- Been thanked: 169 times
Who is online
Users browsing this forum: No registered users and 42 guests