fix: AddonGenerator produces broken/incomplete addon output (#2517)

## Summary

`[AddonGen` currently produces addon scripts that **do not compile**, plus a few
gather-logic and UI bugs. This fixes all of them.

## Compile-breaking (verified)

Every generated addon failed to build because of the item-component emission path:

- **Trailing comma + missing semicolon.** Items were emitted as a multi-line
  `AddComponent(\n … ,\n)` — a trailing comma in the argument list and no terminating
  `;`, i.e. `CS1525: Invalid expression term ')'` and `CS1002: ; expected`.
- **`Deed` missing `new`.** The template emitted
  `public override BaseAddonDeed Deed => {name}AddonDeed();` — invoking the type as a
  method (`CS1955: Non-invocable member … cannot be used like a method`).

Both are now fixed; components are emitted on a single line matching the existing
static-tile path:

```csharp
AddComponent(new AddonComponent(3215) { Light = LightType.Circle300, Hue = 5 }, 2, 3, 5);
```

**Verification:** compiled the generator's *output* (a representative two-component addon —
one plain, one hued + light-source) before and after the change against minimal
`BaseAddon`/`AddonComponent` stubs:

- Before: `CS1525` + `CS1002` (item path), and `CS1955` in isolation for the `Deed` line.
- After: **Build succeeded.**

`Projects/UOContent` also builds clean with the source change.

## Gather-logic + UI (reasoned from the code, not runtime-tested)

- **Inverted Z-range guards.** The tile/item scan used `if (range && …)`, so with the range
  filter off (the default) map tiles and items were never captured — inconsistent with the
  Static pass's `if (!range || …)`. Corrected to match.
- **"Export Items" was dead unless "Export Statics" was also checked** — the items scan was
  nested inside `if (statics)`. Items now scan independently. Placed `Static` items are
  skipped in this path because they're already captured (with hue/light) by the
  `GetItemsInBounds<Static>` pass, which also removes a pre-existing double-count.
- **Swapped gump Min/Max labels** — the "Max" label sat over the Min entry and vice versa.

## Notes

The three gather/UI fixes are reasoned from the code rather than exercised through the
in-game gump, so they're worth a close look in review. The compile fixes are the headline
and are output-verified.
This commit is contained in:
Kamron Batman 2026-07-12 10:15:25 -07:00 committed by GitHub
parent 191d3f3f33
commit 8bd1b3dc28
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -36,7 +36,7 @@ public class AddonGenerator
[SerializationGenerator(0)]
public class {name}Addon : BaseAddon
{
public override BaseAddonDeed Deed => {name}AddonDeed();
public override BaseAddonDeed Deed => new {name}AddonDeed();
[Constructible]
public {name}Addon()
@ -180,7 +180,7 @@ public class AddonGenerator
var tiles = new Dictionary<Point2D, List<StaticTile>>();
if (statics)
if (statics || items)
{
for (var x = start.X; x <= end.X; x++)
{
@ -193,18 +193,28 @@ public class AddonGenerator
{
foreach (var item in map.GetItemsAt(x, y))
{
if (range && item.Z >= min && item.Z <= max)
// Placed Static items are captured (with hue/light) by the
// GetItemsInBounds<Static> pass below; skip them here to avoid duplicates.
if (item is Static)
{
continue;
}
if (!range || item.Z >= min && item.Z <= max)
{
list.Add(new StaticTile((ushort)item.ItemID, (sbyte)item.Z));
}
}
}
foreach (var staticTile in tileMatrix.GetStaticTiles(x, y))
if (statics)
{
if (range && staticTile.Z >= min && staticTile.Z <= max)
foreach (var staticTile in tileMatrix.GetStaticTiles(x, y))
{
list.Add(staticTile);
if (!range || staticTile.Z >= min && staticTile.Z <= max)
{
list.Add(staticTile);
}
}
}
@ -294,29 +304,25 @@ public class AddonGenerator
var light = (item.ItemData.Flags & TileFlag.LightSource) == TileFlag.LightSource ? item.Light : LightType.Empty;
var hue = item.Hue;
sb.Append(" AddComponent(\n");
sb.Append($" AddComponent(new AddonComponent({item.ItemID})");
if (hue != 0 || light != LightType.Empty)
{
sb.Append($" new AddonComponent({item.ItemID})\n");
sb.Append(" {\n");
sb.Append(" {");
if (light != LightType.Empty)
{
sb.Append($" Light = LightType.{light},\n");
sb.Append($" Light = LightType.{light}");
if (hue != 0)
{
sb.Append($", Hue = {hue}");
}
}
if (hue != 0)
else
{
sb.Append($" Hue = {hue},\n");
sb.Append($" Hue = {hue}");
}
sb.Append(" },\n");
sb.Append(" }");
}
else
{
sb.Append($" new AddonComponent({item.ItemID}),\n");
}
sb.Append($" {xOffset},\n");
sb.Append($" {yOffset},\n");
sb.Append($" {zOffset},\n");
sb.Append(" )\n");
sb.Append($", {xOffset}, {yOffset}, {zOffset});\n");
}
// Only pull in Server.Items when the generated addon lives in a different namespace.
@ -387,11 +393,11 @@ public class AddonGenerator
AddCheck(20, 135, 2510, 2511, _state.Range, 2);
AddLabel(40, 135, LabelHue, "Specify Z Range");
AddLabel(50, 160, LabelHue, "Max");
AddLabel(50, 160, LabelHue, "Min");
AddImageTiled(85, 175, 60, 1, 9304);
AddTextEntry(85, 155, 60, 20, LabelHue, 2, _state.Min.ToString());
AddLabel(160, 160, LabelHue, "Min");
AddLabel(160, 160, LabelHue, "Max");
AddImageTiled(200, 175, 60, 1, 9304);
AddTextEntry(200, 155, 60, 20, LabelHue, 3, _state.Max.ToString());