/**
* Copyright (c) 2005-2017, KoLmafia development team
* http://kolmafia.sourceforge.net/
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* [1] Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* [2] Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* [3] Neither the name "KoLmafia" nor the names of its contributors may
* be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package net.sourceforge.kolmafia.persistence;
import java.io.BufferedReader;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import net.sourceforge.kolmafia.KoLCharacter;
import net.sourceforge.kolmafia.KoLConstants;
import net.sourceforge.kolmafia.StaticEntity;
import net.sourceforge.kolmafia.preferences.Preferences;
import net.sourceforge.kolmafia.utilities.FileUtilities;
import net.sourceforge.kolmafia.utilities.StringUtilities;
/**
* Provides utility functions for dealing with quests.
*
*/
public class QuestDatabase
{
public enum Quest
{
LARVA( "questL02Larva" ),
RAT( "questL03Rat" ),
BAT( "questL04Bat" ),
GOBLIN( "questL05Goblin" ),
FRIAR( "questL06Friar" ),
CYRPT( "questL07Cyrptic" ),
TRAPPER( "questL08Trapper" ),
TOPPING( "questL09Topping" ),
GARBAGE( "questL10Garbage" ),
MACGUFFIN( "questL11MacGuffin" ),
BLACK( "questL11Black" ),
WORSHIP( "questL11Worship" ),
MANOR( "questL11Manor" ),
PYRAMID( "questL11Pyramid" ),
PALINDOME( "questL11Palindome" ),
SHEN( "questL11Shen" ),
RON( "questL11Ron" ),
CURSES( "questL11Curses" ),
DOCTOR( "questL11Doctor" ),
BUSINESS( "questL11Business" ),
SPARE( "questL11Spare" ),
DESERT( "questL11Desert" ),
ISLAND_WAR( "questL12War" ),
FINAL( "questL13Final" ),
WAREHOUSE( "questL13Warehouse" ),
MEATCAR( "questG01Meatcar" ),
CITADEL( "questG02Whitecastle" ),
NEMESIS( "questG04Nemesis" ),
MYST( "questG07Myst" ),
MOXIE( "questG08Moxie" ),
MUSCLE( "questG09Muscle" ),
UNTINKER( "questM01Untinker" ),
ARTIST( "questM02Artist" ),
BUGBEAR( "questM03Bugbear" ),
AZAZEL( "questM10Azazel" ),
PIRATE( "questM12Pirate" ),
ESCAPE( "questM13Escape" ),
LOL( "questM15Lol" ),
TEMPLE( "questM16Temple" ),
SPOOKYRAVEN_BABIES( "questM17Babies" ),
SWAMP( "questM18Swamp" ),
HIPPY( "questM19Hippy" ),
SPOOKYRAVEN_NECKLACE( "questM20Necklace" ),
SPOOKYRAVEN_DANCE( "questM21Dance" ),
SHIRT( "questM22Shirt" ),
MEATSMITH( "questM23Meatsmith" ),
DOC( "questM24Doc" ),
ARMORER( "questM25Armorer" ),
GENERATOR( "questF04Elves" ),
CLANCY( "questF05Clancy" ),
SEA_OLD_GUY( "questS01OldGuy" ),
SEA_MONKEES( "questS02Monkees" ),
JIMMY_MUSHROOM( "questESlMushStash" ),
JIMMY_CHEESEBURGER( "questESlCheeseburger" ),
JIMMY_SALT( "questESlSalt" ),
TACO_DAN_AUDIT( "questESlAudit" ),
TACO_DAN_COCKTAIL( "questESlCocktail" ),
TACO_DAN_FISH( "questESlFish" ),
BRODEN_BACTERIA( "questESlBacteria" ),
BRODEN_SPRINKLES( "questESlSprinkles" ),
BRODEN_DEBT( "questESlDebt" ),
EVE( "questESpEVE" ),
JUNGLE_PUN( "questESpJunglePun" ),
GORE( "questESpGore" ),
CLIPPER( "questESpClipper" ),
FAKE_MEDIUM( "questESpFakeMedium" ),
SERUM( "questESpSerum" ),
SMOKES( "questESpSmokes" ),
OUT_OF_ORDER( "questESpOutOfOrder" ),
FISH_TRASH( "questEStFishTrash" ),
GIVE_ME_FUEL( "questEStGiveMeFuel" ),
NASTY_BEARS( "questEStNastyBears" ),
SOCIAL_JUSTICE_I( "questEStSocialJusticeI" ),
SOCIAL_JUSTICE_II( "questEStSocialJusticeII" ),
SUPER_LUBER( "questEStSuperLuber" ),
WORK_WITH_FOOD( "questEStWorkWithFood" ),
ZIPPITY_DOO_DAH( "questEStZippityDooDah" ),
BUCKET( "questECoBucket" ),
TELEGRAM( "questLTTQuestByWire" ),
ORACLE( "questM26Oracle" ),
GHOST( "questPAGhost" ),
;
private final String pref;
private Quest( String pref )
{
this.pref = pref;
}
public String getPref()
{
return this.pref;
}
public String getStatus()
{
return Preferences.getString( this.pref );
}
}
public static final String UNSTARTED = "unstarted";
public static final String STARTED = "started";
public static final String FINISHED = "finished";
public static final Pattern HTML_WHITESPACE = Pattern.compile( "<[^<]+?>|[\\s\\n]" );
public static final Pattern BOO_PEAK_PATTERN = Pattern.compile( "It is currently (\\d+)%" );
public static final Pattern OIL_PEAK_PATTERN = Pattern.compile( "The pressure is currently ([\\d\\.]+) microbowies" );
public static final Pattern COMPETITION_PATTERN = Pattern.compile( "Contest #(\\d): ((\\d+) competitor|(Won!))" );
public static final Pattern ORACLE_QUEST_PATTERN = Pattern.compile( "<b>(.*?)</b>" );
public static final Pattern GHOST_QUEST_PATTERN = Pattern.compile( "<b>(.*?)</b>" );
private static String[][] questLogData = null;
private static String[][] councilData = null;
static
{
reset();
}
public static void reset()
{
BufferedReader reader = FileUtilities.getVersionedReader( "questslog.txt", KoLConstants.QUESTSLOG_VERSION );
ArrayList<String[]> quests = new ArrayList<String[]>();
String[] data;
while ( ( data = FileUtilities.readData( reader ) ) != null )
{
data[ 1 ] = data[ 1 ].replaceAll( "<Player\\sName>",
KoLCharacter.getUserName() );
quests.add( data );
}
questLogData = quests.toArray( new String[ quests.size() ][] );
try
{
reader.close();
}
catch ( Exception e )
{
StaticEntity.printStackTrace( e );
}
reader = FileUtilities.getVersionedReader( "questscouncil.txt", KoLConstants.QUESTSCOUNCIL_VERSION );
quests = new ArrayList<String[]>();
while ( ( data = FileUtilities.readData( reader ) ) != null )
{
quests.add( data );
}
councilData = quests.toArray( new String[ quests.size() ][] );
try
{
reader.close();
}
catch ( Exception e )
{
StaticEntity.printStackTrace( e );
}
}
public static String titleToPref( final String title )
{
if ( title.contains( "White Citadel" ) )
{
// Hard code this quest, for now. The familiar name in the middle of the string is annoying to
// deal with.
return "questG02Whitecastle";
}
for ( int i = 0; i < questLogData.length; ++i )
{
// The title may contain other text, so check if quest title is contained in it
if ( title.toLowerCase().contains( questLogData[ i ][ 1 ].toLowerCase() ) )
{
return questLogData[ i ][ 0 ];
}
}
// couldn't find a match
return "";
}
public static Quest titleToQuest( final String title )
{
String pref = titleToPref( title );
if ( pref.equals( "" ) )
{
return null;
}
for ( Quest q: Quest.values() )
{
if ( q.getPref().equals( pref ) )
{
return q;
}
}
return null;
}
public static String prefToTitle( final String pref )
{
for ( int i = 0; i < questLogData.length; ++i )
{
if ( questLogData[ i ][ 0 ].toLowerCase().contains( pref.toLowerCase() ) )
{
return questLogData[ i ][ 1 ];
}
}
// couldn't find a match
return "";
}
public static int prefToIndex( final String pref )
{
for ( int i = 0; i < questLogData.length; ++i )
{
if ( questLogData[ i ][ 0 ].toLowerCase().contains( pref.toLowerCase() ) )
{
return i;
}
}
// couldn't find a match
return -1;
}
public static String findQuestProgress( String pref, String details )
{
// Special handling due to multiple endings
if ( pref.equals( Quest.ISLAND_WAR.getPref() ) )
{
return handleWarStatus( details );
}
if ( pref.equals( Quest.PIRATE.getPref() ) && details.contains( "Oh, and also you've managed to scam your way belowdecks, which is cool" ) )
{
// Hard code the end of the pirate quest, as it step 6 matches the final text also.
return QuestDatabase.FINISHED;
}
if ( pref.equals( Quest.TOPPING.getPref() ) && details.contains( "The Highland Lord wants you to light" ) )
{
// this is step2. We need to do some other handling for the three sub-parts.
return handlePeakStatus( details );
}
if ( pref.equals( Quest.FINAL.getPref() ) )
{
handleCompetitionStatus( details );
}
// Special handling as there are nine sets of descriptions for the one quest
if ( pref.equals( Quest.TELEGRAM.getPref() ) )
{
return handleTelegramStatus( details );
}
// Get Oracle quest target
if ( pref.equals( Quest.ORACLE.getPref() ) )
{
Matcher matcher = QuestDatabase.ORACLE_QUEST_PATTERN.matcher( details );
if ( matcher.find() )
{
Preferences.setString( "sourceOracleTarget", matcher.group( 1 ) );
}
}
// Get Ghost quest target
if ( pref.equals( Quest.GHOST.getPref() ) )
{
Matcher matcher = QuestDatabase.GHOST_QUEST_PATTERN.matcher( details );
if ( matcher.find() )
{
Preferences.setString( "ghostLocation", matcher.group( 1 ) );
}
}
// First thing to do is find which quest we're talking about.
int index = prefToIndex( pref );
if ( index == -1 )
{
return "";
}
// Next, find the number of quest steps
final int steps = questLogData[ index ].length - 2;
if ( steps < 1 )
{
return "";
}
// Now, try to see if we can find an exact match for response->step. This is often messed up by
// whitespace, html, and the like. We'll handle that below.
int foundAtStep = -1;
for ( int i = 2; i < questLogData[ index ].length; ++i )
{
if ( questLogData[ index ][ i ].contains( details ) )
{
foundAtStep = i - 2;
break;
}
}
if ( foundAtStep == -1 )
{
// Didn't manage to find an exact match. Now try stripping out all whitespace, newlines, and
// anything that looks like html from questData and response. And make everything lower case,
// because player names can be arbitrarily capitalized.
String cleanedResponse = QuestDatabase.HTML_WHITESPACE.matcher( details ).replaceAll( "" )
.toLowerCase();
String cleanedQuest = "";
for ( int i = 2; i < questLogData[ index ].length; ++i )
{
cleanedQuest = QuestDatabase.HTML_WHITESPACE.matcher( questLogData[ index ][ i ] )
.replaceAll( "" ).toLowerCase();
if ( cleanedQuest.contains( cleanedResponse ) )
{
foundAtStep = i - 2;
break;
}
}
}
if ( foundAtStep == -1 )
{
// STILL haven't found a match. Try reversing the match, and chopping up the quest data into
// substrings.
String cleanedResponse = QuestDatabase.HTML_WHITESPACE.matcher( details ).replaceAll( "" )
.toLowerCase();
String cleanedQuest = "";
String questStart = "";
String questEnd = "";
for ( int i = 2; i < questLogData[ index ].length; ++i )
{
cleanedQuest = QuestDatabase.HTML_WHITESPACE.matcher( questLogData[ index ][ i ] )
.replaceAll( "" ).toLowerCase();
if ( cleanedQuest.length() <= 100 )
{
questStart = cleanedQuest;
questEnd = cleanedQuest;
}
else
{
questStart = cleanedQuest.substring( 0, 100 );
questEnd = cleanedQuest.substring( cleanedQuest.length() - 100 );
}
if ( cleanedResponse.contains( questStart )
|| cleanedResponse.contains( questEnd ) )
{
foundAtStep = i - 2;
break;
}
}
}
if ( foundAtStep != -1 )
{
if ( foundAtStep == 0 )
{
return QuestDatabase.STARTED;
}
else if ( foundAtStep == steps - 1 )
{
return QuestDatabase.FINISHED;
}
else
{
return "step" + foundAtStep;
}
}
// Well, none of the above worked. Punt.
return "";
}
private static String handlePeakStatus( String details )
{
Matcher boo = QuestDatabase.BOO_PEAK_PATTERN.matcher( details );
// boo peak handling. 100 is started, 0 is complete.
if ( details.contains( "lit the fire on A-Boo Peak" ) )
{
Preferences.setInteger( "booPeakProgress", 0 );
}
else if ( details.contains( "check out A-Boo Peak" ) )
{
Preferences.setInteger( "booPeakProgress", 100 );
}
else if ( boo.find() )
{
Preferences.setInteger( "booPeakProgress", StringUtilities.parseInt( boo.group( 1 ) ) );
}
// twin peak handling
// No information is present in the quest log between first starting the quest and completing 3/4 of it. Boo.
if ( details.contains( "lit the fire on Twin Peak" ) )
{
// twinPeakProgress is a bit field. 15 is complete.
Preferences.setInteger( "twinPeakProgress", 15 );
}
Matcher oil = QuestDatabase.OIL_PEAK_PATTERN.matcher( details );
// oil peak handling. 310.66 is started, 0 is complete.
if ( details.contains( "lit the fire on Oil Peak" ) )
{
Preferences.setString( "oilPeakProgress", String.valueOf( 0 ) );
}
else if ( details.contains( "go to Oil Peak and investigate" ) )
{
Preferences.setString( "oilPeakProgress", String.valueOf( 310.66 ) );
}
else if ( oil.find() )
{
Preferences.setString( "oilPeakProgress", oil.group( 1 ) );
}
return "step2";
}
private static String handleWarStatus( String details )
{
if ( details.contains( "You led the filthy hippies to victory" )
|| details.contains( "You led the Orcish frat boys to victory" )
|| details.contains( "You started a chain of events" ) )
{
return QuestDatabase.FINISHED;
}
else if ( details.contains( "You've managed to get the war between the hippies and frat boys started" ) )
{
return "step1";
}
else if ( details.contains( "The Council has gotten word of tensions building between the hippies and the frat boys" ) )
{
return QuestDatabase.STARTED;
}
return "";
}
private static void handleCompetitionStatus( String details )
{
Matcher competition = QuestDatabase.COMPETITION_PATTERN.matcher( details );
while ( competition.find() )
{
String preference = "nsContestants" + StringUtilities.parseInt( competition.group( 1 ) );
int left = -1;
if( competition.group( 2 ).equals( "Won!" ) )
{
left = 0;
}
else
{
left = StringUtilities.parseInt( competition.group( 3 ) );
}
Preferences.setInteger( preference, left );
}
if ( details.contains( "Ascend the <a href=place.php?whichplace=nstower class=nounder target=mainpane><b>Naughty Sorceress' Tower</b></a>." ) )
{
QuestDatabase.setQuestIfBetter( Quest.FINAL, "step6" );
}
}
private static String handleTelegramStatus( String details )
{
// It's kludgy, but there are nine different texts for the one quest title, so the existing code can't handle it
// Missing: Fancy Man
if ( details.contains( "Ask around the Rough Diamond Saloon to see if anybody has seen Jeff the Fancy Dude." ) )
{
Preferences.setString( "lttQuestName", "Missing: Fancy Man" );
Preferences.setInteger( "lttQuestDifficulty", 1 );
return "step1";
}
if ( details.contains( "Trek across the desert to Jeff's mining claim." ) )
{
Preferences.setString( "lttQuestName", "Missing: Fancy Man" );
Preferences.setInteger( "lttQuestDifficulty", 1 );
return "step2";
}
if ( details.contains( "Delve deeper into Jeff's Fancy Mine." ) )
{
Preferences.setString( "lttQuestName", "Missing: Fancy Man" );
Preferences.setInteger( "lttQuestDifficulty", 1 );
return "step3";
}
if ( details.contains( "Defeat Jeff the Fancy Skeleton." ) )
{
Preferences.setString( "lttQuestName", "Missing: Fancy Man" );
Preferences.setInteger( "lttQuestDifficulty", 1 );
return "step4";
}
// Missing: Pioneer Daughter
if ( details.contains( "Search for Daisy's homestead." ) )
{
Preferences.setString( "lttQuestName", "Missing: Pioneer Daughter" );
Preferences.setInteger( "lttQuestDifficulty", 1 );
return "step1";
}
if ( details.contains( "Question the cultists in Bloodmilk Cave." ) )
{
Preferences.setString( "lttQuestName", "Missing: Pioneer Daughter" );
Preferences.setInteger( "lttQuestDifficulty", 1 );
return "step2";
}
if ( details.contains( "Fight your way through Daisy's Fortress." ) )
{
Preferences.setString( "lttQuestName", "Missing: Pioneer Daughter" );
Preferences.setInteger( "lttQuestDifficulty", 1 );
return "step3";
}
if ( details.contains( "Defeat Daisy the Unclean." ) )
{
Preferences.setString( "lttQuestName", "Missing: Pioneer Daughter" );
Preferences.setInteger( "lttQuestDifficulty", 1 );
return "step4";
}
// Help! Desperados!
if ( details.contains( "Clear some of the criminals out of Spitback." ) )
{
Preferences.setString( "lttQuestName", "Help! Desperados!" );
Preferences.setInteger( "lttQuestDifficulty", 1 );
return "step1";
}
if ( details.contains( "Find your way to Pecos Dave's hideout." ) )
{
Preferences.setString( "lttQuestName", "Help! Desperados!" );
Preferences.setInteger( "lttQuestDifficulty", 1 );
return "step2";
}
if ( details.contains( "Find Pecos Dave in his mine hideout." ) )
{
Preferences.setString( "lttQuestName", "Help! Desperados!" );
Preferences.setInteger( "lttQuestDifficulty", 1 );
return "step3";
}
if ( details.contains( "Defeat Pecos Dave." ) )
{
Preferences.setString( "lttQuestName", "Help! Desperados!" );
Preferences.setInteger( "lttQuestDifficulty", 1 );
return "step4";
}
// Haunted Boneyard
if ( details.contains( "Find the pastor in his church." ) )
{
Preferences.setString( "lttQuestName", "Haunted Boneyard" );
Preferences.setInteger( "lttQuestDifficulty", 2 );
return "step1";
}
if ( details.contains( "Investigate the local cemetery." ) )
{
Preferences.setString( "lttQuestName", "Haunted Boneyard" );
Preferences.setInteger( "lttQuestDifficulty", 2 );
return "step2";
}
if ( details.contains( "Clear out the ancient cow burial ground." ) )
{
Preferences.setString( "lttQuestName", "Haunted Boneyard" );
Preferences.setInteger( "lttQuestDifficulty", 2 );
return "step3";
}
if ( details.contains( "Defeat Amoon-Ra Cowtep." ) )
{
Preferences.setString( "lttQuestName", "Haunted Boneyard" );
Preferences.setInteger( "lttQuestDifficulty", 2 );
return "step4";
}
// Big Gambling Tournament Announced
if ( details.contains( "Fight your way through the crowd at the gambling tournament." ) )
{
Preferences.setString( "lttQuestName", "Big Gambling Tournament Announced" );
Preferences.setInteger( "lttQuestDifficulty", 2 );
return "step1";
}
if ( details.contains( "Escape from the snake pit!" ) )
{
Preferences.setString( "lttQuestName", "Big Gambling Tournament Announced" );
Preferences.setInteger( "lttQuestDifficulty", 2 );
return "step2";
}
if ( details.contains( "Track down Snakeye Glenn at the Great Western hotel." ) )
{
Preferences.setString( "lttQuestName", "Big Gambling Tournament Announced" );
Preferences.setInteger( "lttQuestDifficulty", 2 );
return "step3";
}
if ( details.contains( "Defeat Snakeeye Glenn." ) )
{
Preferences.setString( "lttQuestName", "Big Gambling Tournament Announced" );
Preferences.setInteger( "lttQuestDifficulty", 2 );
return "step4";
}
// Sheriff Wanted
if ( details.contains( "Fight your way to the sheriff's office and apply for the job." ) )
{
Preferences.setString( "lttQuestName", "Sheriff Wanted" );
Preferences.setInteger( "lttQuestDifficulty", 2 );
return "step1";
}
if ( details.contains( "Head up river to the Placid Lake Gang's hideout." ) )
{
Preferences.setString( "lttQuestName", "Sheriff Wanted" );
Preferences.setInteger( "lttQuestDifficulty", 2 );
return "step2";
}
if ( details.contains( "Search the hideout for the gang's leader." ) )
{
Preferences.setString( "lttQuestName", "Sheriff Wanted" );
Preferences.setInteger( "lttQuestDifficulty", 2 );
return "step3";
}
if ( details.contains( "Defeat Former Sheriff Dan Driscoll." ) )
{
Preferences.setString( "lttQuestName", "Sheriff Wanted" );
Preferences.setInteger( "lttQuestDifficulty", 2 );
return "step4";
}
// Madness at the Mine
if ( details.contains( "Figure out what's going wrong at the mine." ) )
{
Preferences.setString( "lttQuestName", "Madness at the Mine" );
Preferences.setInteger( "lttQuestDifficulty", 3 );
return "step1";
}
if ( details.contains( "Search the desert for the missing foreman." ) )
{
Preferences.setString( "lttQuestName", "Madness at the Mine" );
Preferences.setInteger( "lttQuestDifficulty", 3 );
return "step2";
}
if ( details.contains( "Find that door in the mine again." ) )
{
Preferences.setString( "lttQuestName", "Madness at the Mine" );
Preferences.setInteger( "lttQuestDifficulty", 3 );
return "step3";
}
if ( details.contains( "Defeat the unusual construct." ) )
{
Preferences.setString( "lttQuestName", "Madness at the Mine" );
Preferences.setInteger( "lttQuestDifficulty", 3 );
return "step4";
}
// Missing: Many Children
if ( details.contains( "Find out why the children are going missing." ) )
{
Preferences.setString( "lttQuestName", "Missing: Many Children" );
Preferences.setInteger( "lttQuestDifficulty", 3 );
return "step1";
}
if ( details.contains( "Ride the ghost train." ) )
{
Preferences.setString( "lttQuestName", "Missing: Many Children" );
Preferences.setInteger( "lttQuestDifficulty", 3 );
return "step2";
}
if ( details.contains( "Search Cowtown for the missing children." ) )
{
Preferences.setString( "lttQuestName", "Missing: Many Children" );
Preferences.setInteger( "lttQuestDifficulty", 3 );
return "step3";
}
if ( details.contains( "Defeat Clara." ) )
{
Preferences.setString( "lttQuestName", "Missing: Many Children" );
Preferences.setInteger( "lttQuestDifficulty", 3 );
return "step4";
}
// Wagon Train Escort Wanted
if ( details.contains( "Escort the Hackleton wagon train across the desert." ) )
{
Preferences.setString( "lttQuestName", "Wagon Train Escort Wanted" );
Preferences.setInteger( "lttQuestDifficulty", 3 );
return "step1";
}
if ( details.contains( "Defend the Hackleton wagon train!" ) )
{
Preferences.setString( "lttQuestName", "Wagon Train Escort Wanted" );
Preferences.setInteger( "lttQuestDifficulty", 3 );
return "step2";
}
if ( details.contains( "Defeat the Hackletons." ) )
{
Preferences.setString( "lttQuestName", "Wagon Train Escort Wanted" );
Preferences.setInteger( "lttQuestDifficulty", 3 );
return "step3";
}
if ( details.contains( "Defeat Granny Hackleton." ) ) // Check this works!
{
Preferences.setString( "lttQuestName", "Wagon Train Escort Wanted" );
Preferences.setInteger( "lttQuestDifficulty", 3 );
return "step4";
}
return "";
}
public static void setQuestProgress( Quest quest, String progress )
{
if ( quest == null )
{
return;
}
QuestDatabase.setQuestProgress( quest.getPref(), progress );
}
public static void setQuestProgress( String pref, String status )
{
if ( prefToIndex( pref ) == -1 )
{
return;
}
if ( !status.equals( QuestDatabase.STARTED ) && !status.equals( QuestDatabase.FINISHED )
&& !status.contains( "step" ) && !status.equals( QuestDatabase.UNSTARTED ) )
{
return;
}
Preferences.setString( pref, status );
}
public static void resetQuests()
{
for ( int i = 0; i < questLogData.length; ++i )
{
// Don't reset Spring Beach Break quests
if ( !questLogData[ i ][ 0 ].startsWith( "questESl" ) )
{
QuestDatabase.setQuestProgress( questLogData[ i ][ 0 ], QuestDatabase.UNSTARTED );
}
}
Preferences.resetToDefault( "manorDrawerCount" );
Preferences.resetToDefault( "poolSkill" );
Preferences.resetToDefault( "currentExtremity" );
Preferences.resetToDefault( "chasmBridgeProgress" );
Preferences.resetToDefault( "oilPeakProgress" );
Preferences.resetToDefault( "twinPeakProgress" );
Preferences.resetToDefault( "booPeakProgress" );
Preferences.resetToDefault( "desertExploration" );
Preferences.resetToDefault( "zeppelinProtestors" );
Preferences.resetToDefault( "middleChamberUnlock" );
Preferences.resetToDefault( "lowerChamberUnlock" );
Preferences.resetToDefault( "controlRoomUnlock" );
Preferences.resetToDefault( "hiddenApartmentProgress" );
Preferences.resetToDefault( "hiddenHospitalProgress" );
Preferences.resetToDefault( "hiddenOfficeProgress" );
Preferences.resetToDefault( "hiddenBowlingAlleyProgress" );
Preferences.resetToDefault( "blackForestProgress" );
Preferences.resetToDefault( "nsChallenge1" );
Preferences.resetToDefault( "nsChallenge2" );
Preferences.resetToDefault( "nsChallenge3" );
Preferences.resetToDefault( "nsChallenge4" );
Preferences.resetToDefault( "nsChallenge5" );
Preferences.resetToDefault( "nsContestants1" );
Preferences.resetToDefault( "nsContestants2" );
Preferences.resetToDefault( "nsContestants3" );
Preferences.resetToDefault( "nsTowerDoorKeysUsed" );
Preferences.resetToDefault( "maraisDarkUnlock" );
Preferences.resetToDefault( "maraisWildlifeUnlock" );
Preferences.resetToDefault( "maraisCorpseUnlock" );
Preferences.resetToDefault( "maraisWizardUnlock" );
Preferences.resetToDefault( "maraisBeaverUnlock" );
Preferences.resetToDefault( "maraisVillageUnlock" );
Preferences.resetToDefault( "burnoutsDefeated" );
Preferences.resetToDefault( "corralUnlocked" );
Preferences.resetToDefault( "kolhsTotalSchoolSpirited" );
Preferences.resetToDefault( "haciendaLayout" );
Preferences.resetToDefault( "spookyravenRecipeUsed" );
Preferences.resetToDefault( "controlPanel1" );
Preferences.resetToDefault( "controlPanel2" );
Preferences.resetToDefault( "controlPanel3" );
Preferences.resetToDefault( "controlPanel4" );
Preferences.resetToDefault( "controlPanel5" );
Preferences.resetToDefault( "controlPanel6" );
Preferences.resetToDefault( "controlPanel7" );
Preferences.resetToDefault( "controlPanel8" );
Preferences.resetToDefault( "controlPanel9" );
Preferences.resetToDefault( "controlPanelOmega" );
Preferences.resetToDefault( "SHAWARMAInitiativeUnlocked" );
Preferences.resetToDefault( "canteenUnlocked" );
Preferences.resetToDefault( "armoryUnlocked" );
Preferences.resetToDefault( "writingDesksDefeated" );
Preferences.resetToDefault( "palindomeDudesDefeated" );
Preferences.resetToDefault( "warehouseProgress" );
Preferences.resetToDefault( "dinseyAudienceEngagement" );
Preferences.resetToDefault( "dinseyFilthLevel" );
Preferences.resetToDefault( "dinseyFunProgress" );
Preferences.resetToDefault( "dinseyGatorStenchDamage" );
Preferences.resetToDefault( "dinseyGarbagePirate" );
Preferences.resetToDefault( "dinseyNastyBearsDefeated" );
Preferences.resetToDefault( "dinseyRapidPassEnabled" );
Preferences.resetToDefault( "dinseyRollercoasterNext" );
Preferences.resetToDefault( "dinseyRollercoasterStats" );
Preferences.resetToDefault( "dinseySafetyProtocolsLoose" );
Preferences.resetToDefault( "dinseySocialJusticeIProgress" );
Preferences.resetToDefault( "dinseySocialJusticeIIProgress" );
Preferences.resetToDefault( "dinseyTouristsFed" );
Preferences.resetToDefault( "dinseyToxicMultiplier" );
Preferences.resetToDefault( "walfordBucketItem" );
Preferences.resetToDefault( "walfordBucketProgress" );
Preferences.resetToDefault( "lttQuestDifficulty" );
Preferences.resetToDefault( "lttQuestName" );
Preferences.resetToDefault( "lttQuestStageCount" );
Preferences.resetToDefault( "ghostLocation" );
}
public static void handleCouncilText( String responseText )
{
// First, tokenize by <p> (or <P>, if the HTML happened to be coded by a doofus) tags in the responseText, since there can be multiple quests we need to set.
// This ultimately means that each quest gets set n times when it has n paragraphs - technically weird, but not really an issue other than the minor disk I/O.
String[] responseTokens = responseText.split( "<[pP]>" );
String cleanedResponseToken = "";
String cleanedQuestToken = "";
for ( String responseToken : responseTokens )
{
cleanedResponseToken = QuestDatabase.HTML_WHITESPACE.matcher( responseToken ).replaceAll( "" ).toLowerCase();
for ( int i = 0; i < councilData.length; ++i )
{
for ( int j = 2; j < councilData[ i ].length; ++j )
{
// Now, we have to split the councilData entry by <p> tags too.
// Assume that no two paragraphs are identical, otherwise more loop termination logic is needed.
String[] councilTokens = councilData[ i ][ j ].split( "<[pP]>" );
for ( String councilToken : councilTokens )
{
cleanedQuestToken = QuestDatabase.HTML_WHITESPACE.matcher( councilToken ).replaceAll( "" ).toLowerCase();
if ( cleanedResponseToken.contains( cleanedQuestToken ) )
{
setQuestIfBetter( councilData[ i ][ 0 ], councilData[ i ][ 1 ] );
break;
}
}
}
}
}
}
public static void setQuestIfBetter( Quest quest, String progress )
{
if ( quest == null )
{
return;
}
QuestDatabase.setQuestIfBetter( quest.getPref(), progress );
}
private static void setQuestIfBetter( String pref, String status )
{
String currentStatus = Preferences.getString( pref );
boolean shouldSet = false;
if ( currentStatus.equals( QuestDatabase.UNSTARTED ) )
{
shouldSet = true;
}
else if ( currentStatus.equals( QuestDatabase.STARTED ) )
{
if ( status.startsWith( "step" ) || status.equals( QuestDatabase.FINISHED ) )
{
shouldSet = true;
}
}
else if ( currentStatus.startsWith( "step" ) )
{
if ( status.equals( QuestDatabase.FINISHED ) )
{
shouldSet = true;
}
else if ( status.startsWith( "step" ) )
{
try
{
int currentStep = StringUtilities.parseInt( currentStatus.substring( 4 ) );
int nextStep = StringUtilities.parseInt( status.substring( 4 ) );
if ( nextStep > currentStep )
{
shouldSet = true;
}
}
catch ( NumberFormatException e )
{
shouldSet = true;
}
}
}
else if ( currentStatus.equals( QuestDatabase.FINISHED ) )
{
shouldSet = false;
}
else
{
// there was something garbled in the preference. overwrite it.
shouldSet = true;
}
if ( shouldSet )
{
QuestDatabase.setQuestProgress( pref, status );
}
}
public static boolean isQuestStep( Quest quest, String second )
{
if ( quest == null )
{
return false;
}
return Preferences.getString( quest.getPref() ).equals( second );
}
public static boolean isQuestLaterThan( Quest quest, String second )
{
if ( quest == null )
{
return false;
}
return QuestDatabase.isQuestLaterThan( Preferences.getString( quest.getPref() ), second );
}
public static boolean isQuestLaterThan( String first, String second )
{
if ( first.equals( QuestDatabase.UNSTARTED ) )
{
return false;
}
else if ( first.equals( QuestDatabase.STARTED ) )
{
return second.equals( QuestDatabase.UNSTARTED );
}
else if ( first.startsWith( "step" ) )
{
if ( second.equals( QuestDatabase.FINISHED ) )
{
return false;
}
else if ( second.startsWith( "step" ) )
{
try
{
int currentStepInt = StringUtilities.parseInt( first.substring( 4 ) );
int compareToStepInt = StringUtilities.parseInt( second.substring( 4 ) );
if ( currentStepInt > compareToStepInt )
{
return true;
}
else
{
// step we're comparing to is equal or greater
return false;
}
}
catch ( NumberFormatException e )
{
return false;
}
}
else
{
return true;
}
}
else if ( first.equals( QuestDatabase.FINISHED ) )
{
return !second.equals( QuestDatabase.FINISHED );
}
return false;
}
public static boolean isQuestFinished( Quest quest )
{
if ( quest == null )
{
return false;
}
return Preferences.getString( quest.getPref() ).equals( QuestDatabase.FINISHED );
}
public static String questStepAfter( Quest quest, String step )
{
if ( quest == null )
{
return "";
}
return QuestDatabase.questStepAfter( quest.getPref(), step );
}
public static String questStepAfter( String pref, String step )
{
// First thing to do is find which quest we're talking about.
int index = prefToIndex( pref );
if ( index == -1 )
{
return "";
}
// Next, find the number of quest steps
final int totalSteps = questLogData[ index ].length - 2;
if ( totalSteps < 1 )
{
return "";
}
if ( step.equals( QuestDatabase.UNSTARTED ) )
{
return QuestDatabase.STARTED;
}
if ( step.equals( QuestDatabase.STARTED ) )
{
if ( totalSteps > 2 )
{
return "step1";
}
else
{
return QuestDatabase.FINISHED;
}
}
if ( step.startsWith( "step" ) )
{
try
{
int currentStep = StringUtilities.parseInt( step.substring( 4 ) );
int nextStep = currentStep + 1;
if ( nextStep >= totalSteps )
{
return QuestDatabase.FINISHED;
}
else
{
return "step" + nextStep;
}
}
catch ( NumberFormatException e )
{
return "";
}
}
if ( step.equals( QuestDatabase.FINISHED ) )
{
return "";
}
return "";
}
public static void advanceQuest( Quest quest )
{
if ( quest == null )
{
return;
}
QuestDatabase.advanceQuest( quest.getPref() );
}
public static void advanceQuest( String pref )
{
String currentStep = Preferences.getString( pref );
String nextStep = QuestDatabase.questStepAfter( pref, currentStep );
QuestDatabase.setQuestProgress( pref, nextStep );
}
}