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 }