Comparing sensitive data, confidential files or internal emails?

Most legal and privacy policies prohibit uploading sensitive data online. Diffchecker Desktop ensures your confidential information never leaves your computer. Work offline and compare documents securely.

Untitled diff

Created Diff never expires
11 removals
293 lines
19 additions
300 lines


require "config"
require "config"


local freq = 16
local freq = 16
local freq2 = freq ^ 2
local freq2 = freq ^ 2
local totalgen = 0
local totalgen = 0
local chunksize = 32
local chunksize = 32
local original_tree_count = 0
local original_tree_count = 0
local last_status="-"
local last_status="-"
local debug_text1="-"
local debug_text1="-"
local debug_text2="-"
local debug_text2="-"
local tile_whitelist= {"grass","dirt","landfill"} -- this should find any tile that has grass or dirt in the name, so "grass", "grass-dry" and "dirt-dark" etc.
local tile_whitelist= {"grass","dirt","landfill"} -- this should find any tile that has grass or dirt in the name, so "grass", "grass-dry" and "dirt-dark" etc.


local tree_names = {
local tree_names = {
"tree-01",
"tree-01",
"tree-02",
"tree-02",
"tree-02-red",
"tree-02-red",
"tree-03",
"tree-03",
"tree-04",
"tree-04",
"tree-05",
"tree-05",
"tree-06",
"tree-06",
"tree-06-brown",
"tree-06-brown",
"tree-07",
"tree-07",
"tree-08",
"tree-08",
"tree-08-brown",
"tree-08-brown",
"tree-08-red",
"tree-08-red",
"tree-09",
"tree-09",
"tree-09-brown",
"tree-09-brown",
"tree-09-red"
"tree-09-red"
}
}


local function fmod(a,m)
local function fmod(a,m)
return a - math.floor(a / m) * m
return a - math.floor(a / m) * m
end
end


local function log(msg)
local function log(msg)
if enable_debug_window then
if enable_debug_window then
print("NaturalTree mod:" .. msg)
print("NaturalTree mod:" .. msg)
end
end
end
end


-- Pollution was too high to spawn a tree at thepos, so kill up to 3 surrounding trees
-- Pollution was too high to spawn a tree at thepos, so kill up to 3 surrounding trees
local function kill_surrounding_trees(thepos)
local function kill_surrounding_trees(thepos)
bounds={{thepos[1]-5, thepos[2]-5}, {thepos[1]+5, thepos[2]+5}}
bounds={{thepos[1]-5, thepos[2]-5}, {thepos[1]+5, thepos[2]+5}}
strees=game.surfaces[1].find_entities_filtered{area = bounds, type="tree", limit= 3}
strees=game.surfaces[1].find_entities_filtered{area = bounds, type="tree", limit= 3}
for i=1,#strees do
for i=1,#strees do
log("Killed tree due to pollution at "..strees[i].position.x..","..strees[i].position.y)
log("Killed tree due to pollution at "..strees[i].position.x..","..strees[i].position.y)
strees[i].destroy()
strees[i].destroy()
end
end
end
end


-- search area for specified entity names, return true if there are any.
-- search area for specified entity names, return true if there are any.
local function test_entity(surface,area,names)
local function test_entity(surface,area,names)
for i = 1,#names do
for i = 1,#names do
if 0 ~= surface.count_entities_filtered{area = area, type = names[i]} then
if 0 ~= surface.count_entities_filtered{area = area, type = names[i]} then
return false
return false
end
end
end
end
return true
return true
end
end


-- is the tile at newpos valid and a white-listed tile and pollution low enough (true)
-- is the tile at newpos valid and a white-listed tile and pollution low enough (true)
local function test_tile(surface,newpos)
local function test_tile(surface,newpos)
tile = surface.get_tile(newpos[1],newpos[2])
tile = surface.get_tile(newpos[1],newpos[2])
if not tile.valid then
if not tile.valid then
return false
return false
end
end
for i = 1,#tile_whitelist do
for i = 1,#tile_whitelist do
if string.find(tile.name, tile_whitelist[i]) then
if string.find(tile.name, tile_whitelist[i]) then
if surface.get_pollution({newpos[1],newpos[2]}) < pollution_threshold then
if surface.get_pollution({newpos[1],newpos[2]}) < pollution_threshold then
return true
return true
else
else
kill_surrounding_trees(newpos)
kill_surrounding_trees(newpos)
end
end
end
end
end
end
return false
return false
end
end


