Added a tundra fringe and ragged polar ice edges to the map generator
This commit is contained in:
@@ -73,6 +73,7 @@ const TERRAIN_HEIGHT = {
|
||||
Forest: { floor: 0.08, relief: 0.16 },
|
||||
Land: { floor: 0.04, relief: 0.12 },
|
||||
Desert: { floor: 0.02, relief: 0.08 },
|
||||
Tundra: { floor: 0.0, relief: 0.0 },
|
||||
Ice: { floor: 0.0, relief: 0.04 },
|
||||
Sea: { floor: 0.0, relief: 0.0 },
|
||||
};
|
||||
|
||||
@@ -53,6 +53,7 @@ const LAND_TEXTURES = {
|
||||
Land: { base: TEXTURE_GRASS },
|
||||
Desert: { base: TEXTURE_SAND },
|
||||
Mountain: { base: TEXTURE_GRASS, overlay: TEXTURE_MOUNTAIN },
|
||||
Tundra: { base: TEXTURE_GRASS, overlay: TEXTURE_MOUNTAIN },
|
||||
};
|
||||
|
||||
// Blend radius of the land kernel. A neighbour's centre is one edge-width away
|
||||
@@ -121,28 +122,33 @@ function beachFalloff(distance) {
|
||||
//
|
||||
// `samples` is a list of { tile, dx, dy }: `tile` is the tile data and (dx, dy)
|
||||
// is the offset from the point to that tile's centre. The result carries the
|
||||
// grass/sand base split (summing to 1) and the opacity of each overlay, or null
|
||||
// when no textured-land tile is close enough. The value depends only on the
|
||||
// point and its surroundings, never on which tile asked, so shared vertices
|
||||
// agree.
|
||||
// grass/sand base split (summing to 1), the opacity of each overlay and the
|
||||
// share that comes from tundra, or null when no textured-land tile is close
|
||||
// enough. Tundra shares the mountain art but stays flat, so its share rides in
|
||||
// `flat` and the shader skips the height cap and the relief normal for it. The
|
||||
// value depends only on the point and its surroundings, never on which tile
|
||||
// asked, so shared vertices agree.
|
||||
export function blendTextures(samples) {
|
||||
let grass = 0;
|
||||
let sand = 0;
|
||||
let flat = 0;
|
||||
let total = 0;
|
||||
const overlays = {};
|
||||
for (const name of OVERLAY_TEXTURES) overlays[name] = 0;
|
||||
for (const sample of samples) {
|
||||
const texture = sample.tile && LAND_TEXTURES[sample.tile.terrainType];
|
||||
const tile = sample.tile;
|
||||
const texture = tile && LAND_TEXTURES[tile.terrainType];
|
||||
if (!texture) continue;
|
||||
const weight = falloff(Math.hypot(sample.dx, sample.dy));
|
||||
if (weight <= 0) continue;
|
||||
total += weight;
|
||||
if (texture.base === TEXTURE_SAND) sand += weight;
|
||||
else grass += weight;
|
||||
if (texture.overlay) overlays[texture.overlay] += weight;
|
||||
if (tile.terrainType === "Tundra") flat += weight;
|
||||
else if (texture.overlay) overlays[texture.overlay] += weight;
|
||||
}
|
||||
if (total <= 0) return null;
|
||||
const blend = { grass: grass / total, sand: sand / total };
|
||||
const blend = { grass: grass / total, sand: sand / total, flat: flat / total };
|
||||
for (const name of OVERLAY_TEXTURES) blend[name] = overlays[name] / total;
|
||||
return blend;
|
||||
}
|
||||
@@ -166,16 +172,18 @@ export function blendBeach(samples) {
|
||||
}
|
||||
|
||||
// Per-vertex texture weights, packed for the shader as
|
||||
// [grass, sand, atlas, forest, mountain, water, beach]. Atlas terrains are
|
||||
// fully atlas; textured land is fully texture, with the base split, the overlay
|
||||
// opacities and the coastal sand coming from the neighbouring-tile blend.
|
||||
// Mountain coasts stay rocky, so they get no beach.
|
||||
// [grass, sand, atlas, forest, mountain, water, beach, flat]. Atlas terrains
|
||||
// are fully atlas; textured land is fully texture, with the base split, the
|
||||
// overlay opacities, the coastal sand and the flat tundra share coming from the
|
||||
// neighbouring-tile blend. Mountain and tundra coasts stay rocky, so they get
|
||||
// no beach.
|
||||
export function vertexTextureWeights(tile, samples) {
|
||||
const water = isWaterTile(tile) ? 1 : 0;
|
||||
if (!isTexturedLand(tile)) return [0, 0, 1, 0, 0, water, 0];
|
||||
if (!isTexturedLand(tile)) return [0, 0, 1, 0, 0, water, 0, 0];
|
||||
const blend = blendTextures(samples);
|
||||
if (!blend) return [0, 0, 1, 0, 0, water, 0];
|
||||
const beach = tile.terrainType === "Mountain"
|
||||
if (!blend) return [0, 0, 1, 0, 0, water, 0, 0];
|
||||
const rocky = tile.terrainType === "Mountain" || tile.terrainType === "Tundra";
|
||||
const beach = rocky
|
||||
? 0
|
||||
: Math.min(1, blendBeach(samples) * BEACH_STRENGTH);
|
||||
return [
|
||||
@@ -186,5 +194,6 @@ export function vertexTextureWeights(tile, samples) {
|
||||
blend[TEXTURE_MOUNTAIN],
|
||||
water,
|
||||
beach,
|
||||
blend.flat,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -228,10 +228,13 @@ void main() {
|
||||
// Mountain rock only caps the higher ground: the lower slopes reveal the
|
||||
// grass base, so the texture follows the relief instead of filling the
|
||||
// whole mountain tile. The terrain-type weight still decides where the
|
||||
// range is at all.
|
||||
// range is at all. Tundra reuses the mountain art but is never raised, so
|
||||
// its own weight (vOverlay.w) shows the texture at full strength while the
|
||||
// relief normal below stays flat.
|
||||
float rock = vOverlay.y * smoothstep(uRockLow, uRockHigh, vHeight);
|
||||
float mountainMix = max(rock, vOverlay.w);
|
||||
base = mix(base, forest.rgb, forest.a * vOverlay.x);
|
||||
base = mix(base, mountain.rgb, mountain.a * rock);
|
||||
base = mix(base, mountain.rgb, mountain.a * mountainMix);
|
||||
// Coastal sand fades the sand texture into non-mountain shores.
|
||||
base = mix(base, sand, vOverlay.z);
|
||||
// Sea tiles are replaced by the animated water; the flag is 0 elsewhere.
|
||||
@@ -1083,7 +1086,7 @@ export class GLMapRenderer {
|
||||
buffer[offset + 11] = w[3];
|
||||
buffer[offset + 12] = w[4];
|
||||
buffer[offset + 13] = w[6];
|
||||
buffer[offset + 14] = 0;
|
||||
buffer[offset + 14] = w[7];
|
||||
buffer[offset + 15] = vertex.dx;
|
||||
buffer[offset + 16] = vertex.dy;
|
||||
offset += TERRAIN_FLOATS;
|
||||
|
||||
@@ -10,6 +10,18 @@ export const MAP_CONFIG = {
|
||||
falloffStart: 0.55,
|
||||
falloffStrength: 0.6,
|
||||
iceLatitude: 0.9,
|
||||
tundraLatitude: 0.66,
|
||||
// The polar ice and tundra edges are bent by a finer noise field so the
|
||||
// sheets are jagged rather than straight bands. `iceRoughness` is how far (in
|
||||
// latitude) the edges may wander; the equatorward core beyond that bound is
|
||||
// always ice (and always tundra below it).
|
||||
iceNoise: {
|
||||
frequency: 0.13,
|
||||
octaves: 2,
|
||||
lacunarity: 2.0,
|
||||
gain: 0.5,
|
||||
},
|
||||
iceRoughness: 0.06,
|
||||
noise: {
|
||||
frequency: 0.02,
|
||||
octaves: 1,
|
||||
@@ -32,7 +44,7 @@ export const MAP_CONFIG = {
|
||||
terrainBiomes: [
|
||||
{ terrainType: "Mountain", coverage: 0.03, size: 10 },
|
||||
{ terrainType: "Hills", coverage: 0.1, size: 26 },
|
||||
{ terrainType: "Forest", coverage: 0.16, size: 40 },
|
||||
{ terrainType: "Forest", coverage: 0.22, size: 30 },
|
||||
{ terrainType: "Desert", coverage: 0.08, size: 48 },
|
||||
],
|
||||
capitalMargin: 2,
|
||||
|
||||
@@ -12,6 +12,7 @@ export const TERRAIN_TILES = [
|
||||
{ col: 4, terrainType: "Desert", terrainClass: "Land", populationMultiplier: 0.05, gdpMultiplier: 0.2, movementCostMultiplier: 3.0, defenseBonus: 0, minElevation: -0.2 },
|
||||
{ col: 5, terrainType: "Sea", terrainClass: "Sea", populationMultiplier: 0, gdpMultiplier: 0, movementCostMultiplier: 0, defenseBonus: 0, minElevation: 0 },
|
||||
{ col: 6, terrainType: "Mountain", terrainClass: "Land", populationMultiplier: 0.1, gdpMultiplier: 0.8, movementCostMultiplier: 5.0, defenseBonus: 1.0, minElevation: 0.34 },
|
||||
{ col: 6, terrainType: "Tundra", terrainClass: "Land", populationMultiplier: 0.05, gdpMultiplier: 0.2, movementCostMultiplier: 2.0, defenseBonus: 0, minElevation: 0 },
|
||||
];
|
||||
|
||||
// A city standing on a tile adds this fraction on top of the terrain's own
|
||||
|
||||
+39
-8
@@ -56,7 +56,11 @@ export class MapGenerator {
|
||||
const coast = this._sampleNoise(noise, column, coords, wrapped, radius, this.config.coastNoise);
|
||||
elevation += (this.config.coastRoughness || 0) * coast;
|
||||
}
|
||||
const tile = this._chooseTile(coords, elevation);
|
||||
let iceNoise = 0;
|
||||
if (this.config.iceNoise) {
|
||||
iceNoise = this._sampleNoise(noise, column, coords, wrapped, radius, this.config.iceNoise);
|
||||
}
|
||||
const tile = this._chooseTile(coords, elevation, iceNoise);
|
||||
this.tiles[key(coords.x, coords.y)] = tile;
|
||||
if (tile.terrainClass === "Land") this.landCells.push(coords);
|
||||
}
|
||||
@@ -87,20 +91,34 @@ export class MapGenerator {
|
||||
return value - fade * this.config.falloffStrength;
|
||||
}
|
||||
|
||||
_isPolar(coords) {
|
||||
_isPolar(coords, iceNoise = 0) {
|
||||
const half = this.config.mapSize.y / 2;
|
||||
if (half <= 0) return false;
|
||||
return Math.abs(coords.y) / half >= this.config.iceLatitude;
|
||||
const jitter = Math.min(1, Math.max(-1, iceNoise));
|
||||
const latitude = this.config.iceLatitude + (this.config.iceRoughness || 0) * jitter;
|
||||
return Math.abs(coords.y) / half >= latitude;
|
||||
}
|
||||
|
||||
_chooseTile(coords, elevation) {
|
||||
_chooseTile(coords, elevation, iceNoise) {
|
||||
const palette = this._palette;
|
||||
if (palette.ice && this._isPolar(coords)) return palette.ice;
|
||||
if (palette.ice && this._isPolar(coords, iceNoise)) return palette.ice;
|
||||
if (elevation < this.config.seaLevel) return palette.sea;
|
||||
// Land starts as plains; the terrain clusters are painted over it later.
|
||||
// Tundra fringes the ice sheets; land starts as plains otherwise, and the
|
||||
// terrain clusters are painted over it later.
|
||||
if (palette.tundra && this._isTundra(coords, iceNoise)) return palette.tundra;
|
||||
return palette.plains;
|
||||
}
|
||||
|
||||
// The tundra band just outside the ice, its edge wandering on the same noise
|
||||
// as the ice so the two meet in a ragged line.
|
||||
_isTundra(coords, iceNoise = 0) {
|
||||
const half = this.config.mapSize.y / 2;
|
||||
if (half <= 0) return false;
|
||||
const jitter = Math.min(1, Math.max(-1, iceNoise));
|
||||
const latitude = this.config.tundraLatitude + (this.config.iceRoughness || 0) * jitter;
|
||||
return Math.abs(coords.y) / half >= latitude;
|
||||
}
|
||||
|
||||
// Third pass: scatter clusters of each biome over the land. Clusters are
|
||||
// seeded from random land tiles and grown one tile at a time into an
|
||||
// irregular blob, in priority order (earlier entries win overlaps) and
|
||||
@@ -119,7 +137,8 @@ export class MapGenerator {
|
||||
Math.round((biome.coverage * this.landCells.length) / size)
|
||||
);
|
||||
for (let i = 0; i < count; i++) {
|
||||
const center = this.landCells[rng.range(0, this.landCells.length - 1)];
|
||||
const center = this._pickPaintableCenter(rng);
|
||||
if (!center) continue;
|
||||
// Vary the size so the patches do not all look the same.
|
||||
const grown = Math.max(1, Math.round(size * (0.5 + rng.float())));
|
||||
this._growCluster(center, grown, tile, rng);
|
||||
@@ -127,6 +146,17 @@ export class MapGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
// Draws a cluster centre, retrying a few times when the drawn tile has already
|
||||
// been claimed by a higher-priority biome, so a low-coverage biome is not
|
||||
// silently dropped just because its one seed landed on a forest.
|
||||
_pickPaintableCenter(rng) {
|
||||
for (let attempt = 0; attempt < 8; attempt++) {
|
||||
const center = this.landCells[rng.range(0, this.landCells.length - 1)];
|
||||
if (this._isPaintable(center)) return center;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Grows one irregular blob of `size` tiles from `center`. At every step it
|
||||
// paints a random tile on the blob's edge, so the outline wanders instead of
|
||||
// closing into a hexagon, while the interior fills in rather than leaving
|
||||
@@ -538,5 +568,6 @@ function buildPalette() {
|
||||
const plains = byType.Land || landTiles[0] || null;
|
||||
const sea = TERRAIN_TILES.find((t) => t.terrainClass === "Sea") || null;
|
||||
const ice = TERRAIN_TILES.find((t) => t.terrainClass === "Ice") || null;
|
||||
return { plains, byType, sea, ice };
|
||||
const tundra = byType.Tundra || null;
|
||||
return { plains, byType, sea, ice, tundra };
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
GOVERNMENTS,
|
||||
TECHNOLOGIES,
|
||||
TRAINABLE_UNIT_IDS,
|
||||
TERRAIN_TILES,
|
||||
protoUnitById,
|
||||
buildingById,
|
||||
governmentById,
|
||||
@@ -151,4 +152,14 @@ export class DataTest extends TestCase {
|
||||
this.assertApprox(effectValue(effect, 3), 30);
|
||||
this.assertEqual(effectValue(effect, 0), 0);
|
||||
}
|
||||
|
||||
test_tundra_is_rocky_but_flat_and_unproductive() {
|
||||
const tundra = TERRAIN_TILES.find((t) => t.terrainType === "Tundra");
|
||||
this.assertNotNull(tundra, "tundra exists");
|
||||
this.assertEqual(tundra.terrainClass, "Land", "tundra is land");
|
||||
this.assertEqual(tundra.defenseBonus, 0, "tundra stays flat");
|
||||
this.assertApprox(tundra.populationMultiplier, 0.05, 1e-9);
|
||||
this.assertApprox(tundra.gdpMultiplier, 0.2, 1e-9);
|
||||
this.assertApprox(tundra.movementCostMultiplier, 2.0, 1e-9);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,15 @@ export class HeightmapTest extends TestCase {
|
||||
this.assertEqual(field.at(point.x, point.y), 0, "points over the sea stay at zero");
|
||||
}
|
||||
|
||||
test_tundra_stays_flat_despite_its_rocky_texture() {
|
||||
const field = new HeightField(fixture(() => "Tundra"));
|
||||
for (let x = -2; x <= 2; x++) {
|
||||
for (let y = -2; y <= 2; y++) {
|
||||
this.assertEqual(field.tileHeight(x, y), 0, "tundra never rises");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test_the_open_sea_stays_flat_beside_a_high_coast() {
|
||||
// The distance blend would pull a coastal sea point up towards the mountain
|
||||
// next to it; the sea mask pins it to exactly zero instead.
|
||||
|
||||
@@ -153,15 +153,24 @@ export class MapGeneratorTest extends TestCase {
|
||||
const cfg = config({ iceLatitude: 0.5 });
|
||||
const generator = generate(cfg);
|
||||
const half = cfg.mapSize.y / 2;
|
||||
const core = cfg.iceLatitude + (cfg.iceRoughness || 0);
|
||||
for (const k in generator.tiles) {
|
||||
const i = k.indexOf(",");
|
||||
const y = Number(k.slice(i + 1));
|
||||
if (Math.abs(y) / half >= cfg.iceLatitude) {
|
||||
if (Math.abs(y) / half >= core) {
|
||||
this.assertEqual(generator.tiles[k].terrainType, "Ice", `tile ${k}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test_ice_edge_wanders_with_noise() {
|
||||
const cfg = config({ iceLatitude: 0.5, iceRoughness: 0.2 });
|
||||
const generator = generate(cfg);
|
||||
const coords = { x: 0, y: 6 };
|
||||
this.assertTrue(generator._isPolar(coords, -1), "the edge can reach south");
|
||||
this.assertFalse(generator._isPolar(coords, 1), "the edge can pull north");
|
||||
}
|
||||
|
||||
test_equator_is_not_ice() {
|
||||
const generator = generate();
|
||||
for (const k in generator.tiles) {
|
||||
@@ -171,6 +180,21 @@ export class MapGeneratorTest extends TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
test_tundra_fringes_the_poles() {
|
||||
const cfg = config();
|
||||
const generator = generate(cfg);
|
||||
const half = cfg.mapSize.y / 2;
|
||||
const inner = cfg.tundraLatitude - (cfg.iceRoughness || 0);
|
||||
let tundra = 0;
|
||||
for (const k in generator.tiles) {
|
||||
if (generator.tiles[k].terrainType !== "Tundra") continue;
|
||||
tundra += 1;
|
||||
const y = Number(k.slice(k.indexOf(",") + 1));
|
||||
this.assertGreaterOrEqual(Math.abs(y) / half, inner, `tundra at ${k} is polar`);
|
||||
}
|
||||
this.assertGreater(tundra, 0, "there is tundra near the poles");
|
||||
}
|
||||
|
||||
test_falloff_lowers_the_edges() {
|
||||
const cfg = config({ falloffEnabled: true, falloffStart: 0, falloffStrength: 1 });
|
||||
const generator = generate(cfg);
|
||||
@@ -213,7 +237,7 @@ export class MapGeneratorTest extends TestCase {
|
||||
for (const coords of generator.landCells) {
|
||||
terrainTypes.add(generator.tiles[key(coords.x, coords.y)].terrainType);
|
||||
}
|
||||
for (const type of ["Land", "Forest", "Hills", "Mountain", "Desert"]) {
|
||||
for (const type of ["Land", "Forest", "Hills", "Mountain", "Desert", "Tundra"]) {
|
||||
this.assertTrue(terrainTypes.has(type), `${type} appears`);
|
||||
}
|
||||
}
|
||||
@@ -221,7 +245,11 @@ export class MapGeneratorTest extends TestCase {
|
||||
test_biome_clusters_stay_small() {
|
||||
const generator = generate();
|
||||
for (const biome of MAP_CONFIG.terrainBiomes) {
|
||||
const largest = Math.round(biome.size * 1.5);
|
||||
// Same-type patches may grow into each other, so a connected body is
|
||||
// bounded by the total area the biome was asked to cover, not by one
|
||||
// patch. A biome still cannot run away and swallow the whole map.
|
||||
const target = Math.round((biome.coverage || 0) * generator.landCells.length);
|
||||
const largest = Math.max(Math.round(biome.size * 1.5), target);
|
||||
const sizes = componentSizes(
|
||||
generator,
|
||||
(tile) => tile.terrainType === biome.terrainType
|
||||
|
||||
+21
-5
@@ -29,7 +29,7 @@ function fixture(terrainAt, size = 5) {
|
||||
|
||||
export class TexturesTest extends TestCase {
|
||||
test_grassy_land_uses_grass_and_desert_uses_sand() {
|
||||
for (const type of ["Forest", "Hills", "Land", "Mountain"]) {
|
||||
for (const type of ["Forest", "Hills", "Land", "Mountain", "Tundra"]) {
|
||||
this.assertEqual(landTexture(type).base, "grass", `${type} base`);
|
||||
}
|
||||
this.assertEqual(landTexture("Desert").base, "sand", "desert base");
|
||||
@@ -41,6 +41,7 @@ export class TexturesTest extends TestCase {
|
||||
this.assertTrue(isTexturedLand({ terrainType: "Land" }));
|
||||
this.assertTrue(isTexturedLand({ terrainType: "Desert" }));
|
||||
this.assertTrue(isTexturedLand({ terrainType: "Mountain" }));
|
||||
this.assertTrue(isTexturedLand({ terrainType: "Tundra" }));
|
||||
this.assertFalse(isTexturedLand({ terrainType: "Sea" }));
|
||||
this.assertFalse(isTexturedLand(null));
|
||||
}
|
||||
@@ -55,6 +56,10 @@ export class TexturesTest extends TestCase {
|
||||
"assets/textures/mountain.png",
|
||||
"assets/textures/grass.png",
|
||||
]);
|
||||
this.assertEqual(terrainLayers("Tundra"), [
|
||||
"assets/textures/mountain.png",
|
||||
"assets/textures/grass.png",
|
||||
]);
|
||||
this.assertEqual(terrainLayers("Desert"), ["assets/textures/sand.png"]);
|
||||
this.assertEqual(terrainLayers("Land"), ["assets/textures/grass.png"]);
|
||||
this.assertEmpty(terrainLayers("Ice"), "the atlas terrains keep their art");
|
||||
@@ -116,6 +121,10 @@ export class TexturesTest extends TestCase {
|
||||
const mountain = blendTextures([{ tile: { terrainType: "Mountain" }, dx: 0, dy: 0 }]);
|
||||
this.assertApprox(mountain.mountain, 1, 1e-9, "a mountain keeps its overlay");
|
||||
this.assertApprox(mountain.grass, 1, 1e-9, "mountains sit on grass");
|
||||
const tundra = blendTextures([{ tile: { terrainType: "Tundra" }, dx: 0, dy: 0 }]);
|
||||
this.assertApprox(tundra.flat, 1, 1e-9, "tundra rides in the flat rock channel");
|
||||
this.assertApprox(tundra.mountain, 0, 1e-9, "tundra does not need the height cap");
|
||||
this.assertApprox(tundra.grass, 1, 1e-9, "tundra sits on grass");
|
||||
}
|
||||
|
||||
test_the_blend_is_a_shared_field_from_either_tile() {
|
||||
@@ -164,17 +173,22 @@ export class TexturesTest extends TestCase {
|
||||
{ terrainType: "Ice" },
|
||||
[{ tile: { terrainType: "Land" }, dx: 0, dy: 0 }]
|
||||
);
|
||||
this.assertEqual(atlas, [0, 0, 1, 0, 0, 0, 0], "ice stays on the atlas");
|
||||
this.assertEqual(atlas, [0, 0, 1, 0, 0, 0, 0, 0], "ice stays on the atlas");
|
||||
const land = vertexTextureWeights(
|
||||
{ terrainType: "Land" },
|
||||
[{ tile: { terrainType: "Land" }, dx: 0, dy: 0 }]
|
||||
);
|
||||
this.assertEqual(land, [1, 0, 0, 0, 0, 0, 0], "plains use the grass texture");
|
||||
this.assertEqual(land, [1, 0, 0, 0, 0, 0, 0, 0], "plains use the grass texture");
|
||||
const mountain = vertexTextureWeights(
|
||||
{ terrainType: "Mountain" },
|
||||
[{ tile: { terrainType: "Mountain" }, dx: 0, dy: 0 }]
|
||||
);
|
||||
this.assertEqual(mountain, [1, 0, 0, 0, 1, 0, 0], "mountains sit on grass with their overlay");
|
||||
this.assertEqual(mountain, [1, 0, 0, 0, 1, 0, 0, 0], "mountains sit on grass with their overlay");
|
||||
const tundra = vertexTextureWeights(
|
||||
{ terrainType: "Tundra" },
|
||||
[{ tile: { terrainType: "Tundra" }, dx: 0, dy: 0 }]
|
||||
);
|
||||
this.assertEqual(tundra, [1, 0, 0, 0, 0, 0, 0, 1], "tundra uses the flat rock channel");
|
||||
}
|
||||
|
||||
test_sea_is_flagged_for_the_water_shader() {
|
||||
@@ -182,7 +196,7 @@ export class TexturesTest extends TestCase {
|
||||
{ terrainType: "Sea" },
|
||||
[{ tile: { terrainType: "Sea" }, dx: 0, dy: 0 }]
|
||||
);
|
||||
this.assertEqual(sea, [0, 0, 1, 0, 0, 1, 0], "sea rides on the atlas but is flagged as water");
|
||||
this.assertEqual(sea, [0, 0, 1, 0, 0, 1, 0, 0], "sea rides on the atlas but is flagged as water");
|
||||
this.assertFalse(isTexturedLand({ terrainType: "Sea" }), "sea is not a tiling land texture");
|
||||
}
|
||||
|
||||
@@ -196,6 +210,8 @@ export class TexturesTest extends TestCase {
|
||||
this.assertLess(plains[6], 1, "the ground still shows through");
|
||||
const mountain = vertexTextureWeights({ terrainType: "Mountain" }, samples);
|
||||
this.assertEqual(mountain[6], 0, "mountain coasts stay rocky");
|
||||
const tundra = vertexTextureWeights({ terrainType: "Tundra" }, samples);
|
||||
this.assertEqual(tundra[6], 0, "tundra coasts stay rocky");
|
||||
}
|
||||
|
||||
test_beach_is_a_shared_field_between_land_and_sea() {
|
||||
|
||||
Reference in New Issue
Block a user