The switches used to be held by simply standing near them, and the crown captured
by standing near it - clicking a switch only produced the client's "not implemented
yet" message. This drives both from the original interaction instead:
- Clicking a Crown Switch starts an operation which completes after
CastleSiegeSettings.SwitchPushSeconds (15) and keeps the switch for the guild
until its operator leaves the switch's area. One player per switch; anybody else
clicking it is told another team is on it (C1 B2 14 state 2).
- While one guild holds both switches the crown's shield drops for it, and its
guild master captures the throne by CLICKING the crown and holding it for
CrownHoldTimeSeconds - seeded to 60 now, to match the countdown the client's
registration panel hardcodes. The throne stays contestable until the siege ends.
- The shield now depends on the switches alone, as in the original; the gates and
statues remain what they always were, the obstacle in the way.
The switch info packet (C1 B2 20) is broadcast before any switch-state packet
because the client's "switch released" handler reads its switch table without
checking that it exists - that table is only allocated when the info packet
arrives, so the wrong order crashes the client.
Also fixed while in here:
- The crown registration panel could never be closed: the cancel was only sent
while the master still stood on the crown, which is precisely when the hold does
NOT break. The panel is now closed for the player it was opened for.
- A contested switch was decided by enumeration order.
- Panels opened by the siege are closed when it ends.
- TryCaptureThrone was dead code carrying a second, diverged rule set.
- /csphase advertised the pre-refactor phase names to the client.
- The periodic broadcasts keyed off "UtcNow.Second % n", which silently skips when
a tick runs late; they count ticks now.
The unit tests never compiled against the refactored model - they are migrated to
the state machine and guild ids, and cover the new switch and crown rules. A new
test proves update 105 writes the configuration into an existing database.
ApplyPendingUpdatesTool applies pending configuration updates without the admin
panel; it is [Explicit], so it never runs in a normal test pass.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Brings in the database layer of upstream OpenMU PRs #754 and #860 without
touching AdaMu's working Castle Siege gameplay. This is purely additive: the
existing 5-phase implementation still runs exactly as before.
What is included:
- DataModel: CastleSiegeState (the original Season 6 values 0-9, which are
exactly what the game client's CASTLESIEGE_STATE enum expects),
CastleSiegeJoinSide, and the zone/NPC/upgrade definition types.
- Entities: CastleSiegeData, CastleSiegeGuildRegistration, CastleSiegeNpcState.
These identify a guild by its persistent Guid rather than by name.
- Generated persistence: 8 BasicModel + 8 EntityFramework model classes,
CastleSiegeExtensions, and the regenerated ExtendedTypeContext,
MapsterConfigurator and GameConfiguration partials.
- Migrations: 20260730194321_AddCastleSiege and
20260801162427_ConfigureCastleSiegePersistence, plus the model snapshot.
- EntityDataContext gains the two DbSets and the five model registrations.
- EntityFrameworkContextBase only publishes configuration changes for entities
in the configuration schema, so siege state writes are no longer broadcast as
configuration changes.
AdaMu-specific adaptations:
- UpdateVersion.AddCastleSiegeData is 105, not upstream's 100. AdaMu already
ships 95-104, and the applied-update bookkeeping is keyed on this value, so a
collision would skip or re-run updates on live databases.
- CastleSiegeInitializer does not seed a weekly StateSchedule. Upstream drives
the cycle from a fixed Saturday schedule; AdaMu drives it manually from
CastleSiegeEventPlugIn and the AdminPanel, so the schedule is left empty and
nothing reads it.
The seeded NPC definitions match AdaMu's existing hard-coded coordinates
exactly (6 gates, the two crown switches and the crown), and additionally
provide 4 guardian statues, 6 guardsmen and real gate/statue hit point tables
that the current implementation does not have yet.
Two pre-existing migrations were restyled by upstream (copyright header, using
placement, trailing comma). No functional change.
Verified: full server build succeeds with 0 errors.
Databases whose imported season 6 maps (Arkania 82, Acheron 83, Debenter 84,
Uruk 85, Ferea 86) were created by a fresh initialization end up with the map
rows and exit gates but without the /move WarpInfo entries, so /move answers
"Unknown warp index" for those maps.
AddArkaniaMapUpdateSeason6 and AddImportedMapsUpdateSeason6 return early when
the map already exists, which also skips the spawn gate, the WarpInfo and the
game-server assignment that follow. Update 104 repairs each of those pieces
independently and is safe to run repeatedly.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
AddHeykelSavasiEventUpdateSeason6 (version 100): adds map 92 + terrain,
statue/guard monsters, red/blue base gates, event NPC 560 in Lorencia, and
the mini-game definition to an EXISTING config via the admin Updates page
(no DB wipe). Reuses HeykelSavasiMap/HeykelSavasiInitializer; idempotent.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Embed Terrain93.att (server map No 92 -> loads Terrain93); map Number 90->92.
- Real walkable coords from the LoL arena diamond (center lane x=42):
Red base (42,10) top tip, Blue base (42,92) bottom tip; 7 statues per team along x=42.
- Client World93 + Object93 deployed to PC client separately.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds HeykelSavasiMap (BaseMapInitializer, Number=90/World91) as an empty
terrain-only skeleton and registers it in GameMapsInitializer. Statue/guard
monster definitions and spawns are added by later Heykel Savasi tasks.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Six high-tier imported wings, one per class line: Wizard (242, Soul Master),
Magic (243, Duel Master), Elf (244, High Elf), Summoner (245, Dimension Master),
Warrior Cape (246, Lord Emperor + Fist Master) and Knight (247, Blade Master).
Each: base defense + defense-per-level, 3rd Wing Options, damage increase and
fast flight. Applied via update v99 (AddWarriorWingsUpdateSeason6), reusing the
shared "3rd Wing Options" option set to avoid Guid collisions.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Switch from the basic CreateWing overload to the full one: base
defense 65/60, defense-per-level, wing options (HP recover / phys
damage / defense), damage increase, fast flight. Level req 100.
Same pattern as Arkania: terrain resources Terrain84-87.att, map
initializer classes, registration, and update-plugin (v97) that
creates each map + spawn gate + /move warp AND assigns it to the
game server configuration so it is hosted. Also fix the Arkania
update-plugin to assign the game server config (was missing).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Client tried to load World84 for map 83; MU client world index is
server map number + 1 (Lorencia 0 -> World1). Arkania client assets
are World83, so the server map number is 82 and the terrain resource
is Terrain83.att (number+1).
Two new weapons placed at free item slots so they never collide with
standard Season 6 items. The client name DB (Item_eng.bmd) is the source
of tooltip name/stats, so only free slots are usable for custom items.
Both seed paths are covered:
- Fresh DB: Weapons.Initialize() calls the new CreateImperialWeapons050().
- Existing DB: AddImperialWeapons050UpdateSeason6 (UpdateVersion 95),
applied from the AdminPanel Updates page, with an idempotency guard.
Imperial Sword is a Magic Gladiator weapon (copy of 0/28), Imperial Staff
is a Wizard weapon (copy of 5/31).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>