1
0
forked from mico/idle_moloch

Adds overlays for modal, adds MAX chance geld required

This commit is contained in:
mic0 2024-10-31 22:09:39 +01:00
parent 32bc1177ba
commit e46d6991a4
Signed by: mico
GPG Key ID: A3F8023524CF1C8D
5 changed files with 109 additions and 24 deletions

View File

@ -2,7 +2,7 @@ import { formatUnits } from "viem"
import { BossLevel, usePlayer } from "../providers/PlayerProvider" import { BossLevel, usePlayer } from "../providers/PlayerProvider"
import styles from "../styles/Info.module.css" import styles from "../styles/Info.module.css"
import { useEffect, useReducer, useRef } from "react" import { useEffect, useReducer, useRef } from "react"
import { calculateBalance } from "./Counter" import { calculateBalance, toReadable } from "./Counter"
export const bossLevelToClass: Record<BossLevel, string> = { export const bossLevelToClass: Record<BossLevel, string> = {
0: styles.boss0, 0: styles.boss0,
@ -14,7 +14,7 @@ export const bossLevelToClass: Record<BossLevel, string> = {
6: styles.boss6, 6: styles.boss6,
} }
const bossToName: Record<BossLevel, string> = { export const bossToName: Record<BossLevel, string> = {
0: "Gluttonous", 0: "Gluttonous",
1: "Slothful", 1: "Slothful",
2: "Lusty", 2: "Lusty",
@ -70,11 +70,12 @@ const BossInfo = () => {
const [, render] = useReducer(p => !p, false); const [, render] = useReducer(p => !p, false);
useEffect(() => { useEffect(() => {
const tickInterval = setInterval(() => { const tickInterval = setInterval(() => {
chanceToDefeat.current = getBossChanceToDefeat(boss?.level ?? 0, calculateBalance( const _balance = calculateBalance(
balance ?? BigInt(0), balance ?? BigInt(0),
army?.profit_per_second ?? BigInt(0), army?.profit_per_second ?? BigInt(0),
player?.last_raided_at ?? BigInt(0) player?.last_raided_at ?? BigInt(0)
)) );
chanceToDefeat.current = getBossChanceToDefeat(boss?.level ?? 0, _balance)
render(); render();
}, 100); }, 100);
return () => clearInterval(tickInterval) return () => clearInterval(tickInterval)
@ -86,6 +87,7 @@ const BossInfo = () => {
<strong>{parseFloat((chanceToDefeat.current * 100).toFixed(2))} % to slay</strong>{" "} <strong>{parseFloat((chanceToDefeat.current * 100).toFixed(2))} % to slay</strong>{" "}
{chanceToDefeat.current == maxChance ? <small className={styles.maxed}>(MAXED)</small> : <small>(Max {maxChance * 100}%)</small>} {chanceToDefeat.current == maxChance ? <small className={styles.maxed}>(MAXED)</small> : <small>(Max {maxChance * 100}%)</small>}
</p> </p>
<p><small>{toReadable(bossToBossPower[boss?.level ?? 0])} GELD = max chance</small></p>
</div> </div>
} }

View File

@ -1,29 +1,39 @@
import { formatUnits } from "viem"; import { formatUnits } from "viem";
import { BossLevel, usePlayer } from "../providers/PlayerProvider"; import { usePlayer } from "../providers/PlayerProvider";
import styles from "../styles/Modal.module.css"; import styles from "../styles/Modal.module.css";
import { bossToReward } from "./BossInfo"; import bgStyles from "../styles/Background.module.css";
import { bossToName, bossToReward } from "./BossInfo";
import { bossLevelToClass } from "./Boss";
interface BossOutcomeModalProps { interface BossOutcomeModalProps {
setIsOpen: (val: boolean) => void, setIsOpen: (val: boolean) => void,
outcome: boolean,
ascended: boolean,
bossLevel: BossLevel
} }
const BossOutcomeModal = ({ setIsOpen, outcome, ascended, bossLevel }: BossOutcomeModalProps) => { const BossOutcomeModal = ({ setIsOpen }: BossOutcomeModalProps) => {
const text = outcome ? "and you won! 🤩" : "and you lost 😔"; const { lastBossResult } = usePlayer();
const rewardAmount = parseFloat(parseFloat(formatUnits(bossToReward[bossLevel], 18).toString()).toFixed(4)); 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 = 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> 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> : outcome ? <p>You won <strong>{rewardAmount} RGCVII</strong></p>
: <p>Your GELD is now forfeit. Try again 💪 we know you can do it!</p> : <p>Your GELD is now forfeit.<br />Try again 💪 we know you can do it!</p>
return <div className={styles.modal}> const bossName = bossToName[lastBossResult.variant];
<h2>You battled a boss</h2> const bossClass = bossLevelToClass[lastBossResult.variant];
<p>{text}</p>
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} {rewardText}
<div> <div>
<button onClick={() => setIsOpen(false)}>Proceed</button> <button onClick={() => setIsOpen(false)}>Onward!</button>
</div> </div>
</div> </div>
} }

View File

@ -1,6 +1,7 @@
import { useCallback } from "react"; import { useCallback } from "react";
import { usePlayer } from "../providers/PlayerProvider"; import { usePlayer } from "../providers/PlayerProvider";
import styles from "../styles/Modal.module.css"; import styles from "../styles/Modal.module.css";
import bgStyles from "../styles/Background.module.css";
interface RegistrationModalProps { interface RegistrationModalProps {
isOpen: boolean; isOpen: boolean;
@ -14,13 +15,13 @@ const RegistrationModal = ({ isOpen, setIsOpen }: RegistrationModalProps) => {
setIsOpen(false); setIsOpen(false);
}, [register, setIsOpen]) }, [register, setIsOpen])
if (!isOpen) return null; if (!isOpen) return null;
return <div className={styles.modal}> return <div className={bgStyles.leaderboardOverlay}><div className={styles.modal}>
<h2>Insert coins to continue</h2> <h2 style={{ textAlign: "center" }}>Insert coins to continue</h2>
<div> <div>
<button onClick={() => onRegister("RGCVII")}>500 RGCVII</button> <button onClick={() => onRegister("RGCVII")}>500 RGCVII</button>
<button onClick={() => onRegister("ETH")}>0.0005 ETH</button> <button onClick={() => onRegister("ETH")}>0.0005 ETH</button>
</div> </div>
</div> </div></div>
} }
export default RegistrationModal export default RegistrationModal

View File

@ -1,9 +1,11 @@
import React, { createContext, ReactNode, useCallback, useContext, useEffect, useState } from 'react' import React, { createContext, ReactNode, useCallback, useContext, useEffect, useRef, useState } from 'react'
import { useAccount, useReadContract, useWriteContract } from 'wagmi' import { useAccount, useReadContract, useWriteContract } from 'wagmi'
import contractAbi from "../../../out/RaidGeld.sol/RaidGeld.json" import contractAbi from "../../../out/RaidGeld.sol/RaidGeld.json"
import { Hash, parseEther } from 'viem' import { Hash, parseEther } from 'viem'
import contracts from '../../contract_address' import contracts from '../../contract_address'
import WaitingForTxModal from '../components/WaitingForTxModal' import WaitingForTxModal from '../components/WaitingForTxModal'
import BossOutcomeModal from '../components/BossOutcomeModal'
import styles from "../styles/Background.module.css"
const { contractAddress, daoTokenAddress } = contracts const { contractAddress, daoTokenAddress } = contracts
const abi = contractAbi.abi const abi = contractAbi.abi
@ -33,11 +35,20 @@ export interface Boss {
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 { export interface PlayerContextType {
isRegistered: boolean, isRegistered: boolean,
player: null | Player, player: null | Player,
army: null | Army, army: null | Army,
boss: null | Boss, boss: null | Boss,
lastBossResult: null | LastBossResult,
balance: bigint, balance: bigint,
register: (arg: "ETH" | "RGCVII") => void, register: (arg: "ETH" | "RGCVII") => void,
raid: () => void, raid: () => void,
@ -50,6 +61,7 @@ const PlayerContext = createContext<PlayerContextType>({
player: null, player: null,
army: null, army: null,
boss: null, boss: null,
lastBossResult: null,
balance: BigInt(0), balance: BigInt(0),
register: () => { }, register: () => { },
raid: () => { }, raid: () => { },
@ -61,7 +73,8 @@ const PlayerProvider = ({ children }: { children: ReactNode }) => {
const { address, isConnected } = useAccount(); const { address, isConnected } = useAccount();
const { writeContract, error } = useWriteContract(); const { writeContract, error } = useWriteContract();
const [[txHash, callbackFn], setHashAndCallback] = useState<[Hash | null, () => void]>([null, () => { }]) const [[txHash, callbackFn], setHashAndCallback] = useState<[Hash | null, () => void]>([null, () => { }])
const [bossJustBattled, setBossJustBattled] = useState(false); const [bossBattledModalOpen, setBossBattlesModalOpen] = useState(false);
const hasFetchedLastBossFirstTime = useRef(false);
useEffect(() => { useEffect(() => {
console.warn(error) console.warn(error)
@ -126,6 +139,17 @@ const PlayerProvider = ({ children }: { children: ReactNode }) => {
} }
}); });
const { data: lastBossResult } = useReadContract({
address: contractAddress,
abi,
functionName: 'getLastBossResult',
args: [address],
query: {
enabled: isConnected,
refetchInterval: 15
}
});
console.log(balance, player, army, boss) console.log(balance, player, army, boss)
const register = useCallback((arg: "RGCVII" | "ETH") => { const register = useCallback((arg: "RGCVII" | "ETH") => {
@ -203,6 +227,16 @@ const PlayerProvider = ({ children }: { children: ReactNode }) => {
}) })
}, [writeContract, resetHashAndCallback]) }, [writeContract, resetHashAndCallback])
useEffect(() => {
if (lastBossResult != null) {
if (hasFetchedLastBossFirstTime.current) {
setBossBattlesModalOpen(true);
} else {
hasFetchedLastBossFirstTime.current = true;
}
}
}, [lastBossResult])
return ( return (
<PlayerContext.Provider value={{ <PlayerContext.Provider value={{
isRegistered: isRegistered as boolean, isRegistered: isRegistered as boolean,
@ -210,14 +244,17 @@ const PlayerProvider = ({ children }: { children: ReactNode }) => {
army: army as Army, army: army as Army,
boss: boss as Boss, boss: boss as Boss,
balance: balance as bigint, balance: balance as bigint,
lastBossResult: lastBossResult as LastBossResult,
register, register,
raid, raid,
addUnit, addUnit,
battleWithBoss battleWithBoss
}}> }}>
{children} {children}
{txHash && <WaitingForTxModal hash={txHash} callbackFn={callbackFn} />} <div className={`${(txHash || bossBattledModalOpen) ? styles.leaderboardOverlay : ""}`}>
{bossJustBattled && <WaitingForTxModal hash={txHash} callbackFn={callbackFn} />} {txHash && <WaitingForTxModal hash={txHash} callbackFn={callbackFn} />}
{bossBattledModalOpen && <BossOutcomeModal setIsOpen={setBossBattlesModalOpen} />}
</div>
</PlayerContext.Provider> </PlayerContext.Provider>
); );
} }

View File

@ -47,6 +47,41 @@
margin-bottom: 0; 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;
}
}
@keyframes spin { @keyframes spin {
0% { 0% {