PageRenderTime 48ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/src/worm/features/LayersFeature.java

https://github.com/kmeaw/ultraworm
Java | 454 lines | 313 code | 48 blank | 93 comment | 96 complexity | 46ac40a65f4bfd53b881b1855216e0c8 MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.0
  1. /*
  2. * Copyright (c) 2003-onwards Shaven Puppy Ltd
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are
  7. * met:
  8. *
  9. * * Redistributions of source code must retain the above copyright
  10. * notice, this list of conditions and the following disclaimer.
  11. *
  12. * * Redistributions in binary form must reproduce the above copyright
  13. * notice, this list of conditions and the following disclaimer in the
  14. * documentation and/or other materials provided with the distribution.
  15. *
  16. * * Neither the name of 'Shaven Puppy' nor the names of its contributors
  17. * may be used to endorse or promote products derived from this software
  18. * without specific prior written permission.
  19. *
  20. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
  22. * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  23. * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  24. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  25. * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  26. * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  27. * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  28. * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  29. * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  30. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31. */
  32. package worm.features;
  33. import java.util.List;
  34. import net.puppygames.applet.Screen;
  35. import net.puppygames.applet.effects.Emitter;
  36. import net.puppygames.applet.effects.EmitterFeature;
  37. import org.lwjgl.util.*;
  38. import org.w3c.dom.Element;
  39. import worm.*;
  40. import worm.animation.ThingWithLayers;
  41. import com.shavenpuppy.jglib.Point2f;
  42. import com.shavenpuppy.jglib.resources.Feature;
  43. import com.shavenpuppy.jglib.resources.MappedColor;
  44. import com.shavenpuppy.jglib.sprites.*;
  45. import com.shavenpuppy.jglib.util.FPMath;
  46. import com.shavenpuppy.jglib.util.XMLUtil;
  47. /**
  48. * Describes layers of animation
  49. */
  50. public class LayersFeature extends Feature {
  51. /**
  52. * The name of shadows colours
  53. */
  54. public static final String SHADOW_COLOR_NAME = "shadow".intern();
  55. private static final long serialVersionUID = 1L;
  56. private static final Color TEMP = new Color();
  57. /*
  58. * Resource data
  59. */
  60. /** The various layers */
  61. private Layer[] sprite;
  62. private EmitterLayer[] emitter;
  63. private float scale = 1.0f;
  64. private Point offset;
  65. private float ySortOffset;
  66. /*
  67. * Transient data
  68. */
  69. /** Whether we have any "colored" sprites */
  70. private transient boolean hasColored;
  71. /** Whether any of the colored sprites are attenuated */
  72. private transient boolean hasAttenuated;
  73. /** Layers */
  74. private static class Layer extends Feature {
  75. private String id;
  76. private int layer;
  77. private int subLayer;
  78. private Point2f offset;
  79. private float ySortOffset;
  80. private String animation;
  81. private String image;
  82. private boolean doChildOffset;
  83. // Either we use colored, to color the whole sprite; or we use topColored and bottomColored, to color the top and bottom of the sprite
  84. private MappedColor colored;
  85. private MappedColor topColored, bottomColored;
  86. /** If true, then we'll attenuate the colors with the level colors attenuation colour according to approx. distance from map centre */
  87. private boolean attenuated;
  88. private transient Animation animationResource;
  89. private transient SpriteImage imageResource;
  90. public Layer() {
  91. setAutoCreated();
  92. setSubResource(true);
  93. }
  94. }
  95. /** Emitters */
  96. private static class EmitterLayer extends Feature {
  97. private String emitter;
  98. private Point2f offset;
  99. private float ySortOffset;
  100. private boolean doChildOffset;
  101. private transient EmitterFeature emitterFeature;
  102. public EmitterLayer() {
  103. setAutoCreated();
  104. setSubResource(true);
  105. }
  106. }
  107. /**
  108. * C'tor
  109. */
  110. public LayersFeature() {
  111. setAutoCreated();
  112. }
  113. /**
  114. * C'tor
  115. * @param name
  116. */
  117. public LayersFeature(String name) {
  118. super(name);
  119. setAutoCreated();
  120. }
  121. @Override
  122. public void load(Element element, Loader loader) throws Exception {
  123. super.load(element, loader);
  124. List<Element> layerElements = XMLUtil.getChildren(element, "sprite");
  125. if (layerElements.size() > 0) {
  126. sprite = new Layer[layerElements.size()];
  127. int count = 0;
  128. for (Element child : layerElements) {
  129. sprite[count] = new Layer();
  130. sprite[count].load(child, loader);
  131. count ++;
  132. }
  133. }
  134. List<Element> emitterLayerElements = XMLUtil.getChildren(element, "emitter");
  135. if (emitterLayerElements.size() > 0) {
  136. emitter = new EmitterLayer[emitterLayerElements.size()];
  137. int emitterCount = 0;
  138. for (Element child : emitterLayerElements) {
  139. emitter[emitterCount] = new EmitterLayer();
  140. emitter[emitterCount].load(child, loader);
  141. emitterCount ++;
  142. }
  143. }
  144. }
  145. @Override
  146. protected void doCreate() {
  147. super.doCreate();
  148. if (sprite != null) {
  149. for (Layer element : sprite) {
  150. element.create();
  151. if (element.colored != null || element.topColored != null && element.bottomColored != null) {
  152. hasColored = true;
  153. if (element.attenuated) {
  154. hasAttenuated = true;
  155. }
  156. }
  157. }
  158. }
  159. if (emitter != null) {
  160. for (EmitterLayer element : emitter) {
  161. element.create();
  162. }
  163. }
  164. }
  165. @Override
  166. protected void doDestroy() {
  167. super.doDestroy();
  168. if (sprite != null) {
  169. for (Layer element : sprite) {
  170. element.destroy();
  171. }
  172. }
  173. if (emitter != null) {
  174. for (EmitterLayer element : emitter) {
  175. element.destroy();
  176. }
  177. }
  178. }
  179. /**
  180. * Find a layer by name
  181. * @param id Name; cannot be null
  182. * @return an index, or -1
  183. */
  184. public int getLayer(String id) {
  185. if (sprite == null) {
  186. return -1;
  187. }
  188. for (int i = 0; i < sprite.length; i ++) {
  189. if (id.equals(sprite[i].id)) {
  190. return i;
  191. }
  192. }
  193. return -1;
  194. }
  195. /**
  196. * Create the sprites for an entity
  197. * @param allocator The screen to create the sprites on
  198. * @param owner Owner of the sprites
  199. */
  200. public void createSprites(SpriteAllocator allocator, ThingWithLayers owner) {
  201. createSprites(allocator, 0.0f, 0.0f, owner);
  202. }
  203. /**
  204. * Create the sprites for an entity
  205. * @param allocator The screen to create the sprites on
  206. * @param mapX location of the entity's centre
  207. * @param mapY location of the entity's centre
  208. * @param owner Owner of the sprites
  209. */
  210. public void createSprites(SpriteAllocator allocator, float mapX, float mapY, ThingWithLayers owner) {
  211. if (sprite == null) {
  212. owner.setSprites(new Sprite[0]);
  213. return;
  214. }
  215. Sprite[] s = new Sprite[sprite.length];
  216. float ox, oy;
  217. if (offset != null) {
  218. ox = offset.getX();
  219. oy = offset.getY();
  220. } else {
  221. ox = 0.0f;
  222. oy = 0.0f;
  223. }
  224. for (int i = 0; i < s.length; i ++) {
  225. s[i] = allocator.allocateSprite(owner);
  226. }
  227. owner.setSprites(s);
  228. for (int i = 0; i < s.length; i ++) {
  229. if (sprite[i].animationResource != null) {
  230. s[i].setAnimation(sprite[i].animationResource);
  231. } else {
  232. s[i].setImage(sprite[i].imageResource);
  233. }
  234. s[i].setLayer(sprite[i].layer);
  235. s[i].setSubLayer(sprite[i].subLayer);
  236. s[i].setScale(FPMath.fpValue(scale));
  237. if (sprite[i].offset != null) {
  238. s[i].setOffset((sprite[i].offset.getX() + ox) * scale, (sprite[i].offset.getY() + oy) * scale, 0.0f);
  239. } else {
  240. s[i].setOffset(ox * scale, oy * scale, 0.0f);
  241. }
  242. s[i].setYSortOffset((ySortOffset + sprite[i].ySortOffset) * scale);
  243. if (sprite[i].doChildOffset) {
  244. s[i].setDoChildOffset(true);
  245. }
  246. }
  247. createColors(s);
  248. updateColors(s, mapX, mapY);
  249. }
  250. public void updateLocation(Sprite[] existingSprite, float x, float y) {
  251. for (Sprite element : existingSprite) {
  252. element.setLocation(x, y, 0.0f);
  253. }
  254. }
  255. /**
  256. * Create the emitters at a specific location
  257. * @param screen TODO
  258. * @param x
  259. * @param y
  260. * @return an array of emitters (may be empty but won't be null)
  261. */
  262. public Emitter[] createEmitters(Screen screen, float x, float y) {
  263. if (emitter == null) {
  264. return new Emitter[0];
  265. }
  266. Emitter[] e = new Emitter[emitter.length];
  267. for (int i = 0; i < e.length; i ++) {
  268. if (emitter[i].emitterFeature != null) {
  269. e[i] = emitter[i].emitterFeature.spawn(screen);
  270. float ox, oy;
  271. if (emitter[i].offset != null) {
  272. ox = emitter[i].offset.getX() * scale;
  273. oy = emitter[i].offset.getY() * scale;
  274. } else {
  275. ox = 0;
  276. oy = 0;
  277. }
  278. e[i].setLocation(x + ox, y + oy);
  279. e[i].setYOffset(emitter[i].ySortOffset * scale);
  280. }
  281. }
  282. return e;
  283. }
  284. /**
  285. * Update an existing array of emitters
  286. * @param existingEmitters
  287. * @param x TODO
  288. * @param y TODO
  289. */
  290. public void updateEmitters(Emitter[] existingEmitters, float x, float y) {
  291. for (int i = 0; i < existingEmitters.length; i ++) {
  292. if (existingEmitters[i] != null) {
  293. float ox, oy;
  294. if (emitter[i].offset != null) {
  295. ox = emitter[i].offset.getX() * scale;
  296. oy = emitter[i].offset.getY() * scale;
  297. } else {
  298. ox = 0;
  299. oy = 0;
  300. }
  301. existingEmitters[i].setLocation(x + ox, y + oy);
  302. }
  303. }
  304. }
  305. public void updateScale(Sprite[] existingSprites, float newScale) {
  306. for (int i = 0; i < existingSprites.length; i ++) {
  307. existingSprites[i].setScale(FPMath.fpValue(newScale * scale));
  308. if (sprite[i].offset != null) {
  309. existingSprites[i].setOffset(sprite[i].offset.getX() * newScale * scale, sprite[i].offset.getY() * newScale * scale, 0.0f);
  310. }
  311. existingSprites[i].setYSortOffset(sprite[i].ySortOffset * newScale * scale);
  312. }
  313. }
  314. public void updateColors(Sprite[] existingSprites, float mapX, float mapY) {
  315. if (!hasColored) {
  316. return;
  317. }
  318. float mapWidth, mapHeight, ratio = 0.0f;
  319. if (hasAttenuated) {
  320. mapWidth = Worm.getGameState().getMap().getWidth();
  321. mapHeight = Worm.getGameState().getMap().getHeight();
  322. ratio = ColorAttenuationConstants.dist(mapX / MapRenderer.TILE_SIZE, mapY / MapRenderer.TILE_SIZE, mapWidth, mapHeight) / ColorAttenuationConstants.getMaxDist();
  323. }
  324. updateColors(existingSprites, ratio);
  325. }
  326. public void updateColors(Sprite[] existingSprites, float ratio) {
  327. if (!hasColored) {
  328. return;
  329. }
  330. updateColors(existingSprites, ratio, ratio, ratio, ratio, 0, 0, 0, 0);
  331. }
  332. public void updateColors(Sprite[] existingSprites, float ratio00, float ratio10, float ratio11, float ratio01, int fade00, int fade10, int fade11, int fade01) {
  333. if (!hasColored) {
  334. return;
  335. }
  336. for (int i = 0; i < existingSprites.length; i ++) {
  337. if ((sprite[i].colored != null || sprite[i].topColored != null && sprite[i].bottomColored != null) && sprite[i].attenuated) {
  338. if (existingSprites[i].getColor(0) instanceof AttenuatedColor) { // animcolor command puts ordinary Colors back into sprites. Bah
  339. ((AttenuatedColor) existingSprites[i].getColor(0)).setRatio(ratio00);
  340. ((AttenuatedColor) existingSprites[i].getColor(1)).setRatio(ratio10);
  341. ((AttenuatedColor) existingSprites[i].getColor(2)).setRatio(ratio11);
  342. ((AttenuatedColor) existingSprites[i].getColor(3)).setRatio(ratio01);
  343. ((AttenuatedColor) existingSprites[i].getColor(0)).setFade(fade00);
  344. ((AttenuatedColor) existingSprites[i].getColor(1)).setFade(fade10);
  345. ((AttenuatedColor) existingSprites[i].getColor(2)).setFade(fade11);
  346. ((AttenuatedColor) existingSprites[i].getColor(3)).setFade(fade01);
  347. }
  348. }
  349. }
  350. }
  351. private void createColors(Sprite[] existingSprites) {
  352. if (!hasColored) {
  353. return;
  354. }
  355. for (int i = 0; i < existingSprites.length; i ++) {
  356. if (sprite[i].colored != null) {
  357. MappedColor c = sprite[i].colored;
  358. if (c != null) {
  359. if (sprite[i].attenuated) {
  360. if (c.getColorName() != null && c.getColorName().intern() == SHADOW_COLOR_NAME) {
  361. existingSprites[i].setColor(0, new AttenuatedColor(c, true));
  362. existingSprites[i].setColor(1, new AttenuatedColor(c, true));
  363. existingSprites[i].setColor(2, new AttenuatedColor(c, true));
  364. existingSprites[i].setColor(3, new AttenuatedColor(c, true));
  365. } else {
  366. existingSprites[i].setColor(0, new AttenuatedColor(c));
  367. existingSprites[i].setColor(1, new AttenuatedColor(c));
  368. existingSprites[i].setColor(2, new AttenuatedColor(c));
  369. existingSprites[i].setColor(3, new AttenuatedColor(c));
  370. }
  371. } else {
  372. existingSprites[i].setColors(c);
  373. }
  374. }
  375. } else if (sprite[i].topColored != null && sprite[i].bottomColored != null) {
  376. MappedColor topColor = sprite[i].topColored;
  377. MappedColor bottomColor = sprite[i].bottomColored;
  378. if (topColor != null && bottomColor != null) {
  379. if (sprite[i].attenuated) {
  380. existingSprites[i].setColor(0, new AttenuatedColor(bottomColor));
  381. existingSprites[i].setColor(1, new AttenuatedColor(bottomColor));
  382. existingSprites[i].setColor(2, new AttenuatedColor(topColor));
  383. existingSprites[i].setColor(3, new AttenuatedColor(topColor));
  384. } else {
  385. existingSprites[i].setColor(0, bottomColor);
  386. existingSprites[i].setColor(1, bottomColor);
  387. existingSprites[i].setColor(2, topColor);
  388. existingSprites[i].setColor(3, topColor);
  389. }
  390. }
  391. }
  392. }
  393. }
  394. /**
  395. * @return the scale
  396. */
  397. public float getScale() {
  398. return scale;
  399. }
  400. /**
  401. * @return the offset
  402. */
  403. public ReadablePoint getOffset() {
  404. return offset;
  405. }
  406. }