

-- Copyright (C) 2020 2026 PsycoJaker

-- This file is part of UwU Minetest Mod.

-- UwU Mod is free software: you can redistribute it and/or modify
-- it under the terms of the GNU Affero General Public License as
-- published by the Free Software Foundation, either version 3 of the
-- License, or (at your option) any later version.

-- UwU Mod is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- GNU Affero General Public License for more details.

-- You should have received a copy of the GNU Affero General Public License
-- along with UwU Mod.  If not, see <https://www.gnu.org/licenses/>.


-- uwu/functions.lua

local S = core.get_translator(core.get_current_modname())

uwu = {}
uwu._fx_last = {}
uwu._fx_last_gc = 0
uwu.growhappiness = 50

function uwu.timer()
	return math.random(30, 50)
end

function uwu.newpos(pos)
	local vadd = vector.add
	local up   = vadd(pos, vector.new(0,  1, 0))
	local down = vadd(pos, vector.new(0, -1, 0))
	local n_up   = core.get_node(up).name
	local n_down = core.get_node(down).name
	-- Preferencia: si hay aire abajo, cae
	if n_down == "air" then
		return down
	end
	-- Si hay un obstáculo arriba, intentamos lateral (si hay aire)
	local need_lateral = (n_up ~= "air")
	-- O a veces lateral aunque no haya obstáculo (random walk)
	if not need_lateral and math.random(1, 2) ~= 1 then
		return nil
	end
	local adj = {
		vadd(pos, vector.new( 1, 0, 0)),
		vadd(pos, vector.new(-1, 0, 0)),
		vadd(pos, vector.new( 0, 0, 1)),
		vadd(pos, vector.new( 0, 0,-1)),
	}
	-- Mezcla simple para no sesgar siempre al mismo lado
	for i = #adj, 2, -1 do
		local j = math.random(1, i)
		adj[i], adj[j] = adj[j], adj[i]
	end
	for _, p in ipairs(adj) do
		if core.get_node(p).name == "air" then
			return p
		end
	end
	return nil
end

function uwu.rotate(pos, param2)
	local r = math.random(0, 3)
	local node = core.get_node(pos)
	param2 = param2 or r
	node.param2 = param2
	core.swap_node(pos, node)
	return true
end

function uwu.walk(pos)
	local oldnode = core.get_node(pos)
	local oldnodemeta = core.get_meta(pos):to_table()
	local newpos = uwu.newpos(pos)
	if not newpos then
		return nil
	end
	core.set_node(newpos, oldnode)
	core.get_meta(newpos):from_table(oldnodemeta)
	core.set_node(pos, { name = "air" })
	return newpos
end

function uwu.particles(pos, happiness, texture)
	local now = core.get_gametime()
	local key = pos.x .. "," .. pos.y .. "," .. pos.z
	local last = uwu._fx_last[key] or 0
	if now - last < 1 then
		return
	end
	uwu._fx_last[key] = now
	if now - (uwu._fx_last_gc or 0) > 60 then
		uwu._fx_last_gc = now
		for k, t in pairs(uwu._fx_last) do
			if now - t > 120 then
				uwu._fx_last[k] = nil
			end
		end
	end
	local meta = core.get_meta(pos)
	happiness = happiness or meta:get_int("happiness")
	texture = texture or "heart.png"
	local amount = 6
	if type(happiness) == "number" then
		amount = math.max(2, math.min(12, math.floor(happiness / 10) + 3))
	end
	core.add_particlespawner({
		amount = amount,
		time = 0.2,
		minpos = vector.add(pos, vector.new(-0.1, 0, -0.1)),
		maxpos = vector.add(pos, vector.new( 0.1, 0.2, 0.1)),
		minvel = vector.new(-0.4, 0.8, -0.4),
		maxvel = vector.new( 0.4, 1.6,  0.4),
		minexptime = 0.4,
		maxexptime = 0.8,
		minsize = 0.8,
		maxsize = 1.5,
		collisiondetection = false,
		vertical = false,
		texture = texture,
	})
end

