Making the flow

most floors have anywhere from 2-5 different flows and each flow has around 17-21 nodes (aka 17-21 rooms).

for each of the flows, you plan on making, create a new file named f1b_floorname_flow_01 that inherits from FloorNameDungeonFlows with the number increasing each time.

A good idea when making the flow is to plan out how many rooms you want then drawing out your flow, whether or not it should be a one-way loop, if it's a hub or combat, chest, shop, boss, etc. That way you have a clear look at how the floor will usually be set up and which rooms connect to which.

here is an example you can drop in your class

public static DungeonFlow F1b_FloorName_flow_01()
		{
			try
			{

				DungeonFlow m_CachedFlow = ScriptableObject.CreateInstance<DungeonFlow>();

				DungeonFlowNode entranceNode = GenerateDefaultNode(m_CachedFlow, PrototypeDungeonRoom.RoomCategory.ENTRANCE, ModRoomPrefabs.Mod_Entrance_Room);
				DungeonFlowNode exitNode = GenerateDefaultNode(m_CachedFlow, PrototypeDungeonRoom.RoomCategory.EXIT, ModRoomPrefabs.Mod_Exit_Room);
				DungeonFlowNode bossfoyerNode = GenerateDefaultNode(m_CachedFlow, PrototypeDungeonRoom.RoomCategory.SPECIAL, overrideTable: ModPrefabs.boss_foyertable);
				DungeonFlowNode bossNode = GenerateDefaultNode(m_CachedFlow, PrototypeDungeonRoom.RoomCategory.BOSS, ModRoomPrefabs.Mod_Boss);

				DungeonFlowNode FloorNameShopNode = GenerateDefaultNode(m_CachedFlow, ModPrefabs.shop02.category, overrideTable: ModPrefabs.shop_room_table);
				DungeonFlowNode FloorNameRewardNode_01 = GenerateDefaultNode(m_CachedFlow, PrototypeDungeonRoom.RoomCategory.CONNECTOR, ModPrefabs.gungeon_rewardroom_1);
				DungeonFlowNode FloorNameRewardNode_02 = GenerateDefaultNode(m_CachedFlow, PrototypeDungeonRoom.RoomCategory.CONNECTOR, ModPrefabs.gungeon_rewardroom_1);


				DungeonFlowNode FloorNameRoomNode_01 = GenerateDefaultNode(m_CachedFlow, PrototypeDungeonRoom.RoomCategory.HUB, oneWayLoopTarget: true);
				DungeonFlowNode FloorNameRoomNode_02 = GenerateDefaultNode(m_CachedFlow, PrototypeDungeonRoom.RoomCategory.NORMAL);
				DungeonFlowNode FloorNameRoomNode_04 = GenerateDefaultNode(m_CachedFlow, PrototypeDungeonRoom.RoomCategory.NORMAL);
				DungeonFlowNode FloorNameRoomNode_05 = GenerateDefaultNode(m_CachedFlow, PrototypeDungeonRoom.RoomCategory.NORMAL);
				DungeonFlowNode FloorNameRoomNode_06 = GenerateDefaultNode(m_CachedFlow, PrototypeDungeonRoom.RoomCategory.HUB, oneWayLoopTarget: true);
				DungeonFlowNode FloorNameRoomNode_07 = GenerateDefaultNode(m_CachedFlow, PrototypeDungeonRoom.RoomCategory.NORMAL);
				DungeonFlowNode FloorNameRoomNode_09 = GenerateDefaultNode(m_CachedFlow, PrototypeDungeonRoom.RoomCategory.NORMAL);
				DungeonFlowNode FloorNameRoomNode_10 = GenerateDefaultNode(m_CachedFlow, PrototypeDungeonRoom.RoomCategory.NORMAL);
				DungeonFlowNode FloorNameRoomNode_11 = GenerateDefaultNode(m_CachedFlow, PrototypeDungeonRoom.RoomCategory.HUB);
				DungeonFlowNode FloorNameRoomNode_12 = GenerateDefaultNode(m_CachedFlow, PrototypeDungeonRoom.RoomCategory.NORMAL);
				DungeonFlowNode FloorNameRoomNode_13 = GenerateDefaultNode(m_CachedFlow, PrototypeDungeonRoom.RoomCategory.NORMAL);
				DungeonFlowNode FloorNameRoomNode_14 = GenerateDefaultNode(m_CachedFlow, PrototypeDungeonRoom.RoomCategory.NORMAL);
				DungeonFlowNode FloorNameRoomNode_16 = GenerateDefaultNode(m_CachedFlow, PrototypeDungeonRoom.RoomCategory.NORMAL);
				DungeonFlowNode FloorNameRoomNode_17 = GenerateDefaultNode(m_CachedFlow, PrototypeDungeonRoom.RoomCategory.NORMAL);
				DungeonFlowNode FloorNameRoomNode_18 = GenerateDefaultNode(m_CachedFlow, PrototypeDungeonRoom.RoomCategory.NORMAL);

				m_CachedFlow.name = "F1b_FloorName_Flow_01";
				m_CachedFlow.fallbackRoomTable = ModPrefabs.FloorNameRoomTable;
				m_CachedFlow.phantomRoomTable = null;
				m_CachedFlow.subtypeRestrictions = new List<DungeonFlowSubtypeRestriction>(0);
				m_CachedFlow.flowInjectionData = new List<ProceduralFlowModifierData>(0);
				m_CachedFlow.sharedInjectionData = new List<SharedInjectionData>() { BaseSharedInjectionData };

				m_CachedFlow.Initialize();

				m_CachedFlow.AddNodeToFlow(entranceNode, null);
				// First Looping branch
				m_CachedFlow.AddNodeToFlow(FloorNameRoomNode_16, entranceNode);
				m_CachedFlow.AddNodeToFlow(FloorNameRoomNode_01, FloorNameRoomNode_16);
				// Dead End
				m_CachedFlow.AddNodeToFlow(FloorNameRoomNode_05, FloorNameRoomNode_01);
				// Start of Loop
				m_CachedFlow.AddNodeToFlow(FloorNameRoomNode_02, FloorNameRoomNode_01);
				m_CachedFlow.AddNodeToFlow(FloorNameRoomNode_04, FloorNameRoomNode_02);
				m_CachedFlow.AddNodeToFlow(FloorNameRewardNode_01, FloorNameRoomNode_04);
				// Connect End of Loop to first in chain
				m_CachedFlow.LoopConnectNodes(FloorNameRewardNode_01, FloorNameRoomNode_01);

				// Second Looping branch
				m_CachedFlow.AddNodeToFlow(FloorNameRoomNode_17, entranceNode);
				m_CachedFlow.AddNodeToFlow(FloorNameRoomNode_06, FloorNameRoomNode_17);
				// Dead End
				m_CachedFlow.AddNodeToFlow(FloorNameRoomNode_10, FloorNameRoomNode_06);
				// Start of Loop
				m_CachedFlow.AddNodeToFlow(FloorNameRoomNode_07, FloorNameRoomNode_06);
				m_CachedFlow.AddNodeToFlow(FloorNameRoomNode_09, FloorNameRoomNode_07);
				m_CachedFlow.AddNodeToFlow(FloorNameRewardNode_02, FloorNameRoomNode_09);
				// Connect End of Loop to first in chain
				m_CachedFlow.LoopConnectNodes(FloorNameRewardNode_02, FloorNameRoomNode_06);

				// Splitting path to Shop or Boss
				m_CachedFlow.AddNodeToFlow(FloorNameRoomNode_18, entranceNode);
				m_CachedFlow.AddNodeToFlow(FloorNameRoomNode_11, FloorNameRoomNode_18);
				// Path To Boss
				m_CachedFlow.AddNodeToFlow(FloorNameRoomNode_12, FloorNameRoomNode_11);
				// Path to Shop
				m_CachedFlow.AddNodeToFlow(FloorNameRoomNode_13, FloorNameRoomNode_11);
				m_CachedFlow.AddNodeToFlow(FloorNameShopNode, FloorNameRoomNode_13);
				// Dead End
				m_CachedFlow.AddNodeToFlow(FloorNameRoomNode_14, FloorNameRoomNode_11);


				m_CachedFlow.AddNodeToFlow(bossfoyerNode, FloorNameRoomNode_12);
				m_CachedFlow.AddNodeToFlow(bossNode, bossfoyerNode);
				m_CachedFlow.AddNodeToFlow(exitNode, bossNode);

				m_CachedFlow.FirstNode = entranceNode;

				return m_CachedFlow;
			}
			catch (Exception e)
			{
				ETGModConsole.Log(e.ToString());
				return null;
			}
		}

