CleverRaven / Cataclysm-DDA

Cataclysm - Dark Days Ahead. A turn-based survival game set in a post-apocalyptic world.
http://cataclysmdda.org
Other
10.33k stars 4.14k forks source link

Update mods to not rely on roof adding magic #74002

Open PatrikLundell opened 4 months ago

PatrikLundell commented 4 months ago

Is your feature request related to a problem? Please describe.

I'm trying to update the game's mapgen to explicitly add roofs where they should be in order to be able to remove the usage of roof adding magic. However, I don't intend to deal with mods (and I won't mind if others deal with non mod instances as well), so in order for mods not to be somewhat broken when that happens (if it does, and many months from now), conversion ought to be performed.

Solution you would like.

Mod maintainers going through their mods to ensure any mapgen they engage in produces all the roofs they should have.

Describe alternatives you have considered.

Give no warning and let mods break. That would be rude.

Additional context

An easy way to check for issues is to hack map.cpp operation map::add_roofs to return immediately (that disables the magic roof adding) and then debug spawn the construction (or map special) somewhere and teleport to look at them. Debug spawning scaffolding works to provide a vantage point above the buildings, although teleporting onto where the roof should be would work as well.

The most common issue is to not providing a roof on top of the top story of a building.

Context: What is "roof adding magic"? That's my derisive name for the add_roofs functionality. It scans maps on loading and adds "roof" on top of any "terrain" that has a "roof" specified where there isn't a "roof" above it already. I want that usage gone, except for usage with trees, because it's not intuitive and causes issues (such as the "repair" of "roofs" above rooms you've just dug into). If you want a roof on top of your wall you should build it (and you should be allowed to build whatever roof you want (such as a wall for an additional level) rather than automatically get what the wall specified as its roof. The exception for trees is because I don't see any good way to place them using existing methods, since their position should be somewhat random and you'd need to coordinate the tree top placement with the trunk.

John-Candlebury commented 4 months ago

There are a handfull of use cases where the "roof magic is desireable" the big one is generating ruined surface structures, as happens with aftershock formless ruins.

Those are completely chunk based and having to ensure each chunk is placed with a corresponding maching roof chunk above would be incredibly complex and make expanding them harder.

PatrikLundell commented 4 months ago

That's very useful information.

I'll try to look at this case to understand the case in detail, but I may come back later to ask for more info if I can't understand it.

PatrikLundell commented 4 months ago

I've had a first look at the aftershock formless ruins, but will have to look at it more.

I'll be back after more investigations.

Edit: Yes, it seems to work quite easily:

  {
    "type": "mapgen",
    "method": "json",
    "nested_mapgen_id": "fbml_2_coop_construction",
    "object": {
      "parameters": {
        "fbml_2_construction_palette": {
          "type": "palette_id",
          "scope": "omt",
          "default": {
            "distribution": [
              "fbml_2_concrete_palette",
              "fbml_2_log_palette",
              "fbml_2_metal_palette",
              "fbml_2_migo_resin_palette",
              "fbml_2_rammed_earth_palette",
              "fbml_2_rock_palette",
              "fbml_2_wad_palette",
              "fbml_2_wood_palette"
            ]
          }
        }
      },
      "mapgensize": [ 8, 8 ],
      "rows": [
        "wwwwwww ",
        "w,,,,,w ",
        "w,,,,,w ",
        "w,,,,,w ",
        "w,,,,,w ",
        "w,,,,,w ",
        "w,,,,,w ",
        "w     w "
      ],
      "flags": [ "ALLOW_TERRAIN_UNDER_OTHER_DATA" ],
      "palettes": [ { "param": "fbml_2_construction_palette" } ],
      "place_nested": [
        { "chunks": [ "fbml_2_coop_roof_construction" ], "x": 0, "y": 0, "z": 1 }
      ]
    }
  },
  {
    "type": "mapgen",
    "update_mapgen_id": "fbml_2_coop_construction",
    "method": "json",
    "object": {
      "place_nested": [
        { "chunks": [ "fbml_2_coop_construction" ], "x": 13, "y": 0, "z": 0 }
      ]
    }
  },

results in a chicken coop with a roof of the correct material. The "real" implementation has the roof chunk placed beneath the ground level one in the update_mapgen section, but moving it into the ground chunk as a nest offset by one Z level worked.

Edit 2: Changing mapgen to support 3D turns out to be tricky: Currently tinymaps are used by map::loadn to create one Z level at a time from the bottom up, which means higher Z levels aren't generated yet. In order to support 3D we'd have to somehow first create a basic background so there is something available before the "real" mapgen is performed. It also means tinymap would have to be removed, as 3D cannot be loaded properly by it (any attempt to write things at another Z level results in it being written on the only existing one, without any error indication, and tests even ensure it behaves in that bizarre way).

PatrikLundell commented 3 months ago

Master now supports 3D mapgen, so the formless ruins should now be possible to update with relative ease. The FEMA camp has a similar logic which has been updated to provide roofs, so it could be used as a reference, if needed.

Maleclypse commented 3 months ago

Ok so I've inherited some mapgen from vanilla cave_rat I think I need to work around the old roof magic. I'll keep looking for others that might break

Maleclypse commented 3 months ago

The genius locii maps use roof magic, I might let them just have open roofs though. @Standing-Storm will that make paraclesian starts more dangerous due to exposure?

edit: I think that's all of the maps in XE that need roofing.

PatrikLundell commented 3 months ago

I can help with updating mod maps if provided with info on what to change (@John-Candlebury has provided such info for Aftershock), but I can't perform the testing if them, as that requires knowledge of how to use the mods.

Maleclypse commented 2 months ago

I can help with updating mod maps if provided with info on what to change (@John-Candlebury has provided such info for Aftershock), but I can't perform the testing if them, as that requires knowledge of how to use the mods.

Yeah the cave_rat needs whatever was done to other caves, I’d have to look at them to know. Are caves on z-1 or z0? If z0 I guess they just need a z1roof. Sorry I really need to look at caves to see how they work. I think they are on z0?

PatrikLundell commented 2 months ago

I just looked at the rat cave, and as far as I can see it has an ordinary cave at Z=0 and replaces Z=-1 with its own special version, and adds another special version to Z=-2.

I just debug spawned a rat cave, teleported to it, and looked at it, and as far as I can see it's fine. There isn't a hole in the roof where the slope down is (as I haven't hacked the code I'm playing with to suppress roof magic), which indicates that the normal cave JSON takes care of it.

Technical ramble below, skip if not interested:

There are a few different ways to add a roof. The "easiest" one is to just add another overmap layer above the ground (like most buildings do), but you can't do that with caves as there are multiple version, and the roof obviously has to match the ground level layout.

The second version is to add a nested chunk to the map and have that chuck being offset by 1 Z level (you could make it smaller and offset (x,y) to compensate, but that's easier to screw up than just have a layer on top).

If I remember correctly, I used a third method for the caves, which is to make single tile chunks of the surface level terrains and then nest in a roof level above it (again, with a vertical offset of 1), and then map these to glyphs in the palette. Version 2 would be fine for caves, but there are a fair number of them, so it might be less work to use this method (and less prone to error, as well as easier to update, since you only have to deal with a single layout). This method was necessary for the monster corpse and river caves, as I couldn't find out how to add a roof level to the complex logic (there are no permutations, so that should work if the syntax and logic is correct).

Note that the third method isn't suitable for general buildings, because that would mean you'd need two versions of all walls and doors (one for the exterior, where you want a roof with a gutter, and one for the interior ones, where you'd want a "normal" roof), not to mention that you don't want another chunk on top of the "normal" one except for the highest floor (and you probably want roof decorations anyway, in which case the tile approach isn't suitable).