1
0
forked from mico/idle_moloch

Added LastBossResult struct

This commit is contained in:
mic0 2024-10-31 21:01:45 +01:00
parent 7ccc036b2d
commit 32bc1177ba
Signed by: mico
GPG Key ID: A3F8023524CF1C8D
7 changed files with 98 additions and 11 deletions

View File

@ -24,7 +24,7 @@ const bossToName: Record<BossLevel, string> = {
6: "Greedy", 6: "Greedy",
} }
const bossToReward: Record<BossLevel, bigint> = { export const bossToReward: Record<BossLevel, bigint> = {
0: BigInt("200000000000000000"), 0: BigInt("200000000000000000"),
1: BigInt("28274420000000000000"), 1: BigInt("28274420000000000000"),
2: BigInt("174191628800000000000"), 2: BigInt("174191628800000000000"),

View File

@ -0,0 +1,31 @@
import { formatUnits } from "viem";
import { BossLevel, usePlayer } from "../providers/PlayerProvider";
import styles from "../styles/Modal.module.css";
import { bossToReward } from "./BossInfo";
interface BossOutcomeModalProps {
setIsOpen: (val: boolean) => void,
outcome: boolean,
ascended: boolean,
bossLevel: BossLevel
}
const BossOutcomeModal = ({ setIsOpen, outcome, ascended, bossLevel }: BossOutcomeModalProps) => {
const text = outcome ? "and you won! 🤩" : "and you lost 😔";
const rewardAmount = parseFloat(parseFloat(formatUnits(bossToReward[bossLevel], 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. Try again 💪 we know you can do it!</p>
return <div className={styles.modal}>
<h2>You battled a boss</h2>
<p>{text}</p>
{rewardText}
<div>
<button onClick={() => setIsOpen(false)}>Proceed</button>
</div>
</div>
}
export default BossOutcomeModal

View File

@ -61,6 +61,7 @@ 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);
useEffect(() => { useEffect(() => {
console.warn(error) console.warn(error)
@ -133,11 +134,12 @@ const PlayerProvider = ({ children }: { children: ReactNode }) => {
abi, abi,
address: contractAddress, address: contractAddress,
functionName: 'register_eth', functionName: 'register_eth',
value: parseEther("0.0005"), value: parseEther("0.00005"),
}, { }, {
onSuccess: (hash) => { onSuccess: (hash) => {
setHashAndCallback([hash, resetHashAndCallback]) setHashAndCallback([hash, resetHashAndCallback])
} },
onError: () => resetHashAndCallback()
}) })
} else if (arg === "RGCVII") { } else if (arg === "RGCVII") {
writeContract({ writeContract({
@ -156,10 +158,12 @@ const PlayerProvider = ({ children }: { children: ReactNode }) => {
}, { }, {
onSuccess: (hash) => { onSuccess: (hash) => {
setHashAndCallback([hash, resetHashAndCallback]) setHashAndCallback([hash, resetHashAndCallback])
} },
onError: () => resetHashAndCallback()
}) })
]) ])
} },
onError: () => resetHashAndCallback()
}); });
} }
}, [writeContract, resetHashAndCallback]) }, [writeContract, resetHashAndCallback])
@ -172,7 +176,8 @@ const PlayerProvider = ({ children }: { children: ReactNode }) => {
}, { }, {
onSuccess: (hash) => { onSuccess: (hash) => {
setHashAndCallback([hash, resetHashAndCallback]) setHashAndCallback([hash, resetHashAndCallback])
} },
onError: () => resetHashAndCallback()
}) })
}, [writeContract, resetHashAndCallback]) }, [writeContract, resetHashAndCallback])
@ -190,8 +195,13 @@ const PlayerProvider = ({ children }: { children: ReactNode }) => {
abi, abi,
address: contractAddress, address: contractAddress,
functionName: 'battle_with_boss', functionName: 'battle_with_boss',
}, {
onSuccess: (hash) => {
setHashAndCallback([hash, () => resetHashAndCallback()])
},
onError: () => resetHashAndCallback()
}) })
}, [writeContract]) }, [writeContract, resetHashAndCallback])
return ( return (
<PlayerContext.Provider value={{ <PlayerContext.Provider value={{
@ -207,6 +217,7 @@ const PlayerProvider = ({ children }: { children: ReactNode }) => {
}}> }}>
{children} {children}
{txHash && <WaitingForTxModal hash={txHash} callbackFn={callbackFn} />} {txHash && <WaitingForTxModal hash={txHash} callbackFn={callbackFn} />}
{bossJustBattled && <WaitingForTxModal hash={txHash} callbackFn={callbackFn} />}
</PlayerContext.Provider> </PlayerContext.Provider>
); );
} }

View File

@ -2,7 +2,6 @@ import { getDefaultConfig } from '@rainbow-me/rainbowkit';
import { import {
base, base,
baseSepolia, baseSepolia,
sepolia,
foundry foundry
} from 'wagmi/chains'; } from 'wagmi/chains';
@ -13,7 +12,6 @@ export const config = getDefaultConfig({
baseSepolia, baseSepolia,
foundry, foundry,
base, base,
...(process.env.NEXT_PUBLIC_ENABLE_TESTNETS === 'true' ? [sepolia] : []),
], ],
ssr: true, ssr: true,
}); });

View File

