Hook global::Player.NewRoom(global::Room) to observe the placement paths that notify a player of a realized room. This example adds a short cosmetic shock wave at the player’s arrival position. The verified initial placement path also calls this method, so treat initial placement as an entry for this recipe.
Subscribe with the plugin
private void OnEnable()
{
On.Player.NewRoom += PlayerNewRoom;
}
private void OnDisable()
{
On.Player.NewRoom -= PlayerNewRoom;
}The generated hook delegate has the parameters On.Player.orig_NewRoom orig, Player self, and Room newRoom. Keep the destination argument and call the continuation once:
private void PlayerNewRoom(
On.Player.orig_NewRoom orig,
Player self,
Room newRoom)
{
orig(self, newRoom);
if (newRoom == null || self.room != newRoom ||
self.slatedForDeletetion || self.isNPC ||
self.bodyChunks == null || self.bodyChunks.Length == 0)
return;
newRoom.AddObject(new ShockWave(
self.mainBodyChunk.pos,
45f,
0.08f,
10));
Logger.LogInfo("Entered room " + newRoom.abstractRoom.name);
}In the inspected baseline, global::Creature.PlaceInRoom(global::Room) first calls newRoom.AddObject(this), places all body chunks around the abstract creature’s tile, clears their velocity, and then calls NewRoom(newRoom). Shortcut exit code also moves connected realized objects into the destination room and calls NewRoom when the room changed. After orig, this recipe verifies that self.room is the supplied room before reading the main chunk or adding the effect.
global::ShockWave(UnityEngine.Vector2, System.Single, System.Single, System.Int32, System.Boolean) is a temporary CosmeticSprite. The omitted Boolean uses its default value of false. global::Room.AddObject(global::UpdatableAndDeletable) gives it room ownership, update registration, and drawable registration. Its own update destroys it after its short lifetime.
Choose the right lifetime
Use this hook for behavior tied to the verified player placement calls, such as clearing a target owned by the room, starting an arrival effect, or checking the destination’s name. Each hooked player call runs its own handler. In cooperative play, several players entering the same room can therefore create several effects. Other plugins can invoke or detour NewRoom, so the hook does not guarantee one call for each entry.
For one action per room regardless of player count, store state on the Room instance or a session owner and define when that state resets. For persistent first visit logic, read a campaign flag on entry and set it when the event completes. Saving a setting or progress distinguishes those owners.
Do not use NewRoom as proof that every room system is fully prepared. The player is placed, but later room readiness work can still matter to AI, shortcuts, placed objects, and cameras. Test initial spawn, normal shortcuts, room borders, shelters, death, cooperative entry, and room unloading. The hook fragment compiled against the recorded baseline with .NET SDK 10.0.400 and C# 7.3. It was not run in the game.
Sources
global::Creature.PlaceInRoom(global::Room),global::Creature.SpitOutOfShortCut(IntVector2,global::Room,System.Boolean),global::Player.NewRoom(global::Room),global::ShockWave, andglobal::Room.AddObject(global::UpdatableAndDeletable)in the baseline assembly recorded by Runtime reference.- MonoMod RuntimeDetour hook usage