/*
* CurrentLocation.java
*
* Copyright (c) 2011 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;
import java.io.PrintWriter;
import java.io.Serializable;
import java.util.Date;
import java.util.Locale;
import org.joda.time.DateTime;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import mekhq.MekHQ;
import mekhq.MekHqXmlUtil;
import mekhq.Utilities;
import mekhq.campaign.finances.Transaction;
import mekhq.campaign.universe.Planet;
/**
* This keeps track of a location, which includes both the planet
* and the current position in-system. It may seem a little like
* overkill to have a separate object here, but when we reach a point
* where we want to let a force be in different locations, this will
* make it easier to keep track of everything
*
* @author Jay Lawson <jaylawson39 at yahoo.com>
*/
public class CurrentLocation implements Serializable {
/**
*
*/
private static final long serialVersionUID = -4337642922571022697L;
private Planet currentPlanet;
//keep track of jump path
private JumpPath jumpPath;
private double rechargeTime;
//I would like to keep track of distance, but I ain't too good with fyziks
private double transitTime;
public CurrentLocation() {
this(null,0);
}
public CurrentLocation(Planet planet, double time) {
this.currentPlanet = planet;
this.transitTime = time;
this.rechargeTime = 0.0;
this.transitTime = 0.0;
}
public void setCurrentPlanet(Planet p) {
currentPlanet = p;
}
public void setTransitTime(double time) {
transitTime = time;
}
public boolean isOnPlanet() {
return transitTime <= 0;
}
public boolean isAtJumpPoint() {
return transitTime >= currentPlanet.getTimeToJumpPoint(1.0);
}
public boolean isInTransit() {
return !isOnPlanet() && !isAtJumpPoint();
}
public Planet getCurrentPlanet() {
return currentPlanet;
}
public double getTransitTime() {
return transitTime;
}
public String getReport(Date date) {
DateTime now = Utilities.getDateTimeDay(date);
StringBuilder sb = new StringBuilder();
sb.append("<html><b>Current Location</b><br>");
sb.append(currentPlanet.getPrintableName(now)).append("<br>");
if(null != jumpPath && !jumpPath.isEmpty()) {
sb.append("In transit to ").append(jumpPath.getLastPlanet().getPrintableName(now)).append(" ");
}
if(isOnPlanet()) {
sb.append("<i>on planet</i>");
}
else if(isAtJumpPoint()) {
sb.append("<i>at jump point</i>");
} else {
sb.append("<i>").append(String.format(Locale.ROOT, "%.2f", getTransitTime())).append(" days out </i>");
}
if(!Double.isInfinite(currentPlanet.getRechargeTime(now))) {
sb.append(", <i>").append(String.format(Locale.ROOT, "%.0f", 100.0 * rechargeTime/currentPlanet.getRechargeTime(now))).append("% charged </i>");
}
return sb.append("</html>").toString();
}
public JumpPath getJumpPath() {
return jumpPath;
}
public void setJumpPath(JumpPath path) {
jumpPath = path;
}
/**
* Check for a jump path and if found, do whatever needs to be done to move
* forward
*/
public void newDay(Campaign campaign) {
//recharge even if there is no jump path
//because jumpships don't go anywhere
DateTime currentDate = Utilities.getDateTimeDay(campaign.getCalendar());
double hours = 24.0;
double neededRechargeTime = currentPlanet.getRechargeTime(currentDate);
double usedRechargeTime = Math.min(hours, neededRechargeTime - rechargeTime);
if(usedRechargeTime > 0) {
campaign.addReport("Jumpships spent " + Math.round(100.0 * usedRechargeTime)/100.0 + " hours recharging drives");
rechargeTime += usedRechargeTime;
if(rechargeTime >= neededRechargeTime) {
campaign.addReport("Jumpship drives full charged");
}
}
if(null == jumpPath || jumpPath.isEmpty()) {
return;
}
//if we are not at the final jump point, then check to see if we are transiting
//or if we can jump
if(jumpPath.size() > 1) {
//first check to see if we are transiting
double usedTransitTime = Math.min(hours, 24.0 * (currentPlanet.getTimeToJumpPoint(1.0) - transitTime));
if(usedTransitTime > 0) {
transitTime += usedTransitTime/24.0;
campaign.addReport("Dropships spent " + Math.round(100.0 * usedTransitTime)/100.0 + " hours in transit to jump point");
if(isAtJumpPoint()) {
campaign.addReport("Jump point reached");
}
}
if(isAtJumpPoint() && rechargeTime >= neededRechargeTime) {
//jump
if(campaign.getCampaignOptions().payForTransport()) {
if(!campaign.getFinances().debit(campaign.calculateCostPerJump(true), Transaction.C_TRANSPORT, "jump from " + currentPlanet.getName(currentDate) + " to " + jumpPath.get(1).getName(currentDate), campaign.getCalendar().getTime())) {
campaign.addReport("<font color='red'><b>You cannot afford to make the jump!</b></font>");
return;
}
}
campaign.addReport("Jumping to " + jumpPath.get(1).getPrintableName(currentDate));
currentPlanet = jumpPath.get(1);
jumpPath.removeFirstPlanet();
//reduce remaining hours by usedRechargeTime or usedTransitTime, whichever is greater
hours -= Math.max(usedRechargeTime, usedTransitTime);
transitTime = currentPlanet.getTimeToJumpPoint(1.0);
rechargeTime = 0;
//if there are hours remaining, then begin recharging jump drive
usedRechargeTime = Math.min(hours, neededRechargeTime - rechargeTime);
if(usedRechargeTime > 0) {
campaign.addReport("Jumpships spent " + Math.round(100.0 * usedRechargeTime)/100.0 + " hours recharging drives");
rechargeTime += usedRechargeTime;
if(rechargeTime >= neededRechargeTime) {
campaign.addReport("Jumpship drives full charged");
}
}
}
}
//if we are now at the final jump point, then lets begin in-system transit
if(jumpPath.size() == 1) {
double usedTransitTime = Math.min(hours, 24.0 * transitTime);
campaign.addReport("Dropships spent " + Math.round(100.0 * usedTransitTime)/100.0 + " hours transiting into system");
transitTime -= usedTransitTime/24.0;
if(transitTime <= 0) {
campaign.addReport(jumpPath.getLastPlanet().getPrintableName(currentDate) + " reached.");
//we are here!
transitTime = 0;
jumpPath = null;
}
}
}
public void writeToXml(PrintWriter pw1, int indent) {
pw1.println(MekHqXmlUtil.indentStr(indent) + "<location>");
pw1.println(MekHqXmlUtil.indentStr(indent+1)
+"<currentPlanetName>"
+MekHqXmlUtil.escape(currentPlanet.getId())
+"</currentPlanetName>");
pw1.println(MekHqXmlUtil.indentStr(indent+1)
+"<transitTime>"
+transitTime
+"</transitTime>");
pw1.println(MekHqXmlUtil.indentStr(indent+1)
+"<rechargeTime>"
+rechargeTime
+"</rechargeTime>");
if(null != jumpPath) {
jumpPath.writeToXml(pw1, indent+1);
}
pw1.println(MekHqXmlUtil.indentStr(indent) + "</location>");
}
public static CurrentLocation generateInstanceFromXML(Node wn, Campaign c) {
CurrentLocation retVal = null;
try {
retVal = new CurrentLocation();
NodeList nl = wn.getChildNodes();
for (int x=0; x<nl.getLength(); x++) {
Node wn2 = nl.item(x);
if (wn2.getNodeName().equalsIgnoreCase("currentPlanetName")) {
Planet p = c.getPlanet(wn2.getTextContent());
if(null == p) {
//whoops we cant find your planet man, back to Earth
MekHQ.logError("Couldn't find planet named " + wn2.getTextContent());
p = c.getPlanet("Terra");
if(null == p) {
//if that doesnt work then give the first planet we have
p = c.getPlanets().get(0);
}
}
retVal.currentPlanet = p;
} else if (wn2.getNodeName().equalsIgnoreCase("transitTime")) {
retVal.transitTime = Double.parseDouble(wn2.getTextContent());
} else if (wn2.getNodeName().equalsIgnoreCase("rechargeTime")) {
retVal.rechargeTime = Double.parseDouble(wn2.getTextContent());
} else if (wn2.getNodeName().equalsIgnoreCase("jumpPath")) {
retVal.jumpPath = JumpPath.generateInstanceFromXML(wn2, c);
}
}
} catch (Exception ex) {
// Errrr, apparently either the class name was invalid...
// Or the listed name doesn't exist.
// Doh!
MekHQ.logError(ex);
}
return retVal;
}
}