Skip to content

Commit

Permalink
Implement 8193.36 new script functions/APIs (#753)
Browse files Browse the repository at this point in the history
* Bump NWNX/NWN.Core/NWN.Native versions.

* Update constants.

* Add item possessor tests.

* Use new GetItemPossessor APIs for root/non-root item possessor.

* Add placeable version of possessor test.

* Implement 8193.36 new functions/API.

* Add missing surface material table.

* Bump NWNX version.

* Formatting fixes.
  • Loading branch information
jhett12321 authored Feb 17, 2024
1 parent 6d22926 commit 0cbe693
Show file tree
Hide file tree
Showing 28 changed files with 496 additions and 47 deletions.
2 changes: 1 addition & 1 deletion .env
Original file line number Diff line number Diff line change
@@ -1 +1 @@
NWNX_VERSION=0e77a56
NWNX_VERSION=9865013
4 changes: 2 additions & 2 deletions NWN.Anvil.TestRunner/NWN.Anvil.TestRunner.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="NWN.Core" Version="8193.36.0" PrivateAssets="compile" />
<PackageReference Include="NWN.Native" Version="8193.36.1" PrivateAssets="compile" />
<PackageReference Include="NWN.Core" Version="8193.36.1" PrivateAssets="compile" />
<PackageReference Include="NWN.Native" Version="8193.36.2" PrivateAssets="compile" />
</ItemGroup>

<ItemGroup>
Expand Down
4 changes: 2 additions & 2 deletions NWN.Anvil.Tests/NWN.Anvil.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="NWN.Core" Version="8193.36.0" PrivateAssets="compile" />
<PackageReference Include="NWN.Native" Version="8193.36.1" PrivateAssets="compile" />
<PackageReference Include="NWN.Core" Version="8193.36.1" PrivateAssets="compile" />
<PackageReference Include="NWN.Native" Version="8193.36.2" PrivateAssets="compile" />
</ItemGroup>

<ItemGroup>
Expand Down
84 changes: 84 additions & 0 deletions NWN.Anvil.Tests/src/main/API/Objects/NwItemTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,90 @@ public void SerializeItemAppearancePreservesAppearance(string itemResRef)
Assert.That(item.Appearance.GetSimpleModel(), Is.EqualTo(model));
}

[Test(Description = "An item stored in an item container returns the correct possessing object.")]
public void ItemInContainerOnGroundReturnsCorrectPossessor()
{
Location startLocation = NwModule.Instance.StartingLocation;
NwItem? container = NwItem.Create(StandardResRef.Item.nw_it_contain006, startLocation);
NwItem? item = NwItem.Create(StandardResRef.Item.nw_it_gem013, startLocation);

Assert.That(container, Is.Not.Null, "Item nw_it_contain006 was null after creation.");
Assert.That(container!.IsValid, Is.True, "Item nw_it_contain006 was invalid after creation.");

createdTestObjects.Add(container);

Assert.That(item, Is.Not.Null, "Item nw_it_gem013 was null after creation.");
Assert.That(item!.IsValid, Is.True, "Item nw_it_gem013 was invalid after creation.");

createdTestObjects.Add(item);

container.AcquireItem(item);

Assert.That(item.Possessor, Is.EqualTo(container), "Item possessor does not match expected container.");
Assert.That(item.RootPossessor, Is.Null, "Root possessor is not null.");
}

[Test(Description = "An item stored in an item container on a creature returns the correct possessing object.")]
public void ItemInContainerOnCreatureReturnsCorrectPossessor()
{
Location startLocation = NwModule.Instance.StartingLocation;
NwItem? container = NwItem.Create(StandardResRef.Item.nw_it_contain006, startLocation);
NwItem? item = NwItem.Create(StandardResRef.Item.nw_it_gem013, startLocation);
NwCreature? creature = NwCreature.Create(StandardResRef.Creature.nw_bandit001, startLocation);

Assert.That(container, Is.Not.Null, "Item nw_it_contain006 was null after creation.");
Assert.That(container!.IsValid, Is.True, "Item nw_it_contain006 was invalid after creation.");

createdTestObjects.Add(container);

Assert.That(item, Is.Not.Null, "Item nw_it_gem013 was null after creation.");
Assert.That(item!.IsValid, Is.True, "Item nw_it_gem013 was invalid after creation.");

createdTestObjects.Add(item);

Assert.That(creature, Is.Not.Null, "Creature nw_bandit001 was null after creation.");
Assert.That(creature!.IsValid, Is.True, "Creature nw_bandit001 was invalid after creation.");

createdTestObjects.Add(creature);

container.AcquireItem(item);
creature.AcquireItem(container);

Assert.That(item.Possessor, Is.EqualTo(container), "Item possessor does not match expected container.");
Assert.That(item.RootPossessor, Is.EqualTo(creature), "Root possessor is not the expected creature.");
}

