Elemental Reactions system

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?

1 Like

Try checking out ECS and the variety of libaries available for this.

1 Like

Tables are your best friend.

-- 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:

local REACTIONS = {
    ["Water"] = {
        ["Fire"] = "Vaporise",
        ["Electric"] = "Superconduct",
    },

    ["Fire"] = {
        ["Water"] = "Vaporise",
        ["Electric"] = "Overload",
    },

    ["Electric"] = {
        ["Water"] = "Superconduct",
        ["Fire"] = "Overload",
    },
}
1 Like