it'll require a rework. Apparently right now it's using a recursive algorithm, anything that catches damage runs a recursive resolve procedure, dealing damage and initiating splash as part of the process, if applicable, like for a barrel. In order to rework this one has to devise iterative scalable calculation process which will first distribute damage and then check for any explosions triggered, then process them if there are any, applying more damage, then deliver that damage and check again for explosions, etc. Probably a pseudocode could help:
// An object has two additional fields: a boolean flag "damaged interatively" and an integer "damage received"
// A splash damage function has a designated area of tiles which roll for damage, say "apply_damage(dice,sides)"
// This code triggers after something shoots and the shot is getting resolved.
if shot.splash then shot.doSplashDamage;
level.resolveAllDamage;
while (level.explosionsToProcess>0) do begin
for each (explosion in level.explosions) explosion.doSplashDamage;
level.resolveAllDamage;
end;
...
procedure doSplashDamage;
begin
for each (tile in this.splashArea) if tile.occupied then begin
tile.occupied.takeDamage(this.dice,this.sides,this.sourceTile);
end;
...
procedure object.takeDamage(dice:integer,sides:integer,sourceTile:Tile);
begin
var rolled:integer;
damageToResolve:=true;
rolled:=roll(dice,sides);
inc(damage,rolled)
addKnockback(rolled,source); // this accumulates knockback similarly to damage. Yes, barrels can get knocked back with this as well, get knocked into lava, etc etc. BTW, it'll allow barrels to move from floor to normal objects.
end;
...
procedure level.resolveAllDamage;
begin
explosions:=0;
for each (object in level) if object.damaged then object.processDamage;
// this is as normal, sets damaged to false, applied accumulated damage to HP, processes knockback, calls level.thereWasAnExplosion in case a barrel gets damaged or knocked into lava/acid so the level will know that the process has to be iterated again.
end;