Skip to content

Add missing potion drink effects - #1264

Open
HarleyGilpin wants to merge 11 commits into
GregHib:mainfrom
HarleyGilpin:feat/potions
Open

Add missing potion drink effects#1264
HarleyGilpin wants to merge 11 commits into
GregHib:mainfrom
HarleyGilpin:feat/potions

Conversation

@HarleyGilpin

@HarleyGilpin HarleyGilpin commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Fills in the drink interactions for the potions that had item definitions but no effect, and fixes the bugs found while auditing them.

Six commits, each self-contained and green on its own.

Bugs

Several potions were not drinkable at all. strength_potion_2 had neither heals nor excess, so it was rejected by the guard at the top of Eating.consume. antidote+_mix_2 decanted into super_antipoison_mix_1, turning the item into a different potion when drunk. The extreme_magic block had its weight and examine values reversed across all four doses, so the 4-dose potion described itself as one dose.

weak_range_potion was registered for an item actually named weak_ranged_potion. Wildcard patterns that match nothing throw at startup, but literal ids that match nothing register silently, which is why this went unnoticed.

Energy potions assigned a tenth of the player's current run energy instead of adding a tenth of the maximum, so drinking one drained you. Nettle tea had the same shape.

Effect matching

potionEffects matched with startsWith, which made every effect depend on branch order. antipoison was tested before antipoison+ and antipoison++, so antidote+ and antidote++ granted 90 seconds of immunity instead of 9 and 12 minutes. The same flaw silently dropped any potion whose name did not begin with its family's prefix, including super_prayer and antidote+_mix.

Matching now happens on the item id with its dose suffix removed, so families are selected by exact name and branch order means nothing.

The dose handler treated any consumable ending _1 to _4 as a potion, so cooked crab meat and stealing creation food reported doses remaining. It now recognises potions by their empty vial, which also covers 5-dose potions that the old range excluded.

New effects

Extreme potions and super prayer. Overload grants the boost of all five extremes at once, so the two share one definition rather than repeating the amounts where they could drift apart.

Stealing creation and castle wars potions were undrinkable, missing both a dose chain and an effect. Each reuses the effect of the potion it copies. The examine text identifies the tier: the stealing creation ranging, magic, defence, prayer and energy potions are ordinary strength, while its attack and strength potions and all six castle wars potions are supers.

The poison chalice was consumed and vanished without doing anything. It now leaves a cocktail glass and applies one of seven random outcomes, from a slight taste of apricot through stat drains and heals to a large boost, or a small boost followed by damage. Both healing outcomes scale with maximum life points.

All ten Herblore Habitat potions now work. The mining and woodcutting potions do not bank every resource; each ore or log gathered has a chance to open a short window during which they go to the bank instead of the inventory. The farming potion gives a chance of an extra herb on herb patches only, and the fishing potion turns some raw sharks into raw baron sharks.

Testing

PotionEffectsTest, PoisonChaliceTest and JujuTest are new, covering every bug above as a regression plus the new effects. One asserts that overload and an extreme ranging potion produce the same boost, so the shared definition cannot drift. Full suite passes: 411 classes, 3513 tests.

The two dose strength potion had neither `heals` nor `excess`, so it was
rejected by the guard in `Eating.consume` and could not be drunk at all.

`antidote+_mix_2` decanted into `super_antipoison_mix_1`, turning the item
into a different potion when drunk.

The `extreme_magic` dose block had its `weight` and `examine` values in
reverse order, so the four dose potion described itself as one dose.

`weak_range_potion` was registered for an item named `weak_ranged_potion`,
so the weak ranged potion had no effect. Literal ids are not validated at
startup the way wildcard patterns are, so this registered silently.

Nettle tea assigned run energy rather than adding to it, discarding
whatever the player already had.
`potionEffects` matched on `startsWith`, which made every effect depend on
the order of the branches. `antipoison` was tested before `antipoison+` and
`antipoison++`, so antidote+ and antidote++ granted 90 seconds of immunity
instead of 9 and 12 minutes. The same flaw silently dropped potions whose
name did not begin with the prefix of their family, such as `super_prayer`
and `antidote+_mix`.

Match on the item id with its dose suffix removed instead, so each family
is selected by exact name and branch order carries no meaning.

The dose handler treated any consumable ending in `_1` to `_4` as a potion,
so cooked crab meat and stealing creation food reported doses remaining.
Detect potions by their `empty` vial instead, which also covers the five
dose stealing creation potions that the old range excluded.

Energy potions assigned a tenth of the player's *current* run energy rather
than adding a tenth of the maximum, so drinking one drained the player.

Also removes the dead `smash_vials` block. It read a key that is never set,
duplicating the vial smashing that `Eating.consume` already handles.
Both families had item definitions and herblore recipes but no effect, so
drinking one only consumed a dose.

