Skip to content

Commit

Permalink
add summon system
Browse files Browse the repository at this point in the history
  • Loading branch information
willemolding committed Nov 19, 2023
1 parent f01cf0f commit ce254fb
Show file tree
Hide file tree
Showing 8 changed files with 157 additions and 33 deletions.
9 changes: 8 additions & 1 deletion contracts/src/cards/actions.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use spellcrafter::cards::properties::{
requires_hot_gt, requires_light_gt, requires_dark_gt, chaos_delta_fallback,
power_delta_fallback, hotcold_delta_fallback, lightdark_delta_fallback
};
use spellcrafter::constants::{CHAOS_STAT, POWER_STAT, HOTCOLD_STAT, LIGHTDARK_STAT, BARRIERS_STAT, POLAR_STAT_MIDPOINT};
use spellcrafter::constants::{CHAOS_STAT, POWER_STAT, HOTCOLD_STAT, LIGHTDARK_STAT, BARRIERS_STAT, POLAR_STAT_MIDPOINT, TICKS, CHAOS_PER_TICK};


// modify the game state as demanded by this card
Expand Down Expand Up @@ -86,6 +86,13 @@ fn make_fallback_stat_changes(world: IWorldDispatcher, game_id: u128, card_id: u
}
}

/// Move time forward for the game by this number of ticks
/// Chaos increases by one point per tick
fn tick(world: IWorldDispatcher, game_id: u128, amount: u32) {
increase_stat(world, game_id, TICKS, amount);
increase_stat(world, game_id, CHAOS_STAT, amount * CHAOS_PER_TICK);
}

fn bust_barrier(world: IWorldDispatcher, game_id: u128) {
decrease_stat(world, game_id, BARRIERS_STAT, 1);
}
Expand Down
2 changes: 2 additions & 0 deletions contracts/src/components.cairo
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
mod owner;
mod value_in_game;
mod occupied;
mod familiar;

use owner::Owner;
use value_in_game::ValueInGame;
use occupied::Occupied;
use familiar::Familiar;
9 changes: 9 additions & 0 deletions contracts/src/components/familiar.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

// Represents an entity that is a familiar
#[derive(Model, Copy, Drop, Serde)]
struct Familiar {
#[key]
entity_id: u128,
game_id: u128,
familiar_type_id: u128,
}
19 changes: 19 additions & 0 deletions contracts/src/constants.cairo
Original file line number Diff line number Diff line change
@@ -1,16 +1,35 @@
/// Game Params ///

const INITIAL_BARRIERS: u32 = 3;
const CHAOS_PER_FORAGE: u32 = 3;
const ITEM_LIMIT: u32 = 7;
const FAMILIAR_LIMIT: u32 = 1;

// How many ticks each kind of action takes
const TICKS_PER_FORAGE: u32 = 3;
const TICKS_PER_SUMMON: u32 = 5;
const TICKS_PER_SEND: u32 = 3;
const CHAOS_PER_TICK: u32 = 1;

/// Valid IDs ///

// ensure these dont collide with card ids
const CHAOS_STAT: u128 = 10000;
const POWER_STAT: u128 = 10001;
const BARRIERS_STAT: u128 = 10002;
const ITEMS_HELD: u128 = 10003;
const FAMILIARS_HELD: u128 = 10004;
const TICKS: u128 = 10005;

// this is zero for stats that can go +/-
const POLAR_STAT_MIDPOINT: u32 = 2_147_483_647;

// polar stats
const HOTCOLD_STAT: u128 = 10004;
const LIGHTDARK_STAT: u128 = 10005;

// familiars
const RAVENS: u128 = 30001;
const CATS: u128 = 30002;
const SALAMANDERS: u128 = 30003;
const WOLF_SPIDERS: u128 = 30004;
94 changes: 62 additions & 32 deletions contracts/src/systems.cairo
Original file line number Diff line number Diff line change
@@ -1,28 +1,34 @@
use spellcrafter::types::Region;
use spellcrafter::types::{Region, FamiliarType};

#[starknet::interface]
trait ISpellCrafter<TContractState> {
fn new_game(self: @TContractState) -> u128;
fn forage(self: @TContractState, game_id: u128, region: Region) -> u128;
fn interact(self: @TContractState, game_id: u128, item_id: u128);
fn summon(self: @TContractState, game_id: u128, familiar_type: FamiliarType) -> u128;
}

