-- This is the Minefall garbage core repository.
-- Author: Astrobe
-- Licence (code): same as Minetest.
-- Licence (sound): teleport sound from Nether mod, CC0 1.0

local Version="Administrative Decisions"

mfplayers = {}
local MAX_ENERGY=5 
local talents=minetest.get_mod_storage()

local random=math.random
mf={}

function mf.is_keymaster(name) return mfplayers[name].talent=="keymaster" end
function mf.is_flyer(name) return mfplayers[name].talent=="flyer" end
function mf.is_diver(name) return mfplayers[name].talent=="diver" end

function mf.makeInvisible(player)
	local name=player:get_player_name()
	mfplayers[name].invisible=true
	mfplayers[name].warnTimer=0.1
end

function mf.clan_ok(name, pos)
	local meta=minetest.get_meta(pos)
	if not meta then return true end
	local owner=meta:get_string("owner") or ""
	if owner=="" then return true end
	if (string.len(owner)+string.len(name))%2==0 then return true end
	minetest.chat_send_player(name, minetest.colorize("orange","Sorry, this is owned by the other clan"))
end

function mf.threatWarn(player)
	local playerName=player:get_player_name()
	if playerName and mfplayers[playerName] then mfplayers[playerName].warnTimer=2 end
end



hb.register_hudbar("energy", 0xFFFFFF, "Mana",
{ bar = "hudbars_bar_breath.png", icon = nil },
0, 0, false, "%s: %.1f/%.1f")

hb.register_hudbar("timer", 0xFFFFFF, "Timer",
{ bar = "hudbars_bar_breath.png", icon = nil },
0, 0, true, "%s: %d")

minetest.register_chatcommand("t", {
	description="Starts a timer",
	params="<duration in minutes (5 mins by default)>",
	func = function(name, param)
		local user=core.get_player_by_name(name)
		if not user then return true end
		local duration=(tonumber(param) or 5)
		mfplayers[name].timer=duration*60
		hb.change_hudbar(user, "timer", duration, duration)
		hb.unhide_hudbar(user, "timer")
		return true
	end
})

minetest.register_on_item_eat(function(hp_change, replace, itemstack, user)
	if hp_change + user:get_hp() <= 20 then return end
	local name=user:get_player_name()
	if not name then return end
	local e=(hp_change+user:get_hp() - 20)/2
	mfplayers[name].energy=math.min(mfplayers[name].max_energy, mfplayers[name].energy+e)
	hb.change_hudbar(user, "energy", mfplayers[name].energy)
	return nil
end)
	


local max=math.max
local band=bit.band

local alertCrosshair=
{
	type="image",
	position= {x=0.5, y=0.5},
	text="crosshair.png^[colorize:magenta",
	z_index=1,
	scale={x=1,y=1},
}

