diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 26b69e3..cdaf3cf 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -12,45 +12,38 @@ jobs: steps: - name: Checkout repo - uses: actions/checkout@v2 + uses: actions/checkout@v3 + with: + fetch-depth: 0 - name: Setup Node - uses: actions/setup-node@v1 + uses: actions/setup-node@v3 with: node-version: 18 - name: Install dependencies - uses: bahmutov/npm-install@v1 + run: npm install && sudo apt install genisoimage + + - name: Load game.config.json + uses: antifree/json-to-variables@v1.0.1 with: - useLockFile: false + filename: 'game.config.json' + prefix: game - name: Build project run: npm run build - - name: Upload production-ready build files - uses: actions/upload-artifact@v2 - with: - name: production-files - path: ./dist - - deploy: - name: Deploy - needs: build - runs-on: ubuntu-latest - if: github.ref == 'refs/heads/main' - - steps: - - name: Download artifact - uses: actions/download-artifact@v2 + - name: Load meta.json + uses: antifree/json-to-variables@v1.0.1 with: - name: production-files - path: ./built/ + filename: 'dist/meta.json' + prefix: meta - name: Deploy to GitHub Pages uses: peaceiris/actions-gh-pages@v3 with: github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./built/unpacked + publish_dir: ./dist/web - name: Create release uses: "marvinpinto/action-automatic-releases@latest" @@ -58,7 +51,25 @@ jobs: repo_token: "${{ secrets.GITHUB_TOKEN }}" automatic_release_tag: "latest" prerelease: false - title: "Rerooted latest build" + title: "${{ env.game_title }} latest build" + files: | + ./dist/${{ env.meta_title }}-web.zip + ./dist/${{ env.meta_title }}-win.zip + ./dist/${{ env.meta_title }}-mac.dmg + ./dist/${{ env.meta_title }}-linux.zip + + - name: Upload to Itch + if: ${{env.game_itch_upload == 'true'}} + uses: Ayowel/butler-to-itch@v1.0.0 + with: + butler_key: ${{ secrets.BUTLER_CREDENTIALS }} + itch_user: ${{ env.game_itch_username }} + itch_game: ${{ env.game_itch_game }} files: | - /home/runner/work/uprooting/uprooting/built/Rerooted-GGJ2023-release.zip - /home/runner/work/uprooting/uprooting/built/game-web.zip + ./dist/${{ env.meta_title }}-web.zip + ./dist/${{ env.meta_title }}-win.zip + ./dist/${{ env.meta_title }}-mac.dmg + ./dist/${{ env.meta_title }}-linux.zip + auto_channel: true + butler_version: "latest" + check_signature: false diff --git a/.gitignore b/.gitignore index 2951d45..5ae56f1 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,8 @@ dist-ssr # Editor directories and files .vscode/* +!.vscode/launch.json +!.vscode/settings.json !.vscode/extensions.json .idea .DS_Store @@ -40,4 +42,7 @@ src/neutralino.js # Misc files .cache/ .firebase/ -*.zip \ No newline at end of file +*.zip +src/version.json +neutralino.config.json +__neutralino_globals.js \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..55712c1 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "typescript.tsdk": "node_modules/typescript/lib" +} \ No newline at end of file diff --git a/automation/build-cleanup.ts b/automation/build-cleanup.ts new file mode 100644 index 0000000..06651bb --- /dev/null +++ b/automation/build-cleanup.ts @@ -0,0 +1,18 @@ +import { PluginOption } from 'vite'; +import { rimrafSync } from 'rimraf'; +import { writeFileSync } from 'fs'; +import { build_path, title_dashed } from './constants'; + +const BuildCleanup = () => { + rimrafSync(build_path); + writeFileSync('./dist/meta.json', JSON.stringify({title: title_dashed})); +} + +export default function buildCleanup() { + return { + name: 'build-cleanup', + apply: 'build', + enforce: 'post', + closeBundle: BuildCleanup, + } as PluginOption; +} diff --git a/automation/constants.ts b/automation/constants.ts new file mode 100644 index 0000000..36a1b5a --- /dev/null +++ b/automation/constants.ts @@ -0,0 +1,31 @@ +import { execSync } from 'child_process'; +import { platform } from 'os'; +import { team, title, description, neutralino } from '../game.config.json'; + +export { team, title, description, neutralino }; + +const tryCatch = (fun: () => T, fallback: T): T => { + try { + return fun(); + } catch(e) { + return fallback; + } +} + +export const git_count = tryCatch(() => execSync('git rev-list --count HEAD').toString().trim(), "-1"); +export const git_short = tryCatch(() => execSync('git rev-parse --short HEAD').toString().trim(), "no-git"); +export const git_version = `v${git_count}.${git_short}`; + +export const year_current = new Date().getFullYear(); +export const year_initial = tryCatch(() => Number(platform() == 'win32' + ? execSync('git log --reverse | findstr "Date"').toString().match(/(\d+) \+/)?.[1] + : execSync('git log --reverse | grep "Date" -m 1').toString().match(/(\d+) \+/)?.[1] +), year_current); +export const year_copyright = year_initial == year_current + ? `${year_initial}` : `${year_initial} - ${year_current}`; + +export const team_dashed = team.toLowerCase().replace(/\s/gi, '-'); +export const title_dashed = title.toLowerCase().replace(/\s/gi, '-'); +export const game_dir = `${team_dashed}-${title_dashed}`; +export const build_path = `./dist/${game_dir}/`; + diff --git a/automation/dev.ts b/automation/dev.ts new file mode 100644 index 0000000..efe86c7 --- /dev/null +++ b/automation/dev.ts @@ -0,0 +1,5 @@ +import { execSync } from 'child_process'; +import WriteNeuConfig from './write-neu-config'; + +WriteNeuConfig(); +execSync('vite', { stdio: 'inherit' }); \ No newline at end of file diff --git a/automation/git-version.ts b/automation/git-version.ts new file mode 100644 index 0000000..4a800d4 --- /dev/null +++ b/automation/git-version.ts @@ -0,0 +1,20 @@ +import { PluginOption } from 'vite'; +import { writeFileSync } from 'fs'; +import { git_count, git_short, git_version, title, team } from './constants'; + +const WriteGitVersion = () => { + writeFileSync('./src/version.json', JSON.stringify({ + title, + team, + count: git_count, + short: git_short, + version: git_version + })); +} + +export default function writeGitVersion() { + return { + name: 'write-version-json', + buildStart: WriteGitVersion + } as PluginOption; +} diff --git a/automation/linux-bundle.ts b/automation/linux-bundle.ts new file mode 100644 index 0000000..ab4e58c --- /dev/null +++ b/automation/linux-bundle.ts @@ -0,0 +1,25 @@ +import { PluginOption } from 'vite'; +import { title_dashed, game_dir, build_path } from './constants'; +import { mkdirSync, copyFileSync } from 'fs'; + +const BuildWinApp = () => { + console.log(`Packaging Linux app...`); + + const out_dir = `./dist/linux/${title_dashed}`; + + mkdirSync('./dist/linux'); + mkdirSync(out_dir); + + copyFileSync(`${build_path}/${game_dir}-linux_x64`, `${out_dir}/${title_dashed}-x64`); + copyFileSync(`${build_path}/${game_dir}-linux_arm64`, `${out_dir}/${title_dashed}-arm64`); + copyFileSync(`${build_path}/${game_dir}-linux_armhf`, `${out_dir}/${title_dashed}-armhf`); + copyFileSync(`${build_path}/resources.neu`, `${out_dir}/resources.neu`); +}; + +export default function buildWinApp() { + return { + name: 'build-linux-bundle', + apply: 'build', + closeBundle: BuildWinApp, + } as PluginOption; +} diff --git a/automation/mac-bundle.ts b/automation/mac-bundle.ts new file mode 100644 index 0000000..e3d44eb --- /dev/null +++ b/automation/mac-bundle.ts @@ -0,0 +1,64 @@ +import { PluginOption } from 'vite'; +import { team, title, git_count, git_version, team_dashed, + title_dashed, game_dir, build_path, year_copyright } from './constants'; +import { execSync } from 'child_process'; +import { mkdirSync, writeFileSync, copyFileSync, renameSync } from 'fs'; + +const BuildMacApp = () => { + console.log(`Packaging Mac dmg...`); + + const plist = ` + + + + NSHumanReadableCopyright + ${title} ${git_version} © ${team} ${year_copyright} + CFBundleExecutable + game + CFBundleIdentifier + com.${team_dashed}.${title_dashed} + CFBundleName + ${title} + CFBundleIconFile + icon.png + CFBundleShortVersionString + 0.${git_count} + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + APPL + IFMajorVersion + 0 + IFMinorVersion + ${git_count} + +`; + + const out_dir = `./dist/mac/${title}`; + + mkdirSync(`./dist/mac/`); + mkdirSync(out_dir); + mkdirSync(`${out_dir}/Contents`); + mkdirSync(`${out_dir}/Contents/MacOS`); + mkdirSync(`${out_dir}/Contents/Resources`); + + writeFileSync(`${out_dir}/Contents/info.plist`, plist); + copyFileSync(`${build_path}/${game_dir}-mac_universal`, `${out_dir}/Contents/MacOS/game`); + copyFileSync(`${build_path}/resources.neu`, `${out_dir}/Contents/MacOS/resources.neu`); + copyFileSync(`./src/public/icon.png`, `${out_dir}/Contents/Resources/icon.png`); + renameSync(out_dir, `${out_dir}.app`); + + try { + execSync(`mkisofs -J -R -o ./dist/${title_dashed}-mac.dmg -mac-name -V "${title}" -apple -v -dir-mode 777 -file-mode 777 "./dist/mac/"`); + } catch (err) { + console.log(`Failed to build dmg`); + } +}; + +export default function buildMacApp() { + return { + name: 'build-mac-bundle', + apply: 'build', + closeBundle: BuildMacApp, + } as PluginOption; +} diff --git a/automation/neu-build.ts b/automation/neu-build.ts new file mode 100644 index 0000000..93af7e5 --- /dev/null +++ b/automation/neu-build.ts @@ -0,0 +1,16 @@ +import { execSync } from 'child_process'; +import { PluginOption } from 'vite'; +import WriteNeuConfig from './write-neu-config'; + +export default function neuBuild(): PluginOption { + return { + name: 'neu-build', + apply: 'build', + enforce: 'pre', + closeBundle() { + console.log('Building game app'); + WriteNeuConfig(); + execSync('neu build'); + }, + }; +} diff --git a/automation/neu-dev.ts b/automation/neu-dev.ts new file mode 100644 index 0000000..81eaf48 --- /dev/null +++ b/automation/neu-dev.ts @@ -0,0 +1,10 @@ +import { exec } from 'child_process'; +import WriteNeuConfig from './write-neu-config'; + +WriteNeuConfig(); +const vite = exec('vite'); +vite.stdout?.pipe(process.stdout); +exec('neu run --frontend-lib-dev -- --window-enable-inspector=true').on('close', () => { + vite.kill(); + process.exit(); +}); diff --git a/automation/neu-inject.ts b/automation/neu-inject.ts new file mode 100644 index 0000000..4aa162d --- /dev/null +++ b/automation/neu-inject.ts @@ -0,0 +1,38 @@ +import { PluginOption } from 'vite'; +import { readFileSync } from 'fs'; + +const tryCatch = (fun: () => T, fallback: T): T => { + try { + return fun(); + } catch(e) { + return fallback; + } +} + +const getGlobalsPath = () => { + const isDev = process.env.NODE_ENV == 'development'; + const authInfo = JSON.parse(tryCatch(() => readFileSync('.tmp/auth_info.json').toString(), '{}')); + const port = authInfo.port; + return `${(isDev && port) ? `http://localhost:${port}/` : ''}__neutralino_globals.js` +} + +export default function neuInject(): PluginOption { + return { + name: 'neu-inject', + transformIndexHtml: (html) => { + return { + html, + tags: [{ + tag: 'script', + injectTo: 'head-prepend', + attrs: { + src: getGlobalsPath() + }, + }] + }; + } + }; +} + + + diff --git a/automation/neu-template.json b/automation/neu-template.json new file mode 100644 index 0000000..1c665e7 --- /dev/null +++ b/automation/neu-template.json @@ -0,0 +1,43 @@ +{ + "applicationId": "chocobois-jam-template", + "version": "1.0.0", + "defaultMode": "window", + "port": 0, + "documentRoot": "/dist/web", + "url": "/", + "enableServer": true, + "enableNativeAPI": true, + "tokenSecurity": "one-time", + "logging": { + "enabled": true, + "writeToLogFile": false + }, + "nativeAllowList": ["app.exit", "window.center"], + "modes": { + "window": { + "title": "Chocobois Game Jam Template", + "width": 1440, + "height": 845, + "minWidth": 960, + "minHeight": 570, + "fullScreen": false, + "alwaysOnTop": false, + "icon": "src/public/icon.png", + "enableInspector": false, + "borderless": false, + "maximize": false, + "hidden": false, + "resizable": true, + "exitProcessOnClose": true + } + }, + "cli": { + "binaryName": "chocobois-jam-template", + "resourcesPath": "/dist/", + "binaryVersion": "4.12.0", + "frontendLibrary": { + "patchFile": "/src/index.html", + "devUrl": "http://127.0.0.1:5173/" + } + } +} \ No newline at end of file diff --git a/automation/pre-image-optimizer.ts b/automation/pre-image-optimizer.ts new file mode 100644 index 0000000..2f32a34 --- /dev/null +++ b/automation/pre-image-optimizer.ts @@ -0,0 +1,8 @@ +import { ViteImageOptimizer } from 'vite-plugin-image-optimizer'; + +export default (options?: Parameters[0]) => { + const optimizer = ViteImageOptimizer(options); + optimizer.closeBundle = { sequential: true, handler: optimizer.closeBundle as any} + optimizer.enforce = 'pre'; + return optimizer; +} diff --git a/automation/setup.ts b/automation/setup.ts new file mode 100644 index 0000000..9d1b074 --- /dev/null +++ b/automation/setup.ts @@ -0,0 +1,5 @@ +import { execSync } from 'child_process'; +import WriteNeuConfig from './write-neu-config'; + +WriteNeuConfig(); +execSync('neu update', { stdio: 'inherit' }); diff --git a/automation/win-bundle.ts b/automation/win-bundle.ts new file mode 100644 index 0000000..51d2570 --- /dev/null +++ b/automation/win-bundle.ts @@ -0,0 +1,54 @@ +import { PluginOption } from 'vite'; +import { title, title_dashed, game_dir, build_path, git_count, description, git_version, team } from './constants'; +import { mkdirSync, copyFileSync, readFileSync, writeFileSync } from 'fs'; +import { NtExecutable, NtExecutableResource, Data, Resource } from 'resedit'; +import pngToIco from 'png-to-ico'; + +const BuildWinApp = async () => { + console.log(`Packaging Windows exe...`); + + const out_dir = `./dist/win/${title_dashed}`; + + mkdirSync('./dist/win'); + mkdirSync(out_dir); + copyFileSync(`${build_path}/resources.neu`, `${out_dir}/resources.neu`); + + const data = readFileSync(`${build_path}/${game_dir}-win_x64.exe`); + const exe = NtExecutable.from(data); + const res = NtExecutableResource.from(exe); + + const pngData = readFileSync('./src/public/icon.png'); + const iconFile = Data.IconFile.from(await pngToIco(pngData)); + Resource.IconGroupEntry.replaceIconsForResource( + res.entries, 101, 1033, + iconFile.icons.map((item) => item.data) + ); + + const vi = Resource.VersionInfo.createEmpty(); + vi.setFileVersion(0, 0, Number(git_count), 0, 1033); + vi.setStringValues( + { lang: 1033, codepage: 1200 }, + { + FileDescription: description, + ProductName: `${title} by ${team}`, + ProductVersion: git_version, + CompanyName: team, + } + ); + vi.outputToResourceEntries(res.entries); + + res.outputResource(exe); + writeFileSync(`${out_dir}/${title}.exe`, Buffer.from(exe.generate())); +}; + +export default function buildWinApp() { + return { + name: 'build-windows-bundle', + apply: 'build', + enforce: 'pre', + closeBundle: { + handler: BuildWinApp, + sequential: true + }, + } as PluginOption; +} diff --git a/automation/write-neu-config.ts b/automation/write-neu-config.ts new file mode 100644 index 0000000..7d95736 --- /dev/null +++ b/automation/write-neu-config.ts @@ -0,0 +1,18 @@ +import { team, title, team_dashed, title_dashed, git_count, neutralino } from './constants'; +import neuConf from './neu-template.json'; +import { writeFileSync } from 'fs'; + +export default function WriteNeuConfig() { + neuConf.applicationId = `${team_dashed}.${title_dashed}`; + neuConf.modes.window.title = `${title} by ${team}`; + neuConf.cli.binaryName = `${team_dashed}-${title_dashed}`; + neuConf.version = `0.0.${git_count}`; + if(process.env.NODE_ENV == 'development') { + neuConf.tokenSecurity = 'none'; + } + + neuConf.nativeAllowList = [...neuConf.nativeAllowList, ...neutralino.allow]; + + writeFileSync('neutralino.config.json', JSON.stringify(neuConf)); + writeFileSync('./src/public/__neutralino_globals.js', '// Dummy file'); +} diff --git a/game.config.json b/game.config.json new file mode 100644 index 0000000..13e8722 --- /dev/null +++ b/game.config.json @@ -0,0 +1,16 @@ +{ + "team": "Chocobois", + "title": "Rerooted", + "description": "Grow a tree, navigate your roots in the underground and upgrade your tree!", + "itch": { + "upload": true, + "username": "golen", + "game": "rerooted" + }, + "neutralino": { + "allow": [ + "app.exit", + "window.center" + ] + } +} diff --git a/neutralino.config.json b/neutralino.config.json deleted file mode 100644 index 06959b0..0000000 --- a/neutralino.config.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "applicationId": "rerooted-chocobois-ggj2023", - "version": "1.0.0", - "defaultMode": "window", - "port": 0, - "documentRoot": "/dist/unpacked", - "url": "/", - "enableServer": true, - "enableNativeAPI": true, - "tokenSecurity": "one-time", - "logging": { - "enabled": true, - "writeToLogFile": true - }, - "nativeAllowList": [ - "app.exit", - "os.showMessageBox" - ], - "modes": { - "window": { - "title": "Rerooted - Chocobois Global Game Jam 2023", - "width": 960, - "height": 570, - "minWidth": 960, - "minHeight": 570, - "fullScreen": true, - "alwaysOnTop": false, - "icon": "src/public/icon.png", - "enableInspector": false, - "borderless": false, - "maximize": false, - "hidden": false, - "resizable": true, - "exitProcessOnClose": true - } - }, - "cli": { - "binaryName": "Rerooted-GGJ2023", - "resourcesPath": "/dist/", - "clientLibrary": "/src/neutralino.js", - "binaryVersion": "4.9.0", - "clientVersion": "3.8.0" - } -} \ No newline at end of file diff --git a/package.json b/package.json index 0596b3d..9be2dcf 100644 --- a/package.json +++ b/package.json @@ -1,25 +1,33 @@ { - "name": "rerooted-chocobois-ggj2023", - "private": true, - "version": "0.0.0", - "type": "module", - "license": "unlicensed", - "scripts": { - "start": "vite", - "dev": "vite", - "build": "rimraf dist && vite build", - "postinstall": "neu update" - }, - "devDependencies": { - "@neutralinojs/neu": "^9.4.0", - "rimraf": "^4.1.2", - "typescript": "^4.9.3", - "vite": "^4.1.0", - "vite-plugin-checker": "^0.5.5", - "vite-plugin-zip-pack": "^1.0.5" - }, - "dependencies": { - "phaser": "^3.55.2", - "phaser3-rex-plugins": "^1.1.80" - } + "name": "rerooted-chocobois-ggj2023", + "version": "0.0.0", + "type": "module", + "license": "MIT", + "scripts": { + "start": "vite-node automation/dev.ts", + "dev": "vite-node automation/dev.ts", + "dev-neu": "vite-node automation/neu-dev.ts", + "build": "rimraf dist && vite build", + "postinstall": "vite-node automation/setup.ts" + }, + "dependencies": { + "@neutralinojs/lib": "^3.10.0", + "phaser": "^3.60.0", + "phaser3-rex-plugins": "^1.60.3" + }, + "devDependencies": { + "@neutralinojs/neu": "^9.6.0", + "png-to-ico": "^2.1.8", + "resedit": "^2.0.0", + "rimraf": "^5.0.1", + "sharp": "^0.32.4", + "terser": "^5.19.2", + "typescript": "^5.1.6", + "vite": "^4.4.7", + "vite-node": "^0.33.0", + "vite-plugin-checker": "^0.6.1", + "vite-plugin-image-optimizer": "^1.1.6", + "vite-plugin-zip-pack": "^1.0.6", + "vite-tsconfig-paths": "^4.2.0" + } } diff --git a/src/components/LimitBreakButton.ts b/src/components/LimitBreakButton.ts index b6dceba..86382c0 100644 --- a/src/components/LimitBreakButton.ts +++ b/src/components/LimitBreakButton.ts @@ -90,7 +90,8 @@ export class LimitBreakButton extends Button { if(this.LState == this.UNUSED) { this.LState = this.USING; - this.sprite.input.enabled = false; + if(this.sprite.input) + this.sprite.input.enabled = false; //this.setInteractive(false); //this.active = !this.active; @@ -111,7 +112,8 @@ export class LimitBreakButton extends Button { this.LState = this.UNUSED; this.sprite.setAlpha(1); this.sprite.setFrame(this.LState); - this.sprite.input.enabled = true; + if(this.sprite.input) + this.sprite.input.enabled = true; this.queueResetFlag = false; } else { this.queueResetFlag = true; diff --git a/src/components/Node.ts b/src/components/Node.ts index 4c29bf3..2cf834e 100644 --- a/src/components/Node.ts +++ b/src/components/Node.ts @@ -27,7 +27,7 @@ export class Node extends Button { if (enabled) { this.bindInteractive(this.image, true); const inputPadding = 30*this.scene.SCALE / this.image.scaleX; - this.image.input.hitArea.setTo(-inputPadding, -inputPadding, this.image.width+2*inputPadding, this.image.height+2*inputPadding); + this.image.input?.hitArea.setTo(-inputPadding, -inputPadding, this.image.width+2*inputPadding, this.image.height+2*inputPadding); // this.scene.input.enableDebug(this.image); } else { diff --git a/src/components/Shop.ts b/src/components/Shop.ts index daa8e1b..2942427 100644 --- a/src/components/Shop.ts +++ b/src/components/Shop.ts @@ -315,7 +315,7 @@ export class Shop extends Phaser.GameObjects.Container { this.ownerButton.add(this.ownerImage); this.ownerButton.bindInteractive(this.ownerImage); - this.ownerImage.input.hitArea.setTo(0, 0, this.ownerImage.width, this.ownerImage.height * 2/3); + this.ownerImage.input?.hitArea.setTo(0, 0, this.ownerImage.width, this.ownerImage.height * 2/3); this.ownerButton.on("down", () => { this.scene.sound.play("s_squish1", {rate: 1 + 0.07*Math.sin(this.scene.time.now/800)}); }); @@ -553,7 +553,8 @@ export class Shop extends Phaser.GameObjects.Container { this.buyButton.enabled = false; this.buyButton.setAlpha(0.5); - this.buyImage.input.cursor = "not-allowed" + if(this.buyImage.input) + this.buyImage.input.cursor = "not-allowed" this.ownerImage.setFrame(0); if (itemData) { @@ -572,7 +573,8 @@ export class Shop extends Phaser.GameObjects.Container { if (this.scene.money >= cost) { this.buyButton.enabled = true; this.buyButton.setAlpha(1.0); - this.buyImage.input.cursor = "pointer" + if(this.buyImage.input) + this.buyImage.input.cursor = "pointer" } } // } diff --git a/src/components/ShopItem.ts b/src/components/ShopItem.ts index eeb622d..d03a11f 100644 --- a/src/components/ShopItem.ts +++ b/src/components/ShopItem.ts @@ -59,7 +59,7 @@ export class ShopItem extends Button { this.bindInteractive(this.background, true); const inputPadding = 0 * this.scene.SCALE / this.background.scaleX; - this.background.input.hitArea.setTo(-inputPadding, -inputPadding, this.background.width+2*inputPadding, this.background.height+2*inputPadding); + this.background.input?.hitArea.setTo(-inputPadding, -inputPadding, this.background.width+2*inputPadding, this.background.height+2*inputPadding); // this.scene.input.enableDebug(this.itemImage); } diff --git a/src/components/SurfaceButton.ts b/src/components/SurfaceButton.ts index ba7ef4e..9f8b644 100644 --- a/src/components/SurfaceButton.ts +++ b/src/components/SurfaceButton.ts @@ -67,7 +67,8 @@ export class SurfaceButton extends Button { + 0.06 * bobValue ); this.alpha += 0.2 * (this.alphaGoal - this.alpha); // Smooth transition - this.image.input.enabled = (this.alphaGoal > 0.5); + if(this.image.input) + this.image.input.enabled = (this.alphaGoal > 0.5); } diff --git a/src/components/Tree.ts b/src/components/Tree.ts index 536d9eb..f2d63b7 100644 --- a/src/components/Tree.ts +++ b/src/components/Tree.ts @@ -271,7 +271,7 @@ export class Tree extends Button { // Make the tree clickable this.bindInteractive(this.treeSprite); const inputPadding = 40 * this.scene.SCALE / this.treeSprite.scaleX; - this.treeSprite.input.hitArea.setTo(-inputPadding, -inputPadding, this.treeSprite.width+2*inputPadding, this.treeSprite.height+2*inputPadding); + this.treeSprite.input?.hitArea.setTo(-inputPadding, -inputPadding, this.treeSprite.width+2*inputPadding, this.treeSprite.height+2*inputPadding); } update(time: number, delta: number) { @@ -709,7 +709,7 @@ export class Tree extends Button { this.treeSprite.setScale(this.scene.SCALE / this.treeSprite.height); // Resize and expand input area const inputPadding = 0.1 * this.scene.SCALE / this.treeSprite.scaleX; - this.treeSprite.input.hitArea.setTo(-inputPadding, -inputPadding, this.treeSprite.width+2*inputPadding, this.treeSprite.height+2*inputPadding); + this.treeSprite.input?.hitArea.setTo(-inputPadding, -inputPadding, this.treeSprite.width+2*inputPadding, this.treeSprite.height+2*inputPadding); this.emit("levelUp", this.level); } diff --git a/src/components/ZombieButton.ts b/src/components/ZombieButton.ts index ce71494..cda8638 100644 --- a/src/components/ZombieButton.ts +++ b/src/components/ZombieButton.ts @@ -92,7 +92,8 @@ export class ZombieButton extends Button { this.backContainer.setAlpha(this.alphaMode); this.sprite.setAlpha(this.alphaMode); this.usedUp = true; - this.sprite.input.enabled = false; + if(this.sprite.input) + this.sprite.input.enabled = false; } } advance(sc: number, inc: number) @@ -119,7 +120,8 @@ export class ZombieButton extends Button { this.sprite.setFrame(0); this.percent.clear(); this.usedUp = false; - this.sprite.input.enabled = true; + if(this.sprite.input) + this.sprite.input.enabled = true; this.hiScore = 0; } diff --git a/src/game.ts b/src/game.ts index 9871985..9332b02 100644 --- a/src/game.ts +++ b/src/game.ts @@ -7,8 +7,8 @@ import OutlinePipelinePlugin from 'phaser3-rex-plugins/plugins/outlinepipeline-p const config: Phaser.Types.Core.GameConfig = { type: Phaser.WEBGL, - width: 1920, - height: 1080, + width: 1920/4, + height: 1080/4, mipmapFilter: 'LINEAR_MIPMAP_LINEAR', // mipmapFilter: 'NEAREST', pixelArt: true, diff --git a/src/index.html b/src/index.html index 99c1aa4..cd68373 100644 --- a/src/index.html +++ b/src/index.html @@ -2,18 +2,18 @@ + %VITE_GAME_TITLE% by %VITE_GAME_TEAM% - Rerooted - Chocobois Global Game Jam 2023 - - + + - - + +

