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
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TITLE_RULES = void 0;
|
|
4
|
+
exports.checkForNewTitles = checkForNewTitles;
|
|
5
|
+
exports.getTitleRule = getTitleRule;
|
|
6
|
+
exports.formatNameWithTitle = formatNameWithTitle;
|
|
7
|
+
exports.TITLE_RULES = [
|
|
8
|
+
// Level titles
|
|
9
|
+
{ id: "newborn", label: "Newborn", position: "prefix", hint: "Start your journey", condition: () => true },
|
|
10
|
+
{ id: "apprentice", label: "Apprentice", position: "prefix", hint: "Reach level 5", condition: (s) => s.level >= 5 },
|
|
11
|
+
{ id: "veteran", label: "Veteran", position: "prefix", hint: "Reach level 10", condition: (s) => s.level >= 10 },
|
|
12
|
+
{ id: "elder", label: "Elder", position: "prefix", hint: "Reach level 15", condition: (s) => s.level >= 15 },
|
|
13
|
+
{ id: "master", label: "Master", position: "prefix", hint: "Reach level 20", condition: (s) => s.level >= 20 },
|
|
14
|
+
{ id: "grandmaster", label: "Grandmaster", position: "prefix", hint: "Reach level 30", condition: (s) => s.level >= 30 },
|
|
15
|
+
// Age titles
|
|
16
|
+
{ id: "survivor", label: "Survivor", position: "suffix", hint: "Survive 24 hours", condition: (s) => s.age >= 24 },
|
|
17
|
+
{ id: "ancient", label: "the Ancient", position: "suffix", hint: "Survive 7 days", condition: (s) => s.age >= 168 },
|
|
18
|
+
{ id: "eternal", label: "the Eternal", position: "suffix", hint: "Survive 30 days", condition: (s) => s.age >= 720 },
|
|
19
|
+
// Action titles
|
|
20
|
+
{ id: "snack_lord", label: "Snack Lord", position: "suffix", hint: "Feed 50 times", condition: (s) => s.actionCounts.feeds >= 50 },
|
|
21
|
+
{ id: "well_fed", label: "the Well-Fed", position: "suffix", hint: "Feed 10 times", condition: (s) => s.actionCounts.feeds >= 10 },
|
|
22
|
+
{ id: "sleepyhead", label: "Sleepyhead", position: "suffix", hint: "Sleep 20 times", condition: (s) => s.actionCounts.sleeps >= 20 },
|
|
23
|
+
{ id: "hyperactive", label: "the Hyperactive", position: "suffix", hint: "Play 50 times", condition: (s) => s.actionCounts.plays >= 50 },
|
|
24
|
+
{ id: "playful", label: "the Playful", position: "suffix", hint: "Play 10 times", condition: (s) => s.actionCounts.plays >= 10 },
|
|
25
|
+
{ id: "iron_will", label: "of Iron Will", position: "suffix", hint: "Revive 3 times", condition: (s) => s.actionCounts.revives >= 3 },
|
|
26
|
+
{ id: "healer", label: "the Mended", position: "suffix", hint: "Heal 15 times", condition: (s) => s.actionCounts.heals >= 15 },
|
|
27
|
+
// Fun / rare
|
|
28
|
+
{ id: "hoarder", label: "the Hoarder", position: "suffix", hint: "Collect 20 items", condition: (s) => s.inventory.reduce((sum, sl) => sum + sl.count, 0) >= 20 },
|
|
29
|
+
{ id: "king", label: "King", position: "prefix", hint: "Reach level 25 and survive 7 days", condition: (s) => s.level >= 25 && s.age >= 168 },
|
|
30
|
+
// Adventure / combat titles
|
|
31
|
+
{ id: "adventurer", label: "the Adventurer", position: "suffix", hint: "Complete 1 adventure", condition: (s) => s.adventureStats.adventuresCompleted >= 1 },
|
|
32
|
+
{ id: "vet_explorer", label: "Veteran Explorer", position: "prefix", hint: "Complete 10 adventures", condition: (s) => s.adventureStats.adventuresCompleted >= 10 },
|
|
33
|
+
{ id: "unscathed", label: "the Unscathed", position: "suffix", hint: "Complete an adventure with no damage", condition: (s) => s.adventureStats.perfectAdventures >= 1 },
|
|
34
|
+
{ id: "dragon_slayer", label: "Dragon Slayer", position: "prefix", hint: "Defeat a boss", condition: (s) => s.adventureStats.bossesDefeated >= 1 },
|
|
35
|
+
{ id: "warrior", label: "the Warrior", position: "suffix", hint: "Win 10 battles", condition: (s) => s.adventureStats.battlesWon >= 10 },
|
|
36
|
+
{ id: "champion", label: "Champion", position: "prefix", hint: "Win 50 battles", condition: (s) => s.adventureStats.battlesWon >= 50 },
|
|
37
|
+
{ id: "pacifist", label: "the Pacifist", position: "suffix", hint: "Complete an adventure fleeing all combats", condition: (s) => s.adventureStats.battlesFled >= 3 },
|
|
38
|
+
{ id: "collector", label: "the Collector", position: "suffix", hint: "Own 5 different gear pieces", condition: (s) => s.inventory.filter((sl) => sl.itemId.includes("sword") || sl.itemId.includes("blade") || sl.itemId.includes("mail") || sl.itemId.includes("armor") || sl.itemId.includes("stick") || sl.itemId.includes("cloak") || sl.itemId.includes("scepter") || sl.itemId.includes("fang") || sl.itemId.includes("robe") || sl.itemId.includes("scale")).length >= 5 },
|
|
39
|
+
// Minigame titles
|
|
40
|
+
{ id: "quick_master", label: "Quick Paws Master", position: "suffix", hint: "Score 8+ in Quick Paws", condition: (s) => s.actionCounts.minigamesWon >= 3 },
|
|
41
|
+
{ id: "memory_champ", label: "Memory Champion", position: "suffix", hint: "Win Copycat", condition: (s) => s.actionCounts.minigamesWon >= 2 },
|
|
42
|
+
{ id: "game_master", label: "Game Master", position: "prefix", hint: "Win 10 minigames", condition: (s) => s.actionCounts.minigamesWon >= 10 },
|
|
43
|
+
// Relationship titles
|
|
44
|
+
{ id: "best_friends", label: "Best Friends", position: "suffix", hint: "Pet 50 times", condition: (s) => s.actionCounts.pets >= 50 },
|
|
45
|
+
{ id: "devoted", label: "the Devoted", position: "suffix", hint: "Age over 30 days", condition: (s) => s.age >= 720 },
|
|
46
|
+
{ id: "caretaker", label: "Caretaker", position: "prefix", hint: "Feed, play, sleep, heal 20+ each", condition: (s) => s.actionCounts.feeds >= 20 && s.actionCounts.plays >= 20 && s.actionCounts.sleeps >= 20 && s.actionCounts.heals >= 20 },
|
|
47
|
+
// Combat depth titles
|
|
48
|
+
{ id: "combo_king", label: "Combo King", position: "prefix", hint: "Trigger 10 combos", condition: (s) => s.actionCounts.combosTriggered >= 10 },
|
|
49
|
+
{ id: "elementalist", label: "the Elementalist", position: "suffix", hint: "Win 20 adventures", condition: (s) => s.adventureStats.adventuresCompleted >= 20 },
|
|
50
|
+
{ id: "untouchable", label: "the Untouchable", position: "suffix", hint: "Complete 5 perfect adventures", condition: (s) => s.adventureStats.perfectAdventures >= 5 },
|
|
51
|
+
// Rare/fun titles
|
|
52
|
+
{ id: "the_immortal", label: "the Immortal", position: "suffix", hint: "Revive 10 times", condition: (s) => s.actionCounts.revives >= 10 },
|
|
53
|
+
{ id: "loot_goblin", label: "Loot Goblin", position: "prefix", hint: "Find 50 items", condition: (s) => s.actionCounts.itemsFound >= 50 },
|
|
54
|
+
{ id: "pet_lover", label: "the Beloved", position: "suffix", hint: "Pet 100 times", condition: (s) => s.actionCounts.pets >= 100 },
|
|
55
|
+
{ id: "legend", label: "Legend", position: "prefix", hint: "Level 20 + 50 battles + 10 adventures", condition: (s) => s.level >= 20 && s.adventureStats.battlesWon >= 50 && s.adventureStats.adventuresCompleted >= 10 },
|
|
56
|
+
{ id: "completionist", label: "the Completionist", position: "suffix", hint: "Earn 40 other titles", condition: (s) => s.titles.length >= 40 },
|
|
57
|
+
// Story titles
|
|
58
|
+
{ id: "world_savior", label: "World Savior", position: "prefix", hint: "Complete The Shattered Harmony", condition: (s) => s.questProgress.storyFlags.includes("ch8_complete") },
|
|
59
|
+
{ id: "stone_seeker", label: "Stone Seeker", position: "suffix", hint: "Restore 3 Harmony Stones", condition: (s) => s.questProgress.completedChapters.filter((c) => c.startsWith("ch")).length >= 3 },
|
|
60
|
+
{ id: "shadow_walker", label: "Shadow Walker", position: "prefix", hint: "Enter the Shadow Realm", condition: (s) => s.questProgress.storyFlags.includes("ch7_complete") },
|
|
61
|
+
];
|
|
62
|
+
/**
|
|
63
|
+
* Check for newly earned titles. Returns IDs of titles just earned (not previously in state.titles).
|
|
64
|
+
*/
|
|
65
|
+
function checkForNewTitles(state) {
|
|
66
|
+
const newTitles = [];
|
|
67
|
+
for (const rule of exports.TITLE_RULES) {
|
|
68
|
+
if (!state.titles.includes(rule.id) && rule.condition(state)) {
|
|
69
|
+
newTitles.push(rule.id);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return newTitles;
|
|
73
|
+
}
|
|
74
|
+
function getTitleRule(id) {
|
|
75
|
+
return exports.TITLE_RULES.find((r) => r.id === id);
|
|
76
|
+
}
|
|
77
|
+
/** Format the buddy's display name with active title */
|
|
78
|
+
function formatNameWithTitle(state, speciesName) {
|
|
79
|
+
if (!state.activeTitle)
|
|
80
|
+
return `${state.name} the ${speciesName}`;
|
|
81
|
+
const rule = getTitleRule(state.activeTitle);
|
|
82
|
+
if (!rule)
|
|
83
|
+
return `${state.name} the ${speciesName}`;
|
|
84
|
+
if (rule.position === "prefix") {
|
|
85
|
+
return `${rule.label} ${state.name} the ${speciesName}`;
|
|
86
|
+
}
|
|
87
|
+
return `${state.name} ${rule.label}`;
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=titles.js.map
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
export type Rarity = "common" | "uncommon" | "rare" | "epic" | "legendary";
|
|
2
|
+
export type ActivityType = "idle" | "sleeping" | "eating" | "playing";
|
|
3
|
+
export interface AnimationSet {
|
|
4
|
+
idle: string[];
|
|
5
|
+
sleeping: string[];
|
|
6
|
+
eating: string[];
|
|
7
|
+
playing: string[];
|
|
8
|
+
sick: string[];
|
|
9
|
+
hungry?: string[];
|
|
10
|
+
tired?: string[];
|
|
11
|
+
sad?: string[];
|
|
12
|
+
}
|
|
13
|
+
export interface BuddySpecies {
|
|
14
|
+
id: string;
|
|
15
|
+
name: string;
|
|
16
|
+
rarity: Rarity;
|
|
17
|
+
animations: AnimationSet;
|
|
18
|
+
/** Base stats modifier (1.0 = normal) */
|
|
19
|
+
statModifier: number;
|
|
20
|
+
}
|
|
21
|
+
export interface BuddyStats {
|
|
22
|
+
hunger: number;
|
|
23
|
+
happiness: number;
|
|
24
|
+
health: number;
|
|
25
|
+
energy: number;
|
|
26
|
+
}
|
|
27
|
+
export interface ActivityState {
|
|
28
|
+
type: ActivityType;
|
|
29
|
+
startedAt: number;
|
|
30
|
+
durationMs: number;
|
|
31
|
+
statBaseline: Partial<BuddyStats>;
|
|
32
|
+
statTarget: Partial<BuddyStats>;
|
|
33
|
+
unskippable?: boolean;
|
|
34
|
+
}
|
|
35
|
+
export interface ActionCounts {
|
|
36
|
+
feeds: number;
|
|
37
|
+
plays: number;
|
|
38
|
+
sleeps: number;
|
|
39
|
+
heals: number;
|
|
40
|
+
revives: number;
|
|
41
|
+
pets: number;
|
|
42
|
+
minigamesWon: number;
|
|
43
|
+
itemsFound: number;
|
|
44
|
+
combosTriggered: number;
|
|
45
|
+
}
|
|
46
|
+
export interface InventorySlot {
|
|
47
|
+
itemId: string;
|
|
48
|
+
count: number;
|
|
49
|
+
}
|
|
50
|
+
export interface Notification {
|
|
51
|
+
message: string;
|
|
52
|
+
timestamp: number;
|
|
53
|
+
}
|
|
54
|
+
export interface AdventureStats {
|
|
55
|
+
adventuresCompleted: number;
|
|
56
|
+
battlesWon: number;
|
|
57
|
+
battlesLost: number;
|
|
58
|
+
battlesFled: number;
|
|
59
|
+
bossesDefeated: number;
|
|
60
|
+
totalDamageDealt: number;
|
|
61
|
+
perfectAdventures: number;
|
|
62
|
+
equippedWeapon?: string;
|
|
63
|
+
equippedArmor?: string;
|
|
64
|
+
equippedHelmet?: string;
|
|
65
|
+
equippedBoots?: string;
|
|
66
|
+
equippedAccessory?: string;
|
|
67
|
+
}
|
|
68
|
+
export interface BuddyState {
|
|
69
|
+
name: string;
|
|
70
|
+
speciesId: string;
|
|
71
|
+
stats: BuddyStats;
|
|
72
|
+
xp: number;
|
|
73
|
+
level: number;
|
|
74
|
+
age: number;
|
|
75
|
+
alive: boolean;
|
|
76
|
+
createdAt: number;
|
|
77
|
+
lastUpdated: number;
|
|
78
|
+
activity?: ActivityState;
|
|
79
|
+
titles: string[];
|
|
80
|
+
activeTitle?: string;
|
|
81
|
+
actionCounts: ActionCounts;
|
|
82
|
+
inventory: InventorySlot[];
|
|
83
|
+
notifications: Notification[];
|
|
84
|
+
adventureStats: AdventureStats;
|
|
85
|
+
questProgress: import("../story/types").QuestProgress;
|
|
86
|
+
}
|
|
87
|
+
export declare const RARITY_COLORS: Record<Rarity, string>;
|
|
88
|
+
export declare const RARITY_LABELS: Record<Rarity, string>;
|
|
89
|
+
export declare const RESET = "\u001B[0m";
|
|
90
|
+
export declare const DIM = "\u001B[2m";
|
|
91
|
+
export declare const BOLD = "\u001B[1m";
|
|
92
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BOLD = exports.DIM = exports.RESET = exports.RARITY_LABELS = exports.RARITY_COLORS = void 0;
|
|
4
|
+
exports.RARITY_COLORS = {
|
|
5
|
+
common: "\x1b[37m", // white
|
|
6
|
+
uncommon: "\x1b[32m", // green
|
|
7
|
+
rare: "\x1b[34m", // blue
|
|
8
|
+
epic: "\x1b[35m", // magenta
|
|
9
|
+
legendary: "\x1b[33m", // yellow/gold
|
|
10
|
+
};
|
|
11
|
+
exports.RARITY_LABELS = {
|
|
12
|
+
common: "★ Common",
|
|
13
|
+
uncommon: "★★ Uncommon",
|
|
14
|
+
rare: "★★★ Rare",
|
|
15
|
+
epic: "★★★★ Epic",
|
|
16
|
+
legendary: "★★★★★ Legendary",
|
|
17
|
+
};
|
|
18
|
+
exports.RESET = "\x1b[0m";
|
|
19
|
+
exports.DIM = "\x1b[2m";
|
|
20
|
+
exports.BOLD = "\x1b[1m";
|
|
21
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const activities_1 = require("../buddy/activities");
|
|
4
|
+
const leveling_1 = require("../buddy/leveling");
|
|
5
|
+
const registry_1 = require("./registry");
|
|
6
|
+
const screen_1 = require("../rendering/screen");
|
|
7
|
+
function clampStat(value) {
|
|
8
|
+
return Math.max(0, Math.min(100, value));
|
|
9
|
+
}
|
|
10
|
+
function busyCheck(state, attempted) {
|
|
11
|
+
if (!state.alive)
|
|
12
|
+
return `${state.name} is no longer with us... Use /revive first.`;
|
|
13
|
+
if (state.activity && (0, activities_1.isActivityBlocked)(state.activity.type, attempted)) {
|
|
14
|
+
const remaining = (0, activities_1.formatTimeRemaining)(state.activity);
|
|
15
|
+
const doing = state.activity.type === "sleeping" ? "sleeping" :
|
|
16
|
+
state.activity.type === "eating" ? "eating" : "playing";
|
|
17
|
+
return `${state.name} is ${doing}... (${remaining} remaining)`;
|
|
18
|
+
}
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
/** Factory for activity commands (feed, play, sleep) */
|
|
22
|
+
function registerActivityCommand(name, description, activityType, countKey, message, preCheck) {
|
|
23
|
+
(0, registry_1.registerCommand)({
|
|
24
|
+
name,
|
|
25
|
+
description,
|
|
26
|
+
execute: (state) => {
|
|
27
|
+
const blocked = busyCheck(state, activityType);
|
|
28
|
+
if (blocked)
|
|
29
|
+
return { state: null, message: blocked, rerender: false };
|
|
30
|
+
if (preCheck) {
|
|
31
|
+
const err = preCheck(state);
|
|
32
|
+
if (err)
|
|
33
|
+
return { state: null, message: err, rerender: false };
|
|
34
|
+
}
|
|
35
|
+
const activity = (0, activities_1.startActivity)(state, activityType);
|
|
36
|
+
const counts = { ...state.actionCounts, [countKey]: state.actionCounts[countKey] + 1 };
|
|
37
|
+
return { state: { ...state, activity, actionCounts: counts }, message: message(state.name), rerender: true };
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
registerActivityCommand("feed", "Feed your buddy to restore hunger", "eating", "feeds", (name) => `${name} starts eating! Nom nom...`);
|
|
42
|
+
registerActivityCommand("play", "Play with your buddy to boost happiness", "playing", "plays", (name) => `${name} starts playing!`, (state) => state.stats.energy < 10 ? `${state.name} is too tired to play. Let them /sleep first.` : null);
|
|
43
|
+
registerActivityCommand("sleep", "Let your buddy rest to restore energy", "sleeping", "sleeps", (name) => `${name} curls up and falls asleep... Zzz`);
|
|
44
|
+
(0, registry_1.registerCommand)({
|
|
45
|
+
name: "heal",
|
|
46
|
+
description: "Heal your buddy to restore health (instant)",
|
|
47
|
+
execute: (state) => {
|
|
48
|
+
if (!state.alive) {
|
|
49
|
+
return { state: null, message: `${state.name} is no longer with us... Use /revive first.`, rerender: false };
|
|
50
|
+
}
|
|
51
|
+
const newStats = { ...state.stats, health: clampStat(state.stats.health + 20) };
|
|
52
|
+
const counts = { ...state.actionCounts, heals: state.actionCounts.heals + 1 };
|
|
53
|
+
let newState = { ...state, stats: newStats, xp: state.xp + 3, actionCounts: counts };
|
|
54
|
+
const { state: checked, levelsGained } = (0, leveling_1.checkLevelUp)(newState);
|
|
55
|
+
newState = checked;
|
|
56
|
+
let msg = `You patch up ${state.name}. (+20 health, +3 XP)`;
|
|
57
|
+
if (levelsGained > 0)
|
|
58
|
+
msg += ` ${screen_1.ansi.bold}LEVEL UP! Now level ${newState.level}!${screen_1.ansi.reset}`;
|
|
59
|
+
return { state: newState, message: msg, rerender: true };
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
(0, registry_1.registerCommand)({
|
|
63
|
+
name: "revive",
|
|
64
|
+
description: "Revive a dead buddy (costs half your XP)",
|
|
65
|
+
execute: (state) => {
|
|
66
|
+
if (state.alive) {
|
|
67
|
+
return { state: null, message: `${state.name} is alive and well!`, rerender: false };
|
|
68
|
+
}
|
|
69
|
+
const counts = { ...state.actionCounts, revives: state.actionCounts.revives + 1 };
|
|
70
|
+
const newState = {
|
|
71
|
+
...state,
|
|
72
|
+
alive: true,
|
|
73
|
+
xp: Math.floor(state.xp / 2),
|
|
74
|
+
stats: { hunger: 50, happiness: 50, health: 50, energy: 50 },
|
|
75
|
+
lastUpdated: Date.now(),
|
|
76
|
+
activity: undefined,
|
|
77
|
+
actionCounts: counts,
|
|
78
|
+
};
|
|
79
|
+
return {
|
|
80
|
+
state: newState,
|
|
81
|
+
message: `${state.name} stirs back to life! They lost half their XP but they're back.`,
|
|
82
|
+
rerender: true,
|
|
83
|
+
};
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
(0, registry_1.registerCommand)({
|
|
87
|
+
name: "rename",
|
|
88
|
+
description: "Rename your buddy (usage: /rename NewName)",
|
|
89
|
+
execute: (state, args) => {
|
|
90
|
+
const newName = args.join(" ").trim();
|
|
91
|
+
if (!newName) {
|
|
92
|
+
return { state: null, message: "Usage: /rename <new name>", rerender: false };
|
|
93
|
+
}
|
|
94
|
+
const oldName = state.name;
|
|
95
|
+
const newState = { ...state, name: newName };
|
|
96
|
+
return { state: newState, message: `${oldName} is now known as ${newName}!`, rerender: true };
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
(0, registry_1.registerCommand)({
|
|
100
|
+
name: "help",
|
|
101
|
+
description: "Show all available commands",
|
|
102
|
+
execute: () => {
|
|
103
|
+
return { state: null, message: "Press / to open the command palette!", rerender: false };
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
(0, registry_1.registerCommand)({
|
|
107
|
+
name: "pet",
|
|
108
|
+
description: "Pet your buddy!",
|
|
109
|
+
execute: (state) => {
|
|
110
|
+
if (!state.alive) {
|
|
111
|
+
return { state: null, message: `${state.name} is no longer with us...`, rerender: false };
|
|
112
|
+
}
|
|
113
|
+
// Petting gives a small happiness boost
|
|
114
|
+
const newStats = { ...state.stats, happiness: clampStat(state.stats.happiness + 5) };
|
|
115
|
+
const counts = { ...state.actionCounts, pets: state.actionCounts.pets + 1 };
|
|
116
|
+
const newState = { ...state, stats: newStats, actionCounts: counts };
|
|
117
|
+
return { state: newState, message: "__PET__", rerender: true };
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
(0, registry_1.registerCommand)({
|
|
121
|
+
name: "gear",
|
|
122
|
+
description: "View your equipped gear",
|
|
123
|
+
execute: () => {
|
|
124
|
+
return { state: null, message: "__OPEN_GEAR__", rerender: false };
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
(0, registry_1.registerCommand)({
|
|
128
|
+
name: "combatinfo",
|
|
129
|
+
description: "View your combat stats",
|
|
130
|
+
execute: () => {
|
|
131
|
+
return { state: null, message: "__OPEN_COMBATINFO__", rerender: false };
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
(0, registry_1.registerCommand)({
|
|
135
|
+
name: "quit",
|
|
136
|
+
description: "Save and exit CLIBuddy",
|
|
137
|
+
execute: (state) => {
|
|
138
|
+
return { state: null, message: `Goodbye! ${state.name} will be waiting.`, rerender: false, quit: true };
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
//# sourceMappingURL=actions.js.map
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const species_1 = require("../buddy/species");
|
|
4
|
+
const activities_1 = require("../buddy/activities");
|
|
5
|
+
const titles_1 = require("../buddy/titles");
|
|
6
|
+
const registry_1 = require("./registry");
|
|
7
|
+
(0, registry_1.registerCommand)({
|
|
8
|
+
name: "admin",
|
|
9
|
+
description: "Toggle admin mode (shows debug commands)",
|
|
10
|
+
execute: () => {
|
|
11
|
+
const nowOn = (0, registry_1.toggleAdminMode)();
|
|
12
|
+
return { state: null, message: nowOn ? "Admin mode ON — debug commands visible in palette" : "Admin mode OFF", rerender: false };
|
|
13
|
+
},
|
|
14
|
+
});
|
|
15
|
+
(0, registry_1.registerCommand)({
|
|
16
|
+
name: "swap",
|
|
17
|
+
hidden: true,
|
|
18
|
+
description: "[Admin] Change buddy species",
|
|
19
|
+
execute: (state, args) => {
|
|
20
|
+
const speciesId = args[0]?.toLowerCase();
|
|
21
|
+
if (!speciesId) {
|
|
22
|
+
const list = species_1.SPECIES.map((s) => s.id).join(", ");
|
|
23
|
+
return { state: null, message: `Usage: /swap <species> (${list})`, rerender: false };
|
|
24
|
+
}
|
|
25
|
+
const species = (0, species_1.getSpecies)(speciesId);
|
|
26
|
+
if (!species) {
|
|
27
|
+
const list = species_1.SPECIES.map((s) => s.id).join(", ");
|
|
28
|
+
return { state: null, message: `Unknown species "${speciesId}". Available: ${list}`, rerender: false };
|
|
29
|
+
}
|
|
30
|
+
const newState = { ...state, speciesId: species.id };
|
|
31
|
+
return {
|
|
32
|
+
state: newState,
|
|
33
|
+
message: `${state.name} transformed into a ${species.name}! (${species.rarity})`,
|
|
34
|
+
rerender: true,
|
|
35
|
+
};
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
(0, registry_1.registerCommand)({
|
|
39
|
+
name: "setstat",
|
|
40
|
+
hidden: true,
|
|
41
|
+
description: "[Admin] Set a stat value",
|
|
42
|
+
execute: (state, args) => {
|
|
43
|
+
const statName = args[0]?.toLowerCase();
|
|
44
|
+
const value = parseInt(args[1], 10);
|
|
45
|
+
const validStats = ["hunger", "happiness", "health", "energy"];
|
|
46
|
+
if (!statName || !validStats.includes(statName) || isNaN(value)) {
|
|
47
|
+
return { state: null, message: `Usage: /setstat <${validStats.join("|")}> <0-100>`, rerender: false };
|
|
48
|
+
}
|
|
49
|
+
const clamped = Math.max(0, Math.min(100, value));
|
|
50
|
+
const newStats = { ...state.stats, [statName]: clamped };
|
|
51
|
+
const newState = { ...state, stats: newStats };
|
|
52
|
+
return { state: newState, message: `Set ${statName} to ${clamped}`, rerender: true };
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
(0, registry_1.registerCommand)({
|
|
56
|
+
name: "setlevel",
|
|
57
|
+
hidden: true,
|
|
58
|
+
description: "[Admin] Set buddy level",
|
|
59
|
+
execute: (state, args) => {
|
|
60
|
+
const level = parseInt(args[0], 10);
|
|
61
|
+
if (isNaN(level) || level < 1) {
|
|
62
|
+
return { state: null, message: "Usage: /setlevel <number>", rerender: false };
|
|
63
|
+
}
|
|
64
|
+
const newState = { ...state, level };
|
|
65
|
+
return { state: newState, message: `Set level to ${level}`, rerender: true };
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
(0, registry_1.registerCommand)({
|
|
69
|
+
name: "setxp",
|
|
70
|
+
hidden: true,
|
|
71
|
+
description: "[Admin] Set buddy XP",
|
|
72
|
+
execute: (state, args) => {
|
|
73
|
+
const xp = parseInt(args[0], 10);
|
|
74
|
+
if (isNaN(xp) || xp < 0) {
|
|
75
|
+
return { state: null, message: "Usage: /setxp <number>", rerender: false };
|
|
76
|
+
}
|
|
77
|
+
const newState = { ...state, xp };
|
|
78
|
+
return { state: newState, message: `Set XP to ${xp}`, rerender: true };
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
(0, registry_1.registerCommand)({
|
|
82
|
+
name: "kill",
|
|
83
|
+
hidden: true,
|
|
84
|
+
description: "[Admin] Kill buddy to test death state",
|
|
85
|
+
execute: (state) => {
|
|
86
|
+
const newState = {
|
|
87
|
+
...state,
|
|
88
|
+
alive: false,
|
|
89
|
+
stats: { ...state.stats, health: 0 },
|
|
90
|
+
activity: undefined,
|
|
91
|
+
};
|
|
92
|
+
return { state: newState, message: `${state.name} has been killed (admin).`, rerender: true };
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
(0, registry_1.registerCommand)({
|
|
96
|
+
name: "testact",
|
|
97
|
+
hidden: true,
|
|
98
|
+
description: "[Admin] Force an activity (sleeping/eating/playing)",
|
|
99
|
+
execute: (state, args) => {
|
|
100
|
+
const type = args[0]?.toLowerCase();
|
|
101
|
+
if (type !== "sleeping" && type !== "eating" && type !== "playing") {
|
|
102
|
+
return { state: null, message: "Usage: /testact <sleeping|eating|playing>", rerender: false };
|
|
103
|
+
}
|
|
104
|
+
const activity = (0, activities_1.startActivity)(state, type);
|
|
105
|
+
const newState = { ...state, activity };
|
|
106
|
+
return { state: newState, message: `Forced ${type} activity.`, rerender: true };
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
(0, registry_1.registerCommand)({
|
|
110
|
+
name: "stopact",
|
|
111
|
+
hidden: true,
|
|
112
|
+
description: "[Admin] Cancel current activity",
|
|
113
|
+
execute: (state) => {
|
|
114
|
+
if (!state.activity) {
|
|
115
|
+
return { state: null, message: "No active activity.", rerender: false };
|
|
116
|
+
}
|
|
117
|
+
const newState = { ...state, activity: undefined };
|
|
118
|
+
return { state: newState, message: "Activity cancelled.", rerender: true };
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
(0, registry_1.registerCommand)({
|
|
122
|
+
name: "equip",
|
|
123
|
+
hidden: true,
|
|
124
|
+
description: "[Admin] Equip a title by ID",
|
|
125
|
+
execute: (state, args) => {
|
|
126
|
+
const titleId = args[0]?.toLowerCase();
|
|
127
|
+
if (!titleId) {
|
|
128
|
+
const earned = state.titles.join(", ") || "none";
|
|
129
|
+
return { state: null, message: `Usage: /equip <title-id> Earned: ${earned}`, rerender: false };
|
|
130
|
+
}
|
|
131
|
+
if (!state.titles.includes(titleId)) {
|
|
132
|
+
return { state: null, message: "You haven't earned that title yet!", rerender: false };
|
|
133
|
+
}
|
|
134
|
+
const rule = (0, titles_1.getTitleRule)(titleId);
|
|
135
|
+
const newState = { ...state, activeTitle: titleId };
|
|
136
|
+
return { state: newState, message: `Title equipped: ${rule?.label ?? titleId}`, rerender: true };
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
(0, registry_1.registerCommand)({
|
|
140
|
+
name: "resetstory",
|
|
141
|
+
hidden: true,
|
|
142
|
+
description: "[Admin] Reset all story/quest progress",
|
|
143
|
+
execute: (state) => {
|
|
144
|
+
const { createDefaultQuestProgress } = require("../story/types");
|
|
145
|
+
const newState = { ...state, questProgress: createDefaultQuestProgress() };
|
|
146
|
+
return { state: newState, message: "Story progress reset! All chapters, flags, and gold cleared.", rerender: true };
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
(0, registry_1.registerCommand)({
|
|
150
|
+
name: "resetchapter",
|
|
151
|
+
hidden: true,
|
|
152
|
+
description: "[Admin] Uncomplete a specific chapter (usage: /resetchapter ch1_whispers)",
|
|
153
|
+
execute: (state, args) => {
|
|
154
|
+
const chapterId = args[0];
|
|
155
|
+
if (!chapterId) {
|
|
156
|
+
const completed = state.questProgress.completedChapters.join(", ") || "none";
|
|
157
|
+
return { state: null, message: `Usage: /resetchapter <id> Completed: ${completed}`, rerender: false };
|
|
158
|
+
}
|
|
159
|
+
const progress = { ...state.questProgress };
|
|
160
|
+
progress.completedChapters = progress.completedChapters.filter((c) => c !== chapterId);
|
|
161
|
+
// Also remove the story flag for that chapter
|
|
162
|
+
progress.storyFlags = progress.storyFlags.filter((f) => !f.startsWith(chapterId.replace("ch", "ch").split("_")[0]));
|
|
163
|
+
const newState = { ...state, questProgress: progress };
|
|
164
|
+
return { state: newState, message: `Uncompleted chapter: ${chapterId}`, rerender: true };
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
(0, registry_1.registerCommand)({
|
|
168
|
+
name: "addgold",
|
|
169
|
+
hidden: true,
|
|
170
|
+
description: "[Admin] Add gold (usage: /addgold 100)",
|
|
171
|
+
execute: (state, args) => {
|
|
172
|
+
const amount = parseInt(args[0], 10);
|
|
173
|
+
if (isNaN(amount))
|
|
174
|
+
return { state: null, message: "Usage: /addgold <amount>", rerender: false };
|
|
175
|
+
const progress = { ...state.questProgress, gold: state.questProgress.gold + amount };
|
|
176
|
+
return { state: { ...state, questProgress: progress }, message: `Added ${amount} gold (total: ${progress.gold})`, rerender: true };
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
(0, registry_1.registerCommand)({
|
|
180
|
+
name: "completechapter",
|
|
181
|
+
hidden: true,
|
|
182
|
+
description: "[Admin] Force-complete a chapter (usage: /completechapter ch1_whispers)",
|
|
183
|
+
execute: (state, args) => {
|
|
184
|
+
const chapterId = args[0];
|
|
185
|
+
if (!chapterId)
|
|
186
|
+
return { state: null, message: "Usage: /completechapter <id>", rerender: false };
|
|
187
|
+
const progress = { ...state.questProgress };
|
|
188
|
+
if (!progress.completedChapters.includes(chapterId)) {
|
|
189
|
+
progress.completedChapters = [...progress.completedChapters, chapterId];
|
|
190
|
+
}
|
|
191
|
+
// Derive the story flag (ch1_whispers → ch1_complete)
|
|
192
|
+
const chNum = chapterId.match(/ch(\d+)/)?.[1];
|
|
193
|
+
if (chNum) {
|
|
194
|
+
const flag = `ch${chNum}_complete`;
|
|
195
|
+
if (!progress.storyFlags.includes(flag)) {
|
|
196
|
+
progress.storyFlags = [...progress.storyFlags, flag];
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return { state: { ...state, questProgress: progress }, message: `Completed chapter: ${chapterId}`, rerender: true };
|
|
200
|
+
},
|
|
201
|
+
});
|
|
202
|
+
//# sourceMappingURL=admin.js.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { BuddyState } from "../buddy/types";
|
|
2
|
+
export interface CommandResult {
|
|
3
|
+
/** Updated state (null = no change) */
|
|
4
|
+
state: BuddyState | null;
|
|
5
|
+
/** Message to display to the user */
|
|
6
|
+
message: string;
|
|
7
|
+
/** Should the buddy be re-rendered after this command? */
|
|
8
|
+
rerender: boolean;
|
|
9
|
+
/** Should the app quit? */
|
|
10
|
+
quit?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export interface Command {
|
|
13
|
+
name: string;
|
|
14
|
+
description: string;
|
|
15
|
+
hidden?: boolean;
|
|
16
|
+
execute: (state: BuddyState, args: string[]) => CommandResult;
|
|
17
|
+
}
|
|
18
|
+
export declare function registerCommand(cmd: Command): void;
|
|
19
|
+
export declare function getCommand(name: string): Command | undefined;
|
|
20
|
+
/** Get all commands visible in the palette (respects admin mode) */
|
|
21
|
+
export declare function getAllCommands(): Command[];
|
|
22
|
+
export declare function getCommandNames(): string[];
|
|
23
|
+
export declare function isAdminMode(): boolean;
|
|
24
|
+
export declare function toggleAdminMode(): boolean;
|
|
25
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerCommand = registerCommand;
|
|
4
|
+
exports.getCommand = getCommand;
|
|
5
|
+
exports.getAllCommands = getAllCommands;
|
|
6
|
+
exports.getCommandNames = getCommandNames;
|
|
7
|
+
exports.isAdminMode = isAdminMode;
|
|
8
|
+
exports.toggleAdminMode = toggleAdminMode;
|
|
9
|
+
let adminMode = false;
|
|
10
|
+
const commands = new Map();
|
|
11
|
+
function registerCommand(cmd) {
|
|
12
|
+
commands.set(cmd.name, cmd);
|
|
13
|
+
}
|
|
14
|
+
function getCommand(name) {
|
|
15
|
+
return commands.get(name);
|
|
16
|
+
}
|
|
17
|
+
/** Get all commands visible in the palette (respects admin mode) */
|
|
18
|
+
function getAllCommands() {
|
|
19
|
+
return Array.from(commands.values()).filter((c) => !c.hidden || adminMode).sort((a, b) => a.name.localeCompare(b.name));
|
|
20
|
+
}
|
|
21
|
+
function getCommandNames() {
|
|
22
|
+
return getAllCommands().map((c) => `/${c.name}`);
|
|
23
|
+
}
|
|
24
|
+
function isAdminMode() {
|
|
25
|
+
return adminMode;
|
|
26
|
+
}
|
|
27
|
+
function toggleAdminMode() {
|
|
28
|
+
adminMode = !adminMode;
|
|
29
|
+
return adminMode;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=registry.js.map
|