Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 19 additions & 48 deletions client/src/BattleRoom.lua
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ local GeneratorSource = require("common.engine.GeneratorSource")

-- A Battle Room is a session of matches, keeping track of the room number, player settings, wins / losses etc
---@class BattleRoom : Signal
---@field mode GameMode
---@field mode GameMode The game mode configuration defining rules, player count, and match settings for this battle room
---@field players Player[]
---@field spectators string[]
---@field spectating boolean
Expand Down Expand Up @@ -59,7 +59,8 @@ end)
BattleRoom.states = { Setup = 1, MatchInProgress = 2 }

function BattleRoom.createFromServerMessage(message)
local battleRoom = BattleRoom(message.gameMode)
local gameMode = GameModes.createFromServerData(message.gameMode)
local battleRoom = BattleRoom(gameMode)

if message.spectate_request_granted then
logger.debug("Joining a match as spectator")
Expand Down Expand Up @@ -108,19 +109,8 @@ function BattleRoom.createFromServerMessage(message)
p = Player(player.name, player.publicId or -i, false)
end

-- order is important here as setting style will indirectly also override levelData so it needs to be before updateSettings
if gameMode.style ~= GameModes.Styles.CHOOSE then
p:setStyle(gameMode.style)
else
if player.settings.levelData then
if player.settings.levelData.frameConstants.GARBAGE_HOVER then
p:setStyle(GameModes.Styles.MODERN)
else
p:setStyle(GameModes.Styles.CLASSIC)
end
end
end

-- updateSettings will set levelData which triggers levelDataChanged signal
-- which will automatically update style based on the levelData
p:updateSettings(player.settings)

if player.ratingInfo then
Expand All @@ -141,30 +131,33 @@ function BattleRoom.createFromServerMessage(message)
return battleRoom
end

