Home

Interactions, Policies & Edicts

This page covers all the "action" content types -- things the player or AI can do. There are four types of interaction, plus policies and edicts.

TypeBase ClassTargetDescriptionDiplomacy InteractionUDiplomacyInteractionAnother factionDiplomatic actions between factions.Spy InteractionUSpyInteractionAnother factionEspionage actions using the spy network.Person InteractionUPersonInteractionA characterActions between two characters.Power Bloc InteractionUPowerBlocInteractionA power blocActions targeting internal political factions.PolicyUPolicyOwn factionAdjustable slider affecting faction-wide modifiers.EdictUEdictOwn factionOne-time faction action (always succeeds).

Shared Concepts#

Before diving into each type, here are concepts shared across all interactions.

Cost, Duration, and Difficulty

All faction-level and person interactions share these base properties:

PropertyTypeDescriptionCostint32Gold cost deducted when the interaction starts.Durationint32Days until the interaction completes. 0 = instant.DifficultyenumAffects base success chance. See difficulty enums below.

Success Chance

Success chance is built from an array of named factors. Each factor contributes a value that is summed and clamped to determine the final probability.

Override BlueprintSuccessChance() to provide your factors:

angelscript
UFUNCTION(BlueprintOverride)
TArray<FSuccessChanceFactor> BlueprintSuccessChance() const
{
    TArray<FSuccessChanceFactor> Factors;

    FSuccessChanceFactor BaseFactor;
    BaseFactor.FactorName = Localization::GetText("MyMod", "Factor_Base");
    BaseFactor.ChanceValue = 0.5f;   // 50% base
    Factors.Add(BaseFactor);

    return Factors;
}

For person interactions, the equivalent uses FModifierBreakdown:

angelscript
UFUNCTION(BlueprintOverride)
FModifierBreakdown BlueprintSuccessChance()
{
    FModifierBreakdown Breakdown;
    Breakdown.Modifiers.Add(
        Localization::GetText("MyMod", "Factor_Base").ToString(), 0.5f
    );
    return Breakdown;
}

Availability Checks

Override BlueprintRequirements() to control when the interaction is available, greyed out, or hidden:

angelscript
UFUNCTION(BlueprintOverride)
TArray<FInteractionAvailabilityReason> BlueprintRequirements() const
{
    TArray<FInteractionAvailabilityReason> Reasons;

    if (SomeCondition)
    {
        FInteractionAvailabilityReason Reason;
        Reason.Reason = Localization::GetText("MyMod", "Reason_SomeCondition");
        Reason.AvailabilityStatus = EInteractionAvailability::GreyedOut;
        Reasons.Add(Reason);
    }

    return Reasons;
}

Return an empty array if the interaction is available. Each reason with a non-Available status will either grey out or hide the interaction.

Availability Status Values

Faction interactions (UDiplomacyInteraction, USpyInteraction, UEdict):

angelscript
enum EInteractionAvailability { Available, GreyedOut, Hidden }

Person interactions (UPersonInteraction):

angelscript
enum EPersonInteractionAvailability { Available, GreyedOut, Hidden }

Notifications

All interaction types can provide custom completion notifications:

angelscript
UFUNCTION(BlueprintOverride)
FText GetSuccessNotificationTitle()
{
    return Localization::GetText("MyMod", "SuccessTitle");
}

UFUNCTION(BlueprintOverride)
FText GetSuccessNotificationBody()
{
    return Localization::GetText("MyMod", "SuccessBody");
}

UFUNCTION(BlueprintOverride)
FText GetFailureNotificationTitle()
{
    return Localization::GetText("MyMod", "FailureTitle");
}

UFUNCTION(BlueprintOverride)
FText GetFailureNotificationBody()
{
    return Localization::GetText("MyMod", "FailureBody");
}

Input Requirements

Some interactions require additional input from the player (selecting settlements, entering gold amounts, etc.). These are configured through InputRequirements.

Faction interaction input types:

