diff --git a/app/package-lock.json b/app/package-lock.json
index 00693c4..8be06d6 100644
--- a/app/package-lock.json
+++ b/app/package-lock.json
@@ -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",
diff --git a/app/package.json b/app/package.json
index 3310ec1..68e4f87 100644
--- a/app/package.json
+++ b/app/package.json
@@ -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",
diff --git a/app/public/sounds/arcade_win.wav b/app/public/sounds/arcade_win.wav
new file mode 100644
index 0000000..74c74ba
Binary files /dev/null and b/app/public/sounds/arcade_win.wav differ
diff --git a/app/public/sounds/lost.wav b/app/public/sounds/lost.wav
new file mode 100644
index 0000000..f324b3f
Binary files /dev/null and b/app/public/sounds/lost.wav differ
diff --git a/app/src/components/BossOutcomeModal.tsx b/app/src/components/BossOutcomeModal.tsx
index 793b082..9594f24 100644
--- a/app/src/components/BossOutcomeModal.tsx
+++ b/app/src/components/BossOutcomeModal.tsx
@@ -4,10 +4,10 @@ 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";
interface BossOutcomeModalProps {
- setIsOpen: (val: boolean) => void,
+ setIsOpen: (val: boolean) => void;
}
const BossOutcomeModal = ({ setIsOpen }: BossOutcomeModalProps) => {
@@ -17,25 +17,59 @@ const BossOutcomeModal = ({ setIsOpen }: BossOutcomeModalProps) => {
const outcome = lastBossResult.reward != BigInt(0);
const ascended = lastBossResult.prestigeGained;
- const text = outcome ? and you won! 🤩 : and you lost 😔 ;
- const rewardAmount = parseFloat(parseFloat(formatUnits(bossToReward[lastBossResult.level], 18).toString()).toFixed(4));
- const rewardText =
- ascended ?
You won {rewardAmount} RGCVII and ASCENDED!!! . This means you beat the bosses and gained a Prestige level . Your GELD is now forfeit, but your legend lives on.
- : outcome ? You won {rewardAmount} RGCVII
- : Your GELD is now forfeit. Try again 💪 we know you can do it!
+ if (outcome) {
+ wonSound();
+ } else {
+ lostSound();
+ }
+
+ const text = outcome ? (
+
+ and you won! 🤩
+
+ ) : (
+
+ and you lost 😔
+
+ );
+ const rewardAmount = parseFloat(
+ parseFloat(
+ formatUnits(bossToReward[lastBossResult.level], 18).toString()
+ ).toFixed(4)
+ );
+ const rewardText = ascended ? (
+
+ You won {rewardAmount} RGCVII and{" "}
+ ASCENDED!!! . This means you beat the bosses and gained a{" "}
+ Prestige level . Your GELD is now forfeit, but your legend
+ lives on.
+
+ ) : outcome ? (
+
+ You won {rewardAmount} RGCVII
+
+ ) : (
+
+ Your GELD is now forfeit.
+
+ Try again 💪 we know you can do it!
+
+ );
const bossName = bossToName[lastBossResult.variant];
const bossClass = bossLevelToClass[lastBossResult.variant];
- return
-
You battled {bossName} Moloch!
-
-
{text}
- {rewardText}
-
-
setIsOpen(false)}>Onward!
+ return (
+
+
You battled {bossName} Moloch!
+
+
{text}
+ {rewardText}
+
+ setIsOpen(false)}>Onward!
+
-
-}
+ );
+};
-export default BossOutcomeModal
+export default BossOutcomeModal;
diff --git a/app/src/pages/_app.tsx b/app/src/pages/_app.tsx
index 23c9c58..051d440 100644
--- a/app/src/pages/_app.tsx
+++ b/app/src/pages/_app.tsx
@@ -8,8 +8,8 @@ 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";
const client = new QueryClient();
const font = Texturina({ weight: ["400"], subsets: ["latin"] });
@@ -41,11 +41,19 @@ function MyApp({ Component, pageProps }: AppProps) {
font-family: ${font.style.fontFamily};
}
.pixelFont {
- font-family: ${fontPixel.style.fontFamily};
+ font-family: ${fontPixel.style.fontFamily};
}
`}
+
+
diff --git a/app/src/providers/PlayerProvider.tsx b/app/src/providers/PlayerProvider.tsx
index d1af40c..da27fb7 100644
--- a/app/src/providers/PlayerProvider.tsx
+++ b/app/src/providers/PlayerProvider.tsx
@@ -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;
}
@@ -63,167 +80,199 @@ const PlayerContext = createContext
({
boss: null,
lastBossResult: null,
balance: BigInt(0),
- register: () => { },
- raid: () => { },
- battleWithBoss: () => { },
- addUnit: () => { }
+ register: () => {},
+ raid: () => {},
+ battleWithBoss: () => {},
+ 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: 10000,
- }
+ },
});
- const { data: balance, } = useReadContract({
+ const { data: balance } = useReadContract({
address: contractAddress,
abi,
- functionName: 'balanceOf',
+ functionName: "balanceOf",
args: [address],
query: {
refetchInterval: 10000,
- enabled: isConnected
- }
+ enabled: isConnected,
+ },
});
const { data: player } = useReadContract({
address: contractAddress,
abi,
- functionName: 'getPlayer',
+ functionName: "getPlayer",
args: [address],
query: {
enabled: isConnected,
- refetchInterval: 10000
- }
+ refetchInterval: 10000,
+ },
});
const { data: army } = useReadContract({
address: contractAddress,
abi,
- functionName: 'getArmy',
+ functionName: "getArmy",
args: [address],
query: {
enabled: isConnected,
- refetchInterval: 10000
- }
+ refetchInterval: 10000,
+ },
});
const { data: boss } = useReadContract({
address: contractAddress,
abi,
- functionName: 'getBoss',
+ functionName: "getBoss",
args: [address],
query: {
enabled: isConnected,
- refetchInterval: 10000
- }
+ refetchInterval: 10000,
+ },
});
const { data: lastBossResult } = useReadContract({
address: contractAddress,
abi,
- functionName: 'getLastBossResult',
+ functionName: "getLastBossResult",
args: [address],
query: {
enabled: isConnected,
- refetchInterval: 10000
- }
+ refetchInterval: 10000,
+ },
});
- const register = useCallback((arg: "RGCVII" | "ETH") => {
- if (arg === 'ETH') {
- writeContract({
- abi,
- address: contractAddress,
- functionName: 'register_eth',
- value: parseEther("0.00045"),
- }, {
- onSuccess: (hash) => {
- setHashAndCallback([hash, resetHashAndCallback])
- },
- onError: () => resetHashAndCallback()
- })
- } else if (arg === "RGCVII") {
- writeContract({
- abi,
- address: daoTokenAddress,
- functionName: 'approve',
- args: [contractAddress, parseEther("400")],
- }, {
- onSuccess: (hash) => {
- setHashAndCallback([
- hash,
- () => writeContract({
- abi,
- address: contractAddress,
- functionName: 'register_dao',
- }, {
- onSuccess: (hash) => {
- setHashAndCallback([hash, resetHashAndCallback])
- },
- onError: () => resetHashAndCallback()
- })
- ])
- },
- onError: () => resetHashAndCallback()
- });
- }
- }, [writeContract, resetHashAndCallback])
+ const register = useCallback(
+ (arg: "RGCVII" | "ETH") => {
+ if (arg === "ETH") {
+ writeContract(
+ {
+ abi,
+ address: contractAddress,
+ functionName: "register_eth",
+ value: parseEther("0.00045"),
+ },
+ {
+ onSuccess: (hash) => {
+ setHashAndCallback([hash, resetHashAndCallback]);
+ },
+ onError: () => resetHashAndCallback(),
+ }
+ );
+ } else if (arg === "RGCVII") {
+ writeContract(
+ {
+ abi,
+ address: daoTokenAddress,
+ functionName: "approve",
+ args: [contractAddress, parseEther("400")],
+ },
+ {
+ onSuccess: (hash) => {
+ setHashAndCallback([
+ hash,
+ () =>
+ writeContract(
+ {
+ abi,
+ address: contractAddress,
+ functionName: "register_dao",
+ },
+ {
+ onSuccess: (hash) => {
+ setHashAndCallback([hash, resetHashAndCallback]);
+ },
+ onError: () => resetHashAndCallback(),
+ }
+ ),
+ ]);
+ },
+ onError: () => resetHashAndCallback(),
+ }
+ );
+ }
+ },
+ [writeContract, resetHashAndCallback]
+ );
const raid = useCallback(() => {
- writeContract({
- abi,
- address: contractAddress,
- functionName: 'raid',
- }, {
- onSuccess: (hash) => {
- setHashAndCallback([hash, resetHashAndCallback])
+ writeContract(
+ {
+ abi,
+ address: contractAddress,
+ functionName: "raid",
},
- onError: () => resetHashAndCallback()
- })
- }, [writeContract, resetHashAndCallback])
+ {
+ onSuccess: (hash) => {
+ setHashAndCallback([hash, resetHashAndCallback]);
+ },
+ onError: () => resetHashAndCallback(),
+ }
+ );
+ }, [writeContract, resetHashAndCallback]);
- const addUnit = useCallback((unit: UnitType) => {
- writeContract({
- abi,
- address: contractAddress,
- functionName: 'addUnit',
- args: [unit, 1]
- })
- }, [writeContract])
+ const addUnit = useCallback(
+ (unit: UnitType) => {
+ writeContract(
+ {
+ abi,
+ address: contractAddress,
+ functionName: "addUnit",
+ args: [unit, 1],
+ },
+ {
+ onSuccess: () => {
+ coinSound();
+ },
+ onError: () => resetHashAndCallback(),
+ }
+ );
+ },
+ [writeContract, resetHashAndCallback]
+ );
const battleWithBoss = useCallback(() => {
- writeContract({
- abi,
- address: contractAddress,
- functionName: 'battle_with_boss',
- }, {
- onSuccess: (hash) => {
- setHashAndCallback([hash, () => resetHashAndCallback()])
+ writeContract(
+ {
+ abi,
+ address: contractAddress,
+ functionName: "battle_with_boss",
},
- onError: () => resetHashAndCallback()
- })
- }, [writeContract, resetHashAndCallback])
+ {
+ onSuccess: (hash) => {
+ setHashAndCallback([hash, () => resetHashAndCallback()]);
+ },
+ onError: () => resetHashAndCallback(),
+ }
+ );
+ }, [writeContract, resetHashAndCallback]);
useEffect(() => {
if (lastBossResult != null) {
@@ -233,33 +282,40 @@ const PlayerProvider = ({ children }: { children: ReactNode }) => {
hasFetchedLastBossFirstTime.current = true;
}
}
- }, [lastBossResult])
+ }, [lastBossResult]);
return (
-
+
{children}
-
+
{txHash && }
- {bossBattledModalOpen && }
+ {bossBattledModalOpen && (
+
+ )}
);
-}
+};
export const usePlayer = () => {
return useContext(PlayerContext);
-}
-
-export default PlayerProvider
+};
+export default PlayerProvider;
diff --git a/app/src/utils/soundsEffect.js b/app/src/utils/soundsEffect.js
new file mode 100644
index 0000000..1dc7ed0
--- /dev/null
+++ b/app/src/utils/soundsEffect.js
@@ -0,0 +1,44 @@
+// https://sfxr.me/
+// https://github.com/chr15m/jsfxr?tab=readme-ov-file#library
+
+import { sfxr } from "jsfxr";
+import { Howl, Howler } 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"],
+ });
+
+ // Play the sound.
+ fail.play();
+};
+
+export const wonSound = () => {
+ const won = new Howl({
+ src: ["/sounds/arcade_win.wav"],
+ });
+
+ // Play the sound.
+ won.play();
+};