From a5ae69c66e9e98ee0fbca6fc9a516816537518c6 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sun, 14 Aug 2022 01:25:33 -0400 Subject: [PATCH] Added first example: FullScreenSaver --- .eslintrc.json | 2 +- .github/workflows/dist.yml | 16 ++ docs/Development.md | 1 + .../walkthrough/1. Package Setup.md | 0 .../docs => docs}/walkthrough/10. Data.md | 0 .../docs => docs}/walkthrough/11. Text.md | 0 .../docs => docs}/walkthrough/2. Actors.md | 0 .../walkthrough/3. Instantiation.md | 0 .../docs => docs}/walkthrough/4. Physics.md | 0 .../docs => docs}/walkthrough/5. Inputs.md | 0 .../docs => docs}/walkthrough/6. Players.md | 0 .../docs => docs}/walkthrough/7. Controls.md | 0 .../walkthrough/8. Collisions.md | 0 .../docs => docs}/walkthrough/9. Events.md | 0 .../docs => docs}/walkthrough/README.md | 2 +- examples/FullScreenSaver/.npmignore | 6 + examples/FullScreenSaver/README.md | 39 +++ examples/FullScreenSaver/lib/index.css | 88 +++++++ examples/FullScreenSaver/lib/index.html | 123 +++++++++ examples/FullScreenSaver/lib/theme/Icon.gif | Bin 0 -> 281 bytes .../FullScreenSaver/lib/theme/fonts/README.md | 3 + .../lib/theme/fonts/pressstart2p-webfont.eot | Bin 0 -> 17184 bytes .../lib/theme/fonts/pressstart2p-webfont.svg | 243 ++++++++++++++++++ .../lib/theme/fonts/pressstart2p-webfont.ttf | Bin 0 -> 59680 bytes .../lib/theme/fonts/pressstart2p-webfont.woff | Bin 0 -> 18956 bytes examples/FullScreenSaver/package.json | 101 ++++++++ examples/FullScreenSaver/src/Direction.ts | 6 + .../src/FullScreenSaver.test.ts | 5 + .../FullScreenSaver/src/FullScreenSaver.ts | 99 +++++++ .../src/creators/createMenuGrapher.ts | 19 ++ examples/FullScreenSaver/src/index.ts | 12 + .../src/interface/InterfaceSettings.ts | 157 +++++++++++ examples/FullScreenSaver/src/main.ts | 8 + .../src/sections/Collisions.ts | 41 +++ .../FullScreenSaver/src/sections/Graphics.ts | 55 ++++ .../FullScreenSaver/src/sections/Groups.ts | 7 + .../FullScreenSaver/src/sections/Inputs.ts | 56 ++++ .../src/sections/Maintenance.ts | 42 +++ .../FullScreenSaver/src/sections/Objects.ts | 82 ++++++ .../FullScreenSaver/src/sections/Players.ts | 69 +++++ .../FullScreenSaver/src/sections/Quadrants.ts | 13 + .../FullScreenSaver/src/sections/Scoring.ts | 49 ++++ .../FullScreenSaver/src/sections/Squares.ts | 55 ++++ examples/FullScreenSaver/tsconfig.json | 29 +++ lerna.json | 2 +- package.json | 8 +- packages/eightbittr/README.md | 2 +- .../eightbittr/src/sections/Maintenance.ts | 3 +- .../eightbittr/src/sections/Physics.test.ts | 8 +- packages/eightbittr/src/sections/Physics.ts | 30 +-- packages/menugraphr/src/MenuGraphr.ts | 10 +- .../setup/external/tsconfig.json | 1 - .../setup/package-game.json | 17 +- .../setup/web/lib/index.html | 10 +- .../src/commands/mustache.ts | 16 +- packages/shenanigans-manager/src/typings.ts | 5 + packages/shenanigans-manager/src/utils.ts | 43 +++- .../userwrappr/src/Menus/InitializeMenus.tsx | 2 +- packages/userwrappr/src/Menus/MenuContext.tsx | 1 + .../src/Menus/Options/useOptionState.ts | 1 + packages/userwrappr/src/VisualContext.tsx | 1 + tsconfig.json | 4 +- 62 files changed, 1536 insertions(+), 56 deletions(-) create mode 100644 .github/workflows/dist.yml rename {packages/eightbittr/docs => docs}/walkthrough/1. Package Setup.md (100%) rename {packages/eightbittr/docs => docs}/walkthrough/10. Data.md (100%) rename {packages/eightbittr/docs => docs}/walkthrough/11. Text.md (100%) rename {packages/eightbittr/docs => docs}/walkthrough/2. Actors.md (100%) rename {packages/eightbittr/docs => docs}/walkthrough/3. Instantiation.md (100%) rename {packages/eightbittr/docs => docs}/walkthrough/4. Physics.md (100%) rename {packages/eightbittr/docs => docs}/walkthrough/5. Inputs.md (100%) rename {packages/eightbittr/docs => docs}/walkthrough/6. Players.md (100%) rename {packages/eightbittr/docs => docs}/walkthrough/7. Controls.md (100%) rename {packages/eightbittr/docs => docs}/walkthrough/8. Collisions.md (100%) rename {packages/eightbittr/docs => docs}/walkthrough/9. Events.md (100%) rename {packages/eightbittr/docs => docs}/walkthrough/README.md (92%) create mode 100644 examples/FullScreenSaver/.npmignore create mode 100644 examples/FullScreenSaver/README.md create mode 100644 examples/FullScreenSaver/lib/index.css create mode 100644 examples/FullScreenSaver/lib/index.html create mode 100644 examples/FullScreenSaver/lib/theme/Icon.gif create mode 100644 examples/FullScreenSaver/lib/theme/fonts/README.md create mode 100644 examples/FullScreenSaver/lib/theme/fonts/pressstart2p-webfont.eot create mode 100644 examples/FullScreenSaver/lib/theme/fonts/pressstart2p-webfont.svg create mode 100644 examples/FullScreenSaver/lib/theme/fonts/pressstart2p-webfont.ttf create mode 100644 examples/FullScreenSaver/lib/theme/fonts/pressstart2p-webfont.woff create mode 100644 examples/FullScreenSaver/package.json create mode 100644 examples/FullScreenSaver/src/Direction.ts create mode 100644 examples/FullScreenSaver/src/FullScreenSaver.test.ts create mode 100644 examples/FullScreenSaver/src/FullScreenSaver.ts create mode 100644 examples/FullScreenSaver/src/creators/createMenuGrapher.ts create mode 100644 examples/FullScreenSaver/src/index.ts create mode 100644 examples/FullScreenSaver/src/interface/InterfaceSettings.ts create mode 100644 examples/FullScreenSaver/src/main.ts create mode 100644 examples/FullScreenSaver/src/sections/Collisions.ts create mode 100644 examples/FullScreenSaver/src/sections/Graphics.ts create mode 100644 examples/FullScreenSaver/src/sections/Groups.ts create mode 100644 examples/FullScreenSaver/src/sections/Inputs.ts create mode 100644 examples/FullScreenSaver/src/sections/Maintenance.ts create mode 100644 examples/FullScreenSaver/src/sections/Objects.ts create mode 100644 examples/FullScreenSaver/src/sections/Players.ts create mode 100644 examples/FullScreenSaver/src/sections/Quadrants.ts create mode 100644 examples/FullScreenSaver/src/sections/Scoring.ts create mode 100644 examples/FullScreenSaver/src/sections/Squares.ts create mode 100644 examples/FullScreenSaver/tsconfig.json diff --git a/.eslintrc.json b/.eslintrc.json index 67c73009..42851225 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -16,7 +16,7 @@ ], "parser": "@typescript-eslint/parser", "parserOptions": { - "project": ["./packages/*/tsconfig.json"] + "project": ["./examples/*/tsconfig.json", "./packages/*/tsconfig.json"] }, "plugins": ["@typescript-eslint", "simple-import-sort"], "rules": { diff --git a/.github/workflows/dist.yml b/.github/workflows/dist.yml new file mode 100644 index 00000000..effd96fd --- /dev/null +++ b/.github/workflows/dist.yml @@ -0,0 +1,16 @@ +name: Dist + +on: + push: + branches: + - main + pull_request: + +jobs: + compile: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: ./.github/actions/prepare + - run: yarn compile + - run: yarn dist diff --git a/docs/Development.md b/docs/Development.md index 321dfb3a..88da96f2 100644 --- a/docs/Development.md +++ b/docs/Development.md @@ -33,6 +33,7 @@ cd EightBittr yarn yarn lerna bootstrap yarn run hydrate +yarn lerna run dist ``` ### Local Development diff --git a/packages/eightbittr/docs/walkthrough/1. Package Setup.md b/docs/walkthrough/1. Package Setup.md similarity index 100% rename from packages/eightbittr/docs/walkthrough/1. Package Setup.md rename to docs/walkthrough/1. Package Setup.md diff --git a/packages/eightbittr/docs/walkthrough/10. Data.md b/docs/walkthrough/10. Data.md similarity index 100% rename from packages/eightbittr/docs/walkthrough/10. Data.md rename to docs/walkthrough/10. Data.md diff --git a/packages/eightbittr/docs/walkthrough/11. Text.md b/docs/walkthrough/11. Text.md similarity index 100% rename from packages/eightbittr/docs/walkthrough/11. Text.md rename to docs/walkthrough/11. Text.md diff --git a/packages/eightbittr/docs/walkthrough/2. Actors.md b/docs/walkthrough/2. Actors.md similarity index 100% rename from packages/eightbittr/docs/walkthrough/2. Actors.md rename to docs/walkthrough/2. Actors.md diff --git a/packages/eightbittr/docs/walkthrough/3. Instantiation.md b/docs/walkthrough/3. Instantiation.md similarity index 100% rename from packages/eightbittr/docs/walkthrough/3. Instantiation.md rename to docs/walkthrough/3. Instantiation.md diff --git a/packages/eightbittr/docs/walkthrough/4. Physics.md b/docs/walkthrough/4. Physics.md similarity index 100% rename from packages/eightbittr/docs/walkthrough/4. Physics.md rename to docs/walkthrough/4. Physics.md diff --git a/packages/eightbittr/docs/walkthrough/5. Inputs.md b/docs/walkthrough/5. Inputs.md similarity index 100% rename from packages/eightbittr/docs/walkthrough/5. Inputs.md rename to docs/walkthrough/5. Inputs.md diff --git a/packages/eightbittr/docs/walkthrough/6. Players.md b/docs/walkthrough/6. Players.md similarity index 100% rename from packages/eightbittr/docs/walkthrough/6. Players.md rename to docs/walkthrough/6. Players.md diff --git a/packages/eightbittr/docs/walkthrough/7. Controls.md b/docs/walkthrough/7. Controls.md similarity index 100% rename from packages/eightbittr/docs/walkthrough/7. Controls.md rename to docs/walkthrough/7. Controls.md diff --git a/packages/eightbittr/docs/walkthrough/8. Collisions.md b/docs/walkthrough/8. Collisions.md similarity index 100% rename from packages/eightbittr/docs/walkthrough/8. Collisions.md rename to docs/walkthrough/8. Collisions.md diff --git a/packages/eightbittr/docs/walkthrough/9. Events.md b/docs/walkthrough/9. Events.md similarity index 100% rename from packages/eightbittr/docs/walkthrough/9. Events.md rename to docs/walkthrough/9. Events.md diff --git a/packages/eightbittr/docs/walkthrough/README.md b/docs/walkthrough/README.md similarity index 92% rename from packages/eightbittr/docs/walkthrough/README.md rename to docs/walkthrough/README.md index e6cba699..1df25fd9 100644 --- a/packages/eightbittr/docs/walkthrough/README.md +++ b/docs/walkthrough/README.md @@ -3,7 +3,7 @@ This page will guide you through the steps to create a new game using the EightBittr game engine. The game will feature a player avoiding boxes bouncing around a screen like an old screensaver and include a basic suite of tests. -> Make sure you've read through the other documentation pages listed in the [EightBittr README.md](../../README.md) before reading this one! +> Make sure you've read through the other documentation pages listed in the [README.md](./README.md) before reading this one! ## Preface diff --git a/examples/FullScreenSaver/.npmignore b/examples/FullScreenSaver/.npmignore new file mode 100644 index 00000000..bfe21769 --- /dev/null +++ b/examples/FullScreenSaver/.npmignore @@ -0,0 +1,6 @@ +node_modules/ +test/ +*.test.* +npm-debug.log +*.ts +!*.d.ts diff --git a/examples/FullScreenSaver/README.md b/examples/FullScreenSaver/README.md new file mode 100644 index 00000000..fa8babd8 --- /dev/null +++ b/examples/FullScreenSaver/README.md @@ -0,0 +1,39 @@ + + +# FullScreenSaver + +[![Code Style: Prettier](https://img.shields.io/badge/code_style-prettier-brightgreen.svg)](https://prettier.io) +![TypeScript: Strict](https://img.shields.io/badge/typescript-strict-brightgreen.svg) +[![NPM version](https://badge.fury.io/js/fullscreensaver.svg)](http://badge.fury.io/js/fullscreensaver) +[![Join the chat at https://gitter.im/FullScreenShenanigans/community](https://badges.gitter.im/FullScreenShenanigans/community.svg)](https://gitter.im/FullScreenShenanigans/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + +Example game with EightBittr tooling. + + + +## Usage + +Documentation coming soon™️! + + + +## Development + +This repository is a portion of the [EightBittr monorepo](https://raw.githubusercontent.com/FullScreenShenanigans/EightBittr). +See its [docs/Development.md](../../docs/Development.md) for details on how to get started. 💖 + +### Running Tests + +```shell +yarn run test +``` + +Tests are written in [Mocha](https://github.com/mochajs/mocha) and [Chai](https://github.com/chaijs/chai). +Their files are written using alongside source files under `src/` and named `*.test.ts?`. +Whenever you add, remove, or rename a `*.test.t*` file under `src/`, `watch` will re-run `yarn run test:setup` to regenerate the list of static test files in `test/index.html`. +You can open that file in a browser to debug through the tests, or run `yarn test:run` to run them in headless Chrome. + + + + + diff --git a/examples/FullScreenSaver/lib/index.css b/examples/FullScreenSaver/lib/index.css new file mode 100644 index 00000000..d11b3218 --- /dev/null +++ b/examples/FullScreenSaver/lib/index.css @@ -0,0 +1,88 @@ +/* General elements */ + +html, +body { + margin: 0; + padding: 0; +} + +body { + background: black; + color: #fafafa; + overflow-x: hidden; + overflow-y: scroll; +} + +header { + font-family: "Press Start"; + padding: 0.5rem; + overflow: hidden; + -ms-user-select: none; + -moz-user-select: none; + -webkit-user-select: none; + user-select: none; +} + +section { + clear: both; +} + +a { + color: white; + font-weight: bold; + text-decoration: none; +} + +h1 { + margin: 0; + padding-bottom: 3px; + text-align: center; +} + +/* General animations */ + +.hoverable { + opacity: 0.84; + transition: 140ms opacity; +} + +.hoverable:hover { + opacity: 1; +} + +/* Game section */ + +section#game { + min-height: 210px; + margin-bottom: 35px; +} + +/* Text sections */ + +section.section-text { + margin: auto; + padding: 21px 70px; + max-width: 910px; + font-size: 1.4em; + text-align: center; +} + +.link-github { + color: #cfc; +} + +@media (max-width: 350px) { + section.section-text { + padding: 14px 7px; + } +} + +@font-face { + font-family: "Press Start"; + src: url("theme/fonts/pressstart2p-webfont.eot?#iefix") format("embedded-opentype"), + url("theme/fonts/pressstart2p-webfont.woff") format("woff"), + url("theme/fonts/pressstart2p-webfont.ttf") format("truetype"), + url("theme/fonts/pressstart2p-webfont.svg") format("svg"); + font-weight: normal; + font-style: normal; +} diff --git a/examples/FullScreenSaver/lib/index.html b/examples/FullScreenSaver/lib/index.html new file mode 100644 index 00000000..f67c2c0c --- /dev/null +++ b/examples/FullScreenSaver/lib/index.html @@ -0,0 +1,123 @@ + + + + + FullScreenSaver + + + + + + + + + +
+

FullScreenSaver

+
+ +
+ +
+ +
+ You may + download the game for yourself + from GitHub, the location of FullScreenSaver's open source project. +
+ +
+ + + + + + + + diff --git a/examples/FullScreenSaver/lib/theme/Icon.gif b/examples/FullScreenSaver/lib/theme/Icon.gif new file mode 100644 index 0000000000000000000000000000000000000000..28047a94774bfcf7eb05ff182261d62750f54516 GIT binary patch literal 281 zcmZ?wbhEHbG+;1bSj2!6DE?$&WME)n&;h9csbyfk=dtS!i@_<*IeQ~6%5MHH^Sw80 z$3#iOlveHgA~m`A@4e)8+xK)o*)Al}@20dSxJzTA$5NLUDo*`D%aSwpa%~dO zdh(JreAeT4S9RDXTtA|DYi-*m@tCD??{ELktgUY{lWuO)X>70V>`JTbt?QX!B@-7v zVcOKx8F8};dS^;co4inD+VT}E-R7-YyK2q4jdK@m+8SOYCAe+f4ql<%YxnZ(Kd|yp z#jc|(j_(vWbvkO_$@7~Ni?1wAkG_1v`^y*dhof$%k{4{ZmZq@ko)P@ Q7x{0We?0j0fs?@+0GddBhyVZp literal 0 HcmV?d00001 diff --git a/examples/FullScreenSaver/lib/theme/fonts/README.md b/examples/FullScreenSaver/lib/theme/fonts/README.md new file mode 100644 index 00000000..bebf666e --- /dev/null +++ b/examples/FullScreenSaver/lib/theme/fonts/README.md @@ -0,0 +1,3 @@ +TODO PUT LICENSING INFORMATION HERE +I SET THIS FOLDER UP WHILE OFFLINE BUT DEFINITELY SHOULD INCLUDE SOMETHING ONCE ONLINE :) +CREDIT TO FONT MAKERS OF COURSE diff --git a/examples/FullScreenSaver/lib/theme/fonts/pressstart2p-webfont.eot b/examples/FullScreenSaver/lib/theme/fonts/pressstart2p-webfont.eot new file mode 100644 index 0000000000000000000000000000000000000000..98aa442bad0a8bf5eccce4a954742f2a95732b34 GIT binary patch literal 17184 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 sfay%Nv`>@S4>6Thg4_P_F>R^=0J7DnkpKVy literal 0 HcmV?d00001 diff --git a/examples/FullScreenSaver/lib/theme/fonts/pressstart2p-webfont.svg b/examples/FullScreenSaver/lib/theme/fonts/pressstart2p-webfont.svg new file mode 100644 index 00000000..b1fb3f5c --- /dev/null +++ b/examples/FullScreenSaver/lib/theme/fonts/pressstart2p-webfont.svg @@ -0,0 +1,243 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/FullScreenSaver/lib/theme/fonts/pressstart2p-webfont.ttf b/examples/FullScreenSaver/lib/theme/fonts/pressstart2p-webfont.ttf new file mode 100644 index 0000000000000000000000000000000000000000..a6f43ac9fd1a7d4c87e40e5b3fff0ef17fdb39e0 GIT binary patch literal 59680 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

b>0FTi|1J;)tEgX!D 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 YBePqXwoBv&5p7T&Vhjpc+|0lL0P?k{$N&HU literal 0 HcmV?d00001 diff --git a/examples/FullScreenSaver/lib/theme/fonts/pressstart2p-webfont.woff b/examples/FullScreenSaver/lib/theme/fonts/pressstart2p-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..5b574105860a72cabe03030a1976d6e977f9aab4 GIT binary patch literal 18956 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 literal 0 HcmV?d00001 diff --git a/examples/FullScreenSaver/package.json b/examples/FullScreenSaver/package.json new file mode 100644 index 00000000..d439be19 --- /dev/null +++ b/examples/FullScreenSaver/package.json @@ -0,0 +1,101 @@ +{ + "browser": "./lib/index.js", + "bugs": { + "url": "https://github.com/FullScreenShenanigans/FullScreenSaver/issues" + }, + "dependencies": { + "eightbittr": "^0.8.2", + "menugraphr": "^0.8.1", + "userwrappr": "^0.8.2" + }, + "description": "Example game with EightBittr tooling.", + "devDependencies": { + "@babel/core": "^7.18.10", + "@babel/plugin-proposal-class-properties": "^7.18.6", + "@babel/plugin-proposal-decorators": "^7.18.10", + "@babel/preset-react": "^7.18.6", + "@babel/preset-typescript": "^7.18.6", + "@types/chai": "^4.3.3", + "@types/mocha": "^9.1.1", + "@types/sinon": "^10.0.13", + "@types/sinon-chai": "^3.2.8", + "babel-loader": "8.2.5", + "chai": "^4.3.6", + "husky": "^8.0.1", + "lint-staged": "^13.0.3", + "mocha": "^10.0.0", + "mocha-headless-chrome": "^4.0.0", + "react": "^16.8.6", + "react-dom": "^16.8.6", + "requirejs": "^2.3.6", + "shenanigans-manager": "^0.8.1", + "sinon": "^14.0.0", + "sinon-chai": "^3.7.0", + "webpack": "^5.74.0", + "webpack-cli": "^4.10.0" + }, + "husky": { + "hooks": { + "pre-commit": "lint-staged" + } + }, + "license": "MIT", + "lint-staged": { + "**/*.{css,js,json,md,ts,tsx}": [ + "prettier --ignore-path .prettierignore --write" + ] + }, + "name": "fullscreensaver", + "package": { + "type": "git", + "url": "ssh://git@github.com:FullScreenShenanigans/FullScreenSaver.git" + }, + "scripts": { + "clean": "rm -rf lib *.tsbuildinfo", + "compile": "tsc -b", + "dist": "webpack", + "format": "prettier --ignore-path .prettierignore --check \"./**/*.{css,js,json,md,ts,tsx}\"", + "format:write": "yarn format --write", + "hydrate": "yarn shenanigans-manager hydrate", + "link": "yarn link", + "lint": "eslint --ignore-path .eslintignore \"./src/**/*.{ts,tsx}\"", + "publish:ci": "yarn shenanigans-manager publish-if-updated", + "test": "yarn run test:setup && yarn run test:run", + "test:run": "yarn mocha-headless-chrome --file test/index.html", + "test:setup": "yarn shenanigans-manager generate-tests" + }, + "shenanigans": { + "dist": true, + "example": true, + "game": true, + "loading": { + "externals": [ + { + "js": { + "dev": "react/umd/react.development", + "prod": "react/lib/react.production" + }, + "name": "react" + }, + { + "js": { + "dev": "react-dom/umd/react-dom.development", + "prod": "react-dom/lib/react-dom.production" + }, + "name": "react-dom" + }, + { + "js": { + "dev": "userwrappr/dist/UserWrappr-Delayed" + }, + "name": "UserWrappr-Delayed", + "shenanigansPackage": true + } + ] + }, + "name": "FullScreenSaver", + "web": true + }, + "types": "./lib/index.d.ts", + "version": "0.0.1" +} \ No newline at end of file diff --git a/examples/FullScreenSaver/src/Direction.ts b/examples/FullScreenSaver/src/Direction.ts new file mode 100644 index 00000000..def4129a --- /dev/null +++ b/examples/FullScreenSaver/src/Direction.ts @@ -0,0 +1,6 @@ +export enum Direction { + Top = 0, + Right = 1, + Bottom = 2, + Left = 3, +} diff --git a/examples/FullScreenSaver/src/FullScreenSaver.test.ts b/examples/FullScreenSaver/src/FullScreenSaver.test.ts new file mode 100644 index 00000000..2db61abb --- /dev/null +++ b/examples/FullScreenSaver/src/FullScreenSaver.test.ts @@ -0,0 +1,5 @@ +describe("FullScreenSaver", () => { + it("_", () => { + // TODO: Implement me! + }); +}); diff --git a/examples/FullScreenSaver/src/FullScreenSaver.ts b/examples/FullScreenSaver/src/FullScreenSaver.ts new file mode 100644 index 00000000..915a0e94 --- /dev/null +++ b/examples/FullScreenSaver/src/FullScreenSaver.ts @@ -0,0 +1,99 @@ +import { factory, member } from "autofieldr"; +import { EightBittr, EightBittrConstructorSettings } from "eightbittr"; +import { MenuGraphr } from "menugraphr"; + +import { createMenuGrapher } from "./creators/createMenuGrapher"; +import { Collisions } from "./sections/Collisions"; +import { Graphics } from "./sections/Graphics"; +import { Groups } from "./sections/Groups"; +import { Inputs } from "./sections/Inputs"; +import { Maintenance } from "./sections/Maintenance"; +import { Objects } from "./sections/Objects"; +import { Players } from "./sections/Players"; +import { Quadrants } from "./sections/Quadrants"; +import { Scoring } from "./sections/Scoring"; +import { Squares } from "./sections/Squares"; + +/** + * Example game with EightBittr tooling + */ +export class FullScreenSaver extends EightBittr { + /** + * Text-based menu and dialog management system. + */ + @factory(createMenuGrapher) + public readonly menuGrapher: MenuGraphr; + + /** + * Checkers and callbacks for Actor collisions. + */ + @member(Collisions) + public readonly collisions: Collisions; + + /** + * Stores the visual appearance of Actors. + */ + @member(Graphics) + public readonly graphics: Graphics; + + /** + * Collection settings for Actor group names. + */ + @member(Groups) + public readonly groups: Groups; + + /** + * User input filtering and handling. + */ + @member(Inputs) + public readonly inputs: Inputs; + + /** + * Update logic for Actors in game ticks. + */ + @member(Maintenance) + public readonly maintenance: Maintenance; + + /** + * Raw ObjectMakr factory settings. + */ + @member(Objects) + public readonly objects: Objects; + + /** + * Creates and updates player Actors. + */ + @member(Players) + public readonly players: Players; + + /** + * Arranges game physics quadrants. + */ + @member(Quadrants) + public readonly quadrants: Quadrants; + + /** + * Keeps track of points and high scores. + */ + @member(Scoring) + public readonly scoring: Scoring; + + /** + * Creates square Actors in the game. + */ + @member(Squares) + public readonly squares: Squares; + + /** + * Initializes a new instance of the FullScreenSaver class. + * + * @param settings Settings to be used for initialization. + */ + public constructor(settings: EightBittrConstructorSettings) { + super(settings); + + this.quadsKeeper.resetQuadrants(); + + this.squares.addSquare(this.mapScreener.width / 2, this.mapScreener.height / 2, 2, -2); + } +} diff --git a/examples/FullScreenSaver/src/creators/createMenuGrapher.ts b/examples/FullScreenSaver/src/creators/createMenuGrapher.ts new file mode 100644 index 00000000..c1b5049a --- /dev/null +++ b/examples/FullScreenSaver/src/creators/createMenuGrapher.ts @@ -0,0 +1,19 @@ +import { MenuGraphr } from "menugraphr"; + +import { FullScreenSaver } from "../FullScreenSaver"; + +export const createMenuGrapher = (game: FullScreenSaver): MenuGraphr => + new MenuGraphr({ + game, + schemas: { + Score: { + height: 48, + position: { + vertical: "bottom", + }, + textPaddingX: 8, + textPaddingY: 24, + width: game.mapScreener.width / 2, + }, + }, + }); diff --git a/examples/FullScreenSaver/src/index.ts b/examples/FullScreenSaver/src/index.ts new file mode 100644 index 00000000..fc9dfe25 --- /dev/null +++ b/examples/FullScreenSaver/src/index.ts @@ -0,0 +1,12 @@ +import { UserWrappr } from "userwrappr"; +export * from "./FullScreenSaver"; + +import { createUserWrapprSettings } from "./interface/InterfaceSettings"; + +/** + * Creates a UserWrappr interface around a new FullScreenSaver game. + * + * @param container HTML element to create within. + */ +export const createFullScreenSaverInterface = async (container: HTMLElement): Promise => + new UserWrappr(createUserWrapprSettings()).createDisplay(container); diff --git a/examples/FullScreenSaver/src/interface/InterfaceSettings.ts b/examples/FullScreenSaver/src/interface/InterfaceSettings.ts new file mode 100644 index 00000000..1f51fcd6 --- /dev/null +++ b/examples/FullScreenSaver/src/interface/InterfaceSettings.ts @@ -0,0 +1,157 @@ +import { GameWindow } from "eightbittr"; +import { AbsoluteSizeSchema, UserWrapprSettings, OptionType } from "userwrappr"; + +import { FullScreenSaver } from "../FullScreenSaver"; + +/** + * Global scope around a game, such as a DOM window. + */ +export interface WrappingGameWindow extends GameWindow { + /** + * Game instance, once this has created it. + */ + FSS?: FullScreenSaver; +} + +export interface InterfaceSettingOverrides { + createGame?(size: AbsoluteSizeSchema): FullScreenSaver; + gameWindow?: WrappingGameWindow; +} + +/** + * Friendly name of the default game size. + */ +const defaultSize = "Full"; + +/** + * Sizes the game is allowed to be, keyed by friendly name. + */ +const sizes = { + [defaultSize]: { + width: "100%", + height: "100%", + }, +}; + +/** + * Creates settings for an UserWrappr that will create and wrap a FullScreenSaver instance. + * + * @param gameWindow Global scope around the game interface, if not the global window. + */ +export const createUserWrapprSettings = ({ + createGame = (size: AbsoluteSizeSchema) => new FullScreenSaver(size), + gameWindow = window, +}: InterfaceSettingOverrides = {}): UserWrapprSettings => { + /** + * Game instance, once this has created it. + */ + let game: FullScreenSaver; + + return { + defaultSize: sizes[defaultSize], + createContents: (size: AbsoluteSizeSchema) => { + gameWindow.FSS = game = createGame(size); + game.inputs.initializeGlobalPipes(gameWindow); + game.frameTicker.play(); + + return game.container; + }, + menus: [ + { + options: [ + { + action: (): void => { + game.utilities.takeScreenshot(`FullScreenSaver ${Date.now()}`); + }, + title: "Screenshot", + type: OptionType.Action, + }, + { + getInitialValue: (): string => "1x", + options: [".25x", ".5x", "1x", "2x", "5x", "10x", "20x"], + saveValue: (value: string): void => { + const multiplier = parseFloat(value.replace("x", "")); + game.frameTicker.setInterval(1000 / 60 / multiplier); + game.pixelDrawer.setFramerateSkip(multiplier); + }, + title: "Speed", + type: OptionType.Select, + }, + ], + title: "Options", + }, + ], + styles: { + input: { + fontFamily: "Press Start", + minWidth: "117px", + padding: "3px", + }, + inputButton: { + background: "#ffcc33", + fontFamily: "Press Start", + padding: "7px 3px", + }, + inputButtonAction: { + padding: "11px 3px", + width: "100%", + }, + inputButtonBoolean: { + padding: "7px 21px", + }, + inputButtonOff: { + background: "#ccaa33", + }, + inputSelect: { + minWidth: "35px", + padding: "3px 0", + }, + option: { + alignItems: "center", + margin: "auto", + padding: "7px 0", + maxWidth: "calc(100% - 14px)", + }, + options: { + left: "4px", + right: "4px", + width: "auto", + padding: "4px 3px 21px", + boxShadow: ["0 3px 7px black inset", "0 0 0 4px #99ccff", "0 0 14px black"].join( + ", " + ), + background: "#005599", + }, + optionsList: { + marginBottom: "7px", + }, + menu: { + maxWidth: "385px", + minWidth: "280px", + padding: "7px", + }, + menusInnerArea: { + background: "black", + color: "white", + fontFamily: "Press Start", + transition: "700ms color", + }, + menuTitle: { + fontSize: "16px", + }, + menuTitleButton: { + alignItems: "center", + background: "none", + border: "none", + color: "white", + display: "flex", + fontFamily: "Press Start", + fontSize: "16px", + justifyContent: "center", + }, + menuTitleButtonFake: { + color: "grey", + }, + }, + }; +}; diff --git a/examples/FullScreenSaver/src/main.ts b/examples/FullScreenSaver/src/main.ts new file mode 100644 index 00000000..2dcdb217 --- /dev/null +++ b/examples/FullScreenSaver/src/main.ts @@ -0,0 +1,8 @@ +import { createFullScreenSaverInterface } from "./index"; + +const container = document.getElementById("game")!; + +createFullScreenSaverInterface(container).catch((error: Error): void => { + console.error("An error happened while trying to instantiate FullScreenSaver!"); + console.error("requirejs error:", error); +}); diff --git a/examples/FullScreenSaver/src/sections/Collisions.ts b/examples/FullScreenSaver/src/sections/Collisions.ts new file mode 100644 index 00000000..ecebcce4 --- /dev/null +++ b/examples/FullScreenSaver/src/sections/Collisions.ts @@ -0,0 +1,41 @@ +import { Actor, Collisions as CollisionsBase } from "eightbittr"; + +import { FullScreenSaver } from "../FullScreenSaver"; + +/** + * Checkers and callbacks for Actor collisions. + */ +export class Collisions extends CollisionsBase { + /** + * Names of groups that should be checked for collisions. + */ + public readonly collidingGroupNames: string[] = ["Players"]; + + /** + * Function generators for checking whether two Actors are colliding. + */ + public readonly hitCheckGenerators = { + Players: { + Squares: () => (player: Actor, solid: Actor) => + player.right >= solid.left && + player.left <= solid.right && + player.bottom >= solid.top && + player.top <= solid.bottom, + }, + }; + + /** + * Function generators for reacting to two Actors colliding. + */ + public readonly hitCallbackGenerators = { + Players: { + Squares: () => (player: Actor) => { + this.game.death.kill(player); + + for (const square of this.game.groupHolder.getGroup("Squares").slice(1)) { + this.game.death.kill(square); + } + }, + }, + }; +} diff --git a/examples/FullScreenSaver/src/sections/Graphics.ts b/examples/FullScreenSaver/src/sections/Graphics.ts new file mode 100644 index 00000000..9c9af313 --- /dev/null +++ b/examples/FullScreenSaver/src/sections/Graphics.ts @@ -0,0 +1,55 @@ +import { Graphics as EightBittrGraphics } from "eightbittr"; +import { Palette } from "pixelrendr"; + +import { FullScreenSaver } from "../FullScreenSaver"; + +export class Graphics extends EightBittrGraphics { + public readonly background = "black"; + + public readonly library = { + CharA: "x011,1x06,101x05,101000010001000x15,001x05,101x05,10", + CharB: "x08,x15,0001000010010000100x16,001x05,101x05,10x16,00", + CharC: "x010,111100010000101x07,1x07,1x08,100001000111100", + CharD: "x08,x15,000100001001x05,101x05,101x05,1010000100x15,000", + CharE: "x08,x17,01x07,1x07,x16,001x07,1x07,x17,0", + CharF: "x08,x17,01x07,1x07,x16,001x07,1x07,1", + CharG: "x010,111100010000101x07,100111101x05,100100001000111100", + CharH: "x08,1x05,101x05,101x05,10x17,01x05,101x05,101x05,10", + CharI: "x09,x15,x05,1x07,1x07,1x07,1x07,1x05,x15,00", + CharJ: "x09,x16,x05,1x07,1x07,1000100010001000100001110000", + CharK: "x08,1000010010001000100100001011000011001000100001001x05,10", + CharL: "x08,1x07,1x07,1x07,1x07,1x07,1x07,x17,0", + CharM: "x08,1x05,101100011010101010100100101x05,101x05,101x05,10", + CharN: "x08,1x05,1011000010101000101001001010001010100001101x05,10", + CharO: "x010,111000010001001x05,101x05,101x05,100100010000111000", + CharP: "x08,x16,001x05,101x05,10x16,001x07,1x07,1", + CharQ: "x010,111000010001001x05,101x05,10100010100100010000111010", + CharR: "x08,x16,001x05,101x05,10x16,0010001000100001001x05,10", + CharS: "x09,1111000100001001x08,x15,x08,101x05,100x15,00", + CharT: "x08,x17,00001x07,1x07,1x07,1x07,1x07,10000", + CharU: "x08,1x05,101x05,101x05,101x05,101x05,100100001000x15,0", + CharV: "x08,1x05,101x05,10010001000100010000101x05,101x06,10000", + CharW: "x08,1x05,1010010010101010101010101011000110110001101x05,10", + CharX: "x08,110001100100010000101x06,1x06,1010000100010011000110", + CharY: "x08,1x05,100100010000101x06,1x07,1x07,1x07,10000", + CharZ: "x08,x17,x06,1x06,1x06,1x06,1x06,1x06,x17,0", + Char0: "x018,1110000100110011000110110001100110010000111000", + Char1: "x018,11x05,111x06,11x06,11x06,110000x16,00", + Char2: "x017,x15,001100011x05,111001111000111x05,x17,0", + Char3: "x017,x16,x05,110000111x08,110110001100x15,00", + Char4: "x019,11100001111000110110011001100x17,x05,1100", + Char5: "x016,x16,0011x06,x16,x07,110110001100x15,00", + Char6: "x017,x15,0011x06,x16,0011000110110001100x15,00", + Char7: "x016,x17,01100011x05,11x05,11x05,11x06,110000", + Char8: "x017,x15,00110001100x15,0011000110110001100x15,00", + Char9: "x017,x15,0011000110110001100x16,x06,1100x15,00", + Player: "x21024,", + Square: "x14096,", + }; + + public readonly paletteDefault: Palette = [ + [0, 0, 0, 0], + [255, 255, 255, 255], + [35, 255, 70, 255], + ]; +} diff --git a/examples/FullScreenSaver/src/sections/Groups.ts b/examples/FullScreenSaver/src/sections/Groups.ts new file mode 100644 index 00000000..4e9fcf52 --- /dev/null +++ b/examples/FullScreenSaver/src/sections/Groups.ts @@ -0,0 +1,7 @@ +import { Groups as EightBittrGroups } from "eightbittr"; + +import { FullScreenSaver } from "../FullScreenSaver"; + +export class Groups extends EightBittrGroups { + public readonly groupNames = ["Players", "Squares", "Text"]; +} diff --git a/examples/FullScreenSaver/src/sections/Inputs.ts b/examples/FullScreenSaver/src/sections/Inputs.ts new file mode 100644 index 00000000..74152145 --- /dev/null +++ b/examples/FullScreenSaver/src/sections/Inputs.ts @@ -0,0 +1,56 @@ +import { GameWindow, Inputs as InputsBase } from "eightbittr"; +import { TriggerContainer } from "inputwritr"; + +import { Direction } from "../Direction"; +import { FullScreenSaver } from "../FullScreenSaver"; + +/** + * User input filtering and handling. + */ +export class Inputs extends InputsBase { + /** + * Known, allowed aliases for input event triggers. + */ + public readonly aliases = { + bottom: [40], + left: [37], + right: [39], + top: [38], + }; + + /** + * Mapping of events to their key codes, to their callbacks. + */ + public readonly triggers: TriggerContainer = { + onkeydown: { + bottom: (event) => { + event?.preventDefault(); + this.game.players.requestPlayer(Direction.Bottom); + }, + left: (event) => { + event?.preventDefault(); + this.game.players.requestPlayer(Direction.Left); + }, + right: (event) => { + event?.preventDefault(); + this.game.players.requestPlayer(Direction.Right); + }, + top: (event) => { + event?.preventDefault(); + this.game.players.requestPlayer(Direction.Top); + }, + }, + }; + + /** + * Adds InputWritr pipes as global event listeners. + */ + public initializeGlobalPipes(gameWindow: GameWindow) { + super.initializeGlobalPipes(gameWindow); + + gameWindow.addEventListener( + "keydown", + this.game.inputWriter.makePipe("onkeydown", "keyCode") + ); + } +} diff --git a/examples/FullScreenSaver/src/sections/Maintenance.ts b/examples/FullScreenSaver/src/sections/Maintenance.ts new file mode 100644 index 00000000..e64f76a5 --- /dev/null +++ b/examples/FullScreenSaver/src/sections/Maintenance.ts @@ -0,0 +1,42 @@ +import { Actor, ActorMaintainer, Maintenance as MaintenanceBase } from "eightbittr"; + +import { FullScreenSaver } from "../FullScreenSaver"; + +/** + * Update logic for Actors in game ticks. + */ +export class Maintenance extends MaintenanceBase { + /** + * Maintains a general Actor for a tick. + */ + public readonly maintain = (actor: Actor) => { + this.game.physics.shiftBoth(actor, actor.xVelocity, actor.yVelocity); + + if (actor.top < 0) { + actor.yVelocity *= -1; + this.game.physics.setTop(actor, -actor.top); + } + + if (actor.bottom > this.game.mapScreener.height) { + actor.yVelocity *= -1; + this.game.physics.shiftVertical(actor, this.game.mapScreener.height - actor.bottom); + } + + if (actor.left < 0) { + actor.xVelocity *= -1; + this.game.physics.setLeft(actor, -actor.left); + } + + if (actor.right > this.game.mapScreener.width) { + actor.xVelocity *= -1; + this.game.physics.shiftHorizontal(actor, this.game.mapScreener.width - actor.right); + } + + return undefined; + }; + + public readonly maintainers: [string, ActorMaintainer][] = [ + ["Squares", this.maintain], + ["Players", this.maintain], + ]; +} diff --git a/examples/FullScreenSaver/src/sections/Objects.ts b/examples/FullScreenSaver/src/sections/Objects.ts new file mode 100644 index 00000000..0aecb9b0 --- /dev/null +++ b/examples/FullScreenSaver/src/sections/Objects.ts @@ -0,0 +1,82 @@ +import { Objects as ObjectsBase } from "eightbittr"; +import { ClassInheritance, ClassProperties } from "objectmakr"; + +import { FullScreenSaver } from "../FullScreenSaver"; + +/** + * Raw ObjectMakr factory settings. + */ +export class Objects extends ObjectsBase { + public readonly inheritance: ClassInheritance = { + Actor: { + Player: {}, + Square: {}, + Text: { + Char0: {}, + Char1: {}, + Char2: {}, + Char3: {}, + Char4: {}, + Char5: {}, + Char6: {}, + Char7: {}, + Char8: {}, + Char9: {}, + CharA: {}, + CharB: {}, + CharC: {}, + CharD: {}, + CharE: {}, + CharF: {}, + CharG: {}, + CharH: {}, + CharI: {}, + CharJ: {}, + CharK: {}, + CharL: {}, + CharM: {}, + CharN: {}, + CharO: {}, + CharP: {}, + CharQ: {}, + CharR: {}, + CharS: {}, + CharT: {}, + CharU: {}, + CharV: {}, + CharW: {}, + CharX: {}, + CharY: {}, + CharZ: {}, + Menu: {}, + }, + }, + }; + + public readonly onMake = "onMake"; + + public readonly properties: ClassProperties = { + Actor: { + onMake: this.game.actors.process.bind(this.game.actors), + }, + Menu: { + hidden: true, + }, + Player: { + height: 32, + groupType: "Players", + width: 32, + }, + Square: { + height: 64, + groupType: "Squares", + width: 64, + }, + Text: { + groupType: "Text", + height: 8, + scale: 2, + width: 8, + }, + }; +} diff --git a/examples/FullScreenSaver/src/sections/Players.ts b/examples/FullScreenSaver/src/sections/Players.ts new file mode 100644 index 00000000..a2a4c5c4 --- /dev/null +++ b/examples/FullScreenSaver/src/sections/Players.ts @@ -0,0 +1,69 @@ +import { Section } from "eightbittr"; + +import { Direction } from "../Direction"; +import { FullScreenSaver } from "../FullScreenSaver"; + +/** + * Raw speed player velocities will be based off of. + */ +const speed = 5; + +/** + * Horizontal and vertical player speeds for each possible direction. + */ +const directionVelocities = { + [Direction.Top]: { + xVelocity: 0, + yVelocity: -speed, + }, + [Direction.Right]: { + xVelocity: speed, + yVelocity: 0, + }, + [Direction.Bottom]: { + xVelocity: 0, + yVelocity: speed, + }, + [Direction.Left]: { + xVelocity: -speed, + yVelocity: 0, + }, +}; + +/** + * Creates and updates player Actors. + */ +export class Players extends Section { + /** + * Creates a new Player, if there wasn't one already. + */ + public requestPlayer(direction: Direction) { + const existingPlayer = this.game.groupHolder.getActor("player1"); + const velocities = directionVelocities[direction]; + + if (existingPlayer) { + Object.assign(existingPlayer, velocities); + return; + } + + const square = this.game.groupHolder.getGroup("Squares")[0]; + + const newPlayer = this.game.actors.add([ + "Player", + { + id: "player1", + ...directionVelocities[direction], + }, + ]); + + this.game.physics.setMidObj(newPlayer, square); + this.game.physics.shiftBoth( + newPlayer, + (newPlayer.xVelocity * square.width + 1) / speed, + (newPlayer.yVelocity * square.width + 1) / speed + ); + + this.game.squares.startAddingSquares(newPlayer); + this.game.scoring.start(newPlayer); + } +} diff --git a/examples/FullScreenSaver/src/sections/Quadrants.ts b/examples/FullScreenSaver/src/sections/Quadrants.ts new file mode 100644 index 00000000..9bea9d82 --- /dev/null +++ b/examples/FullScreenSaver/src/sections/Quadrants.ts @@ -0,0 +1,13 @@ +import { Quadrants as QuadrantsBase } from "eightbittr"; + +import { FullScreenSaver } from "../FullScreenSaver"; + +/** + * Arranges game physics quadrants. + */ +export class Quadrants extends QuadrantsBase { + /** + * Groups that should have their quadrants updated. + */ + public readonly activeGroupNames: string[] = ["Players", "Squares"]; +} diff --git a/examples/FullScreenSaver/src/sections/Scoring.ts b/examples/FullScreenSaver/src/sections/Scoring.ts new file mode 100644 index 00000000..889e368a --- /dev/null +++ b/examples/FullScreenSaver/src/sections/Scoring.ts @@ -0,0 +1,49 @@ +import { Actor, Section } from "eightbittr"; + +import { FullScreenSaver } from "FullScreenSaver"; + +/** + * Keeps track of points and high scores. + */ +export class Scoring extends Section { + /** + * Starts regularly increasing score for a player's run. + */ + public start(player: Actor) { + this.game.itemsHolder.setItem("score", 0); + this.refreshDisplay(); + + this.game.timeHandler.addEventInterval( + () => { + if (player.removed) { + const score = this.game.itemsHolder.getItem("score"); + const highScore = this.game.itemsHolder.getItem("highScore"); + + if (!highScore || score > highScore) { + this.game.itemsHolder.setItem("highScore", score); + this.game.itemsHolder.saveItem("highScore"); + } + + this.refreshDisplay(); + return true; + } + + this.game.itemsHolder.increase("score", 1); + this.refreshDisplay(); + return false; + }, + 50, + Infinity + ); + } + + /** + * Creates a Score menu to handle score and high score. + */ + public refreshDisplay() { + const score = this.game.itemsHolder.getItem("score"); + const highScore = this.game.itemsHolder.getItem("highScore") || 0; + this.game.menuGrapher.createMenu("Score"); + this.game.menuGrapher.addMenuDialog("Score", `SCORE ${score}\nHIGHSCORE ${highScore}`); + } +} diff --git a/examples/FullScreenSaver/src/sections/Squares.ts b/examples/FullScreenSaver/src/sections/Squares.ts new file mode 100644 index 00000000..ed2266c6 --- /dev/null +++ b/examples/FullScreenSaver/src/sections/Squares.ts @@ -0,0 +1,55 @@ +import { Actor, Section } from "eightbittr"; + +import { FullScreenSaver } from "../FullScreenSaver"; + +/** + * Creates square Actors in the game. + */ +export class Squares extends Section { + /** + * Maximum number of squares to stop spawning after. + */ + private readonly maximumSquares = + Math.sqrt((this.game.mapScreener.height * this.game.mapScreener.width) / 2000) | 0; + + /** + * Creates a new square in the game. + */ + public addSquare(midX: number, midY: number, xVelocity: number, yVelocity: number) { + const square = this.game.actors.add(["Square", { xVelocity, yVelocity }]); + + this.game.physics.setMid(square, midX, midY); + } + + /** + * Starts spawning new squares while it still can. + */ + public readonly startAddingSquares = (player: Actor) => { + this.game.timeHandler.addEventInterval( + () => { + if (player.removed) { + return true; + } + + const solids = [...this.game.groupHolder.getGroup("Squares")]; + + for (const square of solids) { + if (solids.length >= this.maximumSquares) { + return true; + } + + this.addSquare( + this.game.physics.getMidX(square), + this.game.physics.getMidY(square), + -square.xVelocity * 0.75, + -square.yVelocity * 0.75 + ); + } + + return false; + }, + 150, + Infinity + ); + }; +} diff --git a/examples/FullScreenSaver/tsconfig.json b/examples/FullScreenSaver/tsconfig.json new file mode 100644 index 00000000..1e816178 --- /dev/null +++ b/examples/FullScreenSaver/tsconfig.json @@ -0,0 +1,29 @@ +{ + "compilerOptions": { + "baseUrl": "./src", + "declaration": true, + "declarationMap": true, + "experimentalDecorators": true, + "forceConsistentCasingInFileNames": true, + "jsx": "react", + "module": "amd", + "moduleResolution": "node", + "noFallthroughCasesInSwitch": true, + "noImplicitAny": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "outDir": "./lib", + "pretty": true, + "rootDir": "./src", + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "strictFunctionTypes": false, + "strictPropertyInitialization": false, + "target": "ES2021" + }, + "exclude": ["**/*.d.ts", "**/lib"], + "include": ["./src/**/*.ts", "./src/**/*.tsx"] +} diff --git a/lerna.json b/lerna.json index 7136d942..fa54ba7f 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "npmClient": "yarn", - "packages": ["packages/*"], + "packages": ["examples/*", "packages/*"], "useWorkspaces": true, "version": "0.8.2" } diff --git a/package.json b/package.json index f82eb43c..b2890b4a 100644 --- a/package.json +++ b/package.json @@ -47,14 +47,15 @@ "scripts": { "clean": "lerna run clean", "compile": "tsc -b", + "dist": "lerna run dist", "format": "prettier --ignore-path .prettierignore .", "format:write": "yarn format --write", - "hydrate": "yarn lerna exec --scope shenanigans-manager tsc && lerna run hydrate && yarn run compile", - "link": "lerna run link && yarn lerna exec yarn link shenanigans-manager", + "hydrate": "lerna exec --scope shenanigans-manager tsc && lerna run hydrate && yarn run compile", + "link": "lerna run link && lerna exec yarn link shenanigans-manager", "lint": "eslint --ignore-path .eslintignore \"./packages/**/*.{js,ts,tsx}\"", "lint:md": "yarn markdownlint \"**/*.md\" \".github/**/*.md\"", "lint:spelling": "yarn cspell \"**\" \".github/**/*\"", - "ncu:all": "yarn lerna exec \"ncu -u\" && yarn lerna run ncu:all", + "ncu:all": "lerna exec \"ncu -u\" && lerna run ncu:all", "prepare": "husky install", "publish:ci": "lerna run publish:ci", "test": "lerna run test" @@ -63,6 +64,7 @@ "repository": "https://github.com/FullScreenShenanigans/EightBittr", "version": "0.8.2", "workspaces": [ + "examples/*", "packages/*" ] } diff --git a/packages/eightbittr/README.md b/packages/eightbittr/README.md index 4e54fe88..a660b6c2 100644 --- a/packages/eightbittr/README.md +++ b/packages/eightbittr/README.md @@ -34,7 +34,7 @@ See: - **[docs/Consumption](./docs/Consumption.md)** for how to set up a game using EightBittr. - **[docs/Testing](./docs/Testing.md)** for how to test a game set up with EightBittr. - **[docs/Runtime](./docs/Runtime.md)** for a detailed play-by-play of what happens each game frame. -- **[docs/Walkthrough](./docs/walkthrough/README.md)** for step-by-step instructions on how to set up a game with EightBittr. +- **[../../docs/Walkthrough](../../docs/walkthrough/README.md)** for step-by-step instructions on how to set up a game with EightBittr. - **[docs/FAQs](./docs/FAQs.md)** diff --git a/packages/eightbittr/src/sections/Maintenance.ts b/packages/eightbittr/src/sections/Maintenance.ts index 39fbedfd..e5bcaae6 100644 --- a/packages/eightbittr/src/sections/Maintenance.ts +++ b/packages/eightbittr/src/sections/Maintenance.ts @@ -3,8 +3,9 @@ import { Actor } from "../types"; import { Section } from "./Section"; /** - * TODO EXPLAIN + * Maintains and prunes an Actor for a game tick. * + * @param actor The actor to be maintained. * @returns Whether to remove the Actor from its group. */ export type ActorMaintainer = (actor: Actor) => boolean | undefined; diff --git a/packages/eightbittr/src/sections/Physics.test.ts b/packages/eightbittr/src/sections/Physics.test.ts index 9a084577..526acf4a 100644 --- a/packages/eightbittr/src/sections/Physics.test.ts +++ b/packages/eightbittr/src/sections/Physics.test.ts @@ -397,7 +397,7 @@ describe("Physics", () => { const amount = 7; // Act - physics.shiftHoriz(actor, amount); + physics.shiftHorizontal(actor, amount); // Assert expect(actor.left).to.be.equal(left + amount); @@ -412,7 +412,7 @@ describe("Physics", () => { const bottom = actor.bottom; // Act - physics.shiftHoriz(actor, 7); + physics.shiftHorizontal(actor, 7); // Assert expect(actor.top).to.be.equal(top); @@ -430,7 +430,7 @@ describe("Physics", () => { const amount = 7; // Act - physics.shiftVert(actor, amount); + physics.shiftVertical(actor, amount); // Assert expect(actor.top).to.be.equal(top + amount); @@ -445,7 +445,7 @@ describe("Physics", () => { const right = actor.right; // Act - physics.shiftVert(actor, 7); + physics.shiftVertical(actor, 7); // Assert expect(actor.left).to.be.equal(left); diff --git a/packages/eightbittr/src/sections/Physics.ts b/packages/eightbittr/src/sections/Physics.ts index 357fa716..b47ffd27 100644 --- a/packages/eightbittr/src/sections/Physics.ts +++ b/packages/eightbittr/src/sections/Physics.ts @@ -222,8 +222,8 @@ export class Physics extends Section { return; } - this.shiftHoriz(actor, dx); - this.shiftVert(actor, dy); + this.shiftHorizontal(actor, dx); + this.shiftVertical(actor, dy); } /** @@ -231,7 +231,7 @@ export class Physics extends Section { * * @param dx How far to shift the Actor horizontally. */ - public shiftHoriz(actor: Actor, dx: number): void { + public shiftHorizontal(actor: Actor, dx: number): void { actor.left += dx; actor.right += dx; @@ -255,7 +255,7 @@ export class Physics extends Section { * * @param dy How far to shift the Actor vertically. */ - public shiftVert(actor: Actor, dy: number): void { + public shiftVertical(actor: Actor, dy: number): void { actor.top += dy; actor.bottom += dy; @@ -289,13 +289,13 @@ export class Physics extends Section { /** * Shifts An Actor horizontally by its xVelocity and vertically by its yVelocity, using - * shiftHoriz and shiftVert. + * shiftHorizontal and shiftVertical. * * @param actor */ public updatePosition(actor: Actor): void { - this.shiftHoriz(actor, actor.xVelocity); - this.shiftVert(actor, actor.yVelocity); + this.shiftHorizontal(actor, actor.xVelocity); + this.shiftVertical(actor, actor.yVelocity); } /** @@ -339,9 +339,9 @@ export class Physics extends Section { const midx = this.getMidX(actor); if (midx < dx) { - this.shiftHoriz(actor, Math.min(maxDistance, dx - midx)); + this.shiftHorizontal(actor, Math.min(maxDistance, dx - midx)); } else { - this.shiftHoriz(actor, Math.max(-maxDistance, dx - midx)); + this.shiftHorizontal(actor, Math.max(-maxDistance, dx - midx)); } } @@ -358,9 +358,9 @@ export class Physics extends Section { const midy = this.getMidY(actor); if (midy < dy) { - this.shiftVert(actor, Math.min(maxDistance, dy - midy)); + this.shiftVertical(actor, Math.min(maxDistance, dy - midy)); } else { - this.shiftVert(actor, Math.max(-maxDistance, dy - midy)); + this.shiftVertical(actor, Math.max(-maxDistance, dy - midy)); } } @@ -385,7 +385,7 @@ export class Physics extends Section { } /** - * Shifts An Actor's top up, then sets the bottom (similar to a shiftVert and + * Shifts An Actor's top up, then sets the bottom (similar to a shiftVertical and * a setTop combined). * * @param actor The Actor to be shifted vertically. @@ -399,7 +399,7 @@ export class Physics extends Section { } /** - * Shifts An Actor's right, then sets the left (similar to a shiftHoriz and a + * Shifts An Actor's right, then sets the left (similar to a shiftHorizontal and a * setRight combined). * * @param actor The Actor to be shifted horizontally. @@ -414,7 +414,7 @@ export class Physics extends Section { /** * Shifts An Actor's bottom down, then sets the bottom (similar to a - * shiftVert and a setBottom combined). + * shiftVertical and a setBottom combined). * * @param actor The Actor to be shifted vertically. * @param dy How far to shift the Actor vertically (by default, 0). @@ -427,7 +427,7 @@ export class Physics extends Section { } /** - * Shifts An Actor's left, then sets the right (similar to a shiftHoriz and a + * Shifts An Actor's left, then sets the right (similar to a shiftHorizontal and a * setLeft combined). * * @param actor The Actor to be shifted horizontally. diff --git a/packages/menugraphr/src/MenuGraphr.ts b/packages/menugraphr/src/MenuGraphr.ts index 40b72450..4246e56c 100644 --- a/packages/menugraphr/src/MenuGraphr.ts +++ b/packages/menugraphr/src/MenuGraphr.ts @@ -510,7 +510,7 @@ export class MenuGraphr { optionChild.actors.push(character); if (!schema.position) { - this.game.physics.shiftVert(character, y - menu.top); + this.game.physics.shiftVertical(character, y - menu.top); } } } @@ -1175,11 +1175,11 @@ export class MenuGraphr { } if (offset.top) { - this.game.physics.shiftVert(actor, offset.top); + this.game.physics.shiftVertical(actor, offset.top); } if (offset.left) { - this.game.physics.shiftHoriz(actor, offset.left); + this.game.physics.shiftHorizontal(actor, offset.left); } if (!skipAdd) { @@ -1234,7 +1234,7 @@ export class MenuGraphr { * @returns Whether the character was deleted. */ private scrollCharacterUp(character: Actor, menu: Menu, speed: number): boolean { - this.game.physics.shiftVert(character, -speed); + this.game.physics.shiftVertical(character, -speed); if (character.top < menu.top + ((menu.textYOffset || 0) - speed)) { this.game.death.kill(character); @@ -1416,7 +1416,7 @@ export class MenuGraphr { option.y += offset; for (j = 0; j < optionChild.actors.length; j += 1) { - this.game.physics.shiftVert(optionChild.actors[j], offset); + this.game.physics.shiftVertical(optionChild.actors[j], offset); optionChild.actors[j].hidden = !!( i < menu.scrollingVisualOffset || i >= (menu.scrollingItems || 1) + menu.scrollingVisualOffset diff --git a/packages/shenanigans-manager/setup/external/tsconfig.json b/packages/shenanigans-manager/setup/external/tsconfig.json index fd5d25d0..1e816178 100644 --- a/packages/shenanigans-manager/setup/external/tsconfig.json +++ b/packages/shenanigans-manager/setup/external/tsconfig.json @@ -6,7 +6,6 @@ "experimentalDecorators": true, "forceConsistentCasingInFileNames": true, "jsx": "react", - "lib": ["dom", "es2015"], "module": "amd", "moduleResolution": "node", "noFallthroughCasesInSwitch": true, diff --git a/packages/shenanigans-manager/setup/package-game.json b/packages/shenanigans-manager/setup/package-game.json index b1f796d5..8673ac61 100644 --- a/packages/shenanigans-manager/setup/package-game.json +++ b/packages/shenanigans-manager/setup/package-game.json @@ -1,30 +1,31 @@ { "dependencies": { - "eightbittr": "^0.9.0", - "userwrappr": "^0.9.0" + "eightbittr": "^0.8.2", + "userwrappr": "^0.8.2" }, "shenanigans": { "loading": { "externals": [ { "js": { - "dev": "../node_modules/react/umd/react.development", - "prod": "../node_modules/react/lib/react.production" + "dev": "react/umd/react.development", + "prod": "react/lib/react.production" }, "name": "react" }, { "js": { - "dev": "../node_modules/react-dom/umd/react-dom.development", - "prod": "../node_modules/react-dom/lib/react-dom.production" + "dev": "react-dom/umd/react-dom.development", + "prod": "react-dom/lib/react-dom.production" }, "name": "react-dom" }, { "js": { - "dev": "../node_modules/userwrappr/dist/UserWrappr-Delayed" + "dev": "userwrappr/dist/UserWrappr-Delayed" }, - "name": "UserWrappr-Delayed" + "name": "UserWrappr-Delayed", + "shenanigansPackage": true } ] } diff --git a/packages/shenanigans-manager/setup/web/lib/index.html b/packages/shenanigans-manager/setup/web/lib/index.html index 97b0b9bb..7b5d1779 100644 --- a/packages/shenanigans-manager/setup/web/lib/index.html +++ b/packages/shenanigans-manager/setup/web/lib/index.html @@ -38,7 +38,7 @@ {{shenanigans.web.sections.legal}} - +