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,144 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const types_1 = require("./types");
4
+ const registry_1 = require("./registry");
5
+ const screen_1 = require("../rendering/screen");
6
+ const MOODS = [
7
+ { face: "( ^_^ )", correct: "Happy", options: ["Happy", "Excited", "Sleepy", "Angry"] },
8
+ { face: "( >_< )", correct: "Angry", options: ["Sad", "Angry", "Scared", "Happy"] },
9
+ { face: "( ;_; )", correct: "Sad", options: ["Tired", "Sad", "Happy", "Confused"] },
10
+ { face: "( o_O )", correct: "Surprised", options: ["Surprised", "Angry", "Confused", "Happy"] },
11
+ { face: "( -_- )", correct: "Tired", options: ["Bored", "Tired", "Sad", "Calm"] },
12
+ { face: "( >w< )", correct: "Excited", options: ["Excited", "Happy", "Angry", "Nervous"] },
13
+ { face: "( ._. )", correct: "Confused", options: ["Sad", "Tired", "Confused", "Scared"] },
14
+ { face: "( @_@ )", correct: "Dizzy", options: ["Dizzy", "Surprised", "Excited", "Confused"] },
15
+ { face: "( ^o^ )", correct: "Joyful", options: ["Happy", "Joyful", "Excited", "Proud"] },
16
+ { face: "( T_T )", correct: "Crying", options: ["Sad", "Crying", "Tired", "Angry"] },
17
+ ];
18
+ function shuffle(arr) {
19
+ const copy = [...arr];
20
+ for (let i = copy.length - 1; i > 0; i--) {
21
+ const j = Math.floor(Math.random() * (i + 1));
22
+ [copy[i], copy[j]] = [copy[j], copy[i]];
23
+ }
24
+ return copy;
25
+ }
26
+ const handler = {
27
+ def: {
28
+ id: "moodmatch",
29
+ name: "Mood Match",
30
+ description: "Can you read your buddy's emotions?",
31
+ icon: "▲",
32
+ },
33
+ start() {
34
+ const questions = shuffle(MOODS).slice(0, 8);
35
+ const q = questions[0];
36
+ const shuffledOptions = shuffle(q.options);
37
+ return {
38
+ active: true,
39
+ gameId: "moodmatch",
40
+ phase: "intro",
41
+ score: 0,
42
+ round: 1,
43
+ maxRounds: 8,
44
+ buddyReaction: "Can you tell how I feel?",
45
+ data: {
46
+ questions,
47
+ currentOptions: shuffledOptions,
48
+ answered: false,
49
+ lastCorrect: false,
50
+ },
51
+ ticksInPhase: 0,
52
+ };
53
+ },
54
+ tick(game) {
55
+ game = { ...game, ticksInPhase: game.ticksInPhase + 1 };
56
+ if (game.phase === "intro" && game.ticksInPhase > 3) {
57
+ return { ...game, phase: "playing", ticksInPhase: 0 };
58
+ }
59
+ // Auto-advance after showing answer
60
+ if (game.phase === "playing" && game.data.answered && game.ticksInPhase > 3) {
61
+ if (game.round >= game.maxRounds) {
62
+ return { ...game, phase: "result", ticksInPhase: 0, buddyReaction: (0, types_1.getReaction)(game.score, game.maxRounds) };
63
+ }
64
+ const questions = game.data.questions;
65
+ const q = questions[game.round]; // 0-indexed, round is already incremented
66
+ const shuffledOptions = shuffle(q.options);
67
+ return {
68
+ ...game,
69
+ round: game.round + 1,
70
+ ticksInPhase: 0,
71
+ data: { ...game.data, currentOptions: shuffledOptions, answered: false },
72
+ buddyReaction: "What about this one?",
73
+ };
74
+ }
75
+ return game;
76
+ },
77
+ handleInput(key, game) {
78
+ if (game.phase === "intro") {
79
+ if (key === "\r" || key === "\n")
80
+ return { ...game, phase: "playing", ticksInPhase: 0 };
81
+ return game;
82
+ }
83
+ if (game.phase === "result") {
84
+ if (key === "\r" || key === "\n")
85
+ return { ...game, ticksInPhase: 99 };
86
+ return game;
87
+ }
88
+ if (game.data.answered)
89
+ return game;
90
+ const choice = parseInt(key);
91
+ if (choice < 1 || choice > 4 || isNaN(choice))
92
+ return game;
93
+ const questions = game.data.questions;
94
+ const q = questions[game.round - 1];
95
+ const options = game.data.currentOptions;
96
+ const picked = options[choice - 1];
97
+ const correct = picked === q.correct;
98
+ return {
99
+ ...game,
100
+ score: correct ? game.score + 1 : game.score,
101
+ ticksInPhase: 0,
102
+ data: { ...game.data, answered: true, lastCorrect: correct },
103
+ buddyReaction: correct ? "You know me so well!" : `That was "${q.correct}"!`,
104
+ };
105
+ },
106
+ render(game, buddyName) {
107
+ const lines = [];
108
+ lines.push("");
109
+ lines.push(` ${screen_1.ansi.bold}Mood Match${screen_1.ansi.reset} ${screen_1.ansi.dim}Round ${game.round}/${game.maxRounds} Correct: ${game.score}${screen_1.ansi.reset}`);
110
+ lines.push(` ${screen_1.ansi.dim}${"─".repeat(40)}${screen_1.ansi.reset}`);
111
+ lines.push("");
112
+ if (game.phase === "intro") {
113
+ lines.push(` ${buddyName}: "${game.buddyReaction}"`);
114
+ lines.push("");
115
+ lines.push(` ${screen_1.ansi.dim}Match the face to the emotion!${screen_1.ansi.reset}`);
116
+ lines.push("");
117
+ lines.push(` ${screen_1.ansi.dim}Press Enter to start...${screen_1.ansi.reset}`);
118
+ }
119
+ else if (game.phase === "playing") {
120
+ const questions = game.data.questions;
121
+ const q = questions[game.round - 1];
122
+ const options = game.data.currentOptions;
123
+ lines.push(` ${buddyName}: "${game.buddyReaction}"`);
124
+ lines.push("");
125
+ lines.push(` ${screen_1.ansi.bold}${screen_1.ansi.colors.yellow}${q.face}${screen_1.ansi.reset}`);
126
+ lines.push("");
127
+ for (let i = 0; i < options.length; i++) {
128
+ const marker = game.data.answered && options[i] === q.correct ? `${screen_1.ansi.colors.green}✓ ` : " ";
129
+ lines.push(` ${marker}[${i + 1}] ${options[i]}${screen_1.ansi.reset}`);
130
+ }
131
+ }
132
+ else {
133
+ lines.push(` ${buddyName}: "${game.buddyReaction}"`);
134
+ lines.push("");
135
+ lines.push(` ${screen_1.ansi.bold}Correct: ${game.score}/${game.maxRounds}${screen_1.ansi.reset}`);
136
+ lines.push("");
137
+ lines.push(` ${screen_1.ansi.dim}Press Enter to finish${screen_1.ansi.reset}`);
138
+ }
139
+ lines.push("");
140
+ return lines.join("\n");
141
+ },
142
+ };
143
+ (0, registry_1.registerGame)(handler);
144
+ //# sourceMappingURL=moodmatch.js.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=quickpaws.d.ts.map
@@ -0,0 +1,142 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const types_1 = require("./types");
4
+ const registry_1 = require("./registry");
5
+ const screen_1 = require("../rendering/screen");
6
+ const SYMBOLS = ["★", "♦", "●", "▲"];
7
+ const KEYS = ["1", "2", "3", "4"];
8
+ const TIMEOUT_TICKS = 3; // ~2.4 seconds per symbol
9
+ const handler = {
10
+ def: {
11
+ id: "quickpaws",
12
+ name: "Quick Paws",
13
+ description: "Press the right key before time runs out!",
14
+ icon: "♦",
15
+ },
16
+ start() {
17
+ return {
18
+ active: true,
19
+ gameId: "quickpaws",
20
+ phase: "intro",
21
+ score: 0,
22
+ round: 1,
23
+ maxRounds: 10,
24
+ buddyReaction: "Let's see how fast you are!",
25
+ data: {
26
+ currentSymbol: Math.floor(Math.random() * 4),
27
+ strikes: 0,
28
+ maxStrikes: 3,
29
+ ticksOnSymbol: 0,
30
+ lastResult: "", // "hit" | "miss" | "timeout" | ""
31
+ },
32
+ ticksInPhase: 0,
33
+ };
34
+ },
35
+ tick(game) {
36
+ game = { ...game, ticksInPhase: game.ticksInPhase + 1 };
37
+ if (game.phase === "intro" && game.ticksInPhase > 3) {
38
+ return { ...game, phase: "playing", ticksInPhase: 0, data: { ...game.data, ticksOnSymbol: 0 } };
39
+ }
40
+ if (game.phase === "playing") {
41
+ const ticksOnSymbol = game.data.ticksOnSymbol + 1;
42
+ if (ticksOnSymbol >= TIMEOUT_TICKS) {
43
+ // Timeout — strike
44
+ const strikes = game.data.strikes + 1;
45
+ if (strikes >= game.data.maxStrikes) {
46
+ return { ...game, phase: "result", ticksInPhase: 0, data: { ...game.data, strikes }, buddyReaction: (0, types_1.getReaction)(game.score, game.maxRounds) };
47
+ }
48
+ return {
49
+ ...game,
50
+ data: {
51
+ ...game.data,
52
+ strikes,
53
+ currentSymbol: Math.floor(Math.random() * 4),
54
+ ticksOnSymbol: 0,
55
+ lastResult: "timeout",
56
+ },
57
+ buddyReaction: "Too slow!",
58
+ };
59
+ }
60
+ return { ...game, data: { ...game.data, ticksOnSymbol } };
61
+ }
62
+ return game;
63
+ },
64
+ handleInput(key, game) {
65
+ if (game.phase === "intro") {
66
+ if (key === "\r" || key === "\n")
67
+ return { ...game, phase: "playing", ticksInPhase: 0 };
68
+ return game;
69
+ }
70
+ if (game.phase === "result") {
71
+ if (key === "\r" || key === "\n")
72
+ return { ...game, ticksInPhase: 99 };
73
+ return game;
74
+ }
75
+ const keyIndex = KEYS.indexOf(key);
76
+ if (keyIndex === -1)
77
+ return game;
78
+ const current = game.data.currentSymbol;
79
+ if (keyIndex === current) {
80
+ // Hit!
81
+ const newScore = game.score + 1;
82
+ if (newScore >= game.maxRounds) {
83
+ return { ...game, score: newScore, phase: "result", ticksInPhase: 0, buddyReaction: (0, types_1.getReaction)(newScore, game.maxRounds) };
84
+ }
85
+ return {
86
+ ...game,
87
+ score: newScore,
88
+ round: game.round + 1,
89
+ data: { ...game.data, currentSymbol: Math.floor(Math.random() * 4), ticksOnSymbol: 0, lastResult: "hit" },
90
+ buddyReaction: "Nice!",
91
+ };
92
+ }
93
+ else {
94
+ // Miss — strike
95
+ const strikes = game.data.strikes + 1;
96
+ if (strikes >= game.data.maxStrikes) {
97
+ return { ...game, phase: "result", ticksInPhase: 0, data: { ...game.data, strikes }, buddyReaction: (0, types_1.getReaction)(game.score, game.maxRounds) };
98
+ }
99
+ return {
100
+ ...game,
101
+ data: { ...game.data, strikes, currentSymbol: Math.floor(Math.random() * 4), ticksOnSymbol: 0, lastResult: "miss" },
102
+ buddyReaction: "Wrong one!",
103
+ };
104
+ }
105
+ },
106
+ render(game, buddyName) {
107
+ const lines = [];
108
+ const strikes = game.data.strikes || 0;
109
+ const maxStrikes = game.data.maxStrikes || 3;
110
+ lines.push("");
111
+ lines.push(` ${screen_1.ansi.bold}Quick Paws${screen_1.ansi.reset} ${screen_1.ansi.dim}Score: ${game.score} Strikes: ${"X".repeat(strikes)}${"_".repeat(maxStrikes - strikes)}${screen_1.ansi.reset}`);
112
+ lines.push(` ${screen_1.ansi.dim}${"─".repeat(40)}${screen_1.ansi.reset}`);
113
+ lines.push("");
114
+ if (game.phase === "intro") {
115
+ lines.push(` ${buddyName}: "${game.buddyReaction}"`);
116
+ lines.push("");
117
+ lines.push(` ${screen_1.ansi.dim}Keys: [1]${SYMBOLS[0]} [2]${SYMBOLS[1]} [3]${SYMBOLS[2]} [4]${SYMBOLS[3]}${screen_1.ansi.reset}`);
118
+ lines.push("");
119
+ lines.push(` ${screen_1.ansi.dim}Press Enter to start...${screen_1.ansi.reset}`);
120
+ }
121
+ else if (game.phase === "playing") {
122
+ const current = game.data.currentSymbol;
123
+ lines.push(` ${buddyName}: "${game.buddyReaction}"`);
124
+ lines.push("");
125
+ lines.push(` ${screen_1.ansi.bold}${screen_1.ansi.colors.yellow} Press [${current + 1}] → ${SYMBOLS[current]}${screen_1.ansi.reset}`);
126
+ lines.push("");
127
+ const ticksLeft = TIMEOUT_TICKS - game.data.ticksOnSymbol;
128
+ lines.push(` ${screen_1.ansi.dim}${"█".repeat(ticksLeft)}${"░".repeat(TIMEOUT_TICKS - ticksLeft)}${screen_1.ansi.reset}`);
129
+ }
130
+ else {
131
+ lines.push(` ${buddyName}: "${game.buddyReaction}"`);
132
+ lines.push("");
133
+ lines.push(` ${screen_1.ansi.bold}Final Score: ${game.score}/${game.maxRounds}${screen_1.ansi.reset}`);
134
+ lines.push("");
135
+ lines.push(` ${screen_1.ansi.dim}Press Enter to finish${screen_1.ansi.reset}`);
136
+ }
137
+ lines.push("");
138
+ return lines.join("\n");
139
+ },
140
+ };
141
+ (0, registry_1.registerGame)(handler);
142
+ //# sourceMappingURL=quickpaws.js.map
@@ -0,0 +1,5 @@
1
+ import { MiniGameHandler, MiniGameDef } from "./types";
2
+ export declare function registerGame(handler: MiniGameHandler): void;
3
+ export declare function getGameHandler(id: string): MiniGameHandler | undefined;
4
+ export declare function getAllGames(): MiniGameDef[];
5
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerGame = registerGame;
4
+ exports.getGameHandler = getGameHandler;
5
+ exports.getAllGames = getAllGames;
6
+ const handlers = new Map();
7
+ function registerGame(handler) {
8
+ handlers.set(handler.def.id, handler);
9
+ }
10
+ function getGameHandler(id) {
11
+ return handlers.get(id);
12
+ }
13
+ function getAllGames() {
14
+ return Array.from(handlers.values()).map((h) => h.def);
15
+ }
16
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=rpsplus.d.ts.map
@@ -0,0 +1,168 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const types_1 = require("./types");
4
+ const registry_1 = require("./registry");
5
+ const screen_1 = require("../rendering/screen");
6
+ const CHOICES = ["Rock", "Paper", "Scissors"];
7
+ const ELEMENTS = ["Nature", "Light", "Shadow"];
8
+ const ICONS = ["🪨", "📄", "✂️"];
9
+ // RPS rules: Rock > Scissors, Scissors > Paper, Paper > Rock
10
+ function rpsWinner(player, enemy) {
11
+ if (player === enemy)
12
+ return "draw";
13
+ if ((player + 1) % 3 === enemy)
14
+ return "win"; // Rock(0) beats Scissors(2), etc.
15
+ return "lose";
16
+ }
17
+ function pickRandom(arr) {
18
+ return arr[Math.floor(Math.random() * arr.length)];
19
+ }
20
+ const handler = {
21
+ def: {
22
+ id: "rpsplus",
23
+ name: "RPS+",
24
+ description: "Rock Paper Scissors with elemental twists!",
25
+ icon: "🪨",
26
+ },
27
+ start() {
28
+ return {
29
+ active: true,
30
+ gameId: "rpsplus",
31
+ phase: "intro",
32
+ score: 0,
33
+ round: 1,
34
+ maxRounds: 5,
35
+ buddyReaction: "Best of 5! Rock, Paper, or Scissors?",
36
+ data: {
37
+ enemyChoice: -1,
38
+ playerChoice: -1,
39
+ roundResult: "",
40
+ enemyScore: 0,
41
+ showResult: false,
42
+ // Buddy has a slight pattern — favors one choice
43
+ favoredChoice: Math.floor(Math.random() * 3),
44
+ },
45
+ ticksInPhase: 0,
46
+ };
47
+ },
48
+ tick(game) {
49
+ game = { ...game, ticksInPhase: game.ticksInPhase + 1 };
50
+ if (game.phase === "intro" && game.ticksInPhase > 3) {
51
+ return { ...game, phase: "playing", ticksInPhase: 0 };
52
+ }
53
+ // Auto-advance after showing round result
54
+ if (game.phase === "playing" && game.data.showResult && game.ticksInPhase > 4) {
55
+ const nextRound = game.round + 1;
56
+ if (nextRound > game.maxRounds) {
57
+ const won = game.score > game.data.enemyScore;
58
+ return {
59
+ ...game, phase: "result", ticksInPhase: 0,
60
+ buddyReaction: won ? (0, types_1.getReaction)(game.score, game.maxRounds) : "Good game! You got me!",
61
+ };
62
+ }
63
+ return {
64
+ ...game,
65
+ round: nextRound,
66
+ ticksInPhase: 0,
67
+ data: { ...game.data, showResult: false, playerChoice: -1, enemyChoice: -1, roundResult: "" },
68
+ buddyReaction: `Round ${nextRound}! Choose wisely...`,
69
+ };
70
+ }
71
+ return game;
72
+ },
73
+ handleInput(key, game) {
74
+ if (game.phase === "intro") {
75
+ if (key === "\r" || key === "\n")
76
+ return { ...game, phase: "playing", ticksInPhase: 0 };
77
+ return game;
78
+ }
79
+ if (game.phase === "result") {
80
+ if (key === "\r" || key === "\n")
81
+ return { ...game, ticksInPhase: 99 };
82
+ return game;
83
+ }
84
+ if (game.data.showResult)
85
+ return game; // waiting for auto-advance
86
+ const choice = parseInt(key) - 1;
87
+ if (choice < 0 || choice > 2 || isNaN(choice))
88
+ return game;
89
+ // Enemy choice — slight bias toward favored
90
+ const favored = game.data.favoredChoice;
91
+ let enemyChoice;
92
+ if (Math.random() < 0.4) {
93
+ enemyChoice = favored;
94
+ }
95
+ else {
96
+ enemyChoice = Math.floor(Math.random() * 3);
97
+ }
98
+ const result = rpsWinner(choice, enemyChoice);
99
+ let points = 0;
100
+ let reaction;
101
+ let enemyScore = game.data.enemyScore;
102
+ if (result === "win") {
103
+ points = 1;
104
+ reaction = `${CHOICES[choice]} beats ${CHOICES[enemyChoice]}! Nice!`;
105
+ }
106
+ else if (result === "lose") {
107
+ enemyScore++;
108
+ reaction = `${CHOICES[enemyChoice]} beats ${CHOICES[choice]}! Got you!`;
109
+ }
110
+ else {
111
+ reaction = `Both chose ${CHOICES[choice]}! Draw!`;
112
+ }
113
+ return {
114
+ ...game,
115
+ score: game.score + points,
116
+ ticksInPhase: 0,
117
+ data: { ...game.data, playerChoice: choice, enemyChoice, roundResult: result, enemyScore, showResult: true },
118
+ buddyReaction: reaction,
119
+ };
120
+ },
121
+ render(game, buddyName) {
122
+ const lines = [];
123
+ const enemyScore = game.data.enemyScore || 0;
124
+ lines.push("");
125
+ lines.push(` ${screen_1.ansi.bold}RPS+${screen_1.ansi.reset} ${screen_1.ansi.dim}Round ${game.round}/${game.maxRounds} You: ${game.score} | Buddy: ${enemyScore}${screen_1.ansi.reset}`);
126
+ lines.push(` ${screen_1.ansi.dim}${"─".repeat(40)}${screen_1.ansi.reset}`);
127
+ lines.push("");
128
+ if (game.phase === "intro") {
129
+ lines.push(` ${buddyName}: "${game.buddyReaction}"`);
130
+ lines.push("");
131
+ lines.push(` ${screen_1.ansi.dim}Rock = Nature, Paper = Light, Scissors = Shadow${screen_1.ansi.reset}`);
132
+ lines.push(` ${screen_1.ansi.dim}Elemental advantage = bonus point!${screen_1.ansi.reset}`);
133
+ lines.push("");
134
+ lines.push(` ${screen_1.ansi.dim}Press Enter to start...${screen_1.ansi.reset}`);
135
+ }
136
+ else if (game.phase === "playing") {
137
+ lines.push(` ${buddyName}: "${game.buddyReaction}"`);
138
+ lines.push("");
139
+ if (game.data.showResult) {
140
+ const pc = game.data.playerChoice;
141
+ const ec = game.data.enemyChoice;
142
+ lines.push(` You: ${ICONS[pc]} ${CHOICES[pc]} vs ${buddyName}: ${ICONS[ec]} ${CHOICES[ec]}`);
143
+ lines.push("");
144
+ const result = game.data.roundResult;
145
+ const color = result === "win" ? screen_1.ansi.colors.green : result === "lose" ? screen_1.ansi.colors.red : screen_1.ansi.colors.yellow;
146
+ lines.push(` ${color}${result === "win" ? "WIN!" : result === "lose" ? "LOSE!" : "DRAW!"}${screen_1.ansi.reset}`);
147
+ }
148
+ else {
149
+ lines.push(` [1] ${ICONS[0]} Rock (Nature)`);
150
+ lines.push(` [2] ${ICONS[1]} Paper (Light)`);
151
+ lines.push(` [3] ${ICONS[2]} Scissors (Shadow)`);
152
+ }
153
+ }
154
+ else {
155
+ const won = game.score > enemyScore;
156
+ lines.push(` ${buddyName}: "${game.buddyReaction}"`);
157
+ lines.push("");
158
+ lines.push(` ${screen_1.ansi.bold}Final: You ${game.score} - ${enemyScore} Buddy${screen_1.ansi.reset}`);
159
+ lines.push(` ${won ? `${screen_1.ansi.colors.green}You win!${screen_1.ansi.reset}` : game.score === enemyScore ? `${screen_1.ansi.colors.yellow}It's a tie!${screen_1.ansi.reset}` : `${screen_1.ansi.colors.red}Buddy wins!${screen_1.ansi.reset}`}`);
160
+ lines.push("");
161
+ lines.push(` ${screen_1.ansi.dim}Press Enter to finish${screen_1.ansi.reset}`);
162
+ }
163
+ lines.push("");
164
+ return lines.join("\n");
165
+ },
166
+ };
167
+ (0, registry_1.registerGame)(handler);
168
+ //# sourceMappingURL=rpsplus.js.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=treasurehunt.d.ts.map
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const types_1 = require("./types");
4
+ const registry_1 = require("./registry");
5
+ const screen_1 = require("../rendering/screen");
6
+ // Honest hints — indirect, never say the exact number
7
+ const HONEST_HINTS = [
8
+ (door) => door === 1 ? "I like being first..." : door === 2 ? "The middle is cozy..." : "I went far away...",
9
+ (door) => door === 1 ? "I didn't go far!" : door === 3 ? "I went as far as I could!" : "I'm somewhere in between...",
10
+ (door) => door === 2 ? "Not on the edges..." : "I'm on one of the sides...",
11
+ (door) => door === 1 ? "Think small..." : door === 3 ? "Think big..." : "Not too big, not too small...",
12
+ (_) => "I'll give you a wink if you're close...",
13
+ (_) => "Listen to your gut!",
14
+ ];
15
+ // Tricky hints — actively misleading or point to wrong door
16
+ const TRICKY_HINTS = [
17
+ (door) => { const wrong = door === 1 ? 3 : 1; return `I'm definitely behind door ${wrong}!`; },
18
+ (door) => { const wrong = door === 2 ? 1 : 2; return `The middle one? That's where I am!`.replace("middle", wrong === 1 ? "first" : "last"); },
19
+ (_) => "I'm NOT behind the middle door! ...or am I?",
20
+ (_) => "I left footprints going left... on purpose!",
21
+ (_) => "Would I lie to you? Yes. Yes I would.",
22
+ (door) => `Stay away from door ${door}... that one's boring!`,
23
+ ];
24
+ function pickRandom(arr) {
25
+ return arr[Math.floor(Math.random() * arr.length)];
26
+ }
27
+ const handler = {
28
+ def: {
29
+ id: "treasurehunt",
30
+ name: "Treasure Hunt",
31
+ description: "Find where your buddy is hiding!",
32
+ icon: "●",
33
+ },
34
+ start() {
35
+ const hiddenDoor = Math.floor(Math.random() * 3) + 1;
36
+ const isHonest = Math.random() < 0.7;
37
+ const hint = isHonest
38
+ ? pickRandom(HONEST_HINTS)(hiddenDoor)
39
+ : pickRandom(TRICKY_HINTS)(hiddenDoor);
40
+ return {
41
+ active: true,
42
+ gameId: "treasurehunt",
43
+ phase: "intro",
44
+ score: 0,
45
+ round: 1,
46
+ maxRounds: 5,
47
+ buddyReaction: "I'll hide, you seek!",
48
+ data: { hiddenDoor, hint, revealed: false, lastCorrect: false },
49
+ ticksInPhase: 0,
50
+ };
51
+ },
52
+ tick(game) {
53
+ game = { ...game, ticksInPhase: game.ticksInPhase + 1 };
54
+ if (game.phase === "intro" && game.ticksInPhase > 3) {
55
+ return { ...game, phase: "playing", ticksInPhase: 0 };
56
+ }
57
+ // After revealing answer, auto-advance to next round
58
+ if (game.phase === "playing" && game.data.revealed && game.ticksInPhase > 3) {
59
+ if (game.round >= game.maxRounds) {
60
+ return { ...game, phase: "result", ticksInPhase: 0, buddyReaction: (0, types_1.getReaction)(game.score, game.maxRounds) };
61
+ }
62
+ // Next round
63
+ const hiddenDoor = Math.floor(Math.random() * 3) + 1;
64
+ const isHonest = Math.random() < 0.7;
65
+ const hint = isHonest
66
+ ? pickRandom(HONEST_HINTS)(hiddenDoor)
67
+ : pickRandom(TRICKY_HINTS)(hiddenDoor);
68
+ return {
69
+ ...game,
70
+ round: game.round + 1,
71
+ ticksInPhase: 0,
72
+ data: { hiddenDoor, hint, revealed: false, lastCorrect: false },
73
+ buddyReaction: "I'll hide again!",
74
+ };
75
+ }
76
+ return game;
77
+ },
78
+ handleInput(key, game) {
79
+ if (game.phase === "intro") {
80
+ if (key === "\r" || key === "\n")
81
+ return { ...game, phase: "playing", ticksInPhase: 0 };
82
+ return game;
83
+ }
84
+ if (game.phase === "result") {
85
+ if (key === "\r" || key === "\n")
86
+ return { ...game, ticksInPhase: 99 };
87
+ return game;
88
+ }
89
+ if (game.data.revealed)
90
+ return game;
91
+ const choice = parseInt(key);
92
+ if (choice < 1 || choice > 3 || isNaN(choice))
93
+ return game;
94
+ const correct = choice === game.data.hiddenDoor;
95
+ return {
96
+ ...game,
97
+ score: correct ? game.score + 1 : game.score,
98
+ ticksInPhase: 0,
99
+ data: { ...game.data, revealed: true, lastCorrect: correct },
100
+ buddyReaction: correct ? pickRandom(["You found me!", "How did you know?!", "Good guess!"]) : `Nope! I was behind door ${game.data.hiddenDoor}!`,
101
+ };
102
+ },
103
+ render(game, buddyName) {
104
+ const lines = [];
105
+ lines.push("");
106
+ lines.push(` ${screen_1.ansi.bold}Treasure Hunt${screen_1.ansi.reset} ${screen_1.ansi.dim}Round ${game.round}/${game.maxRounds} Found: ${game.score}${screen_1.ansi.reset}`);
107
+ lines.push(` ${screen_1.ansi.dim}${"─".repeat(40)}${screen_1.ansi.reset}`);
108
+ lines.push("");
109
+ if (game.phase === "intro") {
110
+ lines.push(` ${buddyName}: "${game.buddyReaction}"`);
111
+ lines.push("");
112
+ lines.push(` ${screen_1.ansi.dim}Pick a door: [1] [2] [3]${screen_1.ansi.reset}`);
113
+ lines.push("");
114
+ lines.push(` ${screen_1.ansi.dim}Press Enter to start...${screen_1.ansi.reset}`);
115
+ }
116
+ else if (game.phase === "playing") {
117
+ lines.push(` ${buddyName}: "${game.buddyReaction}"`);
118
+ lines.push("");
119
+ if (!game.data.revealed) {
120
+ lines.push(` ${screen_1.ansi.dim}Hint: ${game.data.hint}${screen_1.ansi.reset}`);
121
+ lines.push("");
122
+ lines.push(` ${screen_1.ansi.bold}[1] 🚪 [2] 🚪 [3] 🚪${screen_1.ansi.reset}`);
123
+ }
124
+ else {
125
+ const hidden = game.data.hiddenDoor;
126
+ const doors = [1, 2, 3].map((d) => {
127
+ if (d === hidden)
128
+ return `${screen_1.ansi.colors.green}[${d}] ✓${screen_1.ansi.reset}`;
129
+ return `${screen_1.ansi.dim}[${d}]${screen_1.ansi.reset}`;
130
+ });
131
+ lines.push(` ${doors.join(" ")}`);
132
+ }
133
+ }
134
+ else {
135
+ lines.push(` ${buddyName}: "${game.buddyReaction}"`);
136
+ lines.push("");
137
+ lines.push(` ${screen_1.ansi.bold}Found: ${game.score}/${game.maxRounds}${screen_1.ansi.reset}`);
138
+ lines.push("");
139
+ lines.push(` ${screen_1.ansi.dim}Press Enter to finish${screen_1.ansi.reset}`);
140
+ }
141
+ lines.push("");
142
+ return lines.join("\n");
143
+ },
144
+ };
145
+ (0, registry_1.registerGame)(handler);
146
+ //# sourceMappingURL=treasurehunt.js.map
@@ -0,0 +1,30 @@
1
+ export interface MiniGameDef {
2
+ id: string;
3
+ name: string;
4
+ description: string;
5
+ icon: string;
6
+ }
7
+ export interface MiniGameState {
8
+ active: boolean;
9
+ gameId: string;
10
+ phase: "intro" | "playing" | "result";
11
+ score: number;
12
+ round: number;
13
+ maxRounds: number;
14
+ buddyReaction: string;
15
+ data: Record<string, unknown>;
16
+ ticksInPhase: number;
17
+ }
18
+ export interface MiniGameHandler {
19
+ def: MiniGameDef;
20
+ start(): MiniGameState;
21
+ tick(game: MiniGameState): MiniGameState;
22
+ handleInput(key: string, game: MiniGameState): MiniGameState;
23
+ render(game: MiniGameState, buddyName: string): string;
24
+ }
25
+ export declare function createInactiveGame(): MiniGameState;
26
+ /** Get XP reward based on score percentage */
27
+ export declare function getXpReward(score: number, maxScore: number, baseXp: number): number;
28
+ /** Pick a buddy reaction based on score percentage */
29
+ export declare function getReaction(score: number, maxScore: number): string;
30
+ //# sourceMappingURL=types.d.ts.map