Hold left click to grab items

This commit is contained in:
Pyrex 2025-02-22 14:25:56 -08:00
parent bfc1e53f3e
commit 0d5dfa6749
10 changed files with 179 additions and 102 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 418 B

After

Width:  |  Height:  |  Size: 455 B

View File

@ -57,6 +57,6 @@ export function addButton(
}, },
new Rect(topLeftPadded, sizePadded), new Rect(topLeftPadded, sizePadded),
enabled, enabled,
cbClick, {onClick: cbClick},
); );
} }

View File

@ -1,8 +1,13 @@
import { D, I } from "./engine/public.ts"; import { D, I } from "./engine/public.ts";
import { Rect } from "./engine/datatypes.ts"; import { Rect } from "./engine/datatypes.ts";
export type Handlers = {
onClick?: () => void,
onSqueeze?: () => void,
}
export class DrawPile { export class DrawPile {
#draws: { depth: number; op: () => void; onClick?: () => void }[]; #draws: { depth: number; op: () => void; handlers?: Handlers, }[];
#hoveredIndex: number | null; #hoveredIndex: number | null;
constructor() { constructor() {
@ -24,7 +29,10 @@ export class DrawPile {
op: (hover: boolean) => void, op: (hover: boolean) => void,
rect: Rect, rect: Rect,
enabled: boolean, enabled: boolean,
onClick: () => void, handlers: {
onClick?: () => void,
onSqueeze?: () => void,
}
) { ) {
let position = I.mousePosition?.offset(D.camera); let position = I.mousePosition?.offset(D.camera);
let hovered = false; let hovered = false;
@ -37,14 +45,24 @@ export class DrawPile {
if (hovered) { if (hovered) {
this.#hoveredIndex = this.#draws.length; this.#hoveredIndex = this.#draws.length;
} }
this.#draws.push({ depth, op: () => op(hovered), onClick: onClick }); this.#draws.push({ depth, op: () => op(hovered), handlers });
} }
executeOnClick() { executeOnClick() {
if (I.isMouseClicked("leftMouse")) { if (I.isMouseClicked("leftMouse")) {
let hi = this.#hoveredIndex; let hi = this.#hoveredIndex;
if (hi != null) { 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) { if (cb != null) {
cb(); cb();
} }

View File

@ -341,11 +341,13 @@ export class EndgameModal {
generalRect, generalRect,
enabled, enabled,
() => { {
if (this.#selectedSuccessor == ix) { onClick: () => {
this.#selectedSuccessor = null; if (this.#selectedSuccessor == ix) {
} else { this.#selectedSuccessor = null;
this.#selectedSuccessor = ix; } else {
this.#selectedSuccessor = ix;
}
} }
}, },
); );
@ -399,14 +401,16 @@ export class EndgameModal {
}, },
generalRect, generalRect,
enabled, enabled,
{
() => { onClick: () => {
if (this.#selectedWish == ix) { if (this.#selectedWish == ix) {
this.#selectedWish = null; this.#selectedWish = null;
} else { } else {
this.#selectedWish = ix; this.#selectedWish = ix;
} }
}, },
}
); );
} }

View File

