Skip to content

Commit bdde90b

Browse files
committed
Merge branch 'development'
2 parents 54aa4e5 + a80e763 commit bdde90b

14 files changed

+98
-50
lines changed

CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,22 @@ All notable changes to this project will be documented in this file.
33

44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
55

6+
## 8193.34.27
7+
https://github.com/nwn-dotnet/Anvil/compare/v8193.34.26...v8193.34.27
8+
9+
### Added
10+
- NwCreature: Added `BodyBag`, `BodyBagTemplate` properties.
11+
- NwPlaceable: Added `IsBodyBag` property.
12+
13+
### Changed
14+
- OnPlayerGuiEvent: `EffectIcon` property is now nullable.
15+
- OnDebugPlayVisualEffect: `Effect` property is now nullable.
16+
- APIs that accept a `TableEntry` parameter now have implicit casts (e.g. `EffectIconTableEntry` & `EffectIcon`).
17+
18+
### Fixed
19+
- NwCreature: Fixed an infinite loop caused by the `Associates` property when having dominated creature associates.
20+
- Added some index/range checks for some usages of game table data.
21+
622
## 8193.34.26
723
https://github.com/nwn-dotnet/Anvil/compare/v8193.34.25...v8193.34.26
824

NWN.Anvil/src/main/API/EngineStructures/ItemProperty.cs

Lines changed: 4 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Linq;
23
using NWN.Core;
34
using NWN.Native.API;
45

@@ -17,19 +18,7 @@ internal ItemProperty(CGameEffect effect, bool memoryOwn) : base(effect, memoryO
1718
/// <summary>
1819
/// Gets the cost table used by this item property.
1920
/// </summary>
20-
public TwoDimArray<ItemPropertyCostTableEntry>? CostTable
21-
{
22-
get
23-
{
24-
int tableIndex = Effect.GetInteger(2);
25-
if (tableIndex >= 0 && tableIndex < NwGameTables.ItemPropertyCostTables.Count)
26-
{
27-
return NwGameTables.ItemPropertyCostTables[tableIndex].Table;
28-
}
29-
30-
return null;
31-
}
32-
}
21+
public TwoDimArray<ItemPropertyCostTableEntry>? CostTable => NwGameTables.ItemPropertyCostTables.ElementAtOrDefault(Effect.GetInteger(2))?.Table;
3322

3423
/// <summary>
3524
/// Gets or sets the cost table entry that is set for this item property.<br/>
@@ -62,19 +51,7 @@ public EffectDuration DurationType
6251
/// <summary>
6352
/// Gets the #1 param table used by this item property.
6453
/// </summary>
65-
public TwoDimArray<ItemPropertyParamTableEntry>? Param1Table
66-
{
67-
get
68-
{
69-
int tableIndex = Effect.GetInteger(4);
70-
if (tableIndex >= 0 && tableIndex < NwGameTables.ItemPropertyParamTables.Count)
71-
{
72-
return NwGameTables.ItemPropertyParamTables[tableIndex].Table;
73-
}
74-
75-
return null;
76-
}
77-
}
54+
public TwoDimArray<ItemPropertyParamTableEntry>? Param1Table => NwGameTables.ItemPropertyParamTables.ElementAtOrDefault(Effect.GetInteger(4))?.Table;
7855

7956
/// <summary>
8057
/// Gets or sets the #1 param table entry that is set for this item property.<br/>
@@ -119,18 +96,7 @@ public ItemPropertyParamTableEntry? Param1TableValue
11996
/// </summary>
12097
public ItemPropertySubTypeTableEntry? SubType
12198
{
122-
get
123-
{
124-
int tableIndex = Effect.GetInteger(1);
125-
TwoDimArray<ItemPropertySubTypeTableEntry>? table = Property.SubTypeTable;
126-
127-
if (tableIndex >= 0 && table != null && tableIndex < table.Count)
128-
{
129-
return table[tableIndex];
130-
}
131-
132-
return null;
133-
}
99+
get => Property.SubTypeTable?.ElementAtOrDefault(Effect.GetInteger(1));
134100
set => Effect.SetInteger(1, value?.RowIndex ?? -1);
135101
}
136102

NWN.Anvil/src/main/API/Events/Game/ModuleEvents/ModuleEvents.OnPlayerGuiEvent.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Linq;
23
using Anvil.API.Events;
34
using NWN.Core;
45

@@ -25,7 +26,7 @@ public sealed class OnPlayerGuiEvent : IEvent
2526
/// <summary>
2627
/// Gets the effect icon that was selected. Only valid in <see cref="GuiEventType.EffectIconClick"/> events.
2728
/// </summary>
28-
public EffectIconTableEntry EffectIcon => NwGameTables.EffectIconTable[integerEventData];
29+
public EffectIconTableEntry? EffectIcon => NwGameTables.EffectIconTable.ElementAtOrDefault(integerEventData);
2930

3031
/// <summary>
3132
/// Gets the object data associated with this GUI event.

NWN.Anvil/src/main/API/Events/Native/DebugEvents/DebugEventFactory.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Linq;
23
using System.Numerics;
34
using System.Runtime.InteropServices;
45
using Anvil.Services;
@@ -120,7 +121,7 @@ private static OnDebugPlayVisualEffect HandleVisualEffectEvent(CNWSMessage messa
120121
{
121122
Player = player,
122123
TargetObject = target.ToNwObject(),
123-
Effect = NwGameTables.VisualEffectTable[visualEffect],
124+
Effect = NwGameTables.VisualEffectTable.ElementAtOrDefault(visualEffect),
124125
Duration = TimeSpan.FromSeconds(duration),
125126
TargetPosition = position,
126127
});

NWN.Anvil/src/main/API/Events/Native/DebugEvents/OnDebugPlayVisualEffect.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public sealed class OnDebugPlayVisualEffect : IEvent
1212
/// <summary>
1313
/// Gets the effect that is attempting to be played.
1414
/// </summary>
15-
public VisualEffectTableEntry Effect { get; internal init; } = null!;
15+
public VisualEffectTableEntry? Effect { get; internal init; }
1616

1717
/// <summary>
1818
/// Gets the player attempting to spawn the visual effect.

NWN.Anvil/src/main/API/Objects/NwCreature.cs

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ internal CNWSCreature Creature
5252
internal NwCreature(CNWSCreature creature) : base(creature)
5353
{
5454
this.creature = creature;
55-
faction = new NwFaction(creature.GetFaction());
55+
faction = new NwFaction(Creature.GetFaction());
5656
Inventory = new Inventory(this, Creature.m_pcItemRepository);
5757
}
5858

@@ -197,6 +197,20 @@ public byte BaseShieldArcaneSpellFailure
197197
set => Creature.m_pStats.m_nBaseShieldArcaneSpellFailure = value;
198198
}
199199

