Spaces:
Running
Running
let hexSize = 40; | |
let hexGrid = []; | |
let buildings = []; | |
let train = { x: 0, y: 0, dir: 1 }; | |
let monsterMode = false; | |
let currentMonster = 'Godzilla'; | |
let monsterX, monsterY; | |
let angle = Math.PI / 6; | |
let debugText = "Loading..."; | |
let playerId = null; | |
const sqrt3 = Math.sqrt(3); | |
// Streamlit connection | |
const { Streamlit } = window; | |
Streamlit.events.addEventListener("message", (event) => { | |
const data = event.data; | |
hexGrid = data.game_state.hex_grid; | |
buildings = data.game_state.buildings; | |
train = data.game_state.train; | |
monsterMode = data.game_state.monster_mode; | |
currentMonster = data.game_state.current_monster; | |
monsterX = data.game_state.monster_x; | |
monsterY = data.game_state.monster_y; | |
playerId = data.player_id; | |
selectedEmoji = data.selected_emoji; | |
debugText = `Player ${playerId} joined. Click to build!`; | |
// Set train track on row 5 if not already set | |
for (let i = 0; i < hexGrid.length; i++) { | |
if (hexGrid[i][5].type === 'empty') hexGrid[i][5].type = 'track'; | |
} | |
}); | |
function setup() { | |
createCanvas(800, 600); | |
Streamlit.setFrameHeight(650); | |
} | |
function draw() { | |
background(220); | |
drawHexGrid(); | |
drawBuildings(); | |
drawTrain(); | |
if (monsterMode) drawMonster(); | |
fill(0); | |
textSize(12); | |
text(debugText, 10, 20); | |
} | |
function drawHexGrid() { | |
for (let i = 0; i < hexGrid.length; i++) { | |
for (let j = 0; j < hexGrid[i].length; j++) { | |
let x = i * hexSize * 1.5; | |
let y = j * hexSize * sqrt3 + (i % 2 === 1 ? hexSize * sqrt3 / 2 : 0); | |
let z = j * hexSize * Math.sin(angle); | |
fill(hexGrid[i][j].type === 'track' ? 100 : 150, 200, 150); | |
stroke(0); | |
beginShape(); | |
for (let k = 0; k < 6; k++) { | |
let angleRad = Math.PI / 3 * k; | |
vertex(x + hexSize * Math.cos(angleRad), y + hexSize * Math.sin(angleRad) - z); | |
} | |
endShape(CLOSE); | |
if (hexGrid[i][j].emoji) { | |
textSize(20); | |
text(hexGrid[i][j].emoji, x - 10, y + 5 - z); | |
} | |
} | |
} | |
} | |
function drawBuildings() { | |
buildings.forEach(b => { | |
let x = b.x * hexSize * 1.5; | |
let y = b.y * hexSize * sqrt3 + (b.x % 2 === 1 ? hexSize * sqrt3 / 2 : 0); | |
let z = b.y * hexSize * Math.sin(angle); | |
fill(b.color); | |
noStroke(); | |
beginShape(); | |
if (b.type === 'Residential') { | |
vertex(x + 10, y + 30 - z); vertex(x + 20, y + 10 - z); vertex(x + 30, y + 30 - z); | |
vertex(x + 25, y + 30 - z); vertex(x + 25, y + 35 - z); vertex(x + 15, y + 35 - z); | |
vertex(x + 15, y + 30 - z); vertex(x + 17, y + 25 - z); vertex(x + 23, y + 25 - z); | |
} else if (b.type === 'Commercial') { | |
vertex(x + 10, y + 35 - z); vertex(x + 15, y + 15 - z); vertex(x + 25, y + 15 - z); | |
vertex(x + 30, y + 35 - z); vertex(x + 27, y + 35 - z); vertex(x + 27, y + 20 - z); | |
vertex(x + 13, y + 20 - z); vertex(x + 13, y + 35 - z); | |
} else if (b.type === 'Industrial') { | |
vertex(x + 5, y + 35 - z); vertex(x + 15, y + 20 - z); vertex(x + 25, y + 20 - z); | |
vertex(x + 35, y + 35 - z); vertex(x + 30, y + 35 - z); vertex(x + 30, y + 15 - z); | |
vertex(x + 33, y + 15 - z); vertex(x + 33, y + 35 - z); | |
} else if (b.type === 'School') { | |
vertex(x + 5, y + 35 - z); vertex(x + 10, y + 20 - z); vertex(x + 30, y + 20 - z); | |
vertex(x + 35, y + 35 - z); vertex(x + 25, y + 35 - z); vertex(x + 25, y + 10 - z); | |
vertex(x + 27, y + 10 - z); vertex(x + 27, y + 35 - z); | |
} else if (b.type === 'PowerPlant') { | |
vertex(x + 5, y + 35 - z); vertex(x + 15, y + 15 - z); vertex(x + 25, y + 15 - z); | |
vertex(x + 35, y + 35 - z); vertex(x + 30, y + 35 - z); vertex(x + 30, y + 25 - z); | |
vertex(x + 20, y + 25 - z); vertex(x + 20, y + 35 - z); | |
} | |
endShape(CLOSE); | |
}); | |
} | |
function drawTrain() { | |
let tx = train.x; | |
let ty = train.y; | |
let tz = train.y * Math.sin(angle); | |
fill(150, 50, 50); | |
noStroke(); | |
beginShape(); | |
vertex(tx + 10, ty + 10 - tz); vertex(tx + 30, ty + 10 - tz); vertex(tx + 35, ty + 20 - tz); | |
vertex(tx + 30, ty + 30 - tz); vertex(tx + 10, ty + 30 - tz); vertex(tx + 5, ty + 20 - tz); | |
vertex(tx + 15, ty + 20 - tz); vertex(tx + 15, ty + 15 - tz); vertex(tx + 25, ty + 15 - tz); | |
vertex(tx + 25, ty + 20 - tz); | |
endShape(CLOSE); | |
train.x += train.dir * 2; | |
if (train.x > width || train.x < 0) train.dir *= -1; | |
saveState(); | |
} | |
function drawMonster() { | |
let mz = monsterY * Math.sin(angle); | |
fill(255, 0, 0); | |
noStroke(); | |
beginShape(); | |
if (currentMonster === 'Godzilla') { | |
vertex(monsterX, monsterY + 40 - mz); vertex(monsterX + 10, monsterY + 20 - mz); | |
vertex(monsterX + 20, monsterY - mz); vertex(monsterX + 30, monsterY + 20 - mz); | |
vertex(monsterX + 40, monsterY + 40 - mz); vertex(monsterX + 35, monsterY + 50 - mz); | |
vertex(monsterX + 25, monsterY + 60 - mz); vertex(monsterX + 15, monsterY + 50 - mz); | |
vertex(monsterX + 20, monsterY + 40 - mz); vertex(monsterX + 25, monsterY + 30 - mz); | |
} else if (currentMonster === 'GiantRobot') { | |
vertex(monsterX, monsterY + 40 - mz); vertex(monsterX + 10, monsterY + 20 - mz); | |
vertex(monsterX + 15, monsterY - mz); vertex(monsterX + 25, monsterY - mz); | |
vertex(monsterX + 30, monsterY + 20 - mz); vertex(monsterX + 40, monsterY + 40 - mz); | |
vertex(monsterX + 35, monsterY + 50 - mz); vertex(monsterX + 20, monsterY + 60 - mz); | |
vertex(monsterX + 5, monsterY + 50 - mz); vertex(monsterX + 15, monsterY + 30 - mz); | |
} | |
endShape(CLOSE); | |
monsterX += random(-5, 5); | |
monsterY += random(-5, 5); | |
monsterX = constrain(monsterX, 0, width); | |
monsterY = constrain(monsterY, 0, height); | |
saveState(); | |
} | |
function mousePressed() { | |
let i = Math.floor(mouseX / (hexSize * 1.5)); | |
let j = Math.floor((mouseY - (i % 2 === 1 ? hexSize * sqrt3 / 2 : 0)) / (hexSize * sqrt3)); | |
if (i >= 0 && i < hexGrid.length && j >= 0 && j < hexGrid[0].length) { | |
if (hexGrid[i][j].type === 'empty' && !monsterMode) { | |
let selectedEmoji = window.Streamlit.initialArgs.selected_emoji; | |
if (['π ', 'π‘', 'π’', 'π£', 'π€', 'π₯', 'π¦', 'π¨', 'π©', 'πͺ'].includes(selectedEmoji)) { | |
let types = ['Residential', 'Commercial', 'Industrial', 'School', 'PowerPlant']; | |
let colors = [[0, 200, 0], [0, 0, 200], [200, 200, 0], [200, 0, 200], [100, 100, 100]]; | |
let idx = Math.floor(Math.random() * 5); | |
buildings.push({ x: i, y: j, type: types[idx], color: colors[idx], player: playerId }); | |
hexGrid[i][j].type = 'building'; | |
} else { | |
hexGrid[i][j].type = 'placed'; | |
hexGrid[i][j].emoji = selectedEmoji; | |
} | |
debugText = `Player ${playerId} placed ${selectedEmoji} at (${i}, ${j})`; | |
saveState(); | |
} | |
} | |
} | |
function keyPressed() { | |
if (key === 'm' || key === 'M') { | |
monsterMode = !monsterMode; | |
debugText = `Monster Mode: ${monsterMode}`; | |
saveState(); | |
} | |
if (key === 'g' || key === 'G') { | |
currentMonster = 'Godzilla'; | |
debugText = "Monster set to Godzilla"; | |
saveState(); | |
} | |
if (key === 'r' || key === 'R') { | |
currentMonster = 'GiantRobot'; | |
debugText = "Monster set to Giant Robot"; | |
saveState(); | |
} | |
if (key === 't' || key === 'T') { | |
train.dir *= -1; | |
debugText = "Train direction reversed"; | |
saveState(); | |
} | |
} | |
function saveState() { | |
const state = { | |
hex_grid: hexGrid, | |
buildings: buildings, | |
train: train, | |
monster_mode: monsterMode, | |
current_monster: currentMonster, | |
monster_x: monsterX, | |
monster_y: monsterY | |
}; | |
Streamlit.setComponentValue(state); | |
} | |
function toggleMonster() { | |
monsterMode = !monsterMode; | |
debugText = `Monster Mode: ${monsterMode}`; | |
saveState(); | |
} | |
function setMonster(monster) { | |
currentMonster = monster; | |
debugText = `Monster set to ${monster}`; | |
saveState(); | |
} | |
function reverseTrain() { | |
train.dir *= -1; | |
debugText = "Train direction reversed"; | |
saveState(); | |
} |