Relationships

What it is

This API reads the player’s relationship scores and tells you when one changes.

Reference

Scores run from -100 to 100.

Relationships scores and change event

Use GetDisposition or All() to read the scores, SetDisposition to set one, Has to check whether the player has one, and RelationshipChanged to hear when a score changes.

public static class Relationships
{
    public static List<RelationshipValue> All();
    public static float GetDisposition(string id);
    public static bool SetDisposition(string id, float value);
    public static bool Has(string id);
    public static bool IsAvailable { get; }
    public static event Action<RelationshipChange> RelationshipChanged;
}
Method Returns What it does
All() List<RelationshipValue> Every relationship the framework tracks.
GetDisposition(id) float The score, or 0 if the player does not have it.
SetDisposition(id, value) bool Sets the score and its target, so the score does not drift back. The score runs from -100 to 100. It is false when the id is unknown, when a save or load is running, when the value is not a finite number, when the value is outside -100 to 100 (the value is refused, not clamped), or when no relationship is loaded. It raises RelationshipChanged.
Has(id) bool It is true when the player has it.
Property Type What it means
IsAvailable bool It is false when the change event is off.
Event Gives you When it fires
RelationshipChanged RelationshipChange It fires when the game’s daily check or SetDisposition changes a score.

RelationshipValue one relationship

Relationships.All() gives you one of these for each relationship the framework tracks. Disposition is the score now, and Target is the score it moves toward.

public sealed class RelationshipValue
{
    public string Id { get; }
    public string Name { get; }
    public float Disposition { get; }
    public float Target { get; }
    public bool Enabled { get; }
}
Property Type What it means
Id string The relationship’s internal name in the game files.
Name string The name the game shows the player.
Disposition float The current score.
Target float The score it moves toward.
Enabled bool It is true when the player has it.

RelationshipChange a score change

Relationships.RelationshipChanged hands you one of these when the game’s daily check changes a score. Previous and Current are the scores before and after.

public sealed class RelationshipChange
{
    public string Id { get; }
    public string Name { get; }
    public float Previous { get; }
    public float Current { get; }
}
Property Type What it means
Id string The relationship’s internal name in the game files.
Name string The name the game shows the player.
Previous float The score before the change.
Current float The score after the change.

Putting it together

Logs any relationship that drops below zero.

using ObenseuerCore;

[ModInfo("example.moodwatch", "Mood Watch", "0.1.0")]
[ModDependency("obenseuer.moddingframework", "0.11.0")]
public sealed class MoodWatch : FrameworkMod
{
    protected override void OnLoad()
    {
        Relationships.RelationshipChanged += change =>
        {
            if (change.Previous >= 0 && change.Current < 0)
                Log.Info(change.Name + " dropped below zero, from " + change.Previous + " to " + change.Current);
        };
        Events.WhenReady(() =>
        {
            if (!Relationships.IsAvailable) Log.Info("Relationship change events are off, so no changes will be reported.");
            foreach (RelationshipValue relationship in Relationships.All())
            {
                if (!relationship.Enabled) continue;
                Log.Info(relationship.Name + ": " + relationship.Disposition + ", moving toward " + relationship.Target);
            }
        });
    }
}
Step What it does
Relationships.IsAvailable false means no changes are reported.
relationship.Enabled Skips relationships the player lacks.

Things to know

  • RelationshipChanged does not fire on add, remove or save load.

Limits

  • Scores are per player, not per NPC.
  • You can’t set scores or targets, because the framework has nothing safe to call.

Details

Target: Obenseuer 0.5.08 (buildid 25969064), Unity 6000.0.77f1 Mono, BepInEx 6 Unity.Mono (be.788, the tested build) or BepInEx 5 (5.4.23 or later). Logs go to BepInEx\logs\obenseuer.moddingframework.npcs.log.

Relationships use feature group obenseuer.moddingframework.npcs.relationship.

When you can call it

  • Call from the main thread, not a thread you started.
  • It is safe to call at any time. Empty reads return 0, false, null or an empty list.
  • If a handler throws, the error is logged and the other handlers still run.
  • Don’t change scenes or NPCs from a handler.

Under the hood

How the framework reads the game, for modders who want to dig deeper.

  • Relationship.CheckDateAndTrigger raises RelationshipChanged, only when the score changed.
  • Scores live on Relationship assets. No game method sets RelationshipController.RelationshipInfo.disposition or dispositionTarget.