icons should exist

This commit is contained in:
Letter N
2021-02-14 11:39:56 +08:00
parent 45d6a5f87f
commit de67720a0a
43 changed files with 738 additions and 475 deletions

View File

@@ -117,8 +117,8 @@
#define ui_borg_camera "CENTER+3:21,SOUTH:5"
#define ui_borg_alerts "CENTER+4:21,SOUTH:5"
#define ui_borg_language_menu "CENTER+4:21,SOUTH+1:5"
#define ui_borg_sensor "CENTER-3:15, SOUTH:5" //LEGACY
#define ui_borg_thrusters "CENTER-5:15, SOUTH:5" //LEGACY
#define ui_borg_sensor "CENTER-6:16, SOUTH:5" //LEGACY
#define ui_borg_thrusters "CENTER-5:16, SOUTH:5" //LEGACY
//Aliens
#define ui_alien_health "EAST,CENTER-1:15"

View File

@@ -676,7 +676,7 @@ SUBSYSTEM_DEF(shuttle)
if(!preview_shuttle)
load_template(loading_template)
preview_shuttle.linkup(loading_template, destination_port)
// preview_shuttle.linkup(loading_template, destination_port)
preview_template = loading_template
// get the existing shuttle information, if any

View File

@@ -454,12 +454,12 @@
return SSshuttle.shuttle_purchase_requirements_met["emagged"]
/datum/map_template/shuttle/emergency/cruise
suffix = "cruise"
name = "The NTSS Independence"
description = "Ordinarily reserved for special functions and events, the Cruise Shuttle Independence can bring a summery cheer to your next station evacuation for a 'modest' fee!"
admin_notes = "This motherfucker is BIG. You might need to force dock it."
credit_cost = 8000
// /datum/map_template/shuttle/emergency/cruise
// suffix = "cruise"
// name = "The NTSS Independence"
// description = "Ordinarily reserved for special functions and events, the Cruise Shuttle Independence can bring a summery cheer to your next station evacuation for a 'modest' fee!"
// admin_notes = "This motherfucker is BIG. You might need to force dock it."
// credit_cost = 8000
/datum/map_template/shuttle/emergency/monkey
suffix = "nature"

View File

@@ -1,3 +1,5 @@
#define DEFAULT_MAP_SIZE 15
/obj/machinery/computer/security
name = "security camera console"
desc = "Used to access the various cameras on the station."
@@ -8,15 +10,19 @@
var/list/network = list("ss13")
var/obj/machinery/camera/active_camera
/// The turf where the camera was last updated.
var/turf/last_camera_turf
var/list/concurrent_users = list()
// Stuff needed to render the map
var/map_name
var/const/default_map_size = 15
var/obj/screen/cam_screen
var/obj/screen/plane_master/lighting/cam_plane_master
var/obj/screen/map_view/cam_screen
/// All the plane masters that need to be applied.
var/list/cam_plane_masters
var/obj/screen/background/cam_background
interaction_flags_machine = INTERACT_MACHINE_ALLOW_SILICON | INTERACT_MACHINE_SET_MACHINE | INTERACT_MACHINE_REQUIRES_SIGHT
/obj/machinery/computer/security/Initialize()
. = ..()
// Map name has to start and end with an A-Z character,
@@ -33,18 +39,20 @@
cam_screen.assigned_map = map_name
cam_screen.del_on_map_removal = FALSE
cam_screen.screen_loc = "[map_name]:1,1"
cam_plane_master = new
cam_plane_master.name = "plane_master"
cam_plane_master.assigned_map = map_name
cam_plane_master.del_on_map_removal = FALSE
cam_plane_master.screen_loc = "[map_name]:CENTER"
cam_plane_masters = list()
for(var/plane in subtypesof(/obj/screen/plane_master))
var/obj/screen/instance = new plane()
instance.assigned_map = map_name
instance.del_on_map_removal = FALSE
instance.screen_loc = "[map_name]:CENTER"
cam_plane_masters += instance
cam_background = new
cam_background.assigned_map = map_name
cam_background.del_on_map_removal = FALSE
/obj/machinery/computer/security/Destroy()
qdel(cam_screen)
qdel(cam_plane_master)
QDEL_LIST(cam_plane_masters)
qdel(cam_background)
return ..()
@@ -56,9 +64,10 @@
/obj/machinery/computer/security/ui_interact(mob/user, datum/tgui/ui)
// Update UI
ui = SStgui.try_update_ui(user, src, ui)
// Show static if can't use the camera
if(!active_camera?.can_use())
show_camera_static()
// Update the camera, showing static if necessary and updating data if the location has moved.
update_active_camera_screen()
if(!ui)
var/user_ref = REF(user)
var/is_living = isliving(user)
@@ -72,7 +81,7 @@
use_power(active_power_usage)
// Register map objects
user.client.register_map_obj(cam_screen)
for(var/plane in cam_plane_master)
for(var/plane in cam_plane_masters)
user.client.register_map_obj(plane)
user.client.register_map_obj(cam_background)
// Open UI
@@ -100,6 +109,7 @@
data["cameras"] += list(list(
name = C.c_tag,
))
return data
/obj/machinery/computer/security/ui_act(action, params)
@@ -110,20 +120,42 @@
if(action == "switch_camera")
var/c_tag = params["name"]
var/list/cameras = get_available_cameras()
var/obj/machinery/camera/C = cameras[c_tag]
active_camera = C
var/obj/machinery/camera/selected_camera = cameras[c_tag]
active_camera = selected_camera
playsound(src, get_sfx("terminal_type"), 25, FALSE)
if(!selected_camera)
return TRUE
update_active_camera_screen()
return TRUE
/obj/machinery/computer/security/proc/update_active_camera_screen()
// Show static if can't use the camera
if(!active_camera?.can_use())
show_camera_static()
return TRUE
return
var/list/visible_turfs = list()
for(var/turf/T in (C.isXRay() \
? range(C.view_range, C) \
: view(C.view_range, C)))
visible_turfs += T
// Is this camera located in or attached to a living thing? If so, assume the camera's loc is the living thing.
var/cam_location = isliving(active_camera.loc) ? active_camera.loc : active_camera
// If we're not forcing an update for some reason and the cameras are in the same location,
// we don't need to update anything.
// Most security cameras will end here as they're not moving.
var/newturf = get_turf(cam_location)
if(last_camera_turf == newturf)
return
// Cameras that get here are moving, and are likely attached to some moving atom such as cyborgs.
last_camera_turf = get_turf(cam_location)
var/list/visible_things = active_camera.isXRay() ? range(active_camera.view_range, cam_location) : view(active_camera.view_range, cam_location)
for(var/turf/visible_turf in visible_things)
visible_turfs += visible_turf
var/list/bbox = get_bbox_of_atoms(visible_turfs)
var/size_x = bbox[3] - bbox[1] + 1
@@ -133,8 +165,6 @@
cam_background.icon_state = "clear"
cam_background.fill_rect(1, 1, size_x, size_y)
return TRUE
/obj/machinery/computer/security/ui_close(mob/user)
var/user_ref = REF(user)
var/is_living = isliving(user)
@@ -151,7 +181,7 @@
/obj/machinery/computer/security/proc/show_camera_static()
cam_screen.vis_contents.Cut()
cam_background.icon_state = "scanline2"
cam_background.fill_rect(1, 1, default_map_size, default_map_size)
cam_background.fill_rect(1, 1, DEFAULT_MAP_SIZE, DEFAULT_MAP_SIZE)
// Returns the list of cameras accessible from this computer
/obj/machinery/computer/security/proc/get_available_cameras()
@@ -179,7 +209,7 @@
name = "security camera monitor"
desc = "An old TV hooked into the station's camera network."
icon_state = "television"
icon_keyboard = null
icon_keyboard = "no_keyboard"
icon_screen = "detective_tv"
pass_flags = PASSTABLE
@@ -211,7 +241,7 @@
/obj/machinery/computer/security/qm
name = "\improper Quartermaster's camera console"
desc = "A console with access to the mining, auxillary base and vault camera networks."
desc = "A console with access to the mining, auxiliary base and vault camera networks."
network = list("mine", "auxbase", "vault")
circuit = null
@@ -222,17 +252,12 @@
desc = "Used for watching an empty arena."
icon = 'icons/obj/stationobjs.dmi'
icon_state = "telescreen"
layer = SIGN_LAYER
network = list("thunder")
density = FALSE
circuit = null
light_power = 0
/obj/machinery/computer/security/telescreen/Initialize()
. = ..()
var/turf/T = get_turf_pixel(src)
if(iswallturf(T))
plane = ABOVE_WALL_PLANE
/obj/machinery/computer/security/telescreen/update_icon_state()
icon_state = initial(icon_state)
if(stat & BROKEN)
@@ -246,21 +271,19 @@
network = list("thunder")
density = FALSE
circuit = null
//interaction_flags_atom = NONE // interact() is called by BigClick()
interaction_flags_atom = NONE // interact() is called by BigClick()
var/icon_state_off = "entertainment_blank"
var/icon_state_on = "entertainment"
/* If someone would like to try to get this long-distance viewing thing working, be my guest. I tried everything I could possibly think of and it just refused to operate correctly.
/obj/machinery/computer/security/telescreen/entertainment/Initialize()
. = ..()
RegisterSignal(src, COMSIG_CLICK, .proc/BigClick)
// Bypass clickchain to allow humans to use the telescreen from a distance
/obj/machinery/computer/security/telescreen/entertainment/proc/BigClick()
interact(usr)
SIGNAL_HANDLER
*/
INVOKE_ASYNC(src, /atom.proc/interact, usr)
/obj/machinery/computer/security/telescreen/entertainment/proc/notify(on)
if(on && icon_state == icon_state_off)
@@ -279,8 +302,8 @@
network = list("rd", "aicore", "aiupload", "minisat", "xeno", "test")
/obj/machinery/computer/security/telescreen/circuitry
name = "circuitry telescreen"
desc = "Used for watching the other eggheads from the safety of the circuitry lab."
name = "research telescreen"
desc = "A telescreen with access to the research division's camera network."
network = list("rd")
/obj/machinery/computer/security/telescreen/ce
@@ -324,8 +347,8 @@
network = list("prison")
/obj/machinery/computer/security/telescreen/auxbase
name = "auxillary base monitor"
desc = "A telescreen that connects to the auxillary base's camera."
name = "auxiliary base monitor"
desc = "A telescreen that connects to the auxiliary base's camera."
network = list("auxbase")
/obj/machinery/computer/security/telescreen/minisat
@@ -346,3 +369,5 @@
for(var/i in network)
network -= i
network += "[idnum][i]"
#undef DEFAULT_MAP_SIZE