angelscript
enum EInteractionInputType { SettlementSelection, GoldAmount, FactionSelection }

Person interaction input types:

angelscript
enum EPersonInteractionInputType {
    PersonSelection, GoldAmount, FactionSelection,
    SettlementSelection, MilitarySelection, TextMessage
}

Input requirements are added in the constructor or in the Initialize() override:

angelscript
UFUNCTION(BlueprintOverride)
void Initialize()
{
    FInteractionInputRequirement Req;
    Req.InputID = n"target_settlements";
    Req.InputType = EInteractionInputType::SettlementSelection;
    Req.InputPrompt = Localization::GetText("MyMod", "SelectSettlements");
    Req.MinSettlements = 1;
    Req.MaxSettlements = 3;
    Req.SettlementSelectionSource = ESettlementSelectionSource::TargetFaction;
    InputRequirements.Add(Req);
}

Retrieve provided inputs in your completion handler:

angelscript
UFUNCTION(BlueprintOverride)
void OnInteractionComplete()
{
    TArray<APopulationCentre> Settlements;
    if (GetProvidedSettlements(n"target_settlements", Settlements))
    {
        // Use the selected settlements
    }
}

Diplomacy Interactions (UDiplomacyInteraction)#

Faction-to-faction diplomatic actions. Inherits from UFactionInteraction.

Difficulty Enum

angelscript
enum EInteractionDifficulty { VeryEasy = 0, Easy = 1, Medium = 2, Hard = 3, VeryHard = 4 }

Properties

All inherited from UFactionInteraction:

PropertyTypeDefaultDescriptionInteractionNameFText--Display name.DescriptionFText--Tooltip description.IconKeyFName--Row key in the DiplomacyIcons DataTable.Costint320Gold cost.Durationint320Days to complete. 0 = instant.DifficultyEInteractionDifficulty--Difficulty rating.bRequiresAgentboolfalseWhether a diplomat is required.bAllowCharacterSelectionboolfalseWhether the player can select a character to send.SuccessOutcomesTArray<FText>--Possible success messages (one chosen randomly).FailureOutcomesTArray<FText>--Possible failure messages.RequiredStatsTMap<EPersonStatistic, int32>--Stat requirements for the agent performing the action.

Overridable Methods

MethodWhen CalledPurposevoid Initialize()After creationSet up input requirements.TArray<FInteractionAvailabilityReason> BlueprintRequirements() constWhen checking availabilityCustom availability logic.TArray<FSuccessChanceFactor> BlueprintSuccessChance() constWhen calculating successBase success chance factors.void OnInteractionComplete()On successApply success effects.void OnInteractionFailure()On failureApply failure effects.bool CanPersonBeSelected(UPerson Person) constWhen building character listFilter which characters can be selected.FText GetSuccessNotificationTitle()On successCustom notification title.FText GetSuccessNotificationBody()On successCustom notification body.FText GetFailureNotificationTitle()On failureCustom notification title.FText GetFailureNotificationBody()On failureCustom notification body.

Runtime Properties

These are available at runtime within your overrides:

PropertyTypeDescriptionFactionAFactionThe target faction.SelectedCharacterUPersonThe selected character (if bAllowCharacterSelection is true).InProgressboolWhether the interaction is currently running.

Example

angelscript
class UProposeTrade : UDiplomacyInteraction
{
    default InteractionName = Localization::GetText("MyMod", "ProposeTrade_Name");
    default Description = Localization::GetText("MyMod", "ProposeTrade_Description");
    default Cost = 200;
    default Duration = 30;
    default Difficulty = EInteractionDifficulty::Easy;
    default bRequiresAgent = true;
    default IconKey = n"ProposeTrade";

    UFUNCTION(BlueprintOverride)
    TArray<FInteractionAvailabilityReason> BlueprintRequirements() const
    {
        TArray<FInteractionAvailabilityReason> Reasons;
        AFaction Source = StrategyGameplay::PlayerFaction;

        // Cannot trade with enemies
        if (Source.GetStatusWith(Faction) == EFactionRelationStatus::War)
        {
            FInteractionAvailabilityReason R;
            R.Reason = Localization::GetText("MyMod", "ProposeTrade_AtWar");
            R.AvailabilityStatus = EInteractionAvailability::Hidden;
            Reasons.Add(R);
        }

        return Reasons;
    }

