From 75b5201294536c639908759d5ee686df439109d0 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Fri, 31 Aug 2018 21:19:53 +0200 Subject: [PATCH 01/11] Refactor ExplosionManager Turned ExplosionManager into a proper class to get rid of its global state and to remove the dependency on the Messenger module. See #267. --- src/CombatState.lua | 15 ++- src/characters/ai/SadisticAIDirector.lua | 5 +- src/items/weapons/ExplosionManager.lua | 155 +++++++++++------------ src/items/weapons/ProjectileManager.lua | 7 +- src/turnbased/states/ExecutionState.lua | 13 +- src/turnbased/states/PlanningState.lua | 13 +- src/ui/overlays/OverlayPainter.lua | 2 +- src/ui/overlays/ParticleLayer.lua | 11 +- 8 files changed, 112 insertions(+), 109 deletions(-) diff --git a/src/CombatState.lua b/src/CombatState.lua index 4aa275c6..89385bb9 100644 --- a/src/CombatState.lua +++ b/src/CombatState.lua @@ -83,13 +83,13 @@ function CombatState:initialize( playerFaction, savegame ) end) end) - self.stateManager = StateManager( self.states ) - self.stateManager:push( 'planning', self.factions ) + self.explosionManager = ExplosionManager( self.map ) + ProjectileManager.init( self.map, self.explosionManager ) - self.sadisticAIDirector = SadisticAIDirector( self.factions, self.stateManager ) + self.stateManager = StateManager( self.states ) + self.stateManager:push( 'planning', self.factions, self.explosionManager ) - ProjectileManager.init( self.map ) - ExplosionManager.init( self.map ) + self.sadisticAIDirector = SadisticAIDirector( self.factions, self.stateManager, self.explosionManager ) -- Register observations. self.map:observe( self ) @@ -139,7 +139,6 @@ end function CombatState:close() ProjectileManager.clear() - ExplosionManager.clear() end function CombatState:keypressed( _, scancode, _ ) @@ -160,6 +159,10 @@ function CombatState:getMap() return self.map end +function CombatState:getExplosionManager() + return self.explosionManager +end + function CombatState:getFactions() return self.factions end diff --git a/src/characters/ai/SadisticAIDirector.lua b/src/characters/ai/SadisticAIDirector.lua index 11e1fa7c..d4703aa6 100644 --- a/src/characters/ai/SadisticAIDirector.lua +++ b/src/characters/ai/SadisticAIDirector.lua @@ -29,9 +29,10 @@ end -- Public Methods -- ------------------------------------------------ -function SadisticAIDirector:initialize( factions, states ) +function SadisticAIDirector:initialize( factions, states, explosionManager ) self.factions = factions self.states = states + self.explosionManager = explosionManager end function SadisticAIDirector:update() @@ -49,7 +50,7 @@ function SadisticAIDirector:update() local success = tickBehaviorTree( tree, character, self.states, self.factions ) if success then - self.states:push( 'execution', self.factions, character ) + self.states:push( 'execution', self.factions, character, self.explosionManager ) return end diff --git a/src/items/weapons/ExplosionManager.lua b/src/items/weapons/ExplosionManager.lua index 067b07b8..7142f525 100644 --- a/src/items/weapons/ExplosionManager.lua +++ b/src/items/weapons/ExplosionManager.lua @@ -1,29 +1,27 @@ -local Bresenham = require( 'lib.Bresenham' ); -local Messenger = require( 'src.Messenger' ); -local SoundManager = require( 'src.SoundManager' ) -local Util = require( 'src.util.Util' ); +--- +-- @module ExplosionManager +-- -- ------------------------------------------------ --- Module +-- Required Modules -- ------------------------------------------------ -local ExplosionManager = {}; +local Bresenham = require( 'lib.Bresenham' ) +local SoundManager = require( 'src.SoundManager' ) +local Util = require( 'src.util.Util' ) +local Observable = require( 'src.util.Observable' ) -- ------------------------------------------------ --- Constants +-- Module -- ------------------------------------------------ -local DAMAGE_TYPES = require( 'src.constants.DAMAGE_TYPES' ); +local ExplosionManager = Observable:subclass( 'ExplosionManager' ) -- ------------------------------------------------ --- Private Variables +-- Constants -- ------------------------------------------------ -local map; -local explosionLayout; -local explosionIndex = 1; -local timer = 0; -local delay = 0.02; +local DAMAGE_TYPES = require( 'src.constants.DAMAGE_TYPES' ) -- ------------------------------------------------ -- Private Functions @@ -31,54 +29,54 @@ local delay = 0.02; --- -- Determines all tiles which are hit by the explosion. --- @param source (Tile) The origin of the explosion. --- @param list (table) A sequence containing all tiles in the explosion's radius. --- @param radius (radius) The maximum radius of the explosion. --- @return (table) A table containing sub-tables for each line. +-- @tparam Tile source The origin of the explosion. +-- @tparam table list A sequence containing all tiles in the explosion's radius. +-- @tparam number radius The maximum radius of the explosion. +-- @treturn table A table containing sub-tables for each line. -- -local function generateExplosionMap( source, list, radius ) - local sx, sy = source:getPosition(); - local queues = {}; +local function generateExplosionMap( map, source, list, radius ) + local sx, sy = source:getPosition() + local queues = {} -- Iterate over all tiles in the list. Get all tiles between the source of -- the explosion and each tile in the list and store them in a table. for _, tile in ipairs( list ) do - local queue = {}; + local queue = {} -- Randomize the radius to prevent all target tiles to be hit covered. This -- is done to prevent a perfectly circular explosion. - local length = love.math.random( radius ); + local length = love.math.random( radius ) if tile ~= source then - local tx, ty = tile:getPosition(); + local tx, ty = tile:getPosition() -- Cast a ray from the source of the explosion to the target tile. Bresenham.line( sx, sy, tx, ty, function( cx, cy, count ) - local target = map:getTileAt( cx, cy ); + local target = map:getTileAt( cx, cy ) -- Stop at impassable world objects that cover the whole tile. if target:hasWorldObject() and not target:getWorldObject():isDestructible() and target:getWorldObject():getHeight() == 100 then - return false; + return false end -- Stop at the maximum range of the explosion. if length == count then - return false; + return false end -- Store the target tile. - queue[#queue + 1] = target; - return true; + queue[#queue + 1] = target + return true end) end -- Store the table for this specific line. - queues[#queues + 1] = queue; + queues[#queues + 1] = queue end - return queues; + return queues end --- @@ -91,97 +89,90 @@ end -- @return (table) A table containing subtables for each step of the explosion. -- local function generateExplosionSteps( queues, damage, radius ) - local layout = {}; + local layout = {} for i = 1, radius do local steps = {} for _, queue in ipairs( queues ) do - local tile = queue[i]; + local tile = queue[i] if tile then - steps[tile] = damage; + steps[tile] = damage end end - layout[i] = steps; + layout[i] = steps end - return layout; + return layout end -- ------------------------------------------------ --- Public Functions +-- Public Methods -- ------------------------------------------------ +--- +-- Initialises the ExplosionManager. +-- @tparam Map map The map object. +-- +function ExplosionManager:initialize( map ) + Observable.initialize( self ) + + self.map = map + self.timer = 0 + self.delay = 0.02 +end + --- -- Updates the current explosion. -- -function ExplosionManager.update( dt ) - if not explosionLayout then - return; +function ExplosionManager:update( dt ) + if not self.explosionLayout then + return end - timer = timer - dt; + self.timer = self.timer - dt -- If the last step of the explosion has been reached delete the current -- explosion layout. - if not explosionLayout[explosionIndex] then - explosionLayout = nil; - return; + if not self.explosionLayout[self.explosionIndex] then + self.explosionLayout = nil + return end -- Advances the explosion and publishes a message about it. - if explosionLayout and timer <= 0 then + if self.explosionLayout and self.timer <= 0 then -- Notify anyone who cares. - Messenger.publish( 'EXPLOSION', explosionLayout[explosionIndex] ); + self:publish( 'EXPLOSION', self.explosionLayout[self.explosionIndex] ) SoundManager.play( 'sound_explode') -- Damage the hit tiles. - for tile, damage in pairs( explosionLayout[explosionIndex] ) do + for tile, damage in pairs( self.explosionLayout[self.explosionIndex] ) do tile:hit( damage, DAMAGE_TYPES.EXPLOSIVE ) end -- Advance the step index. - explosionIndex = explosionIndex + 1; - timer = delay; + self.explosionIndex = self.explosionIndex + 1 + self.timer = self.delay end end ---- --- Initialises the ExplosionManager. --- @param nmap (Map) The map object. --- -function ExplosionManager.init( nmap ) - map = nmap; -end - --- -- Registers and creates a new explosion. --- @param source (Tile) The explosion's source. --- @param damage (number) The explosion's base damage. --- @param radius (number) The explosion's radius. --- -function ExplosionManager.register( source, damage, radius ) - local list = Util.getTilesInCircle( map, source, radius ); - local queues = generateExplosionMap( source, list, radius ); - explosionLayout = generateExplosionSteps( queues, damage, radius ); - explosionIndex = 1; -end - ---- --- Remove any saved state. +-- @tparam Tile source The explosion's source. +-- @tparam number damage The explosion's base damage. +-- @tparam number radius The explosion's radius. -- -function ExplosionManager.clear() - map = nil; - explosionLayout = nil; - explosionIndex = 1; - timer = 0; - delay = 0.02; +function ExplosionManager:register( source, damage, radius ) + local list = Util.getTilesInCircle( self.map, source, radius ) + local queues = generateExplosionMap( self.map, source, list, radius ) + self.explosionLayout = generateExplosionSteps( queues, damage, radius ) + self.explosionIndex = 1 end --- -- Returns true if all explosions have been handled. --- @return (boolean) False if an explosion is active. +-- @treturn boolean False if an explosion is active. -- -function ExplosionManager.isDone() - if explosionLayout then - return false; +function ExplosionManager:isDone() + if self.explosionLayout then + return false end - return true; + return true end -return ExplosionManager; +return ExplosionManager diff --git a/src/items/weapons/ProjectileManager.lua b/src/items/weapons/ProjectileManager.lua index 6bf671b5..fab98ae7 100644 --- a/src/items/weapons/ProjectileManager.lua +++ b/src/items/weapons/ProjectileManager.lua @@ -1,6 +1,5 @@ local Log = require( 'src.util.Log' ); local Messenger = require( 'src.Messenger' ); -local ExplosionManager = require( 'src.items.weapons.ExplosionManager' ); local Util = require( 'src.util.Util' ) local Translator = require( 'src.util.Translator' ) @@ -22,6 +21,7 @@ local DAMAGE_TYPES = require( 'src.constants.DAMAGE_TYPES' ); local queue; local map; +local explosionManager -- ------------------------------------------------ -- Private Functions @@ -35,7 +35,7 @@ local map; -- local function hitTile( tile, projectile ) if projectile:getDamageType() == DAMAGE_TYPES.EXPLOSIVE then - ExplosionManager.register( tile, projectile:getDamage(), projectile:getEffects():getBlastRadius() ) + explosionManager:register( tile, projectile:getDamage(), projectile:getEffects():getBlastRadius() ) return end @@ -145,8 +145,9 @@ end -- Initialise the ProjectileManager. -- @param nmap (Map) The game's map. -- -function ProjectileManager.init( nmap ) +function ProjectileManager.init( nmap, nexplosionManager ) map = nmap; + explosionManager = nexplosionManager end --- diff --git a/src/turnbased/states/ExecutionState.lua b/src/turnbased/states/ExecutionState.lua index 8a97879e..ce811fa2 100644 --- a/src/turnbased/states/ExecutionState.lua +++ b/src/turnbased/states/ExecutionState.lua @@ -8,7 +8,6 @@ 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' ) -- ------------------------------------------------ @@ -34,10 +33,12 @@ function ExecutionState:initialize( stateManager ) self.actionTimer = 0 end -function ExecutionState:enter( factions, character ) - self.factions = factions +function ExecutionState:enter( factions, character, explosionManager ) + self.factions = factions self.character = character - self.delay = character:getFaction():isAIControlled() and AI_DELAY or PLAYER_DELAY + self.explosionManager = explosionManager + + self.delay = character:getFaction():isAIControlled() and AI_DELAY or PLAYER_DELAY end function ExecutionState:update( dt ) @@ -46,8 +47,8 @@ function ExecutionState:update( dt ) return end - if not ExplosionManager.isDone() then - ExplosionManager.update( dt ) + if not self.explosionManager:isDone() then + self.explosionManager:update( dt ) return end diff --git a/src/turnbased/states/PlanningState.lua b/src/turnbased/states/PlanningState.lua index f5dc6d5e..5f7a0950 100644 --- a/src/turnbased/states/PlanningState.lua +++ b/src/turnbased/states/PlanningState.lua @@ -71,7 +71,7 @@ local function handleInputSelectionActions( input, inputStateHandler, character return true end -local function handleOtherActions( input, stateManager, inputStateHandler, factions, character ) +local function handleOtherActions( input, stateManager, inputStateHandler, factions, character, explosionManager ) if input == 'next_character' then inputStateHandler:switch( 'movement' ) factions:getFaction():nextCharacter() @@ -90,7 +90,7 @@ local function handleOtherActions( input, stateManager, inputStateHandler, facti if input == 'open_inventory_screen' then character:enqueueAction( OpenInventory( character, character:getTile() )) - stateManager:push( 'execution', factions, character ) + stateManager:push( 'execution', factions, character, explosionManager ) return true elseif input == 'open_health_screen' then ScreenManager.push( 'playerInfo', character ) @@ -127,8 +127,9 @@ function PlanningState:initialize( stateManager ) self.inputStateHandler:switch( 'movement' ) end -function PlanningState:enter( factions ) +function PlanningState:enter( factions, explosionManager ) self.factions = factions + self.explosionManager = explosionManager end function PlanningState:update() @@ -148,7 +149,7 @@ function PlanningState:input( input ) end if handleCharacterActions( input, character ) then - self.stateManager:push( 'execution', self.factions, character ) + self.stateManager:push( 'execution', self.factions, character, self.explosionManager ) return end @@ -156,7 +157,7 @@ function PlanningState:input( input ) return end - if handleOtherActions( input, self.stateManager, self.inputStateHandler, self.factions, character ) then + if handleOtherActions( input, self.stateManager, self.inputStateHandler, self.factions, character, self.explosionManager ) then return end end @@ -176,7 +177,7 @@ function PlanningState:selectTile( tile, button ) -- Request actions to execute. local execute = self.inputStateHandler:getState():request( tile, character ) if execute then - self.stateManager:push( 'execution', self.factions, character ) + self.stateManager:push( 'execution', self.factions, character, self.explosionManager ) end end diff --git a/src/ui/overlays/OverlayPainter.lua b/src/ui/overlays/OverlayPainter.lua index 8dacef77..646ae0ab 100644 --- a/src/ui/overlays/OverlayPainter.lua +++ b/src/ui/overlays/OverlayPainter.lua @@ -93,7 +93,7 @@ function OverlayPainter:initialize( game, camera ) self.game = game self.camera = camera - self.particleLayer = ParticleLayer() + self.particleLayer = ParticleLayer( game:getExplosionManager() ) self.pulser = Pulser( 2, 0.2, 0.6 ) self.coneOverlay = ConeOverlay( self.game, self.pulser, self.camera ) self.pathOverlay = PathOverlay( self.game, self.pulser ) diff --git a/src/ui/overlays/ParticleLayer.lua b/src/ui/overlays/ParticleLayer.lua index d15118cd..06c6932d 100644 --- a/src/ui/overlays/ParticleLayer.lua +++ b/src/ui/overlays/ParticleLayer.lua @@ -34,7 +34,10 @@ end -- Public Methods -- ------------------------------------------------ -function ParticleLayer:initialize() +function ParticleLayer:initialize( explosionManager ) + self.explosionManager = explosionManager + self.explosionManager:observe( self ) + self.grid = {} self.particles = ObjectPool( Particle ) @@ -52,8 +55,10 @@ function ParticleLayer:initialize() end end end) +end - Messenger.observe( 'EXPLOSION', function( ... ) +function ParticleLayer:receive( event, ... ) + if event == 'EXPLOSION' then local generation = ... for tile, _ in pairs( generation ) do local r = 0.9 @@ -63,7 +68,7 @@ function ParticleLayer:initialize() local fade = math.max( 0.95, love.math.random( 2.5 )) addParticleEffect( self.grid, self.particles, tile:getX(), tile:getY(), r, g, b, a, fade ) end - end) + end end function ParticleLayer:update( dt ) From 7ce92112f82dc5abf79fdd04a4fa81d5098247f8 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Fri, 31 Aug 2018 22:03:32 +0200 Subject: [PATCH 02/11] Refactor ProjectileManager Turned ProjectileManager into a proper class to get rid of its global state and to remove the dependency on the Messenger module. See #267. --- src/CombatState.lua | 16 +- src/characters/actions/RangedAttack.lua | 8 +- src/characters/actions/ThrowingAttack.lua | 8 +- src/characters/ai/SadisticAIDirector.lua | 11 +- .../ai/behaviortree/leafs/BTAttackTarget.lua | 4 +- .../behaviortree/leafs/BTThrowingAttack.lua | 4 +- src/items/weapons/ExplosionManager.lua | 21 +-- src/items/weapons/ProjectileManager.lua | 146 +++++++++--------- src/turnbased/helpers/AttackInput.lua | 6 +- src/turnbased/states/ExecutionState.lua | 8 +- src/turnbased/states/PlanningState.lua | 15 +- src/ui/overlays/OverlayPainter.lua | 2 +- src/ui/overlays/ParticleLayer.lua | 36 +++-- 13 files changed, 145 insertions(+), 140 deletions(-) diff --git a/src/CombatState.lua b/src/CombatState.lua index 89385bb9..c81fc661 100644 --- a/src/CombatState.lua +++ b/src/CombatState.lua @@ -84,12 +84,14 @@ function CombatState:initialize( playerFaction, savegame ) end) self.explosionManager = ExplosionManager( self.map ) - ProjectileManager.init( self.map, self.explosionManager ) + + self.projectileManager = ProjectileManager( self.map ) + self.projectileManager:observe( self.explosionManager ) self.stateManager = StateManager( self.states ) - self.stateManager:push( 'planning', self.factions, self.explosionManager ) + self.stateManager:push( 'planning', self.factions, self.explosionManager, self.projectileManager ) - self.sadisticAIDirector = SadisticAIDirector( self.factions, self.stateManager, self.explosionManager ) + self.sadisticAIDirector = SadisticAIDirector( self.factions, self.stateManager, self.explosionManager, self.projectileManager ) -- Register observations. self.map:observe( self ) @@ -137,10 +139,6 @@ function CombatState:serialize() return t end -function CombatState:close() - ProjectileManager.clear() -end - function CombatState:keypressed( _, scancode, _ ) if self.factions:getFaction():isAIControlled() or self.stateManager:blocksInput() then return @@ -163,6 +161,10 @@ function CombatState:getExplosionManager() return self.explosionManager end +function CombatState:getProjectileManager() + return self.projectileManager +end + function CombatState:getFactions() return self.factions end diff --git a/src/characters/actions/RangedAttack.lua b/src/characters/actions/RangedAttack.lua index e43767ad..9e98fece 100644 --- a/src/characters/actions/RangedAttack.lua +++ b/src/characters/actions/RangedAttack.lua @@ -7,7 +7,6 @@ -- ------------------------------------------------ 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' ) @@ -21,8 +20,10 @@ local RangedAttack = Action:subclass( 'RangedAttack' ) -- Public Methods -- ------------------------------------------------ -function RangedAttack:initialize( character, target ) +function RangedAttack:initialize( character, target, projectileManager ) Action.initialize( self, character, target, character:getWeapon():getAttackCost() ) + + self.projectileManager = projectileManager end function RangedAttack:perform() @@ -45,8 +46,7 @@ function RangedAttack:perform() return true end) - local package = ProjectileQueue( self.character, ax, ay, th ) - ProjectileManager.register( package ) + self.projectileManager:register( ProjectileQueue( self.character, ax, ay, th )) return true end diff --git a/src/characters/actions/ThrowingAttack.lua b/src/characters/actions/ThrowingAttack.lua index 6cc8bca7..fc4b0b90 100644 --- a/src/characters/actions/ThrowingAttack.lua +++ b/src/characters/actions/ThrowingAttack.lua @@ -7,7 +7,6 @@ -- ------------------------------------------------ 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' ) @@ -21,8 +20,10 @@ local ThrowingAttack = Action:subclass( 'ThrowingAttack' ) -- Public Methods -- ------------------------------------------------ -function ThrowingAttack:initialize( character, target ) +function ThrowingAttack:initialize( character, target, projectileManager ) Action.initialize( self, character, target, character:getWeapon():getAttackCost() ) + + self.projectileManager = projectileManager end function ThrowingAttack:perform() @@ -40,8 +41,7 @@ function ThrowingAttack:perform() return true end) - local package = ThrownProjectileQueue( self.character, ax, ay, th ) - ProjectileManager.register( package ) + self.projectileManager:register( ThrownProjectileQueue( self.character, ax, ay, th )) return true end diff --git a/src/characters/ai/SadisticAIDirector.lua b/src/characters/ai/SadisticAIDirector.lua index d4703aa6..b6a6b7ac 100644 --- a/src/characters/ai/SadisticAIDirector.lua +++ b/src/characters/ai/SadisticAIDirector.lua @@ -20,19 +20,20 @@ local SadisticAIDirector = Class( 'SadisticAIDirector' ) -- Private Methods -- ------------------------------------------------ -local function tickBehaviorTree( tree, character, states, factions ) +local function tickBehaviorTree( tree, character, states, factions, projectileManager ) Log.debug( "Tick BehaviorTree for " .. tostring( character ), 'SadisticAIDirector' ) - return tree:traverse( {}, character, states, factions ) + return tree:traverse( {}, character, states, factions, projectileManager ) end -- ------------------------------------------------ -- Public Methods -- ------------------------------------------------ -function SadisticAIDirector:initialize( factions, states, explosionManager ) +function SadisticAIDirector:initialize( factions, states, explosionManager, projectileManager ) self.factions = factions self.states = states self.explosionManager = explosionManager + self.projectileManager = projectileManager end function SadisticAIDirector:update() @@ -48,9 +49,9 @@ function SadisticAIDirector:update() Log.debug( 'Select next character for this turn', 'SadisticAIDirector' ) local character = faction:nextCharacterForTurn() - local success = tickBehaviorTree( tree, character, self.states, self.factions ) + local success = tickBehaviorTree( tree, character, self.states, self.factions, self.projectileManager ) if success then - self.states:push( 'execution', self.factions, character, self.explosionManager ) + self.states:push( 'execution', self.factions, character, self.explosionManager, self.projectileManager ) return end diff --git a/src/characters/ai/behaviortree/leafs/BTAttackTarget.lua b/src/characters/ai/behaviortree/leafs/BTAttackTarget.lua index cccb2357..8c3ac52e 100644 --- a/src/characters/ai/behaviortree/leafs/BTAttackTarget.lua +++ b/src/characters/ai/behaviortree/leafs/BTAttackTarget.lua @@ -21,9 +21,9 @@ local BTAttackTarget = BTLeaf:subclass( 'BTAttackTarget' ) -- ------------------------------------------------ function BTAttackTarget:traverse( ... ) - local blackboard, character = ... + local blackboard, character, _, _, projectileManager = ... - local success = character:enqueueAction( RangedAttack( character, blackboard.target )) + local success = character:enqueueAction( RangedAttack( character, blackboard.target, projectileManager )) if success then Log.debug( 'Character attacks target', 'BTAttackTarget' ) return true diff --git a/src/characters/ai/behaviortree/leafs/BTThrowingAttack.lua b/src/characters/ai/behaviortree/leafs/BTThrowingAttack.lua index 64720e46..b86c78dd 100644 --- a/src/characters/ai/behaviortree/leafs/BTThrowingAttack.lua +++ b/src/characters/ai/behaviortree/leafs/BTThrowingAttack.lua @@ -21,9 +21,9 @@ local BTThrowingAttack = BTLeaf:subclass( 'BTThrowingAttack' ) -- ------------------------------------------------ function BTThrowingAttack:traverse( ... ) - local blackboard, character = ... + local blackboard, character, _, _, projectileManager = ... - local success = character:enqueueAction( ThrowingAttack( character, blackboard.target )) + local success = character:enqueueAction( ThrowingAttack( character, blackboard.target, projectileManager )) if success then -- Store weapon id for the rearm action. blackboard.weaponID = character:getWeapon():getID() diff --git a/src/items/weapons/ExplosionManager.lua b/src/items/weapons/ExplosionManager.lua index 7142f525..31ade5eb 100644 --- a/src/items/weapons/ExplosionManager.lua +++ b/src/items/weapons/ExplosionManager.lua @@ -152,16 +152,19 @@ function ExplosionManager:update( dt ) end --- --- Registers and creates a new explosion. --- @tparam Tile source The explosion's source. --- @tparam number damage The explosion's base damage. --- @tparam number radius The explosion's radius. +-- Receives events. +-- @tparam string event The received event. +-- @tparam varargs ... Variable arguments. -- -function ExplosionManager:register( source, damage, radius ) - local list = Util.getTilesInCircle( self.map, source, radius ) - local queues = generateExplosionMap( self.map, source, list, radius ) - self.explosionLayout = generateExplosionSteps( queues, damage, radius ) - self.explosionIndex = 1 +function ExplosionManager:receive( event, ... ) + if event == 'CREATE_EXPLOSION' then + local source, damage, radius = ... + + local list = Util.getTilesInCircle( self.map, source, radius ) + local queues = generateExplosionMap( self.map, source, list, radius ) + self.explosionLayout = generateExplosionSteps( queues, damage, radius ) + self.explosionIndex = 1 + end end --- diff --git a/src/items/weapons/ProjectileManager.lua b/src/items/weapons/ProjectileManager.lua index fab98ae7..a7c06a01 100644 --- a/src/items/weapons/ProjectileManager.lua +++ b/src/items/weapons/ProjectileManager.lua @@ -1,41 +1,40 @@ -local Log = require( 'src.util.Log' ); -local Messenger = require( 'src.Messenger' ); -local Util = require( 'src.util.Util' ) -local Translator = require( 'src.util.Translator' ) +--- +-- @module ProjectileManager +-- -- ------------------------------------------------ --- Module +-- Required Modules -- ------------------------------------------------ -local ProjectileManager = {}; +local Observable = require( 'src.util.Observable' ) +local Log = require( 'src.util.Log' ) +local Util = require( 'src.util.Util' ) +local Translator = require( 'src.util.Translator' ) -- ------------------------------------------------ --- Constants +-- Module -- ------------------------------------------------ -local DAMAGE_TYPES = require( 'src.constants.DAMAGE_TYPES' ); +local ProjectileManager = Observable:subclass( 'ProjectileManager' ) -- ------------------------------------------------ --- Private Variables +-- Constants -- ------------------------------------------------ -local queue; -local map; -local explosionManager +local DAMAGE_TYPES = require( 'src.constants.DAMAGE_TYPES' ) -- ------------------------------------------------ -- Private Functions -- ------------------------------------------------ --- --- Removes a projectile from the world and hits a tile with the projectile --- damage. --- @param tile (Tile) The tile to hit. --- @param projectile (Projectile) The projectile to remove. +-- Removes a projectile from the world and hits a tile with the projectile damage. +-- @tparam Tile tile The tile to hit. +-- @tparam Projectile projectile The projectile to remove. -- -local function hitTile( tile, projectile ) +local function hitTile( self, tile, projectile ) if projectile:getDamageType() == DAMAGE_TYPES.EXPLOSIVE then - explosionManager:register( tile, projectile:getDamage(), projectile:getEffects():getBlastRadius() ) + self:publish( 'CREATE_EXPLOSION', tile, projectile:getDamage(), projectile:getEffects():getBlastRadius() ) return end @@ -44,32 +43,35 @@ end --- -- Reduces the energy of the projectile with slightly randomized values. --- @params energy (number) The current energy. --- @params reduction (number) The reduction that should be applied. --- @return (number) The modified energy value. +-- @tparam number energy The current energy. +-- @tparam number reduction The reduction that should be applied. +-- @treturn number The modified energy value. -- local function reduceProjectileEnergy( energy, reduction ) - reduction = love.math.random( reduction - 10, reduction + 10 ); + reduction = love.math.random( reduction - 10, reduction + 10 ) reduction = Util.clamp( 1, reduction, 100 ) - return energy - reduction; + return energy - reduction end --- +-- Hits a character. +-- @tparam Tile tile The tile to hit. +-- @tparam Projectile projectile The projectile to remove. -- -local function hitCharacter( tile, projectile ) +local function hitCharacter( self, tile, projectile ) Log.debug( 'Projectile hit character', 'ProjectileManager' ) - hitTile( tile, projectile ) + hitTile( self, tile, projectile ) return true end --- -- Handles how to proceed if the projectile hits a world object. --- @param projectile (Projectile) The projectile to handle. --- @param tile (Tile) The tile to check. --- @param worldObject (WorldObject) The world object to check. --- @treturn boolean Wether to remove the projectile or not. +-- @tparam Projectile projectile The projectile to handle. +-- @tparam Tile tile The tile to check. +-- @tparam WorldObject worldObject The world object to check. +-- @treturn boolean Wether to remove the projectile or not. -- -local function hitWorldObject( projectile, tile, worldObject ) +local function hitWorldObject( self, projectile, tile, worldObject ) if projectile:getHeight() > worldObject:getHeight() then Log.debug( 'Projectile flies over world object', 'ProjectileManager' ) return false @@ -83,35 +85,36 @@ local function hitWorldObject( projectile, tile, worldObject ) -- indestructible world object. This needs to be done to make sure explosions -- occur on the right side of the world object. if projectile:getDamageType() == DAMAGE_TYPES.EXPLOSIVE then - tile = projectile:getPreviousTile(); + tile = projectile:getPreviousTile() end tile:publish( 'MESSAGE_LOG_EVENT', tile, string.format( Translator.getText( 'msg_hit_indestructible_worldobject' ), Translator.getText( worldObject:getID() )), 'INFO' ) - hitTile( tile, projectile ) + hitTile( self, tile, projectile ) return true end Log.debug( 'Projectile hit destructible world object', 'ProjectileManager' ) -- Projectiles passing through world objects lose some of their energy. - local energy = reduceProjectileEnergy( projectile:getEnergy(), worldObject:getEnergyReduction() ); - projectile:setEnergy( energy ); + local energy = reduceProjectileEnergy( projectile:getEnergy(), worldObject:getEnergyReduction() ) + projectile:setEnergy( energy ) tile:publish( 'MESSAGE_LOG_EVENT', tile, string.format( Translator.getText( 'msg_hit_worldobject' ), Translator.getText( worldObject:getID() ), projectile:getDamage() ), 'INFO' ) -- Apply the damage to the tile and only remove it if the energy is 0. - hitTile( tile, projectile ) + hitTile( self, tile, projectile ) return energy <= 0 end --- -- Checks if the projectile has hit any characters or worldobjects. --- @param index (number) The id of the projectile which will be used for removing it. --- @param projectile (Projectile) The projectile to handle. --- @param tile (Tile) The tile to check. --- @param character (Character) The character who fired this projectile. +-- @tparam ProjectileQueue queue The ProjectileQueue to process. +-- @tparam number index The id of the projectile which will be used for removing it. +-- @tparam Projectile projectile The projectile to handle. +-- @tparam Tile tile The tile to check. +-- @tparam Character character The character who fired this projectile. -- -local function checkForHits( index, projectile, tile ) +local function checkForHits( self, queue, index, projectile, tile ) -- Stop movement and remove the projectile if it has reached the map border. if not tile then queue:removeProjectile( index ) @@ -121,14 +124,14 @@ local function checkForHits( index, projectile, tile ) local remove = false if tile:hasCharacter() then - remove = hitCharacter( tile, projectile ) + remove = hitCharacter( self, tile, projectile ) elseif tile:hasWorldObject() then - remove = hitWorldObject( projectile, tile, tile:getWorldObject() ) + remove = hitWorldObject( self, projectile, tile, tile:getWorldObject() ) end if not remove and projectile:hasReachedTarget() then Log.debug( 'Projectile reached target tile', 'ProjectileManager' ) - hitTile( tile, projectile ) + hitTile( self, tile, projectile ) remove = true end @@ -143,67 +146,60 @@ end --- -- Initialise the ProjectileManager. --- @param nmap (Map) The game's map. +-- @tparam Map map The game's map. -- -function ProjectileManager.init( nmap, nexplosionManager ) - map = nmap; - explosionManager = nexplosionManager +function ProjectileManager:initialize( map ) + Observable.initialize( self ) + + self.map = map end --- -- Updates the ProjectileManager which handles a ProjectileQueue for spawning -- projectiles and the interaction of each projectile with the game world. --- @param dt (number) The time since the last frame. +-- @tparam number dt The time since the last frame. -- -function ProjectileManager.update( dt ) - if not queue then - return; +function ProjectileManager:update( dt ) + if not self.queue then + return end -- Updates the time of the projectile queue which handles spawning of -- new projectiles. - queue:update( dt ); + self.queue:update( dt ) -- Update the projectiles currently in the game world. - for i, projectile in pairs( queue:getProjectiles() ) do - projectile:update( dt ); + for i, projectile in pairs( self.queue:getProjectiles() ) do + projectile:update( dt ) -- If the projectile has moved notify the world and check if it has -- hit anything. - if projectile:hasMoved( map ) then - projectile:updateTile( map ); - Messenger.publish( 'PROJECTILE_MOVED', projectile ); + if projectile:hasMoved( self.map ) then + projectile:updateTile( self.map ) + self:publish( 'PROJECTILE_MOVED', projectile ) - checkForHits( i, projectile, projectile:getTile() ) + checkForHits( self, self.queue, i, projectile, projectile:getTile() ) end end end --- -- Registers a new projectile queue. --- @param nqueue (ProjectileQueue) The ProjectileQueue to process. --- -function ProjectileManager.register( nqueue ) - queue = nqueue -end - ---- --- Remove any saved state. +-- @tparam ProjectileQueue queue The ProjectileQueue to process. -- -function ProjectileManager.clear() - queue = nil; - map = nil; +function ProjectileManager:register( queue ) + self.queue = queue end --- -- Returns true if there isn't a queue or if the current queue has been processed. --- @return (boolean) True if the projectile manager has processed the queue. +-- @treturn boolean True if the projectile manager has processed the queue. -- -function ProjectileManager.isDone() - if not queue then - return true; +function ProjectileManager:isDone() + if not self.queue then + return true end - return queue:isDone(); + return self.queue:isDone() end -return ProjectileManager; +return ProjectileManager diff --git a/src/turnbased/helpers/AttackInput.lua b/src/turnbased/helpers/AttackInput.lua index 36489607..0ee9e22a 100644 --- a/src/turnbased/helpers/AttackInput.lua +++ b/src/turnbased/helpers/AttackInput.lua @@ -34,7 +34,7 @@ local WEAPON_TYPES = require( 'src.constants.WEAPON_TYPES' ) -- @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 ) +function AttackInput:request( target, character, projectileManager ) -- Prevent characters from attacking themselves. if target == character:getTile() then return false @@ -54,13 +54,13 @@ function AttackInput:request( target, character ) -- Handle Thrown weapons. if weapon:getSubType() == WEAPON_TYPES.THROWN then - character:enqueueAction( ThrowingAttack( character, target )) + character:enqueueAction( ThrowingAttack( character, target, projectileManager )) character:enqueueAction( Rearm( character, weapon:getID() )) end -- Handle Ranged weapons. if weapon:getSubType() == WEAPON_TYPES.RANGED then - character:enqueueAction( RangedAttack( character, target )) + character:enqueueAction( RangedAttack( character, target, projectileManager )) end return true diff --git a/src/turnbased/states/ExecutionState.lua b/src/turnbased/states/ExecutionState.lua index ce811fa2..e357fbcb 100644 --- a/src/turnbased/states/ExecutionState.lua +++ b/src/turnbased/states/ExecutionState.lua @@ -7,7 +7,6 @@ -- ------------------------------------------------ local Class = require( 'lib.Middleclass' ) -local ProjectileManager = require( 'src.items.weapons.ProjectileManager' ) local Log = require( 'src.util.Log' ) -- ------------------------------------------------ @@ -33,17 +32,18 @@ function ExecutionState:initialize( stateManager ) self.actionTimer = 0 end -function ExecutionState:enter( factions, character, explosionManager ) +function ExecutionState:enter( factions, character, explosionManager, projectileManager ) self.factions = factions self.character = character self.explosionManager = explosionManager + self.projectileManager = projectileManager self.delay = character:getFaction():isAIControlled() and AI_DELAY or PLAYER_DELAY end function ExecutionState:update( dt ) - if not ProjectileManager.isDone() then - ProjectileManager.update( dt ) + if not self.projectileManager:isDone() then + self.projectileManager:update( dt ) return end diff --git a/src/turnbased/states/PlanningState.lua b/src/turnbased/states/PlanningState.lua index 5f7a0950..e2bf45bd 100644 --- a/src/turnbased/states/PlanningState.lua +++ b/src/turnbased/states/PlanningState.lua @@ -71,7 +71,7 @@ local function handleInputSelectionActions( input, inputStateHandler, character return true end -local function handleOtherActions( input, stateManager, inputStateHandler, factions, character, explosionManager ) +local function handleOtherActions( input, stateManager, inputStateHandler, factions, character, explosionManager, projectileManager ) if input == 'next_character' then inputStateHandler:switch( 'movement' ) factions:getFaction():nextCharacter() @@ -90,7 +90,7 @@ local function handleOtherActions( input, stateManager, inputStateHandler, facti if input == 'open_inventory_screen' then character:enqueueAction( OpenInventory( character, character:getTile() )) - stateManager:push( 'execution', factions, character, explosionManager ) + stateManager:push( 'execution', factions, character, explosionManager, projectileManager ) return true elseif input == 'open_health_screen' then ScreenManager.push( 'playerInfo', character ) @@ -127,9 +127,10 @@ function PlanningState:initialize( stateManager ) self.inputStateHandler:switch( 'movement' ) end -function PlanningState:enter( factions, explosionManager ) +function PlanningState:enter( factions, explosionManager, projectileManager ) self.factions = factions self.explosionManager = explosionManager + self.projectileManager = projectileManager end function PlanningState:update() @@ -149,7 +150,7 @@ function PlanningState:input( input ) end if handleCharacterActions( input, character ) then - self.stateManager:push( 'execution', self.factions, character, self.explosionManager ) + self.stateManager:push( 'execution', self.factions, character, self.explosionManager, self.projectileManager ) return end @@ -157,7 +158,7 @@ function PlanningState:input( input ) return end - if handleOtherActions( input, self.stateManager, self.inputStateHandler, self.factions, character, self.explosionManager ) then + if handleOtherActions( input, self.stateManager, self.inputStateHandler, self.factions, character, self.explosionManager, self.projectileManager ) then return end end @@ -175,9 +176,9 @@ function PlanningState:selectTile( tile, button ) end -- Request actions to execute. - local execute = self.inputStateHandler:getState():request( tile, character ) + local execute = self.inputStateHandler:getState():request( tile, character, self.projectileManager ) if execute then - self.stateManager:push( 'execution', self.factions, character, self.explosionManager ) + self.stateManager:push( 'execution', self.factions, character, self.explosionManager, self.projectileManager ) end end diff --git a/src/ui/overlays/OverlayPainter.lua b/src/ui/overlays/OverlayPainter.lua index 646ae0ab..d2d710c0 100644 --- a/src/ui/overlays/OverlayPainter.lua +++ b/src/ui/overlays/OverlayPainter.lua @@ -93,7 +93,7 @@ function OverlayPainter:initialize( game, camera ) self.game = game self.camera = camera - self.particleLayer = ParticleLayer( game:getExplosionManager() ) + self.particleLayer = ParticleLayer( game:getExplosionManager(), game:getProjectileManager() ) self.pulser = Pulser( 2, 0.2, 0.6 ) self.coneOverlay = ConeOverlay( self.game, self.pulser, self.camera ) self.pathOverlay = PathOverlay( self.game, self.pulser ) diff --git a/src/ui/overlays/ParticleLayer.lua b/src/ui/overlays/ParticleLayer.lua index 06c6932d..53a6a1a3 100644 --- a/src/ui/overlays/ParticleLayer.lua +++ b/src/ui/overlays/ParticleLayer.lua @@ -7,7 +7,6 @@ -- ------------------------------------------------ local Class = require( 'lib.Middleclass' ) -local Messenger = require( 'src.Messenger' ) local Particle = require( 'src.ui.overlays.Particle' ) local ObjectPool = require( 'src.util.ObjectPool' ) @@ -34,27 +33,15 @@ end -- Public Methods -- ------------------------------------------------ -function ParticleLayer:initialize( explosionManager ) +function ParticleLayer:initialize( explosionManager, projectileManager ) self.explosionManager = explosionManager self.explosionManager:observe( self ) + self.projectileManager = projectileManager + self.projectileManager:observe( self ) + self.grid = {} self.particles = ObjectPool( Particle ) - - Messenger.observe( 'PROJECTILE_MOVED', function( ... ) - local projectile = ... - local tile = projectile:getTile() - if tile then - if projectile:getEffects():hasCustomSprite() then - addParticleEffect( self.grid, self.particles, tile:getX(), tile:getY(), 1.0, 1.0, 1.0, 1.0, 2.5, projectile:getEffects():getCustomSprite() ) - elseif projectile:getEffects():isExplosive() then - local col = love.math.random( 0.59, 1.0 ) - addParticleEffect( self.grid, self.particles, tile:getX(), tile:getY(), col, col, col, love.math.random( 0.59, 1.0 ), 1.0 ) - else - addParticleEffect( self.grid, self.particles, tile:getX(), tile:getY(), 0.87450, 0.44313, 0.14901, 0.78, 0.5 ) - end - end - end) end function ParticleLayer:receive( event, ... ) @@ -68,6 +55,21 @@ function ParticleLayer:receive( event, ... ) local fade = math.max( 0.95, love.math.random( 2.5 )) addParticleEffect( self.grid, self.particles, tile:getX(), tile:getY(), r, g, b, a, fade ) end + return + end + if event == 'PROJECTILE_MOVED' then + local projectile = ... + local tile = projectile:getTile() + if tile then + if projectile:getEffects():hasCustomSprite() then + addParticleEffect( self.grid, self.particles, tile:getX(), tile:getY(), 1.0, 1.0, 1.0, 1.0, 2.5, projectile:getEffects():getCustomSprite() ) + elseif projectile:getEffects():isExplosive() then + local col = love.math.random( 0.59, 1.0 ) + addParticleEffect( self.grid, self.particles, tile:getX(), tile:getY(), col, col, col, love.math.random( 0.59, 1.0 ), 1.0 ) + else + addParticleEffect( self.grid, self.particles, tile:getX(), tile:getY(), 0.87450, 0.44313, 0.14901, 0.78, 0.5 ) + end + end end end From fd7e9002c025a269ba3515268b5eb2fb8fcc055b Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Fri, 31 Aug 2018 22:04:49 +0200 Subject: [PATCH 03/11] Remove unused Messenger module --- src/Messenger.lua | 55 ----------------------------------------------- 1 file changed, 55 deletions(-) delete mode 100644 src/Messenger.lua diff --git a/src/Messenger.lua b/src/Messenger.lua deleted file mode 100644 index 79b3a9ce..00000000 --- a/src/Messenger.lua +++ /dev/null @@ -1,55 +0,0 @@ ---- --- @module Messenger --- - --- ------------------------------------------------ --- Module --- ------------------------------------------------ - -local Messenger = {} - --- ------------------------------------------------ --- Local Variables --- ------------------------------------------------ - -local subscriptions = {} -local index = 0 - --- ------------------------------------------------ --- Public Functions --- ------------------------------------------------ - ---- --- Publishes a message to all subscribers. --- @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( ... ) - end - end -end - ---- --- Registers a callback belonging to a certain subscriber. --- @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 -end - ---- --- Removes a subscription based on its index. --- @tparam number nindex The index of the subscription to remove. --- -function Messenger.remove( nindex ) - subscriptions[nindex] = nil -end - -return Messenger From 8d61ea33dfaa9f355337c104e8f22f0e32cbb0b3 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Fri, 31 Aug 2018 22:19:50 +0200 Subject: [PATCH 04/11] Fix camera focusing on the wrong character Characters were deactivated in the wrong order when switching to a previous character. This caused the camera to focus on the wrong character. Fixes #268. --- src/characters/Faction.lua | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/characters/Faction.lua b/src/characters/Faction.lua index 2d7d17c9..e6b91ca6 100644 --- a/src/characters/Faction.lua +++ b/src/characters/Faction.lua @@ -244,14 +244,18 @@ end -- @treturn Character The active Character. -- function Faction:prevCharacter() - local previousCharacter = self.active:getObject() + -- Get the currently active character. + local currentCharacter = self.active:getObject() while self.active do + -- Select the previous character or wrap around to the last character + -- in the list. self.active = self.active:getPrev() or self.last - local character = self.active:getObject() - if not character:isDead() then + + local previousCharacter = self.active:getObject() + if not previousCharacter:isDead() then + currentCharacter:deactivate() previousCharacter:activate() - character:deactivate() - return character + return previousCharacter end end end From b72f73185e3925d31a71f44fbac612bcaf09362e Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sun, 2 Sep 2018 20:32:10 +0200 Subject: [PATCH 05/11] Remove validation for TexturePacks --- src/ui/texturepacks/TexturePacks.lua | 32 +--------------------------- 1 file changed, 1 insertion(+), 31 deletions(-) diff --git a/src/ui/texturepacks/TexturePacks.lua b/src/ui/texturepacks/TexturePacks.lua index b91e9904..b0505c8a 100644 --- a/src/ui/texturepacks/TexturePacks.lua +++ b/src/ui/texturepacks/TexturePacks.lua @@ -47,36 +47,6 @@ local current -- Private Functions -- ------------------------------------------------ ---- --- Checks if the loaded module provides all the necessary fields. --- @tparam table module The loaded module to check. --- @treturn boolean True if the module is valid. --- -local function validate( module ) - if not module.name - or not module.font - or not module.tileset then - return false - end - - if not module.font.source - or not module.font.glyphs - or not module.tileset.source - or not module.tileset.tiles then - return false - end - - if not module.font.glyphs.source - or not module.font.glyphs.width - or not module.font.glyphs.height - or not module.tileset.tiles.width - or not module.tileset.tiles.height then - return false - end - - return true -end - --- -- Loads a texture pack. -- @tparam string src The path to load the templates from. @@ -86,7 +56,7 @@ end local function load( src ) local path = src .. INFO_FILE_NAME local module = require( path ) - if not module or not validate( module ) then + if not module then return false end From 1fd60b27eae71bc5bcfd9470a33ed6255c449098 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Fri, 31 Aug 2018 23:11:41 +0200 Subject: [PATCH 06/11] Change how the game assembles fonts This allows us to load charsets for more languages. Right now the default texture pack includes the latin basic charset of the unicode standard. --- res/texturepacks/default/imagefont.png | Bin 1398 -> 0 bytes .../default/imagefont_latin_basic.png | Bin 0 -> 1413 bytes res/texturepacks/default/info.lua | 15 ++-- src/ui/texturepacks/Font.lua | 67 +++++++++++++++--- src/ui/texturepacks/TexturePack.lua | 5 +- src/ui/texturepacks/TexturePacks.lua | 4 +- 6 files changed, 72 insertions(+), 19 deletions(-) delete mode 100644 res/texturepacks/default/imagefont.png create mode 100644 res/texturepacks/default/imagefont_latin_basic.png diff --git a/res/texturepacks/default/imagefont.png b/res/texturepacks/default/imagefont.png deleted file mode 100644 index 65138eaf56d635e788c0a15e93720837f0bec120..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1398 zcmV-+1&R8JP)77Br3qx$`!kTEs_^1-d{M_Rvt8# zgD~oNvU-6m8=n!;wrxQ^s3)eoW4c z@6>C}$PhBdu?m5E&&Re`ck9rpf@4BSaAgTJsxrfqd9jQa z9k3JF`T-6(UXUaDBCy>MY*FJ_b)0qp+f_r<6>QNC z+bT#!K(AW~nf!GOw$VV>w0*#8c{vSivjLc8hftMdk#j%NG|&L?f3V#J+>L(h&IsHjOeQ2MT4ysxGXx0;}mtU z&<+P0EUHV%qG0PS^;k7nyY$Lsmfm&&hhe-N2R^xIk9&Oa)s{V40CQ%>r9%BF7R)k3Qek&%Hjw$RCdq*vBwf!^Dbb^2-NJth;{;7&-{G?sj7X2x-YYpAR2IL0k)@>B3C%FRi(%kV9Q(&Wa(EGaNT9hyqVbO z!NqoD8G)|MQe=Vcyk7dQ6&zNuMLNbE0(%t|KiOdG$*6P(TZA1K*yd*lk(uZ2V2c1V zc80%u#y0w1wkSmw*a~c`!ItsDWjS`<%qik}o^kAM4?G8YXRu`*{3n2IMFq#o`^(7K zdhAQ3J(#LP+g*X}S*6GVTh8^6J)#r9)*HA*R{T7z$Gdg5?&s0}?gtHUoxs*RUIy^* z47O~?1PQhs9ju)kPh_SIG((pk@aeP+;qwzf90$^`@e(cZCW>+ z5=vI-QZ`^t23vGs&$|DtV2gBQGc&;5dss6Z-m3d%gX3yItO`J~0ZCw+1GdP_UlrKC zd%jb^mQ_Jnk$Db-t#r~!C*7$!Po`w@eXHl$dOejWYF|kwUDPq2@+-H}NmqS-bbLO6 zt#r~!C!Ilu{xOH{_Z8S`KWqM^+ZAj(Ur##eq_gXO1IYGz3EG~c2><{907*qoM6N<$ Ef{zp>HPz`X4bYW>q+wP`1RxG^KtX@M`N9J)>&tr zb=FyDopsh(XPtH3bPrF8b=FyDopsh(XPtG{S!bPf3f=SXPdli6zWjXBz5n~`pSw)* z$T*^larMlV_nv0-|FeVV+t2EHj6QEf_xf)mx~pfdyq9`jbe>PJKYF~g>qb8B&hOsY zi^@*PeI6Od-FvU*ZDbs;e>bD+VlRs4k?Rpzw~_Ur-mBUll)s|mh^yam=6R9v(s9I0 zkiB{?M8_Llw^hIP?Q!Q$P`2~D&~dw=cESmDg{W_X+Swo?E4Zhjw!5w}xj_aI^mse1 zTgTt;6sUdQ>z^R%>fI;$14C^us8#uKDrnyUY8B^#q1I6QZSzvaQC|bKGlv_!7^CC( zHmD^kJ<*DlBDf;Q(N&jW2NYZq{y^zpRQ5+#Bs0c46C5b^TQq5L2dmkzRpk#V{ca}I z&U6@!#!Z>$rJ;6gJ+h%z74I6#47F8IOV3{k)ZT5j_g}DLlao-Jd1#%u*)pMa_7J3@ zmOcb`hgww|a8-r4sx7t(YN_Ob0%}Rvk3#L8ZWpLsJ!cHH`zO7LN($Zn9;vKGpGQ=5 zXUDsm1XlRnQP}TtU1B8(Om{|*e(2LWbnR3*9%^@iC`FKMsMQ6hE1P5Fd5X49%KozN z=qlS(=yn!j{tUHeKrJu`(YAHBcxno7#ZO#7uR2>vE znemLI1-(C_hb0ZQR8l2c360t_ot#uad?bii4L0bYi4L->uYIgSts-gGty0nH8yPz! zb29CDwg{1RjqbD#l<4)OPW~?fwHZmhjQNY&{nhzU4z<+~bu}@D+S>J?+i2+a)JU*F z9D>R_>~!*gIy|W0itc1ke^|#*yC>9A8FVTriFSfeANr_-+L?2KNP2%8)G8}h*>-gb z)GE&DlfD%|B-;(Oy`c8vm`|S+um)U-7sNL^d0bM&)qEM?S zg6)vh`{$V_LhURBWW3i<%Rrf`GH9qh^G>ay)=+yI)RIBA%I~gz17Nf=NJH&RQ2b3$ zySx9JNm_J<+EIHk)b{998Sz(&O6`n4CH-GUr^;iO%%2jXPN>v&hTqOmt8T~W2DP)n zE^U8g5-HQ3I~>v~cf0Q_PZq1R*u6^d)|LhMRsx-YWId(#r#oFtLRiA z_s0=X+qqJ!Xlt4YvZ)VMbcb3RF6q09lr9RjHUCF<{0?QPjmOIL_V*uV2v(V&yiK?hDb%)xOU^jD9j%1MN(b=FyDopsiA zD3XxZ<2t{$OED@dIc|GCZ~hFm)>&trlO@(!XPtG{S=Z#cSmiY}L#^$3*YW-a!U!;# T0&IWo00000NkvXXu0mjfnsd`# literal 0 HcmV?d00001 diff --git a/res/texturepacks/default/info.lua b/res/texturepacks/default/info.lua index 6e762e95..8c995879 100644 --- a/res/texturepacks/default/info.lua +++ b/res/texturepacks/default/info.lua @@ -9,11 +9,16 @@ return { } }, font = { - source = 'imagefont.png', - glyphs = { - source = ' ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzÄÖÜäöü0123456789.,:;!?-+/()[]%&"\'*=_<>ß^©|', - width = 8, - height = 16 + width = 8, + height = 16, + charsets = { + { + -- LATIN BASIC + -- 0020-007F (Excluded: 007F) + -- !"# $%&' ()*+ ,-./ 0123 4567 89:; <=>? @ABC DEFG HIJK LMNO PQRS TUVW XYZ[ \]^_ `abc defg hijk lmno pqrs tuvw xyz{ |} + source = 'imagefont_latin_basic.png', + glyphs = [[ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~]] + } } } } diff --git a/src/ui/texturepacks/Font.lua b/src/ui/texturepacks/Font.lua index 366ddd2a..38708b84 100644 --- a/src/ui/texturepacks/Font.lua +++ b/src/ui/texturepacks/Font.lua @@ -20,18 +20,67 @@ local Font = Class( 'Font' ) -- Public Methods -- ------------------------------------------------ +--- +-- Assembles the string containing all glyphs for the ImageFont. +-- @tparam table charsets A sequence containing the different charset definitions. +-- @treturn string The assembled glyph strings. +-- +local function assembleGlyphs( charsets ) + local glyphs = '' + for _, charset in ipairs( charsets ) do + glyphs = glyphs .. charset.glyphs + end + return glyphs +end + +--- +-- Assembles the ImageData for the ImageFont. +-- @tparam number height The height of the ImageFont. +-- @tparam string path The path of the image files to load. +-- @tparam table charsets A sequence containing the different charset definitions. +-- @treturn ImageData The assembled ImageData. +-- +local function assembleImageData( height, path, charsets ) + local fontWidth = 0 + local imageDatas = {} + + -- Check how many pixels we need for the new ImageData and store the + -- different ImageData objects. + for _, definition in ipairs( charsets ) do + local data = love.image.newImageData( path .. definition.source ) + fontWidth = fontWidth + data:getWidth() + imageDatas[#imageDatas + 1] = data + end + + -- Create the empty ImageData. + local fontImageData = love.image.newImageData( fontWidth, height ) + + -- Paste the different ImageDatas to the font's ImageData. + local offset = 0 + for _, data in ipairs( imageDatas ) do + fontImageData:paste( data, offset, 0, 0, 0, data:getDimensions() ) + offset = offset + data:getWidth() + end + + return fontImageData +end + --- -- 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 gw The width of one glyph. --- @tparam number gh The height of one glpyh. --- @treturn Font The new Font instance. +-- @tparam string path The path to load the imagefont file from. +-- @tparam table def The font definition. +-- @treturn Font The new Font instance. -- -function Font:initialize( source, glyphs, gw, gh ) - self.font = love.graphics.newImageFont( source, glyphs ) - self.glyphWidth = gw - self.glyphHeight = gh +function Font:initialize( path, def ) + self.glyphWidth = def.width + self.glyphHeight = def.height + + -- Assemble the parts of the ImageFont. + local glyphs = assembleGlyphs( def.charsets ) + local imageData = assembleImageData( def.height, path, def.charsets ) + + -- Create the ImageFont. + self.font = love.graphics.newImageFont( imageData, glyphs ) end --- diff --git a/src/ui/texturepacks/TexturePack.lua b/src/ui/texturepacks/TexturePack.lua index fa5a0116..7f1fd08e 100644 --- a/src/ui/texturepacks/TexturePack.lua +++ b/src/ui/texturepacks/TexturePack.lua @@ -24,9 +24,8 @@ function TexturePack:initialize( path, source, spriteInfos, colorInfos ) self.name = source.name -- 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 + self.font = Font( path, source.font ) + self.glyphWidth, self.glyphHeight = source.font.width, source.font.height -- Generate tileset. local t = source.tileset diff --git a/src/ui/texturepacks/TexturePacks.lua b/src/ui/texturepacks/TexturePacks.lua index b0505c8a..75b4f93e 100644 --- a/src/ui/texturepacks/TexturePacks.lua +++ b/src/ui/texturepacks/TexturePacks.lua @@ -32,7 +32,7 @@ local DEFAULT = { COLORS = 'colors.lua', WARNING = 'README.txt', SPRITES = 'sprites.lua', - IMAGEFONT = 'imagefont.png', + IMAGEFONT_LATIN_BASIC = 'imagefont_latin_basic.png', SPRITESHEET = 'spritesheet.png', } @@ -133,7 +133,7 @@ local function copyDefaultTexturePack() copyFile( source, target, DEFAULT.COLORS ) copyFile( source, target, DEFAULT.SPRITES ) copyFile( source, target, DEFAULT.WARNING ) - copyFile( source, target, DEFAULT.IMAGEFONT ) + copyFile( source, target, DEFAULT.IMAGEFONT_LATIN_BASIC ) copyFile( source, target, DEFAULT.SPRITESHEET ) end From e4f3eb57bede11ecc62f4059b41944384e516780 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Fri, 31 Aug 2018 23:11:41 +0200 Subject: [PATCH 07/11] Add rudimentary support for latin-1 fonts Closes #269. --- res/texturepacks/default/imagefont_latin_1.png | Bin 0 -> 1018 bytes res/texturepacks/default/info.lua | 7 +++++++ src/ui/texturepacks/TexturePacks.lua | 2 ++ 3 files changed, 9 insertions(+) create mode 100644 res/texturepacks/default/imagefont_latin_1.png diff --git a/res/texturepacks/default/imagefont_latin_1.png b/res/texturepacks/default/imagefont_latin_1.png new file mode 100644 index 0000000000000000000000000000000000000000..d656ee14c9b2265e9393f96cf15b1bddb4a7ed3d GIT binary patch literal 1018 zcmV8{j9~p+vDq9S86Yr?0MEsquPO1bi?^b>cPRm z6%e&J7dSXLf}FVX{hSW$%ARYjG3$q&N<^~7x@4*Bzm?RZQ^jt45{^z4I#slOa%P=x ziCPJF<)GR0;o#sbHzP^#myPRfzR@}F-$!c>5;3!Xmeo#Yk^&=p83j4fxV!732__>{ z_TNkn(W!FJo=5&qVlUb3xssh)cB(89wc1721fAJMt@q3`6&$#%m9p$2ZK-oFcP)V?G#Zfw*#vRNrg_8EXlB0P8yHD#bKvwicFRF1+z-6)ah!B zIeTWZHU9m*m+`d%EJPF>q83q$s9i5=j|ag^T~VtEVy#6*L{9rV%69vOH&IKcl#TzZ za{EOHQ7heyEHiN4MQy7Z>T!ICTDT0NmQ58=`!v_9zDoB?L~RaHyRzb*cTj#T20W|%M3{}lZaZoO0B2y!NHL!en-?=KM=`^%P49q1TQm{ z%jm({YEhfH0y|r&m5SP^q6|@6GpUEDbu4Oo4koLpm1GjB)WX5Z#8%{>Bue`+qW0OK z?)X{O;6-Z(6)I|X+K;Oz>qf`Qr%b=y#)_!TE#9)UA3xoe?ON1kXg}^%X&E0J+PW zdt0I=3HJs!+fF(Xwb}0Ju3)FDL@mz>m~TJE|M~cfrKy_xW0O`m`R95_!HWMXhvvciRI!b6NaGHO{)jBZL2|`u$pV z>ZwGnCODO}F%Y!F!NI{j%SqeJSU)&8U!5wf3;qudE{eZ$|DY&DEgT#iTwG4_ECbdL o4h|0g$NIs+S^VCfu3)#0Uy7xG5em9;BLDyZ07*qoM6N<$f=eOuYybcN literal 0 HcmV?d00001 diff --git a/res/texturepacks/default/info.lua b/res/texturepacks/default/info.lua index 8c995879..d2112e3f 100644 --- a/res/texturepacks/default/info.lua +++ b/res/texturepacks/default/info.lua @@ -18,6 +18,13 @@ return { -- !"# $%&' ()*+ ,-./ 0123 4567 89:; <=>? @ABC DEFG HIJK LMNO PQRS TUVW XYZ[ \]^_ `abc defg hijk lmno pqrs tuvw xyz{ |} source = 'imagefont_latin_basic.png', glyphs = [[ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~]] + }, + { + -- LATIN-1 + -- 00A0-00FF (Excluded: 00A0, 00AD) + -- ¡¢£¤ ¥¦§¨ ©ª«¬ ®¯°± ²³´µ ¶·¸¹ º»¼½ ¾¿ÀÁ ÂÃÄÅ ÆÇÈÉ ÊËÌÍ ÎÏÐÑ ÒÓÔÕ Ö×ØÙ ÚÛÜÝ Þßàá âãäå æçèé êëìí îïðñ òóôõ ö÷øù úûüý þÿ + source = 'imagefont_latin_1.png', + glyphs = [[¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ]] } } } diff --git a/src/ui/texturepacks/TexturePacks.lua b/src/ui/texturepacks/TexturePacks.lua index 75b4f93e..2df0a001 100644 --- a/src/ui/texturepacks/TexturePacks.lua +++ b/src/ui/texturepacks/TexturePacks.lua @@ -33,6 +33,7 @@ local DEFAULT = { WARNING = 'README.txt', SPRITES = 'sprites.lua', IMAGEFONT_LATIN_BASIC = 'imagefont_latin_basic.png', + IMAGEFONT_LATIN_1 = 'imagefont_latin_1.png', SPRITESHEET = 'spritesheet.png', } @@ -134,6 +135,7 @@ local function copyDefaultTexturePack() copyFile( source, target, DEFAULT.SPRITES ) copyFile( source, target, DEFAULT.WARNING ) copyFile( source, target, DEFAULT.IMAGEFONT_LATIN_BASIC ) + copyFile( source, target, DEFAULT.IMAGEFONT_LATIN_1 ) copyFile( source, target, DEFAULT.SPRITESHEET ) end From fcba93c95f66542e76507abd69b88c504c8e35b6 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Fri, 31 Aug 2018 22:23:51 +0200 Subject: [PATCH 08/11] Fix crash when quitting from combat mode --- src/ui/screens/CombatScreen.lua | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/ui/screens/CombatScreen.lua b/src/ui/screens/CombatScreen.lua index 707ae297..c1410a6b 100644 --- a/src/ui/screens/CombatScreen.lua +++ b/src/ui/screens/CombatScreen.lua @@ -122,10 +122,6 @@ function CombatScreen:mousefocus( f ) end end -function CombatScreen:close() - self.combatState:close() -end - function CombatScreen:resize( _, _ ) self.userInterface:resize() end From f6ddcc249eb3144ff1fe8a946b6d4318abb1ef93 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sun, 2 Sep 2018 22:30:05 +0200 Subject: [PATCH 09/11] Fix crash during world object loading Fixes #270. --- src/map/worldobjects/WorldObject.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/map/worldobjects/WorldObject.lua b/src/map/worldobjects/WorldObject.lua index 8e862909..35915b75 100644 --- a/src/map/worldobjects/WorldObject.lua +++ b/src/map/worldobjects/WorldObject.lua @@ -246,7 +246,10 @@ end function WorldObject:setBlocksVision( blocksVision ) self.blocksVision = blocksVision - self:publish( 'TILE_UPDATED', self:getTile() ) + -- Send no update if the map isn't assembled already during map loading. + if self:getMap() then + self:publish( 'TILE_UPDATED', self:getTile() ) + end end --- From 8e2febac24c4df6d861e145b1769b03304404b17 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sun, 2 Sep 2018 22:46:20 +0200 Subject: [PATCH 10/11] Fix crash caused by loading characters Fixes #271. --- src/characters/Character.lua | 1 + src/characters/CharacterFactory.lua | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/characters/Character.lua b/src/characters/Character.lua index d0803752..aba8eb2b 100644 --- a/src/characters/Character.lua +++ b/src/characters/Character.lua @@ -285,6 +285,7 @@ function Character:serialize() local t = { ['class'] = self.creatureClass, ['name'] = self.name, + ['maxActionPoints'] = self.maxActionPoints, ['actionPoints'] = self.actionPoints, ['accuracy'] = self.accuracy, ['throwingSkill'] = self.throwingSkill, diff --git a/src/characters/CharacterFactory.lua b/src/characters/CharacterFactory.lua index 70b0c686..71ebcb1d 100644 --- a/src/characters/CharacterFactory.lua +++ b/src/characters/CharacterFactory.lua @@ -178,7 +178,7 @@ function CharacterFactory.init() end function CharacterFactory.loadCharacter( savedCharacter ) - local character = Character( savedCharacter.class ) + local character = Character( savedCharacter.class, savedCharacter.maxActionPoints ) character:setName( savedCharacter.name ) character:setActionPoints( savedCharacter.actionPoints ) From cf06d9d6c460be9eff5ed2f080efa3ef396fd1c6 Mon Sep 17 00:00:00 2001 From: Robert Machmer Date: Sun, 2 Sep 2018 22:49:57 +0200 Subject: [PATCH 11/11] Prepare version 0.16.1.1627 --- CHANGELOG.md | 16 ++++++++++++++++ README.md | 4 ++-- version.lua | 4 ++-- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f90b1524..b7aa914f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,19 @@ +# Version 0.16.1.1627 - 2018-09-02 (LÖVE 11.1) + +## Additions +- Added support for latin-1 charset. + +## Fixes +- Fixed camera focusing on the wrong character when switching from the current to the previous character. +- Fixed faulty loading of world objects. +- Fixed faulty loading of characters. + +## Other Changes +- Changed how fonts are loaded by the game to support different charsets. + + + + # Version 0.16.0.1615 - 2018-08-30 (LÖVE 11.1) ## Additions diff --git a/README.md b/README.md index e023223b..04a4bbc4 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # On The Roadside -[![Version](https://img.shields.io/badge/Version-0.16.0.1615-blue.svg)](https://github.com/rm-code/on-the-roadside/releases/latest) -[![LOVE](https://img.shields.io/badge/L%C3%96VE-11.0-EA316E.svg)](http://love2d.org/) +[![Version](https://img.shields.io/badge/Version-0.16.1.1627-blue.svg)](https://github.com/rm-code/on-the-roadside/releases/latest) +[![LOVE](https://img.shields.io/badge/L%C3%96VE-11.1-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) _On the Roadside_ is a turn-based strategy game in which you take control of a squad of mercenaries fighting for survival in a world shaped by unknown forces. It currently is in the very _early stages_ of development. diff --git a/version.lua b/version.lua index 19e59ca8..29fcbe9c 100644 --- a/version.lua +++ b/version.lua @@ -1,8 +1,8 @@ local version = { major = 0, minor = 16, - patch = 0, - build = 1615, + patch = 1, + build = 1627, } return string.format( "%d.%d.%d.%d", version.major, version.minor, version.patch, version.build );