View File

@@ -1,21 +1,37 @@
/obj/machinery/computer/pod
name = "mass driver launch control"
desc = "A combined blastdoor and mass driver control unit."
// processing_flags = START_PROCESSING_MANUALLY
/// Connected mass driver
var/obj/machinery/mass_driver/connected = null
var/title = "Mass Driver Controls"
/// ID of the launch control
var/id = 1
var/timing = 0
/// If the launch timer counts down
var/timing = FALSE
/// Time before auto launch
var/time = 30
/// Range in which we search for a mass drivers and poddoors nearby
var/range = 4
/// Countdown timer for the mass driver's delayed launch functionality.
COOLDOWN_DECLARE(massdriver_countdown)
/obj/machinery/computer/pod/Initialize()
. = ..()
for(var/obj/machinery/mass_driver/M in range(range, src))
if(M.id == id)
connected = M
break
/obj/machinery/computer/pod/process(delta_time)
if(COOLDOWN_FINISHED(src, massdriver_countdown))
timing = FALSE
// alarm() sleeps, so we want to end processing first and can't rely on return PROCESS_KILL
end_processing()
alarm()
/**
* Initiates launching sequence by checking if all components are functional, opening poddoors, firing mass drivers and then closing poddoors
*/
/obj/machinery/computer/pod/proc/alarm()
if(stat & (NOPOWER|BROKEN))
return
@@ -39,92 +55,110 @@
if(M.id == id)
M.close()
/obj/machinery/computer/pod/ui_interact(mob/user)
/obj/machinery/computer/pod/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "MassDriverControl", name)
ui.open()
/obj/machinery/computer/pod/ui_data(mob/user)
var/list/data = list()
// If the cooldown has finished, just display the time. If the cooldown hasn't finished, display the cooldown.
var/display_time = COOLDOWN_FINISHED(src, massdriver_countdown) ? time : COOLDOWN_TIMELEFT(src, massdriver_countdown) * 0.1
data["connected"] = connected ? TRUE : FALSE
data["seconds"] = round(display_time % 60)
data["minutes"] = round((display_time - data["seconds"]) / 60)
data["timing"] = timing
data["power"] = connected ? connected.power : 0.25
data["poddoor"] = FALSE
for(var/obj/machinery/door/poddoor/door in range(range, src))
if(door.id == id)
data["poddoor"] = TRUE
break
return data
/obj/machinery/computer/pod/ui_act(action, list/params)
. = ..()
if(!allowed(user))
to_chat(user, "<span class='warning'>Access denied.</span>")
if(.)
return
if(!allowed(usr))
to_chat(usr, "<span class='warning'>Access denied.</span>")
return
var/dat = ""
if(connected)
var/d2
if(timing) //door controls do not need timers.
d2 = "<A href='?src=[REF(src)];time=0'>Stop Time Launch</A>"
else
d2 = "<A href='?src=[REF(src)];time=1'>Initiate Time Launch</A>"
dat += "<HR>\nTimer System: [d2]\nTime Left: [DisplayTimeText(time)] <A href='?src=[REF(src)];tp=-30'>-</A> <A href='?src=[REF(src)];tp=-1'>-</A> <A href='?src=[REF(src)];tp=1'>+</A> <A href='?src=[REF(src)];tp=30'>+</A>"
var/temp = ""
var/list/L = list( 0.25, 0.5, 1, 2, 4, 8, 16 )
for(var/t in L)
if(t == connected.power)
temp += "[t] "
else
temp += "<A href = '?src=[REF(src)];power=[t]'>[t]</A> "
dat += "<HR>\nPower Level: [temp]<BR>\n<A href = '?src=[REF(src)];alarm=1'>Firing Sequence</A><BR>\n<A href = '?src=[REF(src)];drive=1'>Test Fire Driver</A><BR>\n<A href = '?src=[REF(src)];door=1'>Toggle Outer Door</A><BR>"
else
dat += "<BR>\n<A href = '?src=[REF(src)];door=1'>Toggle Outer Door</A><BR>"
dat += "<BR><BR><A href='?src=[REF(user)];mach_close=computer'>Close</A>"
add_fingerprint(usr)
var/datum/browser/popup = new(user, "computer", title, 400, 500)
popup.set_content(dat)
popup.open()
/obj/machinery/computer/pod/process()
if(!..())
switch(action)
if("set_power")
if(!connected)
return
var/value = text2num(params["power"])
if(!value)
return
value = clamp(value, 0.25, 16)
connected.power = value
return TRUE
if("launch")
alarm()
return TRUE
if("time")
timing = !timing
if(timing)
if(time > 0)
time = round(time) - 1
COOLDOWN_START(src, massdriver_countdown, time SECONDS)
begin_processing()
else
alarm()
time = 0
timing = 0
updateDialog()
/obj/machinery/computer/pod/Topic(href, href_list)
if(..())
time = COOLDOWN_TIMELEFT(src, massdriver_countdown) * 0.1
COOLDOWN_RESET(src, massdriver_countdown)
end_processing()
return TRUE
if("input")
var/value = text2num(params["adjust"])
if(!value)
return
if(usr.contents.Find(src) || (in_range(src, usr) && isturf(loc)) || hasSiliconAccessInArea(usr))
usr.set_machine(src)
if(href_list["power"])
var/t = text2num(href_list["power"])
t = min(max(0.25, t), 16)
if(connected)
connected.power = t
if(href_list["alarm"])
alarm()
if(href_list["time"])
timing = text2num(href_list["time"])
if(href_list["tp"])
var/tp = text2num(href_list["tp"])
time += tp
time = min(max(round(time), 0), 120)
if(href_list["door"])
value = round(time + value)
time = clamp(value, 0, 120)
return TRUE
if("door")
for(var/obj/machinery/door/poddoor/M in range(range, src))
if(M.id == id)
if(M.density)
M.open()
else
M.close()
if(href_list["drive"])
return TRUE
if("driver_test")
for(var/obj/machinery/mass_driver/M in range(range, src))
if(M.id == id)
M.power = connected.power
M.power = connected?.power
M.drive()
updateUsrDialog()
return TRUE
/obj/machinery/computer/pod/old
name = "\improper DoorMex control console"
title = "Door Controls"
icon_state = "oldcomp"
icon_screen = "library"
icon_keyboard = null
icon_keyboard = "no_keyboard"
// /obj/machinery/computer/pod/old/mass_driver_controller
// name = "\improper Mass Driver Controller"
// icon = 'icons/obj/airlock_machines.dmi'
// icon_state = "airlock_control_standby"
// icon_keyboard = "no_keyboard"
// density = FALSE
// /obj/machinery/computer/pod/old/mass_driver_controller/toxinsdriver
// id = MASSDRIVER_TOXINS
// //for maps where pod doors are outside of the standard 4 tile controller detection range (ie Pubbystation)
// /obj/machinery/computer/pod/old/mass_driver_controller/toxinsdriver/longrange
// range = 6
// /obj/machinery/computer/pod/old/mass_driver_controller/chapelgun
// id = MASSDRIVER_CHAPEL
// /obj/machinery/computer/pod/old/mass_driver_controller/trash
// id = MASSDRIVER_DISPOSALS
/obj/machinery/computer/pod/old/syndicate
name = "\improper ProComp Executive IIc"
desc = "The Syndicate operate on a tight budget. Operates external airlocks."
title = "External Airlock Controls"
req_access = list(ACCESS_SYNDICATE)
/obj/machinery/computer/pod/old/swf

