/** * 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.Arrays; import java.util.Collections; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import net.sourceforge.kolmafia.objectpool.IntegerPool; import net.sourceforge.kolmafia.objectpool.SkillPool; import net.sourceforge.kolmafia.preferences.Preferences; import net.sourceforge.kolmafia.utilities.StringUtilities; public class PastaThrallData implements Comparable<PastaThrallData> { public static final Object[][] PASTA_THRALLS = { // Thrall type // Thrall ID // Setting name // Skill to bind // Pattern to find name when first summoned // Pattern to find name when subsequently summoned // Image file name // Tiny image file name // Level 1 Power // Level 5 Power // Level 10 Power // Canonical Thrall type { "Vampieroghi", IntegerPool.get( 1 ), "pastaThrall1", IntegerPool.get( SkillPool.BIND_VAMPIEROGHI ), // My name is written in blood across the history of // time . . . but you can call me <name>. Pattern.compile( "but you can call me ([^.]*)\\." ), // You conjure a pieroghi, and there is a hiss as it // becomes inflated with <name>'s presence. Pattern.compile( "inflated with ([^']*)'s presence" ), "vampieroghi.gif", "t_vampieroghi.gif", "Damage and restore HP during combat", "Dispel negative effects after combat", "Maximum HP: +60", StringUtilities.getCanonicalName( "Vampieroghi" ), }, { "Vermincelli", IntegerPool.get( 2 ), "pastaThrall2", IntegerPool.get( SkillPool.BIND_VERMINCELLI ), // I think little <name> will be the best helper. Pattern.compile( "I think little (.*?) will be the best helper\\." ), // You summon a tangled mass of noodles. There is a // rustling sound as <name> chews his way into the // world to occupy his new body. Pattern.compile( "rustling sound as (.*?) chews his way into the world" ), "vermincelli.gif", "t_vermincelli.gif", "Damage and restore MP during combat", "Attack and poison foe during combat", "Maximum MP: +30", StringUtilities.getCanonicalName( "Vermincelli" ), }, { "Angel Hair Wisp", IntegerPool.get( 3 ), "pastaThrall3", IntegerPool.get( SkillPool.BIND_ANGEL_HAIR_WISP ), // "You must call me <name>. You must give me form. I // must live." Pattern.compile( "You must call me ([^.]*])\\." ), // You concentrate, and summon a mass of writhing angel // hair. A chill perm eates the air as <name>'s spirit // enters it. "I live..." Pattern.compile( "A chill perm ?eates the air as (.*?)'s spirit enters it\\." ), "angelwisp.gif", "t_wisp.gif", "Initiative: +5 per level", "Prevent enemy critical hits", "Blocks enemy attacks", StringUtilities.getCanonicalName( "Angel Hair Wisp" ), }, { "Elbow Macaroni", IntegerPool.get( 4 ), "pastaThrall4", IntegerPool.get( SkillPool.BIND_UNDEAD_ELBOW_MACARONI ), // "<name>. My name is <name>." Pattern.compile( "My name is ([^.]*)\\." ), // You focus your thoughts and call out to <name>. He // claws his way up from beneath the ground at your // feet. Pattern.compile( "You focus your thoughts and call out to (.*?)\\." ), "macaroni.gif", "t_elbowmac.gif", "Muscle can't be lower than Mysticality", "Weapon Damage: +2 per level", "Critical Hit Percentage: +10", StringUtilities.getCanonicalName( "Elbow Macaroni" ), }, { "Penne Dreadful", IntegerPool.get( 5 ), "pastaThrall5", IntegerPool.get( SkillPool.BIND_PENNE_DREADFUL ), // "All right, palookah," the private eye says, opening // his mouth for the first time, "the name's // <name>. I'm a gumshoe. You know, a shamus, a // flatfoot, a sleuth. Pattern.compile( "the name's ([^.]*)\\." ), // You calm your mind, and imagine a skeletal assembly // of penne. A lone saxophone breaks the night's // stillness as it appears and <name> possesses it. Pattern.compile( "it appears and (.*?) possesses it" ), "pennedreadful.gif", "t_dreadful.gif", "Moxie can't be lower than Mysticality", "Delevel at start of combat", "Damage Reduction: +10", StringUtilities.getCanonicalName( "Penne Dreadful" ), }, { "Lasagmbie", IntegerPool.get( 6 ), "pastaThrall6", IntegerPool.get( SkillPool.BIND_LASAGMBIE ), // Okay. See you on the other side, <name>. Pattern.compile( "See you on the other side, (.*?)\\." ), // You conjure up a good-sized sheet of lasagna, and // there is a wet thud as <name>'s spirit lands in it. Pattern.compile( "a wet thud as ([^']*)'s spirit lands in it" ), "lasagmbie.gif", "t_lasagmbie.gif", "Meat Drop: +20 + 2 per level", "Attacks for Spooky Damage", "Spooky Spell Damage: +10", StringUtilities.getCanonicalName( "Lasagmbie" ), }, { "Spice Ghost", IntegerPool.get( 7 ), "pastaThrall7", IntegerPool.get( SkillPool.BIND_SPICE_GHOST ), // My name is <name>, and I am in your debt. Pattern.compile( "My name is ([^,]*), and I am in your debt\\." ), // You conjure up a swirling cloud of spicy dried // couscous, and there is a crackle of psychokinetic // energy as <name> possesses it. Pattern.compile( "crackle of psychokinetic energy as (.*?) possesses it\\." ), "spiceghost.gif", "t_spiceghost.gif", "Item Drop: +10 + 1 per level", "spice drop 10/day", "Increases duration of Entangling Noodles", StringUtilities.getCanonicalName( "Spice Ghost" ), }, { "Spaghetti Elemental", IntegerPool.get( 8 ), "pastaThrall8", IntegerPool.get( SkillPool.BIND_SPAGHETTI_ELEMENTAL ), // "I guess you need a name, huh?" you reply. "I'll // call you... um... SshoKodo. That'll do." Pattern.compile( "I'll call you... *um... *([^.]*). * That'll do." ), // You close your eyes and reach out across the border // between worlds, to the Elemental Plane of // Spaghetti. Soon you feel a familiar presence, and // pull SshoKodo into the material world. Pattern.compile( "and pull (.*?) into the material world\\." ), "spagelem1.gif", "t_spagdemon.gif", "Experience: 1-ceil(level/3)", "Prevents first attack", "Spell Damage: +5", StringUtilities.getCanonicalName( "Spaghetti Elemental" ), }, /* { "Boba Fettucini", IntegerPool.get( ItemPool.TWITCHING_TRIGGER_FINGER ), // You decide to name it <name>. Pattern.compile( "You decide to name it ([^.]*)\\." ), // <i>pew pew pew!"</i> <name> shouts excitedly, // drawing a laser pistol from some spiritual dimension // of necessity. Pattern.compile( "</i> (.*) shouts excitedly" ), "bobafett.gif", }, { "Bow Tie Bat", IntegerPool.get( ItemPool.SMOKING_TALON ), // Ugh. I'll take that guano as a yes? You'll need a // name. Let's call you..." You glance around, hoping // for some inspiration. "How about... <name>." Pattern.compile( "How about... ([^.]*)\\." ), // You call out to <name> with your mind, summoning his // spirit even as you create a body for him. He lets // loose an ear-splitting screech. Pattern.compile( "You call out to (.*?) with your mind" ), "bowtiebat.gif", }, */ }; public static final String [] THRALL_ARRAY = new String[ PastaThrallData.PASTA_THRALLS.length ]; public static final String [] CANONICAL_THRALL_ARRAY = new String[ PastaThrallData.PASTA_THRALLS.length ]; static { for ( int i = 0; i < PastaThrallData.THRALL_ARRAY.length; ++i ) { PastaThrallData.THRALL_ARRAY[ i ] = PastaThrallData.dataToType( PASTA_THRALLS[ i ] ); PastaThrallData.CANONICAL_THRALL_ARRAY[ i ] = PastaThrallData.dataToCanonicalType( PASTA_THRALLS[ i ] ); } Arrays.sort( PastaThrallData.THRALL_ARRAY ); }; public static String dataToType( Object[] data ) { return data == null ? "" : (String)data[ 0 ]; } public static int dataToId( Object[] data ) { return data == null ? 0 : ((Integer)data[ 1 ]).intValue(); } public static String dataToSetting( Object[] data ) { return data == null ? "" : (String)data[ 2 ]; } public static int dataToSkillId( Object[] data ) { return data == null ? 0 : ((Integer)data[ 3 ]).intValue(); } public static Pattern dataToPattern1( Object[] data ) { return data == null ? null : (Pattern)data[ 4 ]; } public static Pattern dataToPattern2( Object[] data ) { return data == null ? null : (Pattern)data[ 5 ]; } public static String dataToImage( Object[] data ) { return data == null ? null : (String)data[ 6 ]; } public static String dataToTinyImage( Object[] data ) { return data == null ? null : (String)data[ 7 ]; } public static String dataToLevel1Ability( Object[] data ) { return data == null ? null : (String)data[ 8 ]; } public static String dataToLevel5Ability( Object[] data ) { return data == null ? null : (String)data[ 9 ]; } public static String dataToLevel10Ability( Object[] data ) { return data == null ? null : (String)data[ 10 ]; } public static String dataToCanonicalType( Object[] data ) { return data == null ? "" : (String)data[ 11 ]; } public static Object[] idToData( final int id ) { for ( int i = 0; i < PastaThrallData.PASTA_THRALLS.length; ++i ) { Object[] data = PastaThrallData.PASTA_THRALLS[ i ]; if ( PastaThrallData.dataToId( data ) == id ) { return data; } } return null; } public static final List<String> getMatchingNames( final String substring ) { return StringUtilities.getMatchingNames( PastaThrallData.CANONICAL_THRALL_ARRAY, substring ); } public static Object[] typeToData( final String type ) { // Do fuzzy matching List<String> matchingNames = PastaThrallData.getMatchingNames( type ); if ( matchingNames.size() != 1 ) { return null; } String name = matchingNames.get( 0 ); for ( int i = 0; i < PastaThrallData.PASTA_THRALLS.length; ++i ) { Object[] data = PastaThrallData.PASTA_THRALLS[ i ]; if ( name.equals( PastaThrallData.dataToCanonicalType( data ) ) ) { return data; } } return null; } public static Object[] skillIdToData( final int skillId ) { for ( int i = 0; i < PastaThrallData.PASTA_THRALLS.length; ++i ) { Object[] data = PastaThrallData.PASTA_THRALLS[ i ]; if ( PastaThrallData.dataToSkillId( data ) == skillId ) { return data; } } return null; } public static String idToType( final int id ) { return id == 0 ? "(none)" : PastaThrallData.dataToType( PastaThrallData.idToData( id ) ); } public static final PastaThrallData NO_THRALL = new PastaThrallData( 0 ); public static void initialize() { List<PastaThrallData> thralls = KoLCharacter.getPastaThrallList(); if ( !thralls.contains( PastaThrallData.NO_THRALL ) ) { thralls.add( PastaThrallData.NO_THRALL ); } for ( int i = 0; i < PastaThrallData.PASTA_THRALLS.length; ++i ) { Object[] data = PastaThrallData.PASTA_THRALLS[ i ]; PastaThrallData thrall = new PastaThrallData( data ); if ( !thralls.contains( thrall ) ) { thralls.add( thrall ); } thrall.updateFromSetting(); } Collections.sort( thralls ); } private Object[] data; private final int id; private final String type; private int level; private String name; private String mods; private String modsLookup; public PastaThrallData( final Object [] data ) { this.data = data; this.id = PastaThrallData.dataToId( data ); this.type = PastaThrallData.dataToType( data ); this.level = 0; this.name = ""; if ( this.id != 0 ) { Modifiers mods = Modifiers.getModifiers( "Thrall", this.type ); if ( mods != null ) { this.mods = mods.getString( "Modifiers" ); this.modsLookup = mods.getName(); } } } public PastaThrallData( final int id ) { this( PastaThrallData.idToData( id ) ); } public void updateFromSetting() { if ( this.data == null ) { return; } String setting = Preferences.getString( PastaThrallData.dataToSetting( this.data ) ); int comma = setting.indexOf( "," ); String levelString = comma == -1 ? setting.trim() : setting.substring( 0, comma ).trim(); String name = comma == -1 ? "" : setting.substring( comma +1 ).trim(); this.level = levelString.equals( "" ) ? 0 : StringUtilities.parseInt( levelString ); this.name = name; } private void updateSetting() { if ( this.data == null ) { return; } String settingName = PastaThrallData.dataToSetting( this.data ); if ( this.name.equals( "" ) ) { Preferences.setString( settingName, String.valueOf( this.level) ); } else { String value = String.valueOf( this.level) + "," + this.name; Preferences.setString( settingName, value ); } } public Object [] getData() { return this.data; } public int getId() { return this.id; } public String getType() { return this.type; } public int getLevel() { return this.level; } public String getName() { return this.name; } public String getLevel1Ability() { return this.data == null ? "" : PastaThrallData.dataToLevel1Ability( this.data ); } public String getLevel5Ability() { return this.data == null ? "" : PastaThrallData.dataToLevel5Ability( this.data ); } public String getLevel10Ability() { return this.data == null ? "" : PastaThrallData.dataToLevel10Ability( this.data ); } public String getCurrentModifiers() { if ( this.mods == null ) { return ""; } PastaThrallData current = KoLCharacter.currentPastaThrall(); try { KoLCharacter.setPastaThrall( this ); return Modifiers.evaluateModifiers( this.modsLookup, this.mods ); } finally { KoLCharacter.setPastaThrall( current ); } } public void update( final int level, final String name ) { boolean change = false; if ( level != 0 && level != this.level ) { this.level = level; change = true; } if ( name != null && !name.equals( this.name ) ) { this.name = new String( name ); change = true; } if ( change ) { this.updateSetting(); } } @Override public String toString() { return this.id == 0 ? "(none)" : this.type + " (lvl. " + this.getLevel() + ")"; } @Override public boolean equals( final Object o ) { return o != null && o instanceof PastaThrallData && this.id == ( (PastaThrallData) o ).id; } @Override public int hashCode() { return this.id; } public int compareTo( final PastaThrallData td ) { return this.id - td.id; } public static void handleBinding( final int skillId, final String responseText ) { Object[] data = PastaThrallData.skillIdToData( skillId ); if ( data == null ) { return; } PastaThrallData thrall = KoLCharacter.findPastaThrall( PastaThrallData.dataToId( data ) ); if ( thrall == null ) { return; } KoLCharacter.setPastaThrall( thrall ); Matcher matcher = PastaThrallData.dataToPattern1( data ).matcher( responseText ); if ( matcher.find() ) { thrall.update( 0, matcher.group( 1 ) ); return; } matcher = PastaThrallData.dataToPattern2( data ).matcher( responseText ); if ( matcher.find() ) { thrall.update( 0, matcher.group( 1 ) ); return; } } public static void handleDismissal( final String responseText ) { KoLCharacter.setPastaThrall( PastaThrallData.NO_THRALL ); } }