@ -91,7 +91,7 @@ class Drawing {
sprite: Sprite, sprite: Sprite,
position: Point, position: Point,
ix?: number, ix?: number,
options?: { xScale?: number; yScale: number; angle?: number }, options?: { xScale?: number; yScale?: number; angle?: number },
) { ) {
position = this.camera.negate().offset(position); position = this.camera.negate().offset(position);

View File

@ -115,6 +115,7 @@ export class HuntMode {
).offset(new Point(-192, -192)); ).offset(new Point(-192, -192));
this.#updateFov(); this.#updateFov();
this.#updatePickups();
for (let y = 0; y < this.map.size.h; y += 1) { for (let y = 0; y < this.map.size.h; y += 1) {
for (let x = 0; x < this.map.size.w; x += 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 { #inVisibilityRange(x: number, y: number): boolean {
let dx = x - this.player.x; let dx = x - this.player.x;
let dy = y - this.player.y; let dy = y - this.player.y;
@ -220,18 +230,29 @@ export class HuntMode {
}, },
gridArt.floorRect, gridArt.floorRect,
true, true,
() => { {
if (cost == null || tooExpensive) { onClick: () => {
return; if (cost == null || tooExpensive) {
} return;
if (pickup?.onClick(cellData)) { }
return; if (pickup?.onClick(cellData)) {
} return;
}
getPlayerProgress().spendBlood(cost); getPlayerProgress().spendBlood(cost);
this.movePlayerTo(mapPosition); this.movePlayerTo(mapPosition);
getCheckModal().show(null, null); 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) { if (pickup != null) {

View File

@ -4,10 +4,9 @@ import { choose, shuffle } from "./utils.ts";
import { standardVaultTemplates, VaultTemplate } from "./vaulttemplate.ts"; import { standardVaultTemplates, VaultTemplate } from "./vaulttemplate.ts";
import { ALL_STATS } from "./datatypes.ts"; import { ALL_STATS } from "./datatypes.ts";
import { import {
ExperiencePickup, BreakableBlockPickup, ExperiencePickupCallbacks,
LadderPickup, LadderPickup,
LockPickup, LockPickup, StatPickupCallbacks,
StatPickup,
ThrallItemPickup, ThrallItemPickup,
ThrallPickup, ThrallPickup,
} from "./pickups.ts"; } from "./pickups.ts";
@ -279,21 +278,21 @@ function carveVault(knife: Knife, room: Rect, vaultTemplate: VaultTemplate) {
if (!(a.contains(xy) || b.contains(xy))) { if (!(a.contains(xy) || b.contains(xy))) {
stat = vaultTemplate.stats.secondary; 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 dy = 0; dy < c.size.h; dy++) {
for (let dx = 0; dx < c.size.w; dx++) { for (let dx = 0; dx < c.size.w; dx++) {
let xy = c.top.offset(new Point(dx, dy)); 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 dy = 0; dy < d.size.h; dy++) {
for (let dx = 0; dx < d.size.w; dx++) { for (let dx = 0; dx < d.size.w; dx++) {
let xy = d.top.offset(new Point(dx, dy)); 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); let cell = knife.map.get(goodie);
if (a.contains(goodie)) { if (a.contains(goodie)) {
cell.pickup = new ExperiencePickup(); cell.pickup = new BreakableBlockPickup(new ExperiencePickupCallbacks());
let thrall = vaultTemplate.thrall(); let thrall = vaultTemplate.thrall();
if (!getPlayerProgress().isThrallUnlocked(thrall)) { if (!getPlayerProgress().isThrallUnlocked(thrall)) {
cell.pickup = new ThrallPickup(thrall); cell.pickup = new ThrallPickup(thrall);
@ -345,16 +344,16 @@ function carveVault(knife: Knife, room: Rect, vaultTemplate: VaultTemplate) {
} }
if (b.contains(goodie)) { if (b.contains(goodie)) {
cell.pickup = new ExperiencePickup(); cell.pickup = new BreakableBlockPickup(new ExperiencePickupCallbacks());
} }
if (c.contains(goodie)) { 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 // TODO: Fill this room with the common item for this room
} }
if (d.contains(goodie)) { if (d.contains(goodie)) {
cell.pickup = new ExperiencePickup(); cell.pickup = new BreakableBlockPickup(new ExperiencePickupCallbacks());
// replace with a fancy item if nothing is eligible // replace with a fancy item if nothing is eligible
let thrallItem = vaultTemplate.thrallItem(); 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), new Point(room.size.w - dx - 1, room.size.h - dy - 1),
); );
let stat = choose(ALL_STATS); let stat = choose(ALL_STATS);
knife.map.get(xy0).pickup = new StatPickup(stat); knife.map.get(xy0).pickup = new BreakableBlockPickup(new StatPickupCallbacks(stat));
knife.map.get(xy1).pickup = new StatPickup(stat); knife.map.get(xy1).pickup = new BreakableBlockPickup(new StatPickupCallbacks(stat));
knife.map.get(xy2).pickup = new StatPickup(stat); knife.map.get(xy2).pickup = new BreakableBlockPickup(new StatPickupCallbacks(stat));
knife.map.get(xy3).pickup = new StatPickup(stat); knife.map.get(xy3).pickup = new BreakableBlockPickup(new StatPickupCallbacks(stat));
} }
} }
} }

View File

@ -19,8 +19,7 @@ import { FG_TEXT } from "./colors.ts";
export type Pickup = export type Pickup =
| LockPickup | LockPickup
| StatPickup | BreakableBlockPickup
| ExperiencePickup
| LadderPickup | LadderPickup
| ThrallPickup | ThrallPickup
| ThrallPosterPickup | ThrallPosterPickup
@ -59,17 +58,24 @@ export class LockPickup {
} }
} }
update() { }
onClick(cell: CellView): boolean { onClick(cell: CellView): boolean {
getCheckModal().show(this.check, () => (cell.pickup = null)); getCheckModal().show(this.check, () => (cell.pickup = null));
return true; return true;
} }
onSqueeze() { }
} }
export class StatPickup { const RECOVERY_PER_TICK: number = 0.10;
stat: Stat; export class BreakableBlockPickup {
callbacks: StatPickupCallbacks | ExperiencePickupCallbacks;
breakProgress: number;
constructor(stat: Stat) { constructor(callbacks: StatPickupCallbacks | ExperiencePickupCallbacks) {
this.stat = stat; this.callbacks = callbacks;
this.breakProgress = 0.0;
} }
computeCostToClick() { computeCostToClick() {
@ -90,58 +96,69 @@ export class StatPickup {
drawFloor() {} drawFloor() {}
drawInAir(gridArt: GridArt) { 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) { if (statIndex == -1) {
return; return;
} }
D.drawSprite(sprStatPickup, gridArt.project(5), statIndex, { D.drawSprite(sprStatPickup, at, statIndex, options);
xScale: 2,
yScale: 2,
});
}
onClick(): boolean {
getPlayerProgress().add(this.stat, 1);
getPlayerProgress().purloinItem();
return false;
} }
} }
export class ExperiencePickup { export class ExperiencePickupCallbacks {
computeCostToClick() { constructor() { }
return 100;
}
advertisesBadge() { obtain() {
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 {
getPlayerProgress().addExperience(250); getPlayerProgress().addExperience(250);
getPlayerProgress().purloinItem(); 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() {} drawInAir() {}
update() { }
onClick(): boolean { onClick(): boolean {
getPlayerProgress().addBlood(1000); getPlayerProgress().addBlood(1000);
initHuntMode(new HuntMode(getHuntMode().depth + 1, generateMap())); initHuntMode(new HuntMode(getHuntMode().depth + 1, generateMap()));
return false; return false;
} }
onSqueeze() { }
} }
export class ThrallPickup { export class ThrallPickup {
@ -209,6 +230,8 @@ export class ThrallPickup {
}); });
} }
update() { }
onClick(cell: CellView): boolean { onClick(cell: CellView): boolean {
let data = getThralls().get(this.thrall); let data = getThralls().get(this.thrall);
getCheckModal().show(data.initialCheck, () => { getCheckModal().show(data.initialCheck, () => {
@ -217,6 +240,8 @@ export class ThrallPickup {
}); });
return true; return true;
} }
onSqueeze() { }
} }
export class ThrallPosterPickup { export class ThrallPosterPickup {
@ -251,11 +276,15 @@ export class ThrallPosterPickup {
}); });
} }
update() { }
onClick(cell: CellView): boolean { onClick(cell: CellView): boolean {
let data = getThralls().get(this.thrall); let data = getThralls().get(this.thrall);
getCheckModal().show(data.posterCheck, () => (cell.pickup = null)); getCheckModal().show(data.posterCheck, () => (cell.pickup = null));
return true; return true;
} }
onSqueeze() { }
} }
export class ThrallRecruitedPickup { export class ThrallRecruitedPickup {
@ -306,6 +335,8 @@ export class ThrallRecruitedPickup {
}); });
} }
update() { }
onClick(_cell: CellView): boolean { onClick(_cell: CellView): boolean {
this.spokenTo = true; this.spokenTo = true;
if (this.bitten) { if (this.bitten) {
@ -352,6 +383,8 @@ export class ThrallRecruitedPickup {
); );
return true; return true;
} }
onSqueeze() { }
} }
export class ThrallCollectionPlatePickup { export class ThrallCollectionPlatePickup {
@ -415,6 +448,8 @@ export class ThrallCollectionPlatePickup {
} }
} }
update() { }
onClick(_cell: CellView): boolean { onClick(_cell: CellView): boolean {
let lifeStage = getPlayerProgress().getThrallLifeStage(this.thrall); let lifeStage = getPlayerProgress().getThrallLifeStage(this.thrall);
let itemStage = getPlayerProgress().getThrallItemStage(this.thrall); let itemStage = getPlayerProgress().getThrallItemStage(this.thrall);
@ -472,6 +507,8 @@ export class ThrallCollectionPlatePickup {
// getCheckModal().show(this.check, () => (cell.pickup = null)); // getCheckModal().show(this.check, () => (cell.pickup = null));
return true; return true;
} }
onSqueeze() { }
} }
export class ThrallItemPickup { export class ThrallItemPickup {
@ -507,6 +544,8 @@ export class ThrallItemPickup {
}); });
} }
update() { }
onClick(cell: CellView): boolean { onClick(cell: CellView): boolean {
let data = getThralls().get(this.thrall); let data = getThralls().get(this.thrall);
@ -521,4 +560,6 @@ export class ThrallItemPickup {
getPlayerProgress().obtainThrallItem(this.thrall); getPlayerProgress().obtainThrallItem(this.thrall);
return true; return true;
} }
onSqueeze() { }
} }

View File

@ -99,9 +99,11 @@ export class SkillsModal {
}, },
skillRect, skillRect,
enabled, enabled,
() => { {
this.#skillSelection = skill; onClick: () => {
}, this.#skillSelection = skill;
},
}
); );
y += 16; y += 16;
}); });

View File

@ -1,6 +1,5 @@
import { Sprite } from "./engine/internal/sprite.ts"; import { Sprite } from "./engine/internal/sprite.ts";
import imgRaccoon from "./art/characters/raccoon.png";
import imgResourcePickup from "./art/pickups/resources.png"; import imgResourcePickup from "./art/pickups/resources.png";
import imgStatPickup from "./art/pickups/stats.png"; import imgStatPickup from "./art/pickups/stats.png";
import imgLadder from "./art/pickups/ladder.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 imgThrallStare from "./art/thralls/thrall_stare.png";
import imgThrallStealth from "./art/thralls/thrall_stealth.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( export let sprResourcePickup = new Sprite(
imgResourcePickup, imgResourcePickup,
new Size(32, 32), new Size(32, 32),