View File

@@ -11,8 +11,24 @@
var/id = 1
var/drive_range = 50 //this is mostly irrelevant since current mass drivers throw into space, but you could make a lower-range mass driver for interstation transport or something I guess.
/obj/machinery/mass_driver/connect_to_shuttle(obj/docking_port/mobile/port, obj/docking_port/stationary/dock, idnum, override=FALSE)
id = "[idnum][id]"
// /obj/machinery/mass_driver/chapelgun
// name = "holy driver"
// id = MASSDRIVER_CHAPEL
// /obj/machinery/mass_driver/toxins
// id = MASSDRIVER_TOXINS
// /obj/machinery/mass_driver/trash
// id = MASSDRIVER_DISPOSALS
// /obj/machinery/mass_driver/Destroy()
// for(var/obj/machinery/computer/pod/control in GLOB.machines)
// if(control.id == id)
// control.connected = null
// return ..()
/obj/machinery/mass_driver/connect_to_shuttle(obj/docking_port/mobile/port, obj/docking_port/stationary/dock)
id = "[port.id]_[id]"
/obj/machinery/mass_driver/proc/drive(amount)
if(stat & (BROKEN|NOPOWER))
@@ -22,6 +38,8 @@
var/atom/target = get_edge_target_turf(src, dir)
for(var/atom/movable/O in loc)
if(!O.anchored || ismecha(O)) //Mechs need their launch platforms.
// if(ismob(O) && !isliving(O))
// continue
O_limit++
if(O_limit >= 20)
audible_message("<span class='notice'>[src] lets out a screech, it doesn't seem to be able to handle the load.</span>")
@@ -30,7 +48,6 @@
O.throw_at(target, drive_range * power, power)
flick("mass_driver1", src)
/obj/machinery/mass_driver/emp_act(severity)
. = ..()
if (. & EMP_PROTECT_SELF)

View File

@@ -29,7 +29,7 @@
integrity = round((M.obj_integrity / M.max_integrity) * 100),
charge = M.cell ? round(M.cell.percent()) : null,
airtank = M.internal_tank ? M.return_pressure() : null,
pilot = M.occupant,
pilot = list(M.occupant),
location = get_area_name(M, TRUE),
active_equipment = M.selected,
emp_recharging = MT.recharging,
@@ -38,7 +38,7 @@
if(istype(M, /obj/mecha/working/ripley))
var/obj/mecha/working/ripley/RM = M
mech_data += list(
cargo_space = round((RM.cargo.len / RM.cargo_capacity) * 100)
cargo_space = round((LAZYLEN(RM.cargo) / RM.cargo_capacity) * 100)
)
data["mechs"] += list(mech_data)
@@ -46,7 +46,8 @@
return data
/obj/machinery/computer/mecha/ui_act(action, params)
if(..())
. = ..()
if(.)
return
switch(action)
@@ -57,7 +58,7 @@
var/message = stripped_input(usr, "Input message", "Transmit message")
var/obj/mecha/M = MT.chassis
if(trim(message) && M)
M.occupant_message(message)
to_chat(M.occupant, message)
to_chat(usr, "<span class='notice'>Message sent.</span>")
. = TRUE
if("shock")
@@ -67,8 +68,8 @@
var/obj/mecha/M = MT.chassis
if(M)
MT.shock()
log_game("[key_name(usr)] has activated remote EMP on exosuit [M], located at [loc_name(M)], which is currently [M.occupant? "being piloted by [key_name(M.occupant)]." : "without a pilot."] ")
message_admins("[key_name_admin(usr)][ADMIN_FLW(usr)] has activated remote EMP on exosuit [M][ADMIN_JMP(M)], which is currently [M.occupant ? "being piloted by [key_name_admin(M.occupant)][ADMIN_FLW(M.occupant)]." : "without a pilot."] ")
log_game("[key_name(usr)] has activated remote EMP on exosuit [M], located at [loc_name(M)], which [M.occupant ? "has the occupants [M.occupant]." : "without a pilot."] ")
message_admins("[key_name_admin(usr)][ADMIN_FLW(usr)] has activated remote EMP on exosuit [M][ADMIN_JMP(M)], which is currently [M.occupants ? "occupied by [M.occupant][ADMIN_FLW(M)]." : "without a pilot."] ")
. = TRUE
/obj/item/mecha_parts/mecha_tracking
@@ -101,7 +102,7 @@
<b>Active Equipment:</b> [chassis.selected || "None"]"}
if(istype(chassis, /obj/mecha/working/ripley))
var/obj/mecha/working/ripley/RM = chassis
answer += "<br><b>Used Cargo Space:</b> [round((RM.cargo.len / RM.cargo_capacity * 100), 0.01)]%"
answer += "<br><b>Used Cargo Space:</b> [round((LAZYLEN(RM.cargo) / RM.cargo_capacity * 100), 0.01)]%"
return answer

View File

@@ -439,6 +439,8 @@
if(zero_amount())
return
return split_stack(user, 1)
else
. = ..()
/obj/item/stack/AltClick(mob/living/user)
. = ..()

View File

@@ -1,10 +1,12 @@
/obj/item/tank
name = "tank"
icon = 'icons/obj/tank.dmi'
icon_state = "generic"
lefthand_file = 'icons/mob/inhands/equipment/tanks_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/tanks_righthand.dmi'
flags_1 = CONDUCT_1
slot_flags = ITEM_SLOT_BACK
// worn_icon = 'icons/mob/clothing/back.dmi' //since these can also get thrown into suit storage slots. if something goes on the belt, set this to null.
hitsound = 'sound/weapons/smash.ogg'
pressure_resistance = ONE_ATMOSPHERE * 5
force = 5
@@ -18,12 +20,14 @@
var/distribute_pressure = ONE_ATMOSPHERE
var/integrity = 3
var/volume = 70
/// Icon state when in a tank holder. Null makes it incompatible with tank holder.
var/tank_holder_icon_state = "holder_generic"
/obj/item/tank/ui_action_click(mob/user)
toggle_internals(user)
/obj/item/tank/proc/toggle_internals(mob/user)
var/mob/living/carbon/H = user
var/mob/living/carbon/human/H = user
if(!istype(H))
return
@@ -84,11 +88,16 @@
/obj/item/tank/Destroy()
if(air_contents)
qdel(air_contents)
QDEL_NULL(air_contents)
STOP_PROCESSING(SSobj, src)
. = ..()
// /obj/item/tank/ComponentInitialize()
// . = ..()
// if(tank_holder_icon_state)
// AddComponent(/datum/component/container_item/tank_holder, tank_holder_icon_state)
/obj/item/tank/examine(mob/user)
var/obj/icon = src
. = ..()
@@ -140,13 +149,13 @@
if(T)
T.assume_air(air_contents)
air_update_turf()
playsound(src.loc, 'sound/effects/spray.ogg', 10, 1, -3)
playsound(src.loc, 'sound/effects/spray.ogg', 10, TRUE, -3)
qdel(src)
/obj/item/tank/suicide_act(mob/user)
var/mob/living/carbon/human/H = user
user.visible_message("<span class='suicide'>[user] is putting [src]'s valve to [user.p_their()] lips! It looks like [user.p_theyre()] trying to commit suicide!</span>")
playsound(loc, 'sound/effects/spray.ogg', 10, 1, -3)
playsound(loc, 'sound/effects/spray.ogg', 10, TRUE, -3)
if(!QDELETED(H) && air_contents && air_contents.return_pressure() >= 1000)
for(var/obj/item/W in H)
H.dropItemToGround(W)
@@ -159,12 +168,10 @@
H.spawn_gibs()
H.spill_organs()
H.spread_bodyparts()
return (BRUTELOSS)
/obj/item/tank/attack_ghost(mob/dead/observer/O)
. = ..()
atmosanalyzer_scan(air_contents, O, src, FALSE)
return MANUAL_SUICIDE
else
to_chat(user, "<span class='warning'>There isn't enough pressure in [src] to commit suicide with...</span>")
return SHAME
/obj/item/tank/attackby(obj/item/W, mob/user, params)
add_fingerprint(user)
@@ -182,27 +189,30 @@
ui = new(user, src, "Tank", name)
ui.open()
/obj/item/tank/ui_static_data(mob/user)
. = list (
"defaultReleasePressure" = round(TANK_DEFAULT_RELEASE_PRESSURE),
"minReleasePressure" = round(TANK_MIN_RELEASE_PRESSURE),
"maxReleasePressure" = round(TANK_MAX_RELEASE_PRESSURE),
"leakPressure" = round(TANK_LEAK_PRESSURE),
"fragmentPressure" = round(TANK_FRAGMENT_PRESSURE)
)
/obj/item/tank/ui_data(mob/user)
var/list/data = list()
data["tankPressure"] = round(air_contents.return_pressure() ? air_contents.return_pressure() : 0)
data["releasePressure"] = round(distribute_pressure ? distribute_pressure : 0)
data["defaultReleasePressure"] = round(TANK_DEFAULT_RELEASE_PRESSURE)
data["minReleasePressure"] = round(TANK_MIN_RELEASE_PRESSURE)
data["maxReleasePressure"] = round(TANK_MAX_RELEASE_PRESSURE)
. = list(
"tankPressure" = round(air_contents.return_pressure()),
"releasePressure" = round(distribute_pressure)
)
var/mob/living/carbon/C = user
if(!istype(C))
C = loc.loc
if(!istype(C))
return data
if(C.internal == src)
data["connected"] = TRUE
return data
if(istype(C) && C.internal == src)
.["connected"] = TRUE
/obj/item/tank/ui_act(action, params)
if(..())
. = ..()
if(.)
return
switch(action)
if("pressure")
@@ -228,6 +238,9 @@
/obj/item/tank/return_air()
return air_contents
// /obj/item/tank/return_analyzable_air()
// return air_contents
/obj/item/tank/assume_air(datum/gas_mixture/giver)
air_contents.merge(giver)
@@ -239,10 +252,9 @@
return null
var/tank_pressure = air_contents.return_pressure()
if(tank_pressure < distribute_pressure)
distribute_pressure = tank_pressure
var/actual_distribute_pressure = clamp(tank_pressure, 0, distribute_pressure)
var/moles_needed = distribute_pressure*volume_to_return/(R_IDEAL_GAS_EQUATION*air_contents.return_temperature())
var/moles_needed = actual_distribute_pressure*volume_to_return/(R_IDEAL_GAS_EQUATION*air_contents.return_temperature())
return remove_air(moles_needed)
@@ -266,7 +278,7 @@
log_game("Explosive tank rupture! Last key to touch the tank was [src.fingerprintslast].")
//Give the gas a chance to build up more pressure through reacting
air_contents.react(src)
air_contents.react(src)
// air_contents.react(src)
//Citadel Edit: removing extra react for "balance"
pressure = air_contents.return_pressure()
var/range = (pressure-TANK_FRAGMENT_PRESSURE)/TANK_FRAGMENT_SCALE
@@ -285,7 +297,7 @@
if(!T)
return
T.assume_air(air_contents)
playsound(src.loc, 'sound/effects/spray.ogg', 10, 1, -3)
playsound(src.loc, 'sound/effects/spray.ogg', 10, TRUE, -3)
qdel(src)
else
integrity--

