2 Commits b218fd71f3 ... 1f0ed1b72c

Author SHA1 Message Date
  Tirifto 1f0ed1b72c Ĝisdatigis ĉefan dosieron 3 years ago
  Tirifto fd1fb0447b Aldonis movbildojn de aventuristo kaj gladiatoro 3 years ago
7 changed files with 916 additions and 89 deletions
  1. 1 2
      art/LICENSE.txt
  2. 193 0
      art/adventurer.lua
  3. BIN
      art/adventurer.png
  4. BIN
      art/dwarf.png
  5. 133 0
      art/gladiator.lua
  6. BIN
      art/gladiator.png
  7. 589 87
      main.lua

+ 1 - 2
art/LICENSE.txt

@@ -1,4 +1,4 @@
-serpento, santa, ice_elemental, and dwarf © Ahmet Avci
+serpento, santa, ice_elemental, gladiator, adventurer and dwarf © Ahmet Avci
 	https://opengameart.org/content/2d-sprites-16x16
 	CC-BY 4.0
 
@@ -6,7 +6,6 @@ winter_birds © Refuzzle
 	https://opengameart.org/content/winter-birds
 	CC 0
 
-
 hit_animations.zip © Viktor Hahn
 	https://opengameart.org/content/pixelated-attackhit-animations
 	CC-BY 4.0

+ 193 - 0
art/adventurer.lua

@@ -0,0 +1,193 @@
+--
+-- LOVE2D ANIMATION
+--
+
+--
+-- This file will be loaded through love.filesystem.load
+-- This file describes the different states and frames of the animation
+--
+
+--[[
+	Each sprite sheet contains one or multiple states
+	Each states is represented as a line in the image file
+	The following object describes the different states
+	Switching between different states can be done through code
+	members ->
+		imageSrc : path to the image (png, tga, bmp or jpg)
+		defaultState : the first state
+		states : a table containing each state
+	(State)
+	Each state contains the following members ->
+		frameCount : the number of frames in the state
+		offsetX : starting from the left, the position (in px) of the first frame of the state (aka on the line)
+		offsetY : starting from the top, the position of the line (px)
+		framwW : the width of each frame in the state
+		frameH : the height of each frame in the state
+		nextState : the state which will follow after the last frame is reached
+		switchDelay : the time between each frame (seconds as floating point)
+]]
+
+-- the return statement is mandatory
+return {
+	imageSrc = "art/adventurer.png",
+	defaultState = "inactiveright",
+	states = {
+		inactiveright = { -- the name of the state is arbitrary
+			frameCount = 13,
+			offsetX = 0,
+			offsetY = 0,
+			frameW = 32,
+			frameH = 32,
+			nextState = "inactiveright", -- we loop the running state
+			switchDelay = 0.1
+		},
+		-- 2nd line
+		walkright = {
+			frameCount = 8,
+			offsetX = 0,
+			offsetY = 32,
+			frameW = 32,
+			frameH = 32, -- the frame height can change between states
+			nextState = "walkright", -- after the jump is finished, we switch back to running
+			switchDelay = 0.1
+		},
+		-- 3rd line
+		jump_attackright = {
+			frameCount = 10,
+			offsetX = 0,
+			offsetY = 64, 
+			frameW = 32,
+			frameH = 32,
+			nextState = "inactiveright",
+			switchDelay = 0.1
+		},
+		-- 4rd line
+		attackright = {
+			frameCount = 10,
+			offsetX = 0,
+			offsetY = 96, 
+			frameW = 32,
+			frameH = 32,
+			nextState = "inactiveright",
+			switchDelay = 0.1
+		},
+		-- 5a linio
+		strangeright = {
+			frameCount = 10,
+			offsetX = 0,
+			offsetY = 128, 
+			frameW = 32,
+			frameH = 32,
+			nextState = "strangeright",
+			switchDelay = 0.1
+		},
+		-- 6a linio
+		jumpright = {
+			frameCount = 6,
+			offsetX = 0,
+			offsetY = 160, 
+			frameW = 32,
+			frameH = 32,
+			nextState = "inactiveright",
+			switchDelay = 0.1
+		},
+		-- 7a linio
+		hurtright = {
+			frameCount = 4,
+			offsetX = 0,
+			offsetY = 192, 
+			frameW = 32,
+			frameH = 32,
+			nextState = "inactiveright",
+			switchDelay = 0.1
+		},
+		-- 8a linio
+		ripright = {
+			frameCount = 7,
+			offsetX = 0,
+			offsetY = 224, 
+			frameW = 32,
+			frameH = 32,
+			switchDelay = 0.1
+		},
+		-- 1st line
+		inactiveleft = { -- the name of the state is arbitrary
+			frameCount = 13,
+			offsetX = 0,
+			offsetY = 256,
+			frameW = 32,
+			frameH = 32,
+			nextState = "inactiveright", -- we loop the running state
+			switchDelay = 0.1
+		},
+		-- 2nd line
+		walkleft = {
+			frameCount = 8,
+			offsetX = 0,
+			offsetY = 288,
+			frameW = 32,
+			frameH = 32, -- the frame height can change between states
+			nextState = "walkleft", -- after the jump is finished, we switch back to running
+			switchDelay = 0.1
+		},
+		-- 3rd line
+		jump_attackleft = {
+			frameCount = 10,
+			offsetX = 0,
+			offsetY = 320, 
+			frameW = 32,
+			frameH = 32,
+			nextState = "inactiveleft",
+			switchDelay = 0.1
+		},
+		-- 4rd line
+		attackleft = {
+			frameCount = 10,
+			offsetX = 0,
+			offsetY = 352, 
+			frameW = 32,
+			frameH = 32,
+			nextState = "inactiveleft",
+			switchDelay = 0.1
+		},
+		-- 5a linio
+		strangeleft = {
+			frameCount = 10,
+			offsetX = 0,
+			offsetY = 384, 
+			frameW = 32,
+			frameH = 32,
+			nextState = "strangeleft",
+			switchDelay = 0.1
+		},
+		-- 6a linio
+		jumpleft = {
+			frameCount = 6,
+			offsetX = 0,
+			offsetY = 416, 
+			frameW = 32,
+			frameH = 32,
+			nextState = "inactiveleft",
+			switchDelay = 0.1
+		},
+		-- 7a linio
+		hurtleft = {
+			frameCount = 4,
+			offsetX = 0,
+			offsetY = 448, 
+			frameW = 32,
+			frameH = 32,
+			nextState = "inactiveleft",
+			switchDelay = 0.1
+		},
+		-- 8a linio
+		ripleft = {
+			frameCount = 7,
+			offsetX = 0,
+			offsetY = 480, 
+			frameW = 32,
+			frameH = 32,
+			switchDelay = 0.1
+		}
+	}
+}

