/** * 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.swingui; import java.awt.BorderLayout; import java.awt.CardLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; import java.util.Collections; import java.util.TreeMap; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTabbedPane; import javax.swing.JTextArea; import javax.swing.SwingConstants; import net.java.dev.spellcast.utilities.JComponentUtilities; import net.java.dev.spellcast.utilities.SortedListModel; import net.sourceforge.kolmafia.KoLConstants; import net.sourceforge.kolmafia.KoLGUIConstants; import net.sourceforge.kolmafia.KoLmafia; import net.sourceforge.kolmafia.RequestThread; import net.sourceforge.kolmafia.persistence.BuffBotDatabase; import net.sourceforge.kolmafia.persistence.SkillDatabase; import net.sourceforge.kolmafia.preferences.Preferences; import net.sourceforge.kolmafia.request.SendMailRequest; import net.sourceforge.kolmafia.session.BuffBotManager.Offering; import net.sourceforge.kolmafia.swingui.listener.ThreadedListener; import net.sourceforge.kolmafia.swingui.panel.GenericPanel; import net.sourceforge.kolmafia.swingui.widget.GenericScrollPane; import net.sourceforge.kolmafia.utilities.InputFieldUtilities; import net.sourceforge.kolmafia.utilities.StringUtilities; public class BuffRequestFrame extends GenericFrame { private static final String NO_REQUEST_TEXT = "\nTo whom it may concern:\n\n" + "At the frequent request of individuals wanting to see the name 'BOT_NAME' listed in KoLmafia's buff purchase interface, " + "BOT_NAME has been added to our internal buffbot database.\n\n" + "However, at the request of the individuals responsible for maintaining BOT_NAME, " + "BOT_NAME's formal price list and buff offerings are not available directly through KoLmafia.\n\n" + "You are welcome to use this interface to check whether or not BOT_NAME is currently logged in to KoL. " + "However, we hope this message helps you understand why additional support was not added.\n\n\n" + "Respectfully yours,\nThe KoLmafia development team"; private String botName; private final JComboBox names, types; private final SortedListModel[] nameList; private final TreeMap panelMap; private final JPanel nameContainer; private final CardLayout nameCards; private RequestPanel lastPanel; private BuffRequestPanel mainPanel; private final TotalPriceUpdater priceUpdater = new TotalPriceUpdater(); public BuffRequestFrame() { super( "Purchase Buffs" ); this.panelMap = new TreeMap(); this.nameList = new SortedListModel[ 4 ]; for ( int i = 0; i < 4; ++i ) { this.nameList[ i ] = new SortedListModel(); } this.names = new JComboBox( this.nameList[ 0 ] ); this.types = new JComboBox(); this.types.addItem( "buff packs" ); this.types.addItem( "sauceror buffs" ); this.types.addItem( "turtle tamer buffs" ); this.types.addItem( "accordion thief buffs" ); CardSwitchListener listener = new CardSwitchListener(); this.addActionListener( this.names, listener ); this.addActionListener( this.types, listener ); this.nameCards = new CardLayout(); this.nameContainer = new JPanel( this.nameCards ); this.mainPanel = new BuffRequestPanel(); this.setCenterComponent( this.mainPanel ); this.nameContainer.add( new GenericScrollPane( new JPanel() ), "" ); int lastSelectedIndex = Preferences.getInteger( "lastBuffRequestType" ); if ( lastSelectedIndex >= 0 && lastSelectedIndex < 4 ) { this.types.setSelectedIndex( lastSelectedIndex ); } this.resetCard(); } @Override public JTabbedPane getTabbedPane() { return null; } @Override public void dispose() { Preferences.setInteger( "lastBuffRequestType", this.types.getSelectedIndex() ); super.dispose(); } private void isBotOnline( final String botName ) { if ( KoLmafia.isPlayerOnline( botName ) ) { InputFieldUtilities.alert( botName + " is online." ); } else { InputFieldUtilities.alert( botName + " is probably not online." ); } } private class BuffRequestPanel extends GenericPanel { public BuffRequestPanel() { super( "request", "online?" ); Object[] list = BuffBotDatabase.getCompleteBotList(); for ( int i = 0; i < list.length; ++i ) { if ( list[ i ] == null || list[ i ].equals( "" ) ) { continue; } RequestPanel panel = new RequestPanel( (String) list[ i ] ); BuffRequestFrame.this.panelMap.put( list[ i ], panel ); BuffRequestFrame.this.nameContainer.add( panel, list[ i ] ); } VerifiableElement[] elements = new VerifiableElement[ 2 ]; elements[ 0 ] = new VerifiableElement( "Category: ", BuffRequestFrame.this.types ); elements[ 1 ] = new VerifiableElement( "Bot Name: ", BuffRequestFrame.this.names ); this.setContent( elements ); this.add( BuffRequestFrame.this.nameContainer, BorderLayout.SOUTH ); } @Override public void actionConfirmed() { RequestPanel panel = BuffRequestFrame.this.getPanel(); if ( panel == null ) { return; } JCheckBox[] checkboxes = panel.checkboxes; Offering[] offerings = panel.offerings; ArrayList requests = new ArrayList(); for ( int i = 0; i < checkboxes.length; ++i ) { if ( checkboxes[ i ] != null && checkboxes[ i ].isSelected() ) { checkboxes[ i ].setSelected( false ); requests.add( offerings[ i ].toRequest() ); } } if ( requests.isEmpty() ) { return; } for ( int i = 0; i < requests.size(); ++i ) { KoLmafia.updateDisplay( "Submitting buff request " + ( i + 1 ) + " of " + requests.size() + " to " + BuffRequestFrame.this.botName + "..." ); RequestThread.postRequest( (SendMailRequest) requests.get( i ) ); } KoLmafia.updateDisplay( "Buff requests complete." ); } @Override public boolean shouldAddStatusLabel() { return true; } @Override public void actionCancelled() { BuffRequestFrame.this.isBotOnline( BuffRequestFrame.this.botName ); } } private class RequestPanel extends JPanel { private int lastBuffId = 0; private boolean addedBuffPackLabel = false; private final CardLayout categoryCards = new CardLayout(); private final JPanel[] categoryPanels = new JPanel[ 4 ]; private final JCheckBox[] checkboxes; private final Offering[] offerings; public RequestPanel( final String botName ) { this.setLayout( this.categoryCards ); if ( BuffBotDatabase.getStandardOfferings( botName ).isEmpty() ) { this.checkboxes = null; this.offerings = null; this.addNoRequestMessage( botName ); return; } for ( int i = 0; i < 4; ++i ) { this.categoryPanels[ i ] = new JPanel(); this.categoryPanels[ i ].setLayout( new BoxLayout( this.categoryPanels[ i ], BoxLayout.Y_AXIS ) ); GenericScrollPane scroller = new GenericScrollPane( this.categoryPanels[ i ] ); JComponentUtilities.setComponentSize( scroller, 500, 400 ); this.add( scroller, String.valueOf( i ) ); } ArrayList list = new ArrayList(); list.addAll( BuffBotDatabase.getStandardOfferings( botName ) ); list.addAll( BuffBotDatabase.getPhilanthropicOfferings( botName ) ); Collections.sort( list ); this.offerings = new Offering[ list.size() ]; list.toArray( this.offerings ); this.checkboxes = new JCheckBox[ this.offerings.length ]; for ( int i = 0; i < this.checkboxes.length; ++i ) { if ( this.offerings[ i ].getLowestBuffId() < 1000 ) { continue; } this.checkboxes[ i ] = new JCheckBox( this.offerings[ i ].toString() ); this.checkboxes[ i ].setVerticalTextPosition( SwingConstants.TOP ); BuffRequestFrame.this.addActionListener( this.checkboxes[ i ], BuffRequestFrame.this.priceUpdater ); int price = this.offerings[ i ].getPrice(); int[] turns = this.offerings[ i ].getTurns(); String tooltip = price + " meat (" + KoLConstants.FLOAT_FORMAT.format( (float) turns[ 0 ] / (float) price ) + " turns/meat)"; this.checkboxes[ i ].setToolTipText( tooltip ); int buffId = this.offerings[ i ].getLowestBuffId(); int categoryId = this.getCategory( turns.length, buffId ); this.addBuffLabel( turns.length, buffId, categoryId ); if ( !BuffRequestFrame.this.nameList[ categoryId ].contains( botName ) ) { BuffRequestFrame.this.nameList[ categoryId ].add( botName ); } this.categoryPanels[ categoryId ].add( this.checkboxes[ i ] ); } } private int getCategory( final int count, final int buffId ) { if ( count > 1 ) { return 0; } else if ( SkillDatabase.isSaucerorBuff( buffId ) ) { return 1; } else if ( SkillDatabase.isTurtleTamerBuff( buffId ) ) { return 2; } else if ( SkillDatabase.isAccordionThiefSong( buffId ) ) { return 3; } else { return 0; } } private void addNoRequestMessage( final String botName ) { for ( int i = 0; i < 4; ++i ) { JTextArea message = new JTextArea( StringUtilities.globalStringReplace( BuffRequestFrame.NO_REQUEST_TEXT, "BOT_NAME", botName ) ); message.setColumns( 40 ); message.setLineWrap( true ); message.setWrapStyleWord( true ); message.setEditable( false ); message.setOpaque( false ); message.setFont( KoLGUIConstants.DEFAULT_FONT ); this.add( new GenericScrollPane( message ), String.valueOf( i ) ); } } private void addBuffLabel( final int count, final int buffId, final int categoryId ) { if ( count > 1 ) { if ( this.addedBuffPackLabel ) { return; } this.addedBuffPackLabel = true; this.categoryPanels[ categoryId ].add( new JLabel( "<html><h3>Buff Packs</h3></html>" ) ); this.categoryPanels[ categoryId ].add( Box.createVerticalStrut( 5 ) ); return; } if ( buffId == this.lastBuffId ) { return; } this.lastBuffId = buffId; this.categoryPanels[ categoryId ].add( new JLabel( "<html><h3>" + SkillDatabase.getSkillName( buffId ) + "</h3></html>" ) ); this.categoryPanels[ categoryId ].add( Box.createVerticalStrut( 5 ) ); } } private void updateSendPrice() { if ( this.mainPanel == null ) { return; } RequestPanel panel = this.getPanel(); if ( panel == null || panel.checkboxes == null || panel.offerings == null ) { return; } if ( this.lastPanel != null && this.lastPanel != panel ) { JCheckBox[] checkboxes = this.lastPanel.checkboxes; for ( int i = 0; i < checkboxes.length; ++i ) { if ( checkboxes[ i ] != null ) { checkboxes[ i ].setSelected( false ); } } } this.lastPanel = panel; int price = 0; JCheckBox[] checkboxes = panel.checkboxes; Offering[] offerings = panel.offerings; for ( int i = 0; i < checkboxes.length; ++i ) { if ( checkboxes[ i ] == null || offerings[ i ] == null ) { continue; } if ( checkboxes[ i ].isSelected() ) { price += offerings[ i ].getPrice(); } } this.mainPanel.setStatusMessage( KoLConstants.COMMA_FORMAT.format( price ) + " meat will be sent to " + this.botName ); } private String getCardId() { this.botName = (String) this.names.getSelectedItem(); return this.botName; } private void resetCard() { int typeId = this.types.getSelectedIndex(); if ( typeId != -1 && this.names.getModel() != this.nameList[ typeId ] ) { this.names.setModel( this.nameList[ typeId ] ); } RequestPanel panel = this.getPanel(); if ( typeId == -1 || panel == null ) { this.nameCards.show( this.nameContainer, "" ); this.mainPanel.setStatusMessage( " " ); return; } panel.categoryCards.show( panel, String.valueOf( typeId ) ); this.nameCards.show( this.nameContainer, this.getCardId() ); this.updateSendPrice(); } private RequestPanel getPanel() { String cardId = this.getCardId(); if ( cardId == null ) { return null; } return (RequestPanel) this.panelMap.get( cardId ); } private class CardSwitchListener extends ThreadedListener { @Override protected void execute() { BuffRequestFrame.this.resetCard(); } } private class TotalPriceUpdater implements ActionListener { public void actionPerformed( final ActionEvent e ) { BuffRequestFrame.this.updateSendPrice(); } } }