/** * 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.Arrays; import java.util.Calendar; import java.util.HashMap; import java.util.List; import java.util.TimeZone; 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.Modifiers; import net.sourceforge.kolmafia.RequestLogger; import net.sourceforge.kolmafia.RequestThread; import net.sourceforge.kolmafia.SpecialOutfit; import net.sourceforge.kolmafia.moods.HPRestoreItemList; import net.sourceforge.kolmafia.moods.MPRestoreItemList; import net.sourceforge.kolmafia.moods.ManaBurnManager; 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.IntegerPool; import net.sourceforge.kolmafia.objectpool.ItemPool; import net.sourceforge.kolmafia.objectpool.OutfitPool; import net.sourceforge.kolmafia.persistence.AdventureDatabase; import net.sourceforge.kolmafia.persistence.ConcoctionDatabase; import net.sourceforge.kolmafia.persistence.ConsumablesDatabase; import net.sourceforge.kolmafia.persistence.EffectDatabase; import net.sourceforge.kolmafia.persistence.EquipmentDatabase; import net.sourceforge.kolmafia.persistence.FamiliarDatabase; import net.sourceforge.kolmafia.persistence.ItemDatabase; 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.session.BugbearManager; import net.sourceforge.kolmafia.session.ChoiceManager; import net.sourceforge.kolmafia.session.ClanManager; import net.sourceforge.kolmafia.session.DreadScrollManager; import net.sourceforge.kolmafia.session.EquipmentManager; import net.sourceforge.kolmafia.session.InventoryManager; import net.sourceforge.kolmafia.session.Limitmode; import net.sourceforge.kolmafia.session.QuestManager; import net.sourceforge.kolmafia.session.ResponseTextParser; import net.sourceforge.kolmafia.session.ResultProcessor; import net.sourceforge.kolmafia.session.TurnCounter; import net.sourceforge.kolmafia.swingui.GenericFrame; import net.sourceforge.kolmafia.textui.command.ZapCommand; import net.sourceforge.kolmafia.utilities.InputFieldUtilities; import net.sourceforge.kolmafia.utilities.StringUtilities; public class UseItemRequest extends GenericRequest { public static final AdventureResult INFERNAL_SEAL_CLAW = ItemPool.get( ItemPool.INFERNAL_SEAL_CLAW, 1 ); public static final Pattern DOWHICHITEM_PATTERN = Pattern.compile( "dowhichitem=(\\d+)" ); private static final Pattern ABSORB_PATTERN = Pattern.compile( "absorb=(\\d+)" ); private static final Pattern ROW_PATTERN = Pattern.compile( "<tr>.*?</tr>" ); private static final Pattern INVENTORY_PATTERN = Pattern.compile( "</blockquote></td></tr></table>.*?</body>" ); private static final Pattern HELPER_PATTERN = Pattern.compile( "(utensil|whichcard)=(\\d+)" ); private static final Pattern BRICKO_PATTERN = Pattern.compile( "You break apart your ([\\w\\s]*)." ); private static final Pattern TERMINAL_CHIP_PATTERN = Pattern.compile( "You have (\\d+) so far" ); private static final Pattern FAMILIAR_NAME_PATTERN = Pattern.compile( "You decide to name (?:.*?) <b>(.*?)</b>" ); private static final Pattern FRUIT_TUBING_PATTERN = Pattern.compile( "(?=.*?action=addfruit).*whichfruit=(\\d+)" ); private static final Pattern ADVENTUROUS_RESOLUTION_PATTERN = Pattern.compile( "resolve to do it again" ); private static final Pattern MERKIN_WORDQUIZ_PATTERN = Pattern.compile( "Your Mer-kin vocabulary mastery is now at <b>(\\d*?)%</b>" ); private static final Pattern PURPLE_WORD_PATTERN = Pattern.compile( "don't forget <font color=purple><b><i>(.*?)</i></b></font>" ); // It goes [Xd12] feet, and doesn't hit anything interesting. private static final Pattern ARROW_PATTERN = Pattern.compile( "It goes (\\d+) feet" ); // You pull out the trivia card (#57/1200) and read it. private static final Pattern CARD_PATTERN = Pattern.compile( "You pull out the trivia card \\(#(\\d+)/(\\d+)\\) and read it." ); // <p>Question: Who wrote the banned detective novel <u>Quest of the Witch Mints</u>?<p>Answer: Mssr. Johnny Simon. private static final Pattern QA_PATTERN = Pattern.compile( "(Question|Answer): *(.*?[\\.\\?])<" ); // <center>Total evil: <b>200</b><p>Alcove: <b>50</b><br>Cranny: <b>50</b><br>Niche: <b>50</b><br>Nook: <b>50</b></center> private static final Pattern EVILOMETER_PATTERN = Pattern.compile( "<center>Total evil: <b>(\\d+)</b><p>Alcove: <b>(\\d+)</b><br>Cranny: <b>(\\d+)</b><br>Niche: <b>(\\d+)</b><br>Nook: <b>(\\d+)</b></center>" ); private static final Pattern KEYOTRON_PATTERN = Pattern.compile( "Medbay:</td><td><b>(\\d)/3</b> bio-data segments collected</td></tr>" + "<tr><td align=right>Waste Processing:</td><td><b>(\\d)/3</b> bio-data segments collected</td></tr>" + "<tr><td align=right>Sonar:</td><td><b>(\\d)/3</b> bio-data segments collected</td></tr>" + "<tr><td align=right>Science Lab:</td><td><b>(\\d)/6</b> bio-data segments collected</td></tr>" + "<tr><td align=right>Morgue:</td><td><b>(\\d)/6</b> bio-data segments collected</td></tr>" + "<tr><td align=right>Special Ops:</td><td><b>(\\d)/6</b> bio-data segments collected</td></tr>" + "<tr><td align=right>Engineering:</td><td><b>(\\d)/9</b> bio-data segments collected</td></tr>" + "<tr><td align=right>Navigation:</td><td><b>(\\d)/9</b> bio-data segments collected</td></tr>" + "<tr><td align=right>Galley:</td><td><b>(\\d)/9</b> bio-data segments collected" ); private static final HashMap<Integer,AdventureResult> LIMITED_USES = new HashMap<Integer,AdventureResult>(); static { UseItemRequest.LIMITED_USES.put( IntegerPool.get( ItemPool.ASTRAL_MUSHROOM ), EffectPool.get( EffectPool.HALF_ASTRAL ) ); UseItemRequest.LIMITED_USES.put( IntegerPool.get( ItemPool.ABSINTHE ), EffectPool.get( EffectPool.ABSINTHE ) ); } public static String lastUpdate = ""; public static String limiter = ""; private static AdventureResult lastFruit = null; private static AdventureResult lastUntinker = null; private static boolean retrying = false; protected final int consumptionType; protected AdventureResult itemUsed = null; protected static AdventureResult lastItemUsed = null; protected static AdventureResult lastHelperUsed = null; private static int currentItemId = -1; private static int askedAboutPvP = 0; public static final UseItemRequest getInstance( final int itemId ) { return UseItemRequest.getInstance( itemId, 1 ); } public static final UseItemRequest getInstance( final int itemId, int itemCount ) { return UseItemRequest.getInstance( ItemPool.get( itemId, itemCount ) ); } public static final UseItemRequest getInstance( final AdventureResult item ) { return UseItemRequest.getInstance( UseItemRequest.getConsumptionType( item ), item ); } public static final UseItemRequest getInstance( final int consumptionType, final AdventureResult item ) { switch ( consumptionType ) { case KoLConstants.CONSUME_DRINK: case KoLConstants.CONSUME_DRINK_HELPER: return new DrinkItemRequest( item ); case KoLConstants.CONSUME_EAT: case KoLConstants.CONSUME_FOOD_HELPER: return new EatItemRequest( item ); case KoLConstants.CONSUME_SPLEEN: return new SpleenItemRequest( item ); } return new UseItemRequest( consumptionType, item ); } protected UseItemRequest( final int consumptionType, final AdventureResult item ) { this( UseItemRequest.getConsumptionLocation( consumptionType, item ), consumptionType, item ); } private UseItemRequest( final String location, final int consumptionType, final AdventureResult item ) { super( location ); this.consumptionType = consumptionType; this.itemUsed = item; this.addFormField( "whichitem", String.valueOf( item.getItemId() ) ); } public static final int getConsumptionType( final AdventureResult item ) { int itemId = item.getItemId(); // We want to display the result of using certain items switch ( itemId ) { case ItemPool.HOBO_CODE_BINDER: case ItemPool.STUFFED_BARON: return KoLConstants.MESSAGE_DISPLAY; } int consumptionType = ItemDatabase.getConsumptionType( itemId ); // Spleen items can be marked "usable", if you can only use one // at a time, but must use a SpleenItemRequest if ( consumptionType == KoLConstants.CONSUME_SPLEEN ) { return KoLConstants.CONSUME_SPLEEN; } int attrs = ItemDatabase.getAttributes( itemId ); if ( (attrs & ItemDatabase.ATTR_USABLE) != 0 ) { return KoLConstants.CONSUME_USE; } if ( (attrs & ItemDatabase.ATTR_MULTIPLE) != 0 ) { return KoLConstants.CONSUME_MULTIPLE; } if ( (attrs & ItemDatabase.ATTR_REUSABLE) != 0 ) { return KoLConstants.INFINITE_USES; } return consumptionType; } private static final String getConsumptionLocation( final int consumptionType, final AdventureResult item ) { switch ( consumptionType ) { case KoLConstants.CONSUME_EAT: return "inv_eat.php"; case KoLConstants.CONSUME_DRINK: return "inv_booze.php"; case KoLConstants.CONSUME_SPLEEN: return "inv_spleen.php"; case KoLConstants.GROW_FAMILIAR: return "inv_familiar.php"; case KoLConstants.CONSUME_HOBO: case KoLConstants.CONSUME_GHOST: case KoLConstants.CONSUME_SLIME: return "familiarbinger.php"; case KoLConstants.CONSUME_ROBO: return "inventory.php"; case KoLConstants.CONSUME_SPHERE: return "campground.php"; case KoLConstants.CONSUME_MULTIPLE: if ( item.getCount() > 1 ) { return "multiuse.php"; } return "inv_use.php"; case KoLConstants.INFINITE_USES: { int type = ItemDatabase.getConsumptionType( item.getItemId() ); return type == KoLConstants.CONSUME_MULTIPLE ? "multiuse.php" : "inv_use.php"; } default: return "inv_use.php"; } } public static void setLastItemUsed( final AdventureResult item ) { UseItemRequest.lastItemUsed = item; UseItemRequest.currentItemId = item.getItemId(); } public static final int currentItemId() { return UseItemRequest.currentItemId; } private final boolean isBingeRequest() { switch ( this.consumptionType ) { case KoLConstants.CONSUME_HOBO: case KoLConstants.CONSUME_GHOST: case KoLConstants.CONSUME_SLIME: case KoLConstants.CONSUME_MIMIC: return true; } return false; } private static boolean needsConfirmation( final AdventureResult item ) { switch ( item.getItemId() ) { case ItemPool.NEWBIESPORT_TENT: case ItemPool.BARSKIN_TENT: case ItemPool.COTTAGE: case ItemPool.BRICKO_PYRAMID: case ItemPool.HOUSE: case ItemPool.SANDCASTLE: case ItemPool.GINORMOUS_PUMPKIN: case ItemPool.TWIG_HOUSE: case ItemPool.GINGERBREAD_HOUSE: case ItemPool.HOBO_FORTRESS: case ItemPool.GIANT_FARADAY_CAGE: case ItemPool.SNOW_FORT: case ItemPool.ELEVENT: case ItemPool.RESIDENCE_CUBE: return CampgroundRequest.getCurrentDwelling() != CampgroundRequest.BIG_ROCK; case ItemPool.HOT_BEDDING: case ItemPool.COLD_BEDDING: case ItemPool.STENCH_BEDDING: case ItemPool.SPOOKY_BEDDING: case ItemPool.SLEAZE_BEDDING: case ItemPool.BEANBAG_CHAIR: case ItemPool.GAUZE_HAMMOCK: case ItemPool.SALTWATERBED: case ItemPool.SPIRIT_BED: return CampgroundRequest.getCurrentBed() != null; } return false; } public static boolean askAboutPvP( final String itemName ) { // If we've already asked about PvP, don't nag. if ( UseItemRequest.askedAboutPvP == KoLCharacter.getUserId() ) { return true; } int PvPGain = ConsumablesDatabase.getPvPFights( itemName ); // Does this item even give us PvP fights? if ( PvPGain <= 0 ) { return true; } // Is the hippy stone broken? if ( KoLCharacter.getHippyStoneBroken() ) { return true; } String message = "Are you sure you want consume that before breaking the hippy stone?"; if ( !InputFieldUtilities.confirm( message ) ) { return false; } UseItemRequest.askedAboutPvP = KoLCharacter.getUserId(); return true; } public int getConsumptionType() { return this.consumptionType; } public AdventureResult getItemUsed() { return this.itemUsed; } public static final int maximumUses( final int itemId ) { String itemName = ItemDatabase.getItemName( itemId ); return UseItemRequest.maximumUses( itemId, itemName, KoLConstants.NO_CONSUME, true ); } public static final int maximumUses( final int itemId, final int consumptionType ) { String itemName = ItemDatabase.getItemName( itemId ); return UseItemRequest.maximumUses( itemId, itemName, consumptionType, true ); } public static final int maximumUses( final String itemName ) { int itemId = ItemDatabase.getItemId( itemName ); return UseItemRequest.maximumUses( itemId, itemName, KoLConstants.NO_CONSUME, false ); } private static final int maximumUses( final int itemId, final String itemName, final int consumptionType, final boolean allowOverDrink ) { if ( FightRequest.inMultiFight ) { UseItemRequest.limiter = "multi-stage fight in progress"; return 0; } if ( FightRequest.choiceFollowsFight ) { UseItemRequest.limiter = "choice adventure follows the fight"; return 0; } if ( ChoiceManager.handlingChoice && !ChoiceManager.canWalkAway() ) { UseItemRequest.limiter = "choice adventure in progress"; return 0; } // Beecore path check switch ( itemId ) { case ItemPool.BALL_POLISH: case ItemPool.FRATHOUSE_BLUEPRINTS: case ItemPool.BINDER_CLIP: // These "B" items ARE usable in Beecore. case ItemPool.ICE_BABY: case ItemPool.JUGGLERS_BALLS: case ItemPool.EYEBALL_PENDANT: case ItemPool.SPOOKY_PUTTY_BALL: case ItemPool.LOATHING_LEGION_ABACUS: case ItemPool.LOATHING_LEGION_DEFIBRILLATOR: case ItemPool.LOATHING_LEGION_DOUBLE_PRISM: case ItemPool.LOATHING_LEGION_ROLLERBLADES: // And so are these IOTM foldables return Integer.MAX_VALUE; case ItemPool.COBBS_KNOB_MAP: // This "B" item IS usable in Beecore. UseItemRequest.limiter = "encryption key"; return InventoryManager.getCount( ItemPool.ENCRYPTION_KEY ); default: if ( KoLCharacter.inBeecore() && KoLCharacter.hasBeeosity( itemName ) ) { UseItemRequest.limiter = "bees"; return 0; } break; } // Check binge requests before checking fullness or inebriety switch ( consumptionType ) { case KoLConstants.CONSUME_HOBO: case KoLConstants.CONSUME_GHOST: case KoLConstants.CONSUME_SLIME: return Integer.MAX_VALUE; case KoLConstants.CONSUME_ROBO: return 1; case KoLConstants.CONSUME_GUARDIAN: UseItemRequest.limiter = "character class"; return KoLCharacter.getClassType() == KoLCharacter.PASTAMANCER ? 1 : 0; } // Delegate to specialized classes as appropriate int inebriety = ConsumablesDatabase.getInebriety( itemName ); if ( inebriety > 0 ) { return DrinkItemRequest.maximumUses( itemId, itemName, inebriety, allowOverDrink ); } int fullness = ConsumablesDatabase.getFullness( itemName ); if ( fullness > 0 ) { return EatItemRequest.maximumUses( itemId, itemName, fullness ); } int spleenHit = ConsumablesDatabase.getSpleenHit( itemName ); if ( spleenHit > 0 ) { return SpleenItemRequest.maximumUses( itemId, itemName, spleenHit ); } int restorationMaximum = UseItemRequest.getRestorationMaximum( itemName ); if ( itemId <= 0 ) { return Integer.MAX_VALUE; } // Set reasonable default if the item fails to set a specific reason UseItemRequest.limiter = "a wizard"; switch ( itemId ) { case ItemPool.TINY_HOUSE: case ItemPool.TEARS: // These restore HP/MP but also remove Beaten Up // If you are currently Beaten Up, allow a single // usage. Otherwise, let code below limit based on // needed HP/MP recovery if ( KoLConstants.activeEffects.contains( KoLAdventure.BEATEN_UP ) && restorationMaximum == 0 ) { UseItemRequest.limiter = "needed restoration"; return 1; } break; case ItemPool.GONG: case ItemPool.KETCHUP_HOUND: UseItemRequest.limiter = "usability"; return 1; case ItemPool.MEDICINAL_HERBS: if ( restorationMaximum > 0 ) { UseItemRequest.limiter = "usability"; return 1; } break; case ItemPool.FIELD_GAR_POTION: // Disallow using potion if already Gar-ish Calendar date = Calendar.getInstance( TimeZone.getTimeZone( "GMT-0700" ) ); if( date.get( Calendar.DAY_OF_WEEK ) == Calendar.MONDAY ) { UseItemRequest.limiter = "uselessness on Mondays"; return 0; } if ( KoLConstants.activeEffects.contains( EffectPool.get( EffectPool.GARISH ) ) ) { UseItemRequest.limiter = "existing effect"; return 0; } return 1; case ItemPool.TOASTER: UseItemRequest.limiter = "usability"; return Preferences.getBoolean( "_toastSummoned" ) ? 0 : 1; case ItemPool.AMINO_ACIDS: UseItemRequest.limiter = "usability"; return 3; case ItemPool.DANCE_CARD: // Disallow using a dance card if already active if ( TurnCounter.isCounting( "Dance Card" ) ) { UseItemRequest.limiter = "existing counter"; return 0; } // Or if another counter would end on the same turn UseItemRequest.limiter = TurnCounter.getCounters( "", 3, 3 ); return UseItemRequest.limiter.length() > 0 ? 0 : 1; case ItemPool.GREEN_PEAWEE_MARBLE: case ItemPool.BROWN_CROCK_MARBLE: case ItemPool.RED_CHINA_MARBLE: case ItemPool.LEMONADE_MARBLE: case ItemPool.BUMBLEBEE_MARBLE: case ItemPool.JET_BENNIE_MARBLE: case ItemPool.BEIGE_CLAMBROTH_MARBLE: case ItemPool.STEELY_MARBLE: case ItemPool.BEACH_BALL_MARBLE: case ItemPool.BLACK_CATSEYE_MARBLE: // Using up to 1/2 produces bigger marbles. // Larger quantities can be used, but do nothing. UseItemRequest.limiter = "1/2 inventory"; return InventoryManager.getCount( itemId ) / 2; case ItemPool.CHEF: case ItemPool.CLOCKWORK_CHEF: case ItemPool.BARTENDER: case ItemPool.CLOCKWORK_BARTENDER: case ItemPool.MAID: case ItemPool.CLOCKWORK_MAID: case ItemPool.SCARECROW: case ItemPool.MEAT_GOLEM: case ItemPool.MEAT_GLOBE: // Campground equipment case ItemPool.BEANBAG_CHAIR: case ItemPool.GAUZE_HAMMOCK: case ItemPool.HOT_BEDDING: case ItemPool.COLD_BEDDING: case ItemPool.STENCH_BEDDING: case ItemPool.SPOOKY_BEDDING: case ItemPool.SLEAZE_BEDDING: case ItemPool.SALTWATERBED: case ItemPool.SPIRIT_BED: case ItemPool.BLACK_BLUE_LIGHT: case ItemPool.LOUDMOUTH_LARRY: case ItemPool.PLASMA_BALL: case ItemPool.FENG_SHUI: // Dwelling furnishings UseItemRequest.limiter = "campground regulations"; return 1; case ItemPool.ANCIENT_CURSED_FOOTLOCKER: UseItemRequest.limiter = "simple cursed key"; return InventoryManager.getCount( ItemPool.SIMPLE_CURSED_KEY ); case ItemPool.ORNATE_CURSED_CHEST: UseItemRequest.limiter = "ornate cursed key"; return InventoryManager.getCount( ItemPool.ORNATE_CURSED_KEY ); case ItemPool.GILDED_CURSED_CHEST: UseItemRequest.limiter = "gilded cursed key"; return InventoryManager.getCount( ItemPool.GILDED_CURSED_KEY ); case ItemPool.STUFFED_CHEST: UseItemRequest.limiter = "stuffed key"; return InventoryManager.getCount( ItemPool.STUFFED_KEY ); case ItemPool.PHOTOCOPIER: UseItemRequest.limiter = "photocopied monster"; return InventoryManager.hasItem( ItemPool.PHOTOCOPIED_MONSTER ) ? 0 : 1; case ItemPool.MOJO_FILTER: int spleenUsed = KoLCharacter.getSpleenUse(); int mojoUsesLeft = Math.max( 0, 3 - Preferences.getInteger( "currentMojoFilters" ) ); if( mojoUsesLeft <= spleenUsed ) { UseItemRequest.limiter = "daily limit"; return mojoUsesLeft; } UseItemRequest.limiter = "spleen"; return spleenUsed; case ItemPool.EXPRESS_CARD: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "expressCardUsed" ) ? 0 : 1; case ItemPool.SPICE_MELANGE: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "spiceMelangeUsed" ) ? 0 : 1; case ItemPool.ULTRA_MEGA_SOUR_BALL: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_ultraMegaSourBallUsed" ) ? 0 : 1; case ItemPool.ALIEN_PLANT_POD: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_alienPlantPodUsed" ) ? 0 : 1; case ItemPool.ALIEN_ANIMAL_MILK: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_alienAnimalMilkUsed" ) ? 0 : 1; case ItemPool.BORROWED_TIME: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_borrowedTimeUsed" ) ? 0 : 1; case ItemPool.SYNTHETIC_DOG_HAIR_PILL: if ( KoLCharacter.getInebriety() == 0 ) { UseItemRequest.limiter = "sobriety"; return 0; } UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_syntheticDogHairPillUsed" ) ? 0 : 1; case ItemPool.DISTENTION_PILL: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_distentionPillUsed" ) ? 0 : 1; case ItemPool.BURROWGRUB_HIVE: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "burrowgrubHiveUsed" ) ? 0 : 1; case ItemPool.MOVEABLE_FEAST: String familiar = KoLCharacter.getFamiliar().getRace(); if ( Preferences.getString( "_feastedFamiliars" ).contains( familiar ) ) { UseItemRequest.limiter = "a previous " + familiar + " feasting"; return 0; } UseItemRequest.limiter = "daily limit"; return Math.max( 0, 5 - Preferences.getInteger( "_feastUsed" ) ); case ItemPool.MILK_OF_MAGNESIUM: UseItemRequest.limiter = "remaining fullness"; int milkyTurns = ConsumablesDatabase.MILK.getCount( KoLConstants.activeEffects ); int fullnessAvailable = KoLCharacter.getFullnessLimit() - KoLCharacter.getFullness(); // If our current dose of Got Milk is sufficient to // fill us up, no milk is needed. int unmilkedTurns = fullnessAvailable - milkyTurns; if ( unmilkedTurns <= 0 ) { return 0; } // Otherwise, limit to number of useful potions int milkDuration = 10 + ( KoLCharacter.getClassType() == KoLCharacter.SAUCEROR ? 5 : 0 ) + ( KoLCharacter.hasSkill( "Impetuous Sauciness" ) ? 5 : 0 ); int limit = 0; while ( unmilkedTurns > 0 ) { unmilkedTurns -= milkDuration; limit++; } return limit; case ItemPool.GHOSTLY_BODY_PAINT: case ItemPool.NECROTIZING_BODY_SPRAY: case ItemPool.BITE_LIPSTICK: case ItemPool.WHISKER_PENCIL: case ItemPool.PRESS_ON_RIBS: if ( KoLConstants.activeEffects.contains( EffectPool.get( EffectPool.HAUNTING_LOOKS ) ) || KoLConstants.activeEffects.contains( EffectPool.get( EffectPool.DEAD_SEXY ) ) || KoLConstants.activeEffects.contains( EffectPool.get( EffectPool.VAMPIN ) ) || KoLConstants.activeEffects.contains( EffectPool.get( EffectPool.YIFFABLE_YOU ) ) || KoLConstants.activeEffects.contains( EffectPool.get( EffectPool.BONE_US_ROUND ) ) ) { UseItemRequest.limiter = "your current sexy costume"; return 0; } return 1; case ItemPool.BLACK_PAINT: if ( KoLCharacter.inFistcore() ) { UseItemRequest.limiter = "your teachings"; return 0; } return Integer.MAX_VALUE; case ItemPool.SLAPFIGHTING_BOOK: case ItemPool.SLAPFIGHTING_BOOK_USED: case ItemPool.UNCLE_ROMULUS: case ItemPool.UNCLE_ROMULUS_USED: case ItemPool.SNAKE_CHARMING_BOOK: case ItemPool.SNAKE_CHARMING_BOOK_USED: case ItemPool.ZU_MANNKASE_DIENEN: case ItemPool.ZU_MANNKASE_DIENEN_USED: case ItemPool.DYNAMITE_SUPERMAN_JONES: case ItemPool.DYNAMITE_SUPERMAN_JONES_USED: case ItemPool.INIGO_BOOK: case ItemPool.INIGO_BOOK_USED: String bookClass = UseItemRequest.itemToClass( itemId ); if ( !bookClass.equals( KoLCharacter.getClassType() ) ) { UseItemRequest.limiter = "your class"; return 0; } return Integer.MAX_VALUE; case ItemPool.ALL_YEAR_SUCKER: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_allYearSucker" ) ? 0 : 1; case ItemPool.DARK_CHOCOLATE_HEART: if ( restorationMaximum > 0 ) { UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_darkChocolateHeart" ) ? 0 : 1; } break; case ItemPool.JACKASS_PLUMBER_GAME: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_jackassPlumberGame" ) ? 0 : 1; case ItemPool.TRIVIAL_AVOCATIONS_GAME: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_trivialAvocationsGame" ) ? 0 : 1; case ItemPool.RESOLUTION_ADVENTUROUS: UseItemRequest.limiter = "daily limit"; return ( Preferences.getInteger( "_resolutionAdv" ) == 10 ? 0 : Integer.MAX_VALUE ); case ItemPool.ESSENTIAL_TOFU: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_essentialTofuUsed" ) ? 0 : 1; case ItemPool.VITACHOC_CAPSULE: UseItemRequest.limiter = "daily limit"; return ( 3 - Preferences.getInteger( "_vitachocCapsulesUsed" ) ); case ItemPool.CHOCOLATE_CIGAR: UseItemRequest.limiter = "daily limit"; return ( 3 - Preferences.getInteger( "_chocolateCigarsUsed" ) ); case ItemPool.FANCY_CHOCOLATE: case ItemPool.FANCY_CHOCOLATE_CAR: case ItemPool.FANCY_EVIL_CHOCOLATE: case ItemPool.CHOCOLATE_DISCO_BALL: case ItemPool.CHOCOLATE_PASTA_SPOON: case ItemPool.CHOCOLATE_SAUCEPAN: case ItemPool.CHOCOLATE_SEAL_CLUBBING_CLUB: case ItemPool.CHOCOLATE_STOLEN_ACCORDION: case ItemPool.CHOCOLATE_TURTLE_TOTEM: case ItemPool.BEET_MEDIOCREBAR: case ItemPool.CORN_MEDIOCREBAR: case ItemPool.CABBAGE_MEDIOCREBAR: case ItemPool.CHOCO_CRIMBOT: UseItemRequest.limiter = "daily limit"; return ( 3 - Preferences.getInteger( "_chocolatesUsed" ) ); case ItemPool.LOVE_CHOCOLATE: UseItemRequest.limiter = "daily limit"; return ( 3 - Preferences.getInteger( "_loveChocolatesUsed" ) ); case ItemPool.CREEPY_VOODOO_DOLL: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_creepyVoodooDollUsed" ) ? 0 : 1; case ItemPool.HOBBY_HORSE: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_hobbyHorseUsed" ) ? 0 : 1; case ItemPool.BALL_IN_A_CUP: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_ballInACupUsed" ) ? 0 : 1; case ItemPool.SET_OF_JACKS: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_setOfJacksUsed" ) ? 0 : 1; case ItemPool.BAG_OF_CANDY: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_bagOfCandyUsed" ) ? 0 : 1; case ItemPool.EMBLEM_AKGYXOTH: case ItemPool.IDOL_AKGYXOTH: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_akgyxothUsed" ) ? 0 : 1; case ItemPool.GNOLL_EYE: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_gnollEyeUsed" ) ? 0 : 1; case ItemPool.KOL_CON_SIX_PACK: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_kolConSixPackUsed" ) ? 0 : 1; case ItemPool.MUS_MANUAL: case ItemPool.MYS_MANUAL: case ItemPool.MOX_MANUAL: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_guildManualUsed" ) ? 0 : 1; case ItemPool.STUFFED_POCKETWATCH: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_stuffedPocketwatchUsed" ) ? 0 : 1; case ItemPool.STYX_SPRAY: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_styxSprayUsed" ) ? 0 : 1; case ItemPool.STABONIC_SCROLL: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_stabonicScrollUsed" ) ? 0 : 1; case ItemPool.COAL_PAPERWEIGHT: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_coalPaperweightUsed" ) ? 0 : 1; case ItemPool.JINGLE_BELL: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_jingleBellUsed" ) ? 0 : 1; case ItemPool.BOX_OF_HAMMERS: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_boxOfHammersUsed" ) ? 0 : 1; case ItemPool.TEMPURA_AIR: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_tempuraAirUsed" ) ? 0 : 1; case ItemPool.PRESSURIZED_PNEUMATICITY: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_pneumaticityPotionUsed" ) ? 0 : 1; case ItemPool.HYPERINFLATED_SEAL_LUNG: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_hyperinflatedSealLungUsed" ) ? 0 : 1; case ItemPool.BALLAST_TURTLE: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_ballastTurtleUsed" ) ? 0 : 1; case ItemPool.CSA_FIRE_STARTING_KIT: if ( !KoLCharacter.getHippyStoneBroken() && Preferences.getInteger( "choiceAdventure595" ) == 1 ) { UseItemRequest.limiter = "an unbroken hippy stone"; return 0; } UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_fireStartingKitUsed" ) ? 0 : 1; case ItemPool.LEFT_BEAR_ARM: UseItemRequest.limiter = "insufficient right bear arms"; return InventoryManager.getCount( ItemPool.RIGHT_BEAR_ARM ); case ItemPool.LEGENDARY_BEAT: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_legendaryBeat" ) ? 0 : 1; case ItemPool.CURSED_MICROWAVE: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_cursedMicrowaveUsed" ) ? 0 : 1; case ItemPool.CURSED_KEG: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_cursedKegUsed" ) ? 0 : 1; case ItemPool.TACO_FLIER: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_tacoFlierUsed" ) ? 0 : 1; case ItemPool.SUSPICIOUS_JAR: case ItemPool.GOURD_JAR: case ItemPool.MYSTIC_JAR: case ItemPool.OLD_MAN_JAR: case ItemPool.ARTIST_JAR: case ItemPool.MEATSMITH_JAR: case ItemPool.JICK_JAR: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_psychoJarUsed" ) ? 0 : 1; case ItemPool.FISHY_PIPE: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_fishyPipeUsed" ) ? 0 : 1; case ItemPool.SUSHI_ROLLING_MAT: UseItemRequest.limiter = "usability"; return KoLCharacter.hasSushiMat() ? 0 : 1; case ItemPool.DEFECTIVE_TOKEN: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_defectiveTokenUsed" ) ? 0 : 1; case ItemPool.SILVER_DREAD_FLASK: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_silverDreadFlaskUsed" ) ? 0 : 1; case ItemPool.BRASS_DREAD_FLASK: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_brassDreadFlaskUsed" ) ? 0 : 1; case ItemPool.ETERNAL_CAR_BATTERY: if ( restorationMaximum > 0 ) { UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_eternalCarBatteryUsed" ) ? 0 : 1; } break; case ItemPool.FOLDER_01: case ItemPool.FOLDER_02: case ItemPool.FOLDER_03: case ItemPool.FOLDER_04: case ItemPool.FOLDER_05: case ItemPool.FOLDER_06: case ItemPool.FOLDER_07: case ItemPool.FOLDER_08: case ItemPool.FOLDER_09: case ItemPool.FOLDER_10: case ItemPool.FOLDER_11: case ItemPool.FOLDER_12: case ItemPool.FOLDER_13: case ItemPool.FOLDER_14: case ItemPool.FOLDER_15: case ItemPool.FOLDER_16: case ItemPool.FOLDER_17: case ItemPool.FOLDER_18: case ItemPool.FOLDER_19: case ItemPool.FOLDER_20: case ItemPool.FOLDER_21: case ItemPool.FOLDER_22: case ItemPool.FOLDER_23: UseItemRequest.limiter = "folder holder"; return EquipmentRequest.availableFolder() == -1 ? 0 : 1; case ItemPool.PASTA_ADDITIVE: if ( KoLCharacter.getClassType() != KoLCharacter.PASTAMANCER ) { UseItemRequest.limiter = "character class"; return 0; } UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_pastaAdditive" ) ? 0 : 1; case ItemPool.WARBEAR_BREAKFAST_MACHINE: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_warbearBreakfastMachineUsed" ) ? 0 : 1; case ItemPool.WARBEAR_SODA_MACHINE: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_warbearSodaMachineUsed" ) ? 0 : 1; case ItemPool.WARBEAR_GYROCOPTER: case ItemPool.WARBEAR_GYROCOPTER_BROKEN: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_warbearGyrocopterUsed" ) ? 0 : 1; case ItemPool.WARBEAR_BANK: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_warbearBankUsed" ) ? 0 : 1; case ItemPool.LUPINE_APPETITE_HORMONES: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_lupineHormonesUsed" ) ? 0 : 1; case ItemPool.BLANK_OUT_BOTTLE: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_blankoutUsed" ) ? 0 : 1; case ItemPool.CORRUPTED_STARDUST: case ItemPool.PIXEL_ORB: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_corruptedStardustUsed" ) ? 0 : 1; case ItemPool.SWEET_TOOTH: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_sweetToothUsed" ) ? 0 : 1; case ItemPool.VORACI_TEA: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_voraciTeaUsed" ) ? 0 : 1; case ItemPool.SOBRIE_TEA: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_sobrieTeaUsed" ) ? 0 : 1; case ItemPool.CHRONER_TRIGGER: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_chronerTriggerUsed" ) ? 0 : 1; case ItemPool.CHRONER_CROSS: if ( Preferences.getBoolean( "_chronerCrossUsed" ) ) { UseItemRequest.limiter = "daily limit"; return 0; } if ( !KoLConstants.inventory.contains( ItemPool.get( ItemPool.CHRONER, 1 ) ) ) { UseItemRequest.limiter = "not having a Chroner"; return 0; } return 1; case ItemPool.PICKY_TWEEZERS: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_pickyTweezersUsed" ) ? 0 : 1; case ItemPool.GAUDY_KEY: if ( !KoLCharacter.hasEquipped( ItemPool.get( ItemPool.PIRATE_FLEDGES, 1 ) ) && !EquipmentManager.isWearingOutfit( OutfitPool.SWASHBUCKLING_GETUP ) ) { UseItemRequest.limiter = "not wearing pirate gear"; return 0; } return Integer.MAX_VALUE; case ItemPool.BITTYCAR_HOTCAR: UseItemRequest.limiter = "already being active"; return Preferences.getString( "_bittycar" ).equals( "hotcar" ) ? 0 : 1; case ItemPool.BITTYCAR_MEATCAR: UseItemRequest.limiter = "already being active"; return Preferences.getString( "_bittycar" ).equals( "meatcar" ) ? 0 : 1; case ItemPool.BITTYCAR_SOULCAR: UseItemRequest.limiter = "already being active"; return Preferences.getString( "_bittycar" ).equals( "soulcar" ) ? 0 : 1; case ItemPool.RED_GREEN_RAIN_STICK: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_rainStickUsed" ) ? 0 : 1; case ItemPool.STILL_BEATING_SPLEEN: UseItemRequest.limiter = "already being active"; return Preferences.getInteger( "lastStillBeatingSpleen" ) == KoLCharacter.getAscensions() ? 0 : 1; case ItemPool.HUNGER_SAUCE: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_hungerSauceUsed" ) ? 0 : 1; case ItemPool.BRAIN_PRESERVATION_FLUID: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_brainPreservationFluidUsed" ) ? 0 : 1; case ItemPool.COCKTAIL_SHAKER: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_cocktailShakerUsed" ) ? 0 : 1; case ItemPool.TONIC_DJINN: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_tonicDjinn" ) ? 0 : 1; case ItemPool.TWELVE_NIGHT_ENERGY: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_twelveNightEnergyUsed" ) ? 0 : 1; case ItemPool.FUDGE_SPORK: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_fudgeSporkUsed" ) ? 0 : 1; case ItemPool.MAYONEX: case ItemPool.MAYODIOL: case ItemPool.MAYOSTAT: case ItemPool.MAYOZAPINE: case ItemPool.MAYOFLEX: AdventureResult workshedItem = CampgroundRequest.getCurrentWorkshedItem(); if ( workshedItem == null || workshedItem.getItemId() != ItemPool.MAYO_CLINIC ) { UseItemRequest.limiter = "mayo clinic not installed"; return 0; } UseItemRequest.limiter = "mayonaise already in mouth"; return Preferences.getString( "mayoInMouth" ).equals( "" ) ? 1 : 0; case ItemPool.CIRCLE_DRUM: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_circleDrumUsed" ) ? 0 : 1; case ItemPool.CLARA_BELL: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_claraBellUsed" ) ? 0 : 1; case ItemPool.GLENN_DICE: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_glennGoldenDiceUsed" ) ? 0 : 1; case ItemPool.CODPIECE: case ItemPool.BASS_CLARINET: case ItemPool.FISH_HATCHET: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_floundryItemUsed" ) ? 0 : 1; case ItemPool.BACON_MACHINE: UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_baconMachineUsed" ) ? 0 : 1; case ItemPool.HOLORECORD_POWERGUY: case ItemPool.HOLORECORD_SHRIEKING_WEASEL: case ItemPool.HOLORECORD_SUPERDRIFTER: case ItemPool.HOLORECORD_LUCKY_STRIKES: case ItemPool.HOLORECORD_DRUNK_UNCLES: case ItemPool.HOLORECORD_EMD: case ItemPool.HOLORECORD_PIGS: UseItemRequest.limiter = "lack of Wrist-Boy"; return InventoryManager.hasItem( ItemPool.WRIST_BOY ) ? Integer.MAX_VALUE : 0; case ItemPool.SCHOOL_OF_HARD_KNOCKS_DIPLOMA: if ( !KoLCharacter.getHippyStoneBroken() ) { UseItemRequest.limiter = "an unbroken hippy stone"; return 0; } UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_hardKnocksDiplomaUsed" ) ? 0 : 1; case ItemPool.TURKEY_BLASTER: UseItemRequest.limiter = "daily limit"; return ( 3 - Preferences.getInteger( "_turkeyBlastersUsed" ) ); } if ( restorationMaximum < Integer.MAX_VALUE ) { UseItemRequest.limiter = "needed restoration"; return restorationMaximum; } if ( CampgroundRequest.isWorkshedItem( itemId ) ) { UseItemRequest.limiter = "daily limit"; return Preferences.getBoolean( "_workshedItemUsed" ) ? 0 : 1; } switch ( consumptionType ) { case KoLConstants.GROW_FAMILIAR: if ( KoLCharacter.inAxecore() ) { UseItemRequest.limiter = "Boris's scorn for familiars"; return 0; } UseItemRequest.limiter = "the fine print in your Familiar-Gro\u2122 Terrarium owner's manual"; return 1; case KoLConstants.EQUIP_WEAPON: // Even if you can dual-wield, if we attempt to "use" a // weapon, it will become an "equip", which always goes // in the main hand. case KoLConstants.EQUIP_FAMILIAR: case KoLConstants.EQUIP_HAT: case KoLConstants.EQUIP_PANTS: case KoLConstants.EQUIP_CONTAINER: case KoLConstants.EQUIP_SHIRT: case KoLConstants.EQUIP_OFFHAND: UseItemRequest.limiter = "slot"; return 1; case KoLConstants.EQUIP_ACCESSORY: UseItemRequest.limiter = "slot"; return 3; } Integer key = IntegerPool.get( itemId ); if ( UseItemRequest.LIMITED_USES.containsKey( key ) ) { UseItemRequest.limiter = "unstackable effect"; return KoLConstants.activeEffects.contains( UseItemRequest.LIMITED_USES.get( key ) ) ? 0 : 1; } return Integer.MAX_VALUE; } protected static int getRestorationMaximum( final String itemName ) { float hpRestored = HPRestoreItemList.getHealthRestored( itemName ); boolean restoresHP = hpRestored != Integer.MIN_VALUE; float mpRestored = MPRestoreItemList.getManaRestored( itemName ); boolean restoresMP = mpRestored != Integer.MIN_VALUE; if ( !restoresHP && !restoresMP ) { return Integer.MAX_VALUE; } int maximumSuggested = 0; if ( hpRestored != 0.0f ) { float belowMax = KoLCharacter.getMaximumHP() - KoLCharacter.getCurrentHP(); maximumSuggested = Math.max( maximumSuggested, (int) Math.ceil( belowMax / hpRestored ) ); } if ( mpRestored != 0.0f ) { float belowMax = KoLCharacter.getMaximumMP() - KoLCharacter.getCurrentMP(); maximumSuggested = Math.max( maximumSuggested, (int) Math.ceil( belowMax / mpRestored ) ); } return maximumSuggested; } @Override public void run() { if ( GenericRequest.abortIfInFightOrChoice() ) { return; } // Hide memento items from your familiars if ( this.isBingeRequest() && Preferences.getBoolean( "mementoListActive" ) && KoLConstants.mementoList.contains( this.itemUsed ) ) { KoLmafia.updateDisplay( MafiaState.ERROR, "Don't feed mementos to your familiars." ); return; } // Equipment should be handled by a different kind of request. int itemId = this.itemUsed.getItemId(); boolean isSealFigurine = ItemDatabase.isSealFigurine( itemId ); boolean isBRICKOMonster = ItemDatabase.isBRICKOMonster( itemId ); // Seal figurines require special handling in the HTML, but // they also require some use protection if ( isSealFigurine && !EquipmentManager.wieldingClub() ) { KoLmafia.updateDisplay( MafiaState.ERROR, "You really should wield a club before using that." ); return; } switch ( itemId ) { case ItemPool.DECK_OF_EVERY_CARD: // Treat a "use" of the deck as "play random" ( new DeckOfEveryCardRequest() ).run(); return; case ItemPool.BRICKO_SWORD: case ItemPool.BRICKO_HAT: case ItemPool.BRICKO_PANTS: if ( !InventoryManager.retrieveItem( this.itemUsed ) ) { KoLmafia.updateDisplay( MafiaState.ERROR, "You don't have one of those." ); return; } KoLmafia.updateDisplay( "Splitting bricks..." ); GenericRequest req = new GenericRequest( "inventory.php?action=breakbricko&pwd&whichitem=" + itemId ); RequestThread.postRequest( req ); return; case ItemPool.STICKER_SWORD: case ItemPool.STICKER_CROSSBOW: if ( !InventoryManager.retrieveItem( this.itemUsed ) ) { KoLmafia.updateDisplay( MafiaState.ERROR, "You don't have one of those." ); return; } RequestThread.postRequest( new GenericRequest( "bedazzle.php?action=fold&pwd" ) ); return; case ItemPool.MACGUFFIN_DIARY: case ItemPool.ED_DIARY: { // Make it a RelayRequest since we don't want a charpane refresh RelayRequest request = new RelayRequest( false ); request.constructURLString( "diary.php?textversion=1" ); RequestThread.postRequest( request ); KoLmafia.updateDisplay( "Your father's diary has been read." ); return; } case ItemPool.VOLCANO_MAP: { try { GenericRequest.suppressUpdate( true ); GenericRequest request = new GenericRequest( "inv_use.php?which=3&whichitem=3291&pwd" ); RequestThread.postRequest( request ); // This will redirect to volcanoisland.php } finally { GenericRequest.suppressUpdate( false ); } KoLmafia.updateDisplay( "The secret tropical island volcano lair map has been read." ); return; } case ItemPool.NEWBIESPORT_TENT: case ItemPool.BARSKIN_TENT: case ItemPool.COTTAGE: case ItemPool.BRICKO_PYRAMID: case ItemPool.HOUSE: case ItemPool.SANDCASTLE: case ItemPool.GINORMOUS_PUMPKIN: case ItemPool.TWIG_HOUSE: case ItemPool.GINGERBREAD_HOUSE: case ItemPool.HOBO_FORTRESS: case ItemPool.GIANT_FARADAY_CAGE: case ItemPool.SNOW_FORT: case ItemPool.ELEVENT: case ItemPool.RESIDENCE_CUBE: AdventureResult dwelling = CampgroundRequest.getCurrentDwelling(); int oldLevel = CampgroundRequest.getCurrentDwellingLevel(); int newLevel = CampgroundRequest.dwellingLevel( itemId ); if ( ( oldLevel >= 7 || newLevel < oldLevel ) && dwelling != null && !UseItemRequest.confirmReplacement( dwelling.getName() ) ) { return; } break; case ItemPool.HOT_BEDDING: case ItemPool.COLD_BEDDING: case ItemPool.STENCH_BEDDING: case ItemPool.SPOOKY_BEDDING: case ItemPool.SLEAZE_BEDDING: case ItemPool.SALTWATERBED: case ItemPool.SPIRIT_BED: case ItemPool.BEANBAG_CHAIR: case ItemPool.GAUZE_HAMMOCK: AdventureResult bed = CampgroundRequest.getCurrentBed(); if ( bed != null && !UseItemRequest.confirmReplacement( bed.getName() ) ) { return; } break; case ItemPool.SPICE_MELANGE: case ItemPool.ULTRA_MEGA_SOUR_BALL: boolean unfilledStomach = false; boolean unfilledLiver = false; String organ = null; if ( KoLCharacter.canEat() && KoLCharacter.getFullness() < 3 ) { unfilledStomach = true; } if ( KoLCharacter.canDrink() && KoLCharacter.getInebriety() < 3 ) { unfilledLiver = true; } if ( !unfilledStomach && !unfilledLiver ) { break; } if ( unfilledStomach && unfilledLiver ) { organ = "stomach and liver"; } else if ( unfilledStomach ) { organ = "stomach"; } else if ( unfilledLiver ) { organ = "liver"; } if ( !InputFieldUtilities.confirm( "A " + ItemDatabase.getItemName( itemId ) + "clears 3 " + organ + " and you have not filled that yet. Are you sure you want to use it?" ) ) { return; } break; case ItemPool.ALIEN_ANIMAL_MILK: if ( KoLCharacter.getFullness() >= 3 ) { break; } if ( !InputFieldUtilities.confirm( "Alien animal milk clears 3 stomach" + " and you have not filled that yet. Are you sure you want to use it?" ) ) { return; } break; case ItemPool.ALIEN_PLANT_POD: if ( KoLCharacter.getInebriety() >= 3 ) { break; } if ( !InputFieldUtilities.confirm( "Alien plant pod clears 3 liver" + " and you have not filled that yet. Are you sure you want to use it?" ) ) { return; } break; } switch ( this.consumptionType ) { case KoLConstants.CONSUME_STICKER: case KoLConstants.CONSUME_CARD: case KoLConstants.CONSUME_FOLDER: case KoLConstants.CONSUME_BOOTSKIN: case KoLConstants.CONSUME_BOOTSPUR: case KoLConstants.CONSUME_SIXGUN: case KoLConstants.EQUIP_HAT: case KoLConstants.EQUIP_WEAPON: case KoLConstants.EQUIP_OFFHAND: case KoLConstants.EQUIP_SHIRT: case KoLConstants.EQUIP_PANTS: case KoLConstants.EQUIP_CONTAINER: case KoLConstants.EQUIP_ACCESSORY: case KoLConstants.EQUIP_FAMILIAR: RequestThread.postRequest( new EquipmentRequest( this.itemUsed ) ); return; case KoLConstants.CONSUME_SPHERE: RequestThread.postRequest( new PortalRequest( this.itemUsed ) ); return; case KoLConstants.NO_CONSUME: // no primary use, but a secondary use may be applicable if ( ItemDatabase.getAttribute( itemId, ItemDatabase.ATTR_CURSE ) ) { RequestThread.postRequest( new CurseRequest( this.itemUsed ) ); return; } KoLmafia.updateDisplay( this.itemUsed.getName() + " is unusable." ); return; } UseItemRequest.lastUpdate = ""; if ( !ConsumablesDatabase.meetsLevelRequirement( this.itemUsed.getName() ) ) { UseItemRequest.lastUpdate = "Insufficient level to consume " + this.itemUsed; KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); return; } int maximumUses = UseItemRequest.maximumUses( itemId, this.consumptionType ); if ( maximumUses < this.itemUsed.getCount() ) { KoLmafia.updateDisplay( "(usable quantity of " + this.itemUsed + " is limited to " + maximumUses + " by " + UseItemRequest.limiter + ")" ); this.itemUsed = this.itemUsed.getInstance( maximumUses ); } if ( this.itemUsed.getCount() < 1 ) { return; } // If we were previously using multiuse.php but have reduced consumption to 1, // switch to inv_use.php if ( maximumUses == 1 && this.getPath().equals( "multiuse.php" ) ) { this.constructURLString( "inv_use.php" ); this.addFormField( "whichitem", String.valueOf( this.itemUsed.getItemId() ) ); } switch ( itemId ) { case ItemPool.LUCIFER: // Burn any existing MP that would otherwise be wasted. ManaBurnManager.burnMana( KoLCharacter.getMaximumMP() - 9 * (KoLCharacter.getCurrentHP() - 1) ); break; case ItemPool.WHAT_CARD: case ItemPool.WHEN_CARD: case ItemPool.WHO_CARD: case ItemPool.WHERE_CARD: this.addFormField( "answerplz", "1" ); break; case ItemPool.PHIAL_OF_HOTNESS: case ItemPool.PHIAL_OF_COLDNESS: case ItemPool.PHIAL_OF_SPOOKINESS: case ItemPool.PHIAL_OF_STENCH: case ItemPool.PHIAL_OF_SLEAZINESS: // If it's an elemental phial, remove other elemental effects first. for ( int i = 0; i < BasementRequest.ELEMENT_PHIALS.length; ++i ) { AdventureResult phial = BasementRequest.ELEMENT_PHIALS[ i ]; if ( itemId != phial.getItemId() ) { continue; } // i is the index of the phial we are using for ( int j = 0; j < BasementRequest.ELEMENT_FORMS.length; ++j ) { if ( j == i ) { continue; } AdventureResult form = BasementRequest.ELEMENT_FORMS[ j ]; if ( !KoLConstants.activeEffects.contains( form ) ) { continue; } RequestThread.postRequest( new UneffectRequest( form ) ); if ( !KoLmafia.permitsContinue() ) { return; } break; } break; } break; } if ( this.consumptionType != KoLConstants.INFINITE_USES && !UseItemRequest.sequentialConsume( itemId ) && !InventoryManager.retrieveItem( this.itemUsed ) ) { return; } int iterations = 1; int origCount = this.itemUsed.getCount(); if ( origCount != 1 ) { if ( itemId == ItemPool.YUMMY_TUMMY_BEAN ) { // If not divisible by 20, make the first iteration short iterations = (origCount + 19) / 20; this.itemUsed = this.itemUsed.getInstance( (origCount + 19) % 20 + 1 ); } else if ( itemId == ItemPool.PACK_OF_POGS ) { // If not divisible by 11, make the first iteration short iterations = (origCount + 10) / 11; this.itemUsed = this.itemUsed.getInstance( (origCount + 10) % 11 + 1 ); } else switch ( this.consumptionType ) { case KoLConstants.INFINITE_USES: { int type = ItemDatabase.getConsumptionType( this.itemUsed.getItemId() ); if ( type != KoLConstants.CONSUME_MULTIPLE ) { iterations = origCount; this.itemUsed = this.itemUsed.getInstance( 1 ); } break; } case KoLConstants.CONSUME_MULTIPLE: case KoLConstants.CONSUME_HOBO: case KoLConstants.CONSUME_GHOST: case KoLConstants.CONSUME_SLIME: case KoLConstants.CONSUME_ROBO: break; default: iterations = origCount; this.itemUsed = this.itemUsed.getInstance( 1 ); } } if ( itemId == ItemPool.MAFIA_ARIA ) { SpecialOutfit.createImplicitCheckpoint(); AdventureResult cummerbund = ItemPool.get( ItemPool.CUMMERBUND, 1 ); if ( !KoLCharacter.hasEquipped( cummerbund ) ) { RequestThread.postRequest( new EquipmentRequest( cummerbund ) ); } } String originalURLString = this.getURLString(); for ( int i = 1; i <= iterations && KoLmafia.permitsContinue(); ++i ) { this.constructURLString( originalURLString ); this.useOnce( i, iterations, "Using" ); if ( itemId == ItemPool.YUMMY_TUMMY_BEAN ) { // the first iteration may have been short this.itemUsed = this.itemUsed.getInstance( 20 ); } else if ( itemId == ItemPool.PACK_OF_POGS ) { // the first iteration may have been short this.itemUsed = this.itemUsed.getInstance( 11 ); } if ( ( isSealFigurine || isBRICKOMonster ) && KoLmafia.permitsContinue() ) { this.addFormField( "checked", "1" ); super.run(); } } if ( itemId == ItemPool.MAFIA_ARIA ) { SpecialOutfit.restoreImplicitCheckpoint(); } if ( KoLmafia.permitsContinue() ) { KoLmafia.updateDisplay( "Finished using " + origCount + " " + this.itemUsed.getName() + "." ); } } private static final boolean sequentialConsume( final int itemId ) { return false; } public static final boolean confirmReplacement( final String name ) { if ( !GenericFrame.instanceExists() ) { return true; } if ( !InputFieldUtilities.confirm( "Are you sure you want to replace your " + name + "?" ) ) { return false; } return true; } @Override protected boolean shouldFollowRedirect() { return true; } public void useOnce( final int currentIteration, final int totalIterations, String useTypeAsString ) { UseItemRequest.lastUpdate = ""; if ( this.consumptionType == KoLConstants.CONSUME_ZAP ) { ZapCommand.zap( this.getItemUsed().getName() ); return; } // Check to make sure the character has the item in their // inventory first - if not, report the error message and // return from the method. if ( !InventoryManager.retrieveItem( this.itemUsed ) ) { UseItemRequest.lastUpdate = "Insufficient items to use."; return; } if ( this.getAdventuresUsed() > 0 ) { // If we are about to use an item which can use adventures, set location to "None" // for the benefit of betweenBattleScripts Preferences.setString( "nextAdventure", "None" ); RecoveryManager.runBetweenBattleChecks( true ); } switch ( this.consumptionType ) { case KoLConstants.CONSUME_HOBO: if ( KoLCharacter.getFamiliar().getId() != FamiliarPool.HOBO ) { KoLmafia.updateDisplay( MafiaState.ERROR, "You don't have a Spirit Hobo equipped" ); return; } EatItemRequest.clearFoodHelper(); this.addFormField( "action", "binge" ); this.addFormField( "qty", String.valueOf( this.itemUsed.getCount() ) ); useTypeAsString = "Boozing hobo with"; break; case KoLConstants.CONSUME_GHOST: if ( KoLCharacter.getFamiliar().getId() != FamiliarPool.GHOST ) { KoLmafia.updateDisplay( MafiaState.ERROR, "You don't have a Gluttonous Green Ghost equipped" ); return; } DrinkItemRequest.clearDrinkHelper(); this.addFormField( "action", "binge" ); this.addFormField( "qty", String.valueOf( this.itemUsed.getCount() ) ); useTypeAsString = "Feeding ghost with"; break; case KoLConstants.CONSUME_SLIME: if ( KoLCharacter.getFamiliar().getId() != FamiliarPool.SLIMELING ) { KoLmafia.updateDisplay( MafiaState.ERROR, "You don't have a Slimeling equipped" ); return; } this.addFormField( "action", "binge" ); this.addFormField( "qty", String.valueOf( this.itemUsed.getCount() ) ); useTypeAsString = "Feeding slimeling with"; break; case KoLConstants.CONSUME_MIMIC: if ( KoLCharacter.getFamiliar().getId() != FamiliarPool.STOCKING_MIMIC ) { KoLmafia.updateDisplay( MafiaState.ERROR, "You don't have a Stocking Mimic equipped" ); return; } this.addFormField( "action", "candy" ); useTypeAsString = "Feeding stocking mimic with"; break; case KoLConstants.CONSUME_ROBO: if ( KoLCharacter.getFamiliar().getId() != FamiliarPool.ROBORTENDER ) { KoLmafia.updateDisplay( MafiaState.ERROR, "You don't have a Robortender equipped" ); return; } this.addFormField( "action", "robooze" ); this.addFormField( "ajax", "1" ); useTypeAsString = "Boozing Robortender with"; break; case KoLConstants.CONSUME_MULTIPLE: if ( this.itemUsed.getCount() > 1 ) { this.addFormField( "action", "useitem" ); this.addFormField( "quantity", String.valueOf( this.itemUsed.getCount() ) ); } // Fall through default: this.addFormField( "ajax", "1" ); if ( UseItemRequest.needsConfirmation( this.itemUsed ) ) { this.addFormField( "confirm", "true" ); } break; } this.runOneIteration( currentIteration, totalIterations, useTypeAsString ); } protected void runOneIteration( final int currentIteration, final int totalIterations, String useTypeAsString ) { StringBuilder message = new StringBuilder(); message.append( useTypeAsString ); message.append( " " ); if ( totalIterations == 1 ) { message.append( String.valueOf( this.itemUsed.getCount() ) ); message.append( " " ); } message.append( this.itemUsed.getName() ); if ( totalIterations != 1 ) { message.append( " (" ); message.append( String.valueOf( currentIteration ) ); message.append( " of " ); message.append( String.valueOf( totalIterations ) ); message.append( ")" ); } message.append( "..." ); KoLmafia.updateDisplay( message.toString() ); super.run(); if ( this.getPath().startsWith( "choice.php" ) ) { // A UseItemRequest counts as automation. If this // choice option is not complete, let ChoiceManager // automate it. ChoiceManager.processChoiceAdventure( this.responseText ); } } public static String elementalHelper( String remove, Element resist, int amount ) { int effectId = EffectDatabase.getEffectId( remove ); AdventureResult effect = EffectPool.get( effectId ); if ( KoLConstants.activeEffects.contains( effect ) ) { RequestThread.postRequest( new UneffectRequest( effect ) ); } if ( KoLConstants.activeEffects.contains( effect ) ) { return "Unable to remove " + remove + ", which makes this helper unusable."; } int healthNeeded = (int) Math.ceil(amount * (100.0f - KoLCharacter.getElementalResistance( resist )) / 100.0f); if ( KoLCharacter.getCurrentHP() <= healthNeeded ) { RecoveryManager.recoverHP( healthNeeded + 1 ); } if ( KoLCharacter.getCurrentHP() <= healthNeeded ) { return "Unable to gain enough HP to survive the use of this helper."; } return ""; } @Override public void processResults() { if ( this.getPath().startsWith( "choice.php" ) ) { // We have been redirected. Unlike a redirection to fight.php, // which GenericRequest automates in FightRequest.INSTANCE, we // automate this ourself in runOneIteration. Item consumption // was handled in GenericRequest.checkItemRedirection, so punt. return; } switch ( this.consumptionType ) { case KoLConstants.CONSUME_GHOST: case KoLConstants.CONSUME_HOBO: case KoLConstants.CONSUME_SLIME: case KoLConstants.CONSUME_MIMIC: if ( !UseItemRequest.parseBinge( this.getURLString(), this.responseText ) ) { KoLmafia.updateDisplay( MafiaState.ERROR, "Your current familiar can't use that." ); } return; case KoLConstants.CONSUME_ROBO: if ( !UseItemRequest.parseRobortenderBinge( this.getURLString(), this.responseText ) ) { KoLmafia.updateDisplay( MafiaState.ERROR, "Your Robortender can't drink that." ); } return; } UseItemRequest.lastItemUsed = this.itemUsed; UseItemRequest.currentItemId = this.itemUsed.getItemId(); UseItemRequest.parseConsumption( this.responseText, true ); ResponseTextParser.learnRecipe( this.getURLString(), this.responseText ); } public static final void parseBricko( final String responseText ) { if ( responseText.contains( "You break apart your" ) ) { Matcher matcher = UseItemRequest.BRICKO_PATTERN.matcher( responseText ); if ( matcher.find() ) { AdventureResult brickoItem = ItemPool.get( matcher.group( 1 ), -1 ); ResultProcessor.processResult( brickoItem ); } } } public static final boolean parseBinge( final String urlString, final String responseText ) { AdventureResult item = UseItemRequest.extractBingedItem( urlString ); if ( item == null ) { return true; } // Looks like you don't currently have a familiar capable of // binging. // // You're not currently using a Gluttonous Green Ghost. if ( responseText.contains( "don't currently have" ) || responseText.contains( "not currently using" ) ) { return false; } // You don't have that many of those. if ( responseText.contains( "don't have that many" ) ) { return true; } // You don't actually have any of that item.</ if ( responseText.contains( "don't actually have any" ) ) { return true; } // [familiar name] approaches the [item] but doesn't seem interested. if ( responseText.contains( "doesn't seem interested" ) ) { return true; } // That is not something you can give to your Slimeling if ( responseText.contains( "not something you can give" ) ) { return true; } // <name> takes the <candy> and quickly consumes them. He // grows a bit. if ( responseText.contains( "He grows a bit" ) ) { KoLCharacter.getFamiliar().addNonCombatExperience( item.getCount() ); } FamiliarData familiar = KoLCharacter.getFamiliar(); int id = familiar.getId(); // Estimate Slimeling charges if ( id == FamiliarPool.SLIMELING ) { int count = item.getCount(); int itemId = item.getItemId(); String name = item.getName(); if ( itemId == ItemPool.GNOLLISH_AUTOPLUNGER || ConcoctionDatabase.meatStackCreation( itemId ) != null ) { Preferences.increment( "slimelingStacksDue", count ); } else { // round down for now, since we don't know how this really works float charges = count * EquipmentDatabase.getPower( itemId ) / 10.0F; Preferences.setFloat( "slimelingFullness", Preferences.getFloat( "slimelingFullness" ) + charges ); } } ResultProcessor.processResult( item.getNegation() ); return true; } public static final boolean parseRobortenderBinge( final String urlString, final String responseText ) { // inventory.php?pwd&action=robooze&whichitem=9396 if ( !urlString.startsWith( "inventory.php" ) || !urlString.contains( "action=robooze" ) ) { return false; } if ( !responseText.contains( "the cocktail" ) && !responseText.contains( "the drink" ) ) { return false; } Matcher itemMatcher = GenericRequest.WHICHITEM_PATTERN.matcher( urlString ); if ( !itemMatcher.find() ) { return false; } int itemId = StringUtilities.parseInt( itemMatcher.group( 1 ) ); AdventureResult item = ItemPool.get( itemId, -1 ); String pref = Preferences.getString( "_roboDrinks" ); if ( pref.length() != 0 ) { pref += ","; } pref += item.getName(); Preferences.setString( "_roboDrinks", pref ); KoLCharacter.recalculateAdjustments(); KoLCharacter.updateStatus(); ResultProcessor.processResult( item ); return true; } public static final boolean parseAbsorb( final String urlString, final String responseText ) { if ( !KoLCharacter.inNoobcore() ) { return true; } AdventureResult item = UseItemRequest.extractAbsorbedItem( urlString ); if ( item == null ) { return true; } // You absorb some new knowledge of humanity! // You absorb the // You don't gain any new knowledge from absorbing that item, but you're able to extract a lot of energy from it! // Fail to absorb because it's a bad target: // That's too important to absorb. // You can't absorb something you don't have. if ( responseText.contains( "absorb some new knowledge" ) || responseText.contains( "You absorb the" ) || responseText.contains( "absorbing that item" ) ) { String message = "Absorbing " + item.getName(); KoLCharacter.incrementAbsorbs( 1 ); RequestLogger.printLine( message ); RequestLogger.updateSessionLog( message ); ResultProcessor.processResult( item.getNegation() ); KoLCharacter.recalculateAdjustments(); KoLCharacter.updateStatus(); } return true; } public void parseConsumption() { UseItemRequest.parseConsumption( "", true ); } public static final void parseConsumption( final String responseText, final boolean showHTML ) { if ( UseItemRequest.lastItemUsed == null ) { return; } UseItemRequest.lastUpdate = ""; AdventureResult item = UseItemRequest.lastItemUsed; int itemId = item.getItemId(); int count = item.getCount(); AdventureResult helper = UseItemRequest.lastHelperUsed; UseItemRequest.lastItemUsed = null; UseItemRequest.lastHelperUsed = null; if ( itemId == ItemPool.CARD_SLEEVE ) { EquipmentRequest.parseCardSleeve( responseText ); return; } // If you are in Beecore, certain items can't B used // "You are too scared of Bs to xxx that item." if ( responseText.contains( "You don't have the item you're trying to use." ) ) { UseItemRequest.lastUpdate = "You don't have that item."; KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); return; } if ( KoLCharacter.inBeecore() && responseText.contains( "You are too scared of Bs" ) ) { UseItemRequest.lastUpdate = "You are too scared of Bs."; KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); return; } if ( responseText.contains( "be at least level" ) ) { UseItemRequest.lastUpdate = "Item level too high."; KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); return; } if ( responseText.contains( "You may not" ) ) { UseItemRequest.lastUpdate = "Pathed ascension."; KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); return; } if ( responseText.contains( "no|in a special area" ) ) { UseItemRequest.lastUpdate = "Restricted by limitmode."; KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); return; } int consumptionType = UseItemRequest.getConsumptionType( item ); switch ( consumptionType ) { case KoLConstants.CONSUME_DRINK: case KoLConstants.CONSUME_DRINK_HELPER: DrinkItemRequest.parseConsumption( item, helper, responseText ); return; case KoLConstants.CONSUME_EAT: case KoLConstants.CONSUME_FOOD_HELPER: EatItemRequest.parseConsumption( item, helper, responseText ); return; case KoLConstants.CONSUME_SPLEEN: SpleenItemRequest.parseConsumption( item, helper, responseText ); return; } String name = item.getName(); // At least one item - the ice stein - is neither a drink nor a // drink helper, but acts like a drink. Delegate. int inebriety = ConsumablesDatabase.getInebriety( name ); if ( inebriety > 0 ) { DrinkItemRequest.parseConsumption( item, helper, responseText ); return; } int fullness = ConsumablesDatabase.getFullness( name ); if ( fullness > 0 ) { EatItemRequest.parseConsumption( item, helper, responseText ); return; } int spleenHit = ConsumablesDatabase.getSpleenHit( name ); if ( spleenHit > 0 ) { SpleenItemRequest.parseConsumption( item, helper, responseText ); return; } // Check for consumption helpers, which will need to be removed // from inventory if they were successfully used. if ( helper != null ) { // Check for success message, since there are multiple // ways these could fail: boolean success = true; switch ( helper.getItemId() ) { case ItemPool.PUNCHCARD_ATTACK: case ItemPool.PUNCHCARD_REPAIR: case ItemPool.PUNCHCARD_BUFF: case ItemPool.PUNCHCARD_MODIFY: case ItemPool.PUNCHCARD_BUILD: case ItemPool.PUNCHCARD_TARGET: case ItemPool.PUNCHCARD_SELF: case ItemPool.PUNCHCARD_FLOOR: case ItemPool.PUNCHCARD_DRONE: case ItemPool.PUNCHCARD_WALL: case ItemPool.PUNCHCARD_SPHERE: // A voice speaks (for a long time) from the // helmet: if ( responseText.contains( "A tinny voice emerges from the drone" ) ) { // produces sparking, etc. drones - no special handling needed } else if ( !responseText.contains( "(for a long time)" ) ) { success = false; } else { UseItemRequest.parseEVHelmet( responseText ); } break; } if ( !success ) { UseItemRequest.lastUpdate = "Consumption helper failed."; KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); return; } // Remove the consumption helper from inventory. ResultProcessor.processResult( helper.getNegation() ); } if ( ConcoctionDatabase.singleUseCreation( itemId ) != null ) { SingleUseRequest.parseResponse( item, responseText ); UseItemRequest.lastItemUsed = null; return; } if ( ConcoctionDatabase.multiUseCreation( itemId ) != null && count > 1 ) { MultiUseRequest.parseResponse( item, responseText ); UseItemRequest.lastItemUsed = null; return; } // Check for familiar growth - if a familiar is added, // make sure to update the StaticEntity.getClient(). if ( consumptionType == KoLConstants.GROW_FAMILIAR ) { if ( responseText.contains( "You've already got a familiar of that type." ) ) { UseItemRequest.lastUpdate = "You already have that familiar."; KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); return; } if ( responseText.contains( "you glance fearfully at the moons" ) ) { UseItemRequest.lastUpdate = "Can't hatch that familiar in Bad Moon."; KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); return; } if ( responseText.contains( "You don't have a Terrarium to put that in." ) ) { UseItemRequest.lastUpdate = "You don't have a Terrarium yet."; KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); return; } if ( responseText.contains( "Boris has no need for familiars" ) ) { UseItemRequest.lastUpdate = "Boris has no need for familiars!"; KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); return; } // Remove the familiar larva from inventory. ResultProcessor.processResult( item.getNegation() ); FamiliarData familiar = KoLCharacter.addFamiliar( FamiliarDatabase.growFamiliarLarva( itemId ) ); // If this is a previously unknown familiar, punt if ( familiar == null ) { return; } Matcher matcher = UseItemRequest.FAMILIAR_NAME_PATTERN.matcher( responseText ); if ( matcher.find() ) { familiar.setName( matcher.group(1) ); } // Don't bother showing the result // UseItemRequest.showItemUsage( showHTML, responseText ); return; } if ( responseText.contains( "That item isn't usable in quantity" ) ) { int attrs = ItemDatabase.getAttributes( itemId ); if ( ( attrs & ItemDatabase.ATTR_MULTIPLE ) == 0 ) { // Multi-use was attempted and failed, but the request was not generated by KoLmafia // because KoLmafia already knows that it cannot be multi-used return; } KoLmafia.updateDisplay( MafiaState.ERROR, "Internal data error: item incorrectly flagged as multi-usable." ); return; } // Note that there is at least one item (memory of amino acids) // that can fail with a "too full" message, even though it's // not a food. if ( responseText.contains( "too full" ) ) { UseItemRequest.lastUpdate = "Consumption limit reached."; KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); return; } switch ( consumptionType ) { case KoLConstants.CONSUME_FOOD_HELPER: case KoLConstants.CONSUME_DRINK_HELPER: // Consumption helpers are removed above when you // successfully eat or drink. case KoLConstants.NO_CONSUME: return; case KoLConstants.MESSAGE_DISPLAY: UseItemRequest.showItemUsage( showHTML, responseText ); return; } Matcher matcher; // Perform item-specific processing // If the item is not consumed, either because we detect that // from the responseText or it is always reusable, return from // this method. Otherwise, break from the switch and it will be // removed from inventory afterwards switch ( itemId ) { case ItemPool.LOATHING_LEGION_UNIVERSAL_SCREWDRIVER: { // You jam your screwdriver into your xxx and pry it // apart. if ( UseItemRequest.lastUntinker != null && responseText.contains( "You jam your screwdriver" ) ) { ResultProcessor.processResult( UseItemRequest.lastUntinker.getNegation() ); UseItemRequest.lastUntinker = null; return; } } // Fall through case ItemPool.LOATHING_LEGION_KNIFE: case ItemPool.LOATHING_LEGION_MANY_PURPOSE_HOOK: case ItemPool.LOATHING_LEGION_MOONDIAL: case ItemPool.LOATHING_LEGION_NECKTIE: case ItemPool.LOATHING_LEGION_ELECTRIC_KNIFE: case ItemPool.LOATHING_LEGION_CORKSCREW: case ItemPool.LOATHING_LEGION_CAN_OPENER: case ItemPool.LOATHING_LEGION_CHAINSAW: case ItemPool.LOATHING_LEGION_ROLLERBLADES: case ItemPool.LOATHING_LEGION_FLAMETHROWER: case ItemPool.LOATHING_LEGION_TATTOO_NEEDLE: case ItemPool.LOATHING_LEGION_DEFIBRILLATOR: case ItemPool.LOATHING_LEGION_DOUBLE_PRISM: case ItemPool.LOATHING_LEGION_TAPE_MEASURE: case ItemPool.LOATHING_LEGION_KITCHEN_SINK: case ItemPool.LOATHING_LEGION_ABACUS: case ItemPool.LOATHING_LEGION_HELICOPTER: case ItemPool.LOATHING_LEGION_PIZZA_STONE: case ItemPool.LOATHING_LEGION_JACKHAMMER: case ItemPool.LOATHING_LEGION_HAMMER: // You spend a little while messing with all of the // latches and clasps and little bits of metal, and end // up with ... if ( responseText.contains( "latches and clasps" ) ) { // These are marked as reusable, but they go away when you fold them. ResultProcessor.processResult( item.getNegation() ); } return; case ItemPool.WHAT_CARD: case ItemPool.WHEN_CARD: case ItemPool.WHO_CARD: case ItemPool.WHERE_CARD: { if ( !responseText.contains( "Answer:" ) ) { return; } Matcher card_matcher = UseItemRequest.CARD_PATTERN.matcher( responseText ); if ( card_matcher.find() ) { String message = "Trivia card #" + card_matcher.group( 1 ) + "/" + card_matcher.group( 2 ) + ":"; RequestLogger.printLine( message ); RequestLogger.updateSessionLog( message ); } Matcher QA_matcher = UseItemRequest.QA_PATTERN.matcher( responseText ); while ( QA_matcher.find() ) { String message = QA_matcher.group( 1 ) + ": " + QA_matcher.group( 2 ); RequestLogger.printLine( message ); RequestLogger.updateSessionLog( message ); } break; } case ItemPool.LEGENDARY_BEAT: Preferences.setBoolean( "_legendaryBeat", true ); return; case ItemPool.JACKING_MAP: // The <fruit> disappears into the tube and begins // bouncing around noisily inside the machine. // The <fruit> is sucked into the tube, displacing the // <fruit> that was bouncing around in the machine. You // wonder where it went. if ( UseItemRequest.lastFruit != null && responseText.contains( "into the tube" ) ) { ResultProcessor.processResult( UseItemRequest.lastFruit.getNegation() ); UseItemRequest.lastFruit = null; } return; // If it's a gift package, get the inner message case ItemPool.GIFT1: case ItemPool.GIFT2: case ItemPool.GIFT3: case ItemPool.GIFT4: case ItemPool.GIFT5: case ItemPool.GIFT6: case ItemPool.GIFT7: case ItemPool.GIFT8: case ItemPool.GIFT9: case ItemPool.GIFT10: case ItemPool.GIFT11: case ItemPool.GIFTV: case ItemPool.GIFTR: case ItemPool.GIFTW: case ItemPool.GIFTH: // "You can't receive things from other players // right now." if ( responseText.contains( "You can't receive things" ) ) { UseItemRequest.lastUpdate = "You can't open that package yet."; KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); return; } if ( showHTML ) { UseItemRequest.showItemUsage( true, responseText ); } break; case ItemPool.DANCE_CARD: TurnCounter.stopCounting( "Dance Card" ); TurnCounter.startCounting( 3, "Dance Card loc=395", "guildapp.gif" ); break; case ItemPool.TOASTER: // You push the lever and are rewarded with toast Preferences.setBoolean( "_toastSummoned", true ); return; case ItemPool.GATES_SCROLL: // Oh, and don't forget <font color=purple><b><i>eJyu3</i></b></font>. It's important. Matcher purpleWordMatcher = UseItemRequest.PURPLE_WORD_PATTERN.matcher( responseText ); if ( purpleWordMatcher.find() ) { String purpleWord = purpleWordMatcher.group( 1 ); String message = "64735 Scroll Purple Word found: " + purpleWord + " in clan " + ClanManager.getClanName( false ) + "."; RequestLogger.printLine( "<font color=\"blue\">" + message + "</font>" ); RequestLogger.updateSessionLog( message ); } // You can only use a 64735 scroll if you have the // original dictionary in your inventory // "Even though your name isn't Lee, you're flattered // and hand over your dictionary." if ( !responseText.contains( "you're flattered" ) ) { return; } ResultProcessor.processResult( ItemPool.get( ItemPool.DICTIONARY, -1 ) ); QuestDatabase.setQuestProgress( Quest.TOPPING, QuestDatabase.FINISHED ); QuestDatabase.setQuestProgress( Quest.LOL, QuestDatabase.FINISHED ); break; case ItemPool.ELITE_SCROLL: // "The UB3r 31337 HaX0R stands before you." if ( responseText.contains( "The UB3r 31337 HaX0R stands before you." ) ) { if ( count > 1 ) { (UseItemRequest.getInstance( item.getInstance( count - 1 ) )).run(); item = item.getInstance( 1 ); } } break; case ItemPool.HERMIT_SCRIPT: HermitRequest.hackHermit(); break; case ItemPool.SPARKLER: case ItemPool.SNAKE: case ItemPool.M282: // "You've already celebrated the Fourth of Bor, and // now it's time to get back to work." // "Sorry, but these particular fireworks are illegal // on any day other than the Fourth of Bor. And the law // is a worthy institution, and you should respect and // obey it, no matter what." if ( responseText.contains( "back to work" ) || responseText.contains( "fireworks are illegal" ) ) { return; } break; case ItemPool.GONG: // "You try to bang the gong, but the mallet keeps // falling out of your hand. Maybe you should try it // later, when you've sobered up a little." // "You don't have time to bang a gong. Nor do you have // time to get it on, or to get it on." if ( responseText.contains( "sobered up a little" ) || responseText.contains( "don't have time to bang" ) ) { UseItemRequest.lastUpdate = "Insufficient adventures or sobriety to use a gong."; KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); return; } // "You're already in the middle of a journey of reincarnation." if ( responseText.contains( "middle of a journey of reincarnation" ) ) { if ( UseItemRequest.retrying || KoLConstants.activeEffects.contains( EffectPool.get( EffectPool.FORM_OF_BIRD ) ) || KoLConstants.activeEffects.contains( EffectPool.get( EffectPool.SHAPE_OF_MOLE ) ) || KoLConstants.activeEffects.contains( EffectPool.get( EffectPool.FORM_OF_ROACH ) ) ) { UseItemRequest.lastUpdate = "You're still under a gong effect."; KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); return; // can't use another gong yet } try { UseItemRequest.retrying = true; // prevent recursing more than once int adv = Preferences.getInteger( "welcomeBackAdv" ); if ( adv <= 0 ) { adv = AdventurePool.NOOB_CAVE; } KoLAdventure req = AdventureDatabase.getAdventureByURL( "adventure.php?snarfblat=" + adv ); // Must do some trickery here to // prevent the adventure location from // being changed, and the conditions // reset. String next = Preferences.getString( "nextAdventure" ); KoLAdventure.setNextAdventure( req ); req.overrideAdventuresUsed( 0 ); // don't trigger counters RequestThread.postRequest( req ); req.overrideAdventuresUsed( -1 ); KoLAdventure.setNextAdventure( next ); (UseItemRequest.getInstance( item )).run(); } finally { UseItemRequest.retrying = false; } } // We deduct the gong when we get the intro choice // adventure: The Gong Has Been Bung return; case ItemPool.ENCHANTED_BEAN: // There are three possibilities: // If you haven't been give the quest, "you can't find // anywhere that looks like a good place to plant the // bean" and you're told to "wait until later" // If you've already planted one, "There's already a // beanstalk in the Nearby Plains." In either case, the // bean is not consumed. // Otherwise, "it immediately grows into an enormous // beanstalk". if ( !responseText.contains( "grows into an enormous beanstalk" ) ) { return; } QuestDatabase.setQuestProgress( Quest.GARBAGE, "step1" ); break; case ItemPool.LIBRARY_CARD: // If you've already used a library card today, it is // not consumed. // "You head back to the library, but can't find // anything you feel like reading. You skim a few // celebrity-gossip magazines, and end up feeling kind // of dirty." Preferences.setBoolean( "libraryCardUsed", true ); if ( !responseText.contains( "feeling kind of dirty" ) ) { return; } break; case ItemPool.HEY_DEZE_MAP: // "Your song has pleased me greatly. I will reward you // with some of my crazy imps, to do your bidding." if ( !responseText.contains( "pleased me greatly" ) ) { UseItemRequest.lastUpdate = "Your music was inadequate."; KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); return; } break; case ItemPool.GIANT_CASTLE_MAP: // "I'm sorry, adventurer, but the Sorceress is in // another castle!" if ( !responseText.contains( "Sorceress is in another castle" ) ) { UseItemRequest.lastUpdate = "You couldn't make it all the way to the back door."; KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); return; } break; case ItemPool.DRASTIC_HEALING: // If a scroll of drastic healing was used and didn't // crumble, it is not consumed ResultProcessor.processResult( new AdventureResult( AdventureResult.HP, KoLCharacter.getMaximumHP() ) ); if ( !responseText.contains( "crumble" ) ) { return; } break; case ItemPool.TEARS: KoLConstants.activeEffects.remove( KoLAdventure.BEATEN_UP ); break; case ItemPool.ANTIDOTE: // You're unpoisoned -- don't waste the anti-anti-antidote. if ( responseText.contains( "don't waste the anti" ) ) { return; } break; case ItemPool.TBONE_KEY: if ( !InventoryManager.hasItem( ItemPool.LOCKED_LOCKER ) ) { return; } ResultProcessor.processItem( ItemPool.LOCKED_LOCKER, -1 ); break; case ItemPool.KETCHUP_HOUND: // Successfully using a ketchup hound uses up the Hey // Deze nuts and pagoda plan. if ( responseText.contains( "pagoda" ) ) { ResultProcessor.processItem( ItemPool.HEY_DEZE_NUTS, -1 ); ResultProcessor.processItem( ItemPool.PAGODA_PLANS, -1 ); CampgroundRequest.setCampgroundItem( ItemPool.PAGODA_PLANS, 1 ); } // The ketchup hound does not go away... return; case ItemPool.DOLPHIN_KING_MAP: // "You follow the Dolphin King's map to the bottom of // the sea, and find his glorious treasure." if ( !responseText.contains( "find his glorious treasure" ) ) { UseItemRequest.lastUpdate = "You don't have everything you need."; KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); return; } break; case ItemPool.SLUG_LORD_MAP: // "You make your way to the deepest part of the tank, // and find a chest engraved with the initials S. L." if ( !responseText.contains( "deepest part of the tank" ) ) { UseItemRequest.lastUpdate = "You don't have everything you need."; KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); return; } break; case ItemPool.DR_HOBO_MAP: // "You place it atop the Altar, and grab the Scalpel // at the exact same moment." if ( !responseText.contains( "exact same moment" ) ) { UseItemRequest.lastUpdate = "You don't have everything you need."; KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); return; } // Using the map consumes an asparagus knife ResultProcessor.processItem( ItemPool.ASPARAGUS_KNIFE, -1 ); break; case ItemPool.SHOPPING_LIST: // "Since you've already built a bitchin' meatcar, you // wad the shopping list up and throw it away." UseItemRequest.showItemUsage( showHTML, responseText ); if ( !responseText.contains( "throw it away" ) ) { return; } break; case ItemPool.COBBS_KNOB_MAP: // "You memorize the location of the door, then eat // both the map and the encryption key." if ( !responseText.contains( "memorize the location" ) ) { UseItemRequest.lastUpdate = "You don't have everything you need."; KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); return; } // Using the map consumes the encryption key ResultProcessor.processItem( ItemPool.ENCRYPTION_KEY, -1 ); QuestDatabase.setQuestProgress( Quest.GOBLIN, "step1" ); break; case ItemPool.SPOOKY_MAP: if ( !InventoryManager.hasItem( ItemPool.SPOOKY_SAPLING ) || !InventoryManager.hasItem( ItemPool.SPOOKY_FERTILIZER ) ) { UseItemRequest.lastUpdate = "You don't have everything you need."; KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); return; } ResultProcessor.processItem( ItemPool.SPOOKY_SAPLING, -1 ); ResultProcessor.processItem( ItemPool.SPOOKY_FERTILIZER, -1 ); Preferences.setInteger( "lastTempleUnlock", KoLCharacter.getAscensions() ); // If quest Gotta Worship Them All is started, this completes step 1 if ( QuestDatabase.isQuestLaterThan( Quest.WORSHIP, QuestDatabase.UNSTARTED ) ) { QuestDatabase.setQuestProgress( Quest.WORSHIP, "step1" ); } break; case ItemPool.CARONCH_MAP: // The item is only consumed once you turn in // the nasty booty. That's handled elsewhere. return; case ItemPool.FRATHOUSE_BLUEPRINTS: // The item is only consumed once you turn in the // dentures. That's handled elsewhere. return; case ItemPool.DINGHY_PLANS: // "You need some planks to build the dinghy." if ( !InventoryManager.hasItem( ItemPool.DINGY_PLANKS ) ) { UseItemRequest.lastUpdate = "You need some dingy planks."; KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); return; } ResultProcessor.processItem( ItemPool.DINGY_PLANKS, -1 ); break; case ItemPool.MORTAR_DISSOLVING_RECIPE: if ( responseText.contains( "Screw this scavenger hunt crap" ) ) { Preferences.setString( "spookyravenRecipeUsed", "with_glasses" ); } else if ( !Preferences.getString( "spookyravenRecipeUsed" ).equals( "with_glasses" ) ) { Preferences.setString( "spookyravenRecipeUsed", "no_glasses" ); } break; case ItemPool.FENG_SHUI: if ( !InventoryManager.hasItem( ItemPool.FOUNTAIN ) || !InventoryManager.hasItem( ItemPool.WINDCHIMES ) ) { break; } ResultProcessor.processItem( ItemPool.FOUNTAIN, -1 ); ResultProcessor.processItem( ItemPool.WINDCHIMES, -1 ); CampgroundRequest.setCampgroundItem( ItemPool.FENG_SHUI, 1 ); break; case ItemPool.WARM_SUBJECT: // The first time you use Warm Subject gift // certificates when you have the Torso Awaregness // skill consumes only one, even if you tried to // multi-use the item. // "You go to Warm Subject and browse the shirts for a // while. You find one that you wouldn't mind wearing // ironically. There seems to be only one in the store, // though." if ( responseText.contains( "ironically" ) ) { if ( count > 1 ) { (UseItemRequest.getInstance( item.getInstance( count - 1 ) )).run(); item = item.getInstance( 1 ); } } break; case ItemPool.MINING_OIL: case ItemPool.TAINTED_MINING_OIL: if ( responseText.contains( "Limiting to 100. Sorry" ) ) { if ( count > 100 ) { (UseItemRequest.getInstance( item.getInstance( count - 100 ) )).run(); item = item.getInstance( 1 ); } } break; case ItemPool.PURPLE_SNOWCONE: case ItemPool.GREEN_SNOWCONE: case ItemPool.ORANGE_SNOWCONE: case ItemPool.RED_SNOWCONE: case ItemPool.BLUE_SNOWCONE: case ItemPool.BLACK_SNOWCONE: // "Your mouth is still cold from the last snowcone you // ate. Try again later." if ( responseText.contains( "still cold" ) ) { UseItemRequest.lastUpdate = "Your mouth is too cold."; KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); return; } break; case ItemPool.POTION_OF_PUISSANCE: case ItemPool.POTION_OF_PERSPICACITY: case ItemPool.POTION_OF_PULCHRITUDE: case ItemPool.POTION_OF_PERCEPTION: case ItemPool.POTION_OF_PROFICIENCY: // "You're already under the influence of a // high-pressure sauce potion. If you took this one, // you'd explode. And not in a good way." if ( responseText.contains( "you'd explode" ) ) { UseItemRequest.lastUpdate = "You're already under pressure."; KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); return; } break; case ItemPool.BLUE_CUPCAKE: case ItemPool.GREEN_CUPCAKE: case ItemPool.ORANGE_CUPCAKE: case ItemPool.PURPLE_CUPCAKE: case ItemPool.PINK_CUPCAKE: // "Your stomach is still a little queasy from // digesting a cupcake that may or may not exist in // this dimension. You really don't feel like eating // another one just now." if ( responseText.contains( "a little queasy" ) ) { UseItemRequest.lastUpdate = "Your stomach is too queasy."; KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); return; } break; case ItemPool.VAGUE_AMBIGUITY: case ItemPool.SMOLDERING_PASSION: case ItemPool.ICY_REVENGE: case ItemPool.SUGARY_CUTENESS: case ItemPool.DISTURBING_OBSESSION: case ItemPool.NAUGHTY_INNUENDO: // "Your heart can't take another love song so soon // after the last one. The conflicting emotions would // drive you totally mad." if ( responseText.contains( "conflicting emotions" ) ) { UseItemRequest.lastUpdate = "Your heart is already filled with emotions."; KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); return; } break; case ItemPool.ROLLING_PIN: // Rolling pins remove dough from your inventory. // They are not consumed by being used ResultProcessor.processItem( ItemPool.DOUGH, 0 - InventoryManager.getCount( ItemPool.DOUGH ) ); return; case ItemPool.UNROLLING_PIN: // Unrolling pins remove flat dough from your inventory. // They are not consumed by being used ResultProcessor.processItem( ItemPool.FLAT_DOUGH, 0 - InventoryManager.getCount( ItemPool.FLAT_DOUGH ) ); return; case ItemPool.EXPRESS_CARD: // You feel charged up! if ( responseText.contains( "charged up" ) ) { Preferences.setInteger( "_zapCount", 0 ); } return; case ItemPool.PLUS_SIGN: // "Following The Oracle's advice, you treat the plus // sign as a book, and read it." if ( !responseText.contains( "you treat the plus sign as a book" ) ) { UseItemRequest.lastUpdate = "You don't know how to use it."; KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); return; } // Various punctuation mark items are replaced by their // identified versions. The new items will be detected // by result parsing, but we need to get rid of the old. for ( int i = 4552; i <= 4558; ++i ) { AdventureResult punc = ItemPool.get( i, 1 ); int pcount = punc.getCount( KoLConstants.inventory ); if ( pcount > 0 ) { ResultProcessor.processResult( punc.getInstance( -pcount ) ); } } break; case ItemPool.OVEN: KoLCharacter.setOven( true ); CampgroundRequest.setCampgroundItem( itemId, 1 ); break; case ItemPool.RANGE: KoLCharacter.setRange( true ); CampgroundRequest.setCampgroundItem( itemId, 1 ); break; case ItemPool.CLOCKWORK_CHEF: CampgroundRequest.removeCampgroundItem( ItemPool.get( ItemPool.CHEF, 1 ) ); // Fall through case ItemPool.CHEF: KoLCharacter.setChef( true ); Preferences.setInteger( "chefTurnsUsed", 0 ); CampgroundRequest.setCampgroundItem( itemId, 1 ); break; case ItemPool.SHAKER: KoLCharacter.setShaker( true ); CampgroundRequest.setCampgroundItem( itemId, 1 ); return; case ItemPool.COCKTAIL_KIT: KoLCharacter.setCocktailKit( true ); CampgroundRequest.setCampgroundItem( itemId, 1 ); break; case ItemPool.CLOCKWORK_BARTENDER: CampgroundRequest.removeCampgroundItem( ItemPool.get( ItemPool.BARTENDER, 1 ) ); // Fall through case ItemPool.BARTENDER: KoLCharacter.setBartender( true ); Preferences.setInteger( "bartenderTurnsUsed", 0 ); CampgroundRequest.setCampgroundItem( itemId, 1 ); break; case ItemPool.SUSHI_ROLLING_MAT: KoLCharacter.setSushiMat( true ); CampgroundRequest.setCampgroundItem( itemId, 1 ); break; // Tomes case ItemPool.SNOWCONE_BOOK: case ItemPool.STICKER_BOOK: case ItemPool.SUGAR_BOOK: case ItemPool.CLIP_ART_BOOK: case ItemPool.RAD_LIB_BOOK: case ItemPool.SMITH_BOOK: // Grimoires case ItemPool.HILARIOUS_BOOK: case ItemPool.TASTEFUL_BOOK: case ItemPool.CARD_GAME_BOOK: case ItemPool.GEEKY_BOOK: case ItemPool.CONFISCATOR_BOOK: // Librams case ItemPool.CANDY_BOOK: case ItemPool.DIVINE_BOOK: case ItemPool.LOVE_BOOK: case ItemPool.BRICKO_BOOK: case ItemPool.DICE_BOOK: case ItemPool.RESOLUTION_BOOK: // Others case ItemPool.JEWELRY_BOOK: case ItemPool.RAINBOWS_GRAVITY: case ItemPool.RAGE_GLAND: case ItemPool.KISSIN_COUSINS: case ItemPool.TALES_FROM_THE_FIRESIDE: case ItemPool.BLIZZARDS_I_HAVE_DIED_IN: case ItemPool.MAXING_RELAXING: case ItemPool.BIDDY_CRACKERS_COOKBOOK: case ItemPool.TRAVELS_WITH_JERRY: case ItemPool.LET_ME_BE: case ItemPool.ASLEEP_IN_THE_CEMETERY: case ItemPool.SUMMER_NIGHTS: case ItemPool.SENSUAL_MASSAGE_FOR_CREEPS: case ItemPool.RICHIE_THINGFINDER: case ItemPool.MEDLEY_OF_DIVERSITY: case ItemPool.EXPLOSIVE_ETUDE: case ItemPool.CHORALE_OF_COMPANIONSHIP: case ItemPool.PRELUDE_OF_PRECISION: case ItemPool.HODGMAN_JOURNAL_1: case ItemPool.HODGMAN_JOURNAL_2: case ItemPool.HODGMAN_JOURNAL_3: case ItemPool.HODGMAN_JOURNAL_4: case ItemPool.CRIMBO_CAROL_V1: case ItemPool.CRIMBO_CAROL_V1_USED: case ItemPool.CRIMBO_CAROL_V2: case ItemPool.CRIMBO_CAROL_V2_USED: case ItemPool.CRIMBO_CAROL_V3: case ItemPool.CRIMBO_CAROL_V3_USED: case ItemPool.CRIMBO_CAROL_V4: case ItemPool.CRIMBO_CAROL_V4_USED: case ItemPool.CRIMBO_CAROL_V5: case ItemPool.CRIMBO_CAROL_V5_USED: case ItemPool.CRIMBO_CAROL_V6: case ItemPool.CRIMBO_CAROL_V6_USED: case ItemPool.CRIMBO_CANDY_COOKBOOK: case ItemPool.SLAPFIGHTING_BOOK: case ItemPool.SLAPFIGHTING_BOOK_USED: case ItemPool.UNCLE_ROMULUS: case ItemPool.UNCLE_ROMULUS_USED: case ItemPool.SNAKE_CHARMING_BOOK: case ItemPool.SNAKE_CHARMING_BOOK_USED: case ItemPool.ZU_MANNKASE_DIENEN: case ItemPool.ZU_MANNKASE_DIENEN_USED: case ItemPool.DYNAMITE_SUPERMAN_JONES: case ItemPool.DYNAMITE_SUPERMAN_JONES_USED: case ItemPool.INIGO_BOOK: case ItemPool.INIGO_BOOK_USED: case ItemPool.BLACK_HYMNAL: case ItemPool.ELLSBURY_BOOK: case ItemPool.ELLSBURY_BOOK_USED: case ItemPool.UNEARTHED_METEOROID: case ItemPool.KANSAS_TOYMAKER: case ItemPool.KANSAS_TOYMAKER_USED: case ItemPool.WASSAILING_BOOK: case ItemPool.WASSAILING_BOOK_USED: case ItemPool.CRIMBCO_MANUAL_1: case ItemPool.CRIMBCO_MANUAL_1_USED: case ItemPool.CRIMBCO_MANUAL_2: case ItemPool.CRIMBCO_MANUAL_2_USED: case ItemPool.CRIMBCO_MANUAL_3: case ItemPool.CRIMBCO_MANUAL_3_USED: case ItemPool.CRIMBCO_MANUAL_4: case ItemPool.CRIMBCO_MANUAL_4_USED: case ItemPool.CRIMBCO_MANUAL_5: case ItemPool.CRIMBCO_MANUAL_5_USED: case ItemPool.SKELETON_BOOK: case ItemPool.SKELETON_BOOK_USED: case ItemPool.NECBRONOMICON: case ItemPool.NECBRONOMICON_USED: case ItemPool.PLANT_BOOK: case ItemPool.GHOST_BOOK: case ItemPool.TATTLE_BOOK: case ItemPool.NOTE_FROM_CLANCY: case ItemPool.GRUDGE_BOOK: case ItemPool.JERK_BOOK: case ItemPool.HJODOR_GUIDE: case ItemPool.HJODOR_GUIDE_USED: case ItemPool.INFURIATING_SILENCE_RECORD: case ItemPool.INFURIATING_SILENCE_RECORD_USED: case ItemPool.TRANQUIL_SILENCE_RECORD: case ItemPool.TRANQUIL_SILENCE_RECORD_USED: case ItemPool.MENACING_SILENCE_RECORD: case ItemPool.MENACING_SILENCE_RECORD_USED: case ItemPool.WALBERG_BOOK: case ItemPool.OCELOT_BOOK: case ItemPool.DRESCHER_BOOK: case ItemPool.DECODED_CULT_DOCUMENTS: case ItemPool.WARBEAR_METALWORKING_PRIMER: case ItemPool.WARBEAR_METALWORKING_PRIMER_USED: case ItemPool.WARBEAR_EMPATHY_CHIP: case ItemPool.WARBEAR_EMPATHY_CHIP_USED: case ItemPool.OFFENSIVE_JOKE_BOOK: case ItemPool.COOKING_WITH_GREASE_BOOK: case ItemPool.DINER_HANDBOOK: case ItemPool.ALIEN_SOURCE_CODE: case ItemPool.ALIEN_SOURCE_CODE_USED: case ItemPool.BLACK_BARTS_BOOTY: case ItemPool.HYPERSANE_BOOK: case ItemPool.INTIMIDATING_MIEN_BOOK: case ItemPool.HOLIDAY_FUN_BOOK: case ItemPool.ROM_RAPID_PROTOTYPING: case ItemPool.ROM_RAPID_PROTOTYPING_DIRTY: case ItemPool.ROM_MATHMATICAL_PRECISION: case ItemPool.ROM_MATHMATICAL_PRECISION_DIRTY: case ItemPool.ROM_RUTHLESS_EFFICIENCY: case ItemPool.ROM_RUTHLESS_EFFICIENCY_DIRTY: case ItemPool.ANTAGONISTIC_SNOWMAN_KIT: case ItemPool.SPELUNKER_FORTUNE: case ItemPool.SPELUNKER_FORTUNE_USED: case ItemPool.DINSEY_GUIDE_BOOK: case ItemPool.TRASH_MEMOIR_BOOK: case ItemPool.DINSEY_MAINTENANCE_MANUAL: case ItemPool.DINSEY_AN_AFTERLIFE: case ItemPool.MAP_TO_KOKOMO: case ItemPool.ESSENCE_OF_BEAR: case ItemPool.MANUAL_OF_NUMBEROLOGY: case ItemPool.LAVA_MINERS_DAUGHTER: case ItemPool.PSYCHO_FROM_THE_HEAT: case ItemPool.THE_FIREGATE_TAPES: case ItemPool.COLD_WEATHER_BARTENDER_GUIDE: case ItemPool.TO_BUILD_AN_IGLOO: case ItemPool.CHILL_OF_THE_WILD: case ItemPool.COLD_FANG: case ItemPool.SCROLL_SHATTERING_PUNCH: case ItemPool.SCROLL_SNOKEBOMB: case ItemPool.SCROLL_SHIVERING_MONKEY: case ItemPool.ROM_OF_OPTIMALITY: case ItemPool.WESTERN_BOOK_BRAGGADOCCIO: case ItemPool.WESTERN_BOOK_HELL: case ItemPool.WESTERN_BOOK_LOOK: case ItemPool.TROUT_FISHING_IN_LOATHING: case ItemPool.COMMUNISM_BOOK: case ItemPool.COMMUNISM_BOOK_USED: case ItemPool.BRAIN_TRAINER_GAME: case ItemPool.LASER_EYE_SURGERY_KIT: case ItemPool.MY_LIFE_OF_CRIME_BOOK: case ItemPool.POP_ART_BOOK: case ItemPool.NO_HATS_BOOK: case ItemPool.VIGILANTE_BOOK: case ItemPool.LUMP_STACKING_BOOK: case ItemPool.RETHINKING_CANDY_BOOK: case ItemPool.ELDRITCH_TINCTURE: case ItemPool.ELDRITCH_TINCTURE_DEPLETED: case ItemPool.SPACE_PIRATE_ASTROGATION_HANDBOOK: case ItemPool.NON_EUCLIDEAN_FINANCE: case ItemPool.PEEK_A_BOO: { // You insert the ROM in to your... ROM receptacle and // absorb the knowledge of optimality. You suspect you // can now toggle your optimality at will (from the // skills page)! if ( !responseText.contains( "You acquire a skill" ) && !responseText.contains( "place the Grimoire on the bookshelf" ) && !responseText.contains( "absorb the knowledge of optimality" ) && !responseText.contains( "feel beary" ) && !responseText.contains( "knew how to maximize" ) && !responseText.contains( "additional carrot" ) && !responseText.contains( "larynx become even more pirate" ) && !responseText.contains( "Beleven" ) ) { UseItemRequest.lastUpdate = "You can't learn that skill."; KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); return; } String skill = UseItemRequest.itemToSkill( itemId ); if ( skill != null ) { ResponseTextParser.learnSkill( skill ); } break; } case ItemPool.WESTERN_SLANG_VOL_1: case ItemPool.WESTERN_SLANG_VOL_2: case ItemPool.WESTERN_SLANG_VOL_3: // You memorize all of the violence-related slang terms in the book. // // Advanced Cowpuncher skills have been unlocked in the // Avatar of West of Loathing Challenge Path. // // You memorize all of the food-related slang terms in the book. // // Advanced Beanslinger skills have been unlocked in // the Avatar of West of Loathing Challenge Path. // // You memorize all of the slang terms about scams and con artists. // // Advanced Snake Oiler skills have been unlocked in // the Avatar of West of Loathing Challenge Path. if ( !responseText.contains( "skills have been unlocked" ) ) { UseItemRequest.lastUpdate = "You've already read that book."; KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); return; } // Do we need to track this? break; case ItemPool.CHATEAU_ROOM_KEY: Preferences.setBoolean( "chateauAvailable", true ); // You hike up the mountain to Chateau Mantegna and try // the key in various doors until you find the one it // unlocks. It's quite a nice room! if ( !responseText.contains( "you find the one it unlocks" ) ) { UseItemRequest.lastUpdate = "You've already have a room at the Chateau Mantegna."; KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); return; } break; case ItemPool.GINGERBREAD_CITY: Preferences.setBoolean( "gingerbreadCityAvailable", true ); if ( !responseText.contains( "build a gingerbread city" ) ) { return; } break; case ItemPool.COUNTERFEIT_CITY: if ( responseText.contains( "already a gingerbread city" ) ) { // If you already have access it is not consumed return; } Preferences.setBoolean( "_gingerbreadCityToday", true ); break; case ItemPool.TELEGRAPH_OFFICE_DEED: Preferences.setBoolean( "telegraphOfficeAvailable", true ); // You find a vacant lot on the Right Side of the // Tracks and a loophole in the local tax code that // lets you purchase it for negative 19 Meat. Then you // build a telegraph office out of reclaimed materials // from the city dump, spend 19 Meat on a can of paint, // and before you know it you're in the telegraph // business! if ( !responseText.contains( "You find a vacant lot" ) ) { UseItemRequest.lastUpdate = "You've already opened a telegraph office."; KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); return; } break; case ItemPool.HEART_SHAPED_CRATE: Preferences.setBoolean( "loveTunnelAvailable", true ); if ( !responseText.contains( "You wander" ) ) { UseItemRequest.lastUpdate = "You've already opened a Tunnel of L.O.V.E."; KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); return; } break; case ItemPool.BEAUTIFUL_RAINBOW: { if ( responseText.contains( "don't have the item you're trying to use" ) ) { UseItemRequest.lastUpdate = "You've haven't got that item."; KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); return; } if ( !responseText.contains( "eaten the entire thing" ) ) { UseItemRequest.lastUpdate = "You've already maxed out Belch The Rainbow."; KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); Preferences.setInteger( "skillLevel117", 11 ); return; } ResponseTextParser.learnSkill( "Belch The Rainbow" ); break; } case ItemPool.THUNDER_THIGH: case ItemPool.AQUA_BRAIN: case ItemPool.LIGHTNING_MILK: { // You can't learn anything else from this, so you just throw it away. if ( !responseText.contains( "you just throw it away" ) ) { return; } break; } case ItemPool.OLFACTION_BOOK: { if ( !responseText.contains( "smell has been elevated to a superhuman level" ) ) { UseItemRequest.lastUpdate = "You can't learn that skill."; KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); return; } ResponseTextParser.learnSkill( "Transcendent Olfaction" ); break; } case ItemPool.TEACHINGS_OF_THE_FIST: // You learn a different skill from each scroll Preferences.increment( "fistSkillsKnown", 1 ); ResponseTextParser.learnSkillFromResponse( responseText ); break; case ItemPool.SLIME_SOAKED_HYPOPHYSIS: case ItemPool.SLIME_SOAKED_BRAIN: case ItemPool.SLIME_SOAKED_SWEAT_GLAND: { for ( int i = 46; i <= 48; ++i ) { GenericRequest req = new GenericRequest( "desc_skill.php?whichskill=" + i + "&self=true" ); RequestThread.postRequest( req ); } // You can learn the appropriate skill up to 10 times. // What does it say if you try to use the 11th? if ( !responseText.contains( "You gain a skill" ) ) { // Item may be consumed even if you already have the skill break; } String skill = UseItemRequest.itemToSkill( item.getItemId() ); ResponseTextParser.learnSkill( skill ); break; } case ItemPool.TELESCOPE: // We've added or upgraded our telescope KoLCharacter.setTelescope( true ); // Look through it to check number of upgrades Preferences.setInteger( "lastTelescopeReset", -1 ); KoLCharacter.checkTelescope(); break; case ItemPool.ASTRAL_MUSHROOM: // "You eat the mushroom, and are suddenly engulfed in // a whirling maelstrom of colors and sensations as // your awareness is whisked away to some strange // alternate dimension. Who would have thought that a // glowing, ethereal mushroom could have that kind of // effect?" // "Whoo, man, lemme tell you, you don't need to be // eating another one of those just now, okay?" if ( !responseText.contains( "whirling maelstrom" ) ) { return; } break; case ItemPool.WORKYTIME_TEA: // You're not quite bored enough to drink that much tea. if ( responseText.contains( "not quite bored enough" ) ) { UseItemRequest.lastUpdate = "You're not bored enough to drink that much tea."; KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); return; } break; case ItemPool.ABSINTHE: // "You drink the bottle of absinthe. It tastes like // licorice, pain, and green. Your head begins to ache // and you see a green glow in the general direction of // the distant woods." // "No way are you gonna drink another one of those // until the last one wears off." if ( !responseText.contains( "licorice" ) ) { return; } break; case ItemPool.DUSTY_ANIMAL_SKULL: // The magic that had previously animated the animals kicks back // in, and it stands up shakily and looks at you. "Graaangh?" if ( !responseText.contains( "Graaangh?" ) ) { UseItemRequest.lastUpdate = "You're missing some parts."; KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); return; } // Remove the other 98 bones for ( int i = 1802; i < 1900; ++i ) { ResultProcessor.removeItem( i ); } break; case ItemPool.DRUM_MACHINE: // "And dammit, your hooks were still on there! Oh well." if ( responseText.contains( "hooks were still on" ) ) { if ( KoLCharacter.hasEquipped( ItemPool.WORM_RIDING_HOOKS, EquipmentManager.WEAPON ) ) { // You lose your weapon EquipmentManager.discardEquipment( ItemPool.WORM_RIDING_HOOKS ); KoLmafia.updateDisplay( "Don't forget to equip a weapon!" ); } else { // You lose your hooks ResultProcessor.removeItem( ItemPool.WORM_RIDING_HOOKS ); } int gnasirProgress = Preferences.getInteger( "gnasirProgress" ); gnasirProgress |= 16; Preferences.setInteger( "gnasirProgress", gnasirProgress ); QuestManager.incrementDesertExploration( 30 ); break; } // "You don't have time to play the drums." if ( responseText.contains( "don't have time" ) ) { UseItemRequest.lastUpdate = "Insufficient adventures left."; } // "You're too beaten-up to play the drums." else if ( responseText.contains( "too beaten up" ) ) { UseItemRequest.lastUpdate = "Too beaten up."; } // "You head to your campsite and crank up the drum // machine. You press buttons at random, waiting for // something interesting to happen, but you only // succeed in annoying your neighbors." else if ( responseText.contains( "head to your campsite" ) ) { UseItemRequest.lastUpdate = "Can't find the beach"; } if ( !UseItemRequest.lastUpdate.equals( "" ) ) { KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); } // If we are redirected to a fight, the item is // consumed elsewhere. If we got here, it wasn't // actually consumed return; case ItemPool.CURSED_PIECE_OF_THIRTEEN: // "You take the piece of thirteen to a rare coin // dealer in Seaside Town (he's got a shop set up right // next to that library across the street from the // Sleazy Back Alley) to see what you can get for // it. Turns out you can get X Meat for it." if ( responseText.contains( "rare coin dealer in Seaside Town" ) ) { break; } // You consider taking the piece of thirteen to a rare // coin dealer to see if it's worth anything, but you // don't really have time. if ( responseText.contains( "don't really have time" ) ) { UseItemRequest.lastUpdate = "Insufficient adventures left."; } // "You consider taking the piece of thirteen to a rare // coin dealer to see if it's worth anything, but // you're feeling pretty crappy right now." else if ( responseText.contains( "feeling pretty crappy" ) ) { UseItemRequest.lastUpdate = "Too beaten up."; } if ( !UseItemRequest.lastUpdate.equals( "" ) ) { KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); } // If we are redirected to a fight, the item is // consumed elsewhere. If we got here, it wasn't // actually consumed return; case ItemPool.SPOOKY_PUTTY_MONSTER: // You can't tell what this is supposed to be a copy // of. You squish it back into a sheet. if ( responseText.contains( "squish it back into a sheet" ) ) { Preferences.setString( "spookyPuttyMonster", "" ); break; } // If we are redirected to a fight, the item is // consumed elsewhere. If we got here, it wasn't // actually consumed return; case ItemPool.D10: // You don't have time to go on an adventure. Even an imaginary one. if ( responseText.contains( "don't have time" ) ) { UseItemRequest.lastUpdate = "Insufficient adventures left."; } // Your imagination is too drunk right now. else if ( responseText.contains( "Your imagination is too drunk" ) ) { UseItemRequest.lastUpdate = "Inebriety limit reached."; } // Using one of these items will eventually do // something. I am sorry that eventually is not now, // but I ran out of time before KoL Con. else if ( responseText.contains( "eventually is not now" ) ) { UseItemRequest.lastUpdate = "Not yet implemented."; } else { break; } KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); return; case ItemPool.D12: { // You draw the bow and roll [X]d12 to see how far the arrow flies. Matcher m = ARROW_PATTERN.matcher( responseText ); String distance = m.find() ? m.group( 1 ) : ""; // It goes [Xd12] feet, and just as it's about to hit the ground, // a cart with a big target in it is pulled into view and // the arrow hits it dead center. BULLSEYE. if ( responseText.contains( "BULLSEYE" ) ) { String message = "You get a bullseye at " + distance + " feet."; KoLmafia.updateDisplay( message ); RequestLogger.updateSessionLog( message ); break; } // It goes [Xd12] feet, and doesn't hit anything interesting. // You grumble and put the dice away. else if ( responseText.contains( "You grumble and put the dice away" ) ) { UseItemRequest.lastUpdate = "You grumble and put the dice away."; } // Y'know, you're never going to be able to top what happened last time. That was awesome. else if ( responseText.contains( "That was awesome" ) ) { UseItemRequest.lastUpdate = "You already hit a bullseye."; } // If some unknown message, assume we use up the dice. else { break; } KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); return; } case ItemPool.D20: // You already rolled for initiative. if ( responseText.contains( "You already rolled for initiative" ) ) { UseItemRequest.lastUpdate = "You already rolled for initiative"; } // You can't figure out a good way to roll that // quantity of 20-sided dice. Maybe you should've paid // less attention in gym class. else if ( responseText.contains( "Maybe you should've paid less attention in gym class" ) ) { UseItemRequest.lastUpdate = "Rolling that many d20s doesn't do anything interesting."; } else { break; } KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); return; case ItemPool.BRICKO_OOZE: case ItemPool.BRICKO_BAT: case ItemPool.BRICKO_OYSTER: case ItemPool.BRICKO_TURTLE: case ItemPool.BRICKO_ELEPHANT: case ItemPool.BRICKO_OCTOPUS: case ItemPool.BRICKO_PYTHON: case ItemPool.BRICKO_VACUUM_CLEANER: case ItemPool.BRICKO_AIRSHIP: case ItemPool.BRICKO_CATHEDRAL: case ItemPool.BRICKO_CHICKEN: if ( responseText.contains( "You're sick of playing with BRICKOs today" ) ) { Preferences.setInteger( "_brickoFights", 10 ); UseItemRequest.lastUpdate = "You're sick of playing with BRICKOs today"; KoLmafia.updateDisplay( UseItemRequest.lastUpdate ); return; } // You're too drunk to mess with BRICKO right now. // If we are redirected to a fight, the item is // consumed elsewhere. If we got here, it wasn't // actually consumed return; case ItemPool.RECORDING_BALLAD: case ItemPool.RECORDING_BENETTON: case ItemPool.RECORDING_CHORALE: case ItemPool.RECORDING_DONHO: case ItemPool.RECORDING_ELRON: case ItemPool.RECORDING_INIGO: case ItemPool.RECORDING_PRELUDE: // You already have too many songs stuck in your head. if ( responseText.contains( "too many songs stuck in your head" ) ) { UseItemRequest.lastUpdate = "You have the maximum number of AT buffs already."; KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); return; } break; case ItemPool.FOSSILIZED_BAT_SKULL: case ItemPool.FOSSILIZED_SERPENT_SKULL: case ItemPool.FOSSILIZED_BABOON_SKULL: case ItemPool.FOSSILIZED_WYRM_SKULL: case ItemPool.FOSSILIZED_DEMON_SKULL: case ItemPool.FOSSILIZED_SPIDER_SKULL: // If we are redirected to a fight, the item is // consumed elsewhere. If we got here, it wasn't // actually consumed return; case ItemPool.ICE_SCULPTURE: Preferences.setBoolean( "_iceSculptureUsed", true ); // Ice sculptures are a form of entertainment best // served sparingly. You should wait until tomorrow. // If we are redirected to a fight, the item is // consumed elsewhere. If we got here, it wasn't // actually consumed return; case ItemPool.SHAKING_CAMERA: Preferences.setBoolean( "_cameraUsed", true ); // You get the sense that the monster in this camera // isn't ready to be developed just yet. It'll probably // be ready tomorrow. And no, you can't speed it up by // blowing on it. // If we are redirected to a fight, the item is // consumed elsewhere. If we got here, it wasn't // actually consumed return; case ItemPool.SHAKING_CRAPPY_CAMERA: Preferences.setBoolean( "_crappyCameraUsed", true ); // You get the sense that the monster in this camera // isn't ready to be developed just yet. It'll probably // be ready tomorrow. And no, you can't speed it up by // blowing on it. // If we are redirected to a fight, the item is // consumed elsewhere. If we got here, it wasn't // actually consumed return; case ItemPool.PHOTOCOPIED_MONSTER: Preferences.setBoolean( "_photocopyUsed", true ); // You get nauseated just thinking about the smell of // copier toner. You don't think you can handle // another one of these things today. // If we are redirected to a fight, the item is // consumed elsewhere. If we got here, it wasn't // actually consumed return; case ItemPool.PHOTOCOPIER: // You look around to see if anybody's watching, // and when they're not, you drop your pants and // giggle as you make a photocopy of your ass. // When you go to get up, you shatter the glass of the // photocopier. Dammit. // You don't think you can handle another one of these // things today. // If you had a photocopied monster in your inventory, // nothing happens: // You don't want your desk to get all messy -- // you probably shouldn't copy anything else until // you've dealt with this copy you've already got. if ( !responseText.contains( "you drop your pants and giggle" ) ) { return; } Preferences.setString( "photocopyMonster", "Your butt" ); break; case ItemPool.BGE_TATTOO: // You've already got one of those tattoos on. // You should give this one to somebody who will // appreciate it more. if ( responseText.contains( "You've already got one of those tattoos on" ) ) { return; } break; case ItemPool.DOLPHIN_WHISTLE: // If we are redirected to a fight, the item is // consumed elsewhere. If we got here, it wasn't // actually consumed return; case ItemPool.RUSTY_HEDGE_TRIMMERS: // If we are redirected to a choice, the item is // consumed elsewhere. If we got here, it wasn't // actually consumed return; case ItemPool.LYNYRD_SNARE: // If we are redirected to a fight, the item is // consumed elsewhere. If we got here, it wasn't // actually consumed Preferences.setInteger( "_lynyrdSnareUses", 3 ); return; case ItemPool.MOJO_FILTER: // You strain some of the toxins out of your mojo, and // discard the now-grodulated filter. if ( !responseText.contains( "now-grodulated" ) ) { return; } Preferences.increment( "currentMojoFilters", count ); KoLCharacter.setSpleenUse( KoLCharacter.getSpleenUse() - count ); KoLCharacter.updateStatus(); ConcoctionDatabase.getUsables().sort(); break; case ItemPool.SPICE_MELANGE: // You pop the spice melange into your mouth and chew it up. if ( responseText.contains( "too scared to eat any more of that stuff today" ) ) { Preferences.setBoolean( "spiceMelangeUsed", true ); return; } if ( !responseText.contains( "You pop the spice melange into your mouth and chew it up" ) ) { return; } KoLCharacter.setFullness( KoLCharacter.getFullness() - 3 ); KoLCharacter.setInebriety( Math.max( 0, KoLCharacter.getInebriety() - 3 ) ); Preferences.setBoolean( "spiceMelangeUsed", true ); KoLCharacter.updateStatus(); ConcoctionDatabase.getUsables().sort(); break; case ItemPool.ULTRA_MEGA_SOUR_BALL: // You pop the candy in your mouth, and it immediately absorbs almost all of the moisture in your body. if ( responseText.contains( "too scared to eat any more of that candy today" ) ) { Preferences.setBoolean( "_ultraMegaSourBallUsed", true ); return; } if ( !responseText.contains( "You pop the candy in your mouth, and it immediately absorbs almost all of the moisture in your body" ) ) { return; } KoLCharacter.setFullness( KoLCharacter.getFullness() - 3 ); KoLCharacter.setInebriety( Math.max( 0, KoLCharacter.getInebriety() - 3 ) ); Preferences.setBoolean( "_ultraMegaSourBallUsed", true ); KoLCharacter.updateStatus(); ConcoctionDatabase.getUsables().sort(); break; case ItemPool.ALIEN_ANIMAL_MILK: Preferences.setBoolean( "_alienAnimalMilkUsed", true ); KoLCharacter.setFullness( KoLCharacter.getFullness() - 3 ); KoLCharacter.updateStatus(); ConcoctionDatabase.getUsables().sort(); break; case ItemPool.ALIEN_PLANT_POD: Preferences.setBoolean( "_alienPlantPodUsed", true ); KoLCharacter.setInebriety( Math.max( 0, KoLCharacter.getInebriety() - 3 ) ); KoLCharacter.updateStatus(); ConcoctionDatabase.getUsables().sort(); break; case ItemPool.SYNTHETIC_DOG_HAIR_PILL: //Your liver feels better! And quivers a bit. if ( responseText.contains( "liver can't take any more abuse" ) ) { Preferences.setBoolean( "_syntheticDogHairPillUsed", true ); return; } if ( !responseText.contains( "quivers" ) ) { return; } KoLCharacter.setInebriety( Math.max( 0, KoLCharacter.getInebriety() - 1 ) ); Preferences.setBoolean( "_syntheticDogHairPillUsed", true ); KoLCharacter.updateStatus(); ConcoctionDatabase.getUsables().sort(); break; case ItemPool.DISTENTION_PILL: // Your stomach feels rather stretched out if ( responseText.contains( "stomach can't take any more abuse" ) ) { Preferences.setBoolean( "_distentionPillUsed", true ); return; } if ( !responseText.contains( "stomach feels rather stretched" ) ) { return; } Preferences.setBoolean( "_distentionPillUsed", true ); KoLCharacter.updateStatus(); ConcoctionDatabase.getUsables().sort(); break; case ItemPool.MILK_OF_MAGNESIUM: ConcoctionDatabase.getUsables().sort(); ConcoctionDatabase.queuedFood.touch(); break; case ItemPool.NEWBIESPORT_TENT: case ItemPool.BARSKIN_TENT: case ItemPool.COTTAGE: case ItemPool.BRICKO_PYRAMID: case ItemPool.HOUSE: case ItemPool.SANDCASTLE: case ItemPool.TWIG_HOUSE: case ItemPool.GINGERBREAD_HOUSE: case ItemPool.HOBO_FORTRESS: case ItemPool.GIANT_FARADAY_CAGE: case ItemPool.SNOW_FORT: case ItemPool.ELEVENT: case ItemPool.RESIDENCE_CUBE: if ( responseText.contains( "You've already got" ) ) { return; } CampgroundRequest.destroyFurnishings(); CampgroundRequest.setCurrentDwelling( itemId ); break; case ItemPool.SCARECROW: case ItemPool.MEAT_GOLEM: case ItemPool.MAID: case ItemPool.BLACK_BLUE_LIGHT: case ItemPool.LOUDMOUTH_LARRY: case ItemPool.PLASMA_BALL: case ItemPool.LED_CLOCK: case ItemPool.BONSAI_TREE: case ItemPool.MEAT_GLOBE: if ( responseText.contains( "You've already got" ) ) { return; } CampgroundRequest.setCampgroundItem( itemId, 1 ); break; case ItemPool.CLOCKWORK_MAID: if ( responseText.contains( "You've already got" ) ) { return; } CampgroundRequest.removeCampgroundItem( ItemPool.get( ItemPool.MAID, 1 ) ); CampgroundRequest.setCampgroundItem( ItemPool.CLOCKWORK_MAID, 1 ); break; case ItemPool.BEANBAG_CHAIR: case ItemPool.GAUZE_HAMMOCK: case ItemPool.HOT_BEDDING: case ItemPool.COLD_BEDDING: case ItemPool.STENCH_BEDDING: case ItemPool.SPOOKY_BEDDING: case ItemPool.SLEAZE_BEDDING: case ItemPool.SLEEPING_STOCKING: case ItemPool.LAZYBONES_RECLINER: case ItemPool.SALTWATERBED: case ItemPool.SPIRIT_BED: if ( responseText.contains( "You've already got" ) || responseText.contains( "You don't have" ) ) { return; } CampgroundRequest.setCurrentBed( ItemPool.get( itemId, 1 ) ); CampgroundRequest.setCampgroundItem( itemId, 1 ); break; case ItemPool.MILKY_POTION: case ItemPool.SWIRLY_POTION: case ItemPool.BUBBLY_POTION: case ItemPool.SMOKY_POTION: case ItemPool.CLOUDY_POTION: case ItemPool.EFFERVESCENT_POTION: case ItemPool.FIZZY_POTION: case ItemPool.DARK_POTION: case ItemPool.MURKY_POTION: String[][] strings = ItemPool.bangPotionStrings; for ( int i = 0; i < strings.length; ++i ) { if ( responseText.contains( strings[i][2] ) ) { if ( ItemPool.eliminationProcessor( strings, i, item.getItemId(), 819, 827, "lastBangPotion", " of " ) ) { KoLmafia.updateDisplay( "All bang potions have been identified!" ); } break; } } // You don't consume inebriety potions in HCO or HCT if ( responseText.contains( "You decide not to drink it" ) ) { return; } break; case ItemPool.VIAL_OF_RED_SLIME: case ItemPool.VIAL_OF_YELLOW_SLIME: case ItemPool.VIAL_OF_BLUE_SLIME: strings = ItemPool.slimeVialStrings[0]; for ( int i = 0; i < strings.length; ++i ) { if ( responseText.contains( strings[i][1] ) ) { if ( ItemPool.eliminationProcessor( strings, i, item.getItemId(), ItemPool.VIAL_OF_RED_SLIME, ItemPool.VIAL_OF_BLUE_SLIME, "lastSlimeVial", ": " ) ) { KoLmafia.updateDisplay( "All primary slime vials have been identified!" ); } break; } } break; case ItemPool.VIAL_OF_ORANGE_SLIME: case ItemPool.VIAL_OF_GREEN_SLIME: case ItemPool.VIAL_OF_VIOLET_SLIME: strings = ItemPool.slimeVialStrings[1]; for ( int i = 0; i < strings.length; ++i ) { if ( responseText.contains( strings[i][1] ) ) { if ( ItemPool.eliminationProcessor( strings, i, item.getItemId(), ItemPool.VIAL_OF_ORANGE_SLIME, ItemPool.VIAL_OF_VIOLET_SLIME, "lastSlimeVial", ": " ) ) { KoLmafia.updateDisplay( "All secondary slime vials have been identified!" ); } break; } } break; case ItemPool.VIAL_OF_VERMILION_SLIME: case ItemPool.VIAL_OF_AMBER_SLIME: case ItemPool.VIAL_OF_CHARTREUSE_SLIME: case ItemPool.VIAL_OF_TEAL_SLIME: case ItemPool.VIAL_OF_INDIGO_SLIME: case ItemPool.VIAL_OF_PURPLE_SLIME: strings = ItemPool.slimeVialStrings[2]; for ( int i = 0; i < strings.length; ++i ) { if ( responseText.contains( strings[i][1] ) ) { if ( ItemPool.eliminationProcessor( strings, i, item.getItemId(), ItemPool.VIAL_OF_VERMILION_SLIME, ItemPool.VIAL_OF_PURPLE_SLIME, "lastSlimeVial", ": " ) ) { KoLmafia.updateDisplay( "All tertiary slime vials have been identified!" ); } break; } } break; case ItemPool.ANCIENT_CURSED_FOOTLOCKER: if ( !InventoryManager.hasItem( ItemPool.SIMPLE_CURSED_KEY ) ) { return; } ResultProcessor.processItem( ItemPool.SIMPLE_CURSED_KEY, -1 ); break; case ItemPool.ORNATE_CURSED_CHEST: if ( !InventoryManager.hasItem( ItemPool.ORNATE_CURSED_KEY ) ) { return; } ResultProcessor.processItem( ItemPool.ORNATE_CURSED_KEY, -1 ); break; case ItemPool.GILDED_CURSED_CHEST: if ( !InventoryManager.hasItem( ItemPool.GILDED_CURSED_KEY ) ) { return; } ResultProcessor.processItem( ItemPool.GILDED_CURSED_KEY, -1 ); break; case ItemPool.STUFFED_CHEST: if ( !InventoryManager.hasItem( ItemPool.STUFFED_KEY ) ) { return; } ResultProcessor.processItem( ItemPool.STUFFED_KEY, -1 ); break; case ItemPool.GENERAL_ASSEMBLY_MODULE: // "INSUFFICIENT RESOURCES LOCATED TO DISPENSE CRIMBO // CHEER. PLEASE LOCATE A VITAL APPARATUS VENT AND // REQUISITION APPROPRIATE MATERIALS." if ( responseText.contains( "INSUFFICIENT RESOURCES LOCATED" ) ) { return; } // You breathe a heavy sigh of relief as the pseudopods // emerge from your inventory, carrying the laser // cannon, laser targeting chip, and the set of // Unobtainium straps that you acquired earlier from // the Sinister Dodecahedron. if ( responseText.contains( "carrying the laser cannon" ) ) { ResultProcessor.processResult( ItemPool.get( ItemPool.LASER_CANON, -1 ) ); ResultProcessor.processResult( ItemPool.get( ItemPool.LASER_TARGETING_CHIP, -1 ) ); ResultProcessor.processResult( ItemPool.get( ItemPool.UNOBTAINIUM_STRAPS, -1 ) ); } // You breathe a heavy sigh of relief as the pseudopods // emerge from your inventory, carrying the polymorphic // fastening apparatus, hi-density nylocite leg armor, // and the silicon-infused gluteal shield that you // acquired earlier from the Sinister Dodecahedron. else if ( responseText.contains( "carrying the polymorphic fastening apparatus" ) ) { ResultProcessor.processResult( ItemPool.get( ItemPool.FASTENING_APPARATUS, -1 ) ); ResultProcessor.processResult( ItemPool.get( ItemPool.LEG_ARMOR, -1 ) ); ResultProcessor.processResult( ItemPool.get( ItemPool.GLUTEAL_SHIELD, -1 ) ); } // You breathe a heavy sigh of relief as the pseudopods // emerge from your inventory, carrying the carbonite // visor, plexifoam chin strap, and the kevlateflocite // helmet that you acquired earlier from the Sinister // Dodecahedron. else if ( responseText.contains( "carrying the carbonite visor" ) ) { ResultProcessor.processResult( ItemPool.get( ItemPool.CARBONITE_VISOR, -1 ) ); ResultProcessor.processResult( ItemPool.get( ItemPool.CHIN_STRAP, -1 ) ); ResultProcessor.processResult( ItemPool.get( ItemPool.KEVLATEFLOCITE_HELMET, -1 ) ); } break; case ItemPool.AUGMENTED_DRONE: if ( responseText.contains( "You put an overcharged sphere in the cavity" ) ) { ResultProcessor.processResult( ItemPool.get( ItemPool.OVERCHARGED_POWER_SPHERE, -1 ) ); } break; case ItemPool.NEVERENDING_SODA: Preferences.setBoolean( "oscusSodaUsed", true ); return; case ItemPool.EL_VIBRATO_HELMET: // We should parse the result and save the current // state of the conduits return; case ItemPool.PUNCHCARD_ATTACK: case ItemPool.PUNCHCARD_REPAIR: case ItemPool.PUNCHCARD_BUFF: case ItemPool.PUNCHCARD_MODIFY: case ItemPool.PUNCHCARD_BUILD: if ( !responseText.contains( "A voice emerges" ) ) { // You try to stick the punchcard into <name> // and fail. return; } // We should save the state of the Megadrone break; case ItemPool.OUTRAGEOUS_SOMBRERO: Preferences.setBoolean( "outrageousSombreroUsed", true ); return; case ItemPool.GRUB: case ItemPool.MOTH: case ItemPool.FIRE_ANT: case ItemPool.ICE_ANT: case ItemPool.STINKBUG: case ItemPool.DEATH_WATCH_BEETLE: case ItemPool.LOUSE: // You briefly consider eating the plump juicy grub, // but are filled with revulsion at the prospect. if ( responseText.contains( "filled with revulsion" ) ) { return; } break; case ItemPool.PERSONAL_MASSAGER: // You don't really need a massage right now, as your // neck and back aren't feeling particularly kinky. if ( responseText.contains( "don't really need a massage" ) ) { return; } break; case ItemPool.BURROWGRUB_HIVE: // One way or another, you have used it today Preferences.setBoolean( "burrowgrubHiveUsed", true ); // You pick up the burrowgrub hive to look inside it, // and a bunch of the grubs crawl out of it and burrow // under your skin. It's horrifying. You can still // feel them in there. Gah. if ( responseText.contains( "It's horrifying." ) ) { // You have three grub summons left today Preferences.setInteger( "burrowgrubSummonsRemaining", 3 ); } return; case ItemPool.BOOZEHOUND_TOKEN: // You'd take this thing to a bar and see what you can // trade it in for, but you don't know where any bars // are. if ( responseText.contains( "don't know where any bars are" ) ) { return; } break; case ItemPool.SMALL_LAMINATED_CARD: case ItemPool.LITTLE_LAMINATED_CARD: case ItemPool.NOTBIG_LAMINATED_CARD: case ItemPool.UNLARGE_LAMINATED_CARD: DwarfFactoryRequest.useLaminatedItem( item.getItemId(), responseText ); return; case ItemPool.DWARVISH_DOCUMENT: case ItemPool.DWARVISH_PAPER: case ItemPool.DWARVISH_PARCHMENT: DwarfFactoryRequest.useUnlaminatedItem( item.getItemId(), responseText ); return; case ItemPool.WRETCHED_SEAL: case ItemPool.CUTE_BABY_SEAL: case ItemPool.ARMORED_SEAL: case ItemPool.ANCIENT_SEAL: case ItemPool.SLEEK_SEAL: case ItemPool.SHADOWY_SEAL: case ItemPool.STINKING_SEAL: case ItemPool.CHARRED_SEAL: case ItemPool.COLD_SEAL: case ItemPool.SLIPPERY_SEAL: // If we are redirected to a fight, the item is // consumed elsewhere. If we got here, it wasn't // actually consumed case ItemPool.DEPLETED_URANIUM_SEAL: // You've summoned too many Infernal seals today. Any // more and you're afraid the corruption will be too // much for you to bear. if ( responseText.contains( "too many Infernal seals" ) ) { int maxSummons = 5; if ( KoLCharacter.hasEquipped( INFERNAL_SEAL_CLAW ) || InventoryManager.getCount( ItemPool.INFERNAL_SEAL_CLAW ) > 0 ) { maxSummons = 10; } UseItemRequest.lastUpdate = "Summoning limit reached."; Preferences.setInteger( "_sealsSummoned", maxSummons ); } // In order to perform this summoning ritual, you need // 1 seal-blubber candle. You can pick them up at the // store in the Brotherhood of the Smackdown. else if ( responseText.contains( "Brotherhood of the Smackdown" ) ) { UseItemRequest.lastUpdate = "You need more seal-blubber candles."; } // In order to perform this summoning ritual, you need // 1 imbued seal-blubber candle. else if ( responseText.contains( "you need 1 imbued seal-blubber candle" ) ) { UseItemRequest.lastUpdate = "You need an imbued seal-blubber candle."; } // Only Seal Clubbers may use this item. else if ( responseText.contains( "Only Seal Clubbers may use this item." ) ) { UseItemRequest.lastUpdate = "Only Seal Clubbers may use this item."; } // You need to be at least level 6 to use that item else if ( responseText.contains( "need to be at least level" ) ) { UseItemRequest.lastUpdate = "You are not high enough level."; } if ( !UseItemRequest.lastUpdate.equals( "" ) ) { KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); return; } // You feel your clubbing muscles begin to twitch with // anticipation... Begin the Ritual // // Pressing "Begin the Ritual" reissues the request // with an additional field: check=1 // // inv_use.php?which=3&whichitem=3902&pwd // inv_use.php?whichitem=3902&checked=1&pwd return; case ItemPool.SEAL_IRON_INGOT: // You beat the seal-iron into a formidable club. if ( !responseText.contains( "formidable club" ) ) { return; } break; case ItemPool.SINISTER_ANCIENT_TABLET: AdventureRequest.registerDemonName( "Sinister Ancient Tablet", responseText ); return; case ItemPool.BAG_O_TRICKS: Preferences.setBoolean( "_bagOTricksUsed", true ); return; case ItemPool.EVILOMETER: // Parse the result and save current state UseItemRequest.getEvilLevels( responseText ); return; case ItemPool.EVIL_EYE: if ( responseText.contains( "Evilometer emits three quick beeps" ) ) { int evilness = Math.min( Preferences.getInteger( "cyrptNookEvilness" ), 3*count ); Preferences.increment( "cyrptNookEvilness", -evilness ); Preferences.increment( "cyrptTotalEvilness", -evilness ); } break; case ItemPool.QUASIRELGIOUS_SCULPTURE: case ItemPool.SOLID_GOLD_ROSARY: if ( responseText.contains( "entire Cyrpt feels safer" ) ) { // Can't abuse this, so queue a use item to get new Evilometer value RequestThread.postRequest( UseItemRequest.getInstance( ItemPool.EVILOMETER ) ); } break; case ItemPool.KEYOTRON: UseItemRequest.getBugbearBiodataLevels( responseText ); return; case ItemPool.PEN_PAL_KIT: // You've already got a pen pal. There's no way you // could handle the pressure of contantly forgetting to // reply to two kids from Distant Lands... if ( responseText.contains( "already got a pen pal" ) ) { return; } break; case ItemPool.NEW_YOU_CLUB_MEMBERSHIP_FORM: // Per the instructions on the back of the card, you // place your hand over your heart and recite the // membership oath: "I'm good enough, I'm smart enough, // and doggone it, people like me!" You feel affirmed // already! if ( !responseText.contains( "instructions on the back of the card" ) ) { return; } break; case ItemPool.HONEYPOT: // You gain the "Float Like a Butterfly, Smell Like a // Bee" effect. This prevents bees from appearing // while it is active. As soon as it wears off, a bee // will appear. Stop the bee counters, since turns // remaining of the effect give the same info. TurnCounter.stopCounting( "Bee window begin" ); TurnCounter.stopCounting( "Bee window end" ); break; case ItemPool.RONALD_SHELTER_MAP: case ItemPool.GRIMACE_SHELTER_MAP: // If we are redirected to a choice, the item is // consumed elsewhere. break; case ItemPool.BORROWED_TIME: // Set the preference to true both when we fail and succeed. Preferences.setBoolean( "_borrowedTimeUsed", true ); if ( responseText.contains( "already borrowed some time today" ) ) { return; } // You dip into your future and borrow some time. Be sure to spend it wisely! if ( responseText.contains( "dip into your future" ) ) { KoLCharacter.updateStatus(); Preferences.increment( "extraRolloverAdventures", -20 ); } break; case ItemPool.MOVEABLE_FEAST: // The table is looking pretty bare -- you should wait // until tomorrow, and let some of the food magically regenerate. if ( responseText.contains( "wait until tomorrow" ) ) { Preferences.setInteger( "_feastUsed", 5 ); } // <name> chows down on the moveable feast, // then leans back, sighs, and loosens his belt a couple of notches. else if ( responseText.contains( "chows down" ) ) { Preferences.increment( "_feastUsed", 1 ); String familiar = KoLCharacter.getFamiliar().getRace(); String oldList = Preferences.getString( "_feastedFamiliars" ); String newList = oldList + ( oldList.equals( "" ) ? "" : ";" ) + familiar; Preferences.setString( "_feastedFamiliars", newList ); } return; case ItemPool.STAFF_GUIDE: if ( responseText.contains( "You don't have time to screw around in a haunted house" ) ) { UseItemRequest.lastUpdate = "Insufficient adventures to use a staff guide."; KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); return; } if ( responseText.contains( "You aren't allowed to go to any Haunted Houses right now" ) ) { UseItemRequest.lastUpdate = "You aren't allowed to go to any Haunted Houses right now."; KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); return; } if ( responseText.contains( "You don't know where any haunted sorority houses are right now." ) || responseText.contains( "No way. It's boring in there now that everybody is dead." ) ) { UseItemRequest.lastUpdate = "The Haunted Sorority House is unavailable."; KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); return; } break; case ItemPool.GHOSTLY_BODY_PAINT: case ItemPool.NECROTIZING_BODY_SPRAY: case ItemPool.BITE_LIPSTICK: case ItemPool.WHISKER_PENCIL: case ItemPool.PRESS_ON_RIBS: if ( responseText.contains( "You've already got a sexy costume on" ) ) { UseItemRequest.lastUpdate = "You've already got a sexy costume on."; KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); return; } break; case ItemPool.BLACK_PAINT: if ( KoLCharacter.inFistcore() && responseText.contains( "Your teachings forbid the use of black paint." ) ) { UseItemRequest.lastUpdate = "Your teachings forbid the use of black paint."; KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); return; } break; case ItemPool.ALL_YEAR_SUCKER: Preferences.setBoolean( "_allYearSucker", true ); return; case ItemPool.DARK_CHOCOLATE_HEART: Preferences.setBoolean( "_darkChocolateHeart", true ); return; case ItemPool.JACKASS_PLUMBER_GAME: Preferences.setBoolean( "_jackassPlumberGame", true ); return; case ItemPool.TRIVIAL_AVOCATIONS_GAME: Preferences.setBoolean( "_trivialAvocationsGame", true ); return; case ItemPool.RESOLUTION_ADVENTUROUS: // You read aloud: // "I, <player name>, resolve to live this day as though it were my last day. // Actually, maybe tomorrow. Maybe I'll live tomorrow as though it were my last day. // That seems like a better idea. I'll be more adventurous tomorrow." // Then you resolve to do it again. // Then you resolve to do it again and again. // Then you resolve to do it again and again and again. // Then you resolve to do it again and again and again and again. if ( responseText.contains( "already feeling adventurous enough" ) ) { // player has already used 5 resolutions today int extraAdv = 10 - Preferences.getInteger( "_resolutionAdv" ); Preferences.increment( "extraRolloverAdventures", extraAdv ); Preferences.increment( "_resolutionAdv", extraAdv ); return; } int used = 1; matcher = ADVENTUROUS_RESOLUTION_PATTERN.matcher( responseText ); while ( matcher.find() ) { used += 1; } Preferences.increment( "extraRolloverAdventures", 2 * used ); Preferences.increment( "_resolutionAdv", 2 * used ); if ( used < count ) { item = item.getInstance( used ); } break; case ItemPool.CLANCY_SACKBUT: KoLCharacter.setCurrentInstrument( CharPaneRequest.SACKBUT ); return; case ItemPool.CLANCY_CRUMHORN: KoLCharacter.setCurrentInstrument( CharPaneRequest.CRUMHORN ); return; case ItemPool.CLANCY_LUTE: KoLCharacter.setCurrentInstrument( CharPaneRequest.LUTE ); return; case ItemPool.PEPPERMINT_PACKET: case ItemPool.PUMPKIN_SEEDS: case ItemPool.DRAGON_TEETH: case ItemPool.BEER_SEEDS: case ItemPool.WINTER_SEEDS: case ItemPool.THANKSGARDEN_SEEDS: if ( Limitmode.limitCampground() || KoLCharacter.isEd() || KoLCharacter.inNuclearAutumn() ) { return; } CampgroundRequest.clearCrop(); RequestThread.postRequest( new CampgroundRequest() ); break; case ItemPool.ESSENTIAL_TOFU: Preferences.setBoolean( "_essentialTofuUsed", true ); break; case ItemPool.CHOCOLATE_CIGAR: if ( responseText.contains( "You light the end" ) ) { Preferences.setInteger( "_chocolateCigarsUsed", 1 ); } else if ( responseText.contains( "This one doesn't taste" ) ) { Preferences.setInteger( "_chocolateCigarsUsed", 2 ); } else { Preferences.setInteger( "_chocolateCigarsUsed", 3 ); } break; case ItemPool.VITACHOC_CAPSULE: if ( responseText.contains( "As the nutritive nanobots" ) ) { Preferences.setInteger( "_vitachocCapsulesUsed", 1 ); } else if ( responseText.contains( "Your body is becoming acclimated" ) ) { Preferences.setInteger( "_vitachocCapsulesUsed", 2 ); } else { Preferences.setInteger( "_vitachocCapsulesUsed", 3 ); } break; case ItemPool.FANCY_CHOCOLATE: case ItemPool.FANCY_EVIL_CHOCOLATE: case ItemPool.FANCY_CHOCOLATE_CAR: case ItemPool.CHOCOLATE_SEAL_CLUBBING_CLUB: case ItemPool.CHOCOLATE_TURTLE_TOTEM: case ItemPool.CHOCOLATE_PASTA_SPOON: case ItemPool.CHOCOLATE_SAUCEPAN: case ItemPool.CHOCOLATE_DISCO_BALL: case ItemPool.CHOCOLATE_STOLEN_ACCORDION: case ItemPool.BEET_MEDIOCREBAR: case ItemPool.CORN_MEDIOCREBAR: case ItemPool.CABBAGE_MEDIOCREBAR: case ItemPool.CHOCO_CRIMBOT: Preferences.increment( "_chocolatesUsed" ); break; case ItemPool.LOVE_CHOCOLATE: Preferences.increment( "_loveChocolatesUsed" ); break; case ItemPool.CREEPY_VOODOO_DOLL: Preferences.setBoolean( "_creepyVoodooDollUsed", true ); return; case ItemPool.HOBBY_HORSE: Preferences.setBoolean( "_hobbyHorseUsed", true ); return; case ItemPool.BALL_IN_A_CUP: Preferences.setBoolean( "_ballInACupUsed", true ); return; case ItemPool.SET_OF_JACKS: Preferences.setBoolean( "_setOfJacksUsed", true ); return; case ItemPool.BAG_OF_CANDY: Preferences.setBoolean( "_bagOfCandyUsed", true ); return; case ItemPool.EMBLEM_AKGYXOTH: case ItemPool.IDOL_AKGYXOTH: Preferences.setBoolean( "_akgyxothUsed", true ); return; case ItemPool.GNOLL_EYE: Preferences.setBoolean( "_gnollEyeUsed", true ); return; case ItemPool.KOL_CON_SIX_PACK: Preferences.setBoolean( "_kolConSixPackUsed", true ); return; case ItemPool.MUS_MANUAL: case ItemPool.MYS_MANUAL: case ItemPool.MOX_MANUAL: Preferences.setBoolean( "_guildManualUsed", true ); return; case ItemPool.STUFFED_POCKETWATCH: // You play with the stuffed watch for a while, moving the // hands around at random, watching everything around you // speed up and slow down. It's great fun! if ( responseText.contains( "You play with the stuffed watch" ) ) { Preferences.setBoolean( "_stuffedPocketwatchUsed", true ); } return; case ItemPool.STYX_SPRAY: Preferences.setBoolean( "_styxSprayUsed", true ); return; case ItemPool.STABONIC_SCROLL: Preferences.setBoolean( "_stabonicScrollUsed", true ); return; case ItemPool.COAL_PAPERWEIGHT: Preferences.setBoolean( "_coalPaperweightUsed", true ); return; case ItemPool.JINGLE_BELL: Preferences.setBoolean( "_jingleBellUsed", true ); return; case ItemPool.BOX_OF_HAMMERS: Preferences.setBoolean( "_boxOfHammersUsed", true ); break; case ItemPool.TEMPURA_AIR: Preferences.setBoolean( "_tempuraAirUsed", true ); break; case ItemPool.PRESSURIZED_PNEUMATICITY: if ( responseText.contains( "You pop the cork" ) ) { Preferences.setBoolean( "_pneumaticityPotionUsed", true ); } break; case ItemPool.HYPERINFLATED_SEAL_LUNG: Preferences.setBoolean( "_hyperinflatedSealLungUsed", true ); break; case ItemPool.BALLAST_TURTLE: Preferences.setBoolean( "_ballastTurtleUsed", true ); break; case ItemPool.LEFT_BEAR_ARM: // Both bear arms are used up to create the box // You find a box, carefully label it, and shove a couple of bear arms into it. if ( !responseText.contains( "You find a box" ) ) { return; } ResultProcessor.removeItem( ItemPool.RIGHT_BEAR_ARM ); return; case ItemPool.CURSED_KEG: Preferences.setBoolean( "_cursedKegUsed", true ); return; case ItemPool.CURSED_MICROWAVE: Preferences.setBoolean( "_cursedMicrowaveUsed", true ); return; case ItemPool.TACO_FLIER: Preferences.setBoolean( "_tacoFlierUsed", true ); return; case ItemPool.CRIMBO_CREEPY_HEAD: case ItemPool.CRIMBO_STOGIE_ODORIZER: case ItemPool.CRIMBO_HOT_MUG: case ItemPool.CRIMBO_TREE_FLOCKER: case ItemPool.CRIMBO_RUDOLPH_DOLL: // Instead of checking which one was used and removing // the others here and this one below, remove one of // each here ResultProcessor.processResult( item ); ResultProcessor.processItem( ItemPool.CRIMBO_CREEPY_HEAD, -1 ); ResultProcessor.processItem( ItemPool.CRIMBO_STOGIE_ODORIZER, -1 ); ResultProcessor.processItem( ItemPool.CRIMBO_HOT_MUG, -1 ); ResultProcessor.processItem( ItemPool.CRIMBO_TREE_FLOCKER, -1 ); ResultProcessor.processItem( ItemPool.CRIMBO_RUDOLPH_DOLL, -1 ); return; case ItemPool.SUSPICIOUS_JAR: case ItemPool.GOURD_JAR: case ItemPool.MYSTIC_JAR: case ItemPool.OLD_MAN_JAR: case ItemPool.ARTIST_JAR: case ItemPool.MEATSMITH_JAR: case ItemPool.JICK_JAR: // You may have succeeded if ( !responseText.contains( "You open the jar and peer inside." ) ) { // If not, don't remove it return; } CampgroundRequest.setCampgroundItem( itemId, 1 ); Preferences.setBoolean( "_psychoJarUsed", true ); break; case ItemPool.FISHY_PIPE: Preferences.setBoolean( "_fishyPipeUsed", true ); return; case ItemPool.BLANK_OUT_BOTTLE: if ( KoLCharacter.isJarlsberg() && responseText.contains( "mess with this crap" ) ) { UseItemRequest.lastUpdate = "Jarlsberg hated getting his hands dirty. There is no way he would mess with this crap."; KoLmafia.updateDisplay( MafiaState.ERROR, UseItemRequest.lastUpdate ); return; } Preferences.setBoolean( "_blankoutUsed", true ); break; case ItemPool.SONAR: if ( responseText.contains( "rubble leading west from Guano Junction collapses in a heap" ) ) { QuestDatabase.setQuestProgress( Quest.BAT, "step1" ); } else if ( responseText.contains( "sound waves knock down the pile of rocks on the east side" ) ) { QuestDatabase.setQuestProgress( Quest.BAT, "step2" ); } else if ( responseText.contains( "high frequency noise makes short work of the rubble" ) ) { QuestDatabase.setQuestProgress( Quest.BAT, "step3" ); } break; case ItemPool.SPRING_BEACH_CHARTER: Preferences.setBoolean( "sleazeAirportAlways", true ); if ( !responseText.contains( "name gets added to the registry" ) ) { return; } break; case ItemPool.SPRING_BEACH_TICKET: if ( responseText.contains( "already have access to that place" ) ) { return; } Preferences.setBoolean( "_sleazeAirportToday", true ); break; case ItemPool.SPRING_BEACH_TATTOO_COUPON: if ( !responseText.contains( "stagger into the back room" ) ) { // If not drunk enough the items is not consumed return; } break; case ItemPool.CONSPIRACY_ISLAND_CHARTER: Preferences.setBoolean( "spookyAirportAlways", true ); if ( !responseText.contains( "name gets added to the registry" ) ) { return; } break; case ItemPool.CONSPIRACY_ISLAND_TICKET: if ( responseText.contains( "already have access to that place" ) ) { // If you already have access it is not consumed return; } Preferences.setBoolean( "_spookyAirportToday", true ); break; case ItemPool.SHAWARMA_KEYCARD: Preferences.setBoolean( "SHAWARMAInitiativeUnlocked", true ); break; case ItemPool.BOTTLE_OPENER_KEYCARD: Preferences.setBoolean( "canteenUnlocked", true ); break; case ItemPool.ARMORY_KEYCARD: Preferences.setBoolean( "armoryUnlocked", true ); break; case ItemPool.DINSEY_CHARTER: Preferences.setBoolean( "stenchAirportAlways", true ); if ( !responseText.contains( "name gets added to the registry" ) ) { return; } break; case ItemPool.DINSEY_TICKET: if ( responseText.contains( "already have access to that place" ) ) { // If you already have access it is not consumed return; } Preferences.setBoolean( "_stenchAirportToday", true ); break; case ItemPool.VOLCANO_CHARTER: Preferences.setBoolean( "hotAirportAlways", true ); if ( !responseText.contains( "name gets added to the registry" ) ) { return; } break; case ItemPool.VOLCANO_TICKET: if ( responseText.contains( "already have access to that place" ) ) { // If you already have access it is not consumed return; } Preferences.setBoolean( "_hotAirportToday", true ); break; case ItemPool.GLACIEST_CHARTER: Preferences.setBoolean( "coldAirportAlways", true ); if ( !responseText.contains( "name gets added to the registry" ) ) { return; } break; case ItemPool.GLACIEST_TICKET: if ( responseText.contains( "already have access to that place" ) ) { // If you already have access it is not consumed return; } Preferences.setBoolean( "_coldAirportToday", true ); break; case ItemPool.LOVEBUG_PHEROMONES: Preferences.setBoolean( "lovebugsUnlocked", true ); if ( !responseText.contains( "have been permanently unlocked" ) ) { return; } break; case ItemPool.SPOOKYRAVEN_TELEGRAM: QuestDatabase.setQuestIfBetter( Quest.SPOOKYRAVEN_NECKLACE, QuestDatabase.STARTED ); break; case ItemPool.MERKIN_WORDQUIZ: matcher = MERKIN_WORDQUIZ_PATTERN.matcher( responseText ); if ( !matcher.find() ) { // Otherwise, it is not consumed return; } Preferences.setInteger( "merkinVocabularyMastery", StringUtilities.parseInt( matcher.group(1) ) ); ResultProcessor.removeItem( ItemPool.MERKIN_CHEATSHEET ); break; case ItemPool.DREADSCROLL: // Only two things remove the Mer-kin dreadscroll from inventory: // successfully becoming the Mer-kin High Priest, and attempting // to read it when you are already the Champion of the Arena. // // I guess you're the Mer-kin High Priest now. Cool! // // The sigil burned into your forehead (you know, the one you got // for winning the tournament in the Colosseum?) flares up in // intense pain as you look at the dreadscroll. A tendril of fire // curls out and burns the scroll to ashes. if ( responseText.contains( "I guess you're the Mer-kin High Priest now" ) ) { Preferences.setString( "merkinQuestPath", "scholar" ); break; } if ( !Preferences.getString( "merkinQuestPath" ).equals( "done" ) && responseText.contains( "The sigil burned into your forehead" ) ) { Preferences.setString( "merkinQuestPath", "gladiator" ); Preferences.setInteger( "lastColosseumRoundWon", 15 ); break; } // Otherwise, it is not consumed return; case ItemPool.MERKIN_STASHBOX: ResultProcessor.removeItem( ItemPool.MERKIN_LOCKKEY ); break; case ItemPool.MERKIN_KNUCKLEBONE: DreadScrollManager.handleKnucklebone( responseText ); break; case ItemPool.DEFECTIVE_TOKEN: Preferences.setBoolean( "_defectiveTokenUsed", true ); return; case ItemPool.SILVER_DREAD_FLASK: Preferences.setBoolean( "_silverDreadFlaskUsed", true ); return; case ItemPool.BRASS_DREAD_FLASK: Preferences.setBoolean( "_brassDreadFlaskUsed", true ); return; case ItemPool.MARK_OF_THE_BUGBEAR: case ItemPool.MARK_OF_THE_WEREWOLF: case ItemPool.MARK_OF_THE_ZOMBIE: case ItemPool.MARK_OF_THE_GHOST: case ItemPool.MARK_OF_THE_VAMPIRE: case ItemPool.MARK_OF_THE_SKELETON: if ( !responseText.contains( "You have unlocked a new tattoo" ) ) { return; } break; case ItemPool.DREADSYLVANIAN_ALMANAC: // You've already learned everything you can learn from these things. if ( responseText.contains( "You've already learned everything" ) ) { return; } break; case ItemPool.BOOK_OF_MATCHES: // If Hidden Tavern not already unlocked, new items available if ( Preferences.getInteger( "hiddenTavernUnlock" ) != KoLCharacter.getAscensions() && !responseText.contains( "admire" ) ) { // Unlock Hidden Tavern Preferences.setInteger( "hiddenTavernUnlock", KoLCharacter.getAscensions() ); ConcoctionDatabase.setRefreshNeeded( true ); } return; case ItemPool.ETERNAL_CAR_BATTERY: Preferences.setBoolean( "_eternalCarBatteryUsed", true ); return; case ItemPool.SKELETON: // We detect its loss via the choice adventure. return; case ItemPool.DESERT_PAMPHLET: QuestManager.incrementDesertExploration( 15 ); break; case ItemPool.MODELING_CLAYMORE: // You bury the claymore in the clay (what a coincidence!) // in the middle of the battlefield on the Mysterious // Island of Mystery. Then you hide behind a tree and // watch as it goes off, covering dozens of hippies and // frat boys with globs of wet clay, and forcing them // to head home and take showers. // // Well, the frat boys leave to take showers. The // hippies just leave because they're lazy. if ( responseText.contains( "You bury the claymore in the clay" ) ) { // Look at the island map and make our best // effort to synch up the kill count RequestThread.postRequest( new IslandRequest() ); break; } // Otherwise, it is not consumed return; case ItemPool.STUFFING_FLUFFER: if ( responseText.contains( "hippies and frat orcs" ) ) { // Look at the island map and make our best // effort to synch up the kill count RequestThread.postRequest( new IslandRequest() ); break; } // Otherwise, it is not consumed return; case ItemPool.BLUE_LINT: case ItemPool.GREEN_LINT: case ItemPool.WHITE_LINT: case ItemPool.ORANGE_LINT: if ( responseText.contains( "very improbable thing happens" ) ) { // Remove all four lints ResultProcessor.processItem( ItemPool.BLUE_LINT, -1 ); ResultProcessor.processItem( ItemPool.GREEN_LINT, -1 ); ResultProcessor.processItem( ItemPool.WHITE_LINT, -1 ); ResultProcessor.processItem( ItemPool.ORANGE_LINT, -1 ); } return; case ItemPool.SPIRIT_PILLOW: case ItemPool.SPIRIT_SHEET: case ItemPool.SPIRIT_MATTRESS: case ItemPool.SPIRIT_BLANKET: if ( responseText.contains( "spirit bed" ) ) { // Remove bedding ResultProcessor.processItem( ItemPool.SPIRIT_PILLOW, -1 ); ResultProcessor.processItem( ItemPool.SPIRIT_SHEET, -1 ); ResultProcessor.processItem( ItemPool.SPIRIT_MATTRESS, -1 ); ResultProcessor.processItem( ItemPool.SPIRIT_BLANKET, -1 ); } return; case ItemPool.PASTA_ADDITIVE: Preferences.setBoolean( "_pastaAdditive", true ); return; case ItemPool.WARBEAR_BREAKFAST_MACHINE: Preferences.setBoolean( "_warbearBreakfastMachineUsed", true ); return; case ItemPool.WARBEAR_SODA_MACHINE: Preferences.setBoolean( "_warbearSodaMachineUsed", true ); return; case ItemPool.WARBEAR_GYROCOPTER_BROKEN: Preferences.setBoolean( "_warbearGyrocopterUsed", true ); break; case ItemPool.WARBEAR_BANK: // You don't have 25 Meat to drop into the bank. if ( !responseText.contains( "don't have" ) ) { Preferences.setBoolean( "_warbearBankUsed", true ); } return; case ItemPool.LUPINE_APPETITE_HORMONES: Preferences.setBoolean( "_lupineHormonesUsed", true ); break; case ItemPool.CORRUPTED_STARDUST: case ItemPool.PIXEL_ORB: Preferences.setBoolean( "_corruptedStardustUsed", true ); break; case ItemPool.JARLSBERG_SOUL_FRAGMENT: if ( responseText.contains( "extra skill point" ) ) { Preferences.increment( "jarlsbergPoints" ); } break; case ItemPool.SNEAKY_PETE_SHOT: if ( responseText.contains( "extra skill point" ) ) { Preferences.increment( "sneakyPetePoints" ); } break; case ItemPool.SESHAT_TALISMAN: if ( responseText.contains( "transform into knowledge" ) ) { Preferences.increment( "edPoints" ); } break; case ItemPool.ESSENCE_OF_ANNOYANCE: if ( responseText.contains( "You quaff" ) ) { Preferences.decrement( "summonAnnoyanceCost", 1 ); } break; case ItemPool.SWEET_TOOTH: if ( responseText.contains( "You pop the sweet" ) || responseText.contains( "You already had" ) ) { Preferences.setBoolean( "_sweetToothUsed", true ); } break; case ItemPool.VORACI_TEA: Preferences.setBoolean( "_voraciTeaUsed", true ); break; case ItemPool.SOBRIE_TEA: Preferences.setBoolean( "_sobrieTeaUsed", true ); break; case ItemPool.CHRONER_TRIGGER: Preferences.setBoolean( "_chronerTriggerUsed", true ); return; case ItemPool.CHRONER_CROSS: Preferences.setBoolean( "_chronerCrossUsed", true ); if ( !responseText.contains( "falls right through" ) ) { ResultProcessor.removeItem( ItemPool.CHRONER ); } return; case ItemPool.PALINDROME_BOOK_2: QuestDatabase.setQuestIfBetter( Quest.PALINDOME, "step2" ); return; case ItemPool.STEAM_FIST_1: case ItemPool.STEAM_FIST_2: case ItemPool.STEAM_FIST_3: if ( responseText.contains( "rub the three trading cards together" ) ) { ResultProcessor.processItem( ItemPool.STEAM_FIST_1, -1 ); ResultProcessor.processItem( ItemPool.STEAM_FIST_2, -1 ); ResultProcessor.processItem( ItemPool.STEAM_FIST_3, -1 ); } return; case ItemPool.STEAM_TRIP_1: case ItemPool.STEAM_TRIP_2: case ItemPool.STEAM_TRIP_3: if ( responseText.contains( "rub the three trading cards together" ) ) { ResultProcessor.processItem( ItemPool.STEAM_TRIP_1, -1 ); ResultProcessor.processItem( ItemPool.STEAM_TRIP_2, -1 ); ResultProcessor.processItem( ItemPool.STEAM_TRIP_3, -1 ); } return; case ItemPool.STEAM_METEOID_1: case ItemPool.STEAM_METEOID_2: case ItemPool.STEAM_METEOID_3: if ( responseText.contains( "rub the three trading cards together" ) ) { ResultProcessor.processItem( ItemPool.STEAM_METEOID_1, -1 ); ResultProcessor.processItem( ItemPool.STEAM_METEOID_2, -1 ); ResultProcessor.processItem( ItemPool.STEAM_METEOID_3, -1 ); } return; case ItemPool.STEAM_DEMON_1: case ItemPool.STEAM_DEMON_2: case ItemPool.STEAM_DEMON_3: if ( responseText.contains( "rub the three trading cards together" ) ) { ResultProcessor.processItem( ItemPool.STEAM_DEMON_1, -1 ); ResultProcessor.processItem( ItemPool.STEAM_DEMON_2, -1 ); ResultProcessor.processItem( ItemPool.STEAM_DEMON_3, -1 ); } return; case ItemPool.STEAM_PLUMBER_1: case ItemPool.STEAM_PLUMBER_2: case ItemPool.STEAM_PLUMBER_3: if ( responseText.contains( "rub the three trading cards together" ) ) { ResultProcessor.processItem( ItemPool.STEAM_PLUMBER_1, -1 ); ResultProcessor.processItem( ItemPool.STEAM_PLUMBER_2, -1 ); ResultProcessor.processItem( ItemPool.STEAM_PLUMBER_3, -1 ); } return; case ItemPool.LETTER_FROM_MELVIGN: QuestDatabase.setQuestIfBetter( Quest.SHIRT, QuestDatabase.STARTED ); break; case ItemPool.XIBLAXIAN_SCHEMATIC_COWL: case ItemPool.XIBLAXIAN_SCHEMATIC_TROUSERS: case ItemPool.XIBLAXIAN_SCHEMATIC_VEST: case ItemPool.XIBLAXIAN_SCHEMATIC_BURRITO: case ItemPool.XIBLAXIAN_SCHEMATIC_WHISKEY: case ItemPool.XIBLAXIAN_SCHEMATIC_RESIDENCE: case ItemPool.XIBLAXIAN_SCHEMATIC_GOGGLES: if ( Preferences.getBoolean( "unknownRecipe" + itemId ) ) { String message = "Learned recipe: " + name + " (" + itemId + ")"; RequestLogger.printLine( message ); RequestLogger.updateSessionLog( message ); Preferences.setBoolean( "unknownRecipe" + itemId, false ); } break; case ItemPool.CRIMBOT_TORSO_2: case ItemPool.CRIMBOT_LEFTARM_2: case ItemPool.CRIMBOT_RIGHTARM_2: case ItemPool.CRIMBOT_PROPULSION_2: case ItemPool.CRIMBOT_TORSO_3: case ItemPool.CRIMBOT_LEFTARM_3: case ItemPool.CRIMBOT_RIGHTARM_3: case ItemPool.CRIMBOT_PROPULSION_3: case ItemPool.CRIMBOT_TORSO_4: case ItemPool.CRIMBOT_LEFTARM_4: case ItemPool.CRIMBOT_RIGHTARM_4: case ItemPool.CRIMBOT_PROPULSION_4: case ItemPool.CRIMBOT_TORSO_5: case ItemPool.CRIMBOT_LEFTARM_5: case ItemPool.CRIMBOT_RIGHTARM_5: case ItemPool.CRIMBOT_PROPULSION_5: case ItemPool.CRIMBOT_TORSO_6: case ItemPool.CRIMBOT_LEFTARM_6: case ItemPool.CRIMBOT_RIGHTARM_6: case ItemPool.CRIMBOT_PROPULSION_6: case ItemPool.CRIMBOT_TORSO_7: case ItemPool.CRIMBOT_LEFTARM_7: case ItemPool.CRIMBOT_RIGHTARM_7: case ItemPool.CRIMBOT_PROPULSION_7: case ItemPool.CRIMBOT_TORSO_8: case ItemPool.CRIMBOT_LEFTARM_8: case ItemPool.CRIMBOT_RIGHTARM_8: case ItemPool.CRIMBOT_PROPULSION_8: case ItemPool.CRIMBOT_TORSO_9: case ItemPool.CRIMBOT_LEFTARM_9: case ItemPool.CRIMBOT_RIGHTARM_9: case ItemPool.CRIMBOT_PROPULSION_9: case ItemPool.CRIMBOT_TORSO_10: case ItemPool.CRIMBOT_LEFTARM_10: case ItemPool.CRIMBOT_RIGHTARM_10: case ItemPool.CRIMBOT_PROPULSION_10: case ItemPool.CRIMBOT_TORSO_11: case ItemPool.CRIMBOT_LEFTARM_11: case ItemPool.CRIMBOT_RIGHTARM_11: case ItemPool.CRIMBOT_PROPULSION_11: if ( !responseText.contains( "You feed the schematic into the Crimbot assembler" ) ) { return; } break; case ItemPool.GAUDY_KEY: if ( !responseText.contains( "the key vanishes" ) ) { return; } break; case ItemPool.PICKY_TWEEZERS: Preferences.setBoolean( "_pickyTweezersUsed", true ); return; case ItemPool.BITTYCAR_HOTCAR: Preferences.setString( "_bittycar", "hotcar" ); return; case ItemPool.BITTYCAR_MEATCAR: Preferences.setString( "_bittycar", "meatcar" ); return; case ItemPool.BITTYCAR_SOULCAR: Preferences.setString( "_bittycar", "soulcar" ); return; case ItemPool.RED_GREEN_RAIN_STICK: Preferences.setBoolean( "_rainStickUsed", true ); return; case ItemPool.STILL_BEATING_SPLEEN: Preferences.setInteger( "lastStillBeatingSpleen", KoLCharacter.getAscensions() ); if ( !responseText.contains( "assimilate" ) ) { return; } break; case ItemPool.WAREHOUSE_MAP_PAGE: case ItemPool.WAREHOUSE_INVENTORY_PAGE: if ( responseText.contains( "compare the map" ) ) { ResultProcessor.removeItem( ItemPool.WAREHOUSE_INVENTORY_PAGE ); ResultProcessor.removeItem( ItemPool.WAREHOUSE_MAP_PAGE ); Preferences.increment( "warehouseProgress", 8 ); } return; case ItemPool.MAYONEX: case ItemPool.MAYODIOL: case ItemPool.MAYOSTAT: case ItemPool.MAYOZAPINE: case ItemPool.MAYOFLEX: if ( responseText.contains( "mouth is already full" ) ) { return; } Preferences.setString( "mayoInMouth", item.getName() ); Preferences.increment( "mayoLevel", 1 ); break; case ItemPool.HUNGER_SAUCE: Preferences.setBoolean( "_hungerSauceUsed", true ); break; case ItemPool.BRAIN_PRESERVATION_FLUID: Preferences.setBoolean( "_brainPreservationFluidUsed", true ); break; case ItemPool.RESORT_CHIP: if ( responseText.contains( "turn in 100 chips at the redemption center" ) ) { ResultProcessor.processItem( ItemPool.RESORT_CHIP, -100 ); } return; case ItemPool.COCKTAIL_SHAKER: Preferences.setBoolean( "_cocktailShakerUsed", true ); return; case ItemPool.TONIC_DJINN: if ( responseText.contains( "put the bottle away" ) ) { return; } Preferences.setBoolean( "_tonicDjinn", true ); if ( responseText.contains( "already had a wish today" ) ) { return; } break; case ItemPool.TWELVE_NIGHT_ENERGY: Preferences.setBoolean( "_twelveNightEnergyUsed", true ); break; case ItemPool.CSA_FIRE_STARTING_KIT: // If this worked, it redirected to choice #595 // If it didn't redirect, it was not consumed. Preferences.setBoolean( "_fireStartingKitUsed", true ); return; case ItemPool.SHRINE_BARREL_GOD: Preferences.setBoolean( "barrelShrineUnlocked", true ); break; case ItemPool.HAUNTED_DOGHOUSE: if ( responseText.contains( "You install the doghouse at your campsite" ) ) { CampgroundRequest.setCampgroundItem( ItemPool.HAUNTED_DOGHOUSE, 1 ); break; } return; case ItemPool.GHOST_DOG_CHOW: if ( responseText.contains( "familiar doesn't seem interested" ) ) { // Not used up UseItemRequest.lastUpdate = "Your familiar is not interested in that item."; return; } break; case ItemPool.VYKEA_INSTRUCTIONS: // If "using" the item works, it redirects to choice.php. // If it does not redirect, it gives a message and is // not consumed. return; case ItemPool.CIRCLE_DRUM: // You join the 1,427 other people sitting in a drum circle in Seaside Town. Pattern RHYTHM = Pattern.compile( "You join the (.*?) other people" ); Matcher rhythmMatcher = RHYTHM.matcher( responseText ); if ( rhythmMatcher.find() ) { int boost = StringUtilities.parseIntInternal2( rhythmMatcher.group( 1 ) ); int bonus = ( boost - 1 ) / 10 + 1; Preferences.setInteger( "_feelinTheRhythm", bonus ); } Preferences.setBoolean( "_circleDrumUsed", true ); return; case ItemPool.CLARA_BELL: Preferences.setBoolean( "_claraBellUsed", true ); return; case ItemPool.GLENN_DICE: Preferences.setBoolean( "_glennGoldenDiceUsed", true ); return; case ItemPool.SNOWMAN_CRATE: Preferences.setBoolean( "snojoAvailable", true ); break; case ItemPool.ROYAL_TEA: Preferences.increment( "royalty", 1 ); break; case ItemPool.BOO_CLUE: if ( responseText.contains( "already in the process of investigating" ) ) { // Not used up return; } break; case ItemPool.MOUNTAIN_SKIN: case ItemPool.GRIZZLED_SKIN: case ItemPool.DIAMONDBACK_SKIN: case ItemPool.COAL_SKIN: case ItemPool.FRONTWINDER_SKIN: case ItemPool.ROTTING_SKIN: EquipmentManager.setEquipment( EquipmentManager.BOOTSKIN, item ); break; case ItemPool.QUICKSILVER_SPURS: case ItemPool.THICKSILVER_SPURS: case ItemPool.WICKSILVER_SPURS: case ItemPool.SLICKSILVER_SPURS: case ItemPool.SICKSILVER_SPURS: case ItemPool.NICKSILVER_SPURS: case ItemPool.TICKSILVER_SPURS: EquipmentManager.setEquipment( EquipmentManager.BOOTSPUR, item ); break; case ItemPool.SNAKE_OIL: Preferences.increment( "awolMedicine", 3 ); Preferences.increment( "awolVenom", 3 ); break; case ItemPool.HEIMZ_BEANS: case ItemPool.TESLA_BEANS: case ItemPool.MIXED_BEANS: case ItemPool.HELLFIRE_BEANS: case ItemPool.FRIGID_BEANS: case ItemPool.BLACKEST_EYED_PEAS: case ItemPool.STINKBEANS: case ItemPool.PORK_N_BEANS: case ItemPool.PREMIUM_BEANS: // Cans of Beans are off-hand items which can be // "plated" Since they are equipment, they will not be // removed from inventory below. Do it here. if ( responseText.contains( "You acquire" ) ) { ResultProcessor.processResult( item.getNegation() ); } return; case ItemPool.MEMORIES_OF_COW_PUNCHING: Preferences.increment( "awolDeferredPointsCowpuncher" ); break; case ItemPool.MEMORIES_OF_BEAN_SLINGING: Preferences.increment( "awolDeferredPointsBeanslinger" ); break; case ItemPool.MEMORIES_OF_SNAKE_OILING: Preferences.increment( "awolDeferredPointsSnakeoiler" ); break; case ItemPool.CODPIECE: case ItemPool.BASS_CLARINET: case ItemPool.FISH_HATCHET: Preferences.setBoolean( "_floundryItemUsed", true ); break; case ItemPool.BACON_MACHINE: Preferences.setBoolean( "_baconMachineUsed", true ); break; case ItemPool.SCHOOL_OF_HARD_KNOCKS_DIPLOMA: Preferences.setBoolean( "_hardKnocksDiplomaUsed", true ); break; case ItemPool.SOURCE_TERMINAL_PRAM_CHIP: case ItemPool.SOURCE_TERMINAL_GRAM_CHIP: case ItemPool.SOURCE_TERMINAL_SPAM_CHIP: { // Source terminal chips (10 maximum) // You've already installed the maximum number of those that will fit in your Source terminal. // You pull the cover off of your Source terminal and install the chip. It looks like there's room for up to 10 of this particular kind. You have X so far. int total = 10; Matcher chipMatcher = TERMINAL_CHIP_PATTERN.matcher( responseText ); if ( chipMatcher.find() ) { total = StringUtilities.parseInt( chipMatcher.group( 1 ) ); } switch ( itemId ) { case ItemPool.SOURCE_TERMINAL_PRAM_CHIP: Preferences.setInteger( "sourceTerminalPram", total ); break; case ItemPool.SOURCE_TERMINAL_GRAM_CHIP: Preferences.setInteger( "sourceTerminalGram", total ); break; case ItemPool.SOURCE_TERMINAL_SPAM_CHIP: Preferences.setInteger( "sourceTerminalSpam", total ); break; } if ( responseText.contains( "You've already installed" ) ) { return; } break; } case ItemPool.SOURCE_TERMINAL_CRAM_CHIP: case ItemPool.SOURCE_TERMINAL_DRAM_CHIP: case ItemPool.SOURCE_TERMINAL_TRAM_CHIP: case ItemPool.SOURCE_TERMINAL_INGRAM_CHIP: case ItemPool.SOURCE_TERMINAL_DIAGRAM_CHIP: case ItemPool.SOURCE_TERMINAL_ASHRAM_CHIP: case ItemPool.SOURCE_TERMINAL_SCRAM_CHIP: case ItemPool.SOURCE_TERMINAL_TRIGRAM_CHIP: { // Source terminal chip (1 maximum) // You've already installed a ASHRAM chip in your Source terminal String chipName = null; switch ( itemId ) { case ItemPool.SOURCE_TERMINAL_CRAM_CHIP: chipName = "CRAM"; break; case ItemPool.SOURCE_TERMINAL_DRAM_CHIP: chipName = "DRAM"; break; case ItemPool.SOURCE_TERMINAL_TRAM_CHIP: chipName = "TRAM"; break; case ItemPool.SOURCE_TERMINAL_INGRAM_CHIP: chipName = "INGRAM"; break; case ItemPool.SOURCE_TERMINAL_DIAGRAM_CHIP: chipName = "DIAGRAM"; break; case ItemPool.SOURCE_TERMINAL_ASHRAM_CHIP: chipName = "ASHRAM"; break; case ItemPool.SOURCE_TERMINAL_SCRAM_CHIP: chipName = "SCRAM"; break; case ItemPool.SOURCE_TERMINAL_TRIGRAM_CHIP: chipName = "TRIGRAM"; break; } String known = Preferences.getString( "sourceTerminalChips" ); StringBuilder knownString = new StringBuilder(); knownString.append( known ); if ( !known.contains( chipName ) ) { if ( knownString.length() > 0 ) { knownString.append( "," ); } knownString.append( chipName ); Preferences.setString( "sourceTerminalChips", knownString.toString() ); } if ( responseText.contains( "You've already installed" ) ) { return; } break; } case ItemPool.SOURCE_TERMINAL_SUBSTATS_ENH: case ItemPool.SOURCE_TERMINAL_DAMAGE_ENH: case ItemPool.SOURCE_TERMINAL_CRITICAL_ENH: case ItemPool.SOURCE_TERMINAL_PROTECT_ENQ: case ItemPool.SOURCE_TERMINAL_STATS_ENQ: case ItemPool.SOURCE_TERMINAL_COMPRESS_EDU: case ItemPool.SOURCE_TERMINAL_DUPLICATE_EDU: case ItemPool.SOURCE_TERMINAL_PORTSCAN_EDU: case ItemPool.SOURCE_TERMINAL_TURBO_EDU: case ItemPool.SOURCE_TERMINAL_FAMILIAR_EXT: case ItemPool.SOURCE_TERMINAL_PRAM_EXT: case ItemPool.SOURCE_TERMINAL_GRAM_EXT: case ItemPool.SOURCE_TERMINAL_SPAM_EXT: case ItemPool.SOURCE_TERMINAL_CRAM_EXT: case ItemPool.SOURCE_TERMINAL_DRAM_EXT: case ItemPool.SOURCE_TERMINAL_TRAM_EXT: { // Source terminal file (1 maximum) // You've already installed a copy of tram.ext in your Source terminal String fileName = ItemDatabase.getItemName( itemId ).substring( 22 ); String preference = null; if ( fileName.contains( ".edu" ) ) { preference = "sourceTerminalEducateKnown"; } else if ( fileName.contains( ".enq" ) ) { preference = "sourceTerminalEnquiryKnown"; } else if ( fileName.contains( ".enh" ) ) { preference = "sourceTerminalEnhanceKnown"; } else if ( fileName.contains( ".ext" ) ) { preference = "sourceTerminalExtrudeKnown"; } String known = Preferences.getString( preference ); StringBuilder knownString = new StringBuilder(); knownString.append( known ); if ( !known.contains( fileName ) ) { if ( knownString.length() > 0 ) { knownString.append( "," ); } knownString.append( fileName ); Preferences.setString( preference, knownString.toString() ); } if ( responseText.contains( "You've already installed a copy of" ) ) { return; } break; } case ItemPool.DETECTIVE_APPLICATION: Preferences.setBoolean( "hasDetectiveSchool", true ); break; case ItemPool.HOLORECORD_POWERGUY: case ItemPool.HOLORECORD_SHRIEKING_WEASEL: case ItemPool.HOLORECORD_SUPERDRIFTER: case ItemPool.HOLORECORD_LUCKY_STRIKES: case ItemPool.HOLORECORD_DRUNK_UNCLES: case ItemPool.HOLORECORD_EMD: case ItemPool.HOLORECORD_PIGS: if ( responseText.contains( "already a record" ) ) { ResultProcessor.processResult( item ); } break; case ItemPool.BROKEN_CHOCOLATE_POCKETWATCH: // Using your candy screwdriver and your spare gingerbread parts, you manage to repair the pocketwatch if ( !responseText.contains( "manage to repair" ) ) { return; } ResultProcessor.processItem( ItemPool.SPARE_CHOCOLATE_PARTS, -1 ); break; case ItemPool.HOT_JELLY: Preferences.increment( "_hotJellyUses", count ); break; case ItemPool.SPOOKY_JELLY: Preferences.increment( "_spookyJellyUses", count ); break; case ItemPool.SPACEGATE_ACCESS_BADGE: // She doesn't give you the badge back, and when you // ask, she explains that the facility entrance is now // keyed to your genetic signature, and you can come // and go as you please, unless your genes change for // some reason. Preferences.setBoolean( "spacegateAlways", true ); if ( !responseText.contains( "is now keyed to your genetic signature" ) ) { return; } break; case ItemPool.PORTABLE_SPACEGATE: if ( responseText.contains( "already have access to that place" ) ) { // If you already have access it is not consumed return; } Preferences.setBoolean( "_spacegateToday", true ); break; case ItemPool.SPACE_BABY_CHILDRENS_BOOK: // You read the book and learn a few words in the Space // Baby language. Mostly about cute animals. if ( !responseText.contains( "learn a few words" ) ) { // If you already have access it is not consumed return; } ChoiceManager.parseLanguageFluency( responseText, "spaceBabyLanguageFluency" ); break; } if ( CampgroundRequest.isWorkshedItem( itemId ) ) { Preferences.setBoolean( "_workshedItemUsed", true ); if ( responseText.contains( "already rearranged your workshed" ) ) { return; } CampgroundRequest.setCurrentWorkshedItem( ItemPool.get( itemId, 1 ) ); CampgroundRequest.setCampgroundItem( itemId, 1 ); } // Finally, remove the item from inventory if it was successfully used. switch ( consumptionType ) { case KoLConstants.CONSUME_ZAP: case KoLConstants.EQUIP_FAMILIAR: case KoLConstants.EQUIP_ACCESSORY: case KoLConstants.EQUIP_HAT: case KoLConstants.EQUIP_PANTS: case KoLConstants.EQUIP_WEAPON: case KoLConstants.EQUIP_OFFHAND: case KoLConstants.EQUIP_CONTAINER: case KoLConstants.INFINITE_USES: break; default: if ( !ItemDatabase.isReusable( itemId ) ) { ResultProcessor.processResult( item.getNegation() ); } } } private static final String itemToClass( final int itemId ) { String className = Modifiers.getStringModifier( "Item", itemId, "Class" ); return className.equals( "" ) ? null : className; } private static final String itemToSkill( final int itemId ) { String skillName = Modifiers.getStringModifier( "Item", itemId, "Skill" ); return skillName.equals( "" ) ? null : skillName; } private static final void getEvilLevels( final String responseText ) { int total = 0; int alcove = 0; int cranny = 0; int niche = 0; int nook = 0; Matcher matcher = EVILOMETER_PATTERN.matcher( responseText ); if ( matcher.find() ) { total = StringUtilities.parseInt( matcher.group( 1 ) ); alcove = StringUtilities.parseInt( matcher.group( 2 ) ); cranny = StringUtilities.parseInt( matcher.group( 3 ) ); niche = StringUtilities.parseInt( matcher.group( 4 ) ); nook = StringUtilities.parseInt( matcher.group( 5 ) ); } Preferences.setInteger( "cyrptTotalEvilness", total ); Preferences.setInteger( "cyrptAlcoveEvilness", alcove ); Preferences.setInteger( "cyrptCrannyEvilness", cranny ); Preferences.setInteger( "cyrptNicheEvilness", niche ); Preferences.setInteger( "cyrptNookEvilness", nook ); if ( responseText.contains( "give it a proper burial" ) ) { ResultProcessor.removeItem( ItemPool.EVILOMETER ); } } private static final void getBugbearBiodataLevels( String responseText ) { Matcher matcher = UseItemRequest.KEYOTRON_PATTERN.matcher( responseText ); if ( !matcher.find() ) { return; } for ( int i = 1; i <= 9; ++i ) { Object [] data = BugbearManager.idToData( i ); if ( data == null ) { continue; } BugbearManager.setBiodata( data, matcher.group( i ) ); } return; } private static final void showItemUsage( final boolean showHTML, final String text ) { if ( showHTML ) { KoLmafia.showHTML( "inventory.php?action=message", UseItemRequest.trimInventoryText( text ) ); } } private static final String trimInventoryText( String text ) { // Get rid of first row of first table: the "Results" line Matcher matcher = UseItemRequest.ROW_PATTERN.matcher( text ); if ( matcher.find() ) { text = matcher.replaceFirst( "" ); } // Get rid of inventory listing matcher = UseItemRequest.INVENTORY_PATTERN.matcher( text ); if ( matcher.find() ) { text = matcher.replaceFirst( "</table></body>" ); } return text; } public static final AdventureResult extractItem( final String urlString ) { if ( !urlString.startsWith( "inv_use.php" ) && !urlString.startsWith( "inv_eat.php" ) && !urlString.startsWith( "inv_booze.php" ) && !urlString.startsWith( "inv_spleen.php" ) && !urlString.startsWith( "multiuse.php" ) && !urlString.startsWith( "inv_familiar.php" ) && !(urlString.startsWith( "inventory.php" ) && !urlString.contains( "action=ghost" ) && !urlString.contains( "action=hobo" ) && !urlString.contains( "action=slime" ) && !urlString.contains( "action=breakbricko" ) && !urlString.contains( "action=candy" ) ) ) { return null; } return UseItemRequest.extractItem( urlString, true ); } public static final AdventureResult extractItem( final String urlString, final boolean force ) { Matcher itemMatcher = GenericRequest.WHICHITEM_PATTERN.matcher( urlString ); if ( !itemMatcher.find() ) { return null; } int itemId = StringUtilities.parseInt( itemMatcher.group( 1 ) ); if ( ItemDatabase.getItemName( itemId ) == null ) { return null; } int itemCount = 1; if ( urlString.contains( "multiuse.php" ) || urlString.contains( "inv_eat.php" ) || urlString.contains( "inv_booze.php" ) || urlString.contains( "inv_spleen.php" ) || urlString.contains( "inv_use.php" )) { Matcher quantityMatcher = GenericRequest.QUANTITY_PATTERN.matcher( urlString ); if ( quantityMatcher.find() ) { itemCount = StringUtilities.parseInt( quantityMatcher.group( 1 ) ); } } return ItemPool.get( itemId, itemCount ); } public static final AdventureResult extractBingedItem( final String urlString ) { if ( !urlString.startsWith( "inventory.php" ) && !urlString.startsWith( "familiarbinger.php" ) ) { return null; } Matcher itemMatcher = GenericRequest.WHICHITEM_PATTERN.matcher( urlString ); if ( !itemMatcher.find() ) { return null; } int itemId = StringUtilities.parseInt( itemMatcher.group( 1 ) ); int itemCount = 1; Matcher quantityMatcher = GenericRequest.QTY_PATTERN.matcher( urlString ); if ( quantityMatcher.find() ) { itemCount = StringUtilities.parseInt( quantityMatcher.group( 1 ) ); } return ItemPool.get( itemId, itemCount ); } public static final AdventureResult extractAbsorbedItem( final String urlString ) { if ( !urlString.startsWith( "inventory.php" ) ) { return null; } Matcher itemMatcher = UseItemRequest.ABSORB_PATTERN.matcher( urlString ); if ( !itemMatcher.find() ) { return null; } int itemId = StringUtilities.parseInt( itemMatcher.group( 1 ) ); return ItemPool.get( itemId, 1 ); } private static final AdventureResult extractHelper( final String urlString ) { if ( !urlString.startsWith( "inv_eat.php" ) && !urlString.startsWith( "inv_booze.php" ) && !urlString.startsWith( "inv_spleen.php" ) && !urlString.startsWith( "inv_use.php" ) ) { return null; } return UseItemRequest.extractHelper( urlString, true ); } public static final AdventureResult extractHelper( final String urlString, final boolean force ) { Matcher helperMatcher = UseItemRequest.HELPER_PATTERN.matcher( urlString ); if ( !helperMatcher.find() ) { return null; } int itemId = StringUtilities.parseInt( helperMatcher.group( 2 ) ); if ( ItemDatabase.getItemName( itemId ) == null ) { return null; } return ItemPool.get( itemId ); } public static final boolean registerBingeRequest( final String urlString ) { AdventureResult item = UseItemRequest.extractBingedItem( urlString ); if ( item == null ) { return false; } FamiliarData familiar = KoLCharacter.getFamiliar(); int id = familiar.getId(); if ( id != FamiliarPool.HOBO && id != FamiliarPool.GHOST && id != FamiliarPool.SLIMELING && id != FamiliarPool.STOCKING_MIMIC ) { return false; } int itemId = item.getItemId(); int count = item.getCount(); String name = item.getName(); String useString = "feed " + count + " " + name + " to " + familiar.getRace(); if ( id == FamiliarPool.SLIMELING ) { if ( itemId == ItemPool.GNOLLISH_AUTOPLUNGER || ConcoctionDatabase.meatStackCreation( itemId ) != null ) { useString += " (" + count + " more slime stack(s) due)"; } else { // round down for now, since we don't know how this really works float charges = count * EquipmentDatabase.getPower( itemId ) / 10.0F; useString += " (estimated " + charges + " charges)"; } } RequestLogger.updateSessionLog(); RequestLogger.updateSessionLog( useString ); return true; } public static boolean registerRequest( final String urlString ) { // Don't overwrite lastItemUsed when restoratives are used from // the Skills page or quickskills menu. The request was // initially made to inv_use.php (and lastItemUsed was set at // that time), which redirects to skills.php - but without // enough information in the URL to determine exactly what was used. if ( urlString.startsWith( "skills.php" ) ) { return true; } // If we are transferring to or from the closet from the // inventory, we are not "using" the item if ( urlString.contains( "action=closetpull" ) || urlString.contains( "action=closetpush" ) ) { return ClosetRequest.registerRequest( urlString ); } AdventureResult item = UseItemRequest.extractItem( urlString ); if ( item != null && item.getItemId() == ItemPool.CARD_SLEEVE ) { return EquipmentRequest.registerCardSleeve( urlString ); } // Special handling for twisting Boris's Helm when it is equipped if ( item == null && urlString.contains( "action=twisthorns" ) ) { int slot = -1; if ( urlString.contains( "slot=hat" ) ) { slot = EquipmentManager.HAT; } else if ( urlString.contains( "slot=familiarequip" ) ) { slot = EquipmentManager.FAMILIAR; } if ( slot != -1 ) { AdventureResult before = EquipmentManager.getEquipment( slot ); AdventureResult after = ItemPool.get( before.getItemId() == ItemPool.BORIS_HELM ? ItemPool.BORIS_HELM_ASKEW : ItemPool.BORIS_HELM, 1 ); EquipmentManager.setEquipment( slot, after ); RequestLogger.printLine( "Twisted " + before + " into " + after ); return true; } return false; } // Special handling for shaking Jarlsberg's pan when it is equipped if ( item == null && urlString.contains( "action=shakepan" ) ) { AdventureResult before = EquipmentManager.getEquipment( EquipmentManager.OFFHAND ); AdventureResult after = ItemPool.get( before.getItemId() == ItemPool.JARLS_PAN ? ItemPool.JARLS_COSMIC_PAN : ItemPool.JARLS_PAN, 1 ); EquipmentManager.setEquipment( EquipmentManager.OFFHAND, after ); RequestLogger.printLine( "Shook " + before + " into " + after ); return true; } // Special handling for shaking Sneaky Pete's leather jacket when it is equipped if ( item == null && urlString.contains( "action=popcollar" ) ) { AdventureResult before = EquipmentManager.getEquipment( EquipmentManager.SHIRT ); AdventureResult after = ItemPool.get( before.getItemId() == ItemPool.PETE_JACKET ? ItemPool.PETE_JACKET_COLLAR : ItemPool.PETE_JACKET, 1 ); EquipmentManager.setEquipment( EquipmentManager.SHIRT, after ); RequestLogger.printLine( "Popped " + before + " into " + after ); return true; } // Special handling for twisting toggle switch if ( item == null && urlString.contains( "action=togglebutt" ) ) { AdventureResult before = EquipmentManager.getEquipment( EquipmentManager.FAMILIAR ); AdventureResult after = ItemPool.get( before.getItemId() == ItemPool.TOGGLE_SWITCH_BARTEND ? ItemPool.TOGGLE_SWITCH_BOUNCE : ItemPool.TOGGLE_SWITCH_BARTEND, 1 ); EquipmentManager.discardEquipment( before ); EquipmentManager.setEquipment( EquipmentManager.FAMILIAR, after ); RequestLogger.printLine( "Toggled " + before + " into " + after ); return true; } if ( item == null ) { return UseItemRequest.registerBingeRequest( urlString ); } // Delegate to specialized classes as appropriate int itemId = item.getItemId(); boolean isSealFigurine = ItemDatabase.isSealFigurine( itemId ); boolean isBRICKOMonster = ItemDatabase.isBRICKOMonster( itemId ); if ( ( isSealFigurine || isBRICKOMonster ) && !urlString.contains( "checked=1" ) ) { // Only log the second "use" that actually leads to a fight. return true; } switch ( itemId ) { case ItemPool.AWOL_COMMENDATION: return AWOLQuartermasterRequest.registerRequest( urlString ); case ItemPool.BURT: return BURTRequest.registerRequest( urlString ); case ItemPool.FDKOL_COMMENDATION: return FDKOLRequest.registerRequest( urlString, false ); case ItemPool.FUDGE_WAND: return FudgeWandRequest.registerRequest( urlString ); } // Everything below here will work with the item we extracted UseItemRequest.lastItemUsed = item; UseItemRequest.currentItemId = itemId; UseItemRequest.lastHelperUsed = UseItemRequest.extractHelper( urlString ); if ( urlString.startsWith( "inv_booze.php" ) ) { return DrinkItemRequest.registerRequest(); } if ( urlString.startsWith( "inv_eat.php" ) ) { return EatItemRequest.registerRequest(); } if ( urlString.startsWith( "inv_spleen.php" ) ) { return SpleenItemRequest.registerRequest(); } String name = item.getName(); if ( ConsumablesDatabase.getSpleenHit( name ) > 0 ) { return SpleenItemRequest.registerRequest(); } int count = item.getCount(); int consumptionType = ItemDatabase.getConsumptionType( itemId ); String useString = null; switch ( consumptionType ) { case KoLConstants.NO_CONSUME: if ( itemId != ItemPool.LOATHING_LEGION_JACKHAMMER ) { return false; } break; case KoLConstants.CONSUME_USE: case KoLConstants.CONSUME_MULTIPLE: // See if it is a concoction if ( SingleUseRequest.registerRequest( urlString ) || MultiUseRequest.registerRequest( urlString ) ) { return true; } break; case KoLConstants.CONSUME_EAT: // Fortune cookies, for example if ( urlString.startsWith( "inv_use" ) ) { consumptionType = KoLConstants.CONSUME_USE; break; } break; } switch ( itemId ) { case ItemPool.REFLECTION_OF_MAP: useString = "[" + KoLAdventure.getAdventureCount() + "] Reflection of a Map"; KoLAdventure.locationLogged = true; break; case ItemPool.JACKING_MAP: UseItemRequest.lastFruit = null; Matcher m = UseItemRequest.FRUIT_TUBING_PATTERN.matcher( urlString ); if ( m.find() ) { UseItemRequest.lastFruit = ItemPool.get( StringUtilities.parseInt( m.group( 1 ) ), 1 ); useString = "insert " + UseItemRequest.lastFruit + " into pneumatic tube interface"; } break; case ItemPool.EXPRESS_CARD: Preferences.setBoolean( "expressCardUsed", true ); break; case ItemPool.SPICE_MELANGE: Preferences.setBoolean( "spiceMelangeUsed", true ); break; case ItemPool.ULTRA_MEGA_SOUR_BALL: Preferences.setBoolean( "_ultraMegaSourBallUsed", true ); break; case ItemPool.MUNCHIES_PILL: Preferences.increment( "munchiesPillsUsed", count ); break; case ItemPool.DRINK_ME_POTION: Preferences.increment( "pendingMapReflections", count ); break; case ItemPool.EL_VIBRATO_HELMET: case ItemPool.DRONE: if ( UseItemRequest.lastHelperUsed == null ) { return true; } useString = "insert " + UseItemRequest.lastHelperUsed + " into " + UseItemRequest.lastItemUsed; break; case ItemPool.PUNCHCARD_ATTACK: case ItemPool.PUNCHCARD_REPAIR: case ItemPool.PUNCHCARD_BUFF: case ItemPool.PUNCHCARD_MODIFY: case ItemPool.PUNCHCARD_BUILD: if ( KoLCharacter.getFamiliar().getId() != FamiliarPool.MEGADRONE ) { return true; } useString = "insert " + UseItemRequest.lastItemUsed + " into El Vibrato Megadrone"; break; case ItemPool.WRETCHED_SEAL: case ItemPool.CUTE_BABY_SEAL: case ItemPool.ARMORED_SEAL: case ItemPool.ANCIENT_SEAL: case ItemPool.SLEEK_SEAL: case ItemPool.SHADOWY_SEAL: case ItemPool.STINKING_SEAL: case ItemPool.CHARRED_SEAL: case ItemPool.COLD_SEAL: case ItemPool.SLIPPERY_SEAL: case ItemPool.DEPLETED_URANIUM_SEAL: // You only actually use a seal figurine when you // "Begin the Ritual" if ( urlString.contains( "checked" ) ) { return true; } break; case ItemPool.LOATHING_LEGION_KNIFE: case ItemPool.LOATHING_LEGION_MANY_PURPOSE_HOOK: case ItemPool.LOATHING_LEGION_MOONDIAL: case ItemPool.LOATHING_LEGION_NECKTIE: case ItemPool.LOATHING_LEGION_ELECTRIC_KNIFE: case ItemPool.LOATHING_LEGION_CORKSCREW: case ItemPool.LOATHING_LEGION_CAN_OPENER: case ItemPool.LOATHING_LEGION_CHAINSAW: case ItemPool.LOATHING_LEGION_ROLLERBLADES: case ItemPool.LOATHING_LEGION_FLAMETHROWER: case ItemPool.LOATHING_LEGION_DEFIBRILLATOR: case ItemPool.LOATHING_LEGION_DOUBLE_PRISM: case ItemPool.LOATHING_LEGION_TAPE_MEASURE: case ItemPool.LOATHING_LEGION_KITCHEN_SINK: case ItemPool.LOATHING_LEGION_ABACUS: case ItemPool.LOATHING_LEGION_HELICOPTER: case ItemPool.LOATHING_LEGION_PIZZA_STONE: case ItemPool.LOATHING_LEGION_JACKHAMMER: case ItemPool.LOATHING_LEGION_HAMMER: // inv_use.php?whichitem=xxx&pwd&switch=1 // Attempting to use one of these items takes you to a // page where you can select the new form. // inv_use.php?whichitem=xxx&pwd&switch=1&eq=0&fold=yyy // You only lose the item when you switch form. if ( !urlString.contains( "fold" ) ) { return true; } useString = "fold " + UseItemRequest.lastItemUsed; break; case ItemPool.LOATHING_LEGION_UNIVERSAL_SCREWDRIVER: // You can either use the unversal screwdriver to // untinker something or switch forms. // inv_use.php?whichitem=4926&pwd&action=screw&dowhichitem=xxx if ( urlString.contains( "action=screw" ) ) { Matcher matcher = UseItemRequest.DOWHICHITEM_PATTERN.matcher( urlString ); if ( !matcher.find() ) { UseItemRequest.lastUntinker = null; return true; } int uid = StringUtilities.parseInt( matcher.group( 1 ) ); AdventureResult untinker = ItemPool.get( uid, 1 ); String countStr = "1"; if ( urlString.contains( "untinkerall=on" ) ) { untinker = ItemPool.get( uid, untinker.getCount( KoLConstants.inventory ) ); countStr = "*"; } UseItemRequest.lastUntinker = untinker; useString = "unscrew " + countStr + " " + untinker.getName(); break; } if ( !urlString.contains( "fold" ) ) { return true; } useString = "fold " + UseItemRequest.lastItemUsed; break; case ItemPool.LOATHING_LEGION_TATTOO_NEEDLE: // You can either "use" the reusable tattoo needle or // switch forms. if ( urlString.contains( "switch" ) ) { if ( !urlString.contains( "fold" ) ) { return true; } useString = "fold " + UseItemRequest.lastItemUsed; } break; case ItemPool.D10: if ( count == 2 ) { useString = "roll percentile dice"; break; } // Fall through case ItemPool.D4: case ItemPool.D6: case ItemPool.D8: case ItemPool.D12: case ItemPool.D20: useString = "roll " + count + name; break; case ItemPool.WHAT_CARD: case ItemPool.WHEN_CARD: case ItemPool.WHO_CARD: case ItemPool.WHERE_CARD: // Unless you are looking for the answer, using a card // simply presents you with the question. if ( !urlString.contains( "answerplz=1" ) ) { return true; } break; case ItemPool.LITTLE_FIRKIN: case ItemPool.NORMAL_BARREL: case ItemPool.BIG_TUN: case ItemPool.WEATHERED_BARREL: case ItemPool.DUSTY_BARREL: case ItemPool.DISINTEGRATING_BARREL: case ItemPool.MOIST_BARREL: case ItemPool.ROTTING_BARREL: case ItemPool.MOULDERING_BARREL: case ItemPool.BARNACLED_BARREL: useString = urlString.contains( "choice=1" ) ? "Throw a barrel smashing party!" : ( "smash " + name ); break; case ItemPool.HEIMZ_BEANS: case ItemPool.TESLA_BEANS: case ItemPool.MIXED_BEANS: case ItemPool.HELLFIRE_BEANS: case ItemPool.FRIGID_BEANS: case ItemPool.BLACKEST_EYED_PEAS: case ItemPool.STINKBEANS: case ItemPool.PORK_N_BEANS: useString = "plate " + name; break; } if ( useString == null ) { useString = "use " + count + " " + name ; } RequestLogger.updateSessionLog(); RequestLogger.updateSessionLog( useString ); return true; } @Override public int getAdventuresUsed() { return UseItemRequest.getAdventuresUsedByItem( this.itemUsed ); } public static int getAdventuresUsed( final String urlString ) { AdventureResult item = urlString.contains( "action=chateau_painting" ) ? ChateauRequest.CHATEAU_PAINTING : UseItemRequest.extractItem( urlString ); return item == null ? 0 : item.getItemId() == ItemPool.DECK_OF_EVERY_CARD ? DeckOfEveryCardRequest.getAdventuresUsed( urlString ) : UseItemRequest.getAdventuresUsedByItem( item ); } public static int getAdventuresUsedByItem( AdventureResult item ) { int turns = 0; switch ( item.getItemId() ) { case ItemPool.ABYSSAL_BATTLE_PLANS: case ItemPool.BLACK_PUDDING: case ItemPool.CARONCH_MAP: case ItemPool.CHATEAU_WATERCOLOR: case ItemPool.CRUDE_SCULPTURE: case ItemPool.CURSED_PIECE_OF_THIRTEEN: case ItemPool.DECK_OF_EVERY_CARD: case ItemPool.DOLPHIN_WHISTLE: case ItemPool.ENVYFISH_EGG: case ItemPool.FRATHOUSE_BLUEPRINTS: case ItemPool.ICE_SCULPTURE: case ItemPool.PHOTOCOPIED_MONSTER: case ItemPool.RAIN_DOH_MONSTER: case ItemPool.SHAKING_CAMERA: case ItemPool.SHAKING_CRAPPY_CAMERA: case ItemPool.SHAKING_SKULL: case ItemPool.SPOOKY_PUTTY_MONSTER: case ItemPool.WAX_BUGBEAR: case ItemPool.WHITE_PAGE: case ItemPool.XIBLAXIAN_HOLOTRAINING_SIMCODE: case ItemPool.XIBLAXIAN_POLITICAL_PRISONER: case ItemPool.SCREENCAPPED_MONSTER: // Items that can redirect to a fight that costs turns turns = 1; break; case ItemPool.D4: turns = item.getCount() == 100 ? 1 : 0; break; case ItemPool.D10: // 1d10 gives you a monster // 2d10 gives you a random result and takes a turn turns = ( item.getCount() == 1 || item.getCount() == 2 ) ? 1 : 0; break; case ItemPool.REFLECTION_OF_MAP: case ItemPool.RONALD_SHELTER_MAP: case ItemPool.GRIMACE_SHELTER_MAP: case ItemPool.STAFF_GUIDE: // Items that can redirect to a choice adventure turns = 1; break; case ItemPool.DRUM_MACHINE: // Drum machine doesn't take a turn if you have // worm-riding hooks in inventory or equipped. AdventureResult hooks = ItemPool.get( ItemPool.WORM_RIDING_HOOKS, 1 ); if ( KoLConstants.inventory.contains( hooks ) || KoLCharacter.hasEquipped( hooks, EquipmentManager.WEAPON ) ) { return 0; } turns = 1; break; case ItemPool.GONG: // Roachform is three uninterruptible turns turns = Preferences.getInteger( "choiceAdventure276" ) == 1 ? 3 : 0; break; } return turns * item.getCount(); } static private void parseEVHelmet( String responseText ) { List pieces = Arrays.asList( responseText.split( "(<.*?>)+" ) ); int start = pieces.indexOf( "KROKRO LAZAK FULA:" ); if ( start == -1 ) { start = pieces.indexOf( "SPEAR POWER CONDUIT:" ); } if ( start == -1 || pieces.size() - start < 20 ) { KoLmafia.updateDisplay( MafiaState.ERROR, "Unable to parse conduit levels: " + responseText ); return; } int data = 0; // We have 9 conduits, with a value from 0 to 10 each. // A 9-digit base-11 number won't quite fit in an int, but not all // values are possible - the total of all the conduits cannot be // larger than 10 itself. The largest possible value is therefore // A00000000(11), which is just slightly under 2**31. for ( int i = 0; i < 9; ++i ) { String piece = (String) pieces.get( start + i * 2 + 1 ); int value = ItemPool.EV_HELMET_LEVELS.indexOf( piece ); if ( value == -1 ) { KoLmafia.updateDisplay( MafiaState.ERROR, "Unable to parse conduit level: " + piece ); return; } data = data * 11 + value / 2; } Preferences.setInteger( "lastEVHelmetValue", data ); Preferences.setInteger( "lastEVHelmetReset", KoLCharacter.getAscensions() ); } public static final void handleDiary( final String responseText ) { if ( responseText.contains( "Diary" ) ) { QuestDatabase.setQuestIfBetter( Quest.MACGUFFIN, "step2" ); QuestDatabase.setQuestIfBetter( Quest.BLACK, QuestDatabase.FINISHED ); QuestDatabase.setQuestIfBetter( Quest.DESERT, QuestDatabase.STARTED ); QuestDatabase.setQuestIfBetter( Quest.MANOR, QuestDatabase.STARTED ); QuestDatabase.setQuestIfBetter( Quest.SHEN, QuestDatabase.STARTED ); QuestDatabase.setQuestIfBetter( Quest.RON, QuestDatabase.STARTED ); // If Hidden Temple already unlocked, this completes step 1 of Gotta Worship Them All, otherwise start it. String status = Preferences.getInteger( "lastTempleUnlock" ) == KoLCharacter.getAscensions() ? "step1" : QuestDatabase.STARTED; QuestDatabase.setQuestIfBetter( Quest.WORSHIP, status ); } } }