    UFUNCTION(BlueprintOverride)
    TArray<FSuccessChanceFactor> BlueprintSuccessChance() const
    {
        TArray<FSuccessChanceFactor> Factors;

        FSuccessChanceFactor Base;
        Base.FactorName = Localization::GetText("MyMod", "Factor_Base");
        Base.ChanceValue = 0.6f;
        Factors.Add(Base);

        return Factors;
    }

    UFUNCTION(BlueprintOverride)
    void OnInteractionComplete()
    {
        AFaction Source = StrategyGameplay::PlayerFaction;
        Faction.DiplomacyComponent.AddOpinionModifier(Source, "Trade Agreement", 15.0f, 365);
    }

    UFUNCTION(BlueprintOverride)
    void OnInteractionFailure()
    {
        AFaction Source = StrategyGameplay::PlayerFaction;
        Faction.DiplomacyComponent.AddOpinionModifier(Source, "Failed Trade Proposal", -5.0f, 180);
    }
}

Spy Interactions (USpyInteraction)#

Espionage actions against a target faction. Extends UFactionInteraction with spy-specific properties.

Additional Properties

PropertyTypeDefaultDescriptionRequiredNetworkStrengthfloat0Minimum spy network strength (0.0--1.0) required to attempt.CooldownDaysint3290Days before this interaction can be used again on the same target.LastCompletionDateint320(Runtime) Game day of last completion.

Additional Methods

MethodWhen CalledPurposeUPerson GetAssignedSpy() constAny timeReturns the spy assigned to the target faction.

Spy interactions inherit all the same overridable methods as diplomacy interactions. The system automatically checks RequiredNetworkStrength and CooldownDays in addition to your custom requirements.

Example

angelscript
class UStealSecrets : USpyInteraction
{
    default InteractionName = Localization::GetText("MyMod", "StealSecrets_Name");
    default Description = Localization::GetText("MyMod", "StealSecrets_Description");
    default Cost = 500;
    default Duration = 60;
    default Difficulty = EInteractionDifficulty::Hard;
    default bRequiresAgent = true;
    default IconKey = n"StealSecrets";
    default RequiredNetworkStrength = 0.4f;
    default CooldownDays = 180;

    UFUNCTION(BlueprintOverride)
    TArray<FSuccessChanceFactor> BlueprintSuccessChance() const
    {
        TArray<FSuccessChanceFactor> Factors;

        FSuccessChanceFactor Base;
        Base.FactorName = Localization::GetText("MyMod", "Factor_Base");
        Base.ChanceValue = 0.3f;
        Factors.Add(Base);

        return Factors;
    }

    UFUNCTION(BlueprintOverride)
    void OnInteractionComplete()
    {
        AFaction Source = StrategyGameplay::PlayerFaction;
        Source.Gold += 800;
    }

    UFUNCTION(BlueprintOverride)
    void OnInteractionFailure()
    {
        AFaction Source = StrategyGameplay::PlayerFaction;
        Faction.DiplomacyComponent.AddOpinionModifier(Source, "Caught Spy", -30.0f, 365);
    }
}

Person Interactions (UPersonInteraction)#

Actions between two characters -- the initiator and the target.

Category Enum

angelscript
enum EPersonInteractionCategory {
    Diplomacy, Intrigue, Personal, Military, Economic, Marriage, Family
}

Difficulty Enum

angelscript
enum EPersonInteractionDifficulty { VeryEasy = 0, Easy = 1, Medium = 2, Hard = 3, VeryHard = 4 }

Properties

