Skip to content
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

feat-documentation #85

Closed
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions src/models/battle.cairo
Original file line number Diff line number Diff line change
@@ -1,20 +1,50 @@
// ***************************************************************
// MODEL DEFINITION
// ***************************************************************

// 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,
}

// ***************************************************************
// TEST MODULE
// ***************************************************************

#[cfg(test)]
mod tests {

// ***************************************************************
// TEST CASE: Battle Initialization
// ***************************************************************

use bytebeasts::{models::{battle::Battle},};



#[test]
fn test_battle_initialization() {
let battle = Battle {
Expand Down
172 changes: 122 additions & 50 deletions src/systems/battle.cairo
Original file line number Diff line number Diff line change
@@ -1,52 +1,88 @@
// ***************************************************************
// IMPORTS
// ***************************************************************

// Required modules and models for the battle system.
use bytebeasts::{
models::{beast::Beast, mt::Mt, player::Player, battle::Battle, potion::Potion},
};

// ***************************************************************
// INTERFACE
// ***************************************************************

// 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);
fn flee(ref world: IWorldDispatcher, battle_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 MODULE
// ***************************************************************

// Contract implementing battle actions.
#[dojo::contract]
mod battle_system {
use super::{IBattleActions};

use super::{IBattleActions};
use bytebeasts::{
models::{beast::Beast, mt::Mt, player::Player, battle::Battle, potion::Potion},
};

// ***************************************************************
// EVENTS
// ***************************************************************

/// 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<ContractState> {
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 battle_created_id = 1; // Hardcoded for now
// ***************************************************************
// Battle Actions
// ***************************************************************

#[abi(embed_v0)]
impl BattleActionsImpl of IBattleActions<ContractState> {

// Initializes a new battle.
fn init_battle(ref world: IWorldDispatcher, player_id: u32, opponent_id: u32) -> u32 {
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; // Temporarily hardcoded battle ID
set!(
world,
(Battle {
Expand All @@ -61,117 +97,153 @@ 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;

let base_damage = mt.mt_power * attacker.attack / defender.defense; // Basic damage formula

//TODO: extend with effectivity by type, randomness, etc
let effective_damage = base_damage;

let effective_damage = base_damage; // Placeholder for effectiveness adjustments

// TODO: investigate how can we make It random
// let hit_chance = random_felt252() % 100_u32;

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
}


// ***************************************************************
// Opponent's Turn
// ***************************************************************

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

// ***************************************************************
// Player Attack
// ***************************************************************
// 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
}
}
}

// ***************************************************************
// Use Potion
// ***************************************************************
// 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 }));
}

// ***************************************************************
// Flee Action
// ***************************************************************
danielcdz marked this conversation as resolved.
Show resolved Hide resolved
// 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 }));
}
Expand Down