dirs = {{1,0},{0,-1},{-1,0},{0,1}}
ldirs = {
	r = {1,0},
	u = {0,-1},
	l = {-1,0},
	d = {0,1},
}
itemdata = {
	food = 0,
	poop = 1,
	cellfood = 2,
	sugar = 3,
}
structdata = {
	kitchen = 0,
	wc = 1,
	muscle = 2,
	chlorophyll = 3,
	damage = 4,
	mouth = 5,
	designation = 6,
}

jobs =
{
	{
		name = "idle",
		desc = "Prioritize sleep and food",
	},
	{
		name = "dispose",
		desc = "Dispose of waste (requires disposal chute)",
	},
	{
		name = "metab_nutrient",
		desc = "Convert food into sugar (requires metabolizer)",
	},
	{
		name = "metab_cell",
		desc = "Convert food into cell food (requires metabolizer)",
	},
	{
		name = "build",
		desc = "Work on unfinished buildings",
	},
	{
		name = "consume",
		desc = "Convert sugar into energy (requires muscle)",
	},
	{
		name = "defend",
		desc = "Train for attacks and/or defend against threats",
	},
	{
		name = "heal",
		desc = "Heal damaged tissue (requires sugar)",
	},
}
builds =
{
	{
		name = "chlorophyll",
		desc = "Generates slowly more nutrients (1 coins)",
		price = 1,
	},
	{
		name = "kitchen",
		desc = "Turns nutrients into sugar or homunculus food (2 coins)",
		price = 2,
	},
	{
		name = "muscle",
		desc = "Converts sugar into energy (2 coins)",
		price = 2,
	},
	{
		name = "newhom",
		desc = "Get a new homunculus (4 coins)",
		price = 4,
	},
}

CHOSEN = 1
JOB = 2
ICON = 3
XPOS = 4
YPOS = 5
MOVE = 6

NAME = 1
DESC = 3

B_FUNC = 1
B_NAME = 2
B_DESC = 3

B_DISABLED = 1
B_CLICKED = 2
B_CLICKDELAY = 3
B_TARGET = 4

function init()
	tilesize = 8
	
	units = {}
	items = {}
	structs = {}
	maps = {}
	
	entitylist = {}
	
	joblist = {}
	
	energy = 0.0
	poop = 0
	damage = 0
	money = 5
	
	buildbuttons()
end

