--factorio cave miner - mewmew made this - it is free to use, modify and enjoy
--do /market to spawn the market at map start
--you can use /c map_pregen() to generate the world before playing to reduce any map rendering lags and use /c map_pregen_end() after it is done

--[[ 
To do(maybe):
player list currency issues
biter pets
biters sometimes go for lights / poles  --- maybe random game events in general,  earthquake event
you got bitten by an angry CAVE piranha
]]--


local simplex_noise = require 'map_gen.shared.simplex_noise'
local Event = require 'utils.event'
local market_items = require "cave_miner_market_items"

local function treasure_chest(position)
	local p = game.surfaces[1].find_non_colliding_position("wooden-chest",position, 2,0.5)
	if not p then return end
	local e = game.surfaces[1].create_entity {name="wooden-chest",position=p, force="player"}
	e.minable = false
	local i = e.get_inventory(defines.inventory.chest)
	for x = 1, math.random(1,6), 1 do
		local loot = global.treasure_chest_raffle_table[math.random(1,#global.treasure_chest_raffle_table)]
		i.insert(loot)
	end		
end

local function secret_shop(pos)
	local secret_market_items = {		
    {price = {{"raw-fish", math.random(250,450)}}, offer = {type = 'give-item', item = 'combat-shotgun'}},
    {price = {{"raw-fish", math.random(250,450)}}, offer = {type = 'give-item', item = 'flamethrower'}},
    {price = {{"raw-fish", math.random(75,125)}}, offer = {type = 'give-item', item = 'rocket-launcher'}},
    {price = {{"raw-fish", math.random(4,6)}}, offer = {type = 'give-item', item = 'piercing-rounds-magazine'}},
    {price = {{"raw-fish", math.random(15,30)}}, offer = {type = 'give-item', item = 'uranium-rounds-magazine'}},  
    {price = {{"raw-fish", math.random(10,20)}}, offer = {type = 'give-item', item = 'piercing-shotgun-shell'}},
    {price = {{"raw-fish", math.random(6,12)}}, offer = {type = 'give-item', item = 'flamethrower-ammo'}},
    {price = {{"raw-fish", math.random(10,20)}}, offer = {type = 'give-item', item = 'rocket'}},
    {price = {{"raw-fish", math.random(15,25)}}, offer = {type = 'give-item', item = 'explosive-rocket'}},        
    {price = {{"raw-fish", math.random(15,30)}}, offer = {type = 'give-item', item = 'explosive-cannon-shell'}},
    {price = {{"raw-fish", math.random(25,40)}}, offer = {type = 'give-item', item = 'explosive-uranium-cannon-shell'}},   
    {price = {{"raw-fish", math.random(25,35)}}, offer = {type = 'give-item', item = 'cluster-grenade'}},    
    {price = {{"raw-fish", math.random(300,400)}}, offer = {type = 'give-item', item = 'modular-armor'}},
    {price = {{"raw-fish", math.random(1500,2500)}}, offer = {type = 'give-item', item = 'power-armor'}},
    {price = {{"raw-fish", math.random(4000,8000)}}, offer = {type = 'give-item', item = 'fusion-reactor-equipment'}},
    {price = {{"raw-fish", math.random(60,100)}}, offer = {type = 'give-item', item = 'battery-equipment'}},
    {price = {{"raw-fish", math.random(700,900)}}, offer = {type = 'give-item', item = 'battery-mk2-equipment'}},
    {price = {{"raw-fish", math.random(400,600)}}, offer = {type = 'give-item', item = 'belt-immunity-equipment'}},
    {price = {{"raw-fish", math.random(8001,9000)}}, offer = {type = 'give-item', item = 'night-vision-equipment'}},
    {price = {{"raw-fish", math.random(300,500)}}, offer = {type = 'give-item', item = 'exoskeleton-equipment'}},
    {price = {{"raw-fish", math.random(350,500)}}, offer = {type = 'give-item', item = 'personal-roboport-equipment'}},
    {price = {{"raw-fish", math.random(30,50)}}, offer = {type = 'give-item', item = 'construction-robot'}},
    {price = {{"raw-fish", math.random(250,350)}}, offer = {type = 'give-item', item = 'energy-shield-equipment'}},
    {price = {{"raw-fish", math.random(300,400)}}, offer = {type = 'give-item', item = 'personal-laser-defense-equipment'}},    
    {price = {{"raw-fish", math.random(100,200)}}, offer = {type = 'give-item', item = 'railgun'}},
    {price = {{"raw-fish", math.random(2,4)}}, offer = {type = 'give-item', item = 'railgun-dart'}},
	{price = {{"raw-fish", math.random(125,175)}}, offer = {type = 'give-item', item = 'loader'}},
	{price = {{"raw-fish", math.random(250,350)}}, offer = {type = 'give-item', item = 'fast-loader'}},
	{price = {{"raw-fish", math.random(450,550)}}, offer = {type = 'give-item', item = 'express-loader'}}
	}
	local surface = game.surfaces[1]										
	local market = surface.create_entity {name = "market", position = pos}
	market.destructible = false	
	local market_items_to_add = math.random(8,12)
	while market_items_to_add >= 0 do		
		local i = math.random(1,#secret_market_items)
		if secret_market_items[i] then
			market.add_market_item(secret_market_items[i])
			market_items_to_add = market_items_to_add - 1
			secret_market_items[i] = nil
		end
	end
end

local function on_chunk_generated(event)
	if not global.noise_seed then global.noise_seed = math.random(1,5000000) end	
	local surface = game.surfaces[1]	
	local noise = {}
	local tiles = {}	
	local enemy_building_positions = {}
	local enemy_worm_positions = {}
	local rock_positions = {}
	local fish_positions = {}
	local treasure_chest_positions = {}
	local secret_shop_locations = {}
	local extra_tree_positions = {}
	local tile_to_insert = false
	local entity_has_been_placed = false
	local pos_x = 0
	local pos_y = 0
	local tile_distance_to_center = 0			
	local entities = surface.find_entities(event.area)
	for _, e in pairs(entities) do
		if e.type == "resource" or e.type == "tree" or e.force.name == "enemy" then
			e.destroy()				
		end
	end	
	local noise_seed_add = 25000
	local current_noise_seed_add = noise_seed_add	
	local m1 = 0.12
	local m2 = 0.10
	local m3 = 0.07
	
	for x = 0, 31, 1 do
		for y = 0, 31, 1 do			
			pos_x = event.area.left_top.x + x
			pos_y = event.area.left_top.y + y
			tile_distance_to_center = pos_x^2 + pos_y^2
									
			noise[1] = simplex_noise.d2(pos_x/350, pos_y/350,global.noise_seed+current_noise_seed_add)
			current_noise_seed_add = current_noise_seed_add + noise_seed_add
			noise[2] = simplex_noise.d2(pos_x/200, pos_y/200,global.noise_seed+current_noise_seed_add)
			current_noise_seed_add = current_noise_seed_add + noise_seed_add
			noise[3] = simplex_noise.d2(pos_x/50, pos_y/50,global.noise_seed+current_noise_seed_add)
			current_noise_seed_add = current_noise_seed_add + noise_seed_add
			noise[4] = simplex_noise.d2(pos_x/20, pos_y/20,global.noise_seed+current_noise_seed_add)
			current_noise_seed_add = current_noise_seed_add + noise_seed_add			
			local cave_noise = noise[1] + noise[2]*0.3 + noise[3]*0.1 + noise[4]*0.02
			
			noise[1] = simplex_noise.d2(pos_x/120, pos_y/120,global.noise_seed+current_noise_seed_add)
			current_noise_seed_add = current_noise_seed_add + noise_seed_add
			noise[2] = simplex_noise.d2(pos_x/60, pos_y/60,global.noise_seed+current_noise_seed_add)
			current_noise_seed_add = current_noise_seed_add + noise_seed_add
			noise[3] = simplex_noise.d2(pos_x/40, pos_y/40,global.noise_seed+current_noise_seed_add)
			current_noise_seed_add = current_noise_seed_add + noise_seed_add
			noise[4] = simplex_noise.d2(pos_x/20, pos_y/20,global.noise_seed+current_noise_seed_add)
			current_noise_seed_add = current_noise_seed_add + noise_seed_add			
			local cave_noise_2 = noise[1] + noise[2]*0.4 + noise[3]*0.25 + noise[4]*0.1
			
			noise[1] = simplex_noise.d2(pos_x/50, pos_y/50,global.noise_seed+current_noise_seed_add)
			current_noise_seed_add = current_noise_seed_add + noise_seed_add
			noise[2] = simplex_noise.d2(pos_x/30, pos_y/30,global.noise_seed+current_noise_seed_add)
			current_noise_seed_add = current_noise_seed_add + noise_seed_add
			noise[3] = simplex_noise.d2(pos_x/20, pos_y/20,global.noise_seed+current_noise_seed_add)
			current_noise_seed_add = current_noise_seed_add + noise_seed_add
			noise[4] = simplex_noise.d2(pos_x/10, pos_y/10,global.noise_seed+current_noise_seed_add)
			current_noise_seed_add = current_noise_seed_add + noise_seed_add			
			local cave_noise_3 = noise[1] + noise[2]*0.5 + noise[3]*0.25 + noise[4]*0.1
			
			current_noise_seed_add = noise_seed_add	
			
			tile_to_insert = false	
			if tile_distance_to_center > global.spawn_dome_size then												
			
				if tile_distance_to_center > (global.spawn_dome_size + 5000) * (cave_noise_3 * 0.05 + 1.1) then
					if cave_noise > 1 then
						tile_to_insert = "deepwater"
						table.insert(fish_positions, {pos_x,pos_y})
					else
						if cave_noise > 0.98 then 
							tile_to_insert = "water"
						else
							if cave_noise > 0.82 then
								tile_to_insert = "grass-1"
								table.insert(enemy_building_positions, {pos_x,pos_y})
								--tile_to_insert = "grass-4"
								--if cave_noise > 0.88 then tile_to_insert = "grass-2" end
								if cave_noise > 0.94 then
									table.insert(extra_tree_positions, {pos_x,pos_y})
									table.insert(secret_shop_locations, {pos_x,pos_y})									
								end								
							else
								if cave_noise > 0.72 then
									tile_to_insert = "dirt-6"
									if cave_noise < 0.79 then table.insert(rock_positions, {pos_x,pos_y}) end
								end
							end
						end
					end
				end
				
				if tile_to_insert == false then
					if cave_noise < m1 and cave_noise > m1*-1 then
						tile_to_insert = "dirt-7"
						table.insert(enemy_worm_positions, {pos_x,pos_y})						
						if math.random(1,1000) == 1 then
							local p = surface.find_non_colliding_position("crude-oil",{pos_x,pos_y}, 5,1)
							local e = surface.create_entity {name="crude-oil", position={pos_x,pos_y}, amount=math.random(50000,2000000)}
						end
					else
						if cave_noise_2 < m2 and cave_noise_2 > m2*-1 and cave_noise > (m2*-1) - 0.3 then
							tile_to_insert = "dirt-4"
							table.insert(treasure_chest_positions, {pos_x,pos_y})								
						else
							if cave_noise_3 < m3 and cave_noise_3 > m3*-1 and cave_noise_2 < m2+0.3 and cave_noise_2 > (m2*-1)-0.3 then
								tile_to_insert = "dirt-2"
								table.insert(treasure_chest_positions, {pos_x,pos_y})
							end
						end
					end
				end
				
				if tile_distance_to_center < global.spawn_dome_size * (cave_noise_3 * 0.05 + 1.1)  then									
					tile_to_insert = "dirt-7"
				end
			else
				if tile_distance_to_center < 750 * (1 + cave_noise_3 * 0.8) then
					tile_to_insert = "water"
					table.insert(fish_positions, {pos_x,pos_y})
				else
					tile_to_insert = "grass-1"
				end				
			end
			
			if tile_distance_to_center < global.spawn_dome_size and tile_distance_to_center > global.spawn_dome_size - 500 and tile_to_insert == "grass-1" then
				table.insert(rock_positions, {pos_x,pos_y})	
			end			
			if tile_to_insert == "dirt-7" or tile_to_insert == "dirt-4" or tile_to_insert == "dirt-2" then	
				 table.insert(rock_positions, {pos_x,pos_y})				
			end
			
			if tile_to_insert == false then
				table.insert(tiles, {name = "out-of-map", position = {pos_x,pos_y}}) 
			else
				table.insert(tiles, {name = tile_to_insert, position = {pos_x,pos_y}}) 
			end
		end							
	end
	surface.set_tiles(tiles,true)
		
	for _, p in pairs(treasure_chest_positions) do	
		if math.random(1,200)==1 then 
			treasure_chest(p)
		end
	end
		
	for _, p in pairs(enemy_building_positions) do	
		if math.random(1,50)==1 then
			local pos = surface.find_non_colliding_position("biter-spawner", p, 16, 1)
			if pos then
				if math.random(1,3) == 1 then
					surface.create_entity {name="spitter-spawner",position=pos}
				else
					surface.create_entity {name="biter-spawner",position=pos}
				end
			end			
		end
	end	
	
	for _, p in pairs(enemy_worm_positions) do		
		if math.random(1,350)==1 then
			local tile_distance_to_center = math.sqrt(p[1]^2 + p[2]^2)
			if tile_distance_to_center > global.worm_free_zone_radius then						
				local raffle_index = math.ceil((tile_distance_to_center-global.worm_free_zone_radius)*0.01, 0)
				if raffle_index < 1 then raffle_index = 1 end
				if raffle_index > 10 then raffle_index = 10 end					
				local entity_name = global.worm_raffle_table[raffle_index][math.random(1,#global.worm_raffle_table[raffle_index])]												
				surface.create_entity {name=entity_name, position=p}
			end
		end
	end	
	
	for _, p in pairs(fish_positions) do	
		if math.random(1,25)==1 then 
			surface.create_entity {name="fish",position=p}
		end
	end
		
	for _, p in pairs(secret_shop_locations) do	
		if math.random(1,10)==1 then
			if surface.count_entities_filtered{area={{p[1]-100,p[2]-100},{p[1]+100,p[2]+100}}, name="market", limit=1} == 0 then
				secret_shop(p)
			end
		end
	end	
	
	for _, p in pairs(extra_tree_positions) do	
		if math.random(1,20)==1 then 
			surface.create_entity {name="tree-02",position=p}
		end
	end		
	
	for _, p in pairs(rock_positions) do			
		if math.random(1,3) ~= 1 then
			surface.create_entity {name=global.rock_raffle[math.random(1,#global.rock_raffle)], position=p}
		end
	end
	
	local decorative_names = {}
	for k,v in pairs(game.decorative_prototypes) do
		if v.autoplace_specification then
		  decorative_names[#decorative_names+1] = k
		end
	 end	 
	surface.regenerate_decorative(decorative_names, {{x=math.floor(event.area.left_top.x/32),y=math.floor(event.area.left_top.y/32)}})		
end

local function on_player_joined_game(event)
	local surface = game.surfaces[1]	
	local player = game.players[event.player_index]
	if not global.cave_miner_init_done then		 
		local p = surface.find_non_colliding_position("player", {0,-40}, 10, 1)
		game.forces["player"].set_spawn_position(p,surface)
		player.teleport(p)
		surface.daytime = 0.5
		surface.freeze_daytime = 1
		game.forces["player"].technologies["landfill"].enabled = false
		game.forces["player"].technologies["night-vision-equipment"].enabled = false
		
		global.spawn_dome_size = 20000
		
		global.rock_inhabitants = {}
		global.rock_inhabitants[1] = {"small-biter"}
		global.rock_inhabitants[2] = {"small-biter","small-biter","small-biter","small-biter","small-biter","medium-biter"}
		global.rock_inhabitants[3] = {"small-biter","small-biter","small-biter","small-biter","medium-biter","medium-biter"}
		global.rock_inhabitants[4] = {"small-biter","small-biter","small-biter","medium-biter","medium-biter","small-spitter"}
		global.rock_inhabitants[5] = {"small-biter","small-biter","medium-biter","medium-biter","medium-biter","small-spitter"}
		global.rock_inhabitants[6] = {"small-biter","small-biter","medium-biter","medium-biter","big-biter","small-spitter"}
		global.rock_inhabitants[7] = {"small-biter","small-biter","medium-biter","medium-biter","big-biter","medium-spitter"}
		global.rock_inhabitants[8] = {"small-biter","medium-biter","medium-biter","medium-biter","big-biter","medium-spitter"}
		global.rock_inhabitants[9] = {"small-biter","medium-biter","medium-biter","big-biter","big-biter","medium-spitter"}
		global.rock_inhabitants[10] = {"medium-biter","medium-biter","medium-biter","big-biter","big-biter","big-spitter"}
		global.rock_inhabitants[11] = {"medium-biter","medium-biter","big-biter","big-biter","big-biter","big-spitter"}
		global.rock_inhabitants[12] = {"medium-biter","big-biter","big-biter","big-biter","big-biter","big-spitter"}
		global.rock_inhabitants[13] = {"big-biter","big-biter","big-biter","big-biter","big-biter","big-spitter"}
		global.rock_inhabitants[14] = {"big-biter","big-biter","big-biter","big-biter","behemoth-biter","big-spitter"}
		global.rock_inhabitants[15] = {"big-biter","big-biter","big-biter","behemoth-biter","behemoth-biter","big-spitter"}
		global.rock_inhabitants[16] = {"big-biter","big-biter","big-biter","behemoth-biter","behemoth-biter","behemoth-spitter"}
		global.rock_inhabitants[17] = {"big-biter","big-biter","behemoth-biter","behemoth-biter","behemoth-biter","behemoth-spitter"}
		global.rock_inhabitants[18] = {"big-biter","behemoth-biter","behemoth-biter","behemoth-biter","behemoth-biter","behemoth-spitter"}
		global.rock_inhabitants[19] = {"behemoth-biter","behemoth-biter","behemoth-biter","behemoth-biter","behemoth-biter","behemoth-spitter"}
		global.rock_inhabitants[20] = {"behemoth-biter","behemoth-biter","behemoth-biter","behemoth-biter","behemoth-spitter","behemoth-spitter"}	
		
		global.rock_raffle = {"sand-rock-big","sand-rock-big","rock-big","rock-big","rock-big","rock-huge"}
		
		global.worm_raffle_table = {}
		global.worm_raffle_table[1] = {"small-worm-turret", "small-worm-turret", "small-worm-turret", "small-worm-turret", "small-worm-turret", "small-worm-turret"}
		global.worm_raffle_table[2] = {"small-worm-turret", "small-worm-turret", "small-worm-turret", "small-worm-turret", "small-worm-turret", "small-worm-turret"}
		global.worm_raffle_table[3] = {"small-worm-turret", "small-worm-turret", "small-worm-turret", "small-worm-turret", "small-worm-turret", "medium-worm-turret"}
		global.worm_raffle_table[4] = {"small-worm-turret", "small-worm-turret", "small-worm-turret", "small-worm-turret", "medium-worm-turret", "medium-worm-turret"}
		global.worm_raffle_table[5] = {"small-worm-turret", "small-worm-turret", "small-worm-turret", "medium-worm-turret", "medium-worm-turret", "medium-worm-turret"}
		global.worm_raffle_table[6] = {"small-worm-turret", "small-worm-turret", "small-worm-turret", "medium-worm-turret", "medium-worm-turret", "big-worm-turret"}
		global.worm_raffle_table[7] = {"small-worm-turret", "small-worm-turret", "small-worm-turret", "medium-worm-turret", "medium-worm-turret", "big-worm-turret"}
		global.worm_raffle_table[8] = {"small-worm-turret", "small-worm-turret", "small-worm-turret", "medium-worm-turret", "medium-worm-turret", "big-worm-turret"}
		global.worm_raffle_table[9] = {"small-worm-turret", "small-worm-turret", "medium-worm-turret", "medium-worm-turret", "big-worm-turret", "big-worm-turret"}
		global.worm_raffle_table[10] = {"small-worm-turret", "medium-worm-turret", "medium-worm-turret", "big-worm-turret", "big-worm-turret", "big-worm-turret"}						
		global.worm_free_zone_radius = math.sqrt(global.spawn_dome_size) + 40
		
		global.biter_spawn_schedule = {}
		
		global.treasure_chest_raffle_table = {}
		global.treasure_chest_loot_weights = {}
		table.insert(global.treasure_chest_loot_weights, {{name = 'iron-gear-wheel', count = 32},10})	
		table.insert(global.treasure_chest_loot_weights, {{name = 'coal', count = 3},8})
		table.insert(global.treasure_chest_loot_weights, {{name = 'copper-cable', count = 128},10})
		table.insert(global.treasure_chest_loot_weights, {{name = 'inserter', count = 4},4})		
		table.insert(global.treasure_chest_loot_weights, {{name = 'fast-inserter', count = 4},3})
		table.insert(global.treasure_chest_loot_weights, {{name = 'stack-filter-inserter', count = 2},1})
		table.insert(global.treasure_chest_loot_weights, {{name = 'stack-inserter', count = 2},1})
		table.insert(global.treasure_chest_loot_weights, {{name = 'burner-inserter', count = 16},6})
		table.insert(global.treasure_chest_loot_weights, {{name = 'electric-engine-unit', count = 11},3})		
		table.insert(global.treasure_chest_loot_weights, {{name = 'rocket-fuel', count = 1},3})
		table.insert(global.treasure_chest_loot_weights, {{name = 'empty-barrel', count = 3},7})
		table.insert(global.treasure_chest_loot_weights, {{name = 'lubricant-barrel', count = 1},3})
		table.insert(global.treasure_chest_loot_weights, {{name = 'crude-oil-barrel', count = 4},3})
		table.insert(global.treasure_chest_loot_weights, {{name = 'iron-stick', count = 3},8})
		table.insert(global.treasure_chest_loot_weights, {{name = "small-electric-pole", count = 13},9})
		table.insert(global.treasure_chest_loot_weights, {{name = "firearm-magazine", count = 32},8})
		table.insert(global.treasure_chest_loot_weights, {{name = 'grenade', count = 8},5})
		table.insert(global.treasure_chest_loot_weights, {{name = 'land-mine', count = 15},4})
		table.insert(global.treasure_chest_loot_weights, {{name = 'light-armor', count = 1},1})
		table.insert(global.treasure_chest_loot_weights, {{name = 'heavy-armor', count = 1},2})		
		table.insert(global.treasure_chest_loot_weights, {{name = 'pipe', count = 26},6})		
		table.insert(global.treasure_chest_loot_weights, {{name = 'wooden-chest', count = 1},3})
		table.insert(global.treasure_chest_loot_weights, {{name = 'burner-mining-drill', count = 1},1})
		table.insert(global.treasure_chest_loot_weights, {{name = 'iron-axe', count = 1},4})
		table.insert(global.treasure_chest_loot_weights, {{name = 'raw-wood', count = 24},5})
		table.insert(global.treasure_chest_loot_weights, {{name = 'sulfur', count = 27},7})
		table.insert(global.treasure_chest_loot_weights, {{name = 'explosives', count = 46},6})
		table.insert(global.treasure_chest_loot_weights, {{name = 'shotgun', count = 1},2})
		table.insert(global.treasure_chest_loot_weights, {{name = 'stone-brick', count = 72},4})
		table.insert(global.treasure_chest_loot_weights, {{name = 'small-lamp', count = 4},4})
		table.insert(global.treasure_chest_loot_weights, {{name = 'rail', count = 32},4})	
		table.insert(global.treasure_chest_loot_weights, {{name = 'coin', count = 1},1})
		table.insert(global.treasure_chest_loot_weights, {{name = 'assembling-machine-1', count = 1},2})
		table.insert(global.treasure_chest_loot_weights, {{name = 'assembling-machine-2', count = 1},2})
		table.insert(global.treasure_chest_loot_weights, {{name = 'assembling-machine-3', count = 1},1})
		
		for _, t in pairs (global.treasure_chest_loot_weights) do
			for x = 1, t[2], 1 do
				table.insert(global.treasure_chest_raffle_table, t[1])
			end			
		end
		
		global.ore_spill_cap = 75
		global.mining_prod_research_bonus = 1
		
		global.rock_mining_chance_weights = {}
		global.rock_mining_chance_weights[1] = {"iron-ore",20}
		global.rock_mining_chance_weights[2] = {"copper-ore",18}
		global.rock_mining_chance_weights[3] = {"coal",15}
		global.rock_mining_chance_weights[4] = {"stone",8}
		global.rock_mining_chance_weights[5] = {"uranium-ore",3}
		global.rock_mining_raffle_table = {}				
		for _, t in pairs (global.rock_mining_chance_weights) do
			for x = 1, t[2], 1 do
				table.insert(global.rock_mining_raffle_table, t[1])
			end			
		end

		global.darkness_threat_level = {}
		global.darkness_messages = {
		"Something is lurking in the dark...",
		"A shadow moves. I doubt it is friendly...",
		"The silence grows louder...",
		"Trust not your eyes. They are useless in the dark.",
		"The darkness hides only death. Turn back now.",
		"You hear noises...",
		"They chitter as if laughing, hungry for their next foolish meal...",
		"Despite what the radars tell you, it is not safe here...",
		"The shadows are moving...",
		"You feel like, something is watching you...",		
		}					
		
		global.cave_miner_init_done = true						
	end	
	if player.online_time < 10 then
		global.darkness_threat_level[player.name] = 0
		player.insert {name = 'pistol', count = 1}
		player.insert {name = 'raw-fish', count = 3}
		player.insert {name = 'firearm-magazine', count = 16}			
		player.insert {name = 'iron-gear-wheel', count = 4}
		player.insert {name = 'iron-plate', count = 8}
	end
end

local function spawn_cave_inhabitant(pos)	
	local tile_distance_to_center = math.sqrt(pos.x^2 + pos.y^2)			
	local rock_inhabitants_index = math.ceil((tile_distance_to_center-math.sqrt(global.spawn_dome_size))*0.02, 0)
	if rock_inhabitants_index < 1 then rock_inhabitants_index = 1 end
	if rock_inhabitants_index > 20 then rock_inhabitants_index = 20 end					
	local entity_name = global.rock_inhabitants[rock_inhabitants_index][math.random(1,#global.rock_inhabitants[rock_inhabitants_index])]
	local p = game.surfaces[1].find_non_colliding_position(entity_name , pos, 7, 0.5)			
	if p then game.surfaces[1].create_entity {name=entity_name, position=p} end				
end

local function darkness_events()
	for _, p in pairs (game.connected_players) do		
		if global.darkness_threat_level[p.name] > 4 then						
			for x = 1, global.darkness_threat_level[p.name], 1 do
				spawn_cave_inhabitant(p.position)	
			end				
			local biters_found = game.surfaces[1].find_enemy_units(p.position, 12, "player")
			for _, biter in pairs(biters_found) do				
				biter.set_command({type=defines.command.attack, target=p.character, distraction=defines.distraction.none})					
			end
			p.character.damage(math.random(global.darkness_threat_level[p.name]*3,global.darkness_threat_level[p.name]*4),"enemy")
		end
		--if global.darkness_threat_level[p.name] == 3 then
			--p.character.damage(1,"enemy")				
		--end
		if global.darkness_threat_level[p.name] == 2 then
			p.print(global.darkness_messages[math.random(1,#global.darkness_messages)],{ r=0.65, g=0.0, b=0.0})				
		end
		global.darkness_threat_level[p.name] = global.darkness_threat_level[p.name] + 1
	end
end

local function darkness_checks()
	for _, p in pairs (game.connected_players) do
		p.character.disable_flashlight()
		local tile_distance_to_center = math.sqrt(p.position.x^2 + p.position.y^2)
		if tile_distance_to_center < math.sqrt(global.spawn_dome_size) then
			global.darkness_threat_level[p.name] = 0
		else
			if p.character.driving == true then
				global.darkness_threat_level[p.name] = 0
			else
				local light_source_entities = game.surfaces[1].find_entities_filtered{area={{p.position.x-12,p.position.y-12},{p.position.x+12,p.position.y+12}}, name="small-lamp"}		
				for _, lamp in pairs (light_source_entities) do
					local circuit = lamp.get_or_create_control_behavior()
					if circuit then
						if lamp.energy > 60 and circuit.disabled == false then					
							global.darkness_threat_level[p.name] = 0
							break
						end
					else
						if lamp.energy > 60 then					
							global.darkness_threat_level[p.name] = 0
							break
						end
					end
				end
			end
		end
	end
end

local function on_tick(event)
		
		if game.tick % 30 == 0 then
			if global.biter_spawn_schedule then
				for x, b in pairs (global.biter_spawn_schedule) do
					if game.tick > b[1] then
						spawn_cave_inhabitant(b[2])
						global.biter_spawn_schedule[x] = nil
					end
				end
			end
		end

		if game.tick % 240 == 0 then
			darkness_checks()	
			darkness_events()	
		end		
		
		if game.tick == 30 then
			local surface = game.surfaces[1]						
			--p = game.forces["player"].get_spawn_position(surface)
			local p = game.surfaces[1].find_non_colliding_position("market",{0,-15},60,0.5)
			local market = surface.create_entity {name = "market", position = p}
			market.destructible = false
			
			for _, item in pairs(market_items.spawn) do
				market.add_market_item(item)
			end			
			surface.regenerate_entity({"tree-01", "tree-02","tree-03","tree-04","tree-05","tree-06","tree-07","tree-08","tree-09","dead-dry-hairy-tree","dead-grey-trunk","dead-tree-desert","dry-hairy-tree","dry-tree","rock-big","rock-huge"})			
		end
end

local function on_marked_for_deconstruction(event)
	if event.entity.name == "rock-huge" or event.entity.name == "rock-big" or event.entity.name == "sand-rock-big" then
		event.entity.cancel_deconstruction(game.players[event.player_index].force.name)
	end
end

local function pre_player_mined_item(event)

	if math.random(1,10) == 1 then
		if event.entity.name == "rock-huge" or event.entity.name == "rock-big" or event.entity.name == "sand-rock-big" then		
			for x = 1, math.random(6,10), 1 do
				table.insert(global.biter_spawn_schedule, {game.tick + 30*x, event.entity.position})		
			end			
		end
	end
	
	if event.entity.name == "rock-huge" or event.entity.name == "rock-big" or event.entity.name == "sand-rock-big" then		
		local tile_distance_to_center = math.sqrt(event.entity.position.x^2 + event.entity.position.y^2)
		local player = game.players[event.player_index]
		game.surfaces[1].spill_item_stack(player.position,{name = "raw-fish", count = math.random(3,4)},true)
		local bonus_amount = math.ceil((tile_distance_to_center - math.sqrt(global.spawn_dome_size)) * 0.5, 0) 
		if bonus_amount < 1 then bonus_amount = 0 end		
		local amount = (math.random(55,65) + bonus_amount)*(1+game.forces.player.mining_drill_productivity_bonus)
		amount = math.round(amount,0)		
		local mined_loot = global.rock_mining_raffle_table[math.random(1,#global.rock_mining_raffle_table)]
		if amount > global.ore_spill_cap then
			game.surfaces[1].spill_item_stack(event.entity.position,{name = mined_loot, count = global.ore_spill_cap},true)
			amount = amount - global.ore_spill_cap
			local i = player.insert {name = mined_loot, count = amount}
			amount = amount - i
			if amount > 0 then
				game.surfaces[1].spill_item_stack(event.entity.position,{name = mined_loot, count = amount},true)
			end
		else
			game.surfaces[1].spill_item_stack(event.entity.position,{name = global.rock_mining_raffle_table[math.random(1,#global.rock_mining_raffle_table)], count = amount},true)
		end
	end
end

local function on_player_mined_entity(event)
	if event.entity.name == "rock-huge" or event.entity.name == "rock-big" or event.entity.name == "sand-rock-big" then
		event.buffer.clear()
	end
end

local function on_entity_died(event)
	if event.force.name == "player" then
		if math.random(1,4) == 1 then
			if event.entity.name == "rock-huge" or event.entity.name == "rock-big" or event.entity.name == "sand-rock-big" then			
				for x = 1, math.random(8,12), 1 do
					table.insert(global.biter_spawn_schedule, {game.tick + 10*x, event.entity.position})		
				end			
			end
		end
	end
end

local function on_entity_damaged(event)
	if event.entity.name == "rock-huge" or event.entity.name == "rock-big" or event.entity.name == "sand-rock-big" then
		if event.force.name == "enemy" then 
			event.entity.health = event.entity.health + (event.final_damage_amount - 0.05)
			if event.entity.health <= event.final_damage_amount then event.entity.die() end			
		end		
	end	
end

local function on_player_respawned(event)
	local player = game.players[event.player_index]
	player.character.disable_flashlight()
end

local function on_research_finished(event)
	game.forces.player.manual_mining_speed_modifier = game.forces.player.mining_drill_productivity_bonus * 3
end

function map_pregen(radius)
	if not radius then radius = 1024 end
	game.create_force("map_pregen")
	game.players[1].force = game.forces["map_pregen"]
	game.forces.map_pregen.chart(game.surfaces[1],{{x = -1 * radius, y = -1 * radius}, {x = radius, y = radius}})	
end

function map_pregen_end()
	game.players[1].force = game.forces["player"]
end

function test()
	local x = 0
	if x == 1 then								
		local surface = game.surfaces[1]
		game.player.cheat_mode=true
		game.speed = 1
		surface.daytime = 1
		game.player.force.research_all_technologies()
		game.forces["enemy"].evolution_factor = 0.2
		local chart = 200
		local surface = game.surfaces[1]	
		game.forces["player"].chart(surface, {lefttop = {x = chart*-1, y = chart*-1}, rightbottom = {x = chart, y = chart}})		
	end
end

Event.add(defines.events.on_research_finished, on_research_finished)
Event.add(defines.events.on_player_respawned, on_player_respawned)
Event.add(defines.events.on_player_mined_entity, on_player_mined_entity)
Event.add(defines.events.on_entity_died, on_entity_died)
Event.add(defines.events.on_entity_damaged, on_entity_damaged)
Event.add(defines.events.on_pre_player_mined_item, pre_player_mined_item)
Event.add(defines.events.on_marked_for_deconstruction, on_marked_for_deconstruction)
Event.add(defines.events.on_chunk_generated, on_chunk_generated)
Event.add(defines.events.on_tick, on_tick)	
Event.add(defines.events.on_player_joined_game, on_player_joined_game)