/*
* AmmoStorage.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.PrintWriter;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import megamek.common.AmmoType;
import megamek.common.BombType;
import megamek.common.Entity;
import megamek.common.EquipmentType;
import megamek.common.Mounted;
import megamek.common.TargetRoll;
import mekhq.MekHqXmlUtil;
import mekhq.Utilities;
import mekhq.campaign.Campaign;
import mekhq.campaign.parts.equipment.EquipmentPart;
import mekhq.campaign.personnel.Person;
import mekhq.campaign.universe.Era;
import mekhq.campaign.work.IAcquisitionWork;
/**
* This will be a special type of part that will only exist as spares
* It will determine the amount of ammo of a particular type that
* is available
* @author Jay Lawson <jaylawson39 at yahoo.com>
*/
public class AmmoStorage extends EquipmentPart implements IAcquisitionWork {
private static final long serialVersionUID = 2892728320891712304L;
protected long munition;
protected int shots;
public AmmoStorage() {
this(0, null, 0, null);
}
public AmmoStorage(int tonnage, EquipmentType et, int shots, Campaign c) {
super(tonnage, et, -1, c);
this.shots = shots;
if(null != type && type instanceof AmmoType) {
this.munition = ((AmmoType)type).getMunitionType();
}
}
public AmmoStorage clone() {
AmmoStorage storage = new AmmoStorage(0, getType(), shots, campaign);
storage.copyBaseData(this);
storage.munition = this.munition;
return storage;
}
@Override
public double getTonnage() {
if(((AmmoType)type).getKgPerShot() > 0) {
return ((AmmoType)type).getKgPerShot() * shots/1000.0;
}
return ((double)shots / ((AmmoType)type).getShots());
}
@Override
public long getStickerPrice() {
//costs are a total nightmare
//some costs depend on entity, but we can't do it that way
//because spare parts don't have entities. If parts start on an entity
//thats fine, but this will become problematic when we set up a parts
//store. For now I am just going to pass in a null entity and attempt
//to catch any resulting NPEs
Entity en = null;
boolean isArmored = false;
if (unit != null) {
en = unit.getEntity();
Mounted mounted = unit.getEntity().getEquipment(equipmentNum);
if(null != mounted) {
isArmored = mounted.isArmored();
}
}
int itemCost = 0;
try {
itemCost = (int) type.getCost(en, isArmored, -1);
} catch(NullPointerException ex) {
System.out.println("Found a null entity while calculating cost for " + name);
}
return itemCost;
}
@Override
public long getBuyCost() {
return getStickerPrice();
}
@Override
public long getCurrentValue() {
return (long)(getStickerPrice() * ((double)shots / ((AmmoType)type).getShots()));
}
public int getShots() {
return shots;
}
@Override
public boolean isSamePartType(Part part) {
if (part instanceof AmmoStorage) {
if (getType() instanceof BombType) {
return ((EquipmentPart)part).getType() instanceof BombType
&& ((BombType)getType()).getBombType() == ((BombType)((EquipmentPart)part).getType()).getBombType();
} else {
return ((AmmoType)getType()).getMunitionType() == ((AmmoType)((AmmoStorage)part).getType()).getMunitionType()
&& ((AmmoType)getType()).equals( (Object)((EquipmentPart)part).getType());
}
}
return false;
}
public void changeShots(int s) {
shots = Math.max(0, shots + s);
}
@Override
public void writeToXml(PrintWriter pw1, int indent) {
writeToXmlBegin(pw1, indent);
pw1.println(MekHqXmlUtil.indentStr(indent+1)
+"<equipmentNum>"
+equipmentNum
+"</equipmentNum>");
pw1.println(MekHqXmlUtil.indentStr(indent+1)
+"<typeName>"
+MekHqXmlUtil.escape(typeName)
+"</typeName>");
pw1.println(MekHqXmlUtil.indentStr(indent+1)
+"<munition>"
+munition
+"</munition>");
pw1.println(MekHqXmlUtil.indentStr(indent+1)
+"<shots>"
+shots
+"</shots>");
writeToXmlEnd(pw1, indent);
}
@Override
protected void loadFieldsFromXmlNode(Node wn) {
NodeList nl = wn.getChildNodes();
for (int x=0; x<nl.getLength(); x++) {
Node wn2 = nl.item(x);
if (wn2.getNodeName().equalsIgnoreCase("equipmentNum")) {
equipmentNum = Integer.parseInt(wn2.getTextContent());
} else if (wn2.getNodeName().equalsIgnoreCase("typeName")) {
typeName = wn2.getTextContent();
} else if (wn2.getNodeName().equalsIgnoreCase("munition")) {
munition = Long.parseLong(wn2.getTextContent());
} else if (wn2.getNodeName().equalsIgnoreCase("shots")) {
shots = Integer.parseInt(wn2.getTextContent());
}
}
restore();
}
@Override
public int getAvailability(int era) {
return type.getAvailability(Era.convertEra(era));
}
@Override
public int getTechRating() {
return type.getTechRating();
}
//@Override
//public int getTechBase() {
//return T_BOTH;
//}
@Override
public void fix() {
//nothing to fix
return;
}
@Override
public MissingPart getMissingPart() {
//nothing to do here
return null;
}
@Override
public IAcquisitionWork getAcquisitionWork() {
return new AmmoStorage(1,type,((AmmoType)type).getShots(),campaign);
}
@Override
public TargetRoll getAllMods(Person tech) {
//nothing to do here
return null;
}
@Override
public void updateConditionFromEntity(boolean checkForDestruction) {
//nothing to do here
return;
}
@Override
public void updateConditionFromPart() {
//nothing to do here
return;
}
@Override
public boolean needsFixing() {
return false;
}
public String getDesc() {
String toReturn = "<html><font size='2'";
String scheduled = "";
if (getTeamId() != null) {
scheduled = " (scheduled) ";
}
toReturn += ">";
toReturn += "<b>Reload " + getName() + "</b><br/>";
toReturn += getDetails() + "<br/>";
toReturn += "" + getTimeLeft() + " minutes" + scheduled;
toReturn += "</font></html>";
return toReturn;
}
@Override
public String getDetails() {
return shots + " shots";
}
@Override
public String checkFixable() {
return null;
}
@Override
public String find(int transitDays) {
Part newPart = getNewPart();
newPart.setBrandNew(true);
if(campaign.buyPart(newPart, transitDays)) {
return "<font color='green'><b> part found</b>.</font> It will be delivered in " + transitDays + " days.";
} else {
return "<font color='red'><b> You cannot afford this part. Transaction cancelled</b>.</font>";
}
}
@Override
public Object getNewEquipment() {
return getNewPart();
}
@Override
public String failToFind() {
resetDaysToWait();
return "<font color='red'><b> part not found</b>.</font>";
}
public void changeAmountAvailable(int amount, AmmoType curType) {
AmmoStorage a = null;
long curMunition = curType.getMunitionType();
for(Part part : campaign.getSpareParts()) {
if(!part.isPresent()) {
continue;
}
if(part instanceof AmmoStorage
&& ((AmmoType)((AmmoStorage)part).getType()).equals((Object)curType)
&& curMunition == ((AmmoType)((AmmoStorage)part).getType()).getMunitionType()) {
a = (AmmoStorage)part;
a.changeShots(amount);
break;
}
}
if(null != a && a.getShots() <= 0) {
campaign.removePart(a);
} else if(null == a && amount > 0) {
campaign.addPart(new AmmoStorage(1,curType,amount,campaign), 0);
}
}
@Override
public String getAcquisitionDesc() {
String toReturn = "<html><font size='2'";
toReturn += ">";
toReturn += "<b>" + getAcquisitionDisplayName() + "</b> " + getAcquisitionBonus() + "<br/>";
toReturn += getAcquisitionExtraDesc() + "<br/>";
String[] inventories = campaign.getPartInventory(getAcquisitionPart());
toReturn += inventories[1] + " in transit, " + inventories[2] + " on order<br>";
toReturn += Utilities.getCurrencyString(getStickerPrice()) + "<br/>";
toReturn += "</font></html>";
return toReturn;
}
@Override
public String getAcquisitionDisplayName() {
return type.getDesc();
}
@Override
public String getAcquisitionExtraDesc() {
return ((AmmoType)type).getShots() + " shots (1 ton)";
}
@Override
public String getAcquisitionName() {
return type.getDesc();
}
@Override
public String getAcquisitionBonus() {
String bonus = getAllAcquisitionMods().getValueAsString();
if(getAllAcquisitionMods().getValue() > -1) {
bonus = "+" + bonus;
}
return "(" + bonus + ")";
}
@Override
public Part getAcquisitionPart() {
return getNewPart();
}
@Override
public TargetRoll getAllAcquisitionMods() {
TargetRoll target = new TargetRoll();
// Faction and Tech mod
if(isClanTechBase() && campaign.getCampaignOptions().getClanAcquisitionPenalty() > 0) {
target.addModifier(campaign.getCampaignOptions().getClanAcquisitionPenalty(), "clan-tech");
}
else if(campaign.getCampaignOptions().getIsAcquisitionPenalty() > 0) {
target.addModifier(campaign.getCampaignOptions().getIsAcquisitionPenalty(), "Inner Sphere tech");
}
//availability mod
int avail = getAvailability(campaign.getEra());
int availabilityMod = Availability.getAvailabilityModifier(avail);
target.addModifier(availabilityMod, "availability (" + EquipmentType.getRatingName(avail) + ")");
return target;
}
public Part getNewPart() {
return new AmmoStorage(1,type,shots,campaign);
}
@Override
public String getQuantityName(int quan) {
int totalShots = quan * getShots();
String report = "" + totalShots + " shots of " + getName();
if(totalShots == 1) {
report = "" + totalShots + " shot of " + getName();
}
return report;
}
@Override
public String getArrivalReport() {
double totalShots = quantity * getShots();
String report = getQuantityName(quantity);
if(totalShots == 1) {
report += " has arrived";
} else {
report += " have arrived";
}
return report;
}
@Override
public boolean needsMaintenance() {
return true;
}
@Override
public boolean isPriceAdustedForAmount() {
return true;
}
}