Adding bosses to the game #6

Merged
mico merged 21 commits from feature/boss-mechanic into main 2024-10-31 13:56:05 +00:00
3 changed files with 45 additions and 41 deletions
Showing only changes of commit ecbadf3809 - Show all commits

View File

@ -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));
}

View File

@ -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);
}
}

View File

@ -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}),