Skip to content

Commit

Permalink
Merge branch 'master' into acs-autoresolve
Browse files Browse the repository at this point in the history
  • Loading branch information
Scoppio authored Dec 3, 2024
2 parents 386ac31 + b0dc5e1 commit ac6deb4
Show file tree
Hide file tree
Showing 17 changed files with 278 additions and 103 deletions.
2 changes: 1 addition & 1 deletion MekHQ/data/forcegenerator/3075.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5498,7 +5498,7 @@
</model>
</chassis>
<chassis name='Enforcer' unitType='Mek'>
<availability>CC:1,FS:7,MERC:4,CDP:2,Periphery:2,FVC:7LA:2,DC:1</availability>
<availability>CC:1,FS:7,MERC:4,CDP:2,Periphery:2,FVC:7,LA:2,DC:1</availability>
<model name='ENF-4R'>
<availability>FVC:4-,General:2-,FS:4-,TC:4-</availability>
</model>
Expand Down
6 changes: 6 additions & 0 deletions MekHQ/docs/history.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ MEKHQ VERSION HISTORY:
+ Fix #4600: Add Persistent Initiative Bonus to campaign
+ Fix #5169: Added temp Astechs and Medics to Personnel Report; Support Personnel Salaries now match the Finances tab.
+ PR #5250: Renamed 'Lances' to 'Strategic Formations', Expanded Functionality
+ Fix #4600: Added initiativeMaxBonus to Campaign
+ PR #5263: Refactored Strategic Formation Weight Categories
+ Fix #5262: Refactored Scenario Force Building Logic.
+ PR #5266: Refactored Morale Calculations and Logging
+ PR #5268: Refactored Strategic Formations
+ Fix #5149: Better error message for null values in contract fields

0.50.01 (2024-11-10 1800 UTC)
+ PR #4810: Respecting Trademarks (Mech to Mek)
Expand Down
118 changes: 82 additions & 36 deletions MekHQ/src/mekhq/campaign/Campaign.java
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@
import java.util.Map.Entry;
import java.util.stream.Collectors;

