diff --git a/room.lua b/room.lua index baf426a..09b5523 100644 --- a/room.lua +++ b/room.lua @@ -440,8 +440,24 @@ function Room:switchPlayerColorLocal(color) local targetPlayer = self.player[color] if not targetPlayer then return false end - local canSwitch = not self:onlyColorIsActive(color) - if canSwitch then + local activeColors = self:getActiveColors() + local canJoin = not self:colorIsActive(color) and #activeColors > 0 + if canJoin then + local target = targetPlayer:getGridPos() + for _, activeColor in ipairs(activeColors) do + local activePlayer = self.player[activeColor] + local activePos = activePlayer and activePlayer:getGridPos() + if not activePos or activePos.x ~= target.x or activePos.y ~= target.y then + canJoin = false + break + end + end + end + + local canSwitch = canJoin or not self:onlyColorIsActive(color) + if canJoin then + self:setActiveColor(color, true) + elseif canSwitch then for _, c in ipairs(self:getPossibleColors()) do self:setActiveColor(c, c == color) end diff --git a/tests/test_wire.lua b/tests/test_wire.lua index 9dda775..4e9c47d 100644 --- a/tests/test_wire.lua +++ b/tests/test_wire.lua @@ -291,6 +291,42 @@ test("selecting a parked colour changes to its room", function() currentRoom, setEditorRoomName = oldCurrentRoom, oldSetEditorRoomName end) +test("co-located colours join the active player selection", function() + local function player(x, y) + local entity = {x = x, y = y, switched = false} + function entity:getGridPos() return {x = self.x, y = self.y} end + function entity:playSwitchSound(success) self.switched = success end + return entity + end + local room = { + name = "a", + player = {red = player(3, 4), green = player(3, 4), blue = player(6, 4)}, + activeColors = {red = true, green = false, blue = false}, + colorsPlayerHas = {"red", "green", "blue"}, + } + function room:getPossibleColors() return self.colorsPlayerHas end + function room:getActiveColors() + local colors = {} + for _, color in ipairs(self.colorsPlayerHas) do + if self.activeColors[color] then table.insert(colors, color) end + end + return colors + end + function room:colorIsActive(color) return self.activeColors[color] end + function room:setActiveColor(color, value) self.activeColors[color] = value end + local world = setmetatable({ + levels = {a = room}, roomByName = {a = {name = "a", x = 1, y = 1}}, + }, {__index = World}) + world.flushRuntime = function() end + + assertTrue(world:switchPlayerColor(room, "green")) + assertTrue(room.activeColors.red and room.activeColors.green, + "co-located red and green did not become active together") + assertTrue(world:switchPlayerColor(room, "blue")) + assertTrue(room.activeColors.blue and not room.activeColors.red and not room.activeColors.green, + "separated blue player should still select by itself") +end) + test("one run cannot terminate at two different switches", function() local second = switch("red", 1, 2) local world, _, roomB = makeWorld(second) diff --git a/world.lua b/world.lua index 2d1059a..68cf5cd 100644 --- a/world.lua +++ b/world.lua @@ -731,8 +731,23 @@ function World:switchPlayerColor(room, color) return true end local activeColors = room:getActiveColors() - local canSwitch = #activeColors ~= 1 or activeColors[1] ~= color - if canSwitch then + local canJoin = not room:colorIsActive(color) and #activeColors > 0 + if canJoin then + local target = player:getGridPos() + for _, activeColor in ipairs(activeColors) do + local activePlayer = room.player[activeColor] + local activePos = activePlayer and activePlayer:getGridPos() + if not activePos or activePos.x ~= target.x or activePos.y ~= target.y then + canJoin = false + break + end + end + end + + local canSwitch = canJoin or #activeColors ~= 1 or activeColors[1] ~= color + if canJoin then + room:setActiveColor(color, true) + elseif canSwitch then for _, possibleColor in ipairs(room:getPossibleColors()) do room:setActiveColor(possibleColor, possibleColor == color) end