1
0
forked from 0ad/0ad

More work on RMS:

- Created binaries/data/mods/official/maps/random folder to store maps
and also moved rmlibrary.js to maps (though perhaps it should be
somewhere in system?).
- RM generator now uses "logical terrains" that can have units attached
to them in addition to textures, for things like forests.
- Added basic clump placer, avoid constraints, layered painter, and
random terrains (each tile is chosen between several options).
- Misc. infrastructure changes.

This was SVN commit r2378.
This commit is contained in:
Matei 2005-06-06 07:46:28 +00:00
parent dbecf93e82
commit bd53b14f58
27 changed files with 957 additions and 162 deletions

View File

@ -0,0 +1,10 @@
const SIZE = 128;
init(SIZE, "snow forest", 0);
constr = new AvoidTerrainConstraint("snow grass 2");
println(createArea(
new MultiPlacer(new ClumpPlacer(15.0, 0.01, 0.01), 10, 100),
new TerrainPainter(new RandomTerrain("snow grass 2|wrld_flora_pine", "snow grass 2")),
constr
));

View File

@ -0,0 +1,131 @@
// Object type constants
const
TYPE_RECTPLACER = 1,
TYPE_TERRAINPAINTER = 2,
TYPE_NULLCONSTRAINT = 3,
TYPE_RANDOMTERRAIN = 4,
TYPE_LAYEREDPAINTER = 5,
TYPE_AVOIDAREACONSTRAINT = 6,
TYPE_CLUMPPLACER = 7,
TYPE_EXACTPLACER = 8,
TYPE_AVOIDTERRAINCONSTRAINT = 9,
TYPE_ANDCONSTRAINT = 10,
TYPE_MULTIPLACER = 11;
// Utility functions
function println(x) {
print(x);
print("\n");
}
function argsToArray(x) {
if(x.length!=1) {
var ret = new Array();
for(var i=0; i<x.length; i++) {
ret[i] = x[i];
}
return ret;
}
else {
return x[0];
}
}
function chooseRand() {
if(arguments.length==0) {
error("chooseRand: requires at least 1 argument");
}
var ar = argsToArray(arguments);
return ar[randInt(ar.length)];
}
// Area placers
function RectPlacer(x1, y1, x2, y2) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
this.raw = function() {
return [TYPE_RECTPLACER, this.x1, this.y1, this.x2, this.y2];
}
}
function TerrainPainter(terrain) {
this.terrain = terrain;
this.raw = function() {
return [TYPE_TERRAINPAINTER, this.terrain];
}
}
function NullConstraint() {
this.raw = function() {
return [TYPE_NULLCONSTRAINT];
}
}
function RandomTerrain() {
this.terrains = argsToArray(arguments);
this.raw = function() {
return [TYPE_RANDOMTERRAIN, this.terrains];
}
}
function LayeredPainter(widths, terrains) {
this.widths = widths;
this.terrains = terrains;
this.raw = function() {
return [TYPE_LAYEREDPAINTER, this.widths, this.terrains];
}
}
function AvoidAreaConstraint(area) {
this.area = area;
this.raw = function() {
return [TYPE_AVOIDAREACONSTRAINT, this.area];
}
}
function ClumpPlacer(size, coherence, smoothness) {
this.size = size;
this.coherence = coherence;
this.smoothness = smoothness;
this.raw = function() {
return [TYPE_CLUMPPLACER, this.size, this.coherence, this.smoothness];
}
}
function ExactPlacer(cp, x, y) {
this.cp = cp;
this.x = x;
this.y = y;
this.raw = function() {
return [TYPE_EXACTPLACER, this.cp, this.x, this.y];
}
}
function AvoidTerrainConstraint(texture) {
this.texture = texture;
this.raw = function() {
return [TYPE_AVOIDTERRAINCONSTRAINT, this.texture];
}
}
function AndConstraint(a, b) {
this.a = a;
this.b = b;
this.raw = function() {
return [TYPE_ANDCONSTRAINT, this.a, this.b];
}
}
function MultiPlacer(cp, num, maxFail) {
this.cp = cp;
this.num = num;
this.maxFail = maxFail;
this.raw = function() {
return [TYPE_MULTIPLACER, this.cp, this.num, this.maxFail];
}
}

View File

