Skip to content

Task System

The Task System is Journey’s core framework for creating quests, objectives, and player activities. Tasks can range from simple single-objective quests to complex multi-stage adventures with different progression modes and conditional requirements.

Tasks in Journey consist of:

  • Subtasks: Individual objectives that players complete
  • Events: Triggers that advance subtask progress (28 event types)
  • Progression Types: How subtasks are presented (LINEAR, SEQUENTIAL, RANDOMIZED)
  • Filters: MoLang conditions to validate events
  • Rewards: What players receive upon completion
  • Repetition: Whether and how tasks can repeat

Every task follows this structure:

{
"name": "<gold>Display Name",
"description": [
"<gray>First line of description",
"<yellow>Additional details"
],
"sequential": "linear",
"subtask_count": 1,
"start_requirement": "",
"icon": {
"item_id": "minecraft:compass"
},
"repeat_type": "NONE",
"repeat_interval": 0,
"party_shared": false,
"rewards": [],
"tasks": []
}

Key Fields:

  • name - Display name (supports MiniMessage formatting)
  • description - Array of description lines
  • sequential - Progression type: "linear", "sequential", or "randomized" (also accepts true/false for backward compatibility)
  • subtask_count - For randomized: how many subtasks to select
  • start_requirement - MoLang condition to start (empty = always available)
  • icon - Item icon for UI display
  • repeat_type - "NONE", "DAILY", "WEEKLY", or "CUSTOM"
  • repeat_interval - Days between repeats (for CUSTOM type)
  • party_shared - Default sharing for all subtasks (default: false)
  • rewards - Task completion rewards
  • tasks - Array of subtasks

Journey supports three sequential types that control how subtasks are presented to players.

All subtasks are active simultaneously. Players can complete them in any order.

Use Cases:

  • Collection challenges
  • Exploration objectives
  • Parallel goals

Example:

{
"name": "<gold>Pokémon Variety Challenge",
"sequential": "linear",
"tasks": [
{
"id": "catch_fire",
"name": "Catch Fire Pokémon",
"event": "POKEMON_CAUGHT",
"filter": "q.pokemon.species.identifier == 'cobblemon:charmander'",
"target": 3
},
{
"id": "catch_water",
"name": "Catch Water Pokémon",
"event": "POKEMON_CAUGHT",
"filter": "q.pokemon.species.identifier == 'cobblemon:squirtle'",
"target": 3
},
{
"id": "catch_grass",
"name": "Catch Grass Pokémon",
"event": "POKEMON_CAUGHT",
"filter": "q.pokemon.species.identifier == 'cobblemon:bulbasaur'",
"target": 3
}
]
}

Subtasks must be completed in order. Only the current subtask is active.

Use Cases:

  • Story progression
  • Tutorial chains
  • Ordered quest steps

Example:

{
"name": "<gold>Gym Challenge",
"sequential": "sequential",
"tasks": [
{
"id": "defeat_trainers",
"name": "Defeat Gym Trainers",
"event": "BATTLE_VICTORY",
"filter": "1.0",
"target": 3
},
{
"id": "talk_to_leader",
"name": "Challenge Gym Leader",
"event": "ENTITY_INTERACT",
"filter": "q.entity.uuid == 'gym-leader-uuid'",
"target": 1
},
{
"id": "defeat_leader",
"name": "Defeat Gym Leader",
"event": "BATTLE_VICTORY",
"filter": "1.0",
"target": 1
}
]
}

A random subset of subtasks are selected from the available pool. Requires subtask_count parameter.

Use Cases:

  • Daily quests with variety
  • Random challenges
  • Replayable content

Example:

{
"name": "<gold>Daily Challenge",
"sequential": "randomized",
"subtask_count": 3,
"tasks": [
{
"id": "option1",
"name": "Catch Water Pokémon",
"event": "POKEMON_CAUGHT",
"filter": "q.pokemon.species.identifier == 'cobblemon:magikarp'",
"target": 5
},
{
"id": "option2",
"name": "Win Battles",
"event": "BATTLE_VICTORY",
"filter": "q.battle.is_wild",
"target": 10
},
{
"id": "option3",
"name": "Explore Zones",
"event": "ENTER_ZONE",
"filter": "1.0",
"target": 3
},
{
"id": "option4",
"name": "Evolve Pokémon",
"event": "POKEMON_EVOLVE",
"filter": "1.0",
"target": 1
},
{
"id": "option5",
"name": "Harvest Berries",
"event": "BERRY_HARVESTED",
"filter": "1.0",
"target": 20
}
]
}