Overload grants the boost of all five extreme potions at once, so the two
now share a single `extremeBoost` definition rather than repeating the
amounts in both places where they could drift apart.

Super prayer restores 7 prayer points plus 35% of the prayer level. The
holy wrench and prayer cape bonus follows the two percentage point step
the other prayer restoring potions already use.
Both sets had item definitions but no dose chain and no empty vial, so
they were rejected by the guard in `Eating.consume` and could not be drunk
at all. Give each dose an `excess` and an `empty` vial, which also lets
the dose handler recognise them.

The stealing creation potions are five dose, so the last dose of each
leaves a vial rather than a four dose potion.

Each one reuses the effect of the potion it is a copy of rather than
repeating the amounts, so the two cannot drift apart. The examine text
identifies the tier: the stealing creation ranging, magic, defence,
prayer and energy potions are the ordinary strength, while its attack and
strength potions and all six castle wars potions are super potions.
The poison chalice was consumed and vanished without doing anything. It
now leaves a cocktail glass and applies one of seven random outcomes,
ranging from a slight taste of apricot through stat drains and heals to a
large boost, or a small boost followed by damage. The two healing
outcomes scale with the drinker's maximum life points.

The cadava potion had neither `heals` nor `excess`, so drinking it was
silently ignored. It is not meant to be drinkable, so it now answers with
the refusal it should, and is still not consumed.

Guthix balance and serums 207 and 208 are deliberately left alone. Both
are used on something else rather than drunk, and neither has a drink
effect to implement.
All ten juju potions had item definitions but no effect, so drinking one
only consumed a dose. Each now starts a five minute effect.

The mining and woodcutting potions do not bank every resource. Each ore
or log gathered has a chance to open a short window during which they go
to the bank instead of the inventory, which is what the graphics are for.

The farming potion gives a chance of an extra herb, and only on herb
patches. The fishing potion turns some raw sharks into raw baron sharks.

The hunter potion is not drinkable and now says so rather than wasting a
dose. The cooking potion has no effect yet.

Scentless potion and the three god blessings start their effect but have
nothing to act on, since the jadinkos they apply to do not exist yet.
The juju variables were never declared, so they were written to the
non-persisted map and dropped on save. That made the login handler that
restores them dead code, since the counter it reads was always zero.
Declare them alongside the other potion effect variables.

Persisting them exposed a second problem. Death stops every timer as
though the player had logged out, which skipped the clean up and left a
counter behind with no timer running, so the effect would have come back
on the next login. Clear the effects on death instead.

The banking window ignored whether the bank accepted the item. A player
mining or cutting with a full bank lost every resource for the duration
of the potion, with a success message and full experience, and without
even the floor drop that a full inventory falls back to. Return false
when the bank refuses them so the caller keeps its normal handling.

Also names the two rates for the units they are in. One was a one in
three chance and the other a percentage, which was easy to misread.
The dose handler decided whether an item was a potion by asking whether
its empty container contained "vial". That also matches the dungeoneering
vial and the moonshine vial, and only avoided misreading them because no
such item id happens to end in a digit. Match the two real vials exactly.

The banking window was persisted and restored, so logging out with one
open and a potion that had already expired gave a further seventy five
ticks of free banking on the next login. It is a transient window, so
stop saving it and stop restoring it.

A full bank now closes the window and says so, rather than quietly
falling back to the inventory on every swing with no explanation.

The poison chalice was left without an empty container, unlike the six
other drinks that leave a cocktail glass.
Antifire decremented its remaining time by zero, because the second
argument of `dec` is the amount to subtract rather than a default. The
counter was read unchanged on every tick and never reached zero, so one
dose of antifire lasted until the player died or logged out instead of
six minutes.

Both dragonfire timers shared one stop handler that cleared both
counters, so a super antifire running out also removed a regular antifire
that still had time left. Give each timer its own handler.

That handler also ignored whether it was stopping because the player
logged out, so it wiped the effect on the way out and made persisting it
pointless. Leave the counter alone on logout and clear it on death, which
is what the persistence was for.

Overload had the same death problem. Its counter persists and death stops
timers as though the player had logged out, so the guard skipped the
clean up and the effect came back on the next login.
Gold mineral deposits give up to four ore at once, but the message only
had cases for one, two and three, so a successful four ore haul fell
through to the branch that tells the player they have no inventory space.

The remaining case is still the one that matters, where nothing could be
added at all.
Spiced wine listed its container as "jub", which matches nothing. Its own
destroy text calls it a jug, and jug of wine empties into "jug".

Adds a test that every excess and empty names a real item, since neither
is looked up until a player drinks something, so a typo sits unnoticed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant