-
Notifications
You must be signed in to change notification settings - Fork 50
[FEAT]: Implement store.cairo as data layer between models and systems #101
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Hey @coxmars, I left some comments with a few questions in the |
|
I think that since I was assigned this issue (#81), we could try merging that one first and then add the changes to the store. What do you think? |
Yeah you are right, we need to have other implementations before the final "store" so go ahead and then we can see what else is needed in the store. |
| fn init_beast_attacks(ref self: Store, beast_type: BeastType) { | ||
| match beast_type { | ||
| BeastType::Light => {}, | ||
| BeastType::Magic => {}, | ||
| BeastType::Shadow => {}, | ||
| _ => { panic!("[Store] - BeastType `{}` cannot be initialize.", beast_type); }, | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have two questions about this: the project includes two files that reference BeastType which one should I use? Also, what are the default skills assigned to each beast?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which two files?
Here are the preferred attacks for each beast type:
-
Light type:
- Beam
- Slash
- Pierce
- Wave
-
Magic type:
- Blast
- Freeze
- Burn
- Punch
-
Shadow type:
- Smash
- Crush
- Shock
- Kick
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/types/beast.cairo
#[derive(Copy, Drop, Serde, Debug, PartialEq)]
pub enum BeastType {
Fire,
Water,
Earth,
Electric,
Dragon,
Ice,
Magic,
Rock,
Undefined,
}
/types/beast_type.cairo
#[derive(Introspect, Copy, Drop, Serde, Debug, PartialEq)]
pub enum BeastType {
Light,
Magic,
Shadow,
}
As far as I can see the one I have to use is beast_type.cairo right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes you are right, use /types/beast_type.cairo
| fn award_battle_experience(ref self: Store) {} | ||
| fn is_attack_usable(ref self: Store) {} | ||
| fn update_player_battle_result(ref self: Store) {} | ||
| fn process_attack(ref self: Store) {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you give me more details on these methods? I think there are some that cannot be implemented yet, but we can make a first approach.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, my draft implementation idea for these methods was something like this, based on that approach you can improve it or understand the intention of these methods:
- award_battle_experience 👇🏼
// Handle battle experience reward
fn award_battle_experience(mut self: Store, beast_id: u16, exp_amount: u16) -> bool {
// Read beast and its stats
let mut beast = self.read_beast(beast_id);
// Add experience
beast.experience += exp_amount;
// Check if level up is needed
let exp_needed = ExperienceCalculatorTrait::calculate_exp_needed_for_level(beast.level);
let level_up_occurred = beast.experience >= exp_needed;
if level_up_occurred {
// Calculate remaining exp
beast
.experience =
ExperienceCalculatorTrait::remaining_exp_after_level_up(
beast.level, beast.experience,
);
// Increase level
beast.level += 1;
// Update beast stats
let mut beast_stats = self.read_beast_stats(beast_id);
beast_stats.level_up(beast.beast_type);
self.write_beast_stats(@beast_stats);
}
// Save changes
self.write_beast(@beast);
level_up_occurred
}- is_attack_usable 👇🏼
// Verify if an attack is usable by a beast (exists and meets level requirement)
fn is_attack_usable(self: Store, beast_id: u16, attack_id: u8) -> bool {
let beast = self.read_beast(beast_id);
let attack = self.read_attack(beast_id, attack_id);
beast.level >= attack.min_level_required
}- update_player_battle_result 👇🏼
// Update player battle stats
fn update_player_battle_result(mut self: Store, won: bool) {
let mut player = self.read_player();
if won {
player.battles_won += 1;
} else {
player.battles_lost += 1;
}
player.last_active_day = Timestamp::unix_timestamp_to_day(get_block_timestamp());
self.write_player(@player);
}- process_attack 👇🏼
// Process attack in battle
fn process_attack(
mut self: Store,
battle_id: u64,
attacker_beast_id: u16,
defender_beast_id: u16,
attack_id: u8,
) -> (u16, bool, bool) {
// Read battle
let mut battle = self.read_battle(battle_id);
assert(battle.battle_status == constants::BATTLE_IN_PROGRESS, 'Battle not in progress');
// Verify attack is usable
assert(self.is_attack_usable(attacker_beast_id, attack_id), 'Attack not usable');
// Read beast data
let mut attacker_beast = self.read_beast(attacker_beast_id);
let mut defender_beast = self.read_beast(defender_beast_id);
// Read beast stats
let mut attacker_stats = self.read_beast_stats(attacker_beast_id);
let mut defender_stats = self.read_beast_stats(defender_beast_id);
// Check if attacker can attack
assert(attacker_stats.can_attack(), 'Beast cannot attack');
// Calculate damage
let level_u16: u16 = attacker_beast.level.into();
let (damage, is_favored, is_effective) = attacker_beast
.calculate_attack_damage(
attack_id, defender_beast.beast_type, (attacker_stats.attack, level_u16),
);
// Apply damage
defender_stats.take_damage(damage);
// Update battle timestamp
battle.update_timestamp();
// Check if battle is over
if defender_stats.is_defeated() {
// Determine if player won
let player_won = defender_beast_id == battle.ai_beast_id;
// End battle
battle.end(player_won);
// Update player stats
self.update_player_battle_result(player_won);
// Award experience if player won
if player_won {
self.award_battle_experience(attacker_beast_id, constants::BASE_EXP_GAIN);
}
}
// Save changes
self.write_battle(@battle);
self.write_beast_stats(@attacker_stats);
self.write_beast_stats(@defender_stats);
(damage, is_favored, is_effective)
}There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the drafts were really helpful. Most of the functionality is already implemented, although a few traits are still missing. If it’s alright with you, I’ll add those in this PR.
I’ve left some comments with questions and TODOs. I’ll follow up with a separate comment for each one so we can address them individually.
| // Update player stats | ||
| self.update_player_battle_result(won: true); | ||
|
|
||
| // TODO: Define base experience for winning a game |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same for this case, if there’s any documentation available or if you think it would make sense to include it, I’d be happy to add it in this PR
|
Hi @dubzn sorry for the late response, instead of answering some of your questions I better explain here, for the store we still depend on other implementations like the beast_stats.cairo model trait, I already unassigned that issue so don't you want to implement that better first and then continue with this store? I already have a draft of this impl so you can do it in a quickly way 🫡 Here is the issue: #83 |
Ok, assign me the issue, is there a branch or what do you mean by draft? |
|
Hey guys, I just submitted a PR with the beast_stats changes, could you please take a look so I can move forward and wrap this up? #115 |
Hey @coxmars, there are still a few pending TODOs related to the beast’s experience rewards, but I think it makes sense to handle them in a separate issue. |
coxmars
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dubzn LGTM, thanks for your hard work 🫡
Pull Request Overview
📝 Summary
This PR implements the
storethat manages the models, wrapping the functionality ofwrite_modelandread_model. It also includes some methods that make it easier to create or use certain models.Related Issues
Type of Change
Mark with an
xall the checkboxes that apply (like[x]).🔄 Changes Made
What's Changed
Implement functions to access and save all models in the project, and also add methods that could be useful for logic currently used or for future use.
Implementation Details
Add the next methods to
store.cairofn new(world: WorldStorage) -> Storefn init_beast_attacks(ref self: Store, beast_type: BeastType)fn new_player(ref self: Store) -> Playerfn new_attack(ref self: Store)fn new_beast(ref self: Store, beast_id: u16, beast_type: BeastType) -> Beastfn new_beast_stats(ref self: Store, beast_id: u16, max_hp: u16, current_hp: u16, attack: u16, defense: u16, speed: u16, accuracy: u8, evasion: u8, status_condition: StatusCondition) -> BeastStatsfn new_battle(ref self: Store, battle_id: u256, player1: ContractAddress, player2: ContractAddress, battle_type: u8) -> Battlefn create_rematch(ref self: Store, battle_id: u256) -> Battlefn read_player(self: @Store) -> Playerfn read_player_from_address(self: @Store, player_address: ContractAddress) -> Playerfn read_attack(self: @Store, beast_id: u16, attack_id: u16)fn read_beast(self: @Store, beast_id: u16) -> Beastfn read_ai_beast(self: @Store)fn read_beast_stats(self: @Store, beast_id: u16) -> BeastStatsfn read_battle(self: @Store, battle_id: u256) -> Battlefn write_player(ref self: Store, player: Player)fn write_attack(ref self: Store, attack: u32)fn write_beast(ref self: Store, beast: Beast)fn write_beast_stats(ref self: Store, beast_stats: BeastStats)fn write_battle(ref self: Store, battle: Battle)Change the visibility of some models in
lib.cairo.🔧 Tests Results
Test Coverage
Evidence
Testing Notes
🔜 Next Steps