1
0
forked from mico/idle_moloch

Compare commits

...

13 Commits
main ... main

Author SHA1 Message Date
cc9cb65e4e
Fixes TS interface on Dashboard
Some checks failed
CI / Foundry project (push) Has been cancelled
2024-11-06 23:23:03 +01:00
88a4400307 Merge pull request 'add metrics dashboard' (#20) from metrics into main
Some checks are pending
CI / Foundry project (push) Waiting to run
Reviewed-on: mico/idle_moloch#20
2024-11-06 22:19:30 +00:00
e7aa29fab7
Adds metrics button to the screen
Some checks failed
CI / Foundry project (push) Has been cancelled
CI / Foundry project (pull_request) Has been cancelled
2024-11-06 23:18:35 +01:00
san
edbee4eb0c add metrics dashboard
Some checks failed
CI / Foundry project (push) Waiting to run
CI / Foundry project (pull_request) Has been cancelled
2024-11-06 21:34:24 +05:30
b5dbe91e8a Merge pull request 'soundEffects' (#19) from soundEffects into main
Some checks are pending
CI / Foundry project (push) Waiting to run
Reviewed-on: mico/idle_moloch#19
2024-11-03 22:52:39 +00:00
8e10b9e585
Whoops deleted chainlink lib
Some checks failed
CI / Foundry project (push) Has been cancelled
CI / Foundry project (pull_request) Has been cancelled
2024-11-03 23:51:44 +01:00
711e092e5b
Prevents sounds playing twice on boss modal, lowered the volume oflosing sound just a bit 2024-11-03 23:51:00 +01:00
404a23b2e1 clickSound in app
Some checks failed
CI / Foundry project (push) Waiting to run
CI / Foundry project (pull_request) Has been cancelled
2024-11-03 14:00:37 +01:00
bd0cc6c34d win and lose sounds based on boss outcome
Some checks are pending
CI / Foundry project (push) Waiting to run
2024-11-03 13:12:30 +01:00
91185efa18 win and lose sounds based on boss outcome
Some checks are pending
CI / Foundry project (push) Waiting to run
2024-11-03 13:11:08 +01:00
a5fd3d9e21
Made it so mainnet is selected as primary network
Some checks failed
CI / Foundry project (push) Has been cancelled
2024-11-02 13:02:02 +01:00
e0b99b9d39
Merge branch 'main' of git.mic0.dev:mico/idle_moloch
Some checks are pending
CI / Foundry project (push) Waiting to run
2024-11-02 12:28:31 +01:00
32aaf80c17
Solves the issue with spamming requests 2024-11-02 12:28:19 +01:00
17 changed files with 660 additions and 178 deletions

15
app/package-lock.json generated
View File

@ -11,6 +11,8 @@
"@next/eslint-plugin-next": "^14.2.15",
"@rainbow-me/rainbowkit": "^2.2.0",
"@tanstack/react-query": "^5.55.3",
"howler": "^2.2.4",
"jsfxr": "^1.2.2",
"next": "^14.2.10",
"react": "^18.3.1",
"react-dom": "^18.3.1",
@ -8974,6 +8976,11 @@
"minimalistic-crypto-utils": "^1.0.1"
}
},
"node_modules/howler": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/howler/-/howler-2.2.4.tgz",
"integrity": "sha512-iARIBPgcQrwtEr+tALF+rapJ8qSc+Set2GJQl7xT1MQzWaVkFebdJhR3alVlSiUf5U7nAANKuj3aWpwerocD5w=="
},
"node_modules/http-errors": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
@ -10117,6 +10124,14 @@
"node": ">=6"
}
},
"node_modules/jsfxr": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/jsfxr/-/jsfxr-1.2.2.tgz",
"integrity": "sha512-aBtNHZ/eJVZ3Q12HLj6F0eF20bRJTar6fjHf14zZ/Co5GzcVsEBujJO7IKwAhZS3Pe0xIvUOD3O1BoZ6ij0xhA==",
"bin": {
"sfxr-to-wav": "sfxr-to-wav"
}
},
"node_modules/json-buffer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",

View File

@ -12,6 +12,8 @@
"@next/eslint-plugin-next": "^14.2.15",
"@rainbow-me/rainbowkit": "^2.2.0",
"@tanstack/react-query": "^5.55.3",
"howler": "^2.2.4",
"jsfxr": "^1.2.2",
"next": "^14.2.10",
"react": "^18.3.1",
"react-dom": "^18.3.1",

Binary file not shown.

