Board index Programs with AI or Rules Enforcement Magic: The Gathering - Duels of the Planeswalkers Programming Talk
"Name a card" functions
Moderator: CCGHQ Admins
"Name a card" functions
by thefiremind » 11 Oct 2013, 15:39
I updated my DotP2013 "name a card" functions to DotP2014.
When you need to declare a card name, if you use my functions, you'll be presented with a list of cards. The card you choose will decide the card name. The cards are sorted in alphabetical order, so you can't get information about their order in the libraries, and they are presented only once each, so you can't clearly determine how many copies of a card are in a zone.
Yes, of course you don't get to see all cards when you name a card in real Magic, but in DotP you can always see the contents of a deck you own before a duel, so it's not a bad approximation.
There are functions to save/load card names to/from an external table, one name for each object: this is useful when the object (that is, the card that asks you to name, not the card you name) needs to remember which name has been chosen.
Oh, I was forgetting something important: the AI's reaction. I gave both myself and the AI a deck with 15 Pithing Needle plus 15 various cards with activated abilities. The AI waited a very long time before playing a Pithing Needle, which is bad (unless it had no Pithing Needle in hand before that time), but the card name it chose was... Leyline of Deity! Yes, that's my testing facility card, and it was the best choice, no doubt about it. So the AI definitely understood how Pithing Needle works.
For those who like to see the code without downloading, here's a transcription of the LOL file:
When you need to declare a card name, if you use my functions, you'll be presented with a list of cards. The card you choose will decide the card name. The cards are sorted in alphabetical order, so you can't get information about their order in the libraries, and they are presented only once each, so you can't clearly determine how many copies of a card are in a zone.
Yes, of course you don't get to see all cards when you name a card in real Magic, but in DotP you can always see the contents of a deck you own before a duel, so it's not a bad approximation.
There are functions to save/load card names to/from an external table, one name for each object: this is useful when the object (that is, the card that asks you to name, not the card you name) needs to remember which name has been chosen.
Oh, I was forgetting something important: the AI's reaction. I gave both myself and the AI a deck with 15 Pithing Needle plus 15 various cards with activated abilities. The AI waited a very long time before playing a Pithing Needle, which is bad (unless it had no Pithing Needle in hand before that time), but the card name it chose was... Leyline of Deity! Yes, that's my testing facility card, and it was the best choice, no doubt about it. So the AI definitely understood how Pithing Needle works.
For those who like to see the code without downloading, here's a transcription of the LOL file:
- | Open
- Code: Select all
NAC_NameChest = {}
NAC_SaveName = function(card)
-- saves the card's name using the object itself as index
NAC_NameChest[Object()] = card:GetCardName()
end
NAC_LoadName = function()
-- loads the card's name using the object itself as index
return NAC_NameChest[Object()]
end
NAC_FillAlphabeticChest = function(chest, filter)
-- fills the chest with a list of alphabetically-sorted cards following the set filter
-- Safety return
if chest == nil or filter == nil then
return
end
-- We are just selecting a card for its name, so the filter will always need to be set to ZONE_ANYWHERE
filter:SetZone(ZONE_ANYWHERE)
-- Read the cards
local cards = {}
local card_index = 0
local filter_count = filter:EvaluateObjects()
for i=0,filter_count-1 do
local current_card = filter:GetNthEvaluatedObject(i)
local duplicate = false
-- Get rid of the duplicates (SetUnique is bugged in DotP2014 because it can't be cleared)
if card_index > 0 then
for j=0,card_index-1 do
duplicate = duplicate or ( cards[j]:GetCardName() == current_card:GetCardName() )
end
end
if duplicate == false then
cards[card_index] = current_card
card_index = card_index + 1
end
end
-- Bubble-sorting!
for i=card_index-1,1,-1 do
for j=0,i-1 do
if cards[j]:GetCardName() > cards[j+1]:GetCardName() then
local temp = cards[j]
cards[j] = cards[j+1]
cards[j+1] = temp
end
end
end
-- Finally, copy the cards to the data chest
for i=0,card_index-1 do
chest:Set_CardPtr(i, cards[i])
end
end
NAC_DeclareCardName = function(card, controller)
-- messages all players with the card's name (controller won't get the message, and the default for controller is EffectController)
local doesnt_need_message = EffectController()
if controller ~= nil then
doesnt_need_message = controller
end
for i=0,MTG():GetNumberOfPlayers()-1 do
local player = MTG():GetNthPlayer(i)
if player ~= nil and player:IsAI() == false and player ~= doesnt_need_message then
player:SetCustomQueryInstructionCardPtr(card)
player:BeginNewMultipleChoice()
player:AddMultipleChoiceAnswer("CONTROL_MB_CONFIRM")
player:AskMultipleChoiceQuestion("NAC_CARD_QUERY_CHOSEN_NAME")
end
end
end
- NAME_A_CARD.zip
- "Name a card" functions for DotP2014 plus Pithing Needle and Slaughter Games as examples
- (225.38 KiB) Downloaded 237 times
- Old topic contents for DotP2013 (might still be useful to someone) | Open
- BlindWillow had a very nice idea here to implement cards that require to "name a card". I tried to clean it up a bit and transform it into a prototype that could be used for different cards. You can see the functions and Pithing Needle as an example card.
- Code: Select all
FAC_SELECT_TEAM = 1
FAC_SELECT_PLAYER = 2
FAC_SELECT_ALL = 3
FAC_FLIP_SELECTION = 10
NameChest = {}
SaveName = function(card)
-- saves the card's name using the object itself as index
NameChest[Object()] = card:GetCardName()
end
LoadName = function()
-- loads the card's name using the object itself as index
return NameChest[Object()]
end
FillAlphabeticChest = function(chest, player, selection_constants, nonland)
-- fills effectDC's chest with a list of alphabetically-sorted cards from a player or a player's team
-- Select the affected players
local players = {}
local player_index = 0
if (selection_constants == FAC_SELECT_ALL) or
(selection_constants == FAC_SELECT_TEAM) or
(selection_constants == FAC_SELECT_TEAM + FAC_FLIP_SELECTION) then
-- I'm selecting everyone, or player's team, or the opponents if flipped
for i=0,MTG():GetNumberOfPlayers()-1 do
local nth_player = MTG():GetNthPlayer(i)
if (selection_constants == FAC_SELECT_ALL) or
(nth_player:GetTeam() == player:GetTeam() and selection_constants == FAC_SELECT_TEAM) or
(nth_player:GetTeam() ~= player:GetTeam() and selection_constants == FAC_SELECT_TEAM + FAC_FLIP_SELECTION) then
players[player_index] = nth_player
player_index = player_index + 1
end
end
elseif selection_constants == FAC_SELECT_PLAYER + FAC_FLIP_SELECTION then
-- I'm selecting players different from player
for i=0,MTG():GetNumberOfPlayers()-1 do
local nth_player = MTG():GetNthPlayer(i)
if (nth_player ~= player) then
players[player_index] = nth_player
player_index = player_index + 1
end
end
else
-- I'm selecting player
players[0] = player
player_index = 1
end
-- Safety return
if player_index == 0 then
return
end
-- Read the cards
local cards = {}
local filter = Object():GetFilter()
filter:Clear()
filter:SetUnique()
filter:AddExtra(FILTER_EXTRA_NOT_TOKEN)
if (nonland == 1 or nonland == true) then
filter:AddCardType(CARD_TYPE_LAND)
filter:AddExtra(FILTER_EXTRA_FLIP_CARD_TYPES)
end
filter:NotTargetted()
local card_index = 0
for i=0,player_index-1 do
filter:SetOwner(players[i])
local filter_count = filter:EvaluateObjects()
for j=0,filter_count-1 do
local current_card = filter:GetNthEvaluatedObject(j)
local duplicate = false
-- even with the SetUnique flag, basic lands would get duplicated, so I take care of them
if card_index > 0 then
for k=0,card_index-1 do
duplicate = duplicate or ( cards[k]:GetCardName() == current_card:GetCardName() )
end
end
if not duplicate then
cards[card_index] = current_card
card_index = card_index + 1
end
end
end
-- Bubble-sorting!
for i=card_index-1,1,-1 do
for j=0,i-1 do
if cards[j]:GetCardName() > cards[j+1]:GetCardName() then
local temp = cards[j]
cards[j] = cards[j+1]
cards[j+1] = temp
end
end
end
-- Finally, copy the cards to the data chest
if chest ~= nil then
for i=0,card_index-1 do
chest:Set_CardPtr(i, cards[i])
end
end
end
DeclareCardName = function(card)
-- messages all players with the card's name
local s = " "
MTG():MessageAllPlayers( " named card: \]\n\n\[ "..string.gsub(card:GetCardName(), "_", s)..s )
end
- Code: Select all
<?xml version='1.0'?>
<CARD_V2>
<FILENAME text="PITHING_NEEDLE_253581" />
<CARDNAME text="PITHING_NEEDLE" />
<TITLE>
<LOCALISED_TEXT LanguageCode="en-US"><![CDATA[Pithing Needle]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="it-IT"><![CDATA[Ago Spinale]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="de-DE"><![CDATA[Lähmungsnadel]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="fr-FR"><![CDATA[Aiguille à sectionner]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="es-ES"><![CDATA[Aguja medular]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="jp-JA"><![CDATA[真髄の針]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="ko-KR"><![CDATA[연수에 꽂는 바늘]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="ru-RU"><![CDATA[Смертельная Игла]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="pt-BR"><![CDATA[Agulha Medular]]></LOCALISED_TEXT>
</TITLE>
<MULTIVERSEID value="253581" />
<ARTID value="A253581" />
<ARTIST name="Mark Romanoski" />
<CASTING_COST cost="{1}" />
<FLAVOURTEXT>
<LOCALISED_TEXT LanguageCode="en-US"><![CDATA[The fearful want the procedure before a blood festival. The guilty seek it afterward.]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="it-IT"><![CDATA[I timorosi invocano la procedura prima di un festival del sangue. I colpevoli la cercano dopo.]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="de-DE"><![CDATA[Die Furchtsamen hätten die Operation gerne vor dem Blutfest. Wer sich schuldig gemacht hat, will sie danach.]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="fr-FR"><![CDATA[Les craintifs veulent subir l’opération avant un festival de sang. Les coupables, après.]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="es-ES"><![CDATA[Los temerosos quieren ser operados antes de un festival de la sangre. Los culpables desean ser operados después.]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="jp-JA"><![CDATA[恐れる者は血の祭りの前に処置を求める。罪人は祭りの後で処置を求める。]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="ko-KR"><![CDATA[두려움에 떠는 자들은 피의 축제 전에 시술을 받기를 원하고, 죄책감을 느끼는 자들은 축제 이후에 시술을 받기를 원한다.]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="ru-RU"><![CDATA[Страшащиеся хотят, чтобы процедуру провели до начала Праздника крови. Виновные желают ее после.]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="pt-BR"><![CDATA[Os temerosos desejam o procedimento antes do festival do sangue. Os culpados o procuram depois.]]></LOCALISED_TEXT>
</FLAVOURTEXT>
<TYPE metaname="Artifact" />
<EXPANSION value="RTR" />
<RARITY metaname="R" />
<TRIGGERED_ABILITY replacement_query="1" active_zone="ZONE_TRANSITION">
<LOCALISED_TEXT LanguageCode="en-US"><![CDATA[As Pithing Needle enters the battlefield, name a card.]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="it-IT"><![CDATA[Mentre l’Ago Spinale entra nel campo di battaglia, nomina una carta.]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="de-DE"><![CDATA[Sowie die Lähmungsnadel ins Spiel kommt, benenne eine Karte.]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="fr-FR"><![CDATA[Au moment où l’Aiguille à sectionner arrive sur le champ de bataille, nommez une carte.]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="es-ES"><![CDATA[En cuanto la Aguja medular entre al campo de batalla, nombra una carta.]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="jp-JA"><![CDATA[真髄の針が戦場に出るに際し、カード名を1つ指定する。]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="ko-KR"><![CDATA[연수에 꽂는 바늘이 전장에 들어오면서, 카드 한 장을 호명한다.]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="ru-RU"><![CDATA[При выходе Смертельной Иглы на поле битвы назовите карту.]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="pt-BR"><![CDATA[Conforme Agulha Medular entra no campo de batalha, nomeie um card.]]></LOCALISED_TEXT>
<TRIGGER value="ZONECHANGE_TRANSITION" simple_qualifier="self" to_zone="ZONE_IN_PLAY" />
<RESOLUTION_TIME_ACTION>
local player = EffectController()
local filterDC = EffectDC():Make_Chest(1)
FillAlphabeticChest(filterDC, player, FAC_SELECT_TEAM + FAC_FLIP_SELECTION, 0)
player:ChooseTargetFromDC( NO_VALIDATION, "CARD_QUERY_CHOOSE_CARD", filterDC, EffectDC():Make_Targets(0) )
</RESOLUTION_TIME_ACTION>
<RESOLUTION_TIME_ACTION>
if EffectDC() ~= nil then
local target = EffectDC():Get_Targets(0):Get_CardPtr(0)
if target ~= nil then
DeclareCardName(target)
SaveName(target)
end
end
</RESOLUTION_TIME_ACTION>
</TRIGGERED_ABILITY>
<STATIC_ABILITY filter_zone="ZONE_ANY">
<LOCALISED_TEXT LanguageCode="en-US"><![CDATA[Activated abilities of sources with the chosen name can’t be activated unless they’re mana abilities.]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="it-IT"><![CDATA[Le abilità attivate delle fonti col nome scelto non possono essere attivate a meno che non siano abilità di mana.]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="de-DE"><![CDATA[Aktivierte Fähigkeiten von Quellen mit dem bestimmten Namen können nicht aktiviert werden, außer es sind Manafähigkeiten.]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="fr-FR"><![CDATA[Les capacités activées des sources du nom choisi ne peuvent pas être activées à moins qu’elles ne soient des capacités de mana.]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="es-ES"><![CDATA[Las habilidades activadas de las fuentes con el nombre elegido no pueden activarse a menos que sean habilidades de maná.]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="jp-JA"><![CDATA[選ばれた名前を持つ発生源の起動型能力は、それがマナ能力でないかぎり起動できない。]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="ko-KR"><![CDATA[호명된 이름을 가진 원천의 활성화능력은 마나 능력이 아닌 한 활성화될 수 없다.]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="ru-RU"><![CDATA[Активируемые способности источников с выбранным именем не могут быть активированы, если только они не являются мана-способностями.]]></LOCALISED_TEXT>
<LOCALISED_TEXT LanguageCode="pt-BR"><![CDATA[As habilidades ativadas de fontes com o nome escolhido não podem ser ativadas a menos que sejam habilidades de mana.]]></LOCALISED_TEXT>
<FILTER>
return FilteredCard() ~= nil and
FilteredCard():GetCardName() == LoadName()
</FILTER>
<CONTINUOUS_ACTION layer="8">
if FilteredCard() ~= nil then
FilteredCard():GetCurrentCharacteristics():Characteristic_Set( CHARACTERISTIC_CANT_USE_ACTIVATED_ABILITIES, 1 )
end
</CONTINUOUS_ACTION>
</STATIC_ABILITY>
<AI_BASE_SCORE score="450" zone="ZONE_HAND" />
<AI_BASE_SCORE score="450" zone="ZONE_IN_PLAY" />
</CARD_V2>
I also added an easy-to-use storage for card names: each object will be able to save a card name and retrieve it anytime. Saving the card instead of the card name would arise the problem of protecting the pointer.
Note that I still don't know how the AI manages this. I made a test duel and the AI played Pithing Needle choosing a card, but that card wasn't on the battlefield so I don't know the reason of the choice. At least it didn't choose a basic land.
Last edited by thefiremind on 11 Oct 2013, 16:11, edited 1 time 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: "Name a card" functions
by Kieran » 11 Oct 2013, 15:59
Lol! And this is excellent my friend! Especially since the two cards included are needed for a few decks. Also, Counterbore is now in reach. Quick question though: Will your method conflict with the codes used for Surgical Extraction created by another programmer? The reason I'm asking is because I've already composed a pending deck request using the aforementioned SE card. I don't suspect there should be any interference but I want to be certain.
Re: "Name a card" functions
by thefiremind » 11 Oct 2013, 16:02
Neither Surgical Extraction nor Counterbore need to name a card. Their code can be adapted from the official Lobotomy code, which I used for Slaughter Games as well, but it has nothing to do with the "name a card" part. So, no interferences at all.Kieran wrote:Will your method conflict with the codes used for Surgical Extraction created by another programmer? The reason I'm asking is because I've already composed a pending deck request using the aforementioned SE card. I don't suspect there should be any interference but I want to be certain.
< 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
4 posts
• Page 1 of 1
Who is online
Users browsing this forum: No registered users and 17 guests