Source: rmgen/Object.js

/**
 * @file An Object tries to find locations around a location and returns an array of Entity items holding the template names, owners and locations on success.
 */

/**
 * The SimpleObject attempts to find locations for a random amount of entities with a random distance to the given center.
 */
function SimpleObject(templateName, minCount, maxCount, minDistance, maxDistance, minAngle = 0, maxAngle = 2 * Math.PI, avoidDistance = 1)
{
	this.templateName = templateName;
	this.minCount = minCount;
	this.maxCount = maxCount;
	this.minDistance = minDistance;
	this.maxDistance = maxDistance;
	this.minAngle = minAngle;
	this.maxAngle = maxAngle;
	this.avoidDistanceSquared = Math.square(avoidDistance);

	if (minCount > maxCount)
		throw new Error("SimpleObject: minCount should be less than or equal to maxCount");

	if (minDistance > maxDistance)
		throw new Error("SimpleObject: minDistance should be less than or equal to maxDistance");

	if (minAngle > maxAngle)
		throw new Error("SimpleObject: minAngle should be less than or equal to maxAngle");
}

SimpleObject.prototype.place = function(centerPosition, playerID, avoidPositions, constraint, maxRetries)
{
	let entitySpecs = [];
	let numRetries = 0;
	let validTile = pos => this.templateName.startsWith(g_ActorPrefix) ? g_Map.validTile(pos) : g_Map.validTilePassable(pos);

	for (let i = 0; i < randIntInclusive(this.minCount, this.maxCount); ++i)
		while (true)
		{
			let distance = randFloat(this.minDistance, this.maxDistance);
			let angle = randomAngle();

			let position = Vector2D.sum([centerPosition, new Vector2D(0.5, 0.5), new Vector2D(distance, 0).rotate(-angle)]);

			if (validTile(position) &&
			    (!avoidPositions ||
			        entitySpecs.every(entSpec => entSpec.position.distanceToSquared(position) >= this.avoidDistanceSquared) &&
			        avoidPositions.every(avoid => avoid.position.distanceToSquared(position) >= Math.max(this.avoidDistanceSquared, avoid.distanceSquared))) &&
			    constraint.allows(position.clone().floor()))
			{
				entitySpecs.push({
					"templateName": this.templateName,
					"playerID": playerID,
					"position": position,
					"angle": randFloat(this.minAngle, this.maxAngle)
				});
				break;
			}

			if (numRetries++ > maxRetries)
				return undefined;
		}

	return entitySpecs;
};

/**
 * Same as SimpleObject, but choses one of the given templates at random.
 */
function RandomObject(templateNames, minCount, maxCount, minDistance, maxDistance, minAngle, maxAngle, avoidDistance = 1)
{
	this.templateNames = templateNames;
	this.minCount = minCount;
	this.maxCount = maxCount;
	this.minDistance = minDistance;
	this.maxDistance = maxDistance;
	this.minAngle = minAngle;
	this.maxAngle = maxAngle;
	this.avoidDistance = avoidDistance;
}

RandomObject.prototype.place = function(centerPosition, player, avoidPositions, constraint, maxRetries)
{
	return new SimpleObject(pickRandom(this.templateNames), this.minCount, this.maxCount, this.minDistance, this.maxDistance, this.minAngle, this.maxAngle, this.avoidDistance).place(
		centerPosition, player, avoidPositions, constraint, maxRetries);
};