epfly6 / RepentanceAPIIssueTracker

An unofficial issue tracker for issues with The Binding of Isaac: Repentance's API.
18 stars 1 forks source link

Custom out-of-map rooms keep pickups inside them between levels and even runs #518

Open Cuerzor opened 2 years ago

Cuerzor commented 2 years ago

I want to make additional rooms that out-of-map like devil room or crawlspaces, so I coded like this:

function GenerateAdditionalRooms()
    local game = Game();
    local level = game:GetLevel();
    local currentRoomIndex = level:GetCurrentRoomIndex();
    local seed = game:GetSeeds():GetStageSeed(level:GetStage());
    local rng = RNG();

    for index, roomInfo in pairs(AdditionalRooms) do
        --roomInfo is like {Type = type, Variant = variant, Dimension = dimension}
        local type = roomInfo.Type;
        local variant = roomInfo.Variant;
        local dimension = roomInfo.Dimension;

        -- Get the target out-of-map room's descriptor, I use -58123 as the index here.
        local targetRoom = level:GetRoomByIdx(index, dimension); 

        --Goto the room with specified room data, setting the room with index -3 to the target room data.
        if (type) then
            Isaac.ExecuteCommand("goto s."..type.."."..variant);
        else
            Isaac.ExecuteCommand("goto d."..variant);
        end

        --Get the room data of -3, and set the -58123 room's data to it.
        local targetRoomData = level:GetRoomByIdx(-3).Data;
        targetRoom.Data = targetRoomData; 

        -- Randomize the seeds to avoid crashes.
        rng:SetSeed(seed + index * 58123, 1);
        targetRoom.SpawnSeed = rng:Next();
        targetRoom.AwardSeed = rng:Next();
        targetRoom.DecorationSeed = rng:Next();

        -- Tries to resolve the issue, but useless.
        targetRoom.GridIndex = index;
        targetRoom.SafeGridIndex = index;
        targetRoom.ListIndex = 506-index;
        print(index, "Generated");
    end

    -- Go back to the current room.
    game:StartRoomTransition(currentRoomIndex, Direction.NO_DIRECTION, RoomTransitionAnim.FADE);
end

local function PostNewLevel(mod)
    GenerateAdditionalRooms();
end
Mod:AddCallback(ModCallbacks.MC_POST_NEW_LEVEL, PostNewLevel);

It works well, I can create an extra room that out of map, and I can keep persistent entities, like collectibles, pickups, or machines inside it after enter it again, but there's another problem.

I found that the room keeps all of the persistent entities insided it between levels, and even runs. When I restart the whole game, the room's data get cleared.

So I think is there any method to clear room's entity states?

Note: The entities only keeps if you leave the room or quit the game (not restart the game), or the room's state will not be saved.

Zamiell commented 2 years ago

I found that the room keeps all of the persistent entities insided it between levels, and even runs. When I restart the whole game, the room's data get cleared.

That's pretty wild and was previously unknown. I think that's fairly useful, as there isn't another way to persist entities between runs.

Cuerzor commented 2 years ago

That's pretty wild and was previously unknown. I think that's fairly useful, as there isn't another way to persist entities between runs.

Surely this could be useful in some ways. But I still want a function to clear the room's data manually, this issue bothers me for a time

Zamiell commented 2 years ago

Can you make a copy of the data before putting entities in it?

Cuerzor commented 2 years ago

No I think, I tried every field and function, nothing helps

Zamiell commented 2 years ago

I would recommend that you don't use this hack for "storing" entities, as it relies on memory that isn't supposed to be initialized, and can potentially cause the game to crash. It's fairly easy to keep track of any state related to entities and respawn them at the appropriate times - use a Map<roomGridIndex, Map<position, data>> e.g. look at the algorithm for how IsaacScript stores implements custom trapdoors.

Cuerzor commented 2 years ago

I would recommend that you don't use this hack for "storing" entities, as it relies on memory that isn't supposed to be initialized, and can potentially cause the game to crash. It's fairly easy to keep track of any state related to them and respawn them at the appropriate times - use a Map<roomGridIndex, Map<position, data>> e.g. look at the algorithm for how IsaacScript stores implements custom trapdoors.

Can't understand... I use lua

Zamiell commented 2 years ago

It's just a two dimensional https://en.wikipedia.org/wiki/Associative_array The programming language doesn't matter.

Cuerzor commented 2 years ago

它只是一个二维的https://en.wikipedia.org/wiki/Associative_array 编程语言无关紧要。

Well...I'm not supposing to store entities, I just want a out-of-map room for my custom bossfight

Zamiell commented 2 years ago

Why can't you simply use the debug room?

Cuerzor commented 2 years ago

Why can't you simply use the debug room?

In fact, I'm currently using it, but the debug room will not save and if you continue the game or use glowing hourglass, the game crashes. A lot of players feedbacked this

Zamiell commented 2 years ago

Ok. What floor is it on? You can probably just use ROOM_BLUE_WOOM_IDX instead of the debug room. Change the backdrop once you enter to whatever you want it to be.

Cuerzor commented 2 years ago

Ok. What floor is it on? You can probably just use ROOM_BLUE_WOOM_IDX instead of the debug room. Change the backdrop once you enter to whatever you want it to be.

It's on home floor, and it's backdrop is planetarium which is not changable using Game:ShowHallucination (It will cause the backdrop glitched). Looks like there's no perfect method to resolve it. Fine, just put the problem away.

Zamiell commented 2 years ago

It's on home floor, and it's backdrop is planetarium which is not changable using Game:ShowHallucination (It will cause the backdrop glitched).

You can work around that with set stage. Here's a short example in TypeScript (you'll have to convert it to Lua, but hopefully it should be pretty clear):

function teleportToMyCustomBossRoom() {
  const level = game.GetLevel();
  level.SetStage(-1, StageType.WRATH_OF_THE_LAMB);
  teleport(GridRoom.BLUE_WOMB);
  runNextRoom(postNewRoomMyCustomBossRoom);
}

function postNewRoomMyCustomBossRoom() {
  setBackdrop(BackdropType.MINES_SHAFT);
  const level = game.GetLevel();
  level.SetStage(LevelStage.HOME, StageType.ORIGINAL);
  removeAllTrapdoors();
  spawnCustomBoss();
}

function spawnCustomBoss() {
  // TODO
}