diff --git a/NWN.Anvil.Tests/src/main/API/TwoDimArray/TwoDimArrayTests.cs b/NWN.Anvil.Tests/src/main/API/TwoDimArray/TwoDimArrayTests.cs
index 962059c2b..32e63e303 100644
--- a/NWN.Anvil.Tests/src/main/API/TwoDimArray/TwoDimArrayTests.cs
+++ b/NWN.Anvil.Tests/src/main/API/TwoDimArray/TwoDimArrayTests.cs
@@ -2,7 +2,6 @@
using Anvil.API;
using Anvil.Services;
using NUnit.Framework;
-using NWN.Native.API;
using NWNX.NET.Native;
namespace Anvil.Tests.API
diff --git a/NWN.Anvil/src/main/API/Constants/PlayerDeviceProperty.cs b/NWN.Anvil/src/main/API/Constants/PlayerDeviceProperty.cs
index 64f70a20b..0054ad434 100644
--- a/NWN.Anvil/src/main/API/Constants/PlayerDeviceProperty.cs
+++ b/NWN.Anvil/src/main/API/Constants/PlayerDeviceProperty.cs
@@ -48,12 +48,14 @@ public sealed class PlayerDeviceProperty
public static readonly PlayerDeviceProperty UiMouseoverFeedback = new PlayerDeviceProperty(NWScript.PLAYER_DEVICE_PROPERTY_UI_MOUSEOVER_FEEDBACK);
public static readonly PlayerDeviceProperty UiTextBubble = new PlayerDeviceProperty(NWScript.PLAYER_DEVICE_PROPERTY_UI_TEXT_BUBBLE);
public static readonly PlayerDeviceProperty UiTargetingFeedback = new PlayerDeviceProperty(NWScript.PLAYER_DEVICE_PROPERTY_UI_TARGETING_FEEDBACK);
+ public static readonly PlayerDeviceProperty UiCanClickSelfWhileWalking = new PlayerDeviceProperty(NWScript.PLAYER_DEVICE_PROPERTY_UI_CAN_CLICK_SELF_WHILE_WALKING);
public static readonly PlayerDeviceProperty UiFloatingTextFeedback = new PlayerDeviceProperty(NWScript.PLAYER_DEVICE_PROPERTY_UI_FLOATING_TEXT_FEEDBACK);
public static readonly PlayerDeviceProperty UiFloatingTextFeedbackDamageTotalsOnly = new PlayerDeviceProperty(NWScript.PLAYER_DEVICE_PROPERTY_UI_FLOATING_TEXT_FEEDBACK_DAMAGE_TOTALS_ONLY);
public static readonly PlayerDeviceProperty UiHideQuickchatTextInChatWindow = new PlayerDeviceProperty(NWScript.PLAYER_DEVICE_PROPERTY_UI_HIDE_QUICKCHAT_TEXT_IN_CHAT_WINDOW);
public static readonly PlayerDeviceProperty UiConfirmSelfcastSpells = new PlayerDeviceProperty(NWScript.PLAYER_DEVICE_PROPERTY_UI_CONFIRM_SELFCAST_SPELLS);
public static readonly PlayerDeviceProperty UiConfirmSelfcastFeats = new PlayerDeviceProperty(NWScript.PLAYER_DEVICE_PROPERTY_UI_CONFIRM_SELFCAST_FEATS);
public static readonly PlayerDeviceProperty UiConfirmSelfcastItems = new PlayerDeviceProperty(NWScript.PLAYER_DEVICE_PROPERTY_UI_CONFIRM_SELFCAST_ITEMS);
+ public static readonly PlayerDeviceProperty UiChargenSortClasses = new PlayerDeviceProperty(NWScript.PLAYER_DEVICE_PROPERTY_UI_CHARGEN_SORT_CLASSES);
public static readonly PlayerDeviceProperty UiChatPanePrimaryHeight = new PlayerDeviceProperty(NWScript.PLAYER_DEVICE_PROPERTY_UI_CHAT_PANE_PRIMARY_HEIGHT);
public static readonly PlayerDeviceProperty UiChatPaneSecondaryHeight = new PlayerDeviceProperty(NWScript.PLAYER_DEVICE_PROPERTY_UI_CHAT_PANE_SECONDARY_HEIGHT);
public static readonly PlayerDeviceProperty UiChatSwearFilter = new PlayerDeviceProperty(NWScript.PLAYER_DEVICE_PROPERTY_UI_CHAT_SWEAR_FILTER);
diff --git a/NWN.Anvil/src/main/API/Constants/PlayerPlatform.cs b/NWN.Anvil/src/main/API/Constants/PlayerPlatform.cs
index 7dfa4835b..d41ccae41 100644
--- a/NWN.Anvil/src/main/API/Constants/PlayerPlatform.cs
+++ b/NWN.Anvil/src/main/API/Constants/PlayerPlatform.cs
@@ -13,6 +13,7 @@ public enum PlayerPlatform
LinuxArm64 = NWScript.PLAYER_DEVICE_PLATFORM_LINUX_ARM64,
MacX86 = NWScript.PLAYER_DEVICE_PLATFORM_MAC_X86,
MacX64 = NWScript.PLAYER_DEVICE_PLATFORM_MAC_X64,
+ MacArm64 = NWScript.PLAYER_DEVICE_PLATFORM_MAC_ARM64,
Ios = NWScript.PLAYER_DEVICE_PLATFORM_IOS,
AndroidArm32 = NWScript.PLAYER_DEVICE_PLATFORM_ANDROID_ARM32,
AndroidArm64 = NWScript.PLAYER_DEVICE_PLATFORM_ANDROID_ARM64,
diff --git a/NWN.Anvil/src/main/API/Constants/SavingThrowType.cs b/NWN.Anvil/src/main/API/Constants/SavingThrowType.cs
index 8b8a2be38..70de0dad2 100644
--- a/NWN.Anvil/src/main/API/Constants/SavingThrowType.cs
+++ b/NWN.Anvil/src/main/API/Constants/SavingThrowType.cs
@@ -25,5 +25,6 @@ public enum SavingThrowType
Evil = NWScript.SAVING_THROW_TYPE_EVIL,
Law = NWScript.SAVING_THROW_TYPE_LAW,
Chaos = NWScript.SAVING_THROW_TYPE_CHAOS,
+ Paralysis = NWScript.SAVING_THROW_TYPE_PARALYSIS,
}
}
diff --git a/NWN.Anvil/src/main/API/Constants/SpellFailureType.cs b/NWN.Anvil/src/main/API/Constants/SpellFailureType.cs
new file mode 100644
index 000000000..eb758503c
--- /dev/null
+++ b/NWN.Anvil/src/main/API/Constants/SpellFailureType.cs
@@ -0,0 +1,10 @@
+using NWN.Core;
+
+namespace Anvil.API
+{
+ public enum SpellFailureType
+ {
+ All = NWScript.SPELL_FAILURE_TYPE_ALL,
+ Arcane = NWScript.SPELL_FAILURE_TYPE_ARCANE,
+ }
+}
diff --git a/NWN.Anvil/src/main/API/EngineStructures/Effect.Create.cs b/NWN.Anvil/src/main/API/EngineStructures/Effect.Create.cs
index 32155d4f6..7e1c2e0ab 100644
--- a/NWN.Anvil/src/main/API/EngineStructures/Effect.Create.cs
+++ b/NWN.Anvil/src/main/API/EngineStructures/Effect.Create.cs
@@ -587,9 +587,12 @@ public static Effect Poison(PoisonType poisonType)
///
/// The polymorph to apply.
/// If true, players cannot dismiss the polymorph effect.
- public static Effect Polymorph(PolymorphTableEntry polymorphType, bool locked = false)
+ /// The visual effect that will play when this polymorph is removed. Set to null to have no effect play.
+ /// Set a custom spell ability modifier for the 3 polymorph spells. Save DC is 10 + Innate spell level + this ability modifier.
+ /// Set a custom caster level for the 3 polymorph spells.
+ public static Effect Polymorph(PolymorphTableEntry polymorphType, bool locked = false, VfxType? unPolymorphVfx = VfxType.ImpPolymorph, int spellAbilityModifier = -1, int spellAbilityCasterLevel = 0)
{
- return NWScript.EffectPolymorph(polymorphType.RowIndex, locked.ToInt())!;
+ return NWScript.EffectPolymorph(polymorphType.RowIndex, locked.ToInt(), (int?)unPolymorphVfx ?? -1, spellAbilityModifier, spellAbilityCasterLevel)!;
}
///
@@ -733,9 +736,10 @@ public static Effect Slow()
///
/// A positive number representing the percent chance of spell failing (1-100)
/// The spell school that is affected.
- public static Effect SpellFailure(int failPct, SpellSchool spellSchool = SpellSchool.General)
+ /// Use constants for different spell failure types.
+ public static Effect SpellFailure(int failPct, SpellSchool spellSchool = SpellSchool.General, SpellFailureType failureType = SpellFailureType.All)
{
- return NWScript.EffectSpellFailure(failPct, (int)spellSchool)!;
+ return NWScript.EffectSpellFailure(failPct, (int)spellSchool, (int)failureType)!;
}
///
@@ -786,20 +790,32 @@ public static Effect Stunned()
}
///
- /// Creates an effect to summon a creature.
+ /// Creates an effect to summon a creature from a resref.
/// THIS IS OBJECT CONTEXT SENSITIVE! Use to correctly assign the right owner of the master.
///
/// The template/ResRef of the creature to summon.
- /// A visual effect to display upon summoning.
+ /// A visual effect to display upon summoning.
/// A delay between the visual effect, and the creature actually being added to the area.
/// The appear animation to use.
- public static Effect SummonCreature(string creatureResRef, VfxType vfxType, TimeSpan delay = default, int appearType = 0)
+ /// A visual effect to display when this creature is unsummoned.
+ public static Effect SummonCreature(string creatureResRef, VisualEffectTableEntry summonVfx, TimeSpan delay = default, int appearType = 0, VisualEffectTableEntry? unsummonVfx = default)
{
- return NWScript.EffectSummonCreature(creatureResRef, (int)vfxType, (float)delay.TotalSeconds, appearType)!;
+ return NWScript.EffectSummonCreature(creatureResRef, summonVfx.RowIndex, (float)delay.TotalSeconds, appearType, unsummonVfx?.RowIndex ?? (int)VfxType.ImpUnsummon)!;
}
///
- /// Creates a swarm effect. This is exactly the same as , except, after one dies, another takes its place.
+ /// Creates an effect that uses summon behaviours on an existing creature.
+ ///
+ /// The creature to be used as the summoned creature. Must not have a current "master" creature.
+ /// A visual effect to display when claiming the summoned creature.
+ /// A visual effect to play when the summoned creature is unsummoned.
+ public static Effect SummonCreature(NwCreature summonCreature, VisualEffectTableEntry summonVfx, VisualEffectTableEntry? unsummonVfx = default)
+ {
+ return NWScript.EffectSummonCreature("", summonVfx.RowIndex, 0f, 0, unsummonVfx?.RowIndex ?? (int)VfxType.ImpUnsummon, summonCreature)!;
+ }
+
+ ///
+ /// Creates a swarm effect. This is exactly the same as , except, after one dies, another takes its place.
///
/// If true, while the effect is active and the last creature dies, it will loop back to the first template.
/// The blueprint of the first creature to spawn.
diff --git a/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnDisturbed.cs b/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnDisturbed.cs
index 2f474ea55..c76ff0f25 100644
--- a/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnDisturbed.cs
+++ b/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnDisturbed.cs
@@ -18,21 +18,29 @@ public sealed class OnDisturbed : IEvent
///
/// Gets the that had its inventory disturbed.
///
- public NwCreature CreatureDisturbed { get; } = NWScript.OBJECT_SELF.ToNwObject()!;
+ public NwCreature CreatureDisturbed { get; }
///
/// Gets the that was disturbed in the inventory.
///
- public NwItem DisturbedItem { get; } = NWScript.GetInventoryDisturbItem().ToNwObject()!;
+ public NwItem DisturbedItem { get; }
///
/// Gets the that disturbed another inventory.
///
- public NwCreature Disturber { get; } = NWScript.GetLastDisturbed().ToNwObject()!;
+ public NwCreature Disturber { get; }
- public InventoryDisturbType DisturbType { get; } = (InventoryDisturbType)NWScript.GetInventoryDisturbType();
+ public InventoryDisturbType DisturbType { get; }
NwObject IEvent.Context => CreatureDisturbed;
+
+ public OnDisturbed()
+ {
+ CreatureDisturbed = NWScript.OBJECT_SELF.ToNwObject()!;
+ DisturbedItem = NWScript.GetInventoryDisturbItem(CreatureDisturbed).ToNwObject()!;
+ Disturber = NWScript.GetLastDisturbed(CreatureDisturbed).ToNwObject()!;
+ DisturbType = (InventoryDisturbType)NWScript.GetInventoryDisturbType(CreatureDisturbed);
+ }
}
}
}
diff --git a/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnClose.cs b/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnClose.cs
index b12ea475a..a7a9fe54d 100644
--- a/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnClose.cs
+++ b/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnClose.cs
@@ -15,14 +15,20 @@ public sealed class OnClose : IEvent
///
/// Gets the that closed the .
///
- public NwGameObject ClosedBy { get; } = NWScript.GetLastClosedBy().ToNwObject()!;
+ public NwGameObject ClosedBy { get; }
///
/// Gets the that was closed.
///
- public NwDoor Door { get; } = NWScript.OBJECT_SELF.ToNwObject()!;
+ public NwDoor Door { get; }
NwObject IEvent.Context => Door;
+
+ public OnClose()
+ {
+ Door = NWScript.OBJECT_SELF.ToNwObject()!;
+ ClosedBy = NWScript.GetLastClosedBy(Door).ToNwObject()!;
+ }
}
}
}
diff --git a/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnLock.cs b/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnLock.cs
index fe7c491e2..706a98082 100644
--- a/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnLock.cs
+++ b/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnLock.cs
@@ -15,14 +15,20 @@ public sealed class OnLock : IEvent
///
/// Gets the that was locked.
///
- public NwDoor Door { get; } = NWScript.OBJECT_SELF.ToNwObject()!;
+ public NwDoor Door { get; }
///
/// Gets the that locked this .
///
- public NwCreature LockedBy { get; } = NWScript.GetLastLocked().ToNwObject()!;
+ public NwCreature LockedBy { get; }
NwObject IEvent.Context => Door;
+
+ public OnLock()
+ {
+ Door = NWScript.OBJECT_SELF.ToNwObject()!;
+ LockedBy = NWScript.GetLastLocked(Door).ToNwObject()!;
+ }
}
}
}
diff --git a/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnOpen.cs b/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnOpen.cs
index 0ad33e73c..ca317a3aa 100644
--- a/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnOpen.cs
+++ b/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnOpen.cs
@@ -15,14 +15,20 @@ public sealed class OnOpen : IEvent
///
/// Gets the that was opened.
///
- public NwDoor Door { get; } = NWScript.OBJECT_SELF.ToNwObject()!;
+ public NwDoor Door { get; }
///
/// Gets the that opened the .
///
- public NwGameObject OpenedBy { get; } = NWScript.GetLastOpenedBy().ToNwObject()!;
+ public NwGameObject OpenedBy { get; }
NwObject IEvent.Context => Door;
+
+ public OnOpen()
+ {
+ Door = NWScript.OBJECT_SELF.ToNwObject()!;
+ OpenedBy = NWScript.GetLastOpenedBy(Door).ToNwObject()!;
+ }
}
}
}
diff --git a/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnUnlock.cs b/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnUnlock.cs
index 91c9a9b0b..9a259a9c2 100644
--- a/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnUnlock.cs
+++ b/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnUnlock.cs
@@ -15,14 +15,20 @@ public sealed class OnUnlock : IEvent
///
/// Gets the that was unlocked.
///
- public NwDoor Door { get; } = NWScript.OBJECT_SELF.ToNwObject()!;
+ public NwDoor Door { get; }
///
/// Gets the that unlocked .
///
- public NwCreature UnlockedBy { get; } = NWScript.GetLastUnlocked().ToNwObject()!;
+ public NwCreature UnlockedBy { get; }
NwObject IEvent.Context => Door;
+
+ public OnUnlock()
+ {
+ Door = NWScript.OBJECT_SELF.ToNwObject()!;
+ UnlockedBy = NWScript.GetLastUnlocked(Door).ToNwObject()!;
+ }
}
}
}
diff --git a/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnClose.cs b/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnClose.cs
index 83345b1e3..953665b8d 100644
--- a/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnClose.cs
+++ b/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnClose.cs
@@ -18,14 +18,20 @@ public sealed class OnClose : IEvent
///
/// Gets the that closed the .
///
- public NwCreature? ClosedBy { get; } = NWScript.GetLastClosedBy().ToNwObject();
+ public NwCreature? ClosedBy { get; }
///
/// Gets the that was closed.
///
- public NwPlaceable Placeable { get; } = NWScript.OBJECT_SELF.ToNwObject()!;
+ public NwPlaceable Placeable { get; }
NwObject IEvent.Context => Placeable;
+
+ public OnClose()
+ {
+ Placeable = NWScript.OBJECT_SELF.ToNwObject()!;
+ ClosedBy = NWScript.GetLastClosedBy(Placeable).ToNwObject();
+ }
}
}
}
diff --git a/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnDisturbed.cs b/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnDisturbed.cs
index a3d1fad28..e4444b4d3 100644
--- a/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnDisturbed.cs
+++ b/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnDisturbed.cs
@@ -18,24 +18,32 @@ public sealed class OnDisturbed : IEvent
///
/// Gets the that triggered the disturb event on .
///
- public NwItem? DisturbedItem { get; } = NWScript.GetInventoryDisturbItem().ToNwObject();
+ public NwItem? DisturbedItem { get; }
///
/// Gets the object that disturbed .
///
- public NwGameObject? Disturber { get; } = NWScript.GetLastDisturbed().ToNwObject();
+ public NwGameObject? Disturber { get; }
///
/// Gets the .
///
- public InventoryDisturbType DisturbType { get; } = (InventoryDisturbType)NWScript.GetInventoryDisturbType();
+ public InventoryDisturbType DisturbType { get; }
///
/// Gets the that was disturbed.
///
- public NwPlaceable Placeable { get; } = NWScript.OBJECT_SELF.ToNwObject()!;
+ public NwPlaceable Placeable { get; }
NwObject IEvent.Context => Placeable;
+
+ public OnDisturbed()
+ {
+ Placeable = NWScript.OBJECT_SELF.ToNwObject()!;
+ Disturber = NWScript.GetLastDisturbed(Placeable).ToNwObject();
+ DisturbType = (InventoryDisturbType)NWScript.GetInventoryDisturbType(Placeable);
+ DisturbedItem = NWScript.GetInventoryDisturbItem(Placeable).ToNwObject();
+ }
}
}
}
diff --git a/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnLock.cs b/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnLock.cs
index a885a610e..cc27a8644 100644
--- a/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnLock.cs
+++ b/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnLock.cs
@@ -18,14 +18,20 @@ public sealed class OnLock : IEvent
///
/// Gets the that locked this .
///
- public NwCreature? LockedBy { get; } = NWScript.GetLastLocked().ToNwObject();
+ public NwCreature? LockedBy { get; }
///
/// Gets the that was locked.
///
- public NwPlaceable LockedPlaceable { get; } = NWScript.OBJECT_SELF.ToNwObject()!;
+ public NwPlaceable LockedPlaceable { get; }
NwObject IEvent.Context => LockedPlaceable;
+
+ public OnLock()
+ {
+ LockedPlaceable = NWScript.OBJECT_SELF.ToNwObject()!;
+ LockedBy = NWScript.GetLastLocked(LockedPlaceable).ToNwObject();
+ }
}
}
}
diff --git a/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnOpen.cs b/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnOpen.cs
index dbfb55815..21697d271 100644
--- a/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnOpen.cs
+++ b/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnOpen.cs
@@ -18,14 +18,20 @@ public sealed class OnOpen : IEvent
///
/// Gets the that opened the .
///
- public NwCreature? OpenedBy { get; } = NWScript.GetLastOpenedBy().ToNwObject();
+ public NwCreature? OpenedBy { get; }
///
/// Gets the that was opened.
///
- public NwPlaceable Placeable { get; } = NWScript.OBJECT_SELF.ToNwObject()!;
+ public NwPlaceable Placeable { get; }
NwObject IEvent.Context => Placeable;
+
+ public OnOpen()
+ {
+ Placeable = NWScript.OBJECT_SELF.ToNwObject()!;
+ OpenedBy = NWScript.GetLastOpenedBy(Placeable).ToNwObject();
+ }
}
}
}
diff --git a/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnUnlock.cs b/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnUnlock.cs
index 96dc1840f..104a384bf 100644
--- a/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnUnlock.cs
+++ b/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnUnlock.cs
@@ -18,14 +18,20 @@ public sealed class OnUnlock : IEvent
///
/// Gets the that was unlocked.
///
- public NwPlaceable Placeable { get; } = NWScript.OBJECT_SELF.ToNwObject()!;
+ public NwPlaceable Placeable { get; }
///
/// Gets the that unlocked .
///
- public NwCreature? UnlockedBy { get; } = NWScript.GetLastUnlocked().ToNwObject();
+ public NwCreature? UnlockedBy { get; }
NwObject IEvent.Context => Placeable;
+
+ public OnUnlock()
+ {
+ Placeable = NWScript.OBJECT_SELF.ToNwObject()!;
+ UnlockedBy = NWScript.GetLastUnlocked(Placeable).ToNwObject();
+ }
}
}
}
diff --git a/NWN.Anvil/src/main/API/Events/Game/StoreEvents/StoreEvents.OnClose.cs b/NWN.Anvil/src/main/API/Events/Game/StoreEvents/StoreEvents.OnClose.cs
index 866e4e548..9be5716af 100644
--- a/NWN.Anvil/src/main/API/Events/Game/StoreEvents/StoreEvents.OnClose.cs
+++ b/NWN.Anvil/src/main/API/Events/Game/StoreEvents/StoreEvents.OnClose.cs
@@ -15,14 +15,20 @@ public sealed class OnClose : IEvent
///
/// Gets the that last closed this .
///
- public NwCreature Creature { get; } = NWScript.GetLastClosedBy().ToNwObject()!;
+ public NwCreature Creature { get; }
///
/// Gets the being closed.
///
- public NwStore Store { get; } = NWScript.OBJECT_SELF.ToNwObject()!;
+ public NwStore Store { get; }
NwObject IEvent.Context => Store;
+
+ public OnClose()
+ {
+ Store = NWScript.OBJECT_SELF.ToNwObject()!;
+ Creature = NWScript.GetLastClosedBy(Store).ToNwObject()!;
+ }
}
}
}
diff --git a/NWN.Anvil/src/main/API/Events/Game/StoreEvents/StoreEvents.OnOpen.cs b/NWN.Anvil/src/main/API/Events/Game/StoreEvents/StoreEvents.OnOpen.cs
index d2fa7cc08..59fdbd51f 100644
--- a/NWN.Anvil/src/main/API/Events/Game/StoreEvents/StoreEvents.OnOpen.cs
+++ b/NWN.Anvil/src/main/API/Events/Game/StoreEvents/StoreEvents.OnOpen.cs
@@ -15,14 +15,20 @@ public sealed class OnOpen : IEvent
///
/// Gets the that last opened this store.
///
- public NwPlayer Player { get; } = NWScript.GetLastOpenedBy().ToNwPlayer()!;
+ public NwPlayer Player { get; }
///
/// Gets the being open.
///
- public NwStore Store { get; } = NWScript.OBJECT_SELF.ToNwObject()!;
+ public NwStore Store { get; }
NwObject IEvent.Context => Store;
+
+ public OnOpen()
+ {
+ Store = NWScript.OBJECT_SELF.ToNwObject()!;
+ Player = NWScript.GetLastOpenedBy(Store).ToNwPlayer()!;
+ }
}
}
}
diff --git a/NWN.Anvil/src/main/API/Objects/NwCreature.cs b/NWN.Anvil/src/main/API/Objects/NwCreature.cs
index b4f3907fa..0e3ab9db6 100644
--- a/NWN.Anvil/src/main/API/Objects/NwCreature.cs
+++ b/NWN.Anvil/src/main/API/Objects/NwCreature.cs
@@ -983,6 +983,17 @@ public async Task ActionCastFakeSpellAt(NwSpell spell, NwGameObject target, Proj
NWScript.ActionCastFakeSpellAtObject(spell.Id, target, (int)pathType);
}
+ ///
+ /// Instructs this creature to move and close the specified door.
+ ///
+ /// The door to close.
+ /// If true, the creature will run rather than walk.
+ public async Task ActionCloseDoor(NwDoor door, bool run = false)
+ {
+ await WaitForObjectContext();
+ NWScript.ActionCloseDoor(door, run.ToInt());
+ }
+
///
/// Intructs this creature to enter counterspell combat mode against the specified creature.
///
@@ -1154,6 +1165,17 @@ public async Task ActionMoveTo(NwObject target, bool run = false, float range =
NWScript.ActionMoveToObject(target, run.ToInt(), range);
}
+ ///
+ /// Instructs this creature to move and open the specified door.
+ ///
+ /// The door to open.
+ /// If true, the creature will run rather than walk.
+ public async Task ActionOpenDoor(NwDoor door, bool run = false)
+ {
+ await WaitForObjectContext();
+ NWScript.ActionOpenDoor(door, run.ToInt());
+ }
+
///
/// Instructs this creature to walk over and pick up the specified item on the ground.
///
@@ -1409,11 +1431,7 @@ public void BroadcastSkillRoll(int diceRoll, NwSkill skill, int modifier, int di
creature.BroadcastSkillData(data);
}
- ///
- /// Performs a spell resistance check between this creature, and the specified target object.
- ///
- /// The target of the spell.
- /// A result indicating if the spell was resisted by the target.
+ [Obsolete("Use SpellResistanceCheck instead.")]
public ResistSpellResult CheckResistSpell(NwGameObject target)
{
return (ResistSpellResult)NWScript.ResistSpell(this, target);
@@ -2565,6 +2583,61 @@ public async Task SpeakOneLinerConversation(string dialogResRef = "", NwGameObje
NWScript.SpeakOneLinerConversation(dialogResRef, tokenTarget);
}
+ ///
+ /// Performs a spell resistance check.
+ ///
+ /// The target creature of the spell.
+ /// The spell to use for the check. If null, will auto-detect based on the current running spell script.
+ /// The caster level for the spell. If null, will use the caster's level.
+ /// The spell resistance to penetrate. If null, will use the spell resistance of the target.
+ /// If true, will show feedback for the spell resistance roll.
+ /// True if the target successfully resisted the spell, otherwise false.
+ public bool SpellResistanceCheck(NwGameObject target, NwSpell? spell = null, int? casterLevel = null, int? spellResistance = null, bool feedback = true)
+ {
+ return NWScript.SpellResistanceCheck(target, this, spell?.Id ?? -1, casterLevel ?? -1, spellResistance ?? -1, feedback.ToInt()).ToBool();
+ }
+
+ ///
+ /// Performs a spell immunity check.
+ ///
+ /// The target creature of the spell.
+ /// The spell to use for the check. If null, will auto-detect based on the current running spell script.
+ /// If true, will show feedback for the spell immunity check.
+ /// True if the target is immune to the spell, otherwise false.
+ public bool SpellImmunityCheck(NwGameObject target, NwSpell? spell = null, bool feedback = true)
+ {
+ return NWScript.SpellImmunityCheck(target, this, spell?.Id ?? -1, feedback.ToInt()).ToBool();
+ }
+
+ ///
+ /// Performs a spell absorption check for limited spell absorption effects (e.g. Spell Mantle).
+ ///
+ /// The target creature of the spell.
+ /// The spell to use for the check. If null, will auto-detect based on the current running spell script.
+ /// The spell school to check for. If null, uses the default spell school from the spell parameter.
+ /// The spell level. If null, uses the spell level from the spell parameter using the creature's caster class.
+ /// If true, will remove the spell levels from the effect that would stop it, and remove the effect if 0 or fewer levels remain. If false, the effect is untouched.
+ /// If true, will show feedback for the spell absorption check.
+ /// True if the target successfully absorbed the spell, otherwise false.
+ public bool SpellAbsorptionLimitedCheck(NwGameObject target, NwSpell? spell = null, SpellSchool? spellSchool = null, int? spellLevel = null, bool removeLevels = true, bool feedback = true)
+ {
+ return NWScript.SpellAbsorptionLimitedCheck(target, this, spell?.Id ?? -1, (int?)spellSchool ?? -1, spellLevel ?? -1, removeLevels.ToInt(), feedback.ToInt()).ToBool();
+ }
+
+ ///
+ /// Performs a spell absorption check for unlimited spell absorption effects (e.g. Globe of invulnerability).
+ ///
+ /// The target creature of the spell.
+ /// The spell to use for the check. If null, will auto-detect based on the current running spell script.
+ /// The spell school to check for. If null, uses the default spell school from the spell parameter.
+ /// The spell level. If null, uses the spell level from the spell parameter using the creature's caster class.
+ /// If true, will show feedback for the spell absorption check.
+ /// True if the target successfully absorbed the spell, otherwise false.
+ public bool SpellAbsorptionUnlimitedCheck(NwGameObject target, NwSpell? spell = null, SpellSchool? spellSchool = null, int? spellLevel = null, bool feedback = true)
+ {
+ return NWScript.SpellAbsorptionUnlimitedCheck(target, this, spell?.Id ?? -1, (int?)spellSchool ?? -1, spellLevel ?? -1, feedback.ToInt()).ToBool();
+ }
+
///
/// Instructs this creature to summon their animal companion.
/// Does nothing if this creature has no animal companion available.
@@ -2616,10 +2689,10 @@ public Talent TalentRandom(TalentCategory category)
/// Attempts to perform a melee touch attack on target. This is not a creature action, and assumes that this creature is already within range of the target.
///
/// The target of this touch attack.
- public async Task TouchAttackMelee(NwGameObject target)
+ /// If true, displays combat feedback in the chat window.
+ public TouchAttackResult TouchAttackMelee(NwGameObject target, bool displayFeedback = true)
{
- await WaitForObjectContext();
- return (TouchAttackResult)NWScript.TouchAttackMelee(target);
+ return (TouchAttackResult)NWScript.TouchAttackMelee(target, displayFeedback.ToInt(), this);
}
///
@@ -2627,10 +2700,9 @@ public async Task TouchAttackMelee(NwGameObject target)
///
/// The target of this touch attack.
/// If true, displays combat feedback in the chat window.
- public async Task TouchAttackRanged(NwGameObject target, bool displayFeedback)
+ public TouchAttackResult TouchAttackRanged(NwGameObject target, bool displayFeedback)
{
- await WaitForObjectContext();
- return (TouchAttackResult)NWScript.TouchAttackRanged(target, displayFeedback.ToInt());
+ return (TouchAttackResult)NWScript.TouchAttackRanged(target, displayFeedback.ToInt(), this);
}
///
diff --git a/NWN.Anvil/src/main/API/Objects/NwGameObject.cs b/NWN.Anvil/src/main/API/Objects/NwGameObject.cs
index db9ef14ec..efe7a2fab 100644
--- a/NWN.Anvil/src/main/API/Objects/NwGameObject.cs
+++ b/NWN.Anvil/src/main/API/Objects/NwGameObject.cs
@@ -86,6 +86,33 @@ public int HP
set => GameObject.m_nCurrentHitPoints = value;
}
+ ///
+ /// Gets or sets if this object is destroyable.
+ ///
+ public bool IsDestroyable
+ {
+ get => GameObject.m_bDestroyable.ToBool();
+ set => GameObject.m_bDestroyable = value.ToInt();
+ }
+
+ ///
+ /// Gets or sets if this object is raiseable.
+ ///
+ public bool IsRaiseable
+ {
+ get => GameObject.m_bRaiseable.ToBool();
+ set => gameObject.m_bRaiseable = value.ToInt();
+ }
+
+ ///
+ /// Gets or sets if this object is selectable when dead.
+ ///
+ public bool IsSelectableWhenDead
+ {
+ get => GameObject.m_bSelectableWhenDead.ToBool();
+ set => gameObject.m_bSelectableWhenDead = value.ToInt();
+ }
+
///
/// Gets a value indicating whether this object is in a conversation.
///
@@ -382,19 +409,21 @@ public async Task EndConversation()
/// Rotates this object to face towards target.
///
/// The target object to face.
- public Task FaceToObject(NwGameObject target)
+ public void FaceToObject(NwGameObject target)
{
- return FaceToPoint(target.Position);
+ if (target.Area == Area)
+ {
+ FaceToPoint(target.Position);
+ }
}
///
/// Rotates this object to face a position.
///
/// The position to face towards.
- public virtual async Task FaceToPoint(Vector3 point)
+ public virtual void FaceToPoint(Vector3 point)
{
- await WaitForObjectContext();
- NWScript.SetFacingPoint(point);
+ NWScript.SetFacingPoint(point, this);
}
///
@@ -669,22 +698,15 @@ public void SetColor(ColorChannel colorChannel, int newColor)
/// Rotates this object to face the specified facing angle.
///
/// The angle to face.
- public async Task SetFacing(float facing)
+ public void SetFacing(float facing)
{
- await WaitForObjectContext();
- NWScript.SetFacing(facing);
+ NWScript.SetFacing(facing, this);
}
- ///
- /// Sets whether this object is destroyable.
- ///
- /// If false, this creature does not fade out on death, but sticks around as a corpse.
- /// If true, this creature can be raised via resurrection.
- /// If true, this creature is selectable after death.
- public async Task SetIsDestroyable(bool destroyable, bool raiseable = true, bool selectableWhenDead = false)
+ [Obsolete("Use the IsDestroyable/IsRaiseable/IsSelectableWhenDead properties instead.")]
+ public void SetIsDestroyable(bool destroyable, bool raiseable = true, bool selectableWhenDead = false)
{
- await WaitForObjectContext();
- NWScript.SetIsDestroyable(destroyable.ToInt(), raiseable.ToInt(), selectableWhenDead.ToInt());
+ NWScript.SetIsDestroyable(destroyable.ToInt(), raiseable.ToInt(), selectableWhenDead.ToInt(), this);
}
///
diff --git a/NWN.Anvil/src/main/API/Objects/NwObject.cs b/NWN.Anvil/src/main/API/Objects/NwObject.cs
index 66d9f48a8..def2d7c56 100644
--- a/NWN.Anvil/src/main/API/Objects/NwObject.cs
+++ b/NWN.Anvil/src/main/API/Objects/NwObject.cs
@@ -191,10 +191,9 @@ public async Task AddActionToQueue(System.Action action)
/// on a creature, which will stop the combat music and allow them to rest,
/// engage in dialog, or other actions that they would normally have to wait for.
///
- public async Task ClearActionQueue(bool clearCombatState = false)
+ public void ClearActionQueue(bool clearCombatState = false)
{
- await WaitForObjectContext();
- NWScript.ClearAllActions(clearCombatState.ToInt());
+ NWScript.ClearAllActions(clearCombatState.ToInt(), this);
}
///
diff --git a/NWN.Anvil/src/main/API/Objects/NwPlayer.cs b/NWN.Anvil/src/main/API/Objects/NwPlayer.cs
index f6a756767..983811096 100644
--- a/NWN.Anvil/src/main/API/Objects/NwPlayer.cs
+++ b/NWN.Anvil/src/main/API/Objects/NwPlayer.cs
@@ -247,6 +247,18 @@ public bool IsValid
///
public PlayerLanguage Language => (PlayerLanguage)NWScript.GetPlayerLanguage(ControlledCreature);
+ ///
+ /// Gets the last latency/"ping" result for this player. Results are updated every 6000 milliseconds.
+ /// Returns 0 for unsupported clients.
+ ///
+ public int Latency => NWScript.GetPlayerNetworkLatency(ControlledCreature, false.ToInt());
+
+ ///
+ /// Gets this player's smoothed/average latency. This is a moving calculation that may change in future game releases.
+ /// Returns 0 for unsupported clients.
+ ///
+ public int LatencyAverage => NWScript.GetPlayerNetworkLatency(ControlledCreature, true.ToInt());
+
///
/// Gets the original creature that this player logged in with.
///
@@ -559,6 +571,14 @@ public void BootPlayer(string reason = "")
NWScript.BootPC(ControlledCreature, reason);
}
+ ///
+ /// Cancels any cursor targeting mode the player is currently in.
+ ///
+ public void CancelTargetMode()
+ {
+ CursorTargetService.CancelTargetMode(this);
+ }
+
///
/// Clears looping visual effects on the specified object visible only to this player.
///
diff --git a/NWN.Anvil/src/main/API/Objects/NwStationary.cs b/NWN.Anvil/src/main/API/Objects/NwStationary.cs
index b666e81d8..4129a36e9 100644
--- a/NWN.Anvil/src/main/API/Objects/NwStationary.cs
+++ b/NWN.Anvil/src/main/API/Objects/NwStationary.cs
@@ -1,6 +1,5 @@
using System;
using System.Numerics;
-using System.Threading.Tasks;
using NWN.Core;
using NWN.Native.API;
@@ -71,6 +70,11 @@ public bool Locked
set => NWScript.SetLocked(this, value.ToInt());
}
+ ///
+ /// Gets the object that last locked this stationary object.
+ ///
+ public NwGameObject? LockedBy => NWScript.GetLastLocked(this).ToNwObject();
+
///
/// Gets or sets a value indicating whether a specific key with the tag is required to open this stationary object.
///
@@ -98,6 +102,11 @@ public int UnlockDC
set => NWScript.SetLockUnlockDC(this, value);
}
+ ///
+ /// Gets the object that last unlocked this stationary object.
+ ///
+ public NwGameObject? UnlockedBy => NWScript.GetLastUnlocked(this).ToNwObject();
+
///
/// Creates the specified trap.
///
@@ -109,19 +118,10 @@ public void CreateTrap(TrapBaseType trap, string disarm = "", string triggered =
NWScript.CreateTrapOnObject((int)trap, this, sOnDisarmScript: disarm, sOnTrapTriggeredScript: triggered);
}
- public override Task FaceToPoint(Vector3 point)
+ public override void FaceToPoint(Vector3 point)
{
Vector3 direction = Vector3.Normalize(point - Position);
- return base.FaceToPoint(Position - direction);
- }
-
- ///
- /// Gets the object that last locked this stationary object.
- ///
- public async Task GetLastLockedBy()
- {
- await WaitForObjectContext();
- return NWScript.GetLastLocked().ToNwObject();
+ base.FaceToPoint(Position - direction);
}
///
diff --git a/NWN.Anvil/src/main/Services/CursorTarget/CursorTargetService.cs b/NWN.Anvil/src/main/Services/CursorTarget/CursorTargetService.cs
index 6f23740b8..eb2409aad 100644
--- a/NWN.Anvil/src/main/Services/CursorTarget/CursorTargetService.cs
+++ b/NWN.Anvil/src/main/Services/CursorTarget/CursorTargetService.cs
@@ -41,6 +41,12 @@ internal void EnterTargetMode(NwPlayer player, Action