Dev console: money, items, upgrades, free build, speed (t66)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -57,7 +57,7 @@ function buildVariantChips(P) {
|
||||
const dirChip = !P.dirs ? '' : dragging ? `<span class="chip ok">${glyph(slopeDir || P.dirs[GAME.dragDir])} ${esc(slopeDir || P.dirs[GAME.dragDir])} · from your drag</span>` : `<span class="chip hot">${glyph(slopeDir || dir)} ${esc(slopeDir || dir)} · R</span>`;
|
||||
const fc = GAME.filterCfg;
|
||||
const sws = ms => ms.slice(0, 6).map(m => `<span class="sw" style="display:inline-block;vertical-align:-2px;width:9px;height:9px;margin-right:2px;background:${rgbOf(m)}"></span>`).join('') + (ms.length > 6 ? `+${ms.length - 6}` : '');
|
||||
return pieceUsesWall(P) ? `<button class="chip hot" data-wall-picker><span class="sw" style="display:inline-block;vertical-align:-2px;width:9px;height:9px;margin-right:4px;background:${rgbOf(WALL_OF[GAME.wallMat])}"></span>${esc(MD[WALL_OF[GAME.wallMat]].name.toLowerCase())} · R · F</button>${P.code === BP_WALL ? `<span class="chip hot">${['square', '◣ lower left', '◢ lower right', '◤ upper left', '◥ upper right'][isSlope(pieceCode(P)) ? pieceCode(P) - BP_SLOPE_BL + 1 : 0]} · T · turn / flip</span>` : ''}`
|
||||
return pieceUsesWall(P) ? `<button class="chip hot" data-wall-picker=""><span class="sw" style="display:inline-block;vertical-align:-2px;width:9px;height:9px;margin-right:4px;background:${rgbOf(WALL_OF[GAME.wallMat])}"></span>${esc(MD[WALL_OF[GAME.wallMat]].name.toLowerCase())} · R · F</button>${P.code === BP_WALL ? `<span class="chip hot">${['square', '◣ lower left', '◢ lower right', '◤ upper left', '◥ upper right'][isSlope(pieceCode(P)) ? pieceCode(P) - BP_SLOPE_BL + 1 : 0]} · T · turn / flip</span>` : ''}`
|
||||
: P.filter ? `<span class="chip hot">${fc.inv ? 'carries' : 'drops'} ${fc.m.length ? sws(fc.m) : 'nothing'} · F</span>${dirChip}`
|
||||
: dir ? dirChip : '';
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
// Bottom panel; command dispatch and state live in sim/devconsole.js.
|
||||
function closeDevConsole() {
|
||||
$('dev-console').hidden = true;
|
||||
$('dev-input').blur();
|
||||
}
|
||||
function showDevConsole() {
|
||||
$('dev-console').hidden = false;
|
||||
if (!$('dev-output').children.length) devPrint(['Type help to list commands. Backtick or Escape closes this panel.']);
|
||||
$('dev-input').focus();
|
||||
}
|
||||
function toggleDevConsole() {
|
||||
if (!GAME.running || GAME.loading) return;
|
||||
if (!$('dev-console').hidden) { closeDevConsole(); return; }
|
||||
if (devWorld) { showDevConsole(); return; }
|
||||
dialog('Enable dev console?', '<p>This save will be permanently marked as a dev-console world. If achievements are added later, they will not unlock in this world.</p><p>You can change money, materials, upgrades and game speed. These commands are logged but cannot be undone.</p>', [
|
||||
{ label: 'Cancel' },
|
||||
{ label: 'Enable dev console', cls: 'primary', fn: () => {
|
||||
enableDevConsole(); closeModal('m-dialog'); showDevConsole(); saveWorld('main'); return true;
|
||||
} },
|
||||
]);
|
||||
}
|
||||
function devPrint(lines) {
|
||||
const log = $('dev-output');
|
||||
for (const line of lines) { const row = document.createElement('div'); row.textContent = line; log.appendChild(row); }
|
||||
while (log.children.length > 400) log.firstChild.remove();
|
||||
log.scrollTop = log.scrollHeight;
|
||||
}
|
||||
function runDevCommand(text) {
|
||||
devPrint(devCommand(text, GAME.aim));
|
||||
hudDirty = true; lastBagKey = ''; lastBuildKey = '';
|
||||
renderWallet();
|
||||
if (!$('m-notebook').hidden) renderNotebook();
|
||||
}
|
||||
$('dev-close').onclick = closeDevConsole;
|
||||
$('dev-input').onfocus = () => { keys.clear(); GAME.lmb = GAME.rmb = false; GAME.drag = null; GAME.replace = false; };
|
||||
$('dev-input').onkeydown = e => {
|
||||
if (e.key === 'Escape' || e.key === '`') { e.preventDefault(); e.stopPropagation(); closeDevConsole(); }
|
||||
};
|
||||
$('dev-form').onsubmit = e => {
|
||||
e.preventDefault();
|
||||
const input = $('dev-input'); runDevCommand(input.value); input.value = ''; input.focus();
|
||||
};
|
||||
+2
-1
@@ -24,6 +24,7 @@ window.addEventListener('keydown', e => {
|
||||
if (e.repeat) return;
|
||||
GAME.usingPad = false;
|
||||
switch (k) {
|
||||
case '`': e.preventDefault(); toggleDevConsole(); break;
|
||||
case '1': case '2': case '3': case '4': selectTool(+k - 1); break;
|
||||
case 'c': case 'x': hudDirty = true; break;
|
||||
case 'v': startPaste(); break;
|
||||
@@ -42,7 +43,7 @@ window.addEventListener('keydown', e => {
|
||||
case '[': setBrush(-1); break;
|
||||
case ']': setBrush(1); break;
|
||||
case 'Delete': if (ghostBlocks.size || ghostMachines.length || ghostMolds.length) { clearAllGhosts(); toast('Blueprint ghosts cleared.'); } break;
|
||||
case 'Escape': if (GAME.selDrag || GAME.paste) stopSelectAndPaste(); else openPause(); break;
|
||||
case 'Escape': if (!$('dev-console').hidden) closeDevConsole(); else if (GAME.selDrag || GAME.paste) stopSelectAndPaste(); else openPause(); break;
|
||||
case 'Tab': $('bagpanel').hidden = !$('bagpanel').hidden; break;
|
||||
}
|
||||
});
|
||||
|
||||
+5
-4
@@ -22,11 +22,11 @@ function frame(now) {
|
||||
const pad = pollPad();
|
||||
if (GAME.usingPad) { GAME.aim.x = bot.x + bot.w / 2 + GAME.padAim.x; GAME.aim.y = bot.y + bot.h / 2 + GAME.padAim.y; }
|
||||
else { const w = screenToWorld(GAME.mouse.x, GAME.mouse.y); GAME.aim.x = w.x; GAME.aim.y = w.y; }
|
||||
if (!GAME.paused) {
|
||||
acc += dtReal * SET.simSpeed;
|
||||
if (!GAME.paused && (!devPaused || devSteps > 0)) {
|
||||
acc += devPaused ? 0 : dtReal * SET.simSpeed * devSpeed;
|
||||
let steps = 0;
|
||||
const step = 1 / 60;
|
||||
while (acc >= step && steps < SET.maxSteps) {
|
||||
while ((devPaused ? devSteps > 0 : acc >= step) && steps < SET.maxSteps) {
|
||||
let ix = 0, iy = 0;
|
||||
if (keys.has('a') || keys.has('ArrowLeft')) ix -= 1; if (keys.has('d') || keys.has('ArrowRight')) ix += 1;
|
||||
if (keys.has('w') || keys.has('ArrowUp') || keys.has(' ')) iy -= 1; if (keys.has('s') || keys.has('ArrowDown')) iy += 1;
|
||||
@@ -38,7 +38,8 @@ function frame(now) {
|
||||
botStep(ix, iy, keys.has('Shift') || GAME.padRun > 24);
|
||||
toolTick();
|
||||
const s0 = performance.now(); simTick(); simMs = simMs * 0.9 + (performance.now() - s0) * 0.1;
|
||||
acc -= step; steps++;
|
||||
if (devPaused) devSteps--; else acc -= step;
|
||||
steps++;
|
||||
GAME.playTime += step;
|
||||
}
|
||||
if (steps >= SET.maxSteps) acc = 0;
|
||||
|
||||
+1
-1
@@ -57,7 +57,7 @@ function renderNotebook() {
|
||||
}
|
||||
function controlsHtml() {
|
||||
const kb = [
|
||||
['Fly', 'W A S D'], ['Boost while flying', 'Shift'], ['Tool modifier (while not flying)', 'Shift'], ['Use tool', 'Left mouse'], ['Second action', 'Right mouse'],
|
||||
['Dev console (marks the world)', 'Backtick (`)'], ['Fly', 'W A S D'], ['Boost while flying', 'Shift'], ['Tool modifier (while not flying)', 'Shift'], ['Use tool', 'Left mouse'], ['Second action', 'Right mouse'],
|
||||
['Tools', '1 – 4'], ['Select one to blow · piece', 'Q / E'], ['Switch build tab', 'Shift + Q / E'], ['Variant: material · direction · flip', 'R'], ['Wall: cycle shape · flip diagonal drag', 'T'], ['Wall or mold material menu (Build)', 'F'],
|
||||
['Pick up selection or what you click (Pneumatic nozzle)', 'Left drag · Shift: everything'], ['Select (Pneumatic nozzle)', 'Click a bag name · Q/E'], ['Add to selection (Pneumatic nozzle)', 'Ctrl+click a bag name'], ['Show everything found (Pneumatic nozzle)', 'Hold Ctrl in the bag'], ['Clear selection (Pneumatic nozzle)', 'Clear in the bag'], ['Blow everything loose in the bag', 'Shift + right click'], ['Nozzle or brush size', '[ ] or Shift + wheel'],
|
||||
['Replace what is built (Build)', 'Hold G'], ['Copy a box of what you built', 'Hold C, drag'], ['Cut a box (refunded, paste it back)', 'Hold X, drag'], ['Paste again', 'V'], ['Undo building', 'Ctrl + Z'], ['Blueprint library', 'B'], ['Zoom', 'Mouse wheel'], ['Interact (gates, molds, fans, filters)', 'F'], ['Thermal lens', 'H'], ['Info overlay: filters, belt and screw conveyor directions', 'I'], ['Bot lamp', 'L'],
|
||||
|
||||
+1
-1
@@ -112,7 +112,7 @@ async function saveWorld(slot, quiet) {
|
||||
const list = await listWorlds();
|
||||
let entry = list.find(w => w.id === id);
|
||||
if (!entry) { entry = { id, name, createdAt: savedAt }; list.push(entry); }
|
||||
Object.assign(entry, { savedAt, playTime, seed: s.meta.seed, w: s.meta.W, h: s.meta.H, debt: s.meta.economy.debt, ...(c ? { career: c.id, claimNo } : {}) });
|
||||
Object.assign(entry, { savedAt, playTime, seed: s.meta.seed, w: s.meta.W, h: s.meta.H, debt: s.meta.economy.debt, dev: s.meta.dev, ...(c ? { career: c.id, claimNo } : {}) });
|
||||
await DB.op('readwrite', st => { st.put({ meta: s.meta, buffers, savedAt, playTime }, `${id}:${slot}`); st.put(list, 'worlds'); if (c) st.put(c, 'career:' + c.id); });
|
||||
});
|
||||
if (!quiet) toast(slot === 'auto' ? 'Autosaved.' : 'World saved.');
|
||||
|
||||
@@ -91,6 +91,7 @@ function pieceExtra(P) { return P.filter ? { m: GAME.filterCfg.m.slice(), inv: G
|
||||
function filterBlockAt(x, y) { if (x < 0 || y < 0 || x >= W || y >= H) return -1; const b = ((y / BS) | 0) * BW + ((x / BS) | 0); return isFilter(blockP[b]) ? b : -1; }
|
||||
function chipCost(cost) { const ok = canAfford(cost, 1); return `<span class="chip ${ok ? 'ok' : 'bad'}">${esc(costText(cost, 1))}</span>`; }
|
||||
function pieceCostNow(P, m = GAME.wallMat) {
|
||||
if (devFree) return [[CASH, 0]];
|
||||
if (P.kind === 'mold') return wallCost(m, MOLDS[P.mold].walls);
|
||||
if (P.kind === 'block' && isWall(P.code)) return pieceCost(pieceCode(P), m);
|
||||
return P.cost;
|
||||
|
||||
+3
-2
@@ -11,6 +11,7 @@ function showLoading(on, p, label, title) {
|
||||
if (on) { $('loadbar').style.width = Math.round((p || 0) * 100) + '%'; $('loadtext').textContent = Math.round((p || 0) * 100) + '%'; }
|
||||
}
|
||||
function enterGame() {
|
||||
closeDevConsole(); $('dev-output').textContent = ''; $('dev-input').value = '';
|
||||
$('cards').innerHTML = '';
|
||||
if (R.floaters) R.floaters.length = 0; R.particles.length = 0;
|
||||
GAME.status = ''; GAME.statusT = 0;
|
||||
@@ -80,7 +81,7 @@ async function loadSavedWorld(id) {
|
||||
}
|
||||
async function quitToTitle() {
|
||||
await saveWorld('main', true);
|
||||
GAME.running = false; closeAllModals();
|
||||
GAME.running = false; closeDevConsole(); closeAllModals();
|
||||
$('hud').hidden = true; $('title').hidden = false;
|
||||
refreshContinue();
|
||||
if (!TITLE.raf) drawTitle();
|
||||
@@ -135,7 +136,7 @@ async function renderWorlds(editId) {
|
||||
for (const w of list) {
|
||||
const row = el('div', { class: 'world-row', 'data-world': w.id });
|
||||
const mins = Math.floor((w.playTime || 0) / 60);
|
||||
row.innerHTML = `<div class="world-info"><strong>${esc(w.name)}</strong><div class="note">seed ${esc(w.seed)} · ${esc(w.w)}×${esc(w.h)} grains · ${Math.floor(mins / 60)}:${String(mins % 60).padStart(2, '0')} played</div><div class="note">${w.savedAt ? esc(new Date(w.savedAt).toLocaleString()) : 'Not saved yet'}</div></div>`;
|
||||
row.innerHTML = `<div class="world-info"><strong>${esc(w.name)}</strong>${w.dev ? ' <span class="dev-badge">Dev console</span>' : ''}<div class="note">seed ${esc(w.seed)} · ${esc(w.w)}×${esc(w.h)} grains · ${Math.floor(mins / 60)}:${String(mins % 60).padStart(2, '0')} played</div><div class="note">${w.savedAt ? esc(new Date(w.savedAt).toLocaleString()) : 'Not saved yet'}</div></div>`;
|
||||
const actions = el('div', { class: 'world-actions' });
|
||||
const button = (label, fn, cls = '') => { const b = el('button', { class: 'btn ' + cls }, label); b.onclick = async () => { try { await fn(); } catch (e) { toast('Could not reach browser storage.'); } }; actions.appendChild(b); };
|
||||
if (editId === w.id) {
|
||||
|
||||
Reference in New Issue
Block a user