diff --git a/indexer/env-goerli b/indexer/env-goerli index 252c34f71..97e29d3fc 100644 --- a/indexer/env-goerli +++ b/indexer/env-goerli @@ -1,3 +1,3 @@ -GAME="0x06be55931ec7e63fe429d677677b03302f724b966dde8241cb08c2da76ed5895" +GAME="0x055b3d17ac13c34e758e58c522724edab81163abe4ca1a5b2acc06b258e5d783" START=873000 MONGO_CONNECTION_STRING="mongodb://mongo:mongo@indexer-mongo-1:27017" diff --git a/indexer/src/adventurers.ts b/indexer/src/adventurers.ts index a8713417f..0f483dd81 100644 --- a/indexer/src/adventurers.ts +++ b/indexer/src/adventurers.ts @@ -99,7 +99,6 @@ export default function transform({ header, events }: Block) { case START_GAME: { console.log("START_GAME", "->", "ADVENTURER UPDATES"); const { value } = parseStartGame(event.data, 0); - console.log(value); const as = value.adventurerState; const am = value.adventurerMeta; return [ diff --git a/ui/src/app/api/api.ts b/ui/src/app/api/api.ts index 30590610a..79366c955 100644 --- a/ui/src/app/api/api.ts +++ b/ui/src/app/api/api.ts @@ -1,9 +1,33 @@ -import { useAccount } from "@starknet-react/core"; +import { getRPCUrl } from "../lib/constants"; interface MintEthProps { address: string; } +export const getBlock = async (blockNumber: number) => { + const rpcUrl = getRPCUrl(); + try { + const requestBody = { + jsonrpc: "2.0", + method: "starknet_getBlockWithTxHashes", + params: [{ block_number: blockNumber }], + id: 1, + }; + const response = await fetch(rpcUrl, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(requestBody), + }); + + const data = await response.json(); + return data.result; + } catch (error) { + console.error("Error posting data:", error); + } +}; + export const mintEth = async ({ address }: MintEthProps) => { try { const requestBody = { diff --git a/ui/src/app/components/ArcadeDialog.tsx b/ui/src/app/components/ArcadeDialog.tsx index 0ed9d4d93..9b815a260 100644 --- a/ui/src/app/components/ArcadeDialog.tsx +++ b/ui/src/app/components/ArcadeDialog.tsx @@ -109,7 +109,6 @@ export const ArcadeDialog = () => { const getAccountBalances = async (account: string) => { const balances = await fetchBalanceWithRetry(account); - console.log(balances); setArcadeBalances({ ...arcadebalances, [account]: { diff --git a/ui/src/app/components/CountDown.tsx b/ui/src/app/components/CountDown.tsx index 56ebdeabd..ab14e4ea0 100644 --- a/ui/src/app/components/CountDown.tsx +++ b/ui/src/app/components/CountDown.tsx @@ -7,9 +7,9 @@ const formatTime = (totalSeconds: number) => { const hours = Math.floor(totalSeconds / 3600); const minutes = Math.floor((totalSeconds - hours * 3600) / 60); const seconds = totalSeconds % 60; - return `${hours.toString().padStart(2, "0")}:${minutes + return `${minutes.toString().padStart(2, "0")}:${seconds .toString() - .padStart(2, "0")}:${seconds.toString().padStart(2, "0")}`; + .padStart(2, "0")}`; }; export const HealthCountDown = ({ health }: any) => { @@ -100,10 +100,14 @@ export const PenaltyCountDown = ({ }; export interface EntropyCountDownProps { - targetTime: number; + targetTime: number | null; + countDownExpired: () => void; } -export const EntropyCountDown = ({ targetTime }: EntropyCountDownProps) => { +export const EntropyCountDown = ({ + targetTime, + countDownExpired, +}: EntropyCountDownProps) => { const [seconds, setSeconds] = useState(0); useEffect(() => { if (targetTime) { @@ -111,6 +115,12 @@ export const EntropyCountDown = ({ targetTime }: EntropyCountDownProps) => { const currentTime = new Date().getTime(); const timeRemaining = targetTime - currentTime; setSeconds(Math.floor(timeRemaining / 1000)); + + if (timeRemaining <= 0) { + countDownExpired(); // Call the countDownExpired function when countdown expires + } else { + setSeconds(Math.floor(timeRemaining / 1000)); + } }; updateCountdown(); @@ -122,12 +132,27 @@ export const EntropyCountDown = ({ targetTime }: EntropyCountDownProps) => { } }, [targetTime]); return ( -
- {seconds > 0 && ( - -

{formatTime(seconds)}

-
- )} +
+ + {targetTime ? ( + <> +

Session Starts in

+

+ {seconds === 0 ? "GO" : formatTime(seconds)} +

+ + ) : ( +

+ Loading +

+ )} +
); }; diff --git a/ui/src/app/components/adventurer/InventoryDisplay.tsx b/ui/src/app/components/adventurer/InventoryDisplay.tsx index 01ed410b6..9a24f4dbb 100644 --- a/ui/src/app/components/adventurer/InventoryDisplay.tsx +++ b/ui/src/app/components/adventurer/InventoryDisplay.tsx @@ -77,7 +77,6 @@ export const InventoryCard = ({ const handleEquipItems = (item: string) => { const formattedNewEquipItems = handleCheckSameSlot(slot, equipItems); - console.log(equipItems); const newEquipItems = [ ...formattedNewEquipItems, getKeyFromValue(gameData.ITEMS, item) ?? "", diff --git a/ui/src/app/components/animations/RowLoadre.tsx b/ui/src/app/components/animations/RowLoadre.tsx new file mode 100644 index 000000000..f3b62137a --- /dev/null +++ b/ui/src/app/components/animations/RowLoadre.tsx @@ -0,0 +1,40 @@ +import React, { useState, useEffect } from "react"; + +const RowLoader: React.FC = () => { + const cols = 6; // Change to 6 columns + const [loaderData, setLoaderData] = useState(Array(cols).fill(-1)); + + useEffect(() => { + const row = loaderData; + const randomStartX = Math.floor(Math.random() * cols); + + row[randomStartX] = 0; // Set the initial square to 0 + + let order = 0; + for (let i = 0; i < cols; i++) { + row[i] = order++; + } + + setLoaderData([...row]); + }, []); + + return ( +
+
+ {loaderData.map((order, colIndex) => ( +
+ ))} +
+
+ ); +}; + +export default RowLoader; diff --git a/ui/src/app/components/interlude/Hints.tsx b/ui/src/app/components/interlude/Hints.tsx new file mode 100644 index 000000000..899c462c6 --- /dev/null +++ b/ui/src/app/components/interlude/Hints.tsx @@ -0,0 +1,43 @@ +import { useEffect, useState } from "react"; +import { ActionsTutorial } from "../tutorial/ActionsTutorial"; +import { AdventurerTutorial } from "../tutorial/AdventurerTutorial"; +import { BeastTutorial } from "../tutorial/BeastTutorial"; +import { UpgradeTutorial } from "../tutorial/UpgradeTutorial"; +import RowLoader from "../animations/RowLoadre"; + +export default function Hints() { + const [currentIndex, setCurrentIndex] = useState(0); + const tutorials = [ + , + , + , + , + ]; + useEffect(() => { + if (currentIndex < tutorials.length - 1) { + const timer = setTimeout(() => { + setCurrentIndex((prev) => prev + 1); + }, 10000); + return () => { + clearTimeout(timer); + }; + } else if (currentIndex === tutorials.length - 1) { + const timer = setTimeout(() => { + setCurrentIndex(0); + }, 10000); + return () => clearTimeout(timer); + } + }, [currentIndex]); + + return ( +
+

Hints

+
+
+ +
+
{tutorials[currentIndex]}
+
+
+ ); +} diff --git a/ui/src/app/components/interlude/Lobby.tsx b/ui/src/app/components/interlude/Lobby.tsx index 7b57deafe..8f66ae480 100644 --- a/ui/src/app/components/interlude/Lobby.tsx +++ b/ui/src/app/components/interlude/Lobby.tsx @@ -64,7 +64,11 @@ export default function Lobby() { - + + + + + @@ -74,6 +78,7 @@ export default function Lobby() { key={index} adventurer={adventurer} handleRowSelected={handleRowSelected} + currentBlock={blockData?.block_number!} /> ) )} diff --git a/ui/src/app/components/interlude/LobbyRow.tsx b/ui/src/app/components/interlude/LobbyRow.tsx index e6679e001..37893613e 100644 --- a/ui/src/app/components/interlude/LobbyRow.tsx +++ b/ui/src/app/components/interlude/LobbyRow.tsx @@ -6,9 +6,14 @@ import { soundSelector } from "@/app/hooks/useUiSound"; interface LobbyRowProps { adventurer: Adventurer; handleRowSelected: (id: number) => void; + currentBlock: number; } -const LobbyRow = ({ adventurer, handleRowSelected }: LobbyRowProps) => { +const LobbyRow = ({ + adventurer, + handleRowSelected, + currentBlock, +}: LobbyRowProps) => { const { play: clickPlay } = useUiSounds(soundSelector.click); const adventurersByOwner = useQueriesStore( (state) => state.data.adventurersByOwnerQuery?.adventurers ?? [] @@ -16,6 +21,7 @@ const LobbyRow = ({ adventurer, handleRowSelected }: LobbyRowProps) => { const ownedAdventurer = adventurersByOwner.some( (a) => a.id === adventurer.id ); + return ( { }} > + + + + ); }; diff --git a/ui/src/app/components/leaderboard/ScoreTable.tsx b/ui/src/app/components/leaderboard/ScoreTable.tsx index 7bdb15e9c..8408b93af 100644 --- a/ui/src/app/components/leaderboard/ScoreTable.tsx +++ b/ui/src/app/components/leaderboard/ScoreTable.tsx @@ -32,8 +32,6 @@ const ScoreLeaderboardTable = ({ const scoreIds = scores?.map((score) => score.id ?? 0); - console.log(scoreIds); - const scoresData = useCustomQuery("topScoresQuery", getScoresInList, { ids: scoreIds, }); diff --git a/ui/src/app/components/tutorial/ActionsTutorial.tsx b/ui/src/app/components/tutorial/ActionsTutorial.tsx index e20cd57df..6c3001fdf 100644 --- a/ui/src/app/components/tutorial/ActionsTutorial.tsx +++ b/ui/src/app/components/tutorial/ActionsTutorial.tsx @@ -1,6 +1,6 @@ export const ActionsTutorial = () => { return ( -
+

Explore

During your exploration, you will encounter a number of things. These diff --git a/ui/src/app/components/tutorial/AdventurerTutorial.tsx b/ui/src/app/components/tutorial/AdventurerTutorial.tsx index 5e3539ffe..ab7a56126 100644 --- a/ui/src/app/components/tutorial/AdventurerTutorial.tsx +++ b/ui/src/app/components/tutorial/AdventurerTutorial.tsx @@ -1,6 +1,6 @@ export const AdventurerTutorial = () => { return ( -

+

Welcome to Loot Survivor!

{/*

Create an Adventurer

*/} diff --git a/ui/src/app/components/tutorial/BeastTutorial.tsx b/ui/src/app/components/tutorial/BeastTutorial.tsx index 23295c19b..c919eb16d 100644 --- a/ui/src/app/components/tutorial/BeastTutorial.tsx +++ b/ui/src/app/components/tutorial/BeastTutorial.tsx @@ -1,6 +1,6 @@ export const BeastTutorial = () => { return ( -
+

Beast

Oh dear! You've stumbled upon a beast! A potential ambush lurks. diff --git a/ui/src/app/components/tutorial/EfficaciesTutorial.tsx b/ui/src/app/components/tutorial/EfficaciesTutorial.tsx new file mode 100644 index 000000000..e69de29bb diff --git a/ui/src/app/components/tutorial/UpgradeTutorial.tsx b/ui/src/app/components/tutorial/UpgradeTutorial.tsx index cdc1b6a73..6e8ceea52 100644 --- a/ui/src/app/components/tutorial/UpgradeTutorial.tsx +++ b/ui/src/app/components/tutorial/UpgradeTutorial.tsx @@ -1,6 +1,6 @@ export const UpgradeTutorial = () => { return ( -

+

Level Up

Health Potions

diff --git a/ui/src/app/containers/AdventurerScreen.tsx b/ui/src/app/containers/AdventurerScreen.tsx index 6578468ef..55aeaec3a 100644 --- a/ui/src/app/containers/AdventurerScreen.tsx +++ b/ui/src/app/containers/AdventurerScreen.tsx @@ -33,7 +33,6 @@ export default function AdventurerScreen({ const adventurers = useQueriesStore( (state) => state.data.adventurersByOwnerQuery?.adventurers || [] ); - console.log(adventurers); const resetData = useQueriesStore((state) => state.resetData); const startOption = useUIStore((state) => state.startOption); const setStartOption = useUIStore((state) => state.setStartOption); diff --git a/ui/src/app/containers/BeastScreen.tsx b/ui/src/app/containers/BeastScreen.tsx index c477a1d1f..c0bef31cd 100644 --- a/ui/src/app/containers/BeastScreen.tsx +++ b/ui/src/app/containers/BeastScreen.tsx @@ -12,6 +12,8 @@ import useUIStore from "../hooks/useUIStore"; import useCustomQuery from "../hooks/useCustomQuery"; import { getLastestEntropy } from "../hooks/graphql/queries"; import InterludeScreen from "./InterludeScreen"; +import { getBlock } from "../api/api"; +import { useBlock } from "@starknet-react/core"; interface BeastScreenProps { attack: (...args: any[]) => any; @@ -23,7 +25,6 @@ interface BeastScreenProps { * @description Provides the beast screen for adventurer battles. */ export default function BeastScreen({ attack, flee }: BeastScreenProps) { - const [nextEntropyTime, setNextEntropyTime] = useState(0); const adventurer = useAdventurerStore((state) => state.adventurer); const loading = useLoadingStore((state) => state.loading); const estimatingFee = useUIStore((state) => state.estimatingFee); @@ -39,6 +40,10 @@ export default function BeastScreen({ attack, flee }: BeastScreenProps) { (state) => state.data.battlesByBeastQuery?.battles || [] ); + const { data: blockData } = useBlock({ + refetchInterval: false, + }); + const [buttonText, setButtonText] = useState("Flee"); const handleMouseEnter = () => { @@ -148,38 +153,15 @@ export default function BeastScreen({ attack, flee }: BeastScreenProps) {

); - const latestEntropyData = useCustomQuery( - "latestEntropyQuery", - getLastestEntropy, - undefined - ); - - useEffect(() => { - if (latestEntropyData) { - setData("latestEntropyQuery", latestEntropyData); - const nextEntropyRotationBlock = adventurer?.revealBlock!; - const adventurerStartBlock = adventurer?.startBlock!; - const blockDifference = nextEntropyRotationBlock - adventurerStartBlock; - const blocksPerHour = latestEntropyData.entropy[0].blocksPerHour; - const blocksPerMillisecond = blocksPerHour / (60 * 60 * 1000); - const secondsUntilNextEntropy = blockDifference / blocksPerMillisecond; - const adventurerCreatedTime = new Date( - adventurer?.createdTime! - ).getTime(); - const nextEntropyTime = adventurerCreatedTime + secondsUntilNextEntropy; - setNextEntropyTime(nextEntropyTime); - } - }, [latestEntropyData]); - - const currentTime = new Date().getTime(); - if (showBattleLog) { return ; } return (
- + {blockData?.block_number! < adventurer?.revealBlock! && ( + + )}
{hasBeast ? ( diff --git a/ui/src/app/containers/InterludeScreen.tsx b/ui/src/app/containers/InterludeScreen.tsx index 76ace05e0..074b554b4 100644 --- a/ui/src/app/containers/InterludeScreen.tsx +++ b/ui/src/app/containers/InterludeScreen.tsx @@ -1,15 +1,62 @@ +import { useState, useEffect } from "react"; +import { useBlock } from "@starknet-react/core"; import { EntropyCountDown } from "../components/CountDown"; +import Hints from "../components/interlude/Hints"; +import { fetchAverageBlockTime } from "../lib/utils"; +import useAdventurerStore from "../hooks/useAdventurerStore"; -interface InterludeScreenProps { - nextEntropyTime: number; -} +export default function InterludeScreen() { + const { adventurer } = useAdventurerStore(); + const [fetchedAverageBlockTime, setFetchedAverageBlockTime] = useState(false); + const [averageBlockTime, setAverageBlockTime] = useState(0); + const [nextEntropyTime, setNextEntropyTime] = useState(null); + const [countDownExpired, setCountDownExpired] = useState(false); + + const { data: blockData } = useBlock({ + refetchInterval: false, + }); + + const fetchData = async () => { + const result = await fetchAverageBlockTime(blockData?.block_number!, 10); + setAverageBlockTime(result!); + setFetchedAverageBlockTime(true); + }; + + const getNextEntropyTime = () => { + const nextBlockHashBlock = adventurer?.revealBlock!; + const adventurerStartBlock = adventurer?.startBlock!; + const blockDifference = nextBlockHashBlock - adventurerStartBlock; + const secondsUntilNextEntropy = blockDifference * averageBlockTime; + const adventurerCreatedTime = new Date(adventurer?.createdTime!).getTime(); + const nextEntropyTime = + adventurerCreatedTime + secondsUntilNextEntropy * 1000; + setNextEntropyTime(nextEntropyTime); + }; + + useEffect(() => { + if (fetchedAverageBlockTime) { + getNextEntropyTime(); + } else { + fetchData(); + } + }, [fetchedAverageBlockTime]); -export default function InterludeScreen({ - nextEntropyTime, -}: InterludeScreenProps) { return ( -
- -
+ <> + {!countDownExpired && ( + <> +
+
+ setCountDownExpired(true)} + /> +
+ +
+
+ + )} + ); } diff --git a/ui/src/app/lib/constants.ts b/ui/src/app/lib/constants.ts index 8d0dea133..f5faefbe3 100644 --- a/ui/src/app/lib/constants.ts +++ b/ui/src/app/lib/constants.ts @@ -39,7 +39,7 @@ export function getContracts() { case "goerli": return { eth: "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7", - game: "0x06be55931ec7e63fe429d677677b03302f724b966dde8241cb08c2da76ed5895", + game: "0x055b3d17ac13c34e758e58c522724edab81163abe4ca1a5b2acc06b258e5d783", lords: "0x05e367ac160e5f90c5775089b582dfc987dd148a5a2f977c49def2a6644f724b", }; diff --git a/ui/src/app/lib/utils/index.ts b/ui/src/app/lib/utils/index.ts index 14e735fc5..0a64e2b16 100644 --- a/ui/src/app/lib/utils/index.ts +++ b/ui/src/app/lib/utils/index.ts @@ -13,6 +13,7 @@ import { import { z } from "zod"; import { deathMessages, FEE_CHECK_BALANCE } from "../constants"; import { Call, AccountInterface, Account } from "starknet"; +import { getBlock } from "@/app/api/api"; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); @@ -402,3 +403,24 @@ export async function checkArcadeBalance( return false; } } + +export const fetchAverageBlockTime = async ( + currentBlock: number, + numberOfBlocks: number +) => { + try { + let totalTimeInterval = 0; + + for (let i = currentBlock - numberOfBlocks; i < currentBlock; i++) { + const currentBlockData = await getBlock(i); + const nextBlockData = await getBlock(i + 1); + + const timeInterval = nextBlockData.timestamp - currentBlockData.timestamp; + totalTimeInterval += timeInterval; + } + const averageTime = totalTimeInterval / numberOfBlocks; + return averageTime; + } catch (error) { + console.error("Error:", error); + } +}; diff --git a/ui/src/app/lib/utils/parseEvents.ts b/ui/src/app/lib/utils/parseEvents.ts index 5729826f1..6120e6fa5 100644 --- a/ui/src/app/lib/utils/parseEvents.ts +++ b/ui/src/app/lib/utils/parseEvents.ts @@ -236,7 +236,6 @@ export async function parseEvents( switch (eventName) { case "StartGame": - console.log(raw.data); const startGameData: StartGameEvent = { adventurerState: parseAdventurerState(raw.data.slice(0, 40)), adventurerMeta: { diff --git a/ui/src/app/lib/utils/syscalls.ts b/ui/src/app/lib/utils/syscalls.ts index bd6abc5db..db3bf0d67 100644 --- a/ui/src/app/lib/utils/syscalls.ts +++ b/ui/src/app/lib/utils/syscalls.ts @@ -256,13 +256,12 @@ export function syscalls({ undefined, "StartGame" ); - console.log(startGameEvents[0]); const events = await parseEvents( receipt as InvokeTransactionReceiptResponse, { name: formData["name"], - startBlock: startGameEvents[0].data.startBlock, - revealBlock: startGameEvents[0].data.revealBlock, + startBlock: startGameEvents[0].data[0].startBlock, + revealBlock: startGameEvents[0].data[0].revealBlock, createdTime: new Date(), } );
AdventurerNameAccountDeath TollBest RunBlocks Left
{`${adventurer.name} - ${adventurer.id}`}{adventurer.owner}10100 XP{adventurer?.revealBlock! - currentBlock}