/**
* Copyright (c) 2005-2017, KoLmafia development team
* http://kolmafia.sourceforge.net/
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* [1] Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* [2] Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* [3] Neither the name "KoLmafia" nor the names of its contributors may
* be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package net.sourceforge.kolmafia.request;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import net.sourceforge.kolmafia.KoLAdventure;
import net.sourceforge.kolmafia.KoLCharacter;
import net.sourceforge.kolmafia.KoLConstants.MafiaState;
import net.sourceforge.kolmafia.KoLmafia;
import net.sourceforge.kolmafia.RequestLogger;
import net.sourceforge.kolmafia.RequestThread;
import net.sourceforge.kolmafia.objectpool.IntegerPool;
import net.sourceforge.kolmafia.preferences.Preferences;
import net.sourceforge.kolmafia.session.ClanManager;
import net.sourceforge.kolmafia.session.Limitmode;
import net.sourceforge.kolmafia.utilities.StringUtilities;
public class ClanRumpusRequest
extends GenericRequest
{
private static final Pattern SPOT_PATTERN = Pattern.compile( "spot=(\\d*)" );
private static final Pattern FURNI_PATTERN = Pattern.compile( "furni=(\\d*)" );
private static final Pattern ROOM_PATTERN = Pattern.compile( "action=click&spot=(\\d)&furni=(\\d)" );
private static final Pattern BALLPIT_PATTERN = Pattern.compile( "with ([\\d,]+) ball" );
public static enum RequestType
{
SEARCH,
MUSCLE,
MYST,
MOXIE,
SOFA,
CHIPS,
BALLS,
JUKEBOX,
VISIT,
;
public static RequestType fromString( String text )
{
if ( text != null )
{
for ( RequestType req : RequestType.values() )
{
if ( text.equals( req.toString() ) )
{
return req;
}
}
}
return RequestType.SEARCH;
}
}
public static final int RADIUM = 1;
public static final int WINTERGREEN = 2;
public static final int ENNUI = 3;
public static final Object [][] CHIP_FLAVORS = new Object[][]
{
{ "radium", IntegerPool.get( RADIUM ) },
{ "wintergreen", IntegerPool.get( WINTERGREEN ) },
{ "ennui", IntegerPool.get( ENNUI ) },
};
public static final Object [][] SONGS = new Object[][]
{
{ "meat", "Material Witness", IntegerPool.get( 1 ) },
{ "stats", "No Worries", IntegerPool.get( 2 ) },
{ "item", "Techno Bliss", IntegerPool.get( 3 ) },
{ "initiative", "Metal Speed", IntegerPool.get( 4 ) },
};
public static final int findChips( final String name )
{
for ( int i = 0; i < CHIP_FLAVORS.length; ++i )
{
String flavor = (String) CHIP_FLAVORS[i][0];
if ( name.equals( flavor ) )
{
Integer index = (Integer) CHIP_FLAVORS[i][1];
return index.intValue();
}
}
return 0;
}
public static final int findSong( final String name )
{
if ( StringUtilities.isNumeric( name ) )
{
int n = StringUtilities.parseInt( name );
return n > 0 && n <= SONGS.length ? n : 0;
}
for ( int i = 0; i < SONGS.length; ++i )
{
String modifier = (String) SONGS[i][0];
String effect = (String) SONGS[i][1];
if ( name.equals( modifier ) || name.equals( effect ) )
{
return i + 1;
}
}
return 0;
}
private static final Pattern TURN_PATTERN = Pattern.compile( "numturns=(\\d+)" );
public static enum Equipment
{
NONE( "", 0, 0, 0 ),
GIRL_CALENDAR( "Girls of Loathing Calendar", 1, 1, 0 ),
BOY_CALENDAR( "Boys of Loathing Calendar", 1, 2, 0 ),
PAINTING( "Infuriating Painting", 1, 3, 0 ),
MEAT_ORCHID( "Exotic Hanging Meat Orchid", 1, 4, 1 ),
ARCANE_TOMES( "Collection of Arcane Tomes and Whatnot", 2, 1, 0 ),
SPORTS_MEMORABILIA( "Collection of Sports Memorabilia", 2, 2, 0 ),
SELF_HELP_BOOKS( "Collection of Self-Help Books", 2, 3, 0 ),
SODA_MACHINE( "Soda Machine", 3, 1, 3 ),
JUKEBOX( "Jukebox", 3, 2, 0 ),
KLAW_GAME( "Mr. Klaw \"Skill\" Crane Game", 3, 3, 3 ),
RADIO( "Old-Timey Radio", 4, 1, 1 ),
POTTED_MEAT_BUSH( "Potted Meat Bush", 4, 2, 1 ),
DESK_CALENDAR( "Inspirational Desk Calendar", 4, 3, 0 ),
WRESTLING_MAT( "Wrestling Mat", 5, 1, 0 ),
TANNING_BED( "Tan-U-Lots Tanning Bed", 5, 2, 0 ),
COMFY_SOFA( "Comfy Sofa", 5, 3, 0 ),
BALLPIT( "Awesome Ball Pit", 7, 1, 0 ),
HOBO_WORKOUT( "Hobo-Flex Workout System", 9, 1, 0 ),
SNACK_MACHINE( "Snack Machine", 9, 2, 0 ),
POTTED_MEAT_TREE("Potted Meat Tree", 9, 3, 1 ),
;
public String name;
public int slot;
public int furni;
public int maxUses;
private Equipment( String name, int slot, int furni, int maxUses )
{
this.name = name;
this.slot = slot;
this.furni = furni;
this.maxUses = maxUses;
}
public static String equipmentName( int slot, int furni )
{
if ( furni == 0 )
{
return "";
}
for ( Equipment equipment : Equipment.values() )
{
if ( slot == equipment.slot && furni == equipment.furni )
{
return equipment.name;
}
}
return "";
}
@Override
public String toString()
{
return this.name;
}
public static Equipment toEquip( String name )
{
if ( name == null || name.equals( "" ) )
{
return NONE;
}
for ( Equipment equipment : Equipment.values() )
{
if ( name.equals( equipment.name ) )
{
return equipment;
}
}
return NONE;
}
}
private RequestType action;
private int option;
private int turnCount;
/**
* Constructs a new <code>ClanRumpusRequest</code>.
*
* @param action The identifier for the action you're requesting
*/
private ClanRumpusRequest()
{
super( "clan_rumpus.php" );
this.action = RequestType.VISIT;
}
public ClanRumpusRequest( final RequestType action )
{
super( "clan_rumpus.php" );
this.action = action;
}
public ClanRumpusRequest( final RequestType action, final int option )
{
super( "clan_rumpus.php" );
this.action = action;
this.option = option;
}
/**
* Runs the request. Note that this does not report an error if it fails; it merely parses the results to see if any
* gains were made.
*/
public ClanRumpusRequest setTurnCount( final int turnCount )
{
this.turnCount = turnCount;
return this;
}
private void visitEquipment( final int spot, final int furniture )
{
this.clearDataFields();
this.addFormField( "action", "click" );
this.addFormField( "spot", String.valueOf( spot ) );
this.addFormField( "furni", String.valueOf( furniture ) );
}
@Override
public int getAdventuresUsed()
{
return this.turnCount;
}
@Override
public void run()
{
// Sometimes can't access in Limitmode
if ( Limitmode.limitClan() )
{
return;
}
switch ( this.action )
{
case SEARCH:
this.addFormField( "action", "click" );
this.addFormField( "spot", "7" );
break;
case MUSCLE:
// If we can do inside Degrassi Knoll use the gym.
if ( KoLCharacter.knollAvailable() && !KoLCharacter.inZombiecore() )
{
// First load the choice adventure page
RequestThread.postRequest( new PlaceRequest( "knoll_friendly", "dk_gym" ) );
this.constructURLString( "choice.php" );
this.addFormField( "whichchoice", "792" );
this.addFormField( "option", "1" );
}
// Otherwise, use the one in our clan - if we're in one.
else
{
if ( !ClanManager.getClanRumpus().contains( "Hobo-Flex Workout System" ) )
{
KoLmafia.updateDisplay( MafiaState.ERROR, "You don't have access to a clan muscle gym." );
return;
}
this.constructURLString( "clan_rumpus.php" );
this.addFormField( "preaction", "gym" );
this.addFormField( "whichgym", "3" );
}
break;
case MYST:
// If we can go to Little Canadia, use the gym.
if ( KoLCharacter.canadiaAvailable() )
{
// First load the choice adventure page
RequestThread.postRequest( new PlaceRequest( "canadia", "lc_institute" ) );
this.constructURLString( "choice.php" );
this.addFormField( "whichchoice", "770" );
this.addFormField( "option", "1" );
}
// Otherwise, use the one in our clan - if we're in one.
else
{
if ( !ClanManager.getClanRumpus().contains( "Collection of Arcane Tomes and Whatnot" ) )
{
KoLmafia.updateDisplay( MafiaState.ERROR, "You don't have access to a clan mysticality gym." );
return;
}
this.constructURLString( "clan_rumpus.php" );
this.addFormField( "preaction", "gym" );
this.addFormField( "whichgym", "1" );
}
break;
case MOXIE:
// If we can go to the Gnomish Gnomads Camp, use the gym
if ( KoLCharacter.gnomadsAvailable() )
{
this.constructURLString( "gnomes.php" );
this.addFormField( "action", "train" );
}
// Otherwise, use the one in our clan - if we're in one.
else
{
if ( !ClanManager.getClanRumpus().contains( "Tan-U-Lots Tanning Bed" ) )
{
KoLmafia.updateDisplay( MafiaState.ERROR, "You don't have access to a clan moxie gym." );
return;
}
this.constructURLString( "clan_rumpus.php" );
this.addFormField( "preaction", "gym" );
this.addFormField( "whichgym", "2" );
}
break;
case SOFA:
if ( !ClanManager.getClanRumpus().contains( "Comfy Sofa" ) )
{
KoLmafia.updateDisplay( MafiaState.ERROR, "Your clan doesn't have a sofa." );
return;
}
this.constructURLString( "clan_rumpus.php" );
this.addFormField( "preaction", "nap" );
break;
case CHIPS:
if ( !ClanManager.getClanRumpus().contains( "Snack Machine" ) )
{
KoLmafia.updateDisplay( MafiaState.ERROR, "Your clan doesn't have a Snack Machine." );
return;
}
this.constructURLString( "clan_rumpus.php" );
this.addFormField( "preaction", "buychips" );
this.addFormField( "whichbag", String.valueOf( this.option ) );
break;
case BALLS:
this.constructURLString( "clan_rumpus.php" );
this.addFormField( "preaction", "ballpit" );
break;
case JUKEBOX:
if ( !ClanManager.getClanRumpus().contains( "Jukebox" ) )
{
KoLmafia.updateDisplay( MafiaState.ERROR, "Your clan doesn't have a Jukebox." );
return;
}
this.constructURLString( "clan_rumpus.php" );
this.addFormField( "preaction", "jukebox" );
this.addFormField( "whichsong", String.valueOf( this.option ) );
break;
default:
break;
}
if ( this.turnCount > 0 )
{
this.addFormField( "numturns", String.valueOf( this.turnCount ) );
if ( KoLCharacter.getAdventuresLeft() < this.turnCount )
{
KoLmafia.updateDisplay( MafiaState.ERROR, "Insufficient adventures." );
return;
}
}
else if ( this.action == RequestType.SOFA )
{
return;
}
if ( this.action != RequestType.SEARCH )
{
KoLmafia.updateDisplay( "Executing request..." );
}
super.run();
}
@Override
public void processResults()
{
ClanRumpusRequest.parseResponse( this.getURLString(), this.responseText );
}
public static void parseResponse( final String urlString, final String responseText )
{
if ( urlString.startsWith( "choice.php" ) )
{
if ( urlString.contains( "whichchoice=770" ) || urlString.contains( "whichchoice=792" ) )
{
if ( responseText.contains( "feel the burn" ) ||
responseText.contains( "learn from the sages" ) )
{
RequestLogger.printLine( "Workout completed." );
}
else
{
KoLmafia.updateDisplay( MafiaState.ABORT, "You can't access that gym" );
}
}
return;
}
if ( urlString.startsWith( "gnomes.php" ) )
{
if ( urlString.contains( "place=train" ) )
{
if ( responseText.contains( "learn to be sneakier" ) )
{
RequestLogger.printLine( "Workout completed." );
}
else
{
KoLmafia.updateDisplay( MafiaState.ABORT, "You can't access that gym" );
}
}
return;
}
if ( !urlString.startsWith( "clan_rumpus.php" ) )
{
return;
}
// Start by saving the number of balls in the pit, in case this response
// doesn't have that information. If the user switches clans, then the
// first check is guaranteed to have that information.
String ballpit = null;
if ( !ClanManager.getClanRumpus().isEmpty() )
{
String last = ClanManager.getClanRumpus().get( ClanManager.getClanRumpus().size() - 1 );
if ( last.startsWith( "Awesome Ball Pit" ) )
{
ballpit = last;
}
}
ClanManager.getClanRumpus().clear();
Matcher matcher = ClanRumpusRequest.ROOM_PATTERN.matcher( responseText );
while ( matcher.find() )
{
int spot = StringUtilities.parseInt( matcher.group( 1 ) );
int furni = StringUtilities.parseInt( matcher.group( 2 ) );
String equipmentName = ClanRumpusRequest.Equipment.equipmentName( spot, furni );
if ( !equipmentName.equals( "" ) )
{
ClanManager.addToRumpus( equipmentName );
}
}
if ( responseText.contains( "action=click&spot=7" ) )
{
// We have an Awesome Ball Pit
matcher = ClanRumpusRequest.BALLPIT_PATTERN.matcher( responseText );
if ( matcher.find() )
{
String balls = matcher.group( 1 );
ballpit = "Awesome Ball Pit (" + balls + ")";
}
else if ( responseText.contains( "single ball" ) )
{
ballpit = "Awesome Ball Pit (1)";
}
if ( ballpit != null )
{
ClanManager.addToRumpus( ballpit );
}
}
KoLCharacter.recalculateAdjustments();
matcher = GenericRequest.ACTION_PATTERN.matcher( urlString );
String action = matcher.find() ? matcher.group(1) : null;
if ( action == null )
{
return;
}
if ( action.equals( "gym" ) )
{
if ( responseText.contains( "You work it on out." ) ||
responseText.contains( "You study the secrets of the cosmos." ) ||
responseText.contains( "You bake under the artificial sunlight." ) )
{
KoLmafia.updateDisplay( "Workout completed." );
}
else
{
KoLmafia.updateDisplay( MafiaState.ABORT, "You can't access that gym" );
}
return;
}
if ( action.equals( "nap" ) )
{
// You take a nap on the comfy sofa.
if ( responseText.contains( "You take a nap" ) )
{
KoLmafia.updateDisplay( "Resting completed." );
}
// Either you aren't in a clan or your clan doesn't have a sofa
else
{
KoLmafia.updateDisplay( MafiaState.ABORT, "Resting failed - no Clan Sofa available." );
}
return;
}
if ( action.equals( "ballpit" ) )
{
// You play in the ball pit. Wheeeeeee!
// (You've already played in the ball pit today.)
if ( responseText.contains( "play in the ball pit" ) ||
responseText.contains( "already played in the ball pit" ) )
{
Preferences.setBoolean( "_ballpit", true );
}
return;
}
if ( action.equals( "buychips" ) )
{
// a bag of chips drops into the tray at the bottom
if ( responseText.contains( "a bag of chips drops" ) )
{
Preferences.increment( "_chipBags", 1 );
}
// You press the button and the big metal coil rotates,
// but not far enough to actually drop the
// chips. Dangit!
else if ( responseText.contains( "but not far enough" ) )
{
Preferences.setInteger( "_chipBags", 3 );
}
return;
}
if ( action.equals( "jukebox" ) )
{
// Whether we get a song or not, we are done for the
// day with the Jukebox, unless we ascend, which will
// reset the preference.
Preferences.setBoolean( "_jukebox", true );
return;
}
if ( urlString.contains( "spot=3" ) && urlString.contains( "furni=3" ) )
{
// You carefully guide the claw over the prize that
// looks the easiest to grab. You press the button and
// the claw slowly descends.
if ( responseText.contains( "slowly descends" ) )
{
Preferences.increment( "_klawSummons", 1 );
}
// The machine makes a horrible clanking noise, and a
// wisp of smoke pours out of the prize chute.
//
// The crane machine seems to be broken down. Oh
// well. Maybe they'll fix it by tomorrow.
else if ( responseText.contains( "seems to be broken down" ) )
{
Preferences.setInteger( "_klawSummons", 3 );
}
return;
}
}
public static void getBreakfast()
{
// Sometimes can't access in Limitmode
if ( Limitmode.limitClan() )
{
return;
}
ClanRumpusRequest request = new ClanRumpusRequest();
// The Klaw can be accessed regardless of whether or not
// you are in hardcore, so handle it first.
if ( ClanManager.getClanRumpus().contains( "Mr. Klaw \"Skill\" Crane Game" ) )
{
request.visitEquipment( 3, 3 );
int klawCount = Preferences.getInteger( "_klawSummons" );
while ( klawCount < 3 )
{
request.run();
int count = Preferences.getInteger( "_klawSummons" );
if ( klawCount == count )
{
KoLmafia.updateDisplay( MafiaState.ERROR, "Something went wrong while using the Klaw Machine." );
break;
}
klawCount = count;
}
}
if ( !KoLCharacter.canInteract() )
{
return;
}
List<String> rumpus = new ArrayList<String>( ClanManager.getClanRumpus() );
for ( String equip : rumpus )
{
Equipment equipment = Equipment.toEquip( equip );
// Skip the Mr. Klaw game, since we ran it above
if ( equipment == Equipment.KLAW_GAME )
{
continue;
}
// Things that can be used only once a day should have
// daily preferences to track that and we should check
// that rather than making a server hit
request.visitEquipment( equipment.slot, equipment.furni );
for ( int i = 0; i < equipment.maxUses; i++ )
{
request.run();
}
}
}
public static boolean registerRequest( final String urlString )
{
String action = null;
if ( urlString.startsWith( "clan_rumpus.php" ) )
{
if ( urlString.contains( "whichgym=3" ) )
{
action = "Pump Up Muscle";
}
else if ( urlString.contains( "whichgym=1" ) )
{
action = "Pump Up Mysticality";
}
else if ( urlString.contains( "whichgym=2" ) )
{
action = "Pump Up Moxie";
}
else if ( urlString.contains( "preaction=nap" ) )
{
action = "Rest in Clan Sofa";
}
}
else if ( urlString.startsWith( "place.php" ) )
{
if ( urlString.contains( "action=dk_gym" ) )
{
action = "Pump Up Muscle";
}
else if ( urlString.contains( "action=lc_institute" ) )
{
action = "Pump Up Mysticality";
}
}
else if ( urlString.startsWith( "gnomes.php" ) )
{
if ( urlString.contains( "action=train" ) )
{
action = "Pump Up Moxie";
}
}
if ( action != null )
{
Matcher matcher = ClanRumpusRequest.TURN_PATTERN.matcher( urlString );
if ( !matcher.find() )
{
String message = "[" + KoLAdventure.getAdventureCount() + "] " + action;
RequestLogger.printLine();
RequestLogger.updateSessionLog();
RequestLogger.printLine( message );
RequestLogger.updateSessionLog( message );
return true;
}
// If not enough turns available, nothing will happen.
int turns = StringUtilities.parseInt( matcher.group( 1 ) );
int available = KoLCharacter.getAdventuresLeft();
if ( turns > available )
{
return true;
}
String message = "[" + KoLAdventure.getAdventureCount() + "] " + action + " (" + turns + " turns)";
RequestLogger.printLine();
RequestLogger.updateSessionLog();
RequestLogger.printLine( message );
RequestLogger.updateSessionLog( message );
return true;
}
if ( !urlString.startsWith( "clan_rumpus.php" ) )
{
return false;
}
if ( urlString.contains( "action=buychips" ) )
{
String message = "Buying chips from the Snack Machine in the clan rumpus room";
RequestLogger.printLine( message );
RequestLogger.updateSessionLog( message );
return true;
}
if ( urlString.contains( "preaction=ballpit" ) )
{
String message = "Jumping into the Awesome Ball Pit in the clan rumpus room";
RequestLogger.printLine( message );
RequestLogger.updateSessionLog( message );
return true;
}
if ( urlString.contains( "preaction=jukebox" ) )
{
String message = "Playing a song on the Jukebox in the clan rumpus room";
RequestLogger.printLine( message );
RequestLogger.updateSessionLog( message );
return true;
}
// The only other actions we handle here are clicking on clan
// furniture
if ( !urlString.contains( "action=click" ) )
{
return false;
}
Matcher matcher = SPOT_PATTERN.matcher( urlString );
if ( !matcher.find() )
{
return true;
}
int spot = StringUtilities.parseInt( matcher.group( 1 ) );
matcher = FURNI_PATTERN.matcher( urlString );
if ( !matcher.find() )
{
return true;
}
int furniture = StringUtilities.parseInt( matcher.group( 1 ) );
String equipment = ClanRumpusRequest.Equipment.equipmentName( spot, furniture );
if ( equipment == null )
{
return false;
}
String message = "Visiting " + equipment + " in clan rumpus room";
RequestLogger.printLine( message );
RequestLogger.updateSessionLog( message );
return true;
}
}