Skip to content

Summoning: familiar combat foundation, interactions, dialogue, foraging and boosts#1052

Draft
HarleyGilpin wants to merge 34 commits into
GregHib:mainfrom
HarleyGilpin:feat/summoning/interactions-and-dialogues
Draft

Summoning: familiar combat foundation, interactions, dialogue, foraging and boosts#1052
HarleyGilpin wants to merge 34 commits into
GregHib:mainfrom
HarleyGilpin:feat/summoning/interactions-and-dialogues

Conversation

@HarleyGilpin

Copy link
Copy Markdown
Contributor

Summary

Fleshes out summoning familiars: talk dialogue, special moves, foraging, skill boosts and overhead chat.

Talk dialogue & chatheads

  • Per-familiar Interact talk dialogue for ~60 familiars (random + context-aware lines).
  • Familiars use a large, static correct-model chathead.

Special-move (Special) interactions

  • Unicorn Stallion Cure (gated; inert until poison exists), Pyrelord logs→fire, Lava Titan Teleport to Lava Maze.
  • Dreadfowl Farming boost. Doesn't stack with other Farming boosts; removed on dismiss/death.

Foraging

  • Generic forager system: gathers from a forage_ drop table into the familiar inventory every ~30s, retrieved via Withdraw/Take (reuses the beast-of-burden UI). Covers 20 overworld foragers.

Passive skill boosts

  • Data-driven Player.familiarBoost(skill) — invisible effective-level boost (beaver +2 Woodcutting, obsidian golem +7 Mining, magpie +3 Thieving…). Wired into woodcutting/fishing/mining/firemaking/thieving; never exceeds real level; gone on dismiss/death. Hunter boosts registered but inert.

Overhead dialogue

  • Familiars shout a random overhead line ~every 30s; 83 familiars' lines sourced from the RS Wiki, stored as a data-driven familiar_overhead table.

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.
A plain npc killer has owner_index -1, and Players.indexed(-1) throws
ArrayIndexOutOfBoundsException - so every npc-on-npc kill crashed the
tick. Only resolve an owner when owner_index is set.
Combat data and wiring the familiar combat behaviour builds on:
- summoning.combat.toml / summoning_combat.anims.toml / .gfx.toml: per-
  familiar combat definitions (stats, attack styles, anims, projectiles).
- summoning.npcs.toml: combat stats and combat_def links per familiar.
- gameframe.ifaces.toml: summoning orb / follower-details "Attack" option.
- Summoning.kt: apply the wilderness pvp combat form on summon and call.
- NPCTask.kt: an idle familiar resumes following its owner instead of
  wandering.
- Combat.kt: retaliation anchors its leash via leashAnchor() (owner tile
  for familiars).
- Attack.kt: resolve an npc's swing definition through combat_def.
In multi-combat the familiar engaged the owner's target on combatStart
and (correctly) never switched mid-fight, but once its own target died
nothing re-engaged it if the owner had since moved to another enemy - it
just stood idle. Assist on the owner's combatAttack too: each hit an
idle familiar joins the owner's current (last-attacked) target, while
the idle guard still leaves an actively fighting familiar alone.
@HarleyGilpin HarleyGilpin changed the title Summoning: familiar interactions, dialogue, foraging and boosts Summoning: familiar combat foundation, interactions, dialogue, foraging and boosts Jun 30, 2026
With world.players.collision on, a player stamps BLOCK_PLAYERS on its
tile and an npc's blockMove (BLOCK_PLAYERS|BLOCK_NPCS) is the canTravel
extraFlag, so a familiar couldn't step onto any player tile - standing
between it and its target blocked it. Drop BLOCK_PLAYERS from blockMove
for owned followers (familiars/pets) so they phase through players
including their owner, while keeping BLOCK_NPCS; solid npcs already
stamp FLOOR so the pathfinder still routes the familiar around them.
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