View File

@@ -4,6 +4,11 @@ SAFES
FLOOR SAFES
*/
/// Chance for a sound clue
#define SOUND_CHANCE 10
/// Explosion number threshold for opening safe
#define BROKEN_THRESHOLD 3
//SAFES
/obj/structure/safe
name = "safe"
@@ -14,31 +19,36 @@ FLOOR SAFES
density = TRUE
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
interaction_flags_atom = INTERACT_ATOM_ATTACK_HAND | INTERACT_ATOM_UI_INTERACT
var/open = FALSE //is the safe open?
var/tumbler_1_pos //the tumbler position- from 0 to 72
var/tumbler_1_open //the tumbler position to open at- 0 to 72
var/tumbler_2_pos
var/tumbler_2_open
var/dial = 0 //where is the dial pointing?
var/space = 0 //the combined w_class of everything in the safe
var/maxspace = 24 //the maximum combined w_class of stuff in the safe
var/explosion_count = 0 //Tough, but breakable
/obj/structure/safe/New()
..()
tumbler_1_pos = rand(0, 71)
tumbler_1_open = rand(0, 71)
tumbler_2_pos = rand(0, 71)
tumbler_2_open = rand(0, 71)
/// The maximum combined w_class of stuff in the safe
var/maxspace = 24
/// The amount of tumblers that will be generated
var/number_of_tumblers = 2
/// Whether the safe is open or not
var/open = FALSE
/// Whether the safe is locked or not
var/locked = TRUE
/// The position the dial is pointing to
var/dial = 0
/// The list of tumbler dial positions that need to be hit
var/list/tumblers = list()
/// The index in the tumblers list of the tumbler dial position that needs to be hit
var/current_tumbler_index = 1
/// The combined w_class of everything in the safe
var/space = 0
/// Tough, but breakable if explosion counts reaches set value
var/explosion_count = 0
/obj/structure/safe/Initialize(mapload)
. = ..()
// Combination generation
for(var/i in 1 to number_of_tumblers)
tumblers.Add(rand(0, 99))
if(!mapload)
return
// Put as many items on our turf inside as possible
for(var/obj/item/I in loc)
if(space >= maxspace)
return
@@ -46,141 +56,36 @@ FLOOR SAFES
space += I.w_class
I.forceMove(src)
/obj/structure/safe/proc/check_unlocked(mob/user, canhear)
if(explosion_count > 2)
return 1
if(user && canhear)
if(tumbler_1_pos == tumbler_1_open)
to_chat(user, "<span class='italics'>You hear a [pick("tonk", "krunk", "plunk")] from [src].</span>")
if(tumbler_2_pos == tumbler_2_open)
to_chat(user, "<span class='italics'>You hear a [pick("tink", "krink", "plink")] from [src].</span>")
if(tumbler_1_pos == tumbler_1_open && tumbler_2_pos == tumbler_2_open)
if(user)
visible_message("<i><b>[pick("Spring", "Sprang", "Sproing", "Clunk", "Krunk")]!</b></i>")
return TRUE
return FALSE
/obj/structure/safe/proc/decrement(num)
num -= 1
if(num < 0)
num = 71
return num
/obj/structure/safe/proc/increment(num)
num += 1
if(num > 71)
num = 0
return num
/obj/structure/safe/update_icon_state()
if(open)
icon_state = "[initial(icon_state)]-open"
else
icon_state = initial(icon_state)
/obj/structure/safe/ui_interact(mob/user)
user.set_machine(src)
var/dat = "<center>"
dat += "<a href='?src=[REF(src)];open=1'>[open ? "Close" : "Open"] [src]</a> | <a href='?src=[REF(src)];decrement=1'>-</a> [dial] <a href='?src=[REF(src)];increment=1'>+</a>"
if(open)
dat += "<table>"
for(var/i = contents.len, i>=1, i--)
var/obj/item/P = contents[i]
dat += "<tr><td><a href='?src=[REF(src)];retrieve=[REF(P)]'>[P.name]</a></td></tr>"
dat += "</table></center>"
user << browse("<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'><title>[name]</title></head><body>[dat]</body></html>", "window=safe;size=350x300")
/obj/structure/safe/Topic(href, href_list)
if(!ishuman(usr))
return
var/mob/living/carbon/human/user = usr
if(!user.canUseTopic(src))
return
var/canhear = FALSE
if(user.is_holding_item_of_type(/obj/item/clothing/neck/stethoscope))
canhear = TRUE
if(href_list["open"])
if(check_unlocked())
to_chat(user, "<span class='notice'>You [open ? "close" : "open"] [src].</span>")
open = !open
update_icon()
updateUsrDialog()
return
else
to_chat(user, "<span class='warning'>You can't [open ? "close" : "open"] [src], the lock is engaged!</span>")
return
if(href_list["decrement"])
dial = decrement(dial)
if(dial == tumbler_1_pos + 1 || dial == tumbler_1_pos - 71)
tumbler_1_pos = decrement(tumbler_1_pos)
if(canhear)
to_chat(user, "<span class='italics'>You hear a [pick("clack", "scrape", "clank")] from [src].</span>")
if(tumbler_1_pos == tumbler_2_pos + 37 || tumbler_1_pos == tumbler_2_pos - 35)
tumbler_2_pos = decrement(tumbler_2_pos)
if(canhear)
to_chat(user, "<span class='italics'>You hear a [pick("click", "chink", "clink")] from [src].</span>")
check_unlocked(user, canhear)
updateUsrDialog()
return
if(href_list["increment"])
dial = increment(dial)
if(dial == tumbler_1_pos - 1 || dial == tumbler_1_pos + 71)
tumbler_1_pos = increment(tumbler_1_pos)
if(canhear)
to_chat(user, "<span class='italics'>You hear a [pick("clack", "scrape", "clank")] from [src].</span>")
if(tumbler_1_pos == tumbler_2_pos - 37 || tumbler_1_pos == tumbler_2_pos + 35)
tumbler_2_pos = increment(tumbler_2_pos)
if(canhear)
to_chat(user, "<span class='italics'>You hear a [pick("click", "chink", "clink")] from [src].</span>")
check_unlocked(user, canhear)
updateUsrDialog()
return
if(href_list["retrieve"])
user << browse("", "window=safe") // Close the menu
var/obj/item/P = locate(href_list["retrieve"]) in src
if(open)
if(P && in_range(src, user))
user.put_in_hands(P)
space -= P.w_class
updateUsrDialog()
/obj/structure/safe/attackby(obj/item/I, mob/user, params)
if(open)
. = 1 //no afterattack
. = TRUE //no afterattack
if(I.w_class + space <= maxspace)
space += I.w_class
if(!user.transferItemToLoc(I, src))
to_chat(user, "<span class='warning'>\The [I] is stuck to your hand, you cannot put it in the safe!</span>")
return
to_chat(user, "<span class='notice'>You put [I] in [src].</span>")
updateUsrDialog()
else
to_chat(user, "<span class='warning'>[I] won't fit in [src].</span>")
else
if(istype(I, /obj/item/clothing/neck/stethoscope))
attack_hand(user)
return
else
to_chat(user, "<span class='notice'>[I] won't fit in [src].</span>")
to_chat(user, "<span class='warning'>You can't put [I] into the safe while it is closed!</span>")
return
else if(istype(I, /obj/item/clothing/neck/stethoscope))
to_chat(user, "<span class='warning'>Hold [I] in one of your hands while you manipulate the dial!</span>")
else
return ..()
/obj/structure/safe/handle_atom_del(atom/A)
updateUsrDialog()
/obj/structure/safe/blob_act(obj/structure/blob/B)
return
/obj/structure/safe/ex_act(severity, target)
if(((severity == 2 && target == src) || severity == 1) && explosion_count < 3)
if(((severity == 2 && target == src) || severity == 1) && explosion_count < BROKEN_THRESHOLD)
explosion_count++
switch(explosion_count)
if(1)
@@ -190,6 +95,144 @@ FLOOR SAFES
if(3)
desc = initial(desc) + "\nThe lock seems to be broken."
/obj/structure/safe/ui_assets(mob/user)
return list(
get_asset_datum(/datum/asset/simple/safe),
)
/obj/structure/safe/ui_state(mob/user)
return GLOB.physical_state
/obj/structure/safe/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "Safe", name)
ui.open()
/obj/structure/safe/ui_data(mob/user)
var/list/data = list()
data["dial"] = dial
data["open"] = open
data["locked"] = locked
data["broken"] = check_broken()
if(open)
var/list/contents_names = list()
data["contents"] = contents_names
for(var/obj/O in contents)
contents_names[++contents_names.len] = list("name" = O.name, "sprite" = O.icon_state)
user << browse_rsc(icon(O.icon, O.icon_state), "[O.icon_state].png")
return data
/obj/structure/safe/ui_act(action, params)
. = ..()
if(.)
return
if(!ishuman(usr))
return
var/mob/living/carbon/human/user = usr
if(!user.canUseTopic(src, BE_CLOSE))
return
var/canhear = FALSE
if(user.is_holding_item_of_type(/obj/item/clothing/neck/stethoscope))
canhear = TRUE
switch(action)
if("open")
if(!check_unlocked() && !open && !broken)
to_chat(user, "<span class='warning'>You cannot open [src], as its lock is engaged!</span>")
return
to_chat(user, "<span class='notice'>You [open ? "close" : "open"] [src].</span>")
open = !open
update_icon()
return TRUE
if("turnright")
if(open)
return
if(broken)
to_chat(user, "<span class='warning'>The dial will not turn, as the mechanism is destroyed!</span>")
return
var/ticks = text2num(params["num"])
for(var/i = 1 to ticks)
dial = WRAP(dial - 1, 0, 100)
var/invalid_turn = current_tumbler_index % 2 == 0 || current_tumbler_index > number_of_tumblers
if(invalid_turn) // The moment you turn the wrong way or go too far, the tumblers reset
current_tumbler_index = 1
if(!invalid_turn && dial == tumblers[current_tumbler_index])
notify_user(user, canhear, list("tink", "krink", "plink"), ticks, i)
current_tumbler_index++
else
notify_user(user, canhear, list("clack", "scrape", "clank"), ticks, i)
check_unlocked()
return TRUE
if("turnleft")
if(open)
return
if(broken)
to_chat(user, "<span class='warning'>The dial will not turn, as the mechanism is destroyed!</span>")
return
var/ticks = text2num(params["num"])
for(var/i = 1 to ticks)
dial = WRAP(dial + 1, 0, 100)
var/invalid_turn = current_tumbler_index % 2 != 0 || current_tumbler_index > number_of_tumblers
if(invalid_turn) // The moment you turn the wrong way or go too far, the tumblers reset
current_tumbler_index = 1
if(!invalid_turn && dial == tumblers[current_tumbler_index])
notify_user(user, canhear, list("tonk", "krunk", "plunk"), ticks, i)
current_tumbler_index++
else
notify_user(user, canhear, list("click", "chink", "clink"), ticks, i)
check_unlocked()
return TRUE
if("retrieve")
if(!open)
return
var/index = text2num(params["index"])
if(!index)
return
var/obj/item/I = contents[index]
if(!I || !in_range(src, user))
return
user.put_in_hands(I)
space -= I.w_class
return TRUE
/**
* Checks if safe is considered in a broken state for force-opening the safe
*/
/obj/structure/safe/proc/check_broken()
return broken || explosion_count >= BROKEN_THRESHOLD
/**
* Called every dial turn to determine whether the safe should unlock or not.
*/
/obj/structure/safe/proc/check_unlocked()
if(check_broken())
return TRUE
if(current_tumbler_index > number_of_tumblers)
locked = FALSE
visible_message("<span class='boldnotice'>[pick("Spring", "Sprang", "Sproing", "Clunk", "Krunk")]!</span>")
return TRUE
locked = TRUE
return FALSE
/**
* Called every dial turn to provide feedback if possible.
*/
/obj/structure/safe/proc/notify_user(user, canhear, sounds, total_ticks, current_tick)
if(!canhear)
return
if(current_tick == 2)
to_chat(user, "<span class='italics'>The sounds from [src] are too fast and blend together.</span>")
if(total_ticks == 1 || prob(SOUND_CHANCE))
to_chat(user, "<span class='italics'>You hear a [pick(sounds)] from [src].</span>")
//FLOOR SAFES
/obj/structure/safe/floor
@@ -199,13 +242,11 @@ FLOOR SAFES
level = 1 //underfloor
layer = LOW_OBJ_LAYER
/obj/structure/safe/floor/Initialize(mapload)
. = ..()
if(mapload)
var/turf/T = loc
hide(T.intact)
/obj/structure/safe/floor/hide(var/intact)
invisibility = intact ? INVISIBILITY_MAXIMUM : 0
#undef SOUND_CHANCE
#undef BROKEN_THRESHOLD