function addmap(mapid)
	local id = #maps + 1
	maps[id] = mmf.newObject(mapid)
	
	local map = maps[id]
	local w = map.xRight - map.xLeft
	local h = map.yBottom - map.yTop
	map.w = w
	map.h = h
	map.tiles = {}
	local tiles = map.tiles
	
	for i=0,w-1 do
		for j=0,h-1 do
			local c = MF_gettile(mapid,i,j)
			local tid = i + j * w
			
			MF_settile(i,j,0,0)
			
			if (c == 255) then
				tiles[tid] = 255
				MF_settile(i,j,0,1)
			elseif (c == 128) then
				local unitid = MF_create("unit")
				local unit = mmf.newObject(unitid)
				unit.values[XPOS] = i
				unit.values[YPOS] = j
				unit.strings[NAME] = "unit"
				unit.strings[DESC] = "A helpful homunculus"
				addunit(unitid)
			elseif (c == 100) then
				local unitid = MF_create("item")
				local unit = mmf.newObject(unitid)
				unit.values[XPOS] = i
				unit.values[YPOS] = j
				unit.strings[NAME] = "cellfood"
				unit.strings[DESC] = "Food for the homunculi"
				additem(unitid)
			elseif (c == 90) then
				local unitid = MF_create("item")
				local unit = mmf.newObject(unitid)
				unit.values[XPOS] = i
				unit.values[YPOS] = j
				unit.strings[NAME] = "food"
				unit.strings[DESC] = "Unrefined nutrients"
				additem(unitid)
			elseif (c == 110) then
				local unitid = MF_create("struct")
				local unit = mmf.newObject(unitid)
				unit.values[XPOS] = i
				unit.values[YPOS] = j
				unit.strings[NAME] = "wc"
				unit.strings[DESC] = "Put organic waste here"
				addstruct(unitid)
			elseif (c == 120) then
				local unitid = MF_create("struct")
				local unit = mmf.newObject(unitid)
				unit.values[XPOS] = i
				unit.values[YPOS] = j
				unit.strings[NAME] = "kitchen"
				unit.strings[DESC] = "For refining nutrients into either sugar or food for homunculi"
				addstruct(unitid)
			elseif (c == 130) then
				local unitid = MF_create("struct")
				local unit = mmf.newObject(unitid)
				unit.values[XPOS] = i
				unit.values[YPOS] = j
				unit.strings[NAME] = "muscle"
				unit.strings[DESC] = "Load sugar here to convert it into energy"
				addstruct(unitid)
			elseif (c == 140) then
				local unitid = MF_create("struct")
				local unit = mmf.newObject(unitid)
				unit.values[XPOS] = i
				unit.values[YPOS] = j
				unit.strings[NAME] = "chlorophyll"
				unit.strings[DESC] = "Generates nutrients from sunlight"
				addstruct(unitid)
			elseif (c == 150) then
				local unitid = MF_create("struct")
				local unit = mmf.newObject(unitid)
				unit.values[XPOS] = i
				unit.values[YPOS] = j
				unit.strings[NAME] = "damage"
				unit.strings[DESC] = "An open wound - treat immediately!"
				addstruct(unitid)
			elseif (c == 160) then
				MF_settile(i,j,1,0)
			elseif (c == 170) then
				MF_settile(i,j,1,1)
			elseif (c == 180) then
				local unitid = MF_create("struct")
				local unit = mmf.newObject(unitid)
				unit.values[XPOS] = i
				unit.values[YPOS] = j
				unit.strings[NAME] = "mouth"
				unit.strings[DESC] = "Your mouth; nutrients come from here"
				addstruct(unitid)
			end
		end
	end
end

function addunit(unitid)
	local id = #units + 1
	units[id] = mmf.newObject(unitid)
	
	local unit = units[id]
	unit.path = ""
	unit.step = 0
	unit.xtile = math.floor(unit.values[XPOS])
	unit.ytile = math.floor(unit.values[YPOS])
	unit.map = 1
	unit.work = ""
	unit.workstep = 0
	unit.target = 0
	unit.target2 = 0
	unit.reserved = false
	unit.values[JOB] = 1
	unit.wait = 0
	
	unit.hunger = 1000
	unit.energy = 2000
	unit.poop = 0

	addentity(unitid)
	
	unitbuttons()
end

function additem(unitid)
	local id = #items + 1
	items[id] = mmf.newObject(unitid)
	
	local unit = items[id]
	unit.xtile = math.floor(unit.values[XPOS])
	unit.ytile = math.floor(unit.values[YPOS])
	unit.map = 1
	unit.held = 0
	unit.reserved = false

	addentity(unitid)
end

function addstruct(unitid)
	local id = #structs + 1
	structs[id] = mmf.newObject(unitid)
	
	local unit = structs[id]
	unit.xtile = math.floor(unit.values[XPOS])
	unit.ytile = math.floor(unit.values[YPOS])
	unit.map = 1
	unit.target = 0
	unit.wait = 0
	unit.reserved = false
	
	addentity(unitid)
end

function addentity(unitid)
	local unit = mmf.newObject(unitid)
	local name = unit.strings[NAME]
	
	if (entitylist[name] == nil) then
		entitylist[name] = {}
		entitylist[name]["count"] = 0
	end
	
	local data = entitylist[name]
	data.count = data.count + 1
	local id = data.count
	data[id] = unitid
end

function delentity(unitid)
	local unit = mmf.newObject(unitid)
	local name = unit.strings[NAME]
	
	if (entitylist[name] ~= nil) then
		local data = entitylist[name]
		data.count = data.count - 1
		
		for i,v in ipairs(data) do
			if (v == unitid) then
				table.remove(data, i)
			end
		end
		
		if (data.count <= 0) then
			entitylist[name] = nil
		end
	end
end

