From b9476211e38653afce06a12d6d0940670ab165ad Mon Sep 17 00:00:00 2001 From: terencehill Date: Fri, 13 Aug 2021 17:34:57 +0200 Subject: [PATCH] LMS: refactor LMS code to avoid forcing players to join on connection Minor changes: * players don't have lives in warmup stage * players can become spectators with F3 (without forfeiting) during warmup and countdown to game start * ready-restart moves forfeiters to spectators --- qcsrc/common/gamemodes/gamemode/lms/sv_lms.qc | 137 ++++++++++++------ qcsrc/common/gamemodes/gamemode/lms/sv_lms.qh | 3 + 2 files changed, 92 insertions(+), 48 deletions(-) diff --git a/qcsrc/common/gamemodes/gamemode/lms/sv_lms.qc b/qcsrc/common/gamemodes/gamemode/lms/sv_lms.qc index dc18b7c90..e69e1b7d4 100644 --- a/qcsrc/common/gamemodes/gamemode/lms/sv_lms.qc +++ b/qcsrc/common/gamemodes/gamemode/lms/sv_lms.qc @@ -36,9 +36,12 @@ void ClearWinners(); // limit. int WinningCondition_LMS() { + if (warmup_stage || time <= game_starttime) + return WINNING_NO; + entity first_player = NULL; int totalplayers = 0; - FOREACH_CLIENT(IS_PLAYER(it) && it.frags != FRAGS_PLAYER_OUT_OF_GAME, { + FOREACH_CLIENT(IS_PLAYER(it) && it.frags == FRAGS_PLAYER, { if (!totalplayers) first_player = it; ++totalplayers; @@ -77,10 +80,7 @@ int WinningCondition_LMS() // a winner! // and assign him his first place GameRules_scoring_add(first_player, LMS_RANK, 1); - if(warmup_stage) - return WINNING_NO; - else - return WINNING_YES; + return WINNING_YES; } } } @@ -118,14 +118,31 @@ int WinningCondition_LMS() MUTATOR_HOOKFUNCTION(lms, reset_map_global) { lms_lowest_lives = 999; + lms_quitters = 0; } MUTATOR_HOOKFUNCTION(lms, reset_map_players) { FOREACH_CLIENT(true, { + if (it.frags == FRAGS_PLAYER_OUT_OF_GAME) + { + // players who forfeited (rank >= 256) become spectators + if (it.lms_spectate_warning == 2) + it.frags = FRAGS_SPECTATOR; + else + it.frags = FRAGS_PLAYER; + } + + CS(it).killcount = 0; + it.lmsplayer = 0; + it.lms_spectate_warning = 0; + GameRules_scoring_add(it, LMS_RANK, -GameRules_scoring_add(it, LMS_RANK, 0)); + GameRules_scoring_add(it, LMS_LIVES, -GameRules_scoring_add(it, LMS_LIVES, 0)); + + if (it.frags != FRAGS_PLAYER) + continue; + TRANSMUTE(Player, it); - it.frags = FRAGS_PLAYER; - GameRules_scoring_add(it, LMS_LIVES, LMS_NewPlayerLives()); PutClientInServer(it); }); } @@ -138,21 +155,45 @@ MUTATOR_HOOKFUNCTION(lms, ReadLevelCvars) sv_ready_restart_after_countdown = 0; } +// returns true if player is added to the game +bool lms_AddPlayer(entity player) +{ + if (!player.lmsplayer) + { + int lives = GameRules_scoring_add(player, LMS_LIVES, LMS_NewPlayerLives()); + if(lives <= 0) + return false; + player.lmsplayer = 1; + } + if (warmup_stage || time <= game_starttime) + { + if(player.lms_spectate_warning) + { + player.lms_spectate_warning = 0; + GameRules_scoring_add(player, LMS_RANK, -GameRules_scoring_add(player, LMS_RANK, 0)); + int lives = GameRules_scoring_add(player, LMS_LIVES, 0); + if(lives <= 0) + GameRules_scoring_add(player, LMS_LIVES, LMS_NewPlayerLives()); + } + } + else + { + if(GameRules_scoring_add(player, LMS_LIVES, 0) <= 0) + { + Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_LMS_NOLIVES); + return false; + } + } + return true; +} + MUTATOR_HOOKFUNCTION(lms, PutClientInServer) { entity player = M_ARGV(0, entity); - - if(player.frags == FRAGS_SPECTATOR) - TRANSMUTE(Observer, player); - else + if (!warmup_stage && (IS_BOT_CLIENT(player) || CS(player).jointime != time)) { - float tl = GameRules_scoring_add(player, LMS_LIVES, 0); - if(tl < lms_lowest_lives) - lms_lowest_lives = tl; - if(tl <= 0) + if (GameRules_scoring_add(player, LMS_RANK, 0) || !lms_AddPlayer(player)) TRANSMUTE(Observer, player); - if(warmup_stage) - GameRules_scoring_add(player, LMS_RANK, -GameRules_scoring_add(player, LMS_RANK, 0)); } } @@ -160,14 +201,10 @@ MUTATOR_HOOKFUNCTION(lms, ForbidSpawn) { entity player = M_ARGV(0, entity); - if(warmup_stage) + if (warmup_stage || lms_AddPlayer(player)) return false; - if(player.frags == FRAGS_SPECTATOR || GameRules_scoring_add(player, LMS_LIVES, 0) <= 0) - { - Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_LMS_NOLIVES); - return true; - } - return false; + + return true; } MUTATOR_HOOKFUNCTION(lms, PlayerDies) @@ -186,7 +223,9 @@ MUTATOR_HOOKFUNCTION(lms, PlayerDies) void lms_RemovePlayer(entity player) { - static int quitters = 0; + if (warmup_stage || time < game_starttime) + return; + float player_rank = GameRules_scoring_add(player, LMS_RANK, 0); if (!player_rank) { @@ -194,7 +233,7 @@ void lms_RemovePlayer(entity player) { player.frags = FRAGS_PLAYER_OUT_OF_GAME; int pl_cnt = 0; - FOREACH_CLIENT(IS_PLAYER(it) && it.frags != FRAGS_PLAYER_OUT_OF_GAME, { + FOREACH_CLIENT(IS_PLAYER(it) && it.frags == FRAGS_PLAYER, { pl_cnt++; }); GameRules_scoring_add(player, LMS_RANK, pl_cnt + 1); @@ -202,6 +241,7 @@ void lms_RemovePlayer(entity player) else { FOREACH_CLIENT(true, { + // update rank of other players that were eliminated if (it.frags == FRAGS_PLAYER_OUT_OF_GAME) { float it_rank = GameRules_scoring_add(it, LMS_RANK, 0); @@ -215,11 +255,11 @@ void lms_RemovePlayer(entity player) lms_lowest_lives = tl; } }); - GameRules_scoring_add(player, LMS_RANK, 665 - quitters); // different from 666 + GameRules_scoring_add(player, LMS_RANK, 665 - lms_quitters); // different from 666 if(!warmup_stage) { GameRules_scoring_add(player, LMS_LIVES, -GameRules_scoring_add(player, LMS_LIVES, 0)); - ++quitters; + ++lms_quitters; } player.frags = FRAGS_PLAYER_OUT_OF_GAME; TRANSMUTE(Observer, player); @@ -243,6 +283,7 @@ MUTATOR_HOOKFUNCTION(lms, ClientDisconnect) player.lms_spectate_warning = 3; lms_RemovePlayer(player); + player.lmsplayer = 0; } MUTATOR_HOOKFUNCTION(lms, MakePlayerObserver) @@ -252,27 +293,24 @@ MUTATOR_HOOKFUNCTION(lms, MakePlayerObserver) if (!IS_PLAYER(player)) return true; - lms_RemovePlayer(player); - return true; // prevent team reset -} - -MUTATOR_HOOKFUNCTION(lms, ClientConnect) -{ - entity player = M_ARGV(0, entity); - - if(GameRules_scoring_add(player, LMS_LIVES, LMS_NewPlayerLives()) <= 0) + if (warmup_stage || time <= game_starttime) { - GameRules_scoring_add(player, LMS_RANK, 666); // mark as forced spectator for the hud code + GameRules_scoring_add(player, LMS_LIVES, -GameRules_scoring_add(player, LMS_LIVES, 0)); player.frags = FRAGS_SPECTATOR; + TRANSMUTE(Observer, player); + player.lmsplayer = 0; } + else if (!GameRules_scoring_add(player, LMS_RANK, 0)) + lms_RemovePlayer(player); + return true; // prevent team reset } -// FIXME LMS doesn't allow clients to spectate due to its particular implementation -MUTATOR_HOOKFUNCTION(lms, AutoJoinOnConnection) +MUTATOR_HOOKFUNCTION(lms, ClientConnect) { - if(autocvar_g_campaign) - return false; - return true; + entity player = M_ARGV(0, entity); + TRANSMUTE(Observer, player); + player.frags = FRAGS_SPECTATOR; + player.lms_spectate_warning = 0; } MUTATOR_HOOKFUNCTION(lms, PlayerPreThink) @@ -300,7 +338,7 @@ MUTATOR_HOOKFUNCTION(lms, GiveFragsForKill) { entity frag_target = M_ARGV(1, entity); - if (!warmup_stage) + if (!warmup_stage && time > game_starttime) { // remove a life int tl = GameRules_scoring_add(frag_target, LMS_LIVES, -1); @@ -309,7 +347,7 @@ MUTATOR_HOOKFUNCTION(lms, GiveFragsForKill) if(tl <= 0) { int pl_cnt = 0; - FOREACH_CLIENT(IS_PLAYER(it) && it.frags != FRAGS_PLAYER_OUT_OF_GAME, { + FOREACH_CLIENT(IS_PLAYER(it) && it.frags == FRAGS_PLAYER, { pl_cnt++; }); frag_target.frags = FRAGS_PLAYER_OUT_OF_GAME; @@ -398,7 +436,8 @@ MUTATOR_HOOKFUNCTION(lms, ItemTouch) MUTATOR_HOOKFUNCTION(lms, Bot_FixCount, CBC_ORDER_EXCLUSIVE) { FOREACH_CLIENT(IS_REAL_CLIENT(it), { - ++M_ARGV(0, int); // activerealplayers + if (it.lmsplayer && it.lms_spectate_warning < 2) + ++M_ARGV(0, int); // activerealplayers ++M_ARGV(1, int); // realplayers }); @@ -409,7 +448,7 @@ MUTATOR_HOOKFUNCTION(lms, ClientCommand_Spectate) { entity player = M_ARGV(0, entity); - if(warmup_stage || player.lms_spectate_warning) + if(warmup_stage || time < game_starttime || player.lms_spectate_warning) { // for the forfeit message... player.lms_spectate_warning = 2; @@ -440,7 +479,9 @@ MUTATOR_HOOKFUNCTION(lms, SetWeaponArena) MUTATOR_HOOKFUNCTION(lms, GetPlayerStatus) { - return true; + entity player = M_ARGV(0, entity); + + return boolean(player.lmsplayer); } MUTATOR_HOOKFUNCTION(lms, AddPlayerScore) diff --git a/qcsrc/common/gamemodes/gamemode/lms/sv_lms.qh b/qcsrc/common/gamemodes/gamemode/lms/sv_lms.qh index bf02920d2..bbaa4728b 100644 --- a/qcsrc/common/gamemodes/gamemode/lms/sv_lms.qh +++ b/qcsrc/common/gamemodes/gamemode/lms/sv_lms.qh @@ -7,6 +7,9 @@ // 2 when player goes spectator (presses F3 to spectate for the second time) // 3 when player disconnects .int lms_spectate_warning; +.int lmsplayer; + +int lms_quitters = 0; #define autocvar_g_lms_lives_override cvar("g_lms_lives_override") string autocvar_g_lms_weaponarena = "most_available"; -- 2.39.2