clibuddy 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +60 -0
- package/dist/adventure/adventureUI.d.ts +24 -0
- package/dist/adventure/adventureUI.js +290 -0
- package/dist/adventure/adventures.d.ts +4 -0
- package/dist/adventure/adventures.js +206 -0
- package/dist/adventure/biomes.d.ts +30 -0
- package/dist/adventure/biomes.js +80 -0
- package/dist/adventure/combat/combat.d.ts +14 -0
- package/dist/adventure/combat/combat.js +638 -0
- package/dist/adventure/combat/combatUI.d.ts +5 -0
- package/dist/adventure/combat/combatUI.js +116 -0
- package/dist/adventure/combat/conditions.d.ts +20 -0
- package/dist/adventure/combat/conditions.js +111 -0
- package/dist/adventure/combat/enemies.d.ts +4 -0
- package/dist/adventure/combat/enemies.js +430 -0
- package/dist/adventure/combat/gear.d.ts +3 -0
- package/dist/adventure/combat/gear.js +199 -0
- package/dist/adventure/combat/skills.d.ts +6 -0
- package/dist/adventure/combat/skills.js +197 -0
- package/dist/adventure/combat.d.ts +31 -0
- package/dist/adventure/combat.js +732 -0
- package/dist/adventure/combatUI.d.ts +5 -0
- package/dist/adventure/combatUI.js +116 -0
- package/dist/adventure/endless.d.ts +18 -0
- package/dist/adventure/endless.js +154 -0
- package/dist/adventure/enemies.d.ts +4 -0
- package/dist/adventure/enemies.js +320 -0
- package/dist/adventure/engine.d.ts +20 -0
- package/dist/adventure/engine.js +137 -0
- package/dist/adventure/gear.d.ts +3 -0
- package/dist/adventure/gear.js +149 -0
- package/dist/adventure/generation/biomes.d.ts +30 -0
- package/dist/adventure/generation/biomes.js +102 -0
- package/dist/adventure/generation/endless.d.ts +18 -0
- package/dist/adventure/generation/endless.js +154 -0
- package/dist/adventure/generation/generator.d.ts +9 -0
- package/dist/adventure/generation/generator.js +245 -0
- package/dist/adventure/generation/templates.d.ts +25 -0
- package/dist/adventure/generation/templates.js +228 -0
- package/dist/adventure/generator.d.ts +9 -0
- package/dist/adventure/generator.js +245 -0
- package/dist/adventure/skills.d.ts +6 -0
- package/dist/adventure/skills.js +197 -0
- package/dist/adventure/templates.d.ts +25 -0
- package/dist/adventure/templates.js +228 -0
- package/dist/adventure/types.d.ts +236 -0
- package/dist/adventure/types.js +97 -0
- package/dist/app/state.d.ts +49 -0
- package/dist/app/state.js +51 -0
- package/dist/buddy/activities.d.ts +16 -0
- package/dist/buddy/activities.js +90 -0
- package/dist/buddy/decay.d.ts +3 -0
- package/dist/buddy/decay.js +45 -0
- package/dist/buddy/leveling.d.ts +11 -0
- package/dist/buddy/leveling.js +25 -0
- package/dist/buddy/roll.d.ts +4 -0
- package/dist/buddy/roll.js +61 -0
- package/dist/buddy/species.d.ts +4 -0
- package/dist/buddy/species.js +592 -0
- package/dist/buddy/titles.d.ts +17 -0
- package/dist/buddy/titles.js +89 -0
- package/dist/buddy/types.d.ts +92 -0
- package/dist/buddy/types.js +21 -0
- package/dist/commands/actions.d.ts +2 -0
- package/dist/commands/actions.js +141 -0
- package/dist/commands/admin.d.ts +2 -0
- package/dist/commands/admin.js +202 -0
- package/dist/commands/registry.d.ts +25 -0
- package/dist/commands/registry.js +31 -0
- package/dist/commands/social.d.ts +2 -0
- package/dist/commands/social.js +92 -0
- package/dist/dialogue/engine.d.ts +7 -0
- package/dist/dialogue/engine.js +68 -0
- package/dist/dialogue/lines.d.ts +26 -0
- package/dist/dialogue/lines.js +294 -0
- package/dist/events/engine.d.ts +20 -0
- package/dist/events/engine.js +51 -0
- package/dist/events/events.d.ts +13 -0
- package/dist/events/events.js +149 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +1665 -0
- package/dist/inventory/finding.d.ts +11 -0
- package/dist/inventory/finding.js +99 -0
- package/dist/inventory/items.d.ts +31 -0
- package/dist/inventory/items.js +63 -0
- package/dist/minigames/copycat.d.ts +2 -0
- package/dist/minigames/copycat.js +153 -0
- package/dist/minigames/fetch.d.ts +2 -0
- package/dist/minigames/fetch.js +179 -0
- package/dist/minigames/moodmatch.d.ts +2 -0
- package/dist/minigames/moodmatch.js +144 -0
- package/dist/minigames/quickpaws.d.ts +2 -0
- package/dist/minigames/quickpaws.js +142 -0
- package/dist/minigames/registry.d.ts +5 -0
- package/dist/minigames/registry.js +16 -0
- package/dist/minigames/rpsplus.d.ts +2 -0
- package/dist/minigames/rpsplus.js +168 -0
- package/dist/minigames/treasurehunt.d.ts +2 -0
- package/dist/minigames/treasurehunt.js +146 -0
- package/dist/minigames/types.d.ts +30 -0
- package/dist/minigames/types.js +69 -0
- package/dist/rendering/commandPalette.d.ts +16 -0
- package/dist/rendering/commandPalette.js +77 -0
- package/dist/rendering/display.d.ts +9 -0
- package/dist/rendering/display.js +231 -0
- package/dist/rendering/inventoryUI.d.ts +14 -0
- package/dist/rendering/inventoryUI.js +99 -0
- package/dist/rendering/items.d.ts +7 -0
- package/dist/rendering/items.js +34 -0
- package/dist/rendering/listUtils.d.ts +3 -0
- package/dist/rendering/listUtils.js +24 -0
- package/dist/rendering/minigameUI.d.ts +11 -0
- package/dist/rendering/minigameUI.js +37 -0
- package/dist/rendering/overlayUI.d.ts +24 -0
- package/dist/rendering/overlayUI.js +184 -0
- package/dist/rendering/scene.d.ts +8 -0
- package/dist/rendering/scene.js +87 -0
- package/dist/rendering/screen.d.ts +43 -0
- package/dist/rendering/screen.js +97 -0
- package/dist/sound/sound.d.ts +11 -0
- package/dist/sound/sound.js +55 -0
- package/dist/state/save.d.ts +5 -0
- package/dist/state/save.js +100 -0
- package/dist/state/settings.d.ts +7 -0
- package/dist/state/settings.js +81 -0
- package/dist/story/mainStory.d.ts +4 -0
- package/dist/story/mainStory.js +3111 -0
- package/dist/story/npcs.d.ts +17 -0
- package/dist/story/npcs.js +137 -0
- package/dist/story/progress.d.ts +26 -0
- package/dist/story/progress.js +168 -0
- package/dist/story/seasonal.d.ts +6 -0
- package/dist/story/seasonal.js +96 -0
- package/dist/story/shop.d.ts +7 -0
- package/dist/story/shop.js +26 -0
- package/dist/story/sideQuests.d.ts +4 -0
- package/dist/story/sideQuests.js +151 -0
- package/dist/story/types.d.ts +61 -0
- package/dist/story/types.js +36 -0
- package/dist/updates.d.ts +23 -0
- package/dist/updates.js +142 -0
- package/package.json +53 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Zippy
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# CLIBuddy
|
|
2
|
+
|
|
3
|
+
A tamagotchi-style virtual pet that lives in your terminal. Feed it, play with it, take it on adventures, fight bosses, and watch it grow.
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
/\_/\
|
|
7
|
+
( o.o ) < Hi! I'm your buddy!
|
|
8
|
+
> ^ <
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install -g clibuddy
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Or try without installing:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npx clibuddy
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## What is this?
|
|
24
|
+
|
|
25
|
+
CLIBuddy is a full-screen terminal game where you raise an ASCII art companion. Your buddy has needs (hunger, happiness, health, energy), gains XP, levels up, earns titles, finds items, and goes on adventures with a full story campaign.
|
|
26
|
+
|
|
27
|
+
**Features:**
|
|
28
|
+
- 7 species to roll (Common to Legendary rarity)
|
|
29
|
+
- Deep turn-based combat with elements, skills, combos, and boss phases
|
|
30
|
+
- 8-chapter hand-crafted story with 4 recurring NPCs and ~141 rooms
|
|
31
|
+
- 6 minigames
|
|
32
|
+
- 32 gear items with passive effects
|
|
33
|
+
- 50 earnable titles
|
|
34
|
+
- Procedural dungeon expeditions with 6 biomes
|
|
35
|
+
- Idle events, dialogue, and item discovery
|
|
36
|
+
- Persistent save file (your buddy remembers you)
|
|
37
|
+
|
|
38
|
+
## Controls
|
|
39
|
+
|
|
40
|
+
- Type `/` to open the command palette
|
|
41
|
+
- Arrow keys to navigate, Enter to select
|
|
42
|
+
- All gameplay happens through commands: `/feed`, `/play`, `/sleep`, `/adventure`, etc.
|
|
43
|
+
- Type `/help` for a full command list
|
|
44
|
+
|
|
45
|
+
## Requirements
|
|
46
|
+
|
|
47
|
+
- Node.js 18+
|
|
48
|
+
- A terminal that supports ANSI escape codes (most modern terminals)
|
|
49
|
+
|
|
50
|
+
## Update
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
npm update -g clibuddy
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Or type `/update` inside the game.
|
|
57
|
+
|
|
58
|
+
## License
|
|
59
|
+
|
|
60
|
+
MIT
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { BuddyState } from "../buddy/types";
|
|
2
|
+
import { AdventureDef, AdventureState } from "./types";
|
|
3
|
+
import { QuestChain } from "../story/types";
|
|
4
|
+
export interface AdventureMenuState {
|
|
5
|
+
active: boolean;
|
|
6
|
+
tab: "story" | "expedition" | "endless";
|
|
7
|
+
selectedIndex: number;
|
|
8
|
+
expeditionDifficulty: number;
|
|
9
|
+
expeditionBiomeIndex: number;
|
|
10
|
+
questChains: QuestChain[];
|
|
11
|
+
selectedChainIndex: number;
|
|
12
|
+
}
|
|
13
|
+
export declare function createAdventureMenu(): AdventureMenuState;
|
|
14
|
+
export declare function openAdventureMenu(questChains?: QuestChain[]): AdventureMenuState;
|
|
15
|
+
export declare function switchTab(menu: AdventureMenuState): AdventureMenuState;
|
|
16
|
+
export declare function moveAdventureSelection(menu: AdventureMenuState, delta: number): AdventureMenuState;
|
|
17
|
+
/** Switch between quest chains with left/right */
|
|
18
|
+
export declare function changeQuestChain(menu: AdventureMenuState, delta: number): AdventureMenuState;
|
|
19
|
+
export declare function changeExpeditionBiome(menu: AdventureMenuState, delta: number): AdventureMenuState;
|
|
20
|
+
export declare function getSelectedBiomeId(menu: AdventureMenuState): string;
|
|
21
|
+
export declare function renderAdventureMenu(menu: AdventureMenuState, state: BuddyState): string[];
|
|
22
|
+
export declare function renderRoom(advState: AdventureState, adventure: AdventureDef, buddyName: string): string;
|
|
23
|
+
export declare function renderResult(advState: AdventureState, adventure: AdventureDef, buddyName: string): string;
|
|
24
|
+
//# sourceMappingURL=adventureUI.d.ts.map
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createAdventureMenu = createAdventureMenu;
|
|
4
|
+
exports.openAdventureMenu = openAdventureMenu;
|
|
5
|
+
exports.switchTab = switchTab;
|
|
6
|
+
exports.moveAdventureSelection = moveAdventureSelection;
|
|
7
|
+
exports.changeQuestChain = changeQuestChain;
|
|
8
|
+
exports.changeExpeditionBiome = changeExpeditionBiome;
|
|
9
|
+
exports.getSelectedBiomeId = getSelectedBiomeId;
|
|
10
|
+
exports.renderAdventureMenu = renderAdventureMenu;
|
|
11
|
+
exports.renderRoom = renderRoom;
|
|
12
|
+
exports.renderResult = renderResult;
|
|
13
|
+
const types_1 = require("./types");
|
|
14
|
+
const biomes_1 = require("./generation/biomes");
|
|
15
|
+
const types_2 = require("../story/types");
|
|
16
|
+
const npcs_1 = require("../story/npcs");
|
|
17
|
+
const listUtils_1 = require("../rendering/listUtils");
|
|
18
|
+
const screen_1 = require("../rendering/screen");
|
|
19
|
+
function createAdventureMenu() {
|
|
20
|
+
return { active: false, tab: "story", selectedIndex: 0, expeditionDifficulty: 1, expeditionBiomeIndex: 0, questChains: [], selectedChainIndex: 0 };
|
|
21
|
+
}
|
|
22
|
+
function openAdventureMenu(questChains = []) {
|
|
23
|
+
return { active: true, tab: "story", selectedIndex: 0, expeditionDifficulty: 1, expeditionBiomeIndex: 0, questChains, selectedChainIndex: 0 };
|
|
24
|
+
}
|
|
25
|
+
function switchTab(menu) {
|
|
26
|
+
const tabs = ["story", "expedition", "endless"];
|
|
27
|
+
const idx = (tabs.indexOf(menu.tab) + 1) % tabs.length;
|
|
28
|
+
return { ...menu, tab: tabs[idx], selectedIndex: 0 };
|
|
29
|
+
}
|
|
30
|
+
function moveAdventureSelection(menu, delta) {
|
|
31
|
+
if (menu.tab === "story") {
|
|
32
|
+
const chain = menu.questChains[menu.selectedChainIndex];
|
|
33
|
+
if (!chain)
|
|
34
|
+
return menu;
|
|
35
|
+
return { ...menu, selectedIndex: (0, listUtils_1.moveListIndex)(menu.selectedIndex, delta, chain.chapters.length) };
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
const next = Math.max(1, Math.min(10, menu.expeditionDifficulty + delta));
|
|
39
|
+
return { ...menu, expeditionDifficulty: next };
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/** Switch between quest chains with left/right */
|
|
43
|
+
function changeQuestChain(menu, delta) {
|
|
44
|
+
if (menu.questChains.length === 0)
|
|
45
|
+
return menu;
|
|
46
|
+
const next = (0, listUtils_1.moveListIndex)(menu.selectedChainIndex, delta, menu.questChains.length);
|
|
47
|
+
return { ...menu, selectedChainIndex: next, selectedIndex: 0 };
|
|
48
|
+
}
|
|
49
|
+
function changeExpeditionBiome(menu, delta) {
|
|
50
|
+
const biomeIds = (0, biomes_1.getAllBiomeIds)();
|
|
51
|
+
let next = menu.expeditionBiomeIndex + delta;
|
|
52
|
+
if (next < 0)
|
|
53
|
+
next = biomeIds.length - 1;
|
|
54
|
+
if (next >= biomeIds.length)
|
|
55
|
+
next = 0;
|
|
56
|
+
return { ...menu, expeditionBiomeIndex: next };
|
|
57
|
+
}
|
|
58
|
+
function getSelectedBiomeId(menu) {
|
|
59
|
+
return (0, biomes_1.getAllBiomeIds)()[menu.expeditionBiomeIndex];
|
|
60
|
+
}
|
|
61
|
+
const DIFFICULTY_COLORS = {
|
|
62
|
+
easy: screen_1.ansi.colors.green,
|
|
63
|
+
medium: screen_1.ansi.colors.yellow,
|
|
64
|
+
hard: screen_1.ansi.colors.red,
|
|
65
|
+
boss: screen_1.ansi.colors.magenta,
|
|
66
|
+
};
|
|
67
|
+
const DIFF_LABELS = {
|
|
68
|
+
1: "Peaceful Stroll", 2: "Gentle Walk", 3: "Challenging Trek",
|
|
69
|
+
4: "Tough Hike", 5: "Dangerous Quest", 6: "Heroic Mission",
|
|
70
|
+
7: "Perilous Journey", 8: "Epic Expedition", 9: "Legendary Quest",
|
|
71
|
+
10: "Nightmare Run",
|
|
72
|
+
};
|
|
73
|
+
function renderAdventureMenu(menu, state) {
|
|
74
|
+
const lines = [];
|
|
75
|
+
lines.push(` ${screen_1.ansi.dim}${"─".repeat(48)}${screen_1.ansi.reset}`);
|
|
76
|
+
// Tab bar
|
|
77
|
+
const storyTab = menu.tab === "story" ? `${screen_1.ansi.bold}${screen_1.ansi.colors.yellow}[Story]${screen_1.ansi.reset}` : `${screen_1.ansi.dim}[Story]${screen_1.ansi.reset}`;
|
|
78
|
+
const expTab = menu.tab === "expedition" ? `${screen_1.ansi.bold}${screen_1.ansi.colors.cyan}[Expedition]${screen_1.ansi.reset}` : `${screen_1.ansi.dim}[Expedition]${screen_1.ansi.reset}`;
|
|
79
|
+
const endTab = menu.tab === "endless" ? `${screen_1.ansi.bold}${screen_1.ansi.colors.magenta}[Endless]${screen_1.ansi.reset}` : `${screen_1.ansi.dim}[Endless]${screen_1.ansi.reset}`;
|
|
80
|
+
lines.push(` ${storyTab} ${expTab} ${endTab} ${screen_1.ansi.dim}Tab${screen_1.ansi.reset}`);
|
|
81
|
+
lines.push(` ${screen_1.ansi.dim}${"─".repeat(48)}${screen_1.ansi.reset}`);
|
|
82
|
+
if (menu.tab === "story") {
|
|
83
|
+
renderQuestsTab(lines, menu, state);
|
|
84
|
+
}
|
|
85
|
+
else if (menu.tab === "expedition") {
|
|
86
|
+
renderExpeditionTab(lines, menu, state);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
renderEndlessTab(lines, state);
|
|
90
|
+
}
|
|
91
|
+
lines.push(` ${screen_1.ansi.dim}${"─".repeat(48)}${screen_1.ansi.reset}`);
|
|
92
|
+
return lines;
|
|
93
|
+
}
|
|
94
|
+
function renderQuestsTab(lines, menu, state) {
|
|
95
|
+
if (menu.questChains.length === 0) {
|
|
96
|
+
lines.push(` ${screen_1.ansi.dim}No quest chains available yet.${screen_1.ansi.reset}`);
|
|
97
|
+
lines.push(` ${screen_1.ansi.dim}Check the Story tab for adventures!${screen_1.ansi.reset}`);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const progress = state.questProgress;
|
|
101
|
+
const chain = menu.questChains[menu.selectedChainIndex];
|
|
102
|
+
if (!chain)
|
|
103
|
+
return;
|
|
104
|
+
// Chain selector (left/right)
|
|
105
|
+
const chainLabel = chain.type === "main" ? `${screen_1.ansi.colors.yellow}★${screen_1.ansi.reset}` : chain.type === "seasonal" ? `${screen_1.ansi.colors.cyan}❄${screen_1.ansi.reset}` : `${screen_1.ansi.dim}○${screen_1.ansi.reset}`;
|
|
106
|
+
lines.push(` ${chainLabel} ${screen_1.ansi.bold}${chain.name}${screen_1.ansi.reset} ${screen_1.ansi.dim}(${menu.selectedChainIndex + 1}/${menu.questChains.length}) ←→ to switch${screen_1.ansi.reset}`);
|
|
107
|
+
lines.push(` ${screen_1.ansi.dim}${chain.description}${screen_1.ansi.reset}`);
|
|
108
|
+
lines.push("");
|
|
109
|
+
// Chapter list
|
|
110
|
+
for (let i = 0; i < chain.chapters.length; i++) {
|
|
111
|
+
const chapter = chain.chapters[i];
|
|
112
|
+
const completed = (0, types_2.isChapterComplete)(chapter.id, progress);
|
|
113
|
+
const blockReason = (0, types_2.canStartChapter)(chapter, progress, state.level);
|
|
114
|
+
const selected = i === menu.selectedIndex;
|
|
115
|
+
const pointer = selected ? `${screen_1.ansi.colors.cyan}▸${screen_1.ansi.reset}` : " ";
|
|
116
|
+
if (completed) {
|
|
117
|
+
lines.push(` ${pointer} ${screen_1.ansi.colors.green}✓${screen_1.ansi.reset} ${screen_1.ansi.dim}${chapter.name}${screen_1.ansi.reset}`);
|
|
118
|
+
}
|
|
119
|
+
else if (!blockReason) {
|
|
120
|
+
// Available
|
|
121
|
+
if (selected) {
|
|
122
|
+
lines.push(` ${pointer} ${screen_1.ansi.bold}${chapter.name}${screen_1.ansi.reset} ${screen_1.ansi.dim}Lv${chapter.levelRequired}${screen_1.ansi.reset}`);
|
|
123
|
+
lines.push(` ${screen_1.ansi.dim}${chapter.description}${screen_1.ansi.reset}`);
|
|
124
|
+
lines.push(` ${screen_1.ansi.dim}Rewards: ${chapter.rewards.xp} XP, ${chapter.rewards.gold} gold${chapter.rewards.titleId ? ", new title" : ""}${screen_1.ansi.reset}`);
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
lines.push(` ${pointer} ${chapter.name} ${screen_1.ansi.dim}Lv${chapter.levelRequired}${screen_1.ansi.reset}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
// Locked
|
|
132
|
+
lines.push(` ${pointer} ${screen_1.ansi.dim}🔒 ${chapter.name} — ${blockReason}${screen_1.ansi.reset}`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
lines.push(` ${screen_1.ansi.dim}Enter to start, Esc to close${screen_1.ansi.reset}`);
|
|
136
|
+
}
|
|
137
|
+
function renderExpeditionTab(lines, menu, state) {
|
|
138
|
+
const diff = menu.expeditionDifficulty;
|
|
139
|
+
const biomeIds = (0, biomes_1.getAllBiomeIds)();
|
|
140
|
+
const biomeId = biomeIds[menu.expeditionBiomeIndex];
|
|
141
|
+
const biome = biomes_1.BIOMES[biomeId];
|
|
142
|
+
const label = DIFF_LABELS[diff] ?? `Level ${diff}`;
|
|
143
|
+
const roomCount = diff <= 2 ? "3-4" : diff <= 4 ? "4-5" : diff <= 6 ? "5-6" : diff <= 8 ? "6-7" : "7-8";
|
|
144
|
+
const hasBoss = diff >= 5;
|
|
145
|
+
const diffColor = diff <= 2 ? screen_1.ansi.colors.green : diff <= 5 ? screen_1.ansi.colors.yellow : diff <= 8 ? screen_1.ansi.colors.red : screen_1.ansi.colors.magenta;
|
|
146
|
+
lines.push(` ${screen_1.ansi.bold}Difficulty:${screen_1.ansi.reset} ${diffColor}${diff}${screen_1.ansi.reset} — ${label} ${screen_1.ansi.dim}↑↓${screen_1.ansi.reset}`);
|
|
147
|
+
lines.push(` ${screen_1.ansi.dim}${roomCount} rooms${hasBoss ? ", boss fight" : ""}, Lv${Math.max(1, (diff - 1) * 2)}+ required${screen_1.ansi.reset}`);
|
|
148
|
+
lines.push("");
|
|
149
|
+
lines.push(` ${screen_1.ansi.bold}Biome:${screen_1.ansi.reset} ${biome?.name ?? biomeId} ${screen_1.ansi.dim}←→ to change${screen_1.ansi.reset}`);
|
|
150
|
+
lines.push("");
|
|
151
|
+
const energyCost = 10 + diff * 3;
|
|
152
|
+
const hungerCost = 5 + diff * 2;
|
|
153
|
+
lines.push(` ${screen_1.ansi.dim}Cost: ${energyCost} energy, ${hungerCost} hunger${screen_1.ansi.reset}`);
|
|
154
|
+
const canGo = state.level >= Math.max(1, (diff - 1) * 2) && state.stats.energy >= energyCost && state.stats.hunger >= hungerCost;
|
|
155
|
+
if (canGo) {
|
|
156
|
+
lines.push(` ${screen_1.ansi.colors.green}Ready to embark!${screen_1.ansi.reset}`);
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
lines.push(` ${screen_1.ansi.colors.red}Not ready — check level/energy/hunger${screen_1.ansi.reset}`);
|
|
160
|
+
}
|
|
161
|
+
lines.push(` ${screen_1.ansi.dim}Enter to generate & start, Esc to close${screen_1.ansi.reset}`);
|
|
162
|
+
}
|
|
163
|
+
function renderEndlessTab(lines, state) {
|
|
164
|
+
lines.push(` ${screen_1.ansi.bold}${screen_1.ansi.colors.magenta}The Endless Depths${screen_1.ansi.reset}`);
|
|
165
|
+
lines.push(` ${screen_1.ansi.dim}How deep can you go? One life, escalating difficulty.${screen_1.ansi.reset}`);
|
|
166
|
+
lines.push("");
|
|
167
|
+
lines.push(` ${screen_1.ansi.dim}• Rooms get harder every 3 depths${screen_1.ansi.reset}`);
|
|
168
|
+
lines.push(` ${screen_1.ansi.dim}• Boss every 5 depths${screen_1.ansi.reset}`);
|
|
169
|
+
lines.push(` ${screen_1.ansi.dim}• HP carries between rooms${screen_1.ansi.reset}`);
|
|
170
|
+
lines.push(` ${screen_1.ansi.dim}• Biome rotates each room${screen_1.ansi.reset}`);
|
|
171
|
+
lines.push(` ${screen_1.ansi.dim}• Defeat = run over${screen_1.ansi.reset}`);
|
|
172
|
+
lines.push("");
|
|
173
|
+
lines.push(` ${screen_1.ansi.dim}Requires: Level 5+, 20 energy, 15 hunger${screen_1.ansi.reset}`);
|
|
174
|
+
const canGo = state.level >= 5 && state.stats.energy >= 20 && state.stats.hunger >= 15;
|
|
175
|
+
if (canGo) {
|
|
176
|
+
lines.push(` ${screen_1.ansi.colors.green}Ready to descend!${screen_1.ansi.reset}`);
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
lines.push(` ${screen_1.ansi.colors.red}Not ready — check level/energy/hunger${screen_1.ansi.reset}`);
|
|
180
|
+
}
|
|
181
|
+
lines.push(` ${screen_1.ansi.dim}Enter to begin your descent${screen_1.ansi.reset}`);
|
|
182
|
+
}
|
|
183
|
+
// ─── Adventure Room Rendering ────────────────────────────────
|
|
184
|
+
function renderRoom(advState, adventure, buddyName) {
|
|
185
|
+
const room = (0, types_1.getRoom)(adventure, advState.currentRoomId);
|
|
186
|
+
if (!room)
|
|
187
|
+
return " Room not found!";
|
|
188
|
+
const progress = (0, types_1.getRoomProgress)(advState);
|
|
189
|
+
const lines = [];
|
|
190
|
+
lines.push("");
|
|
191
|
+
if (adventure.isScene || adventure.hideRoomCounter) {
|
|
192
|
+
lines.push(` ${screen_1.ansi.bold}${adventure.name}${screen_1.ansi.reset}`);
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
lines.push(` ${screen_1.ansi.bold}${adventure.name}${screen_1.ansi.reset} ${screen_1.ansi.dim}Room ${progress}/${adventure.rooms.length}${screen_1.ansi.reset} ${screen_1.ansi.dim}Morale: ${advState.morale}%${screen_1.ansi.reset}`);
|
|
196
|
+
}
|
|
197
|
+
lines.push(` ${screen_1.ansi.dim}${"─".repeat(44)}${screen_1.ansi.reset}`);
|
|
198
|
+
lines.push("");
|
|
199
|
+
lines.push(` ${screen_1.ansi.bold}${room.title}${screen_1.ansi.reset}`);
|
|
200
|
+
lines.push("");
|
|
201
|
+
// Show NPC ASCII art if present
|
|
202
|
+
if (room.npcsPresent && room.npcsPresent.length > 0) {
|
|
203
|
+
for (const npcId of room.npcsPresent) {
|
|
204
|
+
const npc = (0, npcs_1.getNPC)(npcId);
|
|
205
|
+
if (npc) {
|
|
206
|
+
lines.push(` ${screen_1.ansi.dim}${npc.name} the ${npc.title}${screen_1.ansi.reset}`);
|
|
207
|
+
for (const artLine of npc.art) {
|
|
208
|
+
lines.push(` ${screen_1.ansi.colors.cyan}${artLine}${screen_1.ansi.reset}`);
|
|
209
|
+
}
|
|
210
|
+
lines.push("");
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
// Paged text display — show LINES_PER_PAGE lines per page
|
|
215
|
+
const LINES_PER_PAGE = 3;
|
|
216
|
+
const totalPages = Math.ceil(room.text.length / LINES_PER_PAGE);
|
|
217
|
+
const currentPage = advState.textPage;
|
|
218
|
+
const textFullyShown = advState.textFullyShown || currentPage >= totalPages - 1;
|
|
219
|
+
// Show text up to current page
|
|
220
|
+
const linesToShow = Math.min(room.text.length, (currentPage + 1) * LINES_PER_PAGE);
|
|
221
|
+
for (let i = 0; i < linesToShow; i++) {
|
|
222
|
+
lines.push(` ${room.text[i]}`);
|
|
223
|
+
}
|
|
224
|
+
lines.push("");
|
|
225
|
+
if (!textFullyShown) {
|
|
226
|
+
// More text to show — prompt to continue reading
|
|
227
|
+
lines.push(` ${screen_1.ansi.dim}▼ Press Enter to continue reading... (${currentPage + 1}/${totalPages})${screen_1.ansi.reset}`);
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
// All text shown — show buddy line + choices
|
|
231
|
+
lines.push(` ${buddyName}: "${advState.buddyReaction}"`);
|
|
232
|
+
lines.push("");
|
|
233
|
+
if (room.type === "narrative" && room.choices) {
|
|
234
|
+
for (let i = 0; i < room.choices.length; i++) {
|
|
235
|
+
lines.push(` [${i + 1}] ${room.choices[i].label}`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
else if (room.type === "event" && room.eventChoices) {
|
|
239
|
+
// Check if event was already resolved
|
|
240
|
+
const alreadyResolved = room.eventChoices.some((c) => c.outcome === advState.buddyReaction);
|
|
241
|
+
if (alreadyResolved) {
|
|
242
|
+
lines.push(` ${screen_1.ansi.dim}Press Enter to continue...${screen_1.ansi.reset}`);
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
for (let i = 0; i < room.eventChoices.length; i++) {
|
|
246
|
+
lines.push(` [${i + 1}] ${room.eventChoices[i].label}`);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
else if (room.type === "treasure") {
|
|
251
|
+
lines.push(` ${screen_1.ansi.dim}Press Enter to search...${screen_1.ansi.reset}`);
|
|
252
|
+
}
|
|
253
|
+
else if (room.type === "rest") {
|
|
254
|
+
lines.push(` [1] Rest here (+${room.healAmount} HP)`);
|
|
255
|
+
lines.push(` [2] Keep moving`);
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
lines.push(` ${screen_1.ansi.dim}Press Enter to continue...${screen_1.ansi.reset}`);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
lines.push("");
|
|
262
|
+
return lines.join("\n");
|
|
263
|
+
}
|
|
264
|
+
// ─── Result Screen ───────────────────────────────────────────
|
|
265
|
+
function renderResult(advState, adventure, buddyName) {
|
|
266
|
+
const lines = [];
|
|
267
|
+
lines.push("");
|
|
268
|
+
lines.push(` ${screen_1.ansi.bold}${screen_1.ansi.colors.green}Adventure Complete!${screen_1.ansi.reset}`);
|
|
269
|
+
lines.push(` ${screen_1.ansi.dim}${"─".repeat(44)}${screen_1.ansi.reset}`);
|
|
270
|
+
lines.push("");
|
|
271
|
+
lines.push(` ${screen_1.ansi.bold}${adventure.name}${screen_1.ansi.reset} — Cleared!`);
|
|
272
|
+
lines.push("");
|
|
273
|
+
lines.push(` Battles: ${advState.battlesWon} won, ${advState.battlesLost} lost, ${advState.battlesFled} fled`);
|
|
274
|
+
if (advState.lootCollected.length > 0) {
|
|
275
|
+
const lootNames = advState.lootCollected.map((id) => id.replace(/_/g, " "));
|
|
276
|
+
lines.push(` Loot: ${lootNames.join(", ")}`);
|
|
277
|
+
}
|
|
278
|
+
lines.push(` XP earned: ${screen_1.ansi.bold}${advState.xpEarned}${screen_1.ansi.reset}`);
|
|
279
|
+
lines.push(` Final morale: ${advState.morale}%`);
|
|
280
|
+
if (advState.damageTaken === 0 && advState.battlesWon > 0) {
|
|
281
|
+
lines.push(` ${screen_1.ansi.colors.yellow}★ PERFECT — No damage taken!${screen_1.ansi.reset}`);
|
|
282
|
+
}
|
|
283
|
+
lines.push("");
|
|
284
|
+
lines.push(` ${buddyName}: "${advState.buddyReaction}"`);
|
|
285
|
+
lines.push("");
|
|
286
|
+
lines.push(` ${screen_1.ansi.dim}Press Enter to return...${screen_1.ansi.reset}`);
|
|
287
|
+
lines.push("");
|
|
288
|
+
return lines.join("\n");
|
|
289
|
+
}
|
|
290
|
+
//# sourceMappingURL=adventureUI.js.map
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ADVENTURES = void 0;
|
|
4
|
+
exports.getAdventure = getAdventure;
|
|
5
|
+
const types_1 = require("./types");
|
|
6
|
+
function makeAdventure(def) {
|
|
7
|
+
return { ...def, roomMap: (0, types_1.buildRoomMap)(def.rooms) };
|
|
8
|
+
}
|
|
9
|
+
exports.ADVENTURES = [
|
|
10
|
+
makeAdventure({
|
|
11
|
+
id: "rustling_woods",
|
|
12
|
+
name: "The Rustling Woods",
|
|
13
|
+
description: "A gentle forest with small critters. Perfect for beginners!",
|
|
14
|
+
difficulty: "easy",
|
|
15
|
+
levelRequired: 1,
|
|
16
|
+
energyCost: 15,
|
|
17
|
+
hungerCost: 10,
|
|
18
|
+
completionXp: 30,
|
|
19
|
+
startRoomId: "rw_entry",
|
|
20
|
+
rooms: [
|
|
21
|
+
{
|
|
22
|
+
id: "rw_entry", type: "narrative", title: "Forest Entrance",
|
|
23
|
+
text: ["Sunlight filters through the canopy.", "A worn path splits into two directions.", "You hear chirping to the left and rustling to the right."],
|
|
24
|
+
buddyLine: "Ooh, an adventure! Which way?",
|
|
25
|
+
choices: [
|
|
26
|
+
{ label: "Follow the chirping (left)", nextRoomId: "rw_clearing" },
|
|
27
|
+
{ label: "Investigate the rustling (right)", nextRoomId: "rw_thicket" },
|
|
28
|
+
],
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: "rw_clearing", type: "combat", title: "Friendly Clearing",
|
|
32
|
+
text: ["You enter a sunny clearing. A squirrel spots you and gets defensive!"],
|
|
33
|
+
buddyLine: "It's just a squirrel... a very angry squirrel!",
|
|
34
|
+
enemyId: "squirrel",
|
|
35
|
+
nextRoomId: "rw_treasure",
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
id: "rw_thicket", type: "combat", title: "Dense Thicket",
|
|
39
|
+
text: ["You push through thick bushes. A bouncing mushroom blocks the path!"],
|
|
40
|
+
buddyLine: "Is that mushroom... alive?!",
|
|
41
|
+
enemyId: "mushroom",
|
|
42
|
+
nextRoomId: "rw_treasure",
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
id: "rw_treasure", type: "treasure", title: "Hidden Hollow",
|
|
46
|
+
text: ["Behind some fallen leaves, you find a small stash!"],
|
|
47
|
+
buddyLine: "Ooh, shiny! Finders keepers!",
|
|
48
|
+
lootPool: ["berry", "herb", "wooden_stick", "leaf_cloak"],
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
}),
|
|
52
|
+
makeAdventure({
|
|
53
|
+
id: "crystal_caves",
|
|
54
|
+
name: "The Crystal Caves",
|
|
55
|
+
description: "Underground caverns with glowing crystals and tricky foes.",
|
|
56
|
+
difficulty: "medium",
|
|
57
|
+
levelRequired: 3,
|
|
58
|
+
energyCost: 20,
|
|
59
|
+
hungerCost: 15,
|
|
60
|
+
completionXp: 50,
|
|
61
|
+
startRoomId: "cc_entry",
|
|
62
|
+
rooms: [
|
|
63
|
+
{
|
|
64
|
+
id: "cc_entry", type: "narrative", title: "Cave Mouth",
|
|
65
|
+
text: ["A cave entrance glimmers with faint blue light.", "The air is cool and damp. Two tunnels branch ahead."],
|
|
66
|
+
buddyLine: "It's pretty in here... and a little spooky.",
|
|
67
|
+
choices: [
|
|
68
|
+
{ label: "Take the narrow crystal tunnel", nextRoomId: "cc_crystal" },
|
|
69
|
+
{ label: "Wade through the shallow stream", nextRoomId: "cc_stream" },
|
|
70
|
+
],
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
id: "cc_crystal", type: "combat", title: "Crystal Chamber",
|
|
74
|
+
text: ["Crystals hum as you enter. A golem awakens from the wall!"],
|
|
75
|
+
buddyLine: "That rock is MOVING!",
|
|
76
|
+
enemyId: "golem",
|
|
77
|
+
nextRoomId: "cc_pool",
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
id: "cc_stream", type: "combat", title: "Underground Stream",
|
|
81
|
+
text: ["A crow perches on a stalactite, eyeing you menacingly."],
|
|
82
|
+
buddyLine: "How did a crow get down here?!",
|
|
83
|
+
enemyId: "crow",
|
|
84
|
+
nextRoomId: "cc_pool",
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
id: "cc_pool", type: "rest", title: "Glowing Pool",
|
|
88
|
+
text: ["A warm pool glows softly. It looks healing..."],
|
|
89
|
+
buddyLine: "Can we rest here? Just for a moment?",
|
|
90
|
+
healAmount: 20,
|
|
91
|
+
nextRoomId: "cc_treasure",
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
id: "cc_treasure", type: "treasure", title: "Crystal Cache",
|
|
95
|
+
text: ["Deep in the cave, crystals surround a gleaming prize!"],
|
|
96
|
+
buddyLine: "Jackpot!",
|
|
97
|
+
lootPool: ["iron_sword", "chain_mail", "potion", "fish"],
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
}),
|
|
101
|
+
makeAdventure({
|
|
102
|
+
id: "windy_peaks",
|
|
103
|
+
name: "The Windy Peaks",
|
|
104
|
+
description: "A treacherous mountain path with fierce creatures and rare gear.",
|
|
105
|
+
difficulty: "hard",
|
|
106
|
+
levelRequired: 6,
|
|
107
|
+
energyCost: 25,
|
|
108
|
+
hungerCost: 20,
|
|
109
|
+
completionXp: 75,
|
|
110
|
+
startRoomId: "wp_entry",
|
|
111
|
+
rooms: [
|
|
112
|
+
{
|
|
113
|
+
id: "wp_entry", type: "narrative", title: "Mountain Base",
|
|
114
|
+
text: ["The wind howls as you begin the ascent.", "A steep path goes straight up. A cave offers shelter to the side."],
|
|
115
|
+
buddyLine: "It's cold up here! But we're tough, right?",
|
|
116
|
+
choices: [
|
|
117
|
+
{ label: "Climb the steep path", nextRoomId: "wp_ridge" },
|
|
118
|
+
{ label: "Explore the mountain cave", nextRoomId: "wp_vent" },
|
|
119
|
+
],
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
id: "wp_ridge", type: "combat", title: "Windswept Ridge",
|
|
123
|
+
text: ["A storm wolf emerges from the blizzard, teeth bared!"],
|
|
124
|
+
buddyLine: "That's a BIG wolf!",
|
|
125
|
+
enemyId: "stormwolf",
|
|
126
|
+
nextRoomId: "wp_hermit",
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
id: "wp_vent", type: "combat", title: "Volcanic Vent",
|
|
130
|
+
text: ["The cave gets hotter. A glowing beetle blocks the way!"],
|
|
131
|
+
buddyLine: "Is it just me or is it really hot in here?!",
|
|
132
|
+
enemyId: "lavabeetle",
|
|
133
|
+
nextRoomId: "wp_hermit",
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
id: "wp_hermit", type: "event", title: "Mountain Hermit",
|
|
137
|
+
text: ["An old traveler sits by a small fire.", "\"Trade you something useful for a Berry?\""],
|
|
138
|
+
buddyLine: "Can we trust them?",
|
|
139
|
+
nextRoomId: "wp_treasure",
|
|
140
|
+
eventChoices: [
|
|
141
|
+
{ label: "Trade a Berry", outcome: "The hermit hands you a gleaming blade!", effect: { itemGain: "crystal_blade" } },
|
|
142
|
+
{ label: "Politely decline", outcome: "The hermit nods and waves you on." },
|
|
143
|
+
],
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
id: "wp_treasure", type: "treasure", title: "Summit Stash",
|
|
147
|
+
text: ["At the peak, tucked under a rock — treasure!"],
|
|
148
|
+
buddyLine: "We made it to the top! And there's LOOT!",
|
|
149
|
+
lootPool: ["crystal_blade", "crystal_armor", "elixir", "cake"],
|
|
150
|
+
},
|
|
151
|
+
],
|
|
152
|
+
}),
|
|
153
|
+
makeAdventure({
|
|
154
|
+
id: "shadow_marsh",
|
|
155
|
+
name: "The Shadow Marsh",
|
|
156
|
+
description: "A dark swamp with a terrible boss lurking in the depths.",
|
|
157
|
+
difficulty: "boss",
|
|
158
|
+
levelRequired: 10,
|
|
159
|
+
energyCost: 30,
|
|
160
|
+
hungerCost: 25,
|
|
161
|
+
completionXp: 100,
|
|
162
|
+
startRoomId: "sm_entry",
|
|
163
|
+
rooms: [
|
|
164
|
+
{
|
|
165
|
+
id: "sm_entry", type: "narrative", title: "Marsh Edge",
|
|
166
|
+
text: ["The ground turns soft and spongy. Fog rolls in.", "Something is watching from the murky water."],
|
|
167
|
+
buddyLine: "I don't like this place...",
|
|
168
|
+
choices: [
|
|
169
|
+
{ label: "Stick to the boardwalk", nextRoomId: "sm_fog" },
|
|
170
|
+
{ label: "Wade through the shallow muck", nextRoomId: "sm_murk" },
|
|
171
|
+
],
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
id: "sm_fog", type: "combat", title: "Foggy Path",
|
|
175
|
+
text: ["A shadow moves through the fog — it's a fox, but darker and meaner!"],
|
|
176
|
+
buddyLine: "That's no friendly fox!",
|
|
177
|
+
enemyId: "shadowfox",
|
|
178
|
+
nextRoomId: "sm_rest",
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
id: "sm_murk", type: "combat", title: "Murky Depths",
|
|
182
|
+
text: ["Something slithers underfoot. A giant snail rises from the muck!"],
|
|
183
|
+
buddyLine: "That snail is HUGE!",
|
|
184
|
+
enemyId: "snail",
|
|
185
|
+
nextRoomId: "sm_rest",
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
id: "sm_rest", type: "rest", title: "Dry Island",
|
|
189
|
+
text: ["A small dry patch in the swamp. Fireflies dance around it."],
|
|
190
|
+
buddyLine: "Let's catch our breath before the big fight...",
|
|
191
|
+
healAmount: 30,
|
|
192
|
+
nextRoomId: "sm_boss",
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
id: "sm_boss", type: "boss", title: "The Marsh King's Lair",
|
|
196
|
+
text: ["The water bubbles. The ground trembles.", "A massive figure rises from the swamp..."],
|
|
197
|
+
buddyLine: "Here it comes... STAY STRONG!",
|
|
198
|
+
enemyId: "marshking",
|
|
199
|
+
},
|
|
200
|
+
],
|
|
201
|
+
}),
|
|
202
|
+
];
|
|
203
|
+
function getAdventure(id) {
|
|
204
|
+
return exports.ADVENTURES.find((a) => a.id === id);
|
|
205
|
+
}
|
|
206
|
+
//# sourceMappingURL=adventures.js.map
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export interface Biome {
|
|
2
|
+
id: string;
|
|
3
|
+
name: string;
|
|
4
|
+
/** Enemy IDs available in this biome, by tier */
|
|
5
|
+
enemies: {
|
|
6
|
+
easy: string[];
|
|
7
|
+
medium: string[];
|
|
8
|
+
hard: string[];
|
|
9
|
+
boss: string[];
|
|
10
|
+
};
|
|
11
|
+
/** Loot pools by rarity tier */
|
|
12
|
+
loot: {
|
|
13
|
+
common: string[];
|
|
14
|
+
uncommon: string[];
|
|
15
|
+
rare: string[];
|
|
16
|
+
epic: string[];
|
|
17
|
+
};
|
|
18
|
+
/** Flavor text pools */
|
|
19
|
+
nouns: string[];
|
|
20
|
+
adjectives: string[];
|
|
21
|
+
landmarks: string[];
|
|
22
|
+
hidingSpots: string[];
|
|
23
|
+
directions: string[];
|
|
24
|
+
/** Buddy species comfort lines — species that feel at home here */
|
|
25
|
+
comfortSpecies: string[];
|
|
26
|
+
}
|
|
27
|
+
export declare const BIOMES: Record<string, Biome>;
|
|
28
|
+
export declare function getBiome(id: string): Biome | undefined;
|
|
29
|
+
export declare function getAllBiomeIds(): string[];
|
|
30
|
+
//# sourceMappingURL=biomes.d.ts.map
|