@ -5,6 +5,8 @@
#include "map.h"
#include "entity.h"
#include "objparse.h"
#include "terrain.h"
#include "simpleconstraints.h"
using namespace std;
@ -15,8 +17,8 @@ JSFunctionSpec globalFunctions[] = {
{"init", init, 3},
{"print", print, 1},
{"error", error, 1},
{"getTerrain", getTerrain, 2},
{"setTerrain", setTerrain, 3},
{"getTexture", getTexture, 2},
{"setTexture", setTexture, 3},
{"getHeight", getHeight, 2},
{"setHeight", setHeight, 3},
{"randInt", randInt, 1},
@ -49,7 +51,7 @@ void ValidateArgs(const char* types, JSContext* cx, uintN argc, jsval* argv, con
break;
case 's':
if(!JSVAL_IS_STRING(argv[i])) {
JS_ReportError(cx, "%s: argument %d must be an integer", function, i+1);
JS_ReportError(cx, "%s: argument %d must be a string", function, i+1);
}
break;
case 'n':
@ -95,13 +97,16 @@ JSBool error(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
JSBool init(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
ValidateArgs("isn", cx, argc, argv, __FUNCTION__);
ValidateArgs("i*n", cx, argc, argv, __FUNCTION__);
if(theMap != 0) {
JS_ReportError(cx, "init: cannot be called twice");
}
int size = JSVAL_TO_INT(argv[0]);
char* baseTerrain = JS_GetStringBytes(JSVAL_TO_STRING(argv[1]));
Terrain* baseTerrain = ParseTerrain(cx, argv[1]);
if(!baseTerrain) {
JS_ReportError(cx, "init: argument 2 must be a terrain");
}
jsdouble baseHeight;
JS_ValueToNumber(cx, argv[2], &baseHeight);
@ -109,31 +114,31 @@ JSBool init(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
return JS_TRUE;
}
JSBool getTerrain(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
JSBool getTexture(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
ValidateArgs("ii", cx, argc, argv, __FUNCTION__);
if(theMap == 0) {
JS_ReportError(cx, "getTerrain: cannot be called before init()");
JS_ReportError(cx, "getTexture: cannot be called before init()");
}
int x = JSVAL_TO_INT(argv[0]);
int y = JSVAL_TO_INT(argv[1]);
string terrain = theMap->getTerrain(x, y);
*rval = NewJSString(terrain);
string texture = theMap->getTexture(x, y);
*rval = NewJSString(texture);
return JS_TRUE;
}
JSBool setTerrain(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
JSBool setTexture(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
ValidateArgs("iis", cx, argc, argv, __FUNCTION__);
if(theMap == 0) {
JS_ReportError(cx, "setTerrain: cannot be called before init()");
JS_ReportError(cx, "setTexture: cannot be called before init()");
}
int x = JSVAL_TO_INT(argv[0]);
int y = JSVAL_TO_INT(argv[1]);
char* terrain = JS_GetStringBytes(JSVAL_TO_STRING(argv[2]));
theMap->setTerrain(x, y, terrain);
char* texture = JS_GetStringBytes(JSVAL_TO_STRING(argv[2]));
theMap->setTexture(x, y, texture);
return JS_TRUE;
}
@ -207,6 +212,23 @@ JSBool addEntity(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *r
return JS_TRUE;
}
JSBool placeTerrain(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
ValidateArgs("ii*", cx, argc, argv, __FUNCTION__);
if(theMap == 0) {
JS_ReportError(cx, "placeTerrain: cannot be called before init()");
}
int x = JSVAL_TO_INT(argv[0]);
int y = JSVAL_TO_INT(argv[1]);
Terrain* terrain = ParseTerrain(cx, argv[2]);
if(!terrain) {
JS_ReportError(cx, "placeTerrain: invalid terrain argument");
}
theMap->placeTerrain(x, y, terrain);
return JS_TRUE;
}
JSBool createArea(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
if(argc != 2 && argc != 3) {
@ -221,14 +243,14 @@ JSBool createArea(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *
Constraint* constr;
if(!(placer = ParsePlacer(cx, argv[0]))) {
JS_ReportError(cx, "createArea: argument 1 must be an area placer");
JS_ReportError(cx, "createArea: argument 1 must be a valid area placer");
}
if(!(painter = ParsePainter(cx, argv[1]))) {
JS_ReportError(cx, "createArea: argument 2 must be an area paint");
JS_ReportError(cx, "createArea: argument 2 must be a valid area painter");
}
if(argc == 3) {
if(!(constr = ParseConstraint(cx, argv[2]))) {
JS_ReportError(cx, "createArea: argument 3 must be a constraint");
JS_ReportError(cx, "createArea: argument 3 must be a valid constraint");
}
}
else {

View File

@ -14,12 +14,14 @@ JSBool print(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
JSBool randInt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
JSBool randFloat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
JSBool getTerrain(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
JSBool setTerrain(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
JSBool getTexture(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
JSBool setTexture(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
JSBool getHeight(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
JSBool setHeight(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
JSBool placeTerrain(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
JSBool addEntity(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
JSBool createArea(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);

View File

@ -3,11 +3,12 @@
#include "point.h"
#include "constraint.h"
#include "map.h"
class CenteredPlacer
{
public:
virtual bool place(class Map* m, Constraint* constr, std::vector<Point>& ret, int x, int y) = 0;
virtual bool place(Map* m, Constraint* constr, std::vector<Point>& ret, int x, int y) = 0;
CenteredPlacer(void);
virtual ~CenteredPlacer(void);

View File

@ -0,0 +1,86 @@
#include "stdafx.h"
#include "clumpplacer.h"
#include "random.h"
using namespace std;
ClumpPlacer::ClumpPlacer(float size, float coherence, float smoothness)
{
this->size = size;
this->coherence = coherence;
this->smoothness = smoothness;
}
ClumpPlacer::~ClumpPlacer()
{
}
bool ClumpPlacer::place(class Map* m, Constraint* constr, std::vector<Point>& ret, int x, int y) {
float radius = sqrt(size / PI);
float perim = 3 * radius * 2 * PI;
int intPerim = (int)(ceil(perim));
vector<float> noise(intPerim);
//cout << "Starting with s=" << smoothness << ", p=" << perim << endl;
//cout << "Ctrl Vals: " << endl;
int ctrlPts = 1 + (int)(1.0/max(smoothness,1.0f/intPerim));
if(ctrlPts > radius * 2 * PI) ctrlPts = (int) (radius * 2 * PI) + 1;
vector<float> ctrlCoords(ctrlPts+1);
vector<float> ctrlVals(ctrlPts+1);
for(int i=0; i<ctrlPts; i++) {
ctrlCoords[i] = i * perim / ctrlPts;
ctrlVals[i] = 2.0*RandFloat();
//cout << ctrlCoords[i] << " " << ctrlVals[i] << endl;
}
//cout << "Noise Vals: " << endl;
int c = 0;
int looped = 0;
for(int i=0; i<intPerim; i++) {
if(ctrlCoords[(c+1) % ctrlPts] < i && !looped) {
c = (c+1) % ctrlPts;
if(c==ctrlPts-1) looped = 1;
}
float t = (i - ctrlCoords[c]) / ((looped ? perim : ctrlCoords[(c+1)%ctrlPts]) - ctrlCoords[c]);
float v0 = ctrlVals[(c+ctrlPts-1)%ctrlPts];
float v1 = ctrlVals[c];
float v2 = ctrlVals[(c+1)%ctrlPts];
float v3 = ctrlVals[(c+2)%ctrlPts];
float P = (v3 - v2) - (v0 - v1);
float Q = (v0 - v1) - P;
float R = v2 - v0;
float S = v1;
noise[i] = P*t*t*t + Q*t*t + R*t + S;
//noise[i] = ctrlVals[(c+1)%ctrlPts] * t + ctrlVals[c] * (1-t);
//cout << i << " " << c << " " << noise[i] << endl;
}
int failed = 0;
for(int p=0; p<intPerim; p++) {
float th = 2 * PI * p / perim;
float r = radius * (1 + (1-coherence)*noise[p]);
float s = sin(th);
float c = cos(th);
float xx=x, yy=y;
for(float k=0; k<r; k++) {
int i = (int)xx, j = (int)yy;
if(m->validT(i, j) && constr->allows(m, i, j)) {
ret.push_back(Point(i, j));
}
else {
failed++;
}
xx += s;
yy += c;
}
}
return failed > 0 ? false : true;
/*if(m->validT(x,y)) {
ret.push_back(Point(x,y));
}
else {
return false;
}
return true;*/
}

View File

@ -0,0 +1,20 @@
#ifndef __CLUMPPLACER_H__
#define __CLUMPPLACER_H__
#include "centeredplacer.h"
#include "map.h"
class ClumpPlacer : public CenteredPlacer
{
private:
float size;
float coherence;
float smoothness;
public:
virtual bool place(Map* m, Constraint* constr, std::vector<Point>& ret, int x, int y);
ClumpPlacer(float size, float coherence, float smoothness);
virtual ~ClumpPlacer();
};
#endif

View File

@ -0,0 +1,74 @@
#include "stdafx.h"
#include "layeredpainter.h"
#include "area.h"
#include "terrain.h"
#include "map.h"
using namespace std;
LayeredPainter::LayeredPainter(const vector<Terrain*>& ts, const vector<int>& ws)
{
terrains = ts;
widths = ws;
}
LayeredPainter::~LayeredPainter(void)
{
}
void LayeredPainter::paint(Map* m, Area* a) {
map<Point, int> saw;
map<Point, int> dist;
queue<Point> q;
// push edge points
vector<Point>& pts = a->points;
for(int i=0; i<pts.size(); i++) {
int x = pts[i].x, y = pts[i].y;
for(int dx=-1; dx<=1; dx++) {
for(int dy=-1; dy<=1; dy++) {
int nx = x+dx, ny = y+dy;
Point np(nx, ny);
if(m->validT(nx, ny) && m->area[nx][ny]!=a && !saw[np]) {
saw[np] = 1;
dist[np] = 0;
q.push(np);
}
}
}
}
// do BFS inwards to find distances to edge
while(!q.empty()) {
Point p = q.front();
q.pop();
int d = dist[p];
// paint if in area
if(m->area[p.x][p.y] == a) {
int w=0, i=0;
for(; i<widths.size(); i++) {
w += widths[i];
if(w>=d) {
break;
}
}
terrains[i]->place(m, p.x, p.y);
}
// enqueue neighbours
for(int dx=-1; dx<=1; dx++) {
for(int dy=-1; dy<=1; dy++) {
int nx = p.x+dx, ny = p.y+dy;
Point np(nx, ny);
if(m->validT(nx, ny) && m->area[nx][ny]==a && !saw[np]) {
saw[np] = 1;
dist[np] = d+1;
q.push(np);
}
}
}
}
}

View File

@ -0,0 +1,20 @@
#ifndef __LAYEREDPAINTER_H__
#define __LAYEREDPAINTER_H__
#include "areapainter.h"
#include "terrain.h"
class LayeredPainter :
public AreaPainter
{
private:
std::vector<Terrain*> terrains;
std::vector<int> widths;
public:
LayeredPainter(const std::vector<Terrain*>& terrains, const std::vector<int>& widths);
~LayeredPainter(void);
virtual void paint(class Map* m, class Area* a);
};
#endif

View File

@ -1,46 +0,0 @@
// Object type constants
const
TYPE_RECTPLACER = 1,
TYPE_TERRAINPAINTER = 2,
TYPE_NULLCONSTRAINT = 3;
// Utility functions
function println(x) {
print(x);
print("\n");
}
function chooseRand() {
if(arguments.length==0) {
error("chooseRand: requires at least 1 argument");
}
var ar = (arguments.length==1 ? arguments[0] : arguments);
return ar[randInt(ar.length)];
}
// Area placers
function RectPlacer(x1, y1, x2, y2) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
this.raw = function() {
return [TYPE_RECTPLACER, this.x1, this.y1, this.x2, this.y2];
}
}
function TerrainPainter(terrain) {
this.terrain = terrain;
this.raw = function() {
return [TYPE_TERRAINPAINTER, this.terrain];
}
}
function NullConstraint() {
this.raw = function() {
return [TYPE_NULLCONSTRAINT];
}
}

View File

@ -5,7 +5,7 @@
using namespace std;
Map::Map(int size, const string& baseTerrain, float baseHeight) {
Map::Map(int size, Terrain* baseTerrain, float baseHeight) {
if(size<0 || size>1024) {
JS_ReportError(cx, "init: map size out of range");
}
@ -15,14 +15,14 @@ Map::Map(int size, const string& baseTerrain, float baseHeight) {
this->size = size;
int baseId = getId(baseTerrain);
terrain = new int*[size];
texture = new int*[size];
for(int i=0; i<size; i++) {
terrain[i] = new int[size];
for(int j=0; j<size; j++) {
terrain[i][j] = baseId;
}
texture[i] = new int[size];
}
terrainEntities = new vector<Entity*>*[size];
for(int i=0; i<size; i++) {
terrainEntities[i] = new vector<Entity*>[size];
}
area = new Area**[size];
@ -40,37 +40,48 @@ Map::Map(int size, const string& baseTerrain, float baseHeight) {
height[i][j] = baseHeight;
}
}
for(int i=0; i<size; i++) {
for(int j=0; j<size; j++) {
baseTerrain->place(this, i, j);
}
}
}
Map::~Map() {
for(int i=0; i<size; i++) {
delete[] terrain[i];
delete[] texture[i];
}
delete[] terrain;
delete[] texture;
for(int i=0; i<size; i++) {
delete[] area[i];
delete[] terrainEntities[i];
}
delete[] area;
delete[] terrainEntities;
for(int i=0; i<size+1; i++) {
delete[] height[i];
}
delete[] height;
for(int i=0; i<size; i++) {
delete[] area[i];
}
delete[] area;
for(int i=0; i<entities.size(); i++) {
delete entities[i];
}
}
int Map::getId(string terrain) {
if(nameToId.find(terrain) != nameToId.end()) {
return nameToId[terrain];
int Map::getId(string texture) {
if(nameToId.find(texture) != nameToId.end()) {
return nameToId[texture];
}
else {
int newId = nameToId.size();
nameToId[terrain] = newId;
idToName[newId] = terrain;
nameToId[texture] = newId;
idToName[newId] = texture;
return newId;
}
}
@ -83,14 +94,14 @@ bool Map::validH(int x, int y) {
return x>=0 && y>=0 && x<size+1 && y<size+1;
}
string Map::getTerrain(int x, int y) {
if(!validT(x,y)) JS_ReportError(cx, "getTerrain: invalid tile position");
return idToName[terrain[x][y]];
string Map::getTexture(int x, int y) {
if(!validT(x,y)) JS_ReportError(cx, "getTexture: invalid tile position");
return idToName[texture[x][y]];
}
void Map::setTerrain(int x, int y, const string& t) {
if(!validT(x,y)) JS_ReportError(cx, "setTerrain: invalid tile position");
terrain[x][y] = getId(t);
void Map::setTexture(int x, int y, const string& t) {
if(!validT(x,y)) JS_ReportError(cx, "setTexture: invalid tile position");
texture[x][y] = getId(t);
}
float Map::getHeight(int x, int y) {
@ -103,6 +114,10 @@ void Map::setHeight(int x, int y, float h) {
height[x][y] = h;
}
void Map::placeTerrain(int x, int y, Terrain* t) {
t->place(this, x, y);
}
void Map::addEntity(Entity* ent) {
entities.push_back(ent);
}
@ -117,5 +132,6 @@ Area* Map::createArea(AreaPlacer* placer, AreaPainter* painter, Constraint* cons
area[points[i].x][points[i].y] = a;
}
painter->paint(this, a);
areas.push_back(a);
return a;
}

View File

@ -6,11 +6,13 @@
#include "areaplacer.h"
#include "constraint.h"
#include "entity.h"
#include "terrain.h"
class Map {
public:
int size;
int** terrain;
int** texture;
std::vector<Entity*>** terrainEntities;
float** height;
Area*** area;
std::map<std::string, int> nameToId;
@ -18,20 +20,22 @@ public:
std::vector<Entity*> entities;
std::vector<Area*> areas;
Map(int size, const std::string& baseTerrain, float baseHeight);
Map(int size, Terrain* baseTerrain, float baseHeight);
~Map();
int getId(std::string terrain);
int getId(std::string texture);
bool validT(int x, int y);
bool validH(int x, int y);
std::string getTerrain(int x, int y);
void setTerrain(int x, int y, const std::string& terrain);
std::string getTexture(int x, int y);
void setTexture(int x, int y, const std::string& texture);
float getHeight(int x, int y);
void setHeight(int x, int y, float height);
void placeTerrain(int x, int y, Terrain* t);
void addEntity(class Entity* ent);
Area* createArea(AreaPlacer* placer, AreaPainter* painter, Constraint* constr);

View File

@ -1,11 +1,17 @@
#include "stdafx.h"
#include "objparse.h"
#include "rmgen.h"
#include "simpleconstraints.h"
#include "simplepainters.h"
#include "simpleplacers.h"
#include "rectplacer.h"
#include "layeredpainter.h"
#include "clumpplacer.h"
using namespace std;
bool GetRaw(JSContext* cx, jsval val, JSObject** retObj, int* retType) {
if(!JSVAL_IS_OBJECT(val)) return 0;
if(!JSVAL_IS_OBJECT(val)) return false;
JSObject* obj = JSVAL_TO_OBJECT(val);
jsval ret;
if(!JS_CallFunctionName(cx, obj, "raw", 0, 0, &ret)) {
@ -61,7 +67,7 @@ bool ParseFields(JSContext* cx, JSObject* array, const char* format, ...) {
}
else
{
cerr << "Internal Error: unsupported type '" << format[i] << "' for ParseArgs!\n";
cerr << "Internal Error: unsupported type '" << format[i] << "' for ParseFields!\n";
Shutdown(1);
return false;
}
@ -70,31 +76,99 @@ bool ParseFields(JSContext* cx, JSObject* array, const char* format, ...) {
return true;
}
AreaPlacer* ParsePlacer(JSContext* cx, jsval val) {
JSObject* obj; int type;
if(!GetRaw(cx, val, &obj, &type)) return 0;
int x1, y1, x2, y2;
switch(type) {
case TYPE_RECTPLACER:
ParseFields(cx, obj, "iiii", &x1, &y1, &x2, &y2);
return new RectPlacer(x1, y1, x2, y2);
default:
return 0;
bool ParseArray(JSContext* cx, jsval val, vector<jsval>& ret) {
ret.clear();
if(!JSVAL_IS_OBJECT(val)) return false;
JSObject* obj = JSVAL_TO_OBJECT(val);
if(!JS_IsArrayObject(cx, obj)) return false;
jsuint len;
JS_GetArrayLength(cx, obj, &len);
for(int i=0; i<len; i++) {
jsval rval;
JS_GetElement(cx, obj, i, &rval);
ret.push_back(rval);
}
return true;
}
AreaPainter* ParsePainter(JSContext* cx, jsval val) {
JSObject* obj; int type;
if(!GetRaw(cx, val, &obj, &type)) return 0;
string terrain;
jsval jsv, jsv2;
Terrain* terrain = 0;
vector<jsval> array;
vector<Terrain*> terrains;
vector<int> widths;
switch(type) {
case TYPE_TERRAINPAINTER:
ParseFields(cx, obj, "s", &terrain);
if(!ParseFields(cx, obj, "*", &jsv)) return 0;
terrain = ParseTerrain(cx, jsv);
if(terrain==0) return 0;
return new TerrainPainter(terrain);
case TYPE_LAYEREDPAINTER:
if(!ParseFields(cx, obj, "**", &jsv, &jsv2)) return 0;
if(!ParseArray(cx, jsv, array)) return 0;
for(int i=0; i<array.size(); i++) {
if(!JSVAL_IS_INT(array[i])) return 0;
widths.push_back(JSVAL_TO_INT(array[i]));
}
if(!ParseArray(cx, jsv2, array)) return 0;
for(int i=0; i<array.size(); i++) {
terrain = ParseTerrain(cx, array[i]);
if(terrain==0) return 0;
terrains.push_back(terrain);
}
if(terrains.size() != 1+widths.size()) return 0;
return new LayeredPainter(terrains, widths);
default:
return 0;
}
}
AreaPlacer* ParsePlacer(JSContext* cx, jsval val) {
JSObject* obj; int type;
if(!GetRaw(cx, val, &obj, &type)) return 0;
jsval jsv;
int x, y, x1, y1, x2, y2, num, maxFail;
CenteredPlacer* cp;
switch(type) {
case TYPE_RECTPLACER:
if(!ParseFields(cx, obj, "iiii", &x1, &y1, &x2, &y2)) return 0;
return new RectPlacer(x1, y1, x2, y2);
case TYPE_EXACTPLACER:
if(!ParseFields(cx, obj, "*ii", &jsv, &x, &y)) return 0;
if(!(cp = ParseCenteredPlacer(cx, jsv))) return 0;
return new ExactPlacer(cp, x, y);
case TYPE_MULTIPLACER:
if(!ParseFields(cx, obj, "*ii", &jsv, &num, &maxFail)) return 0;
if(!(cp = ParseCenteredPlacer(cx, jsv))) return 0;
return new MultiPlacer(cp, num, maxFail);
default:
return 0;
}
}
CenteredPlacer* ParseCenteredPlacer(JSContext* cx, jsval val) {
JSObject* obj; int type;
if(!GetRaw(cx, val, &obj, &type)) return 0;
int areaId;
float size, coherence, smoothness;
switch(type) {
case TYPE_CLUMPPLACER:
if(!ParseFields(cx, obj, "nnn", &size, &coherence, &smoothness)) return 0;
return new ClumpPlacer(size, coherence, smoothness);
default:
return 0;
}
@ -106,11 +180,66 @@ Constraint* ParseConstraint(JSContext* cx, jsval val) {
JSObject* obj; int type;
if(!GetRaw(cx, val, &obj, &type)) return 0;
int areaId;
string texture;
jsval jsv, jsv2;
Constraint* c1, *c2;
switch(type) {
case TYPE_NULLCONSTRAINT:
ParseFields(cx, obj, "");
if(!ParseFields(cx, obj, "")) return 0;
return new NullConstraint();
case TYPE_AVOIDAREACONSTRAINT:
if(!ParseFields(cx, obj, "i", &areaId)) return 0;
if(areaId <= 0 || areaId > theMap->areas.size()) return 0;
return new AvoidAreaConstraint(theMap->areas[areaId-1]);
case TYPE_AVOIDTERRAINCONSTRAINT:
if(!ParseFields(cx, obj, "s", &texture)) return 0;
return new AvoidTerrainConstraint(theMap->getId(texture));
case TYPE_ANDCONSTRAINT:
if(!ParseFields(cx, obj, "**", &jsv, &jsv2)) return 0;
if(!(c1 = ParseConstraint(cx, jsv))) return 0;
if(!(c2 = ParseConstraint(cx, jsv2))) return 0;
return new AndConstraint(c1, c2);
default:
return 0;
}
}
Terrain* ParseTerrain(JSContext* cx, jsval val) {
if(JSVAL_IS_STRING(val)) {
// simple terrains are just encoded as strings
string str = JS_GetStringBytes(JS_ValueToString(cx, val));
return SimpleTerrain::parse(str);
}
else {
// complex terrain type
JSObject* obj; int type;
if(!GetRaw(cx, val, &obj, &type)) return 0;
jsval jsv;
Terrain* terrain = 0;
vector<jsval> array;
vector<Terrain*> terrains;
switch(type) {
case TYPE_RANDOMTERRAIN:
if(!ParseFields(cx, obj, "*", &jsv)) return 0;
if(!ParseArray(cx, jsv, array)) return 0;
for(int i=0; i<array.size(); i++) {
terrain = ParseTerrain(cx, array[i]);
if(terrain==0) return 0;
terrains.push_back(terrain);
}
return new RandomTerrain(terrains);
default:
return 0;
}
return 0;
}
}

View File

@ -3,25 +3,38 @@
#include "map.h"
#include "entity.h"
#include "simpleconstraints.h"
#include "simplepainters.h"
#include "rectplacer.h"
#include "constraint.h"
#include "areapainter.h"
#include "areaplacer.h"
#include "centeredplacer.h"
#include "terrain.h"
// Object type constants
const int
TYPE_RECTPLACER = 1,
TYPE_TERRAINPAINTER = 2,
TYPE_NULLCONSTRAINT = 3;
TYPE_NULLCONSTRAINT = 3,
TYPE_RANDOMTERRAIN = 4,
TYPE_LAYEREDPAINTER = 5,
TYPE_AVOIDAREACONSTRAINT = 6,
TYPE_CLUMPPLACER = 7,
TYPE_EXACTPLACER = 8,
TYPE_AVOIDTERRAINCONSTRAINT = 9,
TYPE_ANDCONSTRAINT = 10,
TYPE_MULTIPLACER = 11;
// Helper functions to parse objects from array versions
JSObject* GetRaw(JSContext* cx, jsval val);
bool ParseFields(JSContext* cx, JSObject* array, const char* format, ...);
bool ParseArray(JSContext* cx, jsval val, std::vector<jsval>& ret);
AreaPainter* ParsePainter(JSContext* cx, jsval val);
AreaPlacer* ParsePlacer(JSContext* cx, jsval val);
Constraint* ParseConstraint(JSContext* cx, jsval val);
Terrain* ParseTerrain(JSContext* cx, jsval val);
CenteredPlacer* ParseCenteredPlacer(JSContext* cx, jsval val);
#endif

View File

@ -9,6 +9,16 @@ using namespace std;
typedef unsigned short u16;
typedef unsigned int u32;
void OutputEntity(Entity* e, ostringstream& xml) {
xml << "\
<Entity>\n\
<Template>" << e->type << "</Template>\n\
<Player>" << e->player << "</Player>\n\
<Position x=\"" << 4*e->x << "\" y=\"" << 4*e->y << "\" z=\"" << 4*e->z << "\" />\n\
<Orientation angle=\"" << e->orientation << "\" />\n\
</Entity>\n";
}
void OutputXml(Map* m, FILE* f) {
ostringstream xml;
xml << "\
@ -25,18 +35,20 @@ void OutputXml(Map* m, FILE* f) {
<Entities>\n";
for(int i=0; i<m->entities.size(); i++) {
Entity* e = m->entities[i];
xml << "\
<Entity>\n\
<Template>" << e->type << "</Template>\n\
<Player>" << e->player << "</Player>\n\
<Position x=\"" << 4*e->x << "\" y=\"" << 4*e->y << "\" z=\"" << 4*e->z << "\" />\n\
<Orientation angle=\"" << e->orientation << "\" />\n\
</Entity>\n";
OutputEntity(m->entities[i], xml);
}
for(int x=0; x<m->size; x++) {
for(int y=0; y<m->size; y++) {
vector<Entity*>& vec = m->terrainEntities[x][y];
for(int i=0; i<vec.size(); i++) {
OutputEntity(vec[i], xml);
}
}
}
xml << "\
</Entities>\
</Entities>\n\
<Nonentities />\n\
</Scenario>\n";
@ -44,7 +56,7 @@ void OutputXml(Map* m, FILE* f) {
}
struct Tile {
u16 texture1; // index into terrain_textures[]
u16 texture1; // index into texture_textures[]
u16 texture2; // index, or 0xFFFF for 'none'
u32 priority; // ???
};
@ -65,13 +77,13 @@ struct PMP {
u16 heightmap[(mapsize*16 + 1)^2]; // (squared, not xor) - vertex heights
u32 num_terrain_textures;
String terrain_textures[num_terrain_textures]; // filenames (no path), e.g. "cliff1.dds"
u32 num_texture_textures;
String texture_textures[num_texture_textures]; // filenames (no path), e.g. "cliff1.dds"
Tile tiles[(mapsize*16)^2];
};*/
int size = m->size;
int numTerrains = m->idToName.size();
int numTextures = m->idToName.size();
// header
fwrite("PSMP", sizeof(char), 4, f);
@ -104,25 +116,25 @@ struct PMP {
}
fwrite(heightmap, sizeof(u16), (size+1)*(size+1), f);
// num terrain textures
fwrite(&numTerrains, sizeof(u32), 1, f);
// num texture textures
fwrite(&numTextures, sizeof(u32), 1, f);
// terrain names
for(int i=0; i<numTerrains; i++) {
// texture names
for(int i=0; i<numTextures; i++) {
string fname = m->idToName[i] + ".dds";
int len = fname.length();
fwrite(&len, sizeof(u32), 1, f);
fwrite(fname.c_str(), sizeof(char), fname.length(), f);
}
// terrain; note that this is an array of 16x16 patches for some reason
// texture; note that this is an array of 16x16 patches for some reason
Tile* tiles = new Tile[size*size];
for(int x=0; x<size; x++) {
for(int y=0; y<size; y++) {
int patchX = x/16, patchY = y/16;
int offX = x%16, offY = y%16;
Tile& t = tiles[ (patchY*size/16 + patchX)*16*16 + (offY*16 + offX) ];
t.texture1 = m->terrain[x][y];
t.texture1 = m->texture[x][y];
t.texture2 = 0xFFFF;
t.priority = 0;
}

View File

@ -7,8 +7,6 @@
using namespace std;
const char* LIBRARY_FILE = "library.js";
JSRuntime *rt = 0;
JSContext *cx = 0;
JSObject *global = 0;
@ -58,8 +56,8 @@ jsval NewJSString(const string& str) {
return STRING_TO_JSVAL(JS_NewString(cx, buf, str.length()));
}
void ExecuteFile(const char* fileName) {
FILE* f = fopen(fileName, "r");
void ExecuteFile(const string& fileName) {
FILE* f = fopen(fileName.c_str(), "r");
if(!f) {
cerr << "Cannot open " << fileName << endl;
Shutdown(1);
@ -72,7 +70,7 @@ void ExecuteFile(const char* fileName) {
}
jsval rval;
JSBool ok = JS_EvaluateScript(cx, global, code.c_str(), code.length(), fileName, 1, &rval);
JSBool ok = JS_EvaluateScript(cx, global, code.c_str(), code.length(), fileName.c_str(), 1, &rval);
if(!ok) Shutdown(1);
}
@ -80,10 +78,16 @@ void ExecuteFile(const char* fileName) {
int main(int argc, char* argv[])
{
const string LIBRARY_FILE = "../data/mods/official/maps/rmlibrary.js";
const string RMS_PATH = "../data/mods/official/maps/random/";
const string SCENARIO_PATH = "../data/mods/official/maps/scenarios/";
clock_t start = clock();
InitJS();
if(argc!=3 && argc!=4) {
cerr << "Usage: rmgen <script> <output name without extension> [<seed>]" << endl;
cerr << "Usage: rmgen <script> <output map> [<seed>] (no file extensions)" << endl;
Shutdown(1);
}
@ -102,22 +106,26 @@ int main(int argc, char* argv[])
string setts = out.str();
jsval rval;
JSBool ok = JS_EvaluateScript(cx, global, setts.c_str(), setts.length(),
"settings declaration", 1, &rval);
"map settings script", 1, &rval);
if(!ok) Shutdown(1);
// Load library
ExecuteFile(LIBRARY_FILE);
// Run the script
ExecuteFile(argv[1]);
ExecuteFile(RMS_PATH + argv[1] + ".js");
if(!theMap) {
cerr << "Error:\n\tScript never called init!" << endl;
Shutdown(1);
}
string outputName = argv[2];
string outputName = SCENARIO_PATH + argv[2];
OutputMap(theMap, outputName);
clock_t end = clock();
printf("Took %0.3f seconds.\n", float(end-start) / CLOCKS_PER_SEC);
Shutdown(0);
}

View File

@ -61,14 +61,15 @@
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="Release"
OutputDirectory="../../../binaries/system"
IntermediateDirectory="Release"
ConfigurationType="1"
CharacterSet="2">
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories="../../../libraries/boost/include;../../../libraries/spidermonkey/include"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
RuntimeLibrary="4"
RuntimeLibrary="3"
UsePrecompiledHeader="3"
WarningLevel="3"
Detect64BitPortabilityProblems="TRUE"
@ -128,12 +129,18 @@
<File
RelativePath=".\centeredplacer.cpp">
</File>
<File
RelativePath=".\clumpplacer.cpp">
</File>
<File
RelativePath=".\constraint.cpp">
</File>
<File
RelativePath=".\entity.cpp">
</File>
<File
RelativePath=".\layeredpainter.cpp">
</File>
<File
RelativePath=".\map.cpp">
</File>
@ -161,6 +168,9 @@
<File
RelativePath=".\simplepainters.cpp">
</File>
<File
RelativePath=".\simpleplacers.cpp">
</File>
<File
RelativePath=".\stdafx.cpp">
<FileConfiguration
@ -176,6 +186,9 @@
UsePrecompiledHeader="1"/>
</FileConfiguration>
</File>
<File
RelativePath=".\terrain.cpp">
</File>
</Filter>
<Filter
Name="Header Files"
@ -196,12 +209,18 @@
<File
RelativePath=".\centeredplacer.h">
</File>
<File
RelativePath=".\clumpplacer.h">
</File>
<File
RelativePath=".\constraint.h">
</File>
<File
RelativePath=".\entity.h">
</File>
<File
RelativePath=".\layeredpainter.h">
</File>
<File
RelativePath=".\map.h">
</File>
@ -229,9 +248,15 @@
<File
RelativePath=".\simplepainters.h">
</File>
<File
RelativePath=".\simpleplacers.h">
</File>
<File
RelativePath=".\stdafx.h">
</File>
<File
RelativePath=".\terrain.h">
</File>
</Filter>
<Filter
Name="Resource Files"
@ -241,14 +266,14 @@
<File
RelativePath="..\..\..\libraries\spidermonkey\lib\js32d.lib">
</File>
<File
RelativePath=".\library.js">
</File>
<File
RelativePath=".\ReadMe.txt">
</File>
<File
RelativePath=".\test.js">
RelativePath="..\..\..\binaries\data\mods\official\maps\rmlibrary.js">
</File>
<File
RelativePath="..\..\..\binaries\data\mods\official\maps\random\test.js">
</File>
</Files>
<Globals>

View File

@ -1,7 +1,45 @@
#include "stdafx.h"
#include "simpleconstraints.h"
using namespace std;
// NullConstraint
bool NullConstraint::allows(Map* m, int x, int y)
{
return true;
}
// AvoidAreaConstraint
AvoidAreaConstraint::AvoidAreaConstraint(Area* area) {
this->area = area;
}
bool AvoidAreaConstraint::allows(Map* m, int x, int y)
{
return m->area[x][y] != area;
}
// AvoidTerrainConstraint
AvoidTerrainConstraint::AvoidTerrainConstraint(int textureId) {
this->textureId = textureId;
}
bool AvoidTerrainConstraint::allows(Map* m, int x, int y)
{
return m->texture[x][y] != textureId;
}
// AndConstraint
AndConstraint::AndConstraint(Constraint* a, Constraint*b) {
this->a = a;
this->b = b;
}
bool AndConstraint::allows(Map* m, int x, int y)
{
return a->allows(m,x,y) && b->allows(m,x,y);
}

View File

@ -3,10 +3,36 @@
#include "constraint.h"
#include "map.h"
#include "area.h"
class NullConstraint : public Constraint {
public:
virtual bool allows(Map* m, int x, int y);
};
class AvoidAreaConstraint : public Constraint {
private:
Area* area;
public:
AvoidAreaConstraint(Area* area);
virtual bool allows(Map* m, int x, int y);
};
class AvoidTerrainConstraint : public Constraint {
private:
int textureId;
public:
AvoidTerrainConstraint(int textureId);
virtual bool allows(Map* m, int x, int y);
};
class AndConstraint : public Constraint {
private:
Constraint* a;
Constraint* b;
public:
AndConstraint(Constraint* a, Constraint* b);
virtual bool allows(Map* m, int x, int y);
};
#endif

View File

@ -1,18 +1,21 @@
#include "stdafx.h"
#include "simplepainters.h"
#include "random.h"
#include "rmgen.h"
using namespace std;
TerrainPainter::TerrainPainter(const string& terrain)
// TerrainPainter
TerrainPainter::TerrainPainter(Terrain* terrain)
{
this->terrain = terrain;
}
void TerrainPainter::paint(Map* m, Area* a)
{
int id = m->getId(terrain);
for (int i=0; i<a->points.size(); i++) {
Point p = a->points[i];
m->terrain[p.x][p.y] = id;
terrain->place(m, p.x, p.y);
}
}

View File

@ -4,12 +4,13 @@
#include "areapainter.h"
#include "map.h"
#include "area.h"
#include "terrain.h"
class TerrainPainter : public AreaPainter {
public:
std::string terrain;
Terrain* terrain;
TerrainPainter(const std::string& terrain);
TerrainPainter(Terrain* terrain);
void paint(Map* m, Area* a);
};

View File

@ -0,0 +1,47 @@
#include "stdafx.h"
#include "simpleplacers.h"
#include "random.h"
using namespace std;
// ExactPlacer
ExactPlacer::ExactPlacer(CenteredPlacer* centeredPlacer, int x, int y) {
this->centeredPlacer = centeredPlacer;
this->x = x;
this->y = y;
}
ExactPlacer::~ExactPlacer() {
}
bool ExactPlacer::place(Map* m, Constraint* constr, std::vector<Point>& ret) {
return centeredPlacer->place(m, constr, ret, x, y);
}
// MultiPlacer
MultiPlacer::MultiPlacer(CenteredPlacer* centeredPlacer, int numToPlace, int maxFail) {
this->centeredPlacer = centeredPlacer;
this->numToPlace = numToPlace;
this->maxFail = maxFail;
}
MultiPlacer::~MultiPlacer() {
}
bool MultiPlacer::place(Map* m, Constraint* constr, std::vector<Point>& ret) {
int failed = 0;
int placed = 0;
while(placed < numToPlace && failed <= maxFail) {
int x = RandInt(m->size);
int y = RandInt(m->size);
if(constr->allows(m,x,y) && centeredPlacer->place(m, constr, ret, x, y)) {
placed++;
}
else {
failed++;
}
}
return placed == numToPlace;
}

View File

@ -0,0 +1,29 @@
#ifndef __SIMPLEPLACERS_H__
#define __SIMPLEPLACERS_H__
#include "areaplacer.h"
#include "centeredplacer.h"
#include "map.h"
#include "constraint.h"
class ExactPlacer: public AreaPlacer {
private:
CenteredPlacer* centeredPlacer;
int x, y;
public:
ExactPlacer(CenteredPlacer* centeredPlacer, int x, int y);
virtual bool place(Map* m, Constraint* constr, std::vector<Point>& ret);
~ExactPlacer();
};
class MultiPlacer: public AreaPlacer {
private:
CenteredPlacer* centeredPlacer;
int numToPlace, maxFail;
public:
MultiPlacer(CenteredPlacer* centeredPlacer, int numToPlace, int maxFail);
virtual bool place(Map* m, Constraint* constr, std::vector<Point>& ret);
~MultiPlacer();
};
#endif

View File

@ -32,6 +32,6 @@
#endif
#include "jsapi.h"
// TODO: reference additional headers your program requires here
const float PI = acos(-1.0f);
#endif

View File

@ -0,0 +1,88 @@
#include "stdafx.h"
#include "terrain.h"
#include "map.h"
#include "entity.h"
#include "random.h"
#include "rmgen.h"
using namespace std;
// Terrain
Terrain::Terrain() {}
Terrain::~Terrain() {}
void Terrain::place(Map* m, int x, int y) {
vector<Entity*>& vec = m->terrainEntities[x][y];
for(int i=0; i<vec.size(); i++) {
delete vec[i];
}
vec.clear();
placeNew(m, x, y);
}
// SimpleTerrain
SimpleTerrain::SimpleTerrain(const std::string& texture)
{
this->texture = texture;
this->treeType = "";
}
SimpleTerrain::SimpleTerrain(const std::string& texture, const std::string& treeType)
{
this->texture = texture;
this->treeType = treeType;
}
void SimpleTerrain::placeNew(Map* m, int x, int y) {
vector<Entity*>& vec = m->terrainEntities[x][y];
if(treeType != "") {
vec.push_back(new Entity(treeType, 0, x+0.5f, 0, y+0.5f, RandFloat()*PI));
}
m->texture[x][y] = m->getId(texture);
}
SimpleTerrain::~SimpleTerrain(void)
{
}
Terrain* SimpleTerrain::parse(const string& name) {
static map<string, Terrain*> parsedTerrains;
if(parsedTerrains.find(name) != parsedTerrains.end()) {
return parsedTerrains[name];
}
else {
string texture, treeType;
size_t pos = name.find('|');
if(pos != name.npos) {
texture = name.substr(0, pos);
treeType = name.substr(pos+1, name.size()-pos-1);
}
else {
texture = name;
treeType = "";
}
return parsedTerrains[name] = new SimpleTerrain(texture, treeType);
}
}
// RandomTerrain
RandomTerrain::RandomTerrain(const vector<Terrain*>& terrains)
{
if(terrains.size()==0) {
JS_ReportError(cx, "RandomTerrain: terrains array must not be empty");
}
this->terrains = terrains;
}
void RandomTerrain::placeNew(Map* m, int x, int y) {
terrains[RandInt(terrains.size())]->placeNew(m, x, y);
}
RandomTerrain::~RandomTerrain(void)
{
}

View File

@ -0,0 +1,44 @@
#ifndef __TERRAIN_H__
#define __TERRAIN_H__
class Terrain
{
public:
Terrain();
void place(class Map* m, int x, int y);
virtual void placeNew(class Map* m, int x, int y) = 0; // template method
virtual ~Terrain(void);
};
class SimpleTerrain: public Terrain
{
private:
std::string texture;
std::string treeType;
public:
SimpleTerrain(const std::string& texture);
SimpleTerrain(const std::string& texture, const std::string& treeType);
static Terrain* parse(const std::string& name);
void placeNew(class Map* m, int x, int y);
~SimpleTerrain(void);
};
class RandomTerrain: public Terrain
{
private:
std::vector<Terrain*> terrains;
public:
RandomTerrain(const std::vector<Terrain*>& terrains);
void placeNew(class Map* m, int x, int y);
~RandomTerrain(void);
};
extern std::map<std::string, Terrain*> parsedTerrains;
#endif

View File

@ -1,8 +0,0 @@
const SIZE = 48;
init(SIZE, "grass1_a", 0);
var placer = new RectPlacer(0,0,3,4);
placer.y2 = 6;
var paint = new TerrainPainter("snow");
var constr = new NullConstraint();
println(createArea(placer, paint));