diff --git a/MekHQ/data/universe/academies/Prestigious Academies.xml b/MekHQ/data/universe/academies/Prestigious Academies.xml
index b94ef7bc41..4cb0fd5eef 100644
--- a/MekHQ/data/universe/academies/Prestigious Academies.xml
+++ b/MekHQ/data/universe/academies/Prestigious Academies.xml
@@ -23,8 +23,6 @@
Addicks University
University
Located in the heart of the Inner Sphere, Addicks University is celebrated for its progressive liberal arts education, particularly in teacher training. The institution uniquely integrates its academic programs with community schools, allowing student teachers to gain invaluable hands-on experience.
- 0
- true
Addicks
2400
2777
@@ -34,6 +32,9 @@
High School
College
16
+ Teaching Graduate
+ Leadership
+ 2300
0
@@ -1795,7 +1796,6 @@
Semester One Curriculum
Small Arms
2300
- 2300
-1
@@ -2324,7 +2324,6 @@
Armored Infantry Graduate
Gunnery/Battlesuit, Anti-Mech
3058
- 3058
Aerospace Graduate
Piloting/Aerospace, Gunnery/Aerospace
2490
@@ -2422,12 +2421,15 @@
Terra
2300
2777
- #N/A
+ 7500
1200
5
High School
Doctorate
16
+ Intergalactic Diplomacy in the Modern Age
+ Negotiation, Leadership
+ 2300
0
diff --git a/MekHQ/data/universe/factions.xml b/MekHQ/data/universe/factions.xml
index f7670bbb69..9eae41e27d 100644
--- a/MekHQ/data/universe/factions.xml
+++ b/MekHQ/data/universe/factions.xml
@@ -1101,6 +1101,8 @@ successor - unimplemented tag describing another faction code as the specified f
FC
Federated Commonwealth
+ Federated Commonwealth Alliance
+ Federated Commonwealth
FS,LA
New Avalon
0,0,0,1,2,2,1,0,0
@@ -1110,6 +1112,8 @@ successor - unimplemented tag describing another faction code as the specified f
Federated Commonwealth.png
248,212,44
is,super,playable
+ 3028
+ 3068
FVC
diff --git a/MekHQ/data/universe/planetary_systems/system_events.xml b/MekHQ/data/universe/planetary_systems/system_events.xml
index 019f8b6c9e..655ff9747e 100644
--- a/MekHQ/data/universe/planetary_systems/system_events.xml
+++ b/MekHQ/data/universe/planetary_systems/system_events.xml
@@ -932465,6 +932465,10 @@
3064-01-05
FC
+
+ 3063-01-01
+ LA
+
3070-01-01
5162011707
diff --git a/MekHQ/docs/Personnel Modules/Turnover & Retention Module.pdf b/MekHQ/docs/Personnel Modules/Turnover & Retention Module.pdf
index 2d072eac14..27adf50d92 100644
Binary files a/MekHQ/docs/Personnel Modules/Turnover & Retention Module.pdf and b/MekHQ/docs/Personnel Modules/Turnover & Retention Module.pdf differ
diff --git a/MekHQ/docs/history.txt b/MekHQ/docs/history.txt
index 187bf27459..167759b7a9 100644
--- a/MekHQ/docs/history.txt
+++ b/MekHQ/docs/history.txt
@@ -103,6 +103,7 @@ MEKHQ VERSION HISTORY:
+ Fix #4235: Fixed Invalid Parsing of Early Childhood when Loading Personnel
+ PR #4242: Added Prestigious Academies D-F
+ PR #4246: Added Prestigious Academies G-J
++ PR #4248: Fixed Missing Qualification from James McKenna University
0.49.19.1 (2024-05-14 1800 UTC)
+ Milestone Release. Backported fixes.
diff --git a/MekHQ/resources/mekhq/resources/Education.properties b/MekHQ/resources/mekhq/resources/Education.properties
index 716210ccf5..bb2c047e80 100644
--- a/MekHQ/resources/mekhq/resources/Education.properties
+++ b/MekHQ/resources/mekhq/resources/Education.properties
@@ -28,6 +28,9 @@ typeInstitute.text=Institute
typeUniversity.text=University
typePolytechnic.text=Polytechnic
typeSchool.text=School
+typeSchoolBoarding.text=Boarding School
+typeFinishingSchool.text=Finishing School
+typePreparatorySchool.text=Preparatory School
#### Academy Suffix
suffixTechnology.text=Technology
diff --git a/MekHQ/resources/mekhq/resources/GUI.properties b/MekHQ/resources/mekhq/resources/GUI.properties
index 0838bd8f9e..e6e49e0f06 100644
--- a/MekHQ/resources/mekhq/resources/GUI.properties
+++ b/MekHQ/resources/mekhq/resources/GUI.properties
@@ -336,6 +336,10 @@ UnresolvedStratConContactsNagDialog.text=You have unresolved contacts on the Str
cargoCapacityNagDialog.title=Exceeded Cargo Capacity
cargoCapacityNagDialog.text=You have exceeded your available cargo capacity. Do you really wish to advance the day?
+### FactionDestroyedNagDialog Class
+InvalidFactionNagDialog.title=Invalid Faction
+InvalidFactionNagDialog.text=Your campaign faction is invalid. Either it has not yet been created, or it has been destroyed.\n\nPlease pick a new faction in campaign options.\nAdvance day anyway?
+
#### Report Dialogs
### CargoReportDialog Class
CargoReportDialog.title=Cargo Report
@@ -713,6 +717,8 @@ optionOutstandingScenariosNag.text=Hide Outstanding Scenarios Nag
optionOutstandingScenariosNag.toolTipText=This allows you to ignore the daily warning for having unfinished scenarios on the current day.
optionCargoCapacityNag.text=Hide Exceeded Cargo Capacity Nag
optionCargoCapacityNag.toolTipText=This allows you to ignore the daily warning for having exceeded your available cargo capacity.
+optionInvalidFactionNag.text=Hide Invalid Faction Nag
+optionInvalidFactionNag.toolTipText=This allows you to ignore the daily warning for having an invalid faction.
## Miscellaneous Tab
miscellaneousTab.title=Miscellaneous Options
lblUserDir.text=User Files Directory:
@@ -1287,8 +1293,9 @@ chkFilterByPercentageOfCost.text=Show only units at
chkFilterByPercentageOfCost.toolTipText=Filter out units above the specified percentage of market value.
spnFilterByPercentageOfCost.toolTipText=Filter out units with market value above this percentage when the filter is enabled.
lblCostPercentageThreshold.text=% of market value or lower
-### Black Market Panel
-lblBlackMarketWarning.text=Open Market & Mercenary Auction: The public sale of units available to your campaign faction\
+### Market Description Panel
+lblMarketDescriptions.text=Open Market: The public sale of units available to your campaign faction\
+
Mercenary Auction: The sale of unwanted units sold through the MRBC (or the era-appropriate equivalent)\
Employer Market: Units or salvage no longer wanted by your employer\
Factory Line: Brand-new units fresh off the production line\
Black Market: Potentially brand-new units which come with the risk of being swindled
diff --git a/MekHQ/src/mekhq/MHQConstants.java b/MekHQ/src/mekhq/MHQConstants.java
index feb0413b54..70f1a462c7 100644
--- a/MekHQ/src/mekhq/MHQConstants.java
+++ b/MekHQ/src/mekhq/MHQConstants.java
@@ -176,6 +176,7 @@ public final class MHQConstants extends SuiteConstants {
public static final String NAG_UNRESOLVED_STRATCON_CONTACTS = "nagUnresolvedStratConContacts";
public static final String NAG_OUTSTANDING_SCENARIOS = "nagOutstandingScenarios";
public static final String NAG_CARGO_CAPACITY = "nagCargoCapacity";
+ public static final String NAG_INVALID_FACTION = "nagInvalidFaction";
//endregion Nag Tab
//region Miscellaneous Options
diff --git a/MekHQ/src/mekhq/campaign/Campaign.java b/MekHQ/src/mekhq/campaign/Campaign.java
index 4b66245935..d8c8aa821e 100644
--- a/MekHQ/src/mekhq/campaign/Campaign.java
+++ b/MekHQ/src/mekhq/campaign/Campaign.java
@@ -1334,6 +1334,12 @@ public Person newDependent(boolean baby) {
Gender.RANDOMIZE);
}
+ if (person.getAge(getLocalDate()) <= 16) {
+ person.setEduHighestEducation(EducationLevel.EARLY_CHILDHOOD);
+ } else {
+ person.setEduHighestEducation(EducationLevel.HIGH_SCHOOL);
+ }
+
return person;
}
diff --git a/MekHQ/src/mekhq/campaign/ResolveScenarioTracker.java b/MekHQ/src/mekhq/campaign/ResolveScenarioTracker.java
index 60d3d8f27c..6a3fb160d1 100644
--- a/MekHQ/src/mekhq/campaign/ResolveScenarioTracker.java
+++ b/MekHQ/src/mekhq/campaign/ResolveScenarioTracker.java
@@ -1475,6 +1475,7 @@ public void resolveScenario(ScenarioStatus resolution, String report) {
person.getHyperlinkedName()));
}
+ // prisoners should generate with lower than average loyalty, so only roll 2d6
person.generateLoyalty(Compute.d6(2));
} else {
continue;
diff --git a/MekHQ/src/mekhq/campaign/market/enums/UnitMarketType.java b/MekHQ/src/mekhq/campaign/market/enums/UnitMarketType.java
index 8081ca259a..9cc648fd4c 100644
--- a/MekHQ/src/mekhq/campaign/market/enums/UnitMarketType.java
+++ b/MekHQ/src/mekhq/campaign/market/enums/UnitMarketType.java
@@ -108,7 +108,49 @@ public String toString() {
return name;
}
+ /**
+ * Calculates the price percentage based on a given modifier and d6 roll.
+ *
+ * @param modifier the modifier to adjust the price (a negative modifier decreases price, positive increases price)
+ * @return the calculated price
+ * @throws IllegalStateException if the roll value is unexpected
+ */
+ public static int getPricePercentage(int modifier) {
+ int roll = Compute.d6(2);
+ int value;
+ switch (roll) {
+ case 2:
+ value = modifier + 3;
+ break;
+ case 3:
+ value = modifier + 2;
+ break;
+ case 4:
+ case 5:
+ value = modifier + 1;
+ break;
+ case 6:
+ case 7:
+ case 8:
+ value = modifier;
+ break;
+ case 9:
+ case 10:
+ value = modifier - 1;
+ break;
+ case 11:
+ value = modifier - 2;
+ break;
+ case 12:
+ value = modifier - 3;
+ break;
+ default:
+ throw new IllegalStateException("Unexpected value in mekhq/campaign/market/unitMarket/AtBMonthlyUnitMarket.java/getPrice: " + roll);
+ }
+
+ return 100 + (value * 5);
+ }
/**
* Returns the quality of a unit based on the given market type.
diff --git a/MekHQ/src/mekhq/campaign/market/unitMarket/AtBMonthlyUnitMarket.java b/MekHQ/src/mekhq/campaign/market/unitMarket/AtBMonthlyUnitMarket.java
index 1f20e06489..23050ce4b2 100644
--- a/MekHQ/src/mekhq/campaign/market/unitMarket/AtBMonthlyUnitMarket.java
+++ b/MekHQ/src/mekhq/campaign/market/unitMarket/AtBMonthlyUnitMarket.java
@@ -39,6 +39,8 @@
import java.util.Collection;
import java.util.List;
+import static mekhq.campaign.market.enums.UnitMarketType.getPricePercentage;
+
public class AtBMonthlyUnitMarket extends AbstractUnitMarket {
//region Constructors
public AtBMonthlyUnitMarket() {
@@ -76,70 +78,70 @@ public void generateUnitOffers(final Campaign campaign) {
Faction faction = campaign.getFaction();
addOffers(campaign, getMarketItemCount("uncommon"), UnitMarketType.OPEN, UnitType.MEK,
- faction, IUnitRating.DRAGOON_F, 6);
+ faction, IUnitRating.DRAGOON_F, 1);
addOffers(campaign, getMarketItemCount("uncommon"), UnitMarketType.OPEN, UnitType.AEROSPACEFIGHTER,
- faction, IUnitRating.DRAGOON_F, 6);
+ faction, IUnitRating.DRAGOON_F, 1);
addOffers(campaign, getMarketItemCount("very common"), UnitMarketType.OPEN, UnitType.TANK,
- faction, IUnitRating.DRAGOON_F, 6);
+ faction, IUnitRating.DRAGOON_F, 1);
addOffers(campaign, getMarketItemCount("common"), UnitMarketType.OPEN, UnitType.CONV_FIGHTER,
- faction, IUnitRating.DRAGOON_F, 6);
+ faction, IUnitRating.DRAGOON_F, 1);
if ((contract != null) && (campaign.getLocalDate().isAfter(contract.getStartDate().minusDays(1)))) {
// Employer Market
faction = contract.getEmployerFaction();
addOffers(campaign, getMarketItemCount("rare"), UnitMarketType.EMPLOYER, UnitType.MEK,
- faction, IUnitRating.DRAGOON_D, 8);
+ faction, IUnitRating.DRAGOON_D, -1);
addOffers(campaign, getMarketItemCount("rare"), UnitMarketType.EMPLOYER, UnitType.AEROSPACEFIGHTER,
- faction, IUnitRating.DRAGOON_D, 8);
+ faction, IUnitRating.DRAGOON_D, -1);
addOffers(campaign, getMarketItemCount("common"), UnitMarketType.EMPLOYER, UnitType.TANK,
- faction, IUnitRating.DRAGOON_D, 8);
+ faction, IUnitRating.DRAGOON_D, -1);
addOffers(campaign, getMarketItemCount("uncommon"), UnitMarketType.EMPLOYER, UnitType.CONV_FIGHTER,
- faction, IUnitRating.DRAGOON_D, 8);
+ faction, IUnitRating.DRAGOON_D, -1);
// Unwanted Salvage Market
faction = contract.getEnemy();
addOffers(campaign, getMarketItemCount("rare"), UnitMarketType.EMPLOYER, UnitType.MEK,
- faction, IUnitRating.DRAGOON_F, 5);
+ faction, IUnitRating.DRAGOON_F, 2);
addOffers(campaign, getMarketItemCount("rare"), UnitMarketType.EMPLOYER, UnitType.AEROSPACEFIGHTER,
- faction, IUnitRating.DRAGOON_F, 5);
+ faction, IUnitRating.DRAGOON_F, 2);
addOffers(campaign, getMarketItemCount("common"), UnitMarketType.EMPLOYER, UnitType.TANK,
- faction, IUnitRating.DRAGOON_F, 5);
+ faction, IUnitRating.DRAGOON_F, 2);
addOffers(campaign, getMarketItemCount("uncommon"), UnitMarketType.EMPLOYER, UnitType.CONV_FIGHTER,
- faction, IUnitRating.DRAGOON_F, 5);
+ faction, IUnitRating.DRAGOON_F, 2);
}
// Mercenary Market
if (!campaign.getFaction().isClan()) {
faction = Factions.getInstance().getFaction("MERC");
- int modifier = -1;
+ int modifier = 1;
if (campaign.getFaction().isMercenary()) {
- modifier = 1;
+ modifier = -1;
}
addOffers(campaign, getMarketItemCount("uncommon"), UnitMarketType.MERCENARY, UnitType.MEK,
- faction, IUnitRating.DRAGOON_C, 7 + modifier);
+ faction, IUnitRating.DRAGOON_C, modifier);
addOffers(campaign, getMarketItemCount("uncommon"), UnitMarketType.MERCENARY, UnitType.AEROSPACEFIGHTER,
- faction, IUnitRating.DRAGOON_C, 7 + modifier);
+ faction, IUnitRating.DRAGOON_C, modifier);
addOffers(campaign, getMarketItemCount("very common"), UnitMarketType.MERCENARY, UnitType.TANK,
- faction, IUnitRating.DRAGOON_C, 7 + modifier);
+ faction, IUnitRating.DRAGOON_C, modifier);
addOffers(campaign, getMarketItemCount("common"), UnitMarketType.MERCENARY, UnitType.CONV_FIGHTER,
- faction, IUnitRating.DRAGOON_C, 7 + modifier);
+ faction, IUnitRating.DRAGOON_C, modifier);
}
// Factory Market
@@ -149,16 +151,16 @@ public void generateUnitOffers(final Campaign campaign) {
if ((!campaign.getFaction().isClan()) && (faction != null) && (!faction.isClan())) {
addOffers(campaign, getMarketItemCount("rare"), UnitMarketType.FACTORY, UnitType.MEK,
- faction, IUnitRating.DRAGOON_A, 6);
+ faction, IUnitRating.DRAGOON_A, 2);
addOffers(campaign, getMarketItemCount("rare"), UnitMarketType.FACTORY, UnitType.AEROSPACEFIGHTER,
- faction, IUnitRating.DRAGOON_A, 6);
+ faction, IUnitRating.DRAGOON_A, 2);
addOffers(campaign, getMarketItemCount("common"), UnitMarketType.FACTORY, UnitType.TANK,
- faction, IUnitRating.DRAGOON_A, 6);
+ faction, IUnitRating.DRAGOON_A, 2);
addOffers(campaign, getMarketItemCount("uncommon"), UnitMarketType.FACTORY, UnitType.CONV_FIGHTER,
- faction, IUnitRating.DRAGOON_A, 6);
+ faction, IUnitRating.DRAGOON_A, 2);
}
}
@@ -167,13 +169,13 @@ public void generateUnitOffers(final Campaign campaign) {
// Clan Factory Market
if ((faction.isClan()) && (campaign.getCurrentSystem().getFactionSet(campaign.getLocalDate()).contains(faction))) {
addOffers(campaign, getMarketItemCount("very common"), UnitMarketType.FACTORY, UnitType.MEK,
- faction, IUnitRating.DRAGOON_A, 9);
+ faction, IUnitRating.DRAGOON_A, -4);
addOffers(campaign, getMarketItemCount("common"), UnitMarketType.FACTORY, UnitType.AEROSPACEFIGHTER,
- faction, IUnitRating.DRAGOON_A, 9);
+ faction, IUnitRating.DRAGOON_A, -4);
addOffers(campaign, getMarketItemCount("uncommon"), UnitMarketType.FACTORY, UnitType.TANK,
- faction, IUnitRating.DRAGOON_A, 9);
+ faction, IUnitRating.DRAGOON_A, -4);
}
// Black Market
@@ -182,16 +184,16 @@ public void generateUnitOffers(final Campaign campaign) {
.getFactionSet(campaign.getLocalDate()));
addOffers(campaign, getMarketItemCount("very rare"), UnitMarketType.BLACK_MARKET, UnitType.MEK,
- faction, IUnitRating.DRAGOON_A, 9);
+ faction, IUnitRating.DRAGOON_A, -8);
addOffers(campaign, getMarketItemCount("very rare"), UnitMarketType.BLACK_MARKET, UnitType.AEROSPACEFIGHTER,
- faction, IUnitRating.DRAGOON_A, 9);
+ faction, IUnitRating.DRAGOON_A, -8);
- addOffers(campaign, getMarketItemCount("uncommon"), UnitMarketType.BLACK_MARKET, UnitType.TANK,
- faction, IUnitRating.DRAGOON_A, 9);
+ addOffers(campaign, getMarketItemCount("rare"), UnitMarketType.BLACK_MARKET, UnitType.TANK,
+ faction, IUnitRating.DRAGOON_A, -8);
addOffers(campaign, getMarketItemCount("rare"), UnitMarketType.BLACK_MARKET, UnitType.CONV_FIGHTER,
- faction, IUnitRating.DRAGOON_A, 9);
+ faction, IUnitRating.DRAGOON_A, -8);
}
writeRefreshReport(campaign);
@@ -224,7 +226,7 @@ private int getMarketItemCount(String rarity) {
@Override
public void addOffers(final Campaign campaign, final int num, UnitMarketType market,
final int unitType, @Nullable Faction faction, final int quality,
- final int priceTarget) {
+ final int priceModifier) {
if (faction == null) {
faction = RandomFactionGenerator.getInstance().getEmployerFaction();
}
@@ -268,8 +270,7 @@ public void addOffers(final Campaign campaign, final int num, UnitMarketType mar
}
}
- final int percent = 100 - (Compute.d6(2) - priceTarget) * 5;
- addSingleUnit(campaign, market, unitType, faction, quality, movementModes, missionRoles, percent);
+ addSingleUnit(campaign, market, unitType, faction, quality, movementModes, missionRoles, getPricePercentage(priceModifier));
}
}
diff --git a/MekHQ/src/mekhq/campaign/personnel/Person.java b/MekHQ/src/mekhq/campaign/personnel/Person.java
index cd1fb0567d..81c7e9cf91 100644
--- a/MekHQ/src/mekhq/campaign/personnel/Person.java
+++ b/MekHQ/src/mekhq/campaign/personnel/Person.java
@@ -3800,7 +3800,7 @@ public void fixReferences(final Campaign campaign) {
* @throws IllegalArgumentException if the provided roll is not between 3 and 18
*/
public void generateLoyalty(int roll) {
- if (roll == 3) {
+ if (roll <= 3) {
setLoyalty(3);
} else if (roll == 4) {
setLoyalty(2);
@@ -3812,7 +3812,7 @@ public void generateLoyalty(int roll) {
setLoyalty(-1);
} else if (roll == 17) {
setLoyalty(-2);
- } else if (roll == 18){
+ } else if (roll >= 18){
setLoyalty(-3);
} else {
throw new IllegalArgumentException("Invalid roll in mekhq/campaign/personnel/Person.java/generateLoyalty: " + roll);
diff --git a/MekHQ/src/mekhq/campaign/personnel/education/EducationController.java b/MekHQ/src/mekhq/campaign/personnel/education/EducationController.java
index b5d0c4f20e..8d925ea03c 100644
--- a/MekHQ/src/mekhq/campaign/personnel/education/EducationController.java
+++ b/MekHQ/src/mekhq/campaign/personnel/education/EducationController.java
@@ -146,6 +146,14 @@ public static void enrollPerson(Campaign campaign, Person person, Academy academ
public static String generateName(Academy academy, String campus) {
ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.Education", MekHQ.getMHQOptions().getLocale());
+ if (academy.isPrepSchool()) {
+ if (Compute.d6(1) <= 3) {
+ return campus + ' ' + generateTypeChild(resources) + ' ' + resources.getString("conjoinerOf.text") + ' ' + generateSuffix(resources);
+ } else {
+ return generateTypeChild(resources) + ' ' + resources.getString("conjoinerOf.text") + ' ' + campus;
+ }
+ }
+
if (Compute.d6(1) <= 3) {
if (academy.isMilitary()) {
return campus + ' ' + generateMilitaryPrefix(resources) + ' ' + generateTypeAdult(resources) + ' ' + resources.getString("conjoinerOf.text") + ' ' + generateSuffix(resources);
@@ -239,6 +247,33 @@ private static String generateTypeAdult(ResourceBundle resources) {
}
}
+
+ /**
+ * Generates a random educational institution type from the provided ResourceBundle.
+ *
+ * @param resources the ResourceBundle containing the localized strings for the educational institution types
+ * @return a randomly selected educational institution type
+ * @throws IllegalStateException if the generated roll is unexpected
+ */
+ private static String generateTypeChild(ResourceBundle resources) {
+ switch (Compute.d6(1)) {
+ case 1:
+ return resources.getString("typeAcademy.text");
+ case 2:
+ return resources.getString("typePreparatorySchool.text");
+ case 3:
+ return resources.getString("typeInstitute.text");
+ case 4:
+ return resources.getString("typeSchoolBoarding.text");
+ case 5:
+ return resources.getString("typeFinishingSchool.text");
+ case 6:
+ return resources.getString("typeSchool.text");
+ default:
+ throw new IllegalStateException("Unexpected roll in generateTypeAdult");
+ }
+ }
+
/**
* Processes a new day for a person in a campaign.
*
@@ -807,10 +842,12 @@ private static void processGraduation(Campaign campaign, Person person, Academy
person.setEduHighestEducation(EducationLevel.parseFromInt(educationLevel));
}
- if ((academy.isReeducationCamp()) && (campaign.getCampaignOptions().isUseReeducationCamps())) {
- if (!Objects.equals(person.getOriginFaction(), campaign.getFaction())) {
+ if (academy.isReeducationCamp()) {
+ if (campaign.getCampaignOptions().isUseReeducationCamps()) {
person.setOriginFaction(campaign.getFaction());
}
+
+ person.generateLoyalty(Compute.d6(4));
}
}
diff --git a/MekHQ/src/mekhq/gui/CampaignGUI.java b/MekHQ/src/mekhq/gui/CampaignGUI.java
index 2f158d0fc7..7a61c7fd14 100644
--- a/MekHQ/src/mekhq/gui/CampaignGUI.java
+++ b/MekHQ/src/mekhq/gui/CampaignGUI.java
@@ -2447,6 +2447,11 @@ public void handleDayEnding(DayEndingEvent evt) {
return;
}
+ if (new InvalidFactionNagDialog(getFrame(), getCampaign()).showDialog().isCancelled()) {
+ evt.cancel();
+ return;
+ }
+
if (new InsufficientAstechsNagDialog(getFrame(), getCampaign()).showDialog().isCancelled()) {
evt.cancel();
return;
diff --git a/MekHQ/src/mekhq/gui/StratconPanel.java b/MekHQ/src/mekhq/gui/StratconPanel.java
index 4103132604..708a76a70d 100644
--- a/MekHQ/src/mekhq/gui/StratconPanel.java
+++ b/MekHQ/src/mekhq/gui/StratconPanel.java
@@ -528,7 +528,7 @@ private void drawScenarios(Graphics2D g2D) {
((scenario.getDeploymentDate() != null) ||
(scenario.isStrategicObjective() && currentTrack.getRevealedCoords().contains(currentCoords)) ||
currentTrack.isGmRevealed() || trackRevealed)) {
- g2D.setColor(Color.RED);
+ g2D.setColor(MekHQ.getMHQOptions().getFontColorNegative());
BufferedImage scenarioImage = getImage(StratconBiomeManifest.FORCE_HOSTILE, ImageType.TerrainTile);
if (scenarioImage != null) {
@@ -836,16 +836,19 @@ private String buildSelectedHexInfo() {
if ((facility != null) && (facility.getFacilityType() != null)) {
if (facility.isStrategicObjective()) {
infoBuilder.append(String.format("
Contract objective located",
- facility.getOwner() == ForceAlignment.Allied ? "green" : "red"));
+ facility.getOwner() == ForceAlignment.Allied ? MekHQ.getMHQOptions().getFontColorPositiveHexColor() : MekHQ.getMHQOptions().getFontColorNegativeHexColor()));
}
- infoBuilder.append((facility.getOwner() == ForceAlignment.Allied) ? "" : "")
+ infoBuilder.append("")
.append("
")
.append(facility.getFormattedDisplayableName())
.append("");
}
} else {
- infoBuilder.append("Recon incomplete");
+ infoBuilder.append("Recon incomplete");
}
infoBuilder.append("
");
diff --git a/MekHQ/src/mekhq/gui/StratconTab.java b/MekHQ/src/mekhq/gui/StratconTab.java
index c94791c388..89b10adb81 100644
--- a/MekHQ/src/mekhq/gui/StratconTab.java
+++ b/MekHQ/src/mekhq/gui/StratconTab.java
@@ -271,9 +271,9 @@ private String buildShortStrategicObjectiveText(StratconCampaignState campaignSt
StringBuilder sb = new StringBuilder();
if (completedObjectives >= desiredObjectives) {
- sb.append("");
+ sb.append("");
} else {
- sb.append("");
+ sb.append("");
}
// special logic for non-independent command clauses
@@ -285,7 +285,7 @@ private String buildShortStrategicObjectiveText(StratconCampaignState campaignSt
}
}
- sb.append("Strategic objectives: " + completedObjectives + "/" + desiredObjectives + " completed");
+ sb.append("Strategic objectives: ").append(completedObjectives).append('/').append(desiredObjectives).append(" completed");
return sb.toString();
}
@@ -313,16 +313,16 @@ private String buildStrategicObjectiveText(StratconCampaignState campaignState)
// special case: allied facilities can get lost at any point in time
if ((objective.getObjectiveType() == StrategicObjectiveType.AlliedFacilityControl) &&
!campaignState.allowEarlyVictory()) {
- sb.append("").append(OBJECTIVE_IN_PROGRESS);
+ sb.append("").append(OBJECTIVE_IN_PROGRESS);
} else if (objectiveCompleted) {
- sb.append("").append(OBJECTIVE_COMPLETED);
+ sb.append("").append(OBJECTIVE_COMPLETED);
} else if (objectiveFailed) {
- sb.append("").append(OBJECTIVE_FAILED);
+ sb.append("").append(OBJECTIVE_FAILED);
} else {
- sb.append("").append(OBJECTIVE_IN_PROGRESS);
+ sb.append("").append(OBJECTIVE_IN_PROGRESS);
}
- sb.append(" ");
+ sb.append(' ');
if (!coordsRevealed && displayCoordinateData) {
sb.append("Locate and ");
@@ -368,11 +368,11 @@ private String buildStrategicObjectiveText(StratconCampaignState campaignState)
boolean contractIsActive = campaignState.getContract().isActiveOn(getCampaignGui().getCampaign().getLocalDate());
if (contractIsActive) {
- sb.append("").append(OBJECTIVE_IN_PROGRESS);
+ sb.append("").append(OBJECTIVE_IN_PROGRESS);
} else if (campaignState.getVictoryPoints() > 0) {
- sb.append("").append(OBJECTIVE_COMPLETED);
+ sb.append("").append(OBJECTIVE_COMPLETED);
} else {
- sb.append("").append(OBJECTIVE_FAILED);
+ sb.append("").append(OBJECTIVE_FAILED);
}
sb.append(" Maintain Campaign Victory Point count above 0 by completing required scenarios")
diff --git a/MekHQ/src/mekhq/gui/adapter/PersonnelTableMouseAdapter.java b/MekHQ/src/mekhq/gui/adapter/PersonnelTableMouseAdapter.java
index d965f7a44a..d1bcb0981f 100644
--- a/MekHQ/src/mekhq/gui/adapter/PersonnelTableMouseAdapter.java
+++ b/MekHQ/src/mekhq/gui/adapter/PersonnelTableMouseAdapter.java
@@ -1518,7 +1518,7 @@ protected Optional createPopupMenu() {
}
if (academySetNames.contains("Prestigious Academies")) {
- if (!campaign.getCampaignOptions().isEnableLocalAcademies()) {
+ if (!campaign.getCampaignOptions().isEnablePrestigiousAcademies()) {
academySetNames.remove("Prestigious Academies");
}
}
diff --git a/MekHQ/src/mekhq/gui/dialog/HireBulkPersonnelDialog.java b/MekHQ/src/mekhq/gui/dialog/HireBulkPersonnelDialog.java
index e443dbce1d..5811180f8b 100644
--- a/MekHQ/src/mekhq/gui/dialog/HireBulkPersonnelDialog.java
+++ b/MekHQ/src/mekhq/gui/dialog/HireBulkPersonnelDialog.java
@@ -28,6 +28,7 @@
import mekhq.campaign.personnel.Person;
import mekhq.campaign.personnel.enums.PersonnelRole;
import mekhq.campaign.personnel.enums.Profession;
+import mekhq.campaign.personnel.enums.education.EducationLevel;
import mekhq.gui.CampaignGUI;
import mekhq.gui.displayWrappers.RankDisplay;
import org.apache.logging.log4j.LogManager;
@@ -357,6 +358,13 @@ private void hire(boolean isGmHire) {
person.limitSkills(age - 13);
}
+ // set education based on age
+ if (age <= 16) {
+ person.setEduHighestEducation(EducationLevel.EARLY_CHILDHOOD);
+ } else {
+ person.setEduHighestEducation(EducationLevel.HIGH_SCHOOL);
+ }
+
if (!campaign.recruitPerson(person, isGmHire)) {
number = 0;
} else {
diff --git a/MekHQ/src/mekhq/gui/dialog/MHQOptionsDialog.java b/MekHQ/src/mekhq/gui/dialog/MHQOptionsDialog.java
index fff07f6f89..0447e32d16 100644
--- a/MekHQ/src/mekhq/gui/dialog/MHQOptionsDialog.java
+++ b/MekHQ/src/mekhq/gui/dialog/MHQOptionsDialog.java
@@ -159,6 +159,7 @@ public class MHQOptionsDialog extends AbstractMHQButtonDialog {
private JCheckBox optionUnresolvedStratConContactsNag;
private JCheckBox optionOutstandingScenariosNag;
private JCheckBox optionCargoCapacityNag;
+ private JCheckBox optionInvalidFactionNag;
//endregion Nag Tab
//region Miscellaneous
@@ -915,6 +916,10 @@ private JPanel createNagTab() {
optionCargoCapacityNag.setToolTipText(resources.getString("optionCargoCapacityNag.toolTipText"));
optionCargoCapacityNag.setName("optionCargoCapacityNag");
+ optionInvalidFactionNag = new JCheckBox(resources.getString("optionInvalidFactionNag.text"));
+ optionInvalidFactionNag.setToolTipText(resources.getString("optionInvalidFactionNag.toolTipText"));
+ optionInvalidFactionNag.setName("optionInvalidFactionNag");
+
// Layout the UI
final JPanel panel = new JPanel();
panel.setName("nagPanel");
@@ -938,6 +943,7 @@ private JPanel createNagTab() {
.addComponent(optionUnresolvedStratConContactsNag)
.addComponent(optionOutstandingScenariosNag)
.addComponent(optionCargoCapacityNag)
+ .addComponent(optionInvalidFactionNag)
);
layout.setHorizontalGroup(
@@ -955,6 +961,7 @@ private JPanel createNagTab() {
.addComponent(optionUnresolvedStratConContactsNag)
.addComponent(optionOutstandingScenariosNag)
.addComponent(optionCargoCapacityNag)
+ .addComponent(optionInvalidFactionNag)
);
return panel;
@@ -1215,6 +1222,7 @@ protected void okAction() {
MekHQ.getMHQOptions().setNagDialogIgnore(MHQConstants.NAG_UNRESOLVED_STRATCON_CONTACTS, optionUnresolvedStratConContactsNag.isSelected());
MekHQ.getMHQOptions().setNagDialogIgnore(MHQConstants.NAG_OUTSTANDING_SCENARIOS, optionOutstandingScenariosNag.isSelected());
MekHQ.getMHQOptions().setNagDialogIgnore(MHQConstants.NAG_CARGO_CAPACITY, optionCargoCapacityNag.isSelected());
+ MekHQ.getMHQOptions().setNagDialogIgnore(MHQConstants.NAG_INVALID_FACTION, optionCargoCapacityNag.isSelected());
PreferenceManager.getClientPreferences().setUserDir(txtUserDir.getText());
PreferenceManager.getInstance().save();
@@ -1330,6 +1338,7 @@ private void setInitialState() {
optionUnresolvedStratConContactsNag.setSelected(MekHQ.getMHQOptions().getNagDialogIgnore(MHQConstants.NAG_UNRESOLVED_STRATCON_CONTACTS));
optionOutstandingScenariosNag.setSelected(MekHQ.getMHQOptions().getNagDialogIgnore(MHQConstants.NAG_OUTSTANDING_SCENARIOS));
optionCargoCapacityNag.setSelected(MekHQ.getMHQOptions().getNagDialogIgnore(MHQConstants.NAG_CARGO_CAPACITY));
+ optionInvalidFactionNag.setSelected(MekHQ.getMHQOptions().getNagDialogIgnore(MHQConstants.NAG_INVALID_FACTION));
txtUserDir.setText(PreferenceManager.getClientPreferences().getUserDir());
spnStartGameDelay.setValue(MekHQ.getMHQOptions().getStartGameDelay());
diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/InvalidFactionNagDialog.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/InvalidFactionNagDialog.java
new file mode 100644
index 0000000000..d3e501c089
--- /dev/null
+++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/InvalidFactionNagDialog.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2021 - 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.dialog.nagDialogs;
+
+import mekhq.MHQConstants;
+import mekhq.MekHQ;
+import mekhq.campaign.Campaign;
+import mekhq.campaign.universe.Faction;
+import mekhq.gui.baseComponents.AbstractMHQNagDialog;
+
+import javax.swing.*;
+import java.time.LocalDate;
+import java.util.Objects;
+
+public class InvalidFactionNagDialog extends AbstractMHQNagDialog {
+ private static boolean isFactionInvalid (Campaign campaign) {
+ Faction campaignFaction = campaign.getFaction();
+
+ if (!campaign.getFaction().validIn(campaign.getLocalDate())) {
+ return true;
+ }
+
+ // this is a special handler for FedSuns as they're the main culprit behind the issue of users having invalid factions.
+ // FS and LA campaigns won't trigger the above conditional, because those factions aren't technically ended when the FedSuns forms
+ // they just become dormant.
+ if (Objects.equals(campaignFaction.getShortName(), "LA")) {
+ // the dates picked are chosen as these are when mhq does the bulk of the faction ownership transfers
+ return ((campaign.getLocalDate().isAfter(LocalDate.of(3040, 1, 18)))
+ && (campaign.getLocalDate().isBefore(LocalDate.of(3067, 4, 20))));
+ }
+
+ // this is another special handler for FedSuns as they're the main culprit behind the issue of users having invalid factions.
+ if (Objects.equals(campaignFaction.getShortName(), "FS")) {
+ return ((campaign.getLocalDate().isAfter(LocalDate.of(3040, 1, 18)))
+ && (campaign.getLocalDate().isBefore(LocalDate.of(3057, 9, 18))));
+ }
+
+ return false;
+ }
+
+ //region Constructors
+ public InvalidFactionNagDialog(final JFrame frame, final Campaign campaign) {
+ super(frame, "InvalidFactionNagDialog", "InvalidFactionNagDialog.title",
+ "InvalidFactionNagDialog.text", campaign, MHQConstants.NAG_INVALID_FACTION);
+ }
+
+ //endregion Constructors
+ @Override
+ protected boolean checkNag() {
+ return !MekHQ.getMHQOptions().getNagDialogIgnore(getKey()) && (isFactionInvalid(getCampaign()));
+ }
+}
diff --git a/MekHQ/src/mekhq/gui/panes/CampaignOptionsPane.java b/MekHQ/src/mekhq/gui/panes/CampaignOptionsPane.java
index cfa9441365..31b42cf129 100644
--- a/MekHQ/src/mekhq/gui/panes/CampaignOptionsPane.java
+++ b/MekHQ/src/mekhq/gui/panes/CampaignOptionsPane.java
@@ -8491,7 +8491,7 @@ public void updateOptions() {
}
campaign.setLocalDate(date);
- if (campaign.getCampaignStartDate() == null) {
+ if ((campaign.getCampaignStartDate() == null) || (campaign.getCampaignStartDate().isAfter(campaign.getLocalDate()))) {
campaign.setCampaignStartDate(date);
}
// Ensure that the MegaMek year GameOption matches the campaign year
diff --git a/MekHQ/src/mekhq/gui/panes/UnitMarketPane.java b/MekHQ/src/mekhq/gui/panes/UnitMarketPane.java
index 1aba6005de..8ce7d9eb40 100644
--- a/MekHQ/src/mekhq/gui/panes/UnitMarketPane.java
+++ b/MekHQ/src/mekhq/gui/panes/UnitMarketPane.java
@@ -198,7 +198,7 @@ protected Component createLeftComponent() {
final JScrollPane marketTableScrollPane = createMarketTablePane();
- final JLabel lblBlackMarketWarning = new JLabel(resources.getString("lblBlackMarketWarning.text"));
+ final JLabel lblMarketDescriptions = new JLabel(resources.getString("lblMarketDescriptions.text"));
// Layout the UI
JPanel panel = new JPanel();
@@ -215,7 +215,7 @@ protected Component createLeftComponent() {
.addComponent(filtersPanel)
.addComponent(getEntityImagePanel(), Alignment.LEADING))
.addComponent(marketTableScrollPane)
- .addComponent(lblBlackMarketWarning)
+ .addComponent(lblMarketDescriptions)
);
layout.setHorizontalGroup(
@@ -224,7 +224,7 @@ protected Component createLeftComponent() {
.addComponent(filtersPanel)
.addComponent(getEntityImagePanel()))
.addComponent(marketTableScrollPane)
- .addComponent(lblBlackMarketWarning, Alignment.TRAILING)
+ .addComponent(lblMarketDescriptions, Alignment.TRAILING)
);
return panel;
}
diff --git a/MekHQ/src/mekhq/gui/view/PersonViewPanel.java b/MekHQ/src/mekhq/gui/view/PersonViewPanel.java
index fd592263da..ad1f0b6f62 100644
--- a/MekHQ/src/mekhq/gui/view/PersonViewPanel.java
+++ b/MekHQ/src/mekhq/gui/view/PersonViewPanel.java
@@ -1468,8 +1468,7 @@ private JPanel fillSkills() {
firsty++;
}
- if ((campaign.getCampaignOptions().isUseFatigue())
- && (person.getEffectiveFatigue(campaign) > 0)) {
+ if ((campaign.getCampaignOptions().isUseFatigue()) && (person.getFatigue() > 0)) {
lblFatigue1.setName("lblFatigue1");
lblFatigue1.setText(resourceMap.getString("lblFatigue1.text"));
gridBagConstraints = new GridBagConstraints();
@@ -1481,7 +1480,7 @@ private JPanel fillSkills() {
StringBuilder fatigueDisplay = new StringBuilder();
int effectiveFatigue = person.getEffectiveFatigue(campaign);
- int fatigueTurnoverModifier = MathUtility.clamp(((person.getFatigue() - 1) / 4) - 1, 0, 3);
+ int fatigueTurnoverModifier = MathUtility.clamp(((person.getEffectiveFatigue(campaign) - 1) / 4) - 1, 0, 3);
fatigueDisplay.append(person.getFatigue());