BIN
art/adventurer.png


BIN
art/dwarf.png


+ 133 - 0
art/gladiator.lua

@@ -0,0 +1,133 @@
+--
+-- LOVE2D ANIMATION
+--
+
+--
+-- This file will be loaded through love.filesystem.load
+-- This file describes the different states and frames of the animation
+--
+
+--[[
+	Each sprite sheet contains one or multiple states
+	Each states is represented as a line in the image file
+	The following object describes the different states
+	Switching between different states can be done through code
+	members ->
+		imageSrc : path to the image (png, tga, bmp or jpg)
+		defaultState : the first state
+		states : a table containing each state
+	(State)
+	Each state contains the following members ->
+		frameCount : the number of frames in the state
+		offsetX : starting from the left, the position (in px) of the first frame of the state (aka on the line)
+		offsetY : starting from the top, the position of the line (px)
+		framwW : the width of each frame in the state
+		frameH : the height of each frame in the state
+		nextState : the state which will follow after the last frame is reached
+		switchDelay : the time between each frame (seconds as floating point)
+]]
+
+-- the return statement is mandatory
+return {
+	imageSrc = "art/gladiator.png",
+	defaultState = "inactiveright",
+	states = {
+		inactiveright = { -- the name of the state is arbitrary
+			frameCount = 5,
+			offsetX = 0,
+			offsetY = 0,
+			frameW = 32,
+			frameH = 32,
+			nextState = "inactiveright", -- we loop the running state
+			switchDelay = 0.1
+		},
+		-- 2nd line
+		walkright = {
+			frameCount = 8,
+			offsetX = 0,
+			offsetY = 32,
+			frameW = 32,
+			frameH = 32, -- the frame height can change between states
+			nextState = "walkright", -- after the jump is finished, we switch back to running
+			switchDelay = 0.1
+		},
+		-- 3rd line
+		attackright = {
+			frameCount = 7,
+			offsetX = 0,
+			offsetY = 64, 
+			frameW = 32,
+			frameH = 32,
+			nextState = "inactiveright",
+			switchDelay = 0.1
+		},
+		-- 4rd line
+		hurtright = {
+			frameCount = 3,
+			offsetX = 0,
+			offsetY = 96, 
+			frameW = 32,
+			frameH = 32,
+			nextState = "inactiveright",
+			switchDelay = 0.1
+		},
+		-- 5a linio
+		ripright = {
+			frameCount = 2,
+			offsetX = 0,
+			offsetY = 128, 
+			frameW = 32,
+			frameH = 32,
+			switchDelay = 0.1
+		},
+		-- 1a linio
+		inactiveleft = { -- the name of the state is arbitrary
+			frameCount = 5,
+			offsetX = 0,
+			offsetY = 160,
+			frameW = 32,
+			frameH = 32,
+			nextState = "inactiveleft", -- we loop the running state
+			switchDelay = 0.1
+		},
+		-- 2nd line
+		walkleft = {
+			frameCount = 8,
+			offsetX = 0,
+			offsetY = 192,
+			frameW = 32,
+			frameH = 32, -- the frame height can change between states
+			nextState = "walkleft", -- after the jump is finished, we switch back to running
+			switchDelay = 0.1
+		},
+		-- 3rd line
+		attackleft = {
+			frameCount = 7,
+			offsetX = 0,
+			offsetY = 224, 
+			frameW = 32,
+			frameH = 32,
+			nextState = "inactiveleft",
+			switchDelay = 0.1
+		},
+		-- 4rd line
+		hurtleft = {
+			frameCount = 3,
+			offsetX = 0,
+			offsetY = 256, 
+			frameW = 32,
+			frameH = 32,
+			nextState = "inactiveleft",
+			switchDelay = 0.1
+		},
+		-- 5a linio
+		ripleft = {
+			frameCount = 2,
+			offsetX = 0,
+			offsetY = 288,
+			frameW = 32,
+			frameH = 32,
+			switchDelay = 0.1
+		}
+	}
+}

BIN
art/gladiator.png


+ 589 - 87
main.lua

@@ -4,18 +4,18 @@ local animate = require 'lib/love-animation'
 local i18n = require 'lib/i18n'
 local sti = require 'lib/Simple-Tiled-Implementation/sti'
 
+-- Button values
 LEFT = -4; RIGHT = -3; UP = -2; DOWN = -1
+YES = -5; NO = -6; NUCLEAR = -7
+
 currentRoom = nil -- Just in case. Gets overriden, but juuust in case.
 states = {}
 
 function love.load()
 	love.graphics.setDefaultFilter("nearest", "nearest")
