Hold left click to grab items
This commit is contained in:
		
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 418 B After Width: | Height: | Size: 455 B | 
| @@ -57,6 +57,6 @@ export function addButton( | ||||
|     }, | ||||
|     new Rect(topLeftPadded, sizePadded), | ||||
|     enabled, | ||||
|     cbClick, | ||||
|     {onClick: cbClick}, | ||||
|   ); | ||||
| } | ||||
|   | ||||
| @@ -1,8 +1,13 @@ | ||||
| import { D, I } from "./engine/public.ts"; | ||||
| import { Rect } from "./engine/datatypes.ts"; | ||||
|  | ||||
| export type Handlers = { | ||||
|   onClick?: () => 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(); | ||||
|         } | ||||
|   | ||||
| @@ -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; | ||||
|           } | ||||
|         }, | ||||
|       } | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -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); | ||||
|  | ||||
|   | ||||
| @@ -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) { | ||||
|   | ||||
| @@ -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)); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
							
								
								
									
										137
									
								
								src/pickups.ts
									
									
									
									
									
								
							
							
						
						
									
										137
									
								
								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() { } | ||||
| } | ||||
|   | ||||
| @@ -99,9 +99,11 @@ export class SkillsModal { | ||||
|         }, | ||||
|         skillRect, | ||||
|         enabled, | ||||
|         () => { | ||||
|           this.#skillSelection = skill; | ||||
|         }, | ||||
|         { | ||||
|           onClick: () => { | ||||
|             this.#skillSelection = skill; | ||||
|           }, | ||||
|         } | ||||
|       ); | ||||
|       y += 16; | ||||
|     }); | ||||
|   | ||||
| @@ -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), | ||||
|   | ||||
		Reference in New Issue
	
	Block a user