generated from nhcarrigan/template
feat: post-prestige automation (auto-adventurer) #76
@@ -210,6 +210,15 @@ export const defaultPrestigeUpgrades: Array<PrestigeUpgrade> = [
|
|||||||
runestonesCost: 1200,
|
runestonesCost: 1200,
|
||||||
},
|
},
|
||||||
// ── Utility Unlocks ───────────────────────────────────────────────────────
|
// ── 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",
|
category: "utility",
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -188,14 +188,16 @@ const howToPlay = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
body:
|
body:
|
||||||
"Toggle automation in the Quests and Boss Encounters panels! Auto-Quest"
|
"Toggle automation in the Quests, Boss Encounters, and Prestige Shop"
|
||||||
+ " automatically sends your party on the highest-zone available quest"
|
+ " panels! Auto-Quest automatically sends your party on the"
|
||||||
+ " as soon as one completes, skipping quests whose combat power"
|
+ " highest-zone available quest as soon as one completes, skipping"
|
||||||
+ " requirement isn't met. Auto-Boss automatically challenges the"
|
+ " quests whose combat power requirement isn't met. Auto-Boss"
|
||||||
+ " highest available boss as soon as one is ready. Both can be toggled"
|
+ " automatically challenges the highest available boss as soon as one"
|
||||||
+ " on or off at any time using the 🤖 Auto button in each panel"
|
+ " is ready. Auto-Adventurer (unlocked via the Prestige Shop for 50"
|
||||||
+ " header.",
|
+ " runestones) automatically purchases the highest-tier adventurer you"
|
||||||
title: "🤖 Auto-Quest & Auto-Boss",
|
+ " can currently afford each tick, keeping your income growing after a"
|
||||||
|
+ " prestige without any manual clicks.",
|
||||||
|
title: "🤖 Auto-Quest, Auto-Boss & Auto-Adventurer",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
body:
|
body:
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ const PrestigePanel = (): JSX.Element => {
|
|||||||
buyPrestigeUpgrade,
|
buyPrestigeUpgrade,
|
||||||
enableNotifications,
|
enableNotifications,
|
||||||
enableSounds,
|
enableSounds,
|
||||||
|
toggleAutoAdventurer,
|
||||||
toggleAutoPrestige,
|
toggleAutoPrestige,
|
||||||
triggerPrestigeToast,
|
triggerPrestigeToast,
|
||||||
} = useGame();
|
} = 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 threshold = calculateThreshold(prestigeData.count);
|
||||||
const isEligible = player.totalGoldEarned >= threshold;
|
const isEligible = player.totalGoldEarned >= threshold;
|
||||||
const runestonePreview = calculateRunestonePreview(
|
const runestonePreview = calculateRunestonePreview(
|
||||||
@@ -173,6 +174,10 @@ const PrestigePanel = (): JSX.Element => {
|
|||||||
void handlePrestige();
|
void handlePrestige();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleAutoAdventurerToggle(): void {
|
||||||
|
toggleAutoAdventurer();
|
||||||
|
}
|
||||||
|
|
||||||
function handleAutoPrestigeToggle(): void {
|
function handleAutoPrestigeToggle(): void {
|
||||||
toggleAutoPrestige();
|
toggleAutoPrestige();
|
||||||
}
|
}
|
||||||
@@ -347,6 +352,9 @@ const PrestigePanel = (): JSX.Element => {
|
|||||||
= prestigeData.runestones >= upgrade.runestonesCost;
|
= prestigeData.runestones >= upgrade.runestonesCost;
|
||||||
const isLoading = buyingId === upgrade.id;
|
const isLoading = buyingId === upgrade.id;
|
||||||
|
|
||||||
|
const isAutoAdventurerToggle
|
||||||
|
= upgrade.id === "auto_adventurer" && purchased;
|
||||||
|
const autoAdventurerEnabled = autoAdventurer ?? false;
|
||||||
const isAutoPrestigeToggle
|
const isAutoPrestigeToggle
|
||||||
= upgrade.id === "auto_prestige" && purchased;
|
= upgrade.id === "auto_prestige" && purchased;
|
||||||
const autoPrestigeEnabled
|
const autoPrestigeEnabled
|
||||||
@@ -381,6 +389,21 @@ const PrestigePanel = (): JSX.Element => {
|
|||||||
: `🔮 ${formatNumber(upgrade.runestonesCost)} Runestones`}
|
: `🔮 ${formatNumber(upgrade.runestonesCost)} Runestones`}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
{isAutoAdventurerToggle
|
||||||
|
? <button
|
||||||
|
className={`auto-prestige-toggle ${
|
||||||
|
autoAdventurerEnabled
|
||||||
|
? "enabled"
|
||||||
|
: "disabled"
|
||||||
|
}`}
|
||||||
|
onClick={handleAutoAdventurerToggle}
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
{autoAdventurerEnabled
|
||||||
|
? "⚡ Auto ON"
|
||||||
|
: "⏸ Auto OFF"}
|
||||||
|
</button>
|
||||||
|
: null}
|
||||||
{isAutoPrestigeToggle
|
{isAutoPrestigeToggle
|
||||||
? <button
|
? <button
|
||||||
className={`auto-prestige-toggle ${
|
className={`auto-prestige-toggle ${
|
||||||
|
|||||||
@@ -447,6 +447,11 @@ interface GameContextValue {
|
|||||||
*/
|
*/
|
||||||
toggleAutoBoss: ()=> void;
|
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).
|
* 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
|
// Detect newly unlocked achievements
|
||||||
unlockedAchievementsReference.current = next.achievements.filter(
|
unlockedAchievementsReference.current = next.achievements.filter(
|
||||||
(a, index) => {
|
(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) => {
|
const setActiveCompanion = useCallback((companionId: string | null) => {
|
||||||
setState((previous) => {
|
setState((previous) => {
|
||||||
if (previous === null) {
|
if (previous === null) {
|
||||||
@@ -2169,6 +2222,7 @@ export const GameProvider = ({
|
|||||||
startQuest,
|
startQuest,
|
||||||
state,
|
state,
|
||||||
syncError,
|
syncError,
|
||||||
|
toggleAutoAdventurer,
|
||||||
toggleAutoBoss,
|
toggleAutoBoss,
|
||||||
toggleAutoPrestige,
|
toggleAutoPrestige,
|
||||||
toggleAutoQuest,
|
toggleAutoQuest,
|
||||||
@@ -2240,6 +2294,7 @@ export const GameProvider = ({
|
|||||||
startQuest,
|
startQuest,
|
||||||
state,
|
state,
|
||||||
syncError,
|
syncError,
|
||||||
|
toggleAutoAdventurer,
|
||||||
toggleAutoBoss,
|
toggleAutoBoss,
|
||||||
toggleAutoPrestige,
|
toggleAutoPrestige,
|
||||||
toggleAutoQuest,
|
toggleAutoQuest,
|
||||||
|
|||||||
@@ -212,6 +212,15 @@ export const PRESTIGE_UPGRADES: Array<PrestigeUpgrade> = [
|
|||||||
runestonesCost: 1200,
|
runestonesCost: 1200,
|
||||||
},
|
},
|
||||||
// ── Utility Unlocks ───────────────────────────────────────────────────────
|
// ── 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",
|
category: "utility",
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -79,6 +79,11 @@ interface GameState {
|
|||||||
*/
|
*/
|
||||||
autoBoss?: boolean;
|
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.
|
* Companion unlock and active selection state — optional for backwards compatibility.
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user