-	a_ttf = love.graphics.newFont("art/font/alagard.ttf", nil, "none")
-	r_ttf = love.graphics.newFont("art/font/romulus.ttf", nil, "none")
-
-	camera = stalker() -- Dependaĵoj: retfilmilo
-	camera:setFollowStyle('TOPDOWN')
-	
+	P_ttf = love.graphics.newFont("art/font/PressStart2P.ttf", nil, "none")
+	p_ttf = love.graphics.newFont("art/font/puntillas.ttf", nil, "none")
+	new camera()
 	game init()
 end
 
@@ -43,6 +43,20 @@ function love.keyreleased(key)
 	state function("keyreleased", key)
 end
 
+function love.resize()
+	local width,height = love.window.getMode()
+	new camera()
+	currentRoom:camera bound()
+end
+
+function new camera()
+	camera = stalker()
+	camera:setFollowStyle('TIGHT_FOLLOW')
+	camera:setFollowLerp(1)
+end
+
+
+
 -- STATES :: SUFOKIĜU :: ℋ𝒜𝒮𝒦ℰℒℒ 𝒞ℛℰ𝒲
 --------------------------------------------------------------------------------
 -- GAME
@@ -71,9 +85,9 @@ function game init()
 	}
 
 	local game mode = { ["functions"] = functions, ["name"] = "game" }
-    add state(game mode)
-    testmenuo = ĉefmenuo()
-    testmenuo:install()
+	add state(game mode)
+	testmenuo = ĉefmenuo()
+	testmenuo:install()
 end
 
 function game update(t)
@@ -104,16 +118,15 @@ Character = class("Character")
 function Character:initialize(sprite file, name)
 	self.name = name
 	self.animation = LoveAnimation.new(sprite file)
-	self.loc = { ["x"]= 0, ["y"] = 0}
+	self.loc = { ["x"] = 0, ["y"] = 0, ["z"] = nil  } --[[Oseble]]--
 	self.width = 32
-	self.height = 32
-    self.z = nil --[[Oseble]]--
+	self.height = 16
 	self.direction = RIGHT
 		-- Right or left― which way the character is facing.
 	self.directionals = { RIGHT = 0, LEFT = 0, UP = 0, DOWN = 0 }
 		-- Directionals show the ascending order of currently pressed buttons.
 	self.walking = false
-    self.walkspeed = 1
+    self.walkspeed = 100
     self.move order = nil
 end
 
@@ -126,8 +139,9 @@ function Character:update(t)
 	self:move to update()
 	self:directionals update()
 	self:animation update(t)
+end
 
-	
+function Character:interact(interacter)
 end
 
 function Character:directionals update()
@@ -164,7 +178,31 @@ function Character:animation update(t)
 	end
 end
 
-function Character:touching wall(direction)
+function Character:touched thing() -- NOTO POR ĴADO: Ĉu ne tro precizas? (K.a. mi korektis.)
+	return currentRoom:get object at("Aĵoj", self.loc.x - 32, self.loc.y - 32)
+end
+
+function Character:touched character()
+	for k,char in pairs(currentRoom.characters) do
+		if (char.name ~= self.name) then
+			if (are colliding(self:bounding box(), char:bounding box()) == true) then
+				return char
+			end
+		end
+	end
+end
+
+function Character:bounding box()
+	local x diff = self.width / 2
+	local y diff = self.height / 2
+	local x1 = self.loc.x - x diff
+	local x2 = self.loc.x + x diff
+	local y1 = self.loc.y - y diff
+	local y2 = self.loc.y + y diff
+	return { ["x1"] = x1, ["x2"] = x2, ["y1"] = y1, ["y2"] = y2 }
+end
+
+function Character:touching wall at(direction)
 	local x = self.loc.x
 	local y = self.loc.y
 	if (direction == UP) then  y = y - self.height
@@ -177,19 +215,19 @@ function Character:touching wall(direction)
 end
 
 function Character:movement(t)
-	local walkspeed = self.walkspeed
+	local walkspeed = self.walkspeed * t
 	local dirs = self.directionals
 
 	if (self.walking == true) then
 		-- actually moving    --  ←←←←←←←←←←←←←←
-		if (dirs[RIGHT] == 1) and (self:touching wall(RIGHT) == false) then
+		if (dirs[RIGHT] == 1) and (self:touching wall at(RIGHT) == false) then
 			self.loc.x = self.loc.x + walkspeed
-		elseif (dirs[LEFT] == 1) and (self:touching wall(LEFT) == false) then
+		elseif (dirs[LEFT] == 1) and (self:touching wall at(LEFT) == false) then
 			self.loc.x = self.loc.x - walkspeed
 		end
-		if (dirs[UP] == 1) and (self:touching wall(UP) == false) then
+		if (dirs[UP] == 1) and (self:touching wall at(UP) == false) then
 			self.loc.y = self.loc.y - walkspeed
-		elseif (dirs[DOWN] == 1) and (self:touching wall(DOWN) == false) then
+		elseif (dirs[DOWN] == 1) and (self:touching wall at(DOWN) == false) then
 			self.loc.y = self.loc.y + walkspeed
 		end
 	end
@@ -204,7 +242,7 @@ function Character:movement(t)
 end
 
 function Character:move to(x, y)
-    self.move order = { ["x"] = x, ["y"] = y}
+    self.move order = { ["x"] = x, ["y"] = y }
 end
 
 function Character:move to update(t)
@@ -244,12 +282,28 @@ Player = class("Player", Character)
 function Player:initialize(sprite file)
 	Character.initialize(self, sprite file, "ludato")
 	self.height = 16
