Skip to content

Conversation

@dubzn
Copy link
Contributor

@dubzn dubzn commented Apr 27, 2025

Pull Request Overview

📝 Summary

This PR implements the store that manages the models, wrapping the functionality of write_model and read_model. It also includes some methods that make it easier to create or use certain models.

Related Issues

Type of Change

Mark with an x all the checkboxes that apply (like [x]).

  • 📝 Documentation (updates to README, docs, or comments)
  • 🐛 Bug fix (non-breaking change which fixes an issue)
  • 👌 Enhancement (non-breaking change which adds functionality)
  • 💥 Breaking change (fix or feature that would cause existing functionality to change)

🔄 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.cairo

    • fn new(world: WorldStorage) -> Store

    • fn init_beast_attacks(ref self: Store, beast_type: BeastType)

    • fn new_player(ref self: Store) -> Player

    • fn new_attack(ref self: Store)

    • fn new_beast(ref self: Store, beast_id: u16, beast_type: BeastType) -> Beast

    • fn 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) -> BeastStats

    • fn new_battle(ref self: Store, battle_id: u256, player1: ContractAddress, player2: ContractAddress, battle_type: u8) -> Battle

    • fn create_rematch(ref self: Store, battle_id: u256) -> Battle

    • fn read_player(self: @Store) -> Player

    • fn read_player_from_address(self: @Store, player_address: ContractAddress) -> Player

    • fn read_attack(self: @Store, beast_id: u16, attack_id: u16)

    • fn read_beast(self: @Store, beast_id: u16) -> Beast

    • fn read_ai_beast(self: @Store)

    • fn read_beast_stats(self: @Store, beast_id: u16) -> BeastStats

    • fn read_battle(self: @Store, battle_id: u256) -> Battle

    • fn 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

  • Unit Tests
  • Integration Tests
  • Manual Testing

Evidence

image

Testing Notes

🔜 Next Steps

@dubzn
Copy link
Contributor Author

dubzn commented Apr 27, 2025

Hey @coxmars, I left some comments with a few questions in the store.cairo file. I think there are still some things that can't be implemented yet. But I'm keeping an eye out for any comments or feedback.

@dubzn
Copy link
Contributor Author

dubzn commented Apr 27, 2025

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?

@coxmars
Copy link
Contributor

coxmars commented Apr 27, 2025

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.

Comment on lines 30 to 37
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); },
}
}
Copy link
Contributor Author

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?

Copy link
Contributor

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

Copy link
Contributor Author

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?

Copy link
Contributor

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

Comment on lines 295 to 298
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) {}
Copy link
Contributor Author

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.

Copy link
Contributor

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

Copy link
Contributor Author

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
Copy link
Contributor Author

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

@coxmars
Copy link
Contributor

coxmars commented May 9, 2025

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

@dubzn
Copy link
Contributor Author

dubzn commented May 10, 2025

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?

@dubzn
Copy link
Contributor Author

dubzn commented May 13, 2025

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

@coxmars
Copy link
Contributor

coxmars commented May 28, 2025

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

Hi @dubzn sorry for the delay, PR merged now you can continue with this store to merge it asap. Thanks for all your work and patience 🫡

@dubzn dubzn marked this pull request as ready for review June 6, 2025 20:13
@dubzn
Copy link
Contributor Author

dubzn commented Jun 6, 2025

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

Hi @dubzn sorry for the delay, PR merged now you can continue with this store to merge it asap. Thanks for all your work and patience 🫡

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.

Copy link
Contributor

@coxmars coxmars left a 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 🫡

@coxmars coxmars merged commit 13e9e49 into AkatsukiLabs:main Jun 7, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

📝 [FEAT]: Implement store.cairo as data layer between models and systems

2 participants