/**
* 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.webui;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import net.sourceforge.kolmafia.KoLAdventure;
import net.sourceforge.kolmafia.KoLmafia;
import net.sourceforge.kolmafia.MonsterData;
import net.sourceforge.kolmafia.RequestEditorKit;
import net.sourceforge.kolmafia.combat.MonsterStatusTracker;
import net.sourceforge.kolmafia.objectpool.AdventurePool;
import net.sourceforge.kolmafia.objectpool.ItemPool;
import net.sourceforge.kolmafia.persistence.SkillDatabase;
import net.sourceforge.kolmafia.preferences.Preferences;
import net.sourceforge.kolmafia.request.FightRequest;
import net.sourceforge.kolmafia.request.GenericRequest;
import net.sourceforge.kolmafia.session.DadManager;
import net.sourceforge.kolmafia.session.InventoryManager;
import net.sourceforge.kolmafia.utilities.StringUtilities;
public class FightDecorator
{
private static final Pattern SKILL_FORM_PATTERN = Pattern.compile( "<form name=skill.*?</form>", Pattern.DOTALL );
private static final Pattern SKILL_OPTION_PATTERN = Pattern.compile( "<option value=\\\"(\\d+)\\\".*?</option>", Pattern.DOTALL );
public static final void selectSkill( final StringBuffer buffer, final String skill )
{
// Extract the "skill" form from the buffer
Matcher matcher = SKILL_FORM_PATTERN.matcher( buffer );
if ( !matcher.find() )
{
return;
}
// Find the desired skill
String oldForm = matcher.group( 0 );
String search = ">" + skill;
if ( !oldForm.contains( search ) )
{
return;
}
// Found it.
StringBuffer newForm = new StringBuffer( oldForm );
// If a skill is already selected, deselect it
StringUtilities.globalStringDelete( newForm, "selected" );
// ... which may have moved the desired skill
// Select the skill
newForm.insert( newForm.indexOf( search ), " selected" );
// Replace the skill form with the munged version
StringUtilities.singleStringReplace( buffer, oldForm, newForm.toString() );
}
public static final void decorateMonster( final StringBuffer buffer )
{
// If we won the fight and got the volcano map, force a topmenu
// refresh so that the "volcano" link is there.
if ( buffer.indexOf( "WINWINWIN" ) != -1 &&
buffer.indexOf( "secret tropical island volcano lair map" ) != -1 )
{
RequestEditorKit.addTopmenuRefresh( buffer );
}
if ( !Preferences.getBoolean( "relayShowSpoilers" ) )
{
return;
}
MonsterData monster = MonsterStatusTracker.getLastMonster();
if ( monster == null )
{
return;
}
String name = monster.getName().toLowerCase();
if ( name.equals( "dad sea monkee" ) )
{
FightDecorator.decorateDadSeaMonkee( buffer );
return;
}
if ( name.endsWith( "balldodger" ) )
{
FightDecorator.decorateBallDodger( buffer );
return;
}
if ( name.endsWith( "bladeswitcher" ) )
{
FightDecorator.decorateBladeSwitcher( buffer );
return;
}
if ( name.endsWith( "netdragger" ) )
{
FightDecorator.decorateNetDragger( buffer );
return;
}
if ( name.equals( "falls-from-sky" ) )
{
FightDecorator.decorateFallsFromSky( buffer );
return;
}
if ( name.equals( "writing desk" ) )
{
FightDecorator.decorateWritingDesk( buffer );
return;
}
if ( name.equals( "unusual construct" ) )
{
FightDecorator.decorateUnusualConstruct( buffer );
}
if ( name.equals( "performer of actions" ) || name.equals( "thinker of thoughts" ) || name.equals( "perceiver of sensations" ) )
{
FightDecorator.decorateMachineTunnelFight( name, buffer );
return;
}
if ( FightRequest.isSourceAgent( monster ) )
{
FightDecorator.decorateSourceAgent( buffer );
return;
}
}
public static final void decorateLocation( final StringBuffer buffer )
{
if ( !Preferences.getBoolean( "relayShowSpoilers" ) )
{
return;
}
int adventure = KoLAdventure.lastAdventureId();
switch ( adventure )
{
case AdventurePool.HAUNTED_KITCHEN:
FightDecorator.decorateHauntedKitchen( buffer );
break;
case AdventurePool.TRAINING_SNOWMAN:
FightDecorator.decorateSnojo( buffer );
break;
}
}
private static final void decorateDadSeaMonkee( final StringBuffer buffer )
{
int round = FightRequest.currentRound;
if ( round < 1 || round > 10 )
{
return;
}
DadManager.Element element = DadManager.weakness( round );
if ( element == DadManager.Element.NONE )
{
return;
}
String spell = DadManager.elementToSpell( element );
if ( spell == null )
{
return;
}
FightDecorator.selectSkill( buffer, spell );
}
public static final void decorateMachineTunnelFight( final String monster, final StringBuffer buffer )
{
if ( monster.equals( "thinker of thoughts" ) )
{
RequestEditorKit.selectOption( buffer, "whichitem", String.valueOf( ItemPool.ABSTRACTION_ACTION ) );
}
else if ( monster.equals( "performer of actions" ) )
{
RequestEditorKit.selectOption( buffer, "whichitem", String.valueOf( ItemPool.ABSTRACTION_SENSATION ) );
}
else if ( monster.equals( "perceiver of sensations" ) )
{
RequestEditorKit.selectOption( buffer, "whichitem", String.valueOf( ItemPool.ABSTRACTION_THOUGHT ) );
}
}
private static final void decorateBallDodger( final StringBuffer buffer )
{
// Looks like he's trying to gain an advantage over you...
if ( buffer.indexOf( "<b>gain</b>" ) != -1 )
{
FightDecorator.selectSkill( buffer, "Net Gain" );
return;
}
// He gets a crazy look in his eyes -- like he's about to experience a serious loss of control...
if ( buffer.indexOf( "<b>loss</b>" ) != -1 )
{
FightDecorator.selectSkill( buffer, "Net Loss" );
return;
}
// His facial features take on an ominous neutrality.
if ( buffer.indexOf( "<b>neutrality</b>" ) != -1 )
{
FightDecorator.selectSkill( buffer, "Net Neutrality" );
return;
}
}
private static final void decorateBladeSwitcher( final StringBuffer buffer )
{
// He begins to bust an especially dope move with his switchblade.
if ( buffer.indexOf( "<b>bust</b>" ) != -1 )
{
FightDecorator.selectSkill( buffer, "Ball Bust" );
return;
}
// He pauses to wipe the sweat from his brow.
if ( buffer.indexOf( "<b>sweat</b>" ) != -1 )
{
FightDecorator.selectSkill( buffer, "Ball Sweat" );
return;
}
// He pulls a little bottle of oil out of his sack and applies it to his switchblade.
if ( buffer.indexOf( "<b>sack</b>" ) != -1 )
{
FightDecorator.selectSkill( buffer, "Ball Sack" );
return;
}
}
private static final void decorateNetDragger( final StringBuffer buffer )
{
// He starts to fold his net up into some sort of a sling.
if ( buffer.indexOf( "<b>sling</b>" ) != -1 )
{
FightDecorator.selectSkill( buffer, "Blade Sling" );
return;
}
// He rolls his net up and draws it back like a baseball bat.
if ( buffer.indexOf( "<b>rolls</b>" ) != -1 )
{
FightDecorator.selectSkill( buffer, "Blade Roller" );
return;
}
// If you were a runner, you'd be tempted to run right now...
if ( buffer.indexOf( "<b>runner</b>" ) != -1 )
{
FightDecorator.selectSkill( buffer, "Blade Runner" );
return;
}
}
private static final void decorateFallsFromSky( final StringBuffer buffer )
{
// While under the effect of Chilled to the Bone,
// Falls-From-Sky can do multiple attacks per round.
//
// Only the last one determines the correct response.
int circle = buffer.lastIndexOf( "begins to spin in a circle" );
int paw = buffer.lastIndexOf( "begins to paw at the ground" );
int shuffle = buffer.lastIndexOf( "shuffles toward you" );
if ( circle > paw && circle > shuffle )
{
FightDecorator.selectSkill( buffer, "Hide Under a Rock" );
return;
}
if ( paw > circle && paw > shuffle )
{
FightDecorator.selectSkill( buffer, "Dive Into a Puddle" );
return;
}
if ( shuffle > circle && shuffle > paw )
{
FightDecorator.selectSkill( buffer, "Hide Behind a Tree" );
return;
}
}
private static final void decorateWritingDesk( final StringBuffer buffer )
{
String indexString = "any necklaces.";
int index = buffer.indexOf( indexString );
if ( index == -1 )
{
return;
}
index += indexString.length();
buffer.insert( index, " (" + Preferences.getInteger( "writingDesksDefeated" ) + "/5 defeated)" );
}
private static final void decorateSourceAgent( final StringBuffer buffer )
{
// Extract the "skill" form from the buffer
Matcher skillForm = SKILL_FORM_PATTERN.matcher( buffer );
if ( !skillForm.find() )
{
return;
}
Matcher option = SKILL_OPTION_PATTERN.matcher( skillForm.group( 0 ) );
while( option.find() )
{
// Remove skills not starting with 21 as they can't be used
// Allow new source terminal skills until we know which are usable
int skillId = StringUtilities.parseInt( option.group( 1 ) );
if ( !SkillDatabase.sourceAgentSkill( skillId ) )
{
StringUtilities.singleStringDelete( buffer, option.group( 0 ) );
}
}
}
private static final void decorateHauntedKitchen( final StringBuffer buffer )
{
if ( InventoryManager.hasItem( ItemPool.BILLIARDS_KEY ) )
{
// Don't show progress on the turn where the key is received
return;
}
// The kitchen's resident flame-belching demon oven kicks into serious overdrive,
// but you manage to tolerate the heat long enough to search through X drawers.
// The garbage disposal turns itself on, filling the kitchen with an indescribably foul
// odor, but you manage to tolerate it long enough to search through X drawers.
String indexString = "drawers.";
int index = buffer.indexOf( indexString );
if ( index == -1 )
{
// You manage to dig through a single drawer looking for the key, but the
// garbage disposal turns itself on, releasing a terrible, terrible smell.
// It drives you back out into the hallway.
indexString = "hallway.";
index = buffer.indexOf( indexString );
if ( index == -1 )
{
// You manage to dig through a single drawer looking for the key,
// but the constant demonic flames belching out of the oven results
// in the kitchen getting too hot for you, and you have to get out of it.
indexString = "out of it.";
index = buffer.indexOf( indexString );
if ( index == -1 )
{
return;
}
}
}
index += indexString.length();
int checked = Preferences.getInteger( "manorDrawerCount" );
StringBuilder insertBuffer = new StringBuilder();
insertBuffer.append( " (" ).append( checked ).append( "/21 searched" );
if ( checked >= 21 )
{
insertBuffer.append( ", key next combat" );
}
insertBuffer.append( ")" );
buffer.insert( index, insertBuffer );
}
private static final void decorateSnojo( final StringBuffer buffer )
{
String indexString = "Adventure Again (The X-32-F Combat Training Snowman)";
int index = buffer.indexOf( indexString );
if ( index == -1 ) return;
index += indexString.length();
int turns = Preferences.getInteger( "_snojoFreeFights" );
StringBuilder insertBuffer = new StringBuilder();
insertBuffer.append( " (" ).append( turns ).append( " free fight" ).append( turns == 1 ? "" : "s" ).append( " used)" );
buffer.insert( index, insertBuffer );
}
private static final void decorateUnusualConstruct( final StringBuffer buffer )
{
Pattern COLOR_PATTERN = Pattern.compile( "(?:LANO|ROUTING) ([a-zA-Z]*)" );
Matcher matcher = COLOR_PATTERN.matcher( buffer );
String colorWord = null;
if ( matcher.find() )
{
colorWord = matcher.group( 1 );
}
else
{
// Something went wrong
RequestEditorKit.selectOption( buffer, "whichitem", String.valueOf( ItemPool.SEAL_TOOTH ) );
return;
}
if ( colorWord.equals( "CHO" )
|| colorWord.equals( "FUNI" )
|| colorWord.equals( "TAZAK" )
|| colorWord.equals( "CANARY" )
|| colorWord.equals( "CITRINE" )
|| colorWord.equals( "GOLD" )
)
{
RequestEditorKit.selectOption( buffer, "whichitem", String.valueOf( ItemPool.STRANGE_DISC_YELLOW ) );
return;
}
if ( colorWord.equals( "CHAKRO" )
|| colorWord.equals( "ZEVE" )
|| colorWord.equals( "ZEVESTANO" )
|| colorWord.equals( "CRIMSON" )
|| colorWord.equals( "RUBY" )
|| colorWord.equals( "VERMILLION" )
)
{
RequestEditorKit.selectOption( buffer, "whichitem", String.valueOf( ItemPool.STRANGE_DISC_RED ) );
return;
}
if ( colorWord.equals( "BUPABU" )
|| colorWord.equals( "PATA" )
|| colorWord.equals( "SOM" )
|| colorWord.equals( "OBSIDIAN" )
|| colorWord.equals( "EBONY" )
|| colorWord.equals( "JET" )
)
{
RequestEditorKit.selectOption( buffer, "whichitem", String.valueOf( ItemPool.STRANGE_DISC_BLACK ) );
return;
}
if ( colorWord.equals( "BE" )
|| colorWord.equals( "ZAKSOM" )
|| colorWord.equals( "ZEVEBENI" )
|| colorWord.equals( "JADE" )
|| colorWord.equals( "VERDIGRIS" )
|| colorWord.equals( "EMERALD" )
)
{
RequestEditorKit.selectOption( buffer, "whichitem", String.valueOf( ItemPool.STRANGE_DISC_GREEN ) );
return;
}
if ( colorWord.equals( "BELA" )
|| colorWord.equals( "BULAZAK" )
|| colorWord.equals( "BU" )
|| colorWord.equals( "FUFUGAKRO" )
|| colorWord.equals( "ULTRAMARINE" )
|| colorWord.equals( "SAPPHIRE" )
|| colorWord.equals( "COBALT" )
)
{
RequestEditorKit.selectOption( buffer, "whichitem", String.valueOf( ItemPool.STRANGE_DISC_BLUE ) );
return;
}
if ( colorWord.equals( "NIPA" )
|| colorWord.equals( "PACHA" )
|| colorWord.equals( "SOMPAPA" )
|| colorWord.equals( "IVORY" )
|| colorWord.equals( "ALABASTER" )
|| colorWord.equals( "PEARL" )
)
{
RequestEditorKit.selectOption( buffer, "whichitem", String.valueOf( ItemPool.STRANGE_DISC_WHITE ) );
return;
}
// If we made it this far, then we don't recognize the word yet. Change to a bogus result to avoid
// being misleading, until full support is added.
RequestEditorKit.selectOption( buffer, "whichitem", String.valueOf( ItemPool.SEAL_TOOTH ) );
}
private static final String END_TAG = "<a name=\"end\"></a>";
public static final void decorateEndOfFight( final StringBuffer buffer )
{
if ( buffer.indexOf( "fight.php" ) != -1 )
{
return;
}
// If this was a Time-Spinner monster
if ( GenericRequest.itemMonster != null &&
GenericRequest.itemMonster.equals( "Time-Spinner" ) )
{
int inventoryLink = buffer.indexOf( "<Center><a href=\"inventory.php\">Back to your Inventory</a></center>" );
if ( inventoryLink != -1 )
{
StringBuilder link = new StringBuilder();
// inv_use.php?whichitem=9104&pwd
link.append( "<center><a href=\"inv_use.php?whichitem=9104&pwd=" );
link.append( GenericRequest.passwordHash );
link.append( "\">Back to your Time-Spinner</a></center><br>" );
buffer.insert( inventoryLink, link.toString() );
}
return;
}
int uses = 0;
if ( GenericRequest.itemMonster != null &&
GenericRequest.itemMonster.equals( "lynyrd snare" ) &&
InventoryManager.getCount( ItemPool.LYNYRD_SNARE ) > 0 &&
( uses = Preferences.getInteger( "_lynyrdSnareUses" ) ) < 3 )
{
int index = buffer.indexOf( END_TAG );
if ( index != -1 )
{
StringBuilder link = new StringBuilder();
// inv_use.php?whichitem=7204&pwd
link.append( "<p><a href=\"inv_use.php?whichitem=7204&pwd=" );
link.append( GenericRequest.passwordHash );
link.append( "\">Use another lynyrd snare (" );
link.append( String.valueOf( uses ) );
link.append( "/3 lynyrds fought today)</a>" );
buffer.insert( index + END_TAG.length() , link.toString() );
}
return;
}
}
}