View File

@@ -1547,22 +1547,17 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
msg += "</UL></BODY></HTML>"
src << browse(msg.Join(), "window=Player_playtime_check")
/datum/admins/proc/cmd_show_exp_panel(client/C)
/datum/admins/proc/cmd_show_exp_panel(client/client_to_check)
if(!check_rights(R_ADMIN))
return
if(!C)
to_chat(usr, "<span class='danger'>ERROR: Client not found.</span>")
if(!client_to_check)
to_chat(usr, "<span class='danger'>ERROR: Client not found.</span>", confidential = TRUE)
return
if(!CONFIG_GET(flag/use_exp_tracking))
to_chat(usr, "<span class='warning'>Tracking is disabled in the server configuration file.</span>")
to_chat(usr, "<span class='warning'>Tracking is disabled in the server configuration file.</span>", confidential = TRUE)
return
var/list/body = list()
body += "<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'><title>Playtime for [C.key]</title></head><BODY><BR>Playtime:"
body += C.get_exp_report()
body += "<A href='?_src_=holder;[HrefToken()];toggleexempt=[REF(C)]'>Toggle Exempt status</a>"
body += "</BODY></HTML>"
usr << browse(body.Join(), "window=playerplaytime[C.ckey];size=550x615")
new /datum/job_report_menu(client_to_check, usr)
/datum/admins/proc/toggle_exempt_status(client/C)
if(!check_rights(R_ADMIN))

View File

@@ -359,3 +359,27 @@ get_true_breath_pressure(pp) --> gas_pp = pp/breath_pp*total_moles()
to_chat(src, "Total time (new gas mixture): [total_time]ms")
to_chat(src, "Operations per second: [100000 / (total_time/1000)]")
*/
/// Releases gas from src to output air. This means that it can not transfer air to gas mixture with higher pressure.
/datum/gas_mixture/proc/release_gas_to(datum/gas_mixture/output_air, target_pressure)
var/output_starting_pressure = output_air.return_pressure()
var/input_starting_pressure = return_pressure()
if(output_starting_pressure >= min(target_pressure,input_starting_pressure-10))
//No need to pump gas if target is already reached or input pressure is too low
//Need at least 10 KPa difference to overcome friction in the mechanism
return FALSE
//Calculate necessary moles to transfer using PV = nRT
if((total_moles() > 0) && (return_temperature()>0))
var/pressure_delta = min(target_pressure - output_starting_pressure, (input_starting_pressure - output_starting_pressure)/2)
//Can not have a pressure delta that would cause output_pressure > input_pressure
var/transfer_moles = pressure_delta*output_air.return_volume()/(return_temperature() * R_IDEAL_GAS_EQUATION)
//Actually transfer the gas
var/datum/gas_mixture/removed = remove(transfer_moles)
output_air.merge(removed)
return TRUE
return FALSE

