1 /***************************************************************************
2  *                 (C) Copyright 2003-2017 - Faiumoni e.V.                 *
3  ***************************************************************************
4  ***************************************************************************
5  *                                                                         *
6  *   This program is free software; you can redistribute it and/or modify  *
7  *   it under the terms of the GNU General Public License as published by  *
8  *   the Free Software Foundation; either version 2 of the License, or     *
9  *   (at your option) any later version.                                   *
10  *                                                                         *
11  ***************************************************************************/
12 package games.stendhal.client.sprite;
13 
14 import java.util.HashMap;
15 import java.util.Map;
16 
17 import org.apache.log4j.Logger;
18 
19 /**
20  * A tileset animation map.
21  */
22 public class TilesetAnimationMap {
23 	/**
24 	 * The default frame delay (in ms).
25 	 */
26 	static final int DEFAULT_DELAY = 500;
27 	private static final Logger LOGGER = Logger.getLogger(TilesetAnimationMap.class);
28 
29 	/**
30 	 * The map of tileset animations.
31 	 */
32 	private Map<Integer, Mapping> animations;
33 
34 	/**
35 	 * Create a tileset animation map.
36 	 */
TilesetAnimationMap()37 	public TilesetAnimationMap() {
38 		animations = new HashMap<Integer, Mapping>();
39 	}
40 
41 	//
42 	// TilesetAnimationMap
43 	//
44 
45 	/**
46 	 * Add a mapping of a tile index to animation frame indexes.
47 	 *
48 	 * <strong>NOTE: The array of frame indexes/delays passed is not copied, and
49 	 * should not be altered after this is called.</strong>
50 	 *
51 	 * @param index
52 	 *            The tile index to map.
53 	 * @param frameIndexes
54 	 *            The indexes of frame tiles.
55 	 * @param frameDelays
56 	 *            The frame delays (in ms).
57 	 */
add(final int index, final int[] frameIndexes, final int[] frameDelays)58 	void add(final int index, final int[] frameIndexes,
59 			final int[] frameDelays) {
60 		animations.put(Integer.valueOf(index), new Mapping(frameIndexes,
61 				frameDelays));
62 	}
63 
64 	/**
65 	 * Add mappings of a tile indexes to animation frame indexes. For each
66 	 * frame, a mapping will be created with the remaining indexes as it's
67 	 * frames (in order, starting with it's index).
68 	 *
69 	 * @param frameIndexes
70 	 *            The indexes of frame tiles.
71 	 * @param frameDelays
72 	 *            The frame delays (in ms).
73 	 *
74 	 * @throws IllegalArgumentException
75 	 *             If the number of indexes and delays don't match.
76 	 */
add(final int[] frameIndexes, final int[] frameDelays)77 	void add(final int[] frameIndexes, final int[] frameDelays) {
78 		if (frameIndexes.length != frameDelays.length) {
79 			throw new IllegalArgumentException(
80 					"Mismatched number of frame indexes and delays");
81 		}
82 
83 		for (int i = 0; i < frameIndexes.length; i++) {
84 			final int[] frames = new int[frameIndexes.length];
85 			final int[] delays = new int[frameIndexes.length];
86 			int tidx = i;
87 
88 			for (int fidx = 0; fidx < frameIndexes.length; fidx++) {
89 				frames[fidx] = frameIndexes[tidx];
90 				delays[fidx] = frameDelays[tidx];
91 
92 				if (++tidx >= frameIndexes.length) {
93 					tidx = 0;
94 				}
95 			}
96 
97 			add(frameIndexes[i], frames, delays);
98 		}
99 	}
100 
101 	/**
102 	 * Get the animated sprite for an indexed tile of a tileset.
103 	 *
104 	 * @param tileset
105 	 *            The tileset to load from.
106 	 * @param index
107 	 *            The index with-in the tileset.
108 	 *
109 	 * @return A sprite, or <code>null</code> if no mapped sprite.
110 	 */
getSprite(final Tileset tileset, final int index)111 	public Sprite getSprite(final Tileset tileset, final int index) {
112 		final Mapping mapping = animations.get(Integer.valueOf(index));
113 
114 		if (mapping == null) {
115 			return null;
116 		}
117 
118 		final int[] frameIndexes = mapping.getIndices();
119 		final int[] frameDelays = mapping.getDelays();
120 
121 		final Sprite[] frames = new Sprite[frameIndexes.length];
122 
123 		for (int i = 0; i < frameIndexes.length; i++) {
124 			frames[i] = tileset.getSprite(frameIndexes[i]);
125 			if (frames[i] == null) {
126 				LOGGER.error("Invalid animation mapping. Tileset does not have "
127 						+ "tile index " + frameIndexes[i] + ".");
128 				return null;
129 			}
130 		}
131 
132 		// Use the reference of the first frame as the reference for the whole
133 		// group
134 		Object ref = null;
135 		Sprite first = frames[0];
136 		if (first != null) {
137 			ref = first.getReference();
138 		}
139 		return new AnimatedSprite(frames, frameDelays, true, ref);
140 	}
141 
142 	//
143 	//
144 
145 	/**
146 	 * A frame indexes/delays mapping entry for an animated tile.
147 	 */
148 	private static class Mapping {
149 		/**
150 		 * The frame indexes.
151 		 */
152 		private int[] indices;
153 
154 		/**
155 		 * The frame delays.
156 		 */
157 		private int[] delays;
158 
159 		/**
160 		 * Create a new Mapping
161 		 *
162 		 * @param indices frame indices
163 		 * @param delays frame delays
164 		 */
Mapping(final int[] indices, final int[] delays)165 		private Mapping(final int[] indices, final int[] delays) {
166 			this.indices = indices;
167 			this.delays = delays;
168 		}
169 
170 		//
171 		// Mapping
172 		//
173 
174 		/**
175 		 * Get the delays.
176 		 *
177 		 * @return delays
178 		 */
getDelays()179 		private int[] getDelays() {
180 			return delays;
181 		}
182 
183 		/**
184 		 * Get the indices
185 		 *
186 		 * @return delays
187 		 */
getIndices()188 		private int[] getIndices() {
189 			return indices;
190 		}
191 	}
192 }
193