Skip to content

Commit 6c3ebca

Browse files
authored
Merge pull request #155 from truthixify/fix-93
Implement GearDetails Struct and Update Spawn Logic
2 parents ca5bdeb + 74d34d6 commit 6c3ebca

3 files changed

Lines changed: 237 additions & 13 deletions

File tree

src/helpers/gear.cairo

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::models::gear::GearType;
1+
use crate::models::gear::{GearType, GearDetails, GearDetailsImpl};
22
use origami_random::dice::{Dice, DiceTrait};
33

44
// Helper function to calculate upgrade multipliers based on level
@@ -167,3 +167,91 @@ pub fn get_min_xp_needed(gear_type: GearType) -> u256 {
167167
}
168168
}
169169

170+
// Helper function to generate random GearDetails
171+
pub fn random_gear_details() -> GearDetails {
172+
let mut gear_type = random_geartype();
173+
174+
if gear_type == GearType::None {
175+
// Fallback to a sane default; alternatively re-roll with a different seed.
176+
gear_type = GearType::Weapon;
177+
}
178+
179+
let min_xp_needed = get_min_xp_needed(gear_type);
180+
let max_upgrade_level = get_max_upgrade_level(gear_type);
181+
182+
// Generate random base damage (between 10 and 100 for simplicity)
183+
let mut dice = DiceTrait::new(90, 'DAMAGE_SEED');
184+
let base_damage: u64 = (10 + dice.roll()).into();
185+
186+
GearDetails {
187+
gear_type, min_xp_needed, base_damage, max_upgrade_level, total_count: 1, variation_ref: 0,
188+
}
189+
}
190+
191+
// Helper function to validate GearDetails array
192+
pub fn validate_gear_details_array(details: @Array<GearDetails>) -> bool {
193+
let mut i = 0;
194+
let mut valid = true;
195+
196+
while i < details.len() {
197+
let gear_details = *details.at(i);
198+
if !gear_details.validate() {
199+
valid = false;
200+
break;
201+
}
202+
i += 1;
203+
};
204+
205+
valid
206+
}
207+
208+
// Helper function to generate multiple GearDetails for batch spawning
209+
pub fn generate_batch_gear_details(amount: u32) -> Array<GearDetails> {
210+
let mut result = array![];
211+
let mut i = 0;
212+
213+
while i < amount {
214+
result.append(random_gear_details());
215+
i += 1;
216+
};
217+
218+
result
219+
}
220+
221+
// Tests for helper functions
222+
#[cfg(test)]
223+
mod tests {
224+
use super::{
225+
GearDetailsImpl, GearType, random_gear_details, validate_gear_details_array,
226+
generate_batch_gear_details,
227+
};
228+
229+
#[test]
230+
fn test_random_gear_details() {
231+
let gear = random_gear_details();
232+
assert(gear.validate(), 'Random gear should be valid');
233+
assert(gear.gear_type != GearType::None, 'Gear type should not be None');
234+
assert(gear.base_damage >= 10 && gear.base_damage <= 100, 'Base damage out of range');
235+
assert(gear.total_count == 1, 'Total count should be 1');
236+
}
237+
238+
#[test]
239+
#[should_panic(expected: 'Gear type cannot be None')]
240+
fn test_validate_gear_details_array() {
241+
let mut details = array![
242+
GearDetailsImpl::new(GearType::Sword, 30, 50, 10, 1, 1),
243+
GearDetailsImpl::new(GearType::Helmet, 15, 0, 5, 1, 0),
244+
];
245+
assert(validate_gear_details_array(@details), 'Valid array should pass');
246+
247+
// Add invalid gear details
248+
details.append(GearDetailsImpl::new(GearType::None, 5, 10, 1, 1, 0));
249+
}
250+
251+
#[test]
252+
fn test_generate_batch_gear_details() {
253+
let details = generate_batch_gear_details(3);
254+
assert(details.len() == 3, 'Should generate 3 gear details');
255+
assert(validate_gear_details_array(@details), 'Batch should be valid');
256+
}
257+
}

src/models/gear.cairo

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,22 @@ pub enum GearType {
5858
Drone // 0x800001
5959
}
6060

