Legacy GM storing card effects

I'm starting a card game project for a client and I need some advice on how I should go about storing the effects each card has. I'm currently storing info about each card in a 2D array.

Here is the current code:
Code:
/// init_card_info()
/*

    Card Number |   Subimage Number   |  Card Type    |   Name   |  Cost   |   Health   |   Armor   |   Attack  |   Description   

*/
var array_width = 9; // Relative to how much info should be in each row
global.total_cards = 10; // Number relative to the amount of cards in the game
var val = 0; // The values to be added to the array

// -- Set default values --
for ( var nn = 0; nn < global.total_cards; nn++; )
{
    for ( var ii = 0; ii < array_width; ii++; )
    {
    // Set the value to be stored in the array to either a card number or the value of noone (-4) to refer to a null or empty value.
        if ii == 0 { val = ii; } else { val = noone; } 

    // Store default values in the array     
        global.card_info[ ii, nn ] = val; 
    }
}



// -- Add information for every card  --
// Store info about each card within the global.card_info 2D array.
// Summons
add_card_info( 0, 1, 1, CARD_TYPE.CT_SUMMON, "Giant Turtle", 5, 10, 15, 1, "Giant Turtle test description." );
add_card_info( 1, 2, 2, CARD_TYPE.CT_SUMMON, "Frail Swordsman", 3, 5, 4, 4, "Frail Swordsman test description." );
add_card_info( 2, 3, 3, CARD_TYPE.CT_SUMMON, "Blue Ogre", 10, 15, 3, 10, "Blue Ogre test description." );
add_card_info( 3, 4, 4, CARD_TYPE.CT_SUMMON, "Flame Goblin", 8, 10, 4, 10, "Flame Goblin test description." );
add_card_info( 4, 5, 5, CARD_TYPE.CT_SUMMON, "Wild Dragon", 12, 15, 2, 10, "Wild Dragon test description." );
// Tools
add_card_info( 5, 6, 6, CARD_TYPE.CT_TOOL, "Wooden Sword", 2, noone, noone, noone, "Increase one unit's attack by three points." );
add_card_info( 6, 7, 7, CARD_TYPE.CT_TOOL, "Rusted Armor", 5, noone, noone, noone, "Increase one unit's armor by seven points." );
add_card_info( 7, 8, 8, CARD_TYPE.CT_TOOL, "Holy Water", 10, noone, noone, noone, "Increase one unit's health by five." );
add_card_info( 8, 9, 9, CARD_TYPE.CT_TOOL, "Degradation Cannon", 8, noone, noone, noone, "Decrease one unit's armor by six." );
add_card_info( 9, 10, 10, CARD_TYPE.CT_TOOL, "Golden Saber", 14, noone, noone, noone, "Increase one unit's attack by 20 points." );

// Events
add_card_info( 10, 11, 11, CARD_TYPE.CT_EVENT, "Sacrificial Restoration", 0, noone, noone, noone, "Sacrifice one summoned unit to restore five crystals." );
add_card_info( 11, 12, 12, CARD_TYPE.CT_EVENT, "Barrier", 3, noone, noone, noone, "Negates any damage inflicted to the user for one turn." );
add_card_info( 12, 13, 13, CARD_TYPE.CT_EVENT, "Quick Draw", 1, noone, noone, noone, "Draw two cards." );
add_card_info( 13, 14, 14, CARD_TYPE.CT_EVENT, "Earthquake", 10, noone, noone, noone, "Destroy all summoned units on the field." );
add_card_info( 14, 15, 15, CARD_TYPE.CT_EVENT, "Sleep Spell",  5, noone, noone, noone, "Select one summoned unit on your opponent's side of the field. That unit cannot attack for one turn." );
Code:
/// add_card_info( Array Coordinate, Card Number, Card Subimg, Card Type, Name, Cost, Health, Armor, Attack, Description )

var array_coord = argument0; // Second coordinate that will make up the position for specific info on each card
var card_num = argument1; // Number associated with the card. This could be used as a unique identifer (id) or for sorting purposes.
var card_subimg = argument2; // The subimage number of the card_img_spr sprite that relates to a specific card. 0 will always be the "backside" image of the card.
var card_type = argument3; // Indicates whether the card is a "Summon", "Tool" or "Event" card.
var name = argument4; // Name of the card
var cost = argument5; // How much it costs to use the card and the penalty for losing a summoned unit.
var hp = argument6; // Amount of health a Summon has. When it reaches 0, the card will be destroyed and the amount of crystals lost depends on the penalty (cost).
var armor = argument7; // Factored into the amount of damage taken from an opponent's attack. Opponent Attack - Armor = Damage
var attack = argument8; // The base damage a monster will deal while attacking
var desc = argument9; // Card description


global.card_info[ 0, array_coord ] = card_num;
global.card_info[ 1, array_coord ] = card_subimg;
global.card_info[ 2, array_coord ] = card_type;
global.card_info[ 3, array_coord ] = name;
global.card_info[ 4, array_coord ] = cost;
global.card_info[ 5, array_coord ] = hp;
global.card_info[ 6, array_coord ] = armor;
global.card_info[ 7, array_coord ] = attack;
global.card_info[ 8, array_coord ] = desc;
 
H

Homunculus