200+
/// <summary>
201+
/// Gets the body bag assigned to this creature as a result of its death.
202+
/// </summary>
203+
public NwPlaceable? BodyBag => Creature.m_oidBodyBag.ToNwObject<NwPlaceable>();
204+
205+
/// <summary>
206+
/// Gets or sets the body bag template to use when this creature dies.
207+
/// </summary>
208+
public BodyBagTableEntry BodyBagTemplate
209+
{
210+
get => NwGameTables.BodyBagTable[Creature.m_nBodyBag];
211+
set => Creature.m_nBodyBag = (byte)value.RowIndex;
212+
}
213+
200214
/// <summary>
201215
/// Gets or sets the calculated challenge rating for this creature.
202216
/// </summary>
@@ -2529,16 +2543,23 @@ private async Task DoActionUnlockObject(NwGameObject target)
25292543
NWScript.ActionUnlockObject(target);
25302544
}
25312545

2532-
private IEnumerable<NwCreature> GetAssociates(AssociateType associateType)
2546+
private List<NwCreature> GetAssociates(AssociateType associateType)
25332547
{
2534-
int i;
2535-
uint current;
2548+
List<NwCreature> associates = new List<NwCreature>();
25362549
int type = (int)associateType;
25372550

2538-
for (i = 1, current = NWScript.GetAssociate(type, this, i); current != Invalid; i++, current = NWScript.GetAssociate(type, this, i))
2551+
for (int i = 0;; i++)
25392552
{
2540-
yield return current.ToNwObject<NwCreature>()!;
2553+
NwCreature? associate = NWScript.GetAssociate(type, this, i).ToNwObject<NwCreature>();
2554+
if (associate == null || associates.Contains(associate))
2555+
{
2556+
break;
2557+
}
2558+
2559+
associates.Add(associate);
25412560
}
2561+
2562+
return associates;
25422563
}
25432564

25442565
private PlayerQuickBarButton InternalGetQuickBarButton(byte index)

NWN.Anvil/src/main/API/Objects/NwPlaceable.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,11 @@ public bool Illumination
7070
/// </summary>
7171
public Inventory Inventory { get; }
7272

73+
/// <summary>
74+
/// Gets if this is a body bag placeable, spawned from a creature's death.
75+
/// </summary>
76+
public bool IsBodyBag => placeable.m_bIsBodyBag.ToBool();
77+
7378
public bool IsStatic
7479
{
7580
get => Placeable.m_bStaticObject.ToBool();

NWN.Anvil/src/main/API/TwoDimArray/Tables/AppearanceTableEntry.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using System.Linq;
2+
13
namespace Anvil.API
24
{
35
/// <summary>
@@ -115,5 +117,10 @@ void ITwoDimArrayEntry.InterpretEntry(TwoDimArrayEntry entry)
115117
BodyBag = entry.GetTableEntry("BODY_BAG", NwGameTables.BodyBagTable);
116118
Targetable = entry.GetBool("TARGETABLE");
117119
}
120+
121+
public static implicit operator AppearanceTableEntry?(AppearanceType appearanceType)
122+
{
123+
return NwGameTables.AppearanceTable.ElementAtOrDefault((int)appearanceType);
124+
}
118125
}
119126
}

