/** * Copyright (c) 2005-2017, KoLmafia development team * http://kolmafia.sourceforge.net/ * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * [1] Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * [2] Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * [3] Neither the name "KoLmafia" nor the names of its contributors may * be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package net.sourceforge.kolmafia.session; import java.util.regex.Matcher; import java.util.regex.Pattern; import net.sourceforge.kolmafia.AdventureResult; import net.sourceforge.kolmafia.AreaCombatData; import net.sourceforge.kolmafia.CoinmasterData; import net.sourceforge.kolmafia.KoLCharacter; import net.sourceforge.kolmafia.KoLConstants; import net.sourceforge.kolmafia.MonsterData; import net.sourceforge.kolmafia.RequestLogger; import net.sourceforge.kolmafia.combat.MonsterStatusTracker; 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.MonsterDatabase; import net.sourceforge.kolmafia.persistence.QuestDatabase; import net.sourceforge.kolmafia.preferences.Preferences; import net.sourceforge.kolmafia.request.CoinMasterRequest; import net.sourceforge.kolmafia.request.DimemasterRequest; import net.sourceforge.kolmafia.request.QuartersmasterRequest; import net.sourceforge.kolmafia.session.EquipmentManager; import net.sourceforge.kolmafia.utilities.StringUtilities; public class IslandManager { private static final AreaCombatData fratboyBattlefield = AdventureDatabase.getAreaCombatData( "The Battlefield (Frat Uniform)" ); private static final AreaCombatData hippyBattlefield = AdventureDatabase.getAreaCombatData( "The Battlefield (Hippy Uniform)" ); public static final boolean isBattlefieldMonster() { return IslandManager.isBattlefieldMonster( MonsterStatusTracker.getLastMonster() ); } public static final boolean isBattlefieldMonster( final String name ) { MonsterData monster = MonsterDatabase.findMonster( name, false ); return IslandManager.isBattlefieldMonster( monster ); } public static final boolean isBattlefieldMonster( MonsterData monster ) { return IslandManager.fratboyBattlefield.hasMonster( monster ) || IslandManager.hippyBattlefield.hasMonster( monster ); } public static final boolean isFratboyBattlefieldMonster( MonsterData monster ) { if ( monster == null ) { return false; } return IslandManager.fratboyBattlefield.hasMonster( monster ); } public static final boolean isHippyBattlefieldMonster( MonsterData monster ) { if ( monster == null ) { return false; } return IslandManager.hippyBattlefield.hasMonster( monster ); } private static final Pattern MAP_PATTERN = Pattern.compile( "bfleft(\\d*).*bfright(\\d*)", Pattern.DOTALL ); private static final Pattern JUNKYARD_PATTERN = Pattern.compile( "(?:The last time I saw my|muttering something about a(?: pair of)?) (.*?)(?:, it was|, they were| and) (.*?)[.<]", Pattern.DOTALL ); private static String missingGremlinTool = null; // Data about current fight private static boolean fratboy = false; private static int lastFratboysDefeated = 0; private static int lastHippiesDefeated = 0; private static int fratboysDefeated = 0; private static int fratboyImage = 0; private static int fratboyMin = 0; private static int fratboyMax = 0; private static int hippiesDefeated = 0; private static int hippyImage = 0; private static int hippyMin = 0; private static int hippyMax = 0; // Data about sidequests private static String currentJunkyardTool = ""; private static String currentJunkyardLocation = ""; private static int currentNunneryMeat = 0; public enum Quest { NONE, JUNKYARD, ORCHARD, ARENA, FARM, LIGHTHOUSE, NUNS, CAMP } public static final void resetIsland() { Preferences.setInteger( "fratboysDefeated", 0 ); Preferences.setInteger( "hippiesDefeated", 0 ); Preferences.setString( "sidequestArenaCompleted", "none" ); Preferences.setString( "sidequestFarmCompleted", "none" ); Preferences.setString( "sidequestJunkyardCompleted", "none" ); Preferences.setString( "sidequestLighthouseCompleted", "none" ); Preferences.setString( "sidequestNunsCompleted", "none" ); Preferences.setString( "sidequestOrchardCompleted", Preferences.getString( "currentHippyStore" ) ); Preferences.setString( "currentJunkyardTool", "" ); Preferences.setString( "currentJunkyardLocation", "" ); Preferences.setInteger( "currentNunneryMeat", 0 ); Preferences.setInteger( "lastFratboyCall", -1 ); Preferences.setInteger( "lastHippyCall", -1 ); Preferences.setInteger( "availableDimes", 0 ); Preferences.setInteger( "availableQuarters", 0 ); Preferences.setString( "sideDefeated", "neither" ); Preferences.setString( "warProgress", "unstarted" ); Preferences.setInteger( "flyeredML", 0 ); } public static final void ensureUpdatedBigIsland() { int lastAscension = Preferences.getInteger( "lastBattlefieldReset" ); if ( lastAscension < KoLCharacter.getAscensions() ) { Preferences.setInteger( "lastBattlefieldReset", KoLCharacter.getAscensions() ); IslandManager.resetIsland(); } // Set variables from user settings IslandManager.fratboysDefeated = Preferences.getInteger( "fratboysDefeated" ); IslandManager.hippiesDefeated = Preferences.getInteger( "hippiesDefeated" ); IslandManager.currentJunkyardTool = Preferences.getString( "currentJunkyardTool" ); IslandManager.currentJunkyardLocation = Preferences.getString( "currentJunkyardLocation" ); IslandManager.currentNunneryMeat = Preferences.getInteger( "currentNunneryMeat" ); } private static Quest quest = Quest.NONE; // Crowther spaded how many kills it takes to display an image in: // http://jick-nerfed.us/forums/viewtopic.php?p=58270#58270 private static final int[] IMAGES = { 0, // Image 0 3, // Image 1 9, // Image 2 17, // Image 3 28, // Image 4 40, // Image 5 52, // Image 6 64, // Image 7 80, // Image 8 96, // Image 9 114, // Image 10 132, // Image 11 152, // Image 12 172, // Image 13 192, // Image 14 224, // Image 15 258, // Image 16 294, // Image 17 332, // Image 18 372, // Image 19 414, // Image 20 458, // Image 21 506, // Image 22 556, // Image 23 606, // Image 24 658, // Image 25 711, // Image 26 766, // Image 27 822, // Image 28 880, // Image 29 939, // Image 30 999, // Image 31 1000 // Image 32 }; public static final boolean fratboy() { return IslandManager.fratboy; } public static final int lastFratboysDefeated() { return IslandManager.lastFratboysDefeated; } public static final int lastHippiesDefeated() { return IslandManager.lastHippiesDefeated; } public static final int fratboysDefeated() { return IslandManager.fratboysDefeated; } public static final int hippiesDefeated() { return IslandManager.hippiesDefeated; } private static final String sideSummary( final String side, final int kills, final int image, int min, final int max ) { if ( kills > min ) { min = kills; } int minLeft = 1000 - max; int maxLeft = 1000 - min; String range = minLeft == maxLeft ? String.valueOf( minLeft ) : String.valueOf( minLeft ) + "-" + String.valueOf( maxLeft ); return kills + " " + side + " defeated; " + range + " left (image " + image + ")."; } public static final String sideSummary( final String side ) { return side.equals( "frat boys" ) ? IslandManager.sideSummary( side, IslandManager.fratboysDefeated, IslandManager.fratboyImage, IslandManager.fratboyMin, IslandManager.fratboyMax ) : side.equals( "hippies" ) ? IslandManager.sideSummary( side, IslandManager.hippiesDefeated, IslandManager.hippyImage, IslandManager.hippyMin, IslandManager.hippyMax ) : ""; } public static final int currentNunneryMeat() { return IslandManager.currentNunneryMeat; } public static final int fratboysDefeatedPerBattle() { return IslandManager.sidequestFactor( "hippy" ) + ( Preferences.getString( "peteMotorbikeCowling" ).equals( "Rocket Launcher" ) ? 3 : 0 ); } public static final int hippiesDefeatedPerBattle() { return IslandManager.sidequestFactor( "fratboy" ) + ( Preferences.getString( "peteMotorbikeCowling" ).equals( "Rocket Launcher" ) ? 3 : 0 ); } private static final String[] SIDEQUEST_PREFERENCES = { "sidequestArenaCompleted", "sidequestFarmCompleted", "sidequestJunkyardCompleted", "sidequestLighthouseCompleted", "sidequestNunsCompleted", "sidequestOrchardCompleted", }; private static final int sidequestFactor( final String completer ) { int factor = 1; for ( int i = 0; i < IslandManager.SIDEQUEST_PREFERENCES.length; ++i ) { String pref = Preferences.getString( IslandManager.SIDEQUEST_PREFERENCES[ i ] ); if ( pref.equals( completer ) ) { factor *= 2; } } return factor; } public static final void startJunkyardQuest() { resetGremlinTool(); } public static final void resetGremlinTool() { IslandManager.missingGremlinTool = null; IslandManager.currentJunkyardTool = ""; Preferences.setString( "currentJunkyardTool", "" ); IslandManager.currentJunkyardLocation = "Yossarian"; Preferences.setString( "currentJunkyardLocation", "Yossarian" ); } public static final String missingGremlinTool() { return IslandManager.missingGremlinTool; } public static final String currentJunkyardTool() { return IslandManager.currentJunkyardTool; } public static final String currentJunkyardLocation() { return IslandManager.currentJunkyardLocation; } public static final String warProgress() { return Preferences.getString( "warProgress" ); } public static final String currentIsland() { String progress = IslandManager.warProgress(); if ( progress.equals( "finished" ) ) { return "postwarisland.php"; } if ( progress.equals( "started" ) ) { return "bigisland.php"; } return "bogus.php"; } public static String questCompleter( final String preference ) { String quest = Preferences.getString( preference ); if ( quest.equals( "hippy" ) ) return "hippies"; if ( quest.equals( "fratboy" ) ) return "fratboys"; return "none"; } public static final String warWinner() { String loser = Preferences.getString( "sideDefeated" ); if ( loser.equals( "hippies" ) ) { return "fratboys"; } if ( loser.equals( "fratboys" ) ) { return "hippies"; } return "neither"; } public static final void startFight() { IslandManager.missingGremlinTool = null; } public static void handleGremlin( final String responseText ) { // Batwinged Gremlin has molybdenum hammer OR // "It does a bombing run over your head..." // Erudite Gremlin has molybdenum crescent wrench OR // "He uses the random junk around him to make an automatic // eyeball-peeler..." // Spider Gremlin has molybdenum pliers OR // "It bites you in the fibula with its mandibles..." // Vegetable Gremlin has molybdenum screwdriver OR // "It picks a beet off of itself and beats you with it..." // "It picks a radish off of itself and tosses it at you..." String text = responseText; if ( text.contains( "bombing run" ) ) { IslandManager.missingGremlinTool = "molybdenum hammer"; } else if ( text.contains( "eyeball-peeler" ) ) { IslandManager.missingGremlinTool = "molybdenum crescent wrench"; } else if ( text.contains( "fibula" ) ) { IslandManager.missingGremlinTool = "molybdenum pliers"; } else if ( text.contains( "off of itself" ) ) { IslandManager.missingGremlinTool = "molybdenum screwdriver"; } } public static final void addNunneryMeat( final AdventureResult result ) { int delta = result.getCount(); IslandManager.currentNunneryMeat = Preferences.increment( "currentNunneryMeat", delta, 100000, false ); int recovered = IslandManager.currentNunneryMeat; int remaining = 100000 - recovered; String message = "The nuns take " + KoLConstants.COMMA_FORMAT.format( delta ) + " Meat; " + KoLConstants.COMMA_FORMAT.format( recovered ) + " recovered, " + KoLConstants.COMMA_FORMAT.format( remaining ) + " left to recover."; RequestLogger.updateSessionLog( message ); RequestLogger.printLine( message ); } /* * Methods to mine data from request responses */ private static final String[][] HIPPY_MESSAGES = { // 2 total { // You see one of your frat brothers take out an // M.C. Escher drawing and show it to a War Hippy // (space) Cadet. The hippy looks at it and runs away // screaming about how he doesn't know which way is // down. "M.C. Escher", // You see a hippy loading his didgeridooka, but before // he can fire it, he's dragged off the battlefield by // another hippy protesting the war. "protesting the war", // You see a "Baker Company" hippy take one bite too // many from a big plate of brownies, then curl up to // take a nap. Looks like he's out of commission for a // while. "out of commission", // You see a hippy a few paces away suddenly realize // that he's violating his deeply held pacifist // beliefs, scream in horror, and run off the // battlefield. "pacifist beliefs", // You look over and see a fellow frat brother // garotting a hippy shaman with the hippy's own // dreadlocks. "Right on, bra!" you shout. "garotting", // You glance over and see one of your frat brothers // hosing down a hippy with soapy water. You laugh and // run over for a high-five. "soapy water", // You glance out over the battlefield and see a hippy // from the F.R.O.G. division get the hiccups and knock // himself out on his own nasty breath. "his own nasty breath", // You see one of the War Hippy's "Jerry's Riggers" // sneeze midway through making a bomb, inadvertently // turning himself into smoke and dust. In the wind. "smoke and dust", // You see a frat boy hose down a hippy Airborne // Commander with sugar water. You applaud as the // Commander gets attacked by her own ferrets. "sugar water", // You see one of your frat brothers paddling a hippy // who seems to be enjoying it. You say "uh, keep up // the good work... bra... yeah." "seems to be enjoying it", // As the hippy falls, you see a hippy a few yards away // clutch his chest and fall over, too. Apparently the // hippy you were fighting was just the astral // projection of another hippy several yards // away. Freaky. "astral projection", }, // 4 total { // You see a War Frat Grill Sergeant hose down three // hippies with white-hot chicken wing sauce. You love // the smell of jaba�ero in the morning. It smells like // victory. "three hippies", // As you finish your fight, you see a nearby Wartender // mixing up a cocktail of vodka and pain for a trio of // charging hippies. "Right on, bra!" you shout. "vodka and pain", // You see one of your frat brothers douse a trio of // nearby hippies in cheap aftershave. They scream and // run off the battlefield to find some incense to // burn. "cheap aftershave", // You see one of your frat brothers line up three // hippies for simultaneous paddling. Don't bathe -- // that's a paddlin'. Light incense -- that's a // paddlin'. Paddlin' a homemade canoe -- oh, you // better believe that's a paddlin'. // "three hippies", // You see one of the "Fortunate 500" make a quick call // on his cell phone. Some mercenaries drive up, shove // three hippies into their bitchin' meat car, and // drive away. // "three hippies", // As you deliver the finishing blow, you see a frat // boy lob a sake bomb into a trio of nearby // hippies. "Nice work, bra!" you shout. "lob a sake bomb", }, // 8 total { // You see one of your Beer Bongadier frat brothers use // a complicated beer bong to spray cheap, skunky beer // on a whole squad hippies at once. "Way to go, bra!" // you shout. "skunky beer", // You glance over and see one of the Roaring Drunks // from the 151st Division overturning a mobile sweat // lodge in a berserker rage. Several sweaty, naked // hippies run out and off the battlefield, brushing // burning coals out of their dreadlocks. "brushing burning coals", // You see one of your frat brothers punch an // F.R.O.G. in the solar plexus, then aim the // subsequent exhale at a squad of hippies standing // nearby. You watch all of them fall to the ground, // gasping for air. "subsequent exhale", // You see a Grillmaster flinging hot kabobs as fast as // he can make them. He skewers one, two, three, four, // five, six... seven! Seven hippies! Ha ha ha! "hot kabobs", }, // 16 total { // A streaking frat boy runs past a nearby funk of // hippies. One look at him makes the hippies have to // go ponder their previous belief that the naked human // body is a beautiful, wholesome thing. "naked human body", // You see one of the Fortunate 500 call in an air // strike. His daddy's personal airship flies over and // dumps cheap beer all over a nearby funk of hippies. "personal airship", // You look over and see a platoon of frat boys round // up a funk of hippies and take them prisoner. Since // being a POW of the frat boys involves a lot of beer // drinking, you're slightly envious. Since it also // involves a lot of paddling, you're somewhat less so. "slightly envious", // You see a kegtank and a mobile sweat lodge facing // off in the distance. Since the kegtank's made of // steel and the sweat lodge is made of wood, you can // guess the outcome. "guess the outcome", }, // 32 total { // You see an entire regiment of hippies throw down // their arms (and their weapons) in disgust and walk // off the battlefield. War! What is it good for? // Absolutely nothing! "Absolutely nothing", // You see a squadron of police cars drive up, and a // squad of policemen arrest a funk of hippies who were // sitting around inhaling smoke from some sort of // glass sculpture. "glass sculpture", // You see a kegtank rumble through the battlefield, // firing beer cans out of its top turret. It mows // down, like, 30 hippies in a row, but then runs out // of ammo. They really should have stocked one more // six-pack. "one more six-pack", }, // 64 total { // You see the a couple of frat boys attaching big, // long planks of wood to either side of a // kegtank. Then they drive through the rank hippy // ranks, mass-paddling as they go. Dozens of hippies // flee the battlefield, tears in their filthy, filthy // eyes. "planks of wood", // You see one of the "Fortunate 500" hang up his PADL // phone, looking smug. Several SWAT vans of police in // full riot gear pull up, and one of them informs the // hippies through a megaphone that this is not a // "designated free speech zone." The hippies throw // rocks and bottles at the police, but most of them // end up shoved into paddy wagons in chains. Er, the // hippies are the ones in the chains. Not the wagons. "SWAT vans", // You see a couple of frat boys stick a fuse into a // huge wooden barrel, light the fuse, and roll it down // the hill to where the hippy forces are // fighting. Judging by the big bada boom that follows, // that barrel was either full of scotch or gunpowder, // and possibly both. "wooden barrel", }, }; private static final String[][] FRAT_MESSAGES = { // 2 total { // You look over and see a fellow hippy warrior using // his dreadlocks to garotte a frat warrior. "Way to // enforce karmic retribution!" you shout. "karmic retribution", // You see a Green Gourmet give a frat boy a plate of // herbal brownies. The frat boy scarfs them all, then // wanders off staring at his hands. "herbal brownies", // Elsewhere on the battlefield, you see a fellow hippy // grab a frat warrior's paddle and give the frat boy a // taste of his own medicine. I guess that could count // as homeopathic healing... "homeopathic healing", // You see a Wartender pour too much lighter fluid on // his grill and go up in a great ball of // fire. Goodness gracious! "much lighter fluid", // You see a Fire Spinner blow a gout of flame onto a // Wartender's grill, charring all the Wartender's // meaty goodness. The Wartender wanders off crying. "meaty goodness", // Nearby, you see one of your sister hippies // explaining the rules of Ultimate Frisbee to a member // of the frat boys' "armchair infantry." His eyes // glaze and he passes out. "Ultimate Frisbee", // You see a member of the frat boy's 151st division // pour himself a stiff drink, knock it back, and // finally pass out from alcohol poisoning. "alcohol poisoning", // You glance over your shoulder and see a squadron of // winged ferrets descend on a frat warrior, entranced // by the sun glinting off his keg shield. "entranced by the sun", // You see a hippy shaman casting a Marxist spell over // a member of the "Fortunate 500" division of the frat // boy army. The frat boy gets on his cell phone and // starts redistributing his wealth. "Marxist spell", // You see a frat boy warrior pound a beer, smash the // can against his forehead, and pass out. You chuckle // to yourself. "smash the can", // You see an F.R.O.G. crunch a bulb of garlic in his // teeth and breathe all over a nearby frat boy, who // turns green and falls over. "bulb of garlic", }, // 4 total { // You hear chanting behind you, and turn to see thick, // ropy (almost anime-esque) vines sprout from a War // Hippy Shaman's dreads and entangle three attacking // frat boy warriors. "three attacking", // Nearby, you see an Elite Fire Spinner take down // three frat boys in a whirl of flame and pain. "three frat boys", // You look over and see three ridiculously drunk // members of the 151st Division run together for a // three-way congratulatory headbutt, which turns into // a three-way concussion. "three-way", // You see a member of the Fortunate 500 take a phone // call, hear him holler something about a stock market // crash, then watch him and two of his fortunate // buddies run off the battlefield in a panic. "stock market crash", // Over the next hill, you see three frat boys abruptly // vanish into a cloud of green smoke. Apparently the // Green Ops Soldiers are on the prowl. // "three frat boys", // You hear excited chittering overhead, and look up to // see a squadron of winged ferrets making a // urine-based bombing run over three frat boys. The // frat boys quickly run off the field to find some // cheap aftershave to cover up the smell. // "three frat boys", }, // 8 total { // Nearby, a War Hippy Elder Shaman nods almost // imperceptibly. A Kegtank hits a gopher hole and tips // over. A squad of confused frat boys stumble out and // off the battlefield. "gopher hole", // You leap out of the way of a runaway Mobile Sweat // Lodge, then watch it run over one, two, three, four, // five, six, seven! Seven frat boys! Ha ha ha! "runaway Mobile Sweat Lodge", // A few yards away, one of the Jerry's Riggers hippies // detonates a bomb underneath a Wartender's grill. An // entire squad of frat boys run from the battlefield // under the onslaught of red-hot coals. "red-hot coals.", // You look over and see one of Jerry's Riggers placing // land mines he made out of paperclips, rubber bands, // and psychedelic mushrooms. A charging squad of frat // boys trips them, and is subsequently dragged off the // field ranting about the giant purple squirrels. "purple squirrels", }, // 16 total { // You turn to see a nearby War Hippy Elder Shaman // making a series of complex hand gestures. A flock of // pigeons swoops down out of the sky and pecks the // living daylights out of a whole platoon of frat // boys. "platoon of", // You see a platoon of charging frat boys get mowed // down by a hippy. Remember, kids, a short-range // weapon (like a paddle) usually does poorly against a // long-range weapon (like a didgeridooka). // "platoon of", // You look over and see a funk of hippies round up a // bunch of frat boys to take as prisoners of // war. Since being a hippy prisoner involves lounging // around inhaling clouds of smoke and eating brownies, // you're somewhat jealous. Since it also involves // non-stop olfactory assault, you're somewhat less so. "funk of hippies", // Nearby, a platoon of frat boys is rocking a mobile // sweat lodge back and forth, trying to tip it // over. When they succeed, they seem surprised by the // hot coals and naked hippies that pour forth, and the // frat boys run away screaming. // "platoon of", }, // 32 total { // A mobile sweat lodge rumbles into a regiment of frat // boys and the hippies inside open all of its vents // simultaneously. Steam that smells like a dozen // baking (and baked) hippies pours out, enveloping the // platoon and sending the frat boys into fits of // nauseated coughing. "regiment", // You see a squadron of police cars drive up, and a // squad of policemen arrest an entire regiment of frat // boys. You hear cries of "She told me she was 18, // bra!" and "I told you, I didn't hit her with a // roofing shingle!" as they're dragged off the // battlefield. // "regiment", // You see a regiment of frat boys decide they're tired // of drinking non-alcoholic beer and tired of not // hitting on chicks, so they throw down their arms, // and then their weapons, and head back to the frat // house. // "regiment", }, // 64 total { // You see an airborne commander trying out a new // strategy: she mixes a tiny bottle of rum she found // on one of the frat boy casualties with a little of // the frat boy's blood, then adds that to the ferret // bait. A fleet of ferrets swoops down, eats the bait, // and goes berserk with alcohol/bloodlust. The frat // boys scream like schoolgirls as the ferrets decimate // their ranks. "scream like schoolgirls", // You see a couple of hippies rigging a mobile sweat // lodge with a public address system. They drive it // through the battlefield, blaring some concept album // about the dark side of Ronald. Frat boys fall asleep // en masse, helpless before music that's horribly // boring if you're not under the influence of // mind-altering drugs. "en masse", // You see an elder hippy shaman close her eyes, clench // her fists, and start to chant. She glows with an // eerie green light as storm clouds bubble and roil // overhead. A funnel cloud descends from the // thunderheads and dances through the frat boy ranks, // whisking them up and away like so many miniature // mobile homes. "mobile homes", }, }; private static final boolean findBattlefieldMessage( final String responseText, final String[] table ) { for ( int i = 0; i < table.length; ++i ) { if ( responseText.contains( table[ i ] ) ) { return true; } } return false; } public static final String victoryMessage( int last, int current ) { int delta = current - last; String side; if ( IslandManager.fratboy ) { side = delta == 1 ? "frat boy" : "frat boys"; } else { side = delta == 1 ? "hippy" : "hippies"; } return delta + " " + side + " defeated; " + current + " down, " + ( 1000 - current ) + " left."; } private static final int[] AREA_UNLOCK = { 64, 192, 458 }; private static final String[] HIPPY_AREA_UNLOCK = { "Lighthouse", "Junkyard", "Arena" }; private static final String[] FRATBOY_AREA_UNLOCK = { "Orchard", "Nunnery", "Farm" }; public static final String areaMessage( final int last, final int current ) { final String[] areas = IslandManager.fratboy ? IslandManager.HIPPY_AREA_UNLOCK : IslandManager.FRATBOY_AREA_UNLOCK; for ( int i = 0; i < IslandManager.AREA_UNLOCK.length; ++i ) { int threshold = IslandManager.AREA_UNLOCK[ i ]; if ( last < threshold && current >= threshold ) { return "The " + areas[ i ] + " is now accessible in this uniform!"; } } return null; } private static final int[] HERO_UNLOCK = { 501, 601, 701, 801, 901, }; private static final String[] HIPPY_HERO = { "Slow Talkin' Elliot", "Neil", "Zim Merman", "the C.A.R.N.I.V.O.R.E. Operative", "the Glass of Orange Juice", }; private static final String[] FRATBOY_HERO = { "the Next-Generation Frat Boy", "Monty Basingstoke-Pratt, IV", "Brutus, the toga-clad lout", "Danglin' Chad", "the War Frat Streaker", }; public static final String heroMessage( final int last, final int current ) { final String[] heroes = IslandManager.fratboy ? IslandManager.FRATBOY_HERO : IslandManager.HIPPY_HERO; for ( int i = 0; i < IslandManager.HERO_UNLOCK.length; ++i ) { int threshold = IslandManager.HERO_UNLOCK[ i ]; if ( last < threshold && current >= threshold ) { return "Keep your eyes open for " + heroes[ i ] + "!"; } } return null; } public static final void handleBattlefield( final String responseText ) { // Nothing to do until battle is done if ( !responseText.contains( "WINWINWIN" ) ) { return; } // We only count known monsters MonsterData monster = MonsterStatusTracker.getLastMonster(); if ( monster == null ) { // The monster is not in the monster database. RequestLogger.updateSessionLog( "Unknown monster found on battlefield: " + monster.getName() ); return; } if ( responseText.contains( "Giant explosions in slow motion" ) ) { // FightRequest can't handle this. ResultProcessor.processResults( true, responseText ); IslandManager.handleEndOfWar( "both" ); return; } String monsterName = monster.getName(); if ( monsterName.equals( "The Big Wisniewski" ) || monsterName.equals( "Big Wisnaqua" ) ) { IslandManager.handleEndOfWar( "hippies" ); return; } if ( monsterName.equals( "The Man" ) || monsterName.equals( "The Aquaman" ) ) { IslandManager.handleEndOfWar( "fratboys" ); return; } } private static final void handleEndOfWar( final String loser ) { String message; if ( loser.equals( "fratboys" ) ) { IslandManager.fratboysDefeated = 1000; Preferences.setInteger( "fratboysDefeated", 1000 ); message = "War finished: fratboys defeated"; } else if ( loser.equals( "hippies" ) ) { IslandManager.hippiesDefeated = 1000; Preferences.setInteger( "hippiesDefeated", 1000 ); message = "War finished: hippies defeated"; } else if ( loser.equals( "both" ) ) { IslandManager.fratboysDefeated = 1000; Preferences.setInteger( "fratboysDefeated", 1000 ); IslandManager.hippiesDefeated = 1000; Preferences.setInteger( "hippiesDefeated", 1000 ); message = "War finished: both sides defeated"; } else { // Say what? return; } RequestLogger.updateSessionLog( message ); RequestLogger.printLine( message ); Preferences.setString( "sideDefeated", loser ); Preferences.setString( "warProgress", "finished" ); QuestDatabase.setQuestProgress( QuestDatabase.Quest.ISLAND_WAR, QuestDatabase.FINISHED ); } public static final void handleBattlefieldMonster( final String responseText, final String monsterName ) { // Nothing to do until battle is done if ( !responseText.contains( "WINWINWIN" ) ) { return; } // You can fax in monsters even after the war is over. Nothing // special to do in that case. if ( Preferences.getString( "warProgress" ).equals( "finished" ) ) { return; } MonsterData monster = MonsterDatabase.findMonster( monsterName, false ); // Decide whether we defeated a hippy or a fratboy warrior if ( IslandManager.isFratboyBattlefieldMonster( monster ) ) { IslandManager.fratboy = false; } else if ( IslandManager.isHippyBattlefieldMonster( monster ) ) { IslandManager.fratboy = true; } else { // Known but unexpected monster on battlefield. RequestLogger.updateSessionLog( "Unexpected monster found on battlefield: " + monsterName ); return; } IslandManager.lastFratboysDefeated = IslandManager.fratboysDefeated; IslandManager.lastHippiesDefeated = IslandManager.hippiesDefeated; // Figure out how many enemies were defeated String[][] table = IslandManager.fratboy ? IslandManager.FRAT_MESSAGES : IslandManager.HIPPY_MESSAGES; int delta = 1; int test = 2; for ( int i = 0; i < table.length; ++i ) { if ( IslandManager.findBattlefieldMessage( responseText, table[ i ] ) ) { delta = test; break; } test *= 2; } // Handle Pete's Motorbike with Rocket Launcher if ( responseText.contains( "rocket launcher blasts 3 extra" ) ) { delta += 3; } int last; int current; if ( IslandManager.fratboy ) { IslandManager.fratboysDefeated = Preferences.increment( "fratboysDefeated", delta, 1000, false ); last = IslandManager.lastFratboysDefeated; current = IslandManager.fratboysDefeated; } else { IslandManager.hippiesDefeated = Preferences.increment( "hippiesDefeated", delta, 1000, false ); last = IslandManager.lastHippiesDefeated; current = IslandManager.hippiesDefeated; } String message = IslandManager.victoryMessage( last, current ); RequestLogger.updateSessionLog( message ); RequestLogger.printLine( message ); message = IslandManager.areaMessage( last, current ); if ( message != null ) { RequestLogger.updateSessionLog( message ); RequestLogger.printLine( message ); } message = IslandManager.heroMessage( last, current ); if ( message != null ) { RequestLogger.updateSessionLog( message ); RequestLogger.printLine( message ); } } public static final void parseIsland( final String location, final String responseText ) { if ( location.startsWith( "bigisland.php" ) ) { IslandManager.parseBigIsland( location, responseText ); } else if ( location.startsWith( "postwarisland.php" ) ) { IslandManager.parsePostwarIsland( location, responseText ); } } public static final void parseBigIsland( final String location, final String responseText ) { Preferences.setString( "warProgress", "started" ); // Parse the map and deduce how many soldiers remain IslandManager.parseBattlefield( responseText ); // Deduce things about quests IslandManager.quest = IslandManager.parseQuest( location ); switch ( IslandManager.quest ) { case ARENA: IslandManager.parseArena( responseText ); break; case JUNKYARD: IslandManager.parseJunkyard( responseText ); break; case ORCHARD: IslandManager.parseOrchard( responseText ); break; case FARM: IslandManager.parseFarm( responseText ); break; case NUNS: IslandManager.parseNunnery( responseText ); break; case LIGHTHOUSE: IslandManager.parseLighthouse( responseText ); break; case CAMP: IslandManager.parseCamp( location, responseText ); break; } } private static final Quest parseQuest( final String location ) { if ( location.contains( "place=concert" ) || location.contains( "action=concert" ) ) { return Quest.ARENA; } if ( location.contains( "action=junkman" ) ) { return Quest.JUNKYARD; } if ( location.contains( "action=stand" ) ) { return Quest.ORCHARD; } if ( location.contains( "action=farmer" ) ) { return Quest.FARM; } if ( location.contains( "place=nunnery" ) ) { return Quest.NUNS; } if ( location.contains( "action=pyro" ) ) { return Quest.LIGHTHOUSE; } if ( location.contains( "whichcamp" ) ) { return Quest.CAMP; } return Quest.NONE; } private static final void parseBattlefield( final String responseText ) { Matcher matcher = IslandManager.MAP_PATTERN.matcher( responseText ); if ( !matcher.find() ) { return; } IslandManager.fratboyImage = StringUtilities.parseInt( matcher.group( 1 ) ); IslandManager.hippyImage = StringUtilities.parseInt( matcher.group( 2 ) ); if ( IslandManager.fratboyImage >= 0 && IslandManager.fratboyImage <= 32 ) { IslandManager.fratboyMin = IslandManager.IMAGES[ IslandManager.fratboyImage ]; if ( IslandManager.fratboyMin == 1000 ) { IslandManager.fratboyMax = 1000; } else { IslandManager.fratboyMax = IslandManager.IMAGES[ IslandManager.fratboyImage + 1 ] - 1; } } if ( IslandManager.hippyImage >= 0 && IslandManager.hippyImage <= 32 ) { IslandManager.hippyMin = IslandManager.IMAGES[ IslandManager.hippyImage ]; if ( IslandManager.hippyMin == 1000 ) { IslandManager.hippyMax = 1000; } else { IslandManager.hippyMax = IslandManager.IMAGES[ IslandManager.hippyImage + 1 ] - 1; } } // Consistency check settings against map if ( IslandManager.fratboysDefeated < IslandManager.fratboyMin ) { IslandManager.fratboysDefeated = IslandManager.fratboyMin; Preferences.setInteger( "fratboysDefeated", IslandManager.fratboysDefeated ); } else if ( IslandManager.fratboysDefeated > IslandManager.fratboyMax ) { IslandManager.fratboysDefeated = IslandManager.fratboyMax; Preferences.setInteger( "fratboysDefeated", IslandManager.fratboysDefeated ); } if ( IslandManager.hippiesDefeated < IslandManager.hippyMin ) { IslandManager.hippiesDefeated = IslandManager.hippyMin; Preferences.setInteger( "hippiesDefeated", IslandManager.hippiesDefeated ); } else if ( IslandManager.hippiesDefeated > IslandManager.hippyMax ) { IslandManager.hippiesDefeated = IslandManager.hippyMax; Preferences.setInteger( "hippiesDefeated", IslandManager.hippiesDefeated ); } } private static final void parseArena( final String responseText ) { // You roll up to the amphitheater and see that the Goat Cheese // Occurence is well into the first song of their four-hour, // one-song set. if ( responseText.contains( "well into the first song" ) ) { Preferences.setString( "sidequestArenaCompleted", "hippy" ); return; } // "Hey, man," he says laconically. "You did a, like, totally // awesome job promoting the concert, man. If you have any // flyers left, I'll take 'em; we can use them at the next // show. Speaking of which, they're hitting the stage in just a // couple of minutes -- you should come back in a few and check // 'em out. It's a totally awesome show, man." if ( responseText.contains( "I'll take 'em" ) ) { Preferences.setString( "sidequestArenaCompleted", "hippy" ); if ( InventoryManager.hasItem( ItemPool.JAM_BAND_FLYERS ) ) { ResultProcessor.processItem( ItemPool.JAM_BAND_FLYERS, -1 ); } return; } // You roll up to the amphitheater and see that Radioactive // Child has already taken the stage. if ( responseText.contains( "has already taken the stage" ) ) { Preferences.setString( "sidequestArenaCompleted", "fratboy" ); return; } // "Hey, bra," he says, "you did excellent work promoting the // show. If you have any flyers left, I'll take them; we can // use them at the next show." if ( responseText.contains( "I'll take them" ) ) { Preferences.setString( "sidequestArenaCompleted", "fratboy" ); if ( InventoryManager.hasItem( ItemPool.ROCK_BAND_FLYERS ) ) { ResultProcessor.processItem( ItemPool.ROCK_BAND_FLYERS, -1 ); } return; } // The stage at the Mysterious Island Arena is empty. if ( responseText.contains( "The stage at the Mysterious Island Arena is empty" ) ) { // Didn't complete quest or defeated the side you // advertised for. Preferences.setString( "sidequestArenaCompleted", "none" ); } } private static final String[][] JUNKYARD_AREAS = { { "a barrel", "next to that barrel with something burning in it", }, { "a refrigerator", "near an abandoned refrigerator", }, { "some tires", "over where the old tires are", }, { "a car", "out by that rusted-out car", }, }; private static final void parseJunkyard( final String responseText ) { String tool = IslandManager.currentJunkyardTool; String location = IslandManager.currentJunkyardLocation; boolean done = false; // The last time I saw my <tool> it was <location>. // (or, if not in uniform:) // He wanders off, muttering something about a <tool> and <location> // // next to that barrel with something burning in it // near an abandoned refrigerator // over where the old tires are // out by that rusted-out car Matcher matcher = IslandManager.JUNKYARD_PATTERN.matcher( responseText ); if ( matcher.find() ) { tool = matcher.group( 1 ); tool = "molybdenum " + ( tool.equals( "wrench" ) ? "crescent " : "" ) + tool; location = matcher.group( 2 ); // Convert out-of-uniform locations to standard location for ( int i = 0; i < IslandManager.JUNKYARD_AREAS.length; ++i ) { String [] locations = IslandManager.JUNKYARD_AREAS[i]; if ( location.equals( locations[0] ) ) { location = locations[1]; break; } } } // As you turn to walk away, he taps you on the shoulder. "I // almost forgot. I made this while you were off getting my // tools. It was boring, but I figure the more time I spend // bored, the longer my life will seem. Anyway, I don't really // want it, so you might as well take it." else if ( responseText.contains( "I made this while you were off getting my tools" ) ) { tool = ""; location = ""; done = true; } if ( !location.equals( IslandManager.currentJunkyardLocation ) ) { IslandManager.currentJunkyardTool = tool; Preferences.setString( "currentJunkyardTool", tool ); IslandManager.currentJunkyardLocation = location; Preferences.setString( "currentJunkyardLocation", location ); } if ( !done ) { return; } // Give the magnet and the tools to Yossarian ResultProcessor.processItem( ItemPool.MOLYBDENUM_MAGNET, -1 ); ResultProcessor.processItem( ItemPool.MOLYBDENUM_HAMMER, -1 ); ResultProcessor.processItem( ItemPool.MOLYBDENUM_SCREWDRIVER, -1 ); ResultProcessor.processItem( ItemPool.MOLYBDENUM_PLIERS, -1 ); ResultProcessor.processItem( ItemPool.MOLYBDENUM_WRENCH, -1 ); if ( responseText.contains( "spark plug earring" ) || responseText.contains( "woven baling wire bracelets" ) || responseText.contains( "gearbox necklace" ) ) { Preferences.setString( "sidequestJunkyardCompleted", "hippy" ); } else if ( responseText.contains( "rusty chain necklace" ) || responseText.contains( "sawblade shield" ) || responseText.contains( "wrench bracelet" ) ) { Preferences.setString( "sidequestJunkyardCompleted", "fratboy" ); } } private static final void parseOrchard( final String responseText ) { // "Is that... it is! The heart of the filthworm queen! You've // done it! You've freed our orchard from the tyranny of // nature!" if ( !responseText.contains( "tyranny of nature" ) ) { return; } if ( InventoryManager.hasItem( ItemPool.FILTHWORM_QUEEN_HEART ) ) { ResultProcessor.processItem( ItemPool.FILTHWORM_QUEEN_HEART, -1 ); } String side = EquipmentManager.isWearingOutfit( OutfitPool.WAR_HIPPY_OUTFIT ) ? "hippy" : "fratboy"; Preferences.setString( "sidequestOrchardCompleted", side ); // The hippy store is available again. Preferences.setInteger( "lastFilthClearance", KoLCharacter.getAscensions() ); Preferences.setString( "currentHippyStore", side ); ConcoctionDatabase.setRefreshNeeded( true ); } private static final void parseFarm( final String responseText ) { // "Well... How about dedicating a portion of your farm to // growing soybeans, to help feed the hippy army?" if ( responseText.contains( "growing soybeans" ) || responseText.contains( "blocks of megatofu" ) ) { Preferences.setString( "sidequestFarmCompleted", "hippy" ); } else if ( responseText.contains( "growing hops" ) || responseText.contains( "bottles of McMillicancuddy" ) ) { Preferences.setString( "sidequestFarmCompleted", "fratboy" ); } } private static final void parseNunnery( final String responseText ) { // "Hello, weary Adventurer! Please, allow us to tend to your // wounds." if ( responseText.contains( "tend to your wounds" ) ) { Preferences.setString( "sidequestNunsCompleted", "hippy" ); } else if ( responseText.contains( "refreshing massage" ) ) { Preferences.setString( "sidequestNunsCompleted", "fratboy" ); } else if ( responseText.contains( "world-weary traveler" ) ) { Preferences.setString( "sidequestNunsCompleted", "none" ); } if ( responseText.contains( "The Sisters tend to your wounds" ) || responseText.contains( "The Sisters give you an invigorating massage" ) ) { Preferences.increment( "nunsVisits", 1 ); } else if ( responseText.contains( "all of the Sisters are busy right now" ) ) { Preferences.setInteger( "nunsVisits", 99 ); } } private static final void parseLighthouse( final String responseText ) { // He gazes at you thoughtfully for a few seconds, then a smile // lights up his face and he says "My life... er... my bombs // for you. My bombs for you, bumpty-bumpty-bump!" if ( !responseText.contains( "My bombs for you" ) ) { return; } String side = EquipmentManager.isWearingOutfit( OutfitPool.WAR_HIPPY_OUTFIT ) ? "hippy" : "fratboy"; Preferences.setString( "sidequestLighthouseCompleted", side ); ResultProcessor.processItem( ItemPool.GUNPOWDER, -5 ); } private static final Pattern CAMP_PATTERN = Pattern.compile( "whichcamp=(\\d+)" ); public static CoinmasterData findCampMaster( final String urlString ) { Matcher campMatcher = IslandManager.CAMP_PATTERN.matcher( urlString ); if ( !campMatcher.find() ) { return null; } String camp = campMatcher.group(1); return camp.equals( "1" ) ? DimemasterRequest.HIPPY : camp.equals( "2" ) ? QuartersmasterRequest.FRATBOY: null; } public static void parseCamp( final String location, final String responseText ) { if ( !location.contains( "whichcamp" ) ) { return; } CoinmasterData data = IslandManager.findCampMaster( location ); if ( data == null ) { return; } CoinMasterRequest.parseResponse( data, location, responseText ); } public static final void parsePostwarIsland( final String location, final String responseText ) { // Deduce which side was defeated IslandManager.deduceWinner( responseText ); // Deduce things about quests IslandManager.quest = IslandManager.parseQuest( location ); switch ( IslandManager.quest ) { case ARENA: IslandManager.parseArena( responseText ); break; case NUNS: IslandManager.parseNunnery( responseText ); break; } } private static final void deduceWinner( final String responseText ) { boolean hippiesLost = responseText.contains( "snarfblat=149" ); boolean fratboysLost = responseText.contains( "snarfblat=150" ); String loser = ( !hippiesLost ) ? "fratboys" : ( !fratboysLost ) ? "hippies" : "both"; Preferences.setString( "sideDefeated", loser ); Preferences.setString( "warProgress", "finished" ); } }