// ============================================================= // ANTHILL - Colony Simulator v0.2 // Build mode: tubes + chambers, energy currency // No backtick or backslash chars (Node-RED / endpoint safe) // ============================================================= (function () { var NL = String.fromCharCode(10); // =========================================================== // API // =========================================================== var API = { endpoint: null, _req: function (m, p, b, cb) { if (!API.endpoint) { if (cb) cb(null, {error: 'no endpoint'}); return; } var o = {method: m, headers: {'Content-Type': 'application/json'}}; if (b) o.body = JSON.stringify(b); fetch(API.endpoint + p, o).then(function(r){return r.json();}).then(function(d){if(cb)cb(d,null);}).catch(function(e){if(cb)cb(null,e);}); }, saveColony: function(id,s,cb){ API._req('POST','/colony/'+id,s,cb); }, loadColony: function(id,cb) { API._req('GET', '/colony/'+id,null,cb); }, saveMap: function(id,m,cb){ API._req('POST','/colony/'+id+'/map',m,cb); }, loadMap: function(id,cb) { API._req('GET', '/colony/'+id+'/map',null,cb); } }; // =========================================================== // CONSTANTS // =========================================================== var CW = 960; var CH = 640; var SURF_Y = 150; var MOUND_CX = CW / 2; var GRID = 200; // px per tunnel grid cell (5x, curved) var SNAP_R = 60; // snap radius in px var POOL = 300; var START_E = 500; var COSTS = { tube_v: 15, tube_h: 15, ch_queen: 200, ch_brood: 75, ch_food: 75, ch_waste: 50 }; var CHAMBER_RADIUS = { ch_queen: 42, ch_brood: 32, ch_food: 32, ch_waste: 26 }; var CHAMBER_COLORS = { ch_queen: 0x6a2400, ch_brood: 0x3a2800, ch_food: 0x2a2000, ch_waste: 0x201810 }; // =========================================================== // TUNNEL SYSTEM // nodes: [{id,x,y}] pixel positions // segs: [{from,to}] pairs of node IDs // chambers: [{id,type,x,y,r,connNodeId}] // connSegs: [{x1,y1,x2,y2,x3,y3}] L-shaped connection paths // =========================================================== var TS = { nodes: [], segs: [], chambers: [], connPaths: [], _nid: 0, _cid: 0, init: function (x, y) { this.nodes = [{id: 0, x: x, y: y}]; this.segs = []; this.chambers = []; this.connPaths = []; this._nid = 1; this._cid = 0; }, freeEndpoints: function () { // Count only OUTGOING connections (tubes started FROM a node). // A node shows as connectable while it has < 2 outgoing tubes, // allowing one straight extension plus one branch per junction. var out = {}; this.nodes.forEach(function(n){ out[n.id] = 0; }); this.segs.forEach(function(s){ out[s.from] = (out[s.from] || 0) + 1; }); return this.nodes.filter(function(n){ return (out[n.id] || 0) < 2; }); }, nearestFree: function (px, py, excludeAboveSurf) { var best = null, bestD = Infinity; this.freeEndpoints().forEach(function(n) { if (excludeAboveSurf && n.y <= SURF_Y) return; var d = Math.hypot(n.x - px, n.y - py); if (d < bestD) { bestD = d; best = n; } }); return bestD <= SNAP_R * 3 ? best : null; }, addTubeV: function (fromNode) { var nx = {id: this._nid++, x: fromNode.x, y: fromNode.y + GRID}; var bd = (Math.random() > 0.5) ? 1 : -1; this.nodes.push(nx); this.segs.push({from: fromNode.id, to: nx.id, kind: 'v', bd: bd}); return nx; }, addTubeH: function (fromNode, dir) { var nx = {id: this._nid++, x: fromNode.x + dir * GRID, y: fromNode.y}; var bd = (Math.random() > 0.5) ? 1 : -1; this.nodes.push(nx); this.segs.push({from: fromNode.id, to: nx.id, kind: 'h', dir: dir, bd: bd}); return nx; }, nearestNode: function (px, py) { var best = null, bestD = Infinity; this.nodes.forEach(function(n) { var d = Math.hypot(n.x - px, n.y - py); if (d < bestD) { bestD = d; best = n; } }); return best; }, addChamber: function (type, px, py) { // Connect only to a free endpoint (same rule as tubes) var fps = this.freeEndpoints(); var conn = null, bestD = Infinity; fps.forEach(function(n) { var d = Math.hypot(n.x - px, n.y - py); if (d < bestD) { bestD = d; conn = n; } }); var r = CHAMBER_RADIUS[type] || 30; var ch = {id: this._cid++, type: type, x: px, y: py, r: r, connNodeId: conn ? conn.id : -1}; this.chambers.push(ch); if (conn) { // L-shaped connection: go horizontal then vertical this.connPaths.push({ x1: px, y1: py, x2: conn.x, y2: py, x3: conn.x, y3: conn.y }); } return ch; } }; // =========================================================== // COLONY SCENE // =========================================================== var ColonyScene = new Phaser.Class({ Extends: Phaser.Scene, initialize: function ColonyScene () { Phaser.Scene.call(this, {key: 'ColonyScene'}); this.energy = START_E; this.antPool = []; this.antPositions = null; this.antCount = 0; this.worker = null; this.tunnelGfx = null; this.ghostGfx = null; this.placing = null; // null | {type, snapNode, valid} this.pendingCh = null; // chamber being positioned before confirm this.arrowBtns = null; this.cursors = null; }, preload: function () {}, create: function () { TS.init(MOUND_CX, SURF_Y + 4); this._drawTerrain(); this._drawMound(); this.tunnelGfx = this.add.graphics().setDepth(4); this.ghostGfx = this.add.graphics().setDepth(20); this._redrawTunnels(); this._makeAntTextures(); // _drawAntSamples removed - use console: window.AntHill.showCastes() this._buildAntPool(); this._initWorker(); this._setupInput(); this._updatePanel(); window.antHillReady = true; window.antHillPlace = this._startPlacing.bind(this); window.antHillCancel = this._cancelPlacing.bind(this); }, // ------------------------------------------------------- // TERRAIN (drawn once, static) // ------------------------------------------------------- _drawTerrain: function () { var bg = this.add.graphics().setDepth(0); // Sky var skyBands = [0x6ab4e8, 0x88c8f0, 0xa8daf8, 0xc4eaff]; var bh = SURF_Y / skyBands.length; for (var i = 0; i < skyBands.length; i++) { bg.fillStyle(skyBands[i]); bg.fillRect(0, i * bh, CW, bh + 1); } // Grass bg.fillStyle(0x58a028); bg.fillRect(0, SURF_Y - 16, CW, 18); bg.fillStyle(0x407818); bg.fillRect(0, SURF_Y - 4, CW, 6); // Underground - flat dark soil, no features bg.fillStyle(0x1e0e04); bg.fillRect(0, SURF_Y, CW, CH - SURF_Y); }, // ------------------------------------------------------- // ANT MOUND - trapezoid with flat top + central shaft // ------------------------------------------------------- _drawMound: function () { var cx = MOUND_CX; var sy = SURF_Y; var topY = sy - 70; var hTop = 24; // half-width at top var hBot = 75; // half-width at base // Shadow under mound var sg = this.add.graphics().setDepth(1); sg.fillStyle(0x140800, 0.6); sg.fillEllipse(cx, sy + 5, hBot * 2 + 10, 14); // Mound body (trapezoid) var mg = this.add.graphics().setDepth(2); mg.fillStyle(0x7c4212); mg.fillPoints([ {x: cx - hBot, y: sy}, {x: cx - hTop, y: topY}, {x: cx + hTop, y: topY}, {x: cx + hBot, y: sy} ], true); // Right-face shading (darker) mg.fillStyle(0x542c0a, 0.6); mg.fillPoints([ {x: cx + hTop, y: topY}, {x: cx + hBot, y: sy}, {x: cx + hBot - 12, y: sy}, {x: cx + hTop - 8, y: topY} ], true); // Left-face highlight (lighter) mg.fillStyle(0xa05a1e, 0.4); mg.fillPoints([ {x: cx - hBot, y: sy}, {x: cx - hTop, y: topY}, {x: cx - hTop + 10, y: topY}, {x: cx - hBot + 14, y: sy} ], true); // Flat top surface mg.fillStyle(0x8a4a14); mg.fillRect(cx - hTop, topY - 3, hTop * 2, 5); // Central shaft (drawn over mound body at depth 3) var shaft = this.add.graphics().setDepth(3); shaft.lineStyle(14, 0x5a3010, 1); shaft.beginPath(); shaft.moveTo(cx, topY); shaft.lineTo(cx, sy + 6); shaft.strokePath(); shaft.lineStyle(7, 0x180800, 1); shaft.beginPath(); shaft.moveTo(cx, topY); shaft.lineTo(cx, sy + 6); shaft.strokePath(); // Entrance hole at flat top shaft.fillStyle(0x0e0400); shaft.fillEllipse(cx, topY + 1, 16, 9); shaft.fillStyle(0x060100); shaft.fillEllipse(cx, topY + 3, 9, 5); }, // ------------------------------------------------------- // TUNNEL RENDERING (redrawn on every build action) // ------------------------------------------------------- _redrawTunnels: function () { var g = this.tunnelGfx; g.clear(); // Draw segments as S-bend bezier curves var BEND = 55; function drawSeg(gfx, s, thick, col) { var n1 = TS.nodes[s.from], n2 = TS.nodes[s.to]; if (!n1 || !n2) return; gfx.lineStyle(thick, col, 1); var path = new Phaser.Curves.Path(n1.x, n1.y); var b = (s.bd || 1) * BEND; if (s.kind === 'h') { var dx = n2.x - n1.x; path.cubicBezierTo(n2.x, n2.y, n1.x + dx/3, n1.y + b, n1.x + 2*dx/3, n1.y - b); } else { var dy = n2.y - n1.y; path.cubicBezierTo(n2.x, n2.y, n1.x + b, n1.y + dy/3, n1.x - b, n1.y + 2*dy/3); } path.draw(gfx, 48); } TS.segs.forEach(function(s){ drawSeg(g, s, 26, 0x5a3010); }); TS.segs.forEach(function(s){ drawSeg(g, s, 20, 0x200c00); }); // Draw chamber connections (L-shaped) g.lineStyle(26, 0x5a3010, 1); TS.connPaths.forEach(function (p) { g.beginPath(); g.moveTo(p.x1, p.y1); g.lineTo(p.x2, p.y2); g.lineTo(p.x3, p.y3); g.strokePath(); }); g.lineStyle(20, 0x200c00, 1); TS.connPaths.forEach(function (p) { g.beginPath(); g.moveTo(p.x1, p.y1); g.lineTo(p.x2, p.y2); g.lineTo(p.x3, p.y3); g.strokePath(); }); // Draw chambers TS.chambers.forEach(function (ch) { var col = CHAMBER_COLORS[ch.type] || 0x3a2010; g.fillStyle(col); g.fillEllipse(ch.x, ch.y, ch.r * 2.2, ch.r * 1.6); g.fillStyle(0x100800); g.fillEllipse(ch.x, ch.y, ch.r * 1.5, ch.r * 1.0); }); // Draw free-endpoint dots (snap targets) g.fillStyle(0xd0a040, 0.6); TS.freeEndpoints().forEach(function (n) { if (n.y > SURF_Y) { g.fillCircle(n.x, n.y, 5); } }); }, // ------------------------------------------------------- // ANT TEXTURES (minor, worker, major, queen) // All face right: gaster(left) - thorax(mid) - head(right) // ------------------------------------------------------- _makeAntTextures: function () { var g = this.add.graphics(); // Ants face RIGHT (top-down view). All castes share the queen's colour. // Antennae splay outward from head tip; mandibles are short fat pincers. // -- MINOR (14 x 8) head center (12, 4) r=2 -- g.clear(); g.fillStyle(0x1c0900); g.fillEllipse(4, 4, 6, 5); g.fillEllipse(8.5, 4, 4, 3.5); g.fillCircle(12, 4, 2); g.lineStyle(0.8, 0x1c0900, 1); g.lineBetween(8,2.5,6,0); g.lineBetween(8.5,2.5,8.5,0); g.lineBetween(9,2.5,11,1); g.lineBetween(8,5.5,6,7.5); g.lineBetween(8.5,5.5,8.5,7.5);g.lineBetween(9,5.5,11,7); // antennae: top-down splay from head tip g.lineStyle(1, 0x1c0900, 1); g.lineBetween(13,3,13.5,2); g.lineBetween(13,5,13.5,6); g.generateTexture('ant_minor', 14, 8); // -- WORKER (18 x 10) head center (14.5, 5) r=3 -- g.clear(); g.fillStyle(0x180700); g.fillEllipse(5, 5, 8, 6); g.fillEllipse(10, 5, 5, 4.5); g.fillCircle(14.5, 5, 3); g.lineStyle(1, 0x180700, 1); g.lineBetween(9,3,7,0); g.lineBetween(10,3,10,0); g.lineBetween(11,3,13,1); g.lineBetween(9,7,7,9.5); g.lineBetween(10,7,10,9.5);g.lineBetween(11,7,13,9); // antennae: top-down splay from head tip, symmetric about y=5 g.lineBetween(15.5,4,17,2.3); g.lineBetween(15.5,6,17,7.7); g.generateTexture('ant_worker', 18, 10); // -- MAJOR (28 x 14) head center (18, 7) r=5 -- g.clear(); g.fillStyle(0x140600); g.fillEllipse(5, 7, 9, 8); g.fillEllipse(12, 7, 6, 6); g.fillCircle(18, 7, 5); g.lineStyle(1.5, 0x140600, 1); g.lineBetween(11,4,8,1); g.lineBetween(12,4,12,0); g.lineBetween(13,4,16,2); g.lineBetween(11,10,8,13); g.lineBetween(12,10,12,13);g.lineBetween(13,10,16,12); // antennae: top-down splay, symmetric about y=7 g.lineBetween(21.5,5,25,3.3); g.lineBetween(21.5,9,25,10.7); // mandibles: short fat pincer g.lineStyle(3, 0x140600, 1); g.lineBetween(23,6.5,25,5.5); g.lineBetween(23,7.5,25,8.5); g.generateTexture('ant_major', 28, 14); // -- QUEEN (36 x 20) head center (27, 10) r=4.5 -- g.clear(); g.fillStyle(0x3c1200); g.fillEllipse(9, 10, 16, 14); g.fillEllipse(21, 10, 9, 8); g.fillCircle(27, 10, 4.5); g.fillStyle(0x2a0e00); g.fillCircle(17.5, 10, 2.5); g.fillStyle(0x3c1200); g.lineStyle(1.5, 0x3c1200, 1); g.lineBetween(20,6,17,2); g.lineBetween(21,6,21,1); g.lineBetween(22,6,25,3); g.lineBetween(20,14,17,18); g.lineBetween(21,14,21,19);g.lineBetween(22,14,25,17); // antennae: top-down splay from head tip, symmetric about y=10 g.lineBetween(30,8.5,34,6.7); g.lineBetween(30,11.5,34,13.3); g.fillStyle(0x5a1e06, 0.5); g.fillEllipse(7, 8, 7, 5); g.generateTexture('ant_queen', 36, 20); g.destroy(); }, // ------------------------------------------------------- // STATIC CASTE SAMPLES // Full-width horizontal band just below ground level // ------------------------------------------------------- _drawAntSamples: function () { var sc = this; var colW = CW / 4; var panY = SURF_Y + 10; var panH = 120; // Full-width white background band var bg = this.add.graphics().setDepth(6); bg.fillStyle(0xffffff, 1); bg.fillRect(0, panY, CW, panH); bg.lineStyle(1, 0xcccccc, 1); bg.lineBetween(0, panY, CW, panY); bg.lineBetween(0, panY + panH, CW, panY + panH); // Column dividers for (var d = 1; d < 4; d++) { bg.lineStyle(1, 0xdddddd, 1); bg.lineBetween(colW * d, panY + 6, colW * d, panY + panH - 6); } // Section label this.add.text(CW / 2, panY + 4, 'COLONY CASTES', { fontSize: '8px', color: '#888888', fontFamily: 'monospace', letterSpacing: 3 }).setOrigin(0.5, 0).setDepth(7); var castes = [ {key: 'ant_minor', label: 'MINOR', role: 'Scout / Nurse', col: '#444444'}, {key: 'ant_worker', label: 'WORKER', role: 'Forager', col: '#333333'}, {key: 'ant_major', label: 'MAJOR', role: 'Soldier', col: '#222222'}, {key: 'ant_queen', label: 'QUEEN', role: 'Colony founder', col: '#111111'} ]; castes.forEach(function (c, i) { var cx = colW * i + colW / 2; var antY = panY + 52; var nameY = panY + 68; var roleY = panY + 81; sc.add.image(cx, antY, c.key).setDepth(7).setScale(1); sc.add.text(cx, nameY, c.label, { fontSize: '10px', color: c.col, fontFamily: 'monospace', letterSpacing: 2 }).setOrigin(0.5, 0).setDepth(7); sc.add.text(cx, roleY, c.role, { fontSize: '8px', color: '#777777', fontFamily: 'monospace' }).setOrigin(0.5, 0).setDepth(7); }); }, _buildAntPool: function () { this.antPool = []; for (var i = 0; i < POOL; i++) { this.antPool.push(this.add.image(0,-999,'ant_worker').setDepth(10).setVisible(false)); } }, // ------------------------------------------------------- // WORKER // ------------------------------------------------------- _initWorker: function () { var src = document.getElementById('ant-worker-src').textContent; var blob = new Blob([src], {type: 'application/javascript'}); var url = URL.createObjectURL(blob); this.worker = new Worker(url); URL.revokeObjectURL(url); var sc = this; this.worker.onmessage = function (e) { var msg = e.data; if (msg.type === 'positions') { sc.antPositions = msg.buf; sc.antCount = msg.count; } }; this.worker.postMessage({type:'init', x: MOUND_CX, y: SURF_Y + 30, antCount: 0}); }, // ------------------------------------------------------- // INPUT // ------------------------------------------------------- _setupInput: function () { var sc = this; this.input.on('pointermove', function (ptr) { sc._onMouseMove(ptr.x, ptr.y); }); this.input.on('pointerdown', function (ptr) { if (ptr.rightButtonDown()) { sc._cancelPlacing(); return; } sc._onClick(ptr.x, ptr.y); }); this.input.keyboard.on('keydown-ESC', function () { sc._cancelPlacing(); }); }, // ------------------------------------------------------- // PLACEMENT FLOW // ------------------------------------------------------- _startPlacing: function (type) { // Deselect if same if (this.placing && this.placing.type === type) { this._cancelPlacing(); return; } this._clearPendingChamber(); this.placing = {type: type, snapNode: null, valid: false}; // Highlight selected panel item var all = document.querySelectorAll('.build-item'); for (var i = 0; i < all.length; i++) all[i].classList.remove('active'); var el = document.getElementById('item-' + type); if (el) el.classList.add('active'); var cancelBtn = document.getElementById('cancel-btn'); if (cancelBtn) cancelBtn.classList.add('visible'); this._setStatus('Click a yellow dot to connect a new ' + (type.indexOf('tube') === 0 ? 'tube' : 'chamber') + '.'); }, _cancelPlacing: function () { this.placing = null; this._clearPendingChamber(); this.ghostGfx.clear(); var all = document.querySelectorAll('.build-item'); for (var i = 0; i < all.length; i++) all[i].classList.remove('active'); var cancelBtn = document.getElementById('cancel-btn'); if (cancelBtn) cancelBtn.classList.remove('visible'); this._setStatus('Select an item to begin building.'); }, _onMouseMove: function (mx, my) { if (!this.placing) return; var g = this.ghostGfx; g.clear(); var type = this.placing.type; if (type === 'tube_v' || type === 'tube_h') { var snap = TS.nearestFree(mx, my, true); this.placing.snapNode = snap; this.placing.valid = !!snap && this.energy >= COSTS[type]; if (snap) { var ex = snap.x, ey = snap.y; var tx = snap.x, ty = snap.y; if (type === 'tube_v') { ty = snap.y + GRID; } else { tx = snap.x + (mx >= snap.x ? GRID : -GRID); } var col = this.placing.valid ? 0x80ff40 : 0xff4020; g.lineStyle(10, col, 0.7); g.beginPath(); g.moveTo(ex, ey); g.lineTo(tx, ty); g.strokePath(); g.fillStyle(col, 0.85); g.fillCircle(tx, ty, 7); } } else { // Chamber ghost follows mouse if (this.pendingCh) return; var r = CHAMBER_RADIUS[type] || 30; var col = (my > SURF_Y && this.energy >= COSTS[type]) ? 0x80ff80 : 0xff4444; g.fillStyle(col, 0.35); g.fillEllipse(mx, my, r * 2.2, r * 1.6); g.lineStyle(2, col, 0.8); g.strokeEllipse(mx, my, r * 2.2, r * 1.6); } }, _onClick: function (mx, my) { if (!this.placing) return; var type = this.placing.type; var cost = COSTS[type]; if (type === 'tube_v' || type === 'tube_h') { var snap = this.placing.snapNode; if (!snap) { this._setStatus('Click a yellow dot to connect.'); return; } if (this.energy < cost) { this._setStatus('Not enough energy!'); return; } if (type === 'tube_v') { TS.addTubeV(snap); } else { var dir = mx >= snap.x ? 1 : -1; TS.addTubeH(snap, dir); } this.energy -= cost; this._redrawTunnels(); this._updatePanel(); this.ghostGfx.clear(); this._setStatus('Tube placed! Click another dot to continue.'); } else { // Chamber - first click drops the pending chamber if (this.pendingCh) return; if (my <= SURF_Y) { this._setStatus('Chambers must go underground.'); return; } if (this.energy < cost) { this._setStatus('Not enough energy!'); return; } this.placing.valid = true; this.pendingCh = {type: type, x: mx, y: my}; this.ghostGfx.clear(); this._showChamberArrows(); this._setStatus('Use arrows to position, then click Place.'); } }, // ------------------------------------------------------- // CHAMBER ARROW CONTROLS // ------------------------------------------------------- _showChamberArrows: function () { var sc = this; var ch = this.pendingCh; var r = CHAMBER_RADIUS[ch.type] || 30; var col = CHAMBER_COLORS[ch.type] || 0x3a2010; // Draw ghost chamber var g = this.ghostGfx; g.clear(); g.fillStyle(col, 0.6); g.fillEllipse(ch.x, ch.y, r * 2.2, r * 1.6); g.lineStyle(2, 0xd0a040, 0.9); g.strokeEllipse(ch.x, ch.y, r * 2.2, r * 1.6); var btnStyle = { fontSize: '18px', color: '#f0d060', backgroundColor: '#2a1404', padding: {x: 8, y: 4} }; var okStyle = { fontSize: '13px', color: '#60ff60', backgroundColor: '#0a2004', padding: {x: 10, y: 5} }; var offset = r + 22; this.arrowBtns = [ this.add.text(ch.x, ch.y - offset, '^', btnStyle).setOrigin(0.5).setDepth(30).setInteractive(), this.add.text(ch.x, ch.y + offset, 'v', btnStyle).setOrigin(0.5).setDepth(30).setInteractive(), this.add.text(ch.x - offset, ch.y, '<', btnStyle).setOrigin(0.5).setDepth(30).setInteractive(), this.add.text(ch.x + offset, ch.y, '>', btnStyle).setOrigin(0.5).setDepth(30).setInteractive(), this.add.text(ch.x, ch.y, 'Place', okStyle).setOrigin(0.5).setDepth(30).setInteractive() ]; var moves = [[0,-GRID],[0,GRID],[-GRID,0],[GRID,0],null]; this.arrowBtns.forEach(function (btn, idx) { btn.on('pointerover', function(){ btn.setAlpha(0.7); }); btn.on('pointerout', function(){ btn.setAlpha(1.0); }); if (idx < 4) { btn.on('pointerdown', function() { sc._movePendingChamber(moves[idx][0], moves[idx][1]); }); } else { btn.on('pointerdown', function() { sc._confirmChamber(); }); } }); }, _movePendingChamber: function (dx, dy) { if (!this.pendingCh) return; this.pendingCh.x += dx; this.pendingCh.y += dy; // Clamp underground if (this.pendingCh.y < SURF_Y + 20) this.pendingCh.y = SURF_Y + 20; this._clearArrows(); this._showChamberArrows(); }, _confirmChamber: function () { if (!this.pendingCh) return; var ch = this.pendingCh; var cost = COSTS[ch.type]; TS.addChamber(ch.type, ch.x, ch.y); this.energy -= cost; this._clearPendingChamber(); this._redrawTunnels(); this._updatePanel(); this._setStatus('Chamber placed and connected!'); // Clear placing mode this._cancelPlacing(); }, _clearArrows: function () { if (this.arrowBtns) { this.arrowBtns.forEach(function(b){ b.destroy(); }); this.arrowBtns = null; } this.ghostGfx.clear(); }, _clearPendingChamber: function () { this._clearArrows(); this.pendingCh = null; }, // ------------------------------------------------------- // HTML PANEL UPDATES // ------------------------------------------------------- _updatePanel: function () { var ev = document.getElementById('energy-val'); if (ev) ev.textContent = this.energy; var items = Object.keys(COSTS); var sc = this; items.forEach(function (id) { var el = document.getElementById('item-' + id); if (!el) return; if (sc.energy < COSTS[id]) { el.classList.add('disabled'); } else { el.classList.remove('disabled'); } }); }, _setStatus: function (msg) { var el = document.getElementById('status-msg'); if (el) el.textContent = msg; }, // ------------------------------------------------------- // MAIN UPDATE // ------------------------------------------------------- update: function () { this._updateAntSprites(); }, _updateAntSprites: function () { if (!this.antPositions) return; var pool = this.antPool; var n = Math.min(this.antCount, pool.length); var buf = this.antPositions; // Ants at or above SURF_Y are inside the mound or on surface - hide them var HIDE_Y = SURF_Y + 2; for (var i = 0; i < n; i++) { var sp = pool[i], b = i * 4; var onSurface = buf[b+1] < HIDE_Y; if (onSurface) { if (sp.visible) sp.setVisible(false); } else { sp.x = buf[b]; sp.y = buf[b+1]; sp.rotation = buf[b+2]; if (!sp.visible) sp.setVisible(true); } } for (var j = n; j < pool.length; j++) { if (pool[j].visible) pool[j].setVisible(false); } } }); // =========================================================== // BOOT // =========================================================== var config = { type: Phaser.AUTO, width: CW, height: CH, backgroundColor: '#060300', parent: 'game-container', scale: { mode: Phaser.Scale.FIT, autoCenter: Phaser.Scale.CENTER_BOTH }, scene: [ColonyScene] }; var game = new Phaser.Game(config); window.AntHill = { game: game, api: API, ts: TS }; })();