minetest.register_globalstep(function(dtime)
	for playerName,playerInfo in pairs(mfplayers) do
		local player = minetest.get_player_by_name(playerName)
		local change=false
		local maxe=playerInfo.max_energy
		local cached_e=playerInfo.cached_e or 0
		if player ~= nil then
				if playerInfo.timer then
					local old=math.ceil(playerInfo.timer/60)
					-- I want the hudbar to update once par second only.
					playerInfo.timer=playerInfo.timer-dtime
					if playerInfo.timer<=0 then
						core.chat_send_player(playerName, "Your timer ended")
						core.sound_play("ding", {gain=1, to_player=playerName}, true)
						hb.hide_hudbar(player, "timer")
						playerInfo.timer=nil
					elseif math.ceil(playerInfo.timer/60)~=old then
						hb.change_hudbar(player, "timer", math.ceil(playerInfo.timer/60))
					end
				end
				local physics=player:get_physics_override()
				local jump=band(player:get_player_control_bits(),16)~=0
				if playerInfo.invisible then
					playerInfo.energy=playerInfo.energy-(1+playerInfo.regen)*dtime
					change=true
					if playerInfo.energy<0 then
						playerInfo.energy=0
						playerInfo.invisible=nil
						player:set_properties{visual_size={x=1, y=1}}
					end
				end
				if playerInfo.energy>dtime and jump then
					physics.gravity= (mf.perk(playerName)=="lift" and -0.1) or 0
					local drain= (mf.is_flyer(playerName) and 2) or 4 
					playerInfo.energy=max(playerInfo.energy-dtime*(drain-playerInfo.regen), 0)
					change=true
				elseif playerInfo.energy < maxe then
					physics.gravity=1
					playerInfo.energy=math.min(playerInfo.energy+dtime*playerInfo.regen, maxe)
					change=true
				else
					change=physics.gravity~=1
					physics.gravity=1
				end

				if change==true then
					player:set_physics_override(physics)
					if math.abs(cached_e-playerInfo.energy)>=0.1 or (playerInfo.energy==maxe and cached_e ~= maxe) then
						hb.change_hudbar(player, "energy", playerInfo.energy, max(maxe,5))
						playerInfo.cached_e=playerInfo.energy
					end
				end
				local mfp=mfplayers[playerName]
				if mfp.warnTimer>0 then mfp.warnTimer=mfp.warnTimer-dtime end
				local hudId=mfp.hudWarn
				if mfp.warnTimer>0 and not hudId then
					mfp.hudWarn=player:hud_add(alertCrosshair)
				elseif mfp.warnTimer<=0 and hudId then
					player:hud_remove(hudId)
					mfp.hudWarn=nil
				end
		end
	end
end)


-- News code derived from "szutil_motd".
-- Calculate form dimensions (configurable) and spec strings.
local fspref, fssuff
do
	local fsw = 15
	local fsh = 12
	local tbw = fsw - 0.5
	local tbh = fsh - 0.5
	fspref = "size[" .. fsw .. "," .. fsh .. ",true]" .. "textarea[0.25,0.25;" .. tbw .. "," .. tbh .. ";;;"
	fssuff = "]button_exit[0," .. tbh .. ";" .. fsw .. ",1;ok;Continue]"
end

local function sendmotd(name)
	local news=""
	news=news..
	"= Minefall: ".. Version.." =\n\n" ..

	"\nANNOUNCEMENTS:\n\n"..
	"New: /t command (starts a timer).\n"..
	"New: 'tobio' equipement (faster movement in water).\n"..
	"New: 'trades' tab.\n"..
	"New: game option to choose the appearance of tree leaves.\n"..
	"New: game option to disable 'columnar' property.\n"..
	"New: Color saturation decreases as your HP gets lower (world turns grey).\n"..
	"New: /sat command sets the max saturation.\n"..
	"Changed: Boats are no longer craftable, obtain them from watermills. New boats cannot be taken back and will disappear if left alone for more than 5 minutes.\n"..
	"Changed: Windmills have a 5-minute cooldown, take grey mese (like watermills).\n"..
	"Changed: yellow wasps now drop only grey&yellow mese only; hives produce honey more consistently.\n"..
	"Changed: cobbles are no longer 'columnar'.\n"..
	"Changed: elephant grass can be used to make straw.\n"..
	"Changed: basalt hit by explosion turns to lava.\n"..
	"Changed: spears merged into shovels; shovels gain spears' properties as weapons.\n"..
	"Changed: protections prevent fire from spreading.\n"..
	"Changed: beetles can eat blueberries.\n"..
	"Changed: rain effect (experimental, possible glitches).\n"..
	"Fixed: a couple of crashes, issue with signs.\n"..
	"\nYou can recall this screen with the /news command.\n"

	minetest.show_formspec(
	name,
	"mf",
	fspref..minetest.formspec_escape(news)..fssuff
	)
end
minetest.register_chatcommand("news", { func = sendmotd })


minetest.register_on_newplayer(function(player)
	local name=player:get_player_name()
	sendmotd(name)
	if minetest.check_player_privs(name, "privs") then
		player:get_inventory():add_item("main", "craftguide:book")
		rp.award(name, 20)
	end
	minetest.chat_send_all(minetest.colorize("orange", "Welcome to "..name.." !"))
	end)

