import { getThralls, ItemStage, LifeStage, Thrall } from "./thralls.ts"; import { CellView, CheckData } from "./newmap.ts"; import { getPlayerProgress } from "./playerprogress.ts"; import { getHuntMode, HuntMode, initHuntMode } from "./huntmode.ts"; import { generateMap } from "./mapgen.ts"; import { ALL_STATS, Stat } from "./datatypes.ts"; import { D } from "./engine/public.ts"; import { sprLadder, sprLock, sprResourcePickup, sprStatPickup, } from "./sprites.ts"; import { GridArt } from "./gridart.ts"; import { getCheckModal } from "./checkmodal.ts"; import { Point, Size } from "./engine/datatypes.ts"; import { choose } from "./utils.ts"; import { FG_TEXT } from "./colors.ts"; export type Pickup = | LockPickup | BreakableBlockPickup | LadderPickup | ThrallPickup | ThrallPosterPickup | ThrallRecruitedPickup; export class LockPickup { check: CheckData; constructor(check: CheckData) { this.check = check; } computeCostToClick() { return 0; } advertisesBadge() { return false; } advertisesClickable() { return true; } blocksMovement() { return true; } obstructsVision() { return true; } drawFloor() {} drawInAir(gridArt: GridArt) { for (let z = 0; z < 5; z += 0.25) { D.drawSprite(sprLock, gridArt.project(z), 0, { xScale: 2.0, yScale: 2.0, }); } } update() {} onClick(cell: CellView): boolean { getCheckModal().show(this.check, () => (cell.pickup = null)); return true; } onSqueeze() {} } const RECOVERY_PER_TICK: number = 0.1; export class BreakableBlockPickup { callbacks: StatPickupCallbacks | ExperiencePickupCallbacks; breakProgress: number; constructor(callbacks: StatPickupCallbacks | ExperiencePickupCallbacks) { this.callbacks = callbacks; this.breakProgress = 0.0; } computeCostToClick() { return this.callbacks.cost; } advertisesBadge() { return false; } advertisesClickable() { return true; } blocksMovement() { return true; } obstructsVision() { return true; } drawFloor() {} drawInAir(gridArt: GridArt) { 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) { getPlayerProgress().spendBlood(this.callbacks.cost); 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; } get cost(): number { return 100; } 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, at, statIndex, options); } } export class ExperiencePickupCallbacks { constructor() {} get cost(): number { return 100; } obtain() { getPlayerProgress().addExperience(250); getPlayerProgress().purloinItem(); } draw( at: Point, options: { xScale?: number; yScale?: number; angle?: number }, ) { D.drawSprite(sprResourcePickup, at, 0, options); } } export class LadderPickup { computeCostToClick() { return 0; } advertisesBadge() { return false; } advertisesClickable() { return true; } blocksMovement() { return true; } obstructsVision() { return false; } drawFloor(gridArt: GridArt) { D.drawSprite(sprLadder, gridArt.project(0.0), 0, { xScale: 2.0, yScale: 2.0, }); } drawInAir() {} update() {} onClick(): boolean { getPlayerProgress().addBlood(1000); initHuntMode(new HuntMode(getHuntMode().depth + 1, generateMap())); return false; } onSqueeze() {} } export class ThrallPickup { thrall: Thrall; constructor(thrall: Thrall) { this.thrall = thrall; } computeCostToClick() { return 0; } advertisesBadge() { return false; } advertisesClickable() { return true; } blocksMovement() { return true; } obstructsVision() { return false; } drawFloor() {} drawInAir(gridArt: GridArt) { let data = getThralls().get(this.thrall); D.drawSprite(data.sprite, gridArt.project(0.0), 0, { xScale: 2.0, yScale: 2.0, }); } update() {} onClick(cell: CellView): boolean { let data = getThralls().get(this.thrall); getCheckModal().show(data.initialCheck, () => { getPlayerProgress().unlockThrall(this.thrall); cell.pickup = null; }); return true; } onSqueeze() {} } export class ThrallPosterPickup { thrall: Thrall; constructor(thrall: Thrall) { this.thrall = thrall; } computeCostToClick() { return 0; } advertisesBadge() { return false; } advertisesClickable() { return true; } blocksMovement() { return true; } obstructsVision() { return false; } drawFloor() {} drawInAir(gridArt: GridArt) { let data = getThralls().get(this.thrall); D.drawSprite(data.sprite, gridArt.project(0.0), 2, { xScale: 2.0, yScale: 2.0, }); } update() {} onClick(cell: CellView): boolean { let data = getThralls().get(this.thrall); getCheckModal().show(data.posterCheck, () => (cell.pickup = null)); return true; } onSqueeze() {} } export class ThrallRecruitedPickup { thrall: Thrall; spokenTo: boolean; bitten: boolean; constructor(thrall: Thrall) { this.thrall = thrall; this.spokenTo = false; this.bitten = false; } computeCostToClick() { return 0; } advertisesBadge() { return !this.spokenTo; } advertisesClickable() { return !this.bitten; } blocksMovement() { return true; } obstructsVision() { return false; } drawFloor() {} drawInAir(gridArt: GridArt) { let data = getThralls().get(this.thrall); let lifeStage = getPlayerProgress().getThrallLifeStage(this.thrall); let ix = 0; let rot = 0; if (lifeStage == LifeStage.Vampirized) { ix = 1; } if (lifeStage == LifeStage.Dead) { ix = 1; rot = 270; } D.drawSprite(data.sprite, gridArt.project(0.0), ix, { xScale: 2.0, yScale: 2.0, angle: rot, }); } update() {} onClick(_cell: CellView): boolean { this.spokenTo = true; if (this.bitten) { return true; } let data = getThralls().get(this.thrall); let lifeStage = getPlayerProgress().getThrallLifeStage(this.thrall); let text = data.lifeStageText[lifeStage]; getCheckModal().show( { label: `${text.prebite}`, options: [ { isChoice: true, countsAsSuccess: true, unlockable: "Bite!", success: text.postbite, }, { isChoice: true, countsAsSuccess: false, unlockable: "Refrain", success: "Maybe next time.", }, ], }, () => { this.bitten = true; getPlayerProgress().addBlood( lifeStage == LifeStage.Fresh ? 1000 : lifeStage == LifeStage.Average ? 500 : lifeStage == LifeStage.Poor ? 300 : lifeStage == LifeStage.Vampirized ? 1500 // lethal bite : // lifeStage == LifeStage.Dead ? 100, ); getPlayerProgress().damageThrall(this.thrall, choose([0.9])); }, ); return true; } onSqueeze() {} } export class ThrallCollectionPlatePickup { thrall: Thrall; rewarded: boolean; constructor(thrall: Thrall) { this.thrall = thrall; this.rewarded = false; } computeCostToClick() { return 0; } advertisesBadge() { let lifeStage = getPlayerProgress().getThrallLifeStage(this.thrall); let itemStage = getPlayerProgress().getThrallItemStage(this.thrall); if (itemStage == ItemStage.Obtained) { // the player should deliver it! make sure they see a badge informing them of that return true; } if ( itemStage == ItemStage.Delivered && lifeStage != LifeStage.Dead && !this.rewarded ) { // the player should collect it! make sure they see a badge informing them of that return true; } // OK, the only interaction is to get a hint return false; } advertisesClickable() { return !this.rewarded; } blocksMovement() { return false; } obstructsVision() { return false; } drawFloor() {} drawInAir(gridArt: GridArt) { let itemStage = getPlayerProgress().getThrallItemStage(this.thrall); let data = getThralls().get(this.thrall); if (itemStage != ItemStage.Delivered) { D.drawRect( gridArt.project(0).offset(new Point(-18, -18)), new Size(36, 36), FG_TEXT, ); } else { D.drawSprite(data.sprite, gridArt.project(2), 3, { xScale: 2.0, yScale: 2.0, }); } } update() {} onClick(_cell: CellView): boolean { let lifeStage = getPlayerProgress().getThrallLifeStage(this.thrall); let itemStage = getPlayerProgress().getThrallItemStage(this.thrall); let data = getThralls().get(this.thrall); // if (itemStage == ItemStage.Untouched) { itemStage = ItemStage.Obtained; } if (itemStage == ItemStage.Untouched) { if (lifeStage == LifeStage.Dead) { getCheckModal().show( { label: "There's no point in delivering this now.", options: [], }, null, ); } else { getCheckModal().show( { label: data.itemHint, options: [], }, null, ); } } else if (itemStage == ItemStage.Obtained) { getPlayerProgress().deliverThrallItem(this.thrall); if (lifeStage != LifeStage.Dead) { getCheckModal().show( { label: data.deliveryMessage, options: [], }, null, ); } } else { if (lifeStage == LifeStage.Dead) { // nothing happens } else if (this.rewarded) { // nothing happens } else { this.rewarded = true; getCheckModal().show( { label: data.rewardMessage, options: [], }, null, ); data.rewardCallback(); } } // getCheckModal().show(this.check, () => (cell.pickup = null)); return true; } onSqueeze() {} } export class ThrallItemPickup { thrall: Thrall; constructor(thrall: Thrall) { this.thrall = thrall; } computeCostToClick() { return 0; } advertisesBadge() { return false; } advertisesClickable() { return true; } blocksMovement() { return true; } obstructsVision() { return false; } drawFloor() {} drawInAir(gridArt: GridArt) { let data = getThralls().get(this.thrall); D.drawSprite(data.sprite, gridArt.project(2), 3, { xScale: 2.0, yScale: 2.0, }); } update() {} onClick(cell: CellView): boolean { let data = getThralls().get(this.thrall); cell.pickup = null; getCheckModal().show( { label: data.itemPickupMessage, options: [], }, null, ); getPlayerProgress().obtainThrallItem(this.thrall); return true; } onSqueeze() {} }