Skip to content

Molang Scripting

Molang is the scripting language that powers everything dynamic in Journey. It’s how you write filters (“only count shiny Pokemon”), conditions (“require 3 gym badges”), and scripts (“give the player a reward and send a message”).

If you’ve written an if statement or a formula in a spreadsheet, you already know the basics. Molang is just expressions that evaluate to values.


Everything in Molang is either a number or a string:

42 A number
3.14 A decimal number
0.0 Zero (also means "false")
1.0 One (also means "true")
'hello' A string (use single quotes)

Booleans don’t exist as a separate type — 0.0 is false, anything non-zero is true.

10 + 5 → 15 Addition
10 - 5 → 5 Subtraction
10 * 5 → 50 Multiplication
10 / 5 → 2 Division
10 == 10 → 1.0 Equal
10 != 5 → 1.0 Not equal
10 > 5 → 1.0 Greater than
10 >= 10 → 1.0 Greater than or equal
10 < 5 → 0.0 Less than
10 <= 10 → 1.0 Less than or equal
true && false → 0.0 AND (both must be true)
true || false → 1.0 OR (at least one must be true)
!true → 0.0 NOT (flips true/false)
condition ? value_if_true : value_if_false

Example:

q.player.has_flag('vip') ? q.player.tell_minimessage('<gold>Welcome, VIP!') : q.player.tell_minimessage('<gray>Welcome!')

In script contexts (rewards, timeline actions), you can chain multiple statements with semicolons:

q.player.add_flag('quest_done'); q.player.tell_minimessage('<gold>Quest complete!'); q.player.progress_levelable('story', 50);

ContextExample
Task filters"filter": "q.pokemon.is_shiny"
Start requirements"start_requirement": "q.player.has_flag('tutorial_done')"
Script rewards"scripts": ["q.player.add_flag('badge_1');"]
Timeline actions"script": "q.player.tell_minimessage('<gold>Hello!')"
Zone scriptsEntry, exit, and inside scripts
Levelable filters"filter": {"type": "script", "filter": "q.block.id == 'minecraft:stone'"}
Buff scriptson_apply_script, on_tick_script, on_remove_script
Entity visibilityConditions that control per-player entity visibility
NPC dialoguesScripts in Cobblemon NPC dialogue actions

These functions are available in any context that has a player (task filters, rewards, timeline scripts, zone scripts, etc.). All are called on q.player.

FunctionReturnsDescription
q.player.has_task('journey:task_id')1.0 / 0.0Check if the player has an active task
q.player.has_subtask('journey:task_id', 'subtask_id')1.0 / 0.0Check if a specific subtask is active
q.player.has_completed_task('journey:task_id')1.0 / 0.0Check if the player has completed a task
q.player.has_completed_subtask('journey:task_id', 'subtask_id')1.0 / 0.0Check if a specific subtask is completed
q.player.start_task('journey:task_id')1.0 / 0.0Start a task for the player
FunctionReturnsDescription
q.player.has_flag('flag_name')1.0 / 0.0Check if the player has a flag
q.player.add_flag('flag_name')Add a flag to the player
q.player.remove_flag('flag_name')Remove a flag from the player
FunctionReturnsDescription
q.player.has_item('minecraft:diamond')1.0 / 0.0Check if player has at least 1
q.player.has_item('minecraft:diamond', 5)1.0 / 0.0Check if player has at least 5
q.player.remove_item('minecraft:diamond', 1)Remove items from inventory
q.player.levelNumberPlayer’s Minecraft XP level
FunctionReturnsDescription
q.player.pokedex()StructGet the player’s Pokedex data
q.player.starter_pokemon()Struct / 0.0Get the player’s starter Pokemon data
q.player.has_party_pokemon_matching('criteria')1.0 / 0.0Check if any party Pokemon matches criteria
q.player.remove_party_pokemon(slot)Remove the Pokemon in a specific party slot
q.player.party_slot_for_pokemon('criteria')NumberFind slot index (0-5) for a matching Pokemon, -1.0 if not found

Pokemon matching criteria use key=value pairs separated by spaces:

