From a53391cb06ea02cae61da7e23f853018ef55d344 Mon Sep 17 00:00:00 2001 From: Brian Lefler Date: Sat, 21 Jul 2018 19:21:40 -0400 Subject: [PATCH] Automatically balance hardcoded & json lab & lab finales against each other. - The 3 lab sections have a 500 weight each (cross, tic-tac-toe, big room) - The 4 lab finales have a weight of 100 each, except arms testing is 200. - An example lab finale map was added as an example, a buffed up autodoc room. - Modified with a change of bionics and 4-9 anesthetic, plus a shocker brute. - Weight is only 25, since I think it's less cool than the other finales. - Finales will now properly handle stairs. - Json finales should be called lab_finale_1level. Hope is to sometime add 2level finales. --- .../lab/lab_floorplans_finale1level.json | 88 ++ json_blacklist | 1 + src/mapgen.cpp | 829 ++++++++++-------- 3 files changed, 543 insertions(+), 375 deletions(-) create mode 100644 data/json/mapgen/lab/lab_floorplans_finale1level.json diff --git a/data/json/mapgen/lab/lab_floorplans_finale1level.json b/data/json/mapgen/lab/lab_floorplans_finale1level.json new file mode 100644 index 0000000000000..e5f70de339956 --- /dev/null +++ b/data/json/mapgen/lab/lab_floorplans_finale1level.json @@ -0,0 +1,88 @@ +[ + { + "//": "autodoc operating theatre", + "type": "mapgen", + "method": "json", + "om_terrain": [ "lab_finale_1level" ], + "weight": 2500, + "object": { + "rotation": [ 0, 3 ], + "fill_ter": "t_rock_floor", + "rows": [ + "..cccccc.|...|,,,|..|,,,", + "c........|...|,,,|.6|,,,", + "c..Ccxc..|...|,,,g..g,,,", + "c........g...|,,,g..g,,,", + "c........g...|,,,D..D,,,", + "......llS|...|---|..|---", + "--gg-+---|...|,,,|..|,,,", + ".............|,,,g..g,,,", + ".............|,,,g..g,,,", + ".............|,,,D..D,,,", + "........|-ggg----|..|---", + "........|r,,,r|t--+-|...", + "........g,,/,,D.....+...", + "........g,,?,,|-ggg-|...", + "........|r,,,r|.........", + "........|-ggg-|.........", + "........................", + "........................", + "..........dd7dd.........", + "..........d.h.d.........", + "...ddxdd.........ddxdd..", + "...d.h.d.........d.h.d..", + "........................", + "........................" + ], + "palettes": [ + "lab_palette", "lab_loot_generic" + ], + "furniture": { + "?": "f_autodoc", + "/": "f_autodoc_couch" + }, + "terrain": { + "C": "t_centrifuge", + "?": "t_floor_blue", + "/": "t_floor_blue", + "7": "t_console", + "r": "t_floor_blue" + }, + "place_loot": [ + { "item": "anesthesia", "x": 15, "y": 11, "repeat": [4,9] } + ], + "mapping": { + "r": { + "items": [ + { "item": "bionics_common", "chance": 40 }, + { "item": "bionics", "chance": 20 }, + { "item": "hospital_medical_items", "chance": 80 }, + { "item": "dissection", "chance": 60 } + ] + } + }, + "computers": { + "6": { + "name": "Operating Theatre Access Control", "security": 0, "options": [ { "name": "EMERGENCY EVAC - OPEN ALL DOORS", "action": "open", "security": 0 } ], "failures": [ + { "action": "damage" }, + { "action": "shutdown" } + ] + }, + "7": { + "name": "Operating Theatre Access Control", "security": 2, "options": [ { "name": "UNLOCK AUTODOC DOOR", "action": "unlock", "security": 6 } ], "failures": [ + { "action": "damage" }, + { "action": "shutdown" } + ] + } + }, + "place_monster": [ + { "monster": [ "mon_broken_cyborg", "mon_zombie_electric" ], "x": [ 14, 16 ], "y": [ 1, 4 ], "chance": 90 }, + { "monster": "mon_broken_cyborg", "x": [ 14, 16 ], "y": [ 6, 9 ], "chance": 90 }, + { "monster": "mon_broken_cyborg", "x": [ 21, 22 ], "y": [ 1, 4 ], "chance": 90 }, + { "monster": "mon_broken_cyborg", "x": [ 21, 22 ], "y": [ 6, 9 ], "chance": 90 }, + { "monster": "mon_zombie_scientist", "x": [15, 19], "y": 12, "chance": 90, "repeat": [1,2] }, + { "monster": "mon_zombie_brute_shocker", "x": [ 9, 10 ], "y": [ 12, 13 ]} + ] + } + } +] diff --git a/json_blacklist b/json_blacklist index 55ac95b9f39b8..0a38ee761ee64 100644 --- a/json_blacklist +++ b/json_blacklist @@ -134,6 +134,7 @@ data/json/mapgen/house/house_rv.json data/json/mapgen/house/house_suicide.json data/json/mapgen/lab/lab_common.json data/json/mapgen/lab/lab_floorplan_cross.json +data/json/mapgen/lab/lab_floorplans_finale1level.json data/json/mapgen/lab/lab_floorplans.json data/json/mapgen/lab/lab_rooms.json data/json/mapgen/laundromat.json diff --git a/src/mapgen.cpp b/src/mapgen.cpp index 03d7cda6d9e84..7e8b5efd8b95c 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -2940,201 +2940,19 @@ ___DEEE|.R.|...,,...|sss\n", ter_set( *p, t_stairs_down ); } } - } else switch (rng(1, 5)) { // Pick a random lab layout - results of 4+ will use json, so 40% w/ a 1,5 rng. - case 1: // Cross shaped - for (int i = 0; i < SEEX * 2; i++) { - for (int j = 0; j < SEEY * 2; j++) { - if ((i < lw || i > SEEX * 2 - 1 - rw) || - ((j < SEEY - 1 || j > SEEY) && (i == SEEX - 2 || i == SEEX + 1))) { - ter_set(i, j, t_concrete_wall); - } else if ((j < tw || j > SEEY * 2 - 1 - bw) || - ((i < SEEX - 1 || i > SEEX) && (j == SEEY - 2 || j == SEEY + 1))) { - ter_set(i, j, t_concrete_wall); - } else { - ter_set(i, j, t_rock_floor); - } - } - } - if (t_above == "lab_stairs" || t_above == "ice_lab_stairs") { - ter_set(rng(SEEX - 1, SEEX), rng(SEEY - 1, SEEY), t_stairs_up); - } - // Top left - if (one_in(2)) { - ter_set(SEEX - 2, int(SEEY / 2), t_door_metal_c); - science_room(this, lw, tw, SEEX - 3, SEEY - 3, zlevel, 1); - } else { - ter_set(int(SEEX / 2), SEEY - 2, t_door_metal_c); - science_room(this, lw, tw, SEEX - 3, SEEY - 3, zlevel, 2); - } - // Top right - if (one_in(2)) { - ter_set(SEEX + 1, int(SEEY / 2), t_door_metal_c); - science_room(this, SEEX + 2, tw, SEEX * 2 - 1 - rw, SEEY - 3, zlevel, 3); - } else { - ter_set(SEEX + int(SEEX / 2), SEEY - 2, t_door_metal_c); - science_room(this, SEEX + 2, tw, SEEX * 2 - 1 - rw, SEEY - 3, zlevel, 2); - } - // Bottom left - if (one_in(2)) { - ter_set(int(SEEX / 2), SEEY + 1, t_door_metal_c); - science_room(this, lw, SEEY + 2, SEEX - 3, SEEY * 2 - 1 - bw, zlevel, 0); - } else { - ter_set(SEEX - 2, SEEY + int(SEEY / 2), t_door_metal_c); - science_room(this, lw, SEEY + 2, SEEX - 3, SEEY * 2 - 1 - bw, zlevel, 1); - } - // Bottom right - if (one_in(2)) { - ter_set(SEEX + int(SEEX / 2), SEEY + 1, t_door_metal_c); - science_room(this, SEEX + 2, SEEY + 2, SEEX * 2 - 1 - rw, SEEY * 2 - 1 - bw, - zlevel, 0); - } else { - ter_set(SEEX + 1, SEEY + int(SEEY / 2), t_door_metal_c); - science_room(this, SEEX + 2, SEEY + 2, SEEX * 2 - 1 - rw, SEEY * 2 - 1 - bw, - zlevel, 3); - } - if (rw == 1) { - ter_set(SEEX * 2 - 1, SEEY - 1, t_door_metal_c); - ter_set(SEEX * 2 - 1, SEEY , t_door_metal_c); - } - if (bw == 1) { - ter_set(SEEX - 1, SEEY * 2 - 1, t_door_metal_c); - ter_set(SEEX , SEEY * 2 - 1, t_door_metal_c); - } - if (terrain_type == "lab_stairs" || terrain_type == "ice_lab_stairs") { // Stairs going down - std::vector stair_points; - if (tw != 0) { - stair_points.push_back(point(SEEX - 1, 2)); - stair_points.push_back(point(SEEX - 1, 2)); - stair_points.push_back(point(SEEX , 2)); - stair_points.push_back(point(SEEX , 2)); - } - if (rw != 1) { - stair_points.push_back(point(SEEX * 2 - 3, SEEY - 1)); - stair_points.push_back(point(SEEX * 2 - 3, SEEY - 1)); - stair_points.push_back(point(SEEX * 2 - 3, SEEY )); - stair_points.push_back(point(SEEX * 2 - 3, SEEY )); - } - if (bw != 1) { - stair_points.push_back(point(SEEX - 1, SEEY * 2 - 3)); - stair_points.push_back(point(SEEX - 1, SEEY * 2 - 3)); - stair_points.push_back(point(SEEX , SEEY * 2 - 3)); - stair_points.push_back(point(SEEX , SEEY * 2 - 3)); - } - if (lw != 0) { - stair_points.push_back(point(2, SEEY - 1)); - stair_points.push_back(point(2, SEEY - 1)); - stair_points.push_back(point(2, SEEY )); - stair_points.push_back(point(2, SEEY )); - } - stair_points.push_back(point(int(SEEX / 2) , SEEY )); - stair_points.push_back(point(int(SEEX / 2) , SEEY - 1)); - stair_points.push_back(point(int(SEEX / 2) + SEEX, SEEY )); - stair_points.push_back(point(int(SEEX / 2) + SEEX, SEEY - 1)); - stair_points.push_back(point(SEEX , int(SEEY / 2) )); - stair_points.push_back(point(SEEX + 2, int(SEEY / 2) )); - stair_points.push_back(point(SEEX , int(SEEY / 2) + SEEY)); - stair_points.push_back(point(SEEX + 2, int(SEEY / 2) + SEEY)); - const point p = random_entry( stair_points ); - ter_set(p.x, p.y, t_stairs_down); - } - - break; - - case 2: // tic-tac-toe # layout - for (int i = 0; i < SEEX * 2; i++) { - for (int j = 0; j < SEEY * 2; j++) { - if (i < lw || i > SEEX * 2 - 1 - rw || i == SEEX - 4 || i == SEEX + 3) { - ter_set(i, j, t_concrete_wall); - } else if (j < lw || j > SEEY * 2 - 1 - bw || j == SEEY - 4 || j == SEEY + 3) { - ter_set(i, j, t_concrete_wall); - } else { - ter_set(i, j, t_rock_floor); - } - } - } - if (t_above == "lab_stairs" || t_above == "ice_lab_stairs") { - ter_set(SEEX - 1, SEEY - 1, t_stairs_up); - ter_set(SEEX , SEEY - 1, t_stairs_up); - ter_set(SEEX - 1, SEEY , t_stairs_up); - ter_set(SEEX , SEEY , t_stairs_up); - } - ter_set(SEEX - rng(0, 1), SEEY - 4, t_door_metal_c); - ter_set(SEEX - rng(0, 1), SEEY + 3, t_door_metal_c); - ter_set(SEEX - 4, SEEY + rng(0, 1), t_door_metal_c); - ter_set(SEEX + 3, SEEY + rng(0, 1), t_door_metal_c); - ter_set(SEEX - 4, int(SEEY / 2), t_door_metal_c); - ter_set(SEEX + 3, int(SEEY / 2), t_door_metal_c); - ter_set(int(SEEX / 2), SEEY - 4, t_door_metal_c); - ter_set(int(SEEX / 2), SEEY + 3, t_door_metal_c); - ter_set(SEEX + int(SEEX / 2), SEEY - 4, t_door_metal_c); - ter_set(SEEX + int(SEEX / 2), SEEY + 3, t_door_metal_c); - ter_set(SEEX - 4, SEEY + int(SEEY / 2), t_door_metal_c); - ter_set(SEEX + 3, SEEY + int(SEEY / 2), t_door_metal_c); - science_room(this, lw, tw, SEEX - 5, SEEY - 5, zlevel, rng(1, 2)); - science_room(this, SEEX - 3, tw, SEEX + 2, SEEY - 5, zlevel, 2); - science_room(this, SEEX + 4, tw, SEEX * 2 - 1 - rw, SEEY - 5, zlevel, rng(2, 3)); - science_room(this, lw, SEEY - 3, SEEX - 5, SEEY + 2, zlevel, 1); - science_room(this, SEEX + 4, SEEY - 3, SEEX * 2 - 1 - rw, SEEY + 2, zlevel, 3); - science_room(this, lw, SEEY + 4, SEEX - 5, SEEY * 2 - 1 - bw, zlevel, rng(0, 1)); - science_room(this, SEEX - 3, SEEY + 4, SEEX + 2, SEEY * 2 - 1 - bw, zlevel, 0); - science_room(this, SEEX + 4, SEEX + 4, SEEX * 2 - 1 - rw, SEEY * 2 - 1 - bw, - zlevel, 3 * rng(0, 1)); - if (rw == 1) { - ter_set(SEEX * 2 - 1, SEEY - 1, t_door_metal_c); - ter_set(SEEX * 2 - 1, SEEY , t_door_metal_c); - } - if (bw == 1) { - ter_set(SEEX - 1, SEEY * 2 - 1, t_door_metal_c); - ter_set(SEEX , SEEY * 2 - 1, t_door_metal_c); - } - if (terrain_type == "lab_stairs" || terrain_type == "ice_lab_stairs") { - ter_set(SEEX - 3 + 5 * rng(0, 1), SEEY - 3 + 5 * rng(0, 1), t_stairs_down); - } - break; + } else { + const std::string function_key = "lab_4side"; + const auto fmapit = oter_mapgen.find( function_key ); + const int hardcoded_4side_map_weight = 1500; // weight of all hardcoded maps. + bool use_hardcoded_4side_map = false; - case 3: // Big room - for (int i = 0; i < SEEX * 2; i++) { - for (int j = 0; j < SEEY * 2; j++) { - if (i < lw || i >= SEEX * 2 - 1 - rw) { - ter_set(i, j, t_concrete_wall); - } else if (j < tw || j >= SEEY * 2 - 1 - bw) { - ter_set(i, j, t_concrete_wall); - } else { - ter_set(i, j, t_rock_floor); - } - } - } - science_room(this, lw, tw, SEEX * 2 - 1 - rw, SEEY * 2 - 1 - bw, - zlevel, rng(0, 3)); - if (t_above == "lab_stairs" || t_above == "ice_lab_stairs") { - if( const auto p = random_point( points_in_rectangle( { lw, tw, abs_sub.z }, { SEEX * 2 - 1 - rw, SEEY * 2 - 1 - bw, abs_sub.z } ), [this]( const tripoint &n ) { return ter( n ) == t_rock_floor; } ) ) { - ter_set( *p, t_stairs_up ); - } - } - if (rw == 1) { - ter_set(SEEX * 2 - 1, SEEY - 1, t_door_metal_c); - ter_set(SEEX * 2 - 1, SEEY , t_door_metal_c); - } - if (bw == 1) { - ter_set(SEEX - 1, SEEY * 2 - 1, t_door_metal_c); - ter_set(SEEX , SEEY * 2 - 1, t_door_metal_c); - } - if (terrain_type == "lab_stairs" || terrain_type == "ice_lab_stairs") { - if( const auto p = random_point( points_in_rectangle( { lw, tw, abs_sub.z }, { SEEX * 2 - 1 - rw, SEEY * 2 - 1 - bw, abs_sub.z } ), [this]( const tripoint &n ) { return ter( n ) == t_rock_floor; } ) ) { - ter_set( *p, t_stairs_down ); - } - } - break; - default: - const std::string function_key = "lab_4side"; - const auto fmapit = oter_mapgen.find( function_key ); + if ( fmapit != oter_mapgen.end() && !fmapit->second.empty() ) { + std::map >::const_iterator weightit = oter_mapgen_weights.find( function_key ); + const int rlast = weightit->second.rbegin()->first; + const int roll = rng(1, rlast + hardcoded_4side_map_weight); - if ( fmapit != oter_mapgen.end() && !fmapit->second.empty() ) { - std::map >::const_iterator weightit = oter_mapgen_weights.find( function_key ); - const int rlast = weightit->second.rbegin()->first; - const int roll = rng(1, rlast); + if (roll <= rlast) { const int fidx = weightit->second.lower_bound( roll )->second; - fmapit->second[fidx]->generate(this, terrain_type, dat, when, density); // If the map template hasn't handled borders, handle them in code. Rotated maps cannot handle @@ -3171,24 +2989,214 @@ ___DEEE|.R.|...,,...|sss\n", } } } - } else { - debugmsg("Error: Tried to generate 4-sided lab but no lab_4side json exists."); + + if (t_above == "lab_stairs" || t_above == "ice_lab_stairs") { + if( const auto p = random_point( points_in_rectangle( { lw, tw, abs_sub.z }, { SEEX * 2 - 1 - rw, SEEY * 2 - 1 - bw, abs_sub.z } ), [this]( const tripoint &n ) { return ter( n ) == t_rock_floor; } ) ) { + ter_set( *p, t_stairs_up ); + } + } + if (terrain_type == "lab_stairs" || terrain_type == "ice_lab_stairs") { + if( const auto p = random_point( points_in_rectangle( { lw, tw, abs_sub.z }, { SEEX * 2 - 1 - rw, SEEY * 2 - 1 - bw, abs_sub.z } ), [this]( const tripoint &n ) { return ter( n ) == t_rock_floor; } ) ) { + ter_set( *p, t_stairs_down ); + } + } + } else { // then weighted roll was in the hardcoded section + use_hardcoded_4side_map = true; + } // end json maps + } else { // then no json maps for lab_4side were found + use_hardcoded_4side_map = true; + } // end if no lab_4side was found. + if (use_hardcoded_4side_map) { + switch (rng(1, 3)) { + case 1: // Cross shaped + for (int i = 0; i < SEEX * 2; i++) { + for (int j = 0; j < SEEY * 2; j++) { + if ((i < lw || i > SEEX * 2 - 1 - rw) || + ((j < SEEY - 1 || j > SEEY) && (i == SEEX - 2 || i == SEEX + 1))) { + ter_set(i, j, t_concrete_wall); + } else if ((j < tw || j > SEEY * 2 - 1 - bw) || + ((i < SEEX - 1 || i > SEEX) && (j == SEEY - 2 || j == SEEY + 1))) { + ter_set(i, j, t_concrete_wall); + } else { + ter_set(i, j, t_rock_floor); + } + } + } + if (t_above == "lab_stairs" || t_above == "ice_lab_stairs") { + ter_set(rng(SEEX - 1, SEEX), rng(SEEY - 1, SEEY), t_stairs_up); + } + // Top left + if (one_in(2)) { + ter_set(SEEX - 2, int(SEEY / 2), t_door_metal_c); + science_room(this, lw, tw, SEEX - 3, SEEY - 3, zlevel, 1); + } else { + ter_set(int(SEEX / 2), SEEY - 2, t_door_metal_c); + science_room(this, lw, tw, SEEX - 3, SEEY - 3, zlevel, 2); + } + // Top right + if (one_in(2)) { + ter_set(SEEX + 1, int(SEEY / 2), t_door_metal_c); + science_room(this, SEEX + 2, tw, SEEX * 2 - 1 - rw, SEEY - 3, zlevel, 3); + } else { + ter_set(SEEX + int(SEEX / 2), SEEY - 2, t_door_metal_c); + science_room(this, SEEX + 2, tw, SEEX * 2 - 1 - rw, SEEY - 3, zlevel, 2); + } + // Bottom left + if (one_in(2)) { + ter_set(int(SEEX / 2), SEEY + 1, t_door_metal_c); + science_room(this, lw, SEEY + 2, SEEX - 3, SEEY * 2 - 1 - bw, zlevel, 0); + } else { + ter_set(SEEX - 2, SEEY + int(SEEY / 2), t_door_metal_c); + science_room(this, lw, SEEY + 2, SEEX - 3, SEEY * 2 - 1 - bw, zlevel, 1); + } + // Bottom right + if (one_in(2)) { + ter_set(SEEX + int(SEEX / 2), SEEY + 1, t_door_metal_c); + science_room(this, SEEX + 2, SEEY + 2, SEEX * 2 - 1 - rw, SEEY * 2 - 1 - bw, + zlevel, 0); + } else { + ter_set(SEEX + 1, SEEY + int(SEEY / 2), t_door_metal_c); + science_room(this, SEEX + 2, SEEY + 2, SEEX * 2 - 1 - rw, SEEY * 2 - 1 - bw, + zlevel, 3); + } + if (rw == 1) { + ter_set(SEEX * 2 - 1, SEEY - 1, t_door_metal_c); + ter_set(SEEX * 2 - 1, SEEY , t_door_metal_c); + } + if (bw == 1) { + ter_set(SEEX - 1, SEEY * 2 - 1, t_door_metal_c); + ter_set(SEEX , SEEY * 2 - 1, t_door_metal_c); + } + if (terrain_type == "lab_stairs" || terrain_type == "ice_lab_stairs") { // Stairs going down + std::vector stair_points; + if (tw != 0) { + stair_points.push_back(point(SEEX - 1, 2)); + stair_points.push_back(point(SEEX - 1, 2)); + stair_points.push_back(point(SEEX , 2)); + stair_points.push_back(point(SEEX , 2)); + } + if (rw != 1) { + stair_points.push_back(point(SEEX * 2 - 3, SEEY - 1)); + stair_points.push_back(point(SEEX * 2 - 3, SEEY - 1)); + stair_points.push_back(point(SEEX * 2 - 3, SEEY )); + stair_points.push_back(point(SEEX * 2 - 3, SEEY )); + } + if (bw != 1) { + stair_points.push_back(point(SEEX - 1, SEEY * 2 - 3)); + stair_points.push_back(point(SEEX - 1, SEEY * 2 - 3)); + stair_points.push_back(point(SEEX , SEEY * 2 - 3)); + stair_points.push_back(point(SEEX , SEEY * 2 - 3)); + } + if (lw != 0) { + stair_points.push_back(point(2, SEEY - 1)); + stair_points.push_back(point(2, SEEY - 1)); + stair_points.push_back(point(2, SEEY )); + stair_points.push_back(point(2, SEEY )); + } + stair_points.push_back(point(int(SEEX / 2) , SEEY )); + stair_points.push_back(point(int(SEEX / 2) , SEEY - 1)); + stair_points.push_back(point(int(SEEX / 2) + SEEX, SEEY )); + stair_points.push_back(point(int(SEEX / 2) + SEEX, SEEY - 1)); + stair_points.push_back(point(SEEX , int(SEEY / 2) )); + stair_points.push_back(point(SEEX + 2, int(SEEY / 2) )); + stair_points.push_back(point(SEEX , int(SEEY / 2) + SEEY)); + stair_points.push_back(point(SEEX + 2, int(SEEY / 2) + SEEY)); + const point p = random_entry( stair_points ); + ter_set(p.x, p.y, t_stairs_down); + } + break; - } - if (t_above == "lab_stairs" || t_above == "ice_lab_stairs") { - if( const auto p = random_point( points_in_rectangle( { lw, tw, abs_sub.z }, { SEEX * 2 - 1 - rw, SEEY * 2 - 1 - bw, abs_sub.z } ), [this]( const tripoint &n ) { return ter( n ) == t_rock_floor; } ) ) { - ter_set( *p, t_stairs_up ); + + case 2: // tic-tac-toe # layout + for (int i = 0; i < SEEX * 2; i++) { + for (int j = 0; j < SEEY * 2; j++) { + if (i < lw || i > SEEX * 2 - 1 - rw || i == SEEX - 4 || i == SEEX + 3) { + ter_set(i, j, t_concrete_wall); + } else if (j < lw || j > SEEY * 2 - 1 - bw || j == SEEY - 4 || j == SEEY + 3) { + ter_set(i, j, t_concrete_wall); + } else { + ter_set(i, j, t_rock_floor); + } + } } - } - if (terrain_type == "lab_stairs" || terrain_type == "ice_lab_stairs") { - if( const auto p = random_point( points_in_rectangle( { lw, tw, abs_sub.z }, { SEEX * 2 - 1 - rw, SEEY * 2 - 1 - bw, abs_sub.z } ), [this]( const tripoint &n ) { return ter( n ) == t_rock_floor; } ) ) { - ter_set( *p, t_stairs_down ); + if (t_above == "lab_stairs" || t_above == "ice_lab_stairs") { + ter_set(SEEX - 1, SEEY - 1, t_stairs_up); + ter_set(SEEX , SEEY - 1, t_stairs_up); + ter_set(SEEX - 1, SEEY , t_stairs_up); + ter_set(SEEX , SEEY , t_stairs_up); + } + ter_set(SEEX - rng(0, 1), SEEY - 4, t_door_metal_c); + ter_set(SEEX - rng(0, 1), SEEY + 3, t_door_metal_c); + ter_set(SEEX - 4, SEEY + rng(0, 1), t_door_metal_c); + ter_set(SEEX + 3, SEEY + rng(0, 1), t_door_metal_c); + ter_set(SEEX - 4, int(SEEY / 2), t_door_metal_c); + ter_set(SEEX + 3, int(SEEY / 2), t_door_metal_c); + ter_set(int(SEEX / 2), SEEY - 4, t_door_metal_c); + ter_set(int(SEEX / 2), SEEY + 3, t_door_metal_c); + ter_set(SEEX + int(SEEX / 2), SEEY - 4, t_door_metal_c); + ter_set(SEEX + int(SEEX / 2), SEEY + 3, t_door_metal_c); + ter_set(SEEX - 4, SEEY + int(SEEY / 2), t_door_metal_c); + ter_set(SEEX + 3, SEEY + int(SEEY / 2), t_door_metal_c); + science_room(this, lw, tw, SEEX - 5, SEEY - 5, zlevel, rng(1, 2)); + science_room(this, SEEX - 3, tw, SEEX + 2, SEEY - 5, zlevel, 2); + science_room(this, SEEX + 4, tw, SEEX * 2 - 1 - rw, SEEY - 5, zlevel, rng(2, 3)); + science_room(this, lw, SEEY - 3, SEEX - 5, SEEY + 2, zlevel, 1); + science_room(this, SEEX + 4, SEEY - 3, SEEX * 2 - 1 - rw, SEEY + 2, zlevel, 3); + science_room(this, lw, SEEY + 4, SEEX - 5, SEEY * 2 - 1 - bw, zlevel, rng(0, 1)); + science_room(this, SEEX - 3, SEEY + 4, SEEX + 2, SEEY * 2 - 1 - bw, zlevel, 0); + science_room(this, SEEX + 4, SEEX + 4, SEEX * 2 - 1 - rw, SEEY * 2 - 1 - bw, + zlevel, 3 * rng(0, 1)); + if (rw == 1) { + ter_set(SEEX * 2 - 1, SEEY - 1, t_door_metal_c); + ter_set(SEEX * 2 - 1, SEEY , t_door_metal_c); + } + if (bw == 1) { + ter_set(SEEX - 1, SEEY * 2 - 1, t_door_metal_c); + ter_set(SEEX , SEEY * 2 - 1, t_door_metal_c); + } + if (terrain_type == "lab_stairs" || terrain_type == "ice_lab_stairs") { + ter_set(SEEX - 3 + 5 * rng(0, 1), SEEY - 3 + 5 * rng(0, 1), t_stairs_down); + } + break; + + case 3: // Big room + for (int i = 0; i < SEEX * 2; i++) { + for (int j = 0; j < SEEY * 2; j++) { + if (i < lw || i >= SEEX * 2 - 1 - rw) { + ter_set(i, j, t_concrete_wall); + } else if (j < tw || j >= SEEY * 2 - 1 - bw) { + ter_set(i, j, t_concrete_wall); + } else { + ter_set(i, j, t_rock_floor); + } + } + } + science_room(this, lw, tw, SEEX * 2 - 1 - rw, SEEY * 2 - 1 - bw, + zlevel, rng(0, 3)); + if (t_above == "lab_stairs" || t_above == "ice_lab_stairs") { + if( const auto p = random_point( points_in_rectangle( { lw, tw, abs_sub.z }, { SEEX * 2 - 1 - rw, SEEY * 2 - 1 - bw, abs_sub.z } ), [this]( const tripoint &n ) { return ter( n ) == t_rock_floor; } ) ) { + ter_set( *p, t_stairs_up ); + } } + if (rw == 1) { + ter_set(SEEX * 2 - 1, SEEY - 1, t_door_metal_c); + ter_set(SEEX * 2 - 1, SEEY , t_door_metal_c); + } + if (bw == 1) { + ter_set(SEEX - 1, SEEY * 2 - 1, t_door_metal_c); + ter_set(SEEX , SEEY * 2 - 1, t_door_metal_c); + } + if (terrain_type == "lab_stairs" || terrain_type == "ice_lab_stairs") { + if( const auto p = random_point( points_in_rectangle( { lw, tw, abs_sub.z }, { SEEX * 2 - 1 - rw, SEEY * 2 - 1 - bw, abs_sub.z } ), [this]( const tripoint &n ) { return ter( n ) == t_rock_floor; } ) ) { + ter_set( *p, t_stairs_down ); + } + } + break; } - break; + } // endif use_hardcoded_4side_map + } // end 1 vs 4 sides + } // end aboveground vs belowground - } - } // Ants will totally wreck up the place tw = 0; rw = 0; @@ -3277,190 +3285,261 @@ ___DEEE|.R.|...,,...|sss\n", lw = is_ot_type("lab", t_west) ? 0 : 2; } - // Start by setting up a large, empty room. - for (int i = 0; i < SEEX * 2; i++) { - for (int j = 0; j < SEEY * 2; j++) { - if (i < lw || i > SEEX * 2 - 1 - rw) { - ter_set(i, j, t_concrete_wall); - } else if (j < tw || j > SEEY * 2 - 1 - bw) { - ter_set(i, j, t_concrete_wall); - } else { - ter_set(i, j, t_rock_floor); + const std::string function_key = "lab_finale_1level"; + const auto fmapit = oter_mapgen.find( function_key ); + const int hardcoded_finale_map_weight = 500; // weight of all hardcoded maps. + bool use_hardcoded_finale_map = false; + + if ( fmapit != oter_mapgen.end() && !fmapit->second.empty() ) { + std::map >::const_iterator weightit = oter_mapgen_weights.find( function_key ); + const int rlast = weightit->second.rbegin()->first; + const int roll = rng(1, rlast + hardcoded_finale_map_weight); + + if (roll <= rlast) { + const int fidx = weightit->second.lower_bound( roll )->second; + fmapit->second[fidx]->generate(this, terrain_type, dat, when, density); + + // If the map template hasn't handled borders, handle them in code. Rotated maps cannot handle + // borders and have to be caught in code. We determine if a border isn't handled by checking + // the east-facing border space where the door normally is -- it should not be a floor. + if( ter(tripoint(23, 11, abs_sub.z)) == t_rock_floor ) { + // TODO: create a ter_reset function that does ter_set, furn_set, and i_clear? + for( int i = 0; i <= 23; i++ ) { + ter_set( 23, i, t_concrete_wall ); + furn_set( 23, i, f_null ); + i_clear( tripoint( 23, i, get_abs_sub().z )); + + ter_set( i, 23, t_concrete_wall ); + furn_set( i, 23, f_null ); + i_clear( tripoint( i, 23, get_abs_sub().z )); + + if( lw == 2 ) { + ter_set( 0, i, t_concrete_wall ); + furn_set( 0, i, f_null ); + i_clear( tripoint( 0, i, get_abs_sub().z )); + } + if( tw == 2 ) { + ter_set( i, 0, t_concrete_wall ); + furn_set( i, 0, f_null ); + i_clear( tripoint( i, 0, get_abs_sub().z )); + } + if( rw != 2 ) { + ter_set( 23, 11, t_door_metal_c ); + ter_set( 23, 12, t_door_metal_c ); + } + if( bw != 2 ) { + ter_set( 11, 23, t_door_metal_c ); + ter_set( 12, 23, t_door_metal_c ); + } + } + } + } else { // then weighted roll was in the hardcoded section + use_hardcoded_finale_map = true; + } // end json maps + } else { // then no json maps for lab_finale_1level were found + use_hardcoded_finale_map = true; + } // end if no lab_4side was found. + + if (use_hardcoded_finale_map) { + // Start by setting up a large, empty room. + for (int i = 0; i < SEEX * 2; i++) { + for (int j = 0; j < SEEY * 2; j++) { + if (i < lw || i > SEEX * 2 - 1 - rw) { + ter_set(i, j, t_concrete_wall); + } else if (j < tw || j > SEEY * 2 - 1 - bw) { + ter_set(i, j, t_concrete_wall); + } else { + ter_set(i, j, t_rock_floor); + } } } - } - if (rw == 1) { - ter_set(SEEX * 2 - 1, SEEY - 1, t_door_metal_c); - ter_set(SEEX * 2 - 1, SEEY , t_door_metal_c); - } - if (bw == 1) { - ter_set(SEEX - 1, SEEY * 2 - 1, t_door_metal_c); - ter_set(SEEX , SEEY * 2 - 1, t_door_metal_c); - } - - int loot_variant; //only used for weapons testing variant. - switch (rng(1, 4)) { - - // Weapons testing - case 1: - loot_variant = rng(1, 100); //The variants have a 67/22/7/4 split. - add_spawn(mon_secubot, 1, 6, 6); - add_spawn(mon_secubot, 1, SEEX * 2 - 7, 6); - add_spawn(mon_secubot, 1, 6, SEEY * 2 - 7); - add_spawn(mon_secubot, 1, SEEX * 2 - 7, SEEY * 2 - 7); - spawn_item( SEEX - 4, SEEY - 2, "id_science" ); - if(loot_variant <= 96) { - mtrap_set( this, SEEX - 3, SEEY - 3, tr_dissector); - mtrap_set( this, SEEX + 2, SEEY - 3, tr_dissector); - mtrap_set( this, SEEX - 3, SEEY + 2, tr_dissector); - mtrap_set( this, SEEX + 2, SEEY + 2, tr_dissector); - line(this, t_reinforced_glass, SEEX + 1, SEEY + 1, SEEX - 2, SEEY + 1); - line(this, t_reinforced_glass, SEEX - 2, SEEY , SEEX - 2, SEEY - 2); - line(this, t_reinforced_glass, SEEX - 1, SEEY - 2, SEEX + 1, SEEY - 2); - ter_set (SEEX + 1, SEEY - 1, t_reinforced_glass); - ter_set (SEEX + 1, SEEY , t_reinforced_door_glass_c); - furn_set(SEEX - 1, SEEY - 1, f_table); - furn_set(SEEX , SEEY - 1, f_table); - furn_set(SEEX - 1, SEEY , f_table); - furn_set(SEEX , SEEY , f_table); - if (loot_variant <= 67 ) { - spawn_item(SEEX - 1, SEEY - 1, "laser_pack", dice(4, 3)); - spawn_item(SEEX , SEEY - 1, "UPS_off"); - spawn_item(SEEX , SEEY - 1, "battery", dice(4, 3)); - spawn_item(SEEX - 1, SEEY , "v29"); - spawn_item(SEEX - 1, SEEY , "laser_rifle", dice (1, 0)); - spawn_item(SEEX , SEEY , "ftk93"); - spawn_item(SEEX - 1, SEEY , "recipe_atomic_battery"); - spawn_item(SEEX , SEEY -1, "solar_panel_v3"); //quantum solar panel, 6 panels in one! - } else if (loot_variant > 67 && loot_variant < 89) { - spawn_item(SEEX - 1, SEEY - 1, "mininuke", dice(3, 6)); - spawn_item(SEEX , SEEY - 1, "mininuke", dice(3, 6)); - spawn_item(SEEX - 1, SEEY , "mininuke", dice(3, 6)); - spawn_item(SEEX , SEEY , "mininuke", dice(3, 6)); - spawn_item(SEEX , SEEY , "recipe_atomic_battery"); - spawn_item(SEEX , SEEY , "solar_panel_v3"); //quantum solar panel, 6 panels in one! - } else { // loot_variant between 90 and 96. - spawn_item(SEEX - 1, SEEY - 1, "rm13_armor"); - spawn_item(SEEX , SEEY - 1, "plut_cell"); - spawn_item(SEEX - 1, SEEY , "plut_cell"); - spawn_item(SEEX , SEEY , "recipe_caseless"); - } - } else { // 4% of the lab ends will be this weapons testing end. - mtrap_set( this, SEEX - 4, SEEY - 3, tr_dissector); - mtrap_set( this, SEEX + 3, SEEY - 3, tr_dissector); - mtrap_set( this, SEEX - 4, SEEY + 2, tr_dissector); - mtrap_set( this, SEEX + 3, SEEY + 2, tr_dissector); - - furn_set(SEEX - 2, SEEY - 1, f_rack); - furn_set(SEEX - 1, SEEY - 1, f_rack); - furn_set(SEEX , SEEY - 1, f_rack); - furn_set(SEEX + 1, SEEY - 1, f_rack); - furn_set(SEEX - 2, SEEY , f_rack); - furn_set(SEEX - 1, SEEY , f_rack); - furn_set(SEEX , SEEY , f_rack); - furn_set(SEEX + 1, SEEY , f_rack); - line(this, t_reinforced_door_glass_c, SEEX - 2, SEEY - 2, SEEX + 1, SEEY - 2); - line(this, t_reinforced_door_glass_c, SEEX - 2, SEEY + 1, SEEX + 1, SEEY + 1); - line(this, t_reinforced_glass, SEEX - 3, SEEY - 2, SEEX - 3, SEEY + 1); - line(this, t_reinforced_glass, SEEX + 2, SEEY - 2, SEEX + 2, SEEY + 1); - place_items("ammo_rare", 96, SEEX - 2, SEEY - 1, SEEX + 1, SEEY - 1, false, 0); - place_items("guns_rare", 96, SEEX - 2, SEEY, SEEX + 1, SEEY, false, 0); - spawn_item(SEEX + 1, SEEY , "solar_panel_v3"); //quantum solar panel, 6 panels in one! + if (rw == 1) { + ter_set(SEEX * 2 - 1, SEEY - 1, t_door_metal_c); + ter_set(SEEX * 2 - 1, SEEY , t_door_metal_c); + } + if (bw == 1) { + ter_set(SEEX - 1, SEEY * 2 - 1, t_door_metal_c); + ter_set(SEEX , SEEY * 2 - 1, t_door_metal_c); } - break; - // Netherworld access - case 2: { - bool monsters_end = false; - if (!one_in(4)) { // Trapped netherworld monsters - monsters_end = true; - static const std::array nethercreatures = { { - mon_flying_polyp, mon_hunting_horror, mon_mi_go, mon_yugg, mon_gelatin, - mon_flaming_eye, mon_kreck, mon_gracke, mon_blank, mon_gozu, mon_shoggoth, - } }; - tw = rng(SEEY + 3, SEEY + 5); - bw = tw + 4; - lw = rng(SEEX - 6, SEEX - 2); - rw = lw + 6; - for (int i = lw; i <= rw; i++) { - for (int j = tw; j <= bw; j++) { - if (j == tw || j == bw) { - if ((i - lw) % 2 == 0) { + int loot_variant; //only used for weapons testing variant. + switch (rng(1, 5)) { + + // Weapons testing - twice as common because it has 4 variants. + case 1: + case 2: + loot_variant = rng(1, 100); //The variants have a 67/22/7/4 split. + add_spawn(mon_secubot, 1, 6, 6); + add_spawn(mon_secubot, 1, SEEX * 2 - 7, 6); + add_spawn(mon_secubot, 1, 6, SEEY * 2 - 7); + add_spawn(mon_secubot, 1, SEEX * 2 - 7, SEEY * 2 - 7); + spawn_item( SEEX - 4, SEEY - 2, "id_science" ); + if(loot_variant <= 96) { + mtrap_set( this, SEEX - 3, SEEY - 3, tr_dissector); + mtrap_set( this, SEEX + 2, SEEY - 3, tr_dissector); + mtrap_set( this, SEEX - 3, SEEY + 2, tr_dissector); + mtrap_set( this, SEEX + 2, SEEY + 2, tr_dissector); + line(this, t_reinforced_glass, SEEX + 1, SEEY + 1, SEEX - 2, SEEY + 1); + line(this, t_reinforced_glass, SEEX - 2, SEEY , SEEX - 2, SEEY - 2); + line(this, t_reinforced_glass, SEEX - 1, SEEY - 2, SEEX + 1, SEEY - 2); + ter_set (SEEX + 1, SEEY - 1, t_reinforced_glass); + ter_set (SEEX + 1, SEEY , t_reinforced_door_glass_c); + furn_set(SEEX - 1, SEEY - 1, f_table); + furn_set(SEEX , SEEY - 1, f_table); + furn_set(SEEX - 1, SEEY , f_table); + furn_set(SEEX , SEEY , f_table); + if (loot_variant <= 67 ) { + spawn_item(SEEX - 1, SEEY - 1, "laser_pack", dice(4, 3)); + spawn_item(SEEX , SEEY - 1, "UPS_off"); + spawn_item(SEEX , SEEY - 1, "battery", dice(4, 3)); + spawn_item(SEEX - 1, SEEY , "v29"); + spawn_item(SEEX - 1, SEEY , "laser_rifle", dice (1, 0)); + spawn_item(SEEX , SEEY , "ftk93"); + spawn_item(SEEX - 1, SEEY , "recipe_atomic_battery"); + spawn_item(SEEX , SEEY -1, "solar_panel_v3"); //quantum solar panel, 6 panels in one! + } else if (loot_variant > 67 && loot_variant < 89) { + spawn_item(SEEX - 1, SEEY - 1, "mininuke", dice(3, 6)); + spawn_item(SEEX , SEEY - 1, "mininuke", dice(3, 6)); + spawn_item(SEEX - 1, SEEY , "mininuke", dice(3, 6)); + spawn_item(SEEX , SEEY , "mininuke", dice(3, 6)); + spawn_item(SEEX , SEEY , "recipe_atomic_battery"); + spawn_item(SEEX , SEEY , "solar_panel_v3"); //quantum solar panel, 6 panels in one! + } else { // loot_variant between 90 and 96. + spawn_item(SEEX - 1, SEEY - 1, "rm13_armor"); + spawn_item(SEEX , SEEY - 1, "plut_cell"); + spawn_item(SEEX - 1, SEEY , "plut_cell"); + spawn_item(SEEX , SEEY , "recipe_caseless"); + } + } else { // 4% of the lab ends will be this weapons testing end. + mtrap_set( this, SEEX - 4, SEEY - 3, tr_dissector); + mtrap_set( this, SEEX + 3, SEEY - 3, tr_dissector); + mtrap_set( this, SEEX - 4, SEEY + 2, tr_dissector); + mtrap_set( this, SEEX + 3, SEEY + 2, tr_dissector); + + furn_set(SEEX - 2, SEEY - 1, f_rack); + furn_set(SEEX - 1, SEEY - 1, f_rack); + furn_set(SEEX , SEEY - 1, f_rack); + furn_set(SEEX + 1, SEEY - 1, f_rack); + furn_set(SEEX - 2, SEEY , f_rack); + furn_set(SEEX - 1, SEEY , f_rack); + furn_set(SEEX , SEEY , f_rack); + furn_set(SEEX + 1, SEEY , f_rack); + line(this, t_reinforced_door_glass_c, SEEX - 2, SEEY - 2, SEEX + 1, SEEY - 2); + line(this, t_reinforced_door_glass_c, SEEX - 2, SEEY + 1, SEEX + 1, SEEY + 1); + line(this, t_reinforced_glass, SEEX - 3, SEEY - 2, SEEX - 3, SEEY + 1); + line(this, t_reinforced_glass, SEEX + 2, SEEY - 2, SEEX + 2, SEEY + 1); + place_items("ammo_rare", 96, SEEX - 2, SEEY - 1, SEEX + 1, SEEY - 1, false, 0); + place_items("guns_rare", 96, SEEX - 2, SEEY, SEEX + 1, SEEY, false, 0); + spawn_item(SEEX + 1, SEEY , "solar_panel_v3"); //quantum solar panel, 6 panels in one! + } + break; + + // Netherworld access + case 3: { + bool monsters_end = false; + if (!one_in(4)) { // Trapped netherworld monsters + monsters_end = true; + static const std::array nethercreatures = { { + mon_flying_polyp, mon_hunting_horror, mon_mi_go, mon_yugg, mon_gelatin, + mon_flaming_eye, mon_kreck, mon_gracke, mon_blank, mon_gozu, mon_shoggoth, + } }; + tw = rng(SEEY + 3, SEEY + 5); + bw = tw + 4; + lw = rng(SEEX - 6, SEEX - 2); + rw = lw + 6; + for (int i = lw; i <= rw; i++) { + for (int j = tw; j <= bw; j++) { + if (j == tw || j == bw) { + if ((i - lw) % 2 == 0) { + ter_set(i, j, t_concrete_wall); + } else { + ter_set(i, j, t_reinforced_glass); + } + } else if ((i - lw) % 2 == 0) { ter_set(i, j, t_concrete_wall); - } else { - ter_set(i, j, t_reinforced_glass); + } else if (j == tw + 2) { + ter_set(i, j, t_concrete_wall); + } else { // Empty space holds monsters! + const mtype_id& type = random_entry( nethercreatures ); + add_spawn(type, 1, i, j); } - } else if ((i - lw) % 2 == 0) { - ter_set(i, j, t_concrete_wall); - } else if (j == tw + 2) { - ter_set(i, j, t_concrete_wall); - } else { // Empty space holds monsters! - const mtype_id& type = random_entry( nethercreatures ); - add_spawn(type, 1, i, j); } } } - } - spawn_item( SEEX - 1, 8, "id_science" ); - tmpcomp = add_computer( tripoint( SEEX, 8, abs_sub.z ), _("Sub-prime contact console"), 7); - if(monsters_end) { //only add these options when there are monsters. - tmpcomp->add_option(_("Terminate Specimens"), COMPACT_TERMINATE, 2); - tmpcomp->add_option(_("Release Specimens"), COMPACT_RELEASE, 3); + spawn_item( SEEX - 1, 8, "id_science" ); + tmpcomp = add_computer( tripoint( SEEX, 8, abs_sub.z ), _("Sub-prime contact console"), 7); + if(monsters_end) { //only add these options when there are monsters. + tmpcomp->add_option(_("Terminate Specimens"), COMPACT_TERMINATE, 2); + tmpcomp->add_option(_("Release Specimens"), COMPACT_RELEASE, 3); + } + tmpcomp->add_option(_("Toggle Portal"), COMPACT_PORTAL, 8); + tmpcomp->add_option(_("Activate Resonance Cascade"), COMPACT_CASCADE, 10); + tmpcomp->add_failure(COMPFAIL_MANHACKS); + tmpcomp->add_failure(COMPFAIL_SECUBOTS); + ter_set(SEEX - 2, 4, t_radio_tower); + ter_set(SEEX + 1, 4, t_radio_tower); + ter_set(SEEX - 2, 7, t_radio_tower); + ter_set(SEEX + 1, 7, t_radio_tower); } - tmpcomp->add_option(_("Toggle Portal"), COMPACT_PORTAL, 8); - tmpcomp->add_option(_("Activate Resonance Cascade"), COMPACT_CASCADE, 10); - tmpcomp->add_failure(COMPFAIL_MANHACKS); - tmpcomp->add_failure(COMPFAIL_SECUBOTS); - ter_set(SEEX - 2, 4, t_radio_tower); - ter_set(SEEX + 1, 4, t_radio_tower); - ter_set(SEEX - 2, 7, t_radio_tower); - ter_set(SEEX + 1, 7, t_radio_tower); - } - break; + break; - // Bionics - case 3: { - add_spawn(mon_secubot, 1, 6, 6); - add_spawn(mon_secubot, 1, SEEX * 2 - 7, 6); - add_spawn(mon_secubot, 1, 6, SEEY * 2 - 7); - add_spawn(mon_secubot, 1, SEEX * 2 - 7, SEEY * 2 - 7); - mtrap_set( this, SEEX - 2, SEEY - 2, tr_dissector); - mtrap_set( this, SEEX + 1, SEEY - 2, tr_dissector); - mtrap_set( this, SEEX - 2, SEEY + 1, tr_dissector); - mtrap_set( this, SEEX + 1, SEEY + 1, tr_dissector); - square_furn(this, f_counter, SEEX - 1, SEEY - 1, SEEX, SEEY); - int item_count = 0; - while (item_count < 5) { - item_count += place_items( "bionics", 75, SEEX - 1, SEEY - 1, SEEX, SEEY, false, 0 ).size(); - } - line(this, t_reinforced_glass, SEEX - 2, SEEY - 2, SEEX + 1, SEEY - 2); - line(this, t_reinforced_glass, SEEX - 2, SEEY + 1, SEEX + 1, SEEY + 1); - line(this, t_reinforced_glass, SEEX - 2, SEEY - 1, SEEX - 2, SEEY); - line(this, t_reinforced_glass, SEEX + 1, SEEY - 1, SEEX + 1, SEEY); - spawn_item( SEEX - 4, SEEY - 3, "id_science" ); - ter_set(SEEX - 3, SEEY - 3, t_console); - tmpcomp = add_computer( tripoint( SEEX - 3, SEEY - 3, abs_sub.z ), _("Bionic access"), 3); - tmpcomp->add_option(_("Manifest"), COMPACT_LIST_BIONICS, 0); - tmpcomp->add_option(_("Open Chambers"), COMPACT_RELEASE, 5); - tmpcomp->add_failure(COMPFAIL_MANHACKS); - tmpcomp->add_failure(COMPFAIL_SECUBOTS); + // Bionics + case 4: { + add_spawn(mon_secubot, 1, 6, 6); + add_spawn(mon_secubot, 1, SEEX * 2 - 7, 6); + add_spawn(mon_secubot, 1, 6, SEEY * 2 - 7); + add_spawn(mon_secubot, 1, SEEX * 2 - 7, SEEY * 2 - 7); + mtrap_set( this, SEEX - 2, SEEY - 2, tr_dissector); + mtrap_set( this, SEEX + 1, SEEY - 2, tr_dissector); + mtrap_set( this, SEEX - 2, SEEY + 1, tr_dissector); + mtrap_set( this, SEEX + 1, SEEY + 1, tr_dissector); + square_furn(this, f_counter, SEEX - 1, SEEY - 1, SEEX, SEEY); + int item_count = 0; + while (item_count < 5) { + item_count += place_items( "bionics", 75, SEEX - 1, SEEY - 1, SEEX, SEEY, false, 0 ).size(); + } + line(this, t_reinforced_glass, SEEX - 2, SEEY - 2, SEEX + 1, SEEY - 2); + line(this, t_reinforced_glass, SEEX - 2, SEEY + 1, SEEX + 1, SEEY + 1); + line(this, t_reinforced_glass, SEEX - 2, SEEY - 1, SEEX - 2, SEEY); + line(this, t_reinforced_glass, SEEX + 1, SEEY - 1, SEEX + 1, SEEY); + spawn_item( SEEX - 4, SEEY - 3, "id_science" ); + ter_set(SEEX - 3, SEEY - 3, t_console); + tmpcomp = add_computer( tripoint( SEEX - 3, SEEY - 3, abs_sub.z ), _("Bionic access"), 3); + tmpcomp->add_option(_("Manifest"), COMPACT_LIST_BIONICS, 0); + tmpcomp->add_option(_("Open Chambers"), COMPACT_RELEASE, 5); + tmpcomp->add_failure(COMPFAIL_MANHACKS); + tmpcomp->add_failure(COMPFAIL_SECUBOTS); + } + break; + + // CVD Forge + case 5: + add_spawn(mon_secubot, 1, 6, 6); + add_spawn(mon_secubot, 1, SEEX * 2 - 7, 6); + add_spawn(mon_secubot, 1, 6, SEEY * 2 - 7); + add_spawn(mon_secubot, 1, SEEX * 2 - 7, SEEY * 2 - 7); + line(this, t_cvdbody, SEEX - 2, SEEY - 2, SEEX - 2, SEEY + 1); + line(this, t_cvdbody, SEEX - 1, SEEY - 2, SEEX - 1, SEEY + 1); + line(this, t_cvdbody, SEEX , SEEY - 1, SEEX , SEEY + 1); + line(this, t_cvdbody, SEEX + 1, SEEY - 2, SEEX + 1, SEEY + 1); + ter_set(SEEX , SEEY - 2, t_cvdmachine); + spawn_item( SEEX, SEEY - 3, "id_science" ); + break; } - break; - // CVD Forge - case 4: - add_spawn(mon_secubot, 1, 6, 6); - add_spawn(mon_secubot, 1, SEEX * 2 - 7, 6); - add_spawn(mon_secubot, 1, 6, SEEY * 2 - 7); - add_spawn(mon_secubot, 1, SEEX * 2 - 7, SEEY * 2 - 7); - line(this, t_cvdbody, SEEX - 2, SEEY - 2, SEEX - 2, SEEY + 1); - line(this, t_cvdbody, SEEX - 1, SEEY - 2, SEEX - 1, SEEY + 1); - line(this, t_cvdbody, SEEX , SEEY - 1, SEEX , SEEY + 1); - line(this, t_cvdbody, SEEX + 1, SEEY - 2, SEEX + 1, SEEY + 1); - ter_set(SEEX , SEEY - 2, t_cvdmachine); - spawn_item( SEEX, SEEY - 3, "id_science" ); - break; + } // end use_hardcoded_lab_finale + + // Handle stairs in the unlikely case they are needed. + if (t_above == "lab_stairs" || t_above == "ice_lab_stairs") { + if( const auto p = random_point( points_in_rectangle( { lw, tw, abs_sub.z }, { SEEX * 2 - 1 - rw, SEEY * 2 - 1 - bw, abs_sub.z } ), [this]( const tripoint &n ) { return ter( n ) == t_rock_floor; } ) ) { + ter_set( *p, t_stairs_up ); + } + } + if (terrain_type == "lab_stairs" || terrain_type == "ice_lab_stairs") { + if( const auto p = random_point( points_in_rectangle( { lw, tw, abs_sub.z }, { SEEX * 2 - 1 - rw, SEEY * 2 - 1 - bw, abs_sub.z } ), [this]( const tripoint &n ) { return ter( n ) == t_rock_floor; } ) ) { + ter_set( *p, t_stairs_down ); + } } // Chance of adding occasional lighting through the finale room.