Hi, so i’m very new to GDsscript and programming in general, and this is my first programming language, and i was trying to practice creating a resource for my modular skill system that can reference my ClassStat resource
this is the ClassStat script
extends Resource
class_name ClassStat
# Core Stat
@export var health : int
@export var mana : int
@export var base_movement_range : int
@export var dodge : int
# Attribute Stat
@export var strength : int
@export var dexterity : int
@export var agility : int
@export var intelligence : int
@export var willpower : int = 7
var max_health : int
var max_mana : int
var movement_range : int
# Leveling variables
var level : int = 1
var experience : int = 0
var experience_threshold : int = 100
func calculate_base_stat():
max_health = health + (strength * 3)
max_mana = mana + (intelligence * 2)
movement_range = round(agility / 3)
dodge = round(agility / 4)
func update_stat():
calculate_base_stat()
func level_up():
level +=1
experience_threshold = int (experience_threshold * 1.5)
func gain_experience(amount : int):
experience += amount
func leveling_up():
if experience >= experience_threshold:
level_up()
and this is the SkillStat script
extends Resource
class_name SkillStat
@export var name : String
@export var base_damage : int
@export var applied_effect : String
var total_damage : int
func calculate_damage():
total_damage = base_damage + attributes
what i’m confused is how do i reference the attributes in the ClassStat script for the calculation in the SkillStat script, sorry if the answer was obvious to some of you, i’m a newbie through and through in the programming world
You need a reference to an instance of the ClassStat resource in order to reference what is inside it. Depending on how you have your system set up, you can/should either obtain a reference of a ClassStat instance through the use of an @export variable, or via a parameter in calculate_damage():
# Instance reference via exported variable
@export var class_stats: ClassStat
# -----------------------------------------------------
# Instance reference via function parameter
func calculate_damage(class_stats: ClassStat):
# Your code
Let me know if you have any questions about anything else.
oh, i didn’t know you had to reference the ClassStat script, also i don’t know how to reference the attributes in the ClassStat, so i have 3 attributes for damage calculation : Strength, Dexterity, and Intelligence
and what i’m confused about is, how do i reference those 3 in the ClassStat, do i need to write 3 function for each of them, or how do i create a variable that can call each of them when i put the right attributes on the skill resource?
what i meant is, i want to export the attributes needed to the inspector panel, and let the code pick the right attributes to calculate itself with if that makes sense? sorry i’m not good with technical words either
thanks for answering me beforehand
How do I reference the stat variables from the ClassStat class?
As mentioned, you need an instance of ClassStat in order to utilize the variables within that class. It seems like you don’t quite grasp the concept of an instance. I will try to provide an adequate description of what it means.
Instances in programming
Let’s start by focusing on ClassStat. Your ClassStat is a class: a concrete description of a unique object. Within a class, you define variables and functions. Variables are used to store and evolve data over time, and functions contain code that performs a set of operations. In short, a class defines the data and behaviour that constitutes an instance of that class. Think of classes as blueprints. You use a blueprint to build a house you can live in but that doesn’t mean you live in the blueprint.
A tried and true example of classes in programming is the Person class.
Person Class Example
Consider the class:
class_name Person
var name: String
var age: int
# This is the constructor for the class.
# A way to initialize the object with specific data.
func _init(name: String, age: int):
self.name = name
self.age = age
A person has a name and an age so these will be part of the class. However, we have yet to create any people; we must create Person instances. Therefore, we invoke the following code:
var p1 = new Person("Jake", 32)
var p2 = new Person("Jill", 27)
var p3 = new Person("Joan", 67)
As such, we have now created three distinct people; three instances of the Person class each with their own data.
So, let’s assume you have a ClassStat instance. You can now reference variables and functions inside the instance through dot notation.
I noticed that you are incorrectly referencing the class_stat parameter in your own example. Remember that GDScript’s syntax has the variable name first, then the type. Perhaps you were just tired?
func calculate_damage(class_stat: ClassStat):
# Since this function is part of SkillStat, you don't need to
# pass the base_damage in as a parameter. It is already available.
total_damage = base_damage + class_stat.strength
return total_damage
…and calculate_damage() would be called with:
var skill_stat: SkillStat # A variable containing a SkillStat instance
var skill_dmg = skill_stat.calculate_damage(self)
# The "self" keyword is a reference to the instance running the code.
# In this case, the ClassStat instance.
Question #2:
I’m afraid I need a better description of what you want to achieve with your skill system.
To export a variable you just need to use the @export attribute. I don’t understand the other part of this question though: “picking the right attributes to calculate itself”. Perhaps you can elaborate this point?
With regards to the "right attribute’ I’m going to guess as this is an RPG, the character/s will have/use different attack options. Maybe a sword, spell,etc. So the choice will either be chosen by the player, or the system will determine by the players “class” or similar. We’re missing a lot of information.
willpower is the only variable having a set value. I’m guessing it’s suppose to be null
I was going to suggest the lazy option and just place all of the variables inside the Player script. But if this the game is going to have a lot of players, a modular approach would be a much better choice.
sorry if what i’m saying sound gibberish to you, it’s been a long day for me trying to learn instancing and class resource, and how they connect, so i got everything messed up
so the game that i’m trying to make is, a tactical turn based RPG, where each entity will have a weapon they can equip that have their own skill tree, so the skill aren’t tied to the entity class, but rather the weapon itself will have the skill, as for the entity class, it will only have restriction to what kind of weapon it can use
each entity class then will have 3 main attributes that the skill will use to calculate it’s damage, and they are : Strength, Dexterity and Intelligence
each skill class then have a base damage, which will pick the correct attributes from the ClassStat to be used as calculation, so for example, let’s say a skill need to use the characters Strength attributes to be used as calculation, how do i reference the strength variable from the ClassStat? and then what if the skill use dexterity for the damage calculation, how do i reference that, do i need to create 3 different func for each attributes or how do i tell the code to use the right attributes for damage calculation
so do i put the damage calculation inside the skill itself? since my weapon will just have the sprite and skill it contain, it will do nothing else right?
so this is my understanding about how this code maybe should work
Character here -> equip weapon permissible by their class -> weapon have skills -> character uses the skill inside the weapon to attack
and then for the damage calculation
take skill base damage -> take entity attributes (strength, dexterity, or intelligence) -> do the calculation -> get total damage -> return the damage value to the entity class -> deal damage to character
so the only interactions here as far as i know, are only between the skills and the entity itself, since weapon will just contain their sprite, and their skill set or skill tree
and then do i call this damage func, inside the ClassStat to reduce the health amount? if so would it look like this
var skill_stat = SkillStat
func take_damage(skill_stat : SkillStat):
health -= skill_stat.total_damage
is that correct, or is my overall perception of doing this is wrong? sorry again for my current knowledge of programming is very basic
You want the damage of a skill to be influenced by one or more of the three attributes: strength, dexterity, and intelligence. You want to be able to configure which attributes are taken into account.
One way of doing this is to create an enum that is based on the available attributes (see docs for description).
enum Attributes {
STR,
DEX,
AGI,
INT
} # The naming is shortened to make it faster to write but it's up to you.
Then you can expose a list in Godot’s inspector that allows you to pick which stat(s) influences the damage output.
class_name SkillStat
@export var stat_influences : Array[Attributes]
Now that you have a list of influential stats, you can iterate over the list of attributes, get the stat for each attribute from the class, and compute the correct damage.
func calculate_damage(class_stat: ClassStat):
var total_stats = 0
# Iterate over all the influences
for i in stat_influences.size():
if stat_influences[i] == Attributes.STR:
totat_stats += class_stat.strength
if stat_influences[i] == Attributes.DEX:
total_stats += class_stat.dexterity
if stat_influences[i] == Attributes.AGI:
total_stats += class_stat.agility
if stat_influences[i] == Attributes.INT:
total_stats += class_stat.intelligence
# Compute the damage
var damage = base_damage * total_stats
return damage
Additional comments
Yes, that is correct. You seem to already have an understanding of the reductionistic principles of programming; that each object is often comprised of smaller, more specialized objects. If you’re interested, I would recommend that you study inheritance and composition in programming. These concepts are vital to understand if you want to create big systems.
Also correct. Actually, exchanging your arrows for dot notation gets you pretty close to the actual code you would write.
var weapon: Weapon
var damage = weapon.skill.calculate_damage(self)
Well, this is the same as your previous arrow-based example just at a different scope, and a damage-dealing operation at the end.
You could indeed implement health in your ClassStat. However, if you plan to build a game that is more than small in size, you should not rely on such an approach. More often than not, health as a concept is used for a wide set of objects in games. To avoid writing repetitive code (the same code in multiple scripts), you would write a Health class. This class/node would then be added to objects via composition. Other nodes can then subscribe to Health’s signals (damage, heal, or death signal) to retrieve information from the Health instance.
You can find an example of a networked health node I created at the bottom of this post. The post in its entirety is concerned with composition and inheritance.
One last thing. You have to remove your habit of saying sorry for not being good enough. There’s nothing wrong with being a beginner. Be excited that you have any programming knowledge at all!
Reserve saying sorry for times when you realize that you haven’t given something or someone the correct amount of energy or attention.
I hope this answered most of your questions. Let me know if you have any more questions.
oh, i think i understand it now, thanks for the explanation buddy, i do read the godot docs for inheritance, class and composition, but i just don’t understand how to applied it in such a big scope
also a quick question, can i still use this inside the ClassStat script?
@export var strength : int
@export var dexterity : int
@export var agility : int
@export var intelligence : int
just for ease of assigning value to the entity later on
also this produces some error
export var attributes : Array[Attributes]
# i change the stat_influence to attributes with lower case a
it says could not find Attributes in the currect scope, did the
var class_stat : ClassStat
not reference the enum perhaps? or did i write something wrong, but the enum in ClassStat the enum are named Attributes with upper case A, just like what you write
EDIT: I don’t know if you can also define Attributes in ClassStat and then do: ClassStat.Attributes.STR. If you can, it probably makes more sense to store it in ClassStat since that is where the stats are also stored.
oh yea, i forgot to write the @ at export, sorry about that little mistake
as for the Attributes enum, i write inside the SkillStat? i thought i need to write in the ClassStat, but i did just moved it into the SkillStat and now it’s fixed, thanks so much my friend
i will definitely remember you when i finished my game