mirror of
https://github.com/litruv/picoGraph.git
synced 2026-07-24 02:36:04 +10:00
Initial commit
moved from private version control
This commit is contained in:
55
scripts/nodes/library/input/btn.js
Normal file
55
scripts/nodes/library/input/btn.js
Normal file
@@ -0,0 +1,55 @@
|
||||
import { createNodeModule } from "../../nodeTypes.js";
|
||||
|
||||
/**
|
||||
* Reads the state of a PICO-8 button using BTN.
|
||||
*/
|
||||
export const btnNode = createNodeModule(
|
||||
{
|
||||
id: "input_btn",
|
||||
title: "Button State",
|
||||
category: "Input",
|
||||
description: "Read the current pressed state for a controller button.",
|
||||
searchTags: ["btn", "input", "button", "press"],
|
||||
inputs: [
|
||||
{
|
||||
id: "button",
|
||||
name: "Button",
|
||||
direction: "input",
|
||||
kind: "number",
|
||||
description: "Button index or glyph code",
|
||||
},
|
||||
{
|
||||
id: "player",
|
||||
name: "Player",
|
||||
direction: "input",
|
||||
kind: "number",
|
||||
description: "Optional player index",
|
||||
},
|
||||
],
|
||||
outputs: [
|
||||
{ id: "value", name: "Pressed", direction: "output", kind: "boolean" },
|
||||
],
|
||||
properties: [],
|
||||
},
|
||||
{
|
||||
evaluateValue: ({ resolveValueInput }) => {
|
||||
const OMIT = "__pg_omit__";
|
||||
const button = resolveValueInput("button", OMIT);
|
||||
const player = resolveValueInput("player", OMIT);
|
||||
|
||||
if (button === OMIT && player === OMIT) {
|
||||
return "btn()";
|
||||
}
|
||||
|
||||
if (button === OMIT) {
|
||||
return `btn(nil, ${player})`;
|
||||
}
|
||||
|
||||
if (player === OMIT) {
|
||||
return `btn(${button})`;
|
||||
}
|
||||
|
||||
return `btn(${button}, ${player})`;
|
||||
},
|
||||
}
|
||||
);
|
||||
56
scripts/nodes/library/input/btnp.js
Normal file
56
scripts/nodes/library/input/btnp.js
Normal file
@@ -0,0 +1,56 @@
|
||||
import { createNodeModule } from "../../nodeTypes.js";
|
||||
|
||||
/**
|
||||
* Reads button press transitions using BTNP.
|
||||
*/
|
||||
export const btnpNode = createNodeModule(
|
||||
{
|
||||
id: "input_btnp",
|
||||
title: "Button Press",
|
||||
category: "Input",
|
||||
description:
|
||||
"Detect when a button is initially pressed or repeats using BTNP.",
|
||||
searchTags: ["btnp", "input", "button", "press", "repeat"],
|
||||
inputs: [
|
||||
{
|
||||
id: "button",
|
||||
name: "Button",
|
||||
direction: "input",
|
||||
kind: "number",
|
||||
description: "Button index or glyph code",
|
||||
},
|
||||
{
|
||||
id: "player",
|
||||
name: "Player",
|
||||
direction: "input",
|
||||
kind: "number",
|
||||
description: "Optional player index",
|
||||
},
|
||||
],
|
||||
outputs: [
|
||||
{ id: "value", name: "Pressed", direction: "output", kind: "boolean" },
|
||||
],
|
||||
properties: [],
|
||||
},
|
||||
{
|
||||
evaluateValue: ({ resolveValueInput }) => {
|
||||
const OMIT = "__pg_omit__";
|
||||
const button = resolveValueInput("button", OMIT);
|
||||
const player = resolveValueInput("player", OMIT);
|
||||
|
||||
if (button === OMIT && player === OMIT) {
|
||||
return "btnp()";
|
||||
}
|
||||
|
||||
if (button === OMIT) {
|
||||
return `btnp(nil, ${player})`;
|
||||
}
|
||||
|
||||
if (player === OMIT) {
|
||||
return `btnp(${button})`;
|
||||
}
|
||||
|
||||
return `btnp(${button}, ${player})`;
|
||||
},
|
||||
}
|
||||
);
|
||||
175
scripts/nodes/library/input/controllerButtons.js
Normal file
175
scripts/nodes/library/input/controllerButtons.js
Normal file
@@ -0,0 +1,175 @@
|
||||
import { createNodeModule } from "../../nodeTypes.js";
|
||||
|
||||
const DEFAULT_PLAYER_VALUE = "0";
|
||||
|
||||
/**
|
||||
* Normalizes the stored player property to a valid controller index.
|
||||
*
|
||||
* @param {unknown} value Raw inspector value.
|
||||
* @returns {number}
|
||||
*/
|
||||
const resolvePlayerIndex = (value) => {
|
||||
const numeric = Number.parseInt(
|
||||
typeof value === "string" ? value : String(value ?? DEFAULT_PLAYER_VALUE),
|
||||
10
|
||||
);
|
||||
if (!Number.isFinite(numeric) || numeric < 0) {
|
||||
return 0;
|
||||
}
|
||||
if (numeric > 7) {
|
||||
return 7;
|
||||
}
|
||||
return numeric;
|
||||
};
|
||||
|
||||
const BUTTON_OPTIONS = [
|
||||
{ value: "0", label: "Left" },
|
||||
{ value: "1", label: "Right" },
|
||||
{ value: "2", label: "Up" },
|
||||
{ value: "3", label: "Down" },
|
||||
{ value: "4", label: "O" },
|
||||
{ value: "5", label: "X" },
|
||||
];
|
||||
|
||||
const BUTTON_VALUE_SET = new Set(BUTTON_OPTIONS.map((option) => option.value));
|
||||
|
||||
const DEFAULT_BUTTON_VALUE = BUTTON_OPTIONS[0]?.value ?? "0";
|
||||
|
||||
/**
|
||||
* Normalizes button property values to a valid BTN index represented as a string.
|
||||
*
|
||||
* @param {unknown} value Raw inspector value.
|
||||
* @returns {string}
|
||||
*/
|
||||
const resolveButtonValue = (value) => {
|
||||
const numeric = Number.parseInt(
|
||||
typeof value === "string" ? value : String(value ?? DEFAULT_BUTTON_VALUE),
|
||||
10
|
||||
);
|
||||
|
||||
const candidate = Number.isFinite(numeric) ? String(numeric) : DEFAULT_BUTTON_VALUE;
|
||||
if (BUTTON_VALUE_SET.has(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
return DEFAULT_BUTTON_VALUE;
|
||||
};
|
||||
|
||||
const PLAYER_OPTIONS = [
|
||||
{ value: "0", label: "Player 0 (Cursors / Z X)" },
|
||||
{ value: "1", label: "Player 1 (SFED / Tab Q W A)" },
|
||||
{ value: "2", label: "Player 2" },
|
||||
{ value: "3", label: "Player 3" },
|
||||
{ value: "4", label: "Player 4" },
|
||||
{ value: "5", label: "Player 5" },
|
||||
{ value: "6", label: "Player 6" },
|
||||
{ value: "7", label: "Player 7" },
|
||||
];
|
||||
|
||||
/**
|
||||
* Evaluates whether the configured button is currently pressed.
|
||||
*/
|
||||
export const controllerButtonsNode = createNodeModule(
|
||||
{
|
||||
id: "input_button_constants",
|
||||
title: "Named Button",
|
||||
category: "Input",
|
||||
description:
|
||||
"Read a specific controller button with a dropdown selector and optional player override.",
|
||||
searchTags: [
|
||||
"button",
|
||||
"controller",
|
||||
"dpad",
|
||||
"input",
|
||||
"player",
|
||||
"named",
|
||||
"press",
|
||||
],
|
||||
inputs: [
|
||||
{
|
||||
id: "button",
|
||||
name: "Button",
|
||||
direction: "input",
|
||||
kind: "number",
|
||||
description: "Optional button index override",
|
||||
},
|
||||
{
|
||||
id: "player",
|
||||
name: "Player",
|
||||
direction: "input",
|
||||
kind: "number",
|
||||
description: "Optional player index override",
|
||||
},
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
id: "pressed",
|
||||
name: "Pressed",
|
||||
direction: "output",
|
||||
kind: "boolean",
|
||||
},
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
key: "button",
|
||||
label: "Button",
|
||||
type: "enum",
|
||||
defaultValue: DEFAULT_BUTTON_VALUE,
|
||||
options: BUTTON_OPTIONS,
|
||||
},
|
||||
{
|
||||
key: "player",
|
||||
label: "Default Player",
|
||||
type: "enum",
|
||||
defaultValue: DEFAULT_PLAYER_VALUE,
|
||||
options: PLAYER_OPTIONS,
|
||||
},
|
||||
],
|
||||
initializeProperties: (properties) => {
|
||||
properties.button = resolveButtonValue(properties.button);
|
||||
const normalizedPlayer = resolvePlayerIndex(properties.player);
|
||||
properties.player = String(normalizedPlayer);
|
||||
const buttonInlineKey = "pin:button";
|
||||
const playerInlineKey = "pin:player";
|
||||
const numericButton = Number.parseInt(properties.button, 10);
|
||||
const numericPlayer = normalizedPlayer;
|
||||
properties[buttonInlineKey] = Number.isFinite(numericButton)
|
||||
? numericButton
|
||||
: 0;
|
||||
properties[playerInlineKey] = Number.isFinite(numericPlayer)
|
||||
? numericPlayer
|
||||
: 0;
|
||||
},
|
||||
onPropertiesChanged: (properties) => {
|
||||
properties.button = resolveButtonValue(properties.button);
|
||||
const normalizedPlayer = resolvePlayerIndex(properties.player);
|
||||
properties.player = String(normalizedPlayer);
|
||||
const buttonInlineKey = "pin:button";
|
||||
const playerInlineKey = "pin:player";
|
||||
const numericButton = Number.parseInt(properties.button, 10);
|
||||
const numericPlayer = normalizedPlayer;
|
||||
properties[buttonInlineKey] = Number.isFinite(numericButton)
|
||||
? numericButton
|
||||
: 0;
|
||||
properties[playerInlineKey] = Number.isFinite(numericPlayer)
|
||||
? numericPlayer
|
||||
: 0;
|
||||
},
|
||||
},
|
||||
{
|
||||
evaluateValue: ({ node, resolveValueInput, formatLiteral }) => {
|
||||
const buttonValue = resolveButtonValue(node.properties.button);
|
||||
const parsedButton = Number.parseInt(buttonValue, 10);
|
||||
const buttonIndex = Number.isFinite(parsedButton) ? parsedButton : 0;
|
||||
const fallbackButton = formatLiteral("number", buttonIndex);
|
||||
const buttonInput = resolveValueInput("button", fallbackButton);
|
||||
|
||||
const fallbackPlayer = formatLiteral(
|
||||
"number",
|
||||
resolvePlayerIndex(node.properties.player)
|
||||
);
|
||||
const playerInput = resolveValueInput("player", fallbackPlayer);
|
||||
|
||||
return `btn(${buttonInput}, ${playerInput})`;
|
||||
},
|
||||
}
|
||||
);
|
||||
17
scripts/nodes/library/input/index.js
Normal file
17
scripts/nodes/library/input/index.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import { btnNode } from "./btn.js";
|
||||
import { btnpNode } from "./btnp.js";
|
||||
import { controllerButtonsNode } from "./controllerButtons.js";
|
||||
|
||||
/**
|
||||
* All input-related node modules backed by PICO-8 helpers.
|
||||
*/
|
||||
export const inputNodes = [controllerButtonsNode, btnNode, btnpNode];
|
||||
|
||||
/**
|
||||
* Checklist tracking coverage of documented input helpers.
|
||||
* @type {Array<{ function: string, nodeId: string, implemented: boolean }>}
|
||||
*/
|
||||
export const inputFunctionChecklist = [
|
||||
{ function: "BTN", nodeId: "input_btn", implemented: true },
|
||||
{ function: "BTNP", nodeId: "input_btnp", implemented: true },
|
||||
];
|
||||
Reference in New Issue
Block a user