Note: Exactly subtask_count (3) subtasks will be randomly selected from the 5 options.


Subtasks are the individual objectives within a task:

{
"id": "unique_subtask_id",
"name": "<yellow>Display Name",
"description": "<gray>What the player needs to do",
"event": "EVENT_TYPE",
"event_data": {},
"filter": "q.condition == value",
"target": 10,
"location": {
"x": 100,
"y": 64,
"z": 200
},
"time_limit": "30m",
"optional": false,
"party_shared": null,
"rewards": []
}

Key Fields:

  • id - Unique identifier within the task
  • name - Display name for this objective
  • description - Objective details
  • event - One of 28 Journey event types (see events.md)
  • event_data - Optional event-specific configuration
  • filter - MoLang expression to validate events
  • target - How many times the event must occur
  • location - Optional waypoint marker for players
  • time_limit - Optional time constraint (format: 30s, 5m, 1h)
  • optional - If true, not required for task completion
  • party_shared - Override task-level sharing (default: inherits from task)
  • rewards - Rewards given when this subtask completes

Journey supports four reward types:

Requires Impactor economy integration.

{
"type": "currency",
"data": {
"currency": "impactor:pokedollars",
"amount": 500
}
}

Executes a server command. Use {player} placeholder for player name.

{
"type": "command",
"data": {
"command": "give {player} minecraft:diamond 5"
}
}

Multiple Commands:

{
"type": "command",
"data": {
"command": "give {player} cobblemon:rare_candy 3"
}
},
{
"type": "command",
"data": {
"command": "tellraw {player} {\"text\":\"Reward claimed!\",\"color\":\"green\"}"
}
}

Executes MoLang scripts. Access to all player MoLang functions.

{
"type": "script",
"data": {
"scripts": [
"q.player.add_flag('quest_complete');",
"q.player.tell_minimessage('<gold>Quest completed!');"
]
}
}

Common Script Patterns:

// Set flags
"scripts": ["q.player.add_flag('gym_badge_boulder');"]
// Give levelable XP
"scripts": ["q.player.progress_levelable('trainer_rank', 100);"]
// Launch timeline
"scripts": ["q.player.launch_timeline('victory_cutscene');"]
// Multiple actions
"scripts": [
"q.player.add_flag('champion_defeated');",
"q.player.progress_levelable('story_progress', 1);",
"q.player.tell_minimessage('<gold>You are the Champion!');"
]

Launches a timeline sequence for the player.

{
"type": "timeline",
"data": {
"timeline": "namespace:timeline_id"
}
}

File: config/journey/tasks/catch_starter.json

{
"name": "<gold>Catch Your First Pokémon",
"description": [
"<gray>Catch any Pokémon to begin your journey!"
],
"sequential": "linear",
"icon": {
"item_id": "cobblemon:poke_ball"
},
"repeat_type": "NONE",
"repeat_interval": 0,
"rewards": [
{
"type": "currency",
"data": {
"currency": "impactor:pokedollars",
"amount": 100
}
}
],
"tasks": [
{
"id": "catch_first",
"name": "Catch a Pokémon",
"description": "Catch any wild Pokémon",
"event": "POKEMON_CAUGHT",
"filter": "1.0",
"target": 1,
"rewards": []
}
]
}

File: config/journey/tasks/gym_challenge.json

{
"name": "<blue>First Gym Challenge",
"description": [
"<gray>Defeat the trainers and challenge",
"<gray>the gym leader to earn your badge!"
],
"sequential": "sequential",
"start_requirement": "q.player.has_flag('tutorial_complete')",
"icon": {
"item_id": "cobblemon:gym_badge"
},
"repeat_type": "NONE",
"rewards": [
{
"type": "script",
"data": {
"scripts": [
"q.player.add_flag('gym_badge_boulder');",
"q.player.tell_minimessage('<gold>You earned the Boulder Badge!');"
]
}
}
],
"tasks": [
{
"id": "enter_gym",
"name": "Enter the Gym",
"event": "ENTER_ZONE",
"filter": "q.zone.uuid == 'pewter-gym-uuid'",
"target": 1,
"location": {
"x": 100,
"y": 64,
"z": 200
},
"rewards": []
},
{
"id": "defeat_trainers",
"name": "Defeat Trainers",
"description": "Defeat 2 gym trainers",
"event": "BATTLE_VICTORY",
"filter": "!q.battle.is_wild",
"target": 2,
"rewards": [
{
"type": "currency",
"data": {
"currency": "impactor:pokedollars",
"amount": 200
}
}
]
},
{
"id": "talk_to_leader",
"name": "Challenge the Leader",
"event": "ENTITY_INTERACT",
"filter": "q.entity.uuid == 'brock-uuid'",
"target": 1,
"rewards": []
},
{
"id": "defeat_leader",
"name": "Defeat Brock",
"description": "Win the gym battle",
"event": "BATTLE_VICTORY",
"filter": "!q.battle.is_wild",
"target": 1,
"rewards": [
{
"type": "currency",
"data": {
"currency": "impactor:pokedollars",
"amount": 500
}
}
]
}
]
}