@ -5,7 +5,7 @@ import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/access/Ownable.sol";
import {RaidGeldUtils} from "../src/RaidGeldUtils.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"; import "../src/Constants.sol";
contract RaidGeld is ERC20, Ownable, Constants { contract RaidGeld is ERC20, Ownable, Constants {
@ -16,6 +16,7 @@ contract RaidGeld is ERC20, Ownable, Constants {
mapping(address => Player) private players; mapping(address => Player) private players;
mapping(address => Army) private armies; mapping(address => Army) private armies;
mapping(address => Boss) private bosses; mapping(address => Boss) private bosses;
mapping(address => LastBossResult) private lastBossResults;
// WETH // WETH
IWETH public immutable weth = IWETH(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 DaoTokenBuyInAmountSet(address indexed owner, uint256 oldAmount, uint256 newAmount);
event PrestigeGained(address indexed player, uint32 prestigeLevel); event PrestigeGained(address indexed player, uint32 prestigeLevel);
event BossDefeated(address indexed player, uint8 bossLevel, uint256 earnings); 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 for functions that should only be available to registered players
modifier onlyPlayer() { modifier onlyPlayer() {
@ -106,6 +108,9 @@ contract RaidGeld is ERC20, Ownable, Constants {
profit_per_second: 0 profit_per_second: 0
}); });
bosses[_playerAddress] = Boss({level: 0, variants: RaidGeldUtils.generate_boss_variants(block.prevrandao)}); 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 // New player want to register with ETH
@ -171,6 +176,11 @@ contract RaidGeld is ERC20, Ownable, Constants {
return bosses[addr]; 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 // Quick fn to check if user is registered
function isRegistered(address addr) public view returns (bool) { function isRegistered(address addr) public view returns (bool) {
return players[addr].created_at != 0; return players[addr].created_at != 0;
@ -248,6 +258,14 @@ contract RaidGeld is ERC20, Ownable, Constants {
? RaidGeldUtils.getBossPower(boss_to_attack.level) ? RaidGeldUtils.getBossPower(boss_to_attack.level)
: balanceOf(msg.sender); : balanceOf(msg.sender);
bool hasWonBattle = RaidGeldUtils.calculateBossFight(boss_to_attack.level, geld_to_burn, block.prevrandao); 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) { if (hasWonBattle) {
// Burn geld, send some sweet DAO Token and continue // Burn geld, send some sweet DAO Token and continue
_burn(msg.sender, geld_to_burn); _burn(msg.sender, geld_to_burn);
@ -256,11 +274,14 @@ contract RaidGeld is ERC20, Ownable, Constants {
daoToken.transfer(msg.sender, reward); daoToken.transfer(msg.sender, reward);
emit BossDefeated(msg.sender, boss_to_attack.level, reward); emit BossDefeated(msg.sender, boss_to_attack.level, reward);
lastBossResults[msg.sender].reward = reward;
if (boss_to_attack.level == 6) { if (boss_to_attack.level == 6) {
// User ascends! Moloch is defeated, user can start a new run // User ascends! Moloch is defeated, user can start a new run
players[msg.sender].prestige_level += 1; players[msg.sender].prestige_level += 1;
emit PrestigeGained(msg.sender, players[msg.sender].prestige_level); emit PrestigeGained(msg.sender, players[msg.sender].prestige_level);
player_dies(msg.sender); player_dies(msg.sender);
lastBossResults[msg.sender].prestigeGained = true;
return [hasWonBattle, true /* New prestige level! */ ]; return [hasWonBattle, true /* New prestige level! */ ];
} else { } else {
// else go to next boss // else go to next boss
@ -268,6 +289,7 @@ contract RaidGeld is ERC20, Ownable, Constants {
} }
} else { } else {
// Whoops u died, boss defeated you // Whoops u died, boss defeated you
lastBossResults[msg.sender].reward = 0;
player_dies(msg.sender); player_dies(msg.sender);
} }
return [hasWonBattle, false /* hasnt gotten prestige level */ ]; return [hasWonBattle, false /* hasnt gotten prestige level */ ];

View File

@ -28,3 +28,11 @@ struct Boss {
uint8 level; uint8 level;
uint8[7] variants; uint8[7] variants;
} }
struct LastBossResult {
uint256 battled_at;
uint256 reward;
uint8 level;
uint8 variant;
bool prestigeGained;
}

View File

@ -3,7 +3,7 @@ pragma solidity ^0.8.13;
import {Test, console} from "forge-std/Test.sol"; import {Test, console} from "forge-std/Test.sol";
import {stdStorage, StdStorage} 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 "../src/RaidGeldUtils.sol";
import {Constants} from "../src/Constants.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 afterBossDaoBalance = raid_geld.daoToken().balanceOf(player1);
uint256 afterBossContractBalance = raid_geld.daoToken().balanceOf(address(raid_geld)); 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 // User should receive funs, contract should lose them
assertLt(initialDaoBalance, afterBossDaoBalance); assertLt(initialDaoBalance, afterBossDaoBalance);
@ -306,6 +313,7 @@ contract raid_geldTest is Test, Constants {
registerPlayerWithDaoToken(); registerPlayerWithDaoToken();
raid_geld.addUnit(0, 1); raid_geld.addUnit(0, 1);
Boss memory boss = raid_geld.getBoss(player1);
bool[2] memory results = raid_geld.battle_with_boss(); bool[2] memory results = raid_geld.battle_with_boss();
// Should lose with just starting GELD // Should lose with just starting GELD
assertEq(results[0], false); assertEq(results[0], false);
@ -321,6 +329,13 @@ contract raid_geldTest is Test, Constants {
assertEq(raid_geld.balanceOf(player1), 0); assertEq(raid_geld.balanceOf(player1), 0);
// Units should reset // Units should reset
assertEq(army.moloch_denier.level, 0); 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 { 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) { if (results[0] == true && results[1] == true) {
success = true; success = true;
Player memory player = raid_geld.getPlayer(player1); 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.prestige_level, 1);
vm.assertEq(player.n_runs, tries); vm.assertEq(player.n_runs, tries);
break; break;