/** * 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 com.sun.java.forums.TableSorter; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.event.ActionListener; import java.util.ArrayList; import java.util.Vector; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTable; import net.java.dev.spellcast.utilities.JComponentUtilities; import net.java.dev.spellcast.utilities.SortedListModel; import net.sourceforge.kolmafia.AdventureResult; import net.sourceforge.kolmafia.KoLCharacter; import net.sourceforge.kolmafia.KoLConstants; import net.sourceforge.kolmafia.KoLConstants.MafiaState; import net.sourceforge.kolmafia.KoLmafia; import net.sourceforge.kolmafia.RequestThread; import net.sourceforge.kolmafia.persistence.ProfileSnapshot; import net.sourceforge.kolmafia.request.ClanBuffRequest; import net.sourceforge.kolmafia.request.ClanMembersRequest; import net.sourceforge.kolmafia.request.ClanStashRequest; import net.sourceforge.kolmafia.request.ClanWarRequest; import net.sourceforge.kolmafia.request.EquipmentRequest; import net.sourceforge.kolmafia.request.ProfileRequest; import net.sourceforge.kolmafia.session.ClanManager; import net.sourceforge.kolmafia.swingui.button.RequestButton; import net.sourceforge.kolmafia.swingui.listener.TableButtonListener; import net.sourceforge.kolmafia.swingui.listener.ThreadedListener; import net.sourceforge.kolmafia.swingui.panel.GenericPanel; import net.sourceforge.kolmafia.swingui.panel.ItemListManagePanel; import net.sourceforge.kolmafia.swingui.panel.LabeledPanel; import net.sourceforge.kolmafia.swingui.table.ButtonRenderer; import net.sourceforge.kolmafia.swingui.table.ListWrapperTableModel; import net.sourceforge.kolmafia.swingui.table.TransparentTable; import net.sourceforge.kolmafia.swingui.widget.AutoFilterComboBox; import net.sourceforge.kolmafia.swingui.widget.AutoHighlightTextField; import net.sourceforge.kolmafia.swingui.widget.GenericScrollPane; import net.sourceforge.kolmafia.swingui.widget.ListCellRendererFactory; import net.sourceforge.kolmafia.utilities.InputFieldUtilities; /** * An extension of <code>KoLFrame</code> which handles all the clan management functionality of Kingdom of Loathing. */ public class ClanManageFrame extends GenericFrame { private static final int MOVE_ALL = 2; private static final int MOVE_ALL_BUT = 3; private final JTable members; private final SnapshotPanel snapshot; private final ClanBuffPanel clanBuff; private final StoragePanel storing; private final WithdrawPanel withdrawal; private final DonationPanel donation; private final AttackPanel attacks; private final WarfarePanel warfare; private final MemberSearchPanel search; public ClanManageFrame() { super( "Clan Management" ); this.snapshot = new SnapshotPanel(); this.attacks = new AttackPanel(); this.storing = new StoragePanel(); this.clanBuff = new ClanBuffPanel(); this.donation = new DonationPanel(); this.withdrawal = new WithdrawPanel(); this.search = new MemberSearchPanel(); this.warfare = new WarfarePanel(); JPanel adminPanel = new JPanel(); adminPanel.setLayout( new BoxLayout( adminPanel, BoxLayout.Y_AXIS ) ); adminPanel.add( this.attacks ); adminPanel.add( this.snapshot ); this.addTab( "Admin", adminPanel ); JPanel spendPanel = new JPanel(); spendPanel.setLayout( new BoxLayout( spendPanel, BoxLayout.Y_AXIS ) ); spendPanel.add( this.donation ); spendPanel.add( this.clanBuff ); spendPanel.add( this.warfare ); this.addTab( "Coffers", spendPanel ); this.tabs.addTab( "Deposit", this.storing ); this.tabs.addTab( "Withdraw", this.withdrawal ); this.members = new TransparentTable( new MemberTableModel() ); this.members.setModel( new TableSorter( this.members.getModel(), this.members.getTableHeader() ) ); this.members.getTableHeader().setReorderingAllowed( false ); this.members.setRowSelectionAllowed( false ); this.members.setAutoResizeMode( JTable.AUTO_RESIZE_NEXT_COLUMN ); this.members.addMouseListener( new TableButtonListener( this.members ) ); this.members.setDefaultRenderer( JButton.class, new ButtonRenderer() ); this.members.setShowGrid( false ); this.members.setIntercellSpacing( new Dimension( 5, 5 ) ); this.members.setRowHeight( 25 ); this.members.getColumnModel().getColumn( 0 ).setMinWidth( 30 ); this.members.getColumnModel().getColumn( 0 ).setMaxWidth( 30 ); this.members.getColumnModel().getColumn( 1 ).setMinWidth( 120 ); this.members.getColumnModel().getColumn( 1 ).setMaxWidth( 120 ); this.members.getColumnModel().getColumn( 3 ).setMinWidth( 120 ); this.members.getColumnModel().getColumn( 3 ).setMaxWidth( 120 ); this.members.getColumnModel().getColumn( 4 ).setMinWidth( 45 ); this.members.getColumnModel().getColumn( 4 ).setMaxWidth( 45 ); GenericScrollPane results = new GenericScrollPane( this.members ); JComponentUtilities.setComponentSize( results, 400, 300 ); JPanel searchPanel = new JPanel( new BorderLayout() ); searchPanel.add( this.search, BorderLayout.NORTH ); JPanel resultsPanel = new JPanel( new BorderLayout() ); resultsPanel.add( this.members.getTableHeader(), BorderLayout.NORTH ); resultsPanel.add( results, BorderLayout.CENTER ); searchPanel.add( resultsPanel, BorderLayout.CENTER ); this.tabs.addTab( "Member Search", searchPanel ); this.setCenterComponent( this.tabs ); } /** * An internal class which represents the panel used for clan buffs in the <code>ClanManageFrame</code>. */ private class ClanBuffPanel extends LabeledPanel { private final boolean isBuffing; private final JComboBox buffField; private final AutoHighlightTextField countField; public ClanBuffPanel() { super( "Buy Clan Buffs", "purchase", "take break", new Dimension( 80, 20 ), new Dimension( 240, 20 ) ); this.isBuffing = false; this.buffField = new JComboBox( ClanBuffRequest.getRequestList() ); this.countField = new AutoHighlightTextField(); VerifiableElement[] elements = new VerifiableElement[ 2 ]; elements[ 0 ] = new VerifiableElement( "Clan Buff: ", this.buffField ); elements[ 1 ] = new VerifiableElement( "# of times: ", this.countField ); this.setContent( elements ); } @Override public void actionConfirmed() { KoLmafia.makeRequest( (Runnable) this.buffField.getSelectedItem(), InputFieldUtilities.getValue( this.countField ) ); } @Override public void actionCancelled() { if ( this.isBuffing ) { KoLmafia.updateDisplay( MafiaState.ERROR, "Purchase attempts cancelled." ); } } } /** * An internal class which represents the panel used for clan buffs in the <code>ClanManageFrame</code>. */ private class AttackPanel extends LabeledPanel { private final JLabel nextAttack; private final AutoFilterComboBox enemyList; public AttackPanel() { super( "Loot Another Clan", "attack", "refresh", new Dimension( 80, 20 ), new Dimension( 240, 20 ) ); this.nextAttack = new JLabel( ClanWarRequest.getNextAttack() ); this.enemyList = new AutoFilterComboBox( ClanWarRequest.getEnemyClans(), false ); VerifiableElement[] elements = new VerifiableElement[ 2 ]; elements[ 0 ] = new VerifiableElement( "Victim: ", this.enemyList ); elements[ 1 ] = new VerifiableElement( " ", this.nextAttack ); this.setContent( elements ); } @Override public void actionConfirmed() { RequestThread.postRequest( (ClanWarRequest) this.enemyList.getSelectedItem() ); } @Override public void actionCancelled() { RequestThread.postRequest( new ClanWarRequest() ); this.nextAttack.setText( ClanWarRequest.getNextAttack() ); } } private class WarfarePanel extends LabeledPanel { private final AutoHighlightTextField goodies; private final AutoHighlightTextField oatmeal, recliners; private final AutoHighlightTextField grunts, flyers, archers; public WarfarePanel() { super( "Prepare for WAR!!!", "purchase", "calculate", new Dimension( 120, 20 ), new Dimension( 200, 20 ) ); this.goodies = new AutoHighlightTextField(); this.oatmeal = new AutoHighlightTextField(); this.recliners = new AutoHighlightTextField(); this.grunts = new AutoHighlightTextField(); this.flyers = new AutoHighlightTextField(); this.archers = new AutoHighlightTextField(); VerifiableElement[] elements = new VerifiableElement[ 6 ]; elements[ 0 ] = new VerifiableElement( "Goodies: ", this.goodies ); elements[ 1 ] = new VerifiableElement( "Oatmeal: ", this.oatmeal ); elements[ 2 ] = new VerifiableElement( "Recliners: ", this.recliners ); elements[ 3 ] = new VerifiableElement( "Ground Troops: ", this.grunts ); elements[ 4 ] = new VerifiableElement( "Airborne Troops: ", this.flyers ); elements[ 5 ] = new VerifiableElement( "La-Z-Archers: ", this.archers ); this.setContent( elements ); } @Override public void actionConfirmed() { RequestThread.postRequest( new ClanWarRequest( InputFieldUtilities.getValue( this.goodies ), InputFieldUtilities.getValue( this.oatmeal ), InputFieldUtilities.getValue( this.recliners ), InputFieldUtilities.getValue( this.grunts ), InputFieldUtilities.getValue( this.flyers ), InputFieldUtilities.getValue( this.archers ) ) ); } @Override public void actionCancelled() { int totalValue = InputFieldUtilities.getValue( this.goodies ) * 1000 + InputFieldUtilities.getValue( this.oatmeal ) * 3 + InputFieldUtilities.getValue( this.recliners ) * 1500 + InputFieldUtilities.getValue( this.grunts ) * 300 + InputFieldUtilities.getValue( this.flyers ) * 500 + InputFieldUtilities.getValue( this.archers ) * 500; InputFieldUtilities.alert( "This purchase will cost " + totalValue + " meat" ); } } /** * An internal class which represents the panel used for donations to the clan coffer. */ private class DonationPanel extends LabeledPanel { private final AutoHighlightTextField amountField; public DonationPanel() { super( "Fund Your Clan", "donate meat", "loot clan", new Dimension( 80, 20 ), new Dimension( 240, 20 ) ); this.amountField = new AutoHighlightTextField(); VerifiableElement[] elements = new VerifiableElement[ 1 ]; elements[ 0 ] = new VerifiableElement( "Amount: ", this.amountField ); this.setContent( elements ); } @Override public void actionConfirmed() { RequestThread.postRequest( new ClanStashRequest( InputFieldUtilities.getValue( this.amountField ) ) ); } @Override public void actionCancelled() { InputFieldUtilities.alert( "The Hermit beat you to it. ARGH!" ); } } private class StoragePanel extends ItemListManagePanel { public StoragePanel() { super( (SortedListModel) KoLConstants.inventory ); this.setButtons( new ActionListener[] { new StorageListener(), new RequestButton( "refresh", new EquipmentRequest( EquipmentRequest.REFRESH ) ) } ); } private class StorageListener extends ThreadedListener { @Override protected void execute() { AdventureResult[] items = StoragePanel.this.getDesiredItems( "Deposit" ); if ( items == null ) { return; } RequestThread.postRequest( new ClanStashRequest( items, ClanStashRequest.ITEMS_TO_STASH ) ); } @Override public String toString() { return "add items"; } } } /** * Internal class used to handle everything related to placing items into the stash. */ private class WithdrawPanel extends ItemListManagePanel { public WithdrawPanel() { super( ClanManager.getStash() ); this.setButtons( new ActionListener[] { new WithdrawListener( ClanManageFrame.MOVE_ALL ), new WithdrawListener( ClanManageFrame.MOVE_ALL_BUT ), new RequestButton( "refresh", new ClanStashRequest() ) } ); this.getElementList().setCellRenderer( ListCellRendererFactory.getDefaultRenderer() ); } private class WithdrawListener extends ThreadedListener { private final int moveType; public WithdrawListener( final int moveType ) { this.moveType = moveType; } @Override protected void execute() { if ( !KoLCharacter.canInteract() ) { return; } AdventureResult[] items; if ( this.moveType == ClanManageFrame.MOVE_ALL_BUT ) { items = (AdventureResult[])WithdrawPanel.this.getSelectedValues(); if ( items.length == 0 ) { AdventureResult[] itemsArray = new AdventureResult[ WithdrawPanel.this.elementModel.size() ]; items = (AdventureResult[])WithdrawPanel.this.elementModel.toArray( itemsArray ); } if ( items.length == 0 ) { return; } Integer value = InputFieldUtilities.getQuantity( "Maximum number of each item allowed in the stash?", Integer.MAX_VALUE, 100 ); int quantity = ( value == null ) ? 0 : value.intValue(); if ( quantity == 0 ) { return; } for ( int i = 0; i < items.length; ++i ) { AdventureResult currentItem = items[ i ]; items[ i ] = currentItem.getInstance( Math.max( 0, currentItem.getCount() - quantity ) ); } } else { items = WithdrawPanel.this.getDesiredItems( "Take" ); } if ( items == null ) { return; } RequestThread.postRequest( new ClanStashRequest( items, ClanStashRequest.STASH_TO_ITEMS ) ); } @Override public String toString() { return this.moveType == ClanManageFrame.MOVE_ALL_BUT ? "cap stash" : "take items"; } } } private class MemberSearchPanel extends GenericPanel { private final JComboBox parameterSelect; private final JComboBox matchSelect; private final AutoHighlightTextField valueField; public MemberSearchPanel() { super( "search clan", "apply changes", new Dimension( 80, 20 ), new Dimension( 240, 20 ) ); this.parameterSelect = new JComboBox(); for ( int i = 0; i < ProfileSnapshot.FILTER_NAMES.length; ++i ) { this.parameterSelect.addItem( ProfileSnapshot.FILTER_NAMES[ i ] ); } this.matchSelect = new JComboBox(); this.matchSelect.addItem( "Less than..." ); this.matchSelect.addItem( "Equal to..." ); this.matchSelect.addItem( "Greater than..." ); this.valueField = new AutoHighlightTextField(); VerifiableElement[] elements = new VerifiableElement[ 3 ]; elements[ 0 ] = new VerifiableElement( "Parameter: ", this.parameterSelect ); elements[ 1 ] = new VerifiableElement( "Constraint: ", this.matchSelect ); elements[ 2 ] = new VerifiableElement( "Value:", this.valueField ); this.setContent( elements, true ); } @Override public void actionConfirmed() { ClanManager.applyFilter( this.matchSelect.getSelectedIndex() - 1, this.parameterSelect.getSelectedIndex(), this.valueField.getText() ); KoLmafia.updateDisplay( "Search results retrieved." ); } @Override public void actionCancelled() { if ( !InputFieldUtilities.finalizeTable( ClanManageFrame.this.members ) ) { return; } KoLmafia.updateDisplay( "Determining changes..." ); ArrayList titleChange = new ArrayList(); ArrayList newTitles = new ArrayList(); ArrayList boots = new ArrayList(); for ( int i = 0; i < ClanManageFrame.this.members.getRowCount(); ++i ) { if ( ( (Boolean) ClanManageFrame.this.members.getValueAt( i, 4 ) ).booleanValue() ) { boots.add( ClanManageFrame.this.members.getValueAt( i, 1 ) ); } titleChange.add( ClanManageFrame.this.members.getValueAt( i, 1 ) ); newTitles.add( ClanManageFrame.this.members.getValueAt( i, 2 ) ); } KoLmafia.updateDisplay( "Applying changes..." ); RequestThread.postRequest( new ClanMembersRequest( titleChange.toArray(), newTitles.toArray(), boots.toArray() ) ); KoLmafia.updateDisplay( "Changes have been applied." ); } } private class MemberTableModel extends ListWrapperTableModel { public MemberTableModel() { super( new String[] { " ", "Name", "Clan Title", "Total Karma", "Boot" }, new Class[] { JButton.class, String.class, String.class, Integer.class, Boolean.class }, new boolean[] { false, false, true, false, true }, ProfileSnapshot.getFilteredList() ); } @Override public Vector constructVector( final Object o ) { ProfileRequest p = (ProfileRequest) o; Vector value = new Vector(); JButton profileButton = new JButton( JComponentUtilities.getImage( "icon_warning_sml.gif" ) ); profileButton.addMouseListener( new ShowProfileListener( p ) ); JComponentUtilities.setComponentSize( profileButton, 20, 20 ); value.add( profileButton ); value.add( p.getPlayerName() ); value.add( p.getTitle() ); value.add( p.getKarma() ); value.add( Boolean.FALSE ); return value; } } private class ShowProfileListener extends ThreadedListener { private final ProfileRequest profile; public ShowProfileListener( final ProfileRequest profile ) { this.profile = profile; } @Override protected void execute() { ProfileFrame.showRequest(this.profile); } } private class SnapshotPanel extends LabeledPanel { private final AutoHighlightTextField mostAscensionsBoardSizeField; private final AutoHighlightTextField mainBoardSizeField; private final AutoHighlightTextField classBoardSizeField; private final AutoHighlightTextField maxAgeField; private final JCheckBox playerMoreThanOnceOption; private final JCheckBox localProfileOption; public SnapshotPanel() { super( "Clan Snapshot", "snapshot", "activity log", new Dimension( 250, 20 ), new Dimension( 50, 20 ) ); VerifiableElement[] elements = new VerifiableElement[ 7 ]; this.mostAscensionsBoardSizeField = new AutoHighlightTextField( "20" ); this.mainBoardSizeField = new AutoHighlightTextField( "10" ); this.classBoardSizeField = new AutoHighlightTextField( "5" ); this.maxAgeField = new AutoHighlightTextField( "0" ); this.playerMoreThanOnceOption = new JCheckBox(); this.localProfileOption = new JCheckBox(); elements[ 0 ] = new VerifiableElement( "Most Ascensions Board Size: ", this.mostAscensionsBoardSizeField ); elements[ 1 ] = new VerifiableElement( "Fastest Ascensions Board Size: ", this.mainBoardSizeField ); elements[ 2 ] = new VerifiableElement( "Class Breakdown Board Size: ", this.classBoardSizeField ); elements[ 3 ] = new VerifiableElement( "Maximum Ascension Age (in days): ", this.maxAgeField ); elements[ 4 ] = new VerifiableElement(); elements[ 5 ] = new VerifiableElement( "Add Internal Profile Links: ", this.localProfileOption ); elements[ 6 ] = new VerifiableElement( "Allow Multiple Appearances: ", this.playerMoreThanOnceOption ); this.setContent( elements, true ); } @Override public void actionConfirmed() { // Now that you've got everything, go ahead and // generate the snapshot. int mostAscensionsBoardSize = InputFieldUtilities.getValue( this.mostAscensionsBoardSizeField, Integer.MAX_VALUE ); int mainBoardSize = InputFieldUtilities.getValue( this.mainBoardSizeField, Integer.MAX_VALUE ); int classBoardSize = InputFieldUtilities.getValue( this.classBoardSizeField, Integer.MAX_VALUE ); int maxAge = InputFieldUtilities.getValue( this.maxAgeField, Integer.MAX_VALUE ); boolean playerMoreThanOnce = this.playerMoreThanOnceOption.isSelected(); boolean localProfileLink = this.localProfileOption.isSelected(); // Now that you've got everything, go ahead and // generate the snapshot. ClanManager.takeSnapshot( mostAscensionsBoardSize, mainBoardSize, classBoardSize, maxAge, playerMoreThanOnce, localProfileLink ); } @Override public void actionCancelled() { ClanManager.saveStashLog(); } } }