From cace34d759974a1739067daf1b31442746196e52 Mon Sep 17 00:00:00 2001 From: catloversg <152669316+catloversg@users.noreply.github.com> Date: Tue, 8 Oct 2024 13:55:11 +0700 Subject: [PATCH] BUGFIX: Wrong calculation in team casualties of Bladeburner action (#1672) --- src/Bladeburner/Actions/TeamCasualties.ts | 41 ++++++++++++-------- test/jest/Bladeburner/TeamCasualties.test.ts | 11 +++++- 2 files changed, 35 insertions(+), 17 deletions(-) diff --git a/src/Bladeburner/Actions/TeamCasualties.ts b/src/Bladeburner/Actions/TeamCasualties.ts index 1f9724f74..34266c8a8 100644 --- a/src/Bladeburner/Actions/TeamCasualties.ts +++ b/src/Bladeburner/Actions/TeamCasualties.ts @@ -26,22 +26,31 @@ export interface TeamActionWithCasualties { * and may result in casualties, reducing the player's hp, killing team members * and killing sleeves (to shock them, sleeves are immortal) * */ -export function resolveTeamCasualties(action: TeamActionWithCasualties, team: OperationTeam, success: boolean) { - const severity = success ? CasualtyFactor.LOW_CASUALTIES : CasualtyFactor.HIGH_CASUALTIES; - const radius = action.teamCount * severity; - const worstCase = severity < 1 ? Math.ceil(radius) : Math.floor(radius); - /** Best case is always no deaths */ - const deaths = team.getTeamCasualtiesRoll(action.getMinimumCasualties(), worstCase); - const humans = action.teamCount - team.sleeveSize; - const humanDeaths = Math.min(humans, deaths); - /** Supporting Sleeves take damage when they are part of losses, - * e.g. 8 sleeves + 3 team members with 4 losses -> 1 sleeve takes damage */ - team.killRandomSupportingSleeves(deaths - humanDeaths); +export function resolveTeamCasualties(action: TeamActionWithCasualties, team: OperationTeam, success: boolean): number { + if (action.teamCount <= 0) { + return 0; + } - /** Clamped, bugfix for PR#1659 - * "BUGFIX: Wrong team size when all team members die in Bladeburner's action" */ - team.teamSize = Math.max(team.teamSize - humanDeaths, team.sleeveSize); - team.teamLost += deaths; + // Operation actions and Black Operation actions have different min casualties: Min of Ops = 0. Min of BlackOps = 1. + const minCasualties = action.getMinimumCasualties(); + const maxCasualties = success + ? Math.ceil(action.teamCount * CasualtyFactor.LOW_CASUALTIES) + : Math.floor(action.teamCount * CasualtyFactor.HIGH_CASUALTIES); + /** + * In the current state, it's safe to assume that minCasualties <= maxCasualties. However, in the future, if we change + * min casualties, LOW_CASUALTIES, or HIGH_CASUALTIES, the call of getTeamCasualtiesRoll may crash. + * getTeamCasualtiesRoll is just getRandomIntInclusive, and that function's parameters need to be in the form of + * (min, max). + */ + const losses = + minCasualties <= maxCasualties ? team.getTeamCasualtiesRoll(minCasualties, maxCasualties) : minCasualties; + team.teamSize -= losses; + if (team.teamSize < team.sleeveSize) { + team.killRandomSupportingSleeves(team.sleeveSize - team.teamSize); + // If this happens, all team members died and some sleeves took damage. In this case, teamSize = sleeveSize. + team.teamSize = team.sleeveSize; + } + team.teamLost += losses; - return deaths; + return losses; } diff --git a/test/jest/Bladeburner/TeamCasualties.test.ts b/test/jest/Bladeburner/TeamCasualties.test.ts index 45c3c17c7..4d62056d2 100644 --- a/test/jest/Bladeburner/TeamCasualties.test.ts +++ b/test/jest/Bladeburner/TeamCasualties.test.ts @@ -18,7 +18,7 @@ import { PlayerObject } from "../../../src/PersonObjects/Player/PlayerObject"; */ describe("Bladeburner Team", () => { const MAX_ROLL = (_: number, high: number) => high; - const MIN_ROLL = (low: number, _: number) => low; + const MIN_ROLL = (low: number, __: number) => low; const BLACK_OP = BlackOperation.createId(BladeburnerBlackOpName.OperationAnnihilus); const OP = Operation.createId(BladeburnerOperationName.Assassination); @@ -96,6 +96,15 @@ describe("Bladeburner Team", () => { }); describe("Casualties", () => { + it.each([[OP], [BLACK_OP]])( + "no change in team size when not using team. Action: %s", + (op: ActionIdFor | ActionIdFor) => { + teamSize(0), supportingSleeves(3), startAction(op), teamUsed(0), actionFails(); + expect(inst.teamSize).toBe(3); + expect(inst.teamLost).toBe(0); + }, + ); + it("do not affect contracts", () => { teamSize(3); inst.action = Contract.createId(BladeburnerContractName.Tracking);