/** * 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.persistence; import java.io.BufferedReader; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.TreeMap; import net.java.dev.spellcast.utilities.LockableListModel; import net.sourceforge.kolmafia.AdventureResult; import net.sourceforge.kolmafia.CoinmasterData; import net.sourceforge.kolmafia.KoLCharacter; import net.sourceforge.kolmafia.KoLConstants; import net.sourceforge.kolmafia.KoLConstants.CraftingType; import net.sourceforge.kolmafia.StaticEntity; import net.sourceforge.kolmafia.objectpool.Concoction; import net.sourceforge.kolmafia.objectpool.ConcoctionPool; import net.sourceforge.kolmafia.objectpool.IntegerPool; import net.sourceforge.kolmafia.objectpool.ItemPool; import net.sourceforge.kolmafia.preferences.Preferences; import net.sourceforge.kolmafia.request.CoinMasterPurchaseRequest; import net.sourceforge.kolmafia.request.PurchaseRequest; import net.sourceforge.kolmafia.utilities.FileUtilities; import net.sourceforge.kolmafia.utilities.StringUtilities; public class CoinmastersDatabase { // Map from Integer( itemId ) -> CoinMasterPurchaseRequest public static final HashMap<Integer, CoinMasterPurchaseRequest> COINMASTER_ITEMS = new HashMap<Integer, CoinMasterPurchaseRequest>(); // Map from String -> LockableListModel public static final TreeMap<String,LockableListModel<AdventureResult>> items = new TreeMap(); // Map from String -> LockableListModel public static final TreeMap<String,LockableListModel<AdventureResult>> buyItems = new TreeMap(); // Map from String -> Map from Integer -> Integer public static final TreeMap<String, Map<Integer, Integer>> buyPrices = new TreeMap<String, Map<Integer, Integer>>(); // Map from String -> LockableListModel public static final TreeMap<String,LockableListModel<AdventureResult>> sellItems = new TreeMap(); // Map from String -> Map from Integer -> Integer public static final TreeMap<String, Map<Integer, Integer>> sellPrices = new TreeMap<String, Map<Integer, Integer>>(); // Map from String -> Map from Integer -> Integer public static final TreeMap<String, Map<Integer, Integer>> itemRows = new TreeMap<String, Map<Integer, Integer>>(); public static final LockableListModel<AdventureResult> getItems( final String key ) { return CoinmastersDatabase.items.get( key ); } public static final LockableListModel<AdventureResult> getBuyItems( final String key ) { return CoinmastersDatabase.buyItems.get( key ); } public static final Map<Integer, Integer> getBuyPrices( final String key ) { return CoinmastersDatabase.buyPrices.get( key ); } public static final LockableListModel<AdventureResult> getSellItems( final String key ) { return CoinmastersDatabase.sellItems.get( key ); } public static final Map<Integer, Integer> getSellPrices( final String key ) { return CoinmastersDatabase.sellPrices.get( key ); } public static final Map<Integer, Integer> getRows( final String key ) { return CoinmastersDatabase.itemRows.get( key ); } public static final Map<Integer, Integer> getOrMakeRows( final String key ) { return CoinmastersDatabase.getOrMakeMap( key, CoinmastersDatabase.itemRows ); } public static final LockableListModel<AdventureResult> getNewList() { return new LockableListModel<AdventureResult>(); } public static final Map<Integer, Integer> getNewMap() { return new TreeMap<Integer, Integer>(); } private static final LockableListModel<AdventureResult> getOrMakeList( final String key, final Map<String,LockableListModel<AdventureResult>> map ) { LockableListModel<AdventureResult> retval = map.get( key ); if ( retval == null ) { retval = CoinmastersDatabase.getNewList(); map.put( key, retval ); } return retval; } private static final Map<Integer, Integer> getOrMakeMap( final String key, final Map<String, Map<Integer, Integer>> map ) { Map<Integer, Integer> retval = (Map<Integer, Integer>) map.get( key ); if ( retval == null ) { retval = CoinmastersDatabase.getNewMap(); map.put( key, retval ); } return retval; } public static final Map<Integer, Integer> invert( final Map<Integer, Integer> map ) { Map<Integer, Integer> retval = new TreeMap<Integer, Integer>(); for ( Entry<Integer, Integer> entry : map.entrySet() ) { retval.put(entry.getValue(), entry.getKey()); } return retval; } static { BufferedReader reader = FileUtilities.getVersionedReader( "coinmasters.txt", KoLConstants.COINMASTERS_VERSION ); String[] data; while ( ( data = FileUtilities.readData( reader ) ) != null ) { if ( data.length < 4 ) { continue; } String master = data[ 0 ]; String type = data[ 1 ]; int price = StringUtilities.parseInt( data[ 2 ] ); Integer iprice = IntegerPool.get( price ); AdventureResult item = AdventureResult.parseItem( data[ 3 ], true ); Integer iitemId = IntegerPool.get( item.getItemId() ); Integer row = null; if ( data.length > 4 ) { String[] extra = data[ 4 ].split( "\\s,\\s" ); for ( String extra1 : extra ) { if ( extra1.startsWith( "ROW" ) ) { row = IntegerPool.get( StringUtilities.parseInt( data[ 4 ].substring( 3 ) ) ); Map<Integer, Integer> rowMap = CoinmastersDatabase.getOrMakeMap( master, CoinmastersDatabase.itemRows ); rowMap.put( iitemId, row ); } } } if ( type.equals( "buy" ) ) { LockableListModel<AdventureResult> list = CoinmastersDatabase.getOrMakeList( master, CoinmastersDatabase.buyItems ); list.add( item.getInstance( CoinmastersDatabase.purchaseLimit( iitemId ) ) ); Map<Integer, Integer> map = CoinmastersDatabase.getOrMakeMap( master, CoinmastersDatabase.buyPrices ); map.put( iitemId, iprice ); } else if ( type.equals( "sell" ) ) { LockableListModel<AdventureResult> list = CoinmastersDatabase.getOrMakeList( master, CoinmastersDatabase.sellItems ); list.add( item ); Map<Integer, Integer> map = CoinmastersDatabase.getOrMakeMap( master, CoinmastersDatabase.sellPrices ); map.put( iitemId, iprice ); } } try { reader.close(); } catch ( Exception e ) { // This should not happen. Therefore, print // a stack trace for debug purposes. StaticEntity.printStackTrace( e ); } } private static final int purchaseLimit( final int itemId ) { switch ( itemId ) { case ItemPool.ZEPPELIN_TICKET: case ItemPool.TALES_OF_DREAD: case ItemPool.BRASS_DREAD_FLASK: case ItemPool.SILVER_DREAD_FLASK: return 1; } return PurchaseRequest.MAX_QUANTITY; } public static final int getPrice( final int itemId, final Map prices ) { if ( itemId == -1 ) { return 0; } Integer price = (Integer) prices.get( (Integer) itemId ); return ( price == null ) ? 0 : price.intValue(); } public static final boolean availableItem( final int itemId ) { switch( itemId ) { case ItemPool.CRIMBO_CAROL_V1: return KoLCharacter.getClassType().equals( KoLCharacter.SEAL_CLUBBER ); case ItemPool.CRIMBO_CAROL_V2: return KoLCharacter.getClassType().equals( KoLCharacter.TURTLE_TAMER ); case ItemPool.CRIMBO_CAROL_V3: return KoLCharacter.getClassType().equals( KoLCharacter.PASTAMANCER ); case ItemPool.CRIMBO_CAROL_V4: return KoLCharacter.getClassType().equals( KoLCharacter.SAUCEROR ); case ItemPool.CRIMBO_CAROL_V5: return KoLCharacter.getClassType().equals( KoLCharacter.DISCO_BANDIT ); case ItemPool.CRIMBO_CAROL_V6: return KoLCharacter.getClassType().equals( KoLCharacter.ACCORDION_THIEF ); case ItemPool.BLACK_BARTS_BOOTY: return Preferences.getInteger( "pirateSwagger" ) >= Preferences.getInteger( "blackBartsBootyCost" ); case ItemPool.HOLIDAY_FUN_BOOK: return Preferences.getInteger( "holidaySwagger" ) >= Preferences.getInteger( "holidayHalsBookCost" ); case ItemPool.ANTAGONISTIC_SNOWMAN_KIT: return Preferences.getInteger( "iceSwagger" ) >= Preferences.getInteger( "antagonisticSnowmanKitCost" ); case ItemPool.MAP_TO_KOKOMO: return Preferences.getInteger( "drunkenSwagger" ) >= Preferences.getInteger( "mapToKokomoCost" ); case ItemPool.ESSENCE_OF_BEAR: return Preferences.getInteger( "bearSwagger" ) >= Preferences.getInteger( "essenceOfBearCost" ); case ItemPool.MANUAL_OF_NUMBEROLOGY: return Preferences.getInteger( "numericSwagger" ) >= Preferences.getInteger( "manualOfNumberologyCost" ); case ItemPool.ROM_OF_OPTIMALITY: return Preferences.getInteger( "optimalSwagger" ) >= Preferences.getInteger( "ROMOfOptimalityCost" ); case ItemPool.SCHOOL_OF_HARD_KNOCKS_DIPLOMA: return Preferences.getInteger( "schoolSwagger" ) >= Preferences.getInteger( "schoolOfHardKnocksDiplomaCost" ); } return true; } public static final void clearPurchaseRequests( CoinmasterData data ) { // Clear all purchase requests for a particular Coin Master Iterator it = CoinmastersDatabase.COINMASTER_ITEMS.values().iterator(); while ( it.hasNext() ) { CoinMasterPurchaseRequest request = (CoinMasterPurchaseRequest) it.next(); if ( request.getData() == data ) { it.remove(); } } } public static final void registerPurchaseRequest( final CoinmasterData data, final AdventureResult item, final AdventureResult price ) { int itemId = item.getItemId(); int quantity = item.getCount(); // Register a purchase request CoinMasterPurchaseRequest request = new CoinMasterPurchaseRequest( data, item, price ); CoinmastersDatabase.COINMASTER_ITEMS.put( IntegerPool.get( itemId ), request ); // Register this in the Concoction for the item // Special case: ten-leaf-clovers are limited if ( itemId == ItemPool.TEN_LEAF_CLOVER ) { return; } Concoction concoction = ConcoctionPool.get( itemId ); if ( concoction == null ) { return; } // If we can create it any other way, prefer that method if ( concoction.getMixingMethod() == CraftingType.NOCREATE ) { concoction.setMixingMethod( CraftingType.COINMASTER ); concoction.addIngredient( price ); } // If we can create this only via a coin master trade, save request if ( concoction.getMixingMethod() == CraftingType.COINMASTER ) { concoction.setPurchaseRequest( request ); } } public static final CoinMasterPurchaseRequest getPurchaseRequest( final int itemId ) { Integer id = IntegerPool.get( itemId ); CoinMasterPurchaseRequest request = (CoinMasterPurchaseRequest) CoinmastersDatabase.COINMASTER_ITEMS.get( id ); if ( request == null ) { return null; } request.setLimit( request.affordableCount() ); request.setCanPurchase(); return request; } public static final boolean contains( final int itemId ) { return CoinmastersDatabase.contains( itemId, true ); } public static final boolean contains( final int itemId, boolean validate ) { PurchaseRequest item = CoinmastersDatabase.getPurchaseRequest( itemId ); return item != null && ( !validate || item.canPurchase() ); } }