-	self.keymap = { ["left"] = LEFT, ["right"] = RIGHT, ["up"] = UP, ["down"] = DOWN }
+	self.keymap = {
+		["left"] = LEFT, ["right"] = RIGHT, ["up"] = UP, ["down"] = DOWN,
+		["kp4"] = LEFT, ["kp6"] = RIGHT, ["kp8"] = UP, ["kp2"] = DOWN,
+		["a"] = LEFT, ["d"] = RIGHT, ["w"] = UP, ["s"] = DOWN,
+		["a"] = LEFT, ["e"] = RIGHT, [","] = UP, ["o"] = DOWN,
+		["q"] = YES, ["z"] = YES, ["space"] = YES, ["return"] = YES,
+		["j"] = NO, ["x"] = NO, ["backspace"] = NO, ["lctrl"] = NO,
+		["escape"] = NUCLEAR, ["capslock"] = NUCLEAR,
+		}
 	self.actionmap = invert table(self.keymap)
 end
 
 function Player:update(t)
+
+	--print("Player pos:" .. self.loc.x .. "x" .. self.loc.y)
 	Character.update(self, t)
+	local tuŝito = self:touched character()
+	if (tuŝito ~= nil) then
+		local fight = Fight:new({self}, {tuŝito})
+		fight:install()
+	end
+	
 	camera:follow(self.loc.x, self.loc.y)
 end
 
@@ -260,6 +314,21 @@ function Player:keypressed(key)
 		self.direction = dir
 	end
 	
+	if (dir == YES) then -- trying to interact
+		local obj = self:touched thing()
+		local char = self:touched character()
+		if (char ~= nil) then
+			char:interact(self)
+		elseif (obj ~= nil) then
+			obj:interact(self)
+		end
+		return
+	end
+	
+	if (dir == NUCLEAR) then -- you wanna yeet out
+		-- menuo ĉi tie :w:
+	end
+	
 	if (dir == RIGHT) then -- you’re going in the right direction
 		dirs[RIGHT] = 1;	if (dirs[LEFT] == 1) then dirs[LEFT] = 2; end
 	elseif (dir == LEFT) then
@@ -279,15 +348,13 @@ function Player:keyreleased(key)
 	end
 end
 
-
 -- ROOM
 ----------------------------------------
 Room = class("Room")
 
 function Room:initialize(map file, door id)
 	self:load map(map file)
-	
-	local ludato = Player:new("art/dwarf.lua")
+	local ludato = Player:new("art/gladiator.lua")
 	self:install character(ludato, door id)
 	self.things = { }
 end
@@ -330,14 +397,11 @@ end
 
 function Room:install()
 	currentRoom = self
-	local width,height = 0
-	for k,layer in pairs(self.map.layers) do
-		if (layer.width ~= nil) then
-			width,height = self.map:convertTileToPixel(layer.width + 1, layer.height + 2)
-			break
-		end
-	end
-	camera:setBounds(-30, -30, width + 30, height + 30)
+	self:camera bound()
+end
+
+function Room:camera bound()
+	camera:setBounds(-30, -30, self.width + 30, self.height + 30)
 end
 
 function Room:load map(map file)
@@ -347,6 +411,12 @@ function Room:load map(map file)
 	self.bottom layers = {}
 	
 	for k,layer in pairs(self.map.layers) do
+		if (layer.name == "Interfrapeco") then
+			table.insert(self.top layers, layer)
+		end
+		if (layer.width ~=nil) then
+			self.width,self.height = self.map:convertTileToPixel(layer.width + 1, layer.height + 2)
+		end
 		if (type(k) == "number") then
 			local ordo = layer.properties.ordo
 			if (ordo == nil) then
@@ -381,6 +451,11 @@ function Room:install character(character, doorid)
 	character.loc.y = spawn.y
 end
 
+function Room:install thing(thing)
+	if (self.things == nil) then self.things = {} end
+	table.insert(self.things, thing)
+end
+
 function Room:on enter()
 end
 
@@ -400,6 +475,37 @@ function Room:get object(layer, property, value)
 	end
 end
 
+-- Find object in the given layer by location
+function Room:get object at(layer name, x, y)
+	local layer = self.map.layers[layer name]
+	local closest = nil
+	local closeness = nil
+	
+	--[[ -- Tiriftaĵo; ne zorgu!
+	self.map:setObjectCoordinates(layer)
+	for k,v in pairs(layer["objects"][1]) do
+		print(k, v)
+	end
+	print("-----------------------------------")
+	]]
+
+	for k,object in pairs(layer.objects) do
+		--print ("Objekto:  " .. object.x .. "x" .. object.y)
+		local distanco = distance(x, y, object.x, object.y)
+		if (closeness == nil) or (closeness > distanco) then
+			closeness = distanco
+			closest = object
+		end
+	end
+	if (closeness ~= nil) and (closeness < 60) then
+		return closest
+	end
+end
+
+function distance(x1, y1, x2, y2)
+	return math.sqrt((x2-x1)^2 + (y2-y1)^2)
+end
+
 function Room:get tile at(layer, x, y)
 	local layer = self.map.layers[layer].data
 	tileX,tileY = self.map:convertPixelToTile(x , y)
@@ -424,7 +530,11 @@ end
 ---------------------------------------
 Menu = class("Menu")
 function Menu:initialize(x, y, offset_x, offset_y, scale, menuItems)
+	-- x, kie komenciĝas la menuo horizontale
+	-- y, kie komenciĝas la menuo vertikale
 	self.x,self.y = x,y
+	-- offset_x, horizontal distance between option and ">>"
+	-- offset_y, vertical distance between options
 	self.offset_x,self.offset_y = offset_x,offset_y
 	self.options = menuItems
 	self.selected = 1
@@ -435,7 +545,7 @@ function Menu:initialize(x, y, offset_x, offset_y, scale, menuItems)
 	self.keys['down'] = false
 	self.keys['enter'] = false
 
