From c0c8ca6eb6426eb64e0a1c086ef834369d6788d7 Mon Sep 17 00:00:00 2001 From: "C.L" Date: Fri, 30 Sep 2022 20:50:10 -0400 Subject: [PATCH] god has left us MAJOR: - Completely revamps the blueprints. - Ports /tg/ blueprints and bastardizes them. Blueprint functions: - Create/modify an existing area (in a 3x3 square. Good for expanding an area to include walls or finetuning) - Create new area or merge two areas. (Allows you to make a new area if the room is airtight or merge two areas together.) - Change area name. - Wire Legend. Allows you to see what wires do. (Move this into its own seperate item) MINOR: - Adds a new global list "wire_name_directory" that is important for revealing wires. - Gave vent pumps, vent scrubbers, and air alarms a new update_area proc that is involved when their area is updated due to a blueprints being used. - Gave areas a 'setup' proc that is called when a new area is created via blueprints. Sets the area vars to what would be default (power off) Adds a new 'areasize' variable to areas that lets you know how large they are. - Added procs to areas to check the power in an area and check change the name of an area properly. - adds a new range_turfs and rect_turfs define. Unused for now. --- .../components/unary/vent_pump.dm | 7 + .../components/unary/vent_scrubber.dm | 6 + code/__defines/turfs.dm | 11 + code/_global_vars/lists/misc.dm | 3 +- code/datums/wires/wires.dm | 1 + code/game/area/areas_vr.dm | 41 ++ code/game/machinery/air_alarm.dm | 20 +- code/game/objects/items/blueprints_vr.dm | 695 ++++++++++++++++++ 8 files changed, 776 insertions(+), 8 deletions(-) create mode 100644 code/game/objects/items/blueprints_vr.dm diff --git a/code/ATMOSPHERICS/components/unary/vent_pump.dm b/code/ATMOSPHERICS/components/unary/vent_pump.dm index 145381146fd..4b8970fd94d 100644 --- a/code/ATMOSPHERICS/components/unary/vent_pump.dm +++ b/code/ATMOSPHERICS/components/unary/vent_pump.dm @@ -94,6 +94,13 @@ assign_uid() id_tag = num2text(uid) +/obj/machinery/atmospherics/unary/vent_pump/proc/update_area() + initial_loc = get_area(loc) + area_uid = "\ref[initial_loc]" + assign_uid() + id_tag = num2text(uid) + + /obj/machinery/atmospherics/unary/vent_pump/Destroy() unregister_radio(src, frequency) if(initial_loc) diff --git a/code/ATMOSPHERICS/components/unary/vent_scrubber.dm b/code/ATMOSPHERICS/components/unary/vent_scrubber.dm index 64a57a744e0..fe3e68130fa 100644 --- a/code/ATMOSPHERICS/components/unary/vent_scrubber.dm +++ b/code/ATMOSPHERICS/components/unary/vent_scrubber.dm @@ -43,6 +43,12 @@ assign_uid() id_tag = num2text(uid) +/obj/machinery/atmospherics/unary/vent_scrubber/proc/update_area() + initial_loc = get_area(loc) + area_uid = "\ref[initial_loc]" + assign_uid() + id_tag = num2text(uid) + /obj/machinery/atmospherics/unary/vent_scrubber/Destroy() unregister_radio(src, frequency) if(initial_loc) diff --git a/code/__defines/turfs.dm b/code/__defines/turfs.dm index 1c4b228b63c..421bfc258f4 100644 --- a/code/__defines/turfs.dm +++ b/code/__defines/turfs.dm @@ -28,3 +28,14 @@ #define OUTDOORS_NO 0 // Ditto. #define OUTDOORS_AREA -1 // If a turf has this, it will defer to the area's settings on init. // Note that after init, it will be either YES or NO. + +//supposedly the fastest way to do this according to https://gist.github.com/Giacom/be635398926bb463b42a +///Returns a list of turf in a square +#define RANGE_TURFS(RADIUS, CENTER) \ + RECT_TURFS(RADIUS, RADIUS, CENTER) + +#define RECT_TURFS(H_RADIUS, V_RADIUS, CENTER) \ + block( \ + locate(max(CENTER.x-(H_RADIUS),1), max(CENTER.y-(V_RADIUS),1), CENTER.z), \ + locate(min(CENTER.x+(H_RADIUS),world.maxx), min(CENTER.y+(V_RADIUS),world.maxy), CENTER.z) \ + ) \ No newline at end of file diff --git a/code/_global_vars/lists/misc.dm b/code/_global_vars/lists/misc.dm index a16573905e0..97cc3753a1e 100644 --- a/code/_global_vars/lists/misc.dm +++ b/code/_global_vars/lists/misc.dm @@ -2,7 +2,8 @@ GLOBAL_LIST_INIT(speech_toppings, list("|" = "i", "+" = "b", "_" = "u")) GLOBAL_LIST_EMPTY(meteor_list) /// List of wire colors for each object type of that round. One for airlocks, one for vendors, etc. -GLOBAL_LIST_EMPTY(wire_color_directory) // This is an associative list with the `holder_type` as the key, and a list of colors as the value. +GLOBAL_LIST_EMPTY(wire_color_directory) // This is an associative list with the `holder_type` as the key, and a list of colors as the value. +GLOBAL_LIST_EMPTY(wire_name_directory) // This is an associative list // Reference list for disposal sort junctions. Filled up by sorting junction's New() GLOBAL_LIST_EMPTY(tagger_locations) diff --git a/code/datums/wires/wires.dm b/code/datums/wires/wires.dm index 83ebb65fa8f..9a411c60ee3 100644 --- a/code/datums/wires/wires.dm +++ b/code/datums/wires/wires.dm @@ -43,6 +43,7 @@ if(!GLOB.wire_color_directory[holder_type]) randomize() GLOB.wire_color_directory[holder_type] = colors + GLOB.wire_name_directory[holder_type] = proper_name else colors = GLOB.wire_color_directory[holder_type] diff --git a/code/game/area/areas_vr.dm b/code/game/area/areas_vr.dm index e356063e2ab..f9326745660 100644 --- a/code/game/area/areas_vr.dm +++ b/code/game/area/areas_vr.dm @@ -5,6 +5,9 @@ var/block_suit_sensors = FALSE //If mob size is limited in the area. var/turf/ceiling_type + // Size of the area in open turfs, only calculated for indoors areas. + var/areasize = 0 + /area/Entered(var/atom/movable/AM, oldLoc) . = ..() if(enter_message && isliving(AM)) @@ -29,3 +32,41 @@ var/turf/TA = GetAbove(T) if(isopenspace(TA)) TA.ChangeTurf(ceiling_type, TRUE, TRUE, TRUE) + +/** + * Setup an area (with the given name) + * + * Sets the area name, sets all status var's to false and adds the area to the sorted area list + * //NOTE: Virgo does not have a sorted area list. + */ +/area/proc/setup(a_name) + name = a_name + power_equip = FALSE + power_light = FALSE + power_environ = FALSE + always_unpowered = FALSE + update_areasize() + +/area/proc/update_areasize() + if(outdoors) + return FALSE + areasize = 0 + for(var/turf/simulated/floor/T in contents) + areasize++ + +/proc/rename_area(a, new_name) + var/area/A = get_area(a) + var/prevname = "[A.name]" + set_area_machinery(A, new_name, prevname) + A.name = new_name + A.update_areasize() + return TRUE + +/area/proc/power_check() + if(!requires_power || !apc) + power_light = 0 + power_equip = 0 + power_environ = 0 + power_change() // all machines set to current power level, also updates lighting icon + if(no_spoilers) + set_spoiler_obfuscation(TRUE) \ No newline at end of file diff --git a/code/game/machinery/air_alarm.dm b/code/game/machinery/air_alarm.dm index ca40f0dc530..82d6fa0f6f4 100644 --- a/code/game/machinery/air_alarm.dm +++ b/code/game/machinery/air_alarm.dm @@ -154,6 +154,12 @@ update_icon() +/obj/machinery/alarm/proc/update_area() + alarm_area = get_area(src) + area_uid = "\ref[alarm_area]" + if(name == "alarm") + name = "[alarm_area.name] Air Alarm" + /obj/machinery/alarm/Initialize() . = ..() set_frequency(frequency) @@ -540,9 +546,9 @@ var/list/list/environment_data = list() data["environment_data"] = environment_data - + DECLARE_TLV_VALUES - + var/pressure = environment.return_pressure() LOAD_TLV_VALUES(TLV["pressure"], pressure) environment_data.Add(list(list( @@ -551,7 +557,7 @@ "unit" = "kPa", "danger_level" = TEST_TLV_VALUES ))) - + var/temperature = environment.temperature LOAD_TLV_VALUES(TLV["temperature"], temperature) environment_data.Add(list(list( @@ -573,7 +579,7 @@ "unit" = "%", "danger_level" = TEST_TLV_VALUES ))) - + if(!locked || issilicon(user) || data["remoteUser"]) var/list/list/vents = list() data["vents"] = vents @@ -595,7 +601,7 @@ "extdefault"= (info["external"] == ONE_ATMOSPHERE), "intdefault"= (info["internal"] == 0), ))) - + var/list/list/scrubbers = list() data["scrubbers"] = scrubbers @@ -622,7 +628,7 @@ data["scrubbers"] = scrubbers data["mode"] = mode - + var/list/list/modes = list() data["modes"] = modes modes[++modes.len] = list("name" = "Filtering - Scrubs out contaminants", "mode" = AALARM_MODE_SCRUBBING, "selected" = mode == AALARM_MODE_SCRUBBING, "danger" = 0) @@ -682,7 +688,7 @@ else target_temperature = input_temperature + T0C return TRUE - + // Account for remote users here. // Yes, this is kinda snowflaky; however, I would argue it would be far more snowflakey // to include "custom hrefs" and all the other bullshit that nano states have just for the diff --git a/code/game/objects/items/blueprints_vr.dm b/code/game/objects/items/blueprints_vr.dm new file mode 100644 index 00000000000..16a8f71fee6 --- /dev/null +++ b/code/game/objects/items/blueprints_vr.dm @@ -0,0 +1,695 @@ +#define BP_MAX_ROOM_SIZE 300 + +// WARNING: ESOTERIC BULLSHIT INSIDE OF THIS FILE. +// This is a port of /tg/'s blueprints that also have Virgo modifications as well. +// However it is heavily modified and lacking the 't-ray' scanner functionality that /TG/ has. We have our own t-rays after all. +// This works and uses a bunch of really odd hacks and trickery to get it to all function. +// If you're looking at this a few years from now and going 'What the hell were they thinking' just know that this was the best we had at the time. + +// Now that I've scared away half the people looking at this file, here's the relevant info: + +// Banning areas: Go to /obj/item/areaeditor/proc/get_area_type and /proc/create_area and add the /area to: (area_or_turf_fail_types) +// That will bar people from doing ANYTHING to those areas. No creating inside of them. No merging into them. Etc. + +// Disallow creation but allow merge/expansion: Go to the same two above again. +// Add the /area to 'blacklisted_areas' in /proc/create_area + + +/area/tether/surfacebase/outside + name = "Outside - Surface" + +/area/groundbase/unexplored/outdoors + name = "\improper Rascal's Pass" + +/area/groundbase/mining + name = "Mining" + +/area/groundbase/unexplored/rock + name = "\improper Rascal's Pass" + +/area/maintenance/groundbase/level1 + name = "Groundbase Level One Maint" + +/area/submap/groundbase/wilderness + name = "Groundbase Wilderness" + +//TG blueprints. +#define AREA_ERRNONE 0 +#define AREA_STATION 1 +#define AREA_SPACE 2 +#define AREA_SPECIAL 3 + +/obj/item/areaeditor + name = "area modification item" + icon = 'icons/obj/items.dmi' + icon_state = "blueprints" + attack_verb = list("attacked", "bapped", "hit") + in_use = FALSE + preserve_item = 1 + var/uses_charges = 0 // If the area editor has limited uses. + var/charges = 0 // The amount of uses the area editor has. + + var/can_create_areas_in = AREA_SPACE // Must be standing in space to create + var/can_create_areas_into = AREA_SPACE // New areas will only overwrite space area turfs. + var/can_expand_areas_in = AREA_STATION // Must be standing in station to expand + var/can_expand_areas_into = AREA_SPACE // Can expand station areas only into space. + var/can_rename_areas_in = AREA_STATION // Only station areas can be reanamed + + + var/const/ROOM_ERR_LOLWAT = 0 // Don't touch these three consts or BYOND will literally tear out your throat + var/const/ROOM_ERR_SPACE = -1 + var/const/ROOM_ERR_TOOLARGE = -2 + var/const/ROOM_ERR_FORBIDDEN = -3 + + var/list/areaColor_turfs = list() + var/legend = 0 //If viewing wires or not. + + //Allows you to build new areas in these areas easily. + var/static/list/BUILDABLE_AREA_TYPES = list( + /area/space, + /area/mine, + //TETHER STUFF BELOW THIS + /area/tether/surfacebase/outside, + //GROUNDBASE STUFF BELOW THIS + /area/groundbase/unexplored/outdoors, + /area/maintenance/groundbase/level1, + /area/submap/groundbase/wilderness, + /area/groundbase/mining + ) + //Forbids you from doing anything to these areas. + var/static/list/SPECIALS = list( + /turf/space, + /area/shuttle, + /area/admin, + /area/arrival, + /area/centcom, + /area/asteroid, + /area/tdome, + /area/syndicate_station, + /area/wizard_station, + /area/prison, + /area/holodeck, + /area/turbolift + // /area/derelict //commented out, all hail derelict-rebuilders! + ) + + + +/obj/item/areaeditor/attack_self(mob/user) //Convert this to TGUI sometime to disallow browser exploits. + add_fingerprint(user) + . = "[src] \ +

