Skip to content

Dragonfire shield charging, discharge and creation - #1186

Merged
GregHib merged 5 commits into
GregHib:mainfrom
HarleyGilpin:feat/DFS-Special
Aug 19, 2026
Merged

Dragonfire shield charging, discharge and creation#1186
GregHib merged 5 commits into
GregHib:mainfrom
HarleyGilpin:feat/DFS-Special

Conversation

@HarleyGilpin

@HarleyGilpin HarleyGilpin commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Implements the dragonfire shield, which had item definitions but no behaviour.

Referenced against the 2011 wiki revision

Shield

  • Charging - every dragonfire and skeletal wyvern icy breath the shield absorbs adds a charge, up to 50. An uncharged shield becomes charged on the first one. Each charge adds one to its melee, ranged and summoning defences.
  • Activate - the worn option spends a charge to breathe dragonfire back at the current target for up to 290, on a two minute cooldown, at up to twelve squares and over obstacles, granting Magic, Defence and Constitution experience. Dragons are immune.
  • Inspect reports the charge count, Empty releases the lot and reverts to the tradeable uncharged shield.
  • Charges are lost with the shield on death, worked out after items kept on death so a protected shield stays charged.

Creation

  • Smith a draconic visage onto an anti-dragon shield at an anvil, needing 90 Smithing and a hammer, for 2000 Smithing experience.
  • Or pay Oziach 1,250,000 coins to forge it. He had no dialogue at all before this, only the shop his definition points at.

Both produce the uncharged shield.

Supporting fixes

Each of these was reached, or made reachable, by the shield.

  • Item.amount counted charges as a quantity. The guard only recognised item-value charges when the definition's starting charge count was above one, which it never is for something that starts empty. A twenty charge shield therefore looked like twenty items: items kept on death protected three of them and the rest dropped as a stack. Capacity now decides, which also covers satchels, tea flasks and enchanted headgear.
  • Dragonfire reduction in Equipment read the wrong entity. It sat in a when (source) and used the attacker's shield and antifire, messaging the attacker. Unreachable while only npcs breathed dragonfire, but firing the shield woke it up and cut the wielder's own damage by 80% while telling them their shield had absorbed it. Dragonfire.maxHit already applies the victim's shield, potion and prayer, so the duplicate is gone and its shield message moved there, where it reaches the victim.
  • Fire resistant shields did nothing against icy breath, from the same wrong-entity bug, and fireResistantShield compared against dragonfire_shield, an alias rather than an equipped id. The cap also replaced the damage rather than limiting it, so a small hit was raised to it. SkeletalWyvern now shares the one shield check instead of keeping its own copy.
  • Player dragonfire granted Constitution experience alone, now Magic and Defence too.
  • Dragonfire.maxHit returned -1 for an immune dragon, which would have thrown out of random.nextInt(0, 0) in Damage.roll as soon as a player could deal dragonfire.

Data

Shield definitions moved out of ancient_cavern.* into data/entity/player/equipment/dragonfire_shield.*, since it is not ancient cavern content.

Testing

20 tests across DragonfireShieldTest, OziachTest and IcyBreathTest covering charging and the cap, the defence bonus, activate with its cooldown, range and dragon immunity, inspect and empty, death conversion, both creation routes and their requirements, and the icy breath cap. Full suite green.

One test changed: EnchantedHeadgearTest asserted a helm's stored scroll count through Item.amount, now read through charges().

Comment thread game/src/main/kotlin/content/entity/player/effect/Dragonfire.kt Outdated
The shield gains a charge from every dragonfire and wyvern icy breath it
absorbs, up to 50, each adding one to its melee, ranged and summoning
defences. Activating the worn shield spends a charge to breathe dragonfire
back at the current target for up to 290, on a two minute cooldown and at up
to twelve squares, over obstacles. Inspect reports the charges and Empty
releases them, reverting to the tradeable uncharged shield.

Creating one is now possible either by smithing a draconic visage onto an
anti-dragon shield at an anvil, needing 90 Smithing and a hammer for 2000
experience, or by paying Oziach 1,250,000 coins to forge it. Oziach had no
dialogue at all before, only the shop his definition points at.

Charges are lost with the shield on death, after items kept on death are
worked out so a protected shield stays charged.

Supporting fixes:

- Item.amount treated the charges held in an item's value as a quantity
  unless the definition's starting charge count was above one, which it never
  is for an item that starts empty. A twenty charge shield counted as twenty
  items, so items kept on death protected three of them and the rest dropped
  as a stack. Capacity now decides, which also covers satchels, tea flasks
  and enchanted headgear.
- Dragonfire damage reduction in Equipment read the attacker's shield and
  antifire rather than the victim's, and messaged the attacker. It was
  unreachable while only npcs breathed dragonfire, but firing the shield
  woke it up and cut the wielder's own damage by 80%. Dragonfire.maxHit
  already applies the victim's shield, potion and prayer, so the duplicate
  is gone and its shield message moved there where it reaches the victim.
- The same wrong-entity bug left a fire resistant shield doing nothing
  against a wyvern's icy breath, and fireResistantShield compared against
  "dragonfire_shield" which is an alias rather than an equipped id. The cap
  also replaced the damage instead of limiting it, so a small hit was raised
  to it. SkeletalWyvern now shares the same shield check.
- Dragonfire dealt by a player granted Constitution experience alone; it now
  grants Magic and Defence as well.
- Dragonfire.maxHit returned -1 for a dragon immune to it, which would have
  thrown out of random.nextInt once a player could deal dragonfire.
A shielded player was told their shield absorbed most of the breath and that
they were horribly burnt by it, which contradict each other. Both 2009scape
and Darkan pick a single message by whichever protection did the most, so
report the shield alone when one is worn.
Void reported the same two messages whatever the target was wearing or
drinking, so an antifire potion or protect from magic went unmentioned and a
fully immune player was still told they were horribly burnt.

Darkan is revision 727 against 2009scape's 530, so its wording is the closer
match for 634. Its ladder is adopted whole, reporting only the strongest
protection: super antifire, shield and potion together, shield, potion,
prayer, then the unprotected hit.
The fire left the shield before the animation had played enough of its wind
up, so hold it back another sixty client ticks.
Ninety client ticks lands the fire just before the ninety five tick activate
animation finishes, rather than after the player has already dropped back to
idle.
@HarleyGilpin
HarleyGilpin marked this pull request as ready for review August 18, 2026 22:31
@GregHib
GregHib merged commit bcadb4f into GregHib:main Aug 19, 2026
2 checks passed
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.

2 participants