generated from nhcarrigan/template
feat: post-prestige automation (auto-adventurer) (#76)
## Summary Closes #61 - Adds the **Autonomous Recruitment** prestige upgrade (50 runestones) to both the API and web data files - Adds `autoAdventurer?: boolean` to the `GameState` type for backwards-compatible saves - Adds tick-loop logic in GameContext that automatically purchases the highest-tier unlocked adventurer the player can afford each frame when the toggle is enabled - Adds `toggleAutoAdventurer` callback and exposes it through the context - Adds toggle UI in the Prestige Shop (mirrors the existing Auto-Prestige toggle pattern) - Updates the How to Play guide in the About panel to document the new automation feature ✨ This issue was created with help from Hikari~ 🌸 Reviewed-on: #76 Co-authored-by: Hikari <hikari@nhcarrigan.com> Co-committed-by: Hikari <hikari@nhcarrigan.com>
This commit was merged in pull request #76.
This commit is contained in:
@@ -210,6 +210,15 @@ export const defaultPrestigeUpgrades: Array<PrestigeUpgrade> = [
|
||||
runestonesCost: 1200,
|
||||
},
|
||||
// ── Utility Unlocks ───────────────────────────────────────────────────────
|
||||
{
|
||||
category: "utility",
|
||||
description:
|
||||
"Unlock the Auto-Adventurer toggle. When enabled, the tick engine will automatically purchase the highest-tier adventurer you can currently afford.",
|
||||
id: "auto_adventurer",
|
||||
multiplier: 1,
|
||||
name: "Autonomous Recruitment",
|
||||
runestonesCost: 50,
|
||||
},
|
||||
{
|
||||
category: "utility",
|
||||
description:
|
||||
|
||||
@@ -188,14 +188,16 @@ const howToPlay = [
|
||||
},
|
||||
{
|
||||
body:
|
||||
"Toggle automation in the Quests and Boss Encounters panels! Auto-Quest"
|
||||
+ " automatically sends your party on the highest-zone available quest"
|
||||
+ " as soon as one completes, skipping quests whose combat power"
|
||||
+ " requirement isn't met. Auto-Boss automatically challenges the"
|
||||
+ " highest available boss as soon as one is ready. Both can be toggled"
|
||||
+ " on or off at any time using the 🤖 Auto button in each panel"
|
||||
+ " header.",
|
||||
title: "🤖 Auto-Quest & Auto-Boss",
|
||||
"Toggle automation in the Quests, Boss Encounters, and Prestige Shop"
|
||||
+ " panels! Auto-Quest automatically sends your party on the"
|
||||
+ " highest-zone available quest as soon as one completes, skipping"
|
||||
+ " quests whose combat power requirement isn't met. Auto-Boss"
|
||||
+ " automatically challenges the highest available boss as soon as one"
|
||||
+ " is ready. Auto-Adventurer (unlocked via the Prestige Shop for 50"
|
||||
+ " runestones) automatically purchases the highest-tier adventurer you"
|
||||
+ " can currently afford each tick, keeping your income growing after a"
|
||||
+ " prestige without any manual clicks.",
|
||||
title: "🤖 Auto-Quest, Auto-Boss & Auto-Adventurer",
|
||||
},
|
||||
{
|
||||
body:
|
||||
|
||||
@@ -89,6 +89,7 @@ const PrestigePanel = (): JSX.Element => {
|
||||
buyPrestigeUpgrade,
|
||||
enableNotifications,
|
||||
enableSounds,
|
||||
toggleAutoAdventurer,
|
||||
toggleAutoPrestige,
|
||||
triggerPrestigeToast,
|
||||
} = useGame();
|
||||
@@ -110,7 +111,7 @@ const PrestigePanel = (): JSX.Element => {
|
||||
);
|
||||
}
|
||||
|
||||
const { prestige: prestigeData, player } = state;
|
||||
const { autoAdventurer, prestige: prestigeData, player } = state;
|
||||
const threshold = calculateThreshold(prestigeData.count);
|
||||
const isEligible = player.totalGoldEarned >= threshold;
|
||||
const runestonePreview = calculateRunestonePreview(
|
||||
@@ -173,6 +174,10 @@ const PrestigePanel = (): JSX.Element => {
|
||||
void handlePrestige();
|
||||
}
|
||||
|
||||
function handleAutoAdventurerToggle(): void {
|
||||
toggleAutoAdventurer();
|
||||
}
|
||||
|
||||
function handleAutoPrestigeToggle(): void {
|
||||
toggleAutoPrestige();
|
||||
}
|
||||
@@ -347,6 +352,9 @@ const PrestigePanel = (): JSX.Element => {
|
||||
= prestigeData.runestones >= upgrade.runestonesCost;
|
||||
const isLoading = buyingId === upgrade.id;
|
||||
|
||||
const isAutoAdventurerToggle
|
||||
= upgrade.id === "auto_adventurer" && purchased;
|
||||
const autoAdventurerEnabled = autoAdventurer ?? false;
|
||||
const isAutoPrestigeToggle
|
||||
= upgrade.id === "auto_prestige" && purchased;
|
||||
const autoPrestigeEnabled
|
||||
@@ -381,6 +389,21 @@ const PrestigePanel = (): JSX.Element => {
|
||||
: `🔮 ${formatNumber(upgrade.runestonesCost)} Runestones`}
|
||||
</p>
|
||||
</div>
|
||||
{isAutoAdventurerToggle
|
||||
? <button
|
||||
className={`auto-prestige-toggle ${
|
||||
autoAdventurerEnabled
|
||||
? "enabled"
|
||||
: "disabled"
|
||||
}`}
|
||||
onClick={handleAutoAdventurerToggle}
|
||||
type="button"
|
||||
>
|
||||
{autoAdventurerEnabled
|
||||
? "⚡ Auto ON"
|
||||
: "⏸ Auto OFF"}
|
||||
</button>
|
||||
: null}
|
||||
{isAutoPrestigeToggle
|
||||
? <button
|
||||
className={`auto-prestige-toggle ${
|
||||
|
||||
@@ -447,6 +447,11 @@ interface GameContextValue {
|
||||
*/
|
||||
toggleAutoBoss: ()=> void;
|
||||
|
||||
/**
|
||||
* Toggle the auto-adventurer setting on/off (requires auto_adventurer prestige upgrade).
|
||||
*/
|
||||
toggleAutoAdventurer: ()=> void;
|
||||
|
||||
/**
|
||||
* Queue of newly unlocked codex entry IDs (for toast notifications).
|
||||
*/
|
||||
@@ -1073,6 +1078,42 @@ export const GameProvider = ({
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-adventurer: buy one of the highest-tier affordable unlocked adventurer per tick
|
||||
if (
|
||||
next.autoAdventurer === true
|
||||
&& next.prestige.purchasedUpgradeIds.includes("auto_adventurer")
|
||||
) {
|
||||
const [ bestAdventurer ] = next.adventurers.
|
||||
filter((adventurer) => {
|
||||
const cost
|
||||
= adventurer.baseCost * Math.pow(1.15, adventurer.count);
|
||||
return adventurer.unlocked && next.resources.gold >= cost;
|
||||
}).
|
||||
sort((adventurerA, adventurerB) => {
|
||||
const costA
|
||||
= adventurerA.baseCost * Math.pow(1.15, adventurerA.count);
|
||||
const costB
|
||||
= adventurerB.baseCost * Math.pow(1.15, adventurerB.count);
|
||||
return costB - costA;
|
||||
});
|
||||
if (bestAdventurer !== undefined) {
|
||||
const purchaseCost
|
||||
= bestAdventurer.baseCost * Math.pow(1.15, bestAdventurer.count);
|
||||
next = {
|
||||
...next,
|
||||
adventurers: next.adventurers.map((adventurer) => {
|
||||
return adventurer.id === bestAdventurer.id
|
||||
? { ...adventurer, count: adventurer.count + 1 }
|
||||
: adventurer;
|
||||
}),
|
||||
resources: {
|
||||
...next.resources,
|
||||
gold: next.resources.gold - purchaseCost,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Detect newly unlocked achievements
|
||||
unlockedAchievementsReference.current = next.achievements.filter(
|
||||
(a, index) => {
|
||||
@@ -1858,6 +1899,18 @@ export const GameProvider = ({
|
||||
});
|
||||
}, []);
|
||||
|
||||
const toggleAutoAdventurer = useCallback(() => {
|
||||
setState((previous) => {
|
||||
if (previous === null) {
|
||||
return previous;
|
||||
}
|
||||
return {
|
||||
...previous,
|
||||
autoAdventurer: previous.autoAdventurer !== true,
|
||||
};
|
||||
});
|
||||
}, []);
|
||||
|
||||
const setActiveCompanion = useCallback((companionId: string | null) => {
|
||||
setState((previous) => {
|
||||
if (previous === null) {
|
||||
@@ -2169,6 +2222,7 @@ export const GameProvider = ({
|
||||
startQuest,
|
||||
state,
|
||||
syncError,
|
||||
toggleAutoAdventurer,
|
||||
toggleAutoBoss,
|
||||
toggleAutoPrestige,
|
||||
toggleAutoQuest,
|
||||
@@ -2240,6 +2294,7 @@ export const GameProvider = ({
|
||||
startQuest,
|
||||
state,
|
||||
syncError,
|
||||
toggleAutoAdventurer,
|
||||
toggleAutoBoss,
|
||||
toggleAutoPrestige,
|
||||
toggleAutoQuest,
|
||||
|
||||
@@ -212,6 +212,15 @@ export const PRESTIGE_UPGRADES: Array<PrestigeUpgrade> = [
|
||||
runestonesCost: 1200,
|
||||
},
|
||||
// ── Utility Unlocks ───────────────────────────────────────────────────────
|
||||
{
|
||||
category: "utility",
|
||||
description:
|
||||
"Unlock the Auto-Adventurer toggle. When enabled, the tick engine will automatically purchase the highest-tier adventurer you can currently afford.",
|
||||
id: "auto_adventurer",
|
||||
multiplier: 1,
|
||||
name: "Autonomous Recruitment",
|
||||
runestonesCost: 50,
|
||||
},
|
||||
{
|
||||
category: "utility",
|
||||
description:
|
||||
|
||||
@@ -79,6 +79,11 @@ interface GameState {
|
||||
*/
|
||||
autoBoss?: boolean;
|
||||
|
||||
/**
|
||||
* When true, the tick engine automatically purchases the highest-tier affordable adventurer.
|
||||
*/
|
||||
autoAdventurer?: boolean;
|
||||
|
||||
/**
|
||||
* Companion unlock and active selection state — optional for backwards compatibility.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user