61+
#[derive(Drop, Copy, Serde)]
62+
pub struct GearDetails {
63+
// Type of gear (e.g., Weapon, Helmet, Vehicle)
64+
pub gear_type: GearType,
65+
// Minimum XP required to use the gear
66+
pub min_xp_needed: u256,
67+
// Base damage value for the gear (0 for non-damaging items)
68+
pub base_damage: u64,
69+
// Maximum upgrade level for the gear
70+
pub max_upgrade_level: u64,
71+
// Initial total count for fungible items (default 1 for non-fungible)
72+
pub total_count: u64,
73+
// Reference to variation (e.g., specific model like "Iron Sword")
74+
pub variation_ref: u256,
75+
}
76+
6177
#[derive(Drop, Copy, Serde, Default)]
6278
pub struct GearProperties {
6379
asset_id: u256,
@@ -166,6 +182,52 @@ pub impl GearImpl of GearTrait {
166182
}
167183
}
168184

185+
// Implementation of GearDetails with validation and default behavior
186+
#[generate_trait]
187+
pub impl GearDetailsImpl of GearDetailsTrait {
188+
// Creates a new GearDetails instance with validation
189+
fn new(
190+
gear_type: GearType,
191+
min_xp_needed: u256,
192+
base_damage: u64,
193+
max_upgrade_level: u64,
194+
total_count: u64,
195+
variation_ref: u256,
196+
) -> GearDetails {
197+
// Validate inputs
198+
assert(gear_type != GearType::None, 'Gear type cannot be None');
199+
assert(min_xp_needed >= 0, 'Min XP cannot be negative');
200+
assert(base_damage <= 1000, 'Base damage exceeds max (1000)');
201+
assert(max_upgrade_level > 0, 'Max upgrade level must be > 0');
202+
assert(total_count > 0, 'Total count must be > 0');
203+
204+
GearDetails {
205+
gear_type, min_xp_needed, base_damage, max_upgrade_level, total_count, variation_ref,
206+
}
207+
}
208+
209+
// Validates the GearDetails instance
210+
fn validate(self: @GearDetails) -> bool {
211+
*self.gear_type != GearType::None
212+
&& *self.min_xp_needed >= 0
213+
&& *self.base_damage <= 1000
214+
&& *self.max_upgrade_level > 0
215+
&& *self.total_count > 0
216+
}
217+
218+
// Creates a default GearDetails for testing or fallback
219+
fn default() -> GearDetails {
220+
GearDetails {
221+
gear_type: GearType::Weapon,
222+
min_xp_needed: 5,
223+
base_damage: 10,
224+
max_upgrade_level: 1,
225+
total_count: 1,
226+
variation_ref: 0,
227+
}
228+
}
229+
}
230+
169231
// Implementation of conversion from GearType to felt252
170232
impl GearTypeIntoFelt252 of Into<GearType, felt252> {
171233
fn into(self: GearType) -> felt252 {
@@ -425,3 +487,57 @@ impl OptionArrayTupleImpl of Clone<Option<Array<(u256, u256)>>> {
425487
}
426488
}
427489
}
490+
491+
// Tests for GearDetails struct
492+
#[cfg(test)]
493+
mod tests {
494+
use super::{GearDetails, GearDetailsImpl, GearType};
495+
496+
#[test]
497+
fn test_valid_gear_details() {
498+
let gear = GearDetailsImpl::new(GearType::Sword, 30, 50, 10, 1, 1);
499+
assert!(gear.validate(), "Valid gear should pass validation");
500+
assert(gear.gear_type == GearType::Sword, 'Gear type mismatch');
501+
assert(gear.min_xp_needed == 30, 'Min XP mismatch');
502+
assert(gear.base_damage == 50, 'Base damage mismatch');
503+
assert(gear.max_upgrade_level == 10, 'Max upgrade level mismatch');
504+
assert(gear.total_count == 1, 'Total count mismatch');
505+
assert(gear.variation_ref == 1, 'Variation ref mismatch');
506+
}
507+
508+
#[test]
509+
#[should_panic(expected: ('Gear type cannot be None',))]
510+
fn test_invalid_gear_type() {
511+
GearDetailsImpl::new(GearType::None, 5, 10, 1, 1, 0);
512+
}
513+
514+
#[test]
515+
#[should_panic(expected: ('Base damage exceeds max (1000)',))]
516+
fn test_invalid_base_damage() {
517+
GearDetailsImpl::new(GearType::Weapon, 5, 1001, 1, 1, 0);
518+
}
519+
520+
#[test]
521+
#[should_panic(expected: ('Max upgrade level must be > 0',))]
522+
fn test_invalid_max_upgrade_level() {
523+
GearDetailsImpl::new(GearType::Weapon, 5, 10, 0, 1, 0);
524+
}
525+
526+
#[test]
527+
#[should_panic(expected: ('Total count must be > 0',))]
528+
fn test_invalid_total_count() {
529+
GearDetailsImpl::new(GearType::Weapon, 5, 10, 1, 0, 0);
530+
}
531+
532+
#[test]
533+
fn test_default_gear_details() {
534+
let gear = GearDetailsImpl::default();
535+
assert(gear.validate(), 'Default gear should be valid');
536+
assert(gear.gear_type == GearType::Weapon, 'Default gear type mismatch');
537+
assert(gear.min_xp_needed == 5, 'Default min XP mismatch');
538+
assert(gear.base_damage == 10, 'Default base damage mismatch');
539+
assert!(gear.max_upgrade_level == 1, "Default max upgrade level mismatch");
540+
assert(gear.total_count == 1, 'Default total count mismatch');
541+
assert(gear.variation_ref == 0, 'Default variation ref mismatch');
542+
}
543+
}

