Skip to content

NwCreature.OnBlocked is not handling a case when blocking object is not a door #860

Description

@xtul9

When you subscribe to creature's OnBlocked event, it's possible to get this exception:

anvil    | E [2026/03/21 13:40:26.469] [Anvil.AnvilCore] An exception occured while executing script "____anvil_event"
anvil    | System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
anvil    |  ---> System.InvalidCastException: Unable to cast object of type 'Anvil.API.NwCreature' to type 'Anvil.API.NwDoor'.
anvil    |    at Anvil.API.IntegerExtensions.ToNwObject[T](UInt32 objectId) in /home/runner/work/Anvil/Anvil/NWN.Anvil/src/main/API/Extensions/IntegerExtensions.cs:line 119
anvil    |    at Anvil.API.Events.CreatureEvents.OnBlocked..ctor() in /home/runner/work/Anvil/Anvil/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnBlocked.cs:line 21
anvil    |    at System.RuntimeType.CreateInstanceOfT()
anvil    |    --- End of inner exception stack trace ---
anvil    |    at System.RuntimeType.CreateInstanceOfT()
anvil    |    at System.Activator.CreateInstance[T]()
anvil    |    at Anvil.API.Events.GameEventFactory.<CheckConstructorRegistered>g__Constructor|12_0[TEvent]() in /home/runner/work/Anvil/Anvil/NWN.Anvil/src/main/API/Events/Game/GameEventFactory.cs:line 72
anvil    |    at Anvil.Services.ScriptDispatchService.TryExecuteScript(String script, UInt32 objectSelf) in /home/runner/work/Anvil/Anvil/NWN.Anvil/src/main/Services/Scripts/ScriptDispatchService.cs:line 22
anvil    |    at Anvil.AnvilCore.OnRunScript(IntPtr scriptPtr, UInt32 oidSelf) in /home/runner/work/Anvil/Anvil/NWN.Anvil/src/main/AnvilCore.FunctionHandlers.cs:line 91

The reason for this is quite simple. The event arguments contain this:

public NwDoor BlockingDoor { get; } = NWScript.GetBlockingDoor().ToNwObject<NwDoor>();

The BlockingDoor property isn't always a door. Sometimes it's a creature. This is contrary to GetBlockingDoor documentation, but even the default event handler proves this is a thing that does happen:

//::///////////////////////////////////////////////
//:: Default On Blocked
//:: NW_C2_DEFAULTE
//:: Copyright (c) 2001 Bioware Corp.
//:://////////////////////////////////////////////
/*
    This will cause blocked creatures to open
    or smash down doors depending on int and
    str.
*/
//:://////////////////////////////////////////////
//:: Created By: Preston Watamaniuk
//:: Created On: Nov 23, 2001
//:://////////////////////////////////////////////

void main()
{
    object oDoor = GetBlockingDoor();
    if (GetObjectType(oDoor) == OBJECT_TYPE_CREATURE)
    {
        // * Increment number of times blocked
        /*SetLocalInt(OBJECT_SELF, "X2_NUMTIMES_BLOCKED", GetLocalInt(OBJECT_SELF, "X2_NUMTIMES_BLOCKED") + 1);
        if (GetLocalInt(OBJECT_SELF, "X2_NUMTIMES_BLOCKED") > 3)
        {
            SpeakString("Blocked by creature");
            SetLocalInt(OBJECT_SELF, "X2_NUMTIMES_BLOCKED",0);
            ClearAllActions();
            object oEnemy = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY);
            if (GetIsObjectValid(oEnemy) == TRUE)
            {
                ActionEquipMostDamagingRanged(oEnemy);
                ActionAttack(oEnemy);
            }
            return;
        }   */
        return;
    }
    if(GetAbilityScore(OBJECT_SELF, ABILITY_INTELLIGENCE) >= 5)
    {
        if(GetIsDoorActionPossible(oDoor, DOOR_ACTION_OPEN) && GetAbilityScore(OBJECT_SELF, ABILITY_INTELLIGENCE) >= 7 )
        {
            DoDoorAction(oDoor, DOOR_ACTION_OPEN);
        }
        else if(GetIsDoorActionPossible(oDoor, DOOR_ACTION_BASH))
        {
            DoDoorAction(oDoor, DOOR_ACTION_BASH);
        }
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions