Skip to content

Add the Imperial Guardian event (Gaion's event) - #992

Merged
sven-n merged 12 commits into
MUnique:masterfrom
apraxico:feature/imperial-guardian-event
Sep 29, 2026
Merged

sven-n merged 12 commits into
MUnique:masterfrom
apraxico:feature/imperial-guardian-event

Conversation

@apraxico

Copy link
Copy Markdown
Contributor

Closes #892

Summary

This adds the Imperial Guardian event (also known as Gaion's event or the Secromicon quest chain) as a new mini game. It covers the whole chain which is described in #892:

  • collect Suspicious Scraps of Paper;
  • five of them become a Gaion's Order;
  • fight in the fortress from Monday to Saturday;
  • collect the six Secromicon Fragments;
  • combine them into a Complete Secromicon;
  • enter the Sunday fortress with it.

Like the other events, it's a clean-room implementation based on the behaviour of the original game. No server code was copied.

How the points of #892 are covered

Thanks to @didiconcs for the gap analysis, it was a good starting point. Point by point:

  1. Scraps → Gaion's Order. The original game has no NPC turn-in for this. The scraps are stacked in the inventory, and a full stack of five turns into a Gaion's Order. SuspiciousScrapOfPaperStackedPlugIn does this, like SignOfDimensionsStackedPlugIn for the Doppelganger event. The scraps had no drop item group yet, so the update adds one: 0.1 % from monsters of level 32 and above.

  2. Day-of-week gating. There is one mini game definition per day, from Monday (1) to Sunday (7). Each day has its own map and entrance: Monday/Thursday on fortress 1, Tuesday/Friday on fortress 2, Wednesday/Saturday on fortress 3, Sunday on fortress 4. Each day also spawns its own monsters and boss. The spawns carry a wave number of day * 10 + zone. For testing, FixedDay in the configuration forces a day.

  3. Secromicon Fragments. The side bosses of Monday to Saturday drop the fragments:

    • 508 → first, 509 → second, 510 → third, 511 → fourth, 507 → fifth, 506 → sixth;
    • one, two or three pieces, with chances of 50/29/21 %.

    This is configurable in FragmentDrops and FragmentCountChances. The existing Chaos Goblin mix (1,000,000 zen, 100 %) turns them into the Complete Secromicon. That mix is what the original servers use too, so it stays as it is.

  4. Sunday dungeon. Fortress 4 is only used on Sunday, and only with a Complete Secromicon.

The entry NPC is Jerint (522), which now opens its event window (NpcWindow.JerintGaionEvententry). Lugard was already wired up for the Doppelganger event in #973.

Behaviour

  • Entrance:
    • Talk to Jerint in Devias. The client sends F7 01, and the server answers with F7 02.
    • Every player needs their own ticket: Gaion's Order from Monday to Saturday, Complete Secromicon on Sunday. The ticket is consumed only when the player actually enters.
    • The minimum level is 150.
    • Players can only enter as members of a party, like in the original game. This is configurable (IsPartyRequired), and each party has at most 5 players.
    • Each party gets its own map instance (MiniGameMapCreationPolicy.OnePerParty).
  • Zones:
    • Each fortress has several zones, separated by gates.
    • Each zone starts with a standby phase (StandbyDuration, 60 s) and is then played for ZoneDuration (10 min).
    • The monsters of a zone are spawned when the zone starts.
    • When a zone is cleared, the next one starts right away (StartNextZoneWhenCleared).
    • The client timer is kept up to date with F7 04.
  • Gates and traps:
    • The gates (524–528) block the way until they are destroyed. Some of them can be attacked right away, others only after the zone's monsters are killed.
    • The terrain of the game client already blocks the gates with the water attribute (0x10), like the original server does. So when a gate is destroyed, the server removes that attribute over the gate's area with packet 0x46.
    • The traps (523) are invisible to the players. The client has no model for them and would show a default model otherwise, so NonPlayerCharacter.IsInvisible was added and ObserverToWorldViewAdapter filters them out.
  • Monster skills:
    • The monsters use the skills of the original game with their animations (0x69). These hit the target or an area and can stun.
    • The skill table is configurable (MonsterSkills, SkillChance).
    • A rage of the bosses (RageHealthPercentage) is available, but disabled by default.
  • End:
    • Success or failure is shown with F7 06.
    • The experience reward depends on the day, and is doubled on Sunday (ExperienceRewards, SundayExperienceMultiplier).
    • The event fails when a zone isn't cleared in time or no player is left on the map.
  • Admin: everything is configurable in the admin panel through ImperialGuardianFeaturePlugIn. The drops of the bosses are left to the admins; only the fragments and scraps are included.

Other changes

  • Monster.TeleportAsync, used by the monster skills.
  • AttackableExtensions.ApplyStunEffectAsync(Player, TimeSpan).
  • NonPlayerCharacter.IsInvisible, described above.
  • Packets F7 01, F7 02, F7 04, F7 06 with docs. The client structs aren't packed, so the sizes include the padding.
  • Map data:
    • The spawns of the fortress maps are now defined in one table (ImperialGuardianSpawns), with their day, zone and direction. Before, those were only known from comments.
    • The directions were the ones of the game client; OpenMU uses them plus one.
    • The safezone of the fortresses is Devias.

Update plugin

AddImperialGuardianDataUpdatePlugIn (version 119, not mandatory) does the following on existing databases:

  • adds the scrap drop group;
  • changes the fortress spawns to wave spawns and fixes their directions;
  • adds the seven mini game definitions;
  • configures Jerint.

It checks for existing data by id, so applying it twice changes nothing. A test covers this.

Tests

  • ImperialGuardianEventDefinitionTest
  • ImperialGuardianRemoteViewTests
  • the packet tests
  • SuspiciousScrapOfPaperStackedPlugInTest
  • ImperialGuardianDataTest: a new database and the update applied twice.

Built with -p:ci=true; all four test suites pass.

Tested in game with the Season 6 client (MuMain), all seven days with FixedDay:

  • entrance with both tickets;
  • all zones, gates and traps;
  • boss skills;
  • fragment drops;
  • the Secromicon mix;
  • the Sunday fortress.

🤖 Generated with Claude Code

apraxico and others added 10 commits September 27, 2026 19:47
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…event

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The monster spawns of the fortress maps are spawned by the event, zone by
zone. Their zone and day, which were only known from comments, are defined
in one table, which is used by the map initializers and the data update.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ts and the data

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…sses get into rage

The monsters use the skills of the original game with their animations,
which hit the target or an area and may stun. The rage of the bosses
is configurable and disabled by default.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… client

The directions of the spawns were the ones of the game client, but OpenMU
uses them plus one, so the gates were rotated. The terrain of the client
already blocks the gates with the water attribute, like the original
server does, so this attribute has to be removed to open a gate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… skipped wait

The game client doesn't know the trap monster and shows a default model
for it, so the traps are invisible like in the original game. The timer
of the client follows the countdown, when the entering phase was skipped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The timer is set by the zones and read by the timer loop, which run
in different tasks. Its type and end are now replaced together and read
with Volatile, like the zone, so they always match.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…doesn't know

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…l game

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

sven-n commented Sep 29, 2026

Copy link
Copy Markdown
Member

Thanks, this is a thorough implementation. The zone loop, gate/terrain handling, ticket consumption only after a successful enter, and the idempotent update plugin all look good.

Blocking: merge conflict in UpdateVersion.cs
master already uses 119 (RefreshKanturuData) and 120 (AddSelupanFallSkill). AddImperialGuardianData = 119 collides with them. Please merge master and renumber it to 121. Also check that the update plugin's CreatedAt sorts after the newer updates.

Minor (non-blocking):

  • MiniGameManager.cs: the diff adds a UTF-8 BOM to the first line. Please revert that so the file isn't touched unnecessarily.
  • AddImperialGuardianDataUpdatePlugIn: the scrap drop group is added to every map, including the fortress maps themselves and event maps like Blood Castle/Devil Square. Is that intended? The initializer path should behave the same way.
  • ImperialGuardianContext.OnMonsterDied: DropFragmentsAsync runs for every monster death. It's filtered by FragmentDrops so it's correct, just a small allocation per kill. Fine as is.

I'll merge once the conflict is resolved. The Azure Pipelines failure is a known credits issue, not caused by this PR.


Generated by Claude Code

apraxico and others added 2 commits September 29, 2026 17:38
…ardian-event

# Conflicts:
#	src/GameServer/RemoteView/MiniGames/Extensions.cs
#	src/GameServer/RemoteView/MiniGames/ShowMiniGameEnterResultViewPlugIn.cs
#	src/Persistence/Initialization/Updates/UpdateVersion.cs
- Sort the update after the ones of master by its creation date.
- Don't add a byte order mark to MiniGameManager.cs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@apraxico

apraxico commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor Author

Thanks for the review!

Blocking: merge conflict in UpdateVersion.cs […] Please merge master and renumber it to 121. Also check that the update plugin's CreatedAt sorts after the newer updates.

Done. I merged master (e867640); the conflicts were in UpdateVersion.cs and next to the Kanturu enter result in the mini game view. AddImperialGuardianData is 121 now. Its CreatedAt is 2026-09-28, which sorts after RefreshKanturuData (09-25) and AddSelupanFallSkill (09-27) (3354ea9). (A follow-up commit set it to the actual date of the renumbering, 09-29, but it came after the merge; 09-28 sorts correctly, too.) All four test suites pass, and the PR is mergeable again.

The Crywolf PR #1005 also used 121; I'll move it to 122 there.

MiniGameManager.cs: the diff adds a UTF-8 BOM to the first line. Please revert that so the file isn't touched unnecessarily.

Reverted (3354ea9). Now the diff only contains the new ImperialGuardian case. I also checked the other modified files: none of them has a changed BOM.

AddImperialGuardianDataUpdatePlugIn: the scrap drop group is added to every map, including the fortress maps themselves and event maps like Blood Castle/Devil Square. Is that intended? The initializer path should behave the same way.

Yes, it's intended, and both paths behave the same:

  • The original drops the Suspicious Scrap of Paper from every monster of level 32 or higher (SuspiciousScrapOfPaperMonsterMinLevelDrop = 32 in the IGCN configuration), without a map restriction that I could find. The drop group has the same minimum monster level.
  • It's the same approach as the Sign of Dimensions of the Doppelganger (Add the Doppelganger (Double Goer) event #973), whose update also adds its drop group to every map.
  • In the initializer, EventTicketItems registers the group with BaseMapInitializer.RegisterDefaultDropItemGroup before GameMapsInitializer creates the maps. So a new database also has it on every map.

If you'd rather exclude the event maps, e.g. by BattleZone or the mini game maps, I can add that to both paths.

ImperialGuardianContext.OnMonsterDied: DropFragmentsAsync runs for every monster death. […] Fine as is.

Agreed, I left it as it is.

🤖 Generated with Claude Code

sven-n commented Sep 29, 2026

Copy link
Copy Markdown
Member

Re-reviewed at 3354ea9. The merge with master is clean. AddImperialGuardianData is now 121 with CreatedAt 2026-09-28, the BOM change is reverted, and the explanation for the drop group on every map is consistent with Doppelganger (#973). LGTM, merging. The Azure Pipelines failure is the known credits issue.


Generated by Claude Code

@sven-n
sven-n merged commit ff14409 into MUnique:master Sep 29, 2026
0 of 2 checks passed
apraxico added a commit to apraxico/OpenMU that referenced this pull request Sep 29, 2026
The imperial guardian event (MUnique#992) takes 121. CreatedAt is the actual
date of the renumbering, like CODING_RULES.md asks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Gaion's Event (Secromicon quest chain) - gap analysis: what exists, what's missing, and a head start on the entry NPCs

2 participants