diff --git a/webclient/src/blockly/actions/consoleLog.ts b/webclient/src/blockly/actions/consoleLog.ts index 337e982..25be994 100644 --- a/webclient/src/blockly/actions/consoleLog.ts +++ b/webclient/src/blockly/actions/consoleLog.ts @@ -1,24 +1,22 @@ import * as Blockly from "blockly"; -import { javascriptGenerator, Order } from "blockly/javascript"; +import { Order } from "blockly/javascript"; +import type { BlockDef } from "../toolbox"; -Blockly.Blocks['console_log_action'] = { - init(this: Blockly.Block) { - this.appendValueInput('VALUE') - .setAlign(Blockly.inputs.Align.RIGHT) - // .setCheck('String') - .appendField('print'); - this.setInputsInline(false); - this.setPreviousStatement(true, null); - this.setNextStatement(true, null); - this.setTooltip('Print value to console'); - this.setHelpUrl(''); - this.setColour(150); +export const consoleLogAction: BlockDef = { + category: 'Actions', + block: { + init(this: Blockly.Block) { + this.appendValueInput('VALUE') + .setAlign(Blockly.inputs.Align.RIGHT) + .appendField('print'); + this.setInputsInline(false); + this.setPreviousStatement(true, null); + this.setNextStatement(true, null); + this.setTooltip('Print value to console'); + } + }, + code(block, generator) { + const value = generator.valueToCode(block, 'VALUE', Order.ATOMIC); + return `console.log(${value});\n`; } }; - -javascriptGenerator.forBlock['console_log_action'] = function (block, generator) { - const valueValue = generator.valueToCode(block, 'VALUE', Order.ATOMIC); - return `console.log(${valueValue});\n`; -}; - -export { }; diff --git a/webclient/src/blockly/actions/index.ts b/webclient/src/blockly/actions/index.ts index 014a712..5ff569f 100644 --- a/webclient/src/blockly/actions/index.ts +++ b/webclient/src/blockly/actions/index.ts @@ -1,3 +1,4 @@ export * from './consoleLog'; -export * from './physics'; +export * from './stopGame'; export * from './waitSeconds'; +export * from './physics'; diff --git a/webclient/src/blockly/actions/physics/applyImpulse.ts b/webclient/src/blockly/actions/physics/applyImpulse.ts index 20007ef..32ee84d 100644 --- a/webclient/src/blockly/actions/physics/applyImpulse.ts +++ b/webclient/src/blockly/actions/physics/applyImpulse.ts @@ -1,29 +1,25 @@ import * as Blockly from "blockly"; -import { javascriptGenerator, Order } from "blockly/javascript"; +import { Order } from "blockly/javascript"; +import type { BlockDef } from "../../toolbox"; -Blockly.Blocks['physics_apply_impulse_action'] = { - init(this: Blockly.Block) { - this.appendDummyInput('') - .appendField('Push me in direction of'); - this.appendValueInput('POSITION') - .setCheck('DPos'); - this.appendDummyInput('') - .appendField('with force') - .appendField(new Blockly.FieldNumber(1, -10, 10, 0.01), 'FORCE'); - - - this.setInputsInline(true); - this.setPreviousStatement(true, null); - this.setNextStatement(true, null); - this.setTooltip('Push me in a direction'); - this.setColour(150); +export const physicsApplyImpulseAction: BlockDef = { + category: 'Actions', + block: { + init(this: Blockly.Block) { + this.appendDummyInput('').appendField('Push me in direction of'); + this.appendValueInput('POSITION').setCheck('DPos'); + this.appendDummyInput('') + .appendField('with force') + .appendField(new Blockly.FieldNumber(1, -10, 10, 0.01), 'FORCE'); + this.setInputsInline(true); + this.setPreviousStatement(true, null); + this.setNextStatement(true, null); + this.setTooltip('Push me in a direction'); + } + }, + code(block, generator) { + const position = generator.valueToCode(block, 'POSITION', Order.ATOMIC); + const force = block.getFieldValue('FORCE'); + return `yield helpers.actions.ApplyImpulse(${position}, ${force});\n`; } }; - -javascriptGenerator.forBlock['physics_apply_impulse_action'] = function (block, generator) { - const positionValue = generator.valueToCode(block, 'POSITION', Order.ATOMIC); - const forceValue = block.getFieldValue('FORCE'); - return `yield helpers.actions.ApplyImpulse(${positionValue}, ${forceValue});\n`; -}; - -export { }; diff --git a/webclient/src/blockly/actions/physics/index.ts b/webclient/src/blockly/actions/physics/index.ts index d6353f5..17af90c 100644 --- a/webclient/src/blockly/actions/physics/index.ts +++ b/webclient/src/blockly/actions/physics/index.ts @@ -1,3 +1,3 @@ export * from './applyImpulse'; export * from './moveInDirection'; -export * from './moveToPosition'; \ No newline at end of file +export * from './moveToPosition'; diff --git a/webclient/src/blockly/actions/physics/moveInDirection.ts b/webclient/src/blockly/actions/physics/moveInDirection.ts index 0bf860c..8ac8cb8 100644 --- a/webclient/src/blockly/actions/physics/moveInDirection.ts +++ b/webclient/src/blockly/actions/physics/moveInDirection.ts @@ -1,30 +1,28 @@ import * as Blockly from "blockly"; -import { javascriptGenerator, Order } from "blockly/javascript"; +import { Order } from "blockly/javascript"; +import type { BlockDef } from "../../toolbox"; -Blockly.Blocks['move_in_direction_action'] = { - init(this: Blockly.Block) { - this.appendDummyInput() - .appendField('Move me in direction'); - this.appendValueInput('DIRECTION') - .setCheck('DPos'); - this.appendDummyInput() - .appendField('for') - .appendField(new Blockly.FieldNumber(5, 0.01, 10000, 0.1), 'DISTANCE') - .appendField('with speed') - .appendField(new Blockly.FieldNumber(5, 0.01, 1000, 0.1), 'SPEED'); - this.setInputsInline(true); - this.setPreviousStatement(true, null); - this.setNextStatement(true, null); - this.setTooltip('Move this object in the given direction for the given distance at the given speed (units/sec), blocking until done'); - this.setColour(20); +export const moveInDirectionAction: BlockDef = { + category: 'Actions', + block: { + init(this: Blockly.Block) { + this.appendDummyInput().appendField('Move me in direction'); + this.appendValueInput('DIRECTION').setCheck('DPos'); + this.appendDummyInput() + .appendField('for') + .appendField(new Blockly.FieldNumber(5, 0.01, 10000, 0.1), 'DISTANCE') + .appendField('with speed') + .appendField(new Blockly.FieldNumber(5, 0.01, 1000, 0.1), 'SPEED'); + this.setInputsInline(true); + this.setPreviousStatement(true, null); + this.setNextStatement(true, null); + this.setTooltip('Move this object in the given direction for the given distance at the given speed (units/sec), blocking until done'); + } + }, + code(block, generator) { + const direction = generator.valueToCode(block, 'DIRECTION', Order.ATOMIC) || '[0,0,1]'; + const distance = block.getFieldValue('DISTANCE'); + const speed = block.getFieldValue('SPEED'); + return `yield helpers.actions.MoveInDirection(${direction}, ${distance}, ${speed});\n`; } }; - -javascriptGenerator.forBlock['move_in_direction_action'] = function (block, generator) { - const direction = generator.valueToCode(block, 'DIRECTION', Order.ATOMIC) || '[0,0,1]'; - const distance = block.getFieldValue('DISTANCE'); - const speed = block.getFieldValue('SPEED'); - return `yield helpers.actions.MoveInDirection(${direction}, ${distance}, ${speed});\n`; -}; - -export { }; diff --git a/webclient/src/blockly/actions/physics/moveToPosition.ts b/webclient/src/blockly/actions/physics/moveToPosition.ts index 479dbf1..9d039fa 100644 --- a/webclient/src/blockly/actions/physics/moveToPosition.ts +++ b/webclient/src/blockly/actions/physics/moveToPosition.ts @@ -1,27 +1,25 @@ import * as Blockly from "blockly"; -import { javascriptGenerator, Order } from "blockly/javascript"; +import { Order } from "blockly/javascript"; +import type { BlockDef } from "../../toolbox"; -Blockly.Blocks['move_to_position_action'] = { - init(this: Blockly.Block) { - this.appendDummyInput() - .appendField('Move me to position'); - this.appendValueInput('TARGET_POSITION') - .setCheck('Pos'); - this.appendDummyInput() - .appendField('with speed') - .appendField(new Blockly.FieldNumber(5, 0.01, 1000, 0.1), 'SPEED'); - this.setInputsInline(true); - this.setPreviousStatement(true, null); - this.setNextStatement(true, null); - this.setTooltip('Move this object to the target position at the given speed (units/sec), blocking until arrived'); - this.setColour(20); +export const moveToPositionAction: BlockDef = { + category: 'Actions', + block: { + init(this: Blockly.Block) { + this.appendDummyInput().appendField('Move me to position'); + this.appendValueInput('TARGET_POSITION').setCheck('Pos'); + this.appendDummyInput() + .appendField('with speed') + .appendField(new Blockly.FieldNumber(5, 0.01, 1000, 0.1), 'SPEED'); + this.setInputsInline(true); + this.setPreviousStatement(true, null); + this.setNextStatement(true, null); + this.setTooltip('Move this object to the target position at the given speed (units/sec), blocking until arrived'); + } + }, + code(block, generator) { + const targetPosition = generator.valueToCode(block, 'TARGET_POSITION', Order.ATOMIC) || '[0,0,0]'; + const speed = block.getFieldValue('SPEED'); + return `yield helpers.actions.MoveToPosition(${targetPosition}, ${speed});\n`; } }; - -javascriptGenerator.forBlock['move_to_position_action'] = function (block, generator) { - const targetPosition = generator.valueToCode(block, 'TARGET_POSITION', Order.ATOMIC) || '[0,0,0]'; - const speed = block.getFieldValue('SPEED'); - return `yield helpers.actions.MoveToPosition(${targetPosition}, ${speed});\n`; -}; - -export { }; diff --git a/webclient/src/blockly/actions/stopGame.ts b/webclient/src/blockly/actions/stopGame.ts new file mode 100644 index 0000000..76349b9 --- /dev/null +++ b/webclient/src/blockly/actions/stopGame.ts @@ -0,0 +1,18 @@ +import * as Blockly from "blockly"; +import type { BlockDef } from "../toolbox"; + +export const stopGameAction: BlockDef = { + category: 'Actions', + block: { + init(this: Blockly.Block) { + this.appendDummyInput().appendField('Stop game'); + this.setInputsInline(false); + this.setPreviousStatement(true, null); + this.setNextStatement(true, null); + this.setTooltip('Stop game'); + } + }, + code(_block, _generator) { + return `yield helpers.actions.StopGame();\n`; + } +}; diff --git a/webclient/src/blockly/actions/waitSeconds.ts b/webclient/src/blockly/actions/waitSeconds.ts index d274e97..3fbaa54 100644 --- a/webclient/src/blockly/actions/waitSeconds.ts +++ b/webclient/src/blockly/actions/waitSeconds.ts @@ -1,22 +1,21 @@ import * as Blockly from "blockly"; -import { javascriptGenerator } from "blockly/javascript"; +import type { BlockDef } from "../toolbox"; -Blockly.Blocks['wait_seconds_action'] = { - init(this: Blockly.Block) { - this.appendDummyInput() - .appendField('Wait') - .appendField(new Blockly.FieldNumber(1, 0, 3600, 0.1), 'SECONDS') - .appendField('seconds'); - this.setPreviousStatement(true, null); - this.setNextStatement(true, null); - this.setTooltip('Pause execution for N seconds'); - this.setColour(120); +export const waitSecondsAction: BlockDef = { + category: 'Actions', + block: { + init(this: Blockly.Block) { + this.appendDummyInput() + .appendField('Wait') + .appendField(new Blockly.FieldNumber(1, 0, 3600, 0.1), 'SECONDS') + .appendField('seconds'); + this.setPreviousStatement(true, null); + this.setNextStatement(true, null); + this.setTooltip('Pause execution for N seconds'); + } + }, + code(block, _generator) { + const seconds = block.getFieldValue('SECONDS'); + return `yield helpers.actions.WaitSeconds(${seconds});\n`; } }; - -javascriptGenerator.forBlock['wait_seconds_action'] = function (block) { - const seconds = block.getFieldValue('SECONDS'); - return `yield helpers.actions.WaitSeconds(${seconds});\n`; -}; - -export { }; diff --git a/webclient/src/blockly/events/gameStart.ts b/webclient/src/blockly/events/gameStart.ts index 72cdecc..424afae 100644 --- a/webclient/src/blockly/events/gameStart.ts +++ b/webclient/src/blockly/events/gameStart.ts @@ -1,17 +1,17 @@ import * as Blockly from "blockly"; -import { javascriptGenerator } from "blockly/javascript"; +import type { BlockDef } from "../toolbox"; -Blockly.common.defineBlocksWithJsonArray([{ - "type": "game_start_event", - "message0": "On game start", - "nextStatement": null, - "style": "event_hat", - "tooltip": "Runs when the game starts.", - "helpUrl": "" -}]); - -javascriptGenerator.forBlock['game_start_event'] = function (_block, _generator) { - return ''; +export const gameStartEvent: BlockDef = { + category: 'Events', + block: { + init(this: Blockly.Block) { + this.appendDummyInput().appendField('On game start'); + this.setNextStatement(true, null); + this.setStyle('event_hat'); + this.setTooltip('Runs when the game starts.'); + } + }, + code(_block, _generator) { + return ''; + } }; - -export { }; diff --git a/webclient/src/blockly/events/imClicked.ts b/webclient/src/blockly/events/imClicked.ts index e6aade8..642aae2 100644 --- a/webclient/src/blockly/events/imClicked.ts +++ b/webclient/src/blockly/events/imClicked.ts @@ -1,18 +1,17 @@ import * as Blockly from "blockly"; -import { javascriptGenerator } from "blockly/javascript"; +import type { BlockDef } from "../toolbox"; -Blockly.Blocks['object_clicked_event'] = { - init(this: Blockly.Block) { - this.appendDummyInput() - .appendField('When I\'m clicked'); - this.setStyle('event_hat'); - this.setNextStatement(true); - this.setTooltip('Fires when an object is clicked'); +export const objectClickedEvent: BlockDef = { + category: 'Events', + block: { + init(this: Blockly.Block) { + this.appendDummyInput().appendField('When I\'m clicked'); + this.setStyle('event_hat'); + this.setNextStatement(true); + this.setTooltip('Fires when an object is clicked'); + } + }, + code(_block, _generator) { + return ''; } }; - -javascriptGenerator.forBlock['object_clicked_event'] = function (_block, _generator) { - return ''; -}; - -export { }; diff --git a/webclient/src/blockly/events/objectTouchedMe.ts b/webclient/src/blockly/events/objectTouchedMe.ts index 2d45879..247867e 100644 --- a/webclient/src/blockly/events/objectTouchedMe.ts +++ b/webclient/src/blockly/events/objectTouchedMe.ts @@ -1,27 +1,25 @@ import * as Blockly from "blockly"; -import { javascriptGenerator, Order } from "blockly/javascript"; +import { Order } from "blockly/javascript"; +import type { BlockDef } from "../toolbox"; -Blockly.Blocks['object_touch_start_event'] = { - init(this: Blockly.Block) { - this.appendDummyInput() - .appendField('When'); - this.appendValueInput('OTHER_OBJECT') - .setCheck('Object'); - this.appendDummyInput() - .appendField('touches me'); - this.setStyle('event_hat'); - this.setNextStatement(true); - this.setTooltip('Fires when an object touches me'); - } -}; - -javascriptGenerator.forBlock['object_touch_start_event'] = function (block, generator) { - const otherObjectValue = generator.valueToCode(block, 'OTHER_OBJECT', Order.NONE) || 'undefined'; - return ` +export const objectTouchStartEvent: BlockDef = { + category: 'Events', + block: { + init(this: Blockly.Block) { + this.appendDummyInput().appendField('When'); + this.appendValueInput('OTHER_OBJECT').setCheck('Object'); + this.appendDummyInput().appendField('touches me'); + this.setStyle('event_hat'); + this.setNextStatement(true); + this.setTooltip('Fires when an object touches me'); + } + }, + code(block, generator) { + const otherObjectValue = generator.valueToCode(block, 'OTHER_OBJECT', Order.NONE) || 'undefined'; + return ` const touchTarget = ${otherObjectValue}; if (!context.other || !touchTarget || context.other.id !== touchTarget.id) return; `; + } }; - -export { }; diff --git a/webclient/src/blockly/events/playerTouchedMe.ts b/webclient/src/blockly/events/playerTouchedMe.ts index d25ad2b..791ffe0 100644 --- a/webclient/src/blockly/events/playerTouchedMe.ts +++ b/webclient/src/blockly/events/playerTouchedMe.ts @@ -1,18 +1,17 @@ import * as Blockly from "blockly"; -import { javascriptGenerator } from "blockly/javascript"; +import type { BlockDef } from "../toolbox"; -Blockly.Blocks['player_touch_start_event'] = { - init(this: Blockly.Block) { - this.appendDummyInput() - .appendField('When player touches me'); - this.setStyle('event_hat'); - this.setNextStatement(true); - this.setTooltip('Fires when player touches me'); +export const playerTouchStartEvent: BlockDef = { + category: 'Events', + block: { + init(this: Blockly.Block) { + this.appendDummyInput().appendField('When player touches me'); + this.setStyle('event_hat'); + this.setNextStatement(true); + this.setTooltip('Fires when player touches me'); + } + }, + code(_block, _generator) { + return ''; } }; - -javascriptGenerator.forBlock['player_touch_start_event'] = function (_block, _generator) { - return ''; -}; - -export { }; diff --git a/webclient/src/blockly/generateCode.ts b/webclient/src/blockly/generateCode.ts index 23de1c5..7b85185 100644 --- a/webclient/src/blockly/generateCode.ts +++ b/webclient/src/blockly/generateCode.ts @@ -1,13 +1,13 @@ import * as Blockly from "blockly"; import { javascriptGenerator } from "blockly/javascript"; -export function generateCode(workspace: Blockly.WorkspaceSvg): string { +export function generateCode(workspace: Blockly.Workspace): string { javascriptGenerator.init(workspace); let result = ''; for (const block of workspace.getTopBlocks(true)) { - if (block.type.endsWith('_event')) { + if (block.type.endsWith('Event')) { - const eventName = block.type.replace(/_event$/, ''); + const eventName = block.type.replace(/Event$/, ''); let body = javascriptGenerator.blockToCode(block, true) as string; diff --git a/webclient/src/blockly/index.ts b/webclient/src/blockly/index.ts index 79a6026..4ee0628 100644 --- a/webclient/src/blockly/index.ts +++ b/webclient/src/blockly/index.ts @@ -1,7 +1,8 @@ export * from './events'; export * from './values'; export * from './actions'; +export * from './loops'; export * from './theme'; export * from './generateCode'; export * from './fieldTypes'; -export * from './loops'; +export * from './toolbox'; diff --git a/webclient/src/blockly/loops/forever.ts b/webclient/src/blockly/loops/forever.ts new file mode 100644 index 0000000..07014e3 --- /dev/null +++ b/webclient/src/blockly/loops/forever.ts @@ -0,0 +1,20 @@ +import * as Blockly from "blockly"; +import { javascriptGenerator } from "blockly/javascript"; +import type { BlockDef } from "../toolbox"; + +export const foreverLoop: BlockDef = { + category: 'Loops', + block: { + init(this: Blockly.Block) { + this.appendDummyInput().appendField('Repeat forever'); + this.appendStatementInput('BODY').setCheck(null); + this.setPreviousStatement(true, null); + this.setNextStatement(true, null); + this.setTooltip('Repeat the enclosed actions forever'); + } + }, + code(block, generator) { + const body = generator.statementToCode(block, 'BODY'); + return `while (true) {\n${body}${javascriptGenerator.INDENT}yield helpers.actions.WaitFrames(1);\n}\n`; + } +}; diff --git a/webclient/src/blockly/loops/index.ts b/webclient/src/blockly/loops/index.ts index ddbb7ba..be42453 100644 --- a/webclient/src/blockly/loops/index.ts +++ b/webclient/src/blockly/loops/index.ts @@ -1 +1 @@ -export * from './repeatForever'; +export * from './forever'; diff --git a/webclient/src/blockly/loops/repeatForever.ts b/webclient/src/blockly/loops/repeatForever.ts deleted file mode 100644 index e0f9fb0..0000000 --- a/webclient/src/blockly/loops/repeatForever.ts +++ /dev/null @@ -1,22 +0,0 @@ -import * as Blockly from "blockly"; -import { javascriptGenerator } from "blockly/javascript"; - -Blockly.Blocks['loop_forever'] = { - init(this: Blockly.Block) { - this.appendDummyInput() - .appendField('Repeat forever'); - this.appendStatementInput('BODY') - .setCheck(null); - this.setPreviousStatement(true, null); - this.setNextStatement(true, null); - this.setTooltip('Repeat the enclosed actions forever'); - this.setColour(120); - } -}; - -javascriptGenerator.forBlock['loop_forever'] = function (block, generator) { - const body = generator.statementToCode(block, 'BODY'); - return `while (true) {\n${body}${generator.INDENT}yield helpers.actions.WaitFrames(1);\n}\n`; -}; - -export { }; diff --git a/webclient/src/blockly/recompile.ts b/webclient/src/blockly/recompile.ts new file mode 100644 index 0000000..2055bec --- /dev/null +++ b/webclient/src/blockly/recompile.ts @@ -0,0 +1,21 @@ +import * as Blockly from "blockly"; +import { runInAction } from "mobx"; +import type { ObjectType } from "../types"; +import * as blocklyBarrel from "./index"; + +export function recompileAll(objectTypes: Record): void { + for (const objectType of Object.values(objectTypes)) { + if (!objectType.program) + continue; + const workspace = new Blockly.Workspace(); + try { + Blockly.serialization.workspaces.load(JSON.parse(objectType.program), workspace); + const js = blocklyBarrel.generateCode(workspace); + runInAction(() => { objectType.javascript = js; }); + } catch { + runInAction(() => { objectType.javascript = undefined; }); + } finally { + workspace.dispose(); + } + } +} diff --git a/webclient/src/blockly/registrar.ts b/webclient/src/blockly/registrar.ts new file mode 100644 index 0000000..4ad9375 --- /dev/null +++ b/webclient/src/blockly/registrar.ts @@ -0,0 +1,12 @@ +import * as barrel from './index'; +import { registerBarrel } from './toolbox'; + +registerBarrel(barrel as Record); + +if (import.meta.hot) { + import.meta.hot.accept('./index', (newBarrel) => { + if (newBarrel) + registerBarrel(newBarrel as Record); + window.dispatchEvent(new CustomEvent('blockly-hmr')); + }); +} diff --git a/webclient/src/blockly/toolbox.ts b/webclient/src/blockly/toolbox.ts new file mode 100644 index 0000000..92a8bbf --- /dev/null +++ b/webclient/src/blockly/toolbox.ts @@ -0,0 +1,71 @@ +import * as Blockly from "blockly"; +import { javascriptGenerator, type JavascriptGenerator, type Order } from "blockly/javascript"; + +export const CATEGORY_COLOURS = { + Events: '180', + Values: '260', + Loops: '50', + Actions: '20', +} as const; + +export type CategoryName = keyof typeof CATEGORY_COLOURS; + +export type BlockDef = { + category: CategoryName; + block: { init(this: Blockly.Block): void }; + code: (block: Blockly.Block, generator: JavascriptGenerator) => string | [string, Order]; +}; + +function isCategoryName(v: unknown): v is CategoryName { + return typeof v === 'string' && v in CATEGORY_COLOURS; +} + +function isBlockDef(v: unknown): v is BlockDef { + if (typeof v !== 'object' || v === null) + return false; + const r = v as Record; + return ( + isCategoryName(r.category) && + typeof r.block === 'object' && r.block !== null && + typeof r.code === 'function' + ); +} + +export function registerBarrel(barrel: Record): void { + for (const [name, value] of Object.entries(barrel)) { + if (!isBlockDef(value)) + continue; + const colour = CATEGORY_COLOURS[value.category]; + Blockly.Blocks[name] = { + init(this: Blockly.Block) { + value.block.init.call(this); + this.setColour(colour); + } + }; + javascriptGenerator.forBlock[name] = value.code; + } +} + +export function barrelToToolbox( + barrel: Record +): Blockly.utils.toolbox.ToolboxDefinition { + const categories = new Map(); + + for (const [name, value] of Object.entries(barrel)) { + if (!isBlockDef(value)) + continue; + if (!categories.has(value.category)) + categories.set(value.category, []); + categories.get(value.category)!.push(name); + } + + return { + kind: 'categoryToolbox', + contents: Array.from(categories.entries()).map(([name, blocks]) => ({ + kind: 'category', + name, + colour: CATEGORY_COLOURS[name], + contents: blocks.map(type => ({ kind: 'block', type })), + })), + }; +} diff --git a/webclient/src/blockly/values/anyObjectOfType.ts b/webclient/src/blockly/values/anyObjectOfType.ts index ba361d3..51aaa7f 100644 --- a/webclient/src/blockly/values/anyObjectOfType.ts +++ b/webclient/src/blockly/values/anyObjectOfType.ts @@ -1,21 +1,19 @@ import * as Blockly from "blockly"; -import { javascriptGenerator, Order } from "blockly/javascript"; +import { Order } from "blockly/javascript"; +import type { BlockDef } from "../toolbox"; -Blockly.Blocks['any_object_of_type_value'] = { - init(this: Blockly.Block) { - this.appendDummyInput('NAME') - .appendField('Any object of type'); - this.appendValueInput('NAME') - .setCheck('ObjectType'); - this.setInputsInline(true); - this.setOutput(true, 'Object'); - this.setTooltip('Any object of type'); - this.setColour(315); +export const anyObjectOfTypeValue: BlockDef = { + category: 'Values', + block: { + init(this: Blockly.Block) { + this.appendDummyInput('NAME').appendField('Any object of type'); + this.appendValueInput('NAME').setCheck('ObjectType'); + this.setInputsInline(true); + this.setOutput(true, 'Object'); + this.setTooltip('Any object of type'); + } + }, + code(_block, _generator) { + return ['123', Order.NONE]; } }; - -javascriptGenerator.forBlock['any_object_of_type_value'] = function (_block, _generator) { - return ['123', Order.NONE]; -}; - -export { }; diff --git a/webclient/src/blockly/values/currentObject.ts b/webclient/src/blockly/values/currentObject.ts index a5c3806..af826b2 100644 --- a/webclient/src/blockly/values/currentObject.ts +++ b/webclient/src/blockly/values/currentObject.ts @@ -1,19 +1,18 @@ import * as Blockly from "blockly"; -import { javascriptGenerator, Order } from "blockly/javascript"; +import { Order } from "blockly/javascript"; +import type { BlockDef } from "../toolbox"; -Blockly.Blocks['current_object_value'] = { - init(this: Blockly.Block) { - this.appendEndRowInput('NAME') - .appendField('Me'); - this.setInputsInline(false); - this.setOutput(true, 'Object'); - this.setTooltip('Returns current object instance'); - this.setColour(315); +export const currentObjectValue: BlockDef = { + category: 'Values', + block: { + init(this: Blockly.Block) { + this.appendEndRowInput('NAME').appendField('Me'); + this.setInputsInline(false); + this.setOutput(true, 'Object'); + this.setTooltip('Returns current object instance'); + } + }, + code(_block, _generator) { + return ['thisObject', Order.NONE]; } }; - -javascriptGenerator.forBlock['current_object_value'] = function (_block, _generator) { - return ['thisObject', Order.NONE]; -}; - -export { }; diff --git a/webclient/src/blockly/values/currentObjectType.ts b/webclient/src/blockly/values/currentObjectType.ts index 86b442c..afe881e 100644 --- a/webclient/src/blockly/values/currentObjectType.ts +++ b/webclient/src/blockly/values/currentObjectType.ts @@ -1,19 +1,18 @@ import * as Blockly from "blockly"; -import { javascriptGenerator, Order } from "blockly/javascript"; +import { Order } from "blockly/javascript"; +import type { BlockDef } from "../toolbox"; -Blockly.Blocks['current_object_type_value'] = { - init(this: Blockly.Block) { - this.appendEndRowInput('NAME') - .appendField('My type'); - this.setInputsInline(false); - this.setOutput(true, 'ObjectType'); - this.setTooltip('Returns current object type'); - this.setColour(315); +export const currentObjectTypeValue: BlockDef = { + category: 'Values', + block: { + init(this: Blockly.Block) { + this.appendEndRowInput('NAME').appendField('My type'); + this.setInputsInline(false); + this.setOutput(true, 'ObjectType'); + this.setTooltip('Returns current object type'); + } + }, + code(_block, _generator) { + return ['thisObjectType', Order.NONE]; } }; - -javascriptGenerator.forBlock['current_object_type_value'] = function (_block, _generator) { - return ['thisObjectType', Order.NONE]; -}; - -export { }; diff --git a/webclient/src/blockly/values/direction.ts b/webclient/src/blockly/values/direction.ts index 2892f13..cdbf5a1 100644 --- a/webclient/src/blockly/values/direction.ts +++ b/webclient/src/blockly/values/direction.ts @@ -1,29 +1,28 @@ import * as Blockly from "blockly"; -import { javascriptGenerator, Order } from "blockly/javascript"; +import { Order } from "blockly/javascript"; +import type { BlockDef } from "../toolbox"; -Blockly.Blocks['dpos_value'] = { - init(this: Blockly.Block) { - this.appendDummyInput() - .appendField('Direction [') - .appendField(new Blockly.FieldNumber(0, -Infinity, Infinity, 0.01), 'X') - .appendField(',') - .appendField(new Blockly.FieldNumber(0, -Infinity, Infinity, 0.01), 'Y') - .appendField(',') - .appendField(new Blockly.FieldNumber(0, -Infinity, Infinity, 0.01), 'Z'); - this.appendDummyInput() - .appendField(']'); - this.setInputsInline(true); - this.setOutput(true, 'DPos'); - this.setTooltip('Direction [dx,dy,dz]'); - this.setColour(315); +export const dposValue: BlockDef = { + category: 'Values', + block: { + init(this: Blockly.Block) { + this.appendDummyInput() + .appendField('Direction [') + .appendField(new Blockly.FieldNumber(0, -Infinity, Infinity, 0.01), 'X') + .appendField(',') + .appendField(new Blockly.FieldNumber(0, -Infinity, Infinity, 0.01), 'Y') + .appendField(',') + .appendField(new Blockly.FieldNumber(0, -Infinity, Infinity, 0.01), 'Z'); + this.appendDummyInput().appendField(']'); + this.setInputsInline(true); + this.setOutput(true, 'DPos'); + this.setTooltip('Direction [dx,dy,dz]'); + } + }, + code(block, _generator) { + const x = block.getFieldValue('X'); + const y = block.getFieldValue('Y'); + const z = block.getFieldValue('Z'); + return [`[${x},${y},${z}]`, Order.ATOMIC]; } }; - -javascriptGenerator.forBlock['dpos_value'] = function (block, _generator) { - const xValue = block.getFieldValue('X'); - const yValue = block.getFieldValue('Y'); - const zValue = block.getFieldValue('Z'); - return [`[${xValue},${yValue},${zValue}]`, Order.ATOMIC]; -}; - -export { }; diff --git a/webclient/src/blockly/values/directionToPosition.ts b/webclient/src/blockly/values/directionToPosition.ts index 2064545..7e5bf87 100644 --- a/webclient/src/blockly/values/directionToPosition.ts +++ b/webclient/src/blockly/values/directionToPosition.ts @@ -1,22 +1,20 @@ import * as Blockly from "blockly"; -import { javascriptGenerator, Order } from "blockly/javascript"; +import { Order } from "blockly/javascript"; +import type { BlockDef } from "../toolbox"; -Blockly.Blocks['direction_to_position_value'] = { - init(this: Blockly.Block) { - this.appendDummyInput() - .appendField('Direction to'); - this.appendValueInput('TARGET') - .setCheck('Pos'); - this.setInputsInline(true); - this.setOutput(true, 'DPos'); - this.setTooltip('Direction to a position'); - this.setColour(315); +export const directionToPositionValue: BlockDef = { + category: 'Values', + block: { + init(this: Blockly.Block) { + this.appendDummyInput().appendField('Direction to'); + this.appendValueInput('TARGET').setCheck('Pos'); + this.setInputsInline(true); + this.setOutput(true, 'DPos'); + this.setTooltip('Direction to a position'); + } + }, + code(block, generator) { + const targetValue = generator.valueToCode(block, 'TARGET', Order.ATOMIC); + return [`(t=>{const p=helpers.positionOfObject(thisObject); const dir = [t[0]-p[0],t[1]-p[1],t[2]-p[2]]; /* helpers.debug.show('${block.id}.targetPosition', ${targetValue}); helpers.debug.show('${block.id}.result', dir); helpers.debug.show('${block.id}.thisObject.position', p); */ return dir;})(${targetValue})`, Order.NONE]; } }; - -javascriptGenerator.forBlock['direction_to_position_value'] = function (block, generator) { - const targetValue = generator.valueToCode(block, 'TARGET', Order.ATOMIC); - return [`(t=>{const p=helpers.positionOfObject(thisObject); const dir = [t[0]-p[0],t[1]-p[1],t[2]-p[2]]; /* helpers.debug.show('${block.id}.targetPosition', ${targetValue}); helpers.debug.show('${block.id}.result', dir); helpers.debug.show('${block.id}.thisObject.position', p); */ return dir;})(${targetValue})`, Order.NONE]; -}; - -export { }; diff --git a/webclient/src/blockly/values/objectById.ts b/webclient/src/blockly/values/objectById.ts index ff294cc..7b44ebd 100644 --- a/webclient/src/blockly/values/objectById.ts +++ b/webclient/src/blockly/values/objectById.ts @@ -1,21 +1,21 @@ import * as Blockly from "blockly"; -import { javascriptGenerator, Order } from "blockly/javascript"; +import { Order } from "blockly/javascript"; +import type { BlockDef } from "../toolbox"; -Blockly.Blocks['object_by_id_value'] = { - init(this: Blockly.Block) { - this.appendEndRowInput() - .appendField('Object with id') - .appendField(new Blockly.FieldTextInput(''), 'TARGET_ID'); - this.setInputsInline(false); - this.setOutput(true, 'Object'); - this.setTooltip('Returns object by id, if any'); - this.setColour(315); +export const objectByIdValue: BlockDef = { + category: 'Values', + block: { + init(this: Blockly.Block) { + this.appendEndRowInput() + .appendField('Object with id') + .appendField(new Blockly.FieldTextInput(''), 'TARGET_ID'); + this.setInputsInline(false); + this.setOutput(true, 'Object'); + this.setTooltip('Returns object by id, if any'); + } + }, + code(block, _generator) { + const targetId = block.getFieldValue('TARGET_ID'); + return [`context.scene.objects['${targetId}']`, Order.ATOMIC]; } }; - -javascriptGenerator.forBlock['object_by_id_value'] = function (block, _generator) { - const targetIdValue = block.getFieldValue('TARGET_ID'); - return [`context.scene.objects['${targetIdValue}']`, Order.ATOMIC]; -}; - -export { }; diff --git a/webclient/src/blockly/values/objectType.ts b/webclient/src/blockly/values/objectType.ts index 035c37a..f5e7a5a 100644 --- a/webclient/src/blockly/values/objectType.ts +++ b/webclient/src/blockly/values/objectType.ts @@ -1,7 +1,8 @@ import * as Blockly from "blockly"; -import { javascriptGenerator, Order } from "blockly/javascript"; +import { Order } from "blockly/javascript"; import { toJS } from "mobx"; import { state } from "../../state"; +import type { BlockDef } from "../toolbox"; export function getObjectTypeDropDownOptions(): [string, string][] { const types = Object.values(toJS(state.world.data.objectTypes)); @@ -12,23 +13,22 @@ export function getObjectTypeDropDownOptions(): [string, string][] { return values; } -Blockly.Blocks['object_type_value'] = { - init(this: Blockly.Block) { - this.appendEndRowInput() - .appendField( - new Blockly.FieldDropdown(getObjectTypeDropDownOptions), - 'OBJECT_TYPE', - ); - this.setInputsInline(false); - this.setOutput(true, 'ObjectType'); - this.setTooltip('Object type'); - this.setColour(315); +export const objectTypeValue: BlockDef = { + category: 'Values', + block: { + init(this: Blockly.Block) { + this.appendEndRowInput() + .appendField( + new Blockly.FieldDropdown(getObjectTypeDropDownOptions), + 'OBJECT_TYPE', + ); + this.setInputsInline(false); + this.setOutput(true, 'ObjectType'); + this.setTooltip('Object type'); + } + }, + code(block, _generator) { + const objectTypeValue = block.getFieldValue('OBJECT_TYPE'); + return [`world.objectTypes[${objectTypeValue}]`, Order.NONE]; } }; - -javascriptGenerator.forBlock['object_type_value'] = function (block, _generator) { - const objectTypeValue = block.getFieldValue('OBJECT_TYPE'); - return [`world.objectTypes[${objectTypeValue}]`, Order.NONE]; -}; - -export { }; diff --git a/webclient/src/blockly/values/playerObject.ts b/webclient/src/blockly/values/playerObject.ts index ef37655..fc32751 100644 --- a/webclient/src/blockly/values/playerObject.ts +++ b/webclient/src/blockly/values/playerObject.ts @@ -1,19 +1,18 @@ import * as Blockly from "blockly"; -import { javascriptGenerator, Order } from "blockly/javascript"; +import { Order } from "blockly/javascript"; +import type { BlockDef } from "../toolbox"; -Blockly.Blocks['player_object_value'] = { - init(this: Blockly.Block) { - this.appendEndRowInput() - .appendField('Player'); - this.setInputsInline(false); - this.setOutput(true, 'Object'); - this.setTooltip('Returns object that is controlled by player'); - this.setColour(315); +export const playerObjectValue: BlockDef = { + category: 'Values', + block: { + init(this: Blockly.Block) { + this.appendEndRowInput().appendField('Player'); + this.setInputsInline(false); + this.setOutput(true, 'Object'); + this.setTooltip('Returns object that is controlled by player'); + } + }, + code(_block, _generator) { + return ['(scene.playerObjectId?scene.objects[scene.playerObjectId]:undefined)', Order.ATOMIC]; } }; - -javascriptGenerator.forBlock['player_object_value'] = function (_block, _generator) { - return ['(scene.playerObjectId?scene.objects[scene.playerObjectId]:undefined)', Order.ATOMIC]; -}; - -export { }; diff --git a/webclient/src/blockly/values/position.ts b/webclient/src/blockly/values/position.ts index fd5aced..3d8256c 100644 --- a/webclient/src/blockly/values/position.ts +++ b/webclient/src/blockly/values/position.ts @@ -1,29 +1,28 @@ import * as Blockly from "blockly"; -import { javascriptGenerator, Order } from "blockly/javascript"; +import { Order } from "blockly/javascript"; +import type { BlockDef } from "../toolbox"; -Blockly.Blocks['pos_value'] = { - init(this: Blockly.Block) { - this.appendDummyInput('POSITION') - .appendField('Position [') - .appendField(new Blockly.FieldNumber(0, -Infinity, Infinity, 0.01), 'X') - .appendField(',') - .appendField(new Blockly.FieldNumber(0, -Infinity, Infinity, 0.01), 'Y') - .appendField(',') - .appendField(new Blockly.FieldNumber(0, -Infinity, Infinity, 0.01), 'Z'); - this.appendDummyInput() - .appendField(']'); - this.setInputsInline(true); - this.setOutput(true, 'Pos'); - this.setTooltip('Position [x,y,z]'); - this.setColour(315); +export const posValue: BlockDef = { + category: 'Values', + block: { + init(this: Blockly.Block) { + this.appendDummyInput('POSITION') + .appendField('Position [') + .appendField(new Blockly.FieldNumber(0, -Infinity, Infinity, 0.01), 'X') + .appendField(',') + .appendField(new Blockly.FieldNumber(0, -Infinity, Infinity, 0.01), 'Y') + .appendField(',') + .appendField(new Blockly.FieldNumber(0, -Infinity, Infinity, 0.01), 'Z'); + this.appendDummyInput().appendField(']'); + this.setInputsInline(true); + this.setOutput(true, 'Pos'); + this.setTooltip('Position [x,y,z]'); + } + }, + code(block, _generator) { + const x = block.getFieldValue('X'); + const y = block.getFieldValue('Y'); + const z = block.getFieldValue('Z'); + return [`[${x},${y},${z}]`, Order.ATOMIC]; } }; - -javascriptGenerator.forBlock['pos_value'] = function (block, _generator) { - const xValue = block.getFieldValue('X'); - const yValue = block.getFieldValue('Y'); - const zValue = block.getFieldValue('Z'); - return [`[${xValue},${yValue},${zValue}]`, Order.ATOMIC]; -}; - -export { }; diff --git a/webclient/src/blockly/values/positionOfObject.ts b/webclient/src/blockly/values/positionOfObject.ts index 78bed7f..c40ed58 100644 --- a/webclient/src/blockly/values/positionOfObject.ts +++ b/webclient/src/blockly/values/positionOfObject.ts @@ -1,22 +1,20 @@ import * as Blockly from "blockly"; -import { javascriptGenerator, Order } from "blockly/javascript"; +import { Order } from "blockly/javascript"; +import type { BlockDef } from "../toolbox"; -Blockly.Blocks['position_of_object_value'] = { - init(this: Blockly.Block) { - this.appendDummyInput() - .appendField('Position of'); - this.appendValueInput('TARGET') - .setCheck('Object'); - this.setInputsInline(true); - this.setOutput(true, 'Pos'); - this.setTooltip('Position of an object'); - this.setColour(315); +export const positionOfObjectValue: BlockDef = { + category: 'Values', + block: { + init(this: Blockly.Block) { + this.appendDummyInput().appendField('Position of'); + this.appendValueInput('TARGET').setCheck('Object'); + this.setInputsInline(true); + this.setOutput(true, 'Pos'); + this.setTooltip('Position of an object'); + } + }, + code(block, generator) { + const targetValue = generator.valueToCode(block, 'TARGET', Order.ATOMIC); + return [`helpers.positionOfObject(${targetValue})`, Order.NONE]; } }; - -javascriptGenerator.forBlock['position_of_object_value'] = function (block, generator) { - const targetValue = generator.valueToCode(block, 'TARGET', Order.ATOMIC); - return [`helpers.positionOfObject(${targetValue})`, Order.NONE]; -}; - -export { }; diff --git a/webclient/src/components/GameObjectView.tsx b/webclient/src/components/GameObjectView.tsx index 16b5f16..08fe4fd 100644 --- a/webclient/src/components/GameObjectView.tsx +++ b/webclient/src/components/GameObjectView.tsx @@ -18,7 +18,7 @@ export const GameObjectView = observer(function (props: GameObjectViewProps) { return ; function handleClick(): void { - state.game?.emitEvent('object_clicked', { object: props.object }); + state.game?.emitEvent('objectClicked', { object: props.object }); } return o.rb()?.handle === otherHandle); - game.emitEvent('object_touch_start', { object: gameObj, other: otherObj }); + game.emitEvent('objectTouchStart', { object: gameObj, other: otherObj }); if (otherObj?.id === game.scene.playerObjectId) - game.emitEvent('player_touch_start', { object: gameObj, other: otherObj }); + game.emitEvent('playerTouchStart', { object: gameObj, other: otherObj }); }} onSync={(data) => { runInAction(() => { diff --git a/webclient/src/components/scriptEditor/ScriptEditorView.tsx b/webclient/src/components/scriptEditor/ScriptEditorView.tsx index 1280672..2144c76 100644 --- a/webclient/src/components/scriptEditor/ScriptEditorView.tsx +++ b/webclient/src/components/scriptEditor/ScriptEditorView.tsx @@ -5,70 +5,11 @@ import * as Blockly from "blockly"; import type { ObjectType } from "../../types"; import './ScriptEditorView.scss'; -import '../../blockly'; -import { gameTheme, generateCode } from '../../blockly'; - -const TOOLBOX: Blockly.utils.toolbox.ToolboxDefinition = { - kind: 'categoryToolbox', - contents: [ - { - kind: 'category', - name: 'Events', - colour: '180', - contents: [ - { kind: 'block', type: 'game_start_event' }, - { kind: 'block', type: 'object_touch_start_event' }, - { kind: 'block', type: 'player_touch_start_event' }, - { kind: 'block', type: 'object_clicked_event' }, - ], - }, - { - kind: 'category', - name: 'Values', - colour: '260', - contents: [ - { kind: 'block', type: 'current_object_value' }, - { kind: 'block', type: 'current_object_type_value' }, - { kind: 'block', type: 'object_type_value' }, - { kind: 'block', type: 'any_object_of_type_value' }, - { kind: 'block', type: 'position_of_object_value' }, - { kind: 'block', type: 'pos_value' }, - { kind: 'block', type: 'dpos_value' }, - { kind: 'block', type: 'direction_to_position_value' }, - { kind: 'block', type: 'object_by_id_value' }, - { kind: 'block', type: 'player_object_value' }, - ], - }, - { - kind: 'category', - name: 'Loops', - colour: '50', - contents: [ - { kind: 'block', type: 'loop_forever' }, - ], - }, - { - kind: 'category', - name: 'Actions', - colour: '20', - contents: [ - { kind: 'block', type: 'console_log_action' }, - { kind: 'block', type: 'move_to_position_action' }, - { kind: 'block', type: 'move_in_direction_action' }, - { kind: 'block', type: 'physics_apply_impulse_action' }, - { kind: 'block', type: 'wait_seconds_action' }, - ], - }, - { - kind: 'category', - name: 'Text', - colour: '160', - contents: [ - { kind: 'block', type: 'text' }, - ], - }, - ], -}; +import '../../blockly/registrar'; +import * as blocklyBarrel from '../../blockly'; +import { gameTheme } from '../../blockly'; +import { recompileAll } from '../../blockly/recompile'; +import { state } from '../../state'; type ScriptEditorViewProps = { objectType: ObjectType; @@ -76,29 +17,44 @@ type ScriptEditorViewProps = { export const ScriptEditorView = observer(function ({ objectType }: ScriptEditorViewProps) { const [popup, setPopup] = useState<{ title: string; content: string } | null>(null); + const [blocklyKey, setBlocklyKey] = useState(0); const containerRef = useRef(null); const workspaceRef = useRef(null); const objectTypeRef = useRef(objectType); useLayoutEffect(() => { objectTypeRef.current = objectType; }); + useEffect(() => { + if (!import.meta.hot) + return; + const handler = () => { + recompileAll(state.world.data.objectTypes); + setBlocklyKey(k => k + 1); + }; + window.addEventListener('blockly-hmr', handler); + return () => window.removeEventListener('blockly-hmr', handler); + }, []); + useEffect(() => { if (!containerRef.current) return; - const workspace = Blockly.inject(containerRef.current, { toolbox: TOOLBOX, theme: gameTheme }); + const toolbox = blocklyBarrel.barrelToToolbox(blocklyBarrel as Record); + const workspace = Blockly.inject(containerRef.current, { toolbox, theme: gameTheme }); workspaceRef.current = workspace; const listener = (e: Blockly.Events.Abstract) => { if (e.isUiEvent) return; + console.log('change event'); + runInAction(() => { const current = objectTypeRef.current; const serialized = JSON.stringify(Blockly.serialization.workspaces.save(workspace)); current.program = serialized; try { - current.javascript = generateCode(workspace); + current.javascript = blocklyBarrel.generateCode(workspace); } catch (err) { console.log(err); @@ -128,7 +84,9 @@ export const ScriptEditorView = observer(function ({ objectType }: ScriptEditorV workspace.dispose(); workspaceRef.current = null; }; - }, [objectType.program]); + // workspace should not reload when objectType.program changes + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [blocklyKey]); useEffect(() => { const workspace = workspaceRef.current; @@ -145,7 +103,7 @@ export const ScriptEditorView = observer(function ({ objectType }: ScriptEditorV } finally { Blockly.Events.enable(); } - }, [objectType.id, objectType.program]); + }, [objectType.id]); // eslint-disable-line react-hooks/exhaustive-deps const showProgram = () => { const raw = objectType.program; diff --git a/webclient/src/scripts/actions/index.ts b/webclient/src/scripts/actions/index.ts index 8d48896..8b18de6 100644 --- a/webclient/src/scripts/actions/index.ts +++ b/webclient/src/scripts/actions/index.ts @@ -1,6 +1,7 @@ +export * from './applyImpulseAction'; +export * from './moveInDirectionAction'; +export * from './moveToPositionAction'; +export * from './stopGameAction'; export * from './waitConditionAction'; export * from './waitFramesAction'; export * from './waitSecondsAction'; -export * from './applyImpulseAction'; -export * from './moveToPositionAction'; -export * from './moveInDirectionAction'; diff --git a/webclient/src/scripts/actions/stopGameAction.ts b/webclient/src/scripts/actions/stopGameAction.ts new file mode 100644 index 0000000..40209a5 --- /dev/null +++ b/webclient/src/scripts/actions/stopGameAction.ts @@ -0,0 +1,15 @@ +import { state } from "../../state"; +import { ScriptAction, type ScriptCoroutineContext } from "../../types"; + +export class StopGameScriptAction extends ScriptAction { + + constructor(coroutine: ScriptCoroutineContext) { + super(coroutine); + } + + public tick(_deltaTime: number): boolean { + state.stopGame(); + + return true; // one shot + } +} diff --git a/webclient/src/state/gameState.ts b/webclient/src/state/gameState.ts index 70a3a02..62040aa 100644 --- a/webclient/src/state/gameState.ts +++ b/webclient/src/state/gameState.ts @@ -134,7 +134,7 @@ export class GameState { if (this.isFirstTick) { this.isFirstTick = false; - this.emitEvent('game_start'); + this.emitEvent('gameStart'); } } } \ No newline at end of file