[station_name()] [src.name]

" + switch(get_area_type()) + if(AREA_SPACE) + . += "

According to the [src.name], you are now in an unclaimed territory.

" + if(AREA_SPECIAL) + . += "

This place is not noted on the [src.name].

" + return //If we're in a special area, no modifying. + . += "

Create or modify an existing area (3x3 space)

" + . += "

Create new area or merge two areas. (Whole Room)

" + . += "There is a note on the corner of the [src.name]: Use 3x3 for fine-tuning and including walls into your area!" + + +/obj/item/areaeditor/Topic(href, href_list) + if(..()) + return TRUE + if ((usr.restrained() || usr.stat || usr.get_active_hand() != src)) + return + if(href_list["create_area"]) + if(in_use) + return + var/area/A = get_area(usr) + if(A.flags & BLUE_SHIELDED) + to_chat(usr, span_warning("You cannot edit restricted areas.")) + return + in_use = TRUE + create_area(usr) + in_use = FALSE + if(href_list["create_area_whole"]) + if(in_use) + return + in_use = TRUE + var/area/A = create_area_whole(usr) + if(A && (A.flags & BLUE_SHIELDED)) + to_chat(usr, span_warning("You cannot edit restricted areas.")) + in_use = FALSE + return + in_use = FALSE + updateUsrDialog() + +//Station blueprints!!! +/obj/item/areaeditor/blueprints + name = "station blueprints" + desc = "Blueprints of the station. There is a \"Classified\" stamp and several coffee stains on it." + var/list/image/showing = list() + var/client/viewing + + +/obj/item/areaeditor/blueprints/Destroy() + //clear_viewer() + return ..() + + +/obj/item/areaeditor/blueprints/attack_self(mob/user) + . = ..() + var/area/A = get_area(user) + if(!legend) + if(get_area_type() == AREA_STATION) + . += "

