diff --git a/contract/src/models/player.cairo b/contract/src/models/player.cairo index 8ec1003..bc3a968 100644 --- a/contract/src/models/player.cairo +++ b/contract/src/models/player.cairo @@ -9,6 +9,7 @@ pub struct Player { pub level: u8, pub xp: u32, pub current_wave: u32, + pub in_game: bool, } #[derive(Copy, Drop, Serde, Debug)] @@ -46,7 +47,7 @@ pub impl PlayerAssert of AssertTrait { pub impl ZeroablePlayerTrait of Zero { #[inline(always)] fn zero() -> Player { - Player { address: ZERO_ADDRESS(), level: 0, xp: 0, current_wave: 0 } + Player { address: ZERO_ADDRESS(), level: 0, xp: 0, current_wave: 0, in_game: false } } #[inline(always)] @@ -61,7 +62,7 @@ pub impl ZeroablePlayerTrait of Zero { } pub fn spawn_player(address: ContractAddress) -> Player { - Player { address, level: 1, xp: 0, current_wave: 1 } + Player { address, level: 1, xp: 0, current_wave: 1, in_game: true } } #[cfg(test)] @@ -78,6 +79,7 @@ mod tests { assert(player.level == 1, 'Invalid level'); assert(player.xp == 0, 'Invalid xp'); assert(player.current_wave == 1, 'Invalid wave'); + assert(player.in_game == true, 'Invalid in_game'); } #[test] @@ -85,5 +87,6 @@ mod tests { let zero_player = ZeroablePlayerTrait::zero(); assert(zero_player.is_zero(), 'Should be zero'); assert(zero_player.level == 0, 'Level should be zero'); + assert(zero_player.in_game == false, 'in_game should be false'); } } diff --git a/contract/src/systems/game.cairo b/contract/src/systems/game.cairo index 64a9b00..eafca10 100644 --- a/contract/src/systems/game.cairo +++ b/contract/src/systems/game.cairo @@ -19,11 +19,12 @@ pub trait IBrawlGame { ) -> u256; } -#[derive(Copy, Drop, Serde)] +#[derive(Copy, Default, Drop, Serde)] pub enum PlayerStatus { Alive, Dead, InGame, + #[default] Waiting, } @@ -47,6 +48,8 @@ pub mod brawl_game { use stark_brawl::store::{Store, StoreImpl}; use stark_brawl::systems::player::{IPlayerSystemDispatcher, IPlayerSystemDispatcherTrait}; + use core::num::traits::Bounded; + #[storage] struct Storage { game_counter: u32, @@ -63,10 +66,17 @@ pub mod brawl_game { fn join_game(ref self: ContractState) { let mut world = self.world_default(); let caller = get_caller_address(); + let existing_player: Player = world.read_model(caller); - let player = spawn_player(caller); - - world.write_model(@player); + if existing_player.is_zero() { + let player = spawn_player(caller); + world.write_model(@player); + } else { + assert(!existing_player.in_game, 'Player already in active game'); + let mut player = existing_player; + player.in_game = true; + world.write_model(@player); + } let player_system_dispatcher = self.player_system_dispatcher(); player_system_dispatcher.initialize(caller); @@ -81,6 +91,7 @@ pub mod brawl_game { // Check if the player exists let player: Player = world.read_model(caller); assert(!player.is_zero(), 'Player does not exist'); + assert(player.in_game, 'Player not in active game'); // Check if the ability exists let ability: Ability = world.read_model(ability_id); @@ -136,15 +147,37 @@ pub mod brawl_game { } fn take_damage(ref self: ContractState, amount: u32) { - // let mut world = self.world_default(); + let caller = get_caller_address(); + let mut world = self.world_default(); + let player: Player = world.read_model(caller); + + assert(!player.is_zero(), 'Player does not exist'); + assert(player.in_game, 'Player not in active game'); + let player_system_dispatcher = self.player_system_dispatcher(); - player_system_dispatcher.take_damage(get_caller_address(), amount.try_into().unwrap()); + assert(amount <= Bounded::MAX, 'Damage too large'); + + let damage_amount: u16 = amount.try_into().unwrap(); + player_system_dispatcher.take_damage(caller, damage_amount); + + // Check if died and cleanup game session + if !player_system_dispatcher.is_alive(caller) { + let mut dead_player = player; + dead_player.in_game = false; + world.write_model(@dead_player); + } } fn attack_enemy(ref self: ContractState, enemy_id: u64, damage: u32) { let caller = get_caller_address(); let world = self.world_default(); + let player: Player = world.read_model(caller); + assert(!player.is_zero(), 'Player does not exist'); + assert(player.in_game, 'Player not in active game'); + + let player_system_dispatcher = self.player_system_dispatcher(); + assert(player_system_dispatcher.is_alive(caller), 'Player not alive'); // Instantiate the Store to interact with models let mut store: Store = StoreImpl::new(world); @@ -173,9 +206,20 @@ pub mod brawl_game { fn get_player_status(ref self: ContractState) -> PlayerStatus { let caller = get_caller_address(); + let world = self.world_default(); + let player: Player = world.read_model(caller); + + if player.is_zero() { + return PlayerStatus::Waiting; + } + + let player: Player = world.read_model(caller); let player_system_dispatcher = self.player_system_dispatcher(); + let alive = player_system_dispatcher.is_alive(caller); - if player_system_dispatcher.is_alive(caller) { + if player.in_game { + PlayerStatus::InGame + } else if alive { PlayerStatus::Alive } else { PlayerStatus::Dead @@ -189,6 +233,7 @@ pub mod brawl_game { // Validate player exists let player: Player = world.read_model(caller); assert(!player.is_zero(), 'Player does not exist'); + assert(player.in_game, 'Player not in active game'); // Validate player is alive let player_system_dispatcher = self.player_system_dispatcher(); @@ -362,7 +407,7 @@ pub mod brawl_game { } fn apply_ability_effect( - self: @ContractState, + ref self: ContractState, effect_type: AbilityEffectType, effect_amount: u32, target: ContractAddress, @@ -378,6 +423,14 @@ pub mod brawl_game { AbilityEffectType::Damage => { let damage_amount: u16 = effect_amount.try_into().unwrap(); player_system_dispatcher.take_damage(target, damage_amount); + + // Check if died and cleanup game session + if !player_system_dispatcher.is_alive(target) { + let mut world = self.world_default(); + let mut target_player: Player = world.read_model(target); + target_player.in_game = false; + world.write_model(@target_player); + } }, AbilityEffectType::Heal => { let heal_amount: u16 = effect_amount.try_into().unwrap(); @@ -394,6 +447,14 @@ pub mod brawl_game { AbilityEffectType::DamageOverTime => { let damage_amount: u16 = effect_amount.try_into().unwrap(); player_system_dispatcher.take_damage(target, damage_amount); + + // Check if died and cleanup game session + if !player_system_dispatcher.is_alive(target) { + let mut world = self.world_default(); + let mut target_player: Player = world.read_model(target); + target_player.in_game = false; + world.write_model(@target_player); + } }, } }