Ok so this might be a bit of a niche question, so I’ll try to simplify as much as possible.
There is this game called Genshin Impact.
Its combat system has a very interesting mechanic where each character ability can apply an element like Water, Fire, Electricity, Ice, etc. to the enemy. If two or more elements are applied on the same enemy, a reaction is triggered. For example, if Water and Ice are applied, the enemy will be frozen (stuck in place) for a few seconds. Or, another example, if Fire and Electricity are applied, an explosion occurs, dealing damage and knockbacks the enemy.
Now let’s say I hypothetically wanted to recreate a similar system in Roblox where everytime I apply two or more elements/statuses to an NPC or player, a reaction is triggered.
I tried to come up with my own method, but I couldn’t think of anything decent. I’ll try to explain my first idea in pseudo-code:
--runs everytime a new element is applied to the player
if player:hasElementApplied(“Water”) and player:hasElementApplied(“Ice”) then
triggerReaction(“Freeze”)
elseif player:hasElementApplied(“Fire”) and player:hasElementApplied(“Electricity”) then
triggerReaction(“Explosion”)
end
Basically everytime a new element is applied to the target player/NPC, an if statement is ran to check if two or more elements are active on a player. If a match is found, then the reaction is triggered.
Now, I’m perfectly aware that this is horrible coding and is just very unpractical and slow, because the moment you have 10+ reactions to consider, your if statement becomes huge and difficult to read, without forgetting how the game would need to check 10+ ifelse’s everytime, which seems pretty slow and performance heavy perhaps.
So my question is, is there a better way to handle a similar system? Perhaps thru a more OOP approach?
-- A table of tables recording the possible duos of elements and the resulting reaction
local REACTIONS = {
{"Water", "Electric", "Superconduct"},
{"Water", "Fire", "Vaporise"},
{"Fire", "Electric", "Overload"},
{"Ice", "Water", "Frozen"},
}
local playerAppliedElements = player:GetAppliedElements() -- Return a list of applied elements
local reaction = nil
for i = 1, #playerAppliedElements do -- Loop through applied elements and find a possible reaction
if #playerAppliedElements < 2 then break end -- Break loop if less there are less than two applied elements (no reactions would occur in this scenario)
local element1 = playerAppliedElements[i]
for k = 1, #playerAppliedElements do -- Loop through applied elements again to find a possible element partner
element2 = playerAppliedElements[k]
if element1 == element2 then
continue
else
-- If a possible element partner, which is not the current element, is found, loop through reactions
for m = 1, #REACTIONS do
local elementPairing = REACTIONS[m]
-- Check if these elements are actually paired with each other
if (elementPairing[1] == element1 and elementPairing[2] == element2) or (elementPairing[1] == element2 and elementPairing[2] == element1) then
reaction = elementPairing[3]
break -- A reaction is found, no need to check for other reactions
end
end
end
if reaction then
break -- A reaction is found, no need to check for other reactions
end
end
if reaction then
break -- A reaction is found, no need to check for other reactions
end
end
if reaction then
triggerReaction(reaction)
end
We could use a dictionary instead of a list, which would be more performant (although the example above is already efficient enough), but it would not be maintainable as we would have to rewrite element pairing: