|
32 | 32 | */ |
33 | 33 | package mekhq.campaign.universe.factionStanding; |
34 | 34 |
|
| 35 | +import java.time.LocalDate; |
| 36 | +import java.util.HashSet; |
| 37 | +import java.util.List; |
| 38 | +import java.util.Set; |
| 39 | + |
| 40 | +import megamek.common.annotations.Nullable; |
35 | 41 | import megamek.logging.MMLogger; |
| 42 | +import mekhq.campaign.mission.AtBContract; |
| 43 | +import mekhq.campaign.universe.Faction; |
| 44 | +import mekhq.campaign.universe.FactionHints; |
| 45 | +import mekhq.campaign.universe.PlanetarySystem; |
36 | 46 |
|
37 | 47 |
|
38 | 48 | public class FactionStandingUtilities { |
@@ -290,4 +300,131 @@ public static int getSupportPointModifierPeriodic(final double regard) { |
290 | 300 |
|
291 | 301 | return standing.getSupportPointModifierPeriodic(); |
292 | 302 | } |
| 303 | + |
| 304 | + /** |
| 305 | + * Determines whether a campaign force is allowed to enter the specified target planetary system, based on |
| 306 | + * population, ownership, outlaw status, contract relationships, and state of war. |
| 307 | + * |
| 308 | + * <p>The rules for entry are as follows:</p> |
| 309 | + * <ol> |
| 310 | + * <li>If the target system is empty (population zero), entry is always permitted.</li> |
| 311 | + * <li>If the target system is owned by any faction that is either an employer or a contract target, entry is |
| 312 | + * allowed.</li> |
| 313 | + * <li>If the player is outlawed in their current system, they may always exit to another system (unless {@code |
| 314 | + * currentSystem} is {@code null}).</li> |
| 315 | + * <li>If the player is outlawed in the target system, entry is denied.</li> |
| 316 | + * <li>If the campaign faction is at war with all system factions, entry is denied.</li> |
| 317 | + * <li>If all system factions are at war with any employer faction, entry is denied.</li> |
| 318 | + * <li>If none of the above conditions block entry, it is permitted.</li> |
| 319 | + * </ol> |
| 320 | + * |
| 321 | + * @param campaignFaction the campaign's primary faction |
| 322 | + * @param factionStandings the standings of the campaign with all factions |
| 323 | + * @param currentSystem the planetary system currently occupied |
| 324 | + * @param targetSystem the planetary system to test entry for |
| 325 | + * @param when the date of attempted entry (population/ownership may change over time) |
| 326 | + * @param activeAtBContracts list of currently active contracts |
| 327 | + * |
| 328 | + * @return {@code true} if entry to the target system is allowed; {@code false} otherwise |
| 329 | + * |
| 330 | + * @author Illiani |
| 331 | + * @since 0.50.07 |
| 332 | + */ |
| 333 | + public static boolean canEnterTargetSystem(Faction campaignFaction, FactionStandings factionStandings, |
| 334 | + @Nullable PlanetarySystem currentSystem, PlanetarySystem targetSystem, LocalDate when, |
| 335 | + List<AtBContract> activeAtBContracts) { |
| 336 | + // Always allowed in empty systems |
| 337 | + if (targetSystem.getPopulation(when) == 0) { |
| 338 | + LOGGER.debug("Target system is empty, access granted"); |
| 339 | + return true; |
| 340 | + } |
| 341 | + |
| 342 | + Set<Faction> systemFactions = targetSystem.getFactionSet(when); |
| 343 | + |
| 344 | + Set<Faction> contractEmployers = new HashSet<>(); |
| 345 | + Set<Faction> contractTargets = new HashSet<>(); |
| 346 | + for (AtBContract contract : activeAtBContracts) { |
| 347 | + contractEmployers.add(contract.getEmployerFaction()); |
| 348 | + contractTargets.add(contract.getEnemy()); |
| 349 | + } |
| 350 | + |
| 351 | + // Entry always allowed if the system is owned by any contract employer or target |
| 352 | + if (systemFactions.stream() |
| 353 | + .anyMatch(systemFaction -> contractEmployers.contains(systemFaction) |
| 354 | + || contractTargets.contains(systemFaction))) { |
| 355 | + LOGGER.debug("System is owned by a contract employer or target, access granted"); |
| 356 | + return true; |
| 357 | + } |
| 358 | + |
| 359 | + // Always allowed to leave if outlawed in the current system |
| 360 | + if (currentSystem != null) { |
| 361 | + if (isOutlawedInSystem(factionStandings, currentSystem, when)) { |
| 362 | + LOGGER.debug("Player is outlawed in current system, but always allowed to escape, access granted"); |
| 363 | + return true; |
| 364 | + } |
| 365 | + } |
| 366 | + |
| 367 | + // Banned if outlawed in the target system |
| 368 | + if (isOutlawedInSystem(factionStandings, targetSystem, when)) { |
| 369 | + LOGGER.debug("Player is outlawed in target system, access denied"); |
| 370 | + return false; |
| 371 | + } |
| 372 | + |
| 373 | + // Disallow if the campaign faction is at war with all system factions |
| 374 | + FactionHints factionHints = FactionHints.defaultFactionHints(); |
| 375 | + boolean allAtWarWithCampaign = systemFactions |
| 376 | + .stream() |
| 377 | + .allMatch(systemFaction -> factionHints.isAtWarWith(campaignFaction, |
| 378 | + systemFaction, |
| 379 | + when)); |
| 380 | + if (allAtWarWithCampaign) { |
| 381 | + LOGGER.debug("Campaign faction is at war with all system factions, access denied"); |
| 382 | + return false; |
| 383 | + } |
| 384 | + |
| 385 | + // Disallow if all system factions are at war with any one employer |
| 386 | + for (Faction employer : contractEmployers) { |
| 387 | + boolean allAtWarWithEmployer = systemFactions |
| 388 | + .stream() |
| 389 | + .allMatch(systemFaction -> factionHints.getCurrentWar(employer, |
| 390 | + systemFaction, |
| 391 | + when) != null); |
| 392 | + if (allAtWarWithEmployer) { |
| 393 | + LOGGER.debug("All system factions are at war with employer {}, access denied", employer.getShortName()); |
| 394 | + return false; |
| 395 | + } |
| 396 | + } |
| 397 | + |
| 398 | + LOGGER.debug("Access granted"); |
| 399 | + return true; |
| 400 | + } |
| 401 | + |
| 402 | + /** |
| 403 | + * Determines whether a faction is outlawed in the specified target system at a given date. |
| 404 | + * |
| 405 | + * <p>This method evaluates the highest standing ("regard") among all factions present in the target planetary |
| 406 | + * system on the specified date. It then checks whether the faction corresponding to the highest regard is |
| 407 | + * considered outlawed.</p> |
| 408 | + * |
| 409 | + * @param factionStandings the faction standings data to use for regard calculations |
| 410 | + * @param targetSystem the planetary system in which to perform the check |
| 411 | + * @param when the date for which to determine outlaw status |
| 412 | + * |
| 413 | + * @return {@code true} if the faction is outlawed in the target system at the given date; {@code false} otherwise |
| 414 | + * |
| 415 | + * @author Illiani |
| 416 | + * @since 0.50.07 |
| 417 | + */ |
| 418 | + private static boolean isOutlawedInSystem(FactionStandings factionStandings, PlanetarySystem targetSystem, |
| 419 | + LocalDate when) { |
| 420 | + double highestRegard = FactionStandingLevel.STANDING_LEVEL_0.getMinimumRegard(); |
| 421 | + for (Faction faction : targetSystem.getFactionSet(when)) { |
| 422 | + double currentRegard = factionStandings.getRegardForFaction(faction.getShortName(), true); |
| 423 | + if (currentRegard > highestRegard) { |
| 424 | + highestRegard = currentRegard; |
| 425 | + } |
| 426 | + } |
| 427 | + |
| 428 | + return isOutlawed(highestRegard); |
| 429 | + } |
293 | 430 | } |
0 commit comments