src/systems/core.cairo

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
///
44
/// Spawn tournamemnts and side quests here, if necessary.
55

6-
use coa::models::gear::Gear;
6+
use coa::models::gear::{Gear, GearDetails};
77
#[starknet::interface]
88
pub trait ICore<TContractState> {
9-
fn spawn_items(ref self: TContractState, amount: u256);
9+
fn spawn_items(ref self: TContractState, gear_details: Array<GearDetails>);
1010
// move to market only items that have been spawned.
1111
// if caller is admin, check spawned items and relocate
1212
// if caller is player,
@@ -74,8 +74,8 @@ pub mod CoreActions {
7474

7575
#[abi(embed_v0)]
7676
pub impl CoreActionsImpl of super::ICore<ContractState> {
77-
//@ryzen-xp
78-
fn spawn_items(ref self: ContractState, mut amount: u256) {
77+
//@ryzen-xp, @truthixify
78+
fn spawn_items(ref self: ContractState, gear_details: Array<GearDetails>) {
7979
let caller = get_caller_address();
8080
let mut world = self.world_default();
8181
let contract: Contract = world.read_model(COA_CONTRACTS);
@@ -86,23 +86,43 @@ pub mod CoreActions {
8686
};
8787

8888
let mut items = array![];
89+
let mut i = 0;
8990

90-
while amount != 0 {
91-
let mut gear: Gear = self.random_gear_generator();
92-
93-
assert(!gear.spawned, 'Gear_already_spawned');
91+
while i < gear_details.len() {
92+
let details = *gear_details.at(i);
93+
assert(details.validate(), 'Invalid gear details');
94+
95+
let item_id: u256 = self.generate_incremental_ids(details.gear_type.into());
96+
let item_type: felt252 = details.gear_type.into();
97+
98+
let mut gear = Gear {
99+
id: item_id,
100+
item_type,
101+
asset_id: item_id,
102+
variation_ref: details.variation_ref,
103+
total_count: details.total_count,
104+
in_action: false,
105+
upgrade_level: 0,
106+
owner: contract_address_const::<0>(),
107+
max_upgrade_level: details.max_upgrade_level,
108+
min_xp_needed: details.min_xp_needed,
109+
spawned: false,
110+
};
111+
112+
assert(!gear.spawned, 'Gear already spawned');
94113
gear.spawned = true;
95-
gear.owner = contract_address_const::<0>();
96114
world.write_model(@gear);
97115

98116
items.append(gear.id);
99-
amount -= 1;
100-
// mint to warehouse
101-
erc1155_dispatcher.mint(contract.warehouse, gear.id, 1, array![].span());
117+
erc1155_dispatcher
118+
.mint(contract.warehouse, gear.id, details.total_count.into(), array![].span());
119+
i += 1;
102120
};
121+
103122
let event = GearSpawned { admin: caller, items };
104123
world.emit_event(@event);
105124
}
125+
106126
// move to market only items that have been spawned.
107127
// if caller is admin, check spawned items and relocate
108128
// if caller is player,

0 commit comments

Comments
 (0)