It is currently 19 Apr 2024, 22:59
   
Text Size

"Name a card" functions

Moderator: CCGHQ Admins

"Name a card" functions

Postby 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:
| 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
If something doesn't work as expected, please let me know.

NAME_A_CARD.zip
"Name a card" functions for DotP2014 plus Pithing Needle and Slaughter Games as examples
(225.38 KiB) Downloaded 234 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>
The main idea I had was to sort the data chest in alphabetical order by GetCardName, this prevents the human players from getting any clue about the position of the cards in the opponents' libraries by looking at the order in which the cards are listed.
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. :lol:
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...
User avatar
thefiremind
Programmer
 
Posts: 3515
Joined: 07 Nov 2011, 10:55
Has thanked: 118 times
Been thanked: 721 times

Re: "Name a card" functions

Postby 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.
Kieran
 
Posts: 232
Joined: 03 Nov 2012, 01:09
Has thanked: 21 times
Been thanked: 16 times

Re: "Name a card" functions

Postby thefiremind » 11 Oct 2013, 16:02

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.
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.
< Former DotP 2012/2013/2014 modder >
Currently busy with life...
User avatar
thefiremind
Programmer
 
Posts: 3515
Joined: 07 Nov 2011, 10:55
Has thanked: 118 times
Been thanked: 721 times

Re: "Name a card" functions

Postby Kieran » 11 Oct 2013, 16:18

Noted. Thanks!
Kieran
 
Posts: 232
Joined: 03 Nov 2012, 01:09
Has thanked: 21 times
Been thanked: 16 times


Return to Programming Talk

Who is online

Users browsing this forum: No registered users and 31 guests


Who is online

In total there are 31 users online :: 0 registered, 0 hidden and 31 guests (based on users active over the past 10 minutes)
Most users ever online was 4143 on 23 Jan 2024, 08:21

Users browsing this forum: No registered users and 31 guests

Login Form