File: config/journey/tasks/daily_challenge.json

{
"name": "<gold>Daily Challenge",
"description": [
"<gray>Complete 3 random objectives",
"<gray>for bonus rewards!"
],
"sequential": "randomized",
"subtask_count": 3,
"icon": {
"item_id": "minecraft:clock"
},
"repeat_type": "DAILY",
"repeat_interval": 1,
"rewards": [
{
"type": "currency",
"data": {
"currency": "impactor:pokedollars",
"amount": 1000
}
}
],
"tasks": [
{
"id": "catch_pokemon",
"name": "Catch Pokémon",
"event": "POKEMON_CAUGHT",
"filter": "1.0",
"target": 5,
"rewards": []
},
{
"id": "win_battles",
"name": "Win Battles",
"event": "BATTLE_VICTORY",
"filter": "1.0",
"target": 10,
"rewards": []
},
{
"id": "harvest_apricorns",
"name": "Harvest Apricorns",
"event": "APRICORN_HARVESTED",
"filter": "1.0",
"target": 20,
"rewards": []
},
{
"id": "evolve_pokemon",
"name": "Evolve Pokémon",
"event": "POKEMON_EVOLVE",
"filter": "1.0",
"target": 1,
"rewards": []
},
{
"id": "explore_zones",
"name": "Explore Zones",
"event": "ENTER_ZONE",
"filter": "1.0",
"target": 3,
"rewards": []
}
]
}

Note: 3 of these 5 objectives will be randomly selected each day.


Control when tasks become available using start_requirement:

{
"start_requirement": ""
}
{
"start_requirement": "q.player.has_flag('tutorial_complete')"
}
{
"start_requirement": "q.player.has_completed_task('journey:previous_quest')"
}
{
"start_requirement": "q.player.has_flag('gym_badge_1') && q.player.has_flag('gym_badge_2') && q.player.has_flag('gym_badge_3')"
}
{
"start_requirement": "q.player.levelable_level('trainer_rank') >= 25.0"
}

{
"repeat_type": "NONE",
"repeat_interval": 0
}
{
"repeat_type": "DAILY",
"repeat_interval": 1
}
{
"repeat_type": "WEEKLY",
"repeat_interval": 1
}
{
"repeat_type": "CUSTOM",
"repeat_interval": 3
}

Resets every 3 days.


Filters use MoLang to validate whether an event should count toward progress.

// Always accepts
"filter": "1.0"
// Always rejects
"filter": "0.0"
// Pokémon species
"filter": "q.pokemon.species.identifier == 'cobblemon:pikachu'"
// Zone UUID
"filter": "q.zone.uuid == 'zone-uuid-here'"
// Entity UUID
"filter": "q.entity.uuid == 'npc-uuid-here'"
// Item ID
"filter": "q.item.id == 'cobblemon:rare_candy'"
// Starter check
"filter": "q.pokemon.is_starter"
// Wild battle check
"filter": "q.battle.is_wild"
// Inverted
"filter": "!q.battle.is_wild"
// Zone restriction
"filter": "q.player.is_in_zone('zone-uuid')"
// Task completion check
"filter": "q.player.has_completed_task('other:quest')"
// Flag check
"filter": "q.player.has_flag('permission_granted')"
// AND logic
"filter": "q.pokemon.is_starter && q.pokemon.level >= 16.0"
// OR logic
"filter": "q.pokemon.species.identifier == 'cobblemon:charmander' || q.pokemon.species.identifier == 'cobblemon:charmeleon'"
// Complex combination
"filter": "q.entity.uuid == 'npc-uuid' && q.player.has_completed_task('previous:quest') && !q.player.has_flag('already_done')"