[Test(Description = "An item stored in an item container on a placeable returns the correct possessing object.")]
public void ItemInContainerOnPlaceableReturnsCorrectPossessor()
{
Location startLocation = NwModule.Instance.StartingLocation;
NwItem? container = NwItem.Create(StandardResRef.Item.nw_it_contain006, startLocation);
NwItem? item = NwItem.Create(StandardResRef.Item.nw_it_gem013, startLocation);
NwPlaceable? placeable = NwPlaceable.Create(StandardResRef.Placeable.plc_chest1, startLocation);

Assert.That(container, Is.Not.Null, "Item nw_it_contain006 was null after creation.");
Assert.That(container!.IsValid, Is.True, "Item nw_it_contain006 was invalid after creation.");

createdTestObjects.Add(container);

Assert.That(item, Is.Not.Null, "Item nw_it_gem013 was null after creation.");
Assert.That(item!.IsValid, Is.True, "Item nw_it_gem013 was invalid after creation.");

createdTestObjects.Add(item);

Assert.That(placeable, Is.Not.Null, "Placeable plc_chest1 was null after creation.");
Assert.That(placeable!.IsValid, Is.True, "Placeable plc_chest1 was invalid after creation.");

createdTestObjects.Add(placeable);
placeable.HasInventory = true;

container.AcquireItem(item);
placeable.AcquireItem(container);

Assert.That(item.Possessor, Is.EqualTo(container), "Item possessor does not match expected container.");
Assert.That(item.RootPossessor, Is.EqualTo(placeable), "Root possessor is not the expected placeable.");
}

