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:
- Create a balance for each attacking element.
- 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
- Sum up the balances for the element pair
- 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!