From b80ba27bc496f1163d3ae8d7077514c69f352eb3 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Wed, 20 Nov 2024 14:42:47 -0600 Subject: [PATCH 01/19] Add planetary diameter to track state initialization Added the calculation of planetary diameter to the initialization of StratconTrackState. This allows the track size to be set proportionally based on the planet's surface area, ensuring more realistic and varied gameplay environments. --- .../stratcon/StratconContractInitializer.java | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java b/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java index 2a18790323..47585721e9 100644 --- a/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java +++ b/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java @@ -70,6 +70,7 @@ public static void initializeCampaignState(AtBContract contract, Campaign campai int maximumTrackIndex = Math.max(0, contract.getRequiredLances() / NUM_LANCES_PER_TRACK); int planetaryTemperature = campaign.getLocation().getPlanet().getTemperature(campaign.getLocalDate()); + double planetaryDiameter = campaign.getLocation().getPlanet().getDiameter(); for (int x = 0; x < maximumTrackIndex; x++) { int scenarioOdds = contractDefinition.getScenarioOdds() @@ -78,7 +79,7 @@ public static void initializeCampaignState(AtBContract contract, Campaign campai .get(Compute.randomInt(contractDefinition.getDeploymentTimes().size())); StratconTrackState track = initializeTrackState(NUM_LANCES_PER_TRACK, scenarioOdds, deploymentTime, - planetaryTemperature); + planetaryTemperature, planetaryDiameter); track.setDisplayableName(String.format("Sector %d", x)); campaignState.addTrack(track); } @@ -94,7 +95,7 @@ public static void initializeCampaignState(AtBContract contract, Campaign campai .get(Compute.randomInt(contractDefinition.getDeploymentTimes().size())); StratconTrackState track = initializeTrackState(oddLanceCount, scenarioOdds, deploymentTime, - planetaryTemperature); + planetaryTemperature, planetaryDiameter); track.setDisplayableName(String.format("Sector %d", campaignState.getTracks().size())); campaignState.addTrack(track); } @@ -193,6 +194,8 @@ public static void initializeCampaignState(AtBContract contract, Campaign campai for (StratconTrackState track : campaignState.getTracks()) { track.getStrategicObjectives().clear(); } + } else { + // Initialize non-objective scenarios } // now we're done @@ -203,20 +206,24 @@ public static void initializeCampaignState(AtBContract contract, Campaign campai * lances. */ public static StratconTrackState initializeTrackState(int numLances, int scenarioOdds, - int deploymentTime, int planetaryTemp) { + int deploymentTime, int planetaryTemp, + double planetaryDiameter) { // to initialize a track, // 1. we set the # of required lances - // 2. set the track size to a total of numlances * 28 hexes, a rectangle that is + // 2. set the track size to a total of numlances * 84 hexes, a rectangle that is // wider than it is taller - // the idea being to create a roughly rectangular playing field that, - // if one deploys a scout lance each week to a different spot, can be more or - // less fully covered StratconTrackState retVal = new StratconTrackState(); retVal.setRequiredLanceCount(numLances); + // calculate planet surface area + double radius = planetaryDiameter / 2; + double planetSurfaceArea = 4 * Math.PI * Math.pow(radius, 2); + // This gives us a decently sized track, without it feeling too large + planetSurfaceArea /= 1000000; + // set width and height - int numHexes = numLances * 28; + int numHexes = (int) Math.round(planetSurfaceArea); int height = (int) Math.floor(Math.sqrt(numHexes)); int width = numHexes / height; retVal.setWidth(width); From bdf3772b236e4c9b3649841b5f388952c9560696 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Wed, 20 Nov 2024 15:09:32 -0600 Subject: [PATCH 02/19] Refactor Stratcon scenario generation to handle null scenarios Added null checks throughout Stratcon scenario generation methods to handle potential null scenarios. Created new methods and updated existing ones to ensure robust error handling and to return null if scenario creation fails. --- .../stratcon/StratconRulesManager.java | 260 ++++++++++++++---- 1 file changed, 202 insertions(+), 58 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java b/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java index ec47576c29..59e3f2d96e 100644 --- a/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java +++ b/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java @@ -52,6 +52,7 @@ import java.util.*; import java.util.stream.Collectors; +import static mekhq.campaign.force.Force.FORCE_NONE; import static mekhq.campaign.mission.ScenarioMapParameters.MapLocation.AllGroundTerrain; import static mekhq.campaign.mission.ScenarioMapParameters.MapLocation.LowAtmosphere; import static mekhq.campaign.mission.ScenarioMapParameters.MapLocation.Space; @@ -138,7 +139,10 @@ public static void generateScenariosForTrack(Campaign campaign, AtBContract cont if (track.getAssignedCoordForces().containsKey(scenarioCoords)) { StratconScenario scenario = generateScenarioForExistingForces(scenarioCoords, track.getAssignedCoordForces().get(scenarioCoords), contract, campaign, track); - generatedScenarios.add(scenario); + + if (scenario != null) { + generatedScenarios.add(scenario); + } continue; } @@ -164,7 +168,10 @@ public static void generateScenariosForTrack(Campaign campaign, AtBContract cont } StratconScenario scenario = setupScenario(scenarioCoords, randomForceID, campaign, contract, track); - generatedScenarios.add(scenario); + + if (scenario != null) { + generatedScenarios.add(scenario); + } } } @@ -190,31 +197,44 @@ public static void generateScenariosForTrack(Campaign campaign, AtBContract cont } /** - * Creates a new StratCon scenario, placing it in an unoccupied location on the specified track. - * If no track is specified, a random one will be chosen. - * An optional scenario template can be applied. - * This method is based on {@code generateScenariosForTrack()}, designed to simplify the - * process by which external classes can add new StratCon scenarios. + * Generates a StratCon scenario. + * This is a utility method that allows us to generate a scenario quickly without specifying + * track state and scenario template. * - * @param campaign The campaign object encapsulating the current campaign state. - * @param contract The contract associated with the current scenario. - * @param track The {@link StratconTrackState} the scenario should be assigned to, or - * {@code null} to select a random track. - * @param template A specific {@link ScenarioTemplate} to use for scenario generation, - * or {@code null} to select scenario template randomly. + * @param campaign The current campaign. + * @param contract The contract associated with the scenario. + * @return A newly generated {@link StratconScenario}, or {@code null} if scenario creation fails. */ - public static void generateExternalScenario(Campaign campaign, AtBContract contract, - @Nullable StratconTrackState track, @Nullable ScenarioTemplate template) { + public static @Nullable StratconScenario generateExternalScenario(Campaign campaign, AtBContract contract) { + return generateExternalScenario(campaign, contract, null, null, null); + } + + /** + * Generates a new StratCon scenario using advanced configuration. + * It provides a scenario based on a given campaign, contract, track, template. + * This is meant for scenario control on a higher level than the overloading methods. + * + * @param campaign The current campaign. + * @param contract The contract associated with the scenario. + * @param track The {@link StratconTrackState} the scenario should be assigned to, or + * {@code null} to select a random track. + * @param scenarioCoords The {@link StratconCoords} where in the track to place the scenario, or + * {@code null} to select a random hex. If populated, {@code track} cannot be + * {@code null} + * @param template A specific {@link ScenarioTemplate} to use for scenario generation, + * or {@code null} to select scenario template randomly. + * @return A newly generated {@link StratconScenario}, or {@code null} if scenario creation fails. + */ + public static @Nullable StratconScenario generateExternalScenario(Campaign campaign, AtBContract contract, + @Nullable StratconTrackState track, @Nullable StratconCoords scenarioCoords, + @Nullable ScenarioTemplate template) { // If we're not generating for a specific track, randomly pick one. if (track == null) { - List tracks = contract.getStratconCampaignState().getTracks(); - Random rand = new Random(); - - if (!tracks.isEmpty()) { - track = tracks.get(rand.nextInt(tracks.size())); - } else { - logger.error("No tracks available. Aborting scenario generation."); - return; + track = getRandomTrack(contract); + + if (track == null) { + logger.error("Failed to generate a random track, aborting scenario generation."); + return null; } } @@ -226,11 +246,13 @@ public static void generateExternalScenario(Campaign campaign, AtBContract contr Map> sortedAvailableForceIDs = sortForcesByMapType(availableForceIDs, campaign); // Select the target coords. - StratconCoords scenarioCoords = getUnoccupiedCoords(track); + if (scenarioCoords == null) { + scenarioCoords = getUnoccupiedCoords(track); + } if (scenarioCoords == null) { - logger.warn("Target track is full, aborting scenario generation"); - return; + logger.warn("Target track is full, aborting scenario generation."); + return null; } // If forces are already assigned to the target coordinates, use those instead of randomly @@ -266,14 +288,104 @@ public static void generateExternalScenario(Campaign campaign, AtBContract contr // If we haven't generated a scenario yet, it's because we need to pick a random force. if (scenario == null) { - int randomForceIndex = Compute.randomInt(availableForceIDs.size()); - int randomForceID = availableForceIDs.get(randomForceIndex); + int availableForces = availableForceIDs.size(); + int randomForceID = FORCE_NONE; + + if (availableForces > 0) { + int randomForceIndex = Compute.randomInt(availableForces); + randomForceID = availableForceIDs.get(randomForceIndex); + } scenario = setupScenario(scenarioCoords, randomForceID, campaign, contract, track, template); } + if (scenario == null) { + return null; + } + // We end by finalizing the scenario finalizeBackingScenario(campaign, contract, track, autoAssignLances, scenario); + + // We return the scenario in case we want to make specific changes. + return scenario; + } + + /** + * Adds a {@link StratconScenario} to the specified contract. This scenario is cloaked so will + * not be visible until the player uncovers it. + * If no {@link StratconTrackState} or {@link ScenarioTemplate} is provided, random one will be + * picked. + * + * @param campaign The current campaign. + * @param contract The {@link AtBContract} associated with the scenario. + * @param trackState The {@link StratconTrackState} in which the scenario occurs. + * If {@code null}, a random trackState is selected. + * @param template The {@link ScenarioTemplate} for the scenario. + * If {@code null}, the default template is used. + * + * @return The created {@link StratconScenario} or @code null}, + * if no {@link ScenarioTemplate} is found or if all coordinates in the provided + * {@link StratconTrackState} are occupied (and therefore, scenario placement is not possible). + */ + public static @Nullable StratconScenario addHiddenExternalScenario(Campaign campaign, AtBContract contract, + @Nullable StratconTrackState trackState, + @Nullable ScenarioTemplate template) { + // If we're not generating for a specific track, randomly pick one. + if (trackState == null) { + trackState = getRandomTrack(contract); + + if (trackState == null) { + logger.error("Failed to generate a random track, aborting scenario generation."); + return null; + } + } + + StratconCoords coords = getUnoccupiedCoords(trackState); + + if (coords == null) { + logger.error(String.format("Unable to place objective scenario on track %s," + + " as all coords were occupied. Aborting.", + trackState.getDisplayableName())); + return null; + } + + // create scenario - don't assign a force yet + StratconScenario scenario = StratconRulesManager.generateScenario(campaign, contract, + trackState, FORCE_NONE, coords, template); + + if (scenario == null) { + return null; + } + + // clear dates, because we don't want the scenario disappearing on us + scenario.setDeploymentDate(null); + scenario.setActionDate(null); + scenario.setReturnDate(null); + scenario.setStrategicObjective(true); + scenario.getBackingScenario().setCloaked(true); + + trackState.addScenario(scenario); + + return scenario; + } + + /** + * Fetches a random {@link StratconTrackState} from the {@link StratconCampaignState}. + * If no tracks are present, it logs an error message and returns {@code null}. + * + * @param contract The {@link AtBContract} from which the track state will be fetched. + * @return The randomly chosen {@link StratconTrackState}, or {@code null} if no tracks are available. + */ + public static @Nullable StratconTrackState getRandomTrack(AtBContract contract) { + List tracks = contract.getStratconCampaignState().getTracks(); + Random rand = new Random(); + + if (!tracks.isEmpty()) { + return tracks.get(rand.nextInt(tracks.size())); + } else { + logger.error("No tracks available. Unable to fetch random track"); + return null; + } } /** @@ -291,7 +403,7 @@ private static void finalizeBackingScenario(Campaign campaign, AtBContract contr StratconScenario scenario) { AtBDynamicScenarioFactory.finalizeScenario(scenario.getBackingScenario(), contract, campaign); setScenarioParametersFromBiome(track, scenario); - swapInPlayerUnits(scenario, campaign, Force.FORCE_NONE); + swapInPlayerUnits(scenario, campaign, FORCE_NONE); if (!autoAssignLances && !scenario.ignoreForceAutoAssignment()) { for (int forceID : scenario.getPlayerTemplateForceIDs()) { @@ -399,7 +511,7 @@ private static void swapInPlayerUnits(StratconScenario scenario, Campaign campai Collection potentialUnits = new HashSet<>(); // find units in player's campaign by default, all units in the TO&E are eligible - if (explicitForceID == Force.FORCE_NONE) { + if (explicitForceID == FORCE_NONE) { for (UUID unitId : campaign.getForces().getUnits()) { try { potentialUnits.add(campaign.getUnit(unitId)); @@ -457,7 +569,7 @@ private static void swapInPlayerUnits(StratconScenario scenario, Campaign campai * @param track The relevant StratCon track. * @return The newly generated {@link StratconScenario}. */ - public static StratconScenario generateScenarioForExistingForces(StratconCoords scenarioCoords, + public static @Nullable StratconScenario generateScenarioForExistingForces(StratconCoords scenarioCoords, Set forceIDs, AtBContract contract, Campaign campaign, StratconTrackState track) { return generateScenarioForExistingForces(scenarioCoords, forceIDs, contract, campaign, @@ -478,7 +590,7 @@ public static StratconScenario generateScenarioForExistingForces(StratconCoords * select a random template. * @return The newly generated {@link StratconScenario}. */ - public static StratconScenario generateScenarioForExistingForces(StratconCoords scenarioCoords, + public static @Nullable StratconScenario generateScenarioForExistingForces(StratconCoords scenarioCoords, Set forceIDs, AtBContract contract, Campaign campaign, StratconTrackState track, @Nullable ScenarioTemplate template) { boolean firstForce = true; @@ -488,6 +600,10 @@ public static StratconScenario generateScenarioForExistingForces(StratconCoords if (firstForce) { scenario = setupScenario(scenarioCoords, forceID, campaign, contract, track, template); firstForce = false; + + if (scenario == null) { + return null; + } } else { scenario.incrementRequiredPlayerLances(); scenario.addPrimaryForce(forceID); @@ -563,7 +679,7 @@ public static void deployForceToCoords(StratconCoords coords, int forceID, Campa * @param track The relevant StratCon track. * @return The newly set up {@link StratconScenario}. */ - private static StratconScenario setupScenario(StratconCoords coords, int forceID, Campaign campaign, + private static @Nullable StratconScenario setupScenario(StratconCoords coords, int forceID, Campaign campaign, AtBContract contract, StratconTrackState track) { return setupScenario(coords, forceID, campaign, contract, track, null); } @@ -586,8 +702,9 @@ private static StratconScenario setupScenario(StratconCoords coords, int forceID * {@code null} to select the scenario template randomly. * @return The newly set up {@link StratconScenario}. */ - private static StratconScenario setupScenario(StratconCoords coords, int forceID, Campaign campaign, - AtBContract contract, StratconTrackState track, @Nullable ScenarioTemplate template) { + private static @Nullable StratconScenario setupScenario(StratconCoords coords, int forceID, Campaign campaign, + AtBContract contract, StratconTrackState track, + @Nullable ScenarioTemplate template) { StratconScenario scenario; if (track.getFacilities().containsKey(coords)) { @@ -603,6 +720,10 @@ private static StratconScenario setupScenario(StratconCoords coords, int forceID scenario = generateScenario(campaign, contract, track, forceID, coords); } + if (scenario == null) { + return null; + } + // we may generate a facility scenario randomly - if so, do the facility-related // stuff and add a new facility to the track if (scenario.getBackingScenario().getTemplate().isFacilityScenario()) { @@ -911,10 +1032,17 @@ public static void commitPrimaryForces(Campaign campaign, StratconScenario scena * is on defence */ private static boolean commanderLanceHasDefensiveAssignment(AtBDynamicScenario scenario, Campaign campaign) { - Unit commanderUnit = scenario.getLanceCommander(campaign).getUnit(); - Lance lance = campaign.getLances().get(commanderUnit.getForceId()); + Person lanceCommander = scenario.getLanceCommander(campaign); + if (lanceCommander != null){ + Unit commanderUnit = lanceCommander.getUnit(); + if (commanderUnit != null) { + Lance lance = campaign.getLances().get(commanderUnit.getForceId()); + + return (lance != null) && lance.getRole().isDefence(); + } + } - return (lance != null) && lance.getRole().isDefence(); + return false; } /** @@ -960,7 +1088,7 @@ private static Map> sortForcesByMapType(List * given force, on the * given track. Also registers it with the track and campaign. */ - private static StratconScenario generateScenario(Campaign campaign, AtBContract contract, StratconTrackState track, + private static @Nullable StratconScenario generateScenario(Campaign campaign, AtBContract contract, StratconTrackState track, int forceID, StratconCoords coords) { int unitType = campaign.getForce(forceID).getPrimaryUnitType(campaign); ScenarioTemplate template = StratconScenarioFactory.getRandomScenario(unitType); @@ -976,10 +1104,27 @@ private static StratconScenario generateScenario(Campaign campaign, AtBContract * given force, on the * given track, using the given template. Also registers it with the campaign. */ - static StratconScenario generateScenario(Campaign campaign, AtBContract contract, StratconTrackState track, + static @Nullable StratconScenario generateScenario(Campaign campaign, AtBContract contract, StratconTrackState track, int forceID, StratconCoords coords, ScenarioTemplate template) { StratconScenario scenario = new StratconScenario(); + if (template == null) { + int unitType = UnitType.MEK; + + try { + unitType = campaign.getForce(forceID).getPrimaryUnitType(campaign); + } catch (NullPointerException ignored) { + // This just means the player has no units + } + + template = StratconScenarioFactory.getRandomScenario(unitType); + } + + if (template == null) { + logger.error("Failed to fetch random scenario template. Aborting scenario generation."); + return null; + } + AtBDynamicScenario backingScenario = AtBDynamicScenarioFactory.initializeScenarioFromTemplate(template, contract, campaign); scenario.setBackingScenario(backingScenario); @@ -1011,10 +1156,10 @@ static StratconScenario generateScenario(Campaign campaign, AtBContract contract // dates, otherwise, the report messages for new scenarios look weird // also, suppress the "new scenario" report if not generating a scenario // for a specific force, as this indicates a contract initialization - campaign.addScenario(backingScenario, contract, forceID == Force.FORCE_NONE); + campaign.addScenario(backingScenario, contract, forceID == FORCE_NONE); scenario.setBackingScenarioID(backingScenario.getId()); - if (forceID > Force.FORCE_NONE) { + if (forceID > FORCE_NONE) { scenario.addPrimaryForce(forceID); } @@ -1252,7 +1397,6 @@ public static boolean forceCompositionMatchesDeclaredUnitType(int primaryUnitTyp * @return List of available force IDs. */ public static List getAvailableForceIDs(Campaign campaign) { - // first, we gather a set of all forces that are already deployed to a track so // we eliminate those later Set forcesInTracks = campaign.getActiveAtBContracts().stream() @@ -1664,34 +1808,34 @@ public static void updateFacilityForScenario(AtBScenario scenario, AtBContract c * ResolveScenarioTracker.finish() * has been invoked. */ - public static void processScenarioCompletion(ResolveScenarioTracker rst) { - if (rst.getMission() instanceof AtBContract) { - StratconCampaignState campaignState = ((AtBContract) rst.getMission()).getStratconCampaignState(); + public static void processScenarioCompletion(ResolveScenarioTracker tracker) { + Campaign campaign = tracker.getCampaign(); + Mission mission = tracker.getMission(); + + if (mission instanceof AtBContract) { + StratconCampaignState campaignState = ((AtBContract) mission).getStratconCampaignState(); if (campaignState == null) { return; } + Scenario backingScenario = tracker.getScenario(); + + boolean victory = backingScenario.getStatus().isOverallVictory(); + for (StratconTrackState track : campaignState.getTracks()) { - if (track.getBackingScenariosMap().containsKey(rst.getScenario().getId())) { + if (track.getBackingScenariosMap().containsKey(backingScenario.getId())) { // things that may potentially happen: // scenario is removed from track - implemented // track gets remaining forces added to reinforcement pool // facility gets remaining forces stored in reinforcement pool // process VP and SO - StratconScenario scenario = track.getBackingScenariosMap().get(rst.getScenario().getId()); + StratconScenario scenario = track.getBackingScenariosMap().get(backingScenario.getId()); StratconFacility facility = track.getFacility(scenario.getCoords()); - boolean victory = rst.getScenario().getStatus().isOverallVictory(); - boolean draw = rst.getScenario().getStatus().isDraw(); - - if (scenario.isRequiredScenario()) { - if (draw) { - // do nothing - } else { - campaignState.updateVictoryPoints(victory ? 1 : -1); - } + if (scenario.isRequiredScenario() && !backingScenario.getStatus().isDraw()) { + campaignState.updateVictoryPoints(victory ? 1 : -1); } // this must be done before removing the scenario from the track @@ -1702,7 +1846,7 @@ public static void processScenarioCompletion(ResolveScenarioTracker rst) { switchFacilityOwner(facility); } - processTrackForceReturnDates(track, rst.getCampaign()); + processTrackForceReturnDates(track, campaign); track.removeScenario(scenario); break; From 01e323051300a4fba901f21a9368581e1e8f8d60 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Wed, 20 Nov 2024 15:09:47 -0600 Subject: [PATCH 03/19] Add `getSize` method to StratconTrackState Implemented a `getSize` method to calculate the track size based on width and height. This change enhances the functionality by providing a straightforward way to retrieve the total size of the track. Additionally, minor formatting adjustments were made for code consistency. --- .../mekhq/campaign/stratcon/StratconTrackState.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/stratcon/StratconTrackState.java b/MekHQ/src/mekhq/campaign/stratcon/StratconTrackState.java index cdfa08cf01..622dd4c2ca 100644 --- a/MekHQ/src/mekhq/campaign/stratcon/StratconTrackState.java +++ b/MekHQ/src/mekhq/campaign/stratcon/StratconTrackState.java @@ -23,9 +23,9 @@ import jakarta.xml.bind.annotation.XmlRootElement; import jakarta.xml.bind.annotation.XmlTransient; import megamek.common.annotations.Nullable; -import mekhq.utilities.MHQXMLUtility; import mekhq.campaign.mission.ScenarioForceTemplate.ForceAlignment; import mekhq.campaign.stratcon.StratconContractDefinition.StrategicObjectiveType; +import mekhq.utilities.MHQXMLUtility; import java.time.LocalDate; import java.util.*; @@ -107,6 +107,13 @@ public void setHeight(int height) { this.height = height; } + /** + * @return The size of the track derived by multiplying width and height. + */ + public int getSize() { + return width * height; + } + @XmlElementWrapper(name = "trackFacilities") @XmlElement(name = "facility") public Map getFacilities() { @@ -159,7 +166,7 @@ public void removeScenario(int campaignScenarioID) { removeScenario(getBackingScenariosMap().get(campaignScenarioID)); } } - + /** * Removes a StratconScenario from this track. */ @@ -169,7 +176,7 @@ public void removeScenario(StratconScenario scenario) { Map objectives = getObjectivesByCoords(); if (objectives.containsKey(scenario.getCoords())) { StrategicObjectiveType objectiveType = objectives.get(scenario.getCoords()).getObjectiveType(); - + switch (objectiveType) { case RequiredScenarioVictory: case SpecificScenarioVictory: From 08f789bb93283dfcbd9e5237175a34dd922b0a62 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Wed, 20 Nov 2024 15:09:56 -0600 Subject: [PATCH 04/19] Initialize non-objective scenarios for StratCon contracts Added logic to seed sectors with hidden forces based on contract type in StratconContractInitializer. Differentiated scenario creation between garrison, raid, pirate hunting, and planetary assault contracts. --- .../stratcon/StratconContractInitializer.java | 35 +++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java b/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java index 47585721e9..afc2221731 100644 --- a/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java +++ b/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java @@ -25,6 +25,7 @@ import mekhq.campaign.mission.*; import mekhq.campaign.mission.ScenarioForceTemplate.ForceAlignment; import mekhq.campaign.mission.atb.AtBScenarioModifier; +import mekhq.campaign.mission.enums.AtBContractType; import mekhq.campaign.mission.enums.ContractCommandRights; import mekhq.campaign.stratcon.StratconContractDefinition.ObjectiveParameters; import mekhq.campaign.stratcon.StratconContractDefinition.StrategicObjectiveType; @@ -33,6 +34,8 @@ import java.util.Collections; import java.util.List; +import static mekhq.campaign.stratcon.StratconRulesManager.addHiddenExternalScenario; + /** * This class handles StratCon state initialization when a contract is signed. */ @@ -185,6 +188,36 @@ public static void initializeCampaignState(AtBContract contract, Campaign campai false, Collections.emptyList()); } + // Initialize non-objective scenarios + for (StratconTrackState track : campaignState.getTracks()) { + AtBContractType contractType = contract.getContractType(); + + // If the contract is a garrison type, we don't want to generate what will appear to be + // a full-scale invasion on day one. + if (contractType.isGarrisonType()) { + break; + } + + // otherwise, seed each sector with hidden forces. + // the number of hidden forces is dependent on the type of contract. + final int OFFENSIVE_MULTIPLIER = 10; + final int DEFENSIVE_MULTIPLIER = 20; + + int multiplier = DEFENSIVE_MULTIPLIER; + + if (contractType.isRaidType() || contractType.isPirateHunting()) { + multiplier = OFFENSIVE_MULTIPLIER; + } else if (contract.getContractType().isPlanetaryAssault()) { + multiplier = OFFENSIVE_MULTIPLIER / 2; + } + + int preDeployedScenarios = track.getSize() / multiplier; + + for (int i = 0; i < preDeployedScenarios; i++) { + addHiddenExternalScenario(campaign, contract, track, null); + } + } + // clean up objectives for integrated command: // we're still going to have all the objective facilities and scenarios // but the player has no control over where they go, so they're @@ -194,8 +227,6 @@ public static void initializeCampaignState(AtBContract contract, Campaign campai for (StratconTrackState track : campaignState.getTracks()) { track.getStrategicObjectives().clear(); } - } else { - // Initialize non-objective scenarios } // now we're done From 127d15d548a811afa1ad882fb108300876bd792b Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Wed, 20 Nov 2024 15:19:19 -0600 Subject: [PATCH 05/19] Update contract initializers for pirate and guerrilla warfare Modified contract generation to prevent full-scale invasions for pirate hunting contracts. Adjusted multiplier settings to treat guerrilla warfare similarly to raid types. These changes enhance the accuracy and realism of mission deployment scenarios. --- .../campaign/stratcon/StratconContractInitializer.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java b/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java index afc2221731..3f5cee9193 100644 --- a/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java +++ b/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java @@ -193,8 +193,9 @@ public static void initializeCampaignState(AtBContract contract, Campaign campai AtBContractType contractType = contract.getContractType(); // If the contract is a garrison type, we don't want to generate what will appear to be - // a full-scale invasion on day one. - if (contractType.isGarrisonType()) { + // a full-scale invasion on day one. Furthermore, Pirates do not have enough resources + // to deploy standing forces in this manner. + if (contractType.isGarrisonType() || contractType.isPirateHunting()) { break; } @@ -205,7 +206,7 @@ public static void initializeCampaignState(AtBContract contract, Campaign campai int multiplier = DEFENSIVE_MULTIPLIER; - if (contractType.isRaidType() || contractType.isPirateHunting()) { + if (contractType.isRaidType() || contractType.isGuerrillaWarfare()) { multiplier = OFFENSIVE_MULTIPLIER; } else if (contract.getContractType().isPlanetaryAssault()) { multiplier = OFFENSIVE_MULTIPLIER / 2; From 01a83090a527b981b33e81cca874c35f4f3f6554 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Wed, 20 Nov 2024 18:24:27 -0600 Subject: [PATCH 06/19] Refactored scenario generation logic in Stratcon modules Removed redundant scenario generation checks and refactored pre-deployment probability calculations. Simplified conditionals in StratconRulesManager, and integrated scenario odds computation into StratconContractInitializer. This enhances code clarity and consistency in scenario management. --- .../campaign/stratcon/StratconContractInitializer.java | 6 +++++- .../mekhq/campaign/stratcon/StratconRulesManager.java | 10 +--------- .../mekhq/gui/stratcon/CampaignManagementDialog.java | 2 +- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java b/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java index 3f5cee9193..2db5df01f9 100644 --- a/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java +++ b/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java @@ -34,7 +34,9 @@ import java.util.Collections; import java.util.List; +import static java.lang.Math.round; import static mekhq.campaign.stratcon.StratconRulesManager.addHiddenExternalScenario; +import static mekhq.campaign.stratcon.StratconRulesManager.calculateScenarioOdds; /** * This class handles StratCon state initialization when a contract is signed. @@ -213,6 +215,8 @@ public static void initializeCampaignState(AtBContract contract, Campaign campai } int preDeployedScenarios = track.getSize() / multiplier; + preDeployedScenarios = (int) round(preDeployedScenarios + * ((double) calculateScenarioOdds(track, contract, false) / 100)); for (int i = 0; i < preDeployedScenarios; i++) { addHiddenExternalScenario(campaign, contract, track, null); @@ -255,7 +259,7 @@ public static StratconTrackState initializeTrackState(int numLances, int scenari planetSurfaceArea /= 1000000; // set width and height - int numHexes = (int) Math.round(planetSurfaceArea); + int numHexes = (int) round(planetSurfaceArea); int height = (int) Math.floor(Math.sqrt(numHexes)); int width = numHexes / height; retVal.setWidth(width); diff --git a/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java b/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java index 59e3f2d96e..22bcc26096 100644 --- a/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java +++ b/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java @@ -649,22 +649,14 @@ public static void deployForceToCoords(StratconCoords coords, int forceID, Campa // don't create a scenario on top of allied facilities StratconFacility facility = track.getFacility(coords); boolean isNonAlliedFacility = (facility != null) && (facility.getOwner() != ForceAlignment.Allied); - int targetNum = calculateScenarioOdds(track, contract, true); - boolean spawnScenario = (facility == null) && (Compute.randomInt(100) <= targetNum); - if (isNonAlliedFacility || spawnScenario) { + if (isNonAlliedFacility) { StratconScenario scenario = setupScenario(coords, forceID, campaign, contract, track); // we deploy immediately in this case, since we deployed the force manually setScenarioDates(0, track, campaign, scenario); AtBDynamicScenarioFactory.finalizeScenario(scenario.getBackingScenario(), contract, campaign); setScenarioParametersFromBiome(track, scenario); - // if we wound up with a field scenario, we may sub in dropships carrying - // units of the force in question - if (spawnScenario && !isNonAlliedFacility) { - swapInPlayerUnits(scenario, campaign, forceID); - } - commitPrimaryForces(campaign, scenario, track); } } diff --git a/MekHQ/src/mekhq/gui/stratcon/CampaignManagementDialog.java b/MekHQ/src/mekhq/gui/stratcon/CampaignManagementDialog.java index 89522fe938..60de457509 100644 --- a/MekHQ/src/mekhq/gui/stratcon/CampaignManagementDialog.java +++ b/MekHQ/src/mekhq/gui/stratcon/CampaignManagementDialog.java @@ -55,7 +55,7 @@ public void display(StratconCampaignState campaignState, StratconTrackState curr lblTrackScenarioOdds.setVisible(gmMode); if (gmMode) { - lblTrackScenarioOdds.setText(String.format("Track Scenario Odds: %d%%", + lblTrackScenarioOdds.setText(String.format("Track Reinforcement Odds: %d%%", StratconRulesManager.calculateScenarioOdds(currentTrack, campaignState.getContract(), false))); } } From 278f10a63232e2592da26c980e993b80b64ed6f0 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Wed, 20 Nov 2024 19:06:05 -0600 Subject: [PATCH 07/19] Add option for scenarios to spawn on player facilities Modified scenario generation to include an option for allowing scenarios to spawn on top of player-allied facilities. Updated function signatures and added logic to accommodate this new parameter, ensuring that scenarios can now be placed more flexibly based on provided configurations. --- .../stratcon/StratconContractInitializer.java | 72 +++++++++++++------ .../stratcon/StratconRulesManager.java | 19 +++-- 2 files changed, 63 insertions(+), 28 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java b/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java index 2db5df01f9..d51f345fb2 100644 --- a/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java +++ b/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java @@ -19,6 +19,7 @@ package mekhq.campaign.stratcon; import megamek.common.Compute; +import megamek.common.annotations.Nullable; import megamek.logging.MMLogger; import mekhq.campaign.Campaign; import mekhq.campaign.force.Force; @@ -33,6 +34,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Random; import static java.lang.Math.round; import static mekhq.campaign.stratcon.StratconRulesManager.addHiddenExternalScenario; @@ -219,7 +221,7 @@ public static void initializeCampaignState(AtBContract contract, Campaign campai * ((double) calculateScenarioOdds(track, contract, false) / 100)); for (int i = 0; i < preDeployedScenarios; i++) { - addHiddenExternalScenario(campaign, contract, track, null); + addHiddenExternalScenario(campaign, contract, track, null, false); } } @@ -330,7 +332,7 @@ private static void initializeTrackFacilities(StratconTrackState trackState, int sf.setStrategicObjective(strategicObjective); sf.getLocalModifiers().addAll(modifiers); - StratconCoords coords = getUnoccupiedCoords(trackState); + StratconCoords coords = getUnoccupiedCoords(trackState, false); if (coords == null) { logger.warn(String.format("Unable to place facility on track %s," + @@ -384,7 +386,7 @@ private static void initializeObjectiveScenarios(Campaign campaign, AtBContract ScenarioTemplate template = StratconScenarioFactory.getSpecificScenario( objectiveScenarios.get(Compute.randomInt(objectiveScenarios.size()))); - StratconCoords coords = getUnoccupiedCoords(trackState); + StratconCoords coords = getUnoccupiedCoords(trackState, false); if (coords == null) { logger.error(String.format("Unable to place objective scenario on track %s," + @@ -430,30 +432,56 @@ private static void initializeObjectiveScenarios(Campaign campaign, AtBContract } /** - * Utility function that, given a track state, picks a random set of unoccupied - * coordinates. + * Searches for a suitable coordinate on the given {@link StratconTrackState}. + * The suitability of a coordinate is determined by the absence of a scenario in the coordinate + * and the absence of a facility or the presence of a player-allied facility (determined by the + * value of allowPlayerFacilities). + *

+ * The method iterates through all possible coordinates and shuffles them to ensure randomness. + * A random coordinate is then selected from the list of suitable coordinates and returned. + * + * @param trackState the {@link StratconTrackState} object on which to perform the search + * @param allowPlayerFacilities a {@link boolean} value indicating whether player-owned facilities + * should be considered suitable + * @return a {@link StratconCoords} object representing the coordinates of a suitable location, + * or {@code null} if no suitable location was found */ - public static StratconCoords getUnoccupiedCoords(StratconTrackState trackState) { - // Maximum number of attempts - int maxAttempts = trackState.getWidth() * trackState.getHeight(); - int attempts = 0; - - int x = Compute.randomInt(trackState.getWidth()); - int y = Compute.randomInt(trackState.getHeight()); - StratconCoords coords = new StratconCoords(x, y); - - while ((trackState.getFacility(coords) != null || trackState.getScenario(coords) != null) && attempts < maxAttempts) { - x = Compute.randomInt(trackState.getWidth()); - y = Compute.randomInt(trackState.getHeight()); - coords = new StratconCoords(x, y); - attempts++; + public static @Nullable StratconCoords getUnoccupiedCoords(StratconTrackState trackState, boolean allowPlayerFacilities) { + int trackHeight = trackState.getHeight(); + int trackWidth = trackState.getWidth(); + + List suitableCoords = new ArrayList<>(); + + for (int y = 0; y < trackHeight; y++) { + for (int x = 0; x < trackWidth; x++) { + StratconCoords coords = new StratconCoords(x, y); + + if (trackState.getScenario(coords) == null) { + suitableCoords.add(coords); + continue; + } + + if (trackState.getFacility(coords) == null) { + suitableCoords.add(coords); + continue; + } + + if (trackState.getFacility(coords).getOwner() != ForceAlignment.Opposing) { + if (allowPlayerFacilities) { + suitableCoords.add(coords); + } + } + } } - if (attempts == maxAttempts) { + Collections.shuffle(suitableCoords); + + if (suitableCoords.isEmpty()) { return null; + } else { + int randomIndex = new Random().nextInt(suitableCoords.size()); + return suitableCoords.get(randomIndex); } - - return coords; } /** diff --git a/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java b/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java index 22bcc26096..bf5455ba70 100644 --- a/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java +++ b/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java @@ -120,6 +120,7 @@ public static void generateScenariosForTrack(Campaign campaign, AtBContract cont } else if (contract.getMoraleLevel().isOverwhelming()) { scenarioRolls += 2; } + for (int scenarioIndex = 0; scenarioIndex < scenarioRolls; scenarioIndex++) { int targetNum = calculateScenarioOdds(track, contract, false); @@ -127,7 +128,7 @@ public static void generateScenariosForTrack(Campaign campaign, AtBContract cont // generate a scenario if (!availableForceIDs.isEmpty() && (Compute.randomInt(100) < targetNum)) { // pick random coordinates and force to drive the scenario - StratconCoords scenarioCoords = getUnoccupiedCoords(track); + StratconCoords scenarioCoords = getUnoccupiedCoords(track, true); if (scenarioCoords == null) { logger.warn("Target track is full, skipping scenario generation"); @@ -206,7 +207,8 @@ public static void generateScenariosForTrack(Campaign campaign, AtBContract cont * @return A newly generated {@link StratconScenario}, or {@code null} if scenario creation fails. */ public static @Nullable StratconScenario generateExternalScenario(Campaign campaign, AtBContract contract) { - return generateExternalScenario(campaign, contract, null, null, null); + return generateExternalScenario(campaign, contract, null, null, + null, false); } /** @@ -223,11 +225,13 @@ public static void generateScenariosForTrack(Campaign campaign, AtBContract cont * {@code null} * @param template A specific {@link ScenarioTemplate} to use for scenario generation, * or {@code null} to select scenario template randomly. + * @param allowPlayerFacilities Whether the scenario is allowed to spawn on top of + * player-allied facilities. * @return A newly generated {@link StratconScenario}, or {@code null} if scenario creation fails. */ public static @Nullable StratconScenario generateExternalScenario(Campaign campaign, AtBContract contract, @Nullable StratconTrackState track, @Nullable StratconCoords scenarioCoords, - @Nullable ScenarioTemplate template) { + @Nullable ScenarioTemplate template, boolean allowPlayerFacilities) { // If we're not generating for a specific track, randomly pick one. if (track == null) { track = getRandomTrack(contract); @@ -247,7 +251,7 @@ public static void generateScenariosForTrack(Campaign campaign, AtBContract cont // Select the target coords. if (scenarioCoords == null) { - scenarioCoords = getUnoccupiedCoords(track); + scenarioCoords = getUnoccupiedCoords(track, allowPlayerFacilities); } if (scenarioCoords == null) { @@ -322,6 +326,8 @@ public static void generateScenariosForTrack(Campaign campaign, AtBContract cont * If {@code null}, a random trackState is selected. * @param template The {@link ScenarioTemplate} for the scenario. * If {@code null}, the default template is used. + * @param allowPlayerFacilities Whether the scenario is allowed to spawn on top of + * player-allied facilities. * * @return The created {@link StratconScenario} or @code null}, * if no {@link ScenarioTemplate} is found or if all coordinates in the provided @@ -329,7 +335,8 @@ public static void generateScenariosForTrack(Campaign campaign, AtBContract cont */ public static @Nullable StratconScenario addHiddenExternalScenario(Campaign campaign, AtBContract contract, @Nullable StratconTrackState trackState, - @Nullable ScenarioTemplate template) { + @Nullable ScenarioTemplate template, + boolean allowPlayerFacilities) { // If we're not generating for a specific track, randomly pick one. if (trackState == null) { trackState = getRandomTrack(contract); @@ -340,7 +347,7 @@ public static void generateScenariosForTrack(Campaign campaign, AtBContract cont } } - StratconCoords coords = getUnoccupiedCoords(trackState); + StratconCoords coords = getUnoccupiedCoords(trackState, allowPlayerFacilities); if (coords == null) { logger.error(String.format("Unable to place objective scenario on track %s," + From 19ad22066aeb50938e78a3772cd9515e5a0a8fe5 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Wed, 20 Nov 2024 21:58:43 -0600 Subject: [PATCH 08/19] Refactor scenario odds and add daily movement processing Refactored the calculation of scenario odds for better readability and maintainability. Also added a new method to handle daily movement processing for scenarios in each track. Expanded pre-deployment logic and cleaned up code to enhance functionality. --- .../mekhq/campaign/mission/AtBContract.java | 16 ++- .../stratcon/StratconContractInitializer.java | 107 +++++++++++----- .../stratcon/StratconRulesManager.java | 115 ++++++++++++------ .../stratcon/CampaignManagementDialog.java | 3 +- 4 files changed, 172 insertions(+), 69 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/mission/AtBContract.java b/MekHQ/src/mekhq/campaign/mission/AtBContract.java index 9b45452716..82c298ddf7 100644 --- a/MekHQ/src/mekhq/campaign/mission/AtBContract.java +++ b/MekHQ/src/mekhq/campaign/mission/AtBContract.java @@ -52,6 +52,7 @@ import mekhq.campaign.stratcon.StratconCampaignState; import mekhq.campaign.stratcon.StratconContractDefinition; import mekhq.campaign.stratcon.StratconContractInitializer; +import mekhq.campaign.stratcon.StratconTrackState; import mekhq.campaign.unit.Unit; import mekhq.campaign.universe.Faction; import mekhq.campaign.universe.Factions; @@ -431,6 +432,7 @@ public void checkMorale(Campaign campaign, LocalDate today) { if (today.isAfter(routEnd)) { setMoraleLevel(AtBMoraleLevel.STALEMATE); routEnd = null; + updateEnemy(campaign, today); // mix it up a little } else { setMoraleLevel(AtBMoraleLevel.ROUTED); @@ -493,8 +495,20 @@ public void checkMorale(Campaign campaign, LocalDate today) { miscModifiers += 2; } + int balanceOfPower = 0; + if (campaign.getCampaignOptions().isUseStratCon()) { + balanceOfPower = -campaign.getLanceList().size() * 5; + + int enemyForceCount = 0; + for (StratconTrackState track : getStratconCampaignState().getTracks()) { + enemyForceCount = track.getScenarios().size(); + } + + balanceOfPower += enemyForceCount; + } + // Total morale modifier calculation - int totalModifier = enemySkillModifier - allySkillModifier + performanceModifier + miscModifiers; + int totalModifier = enemySkillModifier - allySkillModifier + performanceModifier + miscModifiers + balanceOfPower; int roll = Compute.d6(2) + totalModifier; // Morale level determination based on roll value diff --git a/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java b/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java index d51f345fb2..5718e62004 100644 --- a/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java +++ b/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java @@ -37,6 +37,7 @@ import java.util.Random; import static java.lang.Math.round; +import static megamek.common.Coords.ALL_DIRECTIONS; import static mekhq.campaign.stratcon.StratconRulesManager.addHiddenExternalScenario; import static mekhq.campaign.stratcon.StratconRulesManager.calculateScenarioOdds; @@ -194,35 +195,9 @@ public static void initializeCampaignState(AtBContract contract, Campaign campai // Initialize non-objective scenarios for (StratconTrackState track : campaignState.getTracks()) { - AtBContractType contractType = contract.getContractType(); - - // If the contract is a garrison type, we don't want to generate what will appear to be - // a full-scale invasion on day one. Furthermore, Pirates do not have enough resources - // to deploy standing forces in this manner. - if (contractType.isGarrisonType() || contractType.isPirateHunting()) { + if (seedPreDeployedForces(contract, campaign, track)) { break; } - - // otherwise, seed each sector with hidden forces. - // the number of hidden forces is dependent on the type of contract. - final int OFFENSIVE_MULTIPLIER = 10; - final int DEFENSIVE_MULTIPLIER = 20; - - int multiplier = DEFENSIVE_MULTIPLIER; - - if (contractType.isRaidType() || contractType.isGuerrillaWarfare()) { - multiplier = OFFENSIVE_MULTIPLIER; - } else if (contract.getContractType().isPlanetaryAssault()) { - multiplier = OFFENSIVE_MULTIPLIER / 2; - } - - int preDeployedScenarios = track.getSize() / multiplier; - preDeployedScenarios = (int) round(preDeployedScenarios - * ((double) calculateScenarioOdds(track, contract, false) / 100)); - - for (int i = 0; i < preDeployedScenarios; i++) { - addHiddenExternalScenario(campaign, contract, track, null, false); - } } // clean up objectives for integrated command: @@ -239,6 +214,39 @@ public static void initializeCampaignState(AtBContract contract, Campaign campai // now we're done } + public static boolean seedPreDeployedForces(AtBContract contract, Campaign campaign, StratconTrackState track) { + AtBContractType contractType = contract.getContractType(); + + // If the contract is a garrison type, we don't want to generate what will appear to be + // a full-scale invasion on day one. Furthermore, Pirates do not have enough resources + // to deploy standing forces in this manner. + if (contractType.isGarrisonType() || contractType.isPirateHunting()) { + return true; + } + + // otherwise, seed each sector with hidden forces. + // the number of hidden forces is dependent on the type of contract. + final int OFFENSIVE_MULTIPLIER = 10; + final int DEFENSIVE_MULTIPLIER = 20; + + int multiplier = DEFENSIVE_MULTIPLIER; + + if (contractType.isRaidType() || contractType.isGuerrillaWarfare()) { + multiplier = OFFENSIVE_MULTIPLIER; + } else if (contract.getContractType().isPlanetaryAssault()) { + multiplier = OFFENSIVE_MULTIPLIER / 2; + } + + int preDeployedScenarios = track.getSize() / multiplier; + preDeployedScenarios = (int) round(preDeployedScenarios + * ((double) calculateScenarioOdds(track, contract, false) / 100)); + + for (int i = 0; i < preDeployedScenarios; i++) { + addHiddenExternalScenario(campaign, contract, track, null, false); + } + return false; + } + /** * Set up initial state of a track, dimensions are based on number of assigned * lances. @@ -456,8 +464,7 @@ private static void initializeObjectiveScenarios(Campaign campaign, AtBContract for (int x = 0; x < trackWidth; x++) { StratconCoords coords = new StratconCoords(x, y); - if (trackState.getScenario(coords) == null) { - suitableCoords.add(coords); + if (trackState.getScenario(coords) != null) { continue; } @@ -484,6 +491,48 @@ private static void initializeObjectiveScenarios(Campaign campaign, AtBContract } } + /** + * Searches for and returns a suitable adjacent coordinate to the given origin coordinates on + * the provided {@link StratconTrackState}. + * The method checks all the possible directions and considers a coordinate suitable if it + * doesn't contain a scenario and if it either doesn't contain a facility or contains a + * player-allied one. + * If there are multiple suitable coordinates, one is chosen at random. + * + * @param originCoords the {@link StratconCoords} around which to search for a suitable coordinate + * @param trackState the {@link StratconTrackState} on which to perform the search + * @return a {@link StratconCoords} object representing the coordinates of a suitable adjacent + * location, or {code null} if no suitable location was found. + */ + public static @Nullable StratconCoords getUnoccupiedAdjacentCoords(StratconCoords originCoords, + StratconTrackState trackState) { + List suitableCoords = new ArrayList<>(); + + for (int direction : ALL_DIRECTIONS) { + StratconCoords newCoords = originCoords.translate(direction); + + if (trackState.getScenario(newCoords) != null) { + continue; + } + + if (trackState.getFacility(newCoords) == null) { + suitableCoords.add(newCoords); + continue; + } + + if (trackState.getFacility(newCoords).getOwner() != ForceAlignment.Opposing) { + suitableCoords.add(newCoords); + } + } + + if (suitableCoords.isEmpty()) { + return null; + } + + int randomIndex = new Random().nextInt(suitableCoords.size()); + return suitableCoords.get(randomIndex); + } + /** * Given a mission (that's an AtB contract), restore track state information, * such as pointers from StratCon scenario objects to AtB scenario objects. diff --git a/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java b/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java index bf5455ba70..70295c0c81 100644 --- a/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java +++ b/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java @@ -57,6 +57,7 @@ import static mekhq.campaign.mission.ScenarioMapParameters.MapLocation.LowAtmosphere; import static mekhq.campaign.mission.ScenarioMapParameters.MapLocation.Space; import static mekhq.campaign.mission.ScenarioMapParameters.MapLocation.SpecificGroundTerrain; +import static mekhq.campaign.stratcon.StratconContractInitializer.getUnoccupiedAdjacentCoords; import static mekhq.campaign.stratcon.StratconContractInitializer.getUnoccupiedCoords; /** @@ -1709,49 +1710,37 @@ public static boolean canManuallyDeployAnyForce(StratconCoords coords, * figure out the odds of a scenario occurring. */ public static int calculateScenarioOdds(StratconTrackState track, AtBContract contract, - boolean playerDeployingForce) { - // rules: - // ROUTED: 0% - // CRITICAL: -10% when deploying forces to track, 0% attack - // WEAKENED: -5% - // ADVANCING: +5% - // DOMINATING: +10% - // OVERWHELMING: +100% - int moraleModifier = 0; - - switch (contract.getMoraleLevel()) { - case ROUTED: - return 0; - case CRITICAL: - if (playerDeployingForce) { - moraleModifier = -10; + boolean isReinforcements) { + if (contract.getMoraleLevel().isRouted()) { + return 0; + } + + int moraleModifier = switch (contract.getMoraleLevel()) { + case CRITICAL -> { + if (isReinforcements) { + yield -10; } else { - return 0; + yield 0; } - break; - case WEAKENED: - moraleModifier = -5; - break; - case ADVANCING: - moraleModifier = 5; - break; - case DOMINATING: - if (playerDeployingForce) { - moraleModifier = 20; + } + case WEAKENED -> -5; + case ADVANCING -> 5; + case DOMINATING -> { + if (isReinforcements) { + yield 20; } else { - return 10; + yield 10; } - break; - case OVERWHELMING: - if (playerDeployingForce) { - moraleModifier = 50; + } + case OVERWHELMING -> { + if (isReinforcements) { + yield 50; } else { - return 25; + yield 25; } - break; - default: - break; - } + } + default -> 0; + }; int dataCenterModifier = track.getScenarioOddsAdjustment(); @@ -2031,6 +2020,48 @@ public static boolean processIgnoredScenario(StratconScenario scenario, Stratcon return true; } + /** + * Performs the daily movement processing for each active scenario in every track of the given + * {@link StratconCampaignState}. + * This processing involves evaluating each scenario and, if it is not yet deployed, attempting + * to move it to an unoccupied coordinate. + * If movement is possible, the scenario is updated with the new coordinates and parameters are + * set based on the new location's biome. + * If the new location contains a facility, the scenario is replaced with a facility scenario. + * + * @param campaign the current campaign + * @param campaignState the relevant {@link StratconCampaignState} + */ + public static void processDailyMovement(Campaign campaign, StratconCampaignState campaignState) { + for (StratconTrackState track : campaignState.getTracks()) { + List allScenarios = new ArrayList<>(track.getScenarios().values()); + + for (StratconScenario scenario : allScenarios) { + StratconCoords scenarioCoords = scenario.getCoords(); + + if (scenario.getDeploymentDate() == null) { + StratconCoords newCoords = getUnoccupiedAdjacentCoords(scenarioCoords, track); + + if (newCoords == null) { + continue; + } + + track.removeScenario(scenario); + track.updateScenario(scenario); + + if (track.getFacility(newCoords) == null) { + scenario.setCoords(newCoords); + setScenarioParametersFromBiome(track, scenario); + track.addScenario(scenario); + } else { + generateExternalScenario(campaign, campaignState.getContract(), track, newCoords, + null, true); + } + } + } + } + } + public void startup() { MekHQ.registerHandler(this); } @@ -2074,9 +2105,17 @@ public void handleNewDay(NewDayEvent ev) { } } - // on monday, generate new scenarios + processDailyMovement(ev.getCampaign(), campaignState); + + // on monday, generate new scenarios and reinforce existing enemy forces if (isMonday) { generateScenariosForTrack(ev.getCampaign(), contract, track); + + int reinforcementOdds = calculateScenarioOdds(track, contract, true); + + while (Compute.randomInt(100) < reinforcementOdds) { + addHiddenExternalScenario(ev.getCampaign(), contract, track, null, false); + } } } } diff --git a/MekHQ/src/mekhq/gui/stratcon/CampaignManagementDialog.java b/MekHQ/src/mekhq/gui/stratcon/CampaignManagementDialog.java index 60de457509..30d75d17eb 100644 --- a/MekHQ/src/mekhq/gui/stratcon/CampaignManagementDialog.java +++ b/MekHQ/src/mekhq/gui/stratcon/CampaignManagementDialog.java @@ -56,7 +56,8 @@ public void display(StratconCampaignState campaignState, StratconTrackState curr lblTrackScenarioOdds.setVisible(gmMode); if (gmMode) { lblTrackScenarioOdds.setText(String.format("Track Reinforcement Odds: %d%%", - StratconRulesManager.calculateScenarioOdds(currentTrack, campaignState.getContract(), false))); + StratconRulesManager.calculateScenarioOdds(currentTrack, campaignState.getContract(), + true))); } } From 363b2972103e483d4c64c22e67e7ee4a01a1dc5a Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Wed, 20 Nov 2024 22:00:37 -0600 Subject: [PATCH 09/19] Update copyright years and license information Updated the copyright years to 2024 and revised the license information for consistency and accuracy. This ensures the codebase reflects the correct legal information and adheres to the project's licensing guidelines. --- .../campaign/stratcon/StratconTrackState.java | 2 +- .../stratcon/CampaignManagementDialog.java | 29 +++++++++++-------- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/stratcon/StratconTrackState.java b/MekHQ/src/mekhq/campaign/stratcon/StratconTrackState.java index 622dd4c2ca..322bea7a4f 100644 --- a/MekHQ/src/mekhq/campaign/stratcon/StratconTrackState.java +++ b/MekHQ/src/mekhq/campaign/stratcon/StratconTrackState.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2022 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2020-2024 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * diff --git a/MekHQ/src/mekhq/gui/stratcon/CampaignManagementDialog.java b/MekHQ/src/mekhq/gui/stratcon/CampaignManagementDialog.java index 30d75d17eb..b10de2352f 100644 --- a/MekHQ/src/mekhq/gui/stratcon/CampaignManagementDialog.java +++ b/MekHQ/src/mekhq/gui/stratcon/CampaignManagementDialog.java @@ -1,16 +1,21 @@ /* -* MegaMek - Copyright (C) 2021 - The MegaMek Team -* -* This program is free software; you can redistribute it and/or modify it under -* the terms of the GNU General Public License as published by the Free Software -* Foundation; either version 2 of the License, or (at your option) any later -* version. -* -* This program is distributed in the hope that it will be useful, but WITHOUT -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -* details. -*/ + * Copyright (c) 2021-2024 - The MegaMek Team. All Rights Reserved. + * + * This file is part of MekHQ. + * + * MekHQ is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MekHQ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MekHQ. If not, see . + */ package mekhq.gui.stratcon; From 11ba6b54feab96b41d5116498ca50bcb64e54624 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Wed, 20 Nov 2024 22:04:10 -0600 Subject: [PATCH 10/19] Refactored seedPreDeployedForces method to be private Changed the visibility of the seedPreDeployedForces method to private and added a detailed Javadoc comment explaining its functionality. Removed redundant comments from the initializeTrackState method to improve code readability. --- .../stratcon/StratconContractInitializer.java | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java b/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java index 5718e62004..865e321374 100644 --- a/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java +++ b/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java @@ -214,7 +214,18 @@ public static void initializeCampaignState(AtBContract contract, Campaign campai // now we're done } - public static boolean seedPreDeployedForces(AtBContract contract, Campaign campaign, StratconTrackState track) { + /** + * Seeds pre-deployed (hidden) forces in a {@link StratconTrackState}, taking into account + * contract type and intensity. + * + * @param contract the current contract + * @param campaign the current campaign. + * @param track the relevant {@link StratconTrackState} + * + * @return a boolean where {@code true} means forces were not deployed due to being garrison + * type or pirate hunting and {@code false} implies forces have been deployed successfully. + */ + private static boolean seedPreDeployedForces(AtBContract contract, Campaign campaign, StratconTrackState track) { AtBContractType contractType = contract.getContractType(); // If the contract is a garrison type, we don't want to generate what will appear to be @@ -254,11 +265,6 @@ public static boolean seedPreDeployedForces(AtBContract contract, Campaign campa public static StratconTrackState initializeTrackState(int numLances, int scenarioOdds, int deploymentTime, int planetaryTemp, double planetaryDiameter) { - // to initialize a track, - // 1. we set the # of required lances - // 2. set the track size to a total of numlances * 84 hexes, a rectangle that is - // wider than it is taller - StratconTrackState retVal = new StratconTrackState(); retVal.setRequiredLanceCount(numLances); From 74aa6d4244c3d59add0e98d3d72a17bec95953a4 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Wed, 20 Nov 2024 22:10:26 -0600 Subject: [PATCH 11/19] Enable pre-deployed forces seeding in StratCon missions Made `seedPreDeployedForces` method public to allow seeding of pre-deployed forces in StratCon tracks. Also added logic to handle pre-deployment for garrison and pirate hunting contract types. --- MekHQ/src/mekhq/campaign/mission/AtBContract.java | 7 +++++++ .../campaign/stratcon/StratconContractInitializer.java | 6 ++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/mission/AtBContract.java b/MekHQ/src/mekhq/campaign/mission/AtBContract.java index 82c298ddf7..5ad9381a89 100644 --- a/MekHQ/src/mekhq/campaign/mission/AtBContract.java +++ b/MekHQ/src/mekhq/campaign/mission/AtBContract.java @@ -85,6 +85,7 @@ import static megamek.common.enums.SkillLevel.parseFromString; import static mekhq.campaign.mission.AtBDynamicScenarioFactory.getEntity; import static mekhq.campaign.mission.BotForceRandomizer.UNIT_WEIGHT_UNSPECIFIED; +import static mekhq.campaign.stratcon.StratconContractInitializer.seedPreDeployedForces; import static mekhq.campaign.universe.Factions.getFactionLogo; import static mekhq.campaign.universe.fameAndInfamy.BatchallFactions.BATCHALL_FACTIONS; import static mekhq.gui.dialog.HireBulkPersonnelDialog.overrideSkills; @@ -434,6 +435,12 @@ public void checkMorale(Campaign campaign, LocalDate today) { routEnd = null; updateEnemy(campaign, today); // mix it up a little + + if (campaign.getCampaignOptions().isUseStratCon()) { + for (StratconTrackState track : getStratconCampaignState().getTracks()) { + seedPreDeployedForces(this, campaign, track); + } + } } else { setMoraleLevel(AtBMoraleLevel.ROUTED); } diff --git a/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java b/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java index 865e321374..45e05ba3c8 100644 --- a/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java +++ b/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java @@ -225,7 +225,7 @@ public static void initializeCampaignState(AtBContract contract, Campaign campai * @return a boolean where {@code true} means forces were not deployed due to being garrison * type or pirate hunting and {@code false} implies forces have been deployed successfully. */ - private static boolean seedPreDeployedForces(AtBContract contract, Campaign campaign, StratconTrackState track) { + public static boolean seedPreDeployedForces(AtBContract contract, Campaign campaign, StratconTrackState track) { AtBContractType contractType = contract.getContractType(); // If the contract is a garrison type, we don't want to generate what will appear to be @@ -242,7 +242,9 @@ private static boolean seedPreDeployedForces(AtBContract contract, Campaign camp int multiplier = DEFENSIVE_MULTIPLIER; - if (contractType.isRaidType() || contractType.isGuerrillaWarfare()) { + if (contractType.isGarrisonType() || contractType.isPirateHunting()) { + multiplier = (int) (DEFENSIVE_MULTIPLIER * 1.5); + } else if (contractType.isRaidType() || contractType.isGuerrillaWarfare()) { multiplier = OFFENSIVE_MULTIPLIER; } else if (contract.getContractType().isPlanetaryAssault()) { multiplier = OFFENSIVE_MULTIPLIER / 2; From bad48661eea6e6e15bcc56766a5b75ecf4789ee3 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Wed, 20 Nov 2024 22:52:46 -0600 Subject: [PATCH 12/19] Fixed infinite loop in weekly enemy reinforcements. --- MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java b/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java index 70295c0c81..17ab3451eb 100644 --- a/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java +++ b/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java @@ -2113,8 +2113,12 @@ public void handleNewDay(NewDayEvent ev) { int reinforcementOdds = calculateScenarioOdds(track, contract, true); - while (Compute.randomInt(100) < reinforcementOdds) { + int roll = Compute.randomInt(100); + while (roll < reinforcementOdds) { addHiddenExternalScenario(ev.getCampaign(), contract, track, null, false); + + reinforcementOdds -= roll; + roll = Compute.randomInt(100); } } } From 8877ecbcc7565992e177e42711a85a1cdcea6960 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Thu, 21 Nov 2024 11:19:58 -0600 Subject: [PATCH 13/19] Updated PreSeeded Enemy Forces Spawn PreSeeded enemy force count is now based on enemy quality and faction. --- .../mission/enums/AtBContractType.java | 14 ++++- .../stratcon/StratconContractInitializer.java | 63 ++++++++++--------- 2 files changed, 44 insertions(+), 33 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/mission/enums/AtBContractType.java b/MekHQ/src/mekhq/campaign/mission/enums/AtBContractType.java index 155507eb8e..16b5ea8d47 100644 --- a/MekHQ/src/mekhq/campaign/mission/enums/AtBContractType.java +++ b/MekHQ/src/mekhq/campaign/mission/enums/AtBContractType.java @@ -18,8 +18,6 @@ */ package mekhq.campaign.mission.enums; -import java.util.ResourceBundle; - import megamek.common.Compute; import megamek.logging.MMLogger; import mekhq.MekHQ; @@ -28,6 +26,8 @@ import mekhq.campaign.mission.AtBScenario; import mekhq.campaign.universe.enums.EraFlag; +import java.util.ResourceBundle; + public enum AtBContractType { // TODO: Missing Camops Mission Types: ASSASSINATION, ESPIONAGE, MOLE_HUNTING, OBSERVATION_RAID, // RETAINER, SABOTAGE, TERRORISM, HIGH_RISK @@ -138,6 +138,16 @@ public boolean isGarrisonType() { public boolean isRaidType() { return isDiversionaryRaid() || isObjectiveRaid() || isReconRaid() || isExtractionRaid(); } + + /** + * Checks if the given contract is offensive. + * + * @return {@code true} if the contract is of raid type, pirate hunting, or guerrilla warfare; + * {@code false} otherwise + */ + public boolean isOffensive() { + return isRaidType() || isPirateHunting() || isGuerrillaWarfare(); + } // endregion Boolean Comparison Methods public int calculateLength(final boolean variable, final AtBContract contract) { diff --git a/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java b/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java index 45e05ba3c8..46de53b82a 100644 --- a/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java +++ b/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java @@ -38,8 +38,8 @@ import static java.lang.Math.round; import static megamek.common.Coords.ALL_DIRECTIONS; +import static mekhq.campaign.rating.IUnitRating.*; import static mekhq.campaign.stratcon.StratconRulesManager.addHiddenExternalScenario; -import static mekhq.campaign.stratcon.StratconRulesManager.calculateScenarioOdds; /** * This class handles StratCon state initialization when a contract is signed. @@ -195,9 +195,7 @@ public static void initializeCampaignState(AtBContract contract, Campaign campai // Initialize non-objective scenarios for (StratconTrackState track : campaignState.getTracks()) { - if (seedPreDeployedForces(contract, campaign, track)) { - break; - } + seedPreDeployedForces(contract, campaign, track); } // clean up objectives for integrated command: @@ -216,48 +214,51 @@ public static void initializeCampaignState(AtBContract contract, Campaign campai /** * Seeds pre-deployed (hidden) forces in a {@link StratconTrackState}, taking into account - * contract type and intensity. + * contract type, enemy quality, and enemy faction. * * @param contract the current contract * @param campaign the current campaign. * @param track the relevant {@link StratconTrackState} - * - * @return a boolean where {@code true} means forces were not deployed due to being garrison - * type or pirate hunting and {@code false} implies forces have been deployed successfully. */ - public static boolean seedPreDeployedForces(AtBContract contract, Campaign campaign, StratconTrackState track) { + public static void seedPreDeployedForces(AtBContract contract, Campaign campaign, StratconTrackState track) { + final int CLAN_CLUSTER = 27; // Stars + final int IS_BATTALION = 27; // Lances + final int COMSTAR_LEVEL_IV = 36; // Level IIs + + double multiplier = switch (contract.getEnemyQuality()) { + case DRAGOON_F -> 0.25; + case DRAGOON_D -> 0.5; + case DRAGOON_C -> 0.75; + case DRAGOON_B -> 1; + case DRAGOON_A -> 1.5; + case DRAGOON_ASTAR -> 2; + default -> + throw new IllegalStateException( + "Unexpected value in mekhq/campaign/stratcon/StratconContractInitializer.java/seedPreDeployedForces: " + + contract.getEnemyQuality()); + }; + AtBContractType contractType = contract.getContractType(); - // If the contract is a garrison type, we don't want to generate what will appear to be - // a full-scale invasion on day one. Furthermore, Pirates do not have enough resources - // to deploy standing forces in this manner. - if (contractType.isGarrisonType() || contractType.isPirateHunting()) { - return true; + if (contractType.isPirateHunting() || contractType.isGarrisonType()) { + multiplier *= 0.5; + } else if (contractType.isOffensive()) { + multiplier *= 2; } - // otherwise, seed each sector with hidden forces. - // the number of hidden forces is dependent on the type of contract. - final int OFFENSIVE_MULTIPLIER = 10; - final int DEFENSIVE_MULTIPLIER = 20; - - int multiplier = DEFENSIVE_MULTIPLIER; + int elementCount = IS_BATTALION; - if (contractType.isGarrisonType() || contractType.isPirateHunting()) { - multiplier = (int) (DEFENSIVE_MULTIPLIER * 1.5); - } else if (contractType.isRaidType() || contractType.isGuerrillaWarfare()) { - multiplier = OFFENSIVE_MULTIPLIER; - } else if (contract.getContractType().isPlanetaryAssault()) { - multiplier = OFFENSIVE_MULTIPLIER / 2; + if (contract.getEnemy().isClan()) { + elementCount = CLAN_CLUSTER; + } else if (contract.getEnemy().isComStarOrWoB()) { + elementCount = COMSTAR_LEVEL_IV; } - int preDeployedScenarios = track.getSize() / multiplier; - preDeployedScenarios = (int) round(preDeployedScenarios - * ((double) calculateScenarioOdds(track, contract, false) / 100)); + elementCount = (int) round(elementCount * multiplier); - for (int i = 0; i < preDeployedScenarios; i++) { + for (int i = 0; i < elementCount; i++) { addHiddenExternalScenario(campaign, contract, track, null, false); } - return false; } /** From 6450289ccdf002fa7e1beeef4e572ccb9f9d9fc7 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Thu, 21 Nov 2024 11:27:58 -0600 Subject: [PATCH 14/19] Add mass rout processing to Stratcon campaign Introduced a new method, `processMassRout`, in `StratconRulesManager` to handle scenarios without deployment dates. Integrated this method into `AtBContract` to ensure these scenarios are removed and updated appropriately. --- .../mekhq/campaign/mission/AtBContract.java | 3 +++ .../stratcon/StratconRulesManager.java | 24 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/MekHQ/src/mekhq/campaign/mission/AtBContract.java b/MekHQ/src/mekhq/campaign/mission/AtBContract.java index 5ad9381a89..36e3dbbe4f 100644 --- a/MekHQ/src/mekhq/campaign/mission/AtBContract.java +++ b/MekHQ/src/mekhq/campaign/mission/AtBContract.java @@ -86,6 +86,7 @@ import static mekhq.campaign.mission.AtBDynamicScenarioFactory.getEntity; import static mekhq.campaign.mission.BotForceRandomizer.UNIT_WEIGHT_UNSPECIFIED; import static mekhq.campaign.stratcon.StratconContractInitializer.seedPreDeployedForces; +import static mekhq.campaign.stratcon.StratconRulesManager.processMassRout; import static mekhq.campaign.universe.Factions.getFactionLogo; import static mekhq.campaign.universe.fameAndInfamy.BatchallFactions.BATCHALL_FACTIONS; import static mekhq.gui.dialog.HireBulkPersonnelDialog.overrideSkills; @@ -540,6 +541,8 @@ public void checkMorale(Campaign campaign, LocalDate today) { " The contract will conclude tomorrow."); setEndDate(today.plusDays(1)); } + + processMassRout(campaign, getStratconCampaignState()); } // Process the results of the reinforcement roll diff --git a/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java b/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java index 17ab3451eb..1b6232175e 100644 --- a/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java +++ b/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java @@ -2062,6 +2062,30 @@ public static void processDailyMovement(Campaign campaign, StratconCampaignState } } + /** + * Processes a mass rout in the given campaign state. + *

+ * Loops through all tracks in the campaign state. + * For each track, it retrieves all scenarios. + * If scenario's deployment date is {@code null} and scenario is not a strategic objective, + * it is removed from the track and then updated. + * + * @param campaign the current campaign. + * @param campaignState the relevant StratCon campaign state. + */ + public static void processMassRout(Campaign campaign, StratconCampaignState campaignState) { + for (StratconTrackState track : campaignState.getTracks()) { + List allScenarios = new ArrayList<>(track.getScenarios().values()); + + for (StratconScenario scenario : allScenarios) { + if (scenario.getDeploymentDate() == null && !scenario.isStrategicObjective()) { + track.removeScenario(scenario); + track.updateScenario(scenario); + } + } + } + } + public void startup() { MekHQ.registerHandler(this); } From b961d1f2793cc6fe02edc0ab64543f19c94379e6 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Thu, 21 Nov 2024 11:28:20 -0600 Subject: [PATCH 15/19] Add conditional check for StratCon before processing mass rout Ensured that mass rout processing in AtBContract only occurs when StratCon is enabled in campaign options. This change prevents unnecessary operations and aligns with the intended functionality of user-defined campaign options. --- MekHQ/src/mekhq/campaign/mission/AtBContract.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/MekHQ/src/mekhq/campaign/mission/AtBContract.java b/MekHQ/src/mekhq/campaign/mission/AtBContract.java index 36e3dbbe4f..5871ae60b9 100644 --- a/MekHQ/src/mekhq/campaign/mission/AtBContract.java +++ b/MekHQ/src/mekhq/campaign/mission/AtBContract.java @@ -542,7 +542,9 @@ public void checkMorale(Campaign campaign, LocalDate today) { setEndDate(today.plusDays(1)); } - processMassRout(campaign, getStratconCampaignState()); + if (campaign.getCampaignOptions().isUseStratCon()) { + processMassRout(campaign, getStratconCampaignState()); + } } // Process the results of the reinforcement roll From 9db18601b9d892693abf0c2c65601ab9cab8280e Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Thu, 21 Nov 2024 12:03:17 -0600 Subject: [PATCH 16/19] Optimize StratCon handling and improve offensive definition Updated isOffensive method to include planetary assaults, modified processMassRout to streamline parameters, and adjusted seeding logic with revised multipliers. This enhances the clarity and efficiency of the StratCon campaign state management and scenario processing. --- .../mekhq/campaign/mission/AtBContract.java | 2 +- .../mission/enums/AtBContractType.java | 2 +- .../stratcon/StratconContractInitializer.java | 18 ++++++++---------- .../stratcon/StratconRulesManager.java | 3 +-- 4 files changed, 11 insertions(+), 14 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/mission/AtBContract.java b/MekHQ/src/mekhq/campaign/mission/AtBContract.java index 5871ae60b9..15aca7fb34 100644 --- a/MekHQ/src/mekhq/campaign/mission/AtBContract.java +++ b/MekHQ/src/mekhq/campaign/mission/AtBContract.java @@ -543,7 +543,7 @@ public void checkMorale(Campaign campaign, LocalDate today) { } if (campaign.getCampaignOptions().isUseStratCon()) { - processMassRout(campaign, getStratconCampaignState()); + processMassRout(getStratconCampaignState()); } } diff --git a/MekHQ/src/mekhq/campaign/mission/enums/AtBContractType.java b/MekHQ/src/mekhq/campaign/mission/enums/AtBContractType.java index 16b5ea8d47..442e386053 100644 --- a/MekHQ/src/mekhq/campaign/mission/enums/AtBContractType.java +++ b/MekHQ/src/mekhq/campaign/mission/enums/AtBContractType.java @@ -146,7 +146,7 @@ public boolean isRaidType() { * {@code false} otherwise */ public boolean isOffensive() { - return isRaidType() || isPirateHunting() || isGuerrillaWarfare(); + return isRaidType() || isPirateHunting() || isGuerrillaWarfare() || isPlanetaryAssault(); } // endregion Boolean Comparison Methods diff --git a/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java b/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java index 46de53b82a..a5ef1b5373 100644 --- a/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java +++ b/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java @@ -36,6 +36,7 @@ import java.util.List; import java.util.Random; +import static java.lang.Math.ceil; import static java.lang.Math.round; import static megamek.common.Coords.ALL_DIRECTIONS; import static mekhq.campaign.rating.IUnitRating.*; @@ -221,28 +222,25 @@ public static void initializeCampaignState(AtBContract contract, Campaign campai * @param track the relevant {@link StratconTrackState} */ public static void seedPreDeployedForces(AtBContract contract, Campaign campaign, StratconTrackState track) { - final int CLAN_CLUSTER = 27; // Stars - final int IS_BATTALION = 27; // Lances - final int COMSTAR_LEVEL_IV = 36; // Level IIs + // TODO remove reductions once we have friendly forces deploying too + final int CLAN_CLUSTER = 11; // 22 Stars, reduced to 11 + final int IS_BATTALION = 14; // 27 Lances, reduced to 14 + final int COMSTAR_LEVEL_IV = 18; // 36 Level IIs, reduced to 18 double multiplier = switch (contract.getEnemyQuality()) { case DRAGOON_F -> 0.25; case DRAGOON_D -> 0.5; case DRAGOON_C -> 0.75; - case DRAGOON_B -> 1; case DRAGOON_A -> 1.5; case DRAGOON_ASTAR -> 2; - default -> - throw new IllegalStateException( - "Unexpected value in mekhq/campaign/stratcon/StratconContractInitializer.java/seedPreDeployedForces: " - + contract.getEnemyQuality()); + default -> 1; // DRAGOON_B }; AtBContractType contractType = contract.getContractType(); if (contractType.isPirateHunting() || contractType.isGarrisonType()) { multiplier *= 0.5; - } else if (contractType.isOffensive()) { + } else if (contractType.isPlanetaryAssault()) { multiplier *= 2; } @@ -254,7 +252,7 @@ public static void seedPreDeployedForces(AtBContract contract, Campaign campaign elementCount = COMSTAR_LEVEL_IV; } - elementCount = (int) round(elementCount * multiplier); + elementCount = (int) ceil(elementCount * multiplier); for (int i = 0; i < elementCount; i++) { addHiddenExternalScenario(campaign, contract, track, null, false); diff --git a/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java b/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java index 1b6232175e..4d327b34ed 100644 --- a/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java +++ b/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java @@ -2070,10 +2070,9 @@ public static void processDailyMovement(Campaign campaign, StratconCampaignState * If scenario's deployment date is {@code null} and scenario is not a strategic objective, * it is removed from the track and then updated. * - * @param campaign the current campaign. * @param campaignState the relevant StratCon campaign state. */ - public static void processMassRout(Campaign campaign, StratconCampaignState campaignState) { + public static void processMassRout(StratconCampaignState campaignState) { for (StratconTrackState track : campaignState.getTracks()) { List allScenarios = new ArrayList<>(track.getScenarios().values()); From d34b122837c023d5bbff2bda2427c0ab20fecb14 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Thu, 21 Nov 2024 12:40:05 -0600 Subject: [PATCH 17/19] Add player force weighting for adjacent coordinate selection Modified `getUnoccupiedAdjacentCoords` method to include an optional boolean parameter `weightPlayerForces` which prioritizes player-allied forces and facilities when determining suitable adjacent coordinates. Updated `StratconRulesManager` to leverage this new parameter for enhanced scenario deployment logic. --- .../stratcon/StratconContractInitializer.java | 30 +++++++++++++++++-- .../stratcon/StratconRulesManager.java | 5 ++-- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java b/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java index a5ef1b5373..02830bc8dc 100644 --- a/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java +++ b/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java @@ -508,12 +508,16 @@ private static void initializeObjectiveScenarios(Campaign campaign, AtBContract * * @param originCoords the {@link StratconCoords} around which to search for a suitable coordinate * @param trackState the {@link StratconTrackState} on which to perform the search + * @param weightPlayerForces whether to place greater emphasis on player-allied forces and facilities. * @return a {@link StratconCoords} object representing the coordinates of a suitable adjacent * location, or {code null} if no suitable location was found. */ public static @Nullable StratconCoords getUnoccupiedAdjacentCoords(StratconCoords originCoords, - StratconTrackState trackState) { + StratconTrackState trackState, + boolean weightPlayerForces) { List suitableCoords = new ArrayList<>(); + List playerForceCoords = new ArrayList<>(); + List playerFacilityCoords = new ArrayList<>(); for (int direction : ALL_DIRECTIONS) { StratconCoords newCoords = originCoords.translate(direction); @@ -529,6 +533,14 @@ private static void initializeObjectiveScenarios(Campaign campaign, AtBContract if (trackState.getFacility(newCoords).getOwner() != ForceAlignment.Opposing) { suitableCoords.add(newCoords); + + if (weightPlayerForces) { + playerFacilityCoords.add(newCoords); + } + } + + if (trackState.getAssignedForceCoords().containsValue(newCoords)) { + playerForceCoords.add(newCoords); } } @@ -536,7 +548,21 @@ private static void initializeObjectiveScenarios(Campaign campaign, AtBContract return null; } - int randomIndex = new Random().nextInt(suitableCoords.size()); + Random random = new Random(); + + if (weightPlayerForces) { + if (!playerFacilityCoords.isEmpty()) { + int randomIndex = random.nextInt(playerFacilityCoords.size()); + return playerFacilityCoords.get(randomIndex); + } + + if (!playerForceCoords.isEmpty()) { + int randomIndex = random.nextInt(playerForceCoords.size()); + return playerForceCoords.get(randomIndex); + } + } + + int randomIndex = random.nextInt(suitableCoords.size()); return suitableCoords.get(randomIndex); } diff --git a/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java b/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java index 4d327b34ed..abbc5d5fe1 100644 --- a/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java +++ b/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java @@ -2037,10 +2037,9 @@ public static void processDailyMovement(Campaign campaign, StratconCampaignState List allScenarios = new ArrayList<>(track.getScenarios().values()); for (StratconScenario scenario : allScenarios) { - StratconCoords scenarioCoords = scenario.getCoords(); - if (scenario.getDeploymentDate() == null) { - StratconCoords newCoords = getUnoccupiedAdjacentCoords(scenarioCoords, track); + StratconCoords scenarioCoords = scenario.getCoords(); + StratconCoords newCoords = getUnoccupiedAdjacentCoords(scenarioCoords, track, true); if (newCoords == null) { continue; From ded864f851bddc4b29f8d2de6eca19033552a191 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Thu, 21 Nov 2024 12:41:21 -0600 Subject: [PATCH 18/19] Fixed deployment issue for scenarios without a deployment date. Updated the condition to exclude strategic objectives when checking for null deployment dates in scenarios. This ensures that only relevant scenarios get new coordinates, preventing strategic objectives from being relocated erroneously. --- MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java b/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java index abbc5d5fe1..e61e3ed5cd 100644 --- a/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java +++ b/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java @@ -2037,7 +2037,7 @@ public static void processDailyMovement(Campaign campaign, StratconCampaignState List allScenarios = new ArrayList<>(track.getScenarios().values()); for (StratconScenario scenario : allScenarios) { - if (scenario.getDeploymentDate() == null) { + if (scenario.getDeploymentDate() == null && !scenario.isStrategicObjective()) { StratconCoords scenarioCoords = scenario.getCoords(); StratconCoords newCoords = getUnoccupiedAdjacentCoords(scenarioCoords, track, true); From 626a527efd5457f1d7da9e15486ac48c2bc05a7e Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Fri, 22 Nov 2024 14:54:49 -0600 Subject: [PATCH 19/19] Add morale handling and routing logic to contracts Improved the morale calculation and routing logic when handling contracts, especially garrison duties. Updated methods to factor in enemy skill, weapon reliability, and included a more sophisticated approach to determining balance of power. Minor refactoring was also done for code clarity and efficiency. --- .../MekHQ Morale.pdf | Bin 44814 -> 76454 bytes .../mekhq/resources/Campaign.properties | 4 + .../resources/ContractMarketDialog.properties | 13 ++ MekHQ/src/mekhq/campaign/Campaign.java | 26 ++- .../mekhq/campaign/mission/AtBContract.java | 179 ++++++++++++++---- .../stratcon/StratconContractInitializer.java | 37 +++- .../stratcon/StratconRulesManager.java | 6 +- .../gui/dialog/ContractMarketDialog.java | 10 +- 8 files changed, 222 insertions(+), 53 deletions(-) diff --git a/MekHQ/docs/Stratcon and Against the Bot/MekHQ Morale.pdf b/MekHQ/docs/Stratcon and Against the Bot/MekHQ Morale.pdf index 5a504c5560173677acce77d0fef4b6e1d84103c5..c62599e1f0b88ffc3a61c12b968379f87857fc3d 100644 GIT binary patch delta 71593 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%0DEV!GbdtU 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 SqJTmWKy)TvUKK4Q*?$3#TB2|O diff --git a/MekHQ/resources/mekhq/resources/Campaign.properties b/MekHQ/resources/mekhq/resources/Campaign.properties index f83243e46a..a1fcdde7df 100644 --- a/MekHQ/resources/mekhq/resources/Campaign.properties +++ b/MekHQ/resources/mekhq/resources/Campaign.properties @@ -84,3 +84,7 @@ newAtBScenario.format=New scenario "{0}" will occur on {1}. atbScenarioToday.format=Scenario "{0}" is today, deploy a force from your TOE! atbScenarioTodayWithForce.format=Scenario "{0}" is today, {1} has been deployed! generalFallbackAddress.text=Commander +garrisonDutyRouted.text=Long-ranged sensors detect no enemy activity in the AO. +contractMoraleReport.text=Current enemy condition is %s on contract %s.\ +
\ +
%s \ No newline at end of file diff --git a/MekHQ/resources/mekhq/resources/ContractMarketDialog.properties b/MekHQ/resources/mekhq/resources/ContractMarketDialog.properties index b8f7b3021d..5a4d46bfec 100644 --- a/MekHQ/resources/mekhq/resources/ContractMarketDialog.properties +++ b/MekHQ/resources/mekhq/resources/ContractMarketDialog.properties @@ -99,3 +99,16 @@ messageChallengeVeryHard.text=We've reviewed the mission details, and it's our d
\
If you are committed to this course of action, confirm your deployment.\
Otherwise, you may return to review more suitable assignments.
+messageChallengeGarrison.text=You have selected a garrison assignment. As part of this contract,\ + \ your unit will be responsible for maintaining a defensive presence in your assigned Sectors.\ + \ However, we cannot predict when - or even if - you will be called to fight. Furthermore, we\ + \ have no reliable intelligence on potential adversaries or the scale of any engagement that may\ + \ arise.\ +
The provided data is for estimation purposes only. Situational developments may occur\ + \ without warning, and you should be prepared for anything - from prolonged quiet to sudden,\ + \ large-scale conflict. Flexibility and readiness will be essential for the success of this\ + \ contract.\ +
\ +
If you are prepared to assume this role, confirm your deployment.\ +
Otherwise, you may return to review other opportunities.
diff --git a/MekHQ/src/mekhq/campaign/Campaign.java b/MekHQ/src/mekhq/campaign/Campaign.java index 2d7aa84b72..ced9b4ef54 100644 --- a/MekHQ/src/mekhq/campaign/Campaign.java +++ b/MekHQ/src/mekhq/campaign/Campaign.java @@ -3648,6 +3648,14 @@ && getLocation().getJumpPath().getLastSystem().getId().equals(contract.getSystem contract.setStartAndEndDate(getLocalDate().plusDays((int) Math.ceil(getLocation().getTransitTime()))); addReport("The start and end dates of " + contract.getName() + " have been shifted to reflect the current ETA."); + + if (campaignOptions.isUseStratCon() && contract.getMoraleLevel().isRouted()) { + LocalDate newRoutEndDate = contract.getStartDate() + .plusMonths(Math.max(1, Compute.d6() - 3)) + .minusDays(1); + contract.setRoutEndDate(newRoutEndDate); + } + continue; } @@ -3782,14 +3790,22 @@ private void processNewDayATB() { } for (AtBContract contract : getActiveAtBContracts()) { - contract.checkMorale(this, getLocalDate()); + AtBMoraleLevel oldMorale = contract.getMoraleLevel(); - AtBMoraleLevel morale = contract.getMoraleLevel(); + contract.checkMorale(this, getLocalDate()); + AtBMoraleLevel newMorale = contract.getMoraleLevel(); - String report = "Current enemy condition is " + morale + " on contract " - + contract.getName() + "

" + morale.getToolTipText(); + String report = ""; + if (contract.getContractType().isGarrisonDuty()) { + report = resources.getString("garrisonDutyRouted.text"); + } else if (oldMorale != newMorale) { + report = String.format(resources.getString("contractMoraleReport.text"), + newMorale, contract.getName(), newMorale.getToolTipText()); + } - addReport(report); + if (!report.isBlank()) { + addReport(report); + } } } diff --git a/MekHQ/src/mekhq/campaign/mission/AtBContract.java b/MekHQ/src/mekhq/campaign/mission/AtBContract.java index 15aca7fb34..da8806c288 100644 --- a/MekHQ/src/mekhq/campaign/mission/AtBContract.java +++ b/MekHQ/src/mekhq/campaign/mission/AtBContract.java @@ -29,6 +29,7 @@ import megamek.client.ui.swing.util.PlayerColour; import megamek.common.Compute; import megamek.common.Entity; +import megamek.common.TargetRoll; import megamek.common.UnitType; import megamek.common.enums.Gender; import megamek.common.enums.SkillLevel; @@ -39,6 +40,7 @@ import mekhq.campaign.event.MissionChangedEvent; import mekhq.campaign.finances.Money; import mekhq.campaign.force.Force; +import mekhq.campaign.force.Lance; import mekhq.campaign.market.enums.UnitMarketType; import mekhq.campaign.mission.atb.AtBScenarioFactory; import mekhq.campaign.mission.enums.AtBContractType; @@ -48,7 +50,6 @@ import mekhq.campaign.personnel.backgrounds.BackgroundsController; import mekhq.campaign.personnel.enums.PersonnelRole; import mekhq.campaign.personnel.enums.Phenotype; -import mekhq.campaign.rating.IUnitRating; import mekhq.campaign.stratcon.StratconCampaignState; import mekhq.campaign.stratcon.StratconContractDefinition; import mekhq.campaign.stratcon.StratconContractInitializer; @@ -75,6 +76,7 @@ import java.util.List; import java.util.*; +import static java.lang.Math.ceil; import static java.lang.Math.round; import static megamek.client.ratgenerator.ModelRecord.NETWORK_NONE; import static megamek.client.ratgenerator.UnitTable.findTable; @@ -85,6 +87,7 @@ import static megamek.common.enums.SkillLevel.parseFromString; import static mekhq.campaign.mission.AtBDynamicScenarioFactory.getEntity; import static mekhq.campaign.mission.BotForceRandomizer.UNIT_WEIGHT_UNSPECIFIED; +import static mekhq.campaign.rating.IUnitRating.*; import static mekhq.campaign.stratcon.StratconContractInitializer.seedPreDeployedForces; import static mekhq.campaign.stratcon.StratconRulesManager.processMassRout; import static mekhq.campaign.universe.Factions.getFactionLogo; @@ -184,6 +187,16 @@ protected AtBContract() { this(null); } + /** + * Sets the end date of the rout. + * This should only be applied on contracts whose morale equals ROUTED + * + * @param routEnd the {@code LocalDate} representing the end date of the rout + */ + public void setRoutEndDate(LocalDate routEnd) { + this.routEnd = routEnd; + } + public AtBContract(String name) { super(name, "Independent"); employerCode = "IND"; @@ -196,9 +209,9 @@ public AtBContract(String name) { setContractType(AtBContractType.GARRISON_DUTY); setAllySkill(REGULAR); - allyQuality = IUnitRating.DRAGOON_C; + allyQuality = DRAGOON_C; setEnemySkill(REGULAR); - enemyQuality = IUnitRating.DRAGOON_C; + enemyQuality = DRAGOON_C; allyBotName = "Ally"; enemyBotName = "Enemy"; setAllyCamouflage(new Camouflage(Camouflage.COLOUR_CAMOUFLAGE, PlayerColour.RED.name())); @@ -427,7 +440,7 @@ public static boolean isMinorPower(final String factionCode) { public void checkMorale(Campaign campaign, LocalDate today) { // Check whether enemy forces have been reinforced, and whether any current rout continues // beyond its expected date - boolean routContinue = Compute.randomInt(4) < 3; + boolean routContinue = Compute.randomInt(4) == 0; // If there is a rout end date, and it's past today, update morale and enemy state accordingly if (routEnd != null && !routContinue) { @@ -439,7 +452,7 @@ public void checkMorale(Campaign campaign, LocalDate today) { if (campaign.getCampaignOptions().isUseStratCon()) { for (StratconTrackState track : getStratconCampaignState().getTracks()) { - seedPreDeployedForces(this, campaign, track); + seedPreDeployedForces(this, campaign, track, true); } } } else { @@ -448,7 +461,84 @@ public void checkMorale(Campaign campaign, LocalDate today) { return; } - // Initialize counters for victories and defeats + TargetRoll targetNumber = new TargetRoll(); + + // Confidence: + int enemySkillRating = getEnemySkill().getAdjustedValue() - 2; + int allySkillRating = getAllySkill().getAdjustedValue() - 2; + + if (getCommandRights().isIndependent()) { + allySkillRating = (campaign.getCampaignOptions().getUnitRatingMethod().isFMMR() ? getAllySkill() + : campaign.getReputation().getAverageSkillLevel()).getAdjustedValue(); + allySkillRating -= 2; + } + + final LocalDate THE_GREAT_REFUSAL = LocalDate.of(3060, 4, 12); + + if (campaign.getLocalDate().isBefore(THE_GREAT_REFUSAL)) { + if (getEnemy().isClan() && !getEmployerFaction().isClan()) { + enemySkillRating++; + } else if (!getEnemy().isClan() && getEmployerFaction().isClan()) { + allySkillRating++; + } + } + + int confidence = enemySkillRating - allySkillRating; + targetNumber.addModifier(confidence, "confidence"); + + // Reliability: + int reliability = getEnemyQuality(); + + Faction enemy = getEnemy(); + if (enemy.isClan()) { + reliability = Math.max(5, reliability + 1); + } + + reliability = switch (reliability) { + case DRAGOON_F -> -1; + case DRAGOON_D -> { + if (Compute.randomInt(1) == 0) { + yield -1; + } else { + yield 0; + } + } + case DRAGOON_C -> 0; + case DRAGOON_B -> { + if (Compute.randomInt(1) == 0) { + yield 0; + } else { + yield +1; + } + } + case DRAGOON_A -> +1; + default -> { // DRAGOON_ASTAR + if (Compute.randomInt(1) == 0) { + yield +1; + } else { + yield +2; + } + } + }; + + if (enemy.isRebel() + || enemy.isMinorPower() + || enemy.isMercenary() + || enemy.isPirate()) { + reliability--; + } else if (enemy.isClan()) { + reliability++; + } + + targetNumber.addModifier(reliability, "reliability"); + + // Force Type (unimplemented) + // TODO once we have force types defined on the StratCon map, we should handle modifiers here. + // 'Mek or Aircraft == +1 + // Vehicle == +0 + // Infantry == -1 (if unsupported) + + // Performance int victories = 0; int defeats = 0; LocalDate lastMonth = today.minusMonths(1); @@ -474,10 +564,6 @@ public void checkMorale(Campaign campaign, LocalDate today) { } } - // Calculate various modifiers for morale - int enemySkillModifier = getEnemySkill().getAdjustedValue() - REGULAR.getAdjustedValue(); - int allySkillModifier = getAllySkill().getAdjustedValue() - REGULAR.getAdjustedValue(); - int performanceModifier = 0; if (victories > (defeats * 2)) { @@ -490,34 +576,58 @@ public void checkMorale(Campaign campaign, LocalDate today) { performanceModifier++; } - int miscModifiers = moraleMod; - - // Additional morale modifications depending on faction properties - if (Factions.getInstance().getFaction(enemyCode).isPirate()) { - miscModifiers -= 2; - } else if (Factions.getInstance().getFaction(enemyCode).isRebel() - || isMinorPower(enemyCode) - || Factions.getInstance().getFaction(enemyCode).isMercenary()) { - miscModifiers -= 1; - } else if (Factions.getInstance().getFaction(enemyCode).isClan()) { - miscModifiers += 2; - } + targetNumber.addModifier(performanceModifier, "performanceModifier"); + // Balance of Power int balanceOfPower = 0; if (campaign.getCampaignOptions().isUseStratCon()) { - balanceOfPower = -campaign.getLanceList().size() * 5; + int playerForceCount = 0; + + for (Lance lance : campaign.getLances().values()) { + try { + Force force = campaign.getForce(lance.getForceId()); + + if (force.isCombatForce()) { + playerForceCount++; + } + } catch (Exception ex) { + logger.error(String.format("Failed to fetch force %s: %s", + lance.getForceId(), ex.getMessage())); + } + } + + playerForceCount = (int) ceil((double) playerForceCount / campaign.getActiveContracts().size()); + + if (getCommandRights().isHouse() || getCommandRights().isLiaison()) { + playerForceCount = (int) round(playerForceCount * 1.25); + } else if (getCommandRights().isIntegrated()) { + playerForceCount = (int) round(playerForceCount * 1.5); + } int enemyForceCount = 0; for (StratconTrackState track : getStratconCampaignState().getTracks()) { - enemyForceCount = track.getScenarios().size(); + enemyForceCount += track.getScenarios().size(); } - balanceOfPower += enemyForceCount; + if (playerForceCount >= (enemyForceCount * 3)) { + balanceOfPower = -6; + } else if (playerForceCount >= (enemyForceCount * 2)) { + balanceOfPower = -4; + } else if (playerForceCount > enemyForceCount) { + balanceOfPower = -2; + } else if (enemyForceCount >= (playerForceCount * 3)) { + balanceOfPower = 6; + } else if (enemyForceCount >= (playerForceCount * 2)) { + balanceOfPower = 4; + } else if (enemyForceCount > playerForceCount) { + balanceOfPower = 2; + } } + targetNumber.addModifier(balanceOfPower, "balanceOfPower"); + // Total morale modifier calculation - int totalModifier = enemySkillModifier - allySkillModifier + performanceModifier + miscModifiers + balanceOfPower; - int roll = Compute.d6(2) + totalModifier; + int roll = Compute.d6(2) + targetNumber.getValue(); // Morale level determination based on roll value final AtBMoraleLevel[] moraleLevels = AtBMoraleLevel.values(); @@ -543,17 +653,10 @@ public void checkMorale(Campaign campaign, LocalDate today) { } if (campaign.getCampaignOptions().isUseStratCon()) { - processMassRout(getStratconCampaignState()); + processMassRout(getStratconCampaignState(), true); } } - // Process the results of the reinforcement roll - if (!getMoraleLevel().isRouted() && !routContinue) { - setMoraleLevel(moraleLevels[Math.min(getMoraleLevel().ordinal() + 1, moraleLevels.length - 1)]); - campaign.addReport("Long ranged scans have detected the arrival of additional enemy forces."); - return; - } - // Reset external morale modifier moraleMod = 0; } @@ -611,7 +714,7 @@ public int getRepairLocation(final int unitRating) { repairLocation = Unit.SITE_FACILITY_MAINTENANCE; } - if (unitRating >= IUnitRating.DRAGOON_B) { + if (unitRating >= DRAGOON_B) { repairLocation++; } @@ -892,7 +995,7 @@ public void checkEvents(Campaign c) { case 6: final String unitName = c.getUnitMarket().addSingleUnit(c, UnitMarketType.EMPLOYER, MEK, getEmployerFaction(), - IUnitRating.DRAGOON_F, 50); + DRAGOON_F, 50); if (unitName != null) { text += String.format( "Surplus Sale: %s offered by employer on the unit market", @@ -1840,7 +1943,7 @@ public int calculateContractDifficulty(Campaign campaign) { double difference = enemyPower - playerPower; double percentDifference = (difference / playerPower) * 100; - int mappedValue = (int) Math.ceil(Math.abs(percentDifference) / 20); + int mappedValue = (int) ceil(Math.abs(percentDifference) / 20); if (percentDifference < 0) { mappedValue = 5 - mappedValue; } else { diff --git a/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java b/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java index 02830bc8dc..5158a35a45 100644 --- a/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java +++ b/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java @@ -27,10 +27,13 @@ import mekhq.campaign.mission.ScenarioForceTemplate.ForceAlignment; import mekhq.campaign.mission.atb.AtBScenarioModifier; import mekhq.campaign.mission.enums.AtBContractType; +import mekhq.campaign.mission.enums.AtBMoraleLevel; import mekhq.campaign.mission.enums.ContractCommandRights; import mekhq.campaign.stratcon.StratconContractDefinition.ObjectiveParameters; import mekhq.campaign.stratcon.StratconContractDefinition.StrategicObjectiveType; +import mekhq.campaign.universe.Faction; +import java.time.LocalDate; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -41,6 +44,7 @@ import static megamek.common.Coords.ALL_DIRECTIONS; import static mekhq.campaign.rating.IUnitRating.*; import static mekhq.campaign.stratcon.StratconRulesManager.addHiddenExternalScenario; +import static mekhq.campaign.stratcon.StratconRulesManager.processMassRout; /** * This class handles StratCon state initialization when a contract is signed. @@ -196,7 +200,7 @@ public static void initializeCampaignState(AtBContract contract, Campaign campai // Initialize non-objective scenarios for (StratconTrackState track : campaignState.getTracks()) { - seedPreDeployedForces(contract, campaign, track); + seedPreDeployedForces(contract, campaign, track, true); } // clean up objectives for integrated command: @@ -210,6 +214,22 @@ public static void initializeCampaignState(AtBContract contract, Campaign campai } } + // Determine starting morale + if (contract.getContractType().isGarrisonDuty()) { + contract.setMoraleLevel(AtBMoraleLevel.ROUTED); + + LocalDate routEnd = contract.getStartDate().plusMonths(Math.max(1, Compute.d6() - 3)).minusDays(1); + contract.setRoutEndDate(routEnd); + + processMassRout(campaignState, true); + } else { + contract.checkMorale(campaign, campaign.getLocalDate()); + + if (contract.getMoraleLevel().isRouted()) { + contract.setMoraleLevel(AtBMoraleLevel.CRITICAL); + } + } + // now we're done } @@ -220,14 +240,17 @@ public static void initializeCampaignState(AtBContract contract, Campaign campai * @param contract the current contract * @param campaign the current campaign. * @param track the relevant {@link StratconTrackState} + * @param isEnemy whether we are seeding forces for the enemy, or player's allies */ - public static void seedPreDeployedForces(AtBContract contract, Campaign campaign, StratconTrackState track) { + public static int seedPreDeployedForces(AtBContract contract, Campaign campaign, + StratconTrackState track, boolean isEnemy) { // TODO remove reductions once we have friendly forces deploying too final int CLAN_CLUSTER = 11; // 22 Stars, reduced to 11 final int IS_BATTALION = 14; // 27 Lances, reduced to 14 final int COMSTAR_LEVEL_IV = 18; // 36 Level IIs, reduced to 18 - double multiplier = switch (contract.getEnemyQuality()) { + int quality = isEnemy ? contract.getEnemyQuality() : contract.getAllyQuality(); + double multiplier = switch (quality) { case DRAGOON_F -> 0.25; case DRAGOON_D -> 0.5; case DRAGOON_C -> 0.75; @@ -246,9 +269,11 @@ public static void seedPreDeployedForces(AtBContract contract, Campaign campaign int elementCount = IS_BATTALION; - if (contract.getEnemy().isClan()) { + Faction faction = isEnemy ? contract.getEnemy() : contract.getEmployerFaction(); + + if (faction.isClan()) { elementCount = CLAN_CLUSTER; - } else if (contract.getEnemy().isComStarOrWoB()) { + } else if (faction.isComStarOrWoB()) { elementCount = COMSTAR_LEVEL_IV; } @@ -257,6 +282,8 @@ public static void seedPreDeployedForces(AtBContract contract, Campaign campaign for (int i = 0; i < elementCount; i++) { addHiddenExternalScenario(campaign, contract, track, null, false); } + + return elementCount; } /** diff --git a/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java b/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java index e61e3ed5cd..1af1c3f5f7 100644 --- a/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java +++ b/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java @@ -2070,13 +2070,15 @@ public static void processDailyMovement(Campaign campaign, StratconCampaignState * it is removed from the track and then updated. * * @param campaignState the relevant StratCon campaign state. + * @param removeAll whether to remove all scenarios, including those with dates or + * strategic objectives. This should be used sparingly. */ - public static void processMassRout(StratconCampaignState campaignState) { + public static void processMassRout(StratconCampaignState campaignState, boolean removeAll) { for (StratconTrackState track : campaignState.getTracks()) { List allScenarios = new ArrayList<>(track.getScenarios().values()); for (StratconScenario scenario : allScenarios) { - if (scenario.getDeploymentDate() == null && !scenario.isStrategicObjective()) { + if (removeAll || (scenario.getDeploymentDate() == null && !scenario.isStrategicObjective())) { track.removeScenario(scenario); track.updateScenario(scenario); } diff --git a/MekHQ/src/mekhq/gui/dialog/ContractMarketDialog.java b/MekHQ/src/mekhq/gui/dialog/ContractMarketDialog.java index a9d580796f..2edd56216f 100644 --- a/MekHQ/src/mekhq/gui/dialog/ContractMarketDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/ContractMarketDialog.java @@ -482,8 +482,6 @@ private void acceptContract(ActionEvent evt) { } } - contractStartPrompt(campaign, selectedContract); - selectedContract.setName(contractView.getContractName()); campaign.getFinances().credit(TransactionType.CONTRACT_PAYMENT, campaign.getLocalDate(), selectedContract.getTotalAdvanceAmount(), @@ -491,9 +489,11 @@ private void acceptContract(ActionEvent evt) { campaign.addMission(selectedContract); // must be invoked after campaign.addMission to ensure presence of mission ID selectedContract.acceptContract(campaign); + contractStartPrompt(campaign, selectedContract); + contractMarket.removeContract(selectedContract); ((DefaultTableModel) tableContracts.getModel()).removeRow(tableContracts - .convertRowIndexToModel(tableContracts.getSelectedRow())); + .convertRowIndexToModel(tableContracts.getSelectedRow())); refreshContractView(); } } @@ -529,6 +529,10 @@ private boolean triggerConfirmationDialog() { resourceKey = "messageChallengeHard.text"; } + if (((AtBContract) selectedContract).getContractType().isGarrisonDuty()) { + resourceKey = "messageChallengeGarrison.text"; + } + // If resourceKey is not found, just return true, acting as if the player had accepted the mission if (resourceKey.isEmpty()) { return true;