Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Quests #85

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Scarb.lock
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@ dependencies = [
name = "origami_map"
version = "1.0.0-alpha.5"

[[package]]
name = "origami_quest"
version = "1.0.0-alpha.0"
dependencies = [
"dojo",
]

[[package]]
name = "origami_random"
version = "1.0.0-alpha.5"
Expand Down
3 changes: 2 additions & 1 deletion Scarb.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ members = [
"crates/rating",
"crates/security",
"crates/token",
"crates/governance",
"crates/quest",
"crates/governance"
]

[workspace.package]
Expand Down
1 change: 1 addition & 0 deletions crates/quest/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
target
17 changes: 17 additions & 0 deletions crates/quest/Scarb.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "origami_quest"
version = "1.0.0-alpha.0"
description = "Quests"
homepage = "https://github.com/dojoengine/origami/tree/crates/quest"

[dependencies]
dojo.workspace = true

[scripts]
build = "sozo build --manifest-path ./Scarb.toml"
plan = "sozo migrate plan --manifest-path ./Scarb.toml"
migrate = "sozo migrate apply --manifest-path ./Scarb.toml"

[lib]


7 changes: 7 additions & 0 deletions crates/quest/dojo_dev.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

[namespace]
default = "origami_quest"

[world]
seed = "origami_quest"
name = "origami_quest"
177 changes: 177 additions & 0 deletions crates/quest/src/components/quest_registry.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
use starknet::ContractAddress;
use origami_quest::models::quest::Quest;

#[starknet::interface]
trait IQuestRegistry<T> {
fn register_quest(ref self: T, new_quest: Quest) -> felt252;
fn progress(ref self: T, quest_id: felt252, player_id: ContractAddress);
}

#[starknet::component]
mod quest_registry_comp {
use starknet::{ContractAddress, get_caller_address, get_contract_address};
use dojo::world::{
IWorldProvider, IWorldProviderDispatcher, IWorldProviderDispatcherTrait, IWorldDispatcher,
IWorldDispatcherTrait
};
use dojo::model::Model;
use dojo::contract::{IContract, IContractDispatcher, IContractDispatcherTrait};

use origami_quest::models::quest::{
Quest, QuestStore, QuestCounter, QuestCounterStore, QuestType
};
use origami_quest::helpers::quest::{QuestTrait, QuestHelperTrait};
use origami_quest::utils::get_contract_infos;

#[storage]
struct Storage {}

mod Errors {
const NOT_NS_WRITER: felt252 = 'not namespace writer!';
const QUEST_ID_ALREADY_REGISTERED: felt252 = 'quest id already registered!';
const INVALID_QUEST_ID: felt252 = 'invalid quest id';
const INVALID_QUEST: felt252 = 'invalid quest';
const INVALID_CALLER: felt252 = 'invalid caller';
const INVALID_CROSS_NS: felt252 = 'invalid cross ns';
}

#[event]
#[derive(Drop, starknet::Event)]
enum Event {
QuestRegistered: QuestRegistered,
QuestProgress: QuestProgress,
}

#[derive(Drop, Serde, starknet::Event)]
struct QuestRegistered {
#[key]
id: felt252,
quest: Quest,
}

#[derive(Drop, Serde, starknet::Event)]
struct QuestProgress {
#[key]
quest_id: felt252,
#[key]
player_id: ContractAddress,
count: u64,
}

#[embeddable_as(ImplQuestRegistry)]
impl QuestRegistryImpl<
TContractState,
+HasComponent<TContractState>,
+IWorldProvider<TContractState>,
+IContract<TContractState>
> of super::IQuestRegistry<ComponentState<TContractState>> {
fn register_quest(ref self: ComponentState<TContractState>, new_quest: Quest) -> felt252 {
let world = self.get_contract().world();

// retrieve caller account
let caller_account = starknet::get_tx_info().unbox().account_contract_address;

// retrieve current namespace_hash
let namespace_hash = self.get_contract().namespace_hash();

// check caller account is writer for namespace
let is_writer = world.is_writer(namespace_hash, caller_account);
assert(is_writer, Errors::NOT_NS_WRITER);

InternalTrait::register_quest(ref self, new_quest)
}


fn progress(
ref self: ComponentState<TContractState>, quest_id: felt252, player_id: ContractAddress
) {
let world = self.get_contract().world();

// get caller
let caller = get_caller_address();
let caller_disp = IContractDispatcher { contract_address: caller };

let caller_namespace = caller_disp.namespace_hash();
let registry_namespace = IContractDispatcher {
contract_address: get_contract_address()
}
.namespace_hash();
// check same namespace
assert(caller_namespace == registry_namespace, Errors::INVALID_CROSS_NS);

// check caller is a contract registered in world / for selector
let caller_selector = caller_disp.selector();
// panic if not exists
let (_, caller_selector_address) = get_contract_infos(world, caller_selector);
// check retrieved address == caller address
assert(caller_selector_address == caller, Errors::INVALID_CALLER);

InternalTrait::progress(ref self, quest_id, player_id)
}
}

#[generate_trait]
impl InternalImpl<
TContractState, +HasComponent<TContractState>, +IWorldProvider<TContractState>
> of InternalTrait<TContractState> {
fn register_quest(ref self: ComponentState<TContractState>, new_quest: Quest) -> felt252 {
let world = self.get_contract().world();

// get / init quest
let mut quest = QuestStore::get(world, new_quest.id);

// check quest not already registered
assert(!quest.exists(), Errors::QUEST_ID_ALREADY_REGISTERED);

// check quest is valid
assert(new_quest.is_valid(), Errors::INVALID_QUEST);

// create quest
new_quest.set(world);

// // emit event
let id = quest.entity_id();
emit!(world, (Event::QuestRegistered(QuestRegistered { id, quest: new_quest })));

id
}


fn progress(
ref self: ComponentState<TContractState>, quest_id: felt252, player_id: ContractAddress
) {
let world = self.get_contract().world();

// get quest
let mut quest = QuestStore::get(world, quest_id);

// check valid quest
assert(quest.exists(), Errors::INVALID_QUEST_ID);

let mut counter = QuestCounterStore::get(world, quest_id, player_id);

match quest.quest_type {
QuestType::Infinite => {
// increase counter on completion
if quest.is_completed(world, player_id) {
counter.count = counter.count + 1;
counter.set(world);
}
},
QuestType::OneTime => {
// set count to 1
if counter.count < 1 && quest.is_completed(world, player_id) {
counter.count = 1;
counter.set(world);
}
}
};
// // emit QuestProgress
emit!(
world,
(Event::QuestProgress(QuestProgress { quest_id, player_id, count: counter.count }))
);
}
}
}

51 changes: 51 additions & 0 deletions crates/quest/src/contracts/quest_registry.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use starknet::ContractAddress;
use origami_quest::models::quest::Quest;


#[dojo::contract]
mod quest_registry {
use starknet::{ContractAddress, get_caller_address};
use origami_quest::models::quest::{Quest};
use origami_quest::components::quest_registry::{quest_registry_comp, IQuestRegistry};

component!(path: quest_registry_comp, storage: quest_registry, event: QuestRegistryEvent);

#[storage]
struct Storage {
#[substorage(v0)]
quest_registry: quest_registry_comp::Storage,
}

#[event]
#[derive(Copy, Drop, starknet::Event)]
enum Event {
#[flat]
QuestRegistryEvent: quest_registry_comp::Event,
}

mod Errors {
const NOT_NS_WRITER: felt252 = 'not namespace writer!';
}


// impl QuestRegisterympl = quest_registry_comp::QuestRegisteryImpl<ContractState>;

//
// OR
//

#[abi(embed_v0)]
impl ImplQuestRegistry<ContractState> of IQuestRegistry<ContractState> {
fn register_quest(ref self: ContractState, new_quest: Quest) -> felt252 {
// no check on who can register quest
quest_registry_comp::InternalTrait::register_quest(ref self.quest_registry, new_quest)
}

fn progress(ref self: ContractState, quest_id: felt252, player_id: ContractAddress) {
quest_registry_comp::InternalTrait::progress(
ref self.quest_registry, quest_id, player_id
)
}
}
}

Loading
Loading