/* Copyright (c) 2008-2010, developers of the Ascension Log Visualizer
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package com.googlecode.logVisualizer.logData.logSummary;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.googlecode.logVisualizer.logData.Item;
import com.googlecode.logVisualizer.logData.LogDataHolder;
import com.googlecode.logVisualizer.logData.LogDataHolder.CharacterClass;
import com.googlecode.logVisualizer.logData.LogDataHolder.ParsedLogClass;
import com.googlecode.logVisualizer.logData.MPGain;
import com.googlecode.logVisualizer.logData.Skill;
import com.googlecode.logVisualizer.logData.Statgain;
import com.googlecode.logVisualizer.logData.consumables.Consumable;
import com.googlecode.logVisualizer.logData.turn.SingleTurn;
import com.googlecode.logVisualizer.logData.turn.SingleTurn.TurnVersion;
import com.googlecode.logVisualizer.logData.turn.TurnInterval;
import com.googlecode.logVisualizer.logData.turn.TurnInterval.FreeRunaways;
import com.googlecode.logVisualizer.logData.turn.turnAction.FamiliarChange;
import com.googlecode.logVisualizer.logData.turn.turnAction.PlayerSnapshot;
import com.googlecode.logVisualizer.util.CountableSet;
import com.googlecode.logVisualizer.util.DataCounter;
import com.googlecode.logVisualizer.util.DataNumberPair;
import com.googlecode.logVisualizer.util.DataTablesHandler;
import com.googlecode.logVisualizer.util.LookAheadIterator;
/**
* A calculator for various summaries of an ascension log. This class makes use
* of pretty much all features of the {@link TurnInterval} class, thus if not
* all possibilities of the {@link TurnInterval} class are used, because for
* example the source data from which the turn interval was created didn't
* contain the information, this calculator might not be able to create some of
* its summaries. These cases should be pretty obvious though. If for example
* the turn interval does not contain a record of every single turn, it cannot
* make calculations which are based on such data.
* <p>
* Note that this class is immutable, while some of its members may be mutable.
* This has to be taken into account while using this class.
*/
final class SummaryDataCalculator {
private static final Skill OLFACTION = new Skill("transcendent olfaction",
1);
private static final Consumable ODOR_EXTRACTOR = Consumable
.newOtherConsumable("Odor Extractor", 0, 1);
private static final String GUILD_CHALLENGE = "Guild Challenge";
private static final String ENCHANTED_BARBELL = "enchanted barbell";
private static final String CONCENTRATED_MAGICALNESS_PILL = "concentrated magicalness pill";
private static final String GIANT_MOXIE_WEED = "giant moxie weed";
private static final Map<Integer, Integer> LEVEL_STAT_BOARDERS_MAP;
static {
LEVEL_STAT_BOARDERS_MAP = new HashMap<>(45);
// Sets the stat boarders from level 1 to 35.
SummaryDataCalculator.LEVEL_STAT_BOARDERS_MAP.put(1, 0);
for (int i = 2; i <= 35; i++) {
SummaryDataCalculator.LEVEL_STAT_BOARDERS_MAP.put(i,
((i - 1) * (i - 1)) + 4);
}
// Day number has to be set to the same value as is used by the internal
// mafia parser, otherwise this object won't match with the Odor
// Extractors logged by the aforementioned parser.
SummaryDataCalculator.ODOR_EXTRACTOR
.setDayNumberOfUsage(Integer.MAX_VALUE);
}
private final CountableSet<Consumable> consumablesUsed = new CountableSet<>();
private final CountableSet<Item> droppedItems = new CountableSet<>();
private final CountableSet<Skill> skillsCast = new CountableSet<>();
private final DataCounter<String> turnsPerArea = new DataCounter<>(
200);
private final DataCounter<String> familiarUsage = new DataCounter<>();
private final List<LevelData> levels = new ArrayList<>(15);
private final List<DataNumberPair<String>> huntedCombats = new ArrayList<>();
private final List<DataNumberPair<String>> disintegratedCombats = new ArrayList<>();
private final List<DataNumberPair<String>> semirares = new ArrayList<>();
private final List<DataNumberPair<String>> badmoonAdventures = new ArrayList<>();
private final ConsumptionSummary consumptionSummary;
private final FreeRunaways freeRunaways;
private final Sewer sewer = new Sewer();
private final Goatlet goatlet = new Goatlet();
private final InexplicableDoor nesRealm = new InexplicableDoor();
private final QuestTurncounts questTurncounts;
private Statgain totalStatgains = new Statgain();
private Statgain combatsStatgains = new Statgain();
private Statgain noncombatsStatgains = new Statgain();
private Statgain othersStatgains = new Statgain();
private final MPGain mpGains = new MPGain();
private final MeatSummary meatSummary = new MeatSummary();
private final int totalAmountSkillCasts;
private final int totalMPUsed;
private final int totalMeatGain;
private final int totalMeatSpent;
private final int totalTurnsFromRollover;
private final int totalTurnsCombat;
private final int totalTurnsNoncombat;
private final int totalTurnsOther;
SummaryDataCalculator(final LogDataHolder logData) {
if (logData == null) {
throw new NullPointerException("Log data holder must not be null.");
}
final List<Consumable> consumables = new ArrayList<>(100);
// Objects needed for familiar usage summary.
final LookAheadIterator<FamiliarChange> index = new LookAheadIterator<>(
logData.getFamiliarChanges().iterator());
FamiliarChange currentFamiliar = index.hasNext() ? index.next() : null;
int totalFreeRunawaysTries = 0;
int successfulFreeRunaways = 0;
int totalTurnsCombat = 0;
int totalTurnsNoncombat = 0;
int totalTurnsOther = 0;
int totalMeatGain = 0;
int totalMeatSpent = 0;
for (final TurnInterval ti : logData.getTurnsSpent()) {
// Consumables summary, day of usage is only a hindrance here.
for (final Consumable c : ti.getConsumablesUsed()) {
this.totalStatgains = this.totalStatgains.addStats(c
.getStatGain());
final Consumable tmp = c.newInstance();
tmp.setDayNumberOfUsage(Integer.MAX_VALUE);
this.consumablesUsed.addElement(tmp);
}
consumables.addAll(ti.getConsumablesUsed());
// Item summary
for (final Item i : ti.getDroppedItems()) {
this.droppedItems.addElement(i);
}
// Skill summary
for (final Skill s : ti.getSkillsCast()) {
this.skillsCast.addElement(s);
}
// MP summary
this.mpGains.addMPGains(ti.getMPGain());
// Turns per area summary
if (ti.getTotalTurns() > 0) {
this.turnsPerArea.addDataElement(ti.getAreaName(),
ti.getTotalTurns());
}
for (final SingleTurn st : ti.getTurns()) {
// Total turncounts and stats of different turn versions.
this.totalStatgains = this.totalStatgains.addStats(st
.getStatGain());
switch (st.getTurnVersion()) {
case COMBAT:
totalTurnsCombat++;
this.combatsStatgains = this.combatsStatgains.addStats(st
.getStatGain());
break;
case NONCOMBAT:
totalTurnsNoncombat++;
this.noncombatsStatgains = this.noncombatsStatgains
.addStats(st.getStatGain());
break;
case OTHER:
totalTurnsOther++;
this.othersStatgains = this.othersStatgains.addStats(st
.getStatGain());
break;
default:
break;
}
// Familiar usage summary
if ((st.getTurnVersion() == TurnVersion.COMBAT)
&& (currentFamiliar != null)) {
while (index.hasNext()
&& (st.getTurnNumber() > index.peek()
.getTurnNumber())) {
currentFamiliar = index.next();
}
if (currentFamiliar != null) {
this.familiarUsage.addDataElement(currentFamiliar
.getFamiliarName());
}
}
// Hunted combats summary
if ((st.getTurnVersion() == TurnVersion.COMBAT)
&& (st.isSkillCast(SummaryDataCalculator.OLFACTION) || st
.isConsumableUsed(SummaryDataCalculator.ODOR_EXTRACTOR))) {
this.huntedCombats.add(DataNumberPair.of(
st.getEncounterName(), st.getTurnNumber()));
}
// Disintegrated combats summary
if (st.isDisintegrated()) {
this.disintegratedCombats.add(DataNumberPair.of(
st.getEncounterName(), st.getTurnNumber()));
}
// Semirare summary
if (DataTablesHandler.isSemirareEncounter(st)) {
this.semirares.add(DataNumberPair.of(st.getEncounterName(),
st.getTurnNumber()));
}
// Bad Moon summary
if (DataTablesHandler.isBadMoonEncounter(st)) {
this.badmoonAdventures.add(DataNumberPair.of(
st.getEncounterName(), st.getTurnNumber()));
}
}
// Free runaways summary
totalFreeRunawaysTries += ti.getFreeRunaways()
.getNumberOfAttemptedRunaways();
successfulFreeRunaways += ti.getFreeRunaways()
.getNumberOfSuccessfulRunaways();
// Sewer summary
if (ti.getAreaName().equals("Unlucky Sewer")
|| ti.getAreaName().equals("Sewer With Clovers")) {
this.sewer.setTurnsSpent(this.sewer.getTurnsSpent()
+ ti.getTotalTurns());
// If there is a single turn list, look through it for the turn
// numbers.
if (!ti.getTurns().isEmpty()) {
for (final SingleTurn st : ti.getTurns()) {
for (final Item i : st.getDroppedItems()) {
if (i.getName().startsWith("worthless")) {
this.sewer.setTrinketsFound(this.sewer
.getTrinketsFound() + 1);
this.sewer.addTrinketsTurnNumber(st
.getTurnNumber());
}
}
}
} else {
for (final Item i : ti.getDroppedItems()) {
if (i.getName().startsWith("worthless")) {
this.sewer.setTrinketsFound(this.sewer
.getTrinketsFound() + i.getAmount());
// Not really correct but has to do for now.
this.sewer.addTrinketsTurnNumber(ti.getEndTurn());
}
}
}
}
// Goatlet summary
if (ti.getAreaName().equals("Goatlet")) {
this.goatlet.setTurnsSpent(this.goatlet.getTurnsSpent()
+ ti.getTotalTurns());
for (final SingleTurn st : ti.getTurns()) {
if (st.getEncounterName().equals("dairy goat")) {
this.goatlet.setDairyGoatsFound(this.goatlet
.getDairyGoatsFound() + 1);
}
}
for (final Item i : ti.getDroppedItems()) {
if (i.getName().equals("goat cheese")) {
this.goatlet.setCheeseFound(this.goatlet
.getCheeseFound() + i.getAmount());
} else if (i.getName().equals("glass of goat's milk")) {
this.goatlet.setMilkFound(this.goatlet.getMilkFound()
+ i.getAmount());
}
}
}
// 8-Bit Realm summary
if (ti.getAreaName().equals("8-Bit Realm")) {
this.nesRealm.setTurnsSpent(this.nesRealm.getTurnsSpent()
+ ti.getTotalTurns());
for (final SingleTurn st : ti.getTurns()) {
if (st.getEncounterName().equals("Bullet Bill")) {
this.nesRealm.setBulletsFound(this.nesRealm
.getBulletsFound() + 1);
} else if (st.getEncounterName().equals("Blooper")) {
this.nesRealm.setBloopersFound(this.nesRealm
.getBloopersFound() + 1);
}
}
}
// Meat gain/spent
// Nuns encounter meat ignored here.
if (!ti.getAreaName().equals("Themthar Hills")) {
totalMeatGain += ti.getEncounterMeatGain();
}
totalMeatGain += ti.getOtherMeatGain();
totalMeatSpent += ti.getMeatSpent();
}
this.freeRunaways = new FreeRunaways(totalFreeRunawaysTries,
successfulFreeRunaways);
this.totalTurnsCombat = totalTurnsCombat;
this.totalTurnsNoncombat = totalTurnsNoncombat;
this.totalTurnsOther = totalTurnsOther;
// Consumption summary
this.consumptionSummary = new ConsumptionSummary(consumables,
logData.getDayChanges());
final int tempRolloverTurns = logData.getTurnsSpent().last()
.getEndTurn()
- this.consumptionSummary.getTotalTurnsFromFood()
- this.consumptionSummary.getTotalTurnsFromBooze()
- this.consumptionSummary.getTotalTurnsFromOther();
this.totalTurnsFromRollover = tempRolloverTurns < 0 ? 0
: tempRolloverTurns;
// Total meat gain/spent
this.totalMeatGain = totalMeatGain;
this.totalMeatSpent = totalMeatSpent;
// Total amount of skill casts and total MP used
int totalAmountSkillCasts = 0;
int totalMPUsed = 0;
for (final Skill s : this.skillsCast.getElements()) {
totalAmountSkillCasts += s.getAmount();
totalMPUsed += s.getMpCost();
}
this.totalAmountSkillCasts = totalAmountSkillCasts;
this.totalMPUsed = totalMPUsed;
// Level data summary
this.createLevelSummaryData(logData);
// Meat per level summary
for (final TurnInterval ti : logData.getTurnsSpent()) {
for (final SingleTurn st : ti.getTurns()) {
if (!st.getMeat().isMeatGainSpentZero()) {
this.meatSummary.addLevelMeatData(
logData.getCurrentLevel(st.getTurnNumber())
.getLevelNumber(), st.getMeat());
}
}
}
// Quest turncount summary
this.questTurncounts = new QuestTurncounts(logData.getTurnsSpent(),
this.droppedItems.getElements());
}
/**
* Automatically creates the level summary from the turn rundown of the
* ascension log.
*/
private void createLevelSummaryData(final LogDataHolder logData) {
final Iterator<PlayerSnapshot> plSsIter = logData.getPlayerSnapshots()
.iterator();
PlayerSnapshot currentPlayerSnapshot = plSsIter.hasNext() ? plSsIter
.next() : null;
int currentStatBoarder = SummaryDataCalculator.LEVEL_STAT_BOARDERS_MAP
.get(2);
Statgain stats = new Statgain();
int combatTurns = 0;
int noncombatTurns = 0;
int otherTurns = 0;
// Try to guess the character class if it isn't set yet.
if (logData.getCharacterClass() == CharacterClass.NOT_DEFINED) {
final Set<String> guildItems = new HashSet<>(5);
for (final TurnInterval ti : logData.getTurnsSpent()) {
if (ti.getAreaName().equals(
SummaryDataCalculator.GUILD_CHALLENGE)) {
for (final Item i : ti.getDroppedItems()) {
if (i.getName().equals(
SummaryDataCalculator.ENCHANTED_BARBELL)
|| i.getName()
.equals(SummaryDataCalculator.CONCENTRATED_MAGICALNESS_PILL)
|| i.getName().equals(
SummaryDataCalculator.GIANT_MOXIE_WEED)) {
guildItems.add(i.getName());
}
}
}
}
if ((this.totalStatgains.mus > this.totalStatgains.myst)
&& (this.totalStatgains.mus > this.totalStatgains.myst)) {
if (guildItems.contains(SummaryDataCalculator.GIANT_MOXIE_WEED)) {
logData.setCharacterClass("Seal Clubber");
} else {
logData.setCharacterClass("Turtle Tamer");
}
} else if ((this.totalStatgains.myst > this.totalStatgains.mus)
&& (this.totalStatgains.myst > this.totalStatgains.mox)) {
if (guildItems.contains(SummaryDataCalculator.GIANT_MOXIE_WEED)) {
logData.setCharacterClass("Sauceror");
} else {
logData.setCharacterClass("Pastamancer");
}
} else if (guildItems
.contains(SummaryDataCalculator.CONCENTRATED_MAGICALNESS_PILL)) {
logData.setCharacterClass("Accordion Thief");
} else {
logData.setCharacterClass("Disco Bandit");
}
}
// Substats at the start of an ascension.
switch (logData.getCharacterClass()) {
case SEAL_CLUBBER:
stats = new Statgain(15, 5, 10);
break;
case TURTLE_TAMER:
stats = new Statgain(15, 10, 5);
break;
case PASTAMANCER:
stats = new Statgain(10, 15, 5);
break;
case SAUCEROR:
stats = new Statgain(5, 15, 10);
break;
case DISCO_BANDIT:
stats = new Statgain(10, 5, 15);
break;
case ACCORDION_THIEF:
stats = new Statgain(5, 10, 15);
break;
case NOT_DEFINED:
break;
default:
break;
}
// Set level 1.
this.levels.add(new LevelData(1, 0));
this.levels.get(0).setStatsAtLevelReached(stats);
for (final TurnInterval ti : logData.getTurnsSpent()) {
for (final SingleTurn st : ti.getTurns()) {
// Add stats to the stat counter.
stats = stats.addStats(st.getStatGain());
for (final Consumable c : st.getConsumablesUsed()) {
stats = stats.addStats(c.getStatGain());
}
if ((currentPlayerSnapshot != null)
&& (currentPlayerSnapshot.getTurnNumber() <= st
.getTurnNumber())) {
final int playerMus = currentPlayerSnapshot
.getMuscleStats()
* currentPlayerSnapshot.getMuscleStats();
final int playerMyst = currentPlayerSnapshot.getMystStats()
* currentPlayerSnapshot.getMystStats();
final int playerMox = currentPlayerSnapshot.getMoxieStats()
* currentPlayerSnapshot.getMoxieStats();
// Player snapshot is always right, so if it says the player
// stats are higher, set them to that value.
if (playerMus > stats.mus) {
stats = stats.setMuscle(playerMus);
}
if (playerMyst > stats.myst) {
stats = stats.setMyst(playerMyst);
}
if (playerMox > stats.mox) {
stats = stats.setMoxie(playerMox);
}
currentPlayerSnapshot = plSsIter.hasNext() ? plSsIter
.next() : null;
}
// Increment the correct turn counter.
switch (st.getTurnVersion()) {
case COMBAT:
combatTurns++;
break;
case NONCOMBAT:
noncombatTurns++;
break;
case OTHER:
otherTurns++;
break;
case NOT_DEFINED:
break;
default:
break;
}
// Check whether a new level is reached and act accordingly.
while (SummaryDataCalculator.isNewLevelReached(logData, currentStatBoarder,
stats)) {
final LevelData newLevel = this.computeNewLevelReached(
st.getTurnNumber(), stats, combatTurns,
noncombatTurns, otherTurns);
this.levels.add(newLevel);
currentStatBoarder = SummaryDataCalculator.LEVEL_STAT_BOARDERS_MAP
.get(newLevel.getLevelNumber() + 1);
combatTurns = 0;
noncombatTurns = 0;
otherTurns = 0;
}
}
}
// Add level data to the LogDataHolder if it isn't created from a
// pre-parsed ascension log.
if (logData.getParsedLogCreator() == ParsedLogClass.NOT_DEFINED) {
for (final LevelData lvl : this.levels) {
logData.addLevel(lvl);
}
}
}
private static boolean isNewLevelReached(final LogDataHolder logData,
final int currentStatBoarder, final Statgain stats) {
boolean isNewLevelReached = false;
switch (logData.getCharacterClass().getStatClass()) {
case MUSCLE:
isNewLevelReached = currentStatBoarder <= Math.sqrt(stats.mus);
break;
case MYSTICALITY:
isNewLevelReached = currentStatBoarder <= Math.sqrt(stats.myst);
break;
case MOXIE:
isNewLevelReached = currentStatBoarder <= Math.sqrt(stats.mox);
break;
default:
break;
}
return isNewLevelReached;
}
/**
* Adds the still missing data to the current level and returns the next
* level.
*/
private LevelData computeNewLevelReached(final int currentTurnNumber,
final Statgain currentStats, final int combatTurns,
final int noncombatTurns, final int otherTurns) {
final LevelData currentLevel = this.levels.get(this.levels.size() - 1);
final LevelData newLevel = new LevelData(
currentLevel.getLevelNumber() + 1, currentTurnNumber);
final int turnDifference = currentTurnNumber
- currentLevel.getLevelReachedOnTurn();
final int substatAmountCurrentLevel = SummaryDataCalculator.LEVEL_STAT_BOARDERS_MAP
.get(currentLevel.getLevelNumber())
* SummaryDataCalculator.LEVEL_STAT_BOARDERS_MAP
.get(currentLevel.getLevelNumber());
final int substatAmountNewLevel = SummaryDataCalculator.LEVEL_STAT_BOARDERS_MAP
.get(newLevel.getLevelNumber())
* SummaryDataCalculator.LEVEL_STAT_BOARDERS_MAP.get(newLevel
.getLevelNumber());
currentLevel.setCombatTurns(combatTurns);
currentLevel.setNoncombatTurns(noncombatTurns);
currentLevel.setOtherTurns(otherTurns);
if (turnDifference > 0) {
currentLevel
.setStatGainPerTurn(((substatAmountNewLevel - substatAmountCurrentLevel) * 1.0)
/ turnDifference);
} else {
currentLevel.setStatGainPerTurn(substatAmountNewLevel
- substatAmountCurrentLevel);
}
newLevel.setStatsAtLevelReached(currentStats);
return newLevel;
}
/**
* @return A list of areas and the turns spent in them.
*/
List<DataNumberPair<String>> getTurnsPerArea() {
return this.turnsPerArea.getCountedData();
}
/**
* @return A list of all consumables used.
*/
Collection<Consumable> getConsumablesUsed() {
return this.consumablesUsed.getElements();
}
/**
* @return A list of all items dropped.
*/
Collection<Item> getDroppedItems() {
return this.droppedItems.getElements();
}
/**
* @return A list of all skills cast.
*/
Collection<Skill> getSkillsCast() {
return this.skillsCast.getElements();
}
/**
* @return A list of all levels.
*/
List<LevelData> getLevelData() {
return this.levels;
}
/**
* @return A list of all used familiars and how often they were used.
*/
List<DataNumberPair<String>> getFamiliarUsage() {
return this.familiarUsage.getCountedData();
}
/**
* @return A list of all started hunts on combats.
*/
List<DataNumberPair<String>> getHuntedCombats() {
return this.huntedCombats;
}
/**
* @return A list of all disintegrated combats.
*/
List<DataNumberPair<String>> getDisintegratedCombats() {
return this.disintegratedCombats;
}
/**
* @return A list of all semirares.
*/
List<DataNumberPair<String>> getSemirares() {
return this.semirares;
}
/**
* @return A list of all Bad Moon adventures.
*/
List<DataNumberPair<String>> getBadmoonAdventures() {
return this.badmoonAdventures;
}
/**
* @return A summary on consumables used during the ascension.
*/
ConsumptionSummary getConsumptionSummary() {
return this.consumptionSummary;
}
/**
* @return The free runaways over the whole ascension.
*/
public FreeRunaways getFreeRunaways() {
return this.freeRunaways;
}
/**
* @return The RNG data of the Sewer.
*/
Sewer getSewer() {
return this.sewer;
}
/**
* @return The RNG data of the Goatlet.
*/
Goatlet getGoatlet() {
return this.goatlet;
}
/**
* @return The RNG data of the 8-Bit Realm.
*/
InexplicableDoor get8BitRealm() {
return this.nesRealm;
}
/**
* @return The quest turncounts.
*/
QuestTurncounts getQuestTurncounts() {
return this.questTurncounts;
}
/**
* @return The total mp gains collected during this ascension.
*/
MPGain getMPGains() {
return this.mpGains;
}
/**
* @return The meat per level summary.
*/
MeatSummary getMeatSummary() {
return this.meatSummary;
}
/**
* @return The total amount of substats collected during this ascension.
*/
Statgain getTotalStatgains() {
return this.totalStatgains;
}
/**
* @return The total amount of substats from combats collected during this
* ascension.
*/
Statgain getCombatsStatgains() {
return this.combatsStatgains;
}
/**
* @return The total amount of substats from noncombats collected during
* this ascension.
*/
Statgain getNoncombatsStatgains() {
return this.noncombatsStatgains;
}
/**
* @return The total amount of substats from other encounters collected
* during this ascension.
*/
Statgain getOthersStatgains() {
return this.othersStatgains;
}
/**
* @return The total amount of skill casts.
*/
int getTotalAmountSkillCasts() {
return this.totalAmountSkillCasts;
}
/**
* @return The total amount of MP spent on skills.
*/
int getTotalMPUsed() {
return this.totalMPUsed;
}
/**
* @return The total amount of meat gathered.
*/
int getTotalMeatGain() {
return this.totalMeatGain;
}
/**
* @return The total amount of meat spent.
*/
int getTotalMeatSpent() {
return this.totalMeatSpent;
}
/**
* @return The total amount of turns gained from rollover.
*/
int getTotalTurnsFromRollover() {
return this.totalTurnsFromRollover;
}
/**
* @return The total amount of combat turns.
*/
int getTotalTurnsCombat() {
return this.totalTurnsCombat;
}
/**
* @return The total amount of noncombat turns.
*/
int getTotalTurnsNoncombat() {
return this.totalTurnsNoncombat;
}
/**
* @return The total amount of other (smithing, mixing, cooking, etc.)
* turns.
*/
int getTotalTurnsOther() {
return this.totalTurnsOther;
}
}