View File

@@ -55,26 +55,7 @@ Passive gate is similar to the regular pump except:
var/datum/gas_mixture/air1 = airs[1]
var/datum/gas_mixture/air2 = airs[2]
var/output_starting_pressure = air2.return_pressure()
var/input_starting_pressure = air1.return_pressure()
if(output_starting_pressure >= min(target_pressure,input_starting_pressure-10))
//No need to pump gas if target is already reached or input pressure is too low
//Need at least 10 KPa difference to overcome friction in the mechanism
return
//Calculate necessary moles to transfer using PV = nRT
if((air1.total_moles() > 0) && (air1.return_temperature()>0))
var/pressure_delta = min(target_pressure - output_starting_pressure, (input_starting_pressure - output_starting_pressure)/2)
//Can not have a pressure delta that would cause output_pressure > input_pressure
var/transfer_moles = pressure_delta*air2.return_volume()/(air1.return_temperature() * R_IDEAL_GAS_EQUATION)
//Actually transfer the gas
var/datum/gas_mixture/removed = air1.remove(transfer_moles)
air2.merge(removed)
if(air1.release_gas_to(air2, target_pressure))
update_parents()

View File

@@ -2,15 +2,18 @@
/obj/machinery/atmospherics/components/unary/tank
icon = 'icons/obj/atmospherics/pipes/pressure_tank.dmi'
icon_state = "generic"
name = "pressure tank"
desc = "A large vessel containing pressurized gas."
max_integrity = 800
density = TRUE
layer = ABOVE_WINDOW_LAYER
plane = GAME_PLANE
pipe_flags = PIPING_ONE_PER_TURF
var/volume = 10000 //in liters
var/gas_type = 0
/// The typepath of the gas this tank should be filled with.
var/gas_type = null
/obj/machinery/atmospherics/components/unary/tank/New()
..()
@@ -20,6 +23,7 @@
if(gas_type)
air_contents.set_moles(AIR_CONTENTS)
name = "[name] ([GLOB.meta_gas_names[gas_type]])"
setPipingLayer(piping_layer)
/obj/machinery/atmospherics/components/unary/tank/air
icon_state = "grey"
@@ -38,15 +42,71 @@
icon_state = "orange"
gas_type = /datum/gas/plasma
/obj/machinery/atmospherics/components/unary/tank/oxygen
icon_state = "blue"
gas_type = /datum/gas/oxygen
/obj/machinery/atmospherics/components/unary/tank/nitrogen
icon_state = "red"
gas_type = /datum/gas/nitrogen
/obj/machinery/atmospherics/components/unary/tank/nitrous_oxide
/obj/machinery/atmospherics/components/unary/tank/oxygen
icon_state = "blue"
gas_type = /datum/gas/oxygen
/obj/machinery/atmospherics/components/unary/tank/nitrous
icon_state = "red_white"
gas_type = /datum/gas/nitrous_oxide
/obj/machinery/atmospherics/components/unary/tank/bz
gas_type = /datum/gas/bz
// /obj/machinery/atmospherics/components/unary/tank/freon
// icon_state = "blue"
// gas_type = /datum/gas/freon
// /obj/machinery/atmospherics/components/unary/tank/halon
// icon_state = "blue"
// gas_type = /datum/gas/halon
// /obj/machinery/atmospherics/components/unary/tank/healium
// icon_state = "red"
// gas_type = /datum/gas/healium
// /obj/machinery/atmospherics/components/unary/tank/hydrogen
// icon_state = "grey"
// gas_type = /datum/gas/hydrogen
/obj/machinery/atmospherics/components/unary/tank/hypernoblium
icon_state = "blue"
gas_type = /datum/gas/hypernoblium
/obj/machinery/atmospherics/components/unary/tank/miasma
gas_type = /datum/gas/miasma
/obj/machinery/atmospherics/components/unary/tank/nitryl
gas_type = /datum/gas/nitryl
/obj/machinery/atmospherics/components/unary/tank/pluoxium
icon_state = "blue"
gas_type = /datum/gas/pluoxium
// /obj/machinery/atmospherics/components/unary/tank/proto_nitrate
// icon_state = "red"
// gas_type = /datum/gas/proto_nitrate
/obj/machinery/atmospherics/components/unary/tank/stimulum
icon_state = "red"
gas_type = /datum/gas/stimulum
/obj/machinery/atmospherics/components/unary/tank/tritium
gas_type = /datum/gas/tritium
/obj/machinery/atmospherics/components/unary/tank/water_vapor
icon_state = "grey"
gas_type = /datum/gas/water_vapor
// /obj/machinery/atmospherics/components/unary/tank/zauker
// gas_type = /datum/gas/zauker
// /obj/machinery/atmospherics/components/unary/tank/helium
// gas_type = /datum/gas/helium
// /obj/machinery/atmospherics/components/unary/tank/antinoblium
// gas_type = /datum/gas/antinoblium

View File

