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