Board index Programs with AI or Rules Enforcement Magic: The Gathering - Duels of the Planeswalkers Programming Talk
Storing and Retrieving Filters
Moderator: CCGHQ Admins
Storing and Retrieving Filters
by Xander9009 » 11 Sep 2016, 06:28
Being able to store and retrieve filters seems like an idea that might be useful. I don't currently know of any way to store a filter and retrieve it later, perhaps by another card. For instance, protection filters. So, I made a set of functions that can manage this. It's only VERY basically tested, so far, but it did work for the extremely simple filter "creature".
"CW_Filter_" is prefixed on the function name.
The filter (usually preceding the function) is moved into its first parameter.
The normal parameters are placed inside braces.
The idea here is that the "filter" return here is actually a rather complex table. The table contains two parts: indices, filter. This inner filter is the normal filter you'd get from ClearFilter(). Indices normally contains just the global index (but in a table) where this complex filter's instruction table is stored. Whenever you call "add", it appends a new entry to the table consisting of the function ("Add" in this case) and the parameters. The table for "creature" ends up looking like this: {"Add", {FE_TYPE, OP_IS, CARD_TYPE_CREATURE}}.
Non-human artifact would look like this:
{"Add", {FE_SUBTYPE, OP_NOT, CARD_TYPE_HUMAN}}
{"Add", {FE_TYPE, OP_IS, CARD_TYPE_ARTIFACT}}
It can also manage SetZone, AddSubFilter_Or/And, and SetFilterType, all using the same logic.
So, "Target red instant or sorcery card in your graveyard" would end up like this:
Using these functions, it would be really easy to convert a card with protection to a card with an exposed protection filter. This would allow other cards to check if a given card falls within that protection. It would be a pretty big undertaking (there are about 250 cards with protection), but it would be a really simple edit to make to them.
This would allow cards like Avalanche Tusker and anything else that requires knowing if Card A could target/block Card B, like Mirrorwing Dragon (we can already load the TargetDefinition).
I think Monday I'll test the functions thoroughly and if they hold up, I'll see about getting the protection cards converted over. In the meantime, if anyone has thoughts on this, let me know.
http://pastebin.com/7GBuq0y7
- Code: Select all
<RESOLUTION_TIME_ACTION>
local oFilter = CW_Filter_ClearFilter()
CW_Filter_Add(oFilter, {FE_TYPE, OP_IS, CARD_TYPE_CREATURE})
EffectDC():Set_Int(0, CW_Filter_GetGlobalIndex(SmartFilter))
EffectController():ChooseItem("CHOOSE", EffectDC():Make_Targets(1))
</RESOLUTION_TIME_ACTION>
<RESOLUTION_TIME_ACTION>
local oFilter = CW_Filter_GetStoredFilter(EffectDC():Get_Int(0)) -- Technically, the variable is unnecessary.
EffectController():ChooseItem("CHOOSE_AGAIN", EffectDC():Make_Targets(2))
</RESOLUTION_TIME_ACTION>
"CW_Filter_" is prefixed on the function name.
The filter (usually preceding the function) is moved into its first parameter.
The normal parameters are placed inside braces.
The idea here is that the "filter" return here is actually a rather complex table. The table contains two parts: indices, filter. This inner filter is the normal filter you'd get from ClearFilter(). Indices normally contains just the global index (but in a table) where this complex filter's instruction table is stored. Whenever you call "add", it appends a new entry to the table consisting of the function ("Add" in this case) and the parameters. The table for "creature" ends up looking like this: {"Add", {FE_TYPE, OP_IS, CARD_TYPE_CREATURE}}.
Non-human artifact would look like this:
{"Add", {FE_SUBTYPE, OP_NOT, CARD_TYPE_HUMAN}}
{"Add", {FE_TYPE, OP_IS, CARD_TYPE_ARTIFACT}}
It can also manage SetZone, AddSubFilter_Or/And, and SetFilterType, all using the same logic.
So, "Target red instant or sorcery card in your graveyard" would end up like this:
- Code: Select all
{"SetZone", {ZONE_GRAVEYARD, EffectController()}}
{"Add", {FE_COLOUR, OP_IS, COLOUR_RED}}
{"AddSubFilter_Or", {
{"Add", {FE_TYPE, OP_IS, CARD_TYPE_INSTANT}}
{"Add", {FE_TYPE, OP_IS, CARD_TYPE_SORCERY}}
}
Using these functions, it would be really easy to convert a card with protection to a card with an exposed protection filter. This would allow other cards to check if a given card falls within that protection. It would be a pretty big undertaking (there are about 250 cards with protection), but it would be a really simple edit to make to them.
This would allow cards like Avalanche Tusker and anything else that requires knowing if Card A could target/block Card B, like Mirrorwing Dragon (we can already load the TargetDefinition).
I think Monday I'll test the functions thoroughly and if they hold up, I'll see about getting the protection cards converted over. In the meantime, if anyone has thoughts on this, let me know.
http://pastebin.com/7GBuq0y7
Last edited by Xander9009 on 11 Sep 2016, 17:07, edited 2 times in total.
_______________________________
Community Wad - Community Wad Website - How to Help and Report Bugs
Discord: discord.gg/4AXvHzW
Community Wad - Community Wad Website - How to Help and Report Bugs
Discord: discord.gg/4AXvHzW
-
Xander9009 - Programmer
- Posts: 2905
- Joined: 29 Jun 2013, 07:44
- Location: Indiana, United States
- Has thanked: 121 times
- Been thanked: 445 times
Re: Storing and Retrieving Filters
by migookman » 11 Sep 2016, 16:45
This sounds awesome! Can add potential to some cards that are deemed impossible at the moment. If you need some testing, shoot me a message. Right now I'm just coding older cards (thought I would leave the newer cards to newer coders). I have done all I could for cards A and B with my limited ability. Working on 'C's now. I'm also making a percentage of cards done for the CW similar to what Forge does for new releases that would probably look good on the opening of the CW thread.
Re: Storing and Retrieving Filters
by Xander9009 » 11 Sep 2016, 17:21
Feel free to try them out. They're in CW_FILTER.LOL. The first two functions in there aren't part of this set (they were made a long time ago, and were actually never even used).
Note that CW_Filter_GetGlobalIndex() changed to CW_Filter_GetFilterIndex().
There's a quick bit of instructions if you scroll down, which is this right here:
I make no promises they'll work as advertised yet, though lol. Since I made the change to store everything in one global (rather than potentially taking up dozens), I haven't tested it at all. That'll all come either tonight or tomorrow (I'm busy today).
As for the percentage thing, yeah. That's a great idea.
Note that CW_Filter_GetGlobalIndex() changed to CW_Filter_GetFilterIndex().
There's a quick bit of instructions if you scroll down, which is this right here:
- Code: Select all
For basic usage, nothing more is needed except the following information:
1: Precede all filter functions with "CW_Filter_" to get the name of the function you should use.
2: Place the filter you get from "CW_Filter_ClearFilter()" as the first parameter of all other function calls.
3: Call CW_Filter_GetFilterIndex(aoFilter) of that filter to get an index.
4: Store that index somewhere other cards that needs it can access it.
5: Call CW_Filter_GetStoredFilter(iIndex) using that index to remake the filter from anywhere in a usable format.
NOTE: The "aoFilters" (array object filters) these functions deal with are not directly compatible with normal filters.
They're tables. If you /need/ to deal with them as normal filters, you need to call CW_Filter_GetNormalFilter(aoFilter).
The only exception to this is the reconstructed filters. When you call CW_Filter_GetStoredFilter(iIndex), you get a normal
filter, like you would have from ClearFilter().
Nearly every normal filter function is supported, including EvaluateObjects and similar functions.
Only a few (ones which have never been used) are missing. If they're needed, you can call
CW_Filter_GetNormalFilter(aoFilter) to get the actual filter to work with. However, any changes you make to the
base filter will not be stored for later retrieval.
- CW_Filter_ClearFilter
- CW_Filter_Add
- CW_Filter_AddSubFilter_Or
- CW_Filter_AddSubFilter_And
- CW_Filter_SetZone
- CW_Filter_SetFilterType
- CW_Filter_SetPortion
- CW_Filter_SetReversePortion
- CW_Filter_SetUnique
- CW_Filter_SetMarkedObjectsOnly
- CW_Filter_SetUnmarkedObjectsOnly
- CW_Filter_Count
- CW_Filter_CountStopAt
- CW_Filter_EvaluateObjects
- CW_Filter_GetNthEvaluatedObject
- CW_Filter_GetRandomEvaluatedObject
- CW_Filter_EvaluatePlayers
- CW_Filter_GetNthEvaluatedPlayer
- CW_Filter_ChromaCount
- CW_Filter_Invalidate
I make no promises they'll work as advertised yet, though lol. Since I made the change to store everything in one global (rather than potentially taking up dozens), I haven't tested it at all. That'll all come either tonight or tomorrow (I'm busy today).
As for the percentage thing, yeah. That's a great idea.
_______________________________
Community Wad - Community Wad Website - How to Help and Report Bugs
Discord: discord.gg/4AXvHzW
Community Wad - Community Wad Website - How to Help and Report Bugs
Discord: discord.gg/4AXvHzW
-
Xander9009 - Programmer
- Posts: 2905
- Joined: 29 Jun 2013, 07:44
- Location: Indiana, United States
- Has thanked: 121 times
- Been thanked: 445 times
Re: Storing and Retrieving Filters
by Xander9009 » 13 Sep 2016, 00:05
I'm pleased. I ended up rewriting them from scratch when I found a problem with the subfilters not working. Trying to keep everything straight in my head was a bit too difficult, so I recoded everything from scratch. It works basically the same way. All of the functions that need called from cards and stuff are the same.
Internally, however, it's completely different. Now, "smart filters" or "aoFilter" ideas have been replaced with "Screen". "Screen" was chosen because a screen is the same concept as a filter (you can screen objects for certain qualities), but others can see the screen, too.
They've now been tested. This code worked as it should:
Also, in the process of getting this all working, I had to list out table values a lot to see what was going on. As a result, the function Debug() has been updated. It's now a LOT more powerful, especially where tables are concerned.
Debug(sMessage, oParameter)
If sMessage is a string, then the two inputs are put together with ": " between them. So...
However, either sMessage or oParameter can also be a table/array. They'll be run through a function to convert them to a string before being concatenated. It's a recursive function, so it can even show the contents of tables inside tables.
If it comes across a value that isn't a number or a string, it'll list its type instead of breaking.
Hopefully this helps when coding.
Internally, however, it's completely different. Now, "smart filters" or "aoFilter" ideas have been replaced with "Screen". "Screen" was chosen because a screen is the same concept as a filter (you can screen objects for certain qualities), but others can see the screen, too.
They've now been tested. This code worked as it should:
- Code: Select all
<RESOLUTION_TIME_ACTION>
local oFilter = CW_Filter_ClearFilter()
local oSubFilter_Or = CW_Filter_AddSubFilter_Or(oFilter)
local oSubFilter_Land = CW_Filter_AddSubFilter_And(oSubFilter_Or)
CW_Filter_Add(oSubFilter_Land, {FE_CONTROLLER, OP_NOT, EffectController()})
CW_Filter_Add(oSubFilter_Land, {FE_TYPE, OP_IS, CARD_TYPE_LAND})
local oSubFilter_Creature = CW_Filter_AddSubFilter_And(oSubFilter_Or)
CW_Filter_Add(oSubFilter_Creature, {FE_CONTROLLER, OP_IS, EffectController()})
CW_Filter_Add(oSubFilter_Creature, {FE_TYPE, OP_IS, CARD_TYPE_CREATURE})
MTG():DuelDataChest():Set_Int(0, CW_Filter_GetFilterIndex(oFilter))
</RESOLUTION_TIME_ACTION>
- Code: Select all
<RESOLUTION_TIME_ACTION>
local oFilter = CW_Filter_ReconstructFilter(MTG():DuelDataChest():Get_Int(0))
EffectController():ChooseItem("CHOOSE_ONCE_MORE", EffectDC():Make_Targets(3))
</RESOLUTION_TIME_ACTION>
Also, in the process of getting this all working, I had to list out table values a lot to see what was going on. As a result, the function Debug() has been updated. It's now a LOT more powerful, especially where tables are concerned.
Debug(sMessage, oParameter)
If sMessage is a string, then the two inputs are put together with ": " between them. So...
- Code: Select all
local S = "Some string"
Debug("S", S)
However, either sMessage or oParameter can also be a table/array. They'll be run through a function to convert them to a string before being concatenated. It's a recursive function, so it can even show the contents of tables inside tables.
If it comes across a value that isn't a number or a string, it'll list its type instead of breaking.
Hopefully this helps when coding.
_______________________________
Community Wad - Community Wad Website - How to Help and Report Bugs
Discord: discord.gg/4AXvHzW
Community Wad - Community Wad Website - How to Help and Report Bugs
Discord: discord.gg/4AXvHzW
-
Xander9009 - Programmer
- Posts: 2905
- Joined: 29 Jun 2013, 07:44
- Location: Indiana, United States
- Has thanked: 121 times
- Been thanked: 445 times
Re: Storing and Retrieving Filters
by Xander9009 » 13 Sep 2016, 04:07
Alright, update...
Everything seems to be working. I wrote a couple more functions specifically for storing and retrieving protection filters. Because we don't want the filters being stored constantly, the normal filter is left as it already appears on the cards. However, the screen version is added to a one-time RTA at the start of the game. It then calls: CW_Filter_RegisterProtectionFilter(oFilter). From this point on, a few other functions become usable.
CW_Filter_HasProtection(oCard) - Returns true if the cards has registered protection.
CW_Filter_GetProtectionFilter(oCard) - Creates the protection filter (normal filter, which will override any currently active filters) and returns a link to it.
And most importantly:
CW_Filter_HasProtectionFrom(oCardA, oCardB) - Returns true if oCardA is protected from oCardB.
Also useful:
CW_Filter_FilterToDC(oFilter, oChest, iFilterType) - Evaluates the filter and stores the resulting objects in oChest. Note that it can process both players and cards. If players are included (whether alone or with cards) iFilterType must be set to either FILTER_TYPE_CARDS + FILTER_TYPE_PLAYERS or FILTER_TYPE_PLAYERS. If it's both, the it will return two chests, not one. The first contains the objects and the second contains the players.
Both can be captured by simply providing two variables.
------
As a complete example, Abbey Gargoyles was put into a deck. Then, Favor of the Gods (my test card) was given this code:
Also, since the initial protection filter is left alone, even if these do somehow break, the cards that use them won't suddenly stop working.
It all appears to be working. I'm really happy with this progress.
but now it's time for bed. Since it's all set up and working, I think I'll spend tomorrow sorting through the cards with protection and implementing new functions. (The protection check actually needs one tweak: a way to make sure the protection ability is still active. But Neo and I already came up with a method for that a long time ago.)
Everything seems to be working. I wrote a couple more functions specifically for storing and retrieving protection filters. Because we don't want the filters being stored constantly, the normal filter is left as it already appears on the cards. However, the screen version is added to a one-time RTA at the start of the game. It then calls: CW_Filter_RegisterProtectionFilter(oFilter). From this point on, a few other functions become usable.
CW_Filter_HasProtection(oCard) - Returns true if the cards has registered protection.
CW_Filter_GetProtectionFilter(oCard) - Creates the protection filter (normal filter, which will override any currently active filters) and returns a link to it.
And most importantly:
CW_Filter_HasProtectionFrom(oCardA, oCardB) - Returns true if oCardA is protected from oCardB.
Also useful:
CW_Filter_FilterToDC(oFilter, oChest, iFilterType) - Evaluates the filter and stores the resulting objects in oChest. Note that it can process both players and cards. If players are included (whether alone or with cards) iFilterType must be set to either FILTER_TYPE_CARDS + FILTER_TYPE_PLAYERS or FILTER_TYPE_PLAYERS. If it's both, the it will return two chests, not one. The first contains the objects and the second contains the players.
Both can be captured by simply providing two variables.
- Code: Select all
local filter = ClearFilter()
filter:SetFilterType(FILTER_TYPE_CARDS + FILTER_TYPE_PLAYERS)
filter:Add(FE_TYPE, OP_IS, CARD_TYPE_CREATURE)
local Cards, Players = CW_Filter_FilterToDC(filter, EffectDC():Make_Chest(0), FILTER_TYPE_CARDS + FILTER_TYPE_PLAYERS)
------
As a complete example, Abbey Gargoyles was put into a deck. Then, Favor of the Gods (my test card) was given this code:
- Code: Select all
<RESOLUTION_TIME_ACTION>
local oFilter = ClearFilter()
oFilter:Add(FE_TYPE, OP_IS, CARD_TYPE_CREATURE)
local oCreatureChest = CW_Filter_FilterToDC(oFilter, EffectDC():Make_Chest(9000))
</RESOLUTION_TIME_ACTION>
<RESOLUTION_TIME_ACTION mode="1" repeating="1">
local oCard = EffectDC():Get_Chest(1):Get_CardPtr(0)
local i = MTG():GetActionRepCount()
local oCreature = EffectDC():Get_Chest(9000):Get_CardPtr(i)
if oCreature ~= nil then
if CW_Filter_HasProtectionFrom(oCard, oCreature) then
oFilter = ClearFilter()
oFilter:Add(FE_CARD_INSTANCE, OP_IS, oCreature)
EffectController():ChooseItem("Has protection from this card", EffectDC():Make_Chest(9001), QUERY_FLAG_MAY)
end
end
return i < EffectDC():Get_Chest(9000):Count()-1
</RESOLUTION_TIME_ACTION>
Also, since the initial protection filter is left alone, even if these do somehow break, the cards that use them won't suddenly stop working.
It all appears to be working. I'm really happy with this progress.
but now it's time for bed. Since it's all set up and working, I think I'll spend tomorrow sorting through the cards with protection and implementing new functions. (The protection check actually needs one tweak: a way to make sure the protection ability is still active. But Neo and I already came up with a method for that a long time ago.)
_______________________________
Community Wad - Community Wad Website - How to Help and Report Bugs
Discord: discord.gg/4AXvHzW
Community Wad - Community Wad Website - How to Help and Report Bugs
Discord: discord.gg/4AXvHzW
-
Xander9009 - Programmer
- Posts: 2905
- Joined: 29 Jun 2013, 07:44
- Location: Indiana, United States
- Has thanked: 121 times
- Been thanked: 445 times
Re: Storing and Retrieving Filters
by Xander9009 » 13 Sep 2016, 19:52
Update
Alright, it took a lot of trial and error, but Mirrorwing Dragon is working. Three things still need worked out, but the hard part is done. With 4 creatures on the battlefield, one of which was Mirrorwing Dragon, and one of which was Abbey Gargoyles, I cast Kindled Fury on Mirrorwing Dragon and it made two copies. Each copy targeted one of the two other creatures I had out, leaving Mirrorwing Dragon and Abbey Gargoyles untargeted by the copies.
The things that still need fixed are
1: Mirrorwing Dragon triggers when it's targeted, as opposed to when it's the only target.
2: Abbey Gargoyles is currently the only protection card that has the new functions.
3: A manager token needs made to reset a particular global variable at layer 0. Protection cards might need their protection moved from layer 0.
Alright, it took a lot of trial and error, but Mirrorwing Dragon is working. Three things still need worked out, but the hard part is done. With 4 creatures on the battlefield, one of which was Mirrorwing Dragon, and one of which was Abbey Gargoyles, I cast Kindled Fury on Mirrorwing Dragon and it made two copies. Each copy targeted one of the two other creatures I had out, leaving Mirrorwing Dragon and Abbey Gargoyles untargeted by the copies.
The things that still need fixed are
1: Mirrorwing Dragon triggers when it's targeted, as opposed to when it's the only target.
2: Abbey Gargoyles is currently the only protection card that has the new functions.
3: A manager token needs made to reset a particular global variable at layer 0. Protection cards might need their protection moved from layer 0.
_______________________________
Community Wad - Community Wad Website - How to Help and Report Bugs
Discord: discord.gg/4AXvHzW
Community Wad - Community Wad Website - How to Help and Report Bugs
Discord: discord.gg/4AXvHzW
-
Xander9009 - Programmer
- Posts: 2905
- Joined: 29 Jun 2013, 07:44
- Location: Indiana, United States
- Has thanked: 121 times
- Been thanked: 445 times
6 posts
• Page 1 of 1
Who is online
Users browsing this forum: No registered users and 16 guests