function step()
	for i,unit in pairs(units) do
		local jobdata = jobs[unit.values[JOB]]
		unit.hunger = unit.hunger - 1
		unit.energy = unit.energy - 1
		unit.values[MOVE] = 0
		
		unit.flags[7] = false
		unit.flags[8] = false
		
		if (unit.hunger < 0) then
			unit.flags[8] = true
		end
		
		if (unit.energy < 0) then
			unit.flags[7] = true
		end
		
		if (unit.poop > 0) then
			unit.poop = unit.poop - 1
		end
		
		if (unit.hunger < -1000) then
			cancelwork(unit.fixed)
			delentity(unit.fixed)
			delunit(unit.fixed)
			unitbuttons()
		end
		
		local food = findfree("cellfood","item")
		local needfood = false
		
		if ((unit.hunger <= 0) and (food ~= 0)) then
			needfood = true
		end
		
		if (needfood == false) and (unit.energy > 0) and (jobdata.name ~= "idle") then
			if (#joblist > 0) and (unit.reserved == false) then
				local yes = false
				
				for a,job in pairs(joblist) do
					if (yes == false) then
						for c,d in ipairs(job.job) do
							if (jobdata.name == d) then
								unit.work = job.name
								unit.workstep = 0
								unit.target = job.target
								unit.target2 = 0
								unit.reserved = true
								
								table.remove(joblist, a)
								yes = true
								break
							end
						end
					end
				end
			end
		elseif (unit.energy <= 0) and (unit.reserved == false) then
			unit.work = "sleep"
			unit.reserved = true
			unit.workstep = 0
		elseif needfood and (unit.reserved == false) then
			local i = mmf.newObject(food)
			i.reserved = true
			unit.target = food
			unit.work = "eat"
			unit.reserved = true
			unit.workstep = 0
		end
		
		if (unit.work ~= "") and (unit.path == "") then
			handlework(unit.work,unit)
		end
		
		if (unit.poop == 1) then
			local newid = MF_create("item")
			local new = mmf.newObject(newid)
			new.values[XPOS] = math.floor(unit.values[XPOS] + 0.2)
			new.values[YPOS] = math.floor(unit.values[YPOS] + 0.2)
			new.strings[NAME] = "poop"
			new.strings[DESC] = "Waste - don't let it accumulate!"
			additem(newid)
		end
		
		if (string.len(unit.path) > 0) and (unit.step > 0) then
			local cdir = string.sub(unit.path, unit.step, unit.step)
			local ox,oy = 0,0

			local drs = dirs[cdir + 1]
			ox = drs[1]
			oy = drs[2]
			
			local xdist = math.abs(unit.xtile + ox - unit.values[XPOS])
			local ydist = math.abs(unit.ytile + oy - unit.values[YPOS])

			if (unit.values[XPOS] < unit.xtile + ox) and (xdist > 0.1) then
				unit.values[XPOS] = unit.values[XPOS] + 0.1
				unit.values[MOVE] = 1
				unit.direction = 0
			elseif (unit.values[XPOS] > unit.xtile + ox) and (xdist > 0.1) then
				unit.values[XPOS] = unit.values[XPOS] - 0.1
				unit.values[MOVE] = 1
				unit.direction = 15
			end
			
			if (unit.values[YPOS] < unit.ytile + oy) and (ydist > 0.1) then
				unit.values[YPOS] = unit.values[YPOS] + 0.1
				unit.values[MOVE] = 1
			elseif (unit.values[YPOS] > unit.ytile + oy) and (ydist > 0.1) then
				unit.values[YPOS] = unit.values[YPOS] - 0.1
				unit.values[MOVE] = 1
			end
			
			if (xdist < 0.2) and (ydist < 0.2) then
				unit.step = unit.step + 1
				unit.xtile = unit.xtile + ox
				unit.ytile = unit.ytile + oy
				
				if (unit.step > string.len(unit.path)) then
					unit.step = 0
					unit.path = ""
				end
			end
		end
		
		local map = maps[unit.map]
		
		unit.x = map.x + unit.values[XPOS] * tilesize + tilesize * 0.5
		unit.y = map.y + unit.values[YPOS] * tilesize + tilesize * 0.5
		
		if unit.flags[7] then
			unit.values[MOVE] = 2
		end
	end
	
	for i,unit in pairs(items) do
		if (unit.held ~= 0) then
			local u = mmf.newObject(unit.held)
			unit.values[XPOS] = u.values[XPOS]
			unit.values[YPOS] = u.values[YPOS]
		end
		
		local map = maps[unit.map]
		unit.x = map.x + unit.values[XPOS] * tilesize + tilesize * 0.5
		unit.y = map.y + unit.values[YPOS] * tilesize + tilesize * 0.5
		
		unit.values[ICON] = itemdata[unit.strings[NAME]]
		unit.xtile = unit.values[XPOS]
		unit.ytile = unit.values[YPOS]
		
		if (unit.strings[NAME] == "poop") then
			local this = findfree("wc","struct")
			
			if (unit.reserved == false) and (this ~= 0) then
				unit.reserved = true
				table.insert(joblist, {name = "dispose", target = unit.fixed, job = {"dispose"}})
			end
		end
	end
	
	for i,unit in pairs(structs) do
		local map = maps[unit.map]
		unit.x = map.x + unit.values[XPOS] * tilesize + tilesize * 0.5
		unit.y = map.y + unit.values[YPOS] * tilesize + tilesize * 0.5
		unit.values[ICON] = structdata[unit.strings[NAME]]
		
		if (unit.strings[NAME] == "kitchen") then
			local this = findfree("food","item")
			
			if (unit.reserved == false) and (this ~= 0) then
				unit.reserved = true
				table.insert(joblist, {name = "metab", target = unit.fixed, job = {"metab_cell","metab_nutrient"}})
			end
		elseif (unit.strings[NAME] == "muscle") then
			local this = findfree("sugar","item")
			
			if (unit.reserved == false) and (this ~= 0) then
				unit.reserved = true
				table.insert(joblist, {name = "consume", target = unit.fixed, job = {"consume"}})
			end
		elseif (unit.strings[NAME] == "chlorophyll") then
			if (unit.wait < 600) then
				unit.wait = unit.wait + 1
			else
				local newid = MF_create("item")
				local new = mmf.newObject(newid)
				new.values[XPOS] = math.floor(unit.values[XPOS] + 0.2)
				new.values[YPOS] = math.floor(unit.values[YPOS] + 0.2)
				new.strings[NAME] = "food"
				additem(newid)
				unit.wait = math.random(-100,50)
			end
		elseif (unit.strings[NAME] == "damage") then
			local this = findfree("sugar","item")
			
			if (unit.reserved == false) and (this ~= 0) then
				unit.reserved = true
				table.insert(joblist, {name = "heal", target = unit.fixed, job = {"heal"}})
			end
		elseif (unit.strings[NAME] == "designation") then
			if (unit.reserved == false) then
				unit.reserved = true
				table.insert(joblist, {name = "build", target = unit.fixed, job = {"build"}})
			end
		end
	end
	
	poop = unitamount("poop","item")
	damage = unitamount("damage","struct")
	
	return energy,poop,damage,money
end

function near(u1,u2)
	local x1,y1 = u1.xtile,u1.ytile
	local x2,y2 = u2.xtile,u2.ytile
	
	local dx = math.abs(x1 - x2)
	local dy = math.abs(y1 - y2)
	
	if (dx == 0) and (dy == 0) then
		return 1
	elseif ((dx == 1) and (dy == 0)) or ((dx == 0) and (dy == 1)) then
		return 2
	elseif (dx <= 1) and (dy <= 1) then
		return 3
	end
	
	return 0
end

function findfree(name,type)
	local result = 0
	
	if (entitylist[name] ~= nil) then
		for i,v in ipairs(entitylist[name]) do
			local s = mmf.newObject(v)
			
			local test = s.target or s.held
			
			if (s.reserved == false) and (test == 0) and (s.className == type) then
				result = v
				break
			end
		end
	end
	
	return result
end

function unitamount(name,type)
	local result = 0
	
	if (entitylist[name] ~= nil) then
		for i,v in ipairs(entitylist[name]) do
			local s = mmf.newObject(v)
			
			if (s.className == type) then
				result = result + 1
			end
		end
	end
	
	return result
end

function nonechosen()
	--MF_removebyname("unitbutton")
	MF_removebyname("jobbutton")
	for i,unit in ipairs(units) do
		unit.values[CHOSEN] = 0
	end
end

function unitbuttons()
	MF_removebyname("unitbutton")
	
	for i,unit in ipairs(units) do
		local bid = MF_create("button")
		local iid = MF_create("icon")
		local tid = MF_create("thought")
		
		local b = mmf.newObject(bid)
		local ic = mmf.newObject(iid)
		local t = mmf.newObject(tid)
		
		b.strings[B_FUNC] = "unit"
		b.strings[B_NAME] = "unitbutton"
		b.strings[B_DESC] = "Select unit"
		b.values[B_TARGET] = i
		
		ic.values[1] = bid
		ic.direction = 1
		ic.strings[B_NAME] = "unitbutton"
		ic.layer = 2
		ic.values[3] = unit.fixed
		
		t.values[1] = bid
		t.strings[B_NAME] = "unitbutton"
		t.layer = 2
		t.values[3] = unit.fixed
		
		b.x = mmf.frame.xLeft + tilesize * 3 + (i - 1) * tilesize * 5
		b.y = tilesize * 3
		b.layer = 2
	end
end

function jobbuttons()
	MF_removebyname("jobbutton")
	
	local width = 4
	
	for i,data in ipairs(jobs) do
		local i_ = i - 1
		local x = i_ % width
		local y = math.floor(i_ / width)
		
		local bid = MF_create("button")
		local iid = MF_create("icon")
		local b = mmf.newObject(bid)
		local ic = mmf.newObject(iid)
		
		b.strings[B_FUNC] = data.name
		b.strings[B_NAME] = "jobbutton"
		b.strings[B_DESC] = data.desc
		
		ic.values[1] = bid
		ic.direction = 1
		ic.values[2] = i - 1
		ic.strings[B_NAME] = "jobbutton"
		ic.layer = 2
		
		b.x = mmf.frame.xLeft + tilesize * 3 + x * tilesize * 5
		b.y = tilesize * 11 + y * tilesize * 5
		b.layer = 2
	end
end

function buildbuttons()
	MF_removebyname("buildbutton")
	
	for i,data in ipairs(builds) do
		local i_ = i - 1
		local x = 640 - tilesize * 25
		local y = tilesize * 13 + i_ * tilesize * 4.5
		
		local bid = MF_create("button")
		local iid = MF_create("icon")
		local b = mmf.newObject(bid)
		local ic = mmf.newObject(iid)
		
		b.strings[B_FUNC] = data.name
		b.strings[B_NAME] = "buildbutton"
		b.strings[B_DESC] = data.desc
		
		ic.values[1] = bid
		ic.direction = 2
		ic.values[2] = i - 1
		ic.strings[B_NAME] = "buildbutton"
		b.values[10] = data.price
		ic.layer = 2
		
		b.x = x
		b.y = y
		b.layer = 2
	end
end

function clicked(bid)
	local b = mmf.newObject(bid)
	
	local f = b.strings[B_NAME]
	
	if (f == "unitbutton") then
		local t = b.values[B_TARGET]
		
		for i,unit in ipairs(units) do
			if (i == t) then
				unit.values[CHOSEN] = 1
				
				jobbuttons(i)
			else
				unit.values[CHOSEN] = 0
			end
		end
	elseif (f == "jobbutton") then
		for c,d in ipairs(jobs) do
			if (d.name == b.strings[B_FUNC]) then
				for i,unit in ipairs(units) do
					if (unit.values[CHOSEN] == 1) then
						cancelwork(unit.fixed)
						unit.values[JOB] = c
					end
				end
			end
		end
	end
end

function cancelwork(unitid)
	local unit = mmf.newObject(unitid)
	
	if (unit.target ~= nil) then
		if (unit.target ~= 0) then
			local hm = mmf.newObject(unit.target)
			hm.reserved = false
			
			if (hm.className == "item") then
				hm.held = 0
				hm.values[XPOS] = math.floor(hm.values[XPOS] + 0.2)
				hm.values[YPOS] = math.floor(hm.values[YPOS] + 0.2)
			elseif (hm.className == "struct") then
				hm.target = 0
			end
		end
	end
	
	if (unit.target2 ~= nil) then
		if (unit.target2 ~= 0) then
			local hm = mmf.newObject(unit.target2)
			hm.reserved = false
			
			if (hm.className == "item") then
				hm.held = 0
				hm.values[XPOS] = math.floor(hm.values[XPOS] + 0.2)
				hm.values[YPOS] = math.floor(hm.values[YPOS] + 0.2)
			elseif (hm.className == "struct") then
				hm.target = 0
			end
		end
	end
	
	unit.wait = 0
	unit.target = 0
	unit.target2 = 0
	unit.reserved = false
	unit.work = ""
	unit.workstep = 0
	unit.path = ""
	unit.step = 0
end

function delunit(unitid)
	cancelwork(unitid)
	
	local unit = mmf.newObject(unitid)
	local name = unit.className
	
	delentity(unitid)
	
	if (name == "unit") then
		for i,u in ipairs(units) do
			if (u.fixed == unitid) then
				table.remove(units, i)
			end
		end
	elseif (name == "item") then
		for i,u in ipairs(items) do
			if (u.fixed == unitid) then
				table.remove(items, i)
			end
		end
	elseif (name == "struct") then
		for i,u in ipairs(structs) do
			if (u.fixed == unitid) then
				table.remove(structs, i)
			end
		end
	end
	
	MF_remove(unitid)
end

function move(id,unitid)
	local unit = mmf.newObject(unitid)
	
	local movspeed = math.min(energy * 3, 8)
	local jumpspeed = (energy + 12) * 0.5
	
	if (energy > 0) then
		if (id == 1) and (unit.values[2] < movspeed) then
			unit.values[2] = math.min(unit.values[2] + 1, movspeed)
			energy = energy - math.abs(unit.values[2]) * 0.01
		elseif (id == -1) and (unit.values[2] > 0 - movspeed) then
			unit.values[2] = math.max(unit.values[2] - 1, 0 - movspeed)
			energy = energy - math.abs(unit.values[2]) * 0.01
		elseif (id == 2) then
			unit.values[3] = 0 - jumpspeed
			energy = energy * 0.75
		end
	end
	
	if (energy < 1.0) then
		energy = 0
	end
	
	return energy
end

function collision(speed)
	local s = math.floor(math.abs(speed) * 0.2)
	local map = maps[1]
	local w = map.w
	local h = map.h
	
	for i=1,s do
		local found = false
		local x,y = 0,0
		
		while (found == false) do
			x = math.random(0, w-1)
			y = math.random(0, h-1)
			
			local c = MF_gettile(map.fixed,x,y)
			
			if (c == 0) then
				found = true
			end
		end
		
		local unitid = MF_create("struct")
		local unit = mmf.newObject(unitid)
		unit.values[XPOS] = x
		unit.values[YPOS] = y
		unit.strings[NAME] = "damage"
		addstruct(unitid)
	end
end

function pickup(itemid)
	local item = mmf.newObject(unitid)
	
	local mid = findfree("mouth","struct")
	local m = mmf.newObject(mid)
	
	for i=1,8 do
		local newid = MF_create("item")
		local new = mmf.newObject(newid)
		new.values[XPOS] = m.values[XPOS] + math.random(-1,1)
		new.values[YPOS] = m.values[YPOS] + math.random(-1,1)
		new.strings[NAME] = "food"
		additem(newid)
	end
end

function addmoney(amount)
	money = money + amount
end

function designate(bid,x,y,price)
	local b = mmf.newObject(bid)
	
	local unitid = MF_create("struct")
	local unit = mmf.newObject(unitid)
	unit.values[XPOS] = x
	unit.values[YPOS] = y
	unit.strings[NAME] = "designation"
	unit.strings[2] = b.strings[B_FUNC]
	addstruct(unitid)

	addmoney(price)
end