PropertyTypeDefaultDescriptionInteractionNameFText--Display name.DescriptionFText--Tooltip description.IconKeyFName--Row key in the InteractionIcons DataTable.CategoryEPersonInteractionCategoryPersonalUI category grouping.Costint320Gold cost.Durationint321Days to complete. 0 = instant.CooldownDaysint320Days before reuse on the same target.DifficultyEPersonInteractionDifficultyMediumDifficulty rating.bRequiresPresenceboolfalseWhether the initiator must be at the same location.bCanTargetDeadPersonsboolfalseWhether the target can be dead.bAllowInitiatorSelectionboolfalseWhether the player can choose a different initiator.RequiredRelationshipfloat-100Minimum relationship level with the target.RequiredStatsTMap<EPersonStatistic, int32>--Stat requirements for the initiator.SuccessOutcomesTArray<FText>--Possible success messages.FailureOutcomesTArray<FText>--Possible failure messages.

Overridable Methods

MethodWhen CalledPurposeTArray<FPersonInteractionAvailabilityReason> BlueprintRequirements() constWhen checking availabilityCustom availability logic.FModifierBreakdown BlueprintSuccessChance()When calculating successSuccess chance breakdown.bool CanPersonInitiate(UPerson Person) constWhen bAllowInitiatorSelection is trueFilter valid initiators.void OnInteractionComplete()On successApply success effects.void OnInteractionFailure()On failureApply failure effects.FText GetSuccessNotificationTitle()On successCustom notification title.FText GetSuccessNotificationBody()On successCustom notification body.FText GetFailureNotificationTitle()On failureCustom notification title.FText GetFailureNotificationBody()On failureCustom notification body.

Runtime Properties

PropertyTypeDescriptionInitiatingPersonUPersonThe character performing the action.TargetPersonUPersonThe character being targeted.

Example

angelscript
UCLASS(Blueprintable)
class UChallengeToDuel : UPersonInteraction
{
    default InteractionName = Localization::GetText("MyMod", "Duel_Name");
    default Description = Localization::GetText("MyMod", "Duel_Description");
    default Category = EPersonInteractionCategory::Personal;
    default Cost = 0;
    default Duration = 7;
    default CooldownDays = 365;
    default Difficulty = EPersonInteractionDifficulty::Medium;
    default bRequiresPresence = true;
    default IconKey = n"ChallengeToDuel";

    UFUNCTION(BlueprintOverride)
    TArray<FPersonInteractionAvailabilityReason> BlueprintRequirements() const
    {
        TArray<FPersonInteractionAvailabilityReason> Reasons;

        if (InitiatingPerson == TargetPerson)
        {
            FPersonInteractionAvailabilityReason R;
            R.Reason = Localization::GetText("MyMod", "Duel_CannotSelf");
            R.AvailabilityStatus = EPersonInteractionAvailability::Hidden;
            Reasons.Add(R);
        }

        if (TargetPerson != nullptr && TargetPerson.Stats.GetConstitution() < 20.0f)
        {
            FPersonInteractionAvailabilityReason R;
            R.Reason = Localization::GetText("MyMod", "Duel_TooWeak");
            R.AvailabilityStatus = EPersonInteractionAvailability::GreyedOut;
            Reasons.Add(R);
        }

        return Reasons;
    }

    UFUNCTION(BlueprintOverride)
    FModifierBreakdown BlueprintSuccessChance()
    {
        FModifierBreakdown Breakdown;

        Breakdown.Modifiers.Add(
            Localization::GetText("MyMod", "Factor_Base").ToString(), 0.4f
        );

        if (InitiatingPerson != nullptr)
        {
            float TacticsBonus = InitiatingPerson.Stats.GetTactics() * 0.005f;
            Breakdown.Modifiers.Add(
                Localization::GetText("MyMod", "Factor_Tactics").ToString(), TacticsBonus
            );
        }

        return Breakdown;
    }

    UFUNCTION(BlueprintOverride)
    void OnInteractionComplete()
    {
        if (TargetPerson != nullptr)
        {
            TargetPerson.ModifyRelationship(InitiatingPerson, -20.0f);
        }
    }

