diff --git a/art/game/player_blocker.meta b/art/game/player_blocker.meta new file mode 100644 index 0000000..f1cdc1c --- /dev/null +++ b/art/game/player_blocker.meta @@ -0,0 +1 @@ +{class="player_blocker"} diff --git a/art/game/player_blocker_b.png b/art/game/player_blocker_b.png new file mode 100644 index 0000000..3e6bd9d Binary files /dev/null and b/art/game/player_blocker_b.png differ diff --git a/art/game/player_blocker_g.png b/art/game/player_blocker_g.png new file mode 100644 index 0000000..3e6bd9d Binary files /dev/null and b/art/game/player_blocker_g.png differ diff --git a/art/game/player_blocker_r.png b/art/game/player_blocker_r.png new file mode 100644 index 0000000..3e6bd9d Binary files /dev/null and b/art/game/player_blocker_r.png differ diff --git a/entity.lua b/entity.lua index e7ebb9b..bcb9f0d 100644 --- a/entity.lua +++ b/entity.lua @@ -156,6 +156,10 @@ function Entity:canPassOver() return false end +function Entity:blocksPlayer() + return false +end + function Entity:getClass() return self.class.name end diff --git a/generic_player_blocker.lua b/generic_player_blocker.lua new file mode 100644 index 0000000..da0724b --- /dev/null +++ b/generic_player_blocker.lua @@ -0,0 +1,21 @@ +require "entity" + +-- A visual boundary that only affects player movement. It deliberately stays +-- out of the ordinary collidable matrix, so moveable blocks can occupy and +-- pass through the same cell. +GenericPlayerBlocker = class("GenericPlayerBlocker", Entity) + +function GenericPlayerBlocker:initialize(x, y, color, sprite, cellShape) + Entity.initialize(self, { + x = x, + y = y, + color = color, + collision = "none", + sprite = sprite, + cellShape = cellShape + }) +end + +function GenericPlayerBlocker:blocksPlayer() + return true +end diff --git a/level_editor/editor.lua b/level_editor/editor.lua index c242840..ce1e8af 100644 --- a/level_editor/editor.lua +++ b/level_editor/editor.lua @@ -120,6 +120,9 @@ local function rebuildBoxPaletteSections() table.sort(names) table.insert(boxPaletteSections, { prefix = prefix, names = names }) end + if gameAssets.player_blocker then + table.insert(boxPaletteSections, { prefix = "barrier", names = {"player_blocker"} }) + end end local function buildBoxPicker(rect) @@ -1124,7 +1127,7 @@ function editorDraw() end end love.graphics.rectangle("line", s.searchBox.x, s.searchBox.y, s.searchBox.w, s.searchBox.h) - love.graphics.printf(boxPaletteOpen and "BOX VARIANTS" or "SEARCH: " .. assetSearchText, s.searchBox.x + 6, s.searchBox.y + 9, s.searchBox.w - 12, "left") + love.graphics.printf(boxPaletteOpen and "QUICK PALETTE" or "SEARCH: " .. assetSearchText, s.searchBox.x + 6, s.searchBox.y + 9, s.searchBox.w - 12, "left") if s.boxPicker then drawBoxPicker(s.boxPicker) diff --git a/room.lua b/room.lua index 7a68ac5..d354618 100644 --- a/room.lua +++ b/room.lua @@ -14,6 +14,7 @@ require "generic_sticky" require "generic_skewer" require "generic_spiky" require "generic_unmoveable" +require "generic_player_blocker" local Movement = require "movement" @@ -22,6 +23,7 @@ function Room:initialize(t) self.switches = { red = {}, green = {}, blue = {} } self.doors = {} self.collideables = { red = {}, green = {}, blue = {} } + self.playerBlockers = { red = {}, green = {}, blue = {} } self.npcs = {} self.activeColors = {red = false, green = false, blue = false} self.colorsPlayerHas = {} @@ -104,8 +106,10 @@ function Room:initialize(t) self.switchMatrices = {} self.collidableMatrices = {} --{red = {{...}{...}{...}}, ... + self.playerBlockerMatrices = {} self:createSwitchMatrix() self:createCollideablesMatrix() + self:createPlayerBlockerMatrix() -- Resolve switches/doors up front so the very first frame renders correct -- lock state: a block already sitting on a switch presses it before anything @@ -128,6 +132,7 @@ function Room:draw() for _, channel in ipairs(self.colorsPlayerHas) do if self.player[channel] then self.player[channel]:draw(channel) end for _, entity in pairs(self.collideables[channel]) do entity:draw(channel) end + for _, blocker in pairs(self.playerBlockers[channel]) do blocker:draw(channel) end for _, switch in pairs(self.switches[channel]) do switch:draw(channel) end for _, npc in ipairs(self.npcs) do npc:draw(channel) end end @@ -153,6 +158,12 @@ function Room:createSwitchMatrix() end end +function Room:createPlayerBlockerMatrix() + for color, blockers in pairs(self.playerBlockers) do + self.playerBlockerMatrices[color] = self:createEntityMatrix(blockers) + end +end + function Room:createEntityMatrix(entities) local matrix = {} @@ -220,10 +231,16 @@ function Room:movePlayer(key) end if initialTargetEntity then initialTargetEntity:bump() end + local playerBlocker + if self:isInBounds(initialTargetCell) and self.playerBlockerMatrices and self.playerBlockerMatrices[color] then + playerBlocker = self:getCellInMatrix(self.playerBlockerMatrices[color], initialTargetCell) + end if not self:isInBounds(initialTargetCell) then -- let's us move out of bounds to Change Rooms without pulling a bloc_ move = true + elseif playerBlocker and playerBlocker:blocksPlayer() then + move = false else move, entitiesToPush = Movement.findMovableGroup( {initialTargetCell}, dir, @@ -351,6 +368,7 @@ function Room:tick() end end self:createCollideablesMatrix() + self:createPlayerBlockerMatrix() self:createSwitchMatrix() self:resolveSwitchesAndDoors() self:nextRoomCheck() @@ -466,7 +484,7 @@ end -- Instantiate one object from the flat {class,color,x,y} schema. `class` names an -- art asset, and its .meta declares the behaviour ("door"/"switch"/"player"/ --- "moveable"/"sticky"/"skewer"/"spiky"); anything without a .meta defaults to immoveable. npc is the one +-- "moveable"/"sticky"/"skewer"/"spiky"/"player_blocker"); anything without a .meta defaults to immoveable. npc is the one -- keyword handled directly since it has no art. function Room:createEntity(class, color, x, y) if class == "npc" then @@ -509,7 +527,10 @@ function Room:createEntity(class, color, x, y) -- collides on its real cells and its gaps read as empty space. local cellShape = props.cells and props.cells[color] if cellShape and #cellShape == 0 then cellShape = nil end - if behavior == "moveable" then + if behavior == "player_blocker" then + entity = GenericPlayerBlocker:new(x, y, color, sprite, cellShape) + self:registerPlayerBlocker(entity, color) + elseif behavior == "moveable" then entity = GenericMoveable:new(x, y, color, sprite, class, cellShape) elseif behavior == "sticky" then entity = GenericSticky:new(x, y, color, sprite, class, cellShape) @@ -520,7 +541,9 @@ function Room:createEntity(class, color, x, y) else entity = GenericUnmoveable:new(x, y, color, sprite, cellShape) end - self:registerCollidable(entity, {color}) + if behavior ~= "player_blocker" then + self:registerCollidable(entity, {color}) + end end if entity then entity.saveClass = class end @@ -534,6 +557,10 @@ function Room:registerCollidable(entityPointer, colorArray) end end +function Room:registerPlayerBlocker(entityPointer, color) + table.insert(self.playerBlockers[color], entityPointer) +end + function Room:registerDoor(entityPointer, color) table.insert(self.doors, entityPointer) self:registerCollidable(entityPointer, color) @@ -591,6 +618,14 @@ function Room:attemptDelete(mouseX, mouseY, color) table.remove(entityArray, index) end end + -- Player-only blockers are rendered entities, but use their own collision + -- matrix so blocks can overlap them. + local blockerArray = self.playerBlockers[c] + for index = #blockerArray, 1, -1 do + if blockerArray[index]:occupiesCell(cell) then + table.remove(blockerArray, index) + end + end -- switches local switchArray = self.switches[c] for index = #switchArray, 1, -1 do @@ -714,6 +749,12 @@ function Room:serialize(includePlayers) end end + for color, blockerArray in pairs(self.playerBlockers) do + for _, blocker in pairs(blockerArray) do + emit(blocker.saveClass or "player_blocker", color, blocker) + end + end + for color, switchArray in pairs(self.switches) do for _, switch in ipairs(switchArray) do emit("switch", color, switch) diff --git a/rooms/blocker_test.sav b/rooms/blocker_test.sav new file mode 100644 index 0000000..d1e0147 --- /dev/null +++ b/rooms/blocker_test.sav @@ -0,0 +1,925 @@ +{ +objects={ + { + class="player", + color="red", + active=true, + x=6, + y=11 + }, + { + class="player", + color="green", + active=true, + x=6, + y=11 + }, + { + class="player", + color="blue", + active=true, + x=6, + y=11 + }, + { + class="static_wall", + color="red", + y=11, + x=11 + }, + { + class="static_wall", + color="red", + y=10, + x=11 + }, + { + class="static_wall", + color="red", + y=9, + x=11 + }, + { + class="static_wall", + color="red", + y=8, + x=11 + }, + { + class="static_wall", + color="red", + y=7, + x=11 + }, + { + class="static_wall", + color="red", + y=6, + x=11 + }, + { + class="static_wall", + color="red", + y=5, + x=11 + }, + { + class="static_wall", + color="red", + y=2, + x=11 + }, + { + class="static_wall", + color="red", + y=1, + x=11 + }, + { + class="static_wall", + color="red", + y=2, + x=11 + }, + { + class="static_wall", + color="red", + y=3, + x=11 + }, + { + class="static_wall", + color="red", + y=4, + x=11 + }, + { + class="static_wall", + color="red", + y=1, + x=10 + }, + { + class="static_wall", + color="red", + y=1, + x=9 + }, + { + class="static_wall", + color="red", + y=1, + x=8 + }, + { + class="static_wall", + color="red", + y=1, + x=7 + }, + { + class="static_wall", + color="red", + y=1, + x=6 + }, + { + class="static_wall", + color="red", + y=1, + x=5 + }, + { + class="static_wall", + color="red", + y=1, + x=4 + }, + { + class="static_wall", + color="red", + y=1, + x=3 + }, + { + class="static_wall", + color="red", + y=1, + x=2 + }, + { + class="static_wall", + color="red", + y=1, + x=1 + }, + { + class="static_wall", + color="red", + y=2, + x=1 + }, + { + class="static_wall", + color="red", + y=3, + x=1 + }, + { + class="static_wall", + color="red", + y=4, + x=1 + }, + { + class="static_wall", + color="red", + y=5, + x=1 + }, + { + class="static_wall", + color="red", + y=6, + x=1 + }, + { + class="static_wall", + color="red", + y=7, + x=1 + }, + { + class="static_wall", + color="red", + y=8, + x=1 + }, + { + class="static_wall", + color="red", + y=9, + x=1 + }, + { + class="static_wall", + color="red", + y=10, + x=1 + }, + { + class="static_wall", + color="red", + y=11, + x=1 + }, + { + class="static_wall", + color="red", + y=11, + x=2 + }, + { + class="static_wall", + color="red", + y=11, + x=3 + }, + { + class="static_wall", + color="red", + y=11, + x=4 + }, + { + class="static_wall", + color="red", + y=11, + x=5 + }, + { + class="static_wall", + color="red", + y=11, + x=7 + }, + { + class="static_wall", + color="red", + y=11, + x=8 + }, + { + class="static_wall", + color="red", + y=11, + x=9 + }, + { + class="static_wall", + color="red", + y=11, + x=10 + }, + { + class="static_wall", + color="red", + y=5, + x=7 + }, + { + class="static_wall", + color="red", + y=4, + x=7 + }, + { + class="static_wall", + color="red", + y=3, + x=7 + }, + { + class="static_wall", + color="red", + y=2, + x=7 + }, + { + class="static_wall", + color="red", + y=2, + x=5 + }, + { + class="static_wall", + color="red", + y=3, + x=5 + }, + { + class="static_wall", + color="red", + y=4, + x=5 + }, + { + class="static_wall", + color="red", + y=5, + x=5 + }, + { + class="static_wall", + color="green", + y=11, + x=11 + }, + { + class="static_wall", + color="green", + y=10, + x=11 + }, + { + class="static_wall", + color="green", + y=9, + x=11 + }, + { + class="static_wall", + color="green", + y=8, + x=11 + }, + { + class="static_wall", + color="green", + y=7, + x=11 + }, + { + class="static_wall", + color="green", + y=6, + x=11 + }, + { + class="static_wall", + color="green", + y=5, + x=11 + }, + { + class="static_wall", + color="green", + y=2, + x=11 + }, + { + class="static_wall", + color="green", + y=1, + x=11 + }, + { + class="static_wall", + color="green", + y=2, + x=11 + }, + { + class="static_wall", + color="green", + y=3, + x=11 + }, + { + class="static_wall", + color="green", + y=4, + x=11 + }, + { + class="static_wall", + color="green", + y=1, + x=10 + }, + { + class="static_wall", + color="green", + y=1, + x=9 + }, + { + class="static_wall", + color="green", + y=1, + x=8 + }, + { + class="static_wall", + color="green", + y=1, + x=7 + }, + { + class="static_wall", + color="green", + y=1, + x=6 + }, + { + class="static_wall", + color="green", + y=1, + x=5 + }, + { + class="static_wall", + color="green", + y=1, + x=4 + }, + { + class="static_wall", + color="green", + y=1, + x=3 + }, + { + class="static_wall", + color="green", + y=1, + x=2 + }, + { + class="static_wall", + color="green", + y=1, + x=1 + }, + { + class="static_wall", + color="green", + y=2, + x=1 + }, + { + class="static_wall", + color="green", + y=3, + x=1 + }, + { + class="static_wall", + color="green", + y=4, + x=1 + }, + { + class="static_wall", + color="green", + y=5, + x=1 + }, + { + class="static_wall", + color="green", + y=6, + x=1 + }, + { + class="static_wall", + color="green", + y=7, + x=1 + }, + { + class="static_wall", + color="green", + y=8, + x=1 + }, + { + class="static_wall", + color="green", + y=9, + x=1 + }, + { + class="static_wall", + color="green", + y=10, + x=1 + }, + { + class="static_wall", + color="green", + y=11, + x=1 + }, + { + class="static_wall", + color="green", + y=11, + x=2 + }, + { + class="static_wall", + color="green", + y=11, + x=3 + }, + { + class="static_wall", + color="green", + y=11, + x=4 + }, + { + class="static_wall", + color="green", + y=11, + x=5 + }, + { + class="static_wall", + color="green", + y=11, + x=7 + }, + { + class="static_wall", + color="green", + y=11, + x=8 + }, + { + class="static_wall", + color="green", + y=11, + x=9 + }, + { + class="static_wall", + color="green", + y=11, + x=10 + }, + { + class="static_wall", + color="green", + y=5, + x=7 + }, + { + class="static_wall", + color="green", + y=4, + x=7 + }, + { + class="static_wall", + color="green", + y=3, + x=7 + }, + { + class="static_wall", + color="green", + y=2, + x=7 + }, + { + class="static_wall", + color="green", + y=2, + x=5 + }, + { + class="static_wall", + color="green", + y=3, + x=5 + }, + { + class="static_wall", + color="green", + y=4, + x=5 + }, + { + class="static_wall", + color="green", + y=5, + x=5 + }, + { + class="static_wall", + color="blue", + y=11, + x=11 + }, + { + class="static_wall", + color="blue", + y=10, + x=11 + }, + { + class="static_wall", + color="blue", + y=9, + x=11 + }, + { + class="static_wall", + color="blue", + y=8, + x=11 + }, + { + class="static_wall", + color="blue", + y=7, + x=11 + }, + { + class="static_wall", + color="blue", + y=6, + x=11 + }, + { + class="static_wall", + color="blue", + y=5, + x=11 + }, + { + class="static_wall", + color="blue", + y=2, + x=11 + }, + { + class="static_wall", + color="blue", + y=1, + x=11 + }, + { + class="static_wall", + color="blue", + y=2, + x=11 + }, + { + class="static_wall", + color="blue", + y=3, + x=11 + }, + { + class="static_wall", + color="blue", + y=4, + x=11 + }, + { + class="static_wall", + color="blue", + y=1, + x=10 + }, + { + class="static_wall", + color="blue", + y=1, + x=9 + }, + { + class="static_wall", + color="blue", + y=1, + x=8 + }, + { + class="static_wall", + color="blue", + y=1, + x=7 + }, + { + class="static_wall", + color="blue", + y=1, + x=6 + }, + { + class="static_wall", + color="blue", + y=1, + x=5 + }, + { + class="static_wall", + color="blue", + y=1, + x=4 + }, + { + class="static_wall", + color="blue", + y=1, + x=3 + }, + { + class="static_wall", + color="blue", + y=1, + x=2 + }, + { + class="static_wall", + color="blue", + y=1, + x=1 + }, + { + class="static_wall", + color="blue", + y=2, + x=1 + }, + { + class="static_wall", + color="blue", + y=3, + x=1 + }, + { + class="static_wall", + color="blue", + y=4, + x=1 + }, + { + class="static_wall", + color="blue", + y=5, + x=1 + }, + { + class="static_wall", + color="blue", + y=6, + x=1 + }, + { + class="static_wall", + color="blue", + y=7, + x=1 + }, + { + class="static_wall", + color="blue", + y=8, + x=1 + }, + { + class="static_wall", + color="blue", + y=9, + x=1 + }, + { + class="static_wall", + color="blue", + y=10, + x=1 + }, + { + class="static_wall", + color="blue", + y=11, + x=1 + }, + { + class="static_wall", + color="blue", + y=11, + x=2 + }, + { + class="static_wall", + color="blue", + y=11, + x=3 + }, + { + class="static_wall", + color="blue", + y=11, + x=4 + }, + { + class="static_wall", + color="blue", + y=11, + x=5 + }, + { + class="static_wall", + color="blue", + y=11, + x=7 + }, + { + class="static_wall", + color="blue", + y=11, + x=8 + }, + { + class="static_wall", + color="blue", + y=11, + x=9 + }, + { + class="static_wall", + color="blue", + y=11, + x=10 + }, + { + class="static_wall", + color="blue", + y=5, + x=7 + }, + { + class="static_wall", + color="blue", + y=4, + x=7 + }, + { + class="static_wall", + color="blue", + y=3, + x=7 + }, + { + class="static_wall", + color="blue", + y=2, + x=7 + }, + { + class="static_wall", + color="blue", + y=2, + x=5 + }, + { + class="static_wall", + color="blue", + y=3, + x=5 + }, + { + class="static_wall", + color="blue", + y=4, + x=5 + }, + { + class="static_wall", + color="blue", + y=5, + x=5 + }, + { + class="player_blocker", + color="red", + y=5, + x=6 + }, + { + class="player_blocker", + color="green", + y=5, + x=6 + }, + { + class="player_blocker", + color="blue", + y=5, + x=6 + }, + { + class="switch", + color="red", + y=2, + x=6 + }, + { + class="switch", + color="green", + y=2, + x=6 + }, + { + class="switch", + color="blue", + y=2, + x=6 + } +} +} \ No newline at end of file diff --git a/rooms/room_07_07.sav b/rooms/room_08_07.sav similarity index 100% rename from rooms/room_07_07.sav rename to rooms/room_08_07.sav diff --git a/rooms/world.sav b/rooms/world.sav index a0be8d2..149089c 100644 --- a/rooms/world.sav +++ b/rooms/world.sav @@ -1,207 +1,212 @@ { -width=18, height=18, +lastRoom="white_primary", rooms={ { - x=10, name="3", + x=10, y=10 }, { - x=12, name="5", + x=12, y=10 }, { - x=12, name="village_1", + x=12, y=9 }, { - x=13, name="village_3", + x=13, y=8 }, { - x=11, name="village_4", + x=11, y=8 }, { - x=11, name="yellow_1", + x=11, y=7 }, { - x=11, name="yellow_3", + x=11, y=6 }, { - x=12, name="yellow_2", + x=12, y=7 }, { - x=11, name="yellow_4", + x=11, y=5 }, { - x=11, name="yellow_5", + x=11, y=4 }, { - x=10, name="yellow_6", + x=10, y=5 }, { - x=1, name="default", - y=1 - }, - { - x=3, - name="old_yellow_3", - y=1 - }, - { - x=5, - name="test_patt", - y=1 - }, - { - x=7, - name="test_patt_update", - y=1 - }, - { - x=9, - name="white_asymmetrical", - y=1 - }, - { - x=11, - name="white_ok", - y=1 - }, - { - x=13, - name="white_primary", - y=1 - }, - { - x=15, - name="yellow_arches", - y=1 - }, - { - x=17, - name="yellow_asymmetrical", - y=1 - }, - { x=1, - name="yellow_cc_moustache", - y=3 + y=1 }, { + name="old_yellow_3", x=3, - name="yellow_color_changing_levvel_1", - y=3 + y=1 }, { + name="test_patt", + x=5, + y=1 + }, + { + name="test_patt_update", + x=7, + y=1 + }, + { + name="white_asymmetrical", + x=9, + y=1 + }, + { + name="white_ok", + x=11, + y=1 + }, + { + name="white_primary", + x=13, + y=1 + }, + { + name="yellow_arches", + x=15, + y=1 + }, + { + name="yellow_asymmetrical", + x=17, + y=1 + }, + { + name="yellow_cc_moustache", + x=1, + y=3 + }, + { + name="yellow_color_changing_levvel_1", + x=3, + y=3 + }, + { + name="yellow_smile", x=7, - name="yellow_smile", y=3 }, { - x=9, name="2", + x=9, y=10 }, { - x=11, name="room_11_09", + x=11, y=9 }, { - x=9, name="yellow_skull", + x=9, y=3 }, { - x=5, name="jorge_room", + x=5, y=5 }, { - x=11, name="4", + x=11, y=10 }, { - x=1, name="room_01_05", + x=1, y=5 }, { - x=2, name="ok", + x=2, y=8 }, { - x=5, name="room_05_18", + x=5, y=18 }, { - x=9, name="village_start", + x=9, y=11 }, { - x=4, name="spiky_test", + x=4, y=8 }, { - x=5, name="corner_trimino_test", - y=7 - }, - { - x=6, - name="tetromino_loop_t", - y=7 - }, - { x=5, + y=7 + }, + { + name="tetromino_loop_t", + x=6, + y=7 + }, + { name="corner_trimino_test_copy", + x=5, y=6 }, { + name="blocker_test", x=7, - name="room_07_07", y=7 }, { - x=5, name="spiky_test_2", + x=5, y=8 }, { - x=5, name="spiky_test_3", + x=5, y=9 }, { - x=3, name="jorge2", + x=3, y=14 + }, + { + name="room_08_07", + x=8, + y=7 } }, -lastRoom="jorge2" +width=18 } \ No newline at end of file diff --git a/tests/test_movement.lua b/tests/test_movement.lua index 3f0395a..af0c302 100644 --- a/tests/test_movement.lua +++ b/tests/test_movement.lua @@ -361,9 +361,12 @@ end local function roomWith(player, entities, width, height) gridWidth, gridHeight = width or 8, height or 8 local matrix = {} + local blockerMatrix = {} for y = 1, gridHeight do matrix[y] = {} + blockerMatrix[y] = {} for x = 1, gridWidth do matrix[y][x] = false end + for x = 1, gridWidth do blockerMatrix[y][x] = false end end for _, entity in ipairs(entities) do for _, cell in ipairs(entity:getOccupiedCells()) do @@ -376,12 +379,61 @@ local function roomWith(player, entities, width, height) colorsPlayerHas = {"red"}, player = {red = player}, collidableMatrices = {red = matrix}, + playerBlockerMatrices = {red = blockerMatrix}, }, {__index = Room}) room.playerJoinCheck = function() end room.tick = function() end return room end +test("Room keeps player-only blockers out of block collision", function() + loadRoomClass() + local oldAssets, oldLoadSprite = globalAssetProperties, loadSprite + local sprite = {getWidth = function() return 16 end, getHeight = function() return 16 end} + globalAssetProperties = { + player_blocker = { + class = "player_blocker", + sprites = {red = "player_blocker_r.png"}, + cells = {red = {{x = 1, y = 1}}}, + }, + } + loadSprite = function() return sprite end + local room = setmetatable({ + collideables = {red = {}, green = {}, blue = {}}, + playerBlockers = {red = {}, green = {}, blue = {}}, + }, {__index = Room}) + + local blocker = room:createEntity("player_blocker", "red", 4, 3) + + assertTrue(blocker:blocksPlayer()) + assertTrue(#room.playerBlockers.red == 1) + assertTrue(#room.collideables.red == 0, "blockers must not enter block collision") + globalAssetProperties, loadSprite = oldAssets, oldLoadSprite +end) + +test("Room player-only blockers stop the player but do not stop a box", function() + loadRoomClass() + local player = movable(2, 3) + local box = movable(3, 3) + local blocker = movable(4, 3) + function blocker:blocksPlayer() return true end + local room = roomWith(player, {box}, 8, 8) + local blockerMatrix = {} + for y = 1, gridHeight do + blockerMatrix[y] = {} + for x = 1, gridWidth do blockerMatrix[y][x] = false end + end + blockerMatrix[3][4] = blocker + room.playerBlockerMatrices.red = blockerMatrix + + room:movePlayer("right") + + assertTrue(box.x == 4, "box should move onto the player-only blocker") + assertTrue(player.x == 3, "player should move while pushing the box through it") + room:movePlayer("right") + assertTrue(player.x == 3, "player should be stopped by the player-only blocker") +end) + test("Room applies the sticky-regular-player pull", function() loadRoomClass() local sticky = movable(2, 3, true)