EtG Modding Guide
Search…
⌃K

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.)