Elemental Analyzer

Godot Version

4.2.2

Question

I’m trying to create an elemental analyzer. I have a dictionary to store the elements, and each element has a sub dictionary to store weaknesses and resistances. set up like:

var Elements = {
	"Water": {
		"Weaknesses" : ["Water" ,"Wind"],
		"Resistances" : ["Fire" , "Butts"]
}
}

I’ve played around with a few different ways, but nothing I tried gave me the result I wanted. I’m guessing for loops are going to be my friend here but the logic is above my head right now. I need to iterate through each element checking for weaknesses and resistances, and then add or subtract those. So currently, for the water element, the weaknesses should add 1.5, and the resistances should subtract .5, with a total of two elements being selectable. So if both Water, and Butts are weak to wind for instance, and you pick Water/Butts, the weakness would be a total of 3.
The resistances are the same idea but I’m going to cap it at .25 with something like
if weakness_val < .25:
weakness_val = .25

Weaknesses and Resistances also effect each other. So if one element is weak to fire, and the other element picked is resistant to fire, they’ll cancel out and fire will just do neutral damage, value of 1.

what are some ways to achieve this? What should I use, or research? I feel like I’m missing pieces of the puzzle here.

One way of tackling this problem is to implement your logic in multiple steps.

You are describing the effect of your weaknesses and resistances as being modulated by the amount of weaknesses/resistances between the two element pairs. This should be its own step in your algorithm: count the weaknesses and resistances.

Before we convert these weaknesses and resistances into a damage multiplier, they must – as you describe – cancel each other out. This essentially means subtracting the resistances from the weaknesses; 1 weakness minus 1 resistance equals 0 i.e. neutral damage. We can think of this resulting number as the element balance; we have an overall weakness if it’s positive, and an overall resistance if it’s negative.

With the “element balance” in place, we can now compute the damage multiplier. The way I’ve done it can be seen in the code below and mimics the rules you have described.

Algorithm Overview:

  1. Create a balance for each attacking element.
  2. Compute the balance based on how often an element occurs in the Weaknesses and Resistances of each defending element.
    • Weaknesses are positive, and resistances are negative
  3. Sum up the balances for the element pair
  4. Compute the damage multiplier for the balance sum based on your custom rules.

I will be using this element set for the example:

var elements = {
	"Water": {
		"Weaknesses": ["Electric", "Grass"],
		"Resistances": ["Fire", "Water"]
	}
	"Grass": {
		"Weaknesses": ["Fire", "Bug"],
		"Resistances": ["Electric", "Water"]
	}
}

Here’s the example function:

var myEnemy = ["Water", "Grass"]
var myAttack = ["Electric", "Bug"]

# Invocation example
# evaluate_attack_effectiveness(myAttack, myEnemy)

# atk is your attack, and def is your defender (i.e. your enemy)
func evaluate_attack_effectiveness(atk, def) -> float:
	# The corresponding balance of each element (weakness += 1, resist -= 1)
	var element_balances = []   # NOTE: Elements are given by the atk
	element_balances.resize(atk.size())
	element_balances.fill(0)    # They are probably already zero - but let's be sure.
	
	# Determine the balance for each element in the atk
	for i in range(atk.size()):
		var atk_element = atk[i]
		for j in range(def.size()):
			var def_element = def[j]

			# Compare element to all weakness elements
			for weak: String in elements[def_element]["Weaknesses"]:
				if atk_element == weak:
					element_balances[i] += 1
			# Compare element to all resistance elements
			for resist: String in elements[def_element]["Resistances"]:
				if atk_element == resist:
					element_balances[i] -= 1

	# Sum up the set of element balances
	var final_balance = 0
	for balance: int in element_balances:
		final_balance += balance

	# Compute effectiveness (your described rules)
	var effectiveness = 1.0
	if final_balance < 0:
		effectiveness -= 0.5 * final_balance
		effectiveness = max(0.25, effectiveness)
	if final_balance > 0:
		effectiveness += 1.5 * final_balance

	return effectiveness

NOTE: I still don’t know GDScript by heart; there are probably ways that this code can be improved. As I haven’t yet tested this script, please don’t hesitate to make any necessary corrections – or ask any questions. The double-nested for-loops might be hard to grasp at first.

I hope this helps you to understand and solve your problem!

1 Like

Thanks so much for the help! The missing piece of the puzzle I needed was just %s, but this will definitely be helpful when i implement combat! Thanks dude!