File size: 5,597 Bytes
b6a38d7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
MapVar("g_Fire", {})
MapVar("g_DistToFire", {})

local burn_active_terrain = "Dry_BurntGround_01"
local burn_inactive_terrain = "Dry_BurntGround_02"
local burn_terrain_radius = 3 * guim / 2
local toggledEnumFlags = const.efVisible + const.efWalkable + const.efCollision + const.efApplyToGrids

const.VoxelFireMaxDist = 5*guim
const.VoxelFireRange = MulDivRound(const.SlabSizeX, 200, 100)
const.BurnDamageMin = 1
const.BurnDamageMax = 5
local VoxelFireRange = const.VoxelFireRange
local VoxelFireMaxDist = const.VoxelFireMaxDist


function IsBurnActiveCollection(col)
	return not not string.match(string.lower(col.Name), "col_burn_active")
end

function IsBurnInactiveCollection(col)
	return not not string.match(string.lower(col.Name), "col_burn_inactive")
end

function UpdateMapBurningState(burning)
	NetUpdateHash("UpdateMapBurningState")
	local active_func = burning and CObject.SetEnumFlags or CObject.ClearEnumFlags
	local inactive_func = burning and CObject.ClearEnumFlags or CObject.SetEnumFlags
	local terrain_old = Presets.TerrainObj.Default[burning and burn_inactive_terrain or burn_active_terrain].idx
	local terrain_new = Presets.TerrainObj.Default[burning and burn_active_terrain or burn_inactive_terrain].idx

	g_Fire = {}
	g_DistToFire = {}
	local queue = {}
	for _, col in pairs(Collections) do
		if IsBurnActiveCollection(col) then
			MapForEach("map", "collection", col.Index, true, function(obj)
				active_func(obj, toggledEnumFlags)
				if IsKindOf(obj, "ParSystem") and string.match(obj:GetParticlesName(), "Fire") then
					local pos = obj:GetPos()
					local vx, vy, vz = WorldToVoxel(pos)
					local packed_pos = point_pack(vx, vy, vz)
					g_Fire[packed_pos] = GameState.Burning or nil
					g_DistToFire[packed_pos] = GameState.Burning and 0 or nil
					queue[#queue+1] = packed_pos
					terrain.ReplaceTypeCircle(pos, burn_terrain_radius, terrain_old, terrain_new)
				end
			end)
		elseif IsBurnInactiveCollection(col) then
			MapForEach("map", "collection", col.Index, true, function(obj)
				inactive_func(obj, toggledEnumFlags)
			end)
		end
	end
	
	for _, area in ipairs(g_FireAreas) do
		for _, pos in ipairs(area.fire_positions) do
			local vx, vy, vz = WorldToVoxel(pos)
			local packed_pos = point_pack(vx, vy, vz)
			g_Fire[packed_pos] = true
			g_DistToFire[packed_pos] = 0
			queue[#queue+1] = packed_pos
		end
	end
	-- precalc fire adjacency up to VoxelFireMaxDist
	local qi = 1
	while qi < #queue do
		local ppos = queue[qi]
		local dist = g_DistToFire[ppos]
		local x, y, z = point_unpack(ppos)
		local adj, adj_dist
		if dist then
			adj_dist = dist + const.SlabSizeX
			if adj_dist < VoxelFireMaxDist then
				adj = point_pack(x + 1, y, z)			
				if not g_DistToFire[adj] or g_DistToFire[adj] > adj_dist then
					g_DistToFire[adj] = adj_dist
					queue[#queue + 1] = adj
				end
				adj = point_pack(x - 1, y, z)
				if not g_DistToFire[adj] or g_DistToFire[adj] > adj_dist then
					g_DistToFire[adj] = adj_dist
					queue[#queue + 1] = adj
				end
			end
			adj_dist = dist + const.SlabSizeY
			if adj_dist < VoxelFireMaxDist then
				adj = point_pack(x, y + 1, z)
				if not g_DistToFire[adj] or g_DistToFire[adj] > adj_dist then
					g_DistToFire[adj] = adj_dist
					queue[#queue + 1] = adj
				end
				adj = point_pack(x, y - 1, z)
				if not g_DistToFire[adj] or g_DistToFire[adj] > adj_dist then
					g_DistToFire[adj] = adj_dist
					queue[#queue + 1] = adj
				end
			end
			adj_dist = dist + const.SlabSizeZ
			if adj_dist < VoxelFireMaxDist then
				adj = point_pack(x, y, z + 1)
				if not g_DistToFire[adj] or g_DistToFire[adj] > adj_dist then
					g_DistToFire[adj] = adj_dist
					queue[#queue + 1] = adj
				end
				adj = point_pack(x, y, z - 1)
				if not g_DistToFire[adj] or g_DistToFire[adj] > adj_dist then
					g_DistToFire[adj] = adj_dist
					queue[#queue + 1] = adj
				end
			end
		end
		qi = qi + 1
	end
	UpdatePassType()
end

function OnMsg.GameStateChanged(changed)
	if changed.FireStorm ~= nil then 
		UpdateMapBurningState(GameState.FireStorm)
	end
end

function OnMsg.EnterSector()
	UpdateMapBurningState(GameState.FireStorm)
end

function AreVoxelsInFireRange(voxels, range)
	range = range or VoxelFireRange
	-- check for fires in voxels around the unit, considering all voxels occupied by that unit
	for _, voxel in ipairs(voxels) do
		local dist = g_DistToFire[voxel]
		if dist and dist < range then
			return true, dist
		end
	end
end

MapVar("g_dbgFireVisuals", false)

function ToggleFiresDebug()
	if g_dbgFireVisuals then
		for _, obj in ipairs(g_dbgFireVisuals) do
			DoneObject(obj)
		end
		g_dbgFireVisuals = false
		return
	end
	
	g_dbgFireVisuals = {}
	local voxel_box = box(point(-const.SlabSizeX/2, -const.SlabSizeY/2, 0), point(const.SlabSizeX/2, const.SlabSizeY/2, const.SlabSizeZ))
	for ppos, _ in pairs(g_Fire) do
		local pt = point(VoxelToWorld(point_unpack(ppos)))		
		if not pt:IsValidZ() then
			pt = pt:SetTerrainZ()
		end
		
		local fire_box = voxel_box + pt
		local mesh = PlaceBox(fire_box, const.clrOrange, nil, false)
		table.insert(g_dbgFireVisuals, mesh)
	end
	
	for _, unit in ipairs(g_Units) do
		local voxels = unit:GetVisualVoxels()
		local adjacent, dist = AreVoxelsInFireRange(voxels)
		for _, voxel in ipairs(voxels) do
			local pt = point(VoxelToWorld(point_unpack(voxel)))
			local unit_box = voxel_box + pt
			local color = const.clrWhite
			if adjacent and dist < const.SlabSizeX then
				color = const.clrRed
			elseif adjacent then
				color = const.clrYellow
			end
			local mesh = PlaceBox(unit_box, color, nil, false)
			table.insert(g_dbgFireVisuals, mesh)
		end
	end
end