Thrall item pickups
| Before Width: | Height: | Size: 523 B After Width: | Height: | Size: 597 B | 
| Before Width: | Height: | Size: 601 B After Width: | Height: | Size: 660 B | 
| Before Width: | Height: | Size: 614 B After Width: | Height: | Size: 676 B | 
| Before Width: | Height: | Size: 559 B After Width: | Height: | Size: 630 B | 
| Before Width: | Height: | Size: 570 B After Width: | Height: | Size: 652 B | 
| Before Width: | Height: | Size: 572 B After Width: | Height: | Size: 647 B | 
| @@ -107,13 +107,14 @@ export class CheckModal { | ||||
|         let skill = option.skill(); | ||||
|         let skillName = getSkills().get(skill).profile.name; | ||||
|         let hasSkill = getPlayerProgress().hasLearned(skill); | ||||
|         // hasSkill ||= true; | ||||
|         hasSkill ||= true; | ||||
|         if (hasSkill) { | ||||
|           optionLabel = `[${skillName}] ${option.unlockable}`; | ||||
|         } else { | ||||
|           optionLabel = `[Needs ${skillName}] ${option.locked}`; | ||||
|         } | ||||
|         resultMessage = hasSkill ? option.success : option.failure; | ||||
|         accomplished = hasSkill; | ||||
|       } | ||||
|       addButton(this.#drawpile, optionLabel, rect, true, () => { | ||||
|         this.#success = resultMessage; | ||||
|   | ||||
| @@ -3,6 +3,7 @@ import { Grid, Point } from "./engine/datatypes.ts"; | ||||
| import { getThralls } from "./thralls.ts"; | ||||
| import { | ||||
|   LadderPickup, | ||||
|   ThrallCollectionPlatePickup, | ||||
|   ThrallPosterPickup, | ||||
|   ThrallRecruitedPickup, | ||||
| } from "./pickups.ts"; | ||||
| @@ -11,15 +12,15 @@ import { getPlayerProgress } from "./playerprogress.ts"; | ||||
| const BASIC_PLAN = Grid.createGridFromMultilineString(` | ||||
| ##################### | ||||
| #########   ######### | ||||
| ##### A # L # D ##### | ||||
| ##### a #   # d ##### | ||||
| ##### a # L # d ##### | ||||
| ##### A #   # D ##### | ||||
| #####   ## ##   ##### | ||||
| #   ## ##   ## ##   # | ||||
| #Bb               eE# | ||||
| #bB               Ee# | ||||
| #   ## ##   ## ##   # | ||||
| #####   ## ##   ##### | ||||
| ##### c #   # f ##### | ||||
| ##### C # @ # F ##### | ||||
| ##### C #   # F ##### | ||||
| ##### c # @ # f ##### | ||||
| #########   ######### | ||||
| ##################### | ||||
| `); | ||||
| @@ -32,16 +33,20 @@ export function generateManor(): LoadedNewMap { | ||||
|     for (let x = 0; x < BASIC_PLAN.size.w; x++) { | ||||
|       let xy = new Point(x, y); | ||||
|       let cell = map.get(xy); | ||||
|       let unlocked = (ix: number) => | ||||
|         getPlayerProgress().isThrallUnlocked(thralls[ix]); | ||||
|  | ||||
|       let placeThrall = (ix: number) => { | ||||
|         cell.architecture = Architecture.Floor; | ||||
|         if (true || getPlayerProgress().isThrallUnlocked(thralls[ix])) { | ||||
|         if (unlocked(ix)) { | ||||
|           cell.pickup = new ThrallRecruitedPickup(thralls[ix]); | ||||
|         } | ||||
|       }; | ||||
|       let placeThrallPoster = (ix: number) => { | ||||
|         cell.architecture = Architecture.Floor; | ||||
|         if (!getPlayerProgress().isThrallUnlocked(thralls[ix])) { | ||||
|         if (unlocked(ix)) { | ||||
|           cell.pickup = new ThrallCollectionPlatePickup(thralls[ix]); | ||||
|         } else { | ||||
|           cell.pickup = new ThrallPosterPickup(thralls[ix]); | ||||
|         } | ||||
|       }; | ||||
|   | ||||
| @@ -8,9 +8,11 @@ import { | ||||
|   LadderPickup, | ||||
|   LockPickup, | ||||
|   StatPickup, | ||||
|   ThrallItemPickup, | ||||
|   ThrallPickup, | ||||
| } from "./pickups.ts"; | ||||
| import { getPlayerProgress } from "./playerprogress.ts"; | ||||
| import { ItemStage } from "./thralls.ts"; | ||||
|  | ||||
| const WIDTH = 19; | ||||
| const HEIGHT = 19; | ||||
| @@ -334,6 +336,7 @@ function carveVault(knife: Knife, room: Rect, vaultTemplate: VaultTemplate) { | ||||
|     let cell = knife.map.get(goodie); | ||||
|  | ||||
|     if (a.contains(goodie)) { | ||||
|       cell.pickup = new ExperiencePickup(); | ||||
|       let thrall = vaultTemplate.thrall(); | ||||
|       if (!getPlayerProgress().isThrallUnlocked(thrall)) { | ||||
|         cell.pickup = new ThrallPickup(thrall); | ||||
| @@ -350,7 +353,14 @@ function carveVault(knife: Knife, room: Rect, vaultTemplate: VaultTemplate) { | ||||
|     } | ||||
|  | ||||
|     if (d.contains(goodie)) { | ||||
|       // TOOD: Put a fancy item here | ||||
|       cell.pickup = new ExperiencePickup(); | ||||
|  | ||||
|       // replace with a fancy item if nothing is eligible | ||||
|       let thrallItem = vaultTemplate.thrallItem(); | ||||
|       let stage = getPlayerProgress().getThrallItemStage(thrallItem); | ||||
|       if (stage == ItemStage.Untouched) { | ||||
|         cell.pickup = new ThrallItemPickup(thrallItem); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
							
								
								
									
										141
									
								
								src/pickups.ts
									
									
									
									
									
								
							
							
						
						| @@ -1,4 +1,4 @@ | ||||
| import { getThralls, LifeStage, Thrall } from "./thralls.ts"; | ||||
| 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"; | ||||
| @@ -13,8 +13,9 @@ import { | ||||
| } from "./sprites.ts"; | ||||
| import { GridArt } from "./gridart.ts"; | ||||
| import { getCheckModal } from "./checkmodal.ts"; | ||||
| import { Point } from "./engine/datatypes.ts"; | ||||
| import { Point, Size } from "./engine/datatypes.ts"; | ||||
| import { choose } from "./utils.ts"; | ||||
| import { FG_TEXT } from "./colors.ts"; | ||||
|  | ||||
| export type Pickup = | ||||
|   | LockPickup | ||||
| @@ -293,3 +294,139 @@ export class ThrallRecruitedPickup { | ||||
|     return true; | ||||
|   } | ||||
| } | ||||
|  | ||||
| export class ThrallCollectionPlatePickup { | ||||
|   thrall: Thrall; | ||||
|   rewarded: boolean; | ||||
|  | ||||
|   constructor(thrall: Thrall) { | ||||
|     this.thrall = thrall; | ||||
|     this.rewarded = false; | ||||
|   } | ||||
|  | ||||
|   computeCostToClick() { | ||||
|     return 0; | ||||
|   } | ||||
|  | ||||
|   isObstructive() { | ||||
|     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, | ||||
|       }); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   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; | ||||
|   } | ||||
| } | ||||
|  | ||||
| export class ThrallItemPickup { | ||||
|   thrall: Thrall; | ||||
|  | ||||
|   constructor(thrall: Thrall) { | ||||
|     this.thrall = thrall; | ||||
|   } | ||||
|  | ||||
|   computeCostToClick() { | ||||
|     return 100; | ||||
|   } | ||||
|  | ||||
|   isObstructive() { | ||||
|     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, | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   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; | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| import { ALL_STATS, Skill, Stat, SuccessorOption, Wish } from "./datatypes.ts"; | ||||
| import { getSkills } from "./skills.ts"; | ||||
| import { getThralls, LifeStage, Thrall } from "./thralls.ts"; | ||||
| import { getThralls, ItemStage, LifeStage, Thrall } from "./thralls.ts"; | ||||
|  | ||||
| export class PlayerProgress { | ||||
|   #name: string; | ||||
| @@ -15,6 +15,8 @@ export class PlayerProgress { | ||||
|   #untrimmedSkillsAvailable: Skill[]; | ||||
|   #thrallsUnlocked: number[]; | ||||
|   #thrallDamage: Record<number, number>; | ||||
|   #thrallsObtainedItem: number[]; | ||||
|   #thrallsDeliveredItem: number[]; | ||||
|  | ||||
|   constructor(asSuccessor: SuccessorOption, withWish: Wish | null) { | ||||
|     this.#name = asSuccessor.name; | ||||
| @@ -29,6 +31,8 @@ export class PlayerProgress { | ||||
|     this.#untrimmedSkillsAvailable = []; | ||||
|     this.#thrallsUnlocked = []; | ||||
|     this.#thrallDamage = {}; | ||||
|     this.#thrallsObtainedItem = []; | ||||
|     this.#thrallsDeliveredItem = []; | ||||
|  | ||||
|     this.refill(); | ||||
|   } | ||||
| @@ -232,7 +236,6 @@ export class PlayerProgress { | ||||
|  | ||||
|   getThrallLifeStage(thrall: Thrall): LifeStage { | ||||
|     let damage = this.#thrallDamage[thrall.id] ?? 0; | ||||
|     console.log(`damage: ${damage}`); | ||||
|     if (damage < 0.5) { | ||||
|       return LifeStage.Fresh; | ||||
|     } | ||||
| @@ -247,6 +250,30 @@ export class PlayerProgress { | ||||
|     } | ||||
|     return LifeStage.Dead; | ||||
|   } | ||||
|  | ||||
|   obtainThrallItem(thrall: Thrall) { | ||||
|     if (this.#thrallsObtainedItem.indexOf(thrall.id) != -1) { | ||||
|       return; | ||||
|     } | ||||
|     this.#thrallsObtainedItem.push(thrall.id); | ||||
|   } | ||||
|  | ||||
|   deliverThrallItem(thrall: Thrall) { | ||||
|     if (this.#thrallsDeliveredItem.indexOf(thrall.id) != -1) { | ||||
|       return; | ||||
|     } | ||||
|     this.#thrallsDeliveredItem.push(thrall.id); | ||||
|   } | ||||
|  | ||||
|   getThrallItemStage(thrall: Thrall): ItemStage { | ||||
|     if (this.#thrallsDeliveredItem.indexOf(thrall.id) != -1) { | ||||
|       return ItemStage.Delivered; | ||||
|     } | ||||
|     if (this.#thrallsObtainedItem.indexOf(thrall.id) != -1) { | ||||
|       return ItemStage.Obtained; | ||||
|     } | ||||
|     return ItemStage.Untouched; | ||||
|   } | ||||
| } | ||||
|  | ||||
| let active: PlayerProgress | null = null; | ||||
|   | ||||
| @@ -57,41 +57,41 @@ export let sprThrallBat = new Sprite( | ||||
|   imgThrallBat, | ||||
|   new Size(24, 24), | ||||
|   new Point(12, 12), | ||||
|   new Size(3, 1), | ||||
|   3, | ||||
|   new Size(4, 1), | ||||
|   4, | ||||
| ); | ||||
| export let sprThrallCharm = new Sprite( | ||||
|   imgThrallCharm, | ||||
|   new Size(24, 24), | ||||
|   new Point(12, 12), | ||||
|   new Size(3, 1), | ||||
|   3, | ||||
|   new Size(4, 1), | ||||
|   4, | ||||
| ); | ||||
| export let sprThrallLore = new Sprite( | ||||
|   imgThrallLore, | ||||
|   new Size(24, 24), | ||||
|   new Point(12, 12), | ||||
|   new Size(3, 1), | ||||
|   3, | ||||
|   new Size(4, 1), | ||||
|   4, | ||||
| ); | ||||
| export let sprThrallParty = new Sprite( | ||||
|   imgThrallParty, | ||||
|   new Size(24, 24), | ||||
|   new Point(12, 12), | ||||
|   new Size(3, 1), | ||||
|   3, | ||||
|   new Size(4, 1), | ||||
|   4, | ||||
| ); | ||||
| export let sprThrallStare = new Sprite( | ||||
|   imgThrallStare, | ||||
|   new Size(24, 24), | ||||
|   new Point(12, 12), | ||||
|   new Size(3, 1), | ||||
|   3, | ||||
|   new Size(4, 1), | ||||
|   4, | ||||
| ); | ||||
| export let sprThrallStealth = new Sprite( | ||||
|   imgThrallStealth, | ||||
|   new Size(24, 24), | ||||
|   new Point(12, 12), | ||||
|   new Size(3, 1), | ||||
|   3, | ||||
|   new Size(4, 1), | ||||
|   4, | ||||
| ); | ||||
|   | ||||
| @@ -22,6 +22,7 @@ import { | ||||
|   sprThrallStealth, | ||||
| } from "./sprites.ts"; | ||||
| import { Sprite } from "./engine/internal/sprite.ts"; | ||||
| import { getPlayerProgress } from "./playerprogress.ts"; | ||||
|  | ||||
| export type Thrall = { | ||||
|   id: number; | ||||
| @@ -57,6 +58,11 @@ export type ThrallData = { | ||||
|   sprite: Sprite; | ||||
|   posterCheck: CheckData; | ||||
|   initialCheck: CheckData; | ||||
|   itemHint: string; | ||||
|   itemPickupMessage: string; | ||||
|   deliveryMessage: string; | ||||
|   rewardMessage: string; | ||||
|   rewardCallback: () => void; | ||||
|  | ||||
|   lifeStageText: Record<LifeStage, LifeStageText>; | ||||
| }; | ||||
| @@ -69,6 +75,12 @@ export enum LifeStage { | ||||
|   Dead = "dead", | ||||
| } | ||||
|  | ||||
| export enum ItemStage { | ||||
|   Untouched = "untouched", | ||||
|   Obtained = "obtained", | ||||
|   Delivered = "delivered", | ||||
| } | ||||
|  | ||||
| export type LifeStageText = { | ||||
|   prebite: string; | ||||
|   postbite: string; | ||||
| @@ -113,6 +125,16 @@ export let thrallParty = table.add({ | ||||
|       }, | ||||
|     ], | ||||
|   }, | ||||
|   itemHint: | ||||
|     '"Ah. I lost my wedding ring in a poker game.\n\nNot _my_ wedding ring, I won it from a lady."', | ||||
|   itemPickupMessage: | ||||
|     "This antique wedding ring looks like it was worth at least fifty big blinds.", | ||||
|   deliveryMessage: | ||||
|     '"Oh, that? Yeah, I won it." And then lost it, apparently.\n\nHe\'s elated. He will never leave.', | ||||
|   rewardMessage: "Garrett showers you with INT!", | ||||
|   rewardCallback: () => { | ||||
|     getPlayerProgress().add("INT", 10); | ||||
|   }, | ||||
|   lifeStageText: { | ||||
|     fresh: { | ||||
|       prebite: "Garrett flips a poker chip and mutters to himself.", | ||||
| @@ -175,6 +197,16 @@ export let thrallLore = table.add({ | ||||
|       }, | ||||
|     ], | ||||
|   }, | ||||
|   itemHint: | ||||
|     "He sniffs his heels like a wolf.\nMaybe shattering his illusion would be good for him. Maybe it would be really bad...", | ||||
|   itemPickupMessage: | ||||
|     "You can't see yourself in this antique silver mirror. On the other hand, they say silver is effective against wolves.", | ||||
|   deliveryMessage: | ||||
|     "Lupin looks at his own reflection -- with interest, confusion, dismissal, and then deep satisfaction. He loves it. He will never leave.", | ||||
|   rewardMessage: "Lupin showers you with AGI!", | ||||
|   rewardCallback: () => { | ||||
|     getPlayerProgress().add("AGI", 10); | ||||
|   }, | ||||
|   lifeStageText: { | ||||
|     fresh: { | ||||
|       prebite: "Lupin awoos quietly to himself.", | ||||
| @@ -234,6 +266,17 @@ export let thrallBat = table.add({ | ||||
|       }, | ||||
|     ], | ||||
|   }, | ||||
|   itemHint: | ||||
|     "\"I'm from New Zealand, actually.\n\nThere's this regional dish -- I haven't had it in years...\"", | ||||
|   itemPickupMessage: | ||||
|     "This particular instance of gator food resembles an infamous Aotearoan entree: colonial goose.", | ||||
|   deliveryMessage: | ||||
|     'Monica salivates. "This is... this is... simply exquisite!"\n\nShe is happy. She will never leave.', | ||||
|   rewardMessage: "Monica showers you with CHA and INT!", | ||||
|   rewardCallback: () => { | ||||
|     getPlayerProgress().add("CHA", 5); | ||||
|     getPlayerProgress().add("INT", 5); | ||||
|   }, | ||||
|   lifeStageText: { | ||||
|     fresh: { | ||||
|       prebite: "Monica nibbles a pastry.", | ||||
| @@ -294,6 +337,15 @@ export let thrallCharm = table.add({ | ||||
|       }, | ||||
|     ], | ||||
|   }, | ||||
|   itemHint: '"I wish I had some way to look at you... when you\'re not here."', | ||||
|   itemPickupMessage: | ||||
|     "Your photo is going to be in a lot of places if it gets out, but you've got the original.", | ||||
|   deliveryMessage: | ||||
|     "Renfield inhales sharply and widens his stance, trying to hide his physical reaction to your face. He is elated and will never leave.", | ||||
|   rewardMessage: "Renfield showers you with PSI!", | ||||
|   rewardCallback: () => { | ||||
|     getPlayerProgress().add("PSI", 10); | ||||
|   }, | ||||
|   lifeStageText: { | ||||
|     fresh: { | ||||
|       prebite: "Renfield exposes the underside of his jaw.", | ||||
| @@ -351,6 +403,17 @@ export let thrallStealth = table.add({ | ||||
|       }, | ||||
|     ], | ||||
|   }, | ||||
|   itemHint: | ||||
|     '"Do you know what a kobold is? They\'re like me but much smaller."', | ||||
|   itemPickupMessage: | ||||
|     "The freezer is empty except for this frozen kobold, who mutters something about collecting blood for its master.", | ||||
|   deliveryMessage: | ||||
|     "\"That? That's not mine.\" But she wants it. Now it's hers. She will never leave.", | ||||
|   rewardMessage: "Narthyss showers you with CHA and AGI!", | ||||
|   rewardCallback: () => { | ||||
|     getPlayerProgress().add("CHA", 5); | ||||
|     getPlayerProgress().add("AGI", 5); | ||||
|   }, | ||||
|   lifeStageText: { | ||||
|     fresh: { | ||||
|       prebite: "Narthyss is producing a new track on her gamer PC.", | ||||
| @@ -409,6 +472,16 @@ export let thrallStare = table.add({ | ||||
|       }, | ||||
|     ], | ||||
|   }, | ||||
|   itemHint: | ||||
|     "The slight syncopation of its beeping reminds you that it's missing a gear.", | ||||
|   itemPickupMessage: | ||||
|     "This glinting gear would be perfect for a malfunctioning robot.", | ||||
|   deliveryMessage: | ||||
|     "Ridley admires the gear but -- to your surprise -- refuses to jam it into its brain.\n\nThe pup is elated and will never leave.", | ||||
|   rewardMessage: "Ridley showers you with EXP!", | ||||
|   rewardCallback: () => { | ||||
|     getPlayerProgress().addExperience(100); | ||||
|   }, | ||||
|   lifeStageText: { | ||||
|     fresh: { | ||||
|       prebite: "Ridley is solving math problems.", | ||||
|   | ||||
| @@ -33,6 +33,7 @@ import { | ||||
| export type VaultTemplate = { | ||||
|   stats: { primary: Stat; secondary: Stat }; | ||||
|   thrall: () => Thrall; | ||||
|   thrallItem: () => Thrall; | ||||
|   checks: [CheckData, CheckData]; | ||||
| }; | ||||
|  | ||||
| @@ -41,6 +42,7 @@ export const standardVaultTemplates: VaultTemplate[] = [ | ||||
|     // zoo | ||||
|     stats: { primary: "AGI", secondary: "PSI" }, | ||||
|     thrall: () => thrallParty, | ||||
|     thrallItem: () => thrallBat, | ||||
|     checks: [ | ||||
|       { | ||||
|         label: | ||||
| @@ -87,6 +89,7 @@ export const standardVaultTemplates: VaultTemplate[] = [ | ||||
|     // blood bank | ||||
|     stats: { primary: "AGI", secondary: "INT" }, | ||||
|     thrall: () => thrallLore, | ||||
|     thrallItem: () => thrallStealth, | ||||
|     checks: [ | ||||
|       { | ||||
|         label: | ||||
| @@ -132,6 +135,7 @@ export const standardVaultTemplates: VaultTemplate[] = [ | ||||
|     // coffee shop | ||||
|     stats: { primary: "PSI", secondary: "CHA" }, | ||||
|     thrall: () => thrallBat, | ||||
|     thrallItem: () => thrallCharm, | ||||
|     checks: [ | ||||
|       { | ||||
|         label: | ||||
| @@ -178,6 +182,7 @@ export const standardVaultTemplates: VaultTemplate[] = [ | ||||
|     // optometrist | ||||
|     stats: { primary: "PSI", secondary: "PSI" }, | ||||
|     thrall: () => thrallCharm, | ||||
|     thrallItem: () => thrallStare, | ||||
|     checks: [ | ||||
|       { | ||||
|         label: | ||||
| @@ -224,6 +229,7 @@ export const standardVaultTemplates: VaultTemplate[] = [ | ||||
|     // club, | ||||
|     stats: { primary: "CHA", secondary: "PSI" }, | ||||
|     thrall: () => thrallStealth, | ||||
|     thrallItem: () => thrallParty, | ||||
|     checks: [ | ||||
|       { | ||||
|         label: | ||||
| @@ -270,6 +276,7 @@ export const standardVaultTemplates: VaultTemplate[] = [ | ||||
|     // library | ||||
|     stats: { primary: "INT", secondary: "CHA" }, | ||||
|     thrall: () => thrallStare, | ||||
|     thrallItem: () => thrallLore, | ||||
|     checks: [ | ||||
|       { | ||||
|         label: | ||||
|   | ||||