Blueprints Rework (#18947)

Reworks blueprints to use an eye component for area selection and
changing. Blueprints will now work on whichever overmap site they spawn
on if overmap is enabled, though currently they have only been added to
the Horizon.

Adds shuttle-modifying blueprints for altering shuttle areas and
exoplanet outpost blueprints for creating areas on exoplanets. A set of
outpost blueprints and a lockbox containing blueprints for the Horizon's
shuttles have been added to the CE's locker.

Moves eye creation to a component.

Ported from:
https://github.com/NebulaSS13/Nebula/pull/465
https://github.com/NebulaSS13/Nebula/pull/564
https://github.com/NebulaSS13/Nebula/pull/3046
This commit is contained in:
RustingWithYou
2024-04-20 09:56:26 +12:00
committed by GitHub
parent ab23fbdb70
commit 6957bc34f1
29 changed files with 810 additions and 302 deletions
+4
View File
@@ -424,6 +424,9 @@
#include "code\datums\components\local_network.dm"
#include "code\datums\components\armor\armor.dm"
#include "code\datums\components\base_name\base_name.dm"
#include "code\datums\components\eye\_eye.dm"
#include "code\datums\components\eye\blueprints.dm"
#include "code\datums\components\eye\freelook.dm"
#include "code\datums\components\multitool\_multitool.dm"
#include "code\datums\components\multitool\multitool.dm"
#include "code\datums\components\multitool\circuitboards\circuitboards.dm"
@@ -2458,6 +2461,7 @@
#include "code\modules\mob\abstract\freelook\ai\chunk.dm"
#include "code\modules\mob\abstract\freelook\ai\eye.dm"
#include "code\modules\mob\abstract\freelook\ai\update_triggers.dm"
#include "code\modules\mob\abstract\freelook\blueprints\blueprints.dm"
#include "code\modules\mob\abstract\new_player\character_traits.dm"
#include "code\modules\mob\abstract\new_player\login.dm"
#include "code\modules\mob\abstract\new_player\logout.dm"
+1
View File
@@ -135,6 +135,7 @@
#define AREA_FLAG_PRISON BITFLAG(6) // Marks prison area for purposes of checking if brigged/imprisoned
#define AREA_FLAG_NO_GHOST_TELEPORT_ACCESS BITFLAG(7) // Marks whether ghosts should not have teleport access to this area
#define AREA_FLAG_INDESTRUCTIBLE_TURFS BITFLAG(8) //Marks whether or not turfs in this area can be destroyed by explosions
#define AREA_FLAG_IS_BACKGROUND BITFLAG(9) //Marks whether or not blueprints can create areas on top of this area
// Convoluted setup so defines can be supplied by Bay12 main server compile script.
// Should still work fine for people jamming the icons into their repo.
+2
View File
@@ -104,6 +104,8 @@
#define LAZYISIN(L, I) (L ? (I in L) : FALSE)
#define LAZYDISTINCTADD(L, I) if(!L) { L = list(); } L |= I;
#define LAZYREPLACEKEY(L, K, NK) if(L) { if(L[K]) { L[NK] = L[K] } else {L += NK} L -= K; }
///Inserts an item into the list at position X, if the list is null it will initialize it
#define LAZYINSERT(L, I, X) if(!L) { L = list(); } L.Insert(X, I);
// Shims for some list procs in lists.dm.
#define isemptylist(L) (!LAZYLEN(L))
+9
View File
@@ -83,6 +83,9 @@
return B.ClickOn(A, params)
var/list/modifiers = params2list(params)
if(modifiers["right"])
RightClickOn(A)
return TRUE
if(modifiers["shift"] && modifiers["ctrl"])
CtrlShiftClickOn(A)
return TRUE
@@ -306,6 +309,12 @@
/mob/proc/TurfAdjacent(var/turf/T)
return T.AdjacentQuick(src)
/mob/proc/RightClickOn(atom/A)
A.RightClick(src)
/atom/proc/RightClick(mob/user)
return
/*
Control+Shift click
Unused except for AI
+31
View File
@@ -10,6 +10,12 @@
#define AB_CHECK_ALIVE 8
#define AB_CHECK_INSIDE 16
///Eye action targets the parent datum.
#define PARENT_TARGET 0
///Eye action targets the eye mob itself.
#define EYE_TARGET 1
///Eye action targets the eye component.
#define COMPONENT_TARGET 2
/datum/action
var/name = "Generic Action"
@@ -288,6 +294,31 @@
to_chat(usr, SPAN_NOTICE("You press the button on the exterior of \the [target_clothing]."))
target_clothing.action_circuit.activate_pin(1)
/datum/action/eye
action_type = AB_GENERIC
check_flags = AB_CHECK_LYING|AB_CHECK_STUNNED
///The type of /mob/abstract/eye used by the action.
var/eye_type = /mob/abstract/eye
///The relevant owner of the proc to be called by the eye action.
var/target_type = PARENT_TARGET
/datum/action/eye/New(var/datum/component/eye/eye_component)
if(!istype(eye_component))
crash_with("Attempted to generate eye action [src], but no eye component was provided!")
switch(target_type)
if(PARENT_TARGET)
return ..(eye_component.parent)
if(EYE_TARGET)
return ..(eye_component.component_eye)
if(COMPONENT_TARGET)
return ..(eye_component)
else
crash_with("Attempted to generate eye action [src] but an improper target_type ([target_type]) was defined.")
/datum/action/eye/CheckRemoval(mob/living/user)
if(!user.eyeobj || !istype(user.eyeobj, eye_type))
return TRUE
#undef AB_WEST_OFFSET
#undef AB_NORTH_OFFSET
#undef AB_MAX_COLUMNS
+6
View File
@@ -151,3 +151,9 @@
/obj/screen/fullscreen/teleport
icon_state = "teleport"
/obj/screen/fullscreen/blueprints
icon = 'icons/effects/blueprints.dmi'
icon_state = "base"
screen_loc = ui_entire_screen
alpha = 100
+84
View File
@@ -0,0 +1,84 @@
/datum/component/eye
///The actual eye mob linked to the component
var/mob/abstract/eye/component_eye
///What type of /mob/abstract/eye is used
var/eye_type = /mob/abstract/eye
///The mob currently looking through the eye
var/mob/current_looker
///Actions used to pass commands from the eye to the holder. Must be subtypes of /datum/action/eye
///Base type of the action, all subtypes of this are added to actions.
var/action_type
///The actions available to the eye
var/list/actions = list()
/datum/component/eye/Destroy()
unlook()
QDEL_LIST(actions)
. = ..()
/datum/component/eye/proc/look(var/mob/new_looker, var/list/eye_args)
if(new_looker.eyeobj || current_looker)
return FALSE
LAZYINSERT(eye_args, get_turf(current_looker), 1) //Make sure that a loc is provided to the eye
if(!component_eye)
component_eye = new eye_type(arglist(eye_args))
current_looker = new_looker
component_eye.possess(current_looker)
if(action_type)
for(var/atype in subtypesof(action_type))
var/datum/action/eye/action = new atype(src)
actions += action
action.Grant(current_looker)
//Manual unlooking for the looker
var/datum/action/eye/unlook/unlook_action = new(src)
actions += unlook_action
unlook_action.Grant(current_looker)
//Checks for removing the user from the eye outside of unlook actions.
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(unlook))
RegisterSignal(current_looker, COMSIG_MOVABLE_MOVED, PROC_REF(unlook))
RegisterSignal(parent, COMSIG_QDELETING, PROC_REF(unlook))
RegisterSignal(current_looker, COMSIG_QDELETING, PROC_REF(unlook))
RegisterSignal(component_eye, COMSIG_QDELETING, PROC_REF(unlook))
RegisterSignal(current_looker, COMSIG_MOB_LOGOUT, PROC_REF(unlook))
RegisterSignal(current_looker, COMSIG_GLOB_MOB_DEATH, PROC_REF(unlook))
return TRUE
/datum/component/eye/proc/unlook()
if(!isnull(parent))
UnregisterSignal(parent, COMSIG_QDELETING)
UnregisterSignal(parent, COMSIG_MOVABLE_MOVED)
if(!isnull(component_eye))
UnregisterSignal(component_eye, COMSIG_QDELETING)
component_eye.release(current_looker)
if(!isnull(current_looker))
UnregisterSignal(current_looker, COMSIG_MOVABLE_MOVED)
UnregisterSignal(current_looker, COMSIG_QDELETING)
UnregisterSignal(current_looker, COMSIG_MOB_LOGOUT)
UnregisterSignal(current_looker, COMSIG_GLOB_MOB_DEATH)
if(current_looker.client)
current_looker.client.eye = current_looker
current_looker.eyeobj = null
QDEL_NULL(component_eye)
if(current_looker)
for(var/datum/action/A in actions)
A.Remove(current_looker)
current_looker = null
/datum/component/eye/proc/get_eye_turf()
return get_turf(component_eye)
//Every eye created using a subtype of this extension will have this action added for manual unlooking.
/datum/action/eye/unlook
name = "Stop looking"
procname = "unlook"
button_icon_state = "cancel"
target_type = COMPONENT_TARGET
+48
View File
@@ -0,0 +1,48 @@
/datum/component/eye/blueprints
eye_type = /mob/abstract/eye/blueprints
action_type = /datum/action/eye/blueprints
/datum/action/eye/blueprints
eye_type = /mob/abstract/eye/blueprints
/datum/action/eye/blueprints/mark_new_area
name = "Mark New Area"
procname = "create_area"
button_icon_state = "pencil"
target_type = EYE_TARGET
/datum/action/eye/blueprints/remove_selection
name = "Remove Selection"
procname = "remove_selection"
button_icon_state = "eraser"
target_type = EYE_TARGET
/datum/action/eye/blueprints/edit_area
name = "Edit Area"
procname = "edit_area"
button_icon_state = "edit_area"
target_type = EYE_TARGET
/datum/action/eye/blueprints/remove_area
name = "Remove Area"
procname = "remove_area"
button_icon_state = "remove_area"
target_type = EYE_TARGET
/datum/action/eye/blueprints/help
name = "Help"
procname = "help"
button_icon_state = "help"
target_type = COMPONENT_TARGET
/datum/component/eye/blueprints/proc/help()
if(!current_looker)
return
to_chat(current_looker, SPAN_NOTICE("***********************************************************"))
to_chat(current_looker, SPAN_NOTICE("Left Click = Select Area"))
to_chat(current_looker, SPAN_NOTICE("Shift Click = Deselect Area"))
to_chat(current_looker, SPAN_NOTICE("Control Click = Clear Selection"))
to_chat(current_looker, SPAN_NOTICE("***********************************************************"))
/datum/component/eye/blueprints/shuttle
eye_type = /mob/abstract/eye/blueprints/shuttle
+13
View File
@@ -0,0 +1,13 @@
//For mobs connecting to the station's cameranet without needing AI procs
/datum/component/eye/cameranet
eye_type = /mob/abstract/eye/cameranet
//For mobs connecting to other visualnets. Pass visualnet as eye_args in look()
/datum/component/eye/freelook
eye_type = /mob/abstract/eye
/datum/component/eye/freelook/proc/set_visualnet(var/datum/visualnet/net)
if(component_eye)
var/mob/abstract/eye/eye = component_eye
if(istype(eye))
eye.visualnet = net
+1
View File
@@ -59,6 +59,7 @@
return
/atom/proc/additional_sight_flags()
SHOULD_BE_PURE(TRUE)
return 0
/atom/proc/additional_see_invisible()
+133 -257
View File
@@ -1,275 +1,151 @@
/obj/item/blueprints
name = "station blueprints"
name = "blueprints"
desc = "Blueprints of the station. There is a \"Classified\" stamp and several coffee stains on it."
icon = 'icons/obj/items.dmi'
icon = 'icons/obj/item/tools/blueprints.dmi'
icon_state = "blueprints"
attack_verb = list("attacked", "bapped", "hit")
var/datum/wires/airlock/blueprint/airlock_wires
var/datum/wires/vending/blueprint/vending_wires
var/const/AREA_ERRNONE = 0
var/const/AREA_STATION = 1
var/const/AREA_SPACE = 2
var/const/AREA_SPECIAL = 3
var/const/BORDER_ERROR = 0
var/const/BORDER_NONE = 1
var/const/BORDER_BETWEEN = 2
var/const/BORDER_2NDTILE = 3
var/const/BORDER_SPACE = 4
var/const/ROOM_ERR_LOLWAT = 0
var/const/ROOM_ERR_SPACE = -1
var/const/ROOM_ERR_TOOLARGE = -2
var/list/active_blueprints
w_class = ITEMSIZE_SMALL
var/list/valid_z_levels = list()
var/area_prefix
/obj/item/blueprints/Initialize(mapload, ...)
. = ..()
airlock_wires = new(src)
vending_wires = new(src)
return INITIALIZE_HINT_LATELOAD
/obj/item/blueprints/LateInitialize()
. = ..()
desc = "Blueprints of the [station_name()]. There is a \"Classified\" stamp and several coffee stains on it."
if(set_valid_z_levels())
create_blueprint_component()
/obj/item/blueprints/attack_self(mob/user)
if(use_check_and_message(user, USE_DISALLOW_SILICONS))
return
add_fingerprint(user)
interact()
/obj/item/blueprints/equipped(var/mob/user, var/slot)
. = ..()
if(slot == slot_l_hand || slot == slot_r_hand)
START_PROCESSING(SSprocessing, src)
LAZYINITLIST(active_blueprints)
/obj/item/blueprints/dropped(mob/user)
. = ..()
STOP_PROCESSING(SSprocessing, src)
if(user.client)
for(var/thing in active_blueprints)
user.client.images -= thing
LAZYCLEARLIST(active_blueprints)
/obj/item/blueprints/process()
var/mob/user = loc
if(!istype(user))
return PROCESS_KILL
var/turf/origin_turf = get_turf(src)
var/list/new_prints = list()
for(var/turf/T as anything in RANGE_TURFS(world.view, origin_turf))
for(var/image/I as anything in T.blueprints)
new_prints += I
var/list/update_remove = active_blueprints - new_prints
active_blueprints = new_prints - update_remove
for(var/thing in update_remove)
user.client.images -= thing
for(var/thing in active_blueprints)
user.client.images += thing
/obj/item/blueprints/Topic(href, href_list)
..()
if(use_check_and_message(usr, USE_DISALLOW_SILICONS) || usr.get_active_hand() != src)
return
if(!href_list["action"])
add_fingerprint(user)
var/datum/component/eye/blueprints = GetComponent(/datum/component/eye)
if(!(user.z in valid_z_levels))
to_chat(user, SPAN_WARNING("The markings on this are entirely irrelevant to your whereabouts!"))
return
switch(href_list["action"])
if ("create_area")
if(get_area_type() != AREA_SPACE)
interact()
return
create_area()
if ("edit_area")
if (get_area_type()!=AREA_STATION)
interact()
return
edit_area()
if("airlock_wires")
airlock_wires.get_wire_diagram(usr)
if("vending_wires")
vending_wires.get_wire_diagram(usr)
if(blueprints)
if(blueprints.look(user, get_look_args())) // Abandon all peripheral vision, ye who enter here.
to_chat(user, SPAN_NOTICE("You start peering closely at \the [src]"))
return
else
to_chat(user, SPAN_WARNING("You couldn't get a good look at \the [src]. Maybe someone else is using it?"))
return
to_chat(user, SPAN_WARNING("The markings on this are useless!"))
/obj/item/blueprints/interact()
var/area/A = get_area(src)
var/text = {"<HTML><head><title>[src]</title></head><BODY>
<h2>[station_name()] blueprints</h2>
<small>Property of [SSatlas.current_map.company_name]. For heads of staff only. Store in high-secure storage.</small><hr>
"}
switch (get_area_type())
if (AREA_SPACE)
text += {"
<p>According the blueprints, you are now in <b>outer space</b>. Hold your breath.</p>
<p><a href='?src=\ref[src];action=create_area'>Mark this place as new area.</a></p>
"}
if (AREA_STATION)
text += {"
<p>According the blueprints, you are now in <b>\"[A.name]\"</b>.</p>
<p>You may <a href='?src=\ref[src];action=edit_area'>
move an amendment</a> to the drawing.</p>
"}
if (AREA_SPECIAL)
text += {"
<p>This place isn't noted on the blueprint.</p>
"}
/obj/item/blueprints/proc/set_valid_z_levels()
if(SSatlas.current_map.use_overmap)
var/obj/effect/overmap/visitable/sector/S = GLOB.map_sectors["[GET_Z(src)]"]
if(!S) //Blueprints are useless now, but keep them around for fluff
desc = "Some dusty old blueprints. The markings are old, and seem entirely irrelevant for your wherabouts."
return FALSE
name += " - [S.name]"
desc = "Blueprints of \the [S.name]. There is a \"Classified\" stamp and several coffee stains on it."
valid_z_levels += S.map_z
area_prefix = S.name
return TRUE
desc = "Blueprints of the [station_name()]. There is a \"Classified\" stamp and several coffee stains on it."
area_prefix = station_name()
valid_z_levels = SSatlas.current_map.station_levels
text += "<br><a href='?src=\ref[src];action=airlock_wires'>View Airlock Wire Schema</a><br>"
text += "<br><a href='?src=\ref[src];action=vending_wires'>View Vending Machine Wire Schema</a><br>"
/obj/item/blueprints/proc/create_blueprint_component() //So that subtypes can override
AddComponent(/datum/component/eye/blueprints)
text += "</BODY></HTML>"
/obj/item/blueprints/proc/get_look_args() // ditto
return list(valid_z_levels, area_prefix)
var/datum/browser/blueprints_win = new(usr, "blueprints", capitalize_first_letters(name))
blueprints_win.set_content(text)
blueprints_win.open()
//Blueprints for building exoplanet outposts
/obj/item/blueprints/outpost
name = "outpost blueprints"
icon_state = "blueprints2"
/obj/item/blueprints/proc/get_area_type(var/area/A = get_area(src))
if(istype(A, /area/space) || istype(A, /area/mine/unexplored))
return AREA_SPACE
var/list/SPECIALS = list(
/area/shuttle,
/area/centcom,
/area/tdome,
/area/antag
/obj/item/blueprints/outpost/attack_self(mob/user)
var/obj/effect/overmap/visitable/sector/S = GLOB.map_sectors["[GET_Z(user)]"]
area_prefix = S.name
. = ..()
/obj/item/blueprints/outpost/set_valid_z_levels()
if(!SSatlas.current_map.use_overmap)
desc = "Some dusty old blueprints. The markings are old, and seem entirely irrelevant for your wherabouts."
return FALSE
desc = "Blueprints for the daring souls wanting to establish a planetary outpost. Has some sketchy looking stains and what appears to be bite holes."
var/area/overmap/map = global.map_overmap
for(var/obj/effect/overmap/visitable/sector/exoplanet/E in map)
valid_z_levels += E.map_z
return TRUE
/obj/item/blueprints/shuttle
desc_info = "These blueprints can be used to modify a shuttle. In order to be used, the shuttle must be located on its \"Open Space\" z-level. Newly-created areas will be automatically added to the shuttle. If all shuttle areas are removed, the shuttle will be destroyed!"
///Name of the blueprints' linked shuttle. Mapped-in versions should have this preset, or be mapped into the shuttle area itself.
var/shuttle_name
///The actual overmap shuttle type, for setting on preset blueprints.
var/obj/effect/overmap/visitable/ship/landable/shuttle_type
/obj/item/blueprints/shuttle/set_valid_z_levels()
if(SSatlas.current_map.use_overmap)
var/area/A = get_area(src)
if(!shuttle_type)
for(var/obj/effect/overmap/visitable/ship/landable/landable in SSshuttle.ships)
var/datum/shuttle/shuttle = SSshuttle.shuttles[landable.shuttle]
if(shuttle && (A in shuttle.shuttle_area))
shuttle_type = landable
break
else
var/obj/effect/overmap/visitable/ship/landable/S = SSshuttle.ship_by_type(shuttle_type)
shuttle_type = S
if(istype(shuttle_type) && !isnull(shuttle_type))
if(isnull(shuttle_name))
shuttle_name = shuttle_type.shuttle
update_linked_name(shuttle_type, null, shuttle_type.name)
RegisterSignal(shuttle_type, COMSIG_QDELETING, PROC_REF(on_shuttle_destroy))
RegisterSignal(shuttle_type, COMSIG_BASENAME_SETNAME, PROC_REF(update_linked_name))
valid_z_levels += shuttle_type.map_z
area_prefix = shuttle_type.name
return TRUE
// The blueprints are useless now, but keep them around for fluff.
desc = "Some dusty old blueprints. The markings are old, and seem entirely irrelevant for your wherabouts."
return FALSE
/obj/item/blueprints/shuttle/proc/update_linked_name(atom/namee, old_name, new_name)
name = "\improper [new_name] blueprints"
desc = "Blueprints of \the [new_name]. There are several coffee stains on it."
area_prefix = new_name
/obj/item/blueprints/shuttle/proc/on_shuttle_destroy(datum/destroyed)
UnregisterSignal(destroyed, COMSIG_QDELETING)
UnregisterSignal(destroyed, COMSIG_BASENAME_SETNAME)
name = initial(name)
desc = "Some dusty old blueprints. The markings are old, and seem entirely irrelevant for your wherabouts."
valid_z_levels = list()
area_prefix = null
/obj/item/blueprints/shuttle/create_blueprint_component()
AddComponent(/datum/component/eye/blueprints/shuttle)
/obj/item/blueprints/shuttle/get_look_args()
return list(valid_z_levels, area_prefix, shuttle_name)
/obj/item/blueprints/shuttle/intrepid
shuttle_name = "Intrepid"
shuttle_type = /obj/effect/overmap/visitable/ship/landable/intrepid
/obj/item/blueprints/shuttle/mining_shuttle
shuttle_name = "Spark"
shuttle_type = /obj/effect/overmap/visitable/ship/landable/mining_shuttle
/obj/item/blueprints/shuttle/canary
shuttle_name = "Canary"
shuttle_type = /obj/effect/overmap/visitable/ship/landable/canary
/obj/item/storage/lockbox/shuttle_blueprints //Blueprints for modifying the Horizon's shuttles.
name = "shuttle blueprints lockbox"
req_access = list(ACCESS_CE)
starts_with = list(
/obj/item/blueprints/shuttle/intrepid = 1,
/obj/item/blueprints/shuttle/mining_shuttle = 1,
/obj/item/blueprints/shuttle/canary = 1
)
for (var/type in SPECIALS)
if ( istype(A,type) )
return AREA_SPECIAL
return AREA_STATION
/obj/item/blueprints/proc/create_area()
var/res = detect_room(get_turf(usr))
if(!istype(res,/list))
switch(res)
if(ROOM_ERR_SPACE)
to_chat(usr, "<span class='warning'>The new area must be completely airtight!</span>")
return
if(ROOM_ERR_TOOLARGE)
to_chat(usr, "<span class='warning'>The new area too large!</span>")
return
else
to_chat(usr, "<span class='warning'>Error! Please notify administration!</span>")
return
var/list/turf/turfs = res
var/str = sanitizeSafe(input("New area name:","Blueprint Editing", ""), MAX_NAME_LEN)
if(!str || !length(str)) //cancel
return
if(length(str) > 50)
to_chat(usr, "<span class='warning'>Name too long.</span>")
return
var/area/A = new
A.name = str
A.power_equip = 0
A.power_light = 0
A.power_environ = 0
A.always_unpowered = 0
move_turfs_to_area(turfs, A)
A.always_unpowered = 0
sorted_add_area(A)
addtimer(CALLBACK(src, PROC_REF(interact)), 5)
return
/obj/item/blueprints/proc/move_turfs_to_area(var/list/turf/turfs, var/area/A)
A.contents.Add(turfs)
//oldarea.contents.Remove(usr.loc) // not needed
//T.forceMove(A) //error: cannot change constant value
/obj/item/blueprints/proc/edit_area()
var/area/A = get_area(src)
var/prevname = "[A.name]"
var/str = sanitizeSafe(input("New area name:","Blueprint Editing", prevname), MAX_NAME_LEN)
if(!str || !length(str) || str==prevname) //cancel
return
if(length(str) > 50)
to_chat(usr, "<span class='warning'>Text too long.</span>")
return
INVOKE_ASYNC(src, PROC_REF(set_area_machinery_title), A, str, prevname)
A.name = str
sortTim(GLOB.all_areas, GLOBAL_PROC_REF(cmp_text_asc))
to_chat(usr, "<span class='notice'>You set the area '[prevname]' title to '[str]'.</span>")
interact()
return
/obj/item/blueprints/proc/set_area_machinery_title(var/area/A,var/title,var/oldtitle)
if (!oldtitle) // or replacetext goes to infinite loop
return
var/static/list/types_to_rename = list(
/obj/machinery/alarm,
/obj/machinery/power/apc,
/obj/machinery/atmospherics/unary/vent_scrubber,
/obj/machinery/atmospherics/unary/vent_pump,
/obj/machinery/door
)
for(var/obj/machinery/M in A)
if (is_type_in_list(M, types_to_rename))
M.name = replacetext(M.name, oldtitle, title)
CHECK_TICK
/obj/item/blueprints/proc/check_tile_is_border(var/turf/T2,var/dir)
if (istype(T2, /turf/space) || istype(T2, /turf/unsimulated/floor/asteroid))
return BORDER_SPACE //omg hull breach we all going to die here
if (get_area_type(T2.loc)!=AREA_SPACE)
return BORDER_BETWEEN
if (istype(T2, /turf/simulated/wall))
return BORDER_2NDTILE
if (!istype(T2, /turf/simulated))
return BORDER_BETWEEN
for (var/obj/structure/window/W in T2)
if(turn(dir,180) == W.dir)
return BORDER_BETWEEN
if (W.dir in list(NORTHEAST,SOUTHEAST,NORTHWEST,SOUTHWEST))
return BORDER_2NDTILE
for(var/obj/machinery/door/window/D in T2)
if(turn(dir,180) == D.dir)
return BORDER_BETWEEN
if (locate(/obj/machinery/door) in T2)
return BORDER_2NDTILE
return BORDER_NONE
/obj/item/blueprints/proc/detect_room(var/turf/first)
var/list/turf/found = new
var/list/turf/pending = list(first)
while(pending.len)
if (found.len+pending.len > 300)
return ROOM_ERR_TOOLARGE
var/turf/T = pending[1] //why byond havent list::pop()?
pending -= T
for (var/dir in GLOB.cardinal)
var/skip = 0
for (var/obj/structure/window/W in T)
if(dir == W.dir || (W.dir in list(NORTHEAST,SOUTHEAST,NORTHWEST,SOUTHWEST)))
skip = 1; break
if (skip) continue
for(var/obj/machinery/door/window/D in T)
if(dir == D.dir)
skip = 1; break
if (skip) continue
var/turf/NT = get_step(T,dir)
if (!isturf(NT) || (NT in found) || (NT in pending))
continue
var/tile_is_border = check_tile_is_border(NT,dir)
if(tile_is_border != BORDER_BETWEEN)
switch(tile_is_border)
if(BORDER_NONE)
pending+=NT
if(BORDER_2NDTILE)
found+=NT //tile included to new area, but we dont seek more
if(BORDER_SPACE)
return ROOM_ERR_SPACE
found+=T
return found
@@ -29,6 +29,8 @@
new /obj/item/crowbar/rescue_axe/red(src)
new /obj/item/device/radio/eng/off(src)
new /obj/item/grenade/chem_grenade/antifuel(src)
new /obj/item/storage/lockbox/shuttle_blueprints(src)
new /obj/item/blueprints/outpost(src)
// Chief Engineer - Clothing Satchel
// This satchel is used nowhere except in conjunction with the locker above,
+12 -40
View File
@@ -144,55 +144,27 @@
body_parts_covered = FACE|EYES
action_button_name = "Toggle MIU"
origin_tech = list(TECH_DATA = 5, TECH_ENGINEERING = 5)
var/active = FALSE
var/mob/abstract/eye/cameranet/eye
/obj/item/clothing/mask/ai/Initialize()
eye = new(src)
eye.name_suffix = "camera MIU"
. = ..()
/obj/item/clothing/mask/ai/Destroy()
if(eye)
if(active)
disengage_mask(eye.owner)
qdel(eye)
eye = null
. = ..()
AddComponent(/datum/component/eye/cameranet)
/obj/item/clothing/mask/ai/attack_self(mob/user)
if(user.incapacitated())
return
active = !active
to_chat(user, SPAN_NOTICE("You [active ? "" : "dis"]engage \the [src]."))
if(active)
engage_mask(user)
else
disengage_mask(user)
/obj/item/clothing/mask/ai/equipped(mob/user, slot)
..(user, slot)
engage_mask(user)
/obj/item/clothing/mask/ai/dropped(mob/user)
..()
disengage_mask(user)
/obj/item/clothing/mask/ai/proc/engage_mask(mob/user)
if(!active)
return
if(user.get_equipped_item(slot_wear_mask) != src)
to_chat(user, SPAN_WARNING("You must be wearing \the [src] to activate it!"))
return
eye.possess(user)
to_chat(eye.owner, SPAN_NOTICE("You feel disoriented for a moment as your mind connects to the camera network."))
/obj/item/clothing/mask/ai/proc/disengage_mask(mob/user)
if(user == eye.owner)
to_chat(eye.owner, SPAN_NOTICE("You feel disoriented for a moment as your mind disconnects from the camera network."))
eye.release(eye.owner)
eye.forceMove(src)
var/datum/component/eye/cameranet/CN = GetComponent(/datum/component/eye)
if(!CN)
to_chat(user, SPAN_WARNING("\The [src] doesn't respond!"))
return
if(CN.current_looker)
CN.unlook()
to_chat(user, SPAN_NOTICE("You deactivate \the [src]."))
else
CN.look(user)
to_chat(user, SPAN_NOTICE("You activate \the [src]."))
/obj/item/clothing/mask/offworlder
name = "scarab scarf"
@@ -7,6 +7,7 @@
// Generic version of the AI eye without the AI-specific handling, for things like the Camera MIU mask.
name = "Inactive Camera Eye"
name_suffix = "Camera Eye"
living_eye = FALSE
/mob/abstract/eye/cameranet/Initialize()
. = ..()
@@ -16,6 +17,7 @@
name = "Inactive AI Eye"
name_suffix = "AI Eye"
icon_state = "AI-eye"
living_eye = FALSE
/mob/abstract/eye/aiEye/Initialize()
. = ..()
@@ -0,0 +1,330 @@
#define MAX_AREA_SIZE 300
/mob/abstract/eye/blueprints
/// Associative list of turfs -> boolean validity that the player has selected for new area creation.
var/list/selected_turfs = list()
///The overlayed images of the user's selection.
var/list/selection_images = list()
///The last turf selected.
var/turf/last_selected_turf
///The image overlayed on the last selected turf
var/image/last_selected_image
///On what z-levels the blueprints can be used to modify or create areas.
var/list/valid_z_levels = list()
///Displayed to the user to allow them to see what area they're hovering over
var/obj/effect/overlay/area_name_effect
///The prefix of the area name effect display
var/area_prefix
///Displayed to the user on failed area creation
var/list/errors = list()
/mob/abstract/eye/blueprints/Initialize(mapload, var/list/valid_zs, var/area_p)
. = ..(mapload)
if(valid_zs)
valid_z_levels = valid_zs.Copy()
area_prefix = area_p
area_name_effect = new(src)
area_name_effect.icon_state = "nothing"
area_name_effect.maptext_height = 64
area_name_effect.maptext_width = 128
area_name_effect.layer = FLOAT_LAYER
area_name_effect.plane = HUD_PLANE
area_name_effect.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA
area_name_effect.screen_loc = "LEFT+1,BOTTOM+2"
last_selected_image = image('icons/effects/blueprints.dmi', "selected")
last_selected_image.plane = HUD_PLANE
last_selected_image.appearance_flags = NO_CLIENT_COLOR|RESET_COLOR
/mob/abstract/eye/blueprints/Destroy()
QDEL_NULL(area_name_effect)
errors = null
selected_turfs = null
valid_z_levels = null
last_selected_turf = null
. = ..()
/mob/abstract/eye/blueprints/release(var/mob/user)
if(owner && owner.client && user == owner)
owner.client.images.Cut()
. = ..()
/mob/abstract/eye/blueprints/proc/create_area()
var/area_name = sanitizeSafe(tgui_input_text(owner, "New area name: ", "Area Creation", "", MAX_NAME_LEN))
if(!area_name || !length(area_name))
return
if(length(area_name) > 50)
to_chat(owner, SPAN_WARNING("That name is too long!"))
return
if(!check_selection_validity())
to_chat(owner, SPAN_WARNING("Could not mark area: [english_list(errors)]!"))
return
var/area/A = finalize_area(area_name)
for(var/turf/T in selected_turfs)
ChangeArea(T, A)
remove_selection() // Reset the selection for clarity.
/mob/abstract/eye/blueprints/proc/finalize_area(var/area_name)
var/area/A = new()
A.name = area_name
var/area/old_area = get_area(selected_turfs[1])
if(old_area.area_flags & AREA_FLAG_INDESTRUCTIBLE_TURFS) //to prevent new areas on exoplanets being ventable
A.area_flags |= AREA_FLAG_INDESTRUCTIBLE_TURFS
A.power_equip = FALSE
A.power_light = FALSE
A.power_environ = FALSE
A.always_unpowered = FALSE
return A
/mob/abstract/eye/blueprints/proc/remove_area()
var/area/A = get_area(src)
if(!check_modification_validity())
return
if(A.apc)
to_chat(owner, SPAN_WARNING("You must remove the APC from this area before you can remove it from the blueprints!"))
return
to_chat(owner, SPAN_NOTICE("You scrub [A.name] off the blueprints."))
log_and_message_admins("deleted area [A.name] via station blueprints.")
var/background_area = /area/space
var/obj/effect/overmap/visitable/sector/sector = GLOB.map_sectors["[A.z]"]
var/obj/effect/overmap/visitable/sector/exoplanet/exoplanet = sector
if(istype(exoplanet))
background_area = exoplanet.planetary_area
for(var/turf/T in A.contents)
ChangeArea(T, background_area)
if(!locate(/turf) in A)
qdel(A)
/mob/abstract/eye/blueprints/proc/edit_area()
var/area/A = get_area(src)
if(!check_modification_validity())
return
var/prevname = A.name
var/new_area_name = sanitizeSafe(input("Edit area name:","Area Editing", prevname), MAX_NAME_LEN)
if(!new_area_name || !LAZYLEN(new_area_name) || new_area_name==prevname)
return
if(length(new_area_name) > 50)
to_chat(owner, SPAN_WARNING("Text too long."))
return
// Adjusting titles in the old area.
var/static/list/types_to_rename = list(
/obj/machinery/alarm,
/obj/machinery/power/apc,
/obj/machinery/atmospherics/unary/vent_pump,
/obj/machinery/atmospherics/unary/vent_pump,
/obj/machinery/door
)
for(var/obj/machinery/M in A)
if(is_type_in_list(M, types_to_rename))
M.name = replacetext(M.name, prevname, new_area_name)
A.name = new_area_name
to_chat(owner, SPAN_NOTICE("You set the area '[prevname]' title to '[new_area_name]'."))
/mob/abstract/eye/blueprints/ClickOn(atom/A, params)
params = params2list(params)
if(!canClick())
return
if(params["left"])
update_selected_turfs(get_turf(A), params)
if(params["ctrl"]) //Shift-click to clear the selection
remove_selection()
/mob/abstract/eye/blueprints/proc/update_selected_turfs(var/turf/next_selected_turf, var/list/params)
if(!next_selected_turf)
return
if(!last_selected_turf) //The player has only placed one corner of the block
last_selected_turf = next_selected_turf
last_selected_image.loc = last_selected_turf
return
if(last_selected_turf.z != next_selected_turf.z) // No multi-Z areas. Contiguity checks this as well, but this is cheaper.
return
var/list/new_selection = block(last_selected_turf, next_selected_turf)
if(params["shift"]) //Right-click to remove areas from the selection
selected_turfs -= new_selection
else
selected_turfs |= new_selection
last_selected_image.loc = null //Remove last selected turf indicator image
check_selection_validity()
update_images()
/mob/abstract/eye/blueprints/proc/check_selection_validity()
. = TRUE
errors.Cut()
if(!LAZYLEN(selected_turfs)) //Sanity check
errors |= "no turfs are selected"
return FALSE
if(LAZYLEN(selected_turfs) > MAX_AREA_SIZE)
errors |= "selection exceeds max size"
return FALSE
for(var/turf/T in selected_turfs)
var/turf_valid = check_turf_validity(T)
. = min(., turf_valid)
selected_turfs[T] = turf_valid
if(!.) return
. = check_contiguity()
/mob/abstract/eye/blueprints/proc/check_turf_validity(var/turf/T)
. = TRUE
if(!T)
return FALSE
if(!(T.z in valid_z_levels))
errors |= "selection isn't marked on the blueprints"
. = FALSE
var/area/A = T.loc
if(!A) //Safety check
errors |= "selection overlaps unknown location"
return FALSE
if(!(A.area_flags & AREA_FLAG_IS_BACKGROUND)) // Cannot create new areas over old ones.
errors |= "selection overlaps other area"
. = FALSE
if(istype(T, (A.base_turf ? A.base_turf : /turf/space)))
errors |= "selection is exposed to the outside"
. = FALSE
/mob/abstract/eye/blueprints/proc/check_contiguity()
var/turf/start_turf = pick(selected_turfs)
var/list/pending_turfs = list(start_turf)
var/list/checked_turfs = list()
while(pending_turfs.len)
if(LAZYLEN(checked_turfs) > MAX_AREA_SIZE)
errors |= "selection exceeds max size"
break
var/turf/T = pending_turfs[1]
pending_turfs -= T
for(var/dir in GLOB.cardinal) // Floodfill to find all turfs contiguous with the randomly chosen start_turf.
var/turf/NT = get_step(T, dir)
if(!isturf(NT) || !(NT in selected_turfs) || (NT in pending_turfs) || (NT in checked_turfs))
continue
pending_turfs += NT
checked_turfs += T
var/list/incontiguous_turfs = (selected_turfs.Copy() - checked_turfs)
if(LAZYLEN(incontiguous_turfs)) // If turfs still remain in incontiguous_turfs, there are non-contiguous turfs in the selection.
errors |= "selection must be contiguous"
return FALSE
return TRUE
/mob/abstract/eye/blueprints/proc/check_modification_validity()
. = TRUE
var/area/A = get_area(src)
if(!(A.z in valid_z_levels))
to_chat(owner, SPAN_WARNING("The markings on this are entirely irrelevant to your whereabouts!"))
return FALSE
if(A in SSshuttle.shuttle_areas)
to_chat(owner, SPAN_WARNING("This segment of the blueprints looks far too complex. Best not touch it!"))
return FALSE
if(!A || (A.area_flags & AREA_FLAG_IS_BACKGROUND))
to_chat(owner, SPAN_WARNING("This area is not marked on the blueprints!"))
return FALSE
/mob/abstract/eye/blueprints/proc/remove_selection()
selected_turfs.Cut()
last_selected_turf = null
update_images()
/mob/abstract/eye/blueprints/proc/update_images()
if(!owner || !owner.client)
return
owner.client.images -= selection_images
selection_images.Cut()
if(LAZYLEN(selected_turfs))
for(var/turf/T in selected_turfs)
var/selection_icon_state = selected_turfs[T] ? "valid" : "invalid"
var/image/I = image('icons/effects/blueprints.dmi', T, selection_icon_state)
I.plane = HUD_PLANE
I.appearance_flags = NO_CLIENT_COLOR
selection_images += I
owner.client.images |= last_selected_image
owner.client.images += selection_images
/mob/abstract/eye/blueprints/setLoc(T)
. = ..()
var/style = "font-family: 'Fixedsys'; -dm-text-outline: 1 black; font-size: 11px;"
var/area/A = get_area(src)
if(!A)
return
area_name_effect.maptext = "<span style=\"[style]\">[area_prefix], [A.name]</span>"
/mob/abstract/eye/blueprints/additional_sight_flags()
return SEE_TURFS|BLIND
/mob/abstract/eye/blueprints/apply_visual(mob/living/M)
M.overlay_fullscreen("blueprints", /obj/screen/fullscreen/blueprints)
M.client.screen += area_name_effect
M.add_client_color(/datum/client_color/monochrome)
/mob/abstract/eye/blueprints/remove_visual(mob/living/M)
M.clear_fullscreen("blueprints", 0)
M.client.screen -= area_name_effect
M.remove_client_color(/datum/client_color/monochrome)
//Shuttle blueprint eye
/mob/abstract/eye/blueprints/shuttle
var/shuttle_name
/mob/abstract/eye/blueprints/shuttle/Initialize(mapload, list/valid_zs, area_p, shuttle_name)
. = ..()
src.shuttle_name = shuttle_name
/mob/abstract/eye/blueprints/shuttle/check_modification_validity()
. = TRUE
var/area/A = get_area(src)
if(!(A.z in valid_z_levels))
to_chat(owner, SPAN_WARNING("The markings on this are entirely irrelevant to your whereabouts!"))
return FALSE
var/datum/shuttle/our_shuttle = SSshuttle.shuttles[shuttle_name]
if(!(A in our_shuttle.shuttle_area))
to_chat(owner, SPAN_WARNING("That's not a part of the [our_shuttle.name]!"))
return FALSE
if(!A || (A.area_flags & AREA_FLAG_IS_BACKGROUND))
to_chat(owner, SPAN_WARNING("This area is not marked on the blueprints!"))
return FALSE
/mob/abstract/eye/blueprints/shuttle/remove_area()
var/area/A = get_area(src)
if(!check_modification_validity())
return
if(A.apc)
to_chat(owner, SPAN_WARNING("You must remove the APC from this area before you can remove it from the blueprints!"))
return
var/datum/shuttle/our_shuttle = SSshuttle.shuttles[shuttle_name]
if(our_shuttle.shuttle_area.len == 1) //If it's the last shuttle area, make sure that we don't break the shuttle in question.
to_chat(owner, SPAN_WARNING("You cannot delete the last area in a shuttle!"))
return
to_chat(owner, SPAN_NOTICE("You scrub [A.name] off the blueprints."))
log_and_message_admins("deleted area [A.name] from [our_shuttle.name] via shuttle blueprints.")
var/background_area = /area/space
var/obj/effect/overmap/visitable/sector/sector = GLOB.map_sectors["[A.z]"]
var/obj/effect/overmap/visitable/sector/exoplanet/exoplanet = sector
if(istype(exoplanet))
background_area = exoplanet.planetary_area
for(var/turf/T in A.contents)
ChangeArea(T, background_area)
if(!(locate(/turf) in A))
qdel(A) // uh oh, is this safe?
/mob/abstract/eye/blueprints/shuttle/finalize_area(area_name)
var/area/A = ..(area_name)
var/datum/shuttle/our_shuttle = SSshuttle.shuttles[shuttle_name]
our_shuttle.shuttle_area += A
SSshuttle.shuttle_areas += A
RegisterSignal(A, COMSIG_QDELETING, TYPE_PROC_REF(/datum/shuttle, remove_shuttle_area))
return A
#undef MAX_AREA_SIZE
+40
View File
@@ -24,6 +24,11 @@
var/ghostimage = null
var/datum/visualnet/visualnet
///Set if the eye uses special click handling. Distinct from parent mob click handling for AI eye.
var/click_handler_type = /datum/click_handler/eye
///Whether or not our eye uses normal living vision handling
var/living_eye = TRUE
/mob/abstract/eye/New()
ghostimage = image(src.icon,src,src.icon_state)
@@ -78,6 +83,10 @@
name = "[owner.name] ([name_suffix])"
if(owner.client)
owner.client.eye = src
LAZYDISTINCTADD(owner.additional_vision_handlers, src)
apply_visual(owner)
if(click_handler_type)
owner.PushClickHandler(click_handler_type)
setLoc(owner)
visualnet.update_eye_chunks(src, TRUE)
@@ -86,8 +95,14 @@
return
if(owner.eyeobj != src)
return
remove_visual(owner)
LAZYREMOVE(owner.additional_vision_handlers, src)
visualnet.remove_eye(src)
owner.eyeobj = null
if(owner.client)
owner.client.eye = owner
if(click_handler_type)
owner.RemoveClickHandler(click_handler_type)
owner = null
name = initial(name)
@@ -144,3 +159,28 @@
else
sprint = initial
return 1
/mob/abstract/eye/ClickOn(atom/A, params)
if(owner)
return owner.ClickOn(A, params)
return ..()
/mob/abstract/eye/proc/apply_visual(mob/M)
if(M != owner || !M.client)
return FALSE
return TRUE
/mob/abstract/eye/proc/remove_visual(mob/M)
if(M != owner || !M.client)
return FALSE
return TRUE
/datum/click_handler/eye/OnClick(atom/A, params)
var/mob/abstract/eye = user.eyeobj
if(!eye) //Something has broken, ensure the click handler doesn't stick around
user.RemoveClickHandler(src)
return
eye.ClickOn(A, params)
/datum/click_handler/eye/OnDblClick(atom/A, params)
OnClick(A, params)
@@ -1867,6 +1867,8 @@
..()
if(update_hud)
handle_regular_hud_updates()
if(eyeobj)
eyeobj.remove_visual(src)
/mob/living/carbon/human/can_stand_overridden()
+2 -1
View File
@@ -146,6 +146,7 @@
else if(viewflags)
set_sight(viewflags)
else if(eyeobj)
eyeobj.apply_visual(src)
if(eyeobj.owner != src)
reset_view(null)
else if(!client.adminobs)
@@ -160,7 +161,7 @@
/mob/living/proc/update_sight()
set_sight(0)
if(stat == DEAD || eyeobj)
if(stat == DEAD || (eyeobj && !eyeobj.living_eye))
update_dead_sight()
else
update_living_sight()
@@ -2,7 +2,7 @@
name = "\improper Planetary surface"
ambience = list('sound/effects/wind/wind_2_1.ogg','sound/effects/wind/wind_2_2.ogg','sound/effects/wind/wind_3_1.ogg','sound/effects/wind/wind_4_1.ogg','sound/effects/wind/wind_4_2.ogg','sound/effects/wind/wind_5_1.ogg')
always_unpowered = 1
area_flags = AREA_FLAG_INDESTRUCTIBLE_TURFS
area_flags = AREA_FLAG_INDESTRUCTIBLE_TURFS|AREA_FLAG_IS_BACKGROUND
is_outside = OUTSIDE_YES
/area/exoplanet/adhomai
+4
View File
@@ -10,6 +10,10 @@
/datum/shuttle/autodock/overmap/New(var/_name, var/obj/effect/shuttle_landmark/start_waypoint)
..(_name, start_waypoint)
refresh_fuel_ports_list()
for(var/area/A in shuttle_area) //If shuttles initialize after the blueprints, they won't set correctly so we do it here.
var/obj/item/blueprints/shuttle/blueprints = locate() in A
if(blueprints)
blueprints.set_valid_z_levels()
/datum/shuttle/autodock/overmap/proc/refresh_fuel_ports_list() //loop through all
fuel_ports = list()
+1
View File
@@ -207,6 +207,7 @@ var/global/area/overmap/map_overmap // Global object used to locate the overmap
return
if(comms_support)
update_away_freq(name, get_real_name())
SEND_SIGNAL(src, COMSIG_BASENAME_SETNAME, args)
name = get_real_name()
/obj/effect/overmap/visitable/proc/get_real_name()
+8
View File
@@ -49,6 +49,7 @@
if(!istype(A))
CRASH("Shuttle \"[name]\" couldn't locate area [T].")
areas += A
RegisterSignal(A, COMSIG_QDELETING, PROC_REF(remove_shuttle_area))
shuttle_area = areas
if(initial_location)
@@ -311,3 +312,10 @@
/datum/shuttle/proc/on_move_interim()
return
/datum/shuttle/proc/remove_shuttle_area(area/area_to_remove)
UnregisterSignal(area_to_remove, COMSIG_QDELETING)
SSshuttle.shuttle_areas -= area_to_remove
shuttle_area -= area_to_remove
if(!length(shuttle_area))
qdel(src)
@@ -0,0 +1,60 @@
################################
# Example Changelog File
#
# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb.
#
# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.)
# When it is, any changes listed below will disappear.
#
# Valid Prefixes:
# bugfix
# - (fixes bugs)
# wip
# - (work in progress)
# qol
# - (quality of life)
# soundadd
# - (adds a sound)
# sounddel
# - (removes a sound)
# rscadd
# - (adds a feature)
# rscdel
# - (removes a feature)
# imageadd
# - (adds an image or sprite)
# imagedel
# - (removes an image or sprite)
# spellcheck
# - (fixes spelling or grammar)
# experiment
# - (experimental change)
# balance
# - (balance changes)
# code_imp
# - (misc internal code change)
# refactor
# - (refactors code)
# config
# - (makes a change to the config files)
# admin
# - (makes changes to administrator tools)
# server
# - (miscellaneous changes to server)
#################################
# Your name.
author: RustingWithYou
# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again.
delete-after: True
# Any changes you've made. See valid prefix list above.
# INDENT WITH TWO SPACES. NOT TABS. SPACES.
# SCREW THIS UP AND IT WON'T WORK.
# Also, this gets changed to [] after reading. Just remove the brackets when you add new shit.
# Please surround your changes in double quotes ("). It works without them, but if you use certain characters it screws up compiling. The quotes will not show up in the changelog.
changes:
- qol: "Reworks blueprints to use an eye for area modification."
- rscadd: "Adds outpost blueprints for creating areas on exoplanets, and shuttle blueprints for modifying shuttles."
- qol: "Moves eye creation to a component."
Binary file not shown.

After

Width:  |  Height:  |  Size: 486 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 516 B

+1
View File
@@ -38,6 +38,7 @@ Generally you don't want to put your areas in here; if the area is only used in
no_light_control = 1
base_turf = /turf/space
is_outside = OUTSIDE_YES
area_flags = AREA_FLAG_IS_BACKGROUND
/area/space/atmosalert()
return
+1
View File
@@ -4,6 +4,7 @@
icon_state = "mining"
music = list('sound/music/ambimine.ogg', 'sound/music/song_game.ogg')
sound_environment = SOUND_AREA_ASTEROID
area_flags = AREA_FLAG_IS_BACKGROUND
/area/mine/explored
name = "Mine"
+12 -3
View File
@@ -1616,10 +1616,20 @@
/obj/machinery/autolathe,
/turf/simulated/floor/plating,
/area/construction)
"rm" = (
/obj/item/blueprints/shuttle,
/turf/simulated/floor/shuttle,
/area/shuttle/runtime)
"sf" = (
/obj/machinery/computer/ship/sensors/terminal,
/turf/simulated/floor/tiled,
/area/storage/primary)
"sx" = (
/obj/effect/shuttle_landmark/runtime/hangar{
dir = 4
},
/turf/simulated/floor/plating,
/area/construction)
"sU" = (
/obj/machinery/atmospherics/pipe/simple/hidden{
dir = 9
@@ -1707,7 +1717,6 @@
/turf/simulated/floor/plating,
/area/maintenance/maintcentral)
"wL" = (
/obj/effect/shuttle_landmark/runtime/hangar,
/obj/machinery/computer/ship/engines,
/turf/simulated/floor/shuttle,
/area/shuttle/runtime)
@@ -3394,7 +3403,7 @@ Yc
cK
de
Qe
Vm
rm
Jx
wL
Vm
@@ -4167,7 +4176,7 @@ de
de
de
de
de
sx
de
de
de