NWN.Anvil/src/main/API/TwoDimArray/Tables/EffectIconTableEntry.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using System.Linq;
2+
13
namespace Anvil.API
24
{
35
/// <summary>
@@ -28,5 +30,10 @@ public void InterpretEntry(TwoDimArrayEntry entry)
2830
Icon = entry.GetString("Icon");
2931
StrRef = entry.GetStrRef("StrRef");
3032
}
33+
34+
public static implicit operator EffectIconTableEntry?(EffectIcon effectIcon)
35+
{
36+
return NwGameTables.EffectIconTable.ElementAtOrDefault((int)effectIcon);
37+
}
3138
}
3239
}

NWN.Anvil/src/main/API/TwoDimArray/Tables/ItemPropertyTableEntry.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using System.Linq;
2+
13
namespace Anvil.API
24
{
35
public sealed class ItemPropertyTableEntry : ITwoDimArrayEntry
@@ -53,7 +55,7 @@ public sealed class ItemPropertyTableEntry : ITwoDimArrayEntry
5355

5456
void ITwoDimArrayEntry.InterpretEntry(TwoDimArrayEntry entry)
5557
{
56-
ItemMap = NwGameTables.ItemPropertyItemMapTable.GetRow(RowIndex);
58+
ItemMap = NwGameTables.ItemPropertyItemMapTable.ElementAtOrDefault(RowIndex);
5759
Name = entry.GetStrRef("Name");
5860
Label = entry.GetString("Label");
5961
SubTypeTable = entry.GetTable<ItemPropertySubTypeTableEntry>("SubTypeResRef");
@@ -63,5 +65,10 @@ void ITwoDimArrayEntry.InterpretEntry(TwoDimArrayEntry entry)
6365
GameStrRef = entry.GetStrRef("GameStrRef");
6466
Description = entry.GetStrRef("Description");
6567
}
68+
69+
public static implicit operator ItemPropertyTableEntry?(ItemPropertyType propertyType)
70+
{
71+
return NwGameTables.ItemPropertyTable.ElementAtOrDefault((int)propertyType);
72+
}
6673
}
6774
}

NWN.Anvil/src/main/API/TwoDimArray/Tables/PartsTableEntry.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using System.Linq;
2+
13
namespace Anvil.API
24
{
35
public sealed class PartsTableEntry : ITwoDimArrayEntry
@@ -14,7 +16,7 @@ public void InterpretEntry(TwoDimArrayEntry entry)
1416
{
1517
CostModifier = entry.GetInt("COSTMODIFIER");
1618
ACBonus = entry.GetFloat("ACBONUS");
17-
ArmorTableEntry = ACBonus.HasValue ? NwGameTables.ArmorTable[(int)ACBonus.Value] : null;
19+
ArmorTableEntry = ACBonus.HasValue ? NwGameTables.ArmorTable.ElementAtOrDefault((int)ACBonus.Value) : null;
1820
}
1921
}
2022
}

NWN.Anvil/src/main/API/TwoDimArray/Tables/ProgrammedEffectTableEntry.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using System.Linq;
2+
13
namespace Anvil.API
24
{
35
public sealed class ProgrammedEffectTableEntry : ITwoDimArrayEntry
@@ -43,5 +45,10 @@ public void InterpretEntry(TwoDimArrayEntry entry)
4345
intParamValues[i] = entry.GetInt(columnName);
4446
}
4547
}
48+
49+
public static implicit operator ProgrammedEffectTableEntry?(ProgFxType fxType)
50+
{
51+
return NwGameTables.ProgrammedEffectTable.ElementAtOrDefault((int)fxType);
52+
}
4653
}
4754
}

NWN.Anvil/src/main/API/TwoDimArray/Tables/VisualEffectTableEntry.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using System.Linq;
2+
13
namespace Anvil.API
24
{
35
public sealed class VisualEffectTableEntry : ITwoDimArrayEntry
@@ -88,5 +90,10 @@ void ITwoDimArrayEntry.InterpretEntry(TwoDimArrayEntry entry)
8890
LowQualityVariant = entry.GetString("LowQuality");
8991
OrientWithObject = entry.GetBool("OrientWithObject");
9092
}
93+
94+
public static implicit operator VisualEffectTableEntry?(VfxType vfxType)
95+
{
96+
return NwGameTables.VisualEffectTable.ElementAtOrDefault((int)vfxType);
97+
}
9198
}
9299
}

NWN.Anvil/src/main/Services/API/Creature/DamageLevelOverrideService.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Linq;
12
using Anvil.API;
23
using NLog;
34
using NWN.Native.API;
@@ -36,7 +37,7 @@ public void ClearDamageLevelOverride(NwCreature creature)
3637
InternalVariableInt damageLevelOverride = InternalVariables.DamageLevelOverride(creature);
3738
if (damageLevelOverride.HasValue)
3839
{
39-
return NwGameTables.DamageLevelTable[damageLevelOverride.Value];
40+
return NwGameTables.DamageLevelTable.ElementAtOrDefault(damageLevelOverride.Value);
4041
}
4142

4243
return null;

0 commit comments

Comments
 (0)