Skip to content

Harbinger (APMF) v0.9.5 -- Tell a follower where to go and what to wear

Choose a tag to compare

@marthofdoom marthofdoom released this 22 Sep 20:11
· 144 commits to main since this release

Beta. Skyrim AE 1.6.1170 or SE 1.5.97. Pairs with MFO v2.0.8.

Requirements

  • Skyrim on Windows: Anniversary Edition 1.6.1170 or Special Edition 1.5.97. Any other build is refused by name, including a 1.6.x that is not 1170. VR is refused. 1.7.104 is not supported, there is no address library for it.
  • SKSE64 for that runtime.
  • Address Library for SKSE Plugins (the all in one file). Harbinger resolves engine addresses through it. Without it the plugin stops at load with an error box rather than running.

Install: three files now, not two

The archive root is your Data folder.

  • SKSE/Plugins/APMF.dll
  • SKSE/Plugins/APMF.ini
  • APMF.esl (new, and it has to be enabled)

APMF.esl is the first plugin Harbinger has ever shipped. It carries the eight travel packages the new travel facet hands to the game. It is light flagged so it takes no load order slot, it has one master, it overrides nothing and it is about 2.5 KB. If it is missing or disabled, travel refuses every request and says so once in the log. Nothing else in Harbinger depends on it.