q.player.has_party_pokemon_matching('species=pikachu')
q.player.has_party_pokemon_matching('species=pikachu shiny=true')
FunctionReturnsDescription
q.player.has_levelable('name')1.0 / 0.0Check if player has a levelable
q.player.levelable_level('name')NumberGet current level
q.player.levelable_experience('name')NumberGet current XP
q.player.give_levelable('name')Give a levelable to the player
q.player.progress_levelable('name', amount)1.0Add XP to a levelable
q.player.remove_levelable('name')Remove a levelable
q.player.active_levelable()StringGet the currently active levelable ID (or 'none')
q.player.is_levelable_active('name')1.0 / 0.0Check if a specific levelable is active
q.player.switch_active_levelable('name')1.0 / 0.0Switch the active levelable
q.player.levelable_mode()StringGet mode: 'exclusive', 'single_active', or 'multi_active'
FunctionReturnsDescription
q.player.has_buff('buff_id')1.0 / 0.0Check if a buff is active
q.player.buff_amplifier('buff_id')Number / -1.0Get buff amplifier level
q.player.buff_remaining_ticks('buff_id')Number / -1.0Get remaining ticks
q.player.buff_count('buff_id')NumberCount active instances of this buff
q.player.apply_buff('buff_id')Apply with default duration
q.player.apply_buff('buff_id', duration)Apply with specific duration (ticks)
q.player.apply_buff('buff_id', duration, amplifier)Apply with duration and amplifier
q.player.apply_buff('buff_id', duration, amplifier, 'source')Apply with all parameters
q.player.remove_buff('buff_id')NumberRemove buff, returns count removed
q.player.remove_buff_by_source('source')Remove all buffs from a source
q.player.clear_buffs()NumberRemove all buffs, returns count
FunctionReturnsDescription
q.player.is_in_zone('zone-uuid')1.0 / 0.0Check if player is in a specific zone
FunctionReturnsDescription
q.player.tell_minimessage('<gold>Hello!')Send a MiniMessage-formatted chat message
q.player.execute_command('give {player} minecraft:diamond 1')Run a server command ({player} = player name)
FunctionReturnsDescription
q.player.launch_timeline('journey:timeline_id')Launch a timeline
q.player.play_cutscene('cutscene_id')1.0Play a cutscene
q.player.stop_cutscene()1.0Stop the current cutscene
q.player.in_cutscene()1.0 / 0.0Check if player is in a cutscene
q.player.cutscene_progress()NumberGet cutscene progress (0.0 - 1.0)
FunctionReturnsDescription
q.player.start_watching_entity('entity-uuid')Make player see an entity
q.player.stop_watching_entity('entity-uuid')Make player stop seeing an entity

See Entity Visibility for the full visibility system including types, groups, and managed spawn commands.

FunctionReturnsDescription
q.player.stop_battle()1.0Force the player out of their current battle
FunctionReturnsDescription
q.player.play_per_player_entity_animation('entity-uuid', 'anim')1.0Play entity animation only this player sees
FunctionReturnsDescription
q.player.push(x, y, z, force)Apply a force push to the player
FunctionReturnsDescription
q.player.backpack_add_item('item_id')1.0Add 1 item to backpack
q.player.backpack_add_item('item_id', count)1.0Add items to backpack
q.player.backpack_has_item('item_id')1.0 / 0.0Check if backpack has at least 1
q.player.backpack_has_item('item_id', count)1.0 / 0.0Check if backpack has at least N
q.player.backpack_remove_item('item_id', count)NumberRemove items, returns count removed
q.player.backpack_count_item('item_id')NumberCount items in backpack
q.player.backpack_free_slots()NumberGet number of empty slots
q.player.backpack_clear()1.0Clear all items
q.player.backpack_open()1.0Open backpack GUI
q.player.backpack_give_configured('config_id', count)Give a pre-configured item
q.player.backpack_has_configured('config_id', count)1.0 / 0.0Check for configured item
q.player.backpack_remove_configured('config_id', count)Remove configured item
FunctionReturnsDescription
q.player.player_party()StructGet party data (see below)

The party struct has these sub-functions:

q.player.player_party().exists() 1.0 if in a party
q.player.player_party().size() Number of members
q.player.player_party().is_leader() 1.0 if player is leader
q.player.player_party().has_member('Name') 1.0 if member exists
q.player.player_party().leader_name() Leader's name
q.player.player_party().name() Party name
q.player.player_party().max_members() Max allowed members
q.player.player_party().is_public() 1.0 if party is public
FunctionReturnsDescription
q.player.toggle_path_visualization()1.0 / 0.0Toggle path debug display
q.player.show_path_visualization(show)1.0 to show, 0.0 to hide
q.player.preview_path('path_id')1.0 / 0.0Preview a specific path

These functions are available in NPC contexts (Cobblemon NPC dialogues, NPC interactions). Called on the npc object.

