/*
* DefaultMrbcRating.java
*
* Copyright (c) 2009 Jay Lawson <jaylawson39 at yahoo.com>. 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 <http://www.gnu.org/licenses/>.
*/
package mekhq.campaign.rating;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import megamek.common.Crew;
import megamek.common.Entity;
import megamek.common.FixedWingSupport;
import megamek.common.Infantry;
import megamek.common.Jumpship;
import megamek.common.SmallCraft;
import megamek.common.UnitType;
import mekhq.campaign.Campaign;
import mekhq.campaign.personnel.Person;
import mekhq.campaign.personnel.SkillType;
import mekhq.campaign.unit.Unit;
/**
* @author Deric Page (deric (dot) page (at) usa.net)
* @version %Id%
* @since 3/12/2012
*/
class CampaignOpsReputation extends AbstractUnitRating {
private int nonAdminPersonnelCount = 0;
// Tech Support & Admins.
private int mechTechTeamsNeeded = 0;
private int mechanicTeamsNeeded = 0;
private int battleArmorTechTeamsNeeded = 0;
private int aeroTechTeamsNeeded = 0;
private int adminsNeeded = 0;
private int totalTechTeams = 0;
private int mechTechTeams = 0;
private int aeroTechTeams = 0;
private int mechanicTeams = 0;
private int baTechTeams = 0;
private int generalTechTeams = 0;
private final List<String> craftWithoutCrew = new ArrayList<>();
private int technicians = 0;
CampaignOpsReputation(Campaign campaign) {
super(campaign);
}
int getNonAdminPersonnelCount() {
return nonAdminPersonnelCount;
}
int getAdminsNeeded() {
return adminsNeeded;
}
int getVeeCount() {
return getLightVeeCount() + getHeavyVeeCount();
}
int getMechTechTeamsNeeded() {
return mechTechTeamsNeeded;
}
int getMechanicTeamsNeeded() {
return mechanicTeamsNeeded;
}
int getBattleArmorTechTeamsNeeded() {
return battleArmorTechTeamsNeeded;
}
int getAeroTechTeamsNeeded() {
return aeroTechTeamsNeeded;
}
private void countUnits() {
// Reset counts.
setTotalSkillLevels(BigDecimal.ZERO);
List<Unit> unitList = getCampaign().getCopyOfUnits();
for (Unit u : unitList) {
if (null == u) {
continue;
}
if (u.isMothballed()) {
continue;
}
updateUnitCounts(u);
Person p = u.getCommander();
if (p != null) {
getCommanderList().add(p);
}
Entity entity = u.getEntity();
updateBayCount(entity);
int unitType = UnitType.determineUnitTypeCode(entity);
if (UnitType.INFANTRY == unitType || UnitType.BATTLE_ARMOR == unitType) {
updateTotalSkill((Infantry) entity);
} else {
updateTotalSkill(u.getEntity().getCrew(), UnitType.determineUnitTypeCode(entity));
}
// todo: Add Mobile Structure when Megamek supports it.
switch (unitType) {
case UnitType.SPACE_STATION:
case UnitType.NAVAL:
case UnitType.DROPSHIP:
if (u.getFullCrewSize() < u.getActiveCrew().size()) {
addCraftWithoutCrew(u);
}
break;
case UnitType.WARSHIP:
case UnitType.JUMPSHIP:
updateDockingCollarCount((Jumpship) entity);
if (u.getFullCrewSize() < u.getActiveCrew().size()) {
addCraftWithoutCrew(u);
}
break;
}
if (entity instanceof FixedWingSupport) { // UnitType doesn't include FixedWingSupport.
if (u.getFullCrewSize() < u.getActiveCrew().size()) {
addCraftWithoutCrew(u);
}
}
}
}
private void updateTotalSkill(Infantry infantry) {
Crew crew = infantry.getCrew();
if (null == crew) {
return;
}
int gunnery = crew.getGunnery();
int antiMek = infantry.getAntiMekSkill();
if (antiMek == 0) {
antiMek = gunnery + 1;
}
BigDecimal skillLevel = BigDecimal.valueOf(gunnery).add(BigDecimal.valueOf(antiMek));
incrementSkillRatingCounts(getExperienceLevelName(skillLevel));
setTotalSkillLevels(getTotalSkillLevels(false).add(skillLevel));
}
private void updateTotalSkill(Crew crew, int unitType) {
// Make sure we have a crew.
if (crew == null) {
return;
}
boolean hasPilot = false;
int gunnery;
int piloting = 0;
switch (unitType) {
case UnitType.MEK:
case UnitType.WARSHIP:
case UnitType.SMALL_CRAFT:
case UnitType.DROPSHIP:
case UnitType.CONV_FIGHTER:
case UnitType.AERO:
case UnitType.VTOL:
case UnitType.TANK:
gunnery = crew.getGunnery();
piloting = crew.getPiloting();
hasPilot = true;
break;
case UnitType.PROTOMEK:
gunnery = crew.getGunnery();
break;
default:
return;
}
BigDecimal skillLevel = BigDecimal.valueOf(gunnery);
if (hasPilot) {
skillLevel = skillLevel.add(BigDecimal.valueOf(piloting));
} else {
// Assume a piloting equal to Gunnery +1.
skillLevel = skillLevel.add(BigDecimal.valueOf(gunnery)).add(BigDecimal.ONE);
}
incrementSkillRatingCounts(getExperienceLevelName(skillLevel));
setTotalSkillLevels(getTotalSkillLevels(false).add(skillLevel));
}
@Override
protected BigDecimal getNumberUnits() {
return new BigDecimal(getTotalCombatUnits());
}
private int getTotalCombatUnits() {
int totalCombatUnits = getMechCount();
totalCombatUnits += getFighterCount();
totalCombatUnits += getProtoCount();
totalCombatUnits += getVeeCount();
totalCombatUnits += (getBattleArmorCount() / 5);
totalCombatUnits += (getInfantryCount() / 28);
totalCombatUnits += getDropshipCount();
totalCombatUnits += getSmallCraftCount();
return totalCombatUnits;
}
@Override
protected BigDecimal calcAverageExperience() {
int totalCombatUnits = getTotalCombatUnits();
if (totalCombatUnits == 0) {
return BigDecimal.ZERO;
}
return getTotalSkillLevels().divide(BigDecimal.valueOf(totalCombatUnits), 2, BigDecimal.ROUND_HALF_UP);
}
private void calcNeededTechs() {
int protoTeamCount = BigDecimal.valueOf(getProtoCount())
.divide(BigDecimal.valueOf(5), 0, RoundingMode.HALF_UP)
.intValue();
setMechTechTeamsNeeded(getMechCount() + protoTeamCount);
setAeroTechTeamsNeeded(getFighterCount() + getSmallCraftCount());
int infantryTeamCount = BigDecimal.valueOf(getInfantryCount())
.divide(BigDecimal.valueOf(112), 0, RoundingMode.HALF_UP)
.intValue();
setMechanicTeamsNeeded(getSuperHeavyVeeCount() + getVeeCount() + infantryTeamCount);
setBattleArmorTechTeamsNeeded(BigDecimal.valueOf(getBattleArmorCount())
.divide(BigDecimal.valueOf(5), 0, RoundingMode.HALF_UP)
.intValue());
}
private void updatePersonnelCounts() {
setNonAdminPersonnelCount(0);
List<Person> personnelList = new ArrayList<>(getCampaign().getPersonnel());
for (Person p : personnelList) {
if (p.isAdmin() || p.isDoctor()) {
continue;
}
if (p.isTech()) {
technicians++;
}
setNonAdminPersonnelCount(getNonAdminPersonnelCount() + 1);
}
setNonAdminPersonnelCount(getNonAdminPersonnelCount() + getCampaign().getAstechPool());
}
private void calcNeededAdmins() {
setAdminsNeeded(BigDecimal.valueOf(getNonAdminPersonnelCount()).divide(BigDecimal.TEN,
0,
RoundingMode.UP).intValue());
}
// todo Combat Personnel (up to 1/4 total) may be assigned double-duty and count as 1/3 of a tech.
// todo Combat Personnel (up to 1/4 total) may be assigned double-duty and count as 1/3 of an admin.
// todo Distinguish between Merc and Government personnel (1/2 admin needs for gov).
@Override
protected void initValues() {
super.initValues();
setMechCount(0);
setProtoCount(0);
setLightVeeCount(0);
setBattleArmorCount(0);
setInfantryCount(0);
setFighterCount(0);
setDropshipCount(0);
setSmallCraftCount(0);
setJumpshipCount(0);
setDockingCollarCount(0);
clearCraftWithoutCrew();
countUnits();
calcNeededTechs();
updatePersonnelCounts();
calcNeededAdmins();
}
@Override
protected int calculateUnitRatingScore() {
int totalScore = getExperienceValue();
totalScore += getCommanderValue();
totalScore += getCombatRecordValue();
totalScore += getTransportValue();
totalScore += getSupportValue();
totalScore += getFinancialValue();
totalScore += getCrimesPenalty();
totalScore += getIdleTimeModifier();
return totalScore;
}
@Override
public String getAverageExperience() {
if (getNumberUnits().compareTo(BigDecimal.ZERO) == 0) {
return SkillType.getExperienceLevelName(-1);
}
switch (getExperienceValue()) {
case 0:
return SkillType.getExperienceLevelName(SkillType.EXP_GREEN);
case 5:
return SkillType.getExperienceLevelName(SkillType.EXP_REGULAR);
case 10:
return SkillType.getExperienceLevelName(SkillType.EXP_VETERAN);
case 20:
return SkillType.getExperienceLevelName(SkillType.EXP_ELITE);
default:
return SkillType.getExperienceLevelName(-1);
}
}
@Override
protected String getExperienceLevelName(BigDecimal experience) {
if (getNumberUnits().compareTo(BigDecimal.ZERO) == 0) {
return SkillType.getExperienceLevelName(-1);
}
final BigDecimal eliteThreshold = new BigDecimal("4.99");
final BigDecimal vetThreshold = new BigDecimal("8.01");
final BigDecimal regThreshold = new BigDecimal("10.99");
if (experience.compareTo(regThreshold) > 0) {
return SkillType.getExperienceLevelName(SkillType.EXP_GREEN);
} else if (experience.compareTo(vetThreshold) > 0) {
return SkillType.getExperienceLevelName(SkillType.EXP_REGULAR);
} else if (experience.compareTo(eliteThreshold) > 0) {
return SkillType.getExperienceLevelName(SkillType.EXP_VETERAN);
}
return SkillType.getExperienceLevelName(SkillType.EXP_ELITE);
}
public int getExperienceValue() {
if (getNumberUnits().compareTo(BigDecimal.ZERO) == 0) {
return 0;
}
BigDecimal averageExp = calcAverageExperience();
String level = getExperienceLevelName(averageExp);
if (SkillType.getExperienceLevelName(-1).equalsIgnoreCase(level)) {
return 0;
} else if (SkillType.getExperienceLevelName(SkillType.EXP_GREEN).equalsIgnoreCase(level)) {
return 5;
} else if (SkillType.getExperienceLevelName(SkillType.EXP_REGULAR).equalsIgnoreCase(level)) {
return 10;
} else if (SkillType.getExperienceLevelName(SkillType.EXP_VETERAN).equalsIgnoreCase(level)) {
return 20;
}
return 40;
}
@Override
public int getCommanderValue() {
Person commander = getCommander();
if (commander == null) {
return 0;
}
int skillTotal = getCommanderSkill(SkillType.S_LEADER);
skillTotal += getCommanderSkill(SkillType.S_TACTICS);
skillTotal += getCommanderSkill(SkillType.S_STRATEGY);
skillTotal += getCommanderSkill(SkillType.S_NEG);
// ToDo AToW Traits.
// ToDo MHQ would need to support: Combat Sense, Connections, Reputation, Wealth, High CHA, Combat Paralysis
// ToDo Unlucky & Low CHA.
int commanderValue = skillTotal; // ToDo + positiveTraits - negativeTraits.
return commanderValue > 0 ? commanderValue : 1;
}
@Override
public String getUnitRating() {
// Campaign Operations does not use letter-grades.
return getModifier() + " (" + calculateUnitRatingScore() + ")";
}
@Override
public int getUnitRating(int score) {
// Campaign Operations does not use letter-grades.
return 0;
}
@Override
public String getUnitRatingName(int rating) {
// Campaign Operations does not use letter-grades.
return "";
}
@Override
public int getTransportValue() {
if (getTotalCombatUnits() == 0) {
return 0;
}
int totalValue = 0;
// todo Superheavys.
// Find out how short of transport bays we are.
boolean doubleCapacity = true;
boolean fullCapacity = true;
int heavyVeeBays = getHeavyVeeBayCount();
if (getMechBayCount() < getMechCount()) {
fullCapacity = false;
doubleCapacity = false;
} else if (getMechBayCount() < getMechCount() * 2) {
doubleCapacity = false;
}
if (getProtoBayCount() < getProtoCount()) {
fullCapacity = false;
doubleCapacity = false;
} else if (getProtoBayCount() < getProtoCount() * 2) {
doubleCapacity = false;
}
if (getHeavyVeeBayCount() < getHeavyVeeCount()) {
fullCapacity = false;
doubleCapacity = false;
} else if (getHeavyVeeBayCount() < getHeavyVeeCount() * 2) {
doubleCapacity = false;
}
heavyVeeBays -= getHeavyVeeBayCount();
int lightVeeBays = getLightVeeBayCount() + heavyVeeBays;
if (lightVeeBays < getLightVeeCount()) {
fullCapacity = false;
doubleCapacity = false;
} else if (lightVeeBays < getLightVeeCount() * 2) {
doubleCapacity = false;
}
if (getFighterBayCount() < getFighterCount()) {
fullCapacity = false;
doubleCapacity = false;
} else if (getFighterBayCount() < getFighterCount() * 2) {
doubleCapacity = false;
}
if ((getBaBayCount()) < getBattleArmorCount() / 5) {
fullCapacity = false;
doubleCapacity = false;
} else if ((getBaBayCount() * 2) < 2 * getBattleArmorCount() / 5) {
doubleCapacity = false;
}
if (getInfantryBayCount() < getInfantryCount() / 28) {
fullCapacity = false;
doubleCapacity = false;
} else if (getInfantryBayCount() < getInfantryCount() / 14) {
doubleCapacity = false;
}
if (getSmallCraftBayCount() < getSmallCraftCount()) {
fullCapacity = false;
doubleCapacity = false;
} else if (getSmallCraftBayCount() < (getSmallCraftCount() * 2)) {
doubleCapacity = false;
}
//Find the percentage of units that are transported.
if (doubleCapacity) {
totalValue += 10;
} else if (fullCapacity) {
totalValue += 5;
} else {
totalValue -= 5;
}
if (getDropshipCount() < 1) {
totalValue -= 5;
}
// ToDo Calculate transport needs and capacity for support personnel.
// According to Campaign Ops, this will require tracking bay personnel & passenger quarters.
if (getJumpshipCount() > 0) {
totalValue += 10;
}
if (getWarshipCount() > 0) {
totalValue += 10;
Calendar cal = Calendar.getInstance();
cal.set(Calendar.DAY_OF_YEAR, 1);
cal.set(Calendar.MONTH, 1);
cal.set(Calendar.YEAR, 2800);
cal.set(Calendar.HOUR, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
if (getCampaign().getDate().after(cal.getTime())) {
totalValue += 5;
}
}
if ((getDropshipCount() > 0) && (getDockingCollarCount() >= getDropshipCount())) {
totalValue += 5;
}
return totalValue;
}
int calcTechSupportValue() {
int totalValue = 0;
setTotalTechTeams(0);
int astechTeams;
setMechTechTeams(0);
setAeroTechTeams(0);
setMechanicTeams(0);
setBaTechTeams(0);
setGeneralTechTeams(0);
// How many astech teams do we have?
astechTeams = getCampaign().getNumberAstechs() / 6;
for (Person tech : getCampaign().getTechs()) {
// If we're out of astech teams, the rest of the techs are unsupporeted and don't count.
if (astechTeams <= 0) {
break;
}
if (tech.getSkill(SkillType.S_TECH_MECH) != null) {
setMechTechTeams(getMechTechTeams() + 1);
astechTeams--;
} else if (tech.getSkill(SkillType.S_TECH_AERO) != null) {
setAeroTechTeams(getAeroTechTeams() + 1);
astechTeams--;
} else if (tech.getSkill(SkillType.S_TECH_MECHANIC) != null) {
setMechanicTeams(getMechanicTeams() + 1);
astechTeams--;
} else if (tech.getSkill(SkillType.S_TECH_BA) != null) {
setBaTechTeams(getBaTechTeams() + 1);
astechTeams--;
} else {
setGeneralTechTeams(getGeneralTechTeams() + 1);
astechTeams--;
}
}
boolean techShortage = false;
if (getMechTechTeamsNeeded() > getMechTechTeams()) {
techShortage = true;
}
if (getAeroTechTeamsNeeded() > getAeroTechTeams()) {
techShortage = true;
}
if (getMechanicTeamsNeeded() > getMechanicTeams()) {
techShortage = true;
}
if (getBattleArmorTechTeamsNeeded() > getBaTechTeams()) {
techShortage = true;
}
setTotalTechTeams(getMechTechTeams() + getAeroTechTeams() + getMechanicTeams() + getBaTechTeams() +
getGeneralTechTeams());
int totalTechTeamsNeeded = getMechTechTeamsNeeded() + getAeroTechTeamsNeeded() + getMechanicTeamsNeeded() +
getBattleArmorTechTeamsNeeded();
setSupportPercent(BigDecimal.ZERO);
if (totalTechTeamsNeeded != 0) {
setSupportPercent(BigDecimal.valueOf(getTotalTechTeams())
.divide(BigDecimal.valueOf(totalTechTeamsNeeded), 5, BigDecimal.ROUND_HALF_UP)
.multiply(HUNDRED));
}
if (techShortage) {
totalValue -= 5;
} else {
if (getSupportPercent().compareTo(BigDecimal.valueOf(200)) > 0) {
totalValue += 15;
} else if (getSupportPercent().compareTo(BigDecimal.valueOf(175)) > 0) {
totalValue += 10;
} else if (getSupportPercent().compareTo(BigDecimal.valueOf(149)) > 0) {
totalValue += 5;
}
}
return totalValue;
}
// Campaign Ops counts both Doctors and Admins as admins.
private int calcAdminSupportValue() {
int admins = getCampaign().getAdmins().size();
int docs = getCampaign().getAdmins().size();
if (getAdminsNeeded() > (admins + docs)) {
return -5;
}
return 0;
}
private int calcLargeCraftSupportValue() {
boolean crewShortage = false;
for (Unit u : getCampaign().getCopyOfUnits()) {
if (u.getEntity() instanceof SmallCraft || u.getEntity() instanceof Jumpship) {
if (u.getActiveCrew().size() < u.getFullCrewSize()) {
crewShortage = true;
break;
}
}
}
return crewShortage ? -5 : 0;
}
@Override
public int getSupportValue() {
int value = calcTechSupportValue();
value += calcAdminSupportValue();
value += calcLargeCraftSupportValue();
return value;
}
@Override
public BigDecimal getTransportPercent() {
// Handled under getTransportValue()
return BigDecimal.ZERO;
}
public int getFinancialValue() {
return getCampaign().getFinances().isInDebt() ? -10 : 0;
}
// ToDo: MekHQ doesn't currently support recording crimes.
private int getCrimesPenalty() {
return 0;
}
// ToDo MekHQ doesn't current apply completion dates to missions.
private int getIdleTimeModifier() {
return 0;
}
@Override
public int getModifier() {
BigDecimal reputation = new BigDecimal(calculateUnitRatingScore());
return reputation.divide(BigDecimal.TEN, 0, RoundingMode.DOWN).intValue();
}
private String getExperienceDetails() {
StringBuilder out = new StringBuilder();
out.append(String.format("%-" + HEADER_LENGTH + "s %3d", "Experience:", getExperienceValue())).append("\n");
out.append(String.format(" %-" + SUBHEADER_LENGTH + "s %3s",
"Average Experience:",
getExperienceLevelName(calcAverageExperience())))
.append("\n");
final String TEMPLATE = " #%-" + CATEGORY_LENGTH + "s %3d";
Map<String, Integer> skillRatingCounts = getSkillRatingCounts();
boolean first = true;
for (String nm : SkillType.SKILL_LEVEL_NAMES) {
if (skillRatingCounts.containsKey(nm)) {
if (!first) {
out.append("\n");
}
out.append(String.format(TEMPLATE, nm + ":", skillRatingCounts.get(nm)));
first = false;
}
}
return out.toString();
}
private String getCommanderDetails() {
StringBuilder out = new StringBuilder();
String commanderName = null == getCommander() ? "" : "(" + getCommander().getName() + ")";
out.append(String.format("%-" + HEADER_LENGTH + "s %3d %s", "Commander:", getCommanderValue(), commanderName));
final String TEMPLATE = " %-" + SUBHEADER_LENGTH + "s %3d";
out.append("\n").append(String.format(TEMPLATE, "Leadership:", getCommanderSkill(SkillType.S_LEADER)));
out.append("\n").append(String.format(TEMPLATE, "Negotiation:", getCommanderSkill(SkillType.S_NEG)));
out.append("\n").append(String.format(TEMPLATE, "Strategy:", getCommanderSkill(SkillType.S_STRATEGY)));
out.append("\n").append(String.format(TEMPLATE, "Tactics:", getCommanderSkill(SkillType.S_TACTICS)));
return out.toString();
}
private String getCombatRecordDetails() {
final String TEMPLATE = " %-" + SUBHEADER_LENGTH + "s %3d";
return String.format("%-" + HEADER_LENGTH + "s %3d", "Combat Record:", getCombatRecordValue()) +
"\n" + String.format(TEMPLATE, "Successful Missions:", getSuccessCount()) +
"\n" + String.format(TEMPLATE, "Failed Missions:", getFailCount()) +
"\n" + String.format(TEMPLATE, "Contract Breaches:", getBreachCount());
}
String getTransportationDetails() {
final String TEMPLATE = " %-" + CATEGORY_LENGTH + "s %3d needed / %3d available";
int heavyVeeBayCount = getHeavyVeeBayCount();
int excessHeavyVeeBays = Math.max(0, heavyVeeBayCount - getHeavyVeeCount());
String out = String.format("%-" + HEADER_LENGTH + "s %3d", "Transportation:", getTransportValue()) +
"\n" + String.format(TEMPLATE, "Mech Bays:", getMechCount(), getMechBayCount()) +
"\n" + String.format(TEMPLATE, "Fighter Bays:", getFighterCount(), getFighterBayCount()) +
"\n" + String.format(TEMPLATE, "Small Craft Bays:", getSmallCraftCount(), getSmallCraftBayCount()) +
"\n" + String.format(TEMPLATE, "Protomech Bays:", getProtoCount(), getProtoBayCount()) +
"\n" + String.format(TEMPLATE, "Heavy Vehicle Bays:", getHeavyVeeCount(), heavyVeeBayCount) +
"\n" + String.format(TEMPLATE, "Light Vehicle Bays:", getLightVeeCount(), getLightVeeBayCount()) +
" (plus " + excessHeavyVeeBays + " excess heavy)" +
"\n" + String.format(TEMPLATE, "BA Bays:", getBattleArmorCount() / 5, getBaBayCount()) +
"\n" + String.format(TEMPLATE, "Infantry Bays:", getInfantryCount() / 28, getInfantryBayCount()) +
"\n" + String.format(TEMPLATE, "Docking Collars:", getDropshipCount(), getDockingCollarCount());
final String TEMPLATE_2 = " %-" + CATEGORY_LENGTH + "s %3s";
out += "\n" + String.format(TEMPLATE_2, "Has Jumpships?", getJumpshipCount() > 0 ? "Yes" : "No");
out += "\n" + String.format(TEMPLATE_2, "Has Warships?", getWarshipCount() > 0 ? "Yes" : "No");
return out;
}
private String getSupportDetails() {
StringBuilder out = new StringBuilder();
out.append(String.format("%-" + HEADER_LENGTH + "s %3d", "Support:", getSupportValue()));
final String TEMPLATE_CAT = " %-" + CATEGORY_LENGTH + "s %4d needed / %4d available";
out.append("\n Tech Support:");
out.append("\n").append(String.format(TEMPLATE_CAT, "Mech Techs:", getMechTechTeamsNeeded(), getMechTechTeams()));
out.append("\n NOTE: Protomechs and mechs use same techs.");
out.append("\n").append(String.format(TEMPLATE_CAT,
"Aero Techs:",
getAeroTechTeamsNeeded(),
getAeroTechTeams()));
out.append("\n").append(String.format(TEMPLATE_CAT, "Mechanics:", getMechanicTeamsNeeded(), getMechanicTeams()));
out.append("\n NOTE: Vehicles and Infantry use the same mechanics.");
out.append("\n").append(String.format(TEMPLATE_CAT, "BA Techs:",
getBattleArmorTechTeamsNeeded(),
getBaTechTeams()));
out.append("\n").append(String.format(TEMPLATE_CAT, "Astechs:", technicians * 6, getCampaign().getAstechPool()));
out.append("\n").append(String.format(" %-" + (CATEGORY_LENGTH + 4) + "s %4d needed / %4d available",
"Admin Support:",
getAdminsNeeded(),
getTotalAdmins()));
out.append("\n Large Craft Crew:");
if (getCraftWithoutCrew().size() < 1) {
out.append("\n All fully crewed.");
} else {
for (String s : getCraftWithoutCrew()) {
out.append("\n ").append(s).append(" short crew.");
}
}
return out.toString();
}
private int getTotalAdmins() {
return getCampaign().getAdmins().size() + getCampaign().getDoctors().size();
}
@Override
public String getDetails() {
final String TEMPLATE = "%-" + HEADER_LENGTH + "s %s";
initValues();
StringBuilder sb = new StringBuilder();
sb.append(String.format(TEMPLATE, "Unit Reputation:", calculateUnitRatingScore()));
sb.append("\n").append(" Method: Campaign Operations\n\n");
sb.append(getExperienceDetails()).append("\n\n");
sb.append(getCommanderDetails()).append("\n\n");
sb.append(getCombatRecordDetails()).append("\n\n");
sb.append(getTransportationDetails()).append("\n\n");
sb.append(getSupportDetails()).append("\n\n");
sb.append(String.format(TEMPLATE, "Financial", getFinancialValue()));
sb.append("\n").append(String.format(" %-" + SUBHEADER_LENGTH + "s %3s",
"In Debt?",
getCampaign().getFinances().isInDebt() ? "Yes" : "No"));
sb.append("\n\n")
.append(String.format(TEMPLATE, "Criminal Activity:", 0))
.append(" (MHQ does not currently track criminal activity.)");
sb.append("\n\n")
.append(String.format(TEMPLATE, "Inactivity Modifier:", 0))
.append(" (MHQ does not track end dates for missions/contracts.)");
return new String(sb);
}
@Override
public String getHelpText() {
return "Method: Campaign Ops\n" +
"An attempt to match the Campaign Ops method for calculating the Reputation as closely as possible.\n" +
"Known differences include the following:\n" +
"+ Command: Does not incorporate any positive or negative traits from AToW or BRPG3." +
"+ Transportation: Transportation needs of Support Personnel are not accounted for as MHQ does not " +
"track Bay Personnel or Passenger Quarters.\n" +
"+ Criminal Activity: MHQ does not currently track criminal activity." +
"+ Inactivity: MHQ does not track end dates for missions/contracts.";
}
private void setNonAdminPersonnelCount(int nonAdminPersonnelCount) {
this.nonAdminPersonnelCount = nonAdminPersonnelCount;
}
private void setMechTechTeamsNeeded(int mechTechTeamsNeeded) {
this.mechTechTeamsNeeded = mechTechTeamsNeeded;
}
private void setMechanicTeamsNeeded(int mechanicTeamsNeeded) {
this.mechanicTeamsNeeded = mechanicTeamsNeeded;
}
private void setBattleArmorTechTeamsNeeded(int battleArmorTechTeamsNeeded) {
this.battleArmorTechTeamsNeeded = battleArmorTechTeamsNeeded;
}
private void setAeroTechTeamsNeeded(int aeroTechTeamsNeeded) {
this.aeroTechTeamsNeeded = aeroTechTeamsNeeded;
}
private void setAdminsNeeded(int adminsNeeded) {
this.adminsNeeded = adminsNeeded;
}
private int getTotalTechTeams() {
return totalTechTeams;
}
private void setTotalTechTeams(int totalTechTeams) {
this.totalTechTeams = totalTechTeams;
}
private int getMechTechTeams() {
return mechTechTeams;
}
private void setMechTechTeams(int mechTechTeams) {
this.mechTechTeams = mechTechTeams;
}
private int getAeroTechTeams() {
return aeroTechTeams;
}
private void setAeroTechTeams(int aeroTechTeams) {
this.aeroTechTeams = aeroTechTeams;
}
private int getMechanicTeams() {
return mechanicTeams;
}
private void setMechanicTeams(int mechanicTeams) {
this.mechanicTeams = mechanicTeams;
}
private int getBaTechTeams() {
return baTechTeams;
}
private void setBaTechTeams(int baTechTeams) {
this.baTechTeams = baTechTeams;
}
private int getGeneralTechTeams() {
return generalTechTeams;
}
private void setGeneralTechTeams(int generalTechTeams) {
this.generalTechTeams = generalTechTeams;
}
private List<String> getCraftWithoutCrew() {
List<String> copy = new ArrayList<>(craftWithoutCrew.size());
Collections.copy(copy, craftWithoutCrew);
return copy;
}
private void addCraftWithoutCrew(Unit u) {
craftWithoutCrew.add(u.getName());
}
private void clearCraftWithoutCrew() {
craftWithoutCrew.clear();
}
}