#[dojo::contract]
mod spellcrafter_system {
use super::ISpellCrafter;
use starknet::get_caller_address;

use spellcrafter::constants::{INITIAL_BARRIERS, BARRIERS_STAT, HOTCOLD_STAT, LIGHTDARK_STAT, POLAR_STAT_MIDPOINT, CHAOS_STAT, ITEMS_HELD, CHAOS_PER_FORAGE, ITEM_LIMIT};
use spellcrafter::types::Region;
use spellcrafter::components::{Owner, ValueInGame};
use spellcrafter::constants::{
INITIAL_BARRIERS, BARRIERS_STAT, HOTCOLD_STAT, LIGHTDARK_STAT, POLAR_STAT_MIDPOINT,
CHAOS_STAT, ITEMS_HELD, CHAOS_PER_FORAGE, ITEM_LIMIT, FAMILIAR_LIMIT, FAMILIARS_HELD,
TICKS_PER_SUMMON,
};
use spellcrafter::types::{Region, FamiliarType, FamiliarTypeTrait};
use spellcrafter::components::{Owner, ValueInGame, Familiar};
use spellcrafter::utils::assertions::{assert_caller_is_owner, assert_is_alive};
use spellcrafter::utils::random::pass_check;
use spellcrafter::cards::selection::random_card_from_region;
use spellcrafter::cards::actions::{increase_stat, stat_meets_threshold, enact_card, is_dead, bust_barrier};
use spellcrafter::cards::actions::{
increase_stat, stat_meets_threshold, enact_card, is_dead, bust_barrier, tick,
};

#[external(v0)]
impl SpellCrafterImpl of ISpellCrafter<ContractState> {

fn new_game(self: @ContractState) -> u128 {
let world = self.world_dispatcher.read();
let game_id: u128 = world.uuid().into();
Expand All @@ -44,8 +50,13 @@ mod spellcrafter_system {
let world = self.world_dispatcher.read();
assert_caller_is_owner(world, get_caller_address(), game_id);
assert_is_alive(world, game_id);
assert(!stat_meets_threshold(world, game_id, ITEMS_HELD, Option::Some((ITEM_LIMIT, false))), 'Too many items held');

assert(
!stat_meets_threshold(
world, game_id, ITEMS_HELD, Option::Some((ITEM_LIMIT, false))
),
'Too many items held'
);

// TODO This is not simulation safe. Ok for quick protyping only
let tx_info = starknet::get_tx_info().unbox();
let seed = tx_info.transaction_hash;
Expand All @@ -62,6 +73,7 @@ mod spellcrafter_system {
return card_id;
}

// Place a card owned by the player in this game into the spell
fn interact(self: @ContractState, game_id: u128, item_id: u128) {
let world = self.world_dispatcher.read();
assert_caller_is_owner(world, get_caller_address(), game_id);
Expand All @@ -73,7 +85,7 @@ mod spellcrafter_system {

let owned = get!(world, (item_id, game_id), ValueInGame).value;
assert(owned > 0, 'Item is not owned');

let chaos = get!(world, (CHAOS_STAT, game_id), ValueInGame).value;

if !pass_check(seed, chaos) {
Expand All @@ -84,13 +96,43 @@ mod spellcrafter_system {
enact_card(world, game_id, item_id);
}
}

// summon a familiar which can be sent to retrieve items
fn summon(self: @ContractState, game_id: u128, familiar_type: FamiliarType) -> u128 {
let world = self.world_dispatcher.read();
assert_caller_is_owner(world, get_caller_address(), game_id);
assert_is_alive(world, game_id);
assert(
!stat_meets_threshold(
world, game_id, FAMILIARS_HELD, Option::Some((FAMILIAR_LIMIT, false))
),
'Too many familiars'
);
// Move time forward, also increase chaos
tick(world, game_id, TICKS_PER_SUMMON);

// create a new entity for the familiar
let entity_id: u128 = world.uuid().into();
set!(
world,
(
Familiar { entity_id, game_id, familiar_type_id: familiar_type.stat_id() },
Owner { entity_id, address: get_caller_address() },
)
);

// increase the total number of familiars held in this game
increase_stat(world, game_id, FAMILIARS_HELD, 1);

return entity_id;
}
}
}


#[cfg(test)]
mod forage_tests {
use dojo::world::{ IWorldDispatcher, IWorldDispatcherTrait};
mod forage_tests {
use dojo::world::{IWorldDispatcher, IWorldDispatcherTrait};

use spellcrafter::types::Region;
use spellcrafter::utils::testing::{deploy_game, SpellcraftDeployment};
Expand All @@ -102,10 +144,7 @@ mod forage_tests {
#[test]
#[available_gas(300000000000)]
fn test_forage() {
let SpellcraftDeployment {
world,
system,
} = deploy_game();
let SpellcraftDeployment{world, system, } = deploy_game();

let game_id = system.new_game();
let card_id = system.forage(game_id, Region::Forest);
Expand All @@ -117,13 +156,10 @@ mod forage_tests {

#[test]
#[available_gas(300000000000)]
#[should_panic(expected: ('Too many items held', 'ENTRYPOINT_FAILED') )]
#[should_panic(expected: ('Too many items held', 'ENTRYPOINT_FAILED'))]
fn cannot_exceed_max_items() {
let SpellcraftDeployment {
world,
system
} = deploy_game();

let SpellcraftDeployment{world, system } = deploy_game();

let game_id = system.new_game();

// // pre conditions
Expand Down Expand Up @@ -155,7 +191,7 @@ mod interact_tests {
use array::ArrayTrait;
use option::OptionTrait;
use serde::Serde;

use dojo::world::{IWorldDispatcher, IWorldDispatcherTrait};
use dojo::test_utils::deploy_contract;

Expand All @@ -165,15 +201,12 @@ mod interact_tests {
use super::{spellcrafter_system, ISpellCrafterDispatcher, ISpellCrafterDispatcherTrait};

#[test]
#[should_panic(expected: ('Item is not owned', 'ENTRYPOINT_FAILED') )]
#[should_panic(expected: ('Item is not owned', 'ENTRYPOINT_FAILED'))]
#[available_gas(300000000000)]
fn reverts_if_card_not_owned() {
let CARD_ID: u128 = 1;

let SpellcraftDeployment {
world,
system,
} = deploy_game();
let SpellcraftDeployment{world, system, } = deploy_game();

let game_id = system.new_game();
system.interact(game_id, CARD_ID);
Expand All @@ -184,14 +217,11 @@ mod interact_tests {
fn works_if_card_owned() {
let CARD_ID: u128 = 1;

let SpellcraftDeployment {
world,
system
} = deploy_game();
let SpellcraftDeployment{world, system } = deploy_game();

let game_id = system.new_game();

set!(world, ValueInGame{ entity_id: CARD_ID, game_id: game_id, value: 1 });
set!(world, ValueInGame { entity_id: CARD_ID, game_id: game_id, value: 1 });
system.interact(game_id, CARD_ID);
}
}
4 changes: 4 additions & 0 deletions contracts/src/types.cairo
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
mod region;
mod action;
mod familiar;

use region::Region;
use action::{Action, ActionTrait};
use familiar::{FamiliarType, FamiliarTypeTrait};
22 changes: 22 additions & 0 deletions contracts/src/types/action.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/// An action that an entity can be performing while time passes
#[derive(Serde, Copy, Drop, Introspect)]
enum Action {
None: (),
ForageForest: (),
ForageMeadow: (),
ForageVolcano: (),
ForageCave: (),
}

#[generate_trait]
impl ImplAction of ActionTrait {
fn id(self: Action) -> u8 {
match self {
Action::None => 0,
Action::ForageForest => 1,
Action::ForageMeadow => 2,
Action::ForageVolcano => 3,
Action::ForageCave => 4,
}
}
}
31 changes: 31 additions & 0 deletions contracts/src/types/familiar.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use spellcrafter::constants::{RAVENS, CATS, SALAMANDERS, WOLF_SPIDERS};
use spellcrafter::types::{Action, ActionTrait};

#[derive(Serde, Copy, Drop, Introspect)]
enum FamiliarType {
Raven: (),
Cat: (),
Salamanger: (),
WolfSpider: (),
}

#[generate_trait]
impl ImplFamiliarType of FamiliarTypeTrait {
fn stat_id(self: FamiliarType) -> u128 {
match self {
FamiliarType::Raven => RAVENS,
FamiliarType::Cat => CATS,
FamiliarType::Salamanger => SALAMANDERS,
FamiliarType::WolfSpider => WOLF_SPIDERS,
}
}

fn default_action_id(self: FamiliarType) -> u8 {
match self {
FamiliarType::Raven => Action::ForageForest.id(),
FamiliarType::Cat => Action::ForageMeadow.id(),
FamiliarType::Salamanger => Action::ForageVolcano.id(),
FamiliarType::WolfSpider => Action::ForageCave.id(),
}
}
}

0 comments on commit ce254fb

Please sign in to comment.