Conversation
|
Was going to test this out but it appears to be under heavy development still (I downloaded Very excited for it to be stable enough to do more stress testing with. |
| } | ||
|
|
||
| LUA_FUNCTION(place_room) { | ||
| DungeonGenerator* generator = GetDungeonGenerator(L); |
There was a problem hiding this comment.
I think it would be good for DungeonGenerator itself to contain all its functions such as PlaceRoom (ie, generator->PlaceRoom(config, row, col, seed), and the lua bindings just parse the inputs from lua and redirect to the generator's functions. This will separate the DungeonGenerator logic from the lua bindings, and allow DungeonGenerator code to call its own functions.
There was a problem hiding this comment.
Bump. Refactor this logic as DungeonGenerator::PlaceRoom, and just call that from here.
IE, make lua functions minimal (get params from lua, push return values) and do most other things within the DungeonGenerator
| #include "Log.h" | ||
| #include "LuaDungeonGenerator.h" | ||
|
|
||
| DungeonGenerator* GetDungeonGenerator(lua_State* L) { |
There was a problem hiding this comment.
LUALIB_API DungeonGenerator* GetDungeonGenerator(lua_State* L)
I don't know exactly how important the LUALIB_API bit is, but its typically used for such functions. Might impact properly surfacing lua errors.
|
|
||
| std::vector<RoomCoords> forbidden_coords = GetForbiddenNeighbors(base_coords, room_shape, doors); | ||
|
|
||
| for (int i = 0; i < this->num_rooms; i++) { |
There was a problem hiding this comment.
I would suggest having the generator hold a map that ties gridIdx to roomIdx, in order to quickly find the neighboring rooms, and see if a room already occupies this room's slot, as going through the whole room list is not really efficient.
| LUA_FUNCTION(Lua_PlaceDefaultStartingRoom) { | ||
| DungeonGenerator* generator = GetDungeonGenerator(L); | ||
|
|
||
| int doors = (int)luaL_optinteger(L, 2, 15); |
There was a problem hiding this comment.
Since the start room has essentially a predefined config, and GetRandomRoom can only ever pick one room, due to the variant range being set to just the value 2, there is no need to pass the doors parameter.
There was a problem hiding this comment.
The doors parameter limits the allowed doors for the room. It could be useful to mimick the behaviour of the mega satan door/polaroid door
There was a problem hiding this comment.
It would be better to use a system similar to the blocked grid indices, rather than having the user having to deal with doors directly. In the original level gen the doors parameter is meant as a way to specify which doors are necessary, not those that are allowed. Of course, we should still allow control over the doors parameter, when doing very specific things, like how secret rooms and ultra secret room generation has to mark certain doors as allowed, post layout generation.
There was a problem hiding this comment.
I was basing the doors parameter on the LevelGeneratorEntry.doors parameter, since I'm later setting the LevelGeneratorEntry doors field to that. There it does specify the doors argument refers to the allowed doors.
https://repentogon.com/LevelGeneratorEntry.html
There was a problem hiding this comment.
LevelGeneratorEntry is a wrapper for a LevelGenerator_Room and LevelGenerator_Room only specifies the necessary rooms, not the allowed ones. Most likely the parameter was named like that because the necessary rooms end up being used as the allowed rooms when placing the room.
There was a problem hiding this comment.
Bump. IDK if I have input here but need to decide how we want to handle non-standard door layouts for the starting room.
Maybe DM guantol.
| this->shape = room->Shape; | ||
| } | ||
|
|
||
| RoomConfig_Room* DungeonGeneratorRoom::GetRoomConfig(uint32_t seed, int required_doors) { |
There was a problem hiding this comment.
I presume this function was originally meant to do something else due to the unused parameters and unnecessary check for nullptr, what was this supposed to do originally?
There was a problem hiding this comment.
bump.
Remove unused parameter, and simply return this->room;
| this->shape = room->Shape; | ||
| } | ||
|
|
||
| RoomConfig_Room* DungeonGeneratorRoom::GetRoomConfig(uint32_t seed, int required_doors) { |
There was a problem hiding this comment.
bump.
Remove unused parameter, and simply return this->room;
| #include "Log.h" | ||
| #include "LuaDungeonGenerator.h" | ||
|
|
||
| DungeonGenerator* GetDungeonGenerator(lua_State* L) { |
| } | ||
|
|
||
| LUA_FUNCTION(place_room) { | ||
| DungeonGenerator* generator = GetDungeonGenerator(L); |
There was a problem hiding this comment.
Bump. Refactor this logic as DungeonGenerator::PlaceRoom, and just call that from here.
IE, make lua functions minimal (get params from lua, push return values) and do most other things within the DungeonGenerator
|
|
||
| return 1; | ||
| } else { | ||
| return 0; |
There was a problem hiding this comment.
I'd prefer this
...
luaL_setmetatable(L, lua::metatables::DungeonGeneratorRoomMT);
} else {
lua_pushnil(L);
}
return 1;| for callback in GetCallbackIterator(callbackID, param) do | ||
| local ret = RunCallbackInternal(callbackID, callback, dungeonGenerator, rng, dungeonType) | ||
|
|
||
| if type(ret) == "boolean" and ret then |
There was a problem hiding this comment.
I think this is very little room for any semblance of mod compatibility for this callback. I believe we've discussed this a bit before.
MC_PRE_GENERATE_DUNGEON is simply not suitable for modifying layouts, only creating them. The use-case being served here is completely custom layouts, and that is not something that mods can collaborate on without explicit compatibility.
With that in mind, I wonder if instead of having a return value at all, we could just do this:
RunCallbackInternal(callbackID, callback, dungeonGenerator, rng, dungeonType)
if dungeonGenerator:Validate() then
return true
else
dungeonGenerator:Reset()
endIf there was a version of this that was intended to support modifications, it would either be MC_POST_GENERATE_DUNGEON, or a more fleshed out MC_POST_LEVEL_LAYOUT_GENERATED. But that is a weird case. Tacking additional rooms onto the floor is easy enough to do now post-levelgen, so this would come down to practical use-cases for modifying layouts that cannot simply be done later. Stuff to think about/discuss separately, perhaps.
| } | ||
|
|
||
| g_Game->_lastBossRoomListIdx = this->final_boss_index; | ||
|
|
There was a problem hiding this comment.
The DungeonGenerator should support off-grid rooms.
Most likely by adding DungeonGeneratorRoom offGridRooms[21]; (just flip the index)
It should maybe be possible for lua to place RoomConfig_Rooms into these slots (but only by placing RoomConfig_Rooms).
Now, if they were not populated by the mod, we must initialize the following off-grid rooms for (mostly) ALL dungeons:
- Error room (-2)
- Crawlspace dungeon (-4)
- Black market (-6)
- Members card secret shop (-13) (except home & ascent)
- Stairway to heaven angel shop (-18) (except home)
- The Rep+ lil portal room (-20)
- The Beast's room in Home?? (-10)
For generate_dungeon specifically we need these as well, regardless of floor with some exceptions (yes, the game always populates boss rush / mega satan / etc):
- Boss Rush (-5)
- Mega Satan (-7)
- Blue Womb trapdoor (-8) (for all stages except blue womb)
- Void trapdoor (-9) (for blue womb only)
- The "secret exit" room SPECIFICALLY for returning to the main path from Repentance alt path floors (-10)
These will (mostly) boil down to GetRandomRoom calls, but please refer to the 1.7.9b logic (easier to read, tho doesnt help for lil portal) to make sure we do this correctly. Ask for help in the discord thread if needed.
| { | ||
| bool skip = ProcessGenerateDungeonCallback(this, *rng, DEFAULT); | ||
| if (skip) { | ||
| return; |
There was a problem hiding this comment.
I think we need to call generate_mirror_world here, for the appropriate stages. In theory it should just do what it normally does and flips the main layout... Guess we'll find out if it works.
| { | ||
| bool skip = ProcessGenerateDungeonCallback(this, *rng, DEFAULT); | ||
| if (skip) { | ||
| return; |
There was a problem hiding this comment.
We'll need to confirm if there's anything else we need to do here since we're skipping the vanilla code, like anything the game changes or sets that isnt directly tied to the LevelGenerator portion.
I'll need to defer to Guantol for that, though.
Added a new callback and a couple functions that allow for mods to generate custom floors.
MC_PRE_GENERATE_DUNGEON
Called before
level::generate_dungeonis called, which places all the rooms in a floor. Passes an RNG.Return true to cancel vanilla floor generation.
Level:ResetRoomList(bool)
Needs to be called before placing any rooms in the custom level.
The bool is the same as the one in
level::init. Testing it so far, setting it to true makes it work all the time.Level:SetLastBossRoomListIndex(int)
Also needs to be called after creating the custom floor, since not setting it crashes the game when continuing.
Doesn't actually need to be set to a boss room for the game to not crash, but I haven't tested any other side effects apart from boss rooms not spawning the trapdoor if they are not the last.
Sample