DO:

  • Use clear, descriptive names
  • Provide helpful descriptions
  • Set reasonable target values
  • Test filters before deployment
  • Use waypoint locations for guidance

DON’T:

  • Create tasks with 20+ subtasks
  • Use vague objective descriptions
  • Set impossibly high targets
  • Forget to test filter logic
  • Chain too many sequential requirements

DO:

  • Start with simple filters (1.0)
  • Add complexity incrementally
  • Test each condition separately
  • Use player functions for context
  • Document complex logic

DON’T:

  • Create overly complex filters
  • Assume all query paths exist
  • Forget the q. prefix
  • Use untested Cobblemon data paths

DO:

  • Scale rewards to difficulty
  • Provide milestone rewards (subtasks)
  • Use variety (currency + items + flags)
  • Test reward economy

DON’T:

  • Over-reward simple tasks
  • Forget subtask rewards
  • Create inflation with repeatable quests
  • Give rewards without testing

Tasks are automatically registered from JSON files in config/journey/tasks/.

config/journey/tasks/
├── starter_quests/
│ ├── choose_starter.json
│ └── first_catch.json
├── gym_challenges/
│ ├── pewter_gym.json
│ └── cerulean_gym.json
└── daily_quests/
└── daily_challenge.json

Tasks are identified by filename (without .json):

  • File: pewter_gym.json
  • Task ID: journey:pewter_gym

Use namespaces for organization:

  • File: gyms/pewter.json
  • Task ID: journey:gyms/pewter

Give a task to a player:

Terminal window
/journey starttask <player> <task_id>

Example:

Terminal window
/journey starttask @p journey:tutorial_quest

Show task progress in the player’s UI:

Terminal window
/journey tracktask <player> <task_id>

Force complete a task:

Terminal window
/journey completetask <player> <task_id>

Remove a task from a player (doesn’t mark as completed):

Terminal window
/journey removetask <player> <task_id>

Remove a task from the completed tasks list:

Terminal window
/journey removecompletedtask <player>

Manually progress a subtask:

Terminal window
/journey progresssubtask <player> <task_id> <subtask_id> <amount>

Example:

Terminal window
/journey progresssubtask @p journey:gather_items gather_wood 5

Force complete a specific subtask:

Terminal window
/journey completesubtask <player> <task_id> <subtask_id>

{
"start_requirement": "q.player.has_flag('unlock_post_game')",
"rewards": [
{
"type": "script",
"data": {
"scripts": ["q.player.add_flag('champion_defeated');"]
}
}
]
}
{
"start_requirement": "q.player.levelable_level('combat') >= 50.0",
"rewards": [
{
"type": "script",
"data": {
"scripts": ["q.player.progress_levelable('combat', 500);"]
}
}
]
}
{
"tasks": [
{
"id": "explore_zone",
"event": "ENTER_ZONE",
"filter": "q.zone.uuid == 'secret-area-uuid' && q.player.has_flag('map_unlocked')",
"target": 1
}
]
}

Tasks can be embedded in items (see task-items.md):

Terminal window
/journey taskitem give @p journey:quest_task minecraft:paper

Enable party sharing for tasks so party members contribute to shared progress:

{
"name": "<gold>Party Dungeon Quest",
"description": ["<gray>Work together to clear the dungeon!"],
"sequential": "linear",
"party_shared": true,
"tasks": [
{
"id": "defeat_bosses",
"name": "Defeat Dungeon Bosses",
"event": "BATTLE_VICTORY",
"filter": "q.battle.is_boss",
"target": 5
},
{
"id": "find_treasure",
"name": "Find Hidden Treasure",
"event": "BLOCK_BREAK",
"filter": "q.block.id == 'minecraft:chest'",
"target": 3,
"party_shared": false
}
]
}

Party Sharing Behavior:

  • party_shared at task level sets the default for all subtasks
  • party_shared at subtask level overrides the task default
  • When true, progress from any party member counts for all members
  • Requires Party System to be enabled
  • Works cross-server with NATS configuration

Example Use Cases:

  • Shared: Kill boss monsters together (all kills count)
  • Shared: Collect resources as a group (all items count)
  • Not Shared: Talk to NPC (each member talks separately)
  • Not Shared: Enter specific zone (each member enters separately)

Journey’s task system provides a flexible, event-driven framework for creating engaging player objectives. All examples in this documentation are verified against working production configurations.