1
0
forked from 0ad/0ad

- Added stay-in-tile-class and border-tile-class constraints.

- Made objects snap to terrain (the game does this automatically for
entities but it was a problem with actors)
- Small enhancements to the RMS API
- New version of cantabrian_highlands

This was SVN commit r2688.
This commit is contained in:
Matei 2005-09-08 01:20:51 +00:00
parent c428f510f6
commit 467b14e928
18 changed files with 205 additions and 74 deletions

View File

@ -22,11 +22,12 @@ JSFunctionSpec globalFunctions[] = {
{"setTexture", setTexture, 3},
{"getHeight", getHeight, 2},
{"setHeight", setHeight, 3},
{"getMapSize", getMapSize, 0},
{"randInt", randInt, 1},
{"randFloat", randFloat, 0},
{"placeObject", placeObject, 5},
{"placeObject", placeObject, 4},
{"createArea", createArea, 3},
{"createObjectGroup", createObjectGroup, 2},
{"createObjectGroup", createObjectGroup, 3},
{"createTileClass", createTileClass, 0},
{0, 0, 0}
};
@ -220,7 +221,7 @@ JSBool placeObject(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval
JS_ValueToNumber(cx, argv[3], &y);
JS_ValueToNumber(cx, argv[4], &orientation);
theMap->addObject(new Object(type, player, x,0,y, orientation));
theMap->addObject(new Object(type, player, x,y, orientation));
return JS_TRUE;
}
@ -284,7 +285,7 @@ JSBool createArea(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *
JSBool createObjectGroup(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
CheckInit(true, cx, __FUNCTION__);
if(argc != 1 && argc != 2) {
if(argc != 2 && argc != 3) {
JS_ReportError(cx, "createObjectGroup: expected 1 or 2 arguments but got %d", argc);
}
@ -294,16 +295,22 @@ JSBool createObjectGroup(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
if(!(placer = ParseObjectGroupPlacer(cx, argv[0]))) {
JS_ReportError(cx, "createObjectGroup: argument 1 must be a valid object group placer");
}
if(argc == 2) {
if(!(constr = ParseConstraint(cx, argv[1]))) {
JS_ReportError(cx, "createObjectGroup: argument 2 must be a valid constraint");
if(!JSVAL_IS_INT(argv[1])) {
JS_ReportError(cx, "createObjectGroup: argument 2 must be a valid player number");
}
if(argc == 3) {
if(!(constr = ParseConstraint(cx, argv[2]))) {
JS_ReportError(cx, "createObjectGroup: argument 3 must be a valid constraint");
}
}
else {
constr = new NullConstraint();
}
bool ret = theMap->createObjectGroup(placer, constr);
int player = JSVAL_TO_INT(argv[1]);
bool ret = theMap->createObjectGroup(placer, player, constr);
delete placer;
delete constr;
@ -324,3 +331,14 @@ JSBool createTileClass(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, js
*rval = INT_TO_JSVAL(id);
return JS_TRUE;
}
JSBool getMapSize(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
CheckInit(true, cx, __FUNCTION__);
if(argc != 0) {
JS_ReportError(cx, "getMapSize: expected 0 arguments but got %d", argc);
}
*rval = INT_TO_JSVAL(theMap->size);
return JS_TRUE;
}

View File

@ -21,6 +21,7 @@ JSBool getTexture(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *
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 getMapSize(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
// Low-level placement functions
JSBool placeTerrain(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);

View File

@ -18,11 +18,13 @@ ClumpPlacer::~ClumpPlacer()
{
}
bool ClumpPlacer::place(class Map* m, Constraint* constr, std::vector<Point>& ret) {
bool ClumpPlacer::place(class Map* m, Constraint* constr, std::vector<Point>& retVec) {
if(!m->validT(x, y) || !constr->allows(m, x, y)) {
return false;
}
set<Point> ret;
float radius = sqrt(size / PI);
float perim = 3 * radius * 2 * PI;
int intPerim = (int)(ceil(perim));
@ -71,7 +73,7 @@ bool ClumpPlacer::place(class Map* m, Constraint* constr, std::vector<Point>& re
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));
ret.insert(Point(i, j));
}
else {
failed++;
@ -81,6 +83,10 @@ bool ClumpPlacer::place(class Map* m, Constraint* constr, std::vector<Point>& re
}
}
for(set<Point>::iterator it = ret.begin(); it != ret.end(); it++) {
retVec.push_back(*it);
}
return failed > size * failFraction ? false : true;
/*if(m->validT(x,y)) {

View File

@ -39,7 +39,7 @@ bool GetIntField(JSContext* cx, jsval obj, const char* name, int& ret) {
return true;
}
bool GetBoolField(JSContext* cx, jsval obj, const char* name, int& ret) {
bool GetBoolField(JSContext* cx, jsval obj, const char* name, bool& ret) {
jsval val;
if(!GetJsvalField(cx, obj, name, val)) return false;
if(!JSVAL_IS_BOOLEAN(val)) return false;
@ -188,6 +188,7 @@ ObjectGroupPlacer* ParseObjectGroupPlacer(JSContext* cx, jsval val) {
jsval jsv;
vector<jsval> array;
int x, y;
bool avoidSelf;
TileClass* tileClass;
vector<SimpleGroup::Element*> elements;
@ -196,6 +197,7 @@ ObjectGroupPlacer* ParseObjectGroupPlacer(JSContext* cx, jsval val) {
// convert x and y
if(!GetIntField(cx, val, "x", x)) return 0;
if(!GetIntField(cx, val, "y", y)) return 0;
if(!GetBoolField(cx, val, "avoidSelf", avoidSelf)) return 0;
if(!GetTileClassField(cx, val, "tileClass", tileClass)) return 0;
// convert the elements (which will be JS SimpleElement objects)
if(!GetJsvalField(cx, val, "elements", jsv)) return 0;
@ -210,7 +212,7 @@ ObjectGroupPlacer* ParseObjectGroupPlacer(JSContext* cx, jsval val) {
if(!GetFloatField(cx, array[i], "distance", distance)) return 0;
elements[i] = new SimpleGroup::Element(type, count, distance);
}
return new SimpleGroup(elements, tileClass, x, y);
return new SimpleGroup(elements, tileClass, avoidSelf, x, y);
default:
return 0;
@ -222,6 +224,7 @@ Constraint* ParseConstraint(JSContext* cx, jsval val) {
int areaId;
TileClass* tileClass;
float distance;
float distance2;
string texture;
jsval jsv, jsv2;
@ -258,6 +261,17 @@ Constraint* ParseConstraint(JSContext* cx, jsval val) {
if(!GetTileClassField(cx, val, "tileClass", tileClass)) return 0;
return new AvoidTileClassConstraint(tileClass, distance);
case TYPE_STAY_IN_TILE_CLASS_CONSTRAINT:
if(!GetFloatField(cx, val, "distance", distance)) return 0;
if(!GetTileClassField(cx, val, "tileClass", tileClass)) return 0;
return new StayInTileClassConstraint(tileClass, distance);
case TYPE_BORDER_TILE_CLASS_CONSTRAINT:
if(!GetFloatField(cx, val, "distanceInside", distance)) return 0;
if(!GetFloatField(cx, val, "distanceOutside", distance2)) return 0;
if(!GetTileClassField(cx, val, "tileClass", tileClass)) return 0;
return new BorderTileClassConstraint(tileClass, distance, distance2);
default:
return 0;
}

View File

@ -22,7 +22,9 @@ const int
TYPE_SMOOTH_ELEVATION_PAINTER = 9,
TYPE_SIMPLE_GROUP = 10,
TYPE_AVOID_TILE_CLASS_CONSTRAINT = 11,
TYPE_TILE_CLASS_PAINTER = 12;
TYPE_TILE_CLASS_PAINTER = 12,
TYPE_STAY_IN_TILE_CLASS_CONSTRAINT = 13,
TYPE_BORDER_TILE_CLASS_CONSTRAINT = 14;
// Helper functions to convert objects from JS versions

View File

@ -279,8 +279,8 @@ Area* Map::createArea(AreaPlacer* placer, AreaPainter* painter, Constraint* cons
return a;
}
bool Map::createObjectGroup(ObjectGroupPlacer* placer, Constraint* constr) {
return placer->place(this, constr);
bool Map::createObjectGroup(ObjectGroupPlacer* placer, int player, Constraint* constr) {
return placer->place(this, player, constr);
}
int Map::createTileClass() {
@ -288,3 +288,18 @@ int Map::createTileClass() {
return tileClasses.size();
}
float Map::getExactHeight(float x, float y) {
// copied & modified from ScEd
int xi = min((int) floor(x), size);
int yi = min((int) floor(y), size);
float xf = x - xi;
float yf = y - yi;
float h00 = height[xi][yi];
float h01 = height[xi][yi+1];
float h10 = height[xi+1][yi];
float h11 = height[xi+1][yi+1];
return ( 1 - yf ) * ( ( 1 - xf ) * h00 + xf * h10 ) + yf * ( ( 1 - xf ) * h01 + xf * h11 ) ;
}

View File

@ -46,9 +46,11 @@ public:
void addObject(class Object* ent);
Area* createArea(AreaPlacer* placer, AreaPainter* painter, Constraint* constr);
bool createObjectGroup(ObjectGroupPlacer* placer, Constraint* constr);
bool createObjectGroup(ObjectGroupPlacer* placer, int player, Constraint* constr);
int createTileClass(); // returns ID of the new class
float getExactHeight(float x, float y); // get height taking into account terrain curvature
};
#endif

View File

@ -6,12 +6,11 @@ using namespace std;
Object::Object() {
}
Object::Object(const string& name, int player, float x, float y, float z, float orientation) {
Object::Object(const string& name, int player, float x, float y, float orientation) {
this->name = name;
this->player = player;
this->x = x;
this->y = y;
this->z = z;
this->orientation = orientation;
}

View File

@ -5,11 +5,11 @@ class Object {
public:
std::string name; // "template" field for objects, "actor" field for nonobjects
int player; // -1 for nonobjects
float x, y, z;
float x, y;
float orientation;
Object();
Object(const std::string& name, int player, float x, float y, float z, float orientation);
Object(const std::string& name, int player, float x, float y, float orientation);
bool isEntity();
};

View File

@ -7,7 +7,7 @@
class ObjectGroupPlacer
{
public:
virtual bool place(class Map* m, Constraint* constr) = 0;
virtual bool place(class Map* m, int player, Constraint* constr) = 0;
ObjectGroupPlacer(void);
virtual ~ObjectGroupPlacer(void);

View File

@ -7,13 +7,15 @@
using namespace std;
void OutputObject(Object* e, ostringstream& xml) {
void OutputObject(Map* m, Object* e, ostringstream& xml) {
float height = m->getExactHeight(e->x, e->y);
if(e->isEntity()) {
xml << "\
<Entity>\n\
<Template>" << e->name << "</Template>\n\
<Player>" << e->player << "</Player>\n\
<Position x=\"" << 4*e->x << "\" y=\"" << 4*e->y << "\" z=\"" << 4*e->z << "\" />\n\
<Position x=\"" << 4*e->x << "\" y=\"" << height << "\" z=\"" << 4*e->y << "\" />\n\
<Orientation angle=\"" << e->orientation << "\" />\n\
</Entity>\n";
}
@ -21,7 +23,7 @@ void OutputObject(Object* e, ostringstream& xml) {
xml << "\
<Nonentity>\n\
<Actor>" << e->name << "</Actor>\n\
<Position x=\"" << 4*e->x << "\" y=\"" << 4*e->y << "\" z=\"" << 4*e->z << "\" />\n\
<Position x=\"" << 4*e->x << "\" y=\"" << height << "\" z=\"" << 4*e->y << "\" />\n\
<Orientation angle=\"" << e->orientation << "\" />\n\
</Nonentity>\n";
}
@ -30,7 +32,7 @@ void OutputObject(Object* e, ostringstream& xml) {
void OutputObjects(ostringstream& xml, Map* m, bool entities) {
for(int i=0; i<m->objects.size(); i++) {
if(m->objects[i]->isEntity() == entities) {
OutputObject(m->objects[i], xml);
OutputObject(m, m->objects[i], xml);
}
}
@ -39,7 +41,7 @@ void OutputObjects(ostringstream& xml, Map* m, bool entities) {
vector<Object*>& vec = m->terrainObjects[x][y];
for(int i=0; i<vec.size(); i++) {
if(vec[i]->isEntity() == entities) {
OutputObject(vec[i], xml);
OutputObject(m, vec[i], xml);
}
}
}

View File

@ -71,8 +71,37 @@ AvoidTileClassConstraint::AvoidTileClassConstraint(TileClass* tileClass, float d
bool AvoidTileClassConstraint::allows(Map* m, int x, int y)
{
return !tileClass->hasTilesInRadius(x, y, distance);
return tileClass->countMembersInRadius(x, y, distance) == 0;
}
// StayInTileClassConstraint /////////////////////////////////////////////////////////////
StayInTileClassConstraint::StayInTileClassConstraint(TileClass* tileClass, float distance) {
this->tileClass = tileClass;
this->distance = distance;
}
bool StayInTileClassConstraint::allows(Map* m, int x, int y)
{
return tileClass->countNonMembersInRadius(x, y, distance) == 0;
}
// BorderTileClassConstraint /////////////////////////////////////////////////////////////
BorderTileClassConstraint::BorderTileClassConstraint(TileClass* tileClass, float distanceInside,
float distanceOutside) {
this->tileClass = tileClass;
this->distanceInside = distanceInside;
this->distanceOutside = distanceOutside;
}
bool BorderTileClassConstraint::allows(Map* m, int x, int y)
{
return tileClass->countMembersInRadius(x, y, distanceOutside) > 0
&& tileClass->countNonMembersInRadius(x, y, distanceInside) > 0;
}

View File

@ -45,5 +45,23 @@ public:
virtual bool allows(Map* m, int x, int y);
};
class StayInTileClassConstraint : public Constraint {
private:
TileClass* tileClass;
float distance;
public:
StayInTileClassConstraint(TileClass* tileClass, float distance);
virtual bool allows(Map* m, int x, int y);
};
class BorderTileClassConstraint : public Constraint {
private:
TileClass* tileClass;
float distanceInside;
float distanceOutside;
public:
BorderTileClassConstraint(TileClass* tileClass, float distanceInside, float distanceOutside);
virtual bool allows(Map* m, int x, int y);
};
#endif

View File

@ -17,34 +17,50 @@ SimpleGroup::Element::Element(const std::string& t, int c, float d):
SimpleGroup::Element::~Element() {
}
bool SimpleGroup::Element::place(int cx, int cy, Map* m, Constraint* constr, vector<Object*>& ret) {
bool SimpleGroup::Element::place(int cx, int cy, Map* m, int player, bool avoidSelf,
Constraint* constr, vector<Object*>& ret) {
int failCount = 0;
for(int i=0; i<count; i++) {
while(true) {
float ang = RandFloat()*2*PI;
float x = cx + distance*cos(ang) + 0.5f;
float y = cy + distance*sin(ang) + 0.5f;
int ix = (int) x;
int iy = (int) y;
if(m->validT(ix, iy) && constr->allows(m, ix, iy)) {
ret.push_back(new Object(type, 0, x, 0, y, RandFloat()*2*PI));
break;
float x = cx + 0.5f + distance*cos(ang);
float y = cy + 0.5f + distance*sin(ang);
if(x<0 || y<0 || x>m->size || y>m->size) {
goto bad;
}
else {
failCount++;
if(failCount > 20) {
return false;
if(avoidSelf) {
for(int i=0; i<ret.size(); i++) {
float dx = x - ret[i]->x;
float dy = y - ret[i]->y;
if(dx*dx + dy*dy < 1) {
goto bad;
}
}
}
if(!constr->allows(m, (int)x, (int)y)) {
goto bad;
}
// if we got here, we're good
ret.push_back(new Object(type, player, x, y, RandFloat()*2*PI));
break;
bad: failCount++;
if(failCount > 20) {
return false;
}
}
}
return true;
}
bool SimpleGroup::place(Map* m, Constraint* constr) {
bool SimpleGroup::place(Map* m, int player, Constraint* constr) {
vector<Object*> ret;
for(int i=0; i<elements.size(); i++) {
if(!elements[i]->place(x, y, m, constr, ret)) {
if(!elements[i]->place(x, y, m, player, avoidSelf, constr, ret)) {
return false;
}
}
@ -57,8 +73,8 @@ bool SimpleGroup::place(Map* m, Constraint* constr) {
return true;
}
SimpleGroup::SimpleGroup(vector<SimpleGroup::Element*>& e, TileClass* tc, int _x, int _y):
elements(e), x(_x), y(_y), tileClass(tc)
SimpleGroup::SimpleGroup(vector<SimpleGroup::Element*>& e, TileClass* tc, bool as, int _x, int _y):
elements(e), x(_x), y(_y), tileClass(tc), avoidSelf(as)
{
}

View File

@ -17,16 +17,18 @@ public:
Element::Element(const std::string& type, int count, float distance);
Element::~Element();
bool place(int cx, int cy, class Map* m, Constraint* constr, std::vector<Object*>& ret);
bool place(int cx, int cy, class Map* m, int player, bool avoidSelf,
Constraint* constr, std::vector<Object*>& ret);
};
std::vector<Element*> elements;
bool avoidSelf;
int x, y;
TileClass* tileClass;
virtual bool place(class Map* m, Constraint* constr);
virtual bool place(class Map* m, int player, Constraint* constr);
SimpleGroup(std::vector<Element*>& elements, TileClass* tileClass, int x, int y);
SimpleGroup(std::vector<Element*>& elements, TileClass* tileClass, bool avoidSelf, int x, int y);
virtual ~SimpleGroup(void);
};

View File

@ -39,7 +39,7 @@ SimpleTerrain::SimpleTerrain(const std::string& texture, const std::string& tree
void SimpleTerrain::placeNew(Map* m, int x, int y) {
vector<Object*>& vec = m->terrainObjects[x][y];
if(treeType != "") {
vec.push_back(new Object(treeType, 0, x+0.5f, 0, y+0.5f, RandFloat()*PI));
vec.push_back(new Object(treeType, 0, x+0.5f, y+0.5f, RandFloat()*PI));
}
m->texture[x][y] = m->getId(texture);
}

View File

@ -5,45 +5,51 @@ using namespace std;
TileClass::TileClass(int mapSize) {
this->mapSize = mapSize;
inclusionCount = new int*[mapSize];
for(int i=0; i<mapSize; i++) {
inclusionCount[i] = new int[mapSize];
memset(inclusionCount[i], 0, mapSize*sizeof(int));
rc.push_back(new RangeCount(mapSize));
}
}
TileClass::~TileClass() {
for(int i=0; i<mapSize; i++) {
delete inclusionCount[i];
delete rc[i];
}
delete inclusionCount;
}
void TileClass::add(int x, int y) {
rc[y]->add(x, 1);
tiles.insert(Point(x, y));
if(!inclusionCount[x][y]) {
rc[y]->add(x,1);
}
inclusionCount[x][y]++;
}
void TileClass::remove(int x, int y) {
rc[y]->add(x, -1);
if(rc[y]->get(x) == 0) {
tiles.erase(Point(x, y));
inclusionCount[x][y]--;
if(!inclusionCount[x][y]) {
rc[y]->add(x, -1);
}
}
bool TileClass::hasTilesInRadius(float cx, float cy, float r) {
// special check for really small classes
if(tiles.size() < 4*r) {
for(set<Point>::iterator it = tiles.begin(); it != tiles.end(); it++) {
Point p = *it;
float dx = p.x - cx;
float dy = p.y - cy;
if(dx*dx + dy*dy <= r*r) {
return true;
}
}
int TileClass::countMembersInRadius(float cx, float cy, float r) {
int mem, nonMem;
countInRadius(cx, cy, r, mem, nonMem);
return mem;
}
return false;
}
int TileClass::countNonMembersInRadius(float cx, float cy, float r) {
int mem, nonMem;
countInRadius(cx, cy, r, mem, nonMem);
return nonMem;
}
void TileClass::countInRadius(float cx, float cy, float r, int& members, int& nonMembers) {
members = 0;
nonMembers = 0;
for(float y = cy-r; y <= cy+r; y++) {
int iy = (int) y;
@ -57,10 +63,9 @@ bool TileClass::hasTilesInRadius(float cx, float cy, float r) {
float x2 = cx + dx;
int minX = max(0, (int) x1);
int maxX = min(mapSize-1, (int) x2);
if(rc[iy]->get(minX, maxX+1) > 0) {
return true;
}
int total = maxX - minX + 1;
int mem = rc[iy]->get(minX, maxX+1);
members += mem;
nonMembers += total - mem;
}
return false;
}

View File

@ -13,7 +13,7 @@ class TileClass {
private:
int mapSize;
std::vector<RangeCount*> rc; // range count on each row
std::set<Point> tiles; // the distinct tiles in the class
int** inclusionCount; // the inclusion count for each tile
public:
TileClass(int mapSize);
~TileClass();
@ -21,7 +21,9 @@ public:
void add(int x, int y);
void remove(int x, int y);
bool hasTilesInRadius(float cx, float cy, float r);
void countInRadius(float cx, float cy, float r, int& members, int& nonMembers);
int countMembersInRadius(float cx, float cy, float r);
int countNonMembersInRadius(float cx, float cy, float r);
};
#endif