Loading Bundle

diff --git a/src/neu-main.js b/src/neu-main.js deleted file mode 100644 index eef5c78..0000000 --- a/src/neu-main.js +++ /dev/null @@ -1,14 +0,0 @@ -import './neutralino'; - -const NeutralinoLoad = () => { - const onWindowClose = () => { - Neutralino.app.exit(); - } - - Neutralino.init(); - Neutralino.events.on("windowClose", onWindowClose); -} - -if ( window.NL_TOKEN ) { - NeutralinoLoad(); -} diff --git a/src/neu-main.ts b/src/neu-main.ts new file mode 100644 index 0000000..b05a81d --- /dev/null +++ b/src/neu-main.ts @@ -0,0 +1,14 @@ +import * as Neutralino from '@neutralinojs/lib'; +type Neutralino = typeof import('@neutralinojs/lib'); + +const NeutralinoLoad = (Neutralino: Neutralino) => { + Neutralino.window.center(); + Neutralino.events.on("windowClose", () => { + Neutralino.app.exit(); + }); +} + +if((window as any).NL_TOKEN) { + Neutralino.init(); + NeutralinoLoad(Neutralino); +} diff --git a/src/scenes/GameScene.ts b/src/scenes/GameScene.ts index d0b7312..f55829f 100644 --- a/src/scenes/GameScene.ts +++ b/src/scenes/GameScene.ts @@ -947,7 +947,7 @@ export class GameScene extends BaseScene { if(outline) text.setStroke("rgba(0,0,0,0.5)", 120*this.SCALE); // Prevent text from going too far right - const right = text.getRightCenter().x; + const right = text.getRightCenter().x ?? 0; const diff = this.W - right - 80 * this.SCALE; if(diff < 0) text.setX(text.x+diff); diff --git a/src/scenes/PreloadScene.ts b/src/scenes/PreloadScene.ts index c277149..6a158d8 100644 --- a/src/scenes/PreloadScene.ts +++ b/src/scenes/PreloadScene.ts @@ -5,6 +5,7 @@ import { BlurPostFilter } from "../pipelines/BlurPostFilter"; import BendWaves from "../pipelines/BendWavesPostFX"; import BendWaves2 from "../pipelines/BendWavesPostFX2"; +import { title, version } from '@/version.json'; export class PreloadScene extends BaseScene { constructor() { @@ -34,6 +35,7 @@ export class PreloadScene extends BaseScene { // Loading text let text = this.createText(x, y, 16, "#DDDDDD", "Loading...").setOrigin(0, 1.5); + this.createText(5, 5, 12, '#DDDDDD', `${title} ${version}`).setOrigin(0, 0); // Listener this.load.on("progress", (progress: number) => { diff --git a/src/scenes/TitleScene.ts b/src/scenes/TitleScene.ts index 39f9158..6a304a2 100644 --- a/src/scenes/TitleScene.ts +++ b/src/scenes/TitleScene.ts @@ -1,6 +1,7 @@ import { BaseScene } from "./BaseScene"; import { RoundRectangle } from "../components/RoundRectangle"; import { Music } from "./../components/Music"; +import { version } from '@/version.json'; const creditsLeft = `Global Game Jam 2023 @@ -37,6 +38,7 @@ export class TitleScene extends BaseScene { public title: Phaser.GameObjects.Text; public subtitle: Phaser.GameObjects.Text; public tap: Phaser.GameObjects.Text; + public version: Phaser.GameObjects.Text; public musicTitle: Phaser.Sound.WebAudioSound; public select: Phaser.Sound.WebAudioSound; @@ -97,6 +99,12 @@ export class TitleScene extends BaseScene { this.tap.setStroke("#FFF", 40*4*this.SCALE); this.tap.setPadding(2*40*4*this.SCALE); + this.version = this.createText(1, 1, 4*12*this.SCALE, "#000", version); + this.version.setOrigin(0, 0); + this.version.setAlpha(-1); + this.version.setStroke("#FFF", 20); + this.version.setPadding(2); + this.credits = this.add.container(0, 0); this.credits.setVisible(false); this.credits.setAlpha(0); @@ -130,7 +138,7 @@ export class TitleScene extends BaseScene { // Input - this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE).on('down', this.progress, this); + this.input.keyboard?.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE).on('down', this.progress, this); this.input.on('pointerdown', (pointer) => { if (pointer.button == 0) { this.progress(); @@ -151,6 +159,7 @@ export class TitleScene extends BaseScene { if (this.credits.visible) { this.credits.alpha += 0.02 * (1 - this.credits.alpha); + this.version.alpha += 0.02 * ((this.version.visible ? 1 : 0) - this.version.alpha); } } else { diff --git a/tsconfig.json b/tsconfig.json index e3f7c44..58efee0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "ESNext", + "target": "ES2022", "module": "ESNext", "strict": true, "useDefineForClassFields": true, @@ -20,7 +20,11 @@ "removeComments": true, "stripInternal":true, "allowJs": true, - "emitDecoratorMetadata": false + "emitDecoratorMetadata": false, + "paths": { + "@/*": ["./src/*"] + } }, - "include": ["src", "typings"] + "include": ["src", "typings"], + "exclude": ["node_modules"] } diff --git a/vite.config.ts b/vite.config.ts index df0c2a1..27e468b 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,37 +1,70 @@ -import { defineConfig } from 'vite' +import { defineConfig } from 'vite'; + import zip from 'vite-plugin-zip-pack'; -import { execSync } from 'child_process'; -import checker from 'vite-plugin-checker' +import checker from 'vite-plugin-checker'; +import tsconfigPaths from 'vite-tsconfig-paths'; +import getGitVersion from './automation/git-version'; +import preImageOptimizer from './automation/pre-image-optimizer'; +import neuBuild from './automation/neu-build'; +import neuInject from './automation/neu-inject'; +import buildWinApp from './automation/win-bundle'; +import buildMacApp from './automation/mac-bundle'; +import buildLinuxApp from './automation/linux-bundle'; +import buildCleanup from './automation/build-cleanup'; + +import { title, team, description, title_dashed } from './automation/constants'; + +export default () => { + process.env.VITE_GAME_TITLE = title; + process.env.VITE_GAME_TEAM = team; + process.env.VITE_GAME_DESCRIPTION = description; -// https://vitejs.dev/config -export default defineConfig({ - base: './', - root: 'src', - plugins: [ - checker({ - typescript: true - }), - { - name: 'neu-build', - apply: 'build', - closeBundle() { - console.log('Building standalone app') - execSync('neu build --release'); - } - }, - zip({ - inDir: './dist/unpacked', - outDir: './dist', - outFileName: 'game-web.zip' - }) - ], - build: { - outDir: '../dist/unpacked', - chunkSizeWarningLimit: 4096, - assetsInlineLimit: 0, - target: 'esnext' - }, - server: { - host: '127.0.0.1' - } -}); + return defineConfig({ + base: './', + root: 'src', + plugins: [ + tsconfigPaths(), + getGitVersion(), + checker({ + typescript: true, + }), + preImageOptimizer(), + neuBuild(), + neuInject(), + buildWinApp(), + buildMacApp(), + buildLinuxApp(), + zip({ + inDir: './dist/web', + outDir: './dist', + outFileName: `${title_dashed}-web.zip`, + }), + zip({ + inDir: `./dist/win`, + outDir: './dist', + outFileName: `${title_dashed}-win.zip`, + }), + zip({ + inDir: `./dist/linux`, + outDir: './dist', + outFileName: `${title_dashed}-linux.zip`, + }), + buildCleanup(), + ], + build: { + outDir: '../dist/web', + chunkSizeWarningLimit: 4096, + assetsInlineLimit: 0, + target: 'ES2022', + minify: 'terser', + terserOptions: { + format: { + comments: false, + }, + }, + }, + server: { + host: '127.0.0.1', + }, + }); +};