From 0d5dfa6749b149c2aef03ebba5f8d6d5a6f1d558 Mon Sep 17 00:00:00 2001 From: Nyeogmi Date: Sat, 22 Feb 2025 14:25:56 -0800 Subject: [PATCH] Hold left click to grab items --- src/art/pickups/resources.png | Bin 418 -> 455 bytes src/button.ts | 2 +- src/drawpile.ts | 26 ++++++- src/endgamemodal.ts | 28 ++++--- src/engine/internal/drawing.ts | 2 +- src/huntmode.ts | 43 ++++++++--- src/mapgen.ts | 27 ++++--- src/pickups.ts | 137 +++++++++++++++++++++------------ src/skillsmodal.ts | 8 +- src/sprites.ts | 8 -- 10 files changed, 179 insertions(+), 102 deletions(-) diff --git a/src/art/pickups/resources.png b/src/art/pickups/resources.png index 6db5d47d81d3d33fd65d409fbc602c8cf24cc5cf..bc989aa97f5ab1dc339e22af9214f84ef9047864 100644 GIT binary patch delta 347 zcmV-h0i^z-1IGi9F#&;*GDv>_vI1bBlAy}`dw65{PNEY1B9vN5kX(Dw0lDUl_!K~r z25b&s+4~Gb-HjzSGzo|~x1F&DK*iD5DEv1Gmx#8Xg;Yn}PEbU!AHHT_a8yH)S5pw9 zX(~uo*@Nnyhp!oqIgoEZ6Ez$)lmW>pKy|@=R0GhmfJV%b|Fp^iv;coZI}|lyRkE4jeYM;~SitUvD6I&xu6aa$({9be% zLyVNbA2L}0z3sukfJgz@0I?~6BnM)11UA4n?Hf%BL0Ys!NOtJ02e0r30xjDCD)aCC z$7qL8X#a?B2aIL`3f)gcH$baGct}@+)M$r{_%dKfrvR%1E;!{x7U=yaCJPLiGJt`B tHihtz3IGNM>bFBid>JsLQUEQo000&o+%8IVcAo$M002ovPDHLkV1go^lvDr! delta 310 zcmV-60m=Ty1EK?vF#%kWGDv@bAr$~B^Y7t}n>ZsQBUyeKQUNgH zQ^1_t&R7GW;^=D>{+onLMEi?{LO0PJB&+N};opAvn&FrOg0H3^MpC3QQNvL~8I1S< z81ZEQSphH*O;L0^k?q9>h;<8Dg&^CsZ;Y_|`1V6c=(EOIl4BIb;P-!`;}~M36oex_ z1(4)mYz+~Xz0WYz-B@BnlYpRw{Um3B+X;#Y_QTf<4326j@}$ void, + onSqueeze?: () => void, +} + export class DrawPile { - #draws: { depth: number; op: () => void; onClick?: () => void }[]; + #draws: { depth: number; op: () => void; handlers?: Handlers, }[]; #hoveredIndex: number | null; constructor() { @@ -24,7 +29,10 @@ export class DrawPile { op: (hover: boolean) => void, rect: Rect, enabled: boolean, - onClick: () => void, + handlers: { + onClick?: () => void, + onSqueeze?: () => void, + } ) { let position = I.mousePosition?.offset(D.camera); let hovered = false; @@ -37,14 +45,24 @@ export class DrawPile { if (hovered) { this.#hoveredIndex = this.#draws.length; } - this.#draws.push({ depth, op: () => op(hovered), onClick: onClick }); + this.#draws.push({ depth, op: () => op(hovered), handlers }); } executeOnClick() { if (I.isMouseClicked("leftMouse")) { let hi = this.#hoveredIndex; if (hi != null) { - let cb = this.#draws[hi]?.onClick; + let cb = this.#draws[hi]?.handlers?.onClick; + if (cb != null) { + cb(); + } + } + } + + if (I.isMouseDown("leftMouse")) { + let hi = this.#hoveredIndex; + if (hi != null) { + let cb = this.#draws[hi]?.handlers?.onSqueeze; if (cb != null) { cb(); } diff --git a/src/endgamemodal.ts b/src/endgamemodal.ts index 3f958d9..e98df86 100644 --- a/src/endgamemodal.ts +++ b/src/endgamemodal.ts @@ -341,11 +341,13 @@ export class EndgameModal { generalRect, enabled, - () => { - if (this.#selectedSuccessor == ix) { - this.#selectedSuccessor = null; - } else { - this.#selectedSuccessor = ix; + { + onClick: () => { + if (this.#selectedSuccessor == ix) { + this.#selectedSuccessor = null; + } else { + this.#selectedSuccessor = ix; + } } }, ); @@ -399,14 +401,16 @@ export class EndgameModal { }, generalRect, enabled, + { - () => { - if (this.#selectedWish == ix) { - this.#selectedWish = null; - } else { - this.#selectedWish = ix; - } - }, + onClick: () => { + if (this.#selectedWish == ix) { + this.#selectedWish = null; + } else { + this.#selectedWish = ix; + } + }, + } ); } diff --git a/src/engine/internal/drawing.ts b/src/engine/internal/drawing.ts index dc64a17..2b2b014 100644 --- a/src/engine/internal/drawing.ts +++ b/src/engine/internal/drawing.ts @@ -91,7 +91,7 @@ class Drawing { sprite: Sprite, position: Point, ix?: number, - options?: { xScale?: number; yScale: number; angle?: number }, + options?: { xScale?: number; yScale?: number; angle?: number }, ) { position = this.camera.negate().offset(position); diff --git a/src/huntmode.ts b/src/huntmode.ts index 76834ae..90916d0 100644 --- a/src/huntmode.ts +++ b/src/huntmode.ts @@ -115,6 +115,7 @@ export class HuntMode { ).offset(new Point(-192, -192)); this.#updateFov(); + this.#updatePickups(); for (let y = 0; y < this.map.size.h; y += 1) { for (let x = 0; x < this.map.size.w; x += 1) { @@ -156,6 +157,15 @@ export class HuntMode { ); } + #updatePickups() { + for (let y = 0; y < this.map.size.h; y++) { + for (let x = 0; x < this.map.size.w; x++) { + let cell = this.map.get(new Point(x, y)); + cell.pickup?.update(cell); + } + } + } + #inVisibilityRange(x: number, y: number): boolean { let dx = x - this.player.x; let dy = y - this.player.y; @@ -220,18 +230,29 @@ export class HuntMode { }, gridArt.floorRect, true, - () => { - if (cost == null || tooExpensive) { - return; - } - if (pickup?.onClick(cellData)) { - return; - } + { + onClick: () => { + if (cost == null || tooExpensive) { + return; + } + if (pickup?.onClick(cellData)) { + return; + } - getPlayerProgress().spendBlood(cost); - this.movePlayerTo(mapPosition); - getCheckModal().show(null, null); - }, + getPlayerProgress().spendBlood(cost); + this.movePlayerTo(mapPosition); + getCheckModal().show(null, null); + }, + onSqueeze: () => { + // the cost _gates_ squeezes + // but onSqueeze must pay the cost manually if a cost + // is to be paid + if (cost == null || tooExpensive) { + return; + } + pickup?.onSqueeze(cellData); + } + } ); if (pickup != null) { diff --git a/src/mapgen.ts b/src/mapgen.ts index 55f5068..f85f814 100644 --- a/src/mapgen.ts +++ b/src/mapgen.ts @@ -4,10 +4,9 @@ import { choose, shuffle } from "./utils.ts"; import { standardVaultTemplates, VaultTemplate } from "./vaulttemplate.ts"; import { ALL_STATS } from "./datatypes.ts"; import { - ExperiencePickup, + BreakableBlockPickup, ExperiencePickupCallbacks, LadderPickup, - LockPickup, - StatPickup, + LockPickup, StatPickupCallbacks, ThrallItemPickup, ThrallPickup, } from "./pickups.ts"; @@ -279,21 +278,21 @@ function carveVault(knife: Knife, room: Rect, vaultTemplate: VaultTemplate) { if (!(a.contains(xy) || b.contains(xy))) { stat = vaultTemplate.stats.secondary; } - knife.map.get(xy).pickup = new StatPickup(stat); + knife.map.get(xy).pickup = new BreakableBlockPickup(new StatPickupCallbacks(stat)); } } for (let dy = 0; dy < c.size.h; dy++) { for (let dx = 0; dx < c.size.w; dx++) { let xy = c.top.offset(new Point(dx, dy)); - knife.map.get(xy).pickup = new StatPickup(vaultTemplate.stats.primary); + knife.map.get(xy).pickup = new BreakableBlockPickup(new StatPickupCallbacks(vaultTemplate.stats.primary)); } } for (let dy = 0; dy < d.size.h; dy++) { for (let dx = 0; dx < d.size.w; dx++) { let xy = d.top.offset(new Point(dx, dy)); - knife.map.get(xy).pickup = new StatPickup(vaultTemplate.stats.primary); + knife.map.get(xy).pickup = new BreakableBlockPickup(new StatPickupCallbacks(vaultTemplate.stats.primary)); } } @@ -337,7 +336,7 @@ function carveVault(knife: Knife, room: Rect, vaultTemplate: VaultTemplate) { let cell = knife.map.get(goodie); if (a.contains(goodie)) { - cell.pickup = new ExperiencePickup(); + cell.pickup = new BreakableBlockPickup(new ExperiencePickupCallbacks()); let thrall = vaultTemplate.thrall(); if (!getPlayerProgress().isThrallUnlocked(thrall)) { cell.pickup = new ThrallPickup(thrall); @@ -345,16 +344,16 @@ function carveVault(knife: Knife, room: Rect, vaultTemplate: VaultTemplate) { } if (b.contains(goodie)) { - cell.pickup = new ExperiencePickup(); + cell.pickup = new BreakableBlockPickup(new ExperiencePickupCallbacks()); } if (c.contains(goodie)) { - cell.pickup = new ExperiencePickup(); + cell.pickup = new BreakableBlockPickup(new ExperiencePickupCallbacks()); // TODO: Fill this room with the common item for this room } if (d.contains(goodie)) { - cell.pickup = new ExperiencePickup(); + cell.pickup = new BreakableBlockPickup(new ExperiencePickupCallbacks()); // replace with a fancy item if nothing is eligible let thrallItem = vaultTemplate.thrallItem(); @@ -395,10 +394,10 @@ function carveRoom(knife: Knife, room: Rect, label?: string) { new Point(room.size.w - dx - 1, room.size.h - dy - 1), ); let stat = choose(ALL_STATS); - knife.map.get(xy0).pickup = new StatPickup(stat); - knife.map.get(xy1).pickup = new StatPickup(stat); - knife.map.get(xy2).pickup = new StatPickup(stat); - knife.map.get(xy3).pickup = new StatPickup(stat); + knife.map.get(xy0).pickup = new BreakableBlockPickup(new StatPickupCallbacks(stat)); + knife.map.get(xy1).pickup = new BreakableBlockPickup(new StatPickupCallbacks(stat)); + knife.map.get(xy2).pickup = new BreakableBlockPickup(new StatPickupCallbacks(stat)); + knife.map.get(xy3).pickup = new BreakableBlockPickup(new StatPickupCallbacks(stat)); } } } diff --git a/src/pickups.ts b/src/pickups.ts index 19b4408..84feafb 100644 --- a/src/pickups.ts +++ b/src/pickups.ts @@ -19,8 +19,7 @@ import { FG_TEXT } from "./colors.ts"; export type Pickup = | LockPickup - | StatPickup - | ExperiencePickup + | BreakableBlockPickup | LadderPickup | ThrallPickup | ThrallPosterPickup @@ -59,17 +58,24 @@ export class LockPickup { } } + update() { } + onClick(cell: CellView): boolean { getCheckModal().show(this.check, () => (cell.pickup = null)); return true; } + + onSqueeze() { } } -export class StatPickup { - stat: Stat; +const RECOVERY_PER_TICK: number = 0.10; +export class BreakableBlockPickup { + callbacks: StatPickupCallbacks | ExperiencePickupCallbacks; + breakProgress: number; - constructor(stat: Stat) { - this.stat = stat; + constructor(callbacks: StatPickupCallbacks | ExperiencePickupCallbacks) { + this.callbacks = callbacks; + this.breakProgress = 0.0; } computeCostToClick() { @@ -90,58 +96,69 @@ export class StatPickup { drawFloor() {} drawInAir(gridArt: GridArt) { - let statIndex = ALL_STATS.indexOf(this.stat); + let progress = Math.pow(this.breakProgress, 2.15); + let extraMult = 1.0; + let angleRange = 0; + if (progress != 0) { + extraMult = 1.2; + angleRange = 10; + } + + this.callbacks.draw(gridArt.project(5), { + xScale: 2 * (1.0 - progress * 0.7) * extraMult, + yScale: 2 * (1.0 - progress * 0.7) * extraMult, + angle: (2 * progress - 1) * angleRange, + }); + } + + update(cellData: CellView) { + if (this.breakProgress >= 1.0) { + cellData.pickup = null; + this.callbacks.obtain(); + } + + this.breakProgress = Math.max(0.0, this.breakProgress - RECOVERY_PER_TICK); + } + + onClick(): boolean { + return true; + } + + onSqueeze(_cellData: CellView) { + this.breakProgress = Math.min(this.breakProgress + 0.02 + RECOVERY_PER_TICK, 1.0); + } +} + +export class StatPickupCallbacks { + #stat: Stat + + constructor(stat: Stat) { this.#stat = stat; } + + obtain() { + getPlayerProgress().add(this.#stat, 1); + getPlayerProgress().purloinItem(); + } + + draw(at: Point, options: {xScale?: number, yScale?: number, angle?: number}) { + let statIndex = ALL_STATS.indexOf(this.#stat); if (statIndex == -1) { return; } - D.drawSprite(sprStatPickup, gridArt.project(5), statIndex, { - xScale: 2, - yScale: 2, - }); - } - - onClick(): boolean { - getPlayerProgress().add(this.stat, 1); - getPlayerProgress().purloinItem(); - return false; + D.drawSprite(sprStatPickup, at, statIndex, options); } } -export class ExperiencePickup { - computeCostToClick() { - return 100; - } +export class ExperiencePickupCallbacks { + constructor() { } - advertisesBadge() { - return false; - } - - advertisesClickable() { - return true; - } - - isObstructive() { - return true; - } - - drawFloor() {} - drawInAir(gridArt: GridArt) { - D.drawSprite( - sprResourcePickup, - gridArt.project(0.0).offset(new Point(0, -16)), - 0, - { - xScale: 2, - yScale: 2, - }, - ); - } - - onClick(): boolean { + obtain() { getPlayerProgress().addExperience(250); getPlayerProgress().purloinItem(); - return false; + } + + draw(at: Point, options: {xScale?: number, yScale?: number, angle?: number}) { + D.drawSprite(sprResourcePickup, at, 0, options); } } @@ -170,11 +187,15 @@ export class LadderPickup { } drawInAir() {} + update() { } + onClick(): boolean { getPlayerProgress().addBlood(1000); initHuntMode(new HuntMode(getHuntMode().depth + 1, generateMap())); return false; } + + onSqueeze() { } } export class ThrallPickup { @@ -209,6 +230,8 @@ export class ThrallPickup { }); } + update() { } + onClick(cell: CellView): boolean { let data = getThralls().get(this.thrall); getCheckModal().show(data.initialCheck, () => { @@ -217,6 +240,8 @@ export class ThrallPickup { }); return true; } + + onSqueeze() { } } export class ThrallPosterPickup { @@ -251,11 +276,15 @@ export class ThrallPosterPickup { }); } + update() { } + onClick(cell: CellView): boolean { let data = getThralls().get(this.thrall); getCheckModal().show(data.posterCheck, () => (cell.pickup = null)); return true; } + + onSqueeze() { } } export class ThrallRecruitedPickup { @@ -306,6 +335,8 @@ export class ThrallRecruitedPickup { }); } + update() { } + onClick(_cell: CellView): boolean { this.spokenTo = true; if (this.bitten) { @@ -352,6 +383,8 @@ export class ThrallRecruitedPickup { ); return true; } + + onSqueeze() { } } export class ThrallCollectionPlatePickup { @@ -415,6 +448,8 @@ export class ThrallCollectionPlatePickup { } } + update() { } + onClick(_cell: CellView): boolean { let lifeStage = getPlayerProgress().getThrallLifeStage(this.thrall); let itemStage = getPlayerProgress().getThrallItemStage(this.thrall); @@ -472,6 +507,8 @@ export class ThrallCollectionPlatePickup { // getCheckModal().show(this.check, () => (cell.pickup = null)); return true; } + + onSqueeze() { } } export class ThrallItemPickup { @@ -507,6 +544,8 @@ export class ThrallItemPickup { }); } + update() { } + onClick(cell: CellView): boolean { let data = getThralls().get(this.thrall); @@ -521,4 +560,6 @@ export class ThrallItemPickup { getPlayerProgress().obtainThrallItem(this.thrall); return true; } + + onSqueeze() { } } diff --git a/src/skillsmodal.ts b/src/skillsmodal.ts index dc6e627..a8cb43c 100644 --- a/src/skillsmodal.ts +++ b/src/skillsmodal.ts @@ -99,9 +99,11 @@ export class SkillsModal { }, skillRect, enabled, - () => { - this.#skillSelection = skill; - }, + { + onClick: () => { + this.#skillSelection = skill; + }, + } ); y += 16; }); diff --git a/src/sprites.ts b/src/sprites.ts index 5b916c2..54b897d 100644 --- a/src/sprites.ts +++ b/src/sprites.ts @@ -1,6 +1,5 @@ import { Sprite } from "./engine/internal/sprite.ts"; -import imgRaccoon from "./art/characters/raccoon.png"; import imgResourcePickup from "./art/pickups/resources.png"; import imgStatPickup from "./art/pickups/stats.png"; import imgLadder from "./art/pickups/ladder.png"; @@ -14,13 +13,6 @@ import imgThrallParty from "./art/thralls/thrall_party.png"; import imgThrallStare from "./art/thralls/thrall_stare.png"; import imgThrallStealth from "./art/thralls/thrall_stealth.png"; -export let sprRaccoon = new Sprite( - imgRaccoon, - new Size(64, 64), - new Point(32, 32), - new Size(1, 1), - 1, -); export let sprResourcePickup = new Sprite( imgResourcePickup, new Size(32, 32),