minetest.register_on_joinplayer(function(player)
	local playerName = player:get_player_name()

	mfplayers[playerName] = {max_energy=MAX_ENERGY, energy=0, drain=2, regen=1, shielded=true, warnTimer=0}
	mfplayers[playerName].talent=talents:get_string(playerName)
	hb.init_hudbar(player, "energy")
	hb.init_hudbar(player, "timer")
	-- we do want players to use compass and waypoints instead of unusable 3D coordinates.
	player:hud_set_flags {basic_debug=false}
	player:set_moon {scale=4}
	player:set_sky {fog={fog_distance=240}}

	-- hide nametag
	local c=player:get_nametag_attributes().color
	c.a=0
	player:set_nametag_attributes {color=c}
	-- show name when pointing and allow zoom
	player:set_properties { infotext=playerName }
	--player:set_lighting { shadows={intensity=0.1 } }
	player:set_lighting { shadows={intensity=0 } }
	local phy=player:get_physics_override()
	player:set_physics_override(phy)

	if not (minetest.get_player_privs(playerName).interact == true)  then
		sendmotd(playerName)
	end
	-- minetest.chat_send_player(playerName, "Perk=" .. mf.perk(playerName))
end)

minetest.register_on_leaveplayer(function(player)
	local playerName = player:get_player_name()
	mfplayers[playerName] = nil

end)


