Beginner, making sure I understand Resources to set up the monster database in a retro-styled RPG

Version: v4.2.2.stable.official [15073afe3]

Hello, this is my first post, bear with me.

I’ve recently started the Godot journey, coming from another engine that was not Object-Oriented. To learn, I’ve been following the “recreate your favorite game” path. I chose Etrian Odyssey, a series of first-person dungeon crawlers with tile-based, turn-based RPG combat goodness.

It’s been working so far. I found out how to make sprite fonts with kerning to use the official games’ font, utilising Astar for pathfinding, getting a 3D maze generated from a 2D tilemap with Terrain, making use of composition to dictate what inputs menus can use, etc.

Now getting to the point: I want to get started on the battle system, but that requires entities to fight. I originally had this Autoload that fills up an Array of Dictionaries containing the stats of everything… but it looks gross. I eventually learned about Resources, but I’m not sure I’ve got a grasp on how I’m meant to use them.

Player characters (built from standard RPG classes like Warriors, Mages, Healers, etc.) and enemies have:

  • a name;
  • about 8 values for stats (Strength, Agility, etc.);
  • resistances to the 6-7 elements at play (Fire, Ice, etc.);
  • resistances to the 10-ish ailments (Poison, Paralysis, etc.);
  • and more.

So, the way I think I understand Resources is that I’d make a new Script that extends Resource, have it declare a new Class and define its exported variables for my needs… but now what?

Do I just go into my FileSystem Tab and fill up a folder with instances of that new Class, one for every character/monster stat combination, knowing there’ll be hundreds of them? Is that it?

Assuming so, I can have an Autoloaded Array that just stores their path so that, in battle, some generic battler Node knows what set(s) of stats to copy onto itself based on an ID.

I could also go farther and make separate “sub” Resources for resistances and such, to have presets for the occasional common patterns (for example, every type of player character has neutral affinities to everything, unlike monsters which tend to have their own unique set).

All that to ask… is this a valid way to use Resources? Or am I severely misunderstanding the idea/their purpose?
Thank you in advance… and my apologies for this wall of text.

First of all: yes, I guess this is a valid use of resources. You can use them like this if this works for you.
But I’m not sure why you need to create all possible combinations or have an Autoload that holds these.

Resources don’t have to be static. You can create or change them dynamically.

Lets say your resource script looks like this:

class_name RpgCharacterData
extends Resource

enum ClassType { WARRIOR, MAGE, HEALER }

@export var class_type: ClassType
@export var ui_img: Texture
var name: String
var strength: int = 10
#...

You can start with some example / template resources, the way you already described it, by creating .tres files with a common setup.

Let’s say you want to create a new mage enemy:

func create_new_mage_data() -> RpgCharacterData:
	var new_enemy: RpgCharacterData = RpgCharacterData.new()
	new_enemy.class_type = RpgCharacterData.ClassType.MAGE
	new_enemy.name = "Generated Mage"
	new_enemy.strength = randi_range(1,2)
	return new_enemy

If you want to update existing data you can use exports:

@export var mageData: RpgCharacterData

func create_new_mage_data_from_template() -> RpgCharacterData:
	var modified_enemy: RpgCharacterData = mageData.duplicate(true)
	modified_enemy.name = "Dumbledore"
	modified_enemy.strength = 3
	return modified_enemy

I hope this clarifies some things

1 Like

Unfortunately, I’m still very unsure about this. I’m sorry, I’m mostly just a fumbling idiot when it comes to code… and explaining things.

The real question I’m asking myself is “How/Where do I store all the specifics?”

I initially figured I’d need that Autoload, so that in battle, some generic battler Nodes/Scenes could call something like that, upon being instantiated:

# it looks for the given monster_id in the Autoload's list Array, 
# and duplicates the stats at that index,
# and the elemental resistances,
# and the ailment resistances...
func inherit_monster_stats(monster_id: int):
	base_stats = Autoload.list[str(monster_id)]["stats"].duplicate()
	base_elem_resistance = Autoload.list[str(monster_id)]["elem"].duplicate()
	base_ailm_resistance = Autoload.list[str(monster_id)]["ailm"].duplicate()
	#...

I’ll also need that database to be accessed by some bestiary-type menu too.
Of course, I’m willing to bet the proper use cases of Autoloads elude me, too.

Right now, without using Resources, the Autoload looks like this:

func _init():
	# Player Class: Hexer
	create_class("hexer", "Hexer", "Masters of the dark arts, shrouded in mystery. And their cloak.")
	create_class_stats_hp_tp(33, 372, 31, 183) # HP at level 1, HP at level 99, etc.
	create_class_stats_str_vit(1, 60, 1, 56) # strength & vitality
	create_class_stats_int_wis(4, 75, 5, 88) # intelligence & wisdom
	create_class_stats_agi_luc(1, 75, 5, 100) # agility & luck
	create_class_player_affinities() # sets resistances to 100% for player characters
	create_battle_sprite("res://Assets/Sprites/Characters/Hexer1.png")
	create_class_finish() # increments a value so the next functions know which index to fill

	# Monster Type: Rat
	create_class("rat", "Rat", "It's evil! But vaguely cute!")
	create_monster_stats_hp(45) # enemies don't need levels nor mp
	create_monster_stats_str_vit(5, 1)
	create_monster_stats_int_wis(1, 1)
	create_monster_stats_agi_luc(4, 2)
	create_monster_elements(150, 100, 125, 165, 100, 100)
	create_monster_ailments(200, 164, 100, 0, 150, 100, 80, 100, 100, 90)
	create_monster_binds(100, 150 ,150)
	create_battle_sprite("res://Assets/Sprites/Monsters/Rat.png")
	create_class_finish()

I was hoping Resources would let me get away with a relatively cleaner look:
(I imagine there’d be unique IDs to avoid using the path itself)

# Player Class: Hexer
# fills index 0 with that instance of RpgCharacterData
	create_battler_type("res://RpgCharacterData/Class/Hexer.res")

# Enemy Type: Rat
# fills index 1 with that instance of RpgCharacterData
	create_battler_type("res://RpgCharacterData/Enemy/Rat.res")

Anyway, thank you for your answer.
I think I probably should just stick to doing more research on my own, instead of wasting people’s time here, with my very barebones understanding.

1 Like

I think you’re heading in the right direction. My answer was just an attempt to show that you can change the resources and do some procedural generation.

If you want something like a database for every monster, your approach is totally fine. I think using an autoload for looking up the resources for a specific monster / player type is good as well, I just did not know what the use case is.

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.