Integration
Creating Quests
Define quests in your game world using the quest system structures:
use quest::models::{QuestDefinition, QuestTask, QuestMetadata};
// Define quest tasks
let task = QuestTask {
id: 'win_battles',
description: "Win battles against other players",
total: 10_u128,
};
// Create quest definition
let definition = QuestDefinition {
id: 'daily_pvp',
start: get_block_timestamp(), // Quest start time
end: get_block_timestamp() + 86400, // 24 hours later
duration: 3600, // 1 hour active duration
interval: 86400, // Daily interval
tasks: array![task],
conditions: array![], // No prerequisites
rewarder: rewarder_contract_address,
};
// Emit creation event
emit!(world, QuestCreation {
id: definition.id,
definition,
metadata: quest_metadata,
});Quest Timing
Quests support flexible timing patterns:
One-time Quests
QuestDefinition {
start: quest_start_time,
end: quest_end_time,
duration: 0, // No duration limit
interval: 0, // No recurrence
// ...
}Recurring Quests
QuestDefinition {
start: first_occurrence,
end: last_possible_occurrence,
duration: 3600, // 1 hour duration
interval: 86400, // Every 24 hours
// ...
}Limited-time Events
QuestDefinition {
start: event_start,
end: event_end,
duration: event_end - event_start,
interval: 0,
// ...
}Progress Tracking
Track player progress by emitting advancement events:
use quest::events::QuestAdvancement;
// When player makes progress on a task
fn update_quest_progress(
world: IWorldDispatcher,
player_id: ContractAddress,
task_id: felt252,
progress_amount: u128
) {
emit!(world, QuestAdvancement {
player_id,
quest_id: 'daily_pvp',
task_id,
interval_id: get_current_interval_id(),
timestamp: get_block_timestamp(),
count: progress_amount,
});
}Quest Completion
Handle quest completion and reward claiming:
use quest::events::QuestCompletion;
// Mark quest as completed
fn complete_quest(
world: IWorldDispatcher,
player_id: ContractAddress,
quest_id: felt252
) {
emit!(world, QuestCompletion {
player_id,
quest_id,
interval_id: get_current_interval_id(),
timestamp: get_block_timestamp(),
unclaimed: true, // Rewards not yet claimed
lock_count: 0,
});
}Quest Metadata
Define quest display information:
{
"name": "Daily PvP Champion",
"description": "Defeat 10 opponents in PvP battles",
"icon": "https://example.com/quest-icon.png",
"registry": "0x...", // Registry contract address
"rewards": [
{
"name": "Victory Token",
"description": "Exclusive victory commemorative NFT",
"icon": "https://example.com/reward-icon.png"
}
]
}Frontend Integration
The Controller automatically provides quest UI when configured:
Using the Hook
import { useQuests } from '@cartridge/connector';
function QuestComponent() {
const { quests, status, refresh } = useQuests();
if (status === 'loading') return <div>Loading quests...</div>;
if (status === 'error') return <div>Error loading quests</div>;
return (
<div>
{quests.map(quest => (
<div key={quest.id}>
<h3>{quest.name}</h3>
<div>Progress: {quest.progression}%</div>
{quest.completed && !quest.claimed && (
<button onClick={() => claimReward(quest)}>
Claim Reward
</button>
)}
</div>
))}
</div>
);
}Quest Properties
Each quest object contains:
type QuestProps = {
id: string;
intervalId: number;
name: string;
end: number;
completed: boolean;
locked: boolean;
claimed: boolean;
progression: number;
registry: string;
rewards: Item[];
tasks: {
description: string;
total: bigint;
count: bigint;
}[];
};Quest Conditions
Create quest dependencies:
// Quest that requires completing other quests
QuestDefinition {
id: 'master_quest',
conditions: array!['basic_quest', 'intermediate_quest'],
// ... other fields
}Conditional quests will remain locked until all prerequisite quests are completed.
Best Practices
Quest Design
- Keep tasks clear and achievable
- Use appropriate timing for your game's pacing
- Balance difficulty with rewards
- Consider player timezone differences for timed events
Progress Tracking
- Emit progress events immediately when actions occur
- Use consistent task identifiers across your game
- Include sufficient progress data for proper tracking
- Handle edge cases (player disconnection, etc.)
Performance
- Use historical events in Torii for progress data
- Consider batching progress updates for frequent actions
- Cache quest metadata to reduce on-chain reads
- Implement proper error handling for failed events
Example Implementation
For a complete working example, see the GitHub repository quest implementation.