function BattleRoom.createLocalFromGameMode(gameMode, gameScene)
-- Creates a local (offline) BattleRoom from a GameMode configuration.
-- For single-player modes, uses the game's main local player. For multi-player modes,
-- creates temporary local players that don't persist settings changes.
---@param gameMode GameMode The game mode configuration defining rules and player count
---@param gameScene table? Optional scene class to use for matches (defaults to mode's gameScene)
---@param settingChangesUpdateConfig boolean? If true, setting changes update config (default: true). Only applies to single-player modes.
---@return BattleRoom? battleRoom The created battle room, or nil if input configuration assignment fails
function BattleRoom.createLocalFromGameMode(gameMode, gameScene, settingChangesUpdateConfig)
if settingChangesUpdateConfig == nil then
settingChangesUpdateConfig = true
end

local battleRoom = BattleRoom(gameMode, gameScene)

if gameMode.playerCount == 1 then
if settingChangesUpdateConfig and gameMode.playerCount == 1 then
-- always use the game client's local player
battleRoom:addPlayer(GAME.localPlayer)
else
-- with more than 1 local player we can't be sure which player is the "real" regular user
-- so make them both local players that don't update config settings
for i = 1, gameMode.playerCount do
local player = Player.getLocalPlayer()
local player = Player.createLocalPlayerFromConfig()
player.name = loc("player_n", i)
battleRoom:addPlayer(player)
end
end

if gameMode.style ~= GameModes.Styles.CHOOSE then
for i, player in ipairs(battleRoom.players) do
if player.human then
battleRoom.players[i]:setStyle(gameMode.style)
end
end
end

if battleRoom:assignInputConfigurations() then
return battleRoom
else
Expand Down Expand Up @@ -404,28 +397,6 @@ function BattleRoom:createScene(match)
end
end

-- sets the style of "level" presets the players select from
-- 1 = classic
-- 2 = modern
-- longterm we want to abandon the concept of "style" on the player / battleRoom level
-- just setting difficulty or level should set the levelData and done with it, style is a menu-only concept
-- there is no technical reason why someone on level 10 shouldn't be able to play against someone on Hard
-- for now it's a battleRoom wide setting and players have to match
function BattleRoom:setStyle(styleChoice)
-- style could be configurable per play instead but let's not for now
if self.mode.style == GameModes.Styles.CHOOSE then
self.style = styleChoice
self.onStyleChanged(styleChoice)
else
error("Trying to set difficulty style in a game mode that doesn't support style selection")
end
end

-- not player specific, so this gets a separate callback that can only be overwritten once
-- so the UI can update and load up the different controls for it
function BattleRoom.onStyleChanged(style, player)
end

function BattleRoom:startLoadingNewAssets()
if ModLoader.loading_mod == nil then
for _, player in ipairs(self.players) do
Expand Down
8 changes: 4 additions & 4 deletions client/src/ClientMatch.lua
Original file line number Diff line number Diff line change
Expand Up @@ -396,10 +396,10 @@ function ClientMatch:finalizeReplay()
---@cast player Player
metadata.name = player.name
metadata.publicId = player.publicId
if player.settings.style == GameModes.Styles.MODERN then
metadata.level = player.settings.level
else
metadata.difficulty = player.settings.difficulty
if stack.level then
metadata.level = stack.level
elseif stack.difficulty then
metadata.difficulty = stack.difficulty
end
metadata.analytics = player.stack.analytic.data
---@diagnostic disable-next-line: inject-field
Expand Down
34 changes: 13 additions & 21 deletions client/src/Game.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ require("client.src.mods.Theme")
-- Not to be confused with "Match" which is the current battle / instance of the game.
local consts = require("common.engine.consts")
local GraphicsUtil = require("client.src.graphics.graphics_util")
local LevelPresets = require("common.data.LevelPresets")
local class = require("common.lib.class")
local logger = require("common.lib.logger")
local analytics = require("client.src.analytics")
Expand Down Expand Up @@ -257,7 +258,7 @@ end
-- GAME.localPlayer is the standard player for battleRooms that don't get started from replays/spectate
-- it basically represents the player that is operating the client (and thus binds to its configuration)
function Game:initializeLocalPlayer()
self.localPlayer = Player.getLocalPlayer()
self.localPlayer = Player.createLocalPlayerFromConfig()
self.localPlayer:connectSignal("selectedCharacterIdChanged", config, function(config, newId) config.character = newId end)
self.localPlayer:connectSignal("selectedStageIdChanged", config, function(config, newId) config.stage = newId end)
self.localPlayer:connectSignal("panelIdChanged", config, function(config, newId) config.panels = newId end)
Expand All @@ -266,11 +267,17 @@ function Game:initializeLocalPlayer()
self.localPlayer:connectSignal("difficultyChanged", config, function(config, difficulty) config.endless_difficulty = difficulty end)
self.localPlayer:connectSignal("levelChanged", config, function(config, level) config.level = level end)
self.localPlayer:connectSignal("wantsRankedChanged", config, function(config, wantsRanked) config.ranked = wantsRanked end)
self.localPlayer:connectSignal("styleChanged", config, function(config, style)
if style == GameModes.Styles.CLASSIC then
config.endless_level = nil
else
config.endless_level = config.level

self.localPlayer:connectSignal("levelDataChanged", config, function(config, levelData, player)
local presetInfo = LevelPresets.getStyleAndPreset(levelData)
if presetInfo then
if presetInfo.style == GameModes.Styles.MODERN then
config.level = presetInfo.level
config.endless_level = presetInfo.level
else
config.endless_difficulty = presetInfo.difficulty
config.endless_level = nil
end
end
end)
end
Expand Down Expand Up @@ -305,21 +312,6 @@ function Game:createDirectoriesIfNeeded()
end
end

function Game:runUnitTests()
coroutine.yield("Running Unit Tests")

-- GAME.localPlayer is the standard player for battleRooms that don't get started from replays/spectate
-- basically the player that is operating the client
GAME.localPlayer = Player.getLocalPlayer()
-- we need to overwrite the local player as all replay related tests need a non-local player
GAME.localPlayer.isLocal = false

logger.info("Running Unit Tests...")
GAME.muteSound = true
--require("client.tests.Tests")
SoundController:applyConfigVolumes()
end

function Game:runPerformanceTests()
coroutine.yield("Running Performance Tests")
require("tests.StackReplayPerformanceTests")
Expand Down
44 changes: 16 additions & 28 deletions client/src/Player.lua
Original file line number Diff line number Diff line change
Expand Up @@ -107,12 +107,19 @@ function Player:createClientStack(engineStack)
player = self,
}

if self.settings.style == GameModes.Styles.MODERN then
args.level = self.settings.level
else
args.difficulty = self.settings.difficulty
local presetInfo = LevelPresets.getStyleAndPreset(self.settings.levelData)
if presetInfo then
if presetInfo.style == GameModes.Styles.MODERN then
if presetInfo.level then
args.level = self.settings.level
end
else
if presetInfo.difficulty then
args.difficulty = self.settings.difficulty
end
end
end

self.stack = PlayerStack(args)

return self.stack
Expand Down Expand Up @@ -144,7 +151,7 @@ end
function Player:setLevelData(levelData)
self.settings.levelData = levelData
self:setSpeed(levelData.startingSpeed)
self:emitSignal("levelDataChanged", levelData)
self:emitSignal("levelDataChanged", levelData, self)
end

function Player:setSpeed(speed)
Expand Down Expand Up @@ -172,18 +179,11 @@ end
-- sets the style of "level" presets the player selects from
-- 1 = classic
-- 2 = modern
-- longterm we want to abandon the concept of "style" on the player / battleRoom level
-- just setting difficulty or level should set the levelData and done with it, style is a menu-only concept
-- there is no technical reason why someone on level 10 shouldn't be able to play against someone on Hard
-- style is a menu-only concept for UI display
-- derived settings (levelData) should be updated by calling gameMode.updateLocalPlayersDerivedSettings
function Player:setStyle(style)
if style ~= self.settings.style then
self.settings.style = style
if style == GameModes.Styles.MODERN then
self:setLevelData(LevelPresets.getModern(self.settings.level or config.level))
else
self:setLevelData(LevelPresets.getClassic(self.settings.difficulty or config.endless_difficulty))
self:setSpeed(self.settings.speed)
end
self:emitSignal("styleChanged", style)
end
end
Expand Down Expand Up @@ -233,7 +233,7 @@ function Player:unrestrictInputs()
end

---@return Player
function Player.getLocalPlayer()
function Player.createLocalPlayerFromConfig()
local player = Player(config.name, -1, true)

player:setDifficulty(config.endless_difficulty)
Expand All @@ -246,10 +246,8 @@ function Player.getLocalPlayer()
player:setInputMethod(config.inputMethod)
if config.endless_level then
player:setStyle(GameModes.Styles.MODERN)
player:setLevelData(LevelPresets.getModern(player.settings.level))
else
player:setStyle(GameModes.Styles.CLASSIC)
player:setLevelData(LevelPresets.getClassic(player.settings.difficulty))
player:setSpeed(config.endless_speed)
end

Expand Down Expand Up @@ -325,16 +323,6 @@ function Player:updateSettings(settings)
end

if settings.levelData ~= nil then
if settings.level ~= nil then
if settings.levelData.frameConstants.GARBAGE_HOVER then
self:setStyle(GameModes.Styles.MODERN)
self:setLevel(settings.level)
elseif settings.level <= LevelPresets.classicPresetCount then
self:setStyle(GameModes.Styles.CLASSIC)
self:setDifficulty(settings.level)
end
end

self:setLevelData(settings.levelData)
end

Expand Down
1 change: 1 addition & 0 deletions client/src/network/NetClient.lua
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,7 @@ function NetClient:requestSpectate(roomNumber)
end
end

---@param gameMode GameMode
function NetClient:requestRoom(gameMode)
if self:isConnected() then
self.tcpClient:sendRequest(ClientMessages.sendRoomRequest(gameMode))
Expand Down
67 changes: 49 additions & 18 deletions client/src/scenes/CharacterSelect.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ local class = require("common.lib.class")
local logger = require("common.lib.logger")
local tableUtils = require("common.lib.tableUtils")
local GameModes = require("common.data.GameModes")
local LevelPresets = require("common.data.LevelPresets")
local Scene = require("client.src.scenes.Scene")
local ui = require("client.src.ui")
local GraphicsUtil = require("client.src.graphics.graphics_util")
local Character = require("client.src.mods.Character")
local LevelPresets = require("common.data.LevelPresets")

-- The character select screen scene
---@class CharacterSelect : Scene
Expand Down Expand Up @@ -57,7 +57,47 @@ function CharacterSelect:load()
self.ui.cursors = {}
self.ui.characterIcons = {}
self.ui.playerInfos = {}

self:customLoad()

for _, player in ipairs(self.players) do
if player:isHuman() then
if player.isLocal then
self:initializeFromLocalPlayerSettings(player)
end
player:connectSignal("levelDataChanged", self, self.onLevelDataChanged)
self:onLevelDataChanged(player.settings.levelData, player)
end
end
end

function CharacterSelect:onLevelDataChanged(levelData, player)
local presetInfo = LevelPresets.getStyleAndPreset(levelData)

if not presetInfo then
-- Custom levelData, default to current settings
return
end

if presetInfo.style == GameModes.Styles.MODERN then
player:setStyle(GameModes.Styles.MODERN)
if presetInfo.level then
player:setLevel(presetInfo.level)
end
else
player:setStyle(GameModes.Styles.CLASSIC)
if presetInfo.difficulty then
player:setDifficulty(presetInfo.difficulty)
end
end

self:refresh()
end

function CharacterSelect:initializeFromLocalPlayerSettings(player)
player:setStyle(GameModes.Styles.MODERN)
player:setLevel(player.settings.level)
player:setLevelData(LevelPresets.getModern(player.settings.level))
end

---@param player Player
Expand Down Expand Up @@ -893,7 +933,7 @@ function CharacterSelect:createSpeedSlider(player, height, min)
return uiElement
end

function CharacterSelect:createDifficultyCarousel(player, height)
function CharacterSelect:createDifficultyCarousel(player, height, getPresetFunc)
local passengers = {
{ id = 1, uiElement = ui.Label({text = "easy", vAlign = "center", hAlign = "center"})},
{ id = 2, uiElement = ui.Label({text = "normal", vAlign = "center", hAlign = "center"})},
Expand All @@ -918,25 +958,16 @@ function CharacterSelect:createDifficultyCarousel(player, height)
-- Just update on every passenger change
end

local updateDifficultyData = function(difficultyID)
local levelData = LevelPresets.getClassic(difficultyID)
player:setDifficulty(difficultyID)
if self.battleRoom.mode.name == "endless" and difficultyID == 1 then
-- Endless easy uses 5 colors instead of 6
levelData:setColorCount(5)
-- and by extension also allows adjacent panels of the same colors
levelData:setAdjacentDenialFrequency(0)
end
player:setLevelData(levelData)
end
difficultyCarousel.onPassengerUpdateCallback = function(carousel, selectedPassenger)
updateDifficultyData(selectedPassenger.id)
player:setDifficulty(selectedPassenger.id)
if getPresetFunc then
player:setLevelData(getPresetFunc(selectedPassenger.id))
end
GAME.theme:playMoveSfx()
self:refresh()
end
-- Note that this updates the player level data which could be wrong before because of the weird endless case
-- its probably fine for now, but ideally the model should be right when the battle room is created
updateDifficultyData(difficultyCarousel.selectedId)

-- to update the UI if code gets changed from the backend (e.g. network messages)
player:connectSignal("difficultyChanged", difficultyCarousel, difficultyCarousel.setPassengerById)

return difficultyCarousel
end
Expand Down
Loading