How I Hooked a Vanilla Drakeling Without Remapping It

ModsArkASADevLogBlueprints

Where We Were Stuck

The hardest part of this mod was not the XP logic itself.

The real problem was this: we did not have a clean way to know when the important vanilla buff events were happening.

Until that part clicked, the rest of the implementation did not really matter.

What Helped

Vanilla assets checked:

  • ShoulderDragon_Character_BP
  • Buff_ShoulderDragonCheckXP
  • Buff_ShoulderDragonMounted
  • WeapShoulderDragon

Reviewing those was useful to understand the shoulder flow, but it did not immediately give a clean extension point.

Dead Ends First

  • Remap or Drakeling replacement. Rejected early.
  • Working only inside ShoulderDragon_Character_BP. Useful for understanding the lifecycle, but not as the shipping solution.
  • Looking for a clean global event from the player pawn. I investigated it, but no clear hook showed up.
  • Timer polling. It could have worked, but I preferred to avoid it.

What was useful in the vanilla BP was confirming that the shoulder mounted cycle went through:

  • BPOnSetMountedDino
  • BPOnClearMountedDino

The Turning Point

The useful shift was stopping the search on the Drakeling side and moving to the player side instead.

A player buff can listen for other buff activity:

  • BPNotifyOtherBuffActivated
  • BPNotifyOtherBuffDeactivated

That was the bridge.

Once the detector buff was placed on the player and those notifications were enabled, it became possible to detect when the player gained Buff_ShoulderDragonMounted and react there.

Final Architecture

The final implementation uses two custom buffs.

1. Player detector buff

Content/Core/Buffs/Buff_Player_DrakelingDetector

It does this:

  • lives on the player
  • listens for the vanilla mounted Drakeling buff
  • resolves the Drakeling through BuffDamageCauser
  • applies or removes the custom XP buff on the Drakeling

For it to work, these flags have to be enabled:

  • bUseBPNotifyOtherBuffActivated
  • bUseBPNotifyOtherBuffDeactivated

2. Drakeling XP buff

Content/Core/Buffs/Buff_DrakelingTamedXPMultiplier

It does this:

  • lives on the Drakeling
  • listens to BPNotifyExperienceGained
  • filters XP_TAMEDKILL and XP_UNCLAIMEDKILL
  • computes the restored amount from ExpectedExpGain * NonWildKillXPMultiplier
  • handles the XP restore logic on the Drakeling side

Making It Automatic

Once the flow worked manually, the next step was getting rid of the manual part. For that I used:

  • Content/Core/Data/PrimalGameData_BP_DrakelingTamedXPMultiplier
  • Content/Core/Data/ModDataAsset_DrakelingTamedXPMultiplier
  • Content/Core/Singleton_ApplyPlayerDrakelingDetector

What had to be in place:

  • ModDataAsset needed the Additional Default Buffs entry
  • it had to target the player pawn
  • and PrimalGameData had to actually reference that ModDataAsset

The lesson here was pretty clear: if the correct PrimalGameData is not actually in use, the default buff system looks broken even when it is not.

Later I hit the real limitation of that route: Additional Default Buffs is fine for new spawns and respawns, but a survivor that is already alive when the mod comes online will not automatically receive the detector.

Before landing on the final fix, I also investigated a world-buff bootstrap route through AdditionalWorldBuffDefinitions. I did not end up shipping that route because I could not get it working reliably enough. It may still be a valid path, but I did not have the knowledge to close that implementation properly at the time.

That is why the published bootstrap ended up adding a singleton through:

  • ModDataAsset -> ServerExtraWorldSingletonActorClasses

The singleton checks logged-in characters and applies Buff_Player_DrakelingDetector if it is missing, so the mod no longer depends on a death/respawn cycle to become active for existing players.

Config

The config ended up in its own GameUserSettings.ini section:

[DrakelingTamedXPMultiplier]
NonWildKillXPPercent=50

I went with TryGetIntOptionIni and an integer percent because it created a cleaner separation between:

  • missing key
  • 0 as an explicit disable
  • 100 as full restoration

One Extra DevKit Problem

There was also an annoying publishing issue: the uploader gave a generic error, but the real problem was the icon image.

It was not the gameplay logic. It was the icon asset and the uploader constraints.

In this case, the image could not be larger than 1000x1000, and I was trying to upload a 1024x1024 image.