From 36e15c770c19dbc999d3cb5d3d18ac810430b24f Mon Sep 17 00:00:00 2001 From: KevinMB0220 <130603817+KevinMB0220@users.noreply.github.com> Date: Thu, 31 Oct 2024 00:32:20 -0600 Subject: [PATCH] feat-documentation --- src/models/battle.cairo | 19 ++++++ src/systems/battle.cairo | 118 +++++++++++++++++++++++------------- src/tests/test_battle.cairo | 87 +++++++++++++++++--------- 3 files changed, 153 insertions(+), 71 deletions(-) diff --git a/src/models/battle.cairo b/src/models/battle.cairo index 0e9fc53..8c87ecc 100644 --- a/src/models/battle.cairo +++ b/src/models/battle.cairo @@ -1,20 +1,39 @@ +// Defines the `Battle` Model, which represents a battle between two players. +// Includes various fields to track the state and progress of the battle. #[derive(Copy, Drop, Serde)] #[dojo::model] pub struct Battle { + // Unique identifier for the battle. #[key] pub battle_id: u32, + + // ID of the player involved in the battle. pub player_id: u32, + + // ID of the opponent involved in the battle. pub opponent_id: u32, + + // ID of the active beast for the player. pub active_beast_player: u32, + + // ID of the active beast for the opponent. pub active_beast_opponent: u32, + + // Flag to indicate if the battle is currently active (1 for active, 0 for inactive). pub battle_active: u32, + + // Current turn number in the battle. pub turn: u32, } + #[cfg(test)] mod tests { + use bytebeasts::{models::{battle::Battle},}; + + #[test] fn test_battle_initialization() { let battle = Battle { diff --git a/src/systems/battle.cairo b/src/systems/battle.cairo index ce9a915..a57cdb1 100644 --- a/src/systems/battle.cairo +++ b/src/systems/battle.cairo @@ -1,18 +1,28 @@ +// Required modules and models for the battle system. use bytebeasts::{ models::{beast::Beast, mt::Mt, player::Player, battle::Battle, potion::Potion}, }; +// Interface defining battle-related actions. #[dojo::interface] trait IBattleActions { - fn init_battle(ref world: IWorldDispatcher, player_id: u32, opponent_id: u32) -> u32; - fn check_flee_success(player_beast: Beast, opponent_beast: Beast) -> bool; - fn calculate_damage(mt: Mt, attacker: Beast, defender: Beast) -> u32; - fn opponent_turn(ref world: IWorldDispatcher, battle_id: u32); - fn attack(ref world: IWorldDispatcher, battle_id: u32, mt_id: u32); - fn use_potion(ref world: IWorldDispatcher, battle_id: u32, potion_id: u32); + // Initializes a battle + fn init_battle(ref world: IWorldDispatcher, player_id: u32, opponent_id: u32) -> u32; + // Checks flee success + fn check_flee_success(player_beast: Beast, opponent_beast: Beast) -> bool; + // Calculates damage + fn calculate_damage(mt: Mt, attacker: Beast, defender: Beast) -> u32; + // Handles opponent's turn + fn opponent_turn(ref world: IWorldDispatcher, battle_id: u32); + // Executes an attack + fn attack(ref world: IWorldDispatcher, battle_id: u32, mt_id: u32); + // Uses a potion + fn use_potion(ref world: IWorldDispatcher, battle_id: u32, potion_id: u32); + // Attempts to flee fn flee(ref world: IWorldDispatcher, battle_id: u32); } +// Contract implementing battle actions. #[dojo::contract] mod battle_system { use super::{IBattleActions}; @@ -20,31 +30,34 @@ mod battle_system { models::{beast::Beast, mt::Mt, player::Player, battle::Battle, potion::Potion}, }; + // Event emitted for battle status updates. #[derive(Copy, Drop, Serde)] #[dojo::model] #[dojo::event] struct StatusBattle { #[key] - battle_id: u32, - message: felt252, + battle_id: u32, // Battle identifier + message: felt252, // Event message } + // Event emitted for player status updates. #[derive(Copy, Drop, Serde)] #[dojo::model] #[dojo::event] struct Status { #[key] - player_id: u32, - message: felt252, + player_id: u32, // Player identifier + message: felt252, // Event message } #[abi(embed_v0)] impl BattleActionsImpl of IBattleActions { + // Initializes a new battle. fn init_battle(ref world: IWorldDispatcher, player_id: u32, opponent_id: u32) -> u32 { - let player = get!(world, player_id, (Player)); - let opponent = get!(world, opponent_id, (Player)); - let active_beast_player = get!(world, player.beast_1, (Beast)); - let active_beast_opponent = get!(world, opponent.beast_1, (Beast)); + let player = get!(world, player_id, (Player)); // Fetch player data + let opponent = get!(world, opponent_id, (Player)); // Fetch opponent data + let active_beast_player = get!(world, player.beast_1, (Beast)); // Player's beast + let active_beast_opponent = get!(world, opponent.beast_1, (Beast)); // Opponent's beast let battle_created_id = 1; // Hardcoded for now set!( @@ -61,17 +74,19 @@ mod battle_system { ); let message = 'Battle started'; - emit!(world, (Status { player_id: player_id, message: message })); + emit!(world, (Status { player_id: player_id, message: message })); // Emit player status - emit!(world, (StatusBattle { battle_id: battle_created_id, message: message })); + emit!(world, (StatusBattle { battle_id: battle_created_id, message: message })); // Emit battle status - return battle_created_id; + return battle_created_id; // Return battle ID } + // Checks if the player can flee based on beast levels. fn check_flee_success(player_beast: Beast, opponent_beast: Beast) -> bool { player_beast.level > opponent_beast.level } + // Calculates the damage dealt by an attacker to a defender. fn calculate_damage(mt: Mt, attacker: Beast, defender: Beast) -> u32 { let base_damage = mt.mt_power * attacker.attack / defender.defense; @@ -83,98 +98,119 @@ mod battle_system { let hit_chance = 80_u32; // Hardcoded for now if hit_chance > mt.mt_accuracy { - return 0_u32; + return 0_u32; // Misses if hit chance is greater than accuracy } effective_damage } + // Executes the opponent's turn in the battle. fn opponent_turn(ref world: IWorldDispatcher, battle_id: u32) { - let mut battle = get!(world, battle_id, (Battle)); + let mut battle = get!(world, battle_id, (Battle)); // Retrieve battle details let mut player_beast = get!(world, battle.active_beast_player, (Beast)); let mut opponent_beast = get!(world, battle.active_beast_opponent, (Beast)); - let opponent_attack = get!(world, opponent_beast.mt1, (Mt)); + let opponent_attack = get!(world, opponent_beast.mt1, (Mt)); // Opponent's chosen move + // Calculate damage dealt by the opponent to the player let damage = self.calculate_damage(opponent_attack, opponent_beast, player_beast); if damage >= player_beast.current_hp { - player_beast.current_hp = 0; + player_beast.current_hp = 0; // Knock out player’s beast if damage exceeds current HP } else { - player_beast.current_hp -= damage; + player_beast.current_hp -= damage; // Subtract damage from player's beast HP } - set!(world, (player_beast)); + set!(world, (player_beast)); // Update player's beast in the world + // Check if the player's beast is knocked out if player_beast.current_hp <= 0_u32 { let message = 'Player Beast Knocked Out!'; - emit!(world, (StatusBattle { battle_id, message })); - battle.battle_active = 0; - set!(world, (battle)); + emit!(world, (StatusBattle { battle_id, message })); // Emit battle status update + battle.battle_active = 0; // Emit battle status update + set!(world, (battle)); // Update battle status in the world } } + // Executes the player's attack in the battle. fn attack(ref world: IWorldDispatcher, battle_id: u32, mt_id: u32) { - let mut battle = get!(world, battle_id, (Battle)); + let mut battle = get!(world, battle_id, (Battle)); // Retrieve battle details + // Fetch the player's and opponent's active beasts and the chosen move (mt) of the player let player_beast = get!(world, battle.active_beast_player, (Beast)); let mut opponent_beast = get!(world, battle.active_beast_opponent, (Beast)); let mt = get!(world, mt_id, (Mt)); + // Calculate damage dealt by the player to the opponent let damage = self.calculate_damage(mt, player_beast, opponent_beast); + // Apply damage to opponent's beast HP if damage >= opponent_beast.current_hp { - opponent_beast.current_hp = 0; + opponent_beast.current_hp = 0; // Knock out opponent’s beast if damage exceeds current HP } else { - opponent_beast.current_hp -= damage; + opponent_beast.current_hp -= damage; // Subtract damage from opponent's beast HP } - set!(world, (opponent_beast)); + set!(world, (opponent_beast)); // Update opponent's beast in the world + // Check if the opponent's beast is knocked out if opponent_beast.current_hp <= 0_u32 { let message = 'Opponent Beast Knocked Out!'; - emit!(world, (StatusBattle { battle_id, message })); - battle.battle_active = 0; - set!(world, (battle)); + emit!(world, (StatusBattle { battle_id, message })); // Emit battle status update + battle.battle_active = 0; // End the battle + set!(world, (battle)); // Update battle status in the world } else { let message = 'Attack Performed!'; - emit!(world, (StatusBattle { battle_id, message })); + emit!(world, (StatusBattle { battle_id, message })); // Emit message indicating the attack was performed } + } + // Allows the player to use a potion on their beast to restore health. fn use_potion(ref world: IWorldDispatcher, battle_id: u32, potion_id: u32) { - let mut battle = get!(world, battle_id, (Battle)); + let mut battle = get!(world, battle_id, (Battle)); // Retrieve battle details + // Fetch the player's active beast and the potion being used let mut player_beast = get!(world, battle.active_beast_player, (Beast)); let potion = get!(world, potion_id, (Potion)); + // Restore health points based on potion's effect without exceeding max HP if potion.potion_effect <= player_beast.current_hp { - player_beast.current_hp += potion.potion_effect; + player_beast.current_hp += potion.potion_effect; // Add potion effect to current HP } else { - player_beast.current_hp = player_beast.hp; + player_beast.current_hp = player_beast.hp; // Cap HP to the beast's max health } - set!(world, (player_beast)); + set!(world, (player_beast)); // Update the player's beast in the world player_beast = get!(world, battle.active_beast_player, (Beast)); + // Emit an event to notify that the potion has been used let message = 'Item Used!'; emit!(world, (StatusBattle { battle_id, message })); } + // Attempts to flee the battle. Success is determined by the relative levels of the player and opponent beasts. fn flee(ref world: IWorldDispatcher, battle_id: u32) { - let mut battle = get!(world, battle_id, (Battle)); + let mut battle = get!(world, battle_id, (Battle)); // Retrieve battle details + // Fetch both player's and opponent's active beasts let player_beast = get!(world, battle.active_beast_player, (Beast)); let opponent_beast = get!(world, battle.active_beast_opponent, (Beast)); + // Determine if flee attempt is successful based on beast levels let flee_success = self.check_flee_success(player_beast, opponent_beast); if flee_success { - battle.battle_active = 0; - set!(world, (battle)); + battle.battle_active = 0; // Set battle as inactive upon successful flee + set!(world, (battle)); // Update battle status in the world + + // Emit event indicating the player successfully fled the battle let message = 'Player Fled!'; emit!(world, (StatusBattle { battle_id, message })); } else { + // Emit event indicating flee attempt failed let message = 'Flee failed!'; emit!(world, (StatusBattle { battle_id, message })); } + } } } + diff --git a/src/tests/test_battle.cairo b/src/tests/test_battle.cairo index c8b4c80..1c1dd72 100644 --- a/src/tests/test_battle.cairo +++ b/src/tests/test_battle.cairo @@ -18,9 +18,7 @@ mod tests { models::world_elements::{{WorldElements}} }; - - // Helper function - // This function create the world and define the required models + // Initializes the testing environment by creating the world with the required models and deploying the battle contract. #[test] fn setup_world() -> (IWorldDispatcher, IBattleActionsDispatcher) { let mut models = array![ @@ -32,24 +30,28 @@ mod tests { ]; + // Spawns a test world with the specified name and models let world = spawn_test_world("bytebeasts", models); + // Deploys the battle system contract and retrieves its address let contract_address = world.deploy_contract('salt', battle_system::TEST_CLASS_HASH.try_into().unwrap()); + // Initializes the battle system dispatcher with the deployed contract address let battle_system = IBattleActionsDispatcher { contract_address }; + // Grants write permissions for the battle system contract world.grant_writer(dojo::utils::bytearray_hash(@"bytebeasts"), contract_address); + // Returns the world instance and battle system dispatcher (world, battle_system) } - // Helper function - // This function set all the required models - // with their values in the world + // Initializes a battle setup with two players and their beasts, as well as moves and a potion. #[test] fn setup_battle() -> (IWorldDispatcher, IBattleActionsDispatcher) { let (world, battle_system) = setup_world(); + // Define Player 1 (Ash) and his beast (Firebeast) let player_ash = Player { player_id: 1, player_name: 'Ash', @@ -70,18 +72,19 @@ mod tests { current_hp: 200, attack: 7, defense: 7, - mt1: 1, // Fire Blast - mt2: 2, // Ember - mt3: 3, // Flame Wheel - mt4: 4, // Fire Punch + mt1: 1, // Move: Fire Blast + mt2: 2, // Move: Ember + mt3: 3, // Move: Flame Wheel + mt4: 4, // Move: Fire Punch level: 10, experience_to_next_level: 1000 }; + // Define Player 2 (Red) and his beast (Aqua) let opponent_red = Player { player_id: 2, player_name: 'Red', - beast_1: 2, // Beast 1 assigned + beast_1: 2, // Beast 2 assigned beast_2: 0, // No beast assigned beast_3: 0, // No beast assigned beast_4: 0, // No beast assigned @@ -98,21 +101,22 @@ mod tests { current_hp: 200, attack: 5, defense: 5, - mt1: 8, // Hydro Pump - mt2: 6, // Bubble - mt3: 7, // Aqua Tail - mt4: 5, // Water Gun + mt1: 8, // Move: Hydro Pump + mt2: 6, // Move: Bubble + mt3: 7, // Move: Aqua Tail + mt4: 5, // Move: Water Gun level: 5, experience_to_next_level: 1000 }; + // Define a potion to be used in the battle let potion = Potion { potion_id: 1, potion_name: 'Restore everything', potion_effect: 250 }; - // Set Mts + // Set moves (Mts) in the world set!( world, (Mt { @@ -201,6 +205,7 @@ mod tests { }) ); + // Register players, beasts, and potion in the world set!(world,(player_ash)); set!(world,(opponent_red)); @@ -211,15 +216,19 @@ mod tests { set!(world, (potion)); + // Initialize the battle between the two players let _ = battle_system.init_battle(player_ash.player_id, opponent_red.player_id); (world, battle_system) } + // This test verifies the initialization of a battle between two players. #[test] fn test_init_battle() { + // Setup world and battle system let (world, battle_system) = setup_world(); + // Define Player 1 (Ash) with a single beast let player_ash = Player { player_id: 1, player_name: 'Ash', @@ -230,22 +239,25 @@ mod tests { potions: 2 }; + // Define Player 2 (Red) with a single beast let opponent_red = Player { player_id: 2, player_name: 'Red', - beast_1: 2, // Beast 1 assigned + beast_1: 2, // Beast 2 assigned beast_2: 0, // No beast assigned beast_3: 0, // No beast assigned beast_4: 0, // No beast assigned potions: 2 }; + // Register players in the world set!(world,(player_ash)); - set!(world,(opponent_red)); + // Initialize battle between Ash and Red let battle_id = battle_system.init_battle(player_ash.player_id, opponent_red.player_id); + // Retrieve battle data and validate initialization values let battle = get!(world, battle_id,(Battle)); assert!(battle.battle_id == 1, "Wrong battle id"); @@ -257,45 +269,54 @@ mod tests { assert!(battle.turn == 0, "Wrong turn"); } + + // This test simulates a full battle scenario where the player wins. #[test] fn test_battle_player_wins() { println!("-> Battle Started!"); + + // Initialize the battle setup with a predefined player and opponent let (world, battle_system) = setup_battle(); let battle_id = 1; + // Retrieve initial battle and beast states let mut battle = get!(world, battle_id, (Battle)); let mut player_beast = get!(world, battle.active_beast_player, (Beast)); let mut opponent_beast = get!(world, battle.active_beast_opponent, (Beast)); + + // Display initial health points for clarity println!("-> Player beast: {}", player_beast.beast_name); println!("-> Health points: {}", player_beast.hp); println!("-> Opponent beast: {}", opponent_beast.beast_name); println!("-> Health points: {}", opponent_beast.hp); + // Retrieve move (MT) for both player and opponent beasts let mt_player_beast_id = player_beast.mt1; let mt_player_beast = get!(world, mt_player_beast_id, (Mt)); let mt_opponent_beast_id = opponent_beast.mt1; let mt_opponent_beast = get!(world, mt_opponent_beast_id, (Mt)); + // Calculate damage for initial attacks from both sides let player_beast_damage = battle_system.calculate_damage(mt_player_beast, player_beast, opponent_beast); let opponent_beast_damage = battle_system.calculate_damage(mt_opponent_beast, opponent_beast, player_beast); - // attack + // Player's turn to attack battle_system.attack(battle_id, mt_player_beast_id); opponent_beast = get!(world, battle.active_beast_opponent, (Beast)); assert!(opponent_beast.current_hp == opponent_beast.hp - player_beast_damage, "Wrong opponent beast health"); println!("-> Player has performed an attack with MT: {}", mt_player_beast.mt_name); println!("-> Opponent beast health points: {}", opponent_beast.current_hp); - // opponent turn + // Opponent's turn to attack battle_system.opponent_turn(battle_id); player_beast = get!(world, battle.active_beast_player, (Beast)); assert!(player_beast.current_hp == player_beast.hp - opponent_beast_damage, "Wrong player beast health"); println!("-> Opponent has performed an attack with MT: {}", mt_opponent_beast.mt_name); println!("-> Player beast health points: {}", player_beast.current_hp); - // use potion + // Player uses a potion to restore health let potion_id = 1; let potion = get!(world, potion_id, (Potion)); battle_system.use_potion(battle_id, potion_id); @@ -304,7 +325,7 @@ mod tests { println!("-> Player used a {} potion", potion.potion_name); println!("-> Player beast health points after potion: {}", player_beast.current_hp); - // attack with fire punch(one shot one kill) + // Final attack with a powerful move, knocking out the opponent battle_system.attack(battle_id, 4); opponent_beast = get!(world, battle.active_beast_opponent, (Beast)); assert!(opponent_beast.current_hp == 0, "Wrong opponent beast health"); @@ -312,59 +333,65 @@ mod tests { println!("-> Opponent beast health points: {}", opponent_beast.current_hp); println!("-> Player wins the battle!"); - + // Verify that the battle is no longer active battle = get!(world, battle_id, (Battle)); assert!(battle.battle_active == 0, "Wrong battle status"); } - + // This test simulates a battle scenario where the opponent wins. #[test] fn test_battle_opponent_wins() { + // Initialize the battle with a predefined player and opponent let (world, battle_system) = setup_battle(); let battle_id = 1; + // Retrieve initial battle and beast states let mut battle = get!(world, battle_id, (Battle)); let mut player_beast = get!(world, battle.active_beast_player, (Beast)); let mut opponent_beast = get!(world, battle.active_beast_opponent, (Beast)); + // Retrieve and calculate damage for player’s initial attack let mt_player_beast_id = player_beast.mt1; let mt_player_beast = get!(world, mt_player_beast_id, (Mt)); - let player_beast_damage = battle_system.calculate_damage(mt_player_beast, player_beast, opponent_beast); - // attack + // Player attacks the opponent battle_system.attack(battle_id, mt_player_beast_id); opponent_beast = get!(world, battle.active_beast_opponent, (Beast)); assert!(opponent_beast.current_hp == opponent_beast.hp - player_beast_damage, "Wrong opponent beast health"); - // Update Mt to be powerfull + // Final attack with a powerful move, knocking out the opponent let mut mt_opponent_beast = get!(world, 8, (Mt)); // Opponent always attack with mt1 mt_opponent_beast.mt_power = 500; set!(world,(mt_opponent_beast)); - // opponent turn + // Opponent's turn to attack battle_system.opponent_turn(battle_id); player_beast = get!(world, battle.active_beast_player, (Beast)); assert!(player_beast.current_hp == 0, "Wrong player beast health"); + // Verify that the battle is no longer active battle = get!(world, battle_id, (Battle)); assert!(battle.battle_active == 0, "Wrong battle status"); } + // This test simulates the scenario where the player attempts to flee the battle. #[test] fn test_flee() { + // Initialize the battle setup let (world, battle_system) = setup_battle(); let battle_id = 1; - // flee + // Attempt to flee battle_system.flee(battle_id); let battle = get!(world, battle_id, (Battle)); assert!(battle.battle_active == 0, "Wrong battle status"); } + // Verifies that the setup functions execute correctly. #[test] fn test_setup() { let (_, _,) = setup_world(); let (_, _,) = setup_battle(); } -} \ No newline at end of file +}