Skip to main content
← Back

Cobblemon Force Battle NPC & Stat Tracking

Three related Cobblemon behaviours. Moves to Players has NPCs move towards players within a certain range. Force Battle initiates battles with players that they are right next to and can see. Finally, Stat Tracking records wins, losses, and interaction counts on both the player and the NPC, so later behaviours can branch on the result (alt dialogue after first defeat, a different reward on the third visit, a rematch dialogue branch that scales with rematch count). Together they are the foundation for trainers on an adventure Cobblemon server and mimic the battle mechanics of the mainline Pokemon games.

Moves to Players and Force Battle

Add these behaviours to an NPC, set the 'enabled' flags to true, and the NPC starts scanning every 5 ticks for players inside 2 different ranges. When a player is detected in the movement range, the NPC will start walking towards them. If they get close enough, the NPC will stop and trigger a dialogue and then the battle. Detection uses q.entity.can_see, so the NPC needs actual line of sight, not just proximity.

A few details that make the behaviour usable in practice:

  • Ignore perm: players with specified 'ignore perm's are skipped. The default is npc.battle.won.{{npc}}_forced_battle, so once a player has beaten the NPC it does not re-trigger.
  • Battle-state gating: players already in a battle or another dialogue are skipped, so two force-battle NPCs in the same room do not fight over the same player.
  • One target per tick: the first eligible player breaks the loop. As such, a crowd does not get sequentially yanked into back-to-back battles.

The pre-battle dialogue is configurable. Leaving it blank picks from a pool of generic taunts. After-battle dialogue, scripts, and commands are layered on through the after_dialogue_* behaviours, the same composable hooks the rest of the dialogue system uses. As such, a force-battle NPC can grant a perm, give a reward, or open a follow-up conversation when the player wins.

Use cases

  • Wild trainers: the classic Pokémon trope, dropped into the world without writing any scripts.
  • Gym leaders: pair with battle_won_perm to grant a badge perm on win, and the NPC stops triggering. Pair with optional_battle for a rematch button after the first defeat.
  • Ambush enemies: force_battle_radius of 8 or more with a hidden NPC makes a "you can't sneak past" choke point.
  • Tutorial fights: small radius, scripted party, generous afterparty rewards.

Stat tracking

The Track Battles behaviour is a one-toggle add-on. When enabled, every battle resolves into several datapoints:

  • times_won and times_lost on the NPC's data (lifetime W/L for the trainer).
  • times_won and times_lost for this player against this NPC on the player's data, along with total_wins and total_losses (both in general and against NPCs).

A companion behaviour, Track Interactions, does the same for non-battle interactions. times_interacted lives on both the NPC and the player. Both systems write through shared cobblemon:update_npc_tracked_stat and cobblemon:update_player_tracked_stat helpers, so any future tracked stat can hook in without touching the battle code.

The scope lens query tool

The behaviour exposes a debug query: hold a Scope Lens (configurable item) with the npc.check_tracked_stats perm (also configurable) and right-click any tracked NPC. The chat prints:

[INFO]: NPC <uuid> has been interacted with N times.
[INFO]: NPC <uuid> battle record: W wins, L losses.

It is intentionally hidden behind both a perm and a held item, so admins can drop in to audit an NPC without exposing the query to regular players. Other systems can read the same q.player.data.tracked_stat.* values from Molang to branch on them. As such, a quest can offer a special reward if the player has beaten a specific NPC three times, with no extra scripts required.

Techy Stuff

  • Built on Cobblemon's behaviour, dialogue, and Molang systems, configured in JSON.
  • The force-battle scanner runs as an add_tasks_to_activity task on minecraft:idle priority 0, throttled to every 5 game ticks via math.mod(q.entity.world.game_time, 5).
  • Battle outcomes hook into Cobblemon's battle callbacks. Tracked stat writes go through the shared update_npc_tracked_stat / update_player_tracked_stat helpers and persist via save_data().
  • The scope-lens query reuses the same print_and_tell chat helper as the dialogue and animation-interaction systems.

Built for the Callisto and Cobblewilds Cobblemon servers, but available for commission.