/**
* 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.session;
import java.io.BufferedReader;
import java.io.File;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
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.StaticEntity;
import net.sourceforge.kolmafia.objectpool.IntegerPool;
import net.sourceforge.kolmafia.persistence.ItemDatabase;
import net.sourceforge.kolmafia.preferences.Preferences;
import net.sourceforge.kolmafia.request.MushroomRequest;
import net.sourceforge.kolmafia.swingui.MushroomFrame;
import net.sourceforge.kolmafia.utilities.FileUtilities;
import net.sourceforge.kolmafia.utilities.LogStream;
import net.sourceforge.kolmafia.webui.RelayLoader;
public abstract class MushroomManager
{
private static final Pattern PLOT_PATTERN =
Pattern.compile( "<b>Your Mushroom Plot:</b><p><table>(<tr>.*?</tr><tr>.*></tr><tr>.*?</tr><tr>.*</tr>)</table>" );
private static final Pattern SQUARE_PATTERN = Pattern.compile( "<td>(.*?)</td>" );
private static final Pattern IMAGE_PATTERN = Pattern.compile( ".*/((.*)\\.gif)" );
// The player's mushroom plot
//
// 1 2 3 4
// 5 6 7 8
// 9 10 11 12
// 13 14 15 16
private static final String[][] actualPlot = new String[ 4 ][ 4 ];
// Empty spot
public static final int EMPTY = 0;
// Sprout
public static final int SPROUT = 1;
// First generation mushrooms
public static final int SPOOKY = 724;
public static final int KNOB = 303;
public static final int KNOLL = 723;
// Second generation mushrooms
public static final int WARM = 749;
public static final int COOL = 751;
public static final int POINTY = 753;
// Third generation mushrooms
public static final int FLAMING = 755;
public static final int FROZEN = 756;
public static final int STINKY = 757;
// Special mushrooms
public static final int GLOOMY = 1266;
private static final int[][] SPORE_DATA =
{
{ SPOOKY, 30, 1 },
{ KNOB, 40, 2 },
{ KNOLL, 50, 3 }
};
// Assocations between the mushroom Ids
// and the mushroom image.
public static final Object [][] MUSHROOMS =
{
// Sprout and emptiness
{ IntegerPool.get( EMPTY ), "dirt1.gif", "__", "__", IntegerPool.get( 0 ), "empty" },
{ IntegerPool.get( SPROUT ), "mushsprout.gif", "..", "..", IntegerPool.get( 0 ), "unknown" },
// First generation mushrooms
{ IntegerPool.get( KNOB ), "mushroom.gif", "kb", "KB", IntegerPool.get( 1 ), "knob" },
{ IntegerPool.get( KNOLL ), "bmushroom.gif", "kn", "KN", IntegerPool.get( 2 ), "knoll" },
{ IntegerPool.get( SPOOKY ), "spooshroom.gif", "sp", "SP", IntegerPool.get( 3 ), "spooky" },
// Second generation mushrooms
{ IntegerPool.get( WARM ), "flatshroom.gif", "wa", "WA", IntegerPool.get( 4 ), "warm" },
{ IntegerPool.get( COOL ), "plaidroom.gif", "co", "CO", IntegerPool.get( 5 ), "cool" },
{ IntegerPool.get( POINTY ), "tallshroom.gif", "po", "PO", IntegerPool.get( 6 ), "pointy" },
// Third generation mushrooms
{ IntegerPool.get( FLAMING ), "fireshroom.gif", "fl", "FL", IntegerPool.get( 7 ), "flaming" },
{ IntegerPool.get( FROZEN ), "iceshroom.gif", "fr", "FR", IntegerPool.get( 8 ), "frozen" },
{ IntegerPool.get( STINKY ), "stinkshroo.gif", "st", "ST", IntegerPool.get( 9 ), "stinky" },
// Special mushrooms
{ IntegerPool.get( GLOOMY ), "blackshroo.gif", "gl", "GL", IntegerPool.get( 10 ), "gloomy" },
};
public static final int [][] BREEDING =
{
// EMPTY, KNOB, KNOLL, SPOOKY, WARM, COOL, POINTY FLAMING FROZEN STINKY GLOOMY
{ EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY }, // EMPTY
{ EMPTY, KNOB, COOL, WARM, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY }, // KNOB
{ EMPTY, COOL, KNOLL, POINTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY }, // KNOLL
{ EMPTY, WARM, POINTY, SPOOKY, EMPTY, EMPTY, EMPTY, EMPTY, GLOOMY, EMPTY, EMPTY }, // SPOOKY
{ EMPTY, EMPTY, EMPTY, EMPTY, WARM, STINKY, FLAMING, EMPTY, EMPTY, EMPTY, EMPTY }, // WARM
{ EMPTY, EMPTY, EMPTY, EMPTY, STINKY, COOL, FROZEN, EMPTY, EMPTY, EMPTY, EMPTY }, // COOL
{ EMPTY, EMPTY, EMPTY, EMPTY, FLAMING, FROZEN, POINTY, EMPTY, EMPTY, EMPTY, EMPTY }, // POINTY
{ EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, FLAMING, EMPTY, EMPTY, EMPTY }, // FLAMING
{ EMPTY, EMPTY, EMPTY, GLOOMY, EMPTY, EMPTY, EMPTY, EMPTY, FROZEN, EMPTY, EMPTY }, // FROZEN
{ EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, STINKY, EMPTY }, // STINKY
{ EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY } // GLOOMY
};
/**
* Utility method which returns a two-dimensional array showing the arrangement of the plot.
*/
public static final String getMushroomManager( final boolean isDataOnly )
{
if ( !MushroomManager.initialize() )
{
return "";
}
return MushroomManager.getMushroomManager( isDataOnly, MushroomManager.actualPlot );
}
/**
* Utility method which returns a two-dimensional array showing the arrangement of the forecasted plot (ie: what the
* plot will look like tomorrow).
*/
public static final String getForecastedPlot( final boolean isDataOnly )
{
return MushroomManager.getForecastedPlot( isDataOnly, MushroomManager.actualPlot );
}
public static final String getForecastedPlot( final boolean isDataOnly, final String[][] plot )
{
// Construct the forecasted plot now.
boolean[][] changeList = new boolean[ 4 ][ 4 ];
String[][] forecastPlot = new String[ 4 ][ 4 ];
for ( int row = 0; row < 4; ++row )
{
for ( int col = 0; col < 4; ++col )
{
if ( plot[ row ][ col ].equals( "__" ) )
{
forecastPlot[ row ][ col ] = MushroomManager.getForecastSquare( row, col, plot );
changeList[ row ][ col ] = !forecastPlot[ row ][ col ].equals( "__" );
}
else if ( plot[ row ][ col ].equals( plot[ row ][ col ].toLowerCase() ) )
{
forecastPlot[ row ][ col ] = plot[ row ][ col ].toUpperCase();
changeList[ row ][ col ] = false;
}
else
{
forecastPlot[ row ][ col ] = plot[ row ][ col ];
changeList[ row ][ col ] = false;
}
}
}
// Whenever the forecasted plot doesn't match the original plot, the
// surrounding mushrooms are assumed to disappear. Also forecast the
// growth of the mushrooms.
for ( int row = 0; row < 4; ++row )
{
for ( int col = 0; col < 4; ++col )
{
if ( changeList[ row ][ col ] )
{
if ( row != 0 && forecastPlot[ row - 1 ][ col ].equals( forecastPlot[ row - 1 ][ col ].toUpperCase() ) )
{
forecastPlot[ row - 1 ][ col ] = "__";
}
if ( row != 3 && forecastPlot[ row + 1 ][ col ].equals( forecastPlot[ row + 1 ][ col ].toUpperCase() ) )
{
forecastPlot[ row + 1 ][ col ] = "__";
}
if ( col != 0 && forecastPlot[ row ][ col - 1 ].equals( forecastPlot[ row ][ col - 1 ].toUpperCase() ) )
{
forecastPlot[ row ][ col - 1 ] = "__";
}
if ( col != 3 && forecastPlot[ row ][ col + 1 ].equals( forecastPlot[ row ][ col + 1 ].toUpperCase() ) )
{
forecastPlot[ row ][ col + 1 ] = "__";
}
}
}
}
return MushroomManager.getMushroomManager( isDataOnly, forecastPlot );
}
private static final String getForecastSquare( final int row, final int col, final String[][] plot )
{
String[] touched = new String[ 4 ];
// First, determine what kinds of mushrooms
// touch the square.
touched[ 0 ] = row == 0 ? "__" : plot[ row - 1 ][ col ];
touched[ 1 ] = row == 3 ? "__" : plot[ row + 1 ][ col ];
touched[ 2 ] = col == 0 ? "__" : plot[ row ][ col - 1 ];
touched[ 3 ] = col == 3 ? "__" : plot[ row ][ col + 1 ];
// Determine how many adult mushrooms total touch
// the square.
int[] touchIndex = new int[ 4 ];
int touchCount = 0;
for ( int i = 0; i < 4; ++i )
{
if ( !touched[ i ].equals( "__" ) && !touched[ i ].equals( ".." ) )
{
for ( int j = 0; j < MushroomManager.MUSHROOMS.length; ++j )
{
if ( touched[ i ].equals( MushroomManager.MUSHROOMS[ j ][ 3 ] ) )
{
touchIndex[ touchCount ] = ( (Integer) MushroomManager.MUSHROOMS[ j ][ 4 ] ).intValue();
}
}
++touchCount;
}
}
// If exactly two adult mushrooms are touching the
// square, then return the result of the breed.
if ( touchCount == 2 && MushroomManager.BREEDING[ touchIndex[ 0 ] ][ touchIndex[ 1 ] ] != MushroomManager.EMPTY )
{
return MushroomManager.getShorthand( MushroomManager.BREEDING[ touchIndex[ 0 ] ][ touchIndex[ 1 ] ], false );
}
// Otherwise, it'll be the same as whatever is
// there right now.
return plot[ row ][ col ];
}
private static final String getShorthand( final int mushroomType, final boolean isAdult )
{
for ( int i = 0; i < MushroomManager.MUSHROOMS.length; ++i )
{
if ( mushroomType == ( (Integer) MushroomManager.MUSHROOMS[ i ][ 0 ] ).intValue() )
{
return isAdult ? (String) MushroomManager.MUSHROOMS[ i ][ 3 ] : (String) MushroomManager.MUSHROOMS[ i ][ 2 ];
}
}
return "__";
}
private static final String getMushroomManager( boolean isDataOnly, final String[][] plot )
{
// Otherwise, you need to construct the string form
// of the mushroom plot. Shorthand and hypertext are
// the only two versions at the moment.
StringBuffer plotBuffer = new StringBuffer();
if ( !isDataOnly )
{
plotBuffer.append( KoLConstants.LINE_BREAK );
}
for ( int row = 0; row < 4; ++row )
{
// In a hypertext document, you initialize the
// row in the table before you start appending
// the squares.
for ( int col = 0; col < 4; ++col )
{
// Hypertext documents need to have their cells opened before
// the cell can be printed.
if ( !isDataOnly )
{
plotBuffer.append( " " );
}
String square = plot[ row ][ col ];
// Mushroom images are used in hypertext documents, while
// shorthand notation is used in non-hypertext documents.
plotBuffer.append( square == null ? "__" : square );
// Hypertext documents need to have their cells closed before
// another cell can be printed.
if ( isDataOnly )
{
plotBuffer.append( ";" );
}
}
if ( !isDataOnly )
{
plotBuffer.append( KoLConstants.LINE_BREAK );
}
}
// Now that the appropriate string has been constructed,
// return it to the calling method.
return plotBuffer.toString();
}
/**
* Utility method which retrieves the image associated with the given mushroom type.
*/
public static final String getMushroomImage( final String mushroomType )
{
for ( int i = 1; i < MushroomManager.MUSHROOMS.length; ++i )
{
if ( mushroomType.equals( MushroomManager.MUSHROOMS[ i ][ 2 ] ) )
{
return "itemimages/mushsprout.gif";
}
if ( mushroomType.equals( MushroomManager.MUSHROOMS[ i ][ 3 ] ) )
{
return "itemimages/" + MushroomManager.MUSHROOMS[ i ][ 1 ];
}
}
return "itemimages/dirt1.gif";
}
/**
* Utility method which retrieves the mushroom which is associated with the given image.
*/
public static final int getMushroomType( final String mushroomImage )
{
for ( int i = 0; i < MushroomManager.MUSHROOMS.length; ++i )
{
if ( mushroomImage.endsWith( "/" + MushroomManager.MUSHROOMS[ i ][ 1 ] ) )
{
return ( (Integer) MushroomManager.MUSHROOMS[ i ][ 0 ] ).intValue();
}
}
return MushroomManager.EMPTY;
}
public static final int[] getSporeDataByType( final int spore )
{
for ( int i = 0; i < MushroomManager.SPORE_DATA.length; ++i )
{
int [] data = MushroomManager.SPORE_DATA[ i ];
if ( data[ 0 ] == spore )
{
return data;
}
}
return null;
}
public static final int[] getSporeDataByIndex( final int index )
{
for ( int i = 0; i < MushroomManager.SPORE_DATA.length; ++i )
{
int [] data = MushroomManager.SPORE_DATA[ i ];
if ( data[ 2 ] == index )
{
return data;
}
}
return null;
}
public static final int getSporeType( final int[] data )
{
return data[0];
}
public static final String getSporeName( final int[] data )
{
return ItemDatabase.getItemName( data[0] );
}
public static final int getSporePrice( final int[] data )
{
return data[1];
}
public static final int getSporeIndex( final int[] data )
{
return data[2];
}
/**
* One of the major functions of the mushroom plot handler, this method plants the given spore into the given
* position (or square) of the mushroom plot.
*/
public static final boolean plantMushroom( final int square, final int spore )
{
// Validate square parameter. It's possible that
// the user input the wrong spore number.
if ( square < 1 || square > 16 )
{
KoLmafia.updateDisplay( MafiaState.ERROR, "Squares are numbered from 1 to 16." );
return false;
}
// Find the spore that the user wishes to plant
int [] data = MushroomManager.getSporeDataByType( spore );
if ( data == null )
{
KoLmafia.updateDisplay( MafiaState.ERROR, "You can't plant that." );
return false;
}
// Find KoL's internal spore index and the price
int sporeIndex = MushroomManager.getSporeIndex( data );
int sporePrice = MushroomManager.getSporePrice( data );
// Make sure we have enough meat to pay for the spore.
// Rather than using requirements validation, check the
// character data.
if ( KoLCharacter.getAvailableMeat() < sporePrice )
{
KoLmafia.updateDisplay( MafiaState.ERROR, "You can't afford that spore." );
return false;
}
// Make sure we know current state of mushroom plot
// before we plant the mushroom. Bail if it fails.
if ( !MushroomManager.initialize() )
{
return false;
}
// If the square isn't empty, pick what's there
int row = ( square - 1 ) / 4;
int col = ( square - 1 ) % 4;
if ( !MushroomManager.actualPlot[ row ][ col ].equals( "__" ) && !MushroomManager.pickMushroom( square, true ) )
{
return false;
}
// Plant the requested spore.
MushroomRequest request = new MushroomRequest( square, sporeIndex );
KoLmafia.updateDisplay( "Planting " + ItemDatabase.getItemName( spore ) + " spore in square " + square + "..." );
RequestThread.postRequest( request );
// If it failed, bail.
if ( !KoLmafia.permitsContinue() )
{
return false;
}
KoLmafia.updateDisplay( "Spore successfully planted." );
return true;
}
public static final void clearField()
{
for ( int i = 1; i <= 16; ++i )
{
MushroomManager.pickMushroom( i, true );
}
}
/**
* Picks all the mushrooms in all squares. This is equivalent to harvesting your mushroom crop, hence the name.
*/
public static final void harvestMushrooms()
{
for ( int i = 1; i <= 16; ++i )
{
MushroomManager.pickMushroom( i, false );
}
}
/**
* One of the major functions of the mushroom plot handler, this method picks the mushroom located in the given
* square.
*/
public static final boolean pickMushroom( final int square, final boolean pickSpores )
{
// Validate square parameter. It's possible that
// the user input the wrong spore number.
if ( square < 1 || square > 16 )
{
KoLmafia.updateDisplay( MafiaState.ERROR, "Squares are numbered from 1 to 16." );
return false;
}
// Make sure we know current state of mushroom plot
// before we plant the mushroom. Bail if it fails.
if ( !MushroomManager.initialize() )
{
return false;
}
// If the square is not empty, run a request to pick
// the mushroom in the square.
int row = ( square - 1 ) / 4;
int col = ( square - 1 ) % 4;
boolean shouldPick = !MushroomManager.actualPlot[ row ][ col ].equals( "__" );
shouldPick &=
!MushroomManager.actualPlot[ row ][ col ].equals( MushroomManager.actualPlot[ row ][ col ].toLowerCase() ) || pickSpores;
if ( shouldPick )
{
MushroomRequest request = new MushroomRequest( square );
KoLmafia.updateDisplay( "Picking square " + square + "..." );
RequestThread.postRequest( request );
KoLmafia.updateDisplay( "Square picked." );
}
return KoLmafia.permitsContinue();
}
/**
* Utility method used to initialize the state of the plot into the one-dimensional array.
*/
public static final boolean ownsPlot()
{
return KoLCharacter.getAscensions() == Preferences.getInteger( "lastMushroomPlot" );
}
private static final boolean initialize()
{
// If you can't go inside Degrassi Knoll, no go
if ( !KoLCharacter.knollAvailable() && !KoLCharacter.inZombiecore() )
{
KoLmafia.updateDisplay( MafiaState.ERROR, "You can't find the mushroom fields." );
return false;
}
RequestThread.postRequest( new MushroomRequest() );
if ( !MushroomManager.ownsPlot() )
{
KoLmafia.updateDisplay( MafiaState.ERROR, "You haven't bought a mushroom plot yet." );
return false;
}
return true;
}
public static final void parsePlot( final String text )
{
// Pretend all of the sections on the plot are empty
// before you begin parsing the plot.
for ( int row = 0; row < 4; ++row )
{
for ( int col = 0; col < 4; ++col )
{
MushroomManager.actualPlot[ row ][ col ] = "__";
}
}
Matcher plotMatcher = MushroomManager.PLOT_PATTERN.matcher( text );
if ( !plotMatcher.find() )
{
return;
}
// Remember that we have bought a plot this ascension
Preferences.setInteger( "lastMushroomPlot", KoLCharacter.getAscensions() );
// Find all of the squares.
Matcher squareMatcher = MushroomManager.SQUARE_PATTERN.matcher( plotMatcher.group( 1 ) );
for ( int row = 0; row < 4; ++row )
{
for ( int col = 0; col < 4 && squareMatcher.find(); ++col )
{
int result = MushroomManager.parseSquare( squareMatcher.group( 1 ) );
MushroomManager.actualPlot[ row ][ col ] = MushroomManager.getShorthand( result, true );
}
}
}
private static final int parseSquare( final String text )
{
// We figure out what's there based on the image. This
// is done by checking the text in the square against
// the table of square values.
Matcher gifMatcher = MushroomManager.IMAGE_PATTERN.matcher( text );
if ( gifMatcher.find() )
{
String gif = gifMatcher.group( 1 );
for ( int i = 0; i < MushroomManager.MUSHROOMS.length; ++i )
{
if ( gif.equals( MushroomManager.MUSHROOMS[ i ][ 1 ] ) )
{
return ( (Integer) MushroomManager.MUSHROOMS[ i ][ 0 ] ).intValue();
}
}
}
return MushroomManager.EMPTY;
}
public static final int loadLayout( final String filename, final String[][] originalData,
final String[][] planningData )
{
// The easiest file to parse that is already provided is
// the text file which was generated automatically.
int dayIndex = 0;
BufferedReader reader = FileUtilities.getReader( new File( KoLConstants.PLOTS_LOCATION, filename + ".txt" ) );
try
{
String line = "";
String[][] arrayData = new String[ 4 ][ 4 ];
while ( line != null )
{
if ( dayIndex == 0 )
{
for ( int i = 0; i < 16; ++i )
{
originalData[ dayIndex ][ i ] = "__";
}
}
else if ( dayIndex < originalData.length )
{
for ( int i = 0; i < 4; ++i )
{
for ( int j = 0; j < 4; ++j )
{
arrayData[ i ][ j ] = planningData[ dayIndex - 1 ][ i * 4 + j ];
}
}
originalData[ dayIndex ] = MushroomManager.getForecastedPlot( true, arrayData ).split( ";" );
}
// Skip four lines from the mushroom plot,
// which only contain header information.
for ( int i = 0; i < 4 && line != null; ++i )
{
line = reader.readLine();
}
// Now, split the line into individual units
// based on whitespace.
if ( line != null )
{
// Get the plot that will result from the
// previous day's plantings.
for ( int i = 0; i < 4; ++i )
{
if ( ( line = reader.readLine() ) == null )
{
break;
}
line = line.trim();
String[] pieces = line.split( "\\*?\\s+" );
if ( line != null )
{
for ( int j = 4; j < 8; ++j )
{
planningData[ dayIndex ][ i * 4 + j - 4 ] = pieces[ j ].substring( 0, 2 );
}
}
}
// Now that you've wrapped up a day, eat
// an empty line and continue on with the
// next iteration.
++dayIndex;
line = reader.readLine();
}
}
for ( int i = 0; dayIndex > 0 && i < 16; ++i )
{
planningData[ dayIndex - 1 ][ i ] = originalData[ dayIndex - 1 ][ i ];
}
}
catch ( Exception e )
{
StaticEntity.printStackTrace( e );
return Math.max( dayIndex, 2 );
}
// Make sure to close the reader after you're done reading
// all the data in the file.
try
{
reader.close();
return Math.max( dayIndex, 2 );
}
catch ( Exception e )
{
StaticEntity.printStackTrace( e );
return Math.max( dayIndex, 2 );
}
}
private static final void copyMushroomImage( final String location )
{
File source = new File( KoLConstants.IMAGE_LOCATION, location );
File destination = new File( KoLConstants.PLOTS_LOCATION + "/" + location );
FileUtilities.copyFile( source, destination );
}
public static final void saveLayout( final String filename, final String[][] originalData,
final String[][] planningData )
{
File preview = new File( KoLConstants.PLOTS_LOCATION, filename + ".htm" );
PrintStream textLayout = LogStream.openStream( new File( KoLConstants.PLOTS_LOCATION, filename + ".txt" ), true );
PrintStream htmlLayout = LogStream.openStream( preview, true );
PrintStream plotScript = LogStream.openStream( new File( KoLConstants.PLOTS_LOCATION, filename + ".ash" ), true );
// The HTML file needs a little bit of header information
// to make it proper HTML.
htmlLayout.println( "<html><body>" );
// Now that we know that data files can be written okay,
// begin writing layout data.
ArrayList days = new ArrayList();
String image = null;
boolean isTodayEmpty = false;
for ( int i = 0; i < MushroomFrame.MAX_FORECAST && !isTodayEmpty; ++i )
{
textLayout.println();
textLayout.println( "Day " + ( i + 1 ) + ":" );
textLayout.println();
textLayout.println( "Pick Plant" );
htmlLayout.println( "<table border=0 cellpadding=0 cellspacing=0>" );
htmlLayout.println( "<tr><td colspan=9><b>Day " + ( i + 1 ) + ":" + "</b></td></tr>" );
htmlLayout.println( "<tr><td colspan=4>Pick</td><td width=50> </td><td colspan=4>Plant</td></tr>" );
// Compile all the commands which are needed for the
// planting script.
isTodayEmpty = true;
ArrayList commands = new ArrayList();
StringBuffer pickText = new StringBuffer();
StringBuffer pickHtml = new StringBuffer();
StringBuffer plantText = new StringBuffer();
StringBuffer plantHtml = new StringBuffer();
for ( int j = 0; j < 16; ++j )
{
if ( i == 0 )
{
commands.add( "field pick " + ( j + 1 ) );
}
// If you've reached the end of a row, then you
// will need to add line breaks.
if ( j > 0 && j % 4 == 0 )
{
textLayout.print( pickText.toString() );
textLayout.print( " " );
textLayout.println( plantText.toString() );
pickText.setLength( 0 );
plantText.setLength( 0 );
htmlLayout.println( "<tr>" );
htmlLayout.print( "\t" );
htmlLayout.println( pickHtml.toString() );
htmlLayout.print( "<td> </td>" );
htmlLayout.print( "\t" );
htmlLayout.println( plantHtml.toString() );
htmlLayout.print( "<td> </td>" );
htmlLayout.println( "</tr>" );
pickHtml.setLength( 0 );
plantHtml.setLength( 0 );
}
// If the data in the original is different from
// the planned, then script commands are needed.
// Also, the HTML and textual layouts will be a
// bit different pending on what happened.
boolean pickRequired =
!originalData[ i ][ j ].equals( "__" ) && !originalData[ i ][ j ].equals( planningData[ i ][ j ] );
if ( pickRequired )
{
pickText.append( " ***" );
pickHtml.append( "<td style=\"border: 1px dashed red\"><img src=\"" );
commands.add( "field pick " + ( j + 1 ) );
}
else
{
pickText.append( " " + originalData[ i ][ j ] + " " );
pickHtml.append( "<td><img src=\"" );
}
image = MushroomManager.getMushroomImage( originalData[ i ][ j ] );
MushroomManager.copyMushroomImage( image );
pickHtml.append( image );
pickHtml.append( "\"></td>" );
// Spore additions are a little trickier than looking
// just at the difference. Only certain spores can be
// planted, and only certain
boolean addedSpore = !originalData[ i ][ j ].equals( planningData[ i ][ j ] );
if ( addedSpore )
{
addedSpore = false;
if ( planningData[ i ][ j ].startsWith( "kb" ) )
{
commands.add( "field plant " + ( j + 1 ) + " knob" );
addedSpore = true;
}
else if ( planningData[ i ][ j ].startsWith( "kn" ) )
{
commands.add( "field plant " + ( j + 1 ) + " knoll" );
addedSpore = true;
}
else if ( planningData[ i ][ j ].startsWith( "sp" ) )
{
commands.add( "field plant " + ( j + 1 ) + " spooky" );
addedSpore = true;
}
}
// Now that you know for sure whether or not a spore
// was added or a breeding result, update the text.
plantText.append( " " + planningData[ i ][ j ] );
if ( addedSpore )
{
plantText.append( "*" );
plantHtml.append( "<td style=\"border: 1px dashed blue\"><img src=\"" );
image = MushroomManager.getMushroomImage( planningData[ i ][ j ].toUpperCase() );
MushroomManager.copyMushroomImage( image );
plantHtml.append( image );
plantHtml.append( "\"></td>" );
}
else
{
plantText.append( " " );
plantHtml.append( "<td><img src=\"" );
image = MushroomManager.getMushroomImage( planningData[ i ][ j ] );
MushroomManager.copyMushroomImage( image );
plantHtml.append( image );
plantHtml.append( "\"></td>" );
}
isTodayEmpty &= planningData[ i ][ j ].equals( "__" );
}
// Print the data for the last row.
textLayout.print( pickText.toString() );
textLayout.print( " " );
textLayout.println( plantText.toString() );
pickText.setLength( 0 );
plantText.setLength( 0 );
htmlLayout.println( "<tr>" );
htmlLayout.print( "\t" );
htmlLayout.println( pickHtml.toString() );
htmlLayout.print( "<td> </td>" );
htmlLayout.print( "\t" );
htmlLayout.println( plantHtml.toString() );
htmlLayout.print( "<td> </td>" );
htmlLayout.println( "</tr>" );
pickHtml.setLength( 0 );
plantHtml.setLength( 0 );
// Print any needed trailing whitespace into the layouts
// and add the list of commands to be processed later.
textLayout.println();
htmlLayout.println( "</table><br /><br />" );
if ( !isTodayEmpty )
{
days.add( commands );
}
}
// All data has been printed. Add the closing tags to the
// HTML version and then close the streams.
try
{
textLayout.close();
htmlLayout.println( "</body></html>" );
htmlLayout.close();
}
catch ( Exception e )
{
StaticEntity.printStackTrace( e );
return;
}
// Now that all of the commands have been compiled, generate
// the ASH script which will do the layout.
try
{
plotScript.println( "void main()" );
plotScript.println( "{" );
plotScript.println();
plotScript.println( " if ( !have_mushroom_plot() )" );
plotScript.println( " {" );
plotScript.println( " print( \"You do not have a mushroom plot.\" );" );
plotScript.println( " return;" );
plotScript.println( " }" );
plotScript.println();
plotScript.println( " if ( get_property( \"plantingScript\" ) != \"" + filename + "\" )" );
plotScript.println( " {" );
plotScript.println( " set_property( \"plantingDay\", -1 );" );
plotScript.println( " set_property( \"plantingDate\", -1 );" );
plotScript.println( " set_property( \"plantingLength\", " + days.size() + " );" );
plotScript.println( " set_property( \"plantingScript\", \"" + filename + "\" );" );
plotScript.println( " }" );
plotScript.println();
plotScript.println( " if ( get_property( \"plantingDate\" ).string_to_int() == moon_phase() )" );
plotScript.println( " return;" );
plotScript.println();
plotScript.println( " set_property( \"plantingDate\", moon_phase() );" );
plotScript.println( " int index = (get_property( \"plantingDay\" ).string_to_int() + 1) % " + days.size() + ";" );
plotScript.println( " set_property( \"plantingDay\", index );" );
for ( int i = 0; i < days.size(); ++i )
{
ArrayList commands = (ArrayList) days.get( i );
if ( !commands.isEmpty() )
{
plotScript.println();
plotScript.println( " if ( index == " + i + " )" );
plotScript.print( " cli_execute( \"" );
for ( int j = 0; j < commands.size(); ++j )
{
if ( j != 0 )
{
plotScript.print( ";" );
}
plotScript.print( commands.get( j ) );
}
plotScript.println( "\" );" );
}
}
plotScript.println();
plotScript.println( "}" );
plotScript.close();
}
catch ( Exception e )
{
StaticEntity.printStackTrace( e );
return;
}
// Now that everything has been generated, open the HTML
// inside of a browser.
RelayLoader.openSystemBrowser( preview );
}
}