/*
* Skill.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.personnel;
import java.io.PrintWriter;
import java.io.Serializable;
import megamek.common.Compute;
import mekhq.MekHQ;
import mekhq.MekHqXmlSerializable;
import mekhq.MekHqXmlUtil;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* As ov v0.1.9, we will be tracking a group of skills on the person. These skills will define
* personnel rather than subtypes wrapped around pilots and teams. This will allow for considerably
* more flexibility in the kinds of personnel available.
*
* Four important characteristics will determine how each skill works
* level - this is the level of the skill. By default this will go from 0 to 10, but the max will
* be customizable. These won't necessarily correspond to named levels (e.g. Green, Elite)
* By assigning skill costs of 0 to some levels, these can basically be skipped and by
* assigning skill costs of -1, they can be made inaccessible.
* bonus - this is a bonus that the given person has for this skill which is separable from level.
* Primarily this allows for rpg-style attribute bonuses to come into play.
* target - this is the baseline target number for the skill when level and bonus are zero.
* countUp - this is a boolean that defines whether this skill's target is a btech style
* "roll greater than or equal to" (false) or an rpg-style bonus to a roll (true)
* The actual target number for a skill is given by
* countUp: target+lvl+bonus
* !countUp: target - level - bonus
* by clever manipulation of these values and skillcosts in campaignOptions, players should be
* able to recreate any of the rpg versions or their own homebrew system. The default setup
* will follow the core rulebooks (not aToW).
* @author Jay Lawson <jaylawson39 at yahoo.com>
*/
public class Skill implements Serializable, MekHqXmlSerializable {
private static final long serialVersionUID = 2470620816562038469L;
private SkillType type;
private int level;
private int bonus;
public Skill() {
}
public Skill(String t) {
this(t, SkillType.EXP_REGULAR, false, 0);
}
public Skill(String t, int exp, boolean random, int bonus) {
this.type = SkillType.getType(t);
this.level = type.getLevelFromExperience(exp);
//check to see if we should randomize
if(random) {
int roll = Compute.d6();
if(roll < 2 && level > 0) {
this.level--;
}
else if(roll > 5 && level < 10) {
this.level++;
}
}
this.bonus = bonus;
}
public Skill(String t, int lvl, int bns) {
this.type = SkillType.getType(t);
this.level = lvl;
this.bonus = bns;
}
public int getLevel() {
return level;
}
public void setLevel(int l) {
this.level = l;
}
public int getBonus() {
return bonus;
}
public void setBonus(int b) {
this.bonus = b;
}
public SkillType getType() {
return type;
}
public int getFinalSkillValue() {
if(type.countUp()) {
return type.getTarget() + level + bonus;
} else {
return type.getTarget() - level - bonus;
}
}
public void improve() {
if(level >= SkillType.NUM_LEVELS - 1) {
// Can't improve past the max
return;
}
level = level + 1;
//if the cost for the next level is zero (or less than zero), then
//keep improve until you hit a non-zero cost
if(type.getCost(level) <= 0) {
improve();
}
}
public int getCostToImprove() {
int cost = 0;
int i = 1;
while(cost <= 0 && (level+i) < SkillType.NUM_LEVELS) {
cost = type.getCost(level+i);
++i;
}
return cost;
}
public int getExperienceLevel() {
return type.getExperienceLevel(getLevel());
}
@Override
public String toString() {
if(type.countUp()) {
return "+" + getFinalSkillValue();
} else {
return getFinalSkillValue() + "+";
}
}
public void writeToXml(PrintWriter pw1, int indent) {
pw1.println(MekHqXmlUtil.indentStr(indent) + "<skill>");
pw1.println(MekHqXmlUtil.indentStr(indent+1)
+"<type>"
+type.getName()
+"</type>");
pw1.println(MekHqXmlUtil.indentStr(indent+1)
+"<level>"
+level
+"</level>");
pw1.println(MekHqXmlUtil.indentStr(indent+1)
+"<bonus>"
+bonus
+"</bonus>");
pw1.println(MekHqXmlUtil.indentStr(indent) + "</skill>");
}
public static Skill generateInstanceFromXML(Node wn) {
Skill retVal = null;
try {
retVal = new Skill();
// Okay, now load Skill-specific fields!
NodeList nl = wn.getChildNodes();
for (int x=0; x<nl.getLength(); x++) {
Node wn2 = nl.item(x);
if (wn2.getNodeName().equalsIgnoreCase("type")) {
retVal.type = SkillType.getType(wn2.getTextContent());
} else if (wn2.getNodeName().equalsIgnoreCase("level")) {
retVal.level = Integer.parseInt(wn2.getTextContent());
} else if (wn2.getNodeName().equalsIgnoreCase("bonus")) {
retVal.bonus = Integer.parseInt(wn2.getTextContent());
}
}
} catch (Exception ex) {
// Errrr, apparently either the class name was invalid...
// Or the listed name doesn't exist.
// Doh!
MekHQ.logError(ex);
}
return retVal;
}
public void updateType() {
type = SkillType.getType(type.getName());
}
}