/**
* 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.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import net.java.dev.spellcast.utilities.LockableListModel;
import net.sourceforge.kolmafia.AdventureResult;
import net.sourceforge.kolmafia.CoinmasterData;
import net.sourceforge.kolmafia.KoLAdventure;
import net.sourceforge.kolmafia.KoLConstants;
import net.sourceforge.kolmafia.KoLmafia;
import net.sourceforge.kolmafia.RequestLogger;
import net.sourceforge.kolmafia.objectpool.ItemPool;
import net.sourceforge.kolmafia.persistence.AdventureDatabase;
import net.sourceforge.kolmafia.persistence.BountyDatabase;
import net.sourceforge.kolmafia.persistence.CoinmastersDatabase;
import net.sourceforge.kolmafia.persistence.MonsterDatabase;
import net.sourceforge.kolmafia.preferences.Preferences;
import net.sourceforge.kolmafia.swingui.AdventureFrame;
import net.sourceforge.kolmafia.utilities.StringUtilities;
public class BountyHunterHunterRequest
extends CoinMasterRequest
{
public static final String master = "Bounty Hunter Hunter";
private static final LockableListModel<AdventureResult> buyItems = CoinmastersDatabase.getBuyItems( BountyHunterHunterRequest.master );
private static final Map<Integer, Integer> buyPrices = CoinmastersDatabase.getBuyPrices( BountyHunterHunterRequest.master );
private static final Pattern TOKEN_PATTERN = Pattern.compile( "You have.*?<b>([\\d,]+)</b> filthy lucre" );
public static final AdventureResult LUCRE = ItemPool.get( ItemPool.LUCRE, 1 );
public static final CoinmasterData BHH =
new CoinmasterData(
BountyHunterHunterRequest.master,
"hunter",
BountyHunterHunterRequest.class,
"lucre",
"You don't have any filthy lucre",
false,
BountyHunterHunterRequest.TOKEN_PATTERN,
BountyHunterHunterRequest.LUCRE,
null,
null,
"bounty.php",
"buy",
BountyHunterHunterRequest.buyItems,
BountyHunterHunterRequest.buyPrices,
null,
null,
null,
null,
"whichitem",
GenericRequest.WHICHITEM_PATTERN,
"howmany",
GenericRequest.HOWMANY_PATTERN,
null,
null,
true
);
public BountyHunterHunterRequest()
{
super( BountyHunterHunterRequest.BHH );
}
public BountyHunterHunterRequest( final String action )
{
super( BountyHunterHunterRequest.BHH, action );
}
public BountyHunterHunterRequest( final boolean buying, final AdventureResult [] attachments )
{
super( BountyHunterHunterRequest.BHH, buying, attachments );
}
public BountyHunterHunterRequest( final boolean buying, final AdventureResult attachment )
{
super( BountyHunterHunterRequest.BHH, buying, attachment );
}
public BountyHunterHunterRequest( final boolean buying, final int itemId, final int quantity )
{
super( BountyHunterHunterRequest.BHH, buying, itemId, quantity );
}
@Override
public void processResults()
{
BountyHunterHunterRequest.parseResponse( this.getURLString(), this.responseText );
}
private static final Pattern COMPLETED_PATTERN = Pattern.compile( "turn in your (\\d+) (.*?) to the Bounty Hunter Hunter" );
public static void parseResponse( final String location, final String responseText )
{
CoinmasterData data = BountyHunterHunterRequest.BHH;
// Check for completed bounties
Matcher completedMatcher = BountyHunterHunterRequest.COMPLETED_PATTERN.matcher( responseText );
while ( completedMatcher.find() )
{
int bountyCount = StringUtilities.parseInt( completedMatcher.group( 1 ) );
String bountyPlural = completedMatcher.group( 2 );
String bountyItem = BountyDatabase.getName( bountyPlural );
if ( bountyItem != null )
{
AdventureResult result = AdventureResult.tallyItem( bountyItem, -bountyCount, false );
AdventureResult.addResultToList( KoLConstants.tally, result );
}
}
String action = GenericRequest.getAction( location );
if ( action == null )
{
BountyHunterHunterRequest.parseEasy( responseText );
BountyHunterHunterRequest.parseHard( responseText );
BountyHunterHunterRequest.parseSpecial( responseText );
return;
}
if ( action.equals( "takelow" ) )
{
String currentUntakenBounty = Preferences.getString( "_untakenEasyBountyItem" );
if ( !currentUntakenBounty.equals( "" ) )
{
Preferences.setString( "currentEasyBountyItem", currentUntakenBounty + ":0" );
Preferences.setString( "_untakenEasyBountyItem", "" );
}
String bountyLocation = BountyDatabase.getLocation( currentUntakenBounty );
if ( bountyLocation != null )
{
KoLAdventure adventure = AdventureDatabase.getAdventure( bountyLocation );
if ( adventure != null )
{
AdventureFrame.updateSelectedAdventure( adventure );
}
}
return;
}
if ( action.equals( "takehigh" ) )
{
String currentUntakenBounty = Preferences.getString( "_untakenHardBountyItem" );
if ( !currentUntakenBounty.equals( "" ) )
{
Preferences.setString( "currentHardBountyItem", currentUntakenBounty + ":0" );
Preferences.setString( "_untakenHardBountyItem", "" );
}
String bountyLocation = BountyDatabase.getLocation( currentUntakenBounty );
if ( bountyLocation != null )
{
KoLAdventure adventure = AdventureDatabase.getAdventure( bountyLocation );
if ( adventure != null )
{
AdventureFrame.updateSelectedAdventure( adventure );
}
}
return;
}
if ( action.equals( "takespecial" ) )
{
String currentUntakenBounty = Preferences.getString( "_untakenSpecialBountyItem" );
if ( !currentUntakenBounty.equals( "" ) )
{
Preferences.setString( "currentSpecialBountyItem", currentUntakenBounty + ":0" );
Preferences.setString( "_untakenSpecialBountyItem", "" );
}
String bountyLocation = BountyDatabase.getLocation( currentUntakenBounty );
if ( bountyLocation != null )
{
KoLAdventure adventure = AdventureDatabase.getAdventure( bountyLocation );
if ( adventure != null )
{
AdventureFrame.updateSelectedAdventure( adventure );
}
}
return;
}
if ( action.equals( "giveup_low" ) )
{
Preferences.setString( "currentEasyBountyItem", "" );
return;
}
if ( action.equals( "giveup_high" ) )
{
Preferences.setString( "currentHardBountyItem", "" );
return;
}
if ( action.equals( "giveup_spe" ) )
{
Preferences.setString( "currentSpecialBountyItem", "" );
return;
}
CoinMasterRequest.parseResponse( data, location, responseText );
}
private static final void parseBounty( final String responseText,
Pattern takenPattern, Pattern untakenPattern, Pattern quantityPattern,
String currentSetting, String untakenSetting, String unknownSetting )
{
Matcher bountyItemMatcher = takenPattern.matcher( responseText );
if ( !bountyItemMatcher.find() )
{
Matcher bountyUntakenMatcher = untakenPattern.matcher( responseText );
if( bountyUntakenMatcher.find() )
{
String plural = bountyUntakenMatcher.group( 3 );
String bountyItem = BountyDatabase.getName( plural );
if ( bountyItem != null )
{
Preferences.setString( untakenSetting, bountyItem );
}
else
{
Preferences.setString( unknownSetting,
bountyUntakenMatcher.group( 1 ) + ":" +
bountyUntakenMatcher.group( 2 ) + ":" +
bountyUntakenMatcher.group( 3 ) );
}
}
else
{
Preferences.setString( untakenSetting, "" );
}
Preferences.setString( currentSetting, "" );
return;
}
String plural = bountyItemMatcher.group( 2 );
String bountyItem = BountyDatabase.getName( plural );
if ( bountyItem != null )
{
Matcher bountyQtyMatcher = quantityPattern.matcher( responseText );
int bountyQty = bountyQtyMatcher.find() ? StringUtilities.parseInt( bountyQtyMatcher.group( 1 ) ) : 0;
Preferences.setString( currentSetting, bountyItem + ":" + bountyQty );
}
}
private static final Pattern EASY_PATTERN = Pattern.compile( "Easy Bounty! Come back when you've collected (\\d+) (.*?) from" );
private static final Pattern UNTAKEN_EASY_PATTERN = Pattern.compile( "Easy Bounty:.*?/itemimages/(.*?) width.*>(\\d+) (.*?) from.*?takelow" );
private static final Pattern EASY_QTY_PATTERN = Pattern.compile( "Easy Bounty.*?You have collected (\\d+) .*?giveup_low" );
private static final void parseEasy( final String responseText )
{
BountyHunterHunterRequest.parseBounty( responseText,
BountyHunterHunterRequest.EASY_PATTERN,
BountyHunterHunterRequest.UNTAKEN_EASY_PATTERN,
BountyHunterHunterRequest.EASY_QTY_PATTERN,
"currentEasyBountyItem",
"_untakenEasyBountyItem",
"_unknownEasyBountyItem" );
}
private static final Pattern HARD_PATTERN = Pattern.compile( "Hard Bounty! Come back when you've collected (\\d+) (.*?) from" );
private static final Pattern UNTAKEN_HARD_PATTERN = Pattern.compile( "Hard Bounty:.*?/itemimages/(.*?) width.*>(\\d+) (.*?) from.*?takehigh" );
private static final Pattern HARD_QTY_PATTERN = Pattern.compile( "Hard Bounty.*?You have collected (\\d+) .*?giveup_high" );
private static final void parseHard( final String responseText )
{
BountyHunterHunterRequest.parseBounty( responseText,
BountyHunterHunterRequest.HARD_PATTERN,
BountyHunterHunterRequest.UNTAKEN_HARD_PATTERN,
BountyHunterHunterRequest.HARD_QTY_PATTERN,
"currentHardBountyItem",
"_untakenHardBountyItem",
"_unknownHardBountyItem" );
}
private static final Pattern SPECIAL_PATTERN = Pattern.compile( "Specialty Bounty! Come back when you've collected (\\d+) (.*?) from" );
private static final Pattern UNTAKEN_SPECIAL_PATTERN = Pattern.compile( "Specialty Bounty:.*?/itemimages/(.*?) width.*>(\\d+) (.*?) from.*?takespecial" );
private static final Pattern SPECIAL_QTY_PATTERN = Pattern.compile( "Specialty Bounty.*?You have collected (\\d+) .*?giveup_spe" );
private static final void parseSpecial( final String responseText )
{
BountyHunterHunterRequest.parseBounty( responseText,
BountyHunterHunterRequest.SPECIAL_PATTERN,
BountyHunterHunterRequest.UNTAKEN_SPECIAL_PATTERN,
BountyHunterHunterRequest.SPECIAL_QTY_PATTERN,
"currentSpecialBountyItem",
"_untakenSpecialBountyItem",
"_unknownSpecialBountyItem" );
}
private static final Pattern BOUNTY_ITEM_PATTERN =
Pattern.compile( "itemimages/(.*?) width=30 height=30></td><td align=center>You acquire a bounty item: <b>(.*?)</b></td></tr></table>\\((\\d+) of" );
public static final void parseFight( final String monster, final String location, final String responseText )
{
// First cut down responseText to the last image before the bounty, assumes image name is less than 30 characters,
// and that another won't be seen in that time
int imageIndex = responseText.indexOf( ".gif width=30 height=30></td><td align=center>You acquire a bounty item" );
String reducedResponseText = responseText;
if ( imageIndex > 30 )
{
reducedResponseText = responseText.substring( imageIndex - 30 );
}
// If known bounty item we can set the preference correctly based on number found so far
Matcher bountyItemMatcher = BountyHunterHunterRequest.BOUNTY_ITEM_PATTERN.matcher( reducedResponseText );
if( bountyItemMatcher.find() )
{
String bountyItem = bountyItemMatcher.group( 2 );
int bountyCount = StringUtilities.parseInt( bountyItemMatcher.group( 3 ) );
String bountyType = BountyDatabase.getType( bountyItem );
if ( bountyType == null )
{
boolean matched = false;
// Convert monster name to correct case
String monsterTrueCase = MonsterDatabase.findMonster( monster, false ).getName();
String bountyImage = bountyItemMatcher.group( 1 );
// Try to work out what the item should be
String unknownEasyBountyString = Preferences.getString( "_unknownEasyBountyItem" );
String unknownHardBountyString = Preferences.getString( "_unknownHardBountyItem" );
String unknownSpecialBountyString = Preferences.getString( "_unknownSpecialBountyItem" );
if ( !unknownEasyBountyString.equals( "" ) )
{
int bountyIndex = unknownEasyBountyString.indexOf( ":" );
int bountyIndex2 = unknownEasyBountyString.indexOf( ":", bountyIndex + 1 );
String unknownBountyImage = unknownEasyBountyString.substring( 0, bountyIndex );
int unknownBountyNumber = StringUtilities.parseInt( unknownEasyBountyString.substring( bountyIndex + 1, bountyIndex2 ) );
String unknownBountyPlural = unknownEasyBountyString.substring( bountyIndex2 + 1 );
if ( bountyImage.equals( unknownBountyImage ) )
{
// Looks like a match !
BountyDatabase.setValue( bountyItem, unknownBountyPlural, "easy", unknownBountyImage,
unknownBountyNumber, monsterTrueCase, location );
Preferences.setString( "currentEasyBountyItem", bountyItem + ":" + bountyCount );
Preferences.setString( "_unknownEasyBountyItem", "" );
matched = true;
}
}
if ( matched == false && !unknownHardBountyString.equals( "" ) )
{
int bountyIndex = unknownHardBountyString.indexOf( ":" );
int bountyIndex2 = unknownHardBountyString.indexOf( ":", bountyIndex + 1 );
String unknownBountyImage = unknownHardBountyString.substring( 0, bountyIndex );
int unknownBountyNumber = StringUtilities.parseInt( unknownHardBountyString.substring( bountyIndex + 1, bountyIndex2 ) );
String unknownBountyPlural = unknownHardBountyString.substring( bountyIndex2 + 1 );
if ( bountyImage.equals( unknownBountyImage ) )
{
// Looks like a match !
BountyDatabase.setValue( bountyItem, unknownBountyPlural, "hard", unknownBountyImage,
unknownBountyNumber, monsterTrueCase, location );
Preferences.setString( "currentHardBountyItem", bountyItem + ":" + bountyCount );
Preferences.setString( "_unknownHardBountyItem", "" );
matched = true;
}
}
if ( matched == false && !unknownSpecialBountyString.equals( "" ) )
{
int bountyIndex = unknownSpecialBountyString.indexOf( ":" );
int bountyIndex2 = unknownSpecialBountyString.indexOf( ":", bountyIndex + 1 );
String unknownBountyImage = unknownSpecialBountyString.substring( 0, bountyIndex );
int unknownBountyNumber = StringUtilities.parseInt( unknownSpecialBountyString.substring( bountyIndex + 1, bountyIndex2 ) );
String unknownBountyPlural = unknownSpecialBountyString.substring( bountyIndex2 + 1 );
if ( bountyImage.equals( unknownBountyImage ) )
{
// Looks like a match !
BountyDatabase.setValue( bountyItem, unknownBountyPlural, "special", unknownBountyImage,
unknownBountyNumber, monsterTrueCase, location );
Preferences.setString( "currentSpecialBountyItem", bountyItem + ":" + bountyCount );
Preferences.setString( "_unknownSpecialBountyItem", "" );
matched = true;
}
}
if ( matched == false )
{
KoLmafia.updateDisplay( "Bounty Item " + bountyItem + " not yet known to KoLMafia." );
}
}
else if ( bountyType.equals( "easy" ) )
{
Preferences.setString( "currentEasyBountyItem", bountyItem + ":" + bountyCount );
}
else if ( bountyType.equals( "hard" ) )
{
Preferences.setString( "currentHardBountyItem", bountyItem + ":" + bountyCount );
}
else if ( bountyType.equals( "special" ) )
{
Preferences.setString( "currentSpecialBountyItem", bountyItem + ":" + bountyCount );
}
String updateMessage = "You acquire a bounty item: " + bountyItem;
AdventureResult result = AdventureResult.tallyItem( bountyItem, false );
AdventureResult.addResultToList( KoLConstants.tally, result );
RequestLogger.updateSessionLog( updateMessage );
KoLmafia.updateDisplay( updateMessage );
}
}
public static String accessible()
{
return null;
}
public static final boolean registerRequest( final String urlString )
{
if ( !urlString.startsWith( "bounty.php" ) )
{
return false;
}
String action = GenericRequest.getAction( urlString );
if ( action == null )
{
RequestLogger.updateSessionLog();
RequestLogger.updateSessionLog( "Visiting the Bounty Hunter Hunter" );
return true;
}
if ( action.equals( "takelow" ) )
{
String bountyName = Preferences.getString( "_untakenEasyBountyItem" );
if ( bountyName.equals( "" ) )
{
String bounty = Preferences.getString( "_unknownEasyBountyItem" );
if ( !bounty.equals( "" ) )
{
int bountyIndex = bounty.indexOf( ":" );
int bountyIndex2 = bounty.indexOf( ":", bountyIndex + 1 );
int bountyNumber = StringUtilities.parseInt( bounty.substring( bountyIndex + 1, bountyIndex2 ) );
String plural = bounty.substring( bountyIndex2 + 1 );
RequestLogger.updateSessionLog();
RequestLogger.updateSessionLog( "accept unknown easy bounty assignment to collect " + bountyNumber + " " + plural );
}
else
{
RequestLogger.printLine( "no easy bounty accepted" );
}
return true;
}
int bountyNumber = BountyDatabase.getNumber( bountyName );
String plural = BountyDatabase.getPlural( bountyName );
RequestLogger.updateSessionLog();
RequestLogger.updateSessionLog( "accept easy bounty assignment to collect " + bountyNumber + " " + plural );
return true;
}
if ( action.equals( "takehigh" ) )
{
String bountyName = Preferences.getString( "_untakenHardBountyItem" );
if ( bountyName.equals( "" ) )
{
String bounty = Preferences.getString( "_unknownHardBountyItem" );
if ( !bounty.equals( "" ) )
{
int bountyIndex = bounty.indexOf( ":" );
int bountyIndex2 = bounty.indexOf( ":", bountyIndex + 1 );
int bountyNumber = StringUtilities.parseInt( bounty.substring( bountyIndex + 1, bountyIndex2 ) );
String plural = bounty.substring( bountyIndex2 + 1 );
RequestLogger.updateSessionLog();
RequestLogger.updateSessionLog( "accept unknown hard bounty assignment to collect " + bountyNumber + " " + plural );
}
else
{
RequestLogger.printLine( "no hard bounty accepted" );
}
return true;
}
int bountyNumber = BountyDatabase.getNumber( bountyName );
String plural = BountyDatabase.getPlural( bountyName );
RequestLogger.updateSessionLog();
RequestLogger.updateSessionLog( "accept hard bounty assignment to collect " + bountyNumber + " " + plural );
return true;
}
if ( action.equals( "takespecial" ) )
{
String bountyName = Preferences.getString( "_untakenSpecialBountyItem" );
if ( bountyName.equals( "" ) )
{
String bounty = Preferences.getString( "_unknownSpecialBountyItem" );
if ( !bounty.equals( "" ) )
{
int bountyIndex = bounty.indexOf( ":" );
int bountyIndex2 = bounty.indexOf( ":", bountyIndex + 1 );
int bountyNumber = StringUtilities.parseInt( bounty.substring( bountyIndex + 1, bountyIndex2 ) );
String plural = bounty.substring( bountyIndex2 + 1 );
RequestLogger.updateSessionLog();
RequestLogger.updateSessionLog( "accept unknown speciality bounty assignment to collect " + bountyNumber + " " + plural );
}
else
{
RequestLogger.printLine( "no speciality bounty accepted" );
}
return true;
}
int bountyNumber = BountyDatabase.getNumber( bountyName );
String plural = BountyDatabase.getPlural( bountyName );
RequestLogger.updateSessionLog();
RequestLogger.updateSessionLog( "accept specialty bounty assignment to collect " + bountyNumber + " " + plural );
return true;
}
if ( action.equals( "giveup_low" ) )
{
RequestLogger.updateSessionLog();
RequestLogger.updateSessionLog( "abandon easy bounty assignment" );
return true;
}
if ( action.equals( "giveup_high" ) )
{
RequestLogger.updateSessionLog();
RequestLogger.updateSessionLog( "abandon hard bounty assignment" );
return true;
}
if ( action.equals( "giveup_spe" ) )
{
RequestLogger.updateSessionLog();
RequestLogger.updateSessionLog( "abandon special bounty assignment" );
return true;
}
CoinmasterData data = BountyHunterHunterRequest.BHH;
return CoinMasterRequest.registerRequest( data, urlString );
}
}