Skills reference

MythicMobs-class declared skills — pure data, zero script calls on the hot path

View as Markdown

Skills give a declared mob MythicMobs-style abilities: when X happens, with chance C and under conditions, apply mechanics M to targets T. They are Sulfur’s first intentional extension beyond vanilla gameplay.

Native implementation, not interop. Sulfur’s skill system is a from-scratch native API with MythicMobs feature parity as the checklist. No Bukkit/Java plugin is ever loaded or executed, and no YAML compatibility is pursued. The underlying subsystems a mechanic calls into (damage, effects) are the already-ported, jar-faithful vanilla paths — so skill damage respects vanilla armor, i-frames, and knock-on effects exactly.

Skills are even stronger than goals on the declare-once principle: a skill is captured as pure data at load — no Starlark callable is retained at all. The Go hot path interprets the declaration structs directly, so a skill-bearing mob burns zero Starlark calls per tick, idle or active.

Declaring skills

Skills attach to a mob declaration via the skills list of declare_mob:

1declare_mob(
2 name = "static_sheep",
3 base_type = "sheep",
4 attributes = {"max_health": 100.0, "movement_speed": 0.3},
5 skills = [
6 skill(
7 trigger = "timer", # when
8 interval = 100, # required iff trigger == "timer"
9 chance = 1.0, # optional (0, 1]
10 conditions = [condition("health_below", value = 0.5)], # optional, AND-ed
11 targeter = targeter("players_in_radius", r = 10.0), # who
12 mechanics = [ # what, in order
13 mechanic("damage", amount = 5.0),
14 mechanic("effect", effect = "poison", duration = 100, amplifier = 1),
15 ],
16 ),
17 ],
18)

All four builtins (skill, mechanic, targeter, condition) validate everything at load: unknown kinds, missing or forbidden arguments, out-of-range values, and — crucially — capabilities. mechanic("damage") in a plugin whose manifest lacks skills.damage is a loud load error (fail-closed), not a runtime denial.

skill(...)

1skill(trigger, interval=?, chance=1.0, conditions=?, targeter=, mechanics=[...])
ArgumentRules
triggerOne of "timer", "spawn", "damaged", "death". Unknown = load error.
intervalRequired for trigger="timer" (fire period in ticks, >= 1); forbidden for every other trigger.
chanceFire probability in (0, 1]. Default 1.0 — and a chance of exactly 1.0 draws no RNG at all. When < 1.0, the roll draws on the caster’s own per-entity seeded stream.
conditionsOptional list of condition(...) values; all must hold (AND).
targeterRequired, a targeter(...) value.
mechanicsRequired, a non-empty list of mechanic(...) values, applied in declared order to each target.

Triggers

TriggerFires
timerEvery interval ticks, per mob (a countdown armed at spawn; the first fire lands interval ticks after spawn, then re-arms).
spawnOnce, when the mob spawns (after it is live and resolvable by targeters).
damagedAfter a hit lands and the mob survives (the ~onDamaged analogue).
deathOn death, after the loot roll and death broadcast (the ~onDeath analogue).

targeter(kind, r=?)

KindrResolves to
"self"forbiddenThe caster itself.
"nearest_player"required, > 0The nearest living player within r blocks (one target or none).
"players_in_radius"required, > 0Every living player within r blocks.
"mobs_in_radius"required, > 0Every living mob within r blocks, excluding the caster (scoped to the caster’s owning region).

mechanic(kind, ...)

KindArgumentsCapabilityRoutes through
"damage"amount (required, > 0) — raw pre-armor damageskills.damageThe ported vanilla hurt pipelines (applyDamage for players, applyDamageEntity for mobs), attributed to the caster as a mob attack — armor, i-frames, and knockback are vanilla-exact.
"effect"effect (required id), duration (ticks, >= 1), amplifier (>= 0)skills.effectsThe ported effect system (addPlayerEffect / addEntityEffect). Player targets get full modifier/tick semantics; mob targets currently get add + instant semantics (duration ticking for arbitrary mobs is on the roadmap).

Passing an argument that doesn’t belong to the kind (e.g. amount on an effect mechanic) is a load error — arguments are never silently dead.

Implemented effect ids

The effect mechanic accepts these ids, with or without the minecraft: prefix:

poison, regeneration, speed, slowness, weakness, haste, resistance, jump_boost, strength, wither, fire_resistance, water_breathing, instant_health, instant_damage.

An effect outside this set is a load error — never a silently-inert buff.

condition(kind, value)

KindvalueHolds when
"health_below"fraction of MaxHealth in (0, 1]The caster’s health is below value × MaxHealth.

Runtime semantics

Execution order per skill fire: conditions → chance gate → targeter resolve → mechanics in declared order, per target.

  • Per-mob state is just the timer countdowns and a re-entrancy guard; the declaration itself is shared immutable data across all spawned mobs of the type.
  • Re-entrancy is dropped: a mechanic whose side effect re-triggers the same mob (e.g. a self-damage mechanic firing the mob’s own damaged trigger) is suppressed while the skill executes — no cascades.
  • RNG discipline: the only RNG draw is the optional chance gate, on the caster’s own seeded per-entity stream. Vanilla mobs declare no skills, so no vanilla RNG stream is ever perturbed.

Complete example: zapmob

The zapmob test plugin declares a pig-based mob with a periodic AoE zap and a retaliation hit:

1# Two skills:
2# 1. ~onTimer:10 @PlayersInRadius{r=10} -> damage 5 + poison 100 ticks
3# 2. ~onDamaged @NearestPlayer{r=16} -> damage 2 (the retaliation zap)
4
5declare_mob(
6 name = "zapper",
7 base_type = "pig",
8 attributes = {
9 "max_health": 30.0,
10 "movement_speed": 0.25,
11 },
12 skills = [
13 skill(
14 trigger = "timer",
15 interval = 10,
16 targeter = targeter("players_in_radius", r = 10.0),
17 mechanics = [
18 mechanic("damage", amount = 5.0),
19 mechanic("effect", effect = "poison", duration = 100, amplifier = 0),
20 ],
21 ),
22 skill(
23 trigger = "damaged",
24 targeter = targeter("nearest_player", r = 16.0),
25 mechanics = [
26 mechanic("damage", amount = 2.0),
27 ],
28 ),
29 ],
30)

For what’s coming next (summon/heal/throw/teleport mechanics, meta-skills, cooldowns, more triggers and conditions, and ModelEngine-class models), see the Roadmap.