/**
* 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.request;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import net.sourceforge.kolmafia.AdventureResult;
import net.sourceforge.kolmafia.FamiliarData;
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.KoLmafiaCLI;
import net.sourceforge.kolmafia.Modifiers;
import net.sourceforge.kolmafia.RequestThread;
import net.sourceforge.kolmafia.SpecialOutfit;
import net.sourceforge.kolmafia.moods.MoodManager;
import net.sourceforge.kolmafia.moods.RecoveryManager;
import net.sourceforge.kolmafia.objectpool.EffectPool;
import net.sourceforge.kolmafia.objectpool.FamiliarPool;
import net.sourceforge.kolmafia.objectpool.ItemPool;
import net.sourceforge.kolmafia.persistence.MonsterDatabase.Element;
import net.sourceforge.kolmafia.session.EquipmentManager;
import net.sourceforge.kolmafia.utilities.StringUtilities;
import net.sourceforge.kolmafia.webui.BasementDecorator.StatBooster;
public class BasementRequest
extends AdventureRequest
{
private enum TestType
{
NONE,
MONSTER,
REWARD,
ELEMENT,
MUSCLE,
MYSTICALITY,
MOXIE,
MPDRAIN,
HPDRAIN,
;
}
private static final double SAFETY_MARGIN = 1.08;
private static int basementLevel = 0;
private static TestType basementTest = TestType.NONE;
private static String basementTestString = "";
private static double basementTestValue = 0;
private static double basementTestCurrent = 0;
public static String basementMonster = "";
private static String gauntletString = "";
private static int actualStatNeeded = 0;
private static int primaryBoost = 0;
private static int secondaryBoost = 0;
private static double averageResistanceNeeded = 0.0;
private static Element element1 = Element.NONE, element2 = Element.NONE;
private static int vulnerability = 0;
private static Element goodelement = Element.NONE;
private static AdventureResult goodphial = null;
private static AdventureResult goodeffect = null;
private static Element badelement1 = Element.NONE, badelement2 = Element.NONE, badelement3 = Element.NONE;
private static AdventureResult badeffect1 = null, badeffect2 = null, badeffect3 = null;
private static ArrayList<AdventureResult> desirableEffects = new ArrayList<AdventureResult>();
private static int level1, level2;
private static double resistance1, resistance2;
private static double expected1, expected2;
private static String lastResponseText = "";
private static String basementErrorMessage = null;
public static final AdventureResult MUS_EQUAL = EffectPool.get( EffectPool.STABILIZING_OILINESS );
public static final AdventureResult MYS_EQUAL = EffectPool.get( EffectPool.EXPERT_OILINESS );
public static final AdventureResult MOX_EQUAL = EffectPool.get( EffectPool.SLIPPERY_OILINESS );
private static final AdventureResult BLACK_PAINT = EffectPool.get( EffectPool.RED_DOOR_SYNDROME );
private static final AdventureResult HOT_PHIAL = ItemPool.get( ItemPool.PHIAL_OF_HOTNESS, 1 );
private static final AdventureResult COLD_PHIAL = ItemPool.get( ItemPool.PHIAL_OF_COLDNESS, 1 );
private static final AdventureResult SPOOKY_PHIAL = ItemPool.get( ItemPool.PHIAL_OF_SPOOKINESS, 1 );
private static final AdventureResult STENCH_PHIAL = ItemPool.get( ItemPool.PHIAL_OF_STENCH, 1 );
private static final AdventureResult SLEAZE_PHIAL = ItemPool.get( ItemPool.PHIAL_OF_SLEAZINESS, 1 );
public static final AdventureResult MAX_HOT = EffectPool.get( EffectPool.FIREPROOF_LIPS );
public static final AdventureResult MAX_COLD = EffectPool.get( EffectPool.FEVER_FROM_THE_FLAVOR );
public static final AdventureResult MAX_SPOOKY = EffectPool.get( EffectPool.HYPHEMARIFFIC );
public static final AdventureResult MAX_STENCH = EffectPool.get( EffectPool.CANT_SMELL_NOTHING );
public static final AdventureResult MAX_SLEAZE = EffectPool.get( EffectPool.HYPEROFFENDED );
private static final AdventureResult HOT_FORM = EffectPool.get( EffectPool.HOTFORM );
private static final AdventureResult COLD_FORM = EffectPool.get( EffectPool.COLDFORM );
private static final AdventureResult SPOOKY_FORM = EffectPool.get( EffectPool.SPOOKYFORM );
private static final AdventureResult STENCH_FORM = EffectPool.get( EffectPool.STENCHFORM );
private static final AdventureResult SLEAZE_FORM = EffectPool.get( EffectPool.SLEAZEFORM );
private static final Pattern BASEMENT_PATTERN = Pattern.compile( "Level ([\\d,]+)" );
public static final AdventureResult[] ELEMENT_PHIALS =
new AdventureResult[]
{
BasementRequest.HOT_PHIAL,
BasementRequest.COLD_PHIAL,
BasementRequest.SPOOKY_PHIAL,
BasementRequest.STENCH_PHIAL,
BasementRequest.SLEAZE_PHIAL
};
public static final AdventureResult[] ELEMENT_FORMS =
new AdventureResult[]
{
BasementRequest.HOT_FORM,
BasementRequest.COLD_FORM,
BasementRequest.SPOOKY_FORM,
BasementRequest.STENCH_FORM,
BasementRequest.SLEAZE_FORM
};
public static final boolean isElementalImmunity( final String name )
{
for ( int j = 0; j < BasementRequest.ELEMENT_FORMS.length; ++j )
{
if ( name.equals( BasementRequest.ELEMENT_FORMS[ j ].getName() ) )
{
return true;
}
}
return false;
}
public static final FamiliarData SANDWORM =
new FamiliarData( FamiliarPool.SANDWORM );
/**
* Constructs a new <code>/code> which executes an
* adventure in Fernswarthy's Basement by posting to the provided form,
* notifying the givenof results (or errors).
*
* @param adventureName The name of the adventure location
* @param formSource The form to which the data will be posted
* @param adventureId The identifier for the adventure to be executed
*/
public BasementRequest( final String adventureName )
{
super( adventureName, "basement.php", "0" );
}
@Override
public void run()
{
// Clear the data flags and probe the basement to see what we have.
this.data.clear();
super.run();
// Load up the data variables and switch outfits if it's a fight.
BasementRequest.checkBasement();
// If we know we can't pass the test, give an error and bail out now.
if ( BasementRequest.basementErrorMessage != null )
{
KoLmafia.updateDisplay( MafiaState.ERROR, BasementRequest.basementErrorMessage );
return;
}
// Decide which action to set. If it's a stat reward, always
// boost prime stat.
this.addFormField( "action", BasementRequest.getBasementAction( this.responseText ) );
// Attempt to pass the test.
int lastBasementLevel = BasementRequest.basementLevel;
super.run();
// Handle redirection
if ( this.responseCode != 200 )
{
// If it was a fight and we won, good.
if ( FightRequest.INSTANCE.responseCode == 200 && FightRequest.lastResponseText.indexOf( "<!--WINWINWIN-->" ) != -1 )
{
return;
}
// Otherwise ... what is this? Refetch the page and see if we passed test.
this.data.clear();
super.run();
}
// See what basement level we are on now and fail if we've not advanced.
if ( BasementRequest.basementLevel == lastBasementLevel )
{
KoLmafia.updateDisplay( MafiaState.ERROR, "Failed to pass basement test." );
}
}
@Override
public void processResults()
{
BasementRequest.checkBasement( false, this.responseText );
}
public static final String getBasementAction( final String text )
{
if ( text.indexOf( "Got Silk?" ) != -1 )
{
return KoLCharacter.isMoxieClass() ? "1" : "2";
}
if ( text.indexOf( "Save the Dolls" ) != -1 )
{
return KoLCharacter.isMysticalityClass() ? "1" : "2";
}
if ( text.indexOf( "Take the Red Pill" ) != -1 )
{
return KoLCharacter.isMuscleClass() ? "1" : "2";
}
return "1";
}
public static final int getBasementLevel()
{
return BasementRequest.basementLevel;
}
public static final String getBasementLevelName()
{
return "Fernswarthy's Basement (Level " + BasementRequest.basementLevel + ")";
}
public static final String getBasementLevelSummary()
{
switch ( BasementRequest.basementTest )
{
case NONE:
case MONSTER:
return "";
case REWARD:
return BasementRequest.basementTestString;
case ELEMENT:
{
BasementRequest.updateElementalResistanceParameters();
StringBuilder buffer = new StringBuilder( BasementRequest.basementTestString );
buffer.append( " Test: +" );
buffer.append( String.valueOf( BasementRequest.level1 ) );
buffer.append( " " );
buffer.append( BasementRequest.element1.toString() );
buffer.append( " " );
buffer.append( KoLConstants.COMMA_FORMAT.format( BasementRequest.resistance1 ) );
buffer.append( "%" );
if ( BasementRequest.vulnerability == 1 )
{
buffer.append( " (vulnerable) " );
}
buffer.append( " (" );
buffer.append( KoLConstants.COMMA_FORMAT.format( BasementRequest.expected1 ) );
buffer.append( " hp), +" );
buffer.append( String.valueOf( BasementRequest.level2 ) );
buffer.append( " " );
buffer.append( BasementRequest.element2.toString() );
buffer.append( " " );
buffer.append( KoLConstants.COMMA_FORMAT.format( BasementRequest.resistance2 ) );
buffer.append( "%" );
if ( BasementRequest.vulnerability == 2 )
{
buffer.append( " (vulnerable) " );
}
buffer.append( " (" );
buffer.append( KoLConstants.COMMA_FORMAT.format( BasementRequest.expected2 ) );
buffer.append( " hp)" );
return buffer.toString();
}
case HPDRAIN:
{
BasementRequest.updateHPDrainParameters();
StringBuilder buffer = new StringBuilder( BasementRequest.basementTestString );
buffer.append( " Test: " );
buffer.append( KoLConstants.COMMA_FORMAT.format( BasementRequest.basementTestCurrent ) );
buffer.append( " current, " );
buffer.append( BasementRequest.gauntletString );
buffer.append( " needed" );
return buffer.toString();
}
case MUSCLE:
BasementRequest.updateMuscleParameters();
break;
case MYSTICALITY:
BasementRequest.updateMysticalityParameters();
break;
case MOXIE:
BasementRequest.updateMoxieParameters();
break;
case MPDRAIN:
BasementRequest.updateMPDrainParameters();
break;
}
// Stat Test
StringBuilder buffer = new StringBuilder( BasementRequest.basementTestString );
buffer.append( " Test: " );
buffer.append( KoLConstants.COMMA_FORMAT.format( BasementRequest.basementTestCurrent ) );
buffer.append( " current, " );
buffer.append( KoLConstants.COMMA_FORMAT.format( BasementRequest.basementTestValue ) );
buffer.append( " needed" );
return buffer.toString();
}
public static final String getRequirement()
{
if ( BasementRequest.basementTestString.equals( "Elemental Resist" ) )
{
return "<u>" + BasementRequest.basementTestString + "</u><br/>Current: +" + BasementRequest.level1 + " " +
BasementRequest.element1.toString() + " " + KoLConstants.COMMA_FORMAT.format( BasementRequest.resistance1 ) + "%" +
( BasementRequest.vulnerability == 1 ? " (vulnerable) " : "" ) + " (" + KoLConstants.COMMA_FORMAT.format( BasementRequest.expected1 ) + " hp), +" +
BasementRequest.level2 + " " + BasementRequest.element2.toString() + " " + KoLConstants.COMMA_FORMAT.format( BasementRequest.resistance2 ) + "%" +
( BasementRequest.vulnerability == 2 ? " (vulnerable) " : "" ) + " (" + KoLConstants.COMMA_FORMAT.format( BasementRequest.expected2 ) + " hp)</br>" +
"Needed: " + KoLConstants.COMMA_FORMAT.format( BasementRequest.averageResistanceNeeded ) + "% average resistance or " + BasementRequest.goodeffect.getName();
}
if ( BasementRequest.basementTestString.startsWith( "Monster" ) )
{
int index = BasementRequest.basementTestString.indexOf( ": " );
if ( index == -1 )
{
return "";
}
return "<u>Monster</u><br/>" + BasementRequest.basementTestString.substring( index + 2 );
}
if ( BasementRequest.basementTestString.equals( "Maximum HP" ) )
{
return "<u>" + BasementRequest.basementTestString + "</u><br/>" + "Current: " + KoLConstants.COMMA_FORMAT.format( BasementRequest.basementTestCurrent ) + "<br/>" + "Needed: " + BasementRequest.gauntletString;
}
return "<u>" + BasementRequest.basementTestString + "</u><br/>" + "Current: " + KoLConstants.COMMA_FORMAT.format( BasementRequest.basementTestCurrent ) + "<br/>" + "Needed: " + KoLConstants.COMMA_FORMAT.format( BasementRequest.basementTestValue );
}
private static final void changeBasementOutfit( final String name )
{
Object currentTest;
String currentTestString;
// Find desired outfit. Skip "No Change" entry at index 0.
List available = EquipmentManager.getCustomOutfits();
int count = available.size();
for ( int i = 1; i < count; ++i )
{
currentTest = available.get( i );
currentTestString = currentTest.toString().toLowerCase();
if ( currentTestString.indexOf( name ) != -1 )
{
RequestThread.postRequest( new EquipmentRequest( (SpecialOutfit) currentTest ) );
// Restoring to the original outfit after Basement auto-adventuring is
// slow and pointless - you're going to want something related to the
// current outfit to continue, not the original one.
SpecialOutfit.forgetCheckpoints();
return;
}
}
}
private static final boolean checkForElementalTest( boolean autoSwitch, final String responseText )
{
if ( responseText.indexOf( "<b>Peace, Bra!</b>" ) != -1 )
{
BasementRequest.element1 = Element.STENCH;
BasementRequest.element2 = Element.SLEAZE;
BasementRequest.goodelement = BasementRequest.element2;
BasementRequest.goodphial = BasementRequest.SLEAZE_PHIAL;
BasementRequest.goodeffect = BasementRequest.SLEAZE_FORM;
// Stench is vulnerable to Sleaze
BasementRequest.badelement1 = Element.STENCH;
BasementRequest.badeffect1 = BasementRequest.STENCH_FORM;
// Spooky is vulnerable to Stench
BasementRequest.badelement2 = Element.SPOOKY;
BasementRequest.badeffect2 = BasementRequest.SPOOKY_FORM;
// Hot is vulnerable to Sleaze and Stench
BasementRequest.badelement3 = Element.HOT;
BasementRequest.badeffect3 = BasementRequest.HOT_FORM;
}
else if ( responseText.indexOf( "<b>Singled Out</b>" ) != -1 )
{
BasementRequest.element1 = Element.COLD;
BasementRequest.element2 = Element.SLEAZE;
BasementRequest.goodelement = BasementRequest.element1;
BasementRequest.goodphial = BasementRequest.COLD_PHIAL;
BasementRequest.goodeffect = BasementRequest.COLD_FORM;
// Sleaze is vulnerable to Cold
BasementRequest.badelement1 = Element.SLEAZE;
BasementRequest.badeffect1 = BasementRequest.SLEAZE_FORM;
// Stench is vulnerable to Cold
BasementRequest.badelement2 = Element.STENCH;
BasementRequest.badeffect2 = BasementRequest.STENCH_FORM;
// Hot is vulnerable to Sleaze
BasementRequest.badelement3 = Element.HOT;
BasementRequest.badeffect3 = BasementRequest.HOT_FORM;
}
else if ( responseText.indexOf( "<b>Still Better than Pistachio</b>" ) != -1 )
{
BasementRequest.element1 = Element.STENCH;
BasementRequest.element2 = Element.HOT;
BasementRequest.goodelement = BasementRequest.element1;
BasementRequest.goodphial = BasementRequest.STENCH_PHIAL;
BasementRequest.goodeffect = BasementRequest.STENCH_FORM;
// Cold is vulnerable to Hot
BasementRequest.badelement1 = Element.COLD;
BasementRequest.badeffect1 = BasementRequest.COLD_FORM;
// Spooky is vulnerable to Hot
BasementRequest.badelement2 = Element.SPOOKY;
BasementRequest.badeffect2 = BasementRequest.SPOOKY_FORM;
// Hot is vulnerable to Stench
BasementRequest.badelement3 = Element.HOT;
BasementRequest.badeffect3 = BasementRequest.HOT_FORM;
}
else if ( responseText.indexOf( "<b>Unholy Writes</b>" ) != -1 )
{
BasementRequest.element1 = Element.HOT;
BasementRequest.element2 = Element.SPOOKY;
BasementRequest.goodelement = BasementRequest.element1;
BasementRequest.goodphial = BasementRequest.HOT_PHIAL;
BasementRequest.goodeffect = BasementRequest.HOT_FORM;
// Cold is vulnerable to Spooky
BasementRequest.badelement1 = Element.COLD;
BasementRequest.badeffect1 = BasementRequest.COLD_FORM;
// Spooky is vulnerable to Hot
BasementRequest.badelement2 = Element.SPOOKY;
BasementRequest.badeffect2 = BasementRequest.SPOOKY_FORM;
// Sleaze is vulnerable to Spooky
BasementRequest.badelement3 = Element.SLEAZE;
BasementRequest.badeffect3 = BasementRequest.SLEAZE_FORM;
}
else if ( responseText.indexOf( "<b>The Unthawed</b>" ) != -1 )
{
BasementRequest.element1 = Element.COLD;
BasementRequest.element2 = Element.SPOOKY;
BasementRequest.goodelement = BasementRequest.element2;
BasementRequest.goodphial = BasementRequest.SPOOKY_PHIAL;
BasementRequest.goodeffect = BasementRequest.SPOOKY_FORM;
// Cold is vulnerable to Spooky
BasementRequest.badelement1 = Element.COLD;
BasementRequest.badeffect1 = BasementRequest.COLD_FORM;
// Stench is vulnerable to Cold
BasementRequest.badelement2 = Element.STENCH;
BasementRequest.badeffect2 = BasementRequest.STENCH_FORM;
// Sleaze is vulnerable to Cold
BasementRequest.badelement3 = Element.SLEAZE;
BasementRequest.badeffect3 = BasementRequest.SLEAZE_FORM;
}
else
{
// Not a known elemental test
return false;
}
BasementRequest.actualStatNeeded = Modifiers.HP;
BasementRequest.primaryBoost = Modifiers.MUS_PCT;
BasementRequest.secondaryBoost = Modifiers.MUS;
// Add the only beneficial elemental form for this test
boolean hasGoodEffect = KoLConstants.activeEffects.contains( BasementRequest.goodeffect );
if ( !hasGoodEffect )
{
BasementRequest.desirableEffects.add( BasementRequest.goodeffect );
}
BasementRequest.addDesiredEqualizer();
// Add effects that resist the specific elements being tested
// unless we have elemental immunity to that element.
if ( BasementRequest.element1 != BasementRequest.goodelement || !hasGoodEffect )
{
BasementRequest.addDesirableEffects( Modifiers.getPotentialChanges( Modifiers.elementalResistance( BasementRequest.element1 ) ) );
}
if ( BasementRequest.element2 != BasementRequest.goodelement || !hasGoodEffect )
{
BasementRequest.addDesirableEffects( Modifiers.getPotentialChanges( Modifiers.elementalResistance( BasementRequest.element2 ) ) );
}
// Add some effects that resist all elements
if ( !KoLConstants.activeEffects.contains( EffectPool.get( EffectPool.ASTRAL_SHELL ) ) )
{
BasementRequest.desirableEffects.add( EffectPool.get( EffectPool.ASTRAL_SHELL ) );
}
if ( !KoLConstants.activeEffects.contains( EffectPool.get( EffectPool.ELEMENTAL_SPHERE ) ) )
{
BasementRequest.desirableEffects.add( EffectPool.get( EffectPool.ELEMENTAL_SPHERE ) );
}
if ( !KoLConstants.activeEffects.contains( BasementRequest.BLACK_PAINT ) )
{
BasementRequest.desirableEffects.add( BasementRequest.BLACK_PAINT );
}
if ( BasementRequest.canHandleElementTest( autoSwitch, false ) )
{
return true;
}
if ( !autoSwitch )
{
return true;
}
BasementRequest.changeBasementOutfit( "element" );
BasementRequest.canHandleElementTest( autoSwitch, true );
return true;
}
private static final void updateElementalResistanceParameters()
{
BasementRequest.basementTestString = "Elemental Resist";
// According to http://forums.hardcoreoxygenation.com/viewtopic.php?t=3973,
// total elemental damage is roughly 4.48 * x^1.4. Assume the worst-case.
double damage1 =
( (double) Math.pow( BasementRequest.basementLevel, 1.4 ) * 4.48 + 8.0 ) * BasementRequest.SAFETY_MARGIN;
double damage2 = damage1;
BasementRequest.level1 = KoLCharacter.getElementalResistanceLevels( BasementRequest.element1 );
BasementRequest.resistance1 = KoLCharacter.elementalResistanceByLevel( BasementRequest.level1 );
BasementRequest.level2 = KoLCharacter.getElementalResistanceLevels( BasementRequest.element2 );
BasementRequest.resistance2 = KoLCharacter.elementalResistanceByLevel( BasementRequest.level2 );
if ( KoLConstants.activeEffects.contains( BasementRequest.goodeffect ) )
{
if ( BasementRequest.element1 == BasementRequest.goodelement )
{
BasementRequest.resistance1 = 100.0;
}
else
{
BasementRequest.resistance2 = 100.0;
}
}
BasementRequest.vulnerability = 0;
// If you have an elemental form which gives you vulnerability
// to an element, you retain your elemental resistance (as
// shown on the Character Sheet), but damage taken seems to be
// quadrupled.
if ( KoLConstants.activeEffects.contains( BasementRequest.badeffect1 ) || KoLConstants.activeEffects.contains( BasementRequest.badeffect2 ) || KoLConstants.activeEffects.contains( BasementRequest.badeffect3 ) )
{
if ( BasementRequest.element1 == BasementRequest.badelement1 || BasementRequest.element1 == BasementRequest.badelement2 || BasementRequest.element1 == BasementRequest.badelement3 )
{
BasementRequest.vulnerability = 1;
damage1 *= 4;
}
else
{
BasementRequest.vulnerability = 2;
damage2 *= 4;
}
}
BasementRequest.expected1 = Math.max( 1.0, damage1 * ( 100.0 - BasementRequest.resistance1 ) / 100.0 );
BasementRequest.expected2 = Math.max( 1.0, damage2 * ( 100.0 - BasementRequest.resistance2 ) / 100.0 );
BasementRequest.averageResistanceNeeded =
Math.max( 0, (int) Math.ceil( 100.0 * ( 1.0 - KoLCharacter.getMaximumHP() / ( damage1 + damage2 ) ) ) );
BasementRequest.basementTestValue = BasementRequest.expected1 + BasementRequest.expected2;
BasementRequest.basementTestCurrent = KoLCharacter.getMaximumHP();
}
private static final boolean canHandleElementTest( boolean autoSwitch, boolean switchedOutfits )
{
BasementRequest.updateElementalResistanceParameters();
// If you can survive the current elemental test even without a phial,
// then don't bother with any extra buffing.
if ( BasementRequest.expected1 + BasementRequest.expected2 < KoLCharacter.getCurrentHP() )
{
return true;
}
if ( BasementRequest.expected1 + BasementRequest.expected2 < KoLCharacter.getMaximumHP() )
{
if ( autoSwitch )
{
RecoveryManager.recoverHP( (int) ( BasementRequest.expected1 + BasementRequest.expected2 ) );
}
return KoLmafia.permitsContinue();
}
// If you already have the right phial effect, check to see if
// it's sufficient.
if ( KoLConstants.activeEffects.contains( BasementRequest.goodeffect ) )
{
return false;
}
// If you haven't switched outfits yet, it's possible that a simple
// outfit switch will be sufficient to buff up.
if ( !switchedOutfits )
{
return false;
}
// If you can't survive the test, even after an outfit switch, then
// automatically fail.
if ( BasementRequest.expected1 >= BasementRequest.expected2 )
{
if ( 1.0 + BasementRequest.expected2 >= KoLCharacter.getMaximumHP() )
{
BasementRequest.basementErrorMessage =
"You must have at least " + BasementRequest.basementTestValue + "% elemental resistance.";
return false;
}
}
else if ( 1.0 + BasementRequest.expected1 >= KoLCharacter.getMaximumHP() )
{
BasementRequest.basementErrorMessage =
"You must have at least " + BasementRequest.basementTestValue + "% elemental resistance.";
return false;
}
if ( !autoSwitch )
{
BasementRequest.basementErrorMessage =
"You must have at least " + BasementRequest.basementTestValue + "% elemental resistance.";
return false;
}
// You can survive, but you need an elemental phial in order to
// do so. Go ahead and use one, which will automatically
// uneffect any competing phials, first
RequestThread.postRequest( UseItemRequest.getInstance( BasementRequest.goodphial ) );
double damage =
BasementRequest.expected1 >= BasementRequest.expected2 ? BasementRequest.expected2 : BasementRequest.expected1;
RecoveryManager.recoverHP( (int) ( 1.0 + damage ) );
return KoLmafia.permitsContinue();
}
private static final AdventureResult getDesiredEqualizer()
{
if ( KoLCharacter.getBaseMuscle() >= KoLCharacter.getBaseMysticality() && KoLCharacter.getBaseMuscle() >= KoLCharacter.getBaseMoxie() )
{
return BasementRequest.MUS_EQUAL;
}
if ( KoLCharacter.getBaseMysticality() >= KoLCharacter.getBaseMuscle() && KoLCharacter.getBaseMysticality() >= KoLCharacter.getBaseMoxie() )
{
return BasementRequest.MYS_EQUAL;
}
return BasementRequest.MOX_EQUAL;
}
private static final void addDesiredEqualizer()
{
AdventureResult equalizer = BasementRequest.getDesiredEqualizer();
if ( !KoLConstants.activeEffects.contains( equalizer ) )
{
BasementRequest.desirableEffects.add( equalizer );
}
}
private static final double updateMuscleParameters()
{
// According to http://forums.hardcoreoxygenation.com/viewtopic.php?t=3973,
// stat requirement is x^1.4 + 2. Assume the worst-case.
double statRequirement =
( (double) Math.pow( BasementRequest.basementLevel, 1.4 ) + 2.0 ) * BasementRequest.SAFETY_MARGIN;
BasementRequest.basementTestString = "Buffed Muscle";
BasementRequest.basementTestCurrent = KoLCharacter.getAdjustedMuscle();
BasementRequest.basementTestValue = (int) statRequirement;
BasementRequest.actualStatNeeded = Modifiers.MUS;
BasementRequest.primaryBoost = Modifiers.MUS_PCT;
BasementRequest.secondaryBoost = Modifiers.MUS;
return statRequirement;
}
private static final double updateMysticalityParameters()
{
// According to http://forums.hardcoreoxygenation.com/viewtopic.php?t=3973,
// stat requirement is x^1.4 + 2. Assume the worst-case.
double statRequirement =
( (double) Math.pow( BasementRequest.basementLevel, 1.4 ) + 2.0 ) * BasementRequest.SAFETY_MARGIN;
BasementRequest.basementTestString = "Buffed Mysticality";
BasementRequest.basementTestCurrent = KoLCharacter.getAdjustedMysticality();
BasementRequest.basementTestValue = (int) statRequirement;
BasementRequest.actualStatNeeded = Modifiers.MYS;
BasementRequest.primaryBoost = Modifiers.MYS_PCT;
BasementRequest.secondaryBoost = Modifiers.MYS;
return statRequirement;
}
private static final double updateMoxieParameters()
{
// According to http://forums.hardcoreoxygenation.com/viewtopic.php?t=3973,
// stat requirement is x^1.4 + 2. Assume the worst-case.
double statRequirement =
( (double) Math.pow( BasementRequest.basementLevel, 1.4 ) + 2.0 ) * BasementRequest.SAFETY_MARGIN;
BasementRequest.basementTestString = "Buffed Moxie";
BasementRequest.basementTestCurrent = KoLCharacter.getAdjustedMoxie();
BasementRequest.basementTestValue = (int) statRequirement;
BasementRequest.actualStatNeeded = Modifiers.MOX;
BasementRequest.primaryBoost = Modifiers.MOX_PCT;
BasementRequest.secondaryBoost = Modifiers.MOX;
return statRequirement;
}
private static final boolean checkForStatTest( final boolean autoSwitch, final String responseText )
{
if ( responseText.indexOf( "Lift 'em" ) != -1 || responseText.indexOf( "Push it Real Good" ) != -1 || responseText.indexOf( "Ring that Bell" ) != -1 )
{
double statRequirement = BasementRequest.updateMuscleParameters();
BasementRequest.basementTest = TestType.MUSCLE;
BasementRequest.addDesiredEqualizer();
if ( KoLCharacter.getAdjustedMuscle() < statRequirement )
{
if ( autoSwitch )
{
BasementRequest.changeBasementOutfit( "muscle" );
if ( KoLCharacter.getAdjustedMuscle() < statRequirement )
{
KoLmafiaCLI.DEFAULT_SHELL.executeCommand( "maximize", "mus " + statRequirement + " min");
}
}
if ( KoLCharacter.getAdjustedMuscle() < statRequirement )
{
BasementRequest.basementErrorMessage =
"You must have at least " + BasementRequest.basementTestValue + " muscle.";
}
}
return true;
}
if ( responseText.indexOf( "Gathering: The Magic" ) != -1 || responseText.indexOf( "Mop the Floor" ) != -1 || responseText.indexOf( "'doo" ) != -1 )
{
double statRequirement = BasementRequest.updateMysticalityParameters();
BasementRequest.basementTest = TestType.MYSTICALITY;
BasementRequest.addDesiredEqualizer();
if ( KoLCharacter.getAdjustedMysticality() < statRequirement )
{
if ( autoSwitch )
{
BasementRequest.changeBasementOutfit( "mysticality" );
if ( KoLCharacter.getAdjustedMysticality() < statRequirement )
{
KoLmafiaCLI.DEFAULT_SHELL.executeCommand( "maximize", "mys " + statRequirement + " min");
}
}
if ( KoLCharacter.getAdjustedMysticality() < statRequirement )
{
BasementRequest.basementErrorMessage =
"You must have at least " + BasementRequest.basementTestValue + " mysticality.";
}
}
return true;
}
if ( responseText.indexOf( "Don't Wake the Baby" ) != -1 || responseText.indexOf( "Grab a cue" ) != -1 || responseText.indexOf( "Smooth Moves" ) != -1 )
{
double statRequirement = BasementRequest.updateMoxieParameters();
BasementRequest.basementTest = TestType.MOXIE;
BasementRequest.addDesiredEqualizer();
if ( KoLCharacter.getAdjustedMoxie() < statRequirement )
{
if ( autoSwitch )
{
BasementRequest.changeBasementOutfit( "moxie" );
if ( KoLCharacter.getAdjustedMoxie() < statRequirement )
{
KoLmafiaCLI.DEFAULT_SHELL.executeCommand( "maximize", "mox " + statRequirement + " min");
}
}
if ( KoLCharacter.getAdjustedMoxie() < statRequirement )
{
BasementRequest.basementErrorMessage =
"You must have at least " + BasementRequest.basementTestValue + " moxie.";
}
}
return true;
}
return false;
}
private static final double updateMPDrainParameters()
{
// According to
// http://forums.hardcoreoxygenation.com/viewtopic.php?t=3973,
// drain requirement is 1.67 * x^1.4 Assume worst-case.
double drainRequirement =
(double) Math.pow( BasementRequest.basementLevel, 1.4 ) * 1.67 * BasementRequest.SAFETY_MARGIN;
BasementRequest.basementTestString = "Maximum MP";
BasementRequest.basementTestCurrent = KoLCharacter.getMaximumMP();
BasementRequest.basementTestValue = (int) drainRequirement;
BasementRequest.actualStatNeeded = Modifiers.MP;
if ( StatBooster.moxieControlsMP() )
{
BasementRequest.primaryBoost = Modifiers.MOX_PCT;
BasementRequest.secondaryBoost = Modifiers.MOX;
}
else
{
BasementRequest.primaryBoost = Modifiers.MYS_PCT;
BasementRequest.secondaryBoost = Modifiers.MYS;
}
return drainRequirement;
}
private static final double updateHPDrainParameters()
{
// According to starwed at
// http://forums.kingdomofloathing.com/viewtopic.php?t=83342&start=201
// drain requirement is 10.0 * x^1.4. Assume worst-case.
double drainRequirement =
(double) Math.pow( BasementRequest.basementLevel, 1.4 ) * 10.0 * BasementRequest.SAFETY_MARGIN;
BasementRequest.basementTestString = "Maximum HP";
BasementRequest.basementTestCurrent = KoLCharacter.getMaximumHP();
BasementRequest.actualStatNeeded = Modifiers.HP;
BasementRequest.primaryBoost = Modifiers.MUS_PCT;
BasementRequest.secondaryBoost = Modifiers.MUS;
double damageAbsorb =
1.0 - ( (double) Math.sqrt( Math.min( 1000, KoLCharacter.getDamageAbsorption() ) / 10.0 ) - 1.0 ) / 10.0;
double healthRequirement = drainRequirement * damageAbsorb;
BasementRequest.basementTestValue = (int) healthRequirement;
BasementRequest.gauntletString =
(int) drainRequirement + " * " + KoLConstants.FLOAT_FORMAT.format( damageAbsorb ) + " (" + KoLCharacter.getDamageAbsorption() + " DA) = " + KoLConstants.COMMA_FORMAT.format( healthRequirement );
return drainRequirement;
}
private static final boolean checkForDrainTest( final boolean autoSwitch, final String responseText )
{
if ( responseText.indexOf( "Grab the Handles" ) != -1 )
{
double drainRequirement = BasementRequest.updateMPDrainParameters();
BasementRequest.basementTest = TestType.MPDRAIN;
BasementRequest.addDesiredEqualizer();
if ( KoLCharacter.getMaximumMP() < drainRequirement )
{
if ( autoSwitch )
{
BasementRequest.changeBasementOutfit( "mpdrain" );
if ( KoLCharacter.getMaximumMP() < drainRequirement )
{
KoLmafiaCLI.DEFAULT_SHELL.executeCommand( "maximize",
"MP " + drainRequirement + " min");
}
}
if ( KoLCharacter.getMaximumMP() < drainRequirement )
{
BasementRequest.basementErrorMessage = "Insufficient mana to continue.";
return true;
}
}
if ( autoSwitch )
{
RecoveryManager.recoverMP( (int) drainRequirement );
}
return true;
}
if ( responseText.indexOf( "Run the Gauntlet Gauntlet" ) != -1 )
{
double drainRequirement = BasementRequest.updateHPDrainParameters();
BasementRequest.basementTest = TestType.HPDRAIN;
BasementRequest.addDesiredEqualizer();
// Add some effects that improve Damage Absorption
if ( !KoLConstants.activeEffects.contains( EffectPool.get( EffectPool.ASTRAL_SHELL ) ) )
{
BasementRequest.desirableEffects.add( EffectPool.get( EffectPool.ASTRAL_SHELL ) );
}
if ( !KoLConstants.activeEffects.contains( EffectPool.get( EffectPool.GHOSTLY_SHELL ) ) )
{
BasementRequest.desirableEffects.add( EffectPool.get( EffectPool.GHOSTLY_SHELL ) );
}
double damageAbsorb =
1.0 - ( (double) Math.sqrt( Math.min( 1000, KoLCharacter.getDamageAbsorption() ) / 10.0 ) - 1.0 ) / 10.0;
double healthRequirement = drainRequirement * damageAbsorb;
if ( KoLCharacter.getMaximumHP() < healthRequirement )
{
if ( autoSwitch )
{
BasementRequest.changeBasementOutfit( "gauntlet" );
damageAbsorb =
1.0 - ( (double) Math.sqrt( Math.min( 1000, KoLCharacter.getDamageAbsorption() ) / 10.0 ) - 1.0 ) / 10.0;
healthRequirement = drainRequirement * damageAbsorb;
BasementRequest.basementTestValue = (int) healthRequirement;
}
if ( KoLCharacter.getMaximumHP() < healthRequirement )
{
BasementRequest.basementErrorMessage = "Insufficient health to continue.";
return true;
}
}
if ( autoSwitch )
{
RecoveryManager.recoverHP( (int) healthRequirement );
}
return true;
}
return false;
}
private static final boolean checkForReward( final String responseText )
{
if ( responseText.indexOf( "De Los Dioses" ) != -1 )
{
BasementRequest.basementTestString = "Encounter: De Los Dioses";
return true;
}
if ( responseText.indexOf( "The Dusk Zone" ) != -1 )
{
BasementRequest.basementTestString = "Encounter: The Dusk Zone";
return true;
}
if ( responseText.indexOf( "Giggity Bobbity Boo!" ) != -1 )
{
BasementRequest.basementTestString = "Encounter: Giggity Bobbity Boo!";
return true;
}
if ( responseText.indexOf( "No Good Deed" ) != -1 )
{
BasementRequest.basementTestString = "Encounter: No Good Deed";
return true;
}
if ( responseText.indexOf( "<b>Fernswarthy's Basement, Level 500</b>" ) != -1 )
{
BasementRequest.basementTestString = "Encounter: Fernswarthy's Basement, Level 500";
return true;
}
if ( responseText.indexOf( "Got Silk?" ) != -1 )
{
BasementRequest.basementTestString = "Encounter: Got Silk?/Leather is Betther";
return true;
}
if ( responseText.indexOf( "Save the Dolls" ) != -1 )
{
BasementRequest.basementTestString = "Encounter: Save the Dolls/Save the Cardboard";
return true;
}
if ( responseText.indexOf( "Take the Red Pill" ) != -1 )
{
BasementRequest.basementTestString = "Encounter: Take the Red Pill/Take the Blue Pill";
return true;
}
return false;
}
private static final String monsterLevelString()
{
double level =
2.0 * (double) Math.pow( BasementRequest.basementLevel, 1.4 ) + KoLCharacter.getMonsterLevelAdjustment();
return "Monster: Attack/Defense = " + (int) level;
}
private static final boolean checkForMonster( final String responseText )
{
if ( responseText.indexOf( "Don't Fear the Ear" ) != -1 )
{
// Beast with X Ears
BasementRequest.basementMonster = "Beast with X Ears";
BasementRequest.basementTestString = BasementRequest.monsterLevelString();
return true;
}
if ( responseText.indexOf( "Commence to Pokin" ) != -1 )
{
// Beast with X Eyes
BasementRequest.basementMonster = "Beast with X Eyes";
BasementRequest.basementTestString = BasementRequest.monsterLevelString();
return true;
}
if ( responseText.indexOf( "Stone Golem" ) != -1 )
{
// X Stone Golem
BasementRequest.basementMonster = "X Stone Golem";
BasementRequest.basementTestString = BasementRequest.monsterLevelString();
return true;
}
if ( responseText.indexOf( "Hydra" ) != -1 )
{
// X-headed Hydra
BasementRequest.basementMonster = "X-headed Hydra";
BasementRequest.basementTestString = BasementRequest.monsterLevelString();
return true;
}
if ( responseText.indexOf( "Toast that Ghost" ) != -1 )
{
// Ghost of Fernswarthy's Grandfather
BasementRequest.basementMonster = "Ghost of Fernswarthy's Grandfather";
BasementRequest.basementTestString = BasementRequest.monsterLevelString() + "<br>Physically resistant";
return true;
}
if ( responseText.indexOf( "Bottles of Beer on a Golem" ) != -1 )
{
// X Bottles of Beer on a Golem
BasementRequest.basementMonster = "X Bottles of Beer on a Golem";
BasementRequest.basementTestString = BasementRequest.monsterLevelString() + "<br>Blocks most spells";
return true;
}
if ( responseText.indexOf( "Collapse That Waveform" ) != -1 )
{
// X-dimensional Horror
BasementRequest.basementMonster = "X-dimensional Horror";
BasementRequest.basementTestString = BasementRequest.monsterLevelString() + "<br>Blocks physical attacks";
return true;
}
return false;
}
private static final void newBasementLevel( final String responseText )
{
BasementRequest.basementErrorMessage = null;
BasementRequest.basementTestString = "None";
BasementRequest.basementTestValue = 0;
BasementRequest.element1 = Element.NONE;
BasementRequest.element2 = Element.NONE;
BasementRequest.vulnerability = 0;
BasementRequest.goodelement = Element.NONE;
BasementRequest.goodphial = null;
BasementRequest.goodeffect = null;
BasementRequest.badeffect1 = null;
BasementRequest.badeffect2 = null;
BasementRequest.badeffect3 = null;
BasementRequest.badelement1 = Element.NONE;
BasementRequest.badelement2 = Element.NONE;
BasementRequest.badelement3 = Element.NONE;
Matcher levelMatcher = BasementRequest.BASEMENT_PATTERN.matcher( responseText );
if ( !levelMatcher.find() )
{
return;
}
BasementRequest.basementLevel = StringUtilities.parseInt( levelMatcher.group( 1 ) );
KoLAdventure.lastLocationName = BasementRequest.getBasementLevelName();
}
public static final void checkBasement()
{
BasementRequest.checkBasement( true, BasementRequest.lastResponseText );
}
public static final void checkBasement( final String responseText )
{
BasementRequest.checkBasement( false, responseText );
}
public static final void checkBasement( final boolean autoSwitch, final String responseText )
{
BasementRequest.lastResponseText = responseText;
BasementRequest.desirableEffects.clear();
BasementRequest.newBasementLevel( responseText );
if ( BasementRequest.checkForReward( responseText ) )
{
BasementRequest.basementTest = TestType.REWARD;
return;
}
if ( BasementRequest.checkForElementalTest( autoSwitch, responseText ) )
{
BasementRequest.basementTest = TestType.ELEMENT;
return;
}
if ( BasementRequest.checkForStatTest( autoSwitch, responseText ) )
{
return;
}
if ( BasementRequest.checkForDrainTest( autoSwitch, responseText ) )
{
return;
}
if ( !BasementRequest.checkForMonster( responseText ) )
{
return;
}
BasementRequest.basementTest = TestType.MONSTER;
BasementRequest.basementTestCurrent = 0;
BasementRequest.basementTestValue = 0;
BasementRequest.actualStatNeeded = Modifiers.HP;
BasementRequest.primaryBoost = Modifiers.MUS_PCT;
BasementRequest.secondaryBoost = Modifiers.MUS;
BasementRequest.addDesiredEqualizer();
if ( autoSwitch )
{
BasementRequest.changeBasementOutfit( "damage" );
}
}
private static final void getStatBoosters( final ArrayList<AdventureResult> sourceList, final ArrayList<StatBooster> targetList )
{
// Cache skills to avoid lots of string lookups
StatBooster.checkSkills();
Iterator<AdventureResult> it = sourceList.iterator();
while ( it.hasNext() )
{
AdventureResult effect = it.next();
if ( !BasementRequest.wantEffect( effect ) )
{
continue;
}
StatBooster addition = new StatBooster( effect.getName() );
if ( !targetList.contains( addition ) )
{
targetList.add( addition );
}
}
}
private static final void addDesirableEffects( final ArrayList<AdventureResult> sourceList )
{
Iterator<AdventureResult> it = sourceList.iterator();
while ( it.hasNext() )
{
AdventureResult effect = it.next();
if ( BasementRequest.wantEffect( effect ) && !BasementRequest.desirableEffects.contains( effect ) )
{
BasementRequest.desirableEffects.add( effect );
}
}
}
private static final boolean wantEffect( final AdventureResult effect )
{
String action = MoodManager.getDefaultAction( "lose_effect", effect.getName() );
if ( action.equals( "" ) )
{
return false;
}
if ( action.startsWith( "cast" ) )
{
if ( !KoLCharacter.hasSkill( UneffectRequest.effectToSkill( effect.getName() ) ) )
{
return false;
}
}
return true;
}
public static final ArrayList<StatBooster> getStatBoosters()
{
ArrayList<StatBooster> targetList = new ArrayList<StatBooster>();
BasementRequest.getStatBoosters( BasementRequest.desirableEffects, targetList );
BasementRequest.getStatBoosters( Modifiers.getPotentialChanges( BasementRequest.primaryBoost ), targetList );
BasementRequest.getStatBoosters( Modifiers.getPotentialChanges( BasementRequest.secondaryBoost ), targetList );
if ( BasementRequest.actualStatNeeded == Modifiers.HP )
{
BasementRequest.getStatBoosters( Modifiers.getPotentialChanges( Modifiers.HP_PCT ), targetList );
BasementRequest.getStatBoosters( Modifiers.getPotentialChanges( Modifiers.HP ), targetList );
}
else if ( BasementRequest.actualStatNeeded == Modifiers.MP )
{
BasementRequest.getStatBoosters( Modifiers.getPotentialChanges( Modifiers.MP_PCT ), targetList );
BasementRequest.getStatBoosters( Modifiers.getPotentialChanges( Modifiers.MP ), targetList );
}
Collections.sort( targetList );
return targetList;
}
public static int getBasementTestCurrent()
{
return (int) BasementRequest.basementTestCurrent;
}
public static int getBasementTestValue()
{
return (int) BasementRequest.basementTestValue;
}
public static int getActualStatNeeded()
{
return BasementRequest.actualStatNeeded;
}
public static int getPrimaryBoost()
{
return BasementRequest.primaryBoost;
}
public static int getSecondaryBoost()
{
return BasementRequest.secondaryBoost;
}
}