X-Git-Url: https://de.git.xonotic.org/?p=xonotic%2Fxonotic-data.pk3dir.git;a=blobdiff_plain;f=qcsrc%2Fcommon%2Fgamemodes%2Fgamemode%2Flms%2Fsv_lms.qc;h=9a210262b68e80b8b1c8b323a81739f8f8111f94;hp=ba45f6e7ebd12bfb26259986cd1148f9076c33ec;hb=80fbd518b7cbd82c02fee4d7fcf7d43853d6dd97;hpb=9d5295bb4213f6c703d43d158605efe893514ee4 diff --git a/qcsrc/common/gamemodes/gamemode/lms/sv_lms.qc b/qcsrc/common/gamemodes/gamemode/lms/sv_lms.qc index ba45f6e7eb..9a210262b6 100644 --- a/qcsrc/common/gamemodes/gamemode/lms/sv_lms.qc +++ b/qcsrc/common/gamemodes/gamemode/lms/sv_lms.qc @@ -7,9 +7,12 @@ #include int autocvar_g_lms_extra_lives; +float autocvar_g_lms_forfeit_min_match_time; bool autocvar_g_lms_join_anytime; int autocvar_g_lms_last_join; +bool autocvar_g_lms_items; bool autocvar_g_lms_regenerate; +bool autocvar_g_lms_rot; // main functions int LMS_NewPlayerLives() @@ -36,12 +39,21 @@ 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, { - if (!totalplayers) - first_player = it; - ++totalplayers; + int totalplayed = 0; + FOREACH_CLIENT(true, { + if (IS_PLAYER(it) && it.frags == FRAGS_PLAYER) + { + if (!totalplayers) + first_player = it; + ++totalplayers; + } + else if (GameRules_scoring_add(it, LMS_RANK, 0)) + ++totalplayed; }); if (totalplayers) @@ -50,7 +62,7 @@ int WinningCondition_LMS() { // two or more active players - continue with the game - if (autocvar_g_campaign) + if (autocvar_g_campaign && campaign_bots_may_start) { FOREACH_CLIENT(IS_REAL_CLIENT(it), { float pl_lives = GameRules_scoring_add(it, LMS_LIVES, 0); @@ -65,10 +77,15 @@ int WinningCondition_LMS() // exactly one player? ClearWinners(); - SetWinners(winning, 0); // NOTE: exactly one player is still "player", so this works out if (LMS_NewPlayerLives()) { + if (totalplayed && game_starttime > 0 && time > game_starttime + autocvar_g_lms_forfeit_min_match_time) // give players time to join + { + GameRules_scoring_add(first_player, LMS_RANK, 1); + first_player.winning = 1; + return WINNING_YES; + } // game still running (that is, nobody got removed from the game by a frag yet)? then continue return WINNING_NO; } @@ -77,10 +94,8 @@ 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; + first_player.winning = 1; + return WINNING_YES; } } } @@ -89,6 +104,11 @@ int WinningCondition_LMS() // nobody is playing at all... if (LMS_NewPlayerLives()) { + if (totalplayed && game_starttime > 0 && time > game_starttime + autocvar_g_lms_forfeit_min_match_time) // give players time to join + { + ClearWinners(); + return WINNING_YES; + } // wait for players... } else @@ -123,9 +143,25 @@ MUTATOR_HOOKFUNCTION(lms, reset_map_global) 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; + INGAME_STATUS_CLEAR(it); + 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,70 +174,132 @@ 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 (!INGAME(player)) + { + int lives = GameRules_scoring_add(player, LMS_LIVES, LMS_NewPlayerLives()); + if(lives <= 0) + return false; + if (time < game_starttime) + INGAME_STATUS_SET(player, INGAME_STATUS_JOINED); + else + INGAME_STATUS_SET(player, INGAME_STATUS_JOINING); // this is just to delay setting health and armor that can't be done here + } + 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)); } } -MUTATOR_HOOKFUNCTION(lms, ForbidSpawn) +MUTATOR_HOOKFUNCTION(lms, PlayerSpawn) { entity player = M_ARGV(0, entity); - if(warmup_stage) - 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); + if (warmup_stage || time < game_starttime) return true; + + if (INGAME_JOINING(player)) + { + // spawn player with the same amount of health / armor + // as the least healthy player with the least number of lives + int pl_lives = GameRules_scoring_add(player, LMS_LIVES, 0); + float min_health = start_health; + float min_armorvalue = start_armorvalue; + FOREACH_CLIENT(it != player && IS_PLAYER(it) && !IS_DEAD(it) && GameRules_scoring_add(it, LMS_LIVES, 0) == pl_lives, { + if (GetResource(it, RES_HEALTH) < min_health) + min_health = GetResource(it, RES_HEALTH); + if (GetResource(it, RES_ARMOR) < min_armorvalue) + min_armorvalue = GetResource(it, RES_ARMOR); + }); + if (min_health != start_health) + SetResource(player, RES_HEALTH, max(1, min_health)); + if (min_armorvalue != start_armorvalue) + SetResource(player, RES_ARMOR, min_armorvalue); + INGAME_STATUS_SET(player, INGAME_STATUS_JOINED); } - return false; +} + +MUTATOR_HOOKFUNCTION(lms, ForbidSpawn) +{ + entity player = M_ARGV(0, entity); + + if (warmup_stage || lms_AddPlayer(player)) + return false; + + return true; } MUTATOR_HOOKFUNCTION(lms, PlayerDies) { entity frag_target = M_ARGV(2, entity); + float tl = GameRules_scoring_add(frag_target, LMS_LIVES, 0); + if (tl <= 0) + { + frag_target.respawn_flags = RESPAWN_SILENT; + // prevent unwanted sudden rejoin as spectator and movement of spectator camera + frag_target.respawn_time = time + 2; + } frag_target.respawn_flags |= RESPAWN_FORCE; } 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) { if (player.lms_spectate_warning < 2) { - if(IS_BOT_CLIENT(player)) - bot_clear(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); } - else + else if (INGAME(player)) { - FOREACH_CLIENT(true, { + int min_forfeiter_rank = 665; // different from 666 + FOREACH_CLIENT(it != player, { + // 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); if (it_rank > player_rank && it_rank <= 256) GameRules_scoring_add(it, LMS_RANK, -1); + if (it_rank > 256 && it_rank <= min_forfeiter_rank) + min_forfeiter_rank = it_rank - 1; } else if (it.frags != FRAGS_SPECTATOR) { @@ -210,12 +308,9 @@ 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, min_forfeiter_rank); if(!warmup_stage) - { GameRules_scoring_add(player, LMS_LIVES, -GameRules_scoring_add(player, LMS_LIVES, 0)); - ++quitters; - } player.frags = FRAGS_PLAYER_OUT_OF_GAME; TRANSMUTE(Observer, player); } @@ -238,36 +333,38 @@ MUTATOR_HOOKFUNCTION(lms, ClientDisconnect) player.lms_spectate_warning = 3; lms_RemovePlayer(player); + INGAME_STATUS_CLEAR(player); } MUTATOR_HOOKFUNCTION(lms, MakePlayerObserver) { entity player = M_ARGV(0, entity); + bool is_forced = M_ARGV(1, bool); 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); + INGAME_STATUS_CLEAR(player); + } + else + { + if (is_forced) + player.lms_spectate_warning = 2; + 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); + player.frags = FRAGS_SPECTATOR; } MUTATOR_HOOKFUNCTION(lms, PlayerPreThink) @@ -280,9 +377,11 @@ MUTATOR_HOOKFUNCTION(lms, PlayerPreThink) MUTATOR_HOOKFUNCTION(lms, PlayerRegen) { - if(autocvar_g_lms_regenerate) - return false; - return true; + if(!autocvar_g_lms_regenerate) + M_ARGV(2, float) = 0; + if(!autocvar_g_lms_rot) + M_ARGV(3, float) = 0; + return (!autocvar_g_lms_regenerate && !autocvar_g_lms_rot); } MUTATOR_HOOKFUNCTION(lms, ForbidThrowCurrentWeapon) @@ -295,7 +394,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); @@ -304,11 +403,9 @@ 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++; }); - if(IS_BOT_CLIENT(frag_target)) - bot_clear(frag_target); frag_target.frags = FRAGS_PLAYER_OUT_OF_GAME; GameRules_scoring_add(frag_target, LMS_RANK, pl_cnt); } @@ -321,6 +418,9 @@ MUTATOR_HOOKFUNCTION(lms, GiveFragsForKill) MUTATOR_HOOKFUNCTION(lms, SetStartItems) { start_items &= ~(IT_UNLIMITED_AMMO | IT_UNLIMITED_SUPERWEAPONS); + if(!cvar("g_use_ammunition")) + start_items |= IT_UNLIMITED_AMMO; + start_health = warmup_start_health = cvar("g_lms_start_health"); start_armorvalue = warmup_start_armorvalue = cvar("g_lms_start_armor"); start_ammo_shells = warmup_start_ammo_shells = cvar("g_lms_start_ammo_shells"); @@ -339,13 +439,16 @@ MUTATOR_HOOKFUNCTION(lms, ForbidPlayerScore_Clear) MUTATOR_HOOKFUNCTION(lms, FilterItemDefinition) { + if (autocvar_g_lms_items) + return false; + entity definition = M_ARGV(0, entity); if (autocvar_g_lms_extra_lives && definition == ITEM_ExtraLife) { return false; } - return true; + return (autocvar_g_pickup_items <= 0); // only allow items if explicitly enabled } void lms_extralife(entity this) @@ -395,7 +498,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 (INGAME(it) && it.lms_spectate_warning < 2) + ++M_ARGV(0, int); // activerealplayers ++M_ARGV(1, int); // realplayers }); @@ -406,7 +510,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; @@ -416,7 +520,8 @@ MUTATOR_HOOKFUNCTION(lms, ClientCommand_Spectate) if(player.frags != FRAGS_SPECTATOR && player.frags != FRAGS_PLAYER_OUT_OF_GAME) { player.lms_spectate_warning = 1; - sprint(player, "WARNING: you won't be able to enter the game again after spectating in LMS. Use the same command again to spectate anyway.\n"); + sprint(player, "^1WARNING:^7 you can't rejoin this match after spectating. Use the same command again to spectate anyway.\n"); + Send_Notification(NOTIF_ONE_ONLY, player, MSG_CENTER, CENTER_LMS_SPECWARN); } return MUT_SPECCMD_RETURN; } @@ -435,11 +540,6 @@ MUTATOR_HOOKFUNCTION(lms, SetWeaponArena) M_ARGV(0, string) = autocvar_g_lms_weaponarena; } -MUTATOR_HOOKFUNCTION(lms, GetPlayerStatus) -{ - return true; -} - MUTATOR_HOOKFUNCTION(lms, AddPlayerScore) { if(game_stopped)