Skip to content

Commit

Permalink
Merge pull request #6244 from SuperStucco/Forcegen-chassis-availabili…
Browse files Browse the repository at this point in the history
…ty-update

Force generator math updates
  • Loading branch information
HammerGS authored Dec 15, 2024
2 parents ae8cbb8 + cae45e0 commit d1e72c6
Show file tree
Hide file tree
Showing 7 changed files with 964 additions and 320 deletions.
4 changes: 2 additions & 2 deletions megamek/data/forcegenerator/3058.xml
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,8 @@
</faction>
<faction key='CJF'>
<pctOmni>25,25,53,90,100</pctOmni>
<pctClan>53,53,80,46,100</pctClan>
<pctSL>47,47,20,54,0</pctSL>
<pctClan>53,53,80,96,100</pctClan>
<pctSL>47,47,20,4,0</pctSL>
<pctClan unitType='Vehicle'>0,0,20,20,20</pctClan>
<pctSL unitType='Vehicle'>100,100,80,80,80</pctSL>
<salvage pct='17'>CHH:0,CSR:0,CIH:0,CI:0,CSV:2,CFM:0,CCO:0,CGS:0,CSA:0,CDS:0,CW:6,LA:5,CNC:0,CSJ:0,CGB:0</salvage>
Expand Down
4 changes: 2 additions & 2 deletions megamek/data/forcegenerator/3060.xml
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,8 @@
</faction>
<faction key='CJF'>
<pctOmni>25,25,55,90,100</pctOmni>
<pctClan>55,55,80,10,100</pctClan>
<pctSL>45,45,20,90,0</pctSL>
<pctClan>55,55,80,90,100</pctClan>
<pctSL>45,45,20,10,0</pctSL>
<pctClan unitType='Vehicle'>0,0,20,20,20</pctClan>
<pctSL unitType='Vehicle'>100,100,80,80,80</pctSL>
<salvage pct='15'>CW:10,LA:2,CSV:4</salvage>
Expand Down
23 changes: 15 additions & 8 deletions megamek/src/megamek/client/ratgenerator/AbstractUnitRecord.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,23 +40,30 @@ public AbstractUnitRecord(String chassis) {
}