here is a diagram of what this would look like

Now we head back to FloorNameDungeon and we scroll down until we see this:

simply drop all the flows you have made into that list

now we go to FloorNameDungeonFlows and scroll down till you see the comment saying:

we will add our custom flow here soon.

simply add your flows to KnownFlows there

We can now test our floor.

In our module we will init everything in this order:

				ModPrefabs.InitCustomPrefabs();
				ModRoomPrefabs.InitCustomRooms();
				FloorNameDungeonFlows.InitDungeonFlows();
				FloorNameDungeon.InitCustomDungeon();
				Hook hook = new Hook(
				   typeof(GameManager).GetMethod("Awake", BindingFlags.NonPublic | BindingFlags.Instance),
				   typeof(Module).GetMethod("GameManager_Awake", BindingFlags.NonPublic | BindingFlags.Instance),
				   typeof(GameManager)
			   );

and add this method:

private void GameManager_Awake(Action<GameManager> orig, GameManager self)
		{
			orig(self);
			FloorNameDungeon.InitCustomDungeon();
		}

we now need a command to load our floor. Drop this in your modules init:

ETGModConsole.Commands.AddGroup("floor", args =>
                {
                });
                ETGModConsole.Commands.GetGroup("floor").AddUnit("load", this.LoadFloor);

and make this method

private void LoadFloor(string[] obj)
		{
			GameManager.Instance.LoadCustomLevel(FloorNameDungeon.FloorNameDefinition.dungeonSceneName);
		}

now test!

(if it is starting you on something other than your entrance, make sure in your entrance the room category is entrance, set via code, not rat.)

Last updated