/** * 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; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import net.sourceforge.kolmafia.KoLCharacter; import net.sourceforge.kolmafia.objectpool.ItemPool; import net.sourceforge.kolmafia.persistence.MonsterDatabase.Element; import net.sourceforge.kolmafia.preferences.Preferences; import net.sourceforge.kolmafia.session.ResultProcessor; import net.sourceforge.kolmafia.utilities.StringUtilities; public class VYKEACompanionData implements Comparable<VYKEACompanionData> { public static final String [] VYKEA = { "level 1 bookshelf", "level 1 frenzy bookshelf", "level 1 blood bookshelf", "level 1 lightning bookshelf", "level 2 bookshelf", "level 2 frenzy bookshelf", "level 2 blood bookshelf", "level 2 lightning bookshelf", "level 3 bookshelf", "level 3 frenzy bookshelf", "level 3 blood bookshelf", "level 3 lightning bookshelf", "level 4 bookshelf", "level 4 frenzy bookshelf", "level 4 blood bookshelf", "level 4 lightning bookshelf", "level 5 bookshelf", "level 5 frenzy bookshelf", "level 5 blood bookshelf", "level 5 lightning bookshelf", "level 1 ceiling fan", "level 1 frenzy ceiling fan", "level 1 blood ceiling fan", "level 1 lightning ceiling fan", "level 2 ceiling fan", "level 2 frenzy ceiling fan", "level 2 blood ceiling fan", "level 2 lightning ceiling fan", "level 3 ceiling fan", "level 3 frenzy ceiling fan", "level 3 blood ceiling fan", "level 3 lightning ceiling fan", "level 4 ceiling fan", "level 4 frenzy ceiling fan", "level 4 blood ceiling fan", "level 4 lightning ceiling fan", "level 5 ceiling fan", "level 5 frenzy ceiling fan", "level 5 blood ceiling fan", "level 5 lightning ceiling fan", "level 1 couch", "level 1 frenzy couch", "level 1 blood couch", "level 1 lightning couch", "level 2 couch", "level 2 frenzy couch", "level 2 blood couch", "level 2 lightning couch", "level 3 couch", "level 3 frenzy couch", "level 3 blood couch", "level 3 lightning couch", "level 4 couch", "level 4 frenzy couch", "level 4 blood couch", "level 4 lightning couch", "level 5 couch", "level 5 frenzy couch", "level 5 blood couch", "level 5 lightning couch", "level 1 dishrack", "level 1 frenzy dishrack", "level 1 blood dishrack", "level 1 lightning dishrack", "level 2 dishrack", "level 2 frenzy dishrack", "level 2 blood dishrack", "level 2 lightning dishrack", "level 3 dishrack", "level 3 frenzy dishrack", "level 3 blood dishrack", "level 3 lightning dishrack", "level 4 dishrack", "level 4 frenzy dishrack", "level 4 blood dishrack", "level 4 lightning dishrack", "level 5 dishrack", "level 5 frenzy dishrack", "level 5 blood dishrack", "level 5 lightning dishrack", "level 1 dresser", "level 1 frenzy dresser", "level 1 blood dresser", "level 1 lightning dresser", "level 2 dresser", "level 2 frenzy dresser", "level 2 blood dresser", "level 2 lightning dresser", "level 3 dresser", "level 3 frenzy dresser", "level 3 blood dresser", "level 3 lightning dresser", "level 4 dresser", "level 4 frenzy dresser", "level 4 blood dresser", "level 4 lightning dresser", "level 5 dresser", "level 5 frenzy dresser", "level 5 blood dresser", "level 5 lightning dresser", "level 1 lamp", "level 1 frenzy lamp", "level 1 blood lamp", "level 1 lightning lamp", "level 2 lamp", "level 2 frenzy lamp", "level 2 blood lamp", "level 2 lightning lamp", "level 3 lamp", "level 3 frenzy lamp", "level 3 blood lamp", "level 3 lightning lamp", "level 4 lamp", "level 4 frenzy lamp", "level 4 blood lamp", "level 4 lightning lamp", "level 5 lamp", "level 5 frenzy lamp", "level 5 blood lamp", "level 5 lightning lamp", }; public final static int NONE = 0; public final static int BOOKSHELF = 1; public final static int DRESSER = 2; public final static int CEILING_FAN = 3; public final static int COUCH = 4; public final static int LAMP = 5; public final static int DISHRACK = 6; public final static AdventureResult NO_RUNE = ItemPool.get( "(none)", 1 ); public final static AdventureResult FRENZY_RUNE = ItemPool.get( ItemPool.VYKEA_FRENZY_RUNE, 1 ); public final static AdventureResult BLOOD_RUNE = ItemPool.get( ItemPool.VYKEA_BLOOD_RUNE, 1 ); public final static AdventureResult LIGHTNING_RUNE = ItemPool.get( ItemPool.VYKEA_LIGHTNING_RUNE, 1 ); private final int type; private final int level; private final AdventureResult rune; private final String name; // Derived fields private final String image; private final Element attackElement; private final String modifiers; private String stringForm; public static final VYKEACompanionData NO_COMPANION = new VYKEACompanionData(); public static VYKEACompanionData currentCompanion = VYKEACompanionData.NO_COMPANION; public static void initialize( final boolean loadSettings ) { VYKEACompanionData.currentCompanion = VYKEACompanionData.NO_COMPANION; if ( loadSettings ) { VYKEACompanionData.settingsToVYKEACompanion(); } } private static void setVYKEACompanion( final VYKEACompanionData companion, final boolean setSettings ) { VYKEACompanionData.currentCompanion = companion; if ( setSettings ) { Preferences.setString( "_VYKEACompanionName", companion.name ); Preferences.setInteger( "_VYKEACompanionLevel", companion.level ); Preferences.setString( "_VYKEACompanionType", companion.typeToString() ); Preferences.setString( "_VYKEACompanionRune", companion.runeToString() ); } } public static final VYKEACompanionData currentCompanion() { return VYKEACompanionData.currentCompanion; } public VYKEACompanionData() { this( NONE, 0, NO_RUNE, "" ); } public VYKEACompanionData( final int type, final int level, final AdventureResult rune, final String name) { this.type = type; this.level = level; this.rune = rune; this.name = name == null ? "" : name; // Derived fields this.image = ( type < 1 || type > 6 ) ? "" : ( "vykfurn" + String.valueOf( type ) + ".gif" ); switch ( this.type ) { case BOOKSHELF: this.attackElement = Element.SPOOKY; this.modifiers = ""; break; case DRESSER: this.attackElement = Element.SLEAZE; this.modifiers = ""; break; case CEILING_FAN: this.attackElement = Element.COLD; this.modifiers = ""; break; case COUCH: this.attackElement = Element.NONE; this.modifiers = "Meat Drop: +" + String.valueOf( this.level * 10 ) ; break; case LAMP: this.attackElement = Element.HOT; this.modifiers = "Item Drop: +" + String.valueOf( this.level * 10 ) ; break; case DISHRACK: this.attackElement = Element.STENCH; this.modifiers = ""; break; default: this.attackElement = Element.NONE; this.modifiers = ""; break; } // Build this on demand this.stringForm = null; } public int getType() { return this.type; } public int getLevel() { return this.level; } public AdventureResult getRune() { return this.rune; } public String getName() { return this.name; } public String getImage() { return this.image; } public String getModifiers() { return this.modifiers; } public Element getAttackElement() { return this.attackElement; } public static String typeToString( final int type ) { switch( type ) { case BOOKSHELF: return "bookshelf"; case CEILING_FAN: return "ceiling fan"; case COUCH: return "couch"; case DISHRACK: return "dishrack"; case DRESSER: return "dresser"; case LAMP: return "lamp"; } return "unknown"; } public String typeToString() { return VYKEACompanionData.typeToString( this.type ); } public static int stringToType( final String type ) { return type == null ? NONE : type.equals( "bookshelf" ) ? BOOKSHELF : type.equals( "ceiling fan" ) ? CEILING_FAN : type.equals( "couch" ) ? COUCH : type.equals( "dishrack" ) ? DISHRACK : type.equals( "dresser" ) ? DRESSER : type.equals( "lamp" ) ? LAMP : NONE; } public static String runeToString( final AdventureResult rune ) { switch( rune.getItemId() ) { case ItemPool.VYKEA_FRENZY_RUNE: return "frenzy"; case ItemPool.VYKEA_BLOOD_RUNE: return "blood"; case ItemPool.VYKEA_LIGHTNING_RUNE: return "lightning"; } return ""; } public String runeToString() { return VYKEACompanionData.runeToString( this.rune ); } public static AdventureResult stringToRune( final String rune ) { return rune == null ? NO_RUNE : rune.equals( "frenzy" ) ? FRENZY_RUNE : rune.equals( "blood" ) ? BLOOD_RUNE : rune.equals( "lightning" ) ? LIGHTNING_RUNE : NO_RUNE; } // CHEBLI the level 5 lamp private final static Pattern COMPANION_CHARPANE_PATTERN = Pattern.compile( "<b>(.*?)</b> the level (\\d).*(bookshelf|ceiling fan|couch|dishrack|dresser|lamp)" ); public static void parseCharpaneCompanion( final String string ) { // Once you have created a companion today, you can't change it. // Don't waste time parsing it. if ( VYKEACompanionData.currentCompanion != VYKEACompanionData.NO_COMPANION ) { return; } Matcher matcher = COMPANION_CHARPANE_PATTERN.matcher( string ); if ( matcher.find() ) { String name = matcher.group( 1 ); int level = StringUtilities.parseInt( matcher.group( 2 ) ); String typeString = matcher.group( 3 ); int type = VYKEACompanionData.stringToType( typeString ); // Use last saved rune AdventureResult rune = VYKEACompanionData.stringToRune( Preferences.getString( "_VYKEACompanionRune" ) ); VYKEACompanionData companion = new VYKEACompanionData( type, level, rune, name ); VYKEACompanionData.setVYKEACompanion( companion, true ); } } public static void settingsToVYKEACompanion() { String name = Preferences.getString( "_VYKEACompanionName" ); int level = Preferences.getInteger( "_VYKEACompanionLevel" ); int type = VYKEACompanionData.stringToType( Preferences.getString( "_VYKEACompanionType" ) ); AdventureResult rune = VYKEACompanionData.stringToRune( Preferences.getString( "_VYKEACompanionRune" ) ); VYKEACompanionData companion = type == NONE ? NO_COMPANION : new VYKEACompanionData( type, level, rune, name); VYKEACompanionData.setVYKEACompanion( companion, false ); } @Override public String toString() { if ( this.stringForm == null ) { StringBuilder buffer = new StringBuilder(); if ( this.name != null && !this.name.equals( "" ) ) { buffer.append( this.name ); buffer.append( ", the " ); } buffer.append( "level " ); buffer.append( String.valueOf( this.level ) ); if ( this.rune != VYKEACompanionData.NO_RUNE ) { buffer.append( " " ); buffer.append( this.runeToString() ); } buffer.append( " " ); buffer.append( this.typeToString() ); this.stringForm = buffer.toString(); } return this.stringForm; } // CHEBLI, the level 5 blood lamp private final static Pattern COMPANION_PATTERN = Pattern.compile( " *(?:(.*?), the )?level ([12345])(?: (blood|frenzy|lightning))? (bookshelf|ceiling fan|couch|dishrack|dresser|lamp) *" ); public static VYKEACompanionData fromString( final String string ) { Matcher matcher = COMPANION_PATTERN.matcher( string ); if ( matcher.find() ) { String name = matcher.group( 1 ); int level = StringUtilities.parseInt( matcher.group( 2 ) ); String runeString = matcher.group( 3 ); AdventureResult rune = VYKEACompanionData.stringToRune( runeString ); String typeString = matcher.group( 4 ); int type = VYKEACompanionData.stringToType( typeString ); return new VYKEACompanionData( type, level, rune, name ); } return null; } // <span class='guts'>You bolt 5 more rails onto the piece of furniture and take a step back to admire your new... lamp. It's a lamp!<p>You decide to name it... <b>ÅVOBÉ</b></span> private final static Pattern CREATION_PATTERN = Pattern.compile( "<span class='guts'>.*?It's a (bookshelf|ceiling fan|couch|dishrack|dresser|lamp).*?<b>(.*?)</b></span>", Pattern.DOTALL ); public static void assembleCompanion( final int choice, final int decision, final String text ) { // choice 1120 - Some Assembly Required. // 1 - Start with 5 planks -> choice 1121 (if you have a rune) or choice 1122 (if you don't) // 2 - Start with 5 rails -> 1121 (if you have a rune) or choice 1122 (if you don't) // 6 - don't build anything if ( choice == 1120 ) { switch ( decision ) { case 1: // Start with 5 planks -> bookshelf, ceiling fan, dresser ResultProcessor.processItem( ItemPool.VYKEA_PLANK, -5 ); break; case 2: // Start with 5 rails -> couch, dishrack, lamp ResultProcessor.processItem( ItemPool.VYKEA_RAIL, -5 ); break; case 6: // Do nothing return; default: // Invalid decision, presumably from URL manipulation. return; } // You've started construction and cannot abort from // here on. Remove the instructions from inventory. ResultProcessor.processItem( ItemPool.VYKEA_INSTRUCTIONS, -1 ); // Initialize preferences Preferences.setString( "_VYKEACompanionName", "" ); Preferences.setInteger( "_VYKEACompanionLevel", 0 ); Preferences.setString( "_VYKEACompanionType", "" ); Preferences.setString( "_VYKEACompanionRune", "" ); return; } // choice 1121 - Some Assembly Required // 1 - Add a frenzy rune -> choice 1122 (if you have at least 1 dowel) or 1123 (if you don't) // 2 - Add a blood rune -> choice 1122 (if you have at least 1 dowel) or 1123 (if you don't) // 3 - Add a lightning rune -> choice 1122 (if you have at least 1 dowel) or 1123 (if you don't) // 6 - Don't add any runes -> choice 1122 (if you have at least 1 dowel) or 1123 (if you don't) if ( choice == 1121 ) { AdventureResult rune = NO_RUNE; switch ( decision ) { case 1: rune = FRENZY_RUNE; break; case 2: rune = BLOOD_RUNE; break; case 3: rune = LIGHTNING_RUNE; break; case 6: // Don't add any runes break; default: // Invalid decision, presumably from URL manipulation. return; } // Save the rune in the preference Preferences.setString( "_VYKEACompanionRune", VYKEACompanionData.runeToString( rune ) ); // Remove the rune from inventory if ( rune != NO_RUNE ) { ResultProcessor.processItem( rune.getItemId(), -1 ); } return; } // choice 1122 - Some Assembly Required // 1 - Add 1 dowel -> choice 1123 // 2 - Add 11 dowels -> choice 1123 // 3 - Add 23 dowels -> choice 1123 // 4 - Add 37 dowels -> choice 1123 // 6 - Don't add any dowels -> choice 1123 if ( choice == 1122 ) { int level = 1; int dowels = 0; switch ( decision ) { case 1: level = 2; dowels = 1; break; case 2: level = 3; dowels = 11; break; case 3: level = 4; dowels = 23; break; case 4: level = 5; dowels = 37; break; case 6: // Do not add any dowels break; default: // Invalid decision, presumably from URL manipulation. return; } // Save the level in the preference Preferences.setInteger( "_VYKEACompanionLevel", level ); // Remove the dowels from inventory if ( dowels > 0 ) { ResultProcessor.processItem( ItemPool.VYKEA_DOWEL, -dowels ); } return; } // choice 1123 - Some Assembly Required // 1 - Add 5 planks // 2 - Add 5 rails // 3 - Add 5 brackets if ( choice == 1123 ) { switch ( decision ) { case 1: // Add 5 planks -> bookshelf, couch ResultProcessor.processItem( ItemPool.VYKEA_PLANK, -5 ); break; case 2: // Add 5 rails -> dresser, lamp ResultProcessor.processItem( ItemPool.VYKEA_RAIL, -5 ); break; case 3: // Add 5 brackets -> ceiling fan, dishrack ResultProcessor.processItem( ItemPool.VYKEA_BRACKET, -5 ); break; default: // Invalid decision, presumably from URL manipulation. return; } // Parse companion name and type from the result text Matcher matcher = CREATION_PATTERN.matcher( text ); if ( !matcher.find() ) { // Unexpected. We'll pick it up from the charpane. return; } String name = matcher.group( 2 ); String typeString = matcher.group( 1 ); int type = VYKEACompanionData.stringToType( typeString ); // Set them into preferences Preferences.setString( "_VYKEACompanionName", name ); Preferences.setString( "_VYKEACompanionType", typeString ); int level = Preferences.getInteger( "_VYKEACompanionLevel" ); AdventureResult rune = VYKEACompanionData.stringToRune( Preferences.getString( "_VYKEACompanionRune" ) ); // Create the companion VYKEACompanionData companion = new VYKEACompanionData( type, level, rune, name); VYKEACompanionData.setVYKEACompanion( companion, false ); // Adjust modifiers KoLCharacter.recalculateAdjustments(); KoLCharacter.updateStatus(); } } public int compareTo( final VYKEACompanionData o ) { if ( o == null ) { throw new NullPointerException(); } if ( o == this ) { return 0; } if ( this.type != o.type ) { return this.type - o.type; } if ( this.rune != o.rune ) { return this.rune.getItemId() - o.rune.getItemId(); } if ( this.level != o.level ) { return this.level - o.level; } return 0; } }