1
0
forked from mico/idle_moloch

Boss mechanics compile

This commit is contained in:
mic0 2024-10-30 11:58:17 +01:00
parent 64832617cc
commit ecbadf3809
Signed by: mico
GPG Key ID: A3F8023524CF1C8D
3 changed files with 45 additions and 41 deletions

View File

@ -48,14 +48,14 @@ contract RaidGeld is ERC20, Ownable, Constants {
} }
modifier onlyActiveSession() { 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() { modifier newPlay() {
Player memory player = players[msg.sender]; Player memory player = players[msg.sender];
bool notRegistered = player.created_at == 0; 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"); require(notRegistered || returningPlayer, "Active session already in progress");
_; _;
} }
@ -86,7 +86,7 @@ contract RaidGeld is ERC20, Ownable, Constants {
Player memory player = players[addr]; Player memory player = players[addr];
uint32 current_n_runs = player.n_runs + 1; uint32 current_n_runs = player.n_runs + 1;
uint256 current_total_rewards = player.total_rewards; uint256 current_total_rewards = player.total_rewards;
bool has_registered = player.is_registered; bool current_is_registered = player.is_registered;
// Set initial states // Set initial states
players[msg.sender] = Player({ players[msg.sender] = Player({
total_minted: INITIAL_GELD, total_minted: INITIAL_GELD,
@ -94,7 +94,8 @@ contract RaidGeld is ERC20, Ownable, Constants {
last_raided_at: block.timestamp, last_raided_at: block.timestamp,
n_runs: current_n_runs, n_runs: current_n_runs,
total_rewards: current_total_rewards, total_rewards: current_total_rewards,
active_session: false has_active_session: false,
is_registered: current_is_registered
}); });
armies[msg.sender] = Army({ armies[msg.sender] = Army({
moloch_denier: Raider({level: 0}), moloch_denier: Raider({level: 0}),
@ -148,7 +149,7 @@ contract RaidGeld is ERC20, Ownable, Constants {
// Manual minting for itchy fingers // Manual minting for itchy fingers
function raid() external onlyActiveSession { function raid() external onlyActiveSession {
performRaid(); performRaid(msg.sender);
} }
// Helper so we can use it when buying units too // 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 time_past = block.timestamp - players[msg.sender].last_raided_at;
uint256 new_geld = armies[msg.sender].profit_per_second * time_past; 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"); require(balanceOf(msg.sender) + new_geld >= cost, "Not enough GELD to add this unit");
uint256 totalMinted = performRaid(msg.sender); performRaid(msg.sender);
// Emit event
// TODO: Since we are first minting then burning the token, this could be simplified // 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 // 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 { function battle_with_boss() external onlyActiveSession {
// first perform raid // first perform raid
performRaid(); performRaid(msg.sender);
Boss memory boss_to_attack = bosses[msg.sender]; Boss memory boss_to_attack = bosses[msg.sender];
// calculate how much the player will put into battle // calculate how much the player will put into battle
uint256 geld_to_burn = balanceOf(msg.sender) >= RaidGeldUtils.BOSS_POWERS[boss_to_attack.level] uint256 geld_to_burn = balanceOf(msg.sender) >= RaidGeldUtils.getBossPower(boss_to_attack.level)
? RaidGeldUtils.BOSS_POWERS[boss_to_attack.level] ? RaidGeldUtils.getBossPower(boss_to_attack.level)
: balanceOf(msg.sender); : balanceOf(msg.sender);
bool hasWonBattle = RaidGeldUtils.calculateBossFight( 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) { if (hasWonBattle) {
// Burn geld, get some sweet DAO Token and continue // 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 { function player_dies(address player) private {
reset_player(); reset_player(player);
players[player].has_active_session = false; players[player].has_active_session = false;
_burn(msg.sender, balanceOf(player)); _burn(msg.sender, balanceOf(player));
} }

View File

@ -19,26 +19,32 @@ library RaidGeldUtils {
uint256 constant ANOINTED_BASE_COST = 30096000; uint256 constant ANOINTED_BASE_COST = 30096000;
uint256 constant CHAMPION_BASE_COST = 255816000; 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; 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) { function calculateUnitPrice(uint8 unit, uint16 currentLevel, uint16 units) internal pure returns (uint256) {
require(unit <= 3, "No matching unit found"); require(unit <= 3, "No matching unit found");
uint256 rollingPriceCalculation = MOLOCH_DENIER_BASE_COST; uint256 rollingPriceCalculation = MOLOCH_DENIER_BASE_COST;
@ -67,24 +73,23 @@ library RaidGeldUtils {
uint256 apprentice_profit = army.apprentice.level * APPRENTICE_PROFIT; uint256 apprentice_profit = army.apprentice.level * APPRENTICE_PROFIT;
uint256 anointed_profit = army.anointed.level * ANOINTED_PROFIT; uint256 anointed_profit = army.anointed.level * ANOINTED_PROFIT;
uint256 champion_profit = army.champion.level * CHAMPION_PROFIT; uint256 champion_profit = army.champion.level * CHAMPION_PROFIT;
return moloch_denier_profit + apprentice_profit + anointed_profit + champion_profit; return moloch_denier_profit + apprentice_profit + anointed_profit + champion_profit;
} }
// Returns how much Dao Token player is due for winning // Returns how much Dao Token player is due for winning
function calculateBossReward(uint256 bossLevel) public view returns (uint256) { function calculateBossReward(uint8 bossLevel) internal pure returns (uint256) {
return BOSS_POWERS[bossLevel] * (CUMULATIVE_BOSS_CHANCES[bossLevel] * 10 / 5) ** 2; return getBossPower(bossLevel) * (getBossCumulativeChance(bossLevel) * 10 / 5) ** 2;
} }
// Calculates whether user survives the fight // 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 internal
pure 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"); 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, // Relative power as in, you can only put in 800 geld to defeat 900 geld boss,
// but you will get exponentially worse chances // but you will get exponentially worse chances
uint256 relativePower = ((geldBurnt ** 2) * 100) / bossPower ** 2; uint256 relativePower = ((geldBurnt ** 2) * 100) / bossPower ** 2;
@ -93,10 +98,10 @@ library RaidGeldUtils {
} }
// TODO: Implement actual randomness // 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 // returns 0 - 100
require(max >= min, "Max must be greater than or equal to min"); require(max >= min, "Max must be greater than or equal to min");
uint256 range = max - min + 1; uint256 range = max - min + 1;
return min + (uint256(keccak256(abi.encodePacked(timestamp, difficulty))) % range); return min + (uint256(keccak256(abi.encodePacked(prevrandao))) % range);
} }
} }

View File

@ -28,7 +28,7 @@ contract raid_geldTest is Test {
assertGt(price, basePriceChamp * 12); 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({ Army memory army = Army({
moloch_denier: Raider({level: 1}), moloch_denier: Raider({level: 1}),
apprentice: Raider({level: 0}), apprentice: Raider({level: 0}),