Is there some kind of mechanism I can use to cause a door to close automatically when passing through it?

I have a roof access door made, but I want to close it from the inside and I’m not clear about the best way to do that.

/furniture_and_terrain/terrain-roofs.json
  {
    "type": "terrain",
    "id": "t_roof_access_door",
    "name": "closed metal roof access door",
    "description": "A closed roof access door or hinged opening of some kind.",
    "symbol": "o",
    "looks_like": "t_scrap_floor",
    "color": "light_gray",
    "move_cost": 2,
    "flags": [ "TRANSPARENT", "FLAT", "OPENCLOSE_INSIDE", "BARRICADABLE_DOOR", "REDUCE_SCENT", "LOCKED" ],
    "examine_action": "locked_object",
    "open": "t_roof_access_door_open",
    "bash": {
      "str_min": 60,
      "str_max": 210,
      "sound": "metal screeching!",
      "sound_fail": "clang!",
      "ter_set": "t_skylight_frame",
      "items": [ { "item": "scrap", "count": [ 3, 6 ] } ]
    },
    "prying": {
      "result": "t_roof_access_door_open",
      "message": "You pry open the roof access door.",
      "prying_data": { "difficulty": 8, "prying_level": 4, "noisy": true, "failure": "The metal door is hard to leverage open." }
    }
  },
  {
    "type": "terrain",
    "id": "t_roof_access_door_open",
    "name": "open metal roof access door",
    "description": "An open roof access door.  It appears as though the metal shutters can only be activated from the inside.",
    "symbol": "o",
    "looks_like": "t_linoleum_white",
    "color": "light_gray",
    "move_cost": 2,
    "trap": "tr_ledge",
    "roof": "t_flat_roof",
    "examine_action": "ledge",
    "close": "t_roof_access_door",
    "connect_groups": "INDOORFLOOR",
    "flags": [ "TRANSPARENT", "NO_FLOOR", "DOOR", "INDOORS", "EMPTY_SPACE", "TRANSPARENT_FLOOR" ],
    "bash": {
      "str_min": 60,
      "str_max": 210,
      "sound": "metal screeching!",
      "sound_fail": "clang!",
      "ter_set": "t_skylight_frame",
      "items": [ { "item": "scrap", "count": [ 3, 6 ] } ]
    }
  },
/construction_group.json
{
    "type": "construction_group",
    "id": "build_roof_access_door",
    "name": "Build Roof Access Door"
  },
/construction/roofs.json
  {
    "type": "construction",
    "id": "constr_roof_access_door",
    "group": "build_roof_access_door",
    "//": "Step 1: Build roof access door",
    "category": "CONSTRUCT",
    "required_skills": [ [ "fabrication", 2 ] ],
    "time": "60 m",
    "qualities": [ [ { "id": "DRILL", "level": 1 } ], [ { "id": "SAW_M", "level": 1 } ], [ { "id": "WRENCH", "level": 2 } ] ],
    "components": [ [ [ "hinge", 2 ] ], [ [ "nuts_bolts", 6 ] ], [ [ "scrap", 4 ] ], [ [ "sheet_metal", 1  ], [ "sheet_metal_small", 4 ] ] ],
    "pre_terrain": "t_skylight_frame",
    "post_terrain": "t_roof_access_door"
  },

I’ve never made a pull request in my life on someone else’s stuff or really shared anything I’ve messed with. I’ve got no idea why this does not already exist or whatnot. I assume it has to do with the Z-level issue I’m messing with and asking about. I was thinking of maybe trying to use the climbing rope somehow as a way to add an element to the lower z-level, or maybe adding a lower z-level component to the build. I was thinking about a way to “mount” a ladder or something like that. I kinda like the idea of a ladder needed to get in and out of the lower level.

I’m also not super deep into the game timeline and don’t really want to ruin much of anything, but can enemies access a roof and enter?

I don’t entirely understand the syntax and scope of EOC system and that gets kinda spoiler’y to read into in detail with a lot of complex stuff that is not exactly related to what I am trying to do with a trigger on entry.

I tried creating a trap, but I don’t understand how “id”: “tr_ledge” works in the /core/traps.json in contrast to the others in /traps.json and how to write the same list of examine-ledge options… or if that is the right way of doing it.

