Summoning: complete familiar abilities (specials, passives, dialogue)#1052
Open
HarleyGilpin wants to merge 138 commits into
Open
Summoning: complete familiar abilities (specials, passives, dialogue)#1052HarleyGilpin wants to merge 138 commits into
HarleyGilpin wants to merge 138 commits into
Conversation
Port talk dialogue and special-op interactions for ~62 summoning familiars from a reference implementation, matching the existing per-familiar Script pattern (SpiritWolf/AbyssalLurker/AbyssalParasite). - Per-familiar Interact dialogue scripts (random + context-aware: inventory/equipment/stat/BoB-slot/run-energy/staged-state checks), grouped via wildcard ids for minotaurs, cockatrices, titan tiers, and gorajo/clay dungeoneering familiars. - Special ops: unicorn Cure (gated; inert until poison exists), pyrelord logs->firemaking, lava titan Lava Maze teleport, dreadfowl/compost mound Boost Farming. - Generic expression_familiar_* chathead animation family + unicorn cure anim/gfx; dialogue="familiar" + interacts=false on all scriptable familiars.
The generic expression_familiar_* family applied the shared cat head talk animations to every familiar's chathead model, which animates incorrectly for non-feline familiars. Drop the dialogue="familiar" override and the generic family so each familiar renders its own chathead model with no forced (wrong) animation. Correct per-familiar talk anims can be added later as expression_<familiar>_* families (via ::expr_scan), like the existing wolf/bird/leech sets.
Familiars are creatures; add large_head = true to every familiar npc def so their dialogue renders the bigger chathead (as cows, camels and other creature npcs already do).
Foragers (starting with Magpie) periodically gather loot from a forage_<familiar> drop table into the familiar's inventory while summoned, and the player retrieves it via the familiar's Withdraw/Take option (reusing the beast-of-burden inventory + interface). Players can't deposit their own items into a forager. - Forager script: 30s forage timer rolling the drop table into the familiar inventory, plus Withdraw/Take open handlers. Foragers are detected data-drivenly by the existence of a forage_<id> drop table (no new npc-def param, which must map to a real cache param). - forage_magpie drop table (gems/rings) in summoning.drops.toml. - Magpie marked as a beast-of-burden (cap 30) to reuse the storage + withdraw UI; store() rejects deposits into foragers. - Start/stop the forage timer on summon/dismiss. Other forager familiars are now just data: add a forage_<name> table.
Port the forage drop tables for beaver, spirit spider, granite crab, granite lobster, ibis, macaw (grimy herbs), compost mound, evil turnip, fruit bat, giant ent, stranger plant, void ravager and the cockatrice family, and mark each as a beast of burden (cap 30) so they reuse the forage inventory + Withdraw/Take interaction. Tables model the reference's "1/outOf chance, then drop one" via a 1/<roll> outer roll deferring to an `_items` sub-table for multi-item foragers (also corrects magpie, which previously dropped every tick). Item ids resolved to void string ids. Dungeoneering hoardstalker foragers (tier t1-t10) are deferred.
The farming boost was wrongly offered as a "Boost Farming" entry in a
Talk-to/Boost choice on Interact. Make Interact talk-only again and
trigger the boost from the familiar's special-move ("Special") option
instead, via the cast_dreadfowl_strike / cast_generate_compost buttons
on the summoning orb and familiar details tab.
The boost is on the familiar's "Special" right-click option, not the
special-move interface button. Use npcOperate("Special", ...) guarded
to the player's own familiar.
Clicking the dreadfowl's "Special" option now plays the asking-to-fight dialogue, the dreadfowl's special animation (7810) + gfx (1523, from darkanrs' Dreadfowl Strike sync), then boosts Farming by one.
Player plays gfx 1307 and the dreadfowl plays gfx 1317 alongside its special-strike animation (5387) when boosting Farming via the Special option.
The dreadfowl/compost-mound Special Farming boost now refuses to apply when Farming is already boosted (e.g. by a garden pie), and is stripped when the familiar is dismissed or dies (both route through dismissFamiliar) - only if it's still the active, unchanged boost.
Introduce a data-driven registry of the passive skill-level boosts a summoned familiar grants (beaver +2 Woodcutting, granite lobster +4 Fishing, obsidian golem +7 Mining, etc.) and a Player.familiarBoost function. The boost is read live from the active follower and added to the effective level in skill success checks - so it's invisible (not in the skill tab), never lets the player act above their real level, and disappears the moment the familiar is dismissed or killed. Wired into woodcutting, fishing, mining, firemaking and thieving. Hunter boosts (arctic bear, wolpertinger, kyatt, larupia) are in the registry but inert until the Hunter skill is implemented.
Summoned familiars now periodically shout a random overhead line every ~30s (driven by the existing familiar timer), matching RS. Lines are a data-driven map keyed by familiar id, sourced from the RuneScape Wiki transcript "Overhead dialogue" sections - 83 familiars covered. Phoenix and the gorajo (bloodrager/stormbringer/etc.) tiers have no overhead on the wiki and stay silent.
Replace the hardcoded Kotlin FAMILIAR_OVERHEAD map with a data-driven familiar_overhead table (list<string> "lines" column, keyed by familiar id), matching how the rest of the project stores config (e.g. the pet talk tables). The familiar timer reads it via Rows/Tables, so lines can be edited as data without touching code.
Familiars now play their own "fade into existence" animation (alongside the existing summon graphic) when summoned and when called. The per-familiar spawn anims are ported from darkanrs' Pouch spawnAnim values, mapped by npc id, as named <familiar>_spawn animation defs.
Split the grouped IronSteelTitan stub (a single grunt) into separate IronTitan and SteelTitan scripts with their full random talk dialogue, ported from the RuneScape Wiki transcripts (iron titan: 4 conversations; steel titan: 5).
darkanrs' albino rat spawn anim (16080) is outside void's animation cache. The albino rat reuses the generic rat skeleton (Base 1152) for its render anims; that skeleton's summoning-era animation is 8545 - matching how arctic bear (8522, bear skeleton) and the bats (8279, bat skeleton) take their spawn anim from their skeleton's list. 8545 is in cache range, so all 144 familiars now have a spawn animation.
albino rat spawn anim (16080) is outside 634 cache animation boundry. The albino rat reuses the generic rat skeleton (Base 1152) for its render anims; that skeleton's summoning-era animation is 8545 - matching how arctic bear (8522, bear skeleton) and the bats (8279, bat skeleton) take their spawn anim from their skeleton's list. 8545 is in cache range, so all 144 familiars now have a spawn animation.
…dialogues' into feat/summoning/interactions-and-dialogues
A player and its familiar count as one side, so they may both attack the same target in single-way combat: - Target.attackable exempts the owner<->familiar pair from the single-combat gate. - FamiliarCombat lets the familiar be sent at an NPC outside multi-combat when it's the owner's own target (offensive/defensive assist). Combat familiars now also grant their owner combat xp for the damage they deal - the owner's chosen melee style skill (or the familiar's range/magic type) plus Constitution - mirroring CombatExperience.
Familiars wouldn't approach a target they were sent at - they stood frozen and only attacked if it was already in range. Two causes: - Movement only ran pathfinding for players; NPCs used cheap single-step line movement that can't route around obstacles. Owned familiars now pathfind like players so they actually walk to the target. - CombatMovement applied the spawn/aggro leash to familiars, so a target beyond their ~9-tile aggro range (e.g. one commanded up to 16 tiles away) dropped them straight to EmptyMode. Owned familiars are now exempt from the leash - they chase what their owner directs and fall back to following when the fight ends.
Locks in the Target.attackable exemption: in a single-way zone (one attacker per target) the owner and its familiar count as one side, so the familiar may still join the owner's fight.
Reverts the single-way assist allowance: combat familiars can only attack in multi-combat zones. In single-way combat the player can still use the familiar (storage, foraging, special moves) but it won't assist in the fight. Removes the owner/familiar single-combat exemption from Target.attackable and the single-way assist path in FamiliarCombat.
Player and familiar are separate attackers under the single-way "one attacker per target" rule: - commandFamiliarAttack: drop the multi-combat-only gate so a familiar can be ordered to attack in single-way, but pre-check Target.attackable so it can't pile onto an NPC already under attack (e.g. one the owner is fighting), rejecting with a message instead. - assistFamiliar: auto-join only in multi-combat; in single-way the owner's target is theirs alone, so the familiar must be ordered at a separate NPC. Tests cover: ordered solo attack in single-way, no sharing from the player's side, no auto-assist in single-way, auto-assist in multi.
Covers the approach path (interact -> combat) for a target beyond the familiar's approach range, the scenario that wasn't exercised by the adjacent-target ordered-attack test.
When ordered/assisting, the familiar walked to its target via the interact-then-combat path. The interact mode gives up the instant a player or npc fully blocks the route (cantReach -> EmptyMode) and never re-paths once the obstruction clears, leaving the familiar frozen. Drive the familiar with CombatMovement directly instead: it re-paths every tick and (for npcs) never bails on cantReach, so the familiar routes around or waits out blockers and resumes as soon as they move.
NPCCharacterTargetStrategy.destination() returns Tile.EMPTY for npcs with an EMPTY walk mode. Familiars have that walk mode (they don't wander), so destination() always returned EMPTY and recalculate() short-circuited (EMPTY == cleared destination) - the familiar never re-pathed, following only its initial path. A stationary target worked; a target that moved/retreated left the familiar stuck at the old tile. Exempt owned familiars (owner_index set) from the stationary-npc short-circuit so they recompute a path to the target's current tile each tick and chase it.
Familiars persist in CombatMovement and never call cantReach, so an unreachable target (fled somewhere with no path) left them stuck chasing forever. Track ticks of no progress while still far from the target; if the familiar makes none for a short grace period it gives up and reverts to EmptyMode, which NPCTask turns back into following its owner. Moving, or being all but in range (just waiting on a free attack tile in a crowd), resets the grace period so genuine fights aren't abandoned.
When a familiar dealt the killing blow it was the npc's killer, but the death handler only drops loot (and grants slayer / logs the kill) when the killer is a Player, so familiar kills dropped nothing. Resolve a familiar killer to its owner so the loot drops for the player.
The heal was a max-capped restore (darkan's 10%) with a full-health refusal; per the live game it boosts life points by a flat 80 that can carry the owner above their maximum, alongside the 12.5% Defence boost, shared by the fire, ice and moss titans. levels.boost on Constitution does exactly this, capping at max+260 like other constitution boosts.
Darkan's version is damage-only, but per the wiki the spirit dagannoth's spike 'attacks the target, stunning them' - the stun now lands with the projectile (5 ticks, like Bull Rush) rather than the hit alone.
Iron Within (and every other npc-target special) only worked by clicking the button and then clicking a target - a plain click silently armed targeting mode, reading as broken. As in darkan and the live game, a plain click now auto-fires the special at the familiar's current combat target, falling back to the owner's, with a message when neither is fighting; clicking a target directly still works through the approach hooks.
The 1449 graphic showed on both the titan and its target; it belongs on the target tile only - the titan keeps just its attack animation.
The yak used darkan's 853/851 - another wrong-model set from its familiar combatdefs. 2009scape's npc config gives the pack yak's own cluster: attack 5782, defend 5783.
…ead code The per-entry 'X's Y animation' comments in summoning_special.anims/gfx said nothing the entry names don't; only the load-bearing notes remain (suffix conventions, the projectile speed standard, Bull Rush's constant-flight exception, the Toad Bark arc/cache-rotation note, and Herbcall's tick maths). Dead code: dropForage (Fruitfall and Fish Rain place their drops per tile themselves now) and familiarBoostSkill (never called - the boost specials use their own helper), plus unused imports and commandFamiliarAttack's stale PvP-area-only doc.
…ractions-and-dialogues # Conflicts: # game/src/main/kotlin/content/entity/combat/Target.kt
Pikkupstix gains his post-Wolf-Whistle Talk-to dialogue (2009scape transcript: the summoning overview, supplies shop, skillcape purchase) and an Enchant option. Enchanted headgear (from darkan's EnchantedHeadwear) stores combat scrolls: use combat scrolls on a hunter helm to charge it (antlers 40, lizard skull 65, feather headdress 150, one scroll type at a time), empty it back on a summoning obelisk, and a worn charged helm feeds a scroll to a special move when the pack has none. Only combat scrolls store (checked via the scroll -> pouch -> familiar -> npcTarget lookup); non-combat scrolls like Healing Aura are refused. For void's hunter helms the plain form is already the enchanted one (darkan's base==enchanted), so charging is the only visible state change.
…e options Extends enchanted headgear from the three hunter helms to darkan's full set - snakeskin bandana, archer/berserker/warrior/farseer/rune/splitbark helms, helm of Neitiznot, dragon med helm, lunar helm and armadyl helmet - each with its Summoning level and capacity. Unlike the hunter helms (whose plain form is already scroll-capable) these have the real three -state flow: Pikkupstix enchants the plain helm into its empty (e) form, scrolls charge it, and it disenchants back. The (e) middle-state items already exist in the cache; added their item defs. The charged helms already carry Commune and Uncharge in their cache option arrays but had no handlers, so the options did nothing. Commune now reports the stored scrolls and Uncharge empties them back to the pack (reverting to the enchanted form) - replacing the obelisk-empty stopgap.
The count on the follower-details cast button (662,75) and the item is rendered client-side from the item's charge value, so the helm's stored scrolls have to live on the item, not just a server variable. The charged helms now carry a charges/charges_max cap, and the stored count is mirrored onto the worn or held helm's charge whenever it changes - so the number shows on the helm and feeds the cast-button count. Reverts reset the charge to 1 first so an emptied helm is a clean single item, not one carrying the old count.
The count on the follower-details cast button (662,75) and the item is rendered client-side from the item's charge value, so the helm's stored scrolls have to live on the item, not just a server variable. The charged helms now carry a charges/charges_max cap, and the stored count is mirrored onto the worn or held helm's charge whenever it changes - so the number shows on the helm and feeds the cast-button count. Reverts reset the charge to 1 first so an emptied helm is a clean single item, not one carrying the old count.
The albino_rat_spawn entry was absent, so the familiar appeared with no animation on summon/call. 2009scape spawns familiars with a graphic and a player gesture rather than a per-npc animation, so there was no id to lift directly; identified 14859 from void's cache as the albino rat's summon animation - a 23-frame materialise matching the spirit wolf's summon (8298) profile exactly, from the rat's 14856-14863 cluster.
No valid rev-634 spawn animation exists for the albino rat: darkan's value (16080) is a post-634 re-animation, absent from this cache (max animation id 15260), and 2009scape plays no per-familiar spawn anim at all. Better to play nothing than a wrong animation.
Entry no longer zeroes Prayer with a parasite summoned; instead 10 points leak every 8-16 ticks while inside. Losing the parasite in the Abyss drains the remainder, so dismissing it grants no free prayer.
Previously only re-targeted the overlord like a plain attack order. Now fires an immediate magic-based hit (max 76) with its attack visuals, charging two summoning points only when the cast succeeds.
Previously only re-targeted the smoke devil like a plain attack order. Now fires an immediate magic-based hit (max 150) with its attack anim and a dedicated flame projectile (gfx 1473), charging two summoning points only when the cast succeeds.
Familiars had large_head = true across the board, routing every dialogue through the large head component; the engine default of false matches the client. Also drops the per-familiar dialogue expression keys in favour of the cs2-driven chathead lookup.
Dialogue chatheads previously used the humanoid expression animations (or hand-maintained per-familiar toml sets, some wrong - the spirit wolf had the cat's). Familiars now resolve their animation at runtime exactly as interface 662's client script does: the follower_details_chathead_animation varbit value keys enum 1276, or enum 1275 with 50 subtracted when over 50. Also strips the _familiar suffix before setting the varbit so it transmits the mapped index instead of -1 (the map keys are base names), keeping the details interface in sync server-side.
Familiars with chathead value 0 (arctic bear, albino rat, minotaurs) or an out-of-range second-bank key (unicorn stallion) have no entry in enums 1276/1275, so the lookup returned the enums' default - the cat's animation - and playing it on a mismatched head model crashed the client. Missing entries now fall back to the standard expression animations like any unmapped npc.
writeString sends each char as a single byte, so smart quotes (U+2018/9) and ellipses (U+2026) truncate to control bytes the client's font handling can't render - interacting with the arctic bear, talon beast or swamp titan crashed the game. Replace them with plain ASCII in all client-bound strings.
writeString truncates characters over 0xFF to a control byte the client can't render, so smart quotes or ellipses in dialogue crash the game. Fails with file:line of any offending string, ignoring comments and interface option matchers.
The varbit map's zeros are unverified gaps from the original capture, so familiars like the desert wyrm and arctic bear resolved no enum entry and bobbed with humanoid expression animations on non-human head models. Give them a static head (-1) instead, matching what the details interface shows, until their real values are identified.
The details interface animates value-0 familiars (desert wyrm, arctic bear) correctly because the client's datamap lookup returns the enum default - a generic head-bob - for missing keys. Match that instead of sending a static head; the earlier crash was the typographic punctuation, not this animation.
talkToPet keyed the chathead enum by npc id - the wrong key space, the enums key on the varbit value - and passed the resulting animation name as an expression, which double-prefixed the lookup and rendered a static head. Mapped pets (sneakerpeeper) now resolve through familiarChatheadAnimation like familiars; unmapped pets fall back to the neutral expression.
Summoning's current level doubles as the points pool, so gates using Level.has - summoning a pouch familiar, dropping a pet, incubating an egg, enchanting headgear - wrongly refused players whose points had drained below the requirement. Switch them to hasMax, which reads the level derived from experience. Boostable-skill gates (bunyip's Cooking, pyrelord's Firemaking) are left on the current level.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fleshes out summoning familiars: talk dialogue, special moves for every familiar, foraging, skill boosts, passive abilities and overhead chat.
Talk dialogue & chatheads
<id>_combatform - transformed npcs fall back to their base id's interaction handlers.Special moves
Every familiar's cast-button special is implemented - combat hits, stat drains, stuns/binds, AoEs, boosts, heals, teleports, foraging drops and banking. Highlights:
Passive & other abilities
Player.familiarBoost(skill)now supports level-scaled formulas (stranger plant Farming +1+4%, geyser Ranged +1+3%) alongside flat boosts; the graahk's missing Hunter +5 restored.Target.attackable), not a Clan-Wars-only flag.Foraging
Engine fixes surfaced by this work
GraphicEncoder+FixGraphicstool, wired intoCacheBuilder) rotates the backwards-facing Toad Bark cannonball 180 degrees - the protocol itself has no projectile rotation.Overhead dialogue
Manual Testing
Tracked per-familiar in the QA sheet (special move, boosts, other abilities, in-game visuals):
https://docs.google.com/spreadsheets/d/14LjVE4VdZ8FEGrl42_MJjrY3iAfAV_6CJ1ZuV31r-eQ/edit?usp=sharing
In-game verification during development caught and fixed: silent cast buttons, backwards projectiles, wrong attack animations, PvP-area refusals, non-stacking runes, forager output, and follow/step-out movement quirks.
Unit Tests
game/src/test/kotlin/content/skill/summoning- all passing, including:FamiliarSpecialMoveTest- the scroll/points cast gate (spend-on-success, refusals)FamiliarSpecialEffectTest,FamiliarCombatSpecialEffectTest,FamiliarUtilitySpecialEffectTest- every special's effect through the registered blocks and, where the surface matters, the real dispatchers (orb cast clicks, item-on-npc, cast-on-item, dialogue options):engine:testand:game:testsuites greenReferences
Special Moves (Familiars lvl 1-40): https://www.youtube.com/watch?v=kduBCqBiLB0
Summoning Scrolls Knowledge Base Article: https://2011.rs/kb/summoning_scrolls
darkan (primary mechanics reference): local checkout,
Scroll.java/Pouch.java2009scape (animation/config reference): local checkout,
npc_configs.json