From 9377282e708c47f1c37ec864b95eb022d84059d5 Mon Sep 17 00:00:00 2001
From: Jakub Buciuto <46843555+MrJacob12@users.noreply.github.com>
Date: Wed, 18 Mar 2026 10:08:41 +0000
Subject: [PATCH 06/11] feat: add dimension milestones and permanent inventory
rewards
---
src/config/gameConfig.ts | 1 +
src/pages/Dimension.tsx | 244 +++++++++++++++++++++++++----
src/store/gameStore.ts | 27 ++--
src/store/slices/currencySlice.ts | 1 +
src/store/slices/dimensionSlice.ts | 15 +-
src/store/slices/upgradeSlice.ts | 4 +-
6 files changed, 248 insertions(+), 44 deletions(-)
diff --git a/src/config/gameConfig.ts b/src/config/gameConfig.ts
index ee57965..70b0bbf 100644
--- a/src/config/gameConfig.ts
+++ b/src/config/gameConfig.ts
@@ -54,6 +54,7 @@ export const GAME_CONFIG = {
DIMENSIONS: {
BONUS_STEP: 5,
BONUS_AMOUNT: 200, // (dimensionLevel + 1) * 200
+ INVENTORY_BONUS_PER_STEP: 5, // +5 slots every 5 levels
SCALE_FACTOR: 0.25,
MAX_LEVEL: 100,
MAX_LEVEL_REWARD: 1000000,
diff --git a/src/pages/Dimension.tsx b/src/pages/Dimension.tsx
index d3f67ac..2df3470 100644
--- a/src/pages/Dimension.tsx
+++ b/src/pages/Dimension.tsx
@@ -3,13 +3,28 @@ import { Header } from "@/components/game/Header";
import { Footer } from "@/components/game/Footer";
import { Button } from "@/components/ui/button";
import { Link } from "react-router-dom";
-import { Map, ArrowLeft, Sword, History, Zap, Beaker } from "lucide-react";
+import {
+ Map,
+ ArrowLeft,
+ Sword,
+ History,
+ Zap,
+ Beaker,
+ ChevronDown,
+ ChevronUp,
+ Trophy,
+} from "lucide-react";
import { useGameStore, resolveCardStats } from "@/store/gameStore";
import { GameCard as GameCardType } from "@/types/game";
import { GameCard } from "@/components/game/GameCard";
import { toast } from "sonner";
import { formatNumber, formatCurrency } from "@/lib/utils";
import { GAME_CONFIG } from "@/config/gameConfig";
+import {
+ Collapsible,
+ CollapsibleContent,
+ CollapsibleTrigger,
+} from "@/components/ui/collapsible";
const Dimension = () => {
const {
@@ -26,6 +41,8 @@ const Dimension = () => {
} = useGameStore();
const [isFighting, setIsFighting] = useState(false);
+ const [showMilestones, setShowMilestones] = useState(false);
+
const [battleState, setBattleState] = useState<{
playerAttacking: boolean;
enemyAttacking: boolean;
@@ -41,27 +58,30 @@ const Dimension = () => {
// Find player's strongest card and calculate base vs bonus
const playerStats = useMemo(() => {
if (inventory.length === 0) return null;
-
+
// Process inventory to resolve stats first
const resolvedInventory = inventory
.filter(Boolean)
- .map(card => ({
+ .map((card) => ({
card,
- stats: resolveCardStats(card)
+ stats: resolveCardStats(card),
}))
.sort((a, b) => b.stats.power - a.stats.power);
-
+
const strongest = resolvedInventory[0];
// Lab Power Upgrade
- const powerMultiplier = 1 + upgrades.power * GAME_CONFIG.UPGRADES.power.BONUS_PER_LEVEL;
+ const powerMultiplier =
+ 1 + upgrades.power * GAME_CONFIG.UPGRADES.power.BONUS_PER_LEVEL;
const totalPower = Math.floor(strongest.stats.power * powerMultiplier);
return {
card: strongest.card,
basePower: strongest.stats.power,
totalPower,
- bonusPercent: Math.round(upgrades.power * GAME_CONFIG.UPGRADES.power.BONUS_PER_LEVEL * 100),
+ bonusPercent: Math.round(
+ upgrades.power * GAME_CONFIG.UPGRADES.power.BONUS_PER_LEVEL * 100,
+ ),
};
}, [inventory, upgrades.power]);
@@ -79,32 +99,32 @@ const Dimension = () => {
const handleFight = async () => {
if (!playerStats || !currentEnemy) return;
-
+
const enemyStats = resolveCardStats(currentEnemy);
setIsFighting(true);
const playerWins = playerStats.totalPower >= enemyStats.power;
// 1. Player Attack
- setBattleState(s => ({ ...s, playerAttacking: true }));
-
- await new Promise(r => setTimeout(r, 300));
+ setBattleState((s) => ({ ...s, playerAttacking: true }));
+
+ await new Promise((r) => setTimeout(r, 300));
// Damage pops up on enemy
- setBattleState(s => ({ ...s, enemyDamage: playerStats.totalPower }));
-
- await new Promise(r => setTimeout(r, 400));
- setBattleState(s => ({ ...s, playerAttacking: false }));
+ setBattleState((s) => ({ ...s, enemyDamage: playerStats.totalPower }));
+
+ await new Promise((r) => setTimeout(r, 400));
+ setBattleState((s) => ({ ...s, playerAttacking: false }));
// 2. Enemy Attack - ONLY if player didn't win yet
if (!playerWins) {
- await new Promise(r => setTimeout(r, 200));
- setBattleState(s => ({ ...s, enemyAttacking: true }));
-
- await new Promise(r => setTimeout(r, 300));
+ await new Promise((r) => setTimeout(r, 200));
+ setBattleState((s) => ({ ...s, enemyAttacking: true }));
+
+ await new Promise((r) => setTimeout(r, 300));
// Damage pops up on player
- setBattleState(s => ({ ...s, playerDamage: enemyStats.power }));
-
- await new Promise(r => setTimeout(r, 400));
- setBattleState(s => ({ ...s, enemyAttacking: false }));
+ setBattleState((s) => ({ ...s, playerDamage: enemyStats.power }));
+
+ await new Promise((r) => setTimeout(r, 400));
+ setBattleState((s) => ({ ...s, enemyAttacking: false }));
}
// 3. Final Result
@@ -115,28 +135,30 @@ const Dimension = () => {
});
resetDimension(GAME_CONFIG.DIMENSIONS.MAX_LEVEL_REWARD);
} else if (playerWins) {
- const { bonus, milestoneUnlocked } = nextDimensionLevel();
+ const { bonus, inventoryBonus, milestoneUnlocked } =
+ nextDimensionLevel();
if (milestoneUnlocked) {
toast.success(`MILESTONE REACHED!`, {
- description: `You unlocked: ${milestoneUnlocked}!`,
+ description: `You unlocked: ${milestoneUnlocked}! +${inventoryBonus} Card Slots!`,
duration: 5000,
});
} else if (bonus > 0) {
toast.success(`Victory! Level ${dimensionLevel} cleared.`, {
- description: `Milestone Bonus: +${formatCurrency(bonus)} Mega Seeds!`,
+ description: `Bonus: +${formatCurrency(bonus)} Mega Seeds & +${inventoryBonus} Card Slots!`,
});
} else {
toast.success(`Victory! Level ${dimensionLevel} cleared.`);
}
} else {
- const reward = dimensionLevel * GAME_CONFIG.DIMENSIONS.LEVEL_REWARD_MULTIPLIER;
+ const reward =
+ dimensionLevel * GAME_CONFIG.DIMENSIONS.LEVEL_REWARD_MULTIPLIER;
toast.error(`Defeat! You reached level ${dimensionLevel}.`, {
description: `Final Reward: ${formatCurrency(reward)} Mega Seeds.`,
});
resetDimension(reward);
}
-
+
setIsFighting(false);
setBattleState({
playerAttacking: false,
@@ -179,7 +201,13 @@ const Dimension = () => {
- Lab Bonus: +{Math.round(upgrades.power * GAME_CONFIG.UPGRADES.power.BONUS_PER_LEVEL * 100)}% Power
+ Lab Bonus: +
+ {Math.round(
+ upgrades.power *
+ GAME_CONFIG.UPGRADES.power.BONUS_PER_LEVEL *
+ 100,
+ )}
+ % Power
@@ -190,6 +218,159 @@ const Dimension = () => {
+ {/* Milestone Progress Section */}
+
+
+
+
+
+
+
+
+
+
+
+ Rift Completion
+
+
+ Unlock powerful bonuses at each milestone
+
+
+
+
+ {maxDimensionLevel}
+
+ /100
+
+
+
+
+
+
+
+ {/* Milestone Markers */}
+ {[10, 25, 50, 100].map((m) => (
+
= m ? "bg-white/40" : "bg-white/10"}`}
+ style={{ left: `${m}%` }}
+ />
+ ))}
+
+
+
+ {[10, 25, 50, 100].map((m) => {
+ const name = GAME_CONFIG.DIMENSIONS.MILESTONES[m];
+ const isReached = maxDimensionLevel >= m;
+ return (
+
+
+
+ {m}
+
+
+
+ {name || "???"}
+
+
+ {isReached ? "UNLOCKED" : "LOCKED"}
+
+
+ );
+ })}
+
+
+
+
+
+
+
+
+ Current Active Bonus
+
+
+ You've gained{" "}
+
+ +{Math.floor(maxDimensionLevel / 5) * 5}
+ {" "}
+ extra card slots from your rift progression.
+
+
+
+
+
+
+
{!isDimensionActive ? (
@@ -256,7 +437,6 @@ const Dimension = () => {
) : (
-
{/* PLAYER */}
@@ -268,7 +448,7 @@ const Dimension = () => {
}`}
>
{playerStats &&
}
-
+
{/* Player Damage Indicator */}
{battleState.playerDamage !== null && (
@@ -312,7 +492,7 @@ const Dimension = () => {
}`}
>
{currentEnemy &&
}
-
+
{/* Enemy Damage Indicator */}
{battleState.enemyDamage !== null && (
diff --git a/src/store/gameStore.ts b/src/store/gameStore.ts
index 4fdafda..0675bd5 100644
--- a/src/store/gameStore.ts
+++ b/src/store/gameStore.ts
@@ -1,6 +1,7 @@
import { create } from "zustand";
import { persist } from "zustand/middleware";
import { generateCard } from "./cardUtils";
+import { GAME_CONFIG } from "@/config/gameConfig";
import { createCurrencySlice, CurrencySlice } from "./slices/currencySlice";
import { createInventorySlice, InventorySlice } from "./slices/inventorySlice";
@@ -40,17 +41,25 @@ export const useGameStore = create
()(
name: "rick-morty-idle-save",
version: 8,
migrate: migrateGameStore,
- onRehydrateStorage: () => (state: GameStore | undefined) => {
- if (state && state.inventory.length === 0) {
- const starter = generateCard(
- { COMMON: 0.8, RARE: 0.2, HOLO: 0, FULL_ART: 0 },
- 0,
- );
- state.addCard(starter);
- state.addDiscovery(starter.characterId, starter.types);
+ onRehydrateStorage: () => (state) => {
+ if (state) {
+ // Recalculate maxInventory to ensure consistency
+ // Check if upgrades and dimensionInventoryBonus exist before accessing
+ const upgradeBonus = (state.upgrades?.inventory || 0) * GAME_CONFIG.UPGRADES.inventory.BONUS_PER_LEVEL;
+ const dimensionBonus = state.dimensionInventoryBonus || 0;
+
+ state.maxInventory = GAME_CONFIG.INITIAL_MAX_INVENTORY + upgradeBonus + dimensionBonus;
+
+ if (state.inventory && state.inventory.length === 0) {
+ const starter = generateCard(
+ { COMMON: 0.8, RARE: 0.2, HOLO: 0, FULL_ART: 0 },
+ 0,
+ );
+ state.addCard(starter);
+ state.addDiscovery(starter.characterId, starter.types);
+ }
}
},
},
),
);
-
diff --git a/src/store/slices/currencySlice.ts b/src/store/slices/currencySlice.ts
index ceda443..2173dd1 100644
--- a/src/store/slices/currencySlice.ts
+++ b/src/store/slices/currencySlice.ts
@@ -35,6 +35,7 @@ export const createCurrencySlice: StateCreator<
maxDimensionLevel: 1,
isDimensionActive: false,
currentEnemy: null,
+ dimensionInventoryBonus: 0,
upgrades: { seeds: 0, power: 0, inventory: 0 },
lastSaved: Date.now(),
});
diff --git a/src/store/slices/dimensionSlice.ts b/src/store/slices/dimensionSlice.ts
index 1f3557c..adcc460 100644
--- a/src/store/slices/dimensionSlice.ts
+++ b/src/store/slices/dimensionSlice.ts
@@ -10,8 +10,9 @@ export interface DimensionSlice {
maxDimensionLevel: number;
isDimensionActive: boolean;
currentEnemy: GameCard | null;
+ dimensionInventoryBonus: number; // New field for permanent bonus
startDimension: () => boolean;
- nextDimensionLevel: () => { bonus: number; milestoneUnlocked: string | null };
+ nextDimensionLevel: () => { bonus: number; inventoryBonus: number; milestoneUnlocked: string | null };
resetDimension: (reward: number) => void;
}
@@ -25,6 +26,7 @@ export const createDimensionSlice: StateCreator<
maxDimensionLevel: 1,
isDimensionActive: false,
currentEnemy: null,
+ dimensionInventoryBonus: 0,
startDimension: () => {
const { seeds } = get();
@@ -51,10 +53,12 @@ export const createDimensionSlice: StateCreator<
nextDimensionLevel: () => {
const { dimensionLevel } = get();
let bonus = 0;
+ let inventoryBonus = 0;
let milestoneUnlocked = null;
if ((dimensionLevel + 1) % GAME_CONFIG.DIMENSIONS.BONUS_STEP === 0) {
bonus = (dimensionLevel + 1) * GAME_CONFIG.DIMENSIONS.BONUS_AMOUNT;
+ inventoryBonus = GAME_CONFIG.DIMENSIONS.INVENTORY_BONUS_PER_STEP;
}
const nextLvl = dimensionLevel + 1;
@@ -73,15 +77,22 @@ export const createDimensionSlice: StateCreator<
set((s) => {
const nextLevel = s.dimensionLevel + 1;
const newMax = Math.max(s.maxDimensionLevel, nextLevel);
+
+ const newDimensionBonus = s.dimensionInventoryBonus + inventoryBonus;
+ const upgradeBonus = s.upgrades.inventory * GAME_CONFIG.UPGRADES.inventory.BONUS_PER_LEVEL;
+ const newMaxInventory = GAME_CONFIG.INITIAL_MAX_INVENTORY + upgradeBonus + newDimensionBonus;
+
return {
seeds: s.seeds + bonus,
+ dimensionInventoryBonus: newDimensionBonus,
+ maxInventory: newMaxInventory,
dimensionLevel: nextLevel,
maxDimensionLevel: newMax,
currentEnemy: newEnemy,
};
});
- return { bonus, milestoneUnlocked };
+ return { bonus, inventoryBonus, milestoneUnlocked };
},
resetDimension: (reward) => {
diff --git a/src/store/slices/upgradeSlice.ts b/src/store/slices/upgradeSlice.ts
index 2a90093..1b17346 100644
--- a/src/store/slices/upgradeSlice.ts
+++ b/src/store/slices/upgradeSlice.ts
@@ -58,9 +58,11 @@ export const createUpgradeSlice: StateCreator<
};
if (type === "inventory") {
+ const currentDimensionBonus = s.dimensionInventoryBonus || 0;
newState.maxInventory =
GAME_CONFIG.INITIAL_MAX_INVENTORY +
- newLevel * GAME_CONFIG.UPGRADES.inventory.BONUS_PER_LEVEL;
+ newLevel * GAME_CONFIG.UPGRADES.inventory.BONUS_PER_LEVEL +
+ currentDimensionBonus;
}
return newState;
From 74bfcf6bd0e3d38ca499312ea783f251a145d9b0 Mon Sep 17 00:00:00 2001
From: Jakub Buciuto <46843555+MrJacob12@users.noreply.github.com>
Date: Wed, 18 Mar 2026 13:04:40 +0000
Subject: [PATCH 07/11] feat: replace baseMultiplier with lore-accurate IQ
system and update production formula
---
README.md | 4 +-
src/components/game/AutoOpenManager.tsx | 5 +-
src/components/game/GameCard.tsx | 2 +-
src/components/game/card-parts/CardStats.tsx | 11 ++-
src/data/cardTypes.json | 10 +--
src/data/characters.json | 80 ++++++++++----------
src/pages/Collection.tsx | 13 +++-
src/store/cardUtils.ts | 11 ++-
src/types/game.ts | 2 +-
9 files changed, 80 insertions(+), 58 deletions(-)
diff --git a/README.md b/README.md
index 6591339..6c11775 100644
--- a/README.md
+++ b/README.md
@@ -52,7 +52,7 @@ Open `src/data/characters.json` and add a new object to the array:
```json
{
"name": "Character Name",
- "baseMultiplier": 5,
+ "iq": 100,
"status": "Alive",
"species": "Human",
"avatarId": 1
@@ -60,7 +60,7 @@ Open `src/data/characters.json` and add a new object to the array:
```
- **name**: Display name of the character.
-- **baseMultiplier**: Base Mega Seeds per second (integer or float).
+- **iq**: Intelligence Quotient (1-400), used in Mega Seeds production formula: `floor(iq^1.5 * rarity_multiplier * 0.1)`.
- **status**: Character status (`"Alive"`, `"Dead"`, or `"Unknown"`).
- **species**: The species of the character.
- **avatarId**: The ID used for the character's image.
diff --git a/src/components/game/AutoOpenManager.tsx b/src/components/game/AutoOpenManager.tsx
index 76b7764..6eebbe2 100644
--- a/src/components/game/AutoOpenManager.tsx
+++ b/src/components/game/AutoOpenManager.tsx
@@ -116,7 +116,10 @@ export function AutoOpenManager() {
alt=""
/>
-
{stats.character.name}
+
+
{stats.character.name}
+
IQ {stats.character.iq}
+
{card.types.map(t => (
{t}
diff --git a/src/components/game/GameCard.tsx b/src/components/game/GameCard.tsx
index 68df8f2..6320946 100644
--- a/src/components/game/GameCard.tsx
+++ b/src/components/game/GameCard.tsx
@@ -54,7 +54,7 @@ export function GameCard({ card, onClick, isActive }: GameCardProps) {
-
+
diff --git a/src/components/game/card-parts/CardStats.tsx b/src/components/game/card-parts/CardStats.tsx
index d41f73f..a3adde3 100644
--- a/src/components/game/card-parts/CardStats.tsx
+++ b/src/components/game/card-parts/CardStats.tsx
@@ -1,12 +1,13 @@
-import { Leaf, Sword } from "lucide-react";
+import { Leaf, Sword, Brain } from "lucide-react";
import { formatNumber } from "@/lib/utils";
interface CardStatsProps {
income: number;
power: number;
+ iq?: number;
}
-export function CardStats({ income, power }: CardStatsProps) {
+export function CardStats({ income, power, iq }: CardStatsProps) {
return (
@@ -21,6 +22,12 @@ export function CardStats({ income, power }: CardStatsProps) {
{formatNumber(power)}
+ {iq !== undefined && (
+
+
+ {iq}
+
+ )}
);
}
diff --git a/src/data/cardTypes.json b/src/data/cardTypes.json
index 7437fb7..15efd04 100644
--- a/src/data/cardTypes.json
+++ b/src/data/cardTypes.json
@@ -14,35 +14,35 @@
{
"id": "HOLO",
"label": "Holo",
- "multiplier": 3,
+ "multiplier": 4,
"canCombine": true,
"combinesWith": ["FULL_ART"]
},
{
"id": "FULL_ART",
"label": "Full Art",
- "multiplier": 4,
+ "multiplier": 8,
"canCombine": true,
"combinesWith": ["HOLO", "SILVER", "GOLD", "REVERT"]
},
{
"id": "SILVER",
"label": "Silver",
- "multiplier": 6,
+ "multiplier": 10,
"canCombine": true,
"combinesWith": ["FULL_ART"]
},
{
"id": "GOLD",
"label": "Gold",
- "multiplier": 10,
+ "multiplier": 12,
"canCombine": true,
"combinesWith": ["FULL_ART"]
},
{
"id": "REVERT",
"label": "Revert",
- "multiplier": 15,
+ "multiplier": 20,
"canCombine": true,
"combinesWith": ["FULL_ART"]
}
diff --git a/src/data/characters.json b/src/data/characters.json
index 947c3e1..0a981a2 100644
--- a/src/data/characters.json
+++ b/src/data/characters.json
@@ -10,7 +10,7 @@
"location": "Citadel of Ricks",
"customImage": "https://rickandmortyapi.com/api/character/avatar/1.jpeg",
"avatarId": 1,
- "baseMultiplier": 1000,
+ "iq": 500,
"basePower": 500
},
{
@@ -23,7 +23,7 @@
"origin": "unknown",
"location": "Citadel of Ricks",
"avatarId": 2,
- "baseMultiplier": 25,
+ "iq": 90,
"basePower": 150
},
{
@@ -36,7 +36,7 @@
"origin": "Earth (Replacement Dimension)",
"location": "Earth (Replacement Dimension)",
"avatarId": 3,
- "baseMultiplier": 45,
+ "iq": 115,
"basePower": 100
},
{
@@ -49,7 +49,7 @@
"origin": "Earth (Replacement Dimension)",
"location": "Earth (Replacement Dimension)",
"avatarId": 4,
- "baseMultiplier": 50,
+ "iq": 155,
"basePower": 15
},
{
@@ -62,7 +62,7 @@
"origin": "Earth (Replacement Dimension)",
"location": "Earth (Replacement Dimension)",
"avatarId": 5,
- "baseMultiplier": 150,
+ "iq": 20,
"basePower": 1
},
{
@@ -75,7 +75,7 @@
"origin": "Abadango",
"location": "Abadango",
"avatarId": 6,
- "baseMultiplier": 85,
+ "iq": 130,
"basePower": 45
},
{
@@ -88,7 +88,7 @@
"origin": "Earth (Replacement Dimension)",
"location": "Testicle Monster Dimension",
"avatarId": 7,
- "baseMultiplier": 500,
+ "iq": 120,
"basePower": 66
},
{
@@ -101,7 +101,7 @@
"origin": "unknown",
"location": "Citadel of Ricks",
"avatarId": 8,
- "baseMultiplier": 250,
+ "iq": 245,
"basePower": 450
},
{
@@ -114,7 +114,7 @@
"origin": "Earth (Replacement Dimension)",
"location": "Earth (Replacement Dimension)",
"avatarId": 9,
- "baseMultiplier": 1500,
+ "iq": 230,
"basePower": 5
},
{
@@ -127,7 +127,7 @@
"origin": "unknown",
"location": "Worldender's lair",
"avatarId": 10,
- "baseMultiplier": 100,
+ "iq": 110,
"basePower": 300
},
{
@@ -140,7 +140,7 @@
"origin": "Earth (C-137)",
"location": "Earth (Replacement Dimension)",
"avatarId": 11,
- "baseMultiplier": 5000,
+ "iq": 400,
"basePower": 0
},
{
@@ -153,7 +153,7 @@
"origin": "Earth (C-137)",
"location": "Anatomy Park",
"avatarId": 12,
- "baseMultiplier": 12,
+ "iq": 70,
"basePower": 5
},
{
@@ -166,7 +166,7 @@
"origin": "unknown",
"location": "Earth (Replacement Dimension)",
"avatarId": 13,
- "baseMultiplier": 50,
+ "iq": 60,
"basePower": 50
},
{
@@ -179,7 +179,7 @@
"origin": "unknown",
"location": "Citadel of Ricks",
"avatarId": 14,
- "baseMultiplier": 60,
+ "iq": 85,
"basePower": 125
},
{
@@ -192,7 +192,7 @@
"origin": "unknown",
"location": "Citadel of Ricks",
"avatarId": 15,
- "baseMultiplier": 500,
+ "iq": 360,
"basePower": 400
},
{
@@ -205,7 +205,7 @@
"origin": "unknown",
"location": "Earth (Replacement Dimension)",
"avatarId": 16,
- "baseMultiplier": 75,
+ "iq": 90,
"basePower": 80
},
{
@@ -218,7 +218,7 @@
"origin": "Earth (C-137)",
"location": "Anatomy Park",
"avatarId": 17,
- "baseMultiplier": 40,
+ "iq": 100,
"basePower": 20
},
{
@@ -231,7 +231,7 @@
"origin": "unknown",
"location": "Citadel of Ricks",
"avatarId": 18,
- "baseMultiplier": 65,
+ "iq": 88,
"basePower": 135
},
{
@@ -245,7 +245,7 @@
"location": "unknown",
"customImage": "https://static.wikia.nocookie.net/rickandmorty/images/4/49/Antenna_Rick.png/revision/latest?cb=20161121231006",
"avatarId": 19,
- "baseMultiplier": 450,
+ "iq": 365,
"basePower": 350
},
{
@@ -258,7 +258,7 @@
"origin": "unknown",
"location": "Interdimensional Cable",
"avatarId": 20,
- "baseMultiplier": 120,
+ "iq": 50,
"basePower": 10
},
{
@@ -271,7 +271,7 @@
"origin": "unknown",
"location": "Citadel of Ricks",
"avatarId": 21,
- "baseMultiplier": 40,
+ "iq": 82,
"basePower": 110
},
{
@@ -284,7 +284,7 @@
"origin": "unknown",
"location": "Citadel of Ricks",
"avatarId": 22,
- "baseMultiplier": 600,
+ "iq": 370,
"basePower": 450
},
{
@@ -297,7 +297,7 @@
"origin": "unknown",
"location": "Immortality Field Resort",
"avatarId": 23,
- "baseMultiplier": 120,
+ "iq": 80,
"basePower": 50
},
{
@@ -310,7 +310,7 @@
"origin": "Signus 5 Expanse",
"location": "Signus 5 Expanse",
"avatarId": 24,
- "baseMultiplier": 2000,
+ "iq": 330,
"basePower": 10
},
{
@@ -323,7 +323,7 @@
"origin": "Post-Apocalyptic Earth",
"location": "Post-Apocalyptic Earth",
"avatarId": 25,
- "baseMultiplier": 80,
+ "iq": 65,
"basePower": 250
},
{
@@ -336,7 +336,7 @@
"origin": "Purge Planet",
"location": "Purge Planet",
"avatarId": 26,
- "baseMultiplier": 90,
+ "iq": 110,
"basePower": 180
},
{
@@ -349,7 +349,7 @@
"origin": "unknown",
"location": "Citadel of Ricks",
"avatarId": 27,
- "baseMultiplier": 50,
+ "iq": 95,
"basePower": 95
},
{
@@ -362,7 +362,7 @@
"origin": "unknown",
"location": "Interdimensional Cable",
"avatarId": 28,
- "baseMultiplier": 150,
+ "iq": 95,
"basePower": 300
},
{
@@ -375,7 +375,7 @@
"origin": "unknown",
"location": "Interdimensional Cable",
"avatarId": 29,
- "baseMultiplier": 100,
+ "iq": 90,
"basePower": 40
},
{
@@ -388,7 +388,7 @@
"origin": "unknown",
"location": "unknown",
"avatarId": 30,
- "baseMultiplier": 250,
+ "iq": 140,
"basePower": 50
},
{
@@ -401,7 +401,7 @@
"origin": "unknown",
"location": "Earth (Replacement Dimension)",
"avatarId": 31,
- "baseMultiplier": 300,
+ "iq": 180,
"basePower": 150
},
{
@@ -414,7 +414,7 @@
"origin": "unknown",
"location": "Earth (Replacement Dimension)",
"avatarId": 32,
- "baseMultiplier": 75,
+ "iq": 80,
"basePower": 45
},
{
@@ -427,7 +427,7 @@
"origin": "Venzenulon 7",
"location": "Venzenulon 7",
"avatarId": 33,
- "baseMultiplier": 40,
+ "iq": 55,
"basePower": 20
},
{
@@ -440,7 +440,7 @@
"origin": "unknown",
"location": "Interdimensional Cable",
"avatarId": 34,
- "baseMultiplier": 120,
+ "iq": 145,
"basePower": 60
},
{
@@ -453,7 +453,7 @@
"origin": "Bepis 9",
"location": "Bepis 9",
"avatarId": 35,
- "baseMultiplier": 85,
+ "iq": 75,
"basePower": 35
},
{
@@ -466,7 +466,7 @@
"origin": "unknown",
"location": "unknown",
"avatarId": 36,
- "baseMultiplier": 1500,
+ "iq": 320,
"basePower": 200
},
{
@@ -479,7 +479,7 @@
"origin": "Earth (C-500A)",
"location": "Earth (C-500A)",
"avatarId": 37,
- "baseMultiplier": 55,
+ "iq": 158,
"basePower": 20
},
{
@@ -492,7 +492,7 @@
"origin": "Earth (C-137)",
"location": "Earth (C-137)",
"avatarId": 38,
- "baseMultiplier": 50,
+ "iq": 160,
"basePower": 15
},
{
@@ -505,7 +505,7 @@
"origin": "Earth (Evil Rick's Target Dimension)",
"location": "Earth (Evil Rick's Target Dimension)",
"avatarId": 39,
- "baseMultiplier": 50,
+ "iq": 150,
"basePower": 15
},
{
@@ -518,7 +518,7 @@
"origin": "Nuptia 4",
"location": "Nuptia 4",
"avatarId": 40,
- "baseMultiplier": 400,
+ "iq": 190,
"basePower": 250
}
]
diff --git a/src/pages/Collection.tsx b/src/pages/Collection.tsx
index 9ef4d18..113fea9 100644
--- a/src/pages/Collection.tsx
+++ b/src/pages/Collection.tsx
@@ -321,9 +321,16 @@ const Collection = () => {
-
- {isDiscovered ? char.name : "???"}
-
+
+
+ {isDiscovered ? char.name : "???"}
+
+ {isDiscovered && (
+
+ IQ {char.iq}
+
+ )}
+
{/* Rarity Indicators */}
diff --git a/src/store/cardUtils.ts b/src/store/cardUtils.ts
index 1b4631d..8acffbd 100644
--- a/src/store/cardUtils.ts
+++ b/src/store/cardUtils.ts
@@ -76,12 +76,17 @@ export const resolveCardStats = (card: GameCard) => {
return acc * (type?.multiplier || 1);
}, 1);
- const baseIncome =
- card.income !== undefined ? card.income : character.baseMultiplier;
const basePower = card.power !== undefined ? card.power : character.basePower;
+ // New formula: (iq^1.5) * rarityMultiplier / 10
+ const calculatedIncome = Math.floor(
+ Math.pow(character.iq, 1.5) * combinedMultiplier * 0.1,
+ );
+
+ const income = card.income !== undefined ? card.income : calculatedIncome;
+
return {
- income: Math.floor(baseIncome * combinedMultiplier),
+ income,
power: Math.floor(basePower * combinedMultiplier),
character,
};
diff --git a/src/types/game.ts b/src/types/game.ts
index 718db65..5847cea 100644
--- a/src/types/game.ts
+++ b/src/types/game.ts
@@ -16,7 +16,7 @@ export interface Character {
origin: string;
location: string;
avatarId: number;
- baseMultiplier: number;
+ iq: number;
basePower: number;
customImage?: string;
}
From b4b341cd0e8b2d1f2f9cefcb99df17ece1be532f Mon Sep 17 00:00:00 2001
From: Jakub Buciuto <46843555+MrJacob12@users.noreply.github.com>
Date: Wed, 18 Mar 2026 13:09:48 +0000
Subject: [PATCH 08/11] fix: ensure dimensional rift inventory rewards are
one-time milestones
---
src/pages/Dimension.tsx | 4 ++--
src/store/slices/dimensionSlice.ts | 20 ++++++++++++--------
2 files changed, 14 insertions(+), 10 deletions(-)
diff --git a/src/pages/Dimension.tsx b/src/pages/Dimension.tsx
index 2df3470..e476297 100644
--- a/src/pages/Dimension.tsx
+++ b/src/pages/Dimension.tsx
@@ -140,12 +140,12 @@ const Dimension = () => {
if (milestoneUnlocked) {
toast.success(`MILESTONE REACHED!`, {
- description: `You unlocked: ${milestoneUnlocked}! +${inventoryBonus} Card Slots!`,
+ description: `You unlocked: ${milestoneUnlocked}!${inventoryBonus > 0 ? ` +${inventoryBonus} Card Slots!` : ""}`,
duration: 5000,
});
} else if (bonus > 0) {
toast.success(`Victory! Level ${dimensionLevel} cleared.`, {
- description: `Bonus: +${formatCurrency(bonus)} Mega Seeds & +${inventoryBonus} Card Slots!`,
+ description: `Bonus: +${formatCurrency(bonus)} Mega Seeds${inventoryBonus > 0 ? ` & +${inventoryBonus} Card Slots!` : ""}`,
});
} else {
toast.success(`Victory! Level ${dimensionLevel} cleared.`);
diff --git a/src/store/slices/dimensionSlice.ts b/src/store/slices/dimensionSlice.ts
index adcc460..0707ac9 100644
--- a/src/store/slices/dimensionSlice.ts
+++ b/src/store/slices/dimensionSlice.ts
@@ -51,17 +51,22 @@ export const createDimensionSlice: StateCreator<
},
nextDimensionLevel: () => {
- const { dimensionLevel } = get();
+ const { dimensionLevel, maxDimensionLevel } = get();
let bonus = 0;
let inventoryBonus = 0;
let milestoneUnlocked = null;
- if ((dimensionLevel + 1) % GAME_CONFIG.DIMENSIONS.BONUS_STEP === 0) {
- bonus = (dimensionLevel + 1) * GAME_CONFIG.DIMENSIONS.BONUS_AMOUNT;
- inventoryBonus = GAME_CONFIG.DIMENSIONS.INVENTORY_BONUS_PER_STEP;
+ const nextLvl = dimensionLevel + 1;
+ const isNewMax = nextLvl > maxDimensionLevel;
+
+ if (nextLvl % GAME_CONFIG.DIMENSIONS.BONUS_STEP === 0) {
+ bonus = nextLvl * GAME_CONFIG.DIMENSIONS.BONUS_AMOUNT;
+ // Only grant permanent inventory bonus if this is the first time reaching this milestone
+ if (isNewMax) {
+ inventoryBonus = GAME_CONFIG.DIMENSIONS.INVENTORY_BONUS_PER_STEP;
+ }
}
- const nextLvl = dimensionLevel + 1;
milestoneUnlocked = GAME_CONFIG.DIMENSIONS.MILESTONES[nextLvl] || null;
const newEnemy = generateCard(
@@ -75,8 +80,7 @@ export const createDimensionSlice: StateCreator<
newEnemy.income = stats.income;
set((s) => {
- const nextLevel = s.dimensionLevel + 1;
- const newMax = Math.max(s.maxDimensionLevel, nextLevel);
+ const newMax = Math.max(s.maxDimensionLevel, nextLvl);
const newDimensionBonus = s.dimensionInventoryBonus + inventoryBonus;
const upgradeBonus = s.upgrades.inventory * GAME_CONFIG.UPGRADES.inventory.BONUS_PER_LEVEL;
@@ -86,7 +90,7 @@ export const createDimensionSlice: StateCreator<
seeds: s.seeds + bonus,
dimensionInventoryBonus: newDimensionBonus,
maxInventory: newMaxInventory,
- dimensionLevel: nextLevel,
+ dimensionLevel: nextLvl,
maxDimensionLevel: newMax,
currentEnemy: newEnemy,
};
From 1404071728f10e07ca5efd9df190795f03c12bce Mon Sep 17 00:00:00 2001
From: Jakub Buciuto <46843555+MrJacob12@users.noreply.github.com>
Date: Wed, 18 Mar 2026 13:16:41 +0000
Subject: [PATCH 09/11] feat: add 'Sort by IQ' option to collection filters
---
src/pages/Collection.tsx | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/pages/Collection.tsx b/src/pages/Collection.tsx
index 113fea9..bd7ea5f 100644
--- a/src/pages/Collection.tsx
+++ b/src/pages/Collection.tsx
@@ -64,6 +64,7 @@ const Collection = () => {
if (sortBy === "newest") return b.card.timestamp - a.card.timestamp;
if (sortBy === "oldest") return a.card.timestamp - b.card.timestamp;
if (sortBy === "income") return b.stats.income - a.stats.income;
+ if (sortBy === "iq") return b.stats.character.iq - a.stats.character.iq;
return 0;
});
}, [inventory, search, typeFilter, sortBy]);
@@ -228,6 +229,7 @@ const Collection = () => {
Newest First
Oldest First
Highest Income
+
Highest IQ
>
From 10b36d964f373d8818056f10551de06292eee6ec Mon Sep 17 00:00:00 2001
From: Jakub Buciuto <46843555+MrJacob12@users.noreply.github.com>
Date: Wed, 18 Mar 2026 13:34:06 +0000
Subject: [PATCH 10/11] fix: Card sorting, improve selling mode
---
src/pages/Collection.tsx | 37 +++++++++++++++++++++++++++++--------
1 file changed, 29 insertions(+), 8 deletions(-)
diff --git a/src/pages/Collection.tsx b/src/pages/Collection.tsx
index bd7ea5f..dfc568b 100644
--- a/src/pages/Collection.tsx
+++ b/src/pages/Collection.tsx
@@ -52,7 +52,7 @@ const Collection = () => {
return inventory
.filter(Boolean)
.map((card) => ({ card, stats: resolveCardStats(card) }))
- .filter(({ stats }) => {
+ .filter(({ card, stats }) => {
const matchesSearch = stats.character.name
.toLowerCase()
.includes(search.toLowerCase());
@@ -65,6 +65,7 @@ const Collection = () => {
if (sortBy === "oldest") return a.card.timestamp - b.card.timestamp;
if (sortBy === "income") return b.stats.income - a.stats.income;
if (sortBy === "iq") return b.stats.character.iq - a.stats.character.iq;
+ if (sortBy === "power") return b.stats.power - a.stats.power;
return 0;
});
}, [inventory, search, typeFilter, sortBy]);
@@ -230,6 +231,7 @@ const Collection = () => {
Oldest First
Highest Income
Highest IQ
+
Highest Power
>
@@ -370,19 +372,38 @@ const Collection = () => {
{/* Sell Mode Floating Bar */}
{isSellMode && activeTab === 'inventory' && (
-
-
-
+
+
+
{selectedIds.length} Selected
Total: {Math.floor(selectedTotalProfit).toLocaleString()} Seeds
-
+
+