function uwu.add_happiness(pos, num, prob)
	-- if prob take happiness 1/prob times
	local meta = core.get_meta(pos)
	local happiness = meta:get_int("happiness")
	local rprob = prob or 1
	local r = math.random(1, rprob)
	if r ~= 1 or happiness+num < 0 then
		return
	end
	local new_happiness = happiness + num
	meta:set_int("happiness",new_happiness)
	if new_happiness <= 0 then
		meta:set_string("infotext",S("UnU block\nHappiness: @1",new_happiness))
	else
		meta:set_string("infotext",S("UwU block\nHappiness: @1",new_happiness))
	end
	if num > 0 then
		uwu.particles(pos, new_happiness)
	end
	if new_happiness < 200 then
		return
	end

	-- evolución UwU
	core.set_node(pos,{name="air"})
	core.add_entity(pos,"uwu:pet")
	core.sound_play("uwu_transform",{
		pos = pos,
		gain = 2,
		max_hear_distance = 16,
		pitch = 0.9 + math.random()*0.2
	})
	core.add_particlespawner({
		amount = 60,
		time = 0.6,
		minpos = vector.subtract(pos,0.4),
		maxpos = vector.add(pos,0.4),
		minvel = {x=-2,y=2,z=-2},
		maxvel = {x=2,y=4,z=2},
		minacc = {x=0,y=-9.8,z=0},
		maxacc = {x=0,y=-9.8,z=0},
		minexptime = 0.6,
		maxexptime = 1.2,
		minsize = 1,
		maxsize = 2.5,
		texture = "heart.png"
	})

	for i=1,12 do
		local angle = i*math.pi*2/12
		local offset = {
			x = math.cos(angle)*0.6,
			y = 0.3,
			z = math.sin(angle)*0.6
		}
		core.add_particle({
			pos = vector.add(pos,offset),
			velocity = {x=0,y=2,z=0},
			acceleration = {x=0,y=-3,z=0},
			expirationtime = 1,
			size = 2,
			texture = "heart.png"
		})
	end
end

function uwu.burp (pos, prob)
	local rprob = prob or 1
	local r = math.random(1, rprob)
	if r == 1 then
		core.sound_play("burp", {
			pos = pos,
			gain = 1,
			max_hear_distance = 16,
			pitch = math.random(70,110)/100
		})
	else
		core.sound_play("mcl_hunger_bite", {pos = pos, gain = 0.2})
	end
end

function uwu.say_uwu(pos, prob)
	local rprob = prob or 1
	local r = math.random(1, rprob)
	local nodename = core.get_node(pos).name
	local pitch = 1
	if r == 1 then
		if nodename == "uwu:little" then pitch = 2 end
		core.sound_play("uwu", {
			pos = pos,
			gain = 1,
			pitch = pitch
		})
	end
end

function uwu.check_food(itemstack)
	local itemname = itemstack:get_name()
	local is_food = core.get_item_group(itemname, "food") >= 1 -- bool
	local is_bad_food = itemname == "mcl_mobitems:rotten_flesh" -- bool
	-- get energy
	local energy = core.get_item_group(itemname, "eatable")
	if type(energy) ~= "number" or energy <= 0 then
		energy = 1
	end
	if is_bad_food then
		energy = -energy
	end
	return is_food, is_bad_food, energy
end

function uwu.on_rightclick(pos, node, clicker, itemstack)
	if not itemstack or itemstack:is_empty() then
		return itemstack
	end
	local is_food, is_bad_food, energy = uwu.check_food(itemstack)
	if not is_food then
		return itemstack
	end
	local pname = clicker and clicker:is_player() and clicker:get_player_name()
	if not (pname and core.is_creative_enabled(pname)) then
		itemstack:take_item(1)
	end
	uwu.add_happiness(pos, energy)
	uwu.burp(pos, 25)
	return itemstack
end

function uwu.grow(pos)
	local nodename = core.get_node(pos).name
	local newpos = uwu.newpos(pos)
	if nodename == "uwu:block" then
		if newpos then
			core.set_node(newpos, {name = "uwu:little"} )
			uwu.add_happiness(pos, 1-uwu.growhappiness)
			uwu.add_happiness(newpos, 1)
			core.get_node_timer(newpos):start(uwu.timer())
			uwu.say_uwu(pos)
		end
	end
	if nodename == "uwu:little" then
		core.set_node(pos, {name = "uwu:block"} )
		uwu.add_happiness(pos, 1-uwu.growhappiness)
		uwu.say_uwu(pos)
	end
end