BIN
app/public/sounds/lost.wav Normal file

Binary file not shown.

View File

@ -4,30 +4,67 @@ import styles from "../styles/Modal.module.css";
import bgStyles from "../styles/Background.module.css";
import { bossToName, bossToReward } from "./BossInfo";
import { bossLevelToClass } from "./Boss";
import { lostSound, wonSound } from "../utils/soundsEffect";
import { useEffect } from "react";
interface BossOutcomeModalProps {
setIsOpen: (val: boolean) => void,
setIsOpen: (val: boolean) => void;
}
const BossOutcomeModal = ({ setIsOpen }: BossOutcomeModalProps) => {
const { lastBossResult } = usePlayer();
const outcome = lastBossResult?.reward != BigInt(0);
const ascended = lastBossResult?.prestigeGained;
useEffect(() => {
if (lastBossResult != null) {
if (outcome) {
wonSound();
} else {
lostSound();
}
}
}, [outcome, lastBossResult])
if (lastBossResult == null) return null;
const outcome = lastBossResult.reward != BigInt(0);
const ascended = lastBossResult.prestigeGained;
const text = outcome ? <span>and you <strong className={styles.won}>won!</strong> 🤩</span> : <span>and you <strong className={styles.lost}>lost</strong> 😔</span>;
const rewardAmount = parseFloat(parseFloat(formatUnits(bossToReward[lastBossResult.level], 18).toString()).toFixed(4));
const rewardText =
ascended ? <p>You won <strong>{rewardAmount} RGCVII</strong> and <strong>ASCENDED!!!</strong>. This means you beat the bosses and gained a <strong>Prestige level</strong>. Your GELD is now forfeit, but your legend lives on.</p>
: outcome ? <p>You won <strong>{rewardAmount} RGCVII</strong></p>
: <p>Your GELD is now forfeit.<br />Try again 💪 we know you can do it!</p>
const text = outcome ? (
<span>
and you <strong className={styles.won}>won!</strong> 🤩
</span>
) : (
<span>
and you <strong className={styles.lost}>lost</strong> 😔
</span>
);
const rewardAmount = parseFloat(
parseFloat(
formatUnits(bossToReward[lastBossResult.level], 18).toString()
).toFixed(4)
);
const rewardText = ascended ? (
<p>
You won <strong>{rewardAmount} RGCVII</strong> and{" "}
<strong>ASCENDED!!!</strong>. This means you beat the bosses and gained a{" "}
<strong>Prestige level</strong>. Your GELD is now forfeit, but your legend
lives on.
</p>
) : outcome ? (
<p>
You won <strong>{rewardAmount} RGCVII</strong>
</p>
) : (
<p>
Your GELD is now forfeit.
<br />
Try again 💪 we know you can do it!
</p>
);
const bossName = bossToName[lastBossResult.variant];
const bossClass = bossLevelToClass[lastBossResult.variant];
return <div className={`${styles.modal} ${styles.bossModal}`}>
return (
<div className={`${styles.modal} ${styles.bossModal}`}>
<h2>You battled {bossName} Moloch!</h2>
<div className={`${bgStyles.boss} ${bossClass} ${styles.image}`} />
<p className={styles.outcome}>{text}</p>
@ -36,6 +73,7 @@ const BossOutcomeModal = ({ setIsOpen }: BossOutcomeModalProps) => {
<button onClick={() => setIsOpen(false)}>Onward!</button>
</div>
</div>
}
);
};
export default BossOutcomeModal
export default BossOutcomeModal;

View File

