From ecbadf3809ca5f7ffb7ab4ad512b3bfcd803a38e Mon Sep 17 00:00:00 2001 From: Mitja Belak Date: Wed, 30 Oct 2024 11:58:17 +0100 Subject: [PATCH] Boss mechanics compile --- src/RaidGeld.sol | 25 ++++++++--------- src/RaidGeldUtils.sol | 59 ++++++++++++++++++++++------------------ test/RaidGeldUtils.t.sol | 2 +- 3 files changed, 45 insertions(+), 41 deletions(-) diff --git a/src/RaidGeld.sol b/src/RaidGeld.sol index e940ef5..2dcce8b 100644 --- a/src/RaidGeld.sol +++ b/src/RaidGeld.sol @@ -48,14 +48,14 @@ contract RaidGeld is ERC20, Ownable, Constants { } modifier onlyActiveSession() { - require(players[msg.sender].active_session, "Session is not active, you need to buy into the game first"); + require(players[msg.sender].has_active_session, "Session is not active, you need to buy into the game first"); _; } modifier newPlay() { Player memory player = players[msg.sender]; bool notRegistered = player.created_at == 0; - bool returningPlayer = player.has_registered && !player.has_active_session; + bool returningPlayer = player.is_registered && !player.has_active_session; require(notRegistered || returningPlayer, "Active session already in progress"); _; } @@ -86,7 +86,7 @@ contract RaidGeld is ERC20, Ownable, Constants { Player memory player = players[addr]; uint32 current_n_runs = player.n_runs + 1; uint256 current_total_rewards = player.total_rewards; - bool has_registered = player.is_registered; + bool current_is_registered = player.is_registered; // Set initial states players[msg.sender] = Player({ total_minted: INITIAL_GELD, @@ -94,7 +94,8 @@ contract RaidGeld is ERC20, Ownable, Constants { last_raided_at: block.timestamp, n_runs: current_n_runs, total_rewards: current_total_rewards, - active_session: false + has_active_session: false, + is_registered: current_is_registered }); armies[msg.sender] = Army({ moloch_denier: Raider({level: 0}), @@ -148,7 +149,7 @@ contract RaidGeld is ERC20, Ownable, Constants { // Manual minting for itchy fingers function raid() external onlyActiveSession { - performRaid(); + performRaid(msg.sender); } // Helper so we can use it when buying units too @@ -202,9 +203,7 @@ contract RaidGeld is ERC20, Ownable, Constants { uint256 time_past = block.timestamp - players[msg.sender].last_raided_at; uint256 new_geld = armies[msg.sender].profit_per_second * time_past; require(balanceOf(msg.sender) + new_geld >= cost, "Not enough GELD to add this unit"); - uint256 totalMinted = performRaid(msg.sender); - - // Emit event + performRaid(msg.sender); // TODO: Since we are first minting then burning the token, this could be simplified // by first calculating the difference and then minting / burning in just one operation @@ -246,14 +245,14 @@ contract RaidGeld is ERC20, Ownable, Constants { function battle_with_boss() external onlyActiveSession { // first perform raid - performRaid(); + performRaid(msg.sender); Boss memory boss_to_attack = bosses[msg.sender]; // calculate how much the player will put into battle - uint256 geld_to_burn = balanceOf(msg.sender) >= RaidGeldUtils.BOSS_POWERS[boss_to_attack.level] - ? RaidGeldUtils.BOSS_POWERS[boss_to_attack.level] + uint256 geld_to_burn = balanceOf(msg.sender) >= RaidGeldUtils.getBossPower(boss_to_attack.level) + ? RaidGeldUtils.getBossPower(boss_to_attack.level) : balanceOf(msg.sender); bool hasWonBattle = RaidGeldUtils.calculateBossFight( - boss_to_attack.level, geld_to_burn, block.timestamp, block.difficulty + boss_to_attack.level, geld_to_burn, block.prevrandao ); if (hasWonBattle) { // Burn geld, get some sweet DAO Token and continue @@ -268,7 +267,7 @@ contract RaidGeld is ERC20, Ownable, Constants { } function player_dies(address player) private { - reset_player(); + reset_player(player); players[player].has_active_session = false; _burn(msg.sender, balanceOf(player)); } diff --git a/src/RaidGeldUtils.sol b/src/RaidGeldUtils.sol index 6f76b78..f793a44 100644 --- a/src/RaidGeldUtils.sol +++ b/src/RaidGeldUtils.sol @@ -19,26 +19,32 @@ library RaidGeldUtils { uint256 constant ANOINTED_BASE_COST = 30096000; uint256 constant CHAMPION_BASE_COST = 255816000; - // Boss lvels - uint256[7] constant BOSS_POWERS = [ - 9000000, // 900e4 - 90000000, // 900e5 - 900000000, // 900e6 - 9000000000, // 900e7 - 90000000000, // 900e8 - 900000000000, // 900e9 - 9000000000000 // 900e10 - ]; - - // First boss is almost guaranteed, others arent - uint256[7] constant BOSS_CHANCES = [99, 89, 80, 70, 62, 51, 40]; - - // These are cumulative chances from above with precision 18 - uint256[7] constant CUMULATIVE_BOSS_CHANCES = - [99e16, 8811e14, 7048e14, 493416e12, 30591792e10, 1560181392e8, 6240725568e7]; - uint256 constant RISK_BETA = 15e17; + function getBossPower(uint8 level) internal pure returns (uint256 power) { + require(level <= 7, "Bosses only go to level 7"); + if (level == 0) return 9000000; // 900e4 + else if (level == 1) return 90000000; // 900e5 + else if (level == 2) return 900000000; // 900e6 + else if (level == 3) return 9000000000; // 900e7 + else if (level == 4) return 90000000000; // 900e8 + else if (level == 5) return 900000000000; // 900e9 + else if (level == 6) return 9000000000000; // 900e10 + } + + function getBossCumulativeChance(uint8 level) internal pure returns (uint256 chance) { + // for boss chances (percent) [99, 89, 80, 70, 62, 51, 40] + // where cumulative chance is in the form of 18 precision related to 1e18 + require(level <= 7, "Bosses only go to level 7"); + if (level == 0) return 99e16; + else if (level == 1) return 8811e14; + else if (level == 2) return 7048e14; + else if (level == 3) return 493416e12; + else if (level == 4) return 30591792e10; + else if (level == 5) return 1560181392e8; + else if (level == 6) return 6240725568e7; + } + function calculateUnitPrice(uint8 unit, uint16 currentLevel, uint16 units) internal pure returns (uint256) { require(unit <= 3, "No matching unit found"); uint256 rollingPriceCalculation = MOLOCH_DENIER_BASE_COST; @@ -67,24 +73,23 @@ library RaidGeldUtils { uint256 apprentice_profit = army.apprentice.level * APPRENTICE_PROFIT; uint256 anointed_profit = army.anointed.level * ANOINTED_PROFIT; uint256 champion_profit = army.champion.level * CHAMPION_PROFIT; - return moloch_denier_profit + apprentice_profit + anointed_profit + champion_profit; } // Returns how much Dao Token player is due for winning - function calculateBossReward(uint256 bossLevel) public view returns (uint256) { - return BOSS_POWERS[bossLevel] * (CUMULATIVE_BOSS_CHANCES[bossLevel] * 10 / 5) ** 2; + function calculateBossReward(uint8 bossLevel) internal pure returns (uint256) { + return getBossPower(bossLevel) * (getBossCumulativeChance(bossLevel) * 10 / 5) ** 2; } // Calculates whether user survives the fight - function calculateBossFight(uint8 bossLevel, uint256 geldBurnt, uint256 timestamp, uint256 difficulty) + function calculateBossFight(uint8 bossLevel, uint256 geldBurnt, uint256 prevrandao) internal pure - returns (uint256) + returns (bool) { - uint256 bossPower = BOSS_POWERS[bossLevel]; + uint256 bossPower = getBossPower(bossLevel); require(geldBurnt <= bossPower, "Cant try to defeat boss with more than what boss power is"); - uint256 random_n = random(timestamp, difficulty, 1, 100); + uint256 random_n = random(prevrandao, 1, 100); // Relative power as in, you can only put in 800 geld to defeat 900 geld boss, // but you will get exponentially worse chances uint256 relativePower = ((geldBurnt ** 2) * 100) / bossPower ** 2; @@ -93,10 +98,10 @@ library RaidGeldUtils { } // TODO: Implement actual randomness - function random(uint256 timestamp, uint256 difficulty, uint256 min, uint256 max) internal pure returns (uint256) { + function random(uint256 prevrandao, uint256 min, uint256 max) internal pure returns (uint256) { // returns 0 - 100 require(max >= min, "Max must be greater than or equal to min"); uint256 range = max - min + 1; - return min + (uint256(keccak256(abi.encodePacked(timestamp, difficulty))) % range); + return min + (uint256(keccak256(abi.encodePacked(prevrandao))) % range); } } diff --git a/test/RaidGeldUtils.t.sol b/test/RaidGeldUtils.t.sol index 5658e60..fbda317 100644 --- a/test/RaidGeldUtils.t.sol +++ b/test/RaidGeldUtils.t.sol @@ -28,7 +28,7 @@ contract raid_geldTest is Test { assertGt(price, basePriceChamp * 12); } - function test_1_profits_per_second(uint16 _dLvl, uint16 _apLvl, uint16 _anLvl, uint16 _cLvl) public { + function test_1_profits_per_second(uint16 _dLvl, uint16 _apLvl, uint16 _anLvl, uint16 _cLvl) public pure { Army memory army = Army({ moloch_denier: Raider({level: 1}), apprentice: Raider({level: 0}),