/**
* 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;
import java.util.HashSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import net.sourceforge.kolmafia.KoLConstants.MafiaState;
import net.sourceforge.kolmafia.listener.NamedListenerRegistry;
import net.sourceforge.kolmafia.moods.RecoveryManager;
import net.sourceforge.kolmafia.objectpool.AdventurePool;
import net.sourceforge.kolmafia.objectpool.EffectPool;
import net.sourceforge.kolmafia.objectpool.FamiliarPool;
import net.sourceforge.kolmafia.objectpool.ItemPool;
import net.sourceforge.kolmafia.objectpool.OutfitPool;
import net.sourceforge.kolmafia.persistence.AdventureDatabase;
import net.sourceforge.kolmafia.persistence.EquipmentDatabase;
import net.sourceforge.kolmafia.persistence.HolidayDatabase;
import net.sourceforge.kolmafia.persistence.MonsterDatabase.Element;
import net.sourceforge.kolmafia.persistence.QuestDatabase;
import net.sourceforge.kolmafia.persistence.QuestDatabase.Quest;
import net.sourceforge.kolmafia.preferences.Preferences;
import net.sourceforge.kolmafia.request.AdventureRequest;
import net.sourceforge.kolmafia.request.BasementRequest;
import net.sourceforge.kolmafia.request.ClanRumpusRequest;
import net.sourceforge.kolmafia.request.DwarfFactoryRequest;
import net.sourceforge.kolmafia.request.EquipmentRequest;
import net.sourceforge.kolmafia.request.FightRequest;
import net.sourceforge.kolmafia.request.GenericRequest;
import net.sourceforge.kolmafia.request.PlaceRequest;
import net.sourceforge.kolmafia.request.PyramidRequest;
import net.sourceforge.kolmafia.request.QuestLogRequest;
import net.sourceforge.kolmafia.request.RichardRequest;
import net.sourceforge.kolmafia.request.SpelunkyRequest;
import net.sourceforge.kolmafia.request.TavernRequest;
import net.sourceforge.kolmafia.request.UntinkerRequest;
import net.sourceforge.kolmafia.request.UseItemRequest;
import net.sourceforge.kolmafia.session.BatManager;
import net.sourceforge.kolmafia.session.EncounterManager;
import net.sourceforge.kolmafia.session.EquipmentManager;
import net.sourceforge.kolmafia.session.GoalManager;
import net.sourceforge.kolmafia.session.GuildUnlockManager;
import net.sourceforge.kolmafia.session.InventoryManager;
import net.sourceforge.kolmafia.session.Limitmode;
import net.sourceforge.kolmafia.swingui.GenericFrame;
import net.sourceforge.kolmafia.textui.command.CouncilCommand;
import net.sourceforge.kolmafia.utilities.InputFieldUtilities;
import net.sourceforge.kolmafia.utilities.StringUtilities;
public class KoLAdventure
implements Comparable<KoLAdventure>, Runnable
{
public static final String[][] DEMON_TYPES =
{
{ "Summoning Chamber", "Pies" },
{ "Spooky Forest", "Preternatural Greed" },
{ "Sonofa Beach", "Fit To Be Tide" },
{ "Deep Fat Friars' Gate", "Big Flaming Whip" },
{ "Haunted Bathroom", "Demonic Taint" },
{ null, "pile of smoking rags" },
{ null, "Drinks" },
{ "Nemesis' Lair", "Existential Torment" },
{ "Sinister Ancient Tablet", "Burning, Man" },
{ "Strange Cube", "The Pleasures of the Flesh" },
{ "Battlefield", "Infernal Thirst" },
{ null, "Jacked In" },
};
public static final AdventureResult BEATEN_UP = EffectPool.get( EffectPool.BEATEN_UP, 4 );
public static final AdventureResult PERFUME = EffectPool.get( EffectPool.KNOB_GOBLIN_PERFUME, 1 );
public static KoLAdventure lastVisitedLocation = null;
public static boolean locationLogged = false;
public static String lastLocationName = null;
public static String lastLocationURL = null;
private boolean isValidAdventure = false;
private final String zone, parentZone, adventureId, formSource, adventureName, environment;
private final int recommendedStat, waterLevel;
private final String normalString, lowercaseString, parentZoneDescription;
private GenericRequest request;
private final AreaCombatData areaSummary;
private final boolean isNonCombatsOnly;
private static final Pattern ADVENTURE_AGAIN = Pattern.compile( "<a href=\"([^\"]*)\">Adventure Again \\((.*?)\\)</a>" );
private static final HashSet<String> unknownAdventures = new HashSet<String>();
/**
* Constructs a new <code>KoLAdventure</code> with the given specifications.
*
* @param formSource The form associated with the given adventure
* @param adventureId The identifier for this adventure, relative to its form
* @param adventureName The string form, or name of this adventure
*/
public KoLAdventure( final String zone, final String formSource, final String adventureId, final String adventureName )
{
this.formSource = formSource;
this.adventureId = adventureId;
this.zone = zone;
this.adventureName = adventureName;
this.normalString = this.zone + ": " + this.adventureName;
this.lowercaseString = this.normalString.toLowerCase();
this.parentZone = (String) AdventureDatabase.PARENT_ZONES.get( zone );
this.parentZoneDescription = (String) AdventureDatabase.ZONE_DESCRIPTIONS.get( this.parentZone );
this.environment = AdventureDatabase.getEnvironment( adventureName );
this.recommendedStat = AdventureDatabase.getRecommendedStat( adventureName );
this.waterLevel = AdventureDatabase.getWaterLevel( adventureName );
if ( formSource.equals( "dwarffactory.php" ) )
{
this.request = new DwarfFactoryRequest( "ware" );
}
else if ( formSource.equals( "clan_gym.php" ) )
{
this.request = new ClanRumpusRequest( ClanRumpusRequest.RequestType.fromString( adventureId ) );
}
else if ( formSource.equals( "clan_hobopolis.php" ) )
{
this.request = new RichardRequest( StringUtilities.parseInt( adventureId ) );
}
else if ( formSource.equals( "basement.php" ) )
{
this.request = new BasementRequest( adventureName );
}
else
{
this.request = new AdventureRequest( adventureName, formSource, adventureId );
}
this.areaSummary = AdventureDatabase.getAreaCombatData( adventureName );
if ( adventureId == null )
{
this.isNonCombatsOnly = false;
}
else if ( this.areaSummary != null )
{
this.isNonCombatsOnly = this.areaSummary.combats() == 0 && this.areaSummary.getMonsterCount() == 0;
}
else
{
this.isNonCombatsOnly = !( this.request instanceof AdventureRequest );
}
}
public String toLowerCaseString()
{
return this.lowercaseString;
}
/**
* Returns the form source for this adventure.
*/
public String getFormSource()
{
return this.formSource;
}
/**
* Returns the name where this zone is found.
*/
public String getZone()
{
return this.zone;
}
public String getParentZone()
{
return this.parentZone;
}
public String getParentZoneDescription()
{
return this.parentZoneDescription;
}
public String getEnvironment()
{
return this.environment;
}
public int getRecommendedStat()
{
return this.recommendedStat;
}
public int getWaterLevel()
{
return this.waterLevel;
}
/**
* Returns the name of this adventure.
*/
public String getAdventureName()
{
return this.adventureName;
}
public String getPrettyAdventureName( final String urlString )
{
if ( urlString.contains( "pyramid_state" ) )
{
return PyramidRequest.getPyramidLocationString( urlString );
}
if ( urlString.startsWith( "basement.php" ) )
{
return BasementRequest.getBasementLevelName();
}
if ( urlString.startsWith( "cellar.php" ) )
{
return TavernRequest.cellarLocationString( urlString );
}
// *** could do something with barrel.php here
return this.adventureName;
}
// In chamber <b>#1</b> of the Daily Dungeon, you encounter ...
// In the <b>5th</b> chamber of the Daily Dungeon, you encounter ...
private static final Pattern DAILY_DUNGEON_CHAMBER = Pattern.compile( "<b>#?([\\d]+)(?:th)?</b>" );
public static String getPrettyAdventureName( final String locationName, final String responseText )
{
if ( locationName.equals( "The Daily Dungeon" ) )
{
// Parse room number from responseText
Matcher matcher = KoLAdventure.DAILY_DUNGEON_CHAMBER.matcher( responseText );
if ( matcher.find() )
{
String room = matcher.group( 1 );
return locationName + " (Room " + room + ")";
}
}
return locationName;
}
/**
* Returns the adventure Id for this adventure.
*
* @return The adventure Id for this adventure
*/
public String getAdventureId()
{
return this.adventureId;
}
public AreaCombatData getAreaSummary()
{
return this.areaSummary;
}
public boolean isNonCombatsOnly()
{
return this.isNonCombatsOnly && KoLAdventure.hasWanderingMonsters( this.formSource );
}
public static boolean hasWanderingMonsters( String urlString )
{
if ( !urlString.startsWith( "adventure.php" ) )
{
return false;
}
// Romantic targets.
String romanticTarget = Preferences.getString( "romanticTarget" );
if ( romanticTarget != null && !romanticTarget.equals( "" ) )
{
return true;
}
// Holidays.
String holiday = HolidayDatabase.getHoliday();
if ( holiday.contains( "El Dia De Los Muertos Borrachos" ) ||
holiday.contains( "Feast of Boris" ) ||
holiday.contains( "Talk Like a Pirate Day" ) )
{
return true;
}
// Nemesis assassins.
if ( !InventoryManager.hasItem( ItemPool.VOLCANO_MAP ) && QuestDatabase.isQuestLaterThan( Quest.NEMESIS, "step16" ) )
{
return true;
}
// Beecore.
if ( KoLCharacter.inBeecore() )
{
return true;
}
// Mini-Hipster and Artistic Goth Kid
FamiliarData familiar = KoLCharacter.getFamiliar();
if ( familiar != null && ( familiar.getId() == FamiliarPool.HIPSTER || familiar.getId() == FamiliarPool.ARTISTIC_GOTH_KID ) && Preferences.getInteger( "_hipsterAdv" ) < 7 )
{
return true;
}
return false;
}
/**
* Returns the request associated with this adventure.
*
* @return The request for this adventure
*/
public GenericRequest getRequest()
{
return this.request;
}
public void overrideAdventuresUsed( int used )
{
if ( this.request instanceof AdventureRequest )
{
((AdventureRequest) this.request).overrideAdventuresUsed( used );
}
}
/**
* Checks the map location of the given zone. This is to ensure that
* KoLmafia arms any needed flags (such as for the beanstalk).
*/
private void validate( boolean visitedCouncil )
{
this.isValidAdventure = false;
if ( Limitmode.limitZone( this.zone ) )
{
return;
}
if ( this.zone.equals( "Astral" ) )
{
// Update the choice adventure setting
if ( this.adventureId.equals( AdventurePool.BAD_TRIP_ID ) )
{
Preferences.setString( "choiceAdventure71", "1" );
}
else if ( this.adventureId.equals( AdventurePool.MEDIOCRE_TRIP_ID ) )
{
Preferences.setString( "choiceAdventure71", "2" );
}
else
{
Preferences.setString( "choiceAdventure71", "3" );
}
// If the player is not half-astral, then
// make sure they are before continuing.
AdventureResult effect = EffectPool.get( EffectPool.HALF_ASTRAL );
if ( !KoLConstants.activeEffects.contains( effect ) )
{
AdventureResult mushroom = ItemPool.get( ItemPool.ASTRAL_MUSHROOM, 1 );
this.isValidAdventure = InventoryManager.retrieveItem( mushroom );
if ( this.isValidAdventure )
{
RequestThread.postRequest( UseItemRequest.getInstance( mushroom ) );
}
}
this.isValidAdventure = KoLConstants.activeEffects.contains( effect );
return;
}
// Fighting the Goblin King requires effects
if ( this.formSource.equals( "cobbsknob.php" ) )
{
// You have two choices:
// - Wear harem girl outfit and have Perfume effect
// - Wear elite guard uniform and have cake
// Assume that if you are currently wearing an outfit,
// you want to use that method.
if ( EquipmentManager.isWearingOutfit( OutfitPool.HAREM_OUTFIT ) )
{
// Harem girl
if ( KoLConstants.activeEffects.contains( KoLAdventure.PERFUME ) )
{
// We are wearing the outfit and have the effect. Good to go!
this.isValidAdventure = true;
return;
}
if ( !KoLCharacter.inBeecore() &&
InventoryManager.retrieveItem( ItemPool.KNOB_GOBLIN_PERFUME ) )
{
// If we are in Beecore, we have to adventure to get
// the effect. Otherwise, we can use the item
RequestThread.postRequest( UseItemRequest.getInstance( ItemPool.KNOB_GOBLIN_PERFUME ) );
this.isValidAdventure = true;
return;
}
}
else if ( EquipmentManager.isWearingOutfit( OutfitPool.KNOB_ELITE_OUTFIT ) )
{
// Elite Guard
if ( InventoryManager.retrieveItem( ItemPool.KNOB_CAKE ) )
{
// We are wearing the outfit and have the cake. Good to go!
this.isValidAdventure = true;
return;
}
}
// If we get here, we are not currently wearing an
// appropriate outfit or, if we are, we can't use it.
// See what we have.
int outfitId = OutfitPool.NONE;
// Using the harem girl outfit requires only two pieces and a perfume.
// Using the elite guard outfit requires three pieces and Fancy cooking.
// Check for the harem girl first.
if ( EquipmentManager.hasOutfit( OutfitPool.HAREM_OUTFIT ) &&
( KoLConstants.activeEffects.contains( KoLAdventure.PERFUME ) ||
( !KoLCharacter.inBeecore() &&
InventoryManager.retrieveItem( ItemPool.KNOB_GOBLIN_PERFUME ) ) ) )
{
// We have the harem girl outfit and either
// have the effect or, if we are not in
// Beecore, have a perfume.
outfitId = OutfitPool.HAREM_OUTFIT;
}
else if ( EquipmentManager.hasOutfit( OutfitPool.KNOB_ELITE_OUTFIT ) &&
InventoryManager.retrieveItem( ItemPool.KNOB_CAKE ) )
{
// We have the elite guard uniform and have
// made a cake.
outfitId = OutfitPool.KNOB_ELITE_OUTFIT;
}
else
{
return;
}
// Wear the selected outfit.
SpecialOutfit outfit = EquipmentDatabase.getOutfit( outfitId );
RequestThread.postRequest( new EquipmentRequest( outfit ) );
// If we selected the harem girl outfit, use a perfume
if ( outfitId == OutfitPool.HAREM_OUTFIT && !KoLConstants.activeEffects.contains( KoLAdventure.PERFUME ) )
{
RequestThread.postRequest( UseItemRequest.getInstance( ItemPool.KNOB_GOBLIN_PERFUME ) );
}
this.isValidAdventure = true;
return;
}
if ( this.zone.equals( "Lab" ) )
{
this.isValidAdventure = InventoryManager.hasItem( ItemPool.get( ItemPool.LAB_KEY, 1 ) );
return;
}
if ( this.zone.equals( "Menagerie" ) )
{
this.isValidAdventure = InventoryManager.hasItem( ItemPool.get( ItemPool.MENAGERIE_KEY, 1 ) );
return;
}
// Adventuring in the mine warehouse or office requires either
// Mining Gear or the Dwarvish War Uniform
if ( this.formSource.equals( "dwarffactory.php" ) || this.adventureId.equals( AdventurePool.MINE_OFFICE_ID ) )
{
int id1 = OutfitPool.MINING_OUTFIT;
int id2 = OutfitPool.DWARVISH_UNIFORM;
if ( !EquipmentManager.isWearingOutfit( id1 ) && !EquipmentManager.isWearingOutfit( id2 ) )
{
SpecialOutfit outfit =
EquipmentManager.hasOutfit( id1 ) ? EquipmentDatabase.getOutfit( id1 ) :
EquipmentManager.hasOutfit( id2 ) ? EquipmentDatabase.getOutfit( id2 ) :
null;
if ( outfit == null )
{
return;
}
RequestThread.postRequest( new EquipmentRequest( outfit ) );
}
this.isValidAdventure = true;
return;
}
// Disguise zones require outfits
if ( !this.adventureId.equals( AdventurePool.COLA_BATTLEFIELD_ID ) &&
( this.adventureName.contains( "Disguise" ) || this.adventureName.contains( "Uniform" ) ) )
{
int outfitId = EquipmentDatabase.getOutfitId( this );
if ( outfitId > 0 && !EquipmentManager.isWearingOutfit( outfitId ) )
{
SpecialOutfit outfit = EquipmentDatabase.getOutfit( outfitId );
if ( !EquipmentManager.retrieveOutfit( outfit ) )
{
return;
}
RequestThread.postRequest( new EquipmentRequest( outfit ) );
}
this.isValidAdventure = true;
return;
}
// If the person has a continuum transfunctioner, then find
// some way of equipping it. If they do not have one, then
// acquire one then try to equip it.
if ( this.adventureId.equals( AdventurePool.PIXEL_REALM_ID ) || this.zone.equals( "Vanya's Castle" ) )
{
AdventureResult transfunctioner = ItemPool.get( ItemPool.TRANSFUNCTIONER, 1 );
if ( !InventoryManager.hasItem( transfunctioner ) )
{
RequestThread.postRequest( new PlaceRequest( "forestvillage", "fv_mystic" ) );
GenericRequest pixelRequest = new GenericRequest( "choice.php?whichchoice=664&option=1" );
// The early steps cannot be skipped
RequestThread.postRequest( pixelRequest );
RequestThread.postRequest( pixelRequest );
RequestThread.postRequest( pixelRequest );
}
if ( !KoLCharacter.hasEquipped( transfunctioner) )
{
RequestThread.postRequest( new EquipmentRequest( transfunctioner ) );
}
this.isValidAdventure = true;
return;
}
if ( this.adventureId.equals( AdventurePool.PALINDOME_ID ) )
{
AdventureResult talisman = ItemPool.get( ItemPool.TALISMAN, 1 );
if ( !KoLCharacter.hasEquipped( talisman ) )
{
if ( !InventoryManager.hasItem( talisman ) )
{
return;
}
RequestThread.postRequest( new EquipmentRequest( talisman ) );
}
if ( QuestDatabase.isQuestLaterThan( Quest.PALINDOME, QuestDatabase.UNSTARTED ) )
{
this.isValidAdventure = true;
return;
}
GenericRequest request = new PlaceRequest( "plains" );
RequestThread.postRequest( request );
this.isValidAdventure = request.responseText.contains( "palinlink.gif" ) ;
return;
}
if ( this.adventureId.equals( AdventurePool.HOBOPOLIS_SEWERS_ID ) )
{
// Don't auto-adventure unprepared in Hobopolis sewers
this.isValidAdventure = !Preferences.getBoolean( "requireSewerTestItems" ) ||
( KoLCharacter.hasEquipped( ItemPool.get( ItemPool.GATORSKIN_UMBRELLA, 1 ) ) &&
KoLCharacter.hasEquipped( ItemPool.get( ItemPool.HOBO_CODE_BINDER, 1 ) ) &&
InventoryManager.retrieveItem( ItemPool.SEWER_WAD ) &&
InventoryManager.retrieveItem( ItemPool.OOZE_O ) &&
InventoryManager.retrieveItem( ItemPool.DUMPLINGS ) &&
InventoryManager.retrieveItem( ItemPool.OIL_OF_OILINESS, 3 ) );
return;
}
if ( this.adventureId.equals( AdventurePool.HIDDEN_TEMPLE_ID ) )
{
boolean unlocked = KoLCharacter.getTempleUnlocked();
if ( !unlocked )
{
// Visit the distant woods and take a look.
RequestThread.postRequest( new GenericRequest( "woods" ) );
unlocked = KoLCharacter.getTempleUnlocked();
}
this.isValidAdventure = unlocked;
return;
}
if ( this.adventureId.equals( AdventurePool.SHROUDED_PEAK_ID ) )
{
String trapper = Preferences.getString( Quest.TRAPPER.getPref() );
this.isValidAdventure = trapper.equals( "step3" ) || trapper.equals( "step4" );
if ( Preferences.getString( "peteMotorbikeTires" ).equals( "Snow Tires" ) )
{
this.isValidAdventure = true;
return;
}
if ( !this.isValidAdventure )
{
return;
}
this.isValidAdventure = KoLCharacter.getElementalResistanceLevels( Element.COLD ) > 4;
return;
}
if ( this.adventureId.equals( AdventurePool.LOWER_CHAMBER_ID ) )
{
this.isValidAdventure = Preferences.getBoolean( "lowerChamberUnlock" );
return;
}
if ( this.adventureId.equals( AdventurePool.SUMMONING_CHAMBER_ID ) )
{
this.isValidAdventure = QuestDatabase.isQuestLaterThan( Quest.MANOR, "step2" );
return;
}
if ( this.adventureId.equals( AdventurePool.ELDRITCH_FISSURE_ID ) )
{
this.isValidAdventure = Preferences.getBoolean( "eldritchFissureAvailable" );
return;
}
if ( this.adventureId.equals( AdventurePool.ELDRITCH_HORROR_ID ) )
{
this.isValidAdventure = Preferences.getBoolean( "eldritchHorrorAvailable" );
return;
}
if ( !this.formSource.contains( "adventure.php" ) )
{
this.isValidAdventure = true;
return;
}
if ( this.isValidAdventure )
{
return;
}
// If we're trying to take a trip, make sure it's the right one
if ( this.zone.equals( "Astral" ) )
{
// You must be Half-Astral to go on a trip
AdventureResult effect = EffectPool.get( EffectPool.HALF_ASTRAL );
int astral = effect.getCount( KoLConstants.activeEffects );
if ( astral == 0 )
{
RequestThread.postRequest( UseItemRequest.getInstance( ItemPool.ASTRAL_MUSHROOM ) );
if ( !KoLmafia.permitsContinue() )
{
this.isValidAdventure = false;
return;
}
}
// If we haven't selected a trip yet, do so now
if ( astral == 5 )
{
String choice;
if ( this.adventureId.equals( AdventurePool.BAD_TRIP_ID ) )
{
choice = "1";
}
else if ( this.adventureId.equals( AdventurePool.MEDIOCRE_TRIP_ID ) )
{
choice = "2";
}
else
{
choice = "3";
}
// Choose the trip
Preferences.setString( "choiceAdventure71", choice );
String name = this.getAdventureName();
Preferences.setString( "chosenTrip", name );
this.isValidAdventure = true;
return;
}
this.isValidAdventure = true;
return;
}
// The casino is unlocked provided the player
// has a casino pass in their inventory.
if ( this.zone.equals( "Casino" ) )
{
this.isValidAdventure = InventoryManager.retrieveItem( ItemPool.CASINO_PASS );
return;
}
// The island is unlocked provided the player
// has a dingy dinghy in their inventory.
if ( this.zone.equals( "Island" ) )
{
this.isValidAdventure = KoLCharacter.mysteriousIslandAccessible();
if ( this.isValidAdventure )
{
return;
}
this.isValidAdventure = InventoryManager.hasItem( ItemPool.DINGHY_PLANS );
if ( !this.isValidAdventure )
{
return;
}
this.isValidAdventure = InventoryManager.hasItem( ItemPool.DINGY_PLANKS );
if ( !this.isValidAdventure )
{
return;
}
RequestThread.postRequest( UseItemRequest.getInstance( ItemPool.DINGHY_PLANS ) );
return;
}
// The dungeons of doom are only available if you've finished the quest
if ( this.adventureId.equals( AdventurePool.DUNGEON_OF_DOOM_ID ) )
{
this.isValidAdventure = QuestLogRequest.isDungeonOfDoomAvailable();
return;
}
// The Castle Basement is unlocked provided the player has the S.O.C.K
// (legacy: rowboats give access but are no longer creatable)
if ( this.adventureId.equals( AdventurePool.CASTLE_BASEMENT_ID ) )
{
this.isValidAdventure =
InventoryManager.hasItem( ItemPool.get( ItemPool.SOCK, 1 ) ) ||
InventoryManager.hasItem( ItemPool.get( ItemPool.ROWBOAT, 1 ) );
return;
}
if ( this.adventureId.equals( AdventurePool.CASTLE_GROUND_ID ) )
{
this.isValidAdventure = Preferences.getInteger( "lastCastleGroundUnlock" ) == KoLCharacter.getAscensions();
return;
}
if ( this.adventureId.equals( AdventurePool.CASTLE_TOP_ID ) )
{
this.isValidAdventure = Preferences.getInteger( "lastCastleTopUnlock" ) == KoLCharacter.getAscensions();
return;
}
// The Hole in the Sky is unlocked provided the player has a steam-powered rocketship
// (legacy: rowboats give access but are no longer creatable)
if ( this.adventureId.equals( AdventurePool.HOLE_IN_THE_SKY_ID ) )
{
this.isValidAdventure =
InventoryManager.hasItem( ItemPool.get( ItemPool.ROCKETSHIP, 1 ) ) ||
InventoryManager.hasItem( ItemPool.get( ItemPool.ROWBOAT, 1 ) );
return;
}
// The beanstalk is unlocked when the player has planted a
// beanstalk -- but, the bean needs to be planted first.
if ( this.adventureId.equals( AdventurePool.AIRSHIP_ID ) && !QuestDatabase.isQuestLaterThan( Quest.GARBAGE, QuestDatabase.STARTED ) )
{
// If the character is not at least level 10, they have
// no chance to get to the beanstalk
if ( KoLCharacter.getLevel() < 10 )
{
this.isValidAdventure = false;
return;
}
GenericRequest request = new PlaceRequest( "plains" );
RequestThread.postRequest( request );
if ( !request.responseText.contains( "place.php?whichplace=beanstalk" ) )
{
// We see no beanstalk in the Nearby Plains.
// Acquire an enchanted bean and plant it.
if ( !KoLAdventure.getEnchantedBean() )
{
this.isValidAdventure = false;
return;
}
// Make sure the Council has given you the quest
if ( !QuestDatabase.isQuestLaterThan( Quest.GARBAGE, "unstarted" ) )
{
RequestThread.postRequest( CouncilCommand.COUNCIL_VISIT );
}
// Use the enchanted bean by clicking on the coffee grounds.
RequestThread.postRequest( new PlaceRequest( "plains", "garbage_grounds" ) );
}
this.isValidAdventure = true;
return;
}
if ( this.adventureId.equals( AdventurePool.HAUNTED_KITCHEN_ID ) ||
this.adventureId.equals( AdventurePool.HAUNTED_CONSERVATORY_ID ) )
{
// Haunted Kitchen & Conservatory
this.isValidAdventure = QuestDatabase.isQuestLaterThan( Quest.SPOOKYRAVEN_NECKLACE, QuestDatabase.UNSTARTED );
return;
}
if ( this.adventureId.equals( AdventurePool.HAUNTED_LIBRARY_ID ) )
{
// Haunted Library
this.isValidAdventure = InventoryManager.hasItem( ItemPool.LIBRARY_KEY );
return;
}
if ( this.adventureId.equals( AdventurePool.HAUNTED_BILLIARDS_ROOM_ID ) )
{
// Haunted Billiards Room
this.isValidAdventure = InventoryManager.hasItem( ItemPool.BILLIARDS_KEY );
return;
}
if ( this.adventureId.equals( AdventurePool.HAUNTED_BATHROOM_ID ) ||
this.adventureId.equals( AdventurePool.HAUNTED_BEDROOM_ID ) ||
this.adventureId.equals( AdventurePool.HAUNTED_GALLERY_ID ) )
{
// Haunted Bathroom, Bedroom & Gallery
this.isValidAdventure = QuestDatabase.isQuestLaterThan( Quest.SPOOKYRAVEN_DANCE, QuestDatabase.STARTED );
return;
}
if ( this.adventureId.equals( AdventurePool.HAUNTED_BALLROOM_ID ) )
{
// Haunted Ballroom
this.isValidAdventure = QuestDatabase.isQuestLaterThan( Quest.SPOOKYRAVEN_DANCE, "step2" );
return;
}
if ( this.adventureId.equals( AdventurePool.HAUNTED_LABORATORY_ID ) ||
this.adventureId.equals( AdventurePool.HAUNTED_NURSERY_ID ) ||
this.adventureId.equals( AdventurePool.HAUNTED_STORAGE_ROOM_ID ) )
{
// Haunted Lab, Nursery & Storage Room
this.isValidAdventure = QuestDatabase.isQuestLaterThan( Quest.SPOOKYRAVEN_DANCE, "step3" );
return;
}
if ( this.adventureId.equals( AdventurePool.MIDDLE_CHAMBER_ID ) )
{
this.isValidAdventure = Preferences.getBoolean( "middleChamberUnlock" );
return;
}
if ( this.adventureId.equals( AdventurePool.BATRAT_ID ) ||
this.adventureId.equals( AdventurePool.BEANBAT_ID ) ||
this.adventureId.equals( AdventurePool.BOSSBAT_ID ) )
{
if ( this.adventureId.equals( AdventurePool.BATRAT_ID ) && QuestDatabase.isQuestLaterThan( Quest.BAT, QuestDatabase.STARTED ) )
{
this.isValidAdventure = true;
return;
}
if ( this.adventureId.equals( AdventurePool.BEANBAT_ID ) && QuestDatabase.isQuestLaterThan( Quest.BAT, "step1" ) )
{
this.isValidAdventure = true;
return;
}
if ( this.adventureId.equals( AdventurePool.BOSSBAT_ID ) && QuestDatabase.isQuestLaterThan( Quest.BAT, "step2" ) )
{
this.isValidAdventure = true;
return;
}
int sonarToUse = 0;
if ( Preferences.getString( Quest.BAT.getPref() ).equals( QuestDatabase.STARTED ) )
{
sonarToUse = 3;
}
else if ( Preferences.getString( Quest.BAT.getPref() ).equals( "step1" ) )
{
sonarToUse = 2;
}
else if ( Preferences.getString( Quest.BAT.getPref() ).equals( "step2" ) )
{
sonarToUse = 1;
}
AdventureResult sonar = ItemPool.get( ItemPool.SONAR, 1 );
sonarToUse = Math.min( sonarToUse, sonar.getCount( KoLConstants.inventory ) );
RequestThread.postRequest( UseItemRequest.getInstance( ItemPool.get( ItemPool.SONAR, sonarToUse ) ) );
GenericRequest request = new PlaceRequest( "bathole" );
RequestThread.postRequest( request );
this.isValidAdventure =
this.adventureId.equals( AdventurePool.BATRAT_ID ) ?
request.responseText.contains( "batrockleft.gif" ) :
this.adventureId.equals( AdventurePool.BEANBAT_ID ) ?
request.responseText.contains( "batrockright.gif" ) :
request.responseText.contains( "batrockbottom.gif" );
return;
}
if ( this.adventureId.equals( AdventurePool.WHITEYS_GROVE_ID ) )
{
if ( !Preferences.getString( Quest.CITADEL.getPref() ).equals( "unstarted" ) ||
( QuestDatabase.isQuestLaterThan( Quest.PALINDOME, "step2" ) && !KoLCharacter.isEd() ) )
{
this.isValidAdventure = true;
return;
}
GenericRequest request = new GenericRequest( "woods.php" );
RequestThread.postRequest( request );
this.isValidAdventure = request.responseText.contains( "grove.gif" );
if ( !visitedCouncil && !this.isValidAdventure )
{
GuildUnlockManager.unlockGuild();
this.validate( true );
}
return;
}
if ( this.zone.equals( "McLarge" ) )
{
if ( this.adventureId.equals( AdventurePool.MINE_OFFICE_ID ) )
{
this.isValidAdventure = true;
return;
}
if ( this.adventureId.equals( AdventurePool.ITZNOTYERZITZ_MINE_ID ) ||
this.adventureId.equals( AdventurePool.GOATLET_ID ) )
{
this.isValidAdventure = QuestDatabase.isQuestLaterThan( Quest.TRAPPER, QuestDatabase.STARTED );
}
else if ( this.adventureId.equals( AdventurePool.NINJA_SNOWMEN_ID ) ||
this.adventureId.equals( AdventurePool.EXTREME_SLOPE_ID ) )
{
this.isValidAdventure = QuestDatabase.isQuestLaterThan( Quest.TRAPPER, "step1" );
}
else if ( this.adventureId.equals( AdventurePool.ICY_PEAK_ID ) )
{
this.isValidAdventure = QuestDatabase.isQuestFinished( Quest.TRAPPER );
}
else
{
this.isValidAdventure = true;
}
if ( this.isValidAdventure )
{
return;
}
if ( !this.isValidAdventure && visitedCouncil )
{
KoLmafia.updateDisplay( "You must complete a trapper task first." );
return;
}
RequestThread.postRequest( CouncilCommand.COUNCIL_VISIT );
RequestThread.postRequest( new PlaceRequest( "mclargehuge", "trappercabin" ) );
this.validate( true );
return;
}
if ( this.zone.equals( "Highlands" ) )
{
if ( QuestDatabase.isQuestLaterThan( Quest.TOPPING, QuestDatabase.STARTED ) )
{
this.isValidAdventure = true;
return;
}
return;
}
if ( this.adventureId.equals( AdventurePool.ICE_HOTEL_ID ) ||
this.adventureId.equals( AdventurePool.VYKEA_ID ) ||
this.adventureId.equals( AdventurePool.ICE_HOLE_ID ) )
{
boolean unlocked = Preferences.getBoolean( "coldAirportAlways" ) || Preferences.getBoolean( "_coldAirportToday" );
if ( !unlocked )
{
// Visit the airport and take a look.
RequestThread.postRequest( new PlaceRequest( "airport" ) );
unlocked = Preferences.getBoolean( "coldAirportAlways" ) || Preferences.getBoolean( "_coldAirportToday" );
}
this.isValidAdventure = unlocked;
return;
}
if ( this.adventureId.equals( AdventurePool.SMOOCH_ARMY_HQ_ID ) ||
this.adventureId.equals( AdventurePool.VELVET_GOLD_MINE_ID ) ||
this.adventureId.equals( AdventurePool.LAVACO_LAMP_FACTORY_ID ) ||
this.adventureId.equals( AdventurePool.BUBBLIN_CALDERA_ID ) )
{
boolean unlocked = Preferences.getBoolean( "hotAirportAlways" ) || Preferences.getBoolean( "_hotAirportToday" );
if ( !unlocked )
{
// Visit the airport and take a look.
RequestThread.postRequest( new PlaceRequest( "airport" ) );
unlocked = Preferences.getBoolean( "hotAirportAlways" ) || Preferences.getBoolean( "_hotAirportToday" );
}
this.isValidAdventure = unlocked;
return;
}
if ( this.adventureId.equals( AdventurePool.FUN_GUY_MANSION_ID ) ||
this.adventureId.equals( AdventurePool.SLOPPY_SECONDS_DINER_ID ) ||
this.adventureId.equals( AdventurePool.YACHT_ID ) )
{
boolean unlocked = Preferences.getBoolean( "sleazeAirportAlways" ) || Preferences.getBoolean( "_sleazeAirportToday" );
if ( !unlocked )
{
// Visit the airport and take a look.
RequestThread.postRequest( new PlaceRequest( "airport" ) );
unlocked = Preferences.getBoolean( "sleazeAirportAlways" ) || Preferences.getBoolean( "_sleazeAirportToday" );
}
this.isValidAdventure = unlocked;
return;
}
if ( this.adventureId.equals( AdventurePool.DR_WEIRDEAUX_ID ) ||
this.adventureId.equals( AdventurePool.SECRET_GOVERNMENT_LAB_ID ) ||
this.adventureId.equals( AdventurePool.DEEP_DARK_JUNGLE_ID ) )
{
boolean unlocked = Preferences.getBoolean( "spookyAirportAlways" ) || Preferences.getBoolean( "_spookyAirportToday" );
if ( !unlocked )
{
// Visit the airport and take a look.
RequestThread.postRequest( new PlaceRequest( "airport" ) );
unlocked = Preferences.getBoolean( "spookyAirportAlways" ) || Preferences.getBoolean( "_spookyAirportToday" );
}
this.isValidAdventure = unlocked;
return;
}
if ( this.adventureId.equals( AdventurePool.BARF_MOUNTAIN_ID ) ||
this.adventureId.equals( AdventurePool.GARBAGE_BARGES_ID ) ||
this.adventureId.equals( AdventurePool.TOXIC_TEACUPS_ID ) ||
this.adventureId.equals( AdventurePool.LIQUID_WASTE_SLUICE_ID ) )
{
boolean unlocked = Preferences.getBoolean( "stenchAirportAlways" ) || Preferences.getBoolean( "_stenchAirportToday" );
if ( !unlocked )
{
// Visit the airport and take a look.
RequestThread.postRequest( new PlaceRequest( "airport" ) );
unlocked = Preferences.getBoolean( "stenchAirportAlways" ) || Preferences.getBoolean( "_stenchAirportToday" );
}
this.isValidAdventure = unlocked;
return;
}
if ( this.adventureId.equals( AdventurePool.SPACEGATE_ID ) )
{
boolean unlocked = Preferences.getBoolean( "spacegateAlways" ) || Preferences.getBoolean( "_spacegateToday" );
if ( !unlocked )
{
// Visit the mountains and take a look.
RequestThread.postRequest( new PlaceRequest( "mountains" ) );
unlocked = Preferences.getBoolean( "_spacegateToday" );
}
this.isValidAdventure = unlocked;
return;
}
if ( this.adventureId.equals( AdventurePool.GINGERBREAD_CIVIC_CENTER_ID ) ||
this.adventureId.equals( AdventurePool.GINGERBREAD_TRAIN_STATION_ID ) ||
this.adventureId.equals( AdventurePool.GINGERBREAD_INDUSTRIAL_ZONE_ID ) ||
this.adventureId.equals( AdventurePool.GINGERBREAD_RETAIL_DISTRICT_ID ) ||
this.adventureId.equals( AdventurePool.GINGERBREAD_SEWERS_ID ) )
{
boolean unlocked = Preferences.getBoolean( "gingerbreadCityAvailable" ) || Preferences.getBoolean( "_gingerbreadCityToday" );
if ( !unlocked )
{
// Visit the Mountains and take a look.
RequestThread.postRequest( new PlaceRequest( "mountains" ) );
unlocked = Preferences.getBoolean( "_gingerbreadCityToday" );
}
this.isValidAdventure = unlocked;
return;
}
if ( this.adventureId.equals( AdventurePool.EDGE_OF_THE_SWAMP_ID ) )
{
this.isValidAdventure = QuestDatabase.isQuestLaterThan( Quest.SWAMP, "unstarted" );
return;
}
if ( this.adventureId.equals( AdventurePool.DARK_AND_SPOOKY_SWAMP_ID ) )
{
this.isValidAdventure = Preferences.getBoolean( "maraisDarkUnlock" );
return;
}
if ( this.adventureId.equals( AdventurePool.CORPSE_BOG_ID ) )
{
this.isValidAdventure = Preferences.getBoolean( "maraisCorpseUnlock" );
return;
}
if ( this.adventureId.equals( AdventurePool.RUINED_WIZARDS_TOWER_ID ) )
{
this.isValidAdventure = Preferences.getBoolean( "maraisWizardUnlock" );
return;
}
if ( this.adventureId.equals( AdventurePool.WILDLIFE_SANCTUARRRRRGH_ID ) )
{
this.isValidAdventure = Preferences.getBoolean( "maraisWildlifeUnlock" );
return;
}
if ( this.adventureId.equals( AdventurePool.WEIRD_SWAMP_VILLAGE_ID ) )
{
this.isValidAdventure = Preferences.getBoolean( "maraisVillageUnlock" );
return;
}
if ( this.adventureId.equals( AdventurePool.SWAMP_BEAVER_TERRITORY_ID ) )
{
this.isValidAdventure = Preferences.getBoolean( "maraisBeaverUnlock" );
return;
}
this.isValidAdventure = true;
}
private static boolean getEnchantedBean()
{
// Do we have an enchanted bean? Can we get one easily?
if ( InventoryManager.hasItem( ItemPool.ENCHANTED_BEAN ) )
{
return true;
}
// No. We can adventure for one. Ask the user if this is OK.
if ( StaticEntity.isGUIRequired() && GenericFrame.instanceExists() &&
!InputFieldUtilities.confirm( "KoLmafia thinks you haven't planted an enchanted bean yet. Would you like to have KoLmafia automatically adventure to obtain one?" ) )
{
return false;
}
// The user said "do it". So, do it!
KoLAdventure sideTripLocation = AdventureDatabase.getAdventure( "Beanbat Chamber" );
AdventureResult sideTripItem = ItemPool.get( ItemPool.ENCHANTED_BEAN, 1 );
GoalManager.makeSideTrip( sideTripLocation, sideTripItem );
return !KoLmafia.refusesContinue();
}
/**
* Retrieves the string form of the adventure contained within this encapsulation, which is generally the name of
* the adventure.
*
* @return The string form of the adventure
*/
@Override
public String toString()
{
return this.normalString;
}
/**
* Executes the appropriate <code>GenericRequest</code> for the adventure encapsulated by this
* <code>KoLAdventure</code>.
*/
public void run()
{
if ( RecoveryManager.isRecoveryPossible() )
{
if ( !RecoveryManager.runThresholdChecks() )
{
return;
}
}
this.validate( false );
if ( !this.isValidAdventure )
{
StringBuilder sewerMessage = new StringBuilder();
if (this.adventureId.equals( AdventurePool.HOBOPOLIS_SEWERS_ID )) {
sewerMessage.append("requireSewerTestItems is true so: ");
if (!(KoLCharacter.hasEquipped( ItemPool.get( ItemPool.GATORSKIN_UMBRELLA, 1 )))) {
sewerMessage.append("Equip a gatorskin umbrella. ");
}
if (!(KoLCharacter.hasEquipped( ItemPool.get( ItemPool.HOBO_CODE_BINDER, 1 ) ) ) ) {
sewerMessage.append("Equip a hobo code binder. ");
}
if (!InventoryManager.retrieveItem( ItemPool.SEWER_WAD )) {
sewerMessage.append("Acquire 1 sewer wad. ");
}
if (!InventoryManager.retrieveItem( ItemPool.OOZE_O )) {
sewerMessage.append("Acquire 1 bottle of Ooze-O. ");
}
if (!InventoryManager.retrieveItem( ItemPool.DUMPLINGS )) {
sewerMessage.append("Acquire 1 unfortunate dumpling. ");
}
if (!InventoryManager.retrieveItem( ItemPool.OIL_OF_OILINESS, 3 ) ) {
sewerMessage.append("Acquire 3 oil of oiliness. ");
}
} else {
sewerMessage.append("That area is not available.");
}
KoLmafia.updateDisplay( MafiaState.ERROR, sewerMessage.toString() );
return;
}
if ( this.getAdventureId().equals( AdventurePool.THE_SHORE_ID ) &&
KoLCharacter.getAvailableMeat() < ( KoLCharacter.inFistcore() ? 5 : 500 ) )
{
KoLmafia.updateDisplay( MafiaState.ERROR, "Insufficient funds for shore vacation." );
return;
}
String action = Preferences.getString( "battleAction" );
if ( this.request instanceof AdventureRequest && !this.adventureId.equals( AdventurePool.ORC_CHASM_ID ) )
{
if ( !this.isNonCombatsOnly() && action.contains( "dictionary" ) && FightRequest.DICTIONARY1.getCount( KoLConstants.inventory ) < 1 && FightRequest.DICTIONARY2.getCount( KoLConstants.inventory ) < 1 )
{
KoLmafia.updateDisplay( MafiaState.ERROR, "Sorry, you don't have a dictionary." );
return;
}
}
if ( this.areaSummary != null &&
!KoLCharacter.inZombiecore() &&
!KoLCharacter.inNuclearAutumn() &&
this.areaSummary.poison() <= Preferences.getInteger( "autoAntidote" ) &&
!KoLCharacter.hasEquipped( ItemPool.get( ItemPool.BEZOAR_RING, 1 ) ) )
{
SpecialOutfit.createImplicitCheckpoint();
InventoryManager.retrieveItem( ItemPool.ANTIDOTE );
SpecialOutfit.restoreImplicitCheckpoint();
}
if ( !KoLmafia.permitsContinue() )
{
return;
}
// Make sure there are enough adventures to run the request
// so that you don't spam the server unnecessarily.
if ( KoLCharacter.getAdventuresLeft() == 0 || KoLCharacter.getAdventuresLeft() < this.request.getAdventuresUsed() )
{
KoLmafia.updateDisplay( MafiaState.PENDING, "Ran out of adventures." );
return;
}
if ( !this.isNonCombatsOnly() && this.request instanceof AdventureRequest )
{
// Check for dictionaries as a battle strategy, if the
// person is not adventuring at the chasm.
if ( !this.adventureId.equals( AdventurePool.ORC_CHASM_ID ) && this.request.getAdventuresUsed() == 1 && action.contains( "dictionary" ) )
{
if ( !KoLCharacter.getFamiliar().isCombatFamiliar() )
{
KoLmafia.updateDisplay( MafiaState.ERROR, "A dictionary would be useless there." );
return;
}
}
// If the person doesn't stand a chance of surviving,
// automatically quit and tell them so.
if ( action.startsWith( "attack" ) && this.areaSummary != null && !this.areaSummary.willHitSomething() )
{
if ( !KoLCharacter.getFamiliar().isCombatFamiliar() )
{
KoLmafia.updateDisplay( MafiaState.ERROR, "You can't hit anything there." );
return;
}
}
if ( FightRequest.isInvalidShieldlessAttack( action ) )
{
KoLmafia.updateDisplay( MafiaState.ERROR,
"Your selected attack skill is useless without a shield." );
return;
}
}
if ( AdventureDatabase.isPotentialCloverAdventure( adventureName ) && InventoryManager.cloverProtectionActive() )
{
KoLmafia.protectClovers();
}
// If we get this far, then it is safe to run the request
// (without spamming the server).
KoLAdventure.setNextAdventure( this );
if ( RecoveryManager.isRecoveryPossible() )
{
RecoveryManager.runBetweenBattleChecks( !this.isNonCombatsOnly() );
if ( !KoLmafia.permitsContinue() )
{
return;
}
}
RequestThread.postRequest( this.request );
}
private static final Pattern ADVENTUREID_PATTERN = Pattern.compile( "snarfblat=([\\d]+)" );
private static final Pattern MINE_PATTERN = Pattern.compile( "mine=([\\d]+)" );
public static final KoLAdventure setLastAdventure( String adventureId, final String adventureName, String adventureURL, final String container )
{
KoLAdventure adventure = AdventureDatabase.getAdventureByURL( adventureURL );
if ( adventure == null )
{
int index = adventureURL.indexOf( "?" );
String adventurePage;
if ( index != -1 )
{
adventurePage = adventureURL.substring( 0, index );
}
else
{
adventurePage = adventureURL;
}
if ( adventurePage.equals( "mining.php" ) )
{
Matcher matcher= KoLAdventure.MINE_PATTERN.matcher( adventureURL );
adventureId = matcher.find() ? matcher.group( 1 ) : "0";
adventureURL = adventurePage + "?mine=" + adventureId;
}
else if ( adventurePage.equals( "adventure.php" ) )
{
if ( adventureId.equals( "" ) )
{
Matcher matcher= KoLAdventure.ADVENTUREID_PATTERN.matcher( adventureURL );
adventureId = matcher.find() ? matcher.group( 1 ) : "0";
}
}
else if ( adventurePage.equals( "main.php" ) )
{
// This is "(none)" after a new ascension
return null;
}
else if ( KoLAdventure.unknownAdventures.contains( adventureName ) )
{
// If we've already logged this one, don't do it again
return null;
}
else
{
// Don't register as an adventure, but save name
KoLAdventure.unknownAdventures.add( adventureName );
Preferences.setString( "lastAdventure", adventureName );
RequestLogger.updateSessionLog( "Unknown last adventure: id = '" + adventureId + "' name = '" + adventureName + "' URL = '" + adventureURL + "' container = '" + container + "'" );
return null;
}
RequestLogger.printLine( "Adding new location: " + adventureName + " - " + adventureURL );
// We could use "container" to pick the zone the adventure goes in
// Detach strings from the responseText
adventure = new KoLAdventure( "Override", new String( adventurePage ), new String( adventureId ), new String( adventureName ) );
AdventureDatabase.addAdventure( adventure );
}
KoLAdventure.setLastAdventure( adventure );
return adventure;
}
public static final void setLastAdventure( final KoLAdventure adventure )
{
if ( adventure == null )
{
return;
}
String adventureId = adventure.adventureId;
String adventureName = adventure.adventureName;
String adventureURL = adventure.formSource;
KoLAdventure.lastVisitedLocation = adventure;
KoLAdventure.lastLocationName = adventure.getPrettyAdventureName( adventureURL );
KoLAdventure.lastLocationURL = adventureURL;
Preferences.setString( "lastAdventure", adventureName );
// If you were able to access some hidden city areas you must have unlocked them so update quest status
if ( adventureId.equals( AdventurePool.HIDDEN_APARTMENT_ID ) &&
Preferences.getInteger( "hiddenApartmentProgress" ) == 0 )
{
Preferences.setInteger( "hiddenApartmentProgress", 1 );
}
else if ( adventureId.equals( AdventurePool.HIDDEN_HOSPITAL_ID ) &&
Preferences.getInteger( "hiddenHospitalProgress" ) == 0 )
{
Preferences.setInteger( "hiddenHospitalProgress", 1 );
}
else if ( adventureId.equals( AdventurePool.HIDDEN_OFFICE_ID ) &&
Preferences.getInteger( "hiddenOfficeProgress" ) == 0 )
{
Preferences.setInteger( "hiddenOfficeProgress", 1 );
}
else if ( adventureId.equals( AdventurePool.HIDDEN_BOWLING_ALLEY_ID ) &&
Preferences.getInteger( "hiddenBowlingAlleyProgress" ) == 0 )
{
Preferences.setInteger( "hiddenBowlingAlleyProgress", 1 );
}
}
public static final void setNextAdventure( final String adventureName )
{
KoLAdventure adventure = AdventureDatabase.getAdventure( adventureName );
if ( adventure == null )
{
Preferences.setString( "nextAdventure", adventureName );
KoLCharacter.updateSelectedLocation( null );
return;
}
KoLAdventure.setNextAdventure( adventure );
EncounterManager.registerAdventure( adventureName );
}
public static final void setNextAdventure( final KoLAdventure adventure )
{
if ( adventure == null )
{
return;
}
Preferences.setString( "nextAdventure", adventure.adventureName );
KoLCharacter.updateSelectedLocation( adventure );
NamedListenerRegistry.fireChange( "(koladventure)" );
}
public static final KoLAdventure lastVisitedLocation()
{
return KoLAdventure.lastVisitedLocation;
}
public static final int lastAdventureId()
{
KoLAdventure location = KoLAdventure.lastVisitedLocation;
return location == null || !StringUtilities.isNumeric( location.adventureId ) ?
0 : StringUtilities.parseInt( location.adventureId );
}
public static final String lastAdventureIdString()
{
KoLAdventure location = KoLAdventure.lastVisitedLocation;
return location == null ? "" : location.adventureId;
}
public static final boolean recordToSession( final String urlString )
{
// This is the first half of logging an adventure location
// given only the URL. We try to deduce where the player is
// adventuring and save it for verification later. We also do
// some location specific setup.
if ( KoLmafia.isRefreshing() )
{
return false;
}
// See if this is a standard "adventure" in adventures.txt
KoLAdventure adventure = KoLAdventure.findAdventure( urlString );
if ( adventure != null )
{
adventure.prepareToAdventure( urlString );
KoLAdventure.lastVisitedLocation = adventure;
KoLAdventure.lastLocationName = adventure.getPrettyAdventureName( urlString );
KoLAdventure.lastLocationURL = urlString;
KoLAdventure.locationLogged = false;
return true;
}
// No. See if it's a special "adventure"
String location = AdventureDatabase.getUnknownName( urlString );
if ( location == null )
{
return false;
}
if ( !urlString.contains( "?" ) )
{
return true;
}
KoLAdventure.lastVisitedLocation = null;
KoLAdventure.lastLocationName = location;
KoLAdventure.lastLocationURL = urlString;
KoLAdventure.locationLogged = false;
return true;
}
private static KoLAdventure findAdventure( final String urlString )
{
if ( urlString.equals( "barrel.php" ) )
{
return null;
}
if ( urlString.startsWith( "mining.php" ) && urlString.contains( "intro=1" ) )
{
return null;
}
return AdventureDatabase.getAdventureByURL( urlString );
}
private static final KoLAdventure findAdventureAgain( final String responseText )
{
// Look for an "Adventure Again" link and return the
// KoLAdventure that it matches.
Matcher matcher = ADVENTURE_AGAIN.matcher( responseText );
if ( !matcher.find() )
{
return null;
}
return KoLAdventure.findAdventure( matcher.group(1) );
}
private void prepareToAdventure( final String urlString )
{
// If we are in a drunken stupor, return now.
if ( KoLCharacter.isFallingDown() &&
!urlString.startsWith( "trickortreat" ) &&
!KoLCharacter.hasEquipped( ItemPool.get( ItemPool.DRUNKULA_WINEGLASS, 1 ) ) )
{
return;
}
int id = 0;
if ( StringUtilities.isNumeric( this.adventureId ) )
{
id = StringUtilities.parseInt( this.adventureId );
switch ( id )
{
case AdventurePool.FCLE:
AdventureResult mop = ItemPool.get( ItemPool.MIZZENMAST_MOP, 1 );
AdventureResult polish = ItemPool.get( ItemPool.BALL_POLISH, 1 );
AdventureResult sham = ItemPool.get( ItemPool.RIGGING_SHAMPOO, 1 );
if ( InventoryManager.hasItem( mop ) &&
InventoryManager.hasItem( polish ) &&
InventoryManager.hasItem( sham ) )
{
RequestThread.postRequest( UseItemRequest.getInstance( mop ) );
RequestThread.postRequest( UseItemRequest.getInstance( polish ) );
RequestThread.postRequest( UseItemRequest.getInstance( sham ) );
}
break;
}
}
if ( !( this.getRequest() instanceof AdventureRequest ) || this.isValidAdventure )
{
return;
}
// Visit the untinker before adventuring at Degrassi Knoll.
if ( id == AdventurePool.DEGRASSI_KNOLL_GARAGE )
{
UntinkerRequest.canUntinker();
}
this.isValidAdventure = true;
// Make sure you're wearing the appropriate equipment
// for the King's chamber in Cobb's knob.
if ( this.formSource.equals( "cobbsknob.php" ) )
{
this.validate( true );
}
}
// Automated adventuring in an area can result in a failure. We go into
// the ERROR or the PENDING state, depending on whether we should stop
// a script for attempting the adventure. The default is ERROR. Use
// PENDING only when the script could not have known that the attempt
// would fail.
private static final Object [][] ADVENTURE_FAILURES =
{
// KoL bug: returning a blank page. This must be index 0.
{
"",
"KoL returned a blank page.",
},
// Lots of places.
{
"It is recommended that you have at least",
"Your stats are too low for this location. Adventure manually to acknowledge or disable this warning.",
},
// Lots of places.
{
"You shouldn't be here",
"You can't get to that area.",
},
// Lots of places.
{
"not yet be accessible",
"You can't get to that area.",
},
// Lots of places.
{
"You can't get there",
"You can't get to that area.",
},
// Lots of places.
{
"Seriously. It's locked.",
"You can't get to that area.",
},
// 8-bit realm and Vanya's Castle
{
"You can't get to the 8-bit realm right now",
"You can't get to that area.",
},
// Out of adventures
{
"You're out of adventures",
"You're out of adventures.",
MafiaState.PENDING
},
// Out of adventures in the Daily Dungeon
{
"You don't have any adventures.",
"You're out of adventures.",
MafiaState.PENDING
},
// Out of adventures at Shore
{
"You don't have enough Adventures left",
"You're out of adventures.",
MafiaState.PENDING
},
// Out of meat at Shore
{
"You can't afford to go on a vacation",
"You can't afford to go on a vacation.",
},
// Too drunk at shore
{
"You're too drunk to go on vacation",
"You are too drunk to go on a vacation.",
},
// Beaten up at zero HP
{
"You're way too beaten up to go on an adventure right now",
"You can't adventure at 0 HP.",
MafiaState.PENDING
},
// Typical Tavern with less than 100 Meat
{
"Why go to the Tavern if you can't afford to drink?",
"You can't afford to go out drinking.",
},
// The Road to White Citadel
{
"You've already found the White Citadel",
"The Road to the White Citadel is already cleared.",
},
// Friar's Ceremony Location without the three items
{
"You don't appear to have all of the elements necessary to perform the ritual",
"You don't have everything you need.",
},
// You need some sort of stench protection to adventure in there.
// You're going to need some sort of stench protection if you want to adventure here.
{
"need some sort of stench protection",
"You need stench protection.",
},
// You need some sort of protection from the cold if you're
// going to visit the Icy Peak.
{
"You need some sort of protection from the cold",
"You need cold protection.",
},
// You try to enter the Haunted Library, but the door is locked. I guess this particular information doesn't want to be free.
{
"I guess this particular information doesn't want to be free",
"You need the Spookyraven library key.",
},
// Mining while drunk
{
"You're too drunk to spelunk, as it were",
"You are too drunk to go there.",
},
// Pyramid Lower Chamber while drunk
{
"You're too drunk to screw around",
"You are too drunk to go there.",
},
// You can't adventure there without some way of breathing underwater...
{
"without some way of breathing underwater",
"You can't breathe underwater.",
},
// You can't adventure there now -- Gort wouldn't be able to breathe!
{
"wouldn't be able to breathe",
"Your familiar can't breathe underwater.",
},
// It wouldn't be safe to go in there dressed like you are. You should consider a Mer-kin disguise.
{
"You should consider a Mer-kin disguise.",
"You aren't wearing a Mer-kin disguise.",
},
// Attempting to enter the Cola Wars Battlefield with level > 5
{
"The temporal rift in the plains has closed",
"The temporal rift has closed.",
MafiaState.PENDING
},
// Out of your mining uniform, you are quickly identified as a
// stranger and shown the door.
{
"you are quickly identified as a stranger",
"You aren't wearing an appropriate uniform.",
},
// You're not properly equipped for that. Get into a uniform.
{
"Get into a uniform",
"You aren't wearing an appropriate uniform.",
},
// There are no Frat soldiers left
{
"There are no Frat soldiers left",
"There are no Frat soldiers left.",
},
// There are no Hippy soldiers left
{
"There are no Hippy soldiers left",
"There are no Hippy soldiers left.",
},
// Spooky Gravy Burrow before told to go there:
// You should probably stay out of there unless you have a good
// reason to go in. Like if you were on a quest to find
// something in there, or something.
{
"You should probably stay out of there",
"You have not been given the quest to go there yet.",
MafiaState.PENDING
},
// Worm Wood while not Absinthe Minded
{
"For some reason, you can't find your way back there",
"You need to be Absinthe Minded to go there.",
MafiaState.PENDING
},
// "You can't take it any more. The confusion, the nostalgia,
// the inconsistent grammar. You break the bottle on the
// ground, and stomp it to powder."
{
"You break the bottle on the ground",
"You are no longer gazing into the bottle.",
MafiaState.PENDING
},
// You're in the regular dimension now, and don't remember how
// to get back there.
{
"You're in the regular dimension now",
"You are no longer Half-Astral.",
MafiaState.PENDING
},
// The Factory has faded back into the spectral mists, and
// eldritch vapors and such.
{
"faded back into the spectral mists",
"No one may know of its coming or going."
},
// You wander around the farm for a while, but can't find any
// additional ducks to fight. Maybe some more will come out of
// hiding by tomorrow.
{
"can't find any additional ducks",
"Nothing more to do here today.",
MafiaState.PENDING
},
// There are no more ducks here.
{
"no more ducks here",
"Farm area cleared.",
MafiaState.PENDING
},
// You don't know where that place is.
{
"You don't know where that place is.",
"Use a \"DRINK ME\" potion before trying to adventure here.",
MafiaState.PENDING
},
// Orchard failure - You try to enter the feeding chamber, but
// your way is blocked by a wriggling mass of filthworm drones.
// Looks like they don't let anything in here if they don't
// recognize its smell.
{
"Looks like they don't let anything in here if they don't recognize its smell.",
"Use a filthworm hatchling scent gland before trying to adventure here.",
},
// Orchard failure - You try to enter the royal guards'
// chamber, but you're immediately shoved back out into the
// tunnel. Looks like the guards will only let you in here if
// you smell like food.
{
"Looks like the guards will only let you in here if you smell like food.",
"Use a filthworm drone scent gland before trying to adventure here.",
},
// Orchard failure - You try to enter the filthworm queen's
// chamber, but the guards outside the door block the entrance.
// You must not smell right to 'em.
{
"You must not smell right to 'em.",
"Use a filthworm royal guard scent gland before trying to adventure here.",
},
// Orchard failure - The filthworm queen has been slain, and the
// hive lies empty 'neath the orchard.
{
"The filthworm queen has been slain",
"The filthworm queen has been slain.",
},
// You've already retrieved all of the stolen Meat
{
"already retrieved all of the stolen Meat",
"You already recovered the Nuns' Meat.",
MafiaState.PENDING
},
// There are no hippy soldiers left -- the way to their camp is clear!
{
"the way to their camp is clear",
"There are no hippy soldiers left.",
MafiaState.PENDING
},
// Cobb's Knob King's Chamber after defeating the goblin king.
{
"You've already slain the Goblin King",
"You already defeated the Goblin King.",
MafiaState.PENDING
},
// The Haert of the Cyrpt after defeating the Bonerdagon
{
"Bonerdagon has been defeated",
"You already defeated the Bonerdagon.",
MafiaState.PENDING
},
// Any cyrpt area after defeating the sub-boss
{
"already undefiled",
"Cyrpt area cleared.",
MafiaState.PENDING
},
// The Summoning Chamber after Lord Spookyraven has been defeated
//
// You enter the Summoning Chamber. The air is heavy with
// evil, and otherworldly whispers echo melodramatically
// through your mind
{
"otherworldly whispers",
"You already defeated Lord Spookyraven.",
MafiaState.PENDING
},
// Ed the undying defeated
{
"Ed the Undying sleeps once again",
"Ed the Undying has already been defeated.",
MafiaState.PENDING
},
// You probably shouldn't -- you don't trust those rats not to steal your token!
{
"don't trust those rats not to steal",
"You don't trust those rats not to steal your token!.",
MafiaState.PENDING
},
// That's too far to walk, and
// <a href=clan_dreadsylvania.php?place=carriage>the Carriageman</a>
// isn't drunk enough to take you there.
{
"That's too far to walk",
"The Carriageman isn't drunk enough to take you there.",
MafiaState.PENDING
},
// The forest is silent, those who stalked it having themselves been stalked.
{
"The forest is silent",
"The Dreadsylvanian Woods boss has been defeated.",
MafiaState.PENDING
},
// The village is now a ghost town in the figurative sense, rather than the literal.
{
"The village is now a ghost town",
"The Dreadsylvanian Village boss has been defeated.",
MafiaState.PENDING
},
// Look upon this castle, ye mighty, and despair, because the king is dead, baby.
{
"the king is dead, baby",
"The Dreadsylvanian Castle boss has been defeated.",
MafiaState.PENDING
},
// This part of the city is awfully unremarkable, now that
// you've cleared that ancient protector spirit out.
{
"cleared that ancient protector spirit out",
"You already defeated the protector spirit in that square.",
},
// Now that you've put something in the round depression in the
// altar, the altar doesn't really do anything but look
// neat. Those ancient guys really knew how to carve themselves
// an altar, mmhmm.
{
"the altar doesn't really do anything but look neat",
"You already used the altar in that square.",
},
// Here's poor Dr. Henry "Dakota" Fanning, Ph.D, R.I.P., lying
// here in a pile just where you left him.
{
"lying here in a pile just where you left him",
"You already looted Dr. Fanning in that square.",
},
// You wander into the empty temple and look around. Remember
// when you were in here before, and tried to gank some old
// amulet off of that mummy? And then its ghost came out and
// tried to kill you? But you destroyed it, and won the ancient
// doohickey?
//
// Good times, man. Good times.
{
"You wander into the empty temple",
"You already looted the temple in that square.",
},
// You climb the stairs from the castle's basement, but the
// door at the top is closed and you can't find the doorknob.
//
// You'll have to find another way up.
{
"You'll have to find another way up",
"You haven't opened the ground floor of the castle yet.",
},
// You have to learn to walk before you can learn to fly.
//
// Also you can't get to the top floor of a building if you can't get to the ground floor.
{
"you can't get to the ground floor",
"You haven't opened the ground floor of the castle yet.",
},
// The door at the top of the ground floor stairway is also
// closed, and you're still too short to reach a doorknob
// that's forty feet over your head.
//
// You'll have to figure out some other way to get upstairs.
{
"You'll have to figure out some other way to get upstairs",
"You haven't opened the top floor of the castle yet.",
},
// The portal is open! Head home and prepare to save some children!
{
"prepare to save some children",
"The portal is open.",
},
// It looks like things are running pretty smoothly at the
// factory right now -- there's nobody to fight.
{
"things are running pretty smoothly",
"Nothing more to do here today.",
},
// You should talk to Edwing before you head back in there, and
// wait for him to formulate a plan.
{
"You should talk to Edwing",
"Nothing more to do here today.",
},
// The compound is abandoned now...
{
"The compound is abandoned now",
"Nothing more to do here today.",
},
// Between the wind and the weird spiky bits all over it, you
// can't make it to the second story of the fortress without
// some way of escaping gravity.
{
"some way of escaping gravity",
"You are not wearing a warbear hoverbelt.",
},
// Your hoverbelt would totally do the trick to get you up
// there, only it's out of juice.
{
"it's out of juice",
"Your hoverbelt needs a new battery.",
},
// You float up to the third story of the fortress, but all you
// find is a locked door with a keypad next to it, and you
// don't have a code.
{
"you don't have a code",
"You don't have a warbear badge.",
},
// There's nothing left of Ol' Scratch but a crater and a
// stove. Burnbarrel Blvd. is still hot, but it's no longer
// bothered. Or worth bothering with.
{
"There's nothing left of Ol' Scratch",
"Nothing more to do here.",
MafiaState.PENDING
},
// There's nothing left in Exposure Esplanade. All of the snow
// forts have been crushed or melted, all of the igloos are
// vacant, and all of the reindeer are off playing games
// somewhere else.
{
"There's nothing left in Exposure Esplanade",
"Nothing more to do here.",
MafiaState.PENDING
},
// The Heap is empty. Well, let me rephrase that. It's still
// full of garbage, but there's nobody and nothing of interest
// mixed in with the garbage.
{
"The Heap is empty",
"Nothing more to do here.",
MafiaState.PENDING
},
// There's nothing going on here anymore -- the tombs of the
// Ancient Hobo Burial Ground are all as silent as themselves.
{
"There's nothing going on here anymore",
"Nothing more to do here.",
MafiaState.PENDING
},
// There's nothing left in the Purple Light District. All of
// the pawn shops and adult bookshops have closed their doors
// for good.
{
"There's nothing left in the Purple Light District",
"Nothing more to do here.",
MafiaState.PENDING
},
// The Hoboverlord has been defeated, and Hobopolis Town Square
// lies empty.
{
"Hobopolis Town Square lies empty",
"Nothing more to do here.",
MafiaState.PENDING
},
// The bathrooms are empty now -- looks like you've taken care
// of the elf hobo problem for the time being.
{
"bathrooms are empty now",
"Nothing more to do here.",
MafiaState.PENDING
},
// The Skies over Valhalls
// You poke your head through the slash, and find yourself
// looking at Valhalla from a dizzying height. Come down now,
// they'll say, but there's no way you're going all the way
// through that slash without some sort of transportation.
{
"there's no way you're going all the way through that slash",
"You don't have a flying mount.",
MafiaState.PENDING
},
// You can't do anything without some way of flying. And
// before you go pointing at all of the stuff in your inventory
// that says it lets you fly or float or levitate or whatever,
// that stuff won't work. You're gonna need a hideous winged
// yeti mount, because that's the only thing that can handle
// this particular kind of flying. Because of science.
{
"You can't do anything without some way of flying",
"You don't have a flying mount.",
MafiaState.PENDING
},
// There are at least two of everything up there, and you're
// also worried that you might fall off your yeti. You should
// maybe come back when you're at least slightly less drunk.
{
"You should maybe come back when you're at least slightly less drunk",
"You are too drunk.",
MafiaState.PENDING
},
// You don't have the energy to attack a problem this size. Go
// drink some soda or something.
{
"You don't have the energy to attack a problem this size",
"You need at least 20% buffed max MP.",
MafiaState.PENDING
},
// You're not in good enough shape to deal with a threat this
// large. Go get some rest, or put on some band-aids or
// something.
{
"You're not in good enough shape to deal with a threat this large",
"You need at least 20% buffed max HP.",
MafiaState.PENDING
},
// Your El Vibrato portal has run out of power. You should go
// back to your campsite and charge it back up.
{
"Your El Vibrato portal has run out of power",
"Your El Vibrato portal has run out of power",
MafiaState.PENDING
},
// No longer Transpondent
{
"you don't know the transporter frequency",
"You are no longer Transpondent.",
},
// No longer Transpondent
{
"without the proper transporter frequency",
"You are no longer Transpondent.",
},
// No longer Dis Abled
//
// Remember that devilish folio you read?
// No, you don't! You don't have it all still in your head!
// Better find a new one you can read! I swear this:
// 'Til you do, you can't visit the Suburbs of Dis!
{
"you can't visit the Suburbs of Dis",
"You are no longer Dis Abled.",
},
// Abyssal Portals
//
// The area around the portal is quiet. Looks like you took
// care of all of the seals. Maybe check back tomorrow.
{
"area around the portal is quiet",
"The Abyssal Portal is quiet.",
},
// The Secret Government Laboratory
//
// You can't go in there without wearing a Personal Ventilation
// Unit. Who knows what would happen if you breathed the air in
// there.
{
"Who knows what would happen if you breathed the air",
"You need to equip your Personal Ventilation Unit.",
},
// GameInformPowerPro video game levels
//
// You already cleared out this area.
{
"You already cleared out this area",
"You already cleared out this area",
},
// This area is closed.
{
"This area is closed",
"You completed the video game",
},
// You wander around, off the Florida Keys, but can't find anything
{
"off the Florida Keys",
"You need a Tropical Contact High to go there.",
},
// You approach the adorable little door, but you can't figure
// out how to open it. I guess it's a secret door that will
// only open for gravy fairies.
{
"only open for gravy fairies",
"You need to bring an elemental gravy fairy with you.",
},
// You shouldn't be here dressed like that. It's just not safe.
{
"You shouldn't be here dressed like that",
"You can't pass as a pirate.",
},
// LOLmec's lair lies lempty. Empty.
{
"LOLmec's lair lies lempty",
"You already beat LOLmec",
},
// Already beat Yomama.
{
"Already beat Yomama",
"You already beat Yomama",
},
// The ghost has arrived. Your time has run out!
{
"The ghost has arrived",
"Your tale of spelunking is over.",
},
// Gingerbread City
{
"The gingerbread city has collapsed.",
"The gingerbread city has collapsed.",
},
// Hole in the sky
// You can see it, but you can't get to it without some means of traveling...
//
// TO SPACE
{
"you can't get to it",
"You need a way to travel in space.",
},
// The spacegate is out of energy for today. You can explore
// another planet tomorrow. Or the same planet, if you
// want. The spacegate isn't the boss of you.
{
"out of energy for today",
"The Spacegate is out of energy for today.",
}
};
public static final int findAdventureFailure( String responseText )
{
// KoL is known to sometimes simply return a blank page as a
// failure to adventure.
if ( responseText.length() == 0 )
{
return 0;
}
for ( int i = 1; i < ADVENTURE_FAILURES.length; ++i )
{
if ( responseText.contains( (String) ADVENTURE_FAILURES[ i ][ 0 ] ) )
{
return i;
}
}
return -1;
}
public static final String adventureFailureMessage( int index )
{
if ( index >= 0 && index < ADVENTURE_FAILURES.length )
{
return (String) ADVENTURE_FAILURES[ index ][ 1 ];
}
return null;
}
public static final MafiaState adventureFailureSeverity( int index )
{
if ( index >= 0 && index < ADVENTURE_FAILURES.length && ADVENTURE_FAILURES[ index ].length > 2 )
{
return (MafiaState) ADVENTURE_FAILURES[ index ][ 2 ];
}
return MafiaState.ERROR;
}
public static final boolean recordToSession( final String urlString, final String responseText )
{
// This is the second half of logging an adventure location
// after we've submitted the URL and gotten a response, after,
// perhaps, being redirected. Given the old URL, the new URL,
// and the response, we can often do a better job of figuring
// out where we REALLY adventured - if anywhere.
if ( KoLmafia.isRefreshing() )
{
return true;
}
// Only do this once per adventure attempt.
if ( KoLAdventure.locationLogged )
{
return true;
}
String location = KoLAdventure.lastLocationName;
if ( location == null )
{
return false;
}
// Only do this once per adventure attempt.
KoLAdventure.locationLogged = true;
String lastURL = KoLAdventure.lastLocationURL;
if ( lastURL.equals( "basement.php" ) )
{
return true;
}
// See if we've been redirected away from the URL that started
// us adventuring
if ( urlString.equals( lastURL ) )
{
// No. It is possible that we didn't adventure at all
if ( KoLAdventure.findAdventureFailure( responseText ) >= 0 )
{
return false;
}
// See if there is an "adventure again" link, and if
// so, whether it points to where we thought we went.
KoLAdventure again = KoLAdventure.findAdventureAgain( responseText );
if ( again != null && again != KoLAdventure.lastVisitedLocation )
{
location = again.getPrettyAdventureName( urlString );
KoLAdventure.lastVisitedLocation = again;
KoLAdventure.lastLocationName = location;
KoLAdventure.lastLocationURL = urlString;
}
}
else if ( urlString.equals( "cove.php" ) )
{
// Redirected from Pirate Cove to the cove map
return false;
}
else if ( urlString.startsWith( "mining.php" ) )
{
// Redirected to a mine
return false;
}
else if ( urlString.startsWith( "fight.php" ) )
{
// Redirected to a fight. We may or may not be
// adventuring where we thought we were. If your
// autoattack one-hit-kills the foe, the adventure
// again link will tell us where you were.
KoLAdventure again = KoLAdventure.findAdventureAgain( responseText );
if ( again != null && again != lastVisitedLocation )
{
location = again.adventureName;
KoLAdventure.lastVisitedLocation = again;
KoLAdventure.lastLocationName = location;
}
}
else if ( urlString.startsWith( "choice.php" ) )
{
// Redirected to a choice. We may or may not be
// adventuring where we thought we were.
}
// Customize location name, perhaps
location = KoLAdventure.getPrettyAdventureName( location, responseText );
// Update selected adventure information in order to
// keep the GUI synchronized.
KoLAdventure.setLastAdventure( KoLAdventure.lastVisitedLocation );
KoLAdventure.setNextAdventure( KoLAdventure.lastVisitedLocation );
EncounterManager.registerAdventure( location );
String limitmode = KoLCharacter.getLimitmode();
String message = null;
if ( limitmode == Limitmode.SPELUNKY )
{
message = "{" + SpelunkyRequest.getTurnsLeft() + "} " + location;
}
else if ( limitmode == Limitmode.BATMAN )
{
message = "{" + BatManager.getTimeLeftString() + "} " + location;
}
else
{
message = "[" + KoLAdventure.getAdventureCount() + "] " + location;
}
RequestLogger.printLine();
RequestLogger.printLine( message );
RequestLogger.updateSessionLog();
RequestLogger.updateSessionLog( message );
String encounter = "";
if ( urlString.startsWith( "basement.php" ) )
{
encounter = BasementRequest.getBasementLevelSummary();
}
if ( !encounter.equals( "" ) )
{
RequestLogger.printLine( encounter );
RequestLogger.updateSessionLog( encounter );
}
return true;
}
public static final int getAdventureCount()
{
return Preferences.getBoolean( "logReverseOrder" ) ? KoLCharacter.getAdventuresLeft() : KoLCharacter.getCurrentRun() + 1;
}
public int compareTo( final KoLAdventure o )
{
if ( o == null || !( o instanceof KoLAdventure ) )
{
return 1;
}
KoLAdventure ka = (KoLAdventure) o;
// Put things with no evade rating at bottom of list.
int evade1 = this.areaSummary == null ? Integer.MAX_VALUE : this.areaSummary.minEvade();
int evade2 = ka.areaSummary == null ? Integer.MAX_VALUE : ka.areaSummary.minEvade();
if ( evade1 == evade2 )
{
return this.adventureName.compareTo( ka.adventureName );
}
return evade1 - evade2;
}
}