Tell a follower where to go and what to wear

  • A mod can now tell Harbinger "walk this follower over there" and Harbinger does it. Move to a place is a staple, and before this there was no way to ask for it. A mod could claim the combat target facet, but that only records who owns it, and it could offer a package, but only one it shipped in its own plugin. A mod author who wanted to send followers at an enemy ended up calling the game's start-combat function on a timer, which makes a follower search and pace instead of charge, because the game will not path anyone to an enemy they have never seen.
  • What travel does, in full. The follower walks to the destination on real pathing, and Harbinger lets go the moment that follower is in combat. The destination is any object in the world, an enemy, a chest, a marker, anything, and the follower stops about 75 units short of it. A mod can ask for a different distance, between 50 and 512 units.
  • A destination can also be a whole cell. Name a cell instead of an object and the follower walks until they are in it, which is the natural way to say "go inside" or "get to that room". Distance does not apply to a cell, so the stopping distance is ignored for one.
  • The destination does not have to be nearby. A follower will path across cells to something they cannot see yet, the way the game's own travel packages do. Harbinger gives up after two minutes of getting nowhere and says so in the log rather than holding on silently.
  • A destination cannot be a bare set of coordinates, and that is the game's limit rather than a choice. An AI package's destination holds an object or a cell and no coordinates at all. The game's own answer, in every quest Bethesda shipped, is to place an invisible marker and point at the marker. Do the same. A mod that passes coordinates is told no, in the log, instead of having them quietly ignored.
  • What travel does not do, and this is the point. It does not claim the target, does not start a fight, does not hold anyone's attention on anything, and does not pretend anybody has seen anything. A mod that wants the combat target arbitrated asks for that separately, in its own request. One thing per request keeps them from fighting each other. Single calls that bundle several of these together are a good idea and will come later. They are not being ruled out.
  • Harbinger ships a plugin for the first time, and it has to be enabled. APMF.esl carries eight travel packages, one per follower walking somewhere at once. A package's destination lives on the package itself, so two followers walking to two different places need two of them. A ninth at the same time is refused and said so in the log rather than quietly sharing one and sending both to the same place. It is a light plugin, so it takes no load order slot, it has one master, it overrides nothing and it is about 2.5 KB. Without it this one feature is refused and says so. Nothing else in Harbinger depends on it.
  • A mod can now declare a follower's worn set and have it hold. The mod sends the list of items, with a hand for each one if it cares which. Harbinger equips them and refuses the equips the game tries against them: the outfit refresh, the AI's own weapon and armor picks, the re-equip after an item is removed. An item declared for the left hand that the follower holds in the right is moved, and the same weapon can be declared once per hand, so a follower can dual wield through Harbinger. It holds until the mod declares a new set or releases. A follower with no declaration is never touched, and the player is never touched.
  • A mod says which parts of the loadout it owns. Armor, shield, right hand, left hand, ammo, light. Harbinger holds the declared set in those categories and leaves the rest to the game, so a mod that only cares about the hands no longer has to declare the body armor and the arrows to stop the game's own refresh being refused for them. That was the first field run's one real failure. A category can also be denied outright: a follower who must never raise a shield gets the shield category denied, and nothing can equip one there.
  • One rule decides what an item competes for. A shield takes the shield and the left hand. A torch takes light and the left hand. A two hander, a bow or a crossbow takes both hands. A one hander takes the hand the game resolved it for. When the game has not picked a hand yet, it takes both. The same rule runs in the equip check and in the equip pass, so the two cannot disagree.
  • Potions, food, scrolls, ingredients and books are never refused. The equip check only governs armor, weapons, ammo and torches. A follower drinking a potion, or a mod telling one to, passes through untouched.
  • The player can still dress a follower by hand. An equip from the trade or gift menu passes by default, though the mod's next declaration puts back whatever it displaced unless the mod folds the player's choice into that declaration. A Papyrus or console equip passes too, and both checks run before the scope is consulted. A mod that wants the strict form can ask for the player's equips to be refused as well. Unequips are never refused in this version.
  • Bound weapons are equips. A follower's Bound Sword equips through the same check, so a mod that declares a worn set has to include the bound weapon or the follower will cast and hold nothing once enforcement is on.
  • A claim is refused when the equip hook is not installed. With the feature switched off in the ini, on VR, on an unsupported game version, or after a hook conflict, a mod asking for equip authority gets a refusal instead of a claim that does nothing. A mod that stops its own equips on a successful claim would otherwise have equipped nothing all session. The log says why once. One new call answers whether the check is enforcing or only watching.
  • This is the first call-site hook in Harbinger. Every engine equip goes through one internal function that has no virtual entry to hook, so the two places the game calls it are patched instead. Both are checked byte for byte before either is written. If any other mod has touched them the whole feature refuses to install and says so in the log. A conflict is a missing feature, never a crash.
  • The equip log names who is equipping. The first observe run on the Deck left two lines reading Unknown(39637), which turned out to be the AI command dispatcher equipping directly rather than through the two helpers the log already named. Every remaining caller of the equip worker was then checked against both unpacked game images and named: dropping an item, picking one up, a conjured bound weapon equipping itself, the eat package, the re-equip after ammo or pickup, and combat start. Two stay unnamed on purpose, one whose second route could not be pinned and one that nothing in the game references. Each equip check line also names the categories the item competes for, whether the mod owns any of them, whether any is denied, and the slot the game resolved.
  • The cast classify seat and the weapon score seat now run on Skyrim 1.5.97. Both used to refuse anything but 1.6.1170. Every value they read was checked against the 1.5.97 binary and its address library before being placed, and the weapon score seat resolves its four vtables through the address library on both runtimes instead of carrying hard 1.6.1170 addresses. Nothing was guessed.
  • The version gates are exact. Each seat opens on 1.6.1170 and 1.5.97 and refuses any other build by name. A 1.6 build other than 1170 is refused where it used to be admitted unverified. Skyrim 1.7.104 is still unsupported: there is no address library for it, so nothing was placed, and the game does not get that far anyway.
  • One line at startup states what this build placed, before any seat installs. [runtime] <version>: cast-classify open, group-C open, equip-sink open reads the same way for all three, and gated means the version predicate refused that seat. The [apmf][equip-sink] INSTALLED line is still the truth about whether that seat is live.
  • The log now names the Harbinger release a newer mod needs. When a mod asks for an API newer than the installed Harbinger, the refusal line states the installed version and the first release that would satisfy the mod, instead of only the two API numbers.
  • New API revision (v10). Four revisions ship together here: v7 SetEquipSet, v8 SetEquipSetEx and IsEquipAuthorityEnforced, v9 SetEquipScope, and v10 the travel intent. v10 adds no call and no struct change, the new intent rides the request path that was already there. A mod built against any older revision is unaffected.
  • Travel and equip authority both ship in observe mode. bTravelObserveOnly=1 and bEquipObserveOnly=1 in APMF.ini log what they would do and change nothing. Set either to 0 to enforce.
  • Not field-run. The travel facet, the equip authority and the 1.5.97 placement are all CI verified only.
  • No change to any existing facet.

Build provenance

  • commit 46e53d4 · CI run 35770164849
  • APMF.dll sha256 2c1af92b88e4814c3c6bdd54e1468e6296f2bbc4c09f106d729c468fd417c1b2
  • APMF.esl sha256 78a3e6a44e5010eb6a1f0471afcb965d553e009cd5cfb532e4e294c483f7be62