001         package com.croftsoft.apps.tile;
002         
003         import java.applet.*;
004         import java.awt.*;
005         import java.awt.image.BufferStrategy;
006         import java.awt.event.*;
007         import java.awt.geom.*;
008         import java.awt.image.BufferedImage;
009         import java.io.*;
010         import java.net.URL;
011         import java.util.*;
012         import javax.imageio.ImageIO;
013         import javax.swing.*;
014         import javax.swing.event.*;
015    
016         import com.croftsoft.core.CroftSoftConstants;
017         import com.croftsoft.core.animation.*;
018         import com.croftsoft.core.animation.animator.*;
019         import com.croftsoft.core.animation.icon.ColorTileIcon;
020         import com.croftsoft.core.animation.sprite.*;
021         import com.croftsoft.core.animation.painter.*;
022         import com.croftsoft.core.animation.updater.*;
023         import com.croftsoft.core.applet.AppletLib;
024         import com.croftsoft.core.awt.image.ImageLib;
025         import com.croftsoft.core.io.SerializableLib;
026         import com.croftsoft.core.jnlp.JnlpLib;
027         import com.croftsoft.core.lang.StringLib;
028         import com.croftsoft.core.lang.lifecycle.Lifecycle;
029         import com.croftsoft.core.math.MathConstants;
030    
031         /*********************************************************************
032         * Plane of modifiable tiles.
033         *
034         * @version
035         *   2003-08-11
036         * @since
037         *   2003-03-08
038         * @author
039         *   <a href="https://www.croftsoft.com/">David Wallace Croft</a>
040         *********************************************************************/
041    
042         public final class  Tile
043           extends AnimatedApplet
044         //////////////////////////////////////////////////////////////////////
045         //////////////////////////////////////////////////////////////////////
046         {
047    
048         //////////////////////////////////////////////////////////////////////
049         // Applet constants
050         //////////////////////////////////////////////////////////////////////
051    
052         private static final String  VERSION
053           = "2003-08-11";
054    
055         private static final String  TITLE
056           = "CroftSoft Tile";
057    
058         private static final String  APPLET_INFO
059           = "\n" + TITLE + "\n"
060           + CroftSoftConstants.COPYRIGHT + "\n"
061           + CroftSoftConstants.HOME_PAGE + "\n"
062           + "Version " + VERSION + "\n"
063           + "Programming.....:  David Wallace Croft\n"
064           + "Tile Graphics...:  Shannon Kristine Croft\n";
065    
066         //////////////////////////////////////////////////////////////////////
067         // Frame constants
068         //////////////////////////////////////////////////////////////////////
069    
070         private static final String     FRAME_TITLE = TITLE;
071    
072         private static final String     FRAME_ICON_FILENAME
073           = "/images/croftsoft.png";
074           
075         private static final Dimension  FRAME_SIZE = null;
076    
077         private static final String     SHUTDOWN_CONFIRMATION_PROMPT
078           = "Close " + TITLE + "?";
079    
080         //////////////////////////////////////////////////////////////////////
081         // animation constants
082         //////////////////////////////////////////////////////////////////////
083    
084         private static final Color   BACKGROUND_COLOR = Color.BLACK;
085    
086         private static final Cursor  CURSOR
087           = new Cursor ( Cursor.CROSSHAIR_CURSOR );
088    
089         //////////////////////////////////////////////////////////////////////
090         // persistence constants
091         //////////////////////////////////////////////////////////////////////
092    
093         private static final String  LATEST_FILENAME
094           = ".croftsoft/tile/tile_new.dat";
095    
096         private static final String  BACKUP_FILENAME
097           = ".croftsoft/tile/tile_old.dat";
098    
099         private static final String  FILE_CONTENTS_SPEC
100           = "Tile";
101    
102         private static final String  PERSISTENCE_KEY
103           = FILE_CONTENTS_SPEC;
104    
105         private static final String  RESOURCE_PATH_FILENAME
106           = "apps/tile/tile.dat";
107    
108         //////////////////////////////////////////////////////////////////////
109         // graphics constants
110         //////////////////////////////////////////////////////////////////////
111    
112         private static final long       RANDOM_SEED = 0L;
113    
114         private static final String     TILE_MAP_IMAGE_FILENAME
115           = "apps/tile/tile_map.png";
116    
117         private static final String     TILE_DIR = "apps/tile/tiles/";
118    
119         private static final String     TILE_FILENAME_EXTENSION = ".png";
120    
121         /** Default width and height of tile data. */
122         private static final int        DEFAULT_MAP_SIZE = 100;
123    
124         private static final Dimension  DEFAULT_TILE_SIZE
125           = new Dimension ( 40, 40 );
126    
127         private static final int [ ]    DEFAULT_PALETTE = new int [ ] {
128           0xFF000000,   // black
129           0xFF0000FF,   // blue
130           0xFF00FF00,   // green
131           0xFF00FFFF,   // cyan
132           0xFFFF0000,   // red
133           0xFFFF00FF,   // magenta
134           0xFFFFFF00,   // yellow
135           0xFFFFFFFF }; // white
136    
137         private static final int        DEFAULT_PALETTE_INDEX = 0; // black
138    
139         private static final int        SMOOTHING_LOOPS = 1000;
140    
141         //////////////////////////////////////////////////////////////////////
142         // instance variables
143         //////////////////////////////////////////////////////////////////////
144    
145         private int [ ]            palette;
146    
147         private byte [ ] [ ]       tileMap;
148    
149         private EdgeScrollUpdater  edgeScrollUpdater;
150    
151         private Rectangle          componentBounds;
152    
153         private TilePainter        tilePainter;
154    
155         private Ellipse2D.Double   tileEllipse2D;
156    
157         private Point              mousePoint;
158    
159         private boolean            dataIsDirty;
160    
161         private Rectangle          clipBounds;
162    
163         //////////////////////////////////////////////////////////////////////
164         //////////////////////////////////////////////////////////////////////
165    
166         public static void  main ( String [ ]  args )
167         //////////////////////////////////////////////////////////////////////
168         {
169           launch ( new Tile ( ) );
170         }
171    
172         public static AnimationInit  createAnimationInit ( )
173         //////////////////////////////////////////////////////////////////////
174         {
175           AnimationInit  animationInit
176             = new AnimationInit ( );
177    
178           animationInit.setAppletInfo ( APPLET_INFO );
179    
180           animationInit.setBackgroundColor ( BACKGROUND_COLOR );
181    
182           animationInit.setCursor ( CURSOR );
183    
184           animationInit.setFrameIconFilename ( FRAME_ICON_FILENAME );
185    
186           animationInit.setFrameSize ( FRAME_SIZE );
187    
188           animationInit.setFrameTitle ( FRAME_TITLE );
189    
190           animationInit.setShutdownConfirmationPrompt (
191             SHUTDOWN_CONFIRMATION_PROMPT );
192    
193           return animationInit;
194         }
195    
196         //////////////////////////////////////////////////////////////////////
197         //////////////////////////////////////////////////////////////////////
198    
199         public  Tile ( )
200         //////////////////////////////////////////////////////////////////////
201         {
202           super ( createAnimationInit ( ) );
203         }
204    
205         //////////////////////////////////////////////////////////////////////
206         //////////////////////////////////////////////////////////////////////
207    
208         public void  init ( )
209         //////////////////////////////////////////////////////////////////////
210         {
211           super.init ( );
212    
213           TileData  tileData = null;
214    
215           try
216           {
217             tileData = ( TileData ) SerializableLib.load (
218               LATEST_FILENAME,
219               BACKUP_FILENAME,
220               FILE_CONTENTS_SPEC,
221               ( Applet ) this,
222               PERSISTENCE_KEY,
223               getClass ( ).getClassLoader ( ),
224               RESOURCE_PATH_FILENAME );
225           }
226           catch ( Exception  ex )
227           {
228             ex.printStackTrace ( );
229           }
230    
231           if ( tileData == null )
232           {
233             try
234             {
235               tileData = TileData.loadTileDataFromImage (
236                 TILE_MAP_IMAGE_FILENAME,
237                 getClass ( ).getClassLoader ( ) );
238             }
239             catch ( IOException  ex )
240             {
241               ex.printStackTrace ( );
242             }
243           }
244    
245           if ( tileData != null )
246           {
247             palette = tileData.getPalette ( );
248    
249             tileMap = tileData.getTileMap ( );
250           }
251    
252           try
253           {
254             TileData.remapToPalette ( palette, tileMap, DEFAULT_PALETTE_INDEX );
255           }
256           catch ( IllegalArgumentException  ex )
257           {
258             palette = new int [ ] {
259               0xFF0000FF,   // blue water
260               0xFF00FF00 }; // green land
261    
262             tileMap = TileData.generateRandomTileMap (
263               new Random ( RANDOM_SEED ),
264               palette,
265               DEFAULT_MAP_SIZE, // rows
266               DEFAULT_MAP_SIZE, // columns
267               SMOOTHING_LOOPS );
268           }
269    
270           Dimension  tileSize = DEFAULT_TILE_SIZE;
271    
272           Icon [ ]  tileIcons = new Icon [ palette.length ];
273    
274           for ( int  i = 0; i < palette.length; i++ )
275           {
276             int  argb = palette [ i ];
277    
278             Image  tileImage = null;
279    
280             try
281             {
282               String  iconFilename
283                 = TILE_DIR
284                 + StringLib.padLeft ( Integer.toHexString ( argb ), '0', 8 )
285                 + TILE_FILENAME_EXTENSION;
286    
287               tileImage = ImageLib.loadAutomaticImage (
288                 iconFilename,
289                 Transparency.OPAQUE,
290                 animatedComponent,
291                 getClass ( ).getClassLoader ( ),
292                 tileSize );
293             }
294             catch ( IllegalArgumentException  ex )
295             {
296             }
297             catch ( Exception  ex )
298             {
299               ex.printStackTrace ( );
300             }
301    
302             if ( tileImage == null )
303             {
304               tileIcons [ i ]
305                 = new ColorTileIcon ( new Color ( argb ), tileSize );
306             }
307             else
308             {
309               tileIcons [ i ] = new ImageIcon ( tileImage );
310             }
311           }
312    
313           componentBounds = animatedComponent.getBounds ( );
314    
315           tileEllipse2D = new Ellipse2D.Double (
316             0, 0, componentBounds.width, componentBounds.height );
317    
318           tilePainter = new TilePainter (
319             0,
320             0,
321             tileIcons,
322             tileMap,
323             tileSize,
324             tileEllipse2D );
325    
326           edgeScrollUpdater = new EdgeScrollUpdater (
327             animatedComponent,
328             tilePainter.getTileColumns ( ) * tilePainter.getTileWidth  ( ),
329             tilePainter.getTileRows    ( ) * tilePainter.getTileHeight ( ),
330             ( Dimension ) null, // edgeSize
331             10,                 // scrollRate
332             true );             // wrapAround
333    
334           animatedComponent.addMouseListener (
335             new MouseAdapter ( )
336             {
337               public void  mousePressed ( MouseEvent  mouseEvent )
338               {
339                 mousePoint = mouseEvent.getPoint ( );
340               }
341             } );
342    
343           clipBounds = new Rectangle ( );
344         }
345    
346         public void  destroy ( )
347         //////////////////////////////////////////////////////////////////////
348         {
349           if ( dataIsDirty )
350           {
351             try
352             {
353               SerializableLib.save (
354                 new TileData ( palette, tileMap ),
355                 LATEST_FILENAME,
356                 BACKUP_FILENAME,
357                 FILE_CONTENTS_SPEC,
358                 ( Applet ) this,
359                 PERSISTENCE_KEY );
360             }
361             catch ( Exception  ex )
362             {
363               ex.printStackTrace ( );
364             }
365           }
366    
367           super.destroy ( );
368         }
369    
370         //////////////////////////////////////////////////////////////////////
371         // interface ComponentAnimator methods
372         //////////////////////////////////////////////////////////////////////
373    
374         public void  update ( JComponent  component )
375         //////////////////////////////////////////////////////////////////////
376         {
377           edgeScrollUpdater.update ( component );
378    
379           component.getBounds ( componentBounds );
380    
381           tileEllipse2D.x = -edgeScrollUpdater.getTranslateX ( );
382    
383           tileEllipse2D.y = -edgeScrollUpdater.getTranslateY ( );
384    
385           tileEllipse2D.width  = componentBounds.width;
386    
387           tileEllipse2D.height = componentBounds.height;
388    
389           if ( mousePoint != null )
390           {
391             edgeScrollUpdater.translateReverse ( mousePoint );
392    
393             if ( tileEllipse2D.contains ( mousePoint ) )
394             {
395               int  tileRow    = tilePainter.getTileRow    ( mousePoint );
396    
397               int  tileColumn = tilePainter.getTileColumn ( mousePoint );
398    
399               int  paletteIndex = 0xFF & tileMap [ tileRow ] [ tileColumn ];
400    
401               paletteIndex = ( paletteIndex + 1 ) % palette.length;
402    
403               tileMap [ tileRow ] [ tileColumn ] = ( byte ) paletteIndex;
404    
405               dataIsDirty = true;
406    
407               component.repaint ( );
408             }
409    
410             mousePoint = null;
411           }
412         }
413    
414         public void  paint (
415           JComponent  component,
416           Graphics2D  graphics )
417         //////////////////////////////////////////////////////////////////////
418         {
419           graphics.setColor ( BACKGROUND_COLOR );
420    
421           graphics.fillRect ( 0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE );
422    
423           edgeScrollUpdater.translate ( graphics );
424    
425           tilePainter.paint ( component, graphics );
426         }
427    
428         //////////////////////////////////////////////////////////////////////
429         //////////////////////////////////////////////////////////////////////
430         }