So, there's this promising command selectBestPlaces, but once more the documentation is barely useable. The search here didn't yield a single hit either...
But fortunately I've scanned config.cpp some time ago, which enabled me to get some idea what expression in this context could mean, so excuse the little digression:
cost/probability expressions in config.cpp
There is the subclass Ambient in config.cpp containing definitions of all the small animals, and an - tada - expression (named "cost" here) that defines, where and when these animals may apear (all guesswork of course, so don't take this for granted).
The cost for these is a simple function (or expression) returning scalar based on simple arithmetic and some interesting keywords serving as variables of the sampled positions. The higher the output of this function, the more likely is it, that such an animal gets spawned at the sampling position. (plus there are again subclasses of these, where the cost is usually set to 1, while we have another expression for "probability" where `more exclusive?` keywords are beeing used...)
The keywords used in config.cpp are:
- forest
- trees
- meadow
- hills
- houses
- sea
- night
- rain
- windy
- deadBody
Each of theses variable in an expression will be replaced by the actual sampling value at the sample position of this factor, ranging from 0 to 1, where 0 means not at all and 1 means totally! (which allows for easy arithmetic combination of multiple factors)
Thus we can mix factors from three major different dimensions: the geographic dimension, time and the weather. (plus the deadBody thing, which looks like a "bonus" dimension implemented especially for the flies)
Some short examples:
- deep forest: "(1 + forest + trees) * (1 - sea) * (1 - houses)"
- tree, but not in forest: "(1 - forest) * (1 + trees)"
- hill, no forest: "(1 - forest) * (1 + hills) * (1 - sea)"
- house on hill: "(1 + houses) * (1 + hills)"
- ...
This is how leaves get spawned where trees are near, while the game spawns rocks where hills are located (clutter particles). And this is how the HouseFly finds deadBody, DragonFlys the forest and ButterFly the meadow, while the birds disapear in the night or in the rain.
Some examples, taken from config.cpp:
Code:
class BigBirds {
radius = 300;
cost = "((1 + forest + trees) - ((2 * rain)) - houses) * (1 - night) * (1 - sea)";
class Species {
class Hawk {
probability = 0.200000;
cost = 1;
};
};
};
class BigInsects {
radius = 20;
cost = "(5 - (2 * houses)) * (1 - night) * (1 - rain) * (1 - sea) * (1 - windy)";
class Species {
class DragonFly {
probability = "0.6 - (meadow * 0.5) + (forest * 0.4)";
cost = 1;
};
class ButterFly {
probability = "0.4 + (meadow * 0.5) - (forest * 0.4)";
cost = 1;
};
};
};
class SmallInsects {
radius = 3;
cost = "(12 - 8 * hills) * (1 - night) * (1 - rain) * (1 - sea) * (1 - windy)";
class Species {
class HouseFly {
probability = "deadBody + (1 - deadBody) * (0.5 - forest * 0.1 - meadow * 0.2)";
cost = 1;
};
class HoneyBee {
probability = "(1 - deadBody) * (0.5 - forest * 0.1 + meadow * 0.2)";
cost = 1;
};
class Mosquito {
probability = "(1 - deadBody) * (0.2 * forest)";
cost = 1;
};
};
};
class WindClutter {
radius = 10;
cost = "((20 - 5 * rain) * (3 * (windy factor [0.2, 0.5]))) * (1 - sea)";
class Species {
class FxWindGrass1 {
probability = "0.4 - 0.2 * hills - 0.2 * trees";
cost = 1;
};
class FxWindGrass2 {
probability = "0.4 - 0.2 * hills - 0.2 * trees";
cost = 1;
};
class FxWindRock1 {
probability = "0.4 * hills";
cost = 1;
};
class FxCrWindLeaf1 {
probability = "0.2 * trees";
cost = 1;
};
class FxCrWindLeaf2 {
probability = "0.1 * trees + 0.2";
cost = 1;
};
class FxCrWindLeaf3 {
probability = "0.1 * trees";
cost = 1;
};
};
};
...you get the idea. And as it turns out, these config entries (cost/probability) will be feed to exactly this selectBestPlaces command.
selectBestPlaces
Thus, I may (with some surety, based on my experiments) complete the documentation for this command:
Code:
selectBestPlaces [_position,_radius,_expression,_precision,_sourcesCount]
Parameters:
- _position: sample position (2d position seems to be sufficient, so no ASL position needed)
- _radius: defines the area to find the positions in (scalar in meter)
- _expression: cost function (string, not code!), calculated for every sampling position returning a positive number. The higher (positive) the value, the better. Zero means that the expression does not apply. The expression can rely on any of the variables in: [forest, trees, meadow, hills, houses, sea, night, rain, windy, deadBody], maybe more.
- _precision: sample precision/ radius of the sample position (scalar, maybe in meter, but for sure this is no 0 to 1 range). Given a precision of zero, your game will probably halt for some time, most likely for ever for you basically try to divide by zero. I guess we divide the area by this sampling area the precision/radius defines. With a very low value, you may get a lot of "bestPlaces" very near to each other - the precision as min. probably. With a greater value the returned positions are much further apart from each other, though it looks like the min. distance isn't exactly the given precision interpreted as radius... But you get the idea.
- _sourcesCount: number of best places returned (integer)
Returns:
No, you won't get an array of positions. You will get an empty array in case no best places were found (I guess this is true, when all "costs" where exactly zero) OR an array of "bestPlaces", where "bestPlaces" is an array of:
- _this select 0: position
- _this select 1: cost (expression result at this position)
Now be carefull with the sampling radius and precision because selectBestPlaces gets computed in one go and doesn't suffer from any maximum runtime-cap each frame. Thus if you're precision is too small for a too large sampling area, you may freeze the game for several seconds! (too many samples taken, plus all these samples need to be sorted too, to return the "best" ones, of course..)
Oh, after a first unsuccessfull regex search for an official example, I actually do have found just some, all from the animal module (of course, DO!), in Animals_main.fsm:
Code:
_root = configFile >> "CfgVehicles" >> "Goat";
_favouritezones = getText ( _root >> "favouritezones");
_randrefpoint = [(getpos player select 0) +Random(2*_SpawnRadius) -_SpawnRadius,(getpos player select 1) +Random(2*_SpawnRadius) -_SpawnRadius];
_PosList = selectbestplaces [_randrefpoint,_FindPlaceRadius,_favouritezones,10,5];
_PosSelect = _PosList select (floor random (count _PosList));
_Pos = _PosSelect select 0;
So there you go. Happy selecting best places. I imagine this is an elegant way to solve similar problems of finding appropriate places randomly.
And in case some developer pops in, I'd like to know if I've mention all of the available sampling-variables? What's really the meaning of precision? Or anything else, that I got wrong... so once we have the facts straight, we may complete the wiki about it..