@@ -5,22 +5,28 @@
desc = "A canister for the storage of gas."
icon_state = "yellow"
density = TRUE
var/valve_open = FALSE
var/obj/machinery/atmospherics/components/binary/passive_gate/pump
var/release_log = ""
volume = 1000
var/filled = 0.5
var/gas_type
var/release_pressure = ONE_ATMOSPHERE
var/can_max_release_pressure = (ONE_ATMOSPHERE * 10)
var/can_min_release_pressure = (ONE_ATMOSPHERE / 10)
armor = list("melee" = 50, "bullet" = 50, "laser" = 50, "energy" = 100, "bomb" = 10, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 50)
max_integrity = 250
integrity_failure = 0.4
pressure_resistance = 7 * ONE_ATMOSPHERE
var/valve_open = FALSE
var/release_log = ""
var/filled = 0.5
var/gas_type
var/release_pressure = ONE_ATMOSPHERE
var/can_max_release_pressure = (ONE_ATMOSPHERE * 10)
var/can_min_release_pressure = (ONE_ATMOSPHERE / 10)
// this removes atmos fusion cans**
///Max amount of heat allowed inside of the canister before it starts to melt (different tiers have different limits)
// var/heat_limit = 5000
///Max amount of pressure allowed inside of the canister before it starts to break (different tiers have different limits)
// var/pressure_limit = 50000
var/temperature_resistance = 1000 + T0C
var/starter_temp
// Prototype vars
@@ -32,6 +38,8 @@
var/maximum_timer_set = 300
var/timing = FALSE
var/restricted = FALSE
///Set the tier of the canister and overlay used
// var/mode = CANISTER_TIER_1
req_access = list()
var/update = 0
@@ -195,27 +203,19 @@
filled = 1
release_pressure = ONE_ATMOSPHERE*2
/obj/machinery/portable_atmospherics/canister/New(loc, datum/gas_mixture/existing_mixture)
..()
/obj/machinery/portable_atmospherics/canister/Initialize(mapload, datum/gas_mixture/existing_mixture)
. = ..()
if(existing_mixture)
air_contents.copy_from(existing_mixture)
else
create_gas()
pump = new(src, FALSE)
pump.on = TRUE
pump.stat = 0
SSair.add_to_rebuild_queue(pump)
update_icon()
/obj/machinery/portable_atmospherics/canister/Destroy()
qdel(pump)
pump = null
return ..()
/obj/machinery/portable_atmospherics/canister/proc/create_gas()
if(gas_type)
// air_contents.add_gas(gas_type)
if(starter_temp)
air_contents.set_temperature(starter_temp)
air_contents.set_moles(gas_type,(maximum_pressure * filled) * air_contents.return_volume() / (R_IDEAL_GAS_EQUATION * air_contents.return_temperature()))
@@ -266,14 +266,17 @@
if(user.a_intent == INTENT_HARM)
return FALSE
if(stat & BROKEN)
if(!I.tool_start_check(user, amount=0))
return TRUE
to_chat(user, "<span class='notice'>You begin cutting [src] apart...</span>")
if(I.use_tool(src, user, 30, volume=50))
var/pressure = air_contents.return_pressure()
if(pressure > 300)
to_chat(user, "<span class='alert'>The pressure gauge on \the [src] indicates a high pressure inside... maybe you want to reconsider?</span>")
message_admins("[src] deconstructed by [ADMIN_LOOKUPFLW(user)]")
log_game("[src] deconstructed by [key_name(user)]")
to_chat(user, "<span class='notice'>You begin cutting \the [src] apart...</span>")
if(I.use_tool(src, user, 3 SECONDS, volume=50))
to_chat(user, "<span class='notice'>You cut \the [src] apart.</span>")
deconstruct(TRUE)
else
to_chat(user, "<span class='notice'>You cannot slice [src] apart when it isn't broken.</span>")
return TRUE
@@ -317,18 +320,19 @@
valve_open = !valve_open
timing = FALSE
if(!valve_open)
pump.airs[1] = null
pump.airs[2] = null
return
var/turf/T = get_turf(src)
pump.airs[1] = air_contents
pump.airs[2] = holding ? holding.air_contents : T.return_air()
pump.target_pressure = release_pressure
var/datum/gas_mixture/target_air = holding ? holding.air_contents : T.return_air()
pump.process_atmos() // Pump gas.
if(!holding)
air_update_turf() // Update the environment if needed.
if(air_contents.release_gas_to(target_air, release_pressure) && !holding)
air_update_turf()
// var/our_pressure = air_contents.return_pressure()
// var/our_temperature = air_contents.return_temperature()
///function used to check the limit of the canisters and also set the amount of damage that the canister can receive, if the heat and pressure are way higher than the limit the more damage will be done
// currently unused
// if(our_temperature > heat_limit || our_pressure > pressure_limit)
// take_damage(clamp((our_temperature/heat_limit) * (our_pressure/pressure_limit) * delta_time * 2, 5, 50), BURN, 0)
update_icon()
/obj/machinery/portable_atmospherics/canister/ui_state(mob/user)
@@ -340,35 +344,48 @@
ui = new(user, src, "Canister", name)
ui.open()
/obj/machinery/portable_atmospherics/canister/ui_static_data(mob/user)
return list(
"defaultReleasePressure" = round(CAN_DEFAULT_RELEASE_PRESSURE),
"minReleasePressure" = round(can_min_release_pressure),
"maxReleasePressure" = round(can_max_release_pressure),
"pressureLimit" = round(1e14),
"holdingTankLeakPressure" = round(TANK_LEAK_PRESSURE),
"holdingTankFragPressure" = round(TANK_FRAGMENT_PRESSURE)
)
/obj/machinery/portable_atmospherics/canister/ui_data()
var/data = list()
data["portConnected"] = connected_port ? 1 : 0
data["tankPressure"] = round(air_contents.return_pressure() ? air_contents.return_pressure() : 0)
data["releasePressure"] = round(release_pressure ? release_pressure : 0)
data["defaultReleasePressure"] = round(CAN_DEFAULT_RELEASE_PRESSURE)
data["minReleasePressure"] = round(can_min_release_pressure)
data["maxReleasePressure"] = round(can_max_release_pressure)
data["valveOpen"] = valve_open ? 1 : 0
. = list(
"portConnected" = !!connected_port,
"tankPressure" = round(air_contents.return_pressure()),
"releasePressure" = round(release_pressure),
"valveOpen" = !!valve_open,
"isPrototype" = !!prototype,
"hasHoldingTank" = !!holding
)
data["isPrototype"] = prototype ? 1 : 0
if (prototype)
data["restricted"] = restricted
data["timing"] = timing
data["time_left"] = get_time_left()
data["timer_set"] = timer_set
data["timer_is_not_default"] = timer_set != default_timer_set
data["timer_is_not_min"] = timer_set != minimum_timer_set
data["timer_is_not_max"] = timer_set != maximum_timer_set
. += list(
"restricted" = restricted,
"timing" = timing,
"time_left" = get_time_left(),
"timer_set" = timer_set,
"timer_is_not_default" = timer_set != default_timer_set,
"timer_is_not_min" = timer_set != minimum_timer_set,
"timer_is_not_max" = timer_set != maximum_timer_set
)
data["hasHoldingTank"] = holding ? 1 : 0
if (holding)
data["holdingTank"] = list()
data["holdingTank"]["name"] = holding.name
data["holdingTank"]["tankPressure"] = round(holding.air_contents.return_pressure())
return data
. += list(
"holdingTank" = list(
"name" = holding.name,
"tankPressure" = round(holding.air_contents.return_pressure())
)
)
/obj/machinery/portable_atmospherics/canister/ui_act(action, params)
if(..())
. = ..()
if(.)
return
switch(action)
if("relabel")
@@ -377,6 +394,7 @@
var/newtype = label2types[label]
if(newtype)
var/obj/machinery/portable_atmospherics/canister/replacement = newtype
investigate_log("was relabelled to [initial(replacement.name)] by [key_name(usr)].", INVESTIGATE_ATMOS)
name = initial(replacement.name)
desc = initial(replacement.desc)
icon_state = initial(replacement.icon_state)
@@ -458,9 +476,8 @@
if("eject")
if(holding)
if(valve_open)
message_admins("[ADMIN_LOOKUPFLW(usr)] removed [holding] from [src] with valve still open at [ADMIN_VERBOSEJMP(src)] releasing contents into the <span class='boldannounce'>air</span><br>.")
investigate_log("[key_name(usr)] removed the [holding], leaving the valve open and transferring into the <span class='boldannounce'>air</span><br>", INVESTIGATE_ATMOS)
holding.forceMove(get_turf(src))
holding = null
message_admins("[ADMIN_LOOKUPFLW(usr)] removed [holding] from [src] with valve still open at [ADMIN_VERBOSEJMP(src)] releasing contents into the <span class='boldannounce'>air</span>.")
investigate_log("[key_name(usr)] removed the [holding], leaving the valve open and transferring into the <span class='boldannounce'>air</span>.", INVESTIGATE_ATMOS)
replace_tank(usr, FALSE)
. = TRUE
update_icon()

View File

@@ -190,11 +190,7 @@ GLOBAL_VAR_INIT(normal_ooc_colour, "#002eb8")
to_chat(usr, "<span class='notice'>Sorry, tracking is currently disabled.</span>")
return
var/list/body = list()
body += "<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'><title>Playtime for [key]</title></head><BODY><BR>Playtime:"
body += get_exp_report()
body += "</BODY></HTML>"
usr << browse(body.Join(), "window=playerplaytime[ckey];size=550x615")
new /datum/job_report_menu(src, usr)
/client/proc/ignore_key(client)
var/client/C = client

View File

@@ -156,11 +156,7 @@
/obj/machinery/processor/slime
name = "slime processor"
desc = "An industrial grinder with a sticker saying appropriated for science department. Keep hands clear of intake area while operating."
/obj/machinery/processor/slime/Initialize()
. = ..()
var/obj/item/circuitboard/machine/B = new /obj/item/circuitboard/machine/processor/slime(null)
B.apply_default_parts(src)
circuit = /obj/item/circuitboard/machine/processor/slime
/obj/machinery/processor/slime/adjust_item_drop_location(atom/movable/AM)
var/static/list/slimecores = subtypesof(/obj/item/slime_extract)

View File

@@ -0,0 +1,49 @@
#define JOB_REPORT_MENU_FAIL_REASON_TRACKING_DISABLED 1
#define JOB_REPORT_MENU_FAIL_REASON_NO_RECORDS 2
/datum/job_report_menu
var/client/owner
/datum/job_report_menu/New(client/owner, mob/viewer)
src.owner = owner
ui_interact(viewer)
/datum/job_report_menu/ui_state()
return GLOB.always_state
/datum/job_report_menu/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if (!ui)
ui = new(user, src, "TrackedPlaytime")
ui.open()
/datum/job_report_menu/ui_static_data()
if (!CONFIG_GET(flag/use_exp_tracking))
return list("failReason" = JOB_REPORT_MENU_FAIL_REASON_TRACKING_DISABLED)
var/list/play_records = owner.prefs.exp
if (!play_records.len)
owner.set_exp_from_db()
play_records = owner.prefs.exp
if (!play_records.len)
return list("failReason" = JOB_REPORT_MENU_FAIL_REASON_NO_RECORDS)
var/list/data = list()
data["jobPlaytimes"] = list()
data["specialPlaytimes"] = list()
for (var/job_name in SSjob.name_occupations)
var/playtime = play_records[job_name] ? text2num(play_records[job_name]) : 0
data["jobPlaytimes"][job_name] = playtime
for (var/special_name in GLOB.exp_specialmap[EXP_TYPE_SPECIAL])
var/playtime = play_records[special_name] ? text2num(play_records[special_name]) : 0
data["specialPlaytimes"][special_name] = playtime
data["livingTime"] = play_records[EXP_TYPE_LIVING]
data["ghostTime"] = play_records[EXP_TYPE_GHOST]
return data
#undef JOB_REPORT_MENU_FAIL_REASON_TRACKING_DISABLED
#undef JOB_REPORT_MENU_FAIL_REASON_NO_RECORDS

View File

