PageRenderTime 43ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/src/com/chasekernan/hxnova/core/stars/MapCreator.hx

http://hxnova.googlecode.com/
Haxe | 549 lines | 374 code | 103 blank | 72 comment | 59 complexity | 6b34b88e0c77e0ae2a13fd3d59bd1b37 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.core.stars;
  20. import com.chasekernan.hxnova.core.components.ComponentSet;
  21. import com.chasekernan.hxnova.core.components.Scanner;
  22. import com.chasekernan.hxnova.core.minerals.MineralSet;
  23. import com.chasekernan.hxnova.core.players.Player;
  24. import com.chasekernan.hxnova.utils.Vector;
  25. #if flash9
  26. import flash.display.BitmapData;
  27. import flash.display.Sprite;
  28. #end
  29. /**
  30. Creates maps based upon some input data.
  31. Types include rectangle, ring, bezier curve, and bitmap concentrations.
  32. **/
  33. class MapCreator {
  34. public static var DEFAULT_FACTORIES : Int = 10;
  35. public static var DEFAULT_MINES : Int = 10;
  36. public static var DEFAULT_DEFENSES : Int = 10;
  37. public static var DEFAULT_POP : Int = 50000;
  38. public static var DEFAULT_STARTING_MIN : Int = 2000;
  39. private static var NULL_PLAYER = new Player();
  40. private static function createBasicStars(starPoints : Array < Vector > ,
  41. names : Array < String > , stars : Array < Star > ) {
  42. NULL_PLAYER.id = -1;
  43. for (i in 0...starPoints.length) {
  44. var s : Star = new Star();
  45. s.location = starPoints[i];
  46. s.name = names[i];
  47. s.minerals = new MineralSet();
  48. s.concentrations = new Concentrations(false);
  49. s.environment = new Environment();
  50. s.defenses = s.mines = s.factories = 0;
  51. s.hasScanner = false;
  52. s.owner = NULL_PLAYER;
  53. s.population = 0;
  54. stars.push(s);
  55. }
  56. }
  57. /**
  58. Creates a rectangular map and returns the array of stars (which contains
  59. both hws and regular systems).
  60. **/
  61. public static function generateRectMap(dimensions : Vector,
  62. numberOfStars : Int, players : Array<Player>, names : Array<String>,
  63. ?hwMinConc : Concentrations) : Array<Star> {
  64. var stars : Array<Star> = new Array<Star>();
  65. hwMinConc = if(hwMinConc == null) new Concentrations(true)
  66. else hwMinConc;
  67. players = cast(randomizeArray(players.copy()));
  68. names = cast(randomizeArray(names));
  69. var starPoints : Array<Vector> = createRectPoints(dimensions,
  70. numberOfStars, 0.05);
  71. createBasicStars(starPoints, names, stars);
  72. var hwPoints : Array<Vector> = createRectPoints(dimensions,
  73. players.length, 0.3);
  74. for(i in 0...hwPoints.length) {
  75. //hw loc
  76. var point : Vector = hwPoints[i];
  77. //find closest star
  78. var dist : Float = 999999;
  79. var curDist : Float = 0;
  80. var starNum : Int = 0;
  81. for (j in 0...stars.length) {
  82. if (stars[j].isHomeworld != null && stars[j].isHomeworld)
  83. continue;
  84. curDist = stars[j].location.distanceTo(point);
  85. if(curDist < dist) {
  86. dist = curDist;
  87. starNum = j;
  88. }
  89. }
  90. //found the star so now change it to HW
  91. var star : Star = stars[starNum];
  92. var player : Player = players[i];
  93. convertStar(star, player, hwMinConc);
  94. }
  95. return stars;
  96. }
  97. //converts a star into a homeworld
  98. private static function convertStar(star : Star, player : Player,
  99. conc : Concentrations) {
  100. star.owner = player;
  101. star.population = DEFAULT_POP;
  102. player.homeworld = star;
  103. star.isHomeworld = true;
  104. star.factories = DEFAULT_FACTORIES;
  105. star.mines = DEFAULT_MINES;
  106. star.defenses = DEFAULT_DEFENSES;
  107. star.concentrations = conc.clone();
  108. star.minerals = new MineralSet();
  109. star.production = new ProductionQueue(star);
  110. star.hasScanner = true;
  111. for (i in Concentrations.MINERAL_TYPES)
  112. star.minerals.setValue(i, Std.int(DEFAULT_STARTING_MIN *
  113. conc.getValue(i) / 100.0));
  114. star.environment = player.race.habSetting.getPerfectEnv();
  115. }
  116. /**
  117. Creates a ring filled with stars and returns the array of stars with
  118. both hws and regular systems.
  119. **/
  120. public static function generateRingMap(outerRadius : Float,
  121. innerRadius : Float, numberOfStars : Int, players : Array<Player>,
  122. names : Array<String>, ?hwMinConc : Concentrations) : Array<Star> {
  123. var stars : Array<Star> = new Array<Star>();
  124. hwMinConc = if(hwMinConc == null) new Concentrations() else hwMinConc;
  125. players = cast(randomizeArray(players));
  126. names = cast(randomizeArray(names));
  127. var starPoints : Array<Vector> =
  128. createRingPoints(outerRadius, innerRadius, numberOfStars);
  129. createBasicStars(starPoints, names, stars);
  130. var hwPoints : Array<Vector> =
  131. createRingPoints(outerRadius, innerRadius, players.length);
  132. for(i in 0...hwPoints.length) {
  133. //hw loc
  134. var point : Vector = hwPoints[i];
  135. //find closest star
  136. var dist : Float = 999999;
  137. var curDist : Float = 0;
  138. var starNum : Int = 0;
  139. for(j in 0...stars.length) {
  140. curDist = stars[j].location.distanceTo(point);
  141. if(curDist < dist) {
  142. dist = curDist;
  143. starNum = j;
  144. }
  145. }
  146. //found the star so now change it to HW
  147. var star : Star = stars[starNum];
  148. var player : Player = players[i];
  149. convertStar(star, player, hwMinConc);
  150. }
  151. return stars;
  152. }
  153. private static function randomizeArray(a : Array<Dynamic>) :
  154. Array<Dynamic> {
  155. a = a.copy();
  156. var newA : Array<Dynamic> = new Array<Dynamic>();
  157. var i : Int = a.length - 1;
  158. while(i >= 0) {
  159. newA.push(a.splice(Math.floor(
  160. Math.random() * (a.length) - 0.000001), 1)[0]);
  161. i --;
  162. }
  163. return newA;
  164. }
  165. private static function createRectPoints(dimensions : Vector,
  166. numberOfPoints : Int, borderPercent : Float):Array<Vector> {
  167. var points : Array<Vector> = new Array<Vector>();
  168. var sqrt : Int = Math.ceil(Math.sqrt(numberOfPoints));
  169. var plotSize : Vector =
  170. new Vector(dimensions.x / sqrt, dimensions.y / sqrt);
  171. var excessPlots :Int = Math.floor(sqrt * sqrt - numberOfPoints);
  172. var border : Float = borderPercent * Math.min(plotSize.x, plotSize.y);
  173. //remove excess
  174. var randomIntX : Int;
  175. var randomIntY : Int;
  176. var remove : Array<Array<Bool>> = new Array<Array<Bool>>();
  177. //setup array
  178. for(i in 0...sqrt) {
  179. remove[i] = new Array<Bool>();
  180. for(j in 0...sqrt) remove[i][j] = true;
  181. }
  182. //remove extra
  183. for(i in 0...excessPlots) {
  184. do {
  185. randomIntX = Math.floor(Math.random() * (sqrt - 0.0001));
  186. randomIntY = Math.floor(Math.random() * (sqrt - 0.0001));
  187. } while (remove[randomIntX][randomIntY] == false);
  188. remove[randomIntX][randomIntY] = false;
  189. }
  190. //setup actual points
  191. for(i in 0...sqrt) {
  192. for(j in 0...sqrt) {
  193. if(remove[i][j] == false) continue;
  194. var xloc : Int = Math.floor(i * plotSize.x + border +
  195. Math.random() * (plotSize.x - border * 2));
  196. var yloc : Int = Math.floor(j * plotSize.y + border +
  197. Math.random() * (plotSize.y - border * 2));
  198. points.push(new Vector(xloc, yloc));
  199. }
  200. }
  201. return points;
  202. }
  203. private static function createRingPoints(outerRadius : Float,
  204. innerRadius : Float, numberOfPoints : Int) : Array<Vector> {
  205. //divide randomly into angles
  206. var angleWidth : Float = Math.PI * 2 / numberOfPoints;
  207. var angles : Array<Float> = new Array<Float>();
  208. for(i in 0...numberOfPoints) {
  209. angles.push((i * angleWidth) + Math.random() * (0.9 * angleWidth) +
  210. 0.1 * angleWidth);
  211. }
  212. //now place the points
  213. var points : Array<Vector> = new Array<Vector>();
  214. for(i in 0...numberOfPoints) {
  215. var length : Float = Math.random() * (outerRadius - innerRadius) +
  216. innerRadius;
  217. var vec : Vector = new Vector(outerRadius, outerRadius);
  218. vec.x += length * Math.cos(angles[i]);
  219. vec.y += length * Math.sin(angles[i]);
  220. points.push(vec);
  221. }
  222. return points;
  223. }
  224. /**
  225. Creates a map based off of a cubic bezier equation. Essentially, it
  226. expands the curve by the amount radius, so that it forms a solid and
  227. fills that with stars.
  228. **/
  229. public static function generateCubicBezierMap(eq : CubicBezier,
  230. radius : Float, numberOfStars : Int, players : Array<Player>,
  231. names : Array<String>, ?hwMinConc : Concentrations) : Array<Star> {
  232. var stars = new Array<Star>();
  233. hwMinConc = if(hwMinConc == null) new Concentrations() else hwMinConc;
  234. players = cast(randomizeArray(players));
  235. names = cast(randomizeArray(names));
  236. var starPoints : Array<Vector> =
  237. createBezierPoints(eq, radius, numberOfStars, 100);
  238. createBasicStars(starPoints, names, stars);
  239. var hwPoints : Array<Vector> =
  240. createBezierPoints(eq, radius, players.length, 10);
  241. for(i in 0...hwPoints.length) {
  242. //hw loc
  243. var point : Vector = hwPoints[i];
  244. //find closest star
  245. var dist : Float = 999999;
  246. var curDist : Float = 0;
  247. var starNum : Int = 0;
  248. for(j in 0...stars.length) {
  249. curDist = stars[j].location.distanceTo(point);
  250. if(curDist < dist) {
  251. dist = curDist;
  252. starNum = j;
  253. }
  254. }
  255. //found the star so now change it to HW
  256. var star : Star = stars[starNum];
  257. var player : Player = players[i];
  258. convertStar(star, player, hwMinConc);
  259. }
  260. return stars;
  261. }
  262. private static function createBezierPoints(eq : CubicBezier, radius : Float,
  263. numberOfPoints : Int, divisions : Int) : Array<Vector> {
  264. var points = new Array<Vector>();
  265. var lines = eq.getLines(divisions);
  266. var totalLength : Float = 0;
  267. for(i in lines) {
  268. totalLength += i.length;
  269. }
  270. var lengthPerPoint = totalLength / numberOfPoints;
  271. var line : Line;
  272. var cLine : Int = 0;
  273. var cLength : Float = 0;
  274. for(i in 0...numberOfPoints){
  275. line = lines[cLine];
  276. points.push(line.getNormalPoint(radius, cLength));
  277. cLength += lengthPerPoint;
  278. if(cLength > line.length) {
  279. cLength -= line.length;
  280. cLine ++;
  281. }
  282. }
  283. return points;
  284. }
  285. private static function genHWs(stars : Array<Star>, points : Array<Vector>,
  286. players : Array<Player>, hwMinConc : Concentrations) : Void {
  287. for(i in 0...points.length) {
  288. //hw loc
  289. var point : Vector = points[i];
  290. //find closest star
  291. var dist : Float = 999999;
  292. var curDist : Float = 0;
  293. var starNum : Int = 0;
  294. for(j in 0...stars.length) {
  295. curDist = stars[j].location.distanceTo(point);
  296. if(curDist < dist) {
  297. dist = curDist;
  298. starNum = j;
  299. }
  300. }
  301. //found the star so now change it to HW
  302. var star : Star = stars[starNum];
  303. var player : Player = players[i];
  304. convertStar(star, player, hwMinConc);
  305. }
  306. }
  307. #if flash9
  308. /**
  309. Generates the map from a bitmap, where the white pixels represent
  310. possible locations for stars.
  311. Currently only works on flash9 (BitmapData class) but can be ported to
  312. neko/js (maybe php?).
  313. **/
  314. public static function createMapFromBitmap(bitmap : BitmapData,
  315. numberOfStars : Int, players : Array<Player>, names : Array<String>,
  316. ?hwMinConc : Concentrations) : Array<Star> {
  317. var stars = new Array<Star>();
  318. hwMinConc = if(hwMinConc == null) new Concentrations() else hwMinConc;
  319. players = cast(randomizeArray(players));
  320. names = cast(randomizeArray(names));
  321. var starPoints : Array<Vector> =
  322. createBitmapPoints(bitmap.clone(), numberOfStars);
  323. createBasicStars(starPoints, names, stars);
  324. createBasicStars(starPoints, names, stars);
  325. genHWs(stars, createBitmapPoints(bitmap.clone(), players.length),
  326. players, hwMinConc);
  327. return stars;
  328. }
  329. private static function createBitmapPoints(bitmap : BitmapData,
  330. numberOfPoints : Int, ?minDist : Float, ?retries : Int) :
  331. Array<Vector> {
  332. if(retries == null) retries = 2000;
  333. var points = new Array<Vector>();
  334. //calculate number of white points
  335. var wPoints = calcWhitePoints(bitmap);
  336. if(minDist == null) minDist = Math.pow(wPoints.length, 9 / 10) * 0.03 /
  337. numberOfPoints;
  338. for(i in 0...numberOfPoints) {
  339. var j : Int = -1;
  340. var placed = false;
  341. do {
  342. j++;
  343. var p = wPoints[Std.random(wPoints.length)];
  344. var flag = false;
  345. for(k in points) if(p.distanceTo(k) < minDist) {
  346. flag = true;
  347. break;
  348. }
  349. if(flag) continue;
  350. points.push(p);
  351. placed = true;
  352. break;
  353. } while(j < retries);
  354. if(placed = false) points.push(wPoints[Std.random(wPoints.length)]);
  355. }
  356. return points;
  357. }
  358. private static function calcWhitePoints(bitmap : BitmapData) :
  359. Array<Vector> {
  360. var points = new Array<Vector>();
  361. for(x in 0...bitmap.width) {
  362. for(y in 0...bitmap.height){
  363. if(bitmap.getPixel(x, y) == 0xFFFFFF)
  364. points.push(new Vector(x, y));
  365. }
  366. };
  367. return points;
  368. }
  369. #end
  370. }
  371. /**
  372. Class used to create a bezier line.
  373. **/
  374. class CubicBezier{
  375. public var p0 : Vector;
  376. public var p1 : Vector;
  377. public var p2 : Vector;
  378. public var p3 : Vector;
  379. public var ax : Float;
  380. public var bx : Float;
  381. public var cx : Float;
  382. public var ay : Float;
  383. public var by : Float;
  384. public var cy : Float;
  385. public function new(p0 : Vector, p1 : Vector, p2 : Vector, p3 : Vector) {
  386. this.p0 = p0;
  387. this.p1 = p1;
  388. this.p2 = p2;
  389. this.p3 = p3;
  390. cx = 3 * (p1.x - p0.x);
  391. bx = 3 * (p2.x - p1.x) - cx;
  392. ax = p3.x - p0.x - cx - bx;
  393. cy = 3 * (p1.y - p0.y);
  394. by = 3 * (p2.y - p1.y) - cy;
  395. ay = p3.y - p0.y - cy - by;
  396. }
  397. public function getPoint(t : Float) : Vector {
  398. //t is between 0 and 1
  399. var vec = new Vector();
  400. vec.x = ax * t * t * t + bx * t * t + cx * t + p0.x;
  401. vec.y = ay * t * t * t + by * t * t + cy * t + p0.y;
  402. return vec;
  403. }
  404. public function getLines(divisions : Int) : Array<Line> {
  405. var a : Array<Line> = new Array<Line>();
  406. var seg : Float = 1 / divisions;
  407. for(i in 0...divisions) {
  408. a.push(new Line(getPoint(i * seg), getPoint(i * seg + seg)));
  409. }
  410. return a;
  411. }
  412. }
  413. //THIS IS A STATIC LINE DO NOT TRY TO CHANGE THE ENDPOINTS (IT WONT LET YOU)
  414. /**
  415. Line used by the bezier engine to approximate the bezier segments.
  416. **/
  417. class Line {
  418. public var p0(default, null) : Vector;
  419. public var p1(default, null) : Vector;
  420. public var length(default, null) : Float;
  421. //public var slope(default, null) : Float;
  422. public function new(p0 : Vector, p1 : Vector) {
  423. this.p0 = p0;
  424. this.p1 = p1;
  425. length = Math.sqrt((p0.x - p1.x) * (p0.x - p1.x) + (p0.y - p1.y) *
  426. (p0.y - p1.y));
  427. //slope = (p1.y - p0.y) / (p1.x - p0.x);
  428. }
  429. public function getPointAtPercent(p : Float) : Vector{
  430. var angle = p1.getSub(p0).getAngle();
  431. var clength = p * length;
  432. return new Vector(clength * Math.cos(angle), clength *
  433. Math.sin(angle)).getAdd(p0);
  434. }
  435. public function getPointByLength(l : Float) : Vector {
  436. return getPointAtPercent(l / length);
  437. }
  438. //l is how far along the line the normal is
  439. public function getNormalPoint(radius : Float, l : Float) : Vector {
  440. var p = getPointByLength(l);
  441. var angle = p1.getSub(p0).getAngle() +
  442. if(Math.random() < 0.5) Math.PI / 2 else - Math.PI / 2;
  443. var nl = Math.random() * radius;
  444. return new Vector(nl * Math.cos(angle), nl * Math.sin(angle)).getAdd(p);
  445. }
  446. }