Guest
If I had to implement this myself, I'd probably use an external JSON file to store the card data, instead of adding them by code directly, especially if the card database is large, but a 2d array is viable as well and has its advantages. I suggest mapping the indexes of a card property to an enum though, so you can get and set the card name, type, etc... without hardcoding the index.

Example:
Code:
//set a card name
global.card_info[ CARD_PROP.card_name, array_coord ] = "Lightsaber";

//get a card name
var _name = global.card_info[ CARD_PROP.card_name, card_id ];
In your setup it is also difficult to address a specific card. Supposed you want to add a "Blue ogre" card to a hand, how do you plan on getting the array coord that is storing that specific card? Again, if the total number of cards isn't too high, you can map each card to an enum and address them by enum name. The alternative is assigning a unique string identifier to each card and store the array coord in a ds_map, mapping the string identifier to the array coord.

That being said, if I understood what you are asking, you need a way to map each card to "something" that happens when you play it or when activated, correct?
This depends a lot on "what" and "how" effects happen in your game. A simple and fairly flexible solution could be to add a property to your cards that stores a script index. Imagine therefore defining the "Earthquake" card as follows (note the last argument):

Code:
add_card_info( 13, 14, 14, CARD_TYPE.CT_EVENT, "Earthquake", 10, noone, noone, noone, "Destroy all summoned units on the field.", scr_event_eartquake );
When that card is played, all you do is call script execute on that last parameter:

Code:
var _effect = global.card_info[ CARD_PROP.card_effect, card_id ];
script_execute(_effect);
You don't need a script for every effect of course, "Quick draw" could simply call an existing draw_card(number_of_cards) script for example. Note that in this case though, simply having a script index may not be enough, since you may want to also pass some parameters to the script as well. You could in fact use an array instead of the script index directly, and treat the array values as arguments:

Code:
add_card_info( 12, 13, 13, CARD_TYPE.CT_EVENT, "Quick Draw", 1, noone, noone, noone, "Draw two cards.", [scr_draw_cards, 2] );
 
I suggest mapping the indexes of a card property to an enum though, so you can get and set the card name, type, etc... without hardcoding the index.
Thanks for the suggestion. Adding the enums now. If I need to make changes, it will be a lot quicker now.

In your setup it is also difficult to address a specific card. Supposed you want to add a "Blue ogre" card to a hand, how do you plan on getting the array coord that is storing that specific card?
Created a script yesterday to address that issue, forgot to include it the post.
Code:
/// find_card_info( card name, CARD_INFO enum )
/*
    This script will return info related to any card from the global.card_info @D array.  

    ex.
        find_card_info( "Degradation Cannon", CARD_INFO.CI_TYPE )     '
      
        ^ Get the card type for the card named "Degradation Cannon"
*/
var card_name = argument0; // Name of a card as a string
var card_info_arg = argument1; // CARD_INFO enum constant relating to the info the game should return
var val = noone; // Value to be returned. The value of none (-4) will be passed by default
var card_coord = find_card_info( card_name, CARD_INFO.CI_ARRAY_COORD ); // Get the second array coordinate for the card.


// Set the val depending on the enum passed to the script
switch( card_info_arg )
{
    case CARD_INFO.CI_ARRAY_COORD:
        var end_loop = false;  
        for ( var ii = 0; ii < global.total_cards && end_loop == false; ii++; )
        {
            if global.card_info[ 3, ii ] == card_name
            {
                val = ii; // Set the val to the second coordinate for the card name position
                end_loop = true; // Stop the loop
            }
        }  
    break;
      

    case CARD_INFO.CI_NUM:
        val = global.card_info[ 0, card_coord ];                  
    break;

        
    case CARD_INFO.CI_SUBIMG:
        val = global.card_info[ 1, card_coord ];  
    break;
  
  
    case CARD_INFO.CI_TYPE:
        val = global.card_info[ 2, card_coord ];  
    break;
  
    case CARD_INFO.CI_NAME: // Will likely not be used, but adding anyway just for uniformity
        val = global.card_info[ 3, card_coord ];  
    break;  
      
  
    case CARD_INFO.CI_COST:
        val = global.card_info[ 4, card_coord ];  
    break;
  
  
    case CARD_INFO.CI_HP:
        val = global.card_info[ 5, card_coord ];  
    break;
  
  
    case CARD_INFO.CI_ARMOR:
        val = global.card_info[ 6, card_coord ];  
    break;
  
  
    case CARD_INFO.CI_ATK:
        val = global.card_info[ 7, card_coord ];  
    break;
  
  
    case CARD_INFO.CI_DESC:
        val = global.card_info[ 8, card_coord ];  
    break;

}


// Return the value of val
return( val );
However, using a for loop would be a lot slower than your suggestion to use an enum, so when it comes to getting the array coordinate I'll probably take your advice there as well.

EDIT: Looking at this now, the script is kinda unnecessary especially CARD_INFO enum if I implement your CARD_PROP suggestion.

That being said, if I understood what you are asking, you need a way to map each card to "something" that happens when you play it or when activated, correct?
Exactly.
Note that in this case though, simply having a script index may not be enough, since you may want to also pass some parameters to the script as well. You could in fact use an array instead of the script index directly, and treat the array values as arguments
Your solution may have addressed my biggest concern, which is cards with multiple effects or effects that differ depending on certain conditions.

Thanks
 
Top