|
34 | 34 |
|
35 | 35 | import java.util.List; |
36 | 36 |
|
| 37 | +import java.time.LocalDate; |
| 38 | +import java.util.HashSet; |
| 39 | +import java.util.Set; |
| 40 | + |
| 41 | +import megamek.common.annotations.Nullable; |
37 | 42 | import megamek.logging.MMLogger; |
38 | 43 | import mekhq.campaign.mission.AtBContract; |
| 44 | +import mekhq.campaign.universe.Faction; |
| 45 | +import mekhq.campaign.universe.FactionHints; |
| 46 | +import mekhq.campaign.universe.PlanetarySystem; |
39 | 47 |
|
40 | 48 |
|
41 | 49 | public class FactionStandingUtilities { |
@@ -341,4 +349,131 @@ public static boolean isUseCommandCircuit(boolean overridingCommandCircuitRequir |
341 | 349 | useCommandCircuit = hasCommandCircuitAccess(highestRegard); |
342 | 350 | return useCommandCircuit; |
343 | 351 | } |
| 352 | + |
| 353 | + /** |
| 354 | + * Determines whether a campaign force is allowed to enter the specified target planetary system, based on |
| 355 | + * population, ownership, outlaw status, contract relationships, and state of war. |
| 356 | + * |
| 357 | + * <p>The rules for entry are as follows:</p> |
| 358 | + * <ol> |
| 359 | + * <li>If the target system is empty (population zero), entry is always permitted.</li> |
| 360 | + * <li>If the target system is owned by any faction that is either an employer or a contract target, entry is |
| 361 | + * allowed.</li> |
| 362 | + * <li>If the player is outlawed in their current system, they may always exit to another system (unless {@code |
| 363 | + * currentSystem} is {@code null}).</li> |
| 364 | + * <li>If the player is outlawed in the target system, entry is denied.</li> |
| 365 | + * <li>If the campaign faction is at war with all system factions, entry is denied.</li> |
| 366 | + * <li>If all system factions are at war with any employer faction, entry is denied.</li> |
| 367 | + * <li>If none of the above conditions block entry, it is permitted.</li> |
| 368 | + * </ol> |
| 369 | + * |
| 370 | + * @param campaignFaction the campaign's primary faction |
| 371 | + * @param factionStandings the standings of the campaign with all factions |
| 372 | + * @param currentSystem the planetary system currently occupied |
| 373 | + * @param targetSystem the planetary system to test entry for |
| 374 | + * @param when the date of attempted entry (population/ownership may change over time) |
| 375 | + * @param activeAtBContracts list of currently active contracts |
| 376 | + * |
| 377 | + * @return {@code true} if entry to the target system is allowed; {@code false} otherwise |
| 378 | + * |
| 379 | + * @author Illiani |
| 380 | + * @since 0.50.07 |
| 381 | + */ |
| 382 | + public static boolean canEnterTargetSystem(Faction campaignFaction, FactionStandings factionStandings, |
| 383 | + @Nullable PlanetarySystem currentSystem, PlanetarySystem targetSystem, LocalDate when, |
| 384 | + List<AtBContract> activeAtBContracts) { |
| 385 | + // Always allowed in empty systems |
| 386 | + if (targetSystem.getPopulation(when) == 0) { |
| 387 | + LOGGER.debug("Target system is empty, access granted"); |
| 388 | + return true; |
| 389 | + } |
| 390 | + |
| 391 | + Set<Faction> systemFactions = targetSystem.getFactionSet(when); |
| 392 | + |
| 393 | + Set<Faction> contractEmployers = new HashSet<>(); |
| 394 | + Set<Faction> contractTargets = new HashSet<>(); |
| 395 | + for (AtBContract contract : activeAtBContracts) { |
| 396 | + contractEmployers.add(contract.getEmployerFaction()); |
| 397 | + contractTargets.add(contract.getEnemy()); |
| 398 | + } |
| 399 | + |
| 400 | + // Entry always allowed if the system is owned by any contract employer or target |
| 401 | + if (systemFactions.stream() |
| 402 | + .anyMatch(systemFaction -> contractEmployers.contains(systemFaction) |
| 403 | + || contractTargets.contains(systemFaction))) { |
| 404 | + LOGGER.debug("System is owned by a contract employer or target, access granted"); |
| 405 | + return true; |
| 406 | + } |
| 407 | + |
| 408 | + // Always allowed to leave if outlawed in the current system |
| 409 | + if (currentSystem != null) { |
| 410 | + if (isOutlawedInSystem(factionStandings, currentSystem, when)) { |
| 411 | + LOGGER.debug("Player is outlawed in current system, but always allowed to escape, access granted"); |
| 412 | + return true; |
| 413 | + } |
| 414 | + } |
| 415 | + |
| 416 | + // Banned if outlawed in the target system |
| 417 | + if (isOutlawedInSystem(factionStandings, targetSystem, when)) { |
| 418 | + LOGGER.debug("Player is outlawed in target system, access denied"); |
| 419 | + return false; |
| 420 | + } |
| 421 | + |
| 422 | + // Disallow if the campaign faction is at war with all system factions |
| 423 | + FactionHints factionHints = FactionHints.defaultFactionHints(); |
| 424 | + boolean allAtWarWithCampaign = systemFactions |
| 425 | + .stream() |
| 426 | + .allMatch(systemFaction -> factionHints.isAtWarWith(campaignFaction, |
| 427 | + systemFaction, |
| 428 | + when)); |
| 429 | + if (allAtWarWithCampaign) { |
| 430 | + LOGGER.debug("Campaign faction is at war with all system factions, access denied"); |
| 431 | + return false; |
| 432 | + } |
| 433 | + |
| 434 | + // Disallow if all system factions are at war with any one employer |
| 435 | + for (Faction employer : contractEmployers) { |
| 436 | + boolean allAtWarWithEmployer = systemFactions |
| 437 | + .stream() |
| 438 | + .allMatch(systemFaction -> factionHints.getCurrentWar(employer, |
| 439 | + systemFaction, |
| 440 | + when) != null); |
| 441 | + if (allAtWarWithEmployer) { |
| 442 | + LOGGER.debug("All system factions are at war with employer {}, access denied", employer.getShortName()); |
| 443 | + return false; |
| 444 | + } |
| 445 | + } |
| 446 | + |
| 447 | + LOGGER.debug("Access granted"); |
| 448 | + return true; |
| 449 | + } |
| 450 | + |
| 451 | + /** |
| 452 | + * Determines whether a faction is outlawed in the specified target system at a given date. |
| 453 | + * |
| 454 | + * <p>This method evaluates the highest standing ("regard") among all factions present in the target planetary |
| 455 | + * system on the specified date. It then checks whether the faction corresponding to the highest regard is |
| 456 | + * considered outlawed.</p> |
| 457 | + * |
| 458 | + * @param factionStandings the faction standings data to use for regard calculations |
| 459 | + * @param targetSystem the planetary system in which to perform the check |
| 460 | + * @param when the date for which to determine outlaw status |
| 461 | + * |
| 462 | + * @return {@code true} if the faction is outlawed in the target system at the given date; {@code false} otherwise |
| 463 | + * |
| 464 | + * @author Illiani |
| 465 | + * @since 0.50.07 |
| 466 | + */ |
| 467 | + private static boolean isOutlawedInSystem(FactionStandings factionStandings, PlanetarySystem targetSystem, |
| 468 | + LocalDate when) { |
| 469 | + double highestRegard = FactionStandingLevel.STANDING_LEVEL_0.getMinimumRegard(); |
| 470 | + for (Faction faction : targetSystem.getFactionSet(when)) { |
| 471 | + double currentRegard = factionStandings.getRegardForFaction(faction.getShortName(), true); |
| 472 | + if (currentRegard > highestRegard) { |
| 473 | + highestRegard = currentRegard; |
| 474 | + } |
| 475 | + } |
| 476 | + |
| 477 | + return isOutlawed(highestRegard); |
| 478 | + } |
344 | 479 | } |
0 commit comments