├── src ├── helpers │ ├── __mocks__ │ │ └── bitsy.js │ ├── addParagraphBreak.js │ ├── __snapshots__ │ │ ├── utils.test.js.snap │ │ └── kitsy-script-toolkit.test.js.snap │ ├── transform-sprite-data.js │ ├── edit image at runtime.js │ ├── utils.test.js │ ├── kitsy-script-toolkit.test.js │ └── utils.js ├── __image_snapshots__ │ ├── follower-test-js-follower-1-snap.png │ ├── paragraph-break-test-js-p-1-snap.png │ ├── paragraph-break-test-js-p-2-snap.png │ ├── paragraph-break-test-js-p-3-snap.png │ ├── end-from-dialog-test-js-end-1-snap.png │ ├── end-from-dialog-test-js-end-2-snap.png │ ├── gravity-test-js-gravity-jump-1-snap.png │ ├── avatar-by-room-test-js-strike-1-snap.png │ ├── avatar-by-room-test-js-strike-2-snap.png │ ├── avatar-by-room-test-js-strike-3-snap.png │ ├── end-from-dialog-test-js-end-now-1-snap.png │ ├── end-from-dialog-test-js-end-now-2-snap.png │ ├── exit-from-dialog-test-js-exit-1-snap.png │ ├── exit-from-dialog-test-js-exit-2-snap.png │ ├── gravity-test-js-gravity-force-1-snap.png │ ├── gravity-test-js-gravity-invert-1-snap.png │ ├── gravity-test-js-gravity-jetpack-1-snap.png │ ├── itsy-bitsy-test-js-itsy-bitsy-1-snap.png │ ├── itsy-bitsy-test-js-itsy-bitsy-2-snap.png │ ├── itsy-bitsy-test-js-itsy-bitsy-3-snap.png │ ├── itsy-bitsy-test-js-itsy-bitsy-4-snap.png │ ├── itsy-bitsy-test-js-itsy-bitsy-5-snap.png │ ├── long-dialog-test-js-long-dialog-1-snap.png │ ├── long-dialog-test-js-long-dialog-2-snap.png │ ├── solid-items-test-js-solid-items-1-snap.png │ ├── solid-items-test-js-solid-items-2-snap.png │ ├── custom-text-effect-test-js-strike-1-snap.png │ ├── exit-from-dialog-test-js-exit-now-1-snap.png │ ├── exit-from-dialog-test-js-exit-now-2-snap.png │ ├── opaque-tiles-test-js-opaque-tiles-1-snap.png │ ├── palette-maps-test-js-palette-maps-1-snap.png │ ├── edit-image-from-dialog-test-js-image-1-snap.png │ ├── edit-image-from-dialog-test-js-image-2-snap.png │ ├── edit-room-from-dialog-test-js-copy-1-snap.png │ ├── edit-room-from-dialog-test-js-draw-1-snap.png │ ├── edit-room-from-dialog-test-js-erase-1-snap.png │ ├── dialog-choices-test-js-dialog-choices-1-snap.png │ ├── dialog-choices-test-js-dialog-choices-2-snap.png │ ├── dialog-choices-test-js-dialog-choices-3-snap.png │ ├── dialog-choices-test-js-dialog-choices-4-snap.png │ ├── dialog-choices-test-js-dialog-in-front-1-snap.png │ ├── dialog-choices-test-js-dialog-in-front-2-snap.png │ ├── edit-dialog-from-dialog-test-js-dialog-1-snap.png │ ├── edit-dialog-from-dialog-test-js-dialog-2-snap.png │ ├── edit-dialog-from-dialog-test-js-dialog-3-snap.png │ ├── edit-dialog-from-dialog-test-js-dialog-4-snap.png │ ├── edit-room-from-dialog-test-js-copy-box-1-snap.png │ ├── edit-room-from-dialog-test-js-draw-all-1-snap.png │ ├── edit-room-from-dialog-test-js-draw-box-1-snap.png │ ├── edit-room-from-dialog-test-js-replace-1-snap.png │ ├── sprite-effects-test-js-sprite-effects-1-snap.png │ ├── sprite-effects-test-js-sprite-effects-2-snap.png │ ├── textbox-styler-test-js-textbox-styler-1-snap.png │ ├── close-on-ending-test-js-with-ending-text-1-snap.png │ ├── close-on-ending-test-js-with-ending-text-2-snap.png │ ├── dialog-choices-test-js-with-long-dialog-1-snap.png │ ├── dialog-choices-test-js-with-long-dialog-2-snap.png │ ├── edit-image-from-dialog-test-js-image-now-1-snap.png │ ├── edit-image-from-dialog-test-js-image-now-2-snap.png │ ├── edit-room-from-dialog-test-js-erase-all-1-snap.png │ ├── edit-room-from-dialog-test-js-erase-box-1-snap.png │ ├── edit-room-from-dialog-test-js-replace-all-1-snap.png │ ├── edit-room-from-dialog-test-js-replace-box-1-snap.png │ ├── permanent-items-test-js-permanent-items-1-snap.png │ ├── close-on-ending-test-js-without-ending-text-1-snap.png │ ├── character-portraits-test-js-no-reset-persist-1-snap.png │ ├── character-portraits-test-js-no-reset-persist-2-snap.png │ ├── character-portraits-test-js-no-reset-persist-3-snap.png │ ├── character-portraits-test-js-no-reset-persist-4-snap.png │ ├── directional-avatar-test-js-directional-avatar-1-snap.png │ ├── directional-avatar-test-js-directional-avatar-2-snap.png │ ├── directional-avatar-test-js-directional-avatar-3-snap.png │ ├── dynamic-background-test-js-dynamic-background-1-snap.png │ ├── dynamic-background-test-js-dynamic-background-2-snap.png │ ├── edit-player-from-dialog-test-js-edits-player-1-snap.png │ ├── transparent-dialog-test-js-transparent-dialog-1-snap.png │ ├── transparent-dialog-test-js-transparent-dialog-2-snap.png │ ├── direction-in-dialog-test-js-direction-in-dialog-1-snap.png │ ├── direction-in-dialog-test-js-direction-in-dialog-2-snap.png │ ├── direction-in-dialog-test-js-direction-in-dialog-3-snap.png │ ├── direction-in-dialog-test-js-direction-in-dialog-4-snap.png │ ├── dynamic-background-test-js-hack-options-by-room-1-snap.png │ ├── dynamic-background-test-js-hack-options-by-room-2-snap.png │ ├── dynamic-background-test-js-hack-options-default-1-snap.png │ ├── dynamic-background-test-js-hack-options-default-2-snap.png │ ├── multi-sprite-avatar-test-js-multi-sprite-avatar-1-snap.png │ ├── multi-sprite-avatar-test-js-multi-sprite-avatar-2-snap.png │ ├── transparent-sprites-test-js-transparent-sprites-1-snap.png │ ├── character-portraits-test-js-autoreset-dialog-only-1-snap.png │ ├── character-portraits-test-js-autoreset-dialog-only-2-snap.png │ ├── opaque-tiles-test-js-compatible-with-transparency-1-snap.png │ ├── sprite-effects-test-js-compatible-with-transparency-1-snap.png │ ├── sprite-effects-test-js-compatible-with-transparency-2-snap.png │ ├── directional-avatar-test-js-compatible-with-transparency-1-snap.png │ ├── directional-avatar-test-js-compatible-with-transparency-2-snap.png │ ├── edit-image-from-dialog-test-js-compatible-with-transparency-1-snap.png │ └── edit-player-from-dialog-test-js-edits-current-room-if-new-player-is-in-another-room-1-snap.png ├── test │ ├── setupTests.js │ ├── __image_snapshots__ │ │ ├── bitsy-test-js-stop-recording-stops-capturing-a-snapshot-after-every-key-press-1-snap.png │ │ ├── bitsy-test-js-start-recording-starts-capturing-a-snapshot-after-every-key-press-1-snap.png │ │ ├── bitsy-test-js-start-recording-starts-capturing-a-snapshot-after-every-key-press-10-snap.png │ │ ├── bitsy-test-js-start-recording-starts-capturing-a-snapshot-after-every-key-press-11-snap.png │ │ ├── bitsy-test-js-start-recording-starts-capturing-a-snapshot-after-every-key-press-12-snap.png │ │ ├── bitsy-test-js-start-recording-starts-capturing-a-snapshot-after-every-key-press-13-snap.png │ │ ├── bitsy-test-js-start-recording-starts-capturing-a-snapshot-after-every-key-press-2-snap.png │ │ ├── bitsy-test-js-start-recording-starts-capturing-a-snapshot-after-every-key-press-3-snap.png │ │ ├── bitsy-test-js-start-recording-starts-capturing-a-snapshot-after-every-key-press-4-snap.png │ │ ├── bitsy-test-js-start-recording-starts-capturing-a-snapshot-after-every-key-press-5-snap.png │ │ ├── bitsy-test-js-start-recording-starts-capturing-a-snapshot-after-every-key-press-6-snap.png │ │ ├── bitsy-test-js-start-recording-starts-capturing-a-snapshot-after-every-key-press-7-snap.png │ │ ├── bitsy-test-js-start-recording-starts-capturing-a-snapshot-after-every-key-press-8-snap.png │ │ ├── bitsy-test-js-start-recording-starts-capturing-a-snapshot-after-every-key-press-9-snap.png │ │ └── bitsy-test-js-walk-to-cat-moves-the-player-to-the-left-of-the-cat-in-the-default-game-1-snap.png │ └── bitsy.test.js ├── twine-bitsy-comms │ ├── .eslintrc │ ├── SugarCube-v1.js │ ├── Snowman.js │ ├── Sugarcane-Responsive.js │ ├── SugarCube-v2.js │ ├── Jonah.js │ └── Harlowe.js ├── textbox styler.test.js ├── transparent dialog.test.js ├── custom text effect.test.js ├── transparent sprites.test.js ├── follower.test.js ├── paragraph-break.test.js ├── dialog pause.js ├── long dialog.test.js ├── edit dialog from dialog.test.js ├── custom-keyhandlers.test.js ├── exit-from-dialog.test.js ├── opaque tiles.test.js ├── itsy-bitsy.test.js ├── end-from-dialog.test.js ├── unique items.js ├── edit room from dialog.test.js ├── sprite effects.test.js ├── direction in dialog.js ├── edit image from dialog.test.js ├── close on ending.js ├── edit player from dialog.js ├── direction in dialog.test.js ├── palette maps.test.js ├── dialog jump.js ├── transparent dialog.js ├── solid items.test.js ├── permanent items.test.js ├── character portraits.test.js ├── opaque tiles.js ├── long dialog.js ├── dynamic background.js ├── paragraph-break.js ├── javascript dialog.js ├── avatar by room.test.js ├── gamepad input.js ├── dialog choices.test.js ├── edit dialog from dialog.js ├── canvas replacement.js ├── permanent items.js ├── dialog box transition.js ├── transparent sprites.js ├── basic sfx.js ├── dialog audio.js ├── solid items.js ├── custom-keyhandlers.js ├── directional avatar.test.js ├── multi-sprite avatar.test.js ├── directional avatar.js ├── tracery processing.js ├── itsy-bitsy.js ├── noclip.js ├── logic-operators-extended.js ├── end-from-dialog.js ├── smooth moves.js ├── avatar by room.js ├── replace drawing.js ├── edit player from dialog.test.js ├── exit-from-dialog.js ├── custom-exit-effects.js ├── close on ending.test.js ├── character portraits.js ├── external-game-data.js ├── stopwatch.js └── dynamic background.test.js ├── index.mjs ├── .npmignore ├── .babelrc ├── rollup.config.base.json ├── getHacks.js ├── .github └── workflows │ ├── branch.yml │ ├── release.yml │ └── master.yml ├── .gitattributes ├── HeaderCommentPlugin.mjs ├── rollup.config.js ├── TopLevelOptionsPlugin.mjs ├── LICENSE.md ├── .gitignore ├── .eslintrc ├── package.json └── ReadmePlugin.mjs /src/helpers/__mocks__/bitsy.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /index.mjs: -------------------------------------------------------------------------------- 1 | import { build } from './build'; 2 | export default build; 3 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | *.test.js 2 | **/test 3 | **/__snapshots__ 4 | **/__image_snapshots__ 5 | **/__mocks__ 6 | coverage/ 7 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/env" 4 | ], 5 | "env": { 6 | "test": { 7 | "plugins": ["@babel/plugin-transform-runtime"] 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/__image_snapshots__/follower-test-js-follower-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/follower-test-js-follower-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/paragraph-break-test-js-p-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/paragraph-break-test-js-p-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/paragraph-break-test-js-p-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/paragraph-break-test-js-p-2-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/paragraph-break-test-js-p-3-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/paragraph-break-test-js-p-3-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/end-from-dialog-test-js-end-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/end-from-dialog-test-js-end-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/end-from-dialog-test-js-end-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/end-from-dialog-test-js-end-2-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/gravity-test-js-gravity-jump-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/gravity-test-js-gravity-jump-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/avatar-by-room-test-js-strike-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/avatar-by-room-test-js-strike-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/avatar-by-room-test-js-strike-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/avatar-by-room-test-js-strike-2-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/avatar-by-room-test-js-strike-3-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/avatar-by-room-test-js-strike-3-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/end-from-dialog-test-js-end-now-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/end-from-dialog-test-js-end-now-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/end-from-dialog-test-js-end-now-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/end-from-dialog-test-js-end-now-2-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/exit-from-dialog-test-js-exit-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/exit-from-dialog-test-js-exit-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/exit-from-dialog-test-js-exit-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/exit-from-dialog-test-js-exit-2-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/gravity-test-js-gravity-force-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/gravity-test-js-gravity-force-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/gravity-test-js-gravity-invert-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/gravity-test-js-gravity-invert-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/gravity-test-js-gravity-jetpack-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/gravity-test-js-gravity-jetpack-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/itsy-bitsy-test-js-itsy-bitsy-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/itsy-bitsy-test-js-itsy-bitsy-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/itsy-bitsy-test-js-itsy-bitsy-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/itsy-bitsy-test-js-itsy-bitsy-2-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/itsy-bitsy-test-js-itsy-bitsy-3-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/itsy-bitsy-test-js-itsy-bitsy-3-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/itsy-bitsy-test-js-itsy-bitsy-4-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/itsy-bitsy-test-js-itsy-bitsy-4-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/itsy-bitsy-test-js-itsy-bitsy-5-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/itsy-bitsy-test-js-itsy-bitsy-5-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/long-dialog-test-js-long-dialog-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/long-dialog-test-js-long-dialog-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/long-dialog-test-js-long-dialog-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/long-dialog-test-js-long-dialog-2-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/solid-items-test-js-solid-items-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/solid-items-test-js-solid-items-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/solid-items-test-js-solid-items-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/solid-items-test-js-solid-items-2-snap.png -------------------------------------------------------------------------------- /src/test/setupTests.js: -------------------------------------------------------------------------------- 1 | import { 2 | toMatchImageSnapshot, 3 | } from 'jest-image-snapshot'; 4 | 5 | expect.extend({ 6 | toMatchImageSnapshot, 7 | }); 8 | jest.setTimeout(20000); 9 | -------------------------------------------------------------------------------- /src/__image_snapshots__/custom-text-effect-test-js-strike-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/custom-text-effect-test-js-strike-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/exit-from-dialog-test-js-exit-now-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/exit-from-dialog-test-js-exit-now-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/exit-from-dialog-test-js-exit-now-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/exit-from-dialog-test-js-exit-now-2-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/opaque-tiles-test-js-opaque-tiles-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/opaque-tiles-test-js-opaque-tiles-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/palette-maps-test-js-palette-maps-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/palette-maps-test-js-palette-maps-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/edit-image-from-dialog-test-js-image-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/edit-image-from-dialog-test-js-image-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/edit-image-from-dialog-test-js-image-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/edit-image-from-dialog-test-js-image-2-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/edit-room-from-dialog-test-js-copy-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/edit-room-from-dialog-test-js-copy-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/edit-room-from-dialog-test-js-draw-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/edit-room-from-dialog-test-js-draw-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/edit-room-from-dialog-test-js-erase-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/edit-room-from-dialog-test-js-erase-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/dialog-choices-test-js-dialog-choices-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/dialog-choices-test-js-dialog-choices-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/dialog-choices-test-js-dialog-choices-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/dialog-choices-test-js-dialog-choices-2-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/dialog-choices-test-js-dialog-choices-3-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/dialog-choices-test-js-dialog-choices-3-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/dialog-choices-test-js-dialog-choices-4-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/dialog-choices-test-js-dialog-choices-4-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/dialog-choices-test-js-dialog-in-front-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/dialog-choices-test-js-dialog-in-front-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/dialog-choices-test-js-dialog-in-front-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/dialog-choices-test-js-dialog-in-front-2-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/edit-dialog-from-dialog-test-js-dialog-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/edit-dialog-from-dialog-test-js-dialog-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/edit-dialog-from-dialog-test-js-dialog-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/edit-dialog-from-dialog-test-js-dialog-2-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/edit-dialog-from-dialog-test-js-dialog-3-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/edit-dialog-from-dialog-test-js-dialog-3-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/edit-dialog-from-dialog-test-js-dialog-4-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/edit-dialog-from-dialog-test-js-dialog-4-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/edit-room-from-dialog-test-js-copy-box-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/edit-room-from-dialog-test-js-copy-box-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/edit-room-from-dialog-test-js-draw-all-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/edit-room-from-dialog-test-js-draw-all-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/edit-room-from-dialog-test-js-draw-box-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/edit-room-from-dialog-test-js-draw-box-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/edit-room-from-dialog-test-js-replace-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/edit-room-from-dialog-test-js-replace-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/sprite-effects-test-js-sprite-effects-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/sprite-effects-test-js-sprite-effects-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/sprite-effects-test-js-sprite-effects-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/sprite-effects-test-js-sprite-effects-2-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/textbox-styler-test-js-textbox-styler-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/textbox-styler-test-js-textbox-styler-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/close-on-ending-test-js-with-ending-text-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/close-on-ending-test-js-with-ending-text-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/close-on-ending-test-js-with-ending-text-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/close-on-ending-test-js-with-ending-text-2-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/dialog-choices-test-js-with-long-dialog-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/dialog-choices-test-js-with-long-dialog-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/dialog-choices-test-js-with-long-dialog-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/dialog-choices-test-js-with-long-dialog-2-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/edit-image-from-dialog-test-js-image-now-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/edit-image-from-dialog-test-js-image-now-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/edit-image-from-dialog-test-js-image-now-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/edit-image-from-dialog-test-js-image-now-2-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/edit-room-from-dialog-test-js-erase-all-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/edit-room-from-dialog-test-js-erase-all-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/edit-room-from-dialog-test-js-erase-box-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/edit-room-from-dialog-test-js-erase-box-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/edit-room-from-dialog-test-js-replace-all-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/edit-room-from-dialog-test-js-replace-all-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/edit-room-from-dialog-test-js-replace-box-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/edit-room-from-dialog-test-js-replace-box-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/permanent-items-test-js-permanent-items-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/permanent-items-test-js-permanent-items-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/close-on-ending-test-js-without-ending-text-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/close-on-ending-test-js-without-ending-text-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/character-portraits-test-js-no-reset-persist-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/character-portraits-test-js-no-reset-persist-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/character-portraits-test-js-no-reset-persist-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/character-portraits-test-js-no-reset-persist-2-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/character-portraits-test-js-no-reset-persist-3-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/character-portraits-test-js-no-reset-persist-3-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/character-portraits-test-js-no-reset-persist-4-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/character-portraits-test-js-no-reset-persist-4-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/directional-avatar-test-js-directional-avatar-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/directional-avatar-test-js-directional-avatar-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/directional-avatar-test-js-directional-avatar-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/directional-avatar-test-js-directional-avatar-2-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/directional-avatar-test-js-directional-avatar-3-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/directional-avatar-test-js-directional-avatar-3-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/dynamic-background-test-js-dynamic-background-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/dynamic-background-test-js-dynamic-background-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/dynamic-background-test-js-dynamic-background-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/dynamic-background-test-js-dynamic-background-2-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/edit-player-from-dialog-test-js-edits-player-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/edit-player-from-dialog-test-js-edits-player-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/transparent-dialog-test-js-transparent-dialog-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/transparent-dialog-test-js-transparent-dialog-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/transparent-dialog-test-js-transparent-dialog-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/transparent-dialog-test-js-transparent-dialog-2-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/direction-in-dialog-test-js-direction-in-dialog-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/direction-in-dialog-test-js-direction-in-dialog-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/direction-in-dialog-test-js-direction-in-dialog-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/direction-in-dialog-test-js-direction-in-dialog-2-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/direction-in-dialog-test-js-direction-in-dialog-3-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/direction-in-dialog-test-js-direction-in-dialog-3-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/direction-in-dialog-test-js-direction-in-dialog-4-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/direction-in-dialog-test-js-direction-in-dialog-4-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/dynamic-background-test-js-hack-options-by-room-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/dynamic-background-test-js-hack-options-by-room-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/dynamic-background-test-js-hack-options-by-room-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/dynamic-background-test-js-hack-options-by-room-2-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/dynamic-background-test-js-hack-options-default-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/dynamic-background-test-js-hack-options-default-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/dynamic-background-test-js-hack-options-default-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/dynamic-background-test-js-hack-options-default-2-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/multi-sprite-avatar-test-js-multi-sprite-avatar-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/multi-sprite-avatar-test-js-multi-sprite-avatar-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/multi-sprite-avatar-test-js-multi-sprite-avatar-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/multi-sprite-avatar-test-js-multi-sprite-avatar-2-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/transparent-sprites-test-js-transparent-sprites-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/transparent-sprites-test-js-transparent-sprites-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/character-portraits-test-js-autoreset-dialog-only-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/character-portraits-test-js-autoreset-dialog-only-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/character-portraits-test-js-autoreset-dialog-only-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/character-portraits-test-js-autoreset-dialog-only-2-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/opaque-tiles-test-js-compatible-with-transparency-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/opaque-tiles-test-js-compatible-with-transparency-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/sprite-effects-test-js-compatible-with-transparency-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/sprite-effects-test-js-compatible-with-transparency-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/sprite-effects-test-js-compatible-with-transparency-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/sprite-effects-test-js-compatible-with-transparency-2-snap.png -------------------------------------------------------------------------------- /rollup.config.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "external": [ 3 | "bitsy" 4 | ], 5 | "output": { 6 | "format": "iife", 7 | "globals": { 8 | "bitsy": "window" 9 | }, 10 | "indent": false, 11 | "extend": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/__image_snapshots__/directional-avatar-test-js-compatible-with-transparency-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/directional-avatar-test-js-compatible-with-transparency-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/directional-avatar-test-js-compatible-with-transparency-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/directional-avatar-test-js-compatible-with-transparency-2-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/edit-image-from-dialog-test-js-compatible-with-transparency-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/edit-image-from-dialog-test-js-compatible-with-transparency-1-snap.png -------------------------------------------------------------------------------- /src/__image_snapshots__/edit-player-from-dialog-test-js-edits-current-room-if-new-player-is-in-another-room-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/__image_snapshots__/edit-player-from-dialog-test-js-edits-current-room-if-new-player-is-in-another-room-1-snap.png -------------------------------------------------------------------------------- /src/test/__image_snapshots__/bitsy-test-js-stop-recording-stops-capturing-a-snapshot-after-every-key-press-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/test/__image_snapshots__/bitsy-test-js-stop-recording-stops-capturing-a-snapshot-after-every-key-press-1-snap.png -------------------------------------------------------------------------------- /src/test/__image_snapshots__/bitsy-test-js-start-recording-starts-capturing-a-snapshot-after-every-key-press-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/test/__image_snapshots__/bitsy-test-js-start-recording-starts-capturing-a-snapshot-after-every-key-press-1-snap.png -------------------------------------------------------------------------------- /src/test/__image_snapshots__/bitsy-test-js-start-recording-starts-capturing-a-snapshot-after-every-key-press-10-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/test/__image_snapshots__/bitsy-test-js-start-recording-starts-capturing-a-snapshot-after-every-key-press-10-snap.png -------------------------------------------------------------------------------- /src/test/__image_snapshots__/bitsy-test-js-start-recording-starts-capturing-a-snapshot-after-every-key-press-11-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/test/__image_snapshots__/bitsy-test-js-start-recording-starts-capturing-a-snapshot-after-every-key-press-11-snap.png -------------------------------------------------------------------------------- /src/test/__image_snapshots__/bitsy-test-js-start-recording-starts-capturing-a-snapshot-after-every-key-press-12-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/test/__image_snapshots__/bitsy-test-js-start-recording-starts-capturing-a-snapshot-after-every-key-press-12-snap.png -------------------------------------------------------------------------------- /src/test/__image_snapshots__/bitsy-test-js-start-recording-starts-capturing-a-snapshot-after-every-key-press-13-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/test/__image_snapshots__/bitsy-test-js-start-recording-starts-capturing-a-snapshot-after-every-key-press-13-snap.png -------------------------------------------------------------------------------- /src/test/__image_snapshots__/bitsy-test-js-start-recording-starts-capturing-a-snapshot-after-every-key-press-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/test/__image_snapshots__/bitsy-test-js-start-recording-starts-capturing-a-snapshot-after-every-key-press-2-snap.png -------------------------------------------------------------------------------- /src/test/__image_snapshots__/bitsy-test-js-start-recording-starts-capturing-a-snapshot-after-every-key-press-3-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/test/__image_snapshots__/bitsy-test-js-start-recording-starts-capturing-a-snapshot-after-every-key-press-3-snap.png -------------------------------------------------------------------------------- /src/test/__image_snapshots__/bitsy-test-js-start-recording-starts-capturing-a-snapshot-after-every-key-press-4-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/test/__image_snapshots__/bitsy-test-js-start-recording-starts-capturing-a-snapshot-after-every-key-press-4-snap.png -------------------------------------------------------------------------------- /src/test/__image_snapshots__/bitsy-test-js-start-recording-starts-capturing-a-snapshot-after-every-key-press-5-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/test/__image_snapshots__/bitsy-test-js-start-recording-starts-capturing-a-snapshot-after-every-key-press-5-snap.png -------------------------------------------------------------------------------- /src/test/__image_snapshots__/bitsy-test-js-start-recording-starts-capturing-a-snapshot-after-every-key-press-6-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/test/__image_snapshots__/bitsy-test-js-start-recording-starts-capturing-a-snapshot-after-every-key-press-6-snap.png -------------------------------------------------------------------------------- /src/test/__image_snapshots__/bitsy-test-js-start-recording-starts-capturing-a-snapshot-after-every-key-press-7-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/test/__image_snapshots__/bitsy-test-js-start-recording-starts-capturing-a-snapshot-after-every-key-press-7-snap.png -------------------------------------------------------------------------------- /src/test/__image_snapshots__/bitsy-test-js-start-recording-starts-capturing-a-snapshot-after-every-key-press-8-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/test/__image_snapshots__/bitsy-test-js-start-recording-starts-capturing-a-snapshot-after-every-key-press-8-snap.png -------------------------------------------------------------------------------- /src/test/__image_snapshots__/bitsy-test-js-start-recording-starts-capturing-a-snapshot-after-every-key-press-9-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/test/__image_snapshots__/bitsy-test-js-start-recording-starts-capturing-a-snapshot-after-every-key-press-9-snap.png -------------------------------------------------------------------------------- /src/test/__image_snapshots__/bitsy-test-js-walk-to-cat-moves-the-player-to-the-left-of-the-cat-in-the-default-game-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zverik/bitsy-hacks/master/src/test/__image_snapshots__/bitsy-test-js-walk-to-cat-moves-the-player-to-the-left-of-the-cat-in-the-default-game-1-snap.png -------------------------------------------------------------------------------- /src/twine-bitsy-comms/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "globals": { 3 | "State": "readonly", 4 | "Engine": "readonly", 5 | "Selectors": "readonly", 6 | "Macro": "readonly", 7 | "macros": "readonly", 8 | "state": "readonly", 9 | "prerender": "readonly", 10 | "story": "readonly", 11 | "$": "readonly" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /getHacks.js: -------------------------------------------------------------------------------- 1 | import path from "path"; 2 | import fs from 'fs'; 3 | 4 | export default function getHacks() { 5 | const inputDir = "./src/"; 6 | const srcFiles = fs.readdirSync(inputDir); 7 | const hacks = srcFiles 8 | .filter(file => file.match(/^.*?(? path.join(inputDir, file)); 10 | return hacks; 11 | } 12 | -------------------------------------------------------------------------------- /src/helpers/addParagraphBreak.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Helper for printing a paragraph break inside of a dialog function. 3 | * Adds the function `AddParagraphBreak` to `DialogBuffer` 4 | */ 5 | import { 6 | inject, 7 | } from './kitsy-script-toolkit'; 8 | 9 | inject(/(this\.AddLinebreak = )/, 'this.AddParagraphBreak = function() { buffer.push( [[]] ); isActive = true; };\n$1'); 10 | -------------------------------------------------------------------------------- /.github/workflows/branch.yml: -------------------------------------------------------------------------------- 1 | name: branch 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | test: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - uses: actions/setup-node@v1 14 | with: 15 | node-version: 12 16 | - run: npm ci 17 | - run: npm run lint 18 | - run: npm run build 19 | -------------------------------------------------------------------------------- /src/textbox styler.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | start, 3 | walkToCat, 4 | press, 5 | end, 6 | snapshot, 7 | } from './test/bitsy'; 8 | 9 | test('textbox styler', async () => { 10 | await start({ 11 | hacks: ['textbox styler'], 12 | }); 13 | await walkToCat(); 14 | await press('ArrowRight'); // talk to cat 15 | await press('Enter'); // complete dialog page 16 | await snapshot(); 17 | await end(); 18 | }); 19 | -------------------------------------------------------------------------------- /src/transparent dialog.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | start, 3 | walkToCat, 4 | press, 5 | end, 6 | snapshot, 7 | } from './test/bitsy'; 8 | 9 | test('transparent dialog', async () => { 10 | await start({ 11 | hacks: ['transparent dialog'], 12 | }); 13 | await walkToCat(); 14 | await snapshot(); 15 | await press('ArrowRight'); // talk to cat 16 | await press('Enter'); // complete dialog page 17 | await snapshot(); 18 | await end(); 19 | }); 20 | -------------------------------------------------------------------------------- /src/custom text effect.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | start, 3 | walkToCat, 4 | press, 5 | end, 6 | snapshot, 7 | } from './test/bitsy'; 8 | 9 | test('strike', async () => { 10 | await start({ 11 | catDialog: '{strike}strike{strike}{br}custom text effect', 12 | hacks: ['custom text effect'], 13 | }); 14 | await walkToCat(); 15 | await press('ArrowRight'); // talk to cat 16 | await press('Enter'); // complete dialog page 17 | await snapshot(); 18 | await end(); 19 | }); 20 | -------------------------------------------------------------------------------- /src/transparent sprites.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | start, 3 | press, 4 | end, 5 | snapshot, 6 | } from './test/bitsy'; 7 | 8 | test('transparent sprites', async () => { 9 | await start({ 10 | hacks: ['transparent sprites'], 11 | }); 12 | await press('Enter'); // complete title page 13 | await press('Enter'); // end title page 14 | await press('ArrowLeft'); 15 | await press('ArrowLeft'); 16 | await press('ArrowLeft'); // walk on top of tile border 17 | await snapshot(); 18 | await end(); 19 | }); 20 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | release: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - uses: josephbmanley/butler-publish-itchio-action@v1.0.2 13 | env: 14 | BUTLER_CREDENTIALS: ${{ secrets.BUTLER_CREDENTIALS }} 15 | CHANNEL: win-linux-mac-stable 16 | ITCH_GAME: bitsy-hacks 17 | ITCH_USER: seansleblanc 18 | PACKAGE: dist 19 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | dist/* linguist-generated=true 2 | 3 | # Auto detect text files and perform LF normalization 4 | * text=auto 5 | *.js text eol=lf 6 | 7 | # Custom for Visual Studio 8 | *.cs diff=csharp 9 | 10 | # Standard to msysgit 11 | *.doc diff=astextplain 12 | *.DOC diff=astextplain 13 | *.docx diff=astextplain 14 | *.DOCX diff=astextplain 15 | *.dot diff=astextplain 16 | *.DOT diff=astextplain 17 | *.pdf diff=astextplain 18 | *.PDF diff=astextplain 19 | *.rtf diff=astextplain 20 | *.RTF diff=astextplain 21 | -------------------------------------------------------------------------------- /src/follower.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | start, 3 | press, 4 | end, 5 | snapshot, 6 | delay, 7 | } from './test/bitsy'; 8 | 9 | test('follower', async () => { 10 | await start({ 11 | hacks: ['follower'], 12 | }); 13 | await press('Enter'); // complete title page 14 | await press('Enter'); // end title page 15 | await press('ArrowLeft'); 16 | await press('ArrowLeft'); 17 | await press('ArrowLeft'); // walk on top of tile border 18 | await delay(500); // wait for follower to catch up 19 | await snapshot(); 20 | await end(); 21 | }); 22 | -------------------------------------------------------------------------------- /src/paragraph-break.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | start, 3 | walkToCat, 4 | press, 5 | end, 6 | snapshot, 7 | } from './test/bitsy'; 8 | 9 | test('p', async () => { 10 | await start({ 11 | catDialog: 'paragraph 1(p)paragraph 2', 12 | hacks: ['paragraph-break'], 13 | }); 14 | await walkToCat(); 15 | await press('ArrowRight'); // talk to cat 16 | await press('Enter'); // complete dialog page 17 | await snapshot(); 18 | await press('Enter'); // next dialog page 19 | await press('Enter'); // complete dialog page 20 | await snapshot(); 21 | await press('Enter'); // end dialog 22 | await snapshot(); 23 | await end(); 24 | }); 25 | -------------------------------------------------------------------------------- /src/dialog pause.js: -------------------------------------------------------------------------------- 1 | /** 2 | 💬 3 | @file dialog pause 4 | @summary add pauses in between printing text 5 | @license MIT 6 | @version auto 7 | @author Sean S. LeBlanc 8 | 9 | @description 10 | Adds a command that allows you to add pauses in between printing text. 11 | 12 | Example: (pause "1000") 13 | 14 | Note: pause times are in milliseconds 15 | 16 | HOW TO USE: 17 | Copy-paste into a script tag after the bitsy source 18 | */ 19 | import { 20 | addDialogTag, 21 | } from './helpers/kitsy-script-toolkit'; 22 | 23 | addDialogTag('pause', function (environment, parameters, onReturn) { 24 | environment.GetDialogBuffer().Update(-parseFloat(parameters[0])); 25 | onReturn(null); 26 | }); 27 | -------------------------------------------------------------------------------- /.github/workflows/master.yml: -------------------------------------------------------------------------------- 1 | name: master 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | release: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - uses: actions/setup-node@v1 14 | with: 15 | node-version: 12 16 | - run: npm ci 17 | - uses: cycjimmy/semantic-release-action@v2 18 | env: 19 | GITHUB_TOKEN: ${{ secrets.REPO_READ_TOKEN }} 20 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 21 | with: 22 | semantic_version: 17.0.8 23 | extra_plugins: | 24 | @semantic-release/changelog@5.0.1 25 | @semantic-release/git@9.0.0 26 | -------------------------------------------------------------------------------- /src/long dialog.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | start, 3 | walkToCat, 4 | press, 5 | end, 6 | snapshot, 7 | } from './test/bitsy'; 8 | 9 | test('long dialog', async () => { 10 | await start({ 11 | hacks: ['long dialog'], 12 | catDialog: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean aliquet neque quis volutpat interdum. Aenean blandit volutpat maximus. Nam lectus purus, convallis nec finibus sit amet, malesuada eu nisl.', 13 | }); 14 | await walkToCat(); 15 | await press('ArrowRight'); // talk to cat 16 | await press('Enter'); // complete dialog page 17 | await snapshot(); 18 | await press('Enter'); // next dialog page 19 | await press('Enter'); // complete dialog page 20 | await snapshot(); 21 | await end(); 22 | }); 23 | -------------------------------------------------------------------------------- /HeaderCommentPlugin.mjs: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const pkg = require('./package.json'); 3 | // moves the last /**/ block to the top of the output 4 | // assuming the source doesn't have any other /**/ blocks, 5 | // this will be the header 6 | // this is hackier than it should be, but gets the job done for now 7 | export default function () { 8 | return { 9 | renderChunk(code) { 10 | const pattern = /^(\/\*[\S\s]*?\*\/)$/gm; 11 | const matches = code.match(pattern); 12 | if (!matches) { 13 | return { 14 | code: code 15 | }; 16 | } 17 | const header = matches[matches.length - 1]; 18 | return { 19 | code: `${header.replace(/^@version auto$/m, `@version ${pkg.version}`)}\n${code.replace(header, '')}` 20 | }; 21 | } 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /src/edit dialog from dialog.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | start, 3 | walkToCat, 4 | press, 5 | end, 6 | snapshot, 7 | } from './test/bitsy'; 8 | 9 | test('dialog', async () => { 10 | await start({ 11 | catDialog: '\\(dialog "SPR, a, new dialog"\\)(dialog "SPR, a, new dialog")', 12 | hacks: ['edit dialog from dialog'], 13 | }); 14 | await walkToCat(); 15 | await press('ArrowRight'); // talk to cat 16 | await press('Enter'); // complete dialog page 17 | await snapshot(); 18 | await press('Enter'); // end dialog 19 | await snapshot(); 20 | await press('ArrowRight'); // talk to cat again 21 | await press('Enter'); // complete dialog page 22 | await snapshot(); 23 | await press('Enter'); // end dialog 24 | await snapshot(); 25 | await end(); 26 | }); 27 | -------------------------------------------------------------------------------- /src/custom-keyhandlers.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | start, 3 | end, 4 | waitForFrame, 5 | page, 6 | } from './test/bitsy'; 7 | 8 | test('custom-keyhandlers', async () => { 9 | await start({ 10 | hacks: ['custom-keyhandlers'], 11 | }); 12 | const logs = []; 13 | page.on('console', (message) => logs.push(message.text())); 14 | await page.keyboard.down('z'); 15 | await waitForFrame(); 16 | expect(logs).toContain('pressed z'); 17 | expect(logs).toContain('held z for 1 frames'); 18 | await waitForFrame(); 19 | expect(logs).toContain('held z for 2 frames'); // note that b/c of async nature, it'll have more than two at this point 20 | expect(logs).not.toContain('released z'); 21 | await page.keyboard.up('z'); 22 | expect(logs).not.toContain('released z'); 23 | await end(); 24 | }); 25 | -------------------------------------------------------------------------------- /src/helpers/__snapshots__/utils.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`unique returns an array which is a copy of the parameter, without duplicates 1`] = `Array []`; 4 | 5 | exports[`unique returns an array which is a copy of the parameter, without duplicates 2`] = ` 6 | Array [ 7 | 1, 8 | 2, 9 | 3, 10 | 4, 11 | 5, 12 | ] 13 | `; 14 | 15 | exports[`unique returns an array which is a copy of the parameter, without duplicates 3`] = ` 16 | Array [ 17 | "a", 18 | "b", 19 | "c", 20 | "A", 21 | ] 22 | `; 23 | 24 | exports[`unique returns an array which is a copy of the parameter, without duplicates 4`] = ` 25 | Array [ 26 | Object {}, 27 | ] 28 | `; 29 | 30 | exports[`unique returns an array which is a copy of the parameter, without duplicates 5`] = ` 31 | Array [ 32 | 1, 33 | "1", 34 | Object {}, 35 | ] 36 | `; 37 | -------------------------------------------------------------------------------- /src/test/bitsy.test.js: -------------------------------------------------------------------------------- 1 | // baseline test to make sure that bitsy helpers 2 | // function as expected 3 | import { 4 | start, 5 | walkToCat, 6 | snapshot, 7 | startRecording, 8 | stopRecording, 9 | end, 10 | } from './bitsy'; 11 | 12 | test('walkToCat moves the player to the left of the cat in the default game', async () => { 13 | await start(); 14 | await walkToCat(); 15 | await snapshot(); 16 | await end(); 17 | }); 18 | 19 | test('startRecording starts capturing a snapshot after every key press', async () => { 20 | await start(); 21 | await startRecording(); 22 | await walkToCat(); 23 | await end(); 24 | }); 25 | 26 | test('stopRecording stops capturing a snapshot after every key press', async () => { 27 | await start(); 28 | await startRecording(); 29 | await stopRecording(); 30 | await walkToCat(); 31 | await snapshot(); 32 | await end(); 33 | }); 34 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import path from "path"; 2 | import nodeResolve from "rollup-plugin-node-resolve"; 3 | import commonjs from "rollup-plugin-commonjs"; 4 | 5 | import base from "./rollup.config.base.json"; 6 | 7 | import headerComment from "./HeaderCommentPlugin"; 8 | import topLevelOptions from "./TopLevelOptionsPlugin"; 9 | import readme from "./ReadmePlugin"; 10 | import getHacks from "./getHacks"; 11 | 12 | const sharedOptions = { 13 | ...base, 14 | plugins: [ 15 | nodeResolve(), 16 | commonjs(), 17 | readme(), 18 | headerComment(), 19 | topLevelOptions(), 20 | ], 21 | }; 22 | 23 | export default getHacks().map(input => ({ 24 | ...sharedOptions, 25 | input, 26 | output: { 27 | ...sharedOptions.output, 28 | file: `./dist/${path.basename(input).replace(/\s/g,'-')}`, 29 | name: `hacks.${path.basename(input, '.js').replace(/\s/g,'_')}`, 30 | }, 31 | })); 32 | -------------------------------------------------------------------------------- /src/exit-from-dialog.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | start, 3 | walkToCat, 4 | press, 5 | end, 6 | snapshot, 7 | } from './test/bitsy'; 8 | 9 | test('exit', async () => { 10 | await start({ 11 | catDialog: '\\(exit "0,1,1"\\)(exit "0,1,1")', 12 | hacks: ['exit-from-dialog'], 13 | }); 14 | await walkToCat(); 15 | await press('ArrowRight'); // talk to cat 16 | await press('Enter'); // complete dialog page 17 | await snapshot(); 18 | await press('Enter'); // end dialog 19 | await snapshot(); 20 | await end(); 21 | }); 22 | 23 | test('exitNow', async () => { 24 | await start({ 25 | catDialog: '\\(exitNow "0,1,1"\\)(exitNow "0,1,1")', 26 | hacks: ['exit-from-dialog'], 27 | }); 28 | await walkToCat(); 29 | await snapshot(); 30 | await press('ArrowRight'); // talk to cat 31 | await press('Enter'); // complete dialog page 32 | await snapshot(); 33 | await end(); 34 | }); 35 | -------------------------------------------------------------------------------- /src/opaque tiles.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | start, 3 | press, 4 | end, 5 | snapshot, 6 | } from './test/bitsy'; 7 | 8 | test('opaque tiles', async () => { 9 | await start({ 10 | hacks: ['opaque tiles'], 11 | }); 12 | await press('Enter'); // complete title page 13 | await press('Enter'); // end title page 14 | await press('ArrowLeft'); 15 | await press('ArrowLeft'); 16 | await press('ArrowLeft'); // walk on top of tile border 17 | await snapshot(); 18 | await end(); 19 | }); 20 | 21 | test('compatible with transparency', async () => { 22 | await start({ 23 | hacks: ['opaque tiles', 'transparent sprites'], 24 | }); 25 | await press('Enter'); // complete title page 26 | await press('Enter'); // end title page 27 | await press('ArrowLeft'); 28 | await press('ArrowLeft'); 29 | await press('ArrowLeft'); // walk on top of tile border 30 | await snapshot(); 31 | await end(); 32 | }); 33 | -------------------------------------------------------------------------------- /TopLevelOptionsPlugin.mjs: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | // moves the last `var hackOptions={...}` block to the top of the output 3 | // this is hackier than it should be, but gets the job done for now 4 | export default function () { 5 | return { 6 | renderChunk(code) { 7 | // workaround for issues introduced by https://github.com/rollup/rollup/pull/3710/files 8 | code = code 9 | .replace(/bitsy__default\['default'\]/g, 'bitsy') 10 | .replace(/var bitsy__default = /, 'bitsy = bitsy || '); 11 | 12 | // move options 13 | const pattern = /^var\s+hackOptions.*?\s?=\s?{[\s\S]*?^};$/gm; 14 | const matches = code.match(pattern); 15 | if (!matches) { 16 | return { 17 | code: code 18 | }; 19 | } 20 | const header = matches[matches.length - 1]; 21 | return { 22 | code: code.replace(header, '').replace("'use strict';", `'use strict';\n${header}`) 23 | }; 24 | } 25 | }; 26 | } 27 | -------------------------------------------------------------------------------- /src/itsy-bitsy.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | start, 3 | press, 4 | end, 5 | snapshot, 6 | evaluate, 7 | } from './test/bitsy'; 8 | 9 | test('itsy-bitsy', async () => { 10 | await start({ 11 | hacks: ['itsy-bitsy'], 12 | }); 13 | await evaluate(() => { 14 | window.sprite.a.x = window.player().x + 1; 15 | window.sprite.a.y = window.player().y; 16 | }); 17 | await press('Enter'); 18 | await press('Enter'); 19 | await press('Enter'); // complete title pages 20 | await snapshot(); 21 | await press('Enter'); // end title 22 | await snapshot(); 23 | await press('ArrowRight'); // talk to cat 24 | await press('Enter'); // complete dialog page 25 | await snapshot(); 26 | await press('Enter'); // end dialog 27 | await press('ArrowUp'); 28 | await press('ArrowRight'); 29 | await snapshot(); 30 | await press('ArrowDown'); // talk to cat from above 31 | await press('Enter'); // complete dialog page 32 | await snapshot(); 33 | await end(); 34 | }); 35 | -------------------------------------------------------------------------------- /src/end-from-dialog.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | start, 3 | walkToCat, 4 | press, 5 | end, 6 | snapshot, 7 | } from './test/bitsy'; 8 | 9 | test('end', async () => { 10 | await start({ 11 | catDialog: '\\(end "ending"\\)(end "ending")', 12 | hacks: ['end-from-dialog'], 13 | }); 14 | await walkToCat(); 15 | await press('ArrowRight'); // talk to cat 16 | await press('Enter'); // complete dialog page 17 | await snapshot(); 18 | await press('Enter'); // end dialog 19 | await press('Enter'); // complete ending page 20 | await snapshot(); 21 | await end(); 22 | }); 23 | 24 | test('endNow', async () => { 25 | await start({ 26 | catDialog: '\\(endNow "ending"\\)(endNow "ending")', 27 | hacks: ['end-from-dialog'], 28 | }); 29 | await walkToCat(); 30 | await snapshot(); 31 | await press('ArrowRight'); // talk to cat 32 | await press('Enter'); // complete dialog page (also completes ending page due to hack quirk) 33 | await snapshot(); 34 | await end(); 35 | }); 36 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Note that individual hacks have their own license specified; this covers the rest of the repository. 2 | 3 | --- 4 | 5 | MIT License 6 | 7 | Copyright (c) 2019 Sean LeBlanc 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 14 | -------------------------------------------------------------------------------- /src/helpers/transform-sprite-data.js: -------------------------------------------------------------------------------- 1 | /** 2 | @file transform sprite data 3 | @summary Helpers for flipping and rotating sprite data 4 | */ 5 | 6 | // copied from https://stackoverflow.com/a/46805290 7 | function transpose(matrix) { 8 | const rows = matrix.length; 9 | const cols = matrix[0].length; 10 | const grid = []; 11 | for (let j = 0; j < cols; j++) { 12 | grid[j] = Array(rows); 13 | } 14 | for (let i = 0; i < rows; i++) { 15 | for (let j = 0; j < cols; j++) { 16 | grid[j][i] = matrix[i][j]; 17 | } 18 | } 19 | return grid; 20 | } 21 | 22 | // helper function to flip sprite data 23 | export function transformSpriteData(spriteData, v, h, rot) { 24 | var x; 25 | var y; 26 | var x2; 27 | var y2; 28 | var col; 29 | var tmp; 30 | var s = spriteData.slice(); 31 | if (v) { 32 | for (y = 0; y < s.length / 2; ++y) { 33 | y2 = s.length - y - 1; 34 | tmp = s[y]; 35 | s[y] = s[y2]; 36 | s[y2] = tmp; 37 | } 38 | } 39 | if (h) { 40 | for (y = 0; y < s.length; ++y) { 41 | col = s[y] = s[y].slice(); 42 | for (x = 0; x < col.length / 2; ++x) { 43 | x2 = col.length - x - 1; 44 | tmp = col[x]; 45 | col[x] = col[x2]; 46 | col[x2] = tmp; 47 | } 48 | } 49 | } 50 | if (rot) { 51 | s = transpose(s); 52 | } 53 | return s; 54 | } 55 | -------------------------------------------------------------------------------- /src/unique items.js: -------------------------------------------------------------------------------- 1 | /** 2 | ❄ 3 | @file unique items 4 | @summary items which, when picked up, remove all other instances of that item from the game 5 | @license MIT 6 | @version auto 7 | @author Sean S. LeBlanc 8 | 9 | @description 10 | Adds support for items which, when picked up, 11 | remove all other instances of that item from the game. 12 | 13 | HOW TO USE: 14 | 1. Copy-paste this script into a script tag after the bitsy source 15 | 2. Update the `itemIsUnique` function to match your needs 16 | */ 17 | import bitsy from 'bitsy'; 18 | import { 19 | after, 20 | } from './helpers/kitsy-script-toolkit'; 21 | 22 | export var hackOptions = { 23 | itemIsUnique: function (item) { 24 | // return item.name && item.name == 'tea'; // specific unique item 25 | // return ['tea', 'flower', 'hat'].indexOf(item.name) !== -1; // specific unique item list 26 | // return item.name && item.name.indexOf('UNIQUE') !== -1; // unique item flag in name 27 | return true; // all items are unique 28 | }, 29 | }; 30 | 31 | after('onInventoryChanged', function (id) { 32 | if (hackOptions.itemIsUnique(bitsy.item[id])) { 33 | Object.values(bitsy.room).forEach(function (room) { 34 | room.items = room.items.filter(function (i) { 35 | return i.id !== id; 36 | }); 37 | }); 38 | } 39 | }); 40 | -------------------------------------------------------------------------------- /src/edit room from dialog.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | start, 3 | walkToCat, 4 | press, 5 | end, 6 | snapshot, 7 | } from './test/bitsy'; 8 | 9 | async function testDialog(dlg) { 10 | await start({ 11 | catDialog: dlg, 12 | hacks: ['edit room from dialog'], 13 | }); 14 | await walkToCat(); 15 | await press('ArrowRight'); // talk to cat 16 | await snapshot(); 17 | await end(); 18 | } 19 | 20 | test('draw', async () => testDialog('(drawNow "TIL, a, 4, 4, 0")')); 21 | test('drawBox', async () => testDialog('(drawBoxNow "TIL, a, 4, 4, 6, 6, 0")')); 22 | test('drawAll', async () => testDialog('(drawAll "TIL, a, 0")')); 23 | test('erase', async () => testDialog('(eraseNow "TIL, a, 1, 1, 0")')); 24 | test('eraseBox', async () => testDialog('(eraseBoxNow "TIL, a, 1, 1, 2, 2, 0")')); 25 | test('eraseAll', async () => testDialog('(eraseAllNow "TIL, a, 0")')); 26 | test('replace', async () => testDialog('(replaceNow "TIL, a, ITM, 0, 1, 1, 0")')); 27 | test('replaceBox', async () => testDialog('(replaceBoxNow "TIL, a, ITM, 0, 1, 1, 2, 2, 0")')); 28 | test('replaceAll', async () => testDialog('(replaceAllNow "TIL, a, ITM, 0, 0")')); 29 | test('copy', async () => testDialog('(copyNow "TIL, a, 1, 1, 0, 2, 2, 0")')); 30 | test('copyBox', async () => testDialog('(copyBoxNow "TIL, a, 1, 1, 2, 2, 0, 3, 3, 0")')); 31 | -------------------------------------------------------------------------------- /src/sprite effects.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | end, press, snapshot, start, walkToCat, 3 | } from './test/bitsy'; 4 | 5 | test('sprite effects', async () => { 6 | await start({ 7 | catDialog: '\\(spriteEffect "SPR,A,invert"\\){spriteEffect "SPR,A,invert"}', 8 | hacks: [ 9 | 'sprite effects', 10 | ], 11 | }); 12 | await walkToCat(); 13 | await press('ArrowRight'); // talk to cat 14 | await press('Enter'); // complete dialog page 15 | await press('Enter'); // end dialog 16 | await snapshot(); 17 | await press('ArrowRight'); // talk to cat 18 | await press('Enter'); // complete dialog page 19 | await press('Enter'); // end dialog 20 | await snapshot(); 21 | await end(); 22 | }); 23 | 24 | test('compatible with transparency', async () => { 25 | await start({ 26 | catDialog: '\\(spriteEffect "SPR,A,invert"\\){spriteEffect "SPR,A,invert"}', 27 | hacks: [ 28 | 'transparent sprites', 29 | 'sprite effects', 30 | ], 31 | }); 32 | await walkToCat(); 33 | await press('ArrowRight'); // talk to cat 34 | await press('Enter'); // complete dialog page 35 | await press('Enter'); // end dialog 36 | await snapshot(); 37 | await press('ArrowRight'); // talk to cat 38 | await press('Enter'); // complete dialog page 39 | await press('Enter'); // end dialog 40 | await snapshot(); 41 | await end(); 42 | }); 43 | -------------------------------------------------------------------------------- /src/direction in dialog.js: -------------------------------------------------------------------------------- 1 | /** 2 | 🔝 3 | @file direction in dialog 4 | @summary provides a variable with player direction 5 | @license MIT 6 | @version auto 7 | @requires 5.3 8 | @author Sean S. LeBlanc 9 | 10 | @description 11 | Provides a variable "playerDirection" that can be accessed in dialog 12 | The value will be one of: 13 | - "up" 14 | - "down" 15 | - "left" 16 | - "right" 17 | Depending on the last input from the player. 18 | 19 | Note that the variable will describe the direction the player moved, 20 | so if they're interacting with a sprite, the opposite will be the direction from which they came 21 | i.e. if the player moves into a sprite from the left, the variable will be "right" 22 | 23 | HOW TO USE: 24 | Copy-paste into a script tag after the bitsy source 25 | */ 26 | import bitsy from 'bitsy'; 27 | import { 28 | before, 29 | } from './helpers/kitsy-script-toolkit'; 30 | 31 | var keys = {}; 32 | keys[bitsy.Direction.Up] = 'up'; 33 | keys[bitsy.Direction.Down] = 'down'; 34 | keys[bitsy.Direction.Left] = 'left'; 35 | keys[bitsy.Direction.Right] = 'right'; 36 | keys[bitsy.Direction.None] = null; 37 | 38 | before('startDialog', function () { 39 | var direction = keys[bitsy.curPlayerDirection]; 40 | if (direction) { 41 | bitsy.scriptInterpreter.SetVariable('playerDirection', direction); 42 | } 43 | }); 44 | -------------------------------------------------------------------------------- /src/edit image from dialog.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | start, 3 | walkToCat, 4 | press, 5 | end, 6 | snapshot, 7 | } from './test/bitsy'; 8 | 9 | test('image', async () => { 10 | await start({ 11 | catDialog: '\\(image "SPR, a, A"\\)(image "SPR, a, A")', 12 | hacks: ['edit image from dialog'], 13 | }); 14 | await walkToCat(); 15 | await press('ArrowRight'); // talk to cat 16 | await press('Enter'); // complete dialog page 17 | await snapshot(); 18 | await press('Enter'); // end dialog 19 | await snapshot(); 20 | await end(); 21 | }); 22 | 23 | test('imageNow', async () => { 24 | await start({ 25 | catDialog: '\\(imageNow "SPR, a, A"\\)(imageNow "SPR, a, A")', 26 | hacks: ['edit image from dialog'], 27 | }); 28 | await walkToCat(); 29 | await snapshot(); 30 | await press('ArrowRight'); // talk to cat 31 | await press('Enter'); // complete dialog page 32 | await snapshot(); 33 | await end(); 34 | }); 35 | 36 | test('compatible with transparency', async () => { 37 | await start({ 38 | catDialog: '\\(imageNow "SPR, A, a"\\)(imageNow "SPR, A, a")', 39 | hacks: ['edit image from dialog', 'transparent sprites'], 40 | }); 41 | await walkToCat(); 42 | await press('ArrowRight'); // talk to cat 43 | await press('Enter'); // complete dialog page 44 | await press('Enter'); // close dialog 45 | await press('ArrowDown'); 46 | await press('ArrowDown'); // walk on top of tile border 47 | await snapshot(); 48 | await end(); 49 | }); 50 | -------------------------------------------------------------------------------- /src/close on ending.js: -------------------------------------------------------------------------------- 1 | /** 2 | ⛔️ 3 | @file close on ending 4 | @summary Prevents from playing past an ending 5 | @license MIT 6 | @version auto 7 | @author Sean S. LeBlanc 8 | 9 | @description 10 | Prevent from playing past an ending. 11 | When an ending is reached, it will prevent the game from being restarted, 12 | the bitsy game canvas will be removed, and it will attempt to close the window. 13 | Windows can't always be closed due to browser security reasons; 14 | rendering the game unresponsive is the best that can be done in that situation. 15 | 16 | NOTE: This hack also disables the ctrl+r restart prompt, 17 | but players will still be able to manually refresh or close/re-open the page to restart. 18 | 19 | HOW TO USE: 20 | Copy-paste this script into a script tag after the bitsy source 21 | */ 22 | import bitsy from 'bitsy'; 23 | import { 24 | inject, 25 | after, 26 | } from './helpers/kitsy-script-toolkit'; 27 | 28 | // prevent ctrl+r restart prompt 29 | inject(/(function tryRestartGame\(e\) {)/, '$1return;'); 30 | 31 | after('onExitDialog', function () { 32 | if (bitsy.isEnding) { 33 | // prevent further input 34 | var no = function () { 35 | return false; 36 | }; 37 | bitsy.input.isKeyDown = bitsy.input.anyKeyPressed = bitsy.input.swipeLeft = bitsy.input.swipeRight = bitsy.input.swipeUp = bitsy.input.swipeDown = bitsy.input.isTapReleased = no; 38 | // remove canvas 39 | bitsy.canvas.remove(); 40 | // attempt to close 41 | window.close(); 42 | } 43 | }); 44 | -------------------------------------------------------------------------------- /src/edit player from dialog.js: -------------------------------------------------------------------------------- 1 | /** 2 | 👯‍♂️ 3 | @file edit player from dialog 4 | @summary change which sprite is controlled by the player 5 | @license MIT 6 | @version auto 7 | @requires 7.0 8 | @author Sean S. LeBlanc 9 | 10 | @description 11 | You can use this to change which sprite is controlled by the player. 12 | 13 | (player "target") 14 | (playerNow "target") 15 | Parameters: 16 | target: id/name of sprite that will be the new player 17 | 18 | Examples: 19 | (player "a") 20 | (playerNow "a") 21 | 22 | HOW TO USE: 23 | Copy-paste this script into a new script tag after the Bitsy source code. 24 | 25 | NOTE: 26 | - The original player sprite has an id of 'A' by default 27 | - Inventory (i.e. item counts) is per-sprite, not shared. 28 | If you want to simulate "shared" inventory, include standard 29 | dialog variables on your items that increment when picked up 30 | */ 31 | import bitsy from 'bitsy'; 32 | import { 33 | addDualDialogTag, 34 | } from './helpers/kitsy-script-toolkit'; 35 | import { 36 | getImage, 37 | } from './helpers/utils'; 38 | 39 | addDualDialogTag('player', function (environment, parameters) { 40 | var targetId = parameters[0]; 41 | var target = getImage(targetId, bitsy.sprite); 42 | if (!target) { 43 | throw new Error('Could not change player: invalid sprite "' + targetId + '"'); 44 | } 45 | if (!target.room) { 46 | throw new Error('Could not change player: sprite "' + targetId + '" not placed in a room'); 47 | } 48 | bitsy.playerId = targetId; 49 | bitsy.curRoom = target.room; 50 | }); 51 | -------------------------------------------------------------------------------- /src/direction in dialog.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | start, 3 | walkToCat, 4 | press, 5 | end, 6 | snapshot, 7 | } from './test/bitsy'; 8 | 9 | test('direction in dialog', async () => { 10 | await start({ 11 | hacks: ['direction in dialog'], 12 | catDialog: `""" 13 | { 14 | - playerDirection == "up" ? 15 | i'm a cat above you 16 | - playerDirection == "left" ? 17 | i'm a cat to the left of you 18 | - playerDirection == "right" ? 19 | i'm a cat to the right of you 20 | - playerDirection == "down" ? 21 | i'm a cat below you 22 | - else ? 23 | i'm a cat but the hack didn't work 24 | } 25 | """`, 26 | }); 27 | await walkToCat(); 28 | await press('ArrowRight'); // talk to cat from the right 29 | await press('Enter'); // complete dialog page 30 | await snapshot(); 31 | await press('Enter'); // close dialog page 32 | 33 | await press('ArrowUp'); 34 | await press('ArrowRight'); 35 | await press('ArrowDown'); // talk to cat from above 36 | await press('Enter'); // complete dialog page 37 | await snapshot(); 38 | await press('Enter'); // close dialog page 39 | 40 | await press('ArrowRight'); 41 | await press('ArrowDown'); 42 | await press('ArrowLeft'); // talk to cat from the left 43 | await press('Enter'); // complete dialog page 44 | await snapshot(); 45 | await press('Enter'); // close dialog page 46 | 47 | await press('ArrowDown'); 48 | await press('ArrowLeft'); 49 | await press('ArrowUp'); // talk to cat from below 50 | await press('Enter'); // complete dialog page 51 | await snapshot(); 52 | await end(); 53 | }); 54 | -------------------------------------------------------------------------------- /src/palette maps.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | start, 3 | end, 4 | snapshot, 5 | } from './test/bitsy'; 6 | 7 | test('palette maps', async () => { 8 | await start({ 9 | hacks: ['palette maps'], 10 | gamedata: ` 11 | 12 | # BITSY VERSION 7.0 13 | 14 | ! ROOM_FORMAT 1 15 | 16 | PAL 0 17 | 0,82,204 18 | 128,159,255 19 | 255,255,255 20 | 21 | PAL 1 22 | 255,82,204 23 | 255,159,255 24 | 0,255,255 25 | 26 | ROOM 100 27 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 28 | 0,a,a,a,a,a,a,a,a,a,a,a,a,a,a,0 29 | 0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0 30 | 0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0 31 | 0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0 32 | 0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0 33 | 0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0 34 | 0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0 35 | 0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0 36 | 0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0 37 | 0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0 38 | 0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0 39 | 0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0 40 | 0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0 41 | 0,a,a,a,a,a,a,a,a,a,a,a,a,a,a,0 42 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 43 | PAL 0 44 | 45 | TIL a 46 | 11111111 47 | 10000001 48 | 10000001 49 | 10011001 50 | 10011001 51 | 10000001 52 | 10000001 53 | 11111111 54 | 55 | SPR A 56 | 00011000 57 | 00011000 58 | 00011000 59 | 00111100 60 | 01111110 61 | 10111101 62 | 00100100 63 | 00100100 64 | POS 100 4,4 65 | 66 | SPR a 67 | 00000000 68 | 00000000 69 | 01010001 70 | 01110001 71 | 01110010 72 | 01111100 73 | 00111100 74 | 00100100 75 | DLG 0 76 | POS 100 8,12 77 | 78 | ITM 0 79 | 00000000 80 | 00000000 81 | 00000000 82 | 00111100 83 | 01100100 84 | 00100100 85 | 00011000 86 | 00000000 87 | `, 88 | }); 89 | await snapshot(); 90 | await end(); 91 | }); 92 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # node-waf configuration 21 | .lock-wscript 22 | 23 | # Compiled binary addons (http://nodejs.org/api/addons.html) 24 | build/Release 25 | 26 | # Dependency directories 27 | node_modules 28 | jspm_packages 29 | 30 | # Optional npm cache directory 31 | .npm 32 | 33 | # Optional REPL history 34 | .node_repl_history 35 | 36 | # ========================= 37 | # Operating System Files 38 | # ========================= 39 | 40 | # OSX 41 | # ========================= 42 | 43 | .DS_Store 44 | .AppleDouble 45 | .LSOverride 46 | 47 | # Thumbnails 48 | ._* 49 | 50 | # Files that might appear in the root of a volume 51 | .DocumentRevisions-V100 52 | .fseventsd 53 | .Spotlight-V100 54 | .TemporaryItems 55 | .Trashes 56 | .VolumeIcon.icns 57 | 58 | # Directories potentially created on remote AFP share 59 | .AppleDB 60 | .AppleDesktop 61 | Network Trash Folder 62 | Temporary Items 63 | .apdisk 64 | 65 | # Windows 66 | # ========================= 67 | 68 | # Windows image file caches 69 | Thumbs.db 70 | ehthumbs.db 71 | 72 | # Folder config file 73 | Desktop.ini 74 | 75 | # Recycle Bin used on file shares 76 | $RECYCLE.BIN/ 77 | 78 | # Windows Installer files 79 | *.cab 80 | *.msi 81 | *.msm 82 | *.msp 83 | 84 | # Windows shortcuts 85 | *.lnk 86 | -------------------------------------------------------------------------------- /src/dialog jump.js: -------------------------------------------------------------------------------- 1 | /** 2 | 🚀 3 | @file dialog jump 4 | @summary jump from one dialog entry to another 5 | @license MIT 6 | @version auto 7 | @requires 7.0 8 | @author Sean S. LeBlanc 9 | 10 | @description 11 | This can be used to simplify complex dialog 12 | by moving portions to self-contained dialog entries, 13 | and then jumping to the appropriate id when necessary. 14 | 15 | You can also provide raw dialog text instead of an id; 16 | Functionally this isn't much different from writing raw dialog text, 17 | but it has some uses for advanced cases (e.g. when combined with dialog choices) 18 | 19 | Usage: 20 | (jump "dialogId") 21 | (jumpNow "dialogId") 22 | (jump "dialog to print") 23 | (jumpNow "dialog to print") 24 | 25 | Note: be careful of infinite loops, e.g. 26 | DLG_infinite_loop 27 | """ 28 | this will print forever(jump "DLG_infinite_loop") 29 | """ 30 | 31 | HOW TO USE: 32 | Copy-paste into a script tag after the bitsy source 33 | */ 34 | 35 | import bitsy from 'bitsy'; 36 | import { 37 | addDualDialogTag, 38 | } from './helpers/kitsy-script-toolkit'; 39 | 40 | // jump function 41 | function jump(targetDialog) { 42 | if (!targetDialog) { 43 | console.warn('Tried to jump to dialog, but no target dialog provided'); 44 | return; 45 | } 46 | var dialogStr = bitsy.dialog[targetDialog]; 47 | var dialogId; 48 | if (!dialogStr) { 49 | dialogStr = targetDialog; 50 | } else { 51 | dialogId = targetDialog; 52 | dialogStr = dialogStr.src; 53 | } 54 | bitsy.startDialog(dialogStr, dialogId); 55 | } 56 | 57 | addDualDialogTag('jump', function (environment, parameters) { 58 | jump(parameters[0]); 59 | }); 60 | -------------------------------------------------------------------------------- /src/transparent dialog.js: -------------------------------------------------------------------------------- 1 | /** 2 | 👁️‍🗨️ 3 | @file transparent dialog 4 | @summary makes the dialog box have a transparent background 5 | @license MIT 6 | @version auto 7 | @author Sean S. LeBlanc 8 | 9 | @description 10 | Makes the dialog box have a transparent background. 11 | 12 | Note: this one's ~pretty hacky~. 13 | 14 | HOW TO USE: 15 | Copy-paste into a script tag after the bitsy source 16 | */ 17 | import bitsy from 'bitsy'; 18 | import { 19 | inject, 20 | } from './helpers/kitsy-script-toolkit'; 21 | 22 | bitsy.transparentDialog = { 23 | canvas: document.createElement('canvas'), 24 | }; 25 | bitsy.transparentDialog.context = bitsy.transparentDialog.canvas.getContext('2d'); 26 | var drawOverride = ` 27 | if(context == null) return; 28 | transparentDialog.canvas.width = textboxInfo.width*scale; 29 | transparentDialog.canvas.height = textboxInfo.height*scale; 30 | transparentDialog.context.putImageData(textboxInfo.img, 0, 0); 31 | if (isCentered) { 32 | context.drawImage(transparentDialog.canvas, textboxInfo.left*scale, ((height/2)-(textboxInfo.height/2))*scale); 33 | } else if (player().y < mapsize/2) { 34 | context.drawImage(transparentDialog.canvas, textboxInfo.left*scale, (height-textboxInfo.bottom-textboxInfo.height)*scale); 35 | } 36 | else { 37 | context.drawImage(transparentDialog.canvas, textboxInfo.left*scale, textboxInfo.top*scale); 38 | } 39 | return;`; 40 | 41 | // override textbox drawing to use draw image version from above 42 | inject(/(this\.DrawTextbox = function\(\) {)/, '$1' + drawOverride); 43 | 44 | // override textbox clearing pixels to be fully transparent 45 | inject(/(textboxInfo\.img\.data\[i\+3\]=)255/, '$10'); 46 | -------------------------------------------------------------------------------- /src/solid items.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | start, 3 | press, 4 | end, 5 | snapshot, 6 | } from './test/bitsy'; 7 | 8 | test('solid items', async () => { 9 | await start({ 10 | gamedata: ` 11 | 12 | # BITSY VERSION 7.0 13 | 14 | ! ROOM_FORMAT 1 15 | 16 | PAL 0 17 | NAME blueprint 18 | 0,82,204 19 | 128,159,255 20 | 255,255,255 21 | 22 | ROOM 0 23 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 24 | 0,a,a,a,a,a,a,a,a,a,a,a,a,a,a,0 25 | 0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0 26 | 0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0 27 | 0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0 28 | 0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0 29 | 0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0 30 | 0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0 31 | 0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0 32 | 0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0 33 | 0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0 34 | 0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0 35 | 0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0 36 | 0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0 37 | 0,a,a,a,a,a,a,a,a,a,a,a,a,a,a,0 38 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 39 | NAME example room 40 | ITM 0 5,4 41 | PAL 0 42 | 43 | TIL a 44 | 11111111 45 | 10000001 46 | 10000001 47 | 10011001 48 | 10011001 49 | 10000001 50 | 10000001 51 | 11111111 52 | NAME block 53 | 54 | SPR A 55 | 00011000 56 | 00011000 57 | 00011000 58 | 00111100 59 | 01111110 60 | 10111101 61 | 00100100 62 | 00100100 63 | POS 0 4,4 64 | 65 | ITM 0 66 | 00000000 67 | 00000000 68 | 00000000 69 | 00111100 70 | 01100100 71 | 00100100 72 | 00011000 73 | 00000000 74 | NAME tea 75 | DLG 1 76 | 77 | DLG 1 78 | You found a nice warm cup of tea 79 | NAME tea dialog 80 | 81 | VAR a 82 | 42`, 83 | hacks: ['solid items'], 84 | }); 85 | await press('ArrowRight'); // walk onto item 86 | await press('Enter'); // complete dialog 87 | await snapshot(); 88 | await press('Enter'); // end dialog 89 | await snapshot(); 90 | await end(); 91 | }); 92 | -------------------------------------------------------------------------------- /src/permanent items.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | start, 3 | press, 4 | end, 5 | snapshot, 6 | } from './test/bitsy'; 7 | 8 | test('permanent items', async () => { 9 | await start({ 10 | gamedata: ` 11 | 12 | # BITSY VERSION 7.0 13 | 14 | ! ROOM_FORMAT 1 15 | 16 | PAL 0 17 | NAME blueprint 18 | 0,82,204 19 | 128,159,255 20 | 255,255,255 21 | 22 | ROOM 0 23 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 24 | 0,a,a,a,a,a,a,a,a,a,a,a,a,a,a,0 25 | 0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0 26 | 0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0 27 | 0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0 28 | 0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0 29 | 0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0 30 | 0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0 31 | 0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0 32 | 0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0 33 | 0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0 34 | 0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0 35 | 0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0 36 | 0,a,0,0,0,0,0,0,0,0,0,0,0,0,a,0 37 | 0,a,a,a,a,a,a,a,a,a,a,a,a,a,a,0 38 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 39 | NAME example room 40 | ITM 0 5,4 41 | PAL 0 42 | 43 | TIL a 44 | 11111111 45 | 10000001 46 | 10000001 47 | 10011001 48 | 10011001 49 | 10000001 50 | 10000001 51 | 11111111 52 | NAME block 53 | 54 | SPR A 55 | 00011000 56 | 00011000 57 | 00011000 58 | 00111100 59 | 01111110 60 | 10111101 61 | 00100100 62 | 00100100 63 | POS 0 4,4 64 | 65 | ITM 0 66 | 00000000 67 | 00000000 68 | 00000000 69 | 00111100 70 | 01100100 71 | 00100100 72 | 00011000 73 | 00000000 74 | NAME tea 75 | DLG 1 76 | 77 | DLG 1 78 | You found a nice warm cup of tea 79 | NAME tea dialog 80 | 81 | VAR a 82 | 42`, 83 | hacks: ['permanent items'], 84 | }); 85 | await press('ArrowRight'); // walk onto item 86 | await press('Enter'); // complete dialog 87 | await press('Enter'); // end dialog 88 | await press('ArrowRight'); // walk off item 89 | await snapshot(); 90 | await end(); 91 | }); 92 | -------------------------------------------------------------------------------- /src/character portraits.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | end, 3 | press, 4 | snapshot, 5 | start, 6 | walkToCat, 7 | } from './test/bitsy'; 8 | 9 | test('autoreset, dialog only', async () => { 10 | await start({ 11 | catDialog: '(portrait "cat")\\(portrait "cat"\\)', 12 | hacks: [['character portraits', { 13 | scale: 1 / 128, 14 | portraits: { 15 | cat: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==', // rgba(255,0,0,0.5) pixel 16 | }, 17 | autoReset: true, 18 | dialogOnly: true, 19 | }]], 20 | }); 21 | await walkToCat(); 22 | await press('ArrowRight'); // talk to cat 23 | await press('Enter'); // complete dialog page 24 | await snapshot(); 25 | await press('Enter'); // end dialog page 26 | await snapshot(); 27 | await end(); 28 | }); 29 | 30 | test('no reset, persist', async () => { 31 | await start({ 32 | catDialog: `""" 33 | {sequence 34 | - (portrait "cat")\\(portrait "cat"\\) 35 | - (portrait "")\\(portrait ""\\) 36 | } 37 | """`, 38 | hacks: [['character portraits', { 39 | scale: 1 / 128, 40 | portraits: { 41 | cat: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==', // rgba(255,0,0,0.5) pixel 42 | }, 43 | autoReset: false, 44 | dialogOnly: false, 45 | }]], 46 | }); 47 | await walkToCat(); 48 | await press('ArrowRight'); // talk to cat 49 | await press('Enter'); // complete dialog page 50 | await snapshot(); 51 | await press('Enter'); // end dialog page 52 | await snapshot(); 53 | await press('ArrowRight'); // talk to cat 54 | await press('Enter'); // complete dialog page 55 | await snapshot(); 56 | await press('Enter'); // end dialog page 57 | await snapshot(); 58 | await end(); 59 | }); 60 | -------------------------------------------------------------------------------- /src/opaque tiles.js: -------------------------------------------------------------------------------- 1 | /** 2 | ⬛ 3 | @file opaque tiles 4 | @summary tiles which hide the player 5 | @license MIT 6 | @version auto 7 | @author Sean S. LeBlanc 8 | 9 | @description 10 | Render the player underneath certain tiles 11 | instead of always on top of the map. 12 | 13 | Note: compatible with transparency hack! 14 | 15 | HOW TO USE: 16 | 1. Copy-paste this script into a script tag after the bitsy source 17 | 2. Update the `tileIsOpaque` function below to match your needs 18 | */ 19 | import bitsy from 'bitsy'; 20 | import { 21 | before, 22 | after, 23 | inject, 24 | } from './helpers/kitsy-script-toolkit'; 25 | 26 | export var hackOptions = { 27 | tileIsOpaque: function (tile) { 28 | // return tile.name == 'wall'; // specific opaque tile 29 | // return ['wall', 'column', 'door'].indexOf(tile.name) !== -1; // specific opaque tile list 30 | // return tile.name && tile.name.indexOf('OPAQUE') !== -1; // opaque tile flag in name 31 | return true; // all tiles are opaque 32 | }, 33 | }; 34 | 35 | // track whether opaque 36 | var opaque = false; 37 | after('movePlayer', function () { 38 | // check for changes 39 | var player = bitsy.player(); 40 | var tile = bitsy.tile[bitsy.getTile(player.x, player.y)]; 41 | if (!tile) { 42 | opaque = false; 43 | return; 44 | } 45 | opaque = hackOptions.tileIsOpaque(tile); 46 | }); 47 | 48 | // prevent player from drawing on top of opaque tiles 49 | var room; 50 | before('drawRoom', function () { 51 | var player = bitsy.player(); 52 | room = player.room; 53 | player.room = opaque ? null : room; 54 | }); 55 | after('drawRoom', function () { 56 | bitsy.player().room = room; 57 | }); 58 | 59 | // draw player underneath opaque tile 60 | inject(/(\/\/draw tiles)/, 'drawTile(getSpriteImage(player(), getRoomPal(room.id), frameIndex), player().x, player().y, context);\n$1'); 61 | -------------------------------------------------------------------------------- /src/long dialog.js: -------------------------------------------------------------------------------- 1 | /** 2 | 📜 3 | @file long dialog 4 | @summary put more words onscreen 5 | @license MIT 6 | @version auto 7 | @requires 7.0 8 | @author Sean S. LeBlanc 9 | 10 | @description 11 | Makes the dialog box variable in height, allowing it to expand as needed. 12 | 13 | Minimum and maximum size are configurable. 14 | Cheat sheet: 15 | 2: bitsy default 16 | 8: reaches just below the halfway mark 17 | 16: roughly the max of the original bitsy margins 18 | 19: max before cutting off text 19 | 20 | Note: this hack also includes the paragraph break hack 21 | A common pattern in bitsy is using intentional whitespace to force new dialog pages, 22 | but the long dialog hack makes that look awkward since the text box expands. 23 | The paragraph break hack lets you get around this by using a (p) tag to immediately end the current page. 24 | 25 | HOW TO USE: 26 | 1. Copy-paste this script into a new script tag after the Bitsy source code. 27 | 2. edit hackOptions below as needed 28 | */ 29 | import { 30 | inject, 31 | } from './helpers/kitsy-script-toolkit'; 32 | import './paragraph-break'; 33 | 34 | export var hackOptions = { 35 | minRows: 2, 36 | maxRows: 4, 37 | }; 38 | 39 | // override textbox height 40 | inject(/textboxInfo\.height = .+;/, 41 | `Object.defineProperty(textboxInfo, 'height', { 42 | get() { return textboxInfo.padding_vert + (textboxInfo.padding_vert + relativeFontHeight()) * Math.max(${hackOptions.minRows}, dialogBuffer.CurPage().indexOf(dialogBuffer.CurRow())+Math.sign(dialogBuffer.CurCharCount())) + textboxInfo.arrow_height; } 43 | })`); 44 | // prevent textbox from caching 45 | inject(/(if\(textboxInfo\.img == null\))/, '// $1'); 46 | // rewrite hard-coded row limit 47 | inject(/(else if \(curRowIndex )== 0/g, '$1< ' + hackOptions.maxRows + ' - 1'); 48 | inject(/(if \(lastPage\.length) <= 1/, '$1 < ' + hackOptions.maxRows); 49 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb-base", 3 | "env": { 4 | "browser": true 5 | }, 6 | "parserOptions": { 7 | "ecmaVersion": 2017, 8 | "sourceType": "module", 9 | "ecmaFeatures": { 10 | "modules": true 11 | } 12 | }, 13 | "rules": { 14 | "max-len": "off", // just apply common-sense 15 | "import/no-unresolved": "off", // `bitsy` can't be resolved 16 | "no-param-reassign": "off", // necessary for most hacks 17 | 18 | // for dev accessibility 19 | // e.g. helpers from body called in hackOptions, 20 | // var declarations co-located with relevant functions, 21 | // etc. 22 | "no-console": "off", 23 | "no-unused-vars": ["error", { 24 | "vars": "all", 25 | "args": "none" 26 | }], 27 | "vars-on-top": "off", 28 | "no-use-before-define": "off", 29 | 30 | // tabs instead of spaces 31 | "no-tabs": "off", 32 | "indent": ["error", "tab"], 33 | 34 | // don't prefer fancy stuff to account for lack of babel 35 | "import/prefer-default-export": "off", 36 | "import/no-default-export": "error", 37 | "import/no-mutable-exports": "off", 38 | "prefer-rest-params": "off", 39 | "no-var": "off", 40 | "prefer-arrow-callback": "off", 41 | "func-names": "off", 42 | "prefer-destructuring": "off", 43 | "prefer-spread": "off", 44 | "prefer-object-spread": "off", 45 | "prefer-template": "off", 46 | "object-shorthand": "off", 47 | 48 | // i just like using these 🤷‍♀️ 49 | "no-multi-assign": "off", 50 | "no-plusplus": "off", 51 | "no-continue": "off", 52 | }, 53 | "overrides": [{ 54 | "files": ["**/*.test.js", "**/*/test/**/*.js"], 55 | "env": { 56 | "jest": true, 57 | "es6": true 58 | }, 59 | "parserOptions": { 60 | "ecmaVersion": 9, 61 | "sourceType": "module" 62 | }, 63 | "rules": { 64 | "import/no-extraneous-dependencies": ["error", {"devDependencies": true}] 65 | } 66 | }] 67 | } 68 | -------------------------------------------------------------------------------- /src/dynamic background.js: -------------------------------------------------------------------------------- 1 | /** 2 | 🖼 3 | @file dynamic background 4 | @summary HTML background matching bitsy background 5 | @license MIT 6 | @version auto 7 | @author Sean S. LeBlanc 8 | 9 | @description 10 | Updates the background of the html body to match the background colour of the bitsy palette. 11 | 12 | HOW TO USE: 13 | Copy-paste this script into a script tag after the bitsy source 14 | */ 15 | import bitsy from 'bitsy'; 16 | import { after } from './helpers/kitsy-script-toolkit'; 17 | import { getRoom } from './helpers/utils'; 18 | 19 | export var hackOptions = { 20 | // which palette colour to use for the background 21 | // 0 = background 22 | // 1 = tile 23 | // 2 = sprite 24 | default: 0, 25 | // entries here will override the default for the given room 26 | byRoom: { 27 | // examples: 28 | // 0: 2 29 | // 'my room': 1 30 | }, 31 | }; 32 | 33 | // helper function which detects when the palette has changed, 34 | // and updates the background to match 35 | function updateBg() { 36 | // get the palette colour 37 | var c = hackOptions.byRoom[bitsy.curRoom]; 38 | if (c === undefined) { 39 | c = hackOptions.default; 40 | } 41 | 42 | // if the palette changed, update background 43 | var bg = 'rgb(' + bitsy.getPal(bitsy.curPal())[c].join(',') + ')'; 44 | if (document.body.style.background !== bg) { 45 | document.body.style.background = bg; 46 | } 47 | } 48 | 49 | // expand the map to include ids of rooms listed by name 50 | after('load_game', function () { 51 | var room; 52 | Object.keys(hackOptions.byRoom).forEach(function (i) { 53 | room = getRoom(i); 54 | if (room) { 55 | hackOptions.byRoom[room.id] = hackOptions.byRoom[i]; 56 | } 57 | }); 58 | }); 59 | 60 | // wrap every function which involves changing the palette 61 | after('moveSprites', updateBg); 62 | after('movePlayer', updateBg); 63 | after('parseWorld', updateBg); 64 | after('movePlayerThroughExit', updateBg); 65 | -------------------------------------------------------------------------------- /src/paragraph-break.js: -------------------------------------------------------------------------------- 1 | /** 2 | 📃 3 | @file paragraph-break 4 | @summary Adds paragraph breaks to the dialogue parser 5 | @license WTFPL (do WTF you want) 6 | @version auto 7 | @requires Bitsy Version: 5.0, 5.1 8 | @author Sean S. LeBlanc, David Mowatt 9 | 10 | @description 11 | Adds a (p) tag to the dialogue parser that forces the following text to 12 | start on a fresh dialogue screen, eliminating the need to spend hours testing 13 | line lengths or adding multiple line breaks that then have to be reviewed 14 | when you make edits or change the font size. 15 | 16 | Note: Bitsy has a built-in implementation of paragraph-break as of 7.0; 17 | before using this, you may want to check if it fulfills your needs. 18 | 19 | Usage: (p) 20 | 21 | Example: I am a cat(p)and my dialogue contains multitudes 22 | 23 | HOW TO USE: 24 | 1. Copy-paste this script into a new script tag after the Bitsy source code. 25 | It should appear *before* any other mods that handle loading your game 26 | data so it executes *after* them (last-in first-out). 27 | 28 | NOTE: This uses parentheses "()" instead of curly braces "{}" around function 29 | calls because the Bitsy editor's fancy dialog window strips unrecognized 30 | curly-brace functions from dialog text. To keep from losing data, write 31 | these function calls with parentheses like the examples above. 32 | 33 | For full editor integration, you'd *probably* also need to paste this 34 | code at the end of the editor's `bitsy.js` file. Untested. 35 | */ 36 | 37 | import { 38 | addDialogTag, 39 | } from './helpers/kitsy-script-toolkit'; 40 | import './helpers/addParagraphBreak'; 41 | 42 | // Adds the actual dialogue tag. No deferred version is required. 43 | addDialogTag('p', function (environment, parameters, onReturn) { 44 | environment.GetDialogBuffer().AddParagraphBreak(); 45 | onReturn(null); 46 | }); 47 | // End of (p) paragraph break mod 48 | -------------------------------------------------------------------------------- /src/javascript dialog.js: -------------------------------------------------------------------------------- 1 | /** 2 | ☕ 3 | @file javascript dialog 4 | @summary execute arbitrary javascript from dialog 5 | @license MIT 6 | @version auto 7 | @requires Bitsy Version: 4.5, 4.6 8 | @author Sean S. LeBlanc 9 | 10 | @description 11 | Lets you execute arbitrary JavaScript from dialog (including inside conditionals). 12 | If you're familiar with the Bitsy source, this will let you write one-shot hacks 13 | for a wide variety of situations. 14 | 15 | Usage: 16 | (js "") 17 | (jsNow "") 18 | 19 | Examples: 20 | move a sprite: 21 | (js "sprite['a'].x = 10;") 22 | edit palette colour: 23 | (js "getPal(curPal())[0] = [255,0,0];renderImages();") 24 | place an item next to player: 25 | (js "room[curRoom].items.push({id:'0',x:player().x+1,y:player().y});") 26 | verbose facsimile of exit-from-dialog: 27 | (js "var _onExitDialog=onExitDialog;onExitDialog=function(){player().room=curRoom='3';_onExitDialog.apply(this,arguments);onExitDialog=_onExitDialog;};") 28 | 29 | HOW TO USE: 30 | 1. Copy-paste into a script tag after the bitsy source 31 | 2. Add (js "") to your dialog as needed 32 | 33 | NOTE: This uses parentheses "()" instead of curly braces "{}" around function 34 | calls because the Bitsy editor's fancy dialog window strips unrecognized 35 | curly-brace functions from dialog text. To keep from losing data, write 36 | these function calls with parentheses like the examples above. 37 | 38 | For full editor integration, you'd *probably* also need to paste this 39 | code at the end of the editor's `bitsy.js` file. Untested. 40 | */ 41 | 42 | import { 43 | addDualDialogTag, 44 | } from './helpers/kitsy-script-toolkit'; 45 | 46 | // eslint-disable-next-line no-eval 47 | var indirectEval = eval; 48 | 49 | function executeJs(environment, parameters) { 50 | indirectEval(parameters[0]); 51 | } 52 | 53 | addDualDialogTag('js', executeJs); 54 | -------------------------------------------------------------------------------- /src/twine-bitsy-comms/SugarCube-v1.js: -------------------------------------------------------------------------------- 1 | /** 2 | @summary twine-bitsy-comms SugarCube v1 Macro 3 | @license MIT 4 | @version 1.0.0 5 | @author Sean S. LeBlanc 6 | 7 | @description 8 | example: <>./my bitsy.html<> 9 | 10 | the bitsy game will render as an iframe with the class "bitsy" 11 | inside of the passage that includes it; 12 | you can use this to customize its CSS 13 | (e.g. `border: none;`, `image-rendering: pixelated;`) 14 | */ 15 | var sendMessage; 16 | 17 | // setup listeners 18 | function handleBitsyMessage(event) { 19 | var type = event.data.type; 20 | var data = event.data.data; 21 | switch (type) { 22 | case 'start': 23 | sendMessage({ 24 | type: 'variables', 25 | data: state.active.variables, 26 | }); 27 | break; 28 | case 'play': 29 | state.display(data); 30 | break; 31 | case 'back': 32 | Engine.goBack(); 33 | break; 34 | case 'variable': 35 | state.active.variables[data.name] = data.value; 36 | break; 37 | case 'eval': 38 | console.warn('The "eval" command is commented out by default since it\'s easy to abuse. Uncomment it in your javascript if you want to use it.'); 39 | // eval(data); 40 | break; 41 | default: 42 | console.warn('Unhandled message from Bitsy:', type, data); 43 | break; 44 | } 45 | } 46 | window.addEventListener('message', handleBitsyMessage, false); 47 | macros.add('bitsy', { 48 | tags: null, 49 | handler: function () { 50 | // setup iframe 51 | var iframe = document.createElement('iframe'); 52 | iframe.width = 512; 53 | iframe.height = 512; 54 | iframe.src = this.payload[0].contents; 55 | iframe.className = 'bitsy'; 56 | 57 | sendMessage = function (message) { 58 | iframe.contentWindow.postMessage(message, '*'); 59 | }; 60 | 61 | // make sure the iframe keeps focus 62 | // so that bitsy can capture key events 63 | setTimeout(function () { 64 | iframe.focus(); 65 | iframe.onblur = function () { 66 | iframe.focus(); 67 | }; 68 | }); 69 | 70 | // output 71 | this.output.appendChild(iframe); 72 | }, 73 | }); 74 | -------------------------------------------------------------------------------- /src/twine-bitsy-comms/Snowman.js: -------------------------------------------------------------------------------- 1 | /** 2 | @summary twine-bitsy-comms Snowman 3 | @license MIT 4 | @version 1.0.0 5 | @author Sean S.LeBlanc 6 | 7 | @description 8 | example: <% story.bitsy('./ my bitsy.html') %> 9 | 10 | the bitsy game will render as an iframe with the class "bitsy" 11 | inside of the passage that includes it; 12 | you can use this to customize its CSS 13 | (e.g. `border: none;`, `image-rendering: pixelated;`) 14 | */ 15 | story.bitsy = function (src) { 16 | // setup iframe 17 | var iframe = document.createElement('iframe'); 18 | iframe.width = 512; 19 | iframe.height = 512; 20 | iframe.src = src; 21 | iframe.className = 'bitsy'; 22 | 23 | // setup listeners 24 | function handleBitsyMessage(event) { 25 | var type = event.data.type; 26 | var data = event.data.data; 27 | switch (type) { 28 | case 'start': 29 | iframe.contentWindow.postMessage({ 30 | type: 'variables', 31 | data: story.state, 32 | }, '*'); 33 | break; 34 | case 'play': 35 | story.show(data); 36 | break; 37 | case 'back': 38 | window.history.back(); 39 | break; 40 | case 'variable': 41 | story.state[data.name] = data.value; 42 | break; 43 | case 'eval': 44 | console.warn('The "eval" command is commented out by default since it\'s easy to abuse. Uncomment it in your javascript if you want to use it.'); 45 | // eval(data); 46 | break; 47 | default: 48 | console.warn('Unhandled message from Bitsy:', type, data); 49 | break; 50 | } 51 | } 52 | window.addEventListener('message', handleBitsyMessage, false); 53 | $(document).one('showpassage', function () { 54 | window.removeEventListener('message', handleBitsyMessage, false); 55 | }); 56 | 57 | $(window).one('showpassage:after', function () { 58 | document.getElementById('passage').appendChild(iframe); 59 | // make sure the iframe keeps focus 60 | // so that bitsy can capture key events 61 | setTimeout(function () { 62 | iframe.focus(); 63 | iframe.onblur = function () { 64 | iframe.focus(); 65 | }; 66 | }); 67 | }); 68 | }; 69 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bitsy/hecks", 3 | "description": "a collection of re-usable scripts for bitsy game maker", 4 | "keywords": [ 5 | "bitsy", 6 | "hacks" 7 | ], 8 | "main": "index.mjs", 9 | "version": "13.4.2", 10 | "scripts": { 11 | "build": "rollup -c", 12 | "test": "jest --runInBand", 13 | "lint": "eslint ./src/**/*.js", 14 | "postversion": "npm run build" 15 | }, 16 | "author": "Sean S. LeBlanc ", 17 | "license": "MIT (individual hacks have their own license specified)", 18 | "repository": { 19 | "type": "git", 20 | "url": "git://github.com/seleb/bitsy-hacks.git" 21 | }, 22 | "dependencies": { 23 | "@seansleblanc/pink-trombone": "^1.0.1", 24 | "array-flat-polyfill": "^1.0.1", 25 | "babylonjs": "^4.1.0", 26 | "input-gamepads.js": "^2.0.2", 27 | "omggif": "^1.0.10", 28 | "rollup": "^2.26.8", 29 | "rollup-plugin-commonjs": "^10.1.0", 30 | "rollup-plugin-node-resolve": "^5.2.0", 31 | "tracery-grammar": "^2.7.4", 32 | "webglazy": "^3.0.4" 33 | }, 34 | "devDependencies": { 35 | "@babel/plugin-transform-runtime": "^7.11.0", 36 | "@babel/preset-env": "^7.11.0", 37 | "@babel/runtime": "^7.11.2", 38 | "doctrine": "^3.0.0", 39 | "eslint": "^7.7.0", 40 | "eslint-config-airbnb-base": "^14.2.0", 41 | "eslint-plugin-import": "^2.22.0", 42 | "jest": "^26.4.2", 43 | "jest-image-snapshot": "^4.2.0", 44 | "puppeteer": "^4.0.1" 45 | }, 46 | "jest": { 47 | "testEnvironment": "node", 48 | "setupFilesAfterEnv": [ 49 | "/src/test/setupTests.js" 50 | ] 51 | }, 52 | "release": { 53 | "plugins": [ 54 | "@semantic-release/commit-analyzer", 55 | "@semantic-release/release-notes-generator", 56 | "@semantic-release/changelog", 57 | "@semantic-release/npm", 58 | "@semantic-release/github", 59 | [ 60 | "@semantic-release/git", 61 | { 62 | "assets": [ 63 | "dist/*.js", 64 | "README.md", 65 | "CHANGELOG.md", 66 | "package.json", 67 | "package-lock.json" 68 | ] 69 | } 70 | ] 71 | ] 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/twine-bitsy-comms/Sugarcane-Responsive.js: -------------------------------------------------------------------------------- 1 | /** 2 | @summary twine-bitsy-comms Sugarcane/Responsive macro 3 | @license MIT 4 | @version 1.0.0 5 | @author Sean S. LeBlanc 6 | 7 | @description 8 | example: <> 9 | 10 | the bitsy game will render as an iframe with the class "bitsy" 11 | inside of the passage that includes it; 12 | you can use this to customize its CSS 13 | (e.g. `border: none;`, `image-rendering: pixelated;`) 14 | */ 15 | macros.bitsy = { 16 | handler: function () { 17 | // setup iframe 18 | var iframe = document.createElement('iframe'); 19 | iframe.width = 512; 20 | iframe.height = 512; 21 | iframe.src = arguments[2]; 22 | iframe.className = 'bitsy'; 23 | 24 | // setup listeners 25 | function handleBitsyMessage(event) { 26 | var type = event.data.type; 27 | var data = event.data.data; 28 | switch (type) { 29 | case 'start': 30 | iframe.contentWindow.postMessage({ 31 | type: 'variables', 32 | data: state.history[0].variables, 33 | }, '*'); 34 | break; 35 | case 'play': 36 | state.display(data); 37 | break; 38 | case 'back': 39 | window.history.back(); 40 | break; 41 | case 'variable': 42 | state.history[0].variables[data.name] = data.value; 43 | break; 44 | case 'eval': 45 | console.warn('The "eval" command is commented out by default since it\'s easy to abuse. Uncomment it in your javascript if you want to use it.'); 46 | // eval(data); 47 | break; 48 | default: 49 | console.warn('Unhandled message from Bitsy:', type, data); 50 | break; 51 | } 52 | } 53 | window.addEventListener('message', handleBitsyMessage, false); 54 | prerender.bitsy = function () { 55 | window.removeEventListener('message', handleBitsyMessage, false); 56 | }; 57 | 58 | // make sure the iframe keeps focus 59 | // so that bitsy can capture key events 60 | setTimeout(function () { 61 | iframe.focus(); 62 | iframe.onblur = function () { 63 | iframe.focus(); 64 | }; 65 | }); 66 | 67 | // output 68 | arguments[3].output.appendChild(iframe); 69 | }, 70 | }; 71 | -------------------------------------------------------------------------------- /src/twine-bitsy-comms/SugarCube-v2.js: -------------------------------------------------------------------------------- 1 | /** 2 | @summary twine-bitsy-comms SugarCube v2 Macro 3 | @license MIT 4 | @version 1.0.0 5 | @author Sean S. LeBlanc 6 | 7 | @description 8 | example: <>./my bitsy.html<> 9 | 10 | the bitsy game will render as an iframe with the class "bitsy" 11 | inside of the passage that includes it; 12 | you can use this to customize its CSS 13 | (e.g. `border: none;`, `image-rendering: pixelated;`) 14 | */ 15 | Macro.add('bitsy', { 16 | tags: null, 17 | handler: function () { 18 | // setup iframe 19 | var iframe = document.createElement('iframe'); 20 | iframe.width = 512; 21 | iframe.height = 512; 22 | iframe.src = this.payload[0].contents; 23 | iframe.className = 'bitsy'; 24 | 25 | // setup listeners 26 | function handleBitsyMessage(event) { 27 | var type = event.data.type; 28 | var data = event.data.data; 29 | switch (type) { 30 | case 'start': 31 | iframe.contentWindow.postMessage({ 32 | type: 'variables', 33 | data: State.variables, 34 | }, '*'); 35 | break; 36 | case 'play': 37 | Engine.play(data); 38 | break; 39 | case 'back': 40 | Engine.backward(); 41 | break; 42 | case 'variable': 43 | State.variables[data.name] = data.value; 44 | break; 45 | case 'eval': 46 | console.warn('The "eval" command is commented out by default since it\'s easy to abuse. Uncomment it in your javascript if you want to use it.'); 47 | // eval(data); 48 | break; 49 | default: 50 | console.warn('Unhandled message from Bitsy:', type, data); 51 | break; 52 | } 53 | } 54 | window.addEventListener('message', handleBitsyMessage, false); 55 | $(document).one(':passagestart', function () { 56 | window.removeEventListener('message', handleBitsyMessage, false); 57 | }); 58 | 59 | // make sure the iframe keeps focus 60 | // so that bitsy can capture key events 61 | setTimeout(function () { 62 | iframe.focus(); 63 | iframe.onblur = function () { 64 | iframe.focus(); 65 | }; 66 | }); 67 | 68 | // output 69 | this.output.appendChild(iframe); 70 | }, 71 | }); 72 | -------------------------------------------------------------------------------- /src/twine-bitsy-comms/Jonah.js: -------------------------------------------------------------------------------- 1 | /** 2 | @summary twine-bitsy-comms Jonah macro 3 | @license MIT 4 | @version 1.0.0 5 | @author Sean S. LeBlanc 6 | 7 | @description 8 | example: <> 9 | 10 | the bitsy game will render as an iframe with the class "bitsy" 11 | inside of the passage that includes it; 12 | you can use this to customize its CSS 13 | (e.g. `border: none;`, `image-rendering: pixelated;`) 14 | */ 15 | macros.bitsy = { 16 | handler: function () { 17 | // setup iframe 18 | var iframe = document.createElement('iframe'); 19 | iframe.width = 512; 20 | iframe.height = 512; 21 | iframe.src = arguments[2]; 22 | iframe.className = 'bitsy'; 23 | 24 | // setup listeners 25 | function handleBitsyMessage(event) { 26 | var type = event.data.type; 27 | var data = event.data.data; 28 | switch (type) { 29 | case 'start': 30 | iframe.contentWindow.postMessage({ 31 | type: 'variables', 32 | data: state.history[0].variables, 33 | }, '*'); 34 | break; 35 | case 'play': 36 | state.display(data); 37 | break; 38 | case 'back': 39 | state.rewindTo(document.getElementById('passages').lastChild.previousSibling); 40 | break; 41 | case 'variable': 42 | state.history[0].variables[data.name] = data.value; 43 | break; 44 | case 'eval': 45 | console.warn('The "eval" command is commented out by default since it\'s easy to abuse. Uncomment it in your javascript if you want to use it.'); 46 | // eval(data); 47 | break; 48 | default: 49 | console.warn('Unhandled message from Bitsy:', type, data); 50 | break; 51 | } 52 | } 53 | window.addEventListener('message', handleBitsyMessage, false); 54 | prerender.bitsy = function () { 55 | iframe.remove(); 56 | window.removeEventListener('message', handleBitsyMessage, false); 57 | }; 58 | 59 | // make sure the iframe keeps focus 60 | // so that bitsy can capture key events 61 | setTimeout(function () { 62 | iframe.focus(); 63 | iframe.onblur = function () { 64 | iframe.focus(); 65 | }; 66 | }); 67 | 68 | // output 69 | arguments[3].output.appendChild(iframe); 70 | }, 71 | }; 72 | -------------------------------------------------------------------------------- /src/avatar by room.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | start, 3 | press, 4 | end, 5 | snapshot, 6 | } from './test/bitsy'; 7 | 8 | test('strike', async () => { 9 | await start({ 10 | gamedata: ` 11 | 12 | # BITSY VERSION 7.0 13 | 14 | ! ROOM_FORMAT 1 15 | 16 | PAL 0 17 | NAME blueprint 18 | 0,82,204 19 | 128,159,255 20 | 255,255,255 21 | 22 | ROOM 0 23 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 24 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 25 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 26 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 27 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 28 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 29 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 30 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 31 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 32 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 33 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 34 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 35 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 36 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 37 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 38 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 39 | EXT 5,4 1 5,4 40 | PAL 0 41 | 42 | ROOM 1 43 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 44 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 45 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 46 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 47 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 48 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 49 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 50 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 51 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 52 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 53 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 54 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 55 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 56 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 57 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 58 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 59 | PAL 0 60 | 61 | SPR A 62 | 00011000 63 | 00011000 64 | 00011000 65 | 00111100 66 | 01111110 67 | 10111101 68 | 00100100 69 | 00100100 70 | POS 0 4,4 71 | 72 | SPR a 73 | 00000000 74 | 00000000 75 | 01010001 76 | 01110001 77 | 01110010 78 | 01111100 79 | 00111100 80 | 00100100 81 | 82 | VAR a 83 | 42 84 | `, 85 | hacks: [['avatar by room', { 86 | avatarByRoom: { 87 | 1: 'a', 88 | }, 89 | }]], 90 | }); 91 | await snapshot(); 92 | await press('ArrowRight'); // walk through exit 93 | await snapshot(); 94 | await press('ArrowRight'); // take an extra step 95 | await snapshot(); 96 | await end(); 97 | }); 98 | -------------------------------------------------------------------------------- /src/twine-bitsy-comms/Harlowe.js: -------------------------------------------------------------------------------- 1 | /** 2 | @summary twine-bitsy-comms Harlowe script 3 | @license MIT 4 | @version 1.0.1 5 | @author Sean S. LeBlanc 6 | 7 | @description 8 | example (inside of script tag): window.bitsy('./my bitsy.html'); 9 | 10 | the bitsy game will render as an iframe with the class "bitsy" 11 | inside of the passage that includes it; 12 | you can use this to customize its CSS 13 | (e.g. `border: none;`, `image-rendering: pixelated;`) 14 | */ 15 | var sendMessage; 16 | 17 | // setup listeners 18 | function handleBitsyMessage(event) { 19 | var type = event.data.type; 20 | var data = event.data.data; 21 | switch (type) { 22 | case 'start': 23 | var variables = {}; 24 | Object.entries(State.variables).forEach(function (entry) { 25 | if (!entry[0].startsWith('TwineScript_')) { 26 | variables[entry[0]] = entry[1]; 27 | } 28 | }); 29 | sendMessage({ 30 | type: 'variables', 31 | data: variables, 32 | }); 33 | break; 34 | case 'play': 35 | Engine.goToPassage(data); 36 | break; 37 | case 'back': 38 | Engine.goBack(); 39 | break; 40 | case 'variable': 41 | State.variables[data.name] = data.value; 42 | break; 43 | case 'eval': 44 | console.warn('The "eval" command is commented out by default since it\'s easy to abuse. Uncomment it in your javascript if you want to use it.'); 45 | // eval(data); 46 | break; 47 | default: 48 | console.warn('Unhandled message from Bitsy:', type, data); 49 | break; 50 | } 51 | } 52 | window.addEventListener('message', handleBitsyMessage, false); 53 | window.bitsy = function (src) { 54 | // setup iframe 55 | var iframe = document.createElement('iframe'); 56 | iframe.width = 512; 57 | iframe.height = 512; 58 | iframe.src = src; 59 | iframe.className = 'bitsy'; 60 | 61 | sendMessage = function (message) { 62 | iframe.contentWindow.postMessage(message, '*'); 63 | }; 64 | 65 | // make sure the iframe keeps focus 66 | // so that bitsy can capture key events 67 | setTimeout(function () { 68 | iframe.focus(); 69 | iframe.onblur = function () { 70 | iframe.focus(); 71 | }; 72 | }); 73 | 74 | // output 75 | document.querySelector(Selectors.passage).appendChild(iframe); 76 | }; 77 | -------------------------------------------------------------------------------- /src/gamepad input.js: -------------------------------------------------------------------------------- 1 | /** 2 | 🎮 3 | @file gamepad input 4 | @summary HTML5 gamepad support 5 | @license MIT 6 | @version auto 7 | @requires Bitsy Version: 5.1 8 | @author Sean S. LeBlanc 9 | 10 | @description 11 | Adds support for gamepad input. 12 | 13 | Directional input is mapped to the left and right analog sticks, the dpad, and the face buttons (e.g. ABXY). 14 | The same hold-to-move logic used for keyboard input is shared with the gamepad input. 15 | 16 | HOW TO USE: 17 | Copy-paste this script into a script tag after the bitsy source 18 | */ 19 | import bitsy from 'bitsy'; 20 | import gamepads from 'input-gamepads.js'; 21 | import { 22 | before, 23 | after, 24 | } from './helpers/kitsy-script-toolkit'; 25 | 26 | before('startExportedGame', gamepads.init.bind(gamepads)); 27 | var empty = function () {}; 28 | 29 | var move = function (dpad, face, axis, axis2, axispast, axisdir, key) { 30 | // keydown 31 | if ( 32 | gamepads.isJustDown(dpad) 33 | || gamepads.isJustDown(face) 34 | || gamepads.axisJustPast(axis, axispast, axisdir) 35 | || ( 36 | bitsy.playerHoldToMoveTimer <= 0 && ( 37 | gamepads.isDown(dpad) 38 | || gamepads.isDown(face) 39 | || gamepads.axisPast(axis, axispast, axisdir) 40 | ) 41 | ) 42 | ) { 43 | bitsy.curPlayerDirection = bitsy.Direction.None; 44 | bitsy.input.onkeydown({ 45 | keyCode: key, 46 | preventDefault: empty, 47 | }); 48 | } 49 | 50 | // keyup 51 | if ( 52 | gamepads.isJustUp(dpad) 53 | || gamepads.isJustUp(face) 54 | || gamepads.axisJustPast(axis, axispast, -axisdir) 55 | ) { 56 | bitsy.input.onkeyup({ 57 | keyCode: key, 58 | preventDefault: empty, 59 | }); 60 | } 61 | }; 62 | 63 | before('update', function () { 64 | move(gamepads.DPAD_LEFT, gamepads.X, gamepads.LSTICK_H, gamepads.RSTICK_H, -0.5, -1, bitsy.key.left); 65 | move(gamepads.DPAD_RIGHT, gamepads.B, gamepads.LSTICK_H, gamepads.RSTICK_H, 0.5, 1, bitsy.key.right); 66 | move(gamepads.DPAD_UP, gamepads.Y, gamepads.LSTICK_V, gamepads.RSTICK_V, -0.5, -1, bitsy.key.up); 67 | move(gamepads.DPAD_DOWN, gamepads.A, gamepads.LSTICK_V, gamepads.RSTICK_V, 0.5, 1, bitsy.key.down); 68 | }); 69 | after('update', function () { 70 | gamepads.update(); 71 | }); 72 | -------------------------------------------------------------------------------- /src/helpers/edit image at runtime.js: -------------------------------------------------------------------------------- 1 | /** 2 | @file edit image at runtime 3 | @summary API for updating image data at runtime. 4 | @author Sean S. LeBlanc 5 | @description 6 | Adds API for updating sprite, tile, and item data at runtime. 7 | 8 | Individual frames of image data in bitsy are 8x8 1-bit 2D arrays in yx order 9 | e.g. the default player is: 10 | [ 11 | [0,0,0,1,1,0,0,0], 12 | [0,0,0,1,1,0,0,0], 13 | [0,0,0,1,1,0,0,0], 14 | [0,0,1,1,1,1,0,0], 15 | [0,1,1,1,1,1,1,0], 16 | [1,0,1,1,1,1,0,1], 17 | [0,0,1,0,0,1,0,0], 18 | [0,0,1,0,0,1,0,0] 19 | ] 20 | */ 21 | import bitsy from 'bitsy'; 22 | import { 23 | getImage, 24 | } from './utils'; 25 | 26 | export { 27 | getImage, 28 | }; 29 | 30 | /* 31 | Args: 32 | id: string id or name 33 | frame: animation frame (0 or 1) 34 | map: map of images (e.g. `sprite`, `tile`, `item`) 35 | 36 | Returns: a single frame of a image data 37 | */ 38 | export function getImageData(id, frame, map) { 39 | return bitsy.renderer.GetImageSource(getImage(id, map).drw)[frame]; 40 | } 41 | 42 | export function getSpriteData(id, frame) { 43 | return getImageData(id, frame, bitsy.sprite); 44 | } 45 | 46 | export function getTileData(id, frame) { 47 | return getImageData(id, frame, bitsy.tile); 48 | } 49 | 50 | export function getItemData(id, frame) { 51 | return getImageData(id, frame, bitsy.item); 52 | } 53 | 54 | /* 55 | Updates a single frame of image data 56 | 57 | Args: 58 | id: string id or name 59 | frame: animation frame (0 or 1) 60 | map: map of images (e.g. `sprite`, `tile`, `item`) 61 | newData: new data to write to the image data 62 | */ 63 | export function setImageData(id, frame, map, newData) { 64 | var drawing = getImage(id, map); 65 | var drw = drawing.drw; 66 | var img = bitsy.renderer.GetImageSource(drw).slice(); 67 | img[frame] = newData; 68 | bitsy.renderer.SetImageSource(drw, img); 69 | } 70 | 71 | export function setSpriteData(id, frame, newData) { 72 | setImageData(id, frame, bitsy.sprite, newData); 73 | } 74 | 75 | export function setTileData(id, frame, newData) { 76 | setImageData(id, frame, bitsy.tile, newData); 77 | } 78 | 79 | export function setItemData(id, frame, newData) { 80 | setImageData(id, frame, bitsy.item, newData); 81 | } 82 | -------------------------------------------------------------------------------- /src/dialog choices.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | start, 3 | walkToCat, 4 | press, 5 | end, 6 | snapshot, 7 | } from './test/bitsy'; 8 | 9 | test('dialog choices', async () => { 10 | await start({ 11 | catDialog: `""" 12 | {choice 13 | - text after 14 | test a 15 | - no text after 16 | } 17 | """`, 18 | hacks: ['dialog choices'], 19 | }); 20 | await walkToCat(); 21 | await press('ArrowRight'); // talk to cat 22 | await press('Enter'); // complete dialog page 23 | await snapshot(); 24 | await press('Enter'); // select first choice 25 | await press('Enter'); // complete dialog page 26 | await snapshot(); 27 | await press('Enter'); // end dialog 28 | await press('ArrowRight'); // talk to cat again 29 | await press('Enter'); // complete dialog page 30 | await press('ArrowDown'); // highlight second choice 31 | await snapshot(); 32 | await press('Enter'); // select second choice 33 | await snapshot(); 34 | await end(); 35 | }); 36 | 37 | test('dialog in front', async () => { 38 | await start({ 39 | catDialog: `""" 40 | I'm a cat{choice 41 | - text after 42 | test a 43 | - no text after 44 | } 45 | """`, 46 | hacks: ['dialog choices'], 47 | }); 48 | await walkToCat(); 49 | await press('ArrowRight'); // talk to cat 50 | await press('Enter'); // complete dialog page 51 | await press('Enter'); // next page 52 | await press('Enter'); // complete dialog page 53 | await snapshot(); 54 | await press('Enter'); // select first choice 55 | await press('Enter'); // complete dialog page 56 | await snapshot(); 57 | await end(); 58 | }); 59 | 60 | test('with long dialog', async () => { 61 | await start({ 62 | catDialog: `""" 63 | {choice 64 | - text after 65 | test a 66 | - no text after 67 | - text after 2 68 | test b 69 | } 70 | """`, 71 | hacks: ['long dialog', 'dialog choices'], 72 | }); 73 | await walkToCat(); 74 | await press('ArrowRight'); // talk to cat 75 | await press('Enter'); // complete dialog page 76 | await press('ArrowDown'); // highlight second choice 77 | await press('ArrowDown'); // highlight third choice 78 | await snapshot(); 79 | await press('Enter'); // select third choice 80 | await press('Enter'); // complete dialog page 81 | await snapshot(); 82 | await end(); 83 | }); 84 | -------------------------------------------------------------------------------- /src/edit dialog from dialog.js: -------------------------------------------------------------------------------- 1 | /** 2 | 📝 3 | @file edit dialog from dialog 4 | @summary edit dialog from dialog (yes really) 5 | @license MIT 6 | @version auto 7 | @requires 7.0 8 | @author Sean S. LeBlanc 9 | 10 | @description 11 | You can use this to edit the dialog of sprites/items through dialog. 12 | 13 | (dialog "map, target, newDialog") 14 | Parameters: 15 | map: Type of image (SPR or ITM) 16 | target: id/name of image to edit 17 | newDialog: new dialog text 18 | 19 | Examples: 20 | (dialog "SPR, a, I am not a cat") 21 | 22 | HOW TO USE: 23 | Copy-paste this script into a new script tag after the Bitsy source code. 24 | 25 | TIPS: 26 | - The player avatar is always a sprite with id "A"; you can edit your gamedata to give them a name for clarity 27 | - You can use the full names or shorthand of image types (e.g. "SPR" and "sprite" will both work) 28 | */ 29 | import bitsy from 'bitsy'; 30 | import { 31 | addDeferredDialogTag, 32 | after, 33 | } from './helpers/kitsy-script-toolkit'; 34 | import { 35 | getImage, 36 | } from './helpers/utils'; 37 | 38 | // map of maps 39 | var maps; 40 | after('load_game', function () { 41 | maps = { 42 | spr: bitsy.sprite, 43 | sprite: bitsy.sprite, 44 | itm: bitsy.item, 45 | item: bitsy.item, 46 | }; 47 | }); 48 | 49 | function editDialog(environment, parameters) { 50 | // parse parameters 51 | var params = parameters[0].split(/,\s?/); 52 | params[0] = (params[0] || '').toLowerCase(); 53 | var mapId = params[0]; 54 | var tgtId = params[1]; 55 | var newDialog = params[2] || ''; 56 | 57 | if (!mapId || !tgtId) { 58 | throw new Error('Image expects three parameters: "map, target, newDialog", but received: "' + params.join(', ') + '"'); 59 | } 60 | 61 | // get objects 62 | var mapObj = maps[mapId]; 63 | if (!mapObj) { 64 | throw new Error('Invalid map "' + mapId + '". Try "SPR" or "ITM" instead.'); 65 | } 66 | var tgtObj = getImage(tgtId, mapObj); 67 | if (!tgtObj) { 68 | throw new Error('Target "' + tgtId + '" was not the id/name of a ' + mapId + '.'); 69 | } 70 | bitsy.dialog[tgtObj.dlg].src = newDialog; 71 | bitsy.scriptInterpreter.Compile(tgtObj.dlg, newDialog); 72 | } 73 | 74 | // hook up the dialog tag 75 | addDeferredDialogTag('dialog', editDialog); 76 | -------------------------------------------------------------------------------- /src/canvas replacement.js: -------------------------------------------------------------------------------- 1 | /** 2 | 😴 3 | @file canvas replacement 4 | @summary WebGLazy bitsy integration (this one's mostly just for me) 5 | @license MIT 6 | @version auto 7 | @author Sean S. LeBlanc 8 | 9 | @description 10 | Replaces bitsy canvas with a responsive WebGL canvas (this one's mostly just for me) 11 | 12 | HOW TO USE: 13 | 1. Copy-paste this script into a script tag after the bitsy source 14 | 2. For finer scaling, edit `var text_scale = 2` and `var scale = 4` in the bitsy source to `var text_scale = 1` and `var scale = 2` 15 | 3. Edit the hackOptions object passed to the `new WebGLazy` call as needed 16 | 17 | The shader used to render the canvas can be overridden via hack options: 18 | e.g. 19 | var hackOptions = { 20 | glazyOptions = { 21 | fragment: ` 22 | // uv-wave fragment shader 23 | precision mediump float; 24 | uniform sampler2D tex0; 25 | uniform sampler2D tex1; 26 | uniform float time; 27 | uniform vec2 resolution; 28 | 29 | void main(){ 30 | vec2 coord = gl_FragCoord.xy; 31 | vec2 uv = coord.xy / resolution.xy; 32 | uv.x += sin(uv.y * 10.0 + time / 200.0) / 60.0; 33 | uv.y += cos(uv.x * 10.0 + time / 200.0) / 60.0; 34 | vec3 col = texture2D(tex0,uv).rgb; 35 | gl_FragColor = vec4(col, 1.0); 36 | } 37 | `, 38 | }, 39 | }; 40 | */ 41 | import WebGLazy from 'webglazy'; 42 | import { after } from './helpers/kitsy-script-toolkit'; 43 | 44 | export var hackOptions = { 45 | glazyOptions: { 46 | background: 'black', 47 | scaleMode: 'MULTIPLES', // use "FIT" if you prefer size to pixel accuracy 48 | allowDownscaling: true, 49 | disableFeedbackTexture: true, // set this to false if you want to use the feedback texture 50 | }, 51 | init: function (glazy) { 52 | // you can set up any custom uniforms you have here if needed 53 | // e.g. glazy.glLocations.myUniform = glazy.gl.getUniformLocation(glazy.shader.program, 'myUniform'); 54 | }, 55 | update: function (glazy) { 56 | // you can update any custom uniforms you have here if needed 57 | // e.g. glazy.gl.uniform1f(glazy.glLocations.myUniform, 0); 58 | }, 59 | }; 60 | 61 | var glazy; 62 | after('startExportedGame', function () { 63 | glazy = new WebGLazy(hackOptions.glazyOptions); 64 | hackOptions.init(glazy); 65 | }); 66 | 67 | after('update', function () { 68 | hackOptions.update(glazy); 69 | }); 70 | -------------------------------------------------------------------------------- /src/permanent items.js: -------------------------------------------------------------------------------- 1 | /** 2 | ⏳ 3 | @file permanent items 4 | @summary prevent some items from being picked up 5 | @license MIT 6 | @version auto 7 | @requires 7.0 8 | @author Sean S. LeBlanc 9 | 10 | @description 11 | Prevents certain items from being picked up, but allows them to be walked over and triggers their dialog. 12 | 13 | HOW TO USE: 14 | 1. Copy-paste this script into a script tag after the bitsy source 15 | 2. Update the `itemIsPermanent` function below to match your needs 16 | */ 17 | import bitsy from 'bitsy'; 18 | import { before } from './helpers/kitsy-script-toolkit'; 19 | 20 | export var hackOptions = { 21 | itemIsPermanent: function (item) { 22 | // return item.name && item.name == 'tea'; // specific permanent item 23 | // return ['tea', 'flower', 'hat'].indexOf(item.name) !== -1; // specific permanent item list 24 | // return item.name && item.name.indexOf('PERMANENT') !== -1; // permanent item flag in name 25 | return true; // all items are permanent 26 | }, 27 | }; 28 | 29 | var room; 30 | var oldItems; 31 | before('movePlayer', function () { 32 | room = bitsy.room[bitsy.curRoom]; 33 | oldItems = room.items.slice(); 34 | }); 35 | before('startItemDialog', function (itemId, dialogCallback) { 36 | // something changed 37 | if (!hackOptions.itemIsPermanent(bitsy.item[itemId])) { 38 | return undefined; 39 | } 40 | room = bitsy.room[bitsy.curRoom]; 41 | oldItems = room.items.slice(); 42 | return [itemId, function () { 43 | var newItems = room.items; 44 | if (newItems.length === oldItems.length) { 45 | return; // nothing changed 46 | } 47 | 48 | // check for changes 49 | for (var i = 0; i < oldItems.length; ++i) { 50 | if (!newItems[i] 51 | || oldItems[i].x !== newItems[i].x 52 | || oldItems[i].y !== newItems[i].y 53 | || oldItems[i].id !== newItems[i].id 54 | ) { 55 | // something changed 56 | if (hackOptions.itemIsPermanent(bitsy.item[oldItems[i].id])) { 57 | // put that back! 58 | newItems.splice(i, 0, oldItems[i]); 59 | } else { 60 | // add an empty entry for now to keep the arrays aligned 61 | newItems.splice(i, 0, null); 62 | } 63 | } 64 | } 65 | // clear out those empty entries 66 | room.items = newItems.filter(function (item) { 67 | return !!item; 68 | }); 69 | 70 | // run the actual callback 71 | if (dialogCallback) { 72 | dialogCallback(); 73 | } 74 | }]; 75 | }); 76 | -------------------------------------------------------------------------------- /src/dialog box transition.js: -------------------------------------------------------------------------------- 1 | /** 2 | 🔁 3 | @file dialog box transition 4 | @summary adds an easing transition animation to display the dialog box text 5 | @license MIT 6 | @version auto 7 | @requires 4.8, 4.9 8 | @author Delacannon 9 | 10 | @description 11 | A hack that adds an easing transition animation to display the dialog box text 12 | 13 | HOW TO USE: 14 | 1. Copy-paste this script into a script tag after the bitsy source. 15 | */ 16 | 17 | import { 18 | inject, 19 | } from './helpers/kitsy-script-toolkit'; 20 | 21 | export var hackOptions = { 22 | easing: 0.025, // easing speed 23 | }; 24 | 25 | var drawOverride = ` 26 | if(context == null) return; 27 | if (isCentered) { 28 | context.putImageData(textboxInfo.img, textboxInfo.left*scale, ((height/2)-(textboxInfo.height/2))*scale); 29 | this.onExit = ((height/2)-(textboxInfo.height/2))*scale === ((height/2)-(textboxInfo.height/2))*scale 30 | } 31 | else if (player().y < mapsize/2) { 32 | easingDialog(textboxInfo, ${hackOptions.easing}, 33 | !this.onClose ? (height-textboxInfo.bottom-textboxInfo.height)*scale 34 | : (height+textboxInfo.bottom+textboxInfo.height)*scale 35 | ) 36 | this.onExit = this.onClose && textboxInfo.y >= (height+textboxInfo.height)*scale 37 | } 38 | else { 39 | easingDialog(textboxInfo, ${ 40 | hackOptions.easing 41 | }, !this.onClose ? textboxInfo.top*scale : 42 | -textboxInfo.top-textboxInfo.height*scale) 43 | this.onExit = this.onClose && textboxInfo.y <= -textboxInfo.height*scale 44 | } 45 | return;`; 46 | 47 | var functionEasing = ` 48 | function easingDialog(tbox, easing, targetY) { 49 | var vy = (targetY - tbox.y) * easing; 50 | tbox.y += vy; 51 | context.putImageData(tbox.img,tbox.left*scale,tbox.y); 52 | } 53 | this.onClose = false; 54 | this.onExit = false; 55 | `; 56 | 57 | inject( 58 | /(this\.DrawTextbox\(\))/, 59 | '$1\nif(this.onExit && this.onClose){dialogBuffer.EndDialog()}', 60 | ); 61 | inject(/(this\.EndDialog\(\))/, 'dialogRenderer.onClose=true'); 62 | inject(/(var DialogRenderer = function\(\) {)/, `$1${functionEasing}`); 63 | inject(/(var textboxInfo = {)/, '$1y:0,'); 64 | inject( 65 | /(this\.Reset = function\(\) {)/, 66 | `$1 this.onClose=false; 67 | this.onExit=false; 68 | textboxInfo.y = player().y < mapsize/2 ? (height+textboxInfo.bottom+textboxInfo.height)*scale : -(textboxInfo.height) * scale;`, 69 | ); 70 | 71 | inject(/(this\.DrawTextbox = function\(\) {)/, `$1${drawOverride}`); 72 | -------------------------------------------------------------------------------- /src/transparent sprites.js: -------------------------------------------------------------------------------- 1 | /** 2 | 🏁 3 | @file transparent sprites 4 | @summary makes all sprites have transparent backgrounds 5 | @license MIT 6 | @version auto 7 | @requires Bitsy Version: 6.1 8 | @author Sean S. LeBlanc 9 | 10 | @description 11 | Makes all sprites have transparent backgrounds. 12 | i.e. tiles can be seen underneath the player, sprites, and items. 13 | 14 | HOW TO USE: 15 | 1. Copy-paste this script into a script tag after the bitsy source 16 | 2. Edit hackOptions below as needed 17 | */ 18 | import bitsy from 'bitsy'; 19 | import { 20 | before, 21 | } from './helpers/kitsy-script-toolkit'; 22 | 23 | export var hackOptions = { 24 | isTransparent: function (drawing) { 25 | // return drawing.name == 'tea'; // specific transparent drawing 26 | // return ['tea', 'flower', 'hat'].indexOf(drawing.name) !== -1; // specific transparent drawing list 27 | // return drawing.name && drawing.name.indexOf('TRANSPARENT') !== -1; // transparent drawing flag in name 28 | return true; // all drawings are transparent 29 | }, 30 | }; 31 | 32 | var madeTransparent; 33 | var makeTransparent; 34 | before('onready', function () { 35 | madeTransparent = {}; 36 | makeTransparent = false; 37 | }); 38 | before('renderer.GetImage', function (drawing, paletteId, frameOverride) { 39 | // check cache first 40 | var cache = madeTransparent[drawing.drw] = madeTransparent[drawing.drw] || {}; 41 | var p = cache[paletteId] = cache[paletteId] || {}; 42 | var frameIndex = frameOverride || drawing.animation.frameIndex; 43 | var source = bitsy.renderer.GetImageSource(drawing.drw); 44 | if (p[frameIndex] === source) { 45 | // already made this transparent 46 | return; 47 | } 48 | 49 | // flag the next draw as needing to be made transparent 50 | p[frameIndex] = source; 51 | makeTransparent = hackOptions.isTransparent(drawing); 52 | }); 53 | 54 | before('drawTile', function (canvas) { 55 | if (makeTransparent) { 56 | // redraw with all bg pixels transparent 57 | var ctx = canvas.getContext('2d'); 58 | var data = ctx.getImageData(0, 0, canvas.width, canvas.height); 59 | var bg = bitsy.getPal(bitsy.getRoomPal(bitsy.player().room))[0]; 60 | for (let i = 0; i < data.data.length; i += 4) { 61 | var r = data.data[i]; 62 | var g = data.data[i + 1]; 63 | var b = data.data[i + 2]; 64 | if (r === bg[0] && g === bg[1] && b === bg[2]) { 65 | data.data[i + 3] = 0; 66 | } 67 | } 68 | ctx.putImageData(data, 0, 0); 69 | // clear the flag 70 | makeTransparent = false; 71 | } 72 | }); 73 | -------------------------------------------------------------------------------- /src/basic sfx.js: -------------------------------------------------------------------------------- 1 | /** 2 | 🔈 3 | @file basic sfx 4 | @summary "walk" and "talk" sound effect support 5 | @license MIT 6 | @version auto 7 | @author Sean S. LeBlanc 8 | 9 | @description 10 | Adds a basic sound effect API and hooks up "walk" and "talk" sound effects 11 | 12 | The walk sound effect plays every time the player moves. 13 | The talk sound effect plays every time the dialog box changes "pages" (e.g. when it opens, when the player presses a key to continue). 14 | 15 | Includes an optional feature which makes sound effect volume reduce if it was played recently. 16 | 17 | HOW TO USE: 18 | 1. Place your "walk" and "talk" sound files somewhere relative to your bitsy html file 19 | 2. Copy-paste `` into the of your document 20 | 3. Copy-paste `` into the of your document 21 | 4. Copy-paste this script into a script tag after the bitsy source 22 | 23 | Additional sounds can be added by by including more