/* 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.parser.mafiaLogBlockParsers;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Scanner;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.googlecode.logVisualizer.logData.LogDataHolder;
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.turnAction.EquipmentChange;
import com.googlecode.logVisualizer.logData.turn.turnAction.FamiliarChange;
import com.googlecode.logVisualizer.parser.UsefulPatterns;
import com.googlecode.logVisualizer.parser.lineParsers.EquipmentLineParser;
import com.googlecode.logVisualizer.parser.lineParsers.MPGainLineParser;
import com.googlecode.logVisualizer.parser.lineParsers.MPGainLineParser.MPGainType;
import com.googlecode.logVisualizer.parser.lineParsers.MeatLineParser;
import com.googlecode.logVisualizer.parser.lineParsers.MeatLineParser.MeatGainType;
import com.googlecode.logVisualizer.parser.lineParsers.MeatSpentLineParser;
import com.googlecode.logVisualizer.parser.lineParsers.StatLineParser;
import com.googlecode.logVisualizer.util.DataTablesHandler;
/**
* A parser for the consumable used notation in mafia logs.
* <p>
* The format looks like this:
* <p>
* {@code use/eat/drink _amount_ _itemName_}
* <p>
* OR
* <p>
* {@code Buy and eat/drink _amount_ _itemName_ for _meatAmount_ Meat}
* <p>
* Further on, these lines can follow the described top line and will be parsed
* (there may be other lines, but those will be ignored):
* <p>
* {@code You gain _amount_ _substatName_}
* <p>
* {@code You gain _amount_ Meat}
* <p>
* {@code You gain _amount_ Adventure/Adventures}
* <p>
* {@code You gain _amount_ Mana/Mojo/Muscularity Points}
* <p>
* {@code You lose _amount_ _substatName_}
* <p>
* {@code You lose _amount_ Meat}
* <p>
* {@code You lose _amount_ Adventure/Adventures}
*/
public final class ConsumableBlockParser implements LogBlockParser {
private static final Pattern CONSUMABLE_BOUGHT_USED_CAPTURE_PATTERN = Pattern
.compile("([\\w\\s]+) (\\d+) (.+) for \\d+ Meat");
private static final Pattern CONSUMABLE_USED_CAPTURE_PATTERN = Pattern
.compile("([\\w\\s]+) (\\d+) (.+)");
private static final String FOOD_STRING = "eat";
private static final String BOOZE_STRING = "drink";
private static final String ADVENTURE_STRING = "Adventure";
private static final String LOSE_STRING = "You lose";
private static final String LLAMA_COCKROACH_ENCOUNTER_STRING = "Encounter: Form of...Cockroach!";
private static final String COCKROACH_AREA_ENCOUNTER_NAME = "Form of...Cockroach!";
private static final Set<String> STEEL_CONSUMABLES = new HashSet<>(
Arrays.asList("steel margarita", "steel lasagna",
"steel-scented air freshener"));
private final MPGainLineParser mpParser = new MPGainLineParser(
MPGainType.CONSUMABLE);
private final MeatLineParser meatGainParser = new MeatLineParser(
MeatGainType.OTHER);
private final MeatSpentLineParser meatSpentParser = new MeatSpentLineParser();
private final EquipmentLineParser outfitChangeParser = new EquipmentLineParser();
// Only use for Llama cockroach stats parsing.
private final StatLineParser statsParser = new StatLineParser();
// Only use for Llama cockroach mp gains parsing.
private final MPGainLineParser mpParserLlama = new MPGainLineParser(
MPGainType.ENCOUNTER);
private final Matcher consumableBoughtMatcher = ConsumableBlockParser.CONSUMABLE_BOUGHT_USED_CAPTURE_PATTERN
.matcher(UsefulPatterns.EMPTY_STRING);
private final Matcher gainLoseMatcher = UsefulPatterns.GAIN_LOSE
.matcher(UsefulPatterns.EMPTY_STRING);
/**
* {@inheritDoc}
*/
@Override
public void parseBlock(final List<String> block, final LogDataHolder logData) {
// First, parse item name and amount used.
final String consumptionLine = block.get(0);
final Matcher result;
try (final Scanner scanner = new Scanner(consumptionLine)) {
if (this.consumableBoughtMatcher.reset(consumptionLine).matches()) {
result = ConsumableBlockParser.CONSUMABLE_BOUGHT_USED_CAPTURE_PATTERN
.matcher(consumptionLine);
} else {
result = ConsumableBlockParser.CONSUMABLE_USED_CAPTURE_PATTERN
.matcher(consumptionLine);
}
result.find();
final String usageIdentifier = result.group(1);
final int amount = Integer.parseInt(result.group(2));
final String itemName = result.group(3);
int adventureGain = 0;
Statgain consumableStatgain = new Statgain();
scanner.close();
// Amount equal or smaller than zero cannot nor should be further
// processed.
if (amount <= 0) {
return;
}
for (int i = 1; i < block.size(); i++) {
final String line = block.get(i);
// Llama gongs going cockroach need special handling due to the
// way
// mafia logs the Cockroach path.
if (line.equals(ConsumableBlockParser.LLAMA_COCKROACH_ENCOUNTER_STRING)) {
this.parseLlamaCockraochUsage(
block.subList(i, block.size()), logData);
break;
}
if (this.mpParser.parseLine(line, logData)
|| this.meatGainParser.parseLine(line, logData)
|| this.meatSpentParser.parseLine(line, logData)
|| this.outfitChangeParser.parseLine(line, logData)) {
// Empty block, because the parsing has already happened if
// we
// get in here.
} else if (this.gainLoseMatcher.reset(line).matches()) {
final Matcher m = UsefulPatterns.GAIN_LOSE_CAPTURE_PATTERN
.matcher(line);
m.find();
int gainAmount;
if (m.group(1).contains(UsefulPatterns.COMMA)) {
gainAmount = Integer.parseInt(m.group(1).replace(
UsefulPatterns.COMMA,
UsefulPatterns.EMPTY_STRING));
} else {
gainAmount = Integer.parseInt(m.group(1));
}
final String gainIdentifier = m.group(2);
scanner.close();
if (line.startsWith(ConsumableBlockParser.LOSE_STRING)) {
gainAmount *= -1;
}
if (gainIdentifier
.startsWith(ConsumableBlockParser.ADVENTURE_STRING)) {
adventureGain += gainAmount;
} else if (UsefulPatterns.MUSCLE_SUBSTAT_NAMES
.contains(gainIdentifier)) {
consumableStatgain = consumableStatgain.addStats(
gainAmount, 0, 0);
} else if (UsefulPatterns.MYST_SUBSTAT_NAMES
.contains(gainIdentifier)) {
consumableStatgain = consumableStatgain.addStats(0,
gainAmount, 0);
} else if (UsefulPatterns.MOXIE_SUBSTAT_NAMES
.contains(gainIdentifier)) {
consumableStatgain = consumableStatgain.addStats(0, 0,
gainAmount);
}
}
}
// Only add consumable if there was some specific data to it or it
// was
// one of the Azazel quest rewards.
if ((adventureGain != 0)
|| !consumableStatgain.isAllStatsZero()
|| ConsumableBlockParser.STEEL_CONSUMABLES
.contains(itemName)) {
// Add consumable to the turn interval. While the Consumable
// class
// is mutable, no special actions need to be made because of it,
// because the AbstractTurn class does this already internally.
final TurnInterval currentInterval = logData.getTurnsSpent()
.last();
final int currentTurn = currentInterval.getEndTurn();
final Consumable tmpCon;
if (usageIdentifier.contains(ConsumableBlockParser.FOOD_STRING)) {
tmpCon = Consumable.newFoodConsumable(itemName,
adventureGain, amount, currentTurn);
} else if (usageIdentifier
.contains(ConsumableBlockParser.BOOZE_STRING)) {
tmpCon = Consumable.newBoozeConsumable(itemName,
adventureGain, amount, currentTurn);
} else if ((DataTablesHandler.getSpleenHit(itemName) > 0)
&& (adventureGain > 0)) {
tmpCon = Consumable.newSpleenConsumable(itemName,
adventureGain, amount, currentTurn);
} else {
tmpCon = Consumable.newOtherConsumable(itemName,
adventureGain, amount, currentTurn);
}
tmpCon.setDayNumberOfUsage(logData.getLastDayChange()
.getDayNumber());
tmpCon.setStatGain(consumableStatgain);
currentInterval.addConsumableUsed(tmpCon);
}
}
}
private void parseLlamaCockraochUsage(final List<String> block,
final LogDataHolder logData) {
// The way this is implemented means that the cockroach stat gains
// will be added to the third turn. Fixing this would however mean
// even more hardcoding for it than is already done here and it
// would be even less elegant, so it won't be done (for now). Mafia
// might possibly fix its logging of these turns which is another
// reason to not do it here.
final int lastTurnNumber = logData.getTurnsSpent().last().getEndTurn();
final EquipmentChange lastEquipment = logData.getLastEquipmentChange();
final FamiliarChange lastFamiliar = logData.getLastFamiliarChange();
final SingleTurn tmpTurn1 = new SingleTurn(
ConsumableBlockParser.COCKROACH_AREA_ENCOUNTER_NAME,
ConsumableBlockParser.COCKROACH_AREA_ENCOUNTER_NAME,
lastTurnNumber + 1, lastEquipment, lastFamiliar);
final SingleTurn tmpTurn2 = new SingleTurn(
ConsumableBlockParser.COCKROACH_AREA_ENCOUNTER_NAME,
ConsumableBlockParser.COCKROACH_AREA_ENCOUNTER_NAME,
lastTurnNumber + 2, lastEquipment, lastFamiliar);
final SingleTurn tmpTurn3 = new SingleTurn(
ConsumableBlockParser.COCKROACH_AREA_ENCOUNTER_NAME,
ConsumableBlockParser.COCKROACH_AREA_ENCOUNTER_NAME,
lastTurnNumber + 3, lastEquipment, lastFamiliar);
tmpTurn1.setTurnVersion(TurnVersion.OTHER);
tmpTurn2.setTurnVersion(TurnVersion.OTHER);
tmpTurn3.setTurnVersion(TurnVersion.OTHER);
logData.addTurnSpent(tmpTurn1);
logData.addTurnSpent(tmpTurn2);
logData.addTurnSpent(tmpTurn3);
// Parse lines of interest from cockroach path.
for (final String line : block) {
this.statsParser.parseLine(line, logData);
this.mpParserLlama.parseLine(line, logData);
this.outfitChangeParser.parseLine(line, logData);
}
}
}