From 2a2678afc331b4df3ff73115ba815a92aa3fae78 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sat, 28 Jul 2018 01:39:24 +0200 Subject: [PATCH 01/93] =?UTF-8?q?Change=20target=20L=C3=96VE=20version=20t?= =?UTF-8?q?o=2011.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- conf.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf.lua b/conf.lua index 797345da..c661ce89 100644 --- a/conf.lua +++ b/conf.lua @@ -4,7 +4,7 @@ local PROJECT_IDENTITY = "rmcode_otr" local PROJECT_VERSION = require( 'version' ) -local LOVE_VERSION = "11.0" +local LOVE_VERSION = "11.1" --- -- Initialise löve's config file. From 93708653d35609084e9a714d6575640e71bb4833 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sat, 28 Jul 2018 18:07:19 +0200 Subject: [PATCH 02/93] Fix crash when winning the game The game still tried to return to the base menu, which doesn't exist anymore. Fixes #23. --- src/ui/screens/GameOverScreen.lua | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/ui/screens/GameOverScreen.lua b/src/ui/screens/GameOverScreen.lua index 91cfe18a..8de04cd1 100644 --- a/src/ui/screens/GameOverScreen.lua +++ b/src/ui/screens/GameOverScreen.lua @@ -82,12 +82,7 @@ function GameOverScreen:draw() end function GameOverScreen:keypressed() - if self.win then - ScreenManager.pop() - ScreenManager.push( 'base', self.playerFaction ) - else - ScreenManager.switch( 'mainmenu' ) - end + ScreenManager.switch( 'mainmenu' ) end return GameOverScreen From 78ac442324ee343cb5d7691abdfd5b912eac526d Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Tue, 31 Jul 2018 20:43:19 +0200 Subject: [PATCH 03/93] Use class to assemble and draw ASCII titles --- res/text/en_EN/ui_text.lua | 51 ++++++++++++++++++++++- src/ui/elements/UIMenuTitle.lua | 67 +++++++++++++++++++++++++++++++ src/ui/screens/MainMenu.lua | 59 ++------------------------- src/ui/screens/OptionsScreen.lua | 52 ++---------------------- src/ui/screens/SavegameScreen.lua | 47 ++-------------------- 5 files changed, 126 insertions(+), 150 deletions(-) create mode 100644 src/ui/elements/UIMenuTitle.lua diff --git a/res/text/en_EN/ui_text.lua b/res/text/en_EN/ui_text.lua index c7650e24..af448137 100644 --- a/res/text/en_EN/ui_text.lua +++ b/res/text/en_EN/ui_text.lua @@ -109,7 +109,56 @@ locale.strings = { ['ui_ingame_resume'] = "Resume", -- Help Screen - ['ui_help_header'] = 'Help' + ['ui_help_header'] = 'Help', + + -- Titles + ['ui_title_main_menu'] = { + " @@@@ @@ @@ @@@@@@@ @@@ @@@ @@@@@@@ ", + " @@@@@@@@ @@@ @@@ @@@@@@@ @@@ @@@ @@@@@@@@ ", + " @@! @@@ @@!@ @@@ @@! @@! @@@ @@! ", + " !@! @!@ !@!!@!@! !@! !@! @!@ !@! ", + " @!@ !@! @!@ !!@! @!! @!@!@!@! @!!!:! ", + " !@! !!! !@! !!! !!! !!!@!!!! !!!!!: ", + " !!: !!! !!: !!! !!: !!: !!! !!: ", + " :!: !:! :!: !:! :!: :!: !:! :!: ", + " :!:::!!: :: :: :: :: !: ::!::!! ", + " :!:: : : : : : :!:::::! ", + " ", + "@@@@@@@ @@@@ @@@@@@ @@@@@@ @@@@@ @@@ @@@@@@@ @@@@@@@ ", + "@@@@@@@@ @@@@@@@@ @@@@@@@@ @@@@@@@@ @@@@@@@ @@@ @@@@@@@@ @@@@@@@@", + "@@! @@@ @@! @@@ @@! @@@ @@! @@@ !@@ @@! @@! @@@ @@! ", + "!@! @!@ !@! @!@ !@! @!@ !@! @!@ !@! !@! !@! @!@ !@! ", + "@!@!!@! @!@ !@! @!@!@!@! @!@ !@! !!@@!! !!@ @!@ !@! @!!!:! ", + "!!@!@! !@! !!! !!!@!!!! !@! !!! !!@!!! !!! !@! !!! !!!!!: ", + "!!: :!! !!: !!! !!: !!! !!: !!! !:! !!: !!: !!! !!: ", + ":!: !:! :!: !:! :!: !:! :!: !:! !:! :!: :!: !:! :!: ", + " :: !: ::!:!!:: :: :: !:.:.::: ::!:::: :: !:!!::.: ::!:.:: ", + " ! : ::!: ! : ::::..: :::.. : ::..:.: ::..::.:", + }, + ['ui_title_options'] = { + " @@@@ @@@@@@@ @@@@@@@ @@@ @@@@ @@ @@ @@@@@ ", + "@@@@@@@@ @@@@@@@@ @@@@@@@ @@@ @@@@@@@@ @@@ @@@ @@@@@@@ ", + "@@! @@@ @@! @@@ @@! @@! @@! @@@ @@!@ @@@ !@@ ", + "!@! @!@ !@! @!@ !@! !@! !@! @!@ !@!!@!@! !@! ", + "@!@ !@! @!@@!@! @!! !!@ @!@ !@! @!@ !!@! !!@@!! ", + "!@! !!! !!@!!! !!! !!! !@! !!! !@! !!! !!@!!! ", + "!!: !!! !!: !!: !!: !!: !!! !!: !!! !:!", + ":!: !:! :!: :!: :!: :!: !:! :!: !:! !:! ", + ":!:::!!: :: :: :: :!:::!!: :: :: ::!:::: ", + " :!:: : : : :!:: : : :::.. " + }, + ['ui_title_savegames'] = { + " @@@@@ @@@@@@ @@@ @@@ @@@@@@@ @@@@@ ", + "@@@@@@@ @@@@@@@@ @@@ @@@ @@@@@@@@ @@@@@@@ ", + "!@@ @@! @@@ @@! @@@ @@! !@@ ", + "!@! !@! @!@ !@! @!@ !@! !@! ", + "!!@@!! @!@!@!@! @!@ !@! @!!!:! !!@@!! ", + " !!@!!! !!!@!!!! !@! !!! !!!!!: !!@!!! ", + " !:! !!: !!! :!: !!: !!: !:!", + " !:! :!: !:! ::!!:: :!: !:! ", + "::!:::: :: :: !::: ::!::!! ::!:::: ", + " :::.. ! : !: :!:::::! :::.. " + } } return locale; diff --git a/src/ui/elements/UIMenuTitle.lua b/src/ui/elements/UIMenuTitle.lua new file mode 100644 index 00000000..1a34c4c0 --- /dev/null +++ b/src/ui/elements/UIMenuTitle.lua @@ -0,0 +1,67 @@ +--- +-- Takes care of drawing the large ASCII titles used in different menus. +-- @module UIMenuTitle +-- + +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ + +local Class = require( 'lib.Middleclass' ) +local TexturePacks = require( 'src.ui.texturepacks.TexturePacks' ) +local GridHelper = require( 'src.util.GridHelper' ) + +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ + +local UIMenuTitle = Class( 'UIMenuTitle' ) + +-- ------------------------------------------------ +-- Private Functions +-- ------------------------------------------------ + +--- +-- Iterates over the title definition and creates a text object containing the +-- colored lines. +-- @tparam table titleDefinition The table containing all lines to be processed. +-- @treturn Text The drawable text object containing the assembled title. +-- +local function createTitle( titleDefinition ) + local font = TexturePacks.getFont():get() + local title = love.graphics.newText( font ) + for i, line in ipairs( titleDefinition ) do + local coloredtext = {} + for w in string.gmatch( line, '.' ) do + if w == '@' then + coloredtext[#coloredtext + 1] = TexturePacks.getColor( 'ui_title_1' ) + coloredtext[#coloredtext + 1] = 'O' + elseif w == '!' then + coloredtext[#coloredtext + 1] = TexturePacks.getColor( 'ui_title_2' ) + coloredtext[#coloredtext + 1] = w + else + coloredtext[#coloredtext + 1] = TexturePacks.getColor( 'ui_title_3' ) + coloredtext[#coloredtext + 1] = w + end + title:add( coloredtext, 0, i * font:getHeight() ) + end + end + return title +end + +-- ------------------------------------------------ +-- Constructor +-- ------------------------------------------------ + +function UIMenuTitle:initialize( titleDefinition, y ) + self.title = createTitle( titleDefinition ) + self.y = y +end + +function UIMenuTitle:draw() + local cx, _ = GridHelper.centerElement( GridHelper.pixelsToGrid( self.title:getWidth(), 0 )) + local tw, _ = TexturePacks.getTileDimensions() + love.graphics.draw( self.title, cx * tw, self.y * TexturePacks.getFont():getGlyphHeight() ) +end + +return UIMenuTitle diff --git a/src/ui/screens/MainMenu.lua b/src/ui/screens/MainMenu.lua index 26ab2b1d..b9702af5 100644 --- a/src/ui/screens/MainMenu.lua +++ b/src/ui/screens/MainMenu.lua @@ -12,6 +12,7 @@ local GridHelper = require( 'src.util.GridHelper' ) local Translator = require( 'src.util.Translator' ) local UIContainer = require( 'src.ui.elements.UIContainer' ) local Settings = require( 'src.Settings' ) +local UIMenuTitle = require( 'src.ui.elements.UIMenuTitle' ) -- ------------------------------------------------ -- Module @@ -24,30 +25,6 @@ local MainMenu = Screen:subclass( 'MainMenu' ) -- ------------------------------------------------ local TITLE_POSITION = 2 -local TITLE_STRING = { - " @@@@ @@ @@ @@@@@@@ @@@ @@@ @@@@@@@ ", - " @@@@@@@@ @@@ @@@ @@@@@@@ @@@ @@@ @@@@@@@@ ", - " @@! @@@ @@!@ @@@ @@! @@! @@@ @@! ", - " !@! @!@ !@!!@!@! !@! !@! @!@ !@! ", - " @!@ !@! @!@ !!@! @!! @!@!@!@! @!!!:! ", - " !@! !!! !@! !!! !!! !!!@!!!! !!!!!: ", - " !!: !!! !!: !!! !!: !!: !!! !!: ", - " :!: !:! :!: !:! :!: :!: !:! :!: ", - " :!:::!!: :: :: :: :: !: ::!::!! ", - " :!:: : : : : : :!:::::! ", - " ", - "@@@@@@@ @@@@ @@@@@@ @@@@@@ @@@@@ @@@ @@@@@@@ @@@@@@@ ", - "@@@@@@@@ @@@@@@@@ @@@@@@@@ @@@@@@@@ @@@@@@@ @@@ @@@@@@@@ @@@@@@@@", - "@@! @@@ @@! @@@ @@! @@@ @@! @@@ !@@ @@! @@! @@@ @@! ", - "!@! @!@ !@! @!@ !@! @!@ !@! @!@ !@! !@! !@! @!@ !@! ", - "@!@!!@! @!@ !@! @!@!@!@! @!@ !@! !!@@!! !!@ @!@ !@! @!!!:! ", - "!!@!@! !@! !!! !!!@!!!! !@! !!! !!@!!! !!! !@! !!! !!!!!: ", - "!!: :!! !!: !!! !!: !!! !!: !!! !:! !!: !!: !!! !!: ", - ":!: !:! :!: !:! :!: !:! :!: !:! !:! :!: :!: !:! :!: ", - " :: !: ::!:!!:: :: :: !:.:.::: ::!:::: :: !:!!::.: ::!:.:: ", - " ! : ::!: ! : ::::..: :::.. : ::..:.: ::..::.:", -} - local BUTTON_LIST_WIDTH = 60 local BUTTON_LIST_Y = 20 @@ -55,36 +32,6 @@ local BUTTON_LIST_Y = 20 -- Private Functions -- ------------------------------------------------ -local function createTitle() - local font = TexturePacks.getFont() - local title = love.graphics.newText( font:get() ) - - for i, line in ipairs( TITLE_STRING ) do - local coloredtext = {} - for w in string.gmatch( line, '.' ) do - if w == '@' then - coloredtext[#coloredtext + 1] = TexturePacks.getColor( 'ui_title_1' ) - coloredtext[#coloredtext + 1] = 'O' - elseif w == '!' then - coloredtext[#coloredtext + 1] = TexturePacks.getColor( 'ui_title_2' ) - coloredtext[#coloredtext + 1] = w - else - coloredtext[#coloredtext + 1] = TexturePacks.getColor( 'ui_title_3' ) - coloredtext[#coloredtext + 1] = w - end - title:add( coloredtext, 0, i * font:get():getHeight() ) - end - end - - return title -end - -local function drawTitle( title ) - local cx, _ = GridHelper.centerElement( GridHelper.pixelsToGrid( title:getWidth(), title:getHeight() * #TITLE_STRING )) - local tw, _ = TexturePacks.getTileDimensions() - love.graphics.draw( title, cx * tw, TITLE_POSITION * TexturePacks.getFont():getGlyphHeight() ) -end - local function drawDebugInfo( debug ) local font = TexturePacks.getFont() if debug then @@ -134,7 +81,7 @@ end function MainMenu:initialize() love.mouse.setVisible( false ) - self.title = createTitle() + self.title = UIMenuTitle( Translator.getText( 'ui_title_main_menu' ), TITLE_POSITION ) self.buttonList = createButtons() self.container = UIContainer() @@ -149,7 +96,7 @@ function MainMenu:draw() local font = TexturePacks.getFont() font:use() - drawTitle( self.title ) + self.title:draw() self.container:draw() diff --git a/src/ui/screens/OptionsScreen.lua b/src/ui/screens/OptionsScreen.lua index 04aaa19e..cf7d7e22 100644 --- a/src/ui/screens/OptionsScreen.lua +++ b/src/ui/screens/OptionsScreen.lua @@ -19,6 +19,7 @@ local UISelectField = require( 'src.ui.elements.UISelectField' ) local GridHelper = require( 'src.util.GridHelper' ) local Settings = require( 'src.Settings' ) local UIContainer = require( 'src.ui.elements.UIContainer' ) +local UIMenuTitle = require( 'src.ui.elements.UIMenuTitle' ) -- ------------------------------------------------ -- Module @@ -31,19 +32,6 @@ local OptionsScreen = Screen:subclass( 'OptionsScreen' ) -- ------------------------------------------------ local TITLE_POSITION = 2 -local TITLE_STRING = { - " @@@@ @@@@@@@ @@@@@@@ @@@ @@@@ @@ @@ @@@@@ ", - "@@@@@@@@ @@@@@@@@ @@@@@@@ @@@ @@@@@@@@ @@@ @@@ @@@@@@@ ", - "@@! @@@ @@! @@@ @@! @@! @@! @@@ @@!@ @@@ !@@ ", - "!@! @!@ !@! @!@ !@! !@! !@! @!@ !@!!@!@! !@! ", - "@!@ !@! @!@@!@! @!! !!@ @!@ !@! @!@ !!@! !!@@!! ", - "!@! !!! !!@!!! !!! !!! !@! !!! !@! !!! !!@!!! ", - "!!: !!! !!: !!: !!: !!: !!! !!: !!! !:!", - ":!: !:! :!: :!: :!: :!: !:! :!: !:! !:! ", - ":!:::!!: :: :: :: :!:::!!: :: :: ::!:::: ", - " :!:: : : : :!:: : : :::.. " -} - local BUTTON_LIST_WIDTH = 20 local BUTTON_LIST_Y = 20 @@ -51,40 +39,6 @@ local BUTTON_LIST_Y = 20 -- Private Functions -- ------------------------------------------------ ---- --- Creates the ASCII title at the top of the page. --- -local function createTitle() - local font = TexturePacks.getFont():get() - local title = love.graphics.newText( font ) - for i, line in ipairs( TITLE_STRING ) do - local coloredtext = {} - for w in string.gmatch( line, '.' ) do - if w == '@' then - coloredtext[#coloredtext + 1] = TexturePacks.getColor( 'ui_title_1' ) - coloredtext[#coloredtext + 1] = 'O' - elseif w == '!' then - coloredtext[#coloredtext + 1] = TexturePacks.getColor( 'ui_title_2' ) - coloredtext[#coloredtext + 1] = w - else - coloredtext[#coloredtext + 1] = TexturePacks.getColor( 'ui_title_3' ) - coloredtext[#coloredtext + 1] = w - end - title:add( coloredtext, 0, i * font:getHeight() ) - end - end - return title -end - ---- --- Draws the ASCII title at the top of the page at a grid aligned position. --- -local function drawTitle( title ) - local cx, _ = GridHelper.centerElement( GridHelper.pixelsToGrid( title:getWidth(), title:getHeight() * #TITLE_STRING )) - local tw, _ = TexturePacks.getTileDimensions() - love.graphics.draw( title, cx * tw, TITLE_POSITION * TexturePacks.getFont():getGlyphHeight() ) -end - --- -- Closes the OptionsScreen and displays a confirmation dialog if any -- settings have been changed. @@ -389,7 +343,7 @@ end function OptionsScreen:initialize() Settings.load() - self.title = createTitle() + self.title = UIMenuTitle( Translator.getText( 'ui_title_options' ), TITLE_POSITION ) self.buttonList = createUIList() self.container = UIContainer() @@ -409,7 +363,7 @@ end -- Draws the OptionsScreen. -- function OptionsScreen:draw() - drawTitle( self.title ) + self.title:draw() self.container:draw() diff --git a/src/ui/screens/SavegameScreen.lua b/src/ui/screens/SavegameScreen.lua index e8e57814..91c02793 100644 --- a/src/ui/screens/SavegameScreen.lua +++ b/src/ui/screens/SavegameScreen.lua @@ -9,7 +9,6 @@ local Screen = require( 'src.ui.screens.Screen' ) local Translator = require( 'src.util.Translator' ) local ScreenManager = require( 'lib.screenmanager.ScreenManager' ) -local TexturePacks = require( 'src.ui.texturepacks.TexturePacks' ) local SaveHandler = require( 'src.SaveHandler' ) local UICopyrightFooter = require( 'src.ui.elements.UICopyrightFooter' ) local UIVerticalList = require( 'src.ui.elements.lists.UIVerticalList' ) @@ -17,6 +16,7 @@ local UIButton = require( 'src.ui.elements.UIButton' ) local GridHelper = require( 'src.util.GridHelper' ) local UIContainer = require( 'src.ui.elements.UIContainer' ) local Util = require( 'src.util.Util' ) +local UIMenuTitle = require( 'src.ui.elements.UIMenuTitle' ) -- ------------------------------------------------ -- Module @@ -29,19 +29,6 @@ local SavegameScreen = Screen:subclass( 'SavegameScreen' ) -- ------------------------------------------------ local TITLE_POSITION = 2 -local TITLE_STRING = { - " @@@@@ @@@@@@ @@@ @@@ @@@@@@@ @@@@@ ", - "@@@@@@@ @@@@@@@@ @@@ @@@ @@@@@@@@ @@@@@@@ ", - "!@@ @@! @@@ @@! @@@ @@! !@@ ", - "!@! !@! @!@ !@! @!@ !@! !@! ", - "!!@@!! @!@!@!@! @!@ !@! @!!!:! !!@@!! ", - " !!@!!! !!!@!!!! !@! !!! !!!!!: !!@!!! ", - " !:! !!: !!! :!: !!: !!: !:!", - " !:! :!: !:! ::!!:: :!: !:! ", - "::!:::: :: :: !::: ::!::!! ::!:::: ", - " :::.. ! : !: :!:::::! :::.. " -} - local BUTTON_LIST_WIDTH = 20 local BUTTON_LIST_Y = 20 @@ -49,34 +36,6 @@ local BUTTON_LIST_Y = 20 -- Private Functions -- ------------------------------------------------ -local function createTitle() - local font = TexturePacks.getFont():get() - local title = love.graphics.newText( font ) - for i, line in ipairs( TITLE_STRING ) do - local coloredtext = {} - for w in string.gmatch( line, '.' ) do - if w == '@' then - coloredtext[#coloredtext + 1] = TexturePacks.getColor( 'ui_title_1' ) - coloredtext[#coloredtext + 1] = 'O' - elseif w == '!' then - coloredtext[#coloredtext + 1] = TexturePacks.getColor( 'ui_title_2' ) - coloredtext[#coloredtext + 1] = w - else - coloredtext[#coloredtext + 1] = TexturePacks.getColor( 'ui_title_3' ) - coloredtext[#coloredtext + 1] = w - end - title:add( coloredtext, 0, i * font:getHeight() ) - end - end - return title -end - -local function drawTitle( title ) - local cx, _ = GridHelper.centerElement( GridHelper.pixelsToGrid( title:getWidth(), title:getHeight() * #TITLE_STRING )) - local tw, _ = TexturePacks.getTileDimensions() - love.graphics.draw( title, cx * tw, TITLE_POSITION * TexturePacks.getFont():getGlyphHeight() ) -end - local function createBackButton( lx, ly ) local function callback() ScreenManager.switch( 'mainmenu' ) @@ -149,7 +108,7 @@ end -- ------------------------------------------------ function SavegameScreen:initialize() - self.title = createTitle() + self.title = UIMenuTitle( Translator.getText( 'ui_title_savegames'), TITLE_POSITION ) self.buttonList = createButtons() self.container = UIContainer() @@ -163,7 +122,7 @@ function SavegameScreen:update() end function SavegameScreen:draw() - drawTitle( self.title ) + self.title:draw() self.container:draw() self.footer:draw() end From 69974266bb7ece300b8c64788e8e525952e7f545 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Tue, 31 Jul 2018 21:48:42 +0200 Subject: [PATCH 04/93] Add function to draw a UIElement's bounding box --- src/ui/elements/UIElement.lua | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/ui/elements/UIElement.lua b/src/ui/elements/UIElement.lua index b1d0a26c..9b6db97a 100644 --- a/src/ui/elements/UIElement.lua +++ b/src/ui/elements/UIElement.lua @@ -154,4 +154,18 @@ function UIElement:hasFocus() return self.focus end +--- +-- Draws the bounding boxes of the UIElement and all of its children as white +-- rectangles for debugging purposes. +-- @tparam number tw The game's tile width. +-- @tparam number th The game's tile height. +-- +function UIElement:drawBoundingBox( tw, th ) + love.graphics.rectangle( 'line', self.ax * tw, self.ay * th, self.w * tw, self.h * th ) + + for i = 1, #self.children do + self.children[i]:drawBoundingBox( tw, tw ) + end +end + return UIElement From dcd63a947ffaccab97148c082d565570a176d125 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Wed, 1 Aug 2018 23:31:40 +0200 Subject: [PATCH 05/93] Move UserInterface to ui/elements subfolder --- src/ui/{ => elements}/UserInterface.lua | 0 src/ui/screens/CombatScreen.lua | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename src/ui/{ => elements}/UserInterface.lua (100%) diff --git a/src/ui/UserInterface.lua b/src/ui/elements/UserInterface.lua similarity index 100% rename from src/ui/UserInterface.lua rename to src/ui/elements/UserInterface.lua diff --git a/src/ui/screens/CombatScreen.lua b/src/ui/screens/CombatScreen.lua index 4b4f5321..25d0ced1 100644 --- a/src/ui/screens/CombatScreen.lua +++ b/src/ui/screens/CombatScreen.lua @@ -7,7 +7,7 @@ local Screen = require( 'src.ui.screens.Screen' ) local CombatState = require( 'src.CombatState' ) local MapPainter = require( 'src.ui.MapPainter' ) local Camera = require( 'src.ui.Camera' ) -local UserInterface = require( 'src.ui.UserInterface' ) +local UserInterface = require( 'src.ui.elements.UserInterface' ) local OverlayPainter = require( 'src.ui.overlays.OverlayPainter' ) local TexturePacks = require( 'src.ui.texturepacks.TexturePacks' ) local Settings = require( 'src.Settings' ) From ce2a801d10622718c9750770b251c8e1a9b99cd8 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Wed, 1 Aug 2018 23:43:34 +0200 Subject: [PATCH 06/93] Add title for keybinding screen --- res/text/en_EN/ui_text.lua | 12 ++++++++++++ src/ui/screens/KeybindingScreen.lua | 5 +++++ 2 files changed, 17 insertions(+) diff --git a/res/text/en_EN/ui_text.lua b/res/text/en_EN/ui_text.lua index af448137..125bec65 100644 --- a/res/text/en_EN/ui_text.lua +++ b/res/text/en_EN/ui_text.lua @@ -158,6 +158,18 @@ locale.strings = { " !:! :!: !:! ::!!:: :!: !:! ", "::!:::: :: :: !::: ::!::!! ::!:::: ", " :::.. ! : !: :!:::::! :::.. " + }, + ['ui_title_controls'] = { + " @@@@@ @@@@ @@ @@ @@@@@@@ @@@@@@@ @@@@ @@@ @@@@@ ", + "@@@@@@@@ @@@@@@@@ @@@ @@@ @@@@@@@ @@@@@@@@ @@@@@@@@ @@@ @@@@@@@ ", + "@@! @@! @@@ @@!@ @@@ @@! @@! @@@ @@! @@@ @@! !@@ ", + "!@! !@! @!@ !@!!@!@! !@! !@! @!@ !@! @!@ !@! !@! ", + "@!@ @!@ !@! @!@ !!@! @!! @!@!!@! @!@ !@! @!@ !!@@!! ", + "!@! !@! !!! !@! !!! !!! !!@!@! !@! !!! !@! !!@!!! ", + "!!: !!: !!! !!: !!! !!: !!: :!! !!: !!! !!: !:!", + ":!: :!: !:! :!: !:! :!: :!: !:! :!: !:! :!: !:! ", + ":!:::!! :!:::!!: :: :: :: :: !: :!:::!!: :!:::!! ::!:::: ", + " ::!::!: :!:: : : : ! : :!:: !::!::!: :::.. " } } diff --git a/src/ui/screens/KeybindingScreen.lua b/src/ui/screens/KeybindingScreen.lua index f0f1339c..0914cec3 100644 --- a/src/ui/screens/KeybindingScreen.lua +++ b/src/ui/screens/KeybindingScreen.lua @@ -16,6 +16,7 @@ local UIContainer = require( 'src.ui.elements.UIContainer' ) local UIButton = require( 'src.ui.elements.UIButton' ) local UICopyrightFooter = require( 'src.ui.elements.UICopyrightFooter' ) local Util = require( 'src.util.Util' ) +local UIMenuTitle = require( 'src.ui.elements.UIMenuTitle' ) -- ------------------------------------------------ -- Module @@ -27,6 +28,7 @@ local KeybindingScreen = Screen:subclass( 'KeybindingScreen' ) -- Constants -- ------------------------------------------------ +local TITLE_POSITION = 2 local BUTTON_LIST_WIDTH = 20 local BUTTON_LIST_Y = 20 @@ -157,6 +159,8 @@ function KeybindingScreen:initialize() self.container:register( self.buttonList ) self.footer = UICopyrightFooter() + + self.title = UIMenuTitle( Translator.getText( 'ui_title_controls' ), TITLE_POSITION ) end --- @@ -172,6 +176,7 @@ end function KeybindingScreen:draw() self.container:draw() self.footer:draw() + self.title:draw() end --- From 72af0d8968c01b6c3b2782abfc6f808253937f2e Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sat, 4 Aug 2018 01:27:57 +0200 Subject: [PATCH 07/93] Forward new "presses" parameter to ScreenManager --- lib/screenmanager/ScreenManager.lua | 8 ++++---- main.lua | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/screenmanager/ScreenManager.lua b/lib/screenmanager/ScreenManager.lua index ca87891c..4220f927 100644 --- a/lib/screenmanager/ScreenManager.lua +++ b/lib/screenmanager/ScreenManager.lua @@ -339,8 +339,8 @@ end -- @param istouch (boolean) True if the mouse button press originated from a -- touchscreen touch-press. -- -function ScreenManager.mousepressed( x, y, button, istouch ) - ScreenManager.peek():mousepressed( x, y, button, istouch ) +function ScreenManager.mousepressed( x, y, button, istouch, presses ) + ScreenManager.peek():mousepressed( x, y, button, istouch, presses ) end --- @@ -354,8 +354,8 @@ end -- @param istouch (boolean) True if the mouse button release originated from a -- touchscreen touch-release. -- -function ScreenManager.mousereleased( x, y, button, istouch ) - ScreenManager.peek():mousereleased( x, y, button, istouch ) +function ScreenManager.mousereleased( x, y, button, istouch, presses ) + ScreenManager.peek():mousereleased( x, y, button, istouch, presses ) end --- diff --git a/main.lua b/main.lua index 03f6457b..a7208802 100644 --- a/main.lua +++ b/main.lua @@ -147,12 +147,12 @@ function love.textinput( text ) ScreenManager.textinput( text ) end -function love.mousepressed( mx, my, button, isTouch ) - ScreenManager.mousepressed( mx, my, button, isTouch ) +function love.mousepressed( mx, my, button, isTouch, presses ) + ScreenManager.mousepressed( mx, my, button, isTouch, presses ) end -function love.mousereleased( mx, my, button, isTouch ) - ScreenManager.mousereleased( mx, my, button, isTouch ) +function love.mousereleased( mx, my, button, isTouch, presses ) + ScreenManager.mousereleased( mx, my, button, isTouch, presses ) end function love.mousefocus( f ) From cbea83f22b6837e4cb7a9b1bc1ee655564f32e83 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 6 Aug 2018 00:26:08 +0200 Subject: [PATCH 08/93] Use proper icon colors in prefab menu Tile and WorldObject selectors now use the proper colors for each object which should help with selecting the right tile. Closes #242. --- src/ui/elements/UIButton.lua | 23 +++++++++++++++++++---- src/ui/screens/PrefabEditor.lua | 4 ++++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/ui/elements/UIButton.lua b/src/ui/elements/UIButton.lua index dd05daa9..62c89027 100644 --- a/src/ui/elements/UIButton.lua +++ b/src/ui/elements/UIButton.lua @@ -37,6 +37,18 @@ local function selectColor( self ) end end +local function drawIcon( px, py, icon, iconColorID ) + if not icon then + return + end + + if iconColorID then + TexturePacks.setColor( iconColorID ) + end + + love.graphics.draw( TexturePacks.getTileset():getSpritesheet(), icon, px, py ) +end + -- ------------------------------------------------ -- Public Methods -- ------------------------------------------------ @@ -54,10 +66,6 @@ function UIButton:draw() local tw, th = TexturePacks.getTileDimensions() TexturePacks.setColor( selectColor( self )) - -- Draw icon. - if self.icon then - love.graphics.draw( TexturePacks.getTileset():getSpritesheet(), self.icon, self.ax * tw, self.ay * th ) - end -- Draw text. if self.text then @@ -71,6 +79,9 @@ function UIButton:draw() love.graphics.print( self.text, self.ax * tw + w, self.ay * th ) end end + + drawIcon( self.ax * tw, self.ay * th, self.icon, self.iconColorID ) + TexturePacks.resetColor() end @@ -97,6 +108,10 @@ function UIButton:setIcon( id, alt ) self.icon = TexturePacks.getSprite( id, alt ) end +function UIButton:setIconColorID( color ) + self.iconColorID = color +end + function UIButton:setActive( active ) self.active = active end diff --git a/src/ui/screens/PrefabEditor.lua b/src/ui/screens/PrefabEditor.lua index 3434bec0..f3646443 100644 --- a/src/ui/screens/PrefabEditor.lua +++ b/src/ui/screens/PrefabEditor.lua @@ -49,6 +49,8 @@ local function createTileSelector( tileTemplates, tool ) local tmp = UIButton( lx, ly, 0, counter, SELECTOR_WIDTH, 1, callback, Translator.getText( id ), 'left' ) tmp:setIcon( id ) + tmp:setIconColorID( id ) + tileSelector:addChild( tmp ) counter = counter + 1 @@ -75,6 +77,8 @@ local function createWorldObjectSelector( objectTemplates, tool ) else tmp:setIcon( id ) end + tmp:setIconColorID( id ) + objectSelector:addChild( tmp ) counter = counter + 1 From 4a2f9f982dfc416c28b5cbfb0766cf5c78de8e6e Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 6 Aug 2018 23:09:45 +0200 Subject: [PATCH 09/93] Use keybinding layouts for different modes This helps us to add different keybinding layouts for different states of the game (e.g. combat and mapeditor layouts can have the same keys). --- src/CombatState.lua | 2 +- src/Settings.lua | 79 ++++++++++++++++------------- src/ui/screens/CombatScreen.lua | 4 +- src/ui/screens/HelpScreen.lua | 2 +- src/ui/screens/KeybindingModal.lua | 6 ++- src/ui/screens/KeybindingScreen.lua | 52 +++++++++---------- src/ui/screens/MapTest.lua | 4 +- 7 files changed, 79 insertions(+), 70 deletions(-) diff --git a/src/CombatState.lua b/src/CombatState.lua index a74bc32c..d5a67d95 100644 --- a/src/CombatState.lua +++ b/src/CombatState.lua @@ -155,7 +155,7 @@ function CombatState:keypressed( _, scancode, _ ) if self.factions:getFaction():isAIControlled() or self.stateManager:blocksInput() then return end - self.stateManager:input( Settings.mapInput( scancode )) + self.stateManager:input( Settings.mapInput( Settings.INPUTLAYOUTS.COMBAT, scancode )) end function CombatState:mousepressed( mx, my, button ) diff --git a/src/Settings.lua b/src/Settings.lua index b88edd96..f2fbb36d 100644 --- a/src/Settings.lua +++ b/src/Settings.lua @@ -21,7 +21,7 @@ local Settings = {} local FILE_NAME = 'settings.otr' local DEFAULT_SETTINGS = { - version = 5, + version = 6, general = { fullscreen = true, locale = 'en_EN', @@ -31,29 +31,34 @@ local DEFAULT_SETTINGS = { invertedMessageLog = false, }, controls = { - ['x'] = 'action_stand', - ['c'] = 'action_crouch', - ['v'] = 'action_prone', - ['r'] = 'action_reload_weapon', - ['.'] = 'next_weapon_mode', - [','] = 'prev_weapon_mode', - ['m'] = 'movement_mode', - ['a'] = 'attack_mode', - ['e'] = 'interaction_mode', - ['tab'] = 'next_character', - ['lshift'] = 'prev_character', - ['return'] = 'end_turn', - ['i'] = 'open_inventory_screen', - ['h'] = 'open_health_screen', - ['left'] = 'pan_camera_left', - ['right'] = 'pan_camera_right', - ['up'] = 'pan_camera_up', - ['down'] = 'pan_camera_down' + combat = { + ['x'] = 'action_stand', + ['c'] = 'action_crouch', + ['v'] = 'action_prone', + ['r'] = 'action_reload_weapon', + ['.'] = 'next_weapon_mode', + [','] = 'prev_weapon_mode', + ['m'] = 'movement_mode', + ['a'] = 'attack_mode', + ['e'] = 'interaction_mode', + ['tab'] = 'next_character', + ['lshift'] = 'prev_character', + ['return'] = 'end_turn', + ['i'] = 'open_inventory_screen', + ['h'] = 'open_health_screen', + ['left'] = 'pan_camera_left', + ['right'] = 'pan_camera_right', + ['up'] = 'pan_camera_up', + ['down'] = 'pan_camera_down' + } } } local WARNING_TEXT = 'Replacing outdated settings file (v%d) with current default settings (v%d)!' +Settings.INPUTLAYOUTS = {} +Settings.INPUTLAYOUTS.COMBAT = 'combat' + -- ------------------------------------------------ -- Private Variables -- ------------------------------------------------ @@ -112,8 +117,8 @@ end --- -- Maps a scancode to a control action. -- -function Settings.mapInput( scancode ) - return settings.controls[scancode] +function Settings.mapInput( mode, scancode ) + return settings.controls[mode][scancode] end -- ------------------------------------------------ @@ -128,8 +133,8 @@ function Settings.getIngameEditor() return settings.general.mapeditor end -function Settings.getKeybinding( saction ) - for scancode, action in pairs( settings.controls ) do +function Settings.getKeybinding( mode, saction ) + for scancode, action in pairs( settings.controls[mode] ) do if action == saction then return love.keyboard.getKeyFromScancode( scancode ) end @@ -160,19 +165,21 @@ end -- function Settings.hasChanged() local oldSettings = Compressor.load( FILE_NAME ) - for section, content in pairs( oldSettings ) do - if type( content ) == 'table' then - for key, value in pairs( content ) do - if settings[section][key] ~= value then - return true - end - end - else - if settings[section] ~= content then + + for section, content in pairs( oldSettings.general ) do + if settings.general[section] ~= content then + return true + end + end + + for layout, content in pairs( oldSettings.controls ) do + for key, value in pairs( content ) do + if settings.controls[layout][key] ~= value then return true end end end + return false end @@ -204,11 +211,11 @@ function Settings.setTexturepack( ntexturepack ) settings.general.texturepack = ntexturepack end -function Settings.setKeybinding( scancode, saction ) - for oldscancode, action in pairs( settings.controls ) do +function Settings.setKeybinding( mode, scancode, saction ) + for oldscancode, action in pairs( settings.controls[mode] ) do if action == saction then - settings.controls[oldscancode] = nil - settings.controls[scancode] = action + settings.controls[mode][oldscancode] = nil + settings.controls[mode][scancode] = action return end end diff --git a/src/ui/screens/CombatScreen.lua b/src/ui/screens/CombatScreen.lua index 25d0ced1..750f5e0b 100644 --- a/src/ui/screens/CombatScreen.lua +++ b/src/ui/screens/CombatScreen.lua @@ -69,11 +69,11 @@ function CombatScreen:keypressed( key, scancode, isrepeat ) end self.combatState:keypressed( key, scancode, isrepeat ) - self.camera:input( Settings.mapInput( scancode ), true ) + self.camera:input( Settings.mapInput( Settings.INPUTLAYOUTS.COMBAT, scancode ), true ) end function CombatScreen:keyreleased( _, scancode ) - self.camera:input( Settings.mapInput( scancode ), false ) + self.camera:input( Settings.mapInput( Settings.INPUTLAYOUTS.COMBAT, scancode ), false ) end function CombatScreen:mousepressed( _, _, button ) diff --git a/src/ui/screens/HelpScreen.lua b/src/ui/screens/HelpScreen.lua index 16fc6540..df5f8868 100644 --- a/src/ui/screens/HelpScreen.lua +++ b/src/ui/screens/HelpScreen.lua @@ -84,7 +84,7 @@ local function assembleText() for j = 1, #HELP_TEXT[i].children do offset = offset + 1 if type( HELP_TEXT[i].children[j] ) == 'table' then - text:addf( Settings.getKeybinding( HELP_TEXT[i].children[j][1] ), (UI_GRID_WIDTH-2) * tw, 'left', 4*tw, offset * th ) + text:addf( Settings.getKeybinding( Settings.INPUTLAYOUTS.COMBAT, HELP_TEXT[i].children[j][1] ), (UI_GRID_WIDTH-2) * tw, 'left', 4*tw, offset * th ) text:addf( Translator.getText( HELP_TEXT[i].children[j][1] ), (UI_GRID_WIDTH-2) * tw, 'left', 10*tw, offset * th ) else text:addf( HELP_TEXT[i].children[j], (UI_GRID_WIDTH-2) * tw, 'left', 4*tw, offset * th ) diff --git a/src/ui/screens/KeybindingModal.lua b/src/ui/screens/KeybindingModal.lua index e28c8b95..8e23e35a 100644 --- a/src/ui/screens/KeybindingModal.lua +++ b/src/ui/screens/KeybindingModal.lua @@ -63,9 +63,11 @@ end --- -- Initialises the KeybindingModal. +-- @tparam string mode -- @tparam string action -- -function KeybindingModal:initialize( action ) +function KeybindingModal:initialize( mode, action ) + self.mode = mode self.action = action self.x, self.y = GridHelper.centerElement( UI_GRID_WIDTH, UI_GRID_HEIGHT ) @@ -97,7 +99,7 @@ function KeybindingModal:keypressed( _, scancode ) return end - ScreenManager.publish( 'CHANGED_KEYBINDING', scancode, self.action ) + ScreenManager.publish( 'CHANGED_KEYBINDING', scancode, self.mode, self.action ) ScreenManager.pop() end diff --git a/src/ui/screens/KeybindingScreen.lua b/src/ui/screens/KeybindingScreen.lua index 0914cec3..121f1464 100644 --- a/src/ui/screens/KeybindingScreen.lua +++ b/src/ui/screens/KeybindingScreen.lua @@ -29,29 +29,29 @@ local KeybindingScreen = Screen:subclass( 'KeybindingScreen' ) -- ------------------------------------------------ local TITLE_POSITION = 2 -local BUTTON_LIST_WIDTH = 20 +local BUTTON_LIST_WIDTH = 25 local BUTTON_LIST_Y = 20 -- ------------------------------------------------ -- Private Methods -- ------------------------------------------------ -local function createKeybinding( lx, ly, action ) +local function createKeybinding( lx, ly, mode, action ) -- The function to call when the button is activated. local function callback() - ScreenManager.push( 'keybindingmodal', action ) + ScreenManager.push( 'keybindingmodal', mode, action ) end -- Get the text representation for each key and a proper name for the action. - local keyText = Settings.getKeybinding( action ) - local actionText = Translator.getText( action ) + local keyText = Settings.getKeybinding( mode, action ) + local actionText = string.format( '[%s] %s:', mode, Translator.getText( action )) -- Pad the action text with whitespace and place the key text at the end. -- This allows us to align the action name on the left and the key on the -- right. The width is determined by the button list's width which is -- multiplied by two because a character in the font is half the size of -- a grid space. - local text = Util.rightPadString( actionText .. ':', BUTTON_LIST_WIDTH * 2 - #keyText, ' ' ) .. keyText + local text = Util.rightPadString( actionText, BUTTON_LIST_WIDTH * 2 - #keyText, ' ' ) .. keyText -- Create the UIButton. return UIButton( lx, ly, 0, 0, BUTTON_LIST_WIDTH, 1, callback, text, 'left' ) @@ -124,24 +124,24 @@ local function createUIList() local buttonList = UIVerticalList( lx, ly, 0, 0, BUTTON_LIST_WIDTH, 1 ) -- Create the UIElements and add them to the list. - buttonList:addChild( createKeybinding( lx, ly, 'action_stand' )) - buttonList:addChild( createKeybinding( lx, ly, 'action_crouch' )) - buttonList:addChild( createKeybinding( lx, ly, 'action_prone' )) - buttonList:addChild( createKeybinding( lx, ly, 'action_reload_weapon' )) - buttonList:addChild( createKeybinding( lx, ly, 'next_weapon_mode' )) - buttonList:addChild( createKeybinding( lx, ly, 'prev_weapon_mode' )) - buttonList:addChild( createKeybinding( lx, ly, 'movement_mode' )) - buttonList:addChild( createKeybinding( lx, ly, 'attack_mode' )) - buttonList:addChild( createKeybinding( lx, ly, 'interaction_mode' )) - buttonList:addChild( createKeybinding( lx, ly, 'next_character' )) - buttonList:addChild( createKeybinding( lx, ly, 'prev_character' )) - buttonList:addChild( createKeybinding( lx, ly, 'end_turn' )) - buttonList:addChild( createKeybinding( lx, ly, 'open_inventory_screen' )) - buttonList:addChild( createKeybinding( lx, ly, 'open_health_screen' )) - buttonList:addChild( createKeybinding( lx, ly, 'pan_camera_left' )) - buttonList:addChild( createKeybinding( lx, ly, 'pan_camera_right' )) - buttonList:addChild( createKeybinding( lx, ly, 'pan_camera_up' )) - buttonList:addChild( createKeybinding( lx, ly, 'pan_camera_down' )) + buttonList:addChild( createKeybinding( lx, ly, Settings.INPUTLAYOUTS.COMBAT, 'action_stand' )) + buttonList:addChild( createKeybinding( lx, ly, Settings.INPUTLAYOUTS.COMBAT, 'action_crouch' )) + buttonList:addChild( createKeybinding( lx, ly, Settings.INPUTLAYOUTS.COMBAT, 'action_prone' )) + buttonList:addChild( createKeybinding( lx, ly, Settings.INPUTLAYOUTS.COMBAT, 'action_reload_weapon' )) + buttonList:addChild( createKeybinding( lx, ly, Settings.INPUTLAYOUTS.COMBAT, 'next_weapon_mode' )) + buttonList:addChild( createKeybinding( lx, ly, Settings.INPUTLAYOUTS.COMBAT, 'prev_weapon_mode' )) + buttonList:addChild( createKeybinding( lx, ly, Settings.INPUTLAYOUTS.COMBAT, 'movement_mode' )) + buttonList:addChild( createKeybinding( lx, ly, Settings.INPUTLAYOUTS.COMBAT, 'attack_mode' )) + buttonList:addChild( createKeybinding( lx, ly, Settings.INPUTLAYOUTS.COMBAT, 'interaction_mode' )) + buttonList:addChild( createKeybinding( lx, ly, Settings.INPUTLAYOUTS.COMBAT, 'next_character' )) + buttonList:addChild( createKeybinding( lx, ly, Settings.INPUTLAYOUTS.COMBAT, 'prev_character' )) + buttonList:addChild( createKeybinding( lx, ly, Settings.INPUTLAYOUTS.COMBAT, 'end_turn' )) + buttonList:addChild( createKeybinding( lx, ly, Settings.INPUTLAYOUTS.COMBAT, 'open_inventory_screen' )) + buttonList:addChild( createKeybinding( lx, ly, Settings.INPUTLAYOUTS.COMBAT, 'open_health_screen' )) + buttonList:addChild( createKeybinding( lx, ly, Settings.INPUTLAYOUTS.COMBAT, 'pan_camera_left' )) + buttonList:addChild( createKeybinding( lx, ly, Settings.INPUTLAYOUTS.COMBAT, 'pan_camera_right' )) + buttonList:addChild( createKeybinding( lx, ly, Settings.INPUTLAYOUTS.COMBAT, 'pan_camera_up' )) + buttonList:addChild( createKeybinding( lx, ly, Settings.INPUTLAYOUTS.COMBAT, 'pan_camera_down' )) buttonList:addChild( createApplyButton( lx, ly )) buttonList:addChild( createBackButton( lx, ly )) @@ -215,8 +215,8 @@ end function KeybindingScreen:receive( event, ... ) if event == 'CHANGED_KEYBINDING' then - local action, scancode = ... - Settings.setKeybinding( action, scancode ) + local scancode, mode, action = ... + Settings.setKeybinding( mode, scancode, action ) self:initialize() end end diff --git a/src/ui/screens/MapTest.lua b/src/ui/screens/MapTest.lua index e7629967..8b9583da 100644 --- a/src/ui/screens/MapTest.lua +++ b/src/ui/screens/MapTest.lua @@ -73,11 +73,11 @@ function MapTest:keypressed( _, scancode ) ScreenManager.pop() end - self.camera:input( Settings.mapInput( scancode ), true ) + self.camera:input( Settings.mapInput( Settings.INPUTLAYOUTS.COMBAT, scancode ), true ) end function MapTest:keyreleased( _, scancode ) - self.camera:input( Settings.mapInput( scancode ), false ) + self.camera:input( Settings.mapInput( Settings.INPUTLAYOUTS.COMBAT, scancode ), false ) end return MapTest From 2e114e6ed64a2a0a6f99e9d9496bcf2cf78236c9 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Wed, 8 Aug 2018 07:09:46 +0200 Subject: [PATCH 10/93] Add wrap utility function The wrapping utility function allows us to wrap values to the min value if it surpasses the max value (and the other way round). --- src/ui/elements/lists/UIList.lua | 5 +++-- src/util/Util.lua | 12 ++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/ui/elements/lists/UIList.lua b/src/ui/elements/lists/UIList.lua index d1afb68d..f93fae2b 100644 --- a/src/ui/elements/lists/UIList.lua +++ b/src/ui/elements/lists/UIList.lua @@ -7,6 +7,7 @@ -- ------------------------------------------------ local UIElement = require( 'src.ui.elements.UIElement' ) +local Util = require( 'src.util.Util' ) -- ------------------------------------------------ -- Module @@ -26,7 +27,7 @@ end function UIList:prev() if self.children[self.cursor] then self.children[self.cursor]:setFocus( false ) - self.cursor = self.cursor <= 1 and #self.children or self.cursor - 1 + self.cursor = Util.wrap( 1, self.cursor - 1, #self.children ) else self.cursor = 1 end @@ -36,7 +37,7 @@ end function UIList:next() if self.children[self.cursor] then self.children[self.cursor]:setFocus( false ) - self.cursor = self.cursor >= #self.children and 1 or self.cursor + 1 + self.cursor = Util.wrap( 1, self.cursor + 1, #self.children ) else self.cursor = 1 end diff --git a/src/util/Util.lua b/src/util/Util.lua index ac576f50..88632fa4 100644 --- a/src/util/Util.lua +++ b/src/util/Util.lua @@ -100,6 +100,18 @@ function Util.clamp( min, val, max ) return math.max( min, math.min( val, max )) end +--- +-- Wraps a value to min if it surpasses max and vice versa. If the value lies +-- between min and max it is returned. +-- @tparam number min The minimum value to wrap to. +-- @tparam number val The value to wrap. +-- @tparam number max The maximum value to wrap to. +-- @treturn number The wrapped value. +-- +function Util.wrap( min, val, max ) + return val < min and max or val > max and min or val +end + --- -- Picks a random value from a tbl. Works only with sequences. -- @tparam table tbl The table to select from. From 2c48f072b0cf4c211ecf1b9a59ddfa2f305f9267 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Wed, 8 Aug 2018 07:19:44 +0200 Subject: [PATCH 11/93] Refactor scrolling method --- src/ui/elements/lists/UIList.lua | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/ui/elements/lists/UIList.lua b/src/ui/elements/lists/UIList.lua index f93fae2b..6aac39d2 100644 --- a/src/ui/elements/lists/UIList.lua +++ b/src/ui/elements/lists/UIList.lua @@ -15,6 +15,22 @@ local Util = require( 'src.util.Util' ) local UIList = UIElement:subclass( 'UIList' ) +-- ------------------------------------------------ +-- Private Methods +-- ------------------------------------------------ + +local function scrollItem( current, children, cursor, direction ) + if current then + current:setFocus( false ) + cursor = Util.wrap( 1, cursor + direction, #children ) + else + cursor = 1 + end + children[cursor]:setFocus( true ) + + return cursor +end + -- ------------------------------------------------ -- Public Methods -- ------------------------------------------------ @@ -25,23 +41,11 @@ function UIList:initialize( ox, oy, rx, ry, w, h ) end function UIList:prev() - if self.children[self.cursor] then - self.children[self.cursor]:setFocus( false ) - self.cursor = Util.wrap( 1, self.cursor - 1, #self.children ) - else - self.cursor = 1 - end - self.children[self.cursor]:setFocus( true ) + self.cursor = scrollItem( self:getActiveElement(), self.children, self.cursor, -1 ) end function UIList:next() - if self.children[self.cursor] then - self.children[self.cursor]:setFocus( false ) - self.cursor = Util.wrap( 1, self.cursor + 1, #self.children ) - else - self.cursor = 1 - end - self.children[self.cursor]:setFocus( true ) + self.cursor = scrollItem( self:getActiveElement(), self.children, self.cursor, 1 ) end -- ------------------------------------------------ From 6bb752e881ae81a37980742fb23ddf65503a6c2a Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 9 Aug 2018 19:23:19 +0200 Subject: [PATCH 12/93] Use separate method for mouse commands --- src/ui/elements/UIContainer.lua | 2 +- src/ui/elements/lists/UIHorizontalList.lua | 4 ++++ src/ui/elements/lists/UIVerticalList.lua | 4 ++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/ui/elements/UIContainer.lua b/src/ui/elements/UIContainer.lua index dd02b9f1..006754cf 100644 --- a/src/ui/elements/UIContainer.lua +++ b/src/ui/elements/UIContainer.lua @@ -111,7 +111,7 @@ function UIContainer:mousecommand( cmd ) if not self.current then return end - self.list[self.current]:command( cmd ) + self.list[self.current]:mousecommand( cmd ) end return UIContainer diff --git a/src/ui/elements/lists/UIHorizontalList.lua b/src/ui/elements/lists/UIHorizontalList.lua index 3e42351f..f10bedb4 100644 --- a/src/ui/elements/lists/UIHorizontalList.lua +++ b/src/ui/elements/lists/UIHorizontalList.lua @@ -60,4 +60,8 @@ function UIHorizontalList:command( cmd ) end end +function UIHorizontalList:mousecommand( cmd ) + self:getActiveElement():command( cmd ) +end + return UIHorizontalList diff --git a/src/ui/elements/lists/UIVerticalList.lua b/src/ui/elements/lists/UIVerticalList.lua index f550f62c..53e5dc1e 100644 --- a/src/ui/elements/lists/UIVerticalList.lua +++ b/src/ui/elements/lists/UIVerticalList.lua @@ -77,4 +77,8 @@ function UIVerticalList:command( cmd ) end end +function UIVerticalList:mousecommand( cmd ) + self:getActiveElement():command( cmd ) +end + return UIVerticalList From 375f2c2e9900e62d1abf136f31e7412789f417c1 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Tue, 7 Aug 2018 10:00:07 +0200 Subject: [PATCH 13/93] Add paginated list UIElement This element allows us to display multiple UIElements as a scrollable list distributed across multiple pages. Closes #240. --- res/texturepacks/default/sprites.lua | 3 + src/ui/elements/lists/UIPaginatedList.lua | 273 ++++++++++++++++++++++ src/ui/screens/KeybindingScreen.lua | 88 ++++--- 3 files changed, 333 insertions(+), 31 deletions(-) create mode 100644 src/ui/elements/lists/UIPaginatedList.lua diff --git a/res/texturepacks/default/sprites.lua b/res/texturepacks/default/sprites.lua index d6ed25ad..3974d634 100644 --- a/res/texturepacks/default/sprites.lua +++ b/res/texturepacks/default/sprites.lua @@ -143,6 +143,9 @@ return { ['ui_scrollbar_cursor'] = 180, ['ui_scrollbar_element'] = 180, + ['ui_prev_element'] = 18, + ['ui_next_element'] = 17, + -- ============================== -- Prefab Editor -- ============================== diff --git a/src/ui/elements/lists/UIPaginatedList.lua b/src/ui/elements/lists/UIPaginatedList.lua new file mode 100644 index 00000000..02cdc49f --- /dev/null +++ b/src/ui/elements/lists/UIPaginatedList.lua @@ -0,0 +1,273 @@ +--- +-- Displays a set of UIElements as a scrollable list distributed across multiple +-- pages. If all items fit on one page the status bar at the bottom of the list +-- will be hidden. +-- +-- @module UIPaginatedList +-- + +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ + +local UIElement = require( 'src.ui.elements.UIElement' ) +local UIButton = require( 'src.ui.elements.UIButton' ) +local Util = require( 'src.util.Util' ) +local TexturePacks = require( 'src.ui.texturepacks.TexturePacks' ) + +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ + +local UIPaginatedList = UIElement:subclass( 'UIPaginatedList' ) + +-- ------------------------------------------------ +-- Private Methods +-- ------------------------------------------------ + +--- +-- Calculates the maximum amount of pages needed to fit all items. +-- @tparam number numberOfItems The total amout of items to fit into the list. +-- @tparam number height The list's height. +-- @tparam number The maximum amount of pages to create. +-- +local function calculateMaximumPages( numberOfItems, height ) + -- Check if we can fit the list on one page if we hide the status bar. + if numberOfItems <= height then + return 1 + end + -- Calculate pages with the status bar taken into account. + return math.ceil( numberOfItems / ( height-1 )) +end + +--- +-- Fills the pages with items. +-- @tparam table items A sequence containing the UIElements to add to the list. +-- @tparam number maximumPages The maximum amount of pages to create. +-- @tparam number height The list's height. +-- @tparam number ax The list's absolute coordinate along the x-axis. +-- @tparam number ay The list's absolute coordinate along the y-axis. +-- @treturn table The created pages. +-- +local function fillPages( items, maximumPages, height, ax, ay ) + -- If we only have to create one page we can use its full height because the + -- status bar is hidden. + local maxItemsPerPage = maximumPages <= 1 and height or (height-1) + + local pages = {} + local currentPage = {} + + for i = 1, #items do + currentPage[#currentPage + 1] = items[i] + items[i]:setRelativePosition( ax, ay + #currentPage-1 ) + + -- Add the current page to the "pages" table if it is full or if we have + -- added all the items to add. + if #currentPage == maxItemsPerPage or i == #items then + pages[#pages+1] = currentPage + currentPage = {} + end + end + + return pages +end + +--- +-- Draws the page numbers. +-- @tparam number currentPage The current page index. +-- @tparam number maxPages The maximum amount of pages. +-- @tparam number px The absolute coordinate along the x-axis. +-- @tparam number py The absolute coordinate along the y-axis. +-- @tparam number w The list's width. +-- +local function drawPageNumbers( currentPage, maxPages, px, py, w ) + local tw, th = TexturePacks.getTileDimensions() + + local text = string.format( '%d/%d', currentPage, maxPages ) + local offset = TexturePacks.getFont():align( 'right', text, (w-2) * tw ) + + TexturePacks.setColor( 'ui_text_dark' ) + love.graphics.print( text, px * tw + offset, py * th ) + TexturePacks.resetColor() +end + +-- ------------------------------------------------ +-- Constructor +-- ------------------------------------------------ + +--- +-- Creates a new UIPaginatedList. +-- @tparam number ox The origin along the x-axis. +-- @tparam number oy The origin along the y-axis. +-- @tparam number rx The relative coordinate along the x-axis. +-- @tparam number ry The relative coordinate along the y-axis. +-- @tparam number w The width of this element. +-- @tparam number h The height of this element. +-- +function UIPaginatedList:initialize( ox, oy, rx, ry, w, h ) + UIElement.initialize( self, ox, oy, rx, ry, w, h ) + + self.pages = {} + self.currentPage = 1 + self.cursor = 1 +end + +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ + +--- +-- Draws the UIPaginatedList. +-- +function UIPaginatedList:draw() + -- Draw the current page. + for _, item in ipairs( self.pages[self.currentPage] ) do + item:draw() + end + + -- Draw the status bar if we have more than one page. + if self.maxPages > 1 then + drawPageNumbers( self.currentPage, self.maxPages, self.ax, self.ay + self.h - 1, self.w ) + + -- Hide prev button if we reached the first page. + if self.currentPage > 1 then + self.buttonPrev:draw() + end + + -- Hide the next button if we reached the last page. + if self.currentPage < self.maxPages then + self.buttonNext:draw() + end + end +end + +--- +-- Updates the UIPaginatedList. +-- +function UIPaginatedList:update() + if not love.mouse.isVisible() then + return + end + + for i, item in ipairs( self.pages[self.currentPage] ) do + item:setFocus( false ) + if item:isMouseOver() then + item:setFocus( true ) + self.cursor = i + end + end +end + +--- +-- Adds the list of items to display in this paginated list. +-- @tparam table items A sequence containing the UIElements to add to the list. +-- +function UIPaginatedList:setItems( items ) + self.maxPages = calculateMaximumPages( #items, self.h ) + self.pages = fillPages( items, self.maxPages, self.h, self.ax, self.ay ) + + -- Create buttons for the status bar if we have more than one page. + if #self.pages > 1 then + self:addButtons() + end + + -- Set focus to the first item on the list. + self.pages[self.currentPage][self.cursor]:setFocus( true ) +end + +--- +-- Creates the buttons to scroll through the pages. +-- +function UIPaginatedList:addButtons() + self.buttonPrev = UIButton( 0, 0, self.ax + self.w-2, self.ay + self.h-1, 1, 1, function() self:scrollPage( -1 ) end ) + self.buttonPrev:setIcon( 'ui_prev_element' ) + self.buttonNext = UIButton( 0, 0, self.ax + self.w-1, self.ay + self.h-1, 1, 1, function() self:scrollPage( 1 ) end ) + self.buttonNext:setIcon( 'ui_next_element' ) + + self:addChild( self.buttonPrev ) + self:addChild( self.buttonNext ) +end + +--- +-- Scrolls through the pages and stops when we reach the ends of the list. +-- @tparam number direction The direction to scroll to (1, -1). +-- +function UIPaginatedList:scrollPage( direction ) + if self.pages[self.currentPage][self.cursor] then + self.pages[self.currentPage][self.cursor]:setFocus( false ) + end + + self.currentPage = Util.clamp( 1, self.currentPage + direction, self.maxPages ) + + self.pages[self.currentPage][self.cursor]:setFocus( true ) +end + +--- +-- Scrolls through the items and wraps around the ends of the list. +-- @tparam number direction The direction to scroll to (1, -1). +-- +function UIPaginatedList:scrollItem( direction ) + if self.pages[self.currentPage][self.cursor] then + self.pages[self.currentPage][self.cursor]:setFocus( false ) + self.cursor = Util.wrap( 1, self.cursor + direction, #self.pages[self.currentPage] ) + else + self.cursor = 1 + end + + self.pages[self.currentPage][self.cursor]:setFocus( true ) +end + +--- +-- Fowards a command to the currently focused UIElement. +-- @tparam string cmd The command to forward. +-- +function UIPaginatedList:command( cmd ) + -- Scroll through items. + if cmd == 'up' then + self:scrollItem( -1 ) + elseif cmd == 'down' then + self:scrollItem( 1 ) + end + + -- Scroll through pages. + if cmd == 'right' then + self:scrollPage( 1 ) + elseif cmd == 'left' then + self:scrollPage( -1 ) + end + + if cmd == 'activate' and self.pages[self.currentPage][self.cursor] then + self.pages[self.currentPage][self.cursor]:command( cmd ) + end +end + +--- +-- Fowards a mousecommand to the children and items on the current page. +-- @tparam string cmd The command to forward. +-- +function UIPaginatedList:mousecommand( cmd ) + for _, item in ipairs( self.pages[self.currentPage] ) do + if item:isMouseOver() then + item:command( cmd ) + return + end + end + + for i = 1, #self.children do + if self.children[i]:isMouseOver() then + self.children[i]:command( cmd ) + return + end + end +end + +--- +-- Forwards changes to the focus to the children and items on the current page. +-- @tparam boolean focus Wether the element should be focused or not. +-- +function UIPaginatedList:setFocus( focus ) + UIPaginatedList.super.setFocus( self, focus ) + self.pages[self.currentPage][self.cursor]:setFocus( focus ) +end + +return UIPaginatedList diff --git a/src/ui/screens/KeybindingScreen.lua b/src/ui/screens/KeybindingScreen.lua index 121f1464..b0d4e282 100644 --- a/src/ui/screens/KeybindingScreen.lua +++ b/src/ui/screens/KeybindingScreen.lua @@ -17,6 +17,7 @@ local UIButton = require( 'src.ui.elements.UIButton' ) local UICopyrightFooter = require( 'src.ui.elements.UICopyrightFooter' ) local Util = require( 'src.util.Util' ) local UIMenuTitle = require( 'src.ui.elements.UIMenuTitle' ) +local UIPaginatedList = require( 'src.ui.elements.lists.UIPaginatedList' ) -- ------------------------------------------------ -- Module @@ -30,13 +31,14 @@ local KeybindingScreen = Screen:subclass( 'KeybindingScreen' ) local TITLE_POSITION = 2 local BUTTON_LIST_WIDTH = 25 +local BUTTON_LIST_HEIGHT = 10 local BUTTON_LIST_Y = 20 -- ------------------------------------------------ -- Private Methods -- ------------------------------------------------ -local function createKeybinding( lx, ly, mode, action ) +local function createKeybinding( mode, action ) -- The function to call when the button is activated. local function callback() ScreenManager.push( 'keybindingmodal', mode, action ) @@ -54,7 +56,7 @@ local function createKeybinding( lx, ly, mode, action ) local text = Util.rightPadString( actionText, BUTTON_LIST_WIDTH * 2 - #keyText, ' ' ) .. keyText -- Create the UIButton. - return UIButton( lx, ly, 0, 0, BUTTON_LIST_WIDTH, 1, callback, text, 'left' ) + return UIButton( 0, 0, 0, 0, BUTTON_LIST_WIDTH, 1, callback, text, 'left' ) end --- @@ -67,9 +69,9 @@ end --- -- Creates a button which allows the user to apply the new settings. --- @tparam number lx The parent's absolute coordinates along the x-axis. --- @tparam number ly The parent's absolute coordinates along the y-axis. --- @treturn UIButton The newly created UIButton. +-- @tparam number lx The parent's absolute coordinates along the x-axis. +-- @tparam number ly The parent's absolute coordinates along the y-axis. +-- @treturn UIButton The newly created UIButton. -- local function createApplyButton( lx, ly ) -- The function to call when the button is activated. @@ -101,9 +103,9 @@ end --- -- Creates a button which allows the user to return to the main menu. --- @tparam number lx The parent's absolute coordinates along the x-axis. --- @tparam number ly The parent's absolute coordinates along the y-axis. --- @treturn UIButton The newly created UIButton. +-- @tparam number lx The parent's absolute coordinates along the x-axis. +-- @tparam number ly The parent's absolute coordinates along the y-axis. +-- @treturn UIButton The newly created UIButton. -- local function createBackButton( lx, ly ) -- The function to call when the button is activated. @@ -118,30 +120,44 @@ end --- -- Creates a vertical list containing all the ui elements. -- -local function createUIList() +local function createPaginatedKeybindingList() local lx = GridHelper.centerElement( BUTTON_LIST_WIDTH, 1 ) local ly = BUTTON_LIST_Y - local buttonList = UIVerticalList( lx, ly, 0, 0, BUTTON_LIST_WIDTH, 1 ) + local buttonList = UIPaginatedList( lx, ly, 0, 0, BUTTON_LIST_WIDTH, BUTTON_LIST_HEIGHT ) -- Create the UIElements and add them to the list. - buttonList:addChild( createKeybinding( lx, ly, Settings.INPUTLAYOUTS.COMBAT, 'action_stand' )) - buttonList:addChild( createKeybinding( lx, ly, Settings.INPUTLAYOUTS.COMBAT, 'action_crouch' )) - buttonList:addChild( createKeybinding( lx, ly, Settings.INPUTLAYOUTS.COMBAT, 'action_prone' )) - buttonList:addChild( createKeybinding( lx, ly, Settings.INPUTLAYOUTS.COMBAT, 'action_reload_weapon' )) - buttonList:addChild( createKeybinding( lx, ly, Settings.INPUTLAYOUTS.COMBAT, 'next_weapon_mode' )) - buttonList:addChild( createKeybinding( lx, ly, Settings.INPUTLAYOUTS.COMBAT, 'prev_weapon_mode' )) - buttonList:addChild( createKeybinding( lx, ly, Settings.INPUTLAYOUTS.COMBAT, 'movement_mode' )) - buttonList:addChild( createKeybinding( lx, ly, Settings.INPUTLAYOUTS.COMBAT, 'attack_mode' )) - buttonList:addChild( createKeybinding( lx, ly, Settings.INPUTLAYOUTS.COMBAT, 'interaction_mode' )) - buttonList:addChild( createKeybinding( lx, ly, Settings.INPUTLAYOUTS.COMBAT, 'next_character' )) - buttonList:addChild( createKeybinding( lx, ly, Settings.INPUTLAYOUTS.COMBAT, 'prev_character' )) - buttonList:addChild( createKeybinding( lx, ly, Settings.INPUTLAYOUTS.COMBAT, 'end_turn' )) - buttonList:addChild( createKeybinding( lx, ly, Settings.INPUTLAYOUTS.COMBAT, 'open_inventory_screen' )) - buttonList:addChild( createKeybinding( lx, ly, Settings.INPUTLAYOUTS.COMBAT, 'open_health_screen' )) - buttonList:addChild( createKeybinding( lx, ly, Settings.INPUTLAYOUTS.COMBAT, 'pan_camera_left' )) - buttonList:addChild( createKeybinding( lx, ly, Settings.INPUTLAYOUTS.COMBAT, 'pan_camera_right' )) - buttonList:addChild( createKeybinding( lx, ly, Settings.INPUTLAYOUTS.COMBAT, 'pan_camera_up' )) - buttonList:addChild( createKeybinding( lx, ly, Settings.INPUTLAYOUTS.COMBAT, 'pan_camera_down' )) + local items = { + createKeybinding( Settings.INPUTLAYOUTS.COMBAT, 'action_stand' ), + createKeybinding( Settings.INPUTLAYOUTS.COMBAT, 'action_crouch' ), + createKeybinding( Settings.INPUTLAYOUTS.COMBAT, 'action_prone' ), + createKeybinding( Settings.INPUTLAYOUTS.COMBAT, 'action_reload_weapon' ), + createKeybinding( Settings.INPUTLAYOUTS.COMBAT, 'next_weapon_mode' ), + createKeybinding( Settings.INPUTLAYOUTS.COMBAT, 'prev_weapon_mode' ), + createKeybinding( Settings.INPUTLAYOUTS.COMBAT, 'movement_mode' ), + createKeybinding( Settings.INPUTLAYOUTS.COMBAT, 'attack_mode' ), + createKeybinding( Settings.INPUTLAYOUTS.COMBAT, 'interaction_mode' ), + createKeybinding( Settings.INPUTLAYOUTS.COMBAT, 'next_character' ), + createKeybinding( Settings.INPUTLAYOUTS.COMBAT, 'prev_character' ), + createKeybinding( Settings.INPUTLAYOUTS.COMBAT, 'end_turn' ), + createKeybinding( Settings.INPUTLAYOUTS.COMBAT, 'open_inventory_screen' ), + createKeybinding( Settings.INPUTLAYOUTS.COMBAT, 'open_health_screen' ), + createKeybinding( Settings.INPUTLAYOUTS.COMBAT, 'pan_camera_left' ), + createKeybinding( Settings.INPUTLAYOUTS.COMBAT, 'pan_camera_right' ), + createKeybinding( Settings.INPUTLAYOUTS.COMBAT, 'pan_camera_up' ), + createKeybinding( Settings.INPUTLAYOUTS.COMBAT, 'pan_camera_down' ), + } + + buttonList:setItems( items ) + + return buttonList +end + +local function createButtonList() + local lx = GridHelper.centerElement( BUTTON_LIST_WIDTH, 1 ) + local ly = BUTTON_LIST_Y + BUTTON_LIST_HEIGHT + 1 + + local buttonList = UIVerticalList( lx, ly, 0, 0, BUTTON_LIST_WIDTH, 1 ) + buttonList:addChild( createApplyButton( lx, ly )) buttonList:addChild( createBackButton( lx, ly )) @@ -153,9 +169,12 @@ end -- ------------------------------------------------ function KeybindingScreen:initialize() - self.buttonList = createUIList() - self.container = UIContainer() + + self.paginatedList = createPaginatedKeybindingList() + self.buttonList = createButtonList() + + self.container:register( self.paginatedList ) self.container:register( self.buttonList ) self.footer = UICopyrightFooter() @@ -174,7 +193,10 @@ end -- Draws the OptionsScreen. -- function KeybindingScreen:draw() - self.container:draw() + self.paginatedList:draw() + self.paginatedList:drawBoundingBox( 16, 16 ) + self.buttonList:draw() + self.footer:draw() self.title:draw() end @@ -189,6 +211,10 @@ function KeybindingScreen:keypressed( _, scancode ) close() end + if scancode == 'tab' then + self.container:next() + end + if scancode == 'up' then self.container:command( 'up' ) elseif scancode == 'down' then From bbe21e58c1aeb3f62c7dda3aa94b5c103a61509b Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 9 Aug 2018 20:27:04 +0200 Subject: [PATCH 14/93] Use paginated list for tool selectors The prefab editor has been upgraded with paginated lists for the tile and world object selectors. Closes #243. --- src/ui/screens/PrefabEditor.lua | 38 ++++++++++++++++----------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/ui/screens/PrefabEditor.lua b/src/ui/screens/PrefabEditor.lua index f3646443..7bb44f3d 100644 --- a/src/ui/screens/PrefabEditor.lua +++ b/src/ui/screens/PrefabEditor.lua @@ -11,7 +11,6 @@ local ScreenManager = require( 'lib.screenmanager.ScreenManager' ) local TileFactory = require( 'src.map.tiles.TileFactory' ) local WorldObjectFactory = require( 'src.map.worldobjects.WorldObjectFactory' ) local TexturePacks = require( 'src.ui.texturepacks.TexturePacks' ) -local UIVerticalList = require( 'src.ui.elements.lists.UIVerticalList' ) local UIButton = require( 'src.ui.elements.UIButton' ) local Camera = require( 'src.ui.Camera' ) local Translator = require( 'src.util.Translator' ) @@ -19,6 +18,7 @@ local PrefabCanvas = require( 'src.ui.mapeditor.PrefabCanvas' ) local PrefabBrush = require( 'src.ui.mapeditor.PrefabBrush' ) local UIContainer = require( 'src.ui.elements.UIContainer' ) local GridHelper = require( 'src.util.GridHelper' ) +local UIPaginatedList = require( 'src.ui.elements.lists.UIPaginatedList' ) -- ------------------------------------------------ -- Module @@ -39,51 +39,51 @@ local SELECTOR_HEIGHT = 10 local function createTileSelector( tileTemplates, tool ) local lx, ly = 1, 1 - local tileSelector = UIVerticalList( lx, ly, 0, 0, SELECTOR_WIDTH, SELECTOR_HEIGHT ) + local tileSelector = UIPaginatedList( lx, ly, 0, 0, SELECTOR_WIDTH, SELECTOR_HEIGHT ) - local counter = 0 + local buttons = {} for id, template in pairs( tileTemplates ) do local function callback() tool:setBrush( template, 'tile' ) end - local tmp = UIButton( lx, ly, 0, counter, SELECTOR_WIDTH, 1, callback, Translator.getText( id ), 'left' ) - tmp:setIcon( id ) - tmp:setIconColorID( id ) + local button = UIButton( 0, 0, 0, 0, SELECTOR_WIDTH, 1, callback, Translator.getText( id ), 'left' ) + button:setIcon( id ) + button:setIconColorID( id ) - tileSelector:addChild( tmp ) - - counter = counter + 1 + buttons[#buttons + 1] = button end + tileSelector:setItems( buttons ) + return tileSelector end local function createWorldObjectSelector( objectTemplates, tool ) local lx, ly = 1, SELECTOR_HEIGHT + 2 - local objectSelector = UIVerticalList( lx, ly, 0, 0, SELECTOR_WIDTH, SELECTOR_HEIGHT ) + local objectSelector = UIPaginatedList( lx, ly, 0, 0, SELECTOR_WIDTH, SELECTOR_HEIGHT ) - local counter = 0 + local buttons = {} for id, template in pairs( objectTemplates ) do local function callback() tool:setBrush( template, 'worldObject' ) end - local tmp = UIButton( lx, ly, 0, counter, SELECTOR_WIDTH, 1, callback, Translator.getText( id ), 'left' ) + local button = UIButton( 0, 0, 0, 0, SELECTOR_WIDTH, 1, callback, Translator.getText( id ), 'left' ) if template.openable then - tmp:setIcon( id, 'closed' ) + button:setIcon( id, 'closed' ) elseif template.connections then - tmp:setIcon( id, 'default' ) + button:setIcon( id, 'default' ) else - tmp:setIcon( id ) + button:setIcon( id ) end - tmp:setIconColorID( id ) + button:setIconColorID( id ) - objectSelector:addChild( tmp ) - - counter = counter + 1 + buttons[#buttons + 1] = button end + objectSelector:setItems( buttons ) + return objectSelector end From dbe166c8d5e7616591ef3ed50d64599a49f681e1 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 9 Aug 2018 20:27:58 +0200 Subject: [PATCH 15/93] Remove debug drawing call from keybinding screen --- src/ui/screens/KeybindingScreen.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ui/screens/KeybindingScreen.lua b/src/ui/screens/KeybindingScreen.lua index b0d4e282..bd6242fd 100644 --- a/src/ui/screens/KeybindingScreen.lua +++ b/src/ui/screens/KeybindingScreen.lua @@ -194,7 +194,6 @@ end -- function KeybindingScreen:draw() self.paginatedList:draw() - self.paginatedList:drawBoundingBox( 16, 16 ) self.buttonList:draw() self.footer:draw() From 0a7420bf9b08b3b370e68ef4f5654a9305ad8bfd Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 9 Aug 2018 20:39:47 +0200 Subject: [PATCH 16/93] Fix crash when switching to a new page The crash was caused because the list tried to focus an UIElement at the same cursor position. If the previous page contained (for example) nine entries, it also tried to focus the ninth entry on the next page. We now clamp the cursor to the max amount of items on the page. This means the list will automatically focus the last item in the list if there aren't enough items. --- src/ui/elements/lists/UIPaginatedList.lua | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/ui/elements/lists/UIPaginatedList.lua b/src/ui/elements/lists/UIPaginatedList.lua index 02cdc49f..1e1c5e60 100644 --- a/src/ui/elements/lists/UIPaginatedList.lua +++ b/src/ui/elements/lists/UIPaginatedList.lua @@ -197,8 +197,15 @@ function UIPaginatedList:scrollPage( direction ) self.pages[self.currentPage][self.cursor]:setFocus( false ) end + -- Stop at the first and the last page. self.currentPage = Util.clamp( 1, self.currentPage + direction, self.maxPages ) + -- Prevent cursor from jumping to an item that doesn't exist on the last page. + -- This can happen for example if the last page only has four items, but the + -- cursor was focused on the ninth item on the second to last page. + self.cursor = Util.clamp( 1, self.cursor, #self.pages[self.currentPage] ) + + -- Focus the item at the cursor's position on the new page. self.pages[self.currentPage][self.cursor]:setFocus( true ) end From 068c08d5c5905820a3aed16877d479b322c0b1ff Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 9 Aug 2018 20:50:18 +0200 Subject: [PATCH 17/93] Fix flood filling tool in prefab editor Fixes #222. --- src/ui/mapeditor/PrefabBrush.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ui/mapeditor/PrefabBrush.lua b/src/ui/mapeditor/PrefabBrush.lua index 9d4a144a..4cb4d99d 100644 --- a/src/ui/mapeditor/PrefabBrush.lua +++ b/src/ui/mapeditor/PrefabBrush.lua @@ -57,10 +57,10 @@ local function fill( canvas, x, y, type, template, toFill ) return end - fill( canvas, x-1, y, toFill ) - fill( canvas, x+1, y, toFill ) - fill( canvas, x, y-1, toFill ) - fill( canvas, x, y+1, toFill ) + fill( canvas, x-1, y, type, template, toFill ) + fill( canvas, x+1, y, type, template, toFill ) + fill( canvas, x, y-1, type, template, toFill ) + fill( canvas, x, y+1, type, template, toFill ) end -- ------------------------------------------------ From b98a261c7e5fd63dc85165a35a0e144d088fb7fa Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 13 Aug 2018 19:02:45 +0200 Subject: [PATCH 18/93] Update button list on keybinding screen correctly Instead of resetting the whole screen we only update the item list of the paginated list. This way we can keep the proper page and cursor settings of the list while still making sure the buttons are properly displayed. --- src/ui/screens/KeybindingScreen.lua | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/ui/screens/KeybindingScreen.lua b/src/ui/screens/KeybindingScreen.lua index bd6242fd..49d5703d 100644 --- a/src/ui/screens/KeybindingScreen.lua +++ b/src/ui/screens/KeybindingScreen.lua @@ -118,15 +118,11 @@ local function createBackButton( lx, ly ) end --- --- Creates a vertical list containing all the ui elements. +-- Creates a sequence containing all the keybinding buttons. +-- @treturn table A sequence containing the UIButtons for the keybinding list. -- -local function createPaginatedKeybindingList() - local lx = GridHelper.centerElement( BUTTON_LIST_WIDTH, 1 ) - local ly = BUTTON_LIST_Y - local buttonList = UIPaginatedList( lx, ly, 0, 0, BUTTON_LIST_WIDTH, BUTTON_LIST_HEIGHT ) - - -- Create the UIElements and add them to the list. - local items = { +local function getKeyButtonList() + return { createKeybinding( Settings.INPUTLAYOUTS.COMBAT, 'action_stand' ), createKeybinding( Settings.INPUTLAYOUTS.COMBAT, 'action_crouch' ), createKeybinding( Settings.INPUTLAYOUTS.COMBAT, 'action_prone' ), @@ -146,8 +142,17 @@ local function createPaginatedKeybindingList() createKeybinding( Settings.INPUTLAYOUTS.COMBAT, 'pan_camera_up' ), createKeybinding( Settings.INPUTLAYOUTS.COMBAT, 'pan_camera_down' ), } +end + +--- +-- Creates a vertical list containing all the ui elements. +-- +local function createPaginatedKeybindingList() + local lx = GridHelper.centerElement( BUTTON_LIST_WIDTH, 1 ) + local ly = BUTTON_LIST_Y + local buttonList = UIPaginatedList( lx, ly, 0, 0, BUTTON_LIST_WIDTH, BUTTON_LIST_HEIGHT ) - buttonList:setItems( items ) + buttonList:setItems( getKeyButtonList() ) return buttonList end @@ -242,7 +247,7 @@ function KeybindingScreen:receive( event, ... ) if event == 'CHANGED_KEYBINDING' then local scancode, mode, action = ... Settings.setKeybinding( mode, scancode, action ) - self:initialize() + self.paginatedList:setItems( getKeyButtonList() ) end end From 02655257fde3a74bae6102942148f5292c3c0768 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 13 Aug 2018 20:57:21 +0200 Subject: [PATCH 19/93] Fix rebinding of unassigned controls Due to a bug "unassigned" actions couldn't be assigned to a new scancode anymore. Fixes #244. --- src/Settings.lua | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Settings.lua b/src/Settings.lua index f2fbb36d..7bde230c 100644 --- a/src/Settings.lua +++ b/src/Settings.lua @@ -212,6 +212,15 @@ function Settings.setTexturepack( ntexturepack ) end function Settings.setKeybinding( mode, scancode, saction ) + -- If the action is not assigned to a scancode yet we can set it directly. + if Settings.getKeybinding( mode, saction ) == 'unassigned' then + settings.controls[mode][scancode] = saction + return + end + + -- If the action is already mapped to a scancode, we have to remove the old + -- mapping (or else both keys would work for the same action), before we can + -- assign the new one. for oldscancode, action in pairs( settings.controls[mode] ) do if action == saction then settings.controls[mode][oldscancode] = nil From 7b305527ef5768150f46c76275621f896ef5f893 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 13 Aug 2018 21:14:01 +0200 Subject: [PATCH 20/93] Store "unassigned" string in constant variable --- src/Settings.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Settings.lua b/src/Settings.lua index 7bde230c..3a49d655 100644 --- a/src/Settings.lua +++ b/src/Settings.lua @@ -55,6 +55,7 @@ local DEFAULT_SETTINGS = { } local WARNING_TEXT = 'Replacing outdated settings file (v%d) with current default settings (v%d)!' +local UNASSIGNED_SCANCODE = 'unassigned' Settings.INPUTLAYOUTS = {} Settings.INPUTLAYOUTS.COMBAT = 'combat' @@ -139,7 +140,7 @@ function Settings.getKeybinding( mode, saction ) return love.keyboard.getKeyFromScancode( scancode ) end end - return 'unassigned' + return UNASSIGNED_SCANCODE end function Settings.getLocale() @@ -213,7 +214,7 @@ end function Settings.setKeybinding( mode, scancode, saction ) -- If the action is not assigned to a scancode yet we can set it directly. - if Settings.getKeybinding( mode, saction ) == 'unassigned' then + if Settings.getKeybinding( mode, saction ) == UNASSIGNED_SCANCODE then settings.controls[mode][scancode] = saction return end From 8f5a025aebe2e1551fc9dd11105eaf248da055cd Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 13 Aug 2018 22:16:51 +0200 Subject: [PATCH 21/93] Remove outdated documentation --- src/characters/body/BodyFactory.lua | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/characters/body/BodyFactory.lua b/src/characters/body/BodyFactory.lua index 7efc5f7e..9ade6257 100644 --- a/src/characters/body/BodyFactory.lua +++ b/src/characters/body/BodyFactory.lua @@ -1,9 +1,6 @@ --- -- The BodyFactory is used to assemble the bodies of each creature in the game -- from their template files. --- Each creature template needs to come with a .lua file containing general --- stats such as the blood volume and the id of the creature and a .tgf file --- which contains the layout of the body graph. -- -- @module BodyFactory -- From 58b965cae5783fb158d8abb02e77b085fdcf17d6 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 13 Aug 2018 22:35:30 +0200 Subject: [PATCH 22/93] Update documentation --- src/ui/elements/inventory/UIEquipmentList.lua | 40 +++++++++++++++++-- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/src/ui/elements/inventory/UIEquipmentList.lua b/src/ui/elements/inventory/UIEquipmentList.lua index af6060d0..70276caa 100644 --- a/src/ui/elements/inventory/UIEquipmentList.lua +++ b/src/ui/elements/inventory/UIEquipmentList.lua @@ -1,4 +1,7 @@ --- +-- The UIEquipmentList is a specialised list on the inventory screen that takes +-- care of drawing a creature's equipment slots and the equipped items therein. +-- -- @module UIEquipmentList -- @@ -22,17 +25,24 @@ local UIEquipmentList = UIElement:subclass( 'UIEquipmentList' ) -- ------------------------------------------------ --- --- Iterates over all equipment slots an UIEquipmentSlot for them and stores --- them based on their sort order. +-- Creates UIEquipmentSlots for each EquipmentSlot in a creature's body and +-- stores it in a list. -- @treturn table A sequence containing the UIEquipmentSlots. -- local function populateItemList( self ) local nList = {} + + -- Iterate over all equipment slots in the creature's body. for _, slot in pairs( self.equipment:getSlots() ) do + -- Map each slot to a UIEquipmentSlot object. local uiItem = UIEquipmentSlot( self.ax, self.ay, 0, slot:getSortOrder(), self.w, 1, slot ) + + -- Add the UIEquipmentSlot to the newly created list and add it to + -- the UIEquipmentList as a child (@see UIElement). nList[slot:getSortOrder()] = uiItem self:addChild( uiItem ) end + return nList end @@ -52,14 +62,14 @@ function UIEquipmentList:initialize( px, py, x, y, w, h, character ) end --- --- Recreates the item list. +-- Recreates the equipment list. -- function UIEquipmentList:refresh() self.list = populateItemList( self ) end --- --- Draws the list. +-- Draws the equipment slots. -- function UIEquipmentList:draw() for _, slot in ipairs( self.list ) do @@ -118,6 +128,10 @@ function UIEquipmentList:drop( item, origin ) return success end +--- +-- Returns the equipment slot the mouse is currently hovering over. +-- @treturn UIEquipmentSlot The slot the mouse is currently over. +-- function UIEquipmentList:getSlotBelowCursor() for _, uiItem in ipairs( self.list ) do if uiItem:isMouseOver() then @@ -126,6 +140,12 @@ function UIEquipmentList:getSlotBelowCursor() end end +--- +-- Returns the equipment item the mouse is currently hovering over. Note that +-- the item is actually located within the EquipmentSlot object which itself +-- is wrapped inside of a UIEquipmentSlot instance. +-- @treturn Item The item the mouse is currently over. +-- function UIEquipmentList:getItemBelowCursor() for _, uiItem in ipairs( self.list ) do if uiItem:isMouseOver() then @@ -134,12 +154,24 @@ function UIEquipmentList:getItemBelowCursor() end end +--- +-- Highlights the UIEquipmentSlot(s) in which the specified Item fits. +-- @tparam Item nitem The item to highlight slots for. +-- function UIEquipmentList:highlight( nitem ) for _, uiItem in ipairs( self.list ) do uiItem:matchesType( nitem ) end end +--- +-- Checks if an item fits into the UIEquipmentSlot currently located under the +-- mouse cursor. If the mouse isn't hovering over a slot the function returns +-- false. +-- @tparam Item item The item to check for. +-- @treturn boolean True if the item fits. False if it doesn't or the mouse isn't +-- hovering over an UIEquipmentSlot. +-- function UIEquipmentList:doesFit( item ) local slot = self:getSlotBelowCursor() if not slot then From ec73ce97e79b0f9870f2a975bc10f92ed4e469d7 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 13 Aug 2018 22:42:41 +0200 Subject: [PATCH 23/93] Refactor equipment slot highlighting --- src/ui/elements/inventory/UIEquipmentList.lua | 4 ++-- src/ui/elements/inventory/UIEquipmentSlot.lua | 14 ++++++++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/ui/elements/inventory/UIEquipmentList.lua b/src/ui/elements/inventory/UIEquipmentList.lua index 70276caa..40ad293f 100644 --- a/src/ui/elements/inventory/UIEquipmentList.lua +++ b/src/ui/elements/inventory/UIEquipmentList.lua @@ -159,8 +159,8 @@ end -- @tparam Item nitem The item to highlight slots for. -- function UIEquipmentList:highlight( nitem ) - for _, uiItem in ipairs( self.list ) do - uiItem:matchesType( nitem ) + for _, uiEquipmentSlot in ipairs( self.list ) do + uiEquipmentSlot:highlight( nitem ) end end diff --git a/src/ui/elements/inventory/UIEquipmentSlot.lua b/src/ui/elements/inventory/UIEquipmentSlot.lua index 9fdc8137..da0532f6 100644 --- a/src/ui/elements/inventory/UIEquipmentSlot.lua +++ b/src/ui/elements/inventory/UIEquipmentSlot.lua @@ -21,7 +21,7 @@ local UIEquipmentSlot = UIElement:subclass( 'UIElement' ) -- ------------------------------------------------ local function updateBackground( self ) - if self.highlight then + if self.highlighted then self.background:setColor( 'ui_equipment_highlight' ) elseif self:isMouseOver() then self.background:setColor( 'ui_equipment_mouseover' ) @@ -71,12 +71,18 @@ function UIEquipmentSlot:getSlot() return self.slot end -function UIEquipmentSlot:matchesType( item ) +--- +-- Sets the "highlight" flag if the item-type and sub-type of the specified item +-- matches the item-type and sub-type of the EquipmentSlot connected to this +-- UIEquipmentSlot. +-- @tparam Item item The item to check for. +-- +function UIEquipmentSlot:highlight( item ) if not item then - self.highlight = false + self.highlighted = false return end - self.highlight = item:isSameType( self.slot:getItemType(), self.slot:getSubType() ) + self.highlighted = item:isSameType( self.slot:getItemType(), self.slot:getSubType() ) end return UIEquipmentSlot From a0f8dfe92daa391d41b1d7b582fc8547b37e6215 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 13 Aug 2018 22:58:15 +0200 Subject: [PATCH 24/93] Remove separate list used in UIEquipmentList The list was used to keep track of the UIEquipmentSlots, but they were also added to the UIEquipmentList as children. When the list was refreshed only the list was cleared, but slots were still added as children. This caused the children table to grow continuously over time. Since the separate list was superfluous anyway we now use the children table provided by the UIElement parent class to populate the equipment list. Fixes #246. --- src/ui/elements/UIElement.lua | 18 +++++++++++-- src/ui/elements/inventory/UIEquipmentList.lua | 26 +++++++++---------- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/src/ui/elements/UIElement.lua b/src/ui/elements/UIElement.lua index 9b6db97a..d546a6bf 100644 --- a/src/ui/elements/UIElement.lua +++ b/src/ui/elements/UIElement.lua @@ -55,11 +55,25 @@ function UIElement:initialize( ox, oy, rx, ry, w, h ) self.h = h end -function UIElement:addChild( child ) +--- +-- Adds a new UIElement to the list of children. +-- +-- NOTE: If you use custom indexes you should make sure that you don't break the +-- sequence (1, 2, 3, ... n). Trying to iterate over an array with can have +-- unforeseen consequences in Lua. +-- +-- @tparam UIElement child The UIElement to add as a child. +-- @tparam number index A custom index to use (optional). +-- +function UIElement:addChild( child, index ) if not child:isInstanceOf( UIElement ) then error( 'Children of a UIElement must be derived from the UIElement class themselves.' ) end - self.children[#self.children + 1] = child + + -- Create a new index or use the one provided as a parameter. + index = index or #self.children + 1 + + self.children[index] = child end --- diff --git a/src/ui/elements/inventory/UIEquipmentList.lua b/src/ui/elements/inventory/UIEquipmentList.lua index 40ad293f..d861258d 100644 --- a/src/ui/elements/inventory/UIEquipmentList.lua +++ b/src/ui/elements/inventory/UIEquipmentList.lua @@ -30,20 +30,18 @@ local UIEquipmentList = UIElement:subclass( 'UIEquipmentList' ) -- @treturn table A sequence containing the UIEquipmentSlots. -- local function populateItemList( self ) - local nList = {} + -- Clear current children. + self.children = {} -- Iterate over all equipment slots in the creature's body. for _, slot in pairs( self.equipment:getSlots() ) do -- Map each slot to a UIEquipmentSlot object. local uiItem = UIEquipmentSlot( self.ax, self.ay, 0, slot:getSortOrder(), self.w, 1, slot ) - -- Add the UIEquipmentSlot to the newly created list and add it to - -- the UIEquipmentList as a child (@see UIElement). - nList[slot:getSortOrder()] = uiItem - self:addChild( uiItem ) + -- Add the UIEquipmentSlot to the list's children (@see UIElement) and + -- use its sort order to find the correct position. + self:addChild( uiItem, slot:getSortOrder() ) end - - return nList end -- ------------------------------------------------ @@ -65,14 +63,14 @@ end -- Recreates the equipment list. -- function UIEquipmentList:refresh() - self.list = populateItemList( self ) + populateItemList( self ) end --- -- Draws the equipment slots. -- function UIEquipmentList:draw() - for _, slot in ipairs( self.list ) do + for _, slot in ipairs( self.children ) do slot:draw() end end @@ -82,7 +80,7 @@ end -- @treturn UIEquipmentSlot The UIEquipmentSlot containing the actual item. -- function UIEquipmentList:drag() - for _, uiItem in ipairs( self.list ) do + for _, uiItem in ipairs( self.children ) do if uiItem:isMouseOver() and uiItem:getSlot():containsItem() and not uiItem:getSlot():getItem():isPermanent() then local item = self.equipment:removeItem( uiItem:getSlot() ) @@ -111,7 +109,7 @@ function UIEquipmentList:drop( item, origin ) end local success = false - for _, uiItem in ipairs( self.list ) do + for _, uiItem in ipairs( self.children ) do local slot = uiItem:getSlot() if uiItem:isMouseOver() and item:isSameType( slot:getItemType(), slot:getSubType() ) then if slot:containsItem() then @@ -133,7 +131,7 @@ end -- @treturn UIEquipmentSlot The slot the mouse is currently over. -- function UIEquipmentList:getSlotBelowCursor() - for _, uiItem in ipairs( self.list ) do + for _, uiItem in ipairs( self.children ) do if uiItem:isMouseOver() then return uiItem:getSlot() end @@ -147,7 +145,7 @@ end -- @treturn Item The item the mouse is currently over. -- function UIEquipmentList:getItemBelowCursor() - for _, uiItem in ipairs( self.list ) do + for _, uiItem in ipairs( self.children ) do if uiItem:isMouseOver() then return uiItem:getSlot():getItem() end @@ -159,7 +157,7 @@ end -- @tparam Item nitem The item to highlight slots for. -- function UIEquipmentList:highlight( nitem ) - for _, uiEquipmentSlot in ipairs( self.list ) do + for _, uiEquipmentSlot in ipairs( self.children ) do uiEquipmentSlot:highlight( nitem ) end end From 58d359ad64e8f76fb6c8786c858c82e6315398cf Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 13 Aug 2018 23:04:54 +0200 Subject: [PATCH 25/93] Refactor variable names to be more precise --- src/ui/elements/inventory/UIEquipmentList.lua | 42 ++++++++++--------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/src/ui/elements/inventory/UIEquipmentList.lua b/src/ui/elements/inventory/UIEquipmentList.lua index d861258d..80859965 100644 --- a/src/ui/elements/inventory/UIEquipmentList.lua +++ b/src/ui/elements/inventory/UIEquipmentList.lua @@ -36,11 +36,11 @@ local function populateItemList( self ) -- Iterate over all equipment slots in the creature's body. for _, slot in pairs( self.equipment:getSlots() ) do -- Map each slot to a UIEquipmentSlot object. - local uiItem = UIEquipmentSlot( self.ax, self.ay, 0, slot:getSortOrder(), self.w, 1, slot ) + local uiEquipmentSlot = UIEquipmentSlot( self.ax, self.ay, 0, slot:getSortOrder(), self.w, 1, slot ) -- Add the UIEquipmentSlot to the list's children (@see UIElement) and -- use its sort order to find the correct position. - self:addChild( uiItem, slot:getSortOrder() ) + self:addChild( uiEquipmentSlot, slot:getSortOrder() ) end end @@ -70,8 +70,8 @@ end -- Draws the equipment slots. -- function UIEquipmentList:draw() - for _, slot in ipairs( self.children ) do - slot:draw() + for _, uiEquipmentSlot in ipairs( self.children ) do + uiEquipmentSlot:draw() end end @@ -80,16 +80,18 @@ end -- @treturn UIEquipmentSlot The UIEquipmentSlot containing the actual item. -- function UIEquipmentList:drag() - for _, uiItem in ipairs( self.children ) do - if uiItem:isMouseOver() and uiItem:getSlot():containsItem() and not uiItem:getSlot():getItem():isPermanent() then - local item = self.equipment:removeItem( uiItem:getSlot() ) + for _, uiEquipmentSlot in ipairs( self.children ) do + if uiEquipmentSlot:isMouseOver() + and uiEquipmentSlot:getSlot():containsItem() + and not uiEquipmentSlot:getSlot():getItem():isPermanent() then + local item = self.equipment:removeItem( uiEquipmentSlot:getSlot() ) if item:isInstanceOf( Container ) then self.character:getInventory():dropItems( self.character:getTile() ) end self:refresh() - return item, self.equipment, uiItem:getSlot() + return item, self.equipment, uiEquipmentSlot:getSlot() end end end @@ -109,9 +111,9 @@ function UIEquipmentList:drop( item, origin ) end local success = false - for _, uiItem in ipairs( self.children ) do - local slot = uiItem:getSlot() - if uiItem:isMouseOver() and item:isSameType( slot:getItemType(), slot:getSubType() ) then + for _, uiEquipmentSlot in ipairs( self.children ) do + local slot = uiEquipmentSlot:getSlot() + if uiEquipmentSlot:isMouseOver() and item:isSameType( slot:getItemType(), slot:getSubType() ) then if slot:containsItem() then local tmp = self.equipment:removeItem( slot ) success = self.equipment:addItem( slot, item ) @@ -131,9 +133,9 @@ end -- @treturn UIEquipmentSlot The slot the mouse is currently over. -- function UIEquipmentList:getSlotBelowCursor() - for _, uiItem in ipairs( self.children ) do - if uiItem:isMouseOver() then - return uiItem:getSlot() + for _, uiEquipmentSlot in ipairs( self.children ) do + if uiEquipmentSlot:isMouseOver() then + return uiEquipmentSlot:getSlot() end end end @@ -145,9 +147,9 @@ end -- @treturn Item The item the mouse is currently over. -- function UIEquipmentList:getItemBelowCursor() - for _, uiItem in ipairs( self.children ) do - if uiItem:isMouseOver() then - return uiItem:getSlot():getItem() + for _, uiEquipmentSlot in ipairs( self.children ) do + if uiEquipmentSlot:isMouseOver() then + return uiEquipmentSlot:getSlot():getItem() end end end @@ -171,11 +173,11 @@ end -- hovering over an UIEquipmentSlot. -- function UIEquipmentList:doesFit( item ) - local slot = self:getSlotBelowCursor() - if not slot then + local uiEquipmentSlot = self:getSlotBelowCursor() + if not uiEquipmentSlot then return false end - return item:isSameType( slot:getItemType(), slot:getSubType() ) + return item:isSameType( uiEquipmentSlot:getItemType(), uiEquipmentSlot:getSubType() ) end return UIEquipmentList From 73e62d3328dfbd6815e9197c3c1ecac4f618701f Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 13 Aug 2018 23:23:32 +0200 Subject: [PATCH 26/93] Remove separate list used in UIInventoryList The list was used to keep track of the UIInventoryItems, which were also added to the UIInventoryList as UIElement children. When the list was refreshed the children weren't cleared. This caused the children table to grow continuously over time. Since the separate list was superfluous anyway we now use the children table provided by the UIElement parent class to populate the inventory list. Fixes #247. --- src/ui/elements/inventory/UIInventoryList.lua | 40 +++++++++++-------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/src/ui/elements/inventory/UIInventoryList.lua b/src/ui/elements/inventory/UIInventoryList.lua index 78ce0d21..6a411127 100644 --- a/src/ui/elements/inventory/UIInventoryList.lua +++ b/src/ui/elements/inventory/UIInventoryList.lua @@ -1,4 +1,7 @@ --- +-- The UIInventoryList is a specialised list on the inventory screen that takes +-- care of drawing all items a creature currently carries around in its invenory. +-- -- @module UIInventoryList -- @@ -20,22 +23,28 @@ local UIInventoryList = UIElement:subclass( 'UIInventoryList' ) -- Private Methods -- ------------------------------------------------ +--- +-- Creates UIInventoryItems for each Item in a creature's inventory and +-- stores it as a children of the UIInventoryList. +-- @treturn table A sequence containing the UIInventoryItems. +-- local function populateItemList( self ) - local nList = {} + -- Clear current children. + self.children = {} + + -- Iterate over all items in the creature's inventory. for offset, item in ipairs( self.inventory:getItems() ) do - -- Spawn elements at the list's position but offset them vertically. - local uiItem = UIInventoryItem( self.ax, self.ay, 0, offset, self.w, 1, item ) - self:addChild( uiItem ) - nList[#nList + 1] = uiItem + -- Map each Item to an UIInventoryItem object. The UIInventoryItem is spawned at the + -- UIInventoryList's absolute position but offset vertically based on its positon in + -- the inventory. + local uiInventoryItem = UIInventoryItem( self.ax, self.ay, 0, offset, self.w, 1, item ) + self:addChild( uiInventoryItem ) end - return nList end local function generateStorageInfo( self ) local infoText = string.format( 'W: %0.1f/%0.1f V: %0.1f/%0.1f', self.inventory:getWeight(), self.inventory:getWeightLimit(), self.inventory:getVolume(), self.inventory:getVolumeLimit() ) - local info = UILabel( self.ax, self.ay, 0, 0, self.w, 1, infoText, 'ui_text_dim' ) - self:addChild( info ) - return info + self:addChild( UILabel( self.ax, self.ay, 0, 0, self.w, 1, infoText, 'ui_text_dim' )) end -- ------------------------------------------------ @@ -53,19 +62,18 @@ function UIInventoryList:initialize( px, py, x, y, w, h, inventory ) end function UIInventoryList:refresh() - self.list = populateItemList( self ) - self.info = generateStorageInfo( self ) + populateItemList( self ) + generateStorageInfo( self ) end function UIInventoryList:draw() - for _, item in ipairs( self.list ) do + for _, item in ipairs( self.children ) do item:draw() end - self.info:draw() end function UIInventoryList:drag( rmb, fullstack ) - for _, uiItem in ipairs( self.list ) do + for _, uiItem in ipairs( self.children ) do if uiItem:isMouseOver() then local item = uiItem:drag( rmb, fullstack ) self.inventory:removeItem( item ) @@ -76,7 +84,7 @@ function UIInventoryList:drag( rmb, fullstack ) end function UIInventoryList:drop( item ) - for _, uiItem in ipairs( self.list ) do + for _, uiItem in ipairs( self.children ) do if uiItem:isMouseOver() then local success = self.inventory:insertItem( item, uiItem:getItem() ) if success then @@ -95,7 +103,7 @@ function UIInventoryList:drop( item ) end function UIInventoryList:getItemBelowCursor() - for _, uiItem in ipairs( self.list ) do + for _, uiItem in ipairs( self.children ) do if uiItem:isMouseOver() then return uiItem:getItem() end From 07dfaedb677381c069b39cefaffb74bac828651e Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 13 Aug 2018 23:27:36 +0200 Subject: [PATCH 27/93] Update documentation --- src/ui/elements/inventory/UIInventoryList.lua | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/ui/elements/inventory/UIInventoryList.lua b/src/ui/elements/inventory/UIInventoryList.lua index 6a411127..50d303af 100644 --- a/src/ui/elements/inventory/UIInventoryList.lua +++ b/src/ui/elements/inventory/UIInventoryList.lua @@ -42,6 +42,10 @@ local function populateItemList( self ) end end +--- +-- Creates the storage info which shows details about the weight and volume stats +-- of the creature's inventory. +-- local function generateStorageInfo( self ) local infoText = string.format( 'W: %0.1f/%0.1f V: %0.1f/%0.1f', self.inventory:getWeight(), self.inventory:getWeightLimit(), self.inventory:getVolume(), self.inventory:getVolumeLimit() ) self:addChild( UILabel( self.ax, self.ay, 0, 0, self.w, 1, infoText, 'ui_text_dim' )) @@ -52,7 +56,7 @@ end -- ------------------------------------------------ --- --- Creates a new UIEquipmentList instance. +-- Creates a new UIInventoryList instance. -- function UIInventoryList:initialize( px, py, x, y, w, h, inventory ) UIElement.initialize( self, px, py, x, y, w, h ) @@ -61,11 +65,17 @@ function UIInventoryList:initialize( px, py, x, y, w, h, inventory ) self:refresh() end +--- +-- Recreates the inventory list. +-- function UIInventoryList:refresh() populateItemList( self ) generateStorageInfo( self ) end +--- +-- Draws the inventory list. +-- function UIInventoryList:draw() for _, item in ipairs( self.children ) do item:draw() @@ -102,6 +112,11 @@ function UIInventoryList:drop( item ) return false end +--- +-- Returns the Item the mouse cursor is currently over. Note that the Item is +-- actually wrapped into an UIInventoryItem. +-- @treturn Item The item under the mouse cursor. +-- function UIInventoryList:getItemBelowCursor() for _, uiItem in ipairs( self.children ) do if uiItem:isMouseOver() then @@ -110,6 +125,11 @@ function UIInventoryList:getItemBelowCursor() end end +--- +-- Checks if an item fits into the creature's inventory. +-- @tparam Item item The item to check for. +-- @treturn boolean True if the item fits, false otherwise. +-- function UIInventoryList:doesFit( item ) return self.inventory:doesFit( item:getWeight(), item:getVolume() ) end From 076771584234460c9895f1b1cb1ac56ffae87d1e Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Tue, 14 Aug 2018 00:24:37 +0200 Subject: [PATCH 28/93] Fix right-click starting a drag operation Right-clicking an item on the inventory screen now correctly selects it for the item stat area instead of starting a drag operation. Fixes #248. --- src/ui/screens/InventoryScreen.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ui/screens/InventoryScreen.lua b/src/ui/screens/InventoryScreen.lua index 7faf6965..8edadbd6 100644 --- a/src/ui/screens/InventoryScreen.lua +++ b/src/ui/screens/InventoryScreen.lua @@ -382,6 +382,7 @@ end function InventoryScreen:mousepressed( _, _, button ) if button == 2 then selectItem( self.lists, self.itemStats ) + return end drag( button, self.lists, self.dragboard, self.itemStats ) end From f5e04bc5b98b9f92f744a55bebbe29870d8b0060 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Tue, 14 Aug 2018 19:33:50 +0200 Subject: [PATCH 29/93] Fix scroll bar for item description Fixes #245. --- src/ui/screens/InventoryScreen.lua | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/ui/screens/InventoryScreen.lua b/src/ui/screens/InventoryScreen.lua index 8edadbd6..e7000cd3 100644 --- a/src/ui/screens/InventoryScreen.lua +++ b/src/ui/screens/InventoryScreen.lua @@ -388,16 +388,15 @@ function InventoryScreen:mousepressed( _, _, button ) end function InventoryScreen:mousereleased( _, _, _ ) - if not self.dragboard:hasDragContext() then + if self.dragboard:hasDragContext() then + local list = getListBelowCursor( self.lists ) + self.dragboard:drop( list ) + + -- Refresh lists in case volumes have changed. + refreshLists( self.lists ) return end - local list = getListBelowCursor( self.lists ) - self.dragboard:drop( list ) - - -- Refresh lists in case volumes have changed. - refreshLists( self.lists ) - self.itemStats:command( 'activate' ) end From 06d9db772acc31c3c790d6758368c38ae6482b58 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Tue, 14 Aug 2018 20:14:08 +0200 Subject: [PATCH 30/93] Remove outdated check for item tag 'all' --- src/items/ItemFactory.lua | 39 +++++++++++++++++---------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/src/items/ItemFactory.lua b/src/items/ItemFactory.lua index ea922c69..00c93c56 100644 --- a/src/items/ItemFactory.lua +++ b/src/items/ItemFactory.lua @@ -130,33 +130,28 @@ function ItemFactory.createRandomItem( tags, type, subType ) for id, template in pairs( items ) do if template.itemType == type then if not subType or template.subType == subType then - - if tags == 'all' then - list[#list + 1] = id - else - -- Check if the creature's tags allow items of this type. - local whitelisted, blacklisted - for _, itemTag in ipairs( template.tags ) do - whitelisted, blacklisted = false, false - for _, creatureTag in ipairs( tags.whitelist ) do - if itemTag == creatureTag then - whitelisted = true - end + -- Check if the creature's tags allow items of this type. + local whitelisted, blacklisted + for _, itemTag in ipairs( template.tags ) do + whitelisted, blacklisted = false, false + for _, creatureTag in ipairs( tags.whitelist ) do + if itemTag == creatureTag then + whitelisted = true end - for _, creatureTag in ipairs( tags.blacklist ) do - if itemTag == creatureTag then - blacklisted = true - end - end - if not whitelisted or blacklisted then - break + end + for _, creatureTag in ipairs( tags.blacklist ) do + if itemTag == creatureTag then + blacklisted = true end end - - if whitelisted and not blacklisted then - list[#list + 1] = id + if not whitelisted or blacklisted then + break end end + + if whitelisted and not blacklisted then + list[#list + 1] = id + end end end end From 548c9454d7e6beb0cced0f35dc04895dbac63cba Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Tue, 14 Aug 2018 20:42:44 +0200 Subject: [PATCH 31/93] Add debug info when creating equipment --- src/characters/CharacterFactory.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/characters/CharacterFactory.lua b/src/characters/CharacterFactory.lua index 96464c25..5e75f1c4 100644 --- a/src/characters/CharacterFactory.lua +++ b/src/characters/CharacterFactory.lua @@ -11,6 +11,7 @@ local BodyFactory = require( 'src.characters.body.BodyFactory' ) local ItemFactory = require( 'src.items.ItemFactory' ) local Util = require( 'src.util.Util' ) local Translator = require( 'src.util.Translator' ) +local Log = require( 'src.util.Log' ) -- ------------------------------------------------ -- Module @@ -115,6 +116,8 @@ local function createEquipment( character, factionType ) local inventory = body:getInventory() local tags = body:getTags() + Log.debug( string.format( 'Creating equipment [class: %s, id: %s, faction: %s]', character:getCreatureClass(), body:getID(), factionType ), 'CharacterFactory' ) + for _, slot in pairs( equipment:getSlots() ) do -- The player's characters should start mainly with guns. Shurikens, grenades -- and melee weapons should added as secondary weaponry. From 1c70a334f6c68603640d4b1dec70582d58dc8056 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Tue, 14 Aug 2018 21:34:49 +0200 Subject: [PATCH 32/93] Add a selector for the canvas size --- src/ui/Camera.lua | 14 ++++++++-- src/ui/screens/PrefabEditor.lua | 47 +++++++++++++++++++++------------ 2 files changed, 42 insertions(+), 19 deletions(-) diff --git a/src/ui/Camera.lua b/src/ui/Camera.lua index 41025f74..9e3cfa45 100644 --- a/src/ui/Camera.lua +++ b/src/ui/Camera.lua @@ -130,8 +130,8 @@ end --- -- Creates a new Camera instance. --- @tparam number mw The map's width. --- @tparam number mh The map's height. +-- @tparam number mw The horizontal boundary in which to contain the camera. +-- @tparam number mh The vertical boundary in which to contain the camera. -- @tparam number tw The width of the tiles used for map drawing. -- @tparam number th The height of the tiles used for map drawing. -- @treturn Camera A new instance of the Camera class. @@ -234,6 +234,16 @@ function Camera:setTargetPosition( nx, ny ) self.tx, self.ty = nx, ny end +--- +-- Updates the camera's boundaries. +-- @tparam number mw The horizontal boundary in which to contain the camera. +-- @tparam number mh The vertical boundary in which to contain the camera. +-- +function Camera:setBounds( w, h ) + self.mw = w + self.mh = h +end + -- ------------------------------------------------ -- Getters -- ------------------------------------------------ diff --git a/src/ui/screens/PrefabEditor.lua b/src/ui/screens/PrefabEditor.lua index 7bb44f3d..55f16dbc 100644 --- a/src/ui/screens/PrefabEditor.lua +++ b/src/ui/screens/PrefabEditor.lua @@ -33,6 +33,13 @@ local PrefabEditor = Screen:subclass( 'PrefabEditor' ) local SELECTOR_WIDTH = 10 local SELECTOR_HEIGHT = 10 +local CANVAS_SIZES = { + 'XS', + 'S', + 'M', + 'L', + 'XL' +} -- ------------------------------------------------ -- Private Functions -- ------------------------------------------------ @@ -87,6 +94,25 @@ local function createWorldObjectSelector( objectTemplates, tool ) return objectSelector end +local function createCanvasSelector( canvas, camera ) + local lx, ly = 1, 2 * ( SELECTOR_HEIGHT + 2 ) + local canvasSelector = UIPaginatedList( lx, ly, 0, 0, SELECTOR_WIDTH, SELECTOR_HEIGHT ) + + local buttons = {} + for i = 1, #CANVAS_SIZES do + local function callback() + canvas:setSize( CANVAS_SIZES[i] ) + camera:setBounds( canvas:getWidth(), canvas:getHeight() ) + end + + buttons[#buttons + 1] = UIButton( 0, 0, 0, 0, SELECTOR_WIDTH, 1, callback, CANVAS_SIZES[i], 'left' ) + end + + canvasSelector:setItems( buttons ) + + return canvasSelector +end + -- ------------------------------------------------ -- Public Methods -- ------------------------------------------------ @@ -107,9 +133,12 @@ function PrefabEditor:initialize() local objectTemplates = WorldObjectFactory.getTemplates() self.objectSelector = createWorldObjectSelector( objectTemplates, self.tool ) + self.canvasSelector = createCanvasSelector( self.canvas, self.camera ) + self.uiContainer = UIContainer() self.uiContainer:register( self.tileSelector ) self.uiContainer:register( self.objectSelector ) + self.uiContainer:register( self.canvasSelector ) end function PrefabEditor:receive( event, ... ) @@ -125,6 +154,7 @@ function PrefabEditor:draw() self.tileSelector:draw() self.objectSelector:draw() + self.canvasSelector:draw() self.camera:attach() self.canvas:draw() @@ -172,23 +202,6 @@ function PrefabEditor:keypressed( _, scancode ) self.tool:setMode( 'fill' ) end - if scancode == '1' then - self.canvas:setSize( 'XS' ) - self.camera = Camera( self.canvas:getWidth(), self.canvas:getHeight(), TexturePacks.getTileDimensions() ) - elseif scancode == '2' then - self.canvas:setSize( 'S' ) - self.camera = Camera( self.canvas:getWidth(), self.canvas:getHeight(), TexturePacks.getTileDimensions() ) - elseif scancode == '3' then - self.canvas:setSize( 'M' ) - self.camera = Camera( self.canvas:getWidth(), self.canvas:getHeight(), TexturePacks.getTileDimensions() ) - elseif scancode == '4' then - self.canvas:setSize( 'L' ) - self.camera = Camera( self.canvas:getWidth(), self.canvas:getHeight(), TexturePacks.getTileDimensions() ) - elseif scancode == '5' then - self.canvas:setSize( 'XL' ) - self.camera = Camera( self.canvas:getWidth(), self.canvas:getHeight(), TexturePacks.getTileDimensions() ) - end - if scancode == 'h' then self.canvas:toggleObjects() end From 337677d83a83cc1c4a6cdbcca4df09fae43b9099 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Tue, 14 Aug 2018 21:41:36 +0200 Subject: [PATCH 33/93] Add proper camera controls to prefab editor Fixes #241. --- src/Settings.lua | 9 ++++++++- src/ui/elements/UIContainer.lua | 14 ++++++++++++++ src/ui/screens/PrefabEditor.lua | 12 +++++++++++- 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/Settings.lua b/src/Settings.lua index 3a49d655..5c3fe633 100644 --- a/src/Settings.lua +++ b/src/Settings.lua @@ -21,7 +21,7 @@ local Settings = {} local FILE_NAME = 'settings.otr' local DEFAULT_SETTINGS = { - version = 6, + version = 7, general = { fullscreen = true, locale = 'en_EN', @@ -50,6 +50,12 @@ local DEFAULT_SETTINGS = { ['right'] = 'pan_camera_right', ['up'] = 'pan_camera_up', ['down'] = 'pan_camera_down' + }, + prefabeditor = { + ['left'] = 'pan_camera_left', + ['right'] = 'pan_camera_right', + ['up'] = 'pan_camera_up', + ['down'] = 'pan_camera_down' } } } @@ -59,6 +65,7 @@ local UNASSIGNED_SCANCODE = 'unassigned' Settings.INPUTLAYOUTS = {} Settings.INPUTLAYOUTS.COMBAT = 'combat' +Settings.INPUTLAYOUTS.PREFAB_EDITOR = 'prefabeditor' -- ------------------------------------------------ -- Private Variables diff --git a/src/ui/elements/UIContainer.lua b/src/ui/elements/UIContainer.lua index 006754cf..adb3583d 100644 --- a/src/ui/elements/UIContainer.lua +++ b/src/ui/elements/UIContainer.lua @@ -114,4 +114,18 @@ function UIContainer:mousecommand( cmd ) self.list[self.current]:mousecommand( cmd ) end +--- +-- Checks if the UIContainer has focus. This is the case if one of the +-- registered UIElements has focus. +-- @treturn boolean True if one of the UIElements has focus, false otherwise. +-- +function UIContainer:hasFocus() + for i = 1, #self.list do + if self.list[i]:hasFocus() then + return true + end + end + return false +end + return UIContainer diff --git a/src/ui/screens/PrefabEditor.lua b/src/ui/screens/PrefabEditor.lua index 55f16dbc..7e980e85 100644 --- a/src/ui/screens/PrefabEditor.lua +++ b/src/ui/screens/PrefabEditor.lua @@ -19,6 +19,7 @@ local PrefabBrush = require( 'src.ui.mapeditor.PrefabBrush' ) local UIContainer = require( 'src.ui.elements.UIContainer' ) local GridHelper = require( 'src.util.GridHelper' ) local UIPaginatedList = require( 'src.ui.elements.lists.UIPaginatedList' ) +local Settings = require( 'src.Settings' ) -- ------------------------------------------------ -- Module @@ -213,7 +214,16 @@ function PrefabEditor:keypressed( _, scancode ) if scancode == 'return' then self.uiContainer:command( 'activate' ) end - self.uiContainer:command( scancode ) + + if self.uiContainer:hasFocus() then + self.uiContainer:command( scancode ) + else + self.camera:input( Settings.mapInput( Settings.INPUTLAYOUTS.PREFAB_EDITOR, scancode ), true ) + end +end + +function PrefabEditor:keyreleased( _, scancode ) + self.camera:input( Settings.mapInput( Settings.INPUTLAYOUTS.PREFAB_EDITOR, scancode ), false ) end function PrefabEditor:mousemoved() From d41ac133cb90849fcddeb55bb26aa4519248fe4c Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Tue, 14 Aug 2018 21:53:47 +0200 Subject: [PATCH 34/93] Add controls for increasing / decreasing tool size --- src/Settings.lua | 4 +++- src/ui/screens/PrefabEditor.lua | 9 ++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Settings.lua b/src/Settings.lua index 5c3fe633..f3a90835 100644 --- a/src/Settings.lua +++ b/src/Settings.lua @@ -21,7 +21,7 @@ local Settings = {} local FILE_NAME = 'settings.otr' local DEFAULT_SETTINGS = { - version = 7, + version = 8, general = { fullscreen = true, locale = 'en_EN', @@ -52,6 +52,8 @@ local DEFAULT_SETTINGS = { ['down'] = 'pan_camera_down' }, prefabeditor = { + [']'] = 'increase_tool_size', + ['/'] = 'decrease_tool_size', ['left'] = 'pan_camera_left', ['right'] = 'pan_camera_right', ['up'] = 'pan_camera_up', diff --git a/src/ui/screens/PrefabEditor.lua b/src/ui/screens/PrefabEditor.lua index 7e980e85..91dc0199 100644 --- a/src/ui/screens/PrefabEditor.lua +++ b/src/ui/screens/PrefabEditor.lua @@ -187,11 +187,14 @@ end function PrefabEditor:keypressed( _, scancode ) if scancode == 'escape' then ScreenManager.push( 'prefabeditormenu', self.canvas ) + return end - if scancode == ']' then + local action = Settings.mapInput( Settings.INPUTLAYOUTS.PREFAB_EDITOR, scancode ) + + if action == 'increase_tool_size' then self.tool:increase() - elseif scancode == '/' then + elseif action == 'decrease_tool_size' then self.tool:decrease() end @@ -218,7 +221,7 @@ function PrefabEditor:keypressed( _, scancode ) if self.uiContainer:hasFocus() then self.uiContainer:command( scancode ) else - self.camera:input( Settings.mapInput( Settings.INPUTLAYOUTS.PREFAB_EDITOR, scancode ), true ) + self.camera:input( action, true ) end end From 5a21e2720a404ec8769df74e9e1167707405a6e3 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Tue, 14 Aug 2018 22:04:01 +0200 Subject: [PATCH 35/93] Add proper controls for tool selection --- src/Settings.lua | 5 ++++- src/ui/screens/PrefabEditor.lua | 6 +++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Settings.lua b/src/Settings.lua index f3a90835..be10bc3b 100644 --- a/src/Settings.lua +++ b/src/Settings.lua @@ -21,7 +21,7 @@ local Settings = {} local FILE_NAME = 'settings.otr' local DEFAULT_SETTINGS = { - version = 8, + version = 9, general = { fullscreen = true, locale = 'en_EN', @@ -54,6 +54,9 @@ local DEFAULT_SETTINGS = { prefabeditor = { [']'] = 'increase_tool_size', ['/'] = 'decrease_tool_size', + ['d'] = 'mode_draw', + ['e'] = 'mode_erase', + ['f'] = 'mode_fill', ['left'] = 'pan_camera_left', ['right'] = 'pan_camera_right', ['up'] = 'pan_camera_up', diff --git a/src/ui/screens/PrefabEditor.lua b/src/ui/screens/PrefabEditor.lua index 91dc0199..fc2eab74 100644 --- a/src/ui/screens/PrefabEditor.lua +++ b/src/ui/screens/PrefabEditor.lua @@ -198,11 +198,11 @@ function PrefabEditor:keypressed( _, scancode ) self.tool:decrease() end - if scancode == 'd' then + if action == 'mode_draw' then self.tool:setMode( 'draw' ) - elseif scancode == 'e' then + elseif action == 'mode_erase' then self.tool:setMode( 'erase' ) - elseif scancode == 'f' then + elseif action == 'mode_fill' then self.tool:setMode( 'fill' ) end From 7c82d6e3c0809eb2d5e983b21264cb663a476cff Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Tue, 14 Aug 2018 22:06:34 +0200 Subject: [PATCH 36/93] Add proper controls for hiding worldobjects --- src/Settings.lua | 3 ++- src/ui/screens/PrefabEditor.lua | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Settings.lua b/src/Settings.lua index be10bc3b..5d9e7adc 100644 --- a/src/Settings.lua +++ b/src/Settings.lua @@ -21,7 +21,7 @@ local Settings = {} local FILE_NAME = 'settings.otr' local DEFAULT_SETTINGS = { - version = 9, + version = 10, general = { fullscreen = true, locale = 'en_EN', @@ -57,6 +57,7 @@ local DEFAULT_SETTINGS = { ['d'] = 'mode_draw', ['e'] = 'mode_erase', ['f'] = 'mode_fill', + ['h'] = 'hide_worldobjects', ['left'] = 'pan_camera_left', ['right'] = 'pan_camera_right', ['up'] = 'pan_camera_up', diff --git a/src/ui/screens/PrefabEditor.lua b/src/ui/screens/PrefabEditor.lua index fc2eab74..5c48eec1 100644 --- a/src/ui/screens/PrefabEditor.lua +++ b/src/ui/screens/PrefabEditor.lua @@ -206,7 +206,7 @@ function PrefabEditor:keypressed( _, scancode ) self.tool:setMode( 'fill' ) end - if scancode == 'h' then + if action == 'hide_worldobjects' then self.canvas:toggleObjects() end From 4a64c75c467edd79c01a8c0f65d32129535e5ddc Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Tue, 14 Aug 2018 22:10:26 +0200 Subject: [PATCH 37/93] Add new controls to keybinding screen --- res/text/en_EN/ui_text.lua | 7 +++++++ src/ui/screens/KeybindingScreen.lua | 11 +++++++++++ 2 files changed, 18 insertions(+) diff --git a/res/text/en_EN/ui_text.lua b/res/text/en_EN/ui_text.lua index 125bec65..584fcdcc 100644 --- a/res/text/en_EN/ui_text.lua +++ b/res/text/en_EN/ui_text.lua @@ -71,6 +71,13 @@ locale.strings = { ['pan_camera_down'] = "Move camera down", ['ui_enter_key'] = "Press a key you want to asign to this action.\n\nPress escape to cancel.", + ['increase_tool_size'] = "Increase tool size", + ['decrease_tool_size'] = "Decrease tool size", + ['mode_draw'] = "Select drawing tool", + ['mode_erase'] = "Select eraser tool", + ['mode_fill'] = "Select filling tool", + ['hide_worldobjects'] = "Hide object layer", + -- Map Editor ['ui_mapeditor_save'] = "Save Layout", ['ui_mapeditor_load'] = "Load Layout", diff --git a/src/ui/screens/KeybindingScreen.lua b/src/ui/screens/KeybindingScreen.lua index 49d5703d..0e8ddea0 100644 --- a/src/ui/screens/KeybindingScreen.lua +++ b/src/ui/screens/KeybindingScreen.lua @@ -141,6 +141,17 @@ local function getKeyButtonList() createKeybinding( Settings.INPUTLAYOUTS.COMBAT, 'pan_camera_right' ), createKeybinding( Settings.INPUTLAYOUTS.COMBAT, 'pan_camera_up' ), createKeybinding( Settings.INPUTLAYOUTS.COMBAT, 'pan_camera_down' ), + + createKeybinding( Settings.INPUTLAYOUTS.PREFAB_EDITOR, 'increase_tool_size' ), + createKeybinding( Settings.INPUTLAYOUTS.PREFAB_EDITOR, 'decrease_tool_size' ), + createKeybinding( Settings.INPUTLAYOUTS.PREFAB_EDITOR, 'mode_draw' ), + createKeybinding( Settings.INPUTLAYOUTS.PREFAB_EDITOR, 'mode_erase' ), + createKeybinding( Settings.INPUTLAYOUTS.PREFAB_EDITOR, 'mode_fill' ), + createKeybinding( Settings.INPUTLAYOUTS.PREFAB_EDITOR, 'hide_worldobjects' ), + createKeybinding( Settings.INPUTLAYOUTS.PREFAB_EDITOR, 'pan_camera_left' ), + createKeybinding( Settings.INPUTLAYOUTS.PREFAB_EDITOR, 'pan_camera_right' ), + createKeybinding( Settings.INPUTLAYOUTS.PREFAB_EDITOR, 'pan_camera_up' ), + createKeybinding( Settings.INPUTLAYOUTS.PREFAB_EDITOR, 'pan_camera_down' ), } end From b3d13aa9c0bbe15ee99213c1b36f8628674a45ed Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Tue, 14 Aug 2018 22:29:02 +0200 Subject: [PATCH 38/93] Make prefab editor the default editor The game now switches to the prefab edtor by default when selecting the map editor option on the main menu. --- src/ui/screens/MainMenu.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/screens/MainMenu.lua b/src/ui/screens/MainMenu.lua index b9702af5..05e953fc 100644 --- a/src/ui/screens/MainMenu.lua +++ b/src/ui/screens/MainMenu.lua @@ -61,7 +61,7 @@ local function createButtons() -- Only show map editor if it has been activated in the options. if Settings.getIngameEditor() then - local mapEditorButton = UIButton( lx, ly, 0, 0, 10, 1, function() ScreenManager.switch( 'mapeditor' ) end, Translator.getText( 'ui_main_menu_mapeditor' )) + local mapEditorButton = UIButton( lx, ly, 0, 0, 10, 1, function() ScreenManager.switch( 'prefabeditor' ) end, Translator.getText( 'ui_main_menu_mapeditor' )) buttonList:addChild( mapEditorButton ) end From bfbc9fac21638f3eb7f5ce4c98b1928fafaf7212 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Tue, 14 Aug 2018 22:29:54 +0200 Subject: [PATCH 39/93] Show map editor option on main menu by default --- src/Settings.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Settings.lua b/src/Settings.lua index 5d9e7adc..699a3c90 100644 --- a/src/Settings.lua +++ b/src/Settings.lua @@ -21,11 +21,11 @@ local Settings = {} local FILE_NAME = 'settings.otr' local DEFAULT_SETTINGS = { - version = 10, + version = 11, general = { fullscreen = true, locale = 'en_EN', - mapeditor = false, + mapeditor = true, texturepack = 'default', mousepanning = false, invertedMessageLog = false, From e55de51a90162d9ab557b0a6f39d224fd8220696 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Tue, 14 Aug 2018 22:33:09 +0200 Subject: [PATCH 40/93] Remove notification about map editor activation --- res/text/en_EN/ui_text.lua | 1 - src/ui/screens/OptionsScreen.lua | 4 ---- 2 files changed, 5 deletions(-) diff --git a/res/text/en_EN/ui_text.lua b/res/text/en_EN/ui_text.lua index 584fcdcc..aa79ff63 100644 --- a/res/text/en_EN/ui_text.lua +++ b/res/text/en_EN/ui_text.lua @@ -45,7 +45,6 @@ locale.strings = { ['ui_unsaved_changes'] = "There are unsaved changes. Are you sure you want to quit?", ['ui_applied_settings'] = "Your settings have been updated.", ['ui_settings_ingame_editor'] = "Activate Map Editor:", - ['ui_settings_ingame_editor_active'] = "Once activated, the map editor can be accessed from the main menu. This currently is an early development version.", ['ui_settings_mouse_panning'] = "Mouse Panning:", ['ui_settings_invert_messagelog'] = "Invert message log:", ['ui_keybindings'] = "Edit Keybindings", diff --git a/src/ui/screens/OptionsScreen.lua b/src/ui/screens/OptionsScreen.lua index cf7d7e22..22eb0808 100644 --- a/src/ui/screens/OptionsScreen.lua +++ b/src/ui/screens/OptionsScreen.lua @@ -177,10 +177,6 @@ local function createIngameEditorOption( lx, ly ) -- The function to call when the value of the UISelectField changes. local function callback( val ) Settings.setIngameEditor( val ) - - if val then - ScreenManager.push( 'information', Translator.getText( 'ui_settings_ingame_editor_active' )) - end end -- Search the value corresponding to the currently selected option or From 8e6b678530689f0f6f3de47d9a0089ca21aab589 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Wed, 15 Aug 2018 00:31:19 +0200 Subject: [PATCH 41/93] Fix faulty camera bounds after loading of prefab Fixes #250. --- src/ui/screens/PrefabEditor.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ui/screens/PrefabEditor.lua b/src/ui/screens/PrefabEditor.lua index 5c48eec1..486e48ae 100644 --- a/src/ui/screens/PrefabEditor.lua +++ b/src/ui/screens/PrefabEditor.lua @@ -145,6 +145,7 @@ end function PrefabEditor:receive( event, ... ) if event == 'LOAD_LAYOUT' then self.canvas:load( ... ) + self.camera:setBounds( self.canvas:getWidth(), self.canvas:getHeight() ) end end From 4f4e0739fbfd7d501e8b03bf957d379400141c40 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Fri, 17 Aug 2018 16:16:23 +0200 Subject: [PATCH 42/93] Refactor grid generation --- src/map/procedural/ProceduralMapGenerator.lua | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/map/procedural/ProceduralMapGenerator.lua b/src/map/procedural/ProceduralMapGenerator.lua index 0ee4cbc0..dfc4ce27 100644 --- a/src/map/procedural/ProceduralMapGenerator.lua +++ b/src/map/procedural/ProceduralMapGenerator.lua @@ -186,23 +186,21 @@ end --- -- Creates an empty tile grid. --- @tparam number w The width of the grid in parcels. --- @tparam number h The height of the grid in parcels. +-- @tparam number w The grid's width in tiles. +-- @tparam number h The grid's height in tiles. -- @treturn table The new tile grid. --- @treturn number The new tile grid's width in tiles. --- @treturn number The new tile grid's height in height. -- local function createTileGrid( w, h ) local tiles = {} - for x = 1, w * PARCEL_SIZE.WIDTH do + for x = 1, w do tiles[x] = {} - for y = 1, h * PARCEL_SIZE.HEIGHT do + for y = 1, h do -- TODO Better algorithm for placing ground tiles. local id = love.math.random() > 0.7 and 'tile_soil' or 'tile_grass' tiles[x][y] = TileFactory.create( x, y, id ) end end - return tiles, w * PARCEL_SIZE.WIDTH, h * PARCEL_SIZE.HEIGHT + return tiles end --- @@ -266,8 +264,10 @@ function ProceduralMapGenerator:initialize( layout ) self.parcelGrid = ParcelGrid( self.layout.mapwidth, self.layout.mapheight ) self.parcelGrid:createNeighbours() + self.width, self.height = self.layout.mapwidth * PARCEL_SIZE.WIDTH, self.layout.mapheight * PARCEL_SIZE.HEIGHT + -- The actual tile map. - self.tileGrid, self.width, self.height = createTileGrid( self.layout.mapwidth, self.layout.mapheight ) + self.tileGrid = createTileGrid( self.width, self.height ) -- Spawnpoints. self.spawnpoints = { From 4f972eb2e54cc5fd60622e2c91ea0f25bba37b5c Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sat, 18 Aug 2018 13:34:19 +0200 Subject: [PATCH 43/93] Use utility function for picking random values --- src/map/procedural/ProceduralMapGenerator.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/map/procedural/ProceduralMapGenerator.lua b/src/map/procedural/ProceduralMapGenerator.lua index dfc4ce27..9180f0db 100644 --- a/src/map/procedural/ProceduralMapGenerator.lua +++ b/src/map/procedural/ProceduralMapGenerator.lua @@ -258,7 +258,7 @@ end -- function ProceduralMapGenerator:initialize( layout ) -- Use specific layout or select a random one. - self.layout = layout or layouts[love.math.random( #layouts )] + self.layout = layout or Util.pickRandomValue( layouts ) -- Generate empty parcel grid. self.parcelGrid = ParcelGrid( self.layout.mapwidth, self.layout.mapheight ) From 1872d29f9aab36726a7de2a0d395865704a36fc4 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sat, 18 Aug 2018 14:03:46 +0200 Subject: [PATCH 44/93] Remove outdated code for prefab rotation Rectangular prefabs have been removed and therefore don't have to be taken into account for the rotation. --- src/map/procedural/ProceduralMapGenerator.lua | 34 +++---------------- 1 file changed, 4 insertions(+), 30 deletions(-) diff --git a/src/map/procedural/ProceduralMapGenerator.lua b/src/map/procedural/ProceduralMapGenerator.lua index 9180f0db..6f21e66c 100644 --- a/src/map/procedural/ProceduralMapGenerator.lua +++ b/src/map/procedural/ProceduralMapGenerator.lua @@ -68,14 +68,10 @@ end -- @tparam Prefab prefab The prefab to place. -- @tparam number px The starting coordinates along the x-axis for this prefab. -- @tparam number py The starting coordinates along the y-axis for this prefab. --- @tparam number rotate The rotation to apply to the prefab before placing it. -- -local function placePrefab( tileGrid, prefab, px, py, rotate ) - local tiles = prefab.grid - - if rotate then - tiles = ArrayRotation.rotate( tiles, rotate ) - end +local function placePrefab( tileGrid, prefab, px, py ) + -- Rotate prefab randomly. + local tiles = ArrayRotation.rotate( prefab.grid, love.math.random( 0, 3 )) for tx = 1, #tiles do for ty = 1, #tiles[tx] do @@ -90,26 +86,6 @@ local function placePrefab( tileGrid, prefab, px, py, rotate ) end end ---- --- Determines a random rotation for a certain parcel layout. --- @tparam number pw The parcel's width. --- @tparam number ph The parcel's height. --- @treturn number The rotation direction [0, 3]. --- -local function rotateParcels( pw, ph ) - -- Square parcels can be rotated in all directions. - if pw == ph then - return love.math.random( 0, 3 ) - end - - -- Handle rotation for rectangular parcels. - if pw < ph then - return love.math.random() > 0.5 and 1 or 3 - elseif pw > ph then - return love.math.random() > 0.5 and 0 or 2 - end -end - --- -- Iterates over the parcel definitions for this map layout and tries to -- place prefabs for each of them. @@ -128,10 +104,8 @@ local function fillParcels( tileGrid, parcelGrid, parcels ) local prefab = PrefabLoader.getPrefab( type ) if prefab then - local rotation = rotateParcels( definition.w, definition.h ) - -- Place tiles and worldobjects. - placePrefab( tileGrid, prefab, x * PARCEL_SIZE.WIDTH, y * PARCEL_SIZE.HEIGHT, rotation ) + placePrefab( tileGrid, prefab, x * PARCEL_SIZE.WIDTH, y * PARCEL_SIZE.HEIGHT ) end end end From faa2f8e9743cb737ea0e9e59d2446a4d0df625ec Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sun, 19 Aug 2018 21:41:19 +0200 Subject: [PATCH 45/93] Refactor map generation --- src/CombatState.lua | 13 ++---- src/map/Map.lua | 24 +++++++--- src/map/procedural/ProceduralMapGenerator.lua | 44 ++++++------------- src/ui/screens/MapTest.lua | 14 ++---- 4 files changed, 38 insertions(+), 57 deletions(-) diff --git a/src/CombatState.lua b/src/CombatState.lua index d5a67d95..5f9c9d09 100644 --- a/src/CombatState.lua +++ b/src/CombatState.lua @@ -39,18 +39,13 @@ local FACTIONS = require( 'src.constants.FACTIONS' ) local function loadMap( savedMap ) local loader = MapLoader() local tiles, mw, mh = loader:recreateMap( savedMap ) - return Map( tiles, mw, mh ) + local map = Map( mw, mh ) + map:setTiles( tiles ) + return map end local function createMap() - local generator = ProceduralMapGenerator() - - local tiles = generator:getTiles() - local mw, mh = generator:getTileGridDimensions() - - local map = Map( tiles, mw, mh ) - map:setSpawnpoints( generator:getSpawnpoints() ) - return map + return ProceduralMapGenerator():createMap() end -- ------------------------------------------------ diff --git a/src/map/Map.lua b/src/map/Map.lua index 72edf60b..9684402a 100644 --- a/src/map/Map.lua +++ b/src/map/Map.lua @@ -68,20 +68,15 @@ end -- ------------------------------------------------ --- --- Initialises the Map by linking all tiles to their neighbours. --- @tparam table tiles A table containing all of the Map's tiles. +-- Initializes a new Map instance. -- @tparam number width The Map's width. -- @tparam number height The Map's height. -- -function Map:initialize( tiles, width, height ) +function Map:initialize( width, height ) Observable.initialize( self ) - self.tiles = tiles self.width = width self.height = height - - addNeighbours( self, self.tiles ) - observeTiles( self, self.tiles ) end --- @@ -200,6 +195,21 @@ function Map:getDimensions() return self.width, self.height end +-- ------------------------------------------------ +-- Setters +-- ------------------------------------------------ + +--- +-- Sets the tiles for this map and initializes them. +-- @tparam table tiles A table containing all of the Map's tiles. +-- +function Map:setTiles( tiles ) + self.tiles = tiles + + addNeighbours( self, self.tiles ) + observeTiles( self, self.tiles ) +end + --- -- Sets the spawnpoints for this map. -- @tparam table spawnpoints The spawnpoints for all factions on this map. diff --git a/src/map/procedural/ProceduralMapGenerator.lua b/src/map/procedural/ProceduralMapGenerator.lua index 6f21e66c..9db4260d 100644 --- a/src/map/procedural/ProceduralMapGenerator.lua +++ b/src/map/procedural/ProceduralMapGenerator.lua @@ -16,6 +16,7 @@ local TileFactory = require( 'src.map.tiles.TileFactory' ) local WorldObjectFactory = require( 'src.map.worldobjects.WorldObjectFactory' ) local Util = require( 'src.util.Util' ) local Compressor = require( 'src.util.Compressor' ) +local Map = require( 'src.map.Map' ) -- ------------------------------------------------ -- Module @@ -227,13 +228,20 @@ end -- ------------------------------------------------ --- --- Initializes the ProceduralMapGenerator instance. --- @tparam table layout The layout definition to use for map creation (optional). +-- Creates a new procedural map +-- @tparam table layout The layout definition to use for map creation (optional). +-- @treturn Map The newly generated map. -- -function ProceduralMapGenerator:initialize( layout ) +function ProceduralMapGenerator:createMap( layout ) -- Use specific layout or select a random one. self.layout = layout or Util.pickRandomValue( layouts ) + -- Calculate the size of the tile grid. + self.width, self.height = self.layout.mapwidth * PARCEL_SIZE.WIDTH, self.layout.mapheight * PARCEL_SIZE.HEIGHT + + -- Create an empty map. + local map = Map( self.width, self.height ) + -- Generate empty parcel grid. self.parcelGrid = ParcelGrid( self.layout.mapwidth, self.layout.mapheight ) self.parcelGrid:createNeighbours() @@ -255,35 +263,11 @@ function ProceduralMapGenerator:initialize( layout ) spawnRoads( self.parcelGrid, self.tileGrid ) spawnFoliage( self.parcelGrid, self.tileGrid ) createSpawnPoints( self.spawnpoints, self.layout.spawns ) -end - --- ------------------------------------------------ --- Getters --- ------------------------------------------------ - ---- --- Returns the spawnpoints for the generated map. --- @treturn table The spawns for this map. --- -function ProceduralMapGenerator:getSpawnpoints() - return self.spawnpoints -end ---- --- Returns the tile grid of the map. --- @treturn table The tile grid of this map. --- -function ProceduralMapGenerator:getTiles() - return self.tileGrid -end + map:setTiles( self.tileGrid ) + map:setSpawnpoints( self.spawnpoints ) ---- --- Returns the dimensions of the map in tiles. --- @treturn number The new tile grid's width in tiles. --- @treturn number The new tile grid's height in height. --- -function ProceduralMapGenerator:getTileGridDimensions() - return self.width, self.height + return map end return ProceduralMapGenerator diff --git a/src/ui/screens/MapTest.lua b/src/ui/screens/MapTest.lua index 8b9583da..15a6feda 100644 --- a/src/ui/screens/MapTest.lua +++ b/src/ui/screens/MapTest.lua @@ -13,7 +13,6 @@ local Camera = require( 'src.ui.Camera' ) local PrefabLoader = require( 'src.map.procedural.PrefabLoader' ) local ProceduralMapGenerator = require( 'src.map.procedural.ProceduralMapGenerator' ) local TexturePacks = require( 'src.ui.texturepacks.TexturePacks' ) -local Map = require( 'src.map.Map' ) local Settings = require( 'src.Settings' ) -- ------------------------------------------------ @@ -27,15 +26,7 @@ local MapTest = Screen:subclass( 'MapTest' ) -- ------------------------------------------------ local function createMap( layout ) - local generator = ProceduralMapGenerator( layout ) - - local tiles = generator:getTiles() - local mw, mh = generator:getTileGridDimensions() - - local map = Map( tiles, mw, mh ) - map:setSpawnpoints( generator:getSpawnpoints() ) - - return map, mw, mh + return ProceduralMapGenerator():createMap( layout ) end -- ------------------------------------------------ @@ -46,7 +37,8 @@ function MapTest:initialize( layout ) ProceduralMapGenerator.load() PrefabLoader.load() - self.map, self.mw, self.mh = createMap( layout ) + self.map = createMap( layout ) + self.mw, self.mh = self.map:getDimensions() self.mapPainter = MapPainter( self.map ) self.camera = Camera( self.mw, self.mh, TexturePacks.getTileDimensions() ) end From b8062139d0b081577f71585b9c068a98e12f1327 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 20 Aug 2018 01:41:00 +0200 Subject: [PATCH 46/93] Refactor neighbour creation --- src/map/Map.lua | 48 ++++++++++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/src/map/Map.lua b/src/map/Map.lua index 9684402a..e04e951c 100644 --- a/src/map/Map.lua +++ b/src/map/Map.lua @@ -22,32 +22,44 @@ local Map = Observable:subclass( 'Map' ) local DIRECTION = require( 'src.constants.DIRECTION' ) +local DIRECTION_MODIFIERS = { + [DIRECTION.NORTH] = { x = 0, y = -1 }, + [DIRECTION.SOUTH] = { x = 0, y = 1 }, + [DIRECTION.EAST] = { x = 1, y = 0 }, + [DIRECTION.WEST] = { x = -1, y = 0 }, + [DIRECTION.NORTH_EAST] = { x = 1, y = -1 }, + [DIRECTION.NORTH_WEST] = { x = -1, y = -1 }, + [DIRECTION.SOUTH_EAST] = { x = 1, y = 1 }, + [DIRECTION.SOUTH_WEST] = { x = -1, y = 1 } +} + -- ------------------------------------------------ -- Private Methods -- ------------------------------------------------ --- -- Gives each tile a reference to its neighbours. --- @tparam Map self The map instance to use. --- @tparam table tiles A table containing all of the Map's tiles. +-- @tparam Map self The map instance to use. +-- @tparam number x The tile position along the x-axis. +-- @tparam number y The tile position along the y-axis. -- -local function addNeighbours( self, tiles ) - for x = 1, #tiles do - for y = 1, #tiles[x] do - local neighbours = {} - - neighbours[DIRECTION.NORTH] = self:getTileAt( x , y - 1 ) - neighbours[DIRECTION.SOUTH] = self:getTileAt( x , y + 1 ) - neighbours[DIRECTION.NORTH_EAST] = self:getTileAt( x + 1, y - 1 ) - neighbours[DIRECTION.NORTH_WEST] = self:getTileAt( x - 1, y - 1 ) - neighbours[DIRECTION.SOUTH_EAST] = self:getTileAt( x + 1, y + 1 ) - neighbours[DIRECTION.SOUTH_WEST] = self:getTileAt( x - 1, y + 1 ) - neighbours[DIRECTION.EAST] = self:getTileAt( x + 1, y ) - neighbours[DIRECTION.WEST] = self:getTileAt( x - 1, y ) - - tiles[x][y]:addNeighbours( neighbours ) - end +local function createTileNeighbours( self, x, y ) + local neighbours = {} + for direction, modifier in ipairs( DIRECTION_MODIFIERS ) do + neighbours[direction] = self:getTileAt( x + modifier.x, y + modifier.y ) end + return neighbours +end + +--- +-- Gives each tile a reference to its neighbours. +-- @tparam Map self The map instance to use. +-- +local function addNeighbours( self ) + self:iterate( function( tile, x, y ) + -- Create tile neighbours + tile:addNeighbours( createTileNeighbours( self, x, y )) + end) end --- From abbbc54ef3098c0b22d145c8d59afdd765d13d7e Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 20 Aug 2018 01:45:00 +0200 Subject: [PATCH 47/93] Remove superfluous function --- src/map/MapLoader.lua | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/map/MapLoader.lua b/src/map/MapLoader.lua index 18145470..626085bc 100644 --- a/src/map/MapLoader.lua +++ b/src/map/MapLoader.lua @@ -22,17 +22,6 @@ local MapLoader = Class( 'MapLoader' ) -- Private Methods -- ------------------------------------------------ ---- --- Recreates a tile. --- @tparam number x The tile's location along the x-axis. --- @tparam number y The tile's location along the y-axis. --- @tparam string id The id of the tile to create. --- @treturn Tile The loaded tile. --- -local function recreateTile( x, y, id ) - return TileFactory.create( x, y, id ) -end - --- -- Recreates a world object. -- @tparam string id The id of the world object to create. @@ -62,7 +51,7 @@ local function loadSavedTiles( savedTiles ) local loadedTiles = {} for _, tile in ipairs( savedTiles ) do -- Recreate the tile. - local recreatedTile = recreateTile( tile.x, tile.y, tile.id ) + local recreatedTile = TileFactory.create( tile.x, tile.y, tile.id ) -- Recreate any worldobject that was located on the tile. if tile.worldObject then From ee6bbdb9280e5147826c2bf1cfaf970e0a4cbf79 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sun, 19 Aug 2018 21:48:36 +0200 Subject: [PATCH 48/93] Create empty grids for all needed layers --- src/map/Map.lua | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/map/Map.lua b/src/map/Map.lua index e04e951c..845d3339 100644 --- a/src/map/Map.lua +++ b/src/map/Map.lua @@ -75,6 +75,21 @@ local function observeTiles( self, tiles ) end end +--- +-- Creates an empty two dimensional array. +-- @return table The newly created grid. +-- +local function createEmptyGrid( width, height ) + local grid = {} + for x = 1, width do + for y = 1, height do + grid[x] = grid[x] or {} + grid[x][y] = nil + end + end + return grid +end + -- ------------------------------------------------ -- Public Methods -- ------------------------------------------------ @@ -87,6 +102,10 @@ end function Map:initialize( width, height ) Observable.initialize( self ) + self.tiles = createEmptyGrid( width, height ) + self.worldObjects = createEmptyGrid( width, height ) + self.characters = createEmptyGrid( width, height ) + self.width = width self.height = height end From 258fbae22f2998a55584fd7afdc3cdba622f4da3 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sun, 19 Aug 2018 22:18:59 +0200 Subject: [PATCH 49/93] Create tiles directly on the new map instance --- src/map/Map.lua | 15 ++++-- src/map/procedural/ProceduralMapGenerator.lua | 53 ++++++++----------- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/map/Map.lua b/src/map/Map.lua index 845d3339..f540ffa8 100644 --- a/src/map/Map.lua +++ b/src/map/Map.lua @@ -231,12 +231,19 @@ end -- ------------------------------------------------ --- --- Sets the tiles for this map and initializes them. --- @tparam table tiles A table containing all of the Map's tiles. +-- Sets a tile to a specific position on the tile layer. +-- @tparam number x The target position along the x-axis. +-- @tparam number y The target position along the y-axis. +-- @tparam Tile tile The tile to set to the grid. -- -function Map:setTiles( tiles ) - self.tiles = tiles +function Map:setTileAt( x, y, tile ) + self.tiles[x][y] = tile +end +--- +-- TODO remove! +-- +function Map:initGrid() addNeighbours( self, self.tiles ) observeTiles( self, self.tiles ) end diff --git a/src/map/procedural/ProceduralMapGenerator.lua b/src/map/procedural/ProceduralMapGenerator.lua index 9db4260d..55e5e693 100644 --- a/src/map/procedural/ProceduralMapGenerator.lua +++ b/src/map/procedural/ProceduralMapGenerator.lua @@ -65,23 +65,25 @@ end --- -- Places the tiles and world objects belonging to this prefab. --- @tparam table tileGrid The tile grid to place the prefab on. --- @tparam Prefab prefab The prefab to place. --- @tparam number px The starting coordinates along the x-axis for this prefab. --- @tparam number py The starting coordinates along the y-axis for this prefab. +-- @tparam Map map The map to place the prefab on. +-- @tparam Prefab prefab The prefab to place. +-- @tparam number px The starting coordinates along the x-axis for this prefab. +-- @tparam number py The starting coordinates along the y-axis for this prefab. -- -local function placePrefab( tileGrid, prefab, px, py ) +local function placePrefab( map, prefab, px, py ) -- Rotate prefab randomly. local tiles = ArrayRotation.rotate( prefab.grid, love.math.random( 0, 3 )) for tx = 1, #tiles do for ty = 1, #tiles[tx] do + local mapX, mapY = tx + px, ty + py + if tiles[tx][ty].tile then - tileGrid[tx + px][ty + py] = TileFactory.create( tx + px, ty + py, tiles[tx][ty].tile ) + map:setTileAt( mapX, mapY, TileFactory.create( mapX, mapY, tiles[tx][ty].tile )) end if tiles[tx][ty].worldObject then - tileGrid[tx + px][ty + py]:addWorldObject( WorldObjectFactory.create( tiles[tx][ty].worldObject )) + -- tileGrid[tx + px][ty + py]:addWorldObject( WorldObjectFactory.create( tiles[tx][ty].worldObject )) end end end @@ -90,11 +92,11 @@ end --- -- Iterates over the parcel definitions for this map layout and tries to -- place prefabs for each of them. --- @tparam table tileGrid The tile grid to place the prefab on. +-- @tparam Map map The map to place the prefab on. -- @tparam ParcelGrid parcelGrid The parcel grid to fill. -- @tparam table parcels The parcel definitions. -- -local function fillParcels( tileGrid, parcelGrid, parcels ) +local function fillParcels( map, parcelGrid, parcels ) for type, definitions in pairs( parcels ) do Log.debug( string.format( 'Placing %s parcels.', type ), 'ProceduralMapGenerator' ) @@ -106,7 +108,7 @@ local function fillParcels( tileGrid, parcelGrid, parcels ) local prefab = PrefabLoader.getPrefab( type ) if prefab then -- Place tiles and worldobjects. - placePrefab( tileGrid, prefab, x * PARCEL_SIZE.WIDTH, y * PARCEL_SIZE.HEIGHT ) + placePrefab( map, prefab, x * PARCEL_SIZE.WIDTH, y * PARCEL_SIZE.HEIGHT ) end end end @@ -160,22 +162,17 @@ local function spawnRoads( parcelGrid, tileGrid ) end --- --- Creates an empty tile grid. --- @tparam number w The grid's width in tiles. --- @tparam number h The grid's height in tiles. --- @treturn table The new tile grid. +-- Fills the map randomly with dirt and grass tiles. +-- @tparam Map map The map to fill. -- -local function createTileGrid( w, h ) - local tiles = {} - for x = 1, w do - tiles[x] = {} - for y = 1, h do +local function fillMap( map, width, height ) + for x = 1, width do + for y = 1, height do -- TODO Better algorithm for placing ground tiles. local id = love.math.random() > 0.7 and 'tile_soil' or 'tile_grass' - tiles[x][y] = TileFactory.create( x, y, id ) + map:setTileAt( x, y, TileFactory.create( x, y, id )) end end - return tiles end --- @@ -246,11 +243,6 @@ function ProceduralMapGenerator:createMap( layout ) self.parcelGrid = ParcelGrid( self.layout.mapwidth, self.layout.mapheight ) self.parcelGrid:createNeighbours() - self.width, self.height = self.layout.mapwidth * PARCEL_SIZE.WIDTH, self.layout.mapheight * PARCEL_SIZE.HEIGHT - - -- The actual tile map. - self.tileGrid = createTileGrid( self.width, self.height ) - -- Spawnpoints. self.spawnpoints = { allied = {}, @@ -258,13 +250,14 @@ function ProceduralMapGenerator:createMap( layout ) enemy = {} } - fillParcels( self.tileGrid, self.parcelGrid, self.layout.prefabs ) + fillMap( map, map:getDimensions() ) + fillParcels( map, self.parcelGrid, self.layout.prefabs ) - spawnRoads( self.parcelGrid, self.tileGrid ) - spawnFoliage( self.parcelGrid, self.tileGrid ) + -- spawnRoads( self.parcelGrid, self.tileGrid ) + -- spawnFoliage( self.parcelGrid, self.tileGrid ) createSpawnPoints( self.spawnpoints, self.layout.spawns ) - map:setTiles( self.tileGrid ) + map:initGrid() -- TODO remove map:setSpawnpoints( self.spawnpoints ) return map From 5bd6016261a296ac913d992c6e3a3e8169dec269 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sun, 19 Aug 2018 22:23:04 +0200 Subject: [PATCH 50/93] Readd road creation --- src/map/procedural/ProceduralMapGenerator.lua | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/map/procedural/ProceduralMapGenerator.lua b/src/map/procedural/ProceduralMapGenerator.lua index 55e5e693..1c6e1ab5 100644 --- a/src/map/procedural/ProceduralMapGenerator.lua +++ b/src/map/procedural/ProceduralMapGenerator.lua @@ -143,10 +143,10 @@ end --- -- Spawns roads in designated parcels. -- TODO Replace with prefab based system. +-- @tparam Map map The map to place the prefab on. -- @tparam ParcelGrid parcelGrid The parcel grid to read the parcel definitions from. --- @tparam table tileGrid The tile grid to fill. -- -local function spawnRoads( parcelGrid, tileGrid ) +local function spawnRoads( map, parcelGrid ) parcelGrid:iterate( function( parcel, x, y ) if parcel:getType() ~= 'ROAD' then return @@ -155,7 +155,8 @@ local function spawnRoads( parcelGrid, tileGrid ) local tx, ty = x * PARCEL_SIZE.WIDTH, y * PARCEL_SIZE.HEIGHT for w = 1, PARCEL_SIZE.WIDTH do for h = 1, PARCEL_SIZE.HEIGHT do - tileGrid[tx + w][ty + h] = TileFactory.create( tx + w, ty + h, 'tile_asphalt' ) + local mapX, mapY = tx + w, ty + h + map:setTileAt( mapX, mapY, TileFactory.create( mapX, mapY, 'tile_asphalt' )) end end end) @@ -253,7 +254,7 @@ function ProceduralMapGenerator:createMap( layout ) fillMap( map, map:getDimensions() ) fillParcels( map, self.parcelGrid, self.layout.prefabs ) - -- spawnRoads( self.parcelGrid, self.tileGrid ) + spawnRoads( map, self.parcelGrid ) -- spawnFoliage( self.parcelGrid, self.tileGrid ) createSpawnPoints( self.spawnpoints, self.layout.spawns ) From 41707d4282a344f2f602ec8ea96b0d2ae9773a37 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sun, 19 Aug 2018 22:30:34 +0200 Subject: [PATCH 51/93] Create worldobjects on their own dedicated layer --- src/map/Map.lua | 10 ++++++++++ src/map/procedural/ProceduralMapGenerator.lua | 15 ++++++++------- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/map/Map.lua b/src/map/Map.lua index f540ffa8..c674d56a 100644 --- a/src/map/Map.lua +++ b/src/map/Map.lua @@ -240,6 +240,16 @@ function Map:setTileAt( x, y, tile ) self.tiles[x][y] = tile end +--- +-- Sets a worldObject to a specific position on the worldObject layer. +-- @tparam number x The target position along the x-axis. +-- @tparam number y The target position along the y-axis. +-- @tparam WorldObject The worldObject to set to the grid. +-- +function Map:setWorldObjectAt( x, y, worldObject ) + self.worldObjects[x][y] = worldObject +end + --- -- TODO remove! -- diff --git a/src/map/procedural/ProceduralMapGenerator.lua b/src/map/procedural/ProceduralMapGenerator.lua index 1c6e1ab5..88f4170c 100644 --- a/src/map/procedural/ProceduralMapGenerator.lua +++ b/src/map/procedural/ProceduralMapGenerator.lua @@ -83,7 +83,7 @@ local function placePrefab( map, prefab, px, py ) end if tiles[tx][ty].worldObject then - -- tileGrid[tx + px][ty + py]:addWorldObject( WorldObjectFactory.create( tiles[tx][ty].worldObject )) + map:setWorldObjectAt( mapX, mapY, WorldObjectFactory.create( tiles[tx][ty].worldObject )) end end end @@ -116,10 +116,10 @@ end --- -- Spawns trees in designated parcels. +-- @tparam Map map The map to place the prefab on. -- @tparam ParcelGrid parcelGrid The parcel grid to read the parcel definitions from. --- @tparam table tileGrid The tile grid to fill. -- -local function spawnFoliage( parcelGrid, tileGrid ) +local function spawnFoliage( map, parcelGrid ) parcelGrid:iterate( function( parcel, x, y ) if parcel:getType() ~= 'FOLIAGE' then return @@ -128,12 +128,13 @@ local function spawnFoliage( parcelGrid, tileGrid ) local n = parcel:countNeighbours() local tx, ty = x * PARCEL_SIZE.WIDTH, y * PARCEL_SIZE.HEIGHT - for w = 1, PARCEL_SIZE.WIDTH do - for h = 1, PARCEL_SIZE.HEIGHT do + for px = 1, PARCEL_SIZE.WIDTH do + for py = 1, PARCEL_SIZE.HEIGHT do + local mapX, mapY = tx + px, ty + py -- Increase density based on count of neighbouring foliage tiles. if love.math.random() < n/10 then - tileGrid[tx + w][ty + h]:addWorldObject( WorldObjectFactory.create( 'worldobject_tree' )) + map:setWorldObjectAt( mapX, mapY, WorldObjectFactory.create( 'worldobject_tree' )) end end end @@ -255,7 +256,7 @@ function ProceduralMapGenerator:createMap( layout ) fillParcels( map, self.parcelGrid, self.layout.prefabs ) spawnRoads( map, self.parcelGrid ) - -- spawnFoliage( self.parcelGrid, self.tileGrid ) + spawnFoliage( map, self.parcelGrid ) createSpawnPoints( self.spawnpoints, self.layout.spawns ) map:initGrid() -- TODO remove From 1340b2d2987adc6902f5b221046c473441290a2f Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sun, 19 Aug 2018 22:41:16 +0200 Subject: [PATCH 52/93] Fix drawing for world objects --- src/map/Map.lua | 2 +- src/ui/MapPainter.lua | 44 +++++++++++++++++++++---------------------- 2 files changed, 22 insertions(+), 24 deletions(-) diff --git a/src/map/Map.lua b/src/map/Map.lua index c674d56a..ac2a9090 100644 --- a/src/map/Map.lua +++ b/src/map/Map.lua @@ -117,7 +117,7 @@ end function Map:iterate( callback ) for x = 1, self.width do for y = 1, self.height do - callback( self.tiles[x][y], x, y ) + callback( x, y, self.tiles[x][y], self.worldObjects[x][y] ) end end end diff --git a/src/ui/MapPainter.lua b/src/ui/MapPainter.lua index 7c4099e1..27e2e709 100644 --- a/src/ui/MapPainter.lua +++ b/src/ui/MapPainter.lua @@ -85,7 +85,7 @@ local CONNECTION_BITMASK = { -- local function initSpritebatch( map, spritebatch ) local tw, th = TexturePacks.getTileDimensions() - map:iterate( function( tile, x, y ) + map:iterate( function( x, y, tile, _ ) local id = spritebatch:add( TexturePacks.getSprite( 'tile_empty' ), x * tw, y * th ) tile:setSpriteID( id ) tile:setDirty( true ) @@ -95,11 +95,12 @@ end --- -- Selects a color which to use when a tile is drawn based on its contents. --- @tparam Tile tile The tile to choose a color for. --- @tparam Faction faction The faction to draw for. --- @treturn table A table containing RGBA values. +-- @tparam Tile tile The tile to choose a color for. +-- @tparam WorldObject worldObject The worldobject to choose a color for. +-- @tparam Faction faction The faction to draw for. +-- @treturn table A table containing RGBA values. -- -local function selectTileColor( tile, faction ) +local function selectTileColor( tile, worldObject, faction ) -- If there is a faction we check which tiles are currently seen and highlight -- the active character. if faction then @@ -123,8 +124,8 @@ local function selectTileColor( tile, faction ) return TexturePacks.getColor( items[1]:getID() ) end - if tile:hasWorldObject() then - return TexturePacks.getColor( tile:getWorldObject():getID() ) + if worldObject then + return TexturePacks.getColor( worldObject:getID() ) end return TexturePacks.getColor( tile:getID() ) @@ -179,24 +180,21 @@ local function selectWorldObjectSprite( worldObject, tile ) -- Check if the world object sprite connects to adjacent sprites. local connections = worldObject:getConnections() if connections then - local neighbours = tile:getNeighbours() - local result = checkConnection( connections, neighbours[DIRECTION.NORTH], 1 ) + - checkConnection( connections, neighbours[DIRECTION.EAST], 2 ) + - checkConnection( connections, neighbours[DIRECTION.SOUTH], 4 ) + - checkConnection( connections, neighbours[DIRECTION.WEST], 8 ) - return TexturePacks.getSprite( worldObject:getID(), CONNECTION_BITMASK[result] ) + -- FIXME + return TexturePacks.getSprite( worldObject:getID(), CONNECTION_BITMASK[0] ) end return TexturePacks.getSprite( worldObject:getID() ) end --- --- Selects a sprite from the tileset based on the tile and its contents. --- @tparam Tile tile The tile to choose a sprite for. --- @tparam Faction faction The faction to draw for. --- @treturn Quad A quad pointing to a sprite on the tileset. +-- Selects a sprite from the tileset based on the contents of a tile. +-- @tparam Tile tile The tile to choose a sprite for. +-- @tparam WorldObject worldObject The worldObject to choose a sprite for. +-- @tparam Faction faction The faction to draw for. +-- @treturn Quad A quad pointing to a sprite on the tileset. -- -local function selectTileSprite( tile, faction ) +local function selectTileSprite( tile, worldObject, faction ) if tile:isOccupied() and faction and faction:canSee( tile ) then return selectCharacterTile( tile ) end @@ -206,8 +204,8 @@ local function selectTileSprite( tile, faction ) return TexturePacks.getSprite( items[1]:getID() ) end - if tile:hasWorldObject() then - return selectWorldObjectSprite( tile:getWorldObject(), tile ) + if worldObject then + return selectWorldObjectSprite( worldObject, tile ) end return TexturePacks.getSprite( tile:getID() ) @@ -222,10 +220,10 @@ end -- local function updateSpritebatch( spritebatch, map, faction ) local tw, th = TexturePacks.getTileDimensions() - map:iterate( function( tile, x, y ) + map:iterate( function( x, y, tile, worldObject ) if tile:isDirty() then - spritebatch:setColor( selectTileColor( tile, faction )) - spritebatch:set( tile:getSpriteID(), selectTileSprite( tile, faction ), x * tw, y * th ) + spritebatch:setColor( selectTileColor( tile, worldObject, faction )) + spritebatch:set( tile:getSpriteID(), selectTileSprite( tile, worldObject, faction ), x * tw, y * th ) tile:setDirty( false ) end end) From 1d0571048367be7021acbfcde2d483756dbfd40d Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sun, 19 Aug 2018 23:18:52 +0200 Subject: [PATCH 53/93] Fix drawing of connectible worldObjects --- src/map/Map.lua | 32 ++++++++++++++++++++++++---- src/map/worldobjects/WorldObject.lua | 17 +++++++++++++++ src/ui/MapPainter.lua | 20 ++++++++++------- 3 files changed, 57 insertions(+), 12 deletions(-) diff --git a/src/map/Map.lua b/src/map/Map.lua index ac2a9090..92d00995 100644 --- a/src/map/Map.lua +++ b/src/map/Map.lua @@ -37,7 +37,6 @@ local DIRECTION_MODIFIERS = { -- Private Methods -- ------------------------------------------------ ---- -- Gives each tile a reference to its neighbours. -- @tparam Map self The map instance to use. -- @tparam number x The tile position along the x-axis. @@ -51,14 +50,28 @@ local function createTileNeighbours( self, x, y ) return neighbours end +local function createWorldObjectNeighbours( self, x, y ) + local neighbours = {} + for direction, modifier in ipairs( DIRECTION_MODIFIERS ) do + neighbours[direction] = self:getWorldObjectAt( x + modifier.x, y + modifier.y ) + end + return neighbours +end + --- --- Gives each tile a reference to its neighbours. +-- Creates neighbours for tiles and worldobjects. -- @tparam Map self The map instance to use. -- local function addNeighbours( self ) - self:iterate( function( tile, x, y ) + self:iterate( function( x, y, tile, worldObject ) -- Create tile neighbours tile:addNeighbours( createTileNeighbours( self, x, y )) + + -- Create worldObject neighbours + -- Note: Not every grid space will be occupied by a world object! + if worldObject then + worldObject:setNeighbours( createWorldObjectNeighbours( self, x, y )) + end end) end @@ -217,6 +230,16 @@ function Map:getTileAt( x, y ) return self.tiles[x] and self.tiles[x][y] end +--- +-- Returns the WorldObject at the given coordinates. +-- @tparam number x The position along the x-axis. +-- @tparam number y The position along the y-axis. +-- @treturn WorldObject The WorldObject at the given position. +-- +function Map:getWorldObjectAt( x, y ) + return self.worldObjects[x] and self.worldObjects[x][y] +end + --- -- Returns the map's dimensions. -- @treturn number The map's width. @@ -254,7 +277,8 @@ end -- TODO remove! -- function Map:initGrid() - addNeighbours( self, self.tiles ) + addNeighbours( self ) + observeTiles( self, self.tiles ) end diff --git a/src/map/worldobjects/WorldObject.lua b/src/map/worldobjects/WorldObject.lua index fa618102..a898ec46 100644 --- a/src/map/worldobjects/WorldObject.lua +++ b/src/map/worldobjects/WorldObject.lua @@ -194,6 +194,14 @@ function WorldObject:getConnections() return self.connections end +--- +-- Returns a table containing this worldObject's neighbours. +-- @treturn table A table containing the neighbouring worldObjects. +-- +function WorldObject:getNeighbours() + return self.neighbours +end + --- -- Checks wether the WorldObject is destructible. -- @treturn boolean True if the WorldObject is destructible. @@ -231,6 +239,15 @@ end -- Setters -- ------------------------------------------------ +--- +-- Adds a table containing the neighbouring worldObjects. +-- Note that some neighbours might be nil. +-- @tparam table neighbours A table containing the neighbouring worldObjects. +-- +function WorldObject:setNeighbours( neighbours ) + self.neighbours = neighbours +end + --- -- Sets the WorldObject's blocksVision attribute. -- @tparam boolean blocksVision The new attribute value. diff --git a/src/ui/MapPainter.lua b/src/ui/MapPainter.lua index 27e2e709..e9fbfcf5 100644 --- a/src/ui/MapPainter.lua +++ b/src/ui/MapPainter.lua @@ -150,8 +150,8 @@ end -- @treturn number The value indicating a match (0 if the world object doesn't match). -- local function checkConnection( connections, neighbour, value ) - if neighbour and neighbour:hasWorldObject() then - local group = neighbour:getWorldObject():getGroup() + if neighbour then + local group = neighbour:getGroup() if group then for i = 1, #connections do if connections[i] == group then @@ -164,11 +164,11 @@ local function checkConnection( connections, neighbour, value ) end --- --- Selects the tile to use for drawing a worldobject. --- @tparam WorldObject worldObject The worldobject to pick a sprite for. +-- Selects the sprite to use for drawing a worldObject. +-- @tparam WorldObject worldObject The worldObject to pick a sprite for. -- @treturn Quad A quad pointing to the sprite on the active tileset. -- -local function selectWorldObjectSprite( worldObject, tile ) +local function selectWorldObjectSprite( worldObject ) if worldObject:isOpenable() then if worldObject:isPassable() then return TexturePacks.getSprite( worldObject:getID(), 'open' ) @@ -180,8 +180,12 @@ local function selectWorldObjectSprite( worldObject, tile ) -- Check if the world object sprite connects to adjacent sprites. local connections = worldObject:getConnections() if connections then - -- FIXME - return TexturePacks.getSprite( worldObject:getID(), CONNECTION_BITMASK[0] ) + local neighbours = worldObject:getNeighbours() + local result = checkConnection( connections, neighbours[DIRECTION.NORTH], 1 ) + + checkConnection( connections, neighbours[DIRECTION.EAST], 2 ) + + checkConnection( connections, neighbours[DIRECTION.SOUTH], 4 ) + + checkConnection( connections, neighbours[DIRECTION.WEST], 8 ) + return TexturePacks.getSprite( worldObject:getID(), CONNECTION_BITMASK[result] ) end return TexturePacks.getSprite( worldObject:getID() ) @@ -205,7 +209,7 @@ local function selectTileSprite( tile, worldObject, faction ) end if worldObject then - return selectWorldObjectSprite( worldObject, tile ) + return selectWorldObjectSprite( worldObject ) end return TexturePacks.getSprite( tile:getID() ) From 4553be953b7808fe856bc61f1d2b48412e33d452 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sun, 19 Aug 2018 23:36:09 +0200 Subject: [PATCH 54/93] Use dedicated map layer for characters --- src/characters/Faction.lua | 2 +- src/map/Map.lua | 12 +++++++++++- src/ui/MapPainter.lua | 35 ++++++++++++++++++----------------- 3 files changed, 30 insertions(+), 19 deletions(-) diff --git a/src/characters/Faction.lua b/src/characters/Faction.lua index b220a21d..6e8d38b1 100644 --- a/src/characters/Faction.lua +++ b/src/characters/Faction.lua @@ -112,7 +112,7 @@ function Faction:spawnCharacters( map ) tile = map:findSpawnPoint( self.type ) end - tile:setCharacter( character ) + map:setCharacterAt( tile:getX(), tile:getY(), character ) character:setTile( tile ) character:setMap( map ) end) diff --git a/src/map/Map.lua b/src/map/Map.lua index 92d00995..dd651df4 100644 --- a/src/map/Map.lua +++ b/src/map/Map.lua @@ -130,7 +130,7 @@ end function Map:iterate( callback ) for x = 1, self.width do for y = 1, self.height do - callback( x, y, self.tiles[x][y], self.worldObjects[x][y] ) + callback( x, y, self.tiles[x][y], self.worldObjects[x][y], self.characters[x][y] ) end end end @@ -253,6 +253,16 @@ end -- Setters -- ------------------------------------------------ +--- +-- Sets a Character to a specific position on the character layer. +-- @tparam number x The target position along the x-axis. +-- @tparam number y The target position along the y-axis. +-- @tparam Character The character to set to the grid. +-- +function Map:setCharacterAt( x, y, character ) + self.characters[x][y] = character +end + --- -- Sets a tile to a specific position on the tile layer. -- @tparam number x The target position along the x-axis. diff --git a/src/ui/MapPainter.lua b/src/ui/MapPainter.lua index e9fbfcf5..11f31c93 100644 --- a/src/ui/MapPainter.lua +++ b/src/ui/MapPainter.lua @@ -97,10 +97,11 @@ end -- Selects a color which to use when a tile is drawn based on its contents. -- @tparam Tile tile The tile to choose a color for. -- @tparam WorldObject worldObject The worldobject to choose a color for. +-- @tparam Character character A character to choose a color for. -- @tparam Faction faction The faction to draw for. -- @treturn table A table containing RGBA values. -- -local function selectTileColor( tile, worldObject, faction ) +local function selectTileColor( tile, worldObject, character, faction ) -- If there is a faction we check which tiles are currently seen and highlight -- the active character. if faction then @@ -109,14 +110,14 @@ local function selectTileColor( tile, worldObject, faction ) return TexturePacks.getColor( 'tile_unseen' ) end - -- Highlight activate character. - if tile:getCharacter() == faction:getCurrentCharacter() then - return TexturePacks.getColor( COLORS[tile:getCharacter():getFaction():getType()].ACTIVE ) + -- Highlight activated character. + if character == faction:getCurrentCharacter() then + return TexturePacks.getColor( COLORS[character:getFaction():getType()].ACTIVE ) end end - if tile:isOccupied() then - return TexturePacks.getColor( COLORS[tile:getCharacter():getFaction():getType()].INACTIVE ) + if character then + return TexturePacks.getColor( COLORS[character:getFaction():getType()].INACTIVE ) end if not tile:getInventory():isEmpty() then @@ -132,12 +133,11 @@ local function selectTileColor( tile, worldObject, faction ) end --- --- Selects the tile for drawing a tile occupied by a character. --- @tparam Tile tile The tile to pick a sprite for. --- @treturn Quad A quad pointing to the sprite on the active tileset. +-- Selects the sprite for drawing a character. +-- @tparam Character character The character to choose a sprite for. +-- @treturn Quad A quad pointing to the sprite on the active tileset. -- -local function selectCharacterTile( tile ) - local character = tile:getCharacter() +local function selectCharacterTile( character ) return TexturePacks.getSprite( character:getCreatureClass(), character:getStance() ) end @@ -195,12 +195,13 @@ end -- Selects a sprite from the tileset based on the contents of a tile. -- @tparam Tile tile The tile to choose a sprite for. -- @tparam WorldObject worldObject The worldObject to choose a sprite for. +-- @tparam Character character A character to choose a sprite for. -- @tparam Faction faction The faction to draw for. -- @treturn Quad A quad pointing to a sprite on the tileset. -- -local function selectTileSprite( tile, worldObject, faction ) - if tile:isOccupied() and faction and faction:canSee( tile ) then - return selectCharacterTile( tile ) +local function selectTileSprite( tile, worldObject, character, faction ) + if character and faction and faction:canSee( tile ) then + return selectCharacterTile( character ) end if not tile:getInventory():isEmpty() then @@ -224,10 +225,10 @@ end -- local function updateSpritebatch( spritebatch, map, faction ) local tw, th = TexturePacks.getTileDimensions() - map:iterate( function( x, y, tile, worldObject ) + map:iterate( function( x, y, tile, worldObject, character ) if tile:isDirty() then - spritebatch:setColor( selectTileColor( tile, worldObject, faction )) - spritebatch:set( tile:getSpriteID(), selectTileSprite( tile, worldObject, faction ), x * tw, y * th ) + spritebatch:setColor( selectTileColor( tile, worldObject, character, faction )) + spritebatch:set( tile:getSpriteID(), selectTileSprite( tile, worldObject, character, faction ), x * tw, y * th ) tile:setDirty( false ) end end) From d0fe1262718a09d8c6195c75554a1eab2fdbf65a Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sun, 19 Aug 2018 23:59:32 +0200 Subject: [PATCH 55/93] Use MapObject base class for all map objects This class stores map coordinates and a reference to the map itself. Through this class each object will be able to communicate with objects on other layers. --- src/characters/Character.lua | 14 ++--- src/map/Map.lua | 11 +++- src/map/MapLoader.lua | 2 +- src/map/MapObject.lua | 57 +++++++++++++++++++ src/map/procedural/ProceduralMapGenerator.lua | 8 +-- src/map/tiles/Tile.lua | 38 ++----------- src/map/tiles/TileFactory.lua | 10 ++-- src/map/worldobjects/WorldObject.lua | 6 +- 8 files changed, 88 insertions(+), 58 deletions(-) create mode 100644 src/map/MapObject.lua diff --git a/src/characters/Character.lua b/src/characters/Character.lua index f87c9099..8ed5616c 100644 --- a/src/characters/Character.lua +++ b/src/characters/Character.lua @@ -6,7 +6,7 @@ -- Required Modules -- ------------------------------------------------ -local Class = require( 'lib.Middleclass' ) +local MapObject = require( 'src.map.MapObject' ) local Log = require( 'src.util.Log' ) local Queue = require('src.util.Queue') local Bresenham = require( 'lib.Bresenham' ) @@ -17,7 +17,7 @@ local Translator = require( 'src.util.Translator' ) -- Module -- ------------------------------------------------ -local Character = Class( 'Character' ) +local Character = MapObject:subclass( 'Character' ) -- ------------------------------------------------ -- Constants @@ -117,6 +117,8 @@ end -- ------------------------------------------------ function Character:initialize( classID ) + MapObject.initialize( self ) + self.creatureClass = classID self.actionPoints = DEFAULT_ACTION_POINTS @@ -526,14 +528,6 @@ function Character:setFaction( faction ) self.faction = faction end ---- --- Sets the map the character is currently on. --- @tparam Map map The map to set for this character. --- -function Character:setMap( map ) - self.map = map -end - --- -- Sets a new name for this character. -- @tparam string nname The name to set for this character. diff --git a/src/map/Map.lua b/src/map/Map.lua index dd651df4..f8b17851 100644 --- a/src/map/Map.lua +++ b/src/map/Map.lua @@ -261,6 +261,9 @@ end -- function Map:setCharacterAt( x, y, character ) self.characters[x][y] = character + + character:setPosition( x, y ) + character:setMap( self ) end --- @@ -271,6 +274,9 @@ end -- function Map:setTileAt( x, y, tile ) self.tiles[x][y] = tile + + tile:setPosition( x, y ) + tile:setMap( self ) end --- @@ -281,12 +287,15 @@ end -- function Map:setWorldObjectAt( x, y, worldObject ) self.worldObjects[x][y] = worldObject + + worldObject:setPosition( x, y ) + worldObject:setMap( self ) end --- -- TODO remove! -- -function Map:initGrid() +function Map:initializeGrid() addNeighbours( self ) observeTiles( self, self.tiles ) diff --git a/src/map/MapLoader.lua b/src/map/MapLoader.lua index 626085bc..44d74c9a 100644 --- a/src/map/MapLoader.lua +++ b/src/map/MapLoader.lua @@ -51,7 +51,7 @@ local function loadSavedTiles( savedTiles ) local loadedTiles = {} for _, tile in ipairs( savedTiles ) do -- Recreate the tile. - local recreatedTile = TileFactory.create( tile.x, tile.y, tile.id ) + local recreatedTile = TileFactory.create( tile.id ) -- Recreate any worldobject that was located on the tile. if tile.worldObject then diff --git a/src/map/MapObject.lua b/src/map/MapObject.lua new file mode 100644 index 00000000..2a01196f --- /dev/null +++ b/src/map/MapObject.lua @@ -0,0 +1,57 @@ +--- +-- @module MapObject +-- + +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ + +local Observable = require( 'src.util.Observable' ) + +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ + +local MapObject = Observable:subclass( 'MapObject' ) + +-- ------------------------------------------------ +-- Constructor +-- ------------------------------------------------ + +function MapObject:initialize() + Observable.initialize( self ) +end + +-- ------------------------------------------------ +-- Getters +-- ------------------------------------------------ + +function MapObject:getX() + return self.x +end + +function MapObject:getY() + return self.y +end + +function MapObject:getPosition() + return self.x, self.y +end + +function MapObject:getMap() + return self.map +end + +-- ------------------------------------------------ +-- Setters +-- ------------------------------------------------ + +function MapObject:setPosition( x, y ) + self.x, self.y = x, y +end + +function MapObject:setMap( map ) + self.map = map +end + +return MapObject diff --git a/src/map/procedural/ProceduralMapGenerator.lua b/src/map/procedural/ProceduralMapGenerator.lua index 88f4170c..383f8069 100644 --- a/src/map/procedural/ProceduralMapGenerator.lua +++ b/src/map/procedural/ProceduralMapGenerator.lua @@ -79,7 +79,7 @@ local function placePrefab( map, prefab, px, py ) local mapX, mapY = tx + px, ty + py if tiles[tx][ty].tile then - map:setTileAt( mapX, mapY, TileFactory.create( mapX, mapY, tiles[tx][ty].tile )) + map:setTileAt( mapX, mapY, TileFactory.create( tiles[tx][ty].tile )) end if tiles[tx][ty].worldObject then @@ -157,7 +157,7 @@ local function spawnRoads( map, parcelGrid ) for w = 1, PARCEL_SIZE.WIDTH do for h = 1, PARCEL_SIZE.HEIGHT do local mapX, mapY = tx + w, ty + h - map:setTileAt( mapX, mapY, TileFactory.create( mapX, mapY, 'tile_asphalt' )) + map:setTileAt( mapX, mapY, TileFactory.create( 'tile_asphalt' )) end end end) @@ -172,7 +172,7 @@ local function fillMap( map, width, height ) for y = 1, height do -- TODO Better algorithm for placing ground tiles. local id = love.math.random() > 0.7 and 'tile_soil' or 'tile_grass' - map:setTileAt( x, y, TileFactory.create( x, y, id )) + map:setTileAt( x, y, TileFactory.create( id )) end end end @@ -259,7 +259,7 @@ function ProceduralMapGenerator:createMap( layout ) spawnFoliage( map, self.parcelGrid ) createSpawnPoints( self.spawnpoints, self.layout.spawns ) - map:initGrid() -- TODO remove + map:initializeGrid() -- TODO remove map:setSpawnpoints( self.spawnpoints ) return map diff --git a/src/map/tiles/Tile.lua b/src/map/tiles/Tile.lua index d1978e77..ec6e85f8 100644 --- a/src/map/tiles/Tile.lua +++ b/src/map/tiles/Tile.lua @@ -6,14 +6,14 @@ -- Required Modules -- ------------------------------------------------ -local Observable = require( 'src.util.Observable' ) +local MapObject = require( 'src.map.MapObject' ) local Inventory = require( 'src.inventory.Inventory' ) -- ------------------------------------------------ -- Module -- ------------------------------------------------ -local Tile = Observable:subclass( 'Tile' ) +local Tile = MapObject:subclass( 'Tile' ) -- ------------------------------------------------ -- Constants @@ -29,18 +29,13 @@ local DEFAULT_HEIGHT = 10 --- -- Creates a new instance of the Tile class. --- @tparam number x The grid position along the x-axis. --- @tparam number y The grid position along the y-axis. -- @tparam string id The tile's id. -- @tparam number cost The amount of AP it costs to traverse this tile. -- @tparam boolean passable Wether this tile can be traversed. -- @tparam boolean spawn Wether this tile is valid for spawning. -- -function Tile:initialize( x, y, id, cost, passable, spawn ) - Observable.initialize( self ) - - self.x = x - self.y = y +function Tile:initialize( id, cost, passable, spawn ) + MapObject.initialize( self ) self.id = id self.cost = cost @@ -157,15 +152,6 @@ function Tile:getNeighbours() return self.neighbours end ---- --- Returns the tile's grid position. --- @treturn number The tile's position along the x-axis of the grid. --- @treturn number The tile's position along the y-axis of the grid. --- -function Tile:getPosition() - return self.x, self.y -end - --- -- Gets the tile's inventory. -- @treturn Inventory The tile's inventory. @@ -205,22 +191,6 @@ function Tile:getWorldObject() return self.worldObject end ---- --- Returns the tile's grid position along the x-axis. --- @treturn number The tile's position along the x-axis of the grid. --- -function Tile:getX() - return self.x -end - ---- --- Returns the tile's grid position along the y-axis. --- @treturn number The tile's position along the y-axis of the grid. --- -function Tile:getY() - return self.y -end - --- -- Checks if the tile has a world object. -- @treturn boolean True if a WorldObject is located on the tile. diff --git a/src/map/tiles/TileFactory.lua b/src/map/tiles/TileFactory.lua index 9a34a842..d7fe5480 100644 --- a/src/map/tiles/TileFactory.lua +++ b/src/map/tiles/TileFactory.lua @@ -62,15 +62,13 @@ end --- -- Creates a tile of a certain id at the given coordinates. --- @tparam number x The tile's coordinate along the x-axis. --- @tparam number y The tile's coordinate along the y-axis. --- @tparam string id The id of Tile to create. --- @treturn Tile The newly created Tile. +-- @tparam string id The id of Tile to create. +-- @treturn Tile The newly created Tile. -- -function TileFactory.create( x, y, id ) +function TileFactory.create( id ) local template = tiles[id] assert( template, string.format( 'Requested tile id (%s) doesn\'t exist!', id )) - return Tile( x, y, template.id, template.movementCost, template.passable, template.spawn ) + return Tile( template.id, template.movementCost, template.passable, template.spawn ) end --- diff --git a/src/map/worldobjects/WorldObject.lua b/src/map/worldobjects/WorldObject.lua index a898ec46..1d65cf2a 100644 --- a/src/map/worldobjects/WorldObject.lua +++ b/src/map/worldobjects/WorldObject.lua @@ -6,14 +6,14 @@ -- Required Modules -- ------------------------------------------------ -local Class = require( 'lib.Middleclass' ) +local MapObject = require( 'src.map.MapObject' ) local Inventory = require( 'src.inventory.Inventory' ) -- ------------------------------------------------ -- Module -- ------------------------------------------------ -local WorldObject = Class( 'WorldObject' ) +local WorldObject = MapObject:subclass( 'WorldObject' ) -- ------------------------------------------------ -- Public Methods @@ -24,6 +24,8 @@ local WorldObject = Class( 'WorldObject' ) -- @tparam table template The WorldObject's template. -- function WorldObject:initialize( template ) + MapObject.initialize( self ) + self.id = template.id self.height = template.size self.interactionCost = template.interactionCost From e13aa527549ba45a6f4eff4e91af8be41840d9fe Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 20 Aug 2018 00:59:33 +0200 Subject: [PATCH 56/93] Remove hard Tile dependencies from Character --- src/characters/Character.lua | 34 ++++++++-------------------- src/characters/Faction.lua | 1 - src/characters/actions/ClimbOver.lua | 1 - src/characters/actions/Walk.lua | 1 - src/map/MapObject.lua | 4 ++++ 5 files changed, 13 insertions(+), 28 deletions(-) diff --git a/src/characters/Character.lua b/src/characters/Character.lua index 8ed5616c..99d0b0e0 100644 --- a/src/characters/Character.lua +++ b/src/characters/Character.lua @@ -106,9 +106,9 @@ end -- @tparam Character self The character instance to use. -- local function handleDeath( self ) - self:getEquipment():dropAllItems( self.tile ) - self:getInventory():dropAllItems( self.tile ) - self.tile:removeCharacter() + self:getEquipment():dropAllItems( self:getTile() ) + self:getInventory():dropAllItems( self:getTile() ) + self:getTile():removeCharacter() resetFOV( self.fov ) end @@ -186,7 +186,7 @@ function Character:enqueueAction( newAction ) return true end - self.tile:publish( 'MESSAGE_LOG_EVENT', self.tile, Translator.getText( 'msg_character_no_ap_left' ), 'DANGER' ) + self:getTile():publish( 'MESSAGE_LOG_EVENT', self:getTile(), Translator.getText( 'msg_character_no_ap_left' ), 'DANGER' ) Log.debug( 'No AP left. Refused to add Action to Queue.', 'Character' ) return false end @@ -220,8 +220,8 @@ function Character:generateFOV() resetFOV( self.fov ) local range = self.body:getStatusEffects():isBlind() and 1 or self:getViewRange() - local list = Util.getTilesInCircle( self.map, self.tile, range ) - local sx, sy = self.tile:getPosition() + local list = Util.getTilesInCircle( self.map, self:getTile(), range ) + local sx, sy = self:getTile():getPosition() for _, tile in ipairs( list ) do local tx, ty = tile:getPosition() @@ -280,8 +280,8 @@ function Character:serialize() ['stance'] = self.stance, ['finishedTurn'] = self.finishedTurn, ['body'] = self.body:serialize(), - ['x'] = self.tile:getX(), - ['y'] = self.tile:getY() + ['x'] = self:getX(), + ['y'] = self:getY() } return t end @@ -292,7 +292,7 @@ end -- @tparam varags ... Additional parameters to pass along. -- function Character:receive( event, ... ) - self.tile:publish( event, self.tile, ... ) + self:getTile():publish( event, self:getTile(), ... ) end -- ------------------------------------------------ @@ -435,14 +435,6 @@ function Character:getThrowingSkill() return self.throwingSkill end ---- --- Gets the character's tile. --- @treturn Tile The tile the character is located on. --- -function Character:getTile() - return self.tile -end - --- -- Returns the total amount of action points. -- @treturn number The total amount of action points. @@ -544,14 +536,6 @@ function Character:setNationality( nationality ) self.nationality = nationality end ---- --- Sets the character's tile. --- @tparam Tile tile The tile to set the character to. --- -function Character:setTile( tile ) - self.tile = tile -end - --- -- Sets the character's throwing skill. -- @tparam number throwingSkill The character's throwing skill. diff --git a/src/characters/Faction.lua b/src/characters/Faction.lua index 6e8d38b1..9207b04e 100644 --- a/src/characters/Faction.lua +++ b/src/characters/Faction.lua @@ -113,7 +113,6 @@ function Faction:spawnCharacters( map ) end map:setCharacterAt( tile:getX(), tile:getY(), character ) - character:setTile( tile ) character:setMap( map ) end) end diff --git a/src/characters/actions/ClimbOver.lua b/src/characters/actions/ClimbOver.lua index 545a9ae3..fe697f21 100644 --- a/src/characters/actions/ClimbOver.lua +++ b/src/characters/actions/ClimbOver.lua @@ -33,7 +33,6 @@ function ClimbOver:perform() current:removeCharacter() self.target:setCharacter( self.character ) - self.character:setTile( self.target ) SoundManager.play( 'sound_climb' ) return true diff --git a/src/characters/actions/Walk.lua b/src/characters/actions/Walk.lua index f1a10806..4fa0497a 100644 --- a/src/characters/actions/Walk.lua +++ b/src/characters/actions/Walk.lua @@ -34,7 +34,6 @@ function Walk:perform() -- give it a reference to the new tile. current:removeCharacter() self.target:setCharacter( self.character ) - self.character:setTile( self.target ) return true end diff --git a/src/map/MapObject.lua b/src/map/MapObject.lua index 2a01196f..f6287502 100644 --- a/src/map/MapObject.lua +++ b/src/map/MapObject.lua @@ -42,6 +42,10 @@ function MapObject:getMap() return self.map end +function MapObject:getTile() + return self.map:getTileAt( self.x, self.y ) +end + -- ------------------------------------------------ -- Setters -- ------------------------------------------------ From 14242c61fc95606480e3b456bec5414fe6807c5e Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 20 Aug 2018 01:01:17 +0200 Subject: [PATCH 57/93] Remove redundant line --- src/characters/Faction.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/src/characters/Faction.lua b/src/characters/Faction.lua index 9207b04e..f40ac60a 100644 --- a/src/characters/Faction.lua +++ b/src/characters/Faction.lua @@ -113,7 +113,6 @@ function Faction:spawnCharacters( map ) end map:setCharacterAt( tile:getX(), tile:getY(), character ) - character:setMap( map ) end) end From af94a86a9eb06a78a54163fd2a32c3e7314458d1 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 20 Aug 2018 01:14:14 +0200 Subject: [PATCH 58/93] Fix Walk Action We now correctly remove the character from the map and re-add it at the target position. --- src/characters/Character.lua | 9 +++++++++ src/characters/actions/Walk.lua | 5 +---- src/map/Map.lua | 12 ++++++++++++ 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/characters/Character.lua b/src/characters/Character.lua index 99d0b0e0..8d8f666f 100644 --- a/src/characters/Character.lua +++ b/src/characters/Character.lua @@ -266,6 +266,15 @@ function Character:canSee( target ) return self.fov[tx][ty] ~= nil end +--- +-- Moves the character to the new position on the map. +-- @tparam number x The target position along the x-axis. +-- @tparam number y The target position along the y-axis. +-- +function Character:move( x, y ) + self.map:setCharacterAt( x, y, self ) +end + --- -- Serializes the Character instance. -- @treturn table The serialized character instance. diff --git a/src/characters/actions/Walk.lua b/src/characters/actions/Walk.lua index 4fa0497a..ea1c3c3b 100644 --- a/src/characters/actions/Walk.lua +++ b/src/characters/actions/Walk.lua @@ -30,10 +30,7 @@ function Walk:perform() assert( not self.target:isOccupied(), 'Target tile must not be occupied by another character!' ) assert( self.target:isAdjacent( current ), 'Character has to be adjacent to the target tile!' ) - -- Remove the character from the old tile, add it to the new one and - -- give it a reference to the new tile. - current:removeCharacter() - self.target:setCharacter( self.character ) + self.character:move( self.target:getPosition() ) return true end diff --git a/src/map/Map.lua b/src/map/Map.lua index f8b17851..bcb0a33a 100644 --- a/src/map/Map.lua +++ b/src/map/Map.lua @@ -260,6 +260,18 @@ end -- @tparam Character The character to set to the grid. -- function Map:setCharacterAt( x, y, character ) + -- Remove character from old position if necessary. + local oldX, oldY = character:getPosition() + if oldX and oldY then + self.characters[oldX][oldY] = nil + end + + -- Make sure the grid space is empty. + if self.characters[x][y] ~= nil then + error( string.format( 'Target position (%s, %s) is already occupied by a character.', x, y )) + end + + -- Add character to new position. self.characters[x][y] = character character:setPosition( x, y ) From 1920469e13ad4313baeb7c1e19066bceacb77f78 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 20 Aug 2018 01:18:42 +0200 Subject: [PATCH 59/93] Remove direct WorldObject references from Tile --- src/map/MapObject.lua | 8 ++++++++ src/map/tiles/Tile.lua | 45 ++++++------------------------------------ 2 files changed, 14 insertions(+), 39 deletions(-) diff --git a/src/map/MapObject.lua b/src/map/MapObject.lua index f6287502..7becc124 100644 --- a/src/map/MapObject.lua +++ b/src/map/MapObject.lua @@ -46,6 +46,14 @@ function MapObject:getTile() return self.map:getTileAt( self.x, self.y ) end +function MapObject:getWorldObject() + return self.map:getWorldObjectAt( self.x, self.y ) +end + +function MapObject:hasWorldObject() + return self.map:getWorldObjectAt( self.x, self.y ) ~= nil +end + -- ------------------------------------------------ -- Setters -- ------------------------------------------------ diff --git a/src/map/tiles/Tile.lua b/src/map/tiles/Tile.lua index ec6e85f8..260b09fa 100644 --- a/src/map/tiles/Tile.lua +++ b/src/map/tiles/Tile.lua @@ -54,15 +54,6 @@ function Tile:addNeighbours( neighbours ) self.neighbours = neighbours end ---- --- Adds a world object to this tile and marks it for a drawing update. --- @tparam WorldObject worldObject The WorldObject to add. --- -function Tile:addWorldObject( worldObject ) - self.worldObject = worldObject - self:setDirty( true ) -end - --- -- Hits the tile with a certain amount of damage. The tile will distribute -- the damage to any character or world object which it contains. @@ -72,8 +63,8 @@ end function Tile:hit( damage, damageType ) if self:isOccupied() then self.character:hit( damage, damageType ) - elseif self:hasWorldObject() and self.worldObject:isDestructible() then - self.worldObject:damage( damage, damageType ) + elseif self:hasWorldObject() and self:getWorldObject():isDestructible() then + self:getWorldObject():damage( damage, damageType ) end end @@ -85,14 +76,6 @@ function Tile:removeCharacter() self:setDirty( true ) end ---- --- Removes the worldObject from this tile and marks it for updating. --- -function Tile:removeWorldObject() - self.worldObject = nil - self:setDirty( true ) -end - --- -- Serializes the tile. -- @treturn table The serialized Tile object. @@ -105,7 +88,7 @@ function Tile:serialize() } if self:hasWorldObject() then - t.worldObject = self.worldObject:serialize() + t.worldObject = self:getWorldObject():serialize() end if not self.inventory:isEmpty() then @@ -167,8 +150,8 @@ end -- @treturn number The height of this tile. -- function Tile:getHeight() - if self.worldObject then - return self.worldObject:getHeight() + if self:getWorldObject() then + return self:getWorldObject():getHeight() elseif self.character then return self.character:getHeight() end @@ -183,22 +166,6 @@ function Tile:getID() return self.id end ---- --- Returns the world object located on this tile. --- @treturn WorldObject The WorldObject. --- -function Tile:getWorldObject() - return self.worldObject -end - ---- --- Checks if the tile has a world object. --- @treturn boolean True if a WorldObject is located on the tile. --- -function Tile:hasWorldObject() - return self.worldObject ~= nil -end - --- -- Checks if a given tile is adjacent to this tile. -- @treturn boolean True if the tiles are adjacent to each other. @@ -233,7 +200,7 @@ end -- function Tile:isPassable() if self.passable and self:hasWorldObject() then - return self.worldObject:isPassable() + return self:getWorldObject():isPassable() end return self.passable end From ed9942f5467927963792c572693efaab37426089 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 20 Aug 2018 01:19:23 +0200 Subject: [PATCH 60/93] Fix climb over Action --- src/characters/actions/ClimbOver.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/characters/actions/ClimbOver.lua b/src/characters/actions/ClimbOver.lua index fe697f21..f7166973 100644 --- a/src/characters/actions/ClimbOver.lua +++ b/src/characters/actions/ClimbOver.lua @@ -31,8 +31,7 @@ function ClimbOver:perform() assert( self.target:isAdjacent( current ), 'Character has to be adjacent to the target tile!' ) - current:removeCharacter() - self.target:setCharacter( self.character ) + self.character:move( self.target:getPosition() ) SoundManager.play( 'sound_climb' ) return true From 39e52559e2eb8bb0d9b8233e1f563eb02e746d0a Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 20 Aug 2018 01:24:18 +0200 Subject: [PATCH 61/93] Remove direct character references from Tile --- src/characters/actions/Walk.lua | 2 +- .../ai/behaviortree/leafs/BTAquireTarget.lua | 2 +- .../ai/behaviortree/leafs/BTMoveToTarget.lua | 4 +- .../behaviortree/leafs/BTRandomMovement.lua | 4 +- src/characters/pathfinding/PathFinder.lua | 2 +- src/items/weapons/ProjectileManager.lua | 2 +- src/map/Map.lua | 2 +- src/map/MapObject.lua | 8 ++++ src/map/tiles/Tile.lua | 41 ++----------------- src/turnbased/helpers/InteractionInput.lua | 4 +- src/turnbased/helpers/MovementInput.lua | 2 +- src/turnbased/states/PlanningState.lua | 2 +- src/ui/elements/UITileInfo.lua | 2 +- src/ui/overlays/ConeOverlay.lua | 2 +- src/ui/screens/InventoryScreen.lua | 2 +- 15 files changed, 28 insertions(+), 53 deletions(-) diff --git a/src/characters/actions/Walk.lua b/src/characters/actions/Walk.lua index ea1c3c3b..2158a6ab 100644 --- a/src/characters/actions/Walk.lua +++ b/src/characters/actions/Walk.lua @@ -27,7 +27,7 @@ function Walk:perform() local current = self.character:getTile() assert( self.target:isPassable(), 'Target tile has to be passable!' ) - assert( not self.target:isOccupied(), 'Target tile must not be occupied by another character!' ) + assert( not self.target:hasCharacter(), 'Target tile must not be occupied by another character!' ) assert( self.target:isAdjacent( current ), 'Character has to be adjacent to the target tile!' ) self.character:move( self.target:getPosition() ) diff --git a/src/characters/ai/behaviortree/leafs/BTAquireTarget.lua b/src/characters/ai/behaviortree/leafs/BTAquireTarget.lua index ef26dd22..d0605bb8 100644 --- a/src/characters/ai/behaviortree/leafs/BTAquireTarget.lua +++ b/src/characters/ai/behaviortree/leafs/BTAquireTarget.lua @@ -42,7 +42,7 @@ function BTAquireTarget:traverse( ... ) local enemies = {} for i = 1, #tiles do local tile = tiles[i] - if tile:isOccupied() + if tile:hasCharacter() and not tile:getCharacter():isDead() and tile:getCharacter():getFaction():getType() ~= FACTIONS.NEUTRAL and tile:getCharacter():getFaction():getType() ~= character:getFaction():getType() then diff --git a/src/characters/ai/behaviortree/leafs/BTMoveToTarget.lua b/src/characters/ai/behaviortree/leafs/BTMoveToTarget.lua index ba80739f..eccdb2fa 100644 --- a/src/characters/ai/behaviortree/leafs/BTMoveToTarget.lua +++ b/src/characters/ai/behaviortree/leafs/BTMoveToTarget.lua @@ -21,7 +21,7 @@ local BTMoveToTarget = BTLeaf:subclass( 'BTMoveToTarget' ) -- ------------------------------------------------ local function generatePath( target, character ) - if target and target:isPassable() and not target:isOccupied() then + if target and target:isPassable() and not target:hasCharacter() then return PathFinder.generatePath( character:getTile(), target, character:getStance() ) end end @@ -38,7 +38,7 @@ function BTMoveToTarget:traverse( ... ) -- Find the closest neighbour tile to move to. for _, neighbour in pairs( blackboard.target:getNeighbours() ) do - if neighbour:isPassable() and not neighbour:isOccupied() then + if neighbour:isPassable() and not neighbour:hasCharacter() then if not closest then closest = neighbour local px, py = closest:getPosition() diff --git a/src/characters/ai/behaviortree/leafs/BTRandomMovement.lua b/src/characters/ai/behaviortree/leafs/BTRandomMovement.lua index a9d15861..460cab9d 100644 --- a/src/characters/ai/behaviortree/leafs/BTRandomMovement.lua +++ b/src/characters/ai/behaviortree/leafs/BTRandomMovement.lua @@ -21,7 +21,7 @@ local BTRandomMovement = BTLeaf:subclass( 'BTRandomMovement' ) -- ------------------------------------------------ local function generatePath( target, character ) - if target and target:isPassable() and not target:isOccupied() then + if target and target:isPassable() and not target:hasCharacter() then return PathFinder.generatePath( character:getTile(), target, character:getStance() ) end end @@ -44,7 +44,7 @@ function BTRandomMovement:traverse( ... ) end local target = tiles[love.math.random( 1, #tiles )] - if target and target:isPassable() and not target:isOccupied() then + if target and target:isPassable() and not target:hasCharacter() then local path = generatePath( target, character ) if path then local success = path:generateActions( character ) diff --git a/src/characters/pathfinding/PathFinder.lua b/src/characters/pathfinding/PathFinder.lua index aab1d04d..fa97bca4 100644 --- a/src/characters/pathfinding/PathFinder.lua +++ b/src/characters/pathfinding/PathFinder.lua @@ -106,7 +106,7 @@ local function isValidTile( tile, closedList, target ) end -- We don't allow movement to tiles occupied by other characters. - if tile:isOccupied() then + if tile:hasCharacter() then return false end diff --git a/src/items/weapons/ProjectileManager.lua b/src/items/weapons/ProjectileManager.lua index 11e70179..6bf671b5 100644 --- a/src/items/weapons/ProjectileManager.lua +++ b/src/items/weapons/ProjectileManager.lua @@ -120,7 +120,7 @@ local function checkForHits( index, projectile, tile ) local remove = false - if tile:isOccupied() then + if tile:hasCharacter() then remove = hitCharacter( tile, projectile ) elseif tile:hasWorldObject() then remove = hitWorldObject( projectile, tile, tile:getWorldObject() ) diff --git a/src/map/Map.lua b/src/map/Map.lua index bcb0a33a..ff62ce63 100644 --- a/src/map/Map.lua +++ b/src/map/Map.lua @@ -146,7 +146,7 @@ function Map:findSpawnPoint( faction ) local spawn = self.spawnpoints[faction][index] local tile = self:getTileAt( spawn.x, spawn.y ) - if tile:isSpawn() and tile:isPassable() and not tile:isOccupied() then + if tile:isSpawn() and tile:isPassable() and not tile:hasCharacter() then return tile end end diff --git a/src/map/MapObject.lua b/src/map/MapObject.lua index 7becc124..a1b54674 100644 --- a/src/map/MapObject.lua +++ b/src/map/MapObject.lua @@ -46,6 +46,14 @@ function MapObject:getTile() return self.map:getTileAt( self.x, self.y ) end +function MapObject:getCharacter() + return self.map:getCharacterAt( self.x, self.y ) +end + +function MapObject:hasCharacter() + return self.map:getCharacterAt( self.x, self.y ) ~= nil +end + function MapObject:getWorldObject() return self.map:getWorldObjectAt( self.x, self.y ) end diff --git a/src/map/tiles/Tile.lua b/src/map/tiles/Tile.lua index 260b09fa..ebff3a42 100644 --- a/src/map/tiles/Tile.lua +++ b/src/map/tiles/Tile.lua @@ -61,21 +61,13 @@ end -- @tparam string damageType The type of damage the tile is hit with. -- function Tile:hit( damage, damageType ) - if self:isOccupied() then - self.character:hit( damage, damageType ) + if self:hasCharacter() then + self:getCharacter():hit( damage, damageType ) elseif self:hasWorldObject() and self:getWorldObject():isDestructible() then self:getWorldObject():damage( damage, damageType ) end end ---- --- Removes the character from this tile and marks it for updating. --- -function Tile:removeCharacter() - self.character = nil - self:setDirty( true ) -end - --- -- Serializes the tile. -- @treturn table The serialized Tile object. @@ -102,14 +94,6 @@ end -- Getters -- ------------------------------------------------ ---- --- Returns the character standing on this tile. --- @treturn Character The character standing on the tile. --- -function Tile:getCharacter() - return self.character -end - --- -- Returns the tile's unique spriteID. -- @treturn number The tile's spriteID. @@ -152,8 +136,8 @@ end function Tile:getHeight() if self:getWorldObject() then return self:getWorldObject():getHeight() - elseif self.character then - return self.character:getHeight() + elseif self:getCharacter() then + return self:getCharacter():getHeight() end return DEFAULT_HEIGHT end @@ -186,14 +170,6 @@ function Tile:isDirty() return self.dirty end ---- --- Checks if the tile has a character on it. --- @treturn boolean True a character is standing on the tile. --- -function Tile:isOccupied() - return self.character ~= nil -end - --- -- Checks if the tile is passable. -- @treturn boolean True if the tile is passable. @@ -217,15 +193,6 @@ end -- Setters -- ------------------------------------------------ ---- --- Sets a character for this tile and marks the tile for updating. --- @tparam Character character The character to add. --- -function Tile:setCharacter( character ) - self.character = character - self:setDirty( true ) -end - --- -- Sets the dirty state of the tile. -- @tparam boolean dirty Wether the tile should be updated or not. diff --git a/src/turnbased/helpers/InteractionInput.lua b/src/turnbased/helpers/InteractionInput.lua index e2b52ec4..9cba7782 100644 --- a/src/turnbased/helpers/InteractionInput.lua +++ b/src/turnbased/helpers/InteractionInput.lua @@ -48,7 +48,7 @@ end -- function InteractionInput:request( target, character ) -- Check health of enemy characters. - if target:isOccupied() and target:getCharacter():getFaction():getType() ~= character:getFaction():getType() then + if target:hasCharacter() and target:getCharacter():getFaction():getType() ~= character:getFaction():getType() then ScreenManager.push( 'playerInfo', target:getCharacter() ) return true end @@ -73,7 +73,7 @@ function InteractionInput:request( target, character ) end -- Handle interactions with other characters. - if target:isOccupied() then + if target:hasCharacter() then if target:getCharacter():getFaction():getType() == character:getFaction():getType() then character:enqueueAction( OpenInventory( character, target )) return true diff --git a/src/turnbased/helpers/MovementInput.lua b/src/turnbased/helpers/MovementInput.lua index ae0d837d..48d47d6c 100644 --- a/src/turnbased/helpers/MovementInput.lua +++ b/src/turnbased/helpers/MovementInput.lua @@ -20,7 +20,7 @@ local MovementInput = Class( 'MovementInput' ) -- ------------------------------------------------ local function generatePath( target, character ) - if target and target:isPassable() and not target:isOccupied() then + if target and target:isPassable() and not target:hasCharacter() then return PathFinder.generatePath( character:getTile(), target, character:getStance() ) end end diff --git a/src/turnbased/states/PlanningState.lua b/src/turnbased/states/PlanningState.lua index 36042b6c..f5dc6d5e 100644 --- a/src/turnbased/states/PlanningState.lua +++ b/src/turnbased/states/PlanningState.lua @@ -167,7 +167,7 @@ function PlanningState:selectTile( tile, button ) return end - if button == 2 and tile:isOccupied() then + if button == 2 and tile:hasCharacter() then self.inputStateHandler:switch( 'movement' ) self.factions:getFaction():selectCharacter( tile:getCharacter() ) return diff --git a/src/ui/elements/UITileInfo.lua b/src/ui/elements/UITileInfo.lua index 22b2d392..71337564 100644 --- a/src/ui/elements/UITileInfo.lua +++ b/src/ui/elements/UITileInfo.lua @@ -99,7 +99,7 @@ local function inspectTile( textObject, colorTable, tile ) end local _, th = TexturePacks.getTileDimensions() - if tile:isOccupied() then + if tile:hasCharacter() then showCharacterInfo( textObject, colorTable, x, UI_CHARACTER_INFO * th, tile:getCharacter() ) end diff --git a/src/ui/overlays/ConeOverlay.lua b/src/ui/overlays/ConeOverlay.lua index fbb94b3e..99d7ab57 100644 --- a/src/ui/overlays/ConeOverlay.lua +++ b/src/ui/overlays/ConeOverlay.lua @@ -129,7 +129,7 @@ function ConeOverlay:generate() -- 2 means the projectile might be blocked by a world object or character. -- 3 means the projectile will be blocked by a world object. local nstatus = 1 - if tile:isOccupied() or not character:canSee( tile ) then + if tile:hasCharacter() or not character:canSee( tile ) then nstatus = 2 elseif tile:hasWorldObject() and height <= tile:getWorldObject():getHeight() then -- Indestructible worldobjects block the shot. diff --git a/src/ui/screens/InventoryScreen.lua b/src/ui/screens/InventoryScreen.lua index e7000cd3..4f08334a 100644 --- a/src/ui/screens/InventoryScreen.lua +++ b/src/ui/screens/InventoryScreen.lua @@ -173,7 +173,7 @@ local function createTargetInventoryList( x, y, character, target, lists, listL id, inventory = 'inventory_base', target elseif target:hasWorldObject() and target:getWorldObject():isContainer() then id, inventory = 'inventory_container_inventory', target:getWorldObject():getInventory() - elseif target:isOccupied() and target:getCharacter() ~= character and target:getCharacter():getFaction():getType() == character:getFaction():getType() then + elseif target:hasCharacter() and target:getCharacter() ~= character and target:getCharacter():getFaction():getType() == character:getFaction():getType() then id, inventory = 'inventory_character', target:getCharacter():getInventory() else id, inventory = 'inventory_tile_inventory', target:getInventory() From 60a86cf5608081ddd4157cd48f01024de334e97a Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 20 Aug 2018 01:30:07 +0200 Subject: [PATCH 62/93] Fix character removal upon death --- src/characters/Character.lua | 2 +- src/map/Map.lua | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/characters/Character.lua b/src/characters/Character.lua index 8d8f666f..b4539d35 100644 --- a/src/characters/Character.lua +++ b/src/characters/Character.lua @@ -108,7 +108,7 @@ end local function handleDeath( self ) self:getEquipment():dropAllItems( self:getTile() ) self:getInventory():dropAllItems( self:getTile() ) - self:getTile():removeCharacter() + self.map:removeCharacter( self.x, self.y, self ) resetFOV( self.fov ) end diff --git a/src/map/Map.lua b/src/map/Map.lua index ff62ce63..8ab6a235 100644 --- a/src/map/Map.lua +++ b/src/map/Map.lua @@ -216,10 +216,34 @@ function Map:serialize() return t end +--- +-- Removes a Character from a specific position on the character layer. +-- @tparam number x The target position along the x-axis. +-- @tparam number y The target position along the y-axis. +-- @tparam Character character The character to remove from the grid. +-- +function Map:removeCharacter( x, y, character ) + if character ~= self.characters[x][y] then + error( string.format( 'Character at position (%s, %s) does not match the character to remove.', x, y )) + end + + self.characters[x][y] = nil +end + -- ------------------------------------------------ -- Getters -- ------------------------------------------------ +--- +-- Returns the Character at the given coordinates. +-- @tparam number x The position along the x-axis. +-- @tparam number y The position along the y-axis. +-- @treturn Character The Character at the given position. +-- +function Map:getCharacterAt( x, y ) + return self.characters[x] and self.characters[x][y] +end + --- -- Returns the Tile at the given coordinates. -- @tparam number x The position along the x-axis. From 410ba32b489856274faa6352cda810f9fa01e492 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 20 Aug 2018 18:15:58 +0200 Subject: [PATCH 63/93] Generate list of neighbours dynamically If we initialise the neighbours on map creation we get problems upon removing world objects, because we'd also have to update each world object the removed object was a neighbour of. --- src/map/Map.lua | 47 ++++++++++++++++------------ src/map/MapObject.lua | 4 +++ src/map/tiles/Tile.lua | 19 +---------- src/map/worldobjects/WorldObject.lua | 17 ---------- 4 files changed, 32 insertions(+), 55 deletions(-) diff --git a/src/map/Map.lua b/src/map/Map.lua index 8ab6a235..d46aa364 100644 --- a/src/map/Map.lua +++ b/src/map/Map.lua @@ -9,6 +9,8 @@ local Observable = require( 'src.util.Observable' ) local WorldObjectFactory = require( 'src.map.worldobjects.WorldObjectFactory' ) local ItemFactory = require( 'src.items.ItemFactory' ) +local Tile = require( 'src.map.tiles.Tile' ) +local WorldObject = require( 'src.map.worldobjects.WorldObject' ) -- ------------------------------------------------ -- Module @@ -37,10 +39,12 @@ local DIRECTION_MODIFIERS = { -- Private Methods -- ------------------------------------------------ --- Gives each tile a reference to its neighbours. +--- +-- Compiles a list of all neighbouring tiles around a certain coordinate. -- @tparam Map self The map instance to use. -- @tparam number x The tile position along the x-axis. -- @tparam number y The tile position along the y-axis. +-- @treturn table A list of all neighbouring tiles. -- local function createTileNeighbours( self, x, y ) local neighbours = {} @@ -50,6 +54,13 @@ local function createTileNeighbours( self, x, y ) return neighbours end +--- +-- Compiles a list of all neighbouring worldObjects around a certain coordinate. +-- @tparam Map self The map instance to use. +-- @tparam number x The tile position along the x-axis. +-- @tparam number y The tile position along the y-axis. +-- @treturn table A list of all neighbouring worldObjects. +-- local function createWorldObjectNeighbours( self, x, y ) local neighbours = {} for direction, modifier in ipairs( DIRECTION_MODIFIERS ) do @@ -58,23 +69,6 @@ local function createWorldObjectNeighbours( self, x, y ) return neighbours end ---- --- Creates neighbours for tiles and worldobjects. --- @tparam Map self The map instance to use. --- -local function addNeighbours( self ) - self:iterate( function( x, y, tile, worldObject ) - -- Create tile neighbours - tile:addNeighbours( createTileNeighbours( self, x, y )) - - -- Create worldObject neighbours - -- Note: Not every grid space will be occupied by a world object! - if worldObject then - worldObject:setNeighbours( createWorldObjectNeighbours( self, x, y )) - end - end) -end - --- -- Observers each tile so the map can receive events from them. -- @tparam Map self The map instance to use. @@ -273,6 +267,21 @@ function Map:getDimensions() return self.width, self.height end +--- +-- Returns the neighbours around a specific position. +-- @tparam number x The position along the x-axis. +-- @tparam number y The position along the y-axis. +-- @tparam MapObject object The map object to return neighbours for. +-- +function Map:getNeighbours( x, y, object ) + if object:isInstanceOf( Tile ) then + return createTileNeighbours( self, x, y ) + elseif object:isInstanceOf( WorldObject ) then + return createWorldObjectNeighbours( self, x, y ) + end + error( 'Not a valid object to get neighbours for!' ) +end + -- ------------------------------------------------ -- Setters -- ------------------------------------------------ @@ -332,8 +341,6 @@ end -- TODO remove! -- function Map:initializeGrid() - addNeighbours( self ) - observeTiles( self, self.tiles ) end diff --git a/src/map/MapObject.lua b/src/map/MapObject.lua index a1b54674..805b6d50 100644 --- a/src/map/MapObject.lua +++ b/src/map/MapObject.lua @@ -62,6 +62,10 @@ function MapObject:hasWorldObject() return self.map:getWorldObjectAt( self.x, self.y ) ~= nil end +function MapObject:getNeighbours() + return self.map:getNeighbours( self.x, self.y, self ) +end + -- ------------------------------------------------ -- Setters -- ------------------------------------------------ diff --git a/src/map/tiles/Tile.lua b/src/map/tiles/Tile.lua index ebff3a42..f7c81184 100644 --- a/src/map/tiles/Tile.lua +++ b/src/map/tiles/Tile.lua @@ -45,15 +45,6 @@ function Tile:initialize( id, cost, passable, spawn ) self.inventory = Inventory( WEIGHT_LIMIT, VOLUME_LIMIT ) end ---- --- Adds a table containing the neighbouring tiles. Note that some tiles might --- be nil. --- @tparam table neighbours A table containing the neighbouring tiles. --- -function Tile:addNeighbours( neighbours ) - self.neighbours = neighbours -end - --- -- Hits the tile with a certain amount of damage. The tile will distribute -- the damage to any character or world object which it contains. @@ -111,14 +102,6 @@ function Tile:getMovementCost( stance ) return self.cost[stance] end ---- --- Returns a table containing this tile's neighbours. --- @treturn table A table containing the neighbouring tiles. --- -function Tile:getNeighbours() - return self.neighbours -end - --- -- Gets the tile's inventory. -- @treturn Inventory The tile's inventory. @@ -155,7 +138,7 @@ end -- @treturn boolean True if the tiles are adjacent to each other. -- function Tile:isAdjacent( tile ) - for _, neighbour in pairs( self.neighbours ) do + for _, neighbour in pairs( self:getNeighbours() ) do if neighbour == tile then return true end diff --git a/src/map/worldobjects/WorldObject.lua b/src/map/worldobjects/WorldObject.lua index 1d65cf2a..b4d00856 100644 --- a/src/map/worldobjects/WorldObject.lua +++ b/src/map/worldobjects/WorldObject.lua @@ -196,14 +196,6 @@ function WorldObject:getConnections() return self.connections end ---- --- Returns a table containing this worldObject's neighbours. --- @treturn table A table containing the neighbouring worldObjects. --- -function WorldObject:getNeighbours() - return self.neighbours -end - --- -- Checks wether the WorldObject is destructible. -- @treturn boolean True if the WorldObject is destructible. @@ -241,15 +233,6 @@ end -- Setters -- ------------------------------------------------ ---- --- Adds a table containing the neighbouring worldObjects. --- Note that some neighbours might be nil. --- @tparam table neighbours A table containing the neighbouring worldObjects. --- -function WorldObject:setNeighbours( neighbours ) - self.neighbours = neighbours -end - --- -- Sets the WorldObject's blocksVision attribute. -- @tparam boolean blocksVision The new attribute value. From a896cea0a22555dec19afc4e5677b21739c61611 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 20 Aug 2018 18:36:15 +0200 Subject: [PATCH 64/93] Fix savegame creation --- src/characters/Character.lua | 13 ++------ src/characters/CharacterFactory.lua | 3 -- src/characters/Faction.lua | 3 +- src/map/Map.lua | 48 ++++++++++++++++++---------- src/map/tiles/Tile.lua | 4 --- src/map/worldobjects/WorldObject.lua | 2 ++ 6 files changed, 37 insertions(+), 36 deletions(-) diff --git a/src/characters/Character.lua b/src/characters/Character.lua index b4539d35..832467c1 100644 --- a/src/characters/Character.lua +++ b/src/characters/Character.lua @@ -289,8 +289,8 @@ function Character:serialize() ['stance'] = self.stance, ['finishedTurn'] = self.finishedTurn, ['body'] = self.body:serialize(), - ['x'] = self:getX(), - ['y'] = self:getY() + ['x'] = self.x, + ['y'] = self.y } return t end @@ -570,13 +570,4 @@ function Character:setStance( stance ) self.stance = stance end --- TODO Remove hack for saving / loading characters -function Character:getSavedPosition() - return self.savedX, self.savedY -end - -function Character:setSavedPosition( x, y ) - self.savedX, self.savedY = x, y -end - return Character diff --git a/src/characters/CharacterFactory.lua b/src/characters/CharacterFactory.lua index 5e75f1c4..592bf766 100644 --- a/src/characters/CharacterFactory.lua +++ b/src/characters/CharacterFactory.lua @@ -190,9 +190,6 @@ function CharacterFactory.loadCharacter( savedCharacter ) local body = BodyFactory.load( savedCharacter.body ) character:setBody( body ) - -- TODO Remove hack for saving / loading characters - character:setSavedPosition( savedCharacter.x, savedCharacter.y ) - return character end diff --git a/src/characters/Faction.lua b/src/characters/Faction.lua index f40ac60a..5ee5e733 100644 --- a/src/characters/Faction.lua +++ b/src/characters/Faction.lua @@ -101,8 +101,7 @@ end -- function Faction:spawnCharacters( map ) self:iterate( function( character ) - local sx, sy = character:getSavedPosition() - character:setSavedPosition( nil, nil ) + local sx, sy = nil, nil local tile diff --git a/src/map/Map.lua b/src/map/Map.lua index d46aa364..f7d17375 100644 --- a/src/map/Map.lua +++ b/src/map/Map.lua @@ -194,22 +194,6 @@ function Map:receive( event, ... ) self:publish( event, ... ) end -function Map:serialize() - local t = { - width = self.width, - height = self.height, - tiles = {} - } - - for x = 1, #self.tiles do - for y = 1, #self.tiles[x] do - table.insert( t.tiles, self.tiles[x][y]:serialize() ) - end - end - - return t -end - --- -- Removes a Character from a specific position on the character layer. -- @tparam number x The target position along the x-axis. @@ -352,4 +336,36 @@ function Map:setSpawnpoints( spawnpoints ) self.spawnpoints = spawnpoints end +-- ------------------------------------------------ +-- Serialization +-- ------------------------------------------------ + +--- +-- Serializes the Map instance. +-- @treturn table The serialized character instance. +-- +function Map:serialize() + local t = { + ['width'] = self.width, + ['height'] = self.height, + ['tiles'] = {}, + ['worldObjects'] = {}, + ['characters'] = {} + } + + self:iterate( function( _, _, tile, worldObject, character ) + t.tiles[#t.tiles + 1] = tile:serialize() + + if worldObject then + t.worldObjects[#t.worldObjects + 1] = worldObject:serialize() + end + + if character then + t.characters[#t.characters + 1] = character:serialize() + end + end) + + return t +end + return Map diff --git a/src/map/tiles/Tile.lua b/src/map/tiles/Tile.lua index f7c81184..9dd2cbeb 100644 --- a/src/map/tiles/Tile.lua +++ b/src/map/tiles/Tile.lua @@ -70,10 +70,6 @@ function Tile:serialize() ['y'] = self.y } - if self:hasWorldObject() then - t.worldObject = self:getWorldObject():serialize() - end - if not self.inventory:isEmpty() then t['inventory'] = self.inventory:serialize() end diff --git a/src/map/worldobjects/WorldObject.lua b/src/map/worldobjects/WorldObject.lua index b4d00856..8ece7145 100644 --- a/src/map/worldobjects/WorldObject.lua +++ b/src/map/worldobjects/WorldObject.lua @@ -62,6 +62,8 @@ end function WorldObject:serialize() local t = { ['id'] = self.id, + ['x'] = self.x, + ['y'] = self.y, ['hp'] = self.hp, ['passable'] = self.passable, ['blocksVision'] = self.blocksVision From f833ee6a98a377a8d601333f5c3f224b428e54ab Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 20 Aug 2018 19:21:51 +0200 Subject: [PATCH 65/93] Fix savegame loading Fixes #256. --- src/CombatState.lua | 7 +-- src/characters/CharacterFactory.lua | 1 + src/characters/Faction.lua | 2 +- src/map/MapLoader.lua | 77 ++++++++++++++--------------- 4 files changed, 41 insertions(+), 46 deletions(-) diff --git a/src/CombatState.lua b/src/CombatState.lua index 5f9c9d09..612af621 100644 --- a/src/CombatState.lua +++ b/src/CombatState.lua @@ -9,7 +9,6 @@ local Class = require( 'lib.Middleclass' ) local ProceduralMapGenerator = require( 'src.map.procedural.ProceduralMapGenerator' ) local MapLoader = require( 'src.map.MapLoader' ) -local Map = require( 'src.map.Map' ) local Factions = require( 'src.characters.Factions' ) local ProjectileManager = require( 'src.items.weapons.ProjectileManager' ) local ExplosionManager = require( 'src.items.weapons.ExplosionManager' ) @@ -37,11 +36,7 @@ local FACTIONS = require( 'src.constants.FACTIONS' ) -- ------------------------------------------------ local function loadMap( savedMap ) - local loader = MapLoader() - local tiles, mw, mh = loader:recreateMap( savedMap ) - local map = Map( mw, mh ) - map:setTiles( tiles ) - return map + return MapLoader():recreateMap( savedMap ) end local function createMap() diff --git a/src/characters/CharacterFactory.lua b/src/characters/CharacterFactory.lua index 592bf766..e6fb70dc 100644 --- a/src/characters/CharacterFactory.lua +++ b/src/characters/CharacterFactory.lua @@ -186,6 +186,7 @@ function CharacterFactory.loadCharacter( savedCharacter ) character:setThrowingSkill( savedCharacter.throwingSkill ) character:setStance( savedCharacter.stance ) character:setFinishedTurn( savedCharacter.finishedTurn ) + character:setPosition( savedCharacter.x, savedCharacter. y ) local body = BodyFactory.load( savedCharacter.body ) character:setBody( body ) diff --git a/src/characters/Faction.lua b/src/characters/Faction.lua index 5ee5e733..21858bd9 100644 --- a/src/characters/Faction.lua +++ b/src/characters/Faction.lua @@ -101,7 +101,7 @@ end -- function Faction:spawnCharacters( map ) self:iterate( function( character ) - local sx, sy = nil, nil + local sx, sy = character:getPosition() local tile diff --git a/src/map/MapLoader.lua b/src/map/MapLoader.lua index 44d74c9a..bb16e83e 100644 --- a/src/map/MapLoader.lua +++ b/src/map/MapLoader.lua @@ -11,6 +11,7 @@ local Class = require( 'lib.Middleclass' ) local TileFactory = require( 'src.map.tiles.TileFactory' ) local WorldObjectFactory = require( 'src.map.worldobjects.WorldObjectFactory' ) +local Map = require( 'src.map.Map' ) -- ------------------------------------------------ -- Module @@ -23,53 +24,48 @@ local MapLoader = Class( 'MapLoader' ) -- ------------------------------------------------ --- --- Recreates a world object. --- @tparam string id The id of the world object to create. --- @tparam number hp The world object's hit points. --- @tparam boolean passable Wether this world object is passable. --- @tparam boolean blocksVision Wether this world object blocks vision. --- @tparam Inventory inventory The world object's inventory (if it is a container). --- @treturn WorldObject The loaded world object. +-- Recreates the worldObject layer from a saved map and places it on a new map +-- instance. +-- @tparam Map map The map instance to place the worldObjects on. +-- @tparam table worldObjects The saved data for the world objects. -- -local function recreateWorldObject( id, hp, passable, blocksVision, inventory ) - local worldObject = WorldObjectFactory.create( id ) - worldObject:setHitPoints( hp ) - worldObject:setPassable( passable ) - worldObject:setBlocksVision( blocksVision ) - if worldObject:isContainer() and inventory then - worldObject:getInventory():loadItems( inventory ) +local function loadWorldObjects( map, worldObjects ) + for _, worldObjectData in ipairs( worldObjects ) do + -- Recreate the world object. + local worldObject = WorldObjectFactory.create( worldObjectData.id ) + + worldObject:setHitPoints( worldObjectData.hp ) + worldObject:setPassable( worldObjectData.passable ) + worldObject:setBlocksVision( worldObjectData.blocksVision ) + + -- Recreate inventory if world object is a container. + if worldObject:isContainer() and worldObjectData.inventory then + worldObject:getInventory():loadItems( worldObjectData.inventory ) + end + + -- Place world object on the map. + map:setWorldObjectAt( worldObjectData.x, worldObjectData.y, worldObject ) end - return worldObject end --- --- Recreates both tiles and world objects from a save file. --- @tparam table savedTiles The save data for both tiles and world objects. --- @treturn table A table containing the recreated tiles and world objects. +-- Recreates the tile layer from a saved map and places it on a new map instance. +-- @tparam Map map The map instance to place the tiles on. +-- @tparam table tiles The save data for the tiles. -- -local function loadSavedTiles( savedTiles ) - local loadedTiles = {} - for _, tile in ipairs( savedTiles ) do +local function loadTiles( map, tiles ) + for _, tileData in ipairs( tiles ) do -- Recreate the tile. - local recreatedTile = TileFactory.create( tile.id ) - - -- Recreate any worldobject that was located on the tile. - if tile.worldObject then - local obj = tile.worldObject - local worldObject = recreateWorldObject( obj.id, obj.hp, obj.passable, obj.blocksVision, obj.inventory ) - recreatedTile:addWorldObject( worldObject ) - end + local tile = TileFactory.create( tileData.id ) -- Recreate the tile's inventory if it had one. - if tile.inventory then - recreatedTile:getInventory():loadItems( tile.inventory ) + if tileData.inventory then + tile:getInventory():loadItems( tileData.inventory ) end - -- Store tile in the table. - loadedTiles[tile.x] = loadedTiles[tile.x] or {} - loadedTiles[tile.x][tile.y] = recreatedTile + -- Place tile on the map. + map:setTileAt( tileData.x, tileData.y, tile ) end - return loadedTiles end -- ------------------------------------------------ @@ -79,12 +75,15 @@ end --- -- Recreates a map from a savefile. -- @tparam table savedmap A table containing the saved map information. --- @treturn table A table containing the recreated tiles and world objects. --- @treturn number The width of the recreated map. --- @treturn number The height of the recreated map. +-- @treturn Map The loaded map instance. -- function MapLoader:recreateMap( savedmap ) - return loadSavedTiles( savedmap.tiles ), savedmap.width, savedmap.height + local map = Map( savedmap.width, savedmap.height ) + + loadTiles( map, savedmap.tiles ) + loadWorldObjects( map, savedmap.worldObjects ) + + return map end return MapLoader From 97908e2a0e8325ccefe2b0209ca11c668518faab Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 20 Aug 2018 19:35:42 +0200 Subject: [PATCH 66/93] Refactor SaveHandler --- src/SaveHandler.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/SaveHandler.lua b/src/SaveHandler.lua index ec2dd30c..dfcbb99b 100644 --- a/src/SaveHandler.lua +++ b/src/SaveHandler.lua @@ -6,14 +6,14 @@ -- Required Modules -- ------------------------------------------------ -local Log = require( 'src.util.Log' ); +local Log = require( 'src.util.Log' ) local Compressor = require( 'src.util.Compressor' ) -- ------------------------------------------------ -- Module -- ------------------------------------------------ -local SaveHandler = {}; +local SaveHandler = {} -- ------------------------------------------------ -- Constants @@ -75,4 +75,4 @@ function SaveHandler.getSaveFolder() return SAVE_FOLDER end -return SaveHandler; +return SaveHandler From 229ffbf644ca79360e1626be54fd12e502bd5d6a Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 20 Aug 2018 19:35:57 +0200 Subject: [PATCH 67/93] Print info when a new save is created --- src/SaveHandler.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SaveHandler.lua b/src/SaveHandler.lua index dfcbb99b..5c76b074 100644 --- a/src/SaveHandler.lua +++ b/src/SaveHandler.lua @@ -41,7 +41,7 @@ end -- ------------------------------------------------ function SaveHandler.save( t, name ) - Log.print( 'Created savegame: ' .. name, 'SaveHandler' ) + Log.info( 'Created savegame: ' .. name, 'SaveHandler' ) -- Create the saves folder it doesn't exist already. if not love.filesystem.getInfo( SAVE_FOLDER ) then From a971e2d2eb02c5f81f686473d3cc4fd47905e95a Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 20 Aug 2018 20:43:58 +0200 Subject: [PATCH 68/93] Change file name of save file --- src/SaveHandler.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SaveHandler.lua b/src/SaveHandler.lua index 5c76b074..295592d1 100644 --- a/src/SaveHandler.lua +++ b/src/SaveHandler.lua @@ -20,7 +20,7 @@ local SaveHandler = {} -- ------------------------------------------------ local SAVE_FOLDER = 'saves' -local COMPRESSED_SAVE = 'compressed.data' +local COMPRESSED_SAVE = 'game.data' local VERSION_FILE = 'version.data' -- ------------------------------------------------ From 5bcb1143f5e50a43373ce330ad406986f682423e Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 20 Aug 2018 20:50:15 +0200 Subject: [PATCH 69/93] Change map to observe all map objects --- src/map/Map.lua | 24 ++++--------------- src/map/procedural/ProceduralMapGenerator.lua | 1 - 2 files changed, 5 insertions(+), 20 deletions(-) diff --git a/src/map/Map.lua b/src/map/Map.lua index f7d17375..8d4ca24f 100644 --- a/src/map/Map.lua +++ b/src/map/Map.lua @@ -69,19 +69,6 @@ local function createWorldObjectNeighbours( self, x, y ) return neighbours end ---- --- Observers each tile so the map can receive events from them. --- @tparam Map self The map instance to use. --- @tparam table tiles A table containing all of the Map's tiles. --- -local function observeTiles( self, tiles ) - for x = 1, #tiles do - for y = 1, #tiles[x] do - tiles[x][y]:observe( self ) - end - end -end - --- -- Creates an empty two dimensional array. -- @return table The newly created grid. @@ -293,6 +280,8 @@ function Map:setCharacterAt( x, y, character ) character:setPosition( x, y ) character:setMap( self ) + + character:observe( self ) end --- @@ -306,6 +295,8 @@ function Map:setTileAt( x, y, tile ) tile:setPosition( x, y ) tile:setMap( self ) + + tile:observe( self ) end --- @@ -319,13 +310,8 @@ function Map:setWorldObjectAt( x, y, worldObject ) worldObject:setPosition( x, y ) worldObject:setMap( self ) -end ---- --- TODO remove! --- -function Map:initializeGrid() - observeTiles( self, self.tiles ) + worldObject:observe( self ) end --- diff --git a/src/map/procedural/ProceduralMapGenerator.lua b/src/map/procedural/ProceduralMapGenerator.lua index 383f8069..da0e1a53 100644 --- a/src/map/procedural/ProceduralMapGenerator.lua +++ b/src/map/procedural/ProceduralMapGenerator.lua @@ -259,7 +259,6 @@ function ProceduralMapGenerator:createMap( layout ) spawnFoliage( map, self.parcelGrid ) createSpawnPoints( self.spawnpoints, self.layout.spawns ) - map:initializeGrid() -- TODO remove map:setSpawnpoints( self.spawnpoints ) return map From 23620099bcedcde57334194b95ca82c541398b6a Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 20 Aug 2018 22:27:48 +0200 Subject: [PATCH 70/93] Use Tile instance as index for FOV table This increases performance for the "canSee" checks as it uses the hash part of the table instead of creating a sparse 2d grid. See #253. --- src/characters/Character.lua | 25 ++++++------------- .../ai/behaviortree/leafs/BTAquireTarget.lua | 13 +--------- .../ai/behaviortree/leafs/BTCanSeeItem.lua | 15 ++--------- .../behaviortree/leafs/BTRandomMovement.lua | 10 +++----- 4 files changed, 15 insertions(+), 48 deletions(-) diff --git a/src/characters/Character.lua b/src/characters/Character.lua index 832467c1..a75c836c 100644 --- a/src/characters/Character.lua +++ b/src/characters/Character.lua @@ -55,7 +55,7 @@ local function markSeenTiles( cx, cy, counter, self, falloff ) local height = self:getHeight() - (counter+1) * falloff if height <= target:getHeight() then -- Add tile to this character's FOV. - self:addSeenTile( cx, cy, target ) + self:addSeenTile( target ) -- Mark tile for drawing update. target:setDirty( true ) @@ -92,11 +92,9 @@ end -- @tparam table fov The table containing the tiles the character can see. -- local function resetFOV( fov ) - for x, row in pairs( fov ) do - for y, target in pairs( row ) do - target:setDirty( true ) - fov[x][y] = nil - end + for target, _ in pairs( fov ) do + target:setDirty( true ) + fov[target] = nil end end @@ -159,13 +157,10 @@ end --- -- Adds a tile to this character's FOV. --- @tparam number tx The target-tile's position along the x-axis. --- @tparam number ty The target-tile's position along the y-axis. --- @tparam Tile target The target-tile. +-- @tparam Tile target The target-tile. -- -function Character:addSeenTile( tx, ty, target ) - self.fov[tx] = self.fov[tx] or {} - self.fov[tx][ty] = target +function Character:addSeenTile( target ) + self.fov[target] = true end --- @@ -259,11 +254,7 @@ end -- @treturn boolean Wether the character sees the tile. -- function Character:canSee( target ) - local tx, ty = target:getPosition() - if not self.fov[tx] then - return false - end - return self.fov[tx][ty] ~= nil + return self.fov[target] == true end --- diff --git a/src/characters/ai/behaviortree/leafs/BTAquireTarget.lua b/src/characters/ai/behaviortree/leafs/BTAquireTarget.lua index d0605bb8..c30f56e2 100644 --- a/src/characters/ai/behaviortree/leafs/BTAquireTarget.lua +++ b/src/characters/ai/behaviortree/leafs/BTAquireTarget.lua @@ -28,20 +28,9 @@ local FACTIONS = require( 'src.constants.FACTIONS' ) function BTAquireTarget:traverse( ... ) local blackboard, character = ... - local tiles = {} - - -- Get the character's FOV and store the tiles in a sequence for easier access. - local fov = character:getFOV() - for _, rx in pairs( fov ) do - for _, target in pairs( rx ) do - tiles[#tiles + 1] = target - end - end - -- Get all characters visible to this character. local enemies = {} - for i = 1, #tiles do - local tile = tiles[i] + for tile in pairs( character:getFOV() ) do if tile:hasCharacter() and not tile:getCharacter():isDead() and tile:getCharacter():getFaction():getType() ~= FACTIONS.NEUTRAL diff --git a/src/characters/ai/behaviortree/leafs/BTCanSeeItem.lua b/src/characters/ai/behaviortree/leafs/BTCanSeeItem.lua index 55d8f85c..09804d10 100644 --- a/src/characters/ai/behaviortree/leafs/BTCanSeeItem.lua +++ b/src/characters/ai/behaviortree/leafs/BTCanSeeItem.lua @@ -22,20 +22,9 @@ local BTCanSeeItem = BTLeaf:subclass( 'BTCanSeeItem' ) function BTCanSeeItem:traverse( ... ) local blackboard, character = ... - local tiles = {} - - -- Get the character's FOV and store the tiles in a sequence for easier access. - local fov = character:getFOV() - for _, rx in pairs( fov ) do - for _, target in pairs( rx ) do - tiles[#tiles + 1] = target - end - end - - -- Look for any items on those tiles. + -- Look for any items on the seen tiles. local items = {} - for i = 1, #tiles do - local tile = tiles[i] + for tile in pairs( character:getFOV() ) do if not tile:getInventory():isEmpty() then items[#items + 1] = tile end diff --git a/src/characters/ai/behaviortree/leafs/BTRandomMovement.lua b/src/characters/ai/behaviortree/leafs/BTRandomMovement.lua index 460cab9d..b166d647 100644 --- a/src/characters/ai/behaviortree/leafs/BTRandomMovement.lua +++ b/src/characters/ai/behaviortree/leafs/BTRandomMovement.lua @@ -9,6 +9,7 @@ local Log = require( 'src.util.Log' ) local BTLeaf = require( 'src.characters.ai.behaviortree.leafs.BTLeaf' ) local PathFinder = require( 'src.characters.pathfinding.PathFinder' ) +local Util = require( 'src.util.Util' ) -- ------------------------------------------------ -- Module @@ -36,14 +37,11 @@ function BTRandomMovement:traverse( ... ) local tiles = {} -- Get the character's FOV and store the tiles in a sequence for easier access. - local fov = character:getFOV() - for _, rx in pairs( fov ) do - for _, target in pairs( rx ) do - tiles[#tiles + 1] = target - end + for tile in pairs( character:getFOV() ) do + tiles[#tiles + 1] = tile end - local target = tiles[love.math.random( 1, #tiles )] + local target = Util.pickRandomValue( tiles ) if target and target:isPassable() and not target:hasCharacter() then local path = generatePath( target, character ) if path then From 280e80fd6cfd278cf329d8a6fd50b7096490aab6 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 20 Aug 2018 23:34:26 +0200 Subject: [PATCH 71/93] Use removeCharacter method to remove Character --- src/map/Map.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/map/Map.lua b/src/map/Map.lua index 8d4ca24f..3a3109e1 100644 --- a/src/map/Map.lua +++ b/src/map/Map.lua @@ -267,7 +267,7 @@ function Map:setCharacterAt( x, y, character ) -- Remove character from old position if necessary. local oldX, oldY = character:getPosition() if oldX and oldY then - self.characters[oldX][oldY] = nil + self:removeCharacter( oldX, oldY, character ) end -- Make sure the grid space is empty. From 97668ac833b423ee3dd91e0ec152d21e6d0ea181 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 20 Aug 2018 23:53:14 +0200 Subject: [PATCH 72/93] Limit amount of updates for the tile info UI --- src/ui/elements/UITileInfo.lua | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/ui/elements/UITileInfo.lua b/src/ui/elements/UITileInfo.lua index 71337564..b94cf276 100644 --- a/src/ui/elements/UITileInfo.lua +++ b/src/ui/elements/UITileInfo.lua @@ -152,17 +152,30 @@ function UITileInfo:draw() end function UITileInfo:update( mouseX, mouseY, map ) - self.textObject:clear() - local tile = map:getTileAt( mouseX, mouseY ) + + -- Clear the text object and do not update if the mouse currently isn't + -- hovering over a map tile. if not tile then + self.tile = nil + self.textObject:clear() + return + end + + -- Only update the text object if the target has changed to a new tile. + if tile == self.tile then return end - if tile:hasWorldObject() then - inspectWorldObject( self.textObject, self.colorTable, tile:getWorldObject() ) + -- Clear text object and store the new target as the current tile. + self.textObject:clear() + self.tile = tile + + -- Update text object. + if self.tile:hasWorldObject() then + inspectWorldObject( self.textObject, self.colorTable, self.tile:getWorldObject() ) else - inspectTile( self.textObject, self.colorTable, tile ) + inspectTile( self.textObject, self.colorTable, self.tile ) end end From e7253886f08d1109f572c05e42d768bdd6f11931 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Tue, 21 Aug 2018 00:58:38 +0200 Subject: [PATCH 73/93] Use factionFOV to speed up tile visibility checks Each character of a faction increments the factionFOV variable by one if he can see the tile and decrements the factionFOV when he can't see the tile anymore. This way we can quickly check wether a tile is visible to a faction or not (factionFOV > 0). Closes #253. --- src/CombatState.lua | 2 +- src/characters/Character.lua | 16 +++++++++---- src/characters/Faction.lua | 15 ------------ src/map/tiles/Tile.lua | 31 +++++++++++++++++++++++++ src/turnbased/states/ExecutionState.lua | 3 ++- src/ui/MapPainter.lua | 4 ++-- src/ui/overlays/OverlayPainter.lua | 8 ++++++- 7 files changed, 55 insertions(+), 24 deletions(-) diff --git a/src/CombatState.lua b/src/CombatState.lua index 612af621..2e02f3a5 100644 --- a/src/CombatState.lua +++ b/src/CombatState.lua @@ -121,7 +121,7 @@ end function CombatState:receive( event, ... ) if event == 'MESSAGE_LOG_EVENT' then local origin, msg, type = ... - if self.factions:getPlayerFaction():canSee( origin ) then + if origin:isSeenBy( FACTIONS.ALLIED ) then MessageQueue.enqueue( msg, type ) end end diff --git a/src/characters/Character.lua b/src/characters/Character.lua index a75c836c..4cc45ebd 100644 --- a/src/characters/Character.lua +++ b/src/characters/Character.lua @@ -89,11 +89,13 @@ end --- -- Clears the list of seen tiles and marks them for a drawing update. --- @tparam table fov The table containing the tiles the character can see. +-- @tparam table fov The table containing the tiles the character can see. +-- @tparam string factionType The faction's id as defined in the faction constants. -- -local function resetFOV( fov ) +local function resetFOV( fov, factionType ) for target, _ in pairs( fov ) do target:setDirty( true ) + target:decrementFactionFOV( factionType ) fov[target] = nil end end @@ -107,7 +109,7 @@ local function handleDeath( self ) self:getEquipment():dropAllItems( self:getTile() ) self:getInventory():dropAllItems( self:getTile() ) self.map:removeCharacter( self.x, self.y, self ) - resetFOV( self.fov ) + resetFOV( self.fov, self:getFaction():getType() ) end -- ------------------------------------------------ @@ -160,6 +162,12 @@ end -- @tparam Tile target The target-tile. -- function Character:addSeenTile( target ) + -- Update the faction FOV for the tile if it hasn't been seen by this + -- character already. + if not self.fov[target] then + target:incrementFactionFOV( self:getFaction():getType() ) + end + self.fov[target] = true end @@ -212,7 +220,7 @@ end -- the blocksVision attribute set to true. -- function Character:generateFOV() - resetFOV( self.fov ) + resetFOV( self.fov, self:getFaction():getType() ) local range = self.body:getStatusEffects():isBlind() and 1 or self:getViewRange() local list = Util.getTilesInCircle( self.map, self:getTile(), range ) diff --git a/src/characters/Faction.lua b/src/characters/Faction.lua index 21858bd9..2d7d17c9 100644 --- a/src/characters/Faction.lua +++ b/src/characters/Faction.lua @@ -115,21 +115,6 @@ function Faction:spawnCharacters( map ) end) end ---- --- Checks if any of the characters in this Faction can see the target tile. --- @tparam Tile target The tile to check visibility for. --- @treturn boolean Wether a character can see this tile. --- -function Faction:canSee( tile ) - local node = self.root - while node do - if node:getObject():canSee( tile ) then - return true - end - node = node:getNext() - end -end - --- -- Deactivates this Faction right before it is deselected. -- diff --git a/src/map/tiles/Tile.lua b/src/map/tiles/Tile.lua index 9dd2cbeb..3599b9b3 100644 --- a/src/map/tiles/Tile.lua +++ b/src/map/tiles/Tile.lua @@ -19,6 +19,7 @@ local Tile = MapObject:subclass( 'Tile' ) -- Constants -- ------------------------------------------------ +local FACTIONS = require( 'src.constants.FACTIONS' ) local WEIGHT_LIMIT = 1000 local VOLUME_LIMIT = 1000 local DEFAULT_HEIGHT = 10 @@ -42,6 +43,12 @@ function Tile:initialize( id, cost, passable, spawn ) self.passable = passable self.spawn = spawn + self.factionFOV = { + [FACTIONS.ALLIED] = 0, + [FACTIONS.NEUTRAL] = 0, + [FACTIONS.ENEMY] = 0 + } + self.inventory = Inventory( WEIGHT_LIMIT, VOLUME_LIMIT ) end @@ -59,6 +66,22 @@ function Tile:hit( damage, damageType ) end end +--- +-- Increments the faction FOV for a particular faction. +-- @tparam string factionType The faction's id as defined in the faction constants. +-- +function Tile:incrementFactionFOV( factionType ) + self.factionFOV[factionType] = self.factionFOV[factionType] + 1 +end + +--- +-- Decrements the faction FOV for a particular faction. +-- @tparam string factionType The faction's id as defined in the faction constants. +-- +function Tile:decrementFactionFOV( factionType ) + self.factionFOV[factionType] = self.factionFOV[factionType] - 1 +end + --- -- Serializes the tile. -- @treturn table The serialized Tile object. @@ -168,6 +191,14 @@ function Tile:isSpawn() return self.spawn end +--- +-- Determines wether the tile is seen by a certain faction. +-- @treturn boolean True if the tile is seen by at least one character. +-- +function Tile:isSeenBy( factionType ) + return self.factionFOV[factionType] > 0 +end + -- ------------------------------------------------ -- Setters -- ------------------------------------------------ diff --git a/src/turnbased/states/ExecutionState.lua b/src/turnbased/states/ExecutionState.lua index ad0d2a48..8a97879e 100644 --- a/src/turnbased/states/ExecutionState.lua +++ b/src/turnbased/states/ExecutionState.lua @@ -21,6 +21,7 @@ local ExecutionState = Class( 'ExecutionState' ) -- Constants -- ------------------------------------------------ +local FACTIONS = require( 'src.constants.FACTIONS' ) local AI_DELAY = 0 local PLAYER_DELAY = 0.15 @@ -60,7 +61,7 @@ function ExecutionState:update( dt ) if self.character:hasEnqueuedAction() then self.character:performAction() - if self.factions:getPlayerFaction():canSee( self.character:getTile() ) then + if self.character:getTile():isSeenBy( FACTIONS.ALLIED ) then self.delay = PLAYER_DELAY else self.delay = AI_DELAY diff --git a/src/ui/MapPainter.lua b/src/ui/MapPainter.lua index 11f31c93..f1b505f7 100644 --- a/src/ui/MapPainter.lua +++ b/src/ui/MapPainter.lua @@ -106,7 +106,7 @@ local function selectTileColor( tile, worldObject, character, faction ) -- the active character. if faction then -- Dim tiles hidden from the player. - if not faction:canSee( tile ) then + if not tile:isSeenBy( faction:getType() ) then return TexturePacks.getColor( 'tile_unseen' ) end @@ -200,7 +200,7 @@ end -- @treturn Quad A quad pointing to a sprite on the tileset. -- local function selectTileSprite( tile, worldObject, character, faction ) - if character and faction and faction:canSee( tile ) then + if character and tile:isSeenBy( faction:getType() ) then return selectCharacterTile( character ) end diff --git a/src/ui/overlays/OverlayPainter.lua b/src/ui/overlays/OverlayPainter.lua index 51647e2c..8dacef77 100644 --- a/src/ui/overlays/OverlayPainter.lua +++ b/src/ui/overlays/OverlayPainter.lua @@ -27,6 +27,12 @@ local ExecutionState = require( 'src.turnbased.states.ExecutionState' ) local OverlayPainter = Class( 'OverlayPainter' ) +-- ------------------------------------------------ +-- Constants +-- ------------------------------------------------ + +local FACTIONS = require( 'src.constants.FACTIONS' ) + -- ------------------------------------------------ -- Private Methods -- ------------------------------------------------ @@ -61,7 +67,7 @@ end local function drawParticles( tileset, tw, th, particleLayer, game ) for x, row in pairs( particleLayer:getParticleGrid() ) do for y, particle in pairs( row ) do - if game:getFactions():getPlayerFaction():canSee( game:getMap():getTileAt( x, y )) then + if game:getMap():getTileAt( x, y ):isSeenBy( FACTIONS.ALLIED ) then love.graphics.setColor( particle:getColors() ) if particle:getSprite() then love.graphics.draw( tileset:getSpritesheet(), tileset:getSprite( particle:getSprite() ), x * tw, y * th ) From f688f35646817f55e7bf6611f6220312c4c13613 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Tue, 21 Aug 2018 01:27:27 +0200 Subject: [PATCH 74/93] Hide tile info for unseen characters The new function for checking wether a tile is visible which was introduced in e7253886f08d1109f572c05e42d768bdd6f11931 allows us to efficiently check wether a tile is visible to the player. In this case we can now hide the info about enemy characters which can't be seen by the player's characters. Closes #255. --- src/ui/elements/UITileInfo.lua | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/ui/elements/UITileInfo.lua b/src/ui/elements/UITileInfo.lua index b94cf276..4c952041 100644 --- a/src/ui/elements/UITileInfo.lua +++ b/src/ui/elements/UITileInfo.lua @@ -98,6 +98,11 @@ local function inspectTile( textObject, colorTable, tile ) addToTextObject( textObject, colorTable, x, y, TexturePacks.getColor( 'ui_text_error' ), Translator.getText( 'ui_tile_info_impassable' )) end + -- Check if the tile is seen by the player's faction. + if not tile:isSeenBy( FACTIONS.ALLIED ) then + return + end + local _, th = TexturePacks.getTileDimensions() if tile:hasCharacter() then showCharacterInfo( textObject, colorTable, x, UI_CHARACTER_INFO * th, tile:getCharacter() ) From be09f62802bb57250b1f2dac8a145ed3b211b0b6 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Wed, 22 Aug 2018 16:46:25 +0200 Subject: [PATCH 75/93] Adjust world object hit points Fixes #251. --- res/data/WorldObjects.lua | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/res/data/WorldObjects.lua b/res/data/WorldObjects.lua index 81dc5480..ba670515 100644 --- a/res/data/WorldObjects.lua +++ b/res/data/WorldObjects.lua @@ -2,7 +2,7 @@ return { { id = 'worldobject_chair', size = 15, - hp = 45, + hp = 10, interactionCost = { stand = 5, crouch = 6, @@ -20,7 +20,7 @@ return { { id = 'worldobject_toilet', size = 15, - hp = 65, + hp = 12, interactionCost = { stand = 5, crouch = 6, @@ -37,7 +37,7 @@ return { { id = 'worldobject_shower', size = 15, - hp = 35, + hp = 12, interactionCost = { stand = 5, crouch = 6, @@ -54,7 +54,7 @@ return { { id = 'worldobject_crate', size = 50, - hp = 110, + hp = 30, energyReduction = 50, destructible = true, blocksVision = true, @@ -68,7 +68,7 @@ return { { id = 'worldobject_door', size = 100, - hp = 110, + hp = 40, interactionCost = { stand = 3, crouch = 3, @@ -88,7 +88,7 @@ return { { id = 'worldobject_fence', size = 35, - hp = 55, + hp = 14, interactionCost = { stand = 5, crouch = 6, @@ -109,7 +109,7 @@ return { { id = 'worldobject_fencegate', size = 40, - hp = 65, + hp = 15, interactionCost = { stand = 3, crouch = 3, @@ -144,7 +144,7 @@ return { { id = 'worldobject_table', size = 25, - hp = 65, + hp = 20, interactionCost = { stand = 5, crouch = 6, @@ -178,7 +178,7 @@ return { { id = 'worldobject_window', size = 100, - hp = 25, + hp = 1, energyReduction = 10, destructible = true, debrisID = 'worldobject_lowwall', From e86b1666d34afbc05d839bcc1c41cfc65d5b176d Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Tue, 21 Aug 2018 02:07:47 +0200 Subject: [PATCH 76/93] Fix removal of destroyed world objects Fixes #257. --- src/CombatState.lua | 2 - src/map/Map.lua | 100 ++++++++++++++++----------- src/map/worldobjects/WorldObject.lua | 4 ++ 3 files changed, 63 insertions(+), 43 deletions(-) diff --git a/src/CombatState.lua b/src/CombatState.lua index 2e02f3a5..c0a3891c 100644 --- a/src/CombatState.lua +++ b/src/CombatState.lua @@ -100,8 +100,6 @@ function CombatState:initialize( playerFaction, savegame ) end function CombatState:update( dt ) - self.map:update() - -- Update AI if current faction is AI controlled. if self.factions:getFaction():isAIControlled() and not self.stateManager:blocksInput() then self.sadisticAIDirector:update( dt ) diff --git a/src/map/Map.lua b/src/map/Map.lua index 3a3109e1..63ec28dd 100644 --- a/src/map/Map.lua +++ b/src/map/Map.lua @@ -135,48 +135,10 @@ function Map:findSpawnPoint( faction ) end --- --- Updates the map. This resets the visibility attribute for all visible --- tiles and marks them for a drawing update. It also replaces destroyed --- WorldObjects with their debris types or removes them completely. +-- Receives events published by Observables. +-- @tparam string event A string by which the message can be identified. +-- @tparam varargs ... Multiple parameters to push to the receiver. -- -function Map:update() - for x = 1, #self.tiles do - for y = 1, #self.tiles[x] do - local tile = self.tiles[x][y] - if tile:hasWorldObject() and tile:getWorldObject():isDestroyed() then - -- Create items from the destroyed object. - for _, drop in ipairs( tile:getWorldObject():getDrops() ) do - local id, tries, chance = drop.id, drop.tries, drop.chance - for _ = 1, tries do - if love.math.random( 100 ) < chance then - local item = ItemFactory.createItem( id ) - tile:getInventory():addItem( item ) - end - end - end - - -- If the world object was a container drop the items in it. - if tile:getWorldObject():isContainer() and not tile:getWorldObject():getInventory():isEmpty() then - local items = tile:getWorldObject():getInventory():getItems() - for _, item in pairs( items ) do - tile:getInventory():addItem( item ) - end - end - - -- If the world object has a debris object, place that on the tile. - if tile:getWorldObject():getDebrisID() then - local nobj = WorldObjectFactory.create( tile:getWorldObject():getDebrisID() ) - tile:removeWorldObject() - tile:addWorldObject( nobj ) - else - tile:removeWorldObject() - end - self:publish( 'TILE_UPDATED', tile ) - end - end - end -end - function Map:receive( event, ... ) self:publish( event, ... ) end @@ -195,6 +157,57 @@ function Map:removeCharacter( x, y, character ) self.characters[x][y] = nil end +--- +-- Removes a WorldObject from a specific position on the worldObject layer. +-- @tparam number x The target position along the x-axis. +-- @tparam number y The target position along the y-axis. +-- @tparam WorldObject worldObject The worldObject to remove from the grid. +-- +function Map:removeWorldObject( x, y, worldObject ) + if worldObject ~= self.worldObjects[x][y] then + error( string.format( 'WorldObject at position (%s, %s) does not match the WorldObject to remove.', x, y )) + end + + self.worldObjects[x][y] = nil +end + +--- +-- Destroys a WorldObject at a specific position on the worldObject layer. +-- @tparam number x The target position along the x-axis. +-- @tparam number y The target position along the y-axis. +-- @tparam WorldObject worldObject The worldObject to destroy. +-- +function Map:destroyWorldObject( x, y, worldObject ) + local tile = self:getTileAt( x, y ) + + -- Create items from the destroyed object. + for _, drop in ipairs( worldObject:getDrops() ) do + local id, tries, chance = drop.id, drop.tries, drop.chance + for _ = 1, tries do + if love.math.random( 100 ) < chance then + tile:getInventory():addItem( ItemFactory.createItem( id )) + end + end + end + + -- If the world object is a container, drop any contained items. + if worldObject:isContainer() and not worldObject:getInventory():isEmpty() then + for _, item in pairs( worldObject:getInventory():getItems() ) do + tile:getInventory():addItem( item ) + end + end + + -- Remove the object from the map. + self:removeWorldObject( x, y, worldObject ) + + -- Place a debris object if the destroyed world object has one. + if worldObject:getDebrisID() then + self:setWorldObjectAt( x, y, WorldObjectFactory.create( worldObject:getDebrisID() )) + end + + self:publish( 'TILE_UPDATED', tile ) +end + -- ------------------------------------------------ -- Getters -- ------------------------------------------------ @@ -306,6 +319,11 @@ end -- @tparam WorldObject The worldObject to set to the grid. -- function Map:setWorldObjectAt( x, y, worldObject ) + -- Make sure the grid space is empty. + if self.worldObjects[x][y] ~= nil then + error( string.format( 'Target position (%s, %s) is already occupied by a WorldObject.', x, y )) + end + self.worldObjects[x][y] = worldObject worldObject:setPosition( x, y ) diff --git a/src/map/worldobjects/WorldObject.lua b/src/map/worldobjects/WorldObject.lua index 8ece7145..427b2c49 100644 --- a/src/map/worldobjects/WorldObject.lua +++ b/src/map/worldobjects/WorldObject.lua @@ -53,6 +53,10 @@ end -- function WorldObject:damage( dmg ) self.hp = self.hp - dmg + + if self.destructible and self.hp <= 0 then + self.map:destroyWorldObject( self.x, self.y, self ) + end end --- From a448361bc339048a741ac05ce385f5d97c023ae1 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Wed, 22 Aug 2018 18:33:43 +0200 Subject: [PATCH 77/93] Use observer as index --- src/util/Observable.lua | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/util/Observable.lua b/src/util/Observable.lua index 1b35eb82..73e1e0f0 100644 --- a/src/util/Observable.lua +++ b/src/util/Observable.lua @@ -20,22 +20,19 @@ local Observable = Class( 'Observable' ) function Observable:initialize() self.observers = {} - self.index = 1 end function Observable:observe( observer ) assert( observer.receive, "Observer has to have a public receive method." ) - self.observers[self.index] = observer - self.index = self.index + 1 - return self.index + self.observers[observer] = true end -function Observable:remove( index ) - self.observers[index] = nil +function Observable:remove( observer ) + self.observers[observer] = nil end function Observable:publish( event, ... ) - for _, observer in pairs( self.observers ) do + for observer, _ in pairs( self.observers ) do observer:receive( event, ... ) end end From 827951ccca51f2397bf5eb4992d96548737f52f0 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Wed, 22 Aug 2018 18:55:36 +0200 Subject: [PATCH 78/93] Fix crash on inventory screen This crash was caused by trying to drag the info label at the top of the list. --- src/ui/elements/inventory/UIInventoryList.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ui/elements/inventory/UIInventoryList.lua b/src/ui/elements/inventory/UIInventoryList.lua index 50d303af..b11f8a64 100644 --- a/src/ui/elements/inventory/UIInventoryList.lua +++ b/src/ui/elements/inventory/UIInventoryList.lua @@ -84,7 +84,7 @@ end function UIInventoryList:drag( rmb, fullstack ) for _, uiItem in ipairs( self.children ) do - if uiItem:isMouseOver() then + if uiItem:isInstanceOf( UIInventoryItem ) and uiItem:isMouseOver() then local item = uiItem:drag( rmb, fullstack ) self.inventory:removeItem( item ) self:refresh() @@ -95,7 +95,7 @@ end function UIInventoryList:drop( item ) for _, uiItem in ipairs( self.children ) do - if uiItem:isMouseOver() then + if uiItem:isInstanceOf( UIInventoryItem ) and uiItem:isMouseOver() then local success = self.inventory:insertItem( item, uiItem:getItem() ) if success then self:refresh() @@ -119,7 +119,7 @@ end -- function UIInventoryList:getItemBelowCursor() for _, uiItem in ipairs( self.children ) do - if uiItem:isMouseOver() then + if uiItem:isInstanceOf( UIInventoryItem ) and uiItem:isMouseOver() then return uiItem:getItem() end end From d8b904515c8491bede0afa43726a2ddf5e42c013 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Wed, 22 Aug 2018 19:07:03 +0200 Subject: [PATCH 79/93] Use love.graphics.printf to draw UILabel This allows us to make use of the "align" functionality. --- src/ui/elements/UILabel.lua | 5 +++-- src/ui/elements/inventory/UIInventoryItem.lua | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/ui/elements/UILabel.lua b/src/ui/elements/UILabel.lua index 7b950b39..78aa65ad 100644 --- a/src/ui/elements/UILabel.lua +++ b/src/ui/elements/UILabel.lua @@ -20,18 +20,19 @@ local UILabel = UIElement:subclass( 'UILabel' ) -- Constructor -- ------------------------------------------------ -function UILabel:initialize( px, py, x, y, w, h, text, color ) +function UILabel:initialize( px, py, x, y, w, h, text, color, align ) UIElement.initialize( self, px, py, x, y, w, h ) self.text = text self.color = color or 'sys_reset' + self.align = align or 'left' end function UILabel:draw() local tw, th = TexturePacks.getTileDimensions() TexturePacks.setColor( self.color ) - love.graphics.print( self.text, self.ax * tw, self.ay * th ) + love.graphics.printf( self.text, self.ax * tw, self.ay * th, self.w * tw, self.align ) TexturePacks.resetColor() end diff --git a/src/ui/elements/inventory/UIInventoryItem.lua b/src/ui/elements/inventory/UIInventoryItem.lua index d3758f20..e061a669 100644 --- a/src/ui/elements/inventory/UIInventoryItem.lua +++ b/src/ui/elements/inventory/UIInventoryItem.lua @@ -35,7 +35,7 @@ local function createInfo( self ) if self.item:isInstanceOf( ItemStack ) and self.item:getItemCount() > 1 then count = self.item:getItemCount() end - self.amount = UILabel( self.ax, self.ay, self.w-2, 0, self.w, 1, count, 'ui_equipment_item' ) + self.amount = UILabel( self.ax, self.ay, 0, 0, self.w, 1, count, 'ui_equipment_item', 'right' ) self:addChild( self.amount ) end From acade9a09f3258335455ff2617f11b387818ec65 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Wed, 22 Aug 2018 20:58:05 +0200 Subject: [PATCH 80/93] Fix title strings --- res/text/en_EN/ui_text.lua | 62 ++++++++++++++++----------------- src/ui/elements/UIMenuTitle.lua | 4 +-- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/res/text/en_EN/ui_text.lua b/res/text/en_EN/ui_text.lua index aa79ff63..54e70bfd 100644 --- a/res/text/en_EN/ui_text.lua +++ b/res/text/en_EN/ui_text.lua @@ -119,63 +119,63 @@ locale.strings = { -- Titles ['ui_title_main_menu'] = { - " @@@@ @@ @@ @@@@@@@ @@@ @@@ @@@@@@@ ", - " @@@@@@@@ @@@ @@@ @@@@@@@ @@@ @@@ @@@@@@@@ ", - " @@! @@@ @@!@ @@@ @@! @@! @@@ @@! ", - " !@! @!@ !@!!@!@! !@! !@! @!@ !@! ", - " @!@ !@! @!@ !!@! @!! @!@!@!@! @!!!:! ", - " !@! !!! !@! !!! !!! !!!@!!!! !!!!!: ", + " OOOO OO OO OOOOOOO OOO OOO OOOOOOO ", + " OOOOOOOO OOO OOO OOOOOOO OOO OOO OOOOOOOO ", + " OO! OOO OO!O OOO OO! OO! OOO OO! ", + " !O! O!O !O!!O!O! !O! !O! O!O !O! ", + " O!O !O! O!O !!O! O!! O!O!O!O! O!!!:! ", + " !O! !!! !O! !!! !!! !!!O!!!! !!!!!: ", " !!: !!! !!: !!! !!: !!: !!! !!: ", " :!: !:! :!: !:! :!: :!: !:! :!: ", " :!:::!!: :: :: :: :: !: ::!::!! ", " :!:: : : : : : :!:::::! ", " ", - "@@@@@@@ @@@@ @@@@@@ @@@@@@ @@@@@ @@@ @@@@@@@ @@@@@@@ ", - "@@@@@@@@ @@@@@@@@ @@@@@@@@ @@@@@@@@ @@@@@@@ @@@ @@@@@@@@ @@@@@@@@", - "@@! @@@ @@! @@@ @@! @@@ @@! @@@ !@@ @@! @@! @@@ @@! ", - "!@! @!@ !@! @!@ !@! @!@ !@! @!@ !@! !@! !@! @!@ !@! ", - "@!@!!@! @!@ !@! @!@!@!@! @!@ !@! !!@@!! !!@ @!@ !@! @!!!:! ", - "!!@!@! !@! !!! !!!@!!!! !@! !!! !!@!!! !!! !@! !!! !!!!!: ", + "OOOOOOO OOOO OOOOOO OOOOOO OOOOO OOO OOOOOOO OOOOOOO ", + "OOOOOOOO OOOOOOOO OOOOOOOO OOOOOOOO OOOOOOO OOO OOOOOOOO OOOOOOOO", + "OO! OOO OO! OOO OO! OOO OO! OOO !OO OO! OO! OOO OO! ", + "!O! O!O !O! O!O !O! O!O !O! O!O !O! !O! !O! O!O !O! ", + "O!O!!O! O!O !O! O!O!O!O! O!O !O! !!OO!! !!O O!O !O! O!!!:! ", + "!!O!O! !O! !!! !!!O!!!! !O! !!! !!O!!! !!! !O! !!! !!!!!: ", "!!: :!! !!: !!! !!: !!! !!: !!! !:! !!: !!: !!! !!: ", ":!: !:! :!: !:! :!: !:! :!: !:! !:! :!: :!: !:! :!: ", " :: !: ::!:!!:: :: :: !:.:.::: ::!:::: :: !:!!::.: ::!:.:: ", " ! : ::!: ! : ::::..: :::.. : ::..:.: ::..::.:", }, ['ui_title_options'] = { - " @@@@ @@@@@@@ @@@@@@@ @@@ @@@@ @@ @@ @@@@@ ", - "@@@@@@@@ @@@@@@@@ @@@@@@@ @@@ @@@@@@@@ @@@ @@@ @@@@@@@ ", - "@@! @@@ @@! @@@ @@! @@! @@! @@@ @@!@ @@@ !@@ ", - "!@! @!@ !@! @!@ !@! !@! !@! @!@ !@!!@!@! !@! ", - "@!@ !@! @!@@!@! @!! !!@ @!@ !@! @!@ !!@! !!@@!! ", - "!@! !!! !!@!!! !!! !!! !@! !!! !@! !!! !!@!!! ", + " OOOO OOOOOOO OOOOOOO OOO OOOO OO OO OOOOO ", + "OOOOOOOO OOOOOOOO OOOOOOO OOO OOOOOOOO OOO OOO OOOOOOO ", + "OO! OOO OO! OOO OO! OO! OO! OOO OO!O OOO !OO ", + "!O! O!O !O! O!O !O! !O! !O! O!O !O!!O!O! !O! ", + "O!O !O! O!OO!O! O!! !!O O!O !O! O!O !!O! !!OO!! ", + "!O! !!! !!O!!! !!! !!! !O! !!! !O! !!! !!O!!! ", "!!: !!! !!: !!: !!: !!: !!! !!: !!! !:!", ":!: !:! :!: :!: :!: :!: !:! :!: !:! !:! ", ":!:::!!: :: :: :: :!:::!!: :: :: ::!:::: ", " :!:: : : : :!:: : : :::.. " }, ['ui_title_savegames'] = { - " @@@@@ @@@@@@ @@@ @@@ @@@@@@@ @@@@@ ", - "@@@@@@@ @@@@@@@@ @@@ @@@ @@@@@@@@ @@@@@@@ ", - "!@@ @@! @@@ @@! @@@ @@! !@@ ", - "!@! !@! @!@ !@! @!@ !@! !@! ", - "!!@@!! @!@!@!@! @!@ !@! @!!!:! !!@@!! ", - " !!@!!! !!!@!!!! !@! !!! !!!!!: !!@!!! ", + " OOOOO OOOOOO OOO OOO OOOOOOO OOOOO ", + "OOOOOOO OOOOOOOO OOO OOO OOOOOOOO OOOOOOO ", + "!OO OO! OOO OO! OOO OO! !OO ", + "!O! !O! O!O !O! O!O !O! !O! ", + "!!OO!! O!O!O!O! O!O !O! O!!!:! !!OO!! ", + " !!O!!! !!!O!!!! !O! !!! !!!!!: !!O!!! ", " !:! !!: !!! :!: !!: !!: !:!", " !:! :!: !:! ::!!:: :!: !:! ", "::!:::: :: :: !::: ::!::!! ::!:::: ", " :::.. ! : !: :!:::::! :::.. " }, ['ui_title_controls'] = { - " @@@@@ @@@@ @@ @@ @@@@@@@ @@@@@@@ @@@@ @@@ @@@@@ ", - "@@@@@@@@ @@@@@@@@ @@@ @@@ @@@@@@@ @@@@@@@@ @@@@@@@@ @@@ @@@@@@@ ", - "@@! @@! @@@ @@!@ @@@ @@! @@! @@@ @@! @@@ @@! !@@ ", - "!@! !@! @!@ !@!!@!@! !@! !@! @!@ !@! @!@ !@! !@! ", - "@!@ @!@ !@! @!@ !!@! @!! @!@!!@! @!@ !@! @!@ !!@@!! ", - "!@! !@! !!! !@! !!! !!! !!@!@! !@! !!! !@! !!@!!! ", + " OOOOO OOOO OO OO OOOOOOO OOOOOOO OOOO OOO OOOOO ", + "OOOOOOOO OOOOOOOO OOO OOO OOOOOOO OOOOOOOO OOOOOOOO OOO OOOOOOO ", + "OO! OO! OOO OO!O OOO OO! OO! OOO OO! OOO OO! !OO ", + "!O! !O! O!O !O!!O!O! !O! !O! O!O !O! O!O !O! !O! ", + "O!O O!O !O! O!O !!O! O!! O!O!!O! O!O !O! O!O !!OO!! ", + "!O! !O! !!! !O! !!! !!! !!O!O! !O! !!! !O! !!O!!! ", "!!: !!: !!! !!: !!! !!: !!: :!! !!: !!! !!: !:!", ":!: :!: !:! :!: !:! :!: :!: !:! :!: !:! :!: !:! ", ":!:::!! :!:::!!: :: :: :: :: !: :!:::!!: :!:::!! ::!:::: ", - " ::!::!: :!:: : : : ! : :!:: !::!::!: :::.. " + " ::!::!: :!:: : : : ! : :!:: !::!::!: :::.. " } } diff --git a/src/ui/elements/UIMenuTitle.lua b/src/ui/elements/UIMenuTitle.lua index 1a34c4c0..11db5601 100644 --- a/src/ui/elements/UIMenuTitle.lua +++ b/src/ui/elements/UIMenuTitle.lua @@ -33,9 +33,9 @@ local function createTitle( titleDefinition ) for i, line in ipairs( titleDefinition ) do local coloredtext = {} for w in string.gmatch( line, '.' ) do - if w == '@' then + if w == 'O' then coloredtext[#coloredtext + 1] = TexturePacks.getColor( 'ui_title_1' ) - coloredtext[#coloredtext + 1] = 'O' + coloredtext[#coloredtext + 1] = w elseif w == '!' then coloredtext[#coloredtext + 1] = TexturePacks.getColor( 'ui_title_2' ) coloredtext[#coloredtext + 1] = w From e405262096b8a12d513276e7a69378653ee5db80 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Wed, 22 Aug 2018 21:10:27 +0200 Subject: [PATCH 81/93] Fix huge performance issue with menu titles The table was actually added to the Text object each time a new character was added which lead to a huge amount of characters ending up in the mesh. This was dragging down the FPS on older systems a lot (e.g. 500 FPS to 160 FPS on a MacBook Pro 2010). --- src/ui/elements/UIMenuTitle.lua | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/ui/elements/UIMenuTitle.lua b/src/ui/elements/UIMenuTitle.lua index 11db5601..8d5a3a9f 100644 --- a/src/ui/elements/UIMenuTitle.lua +++ b/src/ui/elements/UIMenuTitle.lua @@ -30,8 +30,9 @@ local UIMenuTitle = Class( 'UIMenuTitle' ) local function createTitle( titleDefinition ) local font = TexturePacks.getFont():get() local title = love.graphics.newText( font ) - for i, line in ipairs( titleDefinition ) do - local coloredtext = {} + local coloredtext = {} + + for _, line in ipairs( titleDefinition ) do for w in string.gmatch( line, '.' ) do if w == 'O' then coloredtext[#coloredtext + 1] = TexturePacks.getColor( 'ui_title_1' ) @@ -43,9 +44,12 @@ local function createTitle( titleDefinition ) coloredtext[#coloredtext + 1] = TexturePacks.getColor( 'ui_title_3' ) coloredtext[#coloredtext + 1] = w end - title:add( coloredtext, 0, i * font:getHeight() ) end + coloredtext[#coloredtext + 1] = TexturePacks.getColor( 'tile_unseen' ) + coloredtext[#coloredtext + 1] = '\n' end + + title:add( coloredtext, 0, 0 ) return title end From 1984cc90c982b2e8a4bab0256845e89963f86f25 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Wed, 22 Aug 2018 21:39:29 +0200 Subject: [PATCH 82/93] Use a method specifically for moving characters This fixes an issue when loading the map (because the game tried to remove an character that wasn't placed on the map yet). Fixes #259. --- src/characters/Character.lua | 2 +- src/map/Map.lua | 20 ++++++++++++++------ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/characters/Character.lua b/src/characters/Character.lua index 4cc45ebd..f001196a 100644 --- a/src/characters/Character.lua +++ b/src/characters/Character.lua @@ -271,7 +271,7 @@ end -- @tparam number y The target position along the y-axis. -- function Character:move( x, y ) - self.map:setCharacterAt( x, y, self ) + self.map:moveCharacter( x, y, self ) end --- diff --git a/src/map/Map.lua b/src/map/Map.lua index 63ec28dd..4db37d9a 100644 --- a/src/map/Map.lua +++ b/src/map/Map.lua @@ -143,6 +143,20 @@ function Map:receive( event, ... ) self:publish( event, ... ) end +--- +-- Moves a Character from his current position to a new target position. +-- @tparam number x The target position along the x-axis. +-- @tparam number y The target position along the y-axis. +-- @tparam Character character The character to move. +-- +function Map:moveCharacter( x, y, character ) + -- Remove character from old position. + self:removeCharacter( character:getX(), character:getY(), character ) + + -- Set character to new position. + self:setCharacterAt( x, y, character ) +end + --- -- Removes a Character from a specific position on the character layer. -- @tparam number x The target position along the x-axis. @@ -277,12 +291,6 @@ end -- @tparam Character The character to set to the grid. -- function Map:setCharacterAt( x, y, character ) - -- Remove character from old position if necessary. - local oldX, oldY = character:getPosition() - if oldX and oldY then - self:removeCharacter( oldX, oldY, character ) - end - -- Make sure the grid space is empty. if self.characters[x][y] ~= nil then error( string.format( 'Target position (%s, %s) is already occupied by a character.', x, y )) From 7e0d184b8a9b9fd1bb32845d33d683e37db93e52 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Wed, 22 Aug 2018 23:52:46 +0200 Subject: [PATCH 83/93] Log info about amount of initialized tiles --- src/ui/MapPainter.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/MapPainter.lua b/src/ui/MapPainter.lua index f1b505f7..eeaac542 100644 --- a/src/ui/MapPainter.lua +++ b/src/ui/MapPainter.lua @@ -90,7 +90,7 @@ local function initSpritebatch( map, spritebatch ) tile:setSpriteID( id ) tile:setDirty( true ) end) - Log.debug( string.format( 'Initialised %d tiles.', spritebatch:getCount() ), 'MapPainter' ) + Log.info( string.format( 'Initialised %d tiles.', spritebatch:getCount() ), 'MapPainter' ) end --- From 057a66703ebfae9225b13d07040a5937a6e04f57 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 23 Aug 2018 01:00:58 +0200 Subject: [PATCH 84/93] Fix FOV not updating when doors are openend --- src/map/worldobjects/WorldObject.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/map/worldobjects/WorldObject.lua b/src/map/worldobjects/WorldObject.lua index 427b2c49..8e862909 100644 --- a/src/map/worldobjects/WorldObject.lua +++ b/src/map/worldobjects/WorldObject.lua @@ -245,6 +245,8 @@ end -- function WorldObject:setBlocksVision( blocksVision ) self.blocksVision = blocksVision + + self:publish( 'TILE_UPDATED', self:getTile() ) end --- From a825362835af081aa6f7b7953a9917dc95d00a34 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sun, 26 Aug 2018 00:24:05 +0200 Subject: [PATCH 85/93] Reimplement camera tracking for moving characters This was previously removed in 0.15.0.1521 to get rid of the Messenger dependencies. Closes #260. --- src/characters/Character.lua | 1 + src/ui/screens/CombatScreen.lua | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/src/characters/Character.lua b/src/characters/Character.lua index f001196a..e9b87cf5 100644 --- a/src/characters/Character.lua +++ b/src/characters/Character.lua @@ -272,6 +272,7 @@ end -- function Character:move( x, y ) self.map:moveCharacter( x, y, self ) + self:publish( 'CHARACTER_MOVED', self:getTile() ) end --- diff --git a/src/ui/screens/CombatScreen.lua b/src/ui/screens/CombatScreen.lua index 750f5e0b..d229cd6b 100644 --- a/src/ui/screens/CombatScreen.lua +++ b/src/ui/screens/CombatScreen.lua @@ -11,6 +11,7 @@ local UserInterface = require( 'src.ui.elements.UserInterface' ) local OverlayPainter = require( 'src.ui.overlays.OverlayPainter' ) local TexturePacks = require( 'src.ui.texturepacks.TexturePacks' ) local Settings = require( 'src.Settings' ) +local SoundManager = require( 'src.SoundManager' ) -- ------------------------------------------------ -- Module @@ -18,6 +19,12 @@ local Settings = require( 'src.Settings' ) local CombatScreen = Screen:subclass( 'CombatScreen' ) +-- ------------------------------------------------ +-- Constants +-- ------------------------------------------------ + +local FACTIONS = require( 'src.constants.FACTIONS' ) + -- ------------------------------------------------ -- Public Methods -- ------------------------------------------------ @@ -35,8 +42,23 @@ function CombatScreen:initialize( playerFaction, savegame ) self.userInterface = UserInterface( self.combatState, self.camera ) self.overlayPainter = OverlayPainter( self.combatState, self.camera ) + + self.combatState:getMap():observe( self ) end +function CombatScreen:receive( event, ... ) + if event == 'CHARACTER_MOVED' then + local tile = ... + if not tile:isSeenBy( FACTIONS.ALLIED ) then + return + end + local tw, th = TexturePacks.getTileDimensions() + self.camera:setTargetPosition( tile:getX() * tw, tile:getY() * th ) + return + end +end + + function CombatScreen:draw() self.camera:attach() self.mapPainter:draw() From 2269ef05794f83fa6ad49b9438a340a59a28eef5 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sun, 26 Aug 2018 00:26:10 +0200 Subject: [PATCH 86/93] Reimplement camera movement to selected character This was previously removed in 0.15.0.1521 to get rid of the Messenger dependencies. Closes #261. --- src/characters/Character.lua | 2 ++ src/ui/screens/CombatScreen.lua | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/src/characters/Character.lua b/src/characters/Character.lua index e9b87cf5..e3943fdb 100644 --- a/src/characters/Character.lua +++ b/src/characters/Character.lua @@ -144,6 +144,8 @@ function Character:activate() end self:generateFOV() self:clearActions() + + self:publish( 'CHARACTER_SELECTED', self:getTile() ) end --- diff --git a/src/ui/screens/CombatScreen.lua b/src/ui/screens/CombatScreen.lua index d229cd6b..707ae297 100644 --- a/src/ui/screens/CombatScreen.lua +++ b/src/ui/screens/CombatScreen.lua @@ -56,6 +56,17 @@ function CombatScreen:receive( event, ... ) self.camera:setTargetPosition( tile:getX() * tw, tile:getY() * th ) return end + + if event == 'CHARACTER_SELECTED' then + local tile = ... + if not tile:isSeenBy( FACTIONS.ALLIED ) then + return + end + local tw, th = TexturePacks.getTileDimensions() + self.camera:setTargetPosition( tile:getX() * tw, tile:getY() * th ) + SoundManager.play( 'sound_select' ) + return + end end From edfd80e568d12ad561cd0d7fba4686bd5acd82d0 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sun, 26 Aug 2018 01:33:38 +0200 Subject: [PATCH 87/93] Fixed message queue retaining old messages Fixes #263. --- src/CombatState.lua | 3 +++ src/util/MessageQueue.lua | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/src/CombatState.lua b/src/CombatState.lua index c0a3891c..4aa275c6 100644 --- a/src/CombatState.lua +++ b/src/CombatState.lua @@ -95,6 +95,9 @@ function CombatState:initialize( playerFaction, savegame ) self.map:observe( self ) self.map:observe( self.factions ) + -- Clear the message queue. + MessageQueue.clear() + -- Free memory if possible. collectgarbage( 'collect' ) end diff --git a/src/util/MessageQueue.lua b/src/util/MessageQueue.lua index f3f57f2d..9bfa0e24 100644 --- a/src/util/MessageQueue.lua +++ b/src/util/MessageQueue.lua @@ -34,6 +34,13 @@ function MessageQueue.dequeue() return table.remove( messages, 1 ) end +--- +-- Clears the message queue of any old messages. +-- +function MessageQueue.clear() + messages = {} +end + --- -- Checks wether the message queue is empty. -- @treturn boolean True if the queue is empty. From b258c50b232e9b241a9968d8b51f5bfe0f5502f0 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sun, 26 Aug 2018 01:39:19 +0200 Subject: [PATCH 88/93] Increase height of paginated list --- src/ui/screens/KeybindingScreen.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/screens/KeybindingScreen.lua b/src/ui/screens/KeybindingScreen.lua index 0e8ddea0..e1dae0e3 100644 --- a/src/ui/screens/KeybindingScreen.lua +++ b/src/ui/screens/KeybindingScreen.lua @@ -31,7 +31,7 @@ local KeybindingScreen = Screen:subclass( 'KeybindingScreen' ) local TITLE_POSITION = 2 local BUTTON_LIST_WIDTH = 25 -local BUTTON_LIST_HEIGHT = 10 +local BUTTON_LIST_HEIGHT = 15 local BUTTON_LIST_Y = 20 -- ------------------------------------------------ From 5c7741e4159b01bb2aeb04d2c8fdbc70cd613c6c Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sun, 26 Aug 2018 01:59:44 +0200 Subject: [PATCH 89/93] Fix offset of UIPaginatedList items upon resize Fixes #221. --- src/ui/elements/lists/UIPaginatedList.lua | 23 ++++++++++++++++++++--- src/ui/screens/KeybindingScreen.lua | 7 +++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/ui/elements/lists/UIPaginatedList.lua b/src/ui/elements/lists/UIPaginatedList.lua index 1e1c5e60..bbb0e081 100644 --- a/src/ui/elements/lists/UIPaginatedList.lua +++ b/src/ui/elements/lists/UIPaginatedList.lua @@ -59,7 +59,8 @@ local function fillPages( items, maximumPages, height, ax, ay ) for i = 1, #items do currentPage[#currentPage + 1] = items[i] - items[i]:setRelativePosition( ax, ay + #currentPage-1 ) + items[i]:setOrigin( ax, ay ) + items[i]:setRelativePosition( 0, #currentPage-1 ) -- Add the current page to the "pages" table if it is full or if we have -- added all the items to add. @@ -179,9 +180,9 @@ end -- Creates the buttons to scroll through the pages. -- function UIPaginatedList:addButtons() - self.buttonPrev = UIButton( 0, 0, self.ax + self.w-2, self.ay + self.h-1, 1, 1, function() self:scrollPage( -1 ) end ) + self.buttonPrev = UIButton( self.ax, self.ay, self.w-2, self.h-1, 1, 1, function() self:scrollPage( -1 ) end ) self.buttonPrev:setIcon( 'ui_prev_element' ) - self.buttonNext = UIButton( 0, 0, self.ax + self.w-1, self.ay + self.h-1, 1, 1, function() self:scrollPage( 1 ) end ) + self.buttonNext = UIButton( self.ax, self.ay, self.w-1, self.h-1, 1, 1, function() self:scrollPage( 1 ) end ) self.buttonNext:setIcon( 'ui_next_element' ) self:addChild( self.buttonPrev ) @@ -277,4 +278,20 @@ function UIPaginatedList:setFocus( focus ) self.pages[self.currentPage][self.cursor]:setFocus( focus ) end +--- +-- Forwards changes to the origin of the paginated list to the children and +-- all items on each page. +-- @tparam number ox The new origin along the x-axis. +-- @tparam number oy The new origin along the y-axis. +-- +function UIPaginatedList:setOrigin( ox, oy ) + UIPaginatedList.super.setOrigin( self, ox, oy ) + + for _, page in ipairs( self.pages ) do + for _, item in ipairs( page ) do + item:setOrigin( ox, oy ) + end + end +end + return UIPaginatedList diff --git a/src/ui/screens/KeybindingScreen.lua b/src/ui/screens/KeybindingScreen.lua index e1dae0e3..e947a201 100644 --- a/src/ui/screens/KeybindingScreen.lua +++ b/src/ui/screens/KeybindingScreen.lua @@ -262,4 +262,11 @@ function KeybindingScreen:receive( event, ... ) end end +function KeybindingScreen:resize( _, _ ) + local lx = GridHelper.centerElement( BUTTON_LIST_WIDTH, 1 ) + + self.paginatedList:setOrigin( lx, BUTTON_LIST_Y ) + self.buttonList:setOrigin( lx, BUTTON_LIST_Y + BUTTON_LIST_HEIGHT + 1 ) +end + return KeybindingScreen From 77e06e8565f980c4872b6a6011280102bb3be6e5 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 27 Aug 2018 21:14:56 +0200 Subject: [PATCH 90/93] Remove action to open health panel for enemies Since the addition of the new UI this has become superfluous. --- src/turnbased/helpers/InteractionInput.lua | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/turnbased/helpers/InteractionInput.lua b/src/turnbased/helpers/InteractionInput.lua index 9cba7782..b31ff237 100644 --- a/src/turnbased/helpers/InteractionInput.lua +++ b/src/turnbased/helpers/InteractionInput.lua @@ -47,12 +47,6 @@ end -- @treturn boolean True if an action was created, false otherwise. -- function InteractionInput:request( target, character ) - -- Check health of enemy characters. - if target:hasCharacter() and target:getCharacter():getFaction():getType() ~= character:getFaction():getType() then - ScreenManager.push( 'playerInfo', target:getCharacter() ) - return true - end - -- Characters can only interact with adjacent tiles. if not target:isAdjacent( character:getTile() ) then return false From ad8b0147db82714e2f7da54511d7375a342c1311 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 27 Aug 2018 21:24:00 +0200 Subject: [PATCH 91/93] Fix build --- src/turnbased/helpers/InteractionInput.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/src/turnbased/helpers/InteractionInput.lua b/src/turnbased/helpers/InteractionInput.lua index b31ff237..e90afec4 100644 --- a/src/turnbased/helpers/InteractionInput.lua +++ b/src/turnbased/helpers/InteractionInput.lua @@ -10,7 +10,6 @@ local Class = require( 'lib.Middleclass' ) local Open = require( 'src.characters.actions.Open' ) local Close = require( 'src.characters.actions.Close' ) local OpenInventory = require( 'src.characters.actions.OpenInventory' ) -local ScreenManager = require( 'lib.screenmanager.ScreenManager' ) -- ------------------------------------------------ -- Module From 9526e0111bdf207a52ed666752b19bcab8ef22f2 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Tue, 28 Aug 2018 19:32:56 +0200 Subject: [PATCH 92/93] Use class-based action point values Closes #266. --- res/data/creatures/classes.lua | 3 +++ src/characters/Character.lua | 12 ++++++------ src/characters/CharacterFactory.lua | 2 +- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/res/data/creatures/classes.lua b/res/data/creatures/classes.lua index fa9b7c51..1408e25e 100644 --- a/res/data/creatures/classes.lua +++ b/res/data/creatures/classes.lua @@ -5,6 +5,7 @@ return { 'body_human' }, stats = { + ap = 40, hp = 8 } }, @@ -14,6 +15,7 @@ return { 'body_human' }, stats = { + ap = 30, hp = 5 } }, @@ -23,6 +25,7 @@ return { 'body_dog' }, stats = { + ap = 20, hp = 4 } } diff --git a/src/characters/Character.lua b/src/characters/Character.lua index e3943fdb..d0803752 100644 --- a/src/characters/Character.lua +++ b/src/characters/Character.lua @@ -23,8 +23,6 @@ local Character = MapObject:subclass( 'Character' ) -- Constants -- ------------------------------------------------ -local DEFAULT_ACTION_POINTS = 40 - local STANCES = require( 'src.constants.STANCES' ) local ITEM_TYPES = require( 'src.constants.ITEM_TYPES' ) @@ -116,12 +114,14 @@ end -- Public Methods -- ------------------------------------------------ -function Character:initialize( classID ) +function Character:initialize( classID, actionPoints ) MapObject.initialize( self ) self.creatureClass = classID - self.actionPoints = DEFAULT_ACTION_POINTS + self.maxActionPoints = actionPoints + self.actionPoints = actionPoints + self.actions = Queue() self.fov = {} @@ -240,7 +240,7 @@ end -- Resets the character's action points to the default value. -- function Character:resetActionPoints() - self.actionPoints = DEFAULT_ACTION_POINTS + self.actionPoints = self.maxActionPoints end --- @@ -451,7 +451,7 @@ end -- @treturn number The total amount of action points. -- function Character:getMaxActionPoints() - return DEFAULT_ACTION_POINTS + return self.maxActionPoints end --- diff --git a/src/characters/CharacterFactory.lua b/src/characters/CharacterFactory.lua index e6fb70dc..70b0c686 100644 --- a/src/characters/CharacterFactory.lua +++ b/src/characters/CharacterFactory.lua @@ -197,7 +197,7 @@ end function CharacterFactory.newCharacter( factionType ) local classID = pickCreatureClass( factionType ) local class = findClass( classID ) - local character = Character( classID ) + local character = Character( classID, class.stats.ap ) local bodyType = Util.pickRandomValue( class.body ) if bodyType == 'body_human' then From b6c299f8bfc1424e2c0feb2d89a8fc1363c59cbe Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 30 Aug 2018 20:10:12 +0200 Subject: [PATCH 93/93] Prepare version 0.16.0.1615 --- CHANGELOG.md | 47 +++++++++++++++++++++++++++++++++++++++++++++++ README.md | 2 +- version.lua | 4 ++-- 3 files changed, 50 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c31df6c5..f90b1524 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,50 @@ +# Version 0.16.0.1615 - 2018-08-30 (LÖVE 11.1) + +## Additions +- Added paginated list UIElement. + - Replaced old keybinding list with new paginated lists. + - Replaced selectors in prefab editor with new paginated lists. +- Added keybinding layouts to allow different bindings for the same key based on the game's context. +- Added title to the keybinding screen. +- Added selector for canvas sizes on prefab editor screen. +- Added proper re-bindable controls for the prefab editor. +- Added automatic camera tracking of moving characters (previously removed in 0.15.0.1521). +- Added automatic camera movement when selecting a different character (previously removed in 0.15.0.1521). +- Added class-based action point values. + +## Removals +- Removed action to open health panel for enemy and neutral characters. + +## Fixes +- Fixed huge performance issue with menu titles (especially noticeable on older systems). +- Fixed crash after winning the game because the game tried to switch to the removed base screen. +- Fixed tile info revealing stats of enemy characters which aren't visible to the player's characters. +- Fixed rebinding of unassigned actions. +- Fixed issue where equipment and inventory list weren't clearing their children properly. +- Fixed right-click operation on inventory screen. +- Fixed scroll bar for item description on inventory screen. +- Fixed FOV not updating when a world object is opened. +- Fixed flood filling tool in prefab editor. +- Fixed camera scrolling on prefab editor screen. +- Fixed faulty camera bounds after loading a prefab on prefab editor screen. +- Fixed message log retaining old messages. + +## Other Changes +- Changed targeted LÖVE version to 11.1 "Mysterious Mysteries". +- Changed data structure of the game's combat map. +- Changed FOV checks to be more efficient. + - Checking wether a faction can see a specific tile is roughly five times faster than before. + - Checking wether a character can see a specific tile is roughly three times as fast now. +- Changed tile info to be more efficient by only updating it when necessary. +- Changed world object hit points to fit the current weapon damage values. +- Changed buttons in prefab editor to use correct icon colors. +- Changed cursor in prefab editor to use icon and color of selected sprite. +- Changed map editor to be visible in the main menu by default. +- Changed map editor to start in prefab instead of layout editor mode. + + + + # Version 0.15.0.1521 - 2018-07-27 (LÖVE 11.0) ## Additions diff --git a/README.md b/README.md index 6a1dd39e..e023223b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # On The Roadside -[![Version](https://img.shields.io/badge/Version-0.15.0.1521-blue.svg)](https://github.com/rm-code/on-the-roadside/releases/latest) +[![Version](https://img.shields.io/badge/Version-0.16.0.1615-blue.svg)](https://github.com/rm-code/on-the-roadside/releases/latest) [![LOVE](https://img.shields.io/badge/L%C3%96VE-11.0-EA316E.svg)](http://love2d.org/) [![Build Status](https://travis-ci.com/rm-code/On-The-Roadside.svg?token=q3rLXeyGTBN9VB2zsWMr&branch=develop)](https://travis-ci.com/rm-code/On-The-Roadside) diff --git a/version.lua b/version.lua index a41a836c..19e59ca8 100644 --- a/version.lua +++ b/version.lua @@ -1,8 +1,8 @@ local version = { major = 0, - minor = 15, + minor = 16, patch = 0, - build = 1521, + build = 1615, } return string.format( "%d.%d.%d.%d", version.major, version.minor, version.patch, version.build );