| 1 | /**
|
|---|
| 2 | * @param {Array} templates - Each Item is an Object that contains the properties "templateName" and optionally "margin," "constraints" and "painters".
|
|---|
| 3 | */
|
|---|
| 4 | function CityPainter(templates, angle, playerID)
|
|---|
| 5 | {
|
|---|
| 6 | this.angle = angle;
|
|---|
| 7 | this.playerID = playerID;
|
|---|
| 8 | this.templates = templates.map(template => {
|
|---|
| 9 |
|
|---|
| 10 | const obstructionSize = getObstructionSize(template.templateName, template.margin || 0);
|
|---|
| 11 | return {
|
|---|
| 12 | "templateName": template.templateName,
|
|---|
| 13 | "maxCount": template.maxCount !== undefined ? template.maxCount : Infinity,
|
|---|
| 14 | "constraint": template.constraints && new AndConstraint(template.constraints),
|
|---|
| 15 | "painter": template.painters && new MultiPainter(template.painters),
|
|---|
| 16 | "obstructionCorners": [
|
|---|
| 17 | new Vector2D(0, 0),
|
|---|
| 18 | new Vector2D(obstructionSize.x, 0),
|
|---|
| 19 | new Vector2D(0, obstructionSize.y),
|
|---|
| 20 | obstructionSize
|
|---|
| 21 | ]
|
|---|
| 22 | };
|
|---|
| 23 | });
|
|---|
| 24 | }
|
|---|
| 25 |
|
|---|
| 26 | CityPainter.prototype.paint = function(area)
|
|---|
| 27 | {
|
|---|
| 28 | let templates = this.templates;
|
|---|
| 29 |
|
|---|
| 30 | const templateCounts = {};
|
|---|
| 31 | for (const template of this.templates)
|
|---|
| 32 | templateCounts[template.templateName] = 0;
|
|---|
| 33 |
|
|---|
| 34 | const mapCenter = g_Map.getCenter();
|
|---|
| 35 | const mapSize = g_Map.getSize();
|
|---|
| 36 |
|
|---|
| 37 | // TODO: Due to the rounding, this is wasting a lot of space.
|
|---|
| 38 | // The city would be much denser if it would test for actual shape intersection or
|
|---|
| 39 | // if it would use a custom, more fine-grained obstruction grid
|
|---|
| 40 | const tileClass = g_Map.createTileClass();
|
|---|
| 41 |
|
|---|
| 42 | const processed = new Array(mapSize).fill(0).map(() => new Uint8Array(mapSize));
|
|---|
| 43 |
|
|---|
| 44 | for (let x = 0; x < mapSize; x += 0.5)
|
|---|
| 45 | for (let y = 0; y < mapSize; y += 0.5)
|
|---|
| 46 | {
|
|---|
| 47 | const point = new Vector2D(x, y).rotateAround(this.angle, mapCenter).round();
|
|---|
| 48 | if (!area.contains(point) || processed[point.x][point.y] || !g_Map.validTilePassable(point))
|
|---|
| 49 | continue;
|
|---|
| 50 |
|
|---|
| 51 | processed[point.x][point.y] = 1;
|
|---|
| 52 |
|
|---|
| 53 | for (const template of shuffleArray(templates))
|
|---|
| 54 | {
|
|---|
| 55 | if (template.constraint && !template.constraint.allows(point))
|
|---|
| 56 | continue;
|
|---|
| 57 |
|
|---|
| 58 | // Randomize building angle while keeping it aligned
|
|---|
| 59 | const buildingAngle = this.angle + randIntInclusive(0, 3) * Math.PI / 2;
|
|---|
| 60 |
|
|---|
| 61 | // Place the entity if all points are within the boundaries and don't collide with the other entities
|
|---|
| 62 | const obstructionCorners = template.obstructionCorners.map(obstructionCorner =>
|
|---|
| 63 | Vector2D.add(point, obstructionCorner.clone().rotate(buildingAngle)));
|
|---|
| 64 |
|
|---|
| 65 | const obstructionPoints = new ConvexPolygonPlacer(obstructionCorners, 0).place(
|
|---|
| 66 | new AndConstraint([new StayAreasConstraint([area]), avoidClasses(tileClass, 0), new PassableMapAreaConstraint()]));
|
|---|
| 67 |
|
|---|
| 68 | if (!obstructionPoints)
|
|---|
| 69 | continue;
|
|---|
| 70 |
|
|---|
| 71 | g_Map.placeEntityPassable(template.templateName, this.playerID, Vector2D.average(obstructionCorners), -buildingAngle);
|
|---|
| 72 |
|
|---|
| 73 | if (template.painter)
|
|---|
| 74 | template.painter.paint(new Area(obstructionPoints));
|
|---|
| 75 |
|
|---|
| 76 | for (const obstructionPoint of obstructionPoints)
|
|---|
| 77 | tileClass.add(obstructionPoint);
|
|---|
| 78 |
|
|---|
| 79 | ++templateCounts[template.templateName];
|
|---|
| 80 | templates = templates.filter(template => templateCounts[template.templateName] < template.maxCount);
|
|---|
| 81 | break;
|
|---|
| 82 | }
|
|---|
| 83 | }
|
|---|
| 84 | };
|
|---|