I really wanted something like the "down": { tag/type/label/whatever you call the things in the list of features of each item… from /climbing.json
I wanted to have… “down”:{ “ter_set”: “t_roof_access_door” }

like this
  {
    "type": "terrain",
    "id": "t_roof_access_door",
    "name": "closed metal roof access door",
    "description": "A closed roof access door or hinged opening of some kind.",
    "symbol": "o",
    "looks_like": "t_scrap_floor",
    "color": "light_gray",
    "move_cost": 2,
    "flags": [ "TRANSPARENT", "FLAT", "OPENCLOSE_INSIDE", "BARRICADABLE_DOOR", "REDUCE_SCENT", "LOCKED" ],
    "examine_action": "locked_object",
    "open": "t_roof_access_door_open",
    "bash": {
      "str_min": 60,
      "str_max": 210,
      "sound": "metal screeching!",
      "sound_fail": "clang!",
      "ter_set": "t_skylight_frame",
      "items": [ { "item": "scrap", "count": [ 3, 6 ] } ]
    },
    "prying": {
      "result": "t_roof_access_door_open",
      "message": "You pry open the roof access door.",
      "prying_data": { "difficulty": 8, "prying_level": 4, "noisy": true, "failure": "The metal door is hard to leverage open." }
    }
    "down":{
        "ter_set": "t_roof_access_door" ,
    }
  },

Also, is that cool to just use "looks_like": "t_scrap_floor", instead of trying to figure out how to make a whole tile? I just copied how the skylight was using "looks_like": "t_linoleum_white",. I don’t know if that interior tile is supposed to be relevant to the skylight or something.

Is the door intended to automatically close behind the player when they go home (which can be toggled in game options), or as a trap to close behind an enemy, or both?

Currently, I think I can use the gate controls type of construction to build a way to open the roof access door from below. The “roof access door” I am trying to create is basically the skylight frame with a metal door like you might find on many flat roof buildings. I still haven’t tested the gate controls method yet. I think it might work instead of what I wrote originally. I just found the precheck for terrain in constuction.cpp. The general documentation seems to hint that all the prechecks are for tiles below, but it looks like the code checks every location within one tile, so it should pick up the above tile.

Originally I wanted to be able to close the door behind me if I dropped in from above, but that was not well thought out for my end goal of having light from above, an alternative exit (with a step ladder), and the ability to close the roof access door for lighting and scent reduction at night.
Something like this:

  {
    "type": "terrain",
    "id": "t_roof_access_rope",
    "name": "roof access rope",
    "description": "A dangling end of rope, the other end is tied securely to a roof access door latching mechanism.",
    "symbol": "6",
    "color": "brown",
    "move_cost": 0,
    "connect_groups": "INDOORFLOOR",
    "flags": [ "TRANSPARENT", "NOITEM", "INDOORS", "PERMEABLE", "THIN_OBSTACLE" ],
    "examine_action": "controls_gate",
    "bash": {
      "str_min": 4,
      "str_max": 40,
      "sound": "crunch!",
      "sound_fail": "whump.",
      "ter_set": "t_null",
      "items": [
        { "item": "rope_makeshift_6", "count": [ 0, 1 ] },
        { "item": "splinter", "count": [ 2, 4 ] }
      ]
    },
    "deconstruct": {
      "ter_set": "t_null",
      "items": [
        { "item": "rope_makeshift_6", "count": 1 },
        { "item": "scrap", "count": 1 }
      ]
    }
  },

I just need to make the construction and test it now.

Ideally this would be possible to construct as a single item from above or below, where the door and controls are installed at the same time. I mean it is just “a rope attached to a latching mechanism” to open and close the door. So making it a 2 part build doesn’t make a lot of sense. I know /furniture_and_terrain/terrain-floors-indoor.json file has floor and roof constructions unified, but I am not aware of any constructions I have encountered from above that can add something to the z-level below. I am not sure of the syntax or whatnot.

Thanks for the reply BTW.

I’m not sure, but I think this code in gates.cpp is what is stopping me from making a “gate” for a roof access door. It is explicitly checking for a door, floor, or walls. I can barely read like 60% of this. I’m still really unclear about what is defining a gate (using gate_id = string_id<gate_data>;). I have matched the “_c” and “_o” syntax and copied a similar definition structure as the other gates, but the controls do not pick up a roof mounted access door.

void gate_data::load( const JsonObject &jo, const std::string_view )
{
    mandatory( jo, was_loaded, "door", door );
    mandatory( jo, was_loaded, "floor", floor );
    mandatory( jo, was_loaded, "walls", walls, string_id_reader<ter_t> {} );

    if( !was_loaded || jo.has_member( "messages" ) ) {
        JsonObject messages_obj = jo.get_object( "messages" );

        optional( messages_obj, was_loaded, "pull", pull_message );
        optional( messages_obj, was_loaded, "open", open_message );
        optional( messages_obj, was_loaded, "close", close_message );
        optional( messages_obj, was_loaded, "fail", fail_message );
    }

    optional( jo, was_loaded, "moves", moves, 0 );
    optional( jo, was_loaded, "bashing_damage", bash_dmg, 0 );
}

I probably just need to beat my head against the keyboard a little harder, but that is as far as I have gotten so far.