minetest.register_abm {
	label="Unified fire/lava cooling",
		nodenames={"fire:basic_flame", "default:lava_source"},
		neighbors={"group:cools_lava"},
		interval=5,
		chance=1,
		catch_up=false,
		action=function(pos, node, active_object_count, active_object_count_wider)
			local cooler=minetest.find_node_near(pos, 1, {"group:cools_lava"})
			if not cooler then return end
			minetest.remove_node(cooler)
			if node.name=="fire:basic_flame" then
				minetest.remove_node(pos)
			else
				local blocks={"default:obsidian", "default:basalt"}
				minetest.set_node(pos, {name=blocks[random(1, #blocks)]})
			end

			minetest.sound_play("default_cool_lava", {pos=pos, max_hear_distance=16, gain=0.20})
			end
}

-- Water sports
minetest.register_abm {
	label="Convert flowing sea water into sea water sources",
	nodenames={"default:water_flowing"},
	interval=7,
	chance=1,
	catch_up=false,
	max_y=10, -- sea level can raise.
	action=function(pos, node, active_object_count, active_object_count_wider)
		local u={x=pos.x, y=pos.y-1, z=pos.z}
		local n=minetest.get_node(u).name
		local d=minetest.registered_nodes[n]
		if d and (d.walkable~=false or n=="default:water_source") then minetest.set_node(pos, {name="default:water_source"}) end
	end }

minetest.register_abm {
	label="Springs",
	nodenames={"default:river_water_flowing", "default:river_water_source"},
	interval=241,
	chance=4,
	catch_up=false,
	max_y=70, -- that's about the limit for the swamp biome
	min_y=0,
	action=function(pos, node, active_object_count, active_object_count_wider)
		local u={x=pos.x, y=pos.y-1, z=pos.z}
		local n=minetest.get_node(u).name
		if n=="default:dirt" or n=="default:limestone" then
			minetest.swap_node(u, {name="default:gravel"})
			if n=="default:limestone" then
				minetest.swap_node(pos, {name="default:river_water_source"})
			end
		end
	end }

local function node_explode(pos)
	minetest.remove_node(pos)
	minetest.after(math.random()/2, function(pos) mobs:explosion(pos, 3) end, pos)
end


--[[
minetest.register_craft{
	output="default:diamond",
	type="shapeless",
	recipe= {"default:coalblock","default:mese_crystal","default:coalblock"}
}
]]


--[[
minetest.register_craft {
	type="cooking",
	recipe="group:tree",
	output="default:coal_lump",
	cooktime=90,
}
]]

-- Floods destroy foliage, in order to get consistent landscapes in particular in
-- cold swamps.

for node, _ in pairs(minetest.registered_nodes) do
	if minetest.get_item_group(node, "leafdecay_drop")>0 then
		minetest.override_item(node, { floodable=true })
	end
end

sfinv.register_page("credits", {
	title="Credits",
	get= function(self, player, context)
		local credits="textarea[0.1,0;8,10;;;"..
			minetest.formspec_escape(
			"Minefall by Astrobe, 2025\n"..
			"Version codename: \""..Version.."\"\n"..
			"Source code is available via ContentDB: https://content.minetest.net/packages/Astrobe/minefall\n"..
			"\nSPECIAL THANKS\n"..
			"Rubenwardy (ContentDB, Modding book, ...)\n"..
			"TenPlus1 for his many mods\n".. 
			"D00med for his wonderful and lightweight mobs\n"..
			"The Farlands team for their inspirational Minetest game\n"..
			"Red5 Studio for Firefall, which this game tries to replace.\n"..
			"\nTHANKS\n"..
			"The Luanti and MTG team\n"..
			"The Minetest/Luanti modding community for their many mods\n"..
			"\nSUGGESTED SETTINGS\n"..
			"Orbit tilt: 45°\n"..
			"With water reflections enabled: wave length 2, speed 20, height 0.5\n"..
			"Field of view: 60 (can take some time to get used to)\n"
			).."]"
		return sfinv.make_formspec(player, context, credits, false, nil)
		end
	})

minetest.register_chatcommand("talent", 
{
	description="Select a talent",
	params="<talent name>",
	func = function(name, param)
		local player=minetest.get_player_by_name(name)
		if not player then return end
		local talent=mfplayers[name].talent
		if talent == "" then talent= nil end
		local bmode=bones.get_mode_for(player)
		if param=="" then
			if talent then
				minetest.chat_send_player(name,"Your current talent is: "..
				({keymaster="traveler", diver="fighter", flyer="flyer"})[talent])
			else
				minetest.chat_send_player(name, "No Talent selected. See Help tab for information")
			end
			return true
		end
		if bmode=="keep" then
			minetest.chat_send_player(name, "You need to level up to Regular player first")
			return true
		end 
		-- Due to various name changes, we have to transpose. Yes, it's a mess.
		local realTalent=({traveler="keymaster", fighter="diver", flyer="flyer" })[param]
		if not realTalent then
			minetest.chat_send_player(name, "This is not the name of a talent")
			return true
		end
		if realTalent==mfplayers[name].talent then
			minetest.chat_send_player(name, "This is already your talent.")
			return true
		end
		if rp.get_player_count(name) <= 40 then
			minetest.chat_send_player(name, "You don't have enough RPs")
			return true
		end
		talents:set_string(name, realTalent)
		if bmode ~= "drop" then
			minetest.chat_send_all(minetest.colorize("green", name.." became a Advanced player!"))
			bones.set_mode_for(player, "drop")	
			minetest.sound_play("ding", {gain=1}, true)
		end
		mfplayers[name].talent=realTalent
		minetest.chat_send_player(name, "You have now the '"..param.."' talent")
		rp.award(name, -40) 
		return true	
	end
})

-- This randomly rotates dirt-like nodes, so that if players take a path frequently,
-- it will show in a more-or-less subtle way.
local disturbable={
	["default:dirt_with_grass"]=true,
	["default:dirt_with_dry_grass"]=true,
	["default:dirt_with_coniferous_litter"]=true,
	["default:dirt_with_snow"]=true,
	["default:gravel"]=true,
	--["default:dirt"]=true,
	["default:dry_dirt"]=true,
	["default:dry_dirt_with_stones"]=true,
	["default:snowblock"]=true,
	["default:sand"]=true,
	["default:desert_sand"]=true,
}


local acc=0
minetest.register_globalstep(function(dtime)
	-- TODO: merg with the other global step callback
	acc=acc+dtime
	if acc >= random(3,10) then
		acc=0
		for _, player in ipairs(minetest.get_connected_players()) do
			local pos=player:get_pos()
			pos.y=pos.y-0.5
			local node=minetest.get_node(pos)
			-- I don't think we have to check for protection for that. This
			-- won't even make a falling node fall.
			if node and disturbable[node.name] and (node.param2 or 0) == 0 then
				node.param2=random(1,23)
				minetest.set_node(pos, node)
				minetest.check_for_falling(pos)
			end
		end
	end
end)


-- ALCHEMY
-- Not sure about that. Maybe it is a too good mese sink,
-- maybe it removes the incentive to to more dangerous places 
-- to mine better metals.
--[[
minetest.register_craft {
	output = 'default:tin_lump',
	type="shapeless",
	recipe = {"default:mese_crystal", "default:copper_ingot" }
} 
minetest.register_craft {
	output = 'default:iron_lump',
	type="shapeless",
	recipe = {"default:mese_crystal", "default:tin_ingot" }
} 
minetest.register_craft {
	output = 'default:gold_lump',
	type="shapeless",
	recipe = {"default:mese_crystal", "default:steel_ingot" }
} 
]]

minetest.override_item("default:stone_with_coal", { on_blast=node_explode, on_burn=node_explode })

minetest.register_on_prejoinplayer(function(name, ip)
	if string.find(name, "[^%a-]") then
		minetest.log("Rejecting name: ["..name.."]")
		return "names can only contain letters."
		 end
	end)

-- All I wanted is to be able to check if the user entered a seed,
-- so that if they didn't I could use a nice default seed for an easier start.
-- fixed_map_seed doesn't help; the design of this part of the API feels FUBAR.
-- Eventually I had to use a footer image for the menu to tell the user to use that seed.
--[[
local userSeed=minetest.get_mapgen_params("seed").seed
if userSeed==0 then userSeed="" else userSeed="12341234" end

local userSeed=minetest.settings:get("fixed_map_seed")

minetest.set_mapgen_params
{
	mgname="v7",
	flags="dungeons,caves",
}

]]


for k,v in pairs{
	mgv7_spflags="mountains,noridges,nofloatlands,nocaverns",
} do minetest.set_mapgen_setting(k, v, true) end

function mf.perk(name)
	-- singlerplayer is fisher. Chosen so that the player won't be
	-- too surprised if they go online (=>different name). Also, this gift allows
	-- the player to spend more cotton on cloth and get NPC traders earlier, which
	-- helps with shortening the early game.
	local perks={"fisher", "ranger", "lift"}
	return perks[string.byte(name, -1)%3+1]
end

minetest.register_abm {
	label="stone erosion",
	nodenames={"group:stone"},
	interval=257,
	chance=100000,
	catch_up=false,
	action=function(pos, node)
		if minetest.is_protected(pos) then return end
		pos.y=pos.y-1
		if minetest.get_node(pos).name=="air" then
			pos.y=pos.y+1
			minetest.spawn_falling_node(pos)
			pos.y=pos.y+1
			minetest.check_for_falling(pos)
		end
	end
}

minetest.register_abm {
	label="coral spread",
	nodenames={"default:sand"},
	neighbors="default:water_source",
	interval= 257,
	chance=100,
	catch_up=false,
	max_y=-2,
	action=function(pos, node)
		if minetest.find_node_near(pos, 1, "default:coral") then
			if random(2)==1 then
				minetest.set_node(pos, {name="default:coral"})	
			else
				minetest.set_node(pos, {name="default:coral2"})
			end
		end
	end
}


function core.show_death_screen(player, _reason)
	local msg
	local name=player:get_player_name()
	if minetest.check_player_privs(name, "interact") and (vector.distance(player:get_pos(), beacon.home(name)) > 100) then
		msg="Situation critical; emergency recall."
	else
		msg="You've been found unconscious and brought back home."
	end
	local fs = "size[11,3,true]"..
		"label[3,1;"..core.formspec_escape(msg).."]button_exit[4,2;3,0.5;btn_respawn;OK]"
	core.show_formspec(player:get_player_name(), "__builtin:death", fs)
end
