-- Character selection state CharSelectState = {} CharSelectState.__index = CharSelectState function CharSelectState:new(stateMachine) local self = setmetatable({}, CharSelectState) self.stateMachine = stateMachine self.slots = {} for i = 1, Config.MAX_PLAYERS do self.slots[i] = { status = Config.SLOT_STATUS.OPEN, playerIdx = 0, selectedChar = 1 } end return self end function CharSelectState:enter() for i = 1, Config.MAX_PLAYERS do self.slots[i].status = Config.SLOT_STATUS.OPEN self.slots[i].playerIdx = 0 self.slots[i].selectedChar = 1 end end function CharSelectState:update(dt) local input = self.stateMachine.input -- Check if any player pressed any button if input.globalAny and input.globalAnySource then local playerIdx = input:getSourcePlayerIdx(input.globalAnySource) -- Assign new player if playerIdx == nil then playerIdx = input:assignPlayerToSource(input.globalAnySource) local openSlot = self:findOpenSlot() if openSlot ~= nil then openSlot.playerIdx = playerIdx openSlot.status = Config.SLOT_STATUS.JOINED end end end for i = 1, Config.MAX_PLAYERS do local slot = self:findSlotByPlayer(i) if slot ~= nil then -- Cycle characters with left/right (only when not locked in) if slot.status == Config.SLOT_STATUS.JOINED then if input:wasPressed(i, InputManager.ACTIONS.MOVE_LEFT) then slot.selectedChar = slot.selectedChar - 1 if slot.selectedChar < 1 then slot.selectedChar = #Config.CHARACTERS end elseif input:wasPressed(i, InputManager.ACTIONS.MOVE_RIGHT) then slot.selectedChar = slot.selectedChar + 1 if slot.selectedChar > #Config.CHARACTERS then slot.selectedChar = 1 end end end -- JUMP locks in selection if input:wasPressed(i, InputManager.ACTIONS.JUMP) then slot.status = Config.SLOT_STATUS.READY end -- DEFEND un-lock (back to browsing) if input:wasPressed(i, InputManager.ACTIONS.DEFEND) then if slot.status == Config.SLOT_STATUS.READY then slot.status = Config.SLOT_STATUS.JOINED end end end end -- Check if all joined players are ready self:checkAllReady() end function CharSelectState:findOpenSlot() for i = 1, Config.MAX_PLAYERS do if self.slots[i].status == Config.SLOT_STATUS.OPEN then return self.slots[i] end end return nil end function CharSelectState:findSlotByPlayer(playerIdx) for i = 1, Config.MAX_PLAYERS do if self.slots[i].playerIdx == playerIdx then return self.slots[i] end end return nil end function CharSelectState:checkAllReady() local allReady = true local anyJoined = false for i = 1, Config.MAX_PLAYERS do if self.slots[i].status ~= Config.SLOT_STATUS.OPEN then anyJoined = true if self.slots[i].status ~= Config.SLOT_STATUS.READY then allReady = false end end end if anyJoined and allReady then self.stateMachine:change(Config.STATES.GAME) end end function CharSelectState:draw() local input = self.stateMachine.input -- Background love.graphics.setColor(0.1, 0.1, 0.15, 1) love.graphics.rectangle("fill", 0, 0, Config.SCREEN_W, Config.SCREEN_H) love.graphics.setColor(1, 1, 1, 1) -- Title Utils.printWithShadow("CHARACTER SELECT", 160, 30) -- Player slots for i = 1, Config.MAX_PLAYERS do local slot = self.slots[i] local y = 80 + (i - 1) * 60 -- Player label local color = Config.PLAYER_COLORS[i] love.graphics.setColor(color[1], color[2], color[3], color[4]) Utils.printWithShadow("PLAYER " .. i, 40, y) -- Status local statusText local statusColor if slot.status == Config.SLOT_STATUS.OPEN then statusText = "OPEN" statusColor = { 0.5, 0.5, 0.5 } elseif slot.status == Config.SLOT_STATUS.JOINED then statusText = "JOINED" statusColor = { 1, 1, 0.3 } else statusText = "READY" statusColor = { 0.3, 1, 0.3 } end love.graphics.setColor(statusColor[1], statusColor[2], statusColor[3], 1) Utils.printWithShadow(statusText, 200, y) -- Input method love.graphics.setColor(0.6, 0.6, 0.6, 1) if slot.playerIdx ~= 0 then Utils.printWithShadow(input:getPlayerInputName(slot.playerIdx), 200, y + 12) else Utils.printWithShadow("PRESS ANY BUTTON", 350, y) end -- Character name with arrows if slot.playerIdx ~= 0 then love.graphics.setColor(0.8, 0.8, 0.8, 1) local charName = Config.CHARACTERS[slot.selectedChar].name if slot.status == Config.SLOT_STATUS.JOINED then Utils.printWithShadow("← " .. charName .. " →", 350, y) else Utils.printWithShadow(charName, 350, y) end end end love.graphics.setColor(1, 1, 1, 1) end function CharSelectState:exit() -- Pass player info to game state local gameSlots = {} for i = 1, Config.MAX_PLAYERS do local slot = self.slots[i] if slot.status ~= Config.SLOT_STATUS.OPEN then gameSlots[#gameSlots + 1] = { playerIdx = slot.playerIdx, slot = i, selectedChar = Config.CHARACTERS[slot.selectedChar].name } end end self.stateMachine.players = gameSlots end