From 305ad88684e9ec9766dc01f4755c21ad7342d4f4 Mon Sep 17 00:00:00 2001 From: truthixify Date: Mon, 8 Sep 2025 00:41:02 +0100 Subject: [PATCH 1/3] implemented gear details struct and helper functions --- src/helpers/gear.cairo | 86 +++++++++++++++++++++++++++++- src/models/gear.cairo | 116 +++++++++++++++++++++++++++++++++++++++++ src/systems/core.cairo | 44 +++++++++++----- 3 files changed, 233 insertions(+), 13 deletions(-) diff --git a/src/helpers/gear.cairo b/src/helpers/gear.cairo index cba073c..953153c 100644 --- a/src/helpers/gear.cairo +++ b/src/helpers/gear.cairo @@ -1,4 +1,4 @@ -use crate::models::gear::GearType; +use crate::models::gear::{GearType, GearDetails, GearDetailsImpl}; use origami_random::dice::{Dice, DiceTrait}; // Helper function to calculate upgrade multipliers based on level @@ -167,3 +167,87 @@ pub fn get_min_xp_needed(gear_type: GearType) -> u256 { } } +// Helper function to generate random GearDetails +pub fn random_gear_details() -> GearDetails { + let gear_type = random_geartype(); + let min_xp_needed = get_min_xp_needed(gear_type); + let max_upgrade_level = get_max_upgrade_level(gear_type); + + // Generate random base damage (between 10 and 100 for simplicity) + let mut dice = DiceTrait::new(90, 'DAMAGE_SEED'); + let base_damage: u64 = (10 + dice.roll()).into(); + + GearDetails { + gear_type, + min_xp_needed, + base_damage, + max_upgrade_level, + total_count: 1, + variation_ref: 0, + } +} + +// Helper function to validate GearDetails array +pub fn validate_gear_details_array(details: @Array) -> bool { + let mut i = 0; + let mut valid = true; + + while i < details.len() { + let gear_details = *details.at(i); + if !gear_details.validate() { + valid = false; + break; + } + i += 1; + }; + + valid +} + +// Helper function to generate multiple GearDetails for batch spawning +pub fn generate_batch_gear_details(amount: u32) -> Array { + let mut result = array![]; + let mut i = 0; + + while i < amount { + result.append(random_gear_details()); + i += 1; + }; + + result +} + +// Tests for helper functions +#[cfg(test)] +mod tests { + use super::{GearDetailsImpl, GearType, random_gear_details, validate_gear_details_array, generate_batch_gear_details}; + + #[test] + fn test_random_gear_details() { + let gear = random_gear_details(); + assert(gear.validate(), 'Random gear should be valid'); + assert(gear.gear_type != GearType::None, 'Gear type should not be None'); + assert(gear.base_damage >= 10 && gear.base_damage <= 100, 'Base damage out of range'); + assert(gear.total_count == 1, 'Total count should be 1'); + } + + #[test] + #[should_panic(expected: 'Gear type cannot be None')] + fn test_validate_gear_details_array() { + let mut details = array![ + GearDetailsImpl::new(GearType::Sword, 30, 50, 10, 1, 1), + GearDetailsImpl::new(GearType::Helmet, 15, 0, 5, 1, 0) + ]; + assert(validate_gear_details_array(@details), 'Valid array should pass'); + + // Add invalid gear details + details.append(GearDetailsImpl::new(GearType::None, 5, 10, 1, 1, 0)); + } + + #[test] + fn test_generate_batch_gear_details() { + let details = generate_batch_gear_details(3); + assert(details.len() == 3, 'Should generate 3 gear details'); + assert(validate_gear_details_array(@details), 'Batch should be valid'); + } +} \ No newline at end of file diff --git a/src/models/gear.cairo b/src/models/gear.cairo index 9900125..0a11d8d 100644 --- a/src/models/gear.cairo +++ b/src/models/gear.cairo @@ -58,6 +58,22 @@ pub enum GearType { Drone // 0x800001 } +#[derive(Drop, Copy, Serde)] +pub struct GearDetails { + // Type of gear (e.g., Weapon, Helmet, Vehicle) + pub gear_type: GearType, + // Minimum XP required to use the gear + pub min_xp_needed: u256, + // Base damage value for the gear (0 for non-damaging items) + pub base_damage: u64, + // Maximum upgrade level for the gear + pub max_upgrade_level: u64, + // Initial total count for fungible items (default 1 for non-fungible) + pub total_count: u64, + // Reference to variation (e.g., specific model like "Iron Sword") + pub variation_ref: u256, +} + #[derive(Drop, Copy, Serde, Default)] pub struct GearProperties { asset_id: u256, @@ -166,6 +182,52 @@ pub impl GearImpl of GearTrait { } } +// Implementation of GearDetails with validation and default behavior +#[generate_trait] +pub impl GearDetailsImpl of GearDetailsTrait { + // Creates a new GearDetails instance with validation + fn new( + gear_type: GearType, + min_xp_needed: u256, + base_damage: u64, + max_upgrade_level: u64, + total_count: u64, + variation_ref: u256, + ) -> GearDetails { + // Validate inputs + assert(gear_type != GearType::None, 'Gear type cannot be None'); + assert(min_xp_needed >= 0, 'Min XP cannot be negative'); + assert(base_damage <= 1000, 'Base damage exceeds max (1000)'); + assert(max_upgrade_level > 0, 'Max upgrade level must be > 0'); + assert(total_count > 0, 'Total count must be > 0'); + + GearDetails { + gear_type, min_xp_needed, base_damage, max_upgrade_level, total_count, variation_ref, + } + } + + // Validates the GearDetails instance + fn validate(self: @GearDetails) -> bool { + *self.gear_type != GearType::None + && *self.min_xp_needed >= 0 + && *self.base_damage <= 1000 + && *self.max_upgrade_level > 0 + && *self.total_count > 0 + } + + // Creates a default GearDetails for testing or fallback + fn default() -> GearDetails { + GearDetails { + gear_type: GearType::Weapon, + min_xp_needed: 5, + base_damage: 10, + max_upgrade_level: 1, + total_count: 1, + variation_ref: 0, + } + } +} + // Implementation of conversion from GearType to felt252 impl GearTypeIntoFelt252 of Into { fn into(self: GearType) -> felt252 { @@ -425,3 +487,57 @@ impl OptionArrayTupleImpl of Clone>> { } } } + +// Tests for GearDetails struct +#[cfg(test)] +mod tests { + use super::{GearDetails, GearDetailsImpl, GearType}; + + #[test] + fn test_valid_gear_details() { + let gear = GearDetailsImpl::new(GearType::Sword, 30, 50, 10, 1, 1); + assert!(gear.validate(), "Valid gear should pass validation"); + assert(gear.gear_type == GearType::Sword, 'Gear type mismatch'); + assert(gear.min_xp_needed == 30, 'Min XP mismatch'); + assert(gear.base_damage == 50, 'Base damage mismatch'); + assert(gear.max_upgrade_level == 10, 'Max upgrade level mismatch'); + assert(gear.total_count == 1, 'Total count mismatch'); + assert(gear.variation_ref == 1, 'Variation ref mismatch'); + } + + #[test] + #[should_panic(expected: ('Gear type cannot be None',))] + fn test_invalid_gear_type() { + GearDetailsImpl::new(GearType::None, 5, 10, 1, 1, 0); + } + + #[test] + #[should_panic(expected: ('Base damage exceeds max (1000)',))] + fn test_invalid_base_damage() { + GearDetailsImpl::new(GearType::Weapon, 5, 1001, 1, 1, 0); + } + + #[test] + #[should_panic(expected: ('Max upgrade level must be > 0',))] + fn test_invalid_max_upgrade_level() { + GearDetailsImpl::new(GearType::Weapon, 5, 10, 0, 1, 0); + } + + #[test] + #[should_panic(expected: ('Total count must be > 0',))] + fn test_invalid_total_count() { + GearDetailsImpl::new(GearType::Weapon, 5, 10, 1, 0, 0); + } + + #[test] + fn test_default_gear_details() { + let gear = GearDetailsImpl::default(); + assert(gear.validate(), 'Default gear should be valid'); + assert(gear.gear_type == GearType::Weapon, 'Default gear type mismatch'); + assert(gear.min_xp_needed == 5, 'Default min XP mismatch'); + assert(gear.base_damage == 10, 'Default base damage mismatch'); + assert!(gear.max_upgrade_level == 1, "Default max upgrade level mismatch"); + assert(gear.total_count == 1, 'Default total count mismatch'); + assert(gear.variation_ref == 0, 'Default variation ref mismatch'); + } +} diff --git a/src/systems/core.cairo b/src/systems/core.cairo index 0cc2bfa..25afebd 100644 --- a/src/systems/core.cairo +++ b/src/systems/core.cairo @@ -3,10 +3,10 @@ /// /// Spawn tournamemnts and side quests here, if necessary. -use coa::models::gear::Gear; +use coa::models::gear::{Gear, GearDetails}; #[starknet::interface] pub trait ICore { - fn spawn_items(ref self: TContractState, amount: u256); + fn spawn_items(ref self: TContractState, gear_details: Array); // move to market only items that have been spawned. // if caller is admin, check spawned items and relocate // if caller is player, @@ -74,8 +74,8 @@ pub mod CoreActions { #[abi(embed_v0)] pub impl CoreActionsImpl of super::ICore { - //@ryzen-xp - fn spawn_items(ref self: ContractState, mut amount: u256) { + //@ryzen-xp, @truthixify + fn spawn_items(ref self: ContractState, gear_details: Array) { let caller = get_caller_address(); let mut world = self.world_default(); let contract: Contract = world.read_model(COA_CONTRACTS); @@ -86,23 +86,43 @@ pub mod CoreActions { }; let mut items = array![]; + let mut i = 0; - while amount != 0 { - let mut gear: Gear = self.random_gear_generator(); - - assert(!gear.spawned, 'Gear_already_spawned'); + while i < gear_details.len() { + let details = *gear_details.at(i); + assert(details.validate(), 'Invalid gear details'); + + let item_id: u256 = self.generate_incremental_ids(details.gear_type.into()); + let item_type: felt252 = details.gear_type.into(); + + let mut gear = Gear { + id: item_id, + item_type, + asset_id: item_id, + variation_ref: details.variation_ref, + total_count: details.total_count, + in_action: false, + upgrade_level: 0, + owner: contract_address_const::<0>(), + max_upgrade_level: details.max_upgrade_level, + min_xp_needed: details.min_xp_needed, + spawned: true, + }; + + assert(!gear.spawned, 'Gear already spawned'); gear.spawned = true; - gear.owner = contract_address_const::<0>(); world.write_model(@gear); items.append(gear.id); - amount -= 1; - // mint to warehouse - erc1155_dispatcher.mint(contract.warehouse, gear.id, 1, array![].span()); + erc1155_dispatcher + .mint(contract.warehouse, gear.id, details.total_count.into(), array![].span()); + i += 1; }; + let event = GearSpawned { admin: caller, items }; world.emit_event(@event); } + // move to market only items that have been spawned. // if caller is admin, check spawned items and relocate // if caller is player, From ffaa1573ff3ad1857df403a9b2fe1448f2df7498 Mon Sep 17 00:00:00 2001 From: truthixify Date: Mon, 8 Sep 2025 00:41:32 +0100 Subject: [PATCH 2/3] scarb fmt --- src/helpers/gear.cairo | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/helpers/gear.cairo b/src/helpers/gear.cairo index 953153c..e551ea3 100644 --- a/src/helpers/gear.cairo +++ b/src/helpers/gear.cairo @@ -172,18 +172,13 @@ pub fn random_gear_details() -> GearDetails { let gear_type = random_geartype(); let min_xp_needed = get_min_xp_needed(gear_type); let max_upgrade_level = get_max_upgrade_level(gear_type); - + // Generate random base damage (between 10 and 100 for simplicity) let mut dice = DiceTrait::new(90, 'DAMAGE_SEED'); let base_damage: u64 = (10 + dice.roll()).into(); GearDetails { - gear_type, - min_xp_needed, - base_damage, - max_upgrade_level, - total_count: 1, - variation_ref: 0, + gear_type, min_xp_needed, base_damage, max_upgrade_level, total_count: 1, variation_ref: 0, } } @@ -191,7 +186,7 @@ pub fn random_gear_details() -> GearDetails { pub fn validate_gear_details_array(details: @Array) -> bool { let mut i = 0; let mut valid = true; - + while i < details.len() { let gear_details = *details.at(i); if !gear_details.validate() { @@ -200,7 +195,7 @@ pub fn validate_gear_details_array(details: @Array) -> bool { } i += 1; }; - + valid } @@ -208,19 +203,22 @@ pub fn validate_gear_details_array(details: @Array) -> bool { pub fn generate_batch_gear_details(amount: u32) -> Array { let mut result = array![]; let mut i = 0; - + while i < amount { result.append(random_gear_details()); i += 1; }; - + result } // Tests for helper functions #[cfg(test)] mod tests { - use super::{GearDetailsImpl, GearType, random_gear_details, validate_gear_details_array, generate_batch_gear_details}; + use super::{ + GearDetailsImpl, GearType, random_gear_details, validate_gear_details_array, + generate_batch_gear_details, + }; #[test] fn test_random_gear_details() { @@ -236,7 +234,7 @@ mod tests { fn test_validate_gear_details_array() { let mut details = array![ GearDetailsImpl::new(GearType::Sword, 30, 50, 10, 1, 1), - GearDetailsImpl::new(GearType::Helmet, 15, 0, 5, 1, 0) + GearDetailsImpl::new(GearType::Helmet, 15, 0, 5, 1, 0), ]; assert(validate_gear_details_array(@details), 'Valid array should pass'); @@ -250,4 +248,4 @@ mod tests { assert(details.len() == 3, 'Should generate 3 gear details'); assert(validate_gear_details_array(@details), 'Batch should be valid'); } -} \ No newline at end of file +} From 74d34d690071fff5a065af7857d2dfec8285c3fc Mon Sep 17 00:00:00 2001 From: truthixify Date: Mon, 8 Sep 2025 00:56:09 +0100 Subject: [PATCH 3/3] made some changes according to coderabbit review --- src/helpers/gear.cairo | 8 +++++++- src/systems/core.cairo | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/helpers/gear.cairo b/src/helpers/gear.cairo index e551ea3..c2fd627 100644 --- a/src/helpers/gear.cairo +++ b/src/helpers/gear.cairo @@ -169,7 +169,13 @@ pub fn get_min_xp_needed(gear_type: GearType) -> u256 { // Helper function to generate random GearDetails pub fn random_gear_details() -> GearDetails { - let gear_type = random_geartype(); + let mut gear_type = random_geartype(); + + if gear_type == GearType::None { + // Fallback to a sane default; alternatively re-roll with a different seed. + gear_type = GearType::Weapon; + } + let min_xp_needed = get_min_xp_needed(gear_type); let max_upgrade_level = get_max_upgrade_level(gear_type); diff --git a/src/systems/core.cairo b/src/systems/core.cairo index 25afebd..79635a9 100644 --- a/src/systems/core.cairo +++ b/src/systems/core.cairo @@ -106,7 +106,7 @@ pub mod CoreActions { owner: contract_address_const::<0>(), max_upgrade_level: details.max_upgrade_level, min_xp_needed: details.min_xp_needed, - spawned: true, + spawned: false, }; assert(!gear.spawned, 'Gear already spawned');