From 1a1000b34268eb79ebd895d2aa89b325a6cc9e6d Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 27 Nov 2017 14:25:34 +0100 Subject: [PATCH 001/178] Remove tile exploration Showing the full map to the players allows them to figure out a path to approach certain buildings and objectives instead of basically stumbling around in the dark until they find an enemy. Closes #214. --- res/text/en_EN/ui_text.lua | 1 - res/texturepacks/default/colors.lua | 1 - src/characters/Character.lua | 3 --- src/characters/Factions.lua | 4 ---- src/map/Map.lua | 14 -------------- src/map/MapLoader.lua | 7 ------- src/map/tiles/Tile.lua | 27 --------------------------- src/ui/MapPainter.lua | 12 +++--------- src/ui/UserInterface.lua | 6 ++---- 9 files changed, 5 insertions(+), 70 deletions(-) diff --git a/res/text/en_EN/ui_text.lua b/res/text/en_EN/ui_text.lua index 7b7e789c..650bf3c1 100644 --- a/res/text/en_EN/ui_text.lua +++ b/res/text/en_EN/ui_text.lua @@ -3,7 +3,6 @@ locale.identifier = 'en_EN'; locale.strings = { ['ui_tile'] = "Tile: ", - ['ui_tile_unexplored'] = "Unexplored", ['ui_weapon'] = "Weapon: ", ['ui_ammo'] = "Ammo: ", ['ui_mode'] = "Mode: ", diff --git a/res/texturepacks/default/colors.lua b/res/texturepacks/default/colors.lua index e65e6a40..60365ae4 100644 --- a/res/texturepacks/default/colors.lua +++ b/res/texturepacks/default/colors.lua @@ -40,7 +40,6 @@ return { -- ============================== -- TILES -- ============================== - ['tile_unexplored'] = COLORS.DB00, ['tile_unseen'] = COLORS.DB01, ['tile_asphalt'] = COLORS.DB25, diff --git a/src/characters/Character.lua b/src/characters/Character.lua index 7590dd00..c50cb55d 100644 --- a/src/characters/Character.lua +++ b/src/characters/Character.lua @@ -85,9 +85,6 @@ function Character.new() -- Add tile to this character's FOV. self:addSeenTile( cx, cy, target ); - -- Mark tile as explored for this character's faction. - target:setExplored( faction:getType(), true ); - -- Mark tile for drawing update. target:setDirty( true ); end diff --git a/src/characters/Factions.lua b/src/characters/Factions.lua index f1f37607..3819ac59 100644 --- a/src/characters/Factions.lua +++ b/src/characters/Factions.lua @@ -78,16 +78,12 @@ function Factions.new( map ) function self:nextFaction() active:getObject():deactivate(); - map:updateExplorationInfo( active:getObject():getType() ); - while active do active = active:getNext() or root; local faction = active:getObject(); faction:activate(); if faction:hasLivingCharacters() then - map:updateExplorationInfo( faction:getType() ); - local current = faction:getCurrentCharacter(); if current:isDead() then return self:getFaction():nextCharacter(); diff --git a/src/map/Map.lua b/src/map/Map.lua index c55cf0ff..f9235f90 100644 --- a/src/map/Map.lua +++ b/src/map/Map.lua @@ -142,20 +142,6 @@ function Map.new() end end - --- - -- Marks all tiles which have been explored by this faction as dirty. - -- @param faction (string) The faction identifier. - -- - function self:updateExplorationInfo( faction ) - for x = 1, #tiles do - for y = 1, #tiles[x] do - if tiles[x][y]:isExplored( faction ) then - tiles[x][y]:setDirty( true ) - end - end - end - end - function self:serialize() local t = { width = width, diff --git a/src/map/MapLoader.lua b/src/map/MapLoader.lua index d908e3bf..bf741334 100644 --- a/src/map/MapLoader.lua +++ b/src/map/MapLoader.lua @@ -62,13 +62,6 @@ function MapLoader.new() recreatedTile:getInventory():loadItems( tile.inventory ) end - -- Set the exploration info of each faction for this tile. - if tile.explored then - for i, v in pairs( tile.explored ) do - recreatedTile:setExplored( i, v ) - end - end - -- Store tile in the table. loadedTiles[tile.x] = loadedTiles[tile.x] or {} loadedTiles[tile.x][tile.y] = recreatedTile diff --git a/src/map/tiles/Tile.lua b/src/map/tiles/Tile.lua index c4c1d2f6..d535da79 100644 --- a/src/map/tiles/Tile.lua +++ b/src/map/tiles/Tile.lua @@ -40,8 +40,6 @@ function Tile.new( x, y, template ) local worldObject; local inventory = Inventory.new( WEIGHT_LIMIT, VOLUME_LIMIT ); - local explored; - -- ------------------------------------------------ -- Public Methods -- ------------------------------------------------ @@ -109,13 +107,6 @@ function Tile.new( x, y, template ) t['inventory'] = inventory:serialize(); end - if explored then - t['explored'] = {}; - for faction, bool in pairs( explored ) do - t['explored'][faction] = bool; - end - end - return t; end @@ -248,15 +239,6 @@ function Tile.new( x, y, template ) return dirty; end - --- - -- Checks if the tile has been explored by a faction. - -- @param faction (string) The faction to check for. - -- @return (boolean) True if the tile has been explored. - -- - function self:isExplored( faction ) - return explored and explored[faction] or false; - end - --- -- Checks if the tile has a character on it. -- @return (boolean) True a character is standing on the tile. @@ -297,15 +279,6 @@ function Tile.new( x, y, template ) dirty = ndirty; end - --- - -- Marks the tile as explored for a specific faction. - -- @param faction (string) The faction to mark the tile for. - -- - function self:setExplored( faction, nexplored ) - explored = explored or {}; - explored[faction] = nexplored; - end - --- -- Sets the tile's unique spriteID. -- @param nid (number) The tile's new spriteID. diff --git a/src/ui/MapPainter.lua b/src/ui/MapPainter.lua index 68f20a87..36c2e2a5 100644 --- a/src/ui/MapPainter.lua +++ b/src/ui/MapPainter.lua @@ -71,14 +71,9 @@ end -- @treturn table A table containing RGBA values. -- local function selectTileColor( tile, faction ) - -- If there is a faction we check which tiles are explored and which tiles - -- are currently seen. + -- If there is a faction we check which tiles are currently seen and highlight + -- the active character. if faction then - -- Hide unexplored tiles. - if not tile:isExplored( faction:getType() ) then - return TexturePacks.getColor( 'tile_unexplored' ) - end - -- Dim tiles hidden from the player. if not faction:canSee( tile ) then return TexturePacks.getColor( 'tile_unseen' ) @@ -202,8 +197,7 @@ function MapPainter:update() end --- --- Sets the faction which is used for checking which parts of the map are visible --- and explored. +-- Sets the faction which is used for checking which parts of the map are visible. -- @tparam Faction faction The faction to use. -- function MapPainter:setActiveFaction( faction ) diff --git a/src/ui/UserInterface.lua b/src/ui/UserInterface.lua index 65102ba4..40cd8944 100644 --- a/src/ui/UserInterface.lua +++ b/src/ui/UserInterface.lua @@ -48,11 +48,9 @@ function UserInterface.new( game, camera ) love.graphics.print( Translator.getText( 'ui_tile' ), x, y ); local sw = font:measureWidth( Translator.getText( 'ui_tile' )) - if not tile:isExplored( factions:getFaction():getType() ) then - love.graphics.print( Translator.getText( 'ui_tile_unexplored' ), x + sw, y ); - elseif tile:hasWorldObject() then + if tile:hasWorldObject() then love.graphics.print( Translator.getText( tile:getWorldObject():getID() ), x + sw, y ); - elseif tile:isExplored( factions:getFaction():getType() ) then + else love.graphics.print( Translator.getText( tile:getID() ), x + sw, y ); end end From 9f168dcaf3c20cffb80e51fb0d3b440b9a480a8b Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Wed, 29 Nov 2017 20:18:11 +0100 Subject: [PATCH 002/178] Remove unused argument Fixes travis build. --- src/characters/Factions.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/characters/Factions.lua b/src/characters/Factions.lua index 3819ac59..abee49ed 100644 --- a/src/characters/Factions.lua +++ b/src/characters/Factions.lua @@ -19,7 +19,7 @@ local FACTIONS = require( 'src.constants.FACTIONS' ); -- Constructor -- ------------------------------------------------ -function Factions.new( map ) +function Factions.new() local self = Object.new():addInstance( 'Factions' ); -- ------------------------------------------------ From c0025613cd810ec582c65f1487148b8444b2606b Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 30 Nov 2017 00:19:19 +0100 Subject: [PATCH 003/178] Refactor Walk Action --- src/characters/actions/Action.lua | 63 +++++++++++++++++++++++------ src/characters/actions/Walk.lua | 53 ++++++++++++++---------- src/characters/pathfinding/Path.lua | 6 +-- 3 files changed, 86 insertions(+), 36 deletions(-) diff --git a/src/characters/actions/Action.lua b/src/characters/actions/Action.lua index f1670042..67f175b3 100644 --- a/src/characters/actions/Action.lua +++ b/src/characters/actions/Action.lua @@ -1,19 +1,58 @@ -local Object = require('src.Object'); +--- +-- The parent class for all Actions +-- @module Action +-- -local Action = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ -function Action.new( cost, target ) - local self = Object.new():addInstance( 'Action' ); +local Class = require( 'lib.Middleclass' ) - function self:getCost() - return cost; - end +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ - function self:getTarget() - return target; - end +local Action = Class( 'Action' ) - return self; +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ + +--- +-- Initializes a new instance of the Action class. +-- @tparam Character character The character performing the action. +-- @tparam number cost The number of AP it takes to perform this action. +-- @tparam Tile target The target tile to perform this action on. +-- +function Action:initialize( character, target, cost ) + self.character = character + self.cost = cost + self.target = target +end + +--- +-- Returns the character performing the Action. +-- @treturn number The action's AP cost. +-- +function Action:getCharacter() + return self.character +end + +--- +-- Returns the cost it takes to perform this Action. +-- @treturn number The action's AP cost. +-- +function Action:getCost() + return self.cost +end + +--- +-- Returns the target tile on which to perform the Action. +-- @treturn Tile The target tile for this Action. +-- +function Action:getTarget() + return self.target end -return Action; +return Action diff --git a/src/characters/actions/Walk.lua b/src/characters/actions/Walk.lua index 45bb4805..f4ab45f1 100644 --- a/src/characters/actions/Walk.lua +++ b/src/characters/actions/Walk.lua @@ -1,33 +1,44 @@ -local Action = require('src.characters.actions.Action'); -local Messenger = require( 'src.Messenger' ); +--- +-- The Walk Action takes care of moving a Character from one tile to another. +-- @module Walk +-- -local Walk = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ + +local Action = require( 'src.characters.actions.Action' ) +local Messenger = require( 'src.Messenger' ) -- ------------------------------------------------ --- Constructor +-- Module -- ------------------------------------------------ -function Walk.new( character, target ) - local self = Action.new( target:getMovementCost( character:getStance() ), target ):addInstance( 'Walk' ); +local Walk = Action:subclass( 'Walk' ) - function self:perform() - local current = character:getTile(); +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ + +function Walk:initialize( character, target ) + Action.initialize( self, character, target, target:getMovementCost( character:getStance() )) +end - assert( target:isPassable(), 'Target tile has to be passable!' ); - assert( not target:isOccupied(), 'Target tile must not be occupied by another character!' ); - assert( target:isAdjacent( current ), 'Character has to be adjacent to the target tile!' ); +function Walk:perform() + local current = self.character:getTile() - Messenger.publish( 'CHARACTER_MOVED', character ); + 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( 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(); - target:setCharacter( character ); - character:setTile( target ); - return true; - end + Messenger.publish( 'CHARACTER_MOVED', self.character ) - return self; + -- 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:setTile( self.target ) + return true end -return Walk; +return Walk diff --git a/src/characters/pathfinding/Path.lua b/src/characters/pathfinding/Path.lua index edf3b374..1b1fc603 100644 --- a/src/characters/pathfinding/Path.lua +++ b/src/characters/pathfinding/Path.lua @@ -38,11 +38,11 @@ function Path.new() local success = character:enqueueAction( Open.new( character, tile )); -- Don't create a walk action if the tile is the last one in the path. if index ~= 1 then - success = character:enqueueAction( Walk.new( character, tile )); + success = character:enqueueAction( Walk( character, tile )) end return success; end - return character:enqueueAction( Walk.new( character, tile )); + return character:enqueueAction( Walk( character, tile )) end -- ------------------------------------------------ @@ -97,7 +97,7 @@ function Path.new() character:enqueueAction( OpenInventory.new( character, tile )); end else - success = character:enqueueAction( Walk.new( character, tile )); + success = character:enqueueAction( Walk( character, tile )) end -- Stop adding actions if the previous one wasn't added correctly. From a4bdb0cfaa9e62f1401cbd2168fed9b8cc228c0a Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 30 Nov 2017 00:25:57 +0100 Subject: [PATCH 004/178] Rename Attack to RangedAttack Also updated the code to match the new class system. --- src/characters/actions/Attack.lua | 38 ------------- src/characters/actions/RangedAttack.lua | 53 +++++++++++++++++++ .../ai/behaviortree/leafs/BTAttackTarget.lua | 4 +- src/turnbased/helpers/AttackInput.lua | 4 +- 4 files changed, 57 insertions(+), 42 deletions(-) delete mode 100644 src/characters/actions/Attack.lua create mode 100644 src/characters/actions/RangedAttack.lua diff --git a/src/characters/actions/Attack.lua b/src/characters/actions/Attack.lua deleted file mode 100644 index 4c6d3529..00000000 --- a/src/characters/actions/Attack.lua +++ /dev/null @@ -1,38 +0,0 @@ -local Action = require('src.characters.actions.Action'); -local ProjectileManager = require( 'src.items.weapons.ProjectileManager' ); -local ProjectileQueue = require( 'src.items.weapons.ProjectileQueue' ); -local Bresenham = require( 'lib.Bresenham' ); - -local Attack = {}; - -function Attack.new( character, target ) - local self = Action.new( character:getWeapon():getAttackCost(), target ):addInstance( 'Attack' ); - - function self:perform() - if character:getWeapon():getMagazine():isEmpty() then - return false; - end - - -- Pick the actual target based on the weapon's range attribute. - local ox, oy = character:getTile():getPosition(); - local tx, ty = target:getPosition(); - local th = target:getHeight() - - local ax, ay - Bresenham.line( ox, oy, tx, ty, function( cx, cy, count ) - if count > character:getWeapon():getRange() then - return false; - end - ax, ay = cx, cy - return true; - end); - - local package = ProjectileQueue.new( character, ax, ay, th ) - ProjectileManager.register( package ); - return true; - end - - return self; -end - -return Attack; diff --git a/src/characters/actions/RangedAttack.lua b/src/characters/actions/RangedAttack.lua new file mode 100644 index 00000000..3cdede06 --- /dev/null +++ b/src/characters/actions/RangedAttack.lua @@ -0,0 +1,53 @@ +--- +-- @module RangedAttack +-- + +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ + +local Action = require( 'src.characters.actions.Action' ) +local ProjectileManager = require( 'src.items.weapons.ProjectileManager' ) +local ProjectileQueue = require( 'src.items.weapons.ProjectileQueue' ) +local Bresenham = require( 'lib.Bresenham' ) + +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ + +local RangedAttack = Action:subclass( 'RangedAttack' ) + +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ + +function RangedAttack:initialize( character, target ) + Action.initialize( self, character, target, character:getWeapon():getAttackCost() ) +end + +function RangedAttack:perform() + -- Stop if the character's weapon is empty. + if self.character:getWeapon():getMagazine():isEmpty() then + return false + end + + -- Pick the actual target based on the weapon's range attribute. + local ox, oy = self.character:getTile():getPosition() + local tx, ty = self.target:getPosition() + local th = self.target:getHeight() + + local ax, ay + Bresenham.line( ox, oy, tx, ty, function( cx, cy, count ) + if count > self.character:getWeapon():getRange() then + return false + end + ax, ay = cx, cy + return true + end) + + local package = ProjectileQueue.new( self.character, ax, ay, th ) + ProjectileManager.register( package ) + return true +end + +return RangedAttack diff --git a/src/characters/ai/behaviortree/leafs/BTAttackTarget.lua b/src/characters/ai/behaviortree/leafs/BTAttackTarget.lua index dbdb55ba..1dd2e76d 100644 --- a/src/characters/ai/behaviortree/leafs/BTAttackTarget.lua +++ b/src/characters/ai/behaviortree/leafs/BTAttackTarget.lua @@ -1,6 +1,6 @@ local Log = require( 'src.util.Log' ); local BTLeaf = require( 'src.characters.ai.behaviortree.leafs.BTLeaf' ); -local Attack = require( 'src.characters.actions.Attack' ); +local RangedAttack = require( 'src.characters.actions.RangedAttack' ) local BTAttackTarget = {}; @@ -10,7 +10,7 @@ function BTAttackTarget.new() function self:traverse( ... ) local blackboard, character = ...; - local success = character:enqueueAction( Attack.new( character, blackboard.target )); + local success = character:enqueueAction( RangedAttack( character, blackboard.target )) if success then Log.debug( 'Character attacks target', 'BTAttackTarget' ); return true; diff --git a/src/turnbased/helpers/AttackInput.lua b/src/turnbased/helpers/AttackInput.lua index 7cde8a18..dcf14cf5 100644 --- a/src/turnbased/helpers/AttackInput.lua +++ b/src/turnbased/helpers/AttackInput.lua @@ -1,5 +1,5 @@ local State = require( 'src.turnbased.states.State' ); -local Attack = require( 'src.characters.actions.Attack' ); +local RangedAttack = require( 'src.characters.actions.RangedAttack' ); local MeleeAttack = require( 'src.characters.actions.MeleeAttack' ); local ThrowingAttack = require( 'src.characters.actions.ThrowingAttack' ); local Rearm = require( 'src.characters.actions.Rearm' ); @@ -55,7 +55,7 @@ function AttackInput.new() -- Handle Ranged weapons. if weapon:getSubType() == WEAPON_TYPES.RANGED then - character:enqueueAction( Attack.new( character, target )); + character:enqueueAction( RangedAttack( character, target )) end return true; From 9f8dd19c00ca38cfb5ba828d17b281a413a8c8de Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 30 Nov 2017 00:30:53 +0100 Subject: [PATCH 005/178] Refactor ClimbOver Action --- src/characters/actions/ClimbOver.lua | 49 +++++++++++++++++++--------- src/characters/pathfinding/Path.lua | 4 +-- 2 files changed, 35 insertions(+), 18 deletions(-) diff --git a/src/characters/actions/ClimbOver.lua b/src/characters/actions/ClimbOver.lua index bc8a5191..065b04a7 100644 --- a/src/characters/actions/ClimbOver.lua +++ b/src/characters/actions/ClimbOver.lua @@ -1,25 +1,42 @@ -local Action = require( 'src.characters.actions.Action' ); -local Messenger = require( 'src.Messenger' ); +--- +-- This Action is used when a character tries to climb over a wall or any other +-- climbable object. It removes the Character from the current Tile and places +-- it on the target Tile on top of the WorldObject. +-- @module ClimbOver +-- -local ClimbOver = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ -function ClimbOver.new( character, target ) - local self = Action.new( target:getWorldObject():getInteractionCost( character:getStance() ), target ):addInstance( 'ClimbOver' ); +local Action = require( 'src.characters.actions.Action' ) +local Messenger = require( 'src.Messenger' ) - function self:perform() - local current = character:getTile(); +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ - assert( target:isAdjacent( current ), 'Character has to be adjacent to the target tile!' ); +local ClimbOver = Action:subclass( 'ClimbOver' ) - current:removeCharacter(); - target:setCharacter( character ); - character:setTile( target ); +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ - Messenger.publish( 'SOUND_CLIMB' ); - return true; - end +function ClimbOver:initialize( character, target ) + Action.initialize( self, character, target, target:getWorldObject():getInteractionCost( character:getStance() )) +end + +function ClimbOver:perform() + local current = self.character:getTile() + + assert( self.target:isAdjacent( current ), 'Character has to be adjacent to the target tile!' ) + + current:removeCharacter() + self.target:setCharacter( self.character ) + self.character:setTile( self.target ) - return self; + Messenger.publish( 'SOUND_CLIMB' ) + return true end -return ClimbOver; +return ClimbOver diff --git a/src/characters/pathfinding/Path.lua b/src/characters/pathfinding/Path.lua index 1b1fc603..5fd8415f 100644 --- a/src/characters/pathfinding/Path.lua +++ b/src/characters/pathfinding/Path.lua @@ -2,7 +2,7 @@ local Object = require('src.Object'); local Walk = require( 'src.characters.actions.Walk' ); local Open = require( 'src.characters.actions.Open' ); local OpenInventory = require( 'src.characters.actions.OpenInventory' ); -local ClimbOver = require( 'src.characters.actions.ClimbOver' ); +local ClimbOver = require( 'src.characters.actions.ClimbOver' ) -- ------------------------------------------------ -- Module @@ -90,7 +90,7 @@ function Path.new() end if worldObject:isClimbable() then - success = character:enqueueAction( ClimbOver.new( character, tile )); + success = character:enqueueAction( ClimbOver( character, tile )) end if worldObject:isContainer() then From d4fe30a9214eba2eaec3b23a222fa4d6fb96dfc5 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 30 Nov 2017 00:38:47 +0100 Subject: [PATCH 006/178] Refactor Close Action --- src/characters/actions/Close.lua | 53 ++++++++++++++-------- src/turnbased/helpers/InteractionInput.lua | 2 +- 2 files changed, 36 insertions(+), 19 deletions(-) diff --git a/src/characters/actions/Close.lua b/src/characters/actions/Close.lua index 158bb5d5..d3605480 100644 --- a/src/characters/actions/Close.lua +++ b/src/characters/actions/Close.lua @@ -1,27 +1,44 @@ -local Action = require('src.characters.actions.Action'); -local Messenger = require( 'src.Messenger' ); +--- +-- This Action is used when a character closes an openable world object. +-- @module Close +-- -local Close = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ -function Close.new( character, tile ) - local self = Action.new( tile:getWorldObject():getInteractionCost( character:getStance() ), tile ):addInstance( 'Close' ); +local Action = require( 'src.characters.actions.Action' ) +local Messenger = require( 'src.Messenger' ) - function self:perform() - local target = tile:getWorldObject(); - assert( target:isOpenable(), 'Target needs to be openable!' ); - assert( target:isPassable(), 'Target tile needs to be passable!' ); - assert( tile:isAdjacent( character:getTile() ), 'Character has to be adjacent to the target tile!' ); +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ - Messenger.publish( 'ACTION_DOOR' ); +local Close = Action:subclass( 'Close' ) - target:setPassable( false ); - target:setBlocksVision( true ); +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - tile:setDirty( true ); - return true; - end +function Close:initialize( character, target ) + Action.initialize( self, character, target, target:getWorldObject():getInteractionCost( character:getStance() )) +end + +function Close:perform() + local targetObject = self.target:getWorldObject() + assert( targetObject:isOpenable(), 'Target needs to be openable!' ) + assert( targetObject:isPassable(), 'Target tile needs to be passable!' ) + assert( self.target:isAdjacent( self.character:getTile() ), 'Character has to be adjacent to the target tile!' ) + + Messenger.publish( 'ACTION_DOOR' ) + + -- Update the WorldObject. + targetObject:setPassable( false ) + targetObject:setBlocksVision( true ) - return self; + -- Mark target tile for update. + self.target:setDirty( true ) + return true end -return Close; +return Close diff --git a/src/turnbased/helpers/InteractionInput.lua b/src/turnbased/helpers/InteractionInput.lua index 3dad9b5d..14fe9d17 100644 --- a/src/turnbased/helpers/InteractionInput.lua +++ b/src/turnbased/helpers/InteractionInput.lua @@ -29,7 +29,7 @@ function InteractionInput.new() -- local function handleDoors( target, character ) if target:isPassable() then - character:enqueueAction( Close.new( character, target )); + character:enqueueAction( Close( character, target )) else character:enqueueAction( Open.new( character, target )); end From 3ad8376b9263369f30424aa10548f03b6513589b Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 30 Nov 2017 00:44:13 +0100 Subject: [PATCH 007/178] Refactor Open Action --- src/characters/actions/Open.lua | 53 ++++++++++++++-------- src/characters/pathfinding/Path.lua | 2 +- src/turnbased/helpers/InteractionInput.lua | 2 +- 3 files changed, 37 insertions(+), 20 deletions(-) diff --git a/src/characters/actions/Open.lua b/src/characters/actions/Open.lua index 6b33e2a8..27a77ee4 100644 --- a/src/characters/actions/Open.lua +++ b/src/characters/actions/Open.lua @@ -1,27 +1,44 @@ -local Action = require('src.characters.actions.Action'); -local Messenger = require( 'src.Messenger' ); +--- +-- This Action is used when a character opens an openable world object. +-- @module Open +-- -local Open = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ -function Open.new( character, tile ) - local self = Action.new( tile:getWorldObject():getInteractionCost( character:getStance() ), tile ):addInstance( 'Open' ); +local Action = require( 'src.characters.actions.Action' ) +local Messenger = require( 'src.Messenger' ) - function self:perform() - local target = tile:getWorldObject(); - assert( target:isOpenable(), 'Target needs to be openable!' ); - assert( not target:isPassable(), 'Target tile needs to be impassable!' ); - assert( tile:isAdjacent( character:getTile() ), 'Character has to be adjacent to the target tile!' ); +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ - Messenger.publish( 'ACTION_DOOR' ); +local Open = Action:subclass( 'Close' ) - target:setPassable( true ); - target:setBlocksVision( false ); +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - tile:setDirty( true ); - return true; - end +function Open:initialize( character, target ) + Action.initialize( self, character, target, target:getWorldObject():getInteractionCost( character:getStance() )) +end + +function Open:perform() + local targetObject = self.target:getWorldObject() + assert( targetObject:isOpenable(), 'Target needs to be openable!' ) + assert( not targetObject:isPassable(), 'Target tile needs to be impassable!' ) + assert( self.target:isAdjacent( self.character:getTile() ), 'Character has to be adjacent to the target tile!' ) + + Messenger.publish( 'ACTION_DOOR' ) + + -- Update the WorldObject. + targetObject:setPassable( true ) + targetObject:setBlocksVision( false ) - return self; + -- Mark target tile for update. + self.target:setDirty( true ) + return true end -return Open; +return Open diff --git a/src/characters/pathfinding/Path.lua b/src/characters/pathfinding/Path.lua index 5fd8415f..aea3efca 100644 --- a/src/characters/pathfinding/Path.lua +++ b/src/characters/pathfinding/Path.lua @@ -35,7 +35,7 @@ function Path.new() -- local function handleOpenableObjects( worldObject, character, tile, index ) if not worldObject:isPassable() then - local success = character:enqueueAction( Open.new( character, tile )); + local success = character:enqueueAction( Open( character, tile )) -- Don't create a walk action if the tile is the last one in the path. if index ~= 1 then success = character:enqueueAction( Walk( character, tile )) diff --git a/src/turnbased/helpers/InteractionInput.lua b/src/turnbased/helpers/InteractionInput.lua index 14fe9d17..ae7700e0 100644 --- a/src/turnbased/helpers/InteractionInput.lua +++ b/src/turnbased/helpers/InteractionInput.lua @@ -31,7 +31,7 @@ function InteractionInput.new() if target:isPassable() then character:enqueueAction( Close( character, target )) else - character:enqueueAction( Open.new( character, target )); + character:enqueueAction( Open( character, target )) end end From 566fe171a211cbc7564c80dc7ac3c55fe6ca9e34 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 30 Nov 2017 00:51:43 +0100 Subject: [PATCH 008/178] Refactor Crouch, LieDown and StandUp actions --- src/characters/actions/Crouch.lua | 43 +++++++++++++++++++------- src/characters/actions/LieDown.lua | 43 +++++++++++++++++++------- src/characters/actions/StandUp.lua | 43 +++++++++++++++++++------- src/turnbased/states/PlanningState.lua | 6 ++-- 4 files changed, 96 insertions(+), 39 deletions(-) diff --git a/src/characters/actions/Crouch.lua b/src/characters/actions/Crouch.lua index b6c12c2d..66d081e5 100644 --- a/src/characters/actions/Crouch.lua +++ b/src/characters/actions/Crouch.lua @@ -1,22 +1,41 @@ -local Action = require('src.characters.actions.Action'); +--- +-- This Action is used to change a character's stance to a crouch. +-- @module Crouch +-- -local Crouch = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ + +local Action = require( 'src.characters.actions.Action' ) + +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ + +local Crouch = Action:subclass( 'Crouch' ) + +-- ------------------------------------------------ +-- Constants +-- ------------------------------------------------ local STANCES = require( 'src.constants.STANCES' ) -function Crouch.new( character ) - local self = Action.new( 1, character:getTile() ):addInstance( 'Crouch' ); +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - function self:perform() - if character:getStance() == STANCES.CROUCH then - return false; - end +function Crouch:initialize( character ) + Action.initialize( self, character, character:getTile(), 1 ) +end - character:setStance( STANCES.CROUCH ); - return true; +function Crouch:perform() + if self.character:getStance() == STANCES.CROUCH then + return false end - return self; + self.character:setStance( STANCES.CROUCH ) + return true end -return Crouch; +return Crouch diff --git a/src/characters/actions/LieDown.lua b/src/characters/actions/LieDown.lua index e6f342e2..200bb688 100644 --- a/src/characters/actions/LieDown.lua +++ b/src/characters/actions/LieDown.lua @@ -1,22 +1,41 @@ -local Action = require('src.characters.actions.Action'); +--- +-- This Action is used to change the character to a prone stance. +-- @module LieDown +-- -local LieDown = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ + +local Action = require( 'src.characters.actions.Action' ) + +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ + +local LieDown = Action:subclass( 'LieDown' ) + +-- ------------------------------------------------ +-- Constants +-- ------------------------------------------------ local STANCES = require( 'src.constants.STANCES' ) -function LieDown.new( character ) - local self = Action.new( 1, character:getTile() ):addInstance( 'LieDown' ); +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - function self:perform() - if character:getStance() == STANCES.PRONE then - return false; - end +function LieDown:initialize( character ) + Action.initialize( self, character, character:getTile(), 1 ) +end - character:setStance( STANCES.PRONE ); - return true; +function LieDown:perform() + if self.character:getStance() == STANCES.PRONE then + return false end - return self; + self.character:setStance( STANCES.PRONE ) + return true end -return LieDown; +return LieDown diff --git a/src/characters/actions/StandUp.lua b/src/characters/actions/StandUp.lua index e7d43f51..90007097 100644 --- a/src/characters/actions/StandUp.lua +++ b/src/characters/actions/StandUp.lua @@ -1,22 +1,41 @@ -local Action = require('src.characters.actions.Action'); +--- +-- This Action is used to change the character to a standing stance. +-- @module StandUp +-- -local StandUp = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ + +local Action = require( 'src.characters.actions.Action' ) + +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ + +local StandUp = Action:subclass( 'StandUp' ) + +-- ------------------------------------------------ +-- Constants +-- ------------------------------------------------ local STANCES = require( 'src.constants.STANCES' ) -function StandUp.new( character ) - local self = Action.new( 1, character:getTile() ):addInstance( 'StandUp' ); +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - function self:perform() - if character:getStance() == STANCES.STAND then - return false; - end +function StandUp:initialize( character ) + Action.initialize( self, character, character:getTile(), 1 ) +end - character:setStance( STANCES.STAND ); - return true; +function StandUp:perform() + if self.character:getStance() == STANCES.STAND then + return false end - return self; + self.character:setStance( STANCES.STAND ); + return true end -return StandUp; +return StandUp diff --git a/src/turnbased/states/PlanningState.lua b/src/turnbased/states/PlanningState.lua index e47a8773..2a12ab33 100644 --- a/src/turnbased/states/PlanningState.lua +++ b/src/turnbased/states/PlanningState.lua @@ -54,15 +54,15 @@ function PlanningState.new( stateManager ) character:getWeapon():selectPrevFiringMode(); elseif scancode == 'c' then character:clearActions(); - character:enqueueAction( Crouch.new( character )); + character:enqueueAction( Crouch( character )) stateManager:push( 'execution', factions, character ); elseif scancode == 's' then character:clearActions(); - character:enqueueAction( StandUp.new( character )); + character:enqueueAction( StandUp( character )) stateManager:push( 'execution', factions, character ); elseif scancode == 'p' then character:clearActions(); - character:enqueueAction( LieDown.new( character )); + character:enqueueAction( LieDown( character )) stateManager:push( 'execution', factions, character ); elseif scancode == 'r' then character:clearActions(); From f39127eacfcccd214009c3dbd89bdecf4045c3a7 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 30 Nov 2017 00:57:17 +0100 Subject: [PATCH 009/178] Refactor ThrowingAttack and MeleeAttack actions --- src/characters/actions/MeleeAttack.lua | 45 +++++++---- src/characters/actions/ThrowingAttack.lua | 78 +++++++++++-------- .../ai/behaviortree/leafs/BTMeleeAttack.lua | 2 +- .../behaviortree/leafs/BTThrowingAttack.lua | 2 +- src/turnbased/helpers/AttackInput.lua | 4 +- 5 files changed, 80 insertions(+), 51 deletions(-) diff --git a/src/characters/actions/MeleeAttack.lua b/src/characters/actions/MeleeAttack.lua index 122dab2c..c300f17a 100644 --- a/src/characters/actions/MeleeAttack.lua +++ b/src/characters/actions/MeleeAttack.lua @@ -1,23 +1,38 @@ -local Action = require('src.characters.actions.Action'); -local Messenger = require( 'src.Messenger' ); +--- +-- @module MeleeAttack +-- -local MeleeAttack = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ -function MeleeAttack.new( character, target ) - local self = Action.new( character:getWeapon():getAttackCost(), target ):addInstance( 'MeleeAttack' ); +local Action = require( 'src.characters.actions.Action' ) +local Messenger = require( 'src.Messenger' ) - function self:perform() - if not target:isAdjacent( character:getTile() ) then - return false; - end +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ - local weapon = character:getWeapon(); - Messenger.publish( 'SOUND_ATTACK', weapon ); - target:hit( weapon:getDamage(), weapon:getDamageType() ); - return true; +local MeleeAttack = Action:subclass( 'MeleeAttack' ) + +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ + +function MeleeAttack:initialize( character, target ) + Action.initialize( self, character, target, character:getWeapon():getAttackCost() ) +end + +function MeleeAttack:perform() + if not self.target:isAdjacent( self.character:getTile() ) then + return false end - return self; + local weapon = self.character:getWeapon() + self.target:hit( weapon:getDamage(), weapon:getDamageType() ) + + Messenger.publish( 'SOUND_ATTACK', weapon ) + return true end -return MeleeAttack; +return MeleeAttack diff --git a/src/characters/actions/ThrowingAttack.lua b/src/characters/actions/ThrowingAttack.lua index 6b7090db..9f36f93d 100644 --- a/src/characters/actions/ThrowingAttack.lua +++ b/src/characters/actions/ThrowingAttack.lua @@ -1,34 +1,48 @@ -local Action = require('src.characters.actions.Action'); -local ProjectileManager = require( 'src.items.weapons.ProjectileManager' ); -local ThrownProjectileQueue = require( 'src.items.weapons.ThrownProjectileQueue' ); -local Bresenham = require( 'lib.Bresenham' ); - -local ThrowingAttack = {}; - -function ThrowingAttack.new( character, target ) - local self = Action.new( character:getWeapon():getAttackCost(), target ):addInstance( 'ThrowingAttack' ); - - function self:perform() - -- Pick the actual target based on the weapon's range attribute. - local ox, oy = character:getTile():getPosition(); - local tx, ty = target:getPosition(); - local th = target:getHeight() - - local ax, ay - Bresenham.line( ox, oy, tx, ty, function( cx, cy, count ) - if count > character:getWeapon():getRange() then - return false; - end - ax, ay = cx, cy - return true; - end); - - local package = ThrownProjectileQueue.new( character, ax, ay, th ) - ProjectileManager.register( package ); - return true; - end - - return self; +--- +-- @module ThrowingAttack +-- + +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ + +local Action = require( 'src.characters.actions.Action' ) +local ProjectileManager = require( 'src.items.weapons.ProjectileManager' ) +local ThrownProjectileQueue = require( 'src.items.weapons.ThrownProjectileQueue' ) +local Bresenham = require( 'lib.Bresenham' ) + +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ + +local ThrowingAttack = Action:subclass( 'ThrowingAttack' ) + +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ + +function ThrowingAttack:initialize( character, target ) + Action.initialize( self, character, target, character:getWeapon():getAttackCost() ) +end + +function ThrowingAttack:perform() + -- Pick the actual target based on the weapon's range attribute. + local ox, oy = self.character:getTile():getPosition() + local tx, ty = self.target:getPosition() + local th = self.target:getHeight() + + local ax, ay + Bresenham.line( ox, oy, tx, ty, function( cx, cy, count ) + if count > self.character:getWeapon():getRange() then + return false + end + ax, ay = cx, cy + return true + end) + + local package = ThrownProjectileQueue.new( self.character, ax, ay, th ) + ProjectileManager.register( package ) + return true end -return ThrowingAttack; +return ThrowingAttack diff --git a/src/characters/ai/behaviortree/leafs/BTMeleeAttack.lua b/src/characters/ai/behaviortree/leafs/BTMeleeAttack.lua index e2a37f23..cdebaee4 100644 --- a/src/characters/ai/behaviortree/leafs/BTMeleeAttack.lua +++ b/src/characters/ai/behaviortree/leafs/BTMeleeAttack.lua @@ -10,7 +10,7 @@ function BTMeleeAttack.new() function self:traverse( ... ) local blackboard, character = ...; - local success = character:enqueueAction( MeleeAttack.new( character, blackboard.target )); + local success = character:enqueueAction( MeleeAttack( character, blackboard.target )) if success then Log.debug( 'Character attacks target', 'BTMeleeAttack' ); return true; diff --git a/src/characters/ai/behaviortree/leafs/BTThrowingAttack.lua b/src/characters/ai/behaviortree/leafs/BTThrowingAttack.lua index 46d0f578..a6dd6310 100644 --- a/src/characters/ai/behaviortree/leafs/BTThrowingAttack.lua +++ b/src/characters/ai/behaviortree/leafs/BTThrowingAttack.lua @@ -10,7 +10,7 @@ function BTThrowingAttack.new() function self:traverse( ... ) local blackboard, character = ...; - local success = character:enqueueAction( ThrowingAttack.new( character, blackboard.target )); + local success = character:enqueueAction( ThrowingAttack( character, blackboard.target )) if success then -- Store weapon id for the rearm action. blackboard.weaponID = character:getWeapon():getID(); diff --git a/src/turnbased/helpers/AttackInput.lua b/src/turnbased/helpers/AttackInput.lua index dcf14cf5..bd8776e2 100644 --- a/src/turnbased/helpers/AttackInput.lua +++ b/src/turnbased/helpers/AttackInput.lua @@ -44,12 +44,12 @@ function AttackInput.new() -- Handle Melee weapons. if weapon:getSubType() == WEAPON_TYPES.MELEE then - character:enqueueAction( MeleeAttack.new( character, target )); + character:enqueueAction( MeleeAttack( character, target )) end -- Handle Thrown weapons. if weapon:getSubType() == WEAPON_TYPES.THROWN then - character:enqueueAction( ThrowingAttack.new( character, target )); + character:enqueueAction( ThrowingAttack( character, target )) character:enqueueAction( Rearm.new( character, weapon:getID() )); end From bcd421ca9529c4a4525bd4cc570b6c26991b20e2 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 30 Nov 2017 00:59:50 +0100 Subject: [PATCH 010/178] Refactor OpenInventory Action --- src/characters/actions/OpenInventory.lua | 36 +++++++++++++++------- src/characters/pathfinding/Path.lua | 2 +- src/turnbased/helpers/InteractionInput.lua | 6 ++-- src/turnbased/states/PlanningState.lua | 2 +- 4 files changed, 30 insertions(+), 16 deletions(-) diff --git a/src/characters/actions/OpenInventory.lua b/src/characters/actions/OpenInventory.lua index 9b9cc204..89dd64c9 100644 --- a/src/characters/actions/OpenInventory.lua +++ b/src/characters/actions/OpenInventory.lua @@ -1,17 +1,31 @@ -local Action = require('src.characters.actions.Action'); -local ScreenManager = require( 'lib.screenmanager.ScreenManager' ); +--- +-- @module OpenInventory +-- -local OpenInventory = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ -function OpenInventory.new( character, target ) - local self = Action.new( 0, target ):addInstance( 'OpenInventory' ); +local Action = require( 'src.characters.actions.Action' ) +local ScreenManager = require( 'lib.screenmanager.ScreenManager' ) - function self:perform() - ScreenManager.push( 'inventory', character, target ); - return true; - end +-- ------------------------------------------------ +-- Required Module +-- ------------------------------------------------ - return self; +local OpenInventory = Action:subclass( 'OpenInventory' ) + +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ + +function OpenInventory:initialize( character, target ) + Action.initialize( self, character, target, 0 ) +end + +function OpenInventory:perform() + ScreenManager.push( 'inventory', self.character, self.target ) + return true end -return OpenInventory; +return OpenInventory diff --git a/src/characters/pathfinding/Path.lua b/src/characters/pathfinding/Path.lua index aea3efca..299aaf9d 100644 --- a/src/characters/pathfinding/Path.lua +++ b/src/characters/pathfinding/Path.lua @@ -94,7 +94,7 @@ function Path.new() end if worldObject:isContainer() then - character:enqueueAction( OpenInventory.new( character, tile )); + character:enqueueAction( OpenInventory( character, tile )) end else success = character:enqueueAction( Walk( character, tile )) diff --git a/src/turnbased/helpers/InteractionInput.lua b/src/turnbased/helpers/InteractionInput.lua index ae7700e0..5b739ee0 100644 --- a/src/turnbased/helpers/InteractionInput.lua +++ b/src/turnbased/helpers/InteractionInput.lua @@ -65,7 +65,7 @@ function InteractionInput.new() end if target:getWorldObject():isContainer() then - character:enqueueAction( OpenInventory.new( character, target )); + character:enqueueAction( OpenInventory( character, target )) return true; end return false; @@ -74,14 +74,14 @@ function InteractionInput.new() -- Handle interactions with other characters. if target:isOccupied() then if target:getCharacter():getFaction():getType() == character:getFaction():getType() then - character:enqueueAction( OpenInventory.new( character, target )); + character:enqueueAction( OpenInventory( character, target )) return true; end return false; end -- Allow interaction with empty adjacent tiles. - character:enqueueAction( OpenInventory.new( character, target )) + character:enqueueAction( OpenInventory( character, target )) return true; end diff --git a/src/turnbased/states/PlanningState.lua b/src/turnbased/states/PlanningState.lua index 2a12ab33..104f08d7 100644 --- a/src/turnbased/states/PlanningState.lua +++ b/src/turnbased/states/PlanningState.lua @@ -96,7 +96,7 @@ function PlanningState.new( stateManager ) inputStateHandler:switch( 'movement' ); factions:nextFaction(); elseif scancode == 'i' then - character:enqueueAction( OpenInventory.new( character, character:getTile() )); + character:enqueueAction( OpenInventory( character, character:getTile() )) stateManager:push( 'execution', factions, character ); elseif scancode == 'h' then ScreenManager.push( 'health', character ); From 021b854e625f949d742b70fc5b7d4d394c7868e4 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 30 Nov 2017 01:09:44 +0100 Subject: [PATCH 011/178] Refactor Rearm action --- src/characters/actions/Rearm.lua | 101 +++++++++++------- .../ai/behaviortree/leafs/BTRearm.lua | 2 +- src/turnbased/helpers/AttackInput.lua | 2 +- 3 files changed, 67 insertions(+), 38 deletions(-) diff --git a/src/characters/actions/Rearm.lua b/src/characters/actions/Rearm.lua index 57a9d6cb..44e9ac15 100644 --- a/src/characters/actions/Rearm.lua +++ b/src/characters/actions/Rearm.lua @@ -1,46 +1,75 @@ -local Action = require('src.characters.actions.Action'); - -local Rearm = {}; - -function Rearm.new( character, weaponID ) - local self = Action.new( 0, character:getTile() ):addInstance( 'Rearm' ); - - function self:perform() - local inventory = character:getInventory(); - local weapon; - - for _, item in pairs( inventory:getItems() ) do - if item:instanceOf( 'Weapon' ) and item:getID() == weaponID then - weapon = item; - break; - elseif item:instanceOf( 'ItemStack' ) then - for _, sitem in pairs( item:getItems() ) do - if sitem:instanceOf( 'Weapon' ) and sitem:getID() == weaponID then - weapon = sitem; - break; - end +--- +-- This Action tries to re-equip the same weapon type the character had +-- previously equipped. +-- @module Rearm +-- + +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ + +local Action = require( 'src.characters.actions.Action' ) + +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ + +local Rearm = Action:subclass( 'Rearm' ) + +-- ------------------------------------------------ +-- Private Methods +-- ------------------------------------------------ + +--- +-- Searches through the character's inventory and returns an item that fits +-- the searched weaponID. +-- @tparam Inventory inventory The inventory to search through. +-- @tparam string weaponID The item id to search for. +-- @treturn Item The item matching the searched id (or nil). +-- +local function findItem( inventory, weaponID ) + for _, item in pairs( inventory:getItems() ) do + if item:instanceOf( 'Weapon' ) and item:getID() == weaponID then + return item + elseif item:instanceOf( 'ItemStack' ) then + for _, sitem in pairs( item:getItems() ) do + if sitem:instanceOf( 'Weapon' ) and sitem:getID() == weaponID then + return sitem end end end + end +end - if not weapon then - return false; - end +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - -- Remove item from backpack and add it to the equipment slot. - local equipment = character:getEquipment(); - for _, slot in pairs( equipment:getSlots() ) do - if weapon:isSameType( slot:getItemType(), slot:getSubType() ) then - equipment:addItem( slot, weapon ); - inventory:removeItem( weapon ); - return true; - end - end +function Rearm:initialize( character, weaponID ) + Action.initialize( self, character, character:getTile(), 0 ) + + self.weaponID = weaponID +end - return false; +function Rearm:perform() + local weapon = findItem( self.character:getInventory(), self.weaponID ) + + -- Quit early if we haven't found a valid weapon. + if not weapon then + return false + end + + -- Remove item from backpack and add it to the equipment slot. + local equipment = self.character:getEquipment() + for _, slot in pairs( equipment:getSlots() ) do + if weapon:isSameType( slot:getItemType(), slot:getSubType() ) then + equipment:addItem( slot, weapon ) + self.character:getInventory():removeItem( weapon ) + return true + end end - return self; + return false end -return Rearm; +return Rearm diff --git a/src/characters/ai/behaviortree/leafs/BTRearm.lua b/src/characters/ai/behaviortree/leafs/BTRearm.lua index 0ea1696e..01014bcb 100644 --- a/src/characters/ai/behaviortree/leafs/BTRearm.lua +++ b/src/characters/ai/behaviortree/leafs/BTRearm.lua @@ -10,7 +10,7 @@ function BTRearm.new() function self:traverse( ... ) local blackboard, character = ...; - local success = character:enqueueAction( Rearm.new( character, blackboard.weaponID )); + local success = character:enqueueAction( Rearm( character, blackboard.weaponID )) if success then Log.debug( 'Equipping throwing weapon ' .. blackboard.weaponID, 'BTRearm' ); return true; diff --git a/src/turnbased/helpers/AttackInput.lua b/src/turnbased/helpers/AttackInput.lua index bd8776e2..3b933e14 100644 --- a/src/turnbased/helpers/AttackInput.lua +++ b/src/turnbased/helpers/AttackInput.lua @@ -50,7 +50,7 @@ function AttackInput.new() -- Handle Thrown weapons. if weapon:getSubType() == WEAPON_TYPES.THROWN then character:enqueueAction( ThrowingAttack( character, target )) - character:enqueueAction( Rearm.new( character, weapon:getID() )); + character:enqueueAction( Rearm( character, weapon:getID() )) end -- Handle Ranged weapons. From 33b136281607df4494cb2846800c3350eb0c8456 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 30 Nov 2017 01:21:08 +0100 Subject: [PATCH 012/178] Refactor Reload Action --- src/characters/actions/Reload.lua | 89 +++++++++++-------- .../ai/behaviortree/leafs/BTReload.lua | 2 +- src/turnbased/states/PlanningState.lua | 2 +- 3 files changed, 56 insertions(+), 37 deletions(-) diff --git a/src/characters/actions/Reload.lua b/src/characters/actions/Reload.lua index 34984dad..4ef79ec5 100644 --- a/src/characters/actions/Reload.lua +++ b/src/characters/actions/Reload.lua @@ -1,52 +1,71 @@ -local Log = require( 'src.util.Log' ); -local Action = require('src.characters.actions.Action'); +--- +-- This Action tries to reload the currently equipped weapon. +-- @module Reload +-- -local Reload = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ -function Reload.new( character ) - local self = Action.new( 5, character:getTile() ):addInstance( 'Reload' ); +local Action = require( 'src.characters.actions.Action' ) +local Log = require( 'src.util.Log' ) - local function reload( weapon, inventory, item ) - if item:instanceOf( 'Ammunition' ) and item:getCaliber() == weapon:getMagazine():getCaliber() then - weapon:getMagazine():addRound( item ) - inventory:removeItem( item ) - end +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ + +local Reload = Action:subclass( 'Reload' ) + +-- ------------------------------------------------ +-- Private Methods +-- ------------------------------------------------ + +local function reload( weapon, inventory, item ) + if item:instanceOf( 'Ammunition' ) and item:getCaliber() == weapon:getMagazine():getCaliber() then + weapon:getMagazine():addRound( item ) + inventory:removeItem( item ) end +end - function self:perform() - local weapon = character:getWeapon(); +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - if not weapon or not weapon:isReloadable() then - Log.debug( 'Can not reload.' ); - return false; - end +function Reload:initialize( character ) + Action.initialize( self, character, character:getTile(), 5 ) +end - if weapon:getMagazine():isFull() then - Log.debug( 'Weapon is fully loaded.' ); - return false; - end +function Reload:perform() + local weapon = self.character:getWeapon() - local inventory = character:getInventory(); - for _, item in pairs( inventory:getItems() ) do - if item:instanceOf( 'ItemStack' ) then - for _, sitem in pairs( item:getItems() ) do - reload( weapon, inventory, sitem ) - if weapon:getMagazine():isFull() then - return true - end - end - elseif item:instanceOf( 'Item' ) then - reload( weapon, inventory, item ) + if not weapon or not weapon:isReloadable() then + Log.debug( 'Can not reload.' ) + return false + end + + if weapon:getMagazine():isFull() then + Log.debug( 'Weapon is fully loaded.' ) + return false + end + + local inventory = self.character:getInventory() + for _, item in pairs( inventory:getItems() ) do + if item:instanceOf( 'ItemStack' ) then + for _, sitem in pairs( item:getItems() ) do + reload( weapon, inventory, sitem ) if weapon:getMagazine():isFull() then return true end end + elseif item:instanceOf( 'Item' ) then + reload( weapon, inventory, item ) + if weapon:getMagazine():isFull() then + return true + end end - - return true; end - return self; + return true end -return Reload; +return Reload diff --git a/src/characters/ai/behaviortree/leafs/BTReload.lua b/src/characters/ai/behaviortree/leafs/BTReload.lua index 8aa5c995..44f67c20 100644 --- a/src/characters/ai/behaviortree/leafs/BTReload.lua +++ b/src/characters/ai/behaviortree/leafs/BTReload.lua @@ -10,7 +10,7 @@ function BTReload.new() function self:traverse( ... ) local _, character = ...; - local success = character:enqueueAction( Reload.new( character )); + local success = character:enqueueAction( Reload( character )) if success then Log.debug( 'Reloading weapon', 'BTReload' ); return true; diff --git a/src/turnbased/states/PlanningState.lua b/src/turnbased/states/PlanningState.lua index 104f08d7..d9787751 100644 --- a/src/turnbased/states/PlanningState.lua +++ b/src/turnbased/states/PlanningState.lua @@ -66,7 +66,7 @@ function PlanningState.new( stateManager ) stateManager:push( 'execution', factions, character ); elseif scancode == 'r' then character:clearActions(); - character:enqueueAction( Reload.new( character )); + character:enqueueAction( Reload( character )) stateManager:push( 'execution', factions, character ); elseif scancode == 'a' then -- Make attack mode toggleable. From 05c3b9f6059ac2190663428527b9be35302fdb14 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sun, 26 Nov 2017 22:56:52 +0100 Subject: [PATCH 013/178] Refactor Body class --- src/characters/body/Body.lua | 494 ++++++++++++++++------------ src/characters/body/BodyFactory.lua | 2 +- 2 files changed, 292 insertions(+), 204 deletions(-) diff --git a/src/characters/body/Body.lua b/src/characters/body/Body.lua index 586281b3..e87a9253 100644 --- a/src/characters/body/Body.lua +++ b/src/characters/body/Body.lua @@ -1,264 +1,352 @@ -local Log = require( 'src.util.Log' ); -local Object = require( 'src.Object' ); -local StatusEffects = require( 'src.characters.body.StatusEffects' ); +--- +-- The Body module is used to keep track of a creature's body parts and health +-- related stats. Each Body in the game is modelled as a graph with nodes being +-- the body parts and edges determining how the different parts are connected. +-- +-- The body also contains a creature's the inventory and equipment. +-- +-- @module Body +-- -- ------------------------------------------------ --- Module +-- Required Modules -- ------------------------------------------------ -local Body = {}; +local Log = require( 'src.util.Log' ) +local Class = require( 'lib.Middleclass' ) +local StatusEffects = require( 'src.characters.body.StatusEffects' ) -- ------------------------------------------------ --- Constants +-- Module -- ------------------------------------------------ -local STATUS_EFFECTS = require( 'src.constants.STATUS_EFFECTS' ); +local Body = Class( 'Body' ) -- ------------------------------------------------ --- Constructor +-- Constants -- ------------------------------------------------ -function Body.new( template ) - local self = Object.new():addInstance( 'Body' ); +local STATUS_EFFECTS = require( 'src.constants.STATUS_EFFECTS' ) - local bloodVolume = template.bloodVolume - - local inventory; - local equipment; - local nodes = {}; - local edges = {}; - local statusEffects = StatusEffects.new(); - - -- ------------------------------------------------ - -- Private Methods - -- ------------------------------------------------ +-- ------------------------------------------------ +-- Private Methods +-- ------------------------------------------------ - --- - -- Ticks the bleeding effects on a node and applies the dead effect to the - -- character if the blood volume is below zero. - -- @param node (BodyPart) The bodypart to tick. - -- - local function handleBleeding( node ) - if node:isBleeding() then - bloodVolume = bloodVolume - node:getBloodLoss(); - if bloodVolume <= 0 then - Log.debug( 'Character bleeds to death!', 'Body' ); - statusEffects:add({ STATUS_EFFECTS.DEAD }) - end +--- +-- Ticks the bleeding effects on a node and applies the dead effect to the +-- creature if the blood volume is below zero. +-- @tparam Body self The body instance to use. +-- @tparam BodyPart bodyPart The bodypart to tick. +-- +local function handleBleeding( self, bodyPart ) + if bodyPart:isBleeding() then + self.curBloodVolume = self.curBloodVolume - bodyPart:getBloodLoss() + if self.curBloodVolume <= 0 then + Log.debug( 'Character bleeds to death!', 'Body' ) + self.statusEffects:add({ STATUS_EFFECTS.DEAD }) end end +end - --- - -- Checks if the body part's node is connected to an equipment slot and if - -- that equipment slot contains an item of the type clothing. If that's the - -- case it reduces the damage based on the type of damage and the type of - -- armor stats the item in the equipment slot has. - -- @param node (BodyPart) The body part to damage. - -- @param damage (number) The amount of damage. - -- @param damageType (string) The type of damage. - -- @return (number) The modified damage value. - -- - local function checkArmorProtection( node, damage, _ ) - local slots = equipment:getSlots(); - for _, edge in ipairs( edges ) do - local slot = slots[edge.from]; - -- If edge.from points to an equipment slot and the edge connects it - -- to the body part currently receiving damage we check for armor. - if slot and edge.to == node:getIndex() then - -- Get the slot contains an item and the item is of type clothing we check - -- if the attack actually hits a part that is covered by the armor. - local item = slot:getItem(); - if item and item:instanceOf( 'Armor' ) then - Log.debug( 'Body part is protected by armor ' .. item:getID(), 'Body' ); - if love.math.random( 0, 100 ) < item:getArmorCoverage() then - Log.debug( string.format( 'The armor absorbs %d points of damage.', item:getArmorProtection() ), 'Body' ); - damage = damage - item:getArmorProtection(); - end +--- +-- Checks if the body part's node is connected to an equipment slot and if +-- that equipment slot contains an item of the type clothing. If that's the +-- case it reduces the damage based on the type of damage and the type of +-- armor stats the item in the equipment slot has. +-- @tparam Body self The body instance to use. +-- @tparam BodyPart bodyPart The body part to damage. +-- @tparam number damage The amount of damage. +-- @treturn number The modified damage value. +-- +local function checkArmorProtection( self, bodyPart, damage ) + local slots = self.equipment:getSlots() + for _, edge in ipairs( self.edges ) do + local slot = slots[edge.from] + -- If edge.from points to an equipment slot and the edge connects it + -- to the body part currently receiving damage we check for armor. + if slot and edge.to == bodyPart:getIndex() then + -- Get the slot contains an item and the item is of type clothing we check + -- if the attack actually hits a part that is covered by the armor. + local item = slot:getItem() + if item and item:instanceOf( 'Armor' ) then + Log.debug( 'Body part is protected by armor ' .. item:getID(), 'Body' ) + if love.math.random( 0, 100 ) < item:getArmorCoverage() then + Log.debug( string.format( 'The armor absorbs %d points of damage.', item:getArmorProtection() ), 'Body' ) + damage = damage - item:getArmorProtection() end end end - return damage; end + return damage +end - --- - -- Picks a random entry node and returns it. - -- @return (BodyPart) The bodypart used as an entry point for the graph. - -- - local function selectEntryNode() - local entryNodes = {}; - for _, node in pairs( nodes ) do - if node:isEntryNode() and not node:isDestroyed() then - entryNodes[#entryNodes + 1] = node; - end +--- +-- Picks a random entry node and returns it. +-- @tparam Body self The body instance to use. +-- @treturn BodyPart The bodypart used as an entry point for the graph. +-- +local function selectEntryNode( self ) + local entryNodes = {} + for _, node in pairs( self.nodes ) do + if node:isEntryNode() and not node:isDestroyed() then + entryNodes[#entryNodes + 1] = node end - return entryNodes[love.math.random( #entryNodes )]; end + return entryNodes[love.math.random( #entryNodes )] +end - --- - -- Propagates the damage from parent to child nodes. - -- @param node (BodyPart) The body part to damage. - -- @param damage (number) The amount of damage. - -- @param damageType (string) The type of damage. - -- - local function destroyChildNodes( node ) - node:destroy(); - - -- Add status effects. - statusEffects:add( node:getEffects() ); - - -- Randomly propagate the damage to connected nodes. - for _, edge in ipairs( edges ) do - if edge.from == node:getIndex() then - destroyChildNodes( nodes[edge.to] ); - end +--- +-- Destroys all child nodes. This is used when a parent body part is destroyed +-- and basically simulates how for example the destruction of an upper arm would +-- also mean the lower arm, hand and fingers are destroyed. +-- @tparam Body self The body instance to use. +-- @tparam BodyPart bodyPart The body part to damage. +-- +local function destroyChildNodes( self, bodyPart ) + bodyPart:destroy() + + -- Add status effects. + self.statusEffects:add( bodyPart:getEffects() ) + + -- Recursively destroy child nodes connected to the body part. + for _, edge in ipairs( self.edges ) do + if edge.from == bodyPart:getIndex() then + destroyChildNodes( self, self.nodes[edge.to] ) end end +end - --- - -- Propagates the damage from parent to child nodes. - -- @param node (BodyPart) The body part to damage. - -- @param damage (number) The amount of damage. - -- @param damageType (string) The type of damage. - -- - local function propagateDamage( node, damage, damageType ) - damage = checkArmorProtection( node, damage, damageType ); - - -- Stop damage propagation if the armor has stopped all of the incoming damage. - if damage <= 0 then - return; - end - - node:hit( damage, damageType ); - handleBleeding( node ); +--- +-- Propagates the damage from parent to child nodes. +-- @tparam Body self The body instance to use. +-- @tparam BodyPart bodyPart The body part to damage. +-- @tparam number damage The amount of damage. +-- @tparam string damageType The type of damage. +-- +local function propagateDamage( self, bodyPart, damage, damageType ) + damage = checkArmorProtection( self, bodyPart, damage ) + + -- Stop damage propagation if the armor has stopped all of the incoming damage. + if damage <= 0 then + return + end - -- Manually destroy child nodes if parent node is destroyed. - if node:isDestroyed() then - destroyChildNodes( node ); - return; - end + bodyPart:hit( damage, damageType ) + handleBleeding( self, bodyPart ) - -- Randomly propagate the damage to connected nodes. - for _, edge in ipairs( edges ) do - if edge.from == node:getIndex() and not nodes[edge.to]:isDestroyed() and love.math.random( 100 ) < tonumber( edge.name ) then - propagateDamage( nodes[edge.to], damage, damageType ); - end - end + -- Manually destroy child nodes if parent node is destroyed. + if bodyPart:isDestroyed() then + destroyChildNodes( self, bodyPart ) + return end - -- ------------------------------------------------ - -- Public Methods - -- ------------------------------------------------ - - function self:tickOneTurn() - for _, node in pairs( nodes ) do - if node:isBleeding() then - handleBleeding( node ); - end + -- Randomly propagate the damage to connected nodes. + for _, edge in ipairs( self.edges ) do + if edge.from == bodyPart:getIndex() and not self.nodes[edge.to]:isDestroyed() and love.math.random( 100 ) < tonumber( edge.name ) then + propagateDamage( self, self.nodes[edge.to], damage, damageType ) end end +end - function self:hit( damage, damageType ) - local entryNode = selectEntryNode(); - Log.debug( "Attack enters body at " .. entryNode:getID(), 'Body' ); +-- ------------------------------------------------ +-- Constructor +-- ------------------------------------------------ - propagateDamage( entryNode, damage, damageType ); - end +--- +-- Initializes the Body instance. +-- @tparam string id The creature's id. +-- @tparam number bloodVolume The creature's maximum blood volume. +-- @tparam table tags The creature's tags. +-- @tparam table sizes A table containing the creature's sizes mapped to its stances. +-- +function Body:initialize( id, bloodVolume, tags, sizes ) + self.id = id - function self:addBodyPart( bodyPart ) - local index = bodyPart:getIndex(); - assert( not nodes[index], 'ID already used! BodyParts have to be unique.' ); - nodes[index] = bodyPart; - end + self.maxBloodVolume = bloodVolume + self.curBloodVolume = bloodVolume - function self:addConnection( connection ) - edges[#edges + 1] = connection; - end + self.tags = tags + self.sizes = sizes - function self:serialize() - local t = { - ['id'] = template.id, - ['inventory'] = inventory:serialize(), - ['equipment'] = equipment:serialize(), - ['statusEffects'] = statusEffects:serialize() - }; - - -- Serialize body graph's edges. - t['edges'] = {}; - for i, edge in ipairs( edges ) do - t['edges'][i] = { - ['name'] = edge.name, - ['from'] = edge.from, - ['to'] = edge.to - } - end + self.nodes = {} + self.edges = {} - -- Serialize body graph's nodes. - t['nodes'] = {}; - for i, node in ipairs( nodes ) do - t['nodes'][i] = node:serialize(); - end + self.statusEffects = StatusEffects.new() +end - return t; - end +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - function self:heal() - -- Restore blood volume. - bloodVolume = template.bloodVolume +--- +-- Adds a new body part to this body. +-- @tparam BodyPart bodyPart The body part to add. +-- +function Body:addBodyPart( bodyPart ) + local index = bodyPart:getIndex() + assert( not self.nodes[index], 'ID already used! BodyParts have to be unique.' ) + self.nodes[index] = bodyPart +end - -- Heal body parts. - for _, node in pairs( nodes ) do - node:heal() - end +--- +-- Adds a new connection to this body. Connections are used to determine which +-- body parts are connected to each other and are represented by edges in the +-- body graph. +-- +function Body:addConnection( connection ) + self.edges[#self.edges + 1] = connection +end - -- Remove status effects. - statusEffects:remove({ STATUS_EFFECTS.BLIND }) +--- +-- Updates the body once. This should be used to update status effects once per +-- round. +-- +function Body:tickOneTurn() + for _, node in pairs( self.nodes ) do + if node:isBleeding() then + handleBleeding( self, node ) + end end +end - function self:getBodyParts() - return nodes; - end +--- +-- Selects a random entry point for the damage and then propagates it along the +-- connections to the appropriate body parts. +-- @tparam number damage The damage to apply to the body. +-- @tparam string damageType The type of damage to apply. +-- +function Body:hit( damage, damageType ) + local entryNode = selectEntryNode( self ) + Log.debug( "Attack enters body at " .. entryNode:getID(), 'Body' ) + + propagateDamage( self, entryNode, damage, damageType ) +end - function self:getBodyPart( id ) - return nodes[id]; +--- +-- Serializes the Body. +-- +function Body:serialize() + local t = { + ['id'] = self.id, + ['inventory'] = self.inventory:serialize(), + ['equipment'] = self.equipment:serialize(), + ['statusEffects'] = self.statusEffects:serialize() + } + + -- Serialize body graph's edges. + t['edges'] = {} + for i, edge in ipairs( self.edges ) do + t['edges'][i] = { + ['name'] = edge.name, + ['from'] = edge.from, + ['to'] = edge.to + } end - function self:getEquipment() - return equipment; + -- Serialize body graph's nodes. + t['nodes'] = {} + for i, node in ipairs( self.nodes ) do + t['nodes'][i] = node:serialize() end - function self:getInventory() - return inventory; - end + return t +end - function self:setInventory( ninventory ) - inventory = ninventory; - end +-- ------------------------------------------------ +-- Getters +-- ------------------------------------------------ - function self:setEquipment( nequipment ) - equipment = nequipment; - end +--- +-- Returns the current amount of blood volume the body has. +-- @treturn number The current amount of blood. +-- +function Body:getBloodVolume() + return self.curBloodVolume +end - function self:getStatusEffects() - return statusEffects; - end +--- +-- Returns all body parts belonging to the body. +-- @treturn table A table containing the body parts. +-- +function Body:getBodyParts() + return self.nodes +end - function self:getBloodVolume() - return bloodVolume; - end +--- +-- Returns a specific body part. +-- @tparam number id The id of the body part to return. +-- @treturn BodyPart The body part belonging to the specified id. +-- +function Body:getBodyPart( id ) + return self.nodes[id] +end - function self:getTags() - return template.tags; - end +--- +-- Returns the creature's equipment. +-- @treturn Equipment The equipment. +-- +function Body:getEquipment() + return self.equipment +end - function self:getID() - return template.id; - end +--- +-- Returns the creature's height based on a certain stance. +-- @tparam string stance The stance to get the height for. +-- @treturn number The height based on the stance. +-- +function Body:getHeight( stance ) + return self.sizes[stance] +end - function self:getHeight( stance ) - return template.size[stance]; - end +--- +-- Returns the creature's id. +-- @treturn string The creature's id. +function Body:getID() + return self.id +end + +--- +-- Returns the creature's inventory. +-- @treturn Inventory The inventory. +-- +function Body:getInventory() + return self.inventory +end + +--- +-- Returns the active status effects. +-- @treturn StatusEffects The status effects for this body. +-- +function Body:getStatusEffects() + return self.statusEffects +end + +--- +-- Returns the body's tags. +-- @treturn table The body's tags. +-- +function Body:getTags() + return self.tags +end + +-- ------------------------------------------------ +-- Setters +-- ------------------------------------------------ + +--- +-- Sets the inventory for this body. +-- @tparam Inventory inventory The new inventory. +-- +function Body:setInventory( inventory ) + self.inventory = inventory +end - return self; +--- +-- Sets the equipment for this body. +-- @tparam Equipment equipment The new equipment. +-- +function Body:setEquipment( equipment ) + self.equipment = equipment end -return Body; +return Body diff --git a/src/characters/body/BodyFactory.lua b/src/characters/body/BodyFactory.lua index 5714f24f..de77cf97 100644 --- a/src/characters/body/BodyFactory.lua +++ b/src/characters/body/BodyFactory.lua @@ -125,7 +125,7 @@ end -- @treturn Body A shiny new Body. -- local function assembleBody( creatureID, template, layout ) - local body = Body.new( template ) + local body = Body( template.id, template.bloodVolume, template.tags, template.size ) local equipment = Equipment.new(); local inventory = Inventory.new( template.defaultCarryWeight, template.defaultCarryVolume ) From d84466d4929e650ddae6446359154072c98de9b0 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 30 Nov 2017 02:24:16 +0100 Subject: [PATCH 014/178] Refactor BodyPart class --- src/characters/body/BodyFactory.lua | 2 +- src/characters/body/BodyPart.lua | 277 ++++++++++++++++++---------- src/ui/screens/HealthScreen.lua | 10 +- 3 files changed, 181 insertions(+), 108 deletions(-) diff --git a/src/characters/body/BodyFactory.lua b/src/characters/body/BodyFactory.lua index de77cf97..df6c0934 100644 --- a/src/characters/body/BodyFactory.lua +++ b/src/characters/body/BodyFactory.lua @@ -112,7 +112,7 @@ local function createBodyPart( cid, body, equipment, index, id ) if template.type == 'equipment' then equipment:addSlot( EquipmentSlot.new( index, template )); else - body:addBodyPart( BodyPart.new( index, template )); + body:addBodyPart( BodyPart( index, template.id, template.type, template.health, template.effects )) end end diff --git a/src/characters/body/BodyPart.lua b/src/characters/body/BodyPart.lua index 3fec8e8f..a55406e2 100644 --- a/src/characters/body/BodyPart.lua +++ b/src/characters/body/BodyPart.lua @@ -1,17 +1,25 @@ -local Log = require( 'src.util.Log' ); -local Object = require( 'src.Object' ); +--- +-- @module BodyPart +-- + +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ + +local Class = require( 'lib.Middleclass' ) +local Log = require( 'src.util.Log' ) -- ------------------------------------------------ -- Module -- ------------------------------------------------ -local BodyPart = {}; +local BodyPart = Class( 'BodyPart' ) -- ------------------------------------------------ -- Constants -- ------------------------------------------------ -local DAMAGE_TYPES = require( 'src.constants.DAMAGE_TYPES' ); +local DAMAGE_TYPES = require( 'src.constants.DAMAGE_TYPES' ) local RND_DAMAGE_PICKER = { DAMAGE_TYPES.SLASHING, @@ -31,132 +39,197 @@ local BLEEDING_AMOUNT = { [DAMAGE_TYPES.BLUDGEONING] = 0.9 } + -- ------------------------------------------------ --- Constructor +-- Private Methods -- ------------------------------------------------ -function BodyPart.new( index, template ) - local self = Object.new():addInstance( 'BodyPart' ); - - local health = template.health; - local maxHealth = health; +--- +-- Returns a random sign. +-- @return (number) Either one or minus one. +-- +local function randomSign() + return love.math.random( 0, 1 ) == 0 and -1 or 1 +end - local bleeding = false; - local bloodLoss = 0; +--- +-- Randomly applies a bleeding effect. The chance is based on the damage type. +-- @tparam BodyPart self The BodyPart instance to use. +-- @tparam number damage The amount of damage to apply. +-- @tparam string damageType The type of damage to apply. +-- +local function bleed( self, damage, damageType ) + local chance = BLEEDING_CHANCE[damageType] + if love.math.random( 100 ) < chance then + self.bleeding = true + local fluffModifier = 1 + randomSign() * ( love.math.random( 50 ) / 100 ) + self.bloodLoss = self.bloodLoss + (( damage / self.curHealth ) * fluffModifier * BLEEDING_AMOUNT[damageType] ) + end +end - -- ------------------------------------------------ - -- Private Methods - -- ------------------------------------------------ +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - --- - -- Returns a random sign. - -- @return (number) Either one or minus one. - -- - local function randomSign() - return love.math.random( 0, 1 ) == 0 and -1 or 1; - end +--- +-- Initializes a new BodyPart instance. +-- @tparam number index The BodyPart's index. +-- @tparam string id The BodyPart's id. +-- @tparam string type The BodyPart's type. +-- @tparam number maxHealth The BodyPart's maximum health. +-- @tparam table effects The effects to apply when the body part is destroyed. +-- +function BodyPart:initialize( index, id, type, maxHealth, effects ) + self.index = index - local function bleed( damage, damageType ) - local chance = BLEEDING_CHANCE[damageType]; - if love.math.random( 100 ) < chance then - bleeding = true; - local fluffModifier = 1 + randomSign() * ( love.math.random( 50 ) / 100 ); - bloodLoss = bloodLoss + (( damage / health ) * fluffModifier * BLEEDING_AMOUNT[damageType] ); - end - end + self.id = id + self.type = type - -- ------------------------------------------------ - -- Public Methods - -- ------------------------------------------------ + self.maxHealth = maxHealth + self.curHealth = maxHealth - function self:hit( damage, damageType ) - -- Transform explosive damage to one of the other three damage types. - if damageType == DAMAGE_TYPES.EXPLOSIVE then - damageType = RND_DAMAGE_PICKER[love.math.random( #RND_DAMAGE_PICKER )]; - end + self.effects = effects - health = health - damage; - Log.debug( string.format( 'Hit %s with %d points of %s damage. New hp: %d', template.id, damage, damageType, health ), 'BodyPart' ); + self.bleeding = false + self.bloodLoss = 0 +end - -- Bleeding only affects entry nodes (since they are visible to the player). - if self:isEntryNode() then - bleed( damage, damageType ); - end +--- +-- Hits the body part and applies bleeding effects. +-- @tparam number damage The amount of damage to apply. +-- @tparam string damageType The type of damage to apply. +-- +function BodyPart:hit( damage, damageType ) + -- Transform explosive damage to one of the other three damage types. + if damageType == DAMAGE_TYPES.EXPLOSIVE then + damageType = RND_DAMAGE_PICKER[love.math.random( #RND_DAMAGE_PICKER )] end - function self:destroy() - health = 0; - end + self.curHealth = self.curHealth - damage + Log.debug( string.format( 'Hit %s with %d points of %s damage. New hp: %d', self.id, damage, damageType, self.curHealth ), 'BodyPart' ) - function self:serialize() - local t = { - ['id'] = template.id, - ['health'] = health, - ['maxHealth'] = maxHealth, - ['bleeding'] = bleeding, - ['bloodLoss'] = bloodLoss - }; - return t; + -- Bleeding only affects entry nodes (since they are visible to the player). + if self:isEntryNode() then + bleed( self, damage, damageType ) end +end - function self:load( savedBodyPart ) - health = savedBodyPart.health; - maxHealth = savedBodyPart.maxHealth; - bleeding = savedBodyPart.bleeding; - bloodLoss = savedBodyPart.bloodLoss; - end +--- +-- Destroys the body part. +-- +function BodyPart:destroy() + self.curHealth = 0 +end - function self:getIndex() - return index; - end +--- +-- Serializes the body part. +-- +function BodyPart:serialize() + local t = { + ['id'] = self.id, + ['health'] = self.curHealth, + ['maxHealth'] = self.maxHealth, + ['bleeding'] = self.bleeding, + ['bloodLoss'] = self.bloodLoss + } + return t +end - function self:getID() - return template.id; - end +--- +-- Restores a saved body part. +-- @tparam table savedBodyPart A table containing the saved values. +-- +function BodyPart:load( savedBodyPart ) + self.curHealth = savedBodyPart.health + self.maxHealth = savedBodyPart.maxHealth + self.bleeding = savedBodyPart.bleeding + self.bloodLoss = savedBodyPart.bloodLoss +end - function self:getEffects() - return template.effects; - end +-- ------------------------------------------------ +-- Getters +-- ------------------------------------------------ - function self:isDestroyed() - return health <= 0; - end +--- +-- Returns the body part's current health. +-- @treturn number The current health. +-- +function BodyPart:getCurrentHealth() + return self.curHealth +end - function self:isEntryNode() - return template.type == 'entry'; - end +--- +-- Returns the body part's maximum health. +-- @treturn number The maximum health. +-- +function BodyPart:getMaximumHealth() + return self.maxHealth +end - function self:isContainer() - return template.type == 'container'; - end +--- +-- Returns the amount of blood loss currently active on this body part. +-- @treturn number The amount of blood loss. +-- +function BodyPart:getBloodLoss() + return self.bloodLoss +end - function self:getHealth() - return health; - end +--- +-- Returns the body part's index. +-- @treturn number The body part's index. +-- +function BodyPart:getIndex() + return self.index +end - function self:getMaxHealth() - return maxHealth; - end +--- +-- Returns the body part's id. +-- @treturn string The body part's id. +-- +function BodyPart:getID() + return self.id +end - function self:setHealth( nhealth ) - health = nhealth; - end +--- +-- Returns the body part's status effects. +-- @treturn StatusEffects The body part's status effects. +-- +function BodyPart:getEffects() + return self.effects +end - function self:isBleeding() - return bleeding; - end +--- +-- Checks if the the body part is destroyed. +-- @treturn boolean Returns true if the body part is destroyed. +-- +function BodyPart:isDestroyed() + return self.curHealth <= 0 +end - function self:getBloodLoss() - return bloodLoss; - end +--- +-- Checks if the body part is an entry node. Entry nodes are used to model +-- external body parts. +-- @treturn boolean True if the body part is an entry node. +-- +function BodyPart:isEntryNode() + return self.type == 'entry' +end - function self:heal() - bleeding = false - bloodLoss = 0 - health = maxHealth - end +--- +-- Checks if the body part is a container. Container nodes can contain other +-- body parts. +-- @treturn boolean True if the body part is a container node. +-- +function BodyPart:isContainer() + return self.type == 'container' +end - return self; +--- +-- Checks if this body part is bleeding. +-- @treturn boolean True if the body part is bleeding. +-- +function BodyPart:isBleeding() + return self.bleeding end -return BodyPart; +return BodyPart diff --git a/src/ui/screens/HealthScreen.lua b/src/ui/screens/HealthScreen.lua index 40b271c8..6043017e 100644 --- a/src/ui/screens/HealthScreen.lua +++ b/src/ui/screens/HealthScreen.lua @@ -88,13 +88,13 @@ function HealthScreen.new() if bodyPart:isDestroyed() then TexturePacks.setColor( 'ui_health_destroyed_limb' ) status = 'DED' - elseif bodyPart:getHealth() / bodyPart:getMaxHealth() < 0.2 then + elseif bodyPart:getCurrentHealth() / bodyPart:getMaximumHealth() < 0.2 then TexturePacks.setColor( 'ui_health_badly_damaged_limb' ) status = 'OUCH' - elseif bodyPart:getHealth() / bodyPart:getMaxHealth() < 0.4 then + elseif bodyPart:getCurrentHealth() / bodyPart:getMaximumHealth() < 0.4 then TexturePacks.setColor( 'ui_health_damaged_limb' ) status = 'MEH' - elseif bodyPart:getHealth() / bodyPart:getMaxHealth() < 0.7 then + elseif bodyPart:getCurrentHealth() / bodyPart:getMaximumHealth() < 0.7 then TexturePacks.setColor( 'ui_health_ok_limb' ) status = 'OK' else @@ -110,9 +110,9 @@ function HealthScreen.new() TexturePacks.setColor( 'ui_health_bleeding_fine' ) elseif bodyPart:getBloodLoss() / 1.0 < 0.4 then TexturePacks.setColor( 'ui_health_bleeding_ok' ) - elseif bodyPart:getHealth() / bodyPart:getMaxHealth() < 0.7 then + elseif bodyPart:getCurrentHealth() / bodyPart:getMaximumHealth() < 0.7 then TexturePacks.setColor( 'ui_health_bleeding' ) - elseif bodyPart:getHealth() / bodyPart:getMaxHealth() < 1.0 then + elseif bodyPart:getCurrentHealth() / bodyPart:getMaximumHealth() < 1.0 then TexturePacks.setColor( 'ui_health_bleeding_bad' ) end love.graphics.printf( str, (x+1) * tw, (y+counter) * th, ( UI_GRID_WIDTH - 2 ) * tw, 'center' ) From 10b88732f169292df6d0923d8eebd1d2d94e4aa7 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 30 Nov 2017 02:46:21 +0100 Subject: [PATCH 015/178] Log translator warnings only once --- src/util/Translator.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/util/Translator.lua b/src/util/Translator.lua index 9f6939e6..a838921e 100644 --- a/src/util/Translator.lua +++ b/src/util/Translator.lua @@ -115,9 +115,11 @@ function Translator.getText( id ) -- If the id also doesn't exist in the default locale return an error string. if not locales[defaultLocale][id] then Log.warn( string.format( MISSING_ID_ERROR, id ), 'Translator' ); + locales[locale][id] = string.format( MISSING_ID_ERROR, id ) return string.format( MISSING_ID_ERROR, id ); end + locales[locale][id] = locales[defaultLocale][id] return locales[defaultLocale][id]; end From 8ee1a2c32472570f2a715b602fab64ae343be536 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 30 Nov 2017 23:11:09 +0100 Subject: [PATCH 016/178] Fix badly placed windows in house prefab --- res/data/procgen/prefabs/house_small.prefab | Bin 3130 -> 3138 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/res/data/procgen/prefabs/house_small.prefab b/res/data/procgen/prefabs/house_small.prefab index 43923ed5796567c8b0b1c6149dc6e6c4d06a5fcb..19bca42c8ecfaa02ea81e41bce6ffc4a4059cb1f 100644 GIT binary patch delta 163 zcmdlbaY%x3@R8b z%frtr$jQ&ZD9A6x!U0qx$X~;x&EuxPZ_6mcUdPB^2^0mI!RE-ozg$q4KbDh&zX53E zKSstQf=v2qLOStkNjmY-MrH==3_^xLYy`x{?2|9BYcMX|{FD6;Go#7mZ`?NQ#kq+& HIZCwv9-t$x delta 150 zcmX>ku}gw+#zw|S_Cyu71_quB0v!Js`42J(@q85EcVv*~PhjHUZ(`tJsb=995L97j zV&rdN(&pC} Date: Fri, 1 Dec 2017 00:38:22 +0100 Subject: [PATCH 017/178] Refactor ParcelGrid --- src/map/procedural/ParcelGrid.lua | 142 ++++++++++-------- src/map/procedural/ProceduralMapGenerator.lua | 5 +- 2 files changed, 80 insertions(+), 67 deletions(-) diff --git a/src/map/procedural/ParcelGrid.lua b/src/map/procedural/ParcelGrid.lua index 3a060a11..3fe0d8ed 100644 --- a/src/map/procedural/ParcelGrid.lua +++ b/src/map/procedural/ParcelGrid.lua @@ -6,14 +6,14 @@ -- Required Modules -- ------------------------------------------------ -local Object = require( 'src.Object' ) +local Class = require( 'lib.Middleclass' ) local Parcel = require( 'src.map.procedural.Parcel' ) -- ------------------------------------------------ -- Module -- ------------------------------------------------ -local ParcelGrid = {} +local ParcelGrid = Class( 'ParcelGrid' ) -- ------------------------------------------------ -- Constants @@ -21,84 +21,98 @@ local ParcelGrid = {} local DIRECTION = require( 'src.constants.DIRECTION' ) - -- ------------------------------------------------ --- Constructor +-- Private Methods -- ------------------------------------------------ -function ParcelGrid.new() - local self = Object.new():addInstance( 'ParcelGrid' ) - - local parcels - - local function createGrid( w, h ) - local nparcels = {} - for x = 0, w-1 do - for y = 0, h-1 do - nparcels[x] = nparcels[x] or {} - nparcels[x][y] = Parcel.new( 'empty' ) - end +--- +-- Creates an empty parcel grid. +-- @tparam number w The parcel grid's width. +-- @tparam number h The parcel grid's height. +-- @tparam table The new parcel grid. +-- +local function createGrid( w, h ) + local parcels = {} + for x = 0, w-1 do + for y = 0, h-1 do + parcels[x] = parcels[x] or {} + parcels[x][y] = Parcel.new( 'empty' ) end - return nparcels end + return parcels +end - function self:init( w, h ) - parcels = createGrid( w, h ) - end +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ + +--- +-- Initializes the ParcelGrid. +-- @tparam number w The parcel grid's width. +-- @tparam number h The parcel grid's height. +-- +function ParcelGrid:initialize( w, h ) + self.parcels = createGrid( w, h ) +end - function self:addParcels( x, y, w, h, type ) - for px = 0, w-1 do - for py = 0, h-1 do - parcels[px+x][py+y] = Parcel.new( type ) - end +--- +-- Adds a prefab to the ParcelGrid. +-- @tparam number x The prefab's position on the grid along the x-axis. +-- @tparam number y The prefab's position on the grid along the y-axis. +-- @tparam number w The prefab's width. +-- @tparam number h The prefab's height. +-- @tparam string type The prefab's type. +-- +function ParcelGrid:addPrefab( x, y, w, h, type ) + for px = 0, w-1 do + for py = 0, h-1 do + self.parcels[px+x][py+y] = Parcel.new( type ) end end +end - --- - -- Gives each parcel in the grid a reference to its neighbours. - -- - function self:createNeighbours() - for x = 0, #parcels do - for y = 0, #parcels[x] do - local neighbours = {} - - neighbours[DIRECTION.NORTH] = self:getParcelAt( x , y - 1 ) or false - neighbours[DIRECTION.SOUTH] = self:getParcelAt( x , y + 1 ) or false - neighbours[DIRECTION.NORTH_EAST] = self:getParcelAt( x + 1, y - 1 ) or false - neighbours[DIRECTION.NORTH_WEST] = self:getParcelAt( x - 1, y - 1 ) or false - neighbours[DIRECTION.SOUTH_EAST] = self:getParcelAt( x + 1, y + 1 ) or false - neighbours[DIRECTION.SOUTH_WEST] = self:getParcelAt( x - 1, y + 1 ) or false - neighbours[DIRECTION.EAST] = self:getParcelAt( x + 1, y ) or false - neighbours[DIRECTION.WEST] = self:getParcelAt( x - 1, y ) or false - - parcels[x][y]:setNeighbours( neighbours ) - end +--- +-- Gives each parcel in the grid a reference to its neighbours. +-- +function ParcelGrid:createNeighbours() + for x = 0, #self.parcels do + for y = 0, #self.parcels[x] do + local neighbours = {} + + neighbours[DIRECTION.NORTH] = self:getParcelAt( x , y - 1 ) or false + neighbours[DIRECTION.SOUTH] = self:getParcelAt( x , y + 1 ) or false + neighbours[DIRECTION.NORTH_EAST] = self:getParcelAt( x + 1, y - 1 ) or false + neighbours[DIRECTION.NORTH_WEST] = self:getParcelAt( x - 1, y - 1 ) or false + neighbours[DIRECTION.SOUTH_EAST] = self:getParcelAt( x + 1, y + 1 ) or false + neighbours[DIRECTION.SOUTH_WEST] = self:getParcelAt( x - 1, y + 1 ) or false + neighbours[DIRECTION.EAST] = self:getParcelAt( x + 1, y ) or false + neighbours[DIRECTION.WEST] = self:getParcelAt( x - 1, y ) or false + + self.parcels[x][y]:setNeighbours( neighbours ) end end +end - --- - -- Returns the Parcel at the given coordinates. - -- @tparam number x The position along the x-axis. - -- @tparam number y The position along the y-axis. - -- @treturn Parcel The Parcel at the given position. - -- - function self:getParcelAt( x, y ) - return parcels[x] and parcels[x][y] - end +--- +-- Returns the Parcel at the given coordinates. +-- @tparam number x The position along the x-axis. +-- @tparam number y The position along the y-axis. +-- @treturn Parcel The Parcel at the given position. +-- +function ParcelGrid:getParcelAt( x, y ) + return self.parcels[x] and self.parcels[x][y] +end - --- - -- Iterates over all tiles and performs the callback function on them. - -- @param callback (function) The operation to perform on each tile. - -- - function self:iterate( callback ) - for x = 0, #parcels do - for y = 0, #parcels[x] do - callback( parcels[x][y], x, y ) - end +--- +-- Iterates over all tiles and performs the callback function on them. +-- @param callback (function) The operation to perform on each tile. +-- +function ParcelGrid:iterate( callback ) + for x = 0, #self.parcels do + for y = 0, #self.parcels[x] do + callback( self.parcels[x][y], x, y ) end end - - return self end return ParcelGrid diff --git a/src/map/procedural/ProceduralMapGenerator.lua b/src/map/procedural/ProceduralMapGenerator.lua index 2a4cda0c..11369ffe 100644 --- a/src/map/procedural/ProceduralMapGenerator.lua +++ b/src/map/procedural/ProceduralMapGenerator.lua @@ -172,7 +172,7 @@ function ProceduralMapGenerator.new() for _, definition in ipairs( definitions ) do -- Start coordinates at 0,0. local x, y, w, h = definition.x-1, definition.y-1, definition.w, definition.h - parcelGrid:addParcels( x, y, w, h, type ) + parcelGrid:addPrefab( x, y, w, h, type ) local prefab = PrefabLoader.getPrefab( type ) if prefab then @@ -284,8 +284,7 @@ function ProceduralMapGenerator.new() local layout = nlayout or layouts[love.math.random( #layouts )] -- Generate empty parcel grid. - parcelGrid = ParcelGrid.new() - parcelGrid:init( layout.mapwidth, layout.mapheight ) + parcelGrid = ParcelGrid( layout.mapwidth, layout.mapheight ) -- Generate empty tile grid. tileGrid, width, height = createTileGrid( layout.mapwidth, layout.mapheight ) From 162064eedcca3caf9e9ef934debd9662c6a3149e Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Fri, 1 Dec 2017 11:16:54 +0100 Subject: [PATCH 018/178] Refactor Parcel --- src/map/procedural/Parcel.lua | 64 ++++++++++++------- src/map/procedural/ParcelGrid.lua | 4 +- src/map/procedural/ProceduralMapGenerator.lua | 2 +- 3 files changed, 45 insertions(+), 25 deletions(-) diff --git a/src/map/procedural/Parcel.lua b/src/map/procedural/Parcel.lua index b48b764c..47c22252 100644 --- a/src/map/procedural/Parcel.lua +++ b/src/map/procedural/Parcel.lua @@ -8,42 +8,62 @@ -- Required Modules -- ------------------------------------------------ -local Object = require( 'src.Object' ) +local Class = require( 'lib.Middleclass' ) -- ------------------------------------------------ -- Module -- ------------------------------------------------ -local Parcel = {} +local Parcel = Class( 'Parcel' ) -- ------------------------------------------------ --- Constructor +-- Public Methods -- ------------------------------------------------ -function Parcel.new( type ) - local self = Object.new():addInstance( 'Parcel' ) - - local neighbours +--- +-- Initializes the Parcel instance. +-- @tparam string type The type of Parcel to spawn. +-- +function Parcel:initialize( type ) + self.type = type +end - function self:setNeighbours( nneighbours ) - neighbours = nneighbours +--- +-- Counts the neighbours of the same type around this parcel. +-- @treturn number The number of neighbours with the same type. +-- +function Parcel:countNeighbours() + local count = 0 + for _, neighbour in pairs( self.neighbours ) do + if neighbour and neighbour:getType() == self.type then + count = count + 1 + end end + return count +end - function self:getType() - return type - end +-- ------------------------------------------------ +-- Getters +-- ------------------------------------------------ - function self:getNeighbourCount() - local count = 0 - for _, neighbour in pairs( neighbours ) do - if neighbour and neighbour:getType() == type then - count = count + 1 - end - end - return count - end +--- +-- Returns this parcel's type. +-- @treturn string The parcel's type. +-- +function Parcel:getType() + return self.type +end - return self +-- ------------------------------------------------ +-- Setters +-- ------------------------------------------------ + +--- +-- Sets the neighbours for this parcel. +-- @tparam table neighbours The neighbouring parcels. +-- +function Parcel:setNeighbours( neighbours ) + self.neighbours = neighbours end return Parcel diff --git a/src/map/procedural/ParcelGrid.lua b/src/map/procedural/ParcelGrid.lua index 3fe0d8ed..70ff137d 100644 --- a/src/map/procedural/ParcelGrid.lua +++ b/src/map/procedural/ParcelGrid.lua @@ -36,7 +36,7 @@ local function createGrid( w, h ) for x = 0, w-1 do for y = 0, h-1 do parcels[x] = parcels[x] or {} - parcels[x][y] = Parcel.new( 'empty' ) + parcels[x][y] = Parcel( 'empty' ) end end return parcels @@ -66,7 +66,7 @@ end function ParcelGrid:addPrefab( x, y, w, h, type ) for px = 0, w-1 do for py = 0, h-1 do - self.parcels[px+x][py+y] = Parcel.new( type ) + self.parcels[px+x][py+y] = Parcel( type ) end end end diff --git a/src/map/procedural/ProceduralMapGenerator.lua b/src/map/procedural/ProceduralMapGenerator.lua index 11369ffe..ce43807a 100644 --- a/src/map/procedural/ProceduralMapGenerator.lua +++ b/src/map/procedural/ProceduralMapGenerator.lua @@ -194,7 +194,7 @@ function ProceduralMapGenerator.new() return end - local n = parcel:getNeighbourCount() + local n = parcel:countNeighbours() local tx, ty = x * PARCEL_SIZE.WIDTH, y * PARCEL_SIZE.HEIGHT for w = 1, PARCEL_SIZE.WIDTH do From 76905593a64bf03edd7f87b8903e4b34f1adc20c Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Fri, 1 Dec 2017 11:18:45 +0100 Subject: [PATCH 019/178] Change parcel's type instead of creating a new one --- src/map/procedural/Parcel.lua | 8 ++++++++ src/map/procedural/ParcelGrid.lua | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/map/procedural/Parcel.lua b/src/map/procedural/Parcel.lua index 47c22252..66a31939 100644 --- a/src/map/procedural/Parcel.lua +++ b/src/map/procedural/Parcel.lua @@ -66,4 +66,12 @@ function Parcel:setNeighbours( neighbours ) self.neighbours = neighbours end +--- +-- Sets the type of this parcel. +-- @tparam string type The new type to use. +-- +function Parcel:setType( type ) + self.type = type +end + return Parcel diff --git a/src/map/procedural/ParcelGrid.lua b/src/map/procedural/ParcelGrid.lua index 70ff137d..cdda75b5 100644 --- a/src/map/procedural/ParcelGrid.lua +++ b/src/map/procedural/ParcelGrid.lua @@ -66,7 +66,7 @@ end function ParcelGrid:addPrefab( x, y, w, h, type ) for px = 0, w-1 do for py = 0, h-1 do - self.parcels[px+x][py+y] = Parcel( type ) + self.parcels[px+x][py+y]:setType( type ) end end end From 9fc458b8f1a6273e61eb37ae45bfd508cd2ea431 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Fri, 1 Dec 2017 11:06:54 +0100 Subject: [PATCH 020/178] Refactor ProceduralMapGenerator --- src/CombatState.lua | 3 +- src/map/procedural/ProceduralMapGenerator.lua | 407 +++++++++--------- src/ui/screens/MapTest.lua | 3 +- 3 files changed, 205 insertions(+), 208 deletions(-) diff --git a/src/CombatState.lua b/src/CombatState.lua index b92650b0..3c119817 100644 --- a/src/CombatState.lua +++ b/src/CombatState.lua @@ -61,8 +61,7 @@ function CombatState.new() end local function createMap() - local generator = ProceduralMapGenerator.new() - generator:init() + local generator = ProceduralMapGenerator() local tiles = generator:getTiles() local mw, mh = generator:getTileGridDimensions() diff --git a/src/map/procedural/ProceduralMapGenerator.lua b/src/map/procedural/ProceduralMapGenerator.lua index ce43807a..0ee4cbc0 100644 --- a/src/map/procedural/ProceduralMapGenerator.lua +++ b/src/map/procedural/ProceduralMapGenerator.lua @@ -7,6 +7,7 @@ -- Required Modules -- ------------------------------------------------ +local Class = require( 'lib.Middleclass' ) local Log = require( 'src.util.Log' ) local ArrayRotation = require( 'src.util.ArrayRotation' ) local PrefabLoader = require( 'src.map.procedural.PrefabLoader' ) @@ -20,7 +21,7 @@ local Compressor = require( 'src.util.Compressor' ) -- Module -- ------------------------------------------------ -local ProceduralMapGenerator = {} +local ProceduralMapGenerator = Class( 'ProceduralMapGenerator' ) -- ------------------------------------------------ -- Constants @@ -61,256 +62,254 @@ local function loadLayoutTemplates( sourceFolder ) end end --- ------------------------------------------------ --- Public Functions --- ------------------------------------------------ +--- +-- 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 number rotate The rotation to apply to the prefab before placing it. +-- +local function placePrefab( tileGrid, prefab, px, py, rotate ) + local tiles = prefab.grid -function ProceduralMapGenerator.load() - Log.print( 'Loading vanilla layouts:', 'ProceduralMapGenerator' ) - loadLayoutTemplates( LAYOUTS_SOURCE_FOLDER ) + if rotate then + tiles = ArrayRotation.rotate( tiles, rotate ) + end - Log.print( 'Loading external layouts:', 'ProceduralMapGenerator' ) - loadLayoutTemplates( LAYOUTS_MODS_FOLDER ) -end + for tx = 1, #tiles do + for ty = 1, #tiles[tx] do + if tiles[tx][ty].tile then + tileGrid[tx + px][ty + py] = TileFactory.create( tx + px, ty + py, tiles[tx][ty].tile ) + end --- ------------------------------------------------ --- Constructor --- ------------------------------------------------ + if tiles[tx][ty].worldObject then + tileGrid[tx + px][ty + py]:addWorldObject( WorldObjectFactory.create( tiles[tx][ty].worldObject )) + end + end + end +end -function ProceduralMapGenerator.new() - local self = {} +--- +-- 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 - -- ------------------------------------------------ - -- Private Attributes - -- ------------------------------------------------ + -- 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 - -- The actual tile map. - local tileGrid - local width, height +--- +-- 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 ParcelGrid parcelGrid The parcel grid to fill. +-- @tparam table parcels The parcel definitions. +-- +local function fillParcels( tileGrid, parcelGrid, parcels ) + for type, definitions in pairs( parcels ) do + Log.debug( string.format( 'Placing %s parcels.', type ), 'ProceduralMapGenerator' ) - -- The parcel layout. - local parcelGrid + for _, definition in ipairs( definitions ) do + -- Start coordinates at 0,0. + local x, y, w, h = definition.x-1, definition.y-1, definition.w, definition.h + parcelGrid:addPrefab( x, y, w, h, type ) - -- Spawnpoints. - local spawnpoints = { - allied = {}, - neutral = {}, - enemy = {} - } + local prefab = PrefabLoader.getPrefab( type ) + if prefab then + local rotation = rotateParcels( definition.w, definition.h ) - -- ------------------------------------------------ - -- Private Methods - -- ------------------------------------------------ - - --- - -- Creates a Tile 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 tile The tile's id. - -- @treturn Tile The new Tile object. - -- - local function createTile( x, y, tile ) - return TileFactory.create( x, y, tile ) + -- Place tiles and worldobjects. + placePrefab( tileGrid, prefab, x * PARCEL_SIZE.WIDTH, y * PARCEL_SIZE.HEIGHT, rotation ) + end + end end +end - --- - -- Places the tiles and world objects belonging to this prefab. - -- @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( prefab, px, py, rotate ) - local tiles = prefab.grid - - if rotate then - tiles = ArrayRotation.rotate( tiles, rotate ) +--- +-- Spawns trees in designated parcels. +-- @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 ) + parcelGrid:iterate( function( parcel, x, y ) + if parcel:getType() ~= 'FOLIAGE' then + return end - for tx = 1, #tiles do - for ty = 1, #tiles[tx] do - if tiles[tx][ty].tile then - tileGrid[tx + px][ty + py] = createTile( tx + px, ty + py, tiles[tx][ty].tile ) - end + 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 - if tiles[tx][ty].worldObject then - tileGrid[tx + px][ty + py]:addWorldObject( WorldObjectFactory.create( tiles[tx][ty].worldObject )) + -- 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' )) end end end - end + 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 ) +--- +-- Spawns roads in designated parcels. +-- TODO Replace with prefab based system. +-- @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 ) + parcelGrid:iterate( function( parcel, x, y ) + if parcel:getType() ~= 'ROAD' then + return 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 + 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' ) + end end - end + end) +end - --- - -- Iterates over the parcel definitions for this map layout and tries to - -- place prefabs for each of them. - -- @tparam table parcels The parcel definitions. - -- - local function fillParcels( parcels ) - for type, definitions in pairs( parcels ) do - Log.debug( string.format( 'Placing %s parcels.', type ), 'ProceduralMapGenerator' ) - - for _, definition in ipairs( definitions ) do - -- Start coordinates at 0,0. - local x, y, w, h = definition.x-1, definition.y-1, definition.w, definition.h - parcelGrid:addPrefab( x, y, w, h, type ) - - local prefab = PrefabLoader.getPrefab( type ) - if prefab then - local rotation = rotateParcels( definition.w, definition.h ) - - -- Place tiles and worldobjects. - placePrefab( prefab, x * PARCEL_SIZE.WIDTH, y * PARCEL_SIZE.HEIGHT, rotation ) - end - 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. +-- @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 + tiles[x] = {} + for y = 1, h * PARCEL_SIZE.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 ) end end + return tiles, w * PARCEL_SIZE.WIDTH, h * PARCEL_SIZE.HEIGHT +end - --- - -- Spawns trees in designated parcels. - -- - local function spawnFoliage() - parcelGrid:iterate( function( parcel, x, y ) - if parcel:getType() ~= 'FOLIAGE' then - return - end - - 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 - - -- 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' )) - end - end - end - end) - end +--- +-- Create spawnpoints. +-- TODO Proper implementation. +-- @tparam table spawnpoints The table filled with the spawnpoint definitions. +-- @tparam table spawns The table containing the spawn definitions. +-- +local function createSpawnPoints( spawnpoints, spawns ) + for type, definitions in pairs( spawns ) do + + local target + if type == 'SPAWNS_FRIENDLY' then + target = 'allied' + elseif type == 'SPAWNS_NEUTRAL' then + target = 'neutral' + elseif type == 'SPAWNS_ENEMY' then + target = 'enemy' + end - --- - -- Spawns roads in designated parcels. - -- TODO Replace with prefab based system. - -- - local function spawnRoads() - parcelGrid:iterate( function( parcel, x, y ) - if parcel:getType() ~= 'ROAD' then - return - end + for _, definition in ipairs( definitions ) do + local x, y = (definition.x-1) * PARCEL_SIZE.WIDTH, (definition.y-1) * PARCEL_SIZE.HEIGHT - 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] = createTile( tx + w, ty + h, 'tile_asphalt' ) + spawnpoints[target][#spawnpoints[target] + 1] = { x = x+w, y = y+h } end end - end) - 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 table The new tile grid. - -- - local function createTileGrid( w, h ) - local tiles = {} - for x = 1, w * PARCEL_SIZE.WIDTH do - tiles[x] = {} - for y = 1, h * PARCEL_SIZE.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] = createTile( x, y, id ) - end end - return tiles, w * PARCEL_SIZE.WIDTH, h * PARCEL_SIZE.HEIGHT end +end - --- - -- Create spawnpoints. - -- TODO Proper implementation. - -- - local function createSpawnPoints( spawns ) - for type, definitions in pairs( spawns ) do - - local target - if type == 'SPAWNS_FRIENDLY' then - target = 'allied' - elseif type == 'SPAWNS_NEUTRAL' then - target = 'neutral' - elseif type == 'SPAWNS_ENEMY' then - target = 'enemy' - end +-- ------------------------------------------------ +-- Static Functions +-- ------------------------------------------------ - for _, definition in ipairs( definitions ) do - local x, y = (definition.x-1) * PARCEL_SIZE.WIDTH, (definition.y-1) * PARCEL_SIZE.HEIGHT +--- +-- Loads the map layouts from the game's source files and the mod directory. +-- +function ProceduralMapGenerator.static:load() + Log.print( 'Loading vanilla layouts:', 'ProceduralMapGenerator' ) + loadLayoutTemplates( LAYOUTS_SOURCE_FOLDER ) - for w = 1, PARCEL_SIZE.WIDTH do - for h = 1, PARCEL_SIZE.HEIGHT do - spawnpoints[target][#spawnpoints[target] + 1] = { x = x+w, y = y+h } - end - end - end - end - end + Log.print( 'Loading external layouts:', 'ProceduralMapGenerator' ) + loadLayoutTemplates( LAYOUTS_MODS_FOLDER ) +end - -- ------------------------------------------------ - -- Public Methods - -- ------------------------------------------------ +-- ------------------------------------------------ +-- Constructor +-- ------------------------------------------------ - function self:init( nlayout ) - -- Use specific layout or select a random one. - local layout = nlayout or layouts[love.math.random( #layouts )] +--- +-- Initializes the ProceduralMapGenerator instance. +-- @tparam table layout The layout definition to use for map creation (optional). +-- +function ProceduralMapGenerator:initialize( layout ) + -- Use specific layout or select a random one. + self.layout = layout or layouts[love.math.random( #layouts )] - -- Generate empty parcel grid. - parcelGrid = ParcelGrid( layout.mapwidth, layout.mapheight ) + -- Generate empty parcel grid. + self.parcelGrid = ParcelGrid( self.layout.mapwidth, self.layout.mapheight ) + self.parcelGrid:createNeighbours() - -- Generate empty tile grid. - tileGrid, width, height = createTileGrid( layout.mapwidth, layout.mapheight ) + -- The actual tile map. + self.tileGrid, self.width, self.height = createTileGrid( self.layout.mapwidth, self.layout.mapheight ) - fillParcels( layout.prefabs ) + -- Spawnpoints. + self.spawnpoints = { + allied = {}, + neutral = {}, + enemy = {} + } - parcelGrid:createNeighbours() + fillParcels( self.tileGrid, self.parcelGrid, self.layout.prefabs ) - spawnRoads() - spawnFoliage() - createSpawnPoints( layout.spawns ) - end + spawnRoads( self.parcelGrid, self.tileGrid ) + spawnFoliage( self.parcelGrid, self.tileGrid ) + createSpawnPoints( self.spawnpoints, self.layout.spawns ) +end - function self:getSpawnpoints() - return spawnpoints - end +-- ------------------------------------------------ +-- Getters +-- ------------------------------------------------ - function self:getTiles() - return tileGrid - end +--- +-- Returns the spawnpoints for the generated map. +-- @treturn table The spawns for this map. +-- +function ProceduralMapGenerator:getSpawnpoints() + return self.spawnpoints +end - function self:getTileGridDimensions() - return width, height - end +--- +-- Returns the tile grid of the map. +-- @treturn table The tile grid of this map. +-- +function ProceduralMapGenerator:getTiles() + return self.tileGrid +end - return self +--- +-- 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 end return ProceduralMapGenerator diff --git a/src/ui/screens/MapTest.lua b/src/ui/screens/MapTest.lua index 0b554661..9db22f80 100644 --- a/src/ui/screens/MapTest.lua +++ b/src/ui/screens/MapTest.lua @@ -26,8 +26,7 @@ function MapTest.new() local camera local function createMap() - local generator = ProceduralMapGenerator.new() - generator:init( layout ) + local generator = ProceduralMapGenerator( layout ) local tiles = generator:getTiles() mw, mh = generator:getTileGridDimensions() From 2625ef4e5fcbb07d8d27d3cd0e2e34aae40f8c5e Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Tue, 28 Nov 2017 01:26:35 +0100 Subject: [PATCH 021/178] Add connected sprites for world objects Closes #215. --- res/data/WorldObjects.lua | 9 +++- res/texturepacks/default/sprites.lua | 29 ++++++++++++- src/map/worldobjects/WorldObject.lua | 19 +++++++++ src/ui/MapPainter.lua | 64 +++++++++++++++++++++++++++- 4 files changed, 116 insertions(+), 5 deletions(-) diff --git a/res/data/WorldObjects.lua b/res/data/WorldObjects.lua index ea6963fe..08da76db 100644 --- a/res/data/WorldObjects.lua +++ b/res/data/WorldObjects.lua @@ -79,6 +79,7 @@ return { openable = true, blocksVision = true, blocksPathfinding = false, + group = "DOOR", drops = { { id = 'misc_nail', tries = 10, chance = 20 }, { id = 'misc_splintered_wood', tries = 3, chance = 40 } @@ -98,6 +99,8 @@ return { climbable = true, blocksVision = false, blocksPathfinding = false, + group = "FENCE", + connections = { "DOOR", "FENCE", "WALL" }, drops = { { id = 'misc_nail', tries = 10, chance = 20 }, { id = 'misc_splintered_wood', tries = 3, chance = 40 } @@ -117,6 +120,7 @@ return { openable = true, blocksVision = false, blocksPathfinding = false, + group = "FENCE", drops = { { id = 'misc_nail', tries = 10, chance = 20 }, { id = 'misc_splintered_wood', tries = 3, chance = 40 } @@ -165,7 +169,9 @@ return { size = 100, destructible = false, blocksVision = true, - blocksPathfinding = true + blocksPathfinding = true, + group = "WALL", + connections = { "DOOR", "WALL", "WINDOW" } }, { id = 'worldobject_window', @@ -176,6 +182,7 @@ return { debrisID = 'worldobject_lowwall', blocksVision = false, blocksPathfinding = true, + group = "WINDOW", drops = { { id = 'misc_glass_shard', tries = 6, chance = 20 } } diff --git a/res/texturepacks/default/sprites.lua b/res/texturepacks/default/sprites.lua index 3b850c3c..b7a24b41 100644 --- a/res/texturepacks/default/sprites.lua +++ b/res/texturepacks/default/sprites.lua @@ -18,15 +18,40 @@ return { -- ============================== ['worldobject_chair'] = 111, ['worldobject_crate'] = 247, - ['worldobject_fence'] = 62, ['worldobject_lowwall'] = 62, ['worldobject_table'] = 211, ['worldobject_tree'] = 7, - ['worldobject_wall'] = 36, ['worldobject_window'] = 177, ['worldobject_toilet'] = 226, ['worldobject_shower'] = 127, + -- Connected world objects. + ['worldobject_wall'] = 207, + ['worldobject_wall_vertical'] = 187, + ['worldobject_wall_horizontal'] = 206, + ['worldobject_wall_ne'] = 201, + ['worldobject_wall_nw'] = 189, + ['worldobject_wall_se'] = 202, + ['worldobject_wall_sw'] = 188, + ['worldobject_wall_nes'] = 205, + ['worldobject_wall_new'] = 203, + ['worldobject_wall_nws'] = 186, + ['worldobject_wall_sew'] = 204, + ['worldobject_wall_news'] = 207, + + ['worldobject_fence'] = 198, + ['worldobject_fence_vertical'] = 180, + ['worldobject_fence_horizontal'] = 197, + ['worldobject_fence_ne'] = 193, + ['worldobject_fence_nw'] = 218, + ['worldobject_fence_se'] = 219, + ['worldobject_fence_sw'] = 192, + ['worldobject_fence_nes'] = 196, + ['worldobject_fence_new'] = 194, + ['worldobject_fence_nws'] = 181, + ['worldobject_fence_sew'] = 195, + ['worldobject_fence_news'] = 198, + -- ============================== -- Items -- ============================== diff --git a/src/map/worldobjects/WorldObject.lua b/src/map/worldobjects/WorldObject.lua index d12257c4..54cc8fdf 100644 --- a/src/map/worldobjects/WorldObject.lua +++ b/src/map/worldobjects/WorldObject.lua @@ -35,6 +35,9 @@ function WorldObject.new( template ) local container = template.container; local drops = template.drops; + local group = template.group + local connections = template.connections + local hp = template.hp; local passable = template.passable or false; local blocksVision = template.blocksVision; @@ -173,6 +176,22 @@ function WorldObject.new( template ) return destructible and hp <= 0 or false; end + --- + -- Gets the WorldObject's group. + -- @treturn string The group. + -- + function self:getGroup() + return group + end + + --- + -- Gets the WorldObject's connections. + -- @treturn table The connections. + -- + function self:getConnections() + return connections + end + --- -- Checks wether the WorldObject is destructible. -- @return (boolean) True if the WorldObject is destructible. diff --git a/src/ui/MapPainter.lua b/src/ui/MapPainter.lua index 36c2e2a5..1e073caa 100644 --- a/src/ui/MapPainter.lua +++ b/src/ui/MapPainter.lua @@ -28,6 +28,8 @@ local MapPainter = Class( 'MapPainter' ) local MAX_SPRITES = 16384 -- Enough sprites for a 128*128 map. +local DIRECTION = require( 'src.constants.DIRECTION' ) + local FACTIONS = require( 'src.constants.FACTIONS' ) local COLORS = { [FACTIONS.ALLIED] = { @@ -44,6 +46,30 @@ local COLORS = { }, } +local CONNECTION_BITMASK = { + [0] = '', + + [1] = '_vertical', + [4] = '_vertical', + [2] = '_horizontal', + [8] = '_horizontal', + + [5] = '_vertical', -- North South + [10] = '_horizontal', -- East West + + [3] = '_ne', + [9] = '_nw', + [6] = '_se', + [12] = '_sw', + + [7] = '_nes', + [11] = '_new', + [13] = '_nws', + [14] = '_sew', + + [15] = '_news', +} + -- ------------------------------------------------ -- Private Methods -- ------------------------------------------------ @@ -110,12 +136,34 @@ local function selectCharacterTile( tile ) return TexturePacks.getSprite( character:getBody():getID(), character:getStance() ) end +--- +-- Checks wether there is an adjacent world object with a group that matches the +-- connections of the original world object. +-- @tparam table connections The connections table containing the groups the world object connects to. +-- @tparam Tile neighbour The neighbouring tile to check for a matching world object. +-- @tparam number value The value to return in case the world object matches. +-- @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:getWorldObject():getGroup() then + for i = 1, #connections do + if connections[i] == group then + return value + end + end + end + end + return 0 +end + --- -- Selects the tile 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 ) +local function selectWorldObjectSprite( worldObject, tile ) if worldObject:isOpenable() then if worldObject:isPassable() then return TexturePacks.getSprite( worldObject:getID(), 'open' ) @@ -123,6 +171,18 @@ local function selectWorldObjectSprite( worldObject ) return TexturePacks.getSprite( worldObject:getID(), 'closed' ) end end + + -- 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] ) + end + return TexturePacks.getSprite( worldObject:getID() ) end @@ -142,7 +202,7 @@ local function selectTileSprite( tile, faction ) end if tile:hasWorldObject() then - return selectWorldObjectSprite( tile:getWorldObject() ) + return selectWorldObjectSprite( tile:getWorldObject(), tile ) end return TexturePacks.getSprite( tile:getID() ) From 34186b4b78a2b32a39c72b909262e4de9f24d527 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sun, 3 Dec 2017 15:56:20 +0100 Subject: [PATCH 022/178] Fix connected tiles in PrefabEditor --- src/ui/mapeditor/PrefabCanvas.lua | 124 ++++++++++++++++++++++-------- 1 file changed, 92 insertions(+), 32 deletions(-) diff --git a/src/ui/mapeditor/PrefabCanvas.lua b/src/ui/mapeditor/PrefabCanvas.lua index d9584c7a..94c19a1c 100644 --- a/src/ui/mapeditor/PrefabCanvas.lua +++ b/src/ui/mapeditor/PrefabCanvas.lua @@ -30,6 +30,30 @@ local PREFAB_SIZES = { XL = { TYPE = 'xl', WIDTH = 56, HEIGHT = 56 } -- 7x7 } +local CONNECTION_BITMASK = { + [0] = '', + + [1] = '_vertical', + [4] = '_vertical', + [2] = '_horizontal', + [8] = '_horizontal', + + [5] = '_vertical', -- North South + [10] = '_horizontal', -- East West + + [3] = '_ne', + [9] = '_nw', + [6] = '_se', + [12] = '_sw', + + [7] = '_nes', + [11] = '_new', + [13] = '_nws', + [14] = '_sew', + + [15] = '_news', +} + -- ------------------------------------------------ -- Private Methods -- ------------------------------------------------ @@ -66,42 +90,50 @@ local function drawGridLines( grid, tw, th ) end --- --- Draws the tiles and world objects in the grid. --- @tparam table grid The two-dimensional array containing the grid. --- @tparam number tw The width of each grid cell in pixels. --- @tparam number th The width of each grid cell in pixels. --- @tparam boolean hideObjectLayer Wether to hide the object layer or not. +-- Checks wether there is an adjacent world object with a group that matches the +-- connections of the original world object. +-- @tparam table connections The connections table containing the groups the world object connects to. +-- @tparam Tile neighbour The neighbouring tile to check for a matching world object. +-- @tparam number value The value to return in case the world object matches. +-- @treturn number The value indicating a match (0 if the world object doesn't match). -- -local function drawGrid( grid, tw, th, hideObjectLayer ) - for x = 1, #grid do - for y = 1, #grid[x] do - -- Draw tile layer. - if grid[x][y].tile then - local tileID = grid[x][y].tile.id - TexturePacks.setColor( tileID ) - love.graphics.draw( TexturePacks.getTileset():getSpritesheet(), TexturePacks.getSprite( tileID ), x * tw, y * tw ) - end - - - -- Draw worldobject layer. - if grid[x][y].worldObject and not hideObjectLayer then - local template = grid[x][y].worldObject - local worldObjectID = template.id - TexturePacks.setColor( 'sys_background' ) - love.graphics.rectangle( 'fill', x * tw, y * th, tw, th ) - - local sprite - if template.openable then - sprite = TexturePacks.getSprite( worldObjectID, 'closed' ) - else - sprite = TexturePacks.getSprite( worldObjectID ) +local function checkConnection( connections, neighbour, value ) + if neighbour and neighbour.worldObject then + local group = neighbour.worldObject.group + if group then + for i = 1, #connections do + if connections[i] == group then + return value end - - TexturePacks.setColor( worldObjectID ) - love.graphics.draw( TexturePacks.getTileset():getSpritesheet(), sprite, x * tw, y * th ) end end end + return 0 +end + +--- +-- Selects the tile to use for drawing a worldobject. +-- @tparam PrefabCanvas self The PrefabCanvas instance to use. +-- @tparam table template The world object template to use for drawing. +-- @tparam number x The world object's position along the x-axis. +-- @tparam number y The world object's position along the y-axis. +-- @treturn Quad A quad pointing to the sprite on the active tileset. +-- +local function selectWorldObjectSprite( self, template, x, y ) + if template.openable then + return TexturePacks.getSprite( template.id, 'closed' ) + end + + -- Check if the world object sprite connects to adjacent sprites. + if template.connections then + local result = checkConnection( template.connections, self:getTile( x, y - 1 ), 1 ) + + checkConnection( template.connections, self:getTile( x + 1, y ), 2 ) + + checkConnection( template.connections, self:getTile( x, y + 1 ), 4 ) + + checkConnection( template.connections, self:getTile( x - 1, y ), 8 ) + return TexturePacks.getSprite( template.id .. CONNECTION_BITMASK[result] ) + end + + return TexturePacks.getSprite( template.id ) end -- ------------------------------------------------ @@ -126,7 +158,31 @@ function PrefabCanvas:draw() local tw, th = TexturePacks.getTileDimensions() drawGridLines( self.grid, tw, th ) - drawGrid( self.grid, tw, th, self.hideObjects ) + + for x = 1, #self.grid do + for y = 1, #self.grid[x] do + -- Draw tile layer. + if self.grid[x][y].tile then + local tileID = self.grid[x][y].tile.id + TexturePacks.setColor( tileID ) + love.graphics.draw( TexturePacks.getTileset():getSpritesheet(), TexturePacks.getSprite( tileID ), x * tw, y * tw ) + end + + + -- Draw worldobject layer. + if self.grid[x][y].worldObject and not self.hideObjects then + local template = self.grid[x][y].worldObject + local worldObjectID = template.id + TexturePacks.setColor( 'sys_background' ) + love.graphics.rectangle( 'fill', x * tw, y * th, tw, th ) + + local sprite = selectWorldObjectSprite( self, template, x, y ) + + TexturePacks.setColor( worldObjectID ) + love.graphics.draw( TexturePacks.getTileset():getSpritesheet(), sprite, x * tw, y * th ) + end + end + end end function PrefabCanvas:setTile( x, y, type, content ) @@ -135,6 +191,10 @@ function PrefabCanvas:setTile( x, y, type, content ) end end +function PrefabCanvas:getTile( x, y ) + return self.grid[x] and self.grid[x][y] +end + function PrefabCanvas:getTileID( x, y, type ) if self.grid[x][y][type] then return self.grid[x][y][type].id From 4e72fa52156c2a5f0d8f57649ae8fbd2ca9f5407 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sun, 3 Dec 2017 17:13:21 +0100 Subject: [PATCH 023/178] Use local variable to access world object group --- 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 1e073caa..0b46a67a 100644 --- a/src/ui/MapPainter.lua +++ b/src/ui/MapPainter.lua @@ -147,7 +147,7 @@ end local function checkConnection( connections, neighbour, value ) if neighbour and neighbour:hasWorldObject() then local group = neighbour:getWorldObject():getGroup() - if neighbour:getWorldObject():getGroup() then + if group then for i = 1, #connections do if connections[i] == group then return value From 3fb9717562191bbc33981666627f62824b2c9ad0 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sun, 3 Dec 2017 17:30:09 +0100 Subject: [PATCH 024/178] Use subid for drawing connected tiles This gets rid of unnecessary string concatenations, makes the definition of connected sprites easier and fixes the travis build. --- res/texturepacks/default/sprites.lua | 52 +++++++++++++++------------- src/ui/MapPainter.lua | 47 +++++++++++++------------ src/ui/mapeditor/PrefabCanvas.lua | 47 +++++++++++++------------ src/ui/texturepacks/Tileset.lua | 16 +++++++-- 4 files changed, 92 insertions(+), 70 deletions(-) diff --git a/res/texturepacks/default/sprites.lua b/res/texturepacks/default/sprites.lua index b7a24b41..e288a0fe 100644 --- a/res/texturepacks/default/sprites.lua +++ b/res/texturepacks/default/sprites.lua @@ -26,31 +26,35 @@ return { ['worldobject_shower'] = 127, -- Connected world objects. - ['worldobject_wall'] = 207, - ['worldobject_wall_vertical'] = 187, - ['worldobject_wall_horizontal'] = 206, - ['worldobject_wall_ne'] = 201, - ['worldobject_wall_nw'] = 189, - ['worldobject_wall_se'] = 202, - ['worldobject_wall_sw'] = 188, - ['worldobject_wall_nes'] = 205, - ['worldobject_wall_new'] = 203, - ['worldobject_wall_nws'] = 186, - ['worldobject_wall_sew'] = 204, - ['worldobject_wall_news'] = 207, + ['worldobject_wall'] = { + default = 207, + vertical = 187, + horizontal = 206, + ne = 201, + nw = 189, + se = 202, + sw = 188, + nes = 205, + new = 203, + nws = 186, + sew = 204, + news = 207, + }, - ['worldobject_fence'] = 198, - ['worldobject_fence_vertical'] = 180, - ['worldobject_fence_horizontal'] = 197, - ['worldobject_fence_ne'] = 193, - ['worldobject_fence_nw'] = 218, - ['worldobject_fence_se'] = 219, - ['worldobject_fence_sw'] = 192, - ['worldobject_fence_nes'] = 196, - ['worldobject_fence_new'] = 194, - ['worldobject_fence_nws'] = 181, - ['worldobject_fence_sew'] = 195, - ['worldobject_fence_news'] = 198, + ['worldobject_fence'] = { + default = 198, + vertical = 180, + horizontal = 197, + ne = 193, + nw = 218, + se = 219, + sw = 192, + nes = 196, + new = 194, + nws = 181, + sew = 195, + news = 198, + }, -- ============================== -- Items diff --git a/src/ui/MapPainter.lua b/src/ui/MapPainter.lua index 0b46a67a..b35421a2 100644 --- a/src/ui/MapPainter.lua +++ b/src/ui/MapPainter.lua @@ -47,27 +47,30 @@ local COLORS = { } local CONNECTION_BITMASK = { - [0] = '', - - [1] = '_vertical', - [4] = '_vertical', - [2] = '_horizontal', - [8] = '_horizontal', - - [5] = '_vertical', -- North South - [10] = '_horizontal', -- East West - - [3] = '_ne', - [9] = '_nw', - [6] = '_se', - [12] = '_sw', - - [7] = '_nes', - [11] = '_new', - [13] = '_nws', - [14] = '_sew', - - [15] = '_news', + [0] = 'default', + + -- Straight connections- + [1] = 'vertical', -- North + [4] = 'vertical', -- South + [5] = 'vertical', -- North South + [2] = 'horizontal', -- East + [8] = 'horizontal', -- West + [10] = 'horizontal', -- East West + + -- Corners. + [3] = 'ne', + [9] = 'nw', + [6] = 'se', + [12] = 'sw', + + -- T Intersection + [7] = 'nes', + [11] = 'new', + [13] = 'nws', + [14] = 'sew', + + -- + Intersection + [15] = 'news', } -- ------------------------------------------------ @@ -180,7 +183,7 @@ local function selectWorldObjectSprite( worldObject, tile ) 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] ) + return TexturePacks.getSprite( worldObject:getID(), CONNECTION_BITMASK[result] ) end return TexturePacks.getSprite( worldObject:getID() ) diff --git a/src/ui/mapeditor/PrefabCanvas.lua b/src/ui/mapeditor/PrefabCanvas.lua index 94c19a1c..ef3a5111 100644 --- a/src/ui/mapeditor/PrefabCanvas.lua +++ b/src/ui/mapeditor/PrefabCanvas.lua @@ -31,27 +31,30 @@ local PREFAB_SIZES = { } local CONNECTION_BITMASK = { - [0] = '', - - [1] = '_vertical', - [4] = '_vertical', - [2] = '_horizontal', - [8] = '_horizontal', - - [5] = '_vertical', -- North South - [10] = '_horizontal', -- East West - - [3] = '_ne', - [9] = '_nw', - [6] = '_se', - [12] = '_sw', - - [7] = '_nes', - [11] = '_new', - [13] = '_nws', - [14] = '_sew', - - [15] = '_news', + [0] = 'default', + + -- Straight connections- + [1] = 'vertical', -- North + [4] = 'vertical', -- South + [5] = 'vertical', -- North South + [2] = 'horizontal', -- East + [8] = 'horizontal', -- West + [10] = 'horizontal', -- East West + + -- Corners. + [3] = 'ne', + [9] = 'nw', + [6] = 'se', + [12] = 'sw', + + -- T Intersection + [7] = 'nes', + [11] = 'new', + [13] = 'nws', + [14] = 'sew', + + -- + Intersection + [15] = 'news', } -- ------------------------------------------------ @@ -130,7 +133,7 @@ local function selectWorldObjectSprite( self, template, x, y ) checkConnection( template.connections, self:getTile( x + 1, y ), 2 ) + checkConnection( template.connections, self:getTile( x, y + 1 ), 4 ) + checkConnection( template.connections, self:getTile( x - 1, y ), 8 ) - return TexturePacks.getSprite( template.id .. CONNECTION_BITMASK[result] ) + return TexturePacks.getSprite( template.id, CONNECTION_BITMASK[result] ) end return TexturePacks.getSprite( template.id ) diff --git a/src/ui/texturepacks/Tileset.lua b/src/ui/texturepacks/Tileset.lua index 71aa75ef..943c2019 100644 --- a/src/ui/texturepacks/Tileset.lua +++ b/src/ui/texturepacks/Tileset.lua @@ -61,8 +61,20 @@ function Tileset.new( source, infos, twidth, theight ) -- Getters -- ------------------------------------------------ - function self:getSprite( id, alt ) - return alt and sprites[infos[id][alt]] or sprites[infos[id]] + --- + -- Returns a quad which maps to a sprite on the tileset based on the id. + -- By default an id points to a number which in turn points to a quad in + -- the tileset. + -- If the subid parameter is not nil the id should point to a table which + -- contains a list of subids which point to quad numbers. This can be used + -- to have different sprites based on the state of an object or to draw + -- connected tiles. + -- @tparam string id The id to search for in the infos table. + -- @tparam string subid The subid to search for in the infos table (optional) + -- + function self:getSprite( id, subid ) + local quadIndex = subid and infos[id][subid] or infos[id] + return sprites[quadIndex] end function self:getSpritesheet() From 383f17c1084d35f2b45763527c6ce452d0a7caea Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sun, 3 Dec 2017 18:12:28 +0100 Subject: [PATCH 025/178] Improve error handling for sprite access --- src/ui/texturepacks/Tileset.lua | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/ui/texturepacks/Tileset.lua b/src/ui/texturepacks/Tileset.lua index 943c2019..b1bc478b 100644 --- a/src/ui/texturepacks/Tileset.lua +++ b/src/ui/texturepacks/Tileset.lua @@ -73,8 +73,25 @@ function Tileset.new( source, infos, twidth, theight ) -- @tparam string subid The subid to search for in the infos table (optional) -- function self:getSprite( id, subid ) - local quadIndex = subid and infos[id][subid] or infos[id] - return sprites[quadIndex] + local definition = infos[id] + if not definition then + error( string.format( 'Can not find a sprite definition for [%s].', id )) + end + + if subid then + if type( definition ) ~= 'table' then + error( string.format( 'Tried to get a sub-sprite [%s] from a non-table defintion [%s].', subid, id )) + end + + local index = definition[subid] + if not index then + error( string.format( 'Can not find a sub-sprite definition for [%s][%s].', id, subid )) + end + + return sprites[index] + end + + return sprites[definition] end function self:getSpritesheet() From c84dc08c7e6bf4c4399e14d98b9d5a529a9a816d Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sun, 3 Dec 2017 18:14:03 +0100 Subject: [PATCH 026/178] Turn low wall into a connectable sprite --- res/data/WorldObjects.lua | 4 +++- res/texturepacks/default/sprites.lua | 16 +++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/res/data/WorldObjects.lua b/res/data/WorldObjects.lua index 08da76db..81dc5480 100644 --- a/res/data/WorldObjects.lua +++ b/res/data/WorldObjects.lua @@ -137,7 +137,9 @@ return { destructible = false, climbable = true, blocksVision = true, - blocksPathfinding = false + blocksPathfinding = false, + group = "WALL", + connections = { "DOOR", "FENCE", "WALL" } }, { id = 'worldobject_table', diff --git a/res/texturepacks/default/sprites.lua b/res/texturepacks/default/sprites.lua index e288a0fe..a7565788 100644 --- a/res/texturepacks/default/sprites.lua +++ b/res/texturepacks/default/sprites.lua @@ -18,7 +18,6 @@ return { -- ============================== ['worldobject_chair'] = 111, ['worldobject_crate'] = 247, - ['worldobject_lowwall'] = 62, ['worldobject_table'] = 211, ['worldobject_tree'] = 7, ['worldobject_window'] = 177, @@ -56,6 +55,21 @@ return { news = 198, }, + ['worldobject_lowwall'] = { + default = 198, + vertical = 180, + horizontal = 197, + ne = 193, + nw = 218, + se = 219, + sw = 192, + nes = 196, + new = 194, + nws = 181, + sew = 195, + news = 198, + }, + -- ============================== -- Items -- ============================== From 8f1eaee9465394dce6efa37429f88d7d8a5744dc Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sun, 3 Dec 2017 18:40:39 +0100 Subject: [PATCH 027/178] Remove superfluous tags --- res/data/items/Miscellaneous.lua | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/res/data/items/Miscellaneous.lua b/res/data/items/Miscellaneous.lua index 62834c71..d4ec558d 100644 --- a/res/data/items/Miscellaneous.lua +++ b/res/data/items/Miscellaneous.lua @@ -7,10 +7,7 @@ return { volume = 0.1, equippable = false, stackable = true, - permanent = false, - tags = { - 'misc' - } + permanent = false }, { id = "misc_splintered_wood", @@ -20,10 +17,7 @@ return { volume = 2.0, equippable = false, stackable = true, - permanent = false, - tags = { - 'misc' - } + permanent = false }, { id = "misc_glass_shard", @@ -33,10 +27,7 @@ return { volume = 0.5, equippable = false, stackable = true, - permanent = false, - tags = { - 'misc' - } + permanent = false }, { id = "misc_ceramic_shard", @@ -46,9 +37,6 @@ return { volume = 0.5, equippable = false, stackable = true, - permanent = false, - tags = { - 'misc' - } + permanent = false } } From f5e6c0141f4de2b82e1fdf873d3085dc8f4ca886 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sun, 3 Dec 2017 18:45:32 +0100 Subject: [PATCH 028/178] Improve tile template specs --- tests/spc/tile_template_spec.lua | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/spc/tile_template_spec.lua b/tests/spc/tile_template_spec.lua index 69aba55c..ce1c773c 100644 --- a/tests/spc/tile_template_spec.lua +++ b/tests/spc/tile_template_spec.lua @@ -7,19 +7,19 @@ describe( 'Tile template spec', function() templates = require( FILE_PATH ) end) - it( 'makes sure all tile templates have and id', function() + it( 'makes sure all tile templates have an id', function() for i, template in ipairs( templates ) do - assert.is.truthy( template.id, string.format( "Index number %d.", i )) + assert.not_nil( template.id, string.format( "Index number %d.", i )) end end) it( 'makes sure all passable tiles have movement costs', function() for _, template in ipairs( templates ) do if template.passable then - assert.is.truthy( template.movementCost, template.id ) - assert.is.truthy( template.movementCost.stand, template.id ) - assert.is.truthy( template.movementCost.crouch, template.id ) - assert.is.truthy( template.movementCost.prone, template.id ) + assert.not_nil( template.movementCost, template.id ) + assert.not_nil( template.movementCost.stand, template.id .. ' => stand' ) + assert.not_nil( template.movementCost.crouch, template.id .. ' => crouch' ) + assert.not_nil( template.movementCost.prone, template.id .. ' => prone' ) end end end) @@ -27,7 +27,7 @@ describe( 'Tile template spec', function() it( 'makes sure all impassable tiles have no movement costs', function() for _, template in ipairs( templates ) do if not template.passable then - assert.is.falsy( template.movementCost, template.id ) + assert.is_nil( template.movementCost, template.id ) end end end) From c286b969b12d40a8893cd915f3a4b17f05c66153 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sun, 3 Dec 2017 19:07:57 +0100 Subject: [PATCH 029/178] Refactor Ammunition.lua --- res/data/items/Ammunition.lua | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/res/data/items/Ammunition.lua b/res/data/items/Ammunition.lua index 1318edae..da9f86b1 100644 --- a/res/data/items/Ammunition.lua +++ b/res/data/items/Ammunition.lua @@ -8,12 +8,12 @@ return { equippable = false, stackable = true, permanent = false, + tags = { + 'humanoid' + }, damageType = 'piercing', effects = { customSpeed = { speed = 35 } - }, - tags = { - 'humanoid' } }, { @@ -25,13 +25,13 @@ return { equippable = false, stackable = true, permanent = false, + tags = { + 'humanoid' + }, damageType = 'piercing', effects = { spreadsOnShot = { pellets = 6 }, customSprite = { sprite = "12_gauge" } - }, - tags = { - 'humanoid' } }, { @@ -43,13 +43,13 @@ return { equippable = false, stackable = true, permanent = false, + tags = { + 'humanoid' + }, damageType = 'explosive', effects = { explosive = { blastRadius = 5 }, customSpeed = { speed = 12, increase = 1, final = 35 } - }, - tags = { - 'humanoid' } } } From f3a87dcf784868f73381d22c704f24de4a04da56 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sun, 3 Dec 2017 19:13:08 +0100 Subject: [PATCH 030/178] Refactor Armor.lua --- res/data/items/Armor.lua | 120 +++++++++++++++++++-------------------- 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/res/data/items/Armor.lua b/res/data/items/Armor.lua index e4b0c5ec..65684966 100644 --- a/res/data/items/Armor.lua +++ b/res/data/items/Armor.lua @@ -1,92 +1,92 @@ return { { - id = "footwear_combat_boots", - idDesc = "footwear_combat_boots_desc", - itemType = "Armor", - weight = 3, - volume = 2.3, + id = "footwear_combat_boots", + idDesc = "footwear_combat_boots_desc", + itemType = "Armor", + weight = 3, + volume = 2.3, equippable = true, - stackable = false, - permanent = false, - subType = "Footwear", - armor = { - protection = 12, - coverage = 100 - }, + stackable = false, + permanent = false, tags = { 'humanoid' + }, + subType = "Footwear", + armor = { + protection = 12, + coverage = 100 } }, { - id = "headgear_pasgt_helmet", - idDesc = "headgear_pasgt_helmet_desc", - itemType = "Armor", - weight = 1.3, - volume = 3.1, + id = "headgear_pasgt_helmet", + idDesc = "headgear_pasgt_helmet_desc", + itemType = "Armor", + weight = 1.3, + volume = 3.1, equippable = true, - stackable = false, - permanent = false, - subType = "Headgear", - armor = { - protection = 30, - coverage = 45 - }, + stackable = false, + permanent = false, tags = { 'humanoid' + }, + subType = "Headgear", + armor = { + protection = 30, + coverage = 45 } }, { - id = "jacket_pasgt_vest", - idDesc = "jacket_pasgt_vest_desc", - itemType = "Armor", - weight = 4, - volume = 4.3, + id = "jacket_pasgt_vest", + idDesc = "jacket_pasgt_vest_desc", + itemType = "Armor", + weight = 4, + volume = 4.3, equippable = true, - stackable = false, - permanent = false, - subType = "Jacket", - armor = { - protection = 12, - coverage = 80 - }, + stackable = false, + permanent = false, tags = { 'humanoid' + }, + subType = "Jacket", + armor = { + protection = 12, + coverage = 80 } }, { - id = "trousers_jeans", - idDesc = "trousers_jeans_desc", - itemType = "Armor", - weight = 1.0, - volume = 0.5, + id = "trousers_jeans", + idDesc = "trousers_jeans_desc", + itemType = "Armor", + weight = 1.0, + volume = 0.5, equippable = true, - stackable = false, - permanent = false, - subType = "Trousers", + stackable = false, + permanent = false, + tags = { + 'humanoid' + }, + subType = "Trousers", armor = { protection = 4, - coverage = 100 + coverage = 100 }, - tags = { - 'humanoid' - } }, { - id = "thick_fur", - idDesc = "thick_fur_desc", - itemType = "Armor", - weight = 1.0, - volume = 0.5, + id = "thick_fur", + idDesc = "thick_fur_desc", + itemType = "Armor", + weight = 1.0, + volume = 0.5, equippable = true, - stackable = false, - permanent = true, - subType = "Fur", - armor = { - protection = 4, - coverage = 100 - }, + stackable = false, + permanent = true, tags = { 'creature' + }, + subType = "Fur", + armor = { + protection = 4, + coverage = 100 } } } From efdeab0d3173183c20d979fe1603636f8a6db014 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sun, 3 Dec 2017 19:22:26 +0100 Subject: [PATCH 031/178] Refactor Containers.lua --- res/data/items/Containers.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/res/data/items/Containers.lua b/res/data/items/Containers.lua index 8f0f4221..320b027f 100644 --- a/res/data/items/Containers.lua +++ b/res/data/items/Containers.lua @@ -8,9 +8,9 @@ return { equippable = true, stackable = false, permanent = false, - carryCapacity = 15, tags = { 'humanoid' - } + }, + carryCapacity = 15 } } From d8dcb7ee49afe8337fd55062a2bbfcd5ff3f9ef5 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sun, 3 Dec 2017 19:28:31 +0100 Subject: [PATCH 032/178] Refactor weapon templates --- res/data/items/weapons/Melee.lua | 18 +++++++++--------- res/data/items/weapons/Ranged.lua | 18 +++++++++--------- res/data/items/weapons/Thrown.lua | 12 ++++++------ 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/res/data/items/weapons/Melee.lua b/res/data/items/weapons/Melee.lua index ea675661..61a41ef0 100644 --- a/res/data/items/weapons/Melee.lua +++ b/res/data/items/weapons/Melee.lua @@ -8,6 +8,9 @@ return { equippable = true, stackable = false, permanent = false, + tags = { + 'humanoid' + }, subType = "Melee", damage = 18, reloadable = false, @@ -27,9 +30,6 @@ return { attacks = 1, damageType = 'piercing' } - }, - tags = { - 'humanoid' } }, { @@ -41,6 +41,9 @@ return { equippable = true, stackable = false, permanent = false, + tags = { + 'humanoid' + }, subType = "Melee", damage = 24, reloadable = false, @@ -53,9 +56,6 @@ return { attacks = 1, damageType = 'bludgeoning' } - }, - tags = { - 'humanoid' } }, { @@ -67,6 +67,9 @@ return { equippable = true, stackable = false, permanent = true, + tags = { + 'humanoid' + }, subType = "Melee", damage = 14, reloadable = false, @@ -79,9 +82,6 @@ return { attacks = 1, damageType = 'piercing' } - }, - tags = { - 'creature' } } } diff --git a/res/data/items/weapons/Ranged.lua b/res/data/items/weapons/Ranged.lua index 8c0564cd..81a431c6 100644 --- a/res/data/items/weapons/Ranged.lua +++ b/res/data/items/weapons/Ranged.lua @@ -8,6 +8,9 @@ return { equippable = true, stackable = false, permanent = false, + tags = { + 'humanoid' + }, caliber = "5.45x39mm", sound = 'ASSAULT_RIFLE', subType = "Ranged", @@ -29,9 +32,6 @@ return { accuracy = 40, attacks = 5, } - }, - tags = { - 'humanoid' } }, { @@ -43,6 +43,9 @@ return { equippable = true, stackable = false, permanent = false, + tags = { + 'humanoid' + }, subType = "Ranged", caliber = "OG-7V", sound = 'ROCKET_LAUNCHER', @@ -57,9 +60,6 @@ return { accuracy = 65, attacks = 1, } - }, - tags = { - 'humanoid' } }, { @@ -71,6 +71,9 @@ return { equippable = true, stackable = false, permanent = false, + tags = { + 'humanoid' + }, subType = "Ranged", caliber = "12_gauge", sound = 'SHOTGUN', @@ -85,9 +88,6 @@ return { accuracy = 25, attacks = 1, } - }, - tags = { - 'humanoid' } } } diff --git a/res/data/items/weapons/Thrown.lua b/res/data/items/weapons/Thrown.lua index 5d9eda08..a3847b90 100644 --- a/res/data/items/weapons/Thrown.lua +++ b/res/data/items/weapons/Thrown.lua @@ -8,6 +8,9 @@ return { equippable = true, stackable = true, permanent = false, + tags = { + 'humanoid' + }, subType = "Thrown", damage = 80, reloadable = false, @@ -23,9 +26,6 @@ return { effects = { explosive = { blastRadius = 2 }, customSprite = { sprite = "weapon_m67_grenade" } - }, - tags = { - 'humanoid' } }, { @@ -37,6 +37,9 @@ return { equippable = true, stackable = true, permanent = false, + tags = { + 'humanoid' + }, subType = "Thrown", damage = 24, reloadable = false, @@ -51,9 +54,6 @@ return { }, effects = { customSprite = { sprite = "weapon_shuriken" } - }, - tags = { - 'humanoid' } } } From 4e8b286d2af72dc18d09410522844f4640523ef8 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 4 Dec 2017 12:40:19 +0100 Subject: [PATCH 033/178] Fix weapon tags for bite --- res/data/items/weapons/Melee.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/data/items/weapons/Melee.lua b/res/data/items/weapons/Melee.lua index 61a41ef0..10002707 100644 --- a/res/data/items/weapons/Melee.lua +++ b/res/data/items/weapons/Melee.lua @@ -68,7 +68,7 @@ return { stackable = false, permanent = true, tags = { - 'humanoid' + 'creature' }, subType = "Melee", damage = 14, From c234786b363419708eedad2392cb7537337c4751 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 4 Dec 2017 22:01:07 +0100 Subject: [PATCH 034/178] Implement custom sprites and colors for items Closes #119. --- res/texturepacks/default/colors.lua | 36 ++++++++++++++++++++++++++- res/texturepacks/default/sprites.lua | 37 +++++++++++++++++++++++++--- src/ui/MapPainter.lua | 6 +++-- 3 files changed, 72 insertions(+), 7 deletions(-) diff --git a/res/texturepacks/default/colors.lua b/res/texturepacks/default/colors.lua index 60365ae4..cfb5fbf7 100644 --- a/res/texturepacks/default/colors.lua +++ b/res/texturepacks/default/colors.lua @@ -79,7 +79,41 @@ return { -- ============================== -- ITEMS -- ============================== - ['items'] = COLORS.DB16, + + -- Ammunition + ['5.45x39mm'] = COLORS.DB06, + ['12_gauge'] = COLORS.DB06, + ['OG-7V'] = COLORS.DB06, + + -- Armor + ['footwear_combat_boots'] = COLORS.DB05, + ['headgear_pasgt_helmet'] = COLORS.DB05, + ['jacket_pasgt_vest'] = COLORS.DB05, + ['trousers_jeans'] = COLORS.DB05, + ['thick_fur'] = COLORS.DB05, + + -- Containers + ['bag_small_backpack'] = COLORS.DB17, + + -- Miscellaneous + ['misc_nail'] = COLORS.DB16, + ['misc_splintered_wood'] = COLORS.DB16, + ['misc_glass_shard'] = COLORS.DB16, + ['misc_ceramic_shard'] = COLORS.DB16, + + -- Melee + ['weapon_knife'] = COLORS.DB08, + ['weapon_tonfa'] = COLORS.DB08, + ['weapon_bite'] = COLORS.DB08, + + -- Ranged + ['weapon_aks74'] = COLORS.DB08, + ['weapon_benelli_m4'] = COLORS.DB08, + ['weapon_rpg7'] = COLORS.DB08, + + -- Thrown + ['weapon_m67_grenade'] = COLORS.DB08, + ['weapon_shuriken'] = COLORS.DB08, -- ============================== -- WORLD OBJECTS (OPENABLE) diff --git a/res/texturepacks/default/sprites.lua b/res/texturepacks/default/sprites.lua index a7565788..87630d60 100644 --- a/res/texturepacks/default/sprites.lua +++ b/res/texturepacks/default/sprites.lua @@ -73,12 +73,41 @@ return { -- ============================== -- Items -- ============================== - ['items'] = 34, - - ['weapon_m67_grenade'] = 8, - ['weapon_shuriken'] = 16, + -- Ammunition + ['5.45x39mm'] = 174, ['12_gauge'] = 8, + ['OG-7V'] = 95, + + -- Armor + ['footwear_combat_boots'] = 92, + ['headgear_pasgt_helmet'] = 92, + ['jacket_pasgt_vest'] = 92, + ['trousers_jeans'] = 92, + ['thick_fur'] = 92, + + -- Containers + ['bag_small_backpack'] = 234, + + -- Miscellaneous + ['misc_nail'] = 97, + ['misc_splintered_wood'] = 97, + ['misc_glass_shard'] = 97, + ['misc_ceramic_shard'] = 97, + + -- Melee + ['weapon_knife'] = 60, + ['weapon_tonfa'] = 48, + ['weapon_bite'] = 126, + + -- Ranged + ['weapon_aks74'] = 42, + ['weapon_benelli_m4'] = 42, + ['weapon_rpg7'] = 41, + + -- Thrown + ['weapon_m67_grenade'] = 8, + ['weapon_shuriken'] = 16, -- ============================== -- WORLD OBJECTS (OPENABLE) diff --git a/src/ui/MapPainter.lua b/src/ui/MapPainter.lua index b35421a2..e641a90a 100644 --- a/src/ui/MapPainter.lua +++ b/src/ui/MapPainter.lua @@ -119,7 +119,8 @@ local function selectTileColor( tile, faction ) end if not tile:getInventory():isEmpty() then - return TexturePacks.getColor( 'items' ) + local items = tile:getInventory():getItems() + return TexturePacks.getColor( items[1]:getID() ) end if tile:hasWorldObject() then @@ -201,7 +202,8 @@ local function selectTileSprite( tile, faction ) end if not tile:getInventory():isEmpty() then - return TexturePacks.getSprite( 'items' ) + local items = tile:getInventory():getItems() + return TexturePacks.getSprite( items[1]:getID() ) end if tile:hasWorldObject() then From 4890a07ad48abdebbf1885b22c41193fc17cd21f Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 4 Dec 2017 23:27:01 +0100 Subject: [PATCH 035/178] Mark certain tiles as invalid for creature spawns Closes #212. --- res/data/Tiles.lua | 24 ++++++++++++++++-------- src/map/Map.lua | 2 +- src/map/tiles/Tile.lua | 9 +++++++++ tests/spc/tile_template_spec.lua | 8 +++++--- 4 files changed, 31 insertions(+), 12 deletions(-) diff --git a/res/data/Tiles.lua b/res/data/Tiles.lua index a0591f4d..3b40c921 100644 --- a/res/data/Tiles.lua +++ b/res/data/Tiles.lua @@ -6,7 +6,8 @@ return { crouch = 1, prone = 2, }, - passable = true + passable = true, + spawn = true }, { id = 'tile_deep_water', @@ -19,7 +20,8 @@ return { crouch = 1, prone = 2, }, - passable = true + passable = true, + spawn = true }, { id = 'tile_gravel', @@ -28,7 +30,8 @@ return { crouch = 1, prone = 2, }, - passable = true + passable = true, + spawn = true }, { id = 'tile_soil', @@ -37,7 +40,8 @@ return { crouch = 1, prone = 2, }, - passable = true + passable = true, + spawn = true }, { id = 'tile_water', @@ -46,7 +50,8 @@ return { crouch = 4, prone = 6, }, - passable = true + passable = true, + spawn = false }, { id = 'tile_woodenfloor', @@ -55,7 +60,8 @@ return { crouch = 1, prone = 2, }, - passable = true + passable = true, + spawn = true }, { id = 'tile_carpet', @@ -64,7 +70,8 @@ return { crouch = 1, prone = 2 }, - passable = true + passable = true, + spawn = true }, { id = 'tile_floortiles', @@ -73,6 +80,7 @@ return { crouch = 1, prone = 2 }, - passable = true + passable = true, + spawn = true } } diff --git a/src/map/Map.lua b/src/map/Map.lua index f9235f90..0ceec772 100644 --- a/src/map/Map.lua +++ b/src/map/Map.lua @@ -92,7 +92,7 @@ function Map.new() local spawn = spawnpoints[faction][index] local tile = self:getTileAt( spawn.x, spawn.y ) - if tile:isPassable() and not tile:isOccupied() then + if tile:isSpawn() and tile:isPassable() and not tile:isOccupied() then return tile end end diff --git a/src/map/tiles/Tile.lua b/src/map/tiles/Tile.lua index d535da79..f725d719 100644 --- a/src/map/tiles/Tile.lua +++ b/src/map/tiles/Tile.lua @@ -32,6 +32,7 @@ function Tile.new( x, y, template ) local id = template.id; local movementCost = template.movementCost; local passable = template.passable; + local spawn = template.spawn local spriteID; local dirty; @@ -258,6 +259,14 @@ function Tile.new( x, y, template ) return passable; end + --- + -- Determines wether the tile is valid for spawning. + -- @treturn boolean True if the tile is valid for spawning. + -- + function self:isSpawn() + return spawn + end + -- ------------------------------------------------ -- Setters -- ------------------------------------------------ diff --git a/tests/spc/tile_template_spec.lua b/tests/spc/tile_template_spec.lua index ce1c773c..842adac0 100644 --- a/tests/spc/tile_template_spec.lua +++ b/tests/spc/tile_template_spec.lua @@ -13,21 +13,23 @@ describe( 'Tile template spec', function() end end) - it( 'makes sure all passable tiles have movement costs', function() + it( 'makes sure all passable tiles have their necessary fields', function() for _, template in ipairs( templates ) do if template.passable then assert.not_nil( template.movementCost, template.id ) assert.not_nil( template.movementCost.stand, template.id .. ' => stand' ) assert.not_nil( template.movementCost.crouch, template.id .. ' => crouch' ) assert.not_nil( template.movementCost.prone, template.id .. ' => prone' ) + assert.not_nil( template.spawn, template.id ) end end end) - it( 'makes sure all impassable tiles have no movement costs', function() + it( 'makes sure all impassable tiles have their necessary fields', function() for _, template in ipairs( templates ) do if not template.passable then - assert.is_nil( template.movementCost, template.id ) + assert.is_nil( template.movementCost, template.id ) + assert.is_nil( template.spawn, template.id ) end end end) From 964e7ef9331f5debaed4f8b31028ad541d09e427 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Wed, 6 Dec 2017 02:03:25 +0100 Subject: [PATCH 036/178] Fix delete all functionality in input dialog On the input dialog if there is a template pressing backspace will delete the full text. Pressing backspace afterwards will only delete single characters. --- src/ui/screens/InputDialog.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ui/screens/InputDialog.lua b/src/ui/screens/InputDialog.lua index bc96c829..b8573a0c 100644 --- a/src/ui/screens/InputDialog.lua +++ b/src/ui/screens/InputDialog.lua @@ -144,6 +144,7 @@ function InputDialog.new() if scancode == 'backspace' then if deleteAll then entry = '' + deleteAll = false else entry = entry:sub( 1, #entry-1 ) end From bec6637a9f4dd3128d486ecccf3a9e66cd769379 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Tue, 5 Dec 2017 02:27:31 +0100 Subject: [PATCH 037/178] Adjust ScreenManager to work with Middleclass --- lib/screenmanager/Screen.lua | 107 ---------------------------- lib/screenmanager/ScreenManager.lua | 8 +-- src/ui/screens/Screen.lua | 79 ++++++++++++++++++++ 3 files changed, 82 insertions(+), 112 deletions(-) delete mode 100644 lib/screenmanager/Screen.lua create mode 100644 src/ui/screens/Screen.lua diff --git a/lib/screenmanager/Screen.lua b/lib/screenmanager/Screen.lua deleted file mode 100644 index d4b3621e..00000000 --- a/lib/screenmanager/Screen.lua +++ /dev/null @@ -1,107 +0,0 @@ ---===============================================================================-- --- -- --- Copyright (c) 2014 - 2017 Robert Machmer -- --- -- --- This software is provided 'as-is', without any express or implied -- --- warranty. In no event will the authors be held liable for any damages -- --- arising from the use of this software. -- --- -- --- Permission is granted to anyone to use this software for any purpose, -- --- including commercial applications, and to alter it and redistribute it -- --- freely, subject to the following restrictions: -- --- -- --- 1. The origin of this software must not be misrepresented; you must not -- --- claim that you wrote the original software. If you use this software -- --- in a product, an acknowledgment in the product documentation would be -- --- appreciated but is not required. -- --- 2. Altered source versions must be plainly marked as such, and must not be -- --- misrepresented as being the original software. -- --- 3. This notice may not be removed or altered from any source distribution. -- --- -- ---===============================================================================-- - -local Screen = {} - --- ------------------------------------------------ --- Private Functions --- ------------------------------------------------ - ---- --- Function stub. --- -local function null() - return -end - --- ------------------------------------------------ --- Module --- ------------------------------------------------ - -function Screen.new() - local self = {} - - local active = true - - -- ------------------------------------------------ - -- Public Methods - -- ------------------------------------------------ - - function self:isActive() - return active - end - - function self:setActive( nactiv ) - active = nactiv - end - - -- ------------------------------------------------ - -- Callback-stubs - -- ------------------------------------------------ - - self.init = null - self.close = null - self.receive = null - self.directorydropped = null - self.draw = null - self.filedropped = null - self.focus = null - self.keypressed = null - self.keyreleased = null - self.lowmemory = null - self.mousefocus = null - self.mousemoved = null - self.mousepressed = null - self.mousereleased = null - self.quit = null - self.resize = null - self.textedited = null - self.textinput = null - self.threaderror = null - self.touchmoved = null - self.touchpressed = null - self.touchreleased = null - self.update = null - self.visible = null - self.wheelmoved = null - self.gamepadaxis = null - self.gamepadpressed = null - self.gamepadreleased = null - self.joystickadded = null - self.joystickaxis = null - self.joystickhat = null - self.joystickpressed = null - self.joystickreleased = null - self.joystickremoved = null - - return self -end - --- ------------------------------------------------ --- Return Module --- ------------------------------------------------ - -return Screen - ---================================================================================================== --- Created 02.06.14 - 20:25 = ---================================================================================================== diff --git a/lib/screenmanager/ScreenManager.lua b/lib/screenmanager/ScreenManager.lua index 0b3e7195..ca87891c 100644 --- a/lib/screenmanager/ScreenManager.lua +++ b/lib/screenmanager/ScreenManager.lua @@ -26,7 +26,7 @@ -- @module ScreenManager -- local ScreenManager = { - _VERSION = '2.1.1', + _VERSION = '2.1.1 (modified)', _DESCRIPTION = 'Screen/State Management for the LÖVE framework', _URL = 'https://github.com/rm-code/screenmanager/', } @@ -92,10 +92,8 @@ local function push( screen, args ) end -- Push the new screen onto the stack. - stack[#stack + 1] = screens[screen].new() - - -- Create the new screen and initialise it. - stack[#stack]:init( unpack( args ) ) + stack[#stack + 1] = screens[screen]( unpack( args )) + ScreenManager.peek():setActive( true ) end --- diff --git a/src/ui/screens/Screen.lua b/src/ui/screens/Screen.lua new file mode 100644 index 00000000..fe723c76 --- /dev/null +++ b/src/ui/screens/Screen.lua @@ -0,0 +1,79 @@ +--- +-- @module Screen +-- + +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ + +local Class = require( 'lib.Middleclass' ) + +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ + +local Screen = Class( 'Screen' ) + +-- ------------------------------------------------ +-- Private Functions +-- ------------------------------------------------ + +--- +-- Function stub. +-- +local function null() + return +end + +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ + +function Screen:isActive() + return self.active +end + +function Screen:setActive( activ ) + self.active = activ +end + +-- ------------------------------------------------ +-- Callback-stubs +-- ------------------------------------------------ + +Screen.init = null +Screen.close = null +Screen.receive = null +Screen.directorydropped = null +Screen.draw = null +Screen.filedropped = null +Screen.focus = null +Screen.keypressed = null +Screen.keyreleased = null +Screen.lowmemory = null +Screen.mousefocus = null +Screen.mousemoved = null +Screen.mousepressed = null +Screen.mousereleased = null +Screen.quit = null +Screen.resize = null +Screen.textedited = null +Screen.textinput = null +Screen.threaderror = null +Screen.touchmoved = null +Screen.touchpressed = null +Screen.touchreleased = null +Screen.update = null +Screen.visible = null +Screen.wheelmoved = null +Screen.gamepadaxis = null +Screen.gamepadpressed = null +Screen.gamepadreleased = null +Screen.joystickadded = null +Screen.joystickaxis = null +Screen.joystickhat = null +Screen.joystickpressed = null +Screen.joystickreleased = null +Screen.joystickremoved = null + +return Screen From a907039866fb418db1f6102a1a3b0e9c44751045 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Tue, 5 Dec 2017 02:27:52 +0100 Subject: [PATCH 038/178] Refactor BootLoadingScreen --- src/ui/screens/BootLoadingScreen.lua | 49 ++++++++++++---------------- 1 file changed, 21 insertions(+), 28 deletions(-) diff --git a/src/ui/screens/BootLoadingScreen.lua b/src/ui/screens/BootLoadingScreen.lua index b91d1e65..26ea45fe 100644 --- a/src/ui/screens/BootLoadingScreen.lua +++ b/src/ui/screens/BootLoadingScreen.lua @@ -8,7 +8,7 @@ -- ------------------------------------------------ local ScreenManager = require( 'lib.screenmanager.ScreenManager' ) -local Screen = require( 'lib.screenmanager.Screen' ) +local Screen = require( 'src.ui.screens.Screen' ) local Log = require( 'src.util.Log' ) local Translator = require( 'src.util.Translator' ) local TexturePacks = require( 'src.ui.texturepacks.TexturePacks' ) @@ -27,46 +27,39 @@ local Settings = require( 'src.Settings' ) -- Module -- ------------------------------------------------ -local BootLoadingScreen = {} +local BootLoadingScreen = Screen:subclass( 'BootLoadingScreen' ) -- ------------------------------------------------ -- Constructor -- ------------------------------------------------ -function BootLoadingScreen.new() - local self = Screen.new() +function BootLoadingScreen:initialize() + local startTime = love.timer.getTime() - function self:init() - local startTime = love.timer.getTime() + TexturePacks.load() - TexturePacks.load() + ItemFactory.loadTemplates() + TileFactory.loadTemplates() + BodyFactory.loadTemplates() + WorldObjectFactory.loadTemplates() + BehaviorTreeFactory.loadTemplates() + SoundManager.loadResources() - ItemFactory.loadTemplates() - TileFactory.loadTemplates() - BodyFactory.loadTemplates() - WorldObjectFactory.loadTemplates() - BehaviorTreeFactory.loadTemplates() - SoundManager.loadResources() + CharacterFactory.init() - CharacterFactory.init() + ProceduralMapGenerator.load() + PrefabLoader.load() - ProceduralMapGenerator.load() - PrefabLoader.load() + Settings.load() - -- Load settings. - Settings.load() + Translator.init( Settings.getLocale() ) + TexturePacks.setCurrent( Settings.getTexturepack() ) + love.window.setFullscreen( Settings.getFullscreen() ) - Translator.init( Settings.getLocale() ) - TexturePacks.setCurrent( Settings.getTexturepack() ) - love.window.setFullscreen( Settings.getFullscreen() ) + local endTime = love.timer.getTime() + Log.debug( string.format( 'Loading game resources took %.3f seconds!', endTime - startTime ), 'BootLoadingScreen' ) - local endTime = love.timer.getTime() - Log.debug( string.format( 'Loading game resources took %.3f seconds!', endTime - startTime ), 'BootLoadingScreen' ) - - ScreenManager.switch( 'mainmenu' ) - end - - return self + ScreenManager.switch( 'mainmenu' ) end return BootLoadingScreen From e425121c647036ddbb32c503a80f06f5012af120 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Tue, 5 Dec 2017 02:28:18 +0100 Subject: [PATCH 039/178] Refactor MainMenu --- src/ui/screens/MainMenu.lua | 231 +++++++++++++++++------------------- 1 file changed, 108 insertions(+), 123 deletions(-) diff --git a/src/ui/screens/MainMenu.lua b/src/ui/screens/MainMenu.lua index acb0e4bf..de7e8c60 100644 --- a/src/ui/screens/MainMenu.lua +++ b/src/ui/screens/MainMenu.lua @@ -1,4 +1,4 @@ -local Screen = require( 'lib.screenmanager.Screen' ) +local Screen = require( 'src.ui.screens.Screen' ) local ScreenManager = require( 'lib.screenmanager.ScreenManager' ) local UIButton = require( 'src.ui.elements.UIButton' ) local UIHorizontalList = require( 'src.ui.elements.lists.UIHorizontalList' ) @@ -13,7 +13,7 @@ local Settings = require( 'src.Settings' ) -- Module -- ------------------------------------------------ -local SplashScreen = {} +local MainMenu = Screen:subclass( 'MainMenu' ) -- ------------------------------------------------ -- Constants @@ -48,166 +48,151 @@ local BUTTON_LIST_WIDTH = 60 local BUTTON_LIST_Y = 20 -- ------------------------------------------------ --- Constructor +-- Private Functions -- ------------------------------------------------ -function SplashScreen.new() - local self = Screen.new() - - -- ------------------------------------------------ - -- Private Variables - -- ------------------------------------------------ - - local title - local buttonList - local debug - local footer - local container - - -- ------------------------------------------------ - -- Private Functions - -- ------------------------------------------------ - - local function createTitle() - local font = TexturePacks.getFont() - 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() ) +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 - local function drawTitle() - 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 + return title +end - local function drawDebugInfo() - local font = TexturePacks.getFont() - if debug then - TexturePacks.setColor( 'ui_text_dim' ) - love.graphics.print( love.timer.getFPS() .. ' FPS', font:getGlyphWidth(), font:getGlyphWidth() ) - love.graphics.print( math.floor( collectgarbage( 'count' )) .. ' kb', font:getGlyphWidth(), font:getGlyphWidth() + font:getGlyphHeight() ) - TexturePacks.resetColor() - 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 + TexturePacks.setColor( 'ui_text_dim' ) + love.graphics.print( love.timer.getFPS() .. ' FPS', font:getGlyphWidth(), font:getGlyphWidth() ) + love.graphics.print( math.floor( collectgarbage( 'count' )) .. ' kb', font:getGlyphWidth(), font:getGlyphWidth() + font:getGlyphHeight() ) + TexturePacks.resetColor() end +end - local function createButtons() - local lx = GridHelper.centerElement( BUTTON_LIST_WIDTH, 1 ) +local function createButtons() + local lx = GridHelper.centerElement( BUTTON_LIST_WIDTH, 1 ) - local _, sh = GridHelper.getScreenGridDimensions() - local ly = sh - BUTTON_LIST_Y + local _, sh = GridHelper.getScreenGridDimensions() + local ly = sh - BUTTON_LIST_Y - buttonList = UIHorizontalList( lx, ly, 0, 0, BUTTON_LIST_WIDTH, 1 ) + local buttonList = UIHorizontalList( lx, ly, 0, 0, BUTTON_LIST_WIDTH, 1 ) - local newGameButton = UIButton( lx, ly, 0, 0, 10, 1, function() ScreenManager.switch( 'gamescreen' ) end, Translator.getText( 'ui_main_menu_new_game' )) - buttonList:addChild( newGameButton ) + local newGameButton = UIButton( lx, ly, 0, 0, 10, 1, function() ScreenManager.switch( 'gamescreen' ) end, Translator.getText( 'ui_main_menu_new_game' )) + buttonList:addChild( newGameButton ) - local loadPreviousGameButton = UIButton( lx, ly, 0, 0, 10, 1, function() ScreenManager.switch( 'loadgame' ) end, Translator.getText( 'ui_main_menu_load_game' )) - buttonList:addChild( loadPreviousGameButton ) + local loadPreviousGameButton = UIButton( lx, ly, 0, 0, 10, 1, function() ScreenManager.switch( 'loadgame' ) end, Translator.getText( 'ui_main_menu_load_game' )) + buttonList:addChild( loadPreviousGameButton ) - local openOptionsButton = UIButton( lx, ly, 0, 0, 10, 1, function() ScreenManager.switch( 'options' ) end, Translator.getText( 'ui_main_menu_options' )) - buttonList:addChild( openOptionsButton ) + local openOptionsButton = UIButton( lx, ly, 0, 0, 10, 1, function() ScreenManager.switch( 'options' ) end, Translator.getText( 'ui_main_menu_options' )) + buttonList:addChild( openOptionsButton ) - -- 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' )) - buttonList:addChild( mapEditorButton ) - end + -- 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' )) + buttonList:addChild( mapEditorButton ) + end - local changelogButton = UIButton( lx, ly, 0, 0, 10, 1, function() ScreenManager.switch( 'changelog' ) end, Translator.getText( 'ui_main_menu_changelog' )) - buttonList:addChild( changelogButton ) + local changelogButton = UIButton( lx, ly, 0, 0, 10, 1, function() ScreenManager.switch( 'changelog' ) end, Translator.getText( 'ui_main_menu_changelog' )) + buttonList:addChild( changelogButton ) - local exitGameButton = UIButton( lx, ly, 0, 0, 10, 1, function() love.event.quit() end, Translator.getText( 'ui_main_menu_exit' )) - buttonList:addChild( exitGameButton ) - end + local exitGameButton = UIButton( lx, ly, 0, 0, 10, 1, function() love.event.quit() end, Translator.getText( 'ui_main_menu_exit' )) + buttonList:addChild( exitGameButton ) - -- ------------------------------------------------ - -- Public Methods - -- ------------------------------------------------ + return buttonList +end - function self:init() - love.mouse.setVisible( false ) +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - createTitle() - createButtons() +function MainMenu:initialize() + love.mouse.setVisible( false ) - container = UIContainer() - container:register( buttonList ) + self.title = createTitle() + self.buttonList = createButtons() - footer = UICopyrightFooter.new() + self.container = UIContainer() + self.container:register( self.buttonList ) - -- Flush the LuaJIT cache to prevent memory leaks caused by cached - -- upvalues and closures. - -- @see https://github.com/LuaJIT/LuaJIT/issues/303 - jit.flush() + self.footer = UICopyrightFooter.new() - collectgarbage( 'collect' ) - end + -- Flush the LuaJIT cache to prevent memory leaks caused by cached + -- upvalues and closures. + -- @see https://github.com/LuaJIT/LuaJIT/issues/303 + jit.flush() - function self:draw() - local font = TexturePacks.getFont() - font:use() + collectgarbage( 'collect' ) +end - drawTitle() +function MainMenu:draw() + local font = TexturePacks.getFont() + font:use() - container:draw() + drawTitle( self.title ) - drawDebugInfo() + self.container:draw() - footer:draw() - end + drawDebugInfo( self.debug ) - function self:update() - container:update() - end + self.footer:draw() +end - function self:keypressed( _, scancode ) - love.mouse.setVisible( false ) +function MainMenu:update() + self.container:update() +end - if scancode == 'left' then - container:command( 'left' ) - elseif scancode == 'right' then - container:command( 'right' ) - elseif scancode == 'return' then - container:command( 'activate' ) - end +function MainMenu:keypressed( _, scancode ) + love.mouse.setVisible( false ) - if scancode == 'f1' then - debug = not debug - end + if scancode == 'left' then + self.container:command( 'left' ) + elseif scancode == 'right' then + self.container:command( 'right' ) + elseif scancode == 'return' then + self.container:command( 'activate' ) end - function self:mousemoved() - love.mouse.setVisible( true ) + if scancode == 'f1' then + self.debug = not self.debug end +end - function self:mousereleased() - container:mousecommand( 'activate' ) - end +function MainMenu:mousemoved() + love.mouse.setVisible( true ) +end - function self:resize( _, _ ) - local lx = GridHelper.centerElement( BUTTON_LIST_WIDTH, 1 ) - local _, sh = GridHelper.getScreenGridDimensions() - local ly = sh - BUTTON_LIST_Y +function MainMenu:mousereleased() + self.container:mousecommand( 'activate' ) +end - buttonList:setOrigin( lx, ly ) - end +function MainMenu:resize( _, _ ) + local lx = GridHelper.centerElement( BUTTON_LIST_WIDTH, 1 ) + local _, sh = GridHelper.getScreenGridDimensions() + local ly = sh - BUTTON_LIST_Y - return self + self.buttonList:setOrigin( lx, ly ) end -return SplashScreen +return MainMenu From 025d9e6bb0f24672dfd09c65f7da32ebd0bce994 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Tue, 5 Dec 2017 02:38:50 +0100 Subject: [PATCH 040/178] Refactor ChangelogScreen --- src/ui/screens/ChangelogScreen.lua | 334 ++++++++++++++--------------- 1 file changed, 158 insertions(+), 176 deletions(-) diff --git a/src/ui/screens/ChangelogScreen.lua b/src/ui/screens/ChangelogScreen.lua index 86243c83..da50e79f 100644 --- a/src/ui/screens/ChangelogScreen.lua +++ b/src/ui/screens/ChangelogScreen.lua @@ -6,7 +6,7 @@ -- Required Modules -- ------------------------------------------------ -local Screen = require( 'lib.screenmanager.Screen' ) +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' ) @@ -21,7 +21,7 @@ local UIContainer = require( 'src.ui.elements.UIContainer' ) -- Module -- ------------------------------------------------ -local ChangelogScreen = {} +local ChangelogScreen = Screen:subclass( 'ChangelogScreen' ) -- ------------------------------------------------ -- Constants @@ -66,220 +66,202 @@ local SCROLLAREA_GRID_WIDTH = 56 local SCROLLAREA_GRID_HEIGHT = 42 -- ------------------------------------------------ --- Constructor +-- Private Functions -- ------------------------------------------------ -function ChangelogScreen.new() - local self = Screen.new() - - -- ------------------------------------------------ - -- Private Variables - -- ------------------------------------------------ - - local content - local text - local textHeight - - local buttonList - local font - local footer - - local scrollarea - - local container - - -- ------------------------------------------------ - -- Private Functions - -- ------------------------------------------------ - - --- - -- Checks if a line starts with a certain string. - -- @tparam string line The line to search through. - -- @tparam string search The string to search for. - -- @tparam boolean Returns true if the line starts with the string. - -- - local function startsWith( line, search ) - return string.find( line:upper(), search:upper() ) == 1 - end +--- +-- Checks if a line starts with a certain string. +-- @tparam string line The line to search through. +-- @tparam string search The string to search for. +-- @tparam boolean Returns true if the line starts with the string. +-- +local function startsWith( line, search ) + return string.find( line:upper(), search:upper() ) == 1 +end - --- - -- Parses the changelog file and breaks it down into its sections. - -- - local function parseFile() - content = {} - - local version - local section - local str = love.filesystem.read( LOG_FILE ) - for line in str:gmatch("[^\r\n]+") do - if startsWith( line, SECTION_PREFIXES.VERSION ) then - version = line:gsub( SECTION_PREFIXES.VERSION, '' ) - content[#content + 1] = { version = version } - elseif startsWith( line, SECTION_PREFIXES.ADDITIONS ) then - section = SECTION_IDS.ADDITIONS - content[#content][section] = {} - elseif startsWith( line, SECTION_PREFIXES.REMOVALS ) then - section = SECTION_IDS.REMOVALS - content[#content][section] = {} - elseif startsWith( line, SECTION_PREFIXES.FIXES ) then - section = SECTION_IDS.FIXES - content[#content][section] = {} - elseif startsWith( line, SECTION_PREFIXES.OTHER ) then - section = SECTION_IDS.OTHER - content[#content][section] = {} - elseif content[#content][section] then - local prefix, cline = string.match(line, '( *)%- (.*)') - table.insert( content[#content][section], { text = cline, offset = math.floor( #prefix / 4 ) + 1 }) - end +--- +-- Parses the changelog file and breaks it down into its sections. +-- @treturn table A table containing the parsed changelog file. +-- +local function parseFile() + local content = {} + + local version + local section + local str = love.filesystem.read( LOG_FILE ) + for line in str:gmatch("[^\r\n]+") do + if startsWith( line, SECTION_PREFIXES.VERSION ) then + version = line:gsub( SECTION_PREFIXES.VERSION, '' ) + content[#content + 1] = { version = version } + elseif startsWith( line, SECTION_PREFIXES.ADDITIONS ) then + section = SECTION_IDS.ADDITIONS + content[#content][section] = {} + elseif startsWith( line, SECTION_PREFIXES.REMOVALS ) then + section = SECTION_IDS.REMOVALS + content[#content][section] = {} + elseif startsWith( line, SECTION_PREFIXES.FIXES ) then + section = SECTION_IDS.FIXES + content[#content][section] = {} + elseif startsWith( line, SECTION_PREFIXES.OTHER ) then + section = SECTION_IDS.OTHER + content[#content][section] = {} + elseif content[#content][section] then + local prefix, cline = string.match(line, '( *)%- (.*)') + table.insert( content[#content][section], { text = cline, offset = math.floor( #prefix / 4 ) + 1 }) end end - --- - -- Creates a header for each version in the changelog. - -- @tparam string version The version string to use as a header. - -- @tparam number height The current height of the text object. - -- - local function createVersionHeader( version, height ) - local tw, _ = TexturePacks.getTileDimensions() - height = height + text:getHeight() - height = height + text:getHeight() - text:addf({ TexturePacks.getColor( 'ui_changelog_version' ), version }, (SCROLLAREA_GRID_WIDTH - 1) * tw, 'left', 0, height ) - height = height + text:getHeight() - local underline = version:gsub( '.', '_' ) - text:addf({ TexturePacks.getColor( 'ui_changelog_version' ), underline }, (SCROLLAREA_GRID_WIDTH - 1) * tw, 'left', 0, height ) - height = height + text:getHeight() - height = height + text:getHeight() + return content +end + +--- +-- Creates a header for each version in the changelog. +-- @tparam Text text The text object to add the header to. +-- @tparam string version The version string to use as a header. +-- @tparam number height The current height of the text object. +-- @treturn number The new height of the text object. +-- +local function createVersionHeader( text, version, height ) + local tw, _ = TexturePacks.getTileDimensions() + height = height + text:getHeight() + height = height + text:getHeight() + text:addf({ TexturePacks.getColor( 'ui_changelog_version' ), version }, (SCROLLAREA_GRID_WIDTH - 1) * tw, 'left', 0, height ) + height = height + text:getHeight() + local underline = version:gsub( '.', '_' ) + text:addf({ TexturePacks.getColor( 'ui_changelog_version' ), underline }, (SCROLLAREA_GRID_WIDTH - 1) * tw, 'left', 0, height ) + height = height + text:getHeight() + height = height + text:getHeight() + return height +end + +--- +-- Creates a section. +-- @tparam Text text The text object to add the section to. +-- @tparam string id The type of section to create. +-- @tparam table log A table containing the changelog information for a version. +-- @tparam number height The current height of the Text object. +-- @treturn number The new height of the text object. +-- +local function createSection( text, id, log, height ) + if not log[id] then return height end - --- - -- Creates a section. - -- @tparam string id The type of section to create. - -- @tparam table log A table containing the changelog information for a version. - -- @tparam number height The current height of the Text object. - -- - local function createSection( id, log, height ) - if not log[id] then - return height - end + local tw, _ = TexturePacks.getTileDimensions() - local tw, _ = TexturePacks.getTileDimensions() + text:addf( { TexturePacks.getColor( SECTION_HEADERS[id:upper()].COLOR ), SECTION_HEADERS[id:upper()].TEXT }, (SCROLLAREA_GRID_WIDTH - 1) * tw, 'left', 0, height ) + height = height + text:getHeight() - text:addf( { TexturePacks.getColor( SECTION_HEADERS[id:upper()].COLOR ), SECTION_HEADERS[id:upper()].TEXT }, (SCROLLAREA_GRID_WIDTH - 1) * tw, 'left', 0, height ) + for _, line in ipairs( log[id] ) do + text:addf({ TexturePacks.getColor( 'ui_changelog_text' ), '- ' }, (SCROLLAREA_GRID_WIDTH - 2 - line.offset) * tw, 'left', line.offset * tw, height ) + text:addf({ TexturePacks.getColor( 'ui_changelog_text' ), line.text }, (SCROLLAREA_GRID_WIDTH - 2 - line.offset) * tw, 'left', (1 + line.offset) * tw, height ) height = height + text:getHeight() - - for _, line in ipairs( log[id] ) do - text:addf({ TexturePacks.getColor( 'ui_changelog_text' ), '- ' }, (SCROLLAREA_GRID_WIDTH - 2 - line.offset) * tw, 'left', line.offset * tw, height ) - text:addf({ TexturePacks.getColor( 'ui_changelog_text' ), line.text }, (SCROLLAREA_GRID_WIDTH - 2 - line.offset) * tw, 'left', (1 + line.offset) * tw, height ) - height = height + text:getHeight() - end - - return height + text:getHeight() end - --- - -- Creates the text object containing the changelog. - -- - local function createLog() - text = love.graphics.newText( font:get() ) + return height + text:getHeight() +end - local height = 0 - for _, log in ipairs( content ) do - height = createVersionHeader( log.version, height ) +--- +-- Creates the text object containing the changelog. +-- @tparam table content A table containing the parsed changelog file. +-- @treturn Text The text object containing the changelog ready for drawing. +-- @treturn number The text object's height in pixels. +-- +local function createLog( content ) + local text = love.graphics.newText( TexturePacks:getFont():get() ) + local height = 0 - height = createSection( SECTION_IDS.ADDITIONS, log, height ) - height = createSection( SECTION_IDS.REMOVALS, log, height ) - height = createSection( SECTION_IDS.FIXES, log, height ) - height = createSection( SECTION_IDS.OTHER, log, height ) - end + for _, log in ipairs( content ) do + height = createVersionHeader( text, log.version, height ) - textHeight = height + height = createSection( text, SECTION_IDS.ADDITIONS, log, height ) + height = createSection( text, SECTION_IDS.REMOVALS, log, height ) + height = createSection( text, SECTION_IDS.FIXES, log, height ) + height = createSection( text, SECTION_IDS.OTHER, log, height ) end - --- - -- Creates the back button at the bottom of the screen. - -- - local function createButtons() - local lx, ly = GridHelper.centerElement( SCROLLAREA_GRID_WIDTH, SCROLLAREA_GRID_HEIGHT ) - lx, ly = lx, ly + SCROLLAREA_GRID_HEIGHT - buttonList = UIVerticalList( lx, ly, 0, 0, SCROLLAREA_GRID_WIDTH, SCROLLAREA_GRID_HEIGHT ) + return text, height +end - local closeButton = UIButton( lx, ly, 0, 0, SCROLLAREA_GRID_WIDTH, 1, function() ScreenManager.switch( 'mainmenu' ) end, Translator.getText( 'ui_back' )) - buttonList:addChild( closeButton ) - end +--- +-- Creates the back button at the bottom of the screen. +-- +local function createButtons() + local lx, ly = GridHelper.centerElement( SCROLLAREA_GRID_WIDTH, SCROLLAREA_GRID_HEIGHT ) + lx, ly = lx, ly + SCROLLAREA_GRID_HEIGHT + local buttonList = UIVerticalList( lx, ly, 0, 0, SCROLLAREA_GRID_WIDTH, SCROLLAREA_GRID_HEIGHT ) - -- ------------------------------------------------ - -- Public Methods - -- ------------------------------------------------ + local closeButton = UIButton( lx, ly, 0, 0, SCROLLAREA_GRID_WIDTH, 1, function() ScreenManager.switch( 'mainmenu' ) end, Translator.getText( 'ui_back' )) + buttonList:addChild( closeButton ) - function self:init() - font = TexturePacks.getFont() + return buttonList +end - parseFile() - createLog() +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - createButtons() +function ChangelogScreen:initialize() + local content = parseFile() + self.text, self.textHeight = createLog( content ) - container = UIContainer() - container:register( buttonList ) + self.buttonList = createButtons() - local ox, oy = GridHelper.centerElement( SCROLLAREA_GRID_WIDTH, SCROLLAREA_GRID_HEIGHT ) - scrollarea = UIScrollArea( ox, oy, 0, 0, SCROLLAREA_GRID_WIDTH, SCROLLAREA_GRID_HEIGHT, text, textHeight ) + self.container = UIContainer() + self.container:register( self.buttonList ) - footer = UICopyrightFooter.new() - end + local ox, oy = GridHelper.centerElement( SCROLLAREA_GRID_WIDTH, SCROLLAREA_GRID_HEIGHT ) + self.scrollarea = UIScrollArea( ox, oy, 0, 0, SCROLLAREA_GRID_WIDTH, SCROLLAREA_GRID_HEIGHT, self.text, self.textHeight ) - function self:update() - font = TexturePacks.getFont() - container:update() - end + self.footer = UICopyrightFooter.new() +end - function self:draw() - font:use() - scrollarea:draw() - container:draw() - footer:draw() - end +function ChangelogScreen:update() + self.container:update() +end - function self:keypressed( scancode ) - love.mouse.setVisible( false ) +function ChangelogScreen:draw() + self.scrollarea:draw() + self.container:draw() + self.footer:draw() +end - if scancode == 'escape' then - ScreenManager.switch( 'mainmenu' ) - end +function ChangelogScreen:keypressed( scancode ) + love.mouse.setVisible( false ) - if scancode == 'up' then - container:command( 'up' ) - elseif scancode == 'down' then - container:command( 'down' ) - elseif scancode == 'return' then - container:command( 'activate' ) - end + if scancode == 'escape' then + ScreenManager.switch( 'mainmenu' ) end - function self:mousemoved() - love.mouse.setVisible( true ) + if scancode == 'up' then + self.container:command( 'up' ) + elseif scancode == 'down' then + self.container:command( 'down' ) + elseif scancode == 'return' then + self.container:command( 'activate' ) end +end - function self:mousereleased( _, _ ) - if scrollarea:isMouseOver() then - scrollarea:command( 'activate' ) - elseif buttonList:isMouseOver() then - buttonList:command( 'activate' ) - end - end +function ChangelogScreen:mousemoved() + love.mouse.setVisible( true ) +end - function self:wheelmoved( _, dy ) - scrollarea:command( 'scroll', dy ) +function ChangelogScreen:mousereleased( _, _ ) + if self.scrollarea:isMouseOver() then + self.scrollarea:command( 'activate' ) + elseif self.buttonList:isMouseOver() then + self.buttonList:command( 'activate' ) end +end - function self:resize( _, _ ) - local lx, ly = GridHelper.centerElement( SCROLLAREA_GRID_WIDTH, SCROLLAREA_GRID_HEIGHT ) - buttonList:setOrigin( lx, ly + SCROLLAREA_GRID_HEIGHT ) - scrollarea:setOrigin( lx, ly ) - end +function ChangelogScreen:wheelmoved( _, dy ) + self.scrollarea:command( 'scroll', dy ) +end - return self +function ChangelogScreen:resize( _, _ ) + local lx, ly = GridHelper.centerElement( SCROLLAREA_GRID_WIDTH, SCROLLAREA_GRID_HEIGHT ) + self.buttonList:setOrigin( lx, ly + SCROLLAREA_GRID_HEIGHT ) + self.scrollarea:setOrigin( lx, ly ) end return ChangelogScreen From 71a8b8d4bba62397598ac5d26d1f83a5a66dde13 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Tue, 5 Dec 2017 03:03:46 +0100 Subject: [PATCH 041/178] Refactor GameScreen --- src/ui/screens/GameScreen.lua | 32 ++++++++++---------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/src/ui/screens/GameScreen.lua b/src/ui/screens/GameScreen.lua index f364af7b..f11a13e9 100644 --- a/src/ui/screens/GameScreen.lua +++ b/src/ui/screens/GameScreen.lua @@ -9,7 +9,7 @@ -- Required Modules -- ------------------------------------------------ -local Screen = require( 'lib.screenmanager.Screen' ) +local Screen = require( 'src.ui.screens.Screen' ) local ScreenManager = require( 'lib.screenmanager.ScreenManager' ) local Faction = require( 'src.characters.Faction' ) @@ -17,7 +17,7 @@ local Faction = require( 'src.characters.Faction' ) -- Module -- ------------------------------------------------ -local GameScreen = {} +local GameScreen = Screen:subclass( 'GameScreen' ) -- ------------------------------------------------ -- Constants @@ -29,29 +29,17 @@ local FACTIONS = require( 'src.constants.FACTIONS' ) -- Constructor -- ------------------------------------------------ -function GameScreen.new() - local self = Screen.new() +function GameScreen:initialize( savegame ) + local playerFaction = Faction.new( FACTIONS.ALLIED, false ) - function self:init( savegame ) - local playerFaction = Faction.new( FACTIONS.ALLIED, false ) - - if savegame then - playerFaction:loadCharacters( savegame.factions[FACTIONS.ALLIED] ) - else - playerFaction:addCharacters( 10, 'human' ) - end - - local state = savegame and savegame.type or 'combat' - ScreenManager.push( state, playerFaction, savegame ) - end - - function self:draw() - end - - function self:update() + if savegame then + playerFaction:loadCharacters( savegame.factions[FACTIONS.ALLIED] ) + else + playerFaction:addCharacters( 10, 'human' ) end - return self + local state = savegame and savegame.type or 'combat' + ScreenManager.push( state, playerFaction, savegame ) end return GameScreen From 00e048e89db7ae17d13de24b211f9764262a5144 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Tue, 5 Dec 2017 03:04:07 +0100 Subject: [PATCH 042/178] Refactor HelpScreen --- src/ui/screens/HelpScreen.lua | 164 +++++++++++++++------------------- 1 file changed, 73 insertions(+), 91 deletions(-) diff --git a/src/ui/screens/HelpScreen.lua b/src/ui/screens/HelpScreen.lua index 245da5ee..04571eca 100644 --- a/src/ui/screens/HelpScreen.lua +++ b/src/ui/screens/HelpScreen.lua @@ -1,5 +1,5 @@ local ScreenManager = require( 'lib.screenmanager.ScreenManager' ) -local Screen = require( 'lib.screenmanager.Screen' ) +local Screen = require( 'src.ui.screens.Screen' ) local TexturePacks = require( 'src.ui.texturepacks.TexturePacks' ) local GridHelper = require( 'src.util.GridHelper' ) local UIBackground = require( 'src.ui.elements.UIBackground' ) @@ -10,7 +10,7 @@ local Translator = require( 'src.util.Translator' ) -- Module -- ------------------------------------------------ -local HelpScreen = {} +local HelpScreen = Screen:subclass( 'HelpScreen' ) -- ------------------------------------------------ -- Constants @@ -69,112 +69,94 @@ local HELP_TEXT = { } -- ------------------------------------------------ --- Constructor +-- Private Methods -- ------------------------------------------------ -function HelpScreen.new() - local self = Screen.new() - - -- ------------------------------------------------ - -- Private Attributes - -- ------------------------------------------------ - - local x, y - local header - local background - local outlines - local text - - -- ------------------------------------------------ - -- Private Methods - -- ------------------------------------------------ - - --- - -- Wraps the text into a Text object. - -- - local function assembleText() - text = love.graphics.newText( TexturePacks.getFont():get() ) - - local tw, th = TexturePacks.getTileDimensions() - local offset = 0 - - -- Draw sections first. - for i = 1, #HELP_TEXT do - text:addf({ TexturePacks.getColor( HELP_TEXT[i].color ), HELP_TEXT[i].text }, (UI_GRID_WIDTH-2) * tw, 'left', 0, offset * th ) +--- +-- Wraps the text into a Text object. +-- @treturn Text The text object. +-- +local function assembleText() + local text = love.graphics.newText( TexturePacks.getFont():get() ) + local tw, th = TexturePacks.getTileDimensions() + local offset = 0 + + -- Draw sections first. + for i = 1, #HELP_TEXT do + text:addf({ TexturePacks.getColor( HELP_TEXT[i].color ), HELP_TEXT[i].text }, (UI_GRID_WIDTH-2) * tw, 'left', 0, offset * th ) + offset = offset + 1 + + -- Draw sub items with a slight offset to the right. + for j = 1, #HELP_TEXT[i].children do offset = offset + 1 - - -- Draw sub items with a slight offset to the right. - for j = 1, #HELP_TEXT[i].children do - offset = offset + 1 - text:addf( HELP_TEXT[i].children[j], (UI_GRID_WIDTH-2) * tw, 'left', 4*tw, offset * th ) - end - - offset = offset + 2 - end - end - - --- - -- Generates the outlines for this screen. - -- - local function generateOutlines() - outlines = UIOutlines( x, y, 0, 0, UI_GRID_WIDTH, UI_GRID_HEIGHT ) - - -- Horizontal borders. - for ox = 0, UI_GRID_WIDTH-1 do - outlines:add( ox, 0 ) -- Top - outlines:add( ox, 2 ) -- Header - outlines:add( ox, UI_GRID_HEIGHT-1 ) -- Bottom + text:addf( HELP_TEXT[i].children[j], (UI_GRID_WIDTH-2) * tw, 'left', 4*tw, offset * th ) end - -- Vertical outlines. - for oy = 0, UI_GRID_HEIGHT-1 do - outlines:add( 0, oy ) -- Left - outlines:add( UI_GRID_WIDTH-1, oy ) -- Right - end - - outlines:refresh() + offset = offset + 2 end - -- ------------------------------------------------ - -- Public Methods - -- ------------------------------------------------ - - function self:init() - x, y = GridHelper.centerElement( UI_GRID_WIDTH, UI_GRID_HEIGHT ) + return text +end - background = UIBackground( x, y, 0, 0, UI_GRID_WIDTH, UI_GRID_HEIGHT ) +--- +-- Generates the outlines for this screen. +-- @tparam number x The origin of the screen along the x-axis. +-- @tparam number y The origin of the screen along the y-axis. +-- @treturn UIOutlines The newly created UIOutlines instance. +-- +local function generateOutlines( x, y ) + local outlines = UIOutlines( x, y, 0, 0, UI_GRID_WIDTH, UI_GRID_HEIGHT ) + + -- Horizontal borders. + for ox = 0, UI_GRID_WIDTH-1 do + outlines:add( ox, 0 ) -- Top + outlines:add( ox, 2 ) -- Header + outlines:add( ox, UI_GRID_HEIGHT-1 ) -- Bottom + end - generateOutlines() + -- Vertical outlines. + for oy = 0, UI_GRID_HEIGHT-1 do + outlines:add( 0, oy ) -- Left + outlines:add( UI_GRID_WIDTH-1, oy ) -- Right + end - assembleText() + outlines:refresh() + return outlines +end - header = love.graphics.newText( TexturePacks.getFont():get(), Translator.getText( 'ui_help_header' )) - end +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - function self:draw() - background:draw() - outlines:draw() +function HelpScreen:initialize() + self.x, self.y = GridHelper.centerElement( UI_GRID_WIDTH, UI_GRID_HEIGHT ) + self.background = UIBackground( self.x, self.y, 0, 0, UI_GRID_WIDTH, UI_GRID_HEIGHT ) + self.outlines = generateOutlines( self.x, self.y ) + self.text = assembleText() + self.header = love.graphics.newText( TexturePacks.getFont():get(), Translator.getText( 'ui_help_header' )) +end - local tw, th = TexturePacks.getTileDimensions() - TexturePacks.setColor( 'ui_text' ) - love.graphics.draw( header, (x+1) * tw, (y+1) * th ) - love.graphics.draw( text, (x+1) * tw, (y+3) * th ) - TexturePacks.resetColor() - end +function HelpScreen:draw() + self.background:draw() + self.outlines:draw() - function self:keypressed( key ) - if key == 'escape' then - ScreenManager.pop() - end - end + local tw, th = TexturePacks.getTileDimensions() + TexturePacks.setColor( 'ui_text' ) + love.graphics.draw( self.header, (self.x+1) * tw, (self.y+1) * th ) + love.graphics.draw( self.text, (self.x+1) * tw, (self.y+3) * th ) + TexturePacks.resetColor() +end - function self:resize( _, _ ) - x, y = GridHelper.centerElement( UI_GRID_WIDTH, UI_GRID_HEIGHT ) - background:setOrigin( x, y ) - outlines:setOrigin( x, y ) +function HelpScreen:keypressed( key ) + if key == 'escape' then + ScreenManager.pop() end +end - return self +function HelpScreen:resize( _, _ ) + self.x, self.y = GridHelper.centerElement( UI_GRID_WIDTH, UI_GRID_HEIGHT ) + self.background:setOrigin( self.x, self.y ) + self.outlines:setOrigin( self.x, self.y ) end return HelpScreen From 01a606dbf4a34a0a224d968485114d7faae87980 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Tue, 5 Dec 2017 03:03:58 +0100 Subject: [PATCH 043/178] Refactor CombatScreen --- src/ui/screens/CombatScreen.lua | 149 +++++++++++++++----------------- 1 file changed, 68 insertions(+), 81 deletions(-) diff --git a/src/ui/screens/CombatScreen.lua b/src/ui/screens/CombatScreen.lua index c2447e4b..bafa9764 100644 --- a/src/ui/screens/CombatScreen.lua +++ b/src/ui/screens/CombatScreen.lua @@ -3,7 +3,7 @@ -- local ScreenManager = require( 'lib.screenmanager.ScreenManager' ) -local Screen = require( 'lib.screenmanager.Screen' ) +local Screen = require( 'src.ui.screens.Screen' ) local CombatState = require( 'src.CombatState' ) local MapPainter = require( 'src.ui.MapPainter' ) local CameraHandler = require('src.ui.CameraHandler') @@ -16,108 +16,95 @@ local TexturePacks = require( 'src.ui.texturepacks.TexturePacks' ) -- Module -- ------------------------------------------------ -local CombatScreen = {} +local CombatScreen = Screen:subclass( 'CombatScreen' ) -- ------------------------------------------------ --- Constructor +-- Public Methods -- ------------------------------------------------ -function CombatScreen.new() - local self = Screen.new() +function CombatScreen:initialize( playerFaction, savegame ) + love.mouse.setVisible( true ) - local combatState - local mapPainter - local userInterface - local overlayPainter - local camera - local observations = {} - local tw, th = TexturePacks.getTileDimensions() - - function self:init( playerFaction, savegame ) - love.mouse.setVisible( true ) - - combatState = CombatState.new() - combatState:init( playerFaction, savegame ) + self.combatState = CombatState.new() + self.combatState:init( playerFaction, savegame ) - mapPainter = MapPainter( combatState:getMap() ) + self.mapPainter = MapPainter( self.combatState:getMap() ) - local mw, mh = combatState:getMap():getDimensions() - camera = CameraHandler.new( mw, mh, tw, th ) + local tw, th = TexturePacks.getTileDimensions() + local mw, mh = self.combatState:getMap():getDimensions() + self.camera = CameraHandler.new( mw, mh, tw, th ) - userInterface = UserInterface.new( combatState, camera ) + self.userInterface = UserInterface.new( self.combatState, self.camera ) + self.overlayPainter = OverlayPainter.new( self.combatState, self.camera ) - overlayPainter = OverlayPainter.new( combatState, camera ) - end - - function self:draw() - camera:attach() - mapPainter:draw() - overlayPainter:draw() - camera:detach() - userInterface:draw() - end + self.observations = {} + self.observations[#self.observations + 1] = Messenger.observe( 'SWITCH_CHARACTERS', function( character ) + if not self.combatState:getFactions():getPlayerFaction():canSee( character:getTile() ) then + return + end + self.camera:setTargetPosition( character:getTile():getX() * tw, character:getTile():getY() * th ) + end) - function self:update( dt ) - if self:isActive() then - camera:update( dt ) + self.observations[#self.observations + 1] = Messenger.observe( 'CHARACTER_MOVED', function( character ) + if not self.combatState:getFactions():getPlayerFaction():canSee( character:getTile() ) then + return end + self.camera:setTargetPosition( character:getTile():getX() * tw, character:getTile():getY() * th ) + end) +end - combatState:update( dt ) - mapPainter:setActiveFaction( combatState:getPlayerFaction() ) - mapPainter:update( dt ) - overlayPainter:update( dt ) - userInterface:update( dt ) +function CombatScreen:draw() + self.camera:attach() + self.mapPainter:draw() + self.overlayPainter:draw() + self.camera:detach() + self.userInterface:draw() +end + +function CombatScreen:update( dt ) + if self:isActive() then + self.camera:update( dt ) end - function self:keypressed( key, scancode, isrepeat ) - if scancode == 'f' then - love.window.setFullscreen( not love.window.getFullscreen() ) - end - if scancode == 'f1' then - userInterface:toggleDebugInfo() - end - if scancode == 'escape' then - ScreenManager.push( 'ingamemenu', combatState ) - end + self.combatState:update( dt ) + self.mapPainter:setActiveFaction( self.combatState:getPlayerFaction() ) + self.mapPainter:update( dt ) + self.overlayPainter:update( dt ) + self.userInterface:update( dt ) +end - combatState:keypressed( key, scancode, isrepeat ) +function CombatScreen:keypressed( key, scancode, isrepeat ) + if scancode == 'f' then + love.window.setFullscreen( not love.window.getFullscreen() ) end - - function self:mousepressed( _, _, button ) - local mx, my = camera:getMouseWorldGridPosition() - combatState:mousepressed( mx, my, button ) + if scancode == 'f1' then + self.userInterface:toggleDebugInfo() end - - function self:mousefocus( f ) - if f then - camera:unlock() - else - camera:lock() - end + if scancode == 'escape' then + ScreenManager.push( 'ingamemenu', self.combatState ) end - function self:close() - for i = 1, #observations do - Messenger.remove( observations[i] ) - end - combatState:close() - end + self.combatState:keypressed( key, scancode, isrepeat ) +end - observations[#observations + 1] = Messenger.observe( 'SWITCH_CHARACTERS', function( character ) - if not combatState:getFactions():getPlayerFaction():canSee( character:getTile() ) then - return - end - camera:setTargetPosition( character:getTile():getX() * tw, character:getTile():getY() * th ) - end) +function CombatScreen:mousepressed( _, _, button ) + local mx, my = self.camera:getMouseWorldGridPosition() + self.combatState:mousepressed( mx, my, button ) +end - observations[#observations + 1] = Messenger.observe( 'CHARACTER_MOVED', function( character ) - if not combatState:getFactions():getPlayerFaction():canSee( character:getTile() ) then - return - end - camera:setTargetPosition( character:getTile():getX() * tw, character:getTile():getY() * th ) - end) +function CombatScreen:mousefocus( f ) + if f then + self.camera:unlock() + else + self.camera:lock() + end +end - return self +function CombatScreen:close() + for i = 1, #self.observations do + Messenger.remove( self.observations[i] ) + end + self.combatState:close() end return CombatScreen From d73de856425227786dd7fdc39199372ae191f8d5 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Wed, 6 Dec 2017 01:43:12 +0100 Subject: [PATCH 044/178] Refactor MapEditor --- src/ui/screens/MapEditor.lua | 191 ++++++++++++++++------------------- 1 file changed, 85 insertions(+), 106 deletions(-) diff --git a/src/ui/screens/MapEditor.lua b/src/ui/screens/MapEditor.lua index c3aa3d99..874d18c3 100644 --- a/src/ui/screens/MapEditor.lua +++ b/src/ui/screens/MapEditor.lua @@ -6,7 +6,7 @@ -- Required Modules -- ------------------------------------------------ -local Screen = require( 'lib.screenmanager.Screen' ) +local Screen = require( 'src.ui.screens.Screen' ) local ScreenManager = require( 'lib.screenmanager.ScreenManager' ) local UIVerticalList = require( 'src.ui.elements.lists.UIVerticalList' ) local UIButton = require( 'src.ui.elements.UIButton' ) @@ -19,7 +19,7 @@ local UIContainer = require( 'src.ui.elements.UIContainer' ) -- Module -- ------------------------------------------------ -local MapEditor = {} +local MapEditor = Screen:subclass( 'MapEditor' ) -- ------------------------------------------------ -- Constants @@ -43,142 +43,121 @@ local PREFAB_SIZES = { } -- ------------------------------------------------ --- Constructor +-- Private Functions -- ------------------------------------------------ -function MapEditor.new() - local self = Screen.new() +local function createPrefabSelector( brush ) + local lx, ly = 1, 1 - -- ------------------------------------------------ - -- Private Variables - -- ------------------------------------------------ + local prefabSelector = UIVerticalList( lx, ly, 0, 0, SELECTOR_WIDTH, SELECTOR_HEIGHT ) - local camera - - local prefabSelector + local counter = 0 + for _, template in pairs( PREFAB_SIZES ) do + local function callback() + brush:setTemplate( template ) + end - local canvas - local brush + local str = string.format( '%2s (%sx%s)', template.TYPE, template.PARCEL_WIDTH, template.PARCEL_HEIGHT ) + local tmp = UIButton( lx, ly, 0, counter, SELECTOR_WIDTH, 1, callback, str, 'left' ) + prefabSelector:addChild( tmp ) - local uiContainer + counter = counter + 1 + end - -- ------------------------------------------------ - -- Private Functions - -- ------------------------------------------------ + return prefabSelector +end - local function createPrefabSelector() - local lx, ly = 1, 1 +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - prefabSelector = UIVerticalList( lx, ly, 0, 0, SELECTOR_WIDTH, SELECTOR_HEIGHT ) +function MapEditor:initialize() + love.filesystem.createDirectory( MAP_LAYOUT_DIRECTORY ) + love.mouse.setVisible( true ) - local counter = 0 - for _, template in pairs( PREFAB_SIZES ) do - local function callback() - brush:setTemplate( template ) - end + self.canvas = LayoutCanvas() + self.brush = LayoutBrush( PREFAB_SIZES[1] ) - local str = string.format( '%2s (%sx%s)', template.TYPE, template.PARCEL_WIDTH, template.PARCEL_HEIGHT ) - local tmp = UIButton( lx, ly, 0, counter, SELECTOR_WIDTH, 1, callback, str, 'left' ) - prefabSelector:addChild( tmp ) + self.prefabSelector = createPrefabSelector( self.brush ) - counter = counter + 1 - end - end + self.camera = CameraHandler.new( self.canvas:getWidth(), self.canvas:getHeight(), 16, 16 ) - -- ------------------------------------------------ - -- Public Methods - -- ------------------------------------------------ + self.uiContainer = UIContainer() + self.uiContainer:register( self.prefabSelector ) +end - function self:init() - love.filesystem.createDirectory( MAP_LAYOUT_DIRECTORY ) - love.mouse.setVisible( true ) +function MapEditor:receive( event, ... ) + if event == 'LOAD_LAYOUT' then + local newLayout = ... + self.canvas:setGrid( newLayout ) + end +end - canvas = LayoutCanvas() - brush = LayoutBrush( PREFAB_SIZES[1] ) +function MapEditor:draw() + if not self:isActive() then + return + end - createPrefabSelector() + self.uiContainer:draw() - camera = CameraHandler.new( canvas:getWidth(), canvas:getHeight(), 16, 16 ) + self.camera:attach() + self.canvas:draw() + self.brush:draw() + self.camera:detach() +end - uiContainer = UIContainer() - uiContainer:register( prefabSelector ) +function MapEditor:update( dt ) + if not self:isActive() then + return end - function self:receive( event, ... ) - if event == 'LOAD_LAYOUT' then - local newLayout = ... - canvas:setGrid( newLayout ) - end - end + self.camera:update( dt ) + self.brush:setPosition( self.camera:getMouseWorldGridPosition() ) + self.uiContainer:update() +end - function self:draw() - if not self:isActive() then - return - end +function MapEditor:mousemoved() + love.mouse.setVisible( true ) +end - uiContainer:draw() +function MapEditor:mousepressed() + self.brush:use( self.canvas ) + self.uiContainer:command( 'activate' ) +end - camera:attach() - canvas:draw() - brush:draw() - camera:detach() +function MapEditor:keypressed( _, scancode ) + if scancode == 'escape' then + ScreenManager.push( 'mapeditormenu', self.canvas ) end - function self:update( dt ) - if not self:isActive() then - return - end - - camera:update( dt ) - brush:setPosition( camera:getMouseWorldGridPosition() ) - uiContainer:update() + if scancode == 'tab' then + self.uiContainer:next() end - function self:mousemoved() - love.mouse.setVisible( true ) + if scancode == 'd' then + self.brush:setMode( 'draw' ) + elseif scancode == 'e' then + self.brush:setMode( 'erase' ) end - function self:mousepressed() - brush:use( canvas ) - uiContainer:command( 'activate' ) + if scancode == 'up' then + self.uiContainer:command( 'up' ) + elseif scancode == 'down' then + self.uiContainer:command( 'down' ) + elseif scancode == 'return' then + self.uiContainer:command( 'activate' ) end - function self:keypressed( _, scancode ) - if scancode == 'escape' then - ScreenManager.push( 'mapeditormenu', canvas ) - end - - if scancode == 'tab' then - uiContainer:next() - end - - if scancode == 'd' then - brush:setMode( 'draw' ) - elseif scancode == 'e' then - brush:setMode( 'erase' ) - end - - if scancode == 'up' then - uiContainer:command( 'up' ) - elseif scancode == 'down' then - uiContainer:command( 'down' ) - elseif scancode == 'return' then - uiContainer:command( 'activate' ) - end - - if scancode == 'w' then - canvas:resize( 0, -1 ) - elseif scancode == 's' then - canvas:resize( 0, 1 ) - end - if scancode == 'a' then - canvas:resize( -1, 0 ) - elseif scancode == 'd' then - canvas:resize( 1, 0 ) - end + if scancode == 'w' then + self.canvas:resize( 0, -1 ) + elseif scancode == 's' then + self.canvas:resize( 0, 1 ) + end + if scancode == 'a' then + self.canvas:resize( -1, 0 ) + elseif scancode == 'd' then + self.canvas:resize( 1, 0 ) end - - return self end return MapEditor From 1bd0f6a1427640ae1aa30ea14d9e2f3169b75be3 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Wed, 6 Dec 2017 01:50:21 +0100 Subject: [PATCH 045/178] Refactor IngameCombatMenu --- src/ui/screens/IngameCombatMenu.lua | 207 +++++++++++++--------------- 1 file changed, 98 insertions(+), 109 deletions(-) diff --git a/src/ui/screens/IngameCombatMenu.lua b/src/ui/screens/IngameCombatMenu.lua index 3bc4d1f2..92a1170c 100644 --- a/src/ui/screens/IngameCombatMenu.lua +++ b/src/ui/screens/IngameCombatMenu.lua @@ -1,7 +1,15 @@ -local Screen = require( 'lib.screenmanager.Screen' ); -local ScreenManager = require( 'lib.screenmanager.ScreenManager' ); -local SaveHandler = require( 'src.SaveHandler' ); -local Translator = require( 'src.util.Translator' ); +--- +-- @module IngameCombatMenu +-- + +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ + +local Screen = require( 'src.ui.screens.Screen' ) +local ScreenManager = require( 'lib.screenmanager.ScreenManager' ) +local SaveHandler = require( 'src.SaveHandler' ) +local Translator = require( 'src.util.Translator' ) local UIOutlines = require( 'src.ui.elements.UIOutlines' ) local UIBackground = require( 'src.ui.elements.UIBackground' ) local UIVerticalList = require( 'src.ui.elements.lists.UIVerticalList' ) @@ -14,7 +22,7 @@ local UIContainer = require( 'src.ui.elements.UIContainer' ) -- Module -- ------------------------------------------------ -local IngameCombatMenu = {} +local IngameCombatMenu = Screen:subclass( 'IngameCombatMenu' ) -- ------------------------------------------------ -- Constants @@ -24,53 +32,39 @@ local UI_GRID_WIDTH = 14 local UI_GRID_HEIGHT = 8 -- ------------------------------------------------ --- Constructor +-- Private Methods -- ------------------------------------------------ -function IngameCombatMenu.new() - local self = Screen.new(); - - -- ------------------------------------------------ - -- Private Variables - -- ------------------------------------------------ - - local game; - local buttonList; - - local background - local outlines - local x, y - local tw, th - - local container - - -- ------------------------------------------------ - -- Private Methods - -- ------------------------------------------------ - - --- - -- Generates the outlines for this screen. - -- - local function generateOutlines() - outlines = UIOutlines( x, y, 0, 0, UI_GRID_WIDTH, UI_GRID_HEIGHT ) - - -- Horizontal borders. - for ox = 0, UI_GRID_WIDTH-1 do - outlines:add( ox, 0 ) -- Top - outlines:add( ox, 2 ) -- Header - outlines:add( ox, UI_GRID_HEIGHT-1 ) -- Bottom - end - - -- Vertical outlines. - for oy = 0, UI_GRID_HEIGHT-1 do - outlines:add( 0, oy ) -- Left - outlines:add( UI_GRID_WIDTH-1, oy ) -- Right - end +--- +-- Generates the outlines for this screen. +-- @tparam number x The origin of the screen along the x-axis. +-- @tparam number y The origin of the screen along the y-axis. +-- @treturn UIOutlines The newly created UIOutlines instance. +-- +local function generateOutlines( x, y ) + local outlines = UIOutlines( x, y, 0, 0, UI_GRID_WIDTH, UI_GRID_HEIGHT ) + + -- Horizontal borders. + for ox = 0, UI_GRID_WIDTH-1 do + outlines:add( ox, 0 ) -- Top + outlines:add( ox, 2 ) -- Header + outlines:add( ox, UI_GRID_HEIGHT-1 ) -- Bottom + end - outlines:refresh() + -- Vertical outlines. + for oy = 0, UI_GRID_HEIGHT-1 do + outlines:add( 0, oy ) -- Left + outlines:add( UI_GRID_WIDTH-1, oy ) -- Right end + outlines:refresh() + return outlines +end + +local function createSaveGameButton( lx, ly, game ) + -- Create the callback for the save button. local function saveGame() + -- Create the callback for the confirmation dialog. local function confirmationCallback( name ) SaveHandler.save( game:serialize(), name ) ScreenManager.pop() -- Close input dialog. @@ -83,88 +77,83 @@ function IngameCombatMenu.new() ScreenManager.push( 'inputdialog', Translator.getText( 'ui_ingame_input_save_name' ), date, confirmationCallback ) end - local function createButtons() - local lx, ly = GridHelper.centerElement( UI_GRID_WIDTH, UI_GRID_HEIGHT ) - - buttonList = UIVerticalList( lx, ly, 0, 3, UI_GRID_WIDTH, UI_GRID_HEIGHT ) - - local saveGameButton = UIButton( lx, ly, 0, 0, UI_GRID_WIDTH, 1, saveGame, Translator.getText( 'ui_ingame_save_game' )) - buttonList:addChild( saveGameButton ) - - local openHelpButton = UIButton( lx, ly, 0, 0, UI_GRID_WIDTH, 1, function() ScreenManager.push( 'help' ) end, Translator.getText( 'ui_ingame_open_help' )) - buttonList:addChild( openHelpButton ) - - local exitButton = UIButton( lx, ly, 0, 0, UI_GRID_WIDTH, 1, function() ScreenManager.switch( 'mainmenu' ) end, Translator.getText( 'ui_ingame_exit' )) - buttonList:addChild( exitButton ) - - local resumeButton = UIButton( lx, ly, 0, 0, UI_GRID_WIDTH, 1, function() ScreenManager.pop() end, Translator.getText( 'ui_ingame_resume' )) - buttonList:addChild( resumeButton ) - end + return UIButton( lx, ly, 0, 0, UI_GRID_WIDTH, 1, saveGame, Translator.getText( 'ui_ingame_save_game' )) +end - -- ------------------------------------------------ - -- Public Methods - -- ------------------------------------------------ +local function createButtons( game ) + local lx, ly = GridHelper.centerElement( UI_GRID_WIDTH, UI_GRID_HEIGHT ) + local buttonList = UIVerticalList( lx, ly, 0, 3, UI_GRID_WIDTH, UI_GRID_HEIGHT ) - function self:init( ngame ) - game = ngame; + local saveGameButton = createSaveGameButton( lx, ly, game ) + buttonList:addChild( saveGameButton ) - tw, th = TexturePacks.getTileDimensions() - x, y = GridHelper.centerElement( UI_GRID_WIDTH, UI_GRID_HEIGHT ) + local openHelpButton = UIButton( lx, ly, 0, 0, UI_GRID_WIDTH, 1, function() ScreenManager.push( 'help' ) end, Translator.getText( 'ui_ingame_open_help' )) + buttonList:addChild( openHelpButton ) - background = UIBackground( x, y, 0, 0, UI_GRID_WIDTH, UI_GRID_HEIGHT ) + local exitButton = UIButton( lx, ly, 0, 0, UI_GRID_WIDTH, 1, function() ScreenManager.switch( 'mainmenu' ) end, Translator.getText( 'ui_ingame_exit' )) + buttonList:addChild( exitButton ) - generateOutlines() + local resumeButton = UIButton( lx, ly, 0, 0, UI_GRID_WIDTH, 1, function() ScreenManager.pop() end, Translator.getText( 'ui_ingame_resume' )) + buttonList:addChild( resumeButton ) - createButtons() + return buttonList +end - container = UIContainer() - container:register( buttonList ) - end +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - function self:draw() - background:draw() - outlines:draw() +function IngameCombatMenu:initialize( game ) + self.x, self.y = GridHelper.centerElement( UI_GRID_WIDTH, UI_GRID_HEIGHT ) + self.background = UIBackground( self.x, self.y, 0, 0, UI_GRID_WIDTH, UI_GRID_HEIGHT ) + self.outlines = generateOutlines( self.x, self.y ) + self.buttonList = createButtons( game ) + self.container = UIContainer() + self.container:register( self.buttonList ) +end - container:draw() - love.graphics.printf( Translator.getText( 'ui_ingame_paused' ), (x+1) * tw, (y+1) * th, (UI_GRID_WIDTH - 2) * tw, 'center' ) - end +function IngameCombatMenu:draw() + self.background:draw() + self.outlines:draw() - function self:update() - container:update() - end + self.container:draw() + local tw, th = TexturePacks.getTileDimensions() + love.graphics.printf( Translator.getText( 'ui_ingame_paused' ), (self.x+1) * tw, (self.y+1) * th, (UI_GRID_WIDTH - 2) * tw, 'center' ) +end - function self:keypressed( _, scancode ) - love.mouse.setVisible( false ) +function IngameCombatMenu:update() + self.container:update() +end - if scancode == 'up' then - container:command( 'up' ) - elseif scancode == 'down' then - container:command( 'down' ) - elseif scancode == 'return' then - container:command( 'activate' ) - end +function IngameCombatMenu:keypressed( _, scancode ) + love.mouse.setVisible( false ) - if scancode == 'escape' then - ScreenManager.pop(); - end + if scancode == 'up' then + self.container:command( 'up' ) + elseif scancode == 'down' then + self.container:command( 'down' ) + elseif scancode == 'return' then + self.container:command( 'activate' ) end - function self:mousereleased() - container:command( 'activate' ) + if scancode == 'escape' then + ScreenManager.pop() end +end - function self:mousemoved() - love.mouse.setVisible( true ) - end +function IngameCombatMenu:mousereleased() + self.container:command( 'activate' ) +end - function self:resize( _, _ ) - x, y = GridHelper.centerElement( UI_GRID_WIDTH, UI_GRID_HEIGHT ) - background:setOrigin( x, y ) - outlines:setOrigin( x, y ) - buttonList:setOrigin( x, y ) - end +function IngameCombatMenu:mousemoved() + love.mouse.setVisible( true ) +end - return self; +function IngameCombatMenu:resize( _, _ ) + self.x, self.y = GridHelper.centerElement( UI_GRID_WIDTH, UI_GRID_HEIGHT ) + self.background:setOrigin( self.x, self.y ) + self.outlines:setOrigin( self.x, self.y ) + self.buttonList:setOrigin( self.x, self.y ) end return IngameCombatMenu From b678899b12ab1d454120f6f9cc4d3ab29c8f834b Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Wed, 6 Dec 2017 02:05:12 +0100 Subject: [PATCH 046/178] Refactor InputDialog --- src/ui/screens/InputDialog.lua | 193 +++++++++++++++------------------ 1 file changed, 85 insertions(+), 108 deletions(-) diff --git a/src/ui/screens/InputDialog.lua b/src/ui/screens/InputDialog.lua index b8573a0c..e9002a09 100644 --- a/src/ui/screens/InputDialog.lua +++ b/src/ui/screens/InputDialog.lua @@ -6,7 +6,7 @@ -- Required Modules -- ------------------------------------------------ -local Screen = require( 'lib.screenmanager.Screen' ) +local Screen = require( 'src.ui.screens.Screen' ) local ScreenManager = require( 'lib.screenmanager.ScreenManager' ) local UIOutlines = require( 'src.ui.elements.UIOutlines' ) local UIBackground = require( 'src.ui.elements.UIBackground' ) @@ -17,7 +17,7 @@ local GridHelper = require( 'src.util.GridHelper' ) -- Module -- ------------------------------------------------ -local InputDialog = {} +local InputDialog = Screen:subclass( 'InputDialog' ) -- ------------------------------------------------ -- Constants @@ -29,139 +29,116 @@ local UI_GRID_HEIGHT = 6 local MAX_ENTRY_LENGTH = (UI_GRID_WIDTH - 4) * 2 -- ------------------------------------------------ --- Constructor +-- Private Methods -- ------------------------------------------------ -function InputDialog.new() - local self = Screen.new() - - local background - local outlines - local x, y - local tw, th - - local text - local entry - - local deleteAll - - local confirmCallback - - local timer - local showCursor - - -- ------------------------------------------------ - -- Private Methods - -- ------------------------------------------------ - - --- - -- Generates the outlines for this screen. - -- - local function generateOutlines() - outlines = UIOutlines( x, y, 0, 0, UI_GRID_WIDTH, UI_GRID_HEIGHT ) - - -- Horizontal borders. - for ox = 0, UI_GRID_WIDTH-1 do - outlines:add( ox, 0 ) -- Top - outlines:add( ox, UI_GRID_HEIGHT-1 ) -- Bottom - end - - -- Vertical outlines. - for oy = 0, UI_GRID_HEIGHT-1 do - outlines:add( 0, oy ) -- Left - outlines:add( UI_GRID_WIDTH-1, oy ) -- Right - end +--- +-- Generates the outlines for this screen. +-- @tparam number x The origin of the screen along the x-axis. +-- @tparam number y The origin of the screen along the y-axis. +-- @treturn UIOutlines The newly created UIOutlines instance. +-- +local function generateOutlines( x, y ) + local outlines = UIOutlines( x, y, 0, 0, UI_GRID_WIDTH, UI_GRID_HEIGHT ) - outlines:refresh() + -- Horizontal borders. + for ox = 0, UI_GRID_WIDTH-1 do + outlines:add( ox, 0 ) -- Top + outlines:add( ox, UI_GRID_HEIGHT-1 ) -- Bottom end - -- ------------------------------------------------ - -- Public Methods - -- ------------------------------------------------ + -- Vertical outlines. + for oy = 0, UI_GRID_HEIGHT-1 do + outlines:add( 0, oy ) -- Left + outlines:add( UI_GRID_WIDTH-1, oy ) -- Right + end - --- - -- Initialises the ConfirmationModal. - -- - function self:init( ntext, template, nconfirmCallback ) - timer = 0 + outlines:refresh() + return outlines +end - text = ntext - entry = template or '' - deleteAll = template and true or false +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - confirmCallback = nconfirmCallback +--- +-- Initialises the ConfirmationModal. +-- +function InputDialog:initialize( text, template, confirmCallback ) + self.timer = 0 - tw, th = TexturePacks.getTileDimensions() - x, y = GridHelper.centerElement( UI_GRID_WIDTH, UI_GRID_HEIGHT ) + self.text = text + self.entry = template or '' + self.deleteAll = template and true or false - background = UIBackground( x, y, 0, 0, UI_GRID_WIDTH, UI_GRID_HEIGHT ) + self.confirmCallback = confirmCallback - generateOutlines() - end + self.x, self.y = GridHelper.centerElement( UI_GRID_WIDTH, UI_GRID_HEIGHT ) + self.background = UIBackground( self.x, self.y, 0, 0, UI_GRID_WIDTH, UI_GRID_HEIGHT ) + self.outlines = generateOutlines( self.x, self.y ) +end - function self:update( dt ) - timer = timer + dt +function InputDialog:update( dt ) + self.timer = self.timer + dt - if timer > 0.5 then - showCursor = not showCursor - timer = 0 - end + if self.timer > 0.5 then + self.showCursor = not self.showCursor + self.timer = 0 end +end - --- - -- Draws the ConfirmationModal. - -- - function self:draw() - background:draw() - outlines:draw() +--- +-- Draws the ConfirmationModal. +-- +function InputDialog:draw() + local tw, th = TexturePacks.getTileDimensions() - -- Draw the instructions. - TexturePacks.setColor( 'ui_text' ) - love.graphics.printf( text, (x+1) * tw, (y+1) * th, (UI_GRID_WIDTH-2) * tw, 'left' ) + self.background:draw() + self.outlines:draw() - -- Draw the entered text. - TexturePacks.setColor( 'ui_text_dark' ) - love.graphics.printf( entry, (x+2) * tw, (y+3) * th, (UI_GRID_WIDTH-4) * tw, 'left' ) + -- Draw the instructions. + TexturePacks.setColor( 'ui_text' ) + love.graphics.printf( self.text, (self.x+1) * tw, (self.y+1) * th, (UI_GRID_WIDTH-2) * tw, 'left' ) - if showCursor and #entry < MAX_ENTRY_LENGTH then - TexturePacks.setColor( 'ui_text_dark' ) - love.graphics.print( '_', (x+2) * tw + TexturePacks.getFont():measureWidth( entry ), (y+3) * th ) - end + -- Draw the entered text. + TexturePacks.setColor( 'ui_text_dark' ) + love.graphics.printf( self.entry, (self.x+2) * tw, (self.y+3) * th, (UI_GRID_WIDTH-4) * tw, 'left' ) - TexturePacks.resetColor() + if self.showCursor and #self.entry < MAX_ENTRY_LENGTH then + TexturePacks.setColor( 'ui_text_dark' ) + love.graphics.print( '_', (self.x+2) * tw + TexturePacks.getFont():measureWidth( self.entry ), (self.y+3) * th ) end - --- - -- Handle keypressed events. - -- - function self:keypressed( _, scancode ) - if scancode == 'escape' then - ScreenManager.pop() - elseif scancode == 'return' then - confirmCallback( entry ) - end + TexturePacks.resetColor() +end - if scancode == 'backspace' then - if deleteAll then - entry = '' - deleteAll = false - else - entry = entry:sub( 1, #entry-1 ) - end - end +--- +-- Handle keypressed events. +-- +function InputDialog:keypressed( _, scancode ) + if scancode == 'escape' then + ScreenManager.pop() + elseif scancode == 'return' then + self.confirmCallback( self.entry ) end - --- - -- Handle textinput events. - -- - function self:textinput( character ) - local c = character:match( '[%w_]' ) - if c and #entry < MAX_ENTRY_LENGTH then - entry = entry .. c + if scancode == 'backspace' then + if self.deleteAll then + self.entry = '' + else + self.entry = self.entry:sub( 1, #self.entry-1 ) end end +end - return self +--- +-- Handle textinput events. +-- +function InputDialog:textinput( character ) + local c = character:match( '[%w_]' ) + if c and #self.entry < MAX_ENTRY_LENGTH then + self.entry = self.entry .. c + end end return InputDialog From 7eea731523487f8988ded2eda15026cb6bf8fbdc Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Wed, 6 Dec 2017 02:15:02 +0100 Subject: [PATCH 047/178] Refactor ConfirmationModal --- src/ui/screens/ConfirmationModal.lua | 207 +++++++++++++-------------- 1 file changed, 98 insertions(+), 109 deletions(-) diff --git a/src/ui/screens/ConfirmationModal.lua b/src/ui/screens/ConfirmationModal.lua index db7d864b..652a5a00 100644 --- a/src/ui/screens/ConfirmationModal.lua +++ b/src/ui/screens/ConfirmationModal.lua @@ -8,8 +8,8 @@ -- Required Modules -- ------------------------------------------------ -local Screen = require( 'lib.screenmanager.Screen' ) -local Translator = require( 'src.util.Translator' ); +local Screen = require( 'src.ui.screens.Screen' ) +local Translator = require( 'src.util.Translator' ) local UIOutlines = require( 'src.ui.elements.UIOutlines' ) local UIBackground = require( 'src.ui.elements.UIBackground' ) local UIHorizontalList = require( 'src.ui.elements.lists.UIHorizontalList' ) @@ -21,7 +21,7 @@ local GridHelper = require( 'src.util.GridHelper' ) -- Module -- ------------------------------------------------ -local ConfirmationModal = {} +local ConfirmationModal = Screen:subclass( 'ConfirmationModal' ) -- ------------------------------------------------ -- Constants @@ -33,135 +33,124 @@ local UI_GRID_HEIGHT = 10 local BUTTON_LIST_WIDTH = 20 -- ------------------------------------------------ --- Constructor +-- Private Methods -- ------------------------------------------------ -function ConfirmationModal.new() - local self = Screen.new() - - local background - local outlines - local x, y - local tw, th - - local buttonList - - local text - local trueCallback - local falseCallback - - -- ------------------------------------------------ - -- Private Methods - -- ------------------------------------------------ +--- +-- Generates the outlines for this screen. +-- @tparam number x The origin of the screen along the x-axis. +-- @tparam number y The origin of the screen along the y-axis. +-- @treturn UIOutlines The newly created UIOutlines instance. +-- +local function generateOutlines( x, y ) + local outlines = UIOutlines( x, y, 0, 0, UI_GRID_WIDTH, UI_GRID_HEIGHT ) - --- - -- Generates the outlines for this screen. - -- - local function generateOutlines() - outlines = UIOutlines( x, y, 0, 0, UI_GRID_WIDTH, UI_GRID_HEIGHT ) + -- Horizontal borders. + for ox = 0, UI_GRID_WIDTH-1 do + outlines:add( ox, 0 ) -- Top + outlines:add( ox, UI_GRID_HEIGHT-1 ) -- Bottom + end - -- Horizontal borders. - for ox = 0, UI_GRID_WIDTH-1 do - outlines:add( ox, 0 ) -- Top - outlines:add( ox, UI_GRID_HEIGHT-1 ) -- Bottom - end + -- Vertical outlines. + for oy = 0, UI_GRID_HEIGHT-1 do + outlines:add( 0, oy ) -- Left + outlines:add( UI_GRID_WIDTH-1, oy ) -- Right + end - -- Vertical outlines. - for oy = 0, UI_GRID_HEIGHT-1 do - outlines:add( 0, oy ) -- Left - outlines:add( UI_GRID_WIDTH-1, oy ) -- Right - end + outlines:refresh() + return outlines +end - outlines:refresh() - end +--- +-- Creates the yes and no button at the bottom of the screen. +-- @tparam number y The origin of the screen along the y-axis. +-- @tparam function trueCallback The function to call when the yes button is clicked. +-- @tparam function falseCallback The function to call when the no button is clicked. +-- @treturn UIHorizontalList The button list. +-- +local function createButtons( y, trueCallback, falseCallback ) + local lx = GridHelper.centerElement( BUTTON_LIST_WIDTH, 1 ) + local ly = y + UI_GRID_HEIGHT - 2 - --- - -- Creates the yes and no button at the bottom of the screen. - -- - local function createButtons() - local lx = GridHelper.centerElement( BUTTON_LIST_WIDTH, 1 ) - local ly = y + UI_GRID_HEIGHT - 2 + local buttonList = UIHorizontalList( lx, ly, 0, 0, BUTTON_LIST_WIDTH, 1 ) - buttonList = UIHorizontalList( lx, ly, 0, 0, BUTTON_LIST_WIDTH, 1 ) + local buttonWidth = math.floor( BUTTON_LIST_WIDTH * 0.5 ) - local buttonWidth = math.floor( BUTTON_LIST_WIDTH * 0.5 ) + local yesButton = UIButton( lx, ly, 0, 0, buttonWidth, 1, trueCallback, Translator.getText( 'ui_yes' )) + buttonList:addChild( yesButton ) - local yesButton = UIButton( lx, ly, 0, 0, buttonWidth, 1, trueCallback, Translator.getText( 'ui_yes' )) - buttonList:addChild( yesButton ) + local noButton = UIButton( lx, ly, 0, 0, buttonWidth, 1, falseCallback, Translator.getText( 'ui_no' )) + buttonList:addChild( noButton ) - local noButton = UIButton( lx, ly, 0, 0, buttonWidth, 1, falseCallback, Translator.getText( 'ui_no' )) - buttonList:addChild( noButton ) - end + return buttonList +end - -- ------------------------------------------------ - -- Public Methods - -- ------------------------------------------------ +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - --- - -- Initialises the ConfirmationModal. - -- @tparam string ntext The text to display inside of the modal. - -- @tparam function ntrueCallback The function to call when the yes button is clicked. - -- @tparam function nfalseCallback The function to call when the no button is clicked. - -- - function self:init( ntext, ntrueCallback, nfalseCallback ) - text = ntext - trueCallback = ntrueCallback - falseCallback = nfalseCallback +--- +-- Initialises the ConfirmationModal. +-- @tparam string text The text to display inside of the modal. +-- @tparam function trueCallback The function to call when the yes button is clicked. +-- @tparam function falseCallback The function to call when the no button is clicked. +-- +function ConfirmationModal:initialize( text, trueCallback, falseCallback ) + self.text = text + self.trueCallback = trueCallback + self.falseCallback = falseCallback - tw, th = TexturePacks.getTileDimensions() - x, y = GridHelper.centerElement( UI_GRID_WIDTH, UI_GRID_HEIGHT ) + self.x, self.y = GridHelper.centerElement( UI_GRID_WIDTH, UI_GRID_HEIGHT ) - background = UIBackground( x, y, 0, 0, UI_GRID_WIDTH, UI_GRID_HEIGHT ) + self.background = UIBackground( self.x, self.y, 0, 0, UI_GRID_WIDTH, UI_GRID_HEIGHT ) - generateOutlines() - createButtons() - end + self.outlines = generateOutlines( self.x, self.y ) + self.buttonList = createButtons( self.y, self.trueCallback, self.falseCallback ) +end - --- - -- Draws the ConfirmationModal. - -- - function self:draw() - background:draw() - outlines:draw() - buttonList:draw() - - TexturePacks.setColor( 'ui_text' ) - love.graphics.printf( text, (x+2) * tw, (y+2) * th, (UI_GRID_WIDTH-4) * tw, 'center' ) - TexturePacks.resetColor() - end +--- +-- Draws the ConfirmationModal. +-- +function ConfirmationModal:draw() + self.background:draw() + self.outlines:draw() + self.buttonList:draw() + + local tw, th = TexturePacks.getTileDimensions() + TexturePacks.setColor( 'ui_text' ) + love.graphics.printf( self.text, (self.x+2) * tw, (self.y+2) * th, (UI_GRID_WIDTH-4) * tw, 'center' ) + TexturePacks.resetColor() +end - --- - -- Updates the ConfirmationModal. - -- - function self:update() - buttonList:update() - end +--- +-- Updates the ConfirmationModal. +-- +function ConfirmationModal:update() + self.buttonList:update() +end - --- - -- Handle keypressed events. - -- - function self:keypressed( key, scancode ) - if key == 'escape' then - falseCallback() - end - - if scancode == 'left' then - buttonList:command( 'left' ) - elseif scancode == 'right' then - buttonList:command( 'right' ) - elseif scancode == 'return' then - buttonList:command( 'activate' ) - end +--- +-- Handle keypressed events. +-- +function ConfirmationModal:keypressed( key, scancode ) + if key == 'escape' then + self.falseCallback() end - --- - -- Handle mousereleased events. - -- - function self:mousereleased() - buttonList:command( 'activate' ) + if scancode == 'left' then + self.buttonList:command( 'left' ) + elseif scancode == 'right' then + self.buttonList:command( 'right' ) + elseif scancode == 'return' then + self.buttonList:command( 'activate' ) end +end - return self +--- +-- Handle mousereleased events. +-- +function ConfirmationModal:mousereleased() + self.buttonList:command( 'activate' ) end return ConfirmationModal From 2f829cfaaa0215f4f7d55afecf439ddea069c813 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Wed, 6 Dec 2017 02:15:33 +0100 Subject: [PATCH 048/178] Refactor MapTest --- src/ui/screens/MapTest.lua | 93 +++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 47 deletions(-) diff --git a/src/ui/screens/MapTest.lua b/src/ui/screens/MapTest.lua index 9db22f80..80c2e8cf 100644 --- a/src/ui/screens/MapTest.lua +++ b/src/ui/screens/MapTest.lua @@ -1,4 +1,12 @@ -local Screen = require( 'lib.screenmanager.Screen' ) +--- +-- @module MapTest +-- + +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ + +local Screen = require( 'src.ui.screens.Screen' ) local ScreenManager = require( 'lib.screenmanager.ScreenManager' ) local MapPainter = require( 'src.ui.MapPainter' ) local CameraHandler = require('src.ui.CameraHandler') @@ -11,68 +19,59 @@ local Map = require( 'src.map.Map' ) -- Module -- ------------------------------------------------ -local MapTest = {} +local MapTest = Screen:subclass( 'MapTest' ) -- ------------------------------------------------ --- Constructor +-- Private Methods -- ------------------------------------------------ -function MapTest.new() - local self = Screen.new() - - local layout - local map, mw, mh - local mapPainter - local camera +local function createMap( layout ) + local generator = ProceduralMapGenerator( layout ) - local function createMap() - local generator = ProceduralMapGenerator( layout ) + local tiles = generator:getTiles() + local mw, mh = generator:getTileGridDimensions() - local tiles = generator:getTiles() - mw, mh = generator:getTileGridDimensions() + local map = Map.new() + map:init( tiles, mw, mh ) + map:setSpawnpoints( generator:getSpawnpoints() ) - map = Map.new() - map:init( tiles, mw, mh ) - map:setSpawnpoints( generator:getSpawnpoints() ) - end - - function self:init( nlayout ) - layout = nlayout - - ProceduralMapGenerator.load() - PrefabLoader.load() + return map, mw, mh +end - createMap() +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - mapPainter = MapPainter( map ) +function MapTest:initialize( layout ) + ProceduralMapGenerator.load() + PrefabLoader.load() - camera = CameraHandler.new( mw, mh, TexturePacks.getTileDimensions() ) - end + self.map, self.mw, self.mh = createMap( layout ) + self.mapPainter = MapPainter( self.map ) + self.camera = CameraHandler.new( self.mw, self.mh, TexturePacks.getTileDimensions() ) +end - function self:draw() - TexturePacks.setColor( 'sys_background' ) - love.graphics.rectangle( 'fill', 0, 0, love.graphics.getDimensions() ) - TexturePacks.resetColor() +function MapTest:draw() + TexturePacks.setColor( 'sys_background' ) + love.graphics.rectangle( 'fill', 0, 0, love.graphics.getDimensions() ) + TexturePacks.resetColor() - camera:attach() - mapPainter:draw() - camera:detach() - end + self.camera:attach() + self.mapPainter:draw() + self.camera:detach() +end - function self:update( dt ) - if mapPainter then - mapPainter:update() - end - camera:update( dt ) +function MapTest:update( dt ) + if self.mapPainter then + self.mapPainter:update() end + self.camera:update( dt ) +end - function self:keypressed( _, scancode ) - if scancode == 'escape' then - ScreenManager.pop() - end +function MapTest:keypressed( _, scancode ) + if scancode == 'escape' then + ScreenManager.pop() end - - return self end return MapTest From 00d1df2365cf00aa453233e2e5919adf2eb596c0 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Wed, 6 Dec 2017 02:30:10 +0100 Subject: [PATCH 049/178] Refactor MapEditorMenu --- src/ui/screens/MapEditorMenu.lua | 203 +++++++++++++++---------------- 1 file changed, 96 insertions(+), 107 deletions(-) diff --git a/src/ui/screens/MapEditorMenu.lua b/src/ui/screens/MapEditorMenu.lua index e45019e5..684339e8 100644 --- a/src/ui/screens/MapEditorMenu.lua +++ b/src/ui/screens/MapEditorMenu.lua @@ -6,7 +6,7 @@ -- Required Modules -- ------------------------------------------------ -local Screen = require( 'lib.screenmanager.Screen' ) +local Screen = require( 'src.ui.screens.Screen' ) local ScreenManager = require( 'lib.screenmanager.ScreenManager' ) local Translator = require( 'src.util.Translator' ) local UIOutlines = require( 'src.ui.elements.UIOutlines' ) @@ -22,7 +22,7 @@ local UIContainer = require( 'src.ui.elements.UIContainer' ) -- Module -- ------------------------------------------------ -local MapEditorMenu = {} +local MapEditorMenu = Screen:subclass( 'MapEditorMenu' ) -- ------------------------------------------------ -- Constants @@ -37,50 +37,35 @@ local SAVE_DIR = 'mods/maps/layouts/' local FILE_EXTENSION = '.layout' -- ------------------------------------------------ --- Constructor +-- Private Methods -- ------------------------------------------------ -function MapEditorMenu.new() - local self = Screen.new() - - -- ------------------------------------------------ - -- Private Variables - -- ------------------------------------------------ - - local buttonList - local container - - local background - local outlines - local x, y - - local canvas - - -- ------------------------------------------------ - -- Private Methods - -- ------------------------------------------------ - - --- - -- Generates the outlines for this screen. - -- - local function generateOutlines() - outlines = UIOutlines( x, y, 0, 0, UI_GRID_WIDTH, UI_GRID_HEIGHT ) - - -- Horizontal borders. - for ox = 0, UI_GRID_WIDTH-1 do - outlines:add( ox, 0 ) -- Top - outlines:add( ox, UI_GRID_HEIGHT-1 ) -- Bottom - end +--- +-- Generates the outlines for this screen. +-- @tparam number x The origin of the screen along the x-axis. +-- @tparam number y The origin of the screen along the y-axis. +-- @treturn UIOutlines The newly created UIOutlines instance. +-- +local function generateOutlines( x, y ) + local outlines = UIOutlines( x, y, 0, 0, UI_GRID_WIDTH, UI_GRID_HEIGHT ) - -- Vertical outlines. - for oy = 0, UI_GRID_HEIGHT-1 do - outlines:add( 0, oy ) -- Left - outlines:add( UI_GRID_WIDTH-1, oy ) -- Right - end + -- Horizontal borders. + for ox = 0, UI_GRID_WIDTH-1 do + outlines:add( ox, 0 ) -- Top + outlines:add( ox, UI_GRID_HEIGHT-1 ) -- Bottom + end - outlines:refresh() + -- Vertical outlines. + for oy = 0, UI_GRID_HEIGHT-1 do + outlines:add( 0, oy ) -- Left + outlines:add( UI_GRID_WIDTH-1, oy ) -- Right end + outlines:refresh() + return outlines +end + +local function createSavePrefabButton( lx, ly, canvas ) local function savePrefab() local function confirmCallback( name ) Log.debug( 'Saving a new layout: ' .. name .. FILE_EXTENSION, 'MapEditorMenu' ) @@ -92,102 +77,106 @@ function MapEditorMenu.new() ScreenManager.push( 'inputdialog', Translator.getText( 'ui_mapeditor_enter_name' ), false, confirmCallback ) end - local function loadPrefab() - ScreenManager.push( 'editorloading', SAVE_DIR ) - end + return UIButton( lx, ly, 0, 0, UI_GRID_WIDTH, 1, savePrefab, Translator.getText( 'ui_mapeditor_save' )) +end + +local function loadPrefab() + ScreenManager.push( 'editorloading', SAVE_DIR ) +end +local function createTestMapButton( lx, ly, canvas ) local function testMap() ScreenManager.pop() ScreenManager.push( 'maptest', canvas:getGrid() ) end + return UIButton( lx, ly, 0, 0, UI_GRID_WIDTH, 1, testMap, Translator.getText( 'ui_mapeditor_test' )) +end - local function createButtons() - local lx, ly = GridHelper.centerElement( UI_GRID_WIDTH, UI_GRID_HEIGHT ) +local function createButtons( canvas ) + local lx, ly = GridHelper.centerElement( UI_GRID_WIDTH, UI_GRID_HEIGHT ) - buttonList = UIVerticalList( lx, ly, 0, BUTTON_LIST_VERTICAL_OFFSET, UI_GRID_WIDTH, UI_GRID_HEIGHT ) + local buttonList = UIVerticalList( lx, ly, 0, BUTTON_LIST_VERTICAL_OFFSET, UI_GRID_WIDTH, UI_GRID_HEIGHT ) - local savePrefabButton = UIButton( lx, ly, 0, 0, UI_GRID_WIDTH, 1, savePrefab, Translator.getText( 'ui_mapeditor_save' )) - buttonList:addChild( savePrefabButton ) + local savePrefabButton = createSavePrefabButton( lx, ly, canvas ) + buttonList:addChild( savePrefabButton ) - local loadPrefabButton = UIButton( lx, ly, 0, 0, UI_GRID_WIDTH, 1, loadPrefab, Translator.getText( 'ui_mapeditor_load' )) - buttonList:addChild( loadPrefabButton ) + local loadPrefabButton = UIButton( lx, ly, 0, 0, UI_GRID_WIDTH, 1, loadPrefab, Translator.getText( 'ui_mapeditor_load' )) + buttonList:addChild( loadPrefabButton ) - local testButton = UIButton( lx, ly, 0, 0, UI_GRID_WIDTH, 1, testMap, Translator.getText( 'ui_mapeditor_test' )) - buttonList:addChild( testButton ) + local testButton = createTestMapButton( lx, ly, canvas ) + buttonList:addChild( testButton ) - local switchButton = UIButton( lx, ly, 0, 0, UI_GRID_WIDTH, 1, function() ScreenManager.switch( 'prefabeditor' ) end, Translator.getText( 'ui_mapeditor_switch' )) - buttonList:addChild( switchButton ) + local switchButton = UIButton( lx, ly, 0, 0, UI_GRID_WIDTH, 1, function() ScreenManager.switch( 'prefabeditor' ) end, Translator.getText( 'ui_mapeditor_switch' )) + buttonList:addChild( switchButton ) - local exitButton = UIButton( lx, ly, 0, 0, UI_GRID_WIDTH, 1, function() ScreenManager.switch( 'mainmenu' ) end, Translator.getText( 'ui_mapeditor_exit' )) - buttonList:addChild( exitButton ) + local exitButton = UIButton( lx, ly, 0, 0, UI_GRID_WIDTH, 1, function() ScreenManager.switch( 'mainmenu' ) end, Translator.getText( 'ui_mapeditor_exit' )) + buttonList:addChild( exitButton ) - buttonList:setFocus( true ) - end + buttonList:setFocus( true ) + return buttonList +end - -- ------------------------------------------------ - -- Public Methods - -- ------------------------------------------------ +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - function self:init( ncanvas ) - canvas = ncanvas +function MapEditorMenu:initialize( canvas ) + self.canvas = canvas - x, y = GridHelper.centerElement( UI_GRID_WIDTH, UI_GRID_HEIGHT ) + self.x, self.y = GridHelper.centerElement( UI_GRID_WIDTH, UI_GRID_HEIGHT ) - background = UIBackground( x, y, 0, 0, UI_GRID_WIDTH, UI_GRID_HEIGHT ) - generateOutlines() - createButtons() + self.background = UIBackground( self.x, self.y, 0, 0, UI_GRID_WIDTH, UI_GRID_HEIGHT ) + self.outlines = generateOutlines( self.x, self.y ) + self.buttonList = createButtons( self.canvas ) - container = UIContainer() - container:register( buttonList ) - end + self.container = UIContainer() + self.container:register( self.buttonList ) +end - function self:draw() - if not self:isActive() then - return - end - background:draw() - outlines:draw() - container:draw() +function MapEditorMenu:draw() + if not self:isActive() then + return end + self.background:draw() + self.outlines:draw() + self.container:draw() +end - function self:update() - if not self:isActive() then - return - end - container:update() +function MapEditorMenu:update() + if not self:isActive() then + return end + self.container:update() +end - function self:keypressed( _, scancode ) - love.mouse.setVisible( false ) - if scancode == 'escape' then - ScreenManager.pop() - end - - if scancode == 'up' then - container:command( 'up' ) - elseif scancode == 'down' then - container:command( 'down' ) - elseif scancode == 'return' then - container:command( 'activate' ) - end +function MapEditorMenu:keypressed( _, scancode ) + love.mouse.setVisible( false ) + if scancode == 'escape' then + ScreenManager.pop() end - function self:mousemoved() - love.mouse.setVisible( true ) + if scancode == 'up' then + self.container:command( 'up' ) + elseif scancode == 'down' then + self.container:command( 'down' ) + elseif scancode == 'return' then + self.container:command( 'activate' ) end +end - function self:mousereleased() - container:command( 'activate' ) - end +function MapEditorMenu:mousemoved() + love.mouse.setVisible( true ) +end - function self:resize( _, _ ) - x, y = GridHelper.centerElement( UI_GRID_WIDTH, UI_GRID_HEIGHT ) - background:setOrigin( x, y ) - outlines:setOrigin( x, y ) - buttonList:setOrigin( x, y ) - end +function MapEditorMenu:mousereleased() + self.container:command( 'activate' ) +end - return self +function MapEditorMenu:resize( _, _ ) + self.x, self.y = GridHelper.centerElement( UI_GRID_WIDTH, UI_GRID_HEIGHT ) + self.background:setOrigin( self.x, self.y ) + self.outlines:setOrigin( self.x, self.y ) + self.buttonList:setOrigin( self.x, self.y ) end return MapEditorMenu From c21d81458eb44a185ef71c5bc967755e6e741693 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Wed, 6 Dec 2017 02:41:10 +0100 Subject: [PATCH 050/178] Refactor GameOverScreen --- src/ui/screens/GameOverScreen.lua | 115 +++++++++++++----------------- 1 file changed, 49 insertions(+), 66 deletions(-) diff --git a/src/ui/screens/GameOverScreen.lua b/src/ui/screens/GameOverScreen.lua index cc039661..91cfe18a 100644 --- a/src/ui/screens/GameOverScreen.lua +++ b/src/ui/screens/GameOverScreen.lua @@ -7,7 +7,7 @@ -- ------------------------------------------------ local ScreenManager = require( 'lib.screenmanager.ScreenManager' ) -local Screen = require( 'lib.screenmanager.Screen' ) +local Screen = require( 'src.ui.screens.Screen' ) local Translator = require( 'src.util.Translator' ) local TexturePacks = require( 'src.ui.texturepacks.TexturePacks' ) local GridHelper = require( 'src.util.GridHelper' ) @@ -18,7 +18,7 @@ local UIOutlines = require( 'src.ui.elements.UIOutlines' ) -- Module -- ------------------------------------------------ -local GameOverScreen = {} +local GameOverScreen = Screen:subclass( 'GameOverScreen' ) -- ------------------------------------------------ -- Constants @@ -28,83 +28,66 @@ local UI_GRID_WIDTH = 30 local UI_GRID_HEIGHT = 16 -- ------------------------------------------------ --- Constructor +-- Private Methods -- ------------------------------------------------ -function GameOverScreen.new() - local self = Screen.new() - - -- ------------------------------------------------ - -- Private Attributes - -- ------------------------------------------------ - - local text - local outlines - local background - local x, y - local win - local playerFaction - - -- ------------------------------------------------ - -- Private Methods - -- ------------------------------------------------ - - --- - -- Generates the outlines for this screen. - -- - local function generateOutlines() - outlines = UIOutlines( x, y, 0, 0, UI_GRID_WIDTH, UI_GRID_HEIGHT ) - - -- Horizontal borders. - for ox = 0, UI_GRID_WIDTH-1 do - outlines:add( ox, 0 ) -- Top - outlines:add( ox, UI_GRID_HEIGHT-1 ) -- Bottom - end - - -- Vertical outlines. - for oy = 0, UI_GRID_HEIGHT-1 do - outlines:add( 0, oy ) -- Left - outlines:add( UI_GRID_WIDTH-1, oy ) -- Right - end - - outlines:refresh() +--- +-- Generates the outlines for this screen. +-- @tparam number x The origin of the screen along the x-axis. +-- @tparam number y The origin of the screen along the y-axis. +-- @treturn UIOutlines The newly created UIOutlines instance. +-- +local function generateOutlines( x, y ) + local outlines = UIOutlines( x, y, 0, 0, UI_GRID_WIDTH, UI_GRID_HEIGHT ) + + -- Horizontal borders. + for ox = 0, UI_GRID_WIDTH-1 do + outlines:add( ox, 0 ) -- Top + outlines:add( ox, UI_GRID_HEIGHT-1 ) -- Bottom end - -- ------------------------------------------------ - -- Public Methods - -- ------------------------------------------------ + -- Vertical outlines. + for oy = 0, UI_GRID_HEIGHT-1 do + outlines:add( 0, oy ) -- Left + outlines:add( UI_GRID_WIDTH-1, oy ) -- Right + end + + outlines:refresh() + return outlines +end + +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - function self:init( nplayerFaction, nwin ) - playerFaction = nplayerFaction - win = nwin +function GameOverScreen:initialize( playerFaction, win ) + self.playerFaction = playerFaction + self.win = win - x, y = GridHelper.centerElement( UI_GRID_WIDTH, UI_GRID_HEIGHT ) + self.x, self.y = GridHelper.centerElement( UI_GRID_WIDTH, UI_GRID_HEIGHT ) - background = UIBackground( x, y, 0, 0, UI_GRID_WIDTH, UI_GRID_HEIGHT ) + self.background = UIBackground( self.x, self.y, 0, 0, UI_GRID_WIDTH, UI_GRID_HEIGHT ) - generateOutlines() + self.outlines = generateOutlines( self.x, self.y ) - text = win and Translator.getText( 'ui_win' ) or Translator.getText( 'ui_lose' ) - end + self.text = self.win and Translator.getText( 'ui_win' ) or Translator.getText( 'ui_lose' ) +end - function self:draw() - background:draw() - outlines:draw() +function GameOverScreen:draw() + self.background:draw() + self.outlines:draw() - local tw, th = TexturePacks.getTileDimensions() - love.graphics.printf( text, x * tw, (y+6) * th, UI_GRID_WIDTH * tw, 'center' ) - end + local tw, th = TexturePacks.getTileDimensions() + love.graphics.printf( self.text, self.x * tw, (self.y+6) * th, UI_GRID_WIDTH * tw, 'center' ) +end - function self:keypressed() - if win then - ScreenManager.pop() - ScreenManager.push( 'base', playerFaction ) - else - ScreenManager.switch( 'mainmenu' ) - end +function GameOverScreen:keypressed() + if self.win then + ScreenManager.pop() + ScreenManager.push( 'base', self.playerFaction ) + else + ScreenManager.switch( 'mainmenu' ) end - - return self end return GameOverScreen From 75a8c20acb4b20b49ba1c7c9b44b0743e6a73d95 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Wed, 6 Dec 2017 02:53:02 +0100 Subject: [PATCH 051/178] Refactor HealthScreen --- src/ui/screens/HealthScreen.lua | 208 ++++++++++++++++---------------- 1 file changed, 101 insertions(+), 107 deletions(-) diff --git a/src/ui/screens/HealthScreen.lua b/src/ui/screens/HealthScreen.lua index 6043017e..4e284fa9 100644 --- a/src/ui/screens/HealthScreen.lua +++ b/src/ui/screens/HealthScreen.lua @@ -1,6 +1,14 @@ -local ScreenManager = require( 'lib.screenmanager.ScreenManager' ); -local Screen = require( 'lib.screenmanager.Screen' ); -local Translator = require( 'src.util.Translator' ); +--- +-- @module HealthScreen +-- + +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ + +local ScreenManager = require( 'lib.screenmanager.ScreenManager' ) +local Screen = require( 'src.ui.screens.Screen' ) +local Translator = require( 'src.util.Translator' ) local UIOutlines = require( 'src.ui.elements.UIOutlines' ) local UIBackground = require( 'src.ui.elements.UIBackground' ) local TexturePacks = require( 'src.ui.texturepacks.TexturePacks' ) @@ -10,7 +18,7 @@ local GridHelper = require( 'src.util.GridHelper' ) -- Module -- ------------------------------------------------ -local HealthScreen = {}; +local HealthScreen = Screen:subclass( 'HealthScreen' ) -- ------------------------------------------------ -- Constants @@ -20,128 +28,114 @@ local UI_GRID_WIDTH = 30 local UI_GRID_HEIGHT = 16 -- ------------------------------------------------ --- Constructor +-- Private Methods -- ------------------------------------------------ -function HealthScreen.new() - local self = Screen.new(); - - local character; - local characterType; - - local background - local outlines - local x, y - local tw, th - - -- ------------------------------------------------ - -- Private Methods - -- ------------------------------------------------ - - --- - -- Generates the outlines for this screen. - -- - local function generateOutlines() - outlines = UIOutlines( x, y, 0, 0, UI_GRID_WIDTH, UI_GRID_HEIGHT ) - - -- Horizontal borders. - for ox = 0, UI_GRID_WIDTH-1 do - outlines:add( ox, 0 ) -- Top - outlines:add( ox, 2 ) -- Header - outlines:add( ox, UI_GRID_HEIGHT-1 ) -- Bottom - end - - -- Vertical outlines. - for oy = 0, UI_GRID_HEIGHT-1 do - outlines:add( 0, oy ) -- Left - outlines:add( UI_GRID_WIDTH-1, oy ) -- Right - end +--- +-- Generates the outlines for this screen. +-- @tparam number x The origin of the screen along the x-axis. +-- @tparam number y The origin of the screen along the y-axis. +-- @treturn UIOutlines The newly created UIOutlines instance. +-- +local function generateOutlines( x, y ) + local outlines = UIOutlines( x, y, 0, 0, UI_GRID_WIDTH, UI_GRID_HEIGHT ) + + -- Horizontal borders. + for ox = 0, UI_GRID_WIDTH-1 do + outlines:add( ox, 0 ) -- Top + outlines:add( ox, 2 ) -- Header + outlines:add( ox, UI_GRID_HEIGHT-1 ) -- Bottom + end - outlines:refresh() + -- Vertical outlines. + for oy = 0, UI_GRID_HEIGHT-1 do + outlines:add( 0, oy ) -- Left + outlines:add( UI_GRID_WIDTH-1, oy ) -- Right end - -- ------------------------------------------------ - -- Public Methods - -- ------------------------------------------------ + outlines:refresh() + return outlines +end + +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - function self:init( ncharacter ) - tw, th = TexturePacks.getTileDimensions() - x, y = GridHelper.centerElement( UI_GRID_WIDTH, UI_GRID_HEIGHT ) +function HealthScreen:initialize( character ) + self.x, self.y = GridHelper.centerElement( UI_GRID_WIDTH, UI_GRID_HEIGHT ) - character = ncharacter; - characterType = character:getBody():getID(); + self.character = character + self.characterType = character:getBody():getID() - background = UIBackground( x, y, 0, 0, UI_GRID_WIDTH, UI_GRID_HEIGHT ) + self.background = UIBackground( self.x, self.y, 0, 0, UI_GRID_WIDTH, UI_GRID_HEIGHT ) - generateOutlines() - end + self.outlines = generateOutlines( self.x, self.y ) +end - function self:draw() - background:draw() - outlines:draw() - - local counter = 3; - for _, bodyPart in pairs( character:getBody():getBodyParts() ) do - if bodyPart:isEntryNode() then - counter = counter + 1; - local status; - if bodyPart:isDestroyed() then - TexturePacks.setColor( 'ui_health_destroyed_limb' ) - status = 'DED' - elseif bodyPart:getCurrentHealth() / bodyPart:getMaximumHealth() < 0.2 then - TexturePacks.setColor( 'ui_health_badly_damaged_limb' ) - status = 'OUCH' - elseif bodyPart:getCurrentHealth() / bodyPart:getMaximumHealth() < 0.4 then - TexturePacks.setColor( 'ui_health_damaged_limb' ) - status = 'MEH' +function HealthScreen:draw() + self.background:draw() + self.outlines:draw() + + local tw, th = TexturePacks.getTileDimensions() + local counter = 3 + for _, bodyPart in pairs( self.character:getBody():getBodyParts() ) do + if bodyPart:isEntryNode() then + counter = counter + 1 + local status + if bodyPart:isDestroyed() then + TexturePacks.setColor( 'ui_health_destroyed_limb' ) + status = 'DED' + elseif bodyPart:getCurrentHealth() / bodyPart:getMaximumHealth() < 0.2 then + TexturePacks.setColor( 'ui_health_badly_damaged_limb' ) + status = 'OUCH' + elseif bodyPart:getCurrentHealth() / bodyPart:getMaximumHealth() < 0.4 then + TexturePacks.setColor( 'ui_health_damaged_limb' ) + status = 'MEH' + elseif bodyPart:getCurrentHealth() / bodyPart:getMaximumHealth() < 0.7 then + TexturePacks.setColor( 'ui_health_ok_limb' ) + status = 'OK' + else + TexturePacks.setColor( 'ui_health_fine_limb' ) + status = 'FINE' + end + love.graphics.print( Translator.getText( bodyPart:getID() ), (self.x+1) * tw, (self.y+counter) * th ) + love.graphics.printf( status, (self.x+1) * tw, (self.y+counter) * th, ( UI_GRID_WIDTH - 2 ) * tw, 'right' ) + + if bodyPart:isBleeding() then + local str = string.format( 'Bleeding %1.2f', bodyPart:getBloodLoss() ) + if bodyPart:getBloodLoss() / 1.0 < 0.2 then + TexturePacks.setColor( 'ui_health_bleeding_fine' ) + elseif bodyPart:getBloodLoss() / 1.0 < 0.4 then + TexturePacks.setColor( 'ui_health_bleeding_ok' ) elseif bodyPart:getCurrentHealth() / bodyPart:getMaximumHealth() < 0.7 then - TexturePacks.setColor( 'ui_health_ok_limb' ) - status = 'OK' - else - TexturePacks.setColor( 'ui_health_fine_limb' ) - status = 'FINE' - end - love.graphics.print( Translator.getText( bodyPart:getID() ), (x+1) * tw, (y+counter) * th ) - love.graphics.printf( status, (x+1) * tw, (y+counter) * th, ( UI_GRID_WIDTH - 2 ) * tw, 'right' ) - - if bodyPart:isBleeding() then - local str = string.format( 'Bleeding %1.2f', bodyPart:getBloodLoss() ); - if bodyPart:getBloodLoss() / 1.0 < 0.2 then - TexturePacks.setColor( 'ui_health_bleeding_fine' ) - elseif bodyPart:getBloodLoss() / 1.0 < 0.4 then - TexturePacks.setColor( 'ui_health_bleeding_ok' ) - elseif bodyPart:getCurrentHealth() / bodyPart:getMaximumHealth() < 0.7 then - TexturePacks.setColor( 'ui_health_bleeding' ) - elseif bodyPart:getCurrentHealth() / bodyPart:getMaximumHealth() < 1.0 then - TexturePacks.setColor( 'ui_health_bleeding_bad' ) - end - love.graphics.printf( str, (x+1) * tw, (y+counter) * th, ( UI_GRID_WIDTH - 2 ) * tw, 'center' ) + TexturePacks.setColor( 'ui_health_bleeding' ) + elseif bodyPart:getCurrentHealth() / bodyPart:getMaximumHealth() < 1.0 then + TexturePacks.setColor( 'ui_health_bleeding_bad' ) end + love.graphics.printf( str, (self.x+1) * tw, (self.y+counter) * th, ( UI_GRID_WIDTH - 2 ) * tw, 'center' ) end end + end - TexturePacks.setColor( 'ui_text' ) - - -- Draw character type. - local type = Translator.getText( 'ui_character_type' ) .. Translator.getText( characterType ) - love.graphics.print( type, (x+1) * tw, (y+1) * th ) + TexturePacks.setColor( 'ui_text' ) - -- Draw character name. - if character:getName() then - local name = Translator.getText( 'ui_character_name' ) .. character:getName() - love.graphics.print( name, (x+2) * tw + TexturePacks.getFont():measureWidth( type ), (y+1) * th ) - end + -- Draw character type. + local type = Translator.getText( 'ui_character_type' ) .. Translator.getText( self.characterType ) + love.graphics.print( type, (self.x+1) * tw, (self.y+1) * th ) - TexturePacks.resetColor() + -- Draw character name. + if self.character:getName() then + local name = Translator.getText( 'ui_character_name' ) .. self.character:getName() + love.graphics.print( name, (self.x+2) * tw + TexturePacks.getFont():measureWidth( type ), (self.y+1) * th ) end - function self:keypressed( key ) - if key == 'escape' or key == 'h' then - ScreenManager.pop(); - end - end + TexturePacks.resetColor() +end - return self; +function HealthScreen:keypressed( key ) + if key == 'escape' or key == 'h' then + ScreenManager.pop() + end end -return HealthScreen; +return HealthScreen From 0b48d10b74756b848d79c93aa0a95b8fdd05fc6a Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Wed, 6 Dec 2017 02:57:48 +0100 Subject: [PATCH 052/178] Refactor InformationModal --- src/ui/screens/InformationModal.lua | 198 +++++++++++++--------------- 1 file changed, 94 insertions(+), 104 deletions(-) diff --git a/src/ui/screens/InformationModal.lua b/src/ui/screens/InformationModal.lua index ebade184..21c89b41 100644 --- a/src/ui/screens/InformationModal.lua +++ b/src/ui/screens/InformationModal.lua @@ -7,7 +7,7 @@ -- Required Modules -- ------------------------------------------------ -local Screen = require( 'lib.screenmanager.Screen' ) +local Screen = require( 'src.ui.screens.Screen' ) local ScreenManager = require( 'lib.screenmanager.ScreenManager' ) local Translator = require( 'src.util.Translator' ) local UIOutlines = require( 'src.ui.elements.UIOutlines' ) @@ -21,7 +21,7 @@ local GridHelper = require( 'src.util.GridHelper' ) -- Module -- ------------------------------------------------ -local InformationModal = {} +local InformationModal = Screen:subclass( 'InformationModal' ) -- ------------------------------------------------ -- Constants @@ -33,130 +33,120 @@ local UI_GRID_HEIGHT = 10 local BUTTON_LIST_WIDTH = 20 -- ------------------------------------------------ --- Constructor +-- Private Methods -- ------------------------------------------------ -function InformationModal.new() - local self = Screen.new() - - local background - local outlines - local x, y - local tw, th - - local buttonList - - local text - - -- ------------------------------------------------ - -- Private Methods - -- ------------------------------------------------ - - --- - -- Generates the outlines for this screen. - -- - local function generateOutlines() - outlines = UIOutlines( x, y, 0, 0, UI_GRID_WIDTH, UI_GRID_HEIGHT ) - - -- Horizontal borders. - for ox = 0, UI_GRID_WIDTH-1 do - outlines:add( ox, 0 ) -- Top - outlines:add( ox, UI_GRID_HEIGHT-1 ) -- Bottom - end +--- +-- Generates the outlines for this screen. +-- @tparam number x The origin of the screen along the x-axis. +-- @tparam number y The origin of the screen along the y-axis. +-- @treturn UIOutlines The newly created UIOutlines instance. +-- +local function generateOutlines( x, y ) + local outlines = UIOutlines( x, y, 0, 0, UI_GRID_WIDTH, UI_GRID_HEIGHT ) - -- Vertical outlines. - for oy = 0, UI_GRID_HEIGHT-1 do - outlines:add( 0, oy ) -- Left - outlines:add( UI_GRID_WIDTH-1, oy ) -- Right - end + -- Horizontal borders. + for ox = 0, UI_GRID_WIDTH-1 do + outlines:add( ox, 0 ) -- Top + outlines:add( ox, UI_GRID_HEIGHT-1 ) -- Bottom + end - outlines:refresh() + -- Vertical outlines. + for oy = 0, UI_GRID_HEIGHT-1 do + outlines:add( 0, oy ) -- Left + outlines:add( UI_GRID_WIDTH-1, oy ) -- Right end - --- - -- Creates the yes and no button at the bottom of the screen. - -- - local function createButtons() - local lx = GridHelper.centerElement( BUTTON_LIST_WIDTH, 1 ) - local ly = y + UI_GRID_HEIGHT - 2 + outlines:refresh() + return outlines +end - buttonList = UIHorizontalList( lx, ly, 0, 0, BUTTON_LIST_WIDTH, 1 ) +--- +-- Creates the yes and no button at the bottom of the screen. +-- @tparam number y The origin of the screen along the y-axis. +-- @treturn UIHorizontalList The button list. +-- +local function createButtons( y ) + local lx = GridHelper.centerElement( BUTTON_LIST_WIDTH, 1 ) + local ly = y + UI_GRID_HEIGHT - 2 - local buttonWidth = math.floor( BUTTON_LIST_WIDTH * 0.5 ) + local buttonList = UIHorizontalList( lx, ly, 0, 0, BUTTON_LIST_WIDTH, 1 ) + local buttonWidth = math.floor( BUTTON_LIST_WIDTH * 0.5 ) + local okButton = UIButton( lx, ly, 0, 0, buttonWidth, 1, function() ScreenManager.pop() end, Translator.getText( 'ui_ok' )) + buttonList:addChild( okButton ) - local okButton = UIButton( lx, ly, 0, 0, buttonWidth, 1, function() ScreenManager.pop() end, Translator.getText( 'ui_ok' )) - buttonList:addChild( okButton ) - end + return buttonList +end - -- ------------------------------------------------ - -- Public Methods - -- ------------------------------------------------ +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - --- - -- Initialises the InformationModal. - -- @tparam string ntext The text to display inside of the modal. - -- - function self:init( ntext ) - text = ntext +--- +-- Initialises the InformationModal. +-- @tparam string text The text to display inside of the modal. +-- +function InformationModal:initialize( text ) + self.text = text - tw, th = TexturePacks.getTileDimensions() - x, y = GridHelper.centerElement( UI_GRID_WIDTH, UI_GRID_HEIGHT ) + self.x, self.y = GridHelper.centerElement( UI_GRID_WIDTH, UI_GRID_HEIGHT ) - background = UIBackground( x, y, 0, 0, UI_GRID_WIDTH, UI_GRID_HEIGHT ) + self.background = UIBackground( self.x, self.y, 0, 0, UI_GRID_WIDTH, UI_GRID_HEIGHT ) - generateOutlines() - createButtons() - end + self.outlines = generateOutlines( self.x, self.y ) + self.buttonList = createButtons( self.y ) +end - --- - -- Draws the InformationModal. - -- - function self:draw() - background:draw() - outlines:draw() - buttonList:draw() - - TexturePacks.setColor( 'ui_text' ) - love.graphics.printf( text, (x+2) * tw, (y+2) * th, (UI_GRID_WIDTH-4) * tw, 'center' ) - TexturePacks.resetColor() - end +--- +-- Draws the InformationModal. +-- +function InformationModal:draw() + self.background:draw() + self.outlines:draw() + self.buttonList:draw() + + local tw, th = TexturePacks.getTileDimensions() + TexturePacks.setColor( 'ui_text' ) + love.graphics.printf( self.text, (self.x+2) * tw, (self.y+2) * th, (UI_GRID_WIDTH-4) * tw, 'center' ) + TexturePacks.resetColor() +end - --- - -- Updates the InformationModal. - -- - function self:update() - buttonList:update() - end +--- +-- Updates the InformationModal. +-- +function InformationModal:update() + self.buttonList:update() +end - --- - -- Handle keypressed events. - -- - function self:keypressed( key, scancode ) - if key == 'escape' then - ScreenManager.pop() - end - - if scancode == 'left' then - buttonList:command( 'left' ) - elseif scancode == 'right' then - buttonList:command( 'right' ) - elseif scancode == 'return' then - buttonList:command( 'activate' ) - end +--- +-- Handle keypressed events. +-- +function InformationModal:keypressed( key, scancode ) + if key == 'escape' then + ScreenManager.pop() end - function self:mousemoved() - love.mouse.setVisible( true ) + if scancode == 'left' then + self.buttonList:command( 'left' ) + elseif scancode == 'right' then + self.buttonList:command( 'right' ) + elseif scancode == 'return' then + self.buttonList:command( 'activate' ) end +end - --- - -- Handle mousereleased events. - -- - function self:mousereleased() - buttonList:command( 'activate' ) - end +--- +-- Handle mousemoved events. +-- +function InformationModal:mousemoved() + love.mouse.setVisible( true ) +end - return self +--- +-- Handle mousereleased events. +-- +function InformationModal:mousereleased() + self.buttonList:command( 'activate' ) end return InformationModal From d276223cf26be917da3584b802b94c8863d7937b Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Wed, 6 Dec 2017 03:26:36 +0100 Subject: [PATCH 053/178] Refactor InventoryScreen --- src/ui/screens/InventoryScreen.lua | 614 +++++++++++++++-------------- 1 file changed, 311 insertions(+), 303 deletions(-) diff --git a/src/ui/screens/InventoryScreen.lua b/src/ui/screens/InventoryScreen.lua index d9121ac5..6e54f441 100644 --- a/src/ui/screens/InventoryScreen.lua +++ b/src/ui/screens/InventoryScreen.lua @@ -6,7 +6,7 @@ -- Required Modules -- ------------------------------------------------ -local Screen = require( 'lib.screenmanager.Screen' ) +local Screen = require( 'src.ui.screens.Screen' ) local ScreenManager = require( 'lib.screenmanager.ScreenManager' ) local UIOutlines = require( 'src.ui.elements.UIOutlines' ) local UIBackground = require( 'src.ui.elements.UIBackground' ) @@ -22,7 +22,7 @@ local Translator = require( 'src.util.Translator' ) -- Module -- ------------------------------------------------ -local InventoryScreen = {} +local InventoryScreen = Screen:subclass( 'InventoryScreen' ) -- ------------------------------------------------ -- Constants @@ -45,370 +45,378 @@ local ITEM_STATS_WIDTH = COLUMN_WIDTH local ITEM_STATS_HEIGHT = COLUMN_HEIGHT - EQUIPMENT_HEIGHT - 1 -- ------------------------------------------------ --- Constructor +-- Private Methods -- ------------------------------------------------ -function InventoryScreen.new() - local self = Screen.new() - - local x, y - - local outlines - local background +--- +-- Creates the outlines for the Inventory window. +-- @tparam number x The origin of the screen along the x-axis. +-- @tparam number y The origin of the screen along the y-axis. +-- @treturn UIOutlines The newly created UIOutlines instance. +-- +local function generateOutlines( x, y ) + local outlines = UIOutlines( x, y, 0, 0, UI_GRID_WIDTH, UI_GRID_HEIGHT ) - local lists - local listLabels + -- Horizontal borders. + for ox = 0, UI_GRID_WIDTH-1 do + outlines:add( ox, 0 ) -- Top + outlines:add( ox, UI_GRID_HEIGHT-1 ) -- Bottom + end - local itemStats + -- Horizontal line below Equipment list. + -- Offset calculations: + -- y-axis: Outline top + Header + Outline Header + EQUIPMENT_HEIGHT => EQUIPMENT_HEIGHT+3 + for ox = 0, COLUMN_WIDTH do + outlines:add( ox, EQUIPMENT_HEIGHT+3 ) + end - local dragboard + -- Vertical outlines. + for oy = 0, UI_GRID_HEIGHT-1 do + outlines:add( 0, oy ) -- Left + outlines:add( UI_GRID_WIDTH-1, oy ) -- Right + end - -- ------------------------------------------------ - -- Private Methods - -- ------------------------------------------------ + -- Vertical line after the equipment column. + -- Offset calculations: + -- x:axis: Outline left + First column => 1 + COLUMN_WIDTH + for oy = 0, UI_GRID_HEIGHT-1 do + outlines:add( 1+COLUMN_WIDTH, oy ) + end - --- - -- Creates the outlines for the Inventory window. - -- - local function generateOutlines() - outlines = UIOutlines( x, y, 0, 0, UI_GRID_WIDTH, UI_GRID_HEIGHT ) + -- Vertical line after the inventory column. + -- Offset calculations: + -- x-axis: Outline left + First column + Outline + Second Column + -- => 1 + COLUMN_WIDTH + 1 + COLUMN_WIDTH + -- => 2 + 2*COLUMN_WIDTH + for oy = 0, UI_GRID_HEIGHT-1 do + outlines:add( 2 + 2*COLUMN_WIDTH, oy ) + end - -- Horizontal borders. - for ox = 0, UI_GRID_WIDTH-1 do - outlines:add( ox, 0 ) -- Top - outlines:add( ox, UI_GRID_HEIGHT-1 ) -- Bottom - end + -- Horizontal line for column headers. + -- Offset calculations: + -- x-axis: Outline top + Header line => 2 + for ox = 0, UI_GRID_WIDTH-1 do + outlines:add( ox, 2 ) + end - -- Horizontal line below Equipment list. - -- Offset calculations: - -- y-axis: Outline top + Header + Outline Header + EQUIPMENT_HEIGHT => EQUIPMENT_HEIGHT+3 - for ox = 0, COLUMN_WIDTH do - outlines:add( ox, EQUIPMENT_HEIGHT+3 ) - end + outlines:refresh() + return outlines +end - -- Vertical outlines. - for oy = 0, UI_GRID_HEIGHT-1 do - outlines:add( 0, oy ) -- Left - outlines:add( UI_GRID_WIDTH-1, oy ) -- Right - end +--- +-- Creates the equipment list for the currently selected character and the +-- associated header label. +-- @tparam number x The origin of the screen along the x-axis. +-- @tparam number y The origin of the screen along the y-axis. +-- @tparam Character character The character to use for the equipment list. +-- @tparam table lists A table containing the different inventories. +-- @tparam table listLabels A table containing the labels for each inventory list. +-- +local function createEquipmentList( x, y, character, lists, listLabels ) + -- Offset calculations: + -- x-axis: Outline left => 1 + -- y-axis: Outline top + Header Text + Outline below Header => 3 + local ox, oy = 1, 3 + lists.equipment = UIEquipmentList( x, y, ox, oy, EQUIPMENT_WIDTH, EQUIPMENT_HEIGHT, character ) + + -- Offset calculations: + -- x-axis: Outline left => 1 + -- y-axis: Outline top => 1 + local lx, ly = 1, 1 + listLabels.equipment = UILabel( x, y, lx, ly, EQUIPMENT_WIDTH, 1, Translator.getText( 'inventory_equipment' ), 'ui_inventory_headers' ) +end - -- Vertical line after the equipment column. - -- Offset calculations: - -- x:axis: Outline left + First column => 1 + COLUMN_WIDTH - for oy = 0, UI_GRID_HEIGHT-1 do - outlines:add( 1+COLUMN_WIDTH, oy ) - end +--- +-- Creates the character's "backpack" inventory in which all items he carries +-- are stored and the associated header label. +-- @tparam number x The origin of the screen along the x-axis. +-- @tparam number y The origin of the screen along the y-axis. +-- @tparam Character character The character to use for the equipment list. +-- @tparam table lists A table containing the different inventories. +-- @tparam table listLabels A table containing the labels for each inventory list. +-- +local function createCharacterInventoryList( x, y, character, lists, listLabels ) + -- Offset calculations: + -- x-axis: Outline left + Equipment Column + Equipment Column Outline + -- => 1 + COLUMN_WIDTH + 1 + -- => 2 + COLUMN_WIDTH + -- y-axis: Outline top + Header Text + Outline below Header => 3 + local ox, oy = 2 + COLUMN_WIDTH, 3 + lists.characterInventory = UIInventoryList( x, y, ox, oy, COLUMN_WIDTH, COLUMN_HEIGHT, character:getInventory() ) + + -- Label coordinates relative to the screen's coordinates. + -- x-axis: Outline left + Equipment Column + Equipment Column Outline + -- => 1 + COLUMN_WIDTH + 1 + -- => 2 + COLUMN_WIDTH + -- y-axis: Outline top => 1 + local lx, ly = 2 + COLUMN_WIDTH, 1 + listLabels.characterInventory = UILabel( x, y, lx, ly, COLUMN_WIDTH, 1, Translator.getText( 'inventory_character' ), 'ui_inventory_headers' ) +end - -- Vertical line after the inventory column. - -- Offset calculations: - -- x-axis: Outline left + First column + Outline + Second Column - -- => 1 + COLUMN_WIDTH + 1 + COLUMN_WIDTH - -- => 2 + 2*COLUMN_WIDTH - for oy = 0, UI_GRID_HEIGHT-1 do - outlines:add( 2 + 2*COLUMN_WIDTH, oy ) - end +--- +-- Creates the target inventory with which the character wants to interact and +-- its associated header label. +-- @tparam number x The origin of the screen along the x-axis. +-- @tparam number y The origin of the screen along the y-axis. +-- @tparam Character character The character to use for the equipment list. +-- @tparam Tile target The target tile to interact with. +-- @tparam table lists A table containing the different inventories. +-- @tparam table listLabels A table containing the labels for each inventory list. +-- +local function createTargetInventoryList( x, y, character, target, lists, listLabels ) + local id, inventory + + -- TODO How to handle base inventory? + if target:instanceOf( 'Inventory' ) then + 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 + id, inventory = 'inventory_character', target:getCharacter():getInventory() + else + id, inventory = 'inventory_tile_inventory', target:getInventory() + end - -- Horizontal line for column headers. - -- Offset calculations: - -- x-axis: Outline top + Header line => 2 - for ox = 0, UI_GRID_WIDTH-1 do - outlines:add( ox, 2 ) - end + -- Offset calculations: + -- x-axis: Outline left + Equipment Column + Equipment Column Outline + -- + Character Inventory Column + Character Inventory Column Outline + -- => 1 + COLUMN_WIDTH + 1 + COLUMN_WIDTH + 1 + -- => 3 + 2 * COLUMN_WIDTH + -- y-axis: Outline top + Header Text + Outline below Header => 3 + local ox, oy = 3 + 2 * COLUMN_WIDTH, 3 + lists.targetInventory = UIInventoryList( x, y, ox, oy, COLUMN_WIDTH, COLUMN_HEIGHT, inventory ) + + -- Offset calculations: + -- x-axis: Outline left + Equipment Column + Equipment Column Outline + -- + Character Inventory Column + Character Inventory Column Outline + -- => 1 + COLUMN_WIDTH + 1 + COLUMN_WIDTH + 1 + -- => 3 + 2 * COLUMN_WIDTH + -- y-axis: Outline top => 1 + local lx, ly = 3 + 2 * COLUMN_WIDTH, 1 + listLabels.targetInventory = UILabel( x, y, lx, ly, COLUMN_WIDTH, 1, Translator.getText( id ), 'ui_inventory_headers' ) +end - outlines:refresh() - end +--- +-- Creates the equipment and inventory lists. +-- @tparam number x The origin of the screen along the x-axis. +-- @tparam number y The origin of the screen along the y-axis. +-- @tparam Character character The character to use for the equipment list. +-- @tparam Tile target The target tile to interact with. +-- @treturn table The table containing the different inventory lists. +-- @treturn table The table containing a label for each inventory list. +-- +local function createInventoryLists( x, y, character, target ) + local lists = {} + local listLabels = {} - --- - -- Creates the equipment list for the currently selected character and the - -- associated header label. - -- @tparam Character character The character to use for the equipment list. - -- - local function createEquipmentList( character ) - -- Offset calculations: - -- x-axis: Outline left => 1 - -- y-axis: Outline top + Header Text + Outline below Header => 3 - local ox, oy = 1, 3 - lists.equipment = UIEquipmentList( x, y, ox, oy, EQUIPMENT_WIDTH, EQUIPMENT_HEIGHT, character ) - - -- Offset calculations: - -- x-axis: Outline left => 1 - -- y-axis: Outline top => 1 - local lx, ly = 1, 1 - listLabels.equipment = UILabel( x, y, lx, ly, EQUIPMENT_WIDTH, 1, Translator.getText( 'inventory_equipment' ), 'ui_inventory_headers' ) - end + createEquipmentList( x, y, character, lists, listLabels ) + createCharacterInventoryList( x, y, character, lists, listLabels ) + createTargetInventoryList( x, y, character, target, lists, listLabels ) - --- - -- Creates the character's "backpack" inventory in which all items he carries - -- are stored and the associated header label. - -- @tparam Character character The character to use for the equipment list. - -- - local function createCharacterInventoryList( character ) - -- Offset calculations: - -- x-axis: Outline left + Equipment Column + Equipment Column Outline - -- => 1 + COLUMN_WIDTH + 1 - -- => 2 + COLUMN_WIDTH - -- y-axis: Outline top + Header Text + Outline below Header => 3 - local ox, oy = 2 + COLUMN_WIDTH, 3 - lists.characterInventory = UIInventoryList( x, y, ox, oy, COLUMN_WIDTH, COLUMN_HEIGHT, character:getInventory() ) - - -- Label coordinates relative to the screen's coordinates. - -- x-axis: Outline left + Equipment Column + Equipment Column Outline - -- => 1 + COLUMN_WIDTH + 1 - -- => 2 + COLUMN_WIDTH - -- y-axis: Outline top => 1 - local lx, ly = 2 + COLUMN_WIDTH, 1 - listLabels.characterInventory = UILabel( x, y, lx, ly, COLUMN_WIDTH, 1, Translator.getText( 'inventory_character' ), 'ui_inventory_headers' ) - end + return lists, listLabels +end - --- - -- Creates the target inventory with which the character wants to interact. - -- and the associated header label. - -- @tparam Character character The character to use for the equipment list. - -- @tparam Tile target The target tile to interact with. - -- - local function createTargetInventoryList( character, target ) - local id, inventory - - -- TODO How to handle base inventory? - if target:instanceOf( 'Inventory' ) then - 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 - id, inventory = 'inventory_character', target:getCharacter():getInventory() - else - id, inventory = 'inventory_tile_inventory', target:getInventory() +--- +-- Returns the list the mouse is currently over. +-- @tparam table lists A table containing the different inventories. +-- @return The equipment or inventory list the mouse is over. +-- +local function getListBelowCursor( lists ) + for _, list in pairs( lists ) do + if list:isMouseOver() then + return list end - - -- Offset calculations: - -- x-axis: Outline left + Equipment Column + Equipment Column Outline - -- + Character Inventory Column + Character Inventory Column Outline - -- => 1 + COLUMN_WIDTH + 1 + COLUMN_WIDTH + 1 - -- => 3 + 2 * COLUMN_WIDTH - -- y-axis: Outline top + Header Text + Outline below Header => 3 - local ox, oy = 3 + 2 * COLUMN_WIDTH, 3 - lists.targetInventory = UIInventoryList( x, y, ox, oy, COLUMN_WIDTH, COLUMN_HEIGHT, inventory ) - - -- Offset calculations: - -- x-axis: Outline left + Equipment Column + Equipment Column Outline - -- + Character Inventory Column + Character Inventory Column Outline - -- => 1 + COLUMN_WIDTH + 1 + COLUMN_WIDTH + 1 - -- => 3 + 2 * COLUMN_WIDTH - -- y-axis: Outline top => 1 - local lx, ly = 3 + 2 * COLUMN_WIDTH, 1 - listLabels.targetInventory = UILabel( x, y, lx, ly, COLUMN_WIDTH, 1, Translator.getText( id ), 'ui_inventory_headers' ) end +end - --- - -- Creates the equipment and inventory lists. - -- @tparam Character character The character to use for the equipment list. - -- @tparam Tile target The target tile to interact with. - -- - local function createInventoryLists( character, target ) - lists = {} - listLabels = {} - createEquipmentList( character ) - createCharacterInventoryList( character ) - createTargetInventoryList( character, target ) +--- +-- Refreshes all inventor lists. +-- @tparam table lists A table containing the different inventories. +-- +local function refreshLists( lists ) + for _, list in pairs( lists ) do + list:refresh() end +end - --- - -- Returns the list the mouse is currently over. - -- @return The equipment or inventory list the mouse is over. - -- - local function getListBelowCursor() - for _, list in pairs( lists ) do - if list:isMouseOver() then - return list - end - end +--- +-- Initiates a drag action. +-- @tparam number button The mouse button index. +-- @tparam table lists A table containing the different inventories. +-- @tparam UIInventoryDragboard dragboard The dragboard containing the current dragged item. +-- @tparam UIItemStats itemStats The itemStats object to set the item for. +-- +local function drag( button, lists, dragboard, itemStats ) + -- Can't drag if we are already dragging. + if dragboard:hasDragContext() then + return end - --- - -- Refreshes all inventor lists. - -- - local function refreshLists() - for _, list in pairs( lists ) do - list:refresh() - end + -- Can't drag if not over a list. + local list = getListBelowCursor( lists ) + if not list then + return end - --- - -- Initiates a drag action. - -- @tparam number button The mouse button index. - -- - local function drag( button ) - -- Can't drag if we are already dragging. - if dragboard:hasDragContext() then - return - end + local item, slot = list:drag( button == 2, love.keyboard.isDown( 'lshift' )) - -- Can't drag if not over a list. - local list = getListBelowCursor() - if not list then - return - end + -- Abort if there is nothing to drag here. + if not item then + return + end - local item, slot = list:drag( button == 2, love.keyboard.isDown( 'lshift' )) + -- Display stats for the dragged item. + itemStats:setItem( item ) - -- Abort if there is nothing to drag here. - if not item then - return - end + -- If we have an actual item slot we use it as the origin to + -- which the item is returned in case it can't be dropped anywhere. + dragboard:drag( item, slot or list ) - -- Display stats for the dragged item. - itemStats:setItem( item ) + -- If the dragged item is a container we need to refresh the inventory lists + -- because it changes the inventory volumes. + if item:instanceOf( 'Container' ) then + refreshLists( lists ) + end +end - -- If we have an actual item slot we use it as the origin to - -- which the item is returned in case it can't be dropped anywhere. - dragboard:drag( item, slot or list ) +--- +-- Selects an item for the item stats area. +-- @tparam table lists A table containing the different inventories. +-- @tparam UIItemStats itemStats The itemStats object to set the item for. +-- +local function selectItem( lists, itemStats ) + local list = getListBelowCursor( lists ) + if not list then + return + end - -- If the dragged item is a container we need to refresh the inventory lists - -- because it changes the inventory volumes. - if item:instanceOf( 'Container' ) then - refreshLists() - end + local item = list:getItemBelowCursor() + if not item then + return end - --- - -- Selects an item for the item stats area. - -- - local function selectItem() - local list = getListBelowCursor() - if not list then - return - end + itemStats:setItem( item ) +end - local item = list:getItemBelowCursor() - if not item then - return - end +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - itemStats:setItem( item ) - end +--- +-- Initialises the inventory screen. +-- @tparam Character ncharacter The character to open the inventory for. +-- @tparam Tile ntarget The target tile to open the inventory for. +-- +function InventoryScreen:initialize( character, target ) + love.mouse.setVisible( true ) - -- ------------------------------------------------ - -- Public Methods - -- ------------------------------------------------ + self.x, self.y = GridHelper.centerElement( UI_GRID_WIDTH, UI_GRID_HEIGHT ) - --- - -- Initialises the inventory screen. - -- @tparam Character ncharacter The character to open the inventory for. - -- @tparam Tile ntarget The target tile to open the inventory for. - -- - function self:init( character, target ) - love.mouse.setVisible( true ) + -- General UI Elements. + self.background = UIBackground( self.x, self.y, 0, 0, UI_GRID_WIDTH, UI_GRID_HEIGHT ) + self.outlines = generateOutlines( self.x, self.y ) - x, y = GridHelper.centerElement( UI_GRID_WIDTH, UI_GRID_HEIGHT ) + -- UI inventory lists. + self.lists, self.listLabels = createInventoryLists( self.x, self.y, character, target ) - -- General UI Elements. - background = UIBackground( x, y, 0, 0, UI_GRID_WIDTH, UI_GRID_HEIGHT ) - generateOutlines() + self.dragboard = UIInventoryDragboard.new() - -- UI inventory lists. - createInventoryLists( character, target ) + -- Add the item stats area which displays the item attributes and a description area. + -- x-axis: Outline left => 1 + -- y-axis: Outline top + Header + Outl ine Header + -- + Outline below equipment + EQUIPMENT_HEIGHT + -- => EQUIPMENT_HEIGHT+4 + self.itemStats = UIItemStats( self.x, self.y, 1, EQUIPMENT_HEIGHT+4, ITEM_STATS_WIDTH, ITEM_STATS_HEIGHT ) +end - dragboard = UIInventoryDragboard.new() +--- +-- Draws the inventory screen. +-- +function InventoryScreen:draw() + self.background:draw() + self.outlines:draw() - -- Add the item stats area which displays the item attributes and a description area. - -- x-axis: Outline left => 1 - -- y-axis: Outline top + Header + Outl ine Header - -- + Outline below equipment + EQUIPMENT_HEIGHT - -- => EQUIPMENT_HEIGHT+4 - itemStats = UIItemStats( x, y, 1, EQUIPMENT_HEIGHT+4, ITEM_STATS_WIDTH, ITEM_STATS_HEIGHT ) + for _, list in pairs( self.lists ) do + list:draw() end - --- - -- Draws the inventory screen. - -- - function self:draw() - background:draw() - outlines:draw() - - for _, list in pairs( lists ) do - list:draw() - end + for _, label in pairs( self.listLabels ) do + label:draw() + end - for _, label in pairs( listLabels ) do - label:draw() - end + -- Highlight equipment slot if draggable item can be put there. + self.lists.equipment:highlight( self.dragboard:getDragContext() and self.dragboard:getDraggedItem() ) - -- Highlight equipment slot if draggable item can be put there. - lists.equipment:highlight( dragboard:getDragContext() and dragboard:getDraggedItem() ) + self.itemStats:draw() + self.dragboard:draw( self.lists ) +end - itemStats:draw() - dragboard:draw( lists ) +--- +-- This method is called when the inventory screen is closed. +-- +function InventoryScreen:close() + -- Drop any item that is currently dragged. + if self.dragboard:hasDragContext() then + self.dragboard:drop() end +end - --- - -- This method is called when the inventory screen is closed. - -- - function self:close() - -- Drop any item that is currently dragged. - if dragboard:hasDragContext() then - dragboard:drop() - end - end +-- ------------------------------------------------ +-- Input Callbacks +-- ------------------------------------------------ - -- ------------------------------------------------ - -- Input Callbacks - -- ------------------------------------------------ +function InventoryScreen:keypressed( key, scancode ) + if key == 'escape' or key == 'i' then + ScreenManager.pop() + end - function self:keypressed( key, scancode ) - if key == 'escape' or key == 'i' then - ScreenManager.pop() - end + if scancode == 'up' then + self.itemStats:command( 'up' ) + elseif scancode == 'down' then + self.itemStats:command( 'down' ) + end +end - if scancode == 'up' then - itemStats:command( 'up' ) - elseif scancode == 'down' then - itemStats:command( 'down' ) - end +function InventoryScreen:mousepressed( _, _, button ) + if button == 2 then + selectItem( self.lists, self.itemStats ) end + drag( button, self.lists, self.dragboard, self.itemStats ) +end - function self:mousepressed( _, _, button ) - if button == 2 then - selectItem() - end - drag( button ) +function InventoryScreen:mousereleased( _, _, _ ) + if not self.dragboard:hasDragContext() then + return end - function self:mousereleased( _, _, _ ) - if not dragboard:hasDragContext() then - return - end + local list = getListBelowCursor( self.lists ) + self.dragboard:drop( list ) - local list = getListBelowCursor() - dragboard:drop( list ) + -- Refresh lists in case volumes have changed. + refreshLists( self.lists ) - -- Refresh lists in case volumes have changed. - refreshLists() + self.itemStats:command( 'activate' ) +end - itemStats:command( 'activate' ) - end +function InventoryScreen:wheelmoved( _, dy ) + self.itemStats:command( 'scroll', dy ) +end - function self:wheelmoved( _, dy ) - itemStats:command( 'scroll', dy ) - end +-- ------------------------------------------------ +-- Other Callbacks +-- ------------------------------------------------ - -- ------------------------------------------------ - -- Other Callbacks - -- ------------------------------------------------ - - function self:resize( _, _ ) - x, y = GridHelper.centerElement( UI_GRID_WIDTH, UI_GRID_HEIGHT ) - background:setOrigin( x, y ) - outlines:setOrigin( x, y ) - itemStats:setOrigin( x, y ) - for _, list in pairs( lists ) do - list:setOrigin( x, y ) - end - for _, label in pairs( listLabels ) do - label:setOrigin( x, y ) - end +function InventoryScreen:resize( _, _ ) + self.x, self.y = GridHelper.centerElement( UI_GRID_WIDTH, UI_GRID_HEIGHT ) + self.background:setOrigin( self.x, self.y ) + self.outlines:setOrigin( self.x, self.y ) + self.itemStats:setOrigin( self.x, self.y ) + for _, list in pairs( self.lists ) do + list:setOrigin( self.x, self.y ) + end + for _, label in pairs( self.listLabels ) do + label:setOrigin( self.x, self.y ) end - - return self end return InventoryScreen From 63ecda3bad06f587c35d6ec248462e2dc078cbdc Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sat, 9 Dec 2017 01:21:24 +0100 Subject: [PATCH 054/178] Refactor SavegameScreen --- src/ui/screens/SavegameScreen.lua | 236 ++++++++++++++---------------- 1 file changed, 108 insertions(+), 128 deletions(-) diff --git a/src/ui/screens/SavegameScreen.lua b/src/ui/screens/SavegameScreen.lua index 8437f117..c3186475 100644 --- a/src/ui/screens/SavegameScreen.lua +++ b/src/ui/screens/SavegameScreen.lua @@ -2,7 +2,11 @@ -- @module SavegameScreen -- -local Screen = require( 'lib.screenmanager.Screen' ) +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ + +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' ) @@ -18,7 +22,7 @@ local Util = require( 'src.util.Util' ) -- Module -- ------------------------------------------------ -local SavegameScreen = {} +local SavegameScreen = Screen:subclass( 'SavegameScreen' ) -- ------------------------------------------------ -- Constants @@ -42,167 +46,143 @@ local BUTTON_LIST_WIDTH = 20 local BUTTON_LIST_Y = 20 -- ------------------------------------------------ --- Constructor +-- Private Functions -- ------------------------------------------------ -function SavegameScreen.new() - local self = Screen.new() - - -- ------------------------------------------------ - -- Private Variables - -- ------------------------------------------------ - - local title - local buttonList - local font - local footer - local container - - -- ------------------------------------------------ - -- Private Functions - -- ------------------------------------------------ - - local function createTitle() - 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() ) +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() - 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 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' ) - end - return UIButton( lx, ly, 0, 0, BUTTON_LIST_WIDTH, 1, callback, Translator.getText( 'ui_back' )) +local function createBackButton( lx, ly ) + local function callback() + ScreenManager.switch( 'mainmenu' ) end + return UIButton( lx, ly, 0, 0, BUTTON_LIST_WIDTH, 1, callback, Translator.getText( 'ui_back' )) +end - local function createSaveGameEntry( lx, ly, index, item, folder ) - local version = SaveHandler.loadVersion( folder ) +local function createSaveGameEntry( lx, ly, index, item, folder ) + local version = SaveHandler.loadVersion( folder ) - -- Generate the string for the savegame button showing the name of the saves, - -- the version of the game at which they were created and their creation date. - local str = string.format( '%2d. %s', index, item ) - str = Util.rightPadString( str, 36, ' ') - str = str .. string.format( ' %s %s', version, os.date( '%Y-%m-%d %X', love.filesystem.getLastModified( folder ))) + -- Generate the string for the savegame button showing the name of the saves, + -- the version of the game at which they were created and their creation date. + local str = string.format( '%2d. %s', index, item ) + str = Util.rightPadString( str, 36, ' ') + str = str .. string.format( ' %s %s', version, os.date( '%Y-%m-%d %X', love.filesystem.getLastModified( folder ))) - local function callback() - if version == getVersion() then - local save = SaveHandler.load( folder ) - ScreenManager.switch( 'gamescreen', save ) - end + local function callback() + if version == getVersion() then + local save = SaveHandler.load( folder ) + ScreenManager.switch( 'gamescreen', save ) end - - local button = UIButton( lx, ly, 0, 0, BUTTON_LIST_WIDTH, 1, callback, str, 'center' ) - button:setActive( version == getVersion() ) - return button end + local button = UIButton( lx, ly, 0, 0, BUTTON_LIST_WIDTH, 1, callback, str, 'center' ) + button:setActive( version == getVersion() ) + return button +end - local function createButtons() - local lx = GridHelper.centerElement( BUTTON_LIST_WIDTH, 1 ) - local ly = BUTTON_LIST_Y - buttonList = UIVerticalList( lx, ly, 0, 0, BUTTON_LIST_WIDTH, 1 ) +local function createButtons() + local lx = GridHelper.centerElement( BUTTON_LIST_WIDTH, 1 ) + local ly = BUTTON_LIST_Y - -- Create entries for last five savegames. - local items = love.filesystem.getDirectoryItems( SaveHandler.getSaveFolder() ) - local counter = 0 + local buttonList = UIVerticalList( lx, ly, 0, 0, BUTTON_LIST_WIDTH, 1 ) - for i = #items, 1, -1 do - local item = items[i] - if love.filesystem.isDirectory( SaveHandler.getSaveFolder() .. '/' .. item ) then - counter = counter + 1 - buttonList:addChild( createSaveGameEntry( lx, ly, counter, item, SaveHandler.getSaveFolder() .. '/' .. item )) - end - end + -- Create entries for last five savegames. + local items = love.filesystem.getDirectoryItems( SaveHandler.getSaveFolder() ) + local counter = 0 - buttonList:addChild( createBackButton( lx, ly )) + for i = #items, 1, -1 do + local item = items[i] + if love.filesystem.isDirectory( SaveHandler.getSaveFolder() .. '/' .. item ) then + counter = counter + 1 + buttonList:addChild( createSaveGameEntry( lx, ly, counter, item, SaveHandler.getSaveFolder() .. '/' .. item )) + end end + buttonList:addChild( createBackButton( lx, ly )) + return buttonList +end - -- ------------------------------------------------ - -- Public Methods - -- ------------------------------------------------ - - function self:init() - font = TexturePacks.getFont() - - createTitle() - createButtons() - - container = UIContainer() - container:register( buttonList ) - - footer = UICopyrightFooter.new() - end +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - function self:update() - font = TexturePacks.getFont() - container:update() - end +function SavegameScreen:initialize() + self.title = createTitle() + self.buttonList = createButtons() - function self:draw() - font:use() - drawTitle() + self.container = UIContainer() + self.container:register( self.buttonList ) - container:draw() + self.footer = UICopyrightFooter.new() +end - footer:draw() - end +function SavegameScreen:update() + self.container:update() +end - function self:keypressed( _, scancode ) - love.mouse.setVisible( false ) +function SavegameScreen:draw() + drawTitle( self.title ) + self.container:draw() + self.footer:draw() +end - if scancode == 'escape' then - ScreenManager.switch( 'mainmenu' ) - end +function SavegameScreen:keypressed( _, scancode ) + love.mouse.setVisible( false ) - if scancode == 'up' then - container:command( 'up' ) - elseif scancode == 'down' then - container:command( 'down' ) - elseif scancode == 'return' then - container:command( 'activate' ) - end + if scancode == 'escape' then + ScreenManager.switch( 'mainmenu' ) end - function self:mousemoved() - love.mouse.setVisible( true ) + if scancode == 'up' then + self.container:command( 'up' ) + elseif scancode == 'down' then + self.container:command( 'down' ) + elseif scancode == 'return' then + self.container:command( 'activate' ) end +end - --- - -- Handle mousereleased events. - -- - function self:mousereleased() - container:mousecommand( 'activate' ) - end +function SavegameScreen:mousemoved() + love.mouse.setVisible( true ) +end - function self:resize( _, _ ) - local lx = GridHelper.centerElement( BUTTON_LIST_WIDTH, 1 ) - local ly = BUTTON_LIST_Y - buttonList:setOrigin( lx, ly ) - end +--- +-- Handle mousereleased events. +-- +function SavegameScreen:mousereleased() + self.container:mousecommand( 'activate' ) +end - return self +function SavegameScreen:resize( _, _ ) + local lx = GridHelper.centerElement( BUTTON_LIST_WIDTH, 1 ) + local ly = BUTTON_LIST_Y + self.buttonList:setOrigin( lx, ly ) end return SavegameScreen From 4b5b5530e676fa40c61442edd1c368c7cb1e850e Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sat, 9 Dec 2017 01:24:52 +0100 Subject: [PATCH 055/178] Refactor EditorLoadingScreen --- src/ui/mapeditor/EditorLoadingScreen.lua | 204 +++++++++++------------ 1 file changed, 93 insertions(+), 111 deletions(-) diff --git a/src/ui/mapeditor/EditorLoadingScreen.lua b/src/ui/mapeditor/EditorLoadingScreen.lua index 7ae08bdf..cb0ea3c8 100644 --- a/src/ui/mapeditor/EditorLoadingScreen.lua +++ b/src/ui/mapeditor/EditorLoadingScreen.lua @@ -6,7 +6,7 @@ -- Required Modules -- ------------------------------------------------ -local Screen = require( 'lib.screenmanager.Screen' ) +local Screen = require( 'src.ui.screens.Screen' ) local ScreenManager = require( 'lib.screenmanager.ScreenManager' ) local GridHelper = require( 'src.util.GridHelper' ) local UIOutlines = require( 'src.ui.elements.UIOutlines' ) @@ -23,7 +23,7 @@ local Log = require( 'src.util.Log' ) -- Module -- ------------------------------------------------ -local EditorLoadingScreen = {} +local EditorLoadingScreen = Screen:subclass( 'EditorLoadingScreen' ) -- ------------------------------------------------ -- Constants @@ -40,146 +40,128 @@ local FILE_EXTENSIONS = { } -- ------------------------------------------------ --- Constructor +-- Private Functions -- ------------------------------------------------ -function EditorLoadingScreen.new() - local self = Screen.new() - - -- ------------------------------------------------ - -- Private Variables - -- ------------------------------------------------ - - local buttonList - - local background - local outlines - local x, y - - local uiContainer - - -- ------------------------------------------------ - -- Private Functions - -- ------------------------------------------------ - - --- - -- Generates the outlines for this screen. - -- - local function generateOutlines() - outlines = UIOutlines( x, y, 0, 0, UI_GRID_WIDTH, UI_GRID_HEIGHT ) - - -- Horizontal borders. - for ox = 0, UI_GRID_WIDTH-1 do - outlines:add( ox, 0 ) -- Top - outlines:add( ox, UI_GRID_HEIGHT-1 ) -- Bottom - end +--- +-- Generates the outlines for this screen. +-- @tparam number x The origin of the screen along the x-axis. +-- @tparam number y The origin of the screen along the y-axis. +-- @treturn UIOutlines The newly created UIOutlines instance. +-- +local function generateOutlines( x, y ) + local outlines = UIOutlines( x, y, 0, 0, UI_GRID_WIDTH, UI_GRID_HEIGHT ) - -- Vertical outlines. - for oy = 0, UI_GRID_HEIGHT-1 do - outlines:add( 0, oy ) -- Left - outlines:add( UI_GRID_WIDTH-1, oy ) -- Right - end + -- Horizontal borders. + for ox = 0, UI_GRID_WIDTH-1 do + outlines:add( ox, 0 ) -- Top + outlines:add( ox, UI_GRID_HEIGHT-1 ) -- Bottom + end - outlines:refresh() + -- Vertical outlines. + for oy = 0, UI_GRID_HEIGHT-1 do + outlines:add( 0, oy ) -- Left + outlines:add( UI_GRID_WIDTH-1, oy ) -- Right end - local function isValidFile( path ) - if not love.filesystem.isFile( path ) then - return - end + outlines:refresh() + return outlines +end - local extension = Util.getFileExtension( path ) - if extension == FILE_EXTENSIONS.LAYOUT or extension == FILE_EXTENSIONS.PREFAB then - return true - end - Log.warn( string.format( 'Ignoring invalid file extension: "%s".', extension ), 'EditorLoadingScreen' ) +local function isValidFile( path ) + if not love.filesystem.isFile( path ) then + return end - local function createListEntry( lx, ly, item, folder ) - local function callback() - ScreenManager.publish( 'LOAD_LAYOUT', Compressor.load( folder .. item )) - ScreenManager.pop() - ScreenManager.pop() - end + local extension = Util.getFileExtension( path ) + if extension == FILE_EXTENSIONS.LAYOUT or extension == FILE_EXTENSIONS.PREFAB then + return true + end + Log.warn( string.format( 'Ignoring invalid file extension: "%s".', extension ), 'EditorLoadingScreen' ) +end - return UIButton( lx, ly, 0, 0, UI_GRID_WIDTH, 1, callback, item ) +local function createListEntry( lx, ly, item, folder ) + local function callback() + ScreenManager.publish( 'LOAD_LAYOUT', Compressor.load( folder .. item )) + ScreenManager.pop() + ScreenManager.pop() end - local function createButtons( directory ) - local lx, ly = GridHelper.centerElement( UI_GRID_WIDTH, UI_GRID_HEIGHT ) + return UIButton( lx, ly, 0, 0, UI_GRID_WIDTH, 1, callback, item ) +end - buttonList = UIVerticalList( lx, ly, 0, BUTTON_LIST_VERTICAL_OFFSET, UI_GRID_WIDTH, 1 ) +local function createButtons( directory ) + local lx, ly = GridHelper.centerElement( UI_GRID_WIDTH, UI_GRID_HEIGHT ) + local buttonList = UIVerticalList( lx, ly, 0, BUTTON_LIST_VERTICAL_OFFSET, UI_GRID_WIDTH, 1 ) - -- Create entries for last five savegames. - local items = love.filesystem.getDirectoryItems( directory ) - for i = 1, #items do - if isValidFile( directory .. items[i] ) then - buttonList:addChild( createListEntry( lx, ly, items[i], directory )) - end + -- Create entries for last five savegames. + local items = love.filesystem.getDirectoryItems( directory ) + for i = 1, #items do + if isValidFile( directory .. items[i] ) then + buttonList:addChild( createListEntry( lx, ly, items[i], directory )) end - - local resumeButton = UIButton( lx, ly, 0, 0, UI_GRID_WIDTH, 1, function() ScreenManager.pop() end, Translator.getText( 'ui_back' )) - buttonList:addChild( resumeButton ) - - buttonList:setFocus( true ) end + local resumeButton = UIButton( lx, ly, 0, 0, UI_GRID_WIDTH, 1, function() ScreenManager.pop() end, Translator.getText( 'ui_back' )) + buttonList:addChild( resumeButton ) - -- ------------------------------------------------ - -- Public Methods - -- ------------------------------------------------ + buttonList:setFocus( true ) + return buttonList +end - function self:init( directory ) - x, y = GridHelper.centerElement( UI_GRID_WIDTH, UI_GRID_HEIGHT ) - background = UIBackground( x, y, 0, 0, UI_GRID_WIDTH, UI_GRID_HEIGHT ) - generateOutlines() - createButtons( directory ) +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - uiContainer = UIContainer() - uiContainer:register( buttonList ) - end +function EditorLoadingScreen:initialize( directory ) + self.x, self.y = GridHelper.centerElement( UI_GRID_WIDTH, UI_GRID_HEIGHT ) + self.background = UIBackground( self.x, self.y, 0, 0, UI_GRID_WIDTH, UI_GRID_HEIGHT ) - function self:update() - uiContainer:update() - end + self.outlines = generateOutlines( self.x, self.y ) + self.buttonList = createButtons( directory ) - function self:draw() - background:draw() - outlines:draw() - uiContainer:draw() - end + self.uiContainer = UIContainer() + self.uiContainer:register( self.buttonList ) +end - function self:keypressed( _, scancode ) - if scancode == 'escape' then - ScreenManager.pop() - end +function EditorLoadingScreen:update() + self.uiContainer:update() +end - if scancode == 'up' then - uiContainer:command( 'up' ) - elseif scancode == 'down' then - uiContainer:command( 'down' ) - elseif scancode == 'return' then - uiContainer:command( 'activate' ) - end - end +function EditorLoadingScreen:draw() + self.background:draw() + self.outlines:draw() + self.uiContainer:draw() +end - function self:mousemoved() - love.mouse.setVisible( true ) +function EditorLoadingScreen:keypressed( _, scancode ) + if scancode == 'escape' then + ScreenManager.pop() end - function self:mousereleased() - uiContainer:command( 'activate' ) + if scancode == 'up' then + self.uiContainer:command( 'up' ) + elseif scancode == 'down' then + self.uiContainer:command( 'down' ) + elseif scancode == 'return' then + self.uiContainer:command( 'activate' ) end +end - function self:resize( _, _ ) - x, y = GridHelper.centerElement( UI_GRID_WIDTH, UI_GRID_HEIGHT ) - background:setOrigin( x, y ) - outlines:setOrigin( x, y ) - buttonList:setOrigin( x, y ) - end +function EditorLoadingScreen:mousemoved() + love.mouse.setVisible( true ) +end + +function EditorLoadingScreen:mousereleased() + self.uiContainer:command( 'activate' ) +end - return self +function EditorLoadingScreen:resize( _, _ ) + self.x, self.y = GridHelper.centerElement( UI_GRID_WIDTH, UI_GRID_HEIGHT ) + self.background:setOrigin( self.x, self.y ) + self.outlines:setOrigin( self.x, self.y ) + self.buttonList:setOrigin( self.x, self.y ) end return EditorLoadingScreen From ec5d89d66f0f6d0e76d73a115726bcc71cb5356a Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sat, 9 Dec 2017 01:29:27 +0100 Subject: [PATCH 056/178] Refactor PrefabEditor --- src/ui/screens/PrefabEditor.lua | 274 +++++++++++++++----------------- 1 file changed, 124 insertions(+), 150 deletions(-) diff --git a/src/ui/screens/PrefabEditor.lua b/src/ui/screens/PrefabEditor.lua index 2f091ca4..95dbb8b4 100644 --- a/src/ui/screens/PrefabEditor.lua +++ b/src/ui/screens/PrefabEditor.lua @@ -6,7 +6,7 @@ -- Required Modules -- ------------------------------------------------ -local Screen = require( 'lib.screenmanager.Screen' ) +local Screen = require( 'src.ui.screens.Screen' ) local ScreenManager = require( 'lib.screenmanager.ScreenManager' ) local TileFactory = require( 'src.map.tiles.TileFactory' ) local WorldObjectFactory = require( 'src.map.worldobjects.WorldObjectFactory' ) @@ -24,7 +24,7 @@ local GridHelper = require( 'src.util.GridHelper' ) -- Module -- ------------------------------------------------ -local PrefabEditor = {} +local PrefabEditor = Screen:subclass( 'PrefabEditor' ) -- ------------------------------------------------ -- Constants @@ -34,198 +34,172 @@ local SELECTOR_WIDTH = 10 local SELECTOR_HEIGHT = 10 -- ------------------------------------------------ --- Constructor +-- Private Functions -- ------------------------------------------------ -function PrefabEditor.new() - local self = Screen.new() +local function createTileSelector( tileTemplates, tool ) + local lx, ly = 1, 1 + local tileSelector = UIVerticalList( lx, ly, 0, 0, SELECTOR_WIDTH, SELECTOR_HEIGHT ) - -- ------------------------------------------------ - -- Private Variables - -- ------------------------------------------------ - - local camera - - local tileTemplates - local objectTemplates - - local tileSelector - local objectSelector - - local canvas - local tool + local counter = 0 + for id, template in pairs( tileTemplates ) do + local function callback() + tool:setBrush( template, 'tile' ) + end - local uiContainer + local tmp = UIButton( lx, ly, 0, counter, SELECTOR_WIDTH, 1, callback, Translator.getText( id ), 'left' ) + tmp:setIcon( id ) + tileSelector:addChild( tmp ) - -- ------------------------------------------------ - -- Private Functions - -- ------------------------------------------------ + counter = counter + 1 + end - local function createTileSelector() - local lx, ly = 1, 1 + return tileSelector +end - tileSelector = UIVerticalList( lx, ly, 0, 0, SELECTOR_WIDTH, SELECTOR_HEIGHT ) +local function createWorldObjectSelector( objectTemplates, tool ) + local lx, ly = 1, SELECTOR_HEIGHT + 2 + local objectSelector = UIVerticalList( lx, ly, 0, 0, SELECTOR_WIDTH, SELECTOR_HEIGHT ) - local counter = 0 - for id, template in pairs( tileTemplates ) do - local function callback() - tool:setBrush( template, 'tile' ) - end + local counter = 0 + 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 tmp = UIButton( lx, ly, 0, counter, SELECTOR_WIDTH, 1, callback, Translator.getText( id ), 'left' ) + if template.openable then + tmp:setIcon( id, 'closed' ) + else tmp:setIcon( id ) - tileSelector:addChild( tmp ) - - counter = counter + 1 end + objectSelector:addChild( tmp ) + + counter = counter + 1 end - local function createWorldObjectSelector() - local lx, ly = 1, SELECTOR_HEIGHT + 2 + return objectSelector +end - objectSelector = UIVerticalList( lx, ly, 0, 0, SELECTOR_WIDTH, SELECTOR_HEIGHT ) +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - local counter = 0 - for id, template in pairs( objectTemplates ) do - local function callback() - tool:setBrush( template, 'worldObject' ) - end +function PrefabEditor:initialize() + love.filesystem.createDirectory( 'mods/maps/prefabs' ) + love.mouse.setVisible( true ) - local tmp = UIButton( lx, ly, 0, counter, SELECTOR_WIDTH, 1, callback, Translator.getText( id ), 'left' ) - if template.openable then - tmp:setIcon( id, 'closed' ) - else - tmp:setIcon( id ) - end - objectSelector:addChild( tmp ) + self.canvas = PrefabCanvas( 'XS' ) - counter = counter + 1 - end - end + self.camera = CameraHandler.new( self.canvas:getWidth(), self.canvas:getHeight(), TexturePacks.getTileDimensions() ) - -- ------------------------------------------------ - -- Public Methods - -- ------------------------------------------------ + self.tool = PrefabBrush() - function self:init() - love.filesystem.createDirectory( 'mods/maps/prefabs' ) - love.mouse.setVisible( true ) + local tileTemplates = TileFactory.getTemplates() + self.tileSelector = createTileSelector( tileTemplates, self.tool ) - canvas = PrefabCanvas( 'XS' ) + local objectTemplates = WorldObjectFactory.getTemplates() + self.objectSelector = createWorldObjectSelector( objectTemplates, self.tool ) - camera = CameraHandler.new( canvas:getWidth(), canvas:getHeight(), TexturePacks.getTileDimensions() ) + self.uiContainer = UIContainer() + self.uiContainer:register( self.tileSelector ) + self.uiContainer:register( self.objectSelector ) +end - tileTemplates = TileFactory.getTemplates() - createTileSelector( tileTemplates ) +function PrefabEditor:receive( event, ... ) + if event == 'LOAD_LAYOUT' then + self.canvas:load( ... ) + end +end - objectTemplates = WorldObjectFactory.getTemplates() - createWorldObjectSelector( objectTemplates ) +function PrefabEditor:draw() + self.tileSelector:draw() + self.objectSelector:draw() - tool = PrefabBrush() + self.camera:attach() + self.canvas:draw() + self.tool:draw() + self.camera:detach() - uiContainer = UIContainer() - uiContainer:register( tileSelector ) - uiContainer:register( objectSelector ) - end + local _, sh = GridHelper.getScreenGridDimensions() + local tw, th = TexturePacks.getTileDimensions() - function self:receive( event, ... ) - if event == 'LOAD_LAYOUT' then - local newLayout = ... - canvas:load( newLayout ) - end + local template, type = self.tool:getBrush() + if template then + love.graphics.print( string.format( 'Selected brush: %s (%s)', Translator.getText( template.id ), type ), tw, (sh-1) * th ) end +end - function self:draw() - tileSelector:draw() - objectSelector:draw() +function PrefabEditor:update( dt ) + self.camera:update( dt ) - camera:attach() - canvas:draw() - tool:draw() - camera:detach() + self.uiContainer:update() - local _, sh = GridHelper.getScreenGridDimensions() - local tw, th = TexturePacks.getTileDimensions() + self.tool:setPosition( self.camera:getMouseWorldGridPosition() ) + self.tool:use( self.canvas, self.camera ) +end - local template, type = tool:getBrush() - if template then - love.graphics.print( string.format( 'Selected brush: %s (%s)', Translator.getText( template.id ), type ), tw, (sh-1) * th ) - end +function PrefabEditor:keypressed( _, scancode ) + if scancode == 'escape' then + ScreenManager.push( 'prefabeditormenu', self.canvas ) end - function self:update( dt ) - camera:update( dt ) - - uiContainer:update() - - tool:setPosition( camera:getMouseWorldGridPosition() ) - tool:use( canvas, camera ) + if scancode == ']' then + self.tool:increase() + elseif scancode == '/' then + self.tool:decrease() end - function self:keypressed( _, scancode ) - if scancode == 'escape' then - ScreenManager.push( 'prefabeditormenu', canvas ) - end - - if scancode == ']' then - tool:increase() - elseif scancode == '/' then - tool:decrease() - end - - if scancode == 'd' then - tool:setMode( 'draw' ) - elseif scancode == 'e' then - tool:setMode( 'erase' ) - elseif scancode == 'f' then - tool:setMode( 'fill' ) - end - - if scancode == '1' then - canvas:setSize( 'XS' ) - camera = CameraHandler.new( canvas:getWidth(), canvas:getHeight(), TexturePacks.getTileDimensions() ) - elseif scancode == '2' then - canvas:setSize( 'S' ) - camera = CameraHandler.new( canvas:getWidth(), canvas:getHeight(), TexturePacks.getTileDimensions() ) - elseif scancode == '3' then - canvas:setSize( 'M' ) - camera = CameraHandler.new( canvas:getWidth(), canvas:getHeight(), TexturePacks.getTileDimensions() ) - elseif scancode == '4' then - canvas:setSize( 'L' ) - camera = CameraHandler.new( canvas:getWidth(), canvas:getHeight(), TexturePacks.getTileDimensions() ) - elseif scancode == '5' then - canvas:setSize( 'XL' ) - camera = CameraHandler.new( canvas:getWidth(), canvas:getHeight(), TexturePacks.getTileDimensions() ) - end - - if scancode == 'h' then - canvas:toggleObjects() - end - - if scancode == 'tab' then - uiContainer:next() - end + if scancode == 'd' then + self.tool:setMode( 'draw' ) + elseif scancode == 'e' then + self.tool:setMode( 'erase' ) + elseif scancode == 'f' then + self.tool:setMode( 'fill' ) + end - if scancode == 'return' then - uiContainer:command( 'activate' ) - end - uiContainer:command( scancode ) + if scancode == '1' then + self.canvas:setSize( 'XS' ) + self.camera = CameraHandler.new( self.canvas:getWidth(), self.canvas:getHeight(), TexturePacks.getTileDimensions() ) + elseif scancode == '2' then + self.canvas:setSize( 'S' ) + self.camera = CameraHandler.new( self.canvas:getWidth(), self.canvas:getHeight(), TexturePacks.getTileDimensions() ) + elseif scancode == '3' then + self.canvas:setSize( 'M' ) + self.camera = CameraHandler.new( self.canvas:getWidth(), self.canvas:getHeight(), TexturePacks.getTileDimensions() ) + elseif scancode == '4' then + self.canvas:setSize( 'L' ) + self.camera = CameraHandler.new( self.canvas:getWidth(), self.canvas:getHeight(), TexturePacks.getTileDimensions() ) + elseif scancode == '5' then + self.canvas:setSize( 'XL' ) + self.camera = CameraHandler.new( self.canvas:getWidth(), self.canvas:getHeight(), TexturePacks.getTileDimensions() ) end - function self:mousemoved() - love.mouse.setVisible( true ) + if scancode == 'h' then + self.canvas:toggleObjects() end - function self:mousepressed() - uiContainer:mousecommand( 'activate' ) - tool:setActive( true ) + if scancode == 'tab' then + self.uiContainer:next() end - function self:mousereleased() - tool:setActive( false ) + if scancode == 'return' then + self.uiContainer:command( 'activate' ) end + self.uiContainer:command( scancode ) +end + +function PrefabEditor:mousemoved() + love.mouse.setVisible( true ) +end + +function PrefabEditor:mousepressed() + self.uiContainer:mousecommand( 'activate' ) + self.tool:setActive( true ) +end - return self +function PrefabEditor:mousereleased() + self.tool:setActive( false ) end return PrefabEditor From 3ddf4a518b79a23ca4effb7ee3d46296cd8dcd02 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sat, 9 Dec 2017 01:35:33 +0100 Subject: [PATCH 057/178] Refactor PrefabEditorMenu --- src/ui/screens/PrefabEditorMenu.lua | 207 +++++++++++++--------------- 1 file changed, 95 insertions(+), 112 deletions(-) diff --git a/src/ui/screens/PrefabEditorMenu.lua b/src/ui/screens/PrefabEditorMenu.lua index 05c075c4..26cae871 100644 --- a/src/ui/screens/PrefabEditorMenu.lua +++ b/src/ui/screens/PrefabEditorMenu.lua @@ -6,7 +6,7 @@ -- Required Modules -- ------------------------------------------------ -local Screen = require( 'lib.screenmanager.Screen' ) +local Screen = require( 'src.ui.screens.Screen' ) local ScreenManager = require( 'lib.screenmanager.ScreenManager' ) local Translator = require( 'src.util.Translator' ) local UIOutlines = require( 'src.ui.elements.UIOutlines' ) @@ -22,7 +22,7 @@ local UIContainer = require( 'src.ui.elements.UIContainer' ) -- Module -- ------------------------------------------------ -local PrefabEditorMenu = {} +local PrefabEditorMenu = Screen:subclass( 'PrefabEditorMenu' ) -- ------------------------------------------------ -- Constants @@ -37,50 +37,35 @@ local SAVE_DIR = 'mods/maps/prefabs/' local FILE_EXTENSION = '.prefab' -- ------------------------------------------------ --- Constructor +-- Private Methods -- ------------------------------------------------ -function PrefabEditorMenu.new() - local self = Screen.new() - - -- ------------------------------------------------ - -- Private Variables - -- ------------------------------------------------ - - local buttonList - local container - - local background - local outlines - local x, y - - local canvas - - -- ------------------------------------------------ - -- Private Methods - -- ------------------------------------------------ - - --- - -- Generates the outlines for this screen. - -- - local function generateOutlines() - outlines = UIOutlines( x, y, 0, 0, UI_GRID_WIDTH, UI_GRID_HEIGHT ) - - -- Horizontal borders. - for ox = 0, UI_GRID_WIDTH-1 do - outlines:add( ox, 0 ) -- Top - outlines:add( ox, UI_GRID_HEIGHT-1 ) -- Bottom - end +--- +-- Generates the outlines for this screen. +-- @tparam number x The origin of the screen along the x-axis. +-- @tparam number y The origin of the screen along the y-axis. +-- @treturn UIOutlines The newly created UIOutlines instance. +-- +local function generateOutlines( x, y ) + local outlines = UIOutlines( x, y, 0, 0, UI_GRID_WIDTH, UI_GRID_HEIGHT ) - -- Vertical outlines. - for oy = 0, UI_GRID_HEIGHT-1 do - outlines:add( 0, oy ) -- Left - outlines:add( UI_GRID_WIDTH-1, oy ) -- Right - end + -- Horizontal borders. + for ox = 0, UI_GRID_WIDTH-1 do + outlines:add( ox, 0 ) -- Top + outlines:add( ox, UI_GRID_HEIGHT-1 ) -- Bottom + end - outlines:refresh() + -- Vertical outlines. + for oy = 0, UI_GRID_HEIGHT-1 do + outlines:add( 0, oy ) -- Left + outlines:add( UI_GRID_WIDTH-1, oy ) -- Right end + outlines:refresh() + return outlines +end + +local function createSaveButton( lx, ly, canvas ) local function savePrefab() local function confirmCallback( name ) Log.debug( 'Saving a new prefab: ' .. name .. FILE_EXTENSION, 'PrefabEditorMenu' ) @@ -96,103 +81,101 @@ function PrefabEditorMenu.new() ScreenManager.push( 'inputdialog', Translator.getText( 'ui_prefabeditor_enter_name' ), false, confirmCallback ) end - local function loadPrefab() - ScreenManager.push( 'editorloading', SAVE_DIR ) - end + return UIButton( lx, ly, 0, 0, UI_GRID_WIDTH, 1, savePrefab, Translator.getText( 'ui_prefabeditor_save' )) +end - local function testMap() - ScreenManager.pop() - ScreenManager.push( 'maptest' ) - end +local function loadPrefab() + ScreenManager.push( 'editorloading', SAVE_DIR ) +end - local function createButtons() - local lx, ly = GridHelper.centerElement( UI_GRID_WIDTH, UI_GRID_HEIGHT ) +local function testMap() + ScreenManager.pop() + ScreenManager.push( 'maptest' ) +end - buttonList = UIVerticalList( lx, ly, 0, BUTTON_LIST_VERTICAL_OFFSET, UI_GRID_WIDTH, UI_GRID_HEIGHT ) +local function createButtons( canvas ) + local lx, ly = GridHelper.centerElement( UI_GRID_WIDTH, UI_GRID_HEIGHT ) + local buttonList = UIVerticalList( lx, ly, 0, BUTTON_LIST_VERTICAL_OFFSET, UI_GRID_WIDTH, UI_GRID_HEIGHT ) - local savePrefabButton = UIButton( lx, ly, 0, 0, UI_GRID_WIDTH, 1, savePrefab, Translator.getText( 'ui_prefabeditor_save' )) - buttonList:addChild( savePrefabButton ) + local savePrefabButton = createSaveButton( lx, ly, canvas ) + buttonList:addChild( savePrefabButton ) - local loadPrefabButton = UIButton( lx, ly, 0, 0, UI_GRID_WIDTH, 1, loadPrefab, Translator.getText( 'ui_prefabeditor_load' )) - buttonList:addChild( loadPrefabButton ) + local loadPrefabButton = UIButton( lx, ly, 0, 0, UI_GRID_WIDTH, 1, loadPrefab, Translator.getText( 'ui_prefabeditor_load' )) + buttonList:addChild( loadPrefabButton ) - local testButton = UIButton( lx, ly, 0, 0, UI_GRID_WIDTH, 1, testMap, Translator.getText( 'ui_prefabeditor_test' )) - buttonList:addChild( testButton ) + local testButton = UIButton( lx, ly, 0, 0, UI_GRID_WIDTH, 1, testMap, Translator.getText( 'ui_prefabeditor_test' )) + buttonList:addChild( testButton ) - local switchButton = UIButton( lx, ly, 0, 0, UI_GRID_WIDTH, 1, function() ScreenManager.switch( 'mapeditor' ) end, Translator.getText( 'ui_prefabeditor_switch' )) - buttonList:addChild( switchButton ) + local switchButton = UIButton( lx, ly, 0, 0, UI_GRID_WIDTH, 1, function() ScreenManager.switch( 'mapeditor' ) end, Translator.getText( 'ui_prefabeditor_switch' )) + buttonList:addChild( switchButton ) - local exitButton = UIButton( lx, ly, 0, 0, UI_GRID_WIDTH, 1, function() ScreenManager.switch( 'mainmenu' ) end, Translator.getText( 'ui_prefabeditor_exit' )) - buttonList:addChild( exitButton ) + local exitButton = UIButton( lx, ly, 0, 0, UI_GRID_WIDTH, 1, function() ScreenManager.switch( 'mainmenu' ) end, Translator.getText( 'ui_prefabeditor_exit' )) + buttonList:addChild( exitButton ) - buttonList:setFocus( true ) - end - - -- ------------------------------------------------ - -- Public Methods - -- ------------------------------------------------ + buttonList:setFocus( true ) + return buttonList +end - function self:init( ncanvas ) - canvas = ncanvas +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - x, y = GridHelper.centerElement( UI_GRID_WIDTH, UI_GRID_HEIGHT ) +function PrefabEditorMenu:initialize( canvas ) + self.x, self.y = GridHelper.centerElement( UI_GRID_WIDTH, UI_GRID_HEIGHT ) - background = UIBackground( x, y, 0, 0, UI_GRID_WIDTH, UI_GRID_HEIGHT ) - generateOutlines() - createButtons() + self.background = UIBackground( self.x, self.y, 0, 0, UI_GRID_WIDTH, UI_GRID_HEIGHT ) + self.outlines = generateOutlines( self.x, self.y ) + self.buttonList = createButtons( canvas ) - container = UIContainer() - container:register( buttonList ) - end + self.container = UIContainer() + self.container:register( self.buttonList ) +end - function self:draw() - if not self:isActive() then - return - end - background:draw() - outlines:draw() - container:draw() +function PrefabEditorMenu:draw() + if not self:isActive() then + return end + self.background:draw() + self.outlines:draw() + self.container:draw() +end - function self:update() - if not self:isActive() then - return - end - container:update() +function PrefabEditorMenu:update() + if not self:isActive() then + return end + self.container:update() +end - function self:keypressed( _, scancode ) - love.mouse.setVisible( false ) - - if scancode == 'escape' then - ScreenManager.pop() - end +function PrefabEditorMenu:keypressed( _, scancode ) + love.mouse.setVisible( false ) - if scancode == 'up' then - container:command( 'up' ) - elseif scancode == 'down' then - container:command( 'down' ) - elseif scancode == 'return' then - container:command( 'activate' ) - end + if scancode == 'escape' then + ScreenManager.pop() end - function self:mousemoved() - love.mouse.setVisible( true ) + if scancode == 'up' then + self.container:command( 'up' ) + elseif scancode == 'down' then + self.container:command( 'down' ) + elseif scancode == 'return' then + self.container:command( 'activate' ) end +end - function self:mousereleased() - container:command( 'activate' ) - end +function PrefabEditorMenu:mousemoved() + love.mouse.setVisible( true ) +end - function self:resize( _, _ ) - x, y = GridHelper.centerElement( UI_GRID_WIDTH, UI_GRID_HEIGHT ) - background:setOrigin( x, y ) - outlines:setOrigin( x, y ) - buttonList:setOrigin( x, y ) - end +function PrefabEditorMenu:mousereleased() + self.container:command( 'activate' ) +end - return self +function PrefabEditorMenu:resize( _, _ ) + self.x, self.y = GridHelper.centerElement( UI_GRID_WIDTH, UI_GRID_HEIGHT ) + self.background:setOrigin( self.x, self.y ) + self.outlines:setOrigin( self.x, self.y ) + self.buttonList:setOrigin( self.x, self.y ) end return PrefabEditorMenu From dace358671a8e7e61af998bde92ed9525b306520 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sat, 9 Dec 2017 01:41:27 +0100 Subject: [PATCH 058/178] Refactor OptionsScreen --- src/ui/screens/OptionsScreen.lua | 566 +++++++++++++++---------------- 1 file changed, 273 insertions(+), 293 deletions(-) diff --git a/src/ui/screens/OptionsScreen.lua b/src/ui/screens/OptionsScreen.lua index 656669b9..a82f4935 100644 --- a/src/ui/screens/OptionsScreen.lua +++ b/src/ui/screens/OptionsScreen.lua @@ -8,7 +8,7 @@ -- Required Modules -- ------------------------------------------------ -local Screen = require( 'lib.screenmanager.Screen' ) +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' ) @@ -24,7 +24,7 @@ local UIContainer = require( 'src.ui.elements.UIContainer' ) -- Module -- ------------------------------------------------ -local OptionsScreen = {} +local OptionsScreen = Screen:subclass( 'OptionsScreen' ) -- ------------------------------------------------ -- Constants @@ -48,354 +48,334 @@ local BUTTON_LIST_WIDTH = 20 local BUTTON_LIST_Y = 20 -- ------------------------------------------------ --- Constructor +-- Private Functions -- ------------------------------------------------ -function OptionsScreen.new() - local self = Screen.new() - - -- ------------------------------------------------ - -- Private Variables - -- ------------------------------------------------ - - local title - local buttonList - local font - local footer - local container - - -- ------------------------------------------------ - -- Private Functions - -- ------------------------------------------------ - - --- - -- Creates the ASCII title at the top of the page. - -- - local function createTitle() - 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() ) +--- +-- 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() - 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 +--- +-- 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. - -- - local function close() - if Settings.hasChanged() then - local function confirm() - ScreenManager.switch( 'mainmenu' ) - end - local function cancel() - ScreenManager.pop() - end - ScreenManager.push( 'confirm', Translator.getText( 'ui_unsaved_changes' ), confirm, cancel ) - else +--- +-- Closes the OptionsScreen and displays a confirmation dialog if any +-- settings have been changed. +-- +local function close() + if Settings.hasChanged() then + local function confirm() ScreenManager.switch( 'mainmenu' ) end + local function cancel() + ScreenManager.pop() + end + ScreenManager.push( 'confirm', Translator.getText( 'ui_unsaved_changes' ), confirm, cancel ) + else + ScreenManager.switch( 'mainmenu' ) end +end + +--- +-- Applies the settings and saves them to a file. +-- +local function applySettings() + Settings.save() + Translator.init( Settings.getLocale() ) + TexturePacks.setCurrent( Settings.getTexturepack() ) + love.window.setFullscreen( Settings.getFullscreen() ) + ScreenManager.push( 'information', Translator.getText( 'ui_applied_settings' )) +end - --- - -- Applies the settings and saves them to a file. - -- - local function applySettings() - Settings.save() - Translator.init( Settings.getLocale() ) - TexturePacks.setCurrent( Settings.getTexturepack() ) - love.window.setFullscreen( Settings.getFullscreen() ) - ScreenManager.push( 'information', Translator.getText( 'ui_applied_settings' )) +--- +-- Creates a UISelectField which allows the user to change the game's +-- language 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 UISelectField The newly created UISelectField. +-- +local function createLanguageOption( lx, ly ) + -- The list of values to display. + local listOfValues = { + { displayTextID = Translator.getText( 'ui_lang_eng' ), value = 'en_EN' } + } + + -- The function to call when the value of the UISelectField changes. + local function callback( val ) + Settings.setLocale( val ) end - --- - -- Creates a UISelectField which allows the user to change the game's - -- language 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 UISelectField The newly created UISelectField. - -- - local function createLanguageOption( lx, ly ) - -- The list of values to display. - local listOfValues = { - { displayTextID = Translator.getText( 'ui_lang_eng' ), value = 'en_EN' } - } - - -- The function to call when the value of the UISelectField changes. - local function callback( val ) - Settings.setLocale( val ) + -- Search the value corresponding to the currently selected option or + -- take the first one and make it the current display value. + local default = Settings.getLocale() + for i, option in ipairs( listOfValues ) do + if option.value == Translator.getLocale() then + default = i end + end - -- Search the value corresponding to the currently selected option or - -- take the first one and make it the current display value. - local default = Settings.getLocale() - for i, option in ipairs( listOfValues ) do - if option.value == Translator.getLocale() then - default = i - end - end + -- Create the UISelectField. + return UISelectField( lx, ly, 0, 0, BUTTON_LIST_WIDTH, 1, Translator.getText( 'ui_lang' ), listOfValues, callback, default ) +end - -- Create the UISelectField. - return UISelectField( lx, ly, 0, 0, BUTTON_LIST_WIDTH, 1, Translator.getText( 'ui_lang' ), listOfValues, callback, default ) +--- +-- Creates a UISelectField which allows the user to change the game's +-- fullscreen 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 UISelectField The newly created UISelectField. +-- +local function createFullscreenOption( lx, ly ) + -- The list of values to display. + local listOfValues = { + { displayTextID = Translator.getText( 'ui_on' ), value = true }, + { displayTextID = Translator.getText( 'ui_off' ), value = false } + } + + -- The function to call when the value of the UISelectField changes. + local function callback( val ) + Settings.setFullscreen( val ) end - --- - -- Creates a UISelectField which allows the user to change the game's - -- fullscreen 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 UISelectField The newly created UISelectField. - -- - local function createFullscreenOption( lx, ly ) - -- The list of values to display. - local listOfValues = { - { displayTextID = Translator.getText( 'ui_on' ), value = true }, - { displayTextID = Translator.getText( 'ui_off' ), value = false } - } - - -- The function to call when the value of the UISelectField changes. - local function callback( val ) - Settings.setFullscreen( val ) - end - - -- Search the value corresponding to the currently selected option or - -- take the first one and make it the current display value. - local default = Settings.getFullscreen() - for i, option in ipairs( listOfValues ) do - if option.value == love.window.getFullscreen() then - default = i - end + -- Search the value corresponding to the currently selected option or + -- take the first one and make it the current display value. + local default = Settings.getFullscreen() + for i, option in ipairs( listOfValues ) do + if option.value == love.window.getFullscreen() then + default = i end - - -- Create the UISelectField. - return UISelectField( lx, ly, 0, 0, BUTTON_LIST_WIDTH, 1, Translator.getText( 'ui_fullscreen' ), listOfValues, callback, default ) end - --- - -- Creates a UISelectField which allows the user to activate the ingame map editor. - -- @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 UISelectField The newly created UISelectField. - -- - local function createIngameEditorOption( lx, ly ) - -- The list of values to display. - local listOfValues = { - { displayTextID = Translator.getText( 'ui_on' ), value = true }, - { displayTextID = Translator.getText( 'ui_off' ), value = false } - } - - -- 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 + -- Create the UISelectField. + return UISelectField( lx, ly, 0, 0, BUTTON_LIST_WIDTH, 1, Translator.getText( 'ui_fullscreen' ), listOfValues, callback, default ) +end - -- Search the value corresponding to the currently selected option or - -- take the first one and make it the current display value. - local default = Settings.getIngameEditor() - for i, option in ipairs( listOfValues ) do - if option.value == default then - default = i - end +--- +-- Creates a UISelectField which allows the user to activate the ingame map editor. +-- @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 UISelectField The newly created UISelectField. +-- +local function createIngameEditorOption( lx, ly ) + -- The list of values to display. + local listOfValues = { + { displayTextID = Translator.getText( 'ui_on' ), value = true }, + { displayTextID = Translator.getText( 'ui_off' ), value = false } + } + + -- 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 - - -- Create the UISelectField. - return UISelectField( lx, ly, 0, 0, BUTTON_LIST_WIDTH, 1, Translator.getText( 'ui_settings_ingame_editor' ), listOfValues, callback, default ) end - --- - -- Creates a UISelectField which allows the user to change the game's - -- fullscreen 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 UISelectField The newly created UISelectField. - -- - local function createTexturePackOption( lx, ly ) - -- The list of values to display. We populate it with the TexturePacks - -- we found in the game's directory. - local listOfValues = {} - local packs = TexturePacks.getTexturePacks() - for name, _ in pairs( packs ) do - listOfValues[#listOfValues + 1] = { displayTextID = name, value = name } + -- Search the value corresponding to the currently selected option or + -- take the first one and make it the current display value. + local default = Settings.getIngameEditor() + for i, option in ipairs( listOfValues ) do + if option.value == default then + default = i end + end - -- The function to call when the value of the UISelectField changes. - local function callback( val ) - Settings.setTexturepack( val ) - end + -- Create the UISelectField. + return UISelectField( lx, ly, 0, 0, BUTTON_LIST_WIDTH, 1, Translator.getText( 'ui_settings_ingame_editor' ), listOfValues, callback, default ) +end - -- Search the value corresponding to the currently selected option or - -- take the first one and make it the current display value. - local default = Settings.getTexturepack() - for i, option in ipairs( listOfValues ) do - if option.value == TexturePacks.getName() then - default = i - end - end +--- +-- Creates a UISelectField which allows the user to change the game's +-- fullscreen 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 UISelectField The newly created UISelectField. +-- +local function createTexturePackOption( lx, ly ) + -- The list of values to display. We populate it with the TexturePacks + -- we found in the game's directory. + local listOfValues = {} + local packs = TexturePacks.getTexturePacks() + for name, _ in pairs( packs ) do + listOfValues[#listOfValues + 1] = { displayTextID = name, value = name } + end - -- Create the UISelectField. - return UISelectField( lx, ly, 0, 0, BUTTON_LIST_WIDTH, 1, Translator.getText( 'ui_texturepack' ), listOfValues, callback, default ) + -- The function to call when the value of the UISelectField changes. + local function callback( val ) + Settings.setTexturepack( val ) 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. - -- - local function createApplyButton( lx, ly ) - -- The function to call when the button is activated. - local function callback() - applySettings() + -- Search the value corresponding to the currently selected option or + -- take the first one and make it the current display value. + local default = Settings.getTexturepack() + for i, option in ipairs( listOfValues ) do + if option.value == TexturePacks.getName() then + default = i end - - -- Create the UIButton. - return UIButton( lx, ly, 0, 0, BUTTON_LIST_WIDTH, 1, callback, Translator.getText( 'ui_apply' )) 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. - -- - local function createBackButton( lx, ly ) - -- The function to call when the button is activated. - local function callback() - close() - end + -- Create the UISelectField. + return UISelectField( lx, ly, 0, 0, BUTTON_LIST_WIDTH, 1, Translator.getText( 'ui_texturepack' ), listOfValues, callback, default ) +end - -- Create the UIButton. - return UIButton( lx, ly, 0, 0, BUTTON_LIST_WIDTH, 1, callback, Translator.getText( 'ui_back' )) +--- +-- 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. +-- +local function createApplyButton( lx, ly ) + -- The function to call when the button is activated. + local function callback() + applySettings() end - --- - -- Creates a vertical list containing all the ui elements. - -- - local function createUIList() - local lx = GridHelper.centerElement( BUTTON_LIST_WIDTH, 1 ) - local ly = BUTTON_LIST_Y - - buttonList = UIVerticalList( lx, ly, 0, 0, BUTTON_LIST_WIDTH, 1 ) - - -- Create the UIElements and add them to the list. - buttonList:addChild( createLanguageOption( lx, ly )) - buttonList:addChild( createFullscreenOption( lx, ly )) - buttonList:addChild( createIngameEditorOption( lx, ly )) - buttonList:addChild( createTexturePackOption( lx, ly )) - buttonList:addChild( createApplyButton( lx, ly )) - buttonList:addChild( createBackButton( lx, ly )) + -- Create the UIButton. + return UIButton( lx, ly, 0, 0, BUTTON_LIST_WIDTH, 1, callback, Translator.getText( 'ui_apply' )) +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. +-- +local function createBackButton( lx, ly ) + -- The function to call when the button is activated. + local function callback() + close() end - -- ------------------------------------------------ - -- Public Methods - -- ------------------------------------------------ + -- Create the UIButton. + return UIButton( lx, ly, 0, 0, BUTTON_LIST_WIDTH, 1, callback, Translator.getText( 'ui_back' )) +end - --- - -- Initialises the OptionsScreen. - -- - function self:init() - Settings.load() - font = TexturePacks.getFont() +--- +-- Creates a vertical list containing all the ui elements. +-- +local function createUIList() + local lx = GridHelper.centerElement( BUTTON_LIST_WIDTH, 1 ) + local ly = BUTTON_LIST_Y + local buttonList = UIVerticalList( lx, ly, 0, 0, BUTTON_LIST_WIDTH, 1 ) + + -- Create the UIElements and add them to the list. + buttonList:addChild( createLanguageOption( lx, ly )) + buttonList:addChild( createFullscreenOption( lx, ly )) + buttonList:addChild( createIngameEditorOption( lx, ly )) + buttonList:addChild( createTexturePackOption( lx, ly )) + buttonList:addChild( createApplyButton( lx, ly )) + buttonList:addChild( createBackButton( lx, ly )) + + return buttonList +end + +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - createTitle() - createUIList() +--- +-- Initialises the OptionsScreen. +-- +function OptionsScreen:initialize() + Settings.load() - container = UIContainer() - container:register( buttonList ) + self.title = createTitle() + self.buttonList = createUIList() - footer = UICopyrightFooter.new() - end + self.container = UIContainer() + self.container:register( self.buttonList ) - --- - -- Updates the OptionsScreen. - -- - function self:update() - font = TexturePacks.getFont() - container:update() - end + self.footer = UICopyrightFooter.new() +end - --- - -- Draws the OptionsScreen. - -- - function self:draw() - font:use() - drawTitle() +--- +-- Updates the OptionsScreen. +-- +function OptionsScreen:update() + self.container:update() +end - container:draw() +--- +-- Draws the OptionsScreen. +-- +function OptionsScreen:draw() + drawTitle( self.title ) - footer:draw() - end + self.container:draw() - --- - -- Handle keypressed events. - -- - function self:keypressed( _, scancode ) - love.mouse.setVisible( false ) + self.footer:draw() +end - if scancode == 'escape' then - close() - end +--- +-- Handle keypressed events. +-- +function OptionsScreen:keypressed( _, scancode ) + love.mouse.setVisible( false ) - if scancode == 'up' then - container:command( 'up' ) - elseif scancode == 'down' then - container:command( 'down' ) - elseif scancode == 'left' then - container:command( 'left' ) - elseif scancode == 'right' then - container:command( 'right' ) - elseif scancode == 'return' then - container:command( 'activate' ) - end + if scancode == 'escape' then + close() end - function self:mousemoved() - love.mouse.setVisible( true ) + if scancode == 'up' then + self.container:command( 'up' ) + elseif scancode == 'down' then + self.container:command( 'down' ) + elseif scancode == 'left' then + self.container:command( 'left' ) + elseif scancode == 'right' then + self.container:command( 'right' ) + elseif scancode == 'return' then + self.container:command( 'activate' ) end +end - --- - -- Handle mousereleased events. - -- - function self:mousereleased() - container:mousecommand( 'activate' ) - end +function OptionsScreen:mousemoved() + love.mouse.setVisible( true ) +end - --- - -- Handle resize events and update the position of the UIElements accordingly. - -- - function self:resize( _, _ ) - local lx = GridHelper.centerElement( BUTTON_LIST_WIDTH, 1 ) - local ly = BUTTON_LIST_Y +--- +-- Handle mousereleased events. +-- +function OptionsScreen:mousereleased() + self.container:mousecommand( 'activate' ) +end - buttonList:setOrigin( lx, ly ) - end +--- +-- Handle resize events and update the position of the UIElements accordingly. +-- +function OptionsScreen:resize( _, _ ) + local lx = GridHelper.centerElement( BUTTON_LIST_WIDTH, 1 ) + local ly = BUTTON_LIST_Y - return self + self.buttonList:setOrigin( lx, ly ) end return OptionsScreen From 7e0f1b92dbffd24eb155f5dbe6704dd63095492c Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sat, 9 Dec 2017 02:09:35 +0100 Subject: [PATCH 059/178] Deactivate PrefabEditor if menu is open --- src/ui/screens/PrefabEditor.lua | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/ui/screens/PrefabEditor.lua b/src/ui/screens/PrefabEditor.lua index 95dbb8b4..218ddef2 100644 --- a/src/ui/screens/PrefabEditor.lua +++ b/src/ui/screens/PrefabEditor.lua @@ -113,6 +113,10 @@ function PrefabEditor:receive( event, ... ) end function PrefabEditor:draw() + if not self:isActive() then + return + end + self.tileSelector:draw() self.objectSelector:draw() @@ -131,6 +135,10 @@ function PrefabEditor:draw() end function PrefabEditor:update( dt ) + if not self:isActive() then + return + end + self.camera:update( dt ) self.uiContainer:update() From ec428c56e672a4339220653ece2f699bac617c06 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sat, 9 Dec 2017 03:08:55 +0100 Subject: [PATCH 060/178] Add nicer health indicators on health screen --- res/texturepacks/default/imagefont.png | Bin 1466 -> 1472 bytes res/texturepacks/default/info.lua | 2 +- src/ui/screens/HealthScreen.lua | 47 ++++++++++++++++--------- 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/res/texturepacks/default/imagefont.png b/res/texturepacks/default/imagefont.png index f6299735c217aa893e7631d13f2bc34590435504..ae89ed019a081bfbc8bf31210830f8b4af2de189 100644 GIT binary patch literal 1472 zcmV;x1wZxI_ad7PCDt%)_oQ`!(jXU!|zjf^{c+#-7li^KHt@be;$ZsU+%O&t>f;# zSNF55YmnX^&5A#w;I@5-%EPER}T8>zE*#5cfBjU0;Wy( z`Nq` z@2d0KDZMWU*wWy5n(h>^{posF;N!XPiFU9O*jmr+G_bV<8gIgpxt_pQVEe{>nT)Fw zU>gOvsr~C}2u$ft0ozs9O91Ayj>u0>23zZT2|KMQ)SzG+*%&f#b?GvoLK;3%Ny-}P z9r$#e@6`3K*0~DUS{**ok&l3_)&5oiTjF}v>!t21uoc+SU`xfzNL*ccy^)D2a{scO z@zr&wf$j8qtsPbplqi>o}rd+vPZ_ zu1A5uB-mOVg4aPm*_HRcmAcF*loeDe@9Xsk9RZlG$aZ{6U@1&l&x80hW@>^XQ^3mZ z^90aTjqp3lz&7iLJR|9+j53noz2m0|o$oIRG}F&#ne^}gO=SVbx-Xf6Q=~IQjC`L4w)A?5 zJZC4cjTXA1lWEl*eqw|-lJAoK;Eq48?9ZF;mV@n7$F>4&$!|caz_y}8{ge=OfoKKT zdJ>5fz}Dj^Bi~@IH2R;h7mq(6I^*;{)Asyie=B@S;2E9Ej9^(4)Ez%f$RvuBU^~$v ztO8rB->oj_kaY>rbQG37_O~0@W(&8Ic140MIXOZXoIZW&=QC&&xo{Mj zP7tUBn(o*VBWxLa>9D`Z=+^|;S{*M1G#&>yQRs?}09pgO^}aiREj58~3fPhrZ5nJn z9ac-Bi!2n9qiNF}+o=&*uRrY?$)pPd9ixnI0$Vcv5^-=Bu$>sCt+K1C4lvQNrS|V= zu+4%B3BWsnt!G5BqI0k#*jAq#mB><~ppi-@VFxR9Yza__6q35Z)|$u)Z1ba_q`$I) zne{v>I(l94@)TXfzf^RxD@NwYLSaScz7rUB&{Y()vz@t4AZB?AhA^i6$JK{^jbueDVhs=W$woP^BZ{TVy1-!=Gjfh2(lWJFycT+bXd2 zjI@$@PK7Swhjs$n=sL(kCiRKH6@Pf`xfN`QM6n_;WD^}4Y^{m*>R=l^U+O$OoqHmY z^*;G*rSx*>4)d@F&imCGW4PEAmf9cAJ@|I!GDbi3+^3XycuMGJf6 zXppr+{+3a^$Y@&Ud7Nm^Z?G%rq>BLS)b*s3Zm0OM%KIy@m2PXD5;5tfE2EvDM~(dK zw9lRZCx1}$Uk7K>7?`P1h&#iC!KUb!0dd!37{6(zRRA~@s>_H>7?6d*Pma%zg{=Le;VnelTJG6 zq?1m%E8W-s-}}F-bvh5}q?1m%vvn_RjC9gTC!KWCNhe^HPCDtNlTN@YopjPk2kX9m z{IoC9Nhh6j(n%+sbPosCuMfX>-PN!9dZS-N=Y76UN%W@!vFyu8`)M7c`(EA8vW}nE zjI4KMd&yoex-Xf1?$q6_*XlP$wznFvTHi~0y`S`>`&#|M=z3Rr1x%an&YwPgUq*IC z$JJS#U2B}N+Dk>;y1IXLK15y5yU$N~KRVXo$z$pBwO%h1_r1?&*_TK>-&Nv{=0B?>ShV2v!83`kwN41kaZO;moe1~>wmuJfIB zy{mPu0<2a?PIS;Cz-qO>Re+VaUiEsZ`wCbEtTeDv@iG!uS6**q!in6!Y$tnl-D$u& zd%f0h+m9C(6jPzRyPS*1vK2e!9agixZW%qdos8o&8 zQtKcSe9y#11XwfqQ1tnp2CUr$Sx?^S%_BNGdeMEMJ~?>T-RZfM5hZdD}dFLAe;cK z9tRlt#&M+){EWSL`~lI)ruUh)=R5mbVWi(P!j>6@vL>E8ej<=b0A~X0jE-Ivuv-0Y zb)kmLbFv^h8(1mOi`qd)!PsMey8&yqAUo5pNMI!=J?KKxyDxoz28{#<0FDm)RANkb zScy@xjJ$|GS1D|J{20ErZcx`EZ2pbA*?BblVXvI3X&JSsYH zUGef1UBtgsbe=0l*U184MW?+J1a{C>6sohGx@usJ*x$(`!QS}R1^i_r!Bl5s6`eOh zB`c4_H&WIB7g+}Zth3I8+L!FPWh$QDbyys4W*t_b-pL<$I+NsknF&Z1JD}{D2G;5k z#XEcUb}XWaM5lkbdN1$%LB)BTmLF8}5(!|1!_Ou#G_YC|=+%KWdcM?ocslJwg6h3qB!Fbj zt-=A4?{8$pJrdxe@0+({Az)?yBY7%*c>GJY^1XvU=pMP9We2O+Usb`=+EM8^kIYDG z$9wctV66lx+ODb#{iEkw30SRM76ET+;*snCkL=~8Q3~mH)e#jP>7QuP2>!JH?Oc`wCd4+ghgtOS;*W%uax#MsIf7XHTLi ze^B#R|FwV8oeHcgUr##eq!X}8C!KWCrNOcD`DTE!fc07StPZtw(n%+sbSvuq0&=ß^©', + source = ' ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzÄÖÜäöü0123456789.,:;!?-+/()[]%&"\'*=_<>ß^©|', width = 8, height = 16 } diff --git a/src/ui/screens/HealthScreen.lua b/src/ui/screens/HealthScreen.lua index 4e284fa9..8ab018b2 100644 --- a/src/ui/screens/HealthScreen.lua +++ b/src/ui/screens/HealthScreen.lua @@ -27,6 +27,15 @@ local HealthScreen = Screen:subclass( 'HealthScreen' ) local UI_GRID_WIDTH = 30 local UI_GRID_HEIGHT = 16 +local HEALTH = { + { color = 'ui_health_destroyed_limb', text = '.....' }, + { color = 'ui_health_badly_damaged_limb', text = '|....' }, + { color = 'ui_health_damaged_limb', text = '||...' }, + { color = 'ui_health_damaged_limb', text = '|||..' }, + { color = 'ui_health_ok_limb', text = '||||.' }, + { color = 'ui_health_fine_limb', text = '|||||' }, +} + -- ------------------------------------------------ -- Private Methods -- ------------------------------------------------ @@ -57,6 +66,23 @@ local function generateOutlines( x, y ) return outlines end +--- +-- Takes the health of a limb, compares it to its maximum health and returns +-- a health indicator as well as a fitting color based on the ratio. +-- @tparam BodyPart bodyPart The body part to get the health indicator for. +-- @treturn string The string indicating the body part's health. +-- @treturn table A table containing the color to use for this health indicator. +-- +local function getHealthIndicator( bodyPart ) + -- Take the ratio of the current health vs maximum health. Multiply it by + -- 10 so we can floor it to the next integer, then half it (because our + -- indicators use 5 strokes to show 100% so each stroke == 20% health). + -- The +1 is needed because the table index starts at 1 so at 0 health we + -- need to get the first entry in the table. + local index = math.floor( math.floor(( bodyPart:getCurrentHealth() / bodyPart:getMaximumHealth() ) * 10 ) * 0.5 ) + 1 + return HEALTH[index].text, HEALTH[index].color +end + -- ------------------------------------------------ -- Public Methods -- ------------------------------------------------ @@ -81,23 +107,10 @@ function HealthScreen:draw() for _, bodyPart in pairs( self.character:getBody():getBodyParts() ) do if bodyPart:isEntryNode() then counter = counter + 1 - local status - if bodyPart:isDestroyed() then - TexturePacks.setColor( 'ui_health_destroyed_limb' ) - status = 'DED' - elseif bodyPart:getCurrentHealth() / bodyPart:getMaximumHealth() < 0.2 then - TexturePacks.setColor( 'ui_health_badly_damaged_limb' ) - status = 'OUCH' - elseif bodyPart:getCurrentHealth() / bodyPart:getMaximumHealth() < 0.4 then - TexturePacks.setColor( 'ui_health_damaged_limb' ) - status = 'MEH' - elseif bodyPart:getCurrentHealth() / bodyPart:getMaximumHealth() < 0.7 then - TexturePacks.setColor( 'ui_health_ok_limb' ) - status = 'OK' - else - TexturePacks.setColor( 'ui_health_fine_limb' ) - status = 'FINE' - end + + local status, color = getHealthIndicator( bodyPart ) + TexturePacks.setColor( color ) + love.graphics.print( Translator.getText( bodyPart:getID() ), (self.x+1) * tw, (self.y+counter) * th ) love.graphics.printf( status, (self.x+1) * tw, (self.y+counter) * th, ( UI_GRID_WIDTH - 2 ) * tw, 'right' ) From 5190a4d5e6a18084c77167f6a5948a490a090e79 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sat, 9 Dec 2017 03:18:46 +0100 Subject: [PATCH 061/178] Rename text templates for health screen --- res/text/en_EN/ui_text.lua | 4 ++-- src/ui/screens/HealthScreen.lua | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/res/text/en_EN/ui_text.lua b/res/text/en_EN/ui_text.lua index 650bf3c1..cd001503 100644 --- a/res/text/en_EN/ui_text.lua +++ b/res/text/en_EN/ui_text.lua @@ -10,8 +10,8 @@ locale.strings = { ['ui_lose'] = "Your team is dead. You lose!\n\nPress any key to continue...", -- Health screen - ['ui_character_type'] = "Type: ", - ['ui_character_name'] = "Name: ", + ['ui_healthscreen_type'] = "Type: ", + ['ui_healthscreen_name'] = "Name: ", -- Main Menu buttons ['ui_main_menu_new_game'] = "New", diff --git a/src/ui/screens/HealthScreen.lua b/src/ui/screens/HealthScreen.lua index 8ab018b2..283d5034 100644 --- a/src/ui/screens/HealthScreen.lua +++ b/src/ui/screens/HealthScreen.lua @@ -133,12 +133,12 @@ function HealthScreen:draw() TexturePacks.setColor( 'ui_text' ) -- Draw character type. - local type = Translator.getText( 'ui_character_type' ) .. Translator.getText( self.characterType ) + local type = Translator.getText( 'ui_healthscreen_type' ) .. Translator.getText( self.characterType ) love.graphics.print( type, (self.x+1) * tw, (self.y+1) * th ) -- Draw character name. if self.character:getName() then - local name = Translator.getText( 'ui_character_name' ) .. self.character:getName() + local name = Translator.getText( 'ui_healthscreen_name' ) .. self.character:getName() love.graphics.print( name, (self.x+2) * tw + TexturePacks.getFont():measureWidth( type ), (self.y+1) * th ) end From 5031f2c8b648d51b415c5c26b993924d201f76db Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sat, 9 Dec 2017 03:58:52 +0100 Subject: [PATCH 062/178] Improve limb sections on health screen We are now drawing section headers explaining the different values and use the same colors for all three values based on the limb's health. --- res/text/en_EN/ui_text.lua | 3 ++ res/texturepacks/default/colors.lua | 4 -- src/ui/screens/HealthScreen.lua | 84 +++++++++++++++++++---------- 3 files changed, 59 insertions(+), 32 deletions(-) diff --git a/res/text/en_EN/ui_text.lua b/res/text/en_EN/ui_text.lua index cd001503..9f2a9249 100644 --- a/res/text/en_EN/ui_text.lua +++ b/res/text/en_EN/ui_text.lua @@ -12,6 +12,9 @@ locale.strings = { -- Health screen ['ui_healthscreen_type'] = "Type: ", ['ui_healthscreen_name'] = "Name: ", + ['ui_healthscreen_limb'] = "Limb", + ['ui_healthscreen_bleeding'] = "Bleeding", + ['ui_healthscreen_status'] = "Status", -- Main Menu buttons ['ui_main_menu_new_game'] = "New", diff --git a/res/texturepacks/default/colors.lua b/res/texturepacks/default/colors.lua index cfb5fbf7..d4513394 100644 --- a/res/texturepacks/default/colors.lua +++ b/res/texturepacks/default/colors.lua @@ -188,10 +188,6 @@ return { ['ui_health_damaged_limb'] = COLORS.DB05, ['ui_health_ok_limb'] = COLORS.DB08, ['ui_health_fine_limb'] = COLORS.DB10, - ['ui_health_bleeding_bad'] = COLORS.DB27, - ['ui_health_bleeding'] = COLORS.DB05, - ['ui_health_bleeding_ok'] = COLORS.DB08, - ['ui_health_bleeding_fine'] = COLORS.DB10, -- Changelog Menu ['ui_changelog_version'] = COLORS.DB05, diff --git a/src/ui/screens/HealthScreen.lua b/src/ui/screens/HealthScreen.lua index 283d5034..dd050965 100644 --- a/src/ui/screens/HealthScreen.lua +++ b/src/ui/screens/HealthScreen.lua @@ -83,6 +83,50 @@ local function getHealthIndicator( bodyPart ) return HEALTH[index].text, HEALTH[index].color end +--- +-- Draws the section headers for limbs. +-- @tparam number ox The health screen's origin along the x-axis. +-- @tparam number oy The health screen's origin along the y-axis. +-- @tparam number tw The tile width. +-- @tparam number th The tile height. +-- +local function drawSectionHeaders( ox, oy, tw, th ) + local x, y = (ox + 1) * tw, (oy + 3) * th + local width = (UI_GRID_WIDTH - 2) * tw + + love.graphics.printf( Translator.getText( 'ui_healthscreen_limb' ), x, y, width, 'left' ) + love.graphics.printf( Translator.getText( 'ui_healthscreen_bleeding' ), x, y, width, 'center' ) + love.graphics.printf( Translator.getText( 'ui_healthscreen_status' ), x, y, width, 'right' ) +end + +--- +-- Draws the status of one limb. +-- @tparam number ox The health screen's origin along the x-axis. +-- @tparam number oy The health screen's origin along the y-axis. +-- @tparam number tw The tile width. +-- @tparam number th The tile height. +-- @tparam number offset The vertical line offset for this body part. +-- @tparam BodyPart bodyPart The body part to draw the status for. +-- +local function drawLimbStatus( ox, oy, tw, th, offset, bodyPart ) + local x, y = (ox + 1) * tw, (oy + offset + 4) * th + local width = (UI_GRID_WIDTH - 2) * tw + + local status, color = getHealthIndicator( bodyPart ) + TexturePacks.setColor( color ) + + -- Limb name. + love.graphics.printf( Translator.getText( bodyPart:getID() ), x, y, width, 'left' ) + + -- Bleeding indicator. + if bodyPart:isBleeding() then + love.graphics.printf( string.format( '%1.2f', bodyPart:getBloodLoss() ), x, y, width, 'center' ) + end + + -- Limb status. + love.graphics.printf( status, x, y, width, 'right' ) +end + -- ------------------------------------------------ -- Public Methods -- ------------------------------------------------ @@ -91,7 +135,8 @@ function HealthScreen:initialize( character ) self.x, self.y = GridHelper.centerElement( UI_GRID_WIDTH, UI_GRID_HEIGHT ) self.character = character - self.characterType = character:getBody():getID() + self.body = character:getBody() + self.characterType = self.body:getID() self.background = UIBackground( self.x, self.y, 0, 0, UI_GRID_WIDTH, UI_GRID_HEIGHT ) @@ -103,33 +148,6 @@ function HealthScreen:draw() self.outlines:draw() local tw, th = TexturePacks.getTileDimensions() - local counter = 3 - for _, bodyPart in pairs( self.character:getBody():getBodyParts() ) do - if bodyPart:isEntryNode() then - counter = counter + 1 - - local status, color = getHealthIndicator( bodyPart ) - TexturePacks.setColor( color ) - - love.graphics.print( Translator.getText( bodyPart:getID() ), (self.x+1) * tw, (self.y+counter) * th ) - love.graphics.printf( status, (self.x+1) * tw, (self.y+counter) * th, ( UI_GRID_WIDTH - 2 ) * tw, 'right' ) - - if bodyPart:isBleeding() then - local str = string.format( 'Bleeding %1.2f', bodyPart:getBloodLoss() ) - if bodyPart:getBloodLoss() / 1.0 < 0.2 then - TexturePacks.setColor( 'ui_health_bleeding_fine' ) - elseif bodyPart:getBloodLoss() / 1.0 < 0.4 then - TexturePacks.setColor( 'ui_health_bleeding_ok' ) - elseif bodyPart:getCurrentHealth() / bodyPart:getMaximumHealth() < 0.7 then - TexturePacks.setColor( 'ui_health_bleeding' ) - elseif bodyPart:getCurrentHealth() / bodyPart:getMaximumHealth() < 1.0 then - TexturePacks.setColor( 'ui_health_bleeding_bad' ) - end - love.graphics.printf( str, (self.x+1) * tw, (self.y+counter) * th, ( UI_GRID_WIDTH - 2 ) * tw, 'center' ) - end - end - end - TexturePacks.setColor( 'ui_text' ) -- Draw character type. @@ -142,6 +160,16 @@ function HealthScreen:draw() love.graphics.print( name, (self.x+2) * tw + TexturePacks.getFont():measureWidth( type ), (self.y+1) * th ) end + drawSectionHeaders( self.x, self.y, tw, th ) + + local counter = 0 + for _, bodyPart in pairs( self.body:getBodyParts() ) do + if bodyPart:isEntryNode() then + counter = counter + 1 + drawLimbStatus( self.x, self.y, tw, th, counter, self.body, bodyPart ) + end + end + TexturePacks.resetColor() end From cdab2f9f6aa98d8c448ec2061c2a76557350d2ac Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sat, 9 Dec 2017 04:01:50 +0100 Subject: [PATCH 063/178] Fix crash caused by faulty argument --- src/ui/screens/HealthScreen.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/screens/HealthScreen.lua b/src/ui/screens/HealthScreen.lua index dd050965..a2831f89 100644 --- a/src/ui/screens/HealthScreen.lua +++ b/src/ui/screens/HealthScreen.lua @@ -166,7 +166,7 @@ function HealthScreen:draw() for _, bodyPart in pairs( self.body:getBodyParts() ) do if bodyPart:isEntryNode() then counter = counter + 1 - drawLimbStatus( self.x, self.y, tw, th, counter, self.body, bodyPart ) + drawLimbStatus( self.x, self.y, tw, th, counter, bodyPart ) end end From 3457e66b606d741928a3fd6f95c5a116085d7fb8 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sun, 10 Dec 2017 01:55:29 +0100 Subject: [PATCH 064/178] Refactor Character class --- src/characters/Character.lua | 938 ++++++++++++++-------------- src/characters/CharacterFactory.lua | 4 +- src/characters/Faction.lua | 3 +- 3 files changed, 475 insertions(+), 470 deletions(-) diff --git a/src/characters/Character.lua b/src/characters/Character.lua index c50cb55d..6630cd98 100644 --- a/src/characters/Character.lua +++ b/src/characters/Character.lua @@ -1,563 +1,567 @@ -local Log = require( 'src.util.Log' ); -local Object = require('src.Object'); -local Queue = require('src.util.Queue'); -local Bresenham = require( 'lib.Bresenham' ); -local Util = require( 'src.util.Util' ); +--- +-- @module Character +-- + +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ + +local Class = require( 'lib.Middleclass' ) +local Log = require( 'src.util.Log' ) +local Queue = require('src.util.Queue') +local Bresenham = require( 'lib.Bresenham' ) +local Util = require( 'src.util.Util' ) -- ------------------------------------------------ -- Module -- ------------------------------------------------ -local Character = {}; +local Character = Class( 'Character' ) -- ------------------------------------------------ -- Constants -- ------------------------------------------------ -local DEFAULT_ACTION_POINTS = 40; +local DEFAULT_ACTION_POINTS = 40 local STANCES = require( 'src.constants.STANCES' ) -local ITEM_TYPES = require('src.constants.ITEM_TYPES') +local ITEM_TYPES = require( 'src.constants.ITEM_TYPES' ) -- ------------------------------------------------ --- Constructor +-- Private Methods -- ------------------------------------------------ --- --- Creates a new character and places it on the target tile. --- @treturn Character A new instance of the Character class. +-- Marks a tile as seen by this character if it fullfills the necessary +-- requirements. Used as a callback for Bresenham's line algorithm. +-- @tparam number cx The tile's coordinate along the x-axis. +-- @tparam number cy The tile's coordinate along the y-axis. +-- @tparam number counter The number of tiles touched by the ray so far. +-- @tparam Character self The character instance to use. +-- @tparam number falloff Determines how much height the ray loses each step. +-- @treturn boolean Returns true if the tile can be seen by the character. -- -function Character.new() - local self = Object.new():addInstance( 'Character' ); - - -- ------------------------------------------------ - -- Private Variables - -- ------------------------------------------------ +local function markSeenTiles( cx, cy, counter, self, falloff ) + local target = self.map:getTileAt( cx, cy ) + if not target then + return false + end - local faction + -- Calculate the height of the ray on the current tile. If the height + -- is smaller than the tile's height it is marked as visible. This + -- simulates how small objects can be hidden behind bigger objects, but + -- not the other way around. + local height = self:getHeight() - (counter+1) * falloff + if height <= target:getHeight() then + -- Add tile to this character's FOV. + self:addSeenTile( cx, cy, target ) - local map - local tile + -- Mark tile for drawing update. + target:setDirty( true ) + end - local name - local nationality + -- A world object blocks vision if it has the "blocksVision" flag set + -- to true in its template file and if the ray is smaller than the world + -- object's size. This prevents characters from looking over bigger world + -- objects and allows smaller objects like low walls to cast a "shadow" + -- in which smaller objects could be hidden. + if target:hasWorldObject() + and target:getWorldObject():blocksVision() + and height <= target:getWorldObject():getHeight() then + return false + end - local actionPoints = DEFAULT_ACTION_POINTS; - local actions = Queue.new(); - local fov = {}; + return true +end - local accuracy = love.math.random( 60, 90 ); - local throwingSkill = love.math.random( 60, 90 ); +--- +-- Determine the height falloff for rays of the FOV calculation. This value +-- will be deducted from the ray's height for each tile the ray traverses. +-- @tparam number characterHeight The character's height. +-- @tparam number targetHeight The target's height. +-- @tparam number steps The amount of steps between the character and the target. +-- @treturn number The calculated falloff value. +-- +local function calculateFalloff( characterHeight, targetHeight, steps ) + return (characterHeight - targetHeight) / steps +end - local stance = STANCES.STAND; - local body; +--- +-- 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. +-- +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 + end +end - local finishedTurn = false; +--- +-- Drops all inventory and equipment items, removes the character from +-- the tile and resets the FOV information for this character. +-- @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() + resetFOV( self.fov ) +end - -- TODO Remove hack for saving / loading characters - local savedX, savedY +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - -- ------------------------------------------------ - -- Private Methods - -- ------------------------------------------------ +function Character:initialize() + self.actionPoints = DEFAULT_ACTION_POINTS + self.actions = Queue.new() - --- - -- Marks a tile as seen by this character if it fullfills the necessary - -- requirements. Used as a callback for Bresenham's line algorithm. - -- @tparam number cx The tile's coordinate along the x-axis. - -- @tparam number cy The tile's coordinate along the y-axis. - -- @tparam number counter The number of tiles touched by the ray so far. - -- @tparam number falloff Determines how much height the ray loses each step. - -- @treturn boolean Returns true if the tile can be seen by the character. - -- - local function markSeenTiles( cx, cy, counter, falloff ) - local target = map:getTileAt( cx, cy ); - if not target then - return false; - end + self.fov = {} + self.viewRange = 12 - -- Calculate the height of the ray on the current tile. If the height - -- is smaller than the tile's height it is marked as visible. This - -- simulates how small objects can be hidden behind bigger objects, but - -- not the other way around. - local height = self:getHeight() - (counter+1) * falloff - if height <= target:getHeight() then - -- Add tile to this character's FOV. - self:addSeenTile( cx, cy, target ); - - -- Mark tile for drawing update. - target:setDirty( true ); - end + self.accuracy = love.math.random( 60, 90 ) + self.throwingSkill = love.math.random( 60, 90 ) - -- A world object blocks vision if it has the "blocksVision" flag set - -- to true in its template file and if the ray is smaller than the world - -- object's size. This prevents characters from looking over bigger world - -- objects and allows smaller objects like low walls to cast a "shadow" - -- in which smaller objects could be hidden. - if target:hasWorldObject() - and target:getWorldObject():blocksVision() - and height <= target:getWorldObject():getHeight() then - return false; - end + self.stance = STANCES.STAND - return true; - end - - --- - -- Determine the height falloff for rays of the FOV calculation. This value - -- will be deducted from the ray's height for each tile the ray traverses. - -- @tparam Tile The target tile. - -- @tparam number The distance to the target. - -- @treturn number The calculated falloff value. - -- - local function calculateFalloff( target, steps ) - local oheight = self:getHeight() - local theight = target:getHeight() - - local delta = oheight - theight; - return delta / steps; - end + self.finishedTurn = false +end - --- - -- Clears the list of seen tiles and marks them for a drawing update. - -- - local function resetFOV() - for x, rx in pairs( fov ) do - for y, target in pairs( rx ) do - target:setDirty( true ); - fov[x][y] = nil; - end - end +--- +-- Called when this character is made active by the game. +-- +function Character:activate() + if self:isDead() then + return end + self:generateFOV() + self:clearActions() +end - --- - -- Drops all inventory and equipment items, removes the character from - -- the tile and resets the FOV information for this character. - -- - local function handleDeath() - self:getEquipment():dropAllItems( tile ) - self:getInventory():dropAllItems( tile ) - tile:removeCharacter() - resetFOV() +--- +-- Called when this character is made inactive by the game. +-- +function Character:deactivate() + if self:isDead() then + return end + self:generateFOV() + self:clearActions() +end - -- ------------------------------------------------ - -- Public Methods - -- ------------------------------------------------ +--- +-- 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. +-- +function Character:addSeenTile( tx, ty, target ) + self.fov[tx] = self.fov[tx] or {} + self.fov[tx][ty] = target +end - --- - -- Called when this character is made active by the game. - -- - function self:activate() - if self:isDead() then - return; - end - self:generateFOV(); - self:clearActions(); +--- +-- Adds a new action to the action queue if the character has enough action +-- points to perform it. +-- @tparam Action newAction The action to enqueue. +-- @treturn boolean True if the action was enqueued. +-- +function Character:enqueueAction( newAction ) + local cost = 0 + for _, action in ipairs( self.actions:getItems() ) do + cost = cost + action:getCost() end - --- - -- Adds a tile to this character's FOV. - -- @param tx (number) The target-tile's position along the x-axis. - -- @param ty (number) The target-tile's position along the y-axis. - -- @param target (Tile) The target-tile. - -- - function self:addSeenTile( tx, ty, target ) - fov[tx] = fov[tx] or {}; - fov[tx][ty] = target; + -- Enqueue action only if the character has enough action points left. + if cost + newAction:getCost() <= self.actionPoints then + self.actions:enqueue( newAction ) + return true end - --- - -- Adds a new action to the action queue if the character has enough action - -- points to perform it. - -- @param naction (Action) The action to enqueue. - -- @return (boolean) True if the action was enqueued. - -- - function self:enqueueAction( naction ) - local cost = 0; - for _, action in ipairs( actions:getItems() ) do - cost = cost + action:getCost(); - end - - if cost + naction:getCost() <= actionPoints then - actions:enqueue( naction ); - return true; - end - - Log.debug( 'No AP left. Refused to add Action to Queue.', 'Character' ); - return false; - end + Log.debug( 'No AP left. Refused to add Action to Queue.', 'Character' ) + return false +end - --- - -- Removes the next action from the action queue, reduces the action points - -- of the character by the action's cost and performs the action. - -- - function self:performAction() - local action = actions:dequeue(); - local success = action:perform(); - if success then - actionPoints = actionPoints - action:getCost(); - end - self:generateFOV(); +--- +-- Removes the next action from the action queue, reduces the action points +-- of the character by the action's cost and performs the action. +-- +function Character:performAction() + local action = self.actions:dequeue() + local success = action:perform() + if success then + self.actionPoints = self.actionPoints - action:getCost() end + self:generateFOV() +end - --- - -- Cleas the action queue. - -- - function self:clearActions() - actions:clear(); - end +--- +-- Clears the action queue. +-- +function Character:clearActions() + self.actions:clear() +end - --- - -- Called when this character is made inactive by the game. - -- - function self:deactivate() - if self:isDead() then - return; - end - self:generateFOV(); - self:clearActions(); - end +--- +-- Casts rays in a circle around the character to determine all tiles he can +-- see. Rays stop if they reach the map border or a world object which has +-- the blocksVision attribute set to true. +-- +function Character:generateFOV() + resetFOV( self.fov ) - --- - -- Casts rays in a circle around the character to determine all tiles he can - -- see. Rays stop if they reach the map border or a world object which has - -- the blocksVision attribute set to true. - -- - function self:generateFOV() - resetFOV() - - local range = body:getStatusEffects():isBlind() and 1 or self:getViewRange(); - local list = Util.getTilesInCircle( map, tile, range ); - local sx, sy = tile:getPosition(); - - for _, ttile in ipairs( list ) do - local tx, ty = ttile:getPosition(); - local _, counter = Bresenham.line( sx, sy, tx, ty ); - local falloff = calculateFalloff( ttile, counter ); - Bresenham.line( sx, sy, tx, ty, markSeenTiles, falloff ); - end - end + 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() - --- - -- Resets the character's action points to the default value. - -- - function self:resetActionPoints() - actionPoints = DEFAULT_ACTION_POINTS; + for _, tile in ipairs( list ) do + local tx, ty = tile:getPosition() + local _, counter = Bresenham.line( sx, sy, tx, ty ) + local falloff = calculateFalloff( self:getHeight(), tile:getHeight(), counter ) + Bresenham.line( sx, sy, tx, ty, markSeenTiles, self, falloff ) end +end - --- - -- Hits the character with damage. - -- @param damage (number) The amount of damage the character is hit with. - -- @param damageType (string) The type of damage the tile is hit with. - -- - function self:hit( damage, damageType ) - body:hit( damage, damageType ); +--- +-- Resets the character's action points to the default value. +-- +function Character:resetActionPoints() + self.actionPoints = DEFAULT_ACTION_POINTS +end - self:generateFOV(); +--- +-- Hits the character with damage. +-- @tparam number damage The amount of damage the character is hit with. +-- @tparam string damageType The type of damage the tile is hit with. +-- +function Character:hit( damage, damageType ) + self.body:hit( damage, damageType ) - if self:isDead() then - handleDeath() - end - end + self:generateFOV() - --- - -- Checks if the character can see a certain tile. - -- @param target (Tile) The tile to check. - -- @return (boolean) Wether the character sees the tile. - -- - function self:canSee( target ) - local tx, ty = target:getPosition(); - if not fov[tx] then - return false; - end - return fov[tx][ty] ~= nil; + if self:isDead() then + handleDeath( self ) end +end - function self:tickOneTurn() - body:tickOneTurn(); - if self:isDead() then - handleDeath() - end +--- +-- Checks if the character can see a certain tile. +-- @tparam Tile target The tile to check. +-- @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 +end - function self:serialize() - local t = { - ['name'] = name, - ['actionPoints'] = actionPoints, - ['accuracy'] = accuracy, - ['throwingSkill'] = throwingSkill, - ['stance'] = stance, - ['finishedTurn'] = finishedTurn, - ['body'] = body:serialize(), - ['x'] = tile:getX(), - ['y'] = tile:getY() - } - return t; +--- +-- Updates the character once. This should be used to update status effects and +-- other things which should happen once per round. +-- +function Character:tickOneTurn() + self.body:tickOneTurn() + if self:isDead() then + handleDeath( self ) end +end - -- ------------------------------------------------ - -- Getters - -- ------------------------------------------------ +--- +-- Serializes the Character instance. +-- @treturn table The serialized character instance. +-- +function Character:serialize() + local t = { + ['name'] = self.name, + ['actionPoints'] = self.actionPoints, + ['accuracy'] = self.accuracy, + ['throwingSkill'] = self.throwingSkill, + ['stance'] = self.stance, + ['finishedTurn'] = self.finishedTurn, + ['body'] = self.body:serialize(), + ['x'] = self.tile:getX(), + ['y'] = self.tile:getY() + } + return t +end - --- - -- Returns the accuracy of this character used when shooting guns. - -- @return (number) The accuracy of the character. - -- - function self:getAccuracy() - return accuracy; - end +-- ------------------------------------------------ +-- Getters +-- ------------------------------------------------ - --- - -- Returns the amount of action points. - -- @return (number) The amount of action points. - -- - function self:getActionPoints() - return actionPoints; - end +--- +-- Returns the accuracy of this character used when shooting guns. +-- @treturn number The character's accuracy. +-- +function Character:getAccuracy() + return self.accuracy +end - --- - -- Returns the action queue. - -- @return (table) A sequence containing all actions. - -- - function self:getActions() - return actions:getItems(); - end +--- +-- Returns the amount of action points. +-- @treturn number The amount of action points. +-- +function Character:getActionPoints() + return self.actionPoints +end - --- - -- Returns the action queue. - -- @return (Queue) A queue containing all actions. - -- - function self:getActionQueue() - return actions; - end +--- +-- Returns the action queue. +-- @return table A sequence containing all actions. +-- +function Character:getActions() + return self.actions:getItems() +end - function self:getBody() - return body; - end +--- +-- Returns the action queue. +-- @treturn Queue A queue containing all actions. +-- +function Character:getActionQueue() + return self.actions +end - --- - -- Returns the faction the character belongs to. - -- @return (Faction) The Faction object. - -- - function self:getFaction() - return faction; - end +--- +-- Returns the character's body. +-- @treturn Body The character's body. +-- +function Character:getBody() + return self.body +end - --- - -- Returns the character's equipment. - -- @return (Equipment) The character's equipment. - -- - function self:getEquipment() - return body:getEquipment(); - end +--- +-- Returns the faction the character belongs to. +-- @treturn Faction The Faction object. +-- +function Character:getFaction() + return self.faction +end - --- - -- Returns the character's inventory. - -- @return (Equipment) The character's inventory. - -- - function self:getInventory() - return body:getInventory(); - end +--- +-- Returns the character's equipment. +-- @treturn Equipment The character's equipment. +-- +function Character:getEquipment() + return self.body:getEquipment() +end - --- - -- Returns the character's fov. - -- @return (table) A table containing the tiles this character sees. - -- - function self:getFOV() - return fov; - end +--- +-- Returns the character's inventory. +-- @treturn Equipment The character's inventory. +-- +function Character:getInventory() + return self.body:getInventory() +end - --- - -- Gets the name of this character. - -- @treturn string The name. - -- - function self:getName() - return name - end +--- +-- Returns the character's fov. +-- @treturn table A table containing the tiles this character sees. +-- +function Character:getFOV() + return self.fov +end - --- - -- Returns the character's nationality. - -- @treturn string The nationality. - -- - function self:getNationality() - return nationality - end +--- +-- Gets the name of this character. +-- @treturn string The character's name. +-- +function Character:getName() + return self.name +end - --- - -- Returns the character's size based on his stance. - -- @return (number) The character's size. - -- - function self:getHeight() - return body:getHeight( stance ) - end +--- +-- Returns the character's nationality. +-- @treturn string The nationality. +-- +function Character:getNationality() + return self.nationality +end - --- - -- Returns the character's current stance. - -- @return (number) The character's stance. - -- - function self:getStance() - return stance; - end +--- +-- Returns the character's size based on his stance. +-- @treturn number The character's size. +-- +function Character:getHeight() + return self.body:getHeight( self.stance ) +end - --- - -- Gets the character's throwing skill. - -- @return (number) The character's throwing skill. - -- - function self:getThrowingSkill() - return throwingSkill; - end +--- +-- Returns the character's current stance. +-- @treturn number The character's stance. +-- +function Character:getStance() + return self.stance +end - --- - -- Gets the character's tile. - -- @return (Tile) The tile the character is located on. - -- - function self:getTile() - return tile; - end +--- +-- Gets the character's throwing skill. +-- @treturn number The character's throwing skill. +-- +function Character:getThrowingSkill() + return self.throwingSkill +end - --- - -- Returns the total amount of action points. - -- @return (number) The total amount of action points. - -- - function self:getMaxActionPoints() - return DEFAULT_ACTION_POINTS; - end +--- +-- Gets the character's tile. +-- @treturn Tile The tile the character is located on. +-- +function Character:getTile() + return self.tile +end - --- - -- Returns the view range. - -- @return (number) The view range. - -- - function self:getViewRange() - return 12; - end +--- +-- Returns the total amount of action points. +-- @treturn number The total amount of action points. +-- +function Character:getMaxActionPoints() + return DEFAULT_ACTION_POINTS +end - --- - -- Checks if the character has an action enqueued. - -- @return (boolean) Wether an action is enqueued. - -- - function self:hasEnqueuedAction() - return actions:getSize() > 0; - end +--- +-- Returns the view range. +-- @treturn number The view range. +-- +function Character:getViewRange() + return self.viewRange +end - --- - -- Returns wether the character is dead or not. - -- @return (boolean) Wether the character is dead or not. - -- - function self:isDead() - return body:getStatusEffects():isDead(); - end +--- +-- Checks if the character has an action enqueued. +-- @treturn boolean Wether an action is enqueued. +-- +function Character:hasEnqueuedAction() + return self.actions:getSize() > 0 +end - --- - -- Gets an item of type weapon. - -- @return (Weapon) The weapon item. - -- - function self:getWeapon() - return self:getEquipment():getItem( ITEM_TYPES.WEAPON ); - end +--- +-- Returns wether the character is dead or not. +-- @treturn boolean Wether the character is dead or not. +-- +function Character:isDead() + return self.body:getStatusEffects():isDead() +end - --- - -- Gets wether the character has finished a turn or not. - -- @return (boolean) Wether the character has finished a turn or not. - -- - function self:hasFinishedTurn() - return finishedTurn; - end +--- +-- Gets an item of type weapon. +-- @treturn Weapon The weapon item. +-- +function Character:getWeapon() + return self:getEquipment():getItem( ITEM_TYPES.WEAPON ) +end - -- ------------------------------------------------ - -- Setters - -- ------------------------------------------------ +--- +-- Gets wether the character has finished a turn or not. +-- @treturn boolean Wether the character has finished a turn or not. +-- +function Character:hasFinishedTurn() + return self.finishedTurn +end - --- - -- Sets the character's accuracy attribute. - -- @param naccuracy (number) The new accuracy value. - -- - function self:setAccuracy( naccuracy ) - accuracy = naccuracy; - end +-- ------------------------------------------------ +-- Setters +-- ------------------------------------------------ - --- - -- Sets the character's action points. - -- @param nap (number) The amount of AP to set. - -- - function self:setActionPoints( nap ) - actionPoints = nap; - end +--- +-- Sets the character's accuracy attribute. +-- @tparam number accuracy The new accuracy value. +-- +function Character:setAccuracy( accuracy ) + self.accuracy = accuracy +end - --- - -- Sets the character's new body. - -- @param nbody (Body) The body object to use. - -- - function self:setBody( nbody ) - body = nbody; - end +--- +-- Sets the character's action points. +-- @tparam number ap The amount of AP to set. +-- +function Character:setActionPoints( ap ) + self.actionPoints = ap +end - ---- - -- @tparam Faction faction The Faction object determining the character's faction. - -- - function self:setFaction( nfaction ) - faction = nfaction - end +--- +-- Sets the character's new body. +-- @tparam Body body The body object to use. +-- +function Character:setBody( body ) + self.body = body +end - --- - -- Sets the map the character is currently on. - -- @tparam Map map The map to set for this character. - -- - function self:setMap( nmap ) - map = nmap; - end +---- +-- Sets the faction this character belongs to. +-- @tparam Faction faction The Faction object determining the character's faction. +-- +function Character:setFaction( faction ) + self.faction = faction +end - --- - -- Sets a new name for this character. - -- @tparam string nname The name to set for this character. - -- - function self:setName( nname ) - name = nname - 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 - function self:setNationality( nnationality ) - nationality = nnationality - end +--- +-- Sets a new name for this character. +-- @tparam string nname The name to set for this character. +-- +function Character:setName( name ) + self.name = name +end - --- - -- Sets the character's tile. - -- @param tile (Tile) The tile to set the character to. - -- - function self:setTile( ntile ) - tile = ntile; - end +--- +-- Sets the character's nationality. +-- @tparam string nationality The character's nationality. +-- +function Character:setNationality( nationality ) + self.nationality = nationality +end - function self:setThrowingSkill( nthrowingSkill ) - throwingSkill = nthrowingSkill; - 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 if the character is done with a turn. This is used by the AI handler - -- to determine if the character is viable for another update. - -- @param tile (Tile) The tile to set the character to. - -- - function self:setFinishedTurn( nfinished ) - finishedTurn = nfinished; - end +--- +-- Sets the character's throwing skill. +-- @tparam number throwingSkill The character's throwing skill. +-- +function Character:setThrowingSkill( throwingSkill ) + self.throwingSkill = throwingSkill +end - --- - -- Sets the character's stance. - -- @param nstance (number) The character's new stance. - -- - function self:setStance( nstance ) - stance = nstance; - end +--- +-- Sets if the character is done with a turn. This is used by the AI handler +-- to determine if the character is viable for another update. +-- @tparam boolean finished Wether the character is done with its turn. +-- +function Character:setFinishedTurn( finished ) + self.finishedTurn = finished +end - -- TODO Remove hack for saving / loading characters - function self:getSavedPosition() - return savedX, savedY - end +--- +-- Sets the character's stance. +-- @tparam number stance The character's new stance. +-- +function Character:setStance( stance ) + self.stance = stance +end - function self:setSavedPosition( x, y ) - savedX, savedY = x, y - end +-- TODO Remove hack for saving / loading characters +function Character:getSavedPosition() + return self.savedX, self.savedY +end - return self; +function Character:setSavedPosition( x, y ) + self.savedX, self.savedY = x, y end -return Character; +return Character diff --git a/src/characters/CharacterFactory.lua b/src/characters/CharacterFactory.lua index 2e428efa..87d6a955 100644 --- a/src/characters/CharacterFactory.lua +++ b/src/characters/CharacterFactory.lua @@ -144,7 +144,7 @@ function CharacterFactory.init() end function CharacterFactory.loadCharacter( savedCharacter ) - local character = Character.new() + local character = Character() character:setName( savedCharacter.name ) character:setActionPoints( savedCharacter.actionPoints ); @@ -163,7 +163,7 @@ function CharacterFactory.loadCharacter( savedCharacter ) end function CharacterFactory.newCharacter( type, factionType ) - local character = Character.new() + local character = Character() if type == 'human' then local nationality = chooseNationality() diff --git a/src/characters/Faction.lua b/src/characters/Faction.lua index 83b08d2a..a14adf2f 100644 --- a/src/characters/Faction.lua +++ b/src/characters/Faction.lua @@ -3,6 +3,7 @@ local Object = require('src.Object'); local Node = require('src.util.Node'); local Messenger = require( 'src.Messenger' ); local CharacterFactory = require( 'src.characters.CharacterFactory' ) +local Character = require( 'src.characters.Character' ) -- ------------------------------------------------ -- Module @@ -147,7 +148,7 @@ function Faction.new( type, controlledByAi ) -- @param character (Character) The character to select. -- function self:selectCharacter( character ) - assert( character:instanceOf( 'Character' ), 'Expected object of type Character!' ); + assert( character:isInstanceOf( Character ), 'Expected object of type Character!' ) local node = root; while node do if node:getObject() == character and not node:getObject():isDead() then From b2e78d50d4cee5e678ecf9ccd3484e5f1fbf0e15 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sun, 10 Dec 2017 15:52:33 +0100 Subject: [PATCH 065/178] Prevent pathing to impassable tiles --- src/characters/ai/behaviortree/leafs/BTMoveToTarget.lua | 2 +- src/characters/ai/behaviortree/leafs/BTRandomMovement.lua | 2 +- src/turnbased/helpers/MovementInput.lua | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/characters/ai/behaviortree/leafs/BTMoveToTarget.lua b/src/characters/ai/behaviortree/leafs/BTMoveToTarget.lua index b0f0e22c..226bb129 100644 --- a/src/characters/ai/behaviortree/leafs/BTMoveToTarget.lua +++ b/src/characters/ai/behaviortree/leafs/BTMoveToTarget.lua @@ -10,7 +10,7 @@ function BTMoveToTarget.new() local path; local function generatePath( target, character ) - if target and not target:isOccupied() then + if target and target:isPassable() and not target:isOccupied() then path = PathFinder.generatePath( character, target, true ); end end diff --git a/src/characters/ai/behaviortree/leafs/BTRandomMovement.lua b/src/characters/ai/behaviortree/leafs/BTRandomMovement.lua index 338e68d9..1c7ecaca 100644 --- a/src/characters/ai/behaviortree/leafs/BTRandomMovement.lua +++ b/src/characters/ai/behaviortree/leafs/BTRandomMovement.lua @@ -8,7 +8,7 @@ function BTRandomMovement.new() local self = BTLeaf.new():addInstance( 'BTRandomMovement' ); local function generatePath( target, character ) - if target and not target:isOccupied() then + if target and target:isPassable() and not target:isOccupied() then return PathFinder.generatePath( character, target, true ); end end diff --git a/src/turnbased/helpers/MovementInput.lua b/src/turnbased/helpers/MovementInput.lua index b998d01e..4df77eec 100644 --- a/src/turnbased/helpers/MovementInput.lua +++ b/src/turnbased/helpers/MovementInput.lua @@ -9,7 +9,7 @@ function MovementInput.new() local path; local function generatePath( target, character ) - if target then + if target and target:isPassable() and not target:isOccupied() then path = PathFinder.generatePath( character, target, true ); end end From 571053759b3ebb142cf7dd52ea2dbae939074378 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sun, 10 Dec 2017 17:38:54 +0100 Subject: [PATCH 066/178] Replace ipairs with normal for loop This almost halves the execution time for pathfinding on huge maps. --- src/characters/pathfinding/PathFinder.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/characters/pathfinding/PathFinder.lua b/src/characters/pathfinding/PathFinder.lua index ccc2cffc..38d247c5 100644 --- a/src/characters/pathfinding/PathFinder.lua +++ b/src/characters/pathfinding/PathFinder.lua @@ -100,12 +100,12 @@ end -- @param return (boolean) Wether the tile is in the list or not. -- local function isInList( list, tile ) - for _, node in ipairs( list ) do - if node.tile == tile then - return node; + for i = 1, #list do + if list[i].tile == tile then + return list[i] end end - return false; + return false end --- From 1acebd8252748dd27c9b63282d2608a4fbe0f310 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 11 Dec 2017 02:57:42 +0100 Subject: [PATCH 067/178] Refactor StateManager and states for turn handling --- src/CombatState.lua | 4 +- src/turnbased/StateManager.lua | 63 ++++++ src/turnbased/helpers/AttackInput.lua | 116 +++++----- src/turnbased/helpers/InteractionInput.lua | 156 +++++++------- src/turnbased/helpers/MovementInput.lua | 105 +++++---- src/turnbased/states/ExecutionState.lua | 112 +++++----- src/turnbased/states/PlanningState.lua | 236 +++++++++++---------- src/turnbased/states/State.lua | 15 -- src/turnbased/states/StateManager.lua | 50 ----- src/ui/UserInterface.lua | 12 +- src/ui/overlays/ConeOverlay.lua | 3 +- src/ui/overlays/OverlayPainter.lua | 14 +- src/ui/overlays/PathOverlay.lua | 3 +- 13 files changed, 479 insertions(+), 410 deletions(-) create mode 100644 src/turnbased/StateManager.lua delete mode 100644 src/turnbased/states/State.lua delete mode 100644 src/turnbased/states/StateManager.lua diff --git a/src/CombatState.lua b/src/CombatState.lua index 3c119817..206aa7da 100644 --- a/src/CombatState.lua +++ b/src/CombatState.lua @@ -14,7 +14,7 @@ local Factions = require( 'src.characters.Factions' ); local ProjectileManager = require( 'src.items.weapons.ProjectileManager' ); local ExplosionManager = require( 'src.items.weapons.ExplosionManager' ); local ScreenManager = require( 'lib.screenmanager.ScreenManager' ); -local StateManager = require( 'src.turnbased.states.StateManager' ) +local StateManager = require( 'src.turnbased.StateManager' ) local SadisticAIDirector = require( 'src.characters.ai.SadisticAIDirector' ) local Faction = require( 'src.characters.Faction' ) @@ -106,7 +106,7 @@ function CombatState.new() end) end) - stateManager = StateManager.new( states ) + stateManager = StateManager( states ) stateManager:push( 'planning', factions ) sadisticAIDirector = SadisticAIDirector.new( factions, stateManager ) diff --git a/src/turnbased/StateManager.lua b/src/turnbased/StateManager.lua new file mode 100644 index 00000000..1cb6d1a2 --- /dev/null +++ b/src/turnbased/StateManager.lua @@ -0,0 +1,63 @@ +--- +-- @module StateManager +-- + +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ + +local Class = require( 'lib.Middleclass' ) + +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ + +local StateManager = Class( 'StateManager' ) + +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ + +function StateManager:initialize( states ) + self.stack = {} + self.states = states +end + +function StateManager:switch( state, ... ) + self.stack = {} + self:push( state, ... ) +end + +function StateManager:push( state, ... ) + self.stack[#self.stack + 1] = self.states[state]( self ) + + if self.stack[#self.stack].enter then + self.stack[#self.stack]:enter( ... ) + end +end + +function StateManager:pop() + self.stack[#self.stack] = nil +end + +function StateManager:update( dt ) + self.stack[#self.stack]:update( dt ) +end + +function StateManager:keypressed( key, scancode, isrepeat ) + self.stack[#self.stack]:keypressed( key, scancode, isrepeat ) +end + +function StateManager:selectTile( tile, button ) + self.stack[#self.stack]:selectTile( tile, button ) +end + +function StateManager:blocksInput() + return self.stack[#self.stack]:blocksInput() +end + +function StateManager:getState() + return self.stack[#self.stack] +end + +return StateManager diff --git a/src/turnbased/helpers/AttackInput.lua b/src/turnbased/helpers/AttackInput.lua index 3b933e14..36489607 100644 --- a/src/turnbased/helpers/AttackInput.lua +++ b/src/turnbased/helpers/AttackInput.lua @@ -1,14 +1,22 @@ -local State = require( 'src.turnbased.states.State' ); -local RangedAttack = require( 'src.characters.actions.RangedAttack' ); -local MeleeAttack = require( 'src.characters.actions.MeleeAttack' ); -local ThrowingAttack = require( 'src.characters.actions.ThrowingAttack' ); -local Rearm = require( 'src.characters.actions.Rearm' ); +--- +-- @module AttackInput +-- + +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ + +local Class = require( 'lib.Middleclass' ) +local RangedAttack = require( 'src.characters.actions.RangedAttack' ) +local MeleeAttack = require( 'src.characters.actions.MeleeAttack' ) +local ThrowingAttack = require( 'src.characters.actions.ThrowingAttack' ) +local Rearm = require( 'src.characters.actions.Rearm' ) -- ------------------------------------------------ -- Module -- ------------------------------------------------ -local AttackInput = {}; +local AttackInput = Class( 'AttackInput' ) -- ------------------------------------------------ -- Constants @@ -17,62 +25,56 @@ local AttackInput = {}; local WEAPON_TYPES = require( 'src.constants.WEAPON_TYPES' ) -- ------------------------------------------------ --- Constructor +-- Public Methods -- ------------------------------------------------ -function AttackInput.new() - local self = State.new():addInstance( 'AttackInput' ); - - --- - -- Requests a new action for a given character. - -- @param target (Tile) The tile to act upon. - -- @param character (Character) The character to create the action for. - -- @return (boolean) True if an action was created, false otherwise. - -- - function self:request( target, character ) - -- Prevent characters from attacking themselves. - if target == character:getTile() then - return false; - end - - local weapon = character:getWeapon(); - - -- Characters can't attack with no weapon equipped. - if not weapon then - return false; - end - - -- Handle Melee weapons. - if weapon:getSubType() == WEAPON_TYPES.MELEE then - character:enqueueAction( MeleeAttack( character, target )) - end - - -- Handle Thrown weapons. - if weapon:getSubType() == WEAPON_TYPES.THROWN then - character:enqueueAction( ThrowingAttack( character, target )) - character:enqueueAction( Rearm( character, weapon:getID() )) - end - - -- Handle Ranged weapons. - if weapon:getSubType() == WEAPON_TYPES.RANGED then - character:enqueueAction( RangedAttack( character, target )) - end - - return true; +--- +-- Requests a new action for a given character. +-- @tparam Tile target The tile to act upon. +-- @tparam Character character The character to create the action for. +-- @treturn boolean True if an action was created, false otherwise. +-- +function AttackInput:request( target, character ) + -- Prevent characters from attacking themselves. + if target == character:getTile() then + return false + end + + local weapon = character:getWeapon() + + -- Characters can't attack with no weapon equipped. + if not weapon then + return false end - --- - -- Returns the predicted ap cost for this action. - -- @param character (Character) The character taking the action. - -- @return (number) The cost. - -- - function self:getPredictedAPCost( character ) - if character:getWeapon() then - return character:getWeapon():getAttackCost(); - end + -- Handle Melee weapons. + if weapon:getSubType() == WEAPON_TYPES.MELEE then + character:enqueueAction( MeleeAttack( character, target )) end - return self; + -- Handle Thrown weapons. + if weapon:getSubType() == WEAPON_TYPES.THROWN then + character:enqueueAction( ThrowingAttack( character, target )) + character:enqueueAction( Rearm( character, weapon:getID() )) + end + + -- Handle Ranged weapons. + if weapon:getSubType() == WEAPON_TYPES.RANGED then + character:enqueueAction( RangedAttack( character, target )) + end + + return true +end + +--- +-- Returns the predicted ap cost for this action. +-- @tparam Character character The character taking the action. +-- @treturn number The cost. +-- +function AttackInput:getPredictedAPCost( character ) + if character:getWeapon() then + return character:getWeapon():getAttackCost() + end end -return AttackInput; +return AttackInput diff --git a/src/turnbased/helpers/InteractionInput.lua b/src/turnbased/helpers/InteractionInput.lua index 5b739ee0..e69e958e 100644 --- a/src/turnbased/helpers/InteractionInput.lua +++ b/src/turnbased/helpers/InteractionInput.lua @@ -1,104 +1,102 @@ -local State = require( 'src.turnbased.states.State' ); -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 InteractionInput +-- -- ------------------------------------------------ --- Module +-- Required Modules -- ------------------------------------------------ -local InteractionInput = {}; +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' ) -- ------------------------------------------------ --- Constructor +-- Module -- ------------------------------------------------ -function InteractionInput.new() - local self = State.new():addInstance( 'InteractionInput' ); +local InteractionInput = Class( 'InteractionInput' ) - -- ------------------------------------------------ - -- Private Methods - -- ------------------------------------------------ +-- ------------------------------------------------ +-- Private Methods +-- ------------------------------------------------ - --- - -- Creates opening or closing actions for doors depending on the current - -- state of the door. - -- @param target (Tile) The tile to act upon. - -- @param character (Character) The character to create the action for. - -- - local function handleDoors( target, character ) - if target:isPassable() then - character:enqueueAction( Close( character, target )) - else - character:enqueueAction( Open( character, target )) - end +--- +-- Creates opening or closing actions for doors depending on the current +-- state of the door. +-- @tparam Tile target The tile to act upon. +-- @tparam Character character The character to create the action for. +-- +local function handleDoors( target, character ) + if target:isPassable() then + character:enqueueAction( Close( character, target )) + else + character:enqueueAction( Open( character, target )) end +end - -- ------------------------------------------------ - -- Public Methods - -- ------------------------------------------------ - - --- - -- Requests a new action for a given character. - -- @param target (Tile) The tile to act upon. - -- @param character (Character) The character to create the action for. - -- @return (boolean) True if an action was created, false otherwise. - -- - function self:request( target, character ) - -- Check health of enemy characters. - if target:isOccupied() and target:getCharacter():getFaction():getType() ~= character:getFaction():getType() then - ScreenManager.push( 'health', target:getCharacter() ); - return true; - end +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - -- Characters can only interact with adjacent tiles. - if not target:isAdjacent( character:getTile() ) then - return false; - end +--- +-- Requests a new action for a given character. +-- @tparam Tile target The tile to act upon. +-- @tparam Character character The character to create the action for. +-- @treturn boolean True if an action was created, false otherwise. +-- +function InteractionInput:request( target, character ) + -- Check health of enemy characters. + if target:isOccupied() and target:getCharacter():getFaction():getType() ~= character:getFaction():getType() then + ScreenManager.push( 'health', target:getCharacter() ) + return true + end - -- Handle interactions with world objects. - if target:hasWorldObject() then - if target:getWorldObject():isOpenable() then - handleDoors( target, character ); - return true; - end + -- Characters can only interact with adjacent tiles. + if not target:isAdjacent( character:getTile() ) then + return false + end - if target:getWorldObject():isContainer() then - character:enqueueAction( OpenInventory( character, target )) - return true; - end - return false; + -- Handle interactions with world objects. + if target:hasWorldObject() then + if target:getWorldObject():isOpenable() then + handleDoors( target, character ) + return true end - -- Handle interactions with other characters. - if target:isOccupied() then - if target:getCharacter():getFaction():getType() == character:getFaction():getType() then - character:enqueueAction( OpenInventory( character, target )) - return true; - end - return false; + if target:getWorldObject():isContainer() then + character:enqueueAction( OpenInventory( character, target )) + return true end - - -- Allow interaction with empty adjacent tiles. - character:enqueueAction( OpenInventory( character, target )) - return true; + return false end - --- - -- Returns the predicted ap cost for this action. - -- @param target (Tile) The tile to interact with. - -- @param character (Character) The character taking the action. - -- @return (number) The cost. - -- - function self:getPredictedAPCost( target, character ) - if target:hasWorldObject() then - return target:getWorldObject():getInteractionCost( character:getStance() ) or 0; + -- Handle interactions with other characters. + if target:isOccupied() then + if target:getCharacter():getFaction():getType() == character:getFaction():getType() then + character:enqueueAction( OpenInventory( character, target )) + return true end - return 0; + return false end - return self; + -- Allow interaction with empty adjacent tiles. + character:enqueueAction( OpenInventory( character, target )) + return true +end + +--- +-- Returns the predicted ap cost for this action. +-- @tparam Tile target The tile to interact with. +-- @tparam Character character The character taking the action. +-- @treturn number The cost. +-- +function InteractionInput:getPredictedAPCost( target, character ) + if target:hasWorldObject() then + return target:getWorldObject():getInteractionCost( character:getStance() ) or 0 + end + return 0 end -return InteractionInput; +return InteractionInput diff --git a/src/turnbased/helpers/MovementInput.lua b/src/turnbased/helpers/MovementInput.lua index 4df77eec..6535c7c6 100644 --- a/src/turnbased/helpers/MovementInput.lua +++ b/src/turnbased/helpers/MovementInput.lua @@ -1,51 +1,82 @@ -local State = require( 'src.turnbased.states.State' ); -local PathFinder = require( 'src.characters.pathfinding.PathFinder' ); +--- +-- @module MovementInput +-- -local MovementInput = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ -function MovementInput.new() - local self = State.new():addInstance( 'MovementInput' ); +local Class = require( 'lib.Middleclass' ) +local PathFinder = require( 'src.characters.pathfinding.PathFinder' ) - local path; +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ - local function generatePath( target, character ) - if target and target:isPassable() and not target:isOccupied() then - path = PathFinder.generatePath( character, target, true ); - end - end +local MovementInput = Class( 'MovementInput' ) - function self:request( ... ) - local target, character = ...; - - if target == character:getTile() then - return false; - elseif not path or target ~= path:getTarget() then - generatePath( target, character ); - return false; - else - path:generateActions( character ); - path = nil; - return true; - end - end +-- ------------------------------------------------ +-- Private Methods +-- ------------------------------------------------ - function self:hasPath() - return path ~= nil; +local function generatePath( target, character ) + if target and target:isPassable() and not target:isOccupied() then + return PathFinder.generatePath( character, target, true ) end +end +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - function self:getPath() - return path; +--- +-- Creates a path to the target for the given character. The first time it will +-- generate a path, but don't generate any actions. This is used to preview the +-- path for the player. If there is a path and the target is the target of the +-- existing path the we generate all the actions needed to move the character. +-- @tparam Tile target The tile to act upon. +-- @tparam Character character The character to create the action for. +-- @treturn boolean True if an action was created, false otherwise. +-- +function MovementInput:request( target, character ) + -- Don't generate a path for the tile the character is standing on. + if target == character:getTile() then + return false end - --- - -- Returns the predicted ap cost for this action. - -- @return (number) The cost. - -- - function self:getPredictedAPCost() - return path:getCost(); + -- Generate a new path if we don't have one already or if the target is + -- different from the target of an existing path. + if not self.path or target ~= self.path:getTarget() then + self.path = generatePath( target, character ) + return false end - return self; + -- Generate actions for the existing path. + self.path:generateActions( character ) + self.path = nil + return true +end + +--- +-- Checks wether there is a generated path. +-- @treturn boolean True if a path exists. +-- +function MovementInput:hasPath() + return self.path ~= nil +end + +--- +-- Returns the generated path. +-- @treturn Path The generated path. +function MovementInput:getPath() + return self.path +end + +--- +-- Returns the predicted ap cost for this action. +-- @treturn number The cost. +-- +function MovementInput:getPredictedAPCost() + return self.path:getCost() end -return MovementInput; +return MovementInput diff --git a/src/turnbased/states/ExecutionState.lua b/src/turnbased/states/ExecutionState.lua index d4e7d127..ad0d2a48 100644 --- a/src/turnbased/states/ExecutionState.lua +++ b/src/turnbased/states/ExecutionState.lua @@ -1,67 +1,81 @@ -local State = require( 'src.turnbased.states.State' ); -local ProjectileManager = require( 'src.items.weapons.ProjectileManager' ); -local ExplosionManager = require( 'src.items.weapons.ExplosionManager' ); -local Log = require( 'src.util.Log' ); +--- +-- @module ExecutionState +-- -local ExecutionState = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ -local AI_DELAY = 0; -local PLAYER_DELAY = 0.15; +local Class = require( 'lib.Middleclass' ) +local ProjectileManager = require( 'src.items.weapons.ProjectileManager' ) +local ExplosionManager = require( 'src.items.weapons.ExplosionManager' ) +local Log = require( 'src.util.Log' ) -function ExecutionState.new( stateManager ) - local self = State.new():addInstance( 'ExecutionState' ); +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ - local character; - local actionTimer = 0; - local delay; - local factions; +local ExecutionState = Class( 'ExecutionState' ) - function self:enter( nfactions, ncharacter ) - factions = nfactions; - character = ncharacter; - delay = character:getFaction():isAIControlled() and AI_DELAY or PLAYER_DELAY; - end +-- ------------------------------------------------ +-- Constants +-- ------------------------------------------------ - function self:update( dt ) - if not ProjectileManager.isDone() then - ProjectileManager.update( dt ); - return; - end +local AI_DELAY = 0 +local PLAYER_DELAY = 0.15 - if not ExplosionManager.isDone() then - ExplosionManager.update( dt ); - return; - end +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - if character:isDead() then - Log.debug( string.format( 'Character (%s) is dead. Stopping execution', tostring( character )), 'ExecutionState' ); - stateManager:pop(); - return; - end +function ExecutionState:initialize( stateManager ) + self.stateManager = stateManager + self.actionTimer = 0 +end + +function ExecutionState:enter( factions, character ) + self.factions = factions + self.character = character + self.delay = character:getFaction():isAIControlled() and AI_DELAY or PLAYER_DELAY +end + +function ExecutionState:update( dt ) + if not ProjectileManager.isDone() then + ProjectileManager.update( dt ) + return + end + + if not ExplosionManager.isDone() then + ExplosionManager.update( dt ) + return + end - if actionTimer > delay then - if character:hasEnqueuedAction() then - character:performAction(); + if self.character:isDead() then + Log.debug( string.format( 'Character (%s) is dead. Stopping execution', tostring( self.character )), 'ExecutionState' ) + self.stateManager:pop() + return + end - if factions:getPlayerFaction():canSee( character:getTile() ) then - delay = PLAYER_DELAY; - else - delay = AI_DELAY; - end + if self.actionTimer > self.delay then + if self.character:hasEnqueuedAction() then + self.character:performAction() - actionTimer = 0; + if self.factions:getPlayerFaction():canSee( self.character:getTile() ) then + self.delay = PLAYER_DELAY else - stateManager:pop(); + self.delay = AI_DELAY end - end - actionTimer = actionTimer + dt; - end - function self:blocksInput() - return true; + self.actionTimer = 0 + else + self.stateManager:pop() + end end + self.actionTimer = self.actionTimer + dt +end - return self; +function ExecutionState:blocksInput() + return true end -return ExecutionState; +return ExecutionState diff --git a/src/turnbased/states/PlanningState.lua b/src/turnbased/states/PlanningState.lua index d9787751..d28e56d3 100644 --- a/src/turnbased/states/PlanningState.lua +++ b/src/turnbased/states/PlanningState.lua @@ -1,136 +1,152 @@ -local StateManager = require( 'src.turnbased.states.StateManager' ); -local ScreenManager = require( 'lib.screenmanager.ScreenManager' ); -local State = require( 'src.turnbased.states.State' ); -local Reload = require( 'src.characters.actions.Reload' ); -local StandUp = require( 'src.characters.actions.StandUp' ); -local Crouch = require( 'src.characters.actions.Crouch' ); -local LieDown = require( 'src.characters.actions.LieDown' ); -local OpenInventory = require( 'src.characters.actions.OpenInventory' ); +--- +-- @module PlanningState +-- -local PlanningState = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ -function PlanningState.new( stateManager ) - local self = State.new():addInstance( 'PlanningState' ); +local Class = require( 'lib.Middleclass' ) +local StateManager = require( 'src.turnbased.StateManager' ) +local ScreenManager = require( 'lib.screenmanager.ScreenManager' ) +local Reload = require( 'src.characters.actions.Reload' ) +local StandUp = require( 'src.characters.actions.StandUp' ) +local Crouch = require( 'src.characters.actions.Crouch' ) +local LieDown = require( 'src.characters.actions.LieDown' ) +local OpenInventory = require( 'src.characters.actions.OpenInventory' ) +local AttackInput = require( 'src.turnbased.helpers.AttackInput' ) +local InteractionInput = require( 'src.turnbased.helpers.InteractionInput' ) + +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ + +local PlanningState = Class( 'PlanningState' ) + +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ + +function PlanningState:initialize( stateManager ) + self.stateManager = stateManager - local factions; local inputStates = { attack = require( 'src.turnbased.helpers.AttackInput' ), movement = require( 'src.turnbased.helpers.MovementInput' ), interaction = require( 'src.turnbased.helpers.InteractionInput' ) } - local inputStateHandler = StateManager.new( inputStates ); - inputStateHandler:switch( 'movement' ); - function self:enter( nfactions ) - factions = nfactions; - end + self.inputStateHandler = StateManager( inputStates ) + self.inputStateHandler:switch( 'movement' ) +end - function self:update() - if factions:getFaction():getCurrentCharacter():isDead() then - if factions:getFaction():hasLivingCharacters() then - factions:getFaction():nextCharacter(); - else - factions:nextFaction(); - end - end - end +function PlanningState:enter( factions ) + self.factions = factions +end - -- TODO Refactor input handling. - function self:keypressed( _, scancode, _ ) - local character = factions:getFaction():getCurrentCharacter(); - if character:isDead() then - return; +function PlanningState:update() + if self.factions:getFaction():getCurrentCharacter():isDead() then + if self.factions:getFaction():hasLivingCharacters() then + self.factions:getFaction():nextCharacter() + else + self.factions:nextFaction() end + end +end - if scancode == 'right' then - if not character:getWeapon() then - return; - end - character:getWeapon():selectNextFiringMode(); - elseif scancode == 'left' then - if not character:getWeapon() then - return; - end - character:getWeapon():selectPrevFiringMode(); - elseif scancode == 'c' then - character:clearActions(); - character:enqueueAction( Crouch( character )) - stateManager:push( 'execution', factions, character ); - elseif scancode == 's' then - character:clearActions(); - character:enqueueAction( StandUp( character )) - stateManager:push( 'execution', factions, character ); - elseif scancode == 'p' then - character:clearActions(); - character:enqueueAction( LieDown( character )) - stateManager:push( 'execution', factions, character ); - elseif scancode == 'r' then - character:clearActions(); - character:enqueueAction( Reload( character )) - stateManager:push( 'execution', factions, character ); - elseif scancode == 'a' then - -- Make attack mode toggleable. - character:clearActions(); - if inputStateHandler:getState():instanceOf( 'AttackInput' ) then - inputStateHandler:switch( 'movement' ); - else - inputStateHandler:switch( 'attack' ); - end - elseif scancode == 'e' then - character:clearActions(); - if inputStateHandler:getState():instanceOf( 'InteractionInput' ) then - inputStateHandler:switch( 'movement' ); - else - inputStateHandler:switch( 'interaction' ); - end - elseif scancode == 'm' then - character:clearActions(); - inputStateHandler:switch( 'movement' ); - elseif scancode == 'space' then - inputStateHandler:switch( 'movement' ); - factions:getFaction():nextCharacter(); - elseif scancode == 'backspace' then - inputStateHandler:switch( 'movement' ); - factions:getFaction():prevCharacter(); - elseif scancode == 'return' then - inputStateHandler:switch( 'movement' ); - factions:nextFaction(); - elseif scancode == 'i' then - character:enqueueAction( OpenInventory( character, character:getTile() )) - stateManager:push( 'execution', factions, character ); - elseif scancode == 'h' then - ScreenManager.push( 'health', character ); - end +-- TODO Refactor input handling. +function PlanningState:keypressed( _, scancode, _ ) + local character = self.factions:getFaction():getCurrentCharacter() + if character:isDead() then + return end - function self:selectTile( tile, button ) - local character = factions:getFaction():getCurrentCharacter(); - if not tile or character:isDead() then - return; + if scancode == 'right' then + if not character:getWeapon() then + return end - - if button == 2 and tile:isOccupied() then - inputStateHandler:switch( 'movement' ); - factions:getFaction():selectCharacter( tile:getCharacter() ); - return; + character:getWeapon():selectNextFiringMode() + elseif scancode == 'left' then + if not character:getWeapon() then + return end - - -- Request actions to execute. - local execute = inputStateHandler:getState():request( tile, character ); - if execute then - stateManager:push( 'execution', factions, character ); + character:getWeapon():selectPrevFiringMode() + elseif scancode == 'c' then + character:clearActions() + character:enqueueAction( Crouch( character )) + self.stateManager:push( 'execution', self.factions, character ) + elseif scancode == 's' then + character:clearActions() + character:enqueueAction( StandUp( character )) + self.stateManager:push( 'execution', self.factions, character ) + elseif scancode == 'p' then + character:clearActions() + character:enqueueAction( LieDown( character )) + self.stateManager:push( 'execution', self.factions, character ) + elseif scancode == 'r' then + character:clearActions() + character:enqueueAction( Reload( character )) + self.stateManager:push( 'execution', self.factions, character ) + elseif scancode == 'a' then + -- Make attack mode toggleable. + character:clearActions() + if self.inputStateHandler:getState():isInstanceOf( AttackInput ) then + self.inputStateHandler:switch( 'movement' ) + else + self.inputStateHandler:switch( 'attack' ) end + elseif scancode == 'e' then + character:clearActions() + if self.inputStateHandler:getState():isInstanceOf( InteractionInput ) then + self.inputStateHandler:switch( 'movement' ) + else + self.inputStateHandler:switch( 'interaction' ) + end + elseif scancode == 'm' then + character:clearActions() + self.inputStateHandler:switch( 'movement' ) + elseif scancode == 'space' then + self.inputStateHandler:switch( 'movement' ) + self.factions:getFaction():nextCharacter() + elseif scancode == 'backspace' then + self.inputStateHandler:switch( 'movement' ) + self.factions:getFaction():prevCharacter() + elseif scancode == 'return' then + self.inputStateHandler:switch( 'movement' ) + self.factions:nextFaction() + elseif scancode == 'i' then + character:enqueueAction( OpenInventory( character, character:getTile() )) + self.stateManager:push( 'execution', self.factions, character ) + elseif scancode == 'h' then + ScreenManager.push( 'health', character ) + end +end + +function PlanningState:selectTile( tile, button ) + local character = self.factions:getFaction():getCurrentCharacter() + if not tile or character:isDead() then + return end - function self:getInputMode() - return inputStateHandler:getState(); + if button == 2 and tile:isOccupied() then + self.inputStateHandler:switch( 'movement' ) + self.factions:getFaction():selectCharacter( tile:getCharacter() ) + return end - function self:blocksInput() - return false; + -- Request actions to execute. + local execute = self.inputStateHandler:getState():request( tile, character ) + if execute then + self.stateManager:push( 'execution', self.factions, character ) end +end + +function PlanningState:getInputMode() + return self.inputStateHandler:getState() +end - return self; +function PlanningState:blocksInput() + return false end -return PlanningState; +return PlanningState diff --git a/src/turnbased/states/State.lua b/src/turnbased/states/State.lua deleted file mode 100644 index a5c3b404..00000000 --- a/src/turnbased/states/State.lua +++ /dev/null @@ -1,15 +0,0 @@ -local Object = require('src.Object'); - -local State = {}; - -function State.new() - local self = Object.new():addInstance( 'State' ); - - function self:enter() end - function self:update() end - function self:leave() end - - return self; -end - -return State; diff --git a/src/turnbased/states/StateManager.lua b/src/turnbased/states/StateManager.lua deleted file mode 100644 index 7413d70d..00000000 --- a/src/turnbased/states/StateManager.lua +++ /dev/null @@ -1,50 +0,0 @@ -local StateManager = {}; - -function StateManager.new( states ) - local self = {}; - - local stack = {}; - - function self:blocksInput() - return stack[#stack]:blocksInput(); - end - - function self:update( dt ) - stack[#stack]:update( dt ); - end - - function self:keypressed( key, scancode, isrepeat ) - stack[#stack]:keypressed( key, scancode, isrepeat ) - end - - function self:selectTile( tile, button ) - stack[#stack]:selectTile( tile, button ); - end - - function self:processEvent( event ) - stack[#stack]:processEvent( event ); - end - - function self:switch( state, ... ) - stack = {}; - stack[#stack + 1] = states[state].new( self ); - stack[#stack]:enter( ... ); - end - - function self:push( state, ... ) - stack[#stack + 1] = states[state].new( self ); - stack[#stack]:enter( ... ); - end - - function self:pop() - stack[#stack] = nil; - end - - function self:getState() - return stack[#stack]; - end - - return self; -end - -return StateManager; diff --git a/src/ui/UserInterface.lua b/src/ui/UserInterface.lua index 40cd8944..e016f2b7 100644 --- a/src/ui/UserInterface.lua +++ b/src/ui/UserInterface.lua @@ -1,6 +1,10 @@ local Log = require( 'src.util.Log' ); local Translator = require( 'src.util.Translator' ); local TexturePacks = require( 'src.ui.texturepacks.TexturePacks' ) +local AttackInput = require( 'src.turnbased.helpers.AttackInput' ) +local MovementInput = require( 'src.turnbased.helpers.MovementInput' ) +local InteractionInput = require( 'src.turnbased.helpers.InteractionInput' ) +local ExecutionState = require( 'src.turnbased.states.ExecutionState' ) -- ------------------------------------------------ -- Module @@ -91,7 +95,7 @@ function UserInterface.new( game, camera ) love.graphics.print( apString, tw, love.graphics.getHeight() - th * 5 ) -- Hide the cost display during the turn's execution. - if game:getState():instanceOf( 'ExecutionState' ) then + if game:getState():isInstanceOf( ExecutionState ) then return; end @@ -100,11 +104,11 @@ function UserInterface.new( game, camera ) local cost; if tile then - if mode:instanceOf( 'AttackInput' ) then + if mode:isInstanceOf( AttackInput ) then cost = mode:getPredictedAPCost( character ); - elseif mode:instanceOf( 'InteractionInput' ) then + elseif mode:isInstanceOf( InteractionInput ) then cost = mode:getPredictedAPCost( tile, character ); - elseif mode:instanceOf( 'MovementInput' ) and mode:hasPath() then + elseif mode:isInstanceOf( MovementInput ) and mode:hasPath() then cost = mode:getPredictedAPCost(); end end diff --git a/src/ui/overlays/ConeOverlay.lua b/src/ui/overlays/ConeOverlay.lua index 8c89695e..37ff3566 100644 --- a/src/ui/overlays/ConeOverlay.lua +++ b/src/ui/overlays/ConeOverlay.lua @@ -12,6 +12,7 @@ local Bresenham = require( 'lib.Bresenham' ) local ProjectilePath = require( 'src.items.weapons.ProjectilePath' ) local VectorMath = require( 'src.util.VectorMath' ) local TexturePacks = require( 'src.ui.texturepacks.TexturePacks' ) +local AttackInput = require( 'src.turnbased.helpers.AttackInput' ) -- ------------------------------------------------ -- Module @@ -62,7 +63,7 @@ function ConeOverlay.new( game, pulser, camera ) function self:generate() -- Exit early if we aren't in the attack input mode. - if not game:getState():getInputMode():instanceOf( 'AttackInput' ) then + if not game:getState():getInputMode():isInstanceOf( AttackInput ) then return end diff --git a/src/ui/overlays/OverlayPainter.lua b/src/ui/overlays/OverlayPainter.lua index fd07e68b..e70ec0d9 100644 --- a/src/ui/overlays/OverlayPainter.lua +++ b/src/ui/overlays/OverlayPainter.lua @@ -15,6 +15,10 @@ local ConeOverlay = require( 'src.ui.overlays.ConeOverlay' ) local PathOverlay = require( 'src.ui.overlays.PathOverlay' ) local ParticleLayer = require( 'src.ui.overlays.ParticleLayer' ) local TexturePacks = require( 'src.ui.texturepacks.TexturePacks' ) +local AttackInput = require( 'src.turnbased.helpers.AttackInput' ) +local MovementInput = require( 'src.turnbased.helpers.MovementInput' ) +local InteractionInput = require( 'src.turnbased.helpers.InteractionInput' ) +local ExecutionState = require( 'src.turnbased.states.ExecutionState' ) -- ------------------------------------------------ -- Module @@ -61,11 +65,11 @@ function OverlayPainter.new( game, camera ) love.graphics.rectangle( 'fill', cx, cy, tw, th ) local id - if game:getState():getInputMode():instanceOf( 'MovementInput' ) then + if game:getState():getInputMode():isInstanceOf( MovementInput ) then id = 'ui_mouse_pointer_movement' - elseif game:getState():getInputMode():instanceOf( 'AttackInput' ) then + elseif game:getState():getInputMode():isInstanceOf( AttackInput ) then id = 'ui_mouse_pointer_attack' - elseif game:getState():getInputMode():instanceOf( 'InteractionInput' ) then + elseif game:getState():getInputMode():isInstanceOf( InteractionInput ) then id = 'ui_mouse_pointer_interact' end @@ -102,7 +106,7 @@ function OverlayPainter.new( game, camera ) -- @tparam number dt The time since the last frame. -- function self:update( dt ) - if not game:getState():instanceOf( 'ExecutionState' ) then + if not game:getState():isInstanceOf( ExecutionState ) then coneOverlay:generate() end @@ -116,7 +120,7 @@ function OverlayPainter.new( game, camera ) function self:draw() local character = game:getCurrentCharacter() if not character:getFaction():isAIControlled() - and not game:getState():instanceOf( 'ExecutionState' ) then + and not game:getState():isInstanceOf( ExecutionState ) then pathOverlay:draw() drawMouseCursor() coneOverlay:draw() diff --git a/src/ui/overlays/PathOverlay.lua b/src/ui/overlays/PathOverlay.lua index cefeca3d..966ab801 100644 --- a/src/ui/overlays/PathOverlay.lua +++ b/src/ui/overlays/PathOverlay.lua @@ -9,6 +9,7 @@ local Object = require( 'src.Object' ) local TexturePacks = require( 'src.ui.texturepacks.TexturePacks' ) +local MovementInput = require( 'src.turnbased.helpers.MovementInput' ) -- ------------------------------------------------ -- Module @@ -57,7 +58,7 @@ function PathOverlay.new( game, pulser ) local tw, th = TexturePacks.getTileDimensions() local character = game:getCurrentCharacter() local mode = game:getState():getInputMode() - if mode:instanceOf( 'MovementInput' ) and mode:hasPath() then + if mode:isInstanceOf( MovementInput ) and mode:hasPath() then local total = character:getActionPoints() local ap = total mode:getPath():iterate( function( tile ) From 3a9f1dcc101900cf2b3a9226069af82fb411674d Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 11 Dec 2017 03:39:57 +0100 Subject: [PATCH 068/178] Move planning state controls to settings file The controls are now defined in the default settings table and map to a certain action which is passed down to the input method where an appropriate action is performed. This is part of an effort to allow customizable controls (See #76). --- src/CombatState.lua | 5 +- src/Settings.lua | 23 ++++ src/turnbased/StateManager.lua | 4 +- src/turnbased/states/PlanningState.lua | 155 +++++++++++++++---------- 4 files changed, 124 insertions(+), 63 deletions(-) diff --git a/src/CombatState.lua b/src/CombatState.lua index 206aa7da..3e060904 100644 --- a/src/CombatState.lua +++ b/src/CombatState.lua @@ -17,6 +17,7 @@ local ScreenManager = require( 'lib.screenmanager.ScreenManager' ); local StateManager = require( 'src.turnbased.StateManager' ) local SadisticAIDirector = require( 'src.characters.ai.SadisticAIDirector' ) local Faction = require( 'src.characters.Faction' ) +local Settings = require( 'src.Settings' ) -- ------------------------------------------------ -- Module @@ -162,11 +163,11 @@ function CombatState.new() return factions; end - function self:keypressed( key, scancode, isrepeat ) + function self:keypressed( _, scancode, _ ) if factions:getFaction():isAIControlled() or stateManager:blocksInput() then return end - stateManager:keypressed( key, scancode, isrepeat ) + stateManager:input( Settings.mapInput( scancode )) end function self:mousepressed( mx, my, button ) diff --git a/src/Settings.lua b/src/Settings.lua index 1c7a495c..63e4730f 100644 --- a/src/Settings.lua +++ b/src/Settings.lua @@ -25,6 +25,22 @@ local DEFAULT_SETTINGS = { locale = 'en_EN', mapeditor = false, texturepack = 'default' + }, + controls = { + ['x'] = 'action_stand', + ['c'] = 'action_crouch', + ['v'] = 'action_prone', + ['r'] = 'action_reload_weapon', + ['.'] = 'next_weapon_mode', + [','] = 'prev_weapon_mode', + ['1'] = 'movement_mode', + ['2'] = 'attack_mode', + ['3'] = 'interaction_mode', + ['tab'] = 'next_character', + ['lshift'] = 'prev_character', + ['return'] = 'end_turn', + ['i'] = 'open_inventory_screen', + ['h'] = 'open_health_screen', } } @@ -83,6 +99,13 @@ function Settings.load() settings = Compressor.load( FILE_NAME ) end +--- +-- Maps a scancode to a control action. +-- +function Settings.mapInput( scancode ) + return settings.controls[scancode] +end + -- ------------------------------------------------ -- Getters -- ------------------------------------------------ diff --git a/src/turnbased/StateManager.lua b/src/turnbased/StateManager.lua index 1cb6d1a2..7019a471 100644 --- a/src/turnbased/StateManager.lua +++ b/src/turnbased/StateManager.lua @@ -44,8 +44,8 @@ function StateManager:update( dt ) self.stack[#self.stack]:update( dt ) end -function StateManager:keypressed( key, scancode, isrepeat ) - self.stack[#self.stack]:keypressed( key, scancode, isrepeat ) +function StateManager:input( input ) + self.stack[#self.stack]:input( input ) end function StateManager:selectTile( tile, button ) diff --git a/src/turnbased/states/PlanningState.lua b/src/turnbased/states/PlanningState.lua index d28e56d3..033a84c4 100644 --- a/src/turnbased/states/PlanningState.lua +++ b/src/turnbased/states/PlanningState.lua @@ -23,6 +23,91 @@ local InteractionInput = require( 'src.turnbased.helpers.InteractionInput' ) local PlanningState = Class( 'PlanningState' ) +-- ------------------------------------------------ +-- Private Methods +-- ------------------------------------------------ + +local function handleCharacterActions( input, character ) + local action + if input == 'action_crouch' then + action = Crouch( character ) + elseif input == 'action_stand' then + action = StandUp( character ) + elseif input == 'action_prone' then + action = LieDown( character ) + elseif input == 'action_reload_weapon' then + action = Reload( character ) + else + return false + end + + character:clearActions() + character:enqueueAction( action ) + return true +end + +local function handleInputSelectionActions( input, inputStateHandler, character ) + if input == 'attack_mode' then + if inputStateHandler:getState():isInstanceOf( AttackInput ) then + inputStateHandler:switch( 'movement' ) + else + inputStateHandler:switch( 'attack' ) + end + elseif input == 'interaction_mode' then + if inputStateHandler:getState():isInstanceOf( InteractionInput ) then + inputStateHandler:switch( 'movement' ) + else + inputStateHandler:switch( 'interaction' ) + end + elseif input == 'movement_mode' then + inputStateHandler:switch( 'movement' ) + else + return false + end + + character:clearActions() + return true +end + +local function handleOtherActions( input, stateManager, inputStateHandler, factions, character ) + if input == 'next_character' then + inputStateHandler:switch( 'movement' ) + factions:getFaction():nextCharacter() + return true + elseif input == 'prev_character' then + inputStateHandler:switch( 'movement' ) + factions:getFaction():prevCharacter() + return true + end + + if input == 'end_turn' then + inputStateHandler:switch( 'movement' ) + factions:nextFaction() + return true + end + + if input == 'open_inventory_screen' then + character:enqueueAction( OpenInventory( character, character:getTile() )) + stateManager:push( 'execution', factions, character ) + return true + elseif input == 'open_health_screen' then + ScreenManager.push( 'health', character ) + return true + end + + if not character:getWeapon() then + return false + end + + if input == 'next_weapon_mode' then + character:getWeapon():selectNextFiringMode() + elseif input == 'prev_weapon_mode' then + character:getWeapon():selectPrevFiringMode() + end + + return true +end + -- ------------------------------------------------ -- Public Methods -- ------------------------------------------------ @@ -54,71 +139,23 @@ function PlanningState:update() end end --- TODO Refactor input handling. -function PlanningState:keypressed( _, scancode, _ ) +function PlanningState:input( input ) local character = self.factions:getFaction():getCurrentCharacter() if character:isDead() then return end - if scancode == 'right' then - if not character:getWeapon() then - return - end - character:getWeapon():selectNextFiringMode() - elseif scancode == 'left' then - if not character:getWeapon() then - return - end - character:getWeapon():selectPrevFiringMode() - elseif scancode == 'c' then - character:clearActions() - character:enqueueAction( Crouch( character )) - self.stateManager:push( 'execution', self.factions, character ) - elseif scancode == 's' then - character:clearActions() - character:enqueueAction( StandUp( character )) - self.stateManager:push( 'execution', self.factions, character ) - elseif scancode == 'p' then - character:clearActions() - character:enqueueAction( LieDown( character )) - self.stateManager:push( 'execution', self.factions, character ) - elseif scancode == 'r' then - character:clearActions() - character:enqueueAction( Reload( character )) - self.stateManager:push( 'execution', self.factions, character ) - elseif scancode == 'a' then - -- Make attack mode toggleable. - character:clearActions() - if self.inputStateHandler:getState():isInstanceOf( AttackInput ) then - self.inputStateHandler:switch( 'movement' ) - else - self.inputStateHandler:switch( 'attack' ) - end - elseif scancode == 'e' then - character:clearActions() - if self.inputStateHandler:getState():isInstanceOf( InteractionInput ) then - self.inputStateHandler:switch( 'movement' ) - else - self.inputStateHandler:switch( 'interaction' ) - end - elseif scancode == 'm' then - character:clearActions() - self.inputStateHandler:switch( 'movement' ) - elseif scancode == 'space' then - self.inputStateHandler:switch( 'movement' ) - self.factions:getFaction():nextCharacter() - elseif scancode == 'backspace' then - self.inputStateHandler:switch( 'movement' ) - self.factions:getFaction():prevCharacter() - elseif scancode == 'return' then - self.inputStateHandler:switch( 'movement' ) - self.factions:nextFaction() - elseif scancode == 'i' then - character:enqueueAction( OpenInventory( character, character:getTile() )) + if handleCharacterActions( input, character ) then self.stateManager:push( 'execution', self.factions, character ) - elseif scancode == 'h' then - ScreenManager.push( 'health', character ) + return + end + + if handleInputSelectionActions( input, self.inputStateHandler, character ) then + return + end + + if handleOtherActions( input, self.stateManager, self.inputStateHandler, self.factions, character ) then + return end end From 9cd5fdaf57b4dfed36188b3aa99bae3037ef02a2 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 11 Dec 2017 04:32:45 +0100 Subject: [PATCH 069/178] Use keybindings from settings on health screen This is part of an effort to allow customizable controls (See #76). --- src/Settings.lua | 8 +++++++ src/ui/screens/HelpScreen.lua | 43 +++++++++++++++++------------------ 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/src/Settings.lua b/src/Settings.lua index 63e4730f..c0fa32cf 100644 --- a/src/Settings.lua +++ b/src/Settings.lua @@ -118,6 +118,14 @@ function Settings.getIngameEditor() return settings.general.mapeditor end +function Settings.getKeybinding( saction ) + for scancode, action in pairs( settings.controls ) do + if action == saction then + return love.keyboard.getKeyFromScancode( scancode ) + end + end +end + function Settings.getLocale() return settings.general.locale end diff --git a/src/ui/screens/HelpScreen.lua b/src/ui/screens/HelpScreen.lua index 04571eca..6560ec03 100644 --- a/src/ui/screens/HelpScreen.lua +++ b/src/ui/screens/HelpScreen.lua @@ -5,6 +5,7 @@ local GridHelper = require( 'src.util.GridHelper' ) local UIBackground = require( 'src.ui.elements.UIBackground' ) local UIOutlines = require( 'src.ui.elements.UIOutlines' ) local Translator = require( 'src.util.Translator' ) +local Settings = require( 'src.Settings' ) -- ------------------------------------------------ -- Module @@ -25,45 +26,38 @@ local HELP_TEXT = { color = 'ui_help_section', children = { 'NOTE: Characters can also be selected by right-clicking on them!', - 'backspace - Select previous character', - 'space - Select next character', - 'return - End turn', - 'i - Open inventory', - 'h - Open health panel' + { 'prev_character', 'Select previous character' }, + { 'next_character', 'Select next character' }, + { 'end_turn', 'End turn' }, + { 'open_inventory_screen', 'Open inventory' }, + { 'open_health_screen', 'Open health panel' } } }, { text = 'WEAPONS', color = 'ui_help_section', children = { - 'left - select previous firing mode', - 'right - select next firing mode', - 'r - reload current weapon' + { 'next_weapon_mode', 'Select previous firing mode' }, + { 'prev_weapon_mode', 'Select next firing mode' }, + { 'action_reload_weapon', 'Reload current weapon' } } }, { text = 'STANCES', color = 'ui_help_section', children = { - 's - change stance to Stand', - 'c - change stance to Crouch', - 'p - change stance to Prone' + { 'action_stand', 'Change stance to Stand' }, + { 'action_crouch', 'Change stance to Crouch' }, + { 'action_prone', 'Change stance to Prone' } } }, { text = 'INPUT', color = 'ui_help_section', children = { - 'a - Switch to Attack Mode', - 'm - Switch to Movement Mode', - 'e - Switch to Interaction Mode (e.g. to open barrels or doors)' - } - }, - { - text = 'MISC', - color = 'ui_help_section', - children = { - 'f - Switch between windowed and fullscreen modes' + { 'attack_mode', 'Switch to Attack Mode' }, + { 'movement_mode', 'Switch to Movement Mode' }, + { 'interaction_mode', 'Switch to Interaction Mode (e.g. to open barrels or doors)' } } } } @@ -89,7 +83,12 @@ local function assembleText() -- Draw sub items with a slight offset to the right. for j = 1, #HELP_TEXT[i].children do offset = offset + 1 - text:addf( HELP_TEXT[i].children[j], (UI_GRID_WIDTH-2) * tw, 'left', 4*tw, offset * th ) + 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( HELP_TEXT[i].children[j][2], (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 ) + end end offset = offset + 2 From 9f1cfae8948a8c106e93dbaaef56b4d3ac546de9 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 11 Dec 2017 14:58:18 +0100 Subject: [PATCH 070/178] Fix options wrongly warning about changed values Fixes #220. --- src/Settings.lua | 43 +++++++++++++++++++++---------------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/src/Settings.lua b/src/Settings.lua index c0fa32cf..206aca66 100644 --- a/src/Settings.lua +++ b/src/Settings.lua @@ -49,7 +49,6 @@ local DEFAULT_SETTINGS = { -- ------------------------------------------------ local settings -local changed -- ------------------------------------------------ -- Private Functions @@ -63,18 +62,6 @@ local function create() Settings.save() end ---- --- Sets the changed variable to true if the new value differs from the old. --- @tparam ... old The old value to check. --- @tparam ... new The new value to check. --- @tparam ... The new value to apply. -local function changeValue( old, new ) - if old ~= new then - changed = true - end - return new -end - -- ------------------------------------------------ -- Public Functions -- ------------------------------------------------ @@ -83,7 +70,6 @@ end -- Saves the settings to a file and resets the changed variable. -- function Settings.save() - changed = false Compressor.save( settings, FILE_NAME ) end @@ -135,28 +121,41 @@ function Settings.getTexturepack() return settings.general.texturepack end +--- +-- Compares the current settings to the old settings and checks if any of the +-- values have changed. +-- @treturn boolean True if one or more values have changed. +-- +function Settings.hasChanged() + local oldSettings = Compressor.load( FILE_NAME ) + for section, content in pairs( oldSettings ) do + for key, value in pairs( content ) do + if settings[section][key] ~= value then + return true + end + end + end + return false +end + -- ------------------------------------------------ -- Setters -- ------------------------------------------------ function Settings.setFullscreen( nfullscreen ) - settings.general.fullscreen = changeValue( settings.general.fullscreen, nfullscreen ) + settings.general.fullscreen = nfullscreen end function Settings.setIngameEditor( mapeditor ) - settings.general.mapeditor = changeValue( settings.general.mapeditor, mapeditor ) + settings.general.mapeditor = mapeditor end function Settings.setLocale( nlocale ) - settings.general.locale = changeValue( settings.general.locale, nlocale ) + settings.general.locale = nlocale end function Settings.setTexturepack( ntexturepack ) - settings.general.texturepack = changeValue( settings.general.texturepack, ntexturepack ) -end - -function Settings.hasChanged() - return changed + settings.general.texturepack = ntexturepack end return Settings From 7a8307d32b4bf4be556e757c246fca577d37a9a2 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 11 Dec 2017 15:02:17 +0100 Subject: [PATCH 071/178] Use versioning for settings file This allows us to replace outdated settings files on the user's system. The version of a settings file should change if there have been breaking changes (renamed settings keys, newly added options ...). --- src/Settings.lua | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Settings.lua b/src/Settings.lua index 206aca66..340b0fdd 100644 --- a/src/Settings.lua +++ b/src/Settings.lua @@ -7,6 +7,7 @@ -- ------------------------------------------------ local Compressor = require( 'src.util.Compressor' ) +local Log = require( 'src.util.Log' ) -- ------------------------------------------------ -- Module @@ -20,6 +21,7 @@ local Settings = {} local FILE_NAME = 'settings.otr' local DEFAULT_SETTINGS = { + version = 1, general = { fullscreen = true, locale = 'en_EN', @@ -80,6 +82,9 @@ function Settings.load() -- Create settings file if it doesn't exist yet. if not love.filesystem.isFile( FILE_NAME ) then create() + elseif Compressor.load( FILE_NAME ).version ~= DEFAULT_SETTINGS.version then + Log.warn( 'Detected outdated settings file => Replacing with default settings!', 'Settings' ) + create() end settings = Compressor.load( FILE_NAME ) From c12a3041a7aba734f52136a77af544a61a1d25aa Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 11 Dec 2017 19:31:00 +0100 Subject: [PATCH 072/178] Fix crash in settings screen Caused by trying to loop over a numerical value since the version key in the settings table was changed to contain a number instead of a table. --- src/Settings.lua | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Settings.lua b/src/Settings.lua index 340b0fdd..c2f543db 100644 --- a/src/Settings.lua +++ b/src/Settings.lua @@ -134,8 +134,14 @@ end function Settings.hasChanged() local oldSettings = Compressor.load( FILE_NAME ) for section, content in pairs( oldSettings ) do - for key, value in pairs( content ) do - if settings[section][key] ~= value then + 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 return true end end From 5a83f5dca3a24eab4e47376bcdd460ef2dda0fcf Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 11 Dec 2017 19:51:35 +0100 Subject: [PATCH 073/178] Add section to options that allows rebinding keys From the options screen the player can now access the keybinding screen which allows the rebinding of the game's controls to different keys. Closes #76. --- main.lua | 4 +- res/text/en_EN/ui_text.lua | 18 +++ src/Settings.lua | 11 ++ src/ui/screens/KeybindingModal.lua | 104 ++++++++++++++ src/ui/screens/KeybindingScreen.lua | 211 ++++++++++++++++++++++++++++ src/ui/screens/OptionsScreen.lua | 17 +++ 6 files changed, 364 insertions(+), 1 deletion(-) create mode 100644 src/ui/screens/KeybindingModal.lua create mode 100644 src/ui/screens/KeybindingScreen.lua diff --git a/main.lua b/main.lua index 4126a464..48921c7e 100644 --- a/main.lua +++ b/main.lua @@ -77,7 +77,9 @@ function love.load( args ) mapeditormenu = require( 'src.ui.screens.MapEditorMenu' ), prefabeditor = require( 'src.ui.screens.PrefabEditor' ), prefabeditormenu = require( 'src.ui.screens.PrefabEditorMenu' ), - editorloading = require( 'src.ui.mapeditor.EditorLoadingScreen' ) + editorloading = require( 'src.ui.mapeditor.EditorLoadingScreen' ), + keybindingeditor = require( 'src.ui.screens.KeybindingScreen' ), + keybindingmodal = require( 'src.ui.screens.KeybindingModal' ), } ScreenManager.init( screens, 'bootloading' ) diff --git a/res/text/en_EN/ui_text.lua b/res/text/en_EN/ui_text.lua index 9f2a9249..80f61123 100644 --- a/res/text/en_EN/ui_text.lua +++ b/res/text/en_EN/ui_text.lua @@ -35,6 +35,24 @@ locale.strings = { ['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_keybindings'] = "Edit Keybindings", + + -- Keybindings + ['action_stand'] = "Stand", + ['action_crouch'] = "Crouch", + ['action_prone'] = "Prone", + ['action_reload_weapon'] = "Reload Weapon", + ['next_weapon_mode'] = "Next weapon mode", + ['prev_weapon_mode'] = "Previous weapon mode", + ['movement_mode'] = "Move", + ['attack_mode'] = "Attack", + ['interaction_mode'] = "Interact", + ['next_character'] = "Next character", + ['prev_character'] = "Previous character", + ['end_turn'] = "End turn", + ['open_inventory_screen'] = "Open inventory screen", + ['open_health_screen'] = "Open health screen", + ['ui_enter_key'] = "Press a key you want to asign to this action.\n\nPress escape to cancel.", -- Map Editor ['ui_mapeditor_save'] = "Save Layout", diff --git a/src/Settings.lua b/src/Settings.lua index c2f543db..3c9ab295 100644 --- a/src/Settings.lua +++ b/src/Settings.lua @@ -115,6 +115,7 @@ function Settings.getKeybinding( saction ) return love.keyboard.getKeyFromScancode( scancode ) end end + return 'unassigned' end function Settings.getLocale() @@ -169,4 +170,14 @@ function Settings.setTexturepack( ntexturepack ) settings.general.texturepack = ntexturepack end +function Settings.setKeybinding( scancode, saction ) + for oldscancode, action in pairs( settings.controls ) do + if action == saction then + settings.controls[oldscancode] = nil + settings.controls[scancode] = action + return + end + end +end + return Settings diff --git a/src/ui/screens/KeybindingModal.lua b/src/ui/screens/KeybindingModal.lua new file mode 100644 index 00000000..e28c8b95 --- /dev/null +++ b/src/ui/screens/KeybindingModal.lua @@ -0,0 +1,104 @@ +--- +-- This screen can be used to inform the user of certain events. +-- @module KeybindingModal +-- + +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ + +local ScreenManager = require( 'lib.screenmanager.ScreenManager' ) +local Screen = require( 'src.ui.screens.Screen' ) +local UIOutlines = require( 'src.ui.elements.UIOutlines' ) +local UIBackground = require( 'src.ui.elements.UIBackground' ) +local TexturePacks = require( 'src.ui.texturepacks.TexturePacks' ) +local GridHelper = require( 'src.util.GridHelper' ) +local Translator = require( 'src.util.Translator' ) + +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ + +local KeybindingModal = Screen:subclass( 'KeybindingModal' ) + +-- ------------------------------------------------ +-- Constants +-- ------------------------------------------------ + +local UI_GRID_WIDTH = 20 +local UI_GRID_HEIGHT = 10 + +-- ------------------------------------------------ +-- Private Methods +-- ------------------------------------------------ + +--- +-- Generates the outlines for this screen. +-- @tparam number x The origin of the screen along the x-axis. +-- @tparam number y The origin of the screen along the y-axis. +-- @treturn UIOutlines The newly created UIOutlines instance. +-- +local function generateOutlines( x, y ) + local outlines = UIOutlines( x, y, 0, 0, UI_GRID_WIDTH, UI_GRID_HEIGHT ) + + -- Horizontal borders. + for ox = 0, UI_GRID_WIDTH-1 do + outlines:add( ox, 0 ) -- Top + outlines:add( ox, UI_GRID_HEIGHT-1 ) -- Bottom + end + + -- Vertical outlines. + for oy = 0, UI_GRID_HEIGHT-1 do + outlines:add( 0, oy ) -- Left + outlines:add( UI_GRID_WIDTH-1, oy ) -- Right + end + + outlines:refresh() + return outlines +end + +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ + +--- +-- Initialises the KeybindingModal. +-- @tparam string action +-- +function KeybindingModal:initialize( action ) + self.action = action + + self.x, self.y = GridHelper.centerElement( UI_GRID_WIDTH, UI_GRID_HEIGHT ) + + self.background = UIBackground( self.x, self.y, 0, 0, UI_GRID_WIDTH, UI_GRID_HEIGHT ) + + self.outlines = generateOutlines( self.x, self.y ) +end + +--- +-- Draws the KeybindingModal. +-- +function KeybindingModal:draw() + self.background:draw() + self.outlines:draw() + + local tw, th = TexturePacks.getTileDimensions() + TexturePacks.setColor( 'ui_text' ) + love.graphics.printf( Translator.getText( 'ui_enter_key' ), (self.x+2) * tw, (self.y+2) * th, (UI_GRID_WIDTH-4) * tw, 'center' ) + TexturePacks.resetColor() +end + +--- +-- Handle keyreleased events. +-- +function KeybindingModal:keypressed( _, scancode ) + if scancode == 'escape' then + ScreenManager.pop() + return + end + + ScreenManager.publish( 'CHANGED_KEYBINDING', scancode, self.action ) + ScreenManager.pop() +end + +return KeybindingModal diff --git a/src/ui/screens/KeybindingScreen.lua b/src/ui/screens/KeybindingScreen.lua new file mode 100644 index 00000000..de6265d0 --- /dev/null +++ b/src/ui/screens/KeybindingScreen.lua @@ -0,0 +1,211 @@ +--- +-- @module KeybindingScreen +-- + +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ + +local ScreenManager = require( 'lib.screenmanager.ScreenManager' ) +local Screen = require( 'src.ui.screens.Screen' ) +local Settings = require( 'src.Settings' ) +local Translator = require( 'src.util.Translator' ) +local GridHelper = require( 'src.util.GridHelper' ) +local UIVerticalList = require( 'src.ui.elements.lists.UIVerticalList' ) +local UIContainer = require( 'src.ui.elements.UIContainer' ) +local UIButton = require( 'src.ui.elements.UIButton' ) +local Util = require( 'src.util.Util' ) + +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ + +local KeybindingScreen = Screen:subclass( 'KeybindingScreen' ) + +-- ------------------------------------------------ +-- Constants +-- ------------------------------------------------ + +local BUTTON_LIST_WIDTH = 20 +local BUTTON_LIST_Y = 20 + +-- ------------------------------------------------ +-- Private Methods +-- ------------------------------------------------ + +local function createKeybinding( lx, ly, action ) + -- The function to call when the button is activated. + local function callback() + ScreenManager.push( 'keybindingmodal', 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 ) + + -- 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 + + -- Create the UIButton. + return UIButton( lx, ly, 0, 0, BUTTON_LIST_WIDTH, 1, callback, text, 'left' ) +end + +--- +-- Applies the settings and saves them to a file. +-- +local function applySettings() + Settings.save() + ScreenManager.push( 'information', Translator.getText( 'ui_applied_settings' )) +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. +-- +local function createApplyButton( lx, ly ) + -- The function to call when the button is activated. + local function callback() + applySettings() + end + + -- Create the UIButton. + return UIButton( lx, ly, 0, 0, BUTTON_LIST_WIDTH, 1, callback, Translator.getText( 'ui_apply' )) +end + +--- +-- Closes the OptionsScreen and displays a confirmation dialog if any +-- settings have been changed. +-- +local function close() + if Settings.hasChanged() then + local function confirm() + ScreenManager.switch( 'options' ) + end + local function cancel() + ScreenManager.pop() + end + ScreenManager.push( 'confirm', Translator.getText( 'ui_unsaved_changes' ), confirm, cancel ) + else + ScreenManager.switch( 'options' ) + end +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. +-- +local function createBackButton( lx, ly ) + -- The function to call when the button is activated. + local function callback() + close() + end + + -- Create the UIButton. + return UIButton( lx, ly, 0, 0, BUTTON_LIST_WIDTH, 1, callback, Translator.getText( 'ui_back' )) +end + +--- +-- Creates a vertical list containing all the ui elements. +-- +local function createUIList() + local lx = GridHelper.centerElement( BUTTON_LIST_WIDTH, 1 ) + local ly = BUTTON_LIST_Y + 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( createApplyButton( lx, ly )) + buttonList:addChild( createBackButton( lx, ly )) + + return buttonList +end + +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ + +function KeybindingScreen:initialize() + self.buttonList = createUIList() + + self.container = UIContainer() + self.container:register( self.buttonList ) +end + +--- +-- Updates the OptionsScreen. +-- +function KeybindingScreen:update() + self.container:update() +end + +--- +-- Draws the OptionsScreen. +-- +function KeybindingScreen:draw() + self.container:draw() +end + +--- +-- Handle keypressed events. +-- +function KeybindingScreen:keypressed( _, scancode ) + love.mouse.setVisible( false ) + + if scancode == 'escape' then + close() + end + + if scancode == 'up' then + self.container:command( 'up' ) + elseif scancode == 'down' then + self.container:command( 'down' ) + elseif scancode == 'left' then + self.container:command( 'left' ) + elseif scancode == 'right' then + self.container:command( 'right' ) + elseif scancode == 'return' then + self.container:command( 'activate' ) + end +end + +function KeybindingScreen:mousemoved() + love.mouse.setVisible( true ) +end + +--- +-- Handle mousereleased events. +-- +function KeybindingScreen:mousereleased() + self.container:mousecommand( 'activate' ) +end + +function KeybindingScreen:receive( event, ... ) + if event == 'CHANGED_KEYBINDING' then + local action, scancode = ... + Settings.setKeybinding( action, scancode ) + self:initialize() + end +end + +return KeybindingScreen diff --git a/src/ui/screens/OptionsScreen.lua b/src/ui/screens/OptionsScreen.lua index a82f4935..6b05c787 100644 --- a/src/ui/screens/OptionsScreen.lua +++ b/src/ui/screens/OptionsScreen.lua @@ -246,6 +246,22 @@ local function createTexturePackOption( lx, ly ) return UISelectField( lx, ly, 0, 0, BUTTON_LIST_WIDTH, 1, Translator.getText( 'ui_texturepack' ), listOfValues, callback, default ) end +--- +-- Creates a UISelectField which allows the user to switch to the keybinding screen. +-- @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 createKeybindingOption( lx, ly ) + -- The function to call when the button is activated. + local function callback() + ScreenManager.switch( 'keybindingeditor' ) + end + + -- Create the UIButton. + return UIButton( lx, ly, 0, 0, BUTTON_LIST_WIDTH, 1, callback, Translator.getText( 'ui_keybindings' )) +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. @@ -291,6 +307,7 @@ local function createUIList() buttonList:addChild( createFullscreenOption( lx, ly )) buttonList:addChild( createIngameEditorOption( lx, ly )) buttonList:addChild( createTexturePackOption( lx, ly )) + buttonList:addChild( createKeybindingOption( lx, ly )) buttonList:addChild( createApplyButton( lx, ly )) buttonList:addChild( createBackButton( lx, ly )) From 8cace7d1a14ed0ec2f6af2b9281b04c22d8af304 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Tue, 12 Dec 2017 23:32:02 +0100 Subject: [PATCH 074/178] Rewrite camera module The camera module is now a middleclass class and also implements its own coordinate system modifications (removed the previously used camera library as it did a lot of redundant stuff like rotation and scaling that wasn't really necessary for OTR). --- lib/Camera.lua | 116 --------------------- src/ui/Camera.lua | 177 ++++++++++++++++++++++++++++++++ src/ui/CameraHandler.lua | 157 ---------------------------- src/ui/screens/CombatScreen.lua | 4 +- src/ui/screens/MapEditor.lua | 4 +- src/ui/screens/MapTest.lua | 4 +- src/ui/screens/PrefabEditor.lua | 14 +-- 7 files changed, 190 insertions(+), 286 deletions(-) delete mode 100644 lib/Camera.lua create mode 100644 src/ui/Camera.lua delete mode 100644 src/ui/CameraHandler.lua diff --git a/lib/Camera.lua b/lib/Camera.lua deleted file mode 100644 index 786584d8..00000000 --- a/lib/Camera.lua +++ /dev/null @@ -1,116 +0,0 @@ ---[[ -Copyright (c) 2010-2013 Matthias Richter - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -Except as contained in this notice, the name(s) of the above copyright holders -shall not be used in advertising or otherwise to promote the sale, use or -other dealings in this Software without prior written authorization. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -]]-- - -local cos, sin = math.cos, math.sin - -local camera = {} -camera.__index = camera - -local function new(x,y, zoom, rot) - x,y = x or love.graphics.getWidth()/2, y or love.graphics.getHeight()/2 - zoom = zoom or 1 - rot = rot or 0 - return setmetatable({x = x, y = y, scale = zoom, rot = rot}, camera) -end - -function camera:lookAt(x,y) - self.x, self.y = x,y - return self -end - -function camera:move(x,y) - self.x, self.y = self.x + x, self.y + y - return self -end - -function camera:pos() - return self.x, self.y -end - -function camera:rotate(phi) - self.rot = self.rot + phi - return self -end - -function camera:rotateTo(phi) - self.rot = phi - return self -end - -function camera:zoom(mul) - self.scale = self.scale * mul - return self -end - -function camera:zoomTo(zoom) - self.scale = zoom - return self -end - -function camera:attach() - local cx,cy = love.graphics.getWidth()/(2*self.scale), love.graphics.getHeight()/(2*self.scale) - love.graphics.push() - love.graphics.scale(self.scale) - love.graphics.translate(cx, cy) - love.graphics.rotate(self.rot) - love.graphics.translate(-self.x, -self.y) -end - -function camera:detach() - love.graphics.pop() -end - -function camera:draw(func) - self:attach() - func() - self:detach() -end - -function camera:cameraCoords(x,y) - -- x,y = ((x,y) - (self.x, self.y)):rotated(self.rot) * self.scale + center - local w,h = love.graphics.getWidth(), love.graphics.getHeight() - local c,s = cos(self.rot), sin(self.rot) - x,y = x - self.x, y - self.y - x,y = c*x - s*y, s*x + c*y - return x*self.scale + w/2, y*self.scale + h/2 -end - -function camera:worldCoords(x,y) - -- x,y = (((x,y) - center) / self.scale):rotated(-self.rot) + (self.x,self.y) - local w,h = love.graphics.getWidth(), love.graphics.getHeight() - local c,s = cos(-self.rot), sin(-self.rot) - x,y = (x - w/2) / self.scale, (y - h/2) / self.scale - x,y = c*x - s*y, s*x + c*y - return x+self.x, y+self.y -end - -function camera:mousepos() - return self:worldCoords(love.mouse.getPosition()) -end - --- the module -return setmetatable({new = new}, - {__call = function(_, ...) return new(...) end}) diff --git a/src/ui/Camera.lua b/src/ui/Camera.lua new file mode 100644 index 00000000..afd36e1f --- /dev/null +++ b/src/ui/Camera.lua @@ -0,0 +1,177 @@ +--- +-- @module Camera +-- + +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ + +local Class = require( 'lib.Middleclass' ) +local GridHelper = require( 'src.util.GridHelper' ) +local Util = require( 'src.util.Util' ) + +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ + +local Camera = Class( 'Camera' ) + +-- ------------------------------------------------ +-- Constants +-- ------------------------------------------------ + +local CAMERA_TRACKING_SPEED = 10 +local SCROLL_MARGIN = 15 +local SCROLL_SPEED = 10 + +-- ------------------------------------------------ +-- Private Methods +-- ------------------------------------------------ + +--- +-- Linear interpolation between a and b. +-- @tparam number a The current value. +-- @tparam number b The target value. +-- @tparam number t The time value. +-- @treturn number The interpolated value. +-- +local function lerp( a, b, t ) + return a + ( b - a ) * t +end + +--- +-- Scrolls the camera if the mouse pointer reaches the edges of the screen. +-- @tparam number tx The camera's current target position along the x-axis. +-- @tparam number ty The camera's current target position along the y-axis. +-- @tparam number mw The map's width. +-- @tparam number mh The map's height. +-- @tparam number tw The width of the tiles used for map drawing. +-- @tparam number th The height of the tiles used for map drawing. +-- +local function scroll( tx, ty, mw, tw, mh, th ) + local mx, my = love.mouse.getPosition() + + if mx < SCROLL_MARGIN then + tx = tx - SCROLL_SPEED + elseif mx > love.graphics.getWidth() - SCROLL_MARGIN then + tx = tx + SCROLL_SPEED + end + + if my < SCROLL_MARGIN then + ty = ty - SCROLL_SPEED + elseif my > love.graphics.getHeight() - SCROLL_MARGIN then + ty = ty + SCROLL_SPEED + end + + -- Clamp the camera to the map dimensions. + return Util.clamp( 0, tx, mw * tw ), Util.clamp( 0, ty, mh * th ) +end + +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ + +--- +-- Creates a new Camera instance. +-- @tparam number mw The map's width. +-- @tparam number mh The map's height. +-- @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. +-- +function Camera:initialize( mw, mh, tw, th ) + self.centerX, self.centerY = love.graphics.getWidth() * 0.5, love.graphics.getHeight() * 0.5 + + self.mw, self.mh, self.tw, self.th = mw, mh, tw, th + + -- The camera's target coordinates. + self.tx, self.ty = mw * tw * 0.5, mh * th * 0.5 + + -- The camera's current position, which will be updated each frame to move + -- towards the target coordinates. + self.cx, self.cy = self.tx, self.ty +end + +--- +-- Attaches the camera which translates the game's coordinate system. Everything +-- drawn after this will be offset according to the camera's position. +-- +function Camera:attach() + self.centerX, self.centerY = love.graphics.getWidth() * 0.5, love.graphics.getHeight() * 0.5 + + love.graphics.push() + love.graphics.translate( self.centerX - self.cx, self.centerY - self.cy ) +end + +--- +-- Detaches the camera and resets the coordinate system. +-- +function Camera:detach() + love.graphics.pop() +end + +--- +-- Updates the camera's scrolling, by lerping the camera's current position +-- to the target position. +-- @tparam number dt The time since the last frame. +-- +-- @see setTargetPosition +-- +function Camera:update( dt ) + if not self.locked then + self.tx, self.ty = scroll( self.tx, self.ty, self.mw, self.tw, self.mh, self.th ) + end + + self.cx = math.floor( lerp( self.cx, math.floor( self.tx / self.tw ) * self.tw, dt * CAMERA_TRACKING_SPEED )) + self.cy = math.floor( lerp( self.cy, math.floor( self.ty / self.th ) * self.th, dt * CAMERA_TRACKING_SPEED )) +end + +--- +-- Locks the camera to prevent scrolling. +-- +function Camera:lock() + self.locked = true +end + +--- +-- Unlocks the camera and re-enables scrolling. +-- +function Camera:unlock() + self.locked = false +end + +-- ------------------------------------------------ +-- Setters +-- ------------------------------------------------ + +--- +-- Sets the target position for the camera. This will not move the camera +-- directly. Use the update function to lerp the current camera position +-- to the target position. +-- @tparam number nx The new position along the x axis. +-- @tparam number ny The new position along the y axis. +-- +-- @see update +-- +function Camera:setTargetPosition( nx, ny ) + self.tx, self.ty = nx, ny +end + +-- ------------------------------------------------ +-- Getters +-- ------------------------------------------------ + +--- +-- Returns the mouse position on the game's grid manipulated by the camera's +-- coordinate system. Use this for any mouse interactions with the actual +-- game world (map, objects, etc.). +-- @treturn number The mouse cursor's position along the x-axis. +-- @treturn number The mouse cursor's position along the y-axis. +-- +function Camera:getMouseWorldGridPosition() + local mx, my = love.mouse.getPosition() + mx, my = ( mx - self.centerX ) + self.cx, ( my - self.centerY ) + self.cy + return GridHelper.pixelsToGrid( mx, my ) +end + +return Camera diff --git a/src/ui/CameraHandler.lua b/src/ui/CameraHandler.lua deleted file mode 100644 index 602bbe8c..00000000 --- a/src/ui/CameraHandler.lua +++ /dev/null @@ -1,157 +0,0 @@ ---- --- @module CameraHandler --- - --- ------------------------------------------------ --- Required Modules --- ------------------------------------------------ - -local Camera = require( 'lib.Camera' ) -local GridHelper = require( 'src.util.GridHelper' ) - --- ------------------------------------------------ --- Module --- ------------------------------------------------ - -local CameraHandler = {} - --- ------------------------------------------------ --- Constants --- ------------------------------------------------ - -local CAMERA_TRACKING_SPEED = 10 -local SCROLL_MARGIN = 15 -local SCROLL_SPEED = 10 - --- ------------------------------------------------ --- Constructor --- ------------------------------------------------ - ---- --- Creates a new CameraHandler instance. --- @tparam number mw The map's width. --- @tparam number mh The map's height. --- @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 CameraHandler A new instance of the CameraHandler class. --- -function CameraHandler.new( mw, mh, tw, th ) - local self = Camera.new() - - -- ------------------------------------------------ - -- Private Attributes - -- ------------------------------------------------ - - local px, py = mw * tw * 0.5, mh * th * 0.5 - local tx, ty = px, py - local locked - - -- ------------------------------------------------ - -- Private Methods - -- ------------------------------------------------ - - --- - -- Linear interpolation between a and b. - -- @tparam number a The current value. - -- @tparam number b The target value. - -- @tparam number t The time value. - -- @treturn number The interpolated value. - -- - local function lerp( a, b, t ) - return a + ( b - a ) * t - end - - --- - -- Scrolls the camera if the mouse pointer reaches the edges of the screen. - -- - local function scroll() - local mx, my = love.mouse.getPosition() - local x, y = tx, ty - - if mx < SCROLL_MARGIN then - x = x - SCROLL_SPEED - elseif mx > love.graphics.getWidth() - SCROLL_MARGIN then - x = x + SCROLL_SPEED - end - - if my < SCROLL_MARGIN then - y = y - SCROLL_SPEED - elseif my > love.graphics.getHeight() - SCROLL_MARGIN then - y = y + SCROLL_SPEED - end - - -- Clamp the camera to the map dimensions. - tx = math.max( 0, math.min( x, mw * tw )) - ty = math.max( 0, math.min( y, mh * th )) - end - - -- ------------------------------------------------ - -- Public Methods - -- ------------------------------------------------ - - --- - -- Updates the camera's scrolling, by lerping the camera's current position - -- to the target position. - -- @tparam number dt The time since the last frame. - -- - -- @see setTargetPosition - -- - function self:update( dt ) - if not locked then - scroll() - end - px = lerp( px, math.floor( tx / tw ) * tw, dt * CAMERA_TRACKING_SPEED ) - py = lerp( py, math.floor( ty / th ) * th, dt * CAMERA_TRACKING_SPEED ) - self:lookAt( math.floor( px ), math.floor( py )) - end - - --- - -- Locks the camera to prevent scrolling. - -- - function self:lock() - locked = true - end - - --- - -- Unlocks the camera and re-enables scrolling. - -- - function self:unlock() - locked = false - end - - -- ------------------------------------------------ - -- Setters - -- ------------------------------------------------ - - --- - -- Sets the target position for the camera. This will not move the camera - -- directly. Use the update function to lerp the current camera position - -- to the target position. - -- @tparam number The new position along the x axis. - -- @tparam number The new position along the y axis. - -- - -- @see update - -- - function self:setTargetPosition( dx, dy ) - tx, ty = dx, dy - end - - -- ------------------------------------------------ - -- Getters - -- ------------------------------------------------ - - --- - -- Returns the mouse position on the game's grid manipulated by the camera's - -- coordinate system. Use this for any mouse interactions with the actual - -- game world (map, objects, etc.). - -- @treturn number The mouse cursor's position along the x-axis. - -- @treturn number The mouse cursor's position along the y-axis. - -- - function self:getMouseWorldGridPosition() - return GridHelper.pixelsToGrid( self:mousepos() ) - end - - return self -end - -return CameraHandler diff --git a/src/ui/screens/CombatScreen.lua b/src/ui/screens/CombatScreen.lua index bafa9764..e67f0662 100644 --- a/src/ui/screens/CombatScreen.lua +++ b/src/ui/screens/CombatScreen.lua @@ -6,7 +6,7 @@ local ScreenManager = require( 'lib.screenmanager.ScreenManager' ) local Screen = require( 'src.ui.screens.Screen' ) local CombatState = require( 'src.CombatState' ) local MapPainter = require( 'src.ui.MapPainter' ) -local CameraHandler = require('src.ui.CameraHandler') +local Camera = require( 'src.ui.Camera' ) local UserInterface = require( 'src.ui.UserInterface' ) local OverlayPainter = require( 'src.ui.overlays.OverlayPainter' ) local Messenger = require( 'src.Messenger' ) @@ -32,7 +32,7 @@ function CombatScreen:initialize( playerFaction, savegame ) local tw, th = TexturePacks.getTileDimensions() local mw, mh = self.combatState:getMap():getDimensions() - self.camera = CameraHandler.new( mw, mh, tw, th ) + self.camera = Camera( mw, mh, tw, th ) self.userInterface = UserInterface.new( self.combatState, self.camera ) self.overlayPainter = OverlayPainter.new( self.combatState, self.camera ) diff --git a/src/ui/screens/MapEditor.lua b/src/ui/screens/MapEditor.lua index 874d18c3..db36d5a2 100644 --- a/src/ui/screens/MapEditor.lua +++ b/src/ui/screens/MapEditor.lua @@ -10,7 +10,7 @@ local Screen = require( 'src.ui.screens.Screen' ) local ScreenManager = require( 'lib.screenmanager.ScreenManager' ) local UIVerticalList = require( 'src.ui.elements.lists.UIVerticalList' ) local UIButton = require( 'src.ui.elements.UIButton' ) -local CameraHandler = require('src.ui.CameraHandler') +local Camera = require( 'src.ui.Camera' ) local LayoutCanvas = require( 'src.ui.mapeditor.LayoutCanvas' ) local LayoutBrush = require( 'src.ui.mapeditor.LayoutBrush' ) local UIContainer = require( 'src.ui.elements.UIContainer' ) @@ -80,7 +80,7 @@ function MapEditor:initialize() self.prefabSelector = createPrefabSelector( self.brush ) - self.camera = CameraHandler.new( self.canvas:getWidth(), self.canvas:getHeight(), 16, 16 ) + self.camera = Camera( self.canvas:getWidth(), self.canvas:getHeight(), 16, 16 ) self.uiContainer = UIContainer() self.uiContainer:register( self.prefabSelector ) diff --git a/src/ui/screens/MapTest.lua b/src/ui/screens/MapTest.lua index 80c2e8cf..c8c933d9 100644 --- a/src/ui/screens/MapTest.lua +++ b/src/ui/screens/MapTest.lua @@ -9,7 +9,7 @@ local Screen = require( 'src.ui.screens.Screen' ) local ScreenManager = require( 'lib.screenmanager.ScreenManager' ) local MapPainter = require( 'src.ui.MapPainter' ) -local CameraHandler = require('src.ui.CameraHandler') +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' ) @@ -48,7 +48,7 @@ function MapTest:initialize( layout ) self.map, self.mw, self.mh = createMap( layout ) self.mapPainter = MapPainter( self.map ) - self.camera = CameraHandler.new( self.mw, self.mh, TexturePacks.getTileDimensions() ) + self.camera = Camera( self.mw, self.mh, TexturePacks.getTileDimensions() ) end function MapTest:draw() diff --git a/src/ui/screens/PrefabEditor.lua b/src/ui/screens/PrefabEditor.lua index 218ddef2..6be05c45 100644 --- a/src/ui/screens/PrefabEditor.lua +++ b/src/ui/screens/PrefabEditor.lua @@ -13,7 +13,7 @@ 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 CameraHandler = require('src.ui.CameraHandler') +local Camera = require( 'src.ui.Camera' ) local Translator = require( 'src.util.Translator' ) local PrefabCanvas = require( 'src.ui.mapeditor.PrefabCanvas' ) local PrefabBrush = require( 'src.ui.mapeditor.PrefabBrush' ) @@ -91,7 +91,7 @@ function PrefabEditor:initialize() self.canvas = PrefabCanvas( 'XS' ) - self.camera = CameraHandler.new( self.canvas:getWidth(), self.canvas:getHeight(), TexturePacks.getTileDimensions() ) + self.camera = Camera( self.canvas:getWidth(), self.canvas:getHeight(), TexturePacks.getTileDimensions() ) self.tool = PrefabBrush() @@ -168,19 +168,19 @@ function PrefabEditor:keypressed( _, scancode ) if scancode == '1' then self.canvas:setSize( 'XS' ) - self.camera = CameraHandler.new( self.canvas:getWidth(), self.canvas:getHeight(), TexturePacks.getTileDimensions() ) + self.camera = Camera( self.canvas:getWidth(), self.canvas:getHeight(), TexturePacks.getTileDimensions() ) elseif scancode == '2' then self.canvas:setSize( 'S' ) - self.camera = CameraHandler.new( self.canvas:getWidth(), self.canvas:getHeight(), TexturePacks.getTileDimensions() ) + self.camera = Camera( self.canvas:getWidth(), self.canvas:getHeight(), TexturePacks.getTileDimensions() ) elseif scancode == '3' then self.canvas:setSize( 'M' ) - self.camera = CameraHandler.new( self.canvas:getWidth(), self.canvas:getHeight(), TexturePacks.getTileDimensions() ) + self.camera = Camera( self.canvas:getWidth(), self.canvas:getHeight(), TexturePacks.getTileDimensions() ) elseif scancode == '4' then self.canvas:setSize( 'L' ) - self.camera = CameraHandler.new( self.canvas:getWidth(), self.canvas:getHeight(), TexturePacks.getTileDimensions() ) + self.camera = Camera( self.canvas:getWidth(), self.canvas:getHeight(), TexturePacks.getTileDimensions() ) elseif scancode == '5' then self.canvas:setSize( 'XL' ) - self.camera = CameraHandler.new( self.canvas:getWidth(), self.canvas:getHeight(), TexturePacks.getTileDimensions() ) + self.camera = Camera( self.canvas:getWidth(), self.canvas:getHeight(), TexturePacks.getTileDimensions() ) end if scancode == 'h' then From 2afbceb70c3329d52fdc3d9c572d7cad63673e1c Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Tue, 12 Dec 2017 23:41:53 +0100 Subject: [PATCH 075/178] Allow key scrolling via keyboard input The user can now use the arrow keys to move the camera around. --- src/ui/Camera.lua | 83 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 71 insertions(+), 12 deletions(-) diff --git a/src/ui/Camera.lua b/src/ui/Camera.lua index afd36e1f..5ce6ba06 100644 --- a/src/ui/Camera.lua +++ b/src/ui/Camera.lua @@ -41,30 +41,85 @@ end --- -- Scrolls the camera if the mouse pointer reaches the edges of the screen. +-- @tparam string dir The direction in which to scroll the camera. +-- @tparam number tx The camera's current target position along the x-axis. +-- @tparam number ty The camera's current target position along the y-axis. +-- @tparam number mw The map's width. +-- @tparam number mh The map's height. +-- @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 number The new target position along the x-axis. +-- @treturn number The new target position along the x-axis. +local function scroll( dir, tx, ty, mw, tw, mh, th ) + if dir == 'left' then + tx = tx - SCROLL_SPEED + elseif dir == 'right' then + tx = tx + SCROLL_SPEED + end + + if dir == 'up' then + ty = ty - SCROLL_SPEED + elseif dir == 'down' then + ty = ty + SCROLL_SPEED + end + + -- Clamp the camera to the map dimensions. + return Util.clamp( 0, tx, mw * tw ), Util.clamp( 0, ty, mh * th ) +end + +--- +-- Checks if the mouse is close to the edges of the screens and scrolls the +-- mouse accordingly. -- @tparam number tx The camera's current target position along the x-axis. -- @tparam number ty The camera's current target position along the y-axis. -- @tparam number mw The map's width. -- @tparam number mh The map's height. -- @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 number The new target position along the x-axis. +-- @treturn number The new target position along the x-axis. -- -local function scroll( tx, ty, mw, tw, mh, th ) +local function handleMouseScrolling( tx, ty, mw, tw, mh, th ) local mx, my = love.mouse.getPosition() - if mx < SCROLL_MARGIN then - tx = tx - SCROLL_SPEED + return scroll( 'left', tx, ty, mw, tw, mh, th ) elseif mx > love.graphics.getWidth() - SCROLL_MARGIN then - tx = tx + SCROLL_SPEED + return scroll( 'right', tx, ty, mw, tw, mh, th ) end if my < SCROLL_MARGIN then - ty = ty - SCROLL_SPEED + return scroll( 'up', tx, ty, mw, tw, mh, th ) elseif my > love.graphics.getHeight() - SCROLL_MARGIN then - ty = ty + SCROLL_SPEED + return scroll( 'down', tx, ty, mw, tw, mh, th ) end - -- Clamp the camera to the map dimensions. - return Util.clamp( 0, tx, mw * tw ), Util.clamp( 0, ty, mh * th ) + return tx, ty +end + +--- +-- Scrolls the camera based on keyboard input. +-- @tparam number tx The camera's current target position along the x-axis. +-- @tparam number ty The camera's current target position along the y-axis. +-- @tparam number mw The map's width. +-- @tparam number mh The map's height. +-- @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 number The new target position along the x-axis. +-- @treturn number The new target position along the x-axis. +-- +local function handleKeyboardScrolling( tx, ty, mw, tw, mh, th ) + if love.keyboard.isScancodeDown( 'left' ) then + return scroll( 'left', tx, ty, mw, tw, mh, th ) + elseif love.keyboard.isScancodeDown( 'right' ) then + return scroll( 'right', tx, ty, mw, tw, mh, th ) + end + if love.keyboard.isScancodeDown( 'up' ) then + return scroll( 'up', tx, ty, mw, tw, mh, th ) + elseif love.keyboard.isScancodeDown( 'down' ) then + return scroll( 'down', tx, ty, mw, tw, mh, th ) + end + + return tx, ty end -- ------------------------------------------------ @@ -118,12 +173,16 @@ end -- @see setTargetPosition -- function Camera:update( dt ) - if not self.locked then - self.tx, self.ty = scroll( self.tx, self.ty, self.mw, self.tw, self.mh, self.th ) - end - self.cx = math.floor( lerp( self.cx, math.floor( self.tx / self.tw ) * self.tw, dt * CAMERA_TRACKING_SPEED )) self.cy = math.floor( lerp( self.cy, math.floor( self.ty / self.th ) * self.th, dt * CAMERA_TRACKING_SPEED )) + + -- If the camera is locked we don't check for any scrolling input. + if self.locked then + return + end + + self.tx, self.ty = handleMouseScrolling( self.tx, self.ty, self.mw, self.tw, self.mh, self.th ) + self.tx, self.ty = handleKeyboardScrolling( self.tx, self.ty, self.mw, self.tw, self.mh, self.th ) end --- From d7ba1db9b0146ad37bf8fa96ef0c9ffdd569bc67 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Wed, 13 Dec 2017 00:06:33 +0100 Subject: [PATCH 076/178] Add keybinding configuration for camera controls --- main.lua | 4 +++ res/text/en_EN/ui_text.lua | 4 +++ src/Settings.lua | 6 ++++- src/ui/Camera.lua | 42 +++++++++++++++++++---------- src/ui/screens/CombatScreen.lua | 6 +++++ src/ui/screens/KeybindingScreen.lua | 4 +++ 6 files changed, 51 insertions(+), 15 deletions(-) diff --git a/main.lua b/main.lua index 48921c7e..6f0d5624 100644 --- a/main.lua +++ b/main.lua @@ -109,6 +109,10 @@ function love.keypressed( key, scancode, isrepeat ) ScreenManager.keypressed( key, scancode, isrepeat ) end +function love.keyreleased( key, scancode ) + ScreenManager.keyreleased( key, scancode ) +end + function love.resize( w, h ) ScreenManager.resize( w, h ) end diff --git a/res/text/en_EN/ui_text.lua b/res/text/en_EN/ui_text.lua index 80f61123..31b768c3 100644 --- a/res/text/en_EN/ui_text.lua +++ b/res/text/en_EN/ui_text.lua @@ -52,6 +52,10 @@ locale.strings = { ['end_turn'] = "End turn", ['open_inventory_screen'] = "Open inventory screen", ['open_health_screen'] = "Open health screen", + ['pan_camera_left'] = "Move camera left", + ['pan_camera_right'] = "Move camera right", + ['pan_camera_up'] = "Move camera up", + ['pan_camera_down'] = "Move camera down", ['ui_enter_key'] = "Press a key you want to asign to this action.\n\nPress escape to cancel.", -- Map Editor diff --git a/src/Settings.lua b/src/Settings.lua index 3c9ab295..e258ef87 100644 --- a/src/Settings.lua +++ b/src/Settings.lua @@ -21,7 +21,7 @@ local Settings = {} local FILE_NAME = 'settings.otr' local DEFAULT_SETTINGS = { - version = 1, + version = 2, general = { fullscreen = true, locale = 'en_EN', @@ -43,6 +43,10 @@ local DEFAULT_SETTINGS = { ['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' } } diff --git a/src/ui/Camera.lua b/src/ui/Camera.lua index 5ce6ba06..75906f08 100644 --- a/src/ui/Camera.lua +++ b/src/ui/Camera.lua @@ -98,24 +98,25 @@ end --- -- Scrolls the camera based on keyboard input. --- @tparam number tx The camera's current target position along the x-axis. --- @tparam number ty The camera's current target position along the y-axis. --- @tparam number mw The map's width. --- @tparam number mh The map's height. --- @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 number The new target position along the x-axis. --- @treturn number The new target position along the x-axis. +-- @tparam table controls A table containing camera controls. +-- @tparam number tx The camera's current target position along the x-axis. +-- @tparam number ty The camera's current target position along the y-axis. +-- @tparam number mw The map's width. +-- @tparam number mh The map's height. +-- @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 number The new target position along the x-axis. +-- @treturn number The new target position along the x-axis. -- -local function handleKeyboardScrolling( tx, ty, mw, tw, mh, th ) - if love.keyboard.isScancodeDown( 'left' ) then +local function handleKeyboardScrolling( controls, tx, ty, mw, tw, mh, th ) + if controls.pan_camera_left then return scroll( 'left', tx, ty, mw, tw, mh, th ) - elseif love.keyboard.isScancodeDown( 'right' ) then + elseif controls.pan_camera_right then return scroll( 'right', tx, ty, mw, tw, mh, th ) end - if love.keyboard.isScancodeDown( 'up' ) then + if controls.pan_camera_up then return scroll( 'up', tx, ty, mw, tw, mh, th ) - elseif love.keyboard.isScancodeDown( 'down' ) then + elseif controls.pan_camera_down then return scroll( 'down', tx, ty, mw, tw, mh, th ) end @@ -136,6 +137,7 @@ end -- function Camera:initialize( mw, mh, tw, th ) self.centerX, self.centerY = love.graphics.getWidth() * 0.5, love.graphics.getHeight() * 0.5 + self.controls = {} self.mw, self.mh, self.tw, self.th = mw, mh, tw, th @@ -182,7 +184,7 @@ function Camera:update( dt ) end self.tx, self.ty = handleMouseScrolling( self.tx, self.ty, self.mw, self.tw, self.mh, self.th ) - self.tx, self.ty = handleKeyboardScrolling( self.tx, self.ty, self.mw, self.tw, self.mh, self.th ) + self.tx, self.ty = handleKeyboardScrolling( self.controls, self.tx, self.ty, self.mw, self.tw, self.mh, self.th ) end --- @@ -199,6 +201,18 @@ function Camera:unlock() self.locked = false end +function Camera:input( action, pressed ) + if action == 'pan_camera_left' then + self.controls[action] = pressed + elseif action == 'pan_camera_right' then + self.controls[action] = pressed + elseif action == 'pan_camera_up' then + self.controls[action] = pressed + elseif action == 'pan_camera_down' then + self.controls[action] = pressed + end +end + -- ------------------------------------------------ -- Setters -- ------------------------------------------------ diff --git a/src/ui/screens/CombatScreen.lua b/src/ui/screens/CombatScreen.lua index e67f0662..be0949b2 100644 --- a/src/ui/screens/CombatScreen.lua +++ b/src/ui/screens/CombatScreen.lua @@ -11,6 +11,7 @@ local UserInterface = require( 'src.ui.UserInterface' ) local OverlayPainter = require( 'src.ui.overlays.OverlayPainter' ) local Messenger = require( 'src.Messenger' ) local TexturePacks = require( 'src.ui.texturepacks.TexturePacks' ) +local Settings = require( 'src.Settings' ) -- ------------------------------------------------ -- Module @@ -85,6 +86,11 @@ function CombatScreen:keypressed( key, scancode, isrepeat ) end self.combatState:keypressed( key, scancode, isrepeat ) + self.camera:input( Settings.mapInput( scancode ), true ) +end + +function CombatScreen:keyreleased( _, scancode ) + self.camera:input( Settings.mapInput( scancode ), false ) end function CombatScreen:mousepressed( _, _, button ) diff --git a/src/ui/screens/KeybindingScreen.lua b/src/ui/screens/KeybindingScreen.lua index de6265d0..7cd1a405 100644 --- a/src/ui/screens/KeybindingScreen.lua +++ b/src/ui/screens/KeybindingScreen.lua @@ -135,6 +135,10 @@ local function createUIList() 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( createApplyButton( lx, ly )) buttonList:addChild( createBackButton( lx, ly )) From 2adf5f6d6331d00729b7c7060bd2cea076f06e17 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Wed, 13 Dec 2017 00:26:19 +0100 Subject: [PATCH 077/178] Add option for mouse panning of camera Closes #184. --- res/text/en_EN/ui_text.lua | 1 + src/Settings.lua | 12 +++++++++-- src/ui/Camera.lua | 6 +++++- src/ui/screens/OptionsScreen.lua | 34 ++++++++++++++++++++++++++++++++ 4 files changed, 50 insertions(+), 3 deletions(-) diff --git a/res/text/en_EN/ui_text.lua b/res/text/en_EN/ui_text.lua index 31b768c3..c1e1c4a2 100644 --- a/res/text/en_EN/ui_text.lua +++ b/res/text/en_EN/ui_text.lua @@ -35,6 +35,7 @@ locale.strings = { ['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_keybindings'] = "Edit Keybindings", -- Keybindings diff --git a/src/Settings.lua b/src/Settings.lua index e258ef87..dbeedcce 100644 --- a/src/Settings.lua +++ b/src/Settings.lua @@ -21,12 +21,13 @@ local Settings = {} local FILE_NAME = 'settings.otr' local DEFAULT_SETTINGS = { - version = 2, + version = 3, general = { fullscreen = true, locale = 'en_EN', mapeditor = false, - texturepack = 'default' + texturepack = 'default', + mousepanning = false }, controls = { ['x'] = 'action_stand', @@ -126,6 +127,9 @@ function Settings.getLocale() return settings.general.locale end +function Settings.getMousePanning() + return settings.general.mousepanning +end function Settings.getTexturepack() return settings.general.texturepack @@ -170,6 +174,10 @@ function Settings.setLocale( nlocale ) settings.general.locale = nlocale end +function Settings.setMousePanning( mousepanning ) + settings.general.mousepanning = mousepanning +end + function Settings.setTexturepack( ntexturepack ) settings.general.texturepack = ntexturepack end diff --git a/src/ui/Camera.lua b/src/ui/Camera.lua index 75906f08..41025f74 100644 --- a/src/ui/Camera.lua +++ b/src/ui/Camera.lua @@ -9,6 +9,7 @@ local Class = require( 'lib.Middleclass' ) local GridHelper = require( 'src.util.GridHelper' ) local Util = require( 'src.util.Util' ) +local Settings = require( 'src.Settings' ) -- ------------------------------------------------ -- Module @@ -183,7 +184,10 @@ function Camera:update( dt ) return end - self.tx, self.ty = handleMouseScrolling( self.tx, self.ty, self.mw, self.tw, self.mh, self.th ) + if Settings.getMousePanning() then + self.tx, self.ty = handleMouseScrolling( self.tx, self.ty, self.mw, self.tw, self.mh, self.th ) + end + self.tx, self.ty = handleKeyboardScrolling( self.controls, self.tx, self.ty, self.mw, self.tw, self.mh, self.th ) end diff --git a/src/ui/screens/OptionsScreen.lua b/src/ui/screens/OptionsScreen.lua index 6b05c787..3075b176 100644 --- a/src/ui/screens/OptionsScreen.lua +++ b/src/ui/screens/OptionsScreen.lua @@ -246,6 +246,39 @@ local function createTexturePackOption( lx, ly ) return UISelectField( lx, ly, 0, 0, BUTTON_LIST_WIDTH, 1, Translator.getText( 'ui_texturepack' ), listOfValues, callback, default ) end + +--- +-- Creates a UISelectField which allows the user to activate mouse panning. +-- @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 UISelectField The newly created UISelectField. +-- +local function createMousePanningOption( lx, ly ) + -- The list of values to display. + local listOfValues = { + { displayTextID = Translator.getText( 'ui_on' ), value = true }, + { displayTextID = Translator.getText( 'ui_off' ), value = false } + } + + -- The function to call when the value of the UISelectField changes. + local function callback( val ) + Settings.setMousePanning( val ) + end + + -- Search the value corresponding to the currently selected option or + -- take the first one and make it the current display value. + local default = Settings.getMousePanning() + for i, option in ipairs( listOfValues ) do + if option.value == default then + default = i + end + end + + -- Create the UISelectField. + return UISelectField( lx, ly, 0, 0, BUTTON_LIST_WIDTH, 1, Translator.getText( 'ui_settings_mouse_panning' ), listOfValues, callback, default ) +end + + --- -- Creates a UISelectField which allows the user to switch to the keybinding screen. -- @tparam number lx The parent's absolute coordinates along the x-axis. @@ -307,6 +340,7 @@ local function createUIList() buttonList:addChild( createFullscreenOption( lx, ly )) buttonList:addChild( createIngameEditorOption( lx, ly )) buttonList:addChild( createTexturePackOption( lx, ly )) + buttonList:addChild( createMousePanningOption( lx, ly )) buttonList:addChild( createKeybindingOption( lx, ly )) buttonList:addChild( createApplyButton( lx, ly )) buttonList:addChild( createBackButton( lx, ly )) From 9e9c3afb0b3fc2be156f64d533e05473f9813f8c Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Wed, 13 Dec 2017 00:45:28 +0100 Subject: [PATCH 078/178] Lower the threshold against dying from bleeding A creature now dies when its blood volume drops to 50%. --- src/characters/body/Body.lua | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/characters/body/Body.lua b/src/characters/body/Body.lua index e87a9253..ba8f1a48 100644 --- a/src/characters/body/Body.lua +++ b/src/characters/body/Body.lua @@ -41,7 +41,12 @@ local STATUS_EFFECTS = require( 'src.constants.STATUS_EFFECTS' ) local function handleBleeding( self, bodyPart ) if bodyPart:isBleeding() then self.curBloodVolume = self.curBloodVolume - bodyPart:getBloodLoss() - if self.curBloodVolume <= 0 then + + -- Determine how much blood the body has left. + local ratio = self.curBloodVolume / self.maxBloodVolume + + -- If the blood volume drops below 50% the creature dies. + if ratio < 0.5 then Log.debug( 'Character bleeds to death!', 'Body' ) self.statusEffects:add({ STATUS_EFFECTS.DEAD }) end From 6e28610fed0c7722e462f87ab461a485be3f255c Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Wed, 13 Dec 2017 00:56:40 +0100 Subject: [PATCH 079/178] Refactor UICopyrightFooter --- src/ui/elements/UICopyrightFooter.lua | 26 ++++++++++---------------- src/ui/screens/ChangelogScreen.lua | 2 +- src/ui/screens/MainMenu.lua | 2 +- src/ui/screens/OptionsScreen.lua | 2 +- src/ui/screens/SavegameScreen.lua | 2 +- 5 files changed, 14 insertions(+), 20 deletions(-) diff --git a/src/ui/elements/UICopyrightFooter.lua b/src/ui/elements/UICopyrightFooter.lua index a81e4c36..d95c8073 100644 --- a/src/ui/elements/UICopyrightFooter.lua +++ b/src/ui/elements/UICopyrightFooter.lua @@ -6,7 +6,7 @@ -- Required Modules -- ------------------------------------------------ -local Object = require( 'src.Object' ) +local Class = require( 'lib.Middleclass' ) local TexturePacks = require( 'src.ui.texturepacks.TexturePacks' ) local GridHelper = require( 'src.util.GridHelper' ) @@ -14,7 +14,7 @@ local GridHelper = require( 'src.util.GridHelper' ) -- Module -- ------------------------------------------------ -local UICopyrightFooter = {} +local UICopyrightFooter = Class( 'UICopyrightFooter' ) -- ------------------------------------------------ -- Constants @@ -27,21 +27,15 @@ local COPYRIGHT_STRING = ' © Robert Machmer, 2016-2017. All rights reserved.' -- Constructor -- ------------------------------------------------ -function UICopyrightFooter.new() - local self = Object.new():addInstance( 'UICopyrightFooter' ) +function UICopyrightFooter:draw() + local font = TexturePacks.getFont() + local tw, th = TexturePacks.getTileDimensions() + local sw, sh = GridHelper.getScreenGridDimensions() - function self:draw() - local font = TexturePacks.getFont() - local tw, th = TexturePacks.getTileDimensions() - local sw, sh = GridHelper.getScreenGridDimensions() - - TexturePacks.setColor( 'ui_text_dim' ) - love.graphics.print( VERSION_STRING, sw*tw - font:measureWidth( VERSION_STRING ), sh*th - font:getGlyphHeight() ) - love.graphics.print( COPYRIGHT_STRING, 0, sh*th - font:getGlyphHeight() ) - TexturePacks.resetColor() - end - - return self + TexturePacks.setColor( 'ui_text_dim' ) + love.graphics.print( VERSION_STRING, sw*tw - font:measureWidth( VERSION_STRING ), sh*th - font:getGlyphHeight() ) + love.graphics.print( COPYRIGHT_STRING, 0, sh*th - font:getGlyphHeight() ) + TexturePacks.resetColor() end return UICopyrightFooter diff --git a/src/ui/screens/ChangelogScreen.lua b/src/ui/screens/ChangelogScreen.lua index da50e79f..553017f6 100644 --- a/src/ui/screens/ChangelogScreen.lua +++ b/src/ui/screens/ChangelogScreen.lua @@ -213,7 +213,7 @@ function ChangelogScreen:initialize() local ox, oy = GridHelper.centerElement( SCROLLAREA_GRID_WIDTH, SCROLLAREA_GRID_HEIGHT ) self.scrollarea = UIScrollArea( ox, oy, 0, 0, SCROLLAREA_GRID_WIDTH, SCROLLAREA_GRID_HEIGHT, self.text, self.textHeight ) - self.footer = UICopyrightFooter.new() + self.footer = UICopyrightFooter() end function ChangelogScreen:update() diff --git a/src/ui/screens/MainMenu.lua b/src/ui/screens/MainMenu.lua index de7e8c60..0c639e0c 100644 --- a/src/ui/screens/MainMenu.lua +++ b/src/ui/screens/MainMenu.lua @@ -136,7 +136,7 @@ function MainMenu:initialize() self.container = UIContainer() self.container:register( self.buttonList ) - self.footer = UICopyrightFooter.new() + self.footer = UICopyrightFooter() -- Flush the LuaJIT cache to prevent memory leaks caused by cached -- upvalues and closures. diff --git a/src/ui/screens/OptionsScreen.lua b/src/ui/screens/OptionsScreen.lua index 3075b176..224e3aa8 100644 --- a/src/ui/screens/OptionsScreen.lua +++ b/src/ui/screens/OptionsScreen.lua @@ -364,7 +364,7 @@ function OptionsScreen:initialize() self.container = UIContainer() self.container:register( self.buttonList ) - self.footer = UICopyrightFooter.new() + self.footer = UICopyrightFooter() end --- diff --git a/src/ui/screens/SavegameScreen.lua b/src/ui/screens/SavegameScreen.lua index c3186475..0401b423 100644 --- a/src/ui/screens/SavegameScreen.lua +++ b/src/ui/screens/SavegameScreen.lua @@ -139,7 +139,7 @@ function SavegameScreen:initialize() self.container = UIContainer() self.container:register( self.buttonList ) - self.footer = UICopyrightFooter.new() + self.footer = UICopyrightFooter() end function SavegameScreen:update() From b7249cc8c0b79acd63049774cee41f7255f8de15 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Wed, 13 Dec 2017 00:58:14 +0100 Subject: [PATCH 080/178] Add copyright footer to keybindings screen --- src/ui/screens/KeybindingScreen.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ui/screens/KeybindingScreen.lua b/src/ui/screens/KeybindingScreen.lua index 7cd1a405..f0f1339c 100644 --- a/src/ui/screens/KeybindingScreen.lua +++ b/src/ui/screens/KeybindingScreen.lua @@ -14,6 +14,7 @@ local GridHelper = require( 'src.util.GridHelper' ) local UIVerticalList = require( 'src.ui.elements.lists.UIVerticalList' ) 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' ) -- ------------------------------------------------ @@ -154,6 +155,8 @@ function KeybindingScreen:initialize() self.container = UIContainer() self.container:register( self.buttonList ) + + self.footer = UICopyrightFooter() end --- @@ -168,6 +171,7 @@ end -- function KeybindingScreen:draw() self.container:draw() + self.footer:draw() end --- From b0eae39516d0ce38341304c614ab050acc368a85 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Wed, 13 Dec 2017 00:59:38 +0100 Subject: [PATCH 081/178] Refactor Letterbox --- main.lua | 2 +- src/ui/overlays/Letterbox.lua | 24 +++++++++--------------- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/main.lua b/main.lua index 6f0d5624..b7f21ed3 100644 --- a/main.lua +++ b/main.lua @@ -84,7 +84,7 @@ function love.load( args ) ScreenManager.init( screens, 'bootloading' ) - letterbox = Letterbox.new() + letterbox = Letterbox() end function love.draw() diff --git a/src/ui/overlays/Letterbox.lua b/src/ui/overlays/Letterbox.lua index b1ad90aa..3b927e44 100644 --- a/src/ui/overlays/Letterbox.lua +++ b/src/ui/overlays/Letterbox.lua @@ -9,7 +9,7 @@ -- Required Modules -- ------------------------------------------------ -local Object = require( 'src.Object' ); +local Class = require( 'lib.Middleclass' ) local GridHelper = require( 'src.util.GridHelper' ) local TexturePacks = require( 'src.ui.texturepacks.TexturePacks' ) @@ -17,25 +17,19 @@ local TexturePacks = require( 'src.ui.texturepacks.TexturePacks' ) -- Module -- ------------------------------------------------ -local Letterbox = {} +local Letterbox = Class( 'Letterbox' ) -- ------------------------------------------------ -- Constructor -- ------------------------------------------------ -function Letterbox.new() - local self = Object.new():addInstance( 'Letterbox' ) - - function self:draw() - local sw, sh = love.graphics.getDimensions() - local ow, oh = GridHelper.getScreenOverflow() - TexturePacks.setColor( 'sys_background' ) - love.graphics.rectangle( 'fill', 0, sh-oh, sw, oh ) - love.graphics.rectangle( 'fill', sw-ow, 0, ow, sh ) - TexturePacks.resetColor() - end - - return self +function Letterbox:draw() + local sw, sh = love.graphics.getDimensions() + local ow, oh = GridHelper.getScreenOverflow() + TexturePacks.setColor( 'sys_background' ) + love.graphics.rectangle( 'fill', 0, sh-oh, sw, oh ) + love.graphics.rectangle( 'fill', sw-ow, 0, ow, sh ) + TexturePacks.resetColor() end return Letterbox From 753504f4692dd38f87b381a713eac13db6a8e1e1 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Wed, 13 Dec 2017 01:05:30 +0100 Subject: [PATCH 082/178] Refactor StatusEffects --- src/characters/body/Body.lua | 2 +- src/characters/body/StatusEffects.lua | 165 ++++++++++++-------------- 2 files changed, 79 insertions(+), 88 deletions(-) diff --git a/src/characters/body/Body.lua b/src/characters/body/Body.lua index ba8f1a48..7673f4de 100644 --- a/src/characters/body/Body.lua +++ b/src/characters/body/Body.lua @@ -176,7 +176,7 @@ function Body:initialize( id, bloodVolume, tags, sizes ) self.nodes = {} self.edges = {} - self.statusEffects = StatusEffects.new() + self.statusEffects = StatusEffects() end -- ------------------------------------------------ diff --git a/src/characters/body/StatusEffects.lua b/src/characters/body/StatusEffects.lua index 2ce0bd01..bb875441 100644 --- a/src/characters/body/StatusEffects.lua +++ b/src/characters/body/StatusEffects.lua @@ -8,14 +8,14 @@ -- Required Modules -- ------------------------------------------------ +local Class = require( 'lib.Middleclass' ) local Log = require( 'src.util.Log' ) -local Object = require( 'src.Object' ) -- ------------------------------------------------ -- Module -- ------------------------------------------------ -local StatusEffects = {} +local StatusEffects = Class( 'StatusEffects' ) -- ------------------------------------------------ -- Constants @@ -24,111 +24,102 @@ local StatusEffects = {} local STATUS_EFFECTS = require( 'src.constants.STATUS_EFFECTS' ) -- ------------------------------------------------ --- Constructor +-- Private Methods -- ------------------------------------------------ -function StatusEffects.new() - local self = Object.new():addInstance( 'StatusEffects' ) - - -- ------------------------------------------------ - -- Private Attributes - -- ------------------------------------------------ - - local active = {} - - -- ------------------------------------------------ - -- Private Methods - -- ------------------------------------------------ - - --- - -- Makes sure the applied status effect is from the actual list of status - -- effects. - -- @tparam string effect The effect to check. - -- @treturn boolean True if the status effect is valid. - -- - local function validate( effect ) - for _, constant in pairs( STATUS_EFFECTS ) do - if effect == constant then - return true - end +--- +-- Makes sure the applied status effect is from the actual list of status +-- effects. +-- @tparam string effect The effect to check. +-- @treturn boolean True if the status effect is valid. +-- +local function validate( effect ) + for _, constant in pairs( STATUS_EFFECTS ) do + if effect == constant then + return true end - return false end + return false +end - -- ------------------------------------------------ - -- Public Methods - -- ------------------------------------------------ - - --- - -- Adds one or more status effects. - -- @tparam table effects A table containing the status effects to apply. - -- - function self:add( effects ) - if not effects then - return - end +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - for _, effect in pairs( effects ) do - assert( validate( effect ), string.format( 'Status effect %s is not valid.', effect )) +--- +-- Initializes a new StatusEffects instance. +-- +function StatusEffects:initialize() + self.active = {} +end - -- Only apply status effect if it isn't active already. - if not active[effect] then - Log.debug( 'Apply status effect ' .. effect ) - active[effect] = true - end - end +--- +-- Adds one or more status effects. +-- @tparam table effects A table containing the status effects to apply. +-- +function StatusEffects:add( effects ) + if not effects then + return end - --- - -- Removes one or more status effects. - -- @tparam table effects A table containing the status effects to remove. - -- - function self:remove( effects ) - if not effects then - return - end - - for _, effect in pairs( effects ) do - assert( validate( effect ), string.format( 'Status effect %s is not valid.', effect )) + for _, effect in pairs( effects ) do + assert( validate( effect ), string.format( 'Status effect %s is not valid.', effect )) - Log.debug( 'Removing status effect ' .. effect ) - active[effect] = false + -- Only apply status effect if it isn't active already. + if not self.active[effect] then + Log.debug( 'Apply status effect ' .. effect ) + self.active[effect] = true end end +end - --- - -- Serializes this object. - -- @treturn table A table containing the serialized values. - -- - function self:serialize() - local t = {} - for effect, boolean in pairs( active ) do - t[effect] = boolean - end - return t +--- +-- Removes one or more status effects. +-- @tparam table effects A table containing the status effects to remove. +-- +function StatusEffects:remove( effects ) + if not effects then + return end - -- ------------------------------------------------ - -- Getters - -- ------------------------------------------------ + for _, effect in pairs( effects ) do + assert( validate( effect ), string.format( 'Status effect %s is not valid.', effect )) - --- - -- Returns wether the character is dead. - -- @treturn boolean Wether the character is dead or not. - -- - function self:isDead() - return active[STATUS_EFFECTS.DEAD] + Log.debug( 'Removing status effect ' .. effect ) + self.active[effect] = false end +end - --- - -- Returns wether the character is blind. - -- @treturn boolean Wether the character is blind or not. - -- - function self:isBlind() - return active[STATUS_EFFECTS.BLIND] +--- +-- Serializes this object. +-- @treturn table A table containing the serialized values. +-- +function StatusEffects:serialize() + local t = {} + for effect, boolean in pairs( self.active ) do + t[effect] = boolean end + return t +end - return self +-- ------------------------------------------------ +-- Getters +-- ------------------------------------------------ + +--- +-- Returns wether the character is dead. +-- @treturn boolean Wether the character is dead or not. +-- +function StatusEffects:isDead() + return self.active[STATUS_EFFECTS.DEAD] +end + +--- +-- Returns wether the character is blind. +-- @treturn boolean Wether the character is blind or not. +-- +function StatusEffects:isBlind() + return self.active[STATUS_EFFECTS.BLIND] end return StatusEffects From 6e797b4b84579947032767a0a4b06b7729a4ca49 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Wed, 13 Dec 2017 01:33:50 +0100 Subject: [PATCH 083/178] Refactor Faction --- src/CombatState.lua | 4 +- src/characters/Faction.lua | 560 +++++++++++++++++----------------- src/ui/screens/GameScreen.lua | 2 +- 3 files changed, 285 insertions(+), 281 deletions(-) diff --git a/src/CombatState.lua b/src/CombatState.lua index 3e060904..4f263bee 100644 --- a/src/CombatState.lua +++ b/src/CombatState.lua @@ -84,8 +84,8 @@ function CombatState.new() end factions = Factions.new( map ); - factions:addFaction( Faction.new( FACTIONS.ENEMY, true )) - factions:addFaction( Faction.new( FACTIONS.NEUTRAL, true )) + factions:addFaction( Faction( FACTIONS.ENEMY, true )) + factions:addFaction( Faction( FACTIONS.NEUTRAL, true )) factions:addFaction( playerFaction ) if savegame then diff --git a/src/characters/Faction.lua b/src/characters/Faction.lua index a14adf2f..2c9af111 100644 --- a/src/characters/Faction.lua +++ b/src/characters/Faction.lua @@ -1,7 +1,15 @@ -local Log = require( 'src.util.Log' ); -local Object = require('src.Object'); -local Node = require('src.util.Node'); -local Messenger = require( 'src.Messenger' ); +--- +-- @module Faction +-- + +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ + +local Class = require( 'lib.Middleclass' ) +local Log = require( 'src.util.Log' ) +local Node = require( 'src.util.Node' ) +local Messenger = require( 'src.Messenger' ) local CharacterFactory = require( 'src.characters.CharacterFactory' ) local Character = require( 'src.characters.Character' ) @@ -9,328 +17,324 @@ local Character = require( 'src.characters.Character' ) -- Module -- ------------------------------------------------ -local Faction = {}; +local Faction = Class( 'Faction' ) -- ------------------------------------------------ --- Constructor +-- Public Methods -- ------------------------------------------------ -function Faction.new( type, controlledByAi ) - local self = Object.new():addInstance( 'Faction' ); - - -- ------------------------------------------------ - -- Private Attributes - -- ------------------------------------------------ - - local root; - local active; - local last; - - -- ------------------------------------------------ - -- Public Methods - -- ------------------------------------------------ - - --- - -- Activates this Faction right before it is selected. - -- - function self:activate() - self:iterate( function( character ) - if not character:isDead() then - Log.debug( 'Tick character ' .. tostring( character ), 'Faction' ); - character:setFinishedTurn( false ); - character:tickOneTurn(); - end - end); - end +--- +-- Initializes a new Faction instance. +-- @tparam string type The faction's type. +-- @tparam boolean ai Determines wether the faction is controlled by ai. +-- +function Faction:initialize( type, ai ) + self.type = type + self.ai = ai +end - --- - -- Adds a new Character to this Faction. - -- @param character (Character) The character to add. - -- - function self:addCharacter( character ) - local node = Node.new( character ); - - -- Initialise root node. - if not root then - root = node; - active = root; - last = active; - return; +--- +-- Activates this Faction right before it is selected. +-- +function Faction:activate() + self:iterate( function( character ) + if not character:isDead() then + Log.debug( 'Tick character ' .. tostring( character ), 'Faction' ) + character:setFinishedTurn( false ) + character:tickOneTurn() end + end) +end + +--- +-- Adds a new Character to this Faction. +-- @tparam Character character The character to add. +-- +function Faction:addCharacter( character ) + local node = Node.new( character ) + + -- Initialise root node. + if not self.root then + self.root = node + self.active = self.root + self.last = self.active + return + end - -- Doubly link the new node. - active:linkNext( node ); - node:linkPrev( active ); + -- Doubly link the new node. + self.active:linkNext( node ) + node:linkPrev( self.active ) - -- Make the new node active and mark it as the last node in our list. - active = node; - last = active; + -- Make the new node active and mark it as the last node in our list. + self.active = node + self.last = self.active +end + +--- +-- Adds characters to this faction. +-- @tparam number amount The amount of characters to add. +-- @tparam string ctype The type of characters to add. +-- +function Faction:addCharacters( amount, ctype ) + for _ = 1, amount do + -- Create the new character. + local character = CharacterFactory.newCharacter( ctype, self.type ) + character:setFaction( self ) + + -- Add it to this faction. + self:addCharacter( character ) end +end - --- - -- Adds characters to this faction. - -- @tparam number amount The amount of characters to add. - -- @tparam string ctype The type of characters to add. - -- - function self:addCharacters( amount, ctype ) - for _ = 1, amount do - -- Create the new character. - local character = CharacterFactory.newCharacter( ctype, self:getType() ) - character:setFaction( self ) - - -- Add it to this faction. - self:addCharacter( character ) - end +--- +-- Recreates saved charaters for each faction. +-- @tparam table savedFactions A table containing the information to load all characters. +-- +function Faction:loadCharacters( characters ) + for _, savedCharacter in ipairs( characters ) do + local character = CharacterFactory.loadCharacter( savedCharacter ) + character:setFaction( self ) + self:addCharacter( character ) end +end - --- - -- Recreates saved charaters for each faction. - -- @tparam table savedFactions A table containing the information to load all characters. - -- - function self:loadCharacters( characters ) - for _, savedCharacter in ipairs( characters ) do - local character = CharacterFactory.loadCharacter( savedCharacter ) - character:setFaction( self ) - self:addCharacter( character ) +--- +-- Spawns the characters of this Faction on the given map. +-- @tparam Map map The map to spawn the characters on. +-- +function Faction:spawnCharacters( map ) + self:iterate( function( character ) + local sx, sy = character:getSavedPosition() + character:setSavedPosition( nil, nil ) + + local tile + + if sx and sy then + tile = map:getTileAt( sx, sy ) + else + tile = map:findSpawnPoint( self.type ) end - end - --- - -- Spawns the characters of this Faction on the given map. - -- @tparam Map map The map to spawn the characters on. - -- - function self:spawnCharacters( map ) - self:iterate( function( character ) - local sx, sy = character:getSavedPosition() - character:setSavedPosition( nil, nil ) - - local tile - - if sx and sy then - tile = map:getTileAt( sx, sy ) - else - tile = map:findSpawnPoint( type ) - end - - tile:setCharacter( character ) - character:setTile( tile ) - character:setMap( map ) - end) - end + tile:setCharacter( character ) + character:setTile( tile ) + character:setMap( map ) + end) +end - --- - -- Checks if any of the characters in this Faction can see the target tile. - -- @param target (Tile) The tile to check visibility for. - -- @return (boolean) Wether a character can see this tile. - -- - function self:canSee( tile ) - local node = root; - while node do - if node:getObject():canSee( tile ) then - return true; - end - node = node:getNext(); +--- +-- 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. - -- - function self:deactivate() - self:iterate( function( character ) - character:resetActionPoints(); - character:clearActions(); - end); - end +--- +-- Deactivates this Faction right before it is deselected. +-- +function Faction:deactivate() + self:iterate( function( character ) + character:resetActionPoints() + character:clearActions() + end) +end - --- - -- Finds a certain Character in this Faction and makes him active. - -- @param character (Character) The character to select. - -- - function self:selectCharacter( character ) - assert( character:isInstanceOf( Character ), 'Expected object of type Character!' ) - local node = root; - while node do - if node:getObject() == character and not node:getObject():isDead() then - -- Deactivate old character. - active:getObject():deactivate(); - - -- Activate new character. - active = node; - active:getObject():activate(); - - Messenger.publish( 'SWITCH_CHARACTERS', active:getObject() ); - break; - end - node = node:getNext(); +--- +-- Finds a certain Character in this Faction and makes him active. +-- @tparam Character character The character to select. +-- +function Faction:selectCharacter( character ) + assert( character:isInstanceOf( Character ), 'Expected object of type Character!' ) + local node = self.root + while node do + if node:getObject() == character and not node:getObject():isDead() then + -- Deactivate old character. + self.active:getObject():deactivate() + + -- Activate new character. + self.active = node + self.active:getObject():activate() + + Messenger.publish( 'SWITCH_CHARACTERS', self.active:getObject() ) + break end + node = node:getNext() end +end - --- - -- Checks if any of this faction's characters are still alive. - -- @return (boolean) True if at least one character is alive. - -- - function self:hasLivingCharacters() - local node = root; - while node do - if not node:getObject():isDead() then - return true; - end - - if node == last then - break; - end - - node = node:getNext(); +--- +-- Checks if any of this faction's characters are still alive. +-- @treturn boolean True if at least one character is alive. +-- +function Faction:hasLivingCharacters() + local node = self.root + while node do + if not node:getObject():isDead() then + return true end - return false; - end - --- - -- Checks if any of this faction's characters have taken their actions. - -- @return (boolean) True if all characters have finished their turn. - -- - function self:hasFinishedTurn() - local node = root; - while node do - if not node:getObject():hasFinishedTurn() then - return false; - end - - if node == last then - break; - end - - node = node:getNext(); + if node == self.last then + break end - return true; + + node = node:getNext() end + return false +end - --- - -- Gets the next character who hasn't finished his turn yet. - -- @return (Character) The character with unfinished turn. - -- - function self:nextCharacterForTurn() - local node = root; - while node do - if not node:getObject():hasFinishedTurn() then - return node:getObject(); - end - - if node == last then - break; - end - - node = node:getNext(); +--- +-- Checks if any of this faction's characters have taken their actions. +-- @treturn boolean True if all characters have finished their turn. +-- +function Faction:hasFinishedTurn() + local node = self.root + while node do + if not node:getObject():hasFinishedTurn() then + return false end - error( 'Could not find character with unfinished turn. Use self:hasFinishedTurn() to make sure the faction has characters with unfinshed turns.' ) - end - --- - -- Iterates over all nodes in this Faction, gets their Characters and passes - -- them to the callback function if they are alive. - -- @tparam function callback The callback to use on the characters. - -- - function self:iterate( callback ) - local node = root - while node do - if not node:getObject():isDead() then - callback( node:getObject() ) - end - node = node:getNext() + if node == self.last then + break end + + node = node:getNext() end + return true +end - --- - -- Selects and returns the next Character. - -- @return (Character) The active Character. - -- - function self:nextCharacter() - local previousCharacter = active:getObject(); - while active do - active = active:getNext() or root; - local character = active:getObject(); - if not character:isDead() then - previousCharacter:deactivate(); - character:activate(); - Messenger.publish( 'SWITCH_CHARACTERS', character ); - return character; - end +--- +-- Gets the next character who hasn't finished his turn yet. +-- @treturn Character The character with unfinished turn. +-- +function Faction:nextCharacterForTurn() + local node = self.root + while node do + if not node:getObject():hasFinishedTurn() then + return node:getObject() end - end - --- - -- Selects and returns the previous Character. - -- @return (Character) The active Character. - -- - function self:prevCharacter() - local previousCharacter = active:getObject(); - while active do - active = active:getPrev() or last; - local character = active:getObject(); - if not character:isDead() then - previousCharacter:activate(); - character:deactivate(); - Messenger.publish( 'SWITCH_CHARACTERS', character ); - return character; - end + if node == self.last then + break end + + node = node:getNext() end + error( 'Could not find character with unfinished turn. Use self:hasFinishedTurn() to make sure the faction has characters with unfinshed turns.' ) +end - --- - -- Generates the FOV for characters which can see a certain tile. - -- @param tile (Tile) The tile to check for. - -- - function self:regenerateFOVSelectively( tile ) - local node = root; - while node do - local character = node:getObject(); - if not character:isDead() and character:canSee( tile ) then - character:generateFOV(); - end - node = node:getNext(); +--- +-- Iterates over all nodes in this Faction, gets their Characters and passes +-- them to the callback function if they are alive. +-- @tparam function callback The callback to use on the characters. +-- +function Faction:iterate( callback ) + local node = self.root + while node do + if not node:getObject():isDead() then + callback( node:getObject() ) end + node = node:getNext() end +end - function self:serialize() - local t = {}; - local node = root; - while node do - t[#t+1] = node:getObject():serialize(); - node = node:getNext(); +--- +-- Selects and returns the next Character. +-- @treturn Character The active Character. +-- +function Faction:nextCharacter() + local previousCharacter = self.active:getObject() + while self.active do + self.active = self.active:getNext() or self.root + local character = self.active:getObject() + if not character:isDead() then + previousCharacter:deactivate() + character:activate() + Messenger.publish( 'SWITCH_CHARACTERS', character ) + return character end - return t; end +end - -- ------------------------------------------------ - -- Getters - -- ------------------------------------------------ - - --- - -- Returns this faction's currently active character. - -- @return (Character) The active character. - -- - function self:getCurrentCharacter() - return active:getObject(); +--- +-- Selects and returns the previous Character. +-- @treturn Character The active Character. +-- +function Faction:prevCharacter() + local previousCharacter = self.root:getObject() + while self.root do + self.root = self.root:getPrev() or self.last + local character = self.root:getObject() + if not character:isDead() then + previousCharacter:activate() + character:deactivate() + Messenger.publish( 'SWITCH_CHARACTERS', character ) + return character + end end +end - --- - -- Returns the faction's type. - -- @return (string) The faction's id as defined in the faction constants. - -- - function self:getType() - return type; +--- +-- Generates the FOV for characters which can see a certain tile. +-- @tparam Tile tile The tile to check for. +-- +function Faction:regenerateFOVSelectively( tile ) + local node = self.root + while node do + local character = node:getObject() + if not character:isDead() and character:canSee( tile ) then + character:generateFOV() + end + node = node:getNext() end +end - --- - -- Wether this faction is controlled by the game's AI. - -- @return (boolean) True if it is controlled by the AI. - -- - function self:isAIControlled() - return controlledByAi; +--- +-- Serializes the faction. +-- @treturn table The serialized faction object. +-- +function Faction:serialize() + local t = {} + local node = self.root + while node do + t[#t+1] = node:getObject():serialize() + node = node:getNext() end + return t +end + +-- ------------------------------------------------ +-- Getters +-- ------------------------------------------------ + +--- +-- Returns this faction's currently active character. +-- @treturn Character The active character. +-- +function Faction:getCurrentCharacter() + return self.root:getObject() +end + +--- +-- Returns the faction's type. +-- @treturn string The faction's id as defined in the faction constants. +-- +function Faction:getType() + return self.type +end - return self; +--- +-- Wether this faction is controlled by the game's AI. +-- @treturn boolean True if it is controlled by the AI. +-- +function Faction:isAIControlled() + return self.ai end -return Faction; +return Faction diff --git a/src/ui/screens/GameScreen.lua b/src/ui/screens/GameScreen.lua index f11a13e9..61d8f717 100644 --- a/src/ui/screens/GameScreen.lua +++ b/src/ui/screens/GameScreen.lua @@ -30,7 +30,7 @@ local FACTIONS = require( 'src.constants.FACTIONS' ) -- ------------------------------------------------ function GameScreen:initialize( savegame ) - local playerFaction = Faction.new( FACTIONS.ALLIED, false ) + local playerFaction = Faction( FACTIONS.ALLIED, false ) if savegame then playerFaction:loadCharacters( savegame.factions[FACTIONS.ALLIED] ) From ce72f6088f4da680606add699ff8b19c99430d21 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Wed, 13 Dec 2017 01:41:20 +0100 Subject: [PATCH 084/178] Fix bad refactoring --- src/characters/Faction.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/characters/Faction.lua b/src/characters/Faction.lua index 2c9af111..2dc24031 100644 --- a/src/characters/Faction.lua +++ b/src/characters/Faction.lua @@ -267,10 +267,10 @@ end -- @treturn Character The active Character. -- function Faction:prevCharacter() - local previousCharacter = self.root:getObject() - while self.root do - self.root = self.root:getPrev() or self.last - local character = self.root:getObject() + local previousCharacter = self.active:getObject() + while self.active do + self.active = self.active:getPrev() or self.last + local character = self.active:getObject() if not character:isDead() then previousCharacter:activate() character:deactivate() @@ -318,7 +318,7 @@ end -- @treturn Character The active character. -- function Faction:getCurrentCharacter() - return self.root:getObject() + return self.active:getObject() end --- From bd4a022ca9869d7e94e3f0ea07136f7b3e6e5bb0 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Wed, 13 Dec 2017 02:43:00 +0100 Subject: [PATCH 085/178] Use getter for player faction --- src/CombatState.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CombatState.lua b/src/CombatState.lua index 4f263bee..7009196c 100644 --- a/src/CombatState.lua +++ b/src/CombatState.lua @@ -131,7 +131,7 @@ function CombatState.new() end stateManager:update( dt ) - if not factions:findFaction( FACTIONS.ALLIED ):hasLivingCharacters() then + if not factions:getPlayerFaction():hasLivingCharacters() then ScreenManager.pop() ScreenManager.push( 'gameover', factions:getPlayerFaction(), false ) end From 96a84b87077e664a1b988b2e13a5c1847b34ecde Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Wed, 13 Dec 2017 03:21:04 +0100 Subject: [PATCH 086/178] Fix faulty line offset in changelog screen --- src/ui/screens/ChangelogScreen.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ui/screens/ChangelogScreen.lua b/src/ui/screens/ChangelogScreen.lua index 553017f6..227c9b2b 100644 --- a/src/ui/screens/ChangelogScreen.lua +++ b/src/ui/screens/ChangelogScreen.lua @@ -158,7 +158,9 @@ local function createSection( text, id, log, height ) height = height + text:getHeight() end - return height + text:getHeight() + -- Add a single empty line to the end of the section. + local _, gh = TexturePacks.getGlyphDimensions() + return height + gh end --- From c92113b3ae72c8749829c7f2f1fd645652facf97 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Fri, 15 Dec 2017 18:46:02 +0100 Subject: [PATCH 087/178] Refactor PathFinder --- src/characters/pathfinding/PathFinder.lua | 118 ++++++++++++---------- 1 file changed, 63 insertions(+), 55 deletions(-) diff --git a/src/characters/pathfinding/PathFinder.lua b/src/characters/pathfinding/PathFinder.lua index 38d247c5..83878e99 100644 --- a/src/characters/pathfinding/PathFinder.lua +++ b/src/characters/pathfinding/PathFinder.lua @@ -1,18 +1,26 @@ -local Path = require('src.characters.pathfinding.Path'); +--- +-- @module PathFinder +-- -- ------------------------------------------------ --- Constants +-- Required Modules -- ------------------------------------------------ -local DIRECTION = require( 'src.constants.DIRECTION' ); -local MAX_TILES = 300; -local SQRT = math.sqrt( 2 ); +local Path = require( 'src.characters.pathfinding.Path' ) -- ------------------------------------------------ -- Module -- ------------------------------------------------ -local PathFinder = {}; +local PathFinder = {} + +-- ------------------------------------------------ +-- Constants +-- ------------------------------------------------ + +local DIRECTION = require( 'src.constants.DIRECTION' ) +local MAX_TILES = 300 +local SQRT = math.sqrt( 2 ) -- ------------------------------------------------ -- Private Functions @@ -25,9 +33,9 @@ local PathFinder = {}; -- @return (number) The calculated heuristic. -- local function calculateHeuristic( a, b ) - local distanceX = math.abs( a:getX() - b:getX() ); - local distanceY = math.abs( a:getY() - b:getY() ); - return math.max( distanceX, distanceY ); + local distanceX = math.abs( a:getX() - b:getX() ) + local distanceY = math.abs( a:getY() - b:getY() ) + return math.max( distanceX, distanceY ) end --- @@ -40,9 +48,9 @@ local function getDirectionModifier( direction ) or direction == DIRECTION.NORTH_WEST or direction == DIRECTION.SOUTH_EAST or direction == DIRECTION.SOUTH_WEST then - return SQRT; + return SQRT end - return 1; + return 1 end --- @@ -54,26 +62,26 @@ end -- local function calculateCost( tile, target, character ) if tile:hasWorldObject() then - local worldObject = tile:getWorldObject(); - local interactionCost = worldObject:getInteractionCost( character:getStance() ); + local worldObject = tile:getWorldObject() + local interactionCost = worldObject:getInteractionCost( character:getStance() ) -- We never move on the tile that the character wants to interact with. if tile == target then - return interactionCost; + return interactionCost end -- Open the object and walk on the tile. if worldObject:isOpenable() and not worldObject:isPassable() then - return interactionCost + tile:getMovementCost( character:getStance() ); + return interactionCost + tile:getMovementCost( character:getStance() ) end -- Climbing ignores the movement cost of the tile the world object is on. if worldObject:isClimbable() then - return interactionCost; + return interactionCost end end - return tile:getMovementCost( character:getStance() ); + return tile:getMovementCost( character:getStance() ) end --- @@ -83,14 +91,14 @@ end -- @return (number) The index in the list. -- local function getNextTile( list ) - local index, cost; + local index, cost for i = 1, #list do if not cost or cost > list[i].f then - cost = list[i].f; - index = i; + cost = list[i].f + index = i end end - return list[index], index; + return list[index], index end --- @@ -118,31 +126,31 @@ end local function isValidTile( tile, closedList, target ) -- Ignore tiles that are already used in the path. if isInList( closedList, tile ) then - return false; + return false end -- We don't allow movement to tiles occupied by other characters. if tile:isOccupied() then - return false; + return false end if tile:hasWorldObject() then - local worldObject = tile:getWorldObject(); + local worldObject = tile:getWorldObject() -- Openable world objects never block pathfinding. if worldObject:isOpenable() then - return true; + return true end -- Container objects are valid if they are the target of the path. if worldObject:blocksPathfinding() and worldObject:isContainer() then - return tile == target; + return tile == target end -- Non-blocking world objects are valid too. - return not worldObject:blocksPathfinding(); + return not worldObject:blocksPathfinding() end - return tile:isPassable(); + return tile:isPassable() end --- @@ -151,7 +159,7 @@ end -- @param node (table) The node to add. -- local function addToCLosedList( closedList, node ) - closedList[#closedList + 1] = node; + closedList[#closedList + 1] = node end --- @@ -160,7 +168,7 @@ end -- @param index (number) The index of the node to remove. -- local function removeFromOpenList( openList, index ) - table.remove( openList, index ); + table.remove( openList, index ) end --- @@ -170,17 +178,17 @@ end -- @return (Path) A path object containing tiles to form a path. -- local function finalizePath( endNode ) - local path = Path.new(); - path:addNode( endNode.tile, endNode.actualCost ); + local path = Path.new() + path:addNode( endNode.tile, endNode.actualCost ) -- Build the rest of the path. - local parent = endNode.parent; + local parent = endNode.parent while parent and parent.parent do - path:addNode( parent.tile, parent.actualCost ); - parent = parent.parent; + path:addNode( parent.tile, parent.actualCost ) + parent = parent.parent end - return path; + return path end -- ------------------------------------------------ @@ -194,50 +202,50 @@ end -- @return (Path) A Path object containing tiles to form a path. -- function PathFinder.generatePath( character, target ) - local counter = 0; - local closedList = {}; + local counter = 0 + local closedList = {} local openList = { { tile = character:getTile(), direction = nil, parent = nil, g = 0, f = 0 } -- Starting point. - }; + } while #openList > 0 do - counter = counter + 1; - local current, index = getNextTile( openList ); + counter = counter + 1 + local current, index = getNextTile( openList ) -- Update lists. - addToCLosedList( closedList, current ); - removeFromOpenList( openList, index ); + addToCLosedList( closedList, current ) + removeFromOpenList( openList, index ) -- Stop if we have found the target. if current.tile == target then - return finalizePath( current, character ); + return finalizePath( current, character ) elseif counter > MAX_TILES then - return; -- Abort if we haven't found the tile after searching for a while. + return -- Abort if we haven't found the tile after searching for a while. end -- Look for the next tile. for direction, tile in pairs( current.tile:getNeighbours() ) do -- Check if the tile is valid to use in our path. if isValidTile( tile, closedList, target ) then - local cost = calculateCost( tile, target, character ); - local g = current.g + cost * getDirectionModifier( direction ); - local f = g + calculateHeuristic( tile, target ); + local cost = calculateCost( tile, target, character ) + local g = current.g + cost * getDirectionModifier( direction ) + local f = g + calculateHeuristic( tile, target ) -- Check if the tile is in the open list. If it is not, then -- add it to the open list and proceed. If it already is in -- the open list, update its cost and parent values. - local visitedNode = isInList( openList, tile ); + local visitedNode = isInList( openList, tile ) if not visitedNode then - openList[#openList + 1] = { tile = tile, direction = direction, parent = current, actualCost = cost, g = g, f = f }; + openList[#openList + 1] = { tile = tile, direction = direction, parent = current, actualCost = cost, g = g, f = f } elseif g < visitedNode.g then - visitedNode.direction = direction; - visitedNode.parent = current; - visitedNode.g = g; - visitedNode.f = f; + visitedNode.direction = direction + visitedNode.parent = current + visitedNode.g = g + visitedNode.f = f end end end end end -return PathFinder; +return PathFinder From e00db82b6f230d5beecef1286cfd8a559a955cf8 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sun, 10 Dec 2017 14:57:33 +0100 Subject: [PATCH 088/178] Remove restrictions for pathfinding algorithm --- src/characters/pathfinding/PathFinder.lua | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/characters/pathfinding/PathFinder.lua b/src/characters/pathfinding/PathFinder.lua index 83878e99..a62e05f4 100644 --- a/src/characters/pathfinding/PathFinder.lua +++ b/src/characters/pathfinding/PathFinder.lua @@ -19,7 +19,6 @@ local PathFinder = {} -- ------------------------------------------------ local DIRECTION = require( 'src.constants.DIRECTION' ) -local MAX_TILES = 300 local SQRT = math.sqrt( 2 ) -- ------------------------------------------------ @@ -202,14 +201,12 @@ end -- @return (Path) A Path object containing tiles to form a path. -- function PathFinder.generatePath( character, target ) - local counter = 0 local closedList = {} local openList = { { tile = character:getTile(), direction = nil, parent = nil, g = 0, f = 0 } -- Starting point. } while #openList > 0 do - counter = counter + 1 local current, index = getNextTile( openList ) -- Update lists. @@ -219,8 +216,6 @@ function PathFinder.generatePath( character, target ) -- Stop if we have found the target. if current.tile == target then return finalizePath( current, character ) - elseif counter > MAX_TILES then - return -- Abort if we haven't found the tile after searching for a while. end -- Look for the next tile. From d16d8bf9ca20727ab450b6f48f49ed76fe3da4c3 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sat, 16 Dec 2017 18:47:16 +0100 Subject: [PATCH 089/178] Remove character dependency from Pathfinder --- .../ai/behaviortree/leafs/BTMoveToTarget.lua | 2 +- .../behaviortree/leafs/BTRandomMovement.lua | 2 +- src/characters/pathfinding/PathFinder.lua | 31 ++++++++++--------- src/turnbased/helpers/MovementInput.lua | 2 +- 4 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/characters/ai/behaviortree/leafs/BTMoveToTarget.lua b/src/characters/ai/behaviortree/leafs/BTMoveToTarget.lua index 226bb129..0ad0dc9a 100644 --- a/src/characters/ai/behaviortree/leafs/BTMoveToTarget.lua +++ b/src/characters/ai/behaviortree/leafs/BTMoveToTarget.lua @@ -11,7 +11,7 @@ function BTMoveToTarget.new() local function generatePath( target, character ) if target and target:isPassable() and not target:isOccupied() then - path = PathFinder.generatePath( character, target, true ); + path = PathFinder.generatePath( character:getTile(), target, character:getStance() ) end end diff --git a/src/characters/ai/behaviortree/leafs/BTRandomMovement.lua b/src/characters/ai/behaviortree/leafs/BTRandomMovement.lua index 1c7ecaca..989af53a 100644 --- a/src/characters/ai/behaviortree/leafs/BTRandomMovement.lua +++ b/src/characters/ai/behaviortree/leafs/BTRandomMovement.lua @@ -9,7 +9,7 @@ function BTRandomMovement.new() local function generatePath( target, character ) if target and target:isPassable() and not target:isOccupied() then - return PathFinder.generatePath( character, target, true ); + return PathFinder.generatePath( character:getTile(), target, character:getStance() ) end end diff --git a/src/characters/pathfinding/PathFinder.lua b/src/characters/pathfinding/PathFinder.lua index a62e05f4..2d0bc4c1 100644 --- a/src/characters/pathfinding/PathFinder.lua +++ b/src/characters/pathfinding/PathFinder.lua @@ -54,15 +54,15 @@ end --- -- Calculates the cost of moving to a tile. --- @param tile (Tile) The tile to calculate a cost for. --- @param target (Tile) The target tile of the path. --- @param character (Character) The character to plot a path for. --- @return (number) The calculated movement cost. +-- @tparam Tile tile The tile to calculate a cost for. +-- @tparam Tile target The target tile of the path. +-- @tparam string stance The stance of the creature to search a path for. +-- @treturn number The calculated movement cost. -- -local function calculateCost( tile, target, character ) +local function calculateCost( tile, target, stance ) if tile:hasWorldObject() then local worldObject = tile:getWorldObject() - local interactionCost = worldObject:getInteractionCost( character:getStance() ) + local interactionCost = worldObject:getInteractionCost( stance ) -- We never move on the tile that the character wants to interact with. if tile == target then @@ -71,7 +71,7 @@ local function calculateCost( tile, target, character ) -- Open the object and walk on the tile. if worldObject:isOpenable() and not worldObject:isPassable() then - return interactionCost + tile:getMovementCost( character:getStance() ) + return interactionCost + tile:getMovementCost( stance ) end -- Climbing ignores the movement cost of the tile the world object is on. @@ -80,7 +80,7 @@ local function calculateCost( tile, target, character ) end end - return tile:getMovementCost( character:getStance() ) + return tile:getMovementCost( stance ) end --- @@ -196,14 +196,15 @@ end --- -- Calculates a path between two tiles by using the A* algorithm. --- @param character (Character) The character to plot a path for. --- @param target (Tile) The target. --- @return (Path) A Path object containing tiles to form a path. +-- @tparam Tile start The tile to start the pathfinding at. +-- @tparam Tile target The target to search for. +-- @tparam string stance The stance of the creature to search a path for. +-- @treturn Path The path containing all tiles from the start to the target. -- -function PathFinder.generatePath( character, target ) +function PathFinder.generatePath( start, target, stance ) local closedList = {} local openList = { - { tile = character:getTile(), direction = nil, parent = nil, g = 0, f = 0 } -- Starting point. + { tile = start, direction = nil, parent = nil, g = 0, f = 0 } -- Starting point. } while #openList > 0 do @@ -215,14 +216,14 @@ function PathFinder.generatePath( character, target ) -- Stop if we have found the target. if current.tile == target then - return finalizePath( current, character ) + return finalizePath( current, stance ) end -- Look for the next tile. for direction, tile in pairs( current.tile:getNeighbours() ) do -- Check if the tile is valid to use in our path. if isValidTile( tile, closedList, target ) then - local cost = calculateCost( tile, target, character ) + local cost = calculateCost( tile, target, stance ) local g = current.g + cost * getDirectionModifier( direction ) local f = g + calculateHeuristic( tile, target ) diff --git a/src/turnbased/helpers/MovementInput.lua b/src/turnbased/helpers/MovementInput.lua index 6535c7c6..ae0d837d 100644 --- a/src/turnbased/helpers/MovementInput.lua +++ b/src/turnbased/helpers/MovementInput.lua @@ -21,7 +21,7 @@ local MovementInput = Class( 'MovementInput' ) local function generatePath( target, character ) if target and target:isPassable() and not target:isOccupied() then - return PathFinder.generatePath( character, target, true ) + return PathFinder.generatePath( character:getTile(), target, character:getStance() ) end end -- ------------------------------------------------ From c37ab3e6c0619863047c562ef2e367bd8f0a43ab Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sat, 16 Dec 2017 19:00:25 +0100 Subject: [PATCH 090/178] Remove superfluous function from Pathfinder We remove the next node in the list directly when we find it. --- src/characters/pathfinding/PathFinder.lua | 27 +++++++---------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/src/characters/pathfinding/PathFinder.lua b/src/characters/pathfinding/PathFinder.lua index 2d0bc4c1..391774b4 100644 --- a/src/characters/pathfinding/PathFinder.lua +++ b/src/characters/pathfinding/PathFinder.lua @@ -84,20 +84,19 @@ local function calculateCost( tile, target, stance ) end --- --- Gets the next tile with the lowest cost from a list. --- @param list (table) The list to search through. --- @return (table) The next tile in the list. +-- Removes the node with the lowest cost from the open list and returns it. +-- @treturn table The next node in the list. -- @return (number) The index in the list. -- -local function getNextTile( list ) +local function getNextNode( openList ) local index, cost - for i = 1, #list do - if not cost or cost > list[i].f then - cost = list[i].f + for i = 1, #openList do + if not cost or cost > openList[i].f then + cost = openList[i].f index = i end end - return list[index], index + return table.remove( openList, index ) end --- @@ -161,15 +160,6 @@ local function addToCLosedList( closedList, node ) closedList[#closedList + 1] = node end ---- --- Removes a node from the open list. --- @param openList (table) The open list. --- @param index (number) The index of the node to remove. --- -local function removeFromOpenList( openList, index ) - table.remove( openList, index ) -end - --- -- Traces the closed list from the target to the starting point by going to the -- parents of each tile in the list. @@ -208,11 +198,10 @@ function PathFinder.generatePath( start, target, stance ) } while #openList > 0 do - local current, index = getNextTile( openList ) + local current = getNextNode( openList ) -- Update lists. addToCLosedList( closedList, current ) - removeFromOpenList( openList, index ) -- Stop if we have found the target. if current.tile == target then From 72c0449aac07231e58d37baab21268d74e06e031 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sat, 16 Dec 2017 19:03:12 +0100 Subject: [PATCH 091/178] Break out of the loop once the target is found --- src/characters/pathfinding/PathFinder.lua | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/characters/pathfinding/PathFinder.lua b/src/characters/pathfinding/PathFinder.lua index 391774b4..38c88c1e 100644 --- a/src/characters/pathfinding/PathFinder.lua +++ b/src/characters/pathfinding/PathFinder.lua @@ -197,15 +197,18 @@ function PathFinder.generatePath( start, target, stance ) { tile = start, direction = nil, parent = nil, g = 0, f = 0 } -- Starting point. } + -- The currently evaluated node in the graph. + local current + while #openList > 0 do - local current = getNextNode( openList ) + current = getNextNode( openList ) -- Update lists. addToCLosedList( closedList, current ) -- Stop if we have found the target. if current.tile == target then - return finalizePath( current, stance ) + break; end -- Look for the next tile. @@ -231,6 +234,8 @@ function PathFinder.generatePath( start, target, stance ) end end end + + return finalizePath( current, stance ) end return PathFinder From 353a6c3d0be86403c2910afab25753b713dcef4f Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sun, 17 Dec 2017 12:54:08 +0100 Subject: [PATCH 092/178] Refactor Queue --- src/characters/Character.lua | 2 +- src/items/weapons/ProjectileQueue.lua | 4 +- src/util/ObjectPool.lua | 2 +- src/util/Queue.lua | 68 ++++++++++++++++----------- 4 files changed, 45 insertions(+), 31 deletions(-) diff --git a/src/characters/Character.lua b/src/characters/Character.lua index 6630cd98..7cb944cb 100644 --- a/src/characters/Character.lua +++ b/src/characters/Character.lua @@ -117,7 +117,7 @@ end function Character:initialize() self.actionPoints = DEFAULT_ACTION_POINTS - self.actions = Queue.new() + self.actions = Queue() self.fov = {} self.viewRange = 12 diff --git a/src/items/weapons/ProjectileQueue.lua b/src/items/weapons/ProjectileQueue.lua index 42b96156..6489c0eb 100644 --- a/src/items/weapons/ProjectileQueue.lua +++ b/src/items/weapons/ProjectileQueue.lua @@ -24,7 +24,7 @@ local ProjectileQueue = {}; function ProjectileQueue.new( character, tx, ty, th ) local self = {}; - local ammoQueue = Queue.new(); + local ammoQueue = Queue() local shots; local projectiles = {}; local index = 0; @@ -87,7 +87,7 @@ function ProjectileQueue.new( character, tx, ty, th ) -- function self:update( dt ) timer = timer - dt; - if timer <= 0 and not ammoQueue.isEmpty() then + if timer <= 0 and not ammoQueue:isEmpty() then spawnProjectile(); timer = weapon:getFiringDelay(); end diff --git a/src/util/ObjectPool.lua b/src/util/ObjectPool.lua index 92a1534b..83d3f990 100644 --- a/src/util/ObjectPool.lua +++ b/src/util/ObjectPool.lua @@ -6,7 +6,7 @@ local ObjectPool = {}; function ObjectPool.new( class, type ) local self = Object.new():addInstance( 'ObjectPool' ); - local queue = Queue.new(); + local queue = Queue() function self:request( ... ) local object; diff --git a/src/util/Queue.lua b/src/util/Queue.lua index 749de2b8..a69da2da 100644 --- a/src/util/Queue.lua +++ b/src/util/Queue.lua @@ -1,39 +1,53 @@ -local Queue = {}; +--- +-- @module Queue +-- -function Queue.new() - local self = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ - local queue = {}; +local Class = require( 'lib.Middleclass' ) - function self:enqueue( item ) - table.insert( queue, item ); - end +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ - function self:dequeue() - return table.remove( queue, 1 ); - end +local Queue = Class( 'Queue' ) - function self:peek() - return queue[1]; - end +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - function self:isEmpty() - return #queue == 0; - end +function Queue:initialize() + self.queue = {} +end + +function Queue:enqueue( item ) + table.insert( self.queue, item ) +end + +function Queue:dequeue() + return table.remove( self.queue, 1 ) +end - function self:getSize() - return #queue; - end +function Queue:peek() + return self.queue[1] +end - function self:clear() - queue = {}; - end +function Queue:isEmpty() + return #self.queue == 0 +end - function self:getItems() - return queue; - end +function Queue:getSize() + return #self.queue +end + +function Queue:clear() + self.queue = {} +end - return self; +function Queue:getItems() + return self.queue end -return Queue; +return Queue From 09dd3be5d53f2a828274b648ae267abf282b2c8b Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 18 Dec 2017 00:20:01 +0100 Subject: [PATCH 093/178] Refactor Node --- src/characters/Faction.lua | 2 +- src/characters/Factions.lua | 2 +- src/util/Node.lua | 63 ++++++++++++++++++++----------------- 3 files changed, 37 insertions(+), 30 deletions(-) diff --git a/src/characters/Faction.lua b/src/characters/Faction.lua index 2dc24031..a74cdc96 100644 --- a/src/characters/Faction.lua +++ b/src/characters/Faction.lua @@ -51,7 +51,7 @@ end -- @tparam Character character The character to add. -- function Faction:addCharacter( character ) - local node = Node.new( character ) + local node = Node( character ) -- Initialise root node. if not self.root then diff --git a/src/characters/Factions.lua b/src/characters/Factions.lua index abee49ed..7c5bb011 100644 --- a/src/characters/Factions.lua +++ b/src/characters/Factions.lua @@ -38,7 +38,7 @@ function Factions.new() -- @tparam Faction faction The faction to add. -- function self:addFaction( faction ) - local node = Node.new( faction ) + local node = Node( faction ) -- Initialise root node. if not root then diff --git a/src/util/Node.lua b/src/util/Node.lua index 69584c6e..199dc4be 100644 --- a/src/util/Node.lua +++ b/src/util/Node.lua @@ -1,39 +1,46 @@ -local Object = require('src.Object'); - -local Node = {}; - --- --- Creates a node to be used in a linked list. --- @param object (Object) The object to store in this Node. --- @return (Node) The newly created node. +-- A node to be used in a linked list. +-- @module Node -- -function Node.new( object ) - local self = Object.new():addInstance( 'Node' ); - local next; - local prev; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ + +local Class = require( 'lib.Middleclass' ) - function self:linkNext( node ) - next = node; - end +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ - function self:linkPrev( node ) - prev = node; - end +local Node = Class( 'Node' ) - function self:getNext() - return next; - end +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - function self:getPrev() - return prev; - end +function Node:initialize( object ) + self.object = object +end + +function Node:linkNext( node ) + self.next = node +end - function self:getObject() - return object; - end +function Node:linkPrev( node ) + self.prev = node +end + +function Node:getNext() + return self.next +end + +function Node:getPrev() + return self.prev +end - return self; +function Node:getObject() + return self.object end -return Node; +return Node From 1c8b1849623ca66ab6900ca8e787d0827dd0961f Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 18 Dec 2017 00:40:59 +0100 Subject: [PATCH 094/178] Refactor Tile --- src/characters/Factions.lua | 1 - src/map/tiles/Tile.lua | 498 +++++++++++++++++----------------- src/map/tiles/TileFactory.lua | 2 +- 3 files changed, 252 insertions(+), 249 deletions(-) diff --git a/src/characters/Factions.lua b/src/characters/Factions.lua index 7c5bb011..4e07346b 100644 --- a/src/characters/Factions.lua +++ b/src/characters/Factions.lua @@ -119,7 +119,6 @@ function Factions.new() function self:receive( event, ... ) if event == 'TILE_UPDATED' then local tile = ...; - assert( tile:instanceOf( 'Tile' ), 'Expected an object of type Tile.' ); active:getObject():regenerateFOVSelectively( tile ); end end diff --git a/src/map/tiles/Tile.lua b/src/map/tiles/Tile.lua index f725d719..de9c329d 100644 --- a/src/map/tiles/Tile.lua +++ b/src/map/tiles/Tile.lua @@ -1,302 +1,306 @@ -local Object = require( 'src.Object' ); -local Inventory = require( 'src.inventory.Inventory' ); +--- +-- @module Tile +-- + +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ + +local Class = require( 'lib.Middleclass' ) +local Inventory = require( 'src.inventory.Inventory' ) -- ------------------------------------------------ -- Module -- ------------------------------------------------ -local Tile = {}; +local Tile = Class( 'Tile' ) -- ------------------------------------------------ -- Constants -- ------------------------------------------------ -local WEIGHT_LIMIT = 1000; -local VOLUME_LIMIT = 1000; -local DEFAULT_HEIGHT = 10; +local WEIGHT_LIMIT = 1000 +local VOLUME_LIMIT = 1000 +local DEFAULT_HEIGHT = 10 -- ------------------------------------------------ --- Constructor +-- Public Methods -- ------------------------------------------------ --- -- Creates a new instance of the Tile class. --- @param x (number) The grid position along the x-axis. --- @param y (number) The grid position along the y-axis. --- @param template (table) The tile's template. --- @return (Tile) The new tile. +-- @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.new( x, y, template ) - local self = Object.new():addInstance( 'Tile' ); - - local id = template.id; - local movementCost = template.movementCost; - local passable = template.passable; - local spawn = template.spawn - - local spriteID; - local dirty; - local neighbours; - local character; - local worldObject; - local inventory = Inventory.new( WEIGHT_LIMIT, VOLUME_LIMIT ); - - -- ------------------------------------------------ - -- Public Methods - -- ------------------------------------------------ - - --- - -- Adds a table containing the neighbouring tiles. Note that some tiles - -- might be nil. - -- @param nneighbours (table) A table containing the neighbouring tiles. - -- - function self:addNeighbours( nneighbours ) - neighbours = nneighbours; - end +function Tile:initialize( x, y, id, cost, passable, spawn ) + self.x = x + self.y = y - --- - -- Adds a world object to this tile. - -- @param nworldObject (WorldObject) The WorldObject to add. - -- - function self:addWorldObject( nworldObject ) - worldObject = nworldObject; - self:setDirty( true ); - end + self.id = id + self.cost = cost + self.passable = passable + self.spawn = spawn - --- - -- Hits the tile with a certain amount of damage. The tile will distribute - -- the damage to any character or world object which it contains. - -- @param damage (number) The damage the tile receives. - -- @param damageType (string) The type of damage the tile is hit with. - -- - function self:hit( damage, damageType ) - if self:isOccupied() then - character:hit( damage, damageType ); - elseif self:hasWorldObject() and worldObject:isDestructible() then - worldObject:damage( damage, damageType ); - end - end + self.inventory = Inventory.new( WEIGHT_LIMIT, VOLUME_LIMIT ) +end - --- - -- Removes the character from this tile and marks it for updating. - -- - function self:removeCharacter() - character = nil; - self:setDirty( true ); - 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 - --- - -- Removes the worldObject from this tile and marks it for updating. - -- - function self:removeWorldObject() - worldObject = nil; - self:setDirty( true ); +--- +-- 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. +-- @tparam number damage The damage the tile receives. +-- @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 ) + elseif self:hasWorldObject() and self.worldObject:isDestructible() then + self.worldObject:damage( damage, damageType ) end +end - function self:serialize() - local t = { - ['id'] = id, - ['x'] = x, - ['y'] = y - }; +--- +-- Removes the character from this tile and marks it for updating. +-- +function Tile:removeCharacter() + self.character = nil + self:setDirty( true ) +end - if self:hasWorldObject() then - t.worldObject = worldObject:serialize(); - end +--- +-- Removes the worldObject from this tile and marks it for updating. +-- +function Tile:removeWorldObject() + self.worldObject = nil + self:setDirty( true ) +end - if not inventory:isEmpty() then - t['inventory'] = inventory:serialize(); - end +--- +-- Serializes the tile. +-- @treturn table The serialized Tile object. +-- +function Tile:serialize() + local t = { + ['id'] = self.id, + ['x'] = self.x, + ['y'] = self.y + } + + if self:hasWorldObject() then + t.worldObject = self.worldObject:serialize() + end - return t; + if not self.inventory:isEmpty() then + t['inventory'] = self.inventory:serialize() end - -- ------------------------------------------------ - -- Getters - -- ------------------------------------------------ + return t +end - --- - -- Returns the character standing on this tile. - -- @return (Character) The character standing on the tile. - -- - function self:getCharacter() - return character; - end +-- ------------------------------------------------ +-- Getters +-- ------------------------------------------------ - --- - -- Returns the tile's unique spriteID. - -- @return (number) The tile's spriteID. - -- - function self:getSpriteID() - return spriteID; - end +--- +-- Returns the character standing on this tile. +-- @treturn Character The character standing on the tile. +-- +function Tile:getCharacter() + return self.character +end - --- - -- Returns the cost it takes a character to traverse this tile. - -- @param stance (string) The stance the character is currently in. - -- @return (number) The movement cost for this tile. - -- - function self:getMovementCost( stance ) - return movementCost[stance]; - end +--- +-- Returns the tile's unique spriteID. +-- @treturn number The tile's spriteID. +-- +function Tile:getSpriteID() + return self.spriteID +end - --- - -- Returns a table containing this tile's neighbours. - -- @return (table) A table containing the neighbouring tiles. - -- - function self:getNeighbours() - return neighbours; - end +--- +-- Returns the cost it takes a character to traverse this tile. +-- @tparam string stance The stance the character is currently in. +-- @treturn number The movement cost for this tile. +-- +function Tile:getMovementCost( stance ) + return self.cost[stance] +end - --- - -- Returns the tile's grid position. - -- @return (number) The tile's position along the x-axis of the grid. - -- @return (number) The tile's position along the y-axis of the grid. - -- - function self:getPosition() - return x, y; - 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. - -- @return (Inventory) The tile's inventory. - -- - function self:getInventory() - return inventory; - 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 - --- - -- Returns the height of the tile. If it contains a character or a - -- worldObject it returns the size of those, if not it returns a default - -- value. - -- @treturn number The height of this tile. - -- - function self:getHeight() - if worldObject then - return worldObject:getHeight() - elseif character then - return character:getHeight() - end - return DEFAULT_HEIGHT - end +--- +-- Gets the tile's inventory. +-- @treturn Inventory The tile's inventory. +-- +function Tile:getInventory() + return self.inventory +end - --- - -- Returns the tile's ID. - -- @return (string) The tile's ID. - -- - function self:getID() - return id; +--- +-- Returns the height of the tile. If it contains a character or a +-- worldObject it returns the size of those, if not it returns a default +-- value. +-- @treturn number The height of this tile. +-- +function Tile:getHeight() + if self.worldObject then + return self.worldObject:getHeight() + elseif self.character then + return self.character:getHeight() end + return DEFAULT_HEIGHT +end - --- - -- Returns the world object located on this tile. - -- @return (WorldObject) The WorldObject. - -- - function self:getWorldObject() - return worldObject; - end +--- +-- Returns the tile's ID. +-- @treturn string The tile's ID. +-- +function Tile:getID() + return self.id +end - --- - -- Returns the tile's grid position along the x-axis. - -- @return (number) The tile's position along the x-axis of the grid. - -- - function self:getX() - return x; - end +--- +-- Returns the world object located on this tile. +-- @treturn WorldObject The WorldObject. +-- +function Tile:getWorldObject() + return self.worldObject +end - --- - -- Returns the tile's grid position along the y-axis. - -- @return (number) The tile's position along the y-axis of the grid. - -- - function self:getY() - return y; - 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 - --- - -- Checks if the tile has a world object. - -- @return (boolean) True if a WorldObject is located on the tile. - -- - function self:hasWorldObject() - return worldObject ~= nil; - 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 a given tile is adjacent to this tile. - -- @return (boolean) True if the tiles are adjacent to each other. - -- - function self:isAdjacent( tile ) - for _, neighbour in pairs( neighbours ) do - if neighbour == tile then - return true; - 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. +-- +function Tile:isAdjacent( tile ) + for _, neighbour in pairs( self.neighbours ) do + if neighbour == tile then + return true end end +end - --- - -- Checks if the tile is marked for an update. - -- @return (boolean) True if the tile is dirty. - -- - function self:isDirty() - return dirty; - end +--- +-- Checks if the tile is marked for an update. +-- @treturn boolean True if the tile is dirty. +-- +function Tile:isDirty() + return self.dirty +end - --- - -- Checks if the tile has a character on it. - -- @return (boolean) True a character is standing on the tile. - -- - function self:isOccupied() - return character ~= nil; - 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. - -- @return (boolean) True if the tile is passable. - -- - function self:isPassable() - if passable and self:hasWorldObject() then - return worldObject:isPassable(); - end - return passable; +--- +-- Checks if the tile is passable. +-- @treturn boolean True if the tile is passable. +-- +function Tile:isPassable() + if self.passable and self:hasWorldObject() then + return self.worldObject:isPassable() end + return self.passable +end - --- - -- Determines wether the tile is valid for spawning. - -- @treturn boolean True if the tile is valid for spawning. - -- - function self:isSpawn() - return spawn - end +--- +-- Determines wether the tile is valid for spawning. +-- @treturn boolean True if the tile is valid for spawning. +-- +function Tile:isSpawn() + return self.spawn +end - -- ------------------------------------------------ - -- Setters - -- ------------------------------------------------ - - --- - -- Sets a character for this tile and marks the tile for updating. - -- @tparam Character nchar The character to add. - -- - function self:setCharacter( nchar ) - character = nchar - self:setDirty( true ) - end +-- ------------------------------------------------ +-- Setters +-- ------------------------------------------------ - --- - -- Sets the dirty state of the tile. - -- @param ndirty (boolean) Wether the tile should be updated or not. - -- - function self:setDirty( ndirty ) - dirty = ndirty; - end +--- +-- 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 tile's unique spriteID. - -- @param nid (number) The tile's new spriteID. - -- - function self:setSpriteID( nid ) - spriteID = nid; - end +--- +-- Sets the dirty state of the tile. +-- @tparam boolean dirty Wether the tile should be updated or not. +-- +function Tile:setDirty( dirty ) + self.dirty = dirty +end - return self; +--- +-- Sets the tile's unique spriteID. +-- @tparam number id The tile's new spriteID. +-- +function Tile:setSpriteID( id ) + self.spriteID = id end -return Tile; +return Tile diff --git a/src/map/tiles/TileFactory.lua b/src/map/tiles/TileFactory.lua index ca71ccd9..9a34a842 100644 --- a/src/map/tiles/TileFactory.lua +++ b/src/map/tiles/TileFactory.lua @@ -70,7 +70,7 @@ end function TileFactory.create( x, y, id ) local template = tiles[id] assert( template, string.format( 'Requested tile id (%s) doesn\'t exist!', id )) - return Tile.new( x, y, template ) + return Tile( x, y, template.id, template.movementCost, template.passable, template.spawn ) end --- From a20c818ac0edb302f4d0cc9c2bdd75da703db55b Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 18 Dec 2017 00:43:39 +0100 Subject: [PATCH 095/178] Refactor BTLeaf --- .../ai/behaviortree/leafs/BTLeaf.lua | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/characters/ai/behaviortree/leafs/BTLeaf.lua b/src/characters/ai/behaviortree/leafs/BTLeaf.lua index 8412decc..7bc2045b 100644 --- a/src/characters/ai/behaviortree/leafs/BTLeaf.lua +++ b/src/characters/ai/behaviortree/leafs/BTLeaf.lua @@ -1,15 +1,26 @@ -local Object = require( 'src.Object' ); +--- +-- The base class for all behavior tree leafs. +-- @module BTLeaf +-- -local BTLeaf = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ -function BTLeaf.new() - local self = Object.new():addInstance( 'BTLeaf' ); +local Class = require( 'lib.Middleclass' ) - function self:traverse() - return true; - end +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ - return self; +local BTLeaf = Class( 'BTLeaf' ) + +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ + +function BTLeaf:traverse() + return true end -return BTLeaf; +return BTLeaf From c20a1913b082d6400f5295bfdbace929b33dd757 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 18 Dec 2017 00:47:17 +0100 Subject: [PATCH 096/178] Refactor BTAquireTarget --- .../ai/behaviortree/leafs/BTAquireTarget.lua | 106 +++++++++--------- 1 file changed, 54 insertions(+), 52 deletions(-) diff --git a/src/characters/ai/behaviortree/leafs/BTAquireTarget.lua b/src/characters/ai/behaviortree/leafs/BTAquireTarget.lua index c11ac10c..ef26dd22 100644 --- a/src/characters/ai/behaviortree/leafs/BTAquireTarget.lua +++ b/src/characters/ai/behaviortree/leafs/BTAquireTarget.lua @@ -1,80 +1,82 @@ -local Log = require( 'src.util.Log' ); -local BTLeaf = require( 'src.characters.ai.behaviortree.leafs.BTLeaf' ); +--- +-- @module BTAquireTarget +-- + +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ + +local Log = require( 'src.util.Log' ) +local BTLeaf = require( 'src.characters.ai.behaviortree.leafs.BTLeaf' ) -- ------------------------------------------------ -- Module -- ------------------------------------------------ -local BTAquireTarget = {}; +local BTAquireTarget = BTLeaf:subclass( 'BTAquireTarget' ) -- ------------------------------------------------ -- Constants -- ------------------------------------------------ -local FACTIONS = require( 'src.constants.FACTIONS' ); +local FACTIONS = require( 'src.constants.FACTIONS' ) -- ------------------------------------------------ -- Constructor -- ------------------------------------------------ -function BTAquireTarget.new() - local self = BTLeaf.new():addInstance( 'BTAquireTarget' ); - - function self:traverse( ... ) - local blackboard, character = ...; +function BTAquireTarget:traverse( ... ) + local blackboard, character = ... - local tiles = {}; + 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 + -- 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]; - if tile:isOccupied() - and not tile:getCharacter():isDead() - and tile:getCharacter():getFaction():getType() ~= FACTIONS.NEUTRAL - and tile:getCharacter():getFaction():getType() ~= character:getFaction():getType() then - enemies[#enemies + 1] = tile; - end + -- Get all characters visible to this character. + local enemies = {} + for i = 1, #tiles do + local tile = tiles[i] + if tile:isOccupied() + and not tile:getCharacter():isDead() + and tile:getCharacter():getFaction():getType() ~= FACTIONS.NEUTRAL + and tile:getCharacter():getFaction():getType() ~= character:getFaction():getType() then + enemies[#enemies + 1] = tile end + end - -- Select the closest enemy. - local target; - for i = 1, #enemies do - local t = enemies[i]; - if not target then - target = t; - else - local distanceX = math.abs( target:getX() - character:getTile():getX() ); - local distanceY = math.abs( target:getY() - character:getTile():getY() ); - - local ndistanceX = math.abs( t:getX() - character:getTile():getX() ); - local ndistanceY = math.abs( t:getY() - character:getTile():getY() ); - - if ndistanceX + ndistanceY < distanceX + distanceY then - target = t; - end + -- Select the closest enemy. + local target + for i = 1, #enemies do + local t = enemies[i] + if not target then + target = t + else + local distanceX = math.abs( target:getX() - character:getTile():getX() ) + local distanceY = math.abs( target:getY() - character:getTile():getY() ) + + local ndistanceX = math.abs( t:getX() - character:getTile():getX() ) + local ndistanceY = math.abs( t:getY() - character:getTile():getY() ) + + if ndistanceX + ndistanceY < distanceX + distanceY then + target = t end end + end - if target then - Log.debug( string.format( 'Target found at coordinates %d,%d', target:getPosition() ), 'BTAquireTarget' ); - blackboard.target = target; - return true; - end - - Log.debug( 'No target found', 'BTAquireTarget' ); - return false; + if target then + Log.debug( string.format( 'Target found at coordinates %d,%d', target:getPosition() ), 'BTAquireTarget' ) + blackboard.target = target + return true end - return self; + Log.debug( 'No target found', 'BTAquireTarget' ) + return false end -return BTAquireTarget; +return BTAquireTarget From fe9b1046e823e53cc5169adbf1e5cf0bb469c54a Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 18 Dec 2017 00:47:26 +0100 Subject: [PATCH 097/178] Refactor BTAttackTarget --- .../ai/behaviortree/leafs/BTAttackTarget.lua | 42 ++++++++++++------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/src/characters/ai/behaviortree/leafs/BTAttackTarget.lua b/src/characters/ai/behaviortree/leafs/BTAttackTarget.lua index 1dd2e76d..cccb2357 100644 --- a/src/characters/ai/behaviortree/leafs/BTAttackTarget.lua +++ b/src/characters/ai/behaviortree/leafs/BTAttackTarget.lua @@ -1,26 +1,36 @@ -local Log = require( 'src.util.Log' ); -local BTLeaf = require( 'src.characters.ai.behaviortree.leafs.BTLeaf' ); +--- +-- @module BTAttackTarget +-- + +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ + +local Log = require( 'src.util.Log' ) +local BTLeaf = require( 'src.characters.ai.behaviortree.leafs.BTLeaf' ) local RangedAttack = require( 'src.characters.actions.RangedAttack' ) -local BTAttackTarget = {}; +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ -function BTAttackTarget.new() - local self = BTLeaf.new():addInstance( 'BTAttackTarget' ); +local BTAttackTarget = BTLeaf:subclass( 'BTAttackTarget' ) - function self:traverse( ... ) - local blackboard, character = ...; +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - local success = character:enqueueAction( RangedAttack( character, blackboard.target )) - if success then - Log.debug( 'Character attacks target', 'BTAttackTarget' ); - return true; - end +function BTAttackTarget:traverse( ... ) + local blackboard, character = ... - Log.debug( 'Character can not attack target', 'BTAttackTarget' ); - return false; + local success = character:enqueueAction( RangedAttack( character, blackboard.target )) + if success then + Log.debug( 'Character attacks target', 'BTAttackTarget' ) + return true end - return self; + Log.debug( 'Character can not attack target', 'BTAttackTarget' ) + return false end -return BTAttackTarget; +return BTAttackTarget From d909f1586fdd9b29f7bea621909070426053519d Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 18 Dec 2017 00:50:44 +0100 Subject: [PATCH 098/178] Refactor BTCanAttack --- .../ai/behaviortree/leafs/BTCanAttack.lua | 36 ++++++++++++------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/src/characters/ai/behaviortree/leafs/BTCanAttack.lua b/src/characters/ai/behaviortree/leafs/BTCanAttack.lua index 9d2c76c4..f54dba08 100644 --- a/src/characters/ai/behaviortree/leafs/BTCanAttack.lua +++ b/src/characters/ai/behaviortree/leafs/BTCanAttack.lua @@ -1,20 +1,30 @@ -local Log = require( 'src.util.Log' ); -local BTLeaf = require( 'src.characters.ai.behaviortree.leafs.BTLeaf' ); +--- +-- @module BTCanAttack +-- -local BTCanAttack = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ -function BTCanAttack.new() - local self = BTLeaf.new():addInstance( 'BTCanAttack' ); +local Log = require( 'src.util.Log' ) +local BTLeaf = require( 'src.characters.ai.behaviortree.leafs.BTLeaf' ) - function self:traverse( ... ) - local _, character = ...; +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ - local result = not character:getWeapon():getMagazine():isEmpty(); - Log.debug( result, 'BTCanAttack' ); - return result; - end +local BTCanAttack = BTLeaf:subclass( 'BTCanAttack' ) - return self; +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ + +function BTCanAttack:traverse( ... ) + local _, character = ... + + local result = not character:getWeapon():getMagazine():isEmpty() + Log.debug( result, 'BTCanAttack' ) + return result end -return BTCanAttack; +return BTCanAttack From 5d4bf56559832a4e58b7f7fe92023433eaf5e776 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 18 Dec 2017 00:50:51 +0100 Subject: [PATCH 099/178] Refactor BTCanReload --- .../ai/behaviortree/leafs/BTCanReload.lua | 48 +++++++++++-------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/src/characters/ai/behaviortree/leafs/BTCanReload.lua b/src/characters/ai/behaviortree/leafs/BTCanReload.lua index 296cb175..6fbe8c84 100644 --- a/src/characters/ai/behaviortree/leafs/BTCanReload.lua +++ b/src/characters/ai/behaviortree/leafs/BTCanReload.lua @@ -1,28 +1,38 @@ -local Log = require( 'src.util.Log' ); -local BTLeaf = require( 'src.characters.ai.behaviortree.leafs.BTLeaf' ); +--- +-- @module BTCanReload +-- -local BTCanReload = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ -function BTCanReload.new() - local self = BTLeaf.new():addInstance( 'BTCanReload' ); +local Log = require( 'src.util.Log' ) +local BTLeaf = require( 'src.characters.ai.behaviortree.leafs.BTLeaf' ) - function self:traverse( ... ) - local _, character = ...; +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ - local weapon = character:getWeapon(); - local inventory = character:getInventory(); - for _, item in pairs( inventory:getItems() ) do - if item:instanceOf( 'Magazine' ) and item:getCaliber() == weapon:getCaliber() then - Log.debug( 'Character can reload', 'BTCanReload' ); - return true; - end - end +local BTCanReload = BTLeaf:subclass( 'BTCanReload' ) + +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - Log.debug( 'Character can not reload', 'BTCanReload' ); - return false; +function BTCanReload:traverse( ... ) + local _, character = ... + + local weapon = character:getWeapon() + local inventory = character:getInventory() + for _, item in pairs( inventory:getItems() ) do + if item:instanceOf( 'Magazine' ) and item:getCaliber() == weapon:getCaliber() then + Log.debug( 'Character can reload', 'BTCanReload' ) + return true + end end - return self; + Log.debug( 'Character can not reload', 'BTCanReload' ) + return false end -return BTCanReload; +return BTCanReload From 0560be7f8018443f954d225640ad1887f50c9580 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 18 Dec 2017 00:52:17 +0100 Subject: [PATCH 100/178] Refactor BTCanSeeItem --- .../ai/behaviortree/leafs/BTCanSeeItem.lua | 104 ++++++++++-------- 1 file changed, 57 insertions(+), 47 deletions(-) diff --git a/src/characters/ai/behaviortree/leafs/BTCanSeeItem.lua b/src/characters/ai/behaviortree/leafs/BTCanSeeItem.lua index 5dbc8b85..55d8f85c 100644 --- a/src/characters/ai/behaviortree/leafs/BTCanSeeItem.lua +++ b/src/characters/ai/behaviortree/leafs/BTCanSeeItem.lua @@ -1,63 +1,73 @@ -local Log = require( 'src.util.Log' ); -local BTLeaf = require( 'src.characters.ai.behaviortree.leafs.BTLeaf' ); +--- +-- @module BTCanSeeItem +-- -local BTCanSeeItem = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ -function BTCanSeeItem.new() - local self = BTLeaf.new():addInstance( 'BTCanSeeItem' ); +local Log = require( 'src.util.Log' ) +local BTLeaf = require( 'src.characters.ai.behaviortree.leafs.BTLeaf' ) - function self:traverse( ... ) - local blackboard, character = ...; +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ - local tiles = {}; +local BTCanSeeItem = BTLeaf:subclass( 'BTCanSeeItem' ) - -- 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 +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - -- Look for any items on those tiles. - local items = {}; - for i = 1, #tiles do - local tile = tiles[i]; - if not tile:getInventory():isEmpty() then - items[#items + 1] = tile; - end +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 - -- Select the closest items. - local target; - for i = 1, #items do - local t = items[i]; - if not target then - target = t; - else - local distanceX = math.abs( target:getX() - character:getTile():getX() ); - local distanceY = math.abs( target:getY() - character:getTile():getY() ); - - local ndistanceX = math.abs( t:getX() - character:getTile():getX() ); - local ndistanceY = math.abs( t:getY() - character:getTile():getY() ); - - if ndistanceX + ndistanceY < distanceX + distanceY then - target = t; - end - end + -- Look for any items on those tiles. + local items = {} + for i = 1, #tiles do + local tile = tiles[i] + if not tile:getInventory():isEmpty() then + items[#items + 1] = tile end + end + + -- Select the closest items. + local target + for i = 1, #items do + local t = items[i] + if not target then + target = t + else + local distanceX = math.abs( target:getX() - character:getTile():getX() ) + local distanceY = math.abs( target:getY() - character:getTile():getY() ) - if target then - Log.debug( string.format( 'Item found at coordinates %d,%d', target:getPosition() ), 'BTCanSeeItem' ); - blackboard.target = target; - return true; + local ndistanceX = math.abs( t:getX() - character:getTile():getX() ) + local ndistanceY = math.abs( t:getY() - character:getTile():getY() ) + + if ndistanceX + ndistanceY < distanceX + distanceY then + target = t + end end + end - Log.debug( 'No items found', 'BTCanSeeItem' ); - return false; + if target then + Log.debug( string.format( 'Item found at coordinates %d,%d', target:getPosition() ), 'BTCanSeeItem' ) + blackboard.target = target + return true end - return self; + Log.debug( 'No items found', 'BTCanSeeItem' ) + return false end -return BTCanSeeItem; +return BTCanSeeItem From a32698941021df331ba99646d714b7339c5d18ba Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 18 Dec 2017 00:53:35 +0100 Subject: [PATCH 101/178] Refactor BTHasMeleeWeapon --- .../behaviortree/leafs/BTHasMeleeWeapon.lua | 42 ++++++++++++------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/src/characters/ai/behaviortree/leafs/BTHasMeleeWeapon.lua b/src/characters/ai/behaviortree/leafs/BTHasMeleeWeapon.lua index fc26716b..4bbcea63 100644 --- a/src/characters/ai/behaviortree/leafs/BTHasMeleeWeapon.lua +++ b/src/characters/ai/behaviortree/leafs/BTHasMeleeWeapon.lua @@ -1,22 +1,36 @@ -local Log = require( 'src.util.Log' ); -local BTLeaf = require( 'src.characters.ai.behaviortree.leafs.BTLeaf' ); +--- +-- @module BTHasMeleeWeapon +-- -local BTHasMeleeWeapon = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ -local WEAPON_TYPES = require( 'src.constants.WEAPON_TYPES' ) +local Log = require( 'src.util.Log' ) +local BTLeaf = require( 'src.characters.ai.behaviortree.leafs.BTLeaf' ) + +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ + +local BTHasMeleeWeapon = BTLeaf:subclass( 'BTHasMeleeWeapon' ) -function BTHasMeleeWeapon.new() - local self = BTLeaf.new():addInstance( 'BTHasMeleeWeapon' ); +-- ------------------------------------------------ +-- Constants +-- ------------------------------------------------ + +local WEAPON_TYPES = require( 'src.constants.WEAPON_TYPES' ) - function self:traverse( ... ) - local _, character = ...; +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - local result = character:getWeapon():getSubType() == WEAPON_TYPES.MELEE; - Log.debug( result, 'BTHasMeleeWeapon' ); - return result; - end +function BTHasMeleeWeapon:traverse( ... ) + local _, character = ... - return self; + local result = character:getWeapon():getSubType() == WEAPON_TYPES.MELEE + Log.debug( result, 'BTHasMeleeWeapon' ) + return result end -return BTHasMeleeWeapon; +return BTHasMeleeWeapon From 3f959b5b0b05623f11e2b667567149211c79b182 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 18 Dec 2017 00:54:55 +0100 Subject: [PATCH 102/178] Refactor BTHasRangedWeapon --- .../behaviortree/leafs/BTHasRangedWeapon.lua | 42 ++++++++++++------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/src/characters/ai/behaviortree/leafs/BTHasRangedWeapon.lua b/src/characters/ai/behaviortree/leafs/BTHasRangedWeapon.lua index 0e8c4494..364bd33b 100644 --- a/src/characters/ai/behaviortree/leafs/BTHasRangedWeapon.lua +++ b/src/characters/ai/behaviortree/leafs/BTHasRangedWeapon.lua @@ -1,22 +1,36 @@ -local Log = require( 'src.util.Log' ); -local BTLeaf = require( 'src.characters.ai.behaviortree.leafs.BTLeaf' ); +--- +-- @module BTHasRangedWeapon +-- -local BTHasRangedWeapon = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ -local WEAPON_TYPES = require( 'src.constants.WEAPON_TYPES' ) +local Log = require( 'src.util.Log' ) +local BTLeaf = require( 'src.characters.ai.behaviortree.leafs.BTLeaf' ) + +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ + +local BTHasRangedWeapon = BTLeaf:subclass( 'BTHasRangedWeapon' ) -function BTHasRangedWeapon.new() - local self = BTLeaf.new():addInstance( 'BTHasRangedWeapon' ); +-- ------------------------------------------------ +-- Constants +-- ------------------------------------------------ + +local WEAPON_TYPES = require( 'src.constants.WEAPON_TYPES' ) - function self:traverse( ... ) - local _, character = ...; +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - local result = character:getWeapon():getSubType() == WEAPON_TYPES.RANGED; - Log.debug( result, 'BTHasRangedWeapon' ); - return result; - end +function BTHasRangedWeapon:traverse( ... ) + local _, character = ... - return self; + local result = character:getWeapon():getSubType() == WEAPON_TYPES.RANGED + Log.debug( result, 'BTHasRangedWeapon' ) + return result end -return BTHasRangedWeapon; +return BTHasRangedWeapon From 45b59b7334d2777b3e8b17294024e1844ecf6d02 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 18 Dec 2017 00:56:03 +0100 Subject: [PATCH 103/178] Refactor BTHasThrowingWeapon --- .../leafs/BTHasThrowingWeapon.lua | 42 ++++++++++++------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/src/characters/ai/behaviortree/leafs/BTHasThrowingWeapon.lua b/src/characters/ai/behaviortree/leafs/BTHasThrowingWeapon.lua index 1c805d08..99f78a0f 100644 --- a/src/characters/ai/behaviortree/leafs/BTHasThrowingWeapon.lua +++ b/src/characters/ai/behaviortree/leafs/BTHasThrowingWeapon.lua @@ -1,22 +1,36 @@ -local Log = require( 'src.util.Log' ); -local BTLeaf = require( 'src.characters.ai.behaviortree.leafs.BTLeaf' ); +--- +-- @module BTHasThrowingWeapon +-- -local BTHasThrowingWeapon = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ -local WEAPON_TYPES = require( 'src.constants.WEAPON_TYPES' ) +local Log = require( 'src.util.Log' ) +local BTLeaf = require( 'src.characters.ai.behaviortree.leafs.BTLeaf' ) + +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ + +local BTHasThrowingWeapon = BTLeaf:subclass( 'BTHasThrowingWeapon' ) -function BTHasThrowingWeapon.new() - local self = BTLeaf.new():addInstance( 'BTHasThrowingWeapon' ); +-- ------------------------------------------------ +-- Constants +-- ------------------------------------------------ + +local WEAPON_TYPES = require( 'src.constants.WEAPON_TYPES' ) - function self:traverse( ... ) - local _, character = ...; +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - local result = character:getWeapon():getSubType() == WEAPON_TYPES.THROWN; - Log.debug( result, 'BTHasThrowingWeapon' ); - return result; - end +function BTHasThrowingWeapon:traverse( ... ) + local _, character = ... - return self; + local result = character:getWeapon():getSubType() == WEAPON_TYPES.THROWN + Log.debug( result, 'BTHasThrowingWeapon' ) + return result end -return BTHasThrowingWeapon; +return BTHasThrowingWeapon From 491ebb0554d62bcfb1390a3297131a96c5826f8c Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 18 Dec 2017 00:56:58 +0100 Subject: [PATCH 104/178] Refactor BTHasWeapon --- .../ai/behaviortree/leafs/BTHasWeapon.lua | 36 ++++++++++++------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/src/characters/ai/behaviortree/leafs/BTHasWeapon.lua b/src/characters/ai/behaviortree/leafs/BTHasWeapon.lua index 2d457a93..470b3dbe 100644 --- a/src/characters/ai/behaviortree/leafs/BTHasWeapon.lua +++ b/src/characters/ai/behaviortree/leafs/BTHasWeapon.lua @@ -1,20 +1,30 @@ -local Log = require( 'src.util.Log' ); -local BTLeaf = require( 'src.characters.ai.behaviortree.leafs.BTLeaf' ); +--- +-- @module BTHasWeapon +-- -local BTHasWeapon = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ -function BTHasWeapon.new() - local self = BTLeaf.new():addInstance( 'BTHasWeapon' ); +local Log = require( 'src.util.Log' ) +local BTLeaf = require( 'src.characters.ai.behaviortree.leafs.BTLeaf' ) - function self:traverse( ... ) - local _, character = ...; +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ - local result = character:getWeapon() ~= nil; - Log.debug( result, 'BTHasWeapon' ); - return result; - end +local BTHasWeapon = BTLeaf:subclass( 'BTHasWeapon' ) - return self; +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ + +function BTHasWeapon:traverse( ... ) + local _, character = ... + + local result = character:getWeapon() ~= nil + Log.debug( result, 'BTHasWeapon' ) + return result end -return BTHasWeapon; +return BTHasWeapon From fa0eb21ccb86c62722cf3fd31c4f4b8a51767fee Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 18 Dec 2017 00:58:24 +0100 Subject: [PATCH 105/178] Refactor BTIsAdjacentToTarget --- .../leafs/BTIsAdjacentToTarget.lua | 36 ++++++++++++------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/src/characters/ai/behaviortree/leafs/BTIsAdjacentToTarget.lua b/src/characters/ai/behaviortree/leafs/BTIsAdjacentToTarget.lua index 27f18b0f..7cee8cb4 100644 --- a/src/characters/ai/behaviortree/leafs/BTIsAdjacentToTarget.lua +++ b/src/characters/ai/behaviortree/leafs/BTIsAdjacentToTarget.lua @@ -1,20 +1,30 @@ -local Log = require( 'src.util.Log' ); -local BTLeaf = require( 'src.characters.ai.behaviortree.leafs.BTLeaf' ); +--- +-- @module BTIsAdjacentToTarget +-- -local BTIsAdjacentToTarget = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ -function BTIsAdjacentToTarget.new() - local self = BTLeaf.new():addInstance( 'BTIsAdjacentToTarget' ); +local Log = require( 'src.util.Log' ) +local BTLeaf = require( 'src.characters.ai.behaviortree.leafs.BTLeaf' ) - function self:traverse( ... ) - local blackboard, character = ...; +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ - local result = character:getTile():isAdjacent( blackboard.target ); - Log.debug( result, 'BTIsAdjacentToTarget' ); - return result; - end +local BTIsAdjacentToTarget = BTLeaf:subclass( 'BTIsAdjacentToTarget' ) - return self; +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ + +function BTIsAdjacentToTarget:traverse( ... ) + local blackboard, character = ... + + local result = character:getTile():isAdjacent( blackboard.target ) + Log.debug( result, 'BTIsAdjacentToTarget' ) + return result end -return BTIsAdjacentToTarget; +return BTIsAdjacentToTarget From ace0a9a93bd145e82492198eee7ee0c2951a5632 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 18 Dec 2017 01:00:04 +0100 Subject: [PATCH 106/178] Refactor BTMeleeAttack --- .../ai/behaviortree/leafs/BTMeleeAttack.lua | 44 ++++++++++++------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/src/characters/ai/behaviortree/leafs/BTMeleeAttack.lua b/src/characters/ai/behaviortree/leafs/BTMeleeAttack.lua index cdebaee4..f5adfee1 100644 --- a/src/characters/ai/behaviortree/leafs/BTMeleeAttack.lua +++ b/src/characters/ai/behaviortree/leafs/BTMeleeAttack.lua @@ -1,26 +1,36 @@ -local Log = require( 'src.util.Log' ); -local BTLeaf = require( 'src.characters.ai.behaviortree.leafs.BTLeaf' ); -local MeleeAttack = require( 'src.characters.actions.MeleeAttack' ); +--- +-- @module BTMeleeAttack +-- -local BTMeleeAttack = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ -function BTMeleeAttack.new() - local self = BTLeaf.new():addInstance( 'BTMeleeAttack' ); +local Log = require( 'src.util.Log' ) +local BTLeaf = require( 'src.characters.ai.behaviortree.leafs.BTLeaf' ) +local MeleeAttack = require( 'src.characters.actions.MeleeAttack' ) - function self:traverse( ... ) - local blackboard, character = ...; +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ - local success = character:enqueueAction( MeleeAttack( character, blackboard.target )) - if success then - Log.debug( 'Character attacks target', 'BTMeleeAttack' ); - return true; - end +local BTMeleeAttack = BTLeaf:subclass( 'BTMeleeAttack' ) - Log.debug( 'Character can not attack target', 'BTMeleeAttack' ); - return false; +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ + +function BTMeleeAttack:traverse( ... ) + local blackboard, character = ... + + local success = character:enqueueAction( MeleeAttack( character, blackboard.target )) + if success then + Log.debug( 'Character attacks target', 'BTMeleeAttack' ) + return true end - return self; + Log.debug( 'Character can not attack target', 'BTMeleeAttack' ) + return false end -return BTMeleeAttack; +return BTMeleeAttack From dadbb8034c2c704446fe2017d2ed6f5c6b96e3c1 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 18 Dec 2017 01:04:34 +0100 Subject: [PATCH 107/178] Refactor BTMoveToTarget --- .../ai/behaviortree/leafs/BTMoveToTarget.lua | 106 ++++++++++-------- 1 file changed, 59 insertions(+), 47 deletions(-) diff --git a/src/characters/ai/behaviortree/leafs/BTMoveToTarget.lua b/src/characters/ai/behaviortree/leafs/BTMoveToTarget.lua index 0ad0dc9a..ba80739f 100644 --- a/src/characters/ai/behaviortree/leafs/BTMoveToTarget.lua +++ b/src/characters/ai/behaviortree/leafs/BTMoveToTarget.lua @@ -1,63 +1,75 @@ -local Log = require( 'src.util.Log' ); -local BTLeaf = require( 'src.characters.ai.behaviortree.leafs.BTLeaf' ); -local PathFinder = require( 'src.characters.pathfinding.PathFinder' ); +--- +-- @module BTMoveToTarget +-- -local BTMoveToTarget = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ -function BTMoveToTarget.new() - local self = BTLeaf.new():addInstance( 'BTMoveToTarget' ); +local Log = require( 'src.util.Log' ) +local BTLeaf = require( 'src.characters.ai.behaviortree.leafs.BTLeaf' ) +local PathFinder = require( 'src.characters.pathfinding.PathFinder' ) - local path; +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ - local function generatePath( target, character ) - if target and target:isPassable() and not target:isOccupied() then - path = PathFinder.generatePath( character:getTile(), target, character:getStance() ) - end +local BTMoveToTarget = BTLeaf:subclass( 'BTMoveToTarget' ) + +-- ------------------------------------------------ +-- Private Methods +-- ------------------------------------------------ + +local function generatePath( target, character ) + if target and target:isPassable() and not target:isOccupied() then + return PathFinder.generatePath( character:getTile(), target, character:getStance() ) end +end + +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - function self:traverse( ... ) - local blackboard, character = ...; - - local closest; - local distance; - - -- 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 not closest then - closest = neighbour; - local px, py = closest:getPosition(); - local cx, cy = character:getTile():getPosition(); - distance = math.sqrt(( px - cx ) * ( px - cx ) + ( py - cy ) * ( py - cy )); - else - local px, py = neighbour:getPosition(); - local cx, cy = character:getTile():getPosition(); - local newDistance = math.sqrt(( px - cx ) * ( px - cx ) + ( py - cy ) * ( py - cy )); - if newDistance < distance then - closest = neighbour; - distance = newDistance; - end +function BTMoveToTarget:traverse( ... ) + local blackboard, character = ... + + local closest + local distance + + -- 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 not closest then + closest = neighbour + local px, py = closest:getPosition() + local cx, cy = character:getTile():getPosition() + distance = math.sqrt(( px - cx ) * ( px - cx ) + ( py - cy ) * ( py - cy )) + else + local px, py = neighbour:getPosition() + local cx, cy = character:getTile():getPosition() + local newDistance = math.sqrt(( px - cx ) * ( px - cx ) + ( py - cy ) * ( py - cy )) + if newDistance < distance then + closest = neighbour + distance = newDistance end end end + end - if closest then - generatePath( closest, character ); - if path then - local success = path:generateActions( character ); - if success then - Log.debug( 'Character moves to target.', 'BTMoveToTarget' ); - return true; - end + if closest then + local path = generatePath( closest, character ) + if path then + local success = path:generateActions( character ) + if success then + Log.debug( 'Character moves to target.', 'BTMoveToTarget' ) + return true end - Log.debug( 'No path found.', 'BTMoveToTarget' ); end - - Log.debug( 'No target tile found', 'BTMoveToTarget' ); - return false; + Log.debug( 'No path found.', 'BTMoveToTarget' ) end - return self; + Log.debug( 'No target tile found', 'BTMoveToTarget' ) + return false end -return BTMoveToTarget; +return BTMoveToTarget From fdc0671a5dd0920825b60490cec9d5041b568987 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 18 Dec 2017 01:04:40 +0100 Subject: [PATCH 108/178] Refactor BTMustReload --- .../ai/behaviortree/leafs/BTMustReload.lua | 36 ++++++++++++------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/src/characters/ai/behaviortree/leafs/BTMustReload.lua b/src/characters/ai/behaviortree/leafs/BTMustReload.lua index c37a002a..8d18ac13 100644 --- a/src/characters/ai/behaviortree/leafs/BTMustReload.lua +++ b/src/characters/ai/behaviortree/leafs/BTMustReload.lua @@ -1,20 +1,30 @@ -local Log = require( 'src.util.Log' ); -local BTLeaf = require( 'src.characters.ai.behaviortree.leafs.BTLeaf' ); +--- +-- @module BTMustReload +-- -local BTMustReload = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ -function BTMustReload.new() - local self = BTLeaf.new():addInstance( 'BTMustReload' ); +local Log = require( 'src.util.Log' ) +local BTLeaf = require( 'src.characters.ai.behaviortree.leafs.BTLeaf' ) - function self:traverse( ... ) - local _, character = ...; +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ - local result = character:getWeapon():getMagazine():isEmpty(); - Log.debug( result, 'BTMustReload' ); - return result; - end +local BTMustReload = BTLeaf:subclass( 'BTMustReload' ) - return self; +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ + +function BTMustReload:traverse( ... ) + local _, character = ... + + local result = character:getWeapon():getMagazine():isEmpty() + Log.debug( result, 'BTMustReload' ) + return result end -return BTMustReload; +return BTMustReload From c90d3915a40f3cbf66bb5af50330f833e7431205 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 18 Dec 2017 01:04:48 +0100 Subject: [PATCH 109/178] Refactor BTRandomMovement --- .../behaviortree/leafs/BTRandomMovement.lua | 84 +++++++++++-------- 1 file changed, 49 insertions(+), 35 deletions(-) diff --git a/src/characters/ai/behaviortree/leafs/BTRandomMovement.lua b/src/characters/ai/behaviortree/leafs/BTRandomMovement.lua index 989af53a..a9d15861 100644 --- a/src/characters/ai/behaviortree/leafs/BTRandomMovement.lua +++ b/src/characters/ai/behaviortree/leafs/BTRandomMovement.lua @@ -1,50 +1,64 @@ -local Log = require( 'src.util.Log' ); -local BTLeaf = require( 'src.characters.ai.behaviortree.leafs.BTLeaf' ); -local PathFinder = require( 'src.characters.pathfinding.PathFinder' ); +--- +-- @module BTRandomMovement +-- -local BTRandomMovement = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ -function BTRandomMovement.new() - local self = BTLeaf.new():addInstance( 'BTRandomMovement' ); +local Log = require( 'src.util.Log' ) +local BTLeaf = require( 'src.characters.ai.behaviortree.leafs.BTLeaf' ) +local PathFinder = require( 'src.characters.pathfinding.PathFinder' ) - local function generatePath( target, character ) - if target and target:isPassable() and not target:isOccupied() then - return PathFinder.generatePath( character:getTile(), target, character:getStance() ) - end +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ + +local BTRandomMovement = BTLeaf:subclass( 'BTRandomMovement' ) + +-- ------------------------------------------------ +-- Private Methods +-- ------------------------------------------------ + +local function generatePath( target, character ) + if target and target:isPassable() and not target:isOccupied() then + return PathFinder.generatePath( character:getTile(), target, character:getStance() ) end +end - function self:traverse( ... ) - local _, character = ...; +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - local tiles = {}; +function BTRandomMovement:traverse( ... ) + local _, character = ... - -- 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 + 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 - local target = tiles[love.math.random( 1, #tiles )]; - if target and target:isPassable() and not target:isOccupied() then - local path = generatePath( target, character ); - if path then - local success = path:generateActions( character ); - if success then - Log.debug( 'Character moves to target.', 'BTRandomMovement' ); - return true; - end + local target = tiles[love.math.random( 1, #tiles )] + if target and target:isPassable() and not target:isOccupied() then + local path = generatePath( target, character ) + if path then + local success = path:generateActions( character ) + if success then + Log.debug( 'Character moves to target.', 'BTRandomMovement' ) + return true end - Log.debug( 'Can not find a path to the target', 'BTRandomMovement' ); - return false; end - - Log.debug( 'Invalid target', 'BTRandomMovement' ); - return false; + Log.debug( 'Can not find a path to the target', 'BTRandomMovement' ) + return false end - return self; + Log.debug( 'Invalid target', 'BTRandomMovement' ) + return false end -return BTRandomMovement; +return BTRandomMovement From 16764e3d1ac9fc54989e6790d93ffbe2b9e14312 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 18 Dec 2017 01:10:41 +0100 Subject: [PATCH 110/178] Refactor BTRearm --- .../ai/behaviortree/leafs/BTRearm.lua | 44 ++++++++++++------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/src/characters/ai/behaviortree/leafs/BTRearm.lua b/src/characters/ai/behaviortree/leafs/BTRearm.lua index 01014bcb..4f0e1aaf 100644 --- a/src/characters/ai/behaviortree/leafs/BTRearm.lua +++ b/src/characters/ai/behaviortree/leafs/BTRearm.lua @@ -1,26 +1,36 @@ -local Log = require( 'src.util.Log' ); -local BTLeaf = require( 'src.characters.ai.behaviortree.leafs.BTLeaf' ); -local Rearm = require( 'src.characters.actions.Rearm' ); +--- +-- @module BTRearm +-- -local BTRearm = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ -function BTRearm.new() - local self = BTLeaf.new():addInstance( 'BTRearm' ); +local Log = require( 'src.util.Log' ) +local BTLeaf = require( 'src.characters.ai.behaviortree.leafs.BTLeaf' ) +local Rearm = require( 'src.characters.actions.Rearm' ) - function self:traverse( ... ) - local blackboard, character = ...; +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ - local success = character:enqueueAction( Rearm( character, blackboard.weaponID )) - if success then - Log.debug( 'Equipping throwing weapon ' .. blackboard.weaponID, 'BTRearm' ); - return true; - end +local BTRearm = BTLeaf:subclass( 'BTRearm' ) - Log.debug( 'Equipping throwing weapon', 'BTRearm' ); - return false; +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ + +function BTRearm:traverse( ... ) + local blackboard, character = ... + + local success = character:enqueueAction( Rearm( character, blackboard.weaponID )) + if success then + Log.debug( 'Equipping throwing weapon ' .. blackboard.weaponID, 'BTRearm' ) + return true end - return self; + Log.debug( 'Equipping throwing weapon', 'BTRearm' ) + return false end -return BTRearm; +return BTRearm From f4130abb16a1a976e9683f95cf23f29e570a847d Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 18 Dec 2017 01:10:50 +0100 Subject: [PATCH 111/178] Refactor BTReload --- .../ai/behaviortree/leafs/BTReload.lua | 44 ++++++++++++------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/src/characters/ai/behaviortree/leafs/BTReload.lua b/src/characters/ai/behaviortree/leafs/BTReload.lua index 44f67c20..55d427e0 100644 --- a/src/characters/ai/behaviortree/leafs/BTReload.lua +++ b/src/characters/ai/behaviortree/leafs/BTReload.lua @@ -1,26 +1,36 @@ -local Log = require( 'src.util.Log' ); -local BTLeaf = require( 'src.characters.ai.behaviortree.leafs.BTLeaf' ); -local Reload = require( 'src.characters.actions.Reload' ); +--- +-- @module BTReload +-- -local BTReload = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ -function BTReload.new() - local self = BTLeaf.new():addInstance( 'BTReload' ); +local Log = require( 'src.util.Log' ) +local BTLeaf = require( 'src.characters.ai.behaviortree.leafs.BTLeaf' ) +local Reload = require( 'src.characters.actions.Reload' ) - function self:traverse( ... ) - local _, character = ...; +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ - local success = character:enqueueAction( Reload( character )) - if success then - Log.debug( 'Reloading weapon', 'BTReload' ); - return true; - end +local BTReload = BTLeaf:subclass( 'BTReload' ) - Log.debug( 'Can not reload weapon', 'BTReload' ); - return false; +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ + +function BTReload:traverse( ... ) + local _, character = ... + + local success = character:enqueueAction( Reload( character )) + if success then + Log.debug( 'Reloading weapon', 'BTReload' ) + return true end - return self; + Log.debug( 'Can not reload weapon', 'BTReload' ) + return false end -return BTReload; +return BTReload From 128291c7db22b7ef95a06ee181137f0cac918bca Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 18 Dec 2017 01:10:57 +0100 Subject: [PATCH 112/178] Refactor BTTakeItem --- .../ai/behaviortree/leafs/BTTakeItem.lua | 62 +++++++++++-------- 1 file changed, 36 insertions(+), 26 deletions(-) diff --git a/src/characters/ai/behaviortree/leafs/BTTakeItem.lua b/src/characters/ai/behaviortree/leafs/BTTakeItem.lua index ffe83a42..5a4dd899 100644 --- a/src/characters/ai/behaviortree/leafs/BTTakeItem.lua +++ b/src/characters/ai/behaviortree/leafs/BTTakeItem.lua @@ -1,38 +1,48 @@ -local Log = require( 'src.util.Log' ); -local BTLeaf = require( 'src.characters.ai.behaviortree.leafs.BTLeaf' ); +--- +-- @module BTTakeItem +-- -local BTTakeItem = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ -function BTTakeItem.new() - local self = BTLeaf.new():addInstance( 'BTTakeItem' ); +local Log = require( 'src.util.Log' ) +local BTLeaf = require( 'src.characters.ai.behaviortree.leafs.BTLeaf' ) - function self:traverse( ... ) - local blackboard, character = ...; - local target = blackboard.target; +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ - local pinventory = character:getInventory(); - local tinventory = target:getInventory(); +local BTTakeItem = BTLeaf:subclass( 'BTTakeItem' ) - local titems = tinventory:getItems(); - Log.debug( 'Found items: ' .. #titems, 'BTTakeItem' ); +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - for i = #titems, 1, -1 do - local item = titems[i]; +function BTTakeItem:traverse( ... ) + local blackboard, character = ... + local target = blackboard.target - Log.debug( 'Items left: ' .. #titems, 'BTTakeItem' ); - local success = pinventory:addItem( item ); - if success then - tinventory:removeItem( item ); - Log.debug( 'Took item ' .. item:getID(), 'BTTakeItem' ); - else - Log.debug( 'Didn\'t take item ' .. item:getID(), 'BTTakeItem' ); - end - end + local pinventory = character:getInventory() + local tinventory = target:getInventory() + + local titems = tinventory:getItems() + Log.debug( 'Found items: ' .. #titems, 'BTTakeItem' ) - return true; + for i = #titems, 1, -1 do + local item = titems[i] + + Log.debug( 'Items left: ' .. #titems, 'BTTakeItem' ) + local success = pinventory:addItem( item ) + if success then + tinventory:removeItem( item ) + Log.debug( 'Took item ' .. item:getID(), 'BTTakeItem' ) + else + Log.debug( 'Didn\'t take item ' .. item:getID(), 'BTTakeItem' ) + end end - return self; + return true end -return BTTakeItem; +return BTTakeItem From 634783e418053fe989c13b776aac8aafa2d5249b Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 18 Dec 2017 01:11:05 +0100 Subject: [PATCH 113/178] Refactor BTThrowingAttack --- .../behaviortree/leafs/BTThrowingAttack.lua | 48 +++++++++++-------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/src/characters/ai/behaviortree/leafs/BTThrowingAttack.lua b/src/characters/ai/behaviortree/leafs/BTThrowingAttack.lua index a6dd6310..64720e46 100644 --- a/src/characters/ai/behaviortree/leafs/BTThrowingAttack.lua +++ b/src/characters/ai/behaviortree/leafs/BTThrowingAttack.lua @@ -1,28 +1,38 @@ -local Log = require( 'src.util.Log' ); -local BTLeaf = require( 'src.characters.ai.behaviortree.leafs.BTLeaf' ); -local ThrowingAttack = require( 'src.characters.actions.ThrowingAttack' ); +--- +-- @module BTThrowingAttack +-- -local BTThrowingAttack = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ -function BTThrowingAttack.new() - local self = BTLeaf.new():addInstance( 'BTThrowingAttack' ); +local Log = require( 'src.util.Log' ) +local BTLeaf = require( 'src.characters.ai.behaviortree.leafs.BTLeaf' ) +local ThrowingAttack = require( 'src.characters.actions.ThrowingAttack' ) - function self:traverse( ... ) - local blackboard, character = ...; +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ - local success = character:enqueueAction( ThrowingAttack( character, blackboard.target )) - if success then - -- Store weapon id for the rearm action. - blackboard.weaponID = character:getWeapon():getID(); - Log.debug( 'Character attacks target', 'BTThrowingAttack' ); - return true; - end +local BTThrowingAttack = BTLeaf:subclass( 'BTThrowingAttack' ) - Log.debug( 'Character can not attack target', 'BTThrowingAttack' ); - return false; +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ + +function BTThrowingAttack:traverse( ... ) + local blackboard, character = ... + + local success = character:enqueueAction( ThrowingAttack( character, blackboard.target )) + if success then + -- Store weapon id for the rearm action. + blackboard.weaponID = character:getWeapon():getID() + Log.debug( 'Character attacks target', 'BTThrowingAttack' ) + return true end - return self; + Log.debug( 'Character can not attack target', 'BTThrowingAttack' ) + return false end -return BTThrowingAttack; +return BTThrowingAttack From aa9b2d0b30a1ec6c85b62055b5a34ea1ea3b375a Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 18 Dec 2017 01:20:00 +0100 Subject: [PATCH 114/178] Refactor BTComposite --- .../ai/behaviortree/composite/BTComposite.lua | 54 +++++++++++-------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/src/characters/ai/behaviortree/composite/BTComposite.lua b/src/characters/ai/behaviortree/composite/BTComposite.lua index 34eb89ac..35b2df00 100644 --- a/src/characters/ai/behaviortree/composite/BTComposite.lua +++ b/src/characters/ai/behaviortree/composite/BTComposite.lua @@ -1,31 +1,43 @@ -local Object = require( 'src.Object' ); +--- +-- @module BTComposite +-- -local BTComposite = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ -function BTComposite.new() - local self = Object.new():addInstance( 'BTComposite' ); +local Class = require( 'lib.Middleclass' ) - local children = {}; +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ - function self:addNode( nnode, pos ) - children[pos] = nnode; - end +local BTComposite = Class( 'BTComposite' ) - function self:traverse( ... ) - for _, child in ipairs( children ) do - local success = child:traverse( ... ); - if not success then - return false; - end - end - return true; - end +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ + +function BTComposite:initialize() + self.children = {} +end - function self:getChildren() - return children; +function BTComposite:addNode( nnode, pos ) + self.children[pos] = nnode +end + +function BTComposite:traverse( ... ) + for _, child in ipairs( self.children ) do + local success = child:traverse( ... ) + if not success then + return false + end end + return true +end - return self; +function BTComposite:getChildren() + return self.children end -return BTComposite; +return BTComposite From 8f9b365747a4b8f28f9074f5396edc1af60c37f9 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 18 Dec 2017 01:20:12 +0100 Subject: [PATCH 115/178] Refactor BTSelector --- .../ai/behaviortree/composite/BTSelector.lua | 38 ++++++++++++------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/src/characters/ai/behaviortree/composite/BTSelector.lua b/src/characters/ai/behaviortree/composite/BTSelector.lua index 19754ac3..5a60467c 100644 --- a/src/characters/ai/behaviortree/composite/BTSelector.lua +++ b/src/characters/ai/behaviortree/composite/BTSelector.lua @@ -1,21 +1,31 @@ -local BTComposite = require( 'src.characters.ai.behaviortree.composite.BTComposite' ); +--- +-- @module BTSelector +-- -local BTSelector = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ -function BTSelector.new() - local self = BTComposite.new():addInstance( 'BTSelector' ); +local BTComposite = require( 'src.characters.ai.behaviortree.composite.BTComposite' ) - function self:traverse( ... ) - for _, child in ipairs( self:getChildren() ) do - local success = child:traverse( ... ); - if success then - return true; - end +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ + +local BTSelector = BTComposite:subclass( 'BTSelector' ) + +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ + +function BTSelector:traverse( ... ) + for _, child in ipairs( self:getChildren() ) do + local success = child:traverse( ... ) + if success then + return true end - return false; end - - return self; + return false end -return BTSelector; +return BTSelector From eaf0187cc19fccf2dfbfb9a24098121ea4510892 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 18 Dec 2017 01:20:18 +0100 Subject: [PATCH 116/178] Refactor BTSequence --- .../ai/behaviortree/composite/BTSequence.lua | 38 ++++++++++++------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/src/characters/ai/behaviortree/composite/BTSequence.lua b/src/characters/ai/behaviortree/composite/BTSequence.lua index 8b447ecf..56d3d3ff 100644 --- a/src/characters/ai/behaviortree/composite/BTSequence.lua +++ b/src/characters/ai/behaviortree/composite/BTSequence.lua @@ -1,21 +1,31 @@ -local BTComposite = require( 'src.characters.ai.behaviortree.composite.BTComposite' ); +--- +-- @module BTSequence +-- -local BTSequence = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ -function BTSequence.new() - local self = BTComposite.new():addInstance( 'BTSequence' ); +local BTComposite = require( 'src.characters.ai.behaviortree.composite.BTComposite' ) - function self:traverse( ... ) - for _, child in ipairs( self:getChildren() ) do - local success = child:traverse( ... ); - if not success then - return false; - end +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ + +local BTSequence = BTComposite:subclass( 'BTSequence' ) + +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ + +function BTSequence:traverse( ... ) + for _, child in ipairs( self:getChildren() ) do + local success = child:traverse( ... ) + if not success then + return false end - return true; end - - return self; + return true end -return BTSequence; +return BTSequence From 1db39ec101c3fda5c5332e602d41073c300870ab Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 18 Dec 2017 01:20:31 +0100 Subject: [PATCH 117/178] Use Middleclass constructor --- src/characters/ai/behaviortree/BehaviorTreeFactory.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/characters/ai/behaviortree/BehaviorTreeFactory.lua b/src/characters/ai/behaviortree/BehaviorTreeFactory.lua index 92eb6077..209662ee 100644 --- a/src/characters/ai/behaviortree/BehaviorTreeFactory.lua +++ b/src/characters/ai/behaviortree/BehaviorTreeFactory.lua @@ -71,7 +71,7 @@ local function createTree( layout ) for index, id in ipairs( layout.nodes ) do assert( BLUEPRINTS[id], string.format( 'Behavior blueprint %s does not exist!', id )); - nodes[index] = BLUEPRINTS[id].new(); + nodes[index] = BLUEPRINTS[id]() end for _, edge in ipairs( layout.edges ) do From 8323ad22caaedb7475262029bdcc94094c6f2cd5 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 18 Dec 2017 01:27:58 +0100 Subject: [PATCH 118/178] Refactor Particle --- src/ui/overlays/Particle.lua | 58 ++++++++++++++++++------------- src/ui/overlays/ParticleLayer.lua | 2 +- src/util/ObjectPool.lua | 15 +++----- 3 files changed, 38 insertions(+), 37 deletions(-) diff --git a/src/ui/overlays/Particle.lua b/src/ui/overlays/Particle.lua index 25db83ec..43b31803 100644 --- a/src/ui/overlays/Particle.lua +++ b/src/ui/overlays/Particle.lua @@ -1,37 +1,45 @@ -local Object = require( 'src.Object' ); +--- +-- @module Particle +-- -local Particle = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ -function Particle.new() - local self = Object.new():addInstance( 'Particle' ); +local Class = require( 'lib.Middleclass' ) - local r, g, b, a, fade, sprite +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ - function self:update( dt ) - a = a - dt * fade; - end +local Particle = Class( 'Particle' ) - function self:getColors() - return r, g, b, a; - end +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - function self:getAlpha() - return a; - end +function Particle:update( dt ) + self.a = self.a - dt * self.fade +end + +function Particle:getColors() + return self.r, self.g, self.b, self.a +end - function self:getSprite() - return sprite; - end +function Particle:getAlpha() + return self.a +end - function self:setParameters( nr, ng, nb, na, nfade, nsprite ) - r, g, b, a, fade, sprite = nr, ng, nb, na, nfade, nsprite - end +function Particle:getSprite() + return self.sprite +end - function self:clear() - r, g, b, a, fade, sprite = nil, nil, nil, nil, nil, nil - end +function Particle:setParameters( r, g, b, a, fade, sprite ) + self.r, self.g, self.b, self.a, self.fade, self.sprite = r, g, b, a, fade, sprite +end - return self; +function Particle:clear() + self.r, self.g, self.b, self.a, self.fade, self.sprite = nil, nil, nil, nil, nil, nil end -return Particle; +return Particle diff --git a/src/ui/overlays/ParticleLayer.lua b/src/ui/overlays/ParticleLayer.lua index debbb56b..17031f4e 100644 --- a/src/ui/overlays/ParticleLayer.lua +++ b/src/ui/overlays/ParticleLayer.lua @@ -9,7 +9,7 @@ function ParticleLayer.new() local self = Object.new():addInstance( 'ParticleLayer' ); local grid = {}; - local particles = ObjectPool.new( Particle, 'Particle' ); + local particles = ObjectPool.new( Particle ) local function addParticleEffect( x, y, r, g, b, a, fade, sprite ) grid[x] = grid[x] or {}; diff --git a/src/util/ObjectPool.lua b/src/util/ObjectPool.lua index 83d3f990..d8e096b7 100644 --- a/src/util/ObjectPool.lua +++ b/src/util/ObjectPool.lua @@ -3,7 +3,7 @@ local Queue = require( 'src.util.Queue' ); local ObjectPool = {}; -function ObjectPool.new( class, type ) +function ObjectPool.new( class ) local self = Object.new():addInstance( 'ObjectPool' ); local queue = Queue() @@ -12,7 +12,7 @@ function ObjectPool.new( class, type ) local object; if queue:isEmpty() then - object = class.new(); + object = class() queue:enqueue( object ); end @@ -22,18 +22,11 @@ function ObjectPool.new( class, type ) end function self:deposit( object ) - if type and object:instanceOf( type ) then + if object:isInstanceOf( class ) then object:clear(); queue:enqueue( object ); else - local list = ""; - for i, v in pairs( object.__instances ) do - list = list .. v; - if i ~= #object.__instances then - list = list .. ', '; - end - end - error( string.format( "Object (%s) isn't an instance of the class type required for this ObjectPool (%s).", list, type )); + error( string.format( "Object (%s) isn't an instance of the class type required for this ObjectPool (%s).", object, class )) end end From ab6851027d3a8d893cf56bfc5103142bf7131a9b Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 18 Dec 2017 01:31:47 +0100 Subject: [PATCH 119/178] Refactor ObjectPool --- src/ui/overlays/ParticleLayer.lua | 4 +- src/util/ObjectPool.lua | 63 +++++++++++++++++++------------ 2 files changed, 40 insertions(+), 27 deletions(-) diff --git a/src/ui/overlays/ParticleLayer.lua b/src/ui/overlays/ParticleLayer.lua index 17031f4e..c6821b1f 100644 --- a/src/ui/overlays/ParticleLayer.lua +++ b/src/ui/overlays/ParticleLayer.lua @@ -1,7 +1,7 @@ local Object = require( 'src.Object' ); local Messenger = require( 'src.Messenger' ); local Particle = require( 'src.ui.overlays.Particle' ) -local ObjectPool = require( 'src.util.ObjectPool' ); +local ObjectPool = require( 'src.util.ObjectPool' ) local ParticleLayer = {}; @@ -9,7 +9,7 @@ function ParticleLayer.new() local self = Object.new():addInstance( 'ParticleLayer' ); local grid = {}; - local particles = ObjectPool.new( Particle ) + local particles = ObjectPool( Particle ) local function addParticleEffect( x, y, r, g, b, a, fade, sprite ) grid[x] = grid[x] or {}; diff --git a/src/util/ObjectPool.lua b/src/util/ObjectPool.lua index d8e096b7..0c7878e9 100644 --- a/src/util/ObjectPool.lua +++ b/src/util/ObjectPool.lua @@ -1,36 +1,49 @@ -local Object = require( 'src.Object' ); -local Queue = require( 'src.util.Queue' ); +--- +-- @module ObjectPool +-- -local ObjectPool = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ -function ObjectPool.new( class ) - local self = Object.new():addInstance( 'ObjectPool' ); +local Class = require( 'lib.Middleclass' ) +local Queue = require( 'src.util.Queue' ) - local queue = Queue() +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ - function self:request( ... ) - local object; +local ObjectPool = Class( 'ObjectPool' ) - if queue:isEmpty() then - object = class() - queue:enqueue( object ); - end +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - object = queue:dequeue(); - object:setParameters( ... ) - return object; - end +function ObjectPool:initialize( class ) + self.class = class + self.queue = Queue() +end + +function ObjectPool:request( ... ) + local object - function self:deposit( object ) - if object:isInstanceOf( class ) then - object:clear(); - queue:enqueue( object ); - else - error( string.format( "Object (%s) isn't an instance of the class type required for this ObjectPool (%s).", object, class )) - end + if self.queue:isEmpty() then + object = self.class() + self.queue:enqueue( object ) end - return self; + object = self.queue:dequeue() + object:setParameters( ... ) + return object +end + +function ObjectPool:deposit( object ) + if object:isInstanceOf( self.class ) then + object:clear() + self.queue:enqueue( object ) + else + error( string.format( "Object (%s) isn't an instance of the class type required for this ObjectPool (%s).", object, self.class )) + end end -return ObjectPool; +return ObjectPool From 5f0f68e4573ae226fdd00b5614024695606c9849 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 18 Dec 2017 01:35:54 +0100 Subject: [PATCH 120/178] Refactor ParticleLayer --- src/ui/overlays/OverlayPainter.lua | 2 +- src/ui/overlays/ParticleLayer.lua | 106 +++++++++++++++++------------ 2 files changed, 62 insertions(+), 46 deletions(-) diff --git a/src/ui/overlays/OverlayPainter.lua b/src/ui/overlays/OverlayPainter.lua index e70ec0d9..9060ed6e 100644 --- a/src/ui/overlays/OverlayPainter.lua +++ b/src/ui/overlays/OverlayPainter.lua @@ -43,7 +43,7 @@ function OverlayPainter.new( game, camera ) -- Private Attributes -- ------------------------------------------------ - local particleLayer = ParticleLayer.new() + local particleLayer = ParticleLayer() local pulser = Pulser.new( 4, 80, 80 ); local coneOverlay = ConeOverlay.new( game, pulser, camera ) local pathOverlay = PathOverlay.new( game, pulser ) diff --git a/src/ui/overlays/ParticleLayer.lua b/src/ui/overlays/ParticleLayer.lua index c6821b1f..1308188a 100644 --- a/src/ui/overlays/ParticleLayer.lua +++ b/src/ui/overlays/ParticleLayer.lua @@ -1,71 +1,87 @@ -local Object = require( 'src.Object' ); -local Messenger = require( 'src.Messenger' ); +--- +-- @module ParticleLayer +-- + +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ + +local Class = require( 'lib.Middleclass' ) +local Messenger = require( 'src.Messenger' ) local Particle = require( 'src.ui.overlays.Particle' ) local ObjectPool = require( 'src.util.ObjectPool' ) -local ParticleLayer = {}; +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ -function ParticleLayer.new() - local self = Object.new():addInstance( 'ParticleLayer' ); +local ParticleLayer = Class( 'ParticleLayer' ) - local grid = {}; - local particles = ObjectPool( Particle ) +-- ------------------------------------------------ +-- Private Methods +-- ------------------------------------------------ - local function addParticleEffect( x, y, r, g, b, a, fade, sprite ) - grid[x] = grid[x] or {}; - -- Return previous particles on this tile to the particle pool. - if grid[x][y] then - particles:deposit( grid[x][y] ); - end - grid[x][y] = particles:request( r, g, b, a, fade, sprite ) +local function addParticleEffect( grid, particles, x, y, r, g, b, a, fade, sprite ) + grid[x] = grid[x] or {} + -- Return previous particles on this tile to the particle pool. + if grid[x][y] then + particles:deposit( grid[x][y] ) end + grid[x][y] = particles:request( r, g, b, a, fade, sprite ) +end - function self:update( dt ) - for x, row in pairs( grid ) do - for y, particle in pairs( row ) do - particle:update( dt ); - - -- Remove the Particle if it is invisible. - if particle:getAlpha() <= 0 then - particles:deposit( particle ); - grid[x][y] = nil; - end - end - end - end +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - function self:getParticleGrid() - return grid; - end +function ParticleLayer:initialize() + self.grid = {} + self.particles = ObjectPool( Particle ) Messenger.observe( 'PROJECTILE_MOVED', function( ... ) - local projectile = ...; - local tile = projectile:getTile(); + local projectile = ... + local tile = projectile:getTile() if tile then if projectile:getEffects():hasCustomSprite() then - addParticleEffect( tile:getX(), tile:getY(), 255, 255, 255, 255, 1500, projectile:getEffects():getCustomSprite() ) + addParticleEffect( self.grid, self.particles, tile:getX(), tile:getY(), 255, 255, 255, 255, 1500, projectile:getEffects():getCustomSprite() ) elseif projectile:getEffects():isExplosive() then - local col = love.math.random( 150, 255 ); - addParticleEffect( tile:getX(), tile:getY(), col, col, col, love.math.random( 100, 255 ), 500 ); + local col = love.math.random( 150, 255 ) + addParticleEffect( self.grid, self.particles, tile:getX(), tile:getY(), col, col, col, love.math.random( 100, 255 ), 500 ) else - addParticleEffect( tile:getX(), tile:getY(), 223, 113, 38, 200, 500 ); + addParticleEffect( self.grid, self.particles, tile:getX(), tile:getY(), 223, 113, 38, 200, 500 ) end end end) Messenger.observe( 'EXPLOSION', function( ... ) - local generation = ...; + local generation = ... for tile, life in pairs( generation ) do - local r = 255; - local g = love.math.random( 100, 200 ); - local b = 0; - local a = love.math.random( 200, 255 ); - local fade = 500 / math.min( 3, love.math.random( life )); - addParticleEffect( tile:getX(), tile:getY(), r, g, b, a, fade ); + local r = 255 + local g = love.math.random( 100, 200 ) + local b = 0 + local a = love.math.random( 200, 255 ) + local fade = 500 / math.min( 3, love.math.random( life )) + addParticleEffect( self.grid, self.particles, tile:getX(), tile:getY(), r, g, b, a, fade ) end end) +end + +function ParticleLayer:update( dt ) + for x, row in pairs( self.grid ) do + for y, particle in pairs( row ) do + particle:update( dt ) + + -- Remove the Particle if it is invisible. + if particle:getAlpha() <= 0 then + self.particles:deposit( particle ) + self.grid[x][y] = nil + end + end + end +end - return self; +function ParticleLayer:getParticleGrid() + return self.grid end -return ParticleLayer; +return ParticleLayer From c4d1bd80d6e6dee66b664ced478c52e6fbf1e45a Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 18 Dec 2017 01:49:09 +0100 Subject: [PATCH 121/178] Refactor Projectile --- src/items/weapons/Projectile.lua | 180 ++++++++++---------- src/items/weapons/ProjectileQueue.lua | 4 +- src/items/weapons/ThrownProjectileQueue.lua | 2 +- 3 files changed, 95 insertions(+), 91 deletions(-) diff --git a/src/items/weapons/Projectile.lua b/src/items/weapons/Projectile.lua index 51bf2550..046f7bc2 100644 --- a/src/items/weapons/Projectile.lua +++ b/src/items/weapons/Projectile.lua @@ -1,123 +1,127 @@ -local Object = require( 'src.Object' ); +--- +-- @module Projectile +-- + +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ + +local Class = require( 'lib.Middleclass' ) -- ------------------------------------------------ -- Module -- ------------------------------------------------ -local Projectile = {}; +local Projectile = Class( 'Projectile' ) -- ------------------------------------------------ -- Constants -- ------------------------------------------------ -local DEFAULT_SPEED = 30; +local DEFAULT_SPEED = 30 -- ------------------------------------------------ --- Constructor +-- Public Methods -- ------------------------------------------------ --- --- Creates a new Projectile. --- @param character (Character) The character this projectile belongs to. --- @param tiles (table) A sequence containing all tiles this projectile will pass. --- @param damage (number) The damage this projectile deals. --- @param damageType (string) The type of damage the tile is hit with. --- @param effects (AmmunitionEffects) An object containing different effects associated with ammunition. --- @return (Projectile) A new instance of the Projectile class. +-- Initializes a new Projectile object. +-- @tparam Character character The character this projectile belongs to. +-- @tparam table tiles A sequence containing all tiles this projectile will pass. +-- @tparam number damage The damage this projectile deals. +-- @tparam string damageType The type of damage the tile is hit with. +-- @tparam AmmunitionEffects effects An object containing different effects associated with ammunition. +-- +function Projectile:initialize( character, tiles, damage, damageType, effects ) + self.character = character + self.tiles = tiles + self.damage = damage + self.damageType = damageType + self.effects = effects + + self.energy = 100 + self.timer = 0 + self.index = 1 + self.tile = character:getTile() + self.speed = effects:hasCustomSpeed() and effects:getCustomSpeed() or DEFAULT_SPEED +end + +--- +-- Advances the projectile to the next tile in its queue if the timer is +-- reached. Different types of projectiles can have different speeds. +-- @tparam number dt The time since the last frame update. -- -function Projectile.new( character, tiles, damage, damageType, effects ) - local self = Object.new():addInstance( 'Projectile' ); - - local energy = 100; - local timer = 0; - local index = 1; - local tile = character:getTile(); - local previousTile; - local speed = effects:hasCustomSpeed() and effects:getCustomSpeed() or DEFAULT_SPEED; - - -- ------------------------------------------------ - -- Public Methods - -- ------------------------------------------------ - - --- - -- Advances the projectile to the next tile in its queue if the timer is - -- reached. Different types of projectiles can have different speeds. - -- @param dt (number) The time since the last frame update. - -- - function self:update( dt ) - timer = timer + dt * speed; - if timer > 1 and index < #tiles then - index = index + 1; - timer = 0; - end - - if effects:hasCustomSpeed() then - speed = math.min( speed + effects:getSpeedIncrease(), effects:getFinalSpeed() ); - end +function Projectile:update( dt ) + self.timer = self.timer + dt * self.speed + if self.timer > 1 and self.index < #self.tiles then + self.index = self.index + 1 + self.timer = 0 end - --- - -- Moves the projectile to the next tile. - -- @param map (Map) The game's map. - -- - function self:updateTile( map ) - previousTile = tile; - tile = map:getTileAt( tiles[index].x, tiles[index].y ); + if self.effects:hasCustomSpeed() then + self.speed = math.min( self.speed + self.effects:getSpeedIncrease(), self.effects:getFinalSpeed() ) end +end - -- ------------------------------------------------ - -- Getters - -- ------------------------------------------------ +--- +-- Moves the projectile to the next tile. +-- @tparam Map map The game's map. +-- +function Projectile:updateTile( map ) + self.previousTile = self.tile + self.tile = map:getTileAt( self.tiles[self.index].x, self.tiles[self.index].y ) +end - function self:getCharacter() - return character; - end +-- ------------------------------------------------ +-- Getters +-- ------------------------------------------------ - function self:getEffects() - return effects; - end +function Projectile:getCharacter() + return self.character +end - function self:getDamage() - return damage; - end +function Projectile:getEffects() + return self.effects +end - function self:getDamageType() - return damageType; - end +function Projectile:getDamage() + return self.damage +end - function self:getEnergy() - return energy; - end +function Projectile:getDamageType() + return self.damageType +end - function self:getTile() - return tile; - end +function Projectile:getEnergy() + return self.energy +end - function self:getPreviousTile() - return previousTile; - end +function Projectile:getTile() + return self.tile +end - function self:hasMoved( map ) - return tile ~= map:getTileAt( tiles[index].x, tiles[index].y ); - end +function Projectile:getPreviousTile() + return self.previousTile +end - function self:hasReachedTarget() - return #tiles == index; - end +function Projectile:hasMoved( map ) + return self.tile ~= map:getTileAt( self.tiles[self.index].x, self.tiles[self.index].y ) +end - function self:getHeight() - return tiles[index].z - end +function Projectile:hasReachedTarget() + return #self.tiles == self.index +end - -- ------------------------------------------------ - -- Setters - -- ------------------------------------------------ +function Projectile:getHeight() + return self.tiles[self.index].z +end - function self:setEnergy( nenergy ) - energy = nenergy; - end +-- ------------------------------------------------ +-- Setters +-- ------------------------------------------------ - return self; +function Projectile:setEnergy( energy ) + self.energy = energy end -return Projectile; +return Projectile diff --git a/src/items/weapons/ProjectileQueue.lua b/src/items/weapons/ProjectileQueue.lua index 6489c0eb..7d1f97e1 100644 --- a/src/items/weapons/ProjectileQueue.lua +++ b/src/items/weapons/ProjectileQueue.lua @@ -43,7 +43,7 @@ function ProjectileQueue.new( character, tx, ty, th ) local round = ammoQueue:dequeue(); local path = ProjectilePath.calculate( character, tx, ty, th, weapon, shots - ammoQueue:getSize() ) - local projectile = Projectile.new( character, path, weapon:getDamage(), round:getDamageType(), round:getEffects() ) + local projectile = Projectile( character, path, weapon:getDamage(), round:getDamageType(), round:getEffects() ) -- Play sound and remove the round from the magazine. Messenger.publish( 'SOUND_ATTACK', weapon ); @@ -54,7 +54,7 @@ function ProjectileQueue.new( character, tx, ty, th ) for _ = 1, round:getEffects():getPellets() do index = index + 1; local spreadTiles = ProjectilePath.calculate( character, tx, ty, th, weapon, shots - ammoQueue:getSize() ) - projectiles[index] = Projectile.new( character, spreadTiles, weapon:getDamage(), round:getDamageType(), round:getEffects() ); + projectiles[index] = Projectile( character, spreadTiles, weapon:getDamage(), round:getDamageType(), round:getEffects() ); end return; end diff --git a/src/items/weapons/ThrownProjectileQueue.lua b/src/items/weapons/ThrownProjectileQueue.lua index e38fc0c5..62645302 100644 --- a/src/items/weapons/ThrownProjectileQueue.lua +++ b/src/items/weapons/ThrownProjectileQueue.lua @@ -51,7 +51,7 @@ function ThrownProjectileQueue.new( character, tx, ty, th ) assert( success, "Couldn't remove the item from the character's equipment." ); local tiles = ProjectilePath.calculate( character, tx, ty, th, weapon ) - local projectile = Projectile.new( character, tiles, weapon:getDamage(), weapon:getDamageType(), weapon:getEffects() ); + local projectile = Projectile( character, tiles, weapon:getDamage(), weapon:getDamageType(), weapon:getEffects() ); index = index + 1; projectiles[index] = projectile; From 16ec7dcfa2df595efcf1c18f0b8d046ec5ee9013 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 18 Dec 2017 01:57:34 +0100 Subject: [PATCH 122/178] Refactor Path --- src/characters/pathfinding/Path.lua | 236 +++++++++++----------- src/characters/pathfinding/PathFinder.lua | 2 +- 2 files changed, 123 insertions(+), 115 deletions(-) diff --git a/src/characters/pathfinding/Path.lua b/src/characters/pathfinding/Path.lua index 299aaf9d..c3d56373 100644 --- a/src/characters/pathfinding/Path.lua +++ b/src/characters/pathfinding/Path.lua @@ -1,140 +1,148 @@ -local Object = require('src.Object'); -local Walk = require( 'src.characters.actions.Walk' ); -local Open = require( 'src.characters.actions.Open' ); -local OpenInventory = require( 'src.characters.actions.OpenInventory' ); +--- +-- @module Path +-- + +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ + +local Class = require( 'lib.Middleclass' ) +local Walk = require( 'src.characters.actions.Walk' ) +local Open = require( 'src.characters.actions.Open' ) +local OpenInventory = require( 'src.characters.actions.OpenInventory' ) local ClimbOver = require( 'src.characters.actions.ClimbOver' ) -- ------------------------------------------------ -- Module -- ------------------------------------------------ -local Path = {}; +local Path = Class( 'Path' ) + +-- ------------------------------------------------ +-- Private Methods +-- ------------------------------------------------ --- --- Creates a new path object. +-- Generate smart actions for openable objects. These are a special case, +-- because usually the player will want to move a character onto the tile +-- after the object is openend. +-- @tparam WorldObject worldObject The worldobject to interact with. +-- @tparam Character character The character that interacts with the object. +-- @tparam Tile tile The tile containing the worldobject. +-- @tparam number index The tile's position in the path. -- -function Path.new() - local self = Object.new():addInstance( 'Path' ); - - local path = {}; - local cost = 0; - - -- ------------------------------------------------ - -- Local Functions - -- ------------------------------------------------ - - --- - -- Generate smart actions for openable objects. These are a special case, - -- because usually the player will want to move a character onto the tile - -- after the object is openend. - -- @param worldObject (WorldObject) The worldobject to interact with. - -- @param character (Character) The character that interacts with the object. - -- @param tile (Tile) The tile containing the worldobject. - -- @param index (number) The tile's position in the path. - -- @param (boolean) Return true if the actions have been enqueued. - -- - local function handleOpenableObjects( worldObject, character, tile, index ) - if not worldObject:isPassable() then - local success = character:enqueueAction( Open( character, tile )) - -- Don't create a walk action if the tile is the last one in the path. - if index ~= 1 then - success = character:enqueueAction( Walk( character, tile )) - end - return success; +local function handleOpenableObjects( worldObject, character, tile, index ) + if not worldObject:isPassable() then + local success = character:enqueueAction( Open( character, tile )) + -- Don't create a walk action if the tile is the last one in the path. + if index ~= 1 then + success = character:enqueueAction( Walk( character, tile )) end - return character:enqueueAction( Walk( character, tile )) + return success end + return character:enqueueAction( Walk( character, tile )) +end - -- ------------------------------------------------ - -- Public Functions - -- ------------------------------------------------ - - --- - -- Iterates over the path. The target tile will be processed at last. - -- @param callback (function) A function to call on every tile. - -- - function self:iterate( callback ) - for i = #path, 1, -1 do - callback( path[i], i ); - end - end +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ + +--- +-- Initializes a new Path object. +-- +function Path:initialize() + self.path = {} + self.cost = 0 +end - --- - -- Adds a new tile to this path. - -- @param tile (Tile) A tile to add to this path. - -- @param dcost (number) The cost to traverse this tile. - -- - function self:addNode( tile, dcost ) - path[#path + 1] = tile; - cost = cost + dcost; +--- +-- Iterates over the path. The target tile will be processed at last. +-- @tparam function callback A function to call on every tile. +-- +function Path:iterate( callback ) + for i = #self.path, 1, -1 do + callback( self.path[i], i ) end +end - --- - -- Automagically generates appropriate Actions for each tile in the path. - -- For example if the character moves through a tile with a door object this - -- function will generate an action to open the door and an action to walk - -- onto the tile itself. - -- @param character (Character) The character to create the actions for. - -- - function self:generateActions( character ) - local generatedAction = false; - for index = #path, 1, -1 do - local tile = path[index]; - local success; - - if tile:hasWorldObject() then - local worldObject = tile:getWorldObject(); - - if worldObject:isOpenable() then - success = handleOpenableObjects( worldObject, character, tile, index ); - end - - if worldObject:isClimbable() then - success = character:enqueueAction( ClimbOver( character, tile )) - end - - if worldObject:isContainer() then - character:enqueueAction( OpenInventory( character, tile )) - end - else - success = character:enqueueAction( Walk( character, tile )) +--- +-- Adds a new tile to this path. +-- @tparam Tile tile A tile to add to this path. +-- @tparam number cost The cost to traverse this tile. +-- +function Path:addNode( tile, cost ) + self.path[#self.path + 1] = tile + self.cost = self.cost + cost +end + +--- +-- Automagically generates appropriate Actions for each tile in the path. +-- For example if the character moves through a tile with a door object this +-- function will generate an action to open the door and an action to walk +-- onto the tile itself. +-- @tparam Character character The character to create the actions for. +-- @treturn boolean Wether actions have been created or not. +-- +function Path:generateActions( character ) + local generatedAction = false + for index = #self.path, 1, -1 do + local tile = self.path[index] + local success + + if tile:hasWorldObject() then + local worldObject = tile:getWorldObject() + + if worldObject:isOpenable() then + success = handleOpenableObjects( worldObject, character, tile, index ) end - -- Stop adding actions if the previous one wasn't added correctly. - if not success then - break; - else - generatedAction = true; + if worldObject:isClimbable() then + success = character:enqueueAction( ClimbOver( character, tile )) end - end - return generatedAction; - end - -- ------------------------------------------------ - -- Getters - -- ------------------------------------------------ + if worldObject:isContainer() then + character:enqueueAction( OpenInventory( character, tile )) + end + else + success = character:enqueueAction( Walk( character, tile )) + end - --- - -- Returns the length of the path. - -- @return (number) The length of the path. - -- - function self:getLength() - return #path; + -- Stop adding actions if the previous one wasn't added correctly. + if not success then + break + else + generatedAction = true + end end + return generatedAction +end - --- - -- Returns the target tile of the path. - -- @return (Tile) The target of the path. - -- - function self:getTarget() - return path[1]; - end +-- ------------------------------------------------ +-- Getters +-- ------------------------------------------------ - function self:getCost() - return cost; - end +--- +-- Returns the length of the path. +-- @treturn number The length of the path. +-- +function Path:getLength() + return #self.path +end - return self; +--- +-- Returns the target tile of the path. +-- @treturn Tile The target of the path. +-- +function Path:getTarget() + return self.path[1] +end + +--- +-- Returns the cost for the path. +-- @treturn number The cost. +-- +function Path:getCost() + return self.cost end -return Path; +return Path diff --git a/src/characters/pathfinding/PathFinder.lua b/src/characters/pathfinding/PathFinder.lua index 38c88c1e..7d36ebee 100644 --- a/src/characters/pathfinding/PathFinder.lua +++ b/src/characters/pathfinding/PathFinder.lua @@ -167,7 +167,7 @@ end -- @return (Path) A path object containing tiles to form a path. -- local function finalizePath( endNode ) - local path = Path.new() + local path = Path() path:addNode( endNode.tile, endNode.actualCost ) -- Build the rest of the path. From 2f2694715e205b85caae8fde65871003dece684c Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 18 Dec 2017 02:07:09 +0100 Subject: [PATCH 123/178] Refactor CombatState --- src/CombatState.lua | 248 +++++++++++++++----------------- src/ui/screens/CombatScreen.lua | 3 +- 2 files changed, 118 insertions(+), 133 deletions(-) diff --git a/src/CombatState.lua b/src/CombatState.lua index 7009196c..7d483c04 100644 --- a/src/CombatState.lua +++ b/src/CombatState.lua @@ -6,14 +6,14 @@ -- Required Modules -- ------------------------------------------------ -local Object = require( 'src.Object' ); +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' ); -local ScreenManager = require( 'lib.screenmanager.ScreenManager' ); +local Factions = require( 'src.characters.Factions' ) +local ProjectileManager = require( 'src.items.weapons.ProjectileManager' ) +local ExplosionManager = require( 'src.items.weapons.ExplosionManager' ) +local ScreenManager = require( 'lib.screenmanager.ScreenManager' ) local StateManager = require( 'src.turnbased.StateManager' ) local SadisticAIDirector = require( 'src.characters.ai.SadisticAIDirector' ) local Faction = require( 'src.characters.Faction' ) @@ -23,173 +23,159 @@ local Settings = require( 'src.Settings' ) -- Module -- ------------------------------------------------ -local CombatState = {} +local CombatState = Class( 'CombatState' ) -- ------------------------------------------------ -- Constants -- ------------------------------------------------ -local FACTIONS = require( 'src.constants.FACTIONS' ); +local FACTIONS = require( 'src.constants.FACTIONS' ) -- ------------------------------------------------ --- Constructor +-- Private Methods -- ------------------------------------------------ -function CombatState.new() - local self = Object.new():addInstance( 'CombatState' ) +local function loadMap( savedMap ) + local loader = MapLoader.new() + local tiles, mw, mh = loader:recreateMap( savedMap ) + local map = Map.new() + map:init( tiles, mw, mh ) + return map +end + +local function createMap() + local generator = ProceduralMapGenerator() + + local tiles = generator:getTiles() + local mw, mh = generator:getTileGridDimensions() + + local map = Map.new() + map:init( tiles, mw, mh ) + map:setSpawnpoints( generator:getSpawnpoints() ) + return map +end - local map; - local factions; - local observations = {}; +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - local stateManager - local states = { +function CombatState:initialize( playerFaction, savegame ) + self.observations = {} + self.states = { execution = require( 'src.turnbased.states.ExecutionState' ), planning = require( 'src.turnbased.states.PlanningState' ) } - local sadisticAIDirector - - -- ------------------------------------------------ - -- Local Methods - -- ------------------------------------------------ + if savegame then + self.map = loadMap( savegame.map ) + else + self.map = createMap() + end - local function loadMap( savedMap ) - local loader = MapLoader.new() - local tiles, mw, mh = loader:recreateMap( savedMap ) - map = Map.new() - map:init( tiles, mw, mh ) + self.factions = Factions.new( self.map ) + self.factions:addFaction( Faction( FACTIONS.ENEMY, true )) + self.factions:addFaction( Faction( FACTIONS.NEUTRAL, true )) + self.factions:addFaction( playerFaction ) + + if savegame then + self.factions:findFaction( FACTIONS.ENEMY ):loadCharacters( savegame.factions[FACTIONS.ENEMY] ) + self.factions:findFaction( FACTIONS.NEUTRAL ):loadCharacters( savegame.factions[FACTIONS.NEUTRAL] ) + else + self.factions:findFaction( FACTIONS.ENEMY ):addCharacters( 10, 'human' ) + self.factions:findFaction( FACTIONS.NEUTRAL ):addCharacters( 5, 'dog' ) end - local function createMap() - local generator = ProceduralMapGenerator() + self.factions:findFaction( FACTIONS.ENEMY ):spawnCharacters( self.map ) + self.factions:findFaction( FACTIONS.NEUTRAL ):spawnCharacters( self.map ) + playerFaction:spawnCharacters( self.map ) - local tiles = generator:getTiles() - local mw, mh = generator:getTileGridDimensions() + -- Generate initial FOV for all factions. + self.factions:iterate( function( faction ) + faction:iterate( function( character ) + character:generateFOV() + end) + end) - map = Map.new() - map:init( tiles, mw, mh ) - map:setSpawnpoints( generator:getSpawnpoints() ) - end + self.stateManager = StateManager( self.states ) + self.stateManager:push( 'planning', self.factions ) - -- ------------------------------------------------ - -- Public Methods - -- ------------------------------------------------ - - function self:init( playerFaction, savegame ) - if savegame then - loadMap( savegame.map ) - else - createMap() - end - - factions = Factions.new( map ); - factions:addFaction( Faction( FACTIONS.ENEMY, true )) - factions:addFaction( Faction( FACTIONS.NEUTRAL, true )) - factions:addFaction( playerFaction ) - - if savegame then - factions:findFaction( FACTIONS.ENEMY ):loadCharacters( savegame.factions[FACTIONS.ENEMY] ) - factions:findFaction( FACTIONS.NEUTRAL ):loadCharacters( savegame.factions[FACTIONS.NEUTRAL] ) - else - factions:findFaction( FACTIONS.ENEMY ):addCharacters( 10, 'human' ) - factions:findFaction( FACTIONS.NEUTRAL ):addCharacters( 5, 'dog' ) - end - - factions:findFaction( FACTIONS.ENEMY ):spawnCharacters( map ) - factions:findFaction( FACTIONS.NEUTRAL ):spawnCharacters( map ) - playerFaction:spawnCharacters( map ) - - -- Generate initial FOV for all factions. - factions:iterate( function( faction ) - faction:iterate( function( character ) - character:generateFOV() - end) - end) + self.sadisticAIDirector = SadisticAIDirector.new( self.factions, self.stateManager ) - stateManager = StateManager( states ) - stateManager:push( 'planning', factions ) + ProjectileManager.init( self.map ) + ExplosionManager.init( self.map ) - sadisticAIDirector = SadisticAIDirector.new( factions, stateManager ) + -- Register obsersvations. + self.observations[#self.observations + 1] = self.map:observe( self.factions ) - ProjectileManager.init( map ); - ExplosionManager.init( map ); + -- Free memory if possible. + collectgarbage( 'collect' ) +end - -- Register obsersvations. - observations[#observations + 1] = map:observe( factions ) +function CombatState:update( dt ) + self.map:update() - -- Free memory if possible. - collectgarbage( 'collect' ); + -- Update AI if current faction is AI controlled. + if self.factions:getFaction():isAIControlled() and not self.stateManager:blocksInput() then + self.sadisticAIDirector:update( dt ) end + self.stateManager:update( dt ) - function self:update( dt ) - map:update(); - - -- Update AI if current faction is AI controlled. - if factions:getFaction():isAIControlled() and not stateManager:blocksInput() then - sadisticAIDirector:update( dt ) - end - stateManager:update( dt ) - - if not factions:getPlayerFaction():hasLivingCharacters() then - ScreenManager.pop() - ScreenManager.push( 'gameover', factions:getPlayerFaction(), false ) - end - if not factions:findFaction( FACTIONS.ENEMY ):hasLivingCharacters() then - ScreenManager.pop() - ScreenManager.push( 'gameover', factions:getPlayerFaction(), true ) - end + if not self.factions:getPlayerFaction():hasLivingCharacters() then + ScreenManager.pop() + ScreenManager.push( 'gameover', self.factions:getPlayerFaction(), false ) end - - function self:serialize() - local t = { - ['type'] = 'combat', - ['map'] = map:serialize(), - ['factions'] = factions:serialize() - }; - return t; + if not self.factions:findFaction( FACTIONS.ENEMY ):hasLivingCharacters() then + ScreenManager.pop() + ScreenManager.push( 'gameover', self.factions:getPlayerFaction(), true ) end +end - function self:close() - ProjectileManager.clear(); - ExplosionManager.clear(); - end +function CombatState:serialize() + local t = { + ['type'] = 'combat', + ['map'] = self.map:serialize(), + ['factions'] = self.factions:serialize() + } + return t +end - function self:getMap() - return map; - end +function CombatState:close() + ProjectileManager.clear() + ExplosionManager.clear() +end - function self:getFactions() - return factions; +function CombatState:keypressed( _, scancode, _ ) + if self.factions:getFaction():isAIControlled() or self.stateManager:blocksInput() then + return end + self.stateManager:input( Settings.mapInput( scancode )) +end - function self:keypressed( _, scancode, _ ) - if factions:getFaction():isAIControlled() or stateManager:blocksInput() then - return - end - stateManager:input( Settings.mapInput( scancode )) +function CombatState:mousepressed( mx, my, button ) + if self.factions:getFaction():isAIControlled() or self.stateManager:blocksInput() then + return end + self.stateManager:selectTile( self.map:getTileAt( mx, my ), button ) +end - function self:mousepressed( mx, my, button ) - if factions:getFaction():isAIControlled() or stateManager:blocksInput() then - return - end - stateManager:selectTile( map:getTileAt( mx, my ), button ) - end +function CombatState:getMap() + return self.map +end - function self:getState() - return stateManager:getState() - end +function CombatState:getFactions() + return self.factions +end - function self:getPlayerFaction() - return factions:getPlayerFaction() - end +function CombatState:getState() + return self.stateManager:getState() +end - function self:getCurrentCharacter() - return factions:getFaction():getCurrentCharacter(); - end +function CombatState:getPlayerFaction() + return self.factions:getPlayerFaction() +end - return self; +function CombatState:getCurrentCharacter() + return self.factions:getFaction():getCurrentCharacter() end return CombatState diff --git a/src/ui/screens/CombatScreen.lua b/src/ui/screens/CombatScreen.lua index be0949b2..f43016ee 100644 --- a/src/ui/screens/CombatScreen.lua +++ b/src/ui/screens/CombatScreen.lua @@ -26,8 +26,7 @@ local CombatScreen = Screen:subclass( 'CombatScreen' ) function CombatScreen:initialize( playerFaction, savegame ) love.mouse.setVisible( true ) - self.combatState = CombatState.new() - self.combatState:init( playerFaction, savegame ) + self.combatState = CombatState( playerFaction, savegame ) self.mapPainter = MapPainter( self.combatState:getMap() ) From ebd577fc45b4b1ed9bcfc91adcbaa17351ba3501 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 18 Dec 2017 11:28:38 +0100 Subject: [PATCH 124/178] Fix whitespace in name list --- res/data/Names.lua | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/res/data/Names.lua b/res/data/Names.lua index d4cd5cd6..9e00f726 100644 --- a/res/data/Names.lua +++ b/res/data/Names.lua @@ -7,10 +7,10 @@ return { "Beck", "Becker", "Berger", - "Bergmann ​", - "Brandt ​", + "Bergmann", + "Brandt", "Braun", - "Busch ​", + "Busch", "Böhm", "Dietrich", "Engel", @@ -29,7 +29,7 @@ return { "Herrmann", "Hoffmann", "Hofmann", - "Horn ​", + "Horn", "Huber", "Jung", "Jäger", @@ -41,10 +41,10 @@ return { "Krause", "Krämer", "Krüger", - "Kuhn ​", + "Kuhn", "Köhler", "König", - "Kühn ​", + "Kühn​", "Lang", "Lange", "Lehmann", @@ -60,18 +60,18 @@ return { "Neumann", "Otto", "Peters", - "Pfeiffer ​", - "Pohl ​", + "Pfeiffer", + "Pohl", "Richter", "Roth", - "Sauer ​", + "Sauer", "Schmid", "Schmidt", "Schmitt", "Schmitz", "Schneider", "Scholz", - "Schreiber ​", + "Schreiber", "Schröder", "Schubert", "Schulte", @@ -81,14 +81,14 @@ return { "Schuster", "Schwarz", "Schäfer", - "Seidel ​", + "Seidel", "Simon", "Sommer", "Stein", "Thomas", "Vogel", "Vogt", - "Voigt ​", + "Voigt", "Wagner", "Walter", "Weber", @@ -97,8 +97,8 @@ return { "Winkler", "Winter", "Wolf", - "Wolff ​", - "Ziegler ​", + "Wolff", + "Ziegler", "Zimmermann" }, finnish = { From 83f9716d4ebb992d5454079f34ff1a69b3a9e04d Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Mon, 18 Dec 2017 02:19:41 +0100 Subject: [PATCH 125/178] Refactor WorldObject --- src/characters/Character.lua | 2 +- src/characters/pathfinding/PathFinder.lua | 4 +- src/map/worldobjects/WorldObject.lua | 436 ++++++++++---------- src/map/worldobjects/WorldObjectFactory.lua | 2 +- 4 files changed, 224 insertions(+), 220 deletions(-) diff --git a/src/characters/Character.lua b/src/characters/Character.lua index 7cb944cb..974686de 100644 --- a/src/characters/Character.lua +++ b/src/characters/Character.lua @@ -66,7 +66,7 @@ local function markSeenTiles( cx, cy, counter, self, falloff ) -- objects and allows smaller objects like low walls to cast a "shadow" -- in which smaller objects could be hidden. if target:hasWorldObject() - and target:getWorldObject():blocksVision() + and target:getWorldObject():doesBlockVision() and height <= target:getWorldObject():getHeight() then return false end diff --git a/src/characters/pathfinding/PathFinder.lua b/src/characters/pathfinding/PathFinder.lua index 7d36ebee..16ab5828 100644 --- a/src/characters/pathfinding/PathFinder.lua +++ b/src/characters/pathfinding/PathFinder.lua @@ -141,12 +141,12 @@ local function isValidTile( tile, closedList, target ) end -- Container objects are valid if they are the target of the path. - if worldObject:blocksPathfinding() and worldObject:isContainer() then + if worldObject:doesBlockPathfinding() and worldObject:isContainer() then return tile == target end -- Non-blocking world objects are valid too. - return not worldObject:blocksPathfinding() + return not worldObject:doesBlockPathfinding() end return tile:isPassable() end diff --git a/src/map/worldobjects/WorldObject.lua b/src/map/worldobjects/WorldObject.lua index 54cc8fdf..fc2d9f9d 100644 --- a/src/map/worldobjects/WorldObject.lua +++ b/src/map/worldobjects/WorldObject.lua @@ -1,254 +1,258 @@ -local Object = require( 'src.Object' ); -local Inventory = require( 'src.inventory.Inventory' ); +--- +-- @module WorldObject +-- + +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ + +local Class = require( 'lib.Middleclass' ) +local Inventory = require( 'src.inventory.Inventory' ) -- ------------------------------------------------ -- Module -- ------------------------------------------------ -local WorldObject = {}; +local WorldObject = Class( 'WorldObject' ) -- ------------------------------------------------ --- Constructor +-- Public Methods -- ------------------------------------------------ --- --- Creates a new instance of the WorldObject class. --- @param template (table) The WorldObject's template. --- @return (WorldObject) The new WorldObject. --- -function WorldObject.new( template ) - local self = Object.new():addInstance( 'WorldObject' ); - - -- ------------------------------------------------ - -- Private Attributes - -- ------------------------------------------------ - - local id = template.id; - local height = template.size - local interactionCost = template.interactionCost; - local energyReduction = template.energyReduction; - local destructible = template.destructible; - local debrisID = template.debrisID; - local openable = template.openable or false; - local climbable = template.climbable or false; - local blocksPathfinding = template.blocksPathfinding; - local container = template.container; - local drops = template.drops; - - local group = template.group - local connections = template.connections - - local hp = template.hp; - local passable = template.passable or false; - local blocksVision = template.blocksVision; - local inventory = container and Inventory.new() or nil; - - -- ------------------------------------------------ - -- Public Methods - -- ------------------------------------------------ - - --- - -- Reduces the WorldObject's hit points. - -- @param dmg (number) The amount of damage that was dealt. - -- - function self:damage( dmg ) - hp = hp - dmg; - end - - function self:serialize() - local t = { - ['id'] = id, - ['hp'] = hp, - ['passable'] = passable, - ['blocksVision'] = blocksVision - } - if container and not inventory:isEmpty() then - t['inventory'] = inventory:serialize(); - end - return t; - end +-- Initializes a new instance of the WorldObject class. +-- @tparam table template The WorldObject's template. +-- +function WorldObject:initialize( template ) + self.id = template.id + self.height = template.size + self.interactionCost = template.interactionCost + self.energyReduction = template.energyReduction + self.destructible = template.destructible + self.debrisID = template.debrisID + self.openable = template.openable or false + self.climbable = template.climbable or false + self.blocksPathfinding = template.blocksPathfinding + self.container = template.container + self.drops = template.drops + + self.group = template.group + self.connections = template.connections + + self.hp = template.hp + self.passable = template.passable or false + self.blocksVision = template.blocksVision + self.inventory = self.container and Inventory.new() or nil +end - -- ------------------------------------------------ - -- Getters - -- ------------------------------------------------ +--- +-- Reduces the WorldObject's hit points. +-- @tparam number dmg The amount of damage that was dealt. +-- +function WorldObject:damage( dmg ) + self.hp = self.hp - dmg +end - --- - -- Returns true if the WorldObject should be ignored during pathfinding. - -- @return (boolean) True if it blocks pathfinding. - -- - function self:blocksPathfinding() - return blocksPathfinding; +--- +-- Serializes the world object. +-- @treturn table The serialized world object. +-- +function WorldObject:serialize() + local t = { + ['id'] = self.id, + ['hp'] = self.hp, + ['passable'] = self.passable, + ['blocksVision'] = self.blocksVision + } + if self.container and not self.inventory:isEmpty() then + t['inventory'] = self.inventory:serialize() end + return t +end - --- - -- Returns true if the WorldObject blocks the line of sight. - -- @return (boolean) True if it blocks vision. - -- - function self:blocksVision() - return blocksVision; - end +-- ------------------------------------------------ +-- Getters +-- ------------------------------------------------ - --- - -- Returns the WorldObject id with which this WorldObject will be replaced - -- upon its destruction. - -- @return (string) The WorldObject id used for generating debris. - -- - function self:getDebrisID() - return debrisID; - end +--- +-- Returns true if the WorldObject should be ignored during pathfinding. +-- @treturn boolean True if it blocks pathfinding. +-- +function WorldObject:doesBlockPathfinding() + return self.blocksPathfinding +end - --- - -- Returns the WorldObject's energy reduction attribute. This is used to - -- reduce the amount of force a projectile has after passing through this - -- WorldObject. - -- @return (number) The WorldObject's energy reduction value. - -- - function self:getEnergyReduction() - return energyReduction; - end +--- +-- Returns true if the WorldObject blocks the line of sight. +-- @treturn boolean True if it blocks vision. +-- +function WorldObject:doesBlockVision() + return self.blocksVision +end - --- - -- Returns the WorldObject's hit points. - -- @return (number) The amount of hit points. - -- - function self:getHitPoints() - return hp; - end +--- +-- Returns the WorldObject id with which this WorldObject will be replaced +-- upon its destruction. +-- @treturn string The WorldObject id used for generating debris. +-- +function WorldObject:getDebrisID() + return self.debrisID +end - --- - -- Returns the WorldObject's interaction cost attribute. The cost of interaction - -- is dependent on the stance a character is currently in. - -- @param stance (string) The stance the character is currently in. - -- @return (number) The amount of AP it costs to interact with this WorldObject. - -- - function self:getInteractionCost( stance ) - if not interactionCost then - return 0; - end - return interactionCost[stance]; - end +--- +-- Returns the WorldObject's energy reduction attribute. This is used to +-- reduce the amount of force a projectile has after passing through this +-- WorldObject. +-- @treturn number The WorldObject's energy reduction value. +-- +function WorldObject:getEnergyReduction() + return self.energyReduction +end - --- - -- Returns this container's inventory. - -- @return (Inventory) The inventory. - -- - function self:getInventory() - return inventory; - end +--- +-- Returns the WorldObject's hit points. +-- @treturn number The amount of hit points. +-- +function WorldObject:getHitPoints() + return self.hp +end - --- - -- Returns the WorldObject's height attribute. - -- @return (number) The WorldObject's height. - -- - function self:getHeight() - return height +--- +-- Returns the WorldObject's interaction cost attribute. The cost of interaction +-- is dependent on the stance a character is currently in. +-- @tparam string stance The stance the character is currently in. +-- @treturn number The amount of AP it costs to interact with this WorldObject. +-- +function WorldObject:getInteractionCost( stance ) + if not self.interactionCost then + return 0 end + return self.interactionCost[stance] +end - --- - -- Returns the WorldObject's id. - -- @return (string) The WorldObject's id. - -- - function self:getID() - return id; - end +--- +-- Returns this container's inventory. +-- @treturn Inventory The inventory. +-- +function WorldObject:getInventory() + return self.inventory +end - --- - -- Checks wether the WorldObject's can be climbed over (i.e.: Fences). - -- @return (boolean) True if the WorldObject is climbable. - -- - function self:isClimbable() - return climbable; - end +--- +-- Returns the WorldObject's height attribute. +-- @treturn number The WorldObject's height. +-- +function WorldObject:getHeight() + return self.height +end - --- - -- Checks wether the WorldObject is a container. - -- @return (boolean) True if the WorldObject is a container. - -- - function self:isContainer() - return container; - end +--- +-- Returns the WorldObject's id. +-- @treturn string The WorldObject's id. +-- +function WorldObject:getID() + return self.id +end - --- - -- Checks wether the WorldObject's hit points have been reduced to zero. - -- @return (boolean) True if the WorldObject's hit points are less or equal zero. - -- - function self:isDestroyed() - return destructible and hp <= 0 or false; - end +--- +-- Checks wether the WorldObject's can be climbed over (i.e.: Fences). +-- @treturn boolean True if the WorldObject is climbable. +-- +function WorldObject:isClimbable() + return self.climbable +end - --- - -- Gets the WorldObject's group. - -- @treturn string The group. - -- - function self:getGroup() - return group - end +--- +-- Checks wether the WorldObject is a container. +-- @treturn boolean True if the WorldObject is a container. +-- +function WorldObject:isContainer() + return self.container +end - --- - -- Gets the WorldObject's connections. - -- @treturn table The connections. - -- - function self:getConnections() - return connections - end +--- +-- Checks wether the WorldObject's hit points have been reduced to zero. +-- @treturn boolean True if the WorldObject's hit points are less or equal zero. +-- +function WorldObject:isDestroyed() + return self.destructible and self.hp <= 0 or false +end - --- - -- Checks wether the WorldObject is destructible. - -- @return (boolean) True if the WorldObject is destructible. - -- - function self:isDestructible() - return destructible; - end +--- +-- Gets the WorldObject's group. +-- @treturn string The group. +-- +function WorldObject:getGroup() + return self.group +end - --- - -- Checks wether the WorldObject can be opened (i.e.: Doors, Gates, ...). - -- @return (boolean) True if the WorldObject is openable. - -- - function self:isOpenable() - return openable; - end +--- +-- Gets the WorldObject's connections. +-- @treturn table The connections. +-- +function WorldObject:getConnections() + return self.connections +end - --- - -- Checks wether the WorldObject can be passed through by a Character. - -- @return (boolean) True if the WorldObject is passable. - -- - function self:isPassable() - return passable; - end +--- +-- Checks wether the WorldObject is destructible. +-- @treturn boolean True if the WorldObject is destructible. +-- +function WorldObject:isDestructible() + return self.destructible +end - function self:getDrops() - return drops; - end +--- +-- Checks wether the WorldObject can be opened (i.e.: Doors, Gates, ...). +-- @treturn boolean True if the WorldObject is openable. +-- +function WorldObject:isOpenable() + return self.openable +end - -- ------------------------------------------------ - -- Setters - -- ------------------------------------------------ +--- +-- Checks wether the WorldObject can be passed through by a Character. +-- @treturn boolean True if the WorldObject is passable. +-- +function WorldObject:isPassable() + return self.passable +end - --- - -- Sets the WorldObject's blocksVision attribute. - -- @param nblocksVision (boolean) The new attribute value. - -- - function self:setBlocksVision( nblocksVision ) - blocksVision = nblocksVision; - end +--- +-- Returns a table containing the possible drops for when this world object is +-- being destroyed. +-- @treturn table The drops. +-- +function WorldObject:getDrops() + return self.drops +end - --- - -- Sets this WorldObject's hit points. - -- @param nhp (number) The new hit point value. - -- - function self:setHitPoints( nhp ) - hp = nhp; - end +-- ------------------------------------------------ +-- Setters +-- ------------------------------------------------ - --- - -- Sets the WorldObject's passable attribute. - -- @param npassable (boolean) The new attribute value. - -- - function self:setPassable( npassable ) - passable = npassable; - end +--- +-- Sets the WorldObject's blocksVision attribute. +-- @tparam boolean blocksVision The new attribute value. +-- +function WorldObject:setBlocksVision( blocksVision ) + self.blocksVision = blocksVision +end - return self; +--- +-- Sets this WorldObject's hit points. +-- @tparam number hp The new hit point value. +-- +function WorldObject:setHitPoints( hp ) + self.hp = hp +end + +--- +-- Sets the WorldObject's passable attribute. +-- @tparam boolean passable The new attribute value. +-- +function WorldObject:setPassable( passable ) + self.passable = passable end -return WorldObject; +return WorldObject diff --git a/src/map/worldobjects/WorldObjectFactory.lua b/src/map/worldobjects/WorldObjectFactory.lua index 9ef5dbab..e2375051 100644 --- a/src/map/worldobjects/WorldObjectFactory.lua +++ b/src/map/worldobjects/WorldObjectFactory.lua @@ -69,7 +69,7 @@ end function WorldObjectFactory.create( id ) local template = worldobjects[id] assert( template, string.format( 'Requested worldobject id (%s) doesn\'t exist!', id )) - return WorldObject.new( template ) + return WorldObject( template ) end --- From 57e344f7425c4bac556b0a95f9a372d3c1925d81 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Tue, 19 Dec 2017 12:14:30 +0100 Subject: [PATCH 126/178] Refactor SadisticAIDirector --- src/CombatState.lua | 2 +- src/characters/ai/SadisticAIDirector.lua | 83 +++++++++++++++--------- 2 files changed, 52 insertions(+), 33 deletions(-) diff --git a/src/CombatState.lua b/src/CombatState.lua index 7d483c04..77fe675c 100644 --- a/src/CombatState.lua +++ b/src/CombatState.lua @@ -99,7 +99,7 @@ function CombatState:initialize( playerFaction, savegame ) self.stateManager = StateManager( self.states ) self.stateManager:push( 'planning', self.factions ) - self.sadisticAIDirector = SadisticAIDirector.new( self.factions, self.stateManager ) + self.sadisticAIDirector = SadisticAIDirector( self.factions, self.stateManager ) ProjectileManager.init( self.map ) ExplosionManager.init( self.map ) diff --git a/src/characters/ai/SadisticAIDirector.lua b/src/characters/ai/SadisticAIDirector.lua index 8bbab544..11e1fa7c 100644 --- a/src/characters/ai/SadisticAIDirector.lua +++ b/src/characters/ai/SadisticAIDirector.lua @@ -1,41 +1,60 @@ -local Log = require( 'src.util.Log' ); -local Object = require('src.Object'); -local BehaviorTreeFactory = require( 'src.characters.ai.behaviortree.BehaviorTreeFactory' ); +--- +-- @module SadisticAIDirector +-- -local SadisticAIDirector = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ -function SadisticAIDirector.new( factions, states ) - local self = Object.new():addInstance( 'SadisticAIDirector' ); +local Class = require( 'lib.Middleclass' ) +local Log = require( 'src.util.Log' ) +local BehaviorTreeFactory = require( 'src.characters.ai.behaviortree.BehaviorTreeFactory' ) - local function tickBehaviorTree( tree, character ) - Log.debug( "Tick BehaviorTree for " .. tostring( character ), 'SadisticAIDirector' ); - return tree:traverse( {}, character, states, factions ); +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ + +local SadisticAIDirector = Class( 'SadisticAIDirector' ) + +-- ------------------------------------------------ +-- Private Methods +-- ------------------------------------------------ + +local function tickBehaviorTree( tree, character, states, factions ) + Log.debug( "Tick BehaviorTree for " .. tostring( character ), 'SadisticAIDirector' ) + return tree:traverse( {}, character, states, factions ) +end + +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ + +function SadisticAIDirector:initialize( factions, states ) + self.factions = factions + self.states = states +end + +function SadisticAIDirector:update() + local faction = self.factions:getFaction() + local tree = BehaviorTreeFactory.getTree( faction:getType() ) + if faction:hasFinishedTurn() then + Log.debug( 'Select next faction', 'SadisticAIDirector' ) + self.factions:nextFaction() + return end - function self:update() - local faction = factions:getFaction(); - local tree = BehaviorTreeFactory.getTree( faction:getType() ); - if faction:hasFinishedTurn() then - Log.debug( 'Select next faction', 'SadisticAIDirector' ); - factions:nextFaction(); - return; - end - - -- Select the next character who hasn't finished his turn yet. - Log.debug( 'Select next character for this turn', 'SadisticAIDirector' ); - local character = faction:nextCharacterForTurn(); - - local success = tickBehaviorTree( tree, character ); - if success then - states:push( 'execution', factions, character ); - return; - end - - -- Mark the character as done for this turn. - character:setFinishedTurn( true ); + -- Select the next character who hasn't finished his turn yet. + Log.debug( 'Select next character for this turn', 'SadisticAIDirector' ) + local character = faction:nextCharacterForTurn() + + local success = tickBehaviorTree( tree, character, self.states, self.factions ) + if success then + self.states:push( 'execution', self.factions, character ) + return end - return self; + -- Mark the character as done for this turn. + character:setFinishedTurn( true ) end -return SadisticAIDirector; +return SadisticAIDirector From fb3afb304d93f0b2f8c646ec3154d92948e63708 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Tue, 19 Dec 2017 12:22:54 +0100 Subject: [PATCH 127/178] Refactor EquipmentSlot --- src/characters/body/BodyFactory.lua | 2 +- src/characters/body/EquipmentSlot.lua | 106 +++++++++++++++----------- 2 files changed, 64 insertions(+), 44 deletions(-) diff --git a/src/characters/body/BodyFactory.lua b/src/characters/body/BodyFactory.lua index df6c0934..00c76678 100644 --- a/src/characters/body/BodyFactory.lua +++ b/src/characters/body/BodyFactory.lua @@ -110,7 +110,7 @@ end local function createBodyPart( cid, body, equipment, index, id ) local template = templates[cid][id]; if template.type == 'equipment' then - equipment:addSlot( EquipmentSlot.new( index, template )); + equipment:addSlot( EquipmentSlot( index, template.id, template.itemType, template.subType, template.sort )) else body:addBodyPart( BodyPart( index, template.id, template.type, template.health, template.effects )) end diff --git a/src/characters/body/EquipmentSlot.lua b/src/characters/body/EquipmentSlot.lua index 3c8a7456..2820ac42 100644 --- a/src/characters/body/EquipmentSlot.lua +++ b/src/characters/body/EquipmentSlot.lua @@ -1,60 +1,80 @@ -local Object = require( 'src.Object' ); +--- +-- @module EquipmentSlot +-- -local EquipmentSlot = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ -function EquipmentSlot.new( index, template ) - local self = Object.new():addInstance( 'EquipmentSlot' ); +local Class = require( 'lib.Middleclass' ) - local item; +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ - function self:getIndex() - return index; - end +local EquipmentSlot = Class( 'EquipmentSlot' ) - function self:getID() - return template.id; - end +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - function self:addItem( nitem ) - assert( nitem:getItemType() == self:getItemType(), "Item types do not match." ); - item = nitem; - return true; - end +function EquipmentSlot:initialize( index, id, itemType, subType, order ) + self.index = index + self.id = id + self.itemType = itemType + self.subType = subType + self.order = order +end - function self:getItem() - return item; - end +function EquipmentSlot:addItem( item ) + assert( item:getItemType() == self:getItemType(), "Item types do not match." ) + self.item = item + return true +end - function self:removeItem() - assert( item ~= nil, "Can't remove item from an empty slot." ); - item = nil; - end +function EquipmentSlot:removeItem() + assert( self.item ~= nil, "Can't remove item from an empty slot." ) + self.item = nil +end - function self:getItemType() - return template.itemType; +function EquipmentSlot:serialize() + local t = {} + if self.item then + t['item'] = self.item:serialize() end + return t +end - function self:getSubType() - return template.subType; - end +function EquipmentSlot:containsItem() + return self.item ~= nil +end - function self:containsItem() - return item ~= nil; - end +-- ------------------------------------------------ +-- Getters +-- ------------------------------------------------ - function self:getSortOrder() - return template.sort; - end +function EquipmentSlot:getID() + return self.id +end - function self:serialize() - local t = {}; - if item then - t['item'] = item:serialize() - end - return t; - end +function EquipmentSlot:getIndex() + return self.index +end + +function EquipmentSlot:getItem() + return self.item +end + +function EquipmentSlot:getItemType() + return self.itemType +end + +function EquipmentSlot:getSubType() + return self.subType +end - return self; +function EquipmentSlot:getSortOrder() + return self.order end -return EquipmentSlot; +return EquipmentSlot From a698e41cd1df4cd44c1e4c7057acf8581ff07ab0 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Tue, 19 Dec 2017 13:36:05 +0100 Subject: [PATCH 128/178] Refactor Inventory --- src/characters/body/BodyFactory.lua | 2 +- src/inventory/Inventory.lua | 708 ++++++++++++++------------- src/map/tiles/Tile.lua | 2 +- src/map/worldobjects/WorldObject.lua | 2 +- src/ui/screens/InventoryScreen.lua | 2 +- 5 files changed, 365 insertions(+), 351 deletions(-) diff --git a/src/characters/body/BodyFactory.lua b/src/characters/body/BodyFactory.lua index 00c76678..64572f2e 100644 --- a/src/characters/body/BodyFactory.lua +++ b/src/characters/body/BodyFactory.lua @@ -127,7 +127,7 @@ end local function assembleBody( creatureID, template, layout ) local body = Body( template.id, template.bloodVolume, template.tags, template.size ) local equipment = Equipment.new(); - local inventory = Inventory.new( template.defaultCarryWeight, template.defaultCarryVolume ) + local inventory = Inventory( template.defaultCarryWeight, template.defaultCarryVolume ) equipment:observe( inventory ); diff --git a/src/inventory/Inventory.lua b/src/inventory/Inventory.lua index 756fd4d2..68374726 100644 --- a/src/inventory/Inventory.lua +++ b/src/inventory/Inventory.lua @@ -1,437 +1,451 @@ -local Log = require( 'src.util.Log' ); -local Object = require( 'src.Object' ); -local ItemStack = require( 'src.inventory.ItemStack' ); -local ItemFactory = require( 'src.items.ItemFactory' ); +--- +-- @module Inventory +-- + +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ + +local Class = require( 'lib.Middleclass' ) +local Log = require( 'src.util.Log' ) +local ItemStack = require( 'src.inventory.ItemStack' ) +local ItemFactory = require( 'src.items.ItemFactory' ) -- ------------------------------------------------ -- Module -- ------------------------------------------------ -local Inventory = {}; +local Inventory = Class( 'Inventory' ) -- ------------------------------------------------ -- Constants -- ------------------------------------------------ local ITEM_TYPES = require('src.constants.ITEM_TYPES') -local DEFAULT_WEIGHT_LIMIT = 50; -local DEFAULT_VOLUME_LIMIT = 50; +local DEFAULT_WEIGHT_LIMIT = 50 +local DEFAULT_VOLUME_LIMIT = 50 -- ------------------------------------------------ --- Constructor +-- Private Methods -- ------------------------------------------------ -function Inventory.new( weightLimit, volumeLimit ) - local self = Object.new():addInstance( 'Inventory' ); - - -- ------------------------------------------------ - -- Private Attributes - -- ------------------------------------------------ - - local items = {}; - weightLimit = weightLimit or DEFAULT_WEIGHT_LIMIT; - volumeLimit = volumeLimit or DEFAULT_VOLUME_LIMIT; - - -- ------------------------------------------------ - -- Private Methods - -- ------------------------------------------------ - - --- - -- Returns the combined weight of all items in the inventory. - -- @return (number) The total weight of all items in the inventory. - -- - local function calculateWeight() - local weight = 0; - for _, item in ipairs( items ) do - weight = weight + item:getWeight(); - end - return weight; +--- +-- Returns the combined weight of all items in the inventory. +-- @tparam table items The table containing all items inside of this inventory. +-- @treturn number The total weight of all items in the inventory. +-- +local function calculateWeight( items ) + local weight = 0 + for _, item in ipairs( items ) do + weight = weight + item:getWeight() end + return weight +end - --- - -- Returns the combined volume of all items in the inventory. - -- @return (number) The total volume of all items in the inventory. - -- - local function calculateVolume() - local volume = 0; - for _, item in ipairs( items ) do - volume = volume + item:getVolume(); - end - return volume; +--- +-- Returns the combined volume of all items in the inventory. +-- @tparam table items The table containing all items inside of this inventory. +-- @treturn number The total volume of all items in the inventory. +-- +local function calculateVolume( items ) + local volume = 0 + for _, item in ipairs( items ) do + volume = volume + item:getVolume() end + return volume +end - --- - -- Adds an Item to the inventory. - -- @param item (Item) The Item to add. - -- @param index (number) The index at which to insert the item. - -- @return (boolean) True if the Item was added successfully. - -- - local function addItem( item, index ) - table.insert( items, index, item ); - return true; - end +--- +-- Adds an Item to the inventory. +-- @tparam table items The table containing all items inside of this inventory. +-- @tparam Item item The Item to add. +-- @tparam numnber index The index at which to insert the item. +-- @treturn boolean True if the Item was added successfully. +-- +local function addItem( items, item, index ) + table.insert( items, index, item ) + return true +end - --- - -- Adds an ItemStack to the inventory. - -- @param stack (ItemStack) The ItemStack to add. - -- @param index (number) The index at which to insert the stack. - -- @return (boolean) True if the ItemStack was added successfully. - -- - local function addItemStack( stack, index ) - table.insert( items, index, stack ); - return true; - end +--- +-- Adds an ItemStack to the inventory. +-- @tparam table items The table containing all items inside of this inventory. +-- @tparam ItemStack stack The ItemStack to add. +-- @tparam number index The index at which to insert the stack. +-- @treturn boolean True if the ItemStack was added successfully. +-- +local function addItemStack( items, stack, index ) + table.insert( items, index, stack ) + return true +end - --- - -- Adds a stackable item to the inventory. If the inventory already contains - -- an ItemStack for this Item's ID, it will be added to the existing stack. - -- If not, a new ItemStack for this item will be created. - -- @param item (Item) The stackable item to add. - -- @param index (number) The index at which to insert the item. - -- @return (boolean) True if the item was added successfully. - -- - local function addStackableItem( item, index ) - -- Check if we already have an item stack to add this item to. - for _, stack in ipairs( items ) do - if stack:instanceOf( 'ItemStack' ) and stack:getID() == item:getID() then - stack:addItem( item ); - return true; - end +--- +-- Adds a stackable item to the inventory. If the inventory already contains +-- an ItemStack for this Item's ID, it will be added to the existing stack. +-- If not, a new ItemStack for this item will be created. +-- @tparam table items The table containing all items inside of this inventory. +-- @tparam Item item The stackable item to add. +-- @tparam number index The index at which to insert the item. +-- @treturn boolean True if the item was added successfully. +-- +local function addStackableItem( items, item, index ) + -- Check if we already have an item stack to add this item to. + for _, stack in ipairs( items ) do + if stack:instanceOf( 'ItemStack' ) and stack:getID() == item:getID() then + stack:addItem( item ) + return true end - - -- If not we create a new stack. - local stack = ItemStack.new( item:getID() ); - stack:addItem( item ); - table.insert( items, index, stack ); - return true; end - --- - -- Removes an Item from the inventory. - -- @param item (Item) The Item to remove. - -- @return (boolean) True if the Item was removed successfully. - -- - local function removeItem( item ) - for i = 1, #items do - -- Check if item is part of a stack. - if items[i]:instanceOf( 'ItemStack' ) then - local success = items[i]:removeItem( item ); - if success then - -- Remove the stack if it is empty. - if items[i]:isEmpty() then - table.remove( items, i ); - end - return true; + -- If not we create a new stack. + local stack = ItemStack.new( item:getID() ) + stack:addItem( item ) + table.insert( items, index, stack ) + return true +end + +--- +-- Removes an Item from the inventory. +-- @tparam table items The table containing all items inside of this inventory. +-- @tparam Item item The Item to remove. +-- @treturn boolean True if the Item was removed successfully. +-- +local function removeItem( items, item ) + for i = 1, #items do + -- Check if item is part of a stack. + if items[i]:instanceOf( 'ItemStack' ) then + local success = items[i]:removeItem( item ) + if success then + -- Remove the stack if it is empty. + if items[i]:isEmpty() then + table.remove( items, i ) end - elseif items[i] == item then - table.remove( items, i ); - return true; + return true end + elseif items[i] == item then + table.remove( items, i ) + return true end - return false; end + return false +end - --- - -- Removes an ItemStack from the inventory. - -- @param stack (ItemStack) The ItemStack to remove. - -- @return (boolean) True if the ItemStack was removed successfully. - -- - local function removeItemStack( stack ) - for i = 1, #items do - if items[i]:instanceOf( 'ItemStack' ) and items[i] == stack then - table.remove( items, i ); - return true; - end +--- +-- Removes an ItemStack from the inventory. +-- @tparam table items The table containing all items inside of this inventory. +-- @tparam stack ItemStack The ItemStack to remove. +-- @treturn boolean True if the ItemStack was removed successfully. +-- +local function removeItemStack( items, stack ) + for i = 1, #items do + if items[i]:instanceOf( 'ItemStack' ) and items[i] == stack then + table.remove( items, i ) + return true end - return false; end + return false +end - -- TODO: proper documentation - local function merge( stack, ostack ) - assert( stack:instanceOf( 'ItemStack' ), 'Expected parameter of type ItemStack.' ); - assert( ostack:instanceOf( 'ItemStack' ), 'Expected parameter of type ItemStack.' ); +-- TODO: proper documentation +local function merge( self, stack, ostack ) + assert( stack:instanceOf( 'ItemStack' ), 'Expected parameter of type ItemStack.' ) + assert( ostack:instanceOf( 'ItemStack' ), 'Expected parameter of type ItemStack.' ) - for i = #ostack:getItems(), 1, -1 do - local item = ostack:getItems()[i]; + for i = #ostack:getItems(), 1, -1 do + local item = ostack:getItems()[i] - if not self:doesFit( item:getWeight(), item:getVolume() ) then - return false; - end - - stack:addItem( item ); - ostack:removeItem( item ); + if not self:doesFit( item:getWeight(), item:getVolume() ) then + return false end - return true; + stack:addItem( item ) + ostack:removeItem( item ) end - local function updateVolume( dvol ) - volumeLimit = volumeLimit + dvol; - end + return true +end - -- ------------------------------------------------ - -- Public Methods - -- ------------------------------------------------ - - --- - -- Drops items until the volume of the carried items is smaller than the - -- maximum volume. - -- @param tile (Tile) The tile to drop the items on. - -- - function self:dropItems( tile ) - for i = #items, 1, -1 do - if calculateVolume() > volumeLimit then - local success = tile:getInventory():addItem( items[i] ); - if success then - self:removeItem( items[i] ); - else - break; - end +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ + +function Inventory:initialize( weightLimit, volumeLimit ) + self.weightLimit = weightLimit or DEFAULT_WEIGHT_LIMIT + self.volumeLimit = volumeLimit or DEFAULT_VOLUME_LIMIT + + self.items = {} +end + +--- +-- Drops items until the volume of the carried items is smaller than the +-- maximum volume. +-- @tparam Tile tile The tile to drop the items on. +-- +function Inventory:dropItems( tile ) + for i = #self.items, 1, -1 do + if calculateVolume( self.items ) > self.volumeLimit then + local success = tile:getInventory():addItem( self.items[i] ) + if success then + self:removeItem( self.items[i] ) + else + break end end end +end - --- - -- Drops all items in the inventory with no regards of successfully adding - -- them to the target tile. - -- @param tile (Tile) The tile to drop the items on. - -- - function self:dropAllItems( tile ) - for i = #items, 1, -1 do - tile:getInventory():addItem( items[i] ); - self:removeItem( items[i] ); - end +--- +-- Drops all items in the inventory with no regards of successfully adding +-- them to the target tile. +-- @tparam Tile tile The tile to drop the items on. +-- +function Inventory:dropAllItems( tile ) + for i = #self.items, 1, -1 do + tile:getInventory():addItem( self.items[i] ) + self:removeItem( self.items[i] ) end +end - --- - -- Checks if the item fits in the current inventory by checking the weight - -- and volume parameters. - -- @param weight (number) The weight of the item to check. - -- @param volume (number) The volume of the item to check. - -- @return (boolean) Returns true if the item fits. - -- - function self:doesFit( weight, volume ) - return ( calculateWeight() + weight < weightLimit ) and ( calculateVolume() + volume < volumeLimit ); - end +--- +-- Checks if the item fits in the current inventory by checking the weight +-- and volume parameters. +-- @tparam number weight The weight of the item to check. +-- @tparam number volume The volume of the item to check. +-- @treturn boolean Returns true if the item fits. +-- +function Inventory:doesFit( weight, volume ) + return ( calculateWeight( self.items ) + weight < self.weightLimit ) and ( calculateVolume( self.items ) + volume < self.volumeLimit ) +end - --- - -- Adds an item to the inventory. - -- @param item (Item) The item to add. - -- @param index (number) The index at which to insert the item (optional). - -- @return (boolean) True if the item was added successfully. - -- - function self:addItem( item, index ) - index = index or ( #items + 1 ); +--- +-- Adds an item to the inventory. +-- @tparam Item item The item to add. +-- @tparam number index The index at which to insert the item (optional). +-- @treturn boolean True if the item was added successfully. +-- +function Inventory:addItem( item, index ) + index = index or ( #self.items + 1 ) + + if not self:doesFit( item:getWeight(), item:getVolume() ) then + Log.warn( 'Item doesn\'t fit into the inventory.', 'Inventory' ) + return false + end - if not self:doesFit( item:getWeight(), item:getVolume() ) then - Log.warn( 'Item doesn\'t fit into the inventory.', 'Inventory' ); - return false; - end + if item:instanceOf( 'ItemStack' ) then + return addItemStack( self.items, item, index ) + end - if item:instanceOf( 'ItemStack' ) then - return addItemStack( item, index ); - end - - if item:instanceOf( 'Item' ) then - if item:isStackable() then - return addStackableItem( item, index ); - else - return addItem( item, index ); - end + if item:instanceOf( 'Item' ) then + if item:isStackable() then + return addStackableItem( self.items, item, index ) + else + return addItem( self.items, item, index ) end end +end - --- - -- Inserts an item at the position of another item. - -- @param item (Item) The item to insert. - -- @param oitem (Item) The item to use as the position. - -- @return (boolean) True if the item was inserted successfully; - -- - function self:insertItem( item, oitem ) - for i = 1, #items do - if items[i] == oitem then - if oitem:instanceOf( 'ItemStack' ) and oitem:getID() == item:getID() then - if item:instanceOf( 'ItemStack' ) then - return merge( oitem, item ); - end +--- +-- Inserts an item at the position of another item. +-- @tparam Item item The item to insert. +-- @tparam Item oitem The item to use as the position. +-- @treturn boolean True if the item was inserted successfully +-- +function Inventory:insertItem( item, oitem ) + for i = 1, #self.items do + if self.items[i] == oitem then + if oitem:instanceOf( 'ItemStack' ) and oitem:getID() == item:getID() then + if item:instanceOf( 'ItemStack' ) then + return merge( self, oitem, item ) end - return self:addItem( item, i ); end + return self:addItem( item, i ) end end +end - --- - -- Removes an item from the inventory. - -- @param item (Item) The item to remove. - -- @return (boolean) True if the item was removed successfully. - -- - function self:removeItem( item ) - if item:instanceOf( 'ItemStack' ) then - return removeItemStack( item ); - end +--- +-- Removes an item from the inventory. +-- @tparam Item item The item to remove. +-- @treturn boolean True if the item was removed successfully. +-- +function Inventory:removeItem( item ) + if item:instanceOf( 'ItemStack' ) then + return removeItemStack( self.items, item ) + end - if item:instanceOf( 'Item') then - return removeItem( item ); - end + if item:instanceOf( 'Item') then + return removeItem( self.items, item ) end +end - --- - -- Serializes the inventory. - -- @return (table) The serialized object. - -- - function self:serialize() - local t = {}; - for i = 1, #items do - table.insert( t, items[i]:serialize() ); - end - return t; +--- +-- Serializes the inventory. +-- @treturn table The serialized object. +-- +function Inventory:serialize() + local t = {} + for i = 1, #self.items do + table.insert( t, self.items[i]:serialize() ) end + return t +end - --- - -- Loads items. - -- - function self:loadItems( loadedItems ) - for _, item in pairs( loadedItems ) do - if item.ItemStack then - for _, sitem in ipairs( item.items ) do - self:addItem( ItemFactory.loadItem( sitem )); - end - else - self:addItem( ItemFactory.loadItem( item )); +--- +-- Loads items. +-- +function Inventory:loadItems( loadedItems ) + for _, item in pairs( loadedItems ) do + if item.ItemStack then + for _, sitem in ipairs( item.items ) do + self:addItem( ItemFactory.loadItem( sitem )) end + else + self:addItem( ItemFactory.loadItem( item )) end end +end - --- - -- Checks if the inventory contains a certain type of item. - -- @param type (string) The type to check for. - -- @return (boolean) True if an item of the specified type was found. - -- - function self:containsItemType( type ) - for i = 1, #items do - if items[i]:getItemType() == type then - return true; - end +--- +-- Checks if the inventory contains a certain type of item. +-- @tparam string type The type to check for. +-- @treturn boolean True if an item of the specified type was found. +-- +function Inventory:containsItemType( type ) + for i = 1, #self.items do + if self.items[i]:getItemType() == type then + return true end - return false; end + return false +end - --- - -- Gets an item of the specified type and removes it from the inventory. - -- @param type (string) The type of item to remove. - -- @return (Item) An item of the specified type. - -- - function self:getAndRemoveItem( type ) - for _, item in ipairs( items ) do - if item:getItemType() == type then - if item:instanceOf( 'ItemStack' ) then - local i = item:getItem(); - self:removeItem( i ); - return i; - else - self:removeItem( item ); - return item; - end +--- +-- Gets an item of the specified type and removes it from the inventory. +-- @tparam string type The type of item to remove. +-- @treturn Item An item of the specified type. +-- +function Inventory:getAndRemoveItem( type ) + for _, item in ipairs( self.items ) do + if item:getItemType() == type then + if item:instanceOf( 'ItemStack' ) then + local i = item:getItem() + self:removeItem( i ) + return i + else + self:removeItem( item ) + return item end end end +end - --- - -- Counts items of a certain type and id. - -- Items that are contained within a stack will be counted too. - -- - -- @tparam string type The item type to search for. - -- @tparam string id The item id to search for. - -- @treturn number The total amount of found items. - -- - function self:countItems( type, id ) - local count = 0 - for _, item in ipairs( items ) do - -- Match items based on type and id. - if item:getItemType() == type and item:getID() == id then - -- Count items in stacks. - if item:instanceOf( 'ItemStack' ) then - count = count + item:getItemCount() - else - count = count + 1 - end +--- +-- Counts items of a certain type and id. +-- Items that are contained within a stack will be counted too. +-- +-- @tparam string type The item type to search for. +-- @tparam string id The item id to search for. +-- @treturn number The total amount of found items. +-- +function Inventory:countItems( type, id ) + local count = 0 + for _, item in ipairs( self.items ) do + -- Match items based on type and id. + if item:getItemType() == type and item:getID() == id then + -- Count items in stacks. + if item:instanceOf( 'ItemStack' ) then + count = count + item:getItemCount() + else + count = count + 1 end end - return count end + return count +end - function self:receive( event, ... ) - if event == 'CHANGE_VOLUME' then - updateVolume( ... ); - end +--- +-- Receives events. +-- @tparam string event The received event. +-- @tparam varargs ... Variable arguments. +-- +function Inventory:receive( event, ... ) + if event == 'CHANGE_VOLUME' then + local delta = ... + self.volumeLimit = self.volumeLimit + delta end +end - -- ------------------------------------------------ - -- Getters - -- ------------------------------------------------ +-- ------------------------------------------------ +-- Getters +-- ------------------------------------------------ - --- - -- Gets an item of type weapon. - -- @return (Weapon) The weapon item. - -- - function self:getWeapon() - return self:getItem( ITEM_TYPES.WEAPON ); - end +--- +-- Gets an item of type weapon. +-- @treturn Weapon The weapon item. +-- +function Inventory:getWeapon() + return self:getItem( ITEM_TYPES.WEAPON ) +end - --- - -- Gets an item of the specified type without removing it from the inventory. - -- @param type (string) The type of item to get. - -- @return (Item) An item of the specified type. - -- - function self:getItem( type ) - for i = 1, #items do - if items[i]:getItemType() == type then - return items[i]; - end +--- +-- Gets an item of the specified type without removing it from the inventory. +-- @tparam string type The type of item to get. +-- @treturn Item An item of the specified type. +-- +function Inventory:getItem( type ) + for i = 1, #self.items do + if self.items[i]:getItemType() == type then + return self.items[i] end end +end - --- - -- Gets the table of items this inventory contains. - -- @return (table) A sequence containing the items and stacks in this inventory. - -- - function self:getItems() - return items; - end - - --- - -- Checks if the inventory is empty. - -- @return (boolean) True if the inventory doesn't contain any items or stacks. - -- - function self:isEmpty() - return #items == 0; - end +--- +-- Gets the table of items this inventory contains. +-- @treturn table A sequence containing the items and stacks in this inventory. +-- +function Inventory:getItems() + return self.items +end - --- - -- Gets the used weight for this inventory. - -- @return (number) The weight for this inventory. - -- - function self:getWeight() - return calculateWeight(); - end +--- +-- Checks if the inventory is empty. +-- @treturn boolean True if the inventory doesn't contain any items or stacks. +-- +function Inventory:isEmpty() + return #self.items == 0 +end - --- - -- Gets the weight limit for this inventory. - -- @return (number) The weight limit for this inventory. - -- - function self:getWeightLimit() - return weightLimit; - end +--- +-- Gets the used weight for this inventory. +-- @treturn number The weight for this inventory. +-- +function Inventory:getWeight() + return calculateWeight( self.items ) +end - function self:getVolume() - return calculateVolume(); - end +--- +-- Gets the current volume for this inventory. +-- @treturn number The volume for this inventory. +-- +function Inventory:getVolume() + return calculateVolume( self.items ) +end - function self:getVolumeLimit() - return volumeLimit; - end +--- +-- Gets the weight limit for this inventory. +-- @treturn number The weight limit for this inventory. +-- +function Inventory:getWeightLimit() + return self.weightLimit +end - return self; +--- +-- Gets the volume limit for this inventory. +-- @treturn number The volume limit for this inventory. +-- +function Inventory:getVolumeLimit() + return self.volumeLimit end -return Inventory; +return Inventory diff --git a/src/map/tiles/Tile.lua b/src/map/tiles/Tile.lua index de9c329d..524c55ad 100644 --- a/src/map/tiles/Tile.lua +++ b/src/map/tiles/Tile.lua @@ -45,7 +45,7 @@ function Tile:initialize( x, y, id, cost, passable, spawn ) self.passable = passable self.spawn = spawn - self.inventory = Inventory.new( WEIGHT_LIMIT, VOLUME_LIMIT ) + self.inventory = Inventory( WEIGHT_LIMIT, VOLUME_LIMIT ) end --- diff --git a/src/map/worldobjects/WorldObject.lua b/src/map/worldobjects/WorldObject.lua index fc2d9f9d..fa618102 100644 --- a/src/map/worldobjects/WorldObject.lua +++ b/src/map/worldobjects/WorldObject.lua @@ -42,7 +42,7 @@ function WorldObject:initialize( template ) self.hp = template.hp self.passable = template.passable or false self.blocksVision = template.blocksVision - self.inventory = self.container and Inventory.new() or nil + self.inventory = self.container and Inventory() or nil end --- diff --git a/src/ui/screens/InventoryScreen.lua b/src/ui/screens/InventoryScreen.lua index 6e54f441..d8404504 100644 --- a/src/ui/screens/InventoryScreen.lua +++ b/src/ui/screens/InventoryScreen.lua @@ -167,7 +167,7 @@ local function createTargetInventoryList( x, y, character, target, lists, listL local id, inventory -- TODO How to handle base inventory? - if target:instanceOf( 'Inventory' ) then + if target:isInstanceOf( 'Inventory' ) then id, inventory = 'inventory_base', target elseif target:hasWorldObject() and target:getWorldObject():isContainer() then id, inventory = 'inventory_container_inventory', target:getWorldObject():getInventory() From a5c77dcaa015ec5e6204c94ac70aac86c4a314ef Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Tue, 19 Dec 2017 13:45:47 +0100 Subject: [PATCH 129/178] Refactor Factions --- src/CombatState.lua | 2 +- src/characters/Factions.lua | 225 ++++++++++++++++++------------------ 2 files changed, 115 insertions(+), 112 deletions(-) diff --git a/src/CombatState.lua b/src/CombatState.lua index 77fe675c..6cb9288b 100644 --- a/src/CombatState.lua +++ b/src/CombatState.lua @@ -72,7 +72,7 @@ function CombatState:initialize( playerFaction, savegame ) self.map = createMap() end - self.factions = Factions.new( self.map ) + self.factions = Factions() self.factions:addFaction( Faction( FACTIONS.ENEMY, true )) self.factions:addFaction( Faction( FACTIONS.NEUTRAL, true )) self.factions:addFaction( playerFaction ) diff --git a/src/characters/Factions.lua b/src/characters/Factions.lua index 4e07346b..db829a50 100644 --- a/src/characters/Factions.lua +++ b/src/characters/Factions.lua @@ -1,145 +1,148 @@ -local Object = require( 'src.Object' ); -local Node = require( 'src.util.Node' ); -local Messenger = require( 'src.Messenger' ); -local Log = require( 'src.util.Log' ); +--- +-- @module Factions +-- -- ------------------------------------------------ --- Module +-- Required Modules -- ------------------------------------------------ -local Factions = {}; +local Class = require( 'lib.Middleclass' ) +local Node = require( 'src.util.Node' ) +local Messenger = require( 'src.Messenger' ) +local Log = require( 'src.util.Log' ) -- ------------------------------------------------ --- Constants +-- Module -- ------------------------------------------------ -local FACTIONS = require( 'src.constants.FACTIONS' ); +local Factions = Class( 'Factions' ) -- ------------------------------------------------ --- Constructor +-- Constants -- ------------------------------------------------ -function Factions.new() - local self = Object.new():addInstance( 'Factions' ); +local FACTIONS = require( 'src.constants.FACTIONS' ) - -- ------------------------------------------------ - -- Private Variables - -- ------------------------------------------------ +-- ------------------------------------------------ +-- Public Functions +-- ------------------------------------------------ - local root; - local active; +--- +-- Adds a new faction node to the linked list. +-- @tparam Faction faction The faction to add. +-- +function Factions:addFaction( faction ) + local node = Node( faction ) + + -- Initialise root node. + if not self.root then + self.root = node + self.active = self.root + return self.active:getObject() + end - -- ------------------------------------------------ - -- Public Functions - -- ------------------------------------------------ + -- Doubly link the new node. + self.active:linkNext( node ) + node:linkPrev( self.active ) - --- - -- Adds a new faction node to the linked list. - -- @tparam Faction faction The faction to add. - -- - function self:addFaction( faction ) - local node = Node( faction ) + -- Make it the active node. + self.active = node + return self.active:getObject() +end - -- Initialise root node. - if not root then - root = node - active = root - return active:getObject() +--- +-- Find the faction object belonging to the specified identifier. +-- @tparam string type The identifier to look for. +-- @treturn Faction The faction. +-- +function Factions:findFaction( type ) + local node = self.root + while node do + if node:getObject():getType() == type then + return node:getObject() end - - -- Doubly link the new node. - active:linkNext( node ) - node:linkPrev( active ) - - -- Make it the active node. - active = node - return active:getObject() + node = node:getNext() end +end - --- - -- Find the faction object belonging to the specified identifier. - -- @param type (string) The identifier to look for. - -- @return (Faction) The faction. - -- - function self:findFaction( type ) - local node = root; - while node do - if node:getObject():getType() == type then - return node:getObject(); +--- +-- Selects the next faction and returns the first valid character. +-- @treturn Character The selected Character. +-- +function Factions:nextFaction() + self.active:getObject():deactivate() + + while self.active do + self.active = self.active:getNext() or self.root + local faction = self.active:getObject() + faction:activate() + + if faction:hasLivingCharacters() then + local current = faction:getCurrentCharacter() + if current:isDead() then + return self:getFaction():nextCharacter() end - node = node:getNext(); + Messenger.publish( 'SWITCH_CHARACTERS', current ) + return current end - end - - --- - -- Selects the next faction and returns the first valid character. - -- @return (Character) The selected Character. - -- - function self:nextFaction() - active:getObject():deactivate(); - - while active do - active = active:getNext() or root; - local faction = active:getObject(); - faction:activate(); - - if faction:hasLivingCharacters() then - local current = faction:getCurrentCharacter(); - if current:isDead() then - return self:getFaction():nextCharacter(); - end - Messenger.publish( 'SWITCH_CHARACTERS', current ); - return current; - end - Log.debug( string.format( 'All %s characters are dead.', faction:getType() ), 'Factions' ); - end + Log.debug( string.format( 'All %s characters are dead.', faction:getType() ), 'Factions' ) end +end - --- - -- Iterates over all factions and passes them to the callback function. - -- @tparam function callback The callback to use on the factions. - -- - function self:iterate( callback ) - local node = root; - while node do - callback( node:getObject() ) - node = node:getNext() - end +--- +-- Iterates over all factions and passes them to the callback function. +-- @tparam function callback The callback to use on the factions. +-- +function Factions:iterate( callback ) + local node = self.root + while node do + callback( node:getObject() ) + node = node:getNext() end +end - function self:serialize() - local t = {} - self:iterate( function( faction ) - t[faction:getType()] = faction:serialize() - end) - return t; - end +--- +-- Serializes the Factions object. +-- @treturn table The serialized Factions object. +-- +function Factions:serialize() + local t = {} + self:iterate( function( faction ) + t[faction:getType()] = faction:serialize() + end) + return t +end - function self:receive( event, ... ) - if event == 'TILE_UPDATED' then - local tile = ...; - active:getObject():regenerateFOVSelectively( tile ); - end +--- +-- Receives events. +-- @tparam string event The received event. +-- @tparam varargs ... Variable arguments. +-- +function Factions:receive( event, ... ) + if event == 'TILE_UPDATED' then + self.active:getObject():regenerateFOVSelectively( ... ) end +end - -- ------------------------------------------------ - -- Getters - -- ------------------------------------------------ - - --- - -- Returns the currently active faction. - -- @return (Faction) The selected Faction. - -- - function self:getFaction() - return active:getObject(); - end +-- ------------------------------------------------ +-- Getters +-- ------------------------------------------------ - function self:getPlayerFaction() - return self:findFaction( FACTIONS.ALLIED ) - end +--- +-- Returns the currently active faction. +-- @treturn Faction The selected Faction. +-- +function Factions:getFaction() + return self.active:getObject() +end - return self; +--- +-- Returns the player's faction. +-- @treturn Faction The player's Faction. +-- +function Factions:getPlayerFaction() + return self:findFaction( FACTIONS.ALLIED ) end -return Factions; +return Factions From 82ca78b4c688fe3dd02f13097d5a45b0c74513da Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Tue, 19 Dec 2017 13:50:59 +0100 Subject: [PATCH 130/178] Refactor Tileset --- src/ui/texturepacks/TexturePack.lua | 3 +- src/ui/texturepacks/Tileset.lua | 127 ++++++++++++---------------- 2 files changed, 57 insertions(+), 73 deletions(-) diff --git a/src/ui/texturepacks/TexturePack.lua b/src/ui/texturepacks/TexturePack.lua index 9791a3be..23b8152f 100644 --- a/src/ui/texturepacks/TexturePack.lua +++ b/src/ui/texturepacks/TexturePack.lua @@ -47,8 +47,7 @@ function TexturePack.new() -- Generate tileset. local t = source.tileset - tileset = Tileset.new( path .. t.source, spriteInfos, t.tiles.width, t.tiles.height ) - tileset:init() + tileset = Tileset( path .. t.source, spriteInfos, t.tiles.width, t.tiles.height ) colors = colorInfos end diff --git a/src/ui/texturepacks/Tileset.lua b/src/ui/texturepacks/Tileset.lua index b1bc478b..83c2ebeb 100644 --- a/src/ui/texturepacks/Tileset.lua +++ b/src/ui/texturepacks/Tileset.lua @@ -8,101 +8,86 @@ -- Required Modules -- ------------------------------------------------ -local Object = require( 'src.Object' ) +local Class = require( 'lib.Middleclass' ) -- ------------------------------------------------ -- Module -- ------------------------------------------------ -local Tileset = {} +local Tileset = Class( 'Tileset' ) -- ------------------------------------------------ --- Constructor +-- Public Methods -- ------------------------------------------------ --- -- Creates a new instance of the Tileset class. --- @tparam string source The path to load the spritesheet file from. --- @tparam table infos A table mapping tile and object IDs to sprite information. --- @tparam number twidth The width of one tile. --- @tparam number theight The height of one tile. --- @treturn Font The new Tileset instance. +-- @tparam string source The path to load the spritesheet file from. +-- @tparam table infos A table mapping tile and object IDs to sprite information. +-- @tparam number tw The width of one tile. +-- @tparam number th The height of one tile. +-- @treturn Font The new Tileset instance. -- -function Tileset.new( source, infos, twidth, theight ) - local self = Object.new():addInstance( 'Tileset' ) - - -- ------------------------------------------------ - -- Private Attributes - -- ------------------------------------------------ - - local spritesheet - local sprites - - -- ------------------------------------------------ - -- Public Methods - -- ------------------------------------------------ - - --- - -- Initializes the tileset. - -- - function self:init() - spritesheet = love.graphics.newImage( source ) - sprites = {} - - for x = 1, spritesheet:getWidth() / twidth do - for y = 1, spritesheet:getHeight() / theight do - local qx, qy = (y-1) * twidth, (x-1) * theight - sprites[#sprites + 1] = love.graphics.newQuad( qx, qy, twidth, theight, spritesheet:getDimensions() ) - end +function Tileset:initialize( source, infos, tw, th ) + self.source = source + self.infos = infos + self.tileWidth = tw + self.tileHeight = th + + self.spritesheet = love.graphics.newImage( source ) + self.sprites = {} + + for x = 1, self.spritesheet:getWidth() / tw do + for y = 1, self.spritesheet:getHeight() / th do + local qx, qy = (y-1) * tw, (x-1) * th + self.sprites[#self.sprites + 1] = love.graphics.newQuad( qx, qy, tw, th, self.spritesheet:getDimensions() ) end end +end - -- ------------------------------------------------ - -- Getters - -- ------------------------------------------------ - - --- - -- Returns a quad which maps to a sprite on the tileset based on the id. - -- By default an id points to a number which in turn points to a quad in - -- the tileset. - -- If the subid parameter is not nil the id should point to a table which - -- contains a list of subids which point to quad numbers. This can be used - -- to have different sprites based on the state of an object or to draw - -- connected tiles. - -- @tparam string id The id to search for in the infos table. - -- @tparam string subid The subid to search for in the infos table (optional) - -- - function self:getSprite( id, subid ) - local definition = infos[id] - if not definition then - error( string.format( 'Can not find a sprite definition for [%s].', id )) - end +-- ------------------------------------------------ +-- Getters +-- ------------------------------------------------ - if subid then - if type( definition ) ~= 'table' then - error( string.format( 'Tried to get a sub-sprite [%s] from a non-table defintion [%s].', subid, id )) - end +--- +-- Returns a quad which maps to a sprite on the tileset based on the id. +-- By default an id points to a number which in turn points to a quad in +-- the tileset. +-- If the subid parameter is not nil the id should point to a table which +-- contains a list of subids which point to quad numbers. This can be used +-- to have different sprites based on the state of an object or to draw +-- connected tiles. +-- @tparam string id The id to search for in the infos table. +-- @tparam string subid The subid to search for in the infos table (optional) +-- +function Tileset:getSprite( id, subid ) + local definition = self.infos[id] + if not definition then + error( string.format( 'Can not find a sprite definition for [%s].', id )) + end - local index = definition[subid] - if not index then - error( string.format( 'Can not find a sub-sprite definition for [%s][%s].', id, subid )) - end + if subid then + if type( definition ) ~= 'table' then + error( string.format( 'Tried to get a sub-sprite [%s] from a non-table defintion [%s].', subid, id )) + end - return sprites[index] + local index = definition[subid] + if not index then + error( string.format( 'Can not find a sub-sprite definition for [%s][%s].', id, subid )) end - return sprites[definition] + return self.sprites[index] end - function self:getSpritesheet() - return spritesheet - end + return self.sprites[definition] +end - function self:getTileDimensions() - return twidth, theight - end +function Tileset:getSpritesheet() + return self.spritesheet +end - return self +function Tileset:getTileDimensions() + return self.tileWidth, self.tileHeight end return Tileset From cd9f2dfc74ab8627e1b9761849c05fce8bc865ef Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Tue, 19 Dec 2017 13:54:26 +0100 Subject: [PATCH 131/178] Refactor Font --- src/ui/texturepacks/Font.lua | 122 +++++++++++++--------------- src/ui/texturepacks/TexturePack.lua | 2 +- 2 files changed, 57 insertions(+), 67 deletions(-) diff --git a/src/ui/texturepacks/Font.lua b/src/ui/texturepacks/Font.lua index d8550f26..366ddd2a 100644 --- a/src/ui/texturepacks/Font.lua +++ b/src/ui/texturepacks/Font.lua @@ -8,92 +8,82 @@ -- Required Modules -- ------------------------------------------------ -local Object = require( 'src.Object' ) +local Class = require( 'lib.Middleclass' ) -- ------------------------------------------------ -- Module -- ------------------------------------------------ -local Font = {} +local Font = Class( 'Font' ) -- ------------------------------------------------ --- Constructor +-- Public Methods -- ------------------------------------------------ --- -- Creates a new instance of the Font class. --- @tparam string source The path to load the imagefont file from. --- @tparam string glyphs The glyph definition. --- @tparam number gwidth The width of one glyph. --- @tparam number gheight The height of one glpyh. --- @treturn Font The new Font instance. +-- @tparam string source The path to load the imagefont file from. +-- @tparam string glyphs The glyph definition. +-- @tparam number gw The width of one glyph. +-- @tparam number gh The height of one glpyh. +-- @treturn Font The new Font instance. -- -function Font.new( source, glyphs, gwidth, gheight ) - local self = Object.new():addInstance( 'Font' ) - - -- ------------------------------------------------ - -- Private Variables - -- ------------------------------------------------ - - local font = love.graphics.newImageFont( source, glyphs ) - - -- ------------------------------------------------ - -- Public Methods - -- ------------------------------------------------ +function Font:initialize( source, glyphs, gw, gh ) + self.font = love.graphics.newImageFont( source, glyphs ) + self.glyphWidth = gw + self.glyphHeight = gh +end - --- - -- Sets the current font so that it is used when drawing text. - -- - function self:use() - love.graphics.setFont( font ) - end +--- +-- Sets the current font so that it is used when drawing text. +-- +function Font:use() + love.graphics.setFont( self.font ) +end - --- - -- Measures the width of a string in pixels. - -- @tparam string str The string to measure. - -- @treturn number The width of the string in pixels. - -- - function self:measureWidth( str ) - return font:getWidth( str ) - end +--- +-- Measures the width of a string in pixels. +-- @tparam string str The string to measure. +-- @treturn number The width of the string in pixels. +-- +function Font:measureWidth( str ) + return self.font:getWidth( str ) +end - --- - -- Aligns a string while taking the font's grid into account. - -- @tparam string alignMode Determines how the font is aligned (center, left or right). - -- @tparam string str The text to align. - -- @tparam number width The width of the area in which to align the string. - -- @treturn number The position at which to draw the string. - -- - function self:align( alignMode, str, width ) - if alignMode == 'center' then - local offset = width * 0.5 - font:getWidth( str ) * 0.5 - return math.floor( offset / gwidth ) * gwidth - elseif alignMode == 'left' then - return 0 - elseif alignMode == 'right' then - local offset = width - font:getWidth( str ) - return math.floor( offset / gwidth ) * gwidth - end - error( string.format( 'Invalid align mode "%s". Use "center", "left" or "right" instead.' ), alignMode ) +--- +-- Aligns a string while taking the font's grid into account. +-- @tparam string alignMode Determines how the font is aligned (center, left or right). +-- @tparam string str The text to align. +-- @tparam number width The width of the area in which to align the string. +-- @treturn number The position at which to draw the string. +-- +function Font:align( alignMode, str, width ) + if alignMode == 'center' then + local offset = width * 0.5 - self.font:getWidth( str ) * 0.5 + return math.floor( offset / self.glyphWidth ) * self.glyphWidth + elseif alignMode == 'left' then + return 0 + elseif alignMode == 'right' then + local offset = width - self.font:getWidth( str ) + return math.floor( offset / self.glyphWidth ) * self.glyphWidth end + error( string.format( 'Invalid align mode "%s". Use "center", "left" or "right" instead.' ), alignMode ) +end - -- ------------------------------------------------ - -- Getters - -- ------------------------------------------------ - - function self:get() - return font - end +-- ------------------------------------------------ +-- Getters +-- ------------------------------------------------ - function self:getGlyphWidth() - return gwidth - end +function Font:get() + return self.font +end - function self:getGlyphHeight() - return gheight - end +function Font:getGlyphWidth() + return self.glyphWidth +end - return self +function Font:getGlyphHeight() + return self.glyphHeight end return Font diff --git a/src/ui/texturepacks/TexturePack.lua b/src/ui/texturepacks/TexturePack.lua index 23b8152f..fa0ef9ad 100644 --- a/src/ui/texturepacks/TexturePack.lua +++ b/src/ui/texturepacks/TexturePack.lua @@ -42,7 +42,7 @@ function TexturePack.new() -- Generate font. local f = source.font - font = Font.new( path .. f.source, f.glyphs.source, f.glyphs.width, f.glyphs.height ) + font = Font( path .. f.source, f.glyphs.source, f.glyphs.width, f.glyphs.height ) glyphWidth, glyphHeight = f.glyphs.width, f.glyphs.height -- Generate tileset. From 5d64be439d600842fd805dee589391dba9b962da Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Tue, 19 Dec 2017 13:58:35 +0100 Subject: [PATCH 132/178] Refactor PathOverlay --- src/ui/overlays/OverlayPainter.lua | 2 +- src/ui/overlays/PathOverlay.lua | 107 +++++++++++++++-------------- 2 files changed, 55 insertions(+), 54 deletions(-) diff --git a/src/ui/overlays/OverlayPainter.lua b/src/ui/overlays/OverlayPainter.lua index 9060ed6e..55463dc2 100644 --- a/src/ui/overlays/OverlayPainter.lua +++ b/src/ui/overlays/OverlayPainter.lua @@ -46,7 +46,7 @@ function OverlayPainter.new( game, camera ) local particleLayer = ParticleLayer() local pulser = Pulser.new( 4, 80, 80 ); local coneOverlay = ConeOverlay.new( game, pulser, camera ) - local pathOverlay = PathOverlay.new( game, pulser ) + local pathOverlay = PathOverlay( game, pulser ) local tileset = TexturePacks.getTileset() local tw, th = TexturePacks.getTileDimensions() diff --git a/src/ui/overlays/PathOverlay.lua b/src/ui/overlays/PathOverlay.lua index 966ab801..be32bed8 100644 --- a/src/ui/overlays/PathOverlay.lua +++ b/src/ui/overlays/PathOverlay.lua @@ -1,13 +1,14 @@ --- -- The PathOverlay module draws the movement path of the currently selected -- character. +-- @module PathOverlay -- -- ------------------------------------------------ -- Required Modules -- ------------------------------------------------ -local Object = require( 'src.Object' ) +local Class = require( 'lib.Middleclass' ) local TexturePacks = require( 'src.ui.texturepacks.TexturePacks' ) local MovementInput = require( 'src.turnbased.helpers.MovementInput' ) @@ -15,67 +16,67 @@ local MovementInput = require( 'src.turnbased.helpers.MovementInput' ) -- Module -- ------------------------------------------------ -local PathOverlay = {} +local PathOverlay = Class( 'PathOverlay' ) -- ------------------------------------------------ --- Constructor +-- Private Methods -- ------------------------------------------------ -function PathOverlay.new( game, pulser ) - local self = Object.new():addInstance( 'PathOverlay' ) - - -- ------------------------------------------------ - -- Private Methods - -- ------------------------------------------------ - - --- - -- Selects a color for the node in a path based on the distance to the - -- target and the remaining action points the character has. - -- @param value (number) The cost of the node. - -- @param total (number) The total number of nodes in the path. - -- - local function selectPathNodeColor( value, total ) - local fraction = value / total - if fraction < 0 then - return TexturePacks.getColor( 'ui_path_ap_low' ) - elseif fraction <= 0.2 then - return TexturePacks.getColor( 'ui_path_ap_med' ) - elseif fraction <= 0.6 then - return TexturePacks.getColor( 'ui_path_ap_high' ) - elseif fraction <= 1.0 then - return TexturePacks.getColor( 'ui_path_ap_full' ) - end +--- +-- Selects a color for the node in a path based on the distance to the +-- target and the remaining action points the character has. +-- @param value (number) The cost of the node. +-- @param total (number) The total number of nodes in the path. +-- +local function selectPathNodeColor( value, total ) + local fraction = value / total + if fraction < 0 then + return TexturePacks.getColor( 'ui_path_ap_low' ) + elseif fraction <= 0.2 then + return TexturePacks.getColor( 'ui_path_ap_med' ) + elseif fraction <= 0.6 then + return TexturePacks.getColor( 'ui_path_ap_high' ) + elseif fraction <= 1.0 then + return TexturePacks.getColor( 'ui_path_ap_full' ) end +end + +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - -- ------------------------------------------------ - -- Public Methods - -- ------------------------------------------------ +--- +-- Initializes the PathOverlay object. +-- @tparam Game game The game object. +-- @tparam Pulser pulser The pulser used for animating the overlay. +-- +function PathOverlay:initialize( game, pulser ) + self.game = game + self.pulser = pulser +end - --- - -- Draws a path for this character. - -- - function self:draw() - local tw, th = TexturePacks.getTileDimensions() - local character = game:getCurrentCharacter() - local mode = game:getState():getInputMode() - if mode:isInstanceOf( MovementInput ) and mode:hasPath() then - local total = character:getActionPoints() - local ap = total - mode:getPath():iterate( function( tile ) - ap = ap - tile:getMovementCost( character:getStance() ) +--- +-- Draws a path for this character. +-- +function PathOverlay:draw() + local tw, th = TexturePacks.getTileDimensions() + local character = self.game:getCurrentCharacter() + local mode = self.game:getState():getInputMode() + if mode:isInstanceOf( MovementInput ) and mode:hasPath() then + local total = character:getActionPoints() + local ap = total + mode:getPath():iterate( function( tile ) + ap = ap - tile:getMovementCost( character:getStance() ) - -- Draws the path overlay. - love.graphics.setBlendMode( 'add' ) - local color = selectPathNodeColor( ap, total ) - love.graphics.setColor( color[1], color[2], color[3], pulser:getPulse() ) - love.graphics.rectangle( 'fill', tile:getX() * tw, tile:getY() * th, tw, th ) - TexturePacks.resetColor() - love.graphics.setBlendMode( 'alpha' ) - end) - end + -- Draws the path overlay. + love.graphics.setBlendMode( 'add' ) + local color = selectPathNodeColor( ap, total ) + love.graphics.setColor( color[1], color[2], color[3], self.pulser:getPulse() ) + love.graphics.rectangle( 'fill', tile:getX() * tw, tile:getY() * th, tw, th ) + TexturePacks.resetColor() + love.graphics.setBlendMode( 'alpha' ) + end) end - - return self end return PathOverlay From d6fcfa2b28de7da1136d9d18e76658456180145a Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Tue, 19 Dec 2017 14:12:21 +0100 Subject: [PATCH 133/178] Refactor ItemStack --- src/inventory/Inventory.lua | 26 +++--- src/inventory/ItemStack.lua | 163 ++++++++++++++++++++---------------- 2 files changed, 102 insertions(+), 87 deletions(-) diff --git a/src/inventory/Inventory.lua b/src/inventory/Inventory.lua index 68374726..3d4e0207 100644 --- a/src/inventory/Inventory.lua +++ b/src/inventory/Inventory.lua @@ -91,7 +91,7 @@ end local function addStackableItem( items, item, index ) -- Check if we already have an item stack to add this item to. for _, stack in ipairs( items ) do - if stack:instanceOf( 'ItemStack' ) and stack:getID() == item:getID() then + if stack:isInstanceOf( 'ItemStack' ) and stack:getID() == item:getID() then stack:addItem( item ) return true end @@ -113,7 +113,7 @@ end local function removeItem( items, item ) for i = 1, #items do -- Check if item is part of a stack. - if items[i]:instanceOf( 'ItemStack' ) then + if items[i]:isInstanceOf( 'ItemStack' ) then local success = items[i]:removeItem( item ) if success then -- Remove the stack if it is empty. @@ -138,7 +138,7 @@ end -- local function removeItemStack( items, stack ) for i = 1, #items do - if items[i]:instanceOf( 'ItemStack' ) and items[i] == stack then + if items[i]:isInstanceOf( 'ItemStack' ) and items[i] == stack then table.remove( items, i ) return true end @@ -148,8 +148,8 @@ end -- TODO: proper documentation local function merge( self, stack, ostack ) - assert( stack:instanceOf( 'ItemStack' ), 'Expected parameter of type ItemStack.' ) - assert( ostack:instanceOf( 'ItemStack' ), 'Expected parameter of type ItemStack.' ) + assert( stack:isInstanceOf( 'ItemStack' ), 'Expected parameter of type ItemStack.' ) + assert( ostack:isInstanceOf( 'ItemStack' ), 'Expected parameter of type ItemStack.' ) for i = #ostack:getItems(), 1, -1 do local item = ostack:getItems()[i] @@ -231,11 +231,11 @@ function Inventory:addItem( item, index ) return false end - if item:instanceOf( 'ItemStack' ) then + if item:isInstanceOf( 'ItemStack' ) then return addItemStack( self.items, item, index ) end - if item:instanceOf( 'Item' ) then + if item:isInstanceOf( 'Item' ) then if item:isStackable() then return addStackableItem( self.items, item, index ) else @@ -253,8 +253,8 @@ end function Inventory:insertItem( item, oitem ) for i = 1, #self.items do if self.items[i] == oitem then - if oitem:instanceOf( 'ItemStack' ) and oitem:getID() == item:getID() then - if item:instanceOf( 'ItemStack' ) then + if oitem:isInstanceOf( 'ItemStack' ) and oitem:getID() == item:getID() then + if item:isInstanceOf( 'ItemStack' ) then return merge( self, oitem, item ) end end @@ -269,11 +269,11 @@ end -- @treturn boolean True if the item was removed successfully. -- function Inventory:removeItem( item ) - if item:instanceOf( 'ItemStack' ) then + if item:isInstanceOf( 'ItemStack' ) then return removeItemStack( self.items, item ) end - if item:instanceOf( 'Item') then + if item:isInstanceOf( 'Item') then return removeItem( self.items, item ) end end @@ -327,7 +327,7 @@ end function Inventory:getAndRemoveItem( type ) for _, item in ipairs( self.items ) do if item:getItemType() == type then - if item:instanceOf( 'ItemStack' ) then + if item:isInstanceOf( 'ItemStack' ) then local i = item:getItem() self:removeItem( i ) return i @@ -353,7 +353,7 @@ function Inventory:countItems( type, id ) -- Match items based on type and id. if item:getItemType() == type and item:getID() == id then -- Count items in stacks. - if item:instanceOf( 'ItemStack' ) then + if item:isInstanceOf( 'ItemStack' ) then count = count + item:getItemCount() else count = count + 1 diff --git a/src/inventory/ItemStack.lua b/src/inventory/ItemStack.lua index fff96161..87c22ecd 100644 --- a/src/inventory/ItemStack.lua +++ b/src/inventory/ItemStack.lua @@ -1,103 +1,118 @@ -local Object = require( 'src.Object' ); +--- +-- @module ItemStack +-- -local ItemStack = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ -function ItemStack.new( id ) - local self = Object.new():addInstance( 'ItemStack' ); +local Class = require( 'lib.Middleclass' ) - if not id or type( id ) ~= 'string' then error( 'Expected a parameter of type "string".' ) end; +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ - local items = {}; +local ItemStack = Class( 'ItemStack' ) - function self:addItem( item ) - assert( item:getID() == id, 'ID doesn\'t fit the stack' ); - items[#items + 1] = item; - return true; - end +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - function self:removeItem( item ) - for i = 1, #items do - if items[i] == item then - table.remove( items, i ); - return true; - end - end - return false; +function ItemStack:initialize( id ) + if not id or type( id ) ~= 'string' then + error( 'Expected a parameter of type "string".' ) end - function self:getWeight() - local weight = 0; - for i = 1, #items do - weight = weight + items[i]:getWeight(); + self.id = id + self.items = {} +end + +function ItemStack:addItem( item ) + assert( item:getID() == self.id, 'ID doesn\'t fit the stack' ) + self.items[#self.items + 1] = item + return true +end + +function ItemStack:removeItem( item ) + for i = 1, #self.items do + if self.items[i] == item then + table.remove( self.items, i ) + return true end - return weight; end + return false +end - function self:getVolume() - local volume = 0; - for i = 1, #items do - volume = volume + items[i]:getVolume(); - end - return volume; +function ItemStack:getWeight() + local weight = 0 + for i = 1, #self.items do + weight = weight + self.items[i]:getWeight() end + return weight +end - function self:split() - if #items > 1 then - local count = math.floor( #items * 0.5 ); - local newStack = ItemStack.new( id ); - for i = 1, count do - newStack:addItem( items[i] ); - self:removeItem( items[i] ); - end - return newStack; - end +function ItemStack:getVolume() + local volume = 0 + for i = 1, #self.items do + volume = volume + self.items[i]:getVolume() end + return volume +end - function self:serialize() - local t = { - ['ItemStack'] = true, - ['id'] = id, - ['items'] = {} - }; - for i = 1, #items do - t['items'][i] = items[i]:serialize(); +function ItemStack:split() + if #self.items > 1 then + local count = math.floor( #self.items * 0.5 ) + local newStack = ItemStack( self.id ) + for i = 1, count do + newStack:addItem( self.items[i] ) + self:removeItem( self.items[i] ) end - return t; + return newStack end +end - function self:isSameType( itemType, subType ) - return items[#items]:isSameType( itemType, subType ) +function ItemStack:serialize() + local t = { + ['ItemStack'] = true, + ['id'] = self.id, + ['items'] = {} + } + for i = 1, #self.items do + t['items'][i] = self.items[i]:serialize() end + return t +end - function self:getItem() - return items[#items]; - end +function ItemStack:isSameType( itemType, subType ) + return self.items[#self.items]:isSameType( itemType, subType ) +end - function self:getItems() - return items; - end +function ItemStack:getItem() + return self.items[#self.items] +end - function self:getItemType() - return items[#items]:getItemType(); - end +function ItemStack:getItems() + return self.items +end - function self:getDescriptionID() - return items[#items]:getDescriptionID(); - end +function ItemStack:getItemType() + return self.items[#self.items]:getItemType() +end - function self:getID() - return id; - end +function ItemStack:getDescriptionID() + return self.items[#self.items]:getDescriptionID() +end - function self:getItemCount() - return #items; - end +function ItemStack:getID() + return self.id +end - function self:isEmpty() - return #items == 0; - end +function ItemStack:getItemCount() + return #self.items +end - return self; +function ItemStack:isEmpty() + return #self.items == 0 end -return ItemStack; +return ItemStack From 5bcc13b0553712b866b5eaa678fad7c0e7299d6f Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Tue, 19 Dec 2017 14:12:39 +0100 Subject: [PATCH 134/178] Refactor Item --- src/items/Item.lua | 116 +++++++++++++++++++++++++++------------------ 1 file changed, 69 insertions(+), 47 deletions(-) diff --git a/src/items/Item.lua b/src/items/Item.lua index f3eca76d..5b3aa31d 100644 --- a/src/items/Item.lua +++ b/src/items/Item.lua @@ -1,64 +1,86 @@ -local Object = require( 'src.Object' ); +--- +-- @module Item +-- -local Item = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ -function Item.new( template ) - local self = Object.new():addInstance( 'Item' ); +local Class = require( 'lib.Middleclass' ) - function self:getID() - return template.id; - end +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ - function self:getDescriptionID() - return template.idDesc or 'default_item_description'; - end +local Item = Class( 'Item' ) - function self:getItemType() - return template.itemType; - end +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - function self:getSubType() - return template.subType; - end +function Item:initialize( template ) + self.id = template.id + self.descriptionID = template.idDesc + self.itemType = template.itemType + self.subType = template.subType + self.weight = template.weight + self.volume = template.volume + self.equippable = template.equippable + self.stackable = template.stackable + self.permanent = template.permanent +end - function self:isSameType( itemType, subType ) - if template.itemType == itemType then - if not subType or subType == template.subType then - return true; - end - end - return false; - end +function Item:getID() + return self.id +end - function self:getWeight() - return template.weight; - end +function Item:getDescriptionID() + return self.descriptionID or 'default_item_description' +end - function self:getVolume() - return template.volume; - end +function Item:getItemType() + return self.itemType +end - function self:isEquippable() - return template.equippable; - end +function Item:getSubType() + return self.subType +end - function self:isStackable() - return template.stackable; +function Item:isSameType( itemType, subType ) + if self.itemType == itemType then + if not subType or subType == self.subType then + return true + end end + return false +end - function self:isPermanent() - return template.permanent; - end +function Item:getWeight() + return self.weight +end - function self:serialize() - local t = { - ['id'] = template.id, - ['itemType'] = template.itemType - }; - return t; - end +function Item:getVolume() + return self.volume +end + +function Item:isEquippable() + return self.equippable +end + +function Item:isStackable() + return self.stackable +end + +function Item:isPermanent() + return self.permanent +end - return self; +function Item:serialize() + local t = { + ['id'] = self.id, + ['itemType'] = self.itemType + } + return t end -return Item; +return Item From 5fdee9ea59943a1828704910e7eefcd1d3bf030e Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 21 Dec 2017 00:58:42 +0100 Subject: [PATCH 135/178] Refactor Armor class --- src/items/Armor.lua | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/src/items/Armor.lua b/src/items/Armor.lua index 0aa7be35..54f9cbc8 100644 --- a/src/items/Armor.lua +++ b/src/items/Armor.lua @@ -1,22 +1,36 @@ -local Item = require( 'src.items.Item' ); +--- +-- @module Armor +-- -local Armor = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ -function Armor.new( template ) - local self = Item.new( template ):addInstance( 'Armor' ); +local Item = require( 'src.items.Item' ) - local armorProtection = template.armor.protection; - local armorCoverage = template.armor.coverage; +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ - function self:getArmorProtection() - return armorProtection; - end +local Armor = Item:subclass( 'Armor' ) - function self:getArmorCoverage() - return armorCoverage; - end +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - return self; +function Armor:initialize( template ) + Item.initialize( self, template ) + + self.armorProtection = template.armor.protection + self.armorCoverage = template.armor.coverage +end + +function Armor:getArmorProtection() + return self.armorProtection +end + +function Armor:getArmorCoverage() + return self.armorCoverage end -return Armor; +return Armor From 82e982733ff6771f5774cb278abf66885a116c83 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 21 Dec 2017 00:58:51 +0100 Subject: [PATCH 136/178] Refactor Container class --- src/items/Container.lua | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/src/items/Container.lua b/src/items/Container.lua index c5b8217e..913c2ea9 100644 --- a/src/items/Container.lua +++ b/src/items/Container.lua @@ -1,27 +1,31 @@ -local Item = require( 'src.items.Item' ); +--- +-- @module Container +-- -local Container = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ + +local Item = require( 'src.items.Item' ) -- ------------------------------------------------ --- Constructor +-- Module -- ------------------------------------------------ -function Container.new( template ) - local self = Item.new( template ):addInstance( 'Container' ); +local Container = Item:subclass( 'Container' ) - function self:getCarryCapacity() - return template.carryCapacity; - end +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ + +function Container:initialize( template ) + Item.initialize( self, template ) - function self:serialize() - local t = { - ['id'] = template.id, - ['itemType'] = template.itemType - }; - return t; - end + self.carryCapacity = template.carryCapacity +end - return self; +function Container:getCarryCapacity() + return self.carryCapacity end -return Container; +return Container From 4b69fd648f62c611f11461c6edeef95529663073 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 21 Dec 2017 00:58:57 +0100 Subject: [PATCH 137/178] Refactor Ammunition class --- src/items/weapons/Ammunition.lua | 50 ++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/src/items/weapons/Ammunition.lua b/src/items/weapons/Ammunition.lua index 86896f2f..2581bd89 100644 --- a/src/items/weapons/Ammunition.lua +++ b/src/items/weapons/Ammunition.lua @@ -1,27 +1,41 @@ -local Item = require( 'src.items.Item' ); -local AmmunitionEffects = require( 'src.items.weapons.AmmunitionEffects' ); +--- +-- @module Ammunition +-- -local Ammunition = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ -function Ammunition.new( template ) - local self = Item.new( template ):addInstance( 'Ammunition' ); +local Item = require( 'src.items.Item' ) +local AmmunitionEffects = require( 'src.items.weapons.AmmunitionEffects' ) - local effects = AmmunitionEffects.new( template.effects ); +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ - function self:getDamageType() - return template.damageType; - end +local Ammunition = Item:subclass( 'Ammunition' ) - -- TODO: Add caliber to templates. - function self:getCaliber() - return template.id; - end +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - function self:getEffects() - return effects; - end +function Ammunition:initialize( template ) + Item.initialize( self, template ) - return self; + self.damageType = template.damageType + self.effects = AmmunitionEffects.new( template.effects ) end -return Ammunition; +function Ammunition:getDamageType() + return self.damageType +end + +function Ammunition:getCaliber() + return self.id +end + +function Ammunition:getEffects() + return self.effects +end + +return Ammunition From 1a2cedb592a5513d97755e619dbc4e31e50a2a98 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 21 Dec 2017 00:59:03 +0100 Subject: [PATCH 138/178] Refactor Magazine class --- src/items/weapons/Magazine.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/items/weapons/Magazine.lua b/src/items/weapons/Magazine.lua index 922217e4..2171b7f8 100644 --- a/src/items/weapons/Magazine.lua +++ b/src/items/weapons/Magazine.lua @@ -37,7 +37,7 @@ end -- @tparam Ammunition round The round to add. -- function Magazine:addRound( round ) - assert( round:instanceOf( 'Ammunition' ), 'Expected an item of type Ammunition!' ) + assert( round:isInstanceOf( 'Ammunition' ), 'Expected an item of type Ammunition!' ) self.rounds[#self.rounds + 1] = round end From e0a03080289434479b1835d364e98d24faef1e76 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 21 Dec 2017 00:59:09 +0100 Subject: [PATCH 139/178] Refactor MeleeWeapon class --- src/items/weapons/MeleeWeapon.lua | 32 ++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/src/items/weapons/MeleeWeapon.lua b/src/items/weapons/MeleeWeapon.lua index 0c547da5..e6634096 100644 --- a/src/items/weapons/MeleeWeapon.lua +++ b/src/items/weapons/MeleeWeapon.lua @@ -1,15 +1,29 @@ -local Weapon = require( 'src.items.weapons.Weapon' ); +--- +-- @module MeleeWeapon +-- -local MeleeWeapon = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ -function MeleeWeapon.new( template ) - local self = Weapon.new( template ):addInstance( 'MeleeWeapon' ); +local Weapon = require( 'src.items.weapons.Weapon' ) - function self:getDamageType() - return self:getAttackMode().damageType; - end +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ - return self; +local MeleeWeapon = Weapon:subclass( 'MeleeWeapon' ) + +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ + +function MeleeWeapon:initialize( template ) + Weapon.initialize( self, template ) +end + +function MeleeWeapon:getDamageType() + return self:getAttackMode().damageType end -return MeleeWeapon; +return MeleeWeapon From fedd0904adf01683c37c99db986b50ca77acff60 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 21 Dec 2017 00:59:15 +0100 Subject: [PATCH 140/178] Refactor RangedWeapon class --- src/items/weapons/RangedWeapon.lua | 90 ++++++++++++++++-------------- 1 file changed, 48 insertions(+), 42 deletions(-) diff --git a/src/items/weapons/RangedWeapon.lua b/src/items/weapons/RangedWeapon.lua index 625a5ff6..dc2dc0ba 100644 --- a/src/items/weapons/RangedWeapon.lua +++ b/src/items/weapons/RangedWeapon.lua @@ -1,59 +1,65 @@ -local Weapon = require( 'src.items.weapons.Weapon' ); -local Magazine = require( 'src.items.weapons.Magazine' ); +--- +-- @module RangedWeapon +-- -local RangedWeapon = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ -function RangedWeapon.new( template ) - local self = Weapon.new( template ):addInstance( 'RangedWeapon' ); +local Weapon = require( 'src.items.weapons.Weapon' ) +local Magazine = require( 'src.items.weapons.Magazine' ) - -- ------------------------------------------------ - -- Private Attributes - -- ------------------------------------------------ +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ - local rpm = template.rpm or 60; - local firingDelay = 1 / ( rpm / 60 ); - local magazine = Magazine( template.caliber, template.magSize ) - local range = template.range; +local RangedWeapon = Weapon:subclass( 'RangedWeapon' ) - -- ------------------------------------------------ - -- Public Methods - -- ------------------------------------------------ +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - function self:attack() - magazine:removeShell(); - end +function RangedWeapon:initialize( template ) + Weapon.initialize( self, template ) - function self:serialize() - local t = { - ['id'] = template.id, - ['itemType'] = template.itemType, - ['modeIndex'] = self:getAttackModeIndex() - }; + self.rpm = template.rpm or 60 + self.firingDelay = 1 / ( self.rpm / 60 ) + self.magazine = Magazine( template.caliber, template.magSize ) + self.range = template.range +end - if magazine then - t['magazine'] = magazine:serialize() - end +function RangedWeapon:attack() + self.magazine:removeShell() +end - return t; +function RangedWeapon:serialize() + local t = { + ['id'] = self.template.id, + ['itemType'] = self.template.itemType, + ['modeIndex'] = self:getAttackModeIndex() + } + + if self.magazine then + t['magazine'] = self.magazine:serialize() end - -- ------------------------------------------------ - -- Getters - -- ------------------------------------------------ + return t +end - function self:getFiringDelay() - return firingDelay; - end +-- ------------------------------------------------ +-- Getters +-- ------------------------------------------------ - function self:getMagazine() - return magazine; - end +function RangedWeapon:getFiringDelay() + return self.firingDelay +end - function self:getRange() - return range; - end +function RangedWeapon:getMagazine() + return self.magazine +end - return self; +function RangedWeapon:getRange() + return self.range end -return RangedWeapon; +return RangedWeapon From bb550315ec71bbbe241e0e69d6a56d4486e5af36 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 21 Dec 2017 00:59:22 +0100 Subject: [PATCH 141/178] Refactor ThrownWeapon class --- src/items/weapons/ThrownWeapon.lua | 49 +++++++++++++++++++----------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/src/items/weapons/ThrownWeapon.lua b/src/items/weapons/ThrownWeapon.lua index 823f0faf..adffeb7e 100644 --- a/src/items/weapons/ThrownWeapon.lua +++ b/src/items/weapons/ThrownWeapon.lua @@ -1,26 +1,41 @@ -local Weapon = require( 'src.items.weapons.Weapon' ); -local AmmunitionEffects = require( 'src.items.weapons.AmmunitionEffects' ); +--- +-- @module ThrownWeapon +-- -local ThrownWeapon = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ -function ThrownWeapon.new( template ) - local self = Weapon.new( template ):addInstance( 'ThrownWeapon' ); +local Weapon = require( 'src.items.weapons.Weapon' ) +local AmmunitionEffects = require( 'src.items.weapons.AmmunitionEffects' ) - local effects = AmmunitionEffects.new( template.effects ); +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ - function self:getDamageType() - return self:getAttackMode().damageType; - end +local ThrownWeapon = Weapon:subclass( 'ThrownWeapon' ) - function self:getEffects() - return effects; - end +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - function self:getRange() - return template.range; - end +function ThrownWeapon:initialize( template ) + Weapon.initialize( self, template ) - return self; + self.effects = AmmunitionEffects.new( template.effects ) + self.range = template.range end -return ThrownWeapon; +function ThrownWeapon:getDamageType() + return self:getAttackMode().damageType +end + +function ThrownWeapon:getEffects() + return self.effects +end + +function ThrownWeapon:getRange() + return self.range +end + +return ThrownWeapon From dffc07e2ecdf1ad558eccc99e68b34c601b6b278 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 21 Dec 2017 00:59:28 +0100 Subject: [PATCH 142/178] Refactor Weapon class --- src/items/weapons/Weapon.lua | 179 ++++++++++++++++++----------------- 1 file changed, 91 insertions(+), 88 deletions(-) diff --git a/src/items/weapons/Weapon.lua b/src/items/weapons/Weapon.lua index 1e37847b..e5e775bc 100644 --- a/src/items/weapons/Weapon.lua +++ b/src/items/weapons/Weapon.lua @@ -1,99 +1,102 @@ -local Item = require( 'src.items.Item' ); +--- +-- @module Weapon +-- + +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ + +local Item = require( 'src.items.Item' ) -- ------------------------------------------------ -- Module -- ------------------------------------------------ -local Weapon = {}; +local Weapon = Item:subclass( 'Weapon' ) + +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ + +function Weapon:initialize( template ) + Item.initialize( self, template ) + + self.damage = template.damage + self.reloadable = template.reloadable + + self.modes = template.mode + self.mode = self.modes[1] + self.modeIndex = 1 + + self.sound = template.sound +end + +function Weapon:selectNextFiringMode() + self.modeIndex = self.modeIndex + 1 > #self.modes and 1 or self.modeIndex + 1 + self.mode = self.modes[self.modeIndex] +end + +function Weapon:selectPrevFiringMode() + self.modeIndex = self.modeIndex - 1 < 1 and #self.modes or self.modeIndex - 1 + self.mode = self.modes[self.modeIndex] +end + +function Weapon:serialize() + local t = { + ['id'] = self.id, + ['itemType'] = self.itemType, + ['modeIndex'] = self.modeIndex + } + return t +end + +-- ------------------------------------------------ +-- Getters +-- ------------------------------------------------ + +function Weapon:getAccuracy() + return self.mode.accuracy +end + +function Weapon:getAttackCost() + return self.mode.cost +end + +function Weapon:getDamage() + return self.damage +end + +function Weapon:getAttackMode() + return self.mode +end + +function Weapon:getAttacks() + return self.mode.attacks +end + +function Weapon:getAttackModeIndex() + return self.modeIndex +end + +function Weapon:getSound() + return self.sound +end + +function Weapon:isReloadable() + return self.reloadable +end + +function Weapon:getModes() + return self.modes +end -- ------------------------------------------------ --- Constructor +-- Setters -- ------------------------------------------------ -function Weapon.new( template ) - local self = Item.new( template ):addInstance( 'Weapon' ); - - -- ------------------------------------------------ - -- Private Attributes - -- ------------------------------------------------ - - local damage = template.damage; - local modeIndex = 1; - local mode = template.mode[modeIndex]; - - -- ------------------------------------------------ - -- Public Methods - -- ------------------------------------------------ - - function self:selectNextFiringMode() - modeIndex = modeIndex + 1 > #template.mode and 1 or modeIndex + 1; - mode = template.mode[modeIndex]; - end - - function self:selectPrevFiringMode() - modeIndex = modeIndex - 1 < 1 and #template.mode or modeIndex - 1; - mode = template.mode[modeIndex]; - end - - function self:serialize() - local t = { - ['id'] = template.id, - ['itemType'] = template.itemType, - ['modeIndex'] = modeIndex - }; - return t; - end - - -- ------------------------------------------------ - -- Getters - -- ------------------------------------------------ - - function self:getAccuracy() - return mode.accuracy; - end - - function self:getAttackCost() - return mode.cost; - end - - function self:getDamage() - return damage; - end - - function self:getAttackMode() - return mode; - end - - function self:getAttacks() - return mode.attacks; - end - - function self:getAttackModeIndex() - return modeIndex; - end - - function self:getSound() - return template.sound; - end - - function self:isReloadable() - return template.reloadable; - end - - function self:getModes() - return template.mode; - end - - -- ------------------------------------------------ - -- Setters - -- ------------------------------------------------ - - function self:setAttackMode( nmodeIndex ) - modeIndex = nmodeIndex; - mode = template.mode[modeIndex]; - end - - return self; +function Weapon:setAttackMode( modeIndex ) + self.modeIndex = modeIndex + self.mode = self.modes[self.modeIndex] end -return Weapon; +return Weapon From 1c025a0eaf06b77bf08aa629610b8f6679c3f836 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 21 Dec 2017 01:00:39 +0100 Subject: [PATCH 143/178] Fix constructor calls in ItemFactory --- src/items/ItemFactory.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/items/ItemFactory.lua b/src/items/ItemFactory.lua index 4643a1ea..ea922c69 100644 --- a/src/items/ItemFactory.lua +++ b/src/items/ItemFactory.lua @@ -91,9 +91,9 @@ function ItemFactory.createItem( id ) local template = items[id] Log.debug( template.itemType .. ', ' .. tostring(template.subType), 'ItemFactory' ) if template.itemType == ITEM_TYPES.WEAPON then - return ITEM_CLASSES[template.subType].new( template ) + return ITEM_CLASSES[template.subType]( template ) end - return ITEM_CLASSES[template.itemType].new( template ) + return ITEM_CLASSES[template.itemType]( template ) end --- From bc5618bb7b058bc9a0a39b532303b7e01fbc89f4 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 21 Dec 2017 01:02:27 +0100 Subject: [PATCH 144/178] Fix isInstanceOf call in Magazine --- src/items/weapons/Magazine.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/items/weapons/Magazine.lua b/src/items/weapons/Magazine.lua index 2171b7f8..c508bd6c 100644 --- a/src/items/weapons/Magazine.lua +++ b/src/items/weapons/Magazine.lua @@ -9,6 +9,7 @@ -- ------------------------------------------------ local Class = require( 'lib.Middleclass' ) +local Ammunition = require( 'src.items.weapons.Ammunition' ) -- ------------------------------------------------ -- Module @@ -37,7 +38,7 @@ end -- @tparam Ammunition round The round to add. -- function Magazine:addRound( round ) - assert( round:isInstanceOf( 'Ammunition' ), 'Expected an item of type Ammunition!' ) + assert( round:isInstanceOf( Ammunition ), 'Expected an item of type Ammunition!' ) self.rounds[#self.rounds + 1] = round end From a049da2cc983f471c61e555d58fa8a223c3d5d6f Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 21 Dec 2017 01:09:22 +0100 Subject: [PATCH 145/178] Refactor Observable --- src/util/Observable.lua | 56 +++++++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 22 deletions(-) diff --git a/src/util/Observable.lua b/src/util/Observable.lua index 2ab0f68d..9c9bd8b4 100644 --- a/src/util/Observable.lua +++ b/src/util/Observable.lua @@ -1,31 +1,43 @@ -local Object = require( 'src.Object' ); +--- +-- @module Observable +-- -local Observable = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ -function Observable.new() - local self = Object.new():addInstance( 'Observable' ); +local Class = require( 'lib.Middleclass' ) - local observers = {}; - local index = 1; +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ - function self:observe( observer ) - assert( observer.receive, "Observer has to have a public receive method." ); - observers[index] = observer; - index = index; - return index; - end +local Observable = Class( 'Observable' ) - function self:remove( nindex ) - observers[nindex] = nil; - end +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - function self:publish( event, ... ) - for _, observer in pairs( observers ) do - observer:receive( event, ... ); - end - end +function Observable:initialize() + self.observers = {} + self.index = 1 +end - return self; +function self: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 +end + +function self:remove( index ) + self.observers[index] = nil +end + +function self:publish( event, ... ) + for _, observer in pairs( self.observers ) do + observer:receive( event, ... ) + end end -return Observable; +return Observable From 17c641b920f10dc6483ecbdbfbf71b43708465e1 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 21 Dec 2017 01:15:12 +0100 Subject: [PATCH 146/178] Refactor MapLoader --- src/map/MapLoader.lua | 125 +++++++++++++++++++++++------------------- 1 file changed, 68 insertions(+), 57 deletions(-) diff --git a/src/map/MapLoader.lua b/src/map/MapLoader.lua index bf741334..18145470 100644 --- a/src/map/MapLoader.lua +++ b/src/map/MapLoader.lua @@ -8,7 +8,7 @@ -- Required Modules -- ------------------------------------------------ -local Object = require( 'src.Object' ) +local Class = require( 'lib.Middleclass' ) local TileFactory = require( 'src.map.tiles.TileFactory' ) local WorldObjectFactory = require( 'src.map.worldobjects.WorldObjectFactory' ) @@ -16,75 +16,86 @@ local WorldObjectFactory = require( 'src.map.worldobjects.WorldObjectFactory' ) -- Module -- ------------------------------------------------ -local MapLoader = {} +local MapLoader = Class( 'MapLoader' ) -- ------------------------------------------------ --- Constructor +-- Private Methods -- ------------------------------------------------ -function MapLoader.new() - local self = Object.new():addInstance( '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 - local function recreateTile( x, y, id ) - return TileFactory.create( x, y, id ) +--- +-- 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. +-- +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 ) end + return worldObject +end - 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 ) +--- +-- 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. +-- +local function loadSavedTiles( savedTiles ) + local loadedTiles = {} + for _, tile in ipairs( savedTiles ) do + -- Recreate the tile. + local recreatedTile = recreateTile( tile.x, tile.y, 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 - return worldObject - end - - local function loadSavedTiles( savedTiles ) - local loadedTiles = {} - for _, tile in ipairs( savedTiles ) do - -- Recreate the tile. - local recreatedTile = recreateTile( tile.x, tile.y, 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 - - -- Recreate the tile's inventory if it had one. - if tile.inventory then - recreatedTile:getInventory():loadItems( tile.inventory ) - end - - -- Store tile in the table. - loadedTiles[tile.x] = loadedTiles[tile.x] or {} - loadedTiles[tile.x][tile.y] = recreatedTile + -- Recreate the tile's inventory if it had one. + if tile.inventory then + recreatedTile:getInventory():loadItems( tile.inventory ) end - return loadedTiles - end - -- ------------------------------------------------ - -- Public Methods - -- ------------------------------------------------ - - --- - -- 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. - -- - function self:recreateMap( savedmap ) - return loadSavedTiles( savedmap.tiles ), savedmap.width, savedmap.height + -- Store tile in the table. + loadedTiles[tile.x] = loadedTiles[tile.x] or {} + loadedTiles[tile.x][tile.y] = recreatedTile end + return loadedTiles +end - return self +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ + +--- +-- 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. +-- +function MapLoader:recreateMap( savedmap ) + return loadSavedTiles( savedmap.tiles ), savedmap.width, savedmap.height end return MapLoader From 3142671a950accdd133f2ce2a2f2fc779285d798 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 21 Dec 2017 01:18:10 +0100 Subject: [PATCH 147/178] Refactor AmmunitionEffects --- src/items/weapons/AmmunitionEffects.lua | 110 +++++++++++++----------- 1 file changed, 62 insertions(+), 48 deletions(-) diff --git a/src/items/weapons/AmmunitionEffects.lua b/src/items/weapons/AmmunitionEffects.lua index 5c3c1a5d..434227b5 100644 --- a/src/items/weapons/AmmunitionEffects.lua +++ b/src/items/weapons/AmmunitionEffects.lua @@ -1,67 +1,81 @@ -local Object = require( 'src.Object' ); +--- +-- @module AmmunitionEffects +-- -local AmmunitionEffects = {}; +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ -function AmmunitionEffects.new( template ) - local self = Object.new():addInstance( 'AmmunitionEffects' ); +local Class = require( 'lib.Middleclass' ) - -- ------------------------------------------------ - -- Explosive Ammunition - -- ------------------------------------------------ +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ - function self:isExplosive() - return template.explosive; - end +local AmmunitionEffects = Class( 'AmmunitionEffects' ) - function self:getBlastRadius() - return template.explosive.blastRadius; - end +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - -- ------------------------------------------------ - -- Ammunition that spreads on shot - -- ------------------------------------------------ +function AmmunitionEffects:initialize( template ) + self.template = template +end - function self:spreadsOnShot() - return template.spreadsOnShot; - end +-- ------------------------------------------------ +-- Explosive Ammunition +-- ------------------------------------------------ - function self:getPellets() - return template.spreadsOnShot.pellets; - end +function AmmunitionEffects:isExplosive() + return self.template.explosive +end - -- ------------------------------------------------ - -- Ammunition with custom speed - -- ------------------------------------------------ +function AmmunitionEffects:getBlastRadius() + return self.template.explosive.blastRadius +end - function self:hasCustomSpeed() - return template.customSpeed; - end +-- ------------------------------------------------ +-- Ammunition that spreads on shot +-- ------------------------------------------------ - function self:getCustomSpeed() - return template.customSpeed.speed; - end +function AmmunitionEffects:spreadsOnShot() + return self.template.spreadsOnShot +end - function self:getSpeedIncrease() - return template.customSpeed.increase or 0; - end +function AmmunitionEffects:getPellets() + return self.template.spreadsOnShot.pellets +end - function self:getFinalSpeed() - return template.customSpeed.final or template.customSpeed.speed; - end +-- ------------------------------------------------ +-- Ammunition with custom speed +-- ------------------------------------------------ - -- ------------------------------------------------ - -- Ammunition has custom sprite - -- ------------------------------------------------ +function AmmunitionEffects:hasCustomSpeed() + return self.template.customSpeed +end - function self:hasCustomSprite() - return template.customSprite; - end +function AmmunitionEffects:getCustomSpeed() + return self.template.customSpeed.speed +end - function self:getCustomSprite() - return template.customSprite.sprite; - end +function AmmunitionEffects:getSpeedIncrease() + return self.template.customSpeed.increase or 0 +end + +function AmmunitionEffects:getFinalSpeed() + return self.template.customSpeed.final or self.template.customSpeed.speed +end + +-- ------------------------------------------------ +-- Ammunition has custom sprite +-- ------------------------------------------------ + +function AmmunitionEffects:hasCustomSprite() + return self.template.customSprite +end - return self; +function AmmunitionEffects:getCustomSprite() + return self.template.customSprite.sprite end -return AmmunitionEffects; +return AmmunitionEffects From 24628443beae7ed4e8c17829967977b1b9603da0 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 21 Dec 2017 01:18:25 +0100 Subject: [PATCH 148/178] Remove Object.lua --- src/Object.lua | 22 ---------------------- 1 file changed, 22 deletions(-) delete mode 100644 src/Object.lua diff --git a/src/Object.lua b/src/Object.lua deleted file mode 100644 index 3a9b50b4..00000000 --- a/src/Object.lua +++ /dev/null @@ -1,22 +0,0 @@ -local Object = {}; - -function Object.new() - local self = { - __instances = { - Object = true - }; - }; - - function self:addInstance( class ) - self.__instances[class] = true; - return self; - end - - function self:instanceOf( class ) - return self.__instances[class]; - end - - return self; -end - -return Object; From f7aa00e609b437024f4dd23c31763f8fb128bf39 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 21 Dec 2017 01:20:27 +0100 Subject: [PATCH 149/178] Fix isInstanceOf calls --- src/characters/actions/Rearm.lua | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/characters/actions/Rearm.lua b/src/characters/actions/Rearm.lua index 44e9ac15..aae9f679 100644 --- a/src/characters/actions/Rearm.lua +++ b/src/characters/actions/Rearm.lua @@ -9,6 +9,8 @@ -- ------------------------------------------------ local Action = require( 'src.characters.actions.Action' ) +local Weapon = require( 'src.items.weapons.Weapon' ) +local ItemStack = require( 'src.inventory.ItemStack' ) -- ------------------------------------------------ -- Module @@ -29,11 +31,11 @@ local Rearm = Action:subclass( 'Rearm' ) -- local function findItem( inventory, weaponID ) for _, item in pairs( inventory:getItems() ) do - if item:instanceOf( 'Weapon' ) and item:getID() == weaponID then + if item:isInstanceOf( Weapon ) and item:getID() == weaponID then return item - elseif item:instanceOf( 'ItemStack' ) then + elseif item:isInstanceOf( ItemStack ) then for _, sitem in pairs( item:getItems() ) do - if sitem:instanceOf( 'Weapon' ) and sitem:getID() == weaponID then + if sitem:isInstanceOf( Weapon ) and sitem:getID() == weaponID then return sitem end end From 0720137cc5352824f072cf1bb8fda12a3a888744 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 21 Dec 2017 01:22:52 +0100 Subject: [PATCH 150/178] Refactor TexturePack --- src/ui/texturepacks/TexturePack.lua | 80 +++++++++++----------------- src/ui/texturepacks/TexturePacks.lua | 3 +- 2 files changed, 31 insertions(+), 52 deletions(-) diff --git a/src/ui/texturepacks/TexturePack.lua b/src/ui/texturepacks/TexturePack.lua index fa0ef9ad..512afa35 100644 --- a/src/ui/texturepacks/TexturePack.lua +++ b/src/ui/texturepacks/TexturePack.lua @@ -6,7 +6,7 @@ -- Required Modules -- ------------------------------------------------ -local Object = require( 'src.Object' ) +local Class = require( 'lib.Middleclass' ) local Font = require( 'src.ui.texturepacks.Font' ) local Tileset = require( 'src.ui.texturepacks.Tileset' ) @@ -14,69 +14,49 @@ local Tileset = require( 'src.ui.texturepacks.Tileset' ) -- Module -- ------------------------------------------------ -local TexturePack = {} +local TexturePack = Class( 'TexturePack' ) -- ------------------------------------------------ -- Constructor -- ------------------------------------------------ -function TexturePack.new() - local self = Object.new():addInstance( 'TexturePack' ) +function TexturePack:initialize( path, source, spriteInfos, colorInfos ) + self.name = source.name - -- ------------------------------------------------ - -- Private Variables - -- ------------------------------------------------ + -- Generate font. + local f = source.font + self.font = Font( path .. f.source, f.glyphs.source, f.glyphs.width, f.glyphs.height ) + self.glyphWidth, self.glyphHeight = f.glyphs.width, f.glyphs.height - local name - local font - local glyphWidth, glyphHeight - local tileset - local colors + -- Generate tileset. + local t = source.tileset + self.tileset = Tileset( path .. t.source, spriteInfos, t.tiles.width, t.tiles.height ) - -- ------------------------------------------------ - -- Public Methods - -- ------------------------------------------------ - - function self:init( path, source, spriteInfos, colorInfos ) - name = source.name - - -- Generate font. - local f = source.font - font = Font( path .. f.source, f.glyphs.source, f.glyphs.width, f.glyphs.height ) - glyphWidth, glyphHeight = f.glyphs.width, f.glyphs.height - - -- Generate tileset. - local t = source.tileset - tileset = Tileset( path .. t.source, spriteInfos, t.tiles.width, t.tiles.height ) - - colors = colorInfos - end - - -- ------------------------------------------------ - -- Getters - -- ------------------------------------------------ + self.colors = colorInfos +end - function self:getName() - return name - end +-- ------------------------------------------------ +-- Getters +-- ------------------------------------------------ - function self:getFont() - return font - end +function self:getName() + return self.name +end - function self:getGlyphDimensions() - return glyphWidth, glyphHeight - end +function self:getFont() + return self.font +end - function self:getTileset() - return tileset - end +function self:getGlyphDimensions() + return self.glyphWidth, self.glyphHeight +end - function self:getColor( id ) - return colors[id] - end +function self:getTileset() + return self.tileset +end - return self +function self:getColor( id ) + return self.colors[id] end return TexturePack diff --git a/src/ui/texturepacks/TexturePacks.lua b/src/ui/texturepacks/TexturePacks.lua index d86932b3..924ec6d0 100644 --- a/src/ui/texturepacks/TexturePacks.lua +++ b/src/ui/texturepacks/TexturePacks.lua @@ -99,8 +99,7 @@ local function load( src ) return false end - local tpack = TexturePack.new() - tpack:init( src, module, spriteInfos, colorInfos ) + local tpack = TexturePack( src, module, spriteInfos, colorInfos ) return true, tpack end From 56495c17b0681c3e042486f24a570b7cabd38498 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 21 Dec 2017 01:26:15 +0100 Subject: [PATCH 151/178] Refactor ConeOverlay --- src/ui/overlays/ConeOverlay.lua | 258 ++++++++++++++--------------- src/ui/overlays/OverlayPainter.lua | 2 +- 2 files changed, 128 insertions(+), 132 deletions(-) diff --git a/src/ui/overlays/ConeOverlay.lua b/src/ui/overlays/ConeOverlay.lua index 37ff3566..095c5056 100644 --- a/src/ui/overlays/ConeOverlay.lua +++ b/src/ui/overlays/ConeOverlay.lua @@ -7,7 +7,7 @@ -- Required Modules -- ------------------------------------------------ -local Object = require( 'src.Object' ) +local Class = require( 'lib.Middleclass' ) local Bresenham = require( 'lib.Bresenham' ) local ProjectilePath = require( 'src.items.weapons.ProjectilePath' ) local VectorMath = require( 'src.util.VectorMath' ) @@ -18,7 +18,7 @@ local AttackInput = require( 'src.turnbased.helpers.AttackInput' ) -- Module -- ------------------------------------------------ -local ConeOverlay = {} +local ConeOverlay = Class( 'ConeOverlay' ) -- ------------------------------------------------ -- Constants @@ -27,157 +27,153 @@ local ConeOverlay = {} local WEAPON_TYPES = require( 'src.constants.WEAPON_TYPES' ) -- ------------------------------------------------ --- Constructor +-- Private Methods -- ------------------------------------------------ -function ConeOverlay.new( game, pulser, camera ) - local self = Object.new():addInstance( 'ConeOverlay' ) - - local map = game:getMap() - local cone = {} - local tileset = TexturePacks.getTileset() - local tw, th = tileset:getTileDimensions() - - -- ------------------------------------------------ - -- Private Methods - -- ------------------------------------------------ - - --- - -- Determine the height falloff for the cone overlay. - -- @tparam Tile origin The starting tile. - -- @tparam Tile target The target tile. - -- @tparam number steps The distance to the target. - -- @treturn number The calculated falloff value. - -- - local function calculateFalloff( origin, target, steps ) - local oheight = origin:getHeight() - local theight = target:getHeight() - - local delta = oheight - theight - return delta / steps - end - - -- ------------------------------------------------ - -- Public Methods - -- ------------------------------------------------ +--- +-- Determine the height falloff for the cone overlay. +-- @tparam Tile origin The starting tile. +-- @tparam Tile target The target tile. +-- @tparam number steps The distance to the target. +-- @treturn number The calculated falloff value. +-- +local function calculateFalloff( origin, target, steps ) + local oheight = origin:getHeight() + local theight = target:getHeight() - function self:generate() - -- Exit early if we aren't in the attack input mode. - if not game:getState():getInputMode():isInstanceOf( AttackInput ) then - return - end + local delta = oheight - theight + return delta / steps +end - -- Exit early if the character doesn't have a weapon or the weapon is - -- of type melee. - local weapon = game:getCurrentCharacter():getWeapon() - if not weapon or weapon:getSubType() == WEAPON_TYPES.MELEE then - return - end +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - -- Get the tile at the mouse pointer's position. - local cx, cy = camera:getMouseWorldGridPosition() - local target = map:getTileAt( cx, cy ) +function ConeOverlay:initialize( game, pulser, camera ) + self.game = game + self.map = game:getMap() + self.pulser = pulser + self.camera = camera - -- Exit early if we don't have a valid target. - if not target then - return - end + self.cone = {} + self.tileset = TexturePacks.getTileset() + self.tw, self.th = self.tileset:getTileDimensions() +end - -- Use the number of attacks to predict the deviation for burst attacks correctly. - local count = weapon:getSubType() == WEAPON_TYPES.RANGED and weapon:getAttacks() or 1; - - -- Calculate the deviation. - local character = game:getCurrentCharacter() - local deviation = ProjectilePath.getMaximumDeviation( character, weapon, count ) - local origin = character:getTile() - local px, py = origin:getPosition() - local tx, ty = target:getPosition() - - -- Calculate the height falloff for the cone. - local _, steps = Bresenham.line( px, py, tx, ty ) - local falloff = calculateFalloff( origin, target, steps ) - local startHeight = origin:getHeight() - - do - -- Callback function used in bresenham's algorithm. - -- The status variable has to be declared here so it carries over - -- between different tiles in bresenham's line algorithm. - local status = 1 - local function callback( sx, sy, counter ) - -- Get the tile at the rounded integer coordinates. - local tile = map:getTileAt( math.floor( sx + 0.5 ), math.floor( sy + 0.5 )) - - -- Stop early if the tile is not valid. - if not tile then - return false - end +function ConeOverlay:generate() + -- Exit early if we aren't in the attack input mode. + if not self.game:getState():getInputMode():isInstanceOf( AttackInput ) then + return + end - -- Stop early if the target is out of range. - if counter > weapon:getRange() then - return false - end + -- Exit early if the character doesn't have a weapon or the weapon is + -- of type melee. + local weapon = self.game:getCurrentCharacter():getWeapon() + if not weapon or weapon:getSubType() == WEAPON_TYPES.MELEE then + return + end - -- Always advance the ray on the original tile. - if tile == origin then - return true - end + -- Get the tile at the mouse pointer's position. + local cx, cy = self.camera:getMouseWorldGridPosition() + local target = self.map:getTileAt( cx, cy ) - -- Calculate the height of the ray. - local height = startHeight - (counter+1) * falloff + -- Exit early if we don't have a valid target. + if not target then + return + end - -- Assign a status for this tile. - -- 1 means the projectile can pass freely. - -- 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 - nstatus = 2 - elseif tile:hasWorldObject() and height <= tile:getWorldObject():getHeight() then - -- Indestructible worldobjects block the shot. - if not tile:getWorldObject():isDestructible() then - nstatus = 3 - else - nstatus = 2 - end - end + -- Use the number of attacks to predict the deviation for burst attacks correctly. + local count = weapon:getSubType() == WEAPON_TYPES.RANGED and weapon:getAttacks() or 1 + + -- Calculate the deviation. + local character = self.game:getCurrentCharacter() + local deviation = ProjectilePath.getMaximumDeviation( character, weapon, count ) + local origin = character:getTile() + local px, py = origin:getPosition() + local tx, ty = target:getPosition() + + -- Calculate the height falloff for the cone. + local _, steps = Bresenham.line( px, py, tx, ty ) + local falloff = calculateFalloff( origin, target, steps ) + local startHeight = origin:getHeight() + + do + -- Callback function used in bresenham's algorithm. + -- The status variable has to be declared here so it carries over + -- between different tiles in bresenham's line algorithm. + local status = 1 + local function callback( sx, sy, counter ) + -- Get the tile at the rounded integer coordinates. + local tile = self.map:getTileAt( math.floor( sx + 0.5 ), math.floor( sy + 0.5 )) + + -- Stop early if the tile is not valid. + if not tile then + return false + end - -- The new status can't be better than the previously stored status. - status = math.max( nstatus, status ) + -- Stop early if the target is out of range. + if counter > weapon:getRange() then + return false + end - -- Since multiple arrays might touch the same tile we use the maximum - -- value stored for this tile. - cone[tile] = math.max( status, ( cone[tile] or 1 )) + -- Always advance the ray on the original tile. + if tile == origin then return true end - -- Shoot multiple rays from the negative to the positive maxima for the - -- weapon's "spread" angle. - for angle = -deviation, deviation, 0.2 do - local nx, ny = VectorMath.rotate( px, py, tx, ty, angle ) - status = 1 - Bresenham.line( px, py, nx, ny, callback ) + -- Calculate the height of the ray. + local height = startHeight - (counter+1) * falloff + + -- Assign a status for this tile. + -- 1 means the projectile can pass freely. + -- 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 + nstatus = 2 + elseif tile:hasWorldObject() and height <= tile:getWorldObject():getHeight() then + -- Indestructible worldobjects block the shot. + if not tile:getWorldObject():isDestructible() then + nstatus = 3 + else + nstatus = 2 + end end + + -- The new status can't be better than the previously stored status. + status = math.max( nstatus, status ) + + -- Since multiple arrays might touch the same tile we use the maximum + -- value stored for this tile. + self.cone[tile] = math.max( status, ( self.cone[tile] or 1 )) + return true end - end - function self:draw() - for tile, status in pairs( cone ) do - local color - if status == 1 then - color = TexturePacks.getColor( 'ui_shot_valid' ) - elseif status == 2 then - color = TexturePacks.getColor( 'ui_shot_potentially_blocked' ) - elseif status == 3 then - color = TexturePacks.getColor( 'ui_shot_blocked' ) - end - love.graphics.setColor( color[1], color[2], color[3], pulser:getPulse() ) - love.graphics.rectangle( 'fill', tile:getX() * tw, tile:getY() * th, tw, th ) - cone[tile] = nil + -- Shoot multiple rays from the negative to the positive maxima for the + -- weapon's "spread" angle. + for angle = -deviation, deviation, 0.2 do + local nx, ny = VectorMath.rotate( px, py, tx, ty, angle ) + status = 1 + Bresenham.line( px, py, nx, ny, callback ) end - TexturePacks.resetColor() end +end - return self +function ConeOverlay:draw() + for tile, status in pairs( self.cone ) do + local color + if status == 1 then + color = TexturePacks.getColor( 'ui_shot_valid' ) + elseif status == 2 then + color = TexturePacks.getColor( 'ui_shot_potentially_blocked' ) + elseif status == 3 then + color = TexturePacks.getColor( 'ui_shot_blocked' ) + end + love.graphics.setColor( color[1], color[2], color[3], self.pulser:getPulse() ) + love.graphics.rectangle( 'fill', tile:getX() * self.tw, tile:getY() * self.th, self.tw, self.th ) + self.cone[tile] = nil + end + TexturePacks.resetColor() end return ConeOverlay diff --git a/src/ui/overlays/OverlayPainter.lua b/src/ui/overlays/OverlayPainter.lua index 55463dc2..de8a6e04 100644 --- a/src/ui/overlays/OverlayPainter.lua +++ b/src/ui/overlays/OverlayPainter.lua @@ -45,7 +45,7 @@ function OverlayPainter.new( game, camera ) local particleLayer = ParticleLayer() local pulser = Pulser.new( 4, 80, 80 ); - local coneOverlay = ConeOverlay.new( game, pulser, camera ) + local coneOverlay = ConeOverlay( game, pulser, camera ) local pathOverlay = PathOverlay( game, pulser ) local tileset = TexturePacks.getTileset() local tw, th = TexturePacks.getTileDimensions() From 2ad3e3316bc9d0fa7193b730099baf860833e375 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 21 Dec 2017 01:27:28 +0100 Subject: [PATCH 152/178] Fix wrong class table --- src/ui/texturepacks/TexturePack.lua | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ui/texturepacks/TexturePack.lua b/src/ui/texturepacks/TexturePack.lua index 512afa35..fa5a0116 100644 --- a/src/ui/texturepacks/TexturePack.lua +++ b/src/ui/texturepacks/TexturePack.lua @@ -17,7 +17,7 @@ local Tileset = require( 'src.ui.texturepacks.Tileset' ) local TexturePack = Class( 'TexturePack' ) -- ------------------------------------------------ --- Constructor +-- Public Methods -- ------------------------------------------------ function TexturePack:initialize( path, source, spriteInfos, colorInfos ) @@ -39,23 +39,23 @@ end -- Getters -- ------------------------------------------------ -function self:getName() +function TexturePack:getName() return self.name end -function self:getFont() +function TexturePack:getFont() return self.font end -function self:getGlyphDimensions() +function TexturePack:getGlyphDimensions() return self.glyphWidth, self.glyphHeight end -function self:getTileset() +function TexturePack:getTileset() return self.tileset end -function self:getColor( id ) +function TexturePack:getColor( id ) return self.colors[id] end From 9f719245eab94ce3b7f55fbdf5e9652dfdb43a7f Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 21 Dec 2017 01:27:52 +0100 Subject: [PATCH 153/178] Fix wrong class table --- src/util/Observable.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/util/Observable.lua b/src/util/Observable.lua index 9c9bd8b4..1b35eb82 100644 --- a/src/util/Observable.lua +++ b/src/util/Observable.lua @@ -23,18 +23,18 @@ function Observable:initialize() self.index = 1 end -function self:observe( observer ) +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 end -function self:remove( index ) +function Observable:remove( index ) self.observers[index] = nil end -function self:publish( event, ... ) +function Observable:publish( event, ... ) for _, observer in pairs( self.observers ) do observer:receive( event, ... ) end From 39266e5729c9df6a2ea09fd1f64b7a0216aaafa5 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 21 Dec 2017 01:30:33 +0100 Subject: [PATCH 154/178] Refactor Equipment --- src/characters/body/BodyFactory.lua | 2 +- src/characters/body/Equipment.lua | 157 +++++++++++++++------------- 2 files changed, 85 insertions(+), 74 deletions(-) diff --git a/src/characters/body/BodyFactory.lua b/src/characters/body/BodyFactory.lua index 64572f2e..804ed1f1 100644 --- a/src/characters/body/BodyFactory.lua +++ b/src/characters/body/BodyFactory.lua @@ -126,7 +126,7 @@ end -- local function assembleBody( creatureID, template, layout ) local body = Body( template.id, template.bloodVolume, template.tags, template.size ) - local equipment = Equipment.new(); + local equipment = Equipment() local inventory = Inventory( template.defaultCarryWeight, template.defaultCarryVolume ) equipment:observe( inventory ); diff --git a/src/characters/body/Equipment.lua b/src/characters/body/Equipment.lua index 9e4f56be..d8c35372 100644 --- a/src/characters/body/Equipment.lua +++ b/src/characters/body/Equipment.lua @@ -1,105 +1,116 @@ -local Log = require( 'src.util.Log' ); -local Observable = require( 'src.util.Observable' ); -local ItemFactory = require( 'src.items.ItemFactory' ); +--- +-- @module Equipment +-- + +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ + +local Log = require( 'src.util.Log' ) +local Observable = require( 'src.util.Observable' ) +local ItemFactory = require( 'src.items.ItemFactory' ) +local Container = require( 'src.items.Container' ) -- ------------------------------------------------ -- Module -- ------------------------------------------------ -local Equipment = {}; +local Equipment = Observable:subclass( 'Equipment' ) -function Equipment.new() - local self = Observable.new():addInstance( 'Equipment' ); +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - local slots = {}; +function Equipment:initialize() + Observable.initialize( self ) - function self:addSlot( slot ) - local index = slot:getIndex(); - assert( not slots[index], 'ID already used! BodyParts have to be unique.' ); - slots[index] = slot; - end + self.slots = {} +end + +function Equipment:addSlot( slot ) + local index = slot:getIndex() + assert( not self.slots[index], 'ID already used! BodyParts have to be unique.' ) + self.slots[index] = slot +end - function self:dropAllItems( tile ) - for _, slot in pairs( slots ) do - if slot:containsItem() then - local item = slot:getItem(); - if not item:isPermanent() then - tile:getInventory():addItem( item ); - end - slot:removeItem(); +function Equipment:dropAllItems( tile ) + for _, slot in pairs( self.slots ) do + if slot:containsItem() then + local item = slot:getItem() + if not item:isPermanent() then + tile:getInventory():addItem( item ) end + slot:removeItem() end end +end - function self:removeItem( slot ) - local item = slot:getItem(); - -- TODO search through item table. - if item:instanceOf( 'Container' ) then - self:publish( 'CHANGE_VOLUME', -item:getCarryCapacity() ); - end - - slot:removeItem(); - return item; +function Equipment:removeItem( slot ) + local item = slot:getItem() + -- TODO search through item table. + if item:isInstanceOf( Container ) then + self:publish( 'CHANGE_VOLUME', -item:getCarryCapacity() ) end - function self:searchAndRemoveItem( item ) - for _, slot in pairs( slots ) do - if item == slot:getItem() then - return self:removeItem( slot ); - end + slot:removeItem() + return item +end + +function Equipment:searchAndRemoveItem( item ) + for _, slot in pairs( self.slots ) do + if item == slot:getItem() then + return self:removeItem( slot ) end end +end - function self:addItem( slot, item ) - if slot:getItemType() == item:getItemType() then - if not slot:getSubType() or slot:getSubType() == item:getSubType() then - -- Notify observers of a volume change. - if item:instanceOf( 'Container' ) then - self:publish( 'CHANGE_VOLUME', item:getCarryCapacity() ); - end - - slot:addItem( item ); - return true; +function Equipment:addItem( slot, item ) + if slot:getItemType() == item:getItemType() then + if not slot:getSubType() or slot:getSubType() == item:getSubType() then + -- Notify observers of a volume change. + if item:isInstanceOf( Container ) then + self:publish( 'CHANGE_VOLUME', item:getCarryCapacity() ) end - end - Log.warn( string.format( 'No applicable slot found for item %s', item:getID() ), 'Equipment' ); - return false; - end - function self:serialize() - local t = {}; - for _, slot in pairs( slots ) do - t[slot:getIndex()] = slot:serialize(); + slot:addItem( item ) + return true end - return t; end + Log.warn( string.format( 'No applicable slot found for item %s', item:getID() ), 'Equipment' ) + return false +end - function self:load( savedEquipment ) - for index, slot in pairs( savedEquipment ) do - if slot.item then - slots[index]:addItem( ItemFactory.loadItem( slot.item )); - end - end +function Equipment:serialize() + local t = {} + for _, slot in pairs( self.slots ) do + t[slot:getIndex()] = slot:serialize() end + return t +end - --- - -- Gets an item of the specified type without removing it from the inventory. - -- @param type (string) The type of item to get. - -- @return (Item) An item of the specified type. - -- - function self:getItem( type ) - for _, slot in pairs( slots ) do - if slot:getItemType() == type then - return slot:getItem(); - end +function Equipment:load( savedEquipment ) + for index, slot in pairs( savedEquipment ) do + if slot.item then + self.slots[index]:addItem( ItemFactory.loadItem( slot.item )) end end +end - function self:getSlots() - return slots; +--- +-- Gets an item of the specified type without removing it from the inventory. +-- @param type (string) The type of item to get. +-- @return (Item) An item of the specified type. +-- +function Equipment:getItem( type ) + for _, slot in pairs( self.slots ) do + if slot:getItemType() == type then + return slot:getItem() + end end +end - return self; +function Equipment:getSlots() + return self.slots end -return Equipment; +return Equipment From e628b0b159a551bc22455956807b1287b2209c11 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 21 Dec 2017 01:33:49 +0100 Subject: [PATCH 155/178] Fix faulty constructor syntax --- src/items/weapons/Ammunition.lua | 2 +- src/items/weapons/ThrownWeapon.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/items/weapons/Ammunition.lua b/src/items/weapons/Ammunition.lua index 2581bd89..d1461c31 100644 --- a/src/items/weapons/Ammunition.lua +++ b/src/items/weapons/Ammunition.lua @@ -23,7 +23,7 @@ function Ammunition:initialize( template ) Item.initialize( self, template ) self.damageType = template.damageType - self.effects = AmmunitionEffects.new( template.effects ) + self.effects = AmmunitionEffects( template.effects ) end function Ammunition:getDamageType() diff --git a/src/items/weapons/ThrownWeapon.lua b/src/items/weapons/ThrownWeapon.lua index adffeb7e..d9411450 100644 --- a/src/items/weapons/ThrownWeapon.lua +++ b/src/items/weapons/ThrownWeapon.lua @@ -22,7 +22,7 @@ local ThrownWeapon = Weapon:subclass( 'ThrownWeapon' ) function ThrownWeapon:initialize( template ) Weapon.initialize( self, template ) - self.effects = AmmunitionEffects.new( template.effects ) + self.effects = AmmunitionEffects( template.effects ) self.range = template.range end From ef268b3aa78d83bef2f3b485318750fe25761c36 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 21 Dec 2017 01:40:32 +0100 Subject: [PATCH 156/178] Refactor Map --- src/CombatState.lua | 7 +- src/map/Map.lua | 288 ++++++++++++++++++++++---------------------- 2 files changed, 147 insertions(+), 148 deletions(-) diff --git a/src/CombatState.lua b/src/CombatState.lua index 6cb9288b..1785535f 100644 --- a/src/CombatState.lua +++ b/src/CombatState.lua @@ -38,9 +38,7 @@ local FACTIONS = require( 'src.constants.FACTIONS' ) local function loadMap( savedMap ) local loader = MapLoader.new() local tiles, mw, mh = loader:recreateMap( savedMap ) - local map = Map.new() - map:init( tiles, mw, mh ) - return map + return Map( tiles, mw, mh ) end local function createMap() @@ -49,8 +47,7 @@ local function createMap() local tiles = generator:getTiles() local mw, mh = generator:getTileGridDimensions() - local map = Map.new() - map:init( tiles, mw, mh ) + local map = Map( tiles, mw, mh ) map:setSpawnpoints( generator:getSpawnpoints() ) return map end diff --git a/src/map/Map.lua b/src/map/Map.lua index 0ceec772..9c3c8cfa 100644 --- a/src/map/Map.lua +++ b/src/map/Map.lua @@ -1,3 +1,11 @@ +--- +-- @module Map +-- + +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ + local Observable = require( 'src.util.Observable' ) local WorldObjectFactory = require( 'src.map.worldobjects.WorldObjectFactory' ) local ItemFactory = require( 'src.items.ItemFactory' ) @@ -6,7 +14,7 @@ local ItemFactory = require( 'src.items.ItemFactory' ) -- Module -- ------------------------------------------------ -local Map = {} +local Map = Observable:subclass( 'Map' ) -- ------------------------------------------------ -- Constants @@ -15,177 +23,171 @@ local Map = {} local DIRECTION = require( 'src.constants.DIRECTION' ) -- ------------------------------------------------ --- Constructor +-- Private Methods -- ------------------------------------------------ -function Map.new() - local self = Observable.new():addInstance( 'Map' ) - - -- ------------------------------------------------ - -- Private Attributes - -- ------------------------------------------------ - - local tiles - local width, height - local spawnpoints - - -- ------------------------------------------------ - -- Private Methods - -- ------------------------------------------------ - - --- - -- Gives each tile a reference to its neighbours. - -- - local function addNeighbours() - 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 +--- +-- 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. +-- +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 end +end - -- ------------------------------------------------ - -- Public Methods - -- ------------------------------------------------ - - --- - -- Initialises the map by creating the Tiles, creating references to the - -- neighbouring tiles and adding WorldObjects. - -- - function self:init( ntiles, nwidth, nheight ) - tiles = ntiles - width, height = nwidth, nheight - addNeighbours() - end +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - --- - -- Iterates over all tiles and performs the callback function on them. - -- @param callback (function) The operation to perform on each tile. - -- - function self:iterate( callback ) - for x = 1, width do - for y = 1, height do - callback( tiles[x][y], x, y ) - end +--- +-- Initialises the Map by linking all tiles to their neighbours. +-- @tparam table tiles A table containing all of the Map's tiles. +-- @tparam number width The Map's width. +-- @tparam number height The Map's height. +-- +function Map:initialize( tiles, width, height ) + Observable.initialize( self ) + + self.tiles = tiles + self.width = width + self.height = height + + addNeighbours( self, self.tiles ) +end + +--- +-- Iterates over all tiles and performs the callback function on them. +-- @tparam function callback The operation to perform on each tile. +-- +function Map:iterate( callback ) + for x = 1, self.width do + for y = 1, self.height do + callback( self.tiles[x][y], x, y ) end end +end - --- - -- Randomly searches for a tile on which a player could be spawned. - -- @tparam string faction The faction id to spawn a character for. - -- @treturn Tile A tile suitable for spawning. - -- - function self:findSpawnPoint( faction ) - for _ = 1, 2000 do - local index = love.math.random( #spawnpoints[faction] ) - local spawn = spawnpoints[faction][index] - - local tile = self:getTileAt( spawn.x, spawn.y ) - if tile:isSpawn() and tile:isPassable() and not tile:isOccupied() then - return tile - end +--- +-- Randomly searches for a tile on which a creature could be spawned. +-- @tparam string faction The faction id to spawn a creature for. +-- @treturn Tile A tile suitable for spawning. +-- +function Map:findSpawnPoint( faction ) + for _ = 1, 2000 do + local index = love.math.random( #self.spawnpoints[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 + return tile end - error( string.format( 'Can not find a valid spawnpoint at position!' )) end + error( string.format( 'Can not find a valid spawnpoint at position!' )) +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. - -- - function self:update() - for x = 1, #tiles do - for y = 1, #tiles[x] do - local tile = 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 +--- +-- 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. +-- +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 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() + -- 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 - self:publish( 'TILE_UPDATED', tile ) 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 self:serialize() - local t = { - width = width, - height = height, - tiles = {} - } +function Map:serialize() + local t = { + width = self.width, + height = self.height, + tiles = {} + } - for x = 1, #tiles do - for y = 1, #tiles[x] do - table.insert( t.tiles, tiles[x][y]:serialize() ) - end + for x = 1, #self.tiles do + for y = 1, #self.tiles[x] do + table.insert( t.tiles, self.tiles[x][y]:serialize() ) end - - return t end - -- ------------------------------------------------ - -- Getters - -- ------------------------------------------------ - - --- - -- Returns the Tile at the given coordinates. - -- @param x (number) The position along the x-axis. - -- @param y (number) The position along the y-axis. - -- @return (Tile) The Tile at the given position. - -- - function self:getTileAt( x, y ) - return tiles[x] and tiles[x][y] - end + return t +end - --- - -- Returns the map's dimensions. - -- @treturn number The map's width. - -- @treturn number The map's height. - -- - function self:getDimensions() - return width, height - end +-- ------------------------------------------------ +-- Getters +-- ------------------------------------------------ - function self:setSpawnpoints( nspawnpoints ) - spawnpoints = nspawnpoints - end +--- +-- Returns the Tile at the given coordinates. +-- @tparam number x The position along the x-axis. +-- @tparam number y The position along the y-axis. +-- @treturn Tile The Tile at the given position. +-- +function Map:getTileAt( x, y ) + return self.tiles[x] and self.tiles[x][y] +end + +--- +-- Returns the map's dimensions. +-- @treturn number The map's width. +-- @treturn number The map's height. +-- +function Map:getDimensions() + return self.width, self.height +end - return self +--- +-- Sets the spawnpoints for this map. +-- @tparam table spawnpoints The spawnpoints for all factions on this map. +-- +function Map:setSpawnpoints( spawnpoints ) + self.spawnpoints = spawnpoints end return Map From ca4c2d5169e47223e3a25bd8a2a516fcaa5fa30f Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 21 Dec 2017 01:45:58 +0100 Subject: [PATCH 157/178] Refactor UIInventoryDragboard --- .../inventory/UIInventoryDragboard.lua | 156 ++++++++---------- src/ui/screens/InventoryScreen.lua | 2 +- 2 files changed, 73 insertions(+), 85 deletions(-) diff --git a/src/ui/elements/inventory/UIInventoryDragboard.lua b/src/ui/elements/inventory/UIInventoryDragboard.lua index ecbb11f1..4ba5d297 100644 --- a/src/ui/elements/inventory/UIInventoryDragboard.lua +++ b/src/ui/elements/inventory/UIInventoryDragboard.lua @@ -16,7 +16,7 @@ local UIBackground = require( 'src.ui.elements.UIBackground' ) -- Module -- ------------------------------------------------ -local UIInventoryDragboard = {} +local UIInventoryDragboard = Observable:subclass( 'UIInventoryDragboard' ) -- ------------------------------------------------ -- Constants @@ -25,111 +25,99 @@ local UIInventoryDragboard = {} local ITEM_WIDTH = 20 -- ------------------------------------------------ --- Constructor +-- Private Methods -- ------------------------------------------------ -function UIInventoryDragboard.new() - local self = Observable.new():addInstance( 'UIInventoryDragboard' ) +local function returnItemToOrigin( item, origin ) + -- TODO Hack to bridge gap between middleclass and old oop. + if origin.instanceOf and origin:instanceOf( 'EquipmentSlot' ) then + origin:addItem( item ) + else + origin:drop( item ); + end +end - -- ------------------------------------------------ - -- Private Attributes - -- ------------------------------------------------ +local function createLabel( item ) + local label = Translator.getText( item:getID() ) + if item:instanceOf( 'ItemStack' ) and item:getItemCount() > 1 then + label = string.format( '%s (%d)', label, item:getItemCount() ) + end + return label +end - local background = UIBackground( 0, 0, 0, 0, ITEM_WIDTH, 1 ) - local dragContext +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - -- ------------------------------------------------ - -- Private Methods - -- ------------------------------------------------ +function UIInventoryDragboard:initialize() + Observable.initialize( self ) - local function returnItemToOrigin( item, origin ) - -- TODO Hack to bridge gap between middleclass and old oop. - if origin.instanceOf and origin:instanceOf( 'EquipmentSlot' ) then - origin:addItem( item ) - else - origin:drop( item ); - end - end + self.background = UIBackground( 0, 0, 0, 0, ITEM_WIDTH, 1 ) +end - local function createLabel() - local item = dragContext.item - local label = Translator.getText( item:getID() ) - if item:instanceOf( 'ItemStack' ) and item:getItemCount() > 1 then - label = string.format( '%s (%d)', label, item:getItemCount() ) - end - return label +function UIInventoryDragboard:draw( lists ) + if not self.dragContext then + return end - -- ------------------------------------------------ - -- Public Methods - -- ------------------------------------------------ - - function self:draw( lists ) - if not dragContext then - return - end - - local tw, th = TexturePacks.getTileDimensions() - local gx, gy = GridHelper.getMouseGridPosition() - - -- Move background and draw it. - background:setOrigin( gx, gy ) - background:setColor( 'ui_inventory_drag_bg' ) - background:draw() - - -- Check if the dragged item would fit. - TexturePacks.setColor( 'ui_inventory_drag_text' ) - for _, list in pairs( lists ) do - if list:isMouseOver() then - if not list:doesFit( dragContext.item ) then - TexturePacks.setColor( 'ui_inventory_full' ) - break - end + local tw, th = TexturePacks.getTileDimensions() + local gx, gy = GridHelper.getMouseGridPosition() + + -- Move background and draw it. + self.background:setOrigin( gx, gy ) + self.background:setColor( 'ui_inventory_drag_bg' ) + self.background:draw() + + -- Check if the dragged item would fit. + TexturePacks.setColor( 'ui_inventory_drag_text' ) + for _, list in pairs( lists ) do + if list:isMouseOver() then + if not list:doesFit( self.dragContext.item ) then + TexturePacks.setColor( 'ui_inventory_full' ) + break end end - - love.graphics.print( createLabel(), (gx+1) * tw, gy * th ) - TexturePacks.resetColor() end - function self:drag( item, origin ) - assert( item and origin, 'Missing parameters.' ) - love.mouse.setVisible( false ) - dragContext = { item = item, origin = origin } - end + love.graphics.print( createLabel( self.dragContext.item ), (gx+1) * tw, gy * th ) + TexturePacks.resetColor() +end - function self:drop( target ) - love.mouse.setVisible( true ) +function UIInventoryDragboard:drag( item, origin ) + assert( item and origin, 'Missing parameters.' ) + love.mouse.setVisible( false ) + self.dragContext = { item = item, origin = origin } +end - if not target then - returnItemToOrigin( dragContext.item, dragContext.origin ) - else - local success = target:drop( dragContext.item, dragContext.origin ) - if not success then - returnItemToOrigin( dragContext.item, dragContext.origin ) - end - end +function UIInventoryDragboard:drop( target ) + love.mouse.setVisible( true ) - self:clearDragContext() + if not target then + returnItemToOrigin( self.dragContext.item, self.dragContext.origin ) + else + local success = target:drop( self.dragContext.item, self.dragContext.origin ) + if not success then + returnItemToOrigin( self.dragContext.item, self.dragContext.origin ) + end end - function self:hasDragContext() - return dragContext ~= nil - end + self:clearDragContext() +end - function self:getDragContext() - return dragContext - end +function UIInventoryDragboard:hasDragContext() + return self.dragContext ~= nil +end - function self:clearDragContext() - dragContext = nil - end +function UIInventoryDragboard:getDragContext() + return self.dragContext +end - function self:getDraggedItem() - return dragContext.item - end +function UIInventoryDragboard:clearDragContext() + self.dragContext = nil +end - return self +function UIInventoryDragboard:getDraggedItem() + return self.dragContext.item end return UIInventoryDragboard diff --git a/src/ui/screens/InventoryScreen.lua b/src/ui/screens/InventoryScreen.lua index d8404504..b5dc2e0f 100644 --- a/src/ui/screens/InventoryScreen.lua +++ b/src/ui/screens/InventoryScreen.lua @@ -319,7 +319,7 @@ function InventoryScreen:initialize( character, target ) -- UI inventory lists. self.lists, self.listLabels = createInventoryLists( self.x, self.y, character, target ) - self.dragboard = UIInventoryDragboard.new() + self.dragboard = UIInventoryDragboard() -- Add the item stats area which displays the item attributes and a description area. -- x-axis: Outline left => 1 From 572f40195a072fa767d244edc7bd5d460c120779 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 21 Dec 2017 01:55:46 +0100 Subject: [PATCH 158/178] Fix isInstanceOf calls --- src/characters/actions/Reload.lua | 9 ++++++--- src/characters/ai/behaviortree/leafs/BTCanReload.lua | 3 ++- src/characters/body/Body.lua | 3 ++- src/ui/elements/inventory/UIEquipmentList.lua | 6 ++++-- src/ui/elements/inventory/UIInventoryDragboard.lua | 7 ++++--- src/ui/elements/inventory/UIInventoryItem.lua | 7 ++++--- src/ui/screens/InventoryScreen.lua | 3 ++- 7 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/characters/actions/Reload.lua b/src/characters/actions/Reload.lua index 4ef79ec5..5a1931a0 100644 --- a/src/characters/actions/Reload.lua +++ b/src/characters/actions/Reload.lua @@ -9,6 +9,9 @@ local Action = require( 'src.characters.actions.Action' ) local Log = require( 'src.util.Log' ) +local Ammunition = require( 'src.items.weapons.Ammunition' ) +local Item = require( 'src.items.Item' ) +local ItemStack = require( 'src.inventory.ItemStack' ) -- ------------------------------------------------ -- Module @@ -21,7 +24,7 @@ local Reload = Action:subclass( 'Reload' ) -- ------------------------------------------------ local function reload( weapon, inventory, item ) - if item:instanceOf( 'Ammunition' ) and item:getCaliber() == weapon:getMagazine():getCaliber() then + if item:isInstanceOf( Ammunition ) and item:getCaliber() == weapon:getMagazine():getCaliber() then weapon:getMagazine():addRound( item ) inventory:removeItem( item ) end @@ -50,14 +53,14 @@ function Reload:perform() local inventory = self.character:getInventory() for _, item in pairs( inventory:getItems() ) do - if item:instanceOf( 'ItemStack' ) then + if item:isInstanceOf( ItemStack ) then for _, sitem in pairs( item:getItems() ) do reload( weapon, inventory, sitem ) if weapon:getMagazine():isFull() then return true end end - elseif item:instanceOf( 'Item' ) then + elseif item:isInstanceOf( Item ) then reload( weapon, inventory, item ) if weapon:getMagazine():isFull() then return true diff --git a/src/characters/ai/behaviortree/leafs/BTCanReload.lua b/src/characters/ai/behaviortree/leafs/BTCanReload.lua index 6fbe8c84..18ba4647 100644 --- a/src/characters/ai/behaviortree/leafs/BTCanReload.lua +++ b/src/characters/ai/behaviortree/leafs/BTCanReload.lua @@ -8,6 +8,7 @@ local Log = require( 'src.util.Log' ) local BTLeaf = require( 'src.characters.ai.behaviortree.leafs.BTLeaf' ) +local Magazine = require( 'src.items.weapons.Magazine' ) -- ------------------------------------------------ -- Module @@ -25,7 +26,7 @@ function BTCanReload:traverse( ... ) local weapon = character:getWeapon() local inventory = character:getInventory() for _, item in pairs( inventory:getItems() ) do - if item:instanceOf( 'Magazine' ) and item:getCaliber() == weapon:getCaliber() then + if item:isInstanceOf( Magazine ) and item:getCaliber() == weapon:getCaliber() then Log.debug( 'Character can reload', 'BTCanReload' ) return true end diff --git a/src/characters/body/Body.lua b/src/characters/body/Body.lua index 7673f4de..35e80898 100644 --- a/src/characters/body/Body.lua +++ b/src/characters/body/Body.lua @@ -15,6 +15,7 @@ local Log = require( 'src.util.Log' ) local Class = require( 'lib.Middleclass' ) local StatusEffects = require( 'src.characters.body.StatusEffects' ) +local Armor = require( 'src.items.Armor' ) -- ------------------------------------------------ -- Module @@ -73,7 +74,7 @@ local function checkArmorProtection( self, bodyPart, damage ) -- Get the slot contains an item and the item is of type clothing we check -- if the attack actually hits a part that is covered by the armor. local item = slot:getItem() - if item and item:instanceOf( 'Armor' ) then + if item and item:isInstanceOf( Armor ) then Log.debug( 'Body part is protected by armor ' .. item:getID(), 'Body' ) if love.math.random( 0, 100 ) < item:getArmorCoverage() then Log.debug( string.format( 'The armor absorbs %d points of damage.', item:getArmorProtection() ), 'Body' ) diff --git a/src/ui/elements/inventory/UIEquipmentList.lua b/src/ui/elements/inventory/UIEquipmentList.lua index 695d6f5d..db3fde8b 100644 --- a/src/ui/elements/inventory/UIEquipmentList.lua +++ b/src/ui/elements/inventory/UIEquipmentList.lua @@ -8,6 +8,8 @@ local UIElement = require( 'src.ui.elements.UIElement' ) local UIEquipmentSlot = require( 'src.ui.elements.inventory.UIEquipmentSlot' ) +local Container = require( 'src.items.Container' ) +local ItemStack = require( 'src.inventory.ItemStack' ) -- ------------------------------------------------ -- Module @@ -74,7 +76,7 @@ function UIEquipmentList:drag() if uiItem:isMouseOver() and uiItem:getSlot():containsItem() and not uiItem:getSlot():getItem():isPermanent() then local item = self.equipment:removeItem( uiItem:getSlot() ) - if item:instanceOf( 'Container' ) then + if item:isInstanceOf( Container ) then self.character:getInventory():dropItems( self.character:getTile() ) end @@ -94,7 +96,7 @@ end -- function UIEquipmentList:drop( item, origin ) -- Stacks and unequippable items can't be dropped on equipment lists. - if item:instanceOf( 'ItemStack' ) or not item:isEquippable() then + if item:isInstanceOf( ItemStack ) or not item:isEquippable() then return false end diff --git a/src/ui/elements/inventory/UIInventoryDragboard.lua b/src/ui/elements/inventory/UIInventoryDragboard.lua index 4ba5d297..558f97ba 100644 --- a/src/ui/elements/inventory/UIInventoryDragboard.lua +++ b/src/ui/elements/inventory/UIInventoryDragboard.lua @@ -11,6 +11,8 @@ local TexturePacks = require( 'src.ui.texturepacks.TexturePacks' ) local Translator = require( 'src.util.Translator' ) local GridHelper = require( 'src.util.GridHelper' ) local UIBackground = require( 'src.ui.elements.UIBackground' ) +local EquipmentSlot = require( 'src.characters.body.EquipmentSlot' ) +local ItemStack = require( 'src.inventory.ItemStack' ) -- ------------------------------------------------ -- Module @@ -29,8 +31,7 @@ local ITEM_WIDTH = 20 -- ------------------------------------------------ local function returnItemToOrigin( item, origin ) - -- TODO Hack to bridge gap between middleclass and old oop. - if origin.instanceOf and origin:instanceOf( 'EquipmentSlot' ) then + if origin:isInstanceOf( EquipmentSlot ) then origin:addItem( item ) else origin:drop( item ); @@ -39,7 +40,7 @@ end local function createLabel( item ) local label = Translator.getText( item:getID() ) - if item:instanceOf( 'ItemStack' ) and item:getItemCount() > 1 then + if item:isInstanceOf( ItemStack ) and item:getItemCount() > 1 then label = string.format( '%s (%d)', label, item:getItemCount() ) end return label diff --git a/src/ui/elements/inventory/UIInventoryItem.lua b/src/ui/elements/inventory/UIInventoryItem.lua index 806876d7..d3758f20 100644 --- a/src/ui/elements/inventory/UIInventoryItem.lua +++ b/src/ui/elements/inventory/UIInventoryItem.lua @@ -10,6 +10,7 @@ local UIElement = require( 'src.ui.elements.UIElement' ) local UIBackground = require( 'src.ui.elements.UIBackground' ) local UILabel = require( 'src.ui.elements.UILabel' ) local Translator = require( 'src.util.Translator' ) +local ItemStack = require( 'src.inventory.ItemStack' ) -- ------------------------------------------------ -- Module @@ -31,7 +32,7 @@ end local function createInfo( self ) local count = 1 - if self.item:instanceOf( 'ItemStack' ) and self.item:getItemCount() > 1 then + 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' ) @@ -65,13 +66,13 @@ function UIInventoryItem:draw() end function UIInventoryItem:drag( rmb, fullstack ) - if self.item:instanceOf( 'ItemStack' ) and rmb then + if self.item:isInstanceOf( ItemStack ) and rmb then if self.item:getItemCount() == 1 then return self.item else return self.item:split() end - elseif self.item:instanceOf( 'ItemStack' ) and not fullstack then + elseif self.item:isInstanceOf( ItemStack ) and not fullstack then return self.item:getItem() end return self.item diff --git a/src/ui/screens/InventoryScreen.lua b/src/ui/screens/InventoryScreen.lua index b5dc2e0f..d2b6ee13 100644 --- a/src/ui/screens/InventoryScreen.lua +++ b/src/ui/screens/InventoryScreen.lua @@ -17,6 +17,7 @@ local UIInventoryDragboard = require( 'src.ui.elements.inventory.UIInventoryDrag local UIItemStats = require( 'src.ui.elements.inventory.UIItemStats' ) local GridHelper = require( 'src.util.GridHelper' ) local Translator = require( 'src.util.Translator' ) +local Container = require( 'src.items.Container' ) -- ------------------------------------------------ -- Module @@ -274,7 +275,7 @@ local function drag( button, lists, dragboard, itemStats ) -- If the dragged item is a container we need to refresh the inventory lists -- because it changes the inventory volumes. - if item:instanceOf( 'Container' ) then + if item:isInstanceOf( Container ) then refreshLists( lists ) end end From af0dc24d80b8d70337719d56087db5d346c53b4b Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 21 Dec 2017 02:13:04 +0100 Subject: [PATCH 159/178] Fix broken Inventory --- src/inventory/Inventory.lua | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/inventory/Inventory.lua b/src/inventory/Inventory.lua index 3d4e0207..53960c6e 100644 --- a/src/inventory/Inventory.lua +++ b/src/inventory/Inventory.lua @@ -8,6 +8,7 @@ local Class = require( 'lib.Middleclass' ) local Log = require( 'src.util.Log' ) +local Item = require( 'src.items.Item' ) local ItemStack = require( 'src.inventory.ItemStack' ) local ItemFactory = require( 'src.items.ItemFactory' ) @@ -91,14 +92,14 @@ end local function addStackableItem( items, item, index ) -- Check if we already have an item stack to add this item to. for _, stack in ipairs( items ) do - if stack:isInstanceOf( 'ItemStack' ) and stack:getID() == item:getID() then + if stack:isInstanceOf( ItemStack ) and stack:getID() == item:getID() then stack:addItem( item ) return true end end -- If not we create a new stack. - local stack = ItemStack.new( item:getID() ) + local stack = ItemStack( item:getID() ) stack:addItem( item ) table.insert( items, index, stack ) return true @@ -113,7 +114,7 @@ end local function removeItem( items, item ) for i = 1, #items do -- Check if item is part of a stack. - if items[i]:isInstanceOf( 'ItemStack' ) then + if items[i]:isInstanceOf( ItemStack ) then local success = items[i]:removeItem( item ) if success then -- Remove the stack if it is empty. @@ -138,7 +139,7 @@ end -- local function removeItemStack( items, stack ) for i = 1, #items do - if items[i]:isInstanceOf( 'ItemStack' ) and items[i] == stack then + if items[i]:isInstanceOf( ItemStack ) and items[i] == stack then table.remove( items, i ) return true end @@ -148,8 +149,8 @@ end -- TODO: proper documentation local function merge( self, stack, ostack ) - assert( stack:isInstanceOf( 'ItemStack' ), 'Expected parameter of type ItemStack.' ) - assert( ostack:isInstanceOf( 'ItemStack' ), 'Expected parameter of type ItemStack.' ) + assert( stack:isInstanceOf( ItemStack ), 'Expected parameter of type ItemStack.' ) + assert( ostack:isInstanceOf( ItemStack ), 'Expected parameter of type ItemStack.' ) for i = #ostack:getItems(), 1, -1 do local item = ostack:getItems()[i] @@ -231,11 +232,11 @@ function Inventory:addItem( item, index ) return false end - if item:isInstanceOf( 'ItemStack' ) then + if item:isInstanceOf( ItemStack ) then return addItemStack( self.items, item, index ) end - if item:isInstanceOf( 'Item' ) then + if item:isInstanceOf( Item ) then if item:isStackable() then return addStackableItem( self.items, item, index ) else @@ -253,8 +254,8 @@ end function Inventory:insertItem( item, oitem ) for i = 1, #self.items do if self.items[i] == oitem then - if oitem:isInstanceOf( 'ItemStack' ) and oitem:getID() == item:getID() then - if item:isInstanceOf( 'ItemStack' ) then + if oitem:isInstanceOf( ItemStack ) and oitem:getID() == item:getID() then + if item:isInstanceOf( ItemStack ) then return merge( self, oitem, item ) end end @@ -269,11 +270,11 @@ end -- @treturn boolean True if the item was removed successfully. -- function Inventory:removeItem( item ) - if item:isInstanceOf( 'ItemStack' ) then + if item:isInstanceOf( ItemStack ) then return removeItemStack( self.items, item ) end - if item:isInstanceOf( 'Item') then + if item:isInstanceOf( Item ) then return removeItem( self.items, item ) end end @@ -327,7 +328,7 @@ end function Inventory:getAndRemoveItem( type ) for _, item in ipairs( self.items ) do if item:getItemType() == type then - if item:isInstanceOf( 'ItemStack' ) then + if item:isInstanceOf( ItemStack ) then local i = item:getItem() self:removeItem( i ) return i @@ -353,7 +354,7 @@ function Inventory:countItems( type, id ) -- Match items based on type and id. if item:getItemType() == type and item:getID() == id then -- Count items in stacks. - if item:isInstanceOf( 'ItemStack' ) then + if item:isInstanceOf( ItemStack ) then count = count + item:getItemCount() else count = count + 1 From 43b3761ee5f181bc3d8cbee042b1e8a38a9cf6f0 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 21 Dec 2017 02:16:18 +0100 Subject: [PATCH 160/178] Use new constructor syntax --- src/CombatState.lua | 2 +- src/ui/screens/MapTest.lua | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/CombatState.lua b/src/CombatState.lua index 1785535f..7df1668d 100644 --- a/src/CombatState.lua +++ b/src/CombatState.lua @@ -36,7 +36,7 @@ local FACTIONS = require( 'src.constants.FACTIONS' ) -- ------------------------------------------------ local function loadMap( savedMap ) - local loader = MapLoader.new() + local loader = MapLoader() local tiles, mw, mh = loader:recreateMap( savedMap ) return Map( tiles, mw, mh ) end diff --git a/src/ui/screens/MapTest.lua b/src/ui/screens/MapTest.lua index c8c933d9..7656e9cc 100644 --- a/src/ui/screens/MapTest.lua +++ b/src/ui/screens/MapTest.lua @@ -31,8 +31,7 @@ local function createMap( layout ) local tiles = generator:getTiles() local mw, mh = generator:getTileGridDimensions() - local map = Map.new() - map:init( tiles, mw, mh ) + local map = Map( tiles, mw, mh ) map:setSpawnpoints( generator:getSpawnpoints() ) return map, mw, mh From d9c9e9981087ba7e2890fa5b52521f8667e2b23f Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 21 Dec 2017 02:17:43 +0100 Subject: [PATCH 161/178] Fix faulty parameter type --- src/ui/screens/InventoryScreen.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ui/screens/InventoryScreen.lua b/src/ui/screens/InventoryScreen.lua index d2b6ee13..02631c55 100644 --- a/src/ui/screens/InventoryScreen.lua +++ b/src/ui/screens/InventoryScreen.lua @@ -18,6 +18,7 @@ local UIItemStats = require( 'src.ui.elements.inventory.UIItemStats' ) local GridHelper = require( 'src.util.GridHelper' ) local Translator = require( 'src.util.Translator' ) local Container = require( 'src.items.Container' ) +local Inventory = require( 'src.inventory.Inventory' ) -- ------------------------------------------------ -- Module @@ -168,7 +169,7 @@ local function createTargetInventoryList( x, y, character, target, lists, listL local id, inventory -- TODO How to handle base inventory? - if target:isInstanceOf( 'Inventory' ) then + if target:isInstanceOf( Inventory ) then id, inventory = 'inventory_base', target elseif target:hasWorldObject() and target:getWorldObject():isContainer() then id, inventory = 'inventory_container_inventory', target:getWorldObject():getInventory() From 8ce61b2db8fbd89719b6ec738d097be9d9a017cb Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 21 Dec 2017 02:42:57 +0100 Subject: [PATCH 162/178] Refactor Messenger --- src/Messenger.lua | 46 +++++++++++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/src/Messenger.lua b/src/Messenger.lua index 990d87d3..3b6aece6 100644 --- a/src/Messenger.lua +++ b/src/Messenger.lua @@ -1,39 +1,55 @@ -local Messenger = {}; +--- +-- @module Messenger +-- + +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ + +local Messenger = {} + +-- ------------------------------------------------ +-- Local Variables +-- ------------------------------------------------ + +local subscriptions = {} +local index = 0 -local subscriptions = {}; -local index = 0; +-- ------------------------------------------------ +-- Public Functions +-- ------------------------------------------------ --- -- Publishes a message to all subscribers. --- @param message (string) The message's type. --- @param ... (vararg) One or multiple arguments passed to the subscriber. +-- @tparam string message The message's type. +-- @tparam vararg ... One or multiple arguments passed to the subscriber. -- function Messenger.publish( message, ... ) for _, subscription in pairs( subscriptions ) do if subscription.message == message then - subscription.callback( ... ); + subscription.callback( ... ) end end end --- -- Registers a callback belonging to a certain subscriber. --- @param message (string) The message to listen for. --- @param callback (function) The function to call once the message is published. --- @return (number) The index pointing to the subscription. +-- @tparam string message The message to listen for. +-- @tparam function callback The function to call once the message is published. +-- @treturn number The index pointing to the subscription. -- function Messenger.observe( message, callback ) - index = index + 1; - subscriptions[index] = { message = message, callback = callback }; - return index; + index = index + 1 + subscriptions[index] = { message = message, callback = callback } + return index end --- -- Removes a subscription based on its index. --- @param nindex (number) The index of the subscription to remove. +-- @tparam number index The index of the subscription to remove. -- function Messenger.remove( nindex ) - subscriptions[nindex] = nil; + subscriptions[nindex] = nil end -return Messenger; +return Messenger From c7cf584a6d2fe21e60fb4f2b819221dcb479a9ce Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 21 Dec 2017 02:43:36 +0100 Subject: [PATCH 163/178] Fix param and formal argument name mismatch --- src/Messenger.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Messenger.lua b/src/Messenger.lua index 3b6aece6..79b3a9ce 100644 --- a/src/Messenger.lua +++ b/src/Messenger.lua @@ -46,7 +46,7 @@ end --- -- Removes a subscription based on its index. --- @tparam number index The index of the subscription to remove. +-- @tparam number nindex The index of the subscription to remove. -- function Messenger.remove( nindex ) subscriptions[nindex] = nil From 561fb5e567cd46a2787a7e26616c6e1d796f2047 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 21 Dec 2017 02:44:36 +0100 Subject: [PATCH 164/178] Fix missing @tparam tags --- src/SaveHandler.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/SaveHandler.lua b/src/SaveHandler.lua index 677f045f..a485360b 100644 --- a/src/SaveHandler.lua +++ b/src/SaveHandler.lua @@ -29,8 +29,8 @@ local VERSION_FILE = 'version.data' --- -- Creates a file containing only the version string. --- @string dir The directory to store the version file in. --- @table version A table containing the version field. +-- @tparam string dir The directory to store the version file in. +-- @tparam table version A table containing the version field. -- local function createVersionFile( dir, version ) Compressor.save( version, dir .. '/' .. VERSION_FILE ) From 551f302da0cf9aad5c4723c19e2d7d4fbadd76eb Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 21 Dec 2017 02:48:56 +0100 Subject: [PATCH 165/178] Refactor Pulser --- src/ui/overlays/OverlayPainter.lua | 2 +- src/util/Pulser.lua | 90 +++++++++++++++++------------- 2 files changed, 51 insertions(+), 41 deletions(-) diff --git a/src/ui/overlays/OverlayPainter.lua b/src/ui/overlays/OverlayPainter.lua index de8a6e04..36d03283 100644 --- a/src/ui/overlays/OverlayPainter.lua +++ b/src/ui/overlays/OverlayPainter.lua @@ -44,7 +44,7 @@ function OverlayPainter.new( game, camera ) -- ------------------------------------------------ local particleLayer = ParticleLayer() - local pulser = Pulser.new( 4, 80, 80 ); + local pulser = Pulser( 4, 80, 80 ) local coneOverlay = ConeOverlay( game, pulser, camera ) local pathOverlay = PathOverlay( game, pulser ) local tileset = TexturePacks.getTileset() diff --git a/src/util/Pulser.lua b/src/util/Pulser.lua index bacfac49..34f5bbcb 100644 --- a/src/util/Pulser.lua +++ b/src/util/Pulser.lua @@ -1,42 +1,52 @@ -local Pulser = {}; - -function Pulser.new( nspeed, noffset, nrange ) - local self = {}; - - local speed = nspeed or 1; - local offset = noffset or 1; - local range = nrange or 1; - local timer = 0; - local value; - - -- ------------------------------------------------ - -- Public Functions - -- ------------------------------------------------ - - --- - -- Returns gradually changing values between 0 and 1 which can be used to - -- make elements slowly pulsate. - -- @param dt (number) The time since the last update in seconds. - -- - function self:update( dt ) - timer = timer + dt * speed; - local sin = math.sin( timer ); - value = math.abs( sin ) * range + offset; - end - - -- ------------------------------------------------ - -- Getters - -- ------------------------------------------------ - - --- - -- Returns the pulse value. - -- @return (number) A value between 0 and 1. - -- - function self:getPulse() - return value; - end - - return self; +--- +-- @module Pulser +-- + +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ + +local Class = require( 'lib.Middleclass' ) + +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ + +local Pulser = Class( 'Middleclass' ) + +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ + +function Pulser:initialize( speed, offset, range ) + self.speed = speed or 1 + self.offset = offset or 1 + self.range = range or 1 + + self.timer = 0 + self.value = 0 +end + +--- +-- Returns gradually changing values between 0 and 1 which can be used to +-- make elements slowly pulsate. +-- @tparam number dt The time since the last update in seconds. +-- +function Pulser:update( dt ) + self.timer = self.timer + dt * self.speed + self.value = math.abs( math.sin( self.timer )) * self.range + self.offset +end + +-- ------------------------------------------------ +-- Getters +-- ------------------------------------------------ + +--- +-- Returns the pulse value. +-- @treturn number A value between 0 and 1. +-- +function Pulser:getPulse() + return self.value end -return Pulser; +return Pulser From 0d6f2c2f5b32b6de28646d560b501620016f7fa3 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Thu, 21 Dec 2017 02:51:07 +0100 Subject: [PATCH 166/178] Add ldoc module header --- src/ui/screens/MainMenu.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ui/screens/MainMenu.lua b/src/ui/screens/MainMenu.lua index 0c639e0c..dd4cb584 100644 --- a/src/ui/screens/MainMenu.lua +++ b/src/ui/screens/MainMenu.lua @@ -1,3 +1,7 @@ +--- +-- @module MainMenu +-- + local Screen = require( 'src.ui.screens.Screen' ) local ScreenManager = require( 'lib.screenmanager.ScreenManager' ) local UIButton = require( 'src.ui.elements.UIButton' ) From 64e0805679843cc394190f15abb6f9d5b551eb7f Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Fri, 22 Dec 2017 13:07:12 +0100 Subject: [PATCH 167/178] Fix A* heuristic for diagonal movement --- src/characters/pathfinding/PathFinder.lua | 26 ++--------------------- 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/src/characters/pathfinding/PathFinder.lua b/src/characters/pathfinding/PathFinder.lua index 16ab5828..aab1d04d 100644 --- a/src/characters/pathfinding/PathFinder.lua +++ b/src/characters/pathfinding/PathFinder.lua @@ -14,13 +14,6 @@ local Path = require( 'src.characters.pathfinding.Path' ) local PathFinder = {} --- ------------------------------------------------ --- Constants --- ------------------------------------------------ - -local DIRECTION = require( 'src.constants.DIRECTION' ) -local SQRT = math.sqrt( 2 ) - -- ------------------------------------------------ -- Private Functions -- ------------------------------------------------ @@ -34,22 +27,7 @@ local SQRT = math.sqrt( 2 ) local function calculateHeuristic( a, b ) local distanceX = math.abs( a:getX() - b:getX() ) local distanceY = math.abs( a:getY() - b:getY() ) - return math.max( distanceX, distanceY ) -end - ---- --- Use a modifier for diagonal movement. --- @param direction (string) The direction to get the modifier for. --- @return (number) The modifier. --- -local function getDirectionModifier( direction ) - if direction == DIRECTION.NORTH_EAST - or direction == DIRECTION.NORTH_WEST - or direction == DIRECTION.SOUTH_EAST - or direction == DIRECTION.SOUTH_WEST then - return SQRT - end - return 1 + return (distanceX + distanceY) - 0.9 * math.min( distanceX, distanceY ) end --- @@ -216,7 +194,7 @@ function PathFinder.generatePath( start, target, stance ) -- Check if the tile is valid to use in our path. if isValidTile( tile, closedList, target ) then local cost = calculateCost( tile, target, stance ) - local g = current.g + cost * getDirectionModifier( direction ) + local g = current.g + cost local f = g + calculateHeuristic( tile, target ) -- Check if the tile is in the open list. If it is not, then From 37a5bb963107c7effc206eb8e55036e5c50ce4df Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Fri, 22 Dec 2017 13:11:07 +0100 Subject: [PATCH 168/178] Refactor Util --- src/util/Util.lua | 47 ++++++++++++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/src/util/Util.lua b/src/util/Util.lua index 97942fa6..a05d6881 100644 --- a/src/util/Util.lua +++ b/src/util/Util.lua @@ -1,32 +1,45 @@ -local Util = {}; +--- +-- @module Util +-- + +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ + +local Util = {} + +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ --- -- Gets all tiles within a certain radius around the center tile. --- @param centerTile (Tile) The center of the explosion. --- @param radius (number) The radius in which to get the tiles. --- @return (table) A sequence containing all tiles in the circle. +-- @tparam Map map The map to get the tiles from. +-- @tparam Tile centerTile The center of the circle around which to get the tiles. +-- @tparam number radius The radius in which to get the tiles. +-- @treturn table A sequence containing all tiles in the circle. -- function Util.getTilesInCircle( map, centerTile, radius ) - local list = {}; + local list = {} -- Get all tiles in the rectangle around the centerTile. for x = centerTile:getX() - radius, centerTile:getX() + radius do for y = centerTile:getY() - radius, centerTile:getY() + radius do - local tile = map:getTileAt( x, y ); + local tile = map:getTileAt( x, y ) if tile then - local tx, ty = tile:getPosition(); - tx = centerTile:getX() - tx; - ty = centerTile:getY() - ty; + local tx, ty = tile:getPosition() + tx = centerTile:getX() - tx + ty = centerTile:getY() - ty -- Ignore tiles which lie outside of the radius. if tx * tx + ty * ty <= radius * radius then - list[#list + 1] = tile; + list[#list + 1] = tile end end end end - return list; + return list end --- @@ -51,13 +64,13 @@ end --- -- Clamps a value to a certain range. --- @param min (number) The minimum value to clamp to. --- @param val (number) The value to clamp. --- @param max (number) The maximum value to clamp to. --- @return (number) The clamped value. +-- @tparam number min The minimum value to clamp to. +-- @tparam number val The value to clamp. +-- @tparam number max The maximum value to clamp to. +-- @treturn number The clamped value. -- function Util.clamp( min, val, max ) - return math.max( min, math.min( val, max )); + return math.max( min, math.min( val, max )) end -return Util; +return Util From 67259043f4e0e7daa14c2af51c05a28156384ebd Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Fri, 22 Dec 2017 13:18:05 +0100 Subject: [PATCH 169/178] Move vector math lib to util module --- src/items/weapons/ProjectilePath.lua | 4 ++-- src/ui/overlays/ConeOverlay.lua | 4 ++-- src/util/Util.lua | 27 +++++++++++++++++++++++++ src/util/VectorMath.lua | 30 ---------------------------- 4 files changed, 31 insertions(+), 34 deletions(-) delete mode 100644 src/util/VectorMath.lua diff --git a/src/items/weapons/ProjectilePath.lua b/src/items/weapons/ProjectilePath.lua index 56b1e65f..18691248 100644 --- a/src/items/weapons/ProjectilePath.lua +++ b/src/items/weapons/ProjectilePath.lua @@ -1,5 +1,5 @@ local Bresenham = require( 'lib.Bresenham' ); -local VectorMath = require( 'src.util.VectorMath' ); +local Util = require( 'src.util.Util' ) -- ------------------------------------------------ -- Module @@ -219,7 +219,7 @@ function ProjectilePath.calculate( character, tx, ty, th, weapon, count ) local origin = character:getTile(); local px, py = origin:getPosition(); - local nx, ny = VectorMath.rotate( px, py, tx, ty, actualDeviation, love.math.random( 90, 130 ) / 100 ) + local nx, ny = Util.rotateVector( px, py, tx, ty, actualDeviation, love.math.random( 90, 130 ) / 100 ) nx, ny = math.floor( nx + 0.5 ), math.floor( ny + 0.5 ); -- Determine the height falloff for the projectile. diff --git a/src/ui/overlays/ConeOverlay.lua b/src/ui/overlays/ConeOverlay.lua index 095c5056..fbb94b3e 100644 --- a/src/ui/overlays/ConeOverlay.lua +++ b/src/ui/overlays/ConeOverlay.lua @@ -10,7 +10,7 @@ local Class = require( 'lib.Middleclass' ) local Bresenham = require( 'lib.Bresenham' ) local ProjectilePath = require( 'src.items.weapons.ProjectilePath' ) -local VectorMath = require( 'src.util.VectorMath' ) +local Util = require( 'src.util.Util' ) local TexturePacks = require( 'src.ui.texturepacks.TexturePacks' ) local AttackInput = require( 'src.turnbased.helpers.AttackInput' ) @@ -152,7 +152,7 @@ function ConeOverlay:generate() -- Shoot multiple rays from the negative to the positive maxima for the -- weapon's "spread" angle. for angle = -deviation, deviation, 0.2 do - local nx, ny = VectorMath.rotate( px, py, tx, ty, angle ) + local nx, ny = Util.rotateVector( px, py, tx, ty, angle ) status = 1 Bresenham.line( px, py, nx, ny, callback ) end diff --git a/src/util/Util.lua b/src/util/Util.lua index a05d6881..bc442a1f 100644 --- a/src/util/Util.lua +++ b/src/util/Util.lua @@ -12,6 +12,33 @@ local Util = {} -- Public Methods -- ------------------------------------------------ +--- +-- Rotates the target position by the given angle. +-- @tparam number px The vector's origin along the x-axis. +-- @tparam number py The vector's origin along the y-axis. +-- @tparam number tx The vector's target along the x-axis. +-- @tparam number ty The vector's target along the y-axis. +-- @tparam number angle The angle by which to rotate the vector. +-- @tparam number factor Change the vector's magnitude. +-- @treturn number The new target along the x-axis. +-- @treturn number The new target along the y-axis. +-- +function Util.rotateVector( px, py, tx, ty, angle, factor ) + local vx, vy = tx - px, ty - py + + factor = factor or 1 + vx = vx * factor + vy = vy * factor + + -- Transform angle from degrees to radians. + angle = math.rad( angle ) + + local nx = vx * math.cos( angle ) - vy * math.sin( angle ) + local ny = vx * math.sin( angle ) + vy * math.cos( angle ) + + return px + nx, py + ny +end + --- -- Gets all tiles within a certain radius around the center tile. -- @tparam Map map The map to get the tiles from. diff --git a/src/util/VectorMath.lua b/src/util/VectorMath.lua deleted file mode 100644 index af8c26f9..00000000 --- a/src/util/VectorMath.lua +++ /dev/null @@ -1,30 +0,0 @@ -local VectorMath = {}; - ---- --- Rotates the target position by the given angle. --- @param px (number) --- @param py (number) --- @param tx (number) --- @param ty (number) --- @param angle (number) --- @param factor (number) Change the vector's magnitude. --- @return (number) The new target along the x-axis. --- @return (number) The new target along the y-axis. --- -function VectorMath.rotate( px, py, tx, ty, angle, factor ) - local vx, vy = tx - px, ty - py; - - factor = factor or 1; - vx = vx * factor; - vy = vy * factor; - - -- Transform angle from degrees to radians. - angle = math.rad( angle ); - - local nx = vx * math.cos( angle ) - vy * math.sin( angle ); - local ny = vx * math.sin( angle ) + vy * math.cos( angle ); - - return px + nx, py + ny; -end - -return VectorMath; From 07177bf9dd10485eefc539ff431f966a00b1a3d3 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sat, 23 Dec 2017 02:24:58 +0100 Subject: [PATCH 170/178] Change default controls --- src/Settings.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Settings.lua b/src/Settings.lua index dbeedcce..6438ef45 100644 --- a/src/Settings.lua +++ b/src/Settings.lua @@ -21,7 +21,7 @@ local Settings = {} local FILE_NAME = 'settings.otr' local DEFAULT_SETTINGS = { - version = 3, + version = 4, general = { fullscreen = true, locale = 'en_EN', @@ -36,9 +36,9 @@ local DEFAULT_SETTINGS = { ['r'] = 'action_reload_weapon', ['.'] = 'next_weapon_mode', [','] = 'prev_weapon_mode', - ['1'] = 'movement_mode', - ['2'] = 'attack_mode', - ['3'] = 'interaction_mode', + ['m'] = 'movement_mode', + ['a'] = 'attack_mode', + ['e'] = 'interaction_mode', ['tab'] = 'next_character', ['lshift'] = 'prev_character', ['return'] = 'end_turn', From 59e9879a8e4b29e8ca494101c9a8f2840995a4f5 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sat, 23 Dec 2017 02:48:51 +0100 Subject: [PATCH 171/178] Refactor UserInterface --- src/ui/UserInterface.lua | 229 ++++++++++++++++++-------------- src/ui/screens/CombatScreen.lua | 2 +- 2 files changed, 127 insertions(+), 104 deletions(-) diff --git a/src/ui/UserInterface.lua b/src/ui/UserInterface.lua index e016f2b7..644e876e 100644 --- a/src/ui/UserInterface.lua +++ b/src/ui/UserInterface.lua @@ -1,5 +1,14 @@ -local Log = require( 'src.util.Log' ); -local Translator = require( 'src.util.Translator' ); +--- +-- @module UserInterface +-- + +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ + +local Class = require( 'lib.Middleclass' ) +local Log = require( 'src.util.Log' ) +local Translator = require( 'src.util.Translator' ) local TexturePacks = require( 'src.ui.texturepacks.TexturePacks' ) local AttackInput = require( 'src.turnbased.helpers.AttackInput' ) local MovementInput = require( 'src.turnbased.helpers.MovementInput' ) @@ -10,7 +19,7 @@ local ExecutionState = require( 'src.turnbased.states.ExecutionState' ) -- Module -- ------------------------------------------------ -local UserInterface = {}; +local UserInterface = Class( 'UserInterface' ) -- ------------------------------------------------ -- Constants @@ -19,135 +28,149 @@ local UserInterface = {}; local ITEM_TYPES = require( 'src.constants.ITEM_TYPES' ) -- ------------------------------------------------ --- Constructor +-- Private Methods -- ------------------------------------------------ --- --- Creates an new instance of the UserInterface class. --- @param game (Game) The game object. --- @tparam CameraHandler camera A camera object used to move the map. --- @return (UserInterface) The new instance. +-- Draws some information of the tile the mouse is currently hovering over. +-- @tparam number mouseX The mouse cursor's position along the x-axis. +-- @tparam number mouseY The mouse cursor's position along the y-axis. +-- @tparam Map map The map to inspect. -- -function UserInterface.new( game, camera ) - local self = {}; - - local map = game:getMap(); - local factions = game:getFactions(); - local mouseX, mouseY = 0, 0; - local debug = false; +local function inspectTile( mouseX, mouseY, map ) local font = TexturePacks.getFont() local tw, th = TexturePacks.getTileDimensions() - --- - -- Draws some information of the tile the mouse is currently hovering over. - -- - local function inspectTile() - local x, y = tw, love.graphics.getHeight() - th * 6 - local tile = map:getTileAt( mouseX, mouseY ); + local x, y = tw, love.graphics.getHeight() - th * 6 + local tile = map:getTileAt( mouseX, mouseY ) - if not tile then - return; - end + if not tile then + return + end - love.graphics.print( Translator.getText( 'ui_tile' ), x, y ); + love.graphics.print( Translator.getText( 'ui_tile' ), x, y ) - local sw = font:measureWidth( Translator.getText( 'ui_tile' )) - if tile:hasWorldObject() then - love.graphics.print( Translator.getText( tile:getWorldObject():getID() ), x + sw, y ); - else - love.graphics.print( Translator.getText( tile:getID() ), x + sw, y ); - end + local sw = font:measureWidth( Translator.getText( 'ui_tile' )) + if tile:hasWorldObject() then + love.graphics.print( Translator.getText( tile:getWorldObject():getID() ), x + sw, y ) + else + love.graphics.print( Translator.getText( tile:getID() ), x + sw, y ) end +end - local function drawWeaponInfo( inventory, weapon ) - if weapon then - love.graphics.print( Translator.getText( 'ui_weapon' ), tw, love.graphics.getHeight() - th * 4 ) - love.graphics.print( Translator.getText( weapon:getID() ), tw + font:measureWidth( Translator.getText( 'ui_weapon' )), love.graphics.getHeight() - th * 4 ) - - -- If the weapon is reloadable we show the current ammo in the magazine, - -- the maximum capacity of the magazine and the total amount of ammo - -- on the character. - if weapon:isReloadable() then - local magazine = weapon:getMagazine() - local total = inventory:countItems( ITEM_TYPES.AMMO, magazine:getCaliber() ) - - local text = string.format( ' %d/%d (%d)', magazine:getNumberOfRounds(), magazine:getCapacity(), total ) - love.graphics.print( Translator.getText( 'ui_ammo' ), tw, love.graphics.getHeight() - th * 3 ) - love.graphics.print( text, tw + font:measureWidth( Translator.getText( 'ui_ammo' )), love.graphics.getHeight() - th * 3 ) - end - - love.graphics.print( Translator.getText( 'ui_mode' ), tw, love.graphics.getHeight() - th * 2 ) - love.graphics.print( weapon:getAttackMode().name, tw + font:measureWidth( Translator.getText( 'ui_mode' )), love.graphics.getHeight() - th * 2 ) - end - end +local function drawWeaponInfo( inventory, weapon ) + local font = TexturePacks.getFont() + local tw, th = TexturePacks.getTileDimensions() + + if weapon then + love.graphics.print( Translator.getText( 'ui_weapon' ), tw, love.graphics.getHeight() - th * 4 ) + love.graphics.print( Translator.getText( weapon:getID() ), tw + font:measureWidth( Translator.getText( 'ui_weapon' )), love.graphics.getHeight() - th * 4 ) + + -- If the weapon is reloadable we show the current ammo in the magazine, + -- the maximum capacity of the magazine and the total amount of ammo + -- on the character. + if weapon:isReloadable() then + local magazine = weapon:getMagazine() + local total = inventory:countItems( ITEM_TYPES.AMMO, magazine:getCaliber() ) - local function drawDebugInfo() - if debug then - love.graphics.print( love.timer.getFPS() .. ' FPS', tw, th ) - love.graphics.print( math.floor( collectgarbage( 'count' )) .. ' kb', tw, th * 2 ) - love.graphics.print( 'Mouse: ' .. mouseX .. ', ' .. mouseY, tw, th * 3 ) - love.graphics.print( 'Debug Logging: ' .. tostring( Log.getDebugActive() ), tw, th * 4 ) + local text = string.format( ' %d/%d (%d)', magazine:getNumberOfRounds(), magazine:getCapacity(), total ) + love.graphics.print( Translator.getText( 'ui_ammo' ), tw, love.graphics.getHeight() - th * 3 ) + love.graphics.print( text, tw + font:measureWidth( Translator.getText( 'ui_ammo' )), love.graphics.getHeight() - th * 3 ) end + + love.graphics.print( Translator.getText( 'ui_mode' ), tw, love.graphics.getHeight() - th * 2 ) + love.graphics.print( weapon:getAttackMode().name, tw + font:measureWidth( Translator.getText( 'ui_mode' )), love.graphics.getHeight() - th * 2 ) end +end - local function drawActionPoints( character ) - local apString = 'AP: ' .. character:getActionPoints(); - love.graphics.print( apString, tw, love.graphics.getHeight() - th * 5 ) +local function drawDebugInfo( mouseX, mouseY, debug ) + local tw, th = TexturePacks.getTileDimensions() - -- Hide the cost display during the turn's execution. - if game:getState():isInstanceOf( ExecutionState ) then - return; - end + if debug then + love.graphics.print( love.timer.getFPS() .. ' FPS', tw, th ) + love.graphics.print( math.floor( collectgarbage( 'count' )) .. ' kb', tw, th * 2 ) + love.graphics.print( 'Mouse: ' .. mouseX .. ', ' .. mouseY, tw, th * 3 ) + love.graphics.print( 'Debug Logging: ' .. tostring( Log.getDebugActive() ), tw, th * 4 ) + end +end - local mode = game:getState():getInputMode(); - local tile = game:getMap():getTileAt( camera:getMouseWorldGridPosition() ) - local cost; - - if tile then - if mode:isInstanceOf( AttackInput ) then - cost = mode:getPredictedAPCost( character ); - elseif mode:isInstanceOf( InteractionInput ) then - cost = mode:getPredictedAPCost( tile, character ); - elseif mode:isInstanceOf( MovementInput ) and mode:hasPath() then - cost = mode:getPredictedAPCost(); - end - end +local function drawActionPoints( game, camera, character ) + local font = TexturePacks.getFont() + local tw, th = TexturePacks.getTileDimensions() + local apString = 'AP: ' .. character:getActionPoints() + love.graphics.print( apString, tw, love.graphics.getHeight() - th * 5 ) - if cost then - local costString, costOffset = ' - ' .. cost, font:measureWidth( apString ) - TexturePacks.setColor( 'ui_ap_cost' ) - love.graphics.print( costString, tw + costOffset, love.graphics.getHeight() - th * 5 ) + -- Hide the cost display during the turn's execution. + if game:getState():isInstanceOf( ExecutionState ) then + return + end - local resultString, resultOffset = ' = ' .. character:getActionPoints() - cost, font:measureWidth( apString .. costString ) - TexturePacks.setColor( 'ui_ap_cost_result' ) - love.graphics.print( resultString, tw + resultOffset, love.graphics.getHeight() - th * 5 ) + local mode = game:getState():getInputMode() + local tile = game:getMap():getTileAt( camera:getMouseWorldGridPosition() ) + local cost + + if tile then + if mode:isInstanceOf( AttackInput ) then + cost = mode:getPredictedAPCost( character ) + elseif mode:isInstanceOf( InteractionInput ) then + cost = mode:getPredictedAPCost( tile, character ) + elseif mode:isInstanceOf( MovementInput ) and mode:hasPath() then + cost = mode:getPredictedAPCost() end - TexturePacks.resetColor() end - function self:draw() - drawDebugInfo(); - - local character = factions:getFaction():getCurrentCharacter(); - if factions:getFaction():isAIControlled() then - return; - end + if cost then + local costString, costOffset = ' - ' .. cost, font:measureWidth( apString ) + TexturePacks.setColor( 'ui_ap_cost' ) + love.graphics.print( costString, tw + costOffset, love.graphics.getHeight() - th * 5 ) - drawActionPoints( character ); - inspectTile(); - drawWeaponInfo( character:getInventory(), character:getWeapon() ) + local resultString, resultOffset = ' = ' .. character:getActionPoints() - cost, font:measureWidth( apString .. costString ) + TexturePacks.setColor( 'ui_ap_cost_result' ) + love.graphics.print( resultString, tw + resultOffset, love.graphics.getHeight() - th * 5 ) end + TexturePacks.resetColor() +end - function self:update() - mouseX, mouseY = camera:getMouseWorldGridPosition() - end +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - function self:toggleDebugInfo() - debug = not debug; +--- +-- Initializes an new instance of the UserInterface class. +-- @tparam Game game The game object. +-- @tparam CameraHandler camera A camera object used to move the map. +-- +function UserInterface:initialize( game, camera ) + self.game = game + self.map = game:getMap() + self.factions = game:getFactions() + self.camera = camera + + self.mouseX, self.mouseY = 0, 0 + + self.debug = false +end + +function UserInterface:draw() + drawDebugInfo( self.mouseX, self.mouseY, self.debug ) + + local character = self.factions:getFaction():getCurrentCharacter() + if self.factions:getFaction():isAIControlled() then + return end - return self; + drawActionPoints( self.game, self.camera, character ) + inspectTile( self.mouseX, self.mouseY, self.map ) + drawWeaponInfo( character:getInventory(), character:getWeapon() ) +end + +function UserInterface:update() + self.mouseX, self.mouseY = self.camera:getMouseWorldGridPosition() +end + +function UserInterface:toggleDebugInfo() + self.debug = not self.debug end -return UserInterface; +return UserInterface diff --git a/src/ui/screens/CombatScreen.lua b/src/ui/screens/CombatScreen.lua index f43016ee..b8c1634e 100644 --- a/src/ui/screens/CombatScreen.lua +++ b/src/ui/screens/CombatScreen.lua @@ -34,7 +34,7 @@ function CombatScreen:initialize( playerFaction, savegame ) local mw, mh = self.combatState:getMap():getDimensions() self.camera = Camera( mw, mh, tw, th ) - self.userInterface = UserInterface.new( self.combatState, self.camera ) + self.userInterface = UserInterface( self.combatState, self.camera ) self.overlayPainter = OverlayPainter.new( self.combatState, self.camera ) self.observations = {} From 1ab7eae02c44ef812b1c8c62d9090233b2fb391a Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sat, 23 Dec 2017 02:58:55 +0100 Subject: [PATCH 172/178] Refactor OverlayPainter --- src/ui/overlays/OverlayPainter.lua | 170 ++++++++++++++--------------- src/ui/screens/CombatScreen.lua | 2 +- 2 files changed, 82 insertions(+), 90 deletions(-) diff --git a/src/ui/overlays/OverlayPainter.lua b/src/ui/overlays/OverlayPainter.lua index 36d03283..e05c02ca 100644 --- a/src/ui/overlays/OverlayPainter.lua +++ b/src/ui/overlays/OverlayPainter.lua @@ -10,7 +10,8 @@ -- Required Modules -- ------------------------------------------------ -local Pulser = require( 'src.util.Pulser' ); +local Class = require( 'lib.Middleclass' ) +local Pulser = require( 'src.util.Pulser' ) local ConeOverlay = require( 'src.ui.overlays.ConeOverlay' ) local PathOverlay = require( 'src.ui.overlays.PathOverlay' ) local ParticleLayer = require( 'src.ui.overlays.ParticleLayer' ) @@ -24,112 +25,103 @@ local ExecutionState = require( 'src.turnbased.states.ExecutionState' ) -- Module -- ------------------------------------------------ -local OverlayPainter = {}; +local OverlayPainter = Class( 'OverlayPainter' ) -- ------------------------------------------------ --- Constructor +-- Private Methods -- ------------------------------------------------ --- --- Creates an new instance of the OverlayPainter class. --- @tparam Game game The game object. --- @tparam CameraHandler camera A camera object used to move the map. --- @treturn OverlayPainter The new instance. +-- Draws a mouse cursor that snaps to the grid. -- -function OverlayPainter.new( game, camera ) - local self = {}; - - -- ------------------------------------------------ - -- Private Attributes - -- ------------------------------------------------ - - local particleLayer = ParticleLayer() - local pulser = Pulser( 4, 80, 80 ) - local coneOverlay = ConeOverlay( game, pulser, camera ) - local pathOverlay = PathOverlay( game, pulser ) - local tileset = TexturePacks.getTileset() - local tw, th = TexturePacks.getTileDimensions() - - -- ------------------------------------------------ - -- Private Methods - -- ------------------------------------------------ - - --- - -- Draws a mouse cursor that snaps to the grid. - -- - local function drawMouseCursor() - local gx, gy = camera:getMouseWorldGridPosition() - local cx, cy = gx * tw, gy * th - - TexturePacks.setColor( 'sys_background' ) - love.graphics.rectangle( 'fill', cx, cy, tw, th ) - - local id - if game:getState():getInputMode():isInstanceOf( MovementInput ) then - id = 'ui_mouse_pointer_movement' - elseif game:getState():getInputMode():isInstanceOf( AttackInput ) then - id = 'ui_mouse_pointer_attack' - elseif game:getState():getInputMode():isInstanceOf( InteractionInput ) then - id = 'ui_mouse_pointer_interact' - end - - TexturePacks.setColor( id ) - love.graphics.draw( tileset:getSpritesheet(), tileset:getSprite( id ), cx, cy ) - TexturePacks.resetColor() +local function drawMouseCursor( tileset, tw, th, camera, game ) + local gx, gy = camera:getMouseWorldGridPosition() + local cx, cy = gx * tw, gy * th + + TexturePacks.setColor( 'sys_background' ) + love.graphics.rectangle( 'fill', cx, cy, tw, th ) + + local id + if game:getState():getInputMode():isInstanceOf( MovementInput ) then + id = 'ui_mouse_pointer_movement' + elseif game:getState():getInputMode():isInstanceOf( AttackInput ) then + id = 'ui_mouse_pointer_attack' + elseif game:getState():getInputMode():isInstanceOf( InteractionInput ) then + id = 'ui_mouse_pointer_interact' end - --- - -- Draws all particles on the map that are visible to the player's faction. - -- - local function drawParticles() - 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 - love.graphics.setColor( particle:getColors() ); - if particle:getSprite() then - love.graphics.draw( tileset:getSpritesheet(), tileset:getSprite( particle:getSprite() ), x * tw, y * th ) - else - love.graphics.rectangle( 'fill', x * tw, y * th, tw, th ) - end - TexturePacks.resetColor() + TexturePacks.setColor( id ) + love.graphics.draw( tileset:getSpritesheet(), tileset:getSprite( id ), cx, cy ) + TexturePacks.resetColor() +end + +--- +-- Draws all particles on the map that are visible to the player's faction. +-- +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 + love.graphics.setColor( particle:getColors() ) + if particle:getSprite() then + love.graphics.draw( tileset:getSpritesheet(), tileset:getSprite( particle:getSprite() ), x * tw, y * th ) + else + love.graphics.rectangle( 'fill', x * tw, y * th, tw, th ) end + TexturePacks.resetColor() end end end +end - -- ------------------------------------------------ - -- Public Methods - -- ------------------------------------------------ - - --- - -- Updates the OverlayPainter. - -- @tparam number dt The time since the last frame. - -- - function self:update( dt ) - if not game:getState():isInstanceOf( ExecutionState ) then - coneOverlay:generate() - end +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ + +--- +-- Creates an new instance of the OverlayPainter class. +-- @tparam Game game The game object. +-- @tparam CameraHandler camera A camera object used to move the map. +-- +function OverlayPainter:initialize( game, camera ) + self.game = game + self.camera = camera + + self.particleLayer = ParticleLayer() + self.pulser = Pulser( 4, 80, 80 ) + self.coneOverlay = ConeOverlay( self.game, self.pulser, self.camera ) + self.pathOverlay = PathOverlay( self.game, self.pulser ) + + self.tileset = TexturePacks.getTileset() + self.tw, self.th = TexturePacks.getTileDimensions() +end - particleLayer:update( dt ) - pulser:update( dt ) +--- +-- Updates the OverlayPainter. +-- @tparam number dt The time since the last frame. +-- +function OverlayPainter:update( dt ) + if not self.game:getState():isInstanceOf( ExecutionState ) then + self.coneOverlay:generate() end - --- - -- Draws the OverlayPainter. - -- - function self:draw() - local character = game:getCurrentCharacter() - if not character:getFaction():isAIControlled() - and not game:getState():isInstanceOf( ExecutionState ) then - pathOverlay:draw() - drawMouseCursor() - coneOverlay:draw() - end + self.particleLayer:update( dt ) + self.pulser:update( dt ) +end - drawParticles() +--- +-- Draws the OverlayPainter. +-- +function OverlayPainter:draw() + local character = self.game:getCurrentCharacter() + if not character:getFaction():isAIControlled() + and not self.game:getState():isInstanceOf( ExecutionState ) then + self.pathOverlay:draw() + drawMouseCursor( self.tileset, self.tw, self.th, self.camera, self.game ) + self.coneOverlay:draw() end - return self; + drawParticles( self.tileset, self.tw, self.th, self.particleLayer, self.game ) end -return OverlayPainter; +return OverlayPainter diff --git a/src/ui/screens/CombatScreen.lua b/src/ui/screens/CombatScreen.lua index b8c1634e..5c951f50 100644 --- a/src/ui/screens/CombatScreen.lua +++ b/src/ui/screens/CombatScreen.lua @@ -35,7 +35,7 @@ function CombatScreen:initialize( playerFaction, savegame ) self.camera = Camera( mw, mh, tw, th ) self.userInterface = UserInterface( self.combatState, self.camera ) - self.overlayPainter = OverlayPainter.new( self.combatState, self.camera ) + self.overlayPainter = OverlayPainter( self.combatState, self.camera ) self.observations = {} self.observations[#self.observations + 1] = Messenger.observe( 'SWITCH_CHARACTERS', function( character ) From ddd7fe87ae96ecff35bfd09d5618ee2addd1691d Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Fri, 22 Dec 2017 13:00:47 +0100 Subject: [PATCH 173/178] Refactor ProjectileQueue --- src/characters/actions/RangedAttack.lua | 2 +- src/items/weapons/ProjectileManager.lua | 3 +- src/items/weapons/ProjectileQueue.lua | 245 +++++++++++++----------- 3 files changed, 132 insertions(+), 118 deletions(-) diff --git a/src/characters/actions/RangedAttack.lua b/src/characters/actions/RangedAttack.lua index 3cdede06..e43767ad 100644 --- a/src/characters/actions/RangedAttack.lua +++ b/src/characters/actions/RangedAttack.lua @@ -45,7 +45,7 @@ function RangedAttack:perform() return true end) - local package = ProjectileQueue.new( self.character, ax, ay, th ) + local package = ProjectileQueue( self.character, ax, ay, th ) ProjectileManager.register( package ) return true end diff --git a/src/items/weapons/ProjectileManager.lua b/src/items/weapons/ProjectileManager.lua index fa2daaad..af2e687e 100644 --- a/src/items/weapons/ProjectileManager.lua +++ b/src/items/weapons/ProjectileManager.lua @@ -177,8 +177,7 @@ end -- @param nqueue (ProjectileQueue) The ProjectileQueue to process. -- function ProjectileManager.register( nqueue ) - queue = nqueue; - queue:init(); + queue = nqueue end --- diff --git a/src/items/weapons/ProjectileQueue.lua b/src/items/weapons/ProjectileQueue.lua index 7d1f97e1..a5759b78 100644 --- a/src/items/weapons/ProjectileQueue.lua +++ b/src/items/weapons/ProjectileQueue.lua @@ -1,142 +1,157 @@ -local Projectile = require( 'src.items.weapons.Projectile' ); -local ProjectilePath = require( 'src.items.weapons.ProjectilePath' ); -local Queue = require( 'src.util.Queue' ); -local Messenger = require( 'src.Messenger' ); +--- +-- @module ProjectileQueue +-- + +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ + +local Class = require( 'lib.Middleclass' ) +local Projectile = require( 'src.items.weapons.Projectile' ) +local ProjectilePath = require( 'src.items.weapons.ProjectilePath' ) +local Queue = require( 'src.util.Queue' ) +local Messenger = require( 'src.Messenger' ) -- ------------------------------------------------ -- Module -- ------------------------------------------------ -local ProjectileQueue = {}; +local ProjectileQueue = Class( 'ProjectileQueue' ) -- ------------------------------------------------ --- Constructor +-- Private Methods -- ------------------------------------------------ --- --- Creates a new ProjectileQueue. --- @param character (Character) The character who started the attack. --- @tparam number tx The target's x-coordinate. --- @tparam number ty The target's y-coordinate. --- @tparam number th The target's height. --- @return (ProjectileQueue) A new instance of the ProjectileQueue class. +-- Enqueues projectiles based on the amount specified by the weapon's current +-- attack mode. +-- @tparam Weapon weapon The weapon to generate the projectiles for. +-- @tparam Queue queue The queue to fill. +-- @treturn number The amount of shots fired from the weapon. -- -function ProjectileQueue.new( character, tx, ty, th ) - local self = {}; - - local ammoQueue = Queue() - local shots; - local projectiles = {}; - local index = 0; - local timer = 0; - local weapon = character:getWeapon(); - - -- ------------------------------------------------ - -- Private Methods - -- ------------------------------------------------ - - --- - -- Removes a projectile from the queue and adds it to the table of active - -- projectiles. - -- - local function spawnProjectile() - local round = ammoQueue:dequeue(); - - local path = ProjectilePath.calculate( character, tx, ty, th, weapon, shots - ammoQueue:getSize() ) - local projectile = Projectile( character, path, weapon:getDamage(), round:getDamageType(), round:getEffects() ) - - -- Play sound and remove the round from the magazine. - Messenger.publish( 'SOUND_ATTACK', weapon ); - weapon:getMagazine():removeRound(); - - -- Spawn projectiles for the spread shot. - if round:getEffects():spreadsOnShot() then - for _ = 1, round:getEffects():getPellets() do - index = index + 1; - local spreadTiles = ProjectilePath.calculate( character, tx, ty, th, weapon, shots - ammoQueue:getSize() ) - projectiles[index] = Projectile( character, spreadTiles, weapon:getDamage(), round:getDamageType(), round:getEffects() ); - end - return; - end - - -- Spawn default projectile. - index = index + 1; - projectiles[index] = projectile; +local function calculateShots( weapon, queue ) + local shots = math.min( weapon:getMagazine():getNumberOfRounds(), weapon:getAttacks() ) + for i = 1, shots do + queue:enqueue( weapon:getMagazine():getRound( i )) end + return shots +end - -- ------------------------------------------------ - -- Public Methods - -- ------------------------------------------------ - - --- - -- Generates projectiles based on the weapons firing mode. The value is - -- limited by the amount of rounds in the weapon's magazine. For each - -- projectile an angle of deviation is calculated before it is placed in - -- the queue. - -- - function self:init() - shots = math.min( weapon:getMagazine():getNumberOfRounds(), weapon:getAttacks() ); - for i = 1, shots do - ammoQueue:enqueue( weapon:getMagazine():getRound( i )); +--- +-- Removes a projectile from the queue and adds it to the table of active +-- projectiles. +-- +local function spawnProjectile( queue, character, weapon, shots, projectiles, tx, ty, th, index ) + local round = queue:dequeue() + + local path = ProjectilePath.calculate( character, tx, ty, th, weapon, shots - queue:getSize() ) + local projectile = Projectile( character, path, weapon:getDamage(), round:getDamageType(), round:getEffects() ) + + -- Play sound and remove the round from the magazine. + Messenger.publish( 'SOUND_ATTACK', weapon ) + weapon:getMagazine():removeRound() + + -- Spawn projectiles for the spread shot. + if round:getEffects():spreadsOnShot() then + for _ = 1, round:getEffects():getPellets() do + index = index + 1 + local spreadTiles = ProjectilePath.calculate( character, tx, ty, th, weapon, shots - queue:getSize() ) + projectiles[index] = Projectile( character, spreadTiles, weapon:getDamage(), round:getDamageType(), round:getEffects() ) end + return end - --- - -- Spawns a new projectile after a certain delay defined by the weapon's - -- rate of fire. - -- - function self:update( dt ) - timer = timer - dt; - if timer <= 0 and not ammoQueue:isEmpty() then - spawnProjectile(); - timer = weapon:getFiringDelay(); - end - end + -- Spawn default projectile. + index = index + 1 + projectiles[index] = projectile + return index +end - --- - -- Removes a projectile from the table of active projectiles. - -- @param id (number) The id of the projectile to remove. - -- - function self:removeProjectile( id ) - projectiles[id] = nil; - end - -- ------------------------------------------------ - -- Getters - -- ------------------------------------------------ +-- ------------------------------------------------ +-- Public Methods +-- ------------------------------------------------ - --- - -- Gets the character this attack was performed by. - -- @return (Character) The character. - -- - function self:getCharacter() - return character; - end +--- +-- Creates a new ProjectileQueue. +-- +-- @tparam Character Character The character who started the attack. +-- @tparam number tx The target's x-coordinate. +-- @tparam number ty The target's y-coordinate. +-- @tparam number th The target's height. +-- @treturn ProjectileQueue A new instance of the ProjectileQueue class. +-- +function ProjectileQueue:initialize( character, tx, ty, th ) + self.character = character + self.targetX = tx + self.targetY = ty + self.targetHeight = th + + self.weapon = character:getWeapon() + + self.queue = Queue() + self.projectiles = {} + self.index = 0 + self.timer = 0 - --- - -- Gets the table of projectiles which are active on the map. - -- @return (table) A table containing the projectiles. - function self:getProjectiles() - return projectiles; + self.shots = calculateShots( self.weapon, self.queue ) +end + +--- +-- Spawns a new projectile after a certain delay defined by the weapon's +-- rate of fire. +-- @tparam number dt Time since the last update in seconds. +-- +function ProjectileQueue:update( dt ) + self.timer = self.timer - dt + if self.timer <= 0 and not self.queue:isEmpty() then + spawnProjectile( self.queue, self.character, self.weapon, self.shots, self.projectiles, self.targetX, self.targetY, self.targetHeight, self.index ) + self.timer = self.weapon:getFiringDelay() end +end - --- - -- Checks if this ProjectileQueue is done with all the projectiles. - -- @return (boolean) True if it is done. - -- - function self:isDone() - if not ammoQueue:isEmpty() then - return false; - end +--- +-- Removes a projectile from the table of active projectiles. +-- @tparam number id The id of the projectile to remove. +-- +function ProjectileQueue:removeProjectile( id ) + self.projectiles[id] = nil +end - local count = 0; - for _, _ in pairs( projectiles ) do - count = count + 1; - end - return count == 0; +-- ------------------------------------------------ +-- Getters +-- ------------------------------------------------ + +--- +-- Gets the character this attack was performed by. +-- @treturn Character The character. +-- +function ProjectileQueue:getCharacter() + return self.character +end + +--- +-- Gets the table of projectiles which are active on the map. +-- @treturn table A table containing the projectiles. +-- +function ProjectileQueue:getProjectiles() + return self.projectiles +end + +--- +-- Checks if this ProjectileQueue is done with all the projectiles. +-- @treturn boolean True if it is done. +-- +function ProjectileQueue:isDone() + if not self.queue:isEmpty() then + return false end - return self; + local count = 0 + for _, _ in pairs( self.projectiles ) do + count = count + 1 + end + return count == 0 end -return ProjectileQueue; +return ProjectileQueue From 879ec4136c9c56006225389c17fc95965358e12e Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sat, 23 Dec 2017 16:12:27 +0100 Subject: [PATCH 174/178] Refactor ThrownProjectileQueue --- src/characters/actions/ThrowingAttack.lua | 2 +- src/items/weapons/ThrownProjectileQueue.lua | 167 ++++++++++---------- 2 files changed, 83 insertions(+), 86 deletions(-) diff --git a/src/characters/actions/ThrowingAttack.lua b/src/characters/actions/ThrowingAttack.lua index 9f36f93d..6cc8bca7 100644 --- a/src/characters/actions/ThrowingAttack.lua +++ b/src/characters/actions/ThrowingAttack.lua @@ -40,7 +40,7 @@ function ThrowingAttack:perform() return true end) - local package = ThrownProjectileQueue.new( self.character, ax, ay, th ) + local package = ThrownProjectileQueue( self.character, ax, ay, th ) ProjectileManager.register( package ) return true end diff --git a/src/items/weapons/ThrownProjectileQueue.lua b/src/items/weapons/ThrownProjectileQueue.lua index 62645302..8a8406cf 100644 --- a/src/items/weapons/ThrownProjectileQueue.lua +++ b/src/items/weapons/ThrownProjectileQueue.lua @@ -1,12 +1,21 @@ -local Projectile = require( 'src.items.weapons.Projectile' ); -local ProjectilePath = require( 'src.items.weapons.ProjectilePath' ); -local Messenger = require( 'src.Messenger' ); +--- +-- @module ProjectileQueue +-- + +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ + +local Class = require( 'lib.Middleclass' ) +local Projectile = require( 'src.items.weapons.Projectile' ) +local ProjectilePath = require( 'src.items.weapons.ProjectilePath' ) +local Messenger = require( 'src.Messenger' ) -- ------------------------------------------------ -- Module -- ------------------------------------------------ -local ThrownProjectileQueue = {}; +local ThrownProjectileQueue = Class( 'ThrownProjectileQueue' ) -- ------------------------------------------------ -- Constants @@ -15,98 +24,86 @@ local ThrownProjectileQueue = {}; local WEAPON_TYPES = require( 'src.constants.WEAPON_TYPES' ) -- ------------------------------------------------ --- Constructor +-- Public Methods -- ------------------------------------------------ --- -- Creates a new ThrownProjectileQueue. --- @param character (Character) The character who started the attack. --- @tparam number tx The target's x-coordinate. --- @tparam number ty The target's y-coordinate. --- @tparam number th The target's height. --- @return (ThrownProjectileQueue) A new instance of the ThrownProjectileQueue class. -- -function ThrownProjectileQueue.new( character, tx, ty, th ) - local self = {}; - - local projectiles = {}; - local index = 0; - local weapon = character:getWeapon(); - - -- ------------------------------------------------ - -- Public Methods - -- ------------------------------------------------ - - --- - -- Generates projectiles based on the weapons firing mode. The value is - -- limited by the amount of rounds in the weapon's magazine. For each - -- projectile an angle of deviation is calculated before it is placed in - -- the queue. - -- - function self:init() - assert( weapon:getSubType() == WEAPON_TYPES.THROWN, 'Expected a weapon of type Thrown.' ); - - -- Thrown weapon is removed from the inventory. - local success = character:getEquipment():searchAndRemoveItem( weapon ); - assert( success, "Couldn't remove the item from the character's equipment." ); - - local tiles = ProjectilePath.calculate( character, tx, ty, th, weapon ) - local projectile = Projectile( character, tiles, weapon:getDamage(), weapon:getDamageType(), weapon:getEffects() ); - index = index + 1; - projectiles[index] = projectile; - - -- Play sound. - Messenger.publish( 'SOUND_ATTACK', weapon ); - end +-- @tparam Character Character The character who started the attack. +-- @tparam number tx The target's x-coordinate. +-- @tparam number ty The target's y-coordinate. +-- @tparam number th The target's height. +-- +function ThrownProjectileQueue:initialize( character, tx, ty, th ) + self.character = character - --- - -- Spawns a new projectile after a certain delay defined by the weapon's - -- rate of fire. - -- - function self:update() - return; - end + self.weapon = character:getWeapon() - --- - -- Removes a projectile from the table of active projectiles. - -- @param id (number) The id of the projectile to remove. - -- - function self:removeProjectile( id ) - projectiles[id] = nil; - end + assert( self.weapon:getSubType() == WEAPON_TYPES.THROWN, 'Expected a weapon of type Thrown.' ) - -- ------------------------------------------------ - -- Getters - -- ------------------------------------------------ + self.projectiles = {} + self.index = 0 - --- - -- Gets the character this attack was performed by. - -- @return (Character) The character. - -- - function self:getCharacter() - return character; - end + -- Thrown weapon is removed from the inventory. + local success = character:getEquipment():searchAndRemoveItem( self.weapon ) + assert( success, "Couldn't remove the item from the character's equipment." ) - --- - -- Gets the table of projectiles which are active on the map. - -- @return (table) A table containing the projectiles. - function self:getProjectiles() - return projectiles; - end + local tiles = ProjectilePath.calculate( character, tx, ty, th, self.weapon ) + local projectile = Projectile( character, tiles, self.weapon:getDamage(), self.weapon:getDamageType(), self.weapon:getEffects() ) + self.index = self.index + 1 + self.projectiles[self.index] = projectile - --- - -- Checks if this ThrownProjectileQueue is done with all the projectiles. - -- @return (boolean) True if it is done. - -- - function self:isDone() - local count = 0; - for _, _ in pairs( projectiles ) do - count = count + 1; - end - return count == 0; - end + -- Play sound. + Messenger.publish( 'SOUND_ATTACK', self.weapon ) +end + +--- +-- Spawns a new projectile after a certain delay defined by the weapon's +-- rate of fire. +-- +function ThrownProjectileQueue:update() + return +end - return self; +--- +-- Removes a projectile from the table of active projectiles. +-- @tparam number id The id of the projectile to remove. +-- +function ThrownProjectileQueue:removeProjectile( id ) + self.projectiles[id] = nil +end + +-- ------------------------------------------------ +-- Getters +-- ------------------------------------------------ + +--- +-- Gets the character this attack was performed by. +-- @treturn Character The character. +-- +function ThrownProjectileQueue:getCharacter() + return self.character +end + +--- +-- Gets the table of projectiles which are active on the map. +-- @treturn table A table containing the projectiles. +-- +function ThrownProjectileQueue:getProjectiles() + return self.projectiles +end + +--- +-- Checks if this ThrownProjectileQueue is done with all the projectiles. +-- @treturn boolean True if it is done. +-- +function ThrownProjectileQueue:isDone() + local count = 0 + for _, _ in pairs( self.projectiles ) do + count = count + 1 + end + return count == 0 end -return ThrownProjectileQueue; +return ThrownProjectileQueue From 6db3e079ef36d594b221a8058747c2f71e846cee Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sat, 23 Dec 2017 17:37:28 +0100 Subject: [PATCH 175/178] Remove manual flushing of LuaJIT cache Now that the closure based oop approach has been removed from the game the garbage collector seems to work correctly again and therefore the manual flushing of the LuaJIT cache is no longer necessary. --- src/ui/screens/MainMenu.lua | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/ui/screens/MainMenu.lua b/src/ui/screens/MainMenu.lua index dd4cb584..26ab2b1d 100644 --- a/src/ui/screens/MainMenu.lua +++ b/src/ui/screens/MainMenu.lua @@ -142,11 +142,6 @@ function MainMenu:initialize() self.footer = UICopyrightFooter() - -- Flush the LuaJIT cache to prevent memory leaks caused by cached - -- upvalues and closures. - -- @see https://github.com/LuaJIT/LuaJIT/issues/303 - jit.flush() - collectgarbage( 'collect' ) end From 1343b42f198891cd55298fd1f7ee2a44ef4f7527 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sun, 24 Dec 2017 01:24:47 +0100 Subject: [PATCH 176/178] Fix equipment drag and drop Closes #224. --- src/ui/elements/inventory/UIEquipmentList.lua | 2 +- .../elements/inventory/UIInventoryDragboard.lua | 17 ++++++++--------- src/ui/screens/InventoryScreen.lua | 8 ++++---- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/ui/elements/inventory/UIEquipmentList.lua b/src/ui/elements/inventory/UIEquipmentList.lua index db3fde8b..af6060d0 100644 --- a/src/ui/elements/inventory/UIEquipmentList.lua +++ b/src/ui/elements/inventory/UIEquipmentList.lua @@ -81,7 +81,7 @@ function UIEquipmentList:drag() end self:refresh() - return item, uiItem:getSlot() + return item, self.equipment, uiItem:getSlot() end end end diff --git a/src/ui/elements/inventory/UIInventoryDragboard.lua b/src/ui/elements/inventory/UIInventoryDragboard.lua index 558f97ba..9cfc6445 100644 --- a/src/ui/elements/inventory/UIInventoryDragboard.lua +++ b/src/ui/elements/inventory/UIInventoryDragboard.lua @@ -11,7 +11,7 @@ local TexturePacks = require( 'src.ui.texturepacks.TexturePacks' ) local Translator = require( 'src.util.Translator' ) local GridHelper = require( 'src.util.GridHelper' ) local UIBackground = require( 'src.ui.elements.UIBackground' ) -local EquipmentSlot = require( 'src.characters.body.EquipmentSlot' ) +local Equipment = require( 'src.characters.body.Equipment' ) local ItemStack = require( 'src.inventory.ItemStack' ) -- ------------------------------------------------ @@ -30,9 +30,9 @@ local ITEM_WIDTH = 20 -- Private Methods -- ------------------------------------------------ -local function returnItemToOrigin( item, origin ) - if origin:isInstanceOf( EquipmentSlot ) then - origin:addItem( item ) +local function returnItemToOrigin( item, origin, slot ) + if origin:isInstanceOf( Equipment ) then + origin:addItem( slot, item ) else origin:drop( item ); end @@ -84,21 +84,20 @@ function UIInventoryDragboard:draw( lists ) TexturePacks.resetColor() end -function UIInventoryDragboard:drag( item, origin ) - assert( item and origin, 'Missing parameters.' ) +function UIInventoryDragboard:drag( item, origin, slot ) love.mouse.setVisible( false ) - self.dragContext = { item = item, origin = origin } + self.dragContext = { item = item, origin = origin, slot = slot } end function UIInventoryDragboard:drop( target ) love.mouse.setVisible( true ) if not target then - returnItemToOrigin( self.dragContext.item, self.dragContext.origin ) + returnItemToOrigin( self.dragContext.item, self.dragContext.origin, self.dragContext.slot ) else local success = target:drop( self.dragContext.item, self.dragContext.origin ) if not success then - returnItemToOrigin( self.dragContext.item, self.dragContext.origin ) + returnItemToOrigin( self.dragContext.item, self.dragContext.origin, self.dragContext.slot ) end end diff --git a/src/ui/screens/InventoryScreen.lua b/src/ui/screens/InventoryScreen.lua index 02631c55..2fc3ca8c 100644 --- a/src/ui/screens/InventoryScreen.lua +++ b/src/ui/screens/InventoryScreen.lua @@ -260,7 +260,7 @@ local function drag( button, lists, dragboard, itemStats ) return end - local item, slot = list:drag( button == 2, love.keyboard.isDown( 'lshift' )) + local item, origin, slot = list:drag( button == 2, love.keyboard.isDown( 'lshift' )) -- Abort if there is nothing to drag here. if not item then @@ -270,9 +270,9 @@ local function drag( button, lists, dragboard, itemStats ) -- Display stats for the dragged item. itemStats:setItem( item ) - -- If we have an actual item slot we use it as the origin to - -- which the item is returned in case it can't be dropped anywhere. - dragboard:drag( item, slot or list ) + -- Add the item and list to the dragboard. If the item comes from the + -- equipment list we also pass the slot. + dragboard:drag( item, origin or list, slot ) -- If the dragged item is a container we need to refresh the inventory lists -- because it changes the inventory volumes. From c8b37d1d8d8f005efff1cce69cc6dcf5dc9ce571 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sun, 24 Dec 2017 02:18:03 +0100 Subject: [PATCH 177/178] Refactor Translator --- src/util/Translator.lua | 92 ++++++++++++++++++++++------------------- 1 file changed, 50 insertions(+), 42 deletions(-) diff --git a/src/util/Translator.lua b/src/util/Translator.lua index a838921e..01df67e6 100644 --- a/src/util/Translator.lua +++ b/src/util/Translator.lua @@ -1,27 +1,35 @@ -local Log = require( 'src.util.Log' ); +--- +-- @module Translator +-- + +-- ------------------------------------------------ +-- Required Modules +-- ------------------------------------------------ + +local Log = require( 'src.util.Log' ) -- ------------------------------------------------ -- Module -- ------------------------------------------------ -local Translator = {}; +local Translator = {} -- ------------------------------------------------ -- Constants -- ------------------------------------------------ -local MISSING_TRANSLATION_ERROR = 'Translation for locale %s and ID %s doesn\'t exist. Falling back to default locale.'; -local MISSING_ID_ERROR = 'TEXT_ERROR <%s>'; -local LOCALE_ERROR = 'The selected locale %s doesn\'t exist. Falling back to default locale.'; -local TEMPLATE_DIRECTORY = 'res/text/'; +local MISSING_TRANSLATION_ERROR = 'Translation for locale %s and ID %s doesn\'t exist. Falling back to default locale.' +local MISSING_ID_ERROR = 'TEXT_ERROR <%s>' +local LOCALE_ERROR = 'The selected locale %s doesn\'t exist. Falling back to default locale.' +local TEMPLATE_DIRECTORY = 'res/text/' -- ------------------------------------------------ -- Local Variables -- ------------------------------------------------ -local locales = {}; -local locale; -local defaultLocale; +local locales = {} +local locale +local defaultLocale -- ------------------------------------------------ -- Local Functions @@ -29,21 +37,21 @@ local defaultLocale; --- -- Loads the actual translation files and copies them into the locales table. --- @param path (string) The subfolder to load from. +-- @tparam string path The subfolder to load from. -- local function loadAdditionalText( path ) - local status, loaded = pcall( love.filesystem.load, path ); + local status, loaded = pcall( love.filesystem.load, path ) if not status then - Log.warn( 'Can not load translation file from ' .. path ); + Log.warn( 'Can not load translation file from ' .. path ) else - local template = loaded(); + local template = loaded() -- Load table or create a new one. - locales[template.identifier] = locales[template.identifier] or {}; + locales[template.identifier] = locales[template.identifier] or {} -- Copy translations to the main locale. for i, v in pairs( template.strings ) do - locales[template.identifier][i] = v; + locales[template.identifier][i] = v end end end @@ -51,20 +59,20 @@ end --- -- Searches the template directory for subfolders and tries to load the -- translation files from there. --- @param dir (string) The template directory to search through. +-- @tparam string dir The template directory to search through. -- local function load( dir ) - local subdirectories = love.filesystem.getDirectoryItems( dir ); + local subdirectories = love.filesystem.getDirectoryItems( dir ) for i, subdir in ipairs( subdirectories ) do - local path = dir .. subdir .. '/'; + local path = dir .. subdir .. '/' if love.filesystem.isDirectory( path ) then - local files = love.filesystem.getDirectoryItems( path ); + local files = love.filesystem.getDirectoryItems( path ) -- Loads all the other text files for this locale. for _, file in ipairs( files ) do - loadAdditionalText( path .. file ); + loadAdditionalText( path .. file ) end - Log.debug( string.format( ' %d. %s', i, subdir )); + Log.debug( string.format( ' %d. %s', i, subdir )) end end end @@ -76,17 +84,17 @@ end --- -- Initialises the Translator, by loading the translation files and setting the -- default locale. It produces an error if the default locale doesn't exist. --- @param nlocale (string) The identifier for the default locale (e.g. en_EN) +-- @tparam string nlocale The identifier for the default locale (e.g. en_EN) -- function Translator.init( nlocale ) - Log.debug( 'Load language files:' ); - load( TEMPLATE_DIRECTORY ); + Log.debug( 'Load language files:' ) + load( TEMPLATE_DIRECTORY ) -- Set the default locale and make it the active locale. -- The default locale will be used as a fallback. - assert( locales[nlocale], string.format( 'The selected locale %s doesn\'t exist.', nlocale )); - locale = nlocale; - defaultLocale = nlocale; + assert( locales[nlocale], string.format( 'The selected locale %s doesn\'t exist.', nlocale )) + locale = nlocale + defaultLocale = nlocale end -- ------------------------------------------------ @@ -95,35 +103,35 @@ end --- -- Gets the current locale. --- @return (string) The identifier of the current locale (e.g. en_EN). +-- @treturn string The identifier of the current locale (e.g. en_EN). -- function Translator.getLocale() - return locale; + return locale end --- -- Gets the text for the specified id from the active locale. -- If the id can't be found, the function tries to fall back to the default locale. --- @param id (string) The id of the text to get. --- @return (string) The identifier of the current locale (e.g. en_EN). +-- @tparam string id The id of the text to get. +-- @treturn string The identifier of the current locale (e.g. en_EN). -- function Translator.getText( id ) -- If the id can't be found in the current locale try to use the default one. if not locales[locale][id] then - Log.warn( string.format( MISSING_TRANSLATION_ERROR, locale, id ), 'Translator' ); + Log.warn( string.format( MISSING_TRANSLATION_ERROR, locale, id ), 'Translator' ) -- If the id also doesn't exist in the default locale return an error string. if not locales[defaultLocale][id] then - Log.warn( string.format( MISSING_ID_ERROR, id ), 'Translator' ); + Log.warn( string.format( MISSING_ID_ERROR, id ), 'Translator' ) locales[locale][id] = string.format( MISSING_ID_ERROR, id ) - return string.format( MISSING_ID_ERROR, id ); + return string.format( MISSING_ID_ERROR, id ) end locales[locale][id] = locales[defaultLocale][id] - return locales[defaultLocale][id]; + return locales[defaultLocale][id] end - return locales[locale][id]; + return locales[locale][id] end -- ------------------------------------------------ @@ -133,16 +141,16 @@ end --- -- Sets the locale. If the new locale doesn't exist it prints a warning to the -- console and falls back to the default locale. --- @param nlocale (string) The identifier for the new locale (e.g. en_EN) +-- @tparam string nlocale The identifier for the new locale (e.g. en_EN) -- function Translator.setLocale( nlocale ) if not locales[nlocale] then - Log.warn( string.format( LOCALE_ERROR, nlocale ), 'Translator' ); - locale = defaultLocale; - return; + Log.warn( string.format( LOCALE_ERROR, nlocale ), 'Translator' ) + locale = defaultLocale + return end - locale = nlocale; + locale = nlocale end -return Translator; +return Translator From f4fc8ac8205879f58d68c9b211087f039573c17f Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sun, 24 Dec 2017 12:23:37 +0100 Subject: [PATCH 178/178] Prepare version 0.13.0.1401 --- CHANGELOG.md | 35 +++++++++++++++++++++++++++++++++++ README.md | 2 +- version.lua | 6 +++--- 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a59a78f..6dd55135 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,38 @@ +# Version 0.13.0.1401 - 2017-12-24 + +## Additions +- Added connected sprites for walls and fences. +- Added custom item sprites and colors. +- Added versioning for the settings file. If the game detects an outdated file, it will replace it with the updated one. +- Added new section to the options screen which allows the reconfiguration of keybindings. +- Added key controls for panning the camera. +- Added option to activate mouse panning (deactivated by default). + +## Removals +- Removed map exploration. Instead of hiding unexplored tiles, the map is now fully visible to the player. Tiles which can't be seen by the player's characters are still drawn in greyscale. +- Removed restrictions for the pathfinding algorithm. Characters can now find a path from one side of the map to the other (but it might take a while). Performance improvements for the pathfinding are being worked on as well. +- Removed manual flushing of LuaJIT cache. This was necessary originally since LuaJIT had troubles freeing memory with the closure based approach to oop used before. Now that all classes are using a metatable based approach the garbage collector works as expected. + +## Fixes +- Fixed badly placed windows in small house prefab. +- Fixed prefab-editor being updated and drawn when menu is open. +- Fixed settings screen warning about unsaved changes even if none of the values had changed. +- Fixed faulty offset between sections in changelog screen. +- Fixed the heuristic of the pathfinding module for diagonal movement. +- Fixed volume calculation for backpacks dropped at invalid locations (e.g. outside of the inventory screen). + +## Other Changes +- Changed translator warnings so they are only logged once. +- Changed spawnpoints to ignore certain tiles (e.g. shallow water). +- Changed input dialog to only delete the full text the first time backspace is pressed. Every press after that will only remove a single character. +- Changed health screen to give better indication of a character's current health and status. +- Changed controls to be saved in and loaded from the settings file instead of being hardcoded. +- Changed pathfinding algorithm to prevent pathfinding to invalid target tiles +- Changed threshold for dying from bleeding. A creature now dies when its blood volume reaches 50%. + + + + # Version 0.12.2.1222 - 2017-11-29 ## Fixes diff --git a/README.md b/README.md index da76c01a..cd5eeaeb 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # On The Roadside -[![Version](https://img.shields.io/badge/Version-0.12.2.1222-blue.svg)](https://github.com/rm-code/on-the-roadside/releases/latest) +[![Version](https://img.shields.io/badge/Version-0.13.0.1401-blue.svg)](https://github.com/rm-code/on-the-roadside/releases/latest) [![LOVE](https://img.shields.io/badge/L%C3%96VE-0.10.2-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 5ac1f34f..b023f647 100644 --- a/version.lua +++ b/version.lua @@ -1,8 +1,8 @@ local version = { major = 0, - minor = 12, - patch = 2, - build = 1222, + minor = 13, + patch = 0, + build = 1401, } return string.format( "%d.%d.%d.%d", version.major, version.minor, version.patch, version.build );