/** * 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.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import net.sourceforge.kolmafia.AdventureResult; import net.sourceforge.kolmafia.KoLCharacter; import net.sourceforge.kolmafia.KoLConstants; import net.sourceforge.kolmafia.KoLConstants.MafiaState; import net.sourceforge.kolmafia.KoLmafia; import net.sourceforge.kolmafia.KoLmafiaCLI; import net.sourceforge.kolmafia.RequestLogger; import net.sourceforge.kolmafia.moods.MoodManager; import net.sourceforge.kolmafia.objectpool.EffectPool; import net.sourceforge.kolmafia.objectpool.IntegerPool; import net.sourceforge.kolmafia.objectpool.ItemPool; import net.sourceforge.kolmafia.objectpool.SkillPool; import net.sourceforge.kolmafia.persistence.ConcoctionDatabase; import net.sourceforge.kolmafia.persistence.EffectDatabase; import net.sourceforge.kolmafia.persistence.ItemDatabase; import net.sourceforge.kolmafia.persistence.SkillDatabase; import net.sourceforge.kolmafia.preferences.Preferences; import net.sourceforge.kolmafia.session.InventoryManager; import net.sourceforge.kolmafia.session.ResultProcessor; import net.sourceforge.kolmafia.utilities.StringUtilities; public class UneffectRequest extends GenericRequest { private final int effectId; private final boolean isShruggable; private final boolean needsCocoa; private final boolean isTimer; private final AdventureResult effect; private static final Set<AdventureResult> currentEffectRemovals = new HashSet<AdventureResult>(); private static final AdventureResult USED_REMEDY = ItemPool.get( ItemPool.REMEDY, -1 ); private static final AdventureResult USED_CURE_ALL = ItemPool.get( ItemPool.ANCIENT_CURE_ALL, -1 ); private static final Pattern ID1_PATTERN = Pattern.compile( "whicheffect=(\\d+)" ); private static final Pattern ID2_PATTERN = Pattern.compile( "whichbuff=(\\d+)" ); public static final HashMap<String,String> EFFECT_SKILL = new HashMap<String,String>(); static { for ( Entry<Integer,String> entry : EffectDatabase.defaultActions.entrySet() ) { if ( entry.getValue().startsWith( "cast 1" ) ) { String effectName = EffectDatabase.getEffectName( entry.getKey() ); String skillName = entry.getValue().substring( 7 ); if ( skillName.contains( "|" ) ) { skillName = skillName.substring( 0, skillName.indexOf( "|" ) ); } UneffectRequest.EFFECT_SKILL.put( effectName, skillName ); } } } public UneffectRequest( final AdventureResult effect ) { super( UneffectRequest.isShruggable( effect.getEffectId() ) ? "charsheet.php" : "uneffect.php" ); this.effect = effect; this.effectId = effect.getEffectId(); this.isShruggable = UneffectRequest.isShruggable( this.effectId ); this.needsCocoa = UneffectRequest.needsCocoa( this.effectId ); this.isTimer = this.effectId >= EffectPool.TIMER1 && this.effectId <= EffectPool.TIMER10; if ( this.isShruggable ) { this.addFormField( "action", "unbuff" ); this.addFormField( "ajax", "1" ); this.addFormField( "whichbuff", String.valueOf( this.effectId ) ); } else { this.addFormField( "using", "Yep." ); this.addFormField( "whicheffect", String.valueOf( this.effectId ) ); this.addFormField( "pwd" ); } } @Override protected boolean retryOnTimeout() { return true; } public static final boolean isRemovable( final String effectName ) { return UneffectRequest.isRemovable( EffectDatabase.getEffectId( effectName ) ); } public static final boolean isRemovable( final int effectId ) { // http://forums.kingdomofloathing.com/vb/showthread.php?t=195401 // // The following effects should now no longer be removable by any means: // Goofball Withdrawal // Soul-Crushing Headache // Coated in Slime // Everything Looks Yellow // Everything Looks Blue // Everything Looks Red // Deep-Tainted Mind // Timer effects switch ( effectId ) { case -1: // So, what about the following? case EffectPool.EAU_DE_TORTUE: case EffectPool.CURSED_BY_RNG: case EffectPool.FORM_OF_BIRD: case EffectPool.GOOFBALL_WITHDRAWAL: case EffectPool.SOUL_CRUSHING_HEADACHE: case EffectPool.COATED_IN_SLIME: case EffectPool.EVERYTHING_LOOKS_YELLOW: case EffectPool.EVERYTHING_LOOKS_BLUE: case EffectPool.EVERYTHING_LOOKS_RED: case EffectPool.DEEP_TAINTED_MIND: case EffectPool.SPIRIT_PARIAH: case EffectPool.BORED_WITH_EXPLOSIONS: case EffectPool.FEELING_QUEASY: return false; default: return true; } } public static final boolean isShruggable( final int effectId ) { if ( effectId >= EffectPool.TIMER1 && effectId <= EffectPool.TIMER10 ) { return true; } if ( effectId == EffectPool.JUST_THE_BEST_ANAPESTS || effectId == EffectPool.REASSURED ) { return true; } if ( effectId == EffectPool.RECORD_HUNGER || effectId == EffectPool.DRUNK_AVUNCULAR || ( effectId >= EffectPool.SHRIEKING_WEASEL && effectId <= EffectPool.SUPERDRIFTING ) ) { return true; } String name = EffectDatabase.getEffectName( effectId ); if ( name == null ) { return false; } int id = SkillDatabase.getSkillId( UneffectRequest.effectToSkill( name ) ); return id != -1 && SkillDatabase.isBuff( id ); } public static final boolean isShruggable( final String effectName ) { int effectId = EffectDatabase.getEffectId( effectName ); return UneffectRequest.isShruggable( effectId ); } public static final boolean needsCocoa( final String effectName ) { return UneffectRequest.needsCocoa( EffectDatabase.getEffectId( effectName ) ); } public static final boolean needsCocoa( final int effectId ) { switch ( effectId ) { case EffectPool.CURSE_OF_CLUMSINESS: case EffectPool.CURSE_OF_DULLNESS: case EffectPool.CURSE_OF_EXPOSURE: case EffectPool.CURSE_OF_FORGETFULNESS: case EffectPool.CURSE_OF_HOLLOWNESS: case EffectPool.CURSE_OF_IMPOTENCE: case EffectPool.CURSE_OF_LONELINESS: case EffectPool.CURSE_OF_MISFORTUNE: case EffectPool.CURSE_OF_SLUGGISHNESS: case EffectPool.CURSE_OF_VULNERABILITY: case EffectPool.CURSE_OF_WEAKNESS: case EffectPool.TOUCHED_BY_A_GHOST: case EffectPool.CHILLED_TO_THE_BONE: case EffectPool.NAUSEATED: return true; } return false; } /** * Given the name of an effect, return the name of the skill that created that effect * * @param effectName The name of the effect * @return skill The name of the skill */ public static final String effectToSkill( final String effectName ) { for ( Entry<String,String> entry : UneffectRequest.EFFECT_SKILL.entrySet() ) { if ( entry.getKey().equalsIgnoreCase( effectName ) ) { return entry.getValue(); } } return null; } public static final String skillToEffect( final String skillName ) { // Some skills can produce different effects depending on current effects or class int skillId = SkillDatabase.getSkillId( skillName ); switch( skillId ) { case SkillPool.SPIRIT_BOON: { String blessing = KoLCharacter.getBlessingType(); return blessing == KoLCharacter.SHE_WHO_WAS_BLESSING ? EffectDatabase.getEffectName( EffectPool.BOON_OF_SHE_WHO_WAS ) : blessing == KoLCharacter.STORM_BLESSING ? EffectDatabase.getEffectName( EffectPool.BOON_OF_THE_STORM_TORTOISE ) : blessing == KoLCharacter.WAR_BLESSING ? EffectDatabase.getEffectName( EffectPool.BOON_OF_THE_WAR_SNAPPER ) : "none"; } case SkillPool.SHE_WHO_WAS_BLESSING: return EffectDatabase.getEffectName( KoLCharacter.getClassType() == KoLCharacter.TURTLE_TAMER ? EffectPool.BLESSING_OF_SHE_WHO_WAS : EffectPool.DISDAIN_OF_SHE_WHO_WAS ); case SkillPool.STORM_BLESSING: return EffectDatabase.getEffectName( KoLCharacter.getClassType() == KoLCharacter.TURTLE_TAMER ? EffectPool.BLESSING_OF_THE_STORM_TORTOISE : EffectPool.DISDAIN_OF_THE_STORM_TORTOISE ); case SkillPool.WAR_BLESSING: return EffectDatabase.getEffectName( KoLCharacter.getClassType() == KoLCharacter.TURTLE_TAMER ? EffectPool.BLESSING_OF_THE_WAR_SNAPPER : EffectPool.DISDAIN_OF_THE_WAR_SNAPPER ); case SkillPool.TURTLE_POWER: { String blessing = KoLCharacter.getBlessingType(); return blessing == KoLCharacter.SHE_WHO_WAS_BLESSING ? EffectDatabase.getEffectName( EffectPool.AVATAR_OF_SHE_WHO_WAS ) : blessing == KoLCharacter.STORM_BLESSING ? EffectDatabase.getEffectName( EffectPool.AVATAR_OF_THE_STORM_TORTOISE ) : blessing == KoLCharacter.WAR_BLESSING ? EffectDatabase.getEffectName( EffectPool.AVATAR_OF_THE_WAR_SNAPPER ) : "none"; } case SkillPool.SHIELD_OF_THE_PASTALORD: return EffectDatabase.getEffectName( KoLCharacter.getClassType() == KoLCharacter.PASTAMANCER ? EffectPool.SHIELD_OF_THE_PASTALORD : EffectPool.FLIMSY_SHIELD_OF_THE_PASTALORD ); case SkillPool.BLOOD_SUGAR_SAUCE_MAGIC: return EffectDatabase.getEffectName( KoLCharacter.getClassType() == KoLCharacter.SAUCEROR ? EffectPool.BLOOD_SUGAR_SAUCE_MAGIC : EffectPool.BLOOD_SUGAR_SAUCE_MAGIC_LITE ); } // Handle remaining skills with a lookup for ( Entry<String,String> entry : UneffectRequest.EFFECT_SKILL.entrySet() ) { if ( entry.getValue().equalsIgnoreCase( skillName ) ) { return entry.getKey(); } } return null; } private static Set REMOVABLE_BY_SKILL; private static final Map<String, Set<Integer>> removeWithSkillMap = new LinkedHashMap<String, Set<Integer>>(); private static Set REMOVABLE_BY_ITEM; private static final Map<Integer, Set<Integer>> removeWithItemMap = new LinkedHashMap<Integer, Set<Integer>>(); public static final void reset() { Set<Integer> removableEffects; removableEffects = new HashSet<Integer>(); removeWithItemMap.put( IntegerPool.get( ItemPool.ANTIDOTE ), removableEffects ); removableEffects.add( IntegerPool.get( EffectPool.HARDLY_POISONED ) ); removableEffects.add( IntegerPool.get( EffectPool.MAJORLY_POISONED ) ); removableEffects.add( IntegerPool.get( EffectPool.A_LITTLE_BIT_POISONED ) ); removableEffects.add( IntegerPool.get( EffectPool.SOMEWHAT_POISONED ) ); removableEffects.add( IntegerPool.get( EffectPool.REALLY_QUITE_POISONED ) ); removableEffects = new HashSet<Integer>(); removeWithItemMap.put( IntegerPool.get( ItemPool.TINY_HOUSE ), removableEffects ); removableEffects.add( IntegerPool.get( EffectPool.BEATEN_UP ) ); removableEffects.add( IntegerPool.get( EffectPool.CONFUSED ) ); removableEffects.add( IntegerPool.get( EffectPool.EMBARRASSED ) ); removableEffects.add( IntegerPool.get( EffectPool.SUNBURNED ) ); removableEffects.add( IntegerPool.get( EffectPool.WUSSINESS ) ); removableEffects = new HashSet<Integer>(); removeWithItemMap.put( IntegerPool.get( ItemPool.TEARS ), removableEffects ); removableEffects.add( IntegerPool.get( EffectPool.BEATEN_UP ) ); removableEffects = new HashSet<Integer>(); removeWithItemMap.put( IntegerPool.get( ItemPool.TRIPPLES ), removableEffects ); removableEffects.add( IntegerPool.get( EffectPool.BEATEN_UP ) ); removableEffects = new HashSet<Integer>(); removeWithItemMap.put( IntegerPool.get( ItemPool.HOT_DREADSYLVANIAN_COCOA ), removableEffects ); removableEffects.add( IntegerPool.get( EffectPool.TOUCHED_BY_A_GHOST ) ); removableEffects.add( IntegerPool.get( EffectPool.CHILLED_TO_THE_BONE ) ); removableEffects.add( IntegerPool.get( EffectPool.NAUSEATED ) ); removableEffects.add( IntegerPool.get( EffectPool.CURSE_OF_HOLLOWNESS ) ); removableEffects.add( IntegerPool.get( EffectPool.CURSE_OF_VULNERABILITY ) ); removableEffects.add( IntegerPool.get( EffectPool.CURSE_OF_EXPOSURE ) ); removableEffects.add( IntegerPool.get( EffectPool.CURSE_OF_IMPOTENCE ) ); removableEffects.add( IntegerPool.get( EffectPool.CURSE_OF_DULLNESS ) ); removableEffects.add( IntegerPool.get( EffectPool.CURSE_OF_WEAKNESS ) ); removableEffects.add( IntegerPool.get( EffectPool.CURSE_OF_SLUGGISHNESS ) ); removableEffects.add( IntegerPool.get( EffectPool.CURSE_OF_FORGETFULNESS ) ); removableEffects.add( IntegerPool.get( EffectPool.CURSE_OF_MISFORTUNE ) ); removableEffects.add( IntegerPool.get( EffectPool.CURSE_OF_CLUMSINESS ) ); removableEffects.add( IntegerPool.get( EffectPool.CURSE_OF_LONELINESS ) ); UneffectRequest.REMOVABLE_BY_ITEM = removeWithItemMap.entrySet(); removableEffects = new HashSet<Integer>(); removeWithSkillMap.put( "Tongue of the Walrus", removableEffects ); removableEffects.add( IntegerPool.get( EffectPool.AXE_WOUND ) ); removableEffects.add( IntegerPool.get( EffectPool.BEATEN_UP ) ); removableEffects.add( IntegerPool.get( EffectPool.GRILLED ) ); removableEffects.add( IntegerPool.get( EffectPool.HALF_EATEN_BRAIN ) ); removableEffects.add( IntegerPool.get( EffectPool.MISSING_FINGERS ) ); removableEffects.add( IntegerPool.get( EffectPool.SUNBURNED ) ); removableEffects = new HashSet<Integer>(); removeWithSkillMap.put( "Disco Nap", removableEffects ); removableEffects.add( IntegerPool.get( EffectPool.CONFUSED ) ); removableEffects.add( IntegerPool.get( EffectPool.EMBARRASSED ) ); removableEffects.add( IntegerPool.get( EffectPool.SLEEPY ) ); removableEffects.add( IntegerPool.get( EffectPool.SUNBURNED ) ); removableEffects.add( IntegerPool.get( EffectPool.WUSSINESS ) ); if ( KoLCharacter.hasSkill( "Adventurer of Leisure" ) ) { removableEffects.add( IntegerPool.get( EffectPool.AFFRONTED_DECENCY ) ); removableEffects.add( IntegerPool.get( EffectPool.APATHY ) ); removableEffects.add( IntegerPool.get( EffectPool.CONSUMED_BY_FEAR ) ); removableEffects.add( IntegerPool.get( EffectPool.CUNCTATITIS ) ); removableEffects.add( IntegerPool.get( EffectPool.EASILY_EMBARRASSED ) ); removableEffects.add( IntegerPool.get( EffectPool.EXISTENTIAL_TORMENT ) ); removableEffects.add( IntegerPool.get( EffectPool.LIGHT_HEADED ) ); removableEffects.add( IntegerPool.get( EffectPool.N_SPATIAL_VISION ) ); removableEffects.add( IntegerPool.get( EffectPool.PRESTIDIGYSFUNCTION ) ); removableEffects.add( IntegerPool.get( EffectPool.RAINY_SOUL_MIASMA ) ); removableEffects.add( IntegerPool.get( EffectPool.SOCIALISMYDIA ) ); removableEffects.add( IntegerPool.get( EffectPool.TENUOUS_GRIP_ON_REALITY ) ); removableEffects.add( IntegerPool.get( EffectPool.TETANUS ) ); removableEffects.add( IntegerPool.get( EffectPool.THE_COLORS ) ); removableEffects.add( IntegerPool.get( EffectPool.THE_DISEASE ) ); } // If it can be removed by Shake It Off, it can also be removed by Hot Tub removableEffects = new HashSet<Integer>(); removeWithSkillMap.put( "Shake It Off", removableEffects ); removableEffects.add( IntegerPool.get( EffectPool.A_REVOLUTION_IN_YOUR_MOUTH ) ); removableEffects.add( IntegerPool.get( EffectPool.AFFRONTED_DECENCY ) ); removableEffects.add( IntegerPool.get( EffectPool.ALL_COVERED_IN_WHATSIT ) ); removableEffects.add( IntegerPool.get( EffectPool.APATHY ) ); removableEffects.add( IntegerPool.get( EffectPool.AXE_WOUND ) ); removableEffects.add( IntegerPool.get( EffectPool.BARKING_DOGS ) ); removableEffects.add( IntegerPool.get( EffectPool.BEATEN_UP ) ); removableEffects.add( IntegerPool.get( EffectPool.BEER_IN_YOUR_SHOES ) ); removableEffects.add( IntegerPool.get( EffectPool.BLOODY_HAND ) ); removableEffects.add( IntegerPool.get( EffectPool.CONFUSED ) ); removableEffects.add( IntegerPool.get( EffectPool.CONSUMED_BY_FEAR ) ); removableEffects.add( IntegerPool.get( EffectPool.CORRODED_WEAPON ) ); removableEffects.add( IntegerPool.get( EffectPool.CUNCTATITIS ) ); removableEffects.add( IntegerPool.get( EffectPool.DEADENED_PALATE ) ); removableEffects.add( IntegerPool.get( EffectPool.EASILY_EMBARRASSED ) ); removableEffects.add( IntegerPool.get( EffectPool.EMBARRASSED ) ); removableEffects.add( IntegerPool.get( EffectPool.EXISTENTIAL_TORMENT ) ); removableEffects.add( IntegerPool.get( EffectPool.FLARED_NOSTRILS ) ); removableEffects.add( IntegerPool.get( EffectPool.GRILLED ) ); removableEffects.add( IntegerPool.get( EffectPool.HALF_EATEN_BRAIN ) ); removableEffects.add( IntegerPool.get( EffectPool.HERNIA ) ); removableEffects.add( IntegerPool.get( EffectPool.LIGHT_HEADED ) ); removableEffects.add( IntegerPool.get( EffectPool.MISSING_FINGERS ) ); removableEffects.add( IntegerPool.get( EffectPool.N_SPATIAL_VISION ) ); removableEffects.add( IntegerPool.get( EffectPool.NATURAL_1 ) ); removableEffects.add( IntegerPool.get( EffectPool.PRESTIDIGYSFUNCTION ) ); removableEffects.add( IntegerPool.get( EffectPool.RAINY_SOUL_MIASMA ) ); removableEffects.add( IntegerPool.get( EffectPool.SLEEPY ) ); removableEffects.add( IntegerPool.get( EffectPool.SOCIALISMYDIA ) ); removableEffects.add( IntegerPool.get( EffectPool.STRANGULATED ) ); removableEffects.add( IntegerPool.get( EffectPool.SUNBURNED ) ); removableEffects.add( IntegerPool.get( EffectPool.TANGLED_UP ) ); removableEffects.add( IntegerPool.get( EffectPool.TEMPORARY_BLINDNESS ) ); removableEffects.add( IntegerPool.get( EffectPool.TENUOUS_GRIP_ON_REALITY ) ); removableEffects.add( IntegerPool.get( EffectPool.TETANUS ) ); removableEffects.add( IntegerPool.get( EffectPool.TOAD_IN_THE_HOLE ) ); removableEffects.add( IntegerPool.get( EffectPool.TURNED_INTO_A_SKELETON ) ); removableEffects.add( IntegerPool.get( EffectPool.THE_COLORS ) ); removableEffects.add( IntegerPool.get( EffectPool.THE_DISEASE ) ); removableEffects.add( IntegerPool.get( EffectPool.WUSSINESS ) ); removableEffects = new HashSet<Integer>(); removeWithSkillMap.put( "Pep Talk", removableEffects ); removableEffects.add( IntegerPool.get( EffectPool.OVERCONFIDENT ) ); removableEffects = new HashSet<Integer>(); removeWithSkillMap.put( "Blood Sugar Sauce Magic", removableEffects ); removableEffects.add( IntegerPool.get( EffectPool.BLOOD_SUGAR_SAUCE_MAGIC_LITE ) ); removableEffects.add( IntegerPool.get( EffectPool.BLOOD_SUGAR_SAUCE_MAGIC ) ); removableEffects = new HashSet<Integer>(); removeWithSkillMap.put( "Spirit of Nothing", removableEffects ); removableEffects.add( IntegerPool.get( EffectPool.SPIRIT_OF_CAYENNE ) ); removableEffects.add( IntegerPool.get( EffectPool.SPIRIT_OF_PEPPERMINT ) ); removableEffects.add( IntegerPool.get( EffectPool.SPIRIT_OF_GARLIC ) ); removableEffects.add( IntegerPool.get( EffectPool.SPIRIT_OF_WORMWOOD ) ); removableEffects.add( IntegerPool.get( EffectPool.SPIRIT_OF_BACON_GREASE ) ); removableEffects = new HashSet<Integer>(); removeWithSkillMap.put( "Iron Palm Technique", removableEffects ); removableEffects.add( IntegerPool.get( EffectPool.IRON_PALMS ) ); removableEffects = new HashSet<Integer>(); removeWithSkillMap.put( "Absorb Cowrruption", removableEffects ); removableEffects.add( IntegerPool.get( EffectPool.COWRRUPTION ) ); removableEffects = new HashSet<Integer>(); removeWithSkillMap.put( "Gelatinous Reconstruction", removableEffects ); removableEffects.add( IntegerPool.get( EffectPool.BEATEN_UP ) ); UneffectRequest.REMOVABLE_BY_SKILL = removeWithSkillMap.entrySet(); } static { UneffectRequest.reset(); } public static void removeEffectsWithItem( final int itemId ) { Set<Integer> effects = UneffectRequest.removeWithItemMap.get( IntegerPool.get( itemId ) ); UneffectRequest.removeEffects( effects ); } public static void removeEffectsWithSkill( final int skillId ) { String skillName = SkillDatabase.getSkillName( skillId ); if ( skillName == null ) { return; } Set<Integer> effects = UneffectRequest.removeWithSkillMap.get( skillName ); UneffectRequest.removeEffects( effects ); } private static void removeEffects( final Set<Integer> effects ) { if ( effects == null ) { return; } for ( Integer effectId : effects ) { AdventureResult effect = EffectPool.get( effectId ); KoLConstants.activeEffects.remove( effect ); } } public static String getUneffectSkill( final int effectId ) { Iterator skillIterator = UneffectRequest.REMOVABLE_BY_SKILL.iterator(); while ( skillIterator.hasNext() ) { Entry removable = (Entry) skillIterator.next(); Set removables = (Set) removable.getValue(); if ( !removables.contains( effectId ) ) { continue; } String skillName = (String) removable.getKey(); if ( KoLCharacter.hasSkill( skillName ) ) { return "cast " + skillName; } } return ""; } public static String getUneffectSkill( final String effectName ) { int effectId = EffectDatabase.getEffectId( effectName ); return UneffectRequest.getUneffectSkill( effectId ); } private String getAction() { Boolean canRemoveWithHotTub = false; String name = this.effect.getName(); int effectId = this.effectId; // If there's an action defined in your mood, use it. String action = MoodManager.getDefaultAction( "gain_effect", name ); String skillName = null; if ( action.startsWith( "cast " ) ) { skillName = action.substring( 5 ); } else if ( action.startsWith( "skill " ) ) { skillName = action.substring( 6 ); } if ( skillName != null && !KoLCharacter.hasSkill( skillName ) ) { action = ""; } if ( !action.equals( "" ) && !action.startsWith( "uneffect " ) ) { KoLmafia.updateDisplay( name + " will be removed via pre-defined trigger (" + action + ")..." ); return action; } // If it's shruggable, then the cleanest way is to just shrug it. if ( this.isShruggable ) { return "uneffect [" + effectId + "]"; } // See if it can be removed by a skill. Iterator skillIterator = UneffectRequest.REMOVABLE_BY_SKILL.iterator(); while ( skillIterator.hasNext() ) { Entry removable = (Entry) skillIterator.next(); Set removables = (Set) removable.getValue(); if ( !removables.contains( effectId ) ) { continue; } skillName = (String) removable.getKey(); // If Shake It Off can remove it, so can Hot Tub if ( skillName.equals( "Shake It Off" ) ) { canRemoveWithHotTub = true; } if ( KoLCharacter.hasSkill( skillName ) ) { KoLmafia.updateDisplay( name + " will be removed by skill " + skillName + "..." ); return "cast " + skillName; } } // Can we use Hot Tub to remove it, Hot Tubs available and allowed to use them ? // In Hardcore or Ronin, antidotes are shorter of supply, so use Hot Tub in preference if ( canRemoveWithHotTub && InventoryManager.getCount( ItemPool.VIP_LOUNGE_KEY ) > 0 && !KoLCharacter.inBadMoon() && Preferences.getInteger( "_hotTubSoaks" ) < 5 && Preferences.getBoolean( "uneffectWithHotTub" ) && !KoLCharacter.canInteract() ) { KoLmafia.updateDisplay( name + " will be removed by hot tub..." ); return "hottub"; } // See if it can be removed by an item. boolean hasRemedy = InventoryManager.hasItem( ItemPool.REMEDY ) || InventoryManager.hasItem( ItemPool.ANCIENT_CURE_ALL ); Iterator itemIterator = UneffectRequest.REMOVABLE_BY_ITEM.iterator(); while ( itemIterator.hasNext() ) { Entry removable = (Entry) itemIterator.next(); Set removables = (Set) removable.getValue(); if ( !removables.contains( effectId ) ) { continue; } int itemId = ( (Integer) removable.getKey() ).intValue(); String itemName = ItemDatabase.getItemName( itemId ); if ( InventoryManager.hasItem( itemId ) || InventoryManager.canUseNPCStores( itemId ) || InventoryManager.canUseCoinmasters( itemId ) || (this.needsCocoa || !hasRemedy) && ( InventoryManager.canUseMall( itemId ) || InventoryManager.canUseClanStash( itemId ) ) ) { KoLmafia.updateDisplay( name + " will be removed by item " + itemName + "..." ); return "use 1 [" + itemId + "]"; } } // Default to using a remedy. KoLmafia.updateDisplay( name + " cannot be removed with an available item or skill..." ); return "uneffect [" + effectId + "]"; } @Override public void run() { int index = KoLConstants.activeEffects.indexOf( this.effect ); if ( index == -1 ) { KoLmafia.updateDisplay( MafiaState.ERROR, "You don't have that effect." ); return; } AdventureResult effect = (AdventureResult) KoLConstants.activeEffects.get( index ); if ( !UneffectRequest.isRemovable( this.effectId ) ) { KoLmafia.updateDisplay( MafiaState.ERROR, effect.getName() + " is unremovable." ); return; } if ( effect.getCount() == Integer.MAX_VALUE && this.effectId != EffectPool.OVERCONFIDENT && this.effectId != EffectPool.BLOOD_SUGAR_SAUCE_MAGIC_LITE && this.effectId != EffectPool.BLOOD_SUGAR_SAUCE_MAGIC ) { KoLmafia.updateDisplay( MafiaState.ERROR, effect.getName() + " is intrinsic and cannot be removed." ); return; } if ( !UneffectRequest.currentEffectRemovals.contains( effect ) ) { String action = this.getAction(); if ( !action.equals( "" ) && !action.startsWith( "uneffect" ) && !action.startsWith( "shrug" ) && !action.startsWith( "remedy" ) ) { UneffectRequest.currentEffectRemovals.add( effect ); KoLmafiaCLI.DEFAULT_SHELL.executeLine( action ); UneffectRequest.currentEffectRemovals.remove( effect ); return; } } // If you either have cocoa in inventory or allow purchasing it // via coinmaster or mall, we will have done so above. if ( this.needsCocoa ) { KoLmafia.updateDisplay( MafiaState.ERROR, effect.getName() + " can be removed only with hot Dreadsylvanian cocoa." ); return; } if ( this.isTimer ) { KoLmafia.updateDisplay( "Canceling your timer..." ); } else if ( this.isShruggable ) { KoLmafia.updateDisplay( "Shrugging off your buff..." ); } else if ( InventoryManager.hasItem( ItemPool.ANCIENT_CURE_ALL ) ) { KoLmafia.updateDisplay( "Using ancient cure-all..." ); } else if ( InventoryManager.retrieveItem( ItemPool.REMEDY ) ) { KoLmafia.updateDisplay( "Using soft green whatever..." ); } else { return; } super.run(); } @Override public void processResults() { // Using a remedy no longer says "Effect removed." If you have // more remedies available, it gives a new list, not containing // the effect. If you have no more remedies, it says "You don't // have any more green fluffy antidote echo drops, or whatever // they're called." if ( this.responseText == null ) { // What's wrong? return; } KoLConstants.activeEffects.remove( this.effect ); // If you lose Inigo's, what you can craft changes if ( this.effectId == EffectPool.INIGOS || this.effectId == EffectPool.CRAFT_TEA ) { ConcoctionDatabase.setRefreshNeeded( true ); } // If Gar-ish is gained or lost and autoGarish isn't set, benefit of Lasagna changes if ( this.effectId == EffectPool.GARISH && !Preferences.getBoolean( "autoGarish" ) ) { ConcoctionDatabase.setRefreshNeeded( true ); } KoLmafia.updateDisplay( this.effect.getName() + " removed." ); } public static final boolean registerRequest( final String location ) { if ( !location.startsWith( "uneffect.php" ) && !location.startsWith( "charsheet.php" ) ) { return false; } if ( !location.contains( "?" ) ) { return true; } Matcher idMatcher = location.startsWith( "uneffect.php" ) ? UneffectRequest.ID1_PATTERN.matcher( location ) : UneffectRequest.ID2_PATTERN.matcher( location ); if ( !idMatcher.find() ) { return true; } int id = StringUtilities.parseInt( idMatcher.group( 1 ) ); String name = EffectDatabase.getEffectName( id ); if ( UneffectRequest.isRemovable( id ) && location.startsWith( "uneffect" ) ) { if ( InventoryManager.getCount( ItemPool.ANCIENT_CURE_ALL ) > 0 ) { ResultProcessor.processResult( UneffectRequest.USED_CURE_ALL ); } else { ResultProcessor.processResult( UneffectRequest.USED_REMEDY ); } } RequestLogger.updateSessionLog(); RequestLogger.updateSessionLog( "uneffect " + name ); return true; } }