/**
* Adjusts availability rating for the first couple years after introduction.
* Adjusts availability rating for +/- dynamic. Also reduces availability by
* introduction year, with 1 year before heavily reduced for pre-production
* prototypes and first year slightly reduced for working out initial
* production.
*
* @param ar The AvailabilityRecord for the chassis or model.
* @param rating The force equipment rating.
* @param avRating The AvailabilityRecord for the chassis or model.
* @param equipRating The force equipment rating.
* @param ratingLevels The number of equipment rating levels used by the faction.
* @param year The game year
* @param year The game year
* @return The adjusted availability rating.
*/
public int calcAvailability(AvailabilityRating ar, int rating, int ratingLevels, int year) {
int retVal = ar.adjustForRating(rating, ratingLevels);
public int calcAvailability(AvailabilityRating avRating, int equipRating, int ratingLevels, int year) {
int retVal = avRating.adjustForRating(equipRating, ratingLevels);

if (introYear == year) {
// Pre-production prototypes are heavily reduced
if (year == introYear - 1) {
retVal -= 2;
}
if (introYear == year + 1) {

// Initial production year is slightly reduced
if (year == introYear) {
retVal -= 1;
}

return Math.max(retVal, 0);
}

Expand Down
23 changes: 18 additions & 5 deletions megamek/src/megamek/client/ratgenerator/AvailabilityRating.java
Original file line number Diff line number Diff line change
Expand Up @@ -123,13 +123,26 @@ public int getAvailability() {
return availability;
}

public int adjustForRating(int rating, int numLevels) {
if (rating < 0 || ratingAdjustment == 0) {
/**
* Adjust availability rating for the dynamic +/- value, which is based on
* equipment quality rating. The (+) will reduce availability for commands
* with a lower rating, while the (-) will reduce availability for commands
* with a higher rating.
* @param equipRating zero-based index based on {@code numLevels} of rating to check
* @param numLevels number of equipment levels available, typically 5 (A/B/C/D/F)
* @return integer, may be negative
*/
public int adjustForRating(int equipRating, int numLevels) {
if (ratingAdjustment == 0 || equipRating < 0) {
return availability;
} else if (ratingAdjustment < 0) {
return availability - rating;
}

if (ratingAdjustment > 0) {
// (+) adjustment, reduce availability as equipment rating decreases
return availability - (numLevels - 1 - equipRating);
} else {
return availability - (numLevels - rating);
// (-) adjustment, reduce availability as equipment rating increases
return availability - equipRating;
}
}

Expand Down
170 changes: 157 additions & 13 deletions megamek/src/megamek/client/ratgenerator/ChassisRecord.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,9 @@
*/
package megamek.client.ratgenerator;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.*;

import megamek.common.EntityMovementMode;
import megamek.logging.MMLogger;

/**
Expand Down Expand Up @@ -55,7 +53,153 @@ public List<ModelRecord> getSortedModels() {

}

public int totalModelWeight(int era, String fKey) {
/**
* Generate a list of models for this chassis based on certain criteria.
* Early prototypes may be available one year before official introduction.
* @param exactYear game year
* @param validWeightClasses restrict weight class to one or more classes
* @param movementModes movement mode types, may be null or empty
* @param networkMask specific C3 network equipment
* @return set of models which pass the filter requirements, may be empty
*/
public HashSet<ModelRecord> getFilteredModels(int exactYear,
Collection<Integer> validWeightClasses,
Collection<EntityMovementMode> movementModes,
int networkMask) {

HashSet<ModelRecord> filteredModels = new HashSet<>();

for (ModelRecord curModel : models) {
// Introduction date should be at most 1 year away for pre-production prototypes
if (curModel.introYear > exactYear + 1) {
continue;
}

// Weight class check
if (validWeightClasses != null && !validWeightClasses.isEmpty() && !validWeightClasses.contains(curModel.getWeightClass())) {
continue;
}

// Movement mode check
if (movementModes != null && !movementModes.isEmpty()) {
if (!movementModes.contains(curModel.getMovementMode())) {
continue;
}
}

// C3 network equipment check
if ((networkMask & curModel.getNetworkMask()) != networkMask) {
continue;
}

filteredModels.add(curModel);
}

return filteredModels;
}

/**
* Total the weights of all models for this chassis, including modifiers for
* +/- dynamic adjustment, intro year adjustment, interpolation, and role
* modifications.
* @param validModels models to add up
* @param currentEra year for current era
* @param exactYear current year in game
* @param nextEra start date of next era after the current one
* @param fRec faction data
* @param roles roles selected for generation, may be null or empty
* @param roleStrictness positive number, higher applies heavier role adjustments
* @param equipRating equipment rating to generate for
* @param numRatingLevels how many rating levels are present
* @return sum of calculated weights of all models of this chassis
*/
public double totalModelWeight(HashSet<ModelRecord> validModels,
int currentEra,
int exactYear,
int nextEra,
FactionRecord fRec,
Collection<MissionRole> roles,
int roleStrictness,
int equipRating,
int numRatingLevels,
HashMap<String,Double> weightData) {

RATGenerator ratGen = RATGenerator.getInstance();
AvailabilityRating avRating, nextAvRating;
double retVal = 0;
double adjRating;
double nextRating;
Number roleRating;

// Clear any pre-existing weighting data - this should only cover
// the current set of models
weightData.clear();

// For each model
for (ModelRecord curModel : validModels) {

if (curModel.factionIsExcluded(fRec)) {
continue;
}

// Get the availability rating for the provided faction and year,
// skip processing if not available
avRating = ratGen.findModelAvailabilityRecord(currentEra, curModel.getKey(), fRec);
if (avRating == null || avRating.getAvailability() <= 0) {
continue;
}

// If required, interpolate availability between era start or intro date
// (whichever is later), and start of next era
if (exactYear > currentEra && currentEra != nextEra) {
nextAvRating = ratGen.findModelAvailabilityRecord(nextEra,
curModel.getKey(), fRec);

int interpolationStart = Math.max(currentEra, Math.min(exactYear, curModel.introYear));

adjRating = curModel.calcAvailability(avRating,
equipRating, numRatingLevels, interpolationStart);

nextRating = 0.0;
if (nextAvRating != null) {
nextRating = curModel.calcAvailability(nextAvRating,
equipRating, numRatingLevels, nextEra);
}

if (adjRating != nextRating) {
adjRating = adjRating +
(nextRating - adjRating) * (exactYear - interpolationStart) / (nextEra - interpolationStart);
}

} else {
// Adjust availability for +/- dynamic and intro year
adjRating = curModel.calcAvailability(avRating, equipRating, numRatingLevels, exactYear);
}

if (adjRating <= 0) {
continue;
}

// Adjust availability for roles. Method may return null as a filtering mechanism.
roleRating = MissionRole.adjustAvailabilityByRole(adjRating,
roles, curModel, exactYear, roleStrictness);

if (roleRating == null || roleRating.doubleValue() <= 0) {
continue;
}

// Calculate the weight and add it to the total
adjRating = AvailabilityRating.calcWeight(roleRating.doubleValue());
retVal += adjRating;

// Cache the final availability rating
weightData.put(curModel.getKey(), adjRating);
}

return retVal;
}

public double totalModelWeight(int era, String fKey) {
FactionRecord fRec = RATGenerator.getInstance().getFaction(fKey);
if (fRec == null) {
logger.warn("Attempt to find totalModelWeight for non-existent faction " + fKey);
Expand All @@ -64,15 +208,15 @@ public int totalModelWeight(int era, String fKey) {
return totalModelWeight(era, fRec);
}

public int totalModelWeight(int era, FactionRecord fRec) {
int retVal = 0;
RATGenerator rg = RATGenerator.getInstance();
public double totalModelWeight(int era, FactionRecord fRec) {
double retVal = 0;
RATGenerator ratGen = RATGenerator.getInstance();

for (ModelRecord mr : models) {
AvailabilityRating ar = rg.findModelAvailabilityRecord(era,
mr.getKey(), fRec);
if (ar != null) {
retVal += AvailabilityRating.calcWeight(ar.getAvailability());
for (ModelRecord curModel : models) {
AvailabilityRating avRating = ratGen.findModelAvailabilityRecord(era,
curModel.getKey(), fRec);
if (avRating != null) {
retVal += AvailabilityRating.calcWeight(avRating.getAvailability());
}
}

Expand Down
Loading

0 comments on commit d1e72c6

Please sign in to comment.