Commit graph

478 commits

Author SHA1 Message Date
Jack
6cdec42146
fix: Special moves and various UOR+ bonuses (#2384)
## Special moves
Special moves were added [27 Apr, 2000.](https://uo.com/wiki/ultima-online-wiki/technical/previous-publishes/2000-2/2000-publish-05-27th-april/). This is well past the discussed cutoff date for T2A, which is **[Nov 23, 1999 - Publish 1](https://uo.com/wiki/ultima-online-wiki/technical/previous-publishes/1999-2/1999-publish-01-23rd-november/)** as per the UOSecondAge shard which is the golden standard for T2A accuracy and represents a logical cutoff for this era. 

Regarding the specific date: **Nov 23, 1999 (Publish 1)** - the game significantly changed as a "pre-patch" to UOR on this date, and UOSecondAge picks and chooses from this date based on QoL features, while keeping the gameplay accurate to what is widely known as the "T2A era". However this date is ~4 months before the actual UOR release date.

## Defensive wrestling
Defensively wrestling as (eval+anat)/2 was added in [publish 16](https://uo.com/wiki/ultima-online-wiki/technical/previous-publishes/2002-2/publish-16-changes-and-bug-fixes-24th-july/) and should be gated behind !Core.LBR
* "Unarmed “to-be-hit” changes (chance to get hit while unarmed is now the better of either Wrestling skill, or an average of Anatomy & Evaluate Intelligence)."

## Lumberjacking damage bonus

Lumberjacking damage bonus was added in [UOR](https://uo.com/wiki/ultima-online-wiki/technical/previous-publishes/2000-2/2000-publish-05-27th-april/)

## Anatomy and LJ +10% GM bonus
Anatomy/LJ +10% GM bonus was added in [pub 13](https://uo.com/wiki/ultima-online-wiki/technical/previous-publishes/2001-2/2001-publish-13-19th-august/), which was during UOTD.
2026-03-22 10:55:01 -07:00
Kamron Batman
992bc95164
feat: Refactor Poison system, implement Darkglow & Parasitic effects (#2385)
## Summary

- Refactors the poison system to separate `Index` (globally unique ID) from `Level` (tier within a family), enabling multiple poison families (Standard, Darkglow, Parasitic) to coexist without collisions
- Implements Darkglow and Parasitic poison special effects from Mondain's Legacy: Darkglow boosts damage by 10% when attacker is ranged, Parasitic heals the attacker for damage dealt in melee range
- Fixes several bugs: `Register()` crashing on duplicate `Level` values across families, `IncreaseLevel()` crossing family boundaries, `InfectiousStrike` and `NinjaWeapons` stripping poison family via level-based lookups, and `ArchCure`/`CleansingWinds` using raw `Level + 1` instead of `IncreaseLevel()`

## Changes

**`Projects/Server/Poison.cs`** — Adds `PoisonFamily` enum and abstract `Family` property. Adds `Index` as unique identifier. Fixes `Register()` to check `Index` uniqueness (not `Level`) and validate the new poison's name (not the existing one's). Fixes `IncreaseLevel()` to use `Index + 1`, naturally respecting family boundaries via Index gaps. Replaces linear name lookup with `Dictionary`-based `PoisonsByName`.

**`Projects/UOContent/Misc/Poison.cs`** — Adds `family` parameter to `PoisonImpl`. Implements Darkglow effect (10% damage boost when `From` >1 tile, cliloc 1072850) and Parasitic effect (heals `From` for damage dealt within 1 tile, cliloc 1060203) in `PoisonTimer.OnTick()`. Renames `m_` fields to `_` convention.

**`Projects/UOContent/Misc/PoisonKinds.cs`** — New file. Moves poison registration out of `PoisonImpl` into `PoisonKinds.Configure()`. Adds `PoisonFamily` to Darkglow/Parasitic registrations. Provides extension properties (`Lesser`, `Deadly`, `LesserDarkglow`, etc.), `GetPoison(int level)` (standard-only), `GetPoisonByFamilyAndLevel()`, and `IsDarkglow`/`IsParasitic` instance helpers.

**`Projects/UOContent/Items/Weapons/Abilities/InfectiousStrike.cs`** — Family-aware poison scaling: Darkglow caps at Deadly (Poisoning/33.3), Parasitic caps at Lethal (Poisoning/25), Standard unchanged. Level bump uses `IncreaseLevel()` with family boundary check.

**`Projects/UOContent/Items/Skill Items/Ninjitsu/NinjaWeapons.cs`** — EvilOmen level bump uses `Poison.IncreaseLevel()` instead of `Poison.GetPoison(Level + 1)`.

**`Projects/UOContent/Spells/Fourth/ArchCure.cs`** and **`CleansingWindsSpell.cs`** — Replace `poison.Level + 1` with `Poison.IncreaseLevel(poison).Level` for family-safe cure chance calculation.

**`Projects/Server/Serialization/SerializationExtensions.cs`** — Serializes/deserializes `Index` instead of `Level`.

**`DarkglowPotion.cs`** / **`ParasiticPotion.cs`** — Point to actual Darkglow/Parasitic poisons instead of placeholder `Greater`.

**`PotionKeg.cs`** / **`BasePotion.cs`** — Adds Darkglow, Parasitic, Invisibility, and FlintsPungentBrew to `PotionEffect` enum and keg label support.

## Test plan

- [ ] `dotnet build` compiles cleanly (verified, 0 warnings 0 errors)
- [ ] Verify `PoisonKinds.Configure()` registers all poisons without throwing (Register bug fix)
- [ ] Standard poison behavior unchanged — PoisonField, PoisonSpell, SerpentArrow, SavageShaman, TrappableContainer all use `GetPoison(int level)` which now correctly filters to Standard family
- [ ] Darkglow: poison tick deals +10% damage when attacker is >1 tile away, sends "Darkglow poison increases your damage!" message
- [ ] Parasitic: poison tick heals attacker for damage dealt when within 1 tile, sends heal message
- [ ] InfectiousStrike preserves poison family and respects family-specific skill scaling
- [ ] EvilOmen + NinjaWeapons level bump stays within poison family
- [ ] ArchCure/CleansingWinds cure chance calculations work correctly across all poison families
- [ ] Serialization round-trips correctly using Index
2026-03-21 21:27:22 -07:00
Joe
ff10811d3d
feat: Adds Endless Decanter of Water and Water Elemental acquisition (#2371)
## Summary

- Adds the Endless Decanter of Water (introduced in Publish 66.2 / SA era)
- Players throw a full Pitcher of Water at a Water Elemental for a 10% chance to receive the decanter; the pitcher is always destroyed on impact
- Each Water Elemental can only yield one decanter; the state is persisted and previously saved elementals are migrated to the new serialization version
- The decanter auto-refills from a linked water trough when the owner empties it within 10 tiles of the stored trough location
- Linking stores a Point3D + Map snapshot, supporting both static tile troughs and addon troughs
- The decanter is blessed and displays Linked/Unlinked status in its tooltip
2026-03-14 16:45:06 -07:00
Jack
6b6cc10771
feat: T2A ping-pong mechanic and consider sins behaviour (#2356)
## Summary

- Adds `Mobile.Murderer` virtual property and consolidates kill-threshold checks across the codebase
- Tracks ping-pong count: how many times a player crosses the 5-kill murderer threshold (T2A/UOR/UOTD only, disabled on LBR+)
- Adds `[CommandProperty]` to view a player's ping-pong count via the admin panel
- After enough ping-pongs, player is permanently flagged as a murderer regardless of kill count
- Accounts for perma-red players with low kills in murderer status transition notifications
- Implements era-appropriate "I must consider my sins" speech responses:
  - **T2A**: contextual cliloc flavor text (502122–502126)
  - **UOR–AOS**: raw short/long-term murder counts + ping-pong count if applicable
  - **SE+**: localized stats message (1114370)
- Refactors kill-report logic out of `Keywords.cs` into `PlayerMurderSystem.ReportKillsToSelf`

## Testing

- [x] Thoroughly tested and self reviewed
- [x] Test T2A "I must consider my sins" behaviour over all scenarios.
- [x] Test UOR "I must consider my sins" behaviour over all scenarios.
- [x] Test that LBR does not have ping pongs enabled (I must consider my sins)
- [x] Test serialization cross over from v0 -> v1 increments 1 ping pong if player is already red.

## Notes
* Manually setting kills to 5 does not trigger a ping pong, it must go through the actual murder system. This includes if the kills were manually set to 5 and then migrated (as manually setting kills to 5 never adds the player into the murder system - it only happens via ReportMurderer). This is arguably a bug in the existing system, but one that currently only ever happens via staff interaction.
* Thieves guild SuspendOnMurder specifically checks for kills > 0. This means a person with 0 shorts but 5 ping pongs (flagged as murderer) can steal. This may be accurate, as according to a forum post this is how it works on UOSA which is the T2A gold standard.
* This doesn't implement Pre-T2A behaviour which should be that "I must consider my sins" does nothing at all. The reason I didn't implement it for Pre-T2A is then it 100% have to sit behind a feature flag. I don't mind adding it as a feature flag, just let me know.
2026-03-10 23:10:27 -07:00
Jack
6745cf2075
feat: Adds Mobile.Murderer virtual property, consolidates kill-threshold checks (#2355)
## Summary

- Adds `public virtual bool Murderer => Kills >= 5` property to `Mobile`, replacing ~30 scattered `Kills >= 5` / `Kills < 5` magic-number checks across the codebase
- `BaseCreature` overrides `Murderer` to also return `true` when `AlwaysMurderer` is set
- Updates `Corpse` serialization to v15, storing `_murderer` as a `bool` field (migrated from `int Kills >= 5` in earlier versions)
2026-03-06 08:36:41 -08:00
Bohica
2b80286d2e
fix: Fixes arrow animations by using 0xC7 when possible. (#2343)
### Summary

This is a partial fix for servers that enable 0xC7 and clients that support 0xC7. A proper fix should be made on ClassicUO.
2026-03-05 23:23:20 -08:00
Kamron Batman
e1e1a7c640
fix: Bumps deps. Updates copyrights (#2353) 2026-03-05 19:36:54 -08:00
Kamron Batman
c494fb4cc3
fix: Fixes container enumeration not recycling pooled arrays (#2341)
### Summary

Updates all calls to container.EnumerateItems() to properly dispose of the underlying PooledRefQueue so that we are properly recycling pooled arrays.
2026-02-17 09:54:32 -08:00
Kamron Batman
4b392079e9
fix: Fixes exploits with bank box deposits. Makes stacking more efficient (#2337)
### Summary

- Fix AccountGold gold duplication exploit: When AccountGold.Enabled was true, double-clicking a BankCheck deposited
the full value to the account but then continued creating physical gold piles for the same amount, duplicating the
value
- Fix Deposit/DepositUpTo partial deposit exploit: Both methods created new max-size gold piles and checks without
first filling existing partial stacks, wasting container slots and causing premature "bank full" failures that could
be leveraged to manipulate gold distribution
- Fix BagOfSending bank stacking: Gold and BankCheck items sent via BagOfSending now use Banker.Deposit for efficient
stacking instead of naive TryDropItem, which could fail on a full bank even when existing piles had room
- Improve gold/check deposit efficiency: Banker.Deposit and Banker.DepositUpTo now top off existing gold piles (up to
60k) and bank checks (up to 1M) before creating new items, maximizing use of available container slots
2026-02-13 20:34:35 -08:00
Kamron Batman
f7eab79adb
fix: Fixes arrows in the internal map (#2336) 2026-02-12 21:31:42 -08:00
Kamron Batman
f7f1265216
fix: Fixes PotionKeg causing weight issues (#2330) 2026-02-08 20:55:15 -08:00
Kamron Batman
b191498569
feat: Adds Feature Flag System (#2328)
## Feature Flag System

A runtime feature flag system for dynamically enabling/disabling game systems and blocking specific game elements
without server restarts.

### Overview

Two types of controls:

1. Feature Flags - Named boolean flags that gate entire systems (trading, PvP, vendors, housing, etc.)
2. Dynamic Blocks - Block specific gumps, items, skills, or spells by type

All changes are persisted to JSON, broadcast to online staff, and bypass-able by administrators.

### Predefined Flags
```
┌─────────────────┬──────────┬─────────────────────────────────────┐
│      Flag       │ Category │             Description             │
├─────────────────┼──────────┼─────────────────────────────────────┤
│ player_trading  │ Economy  │ Allow secure trades between players │
├─────────────────┼──────────┼─────────────────────────────────────┤
│ vendor_purchase │ Economy  │ Allow purchasing from NPC vendors   │
├─────────────────┼──────────┼─────────────────────────────────────┤
│ vendor_sell     │ Economy  │ Allow selling to NPC vendors        │
├─────────────────┼──────────┼─────────────────────────────────────┤
│ player_vendors  │ Economy  │ Allow player vendor interactions    │
├─────────────────┼──────────┼─────────────────────────────────────┤
│ bank_access     │ Economy  │ Allow bank box access               │
├─────────────────┼──────────┼─────────────────────────────────────┤
│ house_placement │ Housing  │ Allow new house placements          │
├─────────────────┼──────────┼─────────────────────────────────────┤
│ boat_placement  │ Housing  │ Allow new boat placements           │
├─────────────────┼──────────┼─────────────────────────────────────┤
│ bulk_orders     │ Crafting │ Allow bulk order deeds              │
├─────────────────┼──────────┼─────────────────────────────────────┤
│ pvp_combat      │ Combat   │ Allow player vs player combat       │
└─────────────────┴──────────┴─────────────────────────────────────┘
```

### Commands

#### Feature Flags
```
┌────────────────────────────────────────────────────┬───────────────────────┐
│                      Command                       │      Description      │
├────────────────────────────────────────────────────┼───────────────────────┤
│ [FeatureFlag <key> on|off|toggle|info              │ Manage a feature flag │
├────────────────────────────────────────────────────┼───────────────────────┤
│ [FF <key> on|off                                   │ Shorthand alias       │
├────────────────────────────────────────────────────┼───────────────────────┤
│ [FeatureFlag <key> create <category> <desc>        │ Create a custom flag  │
├────────────────────────────────────────────────────┼───────────────────────┤
│ [FeatureFlag <key> delete                          │ Delete a custom flag  │
├────────────────────────────────────────────────────┼───────────────────────┤
│ [FeatureList [flags|gumps|items|skills|spells|all] │ List all flags/blocks │
├────────────────────────────────────────────────────┼───────────────────────┤
│ [FeatureAdmin                                      │ Open the admin gump   │
└────────────────────────────────────────────────────┴───────────────────────┘
```

#### Gump Blocks
```
┌────────────────────────────────┬──────────────────────────────────────────────────────┐
│            Command             │                     Description                      │
├────────────────────────────────┼──────────────────────────────────────────────────────┤
│ [BlockGump <typeName> [reason] │ Block a gump type from displaying                    │
├────────────────────────────────┼──────────────────────────────────────────────────────┤
│ [UnblockGump <typeName>        │ Remove a gump block                                  │
├────────────────────────────────┼──────────────────────────────────────────────────────┤
│ [ListGumps [self]              │ List open gumps on a player (for finding type names) │
└────────────────────────────────┴──────────────────────────────────────────────────────┘
```

#### Item Blocks
```
┌────────────────────────────────────────────────┬───────────────────────────────┐
│                    Command                     │          Description          │
├────────────────────────────────────────────────┼───────────────────────────────┤
│ [BlockItemUse <typeName|target> [reason]       │ Block item use                │
├────────────────────────────────────────────────┼───────────────────────────────┤
│ [BlockItemEquip <typeName|target> [reason]     │ Block item equipping          │
├────────────────────────────────────────────────┼───────────────────────────────┤
│ [BlockItemContainer <typeName|target> [reason] │ Block container access        │
├────────────────────────────────────────────────┼───────────────────────────────┤
│ [UnblockItemUse <typeName>                     │ Remove use block              │
├────────────────────────────────────────────────┼───────────────────────────────┤
│ [UnblockItemEquip <typeName>                   │ Remove equip block            │
├────────────────────────────────────────────────┼───────────────────────────────┤
│ [UnblockItemContainer <typeName>               │ Remove container access block │
└────────────────────────────────────────────────┴───────────────────────────────┘
```
Item block commands create an entry if one doesn't exist, or flip the relevant flag on an existing entry. Unblock
commands only clear their specific flag -- the entry is removed when all flags (use/equip/container) are off.

#### Skill & Spell Blocks
```
┌──────────────────────────────────────┬──────────────────────┐
│               Command                │     Description      │
├──────────────────────────────────────┼──────────────────────┤
│ [BlockSkill <SkillName> [reason]     │ Block a skill        │
├──────────────────────────────────────┼──────────────────────┤
│ [UnblockSkill <SkillName>            │ Remove a skill block │
├──────────────────────────────────────┼──────────────────────┤
│ [BlockSpell <SpellTypeName> [reason] │ Block a spell        │
├──────────────────────────────────────┼──────────────────────┤
│ [UnblockSpell <SpellTypeName>        │ Remove a spell block │
└──────────────────────────────────────┴──────────────────────┘
```

### Architecture

- Static bool classes for hot-path checks (no dictionary lookups): ServerFeatureFlags (Server project) and
ContentFeatureFlags (UOContent)
- Synced automatically by FeatureFlagManager.SyncStaticFlag() when flags change
- Predefined flags loaded from Distribution/Configuration/FeatureFlags/default-flags.json
- Runtime state persisted to Configuration/FeatureFlags/*.json (flags, gump-blocks, item-blocks, skill-blocks,
spell-blocks)
- Admin gump with tabbed UI for managing all block types, per-entry PropertiesGump editing, and pagination
- All staff at AccessLevel.GameMaster+ bypass dynamic blocks; AccessLevel.Administrator+ bypass feature flags

### Hook Points

Hook: Player trading
Location: Mobile.OpenTrade
Mechanism: ServerFeatureFlags.PlayerTrading
────────────────────────────────────────
Hook: PvP combat
Location: Mobile.CanBeHarmful
Mechanism: ServerFeatureFlags.PvPCombat
────────────────────────────────────────
Hook: Bank access
Location: BankBox.Open()
Mechanism: ServerFeatureFlags.BankAccess
────────────────────────────────────────
Hook: Vendor buy/sell
Location: BaseVendor
Mechanism: ContentFeatureFlags.VendorPurchase/Sell
────────────────────────────────────────
Hook: Player vendors
Location: PlayerVendor
Mechanism: ContentFeatureFlags.PlayerVendors
────────────────────────────────────────
Hook: House placement
Location: HousePlacement.Check()
Mechanism: ContentFeatureFlags.HousePlacement (returns BadRegionTemp)
────────────────────────────────────────
Hook: Boat placement
Location: BaseBoatDeed.OnDoubleClick/OnPlacement
Mechanism: ContentFeatureFlags.BoatPlacement
────────────────────────────────────────
Hook: Bulk orders
Location: SmallBOD/LargeBOD
Mechanism: ContentFeatureFlags.BulkOrders
────────────────────────────────────────
Hook: Gump display
Location: GumpSystem.SendGump
Mechanism: FeatureFlagManager.IsGumpBlocked
────────────────────────────────────────
Hook: Item use
Location: PlayerMobile.AllowItemUse
Mechanism: FeatureFlagManager.IsItemUseBlocked
────────────────────────────────────────
Hook: Item equip
Location: PlayerMobile.CheckEquip
Mechanism: FeatureFlagManager.IsItemEquipBlocked
────────────────────────────────────────
Hook: Container access
Location: BaseContainer.DisplayTo
Mechanism: FeatureFlagManager.IsContainerAccessBlocked
────────────────────────────────────────
Hook: Skill use
Location: PlayerMobile.AllowSkillUse
Mechanism: FeatureFlagManager.IsSkillBlocked
────────────────────────────────────────
Hook: Spell casting
Location: Spell.Cast / Spellbook.CastSpellRequest
Mechanism: FeatureFlagManager.IsSpellBlocked
2026-02-07 12:02:57 -08:00
Kamron Batman
fd2f90cba9
feat: Adds SerializableFieldChanged option (#2324) 2026-02-01 21:28:26 -08:00
Kamron Batman
0404251638
feat: Adds Latin1 text support (#2317)
## Summary

- Adds proper Latin1 encoding support, replacing CP1252 usage throughout the codebase
- Adds specialized, optimized string decoding methods with safe string filtering for each encoding type
- Filters invalid Unicode characters (C0/C1 control codes, non-characters) by removal rather than replacement since
the UO client renders nothing for these characters
- Fixes UTF-16 null terminator position handling to correctly advance by 2 bytes

## Changes

TextEncoding.cs

- Added SearchValues-based invalid byte/char detection for efficient filtering
- Added encoding-specific GetString methods: GetStringAscii, GetStringLatin1, GetStringUtf8, GetStringBigUni,
GetStringLittleUni
- Each method supports a safeString parameter for filtering invalid characters
- Little-endian UTF-16 uses direct memory cast for zero-copy decoding on LE systems
- Invalid characters are removed (not replaced with U+FFFD) since the client renders nothing for them

SpanReader.cs

- Added ReadLatin1() and ReadLatin1Safe() methods
- Rewrote encoding-specific read methods to use optimized TextEncoding.GetString* methods
- Fixed UTF-16 null terminator handling: position now correctly advances by byteLength (2) instead of 1

SpanWriter.cs

- Added WriteLatin1 and WriteLatin1Null methods

## Packet Updates

- Updated all packet code to use Latin1 encoding instead of CP1252
- Affected: account packets, equipment packets, menu packets, message packets, mobile packets, player packets, secure
trade packets, vendor packets, gump packets, book packets, mahjong packets

## Filtering Behavior

Invalid characters filtered in safe mode:
```
┌───────────────┬────────────────────────┐
│     Range     │      Description       │
├───────────────┼────────────────────────┤
│ 0x00-0x1F     │ C0 control codes       │
├───────────────┼────────────────────────┤
│ 0x7F          │ DEL                    │
├───────────────┼────────────────────────┤
│ 0x80-0x9F     │ C1 control codes       │
├───────────────┼────────────────────────┤
│ 0xFFFE-0xFFFF │ Unicode non-characters │
└───────────────┴────────────────────────┘
```

Note: Surrogate pairs (0xD800-0xDFFF) are not filtered because proper validation requires context checking for paired
vs unpaired surrogates. The UO client renders nothing for these anyway.

## Test Plan

- All 631 Server.Tests pass
- Verified client rendering behavior using TestUnicodeGump command (pages 1-5)
- Confirmed U+FFFD, unpaired surrogates, and non-characters all render as blank in client
- Verified Latin1 characters (0xA0-0xFF) display correctly
- Verified C1 control codes (0x80-0x9F) are filtered and don't display
2026-01-22 15:51:00 -08:00
Kamron Batman
ee2cd1d18d
feat: Adds new decay system and SkipSerialization (#2311)
## Summary

- Replaces scan-based decay checking during world saves with an event-driven timer wheel scheduler
- Removes virtual property checks from serialization hot path, achieving 6-8x faster world saves

## Changes

### DecayScheduler (new):
- Timer wheel with 12 HashSet buckets (5-min intervals) + PriorityQueue for active processing
- O(1) register/unregister vs O(n) scan of all items
- Auto start/stop when items exist/empty
- Configurable tick interval with jitter to prevent system synchronization

### Item.cs:
- Added ScheduledDecayTime computed property
- Added UpdateDecayRegistration() called from SetLastMoved(), property setters, AddItem()/RemoveItem()
- Hooks in Visible, Movable, Spawner setters and Delete()

### World.cs:
- Removed _decayQueue, EnqueueForDecay(), ProcessDecay()
- ItemPersistence.Serialize() now tight loop without virtual calls

## Performance

| Metric        | Before     | After     | Improvement |
|---------------|------------|-----------|-------------|
| Items (230K)  | 245K ticks | 34K ticks | 8x faster   |
| Mobiles (43K) | 56K ticks  | 6K ticks  | 9x faster   |
| Total         | 302K ticks | 40K ticks | 7.5x faster |

## Configuration

decay.maxItemsPerTick = 250       # Items processed per tick
decay.tickInterval = 256ms        # Base processing interval
decay.bucketInterval = 5min       # Timer wheel bucket size
decay.jitterMaxMs = 25            # ±25ms tick jitter
2026-01-08 11:43:51 -08:00
Kamron Batman
c14f361de1
fix: Moves containers/bods/traps/etc to serialization generator (#2310) 2026-01-07 21:55:55 -08:00
Kamron Batman
d3b43f6f0e
feat: Fixes spawner bugs with position finding, Adds [ShowSpawnerBorders (#2297)
### Summary

- Fixes BaseSpawner and Spawner bugs.
- Adds [ShowSpawnerBorders to see the spawn bounds.
2025-12-28 18:26:03 -08:00
Kamron Batman
cc4a679fb0
fix: Fixes serializing EngravedText (#2296) 2025-12-28 02:44:49 -08:00
Kamron Batman
3e8d548f38
feat: Add spawn position caching and spiral scan optimization (#2295)
### Summary

Adds spawn position caching and optimization for constrained spawners (e.g., those near houses, water, or blocked terrain).

### Key features:
- Sector-based bitmap cache (32 bytes per 16x16 sector) stores valid spawn positions
- Spiral scan progressively discovers positions from spawner center outward
- Automatic mode detects constrained spawners after 5+ non-transient failures
- Prevents mob spawning inside private houses (allows public AoS buildings)
- Deduplicates sector lookups for multi-bounds spawners (RegionSpawner)
- Cache invalidation on house placement/demolition
- Moves SpawnBounds to Spawner

### New spawner properties:
- SpawnPositionMode: Automatic (default), Enabled, Disabled, Abandoned
- MaxSpawnAttempts: Configurable attempts before optimization engages (default: 5)
2025-12-28 02:40:21 -08:00
Kamron Batman
ebaf104935
chore: Use var everywhere (#2294) 2025-12-27 16:47:28 -08:00
Kamron Batman
8048711483
fix: Fixes ConditionTeleporter.DenyFollowers (#2284) 2025-11-29 16:28:14 -08:00
Kamron Batman
7bd5a853a3
feat: Adds size/style support to gump builders, optimizes EscapeHtml (#2257)
> [!IMPORTANT]
> **Developer Note**
> THIS IS A BREAKING CHANGE TO THE NEW API GUMP.
> Please give us feedback in [discord ](https://muo.gg/discord) if you have issues, need help, or have ideas for a better API change!

### Summary

* Adds support for size/style to dynamic/static builder.
* Drastically simplifies the dynamic/static builder api for AddHtml.
* Cleans up some legacy gump files.
2025-11-23 11:35:31 -08:00
Kamron Batman
4836bff5eb
fix: Eliminates List allocations in various places. (#2158) 2025-11-16 18:33:53 -08:00
Kamron Batman
bbb7dc2294
fix: Fixes dropping a stack of scrolls on a spellbook (#2248) 2025-08-09 20:55:36 -07:00
Kamron Batman
fa0b67ec29
fix: Optimizes items to use default weights. (Part 4) (#2243) 2025-07-24 23:29:29 -07:00
Kamron Batman
40ba85d7b8
fix: Optimizes items to use default weights. (Part 3) (#2242) 2025-07-24 16:21:27 -07:00
Kamron Batman
2c5441731e
fix: Optimizes items to use default weights. (Part 2) (#2241) 2025-07-24 15:41:44 -07:00
Kamron Batman
2eba2868a6
fix: Optimizes items to use default weights. (Part 1) (#2240) 2025-07-24 14:44:39 -07:00
Kamron Batman
8a118170e7
fix: Fixes treasure map chest crash from null guardians (#2236) 2025-07-22 16:01:27 -07:00
Felipe Maya Muniz
4209f7ea16
fix: Add properties for crafting ranged weapons with colored logs (#2222) 2025-07-07 18:51:08 -07:00
Bohica
35faba5087
fix: fixes the id for farmable cabbage (#2227) 2025-07-02 15:04:48 -07:00
Kamron Batman
a286e282a9
fix: Fixes fist disarm (pre-aos) (#2214) 2025-06-07 18:22:54 -07:00
Kamron Batman
0d2ed60fed
fix: Fixes null components in BaseAddon (#2208) 2025-06-02 22:11:40 -10:00
Kamron Batman
f3bcf89015
fix: Fixes weapons not serializing lesser poison (#2194)
### Summary

Fixes weapons not serializing lesser poison.

> [!NOTE]
> Dev Note: After applying this fix, fix weapons already in-game using the following command
> `[global set poison lesser where baseweapon poison = null poisoncharges > 0`
2025-05-23 15:54:17 -07:00
Kamron Batman
e468f79e18
feat: Adds SpecialAttack monster ability trigger. Fixes fanning fire. (#2186) 2025-05-15 22:14:52 -07:00
Kamron Batman
da517f56c4
chore: Fixes variable types for possible performance issues (#2172) 2025-05-01 21:54:41 -07:00
Kamron Batman
47b03f5d62
fix: Fixes NPCs not able to equip armor/clothing/weapons (#2161) 2025-04-21 14:01:08 -07:00
Kamron Batman
4aa272d429
feat: Adds an Item/Mobile memory leak detector. Fixes minor leak in doors. (#2159)
### Summary

Adds the command [TrackLeaks to enable tracking item/mobiles that have been deleted but still have dangling references. Requires adding the _TRACK_LEAKS_ define constant during build.
2025-04-15 19:14:45 -07:00
Kamron Batman
a94814a7ef
fix: Fixes and optimizes NameVerification and ProfanityProtection (#2153)
### Summary

Significantly improves the performance of NameVerification & ProfanityProtection:

```cs
| Method         | Mean      | Error     | StdDev    |
|--------------- |----------:|----------:|----------:|
| ValidateName   | 728.34 ns | 13.244 ns | 11.059 ns |
| ValidateNameSV |  26.62 ns |  0.233 ns |  0.207 ns |
```
2025-04-12 02:26:10 -07:00
Kamron Batman
56e3086ed1
fix: Makes hairdye gump static (#2138) 2025-03-04 15:55:05 -08:00
Kamron Batman
2e08eda8cb
feat: Standardizes South/East selection gumps (#1839) 2025-02-14 00:15:14 -08:00
Kamron Batman
279b10dd0f
feat: Replaces params array with params ReadOnlySpan (#2125) 2025-02-13 21:19:02 -08:00
Kamron Batman
40479c946a
feat: Adds item graphic size to Bounds.bin and makes item graphic offset available to gumps (#2115) 2025-02-10 19:31:39 -08:00
Bohica
772511d1e8
fix: Reverts GetProtOffset, Adds PreT2A Weapon Damage & PreUOR Armor Durability (#2114) 2025-02-06 01:27:40 -08:00
Bohica
51367df0d2
fix: Fixes AR/Durability/Tool Uses for Pre-UOR and LBR (#2113) 2025-02-05 19:54:56 -08:00
Kamron Batman
d407847fc4
fix: Fixes axes not working when equipped or in backpack (#2112) 2025-02-04 22:11:29 -08:00
Kamron Batman
ed87df1a61
fix: Converts bedroll/insurance gumps to static. (#2096) 2025-01-27 14:00:32 -08:00
Kamron Batman
ea364237f0
fix: Fix crash from not enough fish to kill in Aquarium (#2092) 2025-01-25 18:45:20 -08:00
Kamron Batman
a8b625b338
fix: Aquarium no longer uses list to kill fish (#2090) 2025-01-25 17:41:27 -08:00
mark1145
d5160bd6db
fix: Fixes buffs don't start/stop properly. Streamlines constructor and Add/Remove buffs. (#2082) 2025-01-24 18:20:23 -08:00