This commit is contained in:
33333-33333 2026-07-20 14:36:59 +09:00
commit f6d7412744
94 changed files with 3836 additions and 1226 deletions

View file

@ -5,7 +5,7 @@
const WIRE_TYPES = new Set(["wire", "insulated_wire"]);
const LINK_TYPES = new Set(["rope", "rod", "spring", "wire", "insulated_wire"]);
const SIGNAL_SOURCE_TYPES = new Set(["pressure_switch"]);
const DIRECT_SIGNAL_SINK_TYPES = new Set(["rotator", "reciprocator", "robot_cleaner", "gate_fence", "fan", "stove", "dry_ice", "duplicator"]);
const DIRECT_SIGNAL_SINK_TYPES = new Set(["rotator", "reciprocator", "robot_cleaner", "gate_fence", "fan", "stove", "dry_ice", "duplicator", "leakage_device"]);
const DETECTOR_METRICS = Object.freeze({
tarinai_count: { label: "\u305f\u308a\u306a\u3044\u6570", defaultThreshold: 1 },
hunger_avg: { label: "\u5e73\u5747\u7a7a\u8179", defaultThreshold: 70 },
@ -154,9 +154,10 @@
return actors.length;
}
function updatePressureSwitches(worldRef) {
function updatePressureSwitches(worldRef, pressureSwitches = null) {
const minute = currentMinuteOfDay(worldRef);
for (const item of worldRef.itemsOfType?.("pressure_switch") || []) {
const switches = pressureSwitches || worldRef.itemsOfType?.("pressure_switch") || [];
for (const item of switches) {
if (!item || item.dead) continue;
item.pressureTarget = normalizePressureTarget(item.pressureTarget);
const defaultThreshold = PRESSURE_TARGETS[item.pressureTarget]?.defaultThreshold ?? 1;
@ -205,7 +206,62 @@
}
}
function buildSignalNetwork(worldRef) {
function collectSignalSinks(worldRef, out) {
if (!worldRef || !out) return out;
if (typeof worldRef.itemsOfType === "function") {
for (const type of DIRECT_SIGNAL_SINK_TYPES) {
const bucket = worldRef.itemsOfType(type) || [];
if (bucket.length) out.sinks.push(...bucket);
}
return out;
}
for (const item of worldRef.items || []) {
if (item && !item.dead && DIRECT_SIGNAL_SINK_TYPES.has(item.type)) out.sinks.push(item);
}
return out;
}
function collectSignalBuckets(worldRef, includeSinks = true) {
const out = { pressureSwitches: [], boards: [], wires: [], sinks: [] };
if (!worldRef) return out;
if (typeof worldRef.itemsOfType === "function") {
out.pressureSwitches = worldRef.itemsOfType("pressure_switch") || [];
out.boards = worldRef.itemsOfType("circuit_board") || [];
out.wires = [
...(worldRef.itemsOfType("wire") || []),
...(worldRef.itemsOfType("insulated_wire") || []),
];
if (includeSinks) collectSignalSinks(worldRef, out);
return out;
}
// Compatibility fallback for lightweight test worlds that do not expose
// item buckets. Production worlds use the branch above and avoid a full
// world.items scan entirely.
for (const item of worldRef.items || []) {
if (!item || item.dead) continue;
if (item.type === "pressure_switch") out.pressureSwitches.push(item);
else if (item.type === "circuit_board") out.boards.push(item);
if (WIRE_TYPES.has(item.type)) out.wires.push(item);
if (includeSinks && DIRECT_SIGNAL_SINK_TYPES.has(item.type)) out.sinks.push(item);
}
return out;
}
function clearSignalSinks(worldRef, sinks) {
let changed = false;
for (const item of sinks || []) {
if (!item || item.dead) continue;
if (item._signalDriven || item._signalOn || item._wireSignalDriven || item._wireSignalOn) changed = true;
item._wireSignalDriven = false;
item._wireSignalOn = false;
setEffectivePower(item, false, false, worldRef);
}
if (changed) worldRef.drawListDirty = true;
return changed;
}
function buildSignalNetwork(worldRef, signalBuckets = null) {
const buckets = signalBuckets || collectSignalBuckets(worldRef);
const adjacency = new Map();
const connectedNodes = new Set();
const wires = [];
@ -219,20 +275,19 @@
connectedNodes.add(a); connectedNodes.add(b);
};
const boards = [];
for (const item of worldRef.items || []) {
for (const item of buckets.sinks || []) {
if (!item || item.dead) continue;
if (DIRECT_SIGNAL_SINK_TYPES.has(item.type)) {
item._wireSignalDriven = false; item._wireSignalOn = false;
item._directSignalDriven = false; item._directSignalOn = false;
}
if (item.type === "circuit_board") {
circuit?.ensureConfig?.(item);
item._circuitInputState = Object.create(null);
if (!item._circuitOutputState || typeof item._circuitOutputState !== "object") circuit?.evaluate?.(item, item._circuitInputState);
boards.push(item);
}
item._wireSignalDriven = false; item._wireSignalOn = false;
item._directSignalDriven = false; item._directSignalOn = false;
}
for (const wire of worldRef.items || []) {
for (const item of buckets.boards || []) {
if (!item || item.dead) continue;
circuit?.ensureConfig?.(item);
item._circuitInputState = Object.create(null);
if (!item._circuitOutputState || typeof item._circuitOutputState !== "object") circuit?.evaluate?.(item, item._circuitInputState);
boards.push(item);
}
for (const wire of buckets.wires || []) {
if (!isWire(wire)) continue;
const pb = global.TarinaiPhysicsBodySystem;
const endpointA = pb?.endpoint?.(wire, 0);
@ -324,13 +379,10 @@
global.TarinaiAchievements?.recordCircuitBoardSignalTransfer?.(sourceBoard, targetBoard, entry.wire, { world: worldRef });
}
for (const item of worldRef.items || []) {
if (!item || item.dead) continue;
if (DIRECT_SIGNAL_SINK_TYPES.has(item.type) && !componentOf.has(item)) {
item._wireSignalDriven = false; item._wireSignalOn = false;
setEffectivePower(item, false, false, worldRef);
}
if (isWire(item) && !wires.some(entry => entry.wire === item)) item.signalActive = false;
for (const item of buckets.sinks || []) {
if (!item || item.dead || componentOf.has(item)) continue;
item._wireSignalDriven = false; item._wireSignalOn = false;
setEffectivePower(item, false, false, worldRef);
}
if (wireGeometryMoved) {
worldRef.drawListDirty = true;
@ -339,8 +391,6 @@
return wires;
}
const distanceToSegment = (px, py, ax, ay, bx, by) => global.TarinaiGeometry.pointSegmentDistance(px, py, ax, ay, bx, by);
function shockTarget(worldRef, wire, target, now, { connected = false } = {}) {
if (!target || target.dead) return false;
if (!wire._shockCooldowns || typeof wire._shockCooldowns.get !== "function") wire._shockCooldowns = new WeakMap();
@ -391,13 +441,13 @@
const near = worldRef.nearbyTarinai?.(cx, cy, radius, true) || worldRef.tarinai || [];
for (const t of near) {
if (!t || t.dead || worldRef.isTarinaiHiddenInNestBox?.(t)) continue;
if (distanceToSegment(t.x, t.y, pointA.x, pointA.y, pointB.x, pointB.y) > Math.max(8, num(t.radius, 20) * 0.48)) continue;
if (global.TarinaiGeometry.pointSegmentDistance(t.x, t.y, pointA.x, pointA.y, pointB.x, pointB.y) > Math.max(8, num(t.radius, 20) * 0.48)) continue;
shockTarget(worldRef, wire, t, now, { connected: false });
}
const ants = worldRef.nearbyAnts?.(cx, cy, radius, true) || worldRef.ants || [];
for (const ant of ants) {
if (!ant || ant.dead) continue;
if (distanceToSegment(ant.x, ant.y, pointA.x, pointA.y, pointB.x, pointB.y) > Math.max(5, num(ant.r, 6) * 0.9)) continue;
if (global.TarinaiGeometry.pointSegmentDistance(ant.x, ant.y, pointA.x, pointA.y, pointB.x, pointB.y) > Math.max(5, num(ant.r, 6) * 0.9)) continue;
shockTarget(worldRef, wire, ant, now, { connected: false });
}
}
@ -409,10 +459,30 @@
}
function updateWorld(worldRef, dt = 0.016) {
if (!worldRef?.items) return false;
updatePressureSwitches(worldRef);
const wires = buildSignalNetwork(worldRef);
damageFromWires(worldRef, wires);
if (!worldRef) return false;
const buckets = collectSignalBuckets(worldRef, false);
const hasSwitches = buckets.pressureSwitches.length > 0;
const hasNetwork = buckets.wires.length > 0 || buckets.boards.length > 0;
// Normal worlds with no signal controllers stop after four O(1) bucket
// lookups. Sink buckets are only touched while a network exists or once
// when a removed network needs to clear stale power flags.
if (!hasSwitches && !hasNetwork && !worldRef._signalSystemHadNetwork) return false;
if (hasSwitches) updatePressureSwitches(worldRef, buckets.pressureSwitches);
if (!hasNetwork) {
if (worldRef._signalSystemHadNetwork) {
collectSignalSinks(worldRef, buckets);
clearSignalSinks(worldRef, buckets.sinks);
}
worldRef._signalSystemHadNetwork = false;
return hasSwitches;
}
collectSignalSinks(worldRef, buckets);
const wires = buildSignalNetwork(worldRef, buckets);
worldRef._signalSystemHadNetwork = true;
if (wires.length) damageFromWires(worldRef, wires);
return true;
}
@ -429,5 +499,6 @@
DETECTOR_METRICS,
PRESSURE_TARGETS,
detectorMetricValue,
normalizeMinute,
});
})(typeof window !== "undefined" ? window : globalThis);