[TearDown]
public void CleanupTestObjects()
{
Expand Down
4 changes: 2 additions & 2 deletions NWN.Anvil/NWN.Anvil.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="NLog" Version="5.2.5" />
<PackageReference Include="Paket.Core" Version="7.2.1" PrivateAssets="all" />
<PackageReference Include="NWN.Core" Version="8193.36.0" PrivateAssets="compile" />
<PackageReference Include="NWN.Native" Version="8193.36.1" PrivateAssets="compile" />
<PackageReference Include="NWN.Core" Version="8193.36.1" PrivateAssets="compile" />
<PackageReference Include="NWN.Native" Version="8193.36.2" PrivateAssets="compile" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="all" />
</ItemGroup>

Expand Down
18 changes: 18 additions & 0 deletions NWN.Anvil/src/main/API/Constants/AudioStreamIdentifier.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using NWN.Core;

namespace Anvil.API
{
public enum AudioStreamIdentifier
{
Identifier0 = NWScript.AUDIOSTREAM_IDENTIFIER_0,
Identifier1 = NWScript.AUDIOSTREAM_IDENTIFIER_1,
Identifier2 = NWScript.AUDIOSTREAM_IDENTIFIER_2,
Identifier3 = NWScript.AUDIOSTREAM_IDENTIFIER_3,
Identifier4 = NWScript.AUDIOSTREAM_IDENTIFIER_4,
Identifier5 = NWScript.AUDIOSTREAM_IDENTIFIER_5,
Identifier6 = NWScript.AUDIOSTREAM_IDENTIFIER_6,
Identifier7 = NWScript.AUDIOSTREAM_IDENTIFIER_7,
Identifier8 = NWScript.AUDIOSTREAM_IDENTIFIER_8,
Identifier9 = NWScript.AUDIOSTREAM_IDENTIFIER_9,
}
}
12 changes: 12 additions & 0 deletions NWN.Anvil/src/main/API/Constants/EffectType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,5 +86,17 @@ public enum EffectType
BonusFeat = NWScript.EFFECT_TYPE_BONUS_FEAT,
TimeStopImmunity = NWScript.EFFECT_TYPE_TIMESTOP_IMMUNITY,
ForceWalk = NWScript.EFFECT_TYPE_FORCE_WALK,
Appear = NWScript.EFFECT_TYPE_APPEAR,
CutsceneDominated = NWScript.EFFECT_TYPE_CUTSCENE_DOMINATED,
Damage = NWScript.EFFECT_TYPE_DAMAGE,
Death = NWScript.EFFECT_TYPE_DEATH,
Disappear = NWScript.EFFECT_TYPE_DISAPPEAR,
Heal = NWScript.EFFECT_TYPE_HEAL,
HitpointChangeWhenDying = NWScript.EFFECT_TYPE_HITPOINTCHANGEWHENDYING,
Knockdown = NWScript.EFFECT_TYPE_KNOCKDOWN,
ModifyAttacks = NWScript.EFFECT_TYPE_MODIFY_ATTACKS,
SummonCreature = NWScript.EFFECT_TYPE_SUMMON_CREATURE,
Taunt = NWScript.EFFECT_TYPE_TAUNT,
Wounding = NWScript.EFFECT_TYPE_WOUNDING,
}
}
2 changes: 2 additions & 0 deletions NWN.Anvil/src/main/API/Constants/GuiEventType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,7 @@ public enum GuiEventType
OptionsOpen = NWScript.GUIEVENT_OPTIONS_OPEN,
OptionsClose = NWScript.GUIEVENT_OPTIONS_CLOSE,
RadialOpen = NWScript.GUIEVENT_RADIAL_OPEN,
ChatlogPortraitClick = NWScript.GUIEVENT_CHATLOG_PORTRAIT_CLICK,
PlayerlistPlayerTell = NWScript.GUIEVENT_PLAYERLIST_PLAYER_TELL,
}
}
3 changes: 3 additions & 0 deletions NWN.Anvil/src/main/API/Constants/PlayerDeviceProperty.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ public sealed class PlayerDeviceProperty
public static readonly PlayerDeviceProperty UiSpellbookSortSpells = new PlayerDeviceProperty(NWScript.PLAYER_DEVICE_PROPERTY_UI_SPELLBOOK_SORT_SPELLS);
public static readonly PlayerDeviceProperty UiRadialSpellcastingAlwaysSubradial = new PlayerDeviceProperty(NWScript.PLAYER_DEVICE_PROPERTY_UI_RADIAL_SPELLCASTING_ALWAYS_SUBRADIAL);
public static readonly PlayerDeviceProperty UiRadialClassAbilitiesAlwaysSubradial = new PlayerDeviceProperty(NWScript.PLAYER_DEVICE_PROPERTY_UI_RADIAL_CLASS_ABILITIES_ALWAYS_SUBRADIAL);
public static readonly PlayerDeviceProperty UiDisplayLoadscreenHintsInChatlog = new PlayerDeviceProperty(NWScript.PLAYER_DEVICE_PROPERTY_UI_DISPLAY_LOADSCREEN_HINTS_IN_CHATLOG);
public static readonly PlayerDeviceProperty UiMouseScale = new PlayerDeviceProperty(NWScript.PLAYER_DEVICE_PROPERTY_UI_MOUSE_SCALE);
public static readonly PlayerDeviceProperty UiMouseScaleValue = new PlayerDeviceProperty(NWScript.PLAYER_DEVICE_PROPERTY_UI_MOUSE_SCALE_VALUE);
public static readonly PlayerDeviceProperty CameraMode = new PlayerDeviceProperty(NWScript.PLAYER_DEVICE_PROPERTY_CAMERA_MODE);
public static readonly PlayerDeviceProperty CameraEdgeTurning = new PlayerDeviceProperty(NWScript.PLAYER_DEVICE_PROPERTY_CAMERA_EDGE_TURNING);
public static readonly PlayerDeviceProperty CameraDialogZoom = new PlayerDeviceProperty(NWScript.PLAYER_DEVICE_PROPERTY_CAMERA_DIALOG_ZOOM);
Expand Down
7 changes: 7 additions & 0 deletions NWN.Anvil/src/main/API/Constants/ResRefType.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// ReSharper disable InconsistentNaming

namespace Anvil.API
{
public enum ResRefType
Expand Down Expand Up @@ -84,6 +86,11 @@ public enum ResRefType
TML = NWN.Native.API.ResRefType.TML,
SQ3 = NWN.Native.API.ResRefType.SQ3,
LOD = NWN.Native.API.ResRefType.LOD,
GIF = NWN.Native.API.ResRefType.GIF,
PNG = NWN.Native.API.ResRefType.PNG,
JPG = NWN.Native.API.ResRefType.JPG,
CAF = NWN.Native.API.ResRefType.CAF,
JUI = NWN.Native.API.ResRefType.JUI,
IDS = NWN.Native.API.ResRefType.IDS,
ERF = NWN.Native.API.ResRefType.ERF,
BIF = NWN.Native.API.ResRefType.BIF,
Expand Down
19 changes: 15 additions & 4 deletions NWN.Anvil/src/main/API/EngineStructures/Effect.Create.cs
Original file line number Diff line number Diff line change
Expand Up @@ -250,9 +250,10 @@ public static Effect DamageIncrease(int bonus, DamageType damageType = DamageTyp
/// <param name="amount">The damage to remove from each attack.</param>
/// <param name="damagePower">The max enchantment/power bonus of the weapon this effect will resist.</param>
/// <param name="totalAbsorb">The total amount of damage to absorb, before the effect is removed (0 = infinite).</param>
public static Effect DamageReduction(int amount, DamagePower damagePower, int totalAbsorb = 0)
/// <param name="rangedOnly">Set to true to have this damage reduction effect only apply to ranged attacks.</param>
public static Effect DamageReduction(int amount, DamagePower damagePower, int totalAbsorb = 0, bool rangedOnly = false)
{
return NWScript.EffectDamageReduction(amount, (int)damagePower, totalAbsorb)!;
return NWScript.EffectDamageReduction(amount, (int)damagePower, totalAbsorb, rangedOnly.ToInt())!;
}

/// <summary>
Expand All @@ -261,9 +262,10 @@ public static Effect DamageReduction(int amount, DamagePower damagePower, int to
/// <param name="damageType">The type of damage to resist.</param>
/// <param name="amount">The damage to remove from each attack.</param>
/// <param name="totalAbsorb">The total amount of damage to absorb, before the effect is removed (0 = infinite).</param>
public static Effect DamageResistance(DamageType damageType, int amount, int totalAbsorb = 0)
/// <param name="rangedOnly">Set to true to have this damage resistance effect only apply to ranged attacks.</param>
public static Effect DamageResistance(DamageType damageType, int amount, int totalAbsorb = 0, bool rangedOnly = false)
{
return NWScript.EffectDamageResistance((int)damageType, amount, totalAbsorb)!;
return NWScript.EffectDamageResistance((int)damageType, amount, totalAbsorb, rangedOnly.ToInt())!;
}

/// <summary>
Expand Down Expand Up @@ -895,5 +897,14 @@ public static Effect VisualEffect(VisualEffectTableEntry visualEffect, bool miss
{
return NWScript.EffectVisualEffect(visualEffect.RowIndex, missEffect.ToInt(), fScale, vTranslate, vRotate)!;
}

/// <summary>
/// Creates an effect that gives a creature with melee/ranged/touched attacks a bonus to hit.
/// </summary>
/// <param name="bonus">The additional attack bonus.</param>
public static Effect EnemyAttackBonus(int bonus)
{
return NWScript.EffectEnemyAttackBonus(bonus)!;
}
}
}
2 changes: 1 addition & 1 deletion NWN.Anvil/src/main/API/EngineStructures/Effect.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public EffectDuration DurationType
/// <summary>
/// Gets the type of this effect.
/// </summary>
public EffectType EffectType => (EffectType)NWScript.GetEffectType(this);
public EffectType EffectType => (EffectType)NWScript.GetEffectType(this, true.ToInt());

/// <summary>
/// Gets or sets the subtype of this effect.
Expand Down
17 changes: 17 additions & 0 deletions NWN.Anvil/src/main/API/EngineStructures/SQLQuery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,23 @@ public IEnumerable<SQLResult> Results
}
}

/// <summary>
/// Gets the name of the columns declared as a part of this query.
/// </summary>
public string[] Columns
{
get
{
string[] columns = new string[NWScript.SqlGetColumnCount(this)];
for (int i = 0; i < columns.Length; i++)
{
columns[i] = NWScript.SqlGetColumnName(this, i);
}

return columns;
}
}

protected override int StructureId => NWScript.ENGINE_STRUCTURE_SQLQUERY;

public static implicit operator SQLQuery(IntPtr intPtr)
Expand Down
53 changes: 53 additions & 0 deletions NWN.Anvil/src/main/API/EngineStructures/SQLResult.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using System.Numerics;
using NWN.Core;

Expand Down Expand Up @@ -25,6 +26,16 @@ public float GetFloat(int columnIndex)
return NWScript.SqlGetFloat(query, columnIndex);
}

/// <summary>
/// Gets the float result for the specified column.
/// </summary>
/// <param name="columnName">The name of the column to fetch the result.</param>
/// <returns>The float result. Returns 0.0f on an error.</returns>
public float GetFloat(string columnName)
{
return NWScript.SqlGetFloat(query, Array.IndexOf(query.Columns, columnName));
}