@ -0,0 +1,85 @@
import { useEffect, useState } from 'react';
import styles from '../styles/Dashboard.module.css';
interface MetricsData {
totalPlayers: number;
totalRuns: number;
activePlayers24h: number;
totalBossesDefeated: number;
totalPrestigeLevels: number;
}
const SUBGRAPH_URL = "https://api.studio.thegraph.com/query/75782/slay-the-moloch-base-mainnet/version/latest";
const Dashboard = () => {
const [metrics, setMetrics] = useState<MetricsData>();
useEffect(() => {
const fetchMetrics = async () => {
try {
const response = await fetch(SUBGRAPH_URL, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
query: `{
# Get global stats
globalStat(id: "1") {
totalPlayers
totalBossesDefeated
totalPrestigeLevels
totalRuns
}
# Get active players in last 24h
players(where: { lastRaidedAt_gt: "${Math.floor(Date.now() / 1000) - 86400}" }) {
id
}
}`
})
});
const data = await response.json();
setMetrics({
totalPlayers: parseInt(data.data.globalStat.totalPlayers),
totalRuns: parseInt(data.data.globalStat.totalRuns),
activePlayers24h: data.data.players.length,
totalBossesDefeated: parseInt(data.data.globalStat.totalBossesDefeated),
totalPrestigeLevels: parseInt(data.data.globalStat.totalPrestigeLevels)
});
} catch (error) {
console.error("Error fetching metrics:", error);
}
};
fetchMetrics();
const interval = setInterval(fetchMetrics, 30000); // Refresh every 30 seconds
return () => clearInterval(interval);
}, []);
return (
<div className={styles.dashboard}>
<div className={styles.metric}>
<h3>Total Players</h3>
<p>{metrics?.totalPlayers || 0}</p>
</div>
<div className={styles.metric}>
<h3>Total Game Runs</h3>
<p>{metrics?.totalRuns || 0}</p>
</div>
<div className={styles.metric}>
<h3>Active Players (24h)</h3>
<p>{metrics?.activePlayers24h || 0}</p>
</div>
<div className={styles.metric}>
<h3>Total Bosses Defeated</h3>
<p>{metrics?.totalBossesDefeated || 0}</p>
</div>
<div className={styles.metric}>
<h3>Total Prestige Levels</h3>
<p>{metrics?.totalPrestigeLevels || 0}</p>
</div>
</div>
);
};
export default Dashboard;

View File

