forked from mico/idle_moloch
Merging latest main
This commit is contained in:
commit
4d5a00511e
@ -2,7 +2,7 @@ import { formatUnits } from "viem"
|
||||
import { BossLevel, usePlayer } from "../providers/PlayerProvider"
|
||||
import styles from "../styles/Info.module.css"
|
||||
import { useEffect, useReducer, useRef } from "react"
|
||||
import { calculateBalance } from "./Counter"
|
||||
import { calculateBalance, toReadable } from "./Counter"
|
||||
|
||||
export const bossLevelToClass: Record<BossLevel, string> = {
|
||||
0: styles.boss0,
|
||||
@ -14,7 +14,7 @@ export const bossLevelToClass: Record<BossLevel, string> = {
|
||||
6: styles.boss6,
|
||||
}
|
||||
|
||||
const bossToName: Record<BossLevel, string> = {
|
||||
export const bossToName: Record<BossLevel, string> = {
|
||||
0: "Gluttonous",
|
||||
1: "Slothful",
|
||||
2: "Lusty",
|
||||
@ -24,7 +24,7 @@ const bossToName: Record<BossLevel, string> = {
|
||||
6: "Greedy",
|
||||
}
|
||||
|
||||
const bossToReward: Record<BossLevel, bigint> = {
|
||||
export const bossToReward: Record<BossLevel, bigint> = {
|
||||
0: BigInt("200000000000000000"),
|
||||
1: BigInt("28274420000000000000"),
|
||||
2: BigInt("174191628800000000000"),
|
||||
@ -70,22 +70,24 @@ const BossInfo = () => {
|
||||
const [, render] = useReducer(p => !p, false);
|
||||
useEffect(() => {
|
||||
const tickInterval = setInterval(() => {
|
||||
chanceToDefeat.current = getBossChanceToDefeat(boss?.level ?? 0, calculateBalance(
|
||||
const _balance = calculateBalance(
|
||||
balance ?? BigInt(0),
|
||||
army?.profit_per_second ?? BigInt(0),
|
||||
player?.last_raided_at ?? BigInt(0)
|
||||
))
|
||||
);
|
||||
chanceToDefeat.current = getBossChanceToDefeat(boss?.level ?? 0, _balance)
|
||||
render();
|
||||
}, 100);
|
||||
return () => clearInterval(tickInterval)
|
||||
}, [balance, army?.profit_per_second, player?.last_raided_at, boss?.level])
|
||||
return <div className={styles.bossInfo}>
|
||||
<p><strong className={bossLevelToClass[boss?.level || 0]}>{bossToName[variant]}</strong> Moloch <small>(lvl {boss ? boss.level + 1 : 0})</small></p>
|
||||
<p><strong className={bossLevelToClass[variant]}>{bossToName[variant]}</strong> Moloch <small>(lvl {boss ? boss.level + 1 : 0})</small></p>
|
||||
<p><strong className={styles.reward}>{parseFloat(parseFloat(formatUnits(bossToReward[boss?.level || 0], 18).toString()).toFixed(4))} RGCVII</strong> <small>reward</small></p>
|
||||
<p>
|
||||
<strong>{parseFloat((chanceToDefeat.current * 100).toFixed(2))} % to slay</strong>{" "}
|
||||
{chanceToDefeat.current == maxChance ? <small className={styles.maxed}>(MAXED)</small> : <small>(Max {maxChance * 100}%)</small>}
|
||||
</p>
|
||||
<p><small>{toReadable(bossToBossPower[boss?.level ?? 0])} GELD = max chance</small></p>
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
41
app/src/components/BossOutcomeModal.tsx
Normal file
41
app/src/components/BossOutcomeModal.tsx
Normal file
@ -0,0 +1,41 @@
|
||||
import { formatUnits } from "viem";
|
||||
import { usePlayer } from "../providers/PlayerProvider";
|
||||
import styles from "../styles/Modal.module.css";
|
||||
import bgStyles from "../styles/Background.module.css";
|
||||
import { bossToName, bossToReward } from "./BossInfo";
|
||||
import { bossLevelToClass } from "./Boss";
|
||||
|
||||
|
||||
interface BossOutcomeModalProps {
|
||||
setIsOpen: (val: boolean) => void,
|
||||
}
|
||||
|
||||
const BossOutcomeModal = ({ setIsOpen }: BossOutcomeModalProps) => {
|
||||
const { lastBossResult } = usePlayer();
|
||||
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 bossName = bossToName[lastBossResult.variant];
|
||||
const bossClass = bossLevelToClass[lastBossResult.variant];
|
||||
|
||||
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>
|
||||
{rewardText}
|
||||
<div>
|
||||
<button onClick={() => setIsOpen(false)}>Onward!</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
export default BossOutcomeModal
|
||||
@ -44,8 +44,8 @@ export const toReadable = (rawValue: bigint) => {
|
||||
} else {
|
||||
const divided = value / suffixes[i - 1].value;
|
||||
const numStr = (value % suffixes[i - 1].value).toString().slice(0, 3);
|
||||
const remainder = parseInt(numStr.replace(/0+$/, ''), 10);
|
||||
return `${divided.toString()}.${remainder.toString()} ${suffixes[i - 1].suffix}`;
|
||||
const remainder = numStr == "0" ? "" : "." + parseInt(numStr.replace(/0+$/, ''), 10);
|
||||
return `${divided.toString()}${remainder.toString()} ${suffixes[i - 1].suffix}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -202,7 +202,7 @@ function PixelatedQuote() {
|
||||
|
||||
setTimeout(() => {
|
||||
setIsShown(false);
|
||||
}, 5000);
|
||||
}, 8000);
|
||||
}, 10000);
|
||||
|
||||
return () => {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { useCallback } from "react";
|
||||
import { usePlayer } from "../providers/PlayerProvider";
|
||||
import styles from "../styles/Modal.module.css";
|
||||
import bgStyles from "../styles/Background.module.css";
|
||||
|
||||
interface RegistrationModalProps {
|
||||
isOpen: boolean;
|
||||
@ -14,13 +15,14 @@ const RegistrationModal = ({ isOpen, setIsOpen }: RegistrationModalProps) => {
|
||||
setIsOpen(false);
|
||||
}, [register, setIsOpen])
|
||||
if (!isOpen) return null;
|
||||
return <div className={styles.modal}>
|
||||
<h2>Insert coins to continue</h2>
|
||||
return <div className={bgStyles.leaderboardOverlay}><div className={styles.modal}>
|
||||
<span className={styles.closeBtn} onClick={() => setIsOpen(false)}>x</span>
|
||||
<h2 className={styles.textCenter}>Insert coins to continue</h2>
|
||||
<div>
|
||||
<button onClick={() => onRegister("RGCVII")}>500 RGCVII</button>
|
||||
<button onClick={() => onRegister("ETH")}>0.0005 ETH</button>
|
||||
</div>
|
||||
</div>
|
||||
</div></div>
|
||||
}
|
||||
|
||||
export default RegistrationModal
|
||||
|
||||
@ -33,16 +33,13 @@ const Scene = () => {
|
||||
);
|
||||
const variant = boss?.variants[boss.level] || 0;
|
||||
|
||||
return (
|
||||
<div className={styles.frame}>
|
||||
return <div className={styles.frame}>
|
||||
<div className={`${styles.air} ${styles.background_asset}`} />
|
||||
<div className={`${styles.clouds_large} ${styles.background_asset}`} />
|
||||
<Boss />
|
||||
<Tower />
|
||||
<div className={`${styles.clouds_small} ${styles.background_asset}`} />
|
||||
<div
|
||||
className={`${styles.mountains} ${styles.background_asset} ${bossToMountainsClass[variant]}`}
|
||||
/>
|
||||
<div className={`${styles.mountains} ${styles.background_asset} ${bossToMountainsClass[variant]}`} />
|
||||
<div className={`${styles.village} ${styles.background_asset}`} />
|
||||
{isRegistered && player?.has_active_session && <BossInfo />}
|
||||
<MarchingBand />
|
||||
@ -54,7 +51,7 @@ const Scene = () => {
|
||||
className={styles.leaderboardButton}
|
||||
title="Leaderboard"
|
||||
>
|
||||
🏆
|
||||
🏆 <span className={styles.hideMobile}>Top players</span>
|
||||
</button>
|
||||
{isLeaderboardOpen && (
|
||||
<div className={styles.leaderboardOverlay}>
|
||||
@ -66,11 +63,9 @@ const Scene = () => {
|
||||
×
|
||||
</button>
|
||||
<Leaderboard />
|
||||
</div>
|
||||
</div>
|
||||
</div></div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export default Scene;
|
||||
|
||||
@ -36,6 +36,7 @@ function MyApp({ Component, pageProps }: AppProps) {
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
button,
|
||||
.title {
|
||||
font-family: ${font.style.fontFamily};
|
||||
}
|
||||
|
||||
@ -1,61 +1,57 @@
|
||||
import React, {
|
||||
createContext,
|
||||
ReactNode,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
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 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"
|
||||
|
||||
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 {
|
||||
level: BossLevel;
|
||||
variant: BossLevel;
|
||||
battled_at: bigint;
|
||||
reward: bigint;
|
||||
prestigeGained: boolean;
|
||||
}
|
||||
|
||||
export interface PlayerContextType {
|
||||
isRegistered: boolean;
|
||||
player: null | Player;
|
||||
army: null | Army;
|
||||
boss: null | Boss;
|
||||
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;
|
||||
}
|
||||
@ -65,192 +61,207 @@ const PlayerContext = createContext<PlayerContextType>({
|
||||
player: null,
|
||||
army: null,
|
||||
boss: null,
|
||||
lastBossResult: null,
|
||||
balance: BigInt(0),
|
||||
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.error(error?.message);
|
||||
}, [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,
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
const { data: balance } = useReadContract({
|
||||
const { data: balance, } = useReadContract({
|
||||
address: contractAddress,
|
||||
abi,
|
||||
functionName: "balanceOf",
|
||||
functionName: 'balanceOf',
|
||||
args: [address],
|
||||
query: {
|
||||
refetchInterval: 15,
|
||||
enabled: isConnected,
|
||||
},
|
||||
enabled: isConnected
|
||||
}
|
||||
});
|
||||
|
||||
const { data: player } = useReadContract({
|
||||
address: contractAddress,
|
||||
abi,
|
||||
functionName: "getPlayer",
|
||||
functionName: 'getPlayer',
|
||||
args: [address],
|
||||
query: {
|
||||
enabled: isConnected,
|
||||
refetchInterval: 15,
|
||||
},
|
||||
refetchInterval: 15
|
||||
}
|
||||
});
|
||||
|
||||
const { data: army } = useReadContract({
|
||||
address: contractAddress,
|
||||
abi,
|
||||
functionName: "getArmy",
|
||||
functionName: 'getArmy',
|
||||
args: [address],
|
||||
query: {
|
||||
enabled: isConnected,
|
||||
refetchInterval: 15,
|
||||
},
|
||||
refetchInterval: 15
|
||||
}
|
||||
});
|
||||
|
||||
const { data: boss } = useReadContract({
|
||||
address: contractAddress,
|
||||
abi,
|
||||
functionName: "getBoss",
|
||||
functionName: 'getBoss',
|
||||
args: [address],
|
||||
query: {
|
||||
enabled: isConnected,
|
||||
refetchInterval: 15,
|
||||
},
|
||||
refetchInterval: 15
|
||||
}
|
||||
});
|
||||
|
||||
console.log(balance, player, army, boss);
|
||||
|
||||
const register = useCallback(
|
||||
(arg: "RGCVII" | "ETH") => {
|
||||
if (arg === "ETH") {
|
||||
writeContract(
|
||||
{
|
||||
abi,
|
||||
const { data: lastBossResult } = useReadContract({
|
||||
address: contractAddress,
|
||||
functionName: "register_eth",
|
||||
value: parseEther("0.00005"),
|
||||
},
|
||||
{
|
||||
onSuccess: (hash) => {
|
||||
setHashAndCallback([hash, resetHashAndCallback]);
|
||||
},
|
||||
}
|
||||
);
|
||||
} else if (arg === "RGCVII") {
|
||||
writeContract(
|
||||
{
|
||||
abi,
|
||||
address: daoTokenAddress,
|
||||
functionName: "approve",
|
||||
args: [contractAddress, parseEther("500")],
|
||||
},
|
||||
{
|
||||
onSuccess: (hash) => {
|
||||
setHashAndCallback([
|
||||
hash,
|
||||
() =>
|
||||
writeContract(
|
||||
{
|
||||
abi,
|
||||
address: contractAddress,
|
||||
functionName: "register_dao",
|
||||
},
|
||||
{
|
||||
onSuccess: (hash) => {
|
||||
setHashAndCallback([hash, resetHashAndCallback]);
|
||||
},
|
||||
functionName: 'getLastBossResult',
|
||||
args: [address],
|
||||
query: {
|
||||
enabled: isConnected,
|
||||
refetchInterval: 15
|
||||
}
|
||||
),
|
||||
]);
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
[writeContract, resetHashAndCallback]
|
||||
);
|
||||
});
|
||||
|
||||
const raid = useCallback(() => {
|
||||
writeContract(
|
||||
{
|
||||
abi,
|
||||
address: contractAddress,
|
||||
functionName: "raid",
|
||||
},
|
||||
{
|
||||
onSuccess: (hash) => {
|
||||
setHashAndCallback([hash, resetHashAndCallback]);
|
||||
},
|
||||
}
|
||||
);
|
||||
}, [writeContract, resetHashAndCallback]);
|
||||
console.log(balance, player, army, boss)
|
||||
|
||||
const addUnit = useCallback(
|
||||
(unit: UnitType, amount: number = 1) => {
|
||||
const register = useCallback((arg: "RGCVII" | "ETH") => {
|
||||
if (arg === 'ETH') {
|
||||
writeContract({
|
||||
abi,
|
||||
address: contractAddress,
|
||||
functionName: "addUnit",
|
||||
args: [unit, amount],
|
||||
});
|
||||
functionName: 'register_eth',
|
||||
value: parseEther("0.00005"),
|
||||
}, {
|
||||
onSuccess: (hash) => {
|
||||
setHashAndCallback([hash, resetHashAndCallback])
|
||||
},
|
||||
[writeContract]
|
||||
);
|
||||
onError: () => resetHashAndCallback()
|
||||
})
|
||||
} else if (arg === "RGCVII") {
|
||||
writeContract({
|
||||
abi,
|
||||
address: daoTokenAddress,
|
||||
functionName: 'approve',
|
||||
args: [contractAddress, parseEther("500")],
|
||||
}, {
|
||||
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])
|
||||
},
|
||||
onError: () => resetHashAndCallback()
|
||||
})
|
||||
}, [writeContract, resetHashAndCallback])
|
||||
|
||||
const addUnit = useCallback((unit: UnitType) => {
|
||||
writeContract({
|
||||
abi,
|
||||
address: contractAddress,
|
||||
functionName: 'addUnit',
|
||||
args: [unit, 1]
|
||||
})
|
||||
}, [writeContract])
|
||||
|
||||
const battleWithBoss = useCallback(() => {
|
||||
writeContract({
|
||||
abi,
|
||||
address: contractAddress,
|
||||
functionName: "battle_with_boss",
|
||||
});
|
||||
}, [writeContract]);
|
||||
functionName: 'battle_with_boss',
|
||||
}, {
|
||||
onSuccess: (hash) => {
|
||||
setHashAndCallback([hash, () => resetHashAndCallback()])
|
||||
},
|
||||
onError: () => resetHashAndCallback()
|
||||
})
|
||||
}, [writeContract, resetHashAndCallback])
|
||||
|
||||
useEffect(() => {
|
||||
if (lastBossResult != null) {
|
||||
if (hasFetchedLastBossFirstTime.current) {
|
||||
setBossBattlesModalOpen(true);
|
||||
} else {
|
||||
hasFetchedLastBossFirstTime.current = true;
|
||||
}
|
||||
}
|
||||
}, [lastBossResult])
|
||||
|
||||
return (
|
||||
<PlayerContext.Provider
|
||||
value={{
|
||||
<PlayerContext.Provider value={{
|
||||
isRegistered: isRegistered as boolean,
|
||||
player: player as Player,
|
||||
army: army as Army,
|
||||
boss: boss as Boss,
|
||||
balance: balance as bigint,
|
||||
lastBossResult: lastBossResult as LastBossResult,
|
||||
register,
|
||||
raid,
|
||||
addUnit,
|
||||
battleWithBoss,
|
||||
}}
|
||||
>
|
||||
battleWithBoss
|
||||
}}>
|
||||
{children}
|
||||
<div className={`${(txHash || bossBattledModalOpen) ? styles.leaderboardOverlay : ""}`}>
|
||||
{txHash && <WaitingForTxModal hash={txHash} callbackFn={callbackFn} />}
|
||||
{bossBattledModalOpen && <BossOutcomeModal setIsOpen={setBossBattlesModalOpen} />}
|
||||
</div>
|
||||
</PlayerContext.Provider>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export const usePlayer = () => {
|
||||
return useContext(PlayerContext);
|
||||
};
|
||||
}
|
||||
|
||||
export default PlayerProvider
|
||||
|
||||
export default PlayerProvider;
|
||||
|
||||
@ -1,17 +1,13 @@
|
||||
.frame {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-width: 8px;
|
||||
border-image: url("/background/frame.png") 22 fill / auto space;
|
||||
width: 720px;
|
||||
height: 960px;
|
||||
|
||||
max-height: 90vh;
|
||||
@media only screen and (max-width: 600px) {
|
||||
max-height: 90vh;
|
||||
overflow: hidden;
|
||||
max-width: 100vw;
|
||||
max-height: 90vh;
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,18 +36,29 @@
|
||||
top: 250px;
|
||||
height: 150px;
|
||||
background-image: url("/background/clouds_large.png");
|
||||
animation:
|
||||
scrollBackground 28s linear infinite,
|
||||
thunder 4s linear infinite;
|
||||
@media only screen and (max-width: 600px) {
|
||||
animation:
|
||||
scrollBackground 280s linear infinite,
|
||||
thunder 4s linear infinite;
|
||||
}
|
||||
}
|
||||
.clouds_small {
|
||||
top: 285px;
|
||||
top: 275px;
|
||||
height: 82px;
|
||||
background-image: url("/background/clouds_small.png");
|
||||
animation:
|
||||
scrollBackground 20s linear infinite,
|
||||
thunder 12s linear infinite;
|
||||
@media only screen and (max-width: 600px) {
|
||||
top: 285px;
|
||||
animation:
|
||||
scrollBackground 200s linear infinite,
|
||||
thunder 12s linear infinite;
|
||||
}
|
||||
}
|
||||
|
||||
.boss {
|
||||
background-image: url("/background/boss/0_gluttony.svg");
|
||||
@ -248,7 +255,7 @@
|
||||
@media only screen and (max-width: 600px) {
|
||||
left: 80px;
|
||||
bottom: 105px;
|
||||
scale: .7;
|
||||
scale: 0.7;
|
||||
}
|
||||
}
|
||||
.musicButton {
|
||||
@ -397,8 +404,8 @@
|
||||
|
||||
.leaderboardButton {
|
||||
position: absolute;
|
||||
top: 80px;
|
||||
right: 30px;
|
||||
top: 30px;
|
||||
left: 80px;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
@ -409,6 +416,14 @@
|
||||
transition: all 0.2s cubic-bezier(0.265, 1.4, 0.68, 1.65);
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||
color: var(--text-color);
|
||||
& .hideMobile {
|
||||
padding-left: 0.5rem;
|
||||
}
|
||||
@media only screen and (max-width: 600px) {
|
||||
& .hideMobile {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.leaderboardButton:hover {
|
||||
|
||||
@ -16,14 +16,16 @@
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
max-width: 720px;
|
||||
height: 100vh;
|
||||
margin: 0 auto;
|
||||
|
||||
height: 90vh;
|
||||
@media only screen and (max-width: 600px) {
|
||||
max-height: 90vh;
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
padding: 0;
|
||||
margin-top: 10px;
|
||||
margin: 10px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
@ -47,6 +47,65 @@
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
.bossModal {
|
||||
padding: 32px;
|
||||
z-index: 3;
|
||||
max-width: 100%;
|
||||
width: 500px;
|
||||
text-align: center;
|
||||
margin-top: 50px;
|
||||
.outcome {
|
||||
font-size: 1.7rem;
|
||||
}
|
||||
.image {
|
||||
position: relative;
|
||||
margin: 0 auto;
|
||||
top: 0;
|
||||
&::after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
& p {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
& button {
|
||||
margin: 1rem;
|
||||
}
|
||||
.lost {
|
||||
color: var(--accent-color);
|
||||
}
|
||||
.won {
|
||||
color: var(--hover-color);
|
||||
}
|
||||
.lost,
|
||||
.won {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
.textCenter {
|
||||
text-align: center;
|
||||
}
|
||||
.closeBtn {
|
||||
display: inline-block;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
text-align: center;
|
||||
line-height: 26px;
|
||||
border-radius: 4px;
|
||||
position: absolute;
|
||||
right: 32px;
|
||||
top: 32px;
|
||||
background: black;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
.closeBtn:hover {
|
||||
color: var(--hover-color);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
.closeBtn::active {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% {
|
||||
|
||||
@ -2,7 +2,6 @@ import { getDefaultConfig } from '@rainbow-me/rainbowkit';
|
||||
import {
|
||||
base,
|
||||
baseSepolia,
|
||||
sepolia,
|
||||
foundry
|
||||
} from 'wagmi/chains';
|
||||
|
||||
@ -13,7 +12,6 @@ export const config = getDefaultConfig({
|
||||
baseSepolia,
|
||||
foundry,
|
||||
base,
|
||||
...(process.env.NEXT_PUBLIC_ENABLE_TESTNETS === 'true' ? [sepolia] : []),
|
||||
],
|
||||
ssr: true,
|
||||
});
|
||||
|
||||
@ -1 +1 @@
|
||||
Subproject commit 448efeea6640bbbc09373f03fbc9c88e280147ba
|
||||
Subproject commit 3291252c866ad698f6a55ec660259e49a67eb3d0
|
||||
@ -5,7 +5,7 @@ import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
||||
import "@openzeppelin/contracts/access/Ownable.sol";
|
||||
|
||||
import {RaidGeldUtils} from "../src/RaidGeldUtils.sol";
|
||||
import {Army, Player, Raider, Boss} from "../src/RaidGeldStructs.sol";
|
||||
import {Army, Player, Raider, Boss, LastBossResult} from "../src/RaidGeldStructs.sol";
|
||||
import "../src/Constants.sol";
|
||||
|
||||
contract RaidGeld is ERC20, Ownable, Constants {
|
||||
@ -16,6 +16,7 @@ contract RaidGeld is ERC20, Ownable, Constants {
|
||||
mapping(address => Player) private players;
|
||||
mapping(address => Army) private armies;
|
||||
mapping(address => Boss) private bosses;
|
||||
mapping(address => LastBossResult) private lastBossResults;
|
||||
|
||||
// WETH
|
||||
IWETH public immutable weth = IWETH(WETH);
|
||||
@ -44,6 +45,7 @@ contract RaidGeld is ERC20, Ownable, Constants {
|
||||
event DaoTokenBuyInAmountSet(address indexed owner, uint256 oldAmount, uint256 newAmount);
|
||||
event PrestigeGained(address indexed player, uint32 prestigeLevel);
|
||||
event BossDefeated(address indexed player, uint8 bossLevel, uint256 earnings);
|
||||
event BossBattle(address indexed player, uint8 bossLevel, bool hasWon);
|
||||
|
||||
// Modifier for functions that should only be available to registered players
|
||||
modifier onlyPlayer() {
|
||||
@ -106,6 +108,9 @@ contract RaidGeld is ERC20, Ownable, Constants {
|
||||
profit_per_second: 0
|
||||
});
|
||||
bosses[_playerAddress] = Boss({level: 0, variants: RaidGeldUtils.generate_boss_variants(block.prevrandao)});
|
||||
|
||||
// dont change lastBossResult
|
||||
// that only changes after boss battles and on init
|
||||
}
|
||||
|
||||
// New player want to register with ETH
|
||||
@ -171,6 +176,11 @@ contract RaidGeld is ERC20, Ownable, Constants {
|
||||
return bosses[addr];
|
||||
}
|
||||
|
||||
// Function to get the Boss struct
|
||||
function getLastBossResult(address addr) public view returns (LastBossResult memory) {
|
||||
return lastBossResults[addr];
|
||||
}
|
||||
|
||||
// Quick fn to check if user is registered
|
||||
function isRegistered(address addr) public view returns (bool) {
|
||||
return players[addr].created_at != 0;
|
||||
@ -248,6 +258,14 @@ contract RaidGeld is ERC20, Ownable, Constants {
|
||||
? RaidGeldUtils.getBossPower(boss_to_attack.level)
|
||||
: balanceOf(msg.sender);
|
||||
bool hasWonBattle = RaidGeldUtils.calculateBossFight(boss_to_attack.level, geld_to_burn, block.prevrandao);
|
||||
emit BossBattle(msg.sender, boss_to_attack.level, hasWonBattle);
|
||||
lastBossResults[msg.sender] = LastBossResult({
|
||||
battled_at: block.timestamp,
|
||||
level: boss_to_attack.level,
|
||||
variant: boss_to_attack.variants[boss_to_attack.level],
|
||||
prestigeGained: false,
|
||||
reward: 0
|
||||
});
|
||||
if (hasWonBattle) {
|
||||
// Burn geld, send some sweet DAO Token and continue
|
||||
_burn(msg.sender, geld_to_burn);
|
||||
@ -256,11 +274,14 @@ contract RaidGeld is ERC20, Ownable, Constants {
|
||||
daoToken.transfer(msg.sender, reward);
|
||||
emit BossDefeated(msg.sender, boss_to_attack.level, reward);
|
||||
|
||||
lastBossResults[msg.sender].reward = reward;
|
||||
|
||||
if (boss_to_attack.level == 6) {
|
||||
// User ascends! Moloch is defeated, user can start a new run
|
||||
players[msg.sender].prestige_level += 1;
|
||||
emit PrestigeGained(msg.sender, players[msg.sender].prestige_level);
|
||||
player_dies(msg.sender);
|
||||
lastBossResults[msg.sender].prestigeGained = true;
|
||||
return [hasWonBattle, true /* New prestige level! */ ];
|
||||
} else {
|
||||
// else go to next boss
|
||||
@ -268,6 +289,7 @@ contract RaidGeld is ERC20, Ownable, Constants {
|
||||
}
|
||||
} else {
|
||||
// Whoops u died, boss defeated you
|
||||
lastBossResults[msg.sender].reward = 0;
|
||||
player_dies(msg.sender);
|
||||
}
|
||||
return [hasWonBattle, false /* hasnt gotten prestige level */ ];
|
||||
|
||||
@ -28,3 +28,11 @@ struct Boss {
|
||||
uint8 level;
|
||||
uint8[7] variants;
|
||||
}
|
||||
|
||||
struct LastBossResult {
|
||||
uint256 battled_at;
|
||||
uint256 reward;
|
||||
uint8 level;
|
||||
uint8 variant;
|
||||
bool prestigeGained;
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@ pragma solidity ^0.8.13;
|
||||
|
||||
import {Test, console} from "forge-std/Test.sol";
|
||||
import {stdStorage, StdStorage} from "forge-std/Test.sol";
|
||||
import {RaidGeld, Army, Player, Boss} from "../src/RaidGeld.sol";
|
||||
import {RaidGeld, Army, Player, Boss, LastBossResult} from "../src/RaidGeld.sol";
|
||||
import "../src/RaidGeldUtils.sol";
|
||||
import {Constants} from "../src/Constants.sol";
|
||||
|
||||
@ -283,6 +283,13 @@ contract raid_geldTest is Test, Constants {
|
||||
|
||||
uint256 afterBossDaoBalance = raid_geld.daoToken().balanceOf(player1);
|
||||
uint256 afterBossContractBalance = raid_geld.daoToken().balanceOf(address(raid_geld));
|
||||
LastBossResult memory bossResult = raid_geld.getLastBossResult(player1);
|
||||
|
||||
assertEq(bossResult.battled_at, block.timestamp);
|
||||
assertEq(bossResult.reward, afterBossDaoBalance - initialDaoBalance);
|
||||
assertEq(bossResult.level, boss.level);
|
||||
assertEq(bossResult.variant, boss.variants[boss.level]);
|
||||
assertEq(bossResult.prestigeGained, false);
|
||||
|
||||
// User should receive funs, contract should lose them
|
||||
assertLt(initialDaoBalance, afterBossDaoBalance);
|
||||
@ -306,6 +313,7 @@ contract raid_geldTest is Test, Constants {
|
||||
registerPlayerWithDaoToken();
|
||||
raid_geld.addUnit(0, 1);
|
||||
|
||||
Boss memory boss = raid_geld.getBoss(player1);
|
||||
bool[2] memory results = raid_geld.battle_with_boss();
|
||||
// Should lose with just starting GELD
|
||||
assertEq(results[0], false);
|
||||
@ -321,6 +329,13 @@ contract raid_geldTest is Test, Constants {
|
||||
assertEq(raid_geld.balanceOf(player1), 0);
|
||||
// Units should reset
|
||||
assertEq(army.moloch_denier.level, 0);
|
||||
|
||||
LastBossResult memory bossResult = raid_geld.getLastBossResult(player1);
|
||||
assertEq(bossResult.battled_at, block.timestamp);
|
||||
assertEq(bossResult.reward, 0);
|
||||
assertEq(bossResult.level, boss.level);
|
||||
assertEq(bossResult.variant, boss.variants[boss.level]);
|
||||
assertEq(bossResult.prestigeGained, false);
|
||||
}
|
||||
|
||||
function test_08_player_who_lost_can_restart() public {
|
||||
@ -388,6 +403,8 @@ contract raid_geldTest is Test, Constants {
|
||||
if (results[0] == true && results[1] == true) {
|
||||
success = true;
|
||||
Player memory player = raid_geld.getPlayer(player1);
|
||||
LastBossResult memory bossResult = raid_geld.getLastBossResult(player1);
|
||||
assertEq(bossResult.prestigeGained, true);
|
||||
vm.assertEq(player.prestige_level, 1);
|
||||
vm.assertEq(player.n_runs, tries);
|
||||
break;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user