PageRenderTime 65ms CodeModel.GetById 34ms RepoModel.GetById 0ms app.codeStats 0ms

/src/com/chasekernan/hxnova/client/mapviewer/MapViewer.hx

http://hxnova.googlecode.com/
Haxe | 441 lines | 303 code | 71 blank | 67 comment | 61 complexity | 3f3c0277826dfe6e6bf1af898c2f7442 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. /**
  2. * Copyright (C) 2008 Chase Kernan
  3. * chase.kernan@gmail.com
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. *
  18. **/
  19. package com.chasekernan.hxnova.client.mapviewer;
  20. import com.chasekernan.hxnova.client.Selection;
  21. import flash.display.Sprite;
  22. import flash.filters.GlowFilter;
  23. import flash.geom.Rectangle;
  24. import com.chasekernan.hxnova.utils.IntVector;
  25. import com.chasekernan.hxnova.utils.Vector;
  26. import com.chasekernan.hxnova.core.dataholders.Global;
  27. import com.chasekernan.hxnova.core.players.Player;
  28. private typedef ScanPoint = {
  29. var location : Vector;
  30. var radius : Int;
  31. }
  32. class MapViewer extends Sprite {
  33. public static var EPSILON = 10.0;
  34. public static var EPSILON_SQUARED = EPSILON * EPSILON;
  35. public static var DEFAULT_BACKGROUND_COLOR = 0x000000;//0x111111;
  36. public static var NORMAL_SCAN_COLOR = 0x770000;//0x661c1c;
  37. public static var PENETRATING_SCAN_COLOR = 0x444400;
  38. public static var GRID_COLOR = 0xFFFFFF;
  39. public static var GRID_SPACING = 100;
  40. public static var FRIENDLY_COLOR = 0xcccc00;
  41. public static var NUETRAL_COLOR = 0x999999;
  42. public static var ENEMY_COLOR = 0xcc0000;
  43. public static var SELF_COLOR = 0x0066ff;
  44. public static var SELECTION_COLOR = 0x33ffff;
  45. public static var SELECTION_GLOW = {
  46. new GlowFilter(SELECTION_COLOR, 1, 8, 8, 2, 2);
  47. };
  48. public var stars : Array<StarObject>;
  49. private var starsHolder : Sprite;
  50. private var fleetsHolder : Sprite;
  51. public var fleets : Array<FleetObject>;
  52. public var salvage : Array<SalvageObject>;
  53. private var salvageHolder : Sprite;
  54. public var pointOfView : Player;
  55. private var background : Sprite;
  56. private var grid : Sprite;
  57. private var screenSize : IntVector;
  58. private var options : MapOptions;
  59. private var bounds : Rectangle;
  60. private var normalScans : Sprite;
  61. private var penScans : Sprite;
  62. private var penMaskSprite : Sprite;
  63. private var normMaskSprite : Sprite;
  64. private var oldTopLeft : IntVector;
  65. public function new(screenSize : IntVector, pointOfView : Player) {
  66. super();
  67. if (pointOfView == null) throw "Point of view cannot be null.";
  68. this.pointOfView = pointOfView;
  69. setScreenSize(screenSize);
  70. background = new Sprite();
  71. addChild(background);
  72. normalScans = new Sprite();
  73. addChild(normalScans);
  74. normMaskSprite = new Sprite();
  75. addChild(normMaskSprite);
  76. penScans = new Sprite();
  77. addChild(penScans);
  78. penMaskSprite = new Sprite();
  79. addChild(penMaskSprite);
  80. grid = new Sprite();
  81. addChild(grid);
  82. salvageHolder = new Sprite();
  83. addChild(salvageHolder);
  84. salvage = new Array();
  85. for (salv in Global.salvage) {
  86. var obj = new SalvageObject(salv);
  87. salvageHolder.addChild(obj);
  88. salvage.push(obj);
  89. }
  90. starsHolder = new Sprite();
  91. addChild(starsHolder);
  92. stars = new Array();
  93. for (star in Global.stars) {
  94. var obj = new StarObject(star);
  95. starsHolder.addChild(obj);
  96. stars.push(obj);
  97. }
  98. fleetsHolder = new Sprite();
  99. addChild(fleetsHolder);
  100. fleets = new Array();
  101. for (fleet in Global.fleets) {
  102. var obj = new FleetObject(fleet);
  103. fleetsHolder.addChild(obj);
  104. fleets.push(obj);
  105. }
  106. var me = this;
  107. Selection.addListener(function() {
  108. if (me.oldTopLeft == null) me.oldTopLeft = new IntVector(0, 0);
  109. me.render(me.oldTopLeft);
  110. });
  111. }
  112. public function setScreenSize(to : IntVector) : IntVector {
  113. if (to == null) throw "Screen size cannot be null.";
  114. screenSize = to.clone();
  115. return screenSize;
  116. }
  117. public function getScreenSize() : IntVector {
  118. return screenSize;
  119. }
  120. /**
  121. Renders the map with the top left point being a _game_ point.
  122. In order for the options to be omitted, [render] needs to have been
  123. called before with the options given as it uses the previously set
  124. options.
  125. **/
  126. public function render(topLeft : IntVector, ?options : MapOptions) {
  127. if (options == null) {
  128. if (this.options == null) {
  129. throw "Map options must have been set previously in order to" +
  130. " omit them in future calls.";
  131. }
  132. options = this.options;
  133. } else {
  134. this.options = options;
  135. }
  136. oldTopLeft = topLeft.clone();
  137. bounds = new Rectangle(topLeft.x, topLeft.y,
  138. screenSize.x / options.zoom,
  139. screenSize.y / options.zoom);
  140. createBackground();
  141. if (options.drawScanners) {
  142. normalScans.visible = true;
  143. penScans.visible = true;
  144. handleScanners();
  145. } else {
  146. normalScans.visible = false;
  147. penScans.visible = false;
  148. }
  149. if (options.drawGrid) {
  150. grid.visible = true;
  151. renderGrid();
  152. } else {
  153. grid.visible = false;
  154. }
  155. renderStars();
  156. renderFleets();
  157. scaleX = options.zoom;
  158. scaleY = options.zoom;
  159. scrollRect = new Rectangle(0, 0, screenSize.x / options.zoom,
  160. screenSize.y / options.zoom);
  161. }
  162. /**
  163. Returns all of the objects within [EPSILON] units of the given point.
  164. If there are no objects at the given point, then an empty array is
  165. returned.
  166. NOTE: The point specified isn't relative to any point such as the
  167. bounds given to the MapViewer when rendering. Instead it's a physical
  168. point _affected_ by zoom.
  169. **/
  170. public function getObjectsAtPoint(point : Vector, ?zoom = 1.0)
  171. : Array<IMapObject> {
  172. var objects = new Array<IMapObject>();
  173. for (obj in getStarsAtPoint(point, zoom)) objects.push(obj);
  174. for (obj in getFleetsAtPoint(point, zoom)) objects.push(obj);
  175. for (obj in getSalvageAtPoint(point, zoom)) objects.push(obj);
  176. return objects;
  177. }
  178. public function getAllObjects() : Array<IMapObject> {
  179. var objects = new Array<IMapObject>();
  180. for (obj in stars) objects.push(obj);
  181. for (obj in fleets) objects.push(obj);
  182. for (obj in salvage) objects.push(obj);
  183. return objects;
  184. }
  185. /**
  186. Returns all of the star objects within [EPSILON] units of the given
  187. point. If there are no stars at the given point, then an empty array is
  188. returned.
  189. NOTE: The point specified isn't relative to any point such as the
  190. bounds given to the MapViewer when rendering. Instead it's a physical
  191. point _affected_ by zoom.
  192. **/
  193. public function getStarsAtPoint(point : Vector, ?zoom = 1.0)
  194. : Array<StarObject> {
  195. var starObjects = new Array<StarObject>();
  196. for (obj in stars) {
  197. if (obj.star.location.getScale(zoom).distanceSquaredTo(point) <
  198. EPSILON_SQUARED) {
  199. starObjects.push(obj);
  200. }
  201. }
  202. return starObjects;
  203. }
  204. /**
  205. Returns all of the fleet objects within [EPSILON] units of the given
  206. point. If there are no fleets at the given point, then an empty array is
  207. returned.
  208. NOTE: The point specified isn't relative to any point such as the
  209. bounds given to the MapViewer when rendering. Instead it's a physical
  210. point _affected_ by zoom.
  211. **/
  212. public function getFleetsAtPoint(point : Vector, ?zoom = 1.0)
  213. : Array<FleetObject> {
  214. var fleetObjects = new Array<FleetObject>();
  215. for (obj in fleets) {
  216. if (obj.fleet.location.getScale(zoom).distanceSquaredTo(point) <
  217. EPSILON_SQUARED) {
  218. fleetObjects.push(obj);
  219. }
  220. }
  221. return fleetObjects;
  222. }
  223. /**
  224. Returns all of the salvage objects within [EPSILON] units of the given
  225. point. If there is no salvage at the given point, then an empty array
  226. is returned.
  227. NOTE: The point specified isn't relative to any point such as the
  228. bounds given to the MapViewer when rendering. Instead it's a physical
  229. point _affected_ by zoom.
  230. **/
  231. public function getSalvageAtPoint(point : Vector, ?zoom = 1.0)
  232. : Array<SalvageObject> {
  233. var salvageObjects = new Array<SalvageObject>();
  234. for (obj in salvage) {
  235. if (obj.salvage.location.getScale(zoom).distanceSquaredTo(point) <
  236. EPSILON_SQUARED) {
  237. salvageObjects.push(obj);
  238. }
  239. }
  240. return salvageObjects;
  241. }
  242. private inline function renderSelection(obj : IMapObject) {
  243. untyped obj.filters = if (Selection.isSelected(obj)) [SELECTION_GLOW]
  244. else [];
  245. }
  246. private inline function renderStars() {
  247. for (obj in stars) {
  248. if (!bounds.contains(obj.star.location.x, obj.star.location.y)) {
  249. obj.visible = false;
  250. continue;
  251. }
  252. obj.visible = true;
  253. obj.draw(bounds, pointOfView, options);
  254. renderSelection(obj);
  255. }
  256. }
  257. private inline function renderFleets() {
  258. for (obj in fleets) {
  259. if (!bounds.contains(obj.fleet.location.x, obj.fleet.location.y)) {
  260. obj.visible = false;
  261. continue;
  262. }
  263. obj.visible = true;
  264. obj.draw(bounds, pointOfView, options);
  265. renderSelection(obj);
  266. }
  267. }
  268. private inline function createBackground() {
  269. background.graphics.clear();
  270. background.graphics.beginFill(DEFAULT_BACKGROUND_COLOR);
  271. background.graphics.drawRect(0, 0, screenSize.x / options.zoom,
  272. screenSize.y / options.zoom);
  273. background.graphics.endFill();
  274. }
  275. private inline function renderGrid() {
  276. grid.graphics.clear();
  277. grid.graphics.lineStyle(0.25, GRID_COLOR, 0.1);
  278. var fixedScreenWidth = screenSize.x / options.zoom;
  279. var fixedScreenHeight = screenSize.y / options.zoom;
  280. for (i in 1...Math.ceil(fixedScreenWidth / GRID_SPACING)) {
  281. grid.graphics.moveTo(i * GRID_SPACING, 0);
  282. grid.graphics.lineTo(i * GRID_SPACING, fixedScreenHeight);
  283. }
  284. for (j in 1...Math.ceil(fixedScreenHeight / GRID_SPACING)) {
  285. grid.graphics.moveTo(0, j * GRID_SPACING);
  286. grid.graphics.lineTo(fixedScreenWidth, j * GRID_SPACING);
  287. }
  288. }
  289. private function renderScans(points : Array<ScanPoint>, normal : Bool) {
  290. var maskSprite = if (normal) normMaskSprite else penMaskSprite;
  291. var colorSprite = if (normal) normalScans else penScans;
  292. colorSprite.graphics.clear();
  293. colorSprite.graphics.beginFill(if (normal) NORMAL_SCAN_COLOR else
  294. PENETRATING_SCAN_COLOR);
  295. colorSprite.graphics.drawRect(0, 0, screenSize.x / options.zoom,
  296. screenSize.y / options.zoom);
  297. colorSprite.graphics.endFill();
  298. maskSprite.graphics.clear();
  299. maskSprite.graphics.lineStyle(0);
  300. for (point in points) {
  301. //if (!bounds.contains(point.location.x, point.location.y)) continue;
  302. maskSprite.graphics.beginFill(0xFFFFFF);
  303. maskSprite.graphics.drawCircle(point.location.x - bounds.x,
  304. point.location.y - bounds.y,
  305. point.radius);
  306. maskSprite.graphics.endFill();
  307. }
  308. colorSprite.mask = maskSprite;
  309. maskSprite.visible = false;
  310. }
  311. private function handleScanners() {
  312. //mostly a copy from [Scanning]
  313. //TODO: In [Scanning], instead of having 2 versions of the loop,
  314. //just use a closure.
  315. var penPoints = new Array<ScanPoint>();
  316. var normalPoints = new Array<ScanPoint>();
  317. //add star points
  318. var scanner = pointOfView.starScanner;
  319. if (scanner != null) {
  320. var nRadius = scanner.range;
  321. var pRadius = scanner.penetratingRange;
  322. var func =
  323. if (scanner.isPenetrating()) {
  324. function (location : Vector) {
  325. penPoints.push( {radius : pRadius,
  326. location : location.clone()} );
  327. normalPoints.push( {radius : nRadius,
  328. location : location.clone()} );
  329. }
  330. } else {
  331. function (location : Vector) {
  332. normalPoints.push( {radius : nRadius,
  333. location : location.clone()} );
  334. }
  335. };
  336. for (star in Global.stars) {
  337. if (star == null || star.owner.id != pointOfView.id ||
  338. star.hasScanner == null || !star.hasScanner /*||
  339. !bounds.contains(star.location.x, star.location.y)*/) {
  340. continue;
  341. }
  342. func(star.location);
  343. }
  344. }
  345. //add fleet points
  346. for (fleet in Global.fleets) {
  347. if (fleet.owner.id != pointOfView.id /*||
  348. !bounds.contains(fleet.location.x,
  349. fleet.location.y)*/) {
  350. continue;
  351. }
  352. var pRadius = fleet.getPenetratingScanningRange();
  353. var nRadius = fleet.getNormalScanningRange();
  354. if (pRadius > 0) {
  355. penPoints.push( {radius : pRadius,
  356. location : fleet.location.clone()} );
  357. }
  358. if (nRadius > 0) {
  359. normalPoints.push( {radius : pRadius,
  360. location : fleet.location.clone()} );
  361. }
  362. }
  363. renderScans(normalPoints, true);
  364. renderScans(penPoints, false);
  365. }
  366. }