import static mekhq.campaign.force.StrategicFormation.recalculateStrategicFormations;
import static mekhq.campaign.market.contractMarket.ContractAutomation.performAutomatedActivation;
import static mekhq.campaign.personnel.backgrounds.BackgroundsController.randomMercenaryCompanyNameGenerator;
import static mekhq.campaign.personnel.education.EducationController.getAcademy;
Expand Down Expand Up @@ -201,7 +202,7 @@ public class Campaign implements ITechManager {

// hierarchically structured Force object to define TO&E
private Force forces;
private final Hashtable<Integer, StrategicFormation> strategicFormations; // AtB
private Hashtable<Integer, StrategicFormation> strategicFormations; // AtB

private Faction faction;
private int techFactionCode;
Expand Down Expand Up @@ -272,7 +273,7 @@ public class Campaign implements ITechManager {
private final CampaignSummary campaignSummary;
private final Quartermaster quartermaster;
private StoryArc storyArc;
private FameAndInfamyController fameAndInfamy;
private final FameAndInfamyController fameAndInfamy;
private BehaviorSettings autoResolveBehaviorSettings;
private List<Unit> automatedMothballUnits;

Expand Down Expand Up @@ -456,15 +457,74 @@ public List<Force> getAllForces() {
return new ArrayList<>(forceIds.values());
}

public void importLance(StrategicFormation l) {
strategicFormations.put(l.getForceId(), l);
/**
* Adds a {@link StrategicFormation} to the {@code strategicFormations} {@link Hashtable} using
* {@code forceId} as the key.
*
* @param strategicFormation the {@link StrategicFormation} to be added to the {@link Hashtable}
*/
public void addStrategicFormation(StrategicFormation strategicFormation) {
strategicFormations.put(strategicFormation.getForceId(), strategicFormation);
}

/**
* Removes a {@link StrategicFormation} from the {@code strategicFormations} {@link Hashtable}
* using {@code forceId} as they key.
*
* @param forceId the key of the {@link StrategicFormation} to be removed from the {@link Hashtable}
*/
public void removeStrategicFormation(final int forceId) {
this.strategicFormations.remove(forceId);
}

public Hashtable<Integer, StrategicFormation> getStrategicFormations() {
/**
* Returns the {@link Hashtable} containing all the {@link StrategicFormation} objects after
* removing the ineligible ones. Although sanitization might not be necessary, it ensures that
* there is no need for {@code isEligible()} checks when fetching the {@link Hashtable}.
*
* @return the sanitized {@link Hashtable} of {@link StrategicFormation} objects stored in the
* current campaign.
*/
public Hashtable<Integer, StrategicFormation> getStrategicFormationsTable() {
// Here we sanitize the list, ensuring ineligible formations have been removed before
// returning the hashtable. In theory, this shouldn't be necessary, however, having this
// sanitizing step should remove the need for isEligible() checks whenever we fetch the
// hashtable.
List<Integer> formationsToSanitize = new ArrayList<>();
for (StrategicFormation strategicFormation : strategicFormations.values()) {
if (!strategicFormation.isEligible(this)) {
formationsToSanitize.add(strategicFormation.getForceId());
try {
Force force = getForce(strategicFormation.getForceId());
force.setStrategicFormation(false);
} catch (Exception ex) {
// We're not too worried if we can't find the associated Force,
// as this just means it has been deleted at some point and not removed correctly.
}
}
}

for (int id : formationsToSanitize) {
strategicFormations.remove(id);
}

return strategicFormations;
}

public ArrayList<StrategicFormation> getStrategicFormationList() {
/**
* Returns an {@link ArrayList} of all {@link StrategicFormation} objects in the
* {@code strategicFormations} {@link Hashtable}.
* Calls the {@code getStrategicFormationsTable()} method to sanitize the {@link Hashtable}
* before conversion to {@link ArrayList}.
*
* @return an {@link ArrayList} of all the {@link StrategicFormation} objects in the
* {@code strategicFormations} {@link Hashtable}
*/
public ArrayList<StrategicFormation> getAllStrategicFormations() {
// This call allows us to utilize the self-sanitizing feature of getStrategicFormationsTable(),
// without needing to directly include the code here, too.
strategicFormations = getStrategicFormationsTable();

return strategicFormations.values().stream()
.filter(l -> forceIds.containsKey(l.getForceId()))
.collect(Collectors.toCollection(ArrayList::new));
Expand Down Expand Up @@ -911,13 +971,11 @@ public void addForce(Force force, Force superForce) {
forceIds.put(id, force);
lastForceId = id;

if (campaignOptions.isUseAtB() && !force.getUnits().isEmpty()) {
if (null == strategicFormations.get(id)) {
strategicFormations.put(id, new StrategicFormation(force.getId(), this));
}
}

force.updateCommander(this);

if (campaignOptions.isUseAtB()) {
recalculateStrategicFormations(this);
}
}

public void moveForce(Force force, Force superForce) {
Expand Down Expand Up @@ -1010,25 +1068,18 @@ public void addUnitToForce(@Nullable Unit u, int id) {
}

if (campaignOptions.isUseAtB()) {
if ((null != prevForce) && prevForce.getUnits().isEmpty()) {
strategicFormations.remove(prevForce.getId());
}

if ((null == strategicFormations.get(id)) && (null != force)) {
strategicFormations.put(id, new StrategicFormation(force.getId(), this));
}
recalculateStrategicFormations(this);
}
}

/**
* Adds force and all its subforces to the AtB lance table
*/
private void addAllLances(Force force) {
if (force.isStrategicFormation()) {
strategicFormations.put(force.getId(), new StrategicFormation(force.getId(), this));
}
for (Force f : force.getSubForces()) {
addAllLances(f);
private void addAllStrategicFormations(Force force) {
recalculateStrategicFormations(this);

for (Force subForce : force.getSubForces()) {
addAllStrategicFormations(subForce);
}
}

Expand Down Expand Up @@ -3794,7 +3845,7 @@ private void processNewDayATB() {
processShipSearch();

// Training Experience - Award to eligible training Strategic Formations on active contracts
getStrategicFormations().values().stream()
getStrategicFormationsTable().values().stream()
.filter(strategicFormation -> strategicFormation.getRole().isTraining()
&& (strategicFormation.getContract(this) != null) && strategicFormation.isEligible(this)
&& strategicFormation.getContract(this).isActiveOn(getLocalDate(), true))
Expand Down Expand Up @@ -4320,6 +4371,7 @@ public void processNewDayUnits() {
private void processNewDayForces() {
// update formation levels
Force.populateFormationLevelsFromOrigin(this);
recalculateStrategicFormations(this);

// Update the force icons based on the end-of-day unit status if desired
if (MekHQ.getMHQOptions().getNewDayForceIconOperationalStatus()) {
Expand Down Expand Up @@ -5014,17 +5066,13 @@ public void removeForce(Force force) {
}
}
}
MekHQ.triggerEvent(new OrganizationChangedEvent(this, force));

// also remove this force's id from any scenarios
if (force.isDeployed()) {
Scenario s = getScenario(force.getScenarioId());
s.removeForce(fid);
}

if (campaignOptions.isUseAtB()) {
strategicFormations.remove(fid);
}

if (null != force.getParentForce()) {
force.getParentForce().removeSubForce(fid);
}
Expand All @@ -5038,10 +5086,8 @@ public void removeForce(Force force) {
}
}

ArrayList<Force> subs = new ArrayList<>(force.getSubForces());
for (Force sub : subs) {
removeForce(sub);
MekHQ.triggerEvent(new OrganizationChangedEvent(this, sub));
if (campaignOptions.isUseAtB()) {
recalculateStrategicFormations(this);
}
}

Expand Down Expand Up @@ -8330,7 +8376,7 @@ public void initAtB(boolean newCampaign) {
}
}

addAllLances(this.forces);
addAllStrategicFormations(this.forces);

// Determine whether or not there is an active contract
setHasActiveContract();
Expand Down
20 changes: 9 additions & 11 deletions MekHQ/src/mekhq/campaign/force/Force.java
Original file line number Diff line number Diff line change
Expand Up @@ -389,15 +389,17 @@ public Vector<UUID> getUnits() {
*/
public Vector<UUID> getAllUnits(boolean combatForcesOnly) {
Vector<UUID> allUnits;

if (combatForcesOnly && !isCombatForce()) {
allUnits = new Vector<>();
} else {
allUnits = new Vector<>(units);
}

for (Force f : subForces) {
allUnits.addAll(f.getAllUnits(combatForcesOnly));
for (Force force : subForces) {
allUnits.addAll(force.getAllUnits(combatForcesOnly));
}

return allUnits;
}

Expand Down Expand Up @@ -841,11 +843,7 @@ public int hashCode() {
public int getTotalBV(Campaign campaign, boolean forceStandardBattleValue) {
int bvTotal = 0;

for (Force subforce : getSubForces()) {
bvTotal += subforce.getTotalBV(campaign, forceStandardBattleValue);
}

for (UUID unitId : getUnits()) {
for (UUID unitId : getAllUnits(false)) {
// no idea how this would happen, but sometimes a unit in a forces unit ID list
// has an invalid ID?
if (campaign.getUnit(unitId) == null) {
Expand Down Expand Up @@ -904,16 +902,16 @@ public int getTotalUnitCount(Campaign campaign, boolean isClanBidding) {
* Calculates the unit type most represented in this force
* and all subforces.
*
* @param c Working campaign
* @param campaign Working campaign
* @return Majority unit type.
*/
public int getPrimaryUnitType(Campaign c) {
public int getPrimaryUnitType(Campaign campaign) {
Map<Integer, Integer> unitTypeBuckets = new TreeMap<>();
int biggestBucketID = -1;
int biggestBucketCount = 0;

for (UUID id : getUnits()) {
int unitType = c.getUnit(id).getEntity().getUnitType();
for (UUID id : getAllUnits(false)) {
int unitType = campaign.getUnit(id).getEntity().getUnitType();

unitTypeBuckets.merge(unitType, 1, Integer::sum);

Expand Down
Loading

0 comments on commit ac6deb4

Please sign in to comment.