From 452dd58472706aab37ddf28312a0ca0241f0cb27 Mon Sep 17 00:00:00 2001 From: TitanPlayz <118145727+TitanPlayz100@users.noreply.github.com> Date: Tue, 15 Oct 2024 16:49:07 +1100 Subject: [PATCH 1/6] clear delay --- index.html | 1 + info.md | 46 ++++++++++++++++++++++++++--------- src/data/defaultSettings.json | 15 ++++++------ src/mechanics/clearlines.js | 2 ++ src/mechanics/locking.js | 22 +++++++++++------ src/movement/controls.js | 2 +- 6 files changed, 61 insertions(+), 27 deletions(-) diff --git a/index.html b/index.html index 058ad03..63a9327 100644 --- a/index.html +++ b/index.html @@ -182,6 +182,7 @@

Teti

Allow Hold Queue

Infinite Hold

Allow Queue Modification

+

Line Clear Delay

Allspin

Allspin's are mini

Allow history (undo/redo)

diff --git a/info.md b/info.md index 6ee1f5a..89123ee 100644 --- a/info.md +++ b/info.md @@ -16,11 +16,11 @@ This project started out as a learning experience, because surprisingly it was m ## Acknowledgements Thanks [existancepy](https://github.com/existancepy) for all the fixes. -Thanks [ItzBlack6093](https://github.com/ItzBlack6093) for adding TGM Race mode and other fixes. +Thanks [ItzBlack6093](https://github.com/ItzBlack6093) for adding many modes and fixes. Feel free to contribute with features and fixes, and open issues. -Design inspired by [Strangelinx's 'blocks'](https://strangelinx.github.io/blocks/) and [Tetra Legends.](https://tetralegend.app) +Design inspired by [Strangelinx's 'blocks'](https://strangelinx.github.io/blocks/) and [Tetra Legends.](https://tetralegends.app) Icons from [The Noun Project.](https://thenounproject.com) Sound effects from [TETR.IO.](https://tetr.io) Skins from [YHF](https://you.have.fail/ed/at/tetrioplus/) @@ -67,10 +67,28 @@ Skins from [YHF](https://you.have.fail/ed/at/tetrioplus/) ## TODO list Things that I am working on based on other changes -- wow finished +- ready set go start option +- arcade mode + +Maybe: +- cooler action text (maybe if i switch to pixijs then i can make text better) + +Future thoughts (honestly only if i have time): +- I honestly might want to break my rule and start using a framework for rendering. + - (turns out pixijs is much better for games than canvas 2d, and howler for audio) +- I have mixed feelings, because this would make the project technically no longer purely made by me (though the info page is a bit iffy) +- Maybe I don't care and will switch anyway, and maybe in the end it will pay off. +- this would make my most recent changes irrelivent + - such as piece flash and reset animation cause i have to redo them in pixi + - in the future itll probably pay off +*** ## Updates +#### v1.3.3 +- Thanks to itzblack for the Zenith Tower mode +- added option for line clear delay + #### v1.3.2 - reset animation - toggles with stride mode @@ -80,7 +98,7 @@ Things that I am working on based on other changes - added notifications - displays errors - shows export and import messages -- removed assets loading screen, cause it was unecessary +- removed assets loading screen, cause it was unnecessary - added different grid patterns #### v1.3.1 @@ -159,8 +177,8 @@ Things that I am working on based on other changes - can easily add custom gamemodes now using gamemodes JSON - competitive mode - sets certain gamerules, disables custom game settings - - PBs are onyl saved when used -- seperated goals into its own menu + - PBs are only saved when used +- separated goals into its own menu - view pbs in competitive mode menu ### v1.2.0 => COMPETITIVE MODE @@ -203,8 +221,6 @@ Things that I am working on based on other changes *** ## Feature Wishlist Future wants for game, kinda ordered by ease and desire for feature -- ready set go start option -- cooler action text - finesse detection - allow importing tetrio settings and custom game files - small guide on essential things for game @@ -212,14 +228,20 @@ Future wants for game, kinda ordered by ease and desire for feature - WIKI for technical docs about project - glossary of useful terms - more rotation systems (ars, trs, srs/srsX, none) +- more bag systems (pure random, nes random, 14 bag, 7+2 bag) - replay functionality (either save gamestate or save keystrokes idk yet) + - statistics graph - more unique gamemodes (techmino styled) - misdrop remover mode - holdless and next queueless gamemode kinda like qp2 cards - colourblind gamemode -- guide like progression thing? using custom boards (kinda like tetris tres bien) +- guide like progression using custom maps + - maybe like tetris tres bien - achievements, progression tree -- maybe play around with server api stuff, like adding a leaderboard or connecting tetrio stats + - the jstris map creator looks really nice + - could have user made maps section as well +- maybe play around with api stuff + - leaderboards + - connecting tetrio stats - touch settings -- bot to play against -- statistics graph \ No newline at end of file +- bot to play against \ No newline at end of file diff --git a/src/data/defaultSettings.json b/src/data/defaultSettings.json index eb5926c..ade8d14 100644 --- a/src/data/defaultSettings.json +++ b/src/data/defaultSettings.json @@ -27,7 +27,14 @@ "preserveARR": true, "allowHold": true, "infiniteHold": true, + "clearDelay": 0, "gamemode": "sprint", + "allspin": true, + "allspinminis": true, + "history": true, + "competitiveMode": false, + "sidebar": ["time", "apm", "pps"], + "stride": false, "requiredLines": 40, "timeLimit": 120, "requiredAttack": 40, @@ -37,13 +44,7 @@ "allowQueueModify": true, "lookAheadPieces": 3, "raceTarget": 500, - "requiredAltitude": 1650, - "allspin": true, - "allspinminis": true, - "history": true, - "competitiveMode": false, - "sidebar": ["time", "apm", "pps"], - "stride": false + "requiredAltitude": 1650 }, "control": { "rightKey": "ArrowRight", diff --git a/src/mechanics/clearlines.js b/src/mechanics/clearlines.js index 15eeab1..3964865 100644 --- a/src/mechanics/clearlines.js +++ b/src/mechanics/clearlines.js @@ -37,6 +37,7 @@ export class ClearLines { if (clearRows.length > 0) this.game.renderer.bounceBoard("DOWN"); this.game.particles.spawnParticles(0, Math.min(...clearRows), "clear") this.processLineClear(removedGarbage, clearRows); + return clearRows.length; } clearRow(rowNumber) { @@ -66,6 +67,7 @@ export class ClearLines { this.manageGarbageSent(damage); this.game.zenith.AwardLines(damage); + if (linecount == 1) this.game.zenith.AwardLines(1); // render action text if (mech.isAllspin) damagetype = damagetype.replace("Tspin ", this.game.falling.piece.name + " spin "); this.game.renderer.renderActionText(damagetype, isBTB, isPC, damage, linecount); diff --git a/src/mechanics/locking.js b/src/mechanics/locking.js index c6fe641..1300be0 100644 --- a/src/mechanics/locking.js +++ b/src/mechanics/locking.js @@ -4,7 +4,7 @@ export class LockPiece { divLockTimer = document.getElementById("lockTimer"); divLockCounter = document.getElementById("lockCounter"); lockCount; - timings = { lockdelay: 0, lockingTimer: 0 } + timings = { lockdelay: 0, lockingTimer: 0, clearDelay: 0 } startTime = 0; remaining = 0; @@ -73,16 +73,18 @@ export class LockPiece { const lockCoords = this.game.mechanics.board.getMinos("A"); this.game.boardrender.justPlacedCoords = lockCoords; this.game.boardrender.justPlacedAlpha = 1; - + lockCoords.forEach(([x, y]) => { - this.game.boardrender.flashTimes.push({ c:[x, y], t: 15 }) + this.game.boardrender.flashTimes.push({ c: [x, y], t: 15 }) this.game.mechanics.board.rmValue([x, y], "A"); this.game.mechanics.board.addValFront([x, y], "S"); }); + this.game.mechanics.locking.clearLockDelay(); clearInterval(this.game.gravityTimer); - this.game.mechanics.clear.clearLines(lockCoords); - this.game.endGame( // stopped overlap next + const cleared = this.game.mechanics.clear.clearLines(lockCoords); + + this.game.endGame( // check stopped overlap next this.game.mechanics.checkDeath( this.game.mechanics.board.getMinos("S"), this.game.mechanics.board.getMinos("NP") @@ -101,12 +103,18 @@ export class LockPiece { this.game.mechanics.isMini = false; this.game.falling.moved = false; if (this.game.stats.level % 100 != 99 && this.game.stats.level != this.game.settings.game.raceTarget - 1) this.game.stats.level++; + const xvals = [...new Set(lockCoords.map(([x, y]) => x))]; const yval = Math.min(...lockCoords.map(([x, y]) => y)); this.game.particles.spawnParticles(Math.min(...xvals) + 1, yval, "lock", xvals.length); - this.game.mechanics.spawnPiece(this.game.bag.randomiser()); - this.game.history.save(); this.game.renderer.renderDanger(); + + const delay = (cleared > 0) ? this.game.settings.game.clearDelay : 0; + this.timings.clearDelay = setTimeout(() => { + this.game.mechanics.spawnPiece(this.game.bag.randomiser()); + this.game.history.save(); + this.timings.clearDelay = 0; + }, delay); } clearLockDelay(clearCount = true) { diff --git a/src/movement/controls.js b/src/movement/controls.js index 0d4346e..fc44288 100644 --- a/src/movement/controls.js +++ b/src/movement/controls.js @@ -25,7 +25,7 @@ export class Controls { if (key == this.menuKey) this.game.menuactions.toggleDialog(); else if (key == keys.editMenuKey) this.game.menuactions.openEditMenu(); - if (this.game.modals.open || this.game.modals.closing) return; + if (this.game.modals.open || this.game.modals.closing || this.game.mechanics.locking.timings.clearDelay != 0) return; if (event.key != this.menuKey && !this.game.started) this.moves.firstMovement(); if (key == keys.resetKey) this.retry(true); if (this.game.ended) return; From e09a5523f66bfa784835a66f0e0ec38a32162ebf Mon Sep 17 00:00:00 2001 From: TitanPlayz <118145727+TitanPlayz100@users.noreply.github.com> Date: Thu, 17 Oct 2024 21:00:36 +1100 Subject: [PATCH 2/6] Completely changed all rendering to use PIXIjs Changed board, next queue, hold queue, and particles to run using pixijs in webgl, making it much faster. Reimplemented much of the logic for rendering to be more readable. --- .github/workflows/main.yml | 15 +- .gitignore | 5 +- README.md | 18 +- assets/particle.png | Bin 0 -> 171 bytes index.html | 5 +- info.md | 23 +- src/data/blocksprites.json | 20 ++ src/data/defaultSettings.json | 4 +- src/display/boardEffects.js | 6 +- src/display/particles.js | 139 +++++++------ src/display/pixirender.js | 380 ++++++++++++++++++++++++++++++++++ src/display/renderBoard.js | 160 -------------- src/display/renderer.js | 152 ++------------ src/features/sounds.js | 4 +- src/game.js | 20 +- src/main.js | 22 +- src/mechanics/board.js | 10 +- src/mechanics/clearlines.js | 2 +- src/mechanics/locking.js | 8 +- src/menus/modals.js | 2 +- src/movement/controls.js | 7 +- src/movement/movement.js | 4 +- styles/style.css | 4 +- 23 files changed, 581 insertions(+), 429 deletions(-) create mode 100644 assets/particle.png create mode 100644 src/data/blocksprites.json create mode 100644 src/display/pixirender.js delete mode 100644 src/display/renderBoard.js diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 737f7cb..0fbb298 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -3,8 +3,6 @@ name: 'Publish Tauri App' on: workflow_dispatch: -# This workflow will trigger on each push to the `release` branch to create or update a GitHub release, build your app, and upload the artifacts to the release. - jobs: publish-tauri: permissions: @@ -17,7 +15,7 @@ jobs: # args: '--target aarch64-apple-darwin' #- platform: 'macos-latest' # for Intel based macs. # args: '--target x86_64-apple-darwin' - - platform: 'ubuntu-22.04' # for Tauri v1 you could replace this with ubuntu-20.04. + - platform: 'ubuntu-20.04' args: '' - platform: 'windows-latest' args: '' @@ -34,17 +32,13 @@ jobs: - name: install Rust stable uses: dtolnay/rust-toolchain@stable with: - # Those targets are only used on macos runners so it's in an `if` to slightly speed up windows and linux builds. targets: ${{ matrix.platform == 'macos-latest' && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }} - name: install dependencies (ubuntu only) - if: matrix.platform == 'ubuntu-22.04' # This must match the platform value defined above. + if: matrix.platform == 'ubuntu-20.04' run: | sudo apt-get update - sudo apt-get install -y libwebkit2gtk-4.0-dev libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf - # webkitgtk 4.0 is for Tauri v1 - webkitgtk 4.1 is for Tauri v2. - # You can remove the one that doesn't apply to your app to speed up the workflow a bit. - + sudo apt-get install -y libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf - run: | mkdir -p tauri/src @@ -52,6 +46,7 @@ jobs: cp -r assets/ tauri/src/ cp -r styles/ tauri/src/ cp -r src/ tauri/src/ + cp -r lib/ tauri/src/ cd tauri npm install @@ -59,7 +54,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - tagName: app-v__VERSION__ # the action automatically replaces \_\_VERSION\_\_ with the app version. + tagName: app-v__VERSION__ # automatically replaces \_\_VERSION\_\_ with the app version releaseName: 'App v__VERSION__' releaseBody: 'See the assets to download this version and install.' releaseDraft: true diff --git a/.gitignore b/.gitignore index 414c49a..4397437 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .vscode/ -package-lock.json -Cargo.lock \ No newline at end of file +Cargo.lock +*.d.ts +jsconfig.json \ No newline at end of file diff --git a/README.md b/README.md index 60af4c1..6a797a4 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,6 @@ The info page can be found [here](https://titanplayz100.github.io/teti/info.html > Firefox is not supported (due to import assertions) ## Desktop App -### NEW! - Releases are now run through a workflow. They are **up to date** and contain all the latest features. App build using Tauri, feel free to open issues and PRs. @@ -35,7 +33,7 @@ gamemodes = { } ``` -Add functionality mainly in `features/modes.js`. +Add functionality mainly in `features/modes.js`. You can modify existing modules as well from other files ### Adding Audio (sfxlist.json) @@ -48,3 +46,17 @@ You can modify existing modules as well from other files } ``` Use with `this.game.sounds.playSound()` + + +## Types for PIXIjs +Workaround to use types when using pixijs imported through script tag: +- download [pixi.js.d.ts](https://github.com/pixijs/pixijs/releases) from pixijs repo and place file in /src +- setup jsconfig.json to automatically detect pixi.js.d.ts for types + +```json +{ + "typeAcquisition": { + "include": ["pixi.js.d.ts"] + } +} +``` \ No newline at end of file diff --git a/assets/particle.png b/assets/particle.png new file mode 100644 index 0000000000000000000000000000000000000000..3021288d721a5d4561229ab3773e6ea2159ff59c GIT binary patch literal 171 zcmeAS@N?(olHy`uVBq!ia0vp^EFjFn3?!rPH~Ru9#^NA%C&rs6b?Si}&H|6fVg?3o zVGw3ym^DWNC@3A^6XNzopr0L)51oB#j- literal 0 HcmV?d00001 diff --git a/index.html b/index.html index 63a9327..b8fbb1a 100644 --- a/index.html +++ b/index.html @@ -10,6 +10,7 @@ + @@ -18,7 +19,7 @@

