From e8e1014a29dc578c91b150bf8a608cb5ddd2d9a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damian=20Pi=C3=B1ones?= Date: Wed, 30 Oct 2024 17:30:29 -0300 Subject: [PATCH] add select special and modifier cards --- manifests/dev/deployment/manifest.json | 38 ++++- manifests/dev/deployment/manifest.toml | 6 +- src/constants/packs.cairo | 41 +++++- src/constants/specials.cairo | 35 +++++ src/lib.cairo | 3 + src/models/data/game_deck.cairo | 3 +- src/models/status/game/game.cairo | 2 +- src/store.cairo | 7 +- src/systems/game_system.cairo | 64 ++++++++- src/tests/test_game_select_deck.cairo | 12 +- .../test_game_select_modifier_cards.cairo | 69 +++++++++ .../test_game_select_special_cards.cairo | 54 ++++++++ src/utils/packs.cairo | 131 ++++++++++++++++++ src/utils/shop.cairo | 1 + 14 files changed, 449 insertions(+), 17 deletions(-) create mode 100644 src/tests/test_game_select_modifier_cards.cairo create mode 100644 src/tests/test_game_select_special_cards.cairo create mode 100644 src/utils/packs.cairo diff --git a/manifests/dev/deployment/manifest.json b/manifests/dev/deployment/manifest.json index 5f661b6..74ee399 100644 --- a/manifests/dev/deployment/manifest.json +++ b/manifests/dev/deployment/manifest.json @@ -1256,8 +1256,8 @@ { "kind": "DojoContract", "address": "0x6a41badee85305fa1aac33488860360c66b60d7f3b204d6e2cd84071dc3c394", - "class_hash": "0x58feb428c2692bda09414a95336a6f566852ff95338fe1760f21f53e520eba0", - "original_class_hash": "0x58feb428c2692bda09414a95336a6f566852ff95338fe1760f21f53e520eba0", + "class_hash": "0x5f74e789806e15032b0e17a3d05f4f70f03b1bba1f6522bdf755235ac6286af", + "original_class_hash": "0x5f74e789806e15032b0e17a3d05f4f70f03b1bba1f6522bdf755235ac6286af", "base_class_hash": "0x2427dd10a58850ac9a5ca6ce04b7771b05330fd18f2e481831ad903b969e6b2", "abi": [ { @@ -1482,6 +1482,38 @@ "outputs": [], "state_mutability": "external" }, + { + "type": "function", + "name": "select_special_cards", + "inputs": [ + { + "name": "game_id", + "type": "core::integer::u32" + }, + { + "name": "cards_index", + "type": "core::array::Array::" + } + ], + "outputs": [], + "state_mutability": "external" + }, + { + "type": "function", + "name": "select_modifier_cards", + "inputs": [ + { + "name": "game_id", + "type": "core::integer::u32" + }, + { + "name": "cards_index", + "type": "core::array::Array::" + } + ], + "outputs": [], + "state_mutability": "external" + }, { "type": "function", "name": "play", @@ -1666,6 +1698,8 @@ "tag": "jokers_of_neon-game_system", "systems": [ "select_deck", + "select_special_cards", + "select_modifier_cards", "play", "discard", "discard_effect_card", diff --git a/manifests/dev/deployment/manifest.toml b/manifests/dev/deployment/manifest.toml index 9d4ae09..82069c4 100644 --- a/manifests/dev/deployment/manifest.toml +++ b/manifests/dev/deployment/manifest.toml @@ -24,8 +24,8 @@ manifest_name = "dojo-base" [[contracts]] kind = "DojoContract" address = "0x6a41badee85305fa1aac33488860360c66b60d7f3b204d6e2cd84071dc3c394" -class_hash = "0x58feb428c2692bda09414a95336a6f566852ff95338fe1760f21f53e520eba0" -original_class_hash = "0x58feb428c2692bda09414a95336a6f566852ff95338fe1760f21f53e520eba0" +class_hash = "0x5f74e789806e15032b0e17a3d05f4f70f03b1bba1f6522bdf755235ac6286af" +original_class_hash = "0x5f74e789806e15032b0e17a3d05f4f70f03b1bba1f6522bdf755235ac6286af" base_class_hash = "0x2427dd10a58850ac9a5ca6ce04b7771b05330fd18f2e481831ad903b969e6b2" abi = "manifests/dev/deployment/abis/contracts/jokers_of_neon-game_system-7a205bbc.json" reads = [] @@ -34,6 +34,8 @@ init_calldata = [] tag = "jokers_of_neon-game_system" systems = [ "select_deck", + "select_special_cards", + "select_modifier_cards", "play", "discard", "discard_effect_card", diff --git a/src/constants/packs.cairo b/src/constants/packs.cairo index beb593c..52e87b8 100644 --- a/src/constants/packs.cairo +++ b/src/constants/packs.cairo @@ -1,6 +1,9 @@ use jokers_of_neon::constants::card::{JOKER_CARD, NEON_JOKER_CARD}; use jokers_of_neon::constants::modifiers::{modifiers_ids_all, SUIT_HEARTS_MODIFIER_ID}; -use jokers_of_neon::constants::specials::{specials_ids_all, SPECIAL_ALL_CARDS_TO_HEARTS_ID}; +use jokers_of_neon::constants::specials::{ + specials_ids_all, SPECIAL_ALL_CARDS_TO_HEARTS_ID, common_specials_ids, uncommon_specials_ids, rare_specials_ids, + epic_specials_ids, legendary_specials_ids +}; use jokers_of_neon::models::data::blister_pack::BlisterPack; use jokers_of_neon::models::data::card::{Card, CardTrait, Suit, Value, ValueEnumerableImpl}; use jokers_of_neon::utils::constants::{jokers_all, common_cards_all}; @@ -14,8 +17,12 @@ const FIGURES_BLISTER_PACK_ID: u32 = 6; const DECEITFUL_JOKER_BLISTER_PACK_ID: u32 = 7; const LOVERS_BLISTER_PACK_ID: u32 = 8; const SPECIAL_BET_BLISTER_PACK_ID: u32 = 9; + const EMPTY_PACK_ID: u32 = 999; +const SPECIAL_CARDS_PACK_ID: u32 = 20; +const MODIFIER_CARDS_PACK_ID: u32 = 21; + fn blister_packs_ids_all() -> Array { array![ BASIC_BLISTER_PACK_ID, @@ -207,3 +214,35 @@ fn EMPTY_BLISTER_PACK() -> BlisterPack { id: EMPTY_PACK_ID, cost: 0, name: '', probability: 0, size: 0, cards: array![].span(), probs: array![].span(), } } + +fn SPECIAL_CARDS_PACK() -> BlisterPack { + BlisterPack { + id: SPECIAL_CARDS_PACK_ID, + cost: 0, + name: 'special_cards_pack', + probability: 100, + size: 5, + cards: array![ + array![].span(), + legendary_specials_ids().span(), + epic_specials_ids().span(), + rare_specials_ids().span(), + uncommon_specials_ids().span(), + common_specials_ids().span() + ] + .span(), + probs: array![100, 5, 10, 15, 25, 45].span(), + } +} + +fn MODIFIER_CARDS_PACK() -> BlisterPack { + BlisterPack { + id: MODIFIER_CARDS_PACK_ID, + cost: 0, + name: 'modifier_cards_pack', + probability: 100, + size: 5, + cards: array![array![].span(), modifiers_ids_all().span()].span(), + probs: array![100, 100].span(), + } +} diff --git a/src/constants/specials.cairo b/src/constants/specials.cairo index c327df4..647fc13 100644 --- a/src/constants/specials.cairo +++ b/src/constants/specials.cairo @@ -275,3 +275,38 @@ fn specials_ids_all() -> Array { SPECIAL_LUCKY_HAND_ID, ] } + +fn common_specials_ids() -> Array { + array![ + SPECIAL_MULTI_FOR_HEART_ID, + SPECIAL_MULTI_FOR_CLUB_ID, + SPECIAL_MULTI_FOR_DIAMOND_ID, + SPECIAL_MULTI_FOR_SPADE_ID, + SPECIAL_INCREASE_LEVEL_PAIR_ID, + SPECIAL_INCREASE_LEVEL_DOUBLE_PAIR_ID, + SPECIAL_LUCKY_HAND_ID, + ] +} + +fn uncommon_specials_ids() -> Array { + array![ + SPECIAL_INCREASE_LEVEL_STRAIGHT_ID, + SPECIAL_INCREASE_LEVEL_FLUSH_ID, + SPECIAL_STRAIGHT_WITH_FOUR_CARDS_ID, + SPECIAL_FLUSH_WITH_FOUR_CARDS_ID, + SPECIAL_MULTI_ACES_ID, + SPECIAL_NEON_BONUS_ID + ] +} + +fn rare_specials_ids() -> Array { + array![SPECIAL_HAND_THIEF_ID, SPECIAL_LUCKY_SEVEN_ID, SPECIAL_POINTS_FOR_FIGURES_ID, SPECIAL_ALL_CARDS_TO_HEARTS_ID] +} + +fn epic_specials_ids() -> Array { + array![SPECIAL_EXTRA_HELP_ID, SPECIAL_JOKER_BOOSTER_ID, SPECIAL_DEADLINE_ID] +} + +fn legendary_specials_ids() -> Array { + array![SPECIAL_INITIAL_ADVANTAGE_ID, SPECIAL_MODIFIER_BOOSTER_ID] +} diff --git a/src/lib.cairo b/src/lib.cairo index 589e2a4..cb2fb8c 100644 --- a/src/lib.cairo +++ b/src/lib.cairo @@ -52,12 +52,15 @@ mod tests { mod test_game_discard_special_card; mod test_game_play; mod test_game_select_deck; + mod test_game_select_modifier_cards; + mod test_game_select_special_cards; mod utils; } mod utils { mod calculate_hand; mod constants; + mod packs; mod rage; mod random; mod round; diff --git a/src/models/data/game_deck.cairo b/src/models/data/game_deck.cairo index 802d9af..231267b 100644 --- a/src/models/data/game_deck.cairo +++ b/src/models/data/game_deck.cairo @@ -28,12 +28,11 @@ struct DeckCard { #[generate_trait] impl GameDeckImpl of IGameDeck { fn init(ref store: Store, game_id: u32, deck_id: u8) { - // Traditional Deck let mut cards = traditional_cards_all(); cards.append(JOKER_CARD); cards.append(JOKER_CARD); - + if deck_id == SCRIBE_DECK { cards.append(JOKER_CARD); cards.append(JOKER_CARD); diff --git a/src/models/status/game/game.cairo b/src/models/status/game/game.cairo index a031b11..e539e6d 100644 --- a/src/models/status/game/game.cairo +++ b/src/models/status/game/game.cairo @@ -58,7 +58,7 @@ impl DefaultGame of Default { player_score: 0, level: 1, len_hand: 8, - len_max_current_special_cards: 1, + len_max_current_special_cards: 5, len_current_special_cards: 0, current_jokers: 0, state: GameState::IN_GAME, diff --git a/src/store.cairo b/src/store.cairo index 57dee0a..9a23624 100644 --- a/src/store.cairo +++ b/src/store.cairo @@ -56,7 +56,8 @@ use jokers_of_neon::constants::packs::{ FIGURES_BLISTER_PACK, DECEITFUL_JOKER_BLISTER_PACK, LOVERS_BLISTER_PACK, SPECIAL_BET_BLISTER_PACK, EMPTY_BLISTER_PACK, BASIC_BLISTER_PACK_ID, ADVANCED_BLISTER_PACK_ID, JOKER_BLISTER_PACK_ID, SPECIALS_BLISTER_PACK_ID, MODIFIER_BLISTER_PACK_ID, FIGURES_BLISTER_PACK_ID, DECEITFUL_JOKER_BLISTER_PACK_ID, - LOVERS_BLISTER_PACK_ID, SPECIAL_BET_BLISTER_PACK_ID, + LOVERS_BLISTER_PACK_ID, SPECIAL_BET_BLISTER_PACK_ID, SPECIAL_CARDS_PACK_ID, SPECIAL_CARDS_PACK, + MODIFIER_CARDS_PACK_ID, MODIFIER_CARDS_PACK }; use jokers_of_neon::constants::playhand::{ ROYAL_FLUSH, STRAIGHT_FLUSH, FIVE_OF_A_KIND, FOUR_OF_A_KIND, FULL_HOUSE, FLUSH, STRAIGHT, THREE_OF_A_KIND, TWO_PAIR, @@ -345,6 +346,10 @@ impl StoreImpl of StoreTrait { LOVERS_BLISTER_PACK() } else if id == SPECIAL_BET_BLISTER_PACK_ID { SPECIAL_BET_BLISTER_PACK() + } else if id == SPECIAL_CARDS_PACK_ID { + SPECIAL_CARDS_PACK() + } else if id == MODIFIER_CARDS_PACK_ID { + MODIFIER_CARDS_PACK() } else { EMPTY_BLISTER_PACK() } diff --git a/src/systems/game_system.cairo b/src/systems/game_system.cairo index 1b97353..5cddebd 100644 --- a/src/systems/game_system.cairo +++ b/src/systems/game_system.cairo @@ -6,6 +6,8 @@ use jokers_of_neon::models::data::poker_hand::PokerHand; trait IGameSystem { fn create_game(ref world: IWorldDispatcher, player_name: felt252) -> u32; fn select_deck(ref world: IWorldDispatcher, game_id: u32, deck_id: u8); + fn select_special_cards(ref world: IWorldDispatcher, game_id: u32, cards_index: Array); + fn select_modifier_cards(ref world: IWorldDispatcher, game_id: u32, cards_index: Array); fn play(ref world: IWorldDispatcher, game_id: u32, cards_index: Array, modifiers_index: Array); fn discard(ref world: IWorldDispatcher, game_id: u32, cards_index: Array, modifiers_index: Array); fn check_hand( @@ -25,6 +27,7 @@ mod errors { const GAME_NOT_IN_GAME: felt252 = 'Game: is not IN_GAME'; const GAME_NOT_SELECT_SPECIAL_CARDS: felt252 = 'Game:is not SELECT_SPECIAL_CARD'; const GAME_NOT_SELECT_DECK: felt252 = 'Game:is not SELECT_DECK'; + const GAME_NOT_SELECT_MODIFIER_CARDS: felt252 = 'Game:is not SELCT_MODIFIER_CARD'; const USE_INVALID_CARD: felt252 = 'Game: use an invalid card'; const INVALID_DECK_ID: felt252 = 'Game: use an invalid deck'; } @@ -34,6 +37,7 @@ mod game_system { use core::nullable::NullableTrait; use dojo::world::Resource::Contract; use jokers_of_neon::constants::card::{JOKER_CARD, NEON_JOKER_CARD, INVALID_CARD}; + use jokers_of_neon::constants::packs::{SPECIAL_CARDS_PACK_ID, MODIFIER_CARDS_PACK_ID}; use jokers_of_neon::constants::specials::{ SPECIAL_MULTI_FOR_HEART_ID, SPECIAL_MULTI_FOR_CLUB_ID, SPECIAL_MULTI_FOR_DIAMOND_ID, SPECIAL_MULTI_FOR_SPADE_ID, SPECIAL_INCREASE_LEVEL_PAIR_ID, SPECIAL_INCREASE_LEVEL_DOUBLE_PAIR_ID, SPECIAL_INCREASE_LEVEL_STRAIGHT_ID, @@ -56,6 +60,7 @@ mod game_system { use jokers_of_neon::models::status::game::rage::{RageRound, RageRoundStore}; use jokers_of_neon::models::status::round::current_hand_card::{CurrentHandCard, CurrentHandCardTrait}; use jokers_of_neon::models::status::round::round::Round; + use jokers_of_neon::models::status::shop::shop::{BlisterPackResult}; use jokers_of_neon::store::{Store, StoreTrait}; use jokers_of_neon::systems::rage_system::{IRageSystemDispatcher, IRageSystemDispatcherTrait}; @@ -64,6 +69,7 @@ mod game_system { RAGE_CARD_DIMINISHED_HOLD, RAGE_CARD_SILENT_JOKERS, RAGE_CARD_SILENT_HEARTS, RAGE_CARD_SILENT_CLUBS, RAGE_CARD_SILENT_DIAMONDS, RAGE_CARD_SILENT_SPADES, RAGE_CARD_ZERO_WASTE, is_neon_card, is_modifier_card }; + use jokers_of_neon::utils::packs::{open_blister_pack, select_cards_from_blister}; use jokers_of_neon::utils::rage::is_rage_card_active; use jokers_of_neon::utils::round::create_round; use starknet::{ContractAddress, get_caller_address, ClassHash}; @@ -89,7 +95,7 @@ mod game_system { player_score: 0, level: 1, len_hand: 8, - len_max_current_special_cards: 1, + len_max_current_special_cards: 5, len_current_special_cards: 0, current_jokers: 0, state: GameState::SELECT_DECK, @@ -118,10 +124,66 @@ mod game_system { assert(deck_id < 3, errors::INVALID_DECK_ID); GameDeckImpl::init(ref store, game_id, deck_id); + game.state = GameState::SELECT_SPECIAL_CARDS; + store.set_game(game); + + let cards = open_blister_pack(world, ref store, game, SPECIAL_CARDS_PACK_ID); + store.set_blister_pack_result(BlisterPackResult { game_id, cards_picked: false, cards }); + } + + fn select_special_cards(ref world: IWorldDispatcher, game_id: u32, cards_index: Array) { + let mut store: Store = StoreTrait::new(world); + + let mut game = store.get_game(game_id); + // Check that the game exists (if the game has no owner means it does not exists) + assert(game.owner.is_non_zero(), errors::GAME_NOT_FOUND); + + // Check that the owner of the game is the caller + assert(game.owner == get_caller_address(), errors::CALLER_NOT_OWNER); + + // Check that the status of the game + assert(game.state == GameState::SELECT_SPECIAL_CARDS, errors::GAME_NOT_SELECT_SPECIAL_CARDS); + + let mut blister_pack_result = store.get_blister_pack_result(game.id); + assert(cards_index.len() <= 2, errors::INVALID_CARD_INDEX_LEN); + + select_cards_from_blister(world, ref game, blister_pack_result.cards, cards_index); + + blister_pack_result.cards_picked = true; + store.set_blister_pack_result(blister_pack_result); + + let cards = open_blister_pack(world, ref store, game, MODIFIER_CARDS_PACK_ID); + store.set_blister_pack_result(BlisterPackResult { game_id, cards_picked: false, cards }); + game.state = GameState::SELECT_MODIFIER_CARDS; store.set_game(game); } + fn select_modifier_cards(ref world: IWorldDispatcher, game_id: u32, cards_index: Array) { + let mut store: Store = StoreTrait::new(world); + + let mut game = store.get_game(game_id); + // Check that the game exists (if the game has no owner means it does not exists) + assert(game.owner.is_non_zero(), errors::GAME_NOT_FOUND); + + // Check that the owner of the game is the caller + assert(game.owner == get_caller_address(), errors::CALLER_NOT_OWNER); + + // Check that the status of the game + assert(game.state == GameState::SELECT_MODIFIER_CARDS, errors::GAME_NOT_SELECT_MODIFIER_CARDS); + + let mut blister_pack_result = store.get_blister_pack_result(game.id); + assert(cards_index.len() <= 5, errors::INVALID_CARD_INDEX_LEN); + + select_cards_from_blister(world, ref game, blister_pack_result.cards, cards_index); + + blister_pack_result.cards_picked = true; + store.set_blister_pack_result(blister_pack_result); + + // game.state = GameState::; / TODO: + store.set_game(game); + } + fn play(ref world: IWorldDispatcher, game_id: u32, cards_index: Array, modifiers_index: Array) { let mut store: Store = StoreTrait::new(world); diff --git a/src/tests/test_game_select_deck.cairo b/src/tests/test_game_select_deck.cairo index a4100e0..e961c85 100644 --- a/src/tests/test_game_select_deck.cairo +++ b/src/tests/test_game_select_deck.cairo @@ -1,20 +1,18 @@ mod test_select_deck { use dojo::world::{IWorldDispatcher, IWorldDispatcherTrait}; - use jokers_of_neon::models::status::game::game::{Game, GameState}; - use jokers_of_neon::models::data::game_deck::{GameDeck, GameDeckStore, DeckCard, DeckCardStore}; use jokers_of_neon::constants::card::{ - ACE_CLUBS_ID, ACE_DIAMONDS_ID, ACE_HEARTS_ID, ACE_SPADES_ID, JOKER_CARD, INVALID_CARD, SCRIBE_DECK, WARRIOR_DECK, WIZARD_DECK, - traditional_cards_all + ACE_CLUBS_ID, ACE_DIAMONDS_ID, ACE_HEARTS_ID, ACE_SPADES_ID, JOKER_CARD, INVALID_CARD, SCRIBE_DECK, + WARRIOR_DECK, WIZARD_DECK, traditional_cards_all }; use jokers_of_neon::constants::modifiers::{POINTS_MODIFIER_4_ID, MULTI_MODIFIER_4_ID}; + use jokers_of_neon::models::data::game_deck::{GameDeck, GameDeckStore, DeckCard, DeckCardStore}; + use jokers_of_neon::models::status::game::game::{Game, GameState}; use jokers_of_neon::store::{Store, StoreTrait}; use jokers_of_neon::systems::game_system::{game_system, IGameSystemDispatcher, IGameSystemDispatcherTrait}; use jokers_of_neon::tests::setup::{ setup, setup::OWNER, setup::IDojoInitDispatcher, setup::IDojoInitDispatcherTrait }; - use jokers_of_neon::tests::utils::{ - mock_game - }; + use jokers_of_neon::tests::utils::{mock_game}; use starknet::testing::set_contract_address; fn PLAYER() -> starknet::ContractAddress { diff --git a/src/tests/test_game_select_modifier_cards.cairo b/src/tests/test_game_select_modifier_cards.cairo new file mode 100644 index 0000000..69ff148 --- /dev/null +++ b/src/tests/test_game_select_modifier_cards.cairo @@ -0,0 +1,69 @@ +mod test_select_modifier_cards { + use dojo::world::{IWorldDispatcher, IWorldDispatcherTrait}; + use jokers_of_neon::constants::modifiers::{ + POINTS_MODIFIER_1_ID, POINTS_MODIFIER_4_ID, MULTI_MODIFIER_1_ID, MULTI_MODIFIER_4_ID, SUIT_SPADES_MODIFIER_ID + }; + use jokers_of_neon::models::data::game_deck::{GameDeck, GameDeckStore, DeckCard, DeckCardStore}; + use jokers_of_neon::models::status::game::game::{Game, GameState}; + use jokers_of_neon::models::status::shop::shop::BlisterPackResult; + use jokers_of_neon::store::{Store, StoreTrait}; + use jokers_of_neon::systems::game_system::{game_system, IGameSystemDispatcher, IGameSystemDispatcherTrait}; + use jokers_of_neon::tests::setup::{ + setup, setup::OWNER, setup::IDojoInitDispatcher, setup::IDojoInitDispatcherTrait + }; + use jokers_of_neon::tests::utils::{mock_game, mock_game_deck}; + use starknet::testing::set_contract_address; + + fn PLAYER() -> starknet::ContractAddress { + starknet::contract_address_const::<'PLAYER'>() + } + + #[test] + #[available_gas(30000000000000000)] + fn test_select_modifier_cards() { + let (world, systems) = setup::spawn_game(); + let mut store = StoreTrait::new(world); + let mut game = mock_game(ref store, PLAYER()); + + game.state = GameState::SELECT_MODIFIER_CARDS; + store.set_game(game); + + mock_game_deck(world, game.id); + + // Mock BlisterPackResult + let cards = array![ + POINTS_MODIFIER_4_ID, + POINTS_MODIFIER_1_ID, + MULTI_MODIFIER_4_ID, + MULTI_MODIFIER_1_ID, + SUIT_SPADES_MODIFIER_ID, + ]; // 5 modifiers + store.set_blister_pack_result(BlisterPackResult { game_id: game.id, cards_picked: false, cards: cards.span() }); + + let game_deck_before = GameDeckStore::get(world, game.id); + + set_contract_address(PLAYER()); + systems.game_system.select_modifier_cards(game.id, array![0, 1, 2, 3, 4]); + + let game_deck_after = GameDeckStore::get(world, game.id); + assert(game_deck_after.len == game_deck_before.len + 5, 'wrong len_deck_cards'); + + let blister_pack = store.get_blister_pack_result(game.id); + assert(blister_pack.cards_picked == true, 'cards_picked should be true'); + + let MODIFIER_PICKED_1 = DeckCardStore::get(world, game.id, game_deck_after.len - 5); + assert(MODIFIER_PICKED_1.card_id == POINTS_MODIFIER_4_ID, 'wrong modifier card 1'); + + let MODIFIER_PICKED_2 = DeckCardStore::get(world, game.id, game_deck_after.len - 4); + assert(MODIFIER_PICKED_2.card_id == POINTS_MODIFIER_1_ID, 'wrong modifier card 2'); + + let MODIFIER_PICKED_3 = DeckCardStore::get(world, game.id, game_deck_after.len - 3); + assert(MODIFIER_PICKED_3.card_id == MULTI_MODIFIER_4_ID, 'wrong modifier card 3'); + + let MODIFIER_PICKED_4 = DeckCardStore::get(world, game.id, game_deck_after.len - 2); + assert(MODIFIER_PICKED_4.card_id == MULTI_MODIFIER_1_ID, 'wrong modifier card 4'); + + let MODIFIER_PICKED_5 = DeckCardStore::get(world, game.id, game_deck_after.len - 1); + assert(MODIFIER_PICKED_5.card_id == SUIT_SPADES_MODIFIER_ID, 'wrong modifier card 5'); + } +} diff --git a/src/tests/test_game_select_special_cards.cairo b/src/tests/test_game_select_special_cards.cairo new file mode 100644 index 0000000..2f91c86 --- /dev/null +++ b/src/tests/test_game_select_special_cards.cairo @@ -0,0 +1,54 @@ +mod test_select_special_cards { + use dojo::world::{IWorldDispatcher, IWorldDispatcherTrait}; + use jokers_of_neon::constants::specials::{ + SPECIAL_POINTS_FOR_FIGURES_ID, SPECIAL_HAND_THIEF_ID, SPECIAL_INCREASE_LEVEL_DOUBLE_PAIR_ID, + SPECIAL_JOKER_BOOSTER_ID, SPECIAL_MULTI_FOR_DIAMOND_ID + }; + use jokers_of_neon::models::status::game::game::{Game, GameState}; + use jokers_of_neon::models::status::shop::shop::BlisterPackResult; + use jokers_of_neon::store::{Store, StoreTrait}; + use jokers_of_neon::systems::game_system::{game_system, IGameSystemDispatcher, IGameSystemDispatcherTrait}; + use jokers_of_neon::tests::setup::{ + setup, setup::OWNER, setup::IDojoInitDispatcher, setup::IDojoInitDispatcherTrait + }; + use jokers_of_neon::tests::utils::{mock_game}; + use starknet::testing::set_contract_address; + + fn PLAYER() -> starknet::ContractAddress { + starknet::contract_address_const::<'PLAYER'>() + } + + #[test] + #[available_gas(30000000000000000)] + fn test_select_special_cards() { + let (world, systems) = setup::spawn_game(); + let mut store = StoreTrait::new(world); + let mut game = mock_game(ref store, PLAYER()); + + game.state = GameState::SELECT_SPECIAL_CARDS; + store.set_game(game); + + // Mock BlisterPackResult + let cards = array![ + SPECIAL_POINTS_FOR_FIGURES_ID, + SPECIAL_HAND_THIEF_ID, + SPECIAL_INCREASE_LEVEL_DOUBLE_PAIR_ID, + SPECIAL_JOKER_BOOSTER_ID, + SPECIAL_MULTI_FOR_DIAMOND_ID, + ]; // 5 special + store.set_blister_pack_result(BlisterPackResult { game_id: game.id, cards_picked: false, cards: cards.span() }); + + set_contract_address(PLAYER()); + systems.game_system.select_special_cards(game.id, array![4]); + + let game_after = store.get_game(game.id); + assert(game_after.state == GameState::SELECT_MODIFIER_CARDS, 'wrong GameState'); + + let blister_pack = store.get_blister_pack_result(game.id); + assert(blister_pack.cards_picked == true, 'cards_picked should be true'); + + // last special must be `SPECIAL_MULTI_FOR_DIAMOND` + let SPECIAL_PICKED = store.get_current_special_cards(game.id, game_after.len_current_special_cards - 1); + assert(SPECIAL_PICKED.effect_card_id == SPECIAL_MULTI_FOR_DIAMOND_ID, 'wrong special card'); + } +} diff --git a/src/utils/packs.cairo b/src/utils/packs.cairo new file mode 100644 index 0000000..8020305 --- /dev/null +++ b/src/utils/packs.cairo @@ -0,0 +1,131 @@ +use dojo::world::{IWorld, IWorldDispatcher, IWorldDispatcherTrait}; +use jokers_of_neon::constants::card::{JOKER_CARD, NEON_JOKER_CARD}; +use jokers_of_neon::constants::specials::{SPECIAL_HAND_THIEF_ID, SPECIAL_EXTRA_HELP_ID}; +use jokers_of_neon::models::data::game_deck::{GameDeckStore, GameDeckImpl}; +use jokers_of_neon::models::status::game::game::{Game, CurrentSpecialCards, CurrentSpecialCardsStore, GameState}; +use jokers_of_neon::store::{Store, StoreTrait}; +use jokers_of_neon::utils::constants::{is_special_card, is_modifier_card}; +use jokers_of_neon::utils::random::{Random, RandomImpl, RandomTrait}; +use jokers_of_neon::utils::shop::{get_current_special_cards, item_in_array}; + +fn open_blister_pack(world: IWorldDispatcher, ref store: Store, game: Game, blister_pack_id: u32) -> Span { + let mut ret = array![]; + let mut blister_pack = store.get_blister_pack(blister_pack_id); + let mut count_cards = 0; + + let mut guaranteed_cards = array![].span(); + let mut cards = array![]; + let mut probs = array![]; + let mut idx = 0; + loop { + if idx == blister_pack.cards.len() { + break; + } + if idx == 0 { + guaranteed_cards = *blister_pack.cards.at(idx); + } else { + cards.append(*blister_pack.cards.at(idx)); + probs.append(*blister_pack.probs.at(idx)); + } + idx += 1; + }; + + loop { + match guaranteed_cards.pop_front() { + Option::Some(card_id) => { + ret.append(*card_id); + count_cards += 1; + }, + Option::None => { break; } + } + }; + + let mut randomizer = RandomImpl::new(world); + if count_cards < blister_pack.size { + loop { + if count_cards == blister_pack.size { + break; + } + let number_random = randomizer.between::(0, 100); + let index_content = get_index_content(probs.span(), number_random); + let cards_temp = *cards.at(index_content); + let card_id_random = *cards_temp.at(randomizer.between::(0, cards_temp.len() - 1)); + if is_special_card(card_id_random) { + let current_special_cards = get_current_special_cards(ref store, @game); + if item_in_array(@ret, card_id_random) || item_in_array(@current_special_cards, card_id_random) { + continue; + } + } + ret.append(card_id_random); + count_cards += 1; + } + } + ret.span() +} + +fn select_cards_from_blister( + world: IWorldDispatcher, ref game: Game, cards_result: Span, cards_index: Array +) { + let mut idx = 0; + loop { + if idx == cards_index.len() { + break; + } + let card_id = *cards_result.at(*cards_index.at(idx)); + if is_special_card(card_id) { + assert(game.len_current_special_cards + 1 <= game.len_max_current_special_cards, 'special cards full'); + + if card_id == SPECIAL_HAND_THIEF_ID { + game.max_hands += 1; + game.max_discard += 1; + } + if card_id == SPECIAL_EXTRA_HELP_ID { + game.len_hand += 2; + } + + let current_special_cards = CurrentSpecialCards { + game_id: game.id, + idx: game.len_current_special_cards, + effect_card_id: card_id, + is_temporary: false, + remaining: 0 + }; + CurrentSpecialCardsStore::set(@current_special_cards, world); + + game.len_current_special_cards += 1; + } else if is_modifier_card(card_id) { + let mut game_deck = GameDeckStore::get(world, game.id); + game_deck.add(world, card_id); + GameDeckStore::set(@game_deck, world); + } else { + let mut game_deck = GameDeckStore::get(world, game.id); + game_deck.add(world, card_id); + GameDeckStore::set(@game_deck, world); + + // check joker + if card_id == JOKER_CARD || card_id == NEON_JOKER_CARD { + game.current_jokers += 1; + } + } + idx += 1; + }; +} + +fn get_index_content(probs: Span, number_random: u32) -> u32 { + let mut probs = probs; + let mut acum = 0; + let mut idx = 0; + loop { + match probs.pop_front() { + Option::Some(prob) => { + acum += *prob; + + if number_random < acum { + break idx; + } + idx += 1; + }, + Option::None => { break 0; } + } + } +} diff --git a/src/utils/shop.cairo b/src/utils/shop.cairo index 47a88be..96c0eb3 100644 --- a/src/utils/shop.cairo +++ b/src/utils/shop.cairo @@ -1,6 +1,7 @@ use dojo::world::{IWorld, IWorldDispatcher, IWorldDispatcherTrait}; use jokers_of_neon::models::status::game::game::Game; use jokers_of_neon::store::{Store, StoreTrait}; +use jokers_of_neon::utils::constants::is_special_card; use jokers_of_neon::utils::random::{Random, RandomImpl, RandomTrait}; fn item_in_array<