Commit graph

1565 commits

Author SHA1 Message Date
68802df7e2 #W# Spawn: Lots of spawn work and tweaks. 2026-11-02 14:19:47 -05:00
488fa12039 #W# Citizens: Added more monster types and more custom dialog. 2026-11-02 11:23:21 -05:00
75243a4f13 #W# Work: Working on signs ugh. 2026-11-01 14:20:11 -05:00
22ffccfec4 #W# Added: OzThoth Static Gump. 2026-11-01 12:52:57 -05:00
55aada83d4 #W# Added: Town gathering spots for citizens. 2026-11-01 10:53:29 -05:00
d05a274ba6 #W# Added: Made a dynamic citizen spawner that only spawns them on roads and dirt. 2026-10-31 15:35:38 -04:00
ae245cdb69 #W# Fix: CrystalOfKnowlege no longer drops crafting skills. 2026-10-31 13:10:04 -04:00
822e82f12b #W# Citizens: Basic idea for citizens that walk around and talk. 2026-10-31 12:45:44 -04:00
69a2b8772b #W# Bosses: Working on Dungeon Bosses. Added some reward drops to increase stat/skill/totalskill cap. 2026-10-31 12:44:45 -04:00
00c25cd506 #W# New: Portal Spawner that spawns a mob. if the mob doesn't have a combatant after 3 mins it despawns. 2026-10-30 21:04:26 -04:00
21a4cc1ce5 #W# Spawn: Spawning some of the new creatures. 2026-10-30 20:16:36 -04:00
63b29bfc0d #W# Mobs: Tweaked some mob stats. 2026-10-30 14:38:33 -04:00
fbda425fee #W# Mobs: Still working on adding some new mobs. 2026-10-30 14:14:38 -04:00
5bdc30d7ea #W# New: Working on adding new mobs. 2026-10-29 18:52:00 -04:00
ec8264feb0 #W# New: Added a bunch of monster animations. 2026-10-29 17:07:09 -04:00
1b1d7eaf86 #W# Tweak: Cave toads are no longer aggressive. 2026-10-29 10:40:33 -04:00
0daf369b33 #W# Bosses: Working on Dungeon Bosses. Summon Items and Alters. 2026-10-28 18:58:52 -04:00
76e38ff3f7 #W# Crafting: Did some crafting queue polishing. 2026-10-28 15:27:09 -04:00
712247762c #W# Craft: Added a nice crafting queue system. To access the queue there is a button on the bottom right. Under the Skill Guide. 2026-10-28 12:21:59 -04:00
f6f50470a9 #W# Merge: Merged with upstream. Conflicts resolved. 2026-10-27 19:56:03 -04:00
cc59a8ca72 #W# Craft: Arms Lore now gives execptional armor 5 more resists. Weapons now cap at 25% DI, but Arms Lore can push it up to 35%. 1% Per 10 skill points. 2026-10-27 19:50:32 -04:00
0c1cf1b50f #W# Dashboard: Fixed the bandage speed forumula. 2026-10-27 17:39:35 -04:00
c3c9637d3d #W# Gumps: Added a Dashboard command that opens a gump and shows all your stats. 2026-10-27 15:06:18 -04:00
79dc019789 #W# Work: Just doing some simple work stuff. 2026-10-27 11:41:29 -04:00
4681994233 #W# Restructure: Moved some files around. 2026-10-26 13:34:51 -04:00
6f6042b809 #W# Removed: Got ride of stock treasurechest and dungeon trap spawners in favor of the new systems. 2026-10-26 13:19:14 -04:00
aedf8d276f #W# Treasure: DungeonBags/Crates. Bags are trapped level 0-3 loot. Crates are trapped and locked with level 4-6 loot. 2026-10-26 12:26:54 -04:00
9704edebc9 #W# Pedestals: ThiefPedestal tweak and spawns set. 2026-10-25 18:52:23 -04:00
4007f4c347 #W# Skill: Stats now give skill points to some skills. 2026-10-24 19:56:25 -04:00
9fc06bfe93 #W# Loot: Removed TreasureBag Spawns for now. 2026-10-24 17:52:38 -04:00
bc3c5143b9 #W# Rework: Complete rework of ThiefPedestals. 2026-10-24 17:45:20 -04:00
dependabot[bot]
ab59efea86
fix: Bump MailKit from 4.18.1 to 4.18.2 (#2727)
---
updated-dependencies:
- dependency-name: MailKit
  dependency-version: 4.18.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-23 20:22:24 -07:00
LoveDesignUA
302f1ad988
fix: Moving Shot only charges mana for a fired shot (#2726)
Moving Shot charged its mana in `OnBeforeSwing`, before `BaseRanged.OnSwing` decided whether to fire. A swing that fired nothing (a paralyzed or frozen archer, no ammo, a failed harmful check) still took the mana and left the move armed. Every swing after that charged again, so inside the 3-second window you paid twice, until the archer ran out of mana.

`OnBeforeSwing` now only checks the mana. Moving Shot's own `OnHit`/`OnMiss` charge it, and those only run once a shot has fired. This is how the other weapon abilities pay. A miss still costs mana. A parried shot no longer does: the parry clears the move before `OnHit` runs, the same as for every other ability.
2026-10-23 20:21:36 -07:00
a440cb72d1 #W# Loot: Messing around with a random loot generating bag. 2026-10-23 20:57:31 -04:00
6255cf8814 #W# Era: Changed from UOR back to AOS. 2026-10-23 17:19:42 -04:00
fc38c54767 #W# Added: New Skill guide button added to the crafting menu. 2026-10-23 14:38:01 -04:00
92af75f796 #W# Change: Ore now gets smelted 1 at a time instead of the whole stack. Gain chance on each attempt. 2026-10-23 13:09:04 -04:00
6cd2f766da #W# Added: New player area at the entrance of the dungeon. Added some npcs. 2026-10-22 14:23:16 -04:00
48358c80df #W# Dungeon: New player dungeon is spawned and ready to go. 2026-10-22 13:04:47 -04:00
Kamron Batman
88859ac69e
fix: T2A cooking works without a craft menu (#2693)
## Summary
With T2A packet crafting menus enabled (`t2aCraftMenus`, default for pre-UOTD eras), cooking did nothing. Double-clicking a Skillet set a target, and `T2ACraftSystem.ShowMenu` had no Cooking branch, so the target was accepted and silently dropped. Nothing else could cook either: the old RunUO "use food on a heat source" code was never hooked up and was removed in #1054.

T2A cooking never used a menu, so this restores the item-on-item / item-on-heat-source flow:

- **Per-item handling:** each ingredient (`CookableFood`, `Dough`, `SweetDough`, `SackFlour`, `BowlFlour`, `JarHoney`) implements `IT2ACombinable.TryCombine` and switches on the target. `T2ACooking` holds the shared target, water, combine and heat-timer helpers.
- **Heat source:** all `CookableFood` (raw meats, fish, eggs, unbaked pies/quiche/pizzas, cookie and cake mix), dough → bread loaf, sweet dough → muffins. One unit is used up at the start, then a 5 s timer; the Cooking check runs from `CookingLevel` to 100 (bread and muffins use `DefCooking`'s range). The food burns on failure or if the cook moves more than 3 tiles away.
- **Combinations:**
  - Open flour sack or bowl of flour + water → dough
  - Dough + pear / peach / apple / pumpkin / eggs / meat / sausage → the matching unbaked pie, quiche or pizza
  - Dough + honey → sweet dough
  - Sweet dough + honey → cookie mix
  - Flour + sweet dough → cake mix
  - The honey and flour + sweet dough recipes work in either direction.
- **Water:** water drinks (pitchers), buckets, barrels, tubs and trough add-ons, in the pack, in hand, or on the ground within 2 tiles and in line of sight.
- **Cooking tools are passive:** with T2A menus on, a tool whose system has no T2A menu (`T2ACraftSystem.HasMenu`) falls through to `ShowCraftMenu`, which does nothing under T2A. No dead target is left.

Behaviour with T2A menus off is unchanged.

## Follow-ups
- Static troughs/barrels and open water aren't water sources here or for pitcher filling (no static water ID list exists); to be fixed separately.
- Whether a bowl of flour returns an empty bowl is unconfirmed (`//TODO`).
- Cooking tools that must merely be in the pack for some recipes are not modelled yet.

## Testing
- `dotnet build`: 0 warnings, 0 errors
- New `T2ACookingTests`: flour + water, closed sack refused, dough + apple, honey in both directions, cookie mix, cake mix, wrong target consumes nothing, water in another player's pack refused
- Not covered: the heat timer (untested); not yet exercised in game
2026-10-20 23:37:28 -07:00
Kamron Batman
ef8057d38e
fix: T2A menus only offer what the chosen resource can make (#2695)
## Summary
The T2A crafting menus offered items the chosen resource couldn't make:

- **Tailoring honours the targeted cloth hue.** The menu was built from all cloth in the pack, but the craft consumes only the targeted hue, so it offered items that then failed on resources. `CanCraftItem` / `FilterEntries` / `AnyCraftableInCategory` now take the hue and count with the same helper the craft uses.
- **Uncut cloth counts in the hued path.** The hue-aware count and consume matched only `Cloth`, so targeting uncut cloth gave an empty menu. The hued path now uses `CraftItem`'s equivalence table like the unhued path. `T2ACraftSystem` reuses that table instead of a narrower copy, which also covers the coloured woods and leathers on later-era shards.
- **T2A tinkering only uses iron ingots.** Coloured ingots are refused with a re-prompt. The menu filter now uses the selected ingot, and Make Last for tinkering always resolves to iron, so a coloured choice left over from the gump flow can't be used.

## Testing
- `dotnet build`: 0 warnings, 0 errors
- New `T2AHuedFilterTests`: only cloth of the targeted hue counts, and uncut cloth of the same hue counts toward the recipe
- No test for the tinkering target and message flow; not yet exercised in game
2026-10-20 23:12:10 -07:00
dd197bbc1c #W# Fix: Traps where still being spawned fixed now. 2026-10-20 19:53:09 -04:00
11cf73428d #W# Region: Added a new Cave Region. 2026-10-20 19:35:43 -04:00
162af7b68a #W# Added: New ThiefPedestals that only require stealing/snooping, but are also trapped. 2026-10-20 19:14:41 -04:00
0148f6d1fc #W# Added: Taskmanager now has a 4 hour timer running. 2026-10-20 19:13:16 -04:00
Kamron Batman
54c0040ac8
test: run tile-dependent tests on synthetic terrain instead of client files (#2725)
## Summary

About 114 tests skipped on CI because they needed the official client files (`tiledata.mul`, map and multi files). They gave CI no signal, and locally they ran against whichever client a developer had installed. The test hosts now build their own world instead, so every test runs on CI and runs the same on every machine. CI simulation: Server.Tests 837 passed, UOContent.Tests 1131 passed, **0 skipped**.

- **Generated tile data.** The fixtures write a pre-HS `tiledata.mul` from a small invented tile kit (`Server.Tests/Terrain/TestTiles`) and load it through the real `TileData.Load()`. The test hosts no longer read any client directory (`MODERNUO_CLIENT_PATH`, `MODERNUO_TEST_DATA_DIR` and `C:\Ultima Online Classic` are gone from test code). The file is written to `SyntheticData` in each test project's output, overwritten every run and removed by `dotnet clean`.
- **Injected terrain.** `TestTerrain` stamps land and statics into Felucca and Trammel, which share one terrain. Writes after startup must go in a `TestTerrain.Scope()`, which puts back every block it touched and keeps the step cache fresh.
- **Prefab scenes.** Seven named scenes (town, causeway, sewer walkway, coastline, deep sea, map-edge sea, an upstairs room with an L-shaped counter) replace the Britain coordinates the pathing, AI and fishing tests relied on. Each scene-based test asserts its scene is present first, so a missing scene fails instead of passing on flat ground. Stamping all seven takes 3-5 ms.
- **Multi footprints.** Contrived footprints for the three multi IDs content looks up (0x74, 0x7A, 0x0) are seeded through a new internal `MultiData.SetComponents`.
- **Fingerprint.** `StepCacheFile.HashTileDataFile` is extracted so a test can show that a one-flag change to the file changes the fingerprint, while runtime table mutation does not.

### Engine changes

- `TileMatrix.GetLandBlock`/`GetStaticBlock`: on a map without its files, return the block injected with `SetLandBlock`/`SetStaticBlock`, or the default when nothing was injected. Maps with files take the same path as before.
- `MultiData.SetComponents` (internal): registers a footprint without multi files.

### Test changes worth a look

- Values that were properties of real Britain terrain (probes at Z 10, a pinned plain walk mask of 0xC1) are re-pinned to the synthetic plain (Z 0, 0xFF).
- No tests were deleted. The multi walkability and boat-deck tests assert movement behaviour over a footprint, not what a real multi looks like, so they run on the contrived footprints.
- Maps 0 and 1 keep file indexes 0 and 1, and every stamp writes both, rather than sharing one file index. On a map without files the getters never consult the shared-file cache, and its patch bits would hide one facet's injection from the other.

## Test plan

- [x] `dotnet test -p:SolutionDir=... --filter "Category!=Manual"` with the client env vars pointed at an empty directory: 837 + 1131 passed, 0 skipped, 0 failed
- [x] Mutation check: removing the step-cache clear from `TestTerrain` makes the stale-cache tests fail
- [x] CI green on all platforms
2026-10-18 22:07:40 -07:00
Kamron Batman
078be52777
fix: reject archive entries that extract outside the output directory (#2720)
## Summary
`ManagedArchive.ExtractTarFromStream` joined each tar entry name directly onto the output directory, so an entry such as `../x`, `sub/../../x` or an absolute path was written outside it. The restore path (`AutoArchive` → `ExtractTarZstd`/`ExtractTarGz`/`ExtractTar`) feeds archives through this method.

Each entry now resolves to a full path and must stay under the output root (case-insensitive on Windows). An entry that escapes fails the extraction; the existing `catch` in each `Extract*` method logs it and returns `false`.

The input is the server's own backup archives, so this is hardening against a tampered or foreign archive, not something a player can reach.

## Tests
- `ExtractTar_EntryOutsideOutputDirectory_IsRejected` (`../escaped.txt`, `sub/../../escaped.txt`) and `ExtractTar_RootedEntry_IsRejected`. All three fail without the fix.
- All 18 `ManagedArchiveTests` pass.
2026-10-18 20:06:23 -07:00
Kamron Batman
0f89870793
fix: ranged weapon crafting falls back to the recipe resource (#2719)
`BaseRanged.OnCraft` passed the raw resource type to `ApplyWoodBonuses`, which can be null; `CraftResources.GetFromType` threw on the null key and crashed the server. Every other `OnCraft` override already falls back to `craftItem.Resources[0].ItemType` — `BaseRanged` now does the same.

`GetFromType` also returns `CraftResource.None` for a null type, matching its documented contract for unregistered types, so other callers can't hit the same throw.
2026-10-18 18:10:03 -07:00
Kamron Batman
69ff5f3940
fix: champion minions belong to their spawn (#2717)
Stacked on #2716. Retarget to `main` after it merges.

## Problem
Mobiles a champion summons mid-fight aren't tracked by anything:

| Summoner | Minions |
|---|---|
| LordOaks | Silvani queen |
| `SummonPixiesCounter` (LordOaks, Silvani, Twaulo) | 3-6 pixies per trigger |
| Barracoon | 3-6 ratmen per trigger |

`ChampionSpawn.DeleteCreatures` (called on expiry and when the spawn is deleted) clears only `Creatures` and the champion. These minions survive every reset and stay in the save. I surveyed every content creature that creates other mobiles; the others already clean up after themselves: Meraktus deletes its followers, Harrower kills its tentacles, PlagueSpawn and the Enraged animals delete themselves when their owner is gone, and BogThing eats its boglings.

## Changes
- **`BaseCreature.SpawnMinion(minion, target, location)`:** sets the team, places the minion and sets its combatant. Ownership: if the summoner is a champion spawn's champion, one of its creatures, or one of its minions (found through the spawn region), the minion joins that spawn. It refuses, and deletes the minion, if the summoner isn't on a real map. `SummonPixiesCounter` read `source.Map` even though `MonsterAbility.Trigger` notes it can fire after the creature has left the world.
- **`ChampionSpawn.Minions`:** a new serialized list (v12, `MigrateFrom(V11Content)`) kept apart from `Creatures`:
  - killing a minion never counts toward a level, so a leftover summoner can't feed a restarted spawn;
  - `DeleteCreatures` deletes minions along with the creatures, which matches when the spawn is deactivated (`ExpireCreatures`, 10 minutes after `Stop`) or deleted.
- **Callers:** LordOaks, `SummonPixiesCounter` and Barracoon spawn through `SpawnMinion`. A summoner that isn't part of a spawn (a GM `[add]`) still makes standalone minions, as today.

## Testing
- `ChampionMinionTests`:
  - the champion's queen is listed in `Minions` and deleted by `DeleteCreatures`;
  - a minion's minion also belongs to the spawn;
  - a Lord Oaks in the region that isn't the spawn's champion keeps a standalone queen.
- UOContent.Tests: 1237 passed, 2 skipped. The Application build is clean.
2026-10-18 17:48:31 -07:00
Kamron Batman
4e57ec2373
fix: monster ability queries no longer have side effects (#2716)
## Problem
`GetMonsterAbilities()` answers three questions through one virtual: which abilities a creature has (`GetAbility`, `AOS.cs` reflect/barrier, bard fire-breath difficulty), which abilities are enabled right now, and where cooldown state must be cleaned up (`InvalidateNextAbilityTriggers` on death and delete). Every caller assumes it is a pure, stable list. About 70 creatures return a static array; only LordOaks computed it:

```
Mobile.Delete()
 → BaseCreature.OnDelete() → CreatureDeletedEvent
 → MonsterAbility.InvalidateNextAbilityTriggers()
 → LordOaks.GetMonsterAbilities() → CheckQueen() → new Silvani().MoveToWorld(...)
```

- Deleting a Lord Oaks nobody had fought (champion reset, `DeleteCreatures`, GM delete) spawned an orphaned Silvani where he stood.
- The spell damage scalar (`AlterDamageScalarFrom`) spawned her too.
- With the queen dead his list was empty, so cleanup skipped his cooldown entries. The static `Dictionary<BaseCreature, long>` on the ability singleton kept a reference to the deleted creature. `MonsterAbilityGroup` children's entries were never cleaned either.
- Neira threw unholy bones from `AlterDamageScalarFrom`, so any caller asking for a damage scalar (a preview gump, for example) could fire the effect.

## Changes
- **Per-creature cooldowns:** a lazy `Dictionary<MonsterAbility, long>` on `BaseCreature` (`IsAbilityOnCooldown`, `SetAbilityCooldown`, `ClearAbilityCooldowns`). The static dictionaries and the query-driven cleanup are gone, and death still resets cooldowns. Tick comparisons are wraparound-safe.
- **Pure contract:** `GetMonsterAbilities()` is documented as pure. The new `virtual bool CanUseAbility(MonsterAbility)` gates abilities that only work some of the time. Every `TriggerAbility*` loop and `GetAbility` check it.
- **LordOaks:** always lists his abilities; `CanUseAbility` and the 10% spell shield read "queen alive". The queen spawns when he is first aggressed or first given a combatant, never on the internal map. Aggression lands before the damage it causes, so the first spell is still shielded, as before.
- **Neira:** the unholy bone throw moves to `OnDamagedBySpell`.

A follow-up PR makes minions such as the queen, pixies and Barracoon's rats belong to their champion spawn, so a spawn reset cleans them up.

## Testing
- `LordOaksTests` covers three cases:
  - querying or deleting an unengaged Lord Oaks spawns nothing;
  - aggression spawns the queen exactly once, and the shield and abilities follow her life;
  - an Oaks on the internal map never spawns her.
- UOContent.Tests: 1234 passed, 2 skipped.
- A local constructor sweep (every Mobile built and deleted repeatedly) no longer reports LordOaks.
2026-10-18 17:41:27 -07:00