FunctionDescription
npc.play_animation('anim_name')Play animation visible to all nearby players
npc.play_animation('anim_name', 'player-uuid')Play animation visible to one player only
FunctionDescription
npc.say('Hello! I am %npc%!')NPC says a message (%npc% = NPC name)
npc.say('Hello!', 'player-uuid')Say to a specific player only
FunctionReturnsDescription
npc.distance_to_player('player-uuid')NumberDistance in blocks to a player
FunctionReturnsDescription
npc.walk_path('path_id')1.0 / 0.0Start walking a path
npc.stop_walking()1.0 / 0.0Stop walking
npc.pause_walking()1.0 / 0.0Pause walking
npc.resume_walking()1.0 / 0.0Resume walking
npc.is_walking_path()1.0 / 0.0Check if currently walking
npc.walking_progress()NumberWalking progress (0.0 - 100.0)
npc.assign_path('path_id')Assign a path (auto-starts by default)
npc.assign_path('path_id', 0.0)Assign without auto-starting
npc.unassign_path()1.0Unassign current path
npc.has_assigned_path()1.0 / 0.0Check if a path is assigned
npc.get_assigned_path()StringGet assigned path ID
FunctionReturnsDescription
npc.set_visibility_condition("molang_expression")Set condition for all players
npc.set_player_visibility('player-uuid', "molang_expression")Set condition for one player
npc.force_show_to_player('player-uuid')1.0Force NPC visible to player
npc.force_hide_from_player('player-uuid')1.0Force NPC hidden from player
npc.is_visible_to_player('player-uuid')1.0 / 0.0Check if visible to player
npc.update_visibility()1.0Refresh visibility for all players

When events fire (in task filters and levelable filters), they provide event-specific data through the q namespace. See the Events page for the full reference of what each event provides.

Common patterns:

q.pokemon.species.identifier Pokemon species (e.g., 'cobblemon:pikachu')
q.pokemon.level Pokemon level
q.pokemon.is_shiny 1.0 if shiny
q.battle.is_pvw 1.0 if wild battle
q.battle.is_pvn 1.0 if NPC trainer battle
q.zone.uuid Zone UUID
q.block.id Block registry ID
q.item.id Item registry ID
q.entity.uuid Entity UUID

Standard math functions are available:

math.abs(-5) → 5 Absolute value
math.ceil(4.2) → 5 Round up
math.floor(4.8) → 4 Round down
math.round(4.6) → 5 Round to nearest
math.sqrt(16) → 4 Square root
math.pow(2, 3) → 8 Power
math.random() → 0.0-1.0 Random decimal
math.random(1, 10) → 1-10 Random integer in range
math.sin(90) → Sine
math.cos(0) → Cosine
math.min(5, 10) → 5 Minimum
math.max(5, 10) → 10 Maximum

Only allow this task when two previous quests are done:

{
"start_requirement": "q.player.has_completed_task('journey:gym_1') && q.player.has_completed_task('journey:gym_2')"
}

Show different messages based on a flag:

{
"type": "molang",
"script": "q.player.has_flag('secret_key') ? q.player.tell_minimessage('<green>The door opens!') : q.player.tell_minimessage('<red>The door is locked.')"
}

Set flags, give XP, send a message, and launch a cutscene:

{
"type": "script",
"data": {
"scripts": [
"q.player.add_flag('champion_defeated');",
"q.player.progress_levelable('story_progress', 500);",
"q.player.tell_minimessage('<gold><bold>You are the Champion!');",
"q.player.launch_timeline('journey:champion_celebration');"
]
}
}
npc.set_visibility_condition("q.player.has_completed_task('journey:rescue_professor')")

This NPC is invisible to players who haven’t completed the rescue quest.

{
"event": "POKEMON_CAPTURE",
"filter": "q.player.is_in_zone('safari-zone-uuid') && q.player.has_flag('safari_pass')",
"target": 10
}

Only counts catches inside the Safari Zone when the player has a safari pass.

q.player.has_party_pokemon_matching('species=pikachu') ? 1.0 : 0.0
temp.slot = q.player.party_slot_for_pokemon('species=pikachu');
temp.slot >= 0.0 ? q.player.tell_minimessage('<green>Pikachu is in slot ' + temp.slot) : q.player.tell_minimessage('<red>No Pikachu in your party!')

  • Short-circuit evaluation: Put cheap checks first — q.player.has_flag('eligible') && q.player.has_party_pokemon_matching('legendary=true') skips the expensive Pokemon check if the flag isn’t set
  • Strings use single quotes: 'cobblemon:pikachu' not "cobblemon:pikachu"
  • No semicolons in filters: Filters are single expressions. Semicolons are only for script contexts
  • Use q. prefix: Always q.player, q.pokemon, q.zone, etc.
  • No-param functions skip parentheses: q.pokemon.level works, no need for q.pokemon.level()
  • Test incrementally: Start with "filter": "1.0", verify events fire, then add conditions one at a time
  • Check server logs: Molang parse errors appear in the console