    UFUNCTION(BlueprintOverride)
    FText GetSuccessNotificationBody()
    {
        if (TargetPerson != nullptr)
        {
            return FText::FromString(
                Localization::GetText("MyMod", "Duel_SuccessBody").ToString()
                    .Replace("{Target}", TargetPerson.GetFormattedName())
            );
        }
        return Localization::GetText("MyMod", "Duel_SuccessBodyGeneric");
    }
}

Power Bloc Interactions (UPowerBlocInteraction)#

Actions targeting internal political factions (power blocs). These use a simpler interface than other interaction types.

Properties

PropertyTypeDefaultDescriptionInteractionNameFText--Display name.DescriptionFText--Tooltip description.IconKeyFName--Row key in the InteractionIcons DataTable.GoldCostint320Gold cost to execute.CooldownDaysint3230Days before reuse on the same bloc.

Overridable Methods

MethodWhen CalledPurposebool IsApplicableToBloc(UPowerBloc Bloc) constWhen building interaction listWhether this interaction applies to the given bloc type.TArray<FBlocInteractionUnavailableReason> GetUnavailableReasons() constWhen checking availabilityReasons why the interaction is currently unavailable. Empty = available.FBlocInteractionSuccessBreakdown GetSuccessChance() constWhen calculating successSuccess chance with named modifiers.void OnSuccess()On successApply success effects.void OnFailure()On failureApply failure effects.

Runtime Properties

PropertyTypeDescriptionTargetBlocUPowerBlocThe power bloc being targeted.

Success Chance Breakdown

Power bloc interactions use FBlocInteractionSuccessBreakdown:

angelscript
UFUNCTION(BlueprintOverride)
FBlocInteractionSuccessBreakdown GetSuccessChance() const
{
    FBlocInteractionSuccessBreakdown Breakdown;
    Breakdown.BaseChance = 0.5f;    // 50% base
    Breakdown.Modifiers.Add("Relationship Bonus", 0.15f);
    return Breakdown;
}

The final chance is BaseChance + sum of all modifiers, clamped to 0.0--1.0.

Example

angelscript
UCLASS()
class UHostBanquet : UPowerBlocInteraction
{
    default InteractionName = Localization::GetText("MyMod", "HostBanquet_Name");
    default Description = Localization::GetText("MyMod", "HostBanquet_Description");
    default IconKey = n"HostBanquet";
    default GoldCost = 300;
    default CooldownDays = 90;

    UFUNCTION(BlueprintOverride)
    TArray<FBlocInteractionUnavailableReason> GetUnavailableReasons() const
    {
        TArray<FBlocInteractionUnavailableReason> Reasons;

        AFaction PlayerFaction = StrategyGameplay::PlayerFaction;
        if (PlayerFaction.Gold < GoldCost)
        {
            FBlocInteractionUnavailableReason R;
            R.Reason = Localization::GetText("MyMod", "HostBanquet_NotEnoughGold");
            Reasons.Add(R);
        }

        return Reasons;
    }

    UFUNCTION(BlueprintOverride)
    FBlocInteractionSuccessBreakdown GetSuccessChance() const
    {
        FBlocInteractionSuccessBreakdown Breakdown;
        Breakdown.BaseChance = 0.7f;
        return Breakdown;
    }

    UFUNCTION(BlueprintOverride)
    void OnSuccess()
    {
        AFaction PlayerFaction = StrategyGameplay::PlayerFaction;
        PlayerFaction.Gold -= GoldCost;
        // Increase bloc satisfaction
    }

    UFUNCTION(BlueprintOverride)
    void OnFailure()
    {
        AFaction PlayerFaction = StrategyGameplay::PlayerFaction;
        PlayerFaction.Gold -= GoldCost;
        // Gold spent but no benefit
    }
}

Policies (UPolicy)#

Policies are adjustable sliders that affect faction-wide modifiers. Each policy has a value range that the player can adjust.

Properties

