diff --git a/contracts/adventurer/src/adventurer.cairo b/contracts/adventurer/src/adventurer.cairo index 64c0cdf96..b98cb305a 100644 --- a/contracts/adventurer/src/adventurer.cairo +++ b/contracts/adventurer/src/adventurer.cairo @@ -30,7 +30,7 @@ use super::{ }; use lootitems::{ loot::{Loot, ILoot, ImplLoot}, - constants::{ItemSuffix, ItemId, NamePrefixLength, NameSuffixLength} + constants::{ItemSuffix, ItemId, NamePrefixLength, NameSuffixLength, SUFFIX_UNLOCK_GREANTESS} }; use combat::{ combat::{ImplCombat, CombatSpec, SpecialPowers, CombatResult}, @@ -838,7 +838,8 @@ impl ImplAdventurer of IAdventurer { if (previous_level < 15 && new_level >= 19) { // if previous level was below G15 and new level is G19+, sufix and prefixes were unlocked return (true, true); - } else if (previous_level < 15 && new_level >= 15) { + } else if (previous_level < SUFFIX_UNLOCK_GREANTESS + && new_level >= SUFFIX_UNLOCK_GREANTESS) { // if previous level was below G15 and new level is G15+, suffix was unlocked return (true, false); } else if (previous_level < 19 && new_level >= 19) { @@ -1048,52 +1049,52 @@ impl ImplAdventurer of IAdventurer { // @notice Removes a specified suffix boost from an adventurer's stats. // @param self The instance of the Stats struct which contains the adventurer's stats. // @param suffix The suffix to be removed from the adventurer's stats. - fn remove_suffix_boost(ref self: Adventurer, suffix: u8) { + fn remove_suffix_boost(ref self: Stats, suffix: u8) { if (suffix == ItemSuffix::of_Power) { - self.stats.decrease_strength(3); + self.decrease_strength(3); } else if (suffix == ItemSuffix::of_Giant) { - self.stats.decrease_vitality(3); + self.decrease_vitality(3); } else if (suffix == ItemSuffix::of_Titans) { - self.stats.decrease_strength(2); - self.stats.decrease_vitality(1); + self.decrease_strength(2); + self.decrease_vitality(1); } else if (suffix == ItemSuffix::of_Skill) { - self.stats.decrease_dexterity(3); + self.decrease_dexterity(3); } else if (suffix == ItemSuffix::of_Perfection) { - self.stats.decrease_strength(1); - self.stats.decrease_dexterity(1); - self.stats.decrease_vitality(1); + self.decrease_strength(1); + self.decrease_dexterity(1); + self.decrease_vitality(1); } else if (suffix == ItemSuffix::of_Brilliance) { - self.stats.decrease_vitality(3); + self.decrease_vitality(3); } else if (suffix == ItemSuffix::of_Enlightenment) { - self.stats.decrease_vitality(3); + self.decrease_vitality(3); } else if (suffix == ItemSuffix::of_Protection) { - self.stats.decrease_vitality(2); - self.stats.decrease_dexterity(1); + self.decrease_vitality(2); + self.decrease_dexterity(1); } else if (suffix == ItemSuffix::of_Anger) { - self.stats.decrease_strength(2); - self.stats.decrease_dexterity(1); + self.decrease_strength(2); + self.decrease_dexterity(1); } else if (suffix == ItemSuffix::of_Rage) { - self.stats.decrease_strength(1); - self.stats.decrease_vitality(1); - self.stats.decrease_vitality(1); + self.decrease_strength(1); + self.decrease_vitality(1); + self.decrease_vitality(1); } else if (suffix == ItemSuffix::of_Fury) { - self.stats.decrease_vitality(1); - self.stats.decrease_vitality(1); - self.stats.decrease_vitality(1); + self.decrease_vitality(1); + self.decrease_vitality(1); + self.decrease_vitality(1); } else if (suffix == ItemSuffix::of_Vitriol) { - self.stats.decrease_vitality(2); - self.stats.decrease_vitality(1); + self.decrease_vitality(2); + self.decrease_vitality(1); } else if (suffix == ItemSuffix::of_the_Fox) { - self.stats.decrease_dexterity(2); - self.stats.decrease_vitality(1); + self.decrease_dexterity(2); + self.decrease_vitality(1); } else if (suffix == ItemSuffix::of_Detection) { - self.stats.decrease_vitality(2); - self.stats.decrease_dexterity(1); + self.decrease_vitality(2); + self.decrease_dexterity(1); } else if (suffix == ItemSuffix::of_Reflection) { - self.stats.decrease_vitality(1); - self.stats.decrease_vitality(2); + self.decrease_vitality(1); + self.decrease_vitality(2); } else if (suffix == ItemSuffix::of_the_Twins) { - self.stats.decrease_vitality(3); + self.decrease_vitality(3); } } @@ -1101,21 +1102,21 @@ impl ImplAdventurer of IAdventurer { // @param self The Adventurer to check for item specials. // @return Returns true if adventurer has item specials, false otherwise. fn has_item_specials(self: Adventurer) -> bool { - if (self.weapon.get_greatness() >= 15) { + if (self.weapon.get_greatness() >= SUFFIX_UNLOCK_GREANTESS) { true - } else if (self.chest.get_greatness() >= 15) { + } else if (self.chest.get_greatness() >= SUFFIX_UNLOCK_GREANTESS) { true - } else if (self.head.get_greatness() >= 15) { + } else if (self.head.get_greatness() >= SUFFIX_UNLOCK_GREANTESS) { true - } else if (self.waist.get_greatness() >= 15) { + } else if (self.waist.get_greatness() >= SUFFIX_UNLOCK_GREANTESS) { true - } else if (self.foot.get_greatness() >= 15) { + } else if (self.foot.get_greatness() >= SUFFIX_UNLOCK_GREANTESS) { true - } else if (self.hand.get_greatness() >= 15) { + } else if (self.hand.get_greatness() >= SUFFIX_UNLOCK_GREANTESS) { true - } else if (self.neck.get_greatness() >= 15) { + } else if (self.neck.get_greatness() >= SUFFIX_UNLOCK_GREANTESS) { true - } else if (self.ring.get_greatness() >= 15) { + } else if (self.ring.get_greatness() >= SUFFIX_UNLOCK_GREANTESS) { true } else { false @@ -1162,28 +1163,28 @@ impl ImplAdventurer of IAdventurer { strength: 0, dexterity: 0, vitality: 0, charisma: 0, intelligence: 0, wisdom: 0, luck: 0 }; - if (self.weapon.get_greatness() >= 15) { + if (self.weapon.get_greatness() >= SUFFIX_UNLOCK_GREANTESS) { stats .apply_suffix_boost( ImplItemSpecials::get_specials_full(name_storage1, name_storage2, self.weapon) .special1 ); } - if (self.chest.get_greatness() >= 15) { + if (self.chest.get_greatness() >= SUFFIX_UNLOCK_GREANTESS) { stats .apply_suffix_boost( ImplItemSpecials::get_specials_full(name_storage1, name_storage2, self.chest) .special1 ); } - if (self.head.get_greatness() >= 15) { + if (self.head.get_greatness() >= SUFFIX_UNLOCK_GREANTESS) { stats .apply_suffix_boost( ImplItemSpecials::get_specials_full(name_storage1, name_storage2, self.head) .special1 ); } - if (self.waist.get_greatness() >= 15) { + if (self.waist.get_greatness() >= SUFFIX_UNLOCK_GREANTESS) { stats .apply_suffix_boost( ImplItemSpecials::get_specials_full(name_storage1, name_storage2, self.waist) @@ -1191,7 +1192,7 @@ impl ImplAdventurer of IAdventurer { ); } - if (self.foot.get_greatness() >= 15) { + if (self.foot.get_greatness() >= SUFFIX_UNLOCK_GREANTESS) { stats .apply_suffix_boost( ImplItemSpecials::get_specials_full(name_storage1, name_storage2, self.foot) @@ -1199,7 +1200,7 @@ impl ImplAdventurer of IAdventurer { ); } - if (self.hand.get_greatness() >= 15) { + if (self.hand.get_greatness() >= SUFFIX_UNLOCK_GREANTESS) { stats .apply_suffix_boost( ImplItemSpecials::get_specials_full(name_storage1, name_storage2, self.hand) @@ -1207,7 +1208,7 @@ impl ImplAdventurer of IAdventurer { ); } - if (self.neck.get_greatness() >= 15) { + if (self.neck.get_greatness() >= SUFFIX_UNLOCK_GREANTESS) { stats .apply_suffix_boost( ImplItemSpecials::get_specials_full(name_storage1, name_storage2, self.neck) @@ -1215,7 +1216,7 @@ impl ImplAdventurer of IAdventurer { ); } - if (self.ring.get_greatness() >= 15) { + if (self.ring.get_greatness() >= SUFFIX_UNLOCK_GREANTESS) { stats .apply_suffix_boost( ImplItemSpecials::get_specials_full(name_storage1, name_storage2, self.ring) @@ -4069,11 +4070,11 @@ mod tests { // test base case adventurer.stats.strength = 4; - adventurer.remove_suffix_boost(1); + adventurer.stats.remove_suffix_boost(1); assert(adventurer.stats.strength == 1, 'strength should be 1'); // underflow check - adventurer.remove_suffix_boost(1); + adventurer.stats.remove_suffix_boost(1); assert(adventurer.stats.strength == 0, 'strength should still be 0'); } diff --git a/contracts/adventurer/src/adventurer_meta.cairo b/contracts/adventurer/src/adventurer_meta.cairo index 9311eeba4..4e4cb97f1 100644 --- a/contracts/adventurer/src/adventurer_meta.cairo +++ b/contracts/adventurer/src/adventurer_meta.cairo @@ -1,6 +1,6 @@ use starknet::{StorePacking}; use traits::{TryInto, Into}; -use super::stats::{Stats, StatsPacking}; +use super::stats::{Stats, StatsPacking, StatUtils}; #[derive(Drop, Copy, Serde)] struct AdventurerMetadata { @@ -34,6 +34,18 @@ impl PackingAdventurerMetadata of StorePacking { } } +#[generate_trait] +impl ImplAdventurerMetadata of IAdventurerMetadata { + // @notice: Creates a new AdventurerMetadata struct + // @dev: AdventurerMetadata is initialized without any starting stats + // @param name: The name of the adventurer + // @param start_block: The block number at which the adventurer was created + // @return: The newly created AdventurerMetadata struct + fn new(name: u128, start_block: u64) -> AdventurerMetadata { + AdventurerMetadata { name, start_block, starting_stats: StatUtils::new() } + } +} + const TWO_POW_24: u256 = 0x1000000; const TWO_POW_64: u256 = 0x10000000000000000; const TWO_POW_88: u256 = 0x10000000000000000000000; diff --git a/contracts/adventurer/src/bag.cairo b/contracts/adventurer/src/bag.cairo index adade6a4f..cb2e9cf92 100644 --- a/contracts/adventurer/src/bag.cairo +++ b/contracts/adventurer/src/bag.cairo @@ -602,7 +602,7 @@ mod tests { #[test] #[available_gas(2383150)] - fn test_pack_bag() { + fn test_save_bag() { let mut bag = Bag { item_1: ItemPrimitive { id: 127, xp: 511, metadata: 31 }, item_2: ItemPrimitive { id: 127, xp: 511, metadata: 31 }, diff --git a/contracts/game/src/lib.cairo b/contracts/game/src/lib.cairo index 6a550c3ba..ae8c77748 100644 --- a/contracts/game/src/lib.cairo +++ b/contracts/game/src/lib.cairo @@ -40,12 +40,13 @@ mod Game { } }; use lootitems::{ - loot::{ILoot, Loot, ImplLoot}, constants::{ItemId, NamePrefixLength, NameSuffixLength} + loot::{ILoot, Loot, ImplLoot}, + constants::{ItemId, NamePrefixLength, NameSuffixLength, SUFFIX_UNLOCK_GREANTESS} }; use survivor::{ adventurer::{Adventurer, ImplAdventurer, IAdventurer}, stats::{Stats, StatUtils}, item_primitive::{ImplItemPrimitive, ItemPrimitive}, bag::{Bag, IBag, ImplBag}, - adventurer_meta::{AdventurerMetadata}, exploration::ExploreUtils, + adventurer_meta::{AdventurerMetadata, ImplAdventurerMetadata}, exploration::ExploreUtils, constants::{ discovery_constants::DiscoveryEnums::{ExploreResult, DiscoveryType}, adventurer_constants::{ @@ -180,8 +181,8 @@ mod Game { /// @param adventurer_id A u256 representing the ID of the adventurer. /// @param till_beast A boolean flag indicating if the exploration continues until encountering a beast. fn explore(ref self: ContractState, adventurer_id: felt252, till_beast: bool) { - // get adventurer from storage with stat boosts applied - let (mut adventurer, stat_boosts, _) = _unpack_adventurer_and_bag_with_stat_boosts( + // load player assets + let (mut adventurer, adventurer_entropy, game_entropy, _) = _load_player_assets( @self, adventurer_id ); @@ -194,20 +195,14 @@ mod Game { _assert_no_stat_upgrades_available(immutable_adventurer); _assert_not_in_battle(immutable_adventurer); - // get adventurer and game entropy - let (adventurer_entropy, game_entropy) = _get_adventurer_and_game_entropy( - @self, adventurer_id - ); - // ensure player is not exceeding the rate limit - // @player this is to protect you from bots let block_number = starknet::get_block_info().unbox().block_number; if !adventurer.block_changed_since_last_action(block_number) { _assert_rate_limit(adventurer.actions_per_block, game_entropy.get_rate_limit()); } // if game entropy is eligible to be rotated, rotate it - if block_number > game_entropy.next_update_block { + if block_number >= game_entropy.next_update_block { _rotate_game_entropy(ref self); } @@ -217,6 +212,9 @@ mod Game { // get number of blocks between actions let (idle, num_blocks) = _is_idle(immutable_adventurer, game_entropy); + // update players last action block number to reset idle counter + adventurer.set_last_action_block(block_number); + // process explore or apply idle penalty if !idle { _explore( @@ -231,13 +229,7 @@ mod Game { _apply_idle_penalty(ref self, adventurer_id, ref adventurer, num_blocks); } - // update players last action block number to reset idle counter - adventurer.set_last_action_block(starknet::get_block_info().unbox().block_number); - - // pack and save adventurer - _pack_adventurer_remove_stat_boost( - ref self, ref adventurer, adventurer_id, stat_boosts - ); + _save_adventurer(ref self, ref adventurer, adventurer_id); } /// @title Attack Function @@ -247,8 +239,8 @@ mod Game { /// @param adventurer_id A u256 representing the ID of the adventurer. /// @param to_the_death A boolean flag indicating if the attack should continue until either the adventurer or the beast is defeated. fn attack(ref self: ContractState, adventurer_id: felt252, to_the_death: bool) { - // get adventurer from storage with stat boosts applied - let (mut adventurer, stat_boosts, _) = _unpack_adventurer_and_bag_with_stat_boosts( + // load player assets + let (mut adventurer, adventurer_entropy, game_entropy, _) = _load_player_assets( @self, adventurer_id ); @@ -260,23 +252,17 @@ mod Game { _assert_not_dead(immutable_adventurer); _assert_in_battle(immutable_adventurer); - // get adventurer and game entropy - let (adventurer_entropy, game_entropy) = _get_adventurer_and_game_entropy( - @self, adventurer_id - ); - // ensure player is not exceeding the rate limit - // @player this is to protect you from bots let block_number = starknet::get_block_info().unbox().block_number; if !adventurer.block_changed_since_last_action(block_number) { _assert_rate_limit(adventurer.actions_per_block, game_entropy.get_rate_limit()); } // update actions per block - adventurer.update_actions_per_block(starknet::get_block_info().unbox().block_number); + adventurer.update_actions_per_block(block_number); // if game entropy is eligible to be rotated, rotate it - if block_number > game_entropy.next_update_block { + if block_number >= game_entropy.next_update_block { _rotate_game_entropy(ref self); } @@ -285,12 +271,15 @@ mod Game { // so we reveal their starting stats and store them in Adventurer Meta let adventurer_meta = _handle_stat_reveal(@self, ref adventurer, adventurer_id); // update adventurer meta data (this is the last time this storage slot is updated) - _pack_adventurer_meta(ref self, adventurer_id, adventurer_meta); + _save_adventurer_metadata(ref self, adventurer_id, adventurer_meta); } // get number of blocks between actions let (idle, num_blocks) = _is_idle(immutable_adventurer, game_entropy); + // update players last action block + adventurer.set_last_action_block(block_number); + // process attack or apply idle penalty if !idle { // get weapon specials @@ -327,13 +316,7 @@ mod Game { _apply_idle_penalty(ref self, adventurer_id, ref adventurer, num_blocks); } - // update players last action block - adventurer.set_last_action_block(starknet::get_block_info().unbox().block_number); - - // pack and save adventurer - _pack_adventurer_remove_stat_boost( - ref self, ref adventurer, adventurer_id, stat_boosts - ); + _save_adventurer(ref self, ref adventurer, adventurer_id); } /// @title Flee Function @@ -343,8 +326,8 @@ mod Game { /// @param adventurer_id A u256 representing the unique ID of the adventurer. /// @param to_the_death A boolean flag indicating if the flee attempt should continue until either the adventurer escapes or is defeated. fn flee(ref self: ContractState, adventurer_id: felt252, to_the_death: bool) { - // get adventurer from storage with stat boosts applied - let (mut adventurer, stat_boosts, _) = _unpack_adventurer_and_bag_with_stat_boosts( + // load player assets + let (mut adventurer, adventurer_entropy, game_entropy, _) = _load_player_assets( @self, adventurer_id ); @@ -358,13 +341,7 @@ mod Game { _assert_not_starter_beast(immutable_adventurer); _assert_dexterity_not_zero(immutable_adventurer); - // get adventurer and game entropy - let (adventurer_entropy, game_entropy) = _get_adventurer_and_game_entropy( - @self, adventurer_id - ); - // ensure player is not exceeding the rate limit - // @player this is to protect you from bots let block_number = starknet::get_block_info().unbox().block_number; if !adventurer.block_changed_since_last_action(block_number) { _assert_rate_limit(adventurer.actions_per_block, game_entropy.get_rate_limit()); @@ -374,13 +351,16 @@ mod Game { adventurer.update_actions_per_block(block_number); // if game entropy is eligible to be rotated, rotate it - if block_number > game_entropy.next_update_block { + if block_number >= game_entropy.next_update_block { _rotate_game_entropy(ref self); } // get number of blocks between actions let (idle, num_blocks) = _is_idle(immutable_adventurer, game_entropy); + // update players last action block number + adventurer.set_last_action_block(block_number); + // if adventurer is not idle if !idle { // get beast and beast seed @@ -398,22 +378,15 @@ mod Game { to_the_death ); - // if adventurer died while attempting to flee - if (adventurer.health == 0) { - // process death + // if adventurer died while attempting to flee, process death + if adventurer.health == 0 { _process_adventurer_death(ref self, adventurer, adventurer_id, beast.id, 0); } } else { _apply_idle_penalty(ref self, adventurer_id, ref adventurer, num_blocks); } - // update players last action block number - adventurer.set_last_action_block(starknet::get_block_info().unbox().block_number); - - // pack and save adventurer - _pack_adventurer_remove_stat_boost( - ref self, ref adventurer, adventurer_id, stat_boosts - ); + _save_adventurer(ref self, ref adventurer, adventurer_id); } /// @title Equip Function @@ -424,9 +397,8 @@ mod Game { /// @param adventurer_id A u256 representing the unique ID of the adventurer. /// @param items A u8 array representing the item IDs to equip. fn equip(ref self: ContractState, adventurer_id: felt252, items: Array) { - // get adventurer and bag from storage with stat boosts applied - let (mut adventurer, stat_boosts, mut bag) = - _unpack_adventurer_and_bag_with_stat_boosts( + // load player assets + let (mut adventurer, adventurer_entropy, game_entropy, mut bag) = _load_player_assets( @self, adventurer_id ); @@ -436,16 +408,13 @@ mod Game { assert(items.len() != 0, messages::NO_ITEMS); assert(items.len() <= 8, messages::TOO_MANY_ITEMS); - // equip items, passing in items as a clone so we can maintain ownership of original for event - _equip_items(ref self, ref adventurer, ref bag, adventurer_id, items.clone(), false); + // equip items and record the unequipped items for event + let unequipped_items = _equip_items( + @self, ref adventurer, ref bag, adventurer_id, items.clone(), false + ); // if the adventurer is equipping an item during battle, the beast will counter attack if (adventurer.in_battle()) { - // get adventurer and game entropy - let (adventurer_entropy, game_entropy) = _get_adventurer_and_game_entropy( - @self, adventurer_id - ); - // get beast and beast seed let (beast, beast_seed) = adventurer.get_beast(adventurer_entropy); @@ -459,7 +428,10 @@ mod Game { ref self, ref adventurer, adventurer_id, beast, beast_seed, rnd1, rnd2, ); - // emit event + // emit events + __event_EquippedItems( + ref self, adventurer, adventurer_id, bag, items, unequipped_items, + ); __event_AttackedByBeast(ref self, adventurer, adventurer_id, beast_battle_details); // if adventurer died from counter attack, process death @@ -468,14 +440,12 @@ mod Game { } } - // pack and save adventurer - _pack_adventurer_remove_stat_boost( - ref self, ref adventurer, adventurer_id, stat_boosts - ); + // save adventurer + _save_adventurer(ref self, ref adventurer, adventurer_id); // if the bag was mutated, pack and save it if bag.mutated { - _pack_bag(ref self, adventurer_id, bag); + _save_bag(ref self, adventurer_id, bag); } } @@ -486,9 +456,8 @@ mod Game { /// @param adventurer_id A u256 representing the unique ID of the adventurer. /// @param items A u8 Array representing the IDs of the items to drop. fn drop(ref self: ContractState, adventurer_id: felt252, items: Array) { - // get adventurer and bag from storage with stat boosts applied - let (mut adventurer, stat_boosts, mut bag) = - _unpack_adventurer_and_bag_with_stat_boosts( + // load player assets + let (mut adventurer, adventurer_entropy, game_entropy, mut bag) = _load_player_assets( @self, adventurer_id ); @@ -500,16 +469,14 @@ mod Game { // drop items _drop(ref self, ref adventurer, ref bag, adventurer_id, items.clone()); - // if the adventurer was mutated, pack and save it + // if the adventurer was mutated, save it if (adventurer.mutated) { - _pack_adventurer_remove_stat_boost( - ref self, ref adventurer, adventurer_id, stat_boosts - ); + _save_adventurer(ref self, ref adventurer, adventurer_id); } - // if the bag was mutated, pack and save it - if (bag.mutated) { - _pack_bag(ref self, adventurer_id, bag); + // if the bag was mutated, save it + if bag.mutated { + _save_bag(ref self, adventurer_id, bag); } // emit dropped items event @@ -531,9 +498,8 @@ mod Game { stat_upgrades: Stats, items: Array, ) { - // get adventurer and bag from storage with stat boosts applied - let (mut adventurer, stat_boosts, mut bag) = - _unpack_adventurer_and_bag_with_stat_boosts( + // load player assets + let (mut adventurer, adventurer_entropy, game_entropy, mut bag) = _load_player_assets( @self, adventurer_id ); @@ -545,11 +511,7 @@ mod Game { _assert_not_in_battle(immutable_adventurer); _assert_valid_stat_selection(immutable_adventurer, stat_upgrades); - // load game entropy - let game_entropy = _load_game_entropy(@self); - // ensure player is not exceeding the rate limit - // @player this is to protect you from bots let block_number = starknet::get_block_info().unbox().block_number; if !adventurer.block_changed_since_last_action(block_number) { _assert_rate_limit(adventurer.actions_per_block, game_entropy.get_rate_limit()); @@ -559,7 +521,7 @@ mod Game { adventurer.update_actions_per_block(block_number); // if game entropy is eligible to be rotated, rotate it - if block_number > game_entropy.next_update_block { + if block_number >= game_entropy.next_update_block { _rotate_game_entropy(ref self); } @@ -572,6 +534,9 @@ mod Game { return; } + // update players last action block number + adventurer.set_last_action_block(block_number); + // get number of stat upgrades available before we use them let pre_upgrade_stat_points = adventurer.stat_points_available; @@ -585,31 +550,36 @@ mod Game { // if the player is buying items, process purchases if (items.len() != 0) { - _buy_items( - ref self, + let (purchases, equipped_items, unequipped_items) = _buy_items( + @self, ref adventurer, ref bag, adventurer_id, pre_upgrade_stat_points, items.clone() ); - } - // update players last action block number - adventurer.set_last_action_block(starknet::get_block_info().unbox().block_number); + // emit purchased item event + __event_PurchasedItems(ref self, adventurer, adventurer_id, bag, purchases); + + // if purchase required equipment change, + if (equipped_items.len() != 0) { + // emit equipped items event + __event_EquippedItems( + ref self, adventurer, adventurer_id, bag, equipped_items, unequipped_items, + ); + } + } // emit adventurer upgraded event __event_AdventurerUpgraded(ref self, adventurer, adventurer_id, bag, stat_upgrades); // if the upgrade mutated the adventurer's bag - if (bag.mutated) { - _pack_bag(ref self, adventurer_id, bag); + if bag.mutated { + _save_bag(ref self, adventurer_id, bag); } - // remove stat boosts, pack, and save adventurer - _pack_adventurer_remove_stat_boost( - ref self, ref adventurer, adventurer_id, stat_boosts - ); + _save_adventurer(ref self, ref adventurer, adventurer_id); } /// @title Slay Idle Adventurers Function @@ -638,58 +608,55 @@ mod Game { _rotate_game_entropy(ref self); } - // - // view functions - // + // ------------------------------------------ // + // ------------ View Functions -------------- // + // ------------------------------------------ // fn get_adventurer(self: @ContractState, adventurer_id: felt252) -> Adventurer { - let (adventurer, _, _) = _unpack_adventurer_and_bag_with_stat_boosts( - self, adventurer_id - ); - adventurer + _load_adventurer(self, adventurer_id) } fn get_adventurer_no_boosts(self: @ContractState, adventurer_id: felt252) -> Adventurer { - _unpack_adventurer(self, adventurer_id) + _load_adventurer_no_boosts(self, adventurer_id) } fn get_adventurer_meta(self: @ContractState, adventurer_id: felt252) -> AdventurerMetadata { - _unpack_adventurer_meta(self, adventurer_id) + _load_adventurer_metadata(self, adventurer_id) } fn get_bag(self: @ContractState, adventurer_id: felt252) -> Bag { - _unpacked_bag(self, adventurer_id) + _load_bag(self, adventurer_id) } fn get_weapon_specials(self: @ContractState, adventurer_id: felt252) -> ItemSpecials { - let adventurer = _unpack_adventurer(self, adventurer_id); + let adventurer = _load_adventurer_no_boosts(self, adventurer_id); _get_item_specials(self, adventurer_id, adventurer.weapon) } fn get_chest_specials(self: @ContractState, adventurer_id: felt252) -> ItemSpecials { - let adventurer = _unpack_adventurer(self, adventurer_id); + let adventurer = _load_adventurer_no_boosts(self, adventurer_id); _get_item_specials(self, adventurer_id, adventurer.chest) } fn get_head_specials(self: @ContractState, adventurer_id: felt252) -> ItemSpecials { - let adventurer = _unpack_adventurer(self, adventurer_id); + let adventurer = _load_adventurer_no_boosts(self, adventurer_id); _get_item_specials(self, adventurer_id, adventurer.head) } fn get_waist_specials(self: @ContractState, adventurer_id: felt252) -> ItemSpecials { - let adventurer = _unpack_adventurer(self, adventurer_id); + let adventurer = _load_adventurer_no_boosts(self, adventurer_id); _get_item_specials(self, adventurer_id, adventurer.waist) } fn get_foot_specials(self: @ContractState, adventurer_id: felt252) -> ItemSpecials { - let adventurer = _unpack_adventurer(self, adventurer_id); + let adventurer = _load_adventurer_no_boosts(self, adventurer_id); _get_item_specials(self, adventurer_id, adventurer.foot) } fn get_hand_specials(self: @ContractState, adventurer_id: felt252) -> ItemSpecials { - let adventurer = _unpack_adventurer(self, adventurer_id); + let adventurer = _load_adventurer_no_boosts(self, adventurer_id); _get_item_specials(self, adventurer_id, adventurer.hand) } fn get_necklace_specials(self: @ContractState, adventurer_id: felt252) -> ItemSpecials { - let adventurer = _unpack_adventurer(self, adventurer_id); + let adventurer = _load_adventurer_no_boosts(self, adventurer_id); _get_item_specials(self, adventurer_id, adventurer.neck) } fn get_ring_specials(self: @ContractState, adventurer_id: felt252) -> ItemSpecials { - let adventurer = _unpack_adventurer(self, adventurer_id); + let adventurer = _load_adventurer_no_boosts(self, adventurer_id); _get_item_specials(self, adventurer_id, adventurer.ring) } fn get_items_on_market(self: @ContractState, adventurer_id: felt252) -> Array { - let adventurer = _unpack_adventurer(self, adventurer_id); + let adventurer = _load_adventurer_no_boosts(self, adventurer_id); _assert_upgrades_available(adventurer); let adventurer_entropy = _get_adventurer_entropy(self, adventurer_id); @@ -701,10 +668,10 @@ mod Game { fn get_items_on_market_by_slot( self: @ContractState, adventurer_id: felt252, slot: u8 ) -> Array { - let adventurer = _unpack_adventurer(self, adventurer_id); + let adventurer = _load_adventurer(self, adventurer_id); _assert_upgrades_available(adventurer); - let adventurer_entropy = _get_adventurer_entropy(self, adventurer_id); + let adventurer_entropy = _load_adventurer_entropy(self, adventurer_id); _get_items_on_market_by_slot( self, @@ -717,7 +684,7 @@ mod Game { fn get_items_on_market_by_tier( self: @ContractState, adventurer_id: felt252, tier: u8 ) -> Array { - let adventurer = _unpack_adventurer(self, adventurer_id); + let adventurer = _load_adventurer_no_boosts(self, adventurer_id); _assert_upgrades_available(adventurer); let adventurer_entropy = _get_adventurer_entropy(self, adventurer_id); @@ -775,28 +742,28 @@ mod Game { _get_attacking_beast(self, adventurer_id) } fn get_health(self: @ContractState, adventurer_id: felt252) -> u16 { - _unpack_adventurer(self, adventurer_id).health + _load_adventurer_no_boosts(self, adventurer_id).health } fn get_xp(self: @ContractState, adventurer_id: felt252) -> u16 { - _unpack_adventurer(self, adventurer_id).xp + _load_adventurer_no_boosts(self, adventurer_id).xp } fn get_level(self: @ContractState, adventurer_id: felt252) -> u8 { - _unpack_adventurer(self, adventurer_id).get_level() + _load_adventurer_no_boosts(self, adventurer_id).get_level() } fn get_gold(self: @ContractState, adventurer_id: felt252) -> u16 { - _unpack_adventurer(self, adventurer_id).gold + _load_adventurer_no_boosts(self, adventurer_id).gold } fn get_beast_health(self: @ContractState, adventurer_id: felt252) -> u16 { - _unpack_adventurer(self, adventurer_id).beast_health + _load_adventurer_no_boosts(self, adventurer_id).beast_health } fn get_stat_upgrades_available(self: @ContractState, adventurer_id: felt252) -> u8 { - _unpack_adventurer(self, adventurer_id).stat_points_available + _load_adventurer_no_boosts(self, adventurer_id).stat_points_available } fn get_last_action_block(self: @ContractState, adventurer_id: felt252) -> u16 { - _unpack_adventurer(self, adventurer_id).last_action_block + _load_adventurer_no_boosts(self, adventurer_id).last_action_block } fn get_actions_per_block(self: @ContractState, adventurer_id: felt252) -> u8 { - _unpack_adventurer(self, adventurer_id).actions_per_block + _load_adventurer_no_boosts(self, adventurer_id).actions_per_block } fn get_reveal_block(self: @ContractState, adventurer_id: felt252) -> u64 { _get_reveal_block(self, adventurer_id) @@ -804,7 +771,7 @@ mod Game { fn get_equipped_items( self: @ContractState, adventurer_id: felt252 ) -> Array { - let adventurer = _unpack_adventurer(self, adventurer_id); + let adventurer = _load_adventurer_no_boosts(self, adventurer_id); let mut equipped_items = ArrayTrait::::new(); equipped_items.append(adventurer.weapon); equipped_items.append(adventurer.chest); @@ -817,115 +784,94 @@ mod Game { equipped_items } fn get_equipped_weapon(self: @ContractState, adventurer_id: felt252) -> ItemPrimitive { - _unpack_adventurer(self, adventurer_id).weapon + _load_adventurer_no_boosts(self, adventurer_id).weapon } fn get_equipped_chest(self: @ContractState, adventurer_id: felt252) -> ItemPrimitive { - _unpack_adventurer(self, adventurer_id).chest + _load_adventurer_no_boosts(self, adventurer_id).chest } fn get_equipped_head(self: @ContractState, adventurer_id: felt252) -> ItemPrimitive { - _unpack_adventurer(self, adventurer_id).head + _load_adventurer_no_boosts(self, adventurer_id).head } fn get_equipped_waist(self: @ContractState, adventurer_id: felt252) -> ItemPrimitive { - _unpack_adventurer(self, adventurer_id).waist + _load_adventurer_no_boosts(self, adventurer_id).waist } fn get_equipped_foot(self: @ContractState, adventurer_id: felt252) -> ItemPrimitive { - _unpack_adventurer(self, adventurer_id).foot + _load_adventurer_no_boosts(self, adventurer_id).foot } fn get_equipped_hand(self: @ContractState, adventurer_id: felt252) -> ItemPrimitive { - _unpack_adventurer(self, adventurer_id).hand + _load_adventurer_no_boosts(self, adventurer_id).hand } fn get_equipped_necklace(self: @ContractState, adventurer_id: felt252) -> ItemPrimitive { - _unpack_adventurer(self, adventurer_id).neck + _load_adventurer_no_boosts(self, adventurer_id).neck } fn get_equipped_ring(self: @ContractState, adventurer_id: felt252) -> ItemPrimitive { - _unpack_adventurer(self, adventurer_id).ring + _load_adventurer_no_boosts(self, adventurer_id).ring } fn get_weapon_greatness(self: @ContractState, adventurer_id: felt252) -> u8 { - _unpack_adventurer(self, adventurer_id).weapon.get_greatness() + _load_adventurer_no_boosts(self, adventurer_id).weapon.get_greatness() } fn get_chest_greatness(self: @ContractState, adventurer_id: felt252) -> u8 { - _unpack_adventurer(self, adventurer_id).chest.get_greatness() + _load_adventurer_no_boosts(self, adventurer_id).chest.get_greatness() } fn get_head_greatness(self: @ContractState, adventurer_id: felt252) -> u8 { - _unpack_adventurer(self, adventurer_id).head.get_greatness() + _load_adventurer_no_boosts(self, adventurer_id).head.get_greatness() } fn get_waist_greatness(self: @ContractState, adventurer_id: felt252) -> u8 { - _unpack_adventurer(self, adventurer_id).waist.get_greatness() + _load_adventurer_no_boosts(self, adventurer_id).waist.get_greatness() } fn get_foot_greatness(self: @ContractState, adventurer_id: felt252) -> u8 { - _unpack_adventurer(self, adventurer_id).foot.get_greatness() + _load_adventurer_no_boosts(self, adventurer_id).foot.get_greatness() } fn get_hand_greatness(self: @ContractState, adventurer_id: felt252) -> u8 { - _unpack_adventurer(self, adventurer_id).hand.get_greatness() + _load_adventurer_no_boosts(self, adventurer_id).hand.get_greatness() } fn get_necklace_greatness(self: @ContractState, adventurer_id: felt252) -> u8 { - _unpack_adventurer(self, adventurer_id).neck.get_greatness() + _load_adventurer_no_boosts(self, adventurer_id).neck.get_greatness() } fn get_ring_greatness(self: @ContractState, adventurer_id: felt252) -> u8 { - _unpack_adventurer(self, adventurer_id).ring.get_greatness() + _load_adventurer_no_boosts(self, adventurer_id).ring.get_greatness() } fn get_base_stats(self: @ContractState, adventurer_id: felt252) -> Stats { - _unpack_adventurer(self, adventurer_id).stats + _load_adventurer_no_boosts(self, adventurer_id).stats } fn get_stats(self: @ContractState, adventurer_id: felt252) -> Stats { - let (adventurer, _, _) = _unpack_adventurer_and_bag_with_stat_boosts( - self, adventurer_id - ); - adventurer.stats + _load_adventurer(self, adventurer_id).stats } fn get_base_strength(self: @ContractState, adventurer_id: felt252) -> u8 { - _unpack_adventurer(self, adventurer_id).stats.strength + _load_adventurer_no_boosts(self, adventurer_id).stats.strength } fn get_strength(self: @ContractState, adventurer_id: felt252) -> u8 { - let (adventurer, _, _) = _unpack_adventurer_and_bag_with_stat_boosts( - self, adventurer_id - ); - adventurer.stats.strength + _load_adventurer(self, adventurer_id).stats.strength } fn get_base_dexterity(self: @ContractState, adventurer_id: felt252) -> u8 { - _unpack_adventurer(self, adventurer_id).stats.dexterity + _load_adventurer_no_boosts(self, adventurer_id).stats.dexterity } fn get_dexterity(self: @ContractState, adventurer_id: felt252) -> u8 { - let (adventurer, _, _) = _unpack_adventurer_and_bag_with_stat_boosts( - self, adventurer_id - ); - adventurer.stats.dexterity + _load_adventurer(self, adventurer_id).stats.dexterity } fn get_base_vitality(self: @ContractState, adventurer_id: felt252) -> u8 { - _unpack_adventurer(self, adventurer_id).stats.vitality + _load_adventurer_no_boosts(self, adventurer_id).stats.vitality } fn get_vitality(self: @ContractState, adventurer_id: felt252) -> u8 { - let (adventurer, _, _) = _unpack_adventurer_and_bag_with_stat_boosts( - self, adventurer_id - ); - adventurer.stats.vitality + _load_adventurer(self, adventurer_id).stats.vitality } fn get_base_intelligence(self: @ContractState, adventurer_id: felt252) -> u8 { - _unpack_adventurer(self, adventurer_id).stats.intelligence + _load_adventurer_no_boosts(self, adventurer_id).stats.intelligence } fn get_intelligence(self: @ContractState, adventurer_id: felt252) -> u8 { - let (adventurer, _, _) = _unpack_adventurer_and_bag_with_stat_boosts( - self, adventurer_id - ); - adventurer.stats.intelligence + _load_adventurer(self, adventurer_id).stats.intelligence } fn get_base_wisdom(self: @ContractState, adventurer_id: felt252) -> u8 { - _unpack_adventurer(self, adventurer_id).stats.wisdom + _load_adventurer_no_boosts(self, adventurer_id).stats.wisdom } fn get_wisdom(self: @ContractState, adventurer_id: felt252) -> u8 { - let (adventurer, _, _) = _unpack_adventurer_and_bag_with_stat_boosts( - self, adventurer_id - ); - adventurer.stats.wisdom + _load_adventurer(self, adventurer_id).stats.wisdom } fn get_base_charisma(self: @ContractState, adventurer_id: felt252) -> u8 { - _unpack_adventurer(self, adventurer_id).stats.charisma + _load_adventurer_no_boosts(self, adventurer_id).stats.charisma } fn get_charisma(self: @ContractState, adventurer_id: felt252) -> u8 { - let (adventurer, _, _) = _unpack_adventurer_and_bag_with_stat_boosts( - self, adventurer_id - ); - adventurer.stats.charisma + _load_adventurer(self, adventurer_id).stats.charisma } fn get_special_storage( self: @ContractState, adventurer_id: felt252, storage_index: u8 @@ -1024,7 +970,7 @@ mod Game { fn _slay_idle_adventurer(ref self: ContractState, adventurer_id: felt252) { // unpack adventurer from storage (no need for stat boosts) - let mut adventurer = _unpack_adventurer(@self, adventurer_id); + let mut adventurer = _load_adventurer_no_boosts(@self, adventurer_id); // assert adventurer is not already dead _assert_not_dead(adventurer); @@ -1039,7 +985,7 @@ mod Game { _process_adventurer_death(ref self, adventurer, adventurer_id, 0, 0,); // save adventurer (gg) - _pack_adventurer(ref self, adventurer_id, adventurer); + _save_adventurer_no_boosts(ref self, adventurer, adventurer_id); } fn _process_beast_death( @@ -1129,7 +1075,7 @@ mod Game { - STARTING_HEALTH; // update adventurer meta with starting stats, this is last time we need to update adventurer meta data - let mut adventurer_meta = _unpack_adventurer_meta(self, adventurer_id); + let mut adventurer_meta = _load_adventurer_metadata(self, adventurer_id); adventurer_meta.starting_stats = starting_stats; adventurer_meta } @@ -1324,14 +1270,11 @@ mod Game { let mut adventurer = ImplAdventurer::new(weapon); // set the adventurer last action block to the current block + the reveal delay so the idle - // timer doesn't start till the game officially starts after the reveal phase + // timer doesn't start until after the reveal delay adventurer.set_last_action_block(current_block + _get_reveal_block_delay()); - // player doesn't get starting stats until after the commit-and-reveal phase - let empty_stats = StatUtils::new(); - let adventurer_meta = AdventurerMetadata { - name, start_block: current_block, starting_stats: empty_stats - }; + // create meta data for the adventurer + let adventurer_meta = ImplAdventurerMetadata::new(name, current_block); // adventurer immediately gets ambushed by a starter beast let beast_battle_details = _starter_beast_ambush( @@ -1339,8 +1282,8 @@ mod Game { ); // pack and save new adventurer and metadata - _pack_adventurer(ref self, adventurer_id, adventurer); - _pack_adventurer_meta(ref self, adventurer_id, adventurer_meta); + _save_adventurer_no_boosts(ref self, adventurer, adventurer_id); + _save_adventurer_metadata(ref self, adventurer_id, adventurer_meta); // increment the adventurer id counter self._game_counter.write(adventurer_id); @@ -1628,14 +1571,10 @@ mod Game { }; if (name_storage1.mutated) { - _pack_loot_special_names_storage( - ref self, adventurer_id, LOOT_NAME_STORAGE_INDEX_1, name_storage1 - ); + _save_item_specials(ref self, adventurer_id, LOOT_NAME_STORAGE_INDEX_1, name_storage1); } if (name_storage2.mutated) { - _pack_loot_special_names_storage( - ref self, adventurer_id, LOOT_NAME_STORAGE_INDEX_2, name_storage2 - ); + _save_item_specials(ref self, adventurer_id, LOOT_NAME_STORAGE_INDEX_2, name_storage2); } items_leveled_up @@ -1678,6 +1617,9 @@ mod Game { // if item received a suffix as part of the level up if (suffix_unlocked) { + // apply the item stat boosts so that subsequent events include this information + adventurer.stats.apply_suffix_boost(specials.special1); + // check if the suffix provided a vitality boost let vitality_boost = AdventurerUtils::get_vitality_item_boost(specials.special1); if (vitality_boost != 0) { @@ -1923,7 +1865,13 @@ mod Game { // @param bag The reference to the adventurer's bag. // @param item The primitive item to be equipped. // @return The ID of the item that has been unequipped. - fn _equip_item(ref adventurer: Adventurer, ref bag: Bag, item: ItemPrimitive) -> u8 { + fn _equip_item( + self: @ContractState, + ref adventurer: Adventurer, + ref bag: Bag, + adventurer_id: felt252, + item: ItemPrimitive + ) -> u8 { // get the item currently equipped to the slot the item is being equipped to let unequipping_item = adventurer.get_item_at_slot(ImplLoot::get_slot(item.id)); @@ -1931,11 +1879,21 @@ mod Game { if unequipping_item.id != 0 { // put it into the adventurer's bag bag.add_item(unequipping_item); + + // if the item was providing a stat boosts, remove it + if unequipping_item.get_greatness() >= SUFFIX_UNLOCK_GREANTESS { + _remove_item_stat_boost(self, ref adventurer, adventurer_id, unequipping_item); + } } // equip item adventurer.equip_item(item); + // if item being equipped has stat boosts unlocked, apply it to adventurer + if item.get_greatness() >= SUFFIX_UNLOCK_GREANTESS { + _apply_item_stat_boost(self, ref adventurer, adventurer_id, item); + } + // return the item being unequipped for events unequipping_item.id } @@ -1948,13 +1906,13 @@ mod Game { // @param item_id The identifier of the item being equipped // @return an array of items that were unequipped as a result of equipping the items fn _equip_items( - ref self: ContractState, + contract_state: @ContractState, ref adventurer: Adventurer, ref bag: Bag, adventurer_id: felt252, items_to_equip: Array, is_newly_purchased: bool - ) { + ) -> Array { // mutable array from returning items that were unequipped as a result of equipping the items let mut unequipped_items = ArrayTrait::::new(); @@ -1982,11 +1940,19 @@ mod Game { // create new item, equip it, and record if we need unequipped an item let mut new_item = ImplItemPrimitive::new(item_id); new_item.set_metadata_id(adventurer, bag); - unequipped_item_id = _equip_item(ref adventurer, ref bag, new_item); + unequipped_item_id = + _equip_item(contract_state, ref adventurer, ref bag, adventurer_id, new_item); } else { // otherwise item is being equipped from bag // so remove it from bag, equip it, and record if we need to unequip an item - unequipped_item_id = _equip_item(ref adventurer, ref bag, bag.remove_item(item_id)); + unequipped_item_id = + _equip_item( + contract_state, + ref adventurer, + ref bag, + adventurer_id, + bag.remove_item(item_id) + ); } // if an item was unequipped @@ -1998,10 +1964,7 @@ mod Game { i += 1; }; - // and emit equipped item event - __event_EquippedItems( - ref self, adventurer, adventurer_id, bag, equipped_items, unequipped_items, - ); + unequipped_items } // @dev Drops multiple items from the adventurer's possessions, either from equipment or bag. @@ -2050,16 +2013,17 @@ mod Game { // @param adventurer_id The unique identifier for the adventurer. // @param adventurer_entropy The entropy of the adventurer used for randomness. // @param items The Array of ItemPurchase instances representing items to be purchased. + // @return A tuple containing three arrays: the first contains the items purchased, the second contains the items that were equipped as part of the purchase, and the third contains the items that were unequipped as a result of equipping the newly purchased items. fn _buy_items( - ref self: ContractState, + contract_state: @ContractState, ref adventurer: Adventurer, ref bag: Bag, adventurer_id: felt252, stat_points_available: u8, items_to_purchase: Array, - ) { + ) -> (Array, Array, Array) { // get adventurer entropy - let adventurer_entropy = _get_adventurer_entropy(@self, adventurer_id); + let adventurer_entropy = _get_adventurer_entropy(contract_state, adventurer_id); // mutable array for returning items that need to be equipped as part of this purchase let mut unequipped_items = ArrayTrait::::new(); @@ -2082,7 +2046,7 @@ mod Game { ); // buy it and store result in our purchases array for event - purchases.append(_buy_item(ref self, ref adventurer, ref bag, item.item_id)); + purchases.append(_buy_item(ref adventurer, ref bag, item.item_id)); // if item is being equipped as part of the purchase if item.equip { @@ -2097,16 +2061,15 @@ mod Game { item_number += 1; }; - // emit purchased items event - __event_PurchasedItems(ref self, adventurer, adventurer_id, bag, purchases); - // if we have items to equip as part of the purchase if (items_to_equip.len() != 0) { // equip them and record the items that were unequipped - _equip_items( - ref self, ref adventurer, ref bag, adventurer_id, items_to_equip.clone(), true + let unequipped_items = _equip_items( + contract_state, ref adventurer, ref bag, adventurer_id, items_to_equip.clone(), true ); } + + (purchases, items_to_equip, unequipped_items) } // @notice Process the purchase of potions for the adventurer @@ -2174,9 +2137,8 @@ mod Game { // @param adventurer The adventurer buying the item. The function modifies the adventurer's gold and equipment. // @param bag The bag of the adventurer. The function may add items to the bag if the adventurer unequips an item or opts not to equip a purchased item. // @param item_id The ID of the item to be purchased. - fn _buy_item( - ref self: ContractState, ref adventurer: Adventurer, ref bag: Bag, item_id: u8 - ) -> LootWithPrice { + // @return The item that was purchased and its price. + fn _buy_item(ref adventurer: Adventurer, ref bag: Bag, item_id: u8) -> LootWithPrice { // create an immutable copy of our adventurer to use for validation let immutable_adventurer = adventurer; @@ -2204,81 +2166,136 @@ mod Game { // ------------------------------------------ // // ------------ Helper Functions ------------ // // ------------------------------------------ // - fn _unpack_adventurer(self: @ContractState, adventurer_id: felt252) -> Adventurer { - let adventurer = self._adventurer.read(adventurer_id); - adventurer - } - fn _unpack_adventurer_and_bag_with_stat_boosts( + fn _load_player_assets( self: @ContractState, adventurer_id: felt252 - ) -> (Adventurer, Stats, Bag) { - // unpack adventurer - let mut adventurer: Adventurer = self._adventurer.read(adventurer_id); + ) -> (Adventurer, felt252, GameEntropy, Bag) { + let adventurer = _load_adventurer(self, adventurer_id); + let adventurer_entropy = _load_adventurer_entropy(self, adventurer_id); + let game_entropy = _load_game_entropy(self); + let bag = _load_bag(self, adventurer_id); + (adventurer, adventurer_entropy, game_entropy, bag) + } - // start with no stat boosts - let mut item_stat_boosts = StatUtils::new(); + fn _load_adventurer_entropy(self: @ContractState, adventurer_id: felt252) -> felt252 { + // get the block the adventurer started the game on + let start_block = _load_adventurer_metadata(self, adventurer_id).start_block; - // if adventurer has item specials - if adventurer.has_item_specials() { - // get specials from storage - let (name_storage1, name_storage2) = _get_special_storages(self, adventurer_id); - // get resulting stat boosts - item_stat_boosts = adventurer.get_stat_boosts(name_storage1, name_storage2); - // apply stat boosts - adventurer.apply_stat_boosts(item_stat_boosts); + // use longer block delay on mainnet for stronger entropy + let chain_id = starknet::get_execution_info().unbox().tx_info.unbox().chain_id; + if chain_id == MAINNET_CHAIN_ID { + _get_mainnet_entropy(adventurer_id, start_block) + } else if chain_id == GOERLI_CHAIN_ID { + _get_testnet_entropy(adventurer_id, start_block) + } else { + _get_basic_entropy(adventurer_id, start_block) } + } - let starting_stats = _unpack_adventurer_meta(self, adventurer_id).starting_stats; - adventurer.apply_stat_boosts(starting_stats); - - // get bag from storage - let bag = _unpacked_bag(self, adventurer_id); - - // luck isn't stored, it is calculated dynamically - adventurer.set_luck(bag); - - // return adventurer, item stat boots, and bag - (adventurer, item_stat_boosts, bag) + fn _load_adventurer(self: @ContractState, adventurer_id: felt252) -> Adventurer { + let mut adventurer = self._adventurer.read(adventurer_id); + _apply_starting_stats(self, ref adventurer, adventurer_id); + _apply_equipment_stat_boosts(self, ref adventurer, adventurer_id); + _apply_luck(self, ref adventurer, adventurer_id); + adventurer + } + fn _load_adventurer_no_boosts(self: @ContractState, adventurer_id: felt252) -> Adventurer { + self._adventurer.read(adventurer_id) } - #[inline(always)] - fn _pack_adventurer(ref self: ContractState, adventurer_id: felt252, adventurer: Adventurer) { + fn _save_adventurer( + ref self: ContractState, ref adventurer: Adventurer, adventurer_id: felt252, + ) { + _remove_starting_stats(@self, ref adventurer, adventurer_id); + _remove_equipment_stat_boosts(@self, ref adventurer, adventurer_id); self._adventurer.write(adventurer_id, adventurer); } - // @dev Packs and saves an adventurer after removing stat boosts. - // @param adventurer_id The ID of the adventurer to be modified. - // @param adventurer The adventurer to be modified. - // @param stat_boosts The stat boosts to be removed. - fn _pack_adventurer_remove_stat_boost( - ref self: ContractState, - ref adventurer: Adventurer, - adventurer_id: felt252, - stat_boosts: Stats - ) { - // remove item stat boosts - adventurer.remove_stat_boosts(stat_boosts); - - // remove starting stats so they don't consume storage space - let starting_stats = _unpack_adventurer_meta(@self, adventurer_id).starting_stats; - adventurer.remove_stat_boosts(starting_stats); - // save adventurer + fn _save_adventurer_no_boosts( + ref self: ContractState, adventurer: Adventurer, adventurer_id: felt252, + ) { self._adventurer.write(adventurer_id, adventurer); } - #[inline(always)] - fn _unpacked_bag(self: @ContractState, adventurer_id: felt252) -> Bag { + fn _apply_luck(self: @ContractState, ref adventurer: Adventurer, adventurer_id: felt252) { + let bag = _load_bag(self, adventurer_id); + adventurer.set_luck(bag); + } + fn _load_bag(self: @ContractState, adventurer_id: felt252) -> Bag { self._bag.read(adventurer_id) } #[inline(always)] - fn _pack_bag(ref self: ContractState, adventurer_id: felt252, bag: Bag) { + fn _save_bag(ref self: ContractState, adventurer_id: felt252, bag: Bag) { self._bag.write(adventurer_id, bag); } - #[inline(always)] - fn _unpack_adventurer_meta(self: @ContractState, adventurer_id: felt252) -> AdventurerMetadata { + + fn _apply_starting_stats( + self: @ContractState, ref adventurer: Adventurer, adventurer_id: felt252 + ) { + let starting_stats = _load_adventurer_metadata(self, adventurer_id).starting_stats; + adventurer.apply_stat_boosts(starting_stats); + } + + fn _remove_starting_stats( + self: @ContractState, ref adventurer: Adventurer, adventurer_id: felt252 + ) { + let starting_stats = _load_adventurer_metadata(self, adventurer_id).starting_stats; + adventurer.remove_stat_boosts(starting_stats); + } + fn _load_adventurer_metadata( + self: @ContractState, adventurer_id: felt252 + ) -> AdventurerMetadata { self._adventurer_meta.read(adventurer_id) } + + fn _apply_item_stat_boost( + self: @ContractState, + ref adventurer: Adventurer, + adventurer_id: felt252, + item: ItemPrimitive + ) { + let (name_storage1, name_storage2) = _get_special_storages(self, adventurer_id); + let item_specials = ImplItemSpecials::get_specials_full(name_storage1, name_storage2, item); + adventurer.stats.apply_suffix_boost(item_specials.special1); + } + + fn _remove_item_stat_boost( + self: @ContractState, + ref adventurer: Adventurer, + adventurer_id: felt252, + item: ItemPrimitive + ) { + let (name_storage1, name_storage2) = _get_special_storages(self, adventurer_id); + let item_specials = ImplItemSpecials::get_specials_full(name_storage1, name_storage2, item); + adventurer.stats.remove_suffix_boost(item_specials.special1); + } + + fn _apply_equipment_stat_boosts( + self: @ContractState, ref adventurer: Adventurer, adventurer_id: felt252 + ) { + if adventurer.has_item_specials() { + // get specials from storage + let (name_storage1, name_storage2) = _get_special_storages(self, adventurer_id); + // get stat boosts from item specials + let item_stat_boosts = adventurer.get_stat_boosts(name_storage1, name_storage2); + // apply item stat boosts + adventurer.apply_stat_boosts(item_stat_boosts); + } + } + + fn _remove_equipment_stat_boosts( + self: @ContractState, ref adventurer: Adventurer, adventurer_id: felt252 + ) { + if adventurer.has_item_specials() { + // get specials from storage + let (name_storage1, name_storage2) = _get_special_storages(self, adventurer_id); + // get stat boosts from item specials + let item_stat_boosts = adventurer.get_stat_boosts(name_storage1, name_storage2); + // remove item stat boosts + adventurer.remove_stat_boosts(item_stat_boosts); + } + } #[inline(always)] - fn _pack_adventurer_meta( + fn _save_adventurer_metadata( ref self: ContractState, adventurer_id: felt252, adventurer_meta: AdventurerMetadata ) { self._adventurer_meta.write(adventurer_id, adventurer_meta); @@ -2382,7 +2399,7 @@ mod Game { } #[inline(always)] - fn _pack_loot_special_names_storage( + fn _save_item_specials( ref self: ContractState, adventurer_id: felt252, storage_index: u8, @@ -2412,7 +2429,7 @@ mod Game { fn _get_item_specials( self: @ContractState, adventurer_id: felt252, item: ItemPrimitive ) -> ItemSpecials { - if (item.get_greatness() >= 15) { + if (item.get_greatness() >= SUFFIX_UNLOCK_GREANTESS) { ImplItemSpecials::get_specials( _get_specials_storage(self, adventurer_id, _get_storage_index(self, item.metadata)), item @@ -2594,21 +2611,17 @@ mod Game { #[inline(always)] fn _get_potion_price(self: @ContractState, adventurer_id: felt252) -> u16 { - let (adventurer, _, _) = _unpack_adventurer_and_bag_with_stat_boosts(self, adventurer_id); - adventurer.charisma_adjusted_potion_price() + _load_adventurer(self, adventurer_id).charisma_adjusted_potion_price() } fn _get_item_price(self: @ContractState, adventurer_id: felt252, item_id: u8) -> u16 { - let (adventurer, _, _) = _unpack_adventurer_and_bag_with_stat_boosts(self, adventurer_id); let base_item_price = ImplMarket::get_price(ImplLoot::get_tier(item_id)); - let charisma_adjusted_price = adventurer.charisma_adjusted_item_price(base_item_price); - - charisma_adjusted_price + _load_adventurer(self, adventurer_id).charisma_adjusted_item_price(base_item_price) } fn _get_attacking_beast(self: @ContractState, adventurer_id: felt252) -> Beast { // get adventurer - let adventurer = _unpack_adventurer(self, adventurer_id); + let adventurer = _load_adventurer_no_boosts(self, adventurer_id); // assert adventurer is in battle assert(adventurer.beast_health != 0, messages::NOT_IN_BATTLE); @@ -2687,7 +2700,7 @@ mod Game { #[inline(always)] fn _get_adventurer_entropy(self: @ContractState, adventurer_id: felt252) -> felt252 { // get the block the adventurer started the game on - let start_block = _unpack_adventurer_meta(self, adventurer_id).start_block; + let start_block = _load_adventurer_metadata(self, adventurer_id).start_block; // adventurer_ let chain_id = starknet::get_execution_info().unbox().tx_info.unbox().chain_id; @@ -2723,7 +2736,7 @@ mod Game { } fn _get_reveal_block(self: @ContractState, adventurer_id: felt252) -> u64 { - let start_block = _unpack_adventurer_meta(self, adventurer_id).start_block; + let start_block = _load_adventurer_metadata(self, adventurer_id).start_block; let chain_id = starknet::get_execution_info().unbox().tx_info.unbox().chain_id; // wait a full 11 blocks on mainnet so that we can do the minimum current_block - 10 and still get a future block if chain_id == MAINNET_CHAIN_ID { diff --git a/contracts/loot/src/constants.cairo b/contracts/loot/src/constants.cairo index 542b114d5..20e3cf988 100644 --- a/contracts/loot/src/constants.cairo +++ b/contracts/loot/src/constants.cairo @@ -1,5 +1,3 @@ -const NUM_ITEMS: u8 = 101; - mod ItemId { const Pendant: u8 = 1; const Necklace: u8 = 2; @@ -347,8 +345,11 @@ mod ItemSuffix { const of_the_Twins: u8 = 16; } - // suffix and prefix const NamePrefixLength: u8 = 69; // requires 7 bits const NameSuffixLength: u8 = 18; // requires 5 bits const ItemSuffixLength: u8 = 16; // requires 4 bits + +const NUM_ITEMS: u8 = 101; +const SUFFIX_UNLOCK_GREANTESS: u8 = 15; +const PREFIXES_UNLOCK_GREANTESS: u8 = 19;