function initWorld() {
world = new Array(WORLD_WIDTH);
for (let x = 0; x < WORLD_WIDTH; x++) {
world[x] = new Array(WORLD_HEIGHT).fill(AIR);
}
// Initialize world buffer FIRST, before any calls to generateVein or updateWorldBufferTile
worldBuffer = createGraphics(WORLD_WIDTH * TILE_SIZE, WORLD_HEIGHT * TILE_SIZE);
const baseHeight = 30;
for (let x = 0; x < WORLD_WIDTH; x++) {
// Generate terrain with Perlin noise
// https://p5js.org/reference/#/p5/noise
let h = floor(noise(x * 0.12) * 10) + baseHeight;
h = constrain(h, 12, WORLD_HEIGHT - 6);
// Stone underground
for (let y = h; y < WORLD_HEIGHT; y++) {
world[x][y] = STONE;
}
// Dirt and grass surface
if (h - 1 >= 0) world[x][h - 1] = GRASS;
if (h - 2 >= 0) world[x][h - 2] = DIRT;
if (h - 3 >= 0) world[x][h - 3] = DIRT;
// Random trees
if (random() < 0.08 && h - 4 > 0) {
const trunkHeight = floor(random(3, 6)); // Variable trunk height
for (let t = 0; t < trunkHeight; t++) {
const ty = h - 2 - t;
if (ty >= 0) world[x][ty] = WOOD;
}
// Leaves ball
const leafY = h - 2 - trunkHeight;
const leafRadius = floor(random(2, 4));
for (let lx = -leafRadius; lx <= leafRadius; lx++) {
for (let ly = -leafRadius; ly <= 1; ly++) {
const tx = x + lx;
const ty = leafY + ly;
if (tx >= 0 && tx < WORLD_WIDTH && ty >= 0 && ty < WORLD_HEIGHT) {
if (dist(lx, ly, 0, 0) < leafRadius + 0.6 && world[tx][ty] === AIR) {
world[tx][ty] = LEAVES;
}
}
}
}
}
// Generate coal and iron veins (now worldBuffer is initialized)
if (x > 10 && x < WORLD_WIDTH - 10) { // Avoid edges
if (random() < 0.04) { // Coal vein chance
generateVein(x, floor(random(h + 5, WORLD_HEIGHT - 10)), COAL, 3, 6);
}
if (random() < 0.02) { // Iron vein chance
generateVein(x, floor(random(h + 10, WORLD_HEIGHT - 15)), IRON, 2, 4);
}
}
// Add small water pools
if (x > 5 && x < WORLD_WIDTH - 5) {
if (random() < 0.015) { // Water pool chance
const poolHeight = floor(random(2, 4));
const poolWidth = floor(random(4, 8));
const poolY = h - poolHeight;
for (let pw = 0; pw < poolWidth; pw++) {
for (let ph = 0; ph < poolHeight; ph++) {
const tx = x + pw;
const ty = poolY + ph;
if (isInWorld(tx, ty) && world[tx][ty] === AIR) {
world[tx][ty] = WATER;
}
}
}
}
}
}
// Spawn player 1 near center, on top of terrain
const spawnX1 = floor(WORLD_WIDTH / 2);
let spawnY1 = 0;
for (let y = 0; y < WORLD_HEIGHT; y++) {
if (world[spawnX1][y] !== AIR && world[spawnX1][y] !== WATER) { // Spawn on solid ground
spawnY1 = y - 1;
break;
}
}
// Spawn player 2 slightly to the right of player 1
const spawnX2 = spawnX1 + 2; // 2 tiles to the right
let spawnY2 = 0;
for (let y = 0; y < WORLD_HEIGHT; y++) {
if (world[spawnX2][y] !== AIR && world[spawnX2][y] !== WATER) { // Spawn on solid ground
spawnY2 = y - 1;
break;
}
}
players = [
{ // Player 1
id: "player_1",
x: spawnX1 * TILE_SIZE,
y: spawnY1 * TILE_SIZE - PLAYER_HEIGHT,
w: PLAYER_WIDTH,
h: PLAYER_HEIGHT,
vx: 0,
vy: 0,
onGround: false,
inWater: false,
inventory: player1Inventory,
selectedIndex: 0,
controls: PLAYER_CONTROLS[0],
colorBody: PLAYER_CONTROLS[0].colorBody,
colorPants: PLAYER_CONTROLS[0].colorPants,
health: 100,
maxHealth: 100,
hitTimer: 0 // Timer to show hit effect
},
{ // Player 2
id: "player_2",
x: spawnX2 * TILE_SIZE,
y: spawnY2 * TILE_SIZE - PLAYER_HEIGHT,
w: PLAYER_WIDTH,
h: PLAYER_HEIGHT,
vx: 0,
vy: 0,
onGround: false,
inWater: false,
inventory: player2Inventory,
selectedIndex: 0,
controls: PLAYER_CONTROLS[1],
colorBody: PLAYER_CONTROLS[1].colorBody,
colorPants: PLAYER_CONTROLS[1].colorPants,
health: 100,
maxHealth: 100,
hitTimer: 0 // Timer to show hit effect
}
];
// Now that all world generation is done, draw the entire static world to the buffer
drawStaticWorldBuffer();
}