/* * Refit.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.parts; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintStream; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; import java.util.Hashtable; import java.util.UUID; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import megamek.common.AmmoType; import megamek.common.BattleArmor; import megamek.common.BipedMech; import megamek.common.Entity; import megamek.common.EquipmentType; import megamek.common.Infantry; import megamek.common.Mech; import megamek.common.MechFileParser; import megamek.common.MechSummary; import megamek.common.MechSummaryCache; import megamek.common.TargetRoll; import megamek.common.WeaponType; import megamek.common.loaders.BLKFile; import megamek.common.loaders.EntityLoadingException; import megamek.common.weapons.InfantryAttack; import megameklab.com.util.UnitUtil; import mekhq.MekHQ; import mekhq.MekHqXmlUtil; import mekhq.MhqFileUtil; import mekhq.Utilities; import mekhq.Version; import mekhq.campaign.event.PartChangedEvent; import mekhq.campaign.event.UnitRefitEvent; import mekhq.campaign.parts.equipment.AmmoBin; import mekhq.campaign.parts.equipment.EquipmentPart; import mekhq.campaign.parts.equipment.MissingAmmoBin; import mekhq.campaign.parts.equipment.MissingEquipmentPart; import mekhq.campaign.personnel.Person; import mekhq.campaign.personnel.SkillType; import mekhq.campaign.unit.Unit; import mekhq.campaign.work.IAcquisitionWork; import mekhq.campaign.work.IPartWork; /** * This object tracks the refit of a given unit into a new unit. * It has fields for the current entity and the new entity and it * uses these to calculate various characteristics of the refit. * * It can then also be used to track the actual refit process, by * attaching it to a Unit. * * @author Jay Lawson <jaylawson39 at yahoo.com> */ public class Refit extends Part implements IPartWork, IAcquisitionWork { /** * */ private static final long serialVersionUID = -1765098410743713570L; public static final int NO_CHANGE = 0; public static final int CLASS_OMNI = 1; public static final int CLASS_A = 2; public static final int CLASS_B = 3; public static final int CLASS_C = 4; public static final int CLASS_D = 5; public static final int CLASS_E = 6; public static final int CLASS_F = 7; private Unit oldUnit; private Entity newEntity; private int refitClass; private int time; private int timeSpent; private long cost; private boolean failedCheck; private boolean customJob; private boolean isRefurbishing; private boolean kitFound; private String fixableString; private ArrayList<Integer> oldUnitParts; private ArrayList<Integer> newUnitParts; private ArrayList<Part> shoppingList; private int armorNeeded; private Armor newArmorSupplies; private int newArmorSuppliesId; private boolean sameArmorType; private UUID assignedTechId; private int oldTechId = -1; public Refit() { oldUnitParts = new ArrayList<Integer>(); newUnitParts = new ArrayList<Integer>(); shoppingList = new ArrayList<Part>(); fixableString = null; } public Refit(Unit oUnit, Entity newEn, boolean custom, boolean refurbish) { isRefurbishing = refurbish; customJob = custom; oldUnit = oUnit; newEntity = newEn; newEntity.setOwner(oldUnit.getEntity().getOwner()); newEntity.setGame(oldUnit.getEntity().getGame()); oldUnitParts = new ArrayList<Integer>(); newUnitParts = new ArrayList<Integer>(); shoppingList = new ArrayList<Part>(); failedCheck = false; timeSpent = 0; fixableString = null; kitFound = false; campaign = oldUnit.campaign; calculate(); if(customJob) { suggestNewName(); } } public static String getRefitClassName(int refitClass) { switch(refitClass) { case NO_CHANGE: return "No Change"; case CLASS_A: return "Class A (Field)"; case CLASS_B: return "Class B (Field)"; case CLASS_C: return "Class C (Maintenance)"; case CLASS_D: return "Class D (Maintenance)"; case CLASS_E: return "Class E (Factory)"; case CLASS_F: return "Class F (Factory)"; case CLASS_OMNI: return "Omni Repod"; default: return "Unknown"; } } public String getRefitClassName() { return getRefitClassName(refitClass); } public int getRefitClass() { return refitClass; } public long getCost() { return cost; } public ArrayList<Part> getShoppingList() { return shoppingList; } public String[] getShoppingListDescription() { Hashtable<String,Integer> tally = new Hashtable<String,Integer>(); Hashtable<String,String> desc = new Hashtable<String,String>(); for(Part p : shoppingList) { if(p instanceof Armor) { continue; } if(null != tally.get(p.getName())) { tally.put(p.getName(), tally.get(p.getName()) + 1); desc.put(p.getName(), p.getQuantityName(tally.get(p.getName()))); } else { tally.put(p.getName(), 1); desc.put(p.getName(), p.getQuantityName(1)); } } if(null != newArmorSupplies) { int actualAmountNeeded = armorNeeded; Armor existingSupplies = getExistingArmorSupplies(); if(null != existingSupplies) { actualAmountNeeded -= existingSupplies.getAmount(); } if(actualAmountNeeded > 0) { Armor a = (Armor)newArmorSupplies.getNewPart(); a.setAmount(actualAmountNeeded); desc.put(a.getName(), a.getQuantityName(1)); } } String[] descs = new String[desc.keySet().size()]; int i = 0; for(String name : desc.keySet()) { descs[i] = desc.get(name); i++; } return descs; } public int getTime() { return time; } public void calculate() { Unit newUnit = new Unit(newEntity, oldUnit.campaign); newUnit.initializeParts(false); refitClass = NO_CHANGE; boolean isOmniRefit = oldUnit.getEntity().isOmni() && newEntity.isOmni(); if (isOmniRefit && !Utilities.isOmniVariant(oldUnit.getEntity(), newEntity)) { fixableString = "A unit loses omni capabilities if any fixed equipment is modified."; return; } time = 0; sameArmorType = newEntity.getArmorType(0) == oldUnit.getEntity().getArmorType(0); int recycledArmorPoints = 0; boolean replacingLocations = false; boolean[] locationHasNewStuff = new boolean[newEntity.locations()]; boolean[] locationLostOldStuff = new boolean[newEntity.locations()]; HashMap<AmmoType,Integer> ammoNeeded = new HashMap<AmmoType,Integer>(); HashMap<AmmoType,Integer> ammoRemoved = new HashMap<AmmoType,Integer>(); ArrayList<Part> newPartList = new ArrayList<Part>(); //Step 1: put all of the parts from the current unit into a new arraylist so they can //be removed when we find a match. for(Part p : oldUnit.getParts()) { if (!isOmniRefit || p.isOmniPodded()) { oldUnitParts.add(p.getId()); } } //Step 2: loop through the parts arraylist in the newUnit and attempt to find the //corresponding part of missing part in the parts arraylist we just created. Depending on //what we find, we may have: //a) An exact copy in the same location - we move the part from the oldunit parts to the //newunit parts. Nothing needs to be changed in terms of refit class, time, or anything. //b) An exact copy in a different location - move this part to the newunit part list, but //change its location id. Change refit class to C and add time for removing and reinstalling //part. //c) We dont find the part in the oldunit part list. That means this is a new part. Add //this to the newequipment arraylist from step 3. Don't change anything in terms of refit //stats yet, that will happen later. for(Part part : newUnit.getParts()) { if (isOmniRefit && !part.isOmniPodded()) { continue; } boolean partFound = false; Part movedPart = null; int moveIndex = 0; int i = -1; for(int pid : oldUnitParts) { Part oPart = oldUnit.campaign.getPart(pid); i++; if (isOmniRefit && !oPart.isOmniPodded()) { continue; } //FIXME: There have been instances of null oParts here. Save/load will fix these, but //I would like to figure out the source. From experimentation, I think it has to do with //cancelling a prior refit. if(((oPart instanceof MissingPart && ((MissingPart)oPart).isAcceptableReplacement(part, true)) || oPart.isSamePartType(part))) { //need a special check for location and armor amount for armor if(oPart instanceof Armor && (((Armor)oPart).getLocation() != ((Armor)part).getLocation() || ((Armor)oPart).getTotalAmount() != ((Armor)part).getTotalAmount())) { continue; } if(part instanceof EquipmentPart) { //check the location to see if this moved. If so, then don't break, but //save this in case we fail to find equipment in the same location. int loc = ((EquipmentPart)part).getLocation(); boolean rear = ((EquipmentPart)part).isRearFacing(); if((oPart instanceof EquipmentPart && (((EquipmentPart)oPart).getLocation() != loc || ((EquipmentPart)oPart).isRearFacing() != rear)) || (oPart instanceof MissingEquipmentPart && (((MissingEquipmentPart)oPart).getLocation() != loc || ((MissingEquipmentPart)oPart).isRearFacing() != rear))) { movedPart = oPart; moveIndex = i; continue; } } newUnitParts.add(pid); partFound = true; break; } } if(partFound) { oldUnitParts.remove(i); } else if(null != movedPart) { newUnitParts.add(movedPart.getId()); oldUnitParts.remove(moveIndex); if (movedPart.getLocation() >= 0) { locationLostOldStuff[movedPart.getLocation()] = true; } if(isOmniRefit && movedPart.isOmniPodded()) { updateRefitClass(CLASS_OMNI); } else { updateRefitClass(CLASS_C); } if(movedPart instanceof EquipmentPart) { //TODO: set this as salvaging //boolean isSalvaging = movedPart.isSalvaging(); //movedPart.setSalvaging(true); //movedPart.updateConditionFromEntity(false); time += movedPart.getBaseTime(); //movedPart.setSalvaging(isSalvaging); } } else { //its a new part //dont actually add the part iself but rather its missing equivalent //except in the case of armor if(part instanceof Armor || part instanceof AmmoBin) { newPartList.add(part); } else { Part mPart = part.getMissingPart(); if(null != mPart) { newPartList.add(mPart); } else { MekHQ.logError("null missing part for " + part.getName() + " during refit calculations"); } } } } //Step 3: loop through the newequipment list and determine what class of refit it entails, //add time for both installing this part. //This may involve taking a look at remaining oldunit parts to determine whether this item //replaces another item of the same or fewer crits. Also add cost for new equipment. //at the same time, check spare parts for new equipment //first put oldUnitParts in a new arraylist so they can be removed as we find them ArrayList<Integer> tempParts = new ArrayList<Integer>(); tempParts.addAll(oldUnitParts); armorNeeded = 0; int atype = 0; boolean aclan = false; HashMap<Integer,Integer> partQuantity = new HashMap<Integer,Integer>(); for(Part nPart : newPartList) { //TODO: I don't think we need this here anymore nPart.setUnit(oldUnit); //We don't actually want to order new BA suits; we're just pretending that we're altering the //existing suits. if (nPart instanceof MissingBattleArmorSuit) { continue; } /*ADD TIMES AND COSTS*/ if(nPart instanceof MissingPart) { time += nPart.getBaseTime(); Part replacement = ((MissingPart)nPart).findReplacement(true); //check quantity //TODO: the one weakness here is that we will not pick up damaged parts if(null != replacement && null == partQuantity.get(replacement.getId())) { partQuantity.put(replacement.getId(), replacement.getQuantity()); } if(null != replacement && partQuantity.get(replacement.getId()) > 0) { newUnitParts.add(replacement.getId()); //adjust quantity partQuantity.put(replacement.getId(), partQuantity.get(replacement.getId())-1); } else { replacement = ((MissingPart)nPart).getNewPart(); //set entity for variable cost items replacement.setUnit(newUnit); cost += replacement.getActualValue(); shoppingList.add(nPart); } } else if(nPart instanceof Armor) { int totalAmount = ((Armor)nPart).getTotalAmount(); time += totalAmount * ((Armor)nPart).getBaseTimeFor(newEntity); armorNeeded += totalAmount; atype = ((Armor)nPart).getType(); aclan = ((Armor)nPart).isClanTechBase(); //armor always gets added to the shopping list - it will be checked for differently //NOT ANYMORE - I think this is overkill, lets just reuse existing armor parts //shoppingList.add(nPart); } else if(nPart instanceof AmmoBin) { AmmoType type = (AmmoType)((AmmoBin)nPart).getType(); ammoNeeded.merge(type, ((AmmoType)((AmmoBin)nPart).getType()).getShots(), (a, b) -> a + b); time += 120; //check for ammo bins in storage to avoid the proliferation of infinite ammo bins MissingAmmoBin mab = (MissingAmmoBin)nPart.getMissingPart(); Part replacement = mab.findReplacement(true); //check quantity //TODO: the one weakness here is that we will not pick up damaged parts if(null != replacement && null == partQuantity.get(replacement.getId())) { partQuantity.put(replacement.getId(), replacement.getQuantity()); } if(null != replacement && partQuantity.get(replacement.getId()) > 0) { newUnitParts.add(replacement.getId()); //adjust quantity partQuantity.put(replacement.getId(), partQuantity.get(replacement.getId())-1); } else { shoppingList.add(nPart); } } /*CHECK REFIT CLASS*/ if(nPart instanceof MissingEnginePart) { if(oldUnit.getEntity().getEngine().getRating() != newUnit.getEntity().getEngine().getRating()) { updateRefitClass(CLASS_D); } if(newUnit.getEntity().getEngine().getEngineType() != oldUnit.getEntity().getEngine().getEngineType()) { updateRefitClass(CLASS_F); } if(((MissingEnginePart)nPart).getEngine().getSideTorsoCriticalSlots().length > 0) { locationHasNewStuff[Mech.LOC_LT] = true; locationHasNewStuff[Mech.LOC_RT] = true; } } else if(nPart instanceof MissingMekGyro) { updateRefitClass(CLASS_F); } else if(nPart instanceof MissingMekLocation) { replacingLocations = true; if(((Mech)newUnit.getEntity()).hasTSM() != ((Mech)oldUnit.getEntity()).hasTSM()) { updateRefitClass(CLASS_E); } else { updateRefitClass(CLASS_F); } } else if(nPart instanceof Armor) { updateRefitClass(CLASS_C); locationHasNewStuff[((Armor)nPart).getLocation()] = true; } else if(nPart instanceof MissingMekCockpit) { updateRefitClass(CLASS_F); locationHasNewStuff[Mech.LOC_HEAD] = true; }else if(nPart instanceof MissingMekActuator) { if(isOmniRefit && nPart.isOmniPoddable()) { updateRefitClass(CLASS_OMNI); } else { updateRefitClass(CLASS_D); } locationHasNewStuff[((MissingMekActuator)nPart).getLocation()] = true; } else if(nPart instanceof MissingInfantryMotiveType || nPart instanceof MissingInfantryArmorPart) { updateRefitClass(CLASS_A); } else { //determine whether this is A, B, or C if(nPart instanceof MissingEquipmentPart || nPart instanceof AmmoBin) { nPart.setUnit(newUnit); int loc = -1; EquipmentType type = null; if(nPart instanceof MissingEquipmentPart) { loc = ((MissingEquipmentPart)nPart).getLocation(); if(loc > -1 && loc < newEntity.locations()) { locationHasNewStuff[loc] = true; } type = ((MissingEquipmentPart)nPart).getType(); } else { loc = ((AmmoBin)nPart).getLocation(); if(loc > -1 && loc < newEntity.locations()) { locationHasNewStuff[loc] = true; } type = ((AmmoBin)nPart).getType(); } int crits = type.getCriticals(newUnit.getEntity()); nPart.setUnit(oldUnit); int i = -1; boolean matchFound = false; int matchIndex = -1; int rClass = CLASS_D; for(int pid : tempParts) { Part oPart = oldUnit.campaign.getPart(pid); i++; int oLoc = -1; int oCrits = -1; EquipmentType oType = null; if(oPart instanceof EquipmentPart) { oLoc = ((EquipmentPart)oPart).getLocation(); oType = ((EquipmentPart)oPart).getType(); oCrits = oType.getCriticals(oldUnit.getEntity()); } if(oPart instanceof MissingEquipmentPart) { oLoc = ((MissingEquipmentPart)oPart).getLocation(); oType = ((MissingEquipmentPart)oPart).getType(); oCrits = oType.getCriticals(oldUnit.getEntity()); } if(loc != oLoc) { continue; } if(crits == oCrits && (type.hasFlag(WeaponType.F_LASER) == oType.hasFlag(WeaponType.F_LASER)) && (type.hasFlag(WeaponType.F_MISSILE) == oType.hasFlag(WeaponType.F_MISSILE)) && (type.hasFlag(WeaponType.F_BALLISTIC) == oType.hasFlag(WeaponType.F_BALLISTIC)) && (type.hasFlag(WeaponType.F_ARTILLERY) == oType.hasFlag(WeaponType.F_ARTILLERY))) { rClass = CLASS_A; matchFound = true; matchIndex = i; break; } else if (crits <= oCrits) { rClass = CLASS_B; matchFound = true; matchIndex = i; //don't break because we may find something better } else { rClass = CLASS_C; matchFound = true; matchIndex = i; //don't break because we may find something better } } if(isOmniRefit && nPart.isOmniPoddable()) { rClass = CLASS_OMNI; } updateRefitClass(rClass); if(matchFound) { tempParts.remove(matchIndex); } } } } //if oldUnitParts is not empty we are removing some stuff and so this should //be at least a Class A refit if(!oldUnitParts.isEmpty()) { if (isOmniRefit) { updateRefitClass(CLASS_OMNI); } else { updateRefitClass(CLASS_A); } } //Step 4: loop through remaining equipment on oldunit parts and add time for removing. for(int pid : oldUnitParts) { Part oPart = oldUnit.campaign.getPart(pid); //We're pretending we're changing the old suit rather than removing it. //We also want to avoid accounting for legacy InfantryAttack parts. if (oPart instanceof BattleArmorSuit || (oPart instanceof EquipmentPart && ((EquipmentPart)oPart).getType() instanceof InfantryAttack)) { continue; } if (oPart.getLocation() >= 0) { locationLostOldStuff[oPart.getLocation()] = true; } if(oPart instanceof MissingPart) { continue; } if(oPart instanceof AmmoBin) { int remainingShots = ((AmmoBin)oPart).getFullShots() - ((AmmoBin)oPart).getShotsNeeded(); if(remainingShots > 0) { time += 120; ammoRemoved.merge((AmmoType)((AmmoBin)oPart).getType(), remainingShots, (a, b) -> a + b); } continue; } if(oPart instanceof Armor && sameArmorType) { recycledArmorPoints += ((Armor)oPart).getAmount(); continue; } boolean isSalvaging = oldUnit.isSalvage(); oldUnit.setSalvage(true); time += oPart.getBaseTime(); oldUnit.setSalvage(isSalvaging); } if(sameArmorType) { //if this is the same armor type then we can recyle armor armorNeeded -= recycledArmorPoints; } if(armorNeeded > 0) { newArmorSupplies = new Armor(0, atype, 0, 0, false, aclan, oldUnit.campaign); newArmorSupplies.setAmountNeeded(armorNeeded); newArmorSupplies.setRefitId(oldUnit.getId()); //check existing supplies before determining cost Armor existingArmorSupplies = getExistingArmorSupplies(); double tonnageNeeded = newArmorSupplies.getTonnageNeeded(); if(null != existingArmorSupplies) { tonnageNeeded = Math.max(0, tonnageNeeded - existingArmorSupplies.getTonnage()); } newArmorSupplies.setUnit(oldUnit); cost += newArmorSupplies.getStickerPrice() * (tonnageNeeded / 5.0); newArmorSupplies.setUnit(null); } //TODO: use ammo removed from the old unit in the case of changing between full ton and half //ton MG or OS/regular. for(AmmoType type : ammoNeeded.keySet()) { int shotsNeeded = Math.max(ammoNeeded.get(type) - getAmmoAvailable(type), 0); int shotsPerTon = type.getShots(); cost += type.getCost(newEntity, false, -1) * ((double)shotsNeeded/shotsPerTon); } //deal with integral heat sinks //TODO: heat sinks on other units? if(newEntity instanceof Mech && (((Mech)newEntity).hasDoubleHeatSinks() != ((Mech)oldUnit.getEntity()).hasDoubleHeatSinks() || ((Mech)newEntity).hasLaserHeatSinks() != ((Mech)oldUnit.getEntity()).hasLaserHeatSinks())) { time += newEntity.getEngine().integralHeatSinkCapacity(((Mech)newEntity).hasCompactHeatSinks()) * 90; time += oldUnit.getEntity().getEngine().integralHeatSinkCapacity(((Mech)newEntity).hasCompactHeatSinks()) * 90; updateRefitClass(CLASS_D); } //check for CASE //TODO: we still dont have to order the part, we need to get the CASE issues sorted out for(int loc = 0; loc < newEntity.locations(); loc++) { if((newEntity.locationHasCase(loc) != oldUnit.getEntity().locationHasCase(loc) && !(newEntity.isClan() && newEntity instanceof Mech)) || (newEntity instanceof Mech && ((Mech)newEntity).hasCASEII(loc) != ((Mech)oldUnit.getEntity()).hasCASEII(loc))) { if(isOmniRefit) { updateRefitClass(CLASS_OMNI); } else { time += 60; updateRefitClass(CLASS_E); } } } //multiply time by refit class time *= getTimeMultiplier(); if(!customJob) { cost *= 1.1; } //TODO: track the number of locations changed so we can get stuff for omnis //TODO: some class D stuff is not omnipodable if(refitClass == CLASS_OMNI) { int nloc = 0; for(int loc = 0; loc < newEntity.locations(); loc++) { if(locationHasNewStuff[loc] || locationLostOldStuff[loc]) { nloc++; } } time = 30 * nloc; } //infantry take zero time to re-organize //also check for squad size and number changes if(oldUnit.getEntity() instanceof Infantry && !(oldUnit.getEntity() instanceof BattleArmor)) { if(((Infantry)oldUnit.getEntity()).getSquadN() != ((Infantry)newEntity).getSquadN() ||((Infantry)oldUnit.getEntity()).getSquadSize() != ((Infantry)newEntity).getSquadSize()) { updateRefitClass(CLASS_A); } time = 0; } //figure out if we are putting new stuff on a missing location if(!replacingLocations) { for(int loc = 0; loc < newEntity.locations(); loc++) { if(locationHasNewStuff[loc] && oldUnit.isLocationDestroyed(loc)) { String problem = "Can't add new equipment to a missing " + newEntity.getLocationAbbr(loc); if(null == fixableString) { fixableString = problem; } else { fixableString += "\n" + problem; } } } } // Now we set the refurbishment values if (isRefurbishing) { refitClass = CLASS_E; if (newEntity instanceof megamek.common.Warship || newEntity instanceof megamek.common.SpaceStation) { time = 40320; } else if (newEntity instanceof megamek.common.Dropship || newEntity instanceof megamek.common.Jumpship) { time = 13440; } else if (newEntity instanceof Mech || newEntity instanceof megamek.common.Aero || newEntity instanceof megamek.common.ConvFighter || newEntity instanceof megamek.common.SmallCraft) { time = 6720; } else if (newEntity instanceof BattleArmor || newEntity instanceof megamek.common.Tank || newEntity instanceof megamek.common.Protomech) { time = 3360; } else { time = 1111; MekHQ.logMessage("Unit " + newEntity.getModel() + " did not set its time correctly."); } // The cost is equal to 10 percent of the units base value (not modified for quality). cost = (long) (oldUnit.getBuyCost() * .1); } } public void begin() throws EntityLoadingException, IOException { if(customJob) { saveCustomization(); } oldUnit.setRefit(this); newEntity.setOwner(oldUnit.getEntity().getOwner()); reserveNewParts(); if(customJob) { //add the stuff on the shopping list to the master shopping list ArrayList<Part> newShoppingList = new ArrayList<Part>(); for(Part part : shoppingList) { part.setUnit(null); if(part instanceof Armor) { //Taharqa: WE shouldn't be here anymore, given that I am no longer adding //armor by location to the shopping list but instead changing it all via //the newArmorSupplies object, but commented out for completeness //oldUnit.campaign.addPart(part, 0); //part.setRefitId(oldUnit.getId()); //newUnitParts.add(part.getId()); } else if(part instanceof AmmoBin) { //ammo bins are free - bleh AmmoBin bin = (AmmoBin)part; bin.setShotsNeeded(bin.getFullShots()); part.setRefitId(oldUnit.getId()); oldUnit.campaign.addPart(part, 0); newUnitParts.add(part.getId()); bin.loadBin(); if(bin.needsFixing()) { oldUnit.campaign.getShoppingList().addShoppingItem(bin, 1, oldUnit.campaign); //need to call it a second time to use up if found bin.loadBin(); } } else if(part instanceof IAcquisitionWork) { oldUnit.campaign.getShoppingList().addShoppingItem(((IAcquisitionWork)part), 1, oldUnit.campaign); newShoppingList.add(part); } } shoppingList = newShoppingList; if(null != newArmorSupplies) { //add enough armor to the shopping list int armorSupplied = 0; Armor existingArmorSupplies = getExistingArmorSupplies(); if(null != existingArmorSupplies) { armorSupplied = existingArmorSupplies.getAmount(); } while(armorSupplied < armorNeeded) { armorSupplied += ((Armor)newArmorSupplies.getNewPart()).getAmount(); oldUnit.campaign.getShoppingList().addShoppingItem((Armor)newArmorSupplies.getNewPart(),1,oldUnit.campaign); } } } else { for(Part part : shoppingList) { part.setUnit(null); MekHQ.triggerEvent(new PartChangedEvent(part)); } checkForArmorSupplies(); if(shoppingList.isEmpty() && (null == newArmorSupplies || newArmorSupplies.getAmountNeeded() == 0)) { kitFound = true; } else { oldUnit.campaign.getShoppingList().addShoppingItem(this, 1, oldUnit.campaign); } } if (isRefurbishing) { if (campaign.buyRefurbishment(this)) { campaign.addReport("<font color='green'><b>Refurbishment ready to begin</b></font>"); } else { campaign.addReport("You cannot afford to refurbish " + oldUnit.getEntity().getShortName() + ". Transaction cancelled"); } } MekHQ.triggerEvent(new UnitRefitEvent(oldUnit)); } public void reserveNewParts() { //we need to loop through the new parts and //if they are not on a unit already, then we need //to set the refit id. Also, if there is more than one part //then we need to clone a part and reserve that instead ArrayList<Integer> newNewUnitParts = new ArrayList<Integer>(); for(int id : newUnitParts) { Part newPart = oldUnit.campaign.getPart(id); if(newPart.isSpare()) { if(newPart.getQuantity() > 1) { newPart.decrementQuantity(); newPart = newPart.clone(); newPart.setRefitId(oldUnit.getId()); oldUnit.campaign.addPart(newPart, 0); newNewUnitParts.add(newPart.getId()); } else { newPart.setRefitId(oldUnit.getId()); newNewUnitParts.add(id); } } else { newNewUnitParts.add(id); } } newUnitParts = newNewUnitParts; } public boolean partsInTransit() { for (int pid : newUnitParts) { Part part = oldUnit.campaign.getPart(pid); if (part == null) { if(null == part) { MekHQ.logMessage("part with id " + pid + " not found for refit of " + getDesc()); continue; } } if (!part.isPresent()) { return true; } } if(null != newArmorSupplies && !newArmorSupplies.isPresent()) { return true; } return false; } public boolean acquireParts() { if(!customJob) { checkForArmorSupplies(); return kitFound && !partsInTransit() && (null == newArmorSupplies || (armorNeeded - newArmorSupplies.getAmount()) <= 0); } ArrayList<Part> newShoppingList = new ArrayList<Part>(); for(Part part : shoppingList) { if(part instanceof IAcquisitionWork) { //check to see if we found a replacement Part replacement = ((MissingPart)part).findReplacement(true); if(null != replacement) { if(replacement.getQuantity() > 1) { Part actualReplacement = replacement.clone(); actualReplacement.setRefitId(oldUnit.getId()); oldUnit.campaign.addPart(actualReplacement, 0); newUnitParts.add(actualReplacement.getId()); replacement.decrementQuantity(); } else { replacement.setRefitId(oldUnit.getId()); newUnitParts.add(replacement.getId()); } } else { newShoppingList.add(part); } } } // Cycle through newUnitParts, find any ammo bins and if they need loading, try to load them boolean missingAmmo = false; for(int pid : newUnitParts) { Part part = oldUnit.campaign.getPart(pid); if(part instanceof AmmoBin && null == part.getUnit()) { AmmoBin bin = (AmmoBin)part; bin.loadBin(); if(bin.needsFixing()) { missingAmmo = true; } } } checkForArmorSupplies(); shoppingList = newShoppingList; // Also, check to make sure that they're not still in transit! - ralgith 2013/07/09 if (partsInTransit()) { return false; } return shoppingList.size() == 0 && !missingAmmo && (null == newArmorSupplies || (armorNeeded - newArmorSupplies.getAmount()) <= 0); } public void checkForArmorSupplies() { if(null == newArmorSupplies) { return; } Armor existingArmorSupplies = getExistingArmorSupplies(); int actualNeed = armorNeeded - newArmorSupplies.getAmount(); if(null != existingArmorSupplies && actualNeed > 0) { if(existingArmorSupplies.getAmount() > actualNeed) { newArmorSupplies.setAmount(armorNeeded); newArmorSupplies.setAmountNeeded(0); existingArmorSupplies.setAmount(existingArmorSupplies.getAmount() - actualNeed); } else { newArmorSupplies.setAmount(newArmorSupplies.getAmount() + existingArmorSupplies.getAmount()); newArmorSupplies.setAmountNeeded(newArmorSupplies.getAmountNeeded() - existingArmorSupplies.getAmount()); oldUnit.campaign.removePart(existingArmorSupplies); } if(newArmorSupplies.getId() <= 0) { oldUnit.campaign.addPart(newArmorSupplies, 0); } } } public Armor getExistingArmorSupplies() { Armor existingArmorSupplies = null; if(null == newArmorSupplies) { return null; } for(Part part : oldUnit.campaign.getSpareParts()) { if(part instanceof Armor && ((Armor)part).getType() == newArmorSupplies.getType() && ((Armor)part).isClanTechBase() == newArmorSupplies.isClanTechBase() && !part.isReservedForRefit() && part.isPresent()) { existingArmorSupplies = (Armor)part; break; } } return existingArmorSupplies; } public int getAmmoAvailable(AmmoType type) { for(Part part : oldUnit.campaign.getSpareParts()) { if(part instanceof AmmoStorage) { AmmoStorage a = (AmmoStorage)part; if(a.getType().equals(type)) { return a.getShots(); } } } return 0; } private void updateRefitClass(int rClass) { if(rClass > refitClass) { refitClass = rClass; } } public void cancel() { oldUnit.setRefit(null); for(int pid : newUnitParts) { Part part = oldUnit.campaign.getPart(pid); if(null == part) { MekHQ.logMessage("part with id " + pid + " not found for refit of " + getDesc()); continue; } part.setRefitId(null); if(part instanceof Armor) { oldUnit.campaign.removePart(part); } else if(part instanceof AmmoBin) { ((AmmoBin) part).unload(); oldUnit.campaign.removePart(part); } else { Part spare = oldUnit.campaign.checkForExistingSparePart(part); if(null != spare) { spare.incrementQuantity(); oldUnit.campaign.removePart(part); } } } if(null != newArmorSupplies) { newArmorSupplies.setRefitId(null); newArmorSupplies.setUnit(oldUnit); oldUnit.campaign.removePart(newArmorSupplies); newArmorSupplies.changeAmountAvailable(newArmorSupplies.getAmount()); } MekHQ.triggerEvent(new UnitRefitEvent(oldUnit)); } private void complete() { int atype = -1; boolean aclan = false; oldUnit.setRefit(null); Entity oldEntity = oldUnit.getEntity(); ArrayList<Person> soldiers = new ArrayList<Person>(); //unload any soldiers to reload later, because troop size may have changed if(oldEntity instanceof Infantry) { soldiers = oldUnit.getCrew(); for(Person soldier : soldiers) { oldUnit.remove(soldier, true); } } //add old parts to the warehouse for(int pid : oldUnitParts) { Part part = oldUnit.campaign.getPart(pid); if(null == part) { MekHQ.logMessage("old part with id " + pid + " not found for refit of " + getDesc()); continue; } if(part instanceof MekLocation && ((MekLocation)part).getLoc() == Mech.LOC_CT) { part.setUnit(null); oldUnit.campaign.removePart(part); continue; } // SI Should never be "kept" for the Warehouse // We also don't want to generate new BA suits that have been replaced // or allow legacy InfantryAttack BA parts to show up in the warehouse. else if(part instanceof StructuralIntegrity || part instanceof BattleArmorSuit || (part instanceof EquipmentPart && ((EquipmentPart)part).getType() instanceof InfantryAttack)) { part.setUnit(null); oldUnit.campaign.removePart(part); continue; } else if(part instanceof Armor) { Armor a = (Armor)part; //lets just re-use this armor part if(!sameArmorType) { //give the amount back to the warehouse since we are switching types a.changeAmountAvailable(a.getAmount()); if(null != newArmorSupplies) { a.changeType(newArmorSupplies.getType(), newArmorSupplies.isClanTechBase()); } } newUnitParts.add(pid); } else { if(part instanceof AmmoBin) { ((AmmoBin) part).unload(); } Part spare = oldUnit.campaign.checkForExistingSparePart(part); if(null != spare) { spare.incrementQuantity(); oldUnit.campaign.removePart(part); } } part.setUnit(null); } //dont forget to switch entities! oldUnit.setEntity(newEntity); //set up new parts ArrayList<Part> newParts = new ArrayList<Part>(); //We've already made the old suits go *poof*; now we materialize new ones. if (newEntity instanceof BattleArmor) { for (int t = BattleArmor.LOC_TROOPER_1; t < newEntity.locations(); t++) { Part suit = new BattleArmorSuit((BattleArmor)newEntity, t, oldUnit.campaign); newParts.add(suit); suit.setUnit(oldUnit); } } for(int pid : newUnitParts) { Part part = oldUnit.campaign.getPart(pid); if(null == part) { MekHQ.logMessage("part with id " + pid + " not found for refit of " + getDesc()); continue; } part.setUnit(oldUnit); part.setRefitId(null); newParts.add(part); if(part instanceof Armor) { //get amounts correct for armor part.updateConditionFromEntity(false); } else if (part instanceof AmmoBin) { ((AmmoBin)part).loadBin(); } } oldUnit.setParts(newParts); Utilities.unscrambleEquipmentNumbers(oldUnit); assignArmActuators(); if(null != newArmorSupplies) { oldUnit.campaign.removePart(newArmorSupplies); } //in some cases we may have had more armor on the original unit and so we may add more //back then we received if(sameArmorType && armorNeeded < 0) { Armor a = new Armor(0, atype, -1 * armorNeeded, -1, false, aclan, oldUnit.campaign); a.setUnit(oldUnit); a.changeAmountAvailable(a.getAmount()); } for(Part part : oldUnit.getParts()) { part.updateConditionFromPart(); } oldUnit.getEntity().setC3UUIDAsString(oldEntity.getC3UUIDAsString()); oldUnit.getEntity().setExternalIdAsString(oldUnit.getId().toString()); oldUnit.campaign.clearGameData(oldUnit.getEntity()); oldUnit.campaign.reloadGameEntities(); //reload any soldiers for(Person soldier : soldiers) { if(!oldUnit.canTakeMoreGunners()) { break; } oldUnit.addPilotOrSoldier(soldier); } oldUnit.resetPilotAndEntity(); if (isRefurbishing) { for (Part p : oldUnit.getParts()) { if (p.getQuality() != QUALITY_F) { p.improveQuality(); } } } MekHQ.triggerEvent(new UnitRefitEvent(oldUnit)); } public void saveCustomization() throws EntityLoadingException, IOException { UnitUtil.compactCriticals(newEntity); //UnitUtil.reIndexCrits(newEntity); Method is gone? String fileName = MhqFileUtil.escapeReservedCharacters(newEntity.getChassis() + " " + newEntity.getModel()); String sCustomsDir = "data"+File.separator+"mechfiles"+File.separator+"customs"; String sCustomsDirCampaign = sCustomsDir+File.separator+oldUnit.campaign.getName(); File customsDir = new File(sCustomsDir); if(!customsDir.exists()) { customsDir.mkdir(); } File customsDirCampaign = new File(sCustomsDirCampaign); if(!customsDirCampaign.exists()) { customsDirCampaign.mkdir(); } try { if (newEntity instanceof Mech) { //if this file already exists then don't overwrite it or we will end up with a bunch of copies String fileOutName = sCustomsDir + File.separator + fileName + ".mtf"; String fileNameCampaign = sCustomsDirCampaign + File.separator + fileName + ".mtf"; if((new File(fileOutName)).exists() || (new File(fileNameCampaign)).exists()) { throw new IOException("A file already exists with the custom name "+fileNameCampaign+". Please choose a different name. (Unit name and/or model)"); } FileOutputStream out = new FileOutputStream(fileNameCampaign); PrintStream p = new PrintStream(out); p.println(((Mech) newEntity).getMtf()); p.close(); out.close(); } else { //if this file already exists then don't overwrite it or we will end up with a bunch of copies String fileOutName = sCustomsDir + File.separator + fileName + ".blk"; String fileNameCampaign = sCustomsDirCampaign + File.separator + fileName + ".blk"; if((new File(fileOutName)).exists() || (new File(fileNameCampaign)).exists()) { throw new IOException("A file already exists with the custom name "+fileNameCampaign+". Please choose a different name. (Unit name and/or model)"); } BLKFile.encode(fileNameCampaign, newEntity); } } catch (Exception ex) { ex.printStackTrace(); } oldUnit.campaign.addCustom(newEntity.getChassis() + " " + newEntity.getModel()); MechSummaryCache.getInstance().loadMechData(); //I need to change the new entity to the one from the mtf file now, so that equip //nums will match MechSummary summary = MechSummaryCache.getInstance().getMech(newEntity.getChassis() + " " + newEntity.getModel()); if(null == summary) { throw(new EntityLoadingException()); } //try { newEntity = new MechFileParser(summary.getSourceFile(), summary.getEntryName()).getEntity(); /*} catch (EntityLoadingException ex) { Logger.getLogger(CampaignGUI.class.getName()) .log(Level.SEVERE, null, ex); }*/ } private int getTimeMultiplier() { int mult = 0; switch(refitClass) { case NO_CHANGE: mult = 0; break; case CLASS_A: case CLASS_B: mult = 1; break; case CLASS_C: mult = 2; break; case CLASS_D: mult = 3; break; case CLASS_E: mult = 4; break; case CLASS_F: mult = 5; break; default: mult = 1; } if(customJob) { mult *= 2; } return mult; } public Entity getOriginalEntity() { return oldUnit.getEntity(); } public Entity getNewEntity() { return newEntity; } public Unit getOriginalUnit() { return oldUnit; } public boolean hasFailedCheck() { return failedCheck; } @Override public boolean needsFixing() { return true; } @Override public int getDifficulty() { switch(refitClass) { case NO_CHANGE: return 0; case CLASS_A: return 1; case CLASS_B: return 1; case CLASS_C: return 2; case CLASS_D: return 2; case CLASS_E: return 3; case CLASS_F: return 4; case CLASS_OMNI: return -2; default: return 1; } } @Override public TargetRoll getAllMods(Person tech) { TargetRoll mods = new TargetRoll(getDifficulty(), "difficulty"); mods.append(oldUnit.getSiteMod()); if(oldUnit.getEntity().hasQuirk("easy_maintain")) { mods.addModifier(-1, "easy to maintain"); } else if(oldUnit.getEntity().hasQuirk("difficult_maintain")) { mods.addModifier(1, "difficult to maintain"); } if(customJob) { mods.addModifier(2, "custom job"); } return mods; } @Override public String succeed() { complete(); if (isRefurbishing) { return "Refurbishment of " + oldUnit.getEntity().getShortName() + " is complete."; } else { return "The customization of "+ oldUnit.getEntity().getShortName() + " is complete."; } } @Override public String fail(int rating) { timeSpent = 0; failedCheck = true; // Refurbishment doesn't get extra time like standard refits. if (isRefurbishing) { oldUnit.setRefit(null); // Failed roll results in lost time and money return "Refurbishment of " + oldUnit.getEntity().getShortName() + " was unsuccessful"; } else { return "The customization of " + oldUnit.getEntity().getShortName() + " will take " + getTimeLeft() + " additional minutes to complete."; } } @Override public void resetTimeSpent() { timeSpent = 0; } @Override public String getPartName() { if(customJob) { return newEntity.getShortName() + " Customization"; } else if (isRefurbishing) { return newEntity.getShortName() + " Refurbishment"; } else { return newEntity.getShortName() + " Refit Kit"; } } @Override public String getAcquisitionName() { return getPartName(); } @Override public String getName() { return getPartName(); } @Override public int getSkillMin() { return SkillType.EXP_GREEN; } @Override public int getBaseTime() { return time; } @Override public int getActualTime() { return time; } @Override public int getTimeSpent() { return timeSpent; } @Override public int getTimeLeft() { return time - timeSpent; } @Override public void addTimeSpent(int time) { timeSpent += time; } @Override public UUID getTeamId() { return assignedTechId; } @Override public void setTeamId(UUID id) { assignedTechId = id; } @Override public boolean hasWorkedOvertime() { return false; } @Override public void setWorkedOvertime(boolean b) { //do nothing } @Override public int getShorthandedMod() { // TODO Auto-generated method stub return 0; } @Override public void setShorthandedMod(int i) { // TODO Auto-generated method stub } @Override public void updateConditionFromEntity(boolean checkForDestruction) { //do nothing } @Override public void updateConditionFromPart() { //do nothing } @Override public void fix() { //do nothing } @Override public void remove(boolean salvage) { //do nothing } @Override public MissingPart getMissingPart() { //not applicable return null; } @Override public String getDesc() { return newEntity.getModel() + " " + getDetails(); } @Override public String getDetails() { return "(" + getRefitClassName() + "/" + getTimeLeft() + " minutes/" + Utilities.getCurrencyString(getCost()) + ")"; } @Override public Unit getUnit() { return oldUnit; } @Override public boolean isSalvaging() { return false; } @Override public String checkFixable() { return fixableString; } @Override public void writeToXml(PrintWriter pw1, int indentLvl) { pw1.println(MekHqXmlUtil.indentStr(indentLvl) + "<refit>"); pw1.println(MekHqXmlUtil.writeEntityToXmlString(newEntity, indentLvl+1, oldUnit.campaign.getEntities())); pw1.println(MekHqXmlUtil.indentStr(indentLvl + 1) + "<time>" + time + "</time>"); pw1.println(MekHqXmlUtil.indentStr(indentLvl + 1) + "<timeSpent>" + timeSpent + "</timeSpent>"); pw1.println(MekHqXmlUtil.indentStr(indentLvl + 1) + "<refitClass>" + refitClass + "</refitClass>"); pw1.println(MekHqXmlUtil.indentStr(indentLvl + 1) + "<cost>" + cost + "</cost>"); pw1.println(MekHqXmlUtil.indentStr(indentLvl + 1) + "<failedCheck>" + failedCheck + "</failedCheck>"); pw1.println(MekHqXmlUtil.indentStr(indentLvl + 1) + "<customJob>" + customJob + "</customJob>"); pw1.println(MekHqXmlUtil.indentStr(indentLvl + 1) + "<kitFound>" + kitFound + "</kitFound>"); pw1.println(MekHqXmlUtil.indentStr(indentLvl + 1) + "<isRefurbishing>" + isRefurbishing + "</isRefurbishing>"); pw1.println(MekHqXmlUtil.indentStr(indentLvl + 1) + "<armorNeeded>" + armorNeeded + "</armorNeeded>"); pw1.println(MekHqXmlUtil.indentStr(indentLvl + 1) + "<sameArmorType>" + sameArmorType + "</sameArmorType>"); if(null != assignedTechId) { pw1.println(MekHqXmlUtil.indentStr(indentLvl + 1) + "<assignedTechId>" + assignedTechId.toString() + "</assignedTechId>"); } pw1.println(MekHqXmlUtil.indentStr(indentLvl+1) +"<quantity>" +quantity +"</quantity>"); pw1.println(MekHqXmlUtil.indentStr(indentLvl+1) +"<daysToWait>" +daysToWait +"</daysToWait>"); pw1.println(MekHqXmlUtil.indentStr(indentLvl + 1) + "<oldUnitParts>"); for(int pid : oldUnitParts) { pw1.println(MekHqXmlUtil.indentStr(indentLvl + 2) + "<pid>" + pid + "</pid>"); } pw1.println(MekHqXmlUtil.indentStr(indentLvl + 1) + "</oldUnitParts>"); pw1.println(MekHqXmlUtil.indentStr(indentLvl + 1) + "<newUnitParts>"); for(int pid : newUnitParts) { pw1.println(MekHqXmlUtil.indentStr(indentLvl + 2) + "<pid>" + pid + "</pid>"); } pw1.println(MekHqXmlUtil.indentStr(indentLvl + 1) + "</newUnitParts>"); pw1.println(MekHqXmlUtil.indentStr(indentLvl + 1) + "<shoppingList>"); for(Part p : shoppingList) { p.writeToXml(pw1, indentLvl+2); } pw1.println(MekHqXmlUtil.indentStr(indentLvl + 1) + "</shoppingList>"); if(null != newArmorSupplies) { if(newArmorSupplies.getId() == 0) { pw1.println(MekHqXmlUtil.indentStr(indentLvl + 1) + "<newArmorSupplies>"); newArmorSupplies.writeToXml(pw1, indentLvl+2); pw1.println(MekHqXmlUtil.indentStr(indentLvl + 1) + "</newArmorSupplies>"); } else { pw1.println(MekHqXmlUtil.indentStr(indentLvl + 1) + "<newArmorSuppliesId>" + newArmorSupplies.getId() + "</newArmorSuppliesId>"); } } pw1.println(MekHqXmlUtil.indentStr(indentLvl) + "</refit>"); } public static Refit generateInstanceFromXML(Node wn, Unit u, Version version) { Refit retVal = new Refit(); retVal.oldUnit = u; NodeList nl = wn.getChildNodes(); try { for (int x=0; x<nl.getLength(); x++) { Node wn2 = nl.item(x); if (wn2.getNodeName().equalsIgnoreCase("time")) { retVal.time = Integer.parseInt(wn2.getTextContent()); } else if (wn2.getNodeName().equalsIgnoreCase("refitClass")) { retVal.refitClass = Integer.parseInt(wn2.getTextContent()); } else if (wn2.getNodeName().equalsIgnoreCase("timeSpent")) { retVal.timeSpent = Integer.parseInt(wn2.getTextContent()); } else if (wn2.getNodeName().equalsIgnoreCase("quantity")) { retVal.quantity = Integer.parseInt(wn2.getTextContent()); } else if (wn2.getNodeName().equalsIgnoreCase("daysToWait")) { retVal.daysToWait = Integer.parseInt(wn2.getTextContent()); } else if (wn2.getNodeName().equalsIgnoreCase("cost")) { retVal.cost = Long.parseLong(wn2.getTextContent()); } else if (wn2.getNodeName().equalsIgnoreCase("newArmorSuppliesId")) { retVal.newArmorSuppliesId = Integer.parseInt(wn2.getTextContent()); } else if (wn2.getNodeName().equalsIgnoreCase("assignedTechId")) { if(version.getMajorVersion() == 0 && version.getMinorVersion() < 2 && version.getSnapshot() < 14) { retVal.oldTechId = Integer.parseInt(wn2.getTextContent()); } else { retVal.assignedTechId = UUID.fromString(wn2.getTextContent()); } } else if (wn2.getNodeName().equalsIgnoreCase("failedCheck")) { if (wn2.getTextContent().equalsIgnoreCase("true")) retVal.failedCheck = true; else retVal.failedCheck = false; } else if (wn2.getNodeName().equalsIgnoreCase("customJob")) { if (wn2.getTextContent().equalsIgnoreCase("true")) retVal.customJob = true; else retVal.customJob = false; } else if (wn2.getNodeName().equalsIgnoreCase("kitFound")) { if (wn2.getTextContent().equalsIgnoreCase("true")) retVal.kitFound = true; else retVal.kitFound = false; } else if (wn2.getNodeName().equalsIgnoreCase("isRefurbishing")) { if (wn2.getTextContent().equalsIgnoreCase("true")) retVal.isRefurbishing = true; else retVal.isRefurbishing = false; } else if (wn2.getNodeName().equalsIgnoreCase("armorNeeded")) { retVal.armorNeeded = Integer.parseInt(wn2.getTextContent()); } else if (wn2.getNodeName().equalsIgnoreCase("sameArmorType")) { if (wn2.getTextContent().equalsIgnoreCase("true")) retVal.sameArmorType = true; else retVal.sameArmorType = false; } else if (wn2.getNodeName().equalsIgnoreCase("entity")) { retVal.newEntity = MekHqXmlUtil.getEntityFromXmlString(wn2); } else if (wn2.getNodeName().equalsIgnoreCase("oldUnitParts")) { NodeList nl2 = wn2.getChildNodes(); for (int y=0; y<nl2.getLength(); y++) { Node wn3 = nl2.item(y); if (wn3.getNodeName().equalsIgnoreCase("pid")) { retVal.oldUnitParts.add(Integer.parseInt(wn3.getTextContent())); } } } else if (wn2.getNodeName().equalsIgnoreCase("newUnitParts")) { NodeList nl2 = wn2.getChildNodes(); for (int y=0; y<nl2.getLength(); y++) { Node wn3 = nl2.item(y); if (wn3.getNodeName().equalsIgnoreCase("pid")) { retVal.newUnitParts.add(Integer.parseInt(wn3.getTextContent())); } } } else if (wn2.getNodeName().equalsIgnoreCase("shoppingList")) { processShoppingList(retVal, wn2, retVal.oldUnit, version); } else if (wn2.getNodeName().equalsIgnoreCase("newArmorSupplies")) { processArmorSupplies(retVal, wn2, version); } } } catch (Exception ex) { // Doh! MekHQ.logError(ex); } return retVal; } private static void processShoppingList(Refit retVal, Node wn, Unit u, Version version) { NodeList wList = wn.getChildNodes(); // Okay, lets iterate through the children, eh? for (int x = 0; x < wList.getLength(); x++) { Node wn2 = wList.item(x); // If it's not an element node, we ignore it. if (wn2.getNodeType() != Node.ELEMENT_NODE) continue; if (!wn2.getNodeName().equalsIgnoreCase("part")) { // Error condition of sorts! // Errr, what should we do here? MekHQ.logMessage("Unknown node type not loaded in Part nodes: "+wn2.getNodeName()); continue; } Part p = Part.generateInstanceFromXML(wn2, version); p.setUnit(u); if (p != null) { retVal.shoppingList.add(p); } } } private static void processArmorSupplies(Refit retVal, Node wn, Version version) { NodeList wList = wn.getChildNodes(); // Okay, lets iterate through the children, eh? for (int x = 0; x < wList.getLength(); x++) { Node wn2 = wList.item(x); // If it's not an element node, we ignore it. if (wn2.getNodeType() != Node.ELEMENT_NODE) continue; if (!wn2.getNodeName().equalsIgnoreCase("part")) { // Error condition of sorts! // Errr, what should we do here? MekHQ.logMessage("Unknown node type not loaded in Part nodes: "+wn2.getNodeName()); continue; } Part p = Part.generateInstanceFromXML(wn2, version); if (p != null && p instanceof Armor) { retVal.newArmorSupplies = (Armor)p; break; } } } public void reCalc() { setCampaign(oldUnit.campaign); for(Part p : shoppingList) { p.setCampaign(oldUnit.campaign); } if(null != newArmorSupplies) { newArmorSupplies.setCampaign(oldUnit.campaign); } } @Override public Part getNewEquipment() { // TODO Auto-generated method stub return this; } @Override public String getAcquisitionDesc() { return "Fill this in"; } @Override public String getAcquisitionDisplayName() { return null; } @Override public String getAcquisitionExtraDesc() { return null; } @Override public String getAcquisitionBonus() { return null; } @Override public Part getAcquisitionPart() { return null; } public long getStickerPrice() { return cost; } @Override public long getBuyCost() { return getStickerPrice(); } public void addRefitKitParts(int transitDays) { for(Part part : shoppingList) { if(part instanceof Armor) { //Taharqa: WE shouldn't be here anymore, given that I am no longer adding //armor by location to the shopping list but instead changing it all via //the newArmorSupplies object, but commented out for completeness //oldUnit.campaign.addPart(part, transitDays); //part.setUnit(oldUnit); //part.setRefitId(oldUnit.getId()); //newUnitParts.add(part.getId()); } else if(part instanceof AmmoBin) { part.setRefitId(oldUnit.getId()); oldUnit.campaign.addPart(part, 0); newUnitParts.add(part.getId()); AmmoBin bin = (AmmoBin)part; bin.setShotsNeeded(bin.getFullShots()); bin.loadBin(); if(bin.needsFixing()) { oldUnit.campaign.addPart(bin.getNewPart(), transitDays); bin.loadBin(); } } else if(part instanceof MissingPart) { Part newPart = (Part)((IAcquisitionWork)part).getNewEquipment(); newPart.setRefitId(oldUnit.getId()); oldUnit.campaign.addPart(newPart, transitDays); newUnitParts.add(newPart.getId()); } } if(null != newArmorSupplies) { int amount = armorNeeded - newArmorSupplies.getAmount(); if(amount > 0) { Armor a = (Armor)newArmorSupplies.getNewPart(); a.setAmount(amount); oldUnit.campaign.addPart(a, transitDays); } checkForArmorSupplies(); } shoppingList = new ArrayList<Part>(); kitFound = true; } @Override public String find(int transitDays) { if(campaign.buyPart(this, transitDays)) { return "<font color='green'><b> refit kit found.</b> Kit will arrive in " + transitDays + " days.</font>"; } else { return "<font color='red'><b> You cannot afford this refit kit. Transaction cancelled</b>.</font>"; } } @Override public String failToFind() { resetDaysToWait(); return "<font color='red'> refit kit not found.</font>"; } @Override public TargetRoll getAllAcquisitionMods() { TargetRoll roll = new TargetRoll(); int avail = EquipmentType.RATING_A; int techBaseMod = 0; for(Part part : shoppingList) { if(getTechBase() == T_CLAN && campaign.getCampaignOptions().getClanAcquisitionPenalty() > techBaseMod) { techBaseMod = campaign.getCampaignOptions().getClanAcquisitionPenalty(); } else if(getTechBase() == T_IS && campaign.getCampaignOptions().getIsAcquisitionPenalty() > techBaseMod) { techBaseMod = campaign.getCampaignOptions().getIsAcquisitionPenalty(); } else if(getTechBase() == T_BOTH) { int penalty = Math.min(campaign.getCampaignOptions().getClanAcquisitionPenalty(), campaign.getCampaignOptions().getIsAcquisitionPenalty()); if(penalty > techBaseMod) { techBaseMod = penalty; } } avail = Math.max(avail, part.getAvailability(campaign.getEra())); } if(techBaseMod > 0) { roll.addModifier(techBaseMod, "tech limit"); } int availabilityMod = Availability.getAvailabilityModifier(avail); roll.addModifier(availabilityMod, "availability (" + EquipmentType.getRatingName(avail) + ")"); return roll; } public Armor getNewArmorSupplies() { return newArmorSupplies; } public void setNewArmorSupplies(Armor a) { newArmorSupplies = a; } public int getNewArmorSuppliesId() { return newArmorSuppliesId; } @Override public void resetOvertime() { // TODO Auto-generated method stub } @Override public int getTechLevel() { // TODO Auto-generated method stub return 0; } @Override public int getTechBase() { return Part.T_BOTH; } public void fixIdReferences(Hashtable<Integer, UUID> uHash, Hashtable<Integer, UUID> pHash) { assignedTechId = pHash.get(oldTechId); if(null != newArmorSupplies) { newArmorSupplies.fixIdReferences(uHash, pHash); } for(Part p : shoppingList) { p.fixIdReferences(uHash, pHash); } } @Override public boolean isRightTechType(String skillType) { // TODO Auto-generated method stub return true; } public void suggestNewName() { if(newEntity instanceof Infantry && !(newEntity instanceof BattleArmor)) { Infantry infantry = (Infantry)newEntity; String chassis = "?"; switch (infantry.getMovementMode()) { case INF_UMU: chassis = "Scuba "; break; case INF_MOTORIZED: chassis = "Motorized "; break; case INF_JUMP: chassis = "Jump "; break; case HOVER: chassis = "Mechanized Hover "; break; case WHEELED: chassis = "Mechanized Wheeled "; break; case TRACKED: chassis = "Mechanized Tracked "; break; default: chassis = "Foot "; } if(infantry.isSquad()) { chassis += "Squad"; } else { chassis += "Platoon"; } newEntity.setChassis(chassis); String model = "?"; if(infantry.getSecondaryN() > 1 && null != infantry.getSecondaryWeapon()) { model = "(" + infantry.getSecondaryWeapon().getInternalName() + ")"; } else if(null != infantry.getPrimaryWeapon()) { model = "(" + infantry.getPrimaryWeapon().getInternalName() + ")"; } newEntity.setModel(model); } else { //newEntity.setModel(oldUnit.getEntity().getModel() + " Mk II"); } } private void assignArmActuators() { if(!(oldUnit.getEntity() instanceof BipedMech)) { return; } BipedMech m = (BipedMech)oldUnit.getEntity(); //we only need to worry about lower arm actuators and hands Part rightLowerArm = null; Part leftLowerArm = null; Part rightHand = null; Part leftHand = null; MekActuator missingHand1 = null; MekActuator missingHand2 = null; MekActuator missingArm1 = null; MekActuator missingArm2 = null; for(Part part : oldUnit.getParts()) { if(part instanceof MekActuator || part instanceof MissingMekActuator) { int type = -1; int loc = -1; if(part instanceof MekActuator) { type = ((MekActuator)part).getType(); loc = ((MekActuator)part).getLocation(); } else { type = ((MissingMekActuator)part).getType(); loc = ((MissingMekActuator)part).getLocation(); } if(type == Mech.ACTUATOR_LOWER_ARM) { if(loc == Mech.LOC_RARM) { rightLowerArm = part; } else if(loc == Mech.LOC_LARM) { leftLowerArm = part; } else if(null == missingArm1 && part instanceof MekActuator) { missingArm1 = (MekActuator)part; } else if(part instanceof MekActuator) { missingArm2 = (MekActuator)part; } } else if(type == Mech.ACTUATOR_HAND) { if(loc == Mech.LOC_RARM) { rightHand = part; } else if(loc == Mech.LOC_LARM) { leftHand = part; } else if(null == missingHand1 && part instanceof MekActuator) { missingHand1 = (MekActuator)part; } else if(part instanceof MekActuator) { missingHand2 = (MekActuator)part; } } } } //ok now check all the conditions, assign right hand stuff first if(null == rightHand && m.hasSystem(Mech.ACTUATOR_HAND, Mech.LOC_RARM)) { MekActuator part = missingHand1; if(null == part || part.getLocation() != Entity.LOC_NONE) { part = missingHand2; } if(null != part) { part.setLocation(Mech.LOC_RARM); } } if(null == leftHand && m.hasSystem(Mech.ACTUATOR_HAND, Mech.LOC_LARM)) { MekActuator part = missingHand1; if(null == part || part.getLocation() != Entity.LOC_NONE) { part = missingHand2; } if(null != part) { part.setLocation(Mech.LOC_LARM); } } if(null == rightLowerArm && m.hasSystem(Mech.ACTUATOR_LOWER_ARM, Mech.LOC_RARM)) { MekActuator part = missingArm1; if(null == part || part.getLocation() != Entity.LOC_NONE) { part = missingArm2; } if(null != part) { part.setLocation(Mech.LOC_RARM); } } if(null == leftLowerArm && m.hasSystem(Mech.ACTUATOR_LOWER_ARM, Mech.LOC_LARM)) { MekActuator part = missingArm1; if(null == part || part.getLocation() != Entity.LOC_NONE) { part = missingArm2; } if(null != part) { part.setLocation(Mech.LOC_LARM); } } } @Override public String getShoppingListReport(int quantity) { return getAcquisitionName() + " has been added to the procurement list."; } @Override public double getTonnage() { // TODO Auto-generated method stub return 0; } @Override public int getTechRating() { // TODO Auto-generated method stub return 0; } @Override public int getAvailability(int era) { // TODO Auto-generated method stub return 0; } @Override public boolean isSamePartType(Part part) { // TODO Auto-generated method stub return false; } @Override protected void loadFieldsFromXmlNode(Node wn) { // TODO Auto-generated method stub } @Override public Part clone() { // TODO Auto-generated method stub return null; } public boolean isCustomJob() { return customJob; } public boolean kitFound() { return kitFound; } @Override public String getLocationName() { // TODO Auto-generated method stub return null; } @Override public int getLocation() { return Entity.LOC_NONE; } @Override public int getIntroDate() { return EquipmentType.DATE_NONE; } @Override public int getExtinctDate() { return EquipmentType.DATE_NONE; } @Override public int getReIntroDate() { return EquipmentType.DATE_NONE; } public boolean isBeingRefurbished() { return isRefurbishing; } }