This commit is contained in:
33333-33333 2026-07-29 20:52:11 +09:00
commit 091afc2b5c
77 changed files with 4405 additions and 8223 deletions

View file

@ -185,13 +185,22 @@
}
}
function setEffectivePower(node, directDriven, directOn, worldRef) {
function setEffectivePower(node, directDriven, directOn, worldRef, provenance = {}) {
const wasOn = Boolean(node?._achievementSignalWasOn);
node._directSignalDriven = Boolean(directDriven);
node._directSignalOn = Boolean(directDriven && directOn);
node._achievementSignalWasOn = node._directSignalOn;
if (!wasOn && node._directSignalOn) {
global.TarinaiAchievements?.recordSignalActivation?.({ world: worldRef, target: node, type: node.type });
const detectorSources = [...new Set((Array.isArray(provenance.detectorSources) ? provenance.detectorSources : []).filter(Boolean))];
global.TarinaiAchievements?.recordSignalActivation?.({
world: worldRef,
target: node,
type: node.type,
source: detectorSources[0] || null,
detectorSources,
detectorDriven: detectorSources.length > 0,
connected: Boolean(directDriven),
});
if (node.type === "robot_cleaner") global.TarinaiAchievements?.evaluateCleanFreak?.(worldRef, { robot: node, source: "signal-power" });
}
node._signalDriven = node._directSignalDriven;
@ -323,7 +332,12 @@
visited.add(node); nodes.push(node);
for (const next of adjacency.get(node) || []) if (!visited.has(next)) stack.push(next);
}
const component = { nodes, active: false, hasSource: nodes.some(node => SIGNAL_SOURCE_TYPES.has(node.type) || (node.type === "circuit_terminal" && node.direction === "output")) };
const component = {
nodes,
active: false,
hasSource: nodes.some(node => SIGNAL_SOURCE_TYPES.has(node.type) || (node.type === "circuit_terminal" && node.direction === "output")),
detectorSources: [],
};
components.push(component);
nodes.forEach(node => componentOf.set(node, component));
}
@ -331,33 +345,55 @@
// Board outputs can depend on other boards, so resolve the graph to a
// fixed point. Unstable internal combinational cycles are forced OFF by the
// board evaluator; external feedback is still bounded to avoid hanging the simulation.
const detectorSourceSignature = sources => [...new Set((sources || []).map(source => String(source?.id || source?.familyKey || ""))).values()].sort().join("|");
const refreshComponentState = component => {
const directDetectors = component.nodes.filter(node => SIGNAL_SOURCE_TYPES.has(node.type) && node.signalActive);
const outputDetectors = component.nodes.flatMap(node => (
node.type === "circuit_terminal" && node.direction === "output" && node.signalActive
? (Array.isArray(node._signalDetectorSources) ? node._signalDetectorSources : [])
: []
));
const nextSources = [...new Set([...directDetectors, ...outputDetectors].filter(Boolean))];
const active = component.hasSource && component.nodes.some(node => (
(SIGNAL_SOURCE_TYPES.has(node.type) && node.signalActive)
|| (node.type === "circuit_terminal" && node.direction === "output" && node.signalActive)
));
const changed = active !== component.active
|| detectorSourceSignature(nextSources) !== detectorSourceSignature(component.detectorSources);
component.active = active;
component.detectorSources = active ? nextSources : [];
return changed;
};
const maxCircuitPasses = Math.min(256, Math.max(16, boards.length * 2 + 4));
for (let pass = 0; pass < maxCircuitPasses; pass += 1) {
let changed = false;
for (const component of components) {
const active = component.hasSource && component.nodes.some(node => (SIGNAL_SOURCE_TYPES.has(node.type) && node.signalActive) || (node.type === "circuit_terminal" && node.direction === "output" && node.signalActive));
if (active !== component.active) { component.active = active; changed = true; }
}
for (const component of components) changed = refreshComponentState(component) || changed;
for (const board of boards) {
const inputs = Object.create(null);
const inputProvenance = Object.create(null);
for (const port of circuit?.ports?.(board) || []) {
if (port.direction !== "input") continue;
const terminal = circuit.terminal(board, port.portId);
inputs[port.id] = Boolean(componentOf.get(terminal)?.active);
const component = componentOf.get(terminal);
inputs[port.id] = Boolean(component?.active);
inputProvenance[port.id] = component?.detectorSources || [];
}
const before = JSON.stringify(board._circuitOutputState || {});
circuit?.evaluate?.(board, inputs);
if (before !== JSON.stringify(board._circuitOutputState || {})) changed = true;
const beforeSources = JSON.stringify(Object.fromEntries(Object.entries(board._circuitOutputDetectorSources || {}).map(([id, sources]) => [id, detectorSourceSignature(sources)])));
circuit?.evaluate?.(board, inputs, inputProvenance);
const afterSources = JSON.stringify(Object.fromEntries(Object.entries(board._circuitOutputDetectorSources || {}).map(([id, sources]) => [id, detectorSourceSignature(sources)])));
if (before !== JSON.stringify(board._circuitOutputState || {}) || beforeSources !== afterSources) changed = true;
}
if (!changed) break;
}
for (const component of components) {
component.active = component.hasSource && component.nodes.some(node => (SIGNAL_SOURCE_TYPES.has(node.type) && node.signalActive) || (node.type === "circuit_terminal" && node.direction === "output" && node.signalActive));
refreshComponentState(component);
for (const node of component.nodes) {
if (!DIRECT_SIGNAL_SINK_TYPES.has(node.type)) continue;
node._wireSignalDriven = component.hasSource;
node._wireSignalOn = component.hasSource && component.active;
setEffectivePower(node, component.hasSource, component.active, worldRef);
setEffectivePower(node, component.hasSource, component.active, worldRef, { detectorSources: component.detectorSources });
}
}
for (const entry of wires) {