332 lines
15 KiB
Lua
332 lines
15 KiB
Lua
local function fire_damage_entity(surface, entity, force, cause, killPlanes)
|
|
if (entity.valid and entity.position and (killPlanes or entity.type ~= "car")) then
|
|
if(not (entity.prototype.max_health == 0)) then
|
|
-- For thermobarics, with the blast wave carrying the fire
|
|
local type = entity.type
|
|
if (type == "unit" or type == "car" or type == "spider-vehicle") then
|
|
local fireShield = nil
|
|
if entity.grid then
|
|
for _,e in pairs(entity.grid.equipment) do
|
|
if(e.name=="fire-shield-equipment" and e.energy>=500000) then
|
|
fireShield = e;
|
|
break;
|
|
end
|
|
end
|
|
end
|
|
if fireShield then
|
|
fireShield.energy = fireShield.energy-500000
|
|
else
|
|
surface.create_entity{name="fire-sticker", position=entity.position, target=entity}
|
|
end
|
|
if(cause and cause.valid) then
|
|
entity.damage(20, force, "fire", cause)
|
|
if(entity.valid)then
|
|
entity.damage(40, force, "physical", cause)
|
|
end
|
|
if(entity.valid and entity.type == "car" and (entity.prototype.max_health >= 1000 or fireShield)) then
|
|
entity.damage(80, force, "fire", cause)
|
|
end
|
|
else
|
|
entity.damage(20, force, "fire")
|
|
if(entity.valid)then
|
|
entity.damage(40, force, "physical")
|
|
end
|
|
if(entity.valid and entity.type == "car" and (entity.prototype.max_health >= 1000 or fireShield)) then
|
|
entity.damage(80, force, "fire")
|
|
end
|
|
end
|
|
elseif (type ~= "tree") then
|
|
if(cause and cause.valid) then
|
|
entity.damage(100, force, "fire", cause)
|
|
else
|
|
entity.damage(100, force, "fire")
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
local function damage_entity(surface, distSq, ePos, power, fire, damage_init, blast_min_damage, entity, force, cause, corpseMap)
|
|
-- do blast damage - reduced for rails, belts, land mines and flying vehicles, as this makes some sense, and trees in order to leave some alive
|
|
local eProto = entity.prototype
|
|
local damage = power/distSq*damage_init+blast_min_damage
|
|
local t = entity.type
|
|
|
|
if(t=="tree") then
|
|
if(fire) then
|
|
surface.create_entity{name="fire-flame-on-tree", target = entity, position=ePos}
|
|
end
|
|
damage = math.random(damage/8, damage)/2
|
|
|
|
if(eProto.resistances and eProto.resistances.explosion) then
|
|
damage = (damage-entity.prototype.resistances.explosion.decrease)*(1-eProto.resistances.explosion.percent)
|
|
end
|
|
-- If a tree is destroyed, don't bother doing particle effects, just destroy it - huge performance savings
|
|
if(entity.health<damage) then
|
|
entity.destroy()
|
|
surface.create_entity{name="tree-01-stump",position=ePos}
|
|
else
|
|
entity.health = entity.health-damage
|
|
if entity.tree_stage_index_max>1 then
|
|
local damage_level = (1-entity.health/eProto.max_health)*entity.tree_stage_index_max
|
|
entity.tree_stage_index = math.ceil(damage_level)
|
|
end
|
|
end
|
|
return
|
|
else
|
|
if(t=="curved-rail") then
|
|
damage = damage/10
|
|
elseif (t=="straight-rail") then
|
|
damage = damage/10
|
|
elseif (t=="transport-belt") then
|
|
damage = damage/10
|
|
elseif (t=="land-mine") then
|
|
damage = damage/10
|
|
elseif(t=="car" or t=="spider-vehicle") then
|
|
if (next(entity.prototype.collision_mask)==nil)then
|
|
damage = damage/2
|
|
end
|
|
end
|
|
damage = math.random(damage/2, damage*2)
|
|
local calcDamage = damage;
|
|
if(eProto.resistances and eProto.resistances.explosion) then
|
|
calcDamage = (calcDamage-eProto.resistances.explosion.decrease)*(1-eProto.resistances.explosion.percent)
|
|
end
|
|
if((not entity.grid) and entity.health>calcDamage) then
|
|
entity.health = entity.health-calcDamage
|
|
else
|
|
if((not entity.grid) and corpseMap[entity.name]) then
|
|
local corpseName = corpseMap[entity.name]
|
|
--local ghost
|
|
--if(eProto.create_ghost_on_death or eProto.create_ghost_on_death == nil) then
|
|
-- ghost = {inner_name = entity.name, name = "entity-ghost", direction = entity.direction, expires = true, force = entity.force, position = entity.position}
|
|
-- if(t == "assembling-machine" and entity.get_recipe()) then
|
|
-- ghost.recipe = entity.get_recipe().name
|
|
-- end
|
|
--end
|
|
entity.destroy{raise_destroy = true}
|
|
surface.create_entity{name=corpseName, position=ePos}
|
|
--if(eProto.create_ghost_on_death or eProto.create_ghost_on_death == nil) then
|
|
-- surface.create_entity(ghost)
|
|
--end
|
|
else
|
|
if(cause and cause.valid) then
|
|
entity.damage(damage, force, "explosion", cause)
|
|
else
|
|
entity.damage(damage, force, "explosion")
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
local function move_blast(i,blast,pastEHits, corpseMap)
|
|
|
|
-- Compute the number of regions we move the blast in
|
|
local regNum = 8
|
|
if(blast.r<=500 or not blast.doItts) then
|
|
regNum = 8
|
|
elseif(blast.r<=1000) then
|
|
regNum = 24
|
|
elseif(blast.r<=2000) then
|
|
regNum = 48
|
|
elseif(blast.r<=4000) then
|
|
regNum = 96
|
|
else
|
|
regNum = 192
|
|
end
|
|
-- Do we need to wait a while (we might need to if the simulated blast is going faster than expected)
|
|
blast.ittframe = blast.ittframe+1
|
|
if(blast.itt > regNum and blast.ittframe >=8) then
|
|
blast.r = blast.r + blast.speed
|
|
blast.ittframe = 1
|
|
blast.itt = 1
|
|
elseif (blast.itt > regNum) then
|
|
return
|
|
end
|
|
|
|
local surface = game.surfaces[blast["s"]]
|
|
local center = blast["pos"]
|
|
local sideOffset = blast.speed*1.5
|
|
local extraSpace = blast.speed
|
|
|
|
local eHits = pastEHits
|
|
|
|
local area = {{}, {}}
|
|
|
|
-- Some hard-coded regions for small blasts
|
|
local regions = {
|
|
{{center.x-blast.r/2-sideOffset, center.y+(blast.r-extraSpace)*0.86603-0.5}, {center.x+blast.r/2+sideOffset, center.y+blast.r+1}},
|
|
{{center.x-blast.r/2-sideOffset, center.y-blast.r}, {center.x+blast.r/2+sideOffset, center.y-(blast.r-extraSpace)*0.86603+0.5}},
|
|
{{center.x+(blast.r-extraSpace)*0.86603-0.5, center.y-blast.r/2-sideOffset}, {center.x+blast.r+1, center.y+blast.r/2+sideOffset}},
|
|
{{center.x-blast.r, center.y-blast.r/2-sideOffset}, {center.x-(blast.r-extraSpace)*0.86603+0.5, center.y+blast.r/2+sideOffset}},
|
|
|
|
{{center.x-(blast.r-extraSpace)*0.86603-0.5, center.y+blast.r/2-extraSpace/2-0.5}, {center.x-blast.r/2+extraSpace/2+0.5, center.y+(blast.r-extraSpace)*0.86603+0.5}},
|
|
{{center.x+blast.r/2-extraSpace/2-0.5, center.y+blast.r/2-extraSpace/2-0.5}, {center.x+(blast.r-extraSpace)*0.86603+0.5, center.y+(blast.r-extraSpace)*0.86603+0.5}},
|
|
{{center.x-(blast.r-extraSpace)*0.86603-0.5, center.y-(blast.r-extraSpace)*0.86603-0.5}, {center.x-blast.r/2+extraSpace/2+0.5, center.y-blast.r/2+extraSpace/2+0.5}},
|
|
{{center.x+blast.r/2-extraSpace/2-0.5, center.y-(blast.r-extraSpace)*0.86603-0.5}, {center.x+(blast.r-extraSpace)*0.86603+0.5, center.y-blast.r/2+extraSpace/2+0.5}}
|
|
}
|
|
|
|
if(blast.r<=500 or not blast.doItts) then
|
|
area = regions[blast.itt]
|
|
else
|
|
-- otherwise compute the regions for large area blast-waves
|
|
local reg = blast.itt % (regNum/4)
|
|
local currentQuadrant = (math.floor(blast.itt/(regNum/4)))%4
|
|
local angleUnit = 2*3.14159/regNum
|
|
local angleRelative = math.min(angleUnit*(reg+1), angleUnit*(regNum/4-reg-1))
|
|
local angleStart = angleUnit*((regNum/4)*currentQuadrant+reg)
|
|
local overstep = math.sqrt( (blast.r*math.sin(angleRelative))^2+2*blast.r*blast.speed+blast.speed*blast.speed)-blast.r*math.sin(angleRelative)+2;
|
|
|
|
|
|
|
|
if(currentQuadrant==0) then
|
|
if(reg<regNum/8) then
|
|
area = {{center.x + (blast.r-blast.speed)*math.cos(angleStart+angleUnit), center.y + (blast.r)*math.sin(angleStart)},
|
|
{center.x + (blast.r)*math.cos(angleStart), center.y + (blast.r-blast.speed)*math.sin(angleStart+angleUnit)+overstep}}
|
|
elseif(reg==regNum/8) then
|
|
area = {{center.x + (blast.r-blast.speed)*math.cos(angleStart+angleUnit), center.y + (blast.r-blast.speed)*math.sin(angleStart)},
|
|
{center.x + (blast.r-blast.speed)*math.cos(angleStart), center.y + (blast.r-blast.speed)*math.sin(angleStart+angleUnit)}}
|
|
else
|
|
area = {{center.x + (blast.r)*math.cos(angleStart+angleUnit), center.y + (blast.r-blast.speed)*math.sin(angleStart)},
|
|
{center.x + (blast.r-blast.speed)*math.cos(angleStart)+overstep, center.y + (blast.r)*math.sin(angleStart+angleUnit)}}
|
|
end
|
|
elseif(currentQuadrant==1) then
|
|
if(reg<regNum/8) then
|
|
area = {{center.x + (blast.r-blast.speed)*math.cos(angleStart+angleUnit)-overstep, center.y + (blast.r-blast.speed)*math.sin(angleStart+angleUnit)},
|
|
{center.x + (blast.r)*math.cos(angleStart), center.y + (blast.r)*math.sin(angleStart)}}
|
|
elseif(reg==regNum/8) then
|
|
area = {{center.x + (blast.r-blast.speed)*math.cos(angleStart+angleUnit), center.y + (blast.r-blast.speed)*math.sin(angleStart+angleUnit)},
|
|
{center.x + (blast.r-blast.speed)*math.cos(angleStart), center.y + (blast.r-blast.speed)*math.sin(angleStart)}}
|
|
else
|
|
area = {{center.x + (blast.r)*math.cos(angleStart+angleUnit), center.y + (blast.r)*math.sin(angleStart+angleUnit)},
|
|
{center.x + (blast.r-blast.speed)*math.cos(angleStart), center.y + (blast.r-blast.speed)*math.sin(angleStart)+overstep}}
|
|
end
|
|
elseif(currentQuadrant==2) then
|
|
if(reg<regNum/8) then
|
|
area = {{center.x + (blast.r)*math.cos(angleStart), center.y + (blast.r-blast.speed)*math.sin(angleStart+angleUnit)-overstep},
|
|
{center.x + (blast.r-blast.speed)*math.cos(angleStart+angleUnit), center.y + (blast.r)*math.sin(angleStart)}}
|
|
elseif(reg==regNum/8) then
|
|
area = {{center.x + (blast.r-blast.speed)*math.cos(angleStart), center.y + (blast.r-blast.speed)*math.sin(angleStart+angleUnit)},
|
|
{center.x + (blast.r-blast.speed)*math.cos(angleStart+angleUnit), center.y + (blast.r-blast.speed)*math.sin(angleStart)}}
|
|
else
|
|
area = {{center.x + (blast.r-blast.speed)*math.cos(angleStart)-overstep, center.y + (blast.r)*math.sin(angleStart+angleUnit)},
|
|
{center.x + (blast.r)*math.cos(angleStart+angleUnit), center.y + (blast.r-blast.speed)*math.sin(angleStart)}}
|
|
end
|
|
else
|
|
if(reg<regNum/8) then
|
|
area = {{center.x + (blast.r)*math.cos(angleStart), center.y + (blast.r)*math.sin(angleStart)},
|
|
{center.x + (blast.r-blast.speed)*math.cos(angleStart+angleUnit)+overstep, center.y + (blast.r-blast.speed)*math.sin(angleStart+angleUnit)}}
|
|
elseif(reg==regNum/8) then
|
|
area = {{center.x + (blast.r-blast.speed)*math.cos(angleStart), center.y + (blast.r-blast.speed)*math.sin(angleStart)},
|
|
{center.x + (blast.r-blast.speed)*math.cos(angleStart+angleUnit), center.y + (blast.r-blast.speed)*math.sin(angleStart+angleUnit)}}
|
|
else
|
|
area = {{center.x + (blast.r-blast.speed)*math.cos(angleStart), center.y + (blast.r-blast.speed)*math.sin(angleStart)-overstep},
|
|
{center.x + (blast.r)*math.cos(angleStart+angleUnit), center.y + (blast.r)*math.sin(angleStart+angleUnit)}}
|
|
end
|
|
end
|
|
end
|
|
|
|
local entities = surface.find_entities(area)
|
|
eHits = eHits + #entities
|
|
--game.players[1].print(math.floor(blast.itt/6) .. " {" .. area[1][1] .. ", " .. area[1][2] .. "}, {" .. area[2][1] .. ", " .. area[2][2] .. "}")
|
|
local blastInnerSq = (blast.r - blast.speed)*(blast.r - blast.speed)
|
|
local blastSq = blast.r*blast.r
|
|
local cx = center.x
|
|
local cy = center.y
|
|
for _,entity in pairs(entities) do
|
|
if (entity.valid and entity.position) then
|
|
local ePos = entity.position
|
|
local xdif = ePos.x-cx
|
|
local ydif = ePos.y-cy
|
|
local distSq = xdif*xdif + ydif*ydif
|
|
if(distSq <= blastSq and distSq>blastInnerSq and entity.valid and entity.prototype.max_health ~= 0
|
|
and ePos.x>=area[1][1] and ePos.x<area[2][1] and ePos.y>=area[1][2] and ePos.y<area[2][2]) then
|
|
|
|
damage_entity(surface, distSq, ePos, blast.pow, blast.fire, blast.damage_init, blast.blast_min_damage, entity, blast.force, blast.cause, corpseMap)
|
|
if blast.fire then
|
|
fire_damage_entity(surface, entity, blast.force, blast.cause, true)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- For thermobarics, start all the required fires
|
|
if(blast.fire) then
|
|
local area = regions[blast.itt]
|
|
local tiles = surface.find_tiles_filtered{area=area}
|
|
for _,tile in pairs(tiles) do
|
|
local xdif = tile.position.x-center.x
|
|
local ydif = tile.position.y-center.y
|
|
local distSq = xdif*xdif + ydif*ydif
|
|
if(distSq > (blast.r - blast.speed)*(blast.r - blast.speed) and distSq <= blast.r*blast.r) then
|
|
if (blast.r <= blast.fire_rad) then
|
|
local chance = math.random(0, blast.fire_rad)
|
|
if(chance*chance>distSq) then
|
|
surface.create_entity{name="fire-flame",position=tile.position}
|
|
else
|
|
surface.create_entity{name="thermobaric-wave-fire",position=tile.position}
|
|
end
|
|
else
|
|
local chanceWave = math.random(blast.fire_rad, blast.max)
|
|
if(chanceWave*chanceWave>distSq) then
|
|
surface.create_entity{name="thermobaric-wave-fire",position=tile.position}
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
local hasEnded = false
|
|
-- We want to do more regions this frame if the ones we have covered contain very few entities (such as if they are unloaded)
|
|
if(blast.itt == regNum and blast.ittframe>=8) then
|
|
blast.r = blast.r + blast.speed
|
|
blast.itt = 1
|
|
blast.ittframe = 1
|
|
elseif blast.itt ~= regNum then
|
|
blast.itt = blast.itt+1
|
|
if((not blast.doItts) or eHits<4000) then
|
|
hasEnded = move_blast(i, blast,eHits, corpseMap)
|
|
end
|
|
end
|
|
if(blast.r>blast.max and not hasEnded) then
|
|
global.blastWaves[i] = nil
|
|
return true
|
|
end
|
|
return hasEnded
|
|
end
|
|
|
|
|
|
|
|
local function chunk_loaded(chunkLoaderStruct, surface_index, originPos, chunkPosAndArea, x, y, killPlanes, blastSq, force, cause, corpseMap)
|
|
local fireballSq = chunkLoaderStruct.fireball_r*chunkLoaderStruct.fireball_r;
|
|
local cx = originPos.x
|
|
local cy = originPos.y
|
|
local init_blast = chunkLoaderStruct.init_blast
|
|
local blast_min_damage = chunkLoaderStruct.blast_min_damage
|
|
for _,entity in pairs(game.surfaces[surface_index].find_entities(chunkPosAndArea.area)) do
|
|
if (entity.valid and entity.position) then
|
|
local ePos = entity.position
|
|
local xdif = ePos.x-cx
|
|
local ydif = ePos.y-cy
|
|
local distSq = xdif*xdif + ydif*ydif
|
|
if(distSq <= blastSq and entity.prototype.max_health ~= 0
|
|
and ePos.x>=x and ePos.x<x+32 and ePos.y>=y and ePos.y<y+32 and (killPlanes or entity.type ~= "car")) then
|
|
damage_entity(game.surfaces[surface_index], distSq, ePos, fireballSq, false, init_blast, blast_min_damage, entity, force, cause, corpseMap)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
return {
|
|
move_blast = move_blast,
|
|
chunk_loaded = chunk_loaded
|
|
}
|
|
|
|
|