Spaces:
Running
Running
Rebuild map bundle: Forgotten Plains grass/cliffs/props/stones
Browse filesweb/mapSandbox.js re-bundled from auto-battler — picks up the Forgotten Plains
overhaul: plain-green grass base, 2.5D plateau cliffs (shared bakeCliffs), expanded
foliage + multi-tile props + water reeds, the full 3×4 tree, and the new stone-clump
system. No new assets (FP tile/prop sheets were already curated).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- web/mapSandbox.js +209 -51
web/mapSandbox.js
CHANGED
|
@@ -710,6 +710,31 @@ function centerTile([c0, r0], dNW, dNE, dSW, dSE) {
|
|
| 710 |
if (dSW) return [c0, r0 + 4];
|
| 711 |
return [c0 + 1, r0 + 4];
|
| 712 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 713 |
|
| 714 |
// ../auto-battler/src/render/orcKingdom.js
|
| 715 |
var ORC = "/assets/minifantasy/Minifantasy_Orc_Kingdom_v1.0/Minifantasy_Orc_Kingdom_Assets";
|
|
@@ -890,6 +915,15 @@ var FOREST_SCALE = 0.05;
|
|
| 890 |
function forestField(seed, x, y) {
|
| 891 |
return fbm(sub3(seed, 15740503), x * FOREST_SCALE, y * FOREST_SCALE);
|
| 892 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 893 |
var RIVER_SCALE = 0.022;
|
| 894 |
var RIVER_WIDTH = 0.035;
|
| 895 |
var WARP_SCALE2 = 0.03;
|
|
@@ -912,9 +946,9 @@ var FP_MOCKUP_URL = `${FP}/Minifantasy_ForgottenPlainsMockup.png`;
|
|
| 912 |
var FP_MAP_ASSETS = [TILES2, SHADOW, PROPS, PROP_SHADOW];
|
| 913 |
var TILE3 = 8;
|
| 914 |
var CHUNK2 = 32;
|
| 915 |
-
var GRASS_BASE = [
|
| 916 |
-
var GRASS_VARS = [
|
| 917 |
-
var GRASS_RATE =
|
| 918 |
var DIRT_BLOCK = [7, 3];
|
| 919 |
var DIRT_FILL = [8, 4];
|
| 920 |
var DIRT_VARS2 = [[7, 1], [8, 1], [9, 1]];
|
|
@@ -925,21 +959,54 @@ var WATER_BLOCK = [25, 3];
|
|
| 925 |
var WATER_FILL = [26, 4];
|
| 926 |
var WATER_VARS = [[26, 4]];
|
| 927 |
var FILL_RATE = 6;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 928 |
var FOLIAGE = [
|
| 929 |
-
{ tiles: [[9, 6], [10, 6], [11, 6]]
|
| 930 |
-
// grass tufts
|
| 931 |
-
{
|
| 932 |
-
// flowers (red / purple / daisy)
|
| 933 |
-
{ tiles: [[12, 6], [13, 6], [14, 6]], weight: 1 }
|
| 934 |
// small reeds
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 935 |
];
|
| 936 |
var FOLIAGE_WEIGHT = FOLIAGE.reduce((s, g) => s + g.weight, 0);
|
| 937 |
var FOLIAGE_RATE = 11;
|
|
|
|
|
|
|
| 938 |
var TREES = [
|
| 939 |
{ frame: [155, 3, 21, 25], ax: 0.48, ay: 0.96 },
|
| 940 |
-
// tree 1
|
| 941 |
-
{ frame: [155, 35, 21, 25], ax: 0.48, ay: 0.96 }
|
| 942 |
// tree 2 (fruited)
|
|
|
|
|
|
|
| 943 |
];
|
| 944 |
var TREE_SCALE_BASE = 1;
|
| 945 |
var TREE_SCALE_JITTER = 0.14;
|
|
@@ -950,6 +1017,32 @@ var FOREST_BLOCK_Y = 2;
|
|
| 950 |
var FOREST_THRESHOLD = 0.6;
|
| 951 |
var FOREST_MIN_NB = 2;
|
| 952 |
var FOREST_FILL = 0.5;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 953 |
var COL_GRASS2 = [97, 150, 55];
|
| 954 |
var COL_DIRT2 = [118, 80, 38];
|
| 955 |
var COL_STONE = [120, 120, 122];
|
|
@@ -1006,6 +1099,8 @@ function chunkFields2(seed, x0, y0) {
|
|
| 1006 |
return cur;
|
| 1007 |
};
|
| 1008 |
const waterF = clean((x, y) => isRiver(seed, x, y));
|
|
|
|
|
|
|
| 1009 |
const dirtRaw = clean((x, y) => isDirt(seed, x, y));
|
| 1010 |
const stoneRaw = clean((x, y) => isStone(seed, x, y));
|
| 1011 |
const fraw = new Uint8Array(SZ * SZ);
|
|
@@ -1017,6 +1112,15 @@ function chunkFields2(seed, x0, y0) {
|
|
| 1017 |
for (let dj = -1; dj <= 1; dj++) for (let di = -1; di <= 1; di++) if ((di || dj) && fraw[idx(i + di, j + dj)]) c++;
|
| 1018 |
forestRegion[idx(i, j)] = c >= FOREST_MIN_NB ? 1 : 0;
|
| 1019 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1020 |
const dirt = new Uint8Array(SZ * SZ), stone = new Uint8Array(SZ * SZ);
|
| 1021 |
for (let j = WATER_BUFFER; j < SZ - WATER_BUFFER; j++) for (let i = WATER_BUFFER; i < SZ - WATER_BUFFER; i++) {
|
| 1022 |
let nearW = 0;
|
|
@@ -1024,10 +1128,11 @@ function chunkFields2(seed, x0, y0) {
|
|
| 1024 |
nearW = 1;
|
| 1025 |
break;
|
| 1026 |
}
|
| 1027 |
-
|
| 1028 |
-
|
|
|
|
| 1029 |
}
|
| 1030 |
-
return { waterF, dirt, stone, forestRegion, M, SZ, x0, y0 };
|
| 1031 |
}
|
| 1032 |
var FP_GROUND_FILLS = [
|
| 1033 |
{ key: "grass", url: TILES2, tile: GRASS_BASE },
|
|
@@ -1078,11 +1183,12 @@ var fpConfig = (seed) => ({
|
|
| 1078 |
// standalone map is unchanged; the multi-biome overworld passes a per-biome dither mask.
|
| 1079 |
bake({ x0, y0, seed: seed2, ctx, tmp, Sprite, tex, texFrame, add, accept = () => true }) {
|
| 1080 |
const f = chunkFields2(seed2, x0, y0);
|
| 1081 |
-
const { waterF, dirt, stone, forestRegion, M, SZ } = f;
|
| 1082 |
const idx = (i, j) => j * SZ + i;
|
| 1083 |
const atW = (wx, wy) => waterF[idx(wx - x0 + M, wy - y0 + M)] === 1;
|
| 1084 |
const atD = (wx, wy) => dirt[idx(wx - x0 + M, wy - y0 + M)] === 1;
|
| 1085 |
const atS = (wx, wy) => stone[idx(wx - x0 + M, wy - y0 + M)] === 1;
|
|
|
|
| 1086 |
const place = (coord, tx, ty) => {
|
| 1087 |
add(ctx.tiles, coord[0], coord[1], tx, ty);
|
| 1088 |
if (ctx.shadow && (!ctx.shadowSet || ctx.shadowSet.has(coord[0] + "," + coord[1]))) {
|
|
@@ -1117,10 +1223,23 @@ var fpConfig = (seed) => ({
|
|
| 1117 |
if (atS(wx, wy)) blob(atS, STONE_BLOCK, STONE_FILL, STONE_VARS, 2, wx, wy, tx, ty);
|
| 1118 |
if (atW(wx, wy)) blob(atW, WATER_BLOCK, WATER_FILL, WATER_VARS, 3, wx, wy, tx, ty);
|
| 1119 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1120 |
if (ctx.props) for (let ty = 0; ty < CHUNK2; ty++) for (let tx = 0; tx < CHUNK2; tx++) {
|
| 1121 |
if (!accept(tx, ty)) continue;
|
| 1122 |
const wx = x0 + tx, wy = y0 + ty;
|
| 1123 |
-
if (atW(wx, wy)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1124 |
const h2 = hashU32(seed2 ^ 15733114, wx, wy);
|
| 1125 |
if (h2 % FOLIAGE_RATE !== 0) continue;
|
| 1126 |
let pick = (h2 >>> 8) % FOLIAGE_WEIGHT, g = FOLIAGE[0];
|
|
@@ -1131,13 +1250,17 @@ var fpConfig = (seed) => ({
|
|
| 1131 |
}
|
| 1132 |
pick -= grp.weight;
|
| 1133 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1134 |
const [c, r] = g.tiles[(h2 >>> 16) % g.tiles.length];
|
| 1135 |
const sp = new Sprite(texFrame(ctx.props, c * TILE3, r * TILE3, TILE3, TILE3));
|
| 1136 |
sp.x = tx * TILE3;
|
| 1137 |
sp.y = ty * TILE3;
|
| 1138 |
tmp.addChild(sp);
|
| 1139 |
}
|
| 1140 |
-
|
| 1141 |
if (ctx.props) {
|
| 1142 |
const inGrove = (wx, wy) => forestRegion[idx(wx - x0 + M, wy - y0 + M)] === 1;
|
| 1143 |
const bx0 = Math.floor(x0 / FOREST_BLOCK_X), bx1 = Math.floor((x0 + CHUNK2 - 1) / FOREST_BLOCK_X);
|
|
@@ -1150,6 +1273,13 @@ var fpConfig = (seed) => ({
|
|
| 1150 |
if (!inGrove(wx, wy)) continue;
|
| 1151 |
if ((h2 >>> 8 & 65535) / 65536 >= FOREST_FILL) continue;
|
| 1152 |
if (atW(wx, wy) || atS(wx, wy)) continue;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1153 |
const T = TREES[(h2 >>> 24) % TREES.length];
|
| 1154 |
const flip = (h2 >>> 20 & 255) / 256 < TREE_FLIP_RATE;
|
| 1155 |
const sc = TREE_SCALE_BASE * (1 + ((h2 >>> 12 & 255) / 256 - 0.5) * 2 * TREE_SCALE_JITTER);
|
|
@@ -1172,13 +1302,43 @@ var fpConfig = (seed) => ({
|
|
| 1172 |
live.push({ sprite: tr, shadow });
|
| 1173 |
}
|
| 1174 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1175 |
return { meta: f, live };
|
| 1176 |
},
|
| 1177 |
macroColor(seed2, tx, ty) {
|
| 1178 |
-
|
| 1179 |
-
if (
|
| 1180 |
-
|
| 1181 |
-
|
|
|
|
| 1182 |
},
|
| 1183 |
tileIndexAt(wx, wy, meta) {
|
| 1184 |
if (!meta) return null;
|
|
@@ -1217,10 +1377,10 @@ var DARK_THRESHOLD = 0.6;
|
|
| 1217 |
function isDark(seed, x, y) {
|
| 1218 |
return fbm(sub4(seed, 55852), x * DARK_SCALE, y * DARK_SCALE) > DARK_THRESHOLD;
|
| 1219 |
}
|
| 1220 |
-
var
|
| 1221 |
-
var
|
| 1222 |
-
function
|
| 1223 |
-
return fbm(sub4(seed, 57836), x *
|
| 1224 |
}
|
| 1225 |
var BONE_SCALE = 0.055;
|
| 1226 |
var BONE_THRESHOLD = 0.62;
|
|
@@ -1295,6 +1455,26 @@ var FACE_VAR = [[[2, 16], [4, 16], [5, 16]], [[2, 17], [4, 17], [5, 17]]];
|
|
| 1295 |
var FACE_L = [[1, 16], [1, 17]];
|
| 1296 |
var FACE_R = [[6, 16], [6, 17]];
|
| 1297 |
var CLIFF_SPARSE_RATE = 2;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1298 |
var CORRUPT_LIGHT = [1, 1];
|
| 1299 |
var CORRUPT_DARK = [2, 1];
|
| 1300 |
var NECRO_GROUND_FILLS = [
|
|
@@ -1327,7 +1507,7 @@ var COL_LIGHT = [130, 119, 136];
|
|
| 1327 |
var COL_DARK = [105, 99, 113];
|
| 1328 |
var COL_WATER2 = [74, 142, 48];
|
| 1329 |
var COL_BONE = [180, 156, 126];
|
| 1330 |
-
var
|
| 1331 |
var COL_FOREST = [96, 101, 70];
|
| 1332 |
var NECRO_LAYERS = [
|
| 1333 |
["m-bg", "Background", "ground"],
|
|
@@ -1377,12 +1557,12 @@ function boneCenterTile([c0, r0], dNW, dNE, dSW, dSE) {
|
|
| 1377 |
return [c0 + 1, r0 + 1];
|
| 1378 |
}
|
| 1379 |
function biomeColor(seed, tx, ty) {
|
| 1380 |
-
const raised =
|
| 1381 |
if (!raised && isRiver2(seed, tx, ty)) return COL_WATER2;
|
| 1382 |
if (isBone(seed, tx, ty)) return COL_BONE;
|
| 1383 |
let c = isDark(seed, tx, ty) ? COL_DARK : COL_LIGHT;
|
| 1384 |
if (forestField2(seed, tx, ty) > FOREST_THRESHOLD2) c = lerp3(c, COL_FOREST, 0.5);
|
| 1385 |
-
if (raised) c = lerp3(c,
|
| 1386 |
return c;
|
| 1387 |
}
|
| 1388 |
function loadImg2(url) {
|
|
@@ -1439,7 +1619,7 @@ function chunkFields3(seed, x0, y0) {
|
|
| 1439 |
};
|
| 1440 |
const darkRaw = clean((x, y) => isDark(seed, x, y));
|
| 1441 |
const waterField = clean((x, y) => isRiver2(seed, x, y));
|
| 1442 |
-
const raisedField = clean((x, y) =>
|
| 1443 |
const boneField = clean((x, y) => isBone(seed, x, y));
|
| 1444 |
const fraw = new Uint8Array(SZ * SZ);
|
| 1445 |
for (let j = 0; j < SZ; j++) for (let i = 0; i < SZ; i++) fraw[idx(i, j)] = forestField2(seed, x0 + i - M, y0 + j - M) > FOREST_THRESHOLD2 ? 1 : 0;
|
|
@@ -1614,29 +1794,7 @@ var necropolisConfig = (seed) => ({
|
|
| 1614 |
sp.y = ty * TILE4;
|
| 1615 |
tmp.addChild(sp);
|
| 1616 |
}
|
| 1617 |
-
|
| 1618 |
-
if (!accept(tx, ty)) continue;
|
| 1619 |
-
const wx = x0 + tx, wy = y0 + ty;
|
| 1620 |
-
if (isRaisedAt(wx, wy)) {
|
| 1621 |
-
const bN = !isRaisedAt(wx, wy - 1), bE = !isRaisedAt(wx + 1, wy), bS = !isRaisedAt(wx, wy + 1), bW = !isRaisedAt(wx - 1, wy);
|
| 1622 |
-
if (bN && bW) place(LIP_TL, tx, ty);
|
| 1623 |
-
else if (bN && bE) place(LIP_TR, tx, ty);
|
| 1624 |
-
else if (bS && bW) place(LIP_SW, tx, ty);
|
| 1625 |
-
else if (bS && bE) place(LIP_SE, tx, ty);
|
| 1626 |
-
else if (bN) place(sparse(LIP_T, LIP_T_VAR, wx, wy, 1, CLIFF_SPARSE_RATE, seed2), tx, ty);
|
| 1627 |
-
else if (bS) place(sparse(LIP_S, LIP_S_VAR, wx, wy, 2, CLIFF_SPARSE_RATE, seed2), tx, ty);
|
| 1628 |
-
else if (bW) place(sparse(WALL_L, WALL_L_VAR, wx, wy, 3, CLIFF_SPARSE_RATE, seed2), tx, ty);
|
| 1629 |
-
else if (bE) place(sparse(WALL_R, WALL_R_VAR, wx, wy, 4, CLIFF_SPARSE_RATE, seed2), tx, ty);
|
| 1630 |
-
} else {
|
| 1631 |
-
for (let k = 1; k <= FACE_H; k++) {
|
| 1632 |
-
if (isRaisedAt(wx, wy - k)) {
|
| 1633 |
-
const leftEnd = !isRaisedAt(wx - 1, wy - k), rightEnd = !isRaisedAt(wx + 1, wy - k);
|
| 1634 |
-
place(leftEnd ? FACE_L[k - 1] : rightEnd ? FACE_R[k - 1] : sparse(FACE[k - 1], FACE_VAR[k - 1], wx, wy, 5 + k, CLIFF_SPARSE_RATE, seed2), tx, ty);
|
| 1635 |
-
break;
|
| 1636 |
-
}
|
| 1637 |
-
}
|
| 1638 |
-
}
|
| 1639 |
-
}
|
| 1640 |
const live = [];
|
| 1641 |
if (ctx.props) {
|
| 1642 |
const nearWater = (wx, wy) => {
|
|
|
|
| 710 |
if (dSW) return [c0, r0 + 4];
|
| 711 |
return [c0 + 1, r0 + 4];
|
| 712 |
}
|
| 713 |
+
function bakeCliffs(cfg, { chunk, x0, y0, seed, raised, place, accept = () => true }) {
|
| 714 |
+
for (let ty = 0; ty < chunk; ty++) for (let tx = 0; tx < chunk; tx++) {
|
| 715 |
+
if (!accept(tx, ty)) continue;
|
| 716 |
+
const wx = x0 + tx, wy = y0 + ty;
|
| 717 |
+
if (raised(wx, wy)) {
|
| 718 |
+
const bN = !raised(wx, wy - 1), bE = !raised(wx + 1, wy), bS = !raised(wx, wy + 1), bW = !raised(wx - 1, wy);
|
| 719 |
+
if (bN && bW) place(cfg.LIP_TL, tx, ty);
|
| 720 |
+
else if (bN && bE) place(cfg.LIP_TR, tx, ty);
|
| 721 |
+
else if (bS && bW) place(cfg.LIP_SW, tx, ty);
|
| 722 |
+
else if (bS && bE) place(cfg.LIP_SE, tx, ty);
|
| 723 |
+
else if (bN) place(sparse(cfg.LIP_T, cfg.LIP_T_VAR, wx, wy, 1, cfg.RATE, seed), tx, ty);
|
| 724 |
+
else if (bS) place(sparse(cfg.LIP_S, cfg.LIP_S_VAR, wx, wy, 2, cfg.RATE, seed), tx, ty);
|
| 725 |
+
else if (bW) place(sparse(cfg.WALL_L, cfg.WALL_L_VAR, wx, wy, 3, cfg.RATE, seed), tx, ty);
|
| 726 |
+
else if (bE) place(sparse(cfg.WALL_R, cfg.WALL_R_VAR, wx, wy, 4, cfg.RATE, seed), tx, ty);
|
| 727 |
+
} else {
|
| 728 |
+
for (let k = 1; k <= cfg.FACE_H; k++) {
|
| 729 |
+
if (raised(wx, wy - k)) {
|
| 730 |
+
const leftEnd = !raised(wx - 1, wy - k), rightEnd = !raised(wx + 1, wy - k);
|
| 731 |
+
place(leftEnd ? cfg.FACE_L[k - 1] : rightEnd ? cfg.FACE_R[k - 1] : sparse(cfg.FACE[k - 1], cfg.FACE_VAR[k - 1], wx, wy, 5 + k, cfg.RATE, seed), tx, ty);
|
| 732 |
+
break;
|
| 733 |
+
}
|
| 734 |
+
}
|
| 735 |
+
}
|
| 736 |
+
}
|
| 737 |
+
}
|
| 738 |
|
| 739 |
// ../auto-battler/src/render/orcKingdom.js
|
| 740 |
var ORC = "/assets/minifantasy/Minifantasy_Orc_Kingdom_v1.0/Minifantasy_Orc_Kingdom_Assets";
|
|
|
|
| 915 |
function forestField(seed, x, y) {
|
| 916 |
return fbm(sub3(seed, 15740503), x * FOREST_SCALE, y * FOREST_SCALE);
|
| 917 |
}
|
| 918 |
+
var STONE_CLUMP_SCALE = 0.11;
|
| 919 |
+
function stoneClumpField(seed, x, y) {
|
| 920 |
+
return fbm(sub3(seed, 5702849), x * STONE_CLUMP_SCALE, y * STONE_CLUMP_SCALE);
|
| 921 |
+
}
|
| 922 |
+
var ELEV_SCALE = 0.025;
|
| 923 |
+
var ELEV_THRESHOLD = 0.6;
|
| 924 |
+
function isRaised(seed, x, y) {
|
| 925 |
+
return fbm(sub3(seed, 57836), x * ELEV_SCALE, y * ELEV_SCALE) > ELEV_THRESHOLD;
|
| 926 |
+
}
|
| 927 |
var RIVER_SCALE = 0.022;
|
| 928 |
var RIVER_WIDTH = 0.035;
|
| 929 |
var WARP_SCALE2 = 0.03;
|
|
|
|
| 946 |
var FP_MAP_ASSETS = [TILES2, SHADOW, PROPS, PROP_SHADOW];
|
| 947 |
var TILE3 = 8;
|
| 948 |
var CHUNK2 = 32;
|
| 949 |
+
var GRASS_BASE = [37, 11];
|
| 950 |
+
var GRASS_VARS = [...rect(1, 1, 4, 1), ...rect(2, 3, 3, 5)];
|
| 951 |
+
var GRASS_RATE = 9;
|
| 952 |
var DIRT_BLOCK = [7, 3];
|
| 953 |
var DIRT_FILL = [8, 4];
|
| 954 |
var DIRT_VARS2 = [[7, 1], [8, 1], [9, 1]];
|
|
|
|
| 959 |
var WATER_FILL = [26, 4];
|
| 960 |
var WATER_VARS = [[26, 4]];
|
| 961 |
var FILL_RATE = 6;
|
| 962 |
+
var FP_CLIFF = {
|
| 963 |
+
LIP_T: [25, 16],
|
| 964 |
+
LIP_T_VAR: [],
|
| 965 |
+
LIP_TL: [24, 16],
|
| 966 |
+
LIP_TR: [26, 16],
|
| 967 |
+
WALL_L: [24, 17],
|
| 968 |
+
WALL_L_VAR: [],
|
| 969 |
+
WALL_R: [26, 17],
|
| 970 |
+
WALL_R_VAR: [],
|
| 971 |
+
LIP_S: [25, 18],
|
| 972 |
+
LIP_S_VAR: [],
|
| 973 |
+
LIP_SW: [24, 18],
|
| 974 |
+
LIP_SE: [26, 18],
|
| 975 |
+
FACE_H: 2,
|
| 976 |
+
FACE: [[25, 19], [25, 20]],
|
| 977 |
+
FACE_VAR: [[], []],
|
| 978 |
+
FACE_L: [[24, 19], [24, 20]],
|
| 979 |
+
FACE_R: [[26, 19], [26, 20]],
|
| 980 |
+
RATE: 4
|
| 981 |
+
};
|
| 982 |
+
var COL_CLIFF = [150, 138, 112];
|
| 983 |
var FOLIAGE = [
|
| 984 |
+
{ weight: 5, tiles: [[9, 6], [10, 6], [11, 6]] },
|
| 985 |
+
// grass tufts
|
| 986 |
+
{ weight: 3, tiles: [[12, 6], [13, 6]] },
|
|
|
|
|
|
|
| 987 |
// small reeds
|
| 988 |
+
{ weight: 3, tiles: [[9, 9], [10, 9], [11, 9], [12, 9], [13, 9]] },
|
| 989 |
+
// flowers (red/purple/daisy) + small grass
|
| 990 |
+
{ weight: 1, sprite: { c: 14, r: 6, w: 1, h: 3 } },
|
| 991 |
+
// tall reed 1×3
|
| 992 |
+
{ weight: 1, sprite: { c: 15, r: 6, w: 2, h: 3 } },
|
| 993 |
+
// wide reed 2×3
|
| 994 |
+
{ weight: 1, sprite: { c: 17, r: 6, w: 2, h: 3 } },
|
| 995 |
+
// wide reed 2×3
|
| 996 |
+
{ weight: 1, sprite: { c: 14, r: 9, w: 1, h: 2 } }
|
| 997 |
+
// cattail 1×2
|
| 998 |
];
|
| 999 |
var FOLIAGE_WEIGHT = FOLIAGE.reduce((s, g) => s + g.weight, 0);
|
| 1000 |
var FOLIAGE_RATE = 11;
|
| 1001 |
+
var WATER_FOLIAGE = { c: 15, r: 9, w: 1, h: 2 };
|
| 1002 |
+
var WATER_FOLIAGE_RATE = 16;
|
| 1003 |
var TREES = [
|
| 1004 |
{ frame: [155, 3, 21, 25], ax: 0.48, ay: 0.96 },
|
| 1005 |
+
// tree 1 (plain)
|
| 1006 |
+
{ frame: [155, 35, 21, 25], ax: 0.48, ay: 0.96 },
|
| 1007 |
// tree 2 (fruited)
|
| 1008 |
+
{ frame: [152, 32, 24, 32], ax: 0.5, ay: 0.94 }
|
| 1009 |
+
// tree 3 — full 3×4 fruited (19,4)
|
| 1010 |
];
|
| 1011 |
var TREE_SCALE_BASE = 1;
|
| 1012 |
var TREE_SCALE_JITTER = 0.14;
|
|
|
|
| 1017 |
var FOREST_THRESHOLD = 0.6;
|
| 1018 |
var FOREST_MIN_NB = 2;
|
| 1019 |
var FOREST_FILL = 0.5;
|
| 1020 |
+
var STONES = [
|
| 1021 |
+
{ c: 8, r: 3, w: 2, h: 2, weight: 3 },
|
| 1022 |
+
// small rock
|
| 1023 |
+
{ c: 10, r: 3, w: 2, h: 2, weight: 3 },
|
| 1024 |
+
// small rock
|
| 1025 |
+
{ c: 12, r: 3, w: 2, h: 2, weight: 3 },
|
| 1026 |
+
// small flat rock
|
| 1027 |
+
{ c: 14, r: 3, w: 2, h: 2, weight: 3 },
|
| 1028 |
+
// small rock
|
| 1029 |
+
{ c: 16, r: 3, w: 2, h: 2, weight: 3 },
|
| 1030 |
+
// small rock
|
| 1031 |
+
{ c: 6, r: 3, w: 2, h: 3, weight: 2 },
|
| 1032 |
+
// medium boulder
|
| 1033 |
+
{ c: 0, r: 3, w: 2, h: 4, weight: 1 },
|
| 1034 |
+
// big boulder
|
| 1035 |
+
{ c: 2, r: 3, w: 2, h: 4, weight: 1 },
|
| 1036 |
+
// big boulder
|
| 1037 |
+
{ c: 4, r: 3, w: 2, h: 4, weight: 1 }
|
| 1038 |
+
// big knobbly boulder
|
| 1039 |
+
];
|
| 1040 |
+
var STONE_WEIGHT = STONES.reduce((s, g) => s + g.weight, 0);
|
| 1041 |
+
var STONE_BLOCK_X = 2;
|
| 1042 |
+
var STONE_BLOCK_Y = 2;
|
| 1043 |
+
var STONE_CLUMP_THRESHOLD = 0.7;
|
| 1044 |
+
var STONE_CLUMP_MIN_NB = 3;
|
| 1045 |
+
var STONE_CLUMP_FILL = 0.7;
|
| 1046 |
var COL_GRASS2 = [97, 150, 55];
|
| 1047 |
var COL_DIRT2 = [118, 80, 38];
|
| 1048 |
var COL_STONE = [120, 120, 122];
|
|
|
|
| 1099 |
return cur;
|
| 1100 |
};
|
| 1101 |
const waterF = clean((x, y) => isRiver(seed, x, y));
|
| 1102 |
+
const raisedField = clean((x, y) => isRaised(seed, x, y));
|
| 1103 |
+
for (let k = 0; k < waterF.length; k++) if (raisedField[k]) waterF[k] = 0;
|
| 1104 |
const dirtRaw = clean((x, y) => isDirt(seed, x, y));
|
| 1105 |
const stoneRaw = clean((x, y) => isStone(seed, x, y));
|
| 1106 |
const fraw = new Uint8Array(SZ * SZ);
|
|
|
|
| 1112 |
for (let dj = -1; dj <= 1; dj++) for (let di = -1; di <= 1; di++) if ((di || dj) && fraw[idx(i + di, j + dj)]) c++;
|
| 1113 |
forestRegion[idx(i, j)] = c >= FOREST_MIN_NB ? 1 : 0;
|
| 1114 |
}
|
| 1115 |
+
const sraw = new Uint8Array(SZ * SZ);
|
| 1116 |
+
for (let j = 0; j < SZ; j++) for (let i = 0; i < SZ; i++) sraw[idx(i, j)] = stoneClumpField(seed, x0 + i - M, y0 + j - M) > STONE_CLUMP_THRESHOLD ? 1 : 0;
|
| 1117 |
+
const stoneClumpRegion = new Uint8Array(SZ * SZ);
|
| 1118 |
+
for (let j = 1; j < SZ - 1; j++) for (let i = 1; i < SZ - 1; i++) {
|
| 1119 |
+
if (!sraw[idx(i, j)]) continue;
|
| 1120 |
+
let c = 0;
|
| 1121 |
+
for (let dj = -1; dj <= 1; dj++) for (let di = -1; di <= 1; di++) if ((di || dj) && sraw[idx(i + di, j + dj)]) c++;
|
| 1122 |
+
stoneClumpRegion[idx(i, j)] = c >= STONE_CLUMP_MIN_NB ? 1 : 0;
|
| 1123 |
+
}
|
| 1124 |
const dirt = new Uint8Array(SZ * SZ), stone = new Uint8Array(SZ * SZ);
|
| 1125 |
for (let j = WATER_BUFFER; j < SZ - WATER_BUFFER; j++) for (let i = WATER_BUFFER; i < SZ - WATER_BUFFER; i++) {
|
| 1126 |
let nearW = 0;
|
|
|
|
| 1128 |
nearW = 1;
|
| 1129 |
break;
|
| 1130 |
}
|
| 1131 |
+
const rais = raisedField[idx(i, j)];
|
| 1132 |
+
dirt[idx(i, j)] = dirtRaw[idx(i, j)] && !nearW && !rais ? 1 : 0;
|
| 1133 |
+
stone[idx(i, j)] = stoneRaw[idx(i, j)] && !dirtRaw[idx(i, j)] && !nearW && !rais ? 1 : 0;
|
| 1134 |
}
|
| 1135 |
+
return { waterF, dirt, stone, raisedField, forestRegion, stoneClumpRegion, M, SZ, x0, y0 };
|
| 1136 |
}
|
| 1137 |
var FP_GROUND_FILLS = [
|
| 1138 |
{ key: "grass", url: TILES2, tile: GRASS_BASE },
|
|
|
|
| 1183 |
// standalone map is unchanged; the multi-biome overworld passes a per-biome dither mask.
|
| 1184 |
bake({ x0, y0, seed: seed2, ctx, tmp, Sprite, tex, texFrame, add, accept = () => true }) {
|
| 1185 |
const f = chunkFields2(seed2, x0, y0);
|
| 1186 |
+
const { waterF, dirt, stone, raisedField, forestRegion, stoneClumpRegion, M, SZ } = f;
|
| 1187 |
const idx = (i, j) => j * SZ + i;
|
| 1188 |
const atW = (wx, wy) => waterF[idx(wx - x0 + M, wy - y0 + M)] === 1;
|
| 1189 |
const atD = (wx, wy) => dirt[idx(wx - x0 + M, wy - y0 + M)] === 1;
|
| 1190 |
const atS = (wx, wy) => stone[idx(wx - x0 + M, wy - y0 + M)] === 1;
|
| 1191 |
+
const isRaisedAt = (wx, wy) => raisedField[idx(wx - x0 + M, wy - y0 + M)] === 1;
|
| 1192 |
const place = (coord, tx, ty) => {
|
| 1193 |
add(ctx.tiles, coord[0], coord[1], tx, ty);
|
| 1194 |
if (ctx.shadow && (!ctx.shadowSet || ctx.shadowSet.has(coord[0] + "," + coord[1]))) {
|
|
|
|
| 1223 |
if (atS(wx, wy)) blob(atS, STONE_BLOCK, STONE_FILL, STONE_VARS, 2, wx, wy, tx, ty);
|
| 1224 |
if (atW(wx, wy)) blob(atW, WATER_BLOCK, WATER_FILL, WATER_VARS, 3, wx, wy, tx, ty);
|
| 1225 |
}
|
| 1226 |
+
const live = [];
|
| 1227 |
+
const propSprite = (spec, wx, wy) => {
|
| 1228 |
+
const sp = new Sprite(texFrame(ctx.props, spec.c * TILE3, (spec.r - spec.h + 1) * TILE3, spec.w * TILE3, spec.h * TILE3));
|
| 1229 |
+
sp.anchor.set(0.5, 1);
|
| 1230 |
+
sp.x = wx * TILE3 + TILE3 / 2;
|
| 1231 |
+
sp.y = (wy + 1) * TILE3;
|
| 1232 |
+
sp.zIndex = (wy + 1) * TILE3;
|
| 1233 |
+
return sp;
|
| 1234 |
+
};
|
| 1235 |
if (ctx.props) for (let ty = 0; ty < CHUNK2; ty++) for (let tx = 0; tx < CHUNK2; tx++) {
|
| 1236 |
if (!accept(tx, ty)) continue;
|
| 1237 |
const wx = x0 + tx, wy = y0 + ty;
|
| 1238 |
+
if (atW(wx, wy)) {
|
| 1239 |
+
if (hashU32(seed2 ^ 24301, wx, wy) % WATER_FOLIAGE_RATE === 0) live.push({ sprite: propSprite(WATER_FOLIAGE, wx, wy), shadow: null });
|
| 1240 |
+
continue;
|
| 1241 |
+
}
|
| 1242 |
+
if (atD(wx, wy) || atS(wx, wy)) continue;
|
| 1243 |
const h2 = hashU32(seed2 ^ 15733114, wx, wy);
|
| 1244 |
if (h2 % FOLIAGE_RATE !== 0) continue;
|
| 1245 |
let pick = (h2 >>> 8) % FOLIAGE_WEIGHT, g = FOLIAGE[0];
|
|
|
|
| 1250 |
}
|
| 1251 |
pick -= grp.weight;
|
| 1252 |
}
|
| 1253 |
+
if (g.sprite) {
|
| 1254 |
+
live.push({ sprite: propSprite(g.sprite, wx, wy), shadow: null });
|
| 1255 |
+
continue;
|
| 1256 |
+
}
|
| 1257 |
const [c, r] = g.tiles[(h2 >>> 16) % g.tiles.length];
|
| 1258 |
const sp = new Sprite(texFrame(ctx.props, c * TILE3, r * TILE3, TILE3, TILE3));
|
| 1259 |
sp.x = tx * TILE3;
|
| 1260 |
sp.y = ty * TILE3;
|
| 1261 |
tmp.addChild(sp);
|
| 1262 |
}
|
| 1263 |
+
bakeCliffs(FP_CLIFF, { chunk: CHUNK2, x0, y0, seed: seed2, raised: isRaisedAt, place, accept });
|
| 1264 |
if (ctx.props) {
|
| 1265 |
const inGrove = (wx, wy) => forestRegion[idx(wx - x0 + M, wy - y0 + M)] === 1;
|
| 1266 |
const bx0 = Math.floor(x0 / FOREST_BLOCK_X), bx1 = Math.floor((x0 + CHUNK2 - 1) / FOREST_BLOCK_X);
|
|
|
|
| 1273 |
if (!inGrove(wx, wy)) continue;
|
| 1274 |
if ((h2 >>> 8 & 65535) / 65536 >= FOREST_FILL) continue;
|
| 1275 |
if (atW(wx, wy) || atS(wx, wy)) continue;
|
| 1276 |
+
if (isRaisedAt(wx, wy)) continue;
|
| 1277 |
+
let onFace = false;
|
| 1278 |
+
for (let k = 1; k <= FP_CLIFF.FACE_H; k++) if (isRaisedAt(wx, wy - k)) {
|
| 1279 |
+
onFace = true;
|
| 1280 |
+
break;
|
| 1281 |
+
}
|
| 1282 |
+
if (onFace) continue;
|
| 1283 |
const T = TREES[(h2 >>> 24) % TREES.length];
|
| 1284 |
const flip = (h2 >>> 20 & 255) / 256 < TREE_FLIP_RATE;
|
| 1285 |
const sc = TREE_SCALE_BASE * (1 + ((h2 >>> 12 & 255) / 256 - 0.5) * 2 * TREE_SCALE_JITTER);
|
|
|
|
| 1302 |
live.push({ sprite: tr, shadow });
|
| 1303 |
}
|
| 1304 |
}
|
| 1305 |
+
if (ctx.props) {
|
| 1306 |
+
const inClump = (wx, wy) => stoneClumpRegion[idx(wx - x0 + M, wy - y0 + M)] === 1;
|
| 1307 |
+
const sbx0 = Math.floor(x0 / STONE_BLOCK_X), sbx1 = Math.floor((x0 + CHUNK2 - 1) / STONE_BLOCK_X);
|
| 1308 |
+
const sby0 = Math.floor(y0 / STONE_BLOCK_Y), sby1 = Math.floor((y0 + CHUNK2 - 1) / STONE_BLOCK_Y);
|
| 1309 |
+
for (let by = sby0; by <= sby1; by++) for (let bx = sbx0; bx <= sbx1; bx++) {
|
| 1310 |
+
const h2 = hashU32(seed2 ^ 5702885, bx, by);
|
| 1311 |
+
const wx = bx * STONE_BLOCK_X + h2 % STONE_BLOCK_X, wy = by * STONE_BLOCK_Y + (h2 >>> 4) % STONE_BLOCK_Y;
|
| 1312 |
+
if (wx < x0 || wx >= x0 + CHUNK2 || wy < y0 || wy >= y0 + CHUNK2) continue;
|
| 1313 |
+
if (!accept(wx - x0, wy - y0)) continue;
|
| 1314 |
+
if (!inClump(wx, wy)) continue;
|
| 1315 |
+
if ((h2 >>> 8 & 65535) / 65536 >= STONE_CLUMP_FILL) continue;
|
| 1316 |
+
if (atW(wx, wy) || isRaisedAt(wx, wy)) continue;
|
| 1317 |
+
let onFace = false;
|
| 1318 |
+
for (let k = 1; k <= FP_CLIFF.FACE_H; k++) if (isRaisedAt(wx, wy - k)) {
|
| 1319 |
+
onFace = true;
|
| 1320 |
+
break;
|
| 1321 |
+
}
|
| 1322 |
+
if (onFace) continue;
|
| 1323 |
+
let pick = (h2 >>> 20) % STONE_WEIGHT, st = STONES[0];
|
| 1324 |
+
for (const s of STONES) {
|
| 1325 |
+
if (pick < s.weight) {
|
| 1326 |
+
st = s;
|
| 1327 |
+
break;
|
| 1328 |
+
}
|
| 1329 |
+
pick -= s.weight;
|
| 1330 |
+
}
|
| 1331 |
+
live.push({ sprite: propSprite(st, wx, wy), shadow: null });
|
| 1332 |
+
}
|
| 1333 |
+
}
|
| 1334 |
return { meta: f, live };
|
| 1335 |
},
|
| 1336 |
macroColor(seed2, tx, ty) {
|
| 1337 |
+
const raised = isRaised(seed2, tx, ty);
|
| 1338 |
+
if (!raised && isRiver(seed2, tx, ty)) return COL_WATER;
|
| 1339 |
+
let c = isStone(seed2, tx, ty) && !isDirt(seed2, tx, ty) ? COL_STONE : isDirt(seed2, tx, ty) ? COL_DIRT2 : COL_GRASS2;
|
| 1340 |
+
if (raised) c = lerp3(c, COL_CLIFF, 0.25);
|
| 1341 |
+
return c;
|
| 1342 |
},
|
| 1343 |
tileIndexAt(wx, wy, meta) {
|
| 1344 |
if (!meta) return null;
|
|
|
|
| 1377 |
function isDark(seed, x, y) {
|
| 1378 |
return fbm(sub4(seed, 55852), x * DARK_SCALE, y * DARK_SCALE) > DARK_THRESHOLD;
|
| 1379 |
}
|
| 1380 |
+
var ELEV_SCALE2 = 0.025;
|
| 1381 |
+
var ELEV_THRESHOLD2 = 0.6;
|
| 1382 |
+
function isRaised2(seed, x, y) {
|
| 1383 |
+
return fbm(sub4(seed, 57836), x * ELEV_SCALE2, y * ELEV_SCALE2) > ELEV_THRESHOLD2;
|
| 1384 |
}
|
| 1385 |
var BONE_SCALE = 0.055;
|
| 1386 |
var BONE_THRESHOLD = 0.62;
|
|
|
|
| 1455 |
var FACE_L = [[1, 16], [1, 17]];
|
| 1456 |
var FACE_R = [[6, 16], [6, 17]];
|
| 1457 |
var CLIFF_SPARSE_RATE = 2;
|
| 1458 |
+
var NECRO_CLIFF = {
|
| 1459 |
+
LIP_T,
|
| 1460 |
+
LIP_T_VAR,
|
| 1461 |
+
LIP_TL,
|
| 1462 |
+
LIP_TR,
|
| 1463 |
+
WALL_L,
|
| 1464 |
+
WALL_L_VAR,
|
| 1465 |
+
WALL_R,
|
| 1466 |
+
WALL_R_VAR,
|
| 1467 |
+
LIP_S,
|
| 1468 |
+
LIP_S_VAR,
|
| 1469 |
+
LIP_SW,
|
| 1470 |
+
LIP_SE,
|
| 1471 |
+
FACE_H,
|
| 1472 |
+
FACE,
|
| 1473 |
+
FACE_VAR,
|
| 1474 |
+
FACE_L,
|
| 1475 |
+
FACE_R,
|
| 1476 |
+
RATE: CLIFF_SPARSE_RATE
|
| 1477 |
+
};
|
| 1478 |
var CORRUPT_LIGHT = [1, 1];
|
| 1479 |
var CORRUPT_DARK = [2, 1];
|
| 1480 |
var NECRO_GROUND_FILLS = [
|
|
|
|
| 1507 |
var COL_DARK = [105, 99, 113];
|
| 1508 |
var COL_WATER2 = [74, 142, 48];
|
| 1509 |
var COL_BONE = [180, 156, 126];
|
| 1510 |
+
var COL_CLIFF2 = [175, 146, 109];
|
| 1511 |
var COL_FOREST = [96, 101, 70];
|
| 1512 |
var NECRO_LAYERS = [
|
| 1513 |
["m-bg", "Background", "ground"],
|
|
|
|
| 1557 |
return [c0 + 1, r0 + 1];
|
| 1558 |
}
|
| 1559 |
function biomeColor(seed, tx, ty) {
|
| 1560 |
+
const raised = isRaised2(seed, tx, ty);
|
| 1561 |
if (!raised && isRiver2(seed, tx, ty)) return COL_WATER2;
|
| 1562 |
if (isBone(seed, tx, ty)) return COL_BONE;
|
| 1563 |
let c = isDark(seed, tx, ty) ? COL_DARK : COL_LIGHT;
|
| 1564 |
if (forestField2(seed, tx, ty) > FOREST_THRESHOLD2) c = lerp3(c, COL_FOREST, 0.5);
|
| 1565 |
+
if (raised) c = lerp3(c, COL_CLIFF2, 0.22);
|
| 1566 |
return c;
|
| 1567 |
}
|
| 1568 |
function loadImg2(url) {
|
|
|
|
| 1619 |
};
|
| 1620 |
const darkRaw = clean((x, y) => isDark(seed, x, y));
|
| 1621 |
const waterField = clean((x, y) => isRiver2(seed, x, y));
|
| 1622 |
+
const raisedField = clean((x, y) => isRaised2(seed, x, y));
|
| 1623 |
const boneField = clean((x, y) => isBone(seed, x, y));
|
| 1624 |
const fraw = new Uint8Array(SZ * SZ);
|
| 1625 |
for (let j = 0; j < SZ; j++) for (let i = 0; i < SZ; i++) fraw[idx(i, j)] = forestField2(seed, x0 + i - M, y0 + j - M) > FOREST_THRESHOLD2 ? 1 : 0;
|
|
|
|
| 1794 |
sp.y = ty * TILE4;
|
| 1795 |
tmp.addChild(sp);
|
| 1796 |
}
|
| 1797 |
+
bakeCliffs(NECRO_CLIFF, { chunk: CHUNK3, x0, y0, seed: seed2, raised: isRaisedAt, place, accept });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1798 |
const live = [];
|
| 1799 |
if (ctx.props) {
|
| 1800 |
const nearWater = (wx, wy) => {
|