Board index Programs with AI or Rules Enforcement Magic: The Gathering - Duels of the Planeswalkers Programming Talk
Idea: a manager for "can't untap more than..." effects
Moderator: CCGHQ Admins
Idea: a manager for "can't untap more than..." effects
by thefiremind » 17 Oct 2016, 09:06
This could be useful for all DotP iterations, so please, don't hesitate to reply even if you are modding DotP2014.
Dovin Baan made me think again about "can't untap more than..." effects. The major problem in coding one of them is that it should interact nicely with other cards with similar effects.
Here's a list of cards with that kind of effect:However, there are many duplicates, so the list could be transformed into two shorter lists: who can be affected and how.
Who:
Some things we need to be careful with:
"who" and "how" in a DuelDataChest register "how" in the current player's data chest if they are affected by it. The manager reads them, combines them, and gives the correct result during the following untap step.
EDIT: The manager is complete. This version is for Magic Duels, feel free to adapt it for earlier versions (I'm pretty sure that the only thing you need to do is to substitute string indexes with numbered indexes).
Use the code here for reference, or download it together with 2 cards I included in the attachment (I haven't included the images).
Dovin Baan made me think again about "can't untap more than..." effects. The major problem in coding one of them is that it should interact nicely with other cards with similar effects.
Here's a list of cards with that kind of effect:However, there are many duplicates, so the list could be transformed into two shorter lists: who can be affected and how.
Who:
- All players
- The controller (Mungha Wurm only)
- Controller's opponents (Dovin Baan only)
- Can't untap more than 1 artifact
- Can't untap more than 2 permanents
- Can't untap more than 1 land
- Can't untap more than 1 creature
Some things we need to be careful with:
- If one of those cards changes controller, the "who" needs to be updated.
- Static Orb and Winter Orb only work untapped.
- Permanents that can't untap during the current untap step shouldn't be selectable. And that's something I have never tested, but, does a permanent forced not to untap through Hold() or TapAndHold() return true if CHARACTERISTIC_DOESNT_UNTAP is checked on it?
If so, then it's easy, we just need to be sure that we check it before it automatically goes away during the untap step.It turns out that the answer to the question is no, so this solution wasn't feasible.
EDIT: The manager is complete. This version is for Magic Duels, feel free to adapt it for earlier versions (I'm pretty sure that the only thing you need to do is to substitute string indexes with numbered indexes).
Use the code here for reference, or download it together with 2 cards I included in the attachment (I haven't included the images).
- MD_TFM_UNTAP_LIMIT_FUNCTIONS.LOL | Open
- Code: Select all
-- Functions that help coding cards with "can't untap more than..." effects, such as Static Orb
-- WARNING: The first two functions have been copied from my general functions LOL file.
TFM_GetOrMakeChest = function(parent, index)
-- Gets the chest at the given index in the given parent. If it's nil, the chest is initialized.
if parent == nil or index == nil then
return nil
end
local chest = parent:Get_Chest(index)
if chest == nil then
chest = parent:Make_Chest(index)
end
return chest
end
TFM_GetManager = function(index, set, object)
-- Returns the manager registered for the chosen index.
-- If set is true, it also sets the object itself as manager if none has been found.
local mainDC = TFM_GetOrMakeChest( MTG():DuelDataChest(), "TFM_Managers" )
if set == true and mainDC:Get_CardPtr(index) == nil then
object = object or Object()
mainDC:Set_CardPtr(index, object)
end
return mainDC:Get_CardPtr(index)
end
TFM_UL_CARD_TYPE_PERMANENT = -1111
TFM_UL_UNLIMITED = 9999
TFM_UL_PERMANENT_TYPES = {
TFM_UL_CARD_TYPE_PERMANENT,
CARD_TYPE_ARTIFACT,
CARD_TYPE_CREATURE,
CARD_TYPE_ENCHANTMENT,
CARD_TYPE_LAND,
CARD_TYPE_PLANESWALKER,
CARD_TYPE_TRIBAL
}
TFM_SetUntapLimit = function(amount, type, player)
-- To be used in a trigger that fires before the player's untap step, sets that player's untap limit for a card type.
-- If no type is specified (or TFM_UL_CARD_TYPE_PERMANENT is used), the limit applies to all permanents.
-- Send the untap limit to the manager
amount = amount or 1
type = type or TFM_UL_CARD_TYPE_PERMANENT
player = player or TriggerPlayer()
local playerDC = player:PlayerDataChest()
local DC1 = TFM_GetOrMakeChest(playerDC, "TFM_Untap_Limits_1")
local subDC = DC1:Make_Chest( DC1:Count() )
subDC:Set_Int(0, type)
subDC:Set_Int(1, amount)
MTG():ClearFilterMark() -- Prepare to mark untappable cards (the BECAME_UNTAPPED trigger should mark them)
MTG():DuelDataChest():Set_Int("TFM_Untap_Limits_On", 1) -- Turn on the override of the untapping
end
TFM_ManageUntapLimit1 = function(player)
-- To be used in an untap step beginning trigger, reads the untap limits for the given player and reorganizes them.
player = player or TriggerPlayer()
local playerDC = player:PlayerDataChest()
local DC1 = playerDC:Get_Chest("TFM_Untap_Limits_1") -- This chest has the untap limits sent by the cards
if DC1 == nil then
return -- This player has no limits: do nothing
end
playerDC:Free_Compartment("TFM_Untap_Limits_2")
local DC2 = playerDC:Make_Chest("TFM_Untap_Limits_2") -- This chest will have the reorganized untap limits
for i, type in ipairs(TFM_UL_PERMANENT_TYPES) do
DC2:Set_Int(type, TFM_UL_UNLIMITED) -- Any type with this large number will be recognized as having no limit
end
-- Get each limit sent by the cards
local count = DC1:Count()
for i=0,count-1 do
local limitDC = DC1:Get_Chest(i)
local type = limitDC:Get_Int(0)
local amount = limitDC:Get_Int(1)
local currentAmount = DC2:Get_Int(type)
if amount < currentAmount then
DC2:Set_Int(type, amount) -- There's a lower limit for this type: remember it
end
end
playerDC:Free_Compartment("TFM_Untap_Limits_1") -- Delete the limits sent by the cards, we don't need them anymore
end
TFM_ManageUntapLimit2 = function(player)
-- To be used after TFM_ManageUntapLimit1 and in a repeating action, queries the given player on what to untap.
-- Remember to write "return" before the function: the repeating action needs to know whether it should repeat or not!
player = player or TriggerPlayer()
local playerDC = player:PlayerDataChest()
local DC2 = playerDC:Get_Chest("TFM_Untap_Limits_2")
if DC2 == nil then
return false -- This player has no limits: exit
end
local n = MTG():GetActionRepCount()
local filter = ClearFilter()
if n>0 then
local lastTarget = EffectDC():Get_Targets(n-1) and EffectDC():Get_Targets(n-1):Get_CardPtr(0)
if lastTarget ~= nil then
-- Subtract the last target's types from the limit
local lastTargetTypes = lastTarget:GetCardType()
for i, type in ipairs(TFM_UL_PERMANENT_TYPES) do
if DC2:Get_Int(type) < TFM_UL_UNLIMITED and
( type == TFM_UL_CARD_TYPE_PERMANENT or lastTargetTypes:Test(type) ) then
DC2:Int_Sub(type, 1)
end
end
end
for i=0,n-1 do
local target = EffectDC():Get_Targets(i) and EffectDC():Get_Targets(i):Get_CardPtr(0)
if target ~= nil then
filter:Add(FE_CARD_INSTANCE, OP_NOT, target) -- Disallow selecting a previous target again
end
end
end
if DC2:Get_Int(TFM_UL_CARD_TYPE_PERMANENT) == 0 then
return false -- No more permanents to untap: exit
end
filter:Add(FE_CONTROLLER, OP_IS, player)
filter:Add(FE_IS_TAPPED, true)
local subFilter = nil
if DC2:Get_Int(TFM_UL_CARD_TYPE_PERMANENT) == TFM_UL_UNLIMITED then
subFilter = filter:AddSubFilter_Or() -- No limit on all permanents, but only on specific types
end
for i, type in ipairs(TFM_UL_PERMANENT_TYPES) do
if type ~= TFM_UL_CARD_TYPE_PERMANENT then
if DC2:Get_Int(type) == 0 then
filter:Add(FE_TYPE, OP_NOT, type) -- Disallow types whose limit went to 0
elseif subFilter ~= nil and DC2:Get_Int(type) < TFM_UL_UNLIMITED then
subFilter:Add(FE_TYPE, OP_IS, type) -- Restrict to specific types
end
end
end
filter:SetMarkedObjectsOnly() -- Only untappable cards can be selected
if filter:CountStopAt(1) == 0 then
return false -- No feasible targets: exit
end
player:ChooseItem( "CARD_QUERY_CHOOSE_PERMANENT_TO_UNTAP", EffectDC():Make_Targets(n) )
return true
end
TFM_ManageUntapLimit3 = function()
-- To be used after TFM_ManageUntapLimit2, untaps what has been selected.
MTG():DuelDataChest():Set_Int("TFM_Untap_Limits_On", 0) -- Turn off the override of the untapping
local i = 0
local target = EffectDC():Get_Targets(i) and EffectDC():Get_Targets(i):Get_CardPtr(0)
while target ~= nil do
target:Untap()
i = i+1
target = EffectDC():Get_Targets(i) and EffectDC():Get_Targets(i):Get_CardPtr(0)
end
end
- Attachments
-
- Untap Limit Functions.zip
- LOL file + Static Orb + Stoic Angel
- (6.46 KiB) Downloaded 385 times
Last edited by thefiremind on 19 Oct 2016, 15:49, edited 3 times in total.
< Former DotP 2012/2013/2014 modder >
Currently busy with life...
Currently busy with life...
-
thefiremind - Programmer
- Posts: 3515
- Joined: 07 Nov 2011, 10:55
- Has thanked: 118 times
- Been thanked: 721 times
Re: Idea: a manager for "can't untap more than..." effects
by Xander9009 » 17 Oct 2016, 23:27
I'm sort of wondering if you've learned to read minds... I didn't have this idea, but rather I was trying to debug Dovin Baan for 2014 and ran into some issues I can't figure out for the life of me. Decided to visit the programming section and ask for help when I noticed a thread I'd somehow left unread specifically for Dovin Baan...
I'll leave my issues to another thread, of course, but I really like this idea.
Would it be best to have it in a DuelDataChest or a PlayerDataChest? For Dovin Baan and Mungha Wurm, it might be better off in a PlayerDC. Or perhaps have those two split up from the rest? Then each player's manager would check the DuelDC for global ones and their own PlayerDC for player-specific ones.
Did you guys manage to create an invisible manager? I recall something about them no longer being invisible.
Also, this is a little off topic but it might be helpful if you're about to delve into chests: I've got a couple of CW function files that might be useful for Duels as much as they are for 2014, but for this (and many other things), one comes to mind in particualr: CW_DC. It's only got three functions, but they're convenient for simplifying some repetitive code, such as getting a certain subchest, and creating it if it doesn't already exist.
EDIT: And about 5 minutes after posting this (which was a good 2+ hours of fiddling with the thing), I finally realized the issue wasn't even inside the ability at all. It was in the ability's opening tag: replacement_effect="1" instead of replacement_query="1".
I'll leave my issues to another thread, of course, but I really like this idea.
Would it be best to have it in a DuelDataChest or a PlayerDataChest? For Dovin Baan and Mungha Wurm, it might be better off in a PlayerDC. Or perhaps have those two split up from the rest? Then each player's manager would check the DuelDC for global ones and their own PlayerDC for player-specific ones.
Did you guys manage to create an invisible manager? I recall something about them no longer being invisible.
Also, this is a little off topic but it might be helpful if you're about to delve into chests: I've got a couple of CW function files that might be useful for Duels as much as they are for 2014, but for this (and many other things), one comes to mind in particualr: CW_DC. It's only got three functions, but they're convenient for simplifying some repetitive code, such as getting a certain subchest, and creating it if it doesn't already exist.
- CW_DC.LOL | Open
- Code: Select all
-----------------------------
--File Info------------------
-----------------------------
--A simple set of functions for working with data chests.
-------------------------
--Card Chest Registers---
-------------------------
--Make sure registers are entered into CW_Constants.lol
-------------------------
--Function List----------
-------------------------
--Parameters in braces {} are optional.
--CW_DC_DuelDC(iRegister)
-- Retrieves the DuelDataChest at the specified register, creating it if necessary.
-- Input: Int
-- Output: Chest
--CW_DC_PlayerDC(iRegister{, oPlayer = EffectController()})
-- Retrieves the PlayerDataChest at the specified register, creating it if necessary.
-- Input: Int{, Player}
-- Output: Chest
--CW_DC_GetChest(oParentChest, iRegister{, iMakeChest = 1})
-- Get chest {iRegister} within {oParentChest}.
-- {iMakeChest}
-- == 0/false: Do not make chest if it doesn't exist.
-- == 1/true: (Default) Create chest if it doesn't exist.
-- == 2: Create chest (even if it already exists, erasing previous contents).
-- Input: Chest, Int{, Int/Bool}
-- Output: Chest
-------------------------
--Functions Definitions--
-------------------------
--Retrieves the DuelDataChest at the specified register, creating it if necessary.
--Input: Int
--Output: Chest
CW_DC_DuelDC = function(iRegister)
if iRegister == nil or type(iRegister) ~= "number" or iRegister < 0 then
CW_General_Error("CW_DC_GetDuelDC: Invalid register: "..type(iRegister))
return nil
end
local oDuelDC = MTG():DuelDataChest():Get_Chest(iRegister)
if oDuelDC == nil then
oDuelDC = MTG():DuelDataChest():Make_Chest(iRegister)
end
return oDuelDC
end
--Retrieves the PlayerDataChest at the specified register, creating it if necessary.
--Input: Int{, Player}
--Output: Chest
CW_DC_PlayerDC = function(iRegister, oPlayer)
if iRegister == nil or type(iRegister) ~= "number" or iRegister < 0 then
CW_General_Error("CW_DC_GetDuelDC: Invalid register: "..type(iRegister))
return nil
end
if oPlayer == nil then
oPlayer = EffectController()
end
local oPlayerDC = oPlayer:PlayerDataChest():Get_Chest(iRegister)
if oPlayerDC == nil then
oPlayerDC = oPlayer:PlayerDataChest():Make_Chest(iRegister)
end
return oPlayerDC
end
--Get chest {iRegister} within {oParentChest}.
--{iMakeChest}
-- == 0: Do not make chest if it doesn't exist.
-- == 1: (Default) Create chest if it doesn't exist.
-- == 2: Create chest (even if it already exists, erasing previous contents).
--Input: Chest, Int{, Int/Bool}
--Output: Chest
CW_DC_GetChest = function(oParentChest, iRegister, iMakeChest)
if oParentChest == nil then
CW_General_Error("CW_DC_GetChest: oParentChest is nil.")
return nil
elseif iRegister == nil or type(iRegister) ~= "number" then
CW_General_Error("CW_DC_GetChest: iRegister is nil.")
return nil
end
local oChest = oParentChest:Get_Chest(iRegister)
if iMakeChest == nil or iMakeChest == true or iMakeChest == 1 then
iMakeChest = 1
elseif iMakeChest == false or iMakeChest == 0 then
iMakeChest = 0
end
if iMakeChest == 2 or (iMakeChest == 1 and oChest == nil) then
oChest = oParentChest:Make_Chest(iRegister)
end
return oChest
end
EDIT: And about 5 minutes after posting this (which was a good 2+ hours of fiddling with the thing), I finally realized the issue wasn't even inside the ability at all. It was in the ability's opening tag: replacement_effect="1" instead of replacement_query="1".
_______________________________
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: Idea: a manager for "can't untap more than..." effects
by thefiremind » 18 Oct 2016, 08:18
I thought this through a bit more (that's the primary reason I waited for answers before writing code, so I could actually give better answers to myself ) and I think that a PlayerDC is the best option: the "who" doesn't even need to be managed by the manager, we just send the "how" to the affected players' data chests when their turn comes.Xander9009 wrote:Would it be best to have it in a DuelDataChest or a PlayerDataChest?
I tested that if I create an emblem with no types, it's invisible. I haven't tested if it reserves space on the battlefield even if it's invisible. Anyway, for this project I decided to make the cards themselves as managers, as I did for Briarbridge Patrol (first card to trigger registers itself as manager).Xander9009 wrote:Did you guys manage to create an invisible manager? I recall something about them no longer being invisible.
Thanks, I had made something similar for my DotP2014 mod so that I could occupy only 1 register in each main chest and never conflict with other mods, but in Duels there's no such problem because we can use strings as indexes.Xander9009 wrote:Also, this is a little off topic but it might be helpful if you're about to delve into chests: I've got a couple of CW function files that might be useful for Duels as much as they are for 2014, but for this (and many other things), one comes to mind in particualr: CW_DC.
< Former DotP 2012/2013/2014 modder >
Currently busy with life...
Currently busy with life...
-
thefiremind - Programmer
- Posts: 3515
- Joined: 07 Nov 2011, 10:55
- Has thanked: 118 times
- Been thanked: 721 times
Re: Idea: a manager for "can't untap more than..." effects
by Tejahn » 26 Oct 2016, 00:07
Thanks for this! I try it and report back.
Re: Idea: a manager for "can't untap more than..." effects
by Splinterverse2 » 31 Oct 2016, 13:28
This is good stuff. I am excited that we might get Dovin Baan for 2014!
- Splinterverse2
- Posts: 52
- Joined: 20 Sep 2016, 13:52
- Has thanked: 13 times
- Been thanked: 0 time
Re: Idea: a manager for "can't untap more than..." effects
by Xander9009 » 31 Oct 2016, 15:56
Dovin is already in 2014.Splinterverse2 wrote:This is good stuff. I am excited that we might get Dovin Baan for 2014!
_______________________________
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: Idea: a manager for "can't untap more than..." effects
by Splinterverse2 » 31 Oct 2016, 17:44
Doh! Been so busy coding, I haven't played in a while. Hopefully soon.Xander9009 wrote:Dovin is already in 2014.Splinterverse2 wrote:This is good stuff. I am excited that we might get Dovin Baan for 2014!
- Splinterverse2
- Posts: 52
- Joined: 20 Sep 2016, 13:52
- Has thanked: 13 times
- Been thanked: 0 time
7 posts
• Page 1 of 1
Who is online
Users browsing this forum: No registered users and 27 guests