/// <summary>
/// Gets the int result for the specified column.
/// </summary>
Expand All @@ -35,6 +46,16 @@ public int GetInt(int columnIndex)
return NWScript.SqlGetInt(query, columnIndex);
}

/// <summary>
/// Gets the int result for the specified column.
/// </summary>
/// <param name="columnName">The name of the column to fetch the result.</param>
/// <returns>The int result. Returns 0.0f on an error.</returns>
public float GetInt(string columnName)
{
return NWScript.SqlGetInt(query, Array.IndexOf(query.Columns, columnName));
}

/// <summary>
/// Gets the serialized object result for the specified column, and spawns the object at the specified location and inventory.
/// </summary>
Expand All @@ -47,6 +68,18 @@ public int GetInt(int columnIndex)
return NWScript.SqlGetObject(query, columnIndex, spawnLocation, targetInventory).ToNwObject<T>();
}

/// <summary>
/// Gets the serialized object result for the specified column, and spawns the object at the specified location and inventory.
/// </summary>
/// <param name="columnName">The name of the column to fetch the result.</param>
/// <param name="spawnLocation">The location to spawn the object.</param>
/// <param name="targetInventory">(Items only) The target inventory for the item.</param>
/// <returns>The deserialized object. Returns null on an error.</returns>
public T? GetObject<T>(string columnName, Location spawnLocation, NwGameObject? targetInventory = null) where T : NwObject
{
return NWScript.SqlGetObject(query, Array.IndexOf(query.Columns, columnName), spawnLocation, targetInventory).ToNwObject<T>();
}

/// <summary>
/// Gets the string result for the specified column.
/// </summary>
Expand All @@ -57,6 +90,16 @@ public string GetString(int columnIndex)
return NWScript.SqlGetString(query, columnIndex);
}

/// <summary>
/// Gets the string result for the specified column.
/// </summary>
/// <param name="columnName">The name of the column to fetch the result.</param>
/// <returns>The string result. Returns "" on an error.</returns>
public string GetString(string columnName)
{
return NWScript.SqlGetString(query, Array.IndexOf(query.Columns, columnName));
}

/// <summary>
/// Gets the <see cref="Vector3"/> result for the specified column.
/// </summary>
Expand All @@ -66,5 +109,15 @@ public Vector3 GetVector3(int columnIndex)
{
return NWScript.SqlGetVector(query, columnIndex);
}

/// <summary>
/// Gets the <see cref="Vector3"/> result for the specified column.
/// </summary>
/// <param name="columnName">The name of the column to fetch the result.</param>
/// <returns>The <see cref="Vector3"/> result. Returns <see cref="Vector3.Zero"/> on an error.</returns>
public Vector3 GetVector3(string columnName)
{
return NWScript.SqlGetVector(query, Array.IndexOf(query.Columns, columnName));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,15 @@ public sealed class OnPlayerGuiEvent : IEvent
/// Gets the object data associated with this GUI event.
/// </summary>
/// <remarks>
/// <see cref="GuiEventType.ChatBarFocus"/>: The selected chat channel. Does not indicate the actual used channel. 0 = Shout, 1 = Whisper, 2 = Talk, 3 = Party, 4 = DM
/// <see cref="GuiEventType.CharacterSheetSkillClick"/>: The <see cref="NwSkill"/>
/// <see cref="GuiEventType.MinimapMapPinClick"/>: The waypoint the map note is attached to.
/// <see cref="GuiEventType.CharacterSheetSkillClick"/>: The owner of the character sheet.<br/>
/// <see cref="GuiEventType.CharacterSheetFeatClick"/>: The owner of the character sheet.<br/>
/// <see cref="GuiEventType.PlayerListPlayerClick"/>: The player that was clicked.<br/>
/// <see cref="GuiEventType.PartyBarPortraitClick"/>: The creature that was clicked.<br/>
/// <see cref="GuiEventType.DisabledPanelAttemptOpen"/>: For <see cref="GUIPanel.CharacterSheet"/>, the owner of the character sheet. For GUIPanel.Examine*, the object being examined.<br/>
/// <see cref="GuiEventType.ExamineObject"/>: The object being examined.<br/>
/// <see cref="GuiEventType.ChatlogPortraitClick"/>: The owner of the portrait that was clicked.<br/>
/// <see cref="GuiEventType.PlayerlistPlayerTell"/>: The selected player.<br/>
/// </remarks>
public NwObject EventObject { get; } = NWScript.GetLastGuiEventObject().ToNwObject()!;

Expand Down
Loading

0 comments on commit 0cbe693

Please sign in to comment.