From ec86c64954af0c9cb25258cd660a508174fe0ab9 Mon Sep 17 00:00:00 2001 From: Jhett Black <10942655+jhett12321@users.noreply.github.com> Date: Mon, 13 Feb 2023 10:00:11 +1000 Subject: [PATCH 01/17] Update changelog. --- CHANGELOG.md | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d5671b31a..155f4b182 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,28 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## Unreleased -https://github.com/nwn-dotnet/Anvil/compare/v8193.34.23...HEAD +https://github.com/nwn-dotnet/Anvil/compare/v8193.34.24...HEAD + +### Added +- N/A + +### Package Updates +- N/A + +### Changed +- N/A + +### Deprecated +- N/A + +### Removed +- N/A + +### Fixed +- N/A + +## 8193.34.24 +https://github.com/nwn-dotnet/Anvil/compare/v8193.34.23...v8193.34.24 ### Added - Implemented AI message and listen system. @@ -18,16 +39,10 @@ https://github.com/nwn-dotnet/Anvil/compare/v8193.34.23...HEAD ### Package Updates - NWN.Core: 8193.34.10 -> 8193.34.12 -### Changed -- N/A - ### Deprecated - `DoorEvents.OnDialogue` - use `DoorEvents.OnConversation` instead. - `PlaceableEvents.OnDialogue` - use `PlaceableEvents.OnConversation` instead. -### Removed -- N/A - ### Fixed - Fixed an issue with events that caused script handlers not to be called after reloading anvil. From 87ffd17099916cb402afea88011ef341dd0f4d9e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Feb 2023 17:04:14 +1100 Subject: [PATCH 02/17] Bump NLog from 5.1.1 to 5.1.2 (#618) Bumps [NLog](https://github.com/NLog/NLog) from 5.1.1 to 5.1.2. - [Release notes](https://github.com/NLog/NLog/releases) - [Changelog](https://github.com/NLog/NLog/blob/dev/CHANGELOG.md) - [Commits](https://github.com/NLog/NLog/compare/v5.1.1...v5.1.2) --- updated-dependencies: - dependency-name: NLog dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- NWN.Anvil/NWN.Anvil.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NWN.Anvil/NWN.Anvil.csproj b/NWN.Anvil/NWN.Anvil.csproj index 8d12e571e..54518063a 100644 --- a/NWN.Anvil/NWN.Anvil.csproj +++ b/NWN.Anvil/NWN.Anvil.csproj @@ -59,7 +59,7 @@ - + From 0969bf03487079b02397e1f4b9363bccef9a51f9 Mon Sep 17 00:00:00 2001 From: Jhett Black <10942655+jhett12321@users.noreply.github.com> Date: Thu, 23 Feb 2023 17:04:29 +1100 Subject: [PATCH 03/17] Expose HomeStorage class. (#619) --- .../main/Services/Resources/HomeStorage.cs | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/NWN.Anvil/src/main/Services/Resources/HomeStorage.cs b/NWN.Anvil/src/main/Services/Resources/HomeStorage.cs index 14871737e..683165b53 100644 --- a/NWN.Anvil/src/main/Services/Resources/HomeStorage.cs +++ b/NWN.Anvil/src/main/Services/Resources/HomeStorage.cs @@ -4,18 +4,39 @@ namespace Anvil.Services { - internal static class HomeStorage + /// + /// Full directory paths for the anvil home directory. + /// + public static class HomeStorage { + /// + /// The base anvil home directory. + /// private static readonly string AnvilHome = Path.GetFullPath(EnvironmentConfig.AnvilHome, NwServer.Instance.UserDirectory); + /// + /// The NLog config directory. + /// public static string NLogConfig => ResolveFilePath("nlog.config"); + /// + /// The root folder for Paket configuration and package cache. + /// public static string Paket => ResolveDirectoryPath("Paket"); + /// + /// The root plugin data directory. Each plugin is assigned a unique directory in this folder. + /// public static string PluginData => ResolveDirectoryPath("PluginData"); + /// + /// The root plugin directory. Each plugin is assigned a unique directory in this folder. + /// public static string Plugins => ResolveDirectoryPath("Plugins"); + /// + /// The temporary resources directory. Used by some Anvil APIs for ephemeral resources to be visible by ResMan. + /// public static string ResourceTemp => ResolveDirectoryPath("ResourceTemp", false); private static void CreateDirectory(string folderPath) From 1cfb0a97c057bac25f97f0487bb212cac6a8b17d Mon Sep 17 00:00:00 2001 From: Jhett Black <10942655+jhett12321@users.noreply.github.com> Date: Fri, 24 Feb 2023 14:28:45 +1100 Subject: [PATCH 04/17] Hide internal async services. Update docs. (#620) * Hide internal async services. * Update test runner usage of main thread sync context. Expose anvil internals. * Update docs. Add awaiter interfaces with return type. * Add anvil dependencies to test runner. * Add sync context docs. --- .../NWN.Anvil.TestRunner.csproj | 5 +++++ .../src/main/TestRunnerService.cs | 10 +++++----- .../src/main/API/Async/Awaiters/IAwaitable.cs | 12 ++++++++++++ .../src/main/API/Async/Awaiters/IAwaiter.cs | 18 ++++++++++++++++-- .../Awaiters/SynchronizationContextAwaiter.cs | 5 ++++- .../Async/MainThreadSynchronizationContext.cs | 7 ++++++- NWN.Anvil/src/main/API/Async/NwTask.cs | 4 ++-- NWN.Anvil/src/main/Internal/AssemblyInfo.cs | 1 + 8 files changed, 51 insertions(+), 11 deletions(-) diff --git a/NWN.Anvil.TestRunner/NWN.Anvil.TestRunner.csproj b/NWN.Anvil.TestRunner/NWN.Anvil.TestRunner.csproj index 7f1630a1d..a747cad32 100644 --- a/NWN.Anvil.TestRunner/NWN.Anvil.TestRunner.csproj +++ b/NWN.Anvil.TestRunner/NWN.Anvil.TestRunner.csproj @@ -68,6 +68,11 @@ + + + + + diff --git a/NWN.Anvil.TestRunner/src/main/TestRunnerService.cs b/NWN.Anvil.TestRunner/src/main/TestRunnerService.cs index df4f406db..0de197f71 100644 --- a/NWN.Anvil.TestRunner/src/main/TestRunnerService.cs +++ b/NWN.Anvil.TestRunner/src/main/TestRunnerService.cs @@ -20,16 +20,16 @@ internal sealed class TestRunnerService { private static readonly Logger Log = LogManager.GetCurrentClassLogger(); - private readonly NwServer nwServer; + private readonly MainThreadSynchronizationContext mainThreadSynchronizationContext; private readonly Queue testAssemblyQueue = new Queue(); private readonly string outputDir; private Thread testWorkerThread; - public TestRunnerService(PluginStorageService pluginStorageService, NwServer nwServer) + public TestRunnerService(MainThreadSynchronizationContext mainThreadSynchronizationContext, PluginStorageService pluginStorageService) { - this.nwServer = nwServer; + this.mainThreadSynchronizationContext = mainThreadSynchronizationContext; outputDir = pluginStorageService.GetPluginStoragePath(typeof(TestRunnerService).Assembly); NwModule.Instance.OnModuleLoad += OnModuleLoad; @@ -49,7 +49,7 @@ private void PopulateTestQueue() private void OnModuleLoad(ModuleEvents.OnModuleLoad eventData) { - TestCommand.DefaultSynchronizationContext = NwTask.MainThreadSynchronizationContext; + TestCommand.DefaultSynchronizationContext = mainThreadSynchronizationContext; testWorkerThread = new Thread(RunTests); testWorkerThread.Start(); } @@ -72,7 +72,7 @@ private async void Shutdown() { testWorkerThread = null; await NwTask.SwitchToMainThread(); - nwServer.ShutdownServer(); + NwServer.Instance.ShutdownServer(); } private string[] GetRunnerArguments(Assembly assembly) diff --git a/NWN.Anvil/src/main/API/Async/Awaiters/IAwaitable.cs b/NWN.Anvil/src/main/API/Async/Awaiters/IAwaitable.cs index e7fbe9fc0..ea05eecdf 100644 --- a/NWN.Anvil/src/main/API/Async/Awaiters/IAwaitable.cs +++ b/NWN.Anvil/src/main/API/Async/Awaiters/IAwaitable.cs @@ -1,7 +1,19 @@ namespace Anvil.API { + /// + /// Explicit interface to support the compiling of async/await. + /// public interface IAwaitable { IAwaiter GetAwaiter(); } + + /// + /// Explicit interface to support the compiling of async/await. + /// + /// The awaiter return value type. + public interface IAwaitable + { + IAwaiter GetAwaiter(); + } } diff --git a/NWN.Anvil/src/main/API/Async/Awaiters/IAwaiter.cs b/NWN.Anvil/src/main/API/Async/Awaiters/IAwaiter.cs index f29d85dd8..2012fc122 100644 --- a/NWN.Anvil/src/main/API/Async/Awaiters/IAwaiter.cs +++ b/NWN.Anvil/src/main/API/Async/Awaiters/IAwaiter.cs @@ -2,10 +2,24 @@ namespace Anvil.API { + /// + /// Explicit interface for an awaiter. An awaiter is a system that will run an async operation and schedule a continuation when it completes. + /// public interface IAwaiter : INotifyCompletion { - public bool IsCompleted { get; } + bool IsCompleted { get; } - public void GetResult() {} + void GetResult(); + } + + /// + /// Explicit interface for an awaiter. An awaiter is a system that will run an async operation and schedule a continuation when it completes. + /// + /// The awaiter return value type. + public interface IAwaiter : INotifyCompletion + { + bool IsCompleted { get; } + + TResult GetResult(); } } diff --git a/NWN.Anvil/src/main/API/Async/Awaiters/SynchronizationContextAwaiter.cs b/NWN.Anvil/src/main/API/Async/Awaiters/SynchronizationContextAwaiter.cs index a538d7171..bfd3fb97e 100644 --- a/NWN.Anvil/src/main/API/Async/Awaiters/SynchronizationContextAwaiter.cs +++ b/NWN.Anvil/src/main/API/Async/Awaiters/SynchronizationContextAwaiter.cs @@ -2,7 +2,10 @@ namespace Anvil.API { - public readonly struct SynchronizationContextAwaiter : IAwaiter + /// + /// Awaits for the current async context to return to the specified context. + /// + internal readonly struct SynchronizationContextAwaiter : IAwaiter { private static readonly SendOrPostCallback PostCallback = state => ((System.Action?)state)?.Invoke(); diff --git a/NWN.Anvil/src/main/API/Async/MainThreadSynchronizationContext.cs b/NWN.Anvil/src/main/API/Async/MainThreadSynchronizationContext.cs index 02eba8e91..858335280 100644 --- a/NWN.Anvil/src/main/API/Async/MainThreadSynchronizationContext.cs +++ b/NWN.Anvil/src/main/API/Async/MainThreadSynchronizationContext.cs @@ -6,10 +6,15 @@ namespace Anvil.API { + /// + /// This synchronization context is invoked by the NWN server main loop.
+ /// Using , it allows async code to return to a valid script context by using await with this class.
+ /// Async tasks are queued into a list, before being flushed on the next loop update. + ///
[ServiceBinding(typeof(MainThreadSynchronizationContext))] [ServiceBinding(typeof(IUpdateable))] [ServiceBindingOptions(InternalBindingPriority.API)] - public sealed class MainThreadSynchronizationContext : SynchronizationContext, IUpdateable, IAwaitable + internal sealed class MainThreadSynchronizationContext : SynchronizationContext, IUpdateable, IAwaitable { private static readonly Logger Log = LogManager.GetCurrentClassLogger(); diff --git a/NWN.Anvil/src/main/API/Async/NwTask.cs b/NWN.Anvil/src/main/API/Async/NwTask.cs index 4fb242d69..c8c3c4954 100644 --- a/NWN.Anvil/src/main/API/Async/NwTask.cs +++ b/NWN.Anvil/src/main/API/Async/NwTask.cs @@ -13,14 +13,14 @@ namespace Anvil.API //! @import NwTaskExamples.cs /// - /// Awaiters for running NWN code in an async context. + /// Asynchronous tasks and helpers for running NWN APIs in an async context. /// public static class NwTask { private static readonly Logger Log = LogManager.GetCurrentClassLogger(); [Inject] - public static MainThreadSynchronizationContext MainThreadSynchronizationContext { get; private set; } = null!; + private static MainThreadSynchronizationContext MainThreadSynchronizationContext { get; set; } = null!; /// /// Waits until the specified amount of time has passed. diff --git a/NWN.Anvil/src/main/Internal/AssemblyInfo.cs b/NWN.Anvil/src/main/Internal/AssemblyInfo.cs index 937589e98..f5adfa574 100644 --- a/NWN.Anvil/src/main/Internal/AssemblyInfo.cs +++ b/NWN.Anvil/src/main/Internal/AssemblyInfo.cs @@ -1,6 +1,7 @@ using System.Reflection; using System.Runtime.CompilerServices; +[assembly: InternalsVisibleTo("NWN.Anvil.TestRunner")] [assembly: InternalsVisibleTo("NWN.Anvil.Tests")] namespace Anvil.Internal From 934d03d9efcac19ea3b7d468c5ac69c9339e2368 Mon Sep 17 00:00:00 2001 From: Jhett Black <10942655+jhett12321@users.noreply.github.com> Date: Fri, 24 Feb 2023 15:07:10 +1100 Subject: [PATCH 05/17] Update class paths (#621) * Move classes to plural form folders. * Add namespace ignore rule. --- NWN.Anvil.Tests/NWN.Anvil.Tests.csproj.DotSettings | 3 +++ .../{EngineStructure => EngineStructures}/EffectTests.cs | 0 .../src/main/API/{Object => Objects}/NwAreaTests.cs | 0 .../src/main/API/{Object => Objects}/NwCreatureTests.cs | 0 .../src/main/API/{Object => Objects}/NwDoorTests.cs | 0 .../src/main/API/{Object => Objects}/NwEncounterTests.cs | 0 .../src/main/API/{Object => Objects}/NwItemTests.cs | 0 .../src/main/API/{Object => Objects}/NwObjectTests.cs | 0 .../src/main/API/{Object => Objects}/NwPlaceableTests.cs | 0 .../src/main/API/{Object => Objects}/NwSoundTests.cs | 0 .../src/main/API/{Object => Objects}/NwStoreTests.cs | 0 .../src/main/API/{Object => Objects}/NwTriggerTests.cs | 0 .../src/main/API/{Object => Objects}/NwWaypointTests.cs | 0 .../main/API/{Variable => Variables}/LocalVariableTests.cs | 0 .../{Variable => Variables}/ObjectStorageVariableTests.cs | 0 NWN.Anvil/NWN.Anvil.csproj.DotSettings | 6 ++++++ .../AddPropertyPolicy.cs | 0 .../API/{EngineStructure => EngineStructures}/Cassowary.cs | 0 .../CassowaryException.cs | 0 .../CassowaryStrength.cs | 0 .../{EngineStructure => EngineStructures}/Effect.Create.cs | 0 .../API/{EngineStructure => EngineStructures}/Effect.cs | 0 .../API/{EngineStructure => EngineStructures}/EffectBase.cs | 0 .../{EngineStructure => EngineStructures}/EffectParams.cs | 0 .../EngineStructure.cs | 0 .../main/API/{EngineStructure => EngineStructures}/Event.cs | 0 .../API/{EngineStructure => EngineStructures}/HitEffect.cs | 0 .../ItemProperty.Create.cs | 0 .../{EngineStructure => EngineStructures}/ItemProperty.cs | 0 .../main/API/{EngineStructure => EngineStructures}/Json.cs | 0 .../API/{EngineStructure => EngineStructures}/Location.cs | 0 .../MonsterHitEffect.cs | 0 .../API/{EngineStructure => EngineStructures}/SQLQuery.cs | 0 .../API/{EngineStructure => EngineStructures}/SQLResult.cs | 0 .../{EngineStructure => EngineStructures}/Talent.Create.cs | 0 .../API/{EngineStructure => EngineStructures}/Talent.cs | 0 .../TalentExtensions.cs | 0 NWN.Anvil/src/main/API/{Object => Objects}/AreaFlags.cs | 0 .../src/main/API/{Object => Objects}/CreatureClassInfo.cs | 0 .../src/main/API/{Object => Objects}/CreatureLevelInfo.cs | 0 .../src/main/API/{Object => Objects}/CreatureTypeFilter.cs | 0 NWN.Anvil/src/main/API/{Object => Objects}/DayNightMode.cs | 0 .../src/main/API/{Object => Objects}/EncounterListEntry.cs | 0 .../src/main/API/{Object => Objects}/EncounterSpawnPoint.cs | 0 NWN.Anvil/src/main/API/{Object => Objects}/Inventory.cs | 0 .../src/main/API/{Object => Objects}/ItemAppearance.cs | 0 NWN.Anvil/src/main/API/{Object => Objects}/JournalEntry.cs | 0 .../src/main/API/{Object => Objects}/MemorizedSpellSlot.cs | 0 NWN.Anvil/src/main/API/{Object => Objects}/MovementType.cs | 0 .../API/{Object => Objects}/NativeObjectInfoAttribute.cs | 0 NWN.Anvil/src/main/API/{Object => Objects}/NwArea.cs | 0 .../src/main/API/{Object => Objects}/NwAreaOfEffect.cs | 0 NWN.Anvil/src/main/API/{Object => Objects}/NwCreature.cs | 0 NWN.Anvil/src/main/API/{Object => Objects}/NwDoor.cs | 0 NWN.Anvil/src/main/API/{Object => Objects}/NwEncounter.cs | 0 NWN.Anvil/src/main/API/{Object => Objects}/NwGameObject.cs | 0 NWN.Anvil/src/main/API/{Object => Objects}/NwItem.cs | 0 NWN.Anvil/src/main/API/{Object => Objects}/NwModule.cs | 0 .../src/main/API/{Object => Objects}/NwObject.Create.cs | 0 NWN.Anvil/src/main/API/{Object => Objects}/NwObject.cs | 0 NWN.Anvil/src/main/API/{Object => Objects}/NwPlaceable.cs | 0 NWN.Anvil/src/main/API/{Object => Objects}/NwPlayer.cs | 0 NWN.Anvil/src/main/API/{Object => Objects}/NwSound.cs | 0 NWN.Anvil/src/main/API/{Object => Objects}/NwStationary.cs | 0 NWN.Anvil/src/main/API/{Object => Objects}/NwStore.cs | 0 NWN.Anvil/src/main/API/{Object => Objects}/NwTrappable.cs | 0 NWN.Anvil/src/main/API/{Object => Objects}/NwTrigger.cs | 0 NWN.Anvil/src/main/API/{Object => Objects}/NwWaypoint.cs | 0 .../main/API/{Object => Objects}/PlayerQuickBarButton.cs | 0 .../src/main/API/{Object => Objects}/SpecialAbility.cs | 0 NWN.Anvil/src/main/API/{Object => Objects}/TileInfo.cs | 0 .../Campaign/CampaignVariableBool.cs | 0 .../Campaign/CampaignVariableEnum.cs | 0 .../Campaign/CampaignVariableFloat.cs | 0 .../Campaign/CampaignVariableGuid.cs | 0 .../{Variable => Variables}/Campaign/CampaignVariableInt.cs | 0 .../Campaign/CampaignVariableLocation.cs | 0 .../Campaign/CampaignVariableObject.cs | 0 .../Campaign/CampaignVariableString.cs | 0 .../Campaign/CampaignVariableVector.cs | 0 .../main/API/{Variable => Variables}/CampaignVariable.cs | 0 .../main/API/{Variable => Variables}/InternalVariables.cs | 0 .../main/API/{Variable => Variables}/Local/LocalVariable.cs | 0 .../API/{Variable => Variables}/Local/LocalVariableBool.cs | 0 .../{Variable => Variables}/Local/LocalVariableCassowary.cs | 0 .../API/{Variable => Variables}/Local/LocalVariableEnum.cs | 0 .../API/{Variable => Variables}/Local/LocalVariableFloat.cs | 0 .../API/{Variable => Variables}/Local/LocalVariableGuid.cs | 0 .../API/{Variable => Variables}/Local/LocalVariableInt.cs | 0 .../{Variable => Variables}/Local/LocalVariableLocation.cs | 0 .../{Variable => Variables}/Local/LocalVariableObject.cs | 0 .../{Variable => Variables}/Local/LocalVariableString.cs | 0 .../{Variable => Variables}/Local/LocalVariableStruct.cs | 0 .../ObjectStorage/ObjectStorageVariable.cs | 0 .../ObjectStorage/ObjectStorageVariableBool.cs | 0 .../ObjectStorage/ObjectStorageVariableEnum.cs | 0 .../ObjectStorage/ObjectStorageVariableFloat.cs | 0 .../ObjectStorage/ObjectStorageVariableGuid.cs | 0 .../ObjectStorage/ObjectStorageVariableInt.cs | 0 .../ObjectStorage/ObjectStorageVariableString.cs | 0 .../ObjectStorage/ObjectStorageVariableStruct.cs | 0 .../src/main/API/{Variable => Variables}/ObjectVariable.cs | 0 docs/NWN.Anvil.Samples.csproj.DotSettings | 1 + docs/samples/API/{ => Async}/NwTaskExamples.cs | 0 104 files changed, 10 insertions(+) rename NWN.Anvil.Tests/src/main/API/{EngineStructure => EngineStructures}/EffectTests.cs (100%) rename NWN.Anvil.Tests/src/main/API/{Object => Objects}/NwAreaTests.cs (100%) rename NWN.Anvil.Tests/src/main/API/{Object => Objects}/NwCreatureTests.cs (100%) rename NWN.Anvil.Tests/src/main/API/{Object => Objects}/NwDoorTests.cs (100%) rename NWN.Anvil.Tests/src/main/API/{Object => Objects}/NwEncounterTests.cs (100%) rename NWN.Anvil.Tests/src/main/API/{Object => Objects}/NwItemTests.cs (100%) rename NWN.Anvil.Tests/src/main/API/{Object => Objects}/NwObjectTests.cs (100%) rename NWN.Anvil.Tests/src/main/API/{Object => Objects}/NwPlaceableTests.cs (100%) rename NWN.Anvil.Tests/src/main/API/{Object => Objects}/NwSoundTests.cs (100%) rename NWN.Anvil.Tests/src/main/API/{Object => Objects}/NwStoreTests.cs (100%) rename NWN.Anvil.Tests/src/main/API/{Object => Objects}/NwTriggerTests.cs (100%) rename NWN.Anvil.Tests/src/main/API/{Object => Objects}/NwWaypointTests.cs (100%) rename NWN.Anvil.Tests/src/main/API/{Variable => Variables}/LocalVariableTests.cs (100%) rename NWN.Anvil.Tests/src/main/API/{Variable => Variables}/ObjectStorageVariableTests.cs (100%) rename NWN.Anvil/src/main/API/{EngineStructure => EngineStructures}/AddPropertyPolicy.cs (100%) rename NWN.Anvil/src/main/API/{EngineStructure => EngineStructures}/Cassowary.cs (100%) rename NWN.Anvil/src/main/API/{EngineStructure => EngineStructures}/CassowaryException.cs (100%) rename NWN.Anvil/src/main/API/{EngineStructure => EngineStructures}/CassowaryStrength.cs (100%) rename NWN.Anvil/src/main/API/{EngineStructure => EngineStructures}/Effect.Create.cs (100%) rename NWN.Anvil/src/main/API/{EngineStructure => EngineStructures}/Effect.cs (100%) rename NWN.Anvil/src/main/API/{EngineStructure => EngineStructures}/EffectBase.cs (100%) rename NWN.Anvil/src/main/API/{EngineStructure => EngineStructures}/EffectParams.cs (100%) rename NWN.Anvil/src/main/API/{EngineStructure => EngineStructures}/EngineStructure.cs (100%) rename NWN.Anvil/src/main/API/{EngineStructure => EngineStructures}/Event.cs (100%) rename NWN.Anvil/src/main/API/{EngineStructure => EngineStructures}/HitEffect.cs (100%) rename NWN.Anvil/src/main/API/{EngineStructure => EngineStructures}/ItemProperty.Create.cs (100%) rename NWN.Anvil/src/main/API/{EngineStructure => EngineStructures}/ItemProperty.cs (100%) rename NWN.Anvil/src/main/API/{EngineStructure => EngineStructures}/Json.cs (100%) rename NWN.Anvil/src/main/API/{EngineStructure => EngineStructures}/Location.cs (100%) rename NWN.Anvil/src/main/API/{EngineStructure => EngineStructures}/MonsterHitEffect.cs (100%) rename NWN.Anvil/src/main/API/{EngineStructure => EngineStructures}/SQLQuery.cs (100%) rename NWN.Anvil/src/main/API/{EngineStructure => EngineStructures}/SQLResult.cs (100%) rename NWN.Anvil/src/main/API/{EngineStructure => EngineStructures}/Talent.Create.cs (100%) rename NWN.Anvil/src/main/API/{EngineStructure => EngineStructures}/Talent.cs (100%) rename NWN.Anvil/src/main/API/{EngineStructure => EngineStructures}/TalentExtensions.cs (100%) rename NWN.Anvil/src/main/API/{Object => Objects}/AreaFlags.cs (100%) rename NWN.Anvil/src/main/API/{Object => Objects}/CreatureClassInfo.cs (100%) rename NWN.Anvil/src/main/API/{Object => Objects}/CreatureLevelInfo.cs (100%) rename NWN.Anvil/src/main/API/{Object => Objects}/CreatureTypeFilter.cs (100%) rename NWN.Anvil/src/main/API/{Object => Objects}/DayNightMode.cs (100%) rename NWN.Anvil/src/main/API/{Object => Objects}/EncounterListEntry.cs (100%) rename NWN.Anvil/src/main/API/{Object => Objects}/EncounterSpawnPoint.cs (100%) rename NWN.Anvil/src/main/API/{Object => Objects}/Inventory.cs (100%) rename NWN.Anvil/src/main/API/{Object => Objects}/ItemAppearance.cs (100%) rename NWN.Anvil/src/main/API/{Object => Objects}/JournalEntry.cs (100%) rename NWN.Anvil/src/main/API/{Object => Objects}/MemorizedSpellSlot.cs (100%) rename NWN.Anvil/src/main/API/{Object => Objects}/MovementType.cs (100%) rename NWN.Anvil/src/main/API/{Object => Objects}/NativeObjectInfoAttribute.cs (100%) rename NWN.Anvil/src/main/API/{Object => Objects}/NwArea.cs (100%) rename NWN.Anvil/src/main/API/{Object => Objects}/NwAreaOfEffect.cs (100%) rename NWN.Anvil/src/main/API/{Object => Objects}/NwCreature.cs (100%) rename NWN.Anvil/src/main/API/{Object => Objects}/NwDoor.cs (100%) rename NWN.Anvil/src/main/API/{Object => Objects}/NwEncounter.cs (100%) rename NWN.Anvil/src/main/API/{Object => Objects}/NwGameObject.cs (100%) rename NWN.Anvil/src/main/API/{Object => Objects}/NwItem.cs (100%) rename NWN.Anvil/src/main/API/{Object => Objects}/NwModule.cs (100%) rename NWN.Anvil/src/main/API/{Object => Objects}/NwObject.Create.cs (100%) rename NWN.Anvil/src/main/API/{Object => Objects}/NwObject.cs (100%) rename NWN.Anvil/src/main/API/{Object => Objects}/NwPlaceable.cs (100%) rename NWN.Anvil/src/main/API/{Object => Objects}/NwPlayer.cs (100%) rename NWN.Anvil/src/main/API/{Object => Objects}/NwSound.cs (100%) rename NWN.Anvil/src/main/API/{Object => Objects}/NwStationary.cs (100%) rename NWN.Anvil/src/main/API/{Object => Objects}/NwStore.cs (100%) rename NWN.Anvil/src/main/API/{Object => Objects}/NwTrappable.cs (100%) rename NWN.Anvil/src/main/API/{Object => Objects}/NwTrigger.cs (100%) rename NWN.Anvil/src/main/API/{Object => Objects}/NwWaypoint.cs (100%) rename NWN.Anvil/src/main/API/{Object => Objects}/PlayerQuickBarButton.cs (100%) rename NWN.Anvil/src/main/API/{Object => Objects}/SpecialAbility.cs (100%) rename NWN.Anvil/src/main/API/{Object => Objects}/TileInfo.cs (100%) rename NWN.Anvil/src/main/API/{Variable => Variables}/Campaign/CampaignVariableBool.cs (100%) rename NWN.Anvil/src/main/API/{Variable => Variables}/Campaign/CampaignVariableEnum.cs (100%) rename NWN.Anvil/src/main/API/{Variable => Variables}/Campaign/CampaignVariableFloat.cs (100%) rename NWN.Anvil/src/main/API/{Variable => Variables}/Campaign/CampaignVariableGuid.cs (100%) rename NWN.Anvil/src/main/API/{Variable => Variables}/Campaign/CampaignVariableInt.cs (100%) rename NWN.Anvil/src/main/API/{Variable => Variables}/Campaign/CampaignVariableLocation.cs (100%) rename NWN.Anvil/src/main/API/{Variable => Variables}/Campaign/CampaignVariableObject.cs (100%) rename NWN.Anvil/src/main/API/{Variable => Variables}/Campaign/CampaignVariableString.cs (100%) rename NWN.Anvil/src/main/API/{Variable => Variables}/Campaign/CampaignVariableVector.cs (100%) rename NWN.Anvil/src/main/API/{Variable => Variables}/CampaignVariable.cs (100%) rename NWN.Anvil/src/main/API/{Variable => Variables}/InternalVariables.cs (100%) rename NWN.Anvil/src/main/API/{Variable => Variables}/Local/LocalVariable.cs (100%) rename NWN.Anvil/src/main/API/{Variable => Variables}/Local/LocalVariableBool.cs (100%) rename NWN.Anvil/src/main/API/{Variable => Variables}/Local/LocalVariableCassowary.cs (100%) rename NWN.Anvil/src/main/API/{Variable => Variables}/Local/LocalVariableEnum.cs (100%) rename NWN.Anvil/src/main/API/{Variable => Variables}/Local/LocalVariableFloat.cs (100%) rename NWN.Anvil/src/main/API/{Variable => Variables}/Local/LocalVariableGuid.cs (100%) rename NWN.Anvil/src/main/API/{Variable => Variables}/Local/LocalVariableInt.cs (100%) rename NWN.Anvil/src/main/API/{Variable => Variables}/Local/LocalVariableLocation.cs (100%) rename NWN.Anvil/src/main/API/{Variable => Variables}/Local/LocalVariableObject.cs (100%) rename NWN.Anvil/src/main/API/{Variable => Variables}/Local/LocalVariableString.cs (100%) rename NWN.Anvil/src/main/API/{Variable => Variables}/Local/LocalVariableStruct.cs (100%) rename NWN.Anvil/src/main/API/{Variable => Variables}/ObjectStorage/ObjectStorageVariable.cs (100%) rename NWN.Anvil/src/main/API/{Variable => Variables}/ObjectStorage/ObjectStorageVariableBool.cs (100%) rename NWN.Anvil/src/main/API/{Variable => Variables}/ObjectStorage/ObjectStorageVariableEnum.cs (100%) rename NWN.Anvil/src/main/API/{Variable => Variables}/ObjectStorage/ObjectStorageVariableFloat.cs (100%) rename NWN.Anvil/src/main/API/{Variable => Variables}/ObjectStorage/ObjectStorageVariableGuid.cs (100%) rename NWN.Anvil/src/main/API/{Variable => Variables}/ObjectStorage/ObjectStorageVariableInt.cs (100%) rename NWN.Anvil/src/main/API/{Variable => Variables}/ObjectStorage/ObjectStorageVariableString.cs (100%) rename NWN.Anvil/src/main/API/{Variable => Variables}/ObjectStorage/ObjectStorageVariableStruct.cs (100%) rename NWN.Anvil/src/main/API/{Variable => Variables}/ObjectVariable.cs (100%) rename docs/samples/API/{ => Async}/NwTaskExamples.cs (100%) diff --git a/NWN.Anvil.Tests/NWN.Anvil.Tests.csproj.DotSettings b/NWN.Anvil.Tests/NWN.Anvil.Tests.csproj.DotSettings index 84b6462c4..399050c3f 100644 --- a/NWN.Anvil.Tests/NWN.Anvil.Tests.csproj.DotSettings +++ b/NWN.Anvil.Tests/NWN.Anvil.Tests.csproj.DotSettings @@ -2,15 +2,18 @@ True True True + True True True True True True + True True True True True + True True True True \ No newline at end of file diff --git a/NWN.Anvil.Tests/src/main/API/EngineStructure/EffectTests.cs b/NWN.Anvil.Tests/src/main/API/EngineStructures/EffectTests.cs similarity index 100% rename from NWN.Anvil.Tests/src/main/API/EngineStructure/EffectTests.cs rename to NWN.Anvil.Tests/src/main/API/EngineStructures/EffectTests.cs diff --git a/NWN.Anvil.Tests/src/main/API/Object/NwAreaTests.cs b/NWN.Anvil.Tests/src/main/API/Objects/NwAreaTests.cs similarity index 100% rename from NWN.Anvil.Tests/src/main/API/Object/NwAreaTests.cs rename to NWN.Anvil.Tests/src/main/API/Objects/NwAreaTests.cs diff --git a/NWN.Anvil.Tests/src/main/API/Object/NwCreatureTests.cs b/NWN.Anvil.Tests/src/main/API/Objects/NwCreatureTests.cs similarity index 100% rename from NWN.Anvil.Tests/src/main/API/Object/NwCreatureTests.cs rename to NWN.Anvil.Tests/src/main/API/Objects/NwCreatureTests.cs diff --git a/NWN.Anvil.Tests/src/main/API/Object/NwDoorTests.cs b/NWN.Anvil.Tests/src/main/API/Objects/NwDoorTests.cs similarity index 100% rename from NWN.Anvil.Tests/src/main/API/Object/NwDoorTests.cs rename to NWN.Anvil.Tests/src/main/API/Objects/NwDoorTests.cs diff --git a/NWN.Anvil.Tests/src/main/API/Object/NwEncounterTests.cs b/NWN.Anvil.Tests/src/main/API/Objects/NwEncounterTests.cs similarity index 100% rename from NWN.Anvil.Tests/src/main/API/Object/NwEncounterTests.cs rename to NWN.Anvil.Tests/src/main/API/Objects/NwEncounterTests.cs diff --git a/NWN.Anvil.Tests/src/main/API/Object/NwItemTests.cs b/NWN.Anvil.Tests/src/main/API/Objects/NwItemTests.cs similarity index 100% rename from NWN.Anvil.Tests/src/main/API/Object/NwItemTests.cs rename to NWN.Anvil.Tests/src/main/API/Objects/NwItemTests.cs diff --git a/NWN.Anvil.Tests/src/main/API/Object/NwObjectTests.cs b/NWN.Anvil.Tests/src/main/API/Objects/NwObjectTests.cs similarity index 100% rename from NWN.Anvil.Tests/src/main/API/Object/NwObjectTests.cs rename to NWN.Anvil.Tests/src/main/API/Objects/NwObjectTests.cs diff --git a/NWN.Anvil.Tests/src/main/API/Object/NwPlaceableTests.cs b/NWN.Anvil.Tests/src/main/API/Objects/NwPlaceableTests.cs similarity index 100% rename from NWN.Anvil.Tests/src/main/API/Object/NwPlaceableTests.cs rename to NWN.Anvil.Tests/src/main/API/Objects/NwPlaceableTests.cs diff --git a/NWN.Anvil.Tests/src/main/API/Object/NwSoundTests.cs b/NWN.Anvil.Tests/src/main/API/Objects/NwSoundTests.cs similarity index 100% rename from NWN.Anvil.Tests/src/main/API/Object/NwSoundTests.cs rename to NWN.Anvil.Tests/src/main/API/Objects/NwSoundTests.cs diff --git a/NWN.Anvil.Tests/src/main/API/Object/NwStoreTests.cs b/NWN.Anvil.Tests/src/main/API/Objects/NwStoreTests.cs similarity index 100% rename from NWN.Anvil.Tests/src/main/API/Object/NwStoreTests.cs rename to NWN.Anvil.Tests/src/main/API/Objects/NwStoreTests.cs diff --git a/NWN.Anvil.Tests/src/main/API/Object/NwTriggerTests.cs b/NWN.Anvil.Tests/src/main/API/Objects/NwTriggerTests.cs similarity index 100% rename from NWN.Anvil.Tests/src/main/API/Object/NwTriggerTests.cs rename to NWN.Anvil.Tests/src/main/API/Objects/NwTriggerTests.cs diff --git a/NWN.Anvil.Tests/src/main/API/Object/NwWaypointTests.cs b/NWN.Anvil.Tests/src/main/API/Objects/NwWaypointTests.cs similarity index 100% rename from NWN.Anvil.Tests/src/main/API/Object/NwWaypointTests.cs rename to NWN.Anvil.Tests/src/main/API/Objects/NwWaypointTests.cs diff --git a/NWN.Anvil.Tests/src/main/API/Variable/LocalVariableTests.cs b/NWN.Anvil.Tests/src/main/API/Variables/LocalVariableTests.cs similarity index 100% rename from NWN.Anvil.Tests/src/main/API/Variable/LocalVariableTests.cs rename to NWN.Anvil.Tests/src/main/API/Variables/LocalVariableTests.cs diff --git a/NWN.Anvil.Tests/src/main/API/Variable/ObjectStorageVariableTests.cs b/NWN.Anvil.Tests/src/main/API/Variables/ObjectStorageVariableTests.cs similarity index 100% rename from NWN.Anvil.Tests/src/main/API/Variable/ObjectStorageVariableTests.cs rename to NWN.Anvil.Tests/src/main/API/Variables/ObjectStorageVariableTests.cs diff --git a/NWN.Anvil/NWN.Anvil.csproj.DotSettings b/NWN.Anvil/NWN.Anvil.csproj.DotSettings index 289365608..6f84a47ab 100644 --- a/NWN.Anvil/NWN.Anvil.csproj.DotSettings +++ b/NWN.Anvil/NWN.Anvil.csproj.DotSettings @@ -4,6 +4,7 @@ True True True + True True True True @@ -25,6 +26,7 @@ True True True + True True True True @@ -39,6 +41,10 @@ True True True + True + True + True + True True True True diff --git a/NWN.Anvil/src/main/API/EngineStructure/AddPropertyPolicy.cs b/NWN.Anvil/src/main/API/EngineStructures/AddPropertyPolicy.cs similarity index 100% rename from NWN.Anvil/src/main/API/EngineStructure/AddPropertyPolicy.cs rename to NWN.Anvil/src/main/API/EngineStructures/AddPropertyPolicy.cs diff --git a/NWN.Anvil/src/main/API/EngineStructure/Cassowary.cs b/NWN.Anvil/src/main/API/EngineStructures/Cassowary.cs similarity index 100% rename from NWN.Anvil/src/main/API/EngineStructure/Cassowary.cs rename to NWN.Anvil/src/main/API/EngineStructures/Cassowary.cs diff --git a/NWN.Anvil/src/main/API/EngineStructure/CassowaryException.cs b/NWN.Anvil/src/main/API/EngineStructures/CassowaryException.cs similarity index 100% rename from NWN.Anvil/src/main/API/EngineStructure/CassowaryException.cs rename to NWN.Anvil/src/main/API/EngineStructures/CassowaryException.cs diff --git a/NWN.Anvil/src/main/API/EngineStructure/CassowaryStrength.cs b/NWN.Anvil/src/main/API/EngineStructures/CassowaryStrength.cs similarity index 100% rename from NWN.Anvil/src/main/API/EngineStructure/CassowaryStrength.cs rename to NWN.Anvil/src/main/API/EngineStructures/CassowaryStrength.cs diff --git a/NWN.Anvil/src/main/API/EngineStructure/Effect.Create.cs b/NWN.Anvil/src/main/API/EngineStructures/Effect.Create.cs similarity index 100% rename from NWN.Anvil/src/main/API/EngineStructure/Effect.Create.cs rename to NWN.Anvil/src/main/API/EngineStructures/Effect.Create.cs diff --git a/NWN.Anvil/src/main/API/EngineStructure/Effect.cs b/NWN.Anvil/src/main/API/EngineStructures/Effect.cs similarity index 100% rename from NWN.Anvil/src/main/API/EngineStructure/Effect.cs rename to NWN.Anvil/src/main/API/EngineStructures/Effect.cs diff --git a/NWN.Anvil/src/main/API/EngineStructure/EffectBase.cs b/NWN.Anvil/src/main/API/EngineStructures/EffectBase.cs similarity index 100% rename from NWN.Anvil/src/main/API/EngineStructure/EffectBase.cs rename to NWN.Anvil/src/main/API/EngineStructures/EffectBase.cs diff --git a/NWN.Anvil/src/main/API/EngineStructure/EffectParams.cs b/NWN.Anvil/src/main/API/EngineStructures/EffectParams.cs similarity index 100% rename from NWN.Anvil/src/main/API/EngineStructure/EffectParams.cs rename to NWN.Anvil/src/main/API/EngineStructures/EffectParams.cs diff --git a/NWN.Anvil/src/main/API/EngineStructure/EngineStructure.cs b/NWN.Anvil/src/main/API/EngineStructures/EngineStructure.cs similarity index 100% rename from NWN.Anvil/src/main/API/EngineStructure/EngineStructure.cs rename to NWN.Anvil/src/main/API/EngineStructures/EngineStructure.cs diff --git a/NWN.Anvil/src/main/API/EngineStructure/Event.cs b/NWN.Anvil/src/main/API/EngineStructures/Event.cs similarity index 100% rename from NWN.Anvil/src/main/API/EngineStructure/Event.cs rename to NWN.Anvil/src/main/API/EngineStructures/Event.cs diff --git a/NWN.Anvil/src/main/API/EngineStructure/HitEffect.cs b/NWN.Anvil/src/main/API/EngineStructures/HitEffect.cs similarity index 100% rename from NWN.Anvil/src/main/API/EngineStructure/HitEffect.cs rename to NWN.Anvil/src/main/API/EngineStructures/HitEffect.cs diff --git a/NWN.Anvil/src/main/API/EngineStructure/ItemProperty.Create.cs b/NWN.Anvil/src/main/API/EngineStructures/ItemProperty.Create.cs similarity index 100% rename from NWN.Anvil/src/main/API/EngineStructure/ItemProperty.Create.cs rename to NWN.Anvil/src/main/API/EngineStructures/ItemProperty.Create.cs diff --git a/NWN.Anvil/src/main/API/EngineStructure/ItemProperty.cs b/NWN.Anvil/src/main/API/EngineStructures/ItemProperty.cs similarity index 100% rename from NWN.Anvil/src/main/API/EngineStructure/ItemProperty.cs rename to NWN.Anvil/src/main/API/EngineStructures/ItemProperty.cs diff --git a/NWN.Anvil/src/main/API/EngineStructure/Json.cs b/NWN.Anvil/src/main/API/EngineStructures/Json.cs similarity index 100% rename from NWN.Anvil/src/main/API/EngineStructure/Json.cs rename to NWN.Anvil/src/main/API/EngineStructures/Json.cs diff --git a/NWN.Anvil/src/main/API/EngineStructure/Location.cs b/NWN.Anvil/src/main/API/EngineStructures/Location.cs similarity index 100% rename from NWN.Anvil/src/main/API/EngineStructure/Location.cs rename to NWN.Anvil/src/main/API/EngineStructures/Location.cs diff --git a/NWN.Anvil/src/main/API/EngineStructure/MonsterHitEffect.cs b/NWN.Anvil/src/main/API/EngineStructures/MonsterHitEffect.cs similarity index 100% rename from NWN.Anvil/src/main/API/EngineStructure/MonsterHitEffect.cs rename to NWN.Anvil/src/main/API/EngineStructures/MonsterHitEffect.cs diff --git a/NWN.Anvil/src/main/API/EngineStructure/SQLQuery.cs b/NWN.Anvil/src/main/API/EngineStructures/SQLQuery.cs similarity index 100% rename from NWN.Anvil/src/main/API/EngineStructure/SQLQuery.cs rename to NWN.Anvil/src/main/API/EngineStructures/SQLQuery.cs diff --git a/NWN.Anvil/src/main/API/EngineStructure/SQLResult.cs b/NWN.Anvil/src/main/API/EngineStructures/SQLResult.cs similarity index 100% rename from NWN.Anvil/src/main/API/EngineStructure/SQLResult.cs rename to NWN.Anvil/src/main/API/EngineStructures/SQLResult.cs diff --git a/NWN.Anvil/src/main/API/EngineStructure/Talent.Create.cs b/NWN.Anvil/src/main/API/EngineStructures/Talent.Create.cs similarity index 100% rename from NWN.Anvil/src/main/API/EngineStructure/Talent.Create.cs rename to NWN.Anvil/src/main/API/EngineStructures/Talent.Create.cs diff --git a/NWN.Anvil/src/main/API/EngineStructure/Talent.cs b/NWN.Anvil/src/main/API/EngineStructures/Talent.cs similarity index 100% rename from NWN.Anvil/src/main/API/EngineStructure/Talent.cs rename to NWN.Anvil/src/main/API/EngineStructures/Talent.cs diff --git a/NWN.Anvil/src/main/API/EngineStructure/TalentExtensions.cs b/NWN.Anvil/src/main/API/EngineStructures/TalentExtensions.cs similarity index 100% rename from NWN.Anvil/src/main/API/EngineStructure/TalentExtensions.cs rename to NWN.Anvil/src/main/API/EngineStructures/TalentExtensions.cs diff --git a/NWN.Anvil/src/main/API/Object/AreaFlags.cs b/NWN.Anvil/src/main/API/Objects/AreaFlags.cs similarity index 100% rename from NWN.Anvil/src/main/API/Object/AreaFlags.cs rename to NWN.Anvil/src/main/API/Objects/AreaFlags.cs diff --git a/NWN.Anvil/src/main/API/Object/CreatureClassInfo.cs b/NWN.Anvil/src/main/API/Objects/CreatureClassInfo.cs similarity index 100% rename from NWN.Anvil/src/main/API/Object/CreatureClassInfo.cs rename to NWN.Anvil/src/main/API/Objects/CreatureClassInfo.cs diff --git a/NWN.Anvil/src/main/API/Object/CreatureLevelInfo.cs b/NWN.Anvil/src/main/API/Objects/CreatureLevelInfo.cs similarity index 100% rename from NWN.Anvil/src/main/API/Object/CreatureLevelInfo.cs rename to NWN.Anvil/src/main/API/Objects/CreatureLevelInfo.cs diff --git a/NWN.Anvil/src/main/API/Object/CreatureTypeFilter.cs b/NWN.Anvil/src/main/API/Objects/CreatureTypeFilter.cs similarity index 100% rename from NWN.Anvil/src/main/API/Object/CreatureTypeFilter.cs rename to NWN.Anvil/src/main/API/Objects/CreatureTypeFilter.cs diff --git a/NWN.Anvil/src/main/API/Object/DayNightMode.cs b/NWN.Anvil/src/main/API/Objects/DayNightMode.cs similarity index 100% rename from NWN.Anvil/src/main/API/Object/DayNightMode.cs rename to NWN.Anvil/src/main/API/Objects/DayNightMode.cs diff --git a/NWN.Anvil/src/main/API/Object/EncounterListEntry.cs b/NWN.Anvil/src/main/API/Objects/EncounterListEntry.cs similarity index 100% rename from NWN.Anvil/src/main/API/Object/EncounterListEntry.cs rename to NWN.Anvil/src/main/API/Objects/EncounterListEntry.cs diff --git a/NWN.Anvil/src/main/API/Object/EncounterSpawnPoint.cs b/NWN.Anvil/src/main/API/Objects/EncounterSpawnPoint.cs similarity index 100% rename from NWN.Anvil/src/main/API/Object/EncounterSpawnPoint.cs rename to NWN.Anvil/src/main/API/Objects/EncounterSpawnPoint.cs diff --git a/NWN.Anvil/src/main/API/Object/Inventory.cs b/NWN.Anvil/src/main/API/Objects/Inventory.cs similarity index 100% rename from NWN.Anvil/src/main/API/Object/Inventory.cs rename to NWN.Anvil/src/main/API/Objects/Inventory.cs diff --git a/NWN.Anvil/src/main/API/Object/ItemAppearance.cs b/NWN.Anvil/src/main/API/Objects/ItemAppearance.cs similarity index 100% rename from NWN.Anvil/src/main/API/Object/ItemAppearance.cs rename to NWN.Anvil/src/main/API/Objects/ItemAppearance.cs diff --git a/NWN.Anvil/src/main/API/Object/JournalEntry.cs b/NWN.Anvil/src/main/API/Objects/JournalEntry.cs similarity index 100% rename from NWN.Anvil/src/main/API/Object/JournalEntry.cs rename to NWN.Anvil/src/main/API/Objects/JournalEntry.cs diff --git a/NWN.Anvil/src/main/API/Object/MemorizedSpellSlot.cs b/NWN.Anvil/src/main/API/Objects/MemorizedSpellSlot.cs similarity index 100% rename from NWN.Anvil/src/main/API/Object/MemorizedSpellSlot.cs rename to NWN.Anvil/src/main/API/Objects/MemorizedSpellSlot.cs diff --git a/NWN.Anvil/src/main/API/Object/MovementType.cs b/NWN.Anvil/src/main/API/Objects/MovementType.cs similarity index 100% rename from NWN.Anvil/src/main/API/Object/MovementType.cs rename to NWN.Anvil/src/main/API/Objects/MovementType.cs diff --git a/NWN.Anvil/src/main/API/Object/NativeObjectInfoAttribute.cs b/NWN.Anvil/src/main/API/Objects/NativeObjectInfoAttribute.cs similarity index 100% rename from NWN.Anvil/src/main/API/Object/NativeObjectInfoAttribute.cs rename to NWN.Anvil/src/main/API/Objects/NativeObjectInfoAttribute.cs diff --git a/NWN.Anvil/src/main/API/Object/NwArea.cs b/NWN.Anvil/src/main/API/Objects/NwArea.cs similarity index 100% rename from NWN.Anvil/src/main/API/Object/NwArea.cs rename to NWN.Anvil/src/main/API/Objects/NwArea.cs diff --git a/NWN.Anvil/src/main/API/Object/NwAreaOfEffect.cs b/NWN.Anvil/src/main/API/Objects/NwAreaOfEffect.cs similarity index 100% rename from NWN.Anvil/src/main/API/Object/NwAreaOfEffect.cs rename to NWN.Anvil/src/main/API/Objects/NwAreaOfEffect.cs diff --git a/NWN.Anvil/src/main/API/Object/NwCreature.cs b/NWN.Anvil/src/main/API/Objects/NwCreature.cs similarity index 100% rename from NWN.Anvil/src/main/API/Object/NwCreature.cs rename to NWN.Anvil/src/main/API/Objects/NwCreature.cs diff --git a/NWN.Anvil/src/main/API/Object/NwDoor.cs b/NWN.Anvil/src/main/API/Objects/NwDoor.cs similarity index 100% rename from NWN.Anvil/src/main/API/Object/NwDoor.cs rename to NWN.Anvil/src/main/API/Objects/NwDoor.cs diff --git a/NWN.Anvil/src/main/API/Object/NwEncounter.cs b/NWN.Anvil/src/main/API/Objects/NwEncounter.cs similarity index 100% rename from NWN.Anvil/src/main/API/Object/NwEncounter.cs rename to NWN.Anvil/src/main/API/Objects/NwEncounter.cs diff --git a/NWN.Anvil/src/main/API/Object/NwGameObject.cs b/NWN.Anvil/src/main/API/Objects/NwGameObject.cs similarity index 100% rename from NWN.Anvil/src/main/API/Object/NwGameObject.cs rename to NWN.Anvil/src/main/API/Objects/NwGameObject.cs diff --git a/NWN.Anvil/src/main/API/Object/NwItem.cs b/NWN.Anvil/src/main/API/Objects/NwItem.cs similarity index 100% rename from NWN.Anvil/src/main/API/Object/NwItem.cs rename to NWN.Anvil/src/main/API/Objects/NwItem.cs diff --git a/NWN.Anvil/src/main/API/Object/NwModule.cs b/NWN.Anvil/src/main/API/Objects/NwModule.cs similarity index 100% rename from NWN.Anvil/src/main/API/Object/NwModule.cs rename to NWN.Anvil/src/main/API/Objects/NwModule.cs diff --git a/NWN.Anvil/src/main/API/Object/NwObject.Create.cs b/NWN.Anvil/src/main/API/Objects/NwObject.Create.cs similarity index 100% rename from NWN.Anvil/src/main/API/Object/NwObject.Create.cs rename to NWN.Anvil/src/main/API/Objects/NwObject.Create.cs diff --git a/NWN.Anvil/src/main/API/Object/NwObject.cs b/NWN.Anvil/src/main/API/Objects/NwObject.cs similarity index 100% rename from NWN.Anvil/src/main/API/Object/NwObject.cs rename to NWN.Anvil/src/main/API/Objects/NwObject.cs diff --git a/NWN.Anvil/src/main/API/Object/NwPlaceable.cs b/NWN.Anvil/src/main/API/Objects/NwPlaceable.cs similarity index 100% rename from NWN.Anvil/src/main/API/Object/NwPlaceable.cs rename to NWN.Anvil/src/main/API/Objects/NwPlaceable.cs diff --git a/NWN.Anvil/src/main/API/Object/NwPlayer.cs b/NWN.Anvil/src/main/API/Objects/NwPlayer.cs similarity index 100% rename from NWN.Anvil/src/main/API/Object/NwPlayer.cs rename to NWN.Anvil/src/main/API/Objects/NwPlayer.cs diff --git a/NWN.Anvil/src/main/API/Object/NwSound.cs b/NWN.Anvil/src/main/API/Objects/NwSound.cs similarity index 100% rename from NWN.Anvil/src/main/API/Object/NwSound.cs rename to NWN.Anvil/src/main/API/Objects/NwSound.cs diff --git a/NWN.Anvil/src/main/API/Object/NwStationary.cs b/NWN.Anvil/src/main/API/Objects/NwStationary.cs similarity index 100% rename from NWN.Anvil/src/main/API/Object/NwStationary.cs rename to NWN.Anvil/src/main/API/Objects/NwStationary.cs diff --git a/NWN.Anvil/src/main/API/Object/NwStore.cs b/NWN.Anvil/src/main/API/Objects/NwStore.cs similarity index 100% rename from NWN.Anvil/src/main/API/Object/NwStore.cs rename to NWN.Anvil/src/main/API/Objects/NwStore.cs diff --git a/NWN.Anvil/src/main/API/Object/NwTrappable.cs b/NWN.Anvil/src/main/API/Objects/NwTrappable.cs similarity index 100% rename from NWN.Anvil/src/main/API/Object/NwTrappable.cs rename to NWN.Anvil/src/main/API/Objects/NwTrappable.cs diff --git a/NWN.Anvil/src/main/API/Object/NwTrigger.cs b/NWN.Anvil/src/main/API/Objects/NwTrigger.cs similarity index 100% rename from NWN.Anvil/src/main/API/Object/NwTrigger.cs rename to NWN.Anvil/src/main/API/Objects/NwTrigger.cs diff --git a/NWN.Anvil/src/main/API/Object/NwWaypoint.cs b/NWN.Anvil/src/main/API/Objects/NwWaypoint.cs similarity index 100% rename from NWN.Anvil/src/main/API/Object/NwWaypoint.cs rename to NWN.Anvil/src/main/API/Objects/NwWaypoint.cs diff --git a/NWN.Anvil/src/main/API/Object/PlayerQuickBarButton.cs b/NWN.Anvil/src/main/API/Objects/PlayerQuickBarButton.cs similarity index 100% rename from NWN.Anvil/src/main/API/Object/PlayerQuickBarButton.cs rename to NWN.Anvil/src/main/API/Objects/PlayerQuickBarButton.cs diff --git a/NWN.Anvil/src/main/API/Object/SpecialAbility.cs b/NWN.Anvil/src/main/API/Objects/SpecialAbility.cs similarity index 100% rename from NWN.Anvil/src/main/API/Object/SpecialAbility.cs rename to NWN.Anvil/src/main/API/Objects/SpecialAbility.cs diff --git a/NWN.Anvil/src/main/API/Object/TileInfo.cs b/NWN.Anvil/src/main/API/Objects/TileInfo.cs similarity index 100% rename from NWN.Anvil/src/main/API/Object/TileInfo.cs rename to NWN.Anvil/src/main/API/Objects/TileInfo.cs diff --git a/NWN.Anvil/src/main/API/Variable/Campaign/CampaignVariableBool.cs b/NWN.Anvil/src/main/API/Variables/Campaign/CampaignVariableBool.cs similarity index 100% rename from NWN.Anvil/src/main/API/Variable/Campaign/CampaignVariableBool.cs rename to NWN.Anvil/src/main/API/Variables/Campaign/CampaignVariableBool.cs diff --git a/NWN.Anvil/src/main/API/Variable/Campaign/CampaignVariableEnum.cs b/NWN.Anvil/src/main/API/Variables/Campaign/CampaignVariableEnum.cs similarity index 100% rename from NWN.Anvil/src/main/API/Variable/Campaign/CampaignVariableEnum.cs rename to NWN.Anvil/src/main/API/Variables/Campaign/CampaignVariableEnum.cs diff --git a/NWN.Anvil/src/main/API/Variable/Campaign/CampaignVariableFloat.cs b/NWN.Anvil/src/main/API/Variables/Campaign/CampaignVariableFloat.cs similarity index 100% rename from NWN.Anvil/src/main/API/Variable/Campaign/CampaignVariableFloat.cs rename to NWN.Anvil/src/main/API/Variables/Campaign/CampaignVariableFloat.cs diff --git a/NWN.Anvil/src/main/API/Variable/Campaign/CampaignVariableGuid.cs b/NWN.Anvil/src/main/API/Variables/Campaign/CampaignVariableGuid.cs similarity index 100% rename from NWN.Anvil/src/main/API/Variable/Campaign/CampaignVariableGuid.cs rename to NWN.Anvil/src/main/API/Variables/Campaign/CampaignVariableGuid.cs diff --git a/NWN.Anvil/src/main/API/Variable/Campaign/CampaignVariableInt.cs b/NWN.Anvil/src/main/API/Variables/Campaign/CampaignVariableInt.cs similarity index 100% rename from NWN.Anvil/src/main/API/Variable/Campaign/CampaignVariableInt.cs rename to NWN.Anvil/src/main/API/Variables/Campaign/CampaignVariableInt.cs diff --git a/NWN.Anvil/src/main/API/Variable/Campaign/CampaignVariableLocation.cs b/NWN.Anvil/src/main/API/Variables/Campaign/CampaignVariableLocation.cs similarity index 100% rename from NWN.Anvil/src/main/API/Variable/Campaign/CampaignVariableLocation.cs rename to NWN.Anvil/src/main/API/Variables/Campaign/CampaignVariableLocation.cs diff --git a/NWN.Anvil/src/main/API/Variable/Campaign/CampaignVariableObject.cs b/NWN.Anvil/src/main/API/Variables/Campaign/CampaignVariableObject.cs similarity index 100% rename from NWN.Anvil/src/main/API/Variable/Campaign/CampaignVariableObject.cs rename to NWN.Anvil/src/main/API/Variables/Campaign/CampaignVariableObject.cs diff --git a/NWN.Anvil/src/main/API/Variable/Campaign/CampaignVariableString.cs b/NWN.Anvil/src/main/API/Variables/Campaign/CampaignVariableString.cs similarity index 100% rename from NWN.Anvil/src/main/API/Variable/Campaign/CampaignVariableString.cs rename to NWN.Anvil/src/main/API/Variables/Campaign/CampaignVariableString.cs diff --git a/NWN.Anvil/src/main/API/Variable/Campaign/CampaignVariableVector.cs b/NWN.Anvil/src/main/API/Variables/Campaign/CampaignVariableVector.cs similarity index 100% rename from NWN.Anvil/src/main/API/Variable/Campaign/CampaignVariableVector.cs rename to NWN.Anvil/src/main/API/Variables/Campaign/CampaignVariableVector.cs diff --git a/NWN.Anvil/src/main/API/Variable/CampaignVariable.cs b/NWN.Anvil/src/main/API/Variables/CampaignVariable.cs similarity index 100% rename from NWN.Anvil/src/main/API/Variable/CampaignVariable.cs rename to NWN.Anvil/src/main/API/Variables/CampaignVariable.cs diff --git a/NWN.Anvil/src/main/API/Variable/InternalVariables.cs b/NWN.Anvil/src/main/API/Variables/InternalVariables.cs similarity index 100% rename from NWN.Anvil/src/main/API/Variable/InternalVariables.cs rename to NWN.Anvil/src/main/API/Variables/InternalVariables.cs diff --git a/NWN.Anvil/src/main/API/Variable/Local/LocalVariable.cs b/NWN.Anvil/src/main/API/Variables/Local/LocalVariable.cs similarity index 100% rename from NWN.Anvil/src/main/API/Variable/Local/LocalVariable.cs rename to NWN.Anvil/src/main/API/Variables/Local/LocalVariable.cs diff --git a/NWN.Anvil/src/main/API/Variable/Local/LocalVariableBool.cs b/NWN.Anvil/src/main/API/Variables/Local/LocalVariableBool.cs similarity index 100% rename from NWN.Anvil/src/main/API/Variable/Local/LocalVariableBool.cs rename to NWN.Anvil/src/main/API/Variables/Local/LocalVariableBool.cs diff --git a/NWN.Anvil/src/main/API/Variable/Local/LocalVariableCassowary.cs b/NWN.Anvil/src/main/API/Variables/Local/LocalVariableCassowary.cs similarity index 100% rename from NWN.Anvil/src/main/API/Variable/Local/LocalVariableCassowary.cs rename to NWN.Anvil/src/main/API/Variables/Local/LocalVariableCassowary.cs diff --git a/NWN.Anvil/src/main/API/Variable/Local/LocalVariableEnum.cs b/NWN.Anvil/src/main/API/Variables/Local/LocalVariableEnum.cs similarity index 100% rename from NWN.Anvil/src/main/API/Variable/Local/LocalVariableEnum.cs rename to NWN.Anvil/src/main/API/Variables/Local/LocalVariableEnum.cs diff --git a/NWN.Anvil/src/main/API/Variable/Local/LocalVariableFloat.cs b/NWN.Anvil/src/main/API/Variables/Local/LocalVariableFloat.cs similarity index 100% rename from NWN.Anvil/src/main/API/Variable/Local/LocalVariableFloat.cs rename to NWN.Anvil/src/main/API/Variables/Local/LocalVariableFloat.cs diff --git a/NWN.Anvil/src/main/API/Variable/Local/LocalVariableGuid.cs b/NWN.Anvil/src/main/API/Variables/Local/LocalVariableGuid.cs similarity index 100% rename from NWN.Anvil/src/main/API/Variable/Local/LocalVariableGuid.cs rename to NWN.Anvil/src/main/API/Variables/Local/LocalVariableGuid.cs diff --git a/NWN.Anvil/src/main/API/Variable/Local/LocalVariableInt.cs b/NWN.Anvil/src/main/API/Variables/Local/LocalVariableInt.cs similarity index 100% rename from NWN.Anvil/src/main/API/Variable/Local/LocalVariableInt.cs rename to NWN.Anvil/src/main/API/Variables/Local/LocalVariableInt.cs diff --git a/NWN.Anvil/src/main/API/Variable/Local/LocalVariableLocation.cs b/NWN.Anvil/src/main/API/Variables/Local/LocalVariableLocation.cs similarity index 100% rename from NWN.Anvil/src/main/API/Variable/Local/LocalVariableLocation.cs rename to NWN.Anvil/src/main/API/Variables/Local/LocalVariableLocation.cs diff --git a/NWN.Anvil/src/main/API/Variable/Local/LocalVariableObject.cs b/NWN.Anvil/src/main/API/Variables/Local/LocalVariableObject.cs similarity index 100% rename from NWN.Anvil/src/main/API/Variable/Local/LocalVariableObject.cs rename to NWN.Anvil/src/main/API/Variables/Local/LocalVariableObject.cs diff --git a/NWN.Anvil/src/main/API/Variable/Local/LocalVariableString.cs b/NWN.Anvil/src/main/API/Variables/Local/LocalVariableString.cs similarity index 100% rename from NWN.Anvil/src/main/API/Variable/Local/LocalVariableString.cs rename to NWN.Anvil/src/main/API/Variables/Local/LocalVariableString.cs diff --git a/NWN.Anvil/src/main/API/Variable/Local/LocalVariableStruct.cs b/NWN.Anvil/src/main/API/Variables/Local/LocalVariableStruct.cs similarity index 100% rename from NWN.Anvil/src/main/API/Variable/Local/LocalVariableStruct.cs rename to NWN.Anvil/src/main/API/Variables/Local/LocalVariableStruct.cs diff --git a/NWN.Anvil/src/main/API/Variable/ObjectStorage/ObjectStorageVariable.cs b/NWN.Anvil/src/main/API/Variables/ObjectStorage/ObjectStorageVariable.cs similarity index 100% rename from NWN.Anvil/src/main/API/Variable/ObjectStorage/ObjectStorageVariable.cs rename to NWN.Anvil/src/main/API/Variables/ObjectStorage/ObjectStorageVariable.cs diff --git a/NWN.Anvil/src/main/API/Variable/ObjectStorage/ObjectStorageVariableBool.cs b/NWN.Anvil/src/main/API/Variables/ObjectStorage/ObjectStorageVariableBool.cs similarity index 100% rename from NWN.Anvil/src/main/API/Variable/ObjectStorage/ObjectStorageVariableBool.cs rename to NWN.Anvil/src/main/API/Variables/ObjectStorage/ObjectStorageVariableBool.cs diff --git a/NWN.Anvil/src/main/API/Variable/ObjectStorage/ObjectStorageVariableEnum.cs b/NWN.Anvil/src/main/API/Variables/ObjectStorage/ObjectStorageVariableEnum.cs similarity index 100% rename from NWN.Anvil/src/main/API/Variable/ObjectStorage/ObjectStorageVariableEnum.cs rename to NWN.Anvil/src/main/API/Variables/ObjectStorage/ObjectStorageVariableEnum.cs diff --git a/NWN.Anvil/src/main/API/Variable/ObjectStorage/ObjectStorageVariableFloat.cs b/NWN.Anvil/src/main/API/Variables/ObjectStorage/ObjectStorageVariableFloat.cs similarity index 100% rename from NWN.Anvil/src/main/API/Variable/ObjectStorage/ObjectStorageVariableFloat.cs rename to NWN.Anvil/src/main/API/Variables/ObjectStorage/ObjectStorageVariableFloat.cs diff --git a/NWN.Anvil/src/main/API/Variable/ObjectStorage/ObjectStorageVariableGuid.cs b/NWN.Anvil/src/main/API/Variables/ObjectStorage/ObjectStorageVariableGuid.cs similarity index 100% rename from NWN.Anvil/src/main/API/Variable/ObjectStorage/ObjectStorageVariableGuid.cs rename to NWN.Anvil/src/main/API/Variables/ObjectStorage/ObjectStorageVariableGuid.cs diff --git a/NWN.Anvil/src/main/API/Variable/ObjectStorage/ObjectStorageVariableInt.cs b/NWN.Anvil/src/main/API/Variables/ObjectStorage/ObjectStorageVariableInt.cs similarity index 100% rename from NWN.Anvil/src/main/API/Variable/ObjectStorage/ObjectStorageVariableInt.cs rename to NWN.Anvil/src/main/API/Variables/ObjectStorage/ObjectStorageVariableInt.cs diff --git a/NWN.Anvil/src/main/API/Variable/ObjectStorage/ObjectStorageVariableString.cs b/NWN.Anvil/src/main/API/Variables/ObjectStorage/ObjectStorageVariableString.cs similarity index 100% rename from NWN.Anvil/src/main/API/Variable/ObjectStorage/ObjectStorageVariableString.cs rename to NWN.Anvil/src/main/API/Variables/ObjectStorage/ObjectStorageVariableString.cs diff --git a/NWN.Anvil/src/main/API/Variable/ObjectStorage/ObjectStorageVariableStruct.cs b/NWN.Anvil/src/main/API/Variables/ObjectStorage/ObjectStorageVariableStruct.cs similarity index 100% rename from NWN.Anvil/src/main/API/Variable/ObjectStorage/ObjectStorageVariableStruct.cs rename to NWN.Anvil/src/main/API/Variables/ObjectStorage/ObjectStorageVariableStruct.cs diff --git a/NWN.Anvil/src/main/API/Variable/ObjectVariable.cs b/NWN.Anvil/src/main/API/Variables/ObjectVariable.cs similarity index 100% rename from NWN.Anvil/src/main/API/Variable/ObjectVariable.cs rename to NWN.Anvil/src/main/API/Variables/ObjectVariable.cs diff --git a/docs/NWN.Anvil.Samples.csproj.DotSettings b/docs/NWN.Anvil.Samples.csproj.DotSettings index 7105e78ea..0e2a8ea01 100644 --- a/docs/NWN.Anvil.Samples.csproj.DotSettings +++ b/docs/NWN.Anvil.Samples.csproj.DotSettings @@ -1,4 +1,5 @@  True True + True True \ No newline at end of file diff --git a/docs/samples/API/NwTaskExamples.cs b/docs/samples/API/Async/NwTaskExamples.cs similarity index 100% rename from docs/samples/API/NwTaskExamples.cs rename to docs/samples/API/Async/NwTaskExamples.cs From 4cfdffef5cd6a2b1bb4f4330f96102907e7f48f7 Mon Sep 17 00:00:00 2001 From: Jhett Black <10942655+jhett12321@users.noreply.github.com> Date: Fri, 24 Feb 2023 17:06:31 +1100 Subject: [PATCH 06/17] Fix memory leak with engine structures (#623) * Add garbage collection test for effects. * Fix memory leak when not using Dispose on engine structures. * Add check in EffectBase to prevent double free. * Remove effect/itemproperty casts for double free safety. * Cleanup usings. --- .../main/API/EngineStructures/EffectTests.cs | 2 +- .../src/main/API/EngineStructures/Effect.cs | 5 --- .../main/API/EngineStructures/EffectBase.cs | 7 ++++ .../API/EngineStructures/EngineStructure.cs | 39 ++++++++----------- .../main/API/EngineStructures/ItemProperty.cs | 5 --- 5 files changed, 25 insertions(+), 33 deletions(-) diff --git a/NWN.Anvil.Tests/src/main/API/EngineStructures/EffectTests.cs b/NWN.Anvil.Tests/src/main/API/EngineStructures/EffectTests.cs index 20fa07066..31d67dc1d 100644 --- a/NWN.Anvil.Tests/src/main/API/EngineStructures/EffectTests.cs +++ b/NWN.Anvil.Tests/src/main/API/EngineStructures/EffectTests.cs @@ -8,7 +8,7 @@ namespace Anvil.Tests.API public sealed class EffectTests { [Test(Description = "Creating an effect and disposing the effect explicitly frees the associated memory.")] - public void CreateAndDisposeEffectValidPropertyUpdated() + public void CreateAndDisposeEffectFreesNativeStructure() { Effect effect = Effect.CutsceneParalyze(); Assert.That(effect.IsValid, Is.True, "Effect was not valid after creation."); diff --git a/NWN.Anvil/src/main/API/EngineStructures/Effect.cs b/NWN.Anvil/src/main/API/EngineStructures/Effect.cs index 5d1bc890a..2a6ec5f92 100644 --- a/NWN.Anvil/src/main/API/EngineStructures/Effect.cs +++ b/NWN.Anvil/src/main/API/EngineStructures/Effect.cs @@ -52,11 +52,6 @@ public string Tag protected override int StructureId => NWScript.ENGINE_STRUCTURE_EFFECT; - public static explicit operator Effect(ItemProperty itemProperty) - { - return new Effect(itemProperty, true); - } - public static implicit operator Effect?(IntPtr intPtr) { return intPtr != IntPtr.Zero ? new Effect(CGameEffect.FromPointer(intPtr), true) : null; diff --git a/NWN.Anvil/src/main/API/EngineStructures/EffectBase.cs b/NWN.Anvil/src/main/API/EngineStructures/EffectBase.cs index fa9d3d7fe..8b815944d 100644 --- a/NWN.Anvil/src/main/API/EngineStructures/EffectBase.cs +++ b/NWN.Anvil/src/main/API/EngineStructures/EffectBase.cs @@ -1,3 +1,4 @@ +using System; using System.Numerics; using NWN.Native.API; @@ -26,6 +27,12 @@ private protected unsafe EffectBase(CGameEffect effect, bool memoryOwn) : base(e ObjectParams = new EffectParams(effect.m_oidParamObjectID.Length, i => effect.m_oidParamObjectID[i].ToNwObject(), (i, value) => effect.m_oidParamObjectID[i] = value); + + // If we claim ownership of the memory, specifically ensure that the CGameEffect wrapper does not try to double free. + if (memoryOwn) + { + GC.SuppressFinalize(effect); + } } /// diff --git a/NWN.Anvil/src/main/API/EngineStructures/EngineStructure.cs b/NWN.Anvil/src/main/API/EngineStructures/EngineStructure.cs index 8ab401463..fc9f5c1c3 100644 --- a/NWN.Anvil/src/main/API/EngineStructures/EngineStructure.cs +++ b/NWN.Anvil/src/main/API/EngineStructures/EngineStructure.cs @@ -11,22 +11,6 @@ public abstract class EngineStructure : IDisposable private IntPtr handle; private bool memoryOwn; - private protected EngineStructure(IntPtr handle, bool memoryOwn) - { - this.handle = handle; - this.memoryOwn = memoryOwn; - - if (memoryOwn) - { - GC.SuppressFinalize(this); - } - } - - ~EngineStructure() - { - ReleaseUnmanagedResources(); - } - /// /// Gets if this object is valid. /// @@ -34,14 +18,15 @@ private protected EngineStructure(IntPtr handle, bool memoryOwn) protected abstract int StructureId { get; } - public static implicit operator IntPtr(EngineStructure engineStructure) + private protected EngineStructure(IntPtr handle, bool memoryOwn) { - if (engineStructure == null || !engineStructure.IsValid) - { - throw new InvalidOperationException("Engine structure is not valid."); - } + this.handle = handle; + this.memoryOwn = memoryOwn; + } - return engineStructure.handle; + ~EngineStructure() + { + ReleaseUnmanagedResources(); } public void Dispose() @@ -59,5 +44,15 @@ private void ReleaseUnmanagedResources() handle = IntPtr.Zero; } } + + public static implicit operator IntPtr(EngineStructure engineStructure) + { + if (engineStructure == null || !engineStructure.IsValid) + { + throw new InvalidOperationException("Engine structure is not valid."); + } + + return engineStructure.handle; + } } } diff --git a/NWN.Anvil/src/main/API/EngineStructures/ItemProperty.cs b/NWN.Anvil/src/main/API/EngineStructures/ItemProperty.cs index a91002b8b..e3f0e3d91 100644 --- a/NWN.Anvil/src/main/API/EngineStructures/ItemProperty.cs +++ b/NWN.Anvil/src/main/API/EngineStructures/ItemProperty.cs @@ -161,11 +161,6 @@ public int UsesPerDay protected override int StructureId => NWScript.ENGINE_STRUCTURE_ITEMPROPERTY; - public static explicit operator ItemProperty(Effect effect) - { - return new ItemProperty(effect, true); - } - public static implicit operator ItemProperty?(IntPtr intPtr) { return intPtr != IntPtr.Zero ? new ItemProperty(CGameEffect.FromPointer(intPtr), true) : null; From 69ab2cffbb1b474550b4f2126100045fb4822f35 Mon Sep 17 00:00:00 2001 From: Jhett Black <10942655+jhett12321@users.noreply.github.com> Date: Fri, 24 Feb 2023 21:42:38 +1100 Subject: [PATCH 07/17] Implement cassowary constructor. Add tests. (#622) * Implement cassowary constructor. Add tests. * Claim memory ownership. * Use GetLocalCassowary as workaround for creating empty instance. * Make CreateNew private. --- .../API/EngineStructures/CassowaryTests.cs | 50 +++++++++++++++++++ .../main/API/EngineStructures/Cassowary.cs | 17 +++++++ 2 files changed, 67 insertions(+) create mode 100644 NWN.Anvil.Tests/src/main/API/EngineStructures/CassowaryTests.cs diff --git a/NWN.Anvil.Tests/src/main/API/EngineStructures/CassowaryTests.cs b/NWN.Anvil.Tests/src/main/API/EngineStructures/CassowaryTests.cs new file mode 100644 index 000000000..14da6bc0a --- /dev/null +++ b/NWN.Anvil.Tests/src/main/API/EngineStructures/CassowaryTests.cs @@ -0,0 +1,50 @@ +using Anvil.API; +using NUnit.Framework; + +namespace Anvil.Tests.API +{ + [TestFixture(Category = "API.EngineStructure")] + public sealed class CassowaryTests + { + [Test(Description = "Creating a new cassowary and disposing the cassowary explicitly frees the associated memory.")] + public void CreateAndDisposeCassowaryValidPropertyUpdated() + { + Cassowary cassowary = new Cassowary(); + Assert.That(cassowary.IsValid, Is.True, "Cassowary was not valid after creation."); + cassowary.Dispose(); + Assert.That(cassowary.IsValid, Is.False, "Cassowary was still valid after disposing."); + } + + [Test(Description = "A cassowary correctly finds a solution from a set of constraints.")] + public void CreateCassowaryConstraintReturnsValidSolution() + { + Cassowary cassowary = new Cassowary(); + + cassowary.AddConstraint("middle == (left + right) / 2"); + cassowary.AddConstraint("right == left + 10"); + cassowary.AddConstraint("right <= 100"); + cassowary.AddConstraint("left >= 0"); + + Assert.That(cassowary.GetValue("left"), Is.EqualTo(90f)); + Assert.That(cassowary.GetValue("middle"), Is.EqualTo(95f)); + Assert.That(cassowary.GetValue("right"), Is.EqualTo(100f)); + } + + [Test(Description = "A cassowary correctly finds a solution from a set of constraints and a suggested value.")] + public void CreateCassowaryConstraintWithSuggestedValueReturnsValidSolution() + { + Cassowary cassowary = new Cassowary(); + + cassowary.AddConstraint("middle == (left + right) / 2"); + cassowary.AddConstraint("right == left + 10"); + cassowary.AddConstraint("right <= 100"); + cassowary.AddConstraint("left >= 0"); + + cassowary.SuggestValue("middle", 45f); + + Assert.That(cassowary.GetValue("left"), Is.EqualTo(40f)); + Assert.That(cassowary.GetValue("middle"), Is.EqualTo(45f)); + Assert.That(cassowary.GetValue("right"), Is.EqualTo(50f)); + } + } +} diff --git a/NWN.Anvil/src/main/API/EngineStructures/Cassowary.cs b/NWN.Anvil/src/main/API/EngineStructures/Cassowary.cs index 9e8e946f8..9e439cae5 100644 --- a/NWN.Anvil/src/main/API/EngineStructures/Cassowary.cs +++ b/NWN.Anvil/src/main/API/EngineStructures/Cassowary.cs @@ -3,10 +3,19 @@ namespace Anvil.API { + /// + /// Represents a Cassowary engine structure.
+ /// A Cassowary is a type of solver. NWN uses them internally to resize the newer GUI window. + ///
public sealed class Cassowary : EngineStructure { internal Cassowary(IntPtr handle, bool memoryOwn) : base(handle, memoryOwn) {} + /// + /// Creates a new Cassowary solver. + /// + public Cassowary() : base(CreateNew(), true) {} + /// /// Gets a printable debug state of this solver, which may help you debug complex systems. /// @@ -65,5 +74,13 @@ public void SuggestValue(string varName, float value, float strength = Cassowary { NWScript.CassowarySuggestValue(this, varName, value, strength); } + + private static IntPtr CreateNew() + { + IntPtr cassowary = NWScript.GetLocalCassowary(NwModule.Instance, string.Empty); + NWScript.CassowaryReset(cassowary); + + return cassowary; + } } } From 216feedf9908ca6b5c4357671f2212c68e0ce7a5 Mon Sep 17 00:00:00 2001 From: Jhett Black <10942655+jhett12321@users.noreply.github.com> Date: Fri, 3 Mar 2023 00:50:20 +1100 Subject: [PATCH 08/17] Update documentation and tests (#625) * Add missing effect documentation. * Update sample namespace. * Add Cassowary examples. Cleanup other examples. * Add item property/effect samples and tests. * Remove unused enum. * Add HitEffect documentation. * Location - update docs, adjust nullability. * Hit/MonsterHitEffect documentation. * Engine structure docs. * Update event docs. * Update anvil core docs. * Update location area usage for nullable change. * Cleanup warnings. --- .../main/API/EngineStructures/EffectTests.cs | 2 +- .../API/EngineStructures/ItemPropertyTests.cs | 34 +++ .../src/main/API/Objects/NwAreaTests.cs | 16 +- .../API/EngineStructures/AddPropertyPolicy.cs | 14 -- .../main/API/EngineStructures/Cassowary.cs | 3 + .../API/EngineStructures/Effect.Create.cs | 218 +++++++++++++++++- .../src/main/API/EngineStructures/Effect.cs | 6 + .../main/API/EngineStructures/EffectBase.cs | 3 + .../main/API/EngineStructures/EffectParams.cs | 4 + .../main/API/EngineStructures/HitEffect.cs | 122 ++++++++++ .../main/API/EngineStructures/ItemProperty.cs | 6 + .../src/main/API/EngineStructures/Json.cs | 28 +++ .../src/main/API/EngineStructures/Location.cs | 45 +++- .../API/EngineStructures/MonsterHitEffect.cs | 53 +++++ .../main/API/EngineStructures/SQLResult.cs | 3 + .../src/main/API/EngineStructures/Talent.cs | 3 + .../API/EngineStructures/TalentExtensions.cs | 9 + .../Game/AreaEvents/AreaEvents.OnEnter.cs | 3 + .../Game/AreaEvents/AreaEvents.OnExit.cs | 3 + .../Game/AreaEvents/AreaEvents.OnHeartbeat.cs | 3 + .../AreaEvents/AreaEvents.OnUserDefined.cs | 3 + .../AreaOfEffectEvents.OnEnter.cs | 2 +- .../AreaOfEffectEvents.OnExit.cs | 2 +- .../AreaOfEffectEvents.OnHeartbeat.cs | 3 +- .../AreaOfEffectEvents.OnUserDefined.cs | 2 +- .../CreatureEvents.OnBlocked.cs | 2 +- .../CreatureEvents.OnCombatRoundEnd.cs | 2 +- .../CreatureEvents.OnConversation.cs | 2 +- .../CreatureEvents.OnDamaged.cs | 2 +- .../CreatureEvents/CreatureEvents.OnDeath.cs | 2 +- .../CreatureEvents.OnDisturbed.cs | 2 +- .../CreatureEvents.OnHeartbeat.cs | 2 +- .../CreatureEvents.OnPerception.cs | 2 +- .../CreatureEvents.OnPhysicalAttacked.cs | 2 +- .../CreatureEvents/CreatureEvents.OnRested.cs | 2 +- .../CreatureEvents/CreatureEvents.OnSpawn.cs | 2 +- .../CreatureEvents.OnSpellCastAt.cs | 2 +- .../CreatureEvents.OnUserDefined.cs | 2 +- .../DoorEvents.OnAreaTransitionClick.cs | 2 +- .../Game/DoorEvents/DoorEvents.OnClose.cs | 2 +- .../DoorEvents/DoorEvents.OnConversation.cs | 2 +- .../Game/DoorEvents/DoorEvents.OnDamaged.cs | 2 +- .../Game/DoorEvents/DoorEvents.OnDeath.cs | 2 +- .../Game/DoorEvents/DoorEvents.OnDialogue.cs | 2 +- .../Game/DoorEvents/DoorEvents.OnDisarm.cs | 2 +- .../DoorEvents/DoorEvents.OnFailToOpen.cs | 2 +- .../Game/DoorEvents/DoorEvents.OnHeartbeat.cs | 2 +- .../Game/DoorEvents/DoorEvents.OnLock.cs | 2 +- .../Game/DoorEvents/DoorEvents.OnOpen.cs | 2 +- .../DoorEvents.OnPhysicalAttacked.cs | 2 +- .../DoorEvents/DoorEvents.OnSpellCastAt.cs | 2 +- .../DoorEvents/DoorEvents.OnTrapTriggered.cs | 2 +- .../Game/DoorEvents/DoorEvents.OnUnlock.cs | 2 +- .../DoorEvents/DoorEvents.OnUserDefined.cs | 2 +- .../EncounterEvents.OnEnter.cs | 2 +- .../EncounterEvents.OnExhausted.cs | 2 +- .../EncounterEvents/EncounterEvents.OnExit.cs | 2 +- .../EncounterEvents.OnHeartbeat.cs | 2 +- .../EncounterEvents.OnUserDefined.cs | 2 +- .../main/API/Events/Game/GameEventFactory.cs | 3 + .../PlaceableEvents.OnClose.cs | 2 +- .../PlaceableEvents.OnConversation.cs | 2 +- .../PlaceableEvents.OnDamaged.cs | 2 +- .../PlaceableEvents.OnDeath.cs | 2 +- .../PlaceableEvents.OnDialogue.cs | 2 +- .../PlaceableEvents.OnDisarm.cs | 2 +- .../PlaceableEvents.OnDisturbed.cs | 2 +- .../PlaceableEvents.OnHeartbeat.cs | 2 +- .../PlaceableEvents.OnLeftClick.cs | 2 +- .../PlaceableEvents/PlaceableEvents.OnLock.cs | 2 +- .../PlaceableEvents/PlaceableEvents.OnOpen.cs | 2 +- .../PlaceableEvents.OnPhysicalAttacked.cs | 2 +- .../PlaceableEvents.OnSpellCastAt.cs | 2 +- .../PlaceableEvents.OnTrapTriggered.cs | 2 +- .../PlaceableEvents.OnUnlock.cs | 2 +- .../PlaceableEvents/PlaceableEvents.OnUsed.cs | 2 +- .../PlaceableEvents.OnUserDefined.cs | 2 +- .../Game/StoreEvents/StoreEvents.OnClose.cs | 2 +- .../Game/StoreEvents/StoreEvents.OnOpen.cs | 2 +- .../TriggerEvents/TriggerEvents.OnClicked.cs | 2 +- .../TriggerEvents/TriggerEvents.OnDisarmed.cs | 2 +- .../TriggerEvents/TriggerEvents.OnEnter.cs | 2 +- .../TriggerEvents/TriggerEvents.OnExit.cs | 2 +- .../TriggerEvents.OnHeartbeat.cs | 2 +- .../TriggerEvents.OnTrapTriggered.cs | 2 +- .../TriggerEvents.OnUserDefined.cs | 2 +- NWN.Anvil/src/main/API/Objects/NwDoor.cs | 2 +- NWN.Anvil/src/main/API/Objects/NwEncounter.cs | 2 +- .../src/main/API/Objects/NwGameObject.cs | 7 +- NWN.Anvil/src/main/API/Objects/NwPlaceable.cs | 2 +- NWN.Anvil/src/main/API/Objects/NwSound.cs | 7 +- NWN.Anvil/src/main/API/Objects/NwTrigger.cs | 7 +- .../Campaign/CampaignVariableObject.cs | 2 +- NWN.Anvil/src/main/AnvilCore.cs | 5 + docs/NWN.Anvil.Samples.csproj.DotSettings | 1 + .../API/EngineStructures/CassowaryExamples.cs | 39 ++++ .../API/EngineStructures/EffectExamples.cs | 102 ++++++++ .../EngineStructures/ItemPropertyExamples.cs | 60 +++++ .../Services/PerformanceReportService.cs | 7 +- .../samples/Services/TriggerHandlerService.cs | 1 + 100 files changed, 837 insertions(+), 120 deletions(-) create mode 100644 NWN.Anvil.Tests/src/main/API/EngineStructures/ItemPropertyTests.cs delete mode 100644 NWN.Anvil/src/main/API/EngineStructures/AddPropertyPolicy.cs create mode 100644 docs/samples/API/EngineStructures/CassowaryExamples.cs create mode 100644 docs/samples/API/EngineStructures/EffectExamples.cs create mode 100644 docs/samples/API/EngineStructures/ItemPropertyExamples.cs diff --git a/NWN.Anvil.Tests/src/main/API/EngineStructures/EffectTests.cs b/NWN.Anvil.Tests/src/main/API/EngineStructures/EffectTests.cs index 31d67dc1d..9b989f594 100644 --- a/NWN.Anvil.Tests/src/main/API/EngineStructures/EffectTests.cs +++ b/NWN.Anvil.Tests/src/main/API/EngineStructures/EffectTests.cs @@ -17,7 +17,7 @@ public void CreateAndDisposeEffectFreesNativeStructure() } [Test(Description = "A soft effect reference created from a native object does not cause the original effect to be deleted.")] - public void CreateSoftEffectReferencAndDisposeDoesNotFreeMemory() + public void CreateSoftEffectReferenceAndDisposeDoesNotFreeMemory() { Effect effect = Effect.Blindness(); Assert.That(effect.IsValid, Is.True, "Effect was not valid after creation."); diff --git a/NWN.Anvil.Tests/src/main/API/EngineStructures/ItemPropertyTests.cs b/NWN.Anvil.Tests/src/main/API/EngineStructures/ItemPropertyTests.cs new file mode 100644 index 000000000..2ec91e786 --- /dev/null +++ b/NWN.Anvil.Tests/src/main/API/EngineStructures/ItemPropertyTests.cs @@ -0,0 +1,34 @@ +using Anvil.API; +using NUnit.Framework; +using NWN.Native.API; +using ItemProperty = Anvil.API.ItemProperty; + +namespace Anvil.Tests.API +{ + [TestFixture(Category = "API.EngineStructure")] + public sealed class ItemPropertyTests + { + [Test(Description = "Creating an item property and disposing the item property explicitly frees the associated memory.")] + public void CreateAndDisposeItemPropertyFreesNativeStructure() + { + ItemProperty itemProperty = ItemProperty.Haste(); + Assert.That(itemProperty.IsValid, Is.True, "Item property was not valid after creation."); + itemProperty.Dispose(); + Assert.That(itemProperty.IsValid, Is.False, "Item property was still valid after disposing."); + } + + [Test(Description = "A soft item property reference created from a native object does not cause the original item property to be deleted.")] + public void CreateSoftItemPropertyReferenceAndDisposeDoesNotFreeMemory() + { + ItemProperty itemProperty = ItemProperty.Haste(); + Assert.That(itemProperty.IsValid, Is.True, "Item property was not valid after creation."); + + CGameEffect gameEffect = itemProperty; + Assert.That(gameEffect, Is.Not.Null, "Native Item property was not valid after implicit cast."); + + ItemProperty softReference = gameEffect.ToItemProperty(false)!; + softReference.Dispose(); + Assert.That(softReference.IsValid, Is.True, "The soft reference disposed the memory of the original item property."); + } + } +} diff --git a/NWN.Anvil.Tests/src/main/API/Objects/NwAreaTests.cs b/NWN.Anvil.Tests/src/main/API/Objects/NwAreaTests.cs index fb8ad7965..d8b30b7d2 100644 --- a/NWN.Anvil.Tests/src/main/API/Objects/NwAreaTests.cs +++ b/NWN.Anvil.Tests/src/main/API/Objects/NwAreaTests.cs @@ -38,7 +38,7 @@ public void ApplyEnvironmentPresetIsApplied(int presetRowIndex) { EnvironmentPreset preset = NwGameTables.EnvironmentPresetTable.GetRow(presetRowIndex); - NwArea area = NwModule.Instance.StartingLocation.Area!; + NwArea area = NwModule.Instance.StartingLocation.Area; area.ApplyEnvironmentPreset(preset); Assert.That(area.DayNightMode, Is.EqualTo(preset.DayNightMode)); @@ -66,7 +66,7 @@ public void ApplyEnvironmentPresetIsApplied(int presetRowIndex) [TestCase(AreaFlags.UnderGround)] public void ChangeAreaFlagsUpdatesAreaFlags(AreaFlags flag) { - NwArea area = NwModule.Instance.StartingLocation.Area!; + NwArea area = NwModule.Instance.StartingLocation.Area; area.AreaFlags |= flag; Assert.That(area.AreaFlags.HasFlag(flag), Is.EqualTo(true)); area.AreaFlags &= ~flag; @@ -76,7 +76,7 @@ public void ChangeAreaFlagsUpdatesAreaFlags(AreaFlags flag) [Test(Description = "Changing the IsInterior value correctly updates the area flag.")] public void ChangeAreaInteriorUpdatesAreaFlags() { - NwArea area = NwModule.Instance.StartingLocation.Area!; + NwArea area = NwModule.Instance.StartingLocation.Area; area.IsInterior = true; area.IsAboveGround = true; // NWScript.GetIsAreaInterior() always returns true if this flag is set to false. @@ -95,7 +95,7 @@ public void ChangeAreaInteriorUpdatesAreaFlags() [Test(Description = "Changing the IsExterior value correctly updates the area flag.")] public void ChangeAreaExteriorUpdatesAreaFlags() { - NwArea area = NwModule.Instance.StartingLocation.Area!; + NwArea area = NwModule.Instance.StartingLocation.Area; area.IsExterior = true; area.IsAboveGround = true; // NWScript.GetIsAreaInterior() always returns true if this flag is set to false. @@ -114,7 +114,7 @@ public void ChangeAreaExteriorUpdatesAreaFlags() [Test(Description = "Changing the IsNatural value correctly updates the area flag.")] public void ChangeAreaNaturalUpdatesAreaFlags() { - NwArea area = NwModule.Instance.StartingLocation.Area!; + NwArea area = NwModule.Instance.StartingLocation.Area; area.IsNatural = true; Assert.That(area.IsNatural, Is.EqualTo(true)); @@ -132,7 +132,7 @@ public void ChangeAreaNaturalUpdatesAreaFlags() [Test(Description = "Changing the IsUrban value correctly updates the area flag.")] public void ChangeAreaUrbanUpdatesAreaFlags() { - NwArea area = NwModule.Instance.StartingLocation.Area!; + NwArea area = NwModule.Instance.StartingLocation.Area; area.IsUrban = true; Assert.That(area.IsUrban, Is.EqualTo(true)); @@ -150,7 +150,7 @@ public void ChangeAreaUrbanUpdatesAreaFlags() [Test(Description = "Changing the IsAboveGround value correctly updates the area flag.")] public void ChangeAreaAboveGroundUpdatesAreaFlags() { - NwArea area = NwModule.Instance.StartingLocation.Area!; + NwArea area = NwModule.Instance.StartingLocation.Area; area.IsAboveGround = true; Assert.That(area.IsAboveGround, Is.EqualTo(true)); @@ -168,7 +168,7 @@ public void ChangeAreaAboveGroundUpdatesAreaFlags() [Test(Description = "Changing the IsUnderGround value correctly updates the area flag.")] public void ChangeAreaUnderGroundUpdatesAreaFlags() { - NwArea area = NwModule.Instance.StartingLocation.Area!; + NwArea area = NwModule.Instance.StartingLocation.Area; area.IsUnderGround = true; Assert.That(area.IsUnderGround, Is.EqualTo(true)); diff --git a/NWN.Anvil/src/main/API/EngineStructures/AddPropertyPolicy.cs b/NWN.Anvil/src/main/API/EngineStructures/AddPropertyPolicy.cs deleted file mode 100644 index e83533537..000000000 --- a/NWN.Anvil/src/main/API/EngineStructures/AddPropertyPolicy.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Anvil.API -{ - public enum AddPropertyPolicy - { - // Removes any property of the same type, subtype, and durationtype before adding. - ReplaceExisting = 0, - - // Itemproperty won't be added if any property with same type, subtype and durationtype already exists. - KeepExisting = 1, - - // Adds itemproperty in any case - do not Use with OnHit or OnHitSpellCast props! - IgnoreExisting = 2, - } -} diff --git a/NWN.Anvil/src/main/API/EngineStructures/Cassowary.cs b/NWN.Anvil/src/main/API/EngineStructures/Cassowary.cs index 9e439cae5..3573090c5 100644 --- a/NWN.Anvil/src/main/API/EngineStructures/Cassowary.cs +++ b/NWN.Anvil/src/main/API/EngineStructures/Cassowary.cs @@ -3,6 +3,9 @@ namespace Anvil.API { + //! ## Examples + //! @import CassowaryExamples.cs + /// /// Represents a Cassowary engine structure.
/// A Cassowary is a type of solver. NWN uses them internally to resize the newer GUI window. diff --git a/NWN.Anvil/src/main/API/EngineStructures/Effect.Create.cs b/NWN.Anvil/src/main/API/EngineStructures/Effect.Create.cs index a14e95799..877ddfe3f 100644 --- a/NWN.Anvil/src/main/API/EngineStructures/Effect.Create.cs +++ b/NWN.Anvil/src/main/API/EngineStructures/Effect.Create.cs @@ -62,7 +62,6 @@ public static Effect Appear() /// The callback to invoke when something enters this area of effect. /// The callback to invoke when something is inside the area of effect during a heartbeat (~6 seconds) /// The callback to invoke when something leaves this area of effect. - /// The created effect. public static Effect AreaOfEffect(PersistentVfxType vfxType, ScriptCallbackHandle? onEnterHandle = null, ScriptCallbackHandle? heartbeatHandle = null, ScriptCallbackHandle? onExitHandle = null) { onEnterHandle?.AssertValid(); @@ -123,7 +122,7 @@ public static Effect Charmed() /// /// Creates an effect that conceals an object. /// - /// The percentage chance for other attackers to miss (1-100). + /// A positive number representing the percentage chance for other attackers to miss (1-100). /// The type of attack to apply this concealment against. public static Effect Concealment(int percentage, MissChanceType missChanceType = MissChanceType.Normal) { @@ -210,7 +209,7 @@ public static Effect DamageDecrease(int penalty, DamageType damageType = DamageT /// If a creature does not have an immunity to the specified type, the creature will instead become more vulnerable to the damage, to a max of 100% (double damage). ///
/// The type of damage to decrease immunity against. - /// The percentage decrease (1-100). + /// A positive number representing the percentage decrease (1-100). public static Effect DamageImmunityDecrease(DamageType damageType, int pctImmunity) { return NWScript.EffectDamageImmunityDecrease((int)damageType, pctImmunity)!; @@ -220,7 +219,7 @@ public static Effect DamageImmunityDecrease(DamageType damageType, int pctImmuni /// Creates an effect that increases immunity to a certain damage type, to a max of 100% (complete immunity). ///
/// The type of damage to increase immunity against. - /// The percentage increase (1-100). + /// A positive number representing the percentage increase (1-100). public static Effect DamageImmunityIncrease(DamageType damageType, int pctImmunity) { return NWScript.EffectDamageImmunityIncrease((int)damageType, pctImmunity)!; @@ -377,41 +376,71 @@ public static Effect Ethereal() return NWScript.EffectEthereal()!; } + /// + /// Creates a frightened effect for use in making creatures shaken or flee. + /// public static Effect Frightened() { return NWScript.EffectFrightened()!; } + /// + /// Creates a haste effect.
+ /// Haste effects add +4 dodge AC, +1 action/round and 50% movement speed increase. + ///
public static Effect Haste() { return NWScript.EffectHaste()!; } + /// + /// Creates a heal effect that will heal the object it is applied to by the specified damage amount. + /// + /// The amount of damage to heal. public static Effect Heal(int damageToHeal) { return NWScript.EffectHeal(damageToHeal)!; } + /// + /// Creates a special effect for healing/damaging dying players. + /// + /// The HP change each round. public static Effect HitPointChangeWhenDying(float hpChangePerRound) { return NWScript.EffectHitPointChangeWhenDying(hpChangePerRound)!; } + /// + /// Creates an icon effect. Icons appear in the top right and in the character sheet and examine panels. + /// + /// The icon to display. public static Effect Icon(EffectIcon icon) { return NWScript.EffectIcon((int)icon)!; } + /// + /// Creates an immunity effect. This provides immunity to a specific effect type. + /// + /// The type of immunity. public static Effect Immunity(ImmunityType immunityType) { return NWScript.EffectImmunity((int)immunityType)!; } + /// + /// Creates an invisiblity effect. Behaviour is defined from the specified invisibility type. + /// + /// The type of invisibility to apply. public static Effect Invisibility(InvisibilityType invisibilityType) { return NWScript.EffectInvisibility((int)invisibilityType)!; } + /// + /// Creates a knockdown effect. This effect knocks creatures off their feet, they will sit until the effect is removed. + /// public static Effect Knockdown() { return NWScript.EffectKnockdown()!; @@ -441,56 +470,104 @@ public static Effect LinkEffects(Effect baseEffect, IEnumerable effects) return current; } + /// + /// Creates a new effect used to hinder the attacks of the creature it is applied to. + /// + /// A positive number representing the percentage chance to miss. + /// The type of attack that will have the hindered attack. public static Effect MissChance(int missPct, MissChanceType missChanceType = MissChanceType.Normal) { return NWScript.EffectMissChance(missPct, (int)missChanceType)!; } + /// + /// Creates an effect that modifies the amount of attacks a creature can perform. + /// + /// The number of attacks to add to the target (max 5). public static Effect ModifyAttacks(int numAttacks) { return NWScript.EffectModifyAttacks(numAttacks)!; } + /// + /// Creates an effect that reduces the movement speed of a creature. + /// + /// A positive number representing the percentage speed to decrease (0-99) public static Effect MovementSpeedDecrease(int pctChange) { return NWScript.EffectMovementSpeedDecrease(pctChange)!; } + /// + /// Creates an effect that increases the movement speed of a creature. + /// + /// A positive number representing the percentage speed to increase (0-99) public static Effect MovementSpeedIncrease(int pctChange) { return NWScript.EffectMovementSpeedIncrease(pctChange)!; } + /// + /// Creates an effect that will "decrease" the level of the target. + /// + /// The number of negative levels to apply. public static Effect NegativeLevel(int numLevels) { return NWScript.EffectNegativeLevel(numLevels)!; } + /// + /// Creates a paralyze effect. + /// + /// + /// Paralysis is not a mind effect - it stops all movement (and also in-game pauses the creature in their current action), and so they are prone, have no dexterity or strength (act as if had the lowest, 3) and all sneak attacks work against them.
+ /// By default creatures level 4 and below in 10 meters can be "coup de grace" ie instantly killed on a successful hit. + ///
public static Effect Paralyze() { return NWScript.EffectParalyze()!; } + /// + /// Creates an effect that will petrify a creature. It is similar to , but also applies the visual effect. + /// public static Effect Petrify() { return NWScript.EffectPetrify()!; } + /// + /// Creates a poison effect that sets the poisoned status effect, with varying penalties based on the type chosen. + /// + /// The type of poison to apply. public static Effect Poison(PoisonType poisonType) { return NWScript.EffectPoison((int)poisonType)!; } + /// + /// Creates a polymorph effect that changes the target into a different kind of creature. + /// + /// The polymorph to apply. + /// If true, players cannot dismiss the polymorph effect. public static Effect Polymorph(PolymorphType polymorphType, bool locked = false) { return NWScript.EffectPolymorph((int)polymorphType, locked.ToInt())!; } + /// + /// Creates a regeneration effect that will cause the target to heal the specified amount at each interval. + /// + /// The amount to heal. + /// The interval at which the healing is applied. public static Effect Regenerate(int amountPerInterval, TimeSpan interval) { return NWScript.EffectRegenerate(amountPerInterval, (float)interval.TotalSeconds)!; } + /// + /// Creates an effect that raises a creature who is dead to 1 hit point. + /// public static Effect Resurrection() { return NWScript.EffectResurrection()!; @@ -504,7 +581,6 @@ public static Effect Resurrection() /// The callback to invoke per interval. /// The interval in which to call onIntervalHandle. /// Optional string of data saved with the effect, retrievable with Effect.StringParams[0]. - /// The created effect. public static Effect RunAction(ScriptCallbackHandle? onAppliedHandle = null, ScriptCallbackHandle? onRemovedHandle = null, ScriptCallbackHandle? onIntervalHandle = null, TimeSpan interval = default, string data = "") { onAppliedHandle?.AssertValid(); @@ -514,141 +590,273 @@ public static Effect RunAction(ScriptCallbackHandle? onAppliedHandle = null, Scr return NWScript.EffectRunScript(onAppliedHandle?.ScriptName ?? string.Empty, onRemovedHandle?.ScriptName ?? string.Empty, onIntervalHandle?.ScriptName ?? string.Empty, (float)interval.TotalSeconds, data)!; } + /// + /// Creates a sanctuary effect. Sanctuary effects work similar to invisibility effects, but also cause creatures to be unable to hear the target creature, if they fail the DC. + /// + /// The difficulty class for other creatures to beat the effect, allowing them to see and hear the creature. public static Effect Sanctuary(int difficultyClass) { return NWScript.EffectSanctuary(difficultyClass)!; } + /// + /// Creates an effect to decrease one saving throw type. + /// + /// The saving throw to decrease. + /// The amount to decrease the saving throw. + /// The saving throw sub-type to decrease. public static Effect SavingThrowDecrease(SavingThrow savingThrow, int amount, SavingThrowType savingThrowType = SavingThrowType.All) { return NWScript.EffectSavingThrowDecrease((int)savingThrow, amount, (int)savingThrowType)!; } + /// + /// Creates an effect to increase one saving throw type. + /// + /// The saving throw to increase. + /// The amount to increase the saving throw. + /// The saving throw sub-type to increase. public static Effect SavingThrowIncrease(SavingThrow savingThrow, int amount, SavingThrowType savingThrowType = SavingThrowType.All) { return NWScript.EffectSavingThrowIncrease((int)savingThrow, amount, (int)savingThrowType)!; } + /// + /// Creates an effect that allows a creature to see through magical invisibility. + /// public static Effect SeeInvisible() { return NWScript.EffectSeeInvisible()!; } + /// + /// Creates an effect that silences a creature. Silent creatures may not cast spell with verbal components. + /// public static Effect Silence() { return NWScript.EffectSilence()!; } + /// + /// Creates an effect that decreases a specific skill score. + /// + /// The skill to decrease. + /// The amount to decrease the skill by. public static Effect SkillDecrease(NwSkill skill, int amount) { return NWScript.EffectSkillDecrease(skill.Id, amount)!; } + /// + /// Creates an effect that decreases all skill scores. + /// + /// The amount to decrease all skills by. public static Effect SkillDecreaseAll(int amount) { return NWScript.EffectSkillDecrease((int)Skill.AllSkills, amount)!; } + /// + /// Creates an effect that increases a specific skill score. + /// + /// The skill to increase. + /// The amount to increase the skill by. public static Effect SkillIncrease(NwSkill skill, int amount) { return NWScript.EffectSkillIncrease(skill.Id, amount)!; } + /// + /// Creates an effect that increases all skill scores. + /// + /// The amount to increase all skills by. public static Effect SkillIncreaseAll(int amount) { return NWScript.EffectSkillIncrease((int)Skill.AllSkills, amount)!; } + /// + /// Creates a sleep effect. Sleeping creatures may not act, and are subject to coup de grace (4 or less HD) + /// public static Effect Sleep() { return NWScript.EffectSleep()!; } + /// + /// Creates a slow effect. Slowed creatures have no benefits from any haste applied to them, or if they have no haste, have 1 attack per round, -2 AC, -2 attack, -2 Reflex Saving Throw, and -50% movement speed. + /// public static Effect Slow() { return NWScript.EffectSlow()!; } + /// + /// Creates an effect that inhibits spells. + /// + /// 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) { return NWScript.EffectSpellFailure(failPct, (int)spellSchool)!; } + /// + /// Creates an effect that provides immunity to a specific spell + /// + /// The spell to provide immunity for. public static Effect SpellImmunity(Spell spell = API.Spell.AllSpells) { return NWScript.EffectSpellImmunity((int)spell)!; } + /// + /// Creates an effect that absorbs a certain amount of spells. + /// + /// The max level spell that can be absorbed. + /// The total number of spells that can be absorbed. + /// The spell school to absorb specifically. public static Effect SpellLevelAbsorption(int maxSpellLevel, int totalSpellsAbsorbed = 0, SpellSchool spellSchool = SpellSchool.General) { return NWScript.EffectSpellLevelAbsorption(maxSpellLevel, totalSpellsAbsorbed, (int)spellSchool)!; } + /// + /// Creates an effect that decreases the spell resistance of a creature. + /// + /// The amount to decrease. public static Effect SpellResistanceDecrease(int amount) { return NWScript.EffectSpellResistanceDecrease(amount)!; } + /// + /// Creates an effect that increases the spell resistance of a creature. + /// + /// The amount to increase. public static Effect SpellResistanceIncrease(int amount) { return NWScript.EffectSpellResistanceIncrease(amount)!; } + /// + /// Creates a stun effect. This is a mind-effecting effect, and so immunity to mind effects prevents this from working.
+ /// Stunned creatures are flat-footed (no AC dex bonus), and cannot move. + ///
public static Effect Stunned() { return NWScript.EffectStunned()!; } + /// + /// Creates an effect to summon a creature.
+ /// 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 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) { return NWScript.EffectSummonCreature(creatureResRef, (int)vfxType, (float)delay.TotalSeconds, appearType)!; } + /// + /// 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. + /// The blueprint of the second creature to spawn. + /// The blueprint of the third creature to spawn. + /// The blueprint of the fourth creature to spawn. public static Effect Swarm(bool loop, string creatureTemplate1, string creatureTemplate2 = "", string creatureTemplate3 = "", string creatureTemplate4 = "") { return NWScript.EffectSwarm(loop.ToInt(), creatureTemplate1, creatureTemplate2, creatureTemplate3, creatureTemplate4)!; } + /// + /// Creates an effect that will give temporary hitpoints to a target. + /// + /// The amount of temporary hit points to add. public static Effect TemporaryHitpoints(int hitPoints) { return NWScript.EffectTemporaryHitpoints(hitPoints)!; } + /// + /// Creates a time stop effect.
+ /// Time stop applies a special module-wide pause which only the object it is applied to (and DM's) can move and cast spells without penalties or hindrance.
+ /// This is not recommended for multiplayer games as it will cause everyone in the module to pause.
+ /// You can query the current timestop state with . + ///
public static Effect TimeStop() { return NWScript.EffectTimeStop()!; } + /// + /// Creates a true seeing effect. Creatures with true sight can see through stealth, invisibility, sanctuary and darkness. + /// public static Effect TrueSeeing() { return NWScript.EffectTrueSeeing()!; } + /// + /// Creates a turned effect. Turning bypass all fear immunity effects, but should only be used in the turn undead script. + /// public static Effect Turned() { return NWScript.EffectTurned()!; } + /// + /// Creates an effect that decreases a creature's turn resistance, making them more susceptible to turning. + /// + /// A positive number representing the number of hit dice to decrease. public static Effect TurnResistanceDecrease(int hitDiceDecrease) { return NWScript.EffectTurnResistanceDecrease(hitDiceDecrease)!; } + /// + /// Creates an effect that increases a creature's turn resistance, making them more resistant to turning. + /// + /// A positive number representing the number of hit dice to increase. public static Effect TurnResistanceIncrease(int hitDiceIncrease) { return NWScript.EffectTurnResistanceIncrease(hitDiceIncrease)!; } + /// + /// Creates an Ultravision effect. Ultravision lets the target see through and effects with . + /// public static Effect Ultravision() { return NWScript.EffectUltravision()!; } + /// + /// Creates an effect that plays a visual effect when applied. + /// + /// The visual effect to apply. + /// If true, a random vector near or past the target will be generated, as the location for the effect. + /// A scaling factor to apply to the visual effect. + /// A translation vector transform to apply to the visual effect. + /// A rotation vector transform to apply to the visual effect. public static Effect VisualEffect(VfxType visualEffectId, bool missEffect = false, float fScale = 1.0f, System.Numerics.Vector3 vTranslate = default, System.Numerics.Vector3 vRotate = default) { return NWScript.EffectVisualEffect((int)visualEffectId, missEffect.ToInt(), fScale, vTranslate, vRotate)!; } + /// + /// Creates an effect that plays a visual effect when applied. + /// + /// The visual effect to apply. + /// If true, a random vector near or past the target will be generated, as the location for the effect. + /// A scaling factor to apply to the visual effect. + /// A translation vector transform to apply to the visual effect. + /// A rotation vector transform to apply to the visual effect. public static Effect VisualEffect(VisualEffectTableEntry visualEffect, bool missEffect = false, float fScale = 1.0f, System.Numerics.Vector3 vTranslate = default, System.Numerics.Vector3 vRotate = default) { return NWScript.EffectVisualEffect(visualEffect.RowIndex, missEffect.ToInt(), fScale, vTranslate, vRotate)!; diff --git a/NWN.Anvil/src/main/API/EngineStructures/Effect.cs b/NWN.Anvil/src/main/API/EngineStructures/Effect.cs index 2a6ec5f92..f670d961b 100644 --- a/NWN.Anvil/src/main/API/EngineStructures/Effect.cs +++ b/NWN.Anvil/src/main/API/EngineStructures/Effect.cs @@ -4,6 +4,12 @@ namespace Anvil.API { + //! ## Examples + //! @import EffectExamples.cs + + /// + /// Represents an effect engine structure. + /// public sealed partial class Effect : EffectBase { internal Effect(CGameEffect effect, bool memoryOwn) : base(effect, memoryOwn) {} diff --git a/NWN.Anvil/src/main/API/EngineStructures/EffectBase.cs b/NWN.Anvil/src/main/API/EngineStructures/EffectBase.cs index 8b815944d..cba0d979b 100644 --- a/NWN.Anvil/src/main/API/EngineStructures/EffectBase.cs +++ b/NWN.Anvil/src/main/API/EngineStructures/EffectBase.cs @@ -74,6 +74,9 @@ public bool Expose ///
public EffectParams ObjectParams { get; } + /// + /// Gets or sets if this effect should show an icon in the top right and in the character sheet and examine panels. + /// public bool ShowIcon { get => Effect.m_bShowIcon.ToBool(); diff --git a/NWN.Anvil/src/main/API/EngineStructures/EffectParams.cs b/NWN.Anvil/src/main/API/EngineStructures/EffectParams.cs index b121bbb8c..727632251 100644 --- a/NWN.Anvil/src/main/API/EngineStructures/EffectParams.cs +++ b/NWN.Anvil/src/main/API/EngineStructures/EffectParams.cs @@ -4,6 +4,10 @@ namespace Anvil.API { + /// + /// Represents an array of parameters. + /// + /// The parameter type. public sealed class EffectParams : IReadOnlyList { private readonly Func get; diff --git a/NWN.Anvil/src/main/API/EngineStructures/HitEffect.cs b/NWN.Anvil/src/main/API/EngineStructures/HitEffect.cs index d19020986..3f28b8212 100644 --- a/NWN.Anvil/src/main/API/EngineStructures/HitEffect.cs +++ b/NWN.Anvil/src/main/API/EngineStructures/HitEffect.cs @@ -1,5 +1,8 @@ namespace Anvil.API { + /// + /// Represents an "On Hit" item property effect, for use with . + /// public readonly struct HitEffect { internal readonly int Property; @@ -11,126 +14,245 @@ internal HitEffect(IPOnHit property, int special = 0) Special = special; } + /// + /// Creates a property that drains the specified ability score on a successful hit. + /// + /// The ability score to drain. + /// The created HitEffect. public static HitEffect AbilityDrain(IPAbility ability) { return new HitEffect(IPOnHit.AbilityDrain, (int)ability); } + /// + /// Creates a property that applies a blind effect on a successful hit. + /// + /// The duration of the effect. + /// The created HitEffect. public static HitEffect Blindness(IPOnHitDuration duration) { return new HitEffect(IPOnHit.Blindness, (int)duration); } + /// + /// Creates a property that applies a confusion effect on a successful hit. + /// + /// The duration of the effect. + /// The created HitEffect. public static HitEffect Confusion(IPOnHitDuration duration) { return new HitEffect(IPOnHit.Confusion, (int)duration); } + /// + /// Creates a property that applies a daze effect on a successful hit. + /// + /// The duration of the effect. + /// The created HitEffect. public static HitEffect Daze(IPOnHitDuration duration) { return new HitEffect(IPOnHit.Daze, (int)duration); } + /// + /// Creates a property that applies a deafness effect on a successful hit. + /// + /// The duration of the effect. + /// The created HitEffect. public static HitEffect Deafness(IPOnHitDuration duration) { return new HitEffect(IPOnHit.Deafness, (int)duration); } + /// + /// Creates a property that applies a disease effect on a successful hit. + /// + /// The disease type to apply. + /// The created HitEffect. public static HitEffect Disease(DiseaseType diseaseType) { return new HitEffect(IPOnHit.Disease, (int)diseaseType); } + /// + /// Creates a property that dispels magical effects on a successful hit. + /// + /// The created HitEffect. public static HitEffect DispelMagic() { return new HitEffect(IPOnHit.DispelMagic); } + /// + /// Creates a property that applies a doom effect on a successful hit. + /// + /// The duration of the effect. + /// The created HitEffect. public static HitEffect Doom(IPOnHitDuration duration) { return new HitEffect(IPOnHit.Doom, (int)duration); } + /// + /// Creates a property that applies a fear effect on a successful hit. + /// + /// The duration of the effect. + /// The created HitEffect. public static HitEffect Fear(IPOnHitDuration duration) { return new HitEffect(IPOnHit.Fear, (int)duration); } + /// + /// Creates a property that dispels magical effects on a successful hit. + /// + /// The created HitEffect. public static HitEffect GreaterDispel() { return new HitEffect(IPOnHit.GreaterDispel); } + /// + /// Creates a property that applies a hold effect on a successful hit. + /// + /// The duration of the effect. + /// The created HitEffect. public static HitEffect Hold(IPOnHitDuration duration) { return new HitEffect(IPOnHit.Hold, (int)duration); } + /// + /// Creates a property that applies a poison effect on a successful hit. + /// + /// The poison type to apply. + /// The created HitEffect. public static HitEffect ItemPoison(IPPoisonDamage poisonType) { return new HitEffect(IPOnHit.ItemPoison, (int)poisonType); } + /// + /// Creates a property that applies a knock effect on a successful hit. + /// + /// The created HitEffect. public static HitEffect Knock() { return new HitEffect(IPOnHit.Knock); } + /// + /// Creates a property that dispels magical effects on a successful hit. + /// + /// The created HitEffect. public static HitEffect LesserDispel() { return new HitEffect(IPOnHit.LesserDispel); } + /// + /// Creates a property that applies a level drain effect on a successful hit. + /// + /// The amount of levels to drain per hit. + /// The created HitEffect. public static HitEffect LevelDrain(int levelDrain = 1) { return new HitEffect(IPOnHit.LevelDrain, levelDrain); } + /// + /// Creates a property that dispels magical effects on a successful hit. + /// + /// The created HitEffect. public static HitEffect MordsDisjunction() { return new HitEffect(IPOnHit.MordsDisjunction); } + /// + /// Creates a property that applies a silence effect on a successful hit. + /// + /// The duration of the effect. + /// The created HitEffect. public static HitEffect Silence(IPOnHitDuration duration) { return new HitEffect(IPOnHit.Silence, (int)duration); } + /// + /// Creates a property that instantly kills a creature of the specified alignment on a successful hit. + /// + /// The alignment that will be slain. + /// The created HitEffect. public static HitEffect SlayAlignment(IPAlignment alignment) { return new HitEffect(IPOnHit.SlayAlignment, (int)alignment); } + /// + /// Creates a property that instantly kills a creature of the specified alignment group on a successful hit. + /// + /// The alignment group that will be slain. + /// The created HitEffect. public static HitEffect SlayAlignmentGroup(IPAlignmentGroup alignmentGroup) { return new HitEffect(IPOnHit.SlayAlignmentGroup, (int)alignmentGroup); } + /// + /// Creates a property that instantly kills a creature of the specified race on a successful hit. + /// + /// The racial type that will be slain. + /// The created HitEffect. public static HitEffect SlayRace(IPRacialType racialType) { return new HitEffect(IPOnHit.SlayRace, (int)racialType); } + /// + /// Creates a property that applies a sleep effect on a successful hit. + /// + /// The duration of the effect. + /// The created HitEffect. public static HitEffect Sleep(IPOnHitDuration duration) { return new HitEffect(IPOnHit.Sleep, (int)duration); } + /// + /// Creates a property that applies a slow effect on a successful hit. + /// + /// The duration of the effect. + /// The created HitEffect. public static HitEffect Slow(IPOnHitDuration duration) { return new HitEffect(IPOnHit.Slow, (int)duration); } + /// + /// Creates a property that applies a stun effect on a successful hit. + /// + /// The duration of the effect. + /// The created HitEffect. public static HitEffect Stun(IPOnHitDuration duration) { return new HitEffect(IPOnHit.Stun, (int)duration); } + /// + /// Creates a property that instantly kills a creature on a critical hit. + /// + /// The created HitEffect. public static HitEffect Vorpal() { return new HitEffect(IPOnHit.Vorpal); } + /// + /// Creates a property that applies a bleeding damage over time effect until the creature is healed. + /// + /// The damage to apply per round. + /// The created HitEffect. public static HitEffect Wounding(int bleedDamage) { return new HitEffect(IPOnHit.Wounding, bleedDamage); diff --git a/NWN.Anvil/src/main/API/EngineStructures/ItemProperty.cs b/NWN.Anvil/src/main/API/EngineStructures/ItemProperty.cs index e3f0e3d91..486f85e99 100644 --- a/NWN.Anvil/src/main/API/EngineStructures/ItemProperty.cs +++ b/NWN.Anvil/src/main/API/EngineStructures/ItemProperty.cs @@ -4,6 +4,12 @@ namespace Anvil.API { + //! ## Examples + //! @import ItemPropertyExamples.cs + + /// + /// Represents an item property effect engine structure. + /// public sealed partial class ItemProperty : EffectBase { internal ItemProperty(CGameEffect effect, bool memoryOwn) : base(effect, memoryOwn) {} diff --git a/NWN.Anvil/src/main/API/EngineStructures/Json.cs b/NWN.Anvil/src/main/API/EngineStructures/Json.cs index 62f87caad..d2c7e720c 100644 --- a/NWN.Anvil/src/main/API/EngineStructures/Json.cs +++ b/NWN.Anvil/src/main/API/EngineStructures/Json.cs @@ -3,6 +3,11 @@ namespace Anvil.API { + /// + /// Represents a json engine structure. + /// + /// This class is specifically for compatibility with the base game.
+ /// It is recommended to use and for all json-related problems in C#.
public sealed class Json : EngineStructure { internal Json(IntPtr handle, bool memoryOwn) : base(handle, memoryOwn) {} @@ -14,21 +19,44 @@ public static implicit operator Json(IntPtr intPtr) return new Json(intPtr, true); } + /// + /// Parses the specified string as a json structure. + /// + /// The json string to parse. + /// The parsed json structure. public static Json Parse(string jsonString) { return NWScript.JsonParse(jsonString); } + /// + /// Dumps the content of the json object to a string. + /// + /// public string Dump() { return NWScript.JsonDump(this); } + /// + /// Attempts to parse this json structure into a . + /// + /// The location to spawn the parsed object. + /// If this object is an item, the owner of the parsed item. + /// If the object should load object state info from the json structure. + /// The created object, or null if parsing failed. public NwObject? ToNwObject(Location location, NwGameObject? owner = null, bool loadObjectState = true) { return NWScript.JsonToObject(this, location, owner, loadObjectState.ToInt()).ToNwObject(); } + /// + /// Attempts to parse this json structure into a . + /// + /// The location to spawn the parsed object. + /// If this object is an item, the owner of the parsed item. + /// If the object should load object state info from the json structure. + /// The created object, or null if parsing failed. public T? ToNwObject(Location location, NwGameObject? owner = null, bool loadObjectState = true) where T : NwObject { return NWScript.JsonToObject(this, location, owner, loadObjectState.ToInt()).ToNwObject(); diff --git a/NWN.Anvil/src/main/API/EngineStructures/Location.cs b/NWN.Anvil/src/main/API/EngineStructures/Location.cs index a0d6f34f0..978e2f98a 100644 --- a/NWN.Anvil/src/main/API/EngineStructures/Location.cs +++ b/NWN.Anvil/src/main/API/EngineStructures/Location.cs @@ -1,10 +1,14 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Numerics; using NWN.Core; namespace Anvil.API { + /// + /// A location in the module, represented by area, position and orientation. + /// public sealed class Location : EngineStructure { internal Location(IntPtr handle, bool memoryOwn) : base(handle, memoryOwn) {} @@ -12,7 +16,7 @@ internal Location(IntPtr handle, bool memoryOwn) : base(handle, memoryOwn) {} /// /// Gets the associated Area of this location. /// - public NwArea? Area => NWScript.GetAreaFromLocation(this).ToNwObject(); + public NwArea Area => NWScript.GetAreaFromLocation(this).ToNwObject()!; /// /// Gets the inverted rotation value of this location (placeables). @@ -71,9 +75,17 @@ internal Location(IntPtr handle, bool memoryOwn) : base(handle, memoryOwn) {} protected override int StructureId => NWScript.ENGINE_STRUCTURE_LOCATION; - public static Location Create(NwArea area, Vector3 position, float orientation) + /// + /// Create a new location from the specified area, position and orientation + /// + /// The area of the location. + /// The position of the location. + /// The rotation of the location. + /// The created location. + [return: NotNullIfNotNull("area")] + public static Location? Create(NwArea area, Vector3 position, float orientation) { - return NWScript.Location(area, position, orientation)!; + return NWScript.Location(area, position, orientation); } public static implicit operator Location?(IntPtr intPtr) @@ -136,21 +148,39 @@ public float DistanceSquared(Location target) return Vector3.DistanceSquared(target.Position, Position); } + /// + /// Gets all creatures near this location, ordered by distance. + /// public IEnumerable GetNearestCreatures() { return GetNearestCreatures(CreatureTypeFilter.None, CreatureTypeFilter.None, CreatureTypeFilter.None); } + /// + /// Gets all creatures near this location, ordered by distance. + /// + /// A filter for the returned creatures. public IEnumerable GetNearestCreatures(CreatureTypeFilter filter1) { return GetNearestCreatures(filter1, CreatureTypeFilter.None, CreatureTypeFilter.None); } + /// + /// Gets all creatures near this location, ordered by distance. + /// + /// A filter for the returned creatures. + /// A 2nd filter for the returned creatures. public IEnumerable GetNearestCreatures(CreatureTypeFilter filter1, CreatureTypeFilter filter2) { return GetNearestCreatures(filter1, filter2, CreatureTypeFilter.None); } + /// + /// Gets all creatures near this location, ordered by distance. + /// + /// A filter for the returned creatures. + /// A 2nd filter for the returned creatures. + /// A 3rd filter for the returned creatures. public IEnumerable GetNearestCreatures(CreatureTypeFilter filter1, CreatureTypeFilter filter2, CreatureTypeFilter filter3) { int i; @@ -184,6 +214,9 @@ public IEnumerable GetNearestCreatures(CreatureTypeFilter filter1, C } } + /// + /// Gets all objects near this location, ordered by distance. + /// public IEnumerable GetNearestObjectsByType() where T : NwGameObject { int objType = (int)NwObject.GetObjectType(); @@ -199,6 +232,9 @@ public IEnumerable GetNearestObjectsByType() where T : NwGameObject } } + /// + /// Gets all objects in a shape at this location. + /// public IEnumerable GetObjectsInShape(Shape shape, float size, bool losCheck, ObjectTypes objTypes = ObjectTypes.Creature, Vector3 origin = default) { int typeFilter = (int)objTypes; @@ -216,6 +252,9 @@ public IEnumerable GetObjectsInShape(Shape shape, float size, bool } } + /// + /// Gets all objects in a shape at this location of the specified type. + /// public IEnumerable GetObjectsInShapeByType(Shape shape, float size, bool losCheck, Vector3 origin = default) where T : NwGameObject { int typeFilter = (int)NwObject.GetObjectType(); diff --git a/NWN.Anvil/src/main/API/EngineStructures/MonsterHitEffect.cs b/NWN.Anvil/src/main/API/EngineStructures/MonsterHitEffect.cs index aadbc0709..fa8a4b933 100644 --- a/NWN.Anvil/src/main/API/EngineStructures/MonsterHitEffect.cs +++ b/NWN.Anvil/src/main/API/EngineStructures/MonsterHitEffect.cs @@ -1,5 +1,8 @@ namespace Anvil.API { + /// + /// Represents a monster "On Hit" item property effect, for use with . + /// public readonly struct MonsterHitEffect { internal readonly int Property; @@ -11,51 +14,101 @@ internal MonsterHitEffect(IPOnMonsterHit property, int special = 0) Special = special; } + /// + /// Creates a property that drains the specified ability score on a successful hit. + /// + /// The ability score to drain. + /// The created HitEffect. public static MonsterHitEffect AbilityDrain(IPAbility ability) { return new MonsterHitEffect(IPOnMonsterHit.AbilityDrain, (int)ability); } + /// + /// Creates a property that applies a confusion effect on a successful hit. + /// + /// The duration of the effect. + /// The created HitEffect. public static MonsterHitEffect Confusion(IPOnHitDuration duration) { return new MonsterHitEffect(IPOnMonsterHit.Confusion, (int)duration); } + /// + /// Creates a property that applies a disease effect on a successful hit. + /// + /// The disease type to apply. + /// The created HitEffect. public static MonsterHitEffect Disease(DiseaseType diseaseType) { return new MonsterHitEffect(IPOnMonsterHit.Disease, (int)diseaseType); } + /// + /// Creates a property that applies a doom effect on a successful hit. + /// + /// The duration of the effect. + /// The created HitEffect. public static MonsterHitEffect Doom(IPOnHitDuration duration) { return new MonsterHitEffect(IPOnMonsterHit.Doom, (int)duration); } + /// + /// Creates a property that applies a fear effect on a successful hit. + /// + /// The duration of the effect. + /// The created HitEffect. public static MonsterHitEffect Fear(IPOnHitDuration duration) { return new MonsterHitEffect(IPOnMonsterHit.Fear, (int)duration); } + /// + /// Creates a property that applies a level drain effect on a successful hit. + /// + /// The amount of levels to drain per hit. + /// The created HitEffect. public static MonsterHitEffect LevelDrain(int levelDrain = 1) { return new MonsterHitEffect(IPOnMonsterHit.LevelDrain, levelDrain); } + /// + /// Creates a property that applies a poison effect on a successful hit. + /// + /// The poison type to apply. + /// The created HitEffect. public static MonsterHitEffect Poison(PoisonType poisonType) { return new MonsterHitEffect(IPOnMonsterHit.Poison, (int)poisonType); } + /// + /// Creates a property that applies a slow effect on a successful hit. + /// + /// The duration of the effect. + /// The created HitEffect. public static MonsterHitEffect Slow(IPOnHitDuration duration) { return new MonsterHitEffect(IPOnMonsterHit.Slow, (int)duration); } + /// + /// Creates a property that applies a stun effect on a successful hit. + /// + /// The duration of the effect. + /// The created HitEffect. public static MonsterHitEffect Stun(IPOnHitDuration duration) { return new MonsterHitEffect(IPOnMonsterHit.Stun, (int)duration); } + /// + /// Creates a property that applies a bleeding damage over time effect until the creature is healed. + /// + /// The damage to apply per round. + /// The created HitEffect. public static MonsterHitEffect Wounding(int bleedDamage) { return new MonsterHitEffect(IPOnMonsterHit.Wounding, bleedDamage); diff --git a/NWN.Anvil/src/main/API/EngineStructures/SQLResult.cs b/NWN.Anvil/src/main/API/EngineStructures/SQLResult.cs index 48fad8370..3ce2e5831 100644 --- a/NWN.Anvil/src/main/API/EngineStructures/SQLResult.cs +++ b/NWN.Anvil/src/main/API/EngineStructures/SQLResult.cs @@ -3,6 +3,9 @@ namespace Anvil.API { + /// + /// Represents a row returned in an SQL result. See /. + /// public sealed class SQLResult { private readonly SQLQuery query; diff --git a/NWN.Anvil/src/main/API/EngineStructures/Talent.cs b/NWN.Anvil/src/main/API/EngineStructures/Talent.cs index 9614b53d7..006708b34 100644 --- a/NWN.Anvil/src/main/API/EngineStructures/Talent.cs +++ b/NWN.Anvil/src/main/API/EngineStructures/Talent.cs @@ -3,6 +3,9 @@ namespace Anvil.API { + /// + /// Represents a talent structure. A talent is a feat, skill or spell possessed by a creature. + /// public sealed partial class Talent : EngineStructure { internal Talent(IntPtr handle, bool memoryOwn) : base(handle, memoryOwn) {} diff --git a/NWN.Anvil/src/main/API/EngineStructures/TalentExtensions.cs b/NWN.Anvil/src/main/API/EngineStructures/TalentExtensions.cs index d87de051d..1ee189151 100644 --- a/NWN.Anvil/src/main/API/EngineStructures/TalentExtensions.cs +++ b/NWN.Anvil/src/main/API/EngineStructures/TalentExtensions.cs @@ -2,16 +2,25 @@ namespace Anvil.API { public static class TalentExtensions { + /// + /// Converts the specified skill into a . + /// public static Talent ToTalent(this NwSkill skill) { return skill; } + /// + /// Converts the specified spell into a . + /// public static Talent ToTalent(this NwSpell spell) { return spell; } + /// + /// Converts the specified feat into a . + /// public static Talent ToTalent(this NwFeat feat) { return feat; diff --git a/NWN.Anvil/src/main/API/Events/Game/AreaEvents/AreaEvents.OnEnter.cs b/NWN.Anvil/src/main/API/Events/Game/AreaEvents/AreaEvents.OnEnter.cs index fb964099f..418ada530 100644 --- a/NWN.Anvil/src/main/API/Events/Game/AreaEvents/AreaEvents.OnEnter.cs +++ b/NWN.Anvil/src/main/API/Events/Game/AreaEvents/AreaEvents.OnEnter.cs @@ -4,6 +4,9 @@ namespace Anvil.API.Events { + /// + /// Built-in events associated with a specific area. + /// public static partial class AreaEvents { /// diff --git a/NWN.Anvil/src/main/API/Events/Game/AreaEvents/AreaEvents.OnExit.cs b/NWN.Anvil/src/main/API/Events/Game/AreaEvents/AreaEvents.OnExit.cs index 58799ce93..f910fb3be 100644 --- a/NWN.Anvil/src/main/API/Events/Game/AreaEvents/AreaEvents.OnExit.cs +++ b/NWN.Anvil/src/main/API/Events/Game/AreaEvents/AreaEvents.OnExit.cs @@ -4,6 +4,9 @@ namespace Anvil.API.Events { + /// + /// Built-in events associated with a specific area. + /// public static partial class AreaEvents { /// diff --git a/NWN.Anvil/src/main/API/Events/Game/AreaEvents/AreaEvents.OnHeartbeat.cs b/NWN.Anvil/src/main/API/Events/Game/AreaEvents/AreaEvents.OnHeartbeat.cs index 919b47ef9..dec6ee557 100644 --- a/NWN.Anvil/src/main/API/Events/Game/AreaEvents/AreaEvents.OnHeartbeat.cs +++ b/NWN.Anvil/src/main/API/Events/Game/AreaEvents/AreaEvents.OnHeartbeat.cs @@ -4,6 +4,9 @@ namespace Anvil.API.Events { + /// + /// Built-in events associated with a specific area. + /// public static partial class AreaEvents { /// diff --git a/NWN.Anvil/src/main/API/Events/Game/AreaEvents/AreaEvents.OnUserDefined.cs b/NWN.Anvil/src/main/API/Events/Game/AreaEvents/AreaEvents.OnUserDefined.cs index 435cf87a6..b8e4bf6e5 100644 --- a/NWN.Anvil/src/main/API/Events/Game/AreaEvents/AreaEvents.OnUserDefined.cs +++ b/NWN.Anvil/src/main/API/Events/Game/AreaEvents/AreaEvents.OnUserDefined.cs @@ -4,6 +4,9 @@ namespace Anvil.API.Events { + /// + /// Built-in events associated with a specific area. + /// public static partial class AreaEvents { [GameEvent(EventScriptType.AreaOnUserDefinedEvent)] diff --git a/NWN.Anvil/src/main/API/Events/Game/AreaOfEffectEvents/AreaOfEffectEvents.OnEnter.cs b/NWN.Anvil/src/main/API/Events/Game/AreaOfEffectEvents/AreaOfEffectEvents.OnEnter.cs index 2da84f9b9..9f8f0b62c 100644 --- a/NWN.Anvil/src/main/API/Events/Game/AreaOfEffectEvents/AreaOfEffectEvents.OnEnter.cs +++ b/NWN.Anvil/src/main/API/Events/Game/AreaOfEffectEvents/AreaOfEffectEvents.OnEnter.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for effects created with . + /// Built-in events for effects created with . /// public static partial class AreaOfEffectEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/AreaOfEffectEvents/AreaOfEffectEvents.OnExit.cs b/NWN.Anvil/src/main/API/Events/Game/AreaOfEffectEvents/AreaOfEffectEvents.OnExit.cs index 34f509565..a7b56160a 100644 --- a/NWN.Anvil/src/main/API/Events/Game/AreaOfEffectEvents/AreaOfEffectEvents.OnExit.cs +++ b/NWN.Anvil/src/main/API/Events/Game/AreaOfEffectEvents/AreaOfEffectEvents.OnExit.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for effects created with . + /// Built-in events for effects created with . /// public static partial class AreaOfEffectEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/AreaOfEffectEvents/AreaOfEffectEvents.OnHeartbeat.cs b/NWN.Anvil/src/main/API/Events/Game/AreaOfEffectEvents/AreaOfEffectEvents.OnHeartbeat.cs index 26c312143..74110b7df 100644 --- a/NWN.Anvil/src/main/API/Events/Game/AreaOfEffectEvents/AreaOfEffectEvents.OnHeartbeat.cs +++ b/NWN.Anvil/src/main/API/Events/Game/AreaOfEffectEvents/AreaOfEffectEvents.OnHeartbeat.cs @@ -1,12 +1,11 @@ using System; using Anvil.API.Events; -using Anvil.Services; using NWN.Core; namespace Anvil.API.Events { /// - /// Events for effects created with . + /// Built-in events for effects created with . /// public static partial class AreaOfEffectEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/AreaOfEffectEvents/AreaOfEffectEvents.OnUserDefined.cs b/NWN.Anvil/src/main/API/Events/Game/AreaOfEffectEvents/AreaOfEffectEvents.OnUserDefined.cs index e5854b63f..c2254705f 100644 --- a/NWN.Anvil/src/main/API/Events/Game/AreaOfEffectEvents/AreaOfEffectEvents.OnUserDefined.cs +++ b/NWN.Anvil/src/main/API/Events/Game/AreaOfEffectEvents/AreaOfEffectEvents.OnUserDefined.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for effects created with . + /// Built-in events for effects created with . /// public static partial class AreaOfEffectEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnBlocked.cs b/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnBlocked.cs index 37090f33f..4bc448cca 100644 --- a/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnBlocked.cs +++ b/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnBlocked.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for . + /// Built-in events associated with a specific creature. /// public static partial class CreatureEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnCombatRoundEnd.cs b/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnCombatRoundEnd.cs index 496f8420d..d2bad21d3 100644 --- a/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnCombatRoundEnd.cs +++ b/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnCombatRoundEnd.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for . + /// Built-in events associated with a specific creature. /// public static partial class CreatureEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnConversation.cs b/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnConversation.cs index 33fda05ae..06d9f4afd 100644 --- a/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnConversation.cs +++ b/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnConversation.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for . + /// Built-in events associated with a specific creature. /// public static partial class CreatureEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnDamaged.cs b/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnDamaged.cs index dc8285676..0e7648f3c 100644 --- a/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnDamaged.cs +++ b/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnDamaged.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for . + /// Built-in events associated with a specific creature. /// public static partial class CreatureEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnDeath.cs b/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnDeath.cs index 7e2430cdb..bd70642b9 100644 --- a/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnDeath.cs +++ b/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnDeath.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for . + /// Built-in events associated with a specific creature. /// public static partial class CreatureEvents { 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 75ce60f95..2f474ea55 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 @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for . + /// Built-in events associated with a specific creature. /// public static partial class CreatureEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnHeartbeat.cs b/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnHeartbeat.cs index ed512980e..f1097b7fa 100644 --- a/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnHeartbeat.cs +++ b/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnHeartbeat.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for . + /// Built-in events associated with a specific creature. /// public static partial class CreatureEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnPerception.cs b/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnPerception.cs index ab6fba998..cd5fe4cc5 100644 --- a/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnPerception.cs +++ b/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnPerception.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for . + /// Built-in events associated with a specific creature. /// public static partial class CreatureEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnPhysicalAttacked.cs b/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnPhysicalAttacked.cs index 357e821ba..fc7aa750c 100644 --- a/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnPhysicalAttacked.cs +++ b/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnPhysicalAttacked.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for . + /// Built-in events associated with a specific creature. /// public static partial class CreatureEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnRested.cs b/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnRested.cs index 97cc68fa6..51093e85c 100644 --- a/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnRested.cs +++ b/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnRested.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for . + /// Built-in events associated with a specific creature. /// public static partial class CreatureEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnSpawn.cs b/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnSpawn.cs index 9752e1d21..c109bfa1c 100644 --- a/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnSpawn.cs +++ b/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnSpawn.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for . + /// Built-in events associated with a specific creature. /// public static partial class CreatureEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnSpellCastAt.cs b/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnSpellCastAt.cs index 973ae1012..deee349de 100644 --- a/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnSpellCastAt.cs +++ b/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnSpellCastAt.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for . + /// Built-in events associated with a specific creature. /// public static partial class CreatureEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnUserDefined.cs b/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnUserDefined.cs index 5a90788cc..40a94751a 100644 --- a/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnUserDefined.cs +++ b/NWN.Anvil/src/main/API/Events/Game/CreatureEvents/CreatureEvents.OnUserDefined.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for . + /// Built-in events associated with a specific creature. /// public static partial class CreatureEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnAreaTransitionClick.cs b/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnAreaTransitionClick.cs index 160146f4f..ee6c2c23f 100644 --- a/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnAreaTransitionClick.cs +++ b/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnAreaTransitionClick.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for door objects. + /// Built-in events associated with a specific door. /// public static partial class DoorEvents { 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 cef4dc5c1..b12ea475a 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 @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for door objects. + /// Built-in events associated with a specific door. /// public static partial class DoorEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnConversation.cs b/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnConversation.cs index c35b5a8a9..7757ce581 100644 --- a/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnConversation.cs +++ b/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnConversation.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for door objects. + /// Built-in events associated with a specific door. /// public static partial class DoorEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnDamaged.cs b/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnDamaged.cs index 159b8ea69..56ba5a33f 100644 --- a/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnDamaged.cs +++ b/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnDamaged.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for door objects. + /// Built-in events associated with a specific door. /// public static partial class DoorEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnDeath.cs b/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnDeath.cs index 116234d41..26bb04982 100644 --- a/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnDeath.cs +++ b/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnDeath.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for door objects. + /// Built-in events associated with a specific door. /// public static partial class DoorEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnDialogue.cs b/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnDialogue.cs index d00531197..aed86606a 100644 --- a/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnDialogue.cs +++ b/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnDialogue.cs @@ -7,7 +7,7 @@ namespace Anvil.API.Events { /// - /// Events for door objects. + /// Built-in events associated with a specific door. /// public static partial class DoorEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnDisarm.cs b/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnDisarm.cs index 74bfc7f1f..1630b44fe 100644 --- a/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnDisarm.cs +++ b/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnDisarm.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for door objects. + /// Built-in events associated with a specific door. /// public static partial class DoorEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnFailToOpen.cs b/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnFailToOpen.cs index fda726efb..ca32960c3 100644 --- a/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnFailToOpen.cs +++ b/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnFailToOpen.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for door objects. + /// Built-in events associated with a specific door. /// public static partial class DoorEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnHeartbeat.cs b/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnHeartbeat.cs index 25a0af6dc..ed053f602 100644 --- a/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnHeartbeat.cs +++ b/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnHeartbeat.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for door objects. + /// Built-in events associated with a specific door. /// public static partial class DoorEvents { 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 ed0287721..fe7c491e2 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 @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for door objects. + /// Built-in events associated with a specific door. /// public static partial class DoorEvents { 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 6b4f4c037..0ad33e73c 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 @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for door objects. + /// Built-in events associated with a specific door. /// public static partial class DoorEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnPhysicalAttacked.cs b/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnPhysicalAttacked.cs index 842f24715..7056c22ef 100644 --- a/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnPhysicalAttacked.cs +++ b/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnPhysicalAttacked.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for door objects. + /// Built-in events associated with a specific door. /// public static partial class DoorEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnSpellCastAt.cs b/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnSpellCastAt.cs index 78b065724..4ae548a37 100644 --- a/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnSpellCastAt.cs +++ b/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnSpellCastAt.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for door objects. + /// Built-in events associated with a specific door. /// public static partial class DoorEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnTrapTriggered.cs b/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnTrapTriggered.cs index 4ff2ebbc3..1b7621694 100644 --- a/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnTrapTriggered.cs +++ b/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnTrapTriggered.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for door objects. + /// Built-in events associated with a specific door. /// public static partial class DoorEvents { 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 e3b815122..91c9a9b0b 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 @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for door objects. + /// Built-in events associated with a specific door. /// public static partial class DoorEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnUserDefined.cs b/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnUserDefined.cs index f1c838e08..66f04f58d 100644 --- a/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnUserDefined.cs +++ b/NWN.Anvil/src/main/API/Events/Game/DoorEvents/DoorEvents.OnUserDefined.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for door objects. + /// Built-in events associated with a specific door. /// public static partial class DoorEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/EncounterEvents/EncounterEvents.OnEnter.cs b/NWN.Anvil/src/main/API/Events/Game/EncounterEvents/EncounterEvents.OnEnter.cs index 9d744d3b2..74822de1e 100644 --- a/NWN.Anvil/src/main/API/Events/Game/EncounterEvents/EncounterEvents.OnEnter.cs +++ b/NWN.Anvil/src/main/API/Events/Game/EncounterEvents/EncounterEvents.OnEnter.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for triggers. + /// Built-in events associated with a specific encounter. /// public static partial class EncounterEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/EncounterEvents/EncounterEvents.OnExhausted.cs b/NWN.Anvil/src/main/API/Events/Game/EncounterEvents/EncounterEvents.OnExhausted.cs index a8e54c855..864b611b4 100644 --- a/NWN.Anvil/src/main/API/Events/Game/EncounterEvents/EncounterEvents.OnExhausted.cs +++ b/NWN.Anvil/src/main/API/Events/Game/EncounterEvents/EncounterEvents.OnExhausted.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for triggers. + /// Built-in events associated with a specific encounter. /// public static partial class EncounterEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/EncounterEvents/EncounterEvents.OnExit.cs b/NWN.Anvil/src/main/API/Events/Game/EncounterEvents/EncounterEvents.OnExit.cs index 4fdcebc08..5cf5204d2 100644 --- a/NWN.Anvil/src/main/API/Events/Game/EncounterEvents/EncounterEvents.OnExit.cs +++ b/NWN.Anvil/src/main/API/Events/Game/EncounterEvents/EncounterEvents.OnExit.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for triggers. + /// Built-in events associated with a specific encounter. /// public static partial class EncounterEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/EncounterEvents/EncounterEvents.OnHeartbeat.cs b/NWN.Anvil/src/main/API/Events/Game/EncounterEvents/EncounterEvents.OnHeartbeat.cs index 3b2a1fbc7..d6bf9872b 100644 --- a/NWN.Anvil/src/main/API/Events/Game/EncounterEvents/EncounterEvents.OnHeartbeat.cs +++ b/NWN.Anvil/src/main/API/Events/Game/EncounterEvents/EncounterEvents.OnHeartbeat.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for triggers. + /// Built-in events associated with a specific encounter. /// public static partial class EncounterEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/EncounterEvents/EncounterEvents.OnUserDefined.cs b/NWN.Anvil/src/main/API/Events/Game/EncounterEvents/EncounterEvents.OnUserDefined.cs index 1024d0deb..268276255 100644 --- a/NWN.Anvil/src/main/API/Events/Game/EncounterEvents/EncounterEvents.OnUserDefined.cs +++ b/NWN.Anvil/src/main/API/Events/Game/EncounterEvents/EncounterEvents.OnUserDefined.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for triggers. + /// Built-in events associated with a specific encounter. /// public static partial class EncounterEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/GameEventFactory.cs b/NWN.Anvil/src/main/API/Events/Game/GameEventFactory.cs index 12b8d83b5..705862178 100644 --- a/NWN.Anvil/src/main/API/Events/Game/GameEventFactory.cs +++ b/NWN.Anvil/src/main/API/Events/Game/GameEventFactory.cs @@ -7,6 +7,9 @@ namespace Anvil.API.Events { + /// + /// Event factory for built-in game events. + /// [ServiceBinding(typeof(IEventFactory))] [ServiceBinding(typeof(IScriptDispatcher))] public sealed partial class GameEventFactory : IEventFactory, IScriptDispatcher, IDisposable 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 25db77ad9..83345b1e3 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 @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for . + /// Built-in events associated with a specific placeable. /// public static partial class PlaceableEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnConversation.cs b/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnConversation.cs index b13faa547..b7e1a61cb 100644 --- a/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnConversation.cs +++ b/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnConversation.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for . + /// Built-in events associated with a specific placeable. /// public static partial class PlaceableEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnDamaged.cs b/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnDamaged.cs index 000421bf6..27791e166 100644 --- a/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnDamaged.cs +++ b/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnDamaged.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for . + /// Built-in events associated with a specific placeable. /// public static partial class PlaceableEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnDeath.cs b/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnDeath.cs index 82d974c8f..0ae8c8710 100644 --- a/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnDeath.cs +++ b/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnDeath.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for . + /// Built-in events associated with a specific placeable. /// public static partial class PlaceableEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnDialogue.cs b/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnDialogue.cs index d8bb5d1e8..0794b2577 100644 --- a/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnDialogue.cs +++ b/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnDialogue.cs @@ -7,7 +7,7 @@ namespace Anvil.API.Events { /// - /// Events for . + /// Built-in events associated with a specific placeable. /// public static partial class PlaceableEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnDisarm.cs b/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnDisarm.cs index 16f61a5bb..16be4e0c4 100644 --- a/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnDisarm.cs +++ b/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnDisarm.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for . + /// Built-in events associated with a specific placeable. /// public static partial class PlaceableEvents { 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 3990c366f..cd79b2b2f 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 @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for . + /// Built-in events associated with a specific placeable. /// public static partial class PlaceableEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnHeartbeat.cs b/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnHeartbeat.cs index 8369ac76d..d90f41750 100644 --- a/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnHeartbeat.cs +++ b/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnHeartbeat.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for . + /// Built-in events associated with a specific placeable. /// public static partial class PlaceableEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnLeftClick.cs b/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnLeftClick.cs index 7e3d2286a..b9fae8667 100644 --- a/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnLeftClick.cs +++ b/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnLeftClick.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for . + /// Built-in events associated with a specific placeable. /// public static partial class PlaceableEvents { 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 dd0c3d02d..a885a610e 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 @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for . + /// Built-in events associated with a specific placeable. /// public static partial class PlaceableEvents { 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 2598518c4..dbfb55815 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 @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for . + /// Built-in events associated with a specific placeable. /// public static partial class PlaceableEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnPhysicalAttacked.cs b/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnPhysicalAttacked.cs index 4f55cac58..a56957cb3 100644 --- a/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnPhysicalAttacked.cs +++ b/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnPhysicalAttacked.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for . + /// Built-in events associated with a specific placeable. /// public static partial class PlaceableEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnSpellCastAt.cs b/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnSpellCastAt.cs index 051c85f50..6fb0ec54d 100644 --- a/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnSpellCastAt.cs +++ b/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnSpellCastAt.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for . + /// Built-in events associated with a specific placeable. /// public static partial class PlaceableEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnTrapTriggered.cs b/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnTrapTriggered.cs index 25332f0de..ae83ea7e4 100644 --- a/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnTrapTriggered.cs +++ b/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnTrapTriggered.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for . + /// Built-in events associated with a specific placeable. /// public static partial class PlaceableEvents { 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 4f6208cd4..96dc1840f 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 @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for . + /// Built-in events associated with a specific placeable. /// public static partial class PlaceableEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnUsed.cs b/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnUsed.cs index 2c52c9e49..e346fe6b7 100644 --- a/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnUsed.cs +++ b/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnUsed.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for . + /// Built-in events associated with a specific placeable. /// public static partial class PlaceableEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnUserDefined.cs b/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnUserDefined.cs index 5936f771f..11e723ede 100644 --- a/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnUserDefined.cs +++ b/NWN.Anvil/src/main/API/Events/Game/PlaceableEvents/PlaceableEvents.OnUserDefined.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for . + /// Built-in events associated with a specific placeable. /// public static partial class PlaceableEvents { 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 4fd53ef12..866e4e548 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 @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for Merchant/Store objects. + /// Built-in events associated with a specific store. /// public static partial class StoreEvents { 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 794dc5766..d2fa7cc08 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 @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for Merchant/Store objects. + /// Built-in events associated with a specific store. /// public static partial class StoreEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/TriggerEvents/TriggerEvents.OnClicked.cs b/NWN.Anvil/src/main/API/Events/Game/TriggerEvents/TriggerEvents.OnClicked.cs index 206fca269..a6fa9c474 100644 --- a/NWN.Anvil/src/main/API/Events/Game/TriggerEvents/TriggerEvents.OnClicked.cs +++ b/NWN.Anvil/src/main/API/Events/Game/TriggerEvents/TriggerEvents.OnClicked.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for drawn, world-placed triggers. + /// Built-in events associated with a specific trigger. /// public static partial class TriggerEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/TriggerEvents/TriggerEvents.OnDisarmed.cs b/NWN.Anvil/src/main/API/Events/Game/TriggerEvents/TriggerEvents.OnDisarmed.cs index 367f05cf1..964651e50 100644 --- a/NWN.Anvil/src/main/API/Events/Game/TriggerEvents/TriggerEvents.OnDisarmed.cs +++ b/NWN.Anvil/src/main/API/Events/Game/TriggerEvents/TriggerEvents.OnDisarmed.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for drawn, world-placed triggers. + /// Built-in events associated with a specific trigger. /// public static partial class TriggerEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/TriggerEvents/TriggerEvents.OnEnter.cs b/NWN.Anvil/src/main/API/Events/Game/TriggerEvents/TriggerEvents.OnEnter.cs index 38b6b6b5c..514db12d3 100644 --- a/NWN.Anvil/src/main/API/Events/Game/TriggerEvents/TriggerEvents.OnEnter.cs +++ b/NWN.Anvil/src/main/API/Events/Game/TriggerEvents/TriggerEvents.OnEnter.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for drawn, world-placed triggers. + /// Built-in events associated with a specific trigger. /// public static partial class TriggerEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/TriggerEvents/TriggerEvents.OnExit.cs b/NWN.Anvil/src/main/API/Events/Game/TriggerEvents/TriggerEvents.OnExit.cs index d1155c49a..9015a8ddb 100644 --- a/NWN.Anvil/src/main/API/Events/Game/TriggerEvents/TriggerEvents.OnExit.cs +++ b/NWN.Anvil/src/main/API/Events/Game/TriggerEvents/TriggerEvents.OnExit.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for drawn, world-placed triggers. + /// Built-in events associated with a specific trigger. /// public static partial class TriggerEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/TriggerEvents/TriggerEvents.OnHeartbeat.cs b/NWN.Anvil/src/main/API/Events/Game/TriggerEvents/TriggerEvents.OnHeartbeat.cs index 00e8f6bce..96dfa8273 100644 --- a/NWN.Anvil/src/main/API/Events/Game/TriggerEvents/TriggerEvents.OnHeartbeat.cs +++ b/NWN.Anvil/src/main/API/Events/Game/TriggerEvents/TriggerEvents.OnHeartbeat.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for drawn, world-placed triggers. + /// Built-in events associated with a specific trigger. /// public static partial class TriggerEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/TriggerEvents/TriggerEvents.OnTrapTriggered.cs b/NWN.Anvil/src/main/API/Events/Game/TriggerEvents/TriggerEvents.OnTrapTriggered.cs index faf79f2cf..6c93ebf8d 100644 --- a/NWN.Anvil/src/main/API/Events/Game/TriggerEvents/TriggerEvents.OnTrapTriggered.cs +++ b/NWN.Anvil/src/main/API/Events/Game/TriggerEvents/TriggerEvents.OnTrapTriggered.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for drawn, world-placed triggers. + /// Built-in events associated with a specific trigger. /// public static partial class TriggerEvents { diff --git a/NWN.Anvil/src/main/API/Events/Game/TriggerEvents/TriggerEvents.OnUserDefined.cs b/NWN.Anvil/src/main/API/Events/Game/TriggerEvents/TriggerEvents.OnUserDefined.cs index 843bf8f77..8231b79eb 100644 --- a/NWN.Anvil/src/main/API/Events/Game/TriggerEvents/TriggerEvents.OnUserDefined.cs +++ b/NWN.Anvil/src/main/API/Events/Game/TriggerEvents/TriggerEvents.OnUserDefined.cs @@ -5,7 +5,7 @@ namespace Anvil.API.Events { /// - /// Events for drawn, world-placed triggers. + /// Built-in events associated with a specific trigger. /// public static partial class TriggerEvents { diff --git a/NWN.Anvil/src/main/API/Objects/NwDoor.cs b/NWN.Anvil/src/main/API/Objects/NwDoor.cs index 7177047f8..ad6624e0f 100644 --- a/NWN.Anvil/src/main/API/Objects/NwDoor.cs +++ b/NWN.Anvil/src/main/API/Objects/NwDoor.cs @@ -61,7 +61,7 @@ public DoorOpenState DoorOpenState /// The new tag to assign this door. Leave uninitialized/as null to use the template's tag. public static NwDoor? Create(string template, Location location, string? newTag = null) { - if (string.IsNullOrEmpty(template) || location.Area == null) + if (string.IsNullOrEmpty(template)) { return default; } diff --git a/NWN.Anvil/src/main/API/Objects/NwEncounter.cs b/NWN.Anvil/src/main/API/Objects/NwEncounter.cs index 1e135f2ff..e65b747a1 100644 --- a/NWN.Anvil/src/main/API/Objects/NwEncounter.cs +++ b/NWN.Anvil/src/main/API/Objects/NwEncounter.cs @@ -154,7 +154,7 @@ public int Spawns public static NwEncounter? Create(string template, Location location, string? newTag = null) { - if (string.IsNullOrEmpty(template) || location.Area == null) + if (string.IsNullOrEmpty(template)) { return default; } diff --git a/NWN.Anvil/src/main/API/Objects/NwGameObject.cs b/NWN.Anvil/src/main/API/Objects/NwGameObject.cs index 0d2604bc3..a722e888e 100644 --- a/NWN.Anvil/src/main/API/Objects/NwGameObject.cs +++ b/NWN.Anvil/src/main/API/Objects/NwGameObject.cs @@ -101,12 +101,7 @@ public virtual Location? Location return; } - NwArea? area = value.Area; - if (area == null) - { - return; - } - + NwArea area = value.Area; if (area == Area) { Position = value.Position; diff --git a/NWN.Anvil/src/main/API/Objects/NwPlaceable.cs b/NWN.Anvil/src/main/API/Objects/NwPlaceable.cs index 9ad7d16c3..f3eeb7c59 100644 --- a/NWN.Anvil/src/main/API/Objects/NwPlaceable.cs +++ b/NWN.Anvil/src/main/API/Objects/NwPlaceable.cs @@ -108,7 +108,7 @@ public bool Useable public static NwPlaceable? Create(string template, Location location, bool useAppearAnim = false, string newTag = "") { - location = Location.Create(location.Area!, location.Position, location.FlippedRotation); + location = Location.Create(location.Area, location.Position, location.FlippedRotation); return CreateInternal(template, location, useAppearAnim, newTag); } diff --git a/NWN.Anvil/src/main/API/Objects/NwSound.cs b/NWN.Anvil/src/main/API/Objects/NwSound.cs index 0d5a41010..a7bd2afa5 100644 --- a/NWN.Anvil/src/main/API/Objects/NwSound.cs +++ b/NWN.Anvil/src/main/API/Objects/NwSound.cs @@ -41,12 +41,7 @@ public sbyte Volume return default; } - CNWSArea? area = location.Area?.Area; - if (area == null) - { - return default; - } - + CNWSArea area = location.Area.Area; Vector position = location.Position.ToNativeVector(); Vector orientation = location.Rotation.ToVectorOrientation().ToNativeVector(); diff --git a/NWN.Anvil/src/main/API/Objects/NwTrigger.cs b/NWN.Anvil/src/main/API/Objects/NwTrigger.cs index a60db1744..9b9d96060 100644 --- a/NWN.Anvil/src/main/API/Objects/NwTrigger.cs +++ b/NWN.Anvil/src/main/API/Objects/NwTrigger.cs @@ -34,12 +34,7 @@ internal NwTrigger(CNWSTrigger trigger) : base(trigger) return default; } - CNWSArea? area = location.Area?.Area; - if (area == null) - { - return default; - } - + CNWSArea area = location.Area.Area; Vector position = location.Position.ToNativeVector(); Vector orientation = location.Rotation.ToVectorOrientation().ToNativeVector(); diff --git a/NWN.Anvil/src/main/API/Variables/Campaign/CampaignVariableObject.cs b/NWN.Anvil/src/main/API/Variables/Campaign/CampaignVariableObject.cs index 16588670b..5c459cc6b 100644 --- a/NWN.Anvil/src/main/API/Variables/Campaign/CampaignVariableObject.cs +++ b/NWN.Anvil/src/main/API/Variables/Campaign/CampaignVariableObject.cs @@ -30,7 +30,7 @@ public override T Value public T? GetValue(NwGameObject owner) { Location? location = owner.Location; - if (location == null || location.Area == null) + if (location == null) { return null; } diff --git a/NWN.Anvil/src/main/AnvilCore.cs b/NWN.Anvil/src/main/AnvilCore.cs index cdcda8872..f1adac1ee 100644 --- a/NWN.Anvil/src/main/AnvilCore.cs +++ b/NWN.Anvil/src/main/AnvilCore.cs @@ -37,6 +37,11 @@ private AnvilCore(IServiceManager serviceManager) this.serviceManager.CoreServiceContainer.InjectProperties(this); } + /// + /// Gets the specified anvil service instance. + /// + /// The service type to get. + /// The associated anvil service instance. public static T? GetService() { return instance.serviceManager.AnvilServiceContainer.GetInstance(); diff --git a/docs/NWN.Anvil.Samples.csproj.DotSettings b/docs/NWN.Anvil.Samples.csproj.DotSettings index 0e2a8ea01..ba1b46ebc 100644 --- a/docs/NWN.Anvil.Samples.csproj.DotSettings +++ b/docs/NWN.Anvil.Samples.csproj.DotSettings @@ -2,4 +2,5 @@ True True True + True True \ No newline at end of file diff --git a/docs/samples/API/EngineStructures/CassowaryExamples.cs b/docs/samples/API/EngineStructures/CassowaryExamples.cs new file mode 100644 index 000000000..cb3f91d0f --- /dev/null +++ b/docs/samples/API/EngineStructures/CassowaryExamples.cs @@ -0,0 +1,39 @@ +/* + * Examples for creating and using Cassowary solvers. + */ + +using Anvil.API; +using Anvil.Services; +using NLog; + +namespace NWN.Anvil.Samples +{ + [ServiceBinding(typeof(CassowaryExamples))] + public class CassowaryExamples + { + // Gets the server log. By default, this reports to "anvil.log" + private static readonly Logger Log = LogManager.GetCurrentClassLogger(); + + // Based on https://nwnlexicon.com/index.php?title=Cassowary & https://cassowary.readthedocs.io/en/latest/topics/theory.html + public CassowaryExamples() + { + // Create an empty cassowary solver. + Cassowary cTest = new Cassowary(); + + // Add some constraints + cTest.AddConstraint("middle == (left + right) / 2"); + cTest.AddConstraint("right == left + 10"); + cTest.AddConstraint("right <= 100"); + cTest.AddConstraint("left >= 0"); + + // Results should be 90, 95 and 100 + Log.Info($"Solution 1: Left: {cTest.GetValue("left")}, Middle: {cTest.GetValue("middle")}, Right: {cTest.GetValue("right")}"); + + // Suggest middle is 45 + cTest.SuggestValue("middle", 45f); + + // Results should now be 40, 45 and 50 + Log.Info($"Solution 2: Left: {cTest.GetValue("left")}, Middle: {cTest.GetValue("middle")}, Right: {cTest.GetValue("right")}"); + } + } +} diff --git a/docs/samples/API/EngineStructures/EffectExamples.cs b/docs/samples/API/EngineStructures/EffectExamples.cs new file mode 100644 index 000000000..eed57b2c2 --- /dev/null +++ b/docs/samples/API/EngineStructures/EffectExamples.cs @@ -0,0 +1,102 @@ +/* + * Examples for creating and applying effects to objects and locations. + */ + +using System; +using System.Linq; +using Anvil.API; +using Anvil.API.Events; +using Anvil.Services; + +namespace NWN.Anvil.Samples +{ + [ServiceBinding(typeof(EffectExamples))] + public class EffectExamples + { + // Some effects can be declared as fields. This allows you to reuse effects, rather than creating them every time. + private readonly Effect blindnessEffect = Effect.Blindness(); + private readonly Effect bloodVfx = Effect.VisualEffect(VfxType.ComBloodCrtRed); + + public EffectExamples() + { + // Make our blindness effect a supernatural effect. + blindnessEffect.SubType = EffectSubType.Supernatural; + + // Register methods to listen for the player/client enter event. + NwModule.Instance.OnClientEnter += RatSummonExample; + + // When entering a specific area, remove all blindness effects: + NwArea? area = NwObject.FindObjectsWithTag("clear_blind").FirstOrDefault(); + if (area != null) + { + area.OnEnter += RemoveBlindnessExample; + } + + // Register methods to listen for the creature damage event. + NwModule.Instance.OnCreatureDamage += BloodVfxExample; + NwModule.Instance.OnCreatureDamage += BlindnessExample; + } + + /// + /// Spawns a rat creature summon at the module spawn location when a player connects. + /// + private async void RatSummonExample(ModuleEvents.OnClientEnter eventData) + { + // Create the summon effect. + // Some effects require a "caster" for the effect, and must be made within the caster's context. + await eventData.Player.ControlledCreature!.WaitForObjectContext(); + Effect summon = Effect.SummonCreature("nw_rat001", VfxType.ImpUnsummon); + + // Spawn the summon at the module's starting location + Location spawnLocation = NwModule.Instance.StartingLocation; + spawnLocation.ApplyEffect(EffectDuration.Temporary, summon, TimeSpan.FromMinutes(5)); + } + + /// + /// Applies a blood splatter effect to all creatures taking more than 50 damage. + /// + private void BloodVfxExample(OnCreatureDamage eventData) + { + // If the target is a creature, and the attack does more than 50 slashing damage... + if (eventData.Target is NwCreature && eventData.DamageData.Slash > 50) + { + // ...Apply our blood effect to the creature + eventData.Target.ApplyEffect(EffectDuration.Instant, bloodVfx); + } + } + + /// + /// Applies a 5 second blindness effect to all creatures taking more than 100 damage. + /// + private void BlindnessExample(OnCreatureDamage eventData) + { + // If the target is a creature, and the attack does more than 100 bludgeoning damage... + if (eventData.Target is NwCreature && eventData.DamageData.Bludgeoning > 100) + { + // ...Apply our blindness effect for 5 seconds. + eventData.Target.ApplyEffect(EffectDuration.Temporary, blindnessEffect, TimeSpan.FromSeconds(5)); + } + } + + /// + /// Removes any blindness effect still active once a creature enters the area. + /// + private void RemoveBlindnessExample(AreaEvents.OnEnter eventData) + { + // If the object entering is not a creature, early return. + if (eventData.EnteringObject is not NwCreature creature) + { + return; + } + + // Loop through all active effects on the creature, and remove any with the blindness type. + foreach (Effect effect in creature.ActiveEffects) + { + if (effect.EffectType == EffectType.Blindness) + { + creature.RemoveEffect(effect); + } + } + } + } +} diff --git a/docs/samples/API/EngineStructures/ItemPropertyExamples.cs b/docs/samples/API/EngineStructures/ItemPropertyExamples.cs new file mode 100644 index 000000000..bd54a3b17 --- /dev/null +++ b/docs/samples/API/EngineStructures/ItemPropertyExamples.cs @@ -0,0 +1,60 @@ +/* + * Examples for creating, applying and removing item properties. + */ + +using Anvil.API; +using Anvil.API.Events; +using Anvil.Services; + +namespace NWN.Anvil.Samples +{ + [ServiceBinding(typeof(ItemPropertyExamples))] + public class ItemPropertyExamples + { + // Most item properties can be declared as fields. This allows you to reuse effects, rather than creating them every time. + private readonly ItemProperty hasteProperty = ItemProperty.Haste(); + + public ItemPropertyExamples() + { + NwModule.Instance.OnItemEquip += TemporaryHasteExample; + NwModule.Instance.OnClientEnter += RemoveItemPropertyExample; + } + + /// + /// Apply a haste effect for 5 rounds if someone equips an item with the "temp_haste" tag. + /// + private void TemporaryHasteExample(OnItemEquip eventData) + { + // Check if the tag matches the item that was equipped. + if (eventData.Item.Tag == "temp_haste") + { + // Apply the haste item property. + eventData.Item.AddItemProperty(hasteProperty, EffectDuration.Temporary, NwTimeSpan.FromRounds(5)); + } + } + + /// + /// Finds an item with the tag "sword_special", and removes any item property with the tag "special_temp". + /// + private void RemoveItemPropertyExample(ModuleEvents.OnClientEnter eventData) + { + // Find an item in the player's inventory with the tag "sword_special" + NwItem? item = eventData.Player.ControlledCreature!.FindItemWithTag("sword_special"); + + // If they don't have the item, early return. + if (item == null) + { + return; + } + + // Loop over the item's properties. Remove any that have a matching tag. + foreach (ItemProperty property in item.ItemProperties) + { + if (property.Tag == "special_temp") + { + item.RemoveItemProperty(property); + } + } + } + } +} diff --git a/docs/samples/Services/PerformanceReportService.cs b/docs/samples/Services/PerformanceReportService.cs index bffe8be6c..14b65edbf 100644 --- a/docs/samples/Services/PerformanceReportService.cs +++ b/docs/samples/Services/PerformanceReportService.cs @@ -2,9 +2,9 @@ * Report the current tick rate every server loop. */ -using System; using Anvil.API; using Anvil.Services; +using NLog; namespace NWN.Anvil.Samples { @@ -12,9 +12,12 @@ namespace NWN.Anvil.Samples [ServiceBinding(typeof(PerformanceReportService))] public class PerformanceReportService : IUpdateable { + // Gets the server log. By default, this reports to "anvil.log" + private static readonly Logger Log = LogManager.GetCurrentClassLogger(); + public void Update() { - Console.WriteLine($"Current tick rate: {1 / Time.DeltaTime.TotalSeconds}"); + Log.Info($"Current tick rate: {1 / Time.DeltaTime.TotalSeconds}"); } } } diff --git a/docs/samples/Services/TriggerHandlerService.cs b/docs/samples/Services/TriggerHandlerService.cs index 36c2574b4..068325bbb 100644 --- a/docs/samples/Services/TriggerHandlerService.cs +++ b/docs/samples/Services/TriggerHandlerService.cs @@ -13,6 +13,7 @@ namespace NWN.Anvil.Samples [ServiceBinding(typeof(TriggerHandlerService))] public class TriggerHandlerService { + // Gets the server log. By default, this reports to "anvil.log" private static readonly Logger Log = LogManager.GetCurrentClassLogger(); public TriggerHandlerService() From 50dab6ef298bc088cde3249b03d2ac8f3ea504ac Mon Sep 17 00:00:00 2001 From: Jhett Black <10942655+jhett12321@users.noreply.github.com> Date: Fri, 3 Mar 2023 01:28:59 +1100 Subject: [PATCH 09/17] Add ActionJumpToLocation. (#626) --- NWN.Anvil/src/main/API/Objects/NwGameObject.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/NWN.Anvil/src/main/API/Objects/NwGameObject.cs b/NWN.Anvil/src/main/API/Objects/NwGameObject.cs index a722e888e..5c631cc71 100644 --- a/NWN.Anvil/src/main/API/Objects/NwGameObject.cs +++ b/NWN.Anvil/src/main/API/Objects/NwGameObject.cs @@ -248,6 +248,17 @@ public async Task ActionCastSpellAt(NwSpell spell, Location target, MetaMagic me NWScript.ActionCastSpellAtLocation(spell.Id, target, (int)metaMagic, cheat.ToInt(), (int)projectilePathType, instant.ToInt()); } + /// + /// Jumps this object to the specified location.
+ /// Unlike the setter, this method will compute a safe location when teleporting, and may not equal the location specified. + ///
+ /// The location to attempt to jump to. + public async Task ActionJumpToLocation(Location location) + { + await WaitForObjectContext(); + NWScript.ActionJumpToLocation(location); + } + /// /// Instructs this object to do nothing for the specified duration, before continuing with the next item in the action queue. /// From b269722b7e5a652234ab56dc55f71afd9e7233b0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 Mar 2023 01:29:42 +1100 Subject: [PATCH 10/17] Bump Microsoft.CodeAnalysis.CSharp from 4.4.0 to 4.5.0 (#624) Bumps [Microsoft.CodeAnalysis.CSharp](https://github.com/dotnet/roslyn) from 4.4.0 to 4.5.0. - [Release notes](https://github.com/dotnet/roslyn/releases) - [Changelog](https://github.com/dotnet/roslyn/blob/main/docs/Breaking%20API%20Changes.md) - [Commits](https://github.com/dotnet/roslyn/commits) --- updated-dependencies: - dependency-name: Microsoft.CodeAnalysis.CSharp dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- NWN.Anvil.TestRunner/NWN.Anvil.TestRunner.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NWN.Anvil.TestRunner/NWN.Anvil.TestRunner.csproj b/NWN.Anvil.TestRunner/NWN.Anvil.TestRunner.csproj index a747cad32..817b1ec4d 100644 --- a/NWN.Anvil.TestRunner/NWN.Anvil.TestRunner.csproj +++ b/NWN.Anvil.TestRunner/NWN.Anvil.TestRunner.csproj @@ -56,7 +56,7 @@ - + From bc634a1b7b90306a97b95e679287850246ae48cb Mon Sep 17 00:00:00 2001 From: Jhett Black <10942655+jhett12321@users.noreply.github.com> Date: Fri, 3 Mar 2023 01:34:43 +1100 Subject: [PATCH 11/17] Add player quick chat event. (#627) --- .../Native/ClientEvents/OnPlayerQuickChat.cs | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 NWN.Anvil/src/main/API/Events/Native/ClientEvents/OnPlayerQuickChat.cs diff --git a/NWN.Anvil/src/main/API/Events/Native/ClientEvents/OnPlayerQuickChat.cs b/NWN.Anvil/src/main/API/Events/Native/ClientEvents/OnPlayerQuickChat.cs new file mode 100644 index 000000000..cc0ad0512 --- /dev/null +++ b/NWN.Anvil/src/main/API/Events/Native/ClientEvents/OnPlayerQuickChat.cs @@ -0,0 +1,84 @@ +using System; +using System.Runtime.InteropServices; +using Anvil.API.Events; +using Anvil.Services; +using NWN.Native.API; + +namespace Anvil.API.Events +{ + /// + /// Called when a player uses a quick chat command. + /// + public sealed class OnPlayerQuickChat : IEvent + { + /// + /// Gets the player that is doing the quick chat. + /// + public NwPlayer Player { get; private init; } = null!; + + /// + /// Gets the quick chat type that is being played. + /// + public VoiceChatType VoiceChat { get; private init; } + + /// + /// Gets or sets a value indicating whether the character should be prevented from using the quick chat. + /// + public bool PreventQuickChat { get; set; } + + NwObject? IEvent.Context => Player.ControlledCreature; + + public sealed unsafe class Factory : HookEventFactory + { + private static FunctionHook Hook { get; set; } = null!; + + private delegate int HandlePlayerToServerQuickChatMessageHook(void* pMessage, void* pPlayer, byte nMinor); + + protected override IDisposable[] RequestHooks() + { + delegate* unmanaged pHook = &OnHandlePlayerToServerQuickChatMessage; + Hook = HookService.RequestHook(pHook, FunctionsLinux._ZN11CNWSMessage36HandlePlayerToServerQuickChatMessageEP10CNWSPlayerh, HookOrder.Early); + return new IDisposable[] { Hook }; + } + + [UnmanagedCallersOnly] + private static int OnHandlePlayerToServerQuickChatMessage(void* pMessage, void* pPlayer, byte nMinor) + { + CNWSMessage message = CNWSMessage.FromPointer(pMessage); + OnPlayerQuickChat eventData = ProcessEvent(EventCallbackType.Before, new OnPlayerQuickChat + { + Player = CNWSPlayer.FromPointer(pPlayer).ToNwPlayer()!, + VoiceChat = (VoiceChatType)message.PeekMessage(0), + }); + + int retVal = !eventData.PreventQuickChat ? Hook.CallOriginal(pMessage, pPlayer, nMinor) : 0; + ProcessEvent(EventCallbackType.After, eventData); + + return retVal; + } + } + } +} + +namespace Anvil.API +{ + public sealed partial class NwPlayer + { + /// + public event Action OnPlayerQuickChat + { + add => EventService.Subscribe(ControlledCreature, value); + remove => EventService.Unsubscribe(ControlledCreature, value); + } + } + + public sealed partial class NwModule + { + /// + public event Action OnPlayerQuickChat + { + add => EventService.SubscribeAll(value); + remove => EventService.UnsubscribeAll(value); + } + } +} From 06cb91dbce3822ace36e2bdcfe4420478400eab8 Mon Sep 17 00:00:00 2001 From: Jhett Black <10942655+jhett12321@users.noreply.github.com> Date: Fri, 3 Mar 2023 12:46:20 +1100 Subject: [PATCH 12/17] Add lazy service tests. (#628) * Add lazy service tests. * Remove redundant initializers. --- .../Services/Services/LazyServiceTests.cs | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 NWN.Anvil.Tests/src/main/Services/Services/LazyServiceTests.cs diff --git a/NWN.Anvil.Tests/src/main/Services/Services/LazyServiceTests.cs b/NWN.Anvil.Tests/src/main/Services/Services/LazyServiceTests.cs new file mode 100644 index 000000000..cd4e96895 --- /dev/null +++ b/NWN.Anvil.Tests/src/main/Services/Services/LazyServiceTests.cs @@ -0,0 +1,97 @@ +using System; +using Anvil.Services; +using NUnit.Framework; + +namespace Anvil.Tests.Services +{ + [TestFixture(Category = "Services.Services")] + public sealed class LazyServiceTests + { + [Inject] + private static LazyServiceConsumerConstructor StaticLazyServiceConsumerConstructor { get; set; } = null!; + + [Inject] + private static LazyServiceConsumerProperty StaticLazyServiceConsumerProperty { get; set; } = null!; + + [Inject] + private static Lazy StaticLazyServicePropertyStatic { get; set; } = null!; + + [Test(Description = "A lazy service is not initialized until used when declared as a dependency in a constructor.")] + public void ConstructorLazyServiceDependencyInitializedWhenUsed() + { + Assert.That(LazyServiceConstructor.Created, Is.EqualTo(false), "The lazy service was already initialized"); + Assert.That(StaticLazyServiceConsumerConstructor.LazyServiceConstructor.Value, Is.Not.Null, "The lazy service was not initialized after being used."); + Assert.That(LazyServiceConstructor.Created, Is.EqualTo(true), "The lazy service was not initialized after being used."); + } + + [Test(Description = "A lazy service is not initialized until used when declared as a dependency in a property.")] + public void PropertyLazyServiceDependencyInitializedWhenUsed() + { + Assert.That(LazyServiceProperty.Created, Is.EqualTo(false), "The lazy service was already initialized"); + Assert.That(StaticLazyServiceConsumerProperty.LazyServiceProperty.Value, Is.Not.Null, "The lazy service was not initialized after being used."); + Assert.That(LazyServiceProperty.Created, Is.EqualTo(true), "The lazy service was not initialized after being used."); + } + + [Test(Description = "A lazy service is not initialized until used when declared as a dependency in a static property.")] + public void StaticPropertyLazyServiceDependencyInitializedWhenUsed() + { + Assert.That(LazyServicePropertyStatic.Created, Is.EqualTo(false), "The lazy service was already initialized"); + Assert.That(StaticLazyServicePropertyStatic.Value, Is.Not.Null, "The lazy service was not initialized after being used."); + Assert.That(LazyServicePropertyStatic.Created, Is.EqualTo(true), "The lazy service was not initialized after being used."); + } + + [ServiceBinding(typeof(LazyServiceConsumerConstructor))] + internal sealed class LazyServiceConsumerConstructor + { + public Lazy LazyServiceConstructor { get; } + + public LazyServiceConsumerConstructor(Lazy lazyServiceConstructor) + { + LazyServiceConstructor = lazyServiceConstructor; + } + } + + [ServiceBinding(typeof(LazyServiceConsumerProperty))] + internal sealed class LazyServiceConsumerProperty + { + [Inject] + public Lazy LazyServiceProperty { get; init; } = null!; + } + + [ServiceBinding(typeof(LazyServiceProperty))] + [ServiceBindingOptions(Lazy = true)] + internal sealed class LazyServiceProperty + { + public static bool Created; + + public LazyServiceProperty() + { + Created = true; + } + } + + [ServiceBinding(typeof(LazyServicePropertyStatic))] + [ServiceBindingOptions(Lazy = true)] + internal sealed class LazyServicePropertyStatic + { + public static bool Created; + + public LazyServicePropertyStatic() + { + Created = true; + } + } + + [ServiceBinding(typeof(LazyServiceConstructor))] + [ServiceBindingOptions(Lazy = true)] + internal sealed class LazyServiceConstructor + { + public static bool Created; + + public LazyServiceConstructor() + { + Created = true; + } + } + } +} From d6ac82fff234b5df01f7f43aeec012fbd7091a83 Mon Sep 17 00:00:00 2001 From: Jhett Black <10942655+jhett12321@users.noreply.github.com> Date: Fri, 3 Mar 2023 12:55:43 +1100 Subject: [PATCH 13/17] Cleanup NwServer usages. Cleanup lazy services. Add logs for lazy services. (#629) --- .../Services/InjectionServiceTests.cs | 7 +------ NWN.Anvil/src/main/API/Objects/NwPlayer.cs | 19 ++++++++----------- NWN.Anvil/src/main/AnvilCore.cs | 5 +---- .../API/Creature/CreatureForceWalkService.cs | 4 ++++ .../Creature/CreatureWalkRateCapService.cs | 6 +++++- .../Creature/DamageLevelOverrideService.cs | 6 +++++- .../API/Creature/InitiativeModifierService.cs | 6 +++++- .../PlayerRestDurationOverrideService.cs | 6 +++++- .../src/main/Services/Chat/ChatService.cs | 7 ++----- .../Services/Core/Logging/LoggerManager.cs | 10 +++------- 10 files changed, 39 insertions(+), 37 deletions(-) diff --git a/NWN.Anvil.Tests/src/main/Services/Services/InjectionServiceTests.cs b/NWN.Anvil.Tests/src/main/Services/Services/InjectionServiceTests.cs index 1aab5e49f..4e62b249f 100644 --- a/NWN.Anvil.Tests/src/main/Services/Services/InjectionServiceTests.cs +++ b/NWN.Anvil.Tests/src/main/Services/Services/InjectionServiceTests.cs @@ -1,4 +1,3 @@ -using Anvil.API; using Anvil.Services; using NUnit.Framework; @@ -42,7 +41,6 @@ public void InjectionServiceInjectsInstanceDependencies() [Test(Description = "Services with inject properties are implicitly injected.")] public void InjectionServiceInjectsServiceDependencies() { - Assert.That(StaticInjectionTestService?.NwServer, Is.Not.Null, "A constructor dependency was not injected."); Assert.That(StaticInjectionTestService?.ResourceManager, Is.Not.Null, "A constructor dependency was not injected."); Assert.That(StaticInjectionTestService?.ChatService, Is.Null, "A property was injected when it shouldn't have."); Assert.That(StaticInjectionTestService?.EventService, Is.Not.Null, "A property dependency was not injected."); @@ -64,8 +62,6 @@ private sealed class InjectionTest [ServiceBinding(typeof(InjectionTestService))] internal sealed class InjectionTestService { - public NwServer? NwServer { get; } - public ResourceManager? ResourceManager { get; } // Not injected with attribute or initialized from constructor, expected to be null. @@ -78,9 +74,8 @@ internal sealed class InjectionTestService [Inject] internal HookService? HookService { get; init; } - public InjectionTestService(NwServer nwServer, ResourceManager resourceManager) + public InjectionTestService(ResourceManager resourceManager) { - NwServer = nwServer; ResourceManager = resourceManager; } } diff --git a/NWN.Anvil/src/main/API/Objects/NwPlayer.cs b/NWN.Anvil/src/main/API/Objects/NwPlayer.cs index d669f89fb..18fb88a57 100644 --- a/NWN.Anvil/src/main/API/Objects/NwPlayer.cs +++ b/NWN.Anvil/src/main/API/Objects/NwPlayer.cs @@ -29,9 +29,6 @@ public sealed partial class NwPlayer : IEquatable [Inject] private static EventService EventService { get; set; } = null!; - [Inject] - private static NwServer NwServer { get; set; } = null!; - [Inject] private static Lazy ObjectVisibilityService { get; set; } = null!; @@ -39,7 +36,7 @@ public sealed partial class NwPlayer : IEquatable private static Lazy PlayerNameOverrideService { get; set; } = null!; [Inject] - private static PlayerRestDurationOverrideService PlayerRestDurationOverrideService { get; set; } = null!; + private static Lazy PlayerRestDurationOverrideService { get; set; } = null!; private readonly CNWSPlayer player; @@ -280,7 +277,7 @@ public IEnumerable PartyMembers ///
public TimeSpan? RestDurationOverride { - get => LoginCreature != null ? PlayerRestDurationOverrideService.GetDurationOverride(LoginCreature) : null; + get => LoginCreature != null ? PlayerRestDurationOverrideService.Value.GetDurationOverride(LoginCreature) : null; set { if (LoginCreature == null) @@ -290,11 +287,11 @@ public TimeSpan? RestDurationOverride if (value.HasValue) { - PlayerRestDurationOverrideService.SetDurationOverride(LoginCreature, value.Value); + PlayerRestDurationOverrideService.Value.SetDurationOverride(LoginCreature, value.Value); } else { - PlayerRestDurationOverrideService.ClearDurationOverride(LoginCreature); + PlayerRestDurationOverrideService.Value.ClearDurationOverride(LoginCreature); } } } @@ -581,8 +578,8 @@ public async Task Delete(string kickMessage, bool preserveBackup = true) } string bicName = BicFileName; - string serverVault = NwServer.GetAliasPath("SERVERVAULT"); - string playerDir = NwServer.ServerInfo.PersistentWorldOptions.ServerVaultByPlayerName ? PlayerName : CDKey; + string serverVault = NwServer.Instance.GetAliasPath("SERVERVAULT"); + string playerDir = NwServer.Instance.ServerInfo.PersistentWorldOptions.ServerVaultByPlayerName ? PlayerName : CDKey; string characterName = creature.Name; string playerName = PlayerName; @@ -601,11 +598,11 @@ public async Task Delete(string kickMessage, bool preserveBackup = true) await NwTask.NextFrame(); // Delete their character's TURD - bool turdDeleted = NwServer.DeletePlayerTURD(playerName, characterName); + bool turdDeleted = NwServer.Instance.DeletePlayerTURD(playerName, characterName); if (!turdDeleted) { // Server may be using TURDs by CD Key. - turdDeleted = NwServer.DeletePlayerTURD(playerDir, characterName); + turdDeleted = NwServer.Instance.DeletePlayerTURD(playerDir, characterName); } if (!turdDeleted) diff --git a/NWN.Anvil/src/main/AnvilCore.cs b/NWN.Anvil/src/main/AnvilCore.cs index f1adac1ee..e0d321d1f 100644 --- a/NWN.Anvil/src/main/AnvilCore.cs +++ b/NWN.Anvil/src/main/AnvilCore.cs @@ -22,9 +22,6 @@ public sealed class AnvilCore private static AnvilCore instance = null!; - [Inject] - private NwServer NwServer { get; init; } = null!; - [Inject] private VirtualMachineFunctionHandler VirtualMachineFunctionHandler { get; init; } = null!; @@ -93,7 +90,7 @@ public static void Reload() private void CheckServerVersion() { AssemblyName assemblyName = Assemblies.Anvil.GetName(); - Version serverVersion = NwServer.ServerVersion; + Version serverVersion = NwServer.Instance.ServerVersion; if (assemblyName.Version?.Major != serverVersion.Major || assemblyName.Version.Minor != serverVersion.Minor) { diff --git a/NWN.Anvil/src/main/Services/API/Creature/CreatureForceWalkService.cs b/NWN.Anvil/src/main/Services/API/Creature/CreatureForceWalkService.cs index bd97a0471..1210b3c84 100644 --- a/NWN.Anvil/src/main/Services/API/Creature/CreatureForceWalkService.cs +++ b/NWN.Anvil/src/main/Services/API/Creature/CreatureForceWalkService.cs @@ -1,5 +1,6 @@ using System.Linq; using Anvil.API; +using NLog; using NWN.Native.API; namespace Anvil.Services @@ -8,10 +9,13 @@ namespace Anvil.Services [ServiceBindingOptions(InternalBindingPriority.API, Lazy = true)] internal sealed unsafe class CreatureForceWalkService { + private static readonly Logger Log = LogManager.GetCurrentClassLogger(); + private readonly FunctionHook removeLimitMovementSpeedHook; public CreatureForceWalkService(HookService hookService) { + Log.Info($"Initialising optional service {nameof(CreatureForceWalkService)}"); removeLimitMovementSpeedHook = hookService.RequestHook(OnRemoveLimitMovementSpeed, FunctionsLinux._ZN21CNWSEffectListHandler26OnRemoveLimitMovementSpeedEP10CNWSObjectP11CGameEffect, HookOrder.Late); } diff --git a/NWN.Anvil/src/main/Services/API/Creature/CreatureWalkRateCapService.cs b/NWN.Anvil/src/main/Services/API/Creature/CreatureWalkRateCapService.cs index c6f0ef435..3f394a3b9 100644 --- a/NWN.Anvil/src/main/Services/API/Creature/CreatureWalkRateCapService.cs +++ b/NWN.Anvil/src/main/Services/API/Creature/CreatureWalkRateCapService.cs @@ -1,16 +1,20 @@ using Anvil.API; +using NLog; using NWN.Native.API; namespace Anvil.Services { [ServiceBinding(typeof(CreatureWalkRateCapService))] - [ServiceBindingOptions(InternalBindingPriority.API)] + [ServiceBindingOptions(InternalBindingPriority.API, Lazy = true)] internal sealed unsafe class CreatureWalkRateCapService { + private static readonly Logger Log = LogManager.GetCurrentClassLogger(); + private readonly FunctionHook walkRateHook; public CreatureWalkRateCapService(HookService hookService) { + Log.Info($"Initialising optional service {nameof(CreatureWalkRateCapService)}"); walkRateHook = hookService.RequestHook(OnGetWalkRate, FunctionsLinux._ZN12CNWSCreature11GetWalkRateEv, HookOrder.Late); } diff --git a/NWN.Anvil/src/main/Services/API/Creature/DamageLevelOverrideService.cs b/NWN.Anvil/src/main/Services/API/Creature/DamageLevelOverrideService.cs index 29f4296e5..5c02edcdb 100644 --- a/NWN.Anvil/src/main/Services/API/Creature/DamageLevelOverrideService.cs +++ b/NWN.Anvil/src/main/Services/API/Creature/DamageLevelOverrideService.cs @@ -1,16 +1,20 @@ using Anvil.API; +using NLog; using NWN.Native.API; namespace Anvil.Services { [ServiceBinding(typeof(DamageLevelOverrideService))] [ServiceBindingOptions(InternalBindingPriority.API, Lazy = true)] - public sealed unsafe class DamageLevelOverrideService + internal sealed unsafe class DamageLevelOverrideService { + private static readonly Logger Log = LogManager.GetCurrentClassLogger(); + private readonly FunctionHook damageLevelHook; public DamageLevelOverrideService(HookService hookService) { + Log.Info($"Initialising optional service {nameof(DamageLevelOverrideService)}"); damageLevelHook = hookService.RequestHook(OnGetDamageLevel, FunctionsLinux._ZN10CNWSObject14GetDamageLevelEv, HookOrder.Late); } diff --git a/NWN.Anvil/src/main/Services/API/Creature/InitiativeModifierService.cs b/NWN.Anvil/src/main/Services/API/Creature/InitiativeModifierService.cs index 805d1c93f..04d7be6f3 100644 --- a/NWN.Anvil/src/main/Services/API/Creature/InitiativeModifierService.cs +++ b/NWN.Anvil/src/main/Services/API/Creature/InitiativeModifierService.cs @@ -1,5 +1,6 @@ using Anvil.API; using Anvil.Internal; +using NLog; using NWN.Native.API; using Feat = Anvil.API.Feat; @@ -7,12 +8,15 @@ namespace Anvil.Services { [ServiceBinding(typeof(InitiativeModifierService))] [ServiceBindingOptions(InternalBindingPriority.API, Lazy = true)] - public sealed unsafe class InitiativeModifierService + internal sealed unsafe class InitiativeModifierService { + private static readonly Logger Log = LogManager.GetCurrentClassLogger(); + private readonly FunctionHook initiativeModifierHook; public InitiativeModifierService(HookService hookService) { + Log.Info($"Initialising optional service {nameof(InitiativeModifierService)}"); initiativeModifierHook = hookService.RequestHook(OnResolveInitiative, FunctionsLinux._ZN12CNWSCreature17ResolveInitiativeEv, HookOrder.Late); } diff --git a/NWN.Anvil/src/main/Services/API/Player/PlayerRestDurationOverrideService.cs b/NWN.Anvil/src/main/Services/API/Player/PlayerRestDurationOverrideService.cs index a815e5e28..b7989e612 100644 --- a/NWN.Anvil/src/main/Services/API/Player/PlayerRestDurationOverrideService.cs +++ b/NWN.Anvil/src/main/Services/API/Player/PlayerRestDurationOverrideService.cs @@ -1,14 +1,17 @@ using System; using System.Collections.Generic; using Anvil.API; +using NLog; using NWN.Native.API; namespace Anvil.Services { [ServiceBinding(typeof(PlayerRestDurationOverrideService))] - [ServiceBindingOptions(InternalBindingPriority.API)] + [ServiceBindingOptions(InternalBindingPriority.API, Lazy = true)] internal sealed unsafe class PlayerRestDurationOverrideService { + private static readonly Logger Log = LogManager.GetCurrentClassLogger(); + private static readonly CExoString DurationTableKey = "Duration".ToExoString(); private readonly FunctionHook aiActionRestHook; @@ -16,6 +19,7 @@ internal sealed unsafe class PlayerRestDurationOverrideService public PlayerRestDurationOverrideService(HookService hookService) { + Log.Info($"Initialising optional service {nameof(PlayerRestDurationOverrideService)}"); aiActionRestHook = hookService.RequestHook(OnAIActionRest, FunctionsLinux._ZN12CNWSCreature12AIActionRestEP20CNWSObjectActionNode, HookOrder.Late); } diff --git a/NWN.Anvil/src/main/Services/Chat/ChatService.cs b/NWN.Anvil/src/main/Services/Chat/ChatService.cs index 00466722e..9222b1eb3 100644 --- a/NWN.Anvil/src/main/Services/Chat/ChatService.cs +++ b/NWN.Anvil/src/main/Services/Chat/ChatService.cs @@ -17,16 +17,13 @@ public sealed unsafe partial class ChatService { ChatChannel.PlayerWhisper, 3.0f }, }; - private readonly NwServer nwServer; - private readonly Dictionary> playerHearingDistances = new Dictionary>(); private readonly FunctionHook sendServerToPlayerChatMessageHook; private bool customHearingDistances; - public ChatService(HookService hookService, NwServer nwServer) + public ChatService(HookService hookService) { - this.nwServer = nwServer; sendServerToPlayerChatMessageHook = hookService.RequestHook(OnSendServerToPlayerChatMessage, FunctionsLinux._ZN11CNWSMessage29SendServerToPlayerChatMessageEhj10CExoStringjRKS0_, HookOrder.Late); } @@ -171,7 +168,7 @@ private int OnSendServerToPlayerChatMessage(void* pMessage, ChatChannel nChatMes return false.ToInt(); } - if (nChatMessageType == ChatChannel.PlayerShout && nwServer.ServerInfo.PlayOptions.DisallowShouting) + if (nChatMessageType == ChatChannel.PlayerShout && NwServer.Instance.ServerInfo.PlayOptions.DisallowShouting) { nChatMessageType = ChatChannel.PlayerTalk; } diff --git a/NWN.Anvil/src/main/Services/Core/Logging/LoggerManager.cs b/NWN.Anvil/src/main/Services/Core/Logging/LoggerManager.cs index 7fb5ceed6..6d02a6fb3 100644 --- a/NWN.Anvil/src/main/Services/Core/Logging/LoggerManager.cs +++ b/NWN.Anvil/src/main/Services/Core/Logging/LoggerManager.cs @@ -13,12 +13,8 @@ internal sealed class LoggerManager : ICoreService private static readonly Logger Log = LogManager.GetCurrentClassLogger(); private static readonly SimpleLayout DefaultLayout = new SimpleLayout("${level:format=FirstCharacter} [${date}] [${logger}] ${message}${onexception:${newline}${exception:format=ToString}}"); - private readonly NwServer nwServer; - - public LoggerManager(NwServer nwServer) + public LoggerManager() { - this.nwServer = nwServer; - LogManager.AutoShutdown = false; LogManager.Configuration = null; LogManager.ThrowConfigExceptions = true; @@ -45,7 +41,7 @@ void ICoreService.Init() Log.Info("Using default configuration"); } - LogManager.Configuration.Variables["nwn_home"] = nwServer.UserDirectory; + LogManager.Configuration.Variables["nwn_home"] = NwServer.Instance.UserDirectory; } void ICoreService.Load() {} @@ -89,7 +85,7 @@ private static LoggingConfiguration GetDefaultConfig() private LoggingConfiguration GetConfigFromFile(string path) { LoggingConfiguration config = new XmlLoggingConfiguration(path); - config.Variables["nwn_home"] = nwServer.UserDirectory; + config.Variables["nwn_home"] = NwServer.Instance.UserDirectory; return config; } From 115e26677f6be7ede426bb386172c570b789f9eb Mon Sep 17 00:00:00 2001 From: Jhett Black <10942655+jhett12321@users.noreply.github.com> Date: Fri, 3 Mar 2023 15:30:11 +1100 Subject: [PATCH 14/17] NwPlayer: Add object name override support. (#630) * Implement object name overrides. * Fix hook. Add object type check. * Update docs. --- NWN.Anvil/src/main/API/Objects/NwPlayer.cs | 24 ++++ .../main/API/Variables/InternalVariables.cs | 1 + .../Player/PlayerObjectNameOverrideService.cs | 118 ++++++++++++++++++ 3 files changed, 143 insertions(+) create mode 100644 NWN.Anvil/src/main/Services/API/Player/PlayerObjectNameOverrideService.cs diff --git a/NWN.Anvil/src/main/API/Objects/NwPlayer.cs b/NWN.Anvil/src/main/API/Objects/NwPlayer.cs index 18fb88a57..1d13e7c95 100644 --- a/NWN.Anvil/src/main/API/Objects/NwPlayer.cs +++ b/NWN.Anvil/src/main/API/Objects/NwPlayer.cs @@ -38,6 +38,9 @@ public sealed partial class NwPlayer : IEquatable [Inject] private static Lazy PlayerRestDurationOverrideService { get; set; } = null!; + [Inject] + private static Lazy PlayerObjectNameOverrideService { get; set; } = null!; + private readonly CNWSPlayer player; internal CNWSPlayer Player @@ -537,6 +540,15 @@ public void ClearPlayerNameOverride(NwPlayer observer) PlayerNameOverrideService.Value.ClearPlayerNameOverride(this, observer); } + /// + /// Clears an override name for the specified object. + /// + /// The object whose overriden name will be cleared. + public void ClearObjectNameOverride(NwGameObject gameObject) + { + PlayerObjectNameOverrideService.Value.ClearObjectNameOverride(this, gameObject); + } + /// /// Removes the override for the specified texture, reverting to the original texture. /// @@ -1397,6 +1409,18 @@ public void SetPlayerNameOverride(PlayerNameOverride nameOverride, NwPlayer obse PlayerNameOverrideService.Value.SetPlayerNameOverride(this, nameOverride, observer); } + /// + /// Sets an override name for a specific game object, as visible from this player.
+ /// Supports placeables, doors, items and creatures. + ///
+ /// The game object to set a new name for. + /// The new name of the object. + /// Thrown if gameObject is not a placeable, door, item or creature. + public void SetObjectNameOverride(NwGameObject gameObject, string name) + { + PlayerObjectNameOverrideService.Value.SetObjectNameOverride(this, gameObject, name); + } + /// /// Makes ControlledCreature PC load a new texture instead of another. /// diff --git a/NWN.Anvil/src/main/API/Variables/InternalVariables.cs b/NWN.Anvil/src/main/API/Variables/InternalVariables.cs index e7f3908db..a42d230c0 100644 --- a/NWN.Anvil/src/main/API/Variables/InternalVariables.cs +++ b/NWN.Anvil/src/main/API/Variables/InternalVariables.cs @@ -16,5 +16,6 @@ internal static class InternalVariables public static InternalVariableEnum GlobalVisibilityOverride(NwObject gameObject) => gameObject.GetObjectVariable>("VISIBILITY_OVERRIDE"); public static InternalVariableEnum PlayerVisibilityOverride(NwPlayer player, NwObject targetGameObject) => player.ControlledCreature!.GetObjectVariable>("VISIBILITY_OVERRIDE" + targetGameObject.ObjectId); public static InternalVariableFloat WalkRateCap(NwObject creature) => creature.GetObjectVariable("WALK_RATE_CAP"); + public static InternalVariableString ObjectNameOverride(NwPlayer player, NwGameObject gameObject) => gameObject.GetObjectVariable("PLCNO_" + player.LoginCreature!.ObjectId); } } diff --git a/NWN.Anvil/src/main/Services/API/Player/PlayerObjectNameOverrideService.cs b/NWN.Anvil/src/main/Services/API/Player/PlayerObjectNameOverrideService.cs new file mode 100644 index 000000000..501c2bd00 --- /dev/null +++ b/NWN.Anvil/src/main/Services/API/Player/PlayerObjectNameOverrideService.cs @@ -0,0 +1,118 @@ +using System; +using Anvil.API; +using NLog; +using NWN.Native.API; + +namespace Anvil.Services +{ + [ServiceBinding(typeof(PlayerObjectNameOverrideService))] + [ServiceBindingOptions(InternalBindingPriority.API, Lazy = true)] + internal sealed unsafe class PlayerObjectNameOverrideService + { + private static readonly Logger Log = LogManager.GetCurrentClassLogger(); + + private delegate void ComputeGameObjectUpdateForObjectHook(void* pMessage, void* pPlayer, void* pPlayerGameObject, void* pGameObjectArray, uint oidObjectToUpdate); + + private readonly FunctionHook computeGameObjectUpdateForObjectHook; + + public PlayerObjectNameOverrideService(HookService hookService) + { + Log.Info($"Initialising optional service {nameof(PlayerObjectNameOverrideService)}"); + computeGameObjectUpdateForObjectHook = hookService.RequestHook(OnComputeGameObjectUpdateForObject, FunctionsLinux._ZN11CNWSMessage32ComputeGameObjectUpdateForObjectEP10CNWSPlayerP10CNWSObjectP16CGameObjectArrayj, HookOrder.Early); + } + + public void SetObjectNameOverride(NwPlayer player, NwGameObject gameObject, string name) + { + if (!IsSupportedObjectType(gameObject)) + { + throw new ArgumentException($"Object type {gameObject.GetType()} is not supported for name overrides.", nameof(gameObject)); + } + + InternalVariables.ObjectNameOverride(player, gameObject).Value = name; + CLastUpdateObject? lastUpdateObject = player.Player.GetLastUpdateObject(gameObject.ObjectId); + if (lastUpdateObject != null) + { + lastUpdateObject.m_nUpdateDisplayNameSeq--; + } + } + + public void ClearObjectNameOverride(NwPlayer player, NwGameObject gameObject) + { + InternalVariables.ObjectNameOverride(player, gameObject).Delete(); + CLastUpdateObject? lastUpdateObject = player.Player.GetLastUpdateObject(gameObject.ObjectId); + if (lastUpdateObject != null) + { + lastUpdateObject.m_nUpdateDisplayNameSeq--; + } + } + + private void OnComputeGameObjectUpdateForObject(void* pMessage, void* pPlayer, void* pPlayerGameObject, void* pGameObjectArray, uint oidObjectToUpdate) + { + NwGameObject? gameObject = oidObjectToUpdate.ToNwObjectSafe(); + NwPlayer? player = CNWSPlayer.FromPointer(pPlayer).ToNwPlayer(); + + if (player == null || gameObject == null) + { + computeGameObjectUpdateForObjectHook.CallOriginal(pMessage, pPlayer, pPlayerGameObject, pGameObjectArray, oidObjectToUpdate); + return; + } + + InternalVariableString nameOverride = InternalVariables.ObjectNameOverride(player, gameObject); + if (nameOverride.HasNothing) + { + computeGameObjectUpdateForObjectHook.CallOriginal(pMessage, pPlayer, pPlayerGameObject, pGameObjectArray, oidObjectToUpdate); + return; + } + + CExoString? originalName = GetName(gameObject); + if (originalName == null) + { + computeGameObjectUpdateForObjectHook.CallOriginal(pMessage, pPlayer, pPlayerGameObject, pGameObjectArray, oidObjectToUpdate); + return; + } + + originalName = new CExoString(originalName); + + SetName(gameObject, nameOverride.Value!.ToExoString()); + computeGameObjectUpdateForObjectHook.CallOriginal(pMessage, pPlayer, pPlayerGameObject, pGameObjectArray, oidObjectToUpdate); + SetName(gameObject, originalName); + } + + private CExoString? GetName(NwGameObject gameObject) + { + return gameObject switch + { + NwCreature creature => creature.Creature.m_sDisplayName, + NwDoor door => door.Door.m_sDisplayName, + NwItem item => item.Item.m_sDisplayName, + NwPlaceable placeable => placeable.Placeable.m_sDisplayName, + _ => null, + }; + } + + private void SetName(NwGameObject gameObject, CExoString name) + { + if (gameObject is NwCreature creature) + { + creature.Creature.m_sDisplayName = name; + } + else if (gameObject is NwDoor door) + { + door.Door.m_sDisplayName = name; + } + else if (gameObject is NwItem item) + { + item.Item.m_sDisplayName = name; + } + else if (gameObject is NwPlaceable placeable) + { + placeable.Placeable.m_sDisplayName = name; + } + } + + private bool IsSupportedObjectType(NwGameObject gameObject) + { + return gameObject is NwCreature or NwDoor or NwItem or NwPlaceable; + } + } +} From 32c1c1d54e92bf8aae5d4208a85e0d1116f15b5d Mon Sep 17 00:00:00 2001 From: Jhett Black <10942655+jhett12321@users.noreply.github.com> Date: Fri, 3 Mar 2023 17:11:07 +1100 Subject: [PATCH 15/17] Add support for player-specific looping vfx. (#631) --- NWN.Anvil/src/main/API/Objects/NwPlayer.cs | 42 ++++++++++- .../PlayerLoopingVisualEffectService.cs | 72 +++++++++++++++++++ .../Player/PlayerObjectNameOverrideService.cs | 6 ++ 3 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 NWN.Anvil/src/main/Services/API/Player/PlayerLoopingVisualEffectService.cs diff --git a/NWN.Anvil/src/main/API/Objects/NwPlayer.cs b/NWN.Anvil/src/main/API/Objects/NwPlayer.cs index 1d13e7c95..4c437e250 100644 --- a/NWN.Anvil/src/main/API/Objects/NwPlayer.cs +++ b/NWN.Anvil/src/main/API/Objects/NwPlayer.cs @@ -41,6 +41,9 @@ public sealed partial class NwPlayer : IEquatable [Inject] private static Lazy PlayerObjectNameOverrideService { get; set; } = null!; + [Inject] + private static Lazy PlayerLoopingVisualEffectService { get; set; } = null!; + private readonly CNWSPlayer player; internal CNWSPlayer Player @@ -465,6 +468,16 @@ public int AddCustomJournalEntry(JournalEntry entryData, bool silentUpdate = fal return retVal; } + /// + /// Adds the specified visual effect to an object, but only visible to this player. + /// + /// The game object to apply the effect to. + /// The visual effect to apply. + public void AddLoopingVisualEffect(NwGameObject gameObject, VisualEffectTableEntry visualEffect) + { + PlayerLoopingVisualEffectService.Value.AddLoopingVisualEffect(this, gameObject, visualEffect); + } + /// /// Attaches the specified creature to the player as a henchmen. /// @@ -522,6 +535,15 @@ public void BootPlayer(string reason = "") NWScript.BootPC(ControlledCreature, reason); } + /// + /// Clears looping visual effects on the specified object visible only to this player. + /// + /// The object to clear visual effects form. + public void ClearLoopingVisualEffects(NwGameObject gameObject) + { + PlayerLoopingVisualEffectService.Value.ClearLoopingVisualEffects(this, gameObject); + } + /// /// Clears an overridden player character name. /// @@ -996,7 +1018,16 @@ public VisibilityMode GetPersonalVisibilityOverride(NwGameObject target) } /// - /// Gets the current name override for the specified player. + /// Gets a list of visual effects for the specified object visible only to this player. + /// + /// The game object containing the visual effects. + public List? GetLoopingVisualEffects(NwGameObject gameObject) + { + return PlayerLoopingVisualEffectService.Value.GetLoopingVisualEffects(this, gameObject); + } + + /// + /// Gets the current name override set for the specified player. /// /// The specific observer. public PlayerNameOverride? GetPlayerNameOverride(NwPlayer? observer = null) @@ -1004,6 +1035,15 @@ public VisibilityMode GetPersonalVisibilityOverride(NwGameObject target) return PlayerNameOverrideService.Value.GetPlayerNameOverride(this, observer); } + /// + /// Gets the current name override set for the specified object. + /// + /// The game object that has an override name. + public string? GetObjectNameOverride(NwGameObject gameObject) + { + return PlayerObjectNameOverrideService.Value.GetObjectNameOverride(this, gameObject); + } + /// /// Gives the specified XP to the player, adjusted by any multiclass penalty. /// diff --git a/NWN.Anvil/src/main/Services/API/Player/PlayerLoopingVisualEffectService.cs b/NWN.Anvil/src/main/Services/API/Player/PlayerLoopingVisualEffectService.cs new file mode 100644 index 000000000..cbd1e0a2a --- /dev/null +++ b/NWN.Anvil/src/main/Services/API/Player/PlayerLoopingVisualEffectService.cs @@ -0,0 +1,72 @@ +using System.Collections.Generic; +using Anvil.API; +using NLog; +using NWN.Native.API; + +namespace Anvil.Services +{ + [ServiceBinding(typeof(PlayerLoopingVisualEffectService))] + [ServiceBindingOptions(InternalBindingPriority.API, Lazy = true)] + internal sealed unsafe class PlayerLoopingVisualEffectService + { + private static readonly Logger Log = LogManager.GetCurrentClassLogger(); + + private delegate void ComputeGameObjectUpdateForObjectHook(void* pMessage, void* pPlayer, void* pPlayerGameObject, void* pGameObjectArray, uint oidObjectToUpdate); + + private readonly FunctionHook computeGameObjectUpdateForObjectHook; + private readonly Dictionary<(NwPlayer, NwGameObject), List> loopingEffects = new Dictionary<(NwPlayer, NwGameObject), List>(); + + public PlayerLoopingVisualEffectService(HookService hookService) + { + Log.Info($"Initialising optional service {nameof(PlayerLoopingVisualEffectService)}"); + computeGameObjectUpdateForObjectHook = hookService.RequestHook(OnComputeGameObjectUpdateForObject, FunctionsLinux._ZN11CNWSMessage32ComputeGameObjectUpdateForObjectEP10CNWSPlayerP10CNWSObjectP16CGameObjectArrayj, HookOrder.Early); + } + + public List? GetLoopingVisualEffects(NwPlayer player, NwGameObject gameObject) + { + loopingEffects.TryGetValue((player, gameObject), out List? entries); + return entries; + } + + public void AddLoopingVisualEffect(NwPlayer player, NwGameObject gameObject, VisualEffectTableEntry visualEffect) + { + loopingEffects.AddElement((player, gameObject), visualEffect); + } + + public void ClearLoopingVisualEffects(NwPlayer player, NwGameObject gameObject) + { + loopingEffects.Remove((player, gameObject)); + } + + private void OnComputeGameObjectUpdateForObject(void* pMessage, void* pPlayer, void* pPlayerGameObject, void* pGameObjectArray, uint oidObjectToUpdate) + { + NwGameObject? gameObject = oidObjectToUpdate.ToNwObjectSafe(); + NwPlayer? player = CNWSPlayer.FromPointer(pPlayer).ToNwPlayer(); + + if (player == null || gameObject == null) + { + computeGameObjectUpdateForObjectHook.CallOriginal(pMessage, pPlayer, pPlayerGameObject, pGameObjectArray, oidObjectToUpdate); + return; + } + + List? effects = GetLoopingVisualEffects(player, gameObject); + if (effects == null) + { + computeGameObjectUpdateForObjectHook.CallOriginal(pMessage, pPlayer, pPlayerGameObject, pGameObjectArray, oidObjectToUpdate); + return; + } + + foreach (VisualEffectTableEntry effect in effects) + { + gameObject.GameObject.AddLoopingVisualEffect((ushort)effect.RowIndex, NwObject.Invalid, 0); + } + + computeGameObjectUpdateForObjectHook.CallOriginal(pMessage, pPlayer, pPlayerGameObject, pGameObjectArray, oidObjectToUpdate); + + foreach (VisualEffectTableEntry effect in effects) + { + gameObject.GameObject.RemoveLoopingVisualEffect((ushort)effect.RowIndex); + } + } + } +} diff --git a/NWN.Anvil/src/main/Services/API/Player/PlayerObjectNameOverrideService.cs b/NWN.Anvil/src/main/Services/API/Player/PlayerObjectNameOverrideService.cs index 501c2bd00..6b779a3dc 100644 --- a/NWN.Anvil/src/main/Services/API/Player/PlayerObjectNameOverrideService.cs +++ b/NWN.Anvil/src/main/Services/API/Player/PlayerObjectNameOverrideService.cs @@ -21,6 +21,12 @@ public PlayerObjectNameOverrideService(HookService hookService) computeGameObjectUpdateForObjectHook = hookService.RequestHook(OnComputeGameObjectUpdateForObject, FunctionsLinux._ZN11CNWSMessage32ComputeGameObjectUpdateForObjectEP10CNWSPlayerP10CNWSObjectP16CGameObjectArrayj, HookOrder.Early); } + public string? GetObjectNameOverride(NwPlayer player, NwGameObject gameObject) + { + InternalVariableString nameOverride = InternalVariables.ObjectNameOverride(player, gameObject); + return nameOverride.HasValue ? nameOverride.Value : null; + } + public void SetObjectNameOverride(NwPlayer player, NwGameObject gameObject, string name) { if (!IsSupportedObjectType(gameObject)) From e4388144f42dda76ea61ea980a2eec9c55c1d3cb Mon Sep 17 00:00:00 2001 From: Jhett Black <10942655+jhett12321@users.noreply.github.com> Date: Fri, 3 Mar 2023 18:22:05 +1100 Subject: [PATCH 16/17] NwCreature: Add LevelUp. (#632) * NwCreature: Add LevelUp. * Use hookorder.late. * Add tests. --- .../src/main/API/Objects/NwCreatureTests.cs | 51 +++++++++++++++++++ NWN.Anvil/src/main/API/Objects/NwCreature.cs | 37 ++++++++++++++ .../BypassLevelUpValidationService.cs | 51 +++++++++++++++++++ 3 files changed, 139 insertions(+) create mode 100644 NWN.Anvil/src/main/Services/API/Creature/BypassLevelUpValidationService.cs diff --git a/NWN.Anvil.Tests/src/main/API/Objects/NwCreatureTests.cs b/NWN.Anvil.Tests/src/main/API/Objects/NwCreatureTests.cs index 65758cc3d..89ff5f455 100644 --- a/NWN.Anvil.Tests/src/main/API/Objects/NwCreatureTests.cs +++ b/NWN.Anvil.Tests/src/main/API/Objects/NwCreatureTests.cs @@ -261,6 +261,57 @@ public void SetFeatRemainingUsesCorrectlyUpdatesState(byte uses) Assert.That(creature.HasFeatPrepared(feat!), Is.EqualTo(uses > 0), "Creature incorrectly assumes the feat is/is not available."); } + [TestCase(ClassType.Barbarian, 1)] + [TestCase(ClassType.Bard, 1)] + [TestCase(ClassType.Cleric, 1)] + [TestCase(ClassType.Druid, 1)] + [TestCase(ClassType.Fighter, 1)] + [TestCase(ClassType.Ranger, 1)] + [TestCase(ClassType.Rogue, 1)] + [TestCase(ClassType.Sorcerer, 1)] + [TestCase(ClassType.Wizard, 1)] + [TestCase(ClassType.Aberration, 2)] + [TestCase(ClassType.Animal, 2)] + [TestCase(ClassType.Construct, 2)] + [TestCase(ClassType.Humanoid, 2)] + [TestCase(ClassType.Monstrous, 2)] + [TestCase(ClassType.Elemental, 2)] + [TestCase(ClassType.Fey, 2)] + [TestCase(ClassType.Dragon, 2)] + [TestCase(ClassType.Undead, 2)] + [TestCase(ClassType.Commoner, 2)] + [TestCase(ClassType.Beast, 2)] + [TestCase(ClassType.Giant, 2)] + [TestCase(ClassType.MagicalBeast, 2)] + [TestCase(ClassType.Outsider, 2)] + [TestCase(ClassType.Shapechanger, 2)] + [TestCase(ClassType.Vermin, 2)] + public void LevelUpCreatureUpdatesStats(ClassType classType, int levels) + { + Location startLocation = NwModule.Instance.StartingLocation; + NwCreature? creature = NwCreature.Create(StandardResRef.Creature.nw_bandit002, startLocation); + NwClass? nwClass = NwClass.FromClassType(classType); + + Assert.That(creature, Is.Not.Null, "Creature was null after creation."); + Assert.That(nwClass, Is.Not.Null, "Class was null after creation."); + Assert.That(creature!.IsValid, Is.True, "Creature was invalid after creation."); + + createdTestObjects.Add(creature); + CreatureClassInfo? classInfoBefore = creature.GetClassInfo(nwClass); + int classLevels = 0; + + if (classInfoBefore != null) + { + classLevels = classInfoBefore.Level; + } + + creature.LevelUp(nwClass!, levels); + + CreatureClassInfo? classInfo = creature.GetClassInfo(nwClass); + Assert.That(classInfo, Is.Not.Null, "Creature did not have class after level up."); + Assert.That(classInfo!.Level, Is.EqualTo(classLevels + levels), "Creature did not receive the correct amount of levels."); + } + [TearDown] public void CleanupTestObjects() { diff --git a/NWN.Anvil/src/main/API/Objects/NwCreature.cs b/NWN.Anvil/src/main/API/Objects/NwCreature.cs index cc9d1c1d7..d60c20e14 100644 --- a/NWN.Anvil/src/main/API/Objects/NwCreature.cs +++ b/NWN.Anvil/src/main/API/Objects/NwCreature.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using Anvil.Internal; using Anvil.Services; +using NLog; using NWN.Core; using NWN.Native.API; using ObjectType = NWN.Native.API.ObjectType; @@ -17,6 +18,8 @@ namespace Anvil.API [NativeObjectInfo(ObjectTypes.Creature, ObjectType.Creature)] public sealed partial class NwCreature : NwGameObject { + private static readonly Logger Log = LogManager.GetCurrentClassLogger(); + private const byte QuickBarButtonCount = 36; [Inject] @@ -31,6 +34,9 @@ public sealed partial class NwCreature : NwGameObject [Inject] private static Lazy DamageLevelOverrideService { get; set; } = null!; + [Inject] + private static Lazy BypassLevelUpValidationService { get; set; } = null!; + private readonly CNWSCreature creature; private NwFaction faction; @@ -2046,6 +2052,37 @@ public int LevelUpHenchman(NwClass nwClass, PackageType package, bool spellsRead return NWScript.LevelUpHenchman(this, nwClass.Id, (int)package, spellsReady.ToInt()); } + /// + /// Adds levels of the specified class to a creature, bypassing all validation. + /// + /// The class to add. + /// The number of levels to add. + /// Thrown if this creature is a player character. + public void LevelUp(NwClass nwClass, int count) + { + if (IsLoginPlayerCharacter) + { + throw new NotSupportedException("LevelUp may not be used on player characters."); + } + + BypassLevelUpValidationService.Value.DisableValidation = true; + + for (int i = 0; i < count; i++) + { + if (!Creature.m_pStats.LevelUpAutomatic(nwClass.Id, true.ToInt(), (byte)PackageType.Invalid).ToBool()) + { + Log.Error($"Failed to add level of class {nwClass.Name} ({nwClass.Id}), aborting."); + break; + } + } + + BypassLevelUpValidationService.Value.DisableValidation = false; + } + + /// + /// Gets if this creature meets the requirements to take the specified feat. + /// + /// The feat to query. public bool MeetsFeatRequirements(NwFeat feat) { using CExoArrayListUInt16 unused = new CExoArrayListUInt16(); diff --git a/NWN.Anvil/src/main/Services/API/Creature/BypassLevelUpValidationService.cs b/NWN.Anvil/src/main/Services/API/Creature/BypassLevelUpValidationService.cs new file mode 100644 index 000000000..a6f16294c --- /dev/null +++ b/NWN.Anvil/src/main/Services/API/Creature/BypassLevelUpValidationService.cs @@ -0,0 +1,51 @@ +using Anvil.API; +using NWN.Native.API; + +namespace Anvil.Services +{ + [ServiceBinding(typeof(BypassLevelUpValidationService))] + [ServiceBindingOptions(InternalBindingPriority.API, Lazy = true)] + internal sealed unsafe class BypassLevelUpValidationService + { + private delegate int CanLevelUpHook(void* pCreatureStats); + private delegate uint ValidateLevelUpHook(void* pCreatureStats, void* pLevelUpStats, byte nDomain1, byte nDomain2, byte nSchool); + + private readonly FunctionHook canLevelUpHook; + private readonly FunctionHook validateLevelUpHook; + + public bool DisableValidation { get; set; } + + public BypassLevelUpValidationService(HookService hookService) + { + canLevelUpHook = hookService.RequestHook(OnCanLevelUp, FunctionsLinux._ZN17CNWSCreatureStats10CanLevelUpEv, HookOrder.Late); + validateLevelUpHook = hookService.RequestHook(OnValidateLevelUp, FunctionsLinux._ZN17CNWSCreatureStats15ValidateLevelUpEP13CNWLevelStatshhh, HookOrder.Late); + } + + private uint OnValidateLevelUp(void* pCreatureStats, void* pLevelUpStats, byte nDomain1, byte nDomain2, byte nSchool) + { + CNWSCreatureStats creatureStats = CNWSCreatureStats.FromPointer(pCreatureStats); + CNWLevelStats levelStats = CNWLevelStats.FromPointer(pLevelUpStats); + + if (!DisableValidation || creatureStats.m_bIsPC.ToBool()) + { + return validateLevelUpHook.CallOriginal(pCreatureStats, pLevelUpStats, nDomain1, nDomain2, nSchool); + } + + creatureStats.LevelUp(levelStats, nDomain1, nDomain2, nSchool, true.ToInt()); + creatureStats.UpdateCombatInformation(); + return 0; + } + + private int OnCanLevelUp(void* pCreatureStats) + { + CNWSCreatureStats creatureStats = CNWSCreatureStats.FromPointer(pCreatureStats); + + if (!DisableValidation || creatureStats.m_bIsPC.ToBool()) + { + return canLevelUpHook.CallOriginal(pCreatureStats); + } + + return (creatureStats.GetLevel(false.ToInt()) < 60).ToInt(); + } + } +} From d6932225836f78795f9c4a5f295dceb203bf48d8 Mon Sep 17 00:00:00 2001 From: Jhett Black <10942655+jhett12321@users.noreply.github.com> Date: Fri, 3 Mar 2023 18:03:24 +1000 Subject: [PATCH 17/17] Update changelog. --- CHANGELOG.md | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 155f4b182..837ac2620 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,13 +7,20 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). https://github.com/nwn-dotnet/Anvil/compare/v8193.34.24...HEAD ### Added -- N/A +- Exposed `HomeStorage` class for accessing paths in anvil home. +- Added support for creating `Cassowary` solvers through `new Cassowary()` +- NwGameObject: Added `ActionJumpToLocation` method. +- Events: Added `OnPlayerQuickChat` event. +- NwPlayer: Added object name override support (SetObjectNameOverride) +- NwPlayer: Added player-specific looping vfx support (AddLoopingVisualEffect) +- NwCreature: Added `LevelUp` method that bypasses validation. ### Package Updates -- N/A +- NLog: 5.1.1 -> 5.1.2 +- Microsoft.CodeAnalysis.CSharp: 4.4.0 -> 4.5.0 ### Changed -- N/A +- Optional anvil services will now log a message when they are used. ### Deprecated - N/A @@ -22,7 +29,7 @@ https://github.com/nwn-dotnet/Anvil/compare/v8193.34.24...HEAD - N/A ### Fixed -- N/A +- !! Fixed a memory leak when not using `Dispose()` on engine structures. (Effect, Location, ItemProperty, Json, SQLQuery, Talent) ## 8193.34.24 https://github.com/nwn-dotnet/Anvil/compare/v8193.34.23...v8193.34.24