lots of tweaks and updates
This commit is contained in:
parent
b454ffbd40
commit
6d6fbe85a9
18 changed files with 762 additions and 528 deletions
|
|
@ -1,7 +1,7 @@
|
|||
import { GRID, THEME, EFFECT_PRIORITY, VERSION } from '../core/config.js';
|
||||
import { key, parseKey, cellCenter, mixHex, randomBetween } from '../core/utils.js';
|
||||
import { getConveyorNeighbors, routeFromFarmToScanner, outputRoute, scannerCenter, scannerConnector, scannerOutputs, destinationLabel, destinationColor } from '../systems/routing.js';
|
||||
import { upgradedTruckPrice } from '../systems/economy.js';
|
||||
import { key, parseKey, pointToCell, cellCenter, mixHex, randomBetween, yen } from '../core/utils.js';
|
||||
import { getConveyorNeighbors, routeFromFarmToScanner, outputRoute, scannerCenter, scannerConnector, scannerOutputs, destinationLabel, destinationColor, scannerPortHasConveyor } from '../systems/routing.js';
|
||||
import { upgradedMixerPrice, upgradedTruckPrice } from '../systems/economy.js';
|
||||
|
||||
const ASSET_PATHS = {
|
||||
chickMale: './assets/images/chick_male.png',
|
||||
|
|
@ -126,89 +126,68 @@ function drawConveyors(ctx, game) {
|
|||
ctx.lineWidth = 6;
|
||||
ctx.beginPath(); ctx.moveTo(e.a.x, e.a.y); ctx.lineTo(e.b.x, e.b.y); ctx.stroke();
|
||||
}
|
||||
const directionMarkers = collectConveyorDirectionMarkers(game);
|
||||
for (const k of game.conveyorTiles) {
|
||||
const c = cellCenter(...Object.values(parseKey(k)));
|
||||
const ratio = componentRatio(game, k);
|
||||
ctx.fillStyle = ratio > 0.5 ? mixHex(THEME.white, '#ffd6d6', Math.max(0, (ratio - .5) / .5)) : THEME.white;
|
||||
ctx.strokeStyle = THEME.ink; ctx.lineWidth = 2;
|
||||
rect(ctx, c.x - 8, c.y - 8, 16, 16, true, true);
|
||||
drawConveyorDirectionMarkers(ctx, c, directionMarkers.get(k) || []);
|
||||
if (ratio > 0.5) label(ctx, c.x, c.y - 15, `${Math.floor(ratio * 100)}%`, THEME.danger);
|
||||
}
|
||||
for (const farm of game.eggFarms) {
|
||||
const data = routeFromFarmToScanner(game, farm);
|
||||
if (data) drawRouteFlow(ctx, data.route, THEME.muted, 3);
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
function cellKeyFromPoint(game, p) {
|
||||
const cell = pointToCell(p.x, p.y);
|
||||
if (!cell) return null;
|
||||
const center = cellCenter(cell.col, cell.row);
|
||||
if (Math.hypot(center.x - p.x, center.y - p.y) > GRID.cell * 0.38) return null;
|
||||
const k = key(cell.col, cell.row);
|
||||
return game.conveyorTiles.has(k) ? k : null;
|
||||
}
|
||||
function addMarkerFromRoute(game, markers, route) {
|
||||
if (!route || route.length < 2) return;
|
||||
for (let i = 0; i < route.length - 1; i += 1) {
|
||||
const a = route[i], b = route[i + 1];
|
||||
const ak = cellKeyFromPoint(game, a);
|
||||
const bk = cellKeyFromPoint(game, b);
|
||||
if (!ak || !bk || ak === bk) continue;
|
||||
const ac = parseKey(ak), bc = parseKey(bk);
|
||||
const dc = bc.col - ac.col, dr = bc.row - ac.row;
|
||||
if (Math.abs(dc) + Math.abs(dr) !== 1) continue;
|
||||
const angle = Math.atan2(dr, dc);
|
||||
if (!markers.has(ak)) markers.set(ak, []);
|
||||
const list = markers.get(ak);
|
||||
if (!list.some(x => Math.abs(Math.sin((x - angle) / 2)) < 0.01)) list.push(angle);
|
||||
}
|
||||
const flowSegments = new Map();
|
||||
}
|
||||
function collectConveyorDirectionMarkers(game) {
|
||||
const markers = new Map();
|
||||
for (const farm of game.eggFarms) addMarkerFromRoute(game, markers, routeFromFarmToScanner(game, farm)?.route);
|
||||
for (const scanner of game.scanners) {
|
||||
for (const side of ['left', 'right']) {
|
||||
const plan = outputRoute(game, side, scannerCenter(scanner), scanner.id);
|
||||
if (!plan) continue;
|
||||
addRouteFlowSegments(flowSegments, plan.route, plan.destination);
|
||||
}
|
||||
for (const side of ['left', 'right']) addMarkerFromRoute(game, markers, outputRoute(game, side, scannerCenter(scanner), scanner.id)?.route);
|
||||
}
|
||||
drawDestinationFlowSegments(ctx, flowSegments);
|
||||
ctx.restore();
|
||||
return markers;
|
||||
}
|
||||
function drawRouteFlow(ctx, points, color, width) {
|
||||
if (!points || points.length < 2) return;
|
||||
function drawConveyorDirectionMarkers(ctx, center, angles) {
|
||||
const limited = angles.slice(0, 3);
|
||||
if (!limited.length) return;
|
||||
ctx.save();
|
||||
ctx.strokeStyle = color; ctx.lineWidth = width; ctx.globalAlpha = .78; ctx.lineCap = 'round'; ctx.lineJoin = 'round';
|
||||
ctx.beginPath(); ctx.moveTo(points[0].x, points[0].y);
|
||||
for (let i = 1; i < points.length; i += 1) ctx.lineTo(points[i].x, points[i].y);
|
||||
ctx.stroke();
|
||||
ctx.fillStyle = color; ctx.globalAlpha = .92;
|
||||
for (let i = 0; i < points.length - 1; i += 1) {
|
||||
const a = points[i], b = points[i + 1];
|
||||
const dx = b.x - a.x, dy = b.y - a.y, dist = Math.hypot(dx, dy);
|
||||
if (dist < 36) continue;
|
||||
drawArrow(ctx, a.x + dx * .55, a.y + dy * .55, Math.atan2(dy, dx));
|
||||
}
|
||||
ctx.fillStyle = THEME.ink;
|
||||
ctx.globalAlpha = 0.86;
|
||||
limited.forEach((angle, index) => {
|
||||
const offset = (index - (limited.length - 1) / 2) * 4;
|
||||
const nx = Math.cos(angle + Math.PI / 2), ny = Math.sin(angle + Math.PI / 2);
|
||||
drawDirectionTriangle(ctx, center.x + nx * offset, center.y + ny * offset, angle, 6, 5);
|
||||
});
|
||||
ctx.restore();
|
||||
}
|
||||
function flowSegmentKey(a, b) {
|
||||
const ak = `${Math.round(a.x)},${Math.round(a.y)}`;
|
||||
const bk = `${Math.round(b.x)},${Math.round(b.y)}`;
|
||||
return ak < bk ? `${ak}|${bk}` : `${bk}|${ak}`;
|
||||
}
|
||||
function addRouteFlowSegments(map, points, dest) {
|
||||
if (!points || points.length < 2) return;
|
||||
for (let i = 0; i < points.length - 1; i += 1) {
|
||||
const a = points[i], b = points[i + 1];
|
||||
if (Math.hypot(b.x - a.x, b.y - a.y) < 8) continue;
|
||||
const k = flowSegmentKey(a, b);
|
||||
if (!map.has(k)) map.set(k, new Map());
|
||||
const dests = map.get(k);
|
||||
if (!dests.has(dest)) dests.set(dest, { a, b });
|
||||
}
|
||||
}
|
||||
function drawDestinationFlowSegments(ctx, segments) {
|
||||
const order = ['mixer', 'trash', 'truck', 'scanner'];
|
||||
for (const dests of segments.values()) {
|
||||
const entries = [...dests.entries()].sort((a, b) => order.indexOf(a[0]) - order.indexOf(b[0]));
|
||||
const count = entries.length;
|
||||
entries.forEach(([dest, seg], index) => {
|
||||
const offset = (index - (count - 1) / 2) * 12;
|
||||
drawColoredFlowSegment(ctx, seg.a, seg.b, destinationColor(dest), offset, index - (count - 1) / 2);
|
||||
});
|
||||
}
|
||||
}
|
||||
function drawColoredFlowSegment(ctx, a, b, color, offset, arrowShift = 0) {
|
||||
const dx = b.x - a.x, dy = b.y - a.y;
|
||||
const dist = Math.hypot(dx, dy);
|
||||
if (dist < 8) return;
|
||||
const nx = -dy / dist, ny = dx / dist;
|
||||
const ax = a.x + nx * offset, ay = a.y + ny * offset;
|
||||
const bx = b.x + nx * offset, by = b.y + ny * offset;
|
||||
ctx.save();
|
||||
ctx.strokeStyle = color; ctx.lineWidth = 5; ctx.globalAlpha = .9; ctx.lineCap = 'round';
|
||||
ctx.beginPath(); ctx.moveTo(ax, ay); ctx.lineTo(bx, by); ctx.stroke();
|
||||
ctx.fillStyle = color; ctx.globalAlpha = .98;
|
||||
if (dist > 32) drawArrow(ctx, ax + (bx - ax) * .58 + arrowShift * 9, ay + (by - ay) * .58, Math.atan2(dy, dx));
|
||||
ctx.restore();
|
||||
}
|
||||
function drawArrow(ctx, x, y, angle) {
|
||||
|
||||
function drawDirectionTriangle(ctx, x, y, angle, length = 11, halfWidth = 8) {
|
||||
ctx.save(); ctx.translate(x, y); ctx.rotate(angle);
|
||||
ctx.beginPath(); ctx.moveTo(11, 0); ctx.lineTo(-6, -7); ctx.lineTo(-2, 0); ctx.lineTo(-6, 7); ctx.closePath(); ctx.fill();
|
||||
ctx.beginPath(); ctx.moveTo(length, 0); ctx.lineTo(-length * 0.72, -halfWidth); ctx.lineTo(-length * 0.72, halfWidth); ctx.closePath(); ctx.fill();
|
||||
ctx.restore();
|
||||
}
|
||||
function drawScannerConnectors(ctx, game) {
|
||||
|
|
@ -219,14 +198,14 @@ function drawScannerConnectors(ctx, game) {
|
|||
for (const scanner of game.scanners) {
|
||||
const outputs = scannerOutputs(scanner);
|
||||
const items = [
|
||||
{ p: scannerConnector(scanner, 'inputA'), label: 'IN', color: THEME.green },
|
||||
{ p: scannerConnector(scanner, 'left'), label: destinationLabel(outputs.left), color: destinationColor(outputs.left) },
|
||||
{ p: scannerConnector(scanner, 'right'), label: destinationLabel(outputs.right), color: destinationColor(outputs.right) }
|
||||
{ type: 'inputA', p: scannerConnector(scanner, 'inputA'), label: 'IN', color: THEME.green },
|
||||
{ type: 'left', p: scannerConnector(scanner, 'left'), label: destinationLabel(outputs.left), color: destinationColor(outputs.left) },
|
||||
{ type: 'right', p: scannerConnector(scanner, 'right'), label: destinationLabel(outputs.right), color: destinationColor(outputs.right) }
|
||||
];
|
||||
for (const item of items) {
|
||||
if (!item.p) continue;
|
||||
const p = cellCenter(item.p.col, item.p.row);
|
||||
const connected = game.conveyorTiles.has(key(item.p.col, item.p.row));
|
||||
const connected = scannerPortHasConveyor(game, scanner, item.type || (item.label === 'IN' ? 'inputA' : null)) || game.conveyorTiles.has(key(item.p.col, item.p.row));
|
||||
ctx.fillStyle = connected ? item.color : '#f4c2c2';
|
||||
ctx.strokeStyle = THEME.ink;
|
||||
rect(ctx, p.x - 18, p.y - 12, 36, 24, true, true);
|
||||
|
|
@ -361,7 +340,7 @@ function drawMixer(ctx, game) {
|
|||
if (drawImageIfLoaded(ctx, assets.mixer, m.x, m.y, m.w, m.h)) { ctx.restore(); return; }
|
||||
ctx.fillStyle = THEME.white; ctx.strokeStyle = THEME.ink; ctx.lineWidth = 4; rect(ctx, m.x, m.y, m.w, m.h, true, true);
|
||||
ctx.fillStyle = THEME.ink; ctx.font = '900 17px ui-monospace, monospace'; ctx.textAlign = 'center'; ctx.fillText(`MIXER L${m.level}`, m.x + m.w / 2, m.y + 28);
|
||||
ctx.font = '900 10px ui-monospace, monospace'; ctx.fillText(game.mixerHalfTimer > 0 ? `HALF PAY ${game.mixerHalfTimer.toFixed(1)}s` : `PAY ¥5`, m.x + m.w / 2, m.y + 48);
|
||||
ctx.font = '900 10px ui-monospace, monospace'; ctx.fillText(`PAY ${yen(upgradedMixerPrice(game))}`, m.x + m.w / 2, m.y + 48);
|
||||
drawTargetBadge(ctx, m.x + 14, m.y + m.h - 40, m.w - 28, 'SEND MALE', 'male', 'safe meat route');
|
||||
ctx.strokeStyle = THEME.green; ctx.lineWidth = 5;
|
||||
for (let i = 0; i < 3; i += 1) { ctx.beginPath(); ctx.arc(m.x + m.w / 2, m.y + 78, 16 + i * 8, 0, Math.PI * 1.5); ctx.stroke(); }
|
||||
|
|
@ -387,7 +366,7 @@ function drawTruck(ctx, game) {
|
|||
if (drawImageIfLoaded(ctx, assets.truck, t.x, t.y, t.w, t.h)) { ctx.restore(); return; }
|
||||
ctx.fillStyle = THEME.white; ctx.strokeStyle = THEME.ink; ctx.lineWidth = 4; rect(ctx, t.x, t.y, t.w, t.h, true, true);
|
||||
ctx.fillStyle = THEME.ink; ctx.font = '900 15px ui-monospace, monospace'; ctx.textAlign = 'center'; ctx.fillText(`TRUCK L${t.level}`, t.x + t.w / 2, t.y + 24);
|
||||
ctx.font = '900 11px ui-monospace, monospace'; ctx.fillText(`UNIT ${upgradedTruckPrice(game)}円`, t.x + t.w / 2, t.y + 42);
|
||||
ctx.font = '900 11px ui-monospace, monospace'; ctx.fillText(`UNIT ${yen(upgradedTruckPrice(game))}`, t.x + t.w / 2, t.y + 42);
|
||||
const truckTargetType = targetForTruck(game);
|
||||
drawTargetBadge(ctx, t.x + 15, t.y + 50, t.w - 30, `SEND ${truckTargetType.toUpperCase()}`, truckTargetType, game.contractOffer && !game.contractActive ? 'next event' : 'truck cargo');
|
||||
ctx.fillStyle = THEME.greenSoft; rect(ctx, t.x + 13, t.y + 88, t.w - 26, t.h - 103, true, false);
|
||||
|
|
@ -409,15 +388,6 @@ function drawChick(ctx, chick, active, game) {
|
|||
ctx.save();
|
||||
if (active) { ctx.strokeStyle = THEME.green; ctx.lineWidth = 5; ctx.beginPath(); ctx.arc(chick.x, y, chick.radius + 8, 0, Math.PI * 2); ctx.stroke(); }
|
||||
if (rage) { ctx.strokeStyle = THEME.danger; ctx.lineWidth = 3; ctx.beginPath(); ctx.arc(chick.x, y, chick.radius + 12 + Math.sin(chick.bob * 2) * 4, 0, Math.PI * 2); ctx.stroke(); }
|
||||
if (chick.stoppedTimer > 0) {
|
||||
ctx.strokeStyle = THEME.warn;
|
||||
ctx.lineWidth = 4;
|
||||
ctx.setLineDash([5, 4]);
|
||||
ctx.beginPath();
|
||||
ctx.arc(chick.x, y, chick.radius + 10, 0, Math.PI * 2);
|
||||
ctx.stroke();
|
||||
ctx.setLineDash([]);
|
||||
}
|
||||
if (chick.sex === 'poop') drawPoop(ctx, chick.x, y, chick.radius);
|
||||
else {
|
||||
const img = chick.sex === 'male' ? assets.chickMale : assets.chickFemale;
|
||||
|
|
@ -429,15 +399,6 @@ function drawChick(ctx, chick, active, game) {
|
|||
ctx.fillStyle = '#ffaa2e'; ctx.beginPath(); ctx.moveTo(chick.x, y + 2); ctx.lineTo(chick.x + 9, y + 6); ctx.lineTo(chick.x, y + 10); ctx.closePath(); ctx.fill();
|
||||
}
|
||||
}
|
||||
if (chick.stoppedTimer > 0) {
|
||||
ctx.font = '900 10px ui-monospace, monospace';
|
||||
ctx.textAlign = 'center';
|
||||
ctx.lineWidth = 4;
|
||||
ctx.strokeStyle = THEME.white;
|
||||
ctx.fillStyle = THEME.warn;
|
||||
ctx.strokeText('STOPPED', chick.x, y - chick.radius - 13);
|
||||
ctx.fillText('STOPPED', chick.x, y - chick.radius - 13);
|
||||
}
|
||||
ctx.restore();
|
||||
}
|
||||
function nearestRatio(game, chick) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue