soiz1's picture
Upload 811 files
30c32c8 verified
raw
history blame
12.9 kB
// Some parts of this scripts are based on or designed to be compatible-ish with:
// https://arpruss.github.io/gamepad.js (MIT Licensed)
const ExtensionApi = require("../../util/custom-ext-api-to-core.js");
const Scratch = new ExtensionApi(true);
const AXIS_DEADZONE = 0.1;
const BUTTON_DEADZONE = 0.05;
/**
* @param {number|'any'} index 1-indexed index
* @returns {Gamepad[]}
*/
const getGamepads = (index) => {
if (index === 'any') {
return navigator.getGamepads().filter(i => i);
}
const gamepad = navigator.getGamepads()[index - 1];
if (gamepad) {
return [gamepad];
}
return [];
};
/**
* @param {Gamepad} gamepad
* @param {number|'any'} buttonIndex 1-indexed index
* @returns {boolean} false if button does not exist
*/
const isButtonPressed = (gamepad, buttonIndex) => {
if (buttonIndex === 'any') {
return gamepad.buttons.some(i => i.pressed);
}
const button = gamepad.buttons[buttonIndex - 1];
if (!button) {
return false;
}
return button.pressed;
};
/**
* @param {Gamepad} gamepad
* @param {number} buttonIndex 1-indexed index
* @returns {number} 0 if button does not exist
*/
const getButtonValue = (gamepad, buttonIndex) => {
const button = gamepad.buttons[buttonIndex - 1];
if (!button) {
return 0;
}
const value = button.value;
if (value < BUTTON_DEADZONE) {
return 0;
}
return value;
};
/**
* @param {Gamepad} gamepad
* @param {number} axisIndex 1-indexed index
* @returns {number} 0 if axis does not exist
*/
const getAxisValue = (gamepad, axisIndex) => {
const axisValue = gamepad.axes[axisIndex - 1];
if (typeof axisValue !== 'number') {
return 0;
}
if (Math.abs(axisValue) < AXIS_DEADZONE) {
return 0;
}
return axisValue;
};
class GamepadExtension {
getInfo() {
return {
id: 'Gamepad',
name: 'Gamepad',
blocks: [
{
opcode: 'gamepadConnected',
blockType: Scratch.BlockType.BOOLEAN,
text: 'is gamepad [pad] connected?',
arguments: {
pad: {
type: Scratch.ArgumentType.NUMBER,
defaultValue: '1',
menu: 'padMenu'
}
}
},
{
opcode: 'buttonDown',
blockType: Scratch.BlockType.BOOLEAN,
text: 'button [b] on pad [i] pressed?',
arguments: {
b: {
type: Scratch.ArgumentType.NUMBER,
defaultValue: '1',
menu: 'buttonMenu'
},
i: {
type: Scratch.ArgumentType.NUMBER,
defaultValue: '1',
menu: 'padMenu'
}
}
},
{
opcode: 'buttonValue',
blockType: Scratch.BlockType.REPORTER,
text: 'value of button [b] on pad [i]',
arguments: {
b: {
type: Scratch.ArgumentType.NUMBER,
defaultValue: '1',
menu: 'buttonMenu'
},
i: {
type: Scratch.ArgumentType.NUMBER,
defaultValue: '1',
menu: 'padMenu'
}
}
},
{
opcode: 'axisValue',
blockType: Scratch.BlockType.REPORTER,
text: 'value of axis [b] on pad [i]',
arguments: {
b: {
type: Scratch.ArgumentType.NUMBER,
defaultValue: '1',
menu: 'axisMenu'
},
i: {
type: Scratch.ArgumentType.NUMBER,
defaultValue: '1',
menu: 'padMenu'
},
},
},
'---',
{
opcode: 'axisDirection',
blockType: Scratch.BlockType.REPORTER,
text: 'direction of axes [axis] on pad [pad]',
arguments: {
axis: {
type: Scratch.ArgumentType.NUMBER,
defaultValue: '1',
menu: 'axesGroupMenu'
},
pad: {
type: Scratch.ArgumentType.NUMBER,
defaultValue: '1',
menu: 'padMenu'
}
}
},
{
opcode: 'axisMagnitude',
blockType: Scratch.BlockType.REPORTER,
text: 'magnitude of axes [axis] on pad [pad]',
arguments: {
axis: {
type: Scratch.ArgumentType.NUMBER,
defaultValue: '1',
menu: 'axesGroupMenu'
},
pad: {
type: Scratch.ArgumentType.NUMBER,
defaultValue: '1',
menu: 'padMenu'
}
}
},
/*
{
opcode: 'buttonPressedReleased',
blockType: Scratch.BlockType.HAT,
text: 'button [b] [pr] of pad [i]',
arguments: {
b: {
type: Scratch.ArgumentType.NUMBER,
defaultValue: '1'
},
pr: {
type: Scratch.ArgumentType.NUMBER,
defaultValue: '1',
menu: 'pressReleaseMenu'
},
i: {
type: Scratch.ArgumentType.NUMBER,
defaultValue: '1',
menu: 'padMenu'
},
},
},
{
opcode: 'axisMoved',
blockType: Scratch.BlockType.HAT,
text: 'axis [b] of pad [i] moved',
arguments: {
b: {
type: Scratch.ArgumentType.NUMBER,
defaultValue: '1'
},
i: {
type: Scratch.ArgumentType.NUMBER,
defaultValue: '1',
menu: 'padMenu'
},
},
},
*/
'---',
{
opcode: 'rumble',
blockType: Scratch.BlockType.COMMAND,
text: 'rumble strong [s] and weak [w] for [t] sec. on pad [i]',
arguments: {
s: {
type: Scratch.ArgumentType.NUMBER,
defaultValue: '0.25'
},
w: {
type: Scratch.ArgumentType.NUMBER,
defaultValue: '0.5'
},
t: {
type: Scratch.ArgumentType.NUMBER,
defaultValue: '0.25'
},
i: {
type: Scratch.ArgumentType.NUMBER,
defaultValue: '1',
menu: 'padMenu'
},
},
},
],
menus: {
padMenu: {
acceptReporters: true,
items: [
{
text: 'any',
value: 'any'
},
{
text: '1',
value: '1'
},
{
text: '2',
value: '2'
},
{
text: '3',
value: '3'
},
{
text: '4',
value: '4'
}
],
},
buttonMenu: {
acceptReporters: true,
items: [
// Based on an Xbox controller
{
text: 'any',
value: 'any'
},
{
text: 'A (1)',
value: '1'
},
{
text: 'B (2)',
value: '2'
},
{
text: 'X (3)',
value: '3'
},
{
text: 'Y (4)',
value: '4'
},
{
text: 'Left bumper (5)',
value: '5'
},
{
text: 'Right bumper (6)',
value: '6'
},
{
text: 'Left trigger (7)',
value: '7'
},
{
text: 'Right trigger (8)',
value: '8'
},
{
text: 'Select/View (9)',
value: '9'
},
{
text: 'Start/Menu (10)',
value: '10'
},
{
text: 'Left stick (11)',
value: '11'
},
{
text: 'Right stick (12)',
value: '12'
},
{
text: 'D-pad up (13)',
value: '13'
},
{
text: 'D-pad down (14)',
value: '14'
},
{
text: 'D-pad left (15)',
value: '15'
},
{
text: 'D-pad right (16)',
value: '16'
},
]
},
axisMenu: {
acceptReporters: true,
items: [
// Based on an Xbox controller
{
text: 'Left stick horizontal (1)',
value: '1'
},
{
text: 'Left stick vertical (2)',
value: '2'
},
{
text: 'Right stick horizontal (3)',
value: '3'
},
{
text: 'Right stick vertical (4)',
value: '4'
}
]
},
axesGroupMenu: {
acceptReporters: true,
items: [
// Based on an Xbox controller
{
text: 'Left stick (1 & 2)',
value: '1'
},
{
text: 'Right stick (3 & 4)',
value: '3'
}
]
},
/*
pressReleaseMenu: [
{
text: 'press',
value: 1
},
{
text: 'release',
value: 0
}
],
*/
}
};
}
gamepadConnected ({pad}) {
return getGamepads(pad).length > 0;
}
buttonDown ({b, i}) {
for (const gamepad of getGamepads(i)) {
if (isButtonPressed(gamepad, b)) {
return true;
}
}
return false;
}
buttonValue ({b, i}) {
let greatestButton = 0;
for (const gamepad of getGamepads(i)) {
const value = getButtonValue(gamepad, b);
if (value > greatestButton) {
greatestButton = value;
}
}
return greatestButton;
}
axisValue ({b, i}) {
let greatestAxis = 0;
for (const gamepad of getGamepads(i)) {
const axis = getAxisValue(gamepad, b);
if (Math.abs(axis) > Math.abs(greatestAxis)) {
greatestAxis = axis;
}
}
return greatestAxis;
}
axisDirection ({axis, pad}) {
let greatestMagnitude = 0;
let direction = 90;
for (const gamepad of getGamepads(pad)) {
const horizontalAxis = getAxisValue(gamepad, axis);
const verticalAxis = getAxisValue(gamepad, +axis + 1);
const magnitude = Math.sqrt(horizontalAxis ** 2 + verticalAxis ** 2);
if (magnitude > greatestMagnitude) {
greatestMagnitude = magnitude;
direction = Math.atan2(verticalAxis, horizontalAxis) * 180 / Math.PI + 90;
if (direction < 0) {
direction += 360;
}
}
}
return direction;
}
axisMagnitude ({axis, pad}) {
let greatestMagnitude = 0;
for (const gamepad of getGamepads(pad)) {
const horizontalAxis = getAxisValue(gamepad, axis);
const verticalAxis = getAxisValue(gamepad, +axis + 1);
const magnitude = Math.sqrt(horizontalAxis ** 2 + verticalAxis ** 2);
if (magnitude > greatestMagnitude) {
greatestMagnitude = magnitude;
}
}
return greatestMagnitude;
}
rumble ({s, w, t, i}) {
const gamepads = getGamepads(i);
for (const gamepad of gamepads) {
// @ts-ignore
if (gamepad.vibrationActuator) {
// @ts-ignore
gamepad.vibrationActuator.playEffect('dual-rumble', {
startDelay: 0,
duration: t * 1000,
weakMagnitude: w,
strongMagnitude: s
});
}
}
}
}
module.exports = GamepadExtension;