-	self.ttf = r_ttf
+	self.ttf = p_ttf
 end
 
 
@@ -449,19 +559,22 @@ function Menu:install(update, draw, press, release)
     add state({ ["functions"] = functions, ["name"] = "menuo" })
 end
 
+function Menu:uninstall()
+	remove state("menuo")
+end
 
 function Menu:update()
 end
 
-
 function Menu:draw()
 	for i=1,table.maxn(self.options) do
 		local this_y = self.y + (self.offset_y * i)
 
-		love.graphics.draw(self.options[i][1],
-				    self.x, this_y, 0, self.scale, self.scale)
+		love.graphics.draw(
+			love.graphics.newText(p_ttf, self.options[i][1]),
+			self.x, this_y, 0, self.scale, self.scale)
 		if (i == self.selected) then
-			love.graphics.draw(love.graphics.newText(self.ttf, ">>"),
+			love.graphics.draw(love.graphics.newText(p_ttf, ">>"),
 				self.x - self.offset_x, this_y, 0, self.scale, self.scale)
 		end
 	end
@@ -506,6 +619,64 @@ function Menu:keyreleased(key)
 	end
 end
 
+-- MAIN MENU
+-------------------
+Main Menu = class("Main Menu", Menu)
+
+function Main Menu:initialize(menuItems)
+	self:update() -- Gives us self.x and self.y
+	Menu.initialize(self, self.x, self.y, 50, 50, 3, menuItems)
+end
+
+function Main Menu:update() -- Must set self.x and self.y
+	window width, window height = love.graphics.getDimensions()
+	self.x, self.y = (window width - 400) / 2, (window height - 400) / 2
+end
+
+function Main Menu:draw()
+	-- Darken screen
+	window width, window height = love.graphics.getDimensions()
+	love.graphics.setColor(0, 0, 0, 0.5)
+	love.graphics.rectangle("fill",
+	                        0, 0,
+	                        window width, window height)
+	love.graphics.setColor(1, 1, 1)
+	Menu.draw(self)
+end
+
+-- GOURMET MENU
+-------------------
+Gourmet Menu = class("Gourmet Menu", Menu) -- It’s really a “choice menu”…
+
+function Gourmet Menu:initialize(menuItems)
+	self.width  = 400
+	self.height = (6 * 4--[[frame line]]) + (64 * #menuItems)
+	self:update() -- Gives us self.x and self.y
+	Menu.initialize(self, self.x, self.y, 50, 50, 2, menuItems)
+end
+
+function Gourmet Menu:update() -- Must set self.x and self.y
+	window width, window height = love.graphics.getDimensions()
+	self.x = 64
+	self.y = (window height - self.height) / 2
+	self.width = window width - 128
+end
+
+function Gourmet Menu:draw()
+	draw frame(self.x - (frame line * 3),
+	          self.y - (frame line * 2),
+			  self.width  + (frame line * 6),
+			  self.height + (frame line * 4))
+	Menu.draw(self)
+end
+
+function Gourmet Menu:keypressed(key)
+	if (key == "return" or key == "space") then
+		self:uninstall()
+	end
+	Menu.keypressed(self, key)
+end
+
 -- DIALOGUE
 ----------------------------------------
 Dialogue = class("Dialogue")
@@ -518,13 +689,18 @@ end
 function Dialogue:advance()
 	if (self.progress >= table.maxn(self.messages)) then
 		self.progress = 1
-		self:close()
+		self:uninstall()
 	else
 		self.progress = self.progress + 1
+		-- ASSUMING “DO” WILL BE ALONE EVER ONLY
+		if (self.messages[self.progress].act ~= nil) then
+			self.messages[self.progress].act()
+			self:advance()
+		end
 	end
 end
 
-function Dialogue:open()
+function Dialogue:install()
 	local functions = {
 		["update"] = { ["func"] = function (t) self:update(t) end,
 		               ["combine"] = false },
@@ -538,7 +714,7 @@ function Dialogue:open()
 	add state({ ["functions"] = functions, ["name"] = "interparolo" })
 end
 
-function Dialogue:close()
+function Dialogue:uninstall()
 	remove state("interparolo")
 end
 
@@ -551,27 +727,37 @@ function Dialogue:draw()
 	-- wife = getRandomWife()
 	-- wife.husband:die()
 	-- widow width, widow height = wife.graphics.getDimensions()
+    -- oh noooooo xD
 	
-	-- We draw outer rectangle as a cool frame to inner rectangle. B-)
-	out rec width, out rec height = window width - 32, window height / 2 - 32
-	in rec width, in rec height = out rec width - 32, out rec height - 32
-	textbox width, textbox height = in rec width - 16, in rec height - 16
+	msg frame width = window width - 32
+	msg frame height = window height / 2 - 32
+	msg frame x = 16
+	msg frame y = window height / 2 + 16
+	msg width = (msg frame width - (frame line * 3)) / 2
+		-- Ni duonigas, ĉar ni duobligos grandecon de teksto (kaj ties skatolo).
 	
-	love.graphics.setColor(1, 1, 1)
-	love.graphics.rectangle("fill",
-	                        16, window height / 2 + 16,
-	                        out rec width, out rec height)
-
-	love.graphics.setColor(0.4, 0.1, 0.1)
-	love.graphics.rectangle("fill",
-	                        32, window height / 2 + 32,
-	                        in rec width, in rec height)
+	name frame width = 306
+	name frame height = 64
+	name frame x = msg frame x + 48
+	name frame y = msg frame y - name frame height + frame line -- to merge borders.
+	name width = (name frame width - (frame line * 3)) / 2
+		-- Ni duonigas, ĉar ni duobligos grandecon de teksto (kaj ties skatolo).
 	
+	draw frame(msg frame x, msg frame y,
+	          msg frame width, msg frame height)
+	draw frame(name frame x, name frame y,
+	          name frame width, name frame height)
+	
+	love.graphics.printf(self.messages[self.progress].msg, p_ttf,
+	                     msg frame x + (frame line * 3),
+						 msg frame y + (frame line * 2),
+						 msg width, "left", 0, 2, 2)
+	love.graphics.setColor(1, 1, 0.5)
+	love.graphics.printf(self.messages[self.progress].name, p_ttf,
+	                     name frame x + (frame line * 3),
+						 name frame y + (frame line * 2),
+						 name width, "left", 0, 2, 2)
 	love.graphics.setColor(1, 1, 1)
-
-	love.graphics.printf(self.messages[self.progress],
-	                     40, window height / 2 + 40,
-						 textbox width, "left")
 end
 
 function Dialogue:keypressed(key)
@@ -590,16 +776,14 @@ end
 ----------------------------------------
 Thing = class("Thing")
 
-function Thing:initialize()
-    self.things = { }
-    self.name = nil
+function Thing:initialize(name)
+	self.name = name
 end
 
-function Thing:interact()
+function Thing:interact(interacter)
+	print("owo")
 end
 
-
-
 -- Install the important 'hook' functions (draw, update, keypressed/released)
 -- If any of the 'old' functions passed are not nil, then both the new and
 -- old will be added into the new corresponding hook function
@@ -665,30 +849,178 @@ perl -ine 's/stat/reĝim' 'menso de Ĵado.cpp'
 -- INTERPAROLOJ
 -------------------
 prova interparolo = Dialogue:new({
-	"Brittle are your feathers, vitreous duck, vitreous duck.",
-	"Won’t you try and fly over the vale?",
-	"Yes, if you walked, you’d be caught and meet your end for sure. "
-	.. "And if you fall, your fate won’t be of those any batter than that.",
-	"But maybe, just maybe your wings can carry you across this valley, "
-	.. "and perhaps there you can finally be happy, "
-	.. "vitreous duck, vitreous duck."
+	{name = "La profeta pingveno",
+	 msg  = "Brittle are your feathers, vitreous duck, vitreous duck."},
+	{name = "La profeta pingveno",
+	 msg  = "Won’t you try and fly over the vale?"},
+	{name = "La profeta pingveno",
+	 msg  = "Yes, if you walked, you’d be caught and meet your end for sure. "
+	     .. "And if you fall, your fate won’t be of those any better than that."},
+	{name = "La profeta pingveno",
+	 msg  = "But maybe, just maybe your wings can carry you across this valley, "
+	     .. "and perhaps there you can finally be happy, "
+	     .. "vitreous duck, vitreous duck."},
+	{name = "La profeta pingveno",
+	 msg  = "Will you take this flight?"},
+	{act  = function() prova elekto:install() end}
 })
 
+prova elekto = Gourmet Menu:new({
+	{ "Yes",
+	  function() prova elekto jes:install() end },
+	{ "Who the $%!@ are you?!",
+	  function() prova elekto kiu:install() end },
+	{ "No",
+	  function() prova elekto ne:install() end }
+})
+
+prova elekto jes = Dialogue:new({
+	{name = "Vitreca anaso",
+	 msg  = "Yeah, I might as well give it a try. Quack quack."},
+	{name = "La profeta pingveno",
+	 msg  = "May the winds caress you ever so gently, "
+	        .. "vitreous duck, vitreous duck."}
+})
+
+prova elekto kiu = Dialogue:new({
+	{name = "Vitreca anaso",
+	 msg  = "Who the $%!@ are you?! Like, even? Quack quack."},
+	{name = "La profeta pingveno",
+	 msg  = "Let’s see…"},
+	{name = "La profeta pingveno",
+	 msg  = "“La profeta pingveno”, apparently."},
+	{name = "Vitreca anaso",
+	 msg  = "What the $%!@ did you just $%!@ing say about me, you little $%!@? "
+	        .. "I’ll have you know I graduated top of my class in the Navy "
+			.. "Ducks, and I’ve been involved in numerous secret raids on "
+			.. "Al-Quackeda, and I have over 69 confirmed quacks. Quack quack."}
+})
+
+prova elekto ne = Dialogue:new({
+	{name = "Vitreca anaso",
+	 msg  = "Nah, that’s like, way up high, dude. Quack quack."},
+	{name = "La profeta pingveno",
+	 msg  = "git gud lol" },
+	{name = "La profeta pingveno",
+	 msg  = "…vitreous duck, vitreous duck."}
+})
+
+-- Kun malŝtopilo
+---------
+--[[--
+Dialogue:new({
+	{name = "Johan Mojos",
+	 msg  = "This is a plunger we own."},
+	{name = "Johan Mojos",
+	 msg  = "It is quite the plunger, honestly! Big and sturdy, I like it. "
+	        .. "It could probably pull an elf right out of a toilet!"},
+	{name = "Johan Mojos",
+	 msg  = "Not like I’m trying to drop any hints here. "
+	        .. "Just kinda thought it would be good for that sorta thing. "
+			.. "Y’know. Hypothetically speaking."},
+	{act  = function () elekto:install() end}
+})
+
+elekto = Gourmet Menu:new({
+	{ "Take the plunger",
+	  function() elekto jes:install() end },
+	{ "Don’t take the plunger",
+	  function() elekto ne:install() end },
+	{ "Plunge into death’s embrace",
+	  function() elekto morti:install() end }
+})
+
+elekto morti = Dialogue:new({
+	{name = "Johan Mojos",
+	 msg  = "I don’t plan on just sitting around in here until we run out of "
+	        .. "food and starve to death. Desperate times call for desperate "
+			.. "actions!"},
+	{name = "Johan Mojos",
+	 msg  = "If I’m going down, I may as well go down with all the style and "
+	        .. "grandeur worthy of both me and this plunger right here."},
+	{name = "Johan Mojos",
+	 msg  = "Behold! Let the world marvel at my final stunt!"},
+	{name = "Narrator",
+	 msg  = "On saying that, Johan firmly grasped the plunger with both of "
+	        .. "his hands, and unclogged himself in a manner too graphic "
+			.. "to be put into words suited to anyone’s eyes."},
+	{name = "Narrator",
+	 msg  = "Please do NOT try this at home. Ever."},
+})
+
+-- Kun duŝangulo
+------------
+Dialogue:new({
+	{name = "Johan Mojos",
+	 msg  = "This is our shower stall. We take our showers in there."},
+	{name = "Johan Mojos",
+	 msg  = "Right now, it looks like there’s a serial killer hiding in there, "
+	        .. "so I don’t really want to go inside."},
+})
+
+Dialogue:new({
+	{name = "Johan Mojos",
+	 msg  = "No, seriously. I’m not going in there. There’s no way it’s going to "
+	        .. "end well for me."}
+})
+
+Dialogue:new({
+	{name = "Johan Mojos",
+	 msg  = "What would you do if there was a serial killer hiding in your "
+	        .. "stall, huh? I bet you wouldn’t want to join per, either."}
+})
+
+Dialogue:new({
+	{name = "Johan Mojos",
+	 msg  = "In case you’re wondering why don’t we run or call the police… "
+	        .. "well, in case you haven’t noticed, we’re completely snowed up "
+			.. "in here!"}
+})
+
+Dialogue:new({
+	{name = "Johan Mojos",
+	 msg  = "I mean, the killer does look pretty harmless like this. As long "
+	        .. "as we don’t open the stall, we should be perfectly fine!"}
+})
+
+Dialogue:new({
+	{name = "Johan Mojos",
+	 msg  = "You know what? Maybe I will open the stall. It doesn’t seem like "
+	        .. "such a terrible idea, all things considered."}
+})
+
+Dialogue:new({
+	{name = "Johan Mojos",
+	 msg  = "Yeah! What’s the worst that could possibly happen?"},
+	{name = "Narrator",
+	 msg  = "The worst that could possibly happen was being murdered in cold "
+	        .. "blood by the serial killer. That is also exactly what happened."},
+	{name = "Narrator",
+	 msg  = "On commiting that atrocious crime, the serial killer went into "
+	        .. "the kitchen, made perself a cup of tea, and drank it all whilst "
+			.. "relishing the newly gained tranquility of the place."},
+	{name = "Narrator",
+	 msg  = "The nerve!"}
+})
+
+--]]--
+
 
 -- MENUOJ
 -------------------
 function ĉefmenuo()
-    return Menu:new(100, 100, 30, 50, 3, {
-        {love.graphics.newText(a_ttf, "Presi mojosan tekston"),
-            function () print "ANAS~ROMPO-DANCO~ANAS" end},
-		{love.graphics.newText(a_ttf, "Presi malmojosan tekston"),
-		    function () print "Mia nacio multe pli bonas ol la via."; end },
-		{love.graphics.newText(a_ttf, "Fini la mondon"),
-			function () love.event.quit(0) end },
-		{love.graphics.newText(a_ttf, "Provi interparolon"),
-            function () remove state("menuo"); prova interparolo:open(); end },
-        {love.graphics.newText(a_ttf, "Fermi la menuon"),
-            function () remove state("menuo"); end }})
+    return Main Menu:new({
+        { "Presi mojosan tekston",
+          function () print "ANAS~ROMPO-DANCO~ANAS" end },
+		{ "Presi malmojosan tekston",
+		  function () print "Mia nacio multe pli bonas ol la via."; end },
+		{ "Fini la mondon",
+		  function () love.event.quit(0) end },
+		{ "Provi interparolon",
+          function () remove state("menuo"); prova interparolo:install(); end },
+        { "Fermi la menuon",
+          function () remove state("menuo"); end }
+	})
 end
 
 -- ĈAMBROJ
@@ -697,11 +1029,138 @@ Domo = class("Domo", Room)
 
 function Domo:initialize(door id)
 	Room.initialize(self, "maps/Domo.lua", door id)
+	self:install character(Character:new("art/dwarf.lua", "fremdulo"))
+	self:install thing(Thing:new("malŝtopilo"))
+end
+
+-- BATALSISTEMO :bear: :bee: :sweat: :100: 
+--------------------------------------------------------------------------------
+-- BATALO
+-------------------
+Fight = class("Fight")
+--
+--                                            | Johan Mojos
+--                                            | Mojos Koĥek
+--                                            | 
+--       o                          O
+--      -2     1      0      1     2
+--
+--    [o]     [o]
+--      |        |
+--      |      [O]
+--    [O]      |          3
+--
+
+function Fight:initialize(friends, enemies, friend side, enemy side)
+	self.fighters = {}
+	local amikflanko = -2
+	local malamikflanko = 2
+	if (friend side ~= nil) and (friend side == RIGHT) then
+		amikflanko = 2
+		malamikflanko = -2
+	end
 	
-	local fremdulo = Character:new("art/dwarf.lua", "fremdulo")
-	self:install character(fremdulo)
+	new camera()
+
+	for k,player in pairs(friends) do
+		local fighter = nil
+		if (player.name == "ludato") then
+			fighter = PlayerFighter:new(player, self, amikflanko)
+		else
+			fighter = Fighter:new(player, self, amikflanko)
+		end
+		self:install fighter(fighter)
+	end
+	for k,cpu in pairs(enemies) do
+		local fighter = Fighter:new(cpu, self, malamikflanko)
+		self:install fighter(fighter)
+	end
 end
 
+-- install state  (A) :-(
+function Fight:install()
+	local functions = {
+		["update"] = { ["func"] = function (t) self:update(t) end, ["combine"] = false },
+		["draw"] = { ["func"] = function (t) self:draw() end, ["combine"] = false }
+	}
+	add state({["functions"] = functions, ["name"] = "batalo"})
+	camera = stalker() -- Dependaĵoj: retfilmilo
+	camera:setFollowStyle('TOPDOWN')
+end 
+
+-- -2 -1 0 1 2
+-- 13 ←→
+-- ?? mistere ↑↓ ? :w:
+
+
+-- [-2 ] [-1 ] [ 0 ] [ 1 ] [ 2 ]
+
+--              A Ŭ
+
+-- [-2 ] [-1 ] [ 0 ] [ 1 ] [ 2 ]  - vico 1
+-- [-2 ] [-1 ] [ 0 ] [ 1 ] [ 2 ]  - vico 2
+-- [-2 ] [-1 ] [ 0 ] [ 1 ] [ 2 ]  - vico 3
+
+function Fight:install fighter(fighter)
+	if (self.fighters == nil) then self.fighters = {} end
+	print("INSTALANTE")
+	table.insert(self.fighters, fighter)
+end
+
+function Fight:update(t)
+	camera:update(t)
+	for k,fighter in pairs(self.fighters) do
+		print(k)
+		fighter:update(t)
+	end
+end
+
+function Fight:draw()
+	for k,fighter in pairs(self.fighters) do			
+		fighter:draw()
+	end
+end
+
+
+-- BATALANTO -- o/\o Saluton!
+-------------------
+Fighter = class("Fighter")
+
+function Fighter:initialize(character, fight, location)
+	self.animation = LoveAnimation.new("art/dwarf.lua")
+	self.name = character.name
+	self.loc = location
+	self.fight = fight
+end
+
+function Fighter:update(t)
+	print("Mi ĝisdatiĝas, kaj ", self.name, " loko estas", calc battle position(self.loc))
+	self.animation:setPosition(calc battle position(self.loc), 100)
+	self.animation:update(t)
+end
+
+function Fighter:draw()
+	self.animation:draw(2,2)
+end
+
+-- Takes a relative location on battlefield (-2 to 2), returns a screen position
+function calc battle position(location)
+	return (location + 3) * 20
+end
+
+PlayerFighter = class("PlayerFighter", Fighter)
+
+function PlayerFighter:initialize(character, fight, location)
+	self.name = "ludato"
+	Fighter.initialize(self, character, fight, location)
+end
+
+function PlayerFighter:update(t)
+	Fighter.update(self, t)
+	print("OWOWOWOWO")
+	camera:follow(calc battle position(self.loc), 100)
+end 
+
 -- STATE MANAGEMENT -- State or mode tho
 --------------------------------------------------------------------------------
 -- Vi ne komprenos la teĥnikan signifon
@@ -726,7 +1185,7 @@ end
 function state function(func name, parameter)
 	local buffer o func = {}
 	for index,state in pairs(states) do
-		if (state.functions[func name].func ~= nil) then
+		if (state.functions[func name] ~= nil) and (state.functions[func name].func ~= nil) then
 			insert element(buffer o func, state.functions[func name].func, 1)
 			if state.functions[func name].combine == false then break ; end
 		end
@@ -750,7 +1209,6 @@ function remove state(target name) -- Ⓐ
 	end
 end
 
-
 -- UTIL
 --------------------------------------------------------------------------------
 function remove element(tabelo, index)
@@ -780,6 +1238,14 @@ function is valid direction(number)
 	return false
 end
 
+function opposite direction(direction)
+    if (direction == UP) then return DOWN
+    elseif (direction == DOWN) then return UP
+    elseif (direction == LEFT) then return RIGHT
+    elseif (direction == RIGHT) then return LEFT
+    end
+end
+
 -- Replace the keys with the values, and vice versa
 function invert table(table)
 	local newtable = {}
@@ -798,5 +1264,41 @@ function table.clone(org)
 end
 
 function round(number)
+	return love.graphics.circle() -- Perfectly round.
+end
+
+-- box = { x1, y1, x2, y2 }
+-- B) B) B) B)B)B)B)B)B))B
+ 
+ -- /\(•v•)/\ =B> =B> =B>
+ 
+function are colliding(box1, box2)
+	return is colliding(box1, box2) or is colliding(box2, box1)
+end
+ 
+function is colliding(box1, box2)
+	local x interfrapo = ( -- Ĉu dekstra rando enas AŬ ĉu maldekstra?
+		(box1.x2 >= box2.x1 and box1.x2 <= box2.x2) or
+		(box1.x1 >= box2.x1 and box1.x1 <= box2.x2)
+	)
+	local y interfrapo = ( -- Ĉu supra rando enas AŬ ĉu malsupra?
+		(box1.y1 >= box2.y1 and box1.y1 <= box2.y2) or
+		(box1.y2 >= box2.y1 and box1.y2 <= box2.y2)
+	)
+	return (x interfrapo and y interfrapo)
+end
+
+frame line --[[the width thereof]] = 6
 
-end
+function draw frame(x, y, width, height)
+	love.graphics.setColor(1, 1, 1)
+	love.graphics.rectangle("fill",
+	                        x, y,
+	                        width, height)
+		-- This is for the outline; next we draw the inner rectangle.
+	love.graphics.setColor(0.4, 0.1, 0.1)
+	love.graphics.rectangle("fill",
+	                        x + frame line, y + frame line,
+	                        width - (frame line * 2), height - (frame line * 2))
+	love.graphics.setColor(1, 1, 1)
+end