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.
Files changed (143) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +60 -0
  3. package/dist/adventure/adventureUI.d.ts +24 -0
  4. package/dist/adventure/adventureUI.js +290 -0
  5. package/dist/adventure/adventures.d.ts +4 -0
  6. package/dist/adventure/adventures.js +206 -0
  7. package/dist/adventure/biomes.d.ts +30 -0
  8. package/dist/adventure/biomes.js +80 -0
  9. package/dist/adventure/combat/combat.d.ts +14 -0
  10. package/dist/adventure/combat/combat.js +638 -0
  11. package/dist/adventure/combat/combatUI.d.ts +5 -0
  12. package/dist/adventure/combat/combatUI.js +116 -0
  13. package/dist/adventure/combat/conditions.d.ts +20 -0
  14. package/dist/adventure/combat/conditions.js +111 -0
  15. package/dist/adventure/combat/enemies.d.ts +4 -0
  16. package/dist/adventure/combat/enemies.js +430 -0
  17. package/dist/adventure/combat/gear.d.ts +3 -0
  18. package/dist/adventure/combat/gear.js +199 -0
  19. package/dist/adventure/combat/skills.d.ts +6 -0
  20. package/dist/adventure/combat/skills.js +197 -0
  21. package/dist/adventure/combat.d.ts +31 -0
  22. package/dist/adventure/combat.js +732 -0
  23. package/dist/adventure/combatUI.d.ts +5 -0
  24. package/dist/adventure/combatUI.js +116 -0
  25. package/dist/adventure/endless.d.ts +18 -0
  26. package/dist/adventure/endless.js +154 -0
  27. package/dist/adventure/enemies.d.ts +4 -0
  28. package/dist/adventure/enemies.js +320 -0
  29. package/dist/adventure/engine.d.ts +20 -0
  30. package/dist/adventure/engine.js +137 -0
  31. package/dist/adventure/gear.d.ts +3 -0
  32. package/dist/adventure/gear.js +149 -0
  33. package/dist/adventure/generation/biomes.d.ts +30 -0
  34. package/dist/adventure/generation/biomes.js +102 -0
  35. package/dist/adventure/generation/endless.d.ts +18 -0
  36. package/dist/adventure/generation/endless.js +154 -0
  37. package/dist/adventure/generation/generator.d.ts +9 -0
  38. package/dist/adventure/generation/generator.js +245 -0
  39. package/dist/adventure/generation/templates.d.ts +25 -0
  40. package/dist/adventure/generation/templates.js +228 -0
  41. package/dist/adventure/generator.d.ts +9 -0
  42. package/dist/adventure/generator.js +245 -0
  43. package/dist/adventure/skills.d.ts +6 -0
  44. package/dist/adventure/skills.js +197 -0
  45. package/dist/adventure/templates.d.ts +25 -0
  46. package/dist/adventure/templates.js +228 -0
  47. package/dist/adventure/types.d.ts +236 -0
  48. package/dist/adventure/types.js +97 -0
  49. package/dist/app/state.d.ts +49 -0
  50. package/dist/app/state.js +51 -0
  51. package/dist/buddy/activities.d.ts +16 -0
  52. package/dist/buddy/activities.js +90 -0
  53. package/dist/buddy/decay.d.ts +3 -0
  54. package/dist/buddy/decay.js +45 -0
  55. package/dist/buddy/leveling.d.ts +11 -0
  56. package/dist/buddy/leveling.js +25 -0
  57. package/dist/buddy/roll.d.ts +4 -0
  58. package/dist/buddy/roll.js +61 -0
  59. package/dist/buddy/species.d.ts +4 -0
  60. package/dist/buddy/species.js +592 -0
  61. package/dist/buddy/titles.d.ts +17 -0
  62. package/dist/buddy/titles.js +89 -0
  63. package/dist/buddy/types.d.ts +92 -0
  64. package/dist/buddy/types.js +21 -0
  65. package/dist/commands/actions.d.ts +2 -0
  66. package/dist/commands/actions.js +141 -0
  67. package/dist/commands/admin.d.ts +2 -0
  68. package/dist/commands/admin.js +202 -0
  69. package/dist/commands/registry.d.ts +25 -0
  70. package/dist/commands/registry.js +31 -0
  71. package/dist/commands/social.d.ts +2 -0
  72. package/dist/commands/social.js +92 -0
  73. package/dist/dialogue/engine.d.ts +7 -0
  74. package/dist/dialogue/engine.js +68 -0
  75. package/dist/dialogue/lines.d.ts +26 -0
  76. package/dist/dialogue/lines.js +294 -0
  77. package/dist/events/engine.d.ts +20 -0
  78. package/dist/events/engine.js +51 -0
  79. package/dist/events/events.d.ts +13 -0
  80. package/dist/events/events.js +149 -0
  81. package/dist/index.d.ts +10 -0
  82. package/dist/index.js +1665 -0
  83. package/dist/inventory/finding.d.ts +11 -0
  84. package/dist/inventory/finding.js +99 -0
  85. package/dist/inventory/items.d.ts +31 -0
  86. package/dist/inventory/items.js +63 -0
  87. package/dist/minigames/copycat.d.ts +2 -0
  88. package/dist/minigames/copycat.js +153 -0
  89. package/dist/minigames/fetch.d.ts +2 -0
  90. package/dist/minigames/fetch.js +179 -0
  91. package/dist/minigames/moodmatch.d.ts +2 -0
  92. package/dist/minigames/moodmatch.js +144 -0
  93. package/dist/minigames/quickpaws.d.ts +2 -0
  94. package/dist/minigames/quickpaws.js +142 -0
  95. package/dist/minigames/registry.d.ts +5 -0
  96. package/dist/minigames/registry.js +16 -0
  97. package/dist/minigames/rpsplus.d.ts +2 -0
  98. package/dist/minigames/rpsplus.js +168 -0
  99. package/dist/minigames/treasurehunt.d.ts +2 -0
  100. package/dist/minigames/treasurehunt.js +146 -0
  101. package/dist/minigames/types.d.ts +30 -0
  102. package/dist/minigames/types.js +69 -0
  103. package/dist/rendering/commandPalette.d.ts +16 -0
  104. package/dist/rendering/commandPalette.js +77 -0
  105. package/dist/rendering/display.d.ts +9 -0
  106. package/dist/rendering/display.js +231 -0
  107. package/dist/rendering/inventoryUI.d.ts +14 -0
  108. package/dist/rendering/inventoryUI.js +99 -0
  109. package/dist/rendering/items.d.ts +7 -0
  110. package/dist/rendering/items.js +34 -0
  111. package/dist/rendering/listUtils.d.ts +3 -0
  112. package/dist/rendering/listUtils.js +24 -0
  113. package/dist/rendering/minigameUI.d.ts +11 -0
  114. package/dist/rendering/minigameUI.js +37 -0
  115. package/dist/rendering/overlayUI.d.ts +24 -0
  116. package/dist/rendering/overlayUI.js +184 -0
  117. package/dist/rendering/scene.d.ts +8 -0
  118. package/dist/rendering/scene.js +87 -0
  119. package/dist/rendering/screen.d.ts +43 -0
  120. package/dist/rendering/screen.js +97 -0
  121. package/dist/sound/sound.d.ts +11 -0
  122. package/dist/sound/sound.js +55 -0
  123. package/dist/state/save.d.ts +5 -0
  124. package/dist/state/save.js +100 -0
  125. package/dist/state/settings.d.ts +7 -0
  126. package/dist/state/settings.js +81 -0
  127. package/dist/story/mainStory.d.ts +4 -0
  128. package/dist/story/mainStory.js +3111 -0
  129. package/dist/story/npcs.d.ts +17 -0
  130. package/dist/story/npcs.js +137 -0
  131. package/dist/story/progress.d.ts +26 -0
  132. package/dist/story/progress.js +168 -0
  133. package/dist/story/seasonal.d.ts +6 -0
  134. package/dist/story/seasonal.js +96 -0
  135. package/dist/story/shop.d.ts +7 -0
  136. package/dist/story/shop.js +26 -0
  137. package/dist/story/sideQuests.d.ts +4 -0
  138. package/dist/story/sideQuests.js +151 -0
  139. package/dist/story/types.d.ts +61 -0
  140. package/dist/story/types.js +36 -0
  141. package/dist/updates.d.ts +23 -0
  142. package/dist/updates.js +142 -0
  143. package/package.json +53 -0
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createInactiveGame = createInactiveGame;
4
+ exports.getXpReward = getXpReward;
5
+ exports.getReaction = getReaction;
6
+ function createInactiveGame() {
7
+ return {
8
+ active: false,
9
+ gameId: "",
10
+ phase: "intro",
11
+ score: 0,
12
+ round: 0,
13
+ maxRounds: 0,
14
+ buddyReaction: "",
15
+ data: {},
16
+ ticksInPhase: 0,
17
+ };
18
+ }
19
+ /** Get XP reward based on score percentage */
20
+ function getXpReward(score, maxScore, baseXp) {
21
+ if (maxScore === 0)
22
+ return 0;
23
+ const pct = score / maxScore;
24
+ if (pct >= 0.75)
25
+ return Math.ceil(baseXp * 1.5);
26
+ if (pct >= 0.5)
27
+ return baseXp;
28
+ if (pct >= 0.25)
29
+ return Math.ceil(baseXp * 0.5);
30
+ return Math.ceil(baseXp * 0.25);
31
+ }
32
+ /** Pick a buddy reaction based on score percentage */
33
+ function getReaction(score, maxScore) {
34
+ if (maxScore === 0)
35
+ return "That was fun!";
36
+ const pct = score / maxScore;
37
+ if (pct >= 0.75)
38
+ return pickRandom(AMAZING_REACTIONS);
39
+ if (pct >= 0.5)
40
+ return pickRandom(GOOD_REACTIONS);
41
+ if (pct >= 0.25)
42
+ return pickRandom(OK_REACTIONS);
43
+ return pickRandom(BAD_REACTIONS);
44
+ }
45
+ function pickRandom(arr) {
46
+ return arr[Math.floor(Math.random() * arr.length)];
47
+ }
48
+ const AMAZING_REACTIONS = [
49
+ "INCREDIBLE! You're a natural!",
50
+ "Best game EVER!",
51
+ "Wow wow wow! So good!",
52
+ "Can we play again?! That was amazing!",
53
+ ];
54
+ const GOOD_REACTIONS = [
55
+ "Nice job! That was fun!",
56
+ "You're pretty good at this!",
57
+ "I had so much fun!",
58
+ ];
59
+ const OK_REACTIONS = [
60
+ "Not bad! We're getting there!",
61
+ "Good try! Practice makes perfect!",
62
+ "That was okay! Let's try again sometime!",
63
+ ];
64
+ const BAD_REACTIONS = [
65
+ "Better luck next time!",
66
+ "We should practice more...",
67
+ "It's okay, I still had fun!",
68
+ ];
69
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1,16 @@
1
+ export interface PaletteState {
2
+ active: boolean;
3
+ filter: string;
4
+ selectedIndex: number;
5
+ filteredCommands: {
6
+ name: string;
7
+ description: string;
8
+ }[];
9
+ }
10
+ export declare function createPalette(): PaletteState;
11
+ export declare function openPalette(palette: PaletteState): PaletteState;
12
+ export declare function updateFilter(palette: PaletteState, filter: string): PaletteState;
13
+ export declare function moveSelection(palette: PaletteState, delta: number): PaletteState;
14
+ export declare function getSelectedCommand(palette: PaletteState): string | null;
15
+ export declare function renderPalette(palette: PaletteState): string[];
16
+ //# sourceMappingURL=commandPalette.d.ts.map
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createPalette = createPalette;
4
+ exports.openPalette = openPalette;
5
+ exports.updateFilter = updateFilter;
6
+ exports.moveSelection = moveSelection;
7
+ exports.getSelectedCommand = getSelectedCommand;
8
+ exports.renderPalette = renderPalette;
9
+ const registry_1 = require("../commands/registry");
10
+ const screen_1 = require("./screen");
11
+ function createPalette() {
12
+ return {
13
+ active: false,
14
+ filter: "",
15
+ selectedIndex: 0,
16
+ filteredCommands: [],
17
+ };
18
+ }
19
+ function openPalette(palette) {
20
+ const cmds = (0, registry_1.getAllCommands)().map((c) => ({ name: c.name, description: c.description }));
21
+ return {
22
+ active: true,
23
+ filter: "",
24
+ selectedIndex: 0,
25
+ filteredCommands: cmds,
26
+ };
27
+ }
28
+ function updateFilter(palette, filter) {
29
+ const all = (0, registry_1.getAllCommands)().map((c) => ({ name: c.name, description: c.description }));
30
+ const filtered = filter
31
+ ? all.filter((c) => c.name.toLowerCase().startsWith(filter.toLowerCase()))
32
+ : all;
33
+ return {
34
+ ...palette,
35
+ filter,
36
+ filteredCommands: filtered,
37
+ selectedIndex: Math.min(palette.selectedIndex, Math.max(0, filtered.length - 1)),
38
+ };
39
+ }
40
+ function moveSelection(palette, delta) {
41
+ const max = palette.filteredCommands.length - 1;
42
+ if (max < 0)
43
+ return palette;
44
+ let next = palette.selectedIndex + delta;
45
+ if (next < 0)
46
+ next = max;
47
+ if (next > max)
48
+ next = 0;
49
+ return { ...palette, selectedIndex: next };
50
+ }
51
+ function getSelectedCommand(palette) {
52
+ if (palette.filteredCommands.length === 0)
53
+ return null;
54
+ return palette.filteredCommands[palette.selectedIndex].name;
55
+ }
56
+ function renderPalette(palette) {
57
+ const lines = [];
58
+ lines.push(` ${screen_1.ansi.dim}${"─".repeat(40)}${screen_1.ansi.reset}`);
59
+ if (palette.filteredCommands.length === 0) {
60
+ lines.push(` ${screen_1.ansi.dim} No matching commands${screen_1.ansi.reset}`);
61
+ }
62
+ else {
63
+ for (let i = 0; i < palette.filteredCommands.length; i++) {
64
+ const cmd = palette.filteredCommands[i];
65
+ const selected = i === palette.selectedIndex;
66
+ if (selected) {
67
+ lines.push(` ${screen_1.ansi.colors.cyan}▸ /${cmd.name.padEnd(12)}${screen_1.ansi.reset} ${cmd.description}`);
68
+ }
69
+ else {
70
+ lines.push(` ${screen_1.ansi.dim} /${cmd.name.padEnd(12)}${screen_1.ansi.reset} ${screen_1.ansi.dim}${cmd.description}${screen_1.ansi.reset}`);
71
+ }
72
+ }
73
+ }
74
+ lines.push(` ${screen_1.ansi.dim}${"─".repeat(40)}${screen_1.ansi.reset}`);
75
+ return lines;
76
+ }
77
+ //# sourceMappingURL=commandPalette.js.map
@@ -0,0 +1,9 @@
1
+ import { BuddyState } from "../buddy/types";
2
+ /**
3
+ * Renders the full main screen content (buddy + stats + mood).
4
+ * Called on every frame of the animation loop.
5
+ */
6
+ export declare function renderMainScreen(state: BuddyState, frame: number, message?: string, dialogue?: string, dialogueSource?: string): string;
7
+ export declare function renderRollScreen(phase: "waiting" | "rolling" | "reveal", speciesName?: string, speciesArt?: string, rarityColor?: string, rarityLabel?: string): string;
8
+ export declare function renderNamePrompt(speciesName: string): string;
9
+ //# sourceMappingURL=display.d.ts.map
@@ -0,0 +1,231 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.renderMainScreen = renderMainScreen;
4
+ exports.renderRollScreen = renderRollScreen;
5
+ exports.renderNamePrompt = renderNamePrompt;
6
+ const types_1 = require("../buddy/types");
7
+ const species_1 = require("../buddy/species");
8
+ const activities_1 = require("../buddy/activities");
9
+ const titles_1 = require("../buddy/titles");
10
+ const items_1 = require("./items");
11
+ const scene_1 = require("./scene");
12
+ const screen_1 = require("./screen");
13
+ function statBar(value, label, width = 20) {
14
+ const filled = Math.round((value / 100) * width);
15
+ const empty = width - filled;
16
+ let color;
17
+ if (value > 60)
18
+ color = screen_1.ansi.colors.green;
19
+ else if (value > 30)
20
+ color = screen_1.ansi.colors.yellow;
21
+ else
22
+ color = screen_1.ansi.colors.red;
23
+ const bar = color + "█".repeat(filled) + screen_1.ansi.dim + "░".repeat(empty) + screen_1.ansi.reset;
24
+ return ` ${label.padEnd(12)} ${bar} ${color}${Math.round(value).toString().padStart(3)}%${screen_1.ansi.reset}`;
25
+ }
26
+ /**
27
+ * Renders the full main screen content (buddy + stats + mood).
28
+ * Called on every frame of the animation loop.
29
+ */
30
+ function renderMainScreen(state, frame, message, dialogue, dialogueSource) {
31
+ const species = (0, species_1.getSpecies)(state.speciesId);
32
+ if (!species)
33
+ return "Unknown buddy species!";
34
+ const color = types_1.RARITY_COLORS[species.rarity];
35
+ const label = types_1.RARITY_LABELS[species.rarity];
36
+ const lines = [];
37
+ // Header bar
38
+ lines.push("");
39
+ lines.push(` ${screen_1.ansi.bold}CLI Buddy${screen_1.ansi.reset} ${screen_1.ansi.dim}v0.1${screen_1.ansi.reset}`);
40
+ lines.push(` ${screen_1.ansi.dim}${"─".repeat(40)}${screen_1.ansi.reset}`);
41
+ if (!state.alive) {
42
+ lines.push("");
43
+ lines.push(` ${screen_1.ansi.bold}${screen_1.ansi.colors.red}${state.name} has passed away...${screen_1.ansi.reset}`);
44
+ lines.push(` ${screen_1.ansi.dim}They lived for ${formatAge(state.age)}.${screen_1.ansi.reset}`);
45
+ lines.push("");
46
+ lines.push(` ${screen_1.ansi.dim}Use /revive to bring them back.${screen_1.ansi.reset}`);
47
+ lines.push("");
48
+ return lines.join("\n");
49
+ }
50
+ // Buddy info line
51
+ lines.push("");
52
+ const displayName = (0, titles_1.formatNameWithTitle)(state, species.name);
53
+ lines.push(` ${screen_1.ansi.bold}${color}${displayName}${screen_1.ansi.reset} ${screen_1.ansi.dim}${label}${screen_1.ansi.reset}`);
54
+ lines.push(` ${screen_1.ansi.dim}Level ${state.level} • XP: ${state.xp}/${state.level * 50} • Age: ${formatAge(state.age)}${screen_1.ansi.reset}`);
55
+ lines.push("");
56
+ // Select animation set: activity > sick > hungry/tired/sad > idle
57
+ let animKey = "idle";
58
+ const activityType = state.activity?.type ?? "idle";
59
+ if (activityType !== "idle") {
60
+ animKey = activityType;
61
+ }
62
+ else if (state.stats.health < 20) {
63
+ animKey = "sick";
64
+ }
65
+ else if (state.stats.hunger < 30 && species.animations.hungry) {
66
+ animKey = "hungry";
67
+ }
68
+ else if (state.stats.energy < 30 && species.animations.tired) {
69
+ animKey = "tired";
70
+ }
71
+ else if (state.stats.happiness < 30 && species.animations.sad) {
72
+ animKey = "sad";
73
+ }
74
+ const frames = species.animations[animKey] ?? species.animations.idle;
75
+ const frameIndex = frame % frames.length;
76
+ const buddyArtLines = frames[frameIndex].split("\n");
77
+ // Compose scene with item
78
+ const item = items_1.ACTIVITY_ITEMS[activityType] ?? null;
79
+ const sceneLines = (0, scene_1.composeScene)(buddyArtLines, item, activityType === "idle");
80
+ for (const line of sceneLines) {
81
+ lines.push(` ${color}${line}${screen_1.ansi.reset}`);
82
+ }
83
+ // Activity timer
84
+ if (state.activity && state.activity.type !== "idle") {
85
+ const timerLabel = state.activity.type === "sleeping" ? "Sleeping" :
86
+ state.activity.type === "eating" ? "Eating" : "Playing";
87
+ const isRecovery = state.activity.unskippable;
88
+ const label2 = isRecovery ? "Recovering" : timerLabel;
89
+ const skipHint = isRecovery ? "" : " (press any key to skip)";
90
+ lines.push(` ${screen_1.ansi.dim} ${label2}... ${(0, activities_1.formatTimeRemaining)(state.activity)} remaining${skipHint}${screen_1.ansi.reset}`);
91
+ }
92
+ lines.push("");
93
+ // Stats — always visible, update in real time
94
+ lines.push(statBar(state.stats.hunger, "Hunger"));
95
+ lines.push(statBar(state.stats.happiness, "Happiness"));
96
+ lines.push(statBar(state.stats.health, "Health"));
97
+ lines.push(statBar(state.stats.energy, "Energy"));
98
+ lines.push("");
99
+ // Mood message
100
+ // Dialogue overrides mood when present
101
+ if (dialogue) {
102
+ const sourceTag = dialogueSource ? `${screen_1.ansi.dim}[${dialogueSource}]${screen_1.ansi.reset} ` : "";
103
+ lines.push(` ${sourceTag}${screen_1.ansi.colors.cyan}"${dialogue}"${screen_1.ansi.reset}`);
104
+ }
105
+ else {
106
+ lines.push(` ${screen_1.ansi.dim}${getMoodMessage(state)}${screen_1.ansi.reset}`);
107
+ }
108
+ // Action feedback message (fades after a few seconds)
109
+ if (message) {
110
+ lines.push("");
111
+ lines.push(` ${screen_1.ansi.colors.cyan}${message}${screen_1.ansi.reset}`);
112
+ }
113
+ lines.push("");
114
+ // Notification badge + help hint
115
+ const notifCount = state.notifications.length;
116
+ if (notifCount > 0) {
117
+ lines.push(` ${screen_1.ansi.colors.yellow}[${notifCount} notification${notifCount > 1 ? "s" : ""}]${screen_1.ansi.reset} ${screen_1.ansi.dim}Type / for commands${screen_1.ansi.reset}`);
118
+ }
119
+ else {
120
+ lines.push(` ${screen_1.ansi.dim}Type / for commands${screen_1.ansi.reset}`);
121
+ }
122
+ return lines.join("\n");
123
+ }
124
+ function renderRollScreen(phase, speciesName, speciesArt, rarityColor, rarityLabel) {
125
+ const lines = [];
126
+ lines.push("");
127
+ lines.push(` ${screen_1.ansi.bold}CLI Buddy${screen_1.ansi.reset} ${screen_1.ansi.dim}v0.1${screen_1.ansi.reset}`);
128
+ lines.push(` ${screen_1.ansi.dim}${"─".repeat(40)}${screen_1.ansi.reset}`);
129
+ lines.push("");
130
+ if (phase === "waiting") {
131
+ lines.push(` ${screen_1.ansi.dim}Welcome! You don't have a buddy yet.${screen_1.ansi.reset}`);
132
+ lines.push("");
133
+ lines.push(` ${screen_1.ansi.bold}Press Enter to roll your first buddy!${screen_1.ansi.reset}`);
134
+ }
135
+ else if (phase === "rolling" && speciesName && speciesArt) {
136
+ // Cycling silhouettes during roll
137
+ lines.push(` ${screen_1.ansi.dim}Rolling...${screen_1.ansi.reset}`);
138
+ lines.push("");
139
+ lines.push(` ╔══════════════════════════════╗`);
140
+ lines.push(` ║ ║`);
141
+ lines.push(` ║ ${screen_1.ansi.dim}${speciesName.padEnd(26)}${screen_1.ansi.reset} ║`);
142
+ lines.push(` ║ ║`);
143
+ lines.push(` ╚══════════════════════════════╝`);
144
+ lines.push("");
145
+ const artLines = speciesArt.split("\n");
146
+ for (const line of artLines) {
147
+ lines.push(` ${screen_1.ansi.dim}${line}${screen_1.ansi.reset}`);
148
+ }
149
+ }
150
+ else if (phase === "rolling") {
151
+ // Fallback if no species passed
152
+ lines.push(` Rolling${".".repeat((Date.now() / 300 | 0) % 4)}`);
153
+ lines.push("");
154
+ lines.push(` ${screen_1.ansi.dim}✦ ✧ ✦ ✧ ✦${screen_1.ansi.reset}`);
155
+ }
156
+ else if (phase === "reveal" && speciesName && speciesArt && rarityColor && rarityLabel) {
157
+ // Rarity-based border effects
158
+ const isEpic = rarityLabel.includes("Epic");
159
+ const isLegendary = rarityLabel.includes("Legendary");
160
+ const border = isLegendary ? `${rarityColor}✦═` : isEpic ? `${rarityColor}✦═` : "══";
161
+ const corner = isLegendary || isEpic ? `${rarityColor}✦` : "╔";
162
+ lines.push(` ${rarityColor}╔══════════════════════════════╗${screen_1.ansi.reset}`);
163
+ lines.push(` ${rarityColor}║${screen_1.ansi.reset} ${rarityColor}║${screen_1.ansi.reset}`);
164
+ lines.push(` ${rarityColor}║${screen_1.ansi.reset} You got a... ${rarityColor}║${screen_1.ansi.reset}`);
165
+ lines.push(` ${rarityColor}║${screen_1.ansi.reset} ${rarityColor}║${screen_1.ansi.reset}`);
166
+ lines.push(` ${rarityColor}║${screen_1.ansi.reset} ${rarityColor}${screen_1.ansi.bold}${speciesName.padEnd(26)}${screen_1.ansi.reset} ${rarityColor}║${screen_1.ansi.reset}`);
167
+ lines.push(` ${rarityColor}║${screen_1.ansi.reset} ${rarityColor}${rarityLabel.padEnd(26)}${screen_1.ansi.reset} ${rarityColor}║${screen_1.ansi.reset}`);
168
+ lines.push(` ${rarityColor}║${screen_1.ansi.reset} ${rarityColor}║${screen_1.ansi.reset}`);
169
+ lines.push(` ${rarityColor}╚══════════════════════════════╝${screen_1.ansi.reset}`);
170
+ if (isLegendary) {
171
+ lines.push(` ${rarityColor} ✦ ✦ ✦ LEGENDARY ✦ ✦ ✦${screen_1.ansi.reset}`);
172
+ }
173
+ else if (isEpic) {
174
+ lines.push(` ${rarityColor} ✦ ✦ EPIC ✦ ✦${screen_1.ansi.reset}`);
175
+ }
176
+ lines.push("");
177
+ const artLines = speciesArt.split("\n");
178
+ for (const line of artLines) {
179
+ lines.push(` ${rarityColor}${line}${screen_1.ansi.reset}`);
180
+ }
181
+ }
182
+ lines.push("");
183
+ return lines.join("\n");
184
+ }
185
+ function renderNamePrompt(speciesName) {
186
+ const lines = [];
187
+ lines.push("");
188
+ lines.push(` What would you like to name your ${speciesName}?`);
189
+ lines.push("");
190
+ return lines.join("\n");
191
+ }
192
+ function formatAge(hours) {
193
+ if (hours < 1)
194
+ return `${Math.round(hours * 60)}m`;
195
+ if (hours < 24)
196
+ return `${Math.round(hours)}h`;
197
+ const days = Math.floor(hours / 24);
198
+ const remaining = Math.round(hours % 24);
199
+ return `${days}d ${remaining}h`;
200
+ }
201
+ function getMoodMessage(state) {
202
+ // Activity-specific mood
203
+ if (state.activity) {
204
+ switch (state.activity.type) {
205
+ case "sleeping": return state.activity?.unskippable ? `"Ow... everything hurts... need rest..."` : `*Zzz... sweet dreams...*`;
206
+ case "eating": return `*Nom nom nom...*`;
207
+ case "playing": return `*Wheee! So fun!*`;
208
+ }
209
+ }
210
+ const { hunger, happiness, health, energy } = state.stats;
211
+ if (health < 20)
212
+ return `"I feel very sick... Please help me..."`;
213
+ if (hunger < 15)
214
+ return `"I'm starving! Feed me, please!"`;
215
+ if (happiness < 15)
216
+ return `"I'm so lonely... Play with me?"`;
217
+ if (energy < 15)
218
+ return `"I can barely keep my eyes open..."`;
219
+ if (hunger < 40)
220
+ return `"My tummy is rumbling..."`;
221
+ if (happiness < 40)
222
+ return `"I'm a bit bored..."`;
223
+ if (energy < 40)
224
+ return `*yawns sleepily*`;
225
+ if (hunger > 80 && happiness > 80)
226
+ return `"Life is good! I love you!"`;
227
+ if (happiness > 70)
228
+ return `*wags happily*`;
229
+ return `*looks content*`;
230
+ }
231
+ //# sourceMappingURL=display.js.map
@@ -0,0 +1,14 @@
1
+ import { InventorySlot } from "../buddy/types";
2
+ export interface InventoryUIState {
3
+ active: boolean;
4
+ tab: "consumables" | "gear";
5
+ selectedIndex: number;
6
+ slots: InventorySlot[];
7
+ }
8
+ export declare function createInventoryUI(): InventoryUIState;
9
+ export declare function openInventoryUI(slots: InventorySlot[]): InventoryUIState;
10
+ export declare function switchInventoryTab(ui: InventoryUIState): InventoryUIState;
11
+ export declare function moveInventorySelection(ui: InventoryUIState, delta: number): InventoryUIState;
12
+ export declare function getSelectedSlot(ui: InventoryUIState): InventorySlot | null;
13
+ export declare function renderInventoryUI(ui: InventoryUIState, equippedWeapon?: string, equippedArmor?: string, equippedHelmet?: string, equippedBoots?: string, equippedAccessory?: string, gold?: number): string[];
14
+ //# sourceMappingURL=inventoryUI.d.ts.map
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createInventoryUI = createInventoryUI;
4
+ exports.openInventoryUI = openInventoryUI;
5
+ exports.switchInventoryTab = switchInventoryTab;
6
+ exports.moveInventorySelection = moveInventorySelection;
7
+ exports.getSelectedSlot = getSelectedSlot;
8
+ exports.renderInventoryUI = renderInventoryUI;
9
+ const items_1 = require("../inventory/items");
10
+ const listUtils_1 = require("./listUtils");
11
+ const screen_1 = require("./screen");
12
+ function createInventoryUI() {
13
+ return { active: false, tab: "consumables", selectedIndex: 0, slots: [] };
14
+ }
15
+ function openInventoryUI(slots) {
16
+ return { active: true, tab: "consumables", selectedIndex: 0, slots };
17
+ }
18
+ function switchInventoryTab(ui) {
19
+ return { ...ui, tab: ui.tab === "consumables" ? "gear" : "consumables", selectedIndex: 0 };
20
+ }
21
+ function getFilteredSlots(slots, tab) {
22
+ return slots.filter((s) => {
23
+ const item = (0, items_1.getItem)(s.itemId);
24
+ if (!item)
25
+ return false;
26
+ return tab === "gear" ? (0, items_1.isGear)(item) : !(0, items_1.isGear)(item);
27
+ });
28
+ }
29
+ function moveInventorySelection(ui, delta) {
30
+ const filtered = getFilteredSlots(ui.slots, ui.tab);
31
+ return { ...ui, selectedIndex: (0, listUtils_1.moveListIndex)(ui.selectedIndex, delta, filtered.length) };
32
+ }
33
+ function getSelectedSlot(ui) {
34
+ const filtered = getFilteredSlots(ui.slots, ui.tab);
35
+ if (filtered.length === 0)
36
+ return null;
37
+ return filtered[ui.selectedIndex] ?? null;
38
+ }
39
+ function renderInventoryUI(ui, equippedWeapon, equippedArmor, equippedHelmet, equippedBoots, equippedAccessory, gold) {
40
+ const lines = [];
41
+ lines.push(` ${screen_1.ansi.dim}${"─".repeat(48)}${screen_1.ansi.reset}`);
42
+ // Tab bar + gold
43
+ const conTab = ui.tab === "consumables" ? `${screen_1.ansi.bold}${screen_1.ansi.colors.cyan}[Consumables]${screen_1.ansi.reset}` : `${screen_1.ansi.dim}[Consumables]${screen_1.ansi.reset}`;
44
+ const gearTab = ui.tab === "gear" ? `${screen_1.ansi.bold}${screen_1.ansi.colors.cyan}[Gear]${screen_1.ansi.reset}` : `${screen_1.ansi.dim}[Gear]${screen_1.ansi.reset}`;
45
+ const goldDisplay = gold !== undefined ? ` ${screen_1.ansi.colors.yellow}${gold}g${screen_1.ansi.reset}` : "";
46
+ lines.push(` ${conTab} ${gearTab}${goldDisplay} ${screen_1.ansi.dim}Tab${screen_1.ansi.reset}`);
47
+ lines.push(` ${screen_1.ansi.dim}${"─".repeat(48)}${screen_1.ansi.reset}`);
48
+ const filtered = getFilteredSlots(ui.slots, ui.tab);
49
+ if (filtered.length === 0) {
50
+ if (ui.tab === "consumables") {
51
+ lines.push(` ${screen_1.ansi.dim} No consumables. Your buddy will find some!${screen_1.ansi.reset}`);
52
+ }
53
+ else {
54
+ lines.push(` ${screen_1.ansi.dim} No gear yet. Find some on adventures!${screen_1.ansi.reset}`);
55
+ }
56
+ }
57
+ else {
58
+ for (let i = 0; i < filtered.length; i++) {
59
+ const slot = filtered[i];
60
+ const item = (0, items_1.getItem)(slot.itemId);
61
+ if (!item)
62
+ continue;
63
+ const selected = i === ui.selectedIndex;
64
+ const rarityColor = items_1.ITEM_RARITY_COLORS[item.rarity];
65
+ const pointer = selected ? `${screen_1.ansi.colors.cyan}▸${screen_1.ansi.reset}` : " ";
66
+ if (ui.tab === "gear") {
67
+ // Gear: show rarity color, type, stats, equipped tag
68
+ const isEquipped = item.id === equippedWeapon || item.id === equippedArmor || item.id === equippedHelmet || item.id === equippedBoots || item.id === equippedAccessory;
69
+ const equipTag = isEquipped ? ` ${screen_1.ansi.colors.green}[Equipped]${screen_1.ansi.reset}` : "";
70
+ const typeTag = item.type === "weapon" ? "⚔" : item.type === "armor" ? "🛡" : item.type === "helmet" ? "🪖" : item.type === "boots" ? "👢" : "💍";
71
+ if (selected) {
72
+ lines.push(` ${pointer} ${rarityColor}${typeTag} ${screen_1.ansi.bold}${item.name}${screen_1.ansi.reset}${equipTag}`);
73
+ lines.push(` ${screen_1.ansi.dim}${item.description} [${item.rarity}]${screen_1.ansi.reset}`);
74
+ }
75
+ else {
76
+ lines.push(` ${pointer} ${rarityColor}${typeTag} ${item.name}${screen_1.ansi.reset}${equipTag}`);
77
+ }
78
+ }
79
+ else {
80
+ // Consumables: show count, description
81
+ if (selected) {
82
+ lines.push(` ${pointer} ${rarityColor}${screen_1.ansi.bold}${item.name}${screen_1.ansi.reset} x${slot.count} ${screen_1.ansi.dim}${item.description}${screen_1.ansi.reset}`);
83
+ }
84
+ else {
85
+ lines.push(` ${pointer} ${screen_1.ansi.dim}${item.name} x${slot.count}${screen_1.ansi.reset}`);
86
+ }
87
+ }
88
+ }
89
+ }
90
+ lines.push(` ${screen_1.ansi.dim}${"─".repeat(48)}${screen_1.ansi.reset}`);
91
+ if (ui.tab === "gear") {
92
+ lines.push(` ${screen_1.ansi.dim}Enter to equip, Esc to close${screen_1.ansi.reset}`);
93
+ }
94
+ else {
95
+ lines.push(` ${screen_1.ansi.dim}Enter to use, Esc to close${screen_1.ansi.reset}`);
96
+ }
97
+ return lines;
98
+ }
99
+ //# sourceMappingURL=inventoryUI.js.map
@@ -0,0 +1,7 @@
1
+ import { ActivityType } from "../buddy/types";
2
+ export interface SceneItem {
3
+ art: string[];
4
+ position: "left" | "right" | "below";
5
+ }
6
+ export declare const ACTIVITY_ITEMS: Record<ActivityType, SceneItem | null>;
7
+ //# sourceMappingURL=items.d.ts.map
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ACTIVITY_ITEMS = void 0;
4
+ const BED = {
5
+ position: "below",
6
+ art: [
7
+ " ┌──────────────┐",
8
+ " │ ░░░░░░░░░░░░ │",
9
+ " └──┤ ├──┘",
10
+ ],
11
+ };
12
+ const PLATE = {
13
+ position: "right",
14
+ art: [
15
+ " ",
16
+ " \\____/",
17
+ " (____)",
18
+ ],
19
+ };
20
+ const TOY = {
21
+ position: "right",
22
+ art: [
23
+ " ",
24
+ " o ",
25
+ " ",
26
+ ],
27
+ };
28
+ exports.ACTIVITY_ITEMS = {
29
+ idle: null,
30
+ sleeping: BED,
31
+ eating: PLATE,
32
+ playing: TOY,
33
+ };
34
+ //# sourceMappingURL=items.js.map
@@ -0,0 +1,3 @@
1
+ /** Move a selection index with wraparound */
2
+ export declare function moveListIndex(current: number, delta: number, maxItems: number, wrap?: boolean): number;
3
+ //# sourceMappingURL=listUtils.d.ts.map
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.moveListIndex = moveListIndex;
4
+ /** Move a selection index with wraparound */
5
+ function moveListIndex(current, delta, maxItems, wrap = true) {
6
+ if (maxItems <= 0)
7
+ return 0;
8
+ const max = maxItems - 1;
9
+ let next = current + delta;
10
+ if (wrap) {
11
+ if (next < 0)
12
+ next = max;
13
+ if (next > max)
14
+ next = 0;
15
+ }
16
+ else {
17
+ if (next < 0)
18
+ next = 0;
19
+ if (next > max)
20
+ next = max;
21
+ }
22
+ return next;
23
+ }
24
+ //# sourceMappingURL=listUtils.js.map
@@ -0,0 +1,11 @@
1
+ import { MiniGameDef } from "../minigames/types";
2
+ export interface MinigameMenuState {
3
+ active: boolean;
4
+ selectedIndex: number;
5
+ games: MiniGameDef[];
6
+ }
7
+ export declare function createMinigameMenu(): MinigameMenuState;
8
+ export declare function openMinigameMenu(games: MiniGameDef[]): MinigameMenuState;
9
+ export declare function moveMinigameSelection(menu: MinigameMenuState, delta: number): MinigameMenuState;
10
+ export declare function renderMinigameMenu(menu: MinigameMenuState): string[];
11
+ //# sourceMappingURL=minigameUI.d.ts.map
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createMinigameMenu = createMinigameMenu;
4
+ exports.openMinigameMenu = openMinigameMenu;
5
+ exports.moveMinigameSelection = moveMinigameSelection;
6
+ exports.renderMinigameMenu = renderMinigameMenu;
7
+ const listUtils_1 = require("./listUtils");
8
+ const screen_1 = require("./screen");
9
+ function createMinigameMenu() {
10
+ return { active: false, selectedIndex: 0, games: [] };
11
+ }
12
+ function openMinigameMenu(games) {
13
+ return { active: true, selectedIndex: 0, games };
14
+ }
15
+ function moveMinigameSelection(menu, delta) {
16
+ return { ...menu, selectedIndex: (0, listUtils_1.moveListIndex)(menu.selectedIndex, delta, menu.games.length) };
17
+ }
18
+ function renderMinigameMenu(menu) {
19
+ const lines = [];
20
+ lines.push(` ${screen_1.ansi.dim}${"─".repeat(44)}${screen_1.ansi.reset}`);
21
+ lines.push(` ${screen_1.ansi.bold}Mini-Games${screen_1.ansi.reset} ${screen_1.ansi.dim}Enter to play, Esc to close${screen_1.ansi.reset}`);
22
+ lines.push(` ${screen_1.ansi.dim}${"─".repeat(44)}${screen_1.ansi.reset}`);
23
+ for (let i = 0; i < menu.games.length; i++) {
24
+ const game = menu.games[i];
25
+ const selected = i === menu.selectedIndex;
26
+ if (selected) {
27
+ lines.push(` ${screen_1.ansi.colors.cyan}▸ ${game.icon} ${screen_1.ansi.bold}${game.name}${screen_1.ansi.reset}`);
28
+ lines.push(` ${screen_1.ansi.dim}${game.description}${screen_1.ansi.reset}`);
29
+ }
30
+ else {
31
+ lines.push(` ${screen_1.ansi.dim} ${game.icon} ${game.name}${screen_1.ansi.reset}`);
32
+ }
33
+ }
34
+ lines.push(` ${screen_1.ansi.dim}${"─".repeat(44)}${screen_1.ansi.reset}`);
35
+ return lines;
36
+ }
37
+ //# sourceMappingURL=minigameUI.js.map
@@ -0,0 +1,24 @@
1
+ import { BuddyState, Notification } from "../buddy/types";
2
+ import { Settings } from "../state/settings";
3
+ export type OverlayType = "titles" | "notifications" | "settings" | "gear" | "combatinfo" | "shop" | null;
4
+ export interface OverlayState {
5
+ type: OverlayType;
6
+ scrollIndex: number;
7
+ }
8
+ export declare function createOverlay(): OverlayState;
9
+ export declare function openOverlay(type: OverlayType): OverlayState;
10
+ export declare function scrollOverlay(overlay: OverlayState, delta: number, maxItems: number): OverlayState;
11
+ export declare function renderTitlesOverlay(state: BuddyState, overlay: OverlayState): string[];
12
+ export declare function renderNotificationsOverlay(notifications: Notification[], overlay: OverlayState): string[];
13
+ interface SettingEntry {
14
+ key: keyof Settings;
15
+ name: string;
16
+ description: string;
17
+ }
18
+ export declare function getSettingEntries(): SettingEntry[];
19
+ export declare function renderSettingsOverlay(settings: Settings, overlay: OverlayState): string[];
20
+ export declare function renderGearOverlay(state: BuddyState): string[];
21
+ export declare function renderCombatInfoOverlay(state: BuddyState): string[];
22
+ export declare function renderShopOverlay(state: BuddyState, overlay: OverlayState): string[];
23
+ export {};
24
+ //# sourceMappingURL=overlayUI.d.ts.map