@ -8,6 +8,7 @@ import Leaderboard from "./Leaderboard";
import { usePlayer } from "../providers/PlayerProvider";
import Boss from "./Boss";
import BossInfo from "./BossInfo";
import Link from "next/link";
const bossToMountainsClass = {
0: styles.mountains0,
@ -53,6 +54,7 @@ const Scene = () => {
>
🏆 <span className={styles.hideMobile}>Top players</span>
</button>
<Link href="/metrics" className={styles.metricsButton}>📈 <span className={styles.hideMobile}>Game metrics</span></Link>
{isLeaderboardOpen && (
<div className={styles.leaderboardOverlay}>
<div className={styles.leaderboardContent}>

View File

@ -8,8 +8,10 @@ import { RainbowKitProvider, midnightTheme } from "@rainbow-me/rainbowkit";
import { config } from "../wagmi";
import { Press_Start_2P, Texturina } from "next/font/google";
import PlayerProvider from "../providers/PlayerProvider";
import ModalProvider from '../providers/ModalProvider';
import ModalProvider from "../providers/ModalProvider";
import Script from "next/script";
import { useEffect } from "react";
import { clickSound } from "../utils/soundsEffect";
const client = new QueryClient();
const font = Texturina({ weight: ["400"], subsets: ["latin"] });
@ -18,6 +20,18 @@ const font = Texturina({ weight: ["400"], subsets: ["latin"] });
const fontPixel = Press_Start_2P({ weight: ["400"], subsets: ["latin"] });
function MyApp({ Component, pageProps }: AppProps) {
useEffect(() => {
window.addEventListener("click", () => {
clickSound();
});
return () => {
window.removeEventListener("click", () => {
clickSound();
});
};
}, []);
return (
<WagmiProvider config={config}>
<QueryClientProvider client={client}>
@ -46,6 +60,14 @@ function MyApp({ Component, pageProps }: AppProps) {
`}</style>
<PlayerProvider>
<ModalProvider>
<Script
src="https://sfxr.me/riffwave.js"
strategy="beforeInteractive"
/>
<Script
src="https://sfxr.me/sfxr.js"
strategy="beforeInteractive"
/>
<Component {...pageProps} />
</ModalProvider>
</PlayerProvider>

15
app/src/pages/metrics.tsx Normal file
View File

@ -0,0 +1,15 @@
import Dashboard from '../components/Dashboard';
import styles from '../styles/Metrics.module.css';
import Link from 'next/link';
const MetricsPage = () => {
return (
<div className={styles.metricsPage}>
<Link href="/" className={styles.backLink}> Back to game</Link>
<h1>Game Metrics</h1>
<Dashboard />
</div>
);
};
export default MetricsPage;

View File

@ -1,38 +1,55 @@
import React, { createContext, ReactNode, useCallback, useContext, useEffect, useRef, useState } from 'react'
import { useAccount, useReadContract, useWriteContract } from 'wagmi'
import contractAbi from "../../../out/RaidGeld.sol/RaidGeld.json"
import { Hash, parseEther } from 'viem'
import contracts from '../../contract_address'
import WaitingForTxModal from '../components/WaitingForTxModal'
import BossOutcomeModal from '../components/BossOutcomeModal'
import styles from "../styles/Background.module.css"
import React, {
createContext,
ReactNode,
useCallback,
useContext,
useEffect,
useRef,
useState,
} from "react";
import { useAccount, useReadContract, useWriteContract } from "wagmi";
import contractAbi from "../../../out/RaidGeld.sol/RaidGeld.json";
import { Hash, parseEther } from "viem";
import contracts from "../../contract_address";
import WaitingForTxModal from "../components/WaitingForTxModal";
import BossOutcomeModal from "../components/BossOutcomeModal";
import styles from "../styles/Background.module.css";
import { coinSound } from "../utils/soundsEffect";
const { contractAddress, daoTokenAddress } = contracts
const abi = contractAbi.abi
const { contractAddress, daoTokenAddress } = contracts;
const abi = contractAbi.abi;
export type UnitType = 0 | 1 | 2 | 3
export type BossLevel = 0 | 1 | 2 | 3 | 4 | 5 | 6
export type UnitType = 0 | 1 | 2 | 3;
export type BossLevel = 0 | 1 | 2 | 3 | 4 | 5 | 6;
export interface Player {
created_at: bigint,
last_raided_at: bigint,
total_minted: bigint
total_rewards: bigint,
n_runs: number,
prestige_level: number,
is_registered: boolean,
has_active_session: boolean,
created_at: bigint;
last_raided_at: bigint;
total_minted: bigint;
total_rewards: bigint;
n_runs: number;
prestige_level: number;
is_registered: boolean;
has_active_session: boolean;
}
export interface Army {
anointed: { level: number }
apprentice: { level: number }
champion: { level: number }
moloch_denier: { level: number }
profit_per_second: bigint
anointed: { level: number };
apprentice: { level: number };
champion: { level: number };
moloch_denier: { level: number };
profit_per_second: bigint;
}
export interface Boss {
level: BossLevel;
variants: [BossLevel, BossLevel, BossLevel, BossLevel, BossLevel, BossLevel, BossLevel]
variants: [
BossLevel,
BossLevel,
BossLevel,
BossLevel,
BossLevel,
BossLevel,
BossLevel
];
}
export interface LastBossResult {
@ -44,14 +61,14 @@ export interface LastBossResult {
}
export interface PlayerContextType {
isRegistered: boolean,
player: null | Player,
army: null | Army,
boss: null | Boss,
lastBossResult: null | LastBossResult,
balance: bigint,
register: (arg: "ETH" | "RGCVII") => void,
raid: () => void,
isRegistered: boolean;
player: null | Player;
army: null | Army;
boss: null | Boss;
lastBossResult: null | LastBossResult;
balance: bigint;
register: (arg: "ETH" | "RGCVII") => void;
raid: () => void;
battleWithBoss: () => void;
addUnit: (unit: UnitType, amount?: number) => void;
}
@ -66,166 +83,196 @@ const PlayerContext = createContext<PlayerContextType>({
register: () => {},
raid: () => {},
battleWithBoss: () => {},
addUnit: () => { }
addUnit: () => {},
});
const PlayerProvider = ({ children }: { children: ReactNode }) => {
const { address, isConnected } = useAccount();
const { writeContract, error } = useWriteContract();
const [[txHash, callbackFn], setHashAndCallback] = useState<[Hash | null, () => void]>([null, () => { }])
const [[txHash, callbackFn], setHashAndCallback] = useState<
[Hash | null, () => void]
>([null, () => {}]);
const [bossBattledModalOpen, setBossBattlesModalOpen] = useState(false);
const hasFetchedLastBossFirstTime = useRef(false);
useEffect(() => {
console.warn(error)
}, [error])
console.warn(error);
}, [error]);
const resetHashAndCallback = useCallback(() => {
setHashAndCallback([null, () => { }])
}, [])
setHashAndCallback([null, () => {}]);
}, []);
const { data: isRegistered } = useReadContract({
address: contractAddress,
abi,
functionName: 'isRegistered',
functionName: "isRegistered",
args: [address],
query: {
enabled: isConnected,
refetchInterval: 15,
}
refetchInterval: 10000,
},
});
const { data: balance, } = useReadContract({
const { data: balance } = useReadContract({
address: contractAddress,
abi,
functionName: 'balanceOf',
functionName: "balanceOf",
args: [address],
query: {
refetchInterval: 15,
enabled: isConnected
}
refetchInterval: 10000,
enabled: isConnected,
},
});
const { data: player } = useReadContract({
address: contractAddress,
abi,
functionName: 'getPlayer',
functionName: "getPlayer",
args: [address],
query: {
enabled: isConnected,
refetchInterval: 15
}
refetchInterval: 10000,
},
});
const { data: army } = useReadContract({
address: contractAddress,
abi,
functionName: 'getArmy',
functionName: "getArmy",
args: [address],
query: {
enabled: isConnected,
refetchInterval: 15
}
refetchInterval: 10000,
},
});
const { data: boss } = useReadContract({
address: contractAddress,
abi,
functionName: 'getBoss',
functionName: "getBoss",
args: [address],
query: {
enabled: isConnected,
refetchInterval: 15
}
refetchInterval: 10000,
},
});
const { data: lastBossResult } = useReadContract({
address: contractAddress,
abi,
functionName: 'getLastBossResult',
functionName: "getLastBossResult",
args: [address],
query: {
enabled: isConnected,
refetchInterval: 15
}
refetchInterval: 10000,
},
});
console.log(balance, player, army, boss)
const register = useCallback((arg: "RGCVII" | "ETH") => {
if (arg === 'ETH') {
writeContract({
const register = useCallback(
(arg: "RGCVII" | "ETH") => {
if (arg === "ETH") {
writeContract(
{
abi,
address: contractAddress,
functionName: 'register_eth',
functionName: "register_eth",
value: parseEther("0.00045"),
}, {
onSuccess: (hash) => {
setHashAndCallback([hash, resetHashAndCallback])
},
onError: () => resetHashAndCallback()
})
{
onSuccess: (hash) => {
setHashAndCallback([hash, resetHashAndCallback]);
},
onError: () => resetHashAndCallback(),
}
);
} else if (arg === "RGCVII") {
writeContract({
writeContract(
{
abi,
address: daoTokenAddress,
functionName: 'approve',
functionName: "approve",
args: [contractAddress, parseEther("400")],
}, {
},
{
onSuccess: (hash) => {
setHashAndCallback([
hash,
() => writeContract({
() =>
writeContract(
{
abi,
address: contractAddress,
functionName: 'register_dao',
}, {
functionName: "register_dao",
},
{
onSuccess: (hash) => {
setHashAndCallback([hash, resetHashAndCallback])
setHashAndCallback([hash, resetHashAndCallback]);
},
onError: () => resetHashAndCallback()
})
])
},
onError: () => resetHashAndCallback()
});
onError: () => resetHashAndCallback(),
}
}, [writeContract, resetHashAndCallback])
),
]);
},
onError: () => resetHashAndCallback(),
}
);
}
},
[writeContract, resetHashAndCallback]
);
const raid = useCallback(() => {
writeContract({
writeContract(
{
abi,
address: contractAddress,
functionName: 'raid',
}, {
onSuccess: (hash) => {
setHashAndCallback([hash, resetHashAndCallback])
functionName: "raid",
},
onError: () => resetHashAndCallback()
})
}, [writeContract, resetHashAndCallback])
{
onSuccess: (hash) => {
setHashAndCallback([hash, resetHashAndCallback]);
},
onError: () => resetHashAndCallback(),
}
);
}, [writeContract, resetHashAndCallback]);
const addUnit = useCallback((unit: UnitType) => {
writeContract({
const addUnit = useCallback(
(unit: UnitType) => {
writeContract(
{
abi,
address: contractAddress,
functionName: 'addUnit',
args: [unit, 1]
})
}, [writeContract])
functionName: "addUnit",
args: [unit, 1],
},
{
onSuccess: () => {
coinSound();
},
onError: () => resetHashAndCallback(),
}
);
},
[writeContract, resetHashAndCallback]
);
const battleWithBoss = useCallback(() => {
writeContract({
writeContract(
{
abi,
address: contractAddress,
functionName: 'battle_with_boss',
}, {
onSuccess: (hash) => {
setHashAndCallback([hash, () => resetHashAndCallback()])
functionName: "battle_with_boss",
},
onError: () => resetHashAndCallback()
})
}, [writeContract, resetHashAndCallback])
{
onSuccess: (hash) => {
setHashAndCallback([hash, () => resetHashAndCallback()]);
},
onError: () => resetHashAndCallback(),
}
);
}, [writeContract, resetHashAndCallback]);
useEffect(() => {
if (lastBossResult != null) {
@ -235,10 +282,11 @@ const PlayerProvider = ({ children }: { children: ReactNode }) => {
hasFetchedLastBossFirstTime.current = true;
}
}
}, [lastBossResult])
}, [lastBossResult]);
return (
<PlayerContext.Provider value={{
<PlayerContext.Provider
value={{
isRegistered: isRegistered as boolean,
player: player as Player,
army: army as Army,
@ -248,20 +296,26 @@ const PlayerProvider = ({ children }: { children: ReactNode }) => {
register,
raid,
addUnit,
battleWithBoss
}}>
battleWithBoss,
}}
>
{children}
<div className={`${(txHash || bossBattledModalOpen) ? styles.leaderboardOverlay : ""}`}>
<div
className={`${
txHash || bossBattledModalOpen ? styles.leaderboardOverlay : ""
}`}
>
{txHash && <WaitingForTxModal hash={txHash} callbackFn={callbackFn} />}
{bossBattledModalOpen && <BossOutcomeModal setIsOpen={setBossBattlesModalOpen} />}
{bossBattledModalOpen && (
<BossOutcomeModal setIsOpen={setBossBattlesModalOpen} />
)}
</div>
</PlayerContext.Provider>
);
}
};
export const usePlayer = () => {
return useContext(PlayerContext);
}
export default PlayerProvider
};
export default PlayerProvider;

View File

@ -402,7 +402,8 @@
}
}
.leaderboardButton {
.leaderboardButton,
.metricsButton {
position: absolute;
top: 30px;
left: 80px;
@ -425,6 +426,12 @@
}
}
}
.metricsButton {
left: auto;
top: auto;
right: 32px;
bottom: 32px;
}
.leaderboardButton:hover {
transform: scale(1.1);

View File

@ -0,0 +1,29 @@
.dashboard {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 2rem;
padding: 2rem;
max-width: 1200px;
margin: 0 auto;
}
.metric {
background: rgba(0, 0, 0, 0.8);
border-radius: 8px;
padding: 1.5rem;
text-align: center;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.metric h3 {
color: #888;
margin: 0 0 1rem 0;
font-size: 1.1rem;
}
.metric p {
color: #fff;
font-size: 2rem;
margin: 0;
font-weight: bold;
}

View File

@ -0,0 +1,23 @@
.metricsPage {
min-height: 100vh;
padding: 2rem;
background: #1a1a1a;
color: white;
}
.metricsPage h1 {
text-align: center;
margin-bottom: 3rem;
}
.backLink {
display: inline-block;
color: #888;
text-decoration: none;
margin-bottom: 2rem;
transition: color 0.2s;
}
.backLink:hover {
color: white;
}

View File

@ -0,0 +1,44 @@
// https://sfxr.me/
// https://github.com/chr15m/jsfxr?tab=readme-ov-file#library
// https://github.com/goldfire/howler.js
import { sfxr } from "jsfxr";
import { Howl } from "howler";
export const coinSound = () => {
const coin = sfxr.toAudio(
"34T6PkpUzHPSs4CvSaNXpQ6fftpW1yrCDSda6oECJ5CH6BEaokmoZC7aAra2xef61iP6srxUaRZUk8Z2DRJHNMEvtWCLjgKkUCFtpxPe9o8AvYBCJZG1YNNPR"
);
coin.play();
};
export const errorSound = () => {
const fail = sfxr.toAudio(
"3mLuemG9nym33ak7ot6gTcNTFBBnNkcF4rmQFkh1zRhrvJ6totmE1EX61m9LTW9KWGuQpQEMqnVopubShwmxqQK7vAZYMXKbJCxYE9bcTh2qMm9JbMRJAKD5a"
);
fail.play();
};
export const clickSound = () => {
const click = sfxr.toAudio(
"7BMHBGPkXasqBZ54qHeMQTKSwDs2Y176H4hQVNkvQPg5eZyckEhyzKTnAZfqnp9ayL5iPVRNXFNjXAXBbUKhT7U6c1hKZBgWzaWkWTQvmcrCwikKi3RoF7wbd"
);
click.play();
};
export const lostSound = () => {
const fail = new Howl({
src: ["/sounds/lost.wav"],
volume: 0.7
});
fail.play();
};
export const wonSound = () => {
const won = new Howl({
src: ["/sounds/arcade_win.wav"],
});
won.play();
};

View File

@ -9,9 +9,9 @@ export const config = getDefaultConfig({
appName: 'RainbowKit App',
projectId: 'YOUR_PROJECT_ID',
chains: [
baseSepolia,
foundry,
base,
foundry,
baseSepolia,
],
ssr: true,
});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long