Xander9009 wrote:I'm trying to help code a custom ability, and one of the things required is that all of the affected cards (chosen in a filter block) have a continuous ability applied to them. Upon becoming tapped for mana, the effect should end for that particular card, but it shouldn't affect the others. The cards being affected do not belong to EffectController. What's the easiest way to manage this kind of effect?
Initially, I had it using a duration that checked a LinkedDC() int, but the granted triggered ability for BECAME_TAPPED_FOR_MANA wont be able to set the int since it would be firing from a different card (I think).
I thought about using a delayed trigger upon BECAME_TAPPED_FOR_MANA, but I couldn't figure out how to make a separate delayed trigger for each FilteredCard(). I came closest with this approach, but the DURATION still exists as a single block which I think will apply to all of the cards, ending the effect on them all as soon as it's forced to end for one.
Finally, I thought about granting the SetController() ability to the affected card with the duration block included and using a linked_ability_group. This would probably work, but I can't figure out to grant an ability to SetController(), since it would still require knowing the person controlling the effect. This can be achieved with an ObjectDC, but I'm hoping to manage it without requiring any constants or otherwise risking interference.
If necessary, I'll use ObjectDC, but even so, does anyone have any advice on this?
The easiest way I can think of to do this is to put all affected cards into a LinkedDC() when the effect starts along with the total number of affected cards. The STATIC_ABILITY that applies the CONTINUOUS_ACTION would then loop through the total number of cards stored in the LinkedDC() and apply the desired effect to any CardPtr that isn't nil. In a DelayedTrigger on BECAME_TAPPED_FOR_MANA you would search through the LinkedDC() (potentially looking through all registers until you hit the total number of affected cards) looking for the one that was tapped and upon finding that register it would clear the CardPtr (either by setting it to nil explicitly or setting an int value of 0 on that register). For the DelayedTrigger you can set the clean-up to happen once all of the CardPtrs are nil.
In that way once the card has been tapped for mana it is cleared from the LinkedDC() meaning it will no longer get the effect from the STATIC_ABILITY.
If you need this method to support multiple activations then you can assign each activation to use a separate chest in the LinkedDC() and have the STATIC_ABILITY loop through all of the chests that have been assigned. When a chest is emptied you can then set that register to nil and when looking for a new index to assign a chest to you look for the first register with a nil chest (which could be less than the previously set max number of chests). If you have to go beyond the previously set max number of chests you simply pick the next register create a chest there and increment the max number of chests and set that as the new max.
Xander9009 wrote:Unrelated to the previous (but I'm putting it here just 'cause I don't wanna spam the forum), shouldn't it theoretically be possible to produce a set of functions for declaring constants which aren't actually constants? As in, instead of declaring a constant, you declare the constant and then call a function on the constant. That function would set the constant to the first number not yet used by the function (which would require a manager).
In this way, everyone could just call something like "MY_CONST = InitConst()" and InitConst simply returns the one more than it did the last time it was called. That would mean everyone could be very certain they're not going to declare a constant that conflicts with anyone else's, and they don't even have to bother trying to track which numbers they've used before, because it doesn't matter what the number is, so long as it doesn't change throughout the game from the first time it's called.
This probably wouldn't work right unless you link the constant with some other identifier because global variable state is not preserved between actions (meaning "MY_CONST = InitConst()" would be called for every action resulting in lots of duplicate work and eventually running out registers). You could, however, create a set of functions such that if you call GetConst("MyConstIdentifier") it would return either a previously set constant for that identifier or set a new number and assign it to that identifier even without the need for a manager. This would allow people to use descriptive constant names to get a register index which would be less likely to conflict (you could still have a conflict if 2 different modders used the same string identifier). Though it should be noted that storing and retrieving strings to then compare is generally slow and it is just better to use unique indexes and set them statically. If people prefix the IDs they use for their constants with their reserved IdBlocks then conflicts really shouldn't happen.
Example:Person A has IdBlock 1234 reserved and Person B has IdBlock 2345 reserved and they set the indexes like this:
- Code: Select all
PERSON_A_CONST = 1234001
PERSON_B_CONST = 2345001
There shouldn't be any conflicts in this scenario and the overhead is almost non-existant.