PropertyTypeDefaultDescriptionPolicyNameFText--Display name.DescriptionFText--Tooltip description.IconKeyFName--Row key in the PolicyIcons DataTable.Valuefloat0Current slider value.MinValuefloat0Minimum slider value.MaxValuefloat0Maximum slider value.bIncreaseCausesUnrestbooltrueWhether increasing the slider causes unrest.bDecreaseCausesUnrestbooltrueWhether decreasing the slider causes unrest.

Overridable Methods

MethodWhen CalledPurposevoid OnApply(AFaction Faction)When the value changesApply the policy's effects based on current value.FText GetEffectDescription() constWhen rendering tooltipDynamic description of current effects.

Example

angelscript
UCLASS()
class UTaxRate : UPolicy
{
    default PolicyName = Localization::GetText("MyMod", "TaxRate_Name");
    default Description = Localization::GetText("MyMod", "TaxRate_Description");
    default IconKey = n"TaxRate";
    default MinValue = 0.0f;
    default MaxValue = 1.0f;
    default Value = 0.5f;
    default bIncreaseCausesUnrest = true;
    default bDecreaseCausesUnrest = false;

    UFUNCTION(BlueprintOverride)
    void OnApply(AFaction Faction)
    {
        Faction.EconomyComponent.TaxRateModifier = Value;
    }

    UFUNCTION(BlueprintOverride)
    FText GetEffectDescription() const
    {
        int32 Percentage = int32(Value * 100.0f);
        return FText::FromString(
            Localization::GetText("MyMod", "TaxRate_Effect").ToString()
                .Replace("{Percentage}", "" + Percentage)
        );
    }
}

Edicts (UEdict)#

Edicts are one-time faction actions that always succeed. They extend UFactionInteraction with a simplified interface.

Edicts inherit all the interaction properties (InteractionName, Description, Cost, Duration, etc.) but override the success chance to always return 100%.

Overridable Methods

MethodWhen CalledPurposevoid OnApply(AFaction OwningFaction)When the edict is enactedApply the edict's effects.TArray<FInteractionAvailabilityReason> BlueprintRequirements() constWhen checking availabilityCustom availability logic.

Example

angelscript
UCLASS()
class UCallLevies : UEdict
{
    default InteractionName = Localization::GetText("MyMod", "CallLevies_Name");
    default Description = Localization::GetText("MyMod", "CallLevies_Description");
    default Cost = 500;
    default Duration = 0;
    default IconKey = n"CallLevies";

    UFUNCTION(BlueprintOverride)
    void OnApply(AFaction OwningFaction)
    {
        OwningFaction.Gold -= Cost;
        // Spawn levy units across settlements
    }

    UFUNCTION(BlueprintOverride)
    TArray<FInteractionAvailabilityReason> BlueprintRequirements() const
    {
        TArray<FInteractionAvailabilityReason> Reasons;

        AFaction PlayerFaction = StrategyGameplay::PlayerFaction;
        if (PlayerFaction.Gold < Cost)
        {
            FInteractionAvailabilityReason R;
            R.Reason = Localization::GetText("MyMod", "CallLevies_NotEnoughGold");
            R.AvailabilityStatus = EInteractionAvailability::GreyedOut;
            Reasons.Add(R);
        }

        return Reasons;
    }
}

Character Stat Reference#

Several interaction types reference character statistics. Here is the full enum:

angelscript
enum EPersonStatistic {
    Tactics,       // Military command ability
    Authority,     // Leadership and influence
    Cunning,       // Subterfuge and intelligence
    Governance,    // Administrative ability
    Loyalty,       // Faithfulness and reliability
    Constitution,  // Physical health and endurance
    None           // No specific stat
}

Access stats on a character:

angelscript
float Tactics = Character.Stats.GetTactics();
float Authority = Character.Stats.GetAuthority();
float Cunning = Character.Stats.GetCunning();
float Governance = Character.Stats.GetGovernance();
float Loyalty = Character.Stats.GetLoyalty();
float Constitution = Character.Stats.GetConstitution();

Next Steps#