/** * 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.session; import java.util.List; import java.util.Locale; import java.util.regex.Matcher; import java.util.regex.Pattern; import net.sourceforge.kolmafia.AdventureResult; import net.sourceforge.kolmafia.KoLAdventure; import net.sourceforge.kolmafia.KoLCharacter; import net.sourceforge.kolmafia.KoLConstants; import net.sourceforge.kolmafia.KoLConstants.MafiaState; import net.sourceforge.kolmafia.KoLmafia; import net.sourceforge.kolmafia.Modifiers; import net.sourceforge.kolmafia.RequestThread; import net.sourceforge.kolmafia.RequestLogger; import net.sourceforge.kolmafia.SpecialOutfit; import net.sourceforge.kolmafia.objectpool.AdventurePool; import net.sourceforge.kolmafia.objectpool.EffectPool; import net.sourceforge.kolmafia.objectpool.ItemPool; import net.sourceforge.kolmafia.objectpool.OutfitPool; import net.sourceforge.kolmafia.persistence.ConcoctionDatabase; import net.sourceforge.kolmafia.persistence.EquipmentDatabase; import net.sourceforge.kolmafia.persistence.QuestDatabase; import net.sourceforge.kolmafia.persistence.QuestDatabase.Quest; import net.sourceforge.kolmafia.preferences.Preferences; import net.sourceforge.kolmafia.request.AWOLQuartermasterRequest; import net.sourceforge.kolmafia.request.AdventureRequest; import net.sourceforge.kolmafia.request.BURTRequest; import net.sourceforge.kolmafia.request.EquipmentRequest; import net.sourceforge.kolmafia.request.GenericRequest; import net.sourceforge.kolmafia.request.OrcChasmRequest; import net.sourceforge.kolmafia.request.QuestLogRequest; import net.sourceforge.kolmafia.request.TavernRequest; import net.sourceforge.kolmafia.request.UseSkillRequest; import net.sourceforge.kolmafia.session.EquipmentManager; import net.sourceforge.kolmafia.session.InventoryManager; import net.sourceforge.kolmafia.session.ResultProcessor; import net.sourceforge.kolmafia.session.TavernManager; import net.sourceforge.kolmafia.utilities.StringUtilities; import net.sourceforge.kolmafia.webui.BarrelDecorator; public class QuestManager { private static final Pattern ORE_PATTERN = Pattern.compile( "(asbestos|linoleum|chrome) ore[\\. ]" ); private static final Pattern BATHOLE_PATTERN = Pattern.compile( "bathole_(\\d)\\.gif" ); private static final Pattern DRAWER_PATTERN = Pattern.compile( "search through <b>(\\d+)</b> drawers" ); private static final Pattern LIGHTER_PATTERN = Pattern.compile( "group of (\\d+) nearby protesters do the same" ); private static final Pattern TACO_FISH_PATTERN = Pattern.compile( "gain (\\d+) taco fish meat" ); private static final Pattern LOWER_CHAMBER_PATTERN = Pattern.compile( "action=pyramid_state(\\d+)" ); private static final Pattern GORE_PATTERN = Pattern.compile( "(\\d+) pounds of (?:the gore|gore)" ); private static final Pattern TOURIST_PATTERN = Pattern.compile( "and the (\\d+) tourists in front" ); private static final Pattern WALFORD_PATTERN = Pattern.compile( "\\(Walford's bucket filled by (?:an additional |)(\\d+)%\\)" ); private static final Pattern SNOWMAN_PATTERN = Pattern.compile( "otherimages/combatsnowman/" ); private static final Pattern PARANORMAL_PATTERN = Pattern.compile( ""Paranormal disturbance reported (.*?)."" ); public static final void handleQuestChange( GenericRequest request ) { String redirectLocation = request.redirectLocation; if ( redirectLocation != null ) { // If this request redirected to a fight or choice and // was automated, there no response text here return; } String responseText = request.responseText; if ( responseText == null || responseText.equals( "" ) ) { // Similarly, don't process an empty response return; } String location = request.getURLString(); String locationId = request.getFormField( "snarfblat" ); if ( locationId == null ) { locationId = ""; } if ( location.startsWith( "adventure" ) ) { if ( locationId.equals( AdventurePool.ROAD_TO_WHITE_CITADEL_ID ) ) { handleWhiteCitadelChange( responseText ); } else if ( locationId.equals( AdventurePool.WHITEYS_GROVE_ID ) ) { handleWhiteysGroveChange( responseText ); } else if ( locationId.equals( AdventurePool.BARROOM_BRAWL_ID ) ) { handleBarroomBrawlChange( responseText ); } else if ( locationId.equals( AdventurePool.KNOB_SHAFT_ID ) ) { handleKnobShaftChange( responseText ); } else if ( locationId.equals( AdventurePool.EXTREME_SLOPE_ID ) ) { handleExtremityChange( responseText ); } else if ( locationId.equals( AdventurePool.ICY_PEAK_ID ) ) { handleIcyPeakChange( responseText ); } else if ( locationId.equals( AdventurePool.AIRSHIP_ID ) || locationId.equals( AdventurePool.CASTLE_BASEMENT_ID ) || locationId.equals( AdventurePool.CASTLE_GROUND_ID ) || locationId.equals( AdventurePool.CASTLE_TOP_ID ) ) { handleBeanstalkChange( location, responseText ); } else if ( locationId.equals( AdventurePool.ZEPPELIN_PROTESTORS_ID ) ) { handleZeppelinMobChange( responseText ); } else if ( locationId.equals( AdventurePool.RED_ZEPPELIN_ID ) ) { handleZeppelinChange( responseText ); } else if ( locationId.equals( AdventurePool.PALINDOME_ID ) ) { QuestDatabase.setQuestIfBetter( Quest.PALINDOME, QuestDatabase.STARTED ); } else if ( locationId.equals( AdventurePool.POOP_DECK_ID ) ) { handlePoopDeckChange( responseText ); } else if ( locationId.equals( AdventurePool.HAUNTED_BALLROOM_ID ) ) { handleManorSecondFloorChange( location, responseText ); } else if ( locationId.equals( AdventurePool.UPPER_CHAMBER_ID ) || locationId.equals( AdventurePool.MIDDLE_CHAMBER_ID ) ) { handlePyramidChange( location, responseText ); } else if ( locationId.equals( AdventurePool.SLOPPY_SECONDS_DINER_ID ) || locationId.equals( AdventurePool.FUN_GUY_MANSION_ID ) || locationId.equals( AdventurePool.YACHT_ID ) || locationId.equals( AdventurePool.DR_WEIRDEAUX_ID ) || locationId.equals( AdventurePool.SECRET_GOVERNMENT_LAB_ID ) || locationId.equals( AdventurePool.DEEP_DARK_JUNGLE_ID ) || locationId.equals( AdventurePool.BARF_MOUNTAIN_ID ) || locationId.equals( AdventurePool.GARBAGE_BARGES_ID ) || locationId.equals( AdventurePool.TOXIC_TEACUPS_ID ) || locationId.equals( AdventurePool.LIQUID_WASTE_SLUICE_ID ) || locationId.equals( AdventurePool.SMOOCH_ARMY_HQ_ID ) || locationId.equals( AdventurePool.VELVET_GOLD_MINE_ID ) || locationId.equals( AdventurePool.LAVACO_LAMP_FACTORY_ID ) || locationId.equals( AdventurePool.BUBBLIN_CALDERA_ID ) || locationId.equals( AdventurePool.ICE_HOTEL_ID ) || locationId.equals( AdventurePool.VYKEA_ID ) || locationId.equals( AdventurePool.ICE_HOLE_ID ) ) { handleAirportChange( location, responseText ); } else if ( locationId.equals( AdventurePool.MARINARA_TRENCH_ID ) || locationId.equals( AdventurePool.ANENOME_MINE_ID ) || locationId.equals( AdventurePool.DIVE_BAR_ID ) || locationId.equals( AdventurePool.MERKIN_OUTPOST_ID ) || locationId.equals( AdventurePool.CALIGINOUS_ABYSS_ID ) ) { handleSeaChange( location, responseText ); } else if ( locationId.equals( AdventurePool.GINGERBREAD_CIVIC_CENTER_ID ) || locationId.equals( AdventurePool.GINGERBREAD_TRAIN_STATION_ID ) || locationId.equals( AdventurePool.GINGERBREAD_INDUSTRIAL_ZONE_ID ) || locationId.equals( AdventurePool.GINGERBREAD_RETAIL_DISTRICT_ID ) || locationId.equals( AdventurePool.GINGERBREAD_SEWERS_ID ) ) { handleGingerbreadCityChange( location, responseText ); } else if ( locationId.equals( AdventurePool.SPACEGATE_ID ) ) { handleSpacegateChange( location, responseText ); } else if ( KoLCharacter.getInebriety() > 25 ) { handleSneakyPeteChange( responseText ); } } else if ( location.startsWith( "beanstalk" ) ) { if ( responseText.contains( "airship.gif" ) ) { QuestDatabase.setQuestIfBetter( Quest.GARBAGE, "step1" ); } } else if ( location.startsWith( "barrel" ) ) { BarrelDecorator.parseResponse( location, responseText ); } else if ( location.startsWith( "canadia" ) ) { handleCanadiaChange( location, responseText ); } else if ( location.startsWith( "choice.php" ) && location.contains( "forceoption=0" ) ) { // This can have no active choice options and therefore // won't be interpreted by ChoiceManager parseSpacegateTerminal( responseText, false ); } else if ( location.startsWith( "cobbsknob.php" ) ) { if ( location.contains( "action=cell37" ) ) { handleCell37( responseText ); } } else if ( location.startsWith( "council" ) ) { handleCouncilChange( responseText ); } else if ( location.startsWith( "friars" ) ) { handleFriarsChange( responseText ); } else if ( location.startsWith( "guild" ) ) { handleGuildChange( responseText ); } else if ( location.contains( "whichplace=highlands" ) || locationId.equals( AdventurePool.ABOO_PEAK_ID ) || locationId.equals( AdventurePool.OIL_PEAK_ID ) ) { handleHighlandsChange( location, responseText ); } else if ( location.startsWith( "inv_use" ) ) { if ( location.contains( "whichitem=" + ItemPool.AWOL_COMMENDATION ) ) { AWOLQuartermasterRequest.parseResponse( location, responseText ); } else if ( location.contains( "whichitem=" + ItemPool.BURT ) ) { BURTRequest.parseResponse( location, responseText ); } } else if ( location.startsWith( "manor" ) ) { handleManorFirstFloorChange( location, responseText ); } else if ( location.startsWith( "monkeycastle" ) ) { handleSeaChange( location, responseText ); } else if ( location.startsWith( "pandamonium" ) ) { // Quest starts the very instant you click on pandamonium.php QuestDatabase.setQuestIfBetter( Quest.AZAZEL, QuestDatabase.STARTED ); } else if ( location.startsWith( "place.php" ) ) { if ( location.contains( "whichplace=airport" ) ) { handleAirportChange( location, responseText ); } else if ( location.contains( "whichplace=bathole" ) ) { handleBatholeChange( responseText ); } else if ( location.contains( "whichplace=desertbeach" ) ) { if ( location.contains( "action=db_pyramid1" ) ) { handlePyramidChange( location, responseText ); } else { handleBeachChange( responseText ); } } else if ( location.contains( "whichplace=gingerbreadcity" ) ) { handleGingerbreadCityChange( location, responseText ); } else if ( location.contains( "whichplace=manor1" ) ) { handleManorFirstFloorChange( location, responseText ); } else if ( location.contains( "whichplace=manor2" ) ) { handleManorSecondFloorChange( location, responseText ); } else if ( location.contains( "whichplace=manor3" ) && responseText.contains( "Spookyraven Manor Third Floor" ) ) { // If here at all, Necklace and Dance quests are complete and second floor open QuestDatabase.setQuestIfBetter( Quest.SPOOKYRAVEN_NECKLACE, QuestDatabase.FINISHED ); QuestDatabase.setQuestIfBetter( Quest.SPOOKYRAVEN_DANCE, QuestDatabase.FINISHED ); // Legacy code support Preferences.setInteger( "lastSecondFloorUnlock", KoLCharacter.getAscensions() ); } else if ( location.contains( "whichplace=manor4" ) && responseText.contains( "Spookyraven Manor Cellar" ) ) { // If here at all, Necklace and Dance quests are complete and second floor and basement open QuestDatabase.setQuestIfBetter( Quest.SPOOKYRAVEN_NECKLACE, QuestDatabase.FINISHED ); QuestDatabase.setQuestIfBetter( Quest.SPOOKYRAVEN_DANCE, QuestDatabase.FINISHED ); QuestDatabase.setQuestIfBetter( Quest.MANOR, "step1" ); // Legacy code support Preferences.setInteger( "lastSecondFloorUnlock", KoLCharacter.getAscensions() ); if ( responseText.contains( "Cold as ice and twice as smooth" ) ) { QuestDatabase.setQuestProgress( Quest.MANOR, QuestDatabase.FINISHED ); ResultProcessor.removeItem( ItemPool.ED_EYE ); if ( InventoryManager.getCount( ItemPool.ED_FATS_STAFF ) == 0 && InventoryManager.getCount( ItemPool.ED_AMULET ) == 0 ) { QuestDatabase.setQuestProgress( Quest.MACGUFFIN, QuestDatabase.FINISHED ); } } } else if ( location.contains( "whichplace=marais" ) ) { handleMaraisChange( responseText ); } else if ( location.contains( "whichplace=mclargehuge" ) ) { if ( location.contains( "action=trappercabin" ) ) { handleTrapperChange( responseText ); } else if ( location.contains( "action=cloudypeak" ) ) { handleMcLargehugeChange( responseText ); } } else if ( location.contains( "whichplace=orc_chasm" ) ) { handleChasmChange( responseText ); } else if ( location.contains( "whichplace=palindome" ) ) { handlePalindomeChange( location, responseText ); } else if ( location.contains( "whichplace=plains" ) ) { handlePlainsChange( responseText ); } else if ( location.contains( "whichplace=pyramid" ) ) { handlePyramidChange( location, responseText ); } else if ( location.contains( "whichplace=sea_oldman" ) ) { handleSeaChange( location, responseText ); } else if ( location.contains( "whichplace=spacegate" ) ) { handleSpacegateChange( location, responseText ); } else if ( location.endsWith( "whichplace=town" ) ) { // don't catch town_wrong, town_right, or other places handleTownChange( location, responseText ); } else if ( location.contains( "whichplace=woods" ) ) { handleWoodsChange( location, responseText ); } else if ( location.contains( "whichplace=zeppelin" ) ) { if ( responseText.contains( "zep_mob1.gif" ) ) { QuestDatabase.setQuestIfBetter( Quest.RON, "step2" ); } } } else if ( location.startsWith( "questlog" ) ) { QuestLogRequest.registerQuests( false, location, responseText ); } else if ( location.startsWith( "seafloor" ) ) { handleSeaChange( location, responseText ); } else if ( location.startsWith( "tavern" ) ) { TavernManager.handleTavernChange( responseText ); } else if ( location.startsWith( "trickortreat" ) ) { handleTrickOrTreatingChange( responseText ); } else if ( location.startsWith( "wham" ) ) { if ( responseText.contains( "Congratulations! You solved the case" ) ) { Preferences.increment( "_detectiveCasesCompleted" ); } } // Obsolete. Sigh. else if ( location.startsWith( "generate15" ) ) { // You slide the last tile into place ... if ( AdventureRequest.registerDemonName( "Strange Cube", responseText ) || responseText.contains( "slide the last tile" ) ) { ResultProcessor.processItem( ItemPool.STRANGE_CUBE, -1 ); } } } private static void handleMcLargehugeChange( String responseText ) { if ( responseText.contains( "you spy a crude stone staircase" ) || responseText.contains( "notice a set of crude carved stairs" ) ) { QuestDatabase.setQuestIfBetter( Quest.TRAPPER, "step3" ); Preferences.setInteger( "currentExtremity", 0 ); } } private static void handleBarroomBrawlChange( String responseText ) { if ( responseText.contains( "Jackin' the Jukebox" ) ) { QuestDatabase.setQuestProgress( Quest.CLANCY, "step1" ); } } private static void handleKnobShaftChange( String responseText ) { if ( responseText.contains( "A Miner Variation" ) ) { QuestDatabase.setQuestProgress( Quest.CLANCY, "step3" ); } } private static void handleIcyPeakChange( String responseText ) { if ( responseText.contains( "Mercury Rising" ) ) { QuestDatabase.setQuestProgress( Quest.CLANCY, "step7" ); } } private static void handleTownChange( final String location, String responseText ) { QuestManager.handleTimeTower( responseText.contains( "town_tower" ) ); QuestManager.handleEldritchFissure( responseText.contains( "town_eincursion" ) ); QuestManager.handleEldritchHorror( responseText.contains( "town_eicfight2" ) ); if ( location.contains( "town_wrong" ) && !location.contains( "action" ) && !KoLCharacter.inBadMoon() ) { Preferences.setBoolean( "hasDetectiveSchool", responseText.contains( "Precinct" ) ); } } public static void handleTimeTower( final boolean available ) { if ( Preferences.getBoolean( "timeTowerAvailable" ) == available ) { return; } Preferences.setBoolean( "timeTowerAvailable", available ); // time-twitching toolbelt is a free pull if the time tower is // available. Place it in correct storage list. Modifiers.getModifiers( "Item", "time-twitching toolbelt" ); AdventureResult toolbelt = ItemPool.get( ItemPool.TIME_TWITCHING_TOOLBELT, 1 ); List<AdventureResult> source = available ? KoLConstants.storage : KoLConstants.freepulls; List<AdventureResult> dest = available ? KoLConstants.freepulls : KoLConstants.storage; int index = source.indexOf( toolbelt ); if ( index > -1 ) { AdventureResult item = source.get( index ); source.remove( item ); dest.add( item ); } ConcoctionDatabase.setRefreshNeeded( false ); } public static void handleEldritchFissure( final boolean available ) { if ( Preferences.getBoolean( "eldritchFissureAvailable" ) == available ) { return; } Preferences.setBoolean( "eldritchFissureAvailable", available ); } public static void handleEldritchHorror( final boolean available ) { if ( Preferences.getBoolean( "eldritchHorrorAvailable" ) == available ) { return; } Preferences.setBoolean( "eldritchHorrorAvailable", available ); } private static void handleGuildChange( final String responseText ) { if ( responseText.contains( "South of the Border" ) ) { QuestDatabase.setQuestIfBetter( Quest.MEATCAR, QuestDatabase.FINISHED ); } if ( responseText.contains( "not recovered the Epic Weapon yet" ) || responseText.contains( "The Tomb is within the Misspelled Cemetery" ) || responseText.contains( "the Tomb, which is within the Misspelled Cemetery" ) ) { QuestDatabase.setQuestProgress( Quest.NEMESIS, QuestDatabase.STARTED ); } if ( responseText.contains( "Clownlord Beelzebozo" ) ) { QuestDatabase.setQuestProgress( Quest.NEMESIS, "step5" ); } if ( responseText.contains( "Meatsmithing hammer" ) ) { QuestDatabase.setQuestIfBetter( Quest.NEMESIS, "step7" ); } if ( QuestDatabase.isQuestStep( Quest.NEMESIS, "step8" ) ) { QuestDatabase.setQuestProgress( Quest.NEMESIS, "step9" ); } if ( responseText.contains( "in the Big Mountains" ) && !responseText.contains( "not the required mettle to defeat" ) ) { QuestDatabase.setQuestProgress( Quest.NEMESIS, "step10" ); } if ( QuestDatabase.isQuestStep( Quest.NEMESIS, "step16" ) ) { QuestDatabase.setQuestProgress( Quest.NEMESIS, "step17" ); } } private static void handlePoopDeckChange( final String responseText ) { if ( responseText.contains( "unlocks a padlock on a trap door" ) ) { QuestDatabase.setQuestProgress( Quest.PIRATE, QuestDatabase.FINISHED ); } } private static void handleWhiteysGroveChange( final String responseText ) { if ( responseText.contains( "It's A Sign!" ) ) { QuestDatabase.setQuestIfBetter( Quest.CITADEL, "step1" ); } } private static void handleWhiteCitadelChange( final String responseText ) { if ( responseText.contains( "I Guess They Were the Existential Blues Brothers" ) ) { QuestDatabase.setQuestProgress( Quest.CITADEL, "step3" ); } else { QuestDatabase.setQuestIfBetter( Quest.CITADEL, "step2" ); } } private static void handleGingerbreadCityChange( final String location, final String responseText ) { if ( !Preferences.getBoolean( "gingerbreadCityAvailable" ) ) { Preferences.setBoolean( "_gingerbreadCityToday", true ); } if ( responseText.contains( "snarfblat=480" ) ) { Preferences.setBoolean( "gingerRetailUnlocked", true ); } if ( responseText.contains( "snarfblat=481" ) ) { Preferences.setBoolean( "gingerSewersUnlocked", true ); } if ( responseText.contains( "digitalclock.gif" ) ) { Preferences.setBoolean( "gingerAdvanceClockUnlocked", true ); } } private static void handleSpacegateChange( final String location, final String responseText ) { if ( !Preferences.getBoolean( "spacegateAlways" ) ) { Preferences.setBoolean( "_spacegateToday", true ); } } private static void handleManorFirstFloorChange( final String location, final String responseText ) { if ( location.contains( "action=manor1_ladys" ) ) { if ( responseText.contains( "ghostly copy of the necklace" ) ) { QuestDatabase.setQuestProgress( Quest.SPOOKYRAVEN_NECKLACE, QuestDatabase.FINISHED ); } } // Derive quest status from available rooms if ( responseText.contains( "snarfblat=" + AdventurePool.HAUNTED_KITCHEN_ID ) ) { QuestDatabase.setQuestIfBetter( Quest.SPOOKYRAVEN_NECKLACE, QuestDatabase.STARTED ); } if ( responseText.contains( "whichplace=manor2" ) ) { QuestDatabase.setQuestIfBetter( Quest.SPOOKYRAVEN_NECKLACE, QuestDatabase.FINISHED ); // Legacy code support Preferences.setInteger( "lastSecondFloorUnlock", KoLCharacter.getAscensions() ); } if ( responseText.contains( "whichplace=manor4" ) ) { QuestDatabase.setQuestIfBetter( Quest.MANOR, "step1" ); } } private static void handleManorSecondFloorChange( final String location, final String responseText ) { if ( !responseText.contains( "Spookyraven Manor Second Floor" ) ) { return; } if ( location.contains( "action=manor2_ladys" ) ) { if ( responseText.contains( "just want to dance" ) ) { QuestDatabase.setQuestProgress( Quest.SPOOKYRAVEN_DANCE, "step1" ); } } if ( location.contains( AdventurePool.HAUNTED_BALLROOM_ID ) ) { if ( responseText.contains( "Having a Ball in the Ballroom" ) ) { QuestDatabase.setQuestProgress( Quest.SPOOKYRAVEN_DANCE, QuestDatabase.FINISHED ); } } // Derive quest status from available rooms if ( responseText.contains( "snarfblat=" + AdventurePool.HAUNTED_BATHROOM_ID ) ) { QuestDatabase.setQuestIfBetter( Quest.SPOOKYRAVEN_DANCE, "step1" ); } if ( responseText.contains( "snarfblat=" + AdventurePool.HAUNTED_BALLROOM_ID ) ) { QuestDatabase.setQuestIfBetter( Quest.SPOOKYRAVEN_DANCE, "step3" ); } if ( responseText.contains( "whichplace=manor3" ) ) { QuestDatabase.setQuestIfBetter( Quest.SPOOKYRAVEN_DANCE, QuestDatabase.FINISHED ); } // If here at all, Necklace quest is complete QuestDatabase.setQuestIfBetter( Quest.SPOOKYRAVEN_NECKLACE, QuestDatabase.FINISHED ); // Legacy code support Preferences.setInteger( "lastSecondFloorUnlock", KoLCharacter.getAscensions() ); } public static final void handlePyramidChange( final String location, final String responseText ) { if ( location.contains( "action=db_pyramid1" ) ) { // Unlock Pyramid if ( responseText.contains( "the model bursts into flames and is quickly consumed" ) ) { QuestDatabase.setQuestProgress( Quest.PYRAMID, QuestDatabase.STARTED ); } } else if ( location.contains( AdventurePool.UPPER_CHAMBER_ID ) ) { if ( responseText.contains( "Down Dooby-Doo Down Down" ) ) { // Open Middle Chamber Preferences.setBoolean( "middleChamberUnlock", true ); QuestDatabase.setQuestProgress( Quest.PYRAMID, "step1" ); } } else if ( location.contains( AdventurePool.MIDDLE_CHAMBER_ID ) ) { if ( responseText.contains( "Further Down Dooby-Doo Down Down" ) ) { // Open Lower Chamber Preferences.setBoolean( "lowerChamberUnlock", true ); QuestDatabase.setQuestProgress( Quest.PYRAMID, "step2" ); } else if ( responseText.contains( "Under Control" ) ) { // Open Control Room Preferences.setBoolean( "controlRoomUnlock", true ); Preferences.setInteger( "pyramidPosition", 1 ); QuestDatabase.setQuestProgress( Quest.PYRAMID, "step3" ); } else if ( responseText.contains( "Don't You Know Who I Am?" ) ) { QuestDatabase.setQuestProgress( Quest.CLANCY, QuestDatabase.FINISHED ); } // If here, must have unlocked middle chamber Preferences.setBoolean( "middleChamberUnlock", true ); QuestDatabase.setQuestIfBetter( Quest.PYRAMID, "step1" ); } else if ( location.contains( "whichplace=pyramid" ) ) { // Verify settings based on images displayed, in case above steps were missed QuestDatabase.setQuestIfBetter( Quest.PYRAMID, QuestDatabase.STARTED ); if ( responseText.contains( "pyramid_middle.gif" ) ) { Preferences.setBoolean( "middleChamberUnlock", true ); QuestDatabase.setQuestIfBetter( Quest.PYRAMID, "step1" ); } if ( responseText.contains( "pyramid_bottom" ) ) { Preferences.setBoolean( "lowerChamberUnlock", true ); QuestDatabase.setQuestIfBetter( Quest.PYRAMID, "step2" ); } if ( responseText.contains( "pyramid_controlroom.gif" ) ) { Preferences.setBoolean( "controlRoomUnlock", true ); QuestDatabase.setQuestIfBetter( Quest.PYRAMID, "step3" ); } Matcher LowerChamberMatcher = QuestManager.LOWER_CHAMBER_PATTERN.matcher( responseText ); if ( LowerChamberMatcher.find() ) { Preferences.setInteger( "pyramidPosition", StringUtilities.parseInt( LowerChamberMatcher.group( 1 ) ) ); } if ( responseText.contains( "action=pyramid_state1a" ) ) { Preferences.setBoolean( "pyramidBombUsed", true ); } // Lower chamber parsing if ( location.contains( "action=pyramid_state" ) ) { if ( responseText.contains( "the rubble is gone" ) ) { Preferences.setBoolean( "pyramidBombUsed", true ); ResultProcessor.processItem( ItemPool.ANCIENT_BOMB, -1 ); } } } return; } public static final void handleAirportChange( final String location, final String responseText ) { // Check Cold settings if ( !Preferences.getBoolean( "coldAirportAlways" ) ) { // Detect if Airport is open today if ( location.contains( AdventurePool.ICE_HOTEL_ID ) || location.contains( AdventurePool.VYKEA_ID ) || location.contains( AdventurePool.ICE_HOLE_ID ) || location.contains( "whichplace=airport_cold" ) ) { Preferences.setBoolean( "_coldAirportToday", true ); } else if ( location.contains( "whichplace=airport" ) ) { if ( responseText.contains( "whichplace=airport_cold" ) ) { Preferences.setBoolean( "_coldAirportToday", true ); } } } // Check Hot settings if ( !Preferences.getBoolean( "hotAirportAlways" ) ) { // Detect if Airport is open today if ( location.contains( AdventurePool.SMOOCH_ARMY_HQ_ID ) || location.contains( AdventurePool.VELVET_GOLD_MINE_ID ) || location.contains( AdventurePool.LAVACO_LAMP_FACTORY_ID ) || location.contains( AdventurePool.BUBBLIN_CALDERA_ID ) || location.contains( "whichplace=airport_hot" ) ) { Preferences.setBoolean( "_hotAirportToday", true ); } else if ( location.contains( "whichplace=airport" ) ) { if ( responseText.contains( "whichplace=airport_hot" ) ) { Preferences.setBoolean( "_hotAirportToday", true ); } } } // Check Sleaze settings if ( !Preferences.getBoolean( "sleazeAirportAlways" ) ) { // Detect if Airport is open today if ( location.contains( AdventurePool.FUN_GUY_MANSION_ID ) || location.contains( AdventurePool.SLOPPY_SECONDS_DINER_ID ) || location.contains( AdventurePool.YACHT_ID ) || location.contains( "whichplace=airport_sleaze" ) ) { Preferences.setBoolean( "_sleazeAirportToday", true ); } else if ( location.contains( "whichplace=airport" ) ) { if ( responseText.contains( "whichplace=airport_sleaze" ) ) { Preferences.setBoolean( "_sleazeAirportToday", true ); } } } // Check Spooky settings if ( !Preferences.getBoolean( "spookyAirportAlways" ) ) { // Detect if Airport is open today if ( location.contains( AdventurePool.DR_WEIRDEAUX_ID ) || location.contains( AdventurePool.SECRET_GOVERNMENT_LAB_ID ) || location.contains( AdventurePool.DEEP_DARK_JUNGLE_ID ) || location.contains( "whichplace=airport_spooky" ) ) { Preferences.setBoolean( "_spookyAirportToday", true ); } else if ( location.contains( "whichplace=airport" ) ) { if ( responseText.contains( "whichplace=airport_spooky" ) ) { Preferences.setBoolean( "_spookyAirportToday", true ); } } } // Check Stench settings if ( !Preferences.getBoolean( "stenchAirportAlways" ) ) { // Detect if Airport is open today if ( location.contains( AdventurePool.BARF_MOUNTAIN_ID ) || location.contains( AdventurePool.GARBAGE_BARGES_ID ) || location.contains( AdventurePool.TOXIC_TEACUPS_ID ) || location.contains( AdventurePool.LIQUID_WASTE_SLUICE_ID ) || location.contains( "whichplace=airport_stench" ) ) { Preferences.setBoolean( "_stenchAirportToday", true ); } else if ( location.contains( "whichplace=airport" ) ) { if ( responseText.contains( "whichplace=airport_stench" ) ) { Preferences.setBoolean( "_stenchAirportToday", true ); } } } // Detect Bunker state if ( location.contains( "whichplace=airport_spooky_bunker" ) ) { if ( responseText.contains( "action=si_shop1locked" ) ) { Preferences.setBoolean( "SHAWARMAInitiativeUnlocked", false ); } if ( responseText.contains( "action=si_shop2locked" ) ) { Preferences.setBoolean( "canteenUnlocked", false ); } if ( responseText.contains( "action=si_shop3locked" ) ) { Preferences.setBoolean( "armoryUnlocked", false ); } if ( responseText.contains( "whichshop=si_shop1" ) ) { Preferences.setBoolean( "SHAWARMAInitiativeUnlocked", true ); } if ( responseText.contains( "whichshop=si_shop2" ) ) { Preferences.setBoolean( "canteenUnlocked", true ); } if ( responseText.contains( "whichshop=si_shop3" ) ) { Preferences.setBoolean( "armoryUnlocked", true ); } if ( responseText.contains( "find the door to the secret government sandwich shop and use the keycard" ) ) { Preferences.setBoolean( "SHAWARMAInitiativeUnlocked", true ); ResultProcessor.removeItem( ItemPool.SHAWARMA_KEYCARD ); } if ( responseText.contains( "find a door with a bottle-shaped icon on it, zip the keycard through the reader" ) ) { Preferences.setBoolean( "canteenUnlocked", true ); ResultProcessor.removeItem( ItemPool.BOTTLE_OPENER_KEYCARD ); } if ( responseText.contains( "insert the keycard and the door slides open" ) ) { Preferences.setBoolean( "armoryUnlocked", true ); ResultProcessor.removeItem( ItemPool.ARMORY_KEYCARD ); } } return; } private static void handleWoodsChange( final String location, final String responseText ) { if ( responseText.contains( "wcroad.gif" ) ) { QuestDatabase.setQuestIfBetter( Quest.CITADEL, "step1" ); } if ( location.contains( "action=woods_dakota" ) ) { if ( responseText.contains( "need you to pick up a couple things for me" ) ) { QuestDatabase.setQuestProgress( Quest.TEMPLE, QuestDatabase.STARTED ); } else if ( responseText.contains( "make a note of the temple's location" ) ) { QuestDatabase.setQuestProgress( Quest.TEMPLE, QuestDatabase.FINISHED ); Preferences.setInteger( "lastTempleUnlock", KoLCharacter.getAscensions() ); } } else if ( location.contains( "action=woods_hippy" ) && responseText.contains( "You've got this cool boat" ) ) { QuestDatabase.setQuestProgress( Quest.HIPPY, QuestDatabase.FINISHED ); } // If we see the Hidden Temple, mark it as unlocked if ( responseText.contains( "temple.gif" ) ) { Preferences.setInteger( "lastTempleUnlock", KoLCharacter.getAscensions() ); } // If we see the Black Market, update Black Market quest if ( responseText.contains( "blackmarket.gif" ) ) { QuestDatabase.setQuestIfBetter( Quest.BLACK, "step2" ); QuestDatabase.setQuestIfBetter( Quest.MACGUFFIN, "step1" ); Preferences.setInteger( "blackForestProgress", 5 ); } // If we see the link to the empty Black Market, Wu Tang has been defeated if ( responseText.contains( "action=emptybm" ) ) { Preferences.setInteger( "lastWuTangDefeated", KoLCharacter.getAscensions() ); } } private static void handleBatholeChange( final String responseText ) { Matcher m = BATHOLE_PATTERN.matcher( responseText ); if ( !m.find() ) { return; } int image = StringUtilities.parseInt( m.group( 1 ) ); String status = ""; if ( image == 1 ) { status = QuestDatabase.STARTED; } else if ( image == 2 ) { status = "step1"; } else if ( image == 3 ) { status = "step2"; } else if ( image == 4 ) { status = "step3"; } else if ( image == 5 ) { status = "step4"; } QuestDatabase.setQuestIfBetter( Quest.BAT, status ); } private static final void handleSneakyPeteChange( final String responseText ) { if ( responseText.contains( "You hand him your button and take his glowstick" ) ) { EquipmentManager.discardEquipment( ItemPool.NOVELTY_BUTTON ); return; } if ( responseText.contains( "Ah, man, you dropped your crown back there!" ) ) { EquipmentManager.discardEquipment( ItemPool.TATTERED_PAPER_CROWN ); return; } } private static final void handleTrickOrTreatingChange( final String responseText ) { if ( responseText.contains( "pull the pumpkin off of your head" ) ) { EquipmentManager.discardEquipment( ItemPool.PUMPKINHEAD_MASK ); return; } if ( responseText.contains( "gick all over your mummy costume" ) ) { EquipmentManager.discardEquipment( ItemPool.MUMMY_COSTUME ); return; } if ( responseText.contains( "unzipping the mask and throwing it behind you" ) ) { EquipmentManager.discardEquipment( ItemPool.WOLFMAN_MASK ); return; } if ( responseText.contains( "Right on, brah. Here, have some gum." ) ) { ResultProcessor.processItem( ItemPool.RUSSIAN_ICE, -1 ); return; } } private static final void handleCell37( final String responseText ) { if ( responseText.contains( "scientists should have a file on me" ) || responseText.contains( "Did you find that file yet" ) ) { QuestDatabase.setQuestProgress( Quest.ESCAPE, QuestDatabase.STARTED ); } // You pass the folder through the little barred window, and hear Subject 37 flipping through the pages if ( responseText.contains( "pass the folder through" ) ) { ResultProcessor.processItem( ItemPool.SUBJECT_37_FILE, -1 ); // You may already have the item you are sent to get if ( InventoryManager.getCount( ItemPool.GOTO ) > 0 ) { QuestDatabase.setQuestProgress( Quest.ESCAPE, "step3" ); } else { QuestDatabase.setQuestProgress( Quest.ESCAPE, "step2" ); } } // You pass the GOTO through the window, and Subject 37 thanks you. if ( responseText.contains( "pass the GOTO through" ) ) { ResultProcessor.processItem( ItemPool.GOTO, -1 ); // You may already have the item you are sent to get if ( InventoryManager.getCount( ItemPool.WEREMOOSE_SPIT ) > 0 ) { QuestDatabase.setQuestProgress( Quest.ESCAPE, "step5" ); } else { QuestDatabase.setQuestProgress( Quest.ESCAPE, "step4" ); } } // You pass the little vial of of weremoose spit through the window. if ( responseText.contains( "pass the little vial" ) ) { ResultProcessor.processItem( ItemPool.WEREMOOSE_SPIT, -1 ); // You may already have the item you are sent to get if ( InventoryManager.getCount( ItemPool.ABOMINABLE_BLUBBER ) > 0 ) { QuestDatabase.setQuestProgress( Quest.ESCAPE, "step7" ); } else { QuestDatabase.setQuestProgress( Quest.ESCAPE, "step6" ); } } // You hand Subject 37 the glob of abominable blubber. if ( responseText.contains( "hand Subject 37 the glob" ) ) { ResultProcessor.processItem( ItemPool.ABOMINABLE_BLUBBER, -1 ); QuestDatabase.setQuestProgress( Quest.ESCAPE, QuestDatabase.FINISHED ); } } private static final void handleFriarsChange( final String responseText ) { // "Thank you, Adventurer." if ( responseText.contains( "Thank you" ) || responseText.contains( "Please return to us if there's ever anything we can do for you in return" ) ) { ResultProcessor.processItem( ItemPool.DODECAGRAM, -1 ); ResultProcessor.processItem( ItemPool.CANDLES, -1 ); ResultProcessor.processItem( ItemPool.BUTTERKNIFE, -1 ); int knownAscensions = Preferences.getInteger( "knownAscensions" ); Preferences.setInteger( "lastFriarCeremonyAscension", knownAscensions ); QuestDatabase.setQuestProgress( Quest.FRIAR, QuestDatabase.FINISHED ); if ( KoLmafia.isAdventuring() ) { KoLmafia.updateDisplay( MafiaState.PENDING, "Taint cleansed." ); } } } private static final void handleChasmChange( final String responseText ) { if ( responseText.contains( "Huzzah! The bridge is finished!" ) ) { ResultProcessor.processItem( ItemPool.MORNINGWOOD_PLANK, -1 * InventoryManager.getCount( ItemPool.MORNINGWOOD_PLANK ) ); ResultProcessor.processItem( ItemPool.HARDWOOD_PLANK, -1 * InventoryManager.getCount( ItemPool.HARDWOOD_PLANK ) ); ResultProcessor.processItem( ItemPool.WEIRDWOOD_PLANK, -1 * InventoryManager.getCount( ItemPool.WEIRDWOOD_PLANK ) ); ResultProcessor.processItem( ItemPool.THICK_CAULK, -1 * InventoryManager.getCount( ItemPool.THICK_CAULK ) ); ResultProcessor.processItem( ItemPool.LONG_SCREW, -1 * InventoryManager.getCount( ItemPool.LONG_SCREW ) ); ResultProcessor.processItem( ItemPool.BUTT_JOINT, -1 * InventoryManager.getCount( ItemPool.BUTT_JOINT ) ); if ( KoLmafia.isAdventuring() ) { KoLmafia.updateDisplay( MafiaState.PENDING, "You have bridged the Orc Chasm." ); } QuestDatabase.setQuestProgress( Quest.TOPPING, "step1" ); } } private static final void handleHighlandsChange( final String location, final String responseText ) { if ( location.contains( "action=highlands_dude" ) && ( responseText.contains( "trying to, like, order a pizza" ) || responseText.contains( "trying to order a pizza" ) ) ) { QuestDatabase.setQuestProgress( Quest.TOPPING, "step2" ); } if ( location.contains( AdventurePool.ABOO_PEAK_ID ) && responseText.contains( "Come On Ghosty, Light My Pyre" ) || responseText.contains( "orcchasm/fire1.gif" ) ) { Preferences.setInteger( "booPeakProgress", 0 ); } if ( responseText.contains( "orcchasm/fire2.gif" ) ) { Preferences.setInteger( "twinPeakProgress", 15 ); } if ( location.contains( AdventurePool.OIL_PEAK_ID ) && responseText.contains( "Unimpressed with Pressure" ) || responseText.contains( "orcchasm/fire3.gif" ) ) { Preferences.setInteger( "oilPeakProgress", 0 ); } } private static final void handleSeaChange( final String location, final String responseText ) { if ( location.contains( "action=oldman_oldman" ) && responseText.contains( "have you found my boot yet?" ) ) { QuestDatabase.setQuestProgress( Quest.SEA_OLD_GUY, QuestDatabase.STARTED ); } // Little Brother else if ( location.contains( "who=1" ) ) { if ( responseText.contains( "wish my big brother was here" ) ) { QuestDatabase.setQuestProgress( Quest.SEA_MONKEES, "step1" ); } else if ( responseText.contains( "Wanna help me find Grandpa?" ) ) { QuestDatabase.setQuestProgress( Quest.SEA_MONKEES, "step4" ); } else if ( responseText.contains( "he's been actin' awful weird lately" ) ) { QuestDatabase.setQuestProgress( Quest.SEA_MONKEES, "step10" ); } } // Big Brother else if ( location.contains( "who=2" ) ) { if ( responseText.contains( "I found this thing" ) ) { QuestDatabase.setQuestIfBetter( Quest.SEA_MONKEES, "step11" ); } } // Grandpa else if ( location.contains( "action=grandpastory" ) ) { if ( responseText.contains( "bet those lousy Mer-kin up and kidnapped her" ) ) { QuestDatabase.setQuestIfBetter( Quest.SEA_MONKEES, "step6" ); } else if ( responseText.contains( "Gonna need one of them seahorses" ) ) { Preferences.setBoolean( "corralUnlocked", true ); } } else if ( location.contains( AdventurePool.MARINARA_TRENCH_ID ) && responseText.contains( "Show me what you've found, Old Timer" ) ) { QuestDatabase.setQuestProgress( Quest.SEA_MONKEES, "step5" ); } else if ( location.contains( AdventurePool.ANENOME_MINE_ID ) && responseText.contains( "Sure, kid. I can teach you a thing or two" ) ) { QuestDatabase.setQuestProgress( Quest.SEA_MONKEES, "step5" ); } else if ( location.contains( AdventurePool.DIVE_BAR_ID ) && ( responseText.contains( "What causes these things to form?" ) || responseText.contains( "what is that divine instrument?" ) ) ) { QuestDatabase.setQuestProgress( Quest.SEA_MONKEES, "step5" ); } else if ( location.contains( AdventurePool.MERKIN_OUTPOST_ID ) && responseText.contains( "Phew, that was a close one" ) ) { QuestDatabase.setQuestProgress( Quest.SEA_MONKEES, "step9" ); ConcoctionDatabase.setRefreshNeeded( false ); } else if ( location.contains( AdventurePool.CALIGINOUS_ABYSS_ID) && responseText.contains( "I should get dinner on the table for the boys" ) ) { QuestDatabase.setQuestProgress( Quest.SEA_MONKEES, QuestDatabase.FINISHED ); } // Learn about quest progress if visiting sea floor else if ( location.startsWith( "seafloor" ) ) { if ( responseText.contains( "abyss" ) ) { QuestDatabase.setQuestIfBetter( Quest.SEA_MONKEES, "step12" ); } else if ( responseText.contains( "outpost" ) ) { QuestDatabase.setQuestIfBetter( Quest.SEA_MONKEES, "step6" ); } else if ( responseText.contains( "mine" ) && KoLCharacter.isMuscleClass() ) { QuestDatabase.setQuestIfBetter( Quest.SEA_MONKEES, "step4" ); } else if ( responseText.contains( "trench" ) && KoLCharacter.isMysticalityClass() ) { QuestDatabase.setQuestIfBetter( Quest.SEA_MONKEES, "step4" ); } else if ( responseText.contains( "divebar" ) && KoLCharacter.isMoxieClass() ) { QuestDatabase.setQuestIfBetter( Quest.SEA_MONKEES, "step4" ); } else if( responseText.contains( "shipwreck" ) ) { QuestDatabase.setQuestIfBetter( Quest.SEA_MONKEES, "step1" ); } if ( responseText.contains( "corral" ) ) { Preferences.setBoolean( "corralUnlocked", true ); } } // Learn about quest progress if visiting sea monkey castle else if ( location.startsWith( "monkeycastle" ) ) { if ( responseText.contains( "who=4" ) ) { QuestDatabase.setQuestIfBetter( Quest.SEA_MONKEES, QuestDatabase.FINISHED ); } else if ( responseText.contains( "whichshop=grandma" ) ) { QuestDatabase.setQuestIfBetter( Quest.SEA_MONKEES, "step9" ); } else if ( responseText.contains( "who=3" ) ) { QuestDatabase.setQuestIfBetter( Quest.SEA_MONKEES, "step5" ); } else if ( responseText.contains( "who=2" ) ) { QuestDatabase.setQuestIfBetter( Quest.SEA_MONKEES, "step2" ); } } } private static final void handlePlainsChange( final String responseText ) { // You stare at the pile of coffee grounds for a minute and it // occurs to you that maybe your grandma wasn't so crazy after // all. You pull out an enchanted bean and plop it into the // pile of grounds. It immediately grows into an enormous // beanstalk. if ( responseText.contains( "immediately grows into an enormous beanstalk" ) ) { ResultProcessor.processItem( ItemPool.ENCHANTED_BEAN, -1 ); QuestDatabase.setQuestProgress( Quest.GARBAGE, "step1" ); if ( KoLmafia.isAdventuring() ) { KoLmafia.updateDisplay( MafiaState.PENDING, "You have planted a beanstalk." ); } } if ( responseText.contains( "palinlink.gif" ) ) { QuestDatabase.setQuestIfBetter( Quest.PALINDOME, QuestDatabase.STARTED ); } } public static final void handleBeanstalkChange( final String location, final String responseText ) { // If you can adventure in areas, it tells us about quests if ( location.contains( AdventurePool.AIRSHIP_ID ) ) { // Airship available QuestDatabase.setQuestIfBetter( Quest.GARBAGE, "step1" ); if ( responseText.contains( "we're looking for the Four Immateria" ) ) { QuestDatabase.setQuestIfBetter( Quest.GARBAGE, "step2" ); } } else if ( location.contains( AdventurePool.CASTLE_BASEMENT_ID ) ) { // Castle basement available QuestDatabase.setQuestIfBetter( Quest.GARBAGE, "step7" ); if ( responseText.contains( "New Area Unlocked" ) && responseText.contains( "The Ground Floor" ) ) { Preferences.setInteger( "lastCastleGroundUnlock", KoLCharacter.getAscensions() ); QuestDatabase.setQuestProgress( Quest.GARBAGE, "step8" ); } } else if ( location.contains( AdventurePool.CASTLE_GROUND_ID ) ) { // Castle Ground floor available QuestDatabase.setQuestIfBetter( Quest.GARBAGE, "step8" ); if ( responseText.contains( "New Area Unlocked" ) && responseText.contains( "The Top Floor" ) ) { Preferences.setInteger( "lastCastleTopUnlock", KoLCharacter.getAscensions() ); QuestDatabase.setQuestProgress( Quest.GARBAGE, "step9" ); } } else if ( location.contains( AdventurePool.CASTLE_TOP_ID ) && !responseText.contains( "You have to learn to walk" ) && !responseText.contains( "You'll have to figure out some other way" ) ) { // Castle Top floor available QuestDatabase.setQuestIfBetter( Quest.GARBAGE, "step9" ); } } private static final void handleZeppelinMobChange( final String responseText ) { if ( responseText.contains( "mob has cleared out" ) ) { QuestDatabase.setQuestProgress( Quest.RON, "step2" ); } else { QuestDatabase.setQuestIfBetter( Quest.RON, "step1" ); } } private static final void handleZeppelinChange( final String responseText ) { if ( responseText.contains( "sneak aboard the Zeppelin" ) ) { QuestDatabase.setQuestProgress( Quest.RON, "step3" ); } else { QuestDatabase.setQuestIfBetter( Quest.RON, "step2" ); } } private static final void handlePalindomeChange( final String location, final String responseText ) { if ( location.contains( "action=pal_mr" ) ) { if ( responseText.contains( "in the mood for a bowl of wet stunt nut stew" ) ) { QuestDatabase.setQuestProgress( Quest.PALINDOME, "step3" ); ResultProcessor.removeItem( ItemPool.PALINDROME_BOOK_2 ); } } } private static final void handleCanadiaChange( final String location, final String responseText ) { if ( location.contains( "action=lc_marty" ) ) { if ( responseText.contains( "All right, Marty, I'll see what I can do" ) ) { QuestDatabase.setQuestProgress( Quest.SWAMP, QuestDatabase.STARTED ); } } } private static final void handleMaraisChange( final String responseText ) { // Detect unlocked areas if ( responseText.contains( "The Edge of the Swamp" ) ) { QuestDatabase.setQuestIfBetter( Quest.SWAMP, QuestDatabase.STARTED ); } if ( responseText.contains( "The Dark and Spooky Swamp" ) ) { Preferences.setBoolean( "maraisDarkUnlock", true ); } if ( responseText.contains( "The Wildlife Sanctuarrrrrgh" ) ) { Preferences.setBoolean( "maraisWildlifeUnlock", true ); } if ( responseText.contains( "The Corpse Bog" ) ) { Preferences.setBoolean( "maraisCorpseUnlock", true ); } if ( responseText.contains( "The Ruined Wizard Tower" ) ) { Preferences.setBoolean( "maraisWizardUnlock", true ); } if ( responseText.contains( "Swamp Beaver Territory" ) ) { Preferences.setBoolean( "maraisBeaverUnlock", true ); } if ( responseText.contains( "The Weird Swamp Village" ) ) { Preferences.setBoolean( "maraisVillageUnlock", true ); } } private static final Pattern EXP_PATTERN = Pattern.compile( "\\(([\\d]+)%explored\\)" ); private static final void handleBeachChange( final String responseText ) { String expString = ResponseTextParser.parseDivLabel( "db_l11desertlabel", responseText ); Matcher matcher = QuestManager.EXP_PATTERN.matcher( expString ); if ( matcher.find() ) { int explored = StringUtilities.parseInt( matcher.group( 1 ) ); QuestManager.setDesertExploration( explored ); } } private static final void setDesertExploration( final int explored ) { int current = Preferences.getInteger( "desertExploration" ); QuestManager.setDesertExploration( current, explored - current ); } public static final void incrementDesertExploration( final int increment ) { int current = Preferences.getInteger( "desertExploration" ); QuestManager.setDesertExploration( current, increment ); } private static final void setDesertExploration( final int current, final int increment ) { // If we've already registered complete desert exploration, we're done if ( current == 100 ) { return; } // Peg new exploration percentage at 100 int explored = Math.min( current + increment, 100 ); // Save new exploration percentage Preferences.setInteger( "desertExploration", explored ); // If we are done, update the quest if ( explored == 100 ) { QuestDatabase.setQuestProgress( Quest.DESERT, QuestDatabase.FINISHED ); } } private static final void handleTrapperChange( final String responseText ) { Matcher oreMatcher = ORE_PATTERN.matcher( responseText ); if ( oreMatcher.find() ) { Preferences.setString( "trapperOre", oreMatcher.group( 1 ) + " ore" ); QuestDatabase.setQuestIfBetter( Quest.TRAPPER, "step1" ); } else if ( responseText.contains( "He takes the load of cheese and ore" ) || responseText.contains( "haul your load of ore and cheese" ) ) { AdventureResult item = ItemPool.get( Preferences.getString( "trapperOre" ), -3 ); ResultProcessor.processResult( item ); ResultProcessor.processResult( ItemPool.get( ItemPool.GOAT_CHEESE, -3 ) ); QuestDatabase.setQuestIfBetter( Quest.TRAPPER, "step2" ); } // Yeehaw! I heard the noise and seen them mists dissapatin' from clear down here! Ya done it! Ya rightly done it! else if ( responseText.contains( "Yeehaw! I heard the noise" ) ) { Preferences.setInteger( "lastTr4pz0rQuest", KoLCharacter.getAscensions() ); ResultProcessor.removeItem( ItemPool.GROARS_FUR ); QuestDatabase.setQuestProgress( Quest.TRAPPER, QuestDatabase.FINISHED ); } // "Yeehaw!" John exclaims as you drag the huge yeti pelt into his shack. else if ( responseText.contains( "drag the huge yeti pelt into his shack" ) ) { Preferences.setInteger( "lastTr4pz0rQuest", KoLCharacter.getAscensions() ); ResultProcessor.removeItem( ItemPool.WINGED_YETI_FUR ); QuestDatabase.setQuestProgress( Quest.TRAPPER, QuestDatabase.FINISHED ); } } private static final void handleExtremityChange( final String responseText ) { if ( responseText.contains( "Discovering Your Extremity" ) || responseText.contains( "2 eXXtreme 4 U" ) || responseText.contains( "3 eXXXtreme 4ever 6pack" ) ) { Preferences.increment( "currentExtremity" ); } } private static final void handleCouncilChange( final String responseText ) { Preferences.setInteger( "lastCouncilVisit", KoLCharacter.getLevel() ); if ( responseText.contains( "500" ) ) { ResultProcessor.removeItem( ItemPool.MOSQUITO_LARVA ); } if ( responseText.contains( "dragonbone belt buckle" ) ) { ResultProcessor.removeItem( ItemPool.BONERDAGON_SKULL ); } QuestDatabase.handleCouncilText( responseText ); if ( QuestDatabase.isQuestLaterThan( Quest.MACGUFFIN, QuestDatabase.UNSTARTED ) ) { QuestDatabase.setQuestIfBetter( Quest.BLACK, QuestDatabase.STARTED ); } } public static final void unlockGoatlet() { AdventureRequest goatlet = new AdventureRequest( "Goatlet", "adventure.php", AdventurePool.GOATLET_ID ); if ( KoLCharacter.inFistcore() ) { // You can actually get here without knowing Worldpunch // in Softcore by pulling ores. if ( !KoLCharacter.hasSkill( "Worldpunch" ) ) { KoLmafia.updateDisplay( MafiaState.ABORT, "Try again after you learn Worldpunch." ); return; } // If you don't have Earthen Fist active, get it. if ( !KoLConstants.activeEffects.contains( EffectPool.get( EffectPool.EARTHEN_FIST ) ) ) { UseSkillRequest request = UseSkillRequest.getInstance( "Worldpunch" ); request.setBuffCount( 1 ); RequestThread.postRequest( request ); } // Perhaps you ran out of MP. if ( !KoLmafia.permitsContinue() ) { KoLmafia.updateDisplay( MafiaState.ABORT, "Cast Worldpunch and try again." ); } RequestThread.postRequest( goatlet ); return; } if ( KoLCharacter.inAxecore() ) { // No outfit needed; just take option #3 RequestThread.postRequest( goatlet ); return; } if ( !EquipmentManager.hasOutfit( OutfitPool.MINING_OUTFIT ) ) { KoLmafia.updateDisplay( MafiaState.ABORT, "You need a mining outfit to continue." ); return; } if ( EquipmentManager.isWearingOutfit( OutfitPool.MINING_OUTFIT ) ) { RequestThread.postRequest( goatlet ); return; } SpecialOutfit.createImplicitCheckpoint(); ( new EquipmentRequest( EquipmentDatabase.getOutfit( OutfitPool.MINING_OUTFIT ) ) ).run(); RequestThread.postRequest( goatlet ); SpecialOutfit.restoreImplicitCheckpoint(); } /** After we win a fight, some quests may need to be updated. Centralize handling for it here. * @param responseText The text from (at least) the winning round of the fight * @param monster The monster which <s>died</s>got beaten up. */ public static void updateQuestData( String responseText, String monsterName ) { String adventureId = KoLAdventure.lastAdventureIdString(); String counter = adventureId.equals( "ns_01_crowd1" ) ? "nsContestants1" : adventureId.equals( "ns_01_crowd2" ) ? "nsContestants2" : adventureId.equals( "ns_01_crowd3" ) ? "nsContestants3" : null; boolean ghostBusted = false; if ( counter != null ) { int crowd = Preferences.getInteger( counter ); if ( crowd > 0 ) { Preferences.setInteger( counter, crowd - 1 ); } if ( Preferences.getInteger( "nsContestants1" ) == 0 && Preferences.getInteger( "nsContestants2" ) == 0 && Preferences.getInteger( "nsContestants3" ) == 0 ) { QuestDatabase.setQuestProgress( Quest.FINAL, "step2" ); } return; } if ( monsterName.equals( "screambat" ) ) { if ( !QuestDatabase.isQuestLaterThan( Quest.BAT, "step2" ) ) { QuestDatabase.advanceQuest( Quest.BAT ); } } else if ( monsterName.equals( "dirty thieving brigand" ) ) { // "Well," you say, "it would really help the war effort if // your convent could serve as a hospital for our wounded // troops." if ( responseText.contains( "could serve as a hospital" ) ) { Preferences.setString( "sidequestNunsCompleted", "hippy" ); } else if ( responseText.contains( "could serve as a massage parlor" ) ) { Preferences.setString( "sidequestNunsCompleted", "fratboy" ); } } // oil slick: 6.34 // oil tycoon: 19.02 // oil baron: 31.7 // oil cartel: 63.4 // dress pants: 6.34 // lovebug: 6.34 else if ( monsterName.equals( "oil slick" ) ) { double pantsBonus = InventoryManager.getEquippedCount( ItemPool.DRESS_PANTS ) > 0 ? 6.34 : 0; float current = Preferences.getFloat( "oilPeakProgress" ); double lovebug = responseText.contains( "love oil beetle trundles up" ) ? 6.34 : 0; // normalize String setTo = String.format( Locale.US, "%.2f", Math.max( 0, current - 6.34 - pantsBonus - lovebug ) ); Preferences.setString( "oilPeakProgress", setTo ); } else if ( monsterName.equals( "oil tycoon" ) ) { double pantsBonus = InventoryManager.getEquippedCount( ItemPool.DRESS_PANTS ) > 0 ? 6.34 : 0; float current = Preferences.getFloat( "oilPeakProgress" ); double lovebug = responseText.contains( "love oil beetle trundles up" ) ? 6.34 : 0; String setTo = String.format( Locale.US, "%.2f", Math.max( 0, current - 19.02 - pantsBonus - lovebug ) ); Preferences.setString( "oilPeakProgress", setTo ); } else if ( monsterName.equals( "oil baron" ) ) { double pantsBonus = InventoryManager.getEquippedCount( ItemPool.DRESS_PANTS ) > 0 ? 6.34 : 0; float current = Preferences.getFloat( "oilPeakProgress" ); double lovebug = responseText.contains( "love oil beetle trundles up" ) ? 6.34 : 0; String setTo = String.format( Locale.US, "%.2f", Math.max( 0, current - 31.7 - pantsBonus - lovebug ) ); Preferences.setString( "oilPeakProgress", setTo ); } else if ( monsterName.equals( "oil cartel" ) ) { double pantsBonus = InventoryManager.getEquippedCount( ItemPool.DRESS_PANTS ) > 0 ? 6.34 : 0; float current = Preferences.getFloat( "oilPeakProgress" ); double lovebug = responseText.contains( "love oil beetle trundles up" ) ? 6.34 : 0; String setTo = String.format( Locale.US, "%.2f", Math.max( 0, current - 63.4 - pantsBonus - lovebug ) ); Preferences.setString( "oilPeakProgress", setTo ); } else if ( monsterName.equals( "Battlie Knight Ghost" ) || monsterName.equals( "Claybender Sorcerer Ghost" ) || monsterName.equals( "Dusken Raider Ghost" ) || monsterName.equals( "Space Tourist Explorer Ghost" ) || monsterName.equals( "Whatsian Commando Ghost" ) ) { Preferences.decrement( "booPeakProgress", 2 ); } else if ( monsterName.equals( "panicking Knott Yeti" ) ) { QuestDatabase.setQuestIfBetter( Quest.TRAPPER, "step4" ); } else if ( monsterName.equals( "pygmy witch accountant" ) ) { // If you don't have McClusky File (complete), or // McClusky File 5, and accountant doesn't drop file, // you must have unlocked office boss if ( InventoryManager.getCount( ItemPool.MCCLUSKY_FILE ) == 0 && InventoryManager.getCount( ItemPool.MCCLUSKY_FILE_PAGE5 ) == 0 && Preferences.getInteger( "hiddenOfficeProgress" ) < 6 && !responseText.contains( "McClusky file" ) ) { Preferences.setInteger( "hiddenOfficeProgress", 6 ); } } else if ( monsterName.equals( "topiary gopher" ) || monsterName.equals( "topiary chihuahua herd" ) || monsterName.equals( "topiary duck" ) || monsterName.equals( "topiary kiwi" ) ) { // We are still in the Hedge Maze QuestDatabase.setQuestProgress( Quest.FINAL, "step4" ); } else if ( monsterName.equals( "wall of skin" ) ) { QuestDatabase.setQuestProgress( Quest.FINAL, "step7" ); } else if ( monsterName.equals( "wall of meat" ) && responseText.contains( "the stairs to the next floor are clear" ) ) { QuestDatabase.setQuestProgress( Quest.FINAL, "step8" ); } else if ( monsterName.equals( "wall of bones" ) ) { QuestDatabase.setQuestProgress( Quest.FINAL, "step9" ); } else if ( monsterName.equals( "Your Shadow" ) ) { QuestDatabase.setQuestProgress( Quest.FINAL, "step11" ); } else if ( monsterName.equals( "Clancy" ) ) { // We do not currently have a distinct step for Clancy QuestDatabase.setQuestProgress( Quest.FINAL, "step11" ); } else if ( monsterName.equals( "Naughty Sorceress (3)" ) || monsterName.equals( "The Avatar of Sneaky Pete" ) || monsterName.equals( "The Avatar of Boris" ) || monsterName.equals( "Principal Mooney" ) || monsterName.equals( "Rene C. Corman" ) || monsterName.equals( "The Avatar of Jarlsberg" ) || monsterName.equals( "The Rain King" ) || monsterName.equals( "One Thousand Source Agents" ) || responseText.contains( "Thwaitgold bee statuette" ) ) { QuestDatabase.setQuestProgress( Quest.FINAL, "step12" ); } else if ( monsterName.equals( "The Unknown Seal Clubber" ) || monsterName.equals( "The Unknown Turtle Tamer" ) || monsterName.equals( "The Unknown Pastamancer" ) || monsterName.equals( "The Unknown Sauceror" ) || monsterName.equals( "The Unknown Disco Bandit" ) || monsterName.equals( "The Unknown Accordion Thief" ) ) { QuestDatabase.setQuestProgress( Quest.NEMESIS, "step2" ); } else if ( monsterName.equals( "The Clownlord Beelzebozo" ) ) { QuestDatabase.setQuestProgress( Quest.NEMESIS, "step6" ); } else if ( monsterName.equals( "menacing thug" ) ) { QuestDatabase.setQuestProgress( Quest.NEMESIS, "step19" ); } else if ( monsterName.equals( "Mob Penguin hitman" ) ) { QuestDatabase.setQuestProgress( Quest.NEMESIS, "step21" ); } else if ( monsterName.equals( "hunting seal" ) || monsterName.equals( "turtle trapper" ) || monsterName.equals( "evil spaghetti cult assassin" ) || monsterName.equals( "béarnaise zombie" ) || monsterName.equals( "flock of seagulls" ) || monsterName.equals( "mariachi bandolero" ) ) { QuestDatabase.setQuestProgress( Quest.NEMESIS, "step23" ); } else if ( monsterName.equals( "Gorgolok, the Infernal Seal (Volcanic Cave)" ) || monsterName.equals( "Stella, the Turtle Poacher (Volcanic Cave)" ) || monsterName.equals( "Spaghetti Elemental (Volcanic Cave)" ) || monsterName.equals( "Lumpy, the Sinister Sauceblob (Volcanic Cave)" ) || monsterName.equals( "Spirit of New Wave (Volcanic Cave)" ) || monsterName.equals( "Somerset Lopez, Dread Mariachi (Volcanic Cave)" ) ) { QuestDatabase.setQuestProgress( Quest.NEMESIS, "step29" ); } else if ( monsterName.equals( "Sloppy Seconds Burger" ) ) { if ( responseText.contains( "You consult the list and grab the next ingredient" ) ) { Preferences.increment( "buffJimmyIngredients", 1 ); if ( Preferences.getInteger( "buffJimmyIngredients" ) >= 15 ) { QuestDatabase.setQuestProgress( Quest.JIMMY_CHEESEBURGER, "step1" ); } } } else if ( monsterName.equals( "Sloppy Seconds Cocktail" ) ) { if ( responseText.contains( "cocktail sauce bottle" ) || responseText.contains( "defeated foe with your bottle" ) ) { Preferences.increment( "tacoDanCocktailSauce", 1 ); if ( Preferences.getInteger( "tacoDanCocktailSauce" ) >= 15 ) { QuestDatabase.setQuestProgress( Quest.TACO_DAN_COCKTAIL, "step1" ); } } } else if ( monsterName.equals( "Sloppy Seconds Sundae" ) ) { if ( responseText.contains( "sprinkles off" ) ) { Preferences.increment( "brodenSprinkles", 1 ); if ( Preferences.getInteger( "brodenSprinkles" ) >= 15 ) { QuestDatabase.setQuestProgress( Quest.BRODEN_SPRINKLES, "step1" ); } } } else if ( monsterName.equals( "taco fish" ) ) { Matcher FishMeatMatcher = QuestManager.TACO_FISH_PATTERN.matcher( responseText ); if ( FishMeatMatcher.find() ) { Preferences.increment( "tacoDanFishMeat", StringUtilities.parseInt( FishMeatMatcher.group( 1 ) ) ); if ( Preferences.getInteger( "tacoDanFishMeat" ) >= 300 ) { QuestDatabase.setQuestProgress( Quest.TACO_DAN_FISH, "step1" ); } } } else if ( monsterName.equals( "Fun-Guy Playmate" ) ) { if ( responseText.contains( "hot tub with some more bacteria" ) ) { Preferences.increment( "brodenBacteria", 1 ); if ( Preferences.getInteger( "brodenBacteria" ) >= 10 ) { QuestDatabase.setQuestProgress( Quest.BRODEN_BACTERIA, "step1" ); } } } else if ( monsterName.equals( "Wu Tang the Betrayer" ) ) { Preferences.setInteger( "lastWuTangDefeated", KoLCharacter.getAscensions() ); } else if ( monsterName.equals( "Baron Von Ratsworth" ) ) { TavernRequest.addTavernLocation( '6' ); } else if ( monsterName.equals( "Baron Von Ratsworth" ) ) { TavernRequest.addTavernLocation( '6' ); } else if ( monsterName.equals( "Source Agent" ) ) { Preferences.increment( "sourceAgentsDefeated" ); } else if ( monsterName.equals( "pair of burnouts" ) ) { int increment = responseText.contains( "throw the opium grenade" ) ? 3 : 1; Preferences.increment( "burnoutsDefeated", increment, 30, false ); if ( Preferences.getInteger( "burnoutsDefeated" ) == 30 ) { QuestDatabase.setQuestIfBetter( Quest.CITADEL, "step4" ); } } else if ( monsterName.equals( "biclops" ) ) { QuestDatabase.setQuestProgress( Quest.CITADEL, "step5" ); } else if ( monsterName.equals( "surprised and annoyed witch" ) || monsterName.equals( "extremely annoyed witch" ) ) { QuestDatabase.setQuestProgress( Quest.CITADEL, "step7" ); } else if ( monsterName.equals( "Elpízo & Crosybdis" ) ) { QuestDatabase.setQuestProgress( Quest.CITADEL, "step10" ); } else if ( monsterName.equals( "hulking bridge troll" ) ) { OrcChasmRequest.setChasmProgress( 0 ); } else if ( monsterName.equals( "warehouse guard" ) || monsterName.equals( "warehouse janitor" ) || monsterName.equals( "warehouse clerk" ) ) { Preferences.increment( "warehouseProgress", 1 ); } else if ( monsterName.equals( "E.V.E., the robot zombie" ) ) { QuestDatabase.setQuestProgress( Quest.EVE, "step1" ); } else if ( monsterName.equals( "writing desk" ) ) { if ( QuestDatabase.isQuestLaterThan( Quest.SPOOKYRAVEN_NECKLACE, QuestDatabase.UNSTARTED ) && !InventoryManager.hasItem( ItemPool.SPOOKYRAVEN_NECKLACE ) && !QuestDatabase.isQuestFinished( Quest.SPOOKYRAVEN_NECKLACE ) ) { Preferences.increment( "writingDesksDefeated", 1, 5, false ); } } else if ( monsterName.equals( "nasty bear" ) ) { Preferences.increment( "dinseyNastyBearsDefeated", 1, 8, false ); QuestDatabase.setQuestProgress( Quest.NASTY_BEARS, ( Preferences.getInteger( "dinseyNastyBearsDefeated" ) == 8 ? "step2" : "step1" ) ); } else if ( monsterName.equals( "Wart Dinsey" ) ) { Preferences.setInteger( "lastWartDinseyDefeated", KoLCharacter.getAscensions() ); } else if ( monsterName.equals( "Cake Lord" ) ) { QuestDatabase.setQuestProgress( Quest.ARMORER, "step3" ); } else if ( monsterName.equals( "X-32-F Combat Training Snowman" ) ) { int snowParts = -2; Matcher snowmanMatcher = SNOWMAN_PATTERN.matcher( responseText ); while ( snowmanMatcher.find() ) { snowParts++; } if ( snowParts <= 10 ) { Preferences.setInteger( "_snojoFreeFights", snowParts ); String snojoSetting = Preferences.getString( "snojoSetting" ); if ( snojoSetting.equals( "MUSCLE" ) ) { Preferences.increment( "snojoMuscleWins" ); } else if ( snojoSetting.equals( "MYSTICALITY" ) ) { Preferences.increment( "snojoMysticalityWins" ); } else if ( snojoSetting.equals( "MOXIE" ) ) { Preferences.increment( "snojoMoxieWins" ); } } } else if ( monsterName.equals( "drunk cowpoke" ) || monsterName.equals( "surly gambler" ) || monsterName.equals( "wannabe gunslinger" ) || monsterName.equals( "cow cultist" ) || monsterName.equals( "hired gun" ) || monsterName.equals( "camp cook" ) || monsterName.equals( "skeletal gunslinger" ) || monsterName.equals( "restless ghost" ) || monsterName.equals( "buzzard" ) || monsterName.equals( "mountain lion" ) || monsterName.equals( "grizzled bear" ) || monsterName.equals( "diamondback rattler" ) || monsterName.equals( "coal snake" ) || monsterName.equals( "frontwinder" ) || monsterName.equals( "caugr" ) || monsterName.equals( "pyrobove" ) || monsterName.equals( "spidercow" ) || monsterName.equals( "moomy" ) ) { Preferences.increment( "lttQuestStageCount", 1 ); } else if ( monsterName.equals( "Jeff the Fancy Skeleton" ) || monsterName.equals( "Daisy the Unclean" ) || monsterName.equals( "Pecos Dave" ) || monsterName.equals( "Pharaoh Amoon-Ra Cowtep" ) || monsterName.equals( "Snake-Eyes Glenn" ) || monsterName.equals( "Former Sheriff Dan Driscoll" ) || monsterName.equals( "unusual construct" ) || monsterName.equals( "Clara" ) || monsterName.equals( "Granny Hackleton" ) ) { QuestDatabase.setQuestProgress( Quest.TELEGRAM, QuestDatabase.UNSTARTED ); Preferences.setInteger( "lttQuestDifficulty", 0 ); Preferences.setInteger( "lttQuestStageCount", 0 ); Preferences.setString( "lttQuestName", "" ); } else if ( monsterName.equals( "the ghost of Oily McBindle" ) || monsterName.equals( "boneless blobghost" ) || monsterName.equals( "the ghost of Monsieur Baguelle" ) || monsterName.equals( "The Headless Horseman" ) || monsterName.equals( "The Icewoman" ) || monsterName.equals( "The ghost of Ebenoozer Screege" ) || monsterName.equals( "The ghost of Lord Montague Spookyraven" ) || monsterName.equals( "The ghost of Vanillica \"Trashblossom\" Gorton" ) || monsterName.equals( "The ghost of Sam McGee" ) || monsterName.equals( "The ghost of Richard Cockingham" ) || monsterName.equals( "The ghost of Waldo the Carpathian" ) || monsterName.equals( "Emily Koops, a spooky lime" ) || monsterName.equals( "The ghost of Jim Unfortunato" ) ) { QuestDatabase.setQuestProgress( Quest.GHOST, QuestDatabase.UNSTARTED ); Preferences.setString( "ghostLocation", "" ); ghostBusted = true; } else if ( monsterName.equals( "Drab Bard" ) || monsterName.equals( "Bob Racecar" ) || monsterName.equals( "Racecar Bob" ) ) { if ( QuestDatabase.isQuestStep( Quest.PALINDOME, QuestDatabase.STARTED ) ) { Preferences.increment( "palindomeDudesDefeated", 1, 5, false ); } } int adventure = KoLAdventure.lastAdventureId(); switch ( adventure ) { case AdventurePool.MERKIN_COLOSSEUM: // Do not increment round for wandering monsters if ( ( monsterName.equals( "Mer-kin balldodger" ) || monsterName.equals( "Mer-kin netdragger" ) || monsterName.equals( "Mer-kin bladeswitcher" ) || monsterName.equals( "Georgepaul, the Balldodger" ) || monsterName.equals( "Johnringo, the Netdragger" ) || monsterName.equals( "Ringogeorge, the Bladeswitcher" ) ) && // Do mark path chosen unless won round 15 ( Preferences.increment( "lastColosseumRoundWon", 1 ) == 15 ) ) { Preferences.setString( "merkinQuestPath", "gladiator" ); } break; case AdventurePool.THE_DAILY_DUNGEON: Preferences.increment( "_lastDailyDungeonRoom", 1 ); break; case AdventurePool.ARID_DESERT: // clingy monsters do not increment exploration if ( !responseText.contains( "Desert exploration" ) ) { break; } int explored = 1; if ( KoLCharacter.hasEquipped( ItemPool.UV_RESISTANT_COMPASS, EquipmentManager.OFFHAND ) ) { explored += 1; } else if ( KoLCharacter.hasEquipped( ItemPool.DOWSING_ROD, EquipmentManager.OFFHAND ) ) { explored += 2; } if ( Preferences.getString( "peteMotorbikeHeadlight" ).equals( "Blacklight Bulb" ) ) { explored += 2; } QuestManager.incrementDesertExploration( explored ); break; case AdventurePool.ZEPPELIN_PROTESTORS: Matcher LighterMatcher = QuestManager.LIGHTER_PATTERN.matcher( responseText ); if ( LighterMatcher.find() ) { int flamingProtesters = StringUtilities.parseInt( LighterMatcher.group( 1 ) ); Preferences.increment( "zeppelinProtestors", flamingProtesters ); RequestLogger.printLine( "Set fire to " + flamingProtesters + " protesters" ); } else { Preferences.increment( "zeppelinProtestors", 1 ); } break; case AdventurePool.RED_ZEPPELIN: if ( responseText.contains( "inevitable confrontation with Ron Copperhead" ) ) { QuestDatabase.setQuestProgress( Quest.RON, "step4" ); } break; case AdventurePool.HAUNTED_KITCHEN: if ( !InventoryManager.hasItem( ItemPool.BILLIARDS_KEY ) ) { Matcher drawerMatcher = QuestManager.DRAWER_PATTERN.matcher( responseText ); if ( drawerMatcher.find() ) { Preferences.increment( "manorDrawerCount", StringUtilities.parseInt( drawerMatcher.group( 1 ) ) ); } else { Preferences.increment( "manorDrawerCount", 1 ); } } break; case AdventurePool.BLACK_FOREST: if ( responseText.contains( "discover the trail leading to the Black Market" ) ) { QuestDatabase.setQuestProgress( Quest.MACGUFFIN, "step1" ); QuestDatabase.setQuestProgress( Quest.BLACK, "step2" ); Preferences.setInteger( "blackForestProgress", 5 ); } else { if ( responseText.contains( "find a row of blackberry bushes so thick" ) ) { Preferences.setInteger( "blackForestProgress", 1 ); } else if ( responseText.contains( "find a cozy black cottage nestled deep" ) ) { Preferences.setInteger( "blackForestProgress", 2 ); } else if ( responseText.contains( "spot a mineshaft sunk deep into the black depths" ) ) { Preferences.setInteger( "blackForestProgress", 3 ); } else if ( responseText.contains( "find a church that would be picturesque if it wasn't so sinister" ) ) { Preferences.setInteger( "blackForestProgress", 4 ); } QuestDatabase.setQuestIfBetter( Quest.BLACK, "step1" ); } break; case AdventurePool.DEEP_DARK_JUNGLE: if ( responseText.contains( "jungle pun occurs to you" ) ) { Preferences.increment( "junglePuns", 1 ); if ( Preferences.getInteger( "junglePuns" ) >= 11 ) { QuestDatabase.setQuestProgress( Quest.JUNGLE_PUN, "step2" ); } } break; case AdventurePool.SECRET_GOVERNMENT_LAB: Matcher GoreMatcher = QuestManager.GORE_PATTERN.matcher( responseText ); if ( GoreMatcher.find() ) { Preferences.increment( "goreCollected", StringUtilities.parseInt( GoreMatcher.group( 1 ) ) ); if ( Preferences.getInteger( "goreCollected" ) >= 100 ) { QuestDatabase.setQuestProgress( Quest.GORE, "step2" ); } } if ( responseText.contains( "The gore sloshes around nauseatingly in your bucket" ) ) { QuestDatabase.setQuestProgress( Quest.GORE, "step2" ); } break; case AdventurePool.BARF_MOUNTAIN: if ( responseText.contains( "made it to the front of the line" ) ) { Preferences.setBoolean( "dinseyRollercoasterNext", true ); } break; case AdventurePool.GARBAGE_BARGES: if ( QuestDatabase.isQuestLaterThan( Quest.SOCIAL_JUSTICE_I, QuestDatabase.UNSTARTED ) ) { Preferences.increment( "dinseySocialJusticeIProgress", 1 ); } else if ( responseText.contains( "probably not embarrassingly sexist anymore" ) ) { Preferences.setInteger( "dinseySocialJusticeIProgress", 15 ); QuestDatabase.setQuestProgress( Quest.SOCIAL_JUSTICE_I, "step1" ); } if ( responseText.contains( "at least the barges aren't getting hung up on it anymore" ) ) { Preferences.setInteger( "dinseyFilthLevel", 0 ); QuestDatabase.setQuestProgress( Quest.FISH_TRASH, "step2" ); } else if ( responseText.contains( "larger chunks of garbage out of the waterway" ) ) { Preferences.decrement( "dinseyFilthLevel", 5, 0 ); QuestDatabase.setQuestProgress( Quest.FISH_TRASH, "step1" ); } break; case AdventurePool.TOXIC_TEACUPS: if ( responseText.contains( "pretend to be having a good time" ) ) { Preferences.increment( "dinseyFunProgress", 1 ); } else if ( responseText.contains( "surrounding crowd seems to be pretty excited about the ride" ) ) { Preferences.setInteger( "dinseyFunProgress", 15 ); QuestDatabase.setQuestProgress( Quest.ZIPPITY_DOO_DAH, "step2" ); } break; case AdventurePool.LIQUID_WASTE_SLUICE: if ( responseText.contains( "probably not unacceptably racist anymore" ) ) { Preferences.setInteger( "dinseySocialJusticeIIProgress", 15 ); QuestDatabase.setQuestProgress( Quest.SOCIAL_JUSTICE_II, "step1" ); } else if ( QuestDatabase.isQuestLaterThan( Quest.SOCIAL_JUSTICE_II, QuestDatabase.UNSTARTED ) ) { Preferences.increment( "dinseySocialJusticeIIProgress", 1 ); } break; case AdventurePool.ICE_HOTEL: case AdventurePool.VYKEA: case AdventurePool.ICE_HOLE: if ( responseText.contains( "you should take it back to Walford!" ) ) { Preferences.setInteger( "walfordBucketProgress", 100 ); QuestDatabase.setQuestProgress( Quest.BUCKET, "step2" ); break; } Matcher WalfordMatcher = QuestManager.WALFORD_PATTERN.matcher( responseText ); while ( WalfordMatcher.find() ) { Preferences.increment( "walfordBucketProgress", StringUtilities.parseInt( WalfordMatcher.group( 1 ) ) ); if ( Preferences.getInteger( "walfordBucketProgress" ) >= 100 ) { QuestDatabase.setQuestProgress( Quest.BUCKET, "step2" ); } } break; } // Can get a message about a ghost if wearing a Proton Accelerator Pack, // but if you got on the turn you busted a ghost, it is a false alarm. // if ( !ghostBusted && KoLCharacter.hasEquipped( ItemPool.get( ItemPool.PROTON_ACCELERATOR, 1 ) ) ) { Matcher ParanormalMatcher = QuestManager.PARANORMAL_PATTERN.matcher( responseText ); while ( ParanormalMatcher.find() ) { QuestDatabase.setQuestProgress( Quest.GHOST, QuestDatabase.STARTED ); String location = ParanormalMatcher.group( 1 ); // Locations don't exactly match location name or quest log entries, so make them if ( location.contains( "Overgrown Lot" ) ) { Preferences.setString( "ghostLocation", "The Overgrown Lot" ); } else if ( location.contains( "Skeleton Store" ) ) { Preferences.setString( "ghostLocation", "The Skeleton Store" ); } else if ( location.contains( "Madness Bakery" ) ) { Preferences.setString( "ghostLocation", "Madness Bakery" ); } else if ( location.contains( "Spooky Forest" ) ) { Preferences.setString( "ghostLocation", "The Spooky Forest" ); } else if ( location.contains( "Kitchen" ) ) { Preferences.setString( "ghostLocation", "The Haunted Kitchen" ); } else if ( location.contains( "Knob Treasury" ) ) { Preferences.setString( "ghostLocation", "Cobb's Knob Treasury" ); } else if ( location.contains( "Conservatory" ) ) { Preferences.setString( "ghostLocation", "The Haunted Conservatory" ); } else if ( location.contains( "Landfill" ) ) { Preferences.setString( "ghostLocation", "The Old Landfill" ); } else if ( location.contains( "Icy Peak" ) ) { Preferences.setString( "ghostLocation", "The Icy Peak" ); } else if ( location.contains( "Smut Orc Logging Camp" ) ) { Preferences.setString( "ghostLocation", "The Smut Orc Logging Camp" ); } else if ( location.contains( "Gallery" ) ) { Preferences.setString( "ghostLocation", "The Haunted Gallery" ); } else if ( location.contains( "Palindome" ) ) { Preferences.setString( "ghostLocation", "Inside the Palindome" ); } else if ( location.contains( "Wine Cellar" ) ) { Preferences.setString( "ghostLocation", "The Haunted Wine Cellar" ); } Preferences.setInteger( "nextParanormalActivity", KoLCharacter.getTurnsPlayed() + 51 ); String message = "Paranormal activity reported at " + Preferences.getString( "ghostLocation" ) + "."; RequestLogger.printLine( message ); RequestLogger.updateSessionLog( message ); } } } /** After we lose a fight, some quests may need to be updated. Centralize handling for it here. * @param responseText The text from (at least) the losing round of the fight * @param monster The monster which beat us up. */ public static void updateQuestFightLost( String responseText, String monsterName ) { if ( monsterName.equals( "menacing thug" ) ) { QuestDatabase.setQuestProgress( Quest.NEMESIS, "step18" ); } else if ( monsterName.equals( "Mob Penguin hitman" ) ) { QuestDatabase.setQuestProgress( Quest.NEMESIS, "step20" ); } else if ( monsterName.equals( "hunting seal" ) || monsterName.equals( "turtle trapper" ) || monsterName.equals( "evil spaghetti cult assassin" ) || monsterName.equals( "béarnaise zombie" ) || monsterName.equals( "flock of seagulls" ) || monsterName.equals( "mariachi bandolero" ) ) { QuestDatabase.setQuestProgress( Quest.NEMESIS, "step22" ); } else if ( monsterName.equals( "Argarggagarg the Dire Hellseal" ) || monsterName.equals( "Safari Jack, Small-Game Hunter" ) || monsterName.equals( "Yakisoba the Executioner" ) || monsterName.equals( "Heimandatz, Nacho Golem" ) || monsterName.equals( "Jocko Homo" ) || monsterName.equals( "The Mariachi With No Name" ) ) { QuestDatabase.setQuestProgress( Quest.NEMESIS, "step24" ); } else if ( monsterName.equals( "mother hellseal" ) ) { Preferences.decrement( "_sealScreeches", 1, 0 ); } else if ( monsterName.equals( "Travoltron" ) ) { Preferences.setBoolean( "_infernoDiscoVisited", false ); } else if ( monsterName.equals( "Source Agent" ) ) { Preferences.decrement( "sourceAgentsDefeated", 1, 0 ); } } /** After we start a fight, some quests may need to be updated. Centralize handling for it here. * @param responseText The text from (at least) the first round of the fight * @param monster The monster */ public static void updateQuestFightStarted( final String responseText, final String monsterName ) { if ( monsterName.equals( "Gorgolok, the Infernal Seal (Volcanic Cave)" ) || monsterName.equals( "Stella, the Turtle Poacher (Volcanic Cave)" ) || monsterName.equals( "Spaghetti Elemental (Volcanic Cave)" ) || monsterName.equals( "Lumpy, the Sinister Sauceblob (Volcanic Cave)" ) || monsterName.equals( "Spirit of New Wave (Volcanic Cave)" ) || monsterName.equals( "Somerset Lopez, Dread Mariachi (Volcanic Cave)" ) ) { QuestDatabase.setQuestProgress( Quest.NEMESIS, "step28" ); } else if ( monsterName.equals( "Cake Lord" ) ) { QuestDatabase.setQuestProgress( Quest.ARMORER, "step2" ); } else if ( monsterName.equals( "GNG-3-R" ) ) { if ( EquipmentManager.discardEquipment( ItemPool.get( ItemPool.GINGERSERVO ) ) == EquipmentManager.NONE ) { // Remove it from equipment if it is equipped, otherwise remove it from inventory ResultProcessor.processResult( ItemPool.get( ItemPool.GINGERSERVO, -1 ) ); } } } public static void updateQuestItemUsed( final int itemId, final String responseText ) { switch ( itemId ) { case ItemPool.FINGERNAIL_CLIPPERS: if ( responseText.contains( "little sliver of something fingernail-like" ) ) { Preferences.increment( "fingernailsClipped", 1 ); if ( Preferences.getInteger( "fingernailsClipped" ) >= 23 ) { QuestDatabase.setQuestProgress( Quest.CLIPPER, "step1" ); } } break; case ItemPool.DINSEY_REFRESHMENTS: if ( responseText.contains( "realize that the box of refreshments is empty" ) || responseText.contains( "box of snacks is empty" ) ) { QuestDatabase.setQuestProgress( Quest.WORK_WITH_FOOD, "step1" ); Preferences.setInteger( "dinseyTouristsFed", 30 ); } else if ( responseText.contains( "hand out snacks to your opponent" ) ) { int count = 1; if ( responseText.contains( "and the tourist in front" ) ) { count++; } else { Matcher touristMatcher = QuestManager.TOURIST_PATTERN.matcher( responseText ); if ( touristMatcher.find() ) { count+=StringUtilities.parseInt( touristMatcher.group( 1 ) ); } } Preferences.increment( "dinseyTouristsFed", count, 30, false ); } break; } } public static void updateQuestItemEquipped( final int itemId ) { switch ( itemId ) { case ItemPool.GORE_BUCKET: QuestDatabase.setQuestIfBetter( Quest.GORE, "step1" ); break; case ItemPool.MINI_CASSETTE_RECORDER: QuestDatabase.setQuestIfBetter( Quest.JUNGLE_PUN, "step1" ); break; case ItemPool.GPS_WATCH: QuestDatabase.setQuestIfBetter( Quest.OUT_OF_ORDER, "step1" ); break; case ItemPool.TRASH_NET: QuestDatabase.setQuestIfBetter( Quest.FISH_TRASH, "step1" ); break; case ItemPool.LUBE_SHOES: QuestDatabase.setQuestIfBetter( Quest.SUPER_LUBER, "step1" ); break; case ItemPool.MASCOT_MASK: QuestDatabase.setQuestIfBetter( Quest.ZIPPITY_DOO_DAH, "step1" ); break; case ItemPool.WALFORDS_BUCKET: QuestDatabase.setQuestIfBetter( Quest.BUCKET, "step1" ); break; } } public static final Pattern SPACEGATE_PLANET_PATTERN = Pattern.compile( "<td>Current planet: Planet Name: ([^<]+)<br>" ); public static final Pattern SPACEGATE_COORDINATES_PATTERN = Pattern.compile( "<br>Coordinates: ([^<]+)<br>" ); public static final Pattern SPACEGATE_HAZARDS_PATTERN = Pattern.compile( "<br><p>Environmental Hazards:<[Bb]r>(.*)<br>Plant Life:", Pattern.DOTALL ); public static final Pattern SPACEGATE_PLANT_LIFE_PATTERN = Pattern.compile( "<br>Plant Life: (?:<font color=\\w+>)?([^<]+)(?:</font>)? ?(?:<font color=\\w+>(\\(hostile\\))</font>)?<br>" ); public static final Pattern SPACEGATE_ANIMAL_LIFE_PATTERN = Pattern.compile( "<br>Animal Life: (?:<font color=\\w+>)?([^<]+)(?:</font>)? ?(?:<font color=\\w+>(\\(hostile\\))</font>)?<br>" ); public static final Pattern SPACEGATE_INTELLIGENT_LIFE_PATTERN = Pattern.compile( "<br>Intelligent Life: (?:<font color=\\w+>)?([^<]+) ?(?:</font>)?(?:<font color=\\w+>(\\(hostile\\))</font>)?<br>" ); public static final Pattern SPACEGATE_SPANT_PATTERN = Pattern.compile( "<b>Spant</b>" ); public static final Pattern SPACEGATE_MURDERBOT_PATTERN = Pattern.compile( "<b>Murderbot</b>" ); public static final Pattern SPACEGATE_RUINS_PATTERN = Pattern.compile( "<br>ALERT: ANCIENT RUINS DETECTED<br>" ); public static final Pattern SPACEGATE_TURNS_PATTERN = Pattern.compile( "<p>Spacegate Energy remaining: <b><font size=\\+2>(\\d+) </font>" ); public static void parseSpacegateTerminal( final String text, final boolean print ) { if ( !text.contains( "Spacegate Terminal" ) ) { return; } Matcher m = QuestManager.SPACEGATE_PLANET_PATTERN.matcher( text ); String name = m.find() ? m.group( 1 ).trim() : ""; Preferences.setString( "_spacegatePlanetName", name ); if ( print ) { RequestLogger.updateSessionLog( "Planet: " + name ); } m = QuestManager.SPACEGATE_COORDINATES_PATTERN.matcher( text ); String coordinates = m.find() ? m.group( 1 ).trim() : ""; Preferences.setString( "_spacegateCoordinates", coordinates ); int index = coordinates.length() > 0 ? ( coordinates.charAt( 0 ) - 'A' ) : 0; Preferences.setInteger( "_spacegatePlanetIndex", Math.max( 0, Math.min( 25, index ) ) ); if ( print ) { RequestLogger.updateSessionLog( "Coordinates: " + coordinates ); } m = QuestManager.SPACEGATE_HAZARDS_PATTERN.matcher( text ); String hazards = m.find() ? m.group(1).trim(): ""; hazards = StringUtilities.globalStringDelete( hazards, " " ); hazards = StringUtilities.globalStringReplace( hazards, "<br>", "|" ).trim(); Preferences.setString( "_spacegateHazards", hazards ); if ( print ) { RequestLogger.updateSessionLog( "Hazards: " + hazards ); } m = QuestManager.SPACEGATE_PLANT_LIFE_PATTERN.matcher( text ); String plants = m.find() ? m.group(1).trim() + ( m.group(2) == null ? "" : ( " " + m.group(2) ) ) : "none"; Preferences.setString( "_spacegatePlantLife", plants ); if ( print ) { RequestLogger.updateSessionLog( "Plant Life: " + plants ); } m = QuestManager.SPACEGATE_ANIMAL_LIFE_PATTERN.matcher( text ); String animals = m.find() ? m.group(1).trim() + ( m.group(2) == null ? "" : ( " " + m.group(2) ) ) : "none"; Preferences.setString( "_spacegateAnimalLife", animals ); if ( print ) { RequestLogger.updateSessionLog( "Animal Life: " + animals ); } m = QuestManager.SPACEGATE_INTELLIGENT_LIFE_PATTERN.matcher( text ); String intelligent = m.find() ? m.group(1).trim() + ( m.group(2) == null ? "" : ( " " + m.group(2) ) ) : "none"; Preferences.setString( "_spacegateIntelligentLife", intelligent ); if ( print ) { RequestLogger.updateSessionLog( "Intelligent Life: " + intelligent ); } m = QuestManager.SPACEGATE_SPANT_PATTERN.matcher( text ); boolean spants = m.find(); Preferences.setBoolean( "_spacegateSpant", spants ); if ( print ) { RequestLogger.updateSessionLog( "Spant chemical signature detected: " + spants ); } m = QuestManager.SPACEGATE_MURDERBOT_PATTERN.matcher( text ); boolean murderbots = m.find(); Preferences.setBoolean( "_spacegateMurderbot", murderbots ); if ( print ) { RequestLogger.updateSessionLog( "Murderbots frequencies detected: " + murderbots ); } m = QuestManager.SPACEGATE_RUINS_PATTERN.matcher( text ); boolean ruins = m.find(); Preferences.setBoolean( "_spacegateRuins", ruins ); if ( print ) { RequestLogger.updateSessionLog( "Ancient ruins detected: " + ruins ); } m = QuestManager.SPACEGATE_TURNS_PATTERN.matcher( text ); String turns = m.find() ? m.group( 1 ) : "0"; Preferences.setString("_spacegateTurnsLeft", turns ); if ( print ) { RequestLogger.updateSessionLog( "Spacegate turns left: " + turns ); } } }