-- is parameter a equal to any of the b's
-- is parameter a equal to any of the b's
local function eqany(a,b)
local function eqany(a,b)
for i = 1,#b do
for i = 1,#b do
if a == b[i] then
if a == b[i] then
return true
return true
end
end
end
end
return false
return false
end
end


-----------------------------------------
-----------------------------------------
local shuffle_src = {}
local shuffle_src = {}
local shuffle = {}
local shuffled = false


for i = 1,freq do
local function shuffle_it()
for j = 1,freq do
for i = 1,freq do
shuffle_src[i * freq + j] = i * freq + j
for j = 1,freq do
shuffle_src[i * freq + j] = i * freq + j
end
end

while 0 < #shuffle_src do
local p = math.random(1,#shuffle_src)
table.insert(shuffle, shuffle_src[p])
table.remove(shuffle_src, p)
end
end
end
end


local shuffle = {}

while 0 < #shuffle_src do
local p = math.random(1,#shuffle_src)
table.insert(shuffle, shuffle_src[p])
table.remove(shuffle_src, p)
end

-- Playermap is a 2-D map that indicates approximate location of player owned
-- Playermap is a 2-D map that indicates approximate location of player owned
-- entities. It is used for optimizing the algorithm to quickly determine proximity
-- entities. It is used for optimizing the algorithm to quickly determine proximity
-- of the player's properties which would be of player's interest.
-- of the player's properties which would be of player's interest.
-- Because LuaSurface.count_entities_filtered() is slow for large area, we want
-- Because LuaSurface.count_entities_filtered() is slow for large area, we want
-- to call it as few times as possible.
-- to call it as few times as possible.
-- This is similar in function as chunks, but playermap element is greater than
-- This is similar in function as chunks, but playermap element is greater than
-- chunks, because it's not good idea to make scripting languages like Lua
-- chunks, because it's not good idea to make scripting languages like Lua
-- calculating large set of data. Also we only need very rough estimation, so
-- calculating large set of data. Also we only need very rough estimation, so
-- chunk granularity is too fine for us.
-- chunk granularity is too fine for us.
local playermap_freq = 4
local playermap_freq = 4
local playermap = {}
local playermap = {}


local function update_player_map(m, surface)
local function update_player_map(m, surface)
local mm = m % #shuffle + 1
local mm = m % #shuffle + 1
local mx = shuffle[mm] % freq
local mx = shuffle[mm] % freq
local my = math.floor(shuffle[mm] / freq)
local my = math.floor(shuffle[mm] / freq)
for chunk in surface.get_chunks() do
for chunk in surface.get_chunks() do
if fmod(chunk.x + mx, freq) == 0 and fmod(chunk.y + my, freq) == 0 and
if fmod(chunk.x + mx, freq) == 0 and fmod(chunk.y + my, freq) == 0 and
0 < surface.count_entities_filtered{area = {{chunk.x * chunksize, chunk.y * chunksize}, {(chunk.x + 1) * chunksize, (chunk.y + 1) * chunksize}}, force = "player"} then
0 < surface.count_entities_filtered{area = {{chunk.x * chunksize, chunk.y * chunksize}, {(chunk.x + 1) * chunksize, (chunk.y + 1) * chunksize}}, force = "player"} then
local px = math.floor(chunk.x / 4)
local px = math.floor(chunk.x / 4)
local py = math.floor(chunk.y / 4)
local py = math.floor(chunk.y / 4)
if playermap[py] == nil then
if playermap[py] == nil then
playermap[py] = {}
playermap[py] = {}
end
end
playermap[py][px] = m
playermap[py][px] = m
end
end
end
end
if enable_debug_window then
if enable_debug_window then
local pp=game.players[1].position
local pp=game.players[1].position
local ptile = surface.get_tile(pp.x, pp.y)
local ptile = surface.get_tile(pp.x, pp.y)
local ppol=surface.get_pollution({pp.x, pp.y})
local ppol=surface.get_pollution({pp.x, pp.y})
debug_text1="Player is on " .. ptile.name
debug_text1="Player is on " .. ptile.name
debug_text2="Pollution:" .. math.floor(ppol)
debug_text2="Pollution:" .. math.floor(ppol)
end
end
end
end


function on_tick(event)
function on_tick(event)
if not shuffled then
shuffle_it()
shuffled = true
end

-- LuaSurface.count_entities_filtered() is slow, LuaForce.get_entity_count() is much faster, but
-- LuaSurface.count_entities_filtered() is slow, LuaForce.get_entity_count() is much faster, but
-- it needs entity name argument, not type. So we must repeat it for all types of trees.
-- it needs entity name argument, not type. So we must repeat it for all types of trees.
local function count_trees()
local function count_trees()
local c=0
local c=0
for i=1,#tree_names do
for i=1,#tree_names do
c = c + game.forces.neutral.get_entity_count(tree_names[i])
c = c + game.forces.neutral.get_entity_count(tree_names[i])
end
end
return c
return c
end
end


local function grow_trees(m)
local function grow_trees(m)
local num = 0
local num = 0
local allnum = 0
local allnum = 0
local str = ""
local str = ""
local mm = m % #shuffle + 1
local mm = m % #shuffle + 1
local mx = shuffle[mm] % freq
local mx = shuffle[mm] % freq
local my = math.floor(shuffle[mm] / freq)
local my = math.floor(shuffle[mm] / freq)
local surface = game.surfaces[1]
local surface = game.surfaces[1]
local totalc = 0
local totalc = 0
for chunk in surface.get_chunks() do
for chunk in surface.get_chunks() do
allnum = allnum + 1
allnum = allnum + 1


-- Check if any of player's entity is in proximity of this chunk.
-- Check if any of player's entity is in proximity of this chunk.
local function checkPlayerMap()
local function checkPlayerMap()
local px = math.floor(chunk.x / 4)
local px = math.floor(chunk.x / 4)
local py = math.floor(chunk.y / 4)
local py = math.floor(chunk.y / 4)
for y=-1,1 do
for y=-1,1 do
if playermap[py + y] then
if playermap[py + y] then
for x=-1,1 do
for x=-1,1 do
if playermap[py + y][px + x] and m < playermap[py + y][px + x] + freq2 then
if playermap[py + y][px + x] and m < playermap[py + y][px + x] + freq2 then
return true
return true
end
end
end
end
end
end
end
end
return false
return false
end
end


-- Grow trees on only the player's proximity since the player is not
-- Grow trees on only the player's proximity since the player is not
-- interested nor has means to observe deep in the unknown region.
-- interested nor has means to observe deep in the unknown region.
if fmod(chunk.x + mx, freq) == 0 and fmod(chunk.y + my, freq) == 0 and
if fmod(chunk.x + mx, freq) == 0 and fmod(chunk.y + my, freq) == 0 and
checkPlayerMap() then
checkPlayerMap() then
local area = {{chunk.x * chunksize, chunk.y * chunksize}, {(chunk.x + 1) * chunksize, (chunk.y + 1) * chunksize}}
local area = {{chunk.x * chunksize, chunk.y * chunksize}, {(chunk.x + 1) * chunksize, (chunk.y + 1) * chunksize}}
local c = surface.count_entities_filtered{area = area, type = "tree"}
local c = surface.count_entities_filtered{area = area, type = "tree"}
totalc = totalc + c
totalc = totalc + c
if 0 < c then
if 0 < c then
local trees = surface.find_entities_filtered{area = area, type = "tree"}
local trees = surface.find_entities_filtered{area = area, type = "tree"}
if 0 < #trees then
if 0 < #trees then
local nondeadtree = false
local nondeadtree = false
local tree = trees[math.random(#trees)]
local tree = trees[math.random(#trees)]
-- Draw trees until we get a non-dead tree.
-- Draw trees until we get a non-dead tree.
for try = 1,10 do
for try = 1,10 do
if not eqany(tree.name, {"dead-tree", "dry-tree", "dead-grey-trunk", "dry-hairy-tree", "dead-dry-hairy-tree"}) then
if not eqany(tree.name, {"dead-tree", "dry-tree", "dead-grey-trunk", "dry-hairy-tree", "dead-dry-hairy-tree"}) then
nondeadtree = true
nondeadtree = true
break
break
end
end
end
end
if nondeadtree then
if nondeadtree then
local newpos
local newpos
local success = false
local success = false
-- Try until randomly generated position does not block something.
-- Try until randomly generated position does not block something.
for try = 1,10 do
for try = 1,10 do
if tree.valid then
if tree.valid then
newpos = {tree.position.x + (math.random(-5,5)), tree.position.y + (math.random(-5,5))}
newpos = {tree.position.x + (math.random(-5,5)), tree.position.y + (math.random(-5,5))}
local newarea = {{newpos[1] - 1, newpos[2] - 1}, {newpos[1] + 1, newpos[2] + 1}}
local newarea = {{newpos[1] - 1, newpos[2] - 1}, {newpos[1] + 1, newpos[2] + 1}}
local newarea2 = {{newpos[1] - 2, newpos[2] - 2}, {newpos[1] + 2, newpos[2] + 2}}
local newarea2 = {{newpos[1] - 2, newpos[2] - 2}, {newpos[1] + 2, newpos[2] + 2}}
if 0 == surface.count_entities_filtered{area = newarea, type = "tree"} and
if 0 == surface.count_entities_filtered{area = newarea, type = "tree"} and
test_tile(surface, newpos) and
test_tile(surface, newpos) and
0 == surface.count_entities_filtered{area = newarea2, force = "player"} and
0 == surface.count_entities_filtered{area = newarea2, force = "player"} and
surface.can_place_entity{name = tree.name, position = newpos, force = tree.force} then
surface.can_place_entity{name = tree.name, position = newpos, force = tree.force} then
success = true
success = true
break
break
end
end
end
end
end
end
if success then
if success then
num = num + 1
num = num + 1
surface.create_entity{name = tree.name, position = newpos, force = tree.force}
surface.create_entity{name = tree.name, position = newpos, force = tree.force}
log("Created new tree '" .. tree.name .. "' at "..newpos[1]..","..newpos[2])
log("Created new tree '" .. tree.name .. "' at "..newpos[1]..","..newpos[2])
end
end
end
end
end
end
end
end
end
end
end
end
totalgen = totalgen + num
totalgen = totalgen + num
end
end


-- First, cache player map data by searching player owned entities.
-- First, cache player map data by searching player owned entities.
if game.tick % tree_expansion_frequency == 0 then
if game.tick % tree_expansion_frequency == 0 then
local m = math.floor(game.tick / tree_expansion_frequency)
local m = math.floor(game.tick / tree_expansion_frequency)
update_player_map(m, game.surfaces[1])
update_player_map(m, game.surfaces[1])


end
end


-- Delay the loop as half a phase of update_player_map to reduce
-- Delay the loop as half a phase of update_player_map to reduce
-- 'petit-freeze' duration as possible.
-- 'petit-freeze' duration as possible.
if math.floor(game.tick + tree_expansion_frequency / 2) % tree_expansion_frequency == 0 then
if math.floor(game.tick + tree_expansion_frequency / 2) % tree_expansion_frequency == 0 then
local m = math.floor(game.tick / tree_expansion_frequency)
local m = math.floor(game.tick / tree_expansion_frequency)


-- As number of trees grows, the growth rate decreases, maxes at max_trees.
-- As number of trees grows, the growth rate decreases, maxes at max_trees.
local numTrees = count_trees()
local numTrees = count_trees()
if numTrees < max_trees * tree_decrease_start or
if numTrees < max_trees * tree_decrease_start or
numTrees < max_trees * (tree_decrease_start + math.random() * (1 - tree_decrease_start)) then
numTrees < max_trees * (tree_decrease_start + math.random() * (1 - tree_decrease_start)) then
grow_trees(m)
grow_trees(m)
end
end


if enable_debug_window then
if enable_debug_window then
-- Return [rows,active,visited] playermap chunks
-- Return [rows,active,visited] playermap chunks
local function countPlayerMap()
local function countPlayerMap()
local ret = {0,0,0}
local ret = {0,0,0}
for i,v in pairs(playermap) do
for i,v in pairs(playermap) do
ret[1] = ret[1] + 1
ret[1] = ret[1] + 1
for j,w in pairs(v) do
for j,w in pairs(v) do
if m < w + freq2 then
if m < w + freq2 then
ret[2] = ret[2] + 1
ret[2] = ret[2] + 1
end
end
ret[3] = ret[3] + 1
ret[3] = ret[3] + 1
end
end
end
end
return ret
return ret
end
end


if not game.players[1].gui.left.trees then -- create GUI
if not game.players[1].gui.left.trees then -- create GUI
game.players[1].gui.left.add{type="frame", name="trees", caption="Debug Tree Info", direction="vertical"}
game.players[1].gui.left.add{type="frame", name="trees", caption="Debug Tree Info", direction="vertical"}
game.players[1].gui.left.trees.add{type="label",name="m",caption="Cycle: " .. m % #shuffle .. "/" .. #shuffle}
game.players[1].gui.left.trees.add{type="label",name="m",caption="Cycle: " .. m % #shuffle .. "/" .. #shuffle}
game.players[1].gui.left.trees.add{type="label",name="total",caption="Total trees: " .. count_trees()}
game.players[1].gui.left.trees.add{type="label",name="total",caption="Total trees: " .. count_trees()}
game.players[1].gui.left.trees.add{type="label",name="count",caption="Added trees: " .. totalgen}
game.players[1].gui.left.trees.add{type="label",name="count",caption="Added trees: " .. totalgen}
game.players[1].gui.left.trees.add{type="label",name="playermap",caption="info goes here"}
game.players[1].gui.left.trees.add{type="label",name="playermap",caption="info goes here"}
game.players[1].gui.left.trees.add{type="label",name="status1",caption="debug_text1 goes here"}
game.players[1].gui.left.trees.add{type="label",name="status1",caption="debug_text1 goes here"}
game.players[1].gui.left.trees.add{type="label",name="status2",caption="debug_text2 goes here"}
game.players[1].gui.left.trees.add{type="label",name="status2",caption="debug_text2 goes here"}
else -- Update GUI
else -- Update GUI
game.players[1].gui.left.trees.m.caption = "Cycle: " .. m % #shuffle .. "/" .. #shuffle
game.players[1].gui.left.trees.m.caption = "Cycle: " .. m % #shuffle .. "/" .. #shuffle
game.players[1].gui.left.trees.total.caption = "Total trees: " .. count_trees()
game.players[1].gui.left.trees.total.caption = "Total trees: " .. count_trees()
game.players[1].gui.left.trees.count.caption = "Added trees: " .. totalgen
game.players[1].gui.left.trees.count.caption = "Added trees: " .. totalgen
game.players[1].gui.left.trees.status1.caption = "status1=" .. debug_text1
game.players[1].gui.left.trees.status1.caption = "status1=" .. debug_text1
game.players[1].gui.left.trees.status2.caption = "status2=" .. debug_text2
game.players[1].gui.left.trees.status2.caption = "status2=" .. debug_text2
local cc = countPlayerMap()
local cc = countPlayerMap()
game.players[1].gui.left.trees.playermap.caption = "Playermap: (rows/active/visited) " .. cc[1] .. "/" .. cc[2] .. "/" .. cc[3]
game.players[1].gui.left.trees.playermap.caption = "Playermap: (rows/active/visited) " .. cc[1] .. "/" .. cc[2] .. "/" .. cc[3]
end
end
end
end
end
end
end
end


-- Register event handlers
-- Register event handlers
script.on_event(defines.events.on_tick, function(event) on_tick(event) end)
script.on_event(defines.events.on_tick, function(event) on_tick(event) end)