@@ -8,12 +8,12 @@ GLOBAL_LIST(labor_sheet_values)
icon = 'icons/obj/machines/mining_machines.dmi'
icon_state = "console"
density = FALSE
/// Connected stacking machine
var/obj/machinery/mineral/stacking_machine/laborstacker/stacking_machine = null
/// Direction of the stacking machine
var/machinedir = SOUTH
var/obj/machinery/door/airlock/release_door
var/door_tag = "prisonshuttle"
var/obj/item/radio/Radio //needed to send messages to sec radio
/// Needed to send messages to sec radio
var/obj/item/radio/Radio
/obj/machinery/mineral/labor_claim_console/Initialize()
. = ..()
@@ -39,15 +39,23 @@ GLOBAL_LIST(labor_sheet_values)
ui = new(user, src, "LaborClaimConsole", name)
ui.open()
/obj/machinery/mineral/labor_claim_console/ui_static_data(mob/user)
var/list/data = list()
data["ores"] = GLOB.labor_sheet_values
return data
/obj/machinery/mineral/labor_claim_console/ui_data(mob/user)
var/list/data = list()
var/can_go_home = FALSE
data["emagged"] = (obj_flags & EMAGGED) ? 1 : 0
data["emagged"] = FALSE
if(obj_flags & EMAGGED)
data["emagged"] = TRUE
can_go_home = TRUE
var/obj/item/card/id/I = user.get_idcard(TRUE)
var/obj/item/card/id/I
if(isliving(usr))
var/mob/living/L = usr
I = L.get_idcard(TRUE)
if(istype(I, /obj/item/card/id/prisoner))
var/obj/item/card/id/prisoner/P = I
data["id_points"] = P.points
@@ -63,43 +71,46 @@ GLOBAL_LIST(labor_sheet_values)
if(stacking_machine)
data["unclaimed_points"] = stacking_machine.points
data["ores"] = GLOB.labor_sheet_values
data["can_go_home"] = can_go_home
return data
/obj/machinery/mineral/labor_claim_console/ui_act(action, params)
if(..())
. = ..()
if(.)
return
var/mob/M = usr
switch(action)
if("claim_points")
var/mob/M = usr
var/obj/item/card/id/I = M.get_idcard(TRUE)
var/obj/item/card/id/I
if(isliving(M))
var/mob/living/L = M
I = L.get_idcard(TRUE)
if(istype(I, /obj/item/card/id/prisoner))
var/obj/item/card/id/prisoner/P = I
P.points += stacking_machine.points
stacking_machine.points = 0
to_chat(usr, "<span class='notice'>Points transferred.</span>")
. = TRUE
to_chat(M, "<span class='notice'>Points transferred.</span>")
return TRUE
else
to_chat(usr, "<span class='alert'>No valid id for point transfer detected.</span>")
to_chat(M, "<span class='alert'>No valid id for point transfer detected.</span>")
if("move_shuttle")
if(!alone_in_area(get_area(src), usr))
to_chat(usr, "<span class='alert'>Prisoners are only allowed to be released while alone.</span>")
else
if(!alone_in_area(get_area(src), M))
to_chat(M, "<span class='alert'>Prisoners are only allowed to be released while alone.</span>")
return
switch(SSshuttle.moveShuttle("laborcamp", "laborcamp_home", TRUE))
if(1)
to_chat(usr, "<span class='alert'>Shuttle not found.</span>")
to_chat(M, "<span class='alert'>Shuttle not found.</span>")
if(2)
to_chat(usr, "<span class='alert'>Shuttle already at station.</span>")
to_chat(M, "<span class='alert'>Shuttle already at station.</span>")
if(3)
to_chat(usr, "<span class='alert'>No permission to dock could be granted.</span>")
to_chat(M, "<span class='alert'>No permission to dock could be granted.</span>")
else
if(!(obj_flags & EMAGGED))
Radio.set_frequency(FREQ_SECURITY)
Radio.talk_into(src, "A prisoner has returned to the station. Minerals and Prisoner ID card ready for retrieval.", FREQ_SECURITY)
to_chat(usr, "<span class='notice'>Shuttle received message and will be sent shortly.</span>")
. = TRUE
to_chat(M, "<span class='notice'>Shuttle received message and will be sent shortly.</span>")
return TRUE
/obj/machinery/mineral/labor_claim_console/proc/locate_stacking_machine()
stacking_machine = locate(/obj/machinery/mineral/stacking_machine, get_step(src, machinedir))
@@ -110,8 +121,7 @@ GLOBAL_LIST(labor_sheet_values)
/obj/machinery/mineral/labor_claim_console/emag_act(mob/user)
. = ..()
if((obj_flags & EMAGGED))
return
if(!(obj_flags & EMAGGED))
obj_flags |= EMAGGED
to_chat(user, "<span class='warning'>PZZTTPFFFT</span>")
return TRUE
@@ -127,7 +137,7 @@ GLOBAL_LIST(labor_sheet_values)
..()
/obj/machinery/mineral/stacking_machine/laborstacker/attackby(obj/item/I, mob/living/user)
if(istype(I, /obj/item/stack/sheet) && user.canUnEquip(I))
if(istype(I, /obj/item/stack/sheet) && user.canUnEquip(I) && user.a_intent == INTENT_HELP)
var/obj/item/stack/sheet/inp = I
points += inp.point_value * inp.amount
return ..()
@@ -141,7 +151,10 @@ GLOBAL_LIST(labor_sheet_values)
icon_state = "console"
density = FALSE
/obj/machinery/mineral/labor_points_checker/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
/obj/machinery/mineral/labor_points_checker/attack_hand(mob/user)
. = ..()
if(. || user.is_blind())
return
user.examinate(src)
/obj/machinery/mineral/labor_points_checker/attackby(obj/item/I, mob/user, params)

View File

@@ -3,6 +3,9 @@
desc = "A computer system running a deep neural network that processes arbitrary information to produce data useable in the development of new technologies. In layman's terms, it makes research points."
icon = 'icons/obj/machines/research.dmi'
icon_state = "server"
req_access = list(ACCESS_RD) //ONLY THE R&D CAN CHANGE SERVER SETTINGS.
circuit = /obj/item/circuitboard/machine/rdserver
var/datum/techweb/stored_research
var/heat_health = 100
//Code for point mining here.
@@ -15,14 +18,11 @@
var/temp_tolerance_low = 0
var/temp_tolerance_high = T20C
var/temp_penalty_coefficient = 0.5 //1 = -1 points per degree above high tolerance. 0.5 = -0.5 points per degree above high tolerance.
req_access = list(ACCESS_RD) //ONLY THE R&D CAN CHANGE SERVER SETTINGS.
/obj/machinery/rnd/server/Initialize()
. = ..()
SSresearch.servers |= src
stored_research = SSresearch.science_tech
var/obj/item/circuitboard/machine/B = new /obj/item/circuitboard/machine/rdserver(null)
B.apply_default_parts(src)
/obj/machinery/rnd/server/Destroy()
SSresearch.servers -= src

View File

@@ -87,7 +87,7 @@
if(!H.myseed)
return
if(!H.harvest)// So it's bit harder.
to_chat(user, "<span class='warning'>Plant needs to be ready to harvest to perform full data scan.</span>") //Because space dna is actually magic
to_chat(user, "<span class='alert'>Plant needs to be ready to harvest to perform full data scan.</span>") //Because space dna is actually magic
return
if(plants[H.myseed.type])
to_chat(user, "<span class='notice'>Plant data already present in local storage.</span>")
@@ -101,10 +101,10 @@
if(isanimal(target))
var/mob/living/simple_animal/A = target
if(!A.healable)//simple approximation of being animal not a robot or similar
to_chat(user, "<span class='warning'>No compatible DNA detected</span>")
to_chat(user, "<span class='alert'>No compatible DNA detected.</span>")
return
if(animals[target.type])
to_chat(user, "<span class='notice'>Animal data already present in local storage.</span>")
to_chat(user, "<span class='alert'>Animal data already present in local storage.</span>")
return
animals[target.type] = 1
to_chat(user, "<span class='notice'>Animal data added to local storage.</span>")
@@ -173,7 +173,6 @@
qdel(filler)
. = ..()
/obj/machinery/dna_vault/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
@@ -204,15 +203,17 @@
data["choiceB"] = ""
if(user && completed)
var/list/L = power_lottery[user]
if(L && L.len)
if(L?.len)
data["used"] = FALSE
data["choiceA"] = L[1]
data["choiceB"] = L[2]
return data
/obj/machinery/dna_vault/ui_act(action, params)
if(..())
. = ..()
if(.)
return
switch(action)
if("gene")
upgrade(usr,params["choice"])
@@ -244,8 +245,6 @@
else
return ..()
/obj/machinery/dna_vault/proc/upgrade(mob/living/carbon/human/H,upgrade_type)
if(!(upgrade_type in power_lottery[H]))
return

Binary file not shown.

Before

Width:  |  Height:  |  Size: 237 B

After

Width:  |  Height:  |  Size: 337 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 392 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 454 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 487 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 491 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 164 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 256 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 326 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 352 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 468 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 449 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 503 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -2278,6 +2278,7 @@
#include "code\modules\integrated_electronics\subtypes\weaponized.dm"
#include "code\modules\jobs\access.dm"
#include "code\modules\jobs\job_exp.dm"
#include "code\modules\jobs\job_report.dm"
#include "code\modules\jobs\jobs.dm"
#include "code\modules\jobs\job_types\_job.dm"
#include "code\modules\jobs\job_types\ai.dm"