⚠ GO DOWN

-
+
- - - - -

60S LEFT

-
+ - -
-

Next

-

Hold

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

- -
- +
+
@@ -79,6 +46,9 @@

Teti

+ @@ -154,7 +124,6 @@

Teti

Grid Type

Shadow opacity

Coloured Shadow

-

Coloured Borders

Rainbow effect on fast pace

Show Lock Bar

Show out of focus text

diff --git a/info.md b/info.md index 18e2262..4811fcc 100644 --- a/info.md +++ b/info.md @@ -69,18 +69,53 @@ Skins from [YHF](https://you.have.fail/ed/at/tetrioplus/) Things that I am working on based on other changes - ready set go start option - arcade mode -- cooler action text + +Zenith mode additions +- add fatigue: +8:00 - 2 permanent garbage +9:00 - receive 25% more garbage +10:00 - 3 more permanent garbage (5 total) +11:00 - debuff increased to 50% more garbage +12:00 - 5 more permanent garbage (10 total) + +- maybe add a "currently in hyperspeed" graphic +- simulated garbage recieving option + - amount of garbage sent to player increases with floor + - garbage is random bursts + - attacks 8 or larger split using {!!} thing (0.5s gap) +- option for expert mode style + +- update climb bar colours: +Red (#e43921) +Yellow (#ffb400) +Green (#82fc40) +Blue (#3ca6ff) +Magenta (#ff46da) +Khaki (#ffc48e) +"Teal Green" (#99ffc6) +"Cyan Blue" (#00f7ff) +"Soft Pink" (#ffbbea) +White (#ffffff) + *** ## Updates -#### v1.3.4 => PIXIjs +#### v1.3.5 +- New board design inspired from [this reddit post](https://www.reddit.com/r/Tetris/comments/1g80adg/tetris_ui_concept/) +- added continue button in menu, and changed ingame buttons to use pixijs now +- experimenting with animation library called gsap +- changed action text and stats text to use pixi now +- changed PC text to be more TETR.IO-like +- changed spike text to show in the middle of the board (like techmino) +- RIP coloured queues + +#### v1.3.4 => PIXI.js - COMPLETE REWRITE OF ALL GRAPHICS - Everything is now rendered through pixijs - this includes the board, next queue, hold queue, and particles - ye turns out there is a high learning curve with pixi (i lost it several times) - everything should now be much more performant and smooth yay - - some things were changed due to this - need to add border colour back diff --git a/src/data/defaultSettings.json b/src/data/defaultSettings.json index 5b69262..13b626f 100644 --- a/src/data/defaultSettings.json +++ b/src/data/defaultSettings.json @@ -8,7 +8,6 @@ "shadowOpacity": 20, "boardHeight": 80, "colouredShadow": false, - "colouredQueues": true, "lockBar": true, "rainbowPB": true, "boardBounce": 2, diff --git a/src/display/boardEffects.js b/src/display/boardEffects.js index 4298107..7d82384 100644 --- a/src/display/boardEffects.js +++ b/src/display/boardEffects.js @@ -18,9 +18,8 @@ export class BoardEffects { paceCooldown = 0; divBoard = document.getElementById("board"); - divDanger = document.getElementById("dangerOverlay"); - border = document.getElementById('backborder') - backboard = document.getElementById('backboard') + // border = document.getElementById('backborder') + // backboard = document.getElementById('backboard') /** * @@ -101,14 +100,10 @@ export class BoardEffects { } toggleRainbow(pace) { + return; this.border.style.setProperty('--blur-size', pace ? `0.3vmin` : `0vmin`) this.border.style.setProperty('--blur-strength', pace ? '0.7vmin' : '0') this.backboard.style.setProperty('--blur-strength', pace ? '0.5vmin' : '0') this.hasPace = pace; } - - toggleDangerBoard(inDanger) { - this.border.classList.toggle("boardDanger", inDanger); - this.divDanger.style.opacity = inDanger ? "0.1" : "0"; - } } \ No newline at end of file diff --git a/src/display/particles.js b/src/display/particles.js index ca5dace..b7253f4 100644 --- a/src/display/particles.js +++ b/src/display/particles.js @@ -20,10 +20,9 @@ class Point { this.twinkle = twinkle ?? false; this.twinkleTime = twinkleTime ?? this.life; - const particle = new PIXI.Sprite(particleInstance.texture); - this.particle = particle; + this.particle = particleInstance.game.pixi.createParticleSprite() this.particle.tint = colour; - particleInstance.container.addChild(particle); + particleInstance.container.addChild(this.particle); particleInstance.particles.push(this); } @@ -62,10 +61,6 @@ export class Particles { this.container = this.game.pixi.app.stage.getChildByLabel("particles"); } - async loadTexture() { - this.texture = await PIXI.Assets.load('./assets/particle.png'); - } - spawnParticles(posX, posY, type, pieceWidth = 1, cw = false, colour = "white") { if (!this.game.settings.display.particles) return; diff --git a/src/display/pixirender.js b/src/display/pixirender.js index 941191f..20e3b77 100644 --- a/src/display/pixirender.js +++ b/src/display/pixirender.js @@ -1,19 +1,19 @@ import { Game } from '../game.js'; import { defaultSkins } from '../data/data.js'; import blocksprites from '../data/blocksprites.json' with { type: 'json' }; +import { clearSplash } from '../main.js'; export class PixiRender { textures = {}; minoSize; width; height; - resetAnimLength = 30; - resetAnimCurrent = 30; boardAlpha = 1; queueAlpha = 1; justPlacedCoords = []; justPlacedAlpha = 1; - flashTimes = []; + shadowSprites = {}; + editButtonVisible = false; divlock = document.getElementById("lockTimer"); @@ -26,7 +26,7 @@ export class PixiRender { async init() { this.app = new PIXI.Application(); - await this.app.init({ backgroundAlpha: 0, resizeTo: window }); + await this.app.init({ backgroundAlpha: 0, resizeTo: window, autoDensity: true }); document.body.appendChild(this.app.canvas); // grid @@ -47,6 +47,10 @@ export class PixiRender { this.app.stage.addChild(hold); hold.label = "hold"; + const textContainer = new PIXI.Container(); + this.app.stage.addChild(textContainer); + textContainer.label = "textContainer"; + // particles const particles = new PIXI.Container(); this.app.stage.addChild(particles); @@ -54,14 +58,18 @@ export class PixiRender { this.game.particles.initBoard(); // init - this.generateTextures(); - this.game.particles.loadTexture(); + await this.generateTextures(); this.resize(); this.generateAllSprites("board", this.game.board.boardState, 39, [0, 0]); this.generateAllSprites("hold", this.game.renderer.holdQueueGrid, 2, [0, 0]); this.generateAllSprites("next", this.game.renderer.nextQueueGrid, 15, [0, 0]); + this.game.renderer.updateNext(); + this.game.renderer.updateHold(); this.app.ticker.add(time => this.tick(time)); + + gsap.registerPlugin(PixiPlugin); + PixiPlugin.registerPIXI(PIXI); } resize() { @@ -70,10 +78,12 @@ export class PixiRender { const hold = this.app.stage.getChildByLabel("hold"); const board = this.app.stage.getChildByLabel("board"); const particles = this.app.stage.getChildByLabel("particles"); + const textContainer = this.app.stage.getChildByLabel("textContainer"); // clear grid.children.forEach(child => child.destroy()); grid.removeChildren(); + textContainer.removeChildren(); // resize const scale = Number(this.game.settings.display.boardHeight) / 100; @@ -94,34 +104,62 @@ export class PixiRender { board.pivot.y = height * 3 / 2; // hold + const clickRectHold = new PIXI.Graphics().rect(0, 0, width * 2 / 5, height * 3 / 20).fill("transparent"); + clickRectHold.interactive = true; + clickRectHold.cursor = 'pointer' + clickRectHold.on("pointerdown", () => this.game.modals.openModal("queueModify")); + clickRectHold.label = "invincible" + hold.addChild(clickRectHold); hold.x = screenWidth; - hold.y = screenHeight; + hold.y = screenHeight + height * 2 / 20; hold.pivot.x = width / 2 + width * 2 / 5; hold.pivot.y = height / 2; // next + const clickRectNext = new PIXI.Graphics().rect(0, 0, width * 2 / 5, height * 16 / 20).fill("transparent"); + clickRectNext.interactive = true; + clickRectNext.cursor = 'pointer' + clickRectNext.on("pointerdown", () => this.game.modals.openModal("queueModify")); + clickRectNext.label = "invincible" + next.addChild(clickRectNext); next.x = screenWidth; - next.y = screenHeight; - next.pivot.x = width / 2 - width; + next.y = screenHeight + height * 1 / 20; + next.pivot.x = width / 2 - width * 11 / 10; next.pivot.y = height / 2; // grid and outline - const rectGrid = new PIXI.Graphics() + const { textHold, textNext } = this.textGraphics(width); + const { settings, reset, edit } = this.buttonGraphics(width); + + this.boardBG = new PIXI.Graphics() .rect(0, 0, width, height) - .stroke({ color: 0xffffff, width: 1, alignment: 0.5 }) - grid.addChild(rectGrid); + .fill("red") + this.boardBG.alpha = 0; + grid.addChild(this.boardBG); + + this.border = new PIXI.Graphics() + .lineTo(0, height).lineTo(width, height).lineTo(width, 0) + .stroke({ color: 0xffffff, width: 2, alignment: 0.5 }) + grid.addChild(this.border); const rectHold = new PIXI.Graphics() - .rect(0, 0, width * 2 / 5, height * 3 / 20) + .moveTo(0, height * 1 / 4).lineTo(width * 2 / 5, height * 1 / 4) .stroke({ color: 0xffffff, width: 1 }) grid.addChild(rectHold); rectHold.x = - width * 2 / 5; + grid.addChild(textHold); const rectNext = new PIXI.Graphics() - .rect(0, 0, width * 2 / 5, height * 16 / 20) + .moveTo(0, height * 17 / 20).lineTo(width * 2 / 5, height * 17 / 20) .stroke({ color: 0xffffff, width: 1 }) grid.addChild(rectNext); rectNext.x = width; + grid.addChild(textNext); + + grid.addChild(settings); + grid.addChild(reset); + grid.addChild(edit); + this.editButton = edit; grid.x = screenWidth; grid.y = screenHeight; @@ -134,29 +172,194 @@ export class PixiRender { particles.pivot.x = width / 2; particles.pivot.y = height * 3 / 2; + // action text + textContainer.x = screenWidth; + textContainer.y = screenHeight; + textContainer.pivot.x = width / 2; + textContainer.pivot.y = height / 2; + + textContainer.addChild(this.actionTexts.cleartext.sprite) + textContainer.addChild(this.actionTexts.combotext.sprite) + textContainer.addChild(this.actionTexts.btbtext.sprite) + textContainer.addChild(this.actionTexts.spiketext.sprite) + textContainer.addChild(this.actionTexts.pctext.sprite) + this.statTexts.forEach((text) => { + textContainer.addChild(text.stat); + textContainer.addChild(text.statText); + textContainer.addChild(text.statSecondary); + }); + textContainer.addChild(this.objectiveTexts[0]) + textContainer.addChild(this.objectiveTexts[1]) + this.generateGrid(); + this.resetAnimGraphic(); } - // GENRATORS - async generateTextures() { - let url = this.game.settings.display.skin; - if (defaultSkins.includes(url)) url = `./assets/skins/${url}.png`; - const texture = await PIXI.Assets.load(url); - const spritesheet = new PIXI.Spritesheet(texture, blocksprites); - await spritesheet.parse(); - this.textures = spritesheet.textures; - this.game.renderer.updateNext(); - this.game.renderer.updateHold(); + // TEXT + textGraphics(width) { + const style = new PIXI.TextStyle({ fontFamily: "Major Mono Display", fontSize: 18, fill: 0xffffff, fontWeight: "bold", letterSpacing: 1 }); + + const textHold = new PIXI.Text({ text: "hold", style }); + textHold.x = - width * 3.5 / 10; + textHold.resolution = 2; + + const textNext = new PIXI.Text({ text: "next", style }); + textNext.x = width * 11 / 10; + textNext.resolution = 2; + + const actionStyle = new PIXI.TextStyle({ fontFamily: "Major Mono Display", fontSize: 24, fill: "white", fontWeight: "bold" }); + const actionText = (pos) => { + const text = new PIXI.Text({ text: "", style: actionStyle }); + text.resolution = 2; + text.alpha = 0; + text.y = pos * width * 1.5 / 10 + width * 3 / 5; + text.anchor.x = 1; + text.text = "" + return { sprite: text, animation: gsap.timeline() }; + } - const triangleGraphic = new PIXI.Graphics().poly([0, 0, 10, 0, 0, 10]).fill(0xffffff, 0.4); - this.triangle = this.app.renderer.generateTexture(triangleGraphic); + const pcstyle = new PIXI.TextStyle({ fontFamily: "Major Mono Display", fontSize: 24, fill: "yellow", fontWeight: "bold", stroke: { color: "orange", width: 2 } }); + const pcText = new PIXI.Text({ text: "perfect \n clear", style: pcstyle }); + pcText.resolution = 2; + pcText.alpha = 0; + pcText.x = this.width / 2; + pcText.y = this.height / 2; + pcText.pivot.x = pcText.width / 2 - 10; + pcText.pivot.y = pcText.height / 2; + + const spiketext = actionText(3); + spiketext.sprite.x = this.width / 2; + spiketext.sprite.y = this.height * 1 / 4; + spiketext.sprite.anchor.x = 0.5; + spiketext.sprite.anchor.y = 0.5; + + this.actionTexts = { + cleartext: actionText(0), + combotext: actionText(1), + btbtext: actionText(2), + spiketext, + pctext: { sprite: pcText, animation: gsap.timeline() } + } + + const statStyle = new PIXI.TextStyle({ fontFamily: "Major Mono Display", fontSize: 16, fill: "white", fontWeight: "bold" }); + const statTextStyle = new PIXI.TextStyle({ fontFamily: "Montserrat", fontSize: 18, fill: "#999999" }); + const statSecondaryStyle = new PIXI.TextStyle({ fontFamily: "Major Mono Display", fontSize: 26, fill: "white", fontWeight: "bold" }); + const statText = (pos) => { + const stat = new PIXI.Text({ text: "", style: statStyle }); + stat.resolution = 2; + stat.position.set(-width * 1 / 20, this.height - pos * this.height * 2.5 / 20) + stat.anchor.x = 1; + const statText = new PIXI.Text({ text: "", style: statTextStyle }); + statText.resolution = 2; + statText.position.set(-width * 1 / 20, this.height - pos * this.height * 2.5 / 20 - this.height * 2 / 40) + statText.anchor.x = 1; + const statSecondary = new PIXI.Text({ text: "", style: statSecondaryStyle }); + statSecondary.resolution = 2; + statSecondary.position.set(-width * 7 / 20, this.height - pos * this.height * 2.5 / 20 - this.height * 1 / 40) + statSecondary.anchor.x = 1; + return { stat, statText, statSecondary }; + } + + this.statTexts = [ + statText(0.5), + statText(1.5), + statText(2.5) + ] + + const objectiveText = new PIXI.Text({ text: "", style: statSecondaryStyle }); + objectiveText.resolution = 2; + objectiveText.position.set(width*11/10, this.height - this.height * 3 / 40) + + const objectiveNameText = new PIXI.Text({ text: "", style: statTextStyle }); + objectiveNameText.resolution = 2; + objectiveNameText.position.set(width*11/10, this.height - this.height * 5/40) + + this.objectiveTexts = [objectiveText, objectiveNameText]; + + return { textHold, textNext }; + } + + // todo reset action text + showActionText(type, message) { + this.actionTexts[type].animation.kill(); + const text = this.actionTexts[type].sprite; + text.text = message; + this.actionTexts[type].animation = gsap.timeline({ onComplete: () => this.game.mechanics.spikeCounter = 0 }) + .to(text, { duration: 0.2, pixi: { alpha: 1, x: - this.width * 1 / 10, scaleX: 1 }, ease: "power1.inOut" }) + .to(text, { duration: 2, pixi: { alpha: 1, scaleX: 1.1 } }) + .to(text, { duration: 0.2, pixi: { alpha: 0, x: 0 }, ease: "power1.inOut" }) + } + + showSpikeText(num) { + this.actionTexts.spiketext.animation.kill(); + const { colour, power } = this.spikeTextStyle(); + const text = this.actionTexts.spiketext.sprite; + text.text = num; + this.actionTexts.spiketext.animation = gsap.timeline() + .to(text, { duration: 0.1, pixi: { alpha: 0.8, tint: colour, scale: (1 + power / 8) } }) + .to(text, { duration: (1 + power / 8) / power, pixi: { alpha: 0.2 }, ease: "power2.in", repeat: power - 1 }) + .to(text, { duration: 0.2, pixi: { alpha: 0 }, ease: "power1.inOut" }) + } + + spikeTextStyle() { + if (this.game.mechanics.spikeCounter >= 20) return { colour: "#fad9f7", power: 12 }; + if (this.game.mechanics.spikeCounter >= 15) return { colour: "#7ac9fa", power: 8 }; + if (this.game.mechanics.spikeCounter >= 10) return { colour: "#faa823", power: 4 }; + return { colour: "white", power: 1 }; + } + + showPCText() { + this.actionTexts.pctext.animation.kill(); + const pc = this.actionTexts.pctext.sprite; + this.actionTexts.pctext.animation = gsap.timeline() + .to(pc, { duration: 0, pixi: { alpha: 1, scale: 0, rotation: -180 } }) + .to(pc, { duration: 0.5, pixi: { scale: 1.6, rotation: 360 }, ease: "power1.inOut" }) + .to(pc, { duration: 3.5 / 15, pixi: { tint: "orange" }, ease: "power1.inOut", repeat: 15, yoyo: true }) + .to(pc, { duration: 2.5, pixi: { scale: 0, alpha: 0 }, ease: "power3.in" }, "1.5") + } + + // GRAPHICS and GENERATORS + buttonGraphics(width) { + const iconframe = (texture, scale, y) => { + const icon = new PIXI.Sprite(texture) + icon.scale.set(scale) + icon.x = width * 1.525 + icon.y = y + icon.interactive = true + icon.cursor = 'pointer' + icon.alpha = 0.6 + icon.on("pointerover", () => gsap.to(icon, { duration: 0.1, pixi: { alpha: 1, scale: scale * 1.1 }, ease: "power1.inOut" })) + icon.on("pointerout", () => gsap.to(icon, { duration: 0.3, pixi: { alpha: 0.6, scale: scale }, ease: "power1.inOut" })) + return icon + } + + const settings = iconframe(this.settingsIcon, 0.18, 0) + settings.on("pointerdown", () => this.game.modals.openModal("settingsPanel")); + + const reset = iconframe(this.resetIcon, 0.23, width * 3 / 20) + reset.on("pointerdown", () => this.game.controls.retry(true)); + + const edit = iconframe(this.editIcon, 0.21, width * 6 / 20) + edit.on("pointerdown", () => this.game.modals.openModal("editMenu")); + edit.visible = this.editButtonVisible + return { settings, reset, edit }; + } + + toggleEditButton(bool) { + this.editButtonVisible = bool + this.editButton.visible = this.editButtonVisible; + } + + resetAnimGraphic() { + if (this.resetTriangle) this.resetTriangle.destroy(); const triangleGraphic2 = new PIXI.Graphics().poly([0, 0, 100, 0, 0, 100]).fill(0xffffff); triangleGraphic2.rotation = Math.PI * 3 / 2 triangleGraphic2.y = this.height * 2 triangleGraphic2.label = "invincible" this.resetTriangle = triangleGraphic2; + if (this.resetMask) this.resetMask.destroy(); const maskTriangle = new PIXI.Graphics().poly([-this.height, 0, this.width, 0, this.width, this.height + this.width]).fill(0xffffff, 0.4); maskTriangle.x = this.width; maskTriangle.y = this.height; @@ -165,25 +368,47 @@ export class PixiRender { this.resetMask = maskTriangle; } + async generateTextures() { + let url = this.game.settings.display.skin; + if (defaultSkins.includes(url)) url = `./assets/skins/${url}.png`; + const texture = await PIXI.Assets.load(url); + const spritesheet = new PIXI.Spritesheet(texture, blocksprites); + await spritesheet.parse(); + this.textures = spritesheet.textures; + + this.settingsIcon = await PIXI.Assets.load('./assets/icons/settings.svg'); + this.resetIcon = await PIXI.Assets.load('./assets/icons/reset.svg'); + this.editIcon = await PIXI.Assets.load('./assets/icons/edit.svg'); + + const triangleGraphic = new PIXI.Graphics().poly([0, 0, 10, 0, 0, 10]).fill(0xffffff, 0.4); + this.triangle = this.app.renderer.generateTexture(triangleGraphic); + + this.game.particles.texture = await PIXI.Assets.load('./assets/particle.png'); + clearSplash(); + } + generateAllSprites(type, array, yPosChange, [dx, dy] = [0, 0]) { const container = this.app.stage.getChildByLabel(type); - + const shadowArray = []; array.forEach((row, y) => { + const shadowRow = [] row.forEach((col, x) => { const posX = x * this.minoSize; const posY = (yPosChange - y) * this.minoSize; const sprite = new PIXI.Sprite(this.textures['g']); - sprite.x = (posX + dx); - sprite.y = (posY + dy); - sprite.width = this.minoSize; - sprite.height = this.minoSize; + sprite.position.set(posX + dx, posY + dy); + sprite.setSize(this.minoSize); sprite.visible = false; - sprite.label = `invincible ${x}${y}`; + sprite.label = "invincible"; + shadowRow.push(sprite); container.addChild(sprite); }); + shadowArray.push(shadowRow); }); + this.shadowSprites[type] = shadowArray } + generateGrid() { if (this.game.settings.display.showGrid === false) return; const grid = this.app.stage.getChildByLabel("grid"); @@ -220,7 +445,6 @@ export class PixiRender { this.game.boardeffects.rotate(0); this.game.particles.update(); this.game.renderer.dangerParticles(); - this.game.pixi.resetAnimation(); this.updateAlpha(); if (this.game.settings.game.gamemode == "ultra" && Math.floor(this.game.stats.time) == 60) this.game.renderer.renderTimeLeft("60S LEFT"); if (this.game.settings.game.gamemode == "ultra" && Math.floor(this.game.stats.time) == 90) this.game.renderer.renderTimeLeft("30S LEFT"); @@ -228,12 +452,10 @@ export class PixiRender { // RENDERING render(type, array) { - if (this.app === undefined) return; - - /** @type {PIXI.Container} */ const container = this.app.stage.getChildByLabel(type); - const c = [...container.children]; - c.forEach(child => { + const shadowArray = this.shadowSprites[type]; + + [...container.children].forEach(child => { if (child.label.split(" ")[0] == "invincible") return; child.destroy(); // fixed a memory leak (-2 hours) container.removeChild(child); @@ -242,11 +464,11 @@ export class PixiRender { array.forEach((row, y) => { row.forEach((col, x) => { const cell = col.split(" "); - let sprite = container.getChildByLabel(`invincible ${x}${y}`); + const sprite = shadowArray[y][x]; sprite.visible = false; if (cell.includes("A") || cell.includes("S")) { // active or stopped piece sprite.visible = true; - sprite.texture = this.textures[this.getPiece(type, cell[1].toLowerCase())]; + sprite.texture = this.getTexture(type, cell); sprite.alpha = this.getOpacity(cell, type, x, y) ?? this.queueAlpha; } else if (cell.includes("NP") && this.game.renderer.inDanger) { // next piece overlay sprite.visible = true; @@ -260,30 +482,25 @@ export class PixiRender { } }); }); - - if (type == "board") this.updateMinoFlash(container); } - updateMinoFlash(cont) { - this.flashTimes = this.flashTimes - .filter(({ c, t }) => t > 0) - .map(({ c, t }) => { return { c, t: t - 1 }; }); + getTexture(type, cell) { + const piece = this.game.hold.occured && type == "hold" ? "hold" : cell[1].toLowerCase() + return this.textures[piece]; + } - for (let { c, t } of this.flashTimes) { - const [x, y] = c; - const progress = (t / 15) * this.minoSize; + // todo remove flash for cleared lines + flash(coords) { + coords.forEach(([x, y]) => { const triangle = new PIXI.Sprite(this.triangle); triangle.x = x * this.minoSize; triangle.y = (39 - y) * this.minoSize; - triangle.width = progress; - triangle.height = progress; - - cont.addChild(triangle); - } - } - - getPiece(type, cell) { - return this.game.hold.occured && type == "hold" ? "hold" : cell; + triangle.label = "invincible"; + this.board.addChild(triangle); + gsap.timeline({ onComplete: () => this.board.removeChild(triangle) }) + .to(triangle, { duration: 0, pixi: { width: this.minoSize, height: this.minoSize } }) + .to(triangle, { duration: 0.15, pixi: { width: 0, height: 0 }, ease: "power1.inOut", }) + }) } getOpacity(cell, type, x, y) { @@ -298,7 +515,6 @@ export class PixiRender { } } } - return this.boardAlpha.toFixed(2); } @@ -308,11 +524,6 @@ export class PixiRender { return opacity; } - removeCoords([x, y]) { - this.justPlacedCoords = this.justPlacedCoords.filter(c => !(c[0] == x && c[1] == y)); - this.flashTimes = this.flashTimes.filter(({ c, t }) => !(c[0] == x && c[1] == y)); - } - updateAlpha() { if (this.game.settings.game.gamemode != 'lookahead') return; const update = (type, amount) => { @@ -337,44 +548,42 @@ export class PixiRender { update("justPlacedAlpha", 6) } - // RESET ANIMATION - startResetAnimation() { + createParticleSprite() { + return new PIXI.Sprite(this.game.particles.texture); + } + + toggleDangerBG(danger) { + gsap.to(this.boardBG, { duration: 0.2, pixi: { alpha: danger ? 0.1 : 0 } }); + gsap.to(this.border, { duration: 0.2, pixi: { tint: danger ? "red" : "none" } }); + } + + resetAnimation() { this.board.mask = this.resetMask; this.board.addChild(this.resetMask); this.board.addChild(this.resetTriangle); this.game.stopGameTimers(); this.game.controls.resetting = true; - this.resetAnimCurrent = 0; - } - - resetAnimation() { - if (this.resetAnimCurrent >= this.resetAnimLength * 2) return; - this.resetAnimCurrent++; // fade after animation - if (this.boardAlpha < 0.99) this.boardAlpha += 2 / this.resetAnimLength; - if (this.resetAnimCurrent > this.resetAnimLength) return; - // animation using mask - const progress = this.easeInOutCubic(this.resetAnimCurrent / this.resetAnimLength); - this.resetMask.scale.set(1 - progress, 1 - progress); - this.resetTriangle.scale.set(progress * 9, progress * 9); + const animateOpacity = () => { + gsap.timeline() + .to(this.board, { duration: 0, pixi: { alpha: 0 } }) + .to(this.board, { duration: 0.2, pixi: { alpha: 1 } }) + } - this.board.mask = this.resetMask; + gsap.timeline({ onComplete: () => { this.endResetAnimation(); animateOpacity(); } }) + .to(this.resetMask, { duration: 0, pixi: { scale: 1 } }) + .to(this.resetMask, { duration: 0.4, pixi: { scale: 0 }, ease: "power1.inOut", }) - if (this.resetAnimCurrent == this.resetAnimLength) this.endResetAnimation(); + gsap.timeline() + .to(this.resetTriangle, { duration: 0, pixi: { scale: 0 } }) + .to(this.resetTriangle, { duration: 0.4, pixi: { scale: 9 }, ease: "power1.inOut", }) } endResetAnimation() { this.game.startGame(); this.game.controls.resetting = false; - this.boardAlpha = 0; - setTimeout(() => { - this.board.removeChild(this.resetMask); - this.board.mask = null; - this.board.removeChild(this.resetTriangle); - }, 0) - } - - easeInOutCubic(x) { - return -(Math.cos(Math.PI * x) - 1) / 2; + this.board.mask = null; + this.board.removeChild(this.resetMask); + this.board.removeChild(this.resetTriangle); } } diff --git a/src/display/renderer.js b/src/display/renderer.js index 80fa81b..53c6610 100644 --- a/src/display/renderer.js +++ b/src/display/renderer.js @@ -13,26 +13,15 @@ export class Renderer { sidebarSecondary; divBoard = document.getElementById("board"); - divBackboard = document.getElementById("backboard"); - divLinesSent = document.getElementById("linessent"); elementEditPieces = document.getElementById("editMenuPieces"); - - elementStats1 = document.getElementById("stats1"); - elementStats2 = document.getElementById("stats2"); - elementStats3 = document.getElementById("stats3"); - elementStatname1 = document.getElementById("statName1"); - elementStatname2 = document.getElementById("statName2"); - elementStatname3 = document.getElementById("statName3"); - elementSmallStat1 = document.getElementById("smallStat1"); - elementSmallStat2 = document.getElementById("smallStat2"); - elementSmallStat3 = document.getElementById("smallStat3"); - /** * @param {Game} game */ constructor(game) { this.game = game; this.board = game.board; + this.nextQueueGrid = [...Array(15)].map(() => [...Array(4)].map(() => "")); + this.holdQueueGrid = [...Array(3)].map(() => [...Array(4)].map(() => "")); } updateNext() { @@ -48,8 +37,6 @@ export class Renderer { }); this.game.pixi.render("next", this.nextQueueGrid); - // if (this.game.settings.game.gamemode == 'lookahead' || !this.game.settings.display.colouredQueues) return; - // this.canvasNext.style.outlineColor = this.game.bag.nextPiece().colour; } getPiece(name) { @@ -67,15 +54,9 @@ export class Renderer { isI = name == "i"; const [dx, dy] = [isO ? 1 : 0, isO ? 1 : isI ? -1 : 0]; const coords = this.board.pieceToCoords(this.game.hold.piece.shape1); - coords.forEach(([x, y]) => (this.holdQueueGrid[y + dy][x + dx] = "A " + name)); - const len = Math.round(this.game.pixi.minoSize / 2); - const [shiftX, shiftY] = [isO || isI ? 0 : len, isI ? 0 : len]; this.game.pixi.render("hold", this.holdQueueGrid); - // if (this.game.settings.game.gamemode == 'lookahead' || !this.game.settings.display.colouredQueues) return; - // const colour = this.game.hold.occured ? "gray" : this.game.hold.piece.colour - // this.canvasHold.style.outline = `0.2vh solid ${colour}`; } clearHold() { @@ -89,22 +70,16 @@ export class Renderer { if (condition && !this.inDanger) { this.game.sounds.playSound("damage_alert"); } - this.game.boardeffects.toggleDangerBoard(condition) + this.game.pixi.toggleDangerBG(condition); this.inDanger = condition; } renderActionText(damagetype, isBTB, isPC, damage, linecount) { - if (damagetype != "") this.setText("cleartext", damagetype, 2000); - if (this.game.stats.combo > 0) - this.setText("combotext", `Combo ${this.game.stats.combo}`, 2000); - if (isBTB && this.game.stats.btbCount > 0) - this.setText("btbtext", `BTB ${this.game.stats.btbCount} `, 2000); - if (isPC) this.setText("pctext", "Perfect Clear", 2000); - if (damage > 0) this.setText("linessent", `${this.game.mechanics.spikeCounter}`, 1500); - - if (this.game.mechanics.spikeCounter > 0) this.spikePattern("white", 1); - if (this.game.mechanics.spikeCounter >= 10) this.spikePattern("red", 1.1); - if (this.game.mechanics.spikeCounter >= 20) this.spikePattern("lime", 1.2); + if (damagetype != "") this.game.pixi.showActionText("cleartext", damagetype); + if (this.game.stats.combo > 0) this.game.pixi.showActionText("combotext", `Combo ${this.game.stats.combo}`); + if (isBTB && this.game.stats.btbCount > 0) this.game.pixi.showActionText("btbtext", `btb ${this.game.stats.btbCount} `); + if (this.game.mechanics.spikeCounter > 4) this.game.pixi.showSpikeText(`${this.game.mechanics.spikeCounter}`); + if (isPC) this.game.pixi.showPCText(); // audio if (isPC) this.game.sounds.playSound("allclear"); @@ -125,31 +100,6 @@ export class Renderer { this.game.sounds.playSound(`combo_${this.game.stats.combo > 16 ? 16 : this.game.stats.combo}`); } - resetActionText() { - ['btbtext', 'cleartext', 'combotext', 'pctext', 'linessent'].forEach(id => { - document.getElementById(id).style.opacity = "0"; - }) - } - - spikePattern(colour, size) { - this.divLinesSent.style.color = colour; - this.divLinesSent.style.textShadow = `0 0 1vh ${colour}`; - this.divLinesSent.style.fontSize = `${3.5 * size}vh`; - } - - setText(id, text, duration) { - const textbox = document.getElementById(id); - textbox.textContent = text; - textbox.style.transform = "translateX(-2%)"; - textbox.style.opacity = "1"; - if (this.texttimeouts[id] != 0) this.stopTimeout(id); - this.texttimeouts[id] = setTimeout(() => { - textbox.style.opacity = "0"; - textbox.style.transform = "translateX(2%)"; - this.game.mechanics.spikeCounter = 0; - }, duration); - } - stopTimeout(name) { clearTimeout(this.texttimeouts[name]); this.texttimeouts[name] = 0; @@ -164,12 +114,11 @@ export class Renderer { const height = Number(this.game.settings.display.boardHeight); this.divBoard.style.transform = `scale(${height}%) translate(-50%, -50%)`; - // this.canvasHold.style.outline = `0.2vh solid #dbeaf3`; // board opacity - const background = `rgba(0, 0, 0, ${Number(this.game.settings.display.boardOpacity) / 100})`; - this.divBackboard.style.backgroundColor = background; - document.body.style.setProperty('--background', background); + // const background = `rgba(0, 0, 0, ${Number(this.game.settings.display.boardOpacity) / 100})`; + // this.divBackboard.style.backgroundColor = background; + // document.body.style.setProperty('--background', background); // sidebar constants this.sidebarStats = this.game.settings.game.sidebar; @@ -178,7 +127,7 @@ export class Renderer { this.sidebarStats.forEach((stat, index) => { if (stat == "None") stat = "" - this[`elementStatname${index + 1}`].textContent = stat; + this.game.pixi.statTexts[index].statText.text = stat.toUpperCase(); }) if (settings) this.game.pixi.resize(); @@ -187,20 +136,37 @@ export class Renderer { renderSidebar() { this.sidebarStats.forEach((stat, index) => { if (stat == "None") { // no stat - this[`elementStats${index + 1}`].textContent = ""; + this.game.pixi.statTexts[index].stat.text = ""; return; }; - const displayStat = this.game.stats[stat].toFixed(this.sidebarFixed[index]); - this[`elementStats${index + 1}`].textContent = displayStat; + let displayStat = this.game.stats[stat].toFixed(this.sidebarFixed[index]) ?? ""; + if (stat == "time") displayStat = this.formatTime(Number(displayStat), this.sidebarFixed[index]); // reformat time + this.game.pixi.statTexts[index].stat.text = displayStat; if (this.sidebarSecondary[index]) { - const displaySecond = this.game.stats[this.sidebarSecondary[index]] - this[`elementSmallStat${index + 1}`].textContent = displaySecond; + const displaySecond = this.game.stats[this.sidebarSecondary[index]] ?? "" + this.game.pixi.statTexts[index].statSecondary.text = displaySecond; } }) } + createReverseLookup(obj) { + const reverseLookup = {} + for (const [key, array] of Object.entries(obj)) { + array.forEach(item => { + reverseLookup[item] = key; + }); + } + return reverseLookup + } + + formatTime(s, d) { + const minutes = Math.floor(s / 60); + const seconds = (s - minutes * 60).toFixed(d) + return `${minutes>0?minutes:""}:${seconds < 10 ? "0" : ""}${seconds}` + } + renderTimeLeft(text){ const e = document.getElementById("timeLeftText") if (this.texttimeouts["timeLeft"] != 0){ @@ -214,16 +180,6 @@ export class Renderer { }, 3000); } - createReverseLookup(obj) { - const reverseLookup = {} - for (const [key, array] of Object.entries(obj)) { - array.forEach(item => { - reverseLookup[item] = key; - }); - } - return reverseLookup - } - setEditPieceColours() { const elPieces = [...this.elementEditPieces.children]; elPieces.forEach(elpiece => { @@ -249,5 +205,4 @@ export class Renderer { this.game.particles.spawnParticles(0, 0, "dangerboard"); this.game.particles.spawnParticles(0, 20, "dangersides"); } - } diff --git a/src/features/editboard.js b/src/features/editboard.js index 194d45e..8a7878f 100644 --- a/src/features/editboard.js +++ b/src/features/editboard.js @@ -1,15 +1,13 @@ import { Game } from "../game.js"; export class BoardEditor { - clickareasdiv = document.getElementById("clickareas"); + // clickareasdiv = document.getElementById("clickareas"); mousedown = false; currentMode = "fill"; fillPiece = 'G'; fillRow = false; override = false; - elementEditButton = document.getElementById("editButton"); - /** * @param {Game} game */ @@ -112,8 +110,4 @@ export class BoardEditor { }) return { board, next, hold } } - - setEditButton(bool) { - this.elementEditButton.style.display = bool ? "flex" : "none"; - } } \ No newline at end of file diff --git a/src/features/modes.js b/src/features/modes.js index e89226a..5a9cdce 100644 --- a/src/features/modes.js +++ b/src/features/modes.js @@ -3,8 +3,6 @@ import gamemodeJSON from "../data/gamemodes.json" with { type: "json" }; import { gameoverResultText, gameoverText, resultSuffix } from "../data/data.js"; export class Modes { - elementobjectives = document.getElementById("objective"); - divObjectiveText = document.getElementById("objectiveText"); modeJSON; customSettings; @@ -53,7 +51,7 @@ export class Modes { if (statValue != undefined) statValue = Math.round(statValue * 1000) / 1000 let modetext = (statValue == undefined ? '' : statValue) + (resultValue == undefined ? '' : `/${resultValue}`) - this.elementobjectives.textContent = modetext; + this.game.pixi.objectiveTexts[0].text = modetext; } loadModes() { @@ -63,7 +61,10 @@ export class Modes { currentGamemode = 'sprint' } this.setGamemode(currentGamemode); - this.divObjectiveText.textContent = this.modeJSON.objectiveText; + + this.game.pixi.objectiveTexts[1].text = this.modeJSON.objectiveText.toUpperCase(); + this.game.history.setHistoryDiv(this.game.settings.game.gamemode == 'custom'); + this.game.pixi.toggleEditButton(this.game.settings.game.gamemode == 'custom'); } setGamemode(mode) { diff --git a/src/game.js b/src/game.js index ed53cd8..d1f7fb1 100644 --- a/src/game.js +++ b/src/game.js @@ -56,13 +56,19 @@ export class Game { this.modes = new Modes(this); this.zenith = new Zenith(this); this.pixi = new PixiRender(this); + this.init(); + } + async init() { + this.menuactions.loadSettings(); + this.board.resetBoard(); + await this.pixi.init(); + this.modes.loadModes(); this.renderer.renderStyles(); this.renderer.setEditPieceColours(); this.sounds.initSounds(); this.startGame(); - this.pixi.init(); - this.boardeditor.addListeners(); + // this.boardeditor.addListeners(); this.menuactions.addRangeListener(); this.modals.generate.addMenuListeners(); this.modals.generate.generateGamemodeMenu(); @@ -75,6 +81,7 @@ export class Game { startGame() { this.menuactions.loadSettings(); + this.modes.loadModes(); this.resetState(); this.renderer.renderStyles(); this.mechanics.spawnPiece(this.bag.randomiser(true), true); @@ -125,7 +132,6 @@ export class Game { this.board.resetBoard(); this.mechanics.locking.clearLockDelay(); this.boardeffects.toggleRainbow(false); - this.renderer.resetActionText(); this.renderer.renderDanger(); this.particles.clearParticles(); this.renderer.clearHold(); diff --git a/src/main.js b/src/main.js index cbb8b81..c9c99c6 100644 --- a/src/main.js +++ b/src/main.js @@ -15,11 +15,12 @@ window.addEventListener("keydown", event => { let key = event.key.length > 1 ? event.key : event.key.toLowerCase(); // 1 letter words are lowercase if (event.altKey) key = "Alt+" + key; if (event.ctrlKey) key = "Ctrl+" + key; - + game.controls.onKeyDownRepeat(event, key); if (event.repeat) return; game.controls.onKeyDown(event, key); -}) +}); + window.addEventListener("keyup", event => { if (event.key == undefined) return; @@ -39,14 +40,13 @@ window.addEventListener("resize", () => { }, 0); }) -// splash menu -window.addEventListener("DOMContentLoaded", () => { +export function clearSplash() { elementSplashText.textContent = "Ready"; elementSplashScreen.style.opacity = 0; elementSplashScreen.style.scale = 1.2; elementSplashScreen.style.display = "none"; document.getElementById("ignoreText").style.opacity = 0.5; -}) +} window.addEventListener("focus", function () { document.getElementById("nofocus").style.display = "none"; @@ -60,9 +60,3 @@ window.addEventListener("blur", function () { window.onerror = (msg, url, lineNo, columnNo, error) => { game.modals.generate.notif(error, msg + ". ln " + lineNo, "error"); } - -document.getElementById("playingfield").style.display = 'none' -document.getElementById("next").style.display = 'none' -document.getElementById("hold").style.display = 'none' -document.getElementById("backboard").style.display = 'none' -document.getElementById("backborder").style.display = 'none' \ No newline at end of file diff --git a/src/mechanics/clearlines.js b/src/mechanics/clearlines.js index 2380dd6..f342c87 100644 --- a/src/mechanics/clearlines.js +++ b/src/mechanics/clearlines.js @@ -24,7 +24,6 @@ export class ClearLines { removedGarbage++; stopped.filter(c => c[1] == row).forEach(([x, y]) => { // clear rows this.game.mechanics.board.setCoordEmpty([x, y]); - this.game.pixi.removeCoords([x, y]); }); this.game.mechanics.board.moveMinos(stopped.filter(c => c[1] > row), "DOWN", 1); } diff --git a/src/mechanics/locking.js b/src/mechanics/locking.js index 9d01734..596a776 100644 --- a/src/mechanics/locking.js +++ b/src/mechanics/locking.js @@ -75,10 +75,10 @@ export class LockPiece { this.game.pixi.justPlacedAlpha = 1; lockCoords.forEach(([x, y]) => { - this.game.pixi.flashTimes.push({ c: [x, y], t: 15 }) this.game.mechanics.board.rmValue([x, y], "A"); this.game.mechanics.board.addValFront([x, y], "S"); }); + this.game.pixi.flash(lockCoords); this.game.mechanics.locking.clearLockDelay(); clearInterval(this.game.gravityTimer); diff --git a/src/mechanics/zenith.js b/src/mechanics/zenith.js index b4cb54f..98f213c 100644 --- a/src/mechanics/zenith.js +++ b/src/mechanics/zenith.js @@ -109,8 +109,9 @@ export class Zenith { climbSpeedBar.value = point climbSpeedBar.max = require - document.styleSheets[1].cssRules[24].style.backgroundColor = color[speed - 1] - document.styleSheets[1].cssRules[23].style.backgroundColor = color[speed] + // changes css variable, better selection + document.getElementById("climbSpeedBar").style.setProperty("--background-colour", color[speed-1]) + document.getElementById("climbSpeedBar").style.setProperty("--bar-colour", color[speed]) } } \ No newline at end of file diff --git a/src/menus/menuactions.js b/src/menus/menuactions.js index 3120e4b..c92e70a 100644 --- a/src/menus/menuactions.js +++ b/src/menus/menuactions.js @@ -95,16 +95,11 @@ export class MenuActions { loadSettings() { const data = localStorage.getItem("settings") ?? "{}"; this.game.settings.load(JSON.parse(data)) - this.game.modes.loadModes(); - this.game.history.setHistoryDiv(this.game.settings.game.gamemode == 'custom'); - this.game.boardeditor.setEditButton(this.game.settings.game.gamemode == 'custom'); } setGamemode(mode) { this.game.modes.setGamemode(mode); this.game.modes.loadModes(); - this.game.history.setHistoryDiv(this.game.settings.game.gamemode == 'custom'); - this.game.boardeditor.setEditButton(this.game.settings.game.gamemode == 'custom'); } downloadSettings() { @@ -124,6 +119,7 @@ export class MenuActions { reader.onload = () => { localStorage.setItem("settings", reader.result.toString()); this.loadSettings(); + this.game.modes.loadModes(); this.game.modals.generate.notif("Settings Loaded", "User settings have successfully loaded", "message"); }; } diff --git a/src/movement/controls.js b/src/movement/controls.js index f9f3637..08734d0 100644 --- a/src/movement/controls.js +++ b/src/movement/controls.js @@ -153,10 +153,9 @@ export class Controls { if (!animation || this.game.settings.game.stride) { this.game.startGame(); - return; + } else { + this.game.pixi.resetAnimation() } - - this.game.pixi.startResetAnimation() } toggleCursor(enable) { diff --git a/styles/boards.css b/styles/boards.css index e6f70ca..9c4dda7 100644 --- a/styles/boards.css +++ b/styles/boards.css @@ -7,7 +7,7 @@ height: 60vh; aspect-ratio: 0.5; } - +/* #backboard, #backborder { position: absolute; @@ -79,29 +79,7 @@ width: 100%; height: 100%; background-color: var(--background); -} - -#backborder.boardDanger { - outline: 0.2vh solid red !important; - box-shadow: 0 0 3vh red !important; - transition: all 0.5s ease; -} - -#dangerOverlay { - position: absolute; - background-color: red; - transition: all 0.2s ease; - width: 100%; - height: 100%; - opacity: 0; -} - -#playingfield { - position: fixed; - width: 100%; - height: 200%; - top: -100%; -} +} */ #clickareas { position: absolute; @@ -121,38 +99,6 @@ background-color: #7d7d7d4e; } -#grid { - position: fixed; - width: 100%; - height: 100%; -} - -#next { - position: fixed; - left: 101%; - display: grid; - grid-template-columns: repeat(4, 1fr); - grid-template-rows: repeat(15, 1fr); - height: 79%; - width: 40%; - border-radius: 0 1.5vh 1.5vh 0; - outline: 0.2vh solid var(--cl-blue); - background-color: black; - transition: all 0.3s ease; -} - -#hold { - position: fixed; - left: -40%; - display: grid; - grid-template-columns: repeat(4, 1fr); - grid-template-rows: repeat(3, 1fr); - height: 15%; - width: 39.5%; - border-radius: 1.5vh 0 0 1.5vh; - background-color: black; -} - #lockTimer, #lockCounter { position: absolute; @@ -192,6 +138,9 @@ } #climbSpeedBar { + /* css variables which can be changed with js */ + --bar-colour: red; + --background-colour: #00000000; position: absolute; width: 32vh; height: 1vh; @@ -202,12 +151,13 @@ } #climbSpeedBar::-webkit-progress-value { - background-color: red; + background-color: var(--bar-colour); border-radius: 0.7vh; } #climbSpeedBar::-webkit-progress-bar { - background-color: var(--invis); + background-color: var(--background-colour); + border-radius: 0.7vh; } #timeLeftText { @@ -382,16 +332,6 @@ } } -.nextText { - left: 110%; - bottom: 100%; -} - -.holdText { - right: 110%; - bottom: 100%; -} - .objectiveText { left: 110%; bottom: 10%; diff --git a/styles/menus.css b/styles/menus.css index 3b3a563..b03a189 100644 --- a/styles/menus.css +++ b/styles/menus.css @@ -271,24 +271,6 @@ button>img { } /* stats */ - -.actiontext, -.statstext { - position: fixed; - font-size: 3vh; - text-shadow: 0 0 3vh var(--vl-gray); - margin: 0; - text-align: right; - width: 200%; - user-select: none; - transition: all 0.2s ease; - right: 105%; -} - -.actiontext { - opacity: 0; -} - .smalltext { position: absolute; font-size: 0.9em; @@ -296,23 +278,6 @@ button>img { user-select: none; } -#smallStat1, -#smallStat2, -#smallStat3 { - right: 150%; - font-size: 2.1vh; - text-align: right; -} - -#statName1, -#statName2, -#statName3 { - text-align: right; - right: 105%; - transform: translateY(0.5vh); -} - -.statText, #explanationText, #updatetext { display: flex; @@ -322,6 +287,8 @@ button>img { .statText { opacity: 1; + display: flex; + margin: 0.5vmin; } /* focus */ diff --git a/styles/settings.css b/styles/settings.css index 3937a09..371372c 100644 --- a/styles/settings.css +++ b/styles/settings.css @@ -154,7 +154,6 @@ right: 1vw; bottom: 1vh; padding: 1vh; - padding-top: 1.5vh; display: flex; justify-content: center; align-items: center; diff --git a/styles/style.css b/styles/style.css index 72980c8..8dce86e 100644 --- a/styles/style.css +++ b/styles/style.css @@ -56,46 +56,6 @@ body { transition: all 1s ease; } -#openSettingsButton, -#editButton { - top: 5%; - position: fixed; - left: 127%; - display: flex; - justify-content: right; - padding-right: 3%; - align-items: center; - height: 8%; - width: 24%; - border: 0.2vh solid white; - border-radius: 0 1.5vh 1.5vh 0; - transition: all 0.5s ease-out; - background-color: var(--invis); - outline: none; - opacity: 0.6; -} - -#editButton { - top: 15%; -} - -#openSettingsButton:hover, -#editButton:hover { - left: 133%; - opacity: 1; - transition: all 0.2s ease; -} - -#openSettingsButton>img { - height: 70%; - user-select: none; -} - -#editButton>img { - height: 100%; - user-select: none; -} - .dialog img, .scrollSettings img { opacity: 0.6; From 2c9c3bec52f2ddb7ffe712cc6661d6248e52433b Mon Sep 17 00:00:00 2001 From: TitanPlayz <118145727+TitanPlayz100@users.noreply.github.com> Date: Tue, 29 Oct 2024 22:59:14 +1100 Subject: [PATCH 4/6] added final bits of pixi text, fixed animations, changed key presses to be queued --- index.html | 35 ++--- info.md | 10 +- src/data/defaultSettings.json | 3 +- src/data/gamemodes.json | 3 +- src/display/boardEffects.js | 14 +- src/display/particles.js | 2 +- src/display/pixirender.js | 133 ++++++++++++++++--- src/display/renderer.js | 42 +++--- src/features/editboard.js | 62 +++------ src/features/history.js | 9 +- src/features/modes.js | 1 - src/features/profileStats.js | 5 +- src/features/sounds.js | 1 - src/game.js | 4 +- src/main.js | 11 +- src/mechanics/clearlines.js | 1 + src/mechanics/locking.js | 6 +- src/movement/controls.js | 30 +++-- styles/boards.css | 241 ++-------------------------------- styles/menus.css | 23 ++-- styles/style.css | 1 - 21 files changed, 248 insertions(+), 389 deletions(-) diff --git a/index.html b/index.html index 584ed06..5fc10e2 100644 --- a/index.html +++ b/index.html @@ -1,39 +1,33 @@ + teti - teti + + - - - - - - + + + + + +
+

⚠ GO DOWN

-

60S LEFT

- - - -
- - - -
- +

Teti

@@ -43,6 +37,12 @@

Teti

OUT OF FOCUSClick To Return
+
+

history: 0

+ + +
+ @@ -127,6 +127,7 @@

Teti

Rainbow effect on fast pace

Show Lock Bar

Show out of focus text

+

Action Text

Enable Particles

Particle Volume

Particle Size

diff --git a/info.md b/info.md index 4811fcc..cb5ac74 100644 --- a/info.md +++ b/info.md @@ -69,6 +69,11 @@ Skins from [YHF](https://you.have.fail/ed/at/tetrioplus/) Things that I am working on based on other changes - ready set go start option - arcade mode +- rotation centres + +- CHANGE GAME CLASS TO BE EXPORTED MODULES + - remove all this.game references in replacement with just the class being imported + - **no longer shall the shackles of past object oriented java code bound the multiparadigm eutopia which is javascript** Zenith mode additions - add fatigue: @@ -97,7 +102,6 @@ Khaki (#ffc48e) "Soft Pink" (#ffbbea) White (#ffffff) - *** ## Updates @@ -109,6 +113,10 @@ White (#ffffff) - changed PC text to be more TETR.IO-like - changed spike text to show in the middle of the board (like techmino) - RIP coloured queues +- better page loading using defer and lazy loading +- Experimental: pressed keys are stored in a queue, and evry pixi tick they all fire, might be more responsive +- changed timeLeftText animation to use gsap and pixi +- added setting for toggling action text #### v1.3.4 => PIXI.js - COMPLETE REWRITE OF ALL GRAPHICS diff --git a/src/data/defaultSettings.json b/src/data/defaultSettings.json index 13b626f..44c952a 100644 --- a/src/data/defaultSettings.json +++ b/src/data/defaultSettings.json @@ -15,7 +15,8 @@ "particles":true, "particleVolume": 60, "particleSize":1, - "skin":"tetrio" + "skin":"tetrio", + "actionText": true }, "game": { "gravitySpeed": 950, diff --git a/src/data/gamemodes.json b/src/data/gamemodes.json index edb901a..418885b 100644 --- a/src/data/gamemodes.json +++ b/src/data/gamemodes.json @@ -14,7 +14,8 @@ "allspinminis": false, "history": false, "sidebar": ["time", "apm", "pps"], - "stride": false + "stride": false, + "clearDelay": 0 }, "displayName": "Unset", "objectiveText": "", diff --git a/src/display/boardEffects.js b/src/display/boardEffects.js index 7d82384..96bde5f 100644 --- a/src/display/boardEffects.js +++ b/src/display/boardEffects.js @@ -18,8 +18,6 @@ export class BoardEffects { paceCooldown = 0; divBoard = document.getElementById("board"); - // border = document.getElementById('backborder') - // backboard = document.getElementById('backboard') /** * @@ -49,7 +47,6 @@ export class BoardEffects { this.Y = this.clamp(this.Y, 0.5); if (this.X != 0 || this.Y != 0) { - this.divBoard.style.translate = `${this.X}px ${this.Y}px` this.game.pixi.app.canvas.style.translate = `${this.X}px ${this.Y}px` } } @@ -65,7 +62,6 @@ export class BoardEffects { this.R = this.clamp(this.R, 0.1); if (this.R != 0) { - this.divBoard.style.rotate = `${this.R}deg` this.game.pixi.app.canvas.style.rotate = `${this.R}deg` } } @@ -100,10 +96,10 @@ export class BoardEffects { } toggleRainbow(pace) { - return; - this.border.style.setProperty('--blur-size', pace ? `0.3vmin` : `0vmin`) - this.border.style.setProperty('--blur-strength', pace ? '0.7vmin' : '0') - this.backboard.style.setProperty('--blur-strength', pace ? '0.5vmin' : '0') - this.hasPace = pace; + // todo add back + // this.border.style.setProperty('--blur-size', pace ? `0.3vmin` : `0vmin`) + // this.border.style.setProperty('--blur-strength', pace ? '0.7vmin' : '0') + // this.backboard.style.setProperty('--blur-strength', pace ? '0.5vmin' : '0') + // this.hasPace = pace; } } \ No newline at end of file diff --git a/src/display/particles.js b/src/display/particles.js index b7253f4..1cb236a 100644 --- a/src/display/particles.js +++ b/src/display/particles.js @@ -202,7 +202,7 @@ export class Particles { const posX = (direction ? 0 : width) + x + Math.random() * len const posY = y + Math.random() * height; const life = Math.random() * 25 + 50; - const dx = (direction ? 1 : -1) * (Math.random() * 2); + const dx = (direction ? -1 : 1) * (Math.random() * 2); const dy = Math.random() * 2 - 1; const BTBParticle = { x: posX, y: posY, colour, life, dx, dy, gravity: 0.15 } diff --git a/src/display/pixirender.js b/src/display/pixirender.js index 20e3b77..5604c32 100644 --- a/src/display/pixirender.js +++ b/src/display/pixirender.js @@ -14,6 +14,7 @@ export class PixiRender { justPlacedAlpha = 1; shadowSprites = {}; editButtonVisible = false; + currentlyFlashing = {} divlock = document.getElementById("lockTimer"); @@ -27,7 +28,7 @@ export class PixiRender { async init() { this.app = new PIXI.Application(); await this.app.init({ backgroundAlpha: 0, resizeTo: window, autoDensity: true }); - document.body.appendChild(this.app.canvas); + document.body.prepend(this.app.canvas); // grid const grid = new PIXI.Container(); @@ -39,6 +40,10 @@ export class PixiRender { this.app.stage.addChild(this.board); this.board.label = "board"; + const clickArea = new PIXI.Container(); + this.app.stage.addChild(clickArea); + clickArea.label = "clickArea"; + const next = new PIXI.Container(); this.app.stage.addChild(next); next.label = "next"; @@ -77,12 +82,15 @@ export class PixiRender { const next = this.app.stage.getChildByLabel("next"); const hold = this.app.stage.getChildByLabel("hold"); const board = this.app.stage.getChildByLabel("board"); + const clickArea = this.app.stage.getChildByLabel("clickArea"); const particles = this.app.stage.getChildByLabel("particles"); const textContainer = this.app.stage.getChildByLabel("textContainer"); // clear grid.children.forEach(child => child.destroy()); grid.removeChildren(); + clickArea.children.forEach(child => child.destroy()); + clickArea.removeChildren(); textContainer.removeChildren(); // resize @@ -103,6 +111,8 @@ export class PixiRender { board.pivot.x = width / 2; board.pivot.y = height * 3 / 2; + + // hold const clickRectHold = new PIXI.Graphics().rect(0, 0, width * 2 / 5, height * 3 / 20).fill("transparent"); clickRectHold.interactive = true; @@ -127,15 +137,25 @@ export class PixiRender { next.pivot.x = width / 2 - width * 11 / 10; next.pivot.y = height / 2; + const rect2 = new PIXI.Graphics().rect(0, 0, width, height).fill("transparent"); + rect2.interactive = true; + rect2.cursor = 'pointer' + rect2.on("pointerdown", () => console.log("click")); + clickArea.addChild(rect2); + clickArea.x = screenWidth; + clickArea.y = screenHeight; + clickArea.pivot.x = width / 2; + clickArea.pivot.y = height / 2; + // grid and outline const { textHold, textNext } = this.textGraphics(width); const { settings, reset, edit } = this.buttonGraphics(width); - this.boardBG = new PIXI.Graphics() + this.boardDanger = new PIXI.Graphics() .rect(0, 0, width, height) .fill("red") - this.boardBG.alpha = 0; - grid.addChild(this.boardBG); + this.boardDanger.alpha = 0; + grid.addChild(this.boardDanger); this.border = new PIXI.Graphics() .lineTo(0, height).lineTo(width, height).lineTo(width, 0) @@ -156,8 +176,8 @@ export class PixiRender { rectNext.x = width; grid.addChild(textNext); - grid.addChild(settings); grid.addChild(reset); + grid.addChild(settings); grid.addChild(edit); this.editButton = edit; @@ -183,6 +203,7 @@ export class PixiRender { textContainer.addChild(this.actionTexts.btbtext.sprite) textContainer.addChild(this.actionTexts.spiketext.sprite) textContainer.addChild(this.actionTexts.pctext.sprite) + textContainer.addChild(this.actionTexts.timeleft.sprite) this.statTexts.forEach((text) => { textContainer.addChild(text.stat); textContainer.addChild(text.statText); @@ -193,6 +214,7 @@ export class PixiRender { this.generateGrid(); this.resetAnimGraphic(); + this.generateClickMinos(clickArea); } // TEXT @@ -227,6 +249,14 @@ export class PixiRender { pcText.pivot.x = pcText.width / 2 - 10; pcText.pivot.y = pcText.height / 2; + const timeLeftStyle = new PIXI.TextStyle({ fontFamily: "Montserrat", fontSize: 20, fill: "gold", fontWeight: "bold" }); + const timeLeftText = new PIXI.Text({ text: "", style: timeLeftStyle }); + timeLeftText.resolution = 2; + timeLeftText.alpha = 0; + timeLeftText.x = this.width / 2; + timeLeftText.y = this.height * 1 / 8; + timeLeftText.anchor.x = 0.5; + const spiketext = actionText(3); spiketext.sprite.x = this.width / 2; spiketext.sprite.y = this.height * 1 / 4; @@ -238,7 +268,8 @@ export class PixiRender { combotext: actionText(1), btbtext: actionText(2), spiketext, - pctext: { sprite: pcText, animation: gsap.timeline() } + pctext: { sprite: pcText, animation: gsap.timeline() }, + timeleft: { sprite: timeLeftText, animation: gsap.timeline() } } const statStyle = new PIXI.TextStyle({ fontFamily: "Major Mono Display", fontSize: 16, fill: "white", fontWeight: "bold" }); @@ -268,18 +299,32 @@ export class PixiRender { const objectiveText = new PIXI.Text({ text: "", style: statSecondaryStyle }); objectiveText.resolution = 2; - objectiveText.position.set(width*11/10, this.height - this.height * 3 / 40) + objectiveText.position.set(width * 11 / 10, this.height - this.height * 3 / 40) const objectiveNameText = new PIXI.Text({ text: "", style: statTextStyle }); objectiveNameText.resolution = 2; - objectiveNameText.position.set(width*11/10, this.height - this.height * 5/40) + objectiveNameText.position.set(width * 11 / 10, this.height - this.height * 5 / 40) this.objectiveTexts = [objectiveText, objectiveNameText]; return { textHold, textNext }; } - // todo reset action text + resetActionTexts() { + Object.keys(this.actionTexts).forEach(key => { + this.actionTexts[key].animation.pause(); + gsap.to(this.actionTexts[key].sprite, { + duration: 0.2, pixi: { alpha: 0 }, + onComplete: () => this.actionTexts[key].animation.kill() + }) + }) + if (this.timeLeftTextSplit) this.timeLeftTextSplit.forEach(s => { + s.animation.kill(); + s.sprite.destroy(); + }); + this.timeLeftTextSplit = undefined; + } + showActionText(type, message) { this.actionTexts[type].animation.kill(); const text = this.actionTexts[type].sprite; @@ -318,6 +363,23 @@ export class PixiRender { .to(pc, { duration: 2.5, pixi: { scale: 0, alpha: 0 }, ease: "power3.in" }, "1.5") } + showTimeLeftText(msg) { + const textContainer = this.app.stage.getChildByLabel("textContainer"); + this.actionTexts.timeleft.animation.kill(); + const text = this.actionTexts.timeleft.sprite; + text.text = msg; + const split = this.splitSprite(text) + this.timeLeftTextSplit = split.map((s, i) => { + textContainer.addChild(s); + const animation = gsap.timeline({ onComplete: () => s.destroy() }) + .to(s, { duration: 0, pixi: { alpha: 1, x: s.x, tint: "white" } }) + .to(s, { duration: 3, pixi: { x: s.x + 8 * (i - split.length / 2) } }) + .to(s, { duration: 3 / 20, pixi: { tint: "red" }, repeat: 20, yoyo: true, ease: "none" }, "0") + .to(s, { duration: 0.5, pixi: { alpha: 0 } }, "2.5") + return { sprite: s, animation } + }); + } + // GRAPHICS and GENERATORS buttonGraphics(width) { const iconframe = (texture, scale, y) => { @@ -333,12 +395,10 @@ export class PixiRender { return icon } - const settings = iconframe(this.settingsIcon, 0.18, 0) - settings.on("pointerdown", () => this.game.modals.openModal("settingsPanel")); - - const reset = iconframe(this.resetIcon, 0.23, width * 3 / 20) + const reset = iconframe(this.resetIcon, 0.23, 0) reset.on("pointerdown", () => this.game.controls.retry(true)); - + const settings = iconframe(this.settingsIcon, 0.18, width * 3 / 20) + settings.on("pointerdown", () => this.game.modals.openModal("settingsPanel")); const edit = iconframe(this.editIcon, 0.21, width * 6 / 20) edit.on("pointerdown", () => this.game.modals.openModal("editMenu")); edit.visible = this.editButtonVisible @@ -408,6 +468,21 @@ export class PixiRender { this.shadowSprites[type] = shadowArray } + generateClickMinos(clickArea) { + for (let y = 0; y < 20; y++) { + for (let x = 0; x < 10; x++) { + const mino = new PIXI.Sprite(this.textures['g']); + mino.interactive = true; + mino.on("mousedown", () => this.game.boardeditor.mouseDown([x, y], mino)); + mino.on("mouseenter", () => this.game.boardeditor.mouseEnter([x, y], mino)); + mino.on("mouseleave", () => this.game.boardeditor.mouseLeave([x, y], mino)); + clickArea.addChild(mino); + mino.position.set(x * this.minoSize, y * this.minoSize); + mino.setSize(this.minoSize); + mino.alpha = 0; + } + } + } generateGrid() { if (this.game.settings.display.showGrid === false) return; @@ -440,6 +515,7 @@ export class PixiRender { // RENDER CLOCK tick(time) { + this.game.controls.runKeyQueue(); this.render("board", this.game.board.boardState); this.game.boardeffects.move(0, 0); this.game.boardeffects.rotate(0); @@ -489,7 +565,6 @@ export class PixiRender { return this.textures[piece]; } - // todo remove flash for cleared lines flash(coords) { coords.forEach(([x, y]) => { const triangle = new PIXI.Sprite(this.triangle); @@ -497,12 +572,17 @@ export class PixiRender { triangle.y = (39 - y) * this.minoSize; triangle.label = "invincible"; this.board.addChild(triangle); - gsap.timeline({ onComplete: () => this.board.removeChild(triangle) }) + this.currentlyFlashing[`${x},${y}`] = gsap.timeline({ onComplete: () => this.board.removeChild(triangle) }) .to(triangle, { duration: 0, pixi: { width: this.minoSize, height: this.minoSize } }) .to(triangle, { duration: 0.15, pixi: { width: 0, height: 0 }, ease: "power1.inOut", }) }) } + endFlash([x, y]) { + const a = this.currentlyFlashing[`${x},${y}`]; + a.totalProgress(1).kill(); + } + getOpacity(cell, type, x, y) { if (type != "board") return; if (this.divlock.value != 0 && cell.includes("A") && this.game.settings.game.gamemode != "lookahead") { @@ -553,7 +633,7 @@ export class PixiRender { } toggleDangerBG(danger) { - gsap.to(this.boardBG, { duration: 0.2, pixi: { alpha: danger ? 0.1 : 0 } }); + gsap.to(this.boardDanger, { duration: 0.2, pixi: { alpha: danger ? 0.1 : 0 } }); gsap.to(this.border, { duration: 0.2, pixi: { tint: danger ? "red" : "none" } }); } @@ -586,4 +666,23 @@ export class PixiRender { this.board.removeChild(this.resetMask); this.board.removeChild(this.resetTriangle); } + + /** @param {PIXI.Text} textSprite */ + splitSprite(textSprite) { + const target = textSprite; + const textContent = textSprite.text; + let currentX = target.x - target.width / 2; + target.text = ""; + const textChars = textContent.split(""); + /**@type {PIXI.Text[]} */ + let chars = [] + textChars.forEach((char) => { + const charSprite = new PIXI.Text({ text: char, style: target.style }); + charSprite.x = currentX; + charSprite.y = target.y; + currentX += charSprite.width; + chars.push(charSprite); + }); + return chars + } } diff --git a/src/display/renderer.js b/src/display/renderer.js index 53c6610..a97b508 100644 --- a/src/display/renderer.js +++ b/src/display/renderer.js @@ -74,13 +74,13 @@ export class Renderer { this.inDanger = condition; } - renderActionText(damagetype, isBTB, isPC, damage, linecount) { - if (damagetype != "") this.game.pixi.showActionText("cleartext", damagetype); - if (this.game.stats.combo > 0) this.game.pixi.showActionText("combotext", `Combo ${this.game.stats.combo}`); - if (isBTB && this.game.stats.btbCount > 0) this.game.pixi.showActionText("btbtext", `btb ${this.game.stats.btbCount} `); - if (this.game.mechanics.spikeCounter > 4) this.game.pixi.showSpikeText(`${this.game.mechanics.spikeCounter}`); - if (isPC) this.game.pixi.showPCText(); + dangerParticles() { + if (!this.inDanger) return; + this.game.particles.spawnParticles(0, 0, "dangerboard"); + this.game.particles.spawnParticles(0, 20, "dangersides"); + } + renderActionText(damagetype, isBTB, isPC, damage, linecount) { // audio if (isPC) this.game.sounds.playSound("allclear"); if (this.game.stats.btbCount == 2 && isBTB) this.game.sounds.playSound("btb_1"); @@ -98,11 +98,15 @@ export class Renderer { if (this.game.mechanics.spikeCounter >= 15) this.game.sounds.playSound("thunder", false); if (this.game.stats.combo > 0) this.game.sounds.playSound(`combo_${this.game.stats.combo > 16 ? 16 : this.game.stats.combo}`); - } - stopTimeout(name) { - clearTimeout(this.texttimeouts[name]); - this.texttimeouts[name] = 0; + // text + if (this.game.settings.display.actionText == false) return; + if (damagetype != "") this.game.pixi.showActionText("cleartext", damagetype); + if (this.game.stats.combo > 0) this.game.pixi.showActionText("combotext", `Combo ${this.game.stats.combo}`); + if (isBTB && this.game.stats.btbCount > 0) this.game.pixi.showActionText("btbtext", `btb ${this.game.stats.btbCount} `); + if (this.game.mechanics.spikeCounter > 4 && linecount > 0) this.game.pixi.showSpikeText(`${this.game.mechanics.spikeCounter}`); + if (isPC) this.game.pixi.showPCText(); + } renderStyles(settings = false) { @@ -115,6 +119,7 @@ export class Renderer { const height = Number(this.game.settings.display.boardHeight); this.divBoard.style.transform = `scale(${height}%) translate(-50%, -50%)`; + // todo add board background // board opacity // const background = `rgba(0, 0, 0, ${Number(this.game.settings.display.boardOpacity) / 100})`; // this.divBackboard.style.backgroundColor = background; @@ -168,16 +173,7 @@ export class Renderer { } renderTimeLeft(text){ - const e = document.getElementById("timeLeftText") - if (this.texttimeouts["timeLeft"] != 0){ - this.stopTimeout("timeLeft"); - //e.classList.remove("warn"); - } - e.textContent = text - e.classList.add("warn") - this.texttimeouts["timeLeft"] = setTimeout(() => { - e.classList.remove("warn"); - }, 3000); + this.game.pixi.showTimeLeftText(text); } setEditPieceColours() { @@ -199,10 +195,4 @@ export class Renderer { const forces = { "CW": force, "CCW": -force } this.game.boardeffects.rotate(forces[type]); } - - dangerParticles() { - if (!this.inDanger) return; - this.game.particles.spawnParticles(0, 0, "dangerboard"); - this.game.particles.spawnParticles(0, 20, "dangersides"); - } } diff --git a/src/features/editboard.js b/src/features/editboard.js index 8a7878f..c89c5f0 100644 --- a/src/features/editboard.js +++ b/src/features/editboard.js @@ -1,7 +1,6 @@ import { Game } from "../game.js"; export class BoardEditor { - // clickareasdiv = document.getElementById("clickareas"); mousedown = false; currentMode = "fill"; fillPiece = 'G'; @@ -16,51 +15,28 @@ export class BoardEditor { this.board = game.board; } - addListeners() { - // delegate listeners for efficiency - document.body.addEventListener("mousedown", (e) => { - if (e.target.classList.contains('clickmino')) { - const j = Number(e.target.dataset.x) - const i = Number(e.target.dataset.y) - if (this.game.settings.game.gamemode != 'custom') return; - if (this.fillRow) { this.fillWholeRow([j, 19 - i]) } - else { this.fillCell([j, 19 - i]); } - } - }); - - document.body.addEventListener("mouseenter", (e) => { - if (e.target.classList.contains('clickmino')) { - const j = Number(e.target.dataset.x) - const i = Number(e.target.dataset.y) - if (this.game.settings.game.gamemode != 'custom') return; - e.target.classList.add('highlighting') - if (this.mousedown) { - if (this.fillRow) { this.fillWholeRow([j, 19 - i]) } - else { this.fillCell([j, 19 - i]); } - } - } - }, true); + mouseDown([x, y], sprite) { + if (this.game.settings.game.gamemode != 'custom') return; + if (this.fillRow) { this.fillWholeRow([x, 19 - y]) } + else { this.fillCell([x, 19 - y]); } + } - document.body.addEventListener("mouseleave", (e) => { - if (e.target.classList.contains('clickmino')) { - e.target.classList.remove('highlighting') - } - }, true); + mouseEnter([x, y], sprite) { + if (this.game.settings.game.gamemode != 'custom') return; + sprite.alpha = 0.5; + if (this.mousedown) { + if (this.fillRow) { this.fillWholeRow([x, 19 - y]) } + else { this.fillCell([x, 19 - y]); } + } + } - document.body.addEventListener("mouseup", () => { - if (this.mousedown) this.game.history.save(); - this.mousedown = false; - }); + mouseLeave(e, sprite) { + sprite.alpha = 0; + } - for (let i = 0; i < 20; i++) { - for (let j = 0; j < 10; j++) { - const clickarea = document.createElement("div"); - clickarea.classList.add("clickmino"); - clickarea.dataset.x = j.toString(); - clickarea.dataset.y = i.toString(); - this.clickareasdiv.appendChild(clickarea); - } - } + mouseUp(e) { + if (this.mousedown) this.game.history.save(); + this.mousedown = false; } fillCell([x, y]) { diff --git a/src/features/history.js b/src/features/history.js index 2df0386..434e91d 100644 --- a/src/features/history.js +++ b/src/features/history.js @@ -86,7 +86,7 @@ export class History { updateUI() { const branches = this.historyConnections[this.currentState] ?? []; this.selectedbranch = Math.max(...branches); - this.historyelement.textContent = `history: ${this.currentState}`; + this.historyelement.textContent = `current: ${this.currentState}`; if (branches.length <= 1) { this.choiceselement.style.opacity = "0"; this.choiceselement.style.pointerEvents = "none"; @@ -96,7 +96,7 @@ export class History { this.choiceselement.style.opacity = "1"; this.choiceselement.style.pointerEvents = "all"; - [...this.choiceselement.children].forEach(button => button.remove()) + [...document.getElementsByClassName("redochoice")].forEach(button => button.remove()) branches.forEach(state => { const button = document.createElement("button"); button.classList.add("redochoice"); @@ -183,9 +183,4 @@ export class History { this.game.hold.piece = this.game.renderer.getPiece(hold); this.game.mechanics.spawnPiece(this.game.bag.randomiser()); } - - setHistoryDiv(bool) { - this.historyelement.style.display = bool ? "block" : "none"; - } - } \ No newline at end of file diff --git a/src/features/modes.js b/src/features/modes.js index 5a9cdce..bbf6c72 100644 --- a/src/features/modes.js +++ b/src/features/modes.js @@ -63,7 +63,6 @@ export class Modes { this.setGamemode(currentGamemode); this.game.pixi.objectiveTexts[1].text = this.modeJSON.objectiveText.toUpperCase(); - this.game.history.setHistoryDiv(this.game.settings.game.gamemode == 'custom'); this.game.pixi.toggleEditButton(this.game.settings.game.gamemode == 'custom'); } diff --git a/src/features/profileStats.js b/src/features/profileStats.js index 4427eb4..2028fb2 100644 --- a/src/features/profileStats.js +++ b/src/features/profileStats.js @@ -4,6 +4,7 @@ import { Game } from "../game.js"; export class ProfileStats { personalBests = {}; notSaved = ['game', 'level', 'combo'] + elementGameEndTitle = document.getElementById("gameEndTitle"); /** * @param {Game} game @@ -13,7 +14,7 @@ export class ProfileStats { } setPB(score) { - this.game.elementGameEndTitle.textContent = 'GAME ENDED'; + this.elementGameEndTitle.textContent = 'GAME ENDED'; const gamemode = this.game.settings.game.gamemode const gamemodeStats = this.personalBests[gamemode] ?? {}; const currentScore = Number(gamemodeStats.score); @@ -28,7 +29,7 @@ export class ProfileStats { gameStatsKeys.forEach(key => gameStats[key] = this.game.stats[key]) const ts = new Date().toJSON(); this.personalBests[gamemode] = { score, pbstats: gameStats, version: this.game.version, ts }; - this.game.elementGameEndTitle.textContent = 'NEW PB!'; + this.elementGameEndTitle.textContent = 'NEW PB!'; setTimeout(() => this.game.sounds.playSound("personalbest"), 1000); this.game.modals.generate.notif("PB Saved", `PB on ${gamemode} saved`, "success"); diff --git a/src/features/sounds.js b/src/features/sounds.js index a29eb51..43f357f 100644 --- a/src/features/sounds.js +++ b/src/features/sounds.js @@ -111,7 +111,6 @@ export class Sounds { track.connect(this.lowpassfilter); this.lowpassfilter.connect(this.audioContext.destination); }) - // this.playSound("allclear") // i knew it was a bad idea } setAudioLevel() { diff --git a/src/game.js b/src/game.js index d1f7fb1..b339485 100644 --- a/src/game.js +++ b/src/game.js @@ -26,7 +26,7 @@ export class Game { gameTimer = 0; // id of timeout survivalTimer = 0; // id of timeout gravityTimer = 0; - version = '1.3.4'; + version = '1.3.5'; tickrate = 60; elementReason = document.getElementById("reason"); @@ -68,7 +68,6 @@ export class Game { this.renderer.setEditPieceColours(); this.sounds.initSounds(); this.startGame(); - // this.boardeditor.addListeners(); this.menuactions.addRangeListener(); this.modals.generate.addMenuListeners(); this.modals.generate.generateGamemodeMenu(); @@ -136,6 +135,7 @@ export class Game { this.particles.clearParticles(); this.renderer.clearHold(); this.stopGameTimers(); + this.pixi.resetActionTexts(); this.bag = new Bag(this); this.mechanics = new Mechanics(this); diff --git a/src/main.js b/src/main.js index c9c99c6..8aeba31 100644 --- a/src/main.js +++ b/src/main.js @@ -7,9 +7,6 @@ window["menu"] = game.menuactions; window["modal"] = game.modals; window["songs"] = game.sounds; -const elementSplashScreen = document.getElementById("splashScreen"); -const elementSplashText = document.getElementById("splashText"); - window.addEventListener("keydown", event => { if (event.key == undefined) return; let key = event.key.length > 1 ? event.key : event.key.toLowerCase(); // 1 letter words are lowercase @@ -21,7 +18,6 @@ window.addEventListener("keydown", event => { game.controls.onKeyDown(event, key); }); - window.addEventListener("keyup", event => { if (event.key == undefined) return; let key = event.key.length > 1 ? event.key : event.key.toLowerCase(); @@ -32,6 +28,10 @@ window.addEventListener('mousemove', () => { game.controls.toggleCursor(true); }) +document.body.addEventListener("mouseup", (e) => { + game.boardeditor.mouseUp(e); +}); + window.addEventListener("resize", () => { setTimeout(() => { game.pixi.resize(); @@ -40,6 +40,9 @@ window.addEventListener("resize", () => { }, 0); }) + +const elementSplashScreen = document.getElementById("splashScreen"); +const elementSplashText = document.getElementById("splashText"); export function clearSplash() { elementSplashText.textContent = "Ready"; elementSplashScreen.style.opacity = 0; diff --git a/src/mechanics/clearlines.js b/src/mechanics/clearlines.js index f342c87..6288322 100644 --- a/src/mechanics/clearlines.js +++ b/src/mechanics/clearlines.js @@ -24,6 +24,7 @@ export class ClearLines { removedGarbage++; stopped.filter(c => c[1] == row).forEach(([x, y]) => { // clear rows this.game.mechanics.board.setCoordEmpty([x, y]); + this.game.pixi.endFlash([x, y]); }); this.game.mechanics.board.moveMinos(stopped.filter(c => c[1] > row), "DOWN", 1); } diff --git a/src/mechanics/locking.js b/src/mechanics/locking.js index 596a776..b307170 100644 --- a/src/mechanics/locking.js +++ b/src/mechanics/locking.js @@ -110,11 +110,13 @@ export class LockPiece { this.game.renderer.renderDanger(); const delay = (cleared > 0) ? this.game.settings.game.clearDelay : 0; - this.timings.clearDelay = setTimeout(() => { + const onClear = () => { this.game.mechanics.spawnPiece(this.game.bag.randomiser()); this.game.history.save(); this.timings.clearDelay = 0; - }, delay); + } + if (delay == 0) onClear(); + else this.timings.clearDelay = setTimeout(() => onClear(), delay); } clearLockDelay(clearCount = true) { diff --git a/src/movement/controls.js b/src/movement/controls.js index 08734d0..248ab50 100644 --- a/src/movement/controls.js +++ b/src/movement/controls.js @@ -11,6 +11,8 @@ export class Controls { cursorVisible = true; resetting = false; + keyQueue = []; + /** * @param {Game} game */ @@ -31,19 +33,26 @@ export class Controls { if (key == keys.resetKey) this.retry(true); if (this.game.ended) return; - if (key == keys.cwKey) this.moves.rotate("CW"); - else if (key == keys.ccwKey) this.moves.rotate("CCW"); - else if (key == keys.rotate180Key) this.moves.rotate("180"); - else if (key == keys.hdKey) this.moves.harddrop(); - else if (key == keys.holdKey) this.game.mechanics.switchHold(); - else if (key == keys.rightKey) this.startDas("RIGHT"); - else if (key == keys.leftKey) this.startDas("LEFT"); - else if (key == keys.sdKey) this.startArrSD(); - + this.keyQueue.push(key); this.toggleCursor(false); this.game.stats.inputs++; } + runKeyQueue() { + const keys = this.game.settings.control; + this.keyQueue.forEach(key => { + if (key == keys.cwKey) this.moves.rotate("CW"); + else if (key == keys.ccwKey) this.moves.rotate("CCW"); + else if (key == keys.rotate180Key) this.moves.rotate("180"); + else if (key == keys.hdKey) this.moves.harddrop(); + else if (key == keys.holdKey) this.game.mechanics.switchHold(); + else if (key == keys.rightKey) this.startDas("RIGHT"); + else if (key == keys.leftKey) this.startDas("LEFT"); + else if (key == keys.sdKey) this.startArrSD(); + }); + this.keyQueue = []; + } + onKeyDownRepeat(event, key) { // allows for arr undo/redo const keys = this.game.settings.control; if (event.key == this.menuKey) event.preventDefault(); @@ -65,8 +74,7 @@ export class Controls { this.directionState[direction] = "das"; this.stopTimeout("das"); this.stopInterval("arr"); - this.timings.das = setTimeout(() => - Promise.resolve().then(() => this.startArr(direction)), // feels faster with promise but idk + this.timings.das = setTimeout(() => this.startArr(direction), this.game.settings.handling.das ); } diff --git a/styles/boards.css b/styles/boards.css index 9c4dda7..35ff511 100644 --- a/styles/boards.css +++ b/styles/boards.css @@ -6,97 +6,7 @@ transform: scale(1.1) translate(-50%, -50%); height: 60vh; aspect-ratio: 0.5; -} -/* -#backboard, -#backborder { - position: absolute; - height: 100%; - width: 100%; - background-color: var(--background); - outline: 0.2vh solid var(--cl-blue); - box-shadow: 0 0 5vh var(--l-gray); -} - -@property --angle { - syntax: ""; - initial-value: 0deg; - inherits: false; -} - -#backboard { - --blur-size: 7vmin; - --spin-speed: 6s; - --blur-radius: 20vmin; - --blur-strength: 0; - --colours: #ff4545, #00ff99, #006aff, #ff0095, #ff4545; -} - -#backborder { - --blur-size: 0.3vmin; - --spin-speed: 2s; - --blur-radius: 0.3vmin; - --blur-strength: 0; - --colours: #ff4545, #00ff99, #006aff, #ff0095, #ff4545; -} - -#backboard::before, -#backborder::before { - content: " "; - position: absolute; - width: 100%; - height: 100%; - background-image: conic-gradient(from var(--angle), var(--colours)); - padding: var(--blur-size); - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - border-radius: 1vmin; - animation: var(--spin-speed) spin linear infinite; - filter: blur(var(--blur-radius)); - opacity: var(--blur-strength); - transition: all 1s ease; -} - -#backboard { - outline: none; -} - -@keyframes spin { - from { - --angle: 0deg; - } - - to { - --angle: 360deg; - } -} - -#backboard::after, -#backborder::after { - content: " "; - position: absolute; - width: 100%; - height: 100%; - background-color: var(--background); -} */ - -#clickareas { - position: absolute; - width: 100%; - height: 100%; - display: grid; - grid-template-columns: repeat(10, 1fr); - grid-template-rows: repeat(20, 1fr); -} - -.clickmino { - user-select: none; - z-index: 10; -} - -.highlighting { - background-color: #7d7d7d4e; + pointer-events: none; } #lockTimer, @@ -113,26 +23,20 @@ height: 0.6vh; } -#lockTimer::-webkit-progress-bar, -#lockCounter::-webkit-progress-bar, -#garbageQueue::-webkit-progress-bar { +:is(#lockTimer, #lockCounter, #garbageQueue)::-webkit-progress-bar { background-color: var(--invis); } -#lockTimer::-moz-progress-bar, -#lockCounter::-moz-progress-bar, -#garbageQueue::-moz-progress-bar { +:is(#lockTimer, #lockCounter, #garbageQueue)::-moz-progress-bar { background-color: var(--invis); } -#lockTimer::-webkit-progress-value, -#lockCounter::-webkit-progress-value { +:is(#lockTimer, #lockCounter)::-webkit-progress-value { background-color: var(--cl-blue); border-radius: 0.7vh; } -#lockTimer::-moz-progress-value, -#lockCounter::-moz-progress-value { +:is(#lockTimer, #lockCounter)::-moz-progress-value { background-color: var(--cl-blue); border-radius: 0.7vh; } @@ -160,123 +64,6 @@ border-radius: 0.7vh; } -#timeLeftText { - font-family: "Montserrat", sans-serif; - font-weight: bold; - color: gold; - font-size: 1em; - width: 100%; - text-align: center; - bottom: 75%; - opacity: 0; - transition: all 0.3s ease; -} - -#timeLeftText.warn { - animation: timeLeft 3s; -} - -@keyframes timeLeft { - 0% { - opacity: 1; - letter-spacing: 0px; - color: gold; - } - - 5%{ - color: red; - } - - 10%{ - color: gold; - - } - - 15%{ - color: red; - } - - 20%{ - color: gold; - - } - - 25%{ - color: red; - } - - 30%{ - color: gold; - - } - - 35%{ - color: red; - } - - 40%{ - color: gold; - - } - - 45%{ - color: red; - } - - 50%{ - color: gold; - - } - - 55%{ - color: red; - } - - 60%{ - color: gold; - - } - - 65%{ - color: red; - } - - 70% { - opacity: 1; - color:gold - } - - 75%{ - color: red; - } - - 80%{ - color: gold; - - } - - 85%{ - color: red; - } - - 90%{ - color: gold; - - } - - 95%{ - color: red; - } - - - 100% { - opacity: 0; - letter-spacing: 5px; - color: gold; - } -} - - #garbageQueue { position: absolute; width: 122vh; @@ -332,19 +119,19 @@ } } -.objectiveText { - left: 110%; - bottom: 10%; -} - #redochoices { position: absolute; - top: 100%; - left: 110%; - width: 40vw; + display: flex; + bottom: 8vh; + width: 100%; + flex-direction: row; + text-align: center; + justify-content: center; + align-items: center; + flex-wrap: wrap; opacity: 0; - pointer-events: none; transition: all 0.3s ease; + user-select: none; } .redochoice { diff --git a/styles/menus.css b/styles/menus.css index b03a189..49f6a61 100644 --- a/styles/menus.css +++ b/styles/menus.css @@ -270,21 +270,7 @@ button>img { text-shadow: 0 0 3vh white; } -/* stats */ -.smalltext { - position: absolute; - font-size: 0.9em; - color: var(--l-gray); - user-select: none; -} - -#explanationText, -#updatetext { - display: flex; - margin: 0; - opacity: 0.4; -} - +/* do not remove, used in menu */ .statText { opacity: 1; display: flex; @@ -313,4 +299,11 @@ button>img { display: block; color: #fff; font-size: .6em; +} + +#explanationText, +#updatetext { + display: flex; + margin: 0; + opacity: 0.4; } \ No newline at end of file diff --git a/styles/style.css b/styles/style.css index 8dce86e..815b7b5 100644 --- a/styles/style.css +++ b/styles/style.css @@ -1,4 +1,3 @@ -@import url("https://fonts.googleapis.com/css2?family=Montserrat&display=swap"); @import url("https://fonts.googleapis.com/css2?family=Major+Mono+Display&display=swap"); :root { From 0e8e3e48376299ddc7487183697320523cb02073 Mon Sep 17 00:00:00 2001 From: TitanPlayz <118145727+TitanPlayz100@users.noreply.github.com> Date: Tue, 29 Oct 2024 23:12:49 +1100 Subject: [PATCH 5/6] base classic mode --- info.md | 2 ++ src/data/gamemodes.json | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/info.md b/info.md index cb5ac74..659f200 100644 --- a/info.md +++ b/info.md @@ -71,6 +71,8 @@ Things that I am working on based on other changes - arcade mode - rotation centres +- issue holding softdrop and left/right removes piece dropping cooldown + - CHANGE GAME CLASS TO BE EXPORTED MODULES - remove all this.game references in replacement with just the class being imported - **no longer shall the shackles of past object oriented java code bound the multiparadigm eutopia which is javascript** diff --git a/src/data/gamemodes.json b/src/data/gamemodes.json index 418885b..81f814b 100644 --- a/src/data/gamemodes.json +++ b/src/data/gamemodes.json @@ -22,6 +22,7 @@ "goalStat": "", "target": "", "result": "", + "music": "", "compmusic": "", "startBoard": "", @@ -143,5 +144,22 @@ "allspin": true, "allspinminis": true } + }, + "classic": { + "displayName": "Classic", + "objectiveText": "Score", + "goalStat": "score", + "target": "clearlines", + "result": "score", + "settings": { + "requiredLines": 999, + "gravitySpeed": 999, + "lockDelay": 30, + "maxLockMovements": 1, + "nextPieces": 1, + "allowHold": false, + "sidebar": ["time", "score", "pps"], + "clearDelay": 500 + } } } \ No newline at end of file From e01513093a33ea19a50be742244033405e2bc86c Mon Sep 17 00:00:00 2001 From: TitanPlayz <118145727+TitanPlayz100@users.noreply.github.com> Date: Tue, 29 Oct 2024 23:30:57 +1100 Subject: [PATCH 6/6] bug fix zenith --- src/mechanics/gamemode_extended.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/mechanics/gamemode_extended.js b/src/mechanics/gamemode_extended.js index 251c268..0ea9167 100644 --- a/src/mechanics/gamemode_extended.js +++ b/src/mechanics/gamemode_extended.js @@ -109,8 +109,9 @@ export class Zenith { climbSpeedBar.value = point climbSpeedBar.max = require - document.styleSheets[1].cssRules[24].style.backgroundColor = color[speed - 1] - document.styleSheets[1].cssRules[23].style.backgroundColor = color[speed] + // changes css variable, better selection + document.getElementById("climbSpeedBar").style.setProperty("--background-colour", color[speed-1]) + document.getElementById("climbSpeedBar").style.setProperty("--bar-colour", color[speed]) } }