Brick press: clay lumps into green bricks that fire into Bricks (t28)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+3
-3
@@ -26,7 +26,7 @@ function placeBlock(code, v, bx, by, free, extra) {
|
||||
if (code === BP_WALL) setCell(i, WALL_OF[v], AMB[y], 0);
|
||||
else if (code === BP_BELT) { setCell(i, v === 0 ? BELTL : BELTR, AMB[y], 0); markPow(i, true); }
|
||||
else if (code === BP_SHAFT) { setCell(i, SHAFT, AMB[y], 0); markPow(i, true); }
|
||||
else if (isProcCode(code)) setCell(i, code === BP_GRIND ? GRINDER : SAWB, AMB[y], 0);
|
||||
else if (isProcCode(code)) setCell(i, code === BP_GRIND ? GRINDER : code === BP_PRESS ? PRESSB : SAWB, AMB[y], 0);
|
||||
else if (code === BP_GATE) setCell(i, GATE, AMB[y], 0);
|
||||
else if (code === BP_GRATE) { part[i] = P_GRATE; wakeXY(x, y); }
|
||||
else if (code === BP_TUBE) { part[i] = [P_TU, P_TR, P_TD, P_TL][v & 3]; markPow(i, true); wakeXY(x, y); }
|
||||
@@ -53,7 +53,7 @@ function blockIntegrity(b) {
|
||||
if (code === BP_WALL) { if (PH[m] === PH_SOLID && !(flags[i] & F_NAT) && !PARTMAT[m]) n++; }
|
||||
else if (code === BP_BELT) { if (m === BELTL || m === BELTR) n++; }
|
||||
else if (code === BP_SHAFT) { if (m === SHAFT) n++; }
|
||||
else if (isProcCode(code)) { if (m === (code === BP_GRIND ? GRINDER : SAWB)) n++; }
|
||||
else if (isProcCode(code)) { if (m === (code === BP_GRIND ? GRINDER : code === BP_PRESS ? PRESSB : SAWB)) n++; }
|
||||
else if (code === BP_GATE) { if (m === GATE || p === P_GOPEN) n++; }
|
||||
else if (code === BP_GRATE) { if (p === P_GRATE) n++; }
|
||||
else if (code === BP_TUBE) { if (p >= P_TU && p <= P_TR) n++; }
|
||||
@@ -83,7 +83,7 @@ function removeBlock(bx, by) {
|
||||
if (p === P_PIPE) { part[i] = 0; wakeIdx(i); n++; }
|
||||
else if (m === PIPE) { setCell(i, EMPTY, temp[i], 0); n++; }
|
||||
} else if (code === BP_BELT || code === BP_SHAFT || code === BP_GATE || isProcCode(code)) {
|
||||
if (m === BELTL || m === BELTR || m === SHAFT || m === GATE || m === GRINDER || m === SAWB) { setCell(i, EMPTY, temp[i], 0); markPow(i, false); n++; }
|
||||
if (m === BELTL || m === BELTR || m === SHAFT || m === GATE || m === GRINDER || m === SAWB || m === PRESSB) { setCell(i, EMPTY, temp[i], 0); markPow(i, false); n++; }
|
||||
if (p === P_GOPEN) { part[i] = 0; n++; wakeIdx(i); }
|
||||
} else if (p) { part[i] = 0; markPow(i, false); wakeIdx(i); n++; }
|
||||
}
|
||||
|
||||
+2
-2
@@ -10,7 +10,7 @@ const PRICE = new Float32Array(NMAT);
|
||||
for (const [m, v] of [
|
||||
[SAND, 0.1], [LCLAY, 0.2], [LWOOD, 0.3], [ACORN, 0.5], [COAL, 0.5], [RGRAN, 0.2], [RBAS, 0.4],
|
||||
[CUORE, 1], [FEORE, 1.6], [SNORE, 2], [ZNORE, 2.2], [AGORE, 6], [AUORE, 14],
|
||||
[CHARCOAL, 1.2], [RBRICK, 0.8], [CULLET, 1.2], [BARGL, 14], [RSTONE, 0.02], [GRAVEL, 0.03], [BARSB, 0.6], [PLANK, 2.4],
|
||||
[CHARCOAL, 1.2], [RBRICK, 0.8], [CULLET, 1.2], [BARGL, 14], [RSTONE, 0.02], [GRAVEL, 0.03], [BARSB, 0.6], [PLANK, 2.4], [GREENB, 2], [BARBR, 9],
|
||||
[SCRCU, 3], [SCRFE, 5], [SCRST, 11], [SCRSN, 3.5], [SCRZN, 3.5], [SCRAG, 18], [SCRAU, 40], [SCRBZ, 8], [SCRBS, 7],
|
||||
[BARCU, 40], [BARFE, 75], [BARST, 160], [BARSN, 50], [BARZN, 50], [BARAG, 220], [BARAU, 480], [BARBZ, 110], [BARBS, 95],
|
||||
]) PRICE[m] = v;
|
||||
@@ -73,7 +73,7 @@ const STANDING = ['Local buyers', 'County buyers', 'Regional buyers', 'National
|
||||
function standing() { return Math.min(STANDING.length - 1, Math.floor(ordersDone / 4)); }
|
||||
// Builders buy the cheap bulk stuff (rubble, gravel, sand, stone blocks, planks) in big lots at a few
|
||||
// times the market price, so waste rock earns a little. [what, smallest lot, largest lot]
|
||||
const BUILD_GOODS = [[RSTONE, 400, 1500], [GRAVEL, 300, 1000], [SAND, 300, 900], [BARSB, 20, 80], [PLANK, 20, 80]];
|
||||
const BUILD_GOODS = [[RSTONE, 400, 1500], [GRAVEL, 300, 1000], [SAND, 300, 900], [BARSB, 20, 80], [PLANK, 20, 80], [BARBR, 20, 80]];
|
||||
function makeBuildersOrder() {
|
||||
const cands = BUILD_GOODS.filter(([m]) => seen[m] && !orders.some(o => o.m === m));
|
||||
if (!cands.length) return null;
|
||||
|
||||
@@ -136,6 +136,9 @@ function entTick() {
|
||||
// heat: the bar tends toward the temperature around it
|
||||
const T = temp[Math.min(H - 1, cy + 1) * W + Math.min(W - 1, cx + 1)];
|
||||
e.T += (T - e.T) * 0.04;
|
||||
if (FIRETO[e.m] && e.T >= 880 && rnd() < 0.004 + (e.T - 880) * 0.00012) {
|
||||
e.m = FIRETO[e.m]; discover(e.m, e.ix, e.iy, GREENB, 'fire', 880);
|
||||
}
|
||||
if (e.T >= MELT[e.m]) {
|
||||
// melts back into 8 grains of molten metal if they fit here
|
||||
let fits = true;
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ function checkLedger() {
|
||||
const mark = id => { if (!ledger[id]) { ledger[id] = tick || 1; emit({ t: 'milestone', id }); } };
|
||||
if (stats.dug >= 15) mark('dig');
|
||||
if (seen[FIRE]) { mark('fire'); if (!player.lens) { player.lens = true; } }
|
||||
if (seen[BRICK] || seen[RBRICK]) mark('brick');
|
||||
if (seen[BRICK] || seen[RBRICK] || seen[BARBR]) mark('brick');
|
||||
if (seen[CHARCOAL]) mark('charcoal');
|
||||
if (stats.grown > 0) mark('grow');
|
||||
if (seen[MCU]) mark('copper');
|
||||
|
||||
@@ -11,8 +11,8 @@ const EMPTY = 0, GRASS = 1, DIRT = 2, LDIRT = 3, SAND = 4, GRAVEL = 5, CLAY = 6,
|
||||
ZNV = 63, ZNORE = 64, ZINC = 65, MZN = 66, SCRZN = 67, BARZN = 68,
|
||||
AGV = 69, AGORE = 70, SILVER = 71, MAG = 72, SCRAG = 73, BARAG = 74,
|
||||
AUV = 75, AUORE = 76, GOLD = 77, MAU = 78, SCRAU = 79, BARAU = 80,
|
||||
BRONZE = 81, MBZ = 82, SCRBZ = 83, BARBZ = 84, BRASS = 85, MBS = 86, SCRBS = 87, BARBS = 88, DOCKW = 89, MSLAG = 90, PIPE = 91, BARGL = 92, CUTST = 93, BARSB = 94, GRINDER = 95, SAWB = 96, PLANK = 97;
|
||||
const NMAT = 98;
|
||||
BRONZE = 81, MBZ = 82, SCRBZ = 83, BARBZ = 84, BRASS = 85, MBS = 86, SCRBS = 87, BARBS = 88, DOCKW = 89, MSLAG = 90, PIPE = 91, BARGL = 92, CUTST = 93, BARSB = 94, GRINDER = 95, SAWB = 96, PLANK = 97, PRESSB = 98, GREENB = 99, BARBR = 100;
|
||||
const NMAT = 101;
|
||||
|
||||
const MD = [];
|
||||
function def(id, o) {
|
||||
@@ -64,6 +64,7 @@ def(SHAFT, { name: 'Shaft', col: [150, 110, 64], v: 6, k: 0.005, hard: 2, ign: 3
|
||||
def(GATE, { name: 'Gate', col: [146, 74, 58], v: 6, k: 0.004, hard: 3, melt: 1750, meltTo: MAGMA, partMat: true });
|
||||
def(GRINDER, { name: 'Grinder', col: [64, 70, 80], v: 4, k: 0.035, hard: 6, melt: 1450, meltTo: MST, partMat: true });
|
||||
def(SAWB, { name: 'Saw', col: [74, 80, 90], v: 4, k: 0.035, hard: 6, melt: 1450, meltTo: MST, partMat: true });
|
||||
def(PRESSB, { name: 'Brick press', col: [74, 80, 90], v: 4, k: 0.035, hard: 6, melt: 1450, meltTo: MST, partMat: true });
|
||||
def(SPRING, { name: 'Spring', col: [92, 112, 128], v: 30, k: 0.012, hard: 9, yld: 0, desc: 'Groundwater seeps out of this rock whenever there is room next to it.' });
|
||||
def(RSTONE, { name: 'Stone rubble', ph: PH_POWDER, col: [126, 125, 130], v: 30, dens: 1.8, k: 0.01, hard: 0, melt: 1250, meltTo: MAGMA, bag: true, desc: 'Broken stone. 16 grains make a stone block.' });
|
||||
def(RGRAN, { name: 'Granite rubble', ph: PH_POWDER, col: [164, 128, 124], v: 32, dens: 1.9, k: 0.01, hard: 0, melt: 1260, meltTo: MAGMA, bag: true, desc: 'Broken granite. Makes granite blocks.' });
|
||||
@@ -106,6 +107,10 @@ metalFamily({ solid: BRASS, molten: MBS, scrap: SCRBS, bar: BARBS, name: 'Brass'
|
||||
desc: 'Copper with zinc melted in. Golden and valuable.' });
|
||||
def(BARGL, { name: 'Glass brick', ph: PH_ITEM, col: [176, 218, 228], v: 0, dens: 2.5, k: 0, hard: 0, melt: 1300, meltTo: MGL, bag: true, desc: 'Cast from molten glass in an Ingot mold: 8 grains of glass. Sells well, and 2 make a glass wall block.' });
|
||||
def(PLANK, { name: 'Plank', ph: PH_ITEM, col: [204, 158, 104], v: 0, dens: 0.6, k: 0, hard: 0, melt: 9999, bag: true, desc: 'Sawn from 8 wood chunks by a Saw. Builders buy them, and 2 make a wood wall block.' });
|
||||
def(GREENB, { name: 'Green brick', ph: PH_ITEM, col: [202, 164, 136], v: 0, dens: 1.8, k: 0, hard: 0, melt: 1750, meltTo: MAGMA, bag: true, desc: 'Unfired clay pressed into shape by a Brick press. Fire it above ~900°C to make a Brick.' });
|
||||
def(BARBR, { name: 'Brick', ph: PH_ITEM, col: [174, 76, 56], v: 0, dens: 1.8, k: 0, hard: 0, melt: 1750, meltTo: MAGMA, bag: true, desc: 'Fired clay brick. Builders buy them, and 2 make a brick wall block.' });
|
||||
// An item that fires into another item when hot.
|
||||
const FIRETO = new Uint8Array(NMAT); FIRETO[GREENB] = BARBR;
|
||||
def(CUTST, { name: 'Cut stone', col: [168, 162, 150], v: 6, k: 0.012, hard: 4, melt: 1450, meltTo: MAGMA, yld: BARSB, desc: 'Dressed stone blocks laid in courses. Neater than rough stone, and it stands 200°C more heat before it slumps.' });
|
||||
def(BARSB, { name: 'Stone block', ph: PH_ITEM, col: [172, 166, 154], v: 0, dens: 2.6, k: 0, hard: 0, melt: 1450, meltTo: MAGMA, bag: true, desc: 'A block of stone cut square by a Saw, from 8 grains of rubble. Builders buy them, and 2 make a cut stone wall block.' });
|
||||
def(PIPE, { name: 'Pipe', col: [176, 112, 72], v: 4, k: 0.04, cap: 1.5, hard: 4, melt: 1085, meltTo: MCU, partMat: true });
|
||||
|
||||
+5
-3
@@ -4,7 +4,7 @@
|
||||
// Everything you build snaps to a grid of 4x4-grain blocks. The world itself
|
||||
// stays grain-level; only built pieces snap.
|
||||
const BS = 4;
|
||||
const BP_WALL = 1, BP_BELT = 2, BP_TUBE = 3, BP_SHAFT = 4, BP_GRATE = 5, BP_GATE = 6, BP_FILTER = 7, BP_PIPE = 8, BP_PUMP = 9, BP_OUTLET = 10, BP_GRIND = 11, BP_SAW = 12;
|
||||
const BP_WALL = 1, BP_BELT = 2, BP_TUBE = 3, BP_SHAFT = 4, BP_GRATE = 5, BP_GATE = 6, BP_FILTER = 7, BP_PIPE = 8, BP_PUMP = 9, BP_OUTLET = 10, BP_GRIND = 11, BP_SAW = 12, BP_PRESS = 13;
|
||||
function isPipeCode(c) { return c === BP_PIPE || c === BP_PUMP || c === BP_OUTLET; }
|
||||
// Filters: each filter block has a set of materials and a mode. In drop mode (inv 0)
|
||||
// the chosen materials fall through and everything else rides along on top; in carry
|
||||
@@ -25,8 +25,8 @@ function sameFilter(a, b) { return a.inv === b.inv && a.m.length === b.m.length
|
||||
const WALL_OF = new Uint8Array(NMAT);
|
||||
WALL_OF[LDIRT] = DIRT; WALL_OF[RSTONE] = STONE; WALL_OF[RGRAN] = GRANITE; WALL_OF[RBAS] = BASALT; WALL_OF[LCLAY] = CLAY;
|
||||
WALL_OF[LWOOD] = WOOD; WALL_OF[RBRICK] = BRICK; WALL_OF[CULLET] = GLASS; WALL_OF[BARCU] = COPPER; WALL_OF[BARFE] = IRON; WALL_OF[BARST] = STEEL;
|
||||
WALL_OF[BARSB] = CUTST; WALL_OF[PLANK] = WOOD; WALL_OF[BARGL] = GLASS;
|
||||
const WALL_LIST = [RSTONE, BARSB, PLANK, LCLAY, RBRICK, LDIRT, LWOOD, CULLET, BARGL, RGRAN, RBAS, BARCU, BARFE, BARST];
|
||||
WALL_OF[BARSB] = CUTST; WALL_OF[PLANK] = WOOD; WALL_OF[BARBR] = BRICK; WALL_OF[BARGL] = GLASS;
|
||||
const WALL_LIST = [RSTONE, BARSB, PLANK, BARBR, LCLAY, RBRICK, LDIRT, LWOOD, CULLET, BARGL, RGRAN, RBAS, BARCU, BARFE, BARST];
|
||||
const BAR_OF = new Uint8Array(NMAT); BAR_OF[COPPER] = BARCU; BAR_OF[IRON] = BARFE; BAR_OF[STEEL] = BARST;
|
||||
BAR_OF[GLASS] = BARGL; BAR_OF[CUTST] = BARSB; BAR_OF[WOOD] = PLANK; BAR_OF[TIN] = BARSN; BAR_OF[ZINC] = BARZN; BAR_OF[SILVER] = BARAG; BAR_OF[GOLD] = BARAU; BAR_OF[BRONZE] = BARBZ; BAR_OF[BRASS] = BARBS;
|
||||
const GRAINS_PER_BAR = 8;
|
||||
@@ -59,6 +59,8 @@ const PIECES = [
|
||||
desc: 'Lets a pipe line\'s liquid out of the side its arrow points to, wherever there is room for it. Press F to open or close it.' },
|
||||
{ id: 'grinder', code: BP_GRIND, name: 'Grinder', kind: 'block', cost: [[CASH, 60]],
|
||||
desc: 'Grinds rubble, gravel, sand and dirt resting on top into sand and the odd grain of ore. Results drop out below; keep that space clear. Wider rows grind faster. Stack under a Saw to cut stone and grind the rest.' },
|
||||
{ id: 'press', code: BP_PRESS, name: 'Brick press', kind: 'block', cost: [[CASH, 60]],
|
||||
desc: 'Presses clay lumps resting on top into a green brick for every 8. They drop out underneath; put a belt or an open drop under it. Fire green bricks in a fire hotter than about 900°C to make Bricks. Wider rows press faster.' },
|
||||
{ id: 'saw', code: BP_SAW, name: 'Saw', kind: 'block', cost: [[CASH, 90]],
|
||||
desc: 'Cuts stone rubble into stone blocks and wood chunks into planks, one from every 8 grains resting on top. They drop out underneath and pile up there: put a belt or an open drop under it to carry them away. Wider rows cut faster. Stack over a Grinder to grind everything else.' },
|
||||
{ id: 'wheel', name: 'Water wheel', kind: 'machine', bw: 3, bh: 3, cost: [[LWOOD, 48]], hidden: POWER_FREE,
|
||||
|
||||
+12
-7
@@ -15,12 +15,12 @@ const CUT_PER_BRICK = 8;
|
||||
const SAW_CUTS = []; SAW_CUTS[RSTONE] = BARSB; SAW_CUTS[LWOOD] = PLANK;
|
||||
// Block hoppers share an outlet down a vertical stack; only its top spends work.
|
||||
let procState = new Map(), procBlocks = [], procDirty = true;
|
||||
function isProcCode(c) { return c === BP_GRIND || c === BP_SAW; }
|
||||
function isProcCode(c) { return c === BP_GRIND || c === BP_SAW || c === BP_PRESS; }
|
||||
function rebuildProcessors() {
|
||||
procBlocks = [];
|
||||
for (let b = 0; b < NB; b++) if (isProcCode(blockP[b])) {
|
||||
procBlocks.push(b);
|
||||
if (!procState.has(b)) procState.set(b, { work: 0, cut: { [RSTONE]: 0, [LWOOD]: 0 }, busyT: -999, blocked: false });
|
||||
if (!procState.has(b)) procState.set(b, { work: 0, press: 0, cut: { [RSTONE]: 0, [LWOOD]: 0 }, busyT: -999, blocked: false });
|
||||
}
|
||||
procDirty = false;
|
||||
}
|
||||
@@ -45,15 +45,19 @@ function procFeed(b, m, T) {
|
||||
} else if (blockP[b] === BP_SAW && SAW_CUTS[m]) {
|
||||
if (++s.cut[m] < CUT_PER_BRICK) return;
|
||||
s.cut[m] = 0; m = SAW_CUTS[m];
|
||||
} else if (blockP[b] === BP_PRESS && m === LCLAY) {
|
||||
if (++s.press < CUT_PER_BRICK) return;
|
||||
s.press = 0; m = GREENB;
|
||||
}
|
||||
if (b === end) break;
|
||||
b += BW;
|
||||
}
|
||||
if (m === BARSB || m === PLANK) {
|
||||
if (m === BARSB || m === PLANK || m === GREENB) {
|
||||
const e = spawnBar(m, x, (((end / BW) | 0) + 1) * BS, (rnd() - 0.5) * 0.1, 0.05);
|
||||
if (e) {
|
||||
e.T = T; discover(m, x, y, m === PLANK ? LWOOD : RSTONE, 'cut');
|
||||
if (m === PLANK) stats.planks = (stats.planks || 0) + 1;
|
||||
e.T = T; discover(m, x, y, m === GREENB ? LCLAY : m === PLANK ? LWOOD : RSTONE, m === GREENB ? 'press' : 'cut');
|
||||
if (m === GREENB) stats.pressed = (stats.pressed || 0) + 1;
|
||||
else if (m === PLANK) stats.planks = (stats.planks || 0) + 1;
|
||||
else stats.bricks = (stats.bricks || 0) + 1;
|
||||
}
|
||||
} else setCell(procSlot(end), m, T, 0);
|
||||
@@ -71,9 +75,10 @@ function procBlocksTick() {
|
||||
const i = y * W + x + ((k + tick) & 3), m = mat[i];
|
||||
if (m === EMPTY || PH[m] === PH_GAS || PH[m] === PH_SOLID || stamp[i] === tickStamp) continue;
|
||||
let brick = false;
|
||||
if (SAW_CUTS[m]) for (let q = b; q <= end; q += BW) {
|
||||
if (SAW_CUTS[m] || m === LCLAY) for (let q = b; q <= end; q += BW) {
|
||||
if (blockP[q] === BP_GRIND && REFINE[m]) break;
|
||||
if (blockP[q] === BP_SAW) { brick = procState.get(q).cut[m] + 1 >= CUT_PER_BRICK; break; }
|
||||
if (blockP[q] === BP_PRESS && m === LCLAY) { brick = procState.get(q).press + 1 >= CUT_PER_BRICK; break; }
|
||||
if (blockP[q] === BP_SAW && SAW_CUTS[m]) { brick = procState.get(q).cut[m] + 1 >= CUT_PER_BRICK; break; }
|
||||
}
|
||||
if (procSlot(end) < 0 || brick && !entFreeAt(x, (((end / BW) | 0) + 1) * BS)) {
|
||||
for (let q = b; q <= end; q += BW) procState.get(q).blocked = true;
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ const STARTER_DESIGNS = [
|
||||
const MILESTONES = [
|
||||
{ id: 'dig', title: 'Break ground', msg: 'Broken material falls loose. The Pneumatic nozzle (2) picks up and blows the selection. Click or Q/E in the bag selects; Ctrl+click mixes. Hold Ctrl to see everything found. Clear returns to picking what you click. Shift: everything.', hint: 'Hold the left mouse button with the Drill (1): it sends pulses at the cursor that shake rock until it shatters. It falls as rubble, ore and coal.' },
|
||||
{ id: 'fire', title: 'First fire', hint: 'Pick up coal or wood chunks with the nozzle, blow a pile out with right click, then hold the Igniter (4) on it. Fuel only burns where it touches open air.', reward: 'Thermal lens unlocked. Press H to see heat.' },
|
||||
{ id: 'brick', title: 'Fired brick', hint: 'Clay turns into brick after a while above about 900°C, hotter than an open wood fire gets. Blow clay lumps into a coal fire, best walled in like a furnace, and pick up the brick rubble with the Pneumatic nozzle. Brick rubble builds brick blocks.', reward: 'Brick insulates far better than stone and survives 1750°C.' },
|
||||
{ id: 'brick', title: 'Fired brick', hint: 'Clay turns into brick after a while above about 900°C, hotter than an open wood fire gets. Blow clay lumps into a coal fire, best walled in like a furnace, and pick up the brick rubble with the Pneumatic nozzle. Brick rubble builds brick blocks. A Brick press makes neat green bricks to fire.', reward: 'Brick insulates far better than stone and survives 1750°C.' },
|
||||
{ id: 'charcoal', title: 'Charcoal', hint: 'Wood that gets hot without air turns into charcoal instead of burning. Bury a burning pile of wood chunks under dirt, or heat a sealed box of wood chunks from outside.', reward: 'A fuel you can grow: nearly as hot as coal.' },
|
||||
{ id: 'grow', title: 'Plant a tree', msg: 'Wood is renewable. Plant rows of acorns on dirt to farm it.', hint: 'Cutting down a tree drops acorns. Leave one on dirt or grass under open sky and it sprouts, then grows into a tree in about a minute and a half.' },
|
||||
{ id: 'copper', title: 'Molten copper', msg: 'Molten copper sinks to the bottom of the charge. Get it into a mold before it sets.', hint: 'Copper ore melts at 1085°C. An open fire loses too much heat, so wall coal and ore in with blocks and leave a small vent at the top for smoke. Mix ore and coal about half and half: Ctrl+click coal and copper ore in the bag to blow a mix. Right click blows an even mix.' },
|
||||
|
||||
Reference in New Issue
Block a user