/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
- /**
- * Copyright (C) 2008 Chase Kernan
- * chase.kernan@gmail.com
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
- package com.chasekernan.hxnova.core.stars;
-
- import com.chasekernan.hxnova.core.components.ComponentSet;
- import com.chasekernan.hxnova.core.components.Scanner;
- import com.chasekernan.hxnova.core.minerals.MineralSet;
- import com.chasekernan.hxnova.core.players.Player;
- import com.chasekernan.hxnova.utils.Vector;
-
- #if flash9
- import flash.display.BitmapData;
- import flash.display.Sprite;
- #end
-
- /**
- Creates maps based upon some input data.
- Types include rectangle, ring, bezier curve, and bitmap concentrations.
- **/
- class MapCreator {
-
- public static var DEFAULT_FACTORIES : Int = 10;
- public static var DEFAULT_MINES : Int = 10;
- public static var DEFAULT_DEFENSES : Int = 10;
- public static var DEFAULT_POP : Int = 50000;
- public static var DEFAULT_STARTING_MIN : Int = 2000;
-
- private static var NULL_PLAYER = new Player();
-
- private static function createBasicStars(starPoints : Array < Vector > ,
- names : Array < String > , stars : Array < Star > ) {
- NULL_PLAYER.id = -1;
-
- for (i in 0...starPoints.length) {
- var s : Star = new Star();
- s.location = starPoints[i];
- s.name = names[i];
- s.minerals = new MineralSet();
- s.concentrations = new Concentrations(false);
- s.environment = new Environment();
- s.defenses = s.mines = s.factories = 0;
- s.hasScanner = false;
- s.owner = NULL_PLAYER;
- s.population = 0;
- stars.push(s);
- }
- }
-
- /**
- Creates a rectangular map and returns the array of stars (which contains
- both hws and regular systems).
- **/
- public static function generateRectMap(dimensions : Vector,
- numberOfStars : Int, players : Array<Player>, names : Array<String>,
- ?hwMinConc : Concentrations) : Array<Star> {
-
- var stars : Array<Star> = new Array<Star>();
- hwMinConc = if(hwMinConc == null) new Concentrations(true)
- else hwMinConc;
-
- players = cast(randomizeArray(players.copy()));
- names = cast(randomizeArray(names));
-
- var starPoints : Array<Vector> = createRectPoints(dimensions,
- numberOfStars, 0.05);
-
- createBasicStars(starPoints, names, stars);
-
- var hwPoints : Array<Vector> = createRectPoints(dimensions,
- players.length, 0.3);
-
- for(i in 0...hwPoints.length) {
- //hw loc
- var point : Vector = hwPoints[i];
-
- //find closest star
- var dist : Float = 999999;
- var curDist : Float = 0;
- var starNum : Int = 0;
-
- for (j in 0...stars.length) {
- if (stars[j].isHomeworld != null && stars[j].isHomeworld)
- continue;
-
- curDist = stars[j].location.distanceTo(point);
- if(curDist < dist) {
- dist = curDist;
- starNum = j;
- }
- }
-
- //found the star so now change it to HW
- var star : Star = stars[starNum];
- var player : Player = players[i];
-
- convertStar(star, player, hwMinConc);
- }
-
- return stars;
- }
-
- //converts a star into a homeworld
- private static function convertStar(star : Star, player : Player,
- conc : Concentrations) {
- star.owner = player;
- star.population = DEFAULT_POP;
- player.homeworld = star;
- star.isHomeworld = true;
- star.factories = DEFAULT_FACTORIES;
- star.mines = DEFAULT_MINES;
- star.defenses = DEFAULT_DEFENSES;
- star.concentrations = conc.clone();
- star.minerals = new MineralSet();
- star.production = new ProductionQueue(star);
- star.hasScanner = true;
-
- for (i in Concentrations.MINERAL_TYPES)
- star.minerals.setValue(i, Std.int(DEFAULT_STARTING_MIN *
- conc.getValue(i) / 100.0));
-
- star.environment = player.race.habSetting.getPerfectEnv();
- }
-
- /**
- Creates a ring filled with stars and returns the array of stars with
- both hws and regular systems.
- **/
- public static function generateRingMap(outerRadius : Float,
- innerRadius : Float, numberOfStars : Int, players : Array<Player>,
- names : Array<String>, ?hwMinConc : Concentrations) : Array<Star> {
-
- var stars : Array<Star> = new Array<Star>();
- hwMinConc = if(hwMinConc == null) new Concentrations() else hwMinConc;
-
- players = cast(randomizeArray(players));
- names = cast(randomizeArray(names));
-
- var starPoints : Array<Vector> =
- createRingPoints(outerRadius, innerRadius, numberOfStars);
-
- createBasicStars(starPoints, names, stars);
-
- var hwPoints : Array<Vector> =
- createRingPoints(outerRadius, innerRadius, players.length);
-
- for(i in 0...hwPoints.length) {
- //hw loc
- var point : Vector = hwPoints[i];
-
- //find closest star
- var dist : Float = 999999;
- var curDist : Float = 0;
- var starNum : Int = 0;
-
- for(j in 0...stars.length) {
- curDist = stars[j].location.distanceTo(point);
- if(curDist < dist) {
- dist = curDist;
- starNum = j;
- }
- }
-
- //found the star so now change it to HW
- var star : Star = stars[starNum];
- var player : Player = players[i];
-
- convertStar(star, player, hwMinConc);
- }
-
- return stars;
- }
-
- private static function randomizeArray(a : Array<Dynamic>) :
- Array<Dynamic> {
- a = a.copy();
- var newA : Array<Dynamic> = new Array<Dynamic>();
- var i : Int = a.length - 1;
- while(i >= 0) {
- newA.push(a.splice(Math.floor(
- Math.random() * (a.length) - 0.000001), 1)[0]);
- i --;
- }
- return newA;
- }
-
- private static function createRectPoints(dimensions : Vector,
- numberOfPoints : Int, borderPercent : Float):Array<Vector> {
- var points : Array<Vector> = new Array<Vector>();
- var sqrt : Int = Math.ceil(Math.sqrt(numberOfPoints));
- var plotSize : Vector =
- new Vector(dimensions.x / sqrt, dimensions.y / sqrt);
- var excessPlots :Int = Math.floor(sqrt * sqrt - numberOfPoints);
- var border : Float = borderPercent * Math.min(plotSize.x, plotSize.y);
-
- //remove excess
- var randomIntX : Int;
- var randomIntY : Int;
- var remove : Array<Array<Bool>> = new Array<Array<Bool>>();
-
- //setup array
- for(i in 0...sqrt) {
- remove[i] = new Array<Bool>();
- for(j in 0...sqrt) remove[i][j] = true;
- }
-
- //remove extra
- for(i in 0...excessPlots) {
- do {
- randomIntX = Math.floor(Math.random() * (sqrt - 0.0001));
- randomIntY = Math.floor(Math.random() * (sqrt - 0.0001));
- } while (remove[randomIntX][randomIntY] == false);
- remove[randomIntX][randomIntY] = false;
- }
-
- //setup actual points
- for(i in 0...sqrt) {
- for(j in 0...sqrt) {
- if(remove[i][j] == false) continue;
-
- var xloc : Int = Math.floor(i * plotSize.x + border +
- Math.random() * (plotSize.x - border * 2));
- var yloc : Int = Math.floor(j * plotSize.y + border +
- Math.random() * (plotSize.y - border * 2));
-
- points.push(new Vector(xloc, yloc));
- }
- }
- return points;
- }
-
- private static function createRingPoints(outerRadius : Float,
- innerRadius : Float, numberOfPoints : Int) : Array<Vector> {
- //divide randomly into angles
- var angleWidth : Float = Math.PI * 2 / numberOfPoints;
- var angles : Array<Float> = new Array<Float>();
-
- for(i in 0...numberOfPoints) {
- angles.push((i * angleWidth) + Math.random() * (0.9 * angleWidth) +
- 0.1 * angleWidth);
- }
-
- //now place the points
- var points : Array<Vector> = new Array<Vector>();
- for(i in 0...numberOfPoints) {
- var length : Float = Math.random() * (outerRadius - innerRadius) +
- innerRadius;
- var vec : Vector = new Vector(outerRadius, outerRadius);
- vec.x += length * Math.cos(angles[i]);
- vec.y += length * Math.sin(angles[i]);
- points.push(vec);
- }
-
- return points;
- }
-
- /**
- Creates a map based off of a cubic bezier equation. Essentially, it
- expands the curve by the amount radius, so that it forms a solid and
- fills that with stars.
- **/
- public static function generateCubicBezierMap(eq : CubicBezier,
- radius : Float, numberOfStars : Int, players : Array<Player>,
- names : Array<String>, ?hwMinConc : Concentrations) : Array<Star> {
- var stars = new Array<Star>();
-
- hwMinConc = if(hwMinConc == null) new Concentrations() else hwMinConc;
-
- players = cast(randomizeArray(players));
- names = cast(randomizeArray(names));
-
- var starPoints : Array<Vector> =
- createBezierPoints(eq, radius, numberOfStars, 100);
-
- createBasicStars(starPoints, names, stars);
-
- var hwPoints : Array<Vector> =
- createBezierPoints(eq, radius, players.length, 10);
-
- for(i in 0...hwPoints.length) {
- //hw loc
- var point : Vector = hwPoints[i];
-
- //find closest star
- var dist : Float = 999999;
- var curDist : Float = 0;
- var starNum : Int = 0;
-
- for(j in 0...stars.length) {
- curDist = stars[j].location.distanceTo(point);
- if(curDist < dist) {
- dist = curDist;
- starNum = j;
- }
- }
-
- //found the star so now change it to HW
- var star : Star = stars[starNum];
- var player : Player = players[i];
-
- convertStar(star, player, hwMinConc);
- }
-
- return stars;
- }
-
- private static function createBezierPoints(eq : CubicBezier, radius : Float,
- numberOfPoints : Int, divisions : Int) : Array<Vector> {
- var points = new Array<Vector>();
-
- var lines = eq.getLines(divisions);
- var totalLength : Float = 0;
- for(i in lines) {
- totalLength += i.length;
- }
- var lengthPerPoint = totalLength / numberOfPoints;
-
- var line : Line;
- var cLine : Int = 0;
- var cLength : Float = 0;
- for(i in 0...numberOfPoints){
- line = lines[cLine];
- points.push(line.getNormalPoint(radius, cLength));
-
- cLength += lengthPerPoint;
- if(cLength > line.length) {
- cLength -= line.length;
- cLine ++;
- }
- }
-
-
- return points;
- }
-
- private static function genHWs(stars : Array<Star>, points : Array<Vector>,
- players : Array<Player>, hwMinConc : Concentrations) : Void {
- for(i in 0...points.length) {
- //hw loc
- var point : Vector = points[i];
-
- //find closest star
- var dist : Float = 999999;
- var curDist : Float = 0;
- var starNum : Int = 0;
-
- for(j in 0...stars.length) {
- curDist = stars[j].location.distanceTo(point);
- if(curDist < dist) {
- dist = curDist;
- starNum = j;
- }
- }
-
- //found the star so now change it to HW
- var star : Star = stars[starNum];
- var player : Player = players[i];
-
- convertStar(star, player, hwMinConc);
- }
- }
-
- #if flash9
- /**
- Generates the map from a bitmap, where the white pixels represent
- possible locations for stars.
- Currently only works on flash9 (BitmapData class) but can be ported to
- neko/js (maybe php?).
- **/
- public static function createMapFromBitmap(bitmap : BitmapData,
- numberOfStars : Int, players : Array<Player>, names : Array<String>,
- ?hwMinConc : Concentrations) : Array<Star> {
- var stars = new Array<Star>();
-
- hwMinConc = if(hwMinConc == null) new Concentrations() else hwMinConc;
-
- players = cast(randomizeArray(players));
- names = cast(randomizeArray(names));
-
- var starPoints : Array<Vector> =
- createBitmapPoints(bitmap.clone(), numberOfStars);
-
- createBasicStars(starPoints, names, stars);
- createBasicStars(starPoints, names, stars);
-
- genHWs(stars, createBitmapPoints(bitmap.clone(), players.length),
- players, hwMinConc);
-
- return stars;
- }
-
- private static function createBitmapPoints(bitmap : BitmapData,
- numberOfPoints : Int, ?minDist : Float, ?retries : Int) :
- Array<Vector> {
- if(retries == null) retries = 2000;
-
-
- var points = new Array<Vector>();
-
- //calculate number of white points
- var wPoints = calcWhitePoints(bitmap);
- if(minDist == null) minDist = Math.pow(wPoints.length, 9 / 10) * 0.03 /
- numberOfPoints;
-
- for(i in 0...numberOfPoints) {
- var j : Int = -1;
- var placed = false;
- do {
- j++;
- var p = wPoints[Std.random(wPoints.length)];
- var flag = false;
- for(k in points) if(p.distanceTo(k) < minDist) {
- flag = true;
- break;
- }
- if(flag) continue;
- points.push(p);
- placed = true;
- break;
- } while(j < retries);
- if(placed = false) points.push(wPoints[Std.random(wPoints.length)]);
- }
-
- return points;
- }
-
- private static function calcWhitePoints(bitmap : BitmapData) :
- Array<Vector> {
- var points = new Array<Vector>();
- for(x in 0...bitmap.width) {
- for(y in 0...bitmap.height){
- if(bitmap.getPixel(x, y) == 0xFFFFFF)
- points.push(new Vector(x, y));
- }
- };
- return points;
- }
- #end
- }
-
- /**
- Class used to create a bezier line.
- **/
- class CubicBezier{
- public var p0 : Vector;
- public var p1 : Vector;
- public var p2 : Vector;
- public var p3 : Vector;
-
- public var ax : Float;
- public var bx : Float;
- public var cx : Float;
-
- public var ay : Float;
- public var by : Float;
- public var cy : Float;
-
- public function new(p0 : Vector, p1 : Vector, p2 : Vector, p3 : Vector) {
- this.p0 = p0;
- this.p1 = p1;
- this.p2 = p2;
- this.p3 = p3;
-
- cx = 3 * (p1.x - p0.x);
- bx = 3 * (p2.x - p1.x) - cx;
- ax = p3.x - p0.x - cx - bx;
-
- cy = 3 * (p1.y - p0.y);
- by = 3 * (p2.y - p1.y) - cy;
- ay = p3.y - p0.y - cy - by;
- }
-
- public function getPoint(t : Float) : Vector {
- //t is between 0 and 1
- var vec = new Vector();
-
- vec.x = ax * t * t * t + bx * t * t + cx * t + p0.x;
- vec.y = ay * t * t * t + by * t * t + cy * t + p0.y;
-
- return vec;
- }
-
- public function getLines(divisions : Int) : Array<Line> {
- var a : Array<Line> = new Array<Line>();
- var seg : Float = 1 / divisions;
- for(i in 0...divisions) {
- a.push(new Line(getPoint(i * seg), getPoint(i * seg + seg)));
- }
-
- return a;
- }
- }
-
- //THIS IS A STATIC LINE DO NOT TRY TO CHANGE THE ENDPOINTS (IT WONT LET YOU)
-
- /**
- Line used by the bezier engine to approximate the bezier segments.
- **/
- class Line {
- public var p0(default, null) : Vector;
- public var p1(default, null) : Vector;
-
- public var length(default, null) : Float;
- //public var slope(default, null) : Float;
-
- public function new(p0 : Vector, p1 : Vector) {
- this.p0 = p0;
- this.p1 = p1;
- length = Math.sqrt((p0.x - p1.x) * (p0.x - p1.x) + (p0.y - p1.y) *
- (p0.y - p1.y));
- //slope = (p1.y - p0.y) / (p1.x - p0.x);
- }
-
- public function getPointAtPercent(p : Float) : Vector{
- var angle = p1.getSub(p0).getAngle();
- var clength = p * length;
- return new Vector(clength * Math.cos(angle), clength *
- Math.sin(angle)).getAdd(p0);
- }
-
- public function getPointByLength(l : Float) : Vector {
- return getPointAtPercent(l / length);
- }
-
- //l is how far along the line the normal is
- public function getNormalPoint(radius : Float, l : Float) : Vector {
- var p = getPointByLength(l);
- var angle = p1.getSub(p0).getAngle() +
- if(Math.random() < 0.5) Math.PI / 2 else - Math.PI / 2;
- var nl = Math.random() * radius;
- return new Vector(nl * Math.cos(angle), nl * Math.sin(angle)).getAdd(p);
- }
- }