According to \the [src], you are now in \"[html_encode(A.name)]\".

" + . += "

Change area name

" + . += "

View wire colour legend

" + //if(!viewing) + // . += "

View structural data

" + //else + // . += "

Refresh structural data

" + // . += "

Hide structural data

" + else + if(legend == TRUE) + . += "<< Back" + . += view_wire_devices(user); + else + //legend is a wireset + . += "<< Back" + . += view_wire_set(user, legend) + var/datum/browser/popup = new(user, "blueprints", "[src]", 700, 500) + popup.set_content(.) + popup.open() + onclose(user, "blueprints") + + +/obj/item/areaeditor/blueprints/Topic(href, href_list) + if(..()) + return + if(href_list["edit_area"]) + if(get_area_type()!=AREA_STATION) + return + if(in_use) + return + in_use = TRUE + edit_area() + in_use = FALSE + if(href_list["exit_legend"]) + legend = FALSE; + if(href_list["view_legend"]) + legend = TRUE; + if(href_list["view_wireset"]) + legend = href_list["view_wireset"]; + /* + if(href_list["view_blueprints"]) + set_viewer(usr, span_notice("You flip the blueprints over to view the complex information diagram.")) + if(href_list["hide_blueprints"]) + clear_viewer(usr,span_notice("You flip the blueprints over to view the simple information diagram.")) + if(href_list["refresh"]) + clear_viewer(usr) + set_viewer(usr) + */ + attack_self(usr) //this is not the proper way, but neither of the old update procs work! it's too ancient and I'm tired shush. + + +//Code for viewing pipes or whatnot. Think t-ray scanner. +//Code for viewing pipes or whatnot. Think t-ray scanner. +//Code for viewing pipes or whatnot. Think t-ray scanner. +/* +/obj/item/areaeditor/blueprints/proc/get_images(turf/central_turf, viewsize) + . = list() + var/list/dimensions = getviewsize(viewsize) + var/horizontal_radius = dimensions[1] / 2 + var/vertical_radius = dimensions[2] / 2 + for(var/turf/nearby_turf as anything in RECT_TURFS(horizontal_radius, vertical_radius, central_turf)) + if(nearby_turf.blueprint_data) + . += nearby_turf.blueprint_data +*/ +/* +/obj/item/areaeditor/blueprints/proc/set_viewer(mob/user, message = "") + if(user?.client) + if(viewing) + clear_viewer() + viewing = user.client + showing = get_images(get_turf(viewing.eye || user), viewing.view) + viewing.images |= showing + if(message) + to_chat(user, message) +*/ +/* +/obj/item/areaeditor/blueprints/proc/clear_viewer(mob/user, message = "") + if(viewing) + viewing.images -= showing + viewing = null + showing.Cut() + if(message) + to_chat(user, message) +*/ +/obj/item/areaeditor/blueprints/dropped(mob/user) + ..() + //clear_viewer() + if(areaColor_turfs.len) + seeAreaColors_remove() + legend = FALSE + + + +/obj/item/areaeditor/proc/get_area_type(area/A) + if (!A) + A = get_area(usr) + if(A.outdoors) + return AREA_SPACE + + for (var/type in BUILDABLE_AREA_TYPES) + if ( istype(A,type) ) + return AREA_SPACE + + for (var/type in SPECIALS) + if ( istype(A,type) ) + return AREA_SPECIAL + return AREA_STATION + + +/obj/item/areaeditor/blueprints/proc/view_wire_devices(mob/user) + var/message = "
You examine the wire legend.
" + for(var/wireset in GLOB.wire_color_directory) + //if(istype(wireset,/datum/wires/grid_checker))//Uncomment this in if you want the grid checker minigame to not be revealed here. + // continue + message += "
[GLOB.wire_name_directory[wireset]]" + message += "

" + return message + +/obj/item/areaeditor/blueprints/proc/view_wire_set(mob/user, wireset) + //for some reason you can't use wireset directly as a derefencer so this is the next best :/ + for(var/device in GLOB.wire_color_directory) + if("[device]" == wireset) //I know... don't change it... + var/message = "

[GLOB.wire_name_directory[device]]:" + for(var/Col in GLOB.wire_color_directory[device]) + var/wire_name = GLOB.wire_color_directory[device][Col] + if(!findtext(wire_name, WIRE_DUD_PREFIX)) //don't show duds + message += "

[Col]: [wire_name]

" + message += "

" + return message + return "" + + +/obj/item/areaeditor/proc/edit_area() + var/area/A = get_area(usr) + var/prevname = "[A.name]" + var/str = tgui_input_text(usr, "New area name", "Area Creation", max_length = MAX_NAME_LEN) + str = sanitize(str,MAX_NAME_LEN) + if(!str || !length(str) || str==prevname) //cancel + return + if(length(str) > 50) + to_chat(usr, span_warning("The given name is too long. The area's name is unchanged.")) + return + + rename_area(A, str) + + to_chat(usr, span_notice("You rename the '[prevname]' to '[str]'.")) + log_and_message_admins("has changed the area '[prevname]' title to '[str]'.") + A.update_areasize() + interact() + return TRUE + +//Blueprint Subtypes + +/obj/item/areaeditor/blueprints/cyborg + name = "station schematics" + desc = "A digital copy of the station blueprints stored in your memory." + + +/proc/set_area_machinery(area/area, title, oldtitle) + if(!oldtitle) // or replacetext goes to infinite loop + return + for(var/obj/machinery/alarm/airpanel in area) + airpanel.name = replacetext(airpanel.name,oldtitle,title) + airpanel.update_area() + for(var/obj/machinery/power/apc/apcpanel in area) + apcpanel.name = replacetext(apcpanel.name,oldtitle,title) + apcpanel.update_area() //DECIDE IF THIS IS WANTED OR NOT. This can mean that the APC will overwrite the current APC the area being expanded has since areas cant have multiple APCs. + for(var/obj/machinery/atmospherics/unary/vent_scrubber/scrubber in area) + scrubber.name = replacetext(scrubber.name,oldtitle,title) + scrubber.update_area() + for(var/obj/machinery/atmospherics/unary/vent_pump/vent in area) + vent.name = replacetext(vent.name,oldtitle,title) + vent.update_area() + for(var/obj/machinery/door/door in area) + door.name = replacetext(door.name,oldtitle,title) + for(var/obj/machinery/firealarm/firepanel in area) + firepanel.name = replacetext(firepanel.name,oldtitle,title) + area.update_areasize() + //TODO: much much more. Unnamed airlocks, cameras, etc. + +/proc/detect_room(turf/origin, list/break_if_found, max_size=INFINITY) + if(origin.blocks_air) + return list(origin) + + . = list() + var/list/checked_turfs = list() + var/list/found_turfs = list(origin) + while(length(found_turfs)) + var/turf/sourceT = found_turfs[1] + found_turfs.Cut(1, 2) + var/dir_flags = checked_turfs[sourceT] + for(var/dir in GLOB.alldirs) + if(length(.) > max_size) + return + if(dir_flags & dir) // This means we've checked this dir before, probably from the other turf + continue + var/turf/checkT = get_step(sourceT, dir) + if(!checkT) + continue + + checked_turfs[sourceT] |= dir + checked_turfs[checkT] |= turn(dir, 180) + .[sourceT] |= dir + .[checkT] |= turn(dir, 180) + if(break_if_found[checkT.type] || break_if_found[checkT.loc.type]) + return FALSE + + //BEGIN ESOTERIC BULLSHIT + //log_debug("Origin: [origin.c_airblock(checkT)] SourceT: [sourceT.c_airblock(checkT)] 0=NB 1=AB 2=ZB, 3=B") + if(origin.c_airblock(checkT)) //If everything breaks and it doesn't want to work, turn on the above debug and check this line. C.L. 0 = not blocked. + continue + //END ESOTERIC BULLSHIT + + found_turfs += checkT // Since checkT is connected, add it to the list to be processed + if(found_turfs.len) + found_turfs += origin //If this isn't done, it just adds the 8 tiles around the user. + return found_turfs + +/proc/create_area(mob/creator) + // Passed into the above proc as list/break_if_found + // WHATEVER YOU DO, DO NOT LEAVE THE LAST THING IN THE LIST BELOW HAVE A COMMA OR EVERYTHING EVER WILL BREAK + // AND ENGINEERS ALL OVER THE WORLD WILL HARMBATON YOU + // ENSURE THE LAST AREA OR TURF LISTED IS SIMPLY "/area/clownhideout" AND NOT "/area/clownhideout," OR YOU WILL IMMEDIATELY DIE + var/static/list/area_or_turf_fail_types = typecacheof(list( + /turf/space, + /area/shuttle, + /area/admin, + /area/arrival, + /area/centcom, + /area/asteroid, + /area/tdome, + /area/syndicate_station, + /area/wizard_station, + /area/prison, + /area/holodeck, + /turf/simulated/wall/elevator, + /area/turbolift + )) + + // Ignore these areas and dont let people expand them. They can expand into them though + var/static/list/blacklisted_areas = typecacheof(list( + /area/space, + /area/mine, + //TETHER STUFF BELOW THIS + /area/tether/surfacebase/outside, + //GROUNDBASE STUFF BELOW THIS + /area/groundbase/unexplored/outdoors, + /area/maintenance/groundbase/level1, + /area/submap/groundbase/wilderness, + /area/groundbase/mining + )) + + var/list/turfs = detect_room(get_turf(creator), area_or_turf_fail_types, BP_MAX_ROOM_SIZE*2) + if(!turfs) + to_chat(creator, span_warning("The new area must have a floor and not a part of a shuttle.")) + return + if(length(turfs) > BP_MAX_ROOM_SIZE) + to_chat(creator, span_warning("The room you're in is too big. It is [length(turfs) >= BP_MAX_ROOM_SIZE *2 ? "more than 100" : ((length(turfs) / BP_MAX_ROOM_SIZE)-1)*100]% larger than allowed.")) + return + var/list/areas = list("New Area" = /area) + + for(var/i in 1 to length(turfs)) + var/area/place = get_area(turfs[i]) + if(blacklisted_areas[place.type]) + continue + if(!place.requires_power || (place.flags & BLUE_SHIELDED)) + continue // No expanding powerless rooms etc + areas[place.name] = place + + var/area_choice = tgui_input_list(creator, "Choose an area to expand or make a new area", "Area Expansion", areas) + if(isnull(area_choice)) + to_chat(creator, span_warning("No choice selected. No adjustments made.")) + return + area_choice = areas[area_choice] + + var/area/newA + var/area/oldA = get_area(get_turf(creator)) + if(!isarea(area_choice)) + var/str = tgui_input_text(creator, "New area name", "Blueprint Editing", max_length = MAX_NAME_LEN) + str = sanitize(str,MAX_NAME_LEN) + if(!str || !length(str)) //cancel + return + if(length(str) > 50) + to_chat(creator, "Name too long.") + return + for(var/area/A in world) //Check to make sure we're not making a duplicate name. Sanity. + if(A.name == str) + to_chat(creator, "An area in the world alreay has this name.") + return + newA = new area_choice + newA.setup(str) + newA.has_gravity = oldA.has_gravity + else + newA = area_choice + + for(var/i in 1 to length(turfs)) + var/turf/thing = turfs[i] + newA.contents += thing + + + set_area_machinery(newA, newA.name, oldA.name)// Change the name and area defines of all the machinery to the correct area. + oldA.power_check() //Simply makes the area turn the power off if you nicked an APC from it. + to_chat(creator, span_notice("You have created a new area, named [newA.name]. It is now weather proof, and constructing an APC will allow it to be powered.")) + return TRUE + + + +// USED FOR VARIANT ROOM CREATION. +// OLD CODE. DON'T TOUCH OR 100 RABID SQUIRRELS WILL DEVOUR YOU. +// I say old code, but it truly isn't. It's a bastardization of the new create_area code and the old create_area code. +// In essence, it does a few things: Ensure no blacklisted areas are nearby, get the nearby areas (to allow merging), and allow you to make a whole near area. +/obj/item/areaeditor/proc/create_area_whole(mob/creator) //Gets the entire enclosed space and makes a new area out of it. Can overwrite old areas. + + //TODO: Convert this and the one in create_area to a global list. + var/static/list/area_or_turf_fail_types = typecacheof(list( + /turf/space, + /area/shuttle, + /area/admin, + /area/arrival, + /area/centcom, + /area/asteroid, + /area/tdome, + /area/syndicate_station, + /area/wizard_station, + /area/prison, + /area/holodeck, + /turf/simulated/wall/elevator, + /area/turbolift + )) + + var/static/list/blacklisted_areas = typecacheof(list( + /area/space, + /area/mine, + //TETHER STUFF BELOW THIS + /area/tether/surfacebase/outside, + //GROUNDBASE STUFF BELOW THIS + /area/groundbase/unexplored/outdoors, + /area/maintenance/groundbase/level1, + /area/submap/groundbase/wilderness, + /area/groundbase/mining + )) + + var/res = detect_room_ex(get_turf(creator), can_create_areas_into, area_or_turf_fail_types) + if(!res) + to_chat(creator, span_warning("There is an area forbidden from being edited here! Use the fine-tune area creator! (3x3)")) + return + + if(!istype(res,/list)) + switch(res) + if(ROOM_ERR_SPACE) + to_chat(creator, "The new area must be completely airtight!") + return + if(ROOM_ERR_TOOLARGE) + to_chat(creator, "The new area too large!") + return + if(ROOM_ERR_FORBIDDEN) + to_chat(creator, "There is an area forbidden from being edited here!") + return + else + to_chat(creator, "Error! Please notify administration!") + return + var/list/turf/turfs = res + + var/list/areas = list("New Area" = /area) //The list of areas surrounding the user. + var/area/newA //The new area + var/area/oldA = get_area(get_turf(creator)) //The old area (area currently standing in) + var/str //What the new area is named. + + var/list/nearby_turfs_to_check = detect_room(get_turf(creator), area_or_turf_fail_types, BP_MAX_ROOM_SIZE*2) //Get the nearby areas. + + if(!nearby_turfs_to_check) + to_chat(creator, span_warning("The new area must have a floor and not a part of a shuttle.")) + return + if(length(turfs) > BP_MAX_ROOM_SIZE) + to_chat(creator, span_warning("The room you're in is too big. It is [length(turfs) >= BP_MAX_ROOM_SIZE *2 ? "more than 100" : ((length(turfs) / BP_MAX_ROOM_SIZE)-1)*100]% larger than allowed.")) + return + + for(var/i in 1 to length(nearby_turfs_to_check)) + var/area/place = get_area(nearby_turfs_to_check[i]) + if(blacklisted_areas[place.type]) + if(!creator.lastarea != place) //Stops them from merging a blacklisted area to make it larger. Allows them to merge a blacklisted area into an allowed area. (Expansion!) + continue + if(!place.requires_power || (place.flags & BLUE_SHIELDED)) + continue // No expanding powerless rooms etc + areas[place.name] = place + + var/area_choice = tgui_input_list(creator, "Choose an area to merge into the area you are currently standing on OR make a new area..", "Area Expansion", areas) + if(isnull(area_choice)) //They pressed cancel. + to_chat(creator, "No changes made.") + return + + area_choice = areas[area_choice] + + if(!isarea(area_choice)) //They chose "New Area" + str = tgui_input_text(creator, "New area name", "Blueprint Editing", max_length = MAX_NAME_LEN) + str = sanitize(str,MAX_NAME_LEN) + if(!str || !length(str)) //cancel + return + if(length(str) > 50) + to_chat(creator, "Name too long.") + return + for(var/area/A in world) //Check to make sure we're not making a duplicate name. Sanity. + if(A.name == str) + to_chat(creator, "An area in the world alreay has this name.") + return + newA = new area_choice + newA.setup(str) + newA.has_gravity = oldA.has_gravity + else + newA = area_choice + + if(str) //New area, new name. + newA.setup(str) + else + newA.setup(newA.name) + move_turfs_to_area(turfs, newA) + newA.has_gravity = oldA.has_gravity + set_area_machinery(newA, newA.name, oldA.name) + oldA.power_check() //Simply makes the area turn the power off if you nicked an APC from it. + to_chat(creator, span_notice("You have created a new area, named [newA.name]. It is now weather proof, and constructing an APC will allow it to be powered.")) + + + spawn(5) + interact() + return + +/obj/item/areaeditor/proc/move_turfs_to_area(var/list/turf/turfs, var/area/A) + for(var/T in turfs) + ChangeArea(T, A) + + +/obj/item/areaeditor/proc/detect_room_ex(var/turf/first, var/allowedAreas = AREA_SPACE, list/forbiddenAreas) + if(!istype(first)) + return ROOM_ERR_LOLWAT + if(forbiddenAreas[first.loc.type] || forbiddenAreas[first.type]) //Is the area of the starting turf a banned area? Is the turf a banned area? + return ROOM_ERR_FORBIDDEN + var/list/turf/found = new + var/list/turf/pending = list(first) + while(pending.len) + if (found.len+pending.len > BP_MAX_ROOM_SIZE) + return ROOM_ERR_TOOLARGE + var/turf/T = pending[1] //why byond havent list::pop()? + pending -= T + for (var/dir in cardinal) + var/turf/NT = get_step(T,dir) + if (!isturf(NT) || (NT in found) || (NT in pending)) + continue + if(forbiddenAreas[NT.loc.type]) + return ROOM_ERR_FORBIDDEN + // We ask ZAS to determine if its airtight. Thats what matters anyway right? + if(air_master.air_blocked(T, NT)) + // Okay thats the edge of the room + if(get_area_type(NT.loc) == AREA_SPACE && air_master.air_blocked(NT, NT)) + found += NT // So we include walls/doors not already in any area + continue + if (istype(NT, /turf/space)) + return ROOM_ERR_SPACE //omg hull breach we all going to die here + if (istype(NT, /turf/simulated/shuttle)) + return ROOM_ERR_SPACE // Unsure why this, but was in old code. Trusting for now. + if (NT.loc != first.loc && !(get_area_type(NT.loc) & allowedAreas)) + // Edge of a protected area. Lets stop here... + continue + if (!istype(NT, /turf/simulated)) + // Great, unsimulated... eh, just stop searching here + continue + // Okay, NT looks promising, lets continue the search there! + pending += NT + found += T + // end while + return found + + + + + + + +//Nice verbs for the engineer to see where areas start/end. + +/obj/item/areaeditor/verb/seeRoomColors() + set src in usr + set category = "Blueprints" + set name = "Show Room Colors" + + // If standing somewhere we can expand from, use expand perms, otherwise create + var/canOverwrite = (get_area_type() & can_expand_areas_in) ? can_expand_areas_into : can_create_areas_into + var/res = detect_room_ex(get_turf(usr), canOverwrite) + if(!istype(res, /list)) + switch(res) + if(ROOM_ERR_SPACE) + to_chat(usr, "The new area must be completely airtight!") + return + if(ROOM_ERR_TOOLARGE) + to_chat(usr, "The new area too large!") + return + else + to_chat(usr, "Error! Please notify administration!") + return + // Okay we got a room, lets color it + seeAreaColors_remove() + var/icon/green = new('icons/misc/debug_group.dmi', "green") + for(var/turf/T in res) + usr << image(green, T, "blueprints", TURF_LAYER) + areaColor_turfs += T + to_chat(usr, "The space covered by the new area is highlighted in green.") + +/obj/item/areaeditor/verb/seeAreaColors() + set src in usr + set category = "Blueprints" + set name = "Show Area Colors" + + // Remove any existing + seeAreaColors_remove() + + to_chat(usr, "\The [src] shows nearby areas in different colors.") + var/i = 0 + for(var/area/A in range(usr)) + if(get_area_type(A) == AREA_SPACE) + continue // Don't overlay all of space! + var/icon/areaColor = new('icons/misc/debug_rebuild.dmi', "[++i]") + to_chat(usr, "- [A] as [i]") + for(var/turf/T in A.contents) + usr << image(areaColor, T, "blueprints", TURF_LAYER) + areaColor_turfs += T + +/obj/item/areaeditor/verb/seeAreaColors_remove() + set src in usr + set category = "Blueprints" + set name = "Remove Area Colors" + + areaColor_turfs.Cut() + if(usr.client.images.len) + for(var/image/i in usr.client.images) + if(i.icon_state == "blueprints") + usr.client.images.Remove(i) + + +#undef BP_MAX_ROOM_SIZE \ No newline at end of file