diff --git a/binaries/data/mods/official/maps/random/test.js b/binaries/data/mods/official/maps/random/test.js new file mode 100644 index 0000000000..10acab5bfd --- /dev/null +++ b/binaries/data/mods/official/maps/random/test.js @@ -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 +)); diff --git a/binaries/data/mods/official/maps/rmlibrary.js b/binaries/data/mods/official/maps/rmlibrary.js new file mode 100644 index 0000000000..54c706d537 --- /dev/null +++ b/binaries/data/mods/official/maps/rmlibrary.js @@ -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; igetTerrain(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 { diff --git a/source/tools/rmgen/api.h b/source/tools/rmgen/api.h index 6106bb4023..d25717b6c4 100644 --- a/source/tools/rmgen/api.h +++ b/source/tools/rmgen/api.h @@ -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); diff --git a/source/tools/rmgen/centeredplacer.h b/source/tools/rmgen/centeredplacer.h index f3a45b5ddb..4ac88d312f 100644 --- a/source/tools/rmgen/centeredplacer.h +++ b/source/tools/rmgen/centeredplacer.h @@ -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& ret, int x, int y) = 0; + virtual bool place(Map* m, Constraint* constr, std::vector& ret, int x, int y) = 0; CenteredPlacer(void); virtual ~CenteredPlacer(void); diff --git a/source/tools/rmgen/clumpplacer.cpp b/source/tools/rmgen/clumpplacer.cpp new file mode 100644 index 0000000000..3ada85764d --- /dev/null +++ b/source/tools/rmgen/clumpplacer.cpp @@ -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& ret, int x, int y) { + float radius = sqrt(size / PI); + float perim = 3 * radius * 2 * PI; + int intPerim = (int)(ceil(perim)); + vector 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 ctrlCoords(ctrlPts+1); + vector ctrlVals(ctrlPts+1); + for(int i=0; ivalidT(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;*/ +} \ No newline at end of file diff --git a/source/tools/rmgen/clumpplacer.h b/source/tools/rmgen/clumpplacer.h new file mode 100644 index 0000000000..96aa7e319d --- /dev/null +++ b/source/tools/rmgen/clumpplacer.h @@ -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& ret, int x, int y); + + ClumpPlacer(float size, float coherence, float smoothness); + virtual ~ClumpPlacer(); +}; + +#endif \ No newline at end of file diff --git a/source/tools/rmgen/layeredpainter.cpp b/source/tools/rmgen/layeredpainter.cpp new file mode 100644 index 0000000000..51eb488156 --- /dev/null +++ b/source/tools/rmgen/layeredpainter.cpp @@ -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& ts, const vector& ws) +{ + terrains = ts; + widths = ws; +} + +LayeredPainter::~LayeredPainter(void) +{ +} + +void LayeredPainter::paint(Map* m, Area* a) { + map saw; + map dist; + + queue q; + + // push edge points + vector& pts = a->points; + for(int i=0; ivalidT(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=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); + } + } + } + } +} \ No newline at end of file diff --git a/source/tools/rmgen/layeredpainter.h b/source/tools/rmgen/layeredpainter.h new file mode 100644 index 0000000000..c553d33f0b --- /dev/null +++ b/source/tools/rmgen/layeredpainter.h @@ -0,0 +1,20 @@ +#ifndef __LAYEREDPAINTER_H__ +#define __LAYEREDPAINTER_H__ + +#include "areapainter.h" +#include "terrain.h" + +class LayeredPainter : + public AreaPainter +{ +private: + std::vector terrains; + std::vector widths; +public: + LayeredPainter(const std::vector& terrains, const std::vector& widths); + ~LayeredPainter(void); + + virtual void paint(class Map* m, class Area* a); +}; + +#endif \ No newline at end of file diff --git a/source/tools/rmgen/library.js b/source/tools/rmgen/library.js deleted file mode 100644 index 648e2630d2..0000000000 --- a/source/tools/rmgen/library.js +++ /dev/null @@ -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]; - } -} \ No newline at end of file diff --git a/source/tools/rmgen/map.cpp b/source/tools/rmgen/map.cpp index a75feed817..f47ee2c29f 100644 --- a/source/tools/rmgen/map.cpp +++ b/source/tools/rmgen/map.cpp @@ -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]; + for(int i=0; i[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; iplace(this, i, j); + } + } } Map::~Map() { for(int i=0; i=0 && y>=0 && xplace(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; } \ No newline at end of file diff --git a/source/tools/rmgen/map.h b/source/tools/rmgen/map.h index 9e0b929402..d50a49ac93 100644 --- a/source/tools/rmgen/map.h +++ b/source/tools/rmgen/map.h @@ -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** terrainEntities; float** height; Area*** area; std::map nameToId; @@ -18,20 +20,22 @@ public: std::vector entities; std::vector 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); diff --git a/source/tools/rmgen/objparse.cpp b/source/tools/rmgen/objparse.cpp index 80fa32c3ad..e92d9949b0 100644 --- a/source/tools/rmgen/objparse.cpp +++ b/source/tools/rmgen/objparse.cpp @@ -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& 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 array; + vector terrains; + vector 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 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 array; + vector 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& 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 \ No newline at end of file diff --git a/source/tools/rmgen/output.cpp b/source/tools/rmgen/output.cpp index ed7c07e91b..429d5d4d79 100644 --- a/source/tools/rmgen/output.cpp +++ b/source/tools/rmgen/output.cpp @@ -9,6 +9,16 @@ using namespace std; typedef unsigned short u16; typedef unsigned int u32; +void OutputEntity(Entity* e, ostringstream& xml) { + xml << "\ + \n\ + \n\ + " << e->player << "\n\ + x << "\" y=\"" << 4*e->y << "\" z=\"" << 4*e->z << "\" />\n\ + orientation << "\" />\n\ + \n"; +} + void OutputXml(Map* m, FILE* f) { ostringstream xml; xml << "\ @@ -25,18 +35,20 @@ void OutputXml(Map* m, FILE* f) { \n"; for(int i=0; ientities.size(); i++) { - Entity* e = m->entities[i]; - xml << "\ - \n\ - \n\ - " << e->player << "\n\ - x << "\" y=\"" << 4*e->y << "\" z=\"" << 4*e->z << "\" />\n\ - orientation << "\" />\n\ - \n"; + OutputEntity(m->entities[i], xml); + } + + for(int x=0; xsize; x++) { + for(int y=0; ysize; y++) { + vector& vec = m->terrainEntities[x][y]; + for(int i=0; i\ + \n\ \n\ \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; iidToName[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; xterrain[x][y]; + t.texture1 = m->texture[x][y]; t.texture2 = 0xFFFF; t.priority = 0; } diff --git a/source/tools/rmgen/rmgen.cpp b/source/tools/rmgen/rmgen.cpp index e3bb1a6389..9ea97f1778 100644 --- a/source/tools/rmgen/rmgen.cpp +++ b/source/tools/rmgen/rmgen.cpp @@ -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