Monster Collecting RPG Question

pikachurian

Member
I want to make a monster collecting rpg like pokemon. I am having trouble finding a way to handle the monster collecting. Specifically, I want the player to have a party of monsters that can fight in battle. Monsters can be left at a "bank" if you have too many or would like to change your team. How would you handle having a party of monsters with various levels, moves ect. And storing these monsters in a bank while retaining their stats. Also how would you handle having two monsters of the same type with different levels and health in the same party. Should I use ds_maps and ds_lists storing to the different stats and your party. Or should I use objects for the monsters and instancing those objects for the monsters in your bank/party. I am having trouble with thinking of a way to efficiently implement the above features.
 

Neptune

Member
Hey, I've done exactly what you described above in my game Serin Fate, and I will say it is very difficult.
Granted, my experience was mostly just trial and error, but I believe even with a solid coding foundation it is still cumbersome.

Really it doesnt matter how you store your data. No matter how you do it, you will use many data structures.
I think the only time I use a ds_map is for the main over-arching storage of each save file, otherwise it is all lists/grids etc.
Have you ever made a simple inventory? Perhaps start there... Or declare some base data for your creatures as an enumerated value. Enum values are your best friend for this stuff.
 

Neptune

Member
Consider this code, for declaring some global data about your creatures.
Basically you could make a script, that would run one time to declare base data for your creatures... And then anytime you need their base data, retrieve the appropriate data from the global.DS_creatures -- For example, when spawning an instance of your creature; you could grab its base data, and then perhaps make some changes based on RNG to have a unique spawn or something...

GML:
enum CREATURE
{
    snake,
    bird,

    enum_count,
}

var data_grid = ds_grid_create(1,4);
global.DS_creatures = ds_grid_create(1,CREATURE.enum_count);

for(var i = 0; i < CREATURE.enum_count; i++)
{
   switch(i)
   {
       case CREATURE.snake:
           name = "Snek";
           level = 0;
           attack = 3;
           flyer = false;
       break;

      case CREATURE.bird:
           name = "Bird";
           level = 1;
           attack = 5;
           flyer = true;
       break;
   }

    data_grid[# 0,0] = name;
    data_grid[# 0,1] = level;
    data_grid[# 0,2] = attack;
    data_grid[# 0,3] = flyer;
    global.DS_creatures[# 0, i] = ds_grid_write(data_grid);
}
ds_grid_destroy(data_grid);
In this example, I use grids, but you can just as easily use lists.

Once you've done this, say you want to get CREATURE.snake's data... From anywhere, at any time:

GML:
var snake_data = global.DS_creature[# 0,CREATURE.snake];
//read data into grid
//access name etc
 
Last edited:

pikachurian

Member
Thank you for the reply, I got the idea to use ds_maps to store the monster data from a video by Shaun Spalding on weapons, where he used a ds_map for each weapon to store the weapon's data. The ds_maps were stored in a weapon array. If I store the base data for the different monster types in a ds_list/ds_map, how would I make a monster of a certain type in my party have different values, such has health, than its base, without modifying the original base ds_list/ds_map?
 

pikachurian

Member
I saw the trailer for your game. It looks really cool. The game I am working on uses a more traditional turn-based battle system. Would your answers still apply to a turn-based combat game?
 

Neptune

Member
So for each instance to have different stats, you would take your base stats for the species, change some numbers around based on randoms, like attack = base_attack + random(-base_attack/2,base_attack/2);
And then the only difficulty is saving that data... Does your creature exist as an instance? In my game the creatures run around in real time, and can also be in "item form", so my way of handling data is quite complex for them.

If your creatures only come out in a battle (and are otherwise stored in data in list form, like most pokemon games), this could be easier.
You could have global.DS_creatures_base AND global.DS_creatures_rolled... The rolled grid is where you would store your RNG data that you modified from the base (representing an instance of your creature).
That "roll grid" would basically be your player's bank of creatures they have found, which would get bigger and bigger as they found more (or duplicates)... Or every time a creature is found, a row is added to the "roll grid" and each new cell contains the modified data you obtained from the "base grid".
 

pikachurian

Member
Thanks again, by different values I was refereing to things such as having your monster take damage or learning new moves. Would I need to create a new ds_list when you capture a monster to store the values of your instance of that monster type? Whats the best way to store the data of the different monsters in your party and the bank, without using too much memory or processing power?
 

Neptune

Member
Do not worry about performance or processing at all.
Really it shouldnt be too taxing to set up this system. The data is mostly just sitting in memory, and only called here and there it sounds likes (when you enter battle).
For your instance data (list or grid) I would make them larger than the base data... So they have the base data (name, species, description, flyer etc etc) and they have extra cells that have instance data (Current level, HP, stat levels, status effects, equipped items etc etc).


GML:
//base "species" data
[0] --> name
[1] --> description
[2] --> flyer?

//final instance data
[0] --> name
[1] --> description
[2] --> flyer?
//============================================
[3] --> max hp (rolled and possibly changes)
[4] --> current hp (changes)
[5] --> current level (changes)
[6] --> total attack (rolled and possibly changes)
So your species data is called every time you make a new instance (to populate its base information), and then your final "instance" data is what is stored and actively changed as the creature levels up or the player progresses...
Basically ANY data that never changes for each species, hard-code that somewhere for reference.

Alternatively, you don't NEED to keep the unchanging species data with each instance, and you could always reference the global data pool... But it might be cleaner to have it with each instance for access reasons.
 
Last edited:

pikachurian

Member
Thank you. I think I am now going to use ds_lists to store the data of the monsters in your party. How would you handle a leveling up system, where each monsters gains new moves at different levels and increases to their stats, like health.
 

Neptune

Member
Lists will work. You can store lists within lists by writing / reading them to strings.
So have another cell in your "instance data" for a list of abilities.

For leveling, you could have a script that pulls out the creature's data, makes necessary changes, and then sends it back.
 

Iskardes

Member
GML:
var snake_data = global.DS_creature[# 0,CREATURE.snake];
//read data into grid
//access name etc
[/QUOTE]

Your last bit of code here missed of the “s” in creatures but apart from that runs well. I’m trying to code a similar(ish) project, can I ask why you use the ds_grid_write function? I tried to test the code further by simply putting the snakes name to a draw function, but it couldn’t convert to a string. Apologies if it’s a dumb question, I’m struggling to apply data structures in GML. 😔
 

Neptune

Member
Hey, so you write the grid to a string to store it... By writing it to a string, you are literally storing the entire contents of the grid.
If you were to just put the name of the grid, you are not storing its contents - you would be just storing its ID or a reference to the grid.

GML:
var list = ds_list_create();
var grid = ds_grid_create(1,3);
grid[# 0,1] = "hello";

// storing a grid reference into a list
ds_list_add(list,grid);

var reference = list[| 0];
var str = reference[# 0,1]; // retrieve "hello";

//======================================= By doing the above, your grid must constantly exist in memory. The below allows you to pack/unpack as needed.

// storing the entire grid contents into a list
ds_list_add(list,ds_grid_write(grid)); // packing away our grid, to be unpacked later when needed

// retrieving entire stored contents
var retrieve_grid = ds_grid_create(0,0);
ds_grid_read(retrieve_grid,list[| 1]);
retrieve_grid[# 0,1] = "goodbye" // modify
list[| 1] = ds_grid_write(retrieve_grid); // repack
The bottom sequence of unpack / modify data / repack is my go-to way of doing things. It is just the way that works for me :)
I like to use grids over lists in many cases, because it allows you to create them at various sizes, and insert data where you want, instead of just pancaking with ds_list_add ds_list_add ds_list_add ....
 
Last edited:

TailBit

Member
What's happening in @Neptune code is that he prepare a 1 wide X deep grid (which could be a list instead) and fill it with the results from ds_grid_write for each monster, which is not human readable and you must use ds_grid_read before you can get any data out of it, I do find that a bit strange, maybe it was to make the data more compact while playing, but he could have used lists if he just gonna use one dimention?

You could just make one huge grid with all the data:
Code:
enum CREATURE{
    snake,
    bird,

    enum_count,
}

enum _MON_BASE { _name,_hp,_atk,_type,length} // I prefer using length as the enum counter
enum TYPE { normal,flyer,digger,length}

global.DS_creature = ds_grid_create(CREATURE.enum_count,4); // I like to have the monster name first

/* then you usually would add all the monster like so:
data_grid[# CREATURE.snake, _MON_BASE._name] = "snake";
data_grid[# CREATURE.snake, _MON_BASE._hp  ] = 5;
data_grid[# CREATURE.snake, _MON_BASE,_atk ] = 4;
data_grid[# CREATURE.snake, _MON_BASE,_type] = TYPE.flyer;

instead I would recommend that you make a script to make that process a bit easier
(unless you need to fill in more data then the script arguments can take)
*/

mon_data_add(CREATURE.snake, "snake",5,4,TYPE.flyer)
sript mon_data_add
GML:
// mon_data_add( mon_index, "name", hp, atk, type)
data_grid[# argument0, _MON_BASE._name] = argument1;
data_grid[# argument0, _MON_BASE._hp  ] = argument2;
data_grid[# argument0, _MON_BASE,_atk ] = argument3;
data_grid[# argument0, _MON_BASE,_type] = argument4;
draw event:
Code:
var c = CREATURE.snake;
draw_text(x,y, "name: "+data_grid[# c, _MON_BASE._name] + "\nbase hp: " + string(data_grid[# c, _MON_BASE._hp]))
however, you can also use ds_lists, 1 ds_list to hold them all, then one for each monster inside of it, you can then use ds_list_add(monster_list,"name", stat,stat,stat .... ,stat) and if there are too many then you can just continue with another ds_list_add, but you could ofc make another script for the ds_list to put additional data in..

In your finished product you will most likely want to have made a ds_grid_write output of your complete monster list with all the data, so that loading the whole thing is a easy operation..
 

Iskardes

Member
Thanks for your replies! I’m still struggling to apply it to my code, but this has certainly helped. I bought the Heartbeast GML